Fix for mixed version loclists, tests (#521)
[pyelftools.git] / scripts / dwarfdump.py
1 #!/usr/bin/env python
2 #-------------------------------------------------------------------------------
3 # scripts/dwarfdump.py
4 #
5 # A clone of 'llvm-dwarfdump' in Python, based on the pyelftools library
6 # Roughly corresponding to v15
7 #
8 # Sources under https://github.com/llvm/llvm-project/tree/main/llvm/tools/llvm-dwarfdump
9 #
10 # Utterly incompatible with 64-bit DWARF or DWARFv2 targeting a 64-bit machine.
11 # Also incompatible with machines that have a selector/segment in the address.
12 #
13 # Eli Bendersky (eliben@gmail.com)
14 # This code is in the public domain
15 #-------------------------------------------------------------------------------
16 import argparse
17 import os, sys
18 import traceback
19
20 # For running from development directory. It should take precedence over the
21 # installed pyelftools.
22 sys.path.insert(0, '.')
23
24 from elftools import __version__
25 from elftools.common.exceptions import DWARFError, ELFError
26 from elftools.common.utils import bytes2str
27 from elftools.elf.elffile import ELFFile
28 from elftools.dwarf.locationlists import LocationParser, LocationEntry, LocationExpr, LocationViewPair, BaseAddressEntry as LocBaseAddressEntry
29 from elftools.dwarf.ranges import RangeEntry # ranges.BaseAddressEntry collides with the one above
30 import elftools.dwarf.ranges
31 from elftools.dwarf.enums import *
32 from elftools.dwarf.dwarf_expr import DWARFExprParser, DWARFExprOp
33 from elftools.dwarf.datatype_cpp import DIE_name, describe_cpp_datatype
34 from elftools.dwarf.descriptions import describe_reg_name
35
36 # ------------------------------
37 # ------------------------------
38
39 def _get_cu_base(cu):
40 top_die = cu.get_top_DIE()
41 attr = top_die.attributes
42 if 'DW_AT_low_pc' in attr:
43 return attr['DW_AT_low_pc'].value
44 elif 'DW_AT_entry_pc' in attr:
45 return attr['DW_AT_entry_pc'].value
46 else:
47 raise ValueError("Can't find the base IP (low_pc) for a CU")
48
49 def _addr_str_length(die):
50 return die.cu.header.address_size*2
51
52 def _DIE_name(die):
53 if 'DW_AT_name' in die.attributes:
54 return bytes2str(die.attributes['DW_AT_name'].value)
55 elif 'DW_AT_linkage_name' in die.attributes:
56 return bytes2str(die.attributes['DW_AT_linkage_name'].value)
57 else:
58 raise DWARFError()
59
60 def _DIE_linkage_name(die):
61 if 'DW_AT_linkage_name' in die.attributes:
62 return bytes2str(die.attributes['DW_AT_linkage_name'].value)
63 elif 'DW_AT_name' in die.attributes:
64 return bytes2str(die.attributes['DW_AT_name'].value)
65 else:
66 raise DWARFError()
67
68 def _safe_DIE_name(die, default=None):
69 if 'DW_AT_name' in die.attributes:
70 return bytes2str(die.attributes['DW_AT_name'].value)
71 elif 'DW_AT_linkage_name' in die.attributes:
72 return bytes2str(die.attributes['DW_AT_linkage_name'].value)
73 else:
74 return default
75
76 def _safe_DIE_linkage_name(die, default=None):
77 if 'DW_AT_linkage_name' in die.attributes:
78 return bytes2str(die.attributes['DW_AT_linkage_name'].value)
79 elif 'DW_AT_name' in die.attributes:
80 return bytes2str(die.attributes['DW_AT_name'].value)
81 else:
82 return default
83
84 def _desc_ref(attr, die, extra=''):
85 if extra:
86 extra = " \"%s\"" % extra
87 return "cu + 0x%04x => {0x%08x}%s" % (
88 attr.raw_value,
89 die.cu.cu_offset + attr.raw_value,
90 extra)
91
92 def _desc_data(attr, die):
93 """ Hex with length driven by form
94 """
95 len = int(attr.form[12:]) * 2
96 return "0x%0*x" % (len, attr.value,)
97
98 def _desc_strx(attr, die):
99 return "indexed (%08x) string = \"%s\"" % (attr.raw_value, bytes2str(attr.value).replace("\\", "\\\\"))
100
101 FORM_DESCRIPTIONS = dict(
102 DW_FORM_string=lambda attr, die: "\"%s\"" % (bytes2str(attr.value),),
103 DW_FORM_strp=lambda attr, die: " .debug_str[0x%08x] = \"%s\"" % (attr.raw_value, bytes2str(attr.value).replace("\\", "\\\\")),
104 DW_FORM_strx1=_desc_strx,
105 DW_FORM_strx2=_desc_strx,
106 DW_FORM_strx3=_desc_strx,
107 DW_FORM_strx4=_desc_strx,
108 DW_FORM_line_strp=lambda attr, die: ".debug_line_str[0x%08x] = \"%s\"" % (attr.raw_value, bytes2str(attr.value).replace("\\", "\\\\")),
109 DW_FORM_flag_present=lambda attr, die: "true",
110 DW_FORM_flag=lambda attr, die: "0x%02x" % int(attr.value),
111 DW_FORM_addr=lambda attr, die: "0x%0*x" % (_addr_str_length(die), attr.value),
112 DW_FORM_addrx=lambda attr, die: "indexed (%08x) address = 0x%0*x" % (attr.raw_value, _addr_str_length(die), attr.value),
113 DW_FORM_data1=_desc_data,
114 DW_FORM_data2=_desc_data,
115 DW_FORM_data4=_desc_data,
116 DW_FORM_data8=_desc_data,
117 DW_FORM_block1=lambda attr, die: "<0x%02x> %s " % (len(attr.value), " ".join("%02x" %b for b in attr.value)),
118 DW_FORM_block2=lambda attr, die: "<0x%04x> %s " % (len(attr.value), " ".join("%02x" %b for b in attr.value)),
119 DW_FORM_block4=lambda attr, die: "<0x%08x> %s " % (len(attr.value), " ".join("%02x" %b for b in attr.value)),
120 DW_FORM_ref=_desc_ref,
121 DW_FORM_ref1=_desc_ref, DW_FORM_ref2=_desc_ref,
122 DW_FORM_ref4=_desc_ref, DW_FORM_ref8=_desc_ref,
123 DW_FORM_sec_offset=lambda attr,die: "0x%08x" % (attr.value,),
124 DW_FORM_exprloc=lambda attr, die: _desc_expression(attr.value, die)
125 )
126
127 def _desc_enum(attr, enum):
128 """For attributes like DW_AT_language, physically
129 int, logically an enum
130 """
131 return next((k for (k, v) in enum.items() if v == attr.value), str(attr.value))
132
133 def _cu_comp_dir(cu):
134 return bytes2str(cu.get_top_DIE().attributes['DW_AT_comp_dir'].value)
135
136 def _desc_decl_file(attr, die):
137 # Filename/dirname arrays are 0 based in DWARFv5
138 cu = die.cu
139 if not hasattr(cu, "_lineprogram"):
140 cu._lineprogram = die.dwarfinfo.line_program_for_CU(cu)
141 ver5 = cu._lineprogram.header.version >= 5
142 file_index = attr.value if ver5 else attr.value-1
143 if cu._lineprogram and file_index >= 0 and file_index < len(cu._lineprogram.header.file_entry):
144 file_entry = cu._lineprogram.header.file_entry[file_index]
145 dir_index = file_entry.dir_index if ver5 else file_entry.dir_index - 1
146 includes = cu._lineprogram.header.include_directory
147 if dir_index >= 0:
148 dir = bytes2str(includes[dir_index])
149 if dir.startswith('.'):
150 dir = os.path.join(_cu_comp_dir(cu), dir)
151 else:
152 dir = _cu_comp_dir(cu)
153 file_name = bytes2str(file_entry.name)
154 else:
155 raise DWARFError("Invalid source filename entry index in a decl_file attribute")
156 return "\"%s\"" % (os.path.join(dir, file_name),)
157
158
159 def _desc_ranges(attr, die):
160 di = die.cu.dwarfinfo
161 if not hasattr(di, '_rnglists'):
162 di._rangelists = di.range_lists()
163 rangelist = di._rangelists.get_range_list_at_offset(attr.value, die.cu)
164 base_ip = _get_cu_base(die.cu)
165 lines = []
166 addr_str_len = die.cu.header.address_size*2
167 for entry in rangelist:
168 if isinstance(entry, RangeEntry):
169 lines.append(" [0x%0*x, 0x%0*x)" % (
170 addr_str_len,
171 (0 if entry.is_absolute else base_ip) + entry.begin_offset,
172 addr_str_len,
173 (0 if entry.is_absolute else base_ip) + entry.end_offset))
174 elif isinstance(entry, elftools.dwarf.ranges.BaseAddressEntry):
175 base_ip = entry.base_address
176 else:
177 raise NotImplementedError("Unknown object in a range list")
178 prefix = "indexed (0x%x) rangelist = " % attr.raw_value if attr.form == 'DW_FORM_rnglistx' else ''
179 return ("%s0x%08x\n" % (prefix, attr.value)) + "\n".join(lines)
180
181 def _desc_locations(attr, die):
182 cu = die.cu
183 di = cu.dwarfinfo
184 if not hasattr(di, '_loclists'):
185 di._loclists = di.location_lists()
186 if not hasattr(di, '_locparser'):
187 di._locparser = LocationParser(di._loclists)
188 loclist = di._locparser.parse_from_attribute(attr, cu.header.version, die)
189 if isinstance(loclist, LocationExpr):
190 return _desc_expression(loclist.loc_expr, die)
191 else:
192 base_ip = _get_cu_base(cu)
193 lines = []
194 addr_str_len = die.cu.header.address_size*2
195 for entry in loclist:
196 if isinstance(entry, LocationEntry):
197 lines.append(" [0x%0*x, 0x%0*x): %s" % (
198 addr_str_len,
199 (0 if entry.is_absolute else base_ip) + entry.begin_offset,
200 addr_str_len,
201 (0 if entry.is_absolute else base_ip) + entry.end_offset,
202 _desc_expression(entry.loc_expr, die)))
203 elif isinstance(entry, LocBaseAddressEntry):
204 base_ip = entry.base_address
205 else:
206 raise NotImplementedError("Unknown object in a location list")
207 prefix = "indexed (0x%x) loclist = " % attr.raw_value if attr.form == 'DW_FORM_loclistx' else ''
208 return ("%s0x%08x:\n" % (prefix, attr.value)) + "\n".join(lines)
209
210 # By default, numeric arguments are spelled in hex with a leading 0x
211 def _desc_operationarg(s, cu):
212 if isinstance(s, str):
213 return s
214 elif isinstance(s, int):
215 return hex(s)
216 elif isinstance(s, list): # Could be a blob (list of ints), could be a subexpression
217 if len(s) > 0 and isinstance(s[0], DWARFExprOp): # Subexpression
218 return '(' + '; '.join(_desc_operation(op.op, op.op_name, op.args, cu) for op in s) + ')'
219 else:
220 return " ".join((hex(len(s)),) + tuple("0x%02x" % b for b in s))
221
222 def _arch(cu):
223 return cu.dwarfinfo.config.machine_arch
224
225 def _desc_reg(reg_no, cu):
226 return describe_reg_name(reg_no, _arch(cu), True).upper()
227
228 def _desc_operation(op, op_name, args, cu):
229 # Not sure about regx(regno) and bregx(regno, offset)
230 if 0x50 <= op <= 0x6f: # reg0...reg31 - decode reg name
231 return op_name + " " + _desc_reg(op - 0x50, cu)
232 elif 0x70 <= op <= 0x8f: # breg0...breg31(offset) - also decode reg name
233 return '%s %s%+d' % (
234 op_name,
235 _desc_reg(op - 0x70, cu),
236 args[0])
237 elif op_name in ('DW_OP_fbreg', 'DW_OP_bra', 'DW_OP_skip', 'DW_OP_consts', ): # Argument is decimal with a leading sign
238 return op_name + ' ' + "%+d" % (args[0])
239 elif op_name in ('DW_OP_const1s', 'DW_OP_const2s'): # Argument is decimal without a leading sign
240 return op_name + ' ' + "%d" % (args[0])
241 elif op_name in ('DW_OP_entry_value', 'DW_OP_GNU_entry_value'): # No space between opcode and args
242 return op_name + _desc_operationarg(args[0], cu)
243 elif op_name == 'DW_OP_regval_type': # Arg is a DIE pointer
244 return "%s %s (0x%08x -> 0x%08x) \"%s\"" % (
245 op_name,
246 _desc_reg(args[0], cu),
247 args[1],
248 args[1] + cu.cu_offset,
249 _DIE_name(cu._get_cached_DIE(args[1] + cu.cu_offset)))
250 elif op_name == 'DW_OP_convert': # Arg is a DIE pointer
251 return "%s (0x%08x -> 0x%08x) \"%s\"" % (
252 op_name,
253 args[0],
254 args[0] + cu.cu_offset,
255 _DIE_name(cu._get_cached_DIE(args[0] + cu.cu_offset)))
256 elif args:
257 return op_name + ' ' + ', '.join(_desc_operationarg(s, cu) for s in args)
258 else:
259 return op_name
260
261 # TODO: remove this once dwarfdump catches up
262 UNSUPPORTED_OPS = (
263 'DW_OP_implicit_pointer',
264 'DW_OP_deref_type',
265 'DW_OP_GNU_parameter_ref',
266 'DW_OP_GNU_deref_type',
267 'DW_OP_GNU_implicit_pointer',
268 'DW_OP_GNU_convert',
269 'DW_OP_GNU_regval_type')
270
271 def _desc_expression(expr, die):
272 cu = die.cu
273 if not hasattr(cu, '_exprparser'):
274 cu._exprparser = DWARFExprParser(cu.structs)
275
276 parsed = cu._exprparser.parse_expr(expr)
277 # TODO: remove this once dwarfdump catches up
278 first_unsupported = next((i for (i, op) in enumerate(parsed) if op.op_name in UNSUPPORTED_OPS), None)
279 if first_unsupported is None:
280 lines = [_desc_operation(op.op, op.op_name, op.args, cu) for op in parsed]
281 else:
282 lines = [_desc_operation(op.op, op.op_name, op.args, cu) for op in parsed[0:first_unsupported]]
283 start_of_unparsed = parsed[first_unsupported].offset
284 lines.append("<decoding error> " + " ".join("%02x" % b for b in expr[start_of_unparsed:]))
285 return ", ".join(lines)
286
287 def _desc_datatype(attr, die):
288 """Oy vey
289 """
290 return _desc_ref(attr, die, describe_cpp_datatype(die))
291
292 def _get_origin_name(die):
293 func_die = die.get_DIE_from_attribute('DW_AT_abstract_origin')
294 name = _safe_DIE_linkage_name(func_die, '')
295 if not name:
296 if 'DW_AT_specification' in func_die.attributes:
297 name = _DIE_linkage_name(func_die.get_DIE_from_attribute('DW_AT_specification'))
298 elif 'DW_AT_abstract_origin' in func_die.attributes:
299 return _get_origin_name(func_die)
300 return name
301
302 def _desc_origin(attr, die):
303 return _desc_ref(attr, die, _get_origin_name(die))
304
305 def _desc_spec(attr, die):
306 return _desc_ref(attr, die,
307 _DIE_linkage_name(die.get_DIE_from_attribute('DW_AT_specification')))
308
309 def _desc_value(attr, die):
310 return str(attr.value)
311
312 ATTR_DESCRIPTIONS = dict(
313 DW_AT_language=lambda attr, die: _desc_enum(attr, ENUM_DW_LANG),
314 DW_AT_encoding=lambda attr, die: _desc_enum(attr, ENUM_DW_ATE),
315 DW_AT_accessibility=lambda attr, die: _desc_enum(attr, ENUM_DW_ACCESS),
316 DW_AT_inline=lambda attr, die: _desc_enum(attr, ENUM_DW_INL),
317 DW_AT_calling_convention=lambda attr, die: _desc_enum(attr, ENUM_DW_CC),
318 DW_AT_decl_file=_desc_decl_file,
319 DW_AT_decl_line=_desc_value,
320 DW_AT_ranges=_desc_ranges,
321 DW_AT_location=_desc_locations,
322 DW_AT_data_member_location=lambda attr, die: _desc_data(attr, die) if attr.form.startswith('DW_FORM_data') else _desc_locations(attr, die),
323 DW_AT_frame_base=_desc_locations,
324 DW_AT_type=_desc_datatype,
325 DW_AT_call_line=_desc_value,
326 DW_AT_call_file=_desc_decl_file,
327 DW_AT_abstract_origin=_desc_origin,
328 DW_AT_specification=_desc_spec,
329 DW_AT_call_site_value=lambda attr, die: _desc_expression(attr.value, die) if attr.form.startswith('DW_FORM_block') else _desc_locations(attr, die),
330 DW_AT_GNU_call_site_value=lambda attr, die: _desc_expression(attr.value, die) if attr.form.startswith('DW_FORM_block') else _desc_locations(attr, die),
331 )
332
333 class ReadElf(object):
334 """ dump_xxx is used to dump the respective section.
335 Mimics the output of dwarfdump with --verbose
336 """
337 def __init__(self, filename, file, output):
338 """ file:
339 stream object with the ELF file to read
340
341 output:
342 output stream to write to
343 """
344 self.elffile = ELFFile(file)
345 self.output = output
346 self._dwarfinfo = self.elffile.get_dwarf_info()
347 arches = {"EM_386": "i386", "EM_X86_64": "x86-64", "EM_ARM": "littlearm", "EM_AARCH64": "littleaarch64", "EM_LOONGARCH": "loongarch", "EM_RISCV": "littleriscv", "EM_MIPS": "mips"}
348 arch = arches[self.elffile['e_machine']]
349 bits = self.elffile.elfclass
350 self._emitline("%s: file format elf%d-%s" % (filename, bits, arch))
351
352 def _emit(self, s=''):
353 """ Emit an object to output
354 """
355 self.output.write(str(s))
356
357 def _emitline(self, s=''):
358 """ Emit an object to output, followed by a newline
359 """
360 self.output.write(str(s).rstrip() + '\n')
361
362 def dump_info(self):
363 # TODO: DWARF64 will cause discrepancies in hex offset sizes
364 self._emitline(".debug_info contents:")
365 for cu in self._dwarfinfo.iter_CUs():
366 if cu.header.version >= 5:
367 unit_type_str = " unit_type = %s," % cu.header.unit_type
368 else:
369 unit_type_str = ''
370
371 self._emitline("0x%08x: Compile Unit: length = 0x%08x, format = DWARF%d, version = 0x%04x,%s abbr_offset = 0x%04x, addr_size = 0x%02x (next unit at 0x%08x)" %(
372 cu.cu_offset,
373 cu.header.unit_length,
374 cu.structs.dwarf_format,
375 cu.header.version,
376 unit_type_str,
377 cu.header.debug_abbrev_offset,
378 cu.header.address_size,
379 cu.cu_offset + (4 if cu.structs.dwarf_format == 32 else 12) + cu.header.unit_length))
380 self._emitline()
381 parent = cu.get_top_DIE()
382 for die in cu.iter_DIEs():
383 if die.get_parent() == parent:
384 parent = die
385 if not die.is_null():
386 self._emitline("0x%08x: %s [%d] %s %s" % (
387 die.offset,
388 die.tag,
389 die.abbrev_code,
390 '*' if die.has_children else '',
391 '(0x%08x)' % die.get_parent().offset if die.get_parent() is not None else ''))
392 for attr_name in die.attributes:
393 attr = die.attributes[attr_name]
394 self._emitline(" %s [%s] (%s)" % (attr_name, attr.form, self.describe_attr_value(die, attr)))
395 else:
396 self._emitline("0x%08x: NULL" % (die.offset,))
397 parent = die.get_parent()
398 self._emitline()
399
400 def describe_attr_value(self, die, attr):
401 """This describes the attribute value in the way that's compatible
402 with llvm_dwarfdump. Somewhat duplicates the work of describe_attr_value() in descriptions
403 """
404 if attr.name in ATTR_DESCRIPTIONS:
405 return ATTR_DESCRIPTIONS[attr.name](attr, die)
406 elif attr.form in FORM_DESCRIPTIONS:
407 return FORM_DESCRIPTIONS[attr.form](attr, die)
408 else:
409 return str(attr.value)
410
411 def dump_loc(self):
412 pass
413
414 def dump_loclists(self):
415 pass
416
417 def dump_ranges(self):
418 pass
419
420 def dump_v4_rangelist(self, rangelist, cu_map):
421 cu = cu_map[rangelist[0].entry_offset]
422 addr_str_len = cu.header.address_size*2
423 base_ip = _get_cu_base(cu)
424 for entry in rangelist:
425 if isinstance(entry, RangeEntry):
426 self._emitline("[0x%0*x, 0x%0*x)" % (
427 addr_str_len,
428 (0 if entry.is_absolute else base_ip) + entry.begin_offset,
429 addr_str_len,
430 (0 if entry.is_absolute else base_ip) + entry.end_offset))
431 elif isinstance(entry, elftools.dwarf.ranges.BaseAddressEntry):
432 base_ip = entry.base_address
433 else:
434 raise NotImplementedError("Unknown object in a range list")
435
436 def dump_rnglists(self):
437 self._emitline(".debug_rnglists contents:")
438 ranges_sec = self._dwarfinfo.range_lists()
439 if ranges_sec.version < 5:
440 return
441
442 cu_map = {die.attributes['DW_AT_ranges'].value : cu # Dict from range offset to home CU
443 for cu in self._dwarfinfo.iter_CUs()
444 for die in cu.iter_DIEs()
445 if 'DW_AT_ranges' in die.attributes}
446
447 for cu in ranges_sec.iter_CUs():
448 self._emitline("0x%08x: range list header: length = 0x%08x, format = DWARF%d, version = 0x%04x, addr_size = 0x%02x, seg_size = 0x%02x, offset_entry_count = 0x%08x" % (
449 cu.cu_offset,
450 cu.unit_length,
451 64 if cu.is64 else 32,
452 cu.version,
453 cu.address_size,
454 cu.segment_selector_size,
455 cu.offset_count))
456 self._emitline("ranges:")
457 if cu.offset_count > 0:
458 rangelists = [ranges_sec.get_range_list_at_offset_ex(offset) for offset in cu.offsets]
459 else:
460 rangelists = list(ranges_sec.iter_CU_range_lists_ex(cu))
461 # We have to parse it completely before dumping, because dwarfdump aligns columns,
462 # no way to do that without some lookahead
463 max_type_len = max(len(entry.entry_type) for rangelist in rangelists for entry in rangelist)
464 for rangelist in rangelists:
465 self.dump_v5_rangelist(rangelist, cu_map, max_type_len)
466
467 def dump_v5_rangelist(self, rangelist, cu_map, max_type_len):
468 cu = cu_map[rangelist[0].entry_offset]
469 addr_str_len = cu.header.address_size*2
470 base_ip = _get_cu_base(cu)
471 for entry in rangelist:
472 type = entry.entry_type
473 self._emit("0x%08x: [%s]: " % (entry.entry_offset, type.ljust(max_type_len)))
474 if type == 'DW_RLE_base_address':
475 base_ip = entry.address
476 self._emitline("0x%0*x" % (addr_str_len, base_ip))
477 elif type == 'DW_RLE_offset_pair':
478 self._emitline("0x%0*x, 0x%0*x => [0x%0*x, 0x%0*x)" % (
479 addr_str_len, entry.start_offset,
480 addr_str_len, entry.end_offset,
481 addr_str_len, entry.start_offset + base_ip,
482 addr_str_len, entry.end_offset + base_ip))
483 elif type == 'DW_RLE_start_length':
484 self._emitline("0x%0*x, 0x%0*x => [0x%0*x, 0x%0*x)" % (
485 addr_str_len, entry.start_address,
486 addr_str_len, entry.length,
487 addr_str_len, entry.start_address,
488 addr_str_len, entry.start_address + entry.length))
489 elif type == 'DW_RLE_start_end':
490 self._emitline("0x%0*x, 0x%0*x => [0x%0*x, 0x%0*x)" % (
491 addr_str_len, entry.start_address,
492 addr_str_len, entry.end_address,
493 addr_str_len, entry.start_address,
494 addr_str_len, entry.end_address))
495 else:
496 raise NotImplementedError()
497 last = rangelist[-1]
498 self._emitline("0x%08x: [DW_RLE_end_of_list ]" % (last.entry_offset + last.entry_length,))
499
500 SCRIPT_DESCRIPTION = 'Display information about the contents of ELF format files'
501 VERSION_STRING = '%%(prog)s: based on pyelftools %s' % __version__
502
503 def main(stream=None):
504 # parse the command-line arguments and invoke ReadElf
505 argparser = argparse.ArgumentParser(
506 usage='usage: %(prog)s [options] <elf-file>',
507 description=SCRIPT_DESCRIPTION,
508 add_help=False,
509 prog='readelf.py')
510 argparser.add_argument('file',
511 nargs='?', default=None,
512 help='ELF file to parse')
513 argparser.add_argument('-H', '--help',
514 action='store_true', dest='help',
515 help='Display this information')
516 argparser.add_argument('--verbose',
517 action='store_true', dest='verbose',
518 help=('For compatibility with dwarfdump. Non-verbose mode is not implemented.'))
519
520 # Section dumpers
521 sections = ('info', 'loclists', 'rnglists') # 'loc', 'ranges' not implemented yet
522 for section in sections:
523 argparser.add_argument('--debug-%s' % section,
524 action='store_true', dest=section,
525 help=('Display the contents of DWARF debug_%s section.' % section))
526
527 args = argparser.parse_args()
528
529 if args.help or not args.file:
530 argparser.print_help()
531 sys.exit(0)
532
533 # A compatibility hack on top of a compatibility hack :(
534 del ENUM_DW_TAG["DW_TAG_template_type_param"]
535 del ENUM_DW_TAG["DW_TAG_template_value_param"]
536 ENUM_DW_TAG['DW_TAG_template_type_parameter'] = 0x2f
537 ENUM_DW_TAG['DW_TAG_template_value_parameter'] = 0x30
538
539 with open(args.file, 'rb') as file:
540 try:
541 readelf = ReadElf(args.file, file, stream or sys.stdout)
542 if args.info:
543 readelf.dump_info()
544 if args.loclists:
545 readelf.dump_loclists()
546 if args.rnglists:
547 readelf.dump_rnglists()
548 #if args.loc:
549 # readelf.dump_loc()
550 #if args.ranges:
551 # readelf.dump_ranges()
552 except ELFError as ex:
553 sys.stdout.flush()
554 sys.stderr.write('ELF error: %s\n' % ex)
555 if args.show_traceback:
556 traceback.print_exc()
557 sys.exit(1)
558
559 #-------------------------------------------------------------------------------
560 if __name__ == '__main__':
561 main()
562 #profile_main()