add support for parsing of the dynamic section/segment and its tags
authorMike Frysinger <vapier@gentoo.org>
Sat, 23 Mar 2013 06:47:32 +0000 (02:47 -0400)
committerMike Frysinger <vapier@gentoo.org>
Sat, 23 Mar 2013 06:47:32 +0000 (02:47 -0400)
elftools/elf/descriptions.py
elftools/elf/dynamic.py [new file with mode: 0644]
elftools/elf/elffile.py
elftools/elf/enums.py
elftools/elf/structs.py
scripts/readelf.py
test/run_readelf_tests.py

index 2cde281b21e8b9fab5c641710632d500c65d010b..cfd8f6e101577b332ca8a803e316654a8cb15762 100644 (file)
@@ -6,7 +6,9 @@
 # Eli Bendersky (eliben@gmail.com)
 # This code is in the public domain
 #-------------------------------------------------------------------------------
-from .enums import ENUM_E_VERSION, ENUM_RELOC_TYPE_i386, ENUM_RELOC_TYPE_x64
+from .enums import (
+    ENUM_D_TAG, ENUM_E_VERSION, ENUM_RELOC_TYPE_i386, ENUM_RELOC_TYPE_x64
+    )
 from .constants import P_FLAGS, SH_FLAGS
 from ..common.py3compat import iteritems
 
@@ -78,6 +80,9 @@ def describe_reloc_type(x, elffile):
     else:
         return 'unrecognized: %-7x' % (x & 0xFFFFFFFF)
 
+def describe_dyn_tag(x):
+    return _DESCR_D_TAG.get(x, _unknown)
+
 
 #-------------------------------------------------------------------------------
 _unknown = '<unknown>'
