From 0d2c8cc5b00f6ff6de34c5dc82f115db37100701 Mon Sep 17 00:00:00 2001 From: Gary Benson Date: Thu, 14 Jul 2016 11:00:08 +0100 Subject: [PATCH] Support SHT_NOTE sections This commit adds a new NoteSection class with an iter_notes iterator which operates in much the same way as NoteSegment's iter_notes. An example to illustrate usage is added, and its reference output is added for the test suite. --- elftools/elf/elffile.py | 4 ++- elftools/elf/notes.py | 43 ++++++++++++++++++++++ elftools/elf/sections.py | 17 +++++++++ elftools/elf/segments.py | 40 +++++---------------- examples/elf_notes.py | 48 +++++++++++++++++++++++++ examples/reference_output/elf_notes.out | 13 +++++++ 6 files changed, 132 insertions(+), 33 deletions(-) create mode 100644 elftools/elf/notes.py create mode 100644 examples/elf_notes.py create mode 100644 examples/reference_output/elf_notes.out diff --git a/elftools/elf/elffile.py b/elftools/elf/elffile.py index d198749..1cb0f4a 100644 --- a/elftools/elf/elffile.py +++ b/elftools/elf/elffile.py @@ -24,7 +24,7 @@ from ..common.utils import struct_parse, elf_assert from .structs import ELFStructs from .sections import ( Section, StringTableSection, SymbolTableSection, - SUNWSyminfoTableSection, NullSection) + SUNWSyminfoTableSection, NullSection, NoteSection) from .dynamic import DynamicSection, DynamicSegment from .relocation import RelocationSection, RelocationHandler from .gnuversions import ( @@ -307,6 +307,8 @@ class ELFFile(object): section_header, name, self.stream, self) elif sectype == 'SHT_DYNAMIC': return DynamicSection(section_header, name, self.stream, self) + elif sectype == 'SHT_NOTE': + return NoteSection(section_header, name, self.stream, self) else: return Section(section_header, name, self.stream) diff --git a/elftools/elf/notes.py b/elftools/elf/notes.py new file mode 100644 index 0000000..b3a41d6 --- /dev/null +++ b/elftools/elf/notes.py @@ -0,0 +1,43 @@ +#------------------------------------------------------------------------------- +# elftools: elf/notes.py +# +# ELF notes +# +# Eli Bendersky (eliben@gmail.com) +# This code is in the public domain +#------------------------------------------------------------------------------- +from ..common.py3compat import bytes2str +from ..common.utils import struct_parse, roundup +from ..construct import CString + + +def iter_notes(elffile, offset, size): + """ Yield all the notes in a section or segment. + """ + end = offset + size + while offset < end: + note = struct_parse( + elffile.structs.Elf_Nhdr, + elffile.stream, + stream_pos=offset) + note['n_offset'] = offset + offset += elffile.structs.Elf_Nhdr.sizeof() + elffile.stream.seek(offset) + # n_namesz is 4-byte aligned. + disk_namesz = roundup(note['n_namesz'], 2) + note['n_name'] = bytes2str( + CString('').parse(elffile.stream.read(disk_namesz))) + offset += disk_namesz + + desc_data = bytes2str(elffile.stream.read(note['n_descsz'])) + if note['n_type'] == 'NT_GNU_ABI_TAG': + note['n_desc'] = struct_parse(elffile.structs.Elf_Nhdr_abi, + elffile.stream, + offset) + elif note['n_type'] == 'NT_GNU_BUILD_ID': + note['n_desc'] = ''.join('%.2x' % ord(b) for b in desc_data) + else: + note['n_desc'] = desc_data + offset += roundup(note['n_descsz'], 2) + note['n_size'] = offset - note['n_offset'] + yield note diff --git a/elftools/elf/sections.py b/elftools/elf/sections.py index ea7abaf..2058053 100644 --- a/elftools/elf/sections.py +++ b/elftools/elf/sections.py @@ -8,6 +8,8 @@ #------------------------------------------------------------------------------- from ..common.utils import struct_parse, elf_assert, parse_cstring_from_stream from collections import defaultdict +from .notes import iter_notes + class Section(object): """ Base class for ELF sections. Also used for all sections types that have @@ -174,3 +176,18 @@ class SUNWSyminfoTableSection(Section): """ for i in range(1, self.num_symbols() + 1): yield self.get_symbol(i) + + +class NoteSection(Section): + """ ELF NOTE section. Knows how to parse notes. + """ + def __init__(self, header, name, stream, elffile): + super(NoteSection, self).__init__(header, name, stream) + self.elffile = elffile + + def iter_notes(self): + """ Yield all the notes in the section. Each result is a dictionary- + like object with "n_name", "n_type", and "n_desc" fields, amongst + others. + """ + return iter_notes(self.elffile, self['sh_offset'], self['sh_size']) diff --git a/elftools/elf/segments.py b/elftools/elf/segments.py index 15f9b0b..c1c0279 100644 --- a/elftools/elf/segments.py +++ b/elftools/elf/segments.py @@ -7,9 +7,9 @@ # This code is in the public domain #------------------------------------------------------------------------------- from ..construct import CString -from ..common.utils import roundup, struct_parse -from ..common.py3compat import bytes2str +from ..common.utils import struct_parse from .constants import SH_FLAGS +from .notes import iter_notes class Segment(object): @@ -102,36 +102,12 @@ class NoteSegment(Segment): """ def __init__(self, header, stream, elffile): super(NoteSegment, self).__init__(header, stream) - self._elfstructs = elffile.structs + self.elffile = elffile def iter_notes(self): - """ Iterates the list of notes in the segment. + + """ Yield all the notes in the segment. Each result is a dictionary- + like object with "n_name", "n_type", and "n_desc" fields, amongst + others. """ - offset = self['p_offset'] - end = self['p_offset'] + self['p_filesz'] - while offset < end: - note = struct_parse( - self._elfstructs.Elf_Nhdr, - self.stream, - stream_pos=offset) - note['n_offset'] = offset - offset += self._elfstructs.Elf_Nhdr.sizeof() - self.stream.seek(offset) - # n_namesz is 4-byte aligned. - disk_namesz = roundup(note['n_namesz'], 2) - note['n_name'] = bytes2str( - CString('').parse(self.stream.read(disk_namesz))) - offset += disk_namesz - - desc_data = bytes2str(self.stream.read(note['n_descsz'])) - if note['n_type'] == 'NT_GNU_ABI_TAG': - note['n_desc'] = struct_parse(self._elfstructs.Elf_Nhdr_abi, - self.stream, - offset) - elif note['n_type'] == 'NT_GNU_BUILD_ID': - note['n_desc'] = ''.join('%.2x' % ord(b) for b in desc_data) - else: - note['n_desc'] = desc_data - offset += roundup(note['n_descsz'], 2) - note['n_size'] = offset - note['n_offset'] - yield note + return iter_notes(self.elffile, self['p_offset'], self['p_filesz']) diff --git a/examples/elf_notes.py b/examples/elf_notes.py new file mode 100644 index 0000000..9e34918 --- /dev/null +++ b/examples/elf_notes.py @@ -0,0 +1,48 @@ +#------------------------------------------------------------------------------- +# elftools example: elf_notes.py +# +# An example of obtaining note sections from an ELF file and examining +# the notes it contains. +# +# Eli Bendersky (eliben@gmail.com) +# This code is in the public domain +#------------------------------------------------------------------------------- +from __future__ import print_function +import sys + +# If pyelftools is not installed, the example can also run from the root or +# examples/ dir of the source distribution. +sys.path[0:0] = ['.', '..'] + +from elftools.elf.elffile import ELFFile +from elftools.elf.sections import NoteSection + + +def process_file(filename): + print('Processing file:', filename) + with open(filename, 'rb') as f: + for sect in ELFFile(f).iter_sections(): + if not isinstance(sect, NoteSection): + continue + print(' Note section "%s" at offset 0x%.8x with size %d' % ( + sect.name, sect.header['sh_offset'], sect.header['sh_size'])) + for note in sect.iter_notes(): + print(' Name:', note['n_name']) + print(' Type:', note['n_type']) + desc = note['n_desc'] + if note['n_type'] == 'NT_GNU_ABI_TAG': + print(' Desc: %s, ABI: %d.%d.%d' % ( + desc['abi_os'], + desc['abi_major'], + desc['abi_minor'], + desc['abi_tiny'])) + elif note['n_type'] == 'NT_GNU_BUILD_ID': + print(' Desc:', desc) + else: + print(' Desc:', ''.join('%.2x' % ord(b) for b in desc)) + + +if __name__ == '__main__': + if sys.argv[1] == '--test': + for filename in sys.argv[2:]: + process_file(filename) diff --git a/examples/reference_output/elf_notes.out b/examples/reference_output/elf_notes.out new file mode 100644 index 0000000..dbcb5f5 --- /dev/null +++ b/examples/reference_output/elf_notes.out @@ -0,0 +1,13 @@ +Processing file: ./examples/sample_exe64.elf + Note section ".note.ABI-tag" at offset 0x00000254 with size 32 + Name: GNU + Type: NT_GNU_ABI_TAG + Desc: ELF_NOTE_OS_LINUX, ABI: 2.6.4 + Note section ".note.SuSE" at offset 0x00000274 with size 24 + Name: SuSE + Type: 1163097427 + Desc: 01000a02 + Note section ".note.gnu.build-id" at offset 0x0000028c with size 36 + Name: GNU + Type: NT_GNU_BUILD_ID + Desc: 8e50cda8e25993499ac4aa2d8deaf58d0949d47d -- 2.30.2