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