1#!/usr/bin/env python 2#------------------------------------------------------------------------------- 3# scripts/readelf.py 4# 5# A clone of 'readelf' in Python, based on the pyelftools library 6# 7# Eli Bendersky (eliben@gmail.com) 8# This code is in the public domain 9#------------------------------------------------------------------------------- 10import argparse 11import os, sys 12import string 13import traceback 14import itertools 15# Note: zip has different behaviour between Python 2.x and 3.x. 16# - Using izip ensures compatibility. 17try: 18 from itertools import izip 19except: 20 izip = zip 21 22# For running from development directory. It should take precedence over the 23# installed pyelftools. 24sys.path.insert(0, '.') 25 26 27from elftools import __version__ 28from elftools.common.exceptions import ELFError 29from elftools.common.py3compat import ( 30 ifilter, byte2int, bytes2str, itervalues, str2bytes, iterbytes) 31from elftools.elf.elffile import ELFFile 32from elftools.elf.dynamic import DynamicSection, DynamicSegment 33from elftools.elf.enums import ENUM_D_TAG 34from elftools.elf.segments import InterpSegment 35from elftools.elf.sections import ( 36 NoteSection, SymbolTableSection, SymbolTableIndexSection 37) 38from elftools.elf.gnuversions import ( 39 GNUVerSymSection, GNUVerDefSection, 40 GNUVerNeedSection, 41 ) 42from elftools.elf.relocation import RelocationSection 43from elftools.elf.descriptions import ( 44 describe_ei_class, describe_ei_data, describe_ei_version, 45 describe_ei_osabi, describe_e_type, describe_e_machine, 46 describe_e_version_numeric, describe_p_type, describe_p_flags, 47 describe_rh_flags, describe_sh_type, describe_sh_flags, 48 describe_symbol_type, describe_symbol_bind, describe_symbol_visibility, 49 describe_symbol_shndx, describe_reloc_type, describe_dyn_tag, 50 describe_dt_flags, describe_dt_flags_1, describe_ver_flags, describe_note, 51 describe_attr_tag_arm 52 ) 53from elftools.elf.constants import E_FLAGS 54from elftools.elf.constants import E_FLAGS_MASKS 55from elftools.elf.constants import SH_FLAGS 56from elftools.elf.constants import SHN_INDICES 57from elftools.dwarf.dwarfinfo import DWARFInfo 58from elftools.dwarf.descriptions import ( 59 describe_reg_name, describe_attr_value, set_global_machine_arch, 60 describe_CFI_instructions, describe_CFI_register_rule, 61 describe_CFI_CFA_rule, describe_DWARF_expr 62 ) 63from elftools.dwarf.constants import ( 64 DW_LNS_copy, DW_LNS_set_file, DW_LNE_define_file) 65from elftools.dwarf.locationlists import LocationParser, LocationEntry 66from elftools.dwarf.callframe import CIE, FDE, ZERO 67from elftools.ehabi.ehabiinfo import CorruptEHABIEntry, CannotUnwindEHABIEntry, GenericEHABIEntry 68 69 70class ReadElf(object): 71 """ display_* methods are used to emit output into the output stream 72 """ 73 def __init__(self, file, output): 74 """ file: 75 stream object with the ELF file to read 76 77 output: 78 output stream to write to 79 """ 80 self.elffile = ELFFile(file) 81 self.output = output 82 83 # Lazily initialized if a debug dump is requested 84 self._dwarfinfo = None 85 86 self._versioninfo = None 87 88 self._shndx_sections = None 89 90 def display_file_header(self): 91 """ Display the ELF file header 92 """ 93 self._emitline('ELF Header:') 94 self._emit(' Magic: ') 95 self._emit(' '.join('%2.2x' % byte2int(b) 96 for b in self.elffile.e_ident_raw)) 97 self._emitline(' ') 98 header = self.elffile.header 99 e_ident = header['e_ident'] 100 self._emitline(' Class: %s' % 101 describe_ei_class(e_ident['EI_CLASS'])) 102 self._emitline(' Data: %s' % 103 describe_ei_data(e_ident['EI_DATA'])) 104 self._emitline(' Version: %s' % 105 describe_ei_version(e_ident['EI_VERSION'])) 106 self._emitline(' OS/ABI: %s' % 107 describe_ei_osabi(e_ident['EI_OSABI'])) 108 self._emitline(' ABI Version: %d' % 109 e_ident['EI_ABIVERSION']) 110 self._emitline(' Type: %s' % 111 describe_e_type(header['e_type'])) 112 self._emitline(' Machine: %s' % 113 describe_e_machine(header['e_machine'])) 114 self._emitline(' Version: %s' % 115 describe_e_version_numeric(header['e_version'])) 116 self._emitline(' Entry point address: %s' % 117 self._format_hex(header['e_entry'])) 118 self._emit(' Start of program headers: %s' % 119 header['e_phoff']) 120 self._emitline(' (bytes into file)') 121 self._emit(' Start of section headers: %s' % 122 header['e_shoff']) 123 self._emitline(' (bytes into file)') 124 self._emitline(' Flags: %s%s' % 125 (self._format_hex(header['e_flags']), 126 self.decode_flags(header['e_flags']))) 127 self._emitline(' Size of this header: %s (bytes)' % 128 header['e_ehsize']) 129 self._emitline(' Size of program headers: %s (bytes)' % 130 header['e_phentsize']) 131 self._emitline(' Number of program headers: %s' % 132 header['e_phnum']) 133 self._emitline(' Size of section headers: %s (bytes)' % 134 header['e_shentsize']) 135 self._emit(' Number of section headers: %s' % 136 header['e_shnum']) 137 if header['e_shnum'] == 0 and self.elffile.num_sections() != 0: 138 self._emitline(' (%d)' % self.elffile.num_sections()) 139 else: 140 self._emitline('') 141 self._emit(' Section header string table index: %s' % 142 header['e_shstrndx']) 143 if header['e_shstrndx'] == SHN_INDICES.SHN_XINDEX: 144 self._emitline(' (%d)' % self.elffile.get_shstrndx()) 145 else: 146 self._emitline('') 147 148 def decode_flags(self, flags): 149 description = "" 150 if self.elffile['e_machine'] == "EM_ARM": 151 eabi = flags & E_FLAGS.EF_ARM_EABIMASK 152 flags &= ~E_FLAGS.EF_ARM_EABIMASK 153 154 if flags & E_FLAGS.EF_ARM_RELEXEC: 155 description += ', relocatable executabl' 156 flags &= ~E_FLAGS.EF_ARM_RELEXEC 157 158 if eabi == E_FLAGS.EF_ARM_EABI_VER5: 159 EF_ARM_KNOWN_FLAGS = E_FLAGS.EF_ARM_ABI_FLOAT_SOFT|E_FLAGS.EF_ARM_ABI_FLOAT_HARD|E_FLAGS.EF_ARM_LE8|E_FLAGS.EF_ARM_BE8 160 description += ', Version5 EABI' 161 if flags & E_FLAGS.EF_ARM_ABI_FLOAT_SOFT: 162 description += ", soft-float ABI" 163 elif flags & E_FLAGS.EF_ARM_ABI_FLOAT_HARD: 164 description += ", hard-float ABI" 165 166 if flags & E_FLAGS.EF_ARM_BE8: 167 description += ", BE8" 168 elif flags & E_FLAGS.EF_ARM_LE8: 169 description += ", LE8" 170 171 if flags & ~EF_ARM_KNOWN_FLAGS: 172 description += ', <unknown>' 173 else: 174 description += ', <unrecognized EABI>' 175 176 elif self.elffile['e_machine'] == "EM_MIPS": 177 if flags & E_FLAGS.EF_MIPS_NOREORDER: 178 description += ", noreorder" 179 if flags & E_FLAGS.EF_MIPS_PIC: 180 description += ", pic" 181 if flags & E_FLAGS.EF_MIPS_CPIC: 182 description += ", cpic" 183 if (flags & E_FLAGS.EF_MIPS_ABI2): 184 description += ", abi2" 185 if (flags & E_FLAGS.EF_MIPS_32BITMODE): 186 description += ", 32bitmode" 187 if (flags & E_FLAGS_MASKS.EFM_MIPS_ABI_O32): 188 description += ", o32" 189 elif (flags & E_FLAGS_MASKS.EFM_MIPS_ABI_O64): 190 description += ", o64" 191 elif (flags & E_FLAGS_MASKS.EFM_MIPS_ABI_EABI32): 192 description += ", eabi32" 193 elif (flags & E_FLAGS_MASKS.EFM_MIPS_ABI_EABI64): 194 description += ", eabi64" 195 if (flags & E_FLAGS.EF_MIPS_ARCH) == E_FLAGS.EF_MIPS_ARCH_1: 196 description += ", mips1" 197 if (flags & E_FLAGS.EF_MIPS_ARCH) == E_FLAGS.EF_MIPS_ARCH_2: 198 description += ", mips2" 199 if (flags & E_FLAGS.EF_MIPS_ARCH) == E_FLAGS.EF_MIPS_ARCH_3: 200 description += ", mips3" 201 if (flags & E_FLAGS.EF_MIPS_ARCH) == E_FLAGS.EF_MIPS_ARCH_4: 202 description += ", mips4" 203 if (flags & E_FLAGS.EF_MIPS_ARCH) == E_FLAGS.EF_MIPS_ARCH_5: 204 description += ", mips5" 205 if (flags & E_FLAGS.EF_MIPS_ARCH) == E_FLAGS.EF_MIPS_ARCH_32R2: 206 description += ", mips32r2" 207 if (flags & E_FLAGS.EF_MIPS_ARCH) == E_FLAGS.EF_MIPS_ARCH_64R2: 208 description += ", mips64r2" 209 if (flags & E_FLAGS.EF_MIPS_ARCH) == E_FLAGS.EF_MIPS_ARCH_32: 210 description += ", mips32" 211 if (flags & E_FLAGS.EF_MIPS_ARCH) == E_FLAGS.EF_MIPS_ARCH_64: 212 description += ", mips64" 213 214 return description 215 216 def display_program_headers(self, show_heading=True): 217 """ Display the ELF program headers. 218 If show_heading is True, displays the heading for this information 219 (Elf file type is...) 220 """ 221 self._emitline() 222 if self.elffile.num_segments() == 0: 223 self._emitline('There are no program headers in this file.') 224 return 225 226 elfheader = self.elffile.header 227 if show_heading: 228 self._emitline('Elf file type is %s' % 229 describe_e_type(elfheader['e_type'])) 230 self._emitline('Entry point is %s' % 231 self._format_hex(elfheader['e_entry'])) 232 # readelf weirness - why isn't e_phoff printed as hex? (for section 233 # headers, it is...) 234 self._emitline('There are %s program headers, starting at offset %s' % ( 235 self.elffile.num_segments(), elfheader['e_phoff'])) 236 self._emitline() 237 238 self._emitline('Program Headers:') 239 240 # Now comes the table of program headers with their attributes. Note 241 # that due to different formatting constraints of 32-bit and 64-bit 242 # addresses, there are some conditions on elfclass here. 243 # 244 # First comes the table heading 245 # 246 if self.elffile.elfclass == 32: 247 self._emitline(' Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align') 248 else: 249 self._emitline(' Type Offset VirtAddr PhysAddr') 250 self._emitline(' FileSiz MemSiz Flags Align') 251 252 # Now the entries 253 # 254 for segment in self.elffile.iter_segments(): 255 self._emit(' %-14s ' % describe_p_type(segment['p_type'])) 256 257 if self.elffile.elfclass == 32: 258 self._emitline('%s %s %s %s %s %-3s %s' % ( 259 self._format_hex(segment['p_offset'], fieldsize=6), 260 self._format_hex(segment['p_vaddr'], fullhex=True), 261 self._format_hex(segment['p_paddr'], fullhex=True), 262 self._format_hex(segment['p_filesz'], fieldsize=5), 263 self._format_hex(segment['p_memsz'], fieldsize=5), 264 describe_p_flags(segment['p_flags']), 265 self._format_hex(segment['p_align']))) 266 else: # 64 267 self._emitline('%s %s %s' % ( 268 self._format_hex(segment['p_offset'], fullhex=True), 269 self._format_hex(segment['p_vaddr'], fullhex=True), 270 self._format_hex(segment['p_paddr'], fullhex=True))) 271 self._emitline(' %s %s %-3s %s' % ( 272 self._format_hex(segment['p_filesz'], fullhex=True), 273 self._format_hex(segment['p_memsz'], fullhex=True), 274 describe_p_flags(segment['p_flags']), 275 # lead0x set to False for p_align, to mimic readelf. 276 # No idea why the difference from 32-bit mode :-| 277 self._format_hex(segment['p_align'], lead0x=False))) 278 279 if isinstance(segment, InterpSegment): 280 self._emitline(' [Requesting program interpreter: %s]' % 281 segment.get_interp_name()) 282 283 # Sections to segments mapping 284 # 285 if self.elffile.num_sections() == 0: 286 # No sections? We're done 287 return 288 289 self._emitline('\n Section to Segment mapping:') 290 self._emitline(' Segment Sections...') 291 292 for nseg, segment in enumerate(self.elffile.iter_segments()): 293 self._emit(' %2.2d ' % nseg) 294 295 for section in self.elffile.iter_sections(): 296 if ( not section.is_null() and 297 not ((section['sh_flags'] & SH_FLAGS.SHF_TLS) != 0 and 298 section['sh_type'] == 'SHT_NOBITS' and 299 segment['p_type'] != 'PT_TLS') and 300 segment.section_in_segment(section)): 301 self._emit('%s ' % section.name) 302 303 self._emitline('') 304 305 def display_section_headers(self, show_heading=True): 306 """ Display the ELF section headers 307 """ 308 elfheader = self.elffile.header 309 if show_heading: 310 self._emitline('There are %s section headers, starting at offset %s' % ( 311 elfheader['e_shnum'], self._format_hex(elfheader['e_shoff']))) 312 313 if self.elffile.num_sections() == 0: 314 self._emitline('There are no sections in this file.') 315 return 316 317 self._emitline('\nSection Header%s:' % ( 318 's' if self.elffile.num_sections() > 1 else '')) 319 320 # Different formatting constraints of 32-bit and 64-bit addresses 321 # 322 if self.elffile.elfclass == 32: 323 self._emitline(' [Nr] Name Type Addr Off Size ES Flg Lk Inf Al') 324 else: 325 self._emitline(' [Nr] Name Type Address Offset') 326 self._emitline(' Size EntSize Flags Link Info Align') 327 328 # Now the entries 329 # 330 for nsec, section in enumerate(self.elffile.iter_sections()): 331 self._emit(' [%2u] %-17.17s %-15.15s ' % ( 332 nsec, section.name, describe_sh_type(section['sh_type']))) 333 334 if self.elffile.elfclass == 32: 335 self._emitline('%s %s %s %s %3s %2s %3s %2s' % ( 336 self._format_hex(section['sh_addr'], fieldsize=8, lead0x=False), 337 self._format_hex(section['sh_offset'], fieldsize=6, lead0x=False), 338 self._format_hex(section['sh_size'], fieldsize=6, lead0x=False), 339 self._format_hex(section['sh_entsize'], fieldsize=2, lead0x=False), 340 describe_sh_flags(section['sh_flags']), 341 section['sh_link'], section['sh_info'], 342 section['sh_addralign'])) 343 else: # 64 344 self._emitline(' %s %s' % ( 345 self._format_hex(section['sh_addr'], fullhex=True, lead0x=False), 346 self._format_hex(section['sh_offset'], 347 fieldsize=16 if section['sh_offset'] > 0xffffffff else 8, 348 lead0x=False))) 349 self._emitline(' %s %s %3s %2s %3s %s' % ( 350 self._format_hex(section['sh_size'], fullhex=True, lead0x=False), 351 self._format_hex(section['sh_entsize'], fullhex=True, lead0x=False), 352 describe_sh_flags(section['sh_flags']), 353 section['sh_link'], section['sh_info'], 354 section['sh_addralign'])) 355 356 self._emitline('Key to Flags:') 357 self._emitline(' W (write), A (alloc), X (execute), M (merge),' 358 ' S (strings), I (info),') 359 self._emitline(' L (link order), O (extra OS processing required),' 360 ' G (group), T (TLS),') 361 self._emitline(' C (compressed), x (unknown), o (OS specific),' 362 ' E (exclude),') 363 self._emit(' ') 364 if self.elffile['e_machine'] == 'EM_ARM': 365 self._emit('y (purecode), ') 366 self._emitline('p (processor specific)') 367 368 def display_symbol_tables(self): 369 """ Display the symbol tables contained in the file 370 """ 371 self._init_versioninfo() 372 373 symbol_tables = [(idx, s) for idx, s in enumerate(self.elffile.iter_sections()) 374 if isinstance(s, SymbolTableSection)] 375 376 if not symbol_tables and self.elffile.num_sections() == 0: 377 self._emitline('') 378 self._emitline('Dynamic symbol information is not available for' 379 ' displaying symbols.') 380 381 for section_index, section in symbol_tables: 382 if not isinstance(section, SymbolTableSection): 383 continue 384 385 if section['sh_entsize'] == 0: 386 self._emitline("\nSymbol table '%s' has a sh_entsize of zero!" % ( 387 section.name)) 388 continue 389 390 self._emitline("\nSymbol table '%s' contains %s entries:" % ( 391 section.name, section.num_symbols())) 392 393 if self.elffile.elfclass == 32: 394 self._emitline(' Num: Value Size Type Bind Vis Ndx Name') 395 else: # 64 396 self._emitline(' Num: Value Size Type Bind Vis Ndx Name') 397 398 for nsym, symbol in enumerate(section.iter_symbols()): 399 version_info = '' 400 # readelf doesn't display version info for Solaris versioning 401 if (section['sh_type'] == 'SHT_DYNSYM' and 402 self._versioninfo['type'] == 'GNU'): 403 version = self._symbol_version(nsym) 404 if (version['name'] != symbol.name and 405 version['index'] not in ('VER_NDX_LOCAL', 406 'VER_NDX_GLOBAL')): 407 if version['filename']: 408 # external symbol 409 version_info = '@%(name)s (%(index)i)' % version 410 else: 411 # internal symbol 412 if version['hidden']: 413 version_info = '@%(name)s' % version 414 else: 415 version_info = '@@%(name)s' % version 416 417 # symbol names are truncated to 25 chars, similarly to readelf 418 self._emitline('%6d: %s %s %-7s %-6s %-7s %4s %.25s%s' % ( 419 nsym, 420 self._format_hex( 421 symbol['st_value'], fullhex=True, lead0x=False), 422 "%5d" % symbol['st_size'] if symbol['st_size'] < 100000 else hex(symbol['st_size']), 423 describe_symbol_type(symbol['st_info']['type']), 424 describe_symbol_bind(symbol['st_info']['bind']), 425 describe_symbol_visibility(symbol['st_other']['visibility']), 426 describe_symbol_shndx(self._get_symbol_shndx(symbol, 427 nsym, 428 section_index)), 429 symbol.name, 430 version_info)) 431 432 def display_dynamic_tags(self): 433 """ Display the dynamic tags contained in the file 434 """ 435 has_dynamic_sections = False 436 for section in self.elffile.iter_sections(): 437 if not isinstance(section, DynamicSection): 438 continue 439 440 has_dynamic_sections = True 441 self._emitline("\nDynamic section at offset %s contains %s entries:" % ( 442 self._format_hex(section['sh_offset']), 443 section.num_tags())) 444 self._emitline(" Tag Type Name/Value") 445 446 padding = 20 + (8 if self.elffile.elfclass == 32 else 0) 447 for tag in section.iter_tags(): 448 if tag.entry.d_tag == 'DT_NEEDED': 449 parsed = 'Shared library: [%s]' % tag.needed 450 elif tag.entry.d_tag == 'DT_RPATH': 451 parsed = 'Library rpath: [%s]' % tag.rpath 452 elif tag.entry.d_tag == 'DT_RUNPATH': 453 parsed = 'Library runpath: [%s]' % tag.runpath 454 elif tag.entry.d_tag == 'DT_SONAME': 455 parsed = 'Library soname: [%s]' % tag.soname 456 elif tag.entry.d_tag.endswith(('SZ', 'ENT')): 457 parsed = '%i (bytes)' % tag['d_val'] 458 elif tag.entry.d_tag == 'DT_FLAGS': 459 parsed = describe_dt_flags(tag.entry.d_val) 460 elif tag.entry.d_tag == 'DT_FLAGS_1': 461 parsed = 'Flags: %s' % describe_dt_flags_1(tag.entry.d_val) 462 elif tag.entry.d_tag.endswith(('NUM', 'COUNT')): 463 parsed = '%i' % tag['d_val'] 464 elif tag.entry.d_tag == 'DT_PLTREL': 465 s = describe_dyn_tag(tag.entry.d_val) 466 if s.startswith('DT_'): 467 s = s[3:] 468 parsed = '%s' % s 469 elif tag.entry.d_tag == 'DT_MIPS_FLAGS': 470 parsed = describe_rh_flags(tag.entry.d_val) 471 elif tag.entry.d_tag in ('DT_MIPS_SYMTABNO', 472 'DT_MIPS_LOCAL_GOTNO'): 473 parsed = str(tag.entry.d_val) 474 else: 475 parsed = '%#x' % tag['d_val'] 476 477 self._emitline(" %s %-*s %s" % ( 478 self._format_hex(ENUM_D_TAG.get(tag.entry.d_tag, tag.entry.d_tag), 479 fullhex=True, lead0x=True), 480 padding, 481 '(%s)' % (tag.entry.d_tag[3:],), 482 parsed)) 483 if not has_dynamic_sections: 484 self._emitline("\nThere is no dynamic section in this file.") 485 486 def display_notes(self): 487 """ Display the notes contained in the file 488 """ 489 for section in self.elffile.iter_sections(): 490 if isinstance(section, NoteSection): 491 for note in section.iter_notes(): 492 self._emitline("\nDisplaying notes found in: {}".format( 493 section.name)) 494 self._emitline(' Owner Data size Description') 495 self._emitline(' %s %s\t%s' % ( 496 note['n_name'].ljust(20), 497 self._format_hex(note['n_descsz'], fieldsize=8), 498 describe_note(note))) 499 500 def display_relocations(self): 501 """ Display the relocations contained in the file 502 """ 503 has_relocation_sections = False 504 for section in self.elffile.iter_sections(): 505 if not isinstance(section, RelocationSection): 506 continue 507 508 has_relocation_sections = True 509 self._emitline("\nRelocation section '%.128s' at offset %s contains %s entries:" % ( 510 section.name, 511 self._format_hex(section['sh_offset']), 512 section.num_relocations())) 513 if section.is_RELA(): 514 self._emitline(" Offset Info Type Sym. Value Sym. Name + Addend") 515 else: 516 self._emitline(" Offset Info Type Sym.Value Sym. Name") 517 518 # The symbol table section pointed to in sh_link 519 symtable = self.elffile.get_section(section['sh_link']) 520 521 for rel in section.iter_relocations(): 522 hexwidth = 8 if self.elffile.elfclass == 32 else 12 523 self._emit('%s %s %-17.17s' % ( 524 self._format_hex(rel['r_offset'], 525 fieldsize=hexwidth, lead0x=False), 526 self._format_hex(rel['r_info'], 527 fieldsize=hexwidth, lead0x=False), 528 describe_reloc_type( 529 rel['r_info_type'], self.elffile))) 530 531 if rel['r_info_sym'] == 0: 532 if section.is_RELA(): 533 fieldsize = 8 if self.elffile.elfclass == 32 else 16 534 addend = self._format_hex(rel['r_addend'], lead0x=False) 535 self._emit(' %s %s' % (' ' * fieldsize, addend)) 536 self._emitline() 537 538 else: 539 symbol = symtable.get_symbol(rel['r_info_sym']) 540 # Some symbols have zero 'st_name', so instead what's used 541 # is the name of the section they point at. Truncate symbol 542 # names (excluding version info) to 22 chars, similarly to 543 # readelf. 544 if symbol['st_name'] == 0: 545 symsecidx = self._get_symbol_shndx(symbol, 546 rel['r_info_sym'], 547 section['sh_link']) 548 symsec = self.elffile.get_section(symsecidx) 549 symbol_name = symsec.name 550 version = '' 551 else: 552 symbol_name = symbol.name 553 version = self._symbol_version(rel['r_info_sym']) 554 version = (version['name'] 555 if version and version['name'] else '') 556 symbol_name = '%.22s' % symbol_name 557 if version: 558 symbol_name += '@' + version 559 560 self._emit(' %s %s' % ( 561 self._format_hex( 562 symbol['st_value'], 563 fullhex=True, lead0x=False), 564 symbol_name)) 565 if section.is_RELA(): 566 self._emit(' %s %x' % ( 567 '+' if rel['r_addend'] >= 0 else '-', 568 abs(rel['r_addend']))) 569 self._emitline() 570 571 # Emit the two additional relocation types for ELF64 MIPS 572 # binaries. 573 if (self.elffile.elfclass == 64 and 574 self.elffile['e_machine'] == 'EM_MIPS'): 575 for i in (2, 3): 576 rtype = rel['r_info_type%s' % i] 577 self._emit(' Type%s: %s' % ( 578 i, 579 describe_reloc_type(rtype, self.elffile))) 580 self._emitline() 581 582 if not has_relocation_sections: 583 self._emitline('\nThere are no relocations in this file.') 584 585 def display_arm_unwind(self): 586 if not self.elffile.has_ehabi_info(): 587 self._emitline('There are no .ARM.idx sections in this file.') 588 return 589 for ehabi_info in self.elffile.get_ehabi_infos(): 590 # Unwind section '.ARM.exidx' at offset 0x203e8 contains 1009 entries: 591 self._emitline("\nUnwind section '%s' at offset 0x%x contains %d entries" % ( 592 ehabi_info.section_name(), 593 ehabi_info.section_offset(), 594 ehabi_info.num_entry() 595 )) 596 597 for i in range(ehabi_info.num_entry()): 598 entry = ehabi_info.get_entry(i) 599 self._emitline() 600 self._emitline("Entry %d:" % i) 601 if isinstance(entry, CorruptEHABIEntry): 602 self._emitline(" [corrupt] %s" % entry.reason) 603 continue 604 self._emit(" Function offset 0x%x: " % entry.function_offset) 605 if isinstance(entry, CannotUnwindEHABIEntry): 606 self._emitline("[cantunwind]") 607 continue 608 elif entry.eh_table_offset: 609 self._emitline("@0x%x" % entry.eh_table_offset) 610 else: 611 self._emitline("Compact (inline)") 612 if isinstance(entry, GenericEHABIEntry): 613 self._emitline(" Personality: 0x%x" % entry.personality) 614 else: 615 self._emitline(" Compact model index: %d" % entry.personality) 616 for mnemonic_item in entry.mnmemonic_array(): 617 self._emit(' ') 618 self._emitline(mnemonic_item) 619 620 def display_version_info(self): 621 """ Display the version info contained in the file 622 """ 623 self._init_versioninfo() 624 625 if not self._versioninfo['type']: 626 self._emitline("\nNo version information found in this file.") 627 return 628 629 for section in self.elffile.iter_sections(): 630 if isinstance(section, GNUVerSymSection): 631 self._print_version_section_header( 632 section, 'Version symbols', lead0x=False) 633 634 num_symbols = section.num_symbols() 635 636 # Symbol version info are printed four by four entries 637 for idx_by_4 in range(0, num_symbols, 4): 638 639 self._emit(' %03x:' % idx_by_4) 640 641 for idx in range(idx_by_4, min(idx_by_4 + 4, num_symbols)): 642 643 symbol_version = self._symbol_version(idx) 644 if symbol_version['index'] == 'VER_NDX_LOCAL': 645 version_index = 0 646 version_name = '(*local*)' 647 elif symbol_version['index'] == 'VER_NDX_GLOBAL': 648 version_index = 1 649 version_name = '(*global*)' 650 else: 651 version_index = symbol_version['index'] 652 version_name = '(%(name)s)' % symbol_version 653 654 visibility = 'h' if symbol_version['hidden'] else ' ' 655 656 self._emit('%4x%s%-13s' % ( 657 version_index, visibility, version_name)) 658 659 self._emitline() 660 661 elif isinstance(section, GNUVerDefSection): 662 self._print_version_section_header( 663 section, 'Version definition', indent=2) 664 665 offset = 0 666 for verdef, verdaux_iter in section.iter_versions(): 667 verdaux = next(verdaux_iter) 668 669 name = verdaux.name 670 if verdef['vd_flags']: 671 flags = describe_ver_flags(verdef['vd_flags']) 672 # Mimic exactly the readelf output 673 flags += ' ' 674 else: 675 flags = 'none' 676 677 self._emitline(' %s: Rev: %i Flags: %s Index: %i' 678 ' Cnt: %i Name: %s' % ( 679 self._format_hex(offset, fieldsize=6, 680 alternate=True), 681 verdef['vd_version'], flags, verdef['vd_ndx'], 682 verdef['vd_cnt'], name)) 683 684 verdaux_offset = ( 685 offset + verdef['vd_aux'] + verdaux['vda_next']) 686 for idx, verdaux in enumerate(verdaux_iter, start=1): 687 self._emitline(' %s: Parent %i: %s' % 688 (self._format_hex(verdaux_offset, fieldsize=4), 689 idx, verdaux.name)) 690 verdaux_offset += verdaux['vda_next'] 691 692 offset += verdef['vd_next'] 693 694 elif isinstance(section, GNUVerNeedSection): 695 self._print_version_section_header(section, 'Version needs') 696 697 offset = 0 698 for verneed, verneed_iter in section.iter_versions(): 699 700 self._emitline(' %s: Version: %i File: %s Cnt: %i' % ( 701 self._format_hex(offset, fieldsize=6, 702 alternate=True), 703 verneed['vn_version'], verneed.name, 704 verneed['vn_cnt'])) 705 706 vernaux_offset = offset + verneed['vn_aux'] 707 for idx, vernaux in enumerate(verneed_iter, start=1): 708 if vernaux['vna_flags']: 709 flags = describe_ver_flags(vernaux['vna_flags']) 710 # Mimic exactly the readelf output 711 flags += ' ' 712 else: 713 flags = 'none' 714 715 self._emitline( 716 ' %s: Name: %s Flags: %s Version: %i' % ( 717 self._format_hex(vernaux_offset, fieldsize=4), 718 vernaux.name, flags, 719 vernaux['vna_other'])) 720 721 vernaux_offset += vernaux['vna_next'] 722 723 offset += verneed['vn_next'] 724 725 def display_arch_specific(self): 726 """ Display the architecture-specific info contained in the file. 727 """ 728 if self.elffile['e_machine'] == 'EM_ARM': 729 self._display_arch_specific_arm() 730 731 def display_hex_dump(self, section_spec): 732 """ Display a hex dump of a section. section_spec is either a section 733 number or a name. 734 """ 735 section = self._section_from_spec(section_spec) 736 if section is None: 737 # readelf prints the warning to stderr. Even though stderrs are not compared 738 # in tests, we comply with that behavior. 739 sys.stderr.write('readelf: Warning: Section \'%s\' was not dumped because it does not exist!\n' % ( 740 section_spec)) 741 return 742 if section['sh_type'] == 'SHT_NOBITS': 743 self._emitline("\nSection '%s' has no data to dump." % ( 744 section_spec)) 745 return 746 747 self._emitline("\nHex dump of section '%s':" % section.name) 748 self._note_relocs_for_section(section) 749 addr = section['sh_addr'] 750 data = section.data() 751 dataptr = 0 752 753 while dataptr < len(data): 754 bytesleft = len(data) - dataptr 755 # chunks of 16 bytes per line 756 linebytes = 16 if bytesleft > 16 else bytesleft 757 758 self._emit(' %s ' % self._format_hex(addr, fieldsize=8)) 759 for i in range(16): 760 if i < linebytes: 761 self._emit('%2.2x' % byte2int(data[dataptr + i])) 762 else: 763 self._emit(' ') 764 if i % 4 == 3: 765 self._emit(' ') 766 767 for i in range(linebytes): 768 c = data[dataptr + i : dataptr + i + 1] 769 if byte2int(c[0]) >= 32 and byte2int(c[0]) < 0x7f: 770 self._emit(bytes2str(c)) 771 else: 772 self._emit(bytes2str(b'.')) 773 774 self._emitline() 775 addr += linebytes 776 dataptr += linebytes 777 778 self._emitline() 779 780 def display_string_dump(self, section_spec): 781 """ Display a strings dump of a section. section_spec is either a 782 section number or a name. 783 """ 784 section = self._section_from_spec(section_spec) 785 if section is None: 786 # readelf prints the warning to stderr. Even though stderrs are not compared 787 # in tests, we comply with that behavior. 788 sys.stderr.write('readelf.py: Warning: Section \'%s\' was not dumped because it does not exist!\n' % ( 789 section_spec)) 790 return 791 if section['sh_type'] == 'SHT_NOBITS': 792 self._emitline("\nSection '%s' has no data to dump." % ( 793 section_spec)) 794 return 795 796 self._emitline("\nString dump of section '%s':" % section.name) 797 798 found = False 799 data = section.data() 800 dataptr = 0 801 802 while dataptr < len(data): 803 while ( dataptr < len(data) and 804 not (32 <= byte2int(data[dataptr]) <= 127)): 805 dataptr += 1 806 807 if dataptr >= len(data): 808 break 809 810 endptr = dataptr 811 while endptr < len(data) and byte2int(data[endptr]) != 0: 812 endptr += 1 813 814 found = True 815 self._emitline(' [%6x] %s' % ( 816 dataptr, bytes2str(data[dataptr:endptr]))) 817 818 dataptr = endptr 819 820 if not found: 821 self._emitline(' No strings found in this section.') 822 else: 823 self._emitline() 824 825 def display_debug_dump(self, dump_what): 826 """ Dump a DWARF section 827 """ 828 self._init_dwarfinfo() 829 if self._dwarfinfo is None: 830 return 831 832 set_global_machine_arch(self.elffile.get_machine_arch()) 833 834 if dump_what == 'info': 835 self._dump_debug_info() 836 elif dump_what == 'decodedline': 837 self._dump_debug_line_programs() 838 elif dump_what == 'frames': 839 self._dump_debug_frames() 840 elif dump_what == 'frames-interp': 841 self._dump_debug_frames_interp() 842 elif dump_what == 'aranges': 843 self._dump_debug_aranges() 844 elif dump_what in { 'pubtypes', 'pubnames' }: 845 self._dump_debug_namelut(dump_what) 846 elif dump_what == 'loc': 847 self._dump_debug_locations() 848 else: 849 self._emitline('debug dump not yet supported for "%s"' % dump_what) 850 851 def _format_hex(self, addr, fieldsize=None, fullhex=False, lead0x=True, 852 alternate=False): 853 """ Format an address into a hexadecimal string. 854 855 fieldsize: 856 Size of the hexadecimal field (with leading zeros to fit the 857 address into. For example with fieldsize=8, the format will 858 be %08x 859 If None, the minimal required field size will be used. 860 861 fullhex: 862 If True, override fieldsize to set it to the maximal size 863 needed for the elfclass 864 865 lead0x: 866 If True, leading 0x is added 867 868 alternate: 869 If True, override lead0x to emulate the alternate 870 hexadecimal form specified in format string with the # 871 character: only non-zero values are prefixed with 0x. 872 This form is used by readelf. 873 """ 874 if alternate: 875 if addr == 0: 876 lead0x = False 877 else: 878 lead0x = True 879 fieldsize -= 2 880 881 s = '0x' if lead0x else '' 882 if fullhex: 883 fieldsize = 8 if self.elffile.elfclass == 32 else 16 884 if fieldsize is None: 885 field = '%x' 886 else: 887 field = '%' + '0%sx' % fieldsize 888 return s + field % addr 889 890 def _print_version_section_header(self, version_section, name, lead0x=True, 891 indent=1): 892 """ Print a section header of one version related section (versym, 893 verneed or verdef) with some options to accomodate readelf 894 little differences between each header (e.g. indentation 895 and 0x prefixing). 896 """ 897 if hasattr(version_section, 'num_versions'): 898 num_entries = version_section.num_versions() 899 else: 900 num_entries = version_section.num_symbols() 901 902 self._emitline("\n%s section '%s' contains %s entries:" % 903 (name, version_section.name, num_entries)) 904 self._emitline('%sAddr: %s Offset: %s Link: %i (%s)' % ( 905 ' ' * indent, 906 self._format_hex( 907 version_section['sh_addr'], fieldsize=16, lead0x=lead0x), 908 self._format_hex( 909 version_section['sh_offset'], fieldsize=6, lead0x=True), 910 version_section['sh_link'], 911 self.elffile.get_section(version_section['sh_link']).name 912 ) 913 ) 914 915 def _init_versioninfo(self): 916 """ Search and initialize informations about version related sections 917 and the kind of versioning used (GNU or Solaris). 918 """ 919 if self._versioninfo is not None: 920 return 921 922 self._versioninfo = {'versym': None, 'verdef': None, 923 'verneed': None, 'type': None} 924 925 for section in self.elffile.iter_sections(): 926 if isinstance(section, GNUVerSymSection): 927 self._versioninfo['versym'] = section 928 elif isinstance(section, GNUVerDefSection): 929 self._versioninfo['verdef'] = section 930 elif isinstance(section, GNUVerNeedSection): 931 self._versioninfo['verneed'] = section 932 elif isinstance(section, DynamicSection): 933 for tag in section.iter_tags(): 934 if tag['d_tag'] == 'DT_VERSYM': 935 self._versioninfo['type'] = 'GNU' 936 break 937 938 if not self._versioninfo['type'] and ( 939 self._versioninfo['verneed'] or self._versioninfo['verdef']): 940 self._versioninfo['type'] = 'Solaris' 941 942 def _symbol_version(self, nsym): 943 """ Return a dict containing information on the 944 or None if no version information is available 945 """ 946 self._init_versioninfo() 947 948 symbol_version = dict.fromkeys(('index', 'name', 'filename', 'hidden')) 949 950 if (not self._versioninfo['versym'] or 951 nsym >= self._versioninfo['versym'].num_symbols()): 952 return None 953 954 symbol = self._versioninfo['versym'].get_symbol(nsym) 955 index = symbol.entry['ndx'] 956 if not index in ('VER_NDX_LOCAL', 'VER_NDX_GLOBAL'): 957 index = int(index) 958 959 if self._versioninfo['type'] == 'GNU': 960 # In GNU versioning mode, the highest bit is used to 961 # store whether the symbol is hidden or not 962 if index & 0x8000: 963 index &= ~0x8000 964 symbol_version['hidden'] = True 965 966 if (self._versioninfo['verdef'] and 967 index <= self._versioninfo['verdef'].num_versions()): 968 _, verdaux_iter = \ 969 self._versioninfo['verdef'].get_version(index) 970 symbol_version['name'] = next(verdaux_iter).name 971 else: 972 verneed, vernaux = \ 973 self._versioninfo['verneed'].get_version(index) 974 symbol_version['name'] = vernaux.name 975 symbol_version['filename'] = verneed.name 976 977 symbol_version['index'] = index 978 return symbol_version 979 980 def _section_from_spec(self, spec): 981 """ Retrieve a section given a "spec" (either number or name). 982 Return None if no such section exists in the file. 983 """ 984 try: 985 num = int(spec) 986 if num < self.elffile.num_sections(): 987 return self.elffile.get_section(num) 988 else: 989 return None 990 except ValueError: 991 # Not a number. Must be a name then 992 return self.elffile.get_section_by_name(spec) 993 994 def _get_symbol_shndx(self, symbol, symbol_index, symtab_index): 995 """ Get the index into the section header table for the "symbol" 996 at "symbol_index" located in the symbol table with section index 997 "symtab_index". 998 """ 999 symbol_shndx = symbol['st_shndx'] 1000 if symbol_shndx != SHN_INDICES.SHN_XINDEX: 1001 return symbol_shndx 1002 1003 # Check for or lazily construct index section mapping (symbol table 1004 # index -> corresponding symbol table index section object) 1005 if self._shndx_sections is None: 1006 self._shndx_sections = {sec.symboltable: sec for sec in self.elffile.iter_sections() 1007 if isinstance(sec, SymbolTableIndexSection)} 1008 return self._shndx_sections[symtab_index].get_section_index(symbol_index) 1009 1010 def _note_relocs_for_section(self, section): 1011 """ If there are relocation sections pointing to the givne section, 1012 emit a note about it. 1013 """ 1014 for relsec in self.elffile.iter_sections(): 1015 if isinstance(relsec, RelocationSection): 1016 info_idx = relsec['sh_info'] 1017 if self.elffile.get_section(info_idx) == section: 1018 self._emitline(' Note: This section has relocations against it, but these have NOT been applied to this dump.') 1019 return 1020 1021 def _init_dwarfinfo(self): 1022 """ Initialize the DWARF info contained in the file and assign it to 1023 self._dwarfinfo. 1024 Leave self._dwarfinfo at None if no DWARF info was found in the file 1025 """ 1026 if self._dwarfinfo is not None: 1027 return 1028 1029 if self.elffile.has_dwarf_info(): 1030 self._dwarfinfo = self.elffile.get_dwarf_info() 1031 else: 1032 self._dwarfinfo = None 1033 1034 def _dump_debug_info(self): 1035 """ Dump the debugging info section. 1036 """ 1037 if not self._dwarfinfo.has_debug_info: 1038 return 1039 self._emitline('Contents of the %s section:\n' % self._dwarfinfo.debug_info_sec.name) 1040 1041 # Offset of the .debug_info section in the stream 1042 section_offset = self._dwarfinfo.debug_info_sec.global_offset 1043 1044 for cu in self._dwarfinfo.iter_CUs(): 1045 self._emitline(' Compilation Unit @ offset %s:' % 1046 self._format_hex(cu.cu_offset)) 1047 self._emitline(' Length: %s (%s)' % ( 1048 self._format_hex(cu['unit_length']), 1049 '%s-bit' % cu.dwarf_format())) 1050 self._emitline(' Version: %s' % cu['version']), 1051 self._emitline(' Abbrev Offset: %s' % ( 1052 self._format_hex(cu['debug_abbrev_offset']))), 1053 self._emitline(' Pointer Size: %s' % cu['address_size']) 1054 1055 # The nesting depth of each DIE within the tree of DIEs must be 1056 # displayed. To implement this, a counter is incremented each time 1057 # the current DIE has children, and decremented when a null die is 1058 # encountered. Due to the way the DIE tree is serialized, this will 1059 # correctly reflect the nesting depth 1060 # 1061 die_depth = 0 1062 current_function = None 1063 for die in cu.iter_DIEs(): 1064 if die.tag == 'DW_TAG_subprogram': 1065 current_function = die 1066 self._emitline(' <%s><%x>: Abbrev Number: %s%s' % ( 1067 die_depth, 1068 die.offset, 1069 die.abbrev_code, 1070 (' (%s)' % die.tag) if not die.is_null() else '')) 1071 if die.is_null(): 1072 die_depth -= 1 1073 continue 1074 1075 for attr in itervalues(die.attributes): 1076 name = attr.name 1077 # Unknown attribute values are passed-through as integers 1078 if isinstance(name, int): 1079 name = 'Unknown AT value: %x' % name 1080 1081 attr_desc = describe_attr_value(attr, die, section_offset) 1082 1083 if 'DW_OP_fbreg' in attr_desc and current_function and not 'DW_AT_frame_base' in current_function.attributes: 1084 postfix = ' [without dw_at_frame_base]' 1085 else: 1086 postfix = '' 1087 1088 self._emitline(' <%x> %-18s: %s%s' % ( 1089 attr.offset, 1090 name, 1091 attr_desc, 1092 postfix)) 1093 1094 if die.has_children: 1095 die_depth += 1 1096 1097 self._emitline() 1098 1099 def _dump_debug_line_programs(self): 1100 """ Dump the (decoded) line programs from .debug_line 1101 The programs are dumped in the order of the CUs they belong to. 1102 """ 1103 if not self._dwarfinfo.has_debug_info: 1104 return 1105 self._emitline('Decoded dump of debug contents of section %s:\n' % self._dwarfinfo.debug_line_sec.name) 1106 1107 for cu in self._dwarfinfo.iter_CUs(): 1108 lineprogram = self._dwarfinfo.line_program_for_CU(cu) 1109 1110 cu_filename = bytes2str(lineprogram['file_entry'][0].name) 1111 if len(lineprogram['include_directory']) > 0: 1112 dir_index = lineprogram['file_entry'][0].dir_index 1113 if dir_index > 0: 1114 dir = lineprogram['include_directory'][dir_index - 1] 1115 else: 1116 dir = b'.' 1117 cu_filename = '%s/%s' % (bytes2str(dir), cu_filename) 1118 1119 self._emitline('CU: %s:' % cu_filename) 1120 self._emitline('File name Line number Starting address') 1121 1122 # Print each state's file, line and address information. For some 1123 # instructions other output is needed to be compatible with 1124 # readelf. 1125 for entry in lineprogram.get_entries(): 1126 state = entry.state 1127 if state is None: 1128 # Special handling for commands that don't set a new state 1129 if entry.command == DW_LNS_set_file: 1130 file_entry = lineprogram['file_entry'][entry.args[0] - 1] 1131 if file_entry.dir_index == 0: 1132 # current directory 1133 self._emitline('\n./%s:[++]' % ( 1134 bytes2str(file_entry.name))) 1135 else: 1136 self._emitline('\n%s/%s:' % ( 1137 bytes2str(lineprogram['include_directory'][file_entry.dir_index - 1]), 1138 bytes2str(file_entry.name))) 1139 elif entry.command == DW_LNE_define_file: 1140 self._emitline('%s:' % ( 1141 bytes2str(lineprogram['include_directory'][entry.args[0].dir_index]))) 1142 elif not state.end_sequence: 1143 # readelf doesn't print the state after end_sequence 1144 # instructions. I think it's a bug but to be compatible 1145 # I don't print them too. 1146 if lineprogram['version'] < 4: 1147 self._emitline('%-35s %11d %18s' % ( 1148 bytes2str(lineprogram['file_entry'][state.file - 1].name), 1149 state.line, 1150 '0' if state.address == 0 else 1151 self._format_hex(state.address))) 1152 else: 1153 self._emitline('%-35s %11d %18s[%d]' % ( 1154 bytes2str(lineprogram['file_entry'][state.file - 1].name), 1155 state.line, 1156 '0' if state.address == 0 else 1157 self._format_hex(state.address), 1158 state.op_index)) 1159 if entry.command == DW_LNS_copy: 1160 # Another readelf oddity... 1161 self._emitline() 1162 1163 def _dump_frames_info(self, section, cfi_entries): 1164 """ Dump the raw call frame info in a section. 1165 1166 `section` is the Section instance that contains the call frame info 1167 while `cfi_entries` must be an iterable that yields the sequence of 1168 CIE or FDE instances. 1169 """ 1170 self._emitline('Contents of the %s section:' % section.name) 1171 1172 for entry in cfi_entries: 1173 if isinstance(entry, CIE): 1174 self._emitline('\n%08x %s %s CIE' % ( 1175 entry.offset, 1176 self._format_hex(entry['length'], fullhex=True, lead0x=False), 1177 self._format_hex(entry['CIE_id'], fieldsize=8, lead0x=False))) 1178 self._emitline(' Version: %d' % entry['version']) 1179 self._emitline(' Augmentation: "%s"' % bytes2str(entry['augmentation'])) 1180 self._emitline(' Code alignment factor: %u' % entry['code_alignment_factor']) 1181 self._emitline(' Data alignment factor: %d' % entry['data_alignment_factor']) 1182 self._emitline(' Return address column: %d' % entry['return_address_register']) 1183 if entry.augmentation_bytes: 1184 self._emitline(' Augmentation data: {}'.format(' '.join( 1185 '{:02x}'.format(ord(b)) 1186 for b in iterbytes(entry.augmentation_bytes) 1187 ))) 1188 self._emitline() 1189 1190 elif isinstance(entry, FDE): 1191 self._emitline('\n%08x %s %s FDE cie=%08x pc=%s..%s' % ( 1192 entry.offset, 1193 self._format_hex(entry['length'], fullhex=True, lead0x=False), 1194 self._format_hex(entry['CIE_pointer'], fieldsize=8, lead0x=False), 1195 entry.cie.offset, 1196 self._format_hex(entry['initial_location'], fullhex=True, lead0x=False), 1197 self._format_hex( 1198 entry['initial_location'] + entry['address_range'], 1199 fullhex=True, lead0x=False))) 1200 if entry.augmentation_bytes: 1201 self._emitline(' Augmentation data: {}'.format(' '.join( 1202 '{:02x}'.format(ord(b)) 1203 for b in iterbytes(entry.augmentation_bytes) 1204 ))) 1205 1206 else: # ZERO terminator 1207 assert isinstance(entry, ZERO) 1208 self._emitline('\n%08x ZERO terminator' % entry.offset) 1209 continue 1210 1211 self._emit(describe_CFI_instructions(entry)) 1212 self._emitline() 1213 1214 def _dump_debug_frames(self): 1215 """ Dump the raw frame info from .debug_frame and .eh_frame sections. 1216 """ 1217 if self._dwarfinfo.has_EH_CFI(): 1218 self._dump_frames_info( 1219 self._dwarfinfo.eh_frame_sec, 1220 self._dwarfinfo.EH_CFI_entries()) 1221 self._emitline() 1222 1223 if self._dwarfinfo.has_CFI(): 1224 self._dump_frames_info( 1225 self._dwarfinfo.debug_frame_sec, 1226 self._dwarfinfo.CFI_entries()) 1227 1228 def _dump_debug_namelut(self, what): 1229 """ 1230 Dump the debug pubnames section. 1231 """ 1232 if what == 'pubnames': 1233 namelut = self._dwarfinfo.get_pubnames() 1234 section = self._dwarfinfo.debug_pubnames_sec 1235 else: 1236 namelut = self._dwarfinfo.get_pubtypes() 1237 section = self._dwarfinfo.debug_pubtypes_sec 1238 1239 # readelf prints nothing if the section is not present. 1240 if namelut is None or len(namelut) == 0: 1241 return 1242 1243 self._emitline('Contents of the %s section:' % section.name) 1244 self._emitline() 1245 1246 cu_headers = namelut.get_cu_headers() 1247 1248 # go over CU-by-CU first and item-by-item next. 1249 for (cu_hdr, (cu_ofs, items)) in izip(cu_headers, itertools.groupby( 1250 namelut.items(), key = lambda x: x[1].cu_ofs)): 1251 1252 self._emitline(' Length: %d' % cu_hdr.unit_length) 1253 self._emitline(' Version: %d' % cu_hdr.version) 1254 self._emitline(' Offset into .debug_info section: 0x%x' % cu_hdr.debug_info_offset) 1255 self._emitline(' Size of area in .debug_info section: %d' % cu_hdr.debug_info_length) 1256 self._emitline() 1257 self._emitline(' Offset Name') 1258 for item in items: 1259 self._emitline(' %x %s' % (item[1].die_ofs - cu_ofs, item[0])) 1260 self._emitline() 1261 1262 def _dump_debug_aranges(self): 1263 """ Dump the aranges table 1264 """ 1265 aranges_table = self._dwarfinfo.get_aranges() 1266 if aranges_table == None: 1267 return 1268 # seems redundent, but we need to get the unsorted set of entries to match system readelf 1269 unordered_entries = aranges_table._get_entries() 1270 1271 if len(unordered_entries) == 0: 1272 self._emitline() 1273 self._emitline("Section '.debug_aranges' has no debugging data.") 1274 return 1275 1276 self._emitline('Contents of the %s section:' % self._dwarfinfo.debug_aranges_sec.name) 1277 self._emitline() 1278 prev_offset = None 1279 for entry in unordered_entries: 1280 if prev_offset != entry.info_offset: 1281 if entry != unordered_entries[0]: 1282 self._emitline(' %s %s' % ( 1283 self._format_hex(0, fullhex=True, lead0x=False), 1284 self._format_hex(0, fullhex=True, lead0x=False))) 1285 self._emitline(' Length: %d' % (entry.unit_length)) 1286 self._emitline(' Version: %d' % (entry.version)) 1287 self._emitline(' Offset into .debug_info: 0x%x' % (entry.info_offset)) 1288 self._emitline(' Pointer Size: %d' % (entry.address_size)) 1289 self._emitline(' Segment Size: %d' % (entry.segment_size)) 1290 self._emitline() 1291 self._emitline(' Address Length') 1292 self._emitline(' %s %s' % ( 1293 self._format_hex(entry.begin_addr, fullhex=True, lead0x=False), 1294 self._format_hex(entry.length, fullhex=True, lead0x=False))) 1295 prev_offset = entry.info_offset 1296 self._emitline(' %s %s' % ( 1297 self._format_hex(0, fullhex=True, lead0x=False), 1298 self._format_hex(0, fullhex=True, lead0x=False))) 1299 1300 def _dump_frames_interp_info(self, section, cfi_entries): 1301 """ Dump interpreted (decoded) frame information in a section. 1302 1303 `section` is the Section instance that contains the call frame info 1304 while `cfi_entries` must be an iterable that yields the sequence of 1305 CIE or FDE instances. 1306 """ 1307 self._emitline('Contents of the %s section:' % section.name) 1308 1309 for entry in cfi_entries: 1310 if isinstance(entry, CIE): 1311 self._emitline('\n%08x %s %s CIE "%s" cf=%d df=%d ra=%d' % ( 1312 entry.offset, 1313 self._format_hex(entry['length'], fullhex=True, lead0x=False), 1314 self._format_hex(entry['CIE_id'], fieldsize=8, lead0x=False), 1315 bytes2str(entry['augmentation']), 1316 entry['code_alignment_factor'], 1317 entry['data_alignment_factor'], 1318 entry['return_address_register'])) 1319 ra_regnum = entry['return_address_register'] 1320 1321 elif isinstance(entry, FDE): 1322 self._emitline('\n%08x %s %s FDE cie=%08x pc=%s..%s' % ( 1323 entry.offset, 1324 self._format_hex(entry['length'], fullhex=True, lead0x=False), 1325 self._format_hex(entry['CIE_pointer'], fieldsize=8, lead0x=False), 1326 entry.cie.offset, 1327 self._format_hex(entry['initial_location'], fullhex=True, lead0x=False), 1328 self._format_hex(entry['initial_location'] + entry['address_range'], 1329 fullhex=True, lead0x=False))) 1330 ra_regnum = entry.cie['return_address_register'] 1331 1332 # If the FDE brings adds no unwinding information compared to 1333 # its CIE, omit its table. 1334 if (len(entry.get_decoded().table) == 1335 len(entry.cie.get_decoded().table)): 1336 continue 1337 1338 else: # ZERO terminator 1339 assert isinstance(entry, ZERO) 1340 self._emitline('\n%08x ZERO terminator' % entry.offset) 1341 continue 1342 1343 # Decode the table. 1344 decoded_table = entry.get_decoded() 1345 if len(decoded_table.table) == 0: 1346 continue 1347 1348 # Print the heading row for the decoded table 1349 self._emit(' LOC') 1350 self._emit(' ' if entry.structs.address_size == 4 else ' ') 1351 self._emit(' CFA ') 1352 1353 # Look at the registers the decoded table describes. 1354 # We build reg_order here to match readelf's order. In particular, 1355 # registers are sorted by their number, and the register matching 1356 # ra_regnum is always listed last with a special heading. 1357 decoded_table = entry.get_decoded() 1358 reg_order = sorted(ifilter( 1359 lambda r: r != ra_regnum, 1360 decoded_table.reg_order)) 1361 if len(decoded_table.reg_order): 1362 1363 # Headings for the registers 1364 for regnum in reg_order: 1365 self._emit('%-6s' % describe_reg_name(regnum)) 1366 self._emitline('ra ') 1367 1368 # Now include ra_regnum in reg_order to print its values 1369 # similarly to the other registers. 1370 reg_order.append(ra_regnum) 1371 else: 1372 self._emitline() 1373 1374 for line in decoded_table.table: 1375 self._emit(self._format_hex( 1376 line['pc'], fullhex=True, lead0x=False)) 1377 1378 if line['cfa'] is not None: 1379 s = describe_CFI_CFA_rule(line['cfa']) 1380 else: 1381 s = 'u' 1382 self._emit(' %-9s' % s) 1383 1384 for regnum in reg_order: 1385 if regnum in line: 1386 s = describe_CFI_register_rule(line[regnum]) 1387 else: 1388 s = 'u' 1389 self._emit('%-6s' % s) 1390 self._emitline() 1391 self._emitline() 1392 1393 def _dump_debug_frames_interp(self): 1394 """ Dump the interpreted (decoded) frame information from .debug_frame 1395 and .eh_framae sections. 1396 """ 1397 if self._dwarfinfo.has_EH_CFI(): 1398 self._dump_frames_interp_info( 1399 self._dwarfinfo.eh_frame_sec, 1400 self._dwarfinfo.EH_CFI_entries()) 1401 self._emitline() 1402 1403 if self._dwarfinfo.has_CFI(): 1404 self._dump_frames_interp_info( 1405 self._dwarfinfo.debug_frame_sec, 1406 self._dwarfinfo.CFI_entries()) 1407 1408 def _dump_debug_locations(self): 1409 """ Dump the location lists from .debug_location section 1410 """ 1411 def _get_cu_base(cu): 1412 top_die = cu.get_top_DIE() 1413 attr = top_die.attributes 1414 if 'DW_AT_low_pc' in attr: 1415 return attr['DW_AT_low_pc'].value 1416 elif 'DW_AT_entry_pc' in attr: 1417 return attr['DW_AT_entry_pc'].value 1418 else: 1419 raise ValueError("Can't find the base IP (low_pc) for a CU") 1420 1421 di = self._dwarfinfo 1422 loc_lists = di.location_lists() 1423 if not loc_lists: # No locations section - readelf outputs nothing 1424 return 1425 1426 loc_lists = list(loc_lists.iter_location_lists()) 1427 if len(loc_lists) == 0: 1428 # Present but empty locations section - readelf outputs a message 1429 self._emitline("\nSection '%s' has no debugging data." % di.debug_loc_sec.name) 1430 return 1431 1432 # To dump a location list, one needs to know the CU. 1433 # Scroll through DIEs once, list the known location list offsets 1434 cu_map = dict() # Loc list offset => CU 1435 for cu in di.iter_CUs(): 1436 for die in cu.iter_DIEs(): 1437 for key in die.attributes: 1438 attr = die.attributes[key] 1439 if (LocationParser.attribute_has_location(attr, cu['version']) and 1440 not LocationParser._attribute_has_loc_expr(attr, cu['version'])): 1441 cu_map[attr.value] = cu 1442 1443 addr_size = di.config.default_address_size # In bytes, 4 or 8 1444 addr_width = addr_size * 2 # In hex digits, 8 or 16 1445 line_template = " %%08x %%0%dx %%0%dx %%s%%s" % (addr_width, addr_width) 1446 1447 self._emitline('Contents of the %s section:\n' % di.debug_loc_sec.name) 1448 self._emitline(' Offset Begin End Expression') 1449 for loc_list in loc_lists: 1450 cu = cu_map.get(loc_list[0].entry_offset, False) 1451 if not cu: 1452 raise ValueError("Location list can't be tracked to a CU") 1453 base_ip = _get_cu_base(cu) 1454 for entry in loc_list: 1455 # TODO: support BaseAddressEntry lines 1456 expr = describe_DWARF_expr(entry.loc_expr, cu.structs, cu.cu_offset) 1457 postfix = ' (start == end)' if entry.begin_offset == entry.end_offset else '' 1458 self._emitline(line_template % ( 1459 entry.entry_offset, 1460 base_ip + entry.begin_offset, 1461 base_ip + entry.end_offset, 1462 expr, 1463 postfix)) 1464 # Pyelftools doesn't store the terminating entry, 1465 # but readelf emits its offset, so this should too. 1466 last = loc_list[-1] 1467 last_len = 2*addr_size 1468 if isinstance(last, LocationEntry): 1469 last_len += 2 + len(last.loc_expr) 1470 self._emitline(" %08x <End of list>" % (last.entry_offset + last_len)) 1471 1472 def _display_arch_specific_arm(self): 1473 """ Display the ARM architecture-specific info contained in the file. 1474 """ 1475 attr_sec = self.elffile.get_section_by_name('.ARM.attributes') 1476 1477 for s in attr_sec.iter_subsections(): 1478 self._emitline("Attribute Section: %s" % s.header['vendor_name']) 1479 for ss in s.iter_subsubsections(): 1480 h_val = "" if ss.header.extra is None else " ".join("%d" % x for x in ss.header.extra) 1481 self._emitline(describe_attr_tag_arm(ss.header.tag, h_val, None)) 1482 1483 for attr in ss.iter_attributes(): 1484 self._emit(' ') 1485 self._emitline(describe_attr_tag_arm(attr.tag, 1486 attr.value, 1487 attr.extra)) 1488 1489 def _emit(self, s=''): 1490 """ Emit an object to output 1491 """ 1492 self.output.write(str(s)) 1493 1494 def _emitline(self, s=''): 1495 """ Emit an object to output, followed by a newline 1496 """ 1497 self.output.write(str(s).rstrip() + '\n') 1498 1499 1500SCRIPT_DESCRIPTION = 'Display information about the contents of ELF format files' 1501VERSION_STRING = '%%(prog)s: based on pyelftools %s' % __version__ 1502 1503 1504def main(stream=None): 1505 # parse the command-line arguments and invoke ReadElf 1506 argparser = argparse.ArgumentParser( 1507 usage='usage: %(prog)s [options] <elf-file>', 1508 description=SCRIPT_DESCRIPTION, 1509 add_help=False, # -h is a real option of readelf 1510 prog='readelf.py') 1511 argparser.add_argument('file', 1512 nargs='?', default=None, 1513 help='ELF file to parse') 1514 argparser.add_argument('-v', '--version', 1515 action='version', version=VERSION_STRING) 1516 argparser.add_argument('-d', '--dynamic', 1517 action='store_true', dest='show_dynamic_tags', 1518 help='Display the dynamic section') 1519 argparser.add_argument('-H', '--help', 1520 action='store_true', dest='help', 1521 help='Display this information') 1522 argparser.add_argument('-h', '--file-header', 1523 action='store_true', dest='show_file_header', 1524 help='Display the ELF file header') 1525 argparser.add_argument('-l', '--program-headers', '--segments', 1526 action='store_true', dest='show_program_header', 1527 help='Display the program headers') 1528 argparser.add_argument('-S', '--section-headers', '--sections', 1529 action='store_true', dest='show_section_header', 1530 help="Display the sections' headers") 1531 argparser.add_argument('-e', '--headers', 1532 action='store_true', dest='show_all_headers', 1533 help='Equivalent to: -h -l -S') 1534 argparser.add_argument('-s', '--symbols', '--syms', 1535 action='store_true', dest='show_symbols', 1536 help='Display the symbol table') 1537 argparser.add_argument('-n', '--notes', 1538 action='store_true', dest='show_notes', 1539 help='Display the core notes (if present)') 1540 argparser.add_argument('-r', '--relocs', 1541 action='store_true', dest='show_relocs', 1542 help='Display the relocations (if present)') 1543 argparser.add_argument('-au', '--arm-unwind', 1544 action='store_true', dest='show_arm_unwind', 1545 help='Display the armeabi unwind information (if present)') 1546 argparser.add_argument('-x', '--hex-dump', 1547 action='store', dest='show_hex_dump', metavar='<number|name>', 1548 help='Dump the contents of section <number|name> as bytes') 1549 argparser.add_argument('-p', '--string-dump', 1550 action='store', dest='show_string_dump', metavar='<number|name>', 1551 help='Dump the contents of section <number|name> as strings') 1552 argparser.add_argument('-V', '--version-info', 1553 action='store_true', dest='show_version_info', 1554 help='Display the version sections (if present)') 1555 argparser.add_argument('-A', '--arch-specific', 1556 action='store_true', dest='show_arch_specific', 1557 help='Display the architecture-specific information (if present)') 1558 argparser.add_argument('--debug-dump', 1559 action='store', dest='debug_dump_what', metavar='<what>', 1560 help=( 1561 'Display the contents of DWARF debug sections. <what> can ' + 1562 'one of {info,decodedline,frames,frames-interp,aranges,pubtypes,pubnames,loc}')) 1563 argparser.add_argument('--traceback', 1564 action='store_true', dest='show_traceback', 1565 help='Dump the Python traceback on ELFError' 1566 ' exceptions from elftools') 1567 1568 args = argparser.parse_args() 1569 1570 if args.help or not args.file: 1571 argparser.print_help() 1572 sys.exit(0) 1573 1574 if args.show_all_headers: 1575 do_file_header = do_section_header = do_program_header = True 1576 else: 1577 do_file_header = args.show_file_header 1578 do_section_header = args.show_section_header 1579 do_program_header = args.show_program_header 1580 1581 with open(args.file, 'rb') as file: 1582 try: 1583 readelf = ReadElf(file, stream or sys.stdout) 1584 if do_file_header: 1585 readelf.display_file_header() 1586 if do_section_header: 1587 readelf.display_section_headers( 1588 show_heading=not do_file_header) 1589 if do_program_header: 1590 readelf.display_program_headers( 1591 show_heading=not do_file_header) 1592 if args.show_dynamic_tags: 1593 readelf.display_dynamic_tags() 1594 if args.show_symbols: 1595 readelf.display_symbol_tables() 1596 if args.show_notes: 1597 readelf.display_notes() 1598 if args.show_relocs: 1599 readelf.display_relocations() 1600 if args.show_arm_unwind: 1601 readelf.display_arm_unwind() 1602 if args.show_version_info: 1603 readelf.display_version_info() 1604 if args.show_arch_specific: 1605 readelf.display_arch_specific() 1606 if args.show_hex_dump: 1607 readelf.display_hex_dump(args.show_hex_dump) 1608 if args.show_string_dump: 1609 readelf.display_string_dump(args.show_string_dump) 1610 if args.debug_dump_what: 1611 readelf.display_debug_dump(args.debug_dump_what) 1612 except ELFError as ex: 1613 sys.stdout.flush() 1614 sys.stderr.write('ELF error: %s\n' % ex) 1615 if args.show_traceback: 1616 traceback.print_exc() 1617 sys.exit(1) 1618 1619 1620def profile_main(): 1621 # Run 'main' redirecting its output to readelfout.txt 1622 # Saves profiling information in readelf.profile 1623 PROFFILE = 'readelf.profile' 1624 import cProfile 1625 cProfile.run('main(open("readelfout.txt", "w"))', PROFFILE) 1626 1627 # Dig in some profiling stats 1628 import pstats 1629 p = pstats.Stats(PROFFILE) 1630 p.sort_stats('cumulative').print_stats(25) 1631 1632 1633#------------------------------------------------------------------------------- 1634if __name__ == '__main__': 1635 main() 1636 #profile_main() 1637