Support SHT_NOTE sections
authorGary Benson <gbenson@redhat.com>
Thu, 14 Jul 2016 10:00:08 +0000 (11:00 +0100)
committerGary Benson <gbenson@redhat.com>
Mon, 1 Aug 2016 09:25:18 +0000 (10:25 +0100)
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
elftools/elf/notes.py [new file with mode: 0644]
elftools/elf/sections.py
elftools/elf/segments.py
examples/elf_notes.py [new file with mode: 0644]
examples/reference_output/elf_notes.out [new file with mode: 0644]

index d1987497b8e543ba297333d8e7c3b41b18d5c990..1cb0f4a532be316373b6638a6b84f0618bb55cb0 100644 (file)
@@ -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 (file)
index 0000000..b3a41d6
--- /dev/null
@@ -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
index ea7abaf6269edee083a34f6c58b7cd3f77a7d860..205805368a993f17af43b6ce05f719b8edbdc383 100644 (file)
@@ -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'])
index 15f9b0bbc89657860d5e9c632896a725d511e273..c1c02793150ab19ae69f0e1f7f05f7f9e444bdbd 100644 (file)
@@ -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 (file)
index 0000000..9e34918
--- /dev/null
@@ -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 (file)
index 0000000..dbcb5f5
--- /dev/null
@@ -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