1#------------------------------------------------------------------------------ 2# elftools: elf/gnuversions.py 3# 4# ELF sections 5# 6# Yann Rouillard (yann@pleiades.fr.eu.org) 7# This code is in the public domain 8#------------------------------------------------------------------------------ 9from ..construct import CString 10from ..common.utils import struct_parse, elf_assert 11from .sections import Section, Symbol 12 13 14class Version(object): 15 """ Version object - representing a version definition or dependency 16 entry from a "Version Needed" or a "Version Dependency" table section. 17 18 This kind of entry contains a pointer to an array of auxiliary entries 19 that store the information about version names or dependencies. 20 These entries are not stored in this object and should be accessed 21 through the appropriate method of a section object which will return 22 an iterator of VersionAuxiliary objects. 23 24 Similarly to Section objects, allows dictionary-like access to 25 verdef/verneed entry 26 """ 27 def __init__(self, entry, name=None): 28 self.entry = entry 29 self.name = name 30 31 def __getitem__(self, name): 32 """ Implement dict-like access to entry 33 """ 34 return self.entry[name] 35 36 37class VersionAuxiliary(object): 38 """ Version Auxiliary object - representing an auxiliary entry of a version 39 definition or dependency entry 40 41 Similarly to Section objects, allows dictionary-like access to the 42 verdaux/vernaux entry 43 """ 44 def __init__(self, entry, name): 45 self.entry = entry 46 self.name = name 47 48 def __getitem__(self, name): 49 """ Implement dict-like access to entries 50 """ 51 return self.entry[name] 52 53 54class GNUVersionSection(Section): 55 """ Common ancestor class for ELF SUNW|GNU Version Needed/Dependency 56 sections class which contains shareable code 57 """ 58 59 def __init__(self, header, name, elffile, stringtable, 60 field_prefix, version_struct, version_auxiliaries_struct): 61 super(GNUVersionSection, self).__init__(header, name, elffile) 62 self.stringtable = stringtable 63 self.field_prefix = field_prefix 64 self.version_struct = version_struct 65 self.version_auxiliaries_struct = version_auxiliaries_struct 66 67 def num_versions(self): 68 """ Number of version entries in the section 69 """ 70 return self['sh_info'] 71 72 def _field_name(self, name, auxiliary=False): 73 """ Return the real field's name of version or a version auxiliary 74 entry 75 """ 76 middle = 'a_' if auxiliary else '_' 77 return self.field_prefix + middle + name 78 79 def _iter_version_auxiliaries(self, entry_offset, count): 80 """ Yield all auxiliary entries of a version entry 81 """ 82 name_field = self._field_name('name', auxiliary=True) 83 next_field = self._field_name('next', auxiliary=True) 84 85 for _ in range(count): 86 entry = struct_parse( 87 self.version_auxiliaries_struct, 88 self.stream, 89 stream_pos=entry_offset) 90 91 name = self.stringtable.get_string(entry[name_field]) 92 version_aux = VersionAuxiliary(entry, name) 93 yield version_aux 94 95 entry_offset += entry[next_field] 96 97 def iter_versions(self): 98 """ Yield all the version entries in the section 99 Each time it returns the main version structure 100 and an iterator to walk through its auxiliaries entries 101 """ 102 aux_field = self._field_name('aux') 103 count_field = self._field_name('cnt') 104 next_field = self._field_name('next') 105 106 entry_offset = self['sh_offset'] 107 for _ in range(self.num_versions()): 108 entry = struct_parse( 109 self.version_struct, 110 self.stream, 111 stream_pos=entry_offset) 112 113 elf_assert(entry[count_field] > 0, 114 'Expected number of version auxiliary entries (%s) to be > 0' 115 'for the following version entry: %s' % ( 116 count_field, str(entry))) 117 118 version = Version(entry) 119 aux_entries_offset = entry_offset + entry[aux_field] 120 version_auxiliaries_iter = self._iter_version_auxiliaries( 121 aux_entries_offset, entry[count_field]) 122 123 yield version, version_auxiliaries_iter 124 125 entry_offset += entry[next_field] 126 127 128class GNUVerNeedSection(GNUVersionSection): 129 """ ELF SUNW or GNU Version Needed table section. 130 Has an associated StringTableSection that's passed in the constructor. 131 """ 132 def __init__(self, header, name, elffile, stringtable): 133 super(GNUVerNeedSection, self).__init__( 134 header, name, elffile, stringtable, 'vn', 135 elffile.structs.Elf_Verneed, elffile.structs.Elf_Vernaux) 136 self._has_indexes = None 137 138 def has_indexes(self): 139 """ Return True if at least one version definition entry has an index 140 that is stored in the vna_other field. 141 This information is used for symbol versioning 142 """ 143 if self._has_indexes is None: 144 self._has_indexes = False 145 for _, vernaux_iter in self.iter_versions(): 146 for vernaux in vernaux_iter: 147 if vernaux['vna_other']: 148 self._has_indexes = True 149 break 150 151 return self._has_indexes 152 153 def iter_versions(self): 154 for verneed, vernaux in super(GNUVerNeedSection, self).iter_versions(): 155 verneed.name = self.stringtable.get_string(verneed['vn_file']) 156 yield verneed, vernaux 157 158 def get_version(self, index): 159 """ Get the version information located at index #n in the table 160 Return boths the verneed structure and the vernaux structure 161 that contains the name of the version 162 """ 163 for verneed, vernaux_iter in self.iter_versions(): 164 for vernaux in vernaux_iter: 165 if vernaux['vna_other'] == index: 166 return verneed, vernaux 167 168 return None 169 170 171class GNUVerDefSection(GNUVersionSection): 172 """ ELF SUNW or GNU Version Definition table section. 173 Has an associated StringTableSection that's passed in the constructor. 174 """ 175 def __init__(self, header, name, elffile, stringtable): 176 super(GNUVerDefSection, self).__init__( 177 header, name, elffile, stringtable, 'vd', 178 elffile.structs.Elf_Verdef, elffile.structs.Elf_Verdaux) 179 180 def get_version(self, index): 181 """ Get the version information located at index #n in the table 182 Return boths the verdef structure and an iterator to retrieve 183 both the version names and dependencies in the form of 184 verdaux entries 185 """ 186 for verdef, verdaux_iter in self.iter_versions(): 187 if verdef['vd_ndx'] == index: 188 return verdef, verdaux_iter 189 190 return None 191 192 193class GNUVerSymSection(Section): 194 """ ELF SUNW or GNU Versym table section. 195 Has an associated SymbolTableSection that's passed in the constructor. 196 """ 197 def __init__(self, header, name, elffile, symboltable): 198 super(GNUVerSymSection, self).__init__(header, name, elffile) 199 self.symboltable = symboltable 200 201 def num_symbols(self): 202 """ Number of symbols in the table 203 """ 204 return self['sh_size'] // self['sh_entsize'] 205 206 def get_symbol(self, n): 207 """ Get the symbol at index #n from the table (Symbol object) 208 It begins at 1 and not 0 since the first entry is used to 209 store the current version of the syminfo table 210 """ 211 # Grab the symbol's entry from the stream 212 entry_offset = self['sh_offset'] + n * self['sh_entsize'] 213 entry = struct_parse( 214 self.structs.Elf_Versym, 215 self.stream, 216 stream_pos=entry_offset) 217 # Find the symbol name in the associated symbol table 218 name = self.symboltable.get_symbol(n).name 219 return Symbol(entry, name) 220 221 def iter_symbols(self): 222 """ Yield all the symbols in the table 223 """ 224 for i in range(self.num_symbols()): 225 yield self.get_symbol(i) 226