@@ -237,4 +242,5 @@ _DESCR_RELOC_TYPE_i386 = dict(
 _DESCR_RELOC_TYPE_x64 = dict(
         (v, k) for k, v in iteritems(ENUM_RELOC_TYPE_x64))
 
-
+_DESCR_D_TAG = dict(
+        (v, k) for k, v in iteritems(ENUM_D_TAG))
diff --git a/elftools/elf/dynamic.py b/elftools/elf/dynamic.py
new file mode 100644 (file)
index 0000000..01c34ea
--- /dev/null
@@ -0,0 +1,105 @@
+#-------------------------------------------------------------------------------
+# elftools: elf/dynamic.py
+#
+# ELF Dynamic Tags
+#
+# Mike Frysinger (vapier@gentoo.org)
+# This code is in the public domain
+#-------------------------------------------------------------------------------
+import itertools
+
+from .sections import Section
+from .segments import Segment
+from ..common.utils import struct_parse
+
+from .enums import ENUM_D_TAG
+
+
+class DynamicTag(object):
+    """ Dynamic Tag object - representing a single dynamic tag entry from a
+        dynamic section.
+
+        Similarly to Section objects, allows dictionary-like access to the
+        dynamic tag.
+    """
+
+    _HANDLED_TAGS = frozenset(['DT_NEEDED', 'DT_RPATH', 'DT_RUNPATH'])
+
+    def __init__(self, entry, elffile):
+        self.entry = entry
+        if entry.d_tag in self._HANDLED_TAGS:
+            dynstr = elffile.get_section_by_name(b'.dynstr')
+            setattr(self, entry.d_tag[3:].lower(), dynstr.get_string(self.entry.d_val))
+
+    def __getitem__(self, name):
+        """ Implement dict-like access to entries
+        """
+        return self.entry[name]
+
+    def __repr__(self):
+        return '<DynamicTag (%s): %r>' % (self.entry.d_tag, self.entry)
+
+    def __str__(self):
+        if self.entry.d_tag in self._HANDLED_TAGS:
+            s = '"%s"' % getattr(self, self.entry.d_tag[3:].lower())
+        else:
+            s = '%#x' % self.entry.d_ptr
+        return '<DynamicTag (%s) %s>' % (self.entry.d_tag, s)
+
+
+class Dynamic(object):
+    def __init__(self, stream, elffile, position):
+        self._stream = stream
+        self._elffile = elffile
+        self._elfstructs = elffile.structs
+        self._num_tags = -1;
+        self._offset = position
+        self._tagsize = self._elfstructs.Elf_Dyn.sizeof()
+
+    def iter_tags(self, type=None):
+        """ Yield all tags (limit to |type| if specified)
+        """
+        for n in itertools.count():
+            tag = self.get_tag(n)
+            if type is None or tag.entry.d_tag == type:
+                yield tag
+            if tag.entry.d_tag == 'DT_NULL':
+                break
+
+    def get_tag(self, n):
+        """ Get the tag at index #n from the file (DynamicTag object)
+        """
+        offset = self._offset + n * self._tagsize
+        entry = struct_parse(
+            self._elfstructs.Elf_Dyn,
+            self._stream,
+            stream_pos=offset)
+        return DynamicTag(entry, self._elffile)
+
+    def num_tags(self):
+        """ Number of dynamic tags in the file
+        """
+        if self._num_tags != -1:
+            return self._num_tags
+
+        for n in itertools.count():
+            tag = self.get_tag(n)
+            if tag.entry.d_tag == 'DT_NULL':
+                self._num_tags = n + 1
+                return self._num_tags
+
+
+class DynamicSection(Section, Dynamic):
+    """ ELF dynamic table section.  Knows how to process the list of tags.
+    """
+    def __init__(self, header, name, stream, elffile):
+        Section.__init__(self, header, name, stream)
+        Dynamic.__init__(self, stream, elffile, self['sh_offset'])
+
+
+class DynamicSegment(Segment, Dynamic):
+    """ ELF dynamic table segment.  Knows how to process the list of tags.
+    """
+    def __init__(self, header, stream, elffile):
+        Segment.__init__(self, header, stream)
+        Dynamic.__init__(self, stream, elffile, self['p_offset'])
index 4814c7f7998571e481784c10bec96ba7df14579f..1d8d6de21245f7764abd1c1aaf583cce7f140a6f 100644 (file)
@@ -13,6 +13,7 @@ from ..construct import ConstructError
 from .structs import ELFStructs
 from .sections import (
         Section, StringTableSection, SymbolTableSection, NullSection)
+from .dynamic import DynamicSection, DynamicSegment
 from .relocation import RelocationSection, RelocationHandler
 from .segments import Segment, InterpSegment
 from .enums import ENUM_RELOC_TYPE_i386, ENUM_RELOC_TYPE_x64
@@ -210,6 +211,8 @@ class ELFFile(object):
         segtype = segment_header['p_type']
         if segtype == 'PT_INTERP':
             return InterpSegment(segment_header, self.stream)
+        elif segtype == 'PT_DYNAMIC':
+            return DynamicSegment(segment_header, self.stream, self)
         else:
             return Segment(segment_header, self.stream)
 
@@ -243,6 +246,8 @@ class ELFFile(object):
         elif sectype in ('SHT_REL', 'SHT_RELA'):
             return RelocationSection(
                 section_header, name, self.stream, self)
+        elif sectype == 'SHT_DYNAMIC':
+            return DynamicSection(section_header, name, self.stream, self)
         else:
             return Section(section_header, name, self.stream)
 
index 373706c5765b0608c0afb3d8389f3ccdbfdb2a95..ee304058e166ed312b11fe839e385b140a7fd20e 100644 (file)
@@ -187,6 +187,82 @@ ENUM_ST_SHNDX = dict(
     _default_=Pass,
 )
 
+# d_tag
+ENUM_D_TAG = dict(
+    DT_NULL=0,
+    DT_NEEDED=1,
+    DT_PLTRELSZ=2,
+    DT_PLTGOT=3,
+    DT_HASH=4,
+    DT_STRTAB=5,
+    DT_SYMTAB=6,
+    DT_RELA=7,
+    DT_RELASZ=8,
+    DT_RELAENT=9,
+    DT_STRSZ=10,
+    DT_SYMENT=11,
+    DT_INIT=12,
+    DT_FINI=13,
+    DT_SONAME=14,
+    DT_RPATH=15,
+    DT_SYMBOLIC=16,
+    DT_REL=17,
+    DT_RELSZ=18,
+    DT_RELENT=19,
+    DT_PLTREL=20,
+    DT_DEBUG=21,
+    DT_TEXTREL=22,
+    DT_JMPREL=23,
+    DT_BIND_NOW=24,
+    DT_INIT_ARRAY=25,
+    DT_FINI_ARRAY=26,
+    DT_INIT_ARRAYSZ=27,
+    DT_FINI_ARRAYSZ=28,
+    DT_RUNPATH=29,
+    DT_FLAGS=30,
+    DT_ENCODING=32,
+    DT_PREINIT_ARRAY=32,
+    DT_PREINIT_ARRAYSZ=33,
+    DT_NUM=34,
+    DT_LOOS=0x6000000d,
+    DT_HIOS=0x6ffff000,
+    DT_LOPROC=0x70000000,
+    DT_HIPROC=0x7fffffff,
+    DT_PROCNUM=0x35,
+    DT_VALRNGLO=0x6ffffd00,
+    DT_GNU_PRELINKED=0x6ffffdf5,
+    DT_GNU_CONFLICTSZ=0x6ffffdf6,
+    DT_GNU_LIBLISTSZ=0x6ffffdf7,
+    DT_CHECKSUM=0x6ffffdf8,
+    DT_PLTPADSZ=0x6ffffdf9,
+    DT_MOVEENT=0x6ffffdfa,
+    DT_MOVESZ=0x6ffffdfb,
+    DT_SYMINSZ=0x6ffffdfe,
+    DT_SYMINENT=0x6ffffdff,
+    DT_GNU_HASH=0x6ffffef5,
+    DT_TLSDESC_PLT=0x6ffffef6,
+    DT_TLSDESC_GOT=0x6ffffef7,
+    DT_GNU_CONFLICT=0x6ffffef8,
+    DT_GNU_LIBLIST=0x6ffffef9,
+    DT_CONFIG=0x6ffffefa,
+    DT_DEPAUDIT=0x6ffffefb,
+    DT_AUDIT=0x6ffffefc,
+    DT_PLTPAD=0x6ffffefd,
+    DT_MOVETAB=0x6ffffefe,
+    DT_SYMINFO=0x6ffffeff,
+    DT_VERSYM=0x6ffffff0,
+    DT_RELACOUNT=0x6ffffff9,
+    DT_RELCOUNT=0x6ffffffa,
+    DT_FLAGS_1=0x6ffffffb,
+    DT_VERDEF=0x6ffffffc,
+    DT_VERDEFNUM=0x6ffffffd,
+    DT_VERNEED=0x6ffffffe,
+    DT_VERNEEDNUM=0x6fffffff,
+    DT_AUXILIARY=0x7ffffffd,
+    DT_FILTER=0x7fffffff,
+    _default_=Pass,
+)
+
 ENUM_RELOC_TYPE_i386 = dict(
     R_386_NONE=0,
     R_386_32=1,
index 2c55d5bb40feb9b87d8c2558532ce2be2c0170b5..08567de4fbe4c86b2f417d07f06f34b1dace8d12 100644 (file)
@@ -72,6 +72,7 @@ class ELFStructs(object):
         self._create_shdr()
         self._create_sym()
         self._create_rel()
+        self._create_dyn()
     
     def _create_ehdr(self):
         self.Elf_Ehdr = Struct('Elf_Ehdr',
@@ -165,6 +166,13 @@ class ELFStructs(object):
             self.Elf_sxword('r_addend'),
         )
 
+    def _create_dyn(self):
+        self.Elf_Dyn = Struct('Elf_Dyn',
+            Enum(self.Elf_sxword('d_tag'), **ENUM_D_TAG),
+            self.Elf_xword('d_val'),
+            Value('d_ptr', lambda ctx: ctx['d_val']),
+        )
+
     def _create_sym(self):
         # st_info is hierarchical. To access the type, use
         # container['st_info']['type']
index 77809cfda1366dfea9c82774d7b00f878fdb2ace..b9b07a3359c7ed5b403d05d0214d9f89b7703ae0 100755 (executable)
@@ -23,6 +23,8 @@ from elftools.common.exceptions import ELFError
 from elftools.common.py3compat import (
         ifilter, byte2int, bytes2str, itervalues, str2bytes)
 from elftools.elf.elffile import ELFFile
+from elftools.elf.dynamic import DynamicSection, DynamicSegment
+from elftools.elf.enums import ENUM_D_TAG
 from elftools.elf.segments import InterpSegment
 from elftools.elf.sections import SymbolTableSection
 from elftools.elf.relocation import RelocationSection
@@ -32,7 +34,7 @@ from elftools.elf.descriptions import (
     describe_e_version_numeric, describe_p_type, describe_p_flags,
     describe_sh_type, describe_sh_flags,
     describe_symbol_type, describe_symbol_bind, describe_symbol_visibility,
-    describe_symbol_shndx, describe_reloc_type,
+    describe_symbol_shndx, describe_reloc_type, describe_dyn_tag,
     )
 from elftools.dwarf.dwarfinfo import DWARFInfo
 from elftools.dwarf.descriptions import (
@@ -283,6 +285,49 @@ class ReadElf(object):
                     describe_symbol_shndx(symbol['st_shndx']),
                     bytes2str(symbol.name)))
 
+    def display_dynamic_tags(self):
+        """ Display the dynamic tags contained in the file
+        """
+        has_dynamic_section = False
+        for section in self.elffile.iter_sections():
+            if not isinstance(section, DynamicSection):
+                continue
+
+            has_relocation_sections = True
+            self._emitline("\nDynamic section at offset %s contains %s entries:" % (
+                self._format_hex(section['sh_offset']),
+                section.num_tags()))
+            self._emitline("  Tag        Type                         Name/Value")
+
+            hexwidth = 8 if self.elffile.elfclass == 32 else 16
+            padding = 20 + (8 if self.elffile.elfclass == 32 else 0)
+            for tag in section.iter_tags():
+                if tag.entry.d_tag == 'DT_NEEDED':
+                    parsed = 'Shared library: [%s]' % tag.needed
+                elif tag.entry.d_tag == 'DT_RPATH':
+                    parsed = 'Library rpath: [%s]' % tag.rpath
+                elif tag.entry.d_tag == 'DT_RUNPATH':
+                    parsed = 'Library runpath: [%s]' % tag.runpath
+                elif (tag.entry.d_tag.endswith('SZ') or
+                      tag.entry.d_tag.endswith('ENT')):
+                    parsed = '%i (bytes)' % tag['d_val']
+                elif tag.entry.d_tag.endswith('NUM'):
+                    parsed = '%i' % tag['d_val']
+                elif tag.entry.d_tag == 'DT_PLTREL':
+                    s = describe_dyn_tag(tag.entry.d_val)
+                    if s.startswith('DT_'):
+                        s = s[3:]
+                    parsed = '%s' % s
+                else:
+                    parsed = '%#x' % tag['d_val']
+
+                self._emitline(" %s %-*s %s" % (
+                    self._format_hex(ENUM_D_TAG.get(tag.entry.d_tag, tag.entry.d_tag),
+                        fieldsize=hexwidth, lead0x=True),
+                    padding,
+                    '(%s)' % (tag.entry.d_tag[3:],),
+                    parsed))
+
     def display_relocations(self):
         """ Display the relocations contained in the file
         """
@@ -727,6 +772,9 @@ def main(stream=None):
             add_help_option=False, # -h is a real option of readelf
             prog='readelf.py',
             version=VERSION_STRING)
+    optparser.add_option('-d', '--dynamic',
+            action='store_true', dest='show_dynamic_tags',
+            help='Display the dynamic section')
     optparser.add_option('-H', '--help',
             action='store_true', dest='help',
             help='Display this information')
@@ -784,6 +832,8 @@ def main(stream=None):
             if do_program_header:
                 readelf.display_program_headers(
                         show_heading=not do_file_header)
+            if options.show_dynamic_tags:
+                readelf.display_dynamic_tags()
             if options.show_symbols:
                 readelf.display_symbol_tables()
             if options.show_relocs:
index 984b7a550443c3631b3c3ee0413e4baecd26dc47..1adec09fa75564ff9d6a0ff7874300c50d116975 100755 (executable)
@@ -40,7 +40,7 @@ def run_test_on_file(filename, verbose=False):
     success = True
     testlog.info("Test file '%s'" % filename)
     for option in [
-            '-e', '-s', '-r', '-x.text', '-p.shstrtab',
+            '-e', '-d', '-s', '-r', '-x.text', '-p.shstrtab',
             '--debug-dump=info', '--debug-dump=decodedline',
             '--debug-dump=frames', '--debug-dump=frames-interp']:
         if verbose: testlog.info("..option='%s'" % option)