From: Yann Rouillard Date: Mon, 27 May 2013 20:44:28 +0000 (+0200) Subject: added support for version definition, version dependency and version symbol sections... X-Git-Tag: v0.22~48 X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=7b24670deb538b916a9264641517a0031bd7fb6c;p=pyelftools.git added support for version definition, version dependency and version symbol sections in pyelftools and readelf.py --- diff --git a/elftools/elf/constants.py b/elftools/elf/constants.py index 8387c0f..85b4c20 100644 --- a/elftools/elf/constants.py +++ b/elftools/elf/constants.py @@ -62,3 +62,8 @@ class SUNW_SYMINFO_FLAGS(object): SYMINFO_FLG_INTERPOSE=0x80 SYMINFO_FLG_CAP=0x100 SYMINFO_FLG_DEFERRED=0x200 + +class VER_FLAGS(object): + VER_FLG_BASE=0x1 + VER_FLG_WEAK=0x2 + VER_FLG_INFO=0x4 diff --git a/elftools/elf/descriptions.py b/elftools/elf/descriptions.py index c1cda6a..f760dfb 100644 --- a/elftools/elf/descriptions.py +++ b/elftools/elf/descriptions.py @@ -9,7 +9,7 @@ from .enums import ( ENUM_D_TAG, ENUM_E_VERSION, ENUM_RELOC_TYPE_i386, ENUM_RELOC_TYPE_x64 ) -from .constants import P_FLAGS, SH_FLAGS, SUNW_SYMINFO_FLAGS +from .constants import P_FLAGS, SH_FLAGS, SUNW_SYMINFO_FLAGS, VER_FLAGS from ..common.py3compat import iteritems @@ -103,6 +103,18 @@ def describe_syminfo_flags(x): def describe_symbol_boundto(x): return _DESCR_SYMINFO_BOUNDTO.get(x, '%3s' % x) +def describe_ver_flags(x): + s = '' + for flag in ( + VER_FLAGS.VER_FLG_WEAK, + VER_FLAGS.VER_FLG_BASE, + VER_FLAGS.VER_FLG_INFO): + if x & flag: + if s: + s += ' | ' + s += _DESCR_VER_FLAGS[flag] + return s + #------------------------------------------------------------------------------- _unknown = '' @@ -275,6 +287,13 @@ _DESCR_SYMINFO_BOUNDTO = dict( SYMINFO_BT_EXTERN='', ) +_DESCR_VER_FLAGS = { + 0: '', + VER_FLAGS.VER_FLG_BASE: 'BASE', + VER_FLAGS.VER_FLG_WEAK: 'WEAK', + VER_FLAGS.VER_FLG_INFO: 'INFO', +} + _DESCR_RELOC_TYPE_i386 = dict( (v, k) for k, v in iteritems(ENUM_RELOC_TYPE_i386)) diff --git a/elftools/elf/elffile.py b/elftools/elf/elffile.py index ccb7e56..67b39af 100644 --- a/elftools/elf/elffile.py +++ b/elftools/elf/elffile.py @@ -13,7 +13,9 @@ from ..construct import ConstructError from .structs import ELFStructs from .sections import ( Section, StringTableSection, SymbolTableSection, - SUNWSyminfoTableSection, NullSection) + SUNWSyminfoTableSection, VerneedTableSection, + VerdefTableSection, VersymTableSection, + NullSection) from .dynamic import DynamicSection, DynamicSegment from .relocation import RelocationSection, RelocationHandler from .segments import Segment, InterpSegment @@ -248,6 +250,12 @@ class ELFFile(object): return self._make_symbol_table_section(section_header, name) elif sectype == 'SHT_SUNW_syminfo': return self._make_sunwsyminfo_table_section(section_header, name) + elif sectype == 'SHT_GNU_verneed': + return self._make_verneed_table_section(section_header, name) + elif sectype == 'SHT_GNU_verdef': + return self._make_verdef_table_section(section_header, name) + elif sectype == 'SHT_GNU_versym': + return self._make_versym_table_section(section_header, name) elif sectype in ('SHT_REL', 'SHT_RELA'): return RelocationSection( section_header, name, self.stream, self) @@ -276,6 +284,36 @@ class ELFFile(object): elffile=self, symboltable=strtab_section) + def _make_verneed_table_section(self, section_header, name): + """ Create a VerneedTableSection + """ + linked_strtab_index = section_header['sh_link'] + strtab_section = self.get_section(linked_strtab_index) + return VerneedTableSection( + section_header, name, self.stream, + elffile=self, + stringtable=strtab_section) + + def _make_verdef_table_section(self, section_header, name): + """ Create a VerdefTableSection + """ + linked_strtab_index = section_header['sh_link'] + strtab_section = self.get_section(linked_strtab_index) + return VerdefTableSection( + section_header, name, self.stream, + elffile=self, + stringtable=strtab_section) + + def _make_versym_table_section(self, section_header, name): + """ Create a VersymTableSection + """ + linked_strtab_index = section_header['sh_link'] + strtab_section = self.get_section(linked_strtab_index) + return VersymTableSection( + section_header, name, self.stream, + elffile=self, + symboltable=strtab_section) + def _get_segment_header(self, n): """ Find the header of segment #n, parse it and return the struct """ diff --git a/elftools/elf/enums.py b/elftools/elf/enums.py index 121c287..5fd8238 100644 --- a/elftools/elf/enums.py +++ b/elftools/elf/enums.py @@ -184,9 +184,9 @@ ENUM_SH_TYPE = dict( SHT_NUM=19, SHT_LOOS=0x60000000, SHT_GNU_HASH=0x6ffffff6, - SHT_GNU_verdef=0x6ffffffd, - SHT_GNU_verneed=0x6ffffffe, - SHT_GNU_versym=0x6fffffff, + SHT_GNU_verdef=0x6ffffffd, # also SHT_SUNW_verdef + SHT_GNU_verneed=0x6ffffffe, # also SHT_SUNW_verneed + SHT_GNU_versym=0x6fffffff, # also SHT_SUNW_versym SHT_LOPROC=0x70000000, SHT_HIPROC=0x7fffffff, SHT_LOUSER=0x80000000, @@ -456,3 +456,20 @@ ENUM_SUNW_SYMINFO_BOUNDTO = dict( _default_=Pass, ) +# Versym section, version dependency index +ENUM_VERSYM = dict( + VER_NDX_LOCAL=0, + VER_NDX_GLOBAL=1, + VER_NDX_LORESERVE=0xff00, + VER_NDX_ELIMINATE=0xff01, + _default_=Pass, +) +# Sunw Syminfo Bound To special values +ENUM_SUNW_SYMINFO_BOUNDTO = dict( + SYMINFO_BT_SELF=0xffff, + SYMINFO_BT_PARENT=0xfffe, + SYMINFO_BT_NONE=0xfffd, + SYMINFO_BT_EXTERN=0xfffc, + _default_=Pass, +) + diff --git a/elftools/elf/sections.py b/elftools/elf/sections.py index ce62450..8534144 100644 --- a/elftools/elf/sections.py +++ b/elftools/elf/sections.py @@ -158,3 +158,245 @@ class SUNWSyminfoTableSection(Section): """ for i in range(1, self.num_symbols() + 1): yield self.get_symbol(i) + + +class Version(object): + """ Version object - representing a version definition or dependency + entry from a "Version Needed" or a "Version Dependency" table section. + + This kind of entry contains a pointer to an array of auxiliary entries + that store the information about version names or dependencies. + These entries are not stored in this object and should be accessed + through the appropriate method of a section object which will return + an iterator of VersionAuxiliary objects. + + Similarly to Section objects, allows dictionary-like access to + verdef/verneed entry + """ + def __init__(self, entry, name=None): + self.entry = entry + self.name = name + + def __getitem__(self, name): + """ Implement dict-like access to entry + """ + return self.entry[name] + + +class VersionAuxiliary(object): + """ Version Auxiliary object - representing an auxiliary entry of a version + definition or dependency entry + + Similarly to Section objects, allows dictionary-like access to the + verdaux/vernaux entry + """ + def __init__(self, entry, name): + self.entry = entry + self.name = name + + def __getitem__(self, name): + """ Implement dict-like access to entries + """ + return self.entry[name] + +class VerneedTableSection(Section): + """ ELF SUNW or GNU Version Needed table section. + Has an associated StringTableSection that's passed in the constructor. + """ + def __init__(self, header, name, stream, elffile, stringtable): + super(VerneedTableSection, self).__init__(header, name, stream) + self.elffile = elffile + self.elfstructs = self.elffile.structs + self.stringtable = stringtable + self._has_indexes = None + + def num_versions(self): + """ Number of version dependency in the table + """ + return self['sh_info'] + + def has_indexes(self): + """ Return True if at least one version definition entry has an index + that is stored in the vna_other field. + This information is used for symbol versioning + """ + if self._has_indexes is None: + self._has_indexes = False + for _, vernaux_iter in self.iter_versions(): + for vernaux in vernaux_iter: + if vernaux['vna_other']: + self._has_indexes = True + break + + return self._has_indexes + + def get_version(self, index): + """ Get the version information located at index #n in the table + Return boths the verneed structure and the vernaux structure + that contains the name of the version + """ + for verneed, vernaux_iter in self.iter_versions(): + for vernaux in vernaux_iter: + if vernaux['vna_other'] == index: + return verneed, vernaux + + return None + + + def _iter_version_auxiliaries(self, entry_offset, count): + """ Yield all auxiliary entries of a version dependency + """ + for _ in range(count): + entry = struct_parse( + self.elfstructs.Elf_Vernaux, + self.stream, + stream_pos=entry_offset) + + name = self.stringtable.get_string(entry['vna_name']) + version_aux = VersionAuxiliary(entry, name) + yield version_aux + + if not entry['vna_next']: + break + + entry_offset += entry['vna_next'] + + + def iter_versions(self): + """ Yield all the version dependencies entries in the table + Each time it returns the main version dependency structure + and an iterator to walk through its auxiliaries entries + """ + entry_offset = self['sh_offset'] + for _ in range(self.num_versions()): + entry = struct_parse( + self.elfstructs.Elf_Verneed, + self.stream, + stream_pos=entry_offset) + + name = self.stringtable.get_string(entry['vn_file']) + elf_assert(entry['vn_cnt'] > 0, + 'Expected number of version names to be > 0 for version definition %s' % name) + + verneed = Version(entry, name) + aux_entries_offset = entry_offset + entry['vn_aux'] + vernaux_iter = self._iter_version_auxiliaries(aux_entries_offset, + entry['vn_cnt']) + yield verneed, vernaux_iter + + if not entry['vn_next']: + break + + entry_offset += entry['vn_next'] + + +class VerdefTableSection(Section): + """ ELF SUNW or GNU Version Definition table section. + Has an associated StringTableSection that's passed in the constructor. + """ + def __init__(self, header, name, stream, elffile, stringtable): + super(VerdefTableSection, self).__init__(header, name, stream) + self.elffile = elffile + self.elfstructs = self.elffile.structs + self.stringtable = stringtable + + def num_versions(self): + """ Number of version definitions in the table + """ + return self['sh_info'] + + def get_version(self, index): + """ Get the version information located at index #n in the table + Return boths the verdef structure and an iterator to retrieve + both the version names and dependencies in the form of + verdaux entries + """ + for verdef, verdaux_iter in self.iter_versions(): + if verdef['vd_ndx'] == index: + return verdef, verdaux_iter + + return None + + def _iter_version_auxiliaries(self, entry_offset, count): + """ Yield all auxiliary entries of a version definition + """ + for _ in range(count): + entry = struct_parse( + self.elfstructs.Elf_Verdaux, + self.stream, + stream_pos=entry_offset) + + name = self.stringtable.get_string(entry['vda_name']) + vernaux = VersionAuxiliary(entry, name) + yield vernaux + + if not entry['vda_next']: + break + + entry_offset += entry['vda_next'] + + + def iter_versions(self): + """ Yield all the version definition entries in the table + Each time it returns the main version definition structure + and an iterator to walk through its auxiliaries entries + """ + entry_offset = self['sh_offset'] + for _ in range(self.num_versions()): + entry = struct_parse( + self.elfstructs.Elf_Verdef, + self.stream, + stream_pos=entry_offset) + + elf_assert(entry['vd_cnt'] > 0, + 'Expected number of version names to be > 0' + 'for version definition at index %i' % entry['vd_ndx']) + + verdef = Version(entry) + aux_entries_offset = entry_offset + entry['vd_aux'] + verdaux_iter = self._iter_version_auxiliaries(aux_entries_offset, + entry['vd_cnt']) + yield verdef, verdaux_iter + + if not entry['vd_next']: + break + + entry_offset += entry['vd_next'] + + +class VersymTableSection(Section): + """ ELF SUNW or GNU Versym table section. + Has an associated SymbolTableSection that's passed in the constructor. + """ + def __init__(self, header, name, stream, elffile, symboltable): + super(VersymTableSection, self).__init__(header, name, stream) + self.elffile = elffile + self.elfstructs = self.elffile.structs + self.symboltable = symboltable + + def num_symbols(self): + """ Number of symbols in the table + """ + return self['sh_size'] // self['sh_entsize'] + + def get_symbol(self, n): + """ Get the symbol at index #n from the table (Symbol object) + It begins at 1 and not 0 since the first entry is used to + store the current version of the syminfo table + """ + # Grab the symbol's entry from the stream + entry_offset = self['sh_offset'] + n * self['sh_entsize'] + entry = struct_parse( + self.elfstructs.Elf_Versym, + self.stream, + stream_pos=entry_offset) + # Find the symbol name in the associated symbol table + name = self.symboltable.get_symbol(n).name + return Symbol(entry, name) + + def iter_symbols(self): + """ Yield all the symbols in the table + """ + for i in range(self.num_symbols()): + yield self.get_symbol(i) + diff --git a/elftools/elf/structs.py b/elftools/elf/structs.py index 72d07fa..fba2503 100644 --- a/elftools/elf/structs.py +++ b/elftools/elf/structs.py @@ -74,6 +74,9 @@ class ELFStructs(object): self._create_rel() self._create_dyn() self._create_sunw_syminfo() + self._create_version_needed() + self._create_version_definition() + self._create_version_symbol() def _create_ehdr(self): self.Elf_Ehdr = Struct('Elf_Ehdr', @@ -209,3 +212,45 @@ class ELFStructs(object): Enum(self.Elf_half('si_boundto'), **ENUM_SUNW_SYMINFO_BOUNDTO), self.Elf_half('si_flags'), ) + + def _create_version_needed(self): + # Structure of "version needed" entries is documented in + # Oracle "Linker and Libraries Guide", Chapter 7 Object File Format + self.Elf_Verneed = Struct('Elf_Verneed', + self.Elf_half('vn_version'), + self.Elf_half('vn_cnt'), + self.Elf_word('vn_file'), + self.Elf_word('vn_aux'), + self.Elf_word('vn_next'), + ) + self.Elf_Vernaux = Struct('Elf_Vernaux', + self.Elf_word('vna_hash'), + self.Elf_half('vna_flags'), + self.Elf_half('vna_other'), + self.Elf_word('vna_name'), + self.Elf_word('vna_next'), + ) + + def _create_version_definition(self): + # Structure off "version definition" entries are documented in + # Oracle "Linker and Libraries Guide", Chapter 7 Object File Format + self.Elf_Verdef = Struct('Elf_Verdef', + self.Elf_half('vd_version'), + self.Elf_half('vd_flags'), + self.Elf_half('vd_ndx'), + self.Elf_half('vd_cnt'), + self.Elf_word('vd_hash'), + self.Elf_word('vd_aux'), + self.Elf_word('vd_next'), + ) + self.Elf_Verdaux = Struct('Elf_Verdaux', + self.Elf_word('vda_name'), + self.Elf_word('vda_next'), + ) + + def _create_version_symbol(self): + # Structure off "version symbol" entries are documented in + # Oracle "Linker and Libraries Guide", Chapter 7 Object File Format + self.Elf_Versym = Struct('Elf_Versym', + Enum(self.Elf_half('ndx'), **ENUM_VERSYM), + ) diff --git a/scripts/readelf.py b/scripts/readelf.py index 720106d..b437a5f 100755 --- a/scripts/readelf.py +++ b/scripts/readelf.py @@ -24,7 +24,10 @@ 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.sections import ( + SymbolTableSection, VersymTableSection, + VerdefTableSection, VerneedTableSection, + ) from elftools.elf.relocation import RelocationSection from elftools.elf.descriptions import ( describe_ei_class, describe_ei_data, describe_ei_version, @@ -33,6 +36,7 @@ from elftools.elf.descriptions import ( describe_sh_type, describe_sh_flags, describe_symbol_type, describe_symbol_bind, describe_symbol_visibility, describe_symbol_shndx, describe_reloc_type, describe_dyn_tag, + describe_ver_flags, ) from elftools.dwarf.dwarfinfo import DWARFInfo from elftools.dwarf.descriptions import ( @@ -61,6 +65,9 @@ class ReadElf(object): # Lazily initialized if a debug dump is requested self._dwarfinfo = None + self._versioninfo = None + + def display_file_header(self): """ Display the ELF file header """ @@ -254,6 +261,8 @@ class ReadElf(object): def display_symbol_tables(self): """ Display the symbol tables contained in the file """ + self._init_versioninfo() + for section in self.elffile.iter_sections(): if not isinstance(section, SymbolTableSection): continue @@ -263,6 +272,7 @@ class ReadElf(object): bytes2str(section.name))) continue + self._emitline("\nSymbol table '%s' contains %s entries:" % ( bytes2str(section.name), section.num_symbols())) @@ -272,8 +282,27 @@ class ReadElf(object): self._emitline(' Num: Value Size Type Bind Vis Ndx Name') for nsym, symbol in enumerate(section.iter_symbols()): + + version_info = '' + # readelf doesn't display version info for Solaris versioning + if (section['sh_type'] == 'SHT_DYNSYM' and + self._versioninfo['type'] == 'GNU'): + version = self._symbol_version(nsym) + if (version['name'] != bytes2str(symbol.name) and + version['index'] not in ('VER_NDX_LOCAL', + 'VER_NDX_GLOBAL')): + if version['filename']: + # external symbol + version_info = '@%(name)s (%(index)i)' % version + else: + # internal symbol + if version['hidden']: + version_info = '@%(name)s' % version + else: + version_info = '@@%(name)s' % version + # symbol names are truncated to 25 chars, similarly to readelf - self._emitline('%6d: %s %5d %-7s %-6s %-7s %4s %.25s' % ( + self._emitline('%6d: %s %5d %-7s %-6s %-7s %4s %.25s%s' % ( nsym, self._format_hex(symbol['st_value'], fullhex=True, lead0x=False), symbol['st_size'], @@ -281,7 +310,8 @@ class ReadElf(object): describe_symbol_bind(symbol['st_info']['bind']), describe_symbol_visibility(symbol['st_other']['visibility']), describe_symbol_shndx(symbol['st_shndx']), - bytes2str(symbol.name))) + bytes2str(symbol.name), + version_info)) def display_dynamic_tags(self): """ Display the dynamic tags contained in the file @@ -384,6 +414,114 @@ class ReadElf(object): if not has_relocation_sections: self._emitline('\nThere are no relocations in this file.') + def display_version_info(self): + """ Display the version info contained in the file + """ + self._init_versioninfo() + + if not self._versioninfo['type']: + self._emitline("\nNo version information found in this file.") + return + + for section in self.elffile.iter_sections(): + if isinstance(section, VersymTableSection): + + self._print_version_section_header( + section, 'Version symbols', lead0x=False) + + num_symbols = section.num_symbols() + + # Symbol version info are printed four by four entries + for idx_by_4 in range(0, num_symbols, 4): + + self._emit(' %03x:' % idx_by_4) + + for idx in range(idx_by_4, min(idx_by_4 + 4, num_symbols)): + + symbol_version = self._symbol_version(idx) + if symbol_version['index'] == 'VER_NDX_LOCAL': + version_index = 0 + version_name = '(*local*)' + elif symbol_version['index'] == 'VER_NDX_GLOBAL': + version_index = 1 + version_name = '(*global*)' + else: + version_index = symbol_version['index'] + version_name = '(%(name)s)' % symbol_version + + visibility = 'h' if symbol_version['hidden'] else ' ' + + self._emit('%4x%s%-13s' % ( + version_index, visibility, version_name)) + + self._emitline() + + elif isinstance(section, VerdefTableSection): + + self._print_version_section_header( + section, 'Version definition', indent=2) + + offset = 0 + for verdef, verdaux_iter in section.iter_versions(): + verdaux = next(verdaux_iter) + + name = verdaux.name + if verdef['vd_flags']: + flags = describe_ver_flags(verdef['vd_flags']) + # Mimic exactly the readelf output + flags += ' ' + else: + flags = 'none' + + self._emitline(' %s: Rev: %i Flags: %s Index: %i' + ' Cnt: %i Name: %s' % ( + self._format_hex(offset, fieldsize=6, + alternate=True), + verdef['vd_version'], flags, verdef['vd_ndx'], + verdef['vd_cnt'], bytes2str(name))) + + verdaux_offset = ( + offset + verdef['vd_aux'] + verdaux['vda_next']) + for idx, verdaux in enumerate(verdaux_iter, start=1): + self._emitline(' %s: Parent %i: %s' % + (self._format_hex(verdaux_offset, fieldsize=4), + idx, bytes2str(verdaux.name))) + verdaux_offset += verdaux['vda_next'] + + offset += verdef['vd_next'] + + elif isinstance(section, VerneedTableSection): + + self._print_version_section_header(section, 'Version needs') + + offset = 0 + for verneed, verneed_iter in section.iter_versions(): + + self._emitline(' %s: Version: %i File: %s Cnt: %i' % ( + self._format_hex(offset, fieldsize=6, + alternate=True), + verneed['vn_version'], bytes2str(verneed.name), + verneed['vn_cnt'])) + + vernaux_offset = offset + verneed['vn_aux'] + for idx, vernaux in enumerate(verneed_iter, start=1): + if vernaux['vna_flags']: + flags = describe_ver_flags(vernaux['vna_flags']) + # Mimic exactly the readelf output + flags += ' ' + else: + flags = 'none' + + self._emitline( + ' %s: Name: %s Flags: %s Version: %i' % ( + self._format_hex(vernaux_offset, fieldsize=4), + bytes2str(vernaux.name), flags, + vernaux['vna_other'])) + + vernaux_offset += vernaux['vna_next'] + + offset += verneed['vn_next'] + def display_hex_dump(self, section_spec): """ Display a hex dump of a section. section_spec is either a section number or a name. @@ -486,7 +624,8 @@ class ReadElf(object): else: self._emitline('debug dump not yet supported for "%s"' % dump_what) - def _format_hex(self, addr, fieldsize=None, fullhex=False, lead0x=True): + def _format_hex(self, addr, fieldsize=None, fullhex=False, lead0x=True, + alternate=False): """ Format an address into a hexadecimal string. fieldsize: @@ -501,7 +640,20 @@ class ReadElf(object): lead0x: If True, leading 0x is added + + alternate: + If True, override lead0x to emulate the alternate + hexadecimal form specified in format string with the # + character: only non-zero values are prefixed with 0x. + This form is used by readelf. """ + if alternate: + if addr == 0: + lead0x = False + else: + lead0x = True + fieldsize -= 2 + s = '0x' if lead0x else '' if fullhex: fieldsize = 8 if self.elffile.elfclass == 32 else 16 @@ -511,6 +663,95 @@ class ReadElf(object): field = '%' + '0%sx' % fieldsize return s + field % addr + + def _print_version_section_header(self, version_section, name, lead0x=True, indent=1): + """ Print a section header of one version related section (versym, verneed or verdef) + with some options to accomodate readelf little differences between each header + (e.g. indentation and 0x prefixing). + """ + if hasattr(version_section, 'num_versions'): + num_entries = version_section.num_versions() + else: + num_entries = version_section.num_symbols() + + self._emitline("\n%s section '%s' contains %s entries:" % + (name, bytes2str(version_section.name), num_entries)) + self._emitline('%sAddr: %s Offset: %s Link: %i (%s)' % + (' ' * indent, + self._format_hex(version_section['sh_addr'], fieldsize=16, lead0x=lead0x), + self._format_hex(version_section['sh_offset'], fieldsize=6, lead0x=True), + version_section['sh_link'], + bytes2str(self.elffile.get_section(version_section['sh_link']).name))) + + + + def _init_versioninfo(self): + """ Search and initialize informations about version related sections + and the kind of versioning used (GNU or Solaris). + """ + if self._versioninfo is not None: + return + + self._versioninfo = { 'versym': None, 'verdef': None, + 'verneed': None, 'type': None } + + for section in self.elffile.iter_sections(): + if isinstance(section, VersymTableSection): + self._versioninfo['versym'] = section + elif isinstance(section, VerdefTableSection): + self._versioninfo['verdef'] = section + elif isinstance(section, VerneedTableSection): + self._versioninfo['verneed'] = section + elif isinstance(section, DynamicSection): + for tag in section.iter_tags(): + if tag['d_tag'] == 'DT_VERSYM': + self._versioninfo['type'] = 'GNU' + break + + if not self._versioninfo['type'] and ( + self._versioninfo['verneed'] or self._versioninfo['verdef']): + self._versioninfo['type'] = 'Solaris' + + + def _symbol_version(self, nsym): + """ Return a dict containing information on the + or None if no version information is available + """ + self._init_versioninfo() + + symbol_version = dict.fromkeys(('index', 'name', 'filename', 'hidden')) + + if (not self._versioninfo['versym'] or + nsym >= self._versioninfo['versym'].num_symbols()): + return None + + symbol = self._versioninfo['versym'].get_symbol(nsym) + index = symbol.entry['ndx'] + if not index in ('VER_NDX_LOCAL', 'VER_NDX_GLOBAL'): + index = int(index) + + if self._versioninfo['type'] == 'GNU': + # In GNU versioning mode, the highest bit is used to + # store wether the symbol is hidden or not + if index & 0x8000: + index &= ~0x8000; + symbol_version['hidden'] = True + + if (self._versioninfo['verdef'] and + index <= self._versioninfo['verdef'].num_versions()): + _, verdaux_iter = \ + self._versioninfo['verdef'].get_version(index) + symbol_version['name'] = bytes2str(next(verdaux_iter).name) + else: + verneed, vernaux = \ + self._versioninfo['verneed'].get_version(index) + symbol_version['name'] = bytes2str(vernaux.name) + symbol_version['filename'] = bytes2str(verneed.name) + + symbol_version['index'] = index + return symbol_version + + def _section_from_spec(self, spec): """ Retrieve a section given a "spec" (either number or name). Return None if no such section exists in the file. @@ -802,6 +1043,9 @@ def main(stream=None): optparser.add_option('-p', '--string-dump', action='store', dest='show_string_dump', metavar='', help='Dump the contents of section as strings') + optparser.add_option('-V', '--version-info', + action='store_true', dest='show_version_info', + help='Display the version sections (if present)') optparser.add_option('--debug-dump', action='store', dest='debug_dump_what', metavar='', help=( @@ -838,6 +1082,8 @@ def main(stream=None): readelf.display_symbol_tables() if options.show_relocs: readelf.display_relocations() + if options.show_version_info: + readelf.display_version_info() if options.show_hex_dump: readelf.display_hex_dump(options.show_hex_dump) if options.show_string_dump: diff --git a/test/run_readelf_tests.py b/test/run_readelf_tests.py index bd15fff..57ab197 100755 --- a/test/run_readelf_tests.py +++ b/test/run_readelf_tests.py @@ -52,7 +52,8 @@ def run_test_on_file(filename, verbose=False): for option in [ '-e', '-d', '-s', '-r', '-x.text', '-p.shstrtab', '--debug-dump=info', '--debug-dump=decodedline', - '--debug-dump=frames', '--debug-dump=frames-interp']: + '--debug-dump=frames', '--debug-dump=frames-interp', + '-V']: if verbose: testlog.info("..option='%s'" % option) # stdouts will be a 2-element list: output of readelf and output # of scripts/readelf.py