1# 2# Copyright (C) 2017 The Android Open Source Project 3# 4# Licensed under the Apache License, Version 2.0 (the "License"); 5# you may not use this file except in compliance with the License. 6# You may obtain a copy of the License at 7# 8# http://www.apache.org/licenses/LICENSE-2.0 9# 10# Unless required by applicable law or agreed to in writing, software 11# distributed under the License is distributed on an "AS IS" BASIS, 12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13# See the License for the specific language governing permissions and 14# limitations under the License. 15# 16r"""This file contains an ELF parser and ELF header structures. 17 18Example usage: 19 import elf_parser 20 with elf_parser.ElfParser(file) as e: 21 print('\n'.join(e.ListGlobalDynamicSymbols())) 22 print('\n'.join(e.ListDependencies()[0])) 23""" 24 25import ctypes 26import os 27import struct 28 29from vts.utils.python.library.elf import consts 30from vts.utils.python.library.elf import structs 31from vts.utils.python.library.elf import utils 32 33 34class ElfError(Exception): 35 """The exception raised by ElfParser.""" 36 pass 37 38 39class ElfParser(object): 40 """The class reads information from an ELF file. 41 42 Attributes: 43 _file: The ELF file object. 44 _begin_offset: The offset of the ELF object in the file. The value is 45 non-zero if the ELF is in an archive, such as .a file. 46 _file_size: Size of the file. 47 bitness: Bitness of the ELF. 48 Ehdr: An Elf_Endr, the ELF header structure of the file. 49 Shdr: A list of Elf_Shdr, the section headers of the file. 50 Elf_Addr: ELF unsigned program address type. 51 Elf_Off: ELF unsigned file offset type. 52 Elf_Half: ELF unsigned medium integer type. 53 Elf_Word: ELF unsigned integer type. 54 Elf_Sword: ELF signed integer type. 55 Elf_Ehdr: ELF header class. 56 Elf_Shdr: ELF section header class. 57 Elf_Dyn: ELF dynamic entry class. 58 Elf_Sym: ELF symbol entry class. 59 Elf_Rel: ELF relocation entry class. 60 Elf_Rela: ELF relocation entry class with explicit addend. 61 Elf_Phdr: ELF program header class. 62 Elf_Nhdr: ELF note header class. 63 """ 64 65 def __init__(self, file_path, begin_offset=0): 66 """Creates a parser to open and read an ELF file. 67 68 Args: 69 file_path: The path to the file. 70 begin_offset: The offset of the ELF object in the file. 71 72 Raises: 73 ElfError: File is not a valid ELF. 74 """ 75 self._begin_offset = begin_offset 76 try: 77 self._file = open(file_path, 'rb') 78 except IOError as e: 79 raise ElfError(e) 80 try: 81 self._file_size = os.fstat(self._file.fileno()).st_size 82 except OSError as e: 83 self.Close() 84 raise ElfError(e) 85 86 try: 87 e_ident = self._SeekRead(0, consts.EI_NIDENT) 88 89 if e_ident[:4] != consts.ELF_MAGIC_NUMBER: 90 raise ElfError('Unexpected magic bytes: {}'.format(e_ident[:4])) 91 92 if utils.ByteToInt(e_ident[consts.EI_CLASS]) not in ( 93 consts.ELFCLASS32, consts.ELFCLASS64): 94 raise ElfError('Unexpected file class: {}' 95 .format(e_ident[consts.EI_CLASS])) 96 97 if utils.ByteToInt(e_ident[consts.EI_DATA]) != consts.ELFDATA2LSB: 98 raise ElfError('Unexpected data encoding: {}' 99 .format(e_ident[consts.EI_DATA])) 100 except ElfError: 101 self.Close() 102 raise 103 104 if utils.ByteToInt(e_ident[consts.EI_CLASS]) == consts.ELFCLASS32: 105 self.bitness = 32 106 self.Elf_Addr = structs.Elf32_Addr 107 self.Elf_Off = structs.Elf32_Off 108 self.Elf_Half = structs.Elf32_Half 109 self.Elf_Word = structs.Elf32_Word 110 self.Elf_Sword = structs.Elf32_Sword 111 self.Elf_Ehdr = structs.Elf32_Ehdr 112 self.Elf_Shdr = structs.Elf32_Shdr 113 self.Elf_Dyn = structs.Elf32_Dyn 114 self.Elf_Sym = structs.Elf32_Sym 115 self.Elf_Rel = structs.Elf32_Rel 116 self.Elf_Rela = structs.Elf32_Rela 117 self.Elf_Phdr = structs.Elf32_Phdr 118 self.Elf_Nhdr = structs.Elf32_Nhdr 119 else: 120 self.bitness = 64 121 self.Elf_Addr = structs.Elf64_Addr 122 self.Elf_Off = structs.Elf64_Off 123 self.Elf_Half = structs.Elf64_Half 124 self.Elf_Word = structs.Elf64_Word 125 self.Elf_Sword = structs.Elf64_Sword 126 self.Elf_Ehdr = structs.Elf64_Ehdr 127 self.Elf_Shdr = structs.Elf64_Shdr 128 self.Elf_Dyn = structs.Elf64_Dyn 129 self.Elf_Sym = structs.Elf64_Sym 130 self.Elf_Rel = structs.Elf64_Rel 131 self.Elf_Rela = structs.Elf64_Rela 132 self.Elf_Phdr = structs.Elf64_Phdr 133 self.Elf_Nhdr = structs.Elf64_Nhdr 134 135 try: 136 self.Ehdr = self._SeekReadStruct(0, self.Elf_Ehdr) 137 shoff = self.Ehdr.e_shoff 138 shentsize = self.Ehdr.e_shentsize 139 self.Shdr = [self._SeekReadStruct(shoff + i * shentsize, 140 self.Elf_Shdr) 141 for i in range(self.Ehdr.e_shnum)] 142 except ElfError: 143 self.Close() 144 raise 145 146 def __del__(self): 147 """Closes the ELF file.""" 148 self.Close() 149 150 def __enter__(self): 151 return self 152 153 def __exit__(self, exc_type, exc_value, traceback): 154 """Closes the ELF file.""" 155 self.Close() 156 157 def Close(self): 158 """Closes the ELF file.""" 159 if hasattr(self, "_file"): 160 self._file.close() 161 162 def _SeekRead(self, offset, read_size): 163 """Reads a byte string at specific offset in the file. 164 165 Args: 166 offset: An integer, the offset from the beginning of the ELF. 167 read_size: An integer, number of bytes to read. 168 169 Returns: 170 A bytes object which is the file content. 171 172 Raises: 173 ElfError: Fails to seek and read. 174 """ 175 if offset + read_size > self._file_size: 176 raise ElfError("Read beyond end of file.") 177 try: 178 self._file.seek(self._begin_offset + offset) 179 return self._file.read(read_size) 180 except IOError as e: 181 raise ElfError(e) 182 183 def _SeekRead8(self, offset): 184 """Reads an 1-byte integer from file.""" 185 return struct.unpack("B", self._SeekRead(offset, 1))[0] 186 187 def _SeekRead16(self, offset): 188 """Reads a 2-byte integer from file.""" 189 return struct.unpack("H", self._SeekRead(offset, 2))[0] 190 191 def _SeekRead32(self, offset): 192 """Reads a 4-byte integer from file.""" 193 return struct.unpack("I", self._SeekRead(offset, 4))[0] 194 195 def _SeekRead64(self, offset): 196 """Reads an 8-byte integer from file.""" 197 return struct.unpack("Q", self._SeekRead(offset, 8))[0] 198 199 def _SeekReadString(self, offset): 200 """Reads a null-terminated string starting from specific offset. 201 202 Args: 203 offset: The offset from the beginning of the ELF object. 204 205 Returns: 206 A string, excluding the null character. 207 208 Raises: 209 ElfError: String reaches end of file without null terminator. 210 """ 211 ret = b"" 212 buf_size = 16 213 self._file.seek(self._begin_offset + offset) 214 while True: 215 try: 216 buf = self._file.read(buf_size) 217 except IOError as e: 218 raise ElfError(e) 219 end_index = buf.find(b"\0") 220 if end_index < 0: 221 ret += buf 222 else: 223 ret += buf[:end_index] 224 return utils.BytesToString(ret) 225 if len(buf) != buf_size: 226 raise ElfError("Null-terminated string reaches end of file.") 227 228 def _SeekReadStruct(self, offset, struct_type): 229 """Reads a ctypes.Structure / ctypes.Union from file. 230 231 Args: 232 offset: An integer, the offset from the beginning of the ELF. 233 struct_type: A class, the structure type to read. 234 235 Returns: 236 An object of struct_type. 237 238 Raises: 239 ElfError: Fails to seek and read. 240 Fails to create struct_type instance. 241 """ 242 raw_bytes = self._SeekRead(offset, ctypes.sizeof(struct_type)) 243 try: 244 return struct_type.from_buffer_copy(raw_bytes) 245 except ValueError as e: 246 raise ElfError(e) 247 248 def GetString(self, strtab, offset): 249 """Retrieves a null-terminated string from string table. 250 251 Args: 252 strtab: Section header of the string table. 253 offset: Section offset (string index) to start reading from. 254 255 Returns: 256 A string without the null terminator. 257 258 Raises: 259 ElfError: Fails to seek and read. 260 """ 261 return self._SeekReadString(strtab.sh_offset + offset) 262 263 def GetSectionName(self, sh): 264 """Returns a section name. 265 266 Args: 267 sh: A section header. 268 269 Returns: 270 A String. 271 272 Raises: 273 ElfError: Fails to seek and read. 274 """ 275 strtab = self.Shdr[self.Ehdr.e_shstrndx] 276 return self.GetString(strtab, sh.sh_name) 277 278 def GetSectionsByName(self, name): 279 """Returns a generator of section headers from a given name. 280 281 If multiple sections have the same name, yield them all. 282 283 Args: 284 name: The section name to search for. 285 286 Returns: 287 A generator of Elf_Shdr. 288 289 Raises: 290 ElfError: Fails to seek and read. 291 """ 292 return (sh for sh in self.Shdr if name == self.GetSectionName(sh)) 293 294 def GetSectionByName(self, name): 295 """Returns a section header whose name equals a given name. 296 297 Returns only the first match, assuming the section name is unique. 298 299 Args: 300 name: The section name to search for. 301 302 Returns: 303 An Elf_Shdr if found. 304 None if no sections have the given name. 305 306 Raises: 307 ElfError: Fails to seek and read. 308 """ 309 for sh in self.GetSectionsByName(name): 310 return sh 311 return None 312 313 def GetDynamic(self, dynamic): 314 """Yields the _DYNAMIC array. 315 316 Args: 317 dynamic: Section header of the dynamic section. 318 319 Yields: 320 Elf_Dyn. 321 322 Raises: 323 ElfError: Fails to seek and read. 324 """ 325 off = dynamic.sh_offset 326 num = int(dynamic.sh_size // dynamic.sh_entsize) 327 for _ in range(num): 328 dyn = self._SeekReadStruct(off, self.Elf_Dyn) 329 yield dyn 330 if dyn.d_tag == consts.DT_NULL: 331 break 332 off += dynamic.sh_entsize 333 334 def GetSymbol(self, symtab, idx): 335 """Retrieves a Elf_Sym entry from symbol table. 336 337 Args: 338 symtab: A symbol table. 339 idx: An integer, symbol table index. 340 341 Returns: 342 An Elf_Sym. 343 344 Raises: 345 ElfError: Fails to seek and read. 346 """ 347 off = symtab.sh_offset + idx * symtab.sh_entsize 348 return self._SeekReadStruct(off, self.Elf_Sym) 349 350 def GetSymbols(self, symtab): 351 """Returns a generator of Elf_Sym in symbol table. 352 353 Args: 354 symtab: A symbol table. 355 356 Returns: 357 A generator of Elf_Sym. 358 359 Raises: 360 ElfError: Fails to seek and read. 361 """ 362 num = int(symtab.sh_size // symtab.sh_entsize) 363 return (self.GetSymbol(symtab, i) for i in range(num)) 364 365 def GetRelocationSymbol(self, symtab, rel): 366 """Retrieves the Elf_Sym with respect to an Elf_Rel / Elf_Rela. 367 368 Args: 369 symtab: A symbol table. 370 rel: A Elf_Rel or Elf_Rela. 371 372 Returns: 373 An Elf_Sym. 374 375 Raises: 376 ElfError: Fails to seek and read. 377 """ 378 return self.GetSymbol(symtab, rel.GetSymbol()) 379 380 def _CreateElfRel(self, offset, info): 381 """Creates an instance of Elf_Rel. 382 383 Args: 384 offset: The initial value of r_offset. 385 info: The initial value of r_info. 386 387 Returns: 388 An Elf_Rel. 389 """ 390 elf_rel = self.Elf_Rel() 391 elf_rel.r_offset = offset 392 elf_rel.r_info = info 393 return elf_rel 394 395 def _DecodeAndroidRelr(self, rel): 396 """Decodes a SHT_RELR / SHT_ANDROID_RELR section. 397 398 Args: 399 rel: A relocation table. 400 401 Yields: 402 Elf_Rel. 403 404 Raises: 405 ElfError: Fails to seek and read. 406 """ 407 if self.bitness == 32: 408 addr_size = 4 409 seek_read_entry = self._SeekRead32 410 else: 411 addr_size = 8 412 seek_read_entry = self._SeekRead64 413 414 rel_offset = 0 415 for ent_offset in range(rel.sh_offset, rel.sh_offset + rel.sh_size, 416 rel.sh_entsize): 417 relr_entry = seek_read_entry(ent_offset) 418 if (relr_entry & 1) == 0: 419 # The entry is an address. 420 yield self._CreateElfRel(relr_entry, 0) 421 rel_offset = relr_entry + addr_size 422 else: 423 # The entry is a bitmap. 424 for bit_idx in range(1, rel.sh_entsize * 8): 425 if (relr_entry >> bit_idx) & 1: 426 yield self._CreateElfRel(rel_offset, 0) 427 rel_offset += addr_size 428 429 def GetRelocation(self, rel, idx): 430 """Retrieves a Elf_Rel / Elf_Rela entry from relocation table. 431 432 Args: 433 rel: A relocation table. 434 idx: An integer, relocation table index. 435 436 Returns: 437 An Elf_Rel or Elf_Rela. 438 439 Raises: 440 ElfError: Fails to seek and read. 441 """ 442 off = rel.sh_offset + idx * rel.sh_entsize 443 if rel.sh_type == consts.SHT_RELA: 444 return self._SeekReadStruct(off, self.Elf_Rela) 445 return self._SeekReadStruct(off, self.Elf_Rel) 446 447 def GetRelocations(self, rel): 448 """Returns a generator of Elf_Rel / Elf_Rela in relocation table. 449 450 Args: 451 rel: A relocation table. 452 453 Returns: 454 A generator of Elf_Rel or Elf_Rela. 455 456 Raises: 457 ElfError: Fails to seek and read. 458 """ 459 if rel.sh_type in (consts.SHT_ANDROID_REL, consts.SHT_ANDROID_RELA): 460 relocations = self._UnpackAndroidRela(rel) 461 if rel.sh_type == consts.SHT_ANDROID_REL: 462 return (self.Elf_Rel(r_offset=rela.r_offset, r_info=rela.r_info) 463 for rela in relocations) 464 return relocations 465 elif rel.sh_type in (consts.SHT_RELR, consts.SHT_ANDROID_RELR): 466 return self._DecodeAndroidRelr(rel) 467 else: 468 num = int(rel.sh_size // rel.sh_entsize) 469 return (self.GetRelocation(rel, i) for i in range(num)) 470 471 def _UnpackAndroidRela(self, android_rela): 472 """Unpacks a SHT_ANDROID_REL / SHT_ANDROID_RELA section. 473 474 Args: 475 android_rela: The packed section's section header. 476 477 Yields: 478 Elf_Rela. 479 480 Raises: 481 ElfError: Fails to decode android rela section. 482 """ 483 data = self._SeekRead(android_rela.sh_offset, android_rela.sh_size) 484 # Check packed section header. 485 if len(data) < 4 or data[:4] != b'APS2': 486 raise ElfError('Unexpected SHT_ANDROID_RELA header: {}' 487 .format(data[:4])) 488 # Decode SLEB128 word stream. 489 def _PackedWordsGen(): 490 cur = 4 491 while cur < len(data): 492 try: 493 value, num = utils.DecodeSLEB128(data, cur) 494 except IndexError: 495 raise ElfError('Decoding pass end of section.') 496 yield value 497 cur += num 498 raise ElfError('Decoding pass end of section.') 499 500 _packed_words_gen = _PackedWordsGen() 501 _PopWord = lambda: next(_packed_words_gen) 502 # Decode delta encoded relocation data. 503 current_count = 0 504 total_count = _PopWord() 505 offset = _PopWord() 506 addend = 0 507 while current_count < total_count: 508 # Read relocaiton group info. 509 group_size = _PopWord() 510 group_flags = _PopWord() 511 group_offset_delta = 0 512 # Read group flag and prepare delta values. 513 grouped_by_info = ( 514 group_flags & consts.RELOCATION_GROUPED_BY_INFO_FLAG) 515 grouped_by_offset_delta = ( 516 group_flags & consts.RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG) 517 grouped_by_addend = ( 518 group_flags & consts.RELOCATION_GROUPED_BY_ADDEND_FLAG) 519 group_has_addend = ( 520 group_flags & consts.RELOCATION_GROUP_HAS_ADDEND_FLAG) 521 if grouped_by_offset_delta: 522 group_offset_delta = _PopWord() 523 if grouped_by_info: 524 info = _PopWord() 525 if group_has_addend and grouped_by_addend: 526 addend += _PopWord() 527 if not group_has_addend: 528 addend = 0 529 # Handle each relocation entry in group. 530 for _ in range(group_size): 531 if grouped_by_offset_delta: 532 offset += group_offset_delta 533 else: 534 offset += _PopWord() 535 if not grouped_by_info: 536 info = _PopWord() 537 if group_has_addend and not grouped_by_addend: 538 addend += _PopWord() 539 540 relocation = self.Elf_Rela(r_offset=offset, 541 r_info=info, 542 r_addend=addend) 543 yield relocation 544 current_count += group_size 545 546 def _LoadDynamicSection(self, dynamic): 547 """Reads entries from dynamic section. 548 549 Args: 550 dynamic: Section header of the dynamic section. 551 552 Returns: 553 A dict of {DT_NEEDED: [libraries names], DT_RUNPATH: [paths]} 554 where the library names and the paths are strings. 555 556 Raises: 557 ElfError: Fails to find dynamic string table. 558 """ 559 strtab_addr = None 560 dt_needed_offsets = [] 561 dt_runpath_offsets = [] 562 for dyn in self.GetDynamic(dynamic): 563 if dyn.d_tag == consts.DT_NEEDED: 564 dt_needed_offsets.append(dyn.d_un.d_val) 565 elif dyn.d_tag == consts.DT_RUNPATH: 566 dt_runpath_offsets.append(dyn.d_un.d_val) 567 elif dyn.d_tag == consts.DT_STRTAB: 568 strtab_addr = dyn.d_un.d_ptr 569 570 if strtab_addr is None: 571 raise ElfError("Cannot find string table address in dynamic " 572 "section.") 573 try: 574 strtab = next(sh for sh in self.Shdr if sh.sh_addr == strtab_addr) 575 except StopIteration: 576 raise ElfError("Cannot find dynamic string table.") 577 dt_needed = [self.GetString(strtab, off) for off in dt_needed_offsets] 578 dt_runpath = [] 579 for off in dt_runpath_offsets: 580 dt_runpath.extend(self.GetString(strtab, off).split(":")) 581 return {consts.DT_NEEDED: dt_needed, consts.DT_RUNPATH: dt_runpath} 582 583 def IsExecutable(self): 584 """Returns whether the ELF is executable.""" 585 return self.Ehdr.e_type == consts.ET_EXEC 586 587 def IsSharedObject(self): 588 """Returns whether the ELF is a shared object.""" 589 return self.Ehdr.e_type == consts.ET_DYN 590 591 def HasAndroidIdent(self): 592 """Returns whether the ELF has a .note.android.ident section.""" 593 for sh in self.GetSectionsByName(".note.android.ident"): 594 nh = self._SeekReadStruct(sh.sh_offset, self.Elf_Nhdr) 595 name = self._SeekRead(sh.sh_offset + ctypes.sizeof(self.Elf_Nhdr), 596 nh.n_namesz) 597 if name == b"Android\0": 598 return True 599 return False 600 601 def MatchCpuAbi(self, abi): 602 """Returns whether the ELF matches the ABI. 603 604 Args: 605 abi: A string, the name of the ABI. 606 607 Returns: 608 A boolean, whether the ELF matches the ABI. 609 """ 610 for abi_prefix, machine in (("arm64", consts.EM_AARCH64), 611 ("arm", consts.EM_ARM), 612 ("mips64", consts.EM_MIPS), 613 ("mips", consts.EM_MIPS), 614 ("x86_64", consts.EM_X86_64), 615 ("x86", consts.EM_386)): 616 if abi.startswith(abi_prefix): 617 return self.Ehdr.e_machine == machine 618 return False 619 620 def ListDependencies(self): 621 """Lists the shared libraries that the ELF depends on. 622 623 Returns: 624 2 lists of strings, the names of the depended libraries and the 625 search paths. 626 """ 627 deps = [] 628 runpaths = [] 629 for sh in self.Shdr: 630 if sh.sh_type == consts.SHT_DYNAMIC: 631 dynamic = self._LoadDynamicSection(sh) 632 deps.extend(dynamic[consts.DT_NEEDED]) 633 runpaths.extend(dynamic[consts.DT_RUNPATH]) 634 return deps, runpaths 635 636 def ListGlobalSymbols(self, include_weak=False, 637 symtab_name=consts.SYMTAB, 638 strtab_name=consts.STRTAB): 639 """Lists the global symbols defined in the ELF. 640 641 Args: 642 include_weak: A boolean, whether to include weak symbols. 643 symtab_name: A string, the name of the symbol table. 644 strtab_name: A string, the name of the string table. 645 646 Returns: 647 A list of strings, the names of the symbols. 648 649 Raises: 650 ElfError: Fails to find symbol table. 651 """ 652 symtab = self.GetSectionByName(symtab_name) 653 strtab = self.GetSectionByName(strtab_name) 654 if not symtab or not strtab or symtab.sh_size == 0: 655 raise ElfError("Cannot find symbol table.") 656 657 include_bindings = [consts.STB_GLOBAL] 658 if include_weak: 659 include_bindings.append(consts.STB_WEAK) 660 661 sym_names = [] 662 for sym in self.GetSymbols(symtab): 663 # Global symbols can be defined at most once at link time, 664 # while weak symbols may have multiple definitions. 665 if sym.GetType() == consts.STT_NOTYPE: 666 continue 667 if sym.GetBinding() not in include_bindings: 668 continue 669 if sym.st_shndx == consts.SHN_UNDEF: 670 continue 671 sym_names.append(self.GetString(strtab, sym.st_name)) 672 return sym_names 673 674 def ListGlobalDynamicSymbols(self, include_weak=False): 675 """Lists the global dynamic symbols defined in the ELF. 676 677 Args: 678 include_weak: A boolean, whether to include weak symbols. 679 680 Returns: 681 A list of strings, the names of the symbols. 682 683 Raises: 684 ElfError: Fails to find symbol table. 685 """ 686 return self.ListGlobalSymbols(include_weak, 687 consts.DYNSYM, consts.DYNSTR) 688 689 def GetProgramInterpreter(self): 690 """Gets the path to the program interpreter of the ELF. 691 692 Returns: 693 A string, the contents of .interp section. 694 None if the section is not found. 695 """ 696 for ph_index in range(self.Ehdr.e_phnum): 697 ph = self._SeekReadStruct( 698 self.Ehdr.e_phoff + ph_index * self.Ehdr.e_phentsize, 699 self.Elf_Phdr) 700 if ph.p_type == consts.PT_INTERP: 701 return self._SeekReadString(ph.p_offset) 702