From 5699e18d7be7c4edaa036a312f8fe16dc374e3db Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Sat, 23 Mar 2013 02:47:32 -0400 Subject: [PATCH] add support for parsing of the dynamic section/segment and its tags --- elftools/elf/descriptions.py | 10 +++- elftools/elf/dynamic.py | 105 +++++++++++++++++++++++++++++++++++ elftools/elf/elffile.py | 5 ++ elftools/elf/enums.py | 76 +++++++++++++++++++++++++ elftools/elf/structs.py | 8 +++ scripts/readelf.py | 52 ++++++++++++++++- test/run_readelf_tests.py | 2 +- 7 files changed, 254 insertions(+), 4 deletions(-) create mode 100644 elftools/elf/dynamic.py diff --git a/elftools/elf/descriptions.py b/elftools/elf/descriptions.py index 2cde281..cfd8f6e 100644 --- a/elftools/elf/descriptions.py +++ b/elftools/elf/descriptions.py @@ -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 = '' @@ -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 index 0000000..01c34ea --- /dev/null +++ b/elftools/elf/dynamic.py @@ -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 '' % (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 '' % (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']) diff --git a/elftools/elf/elffile.py b/elftools/elf/elffile.py index 4814c7f..1d8d6de 100644 --- a/elftools/elf/elffile.py +++ b/elftools/elf/elffile.py @@ -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) diff --git a/elftools/elf/enums.py b/elftools/elf/enums.py index 373706c..ee30405 100644 --- a/elftools/elf/enums.py +++ b/elftools/elf/enums.py @@ -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, diff --git a/elftools/elf/structs.py b/elftools/elf/structs.py index 2c55d5b..08567de 100644 --- a/elftools/elf/structs.py +++ b/elftools/elf/structs.py @@ -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'] diff --git a/scripts/readelf.py b/scripts/readelf.py index 77809cf..b9b07a3 100755 --- a/scripts/readelf.py +++ b/scripts/readelf.py @@ -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: diff --git a/test/run_readelf_tests.py b/test/run_readelf_tests.py index 984b7a5..1adec09 100755 --- a/test/run_readelf_tests.py +++ b/test/run_readelf_tests.py @@ -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) -- 2.30.2