e593f1c63f82387b9b5ca1aea9c4bf41542027a0
[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, posixpath
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 = posixpath.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\"" % (posixpath.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 )
330
331 class ReadElf(object):
332 """ dump_xxx is used to dump the respective section.
333 Mimics the output of dwarfdump with --verbose
334 """
335 def __init__(self, filename, file, output):
336 """ file:
337 stream object with the ELF file to read
338
339 output:
340 output stream to write to
341 """
342 self.elffile = ELFFile(file)
343 self.output = output
344 self._dwarfinfo = self.elffile.get_dwarf_info()
345 arches = {"EM_386": "i386", "EM_X86_64": "x86-64"}
346 arch = arches[self.elffile['e_machine']]
347 bits = self.elffile.elfclass
348 self._emitline("%s: file format elf%d-%s" % (filename, bits, arch))
349
350 def _emit(self, s=''):
351 """ Emit an object to output
352 """
353 self.output.write(str(s))
354
355 def _emitline(self, s=''):
356 """ Emit an object to output, followed by a newline
357 """
358 self.output.write(str(s).rstrip() + '\n')
359
360 def dump_info(self):
361 # TODO: DWARF64 will cause discrepancies in hex offset sizes
362 self._emitline(".debug_info contents:")
363 for cu in self._dwarfinfo.iter_CUs():
364 if cu.header.version >= 5:
365 ut = next(k for (k,v) in ENUM_DW_UT.items() if v == cu.header.unit_type)
366 unit_type_str = " unit_type = %s," % ut
367 else:
368 unit_type_str = ''
369
370 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)" %(
371 cu.cu_offset,
372 cu.header.unit_length,
373 cu.structs.dwarf_format,
374 cu.header.version,
375 unit_type_str,
376 cu.header.debug_abbrev_offset,
377 cu.header.address_size,
378 cu.cu_offset + (4 if cu.structs.dwarf_format == 32 else 12) + cu.header.unit_length))
379 self._emitline()
380 parent = cu.get_top_DIE()
381 for die in cu.iter_DIEs():
382 if die.get_parent() == parent:
383 parent = die
384 if not die.is_null():
385 self._emitline("0x%08x: %s [%d] %s %s" % (
386 die.offset,
387 die.tag,
388 die.abbrev_code,
389 '*' if die.has_children else '',
390 '(0x%08x)' % die.get_parent().offset if die.get_parent() is not None else ''))
391 for attr_name in die.attributes:
392 attr = die.attributes[attr_name]
393 self._emitline(" %s [%s] (%s)" % (attr_name, attr.form, self.describe_attr_value(die, attr)))
394 else:
395 self._emitline("0x%08x: NULL" % (die.offset,))
396 parent = die.get_parent()
397 self._emitline()
398
399 def describe_attr_value(self, die, attr):
400 """This describes the attribute value in the way that's compatible
401 with llvm_dwarfdump. Somewhat duplicates the work of describe_attr_value() in descriptions
402 """
403 if attr.name in ATTR_DESCRIPTIONS:
404 return ATTR_DESCRIPTIONS[attr.name](attr, die)
405 elif attr.form in FORM_DESCRIPTIONS:
406 return FORM_DESCRIPTIONS[attr.form](attr, die)
407 else:
408 return str(attr.value)
409
410 def dump_loc(self):
411 pass
412
413 def dump_loclists(self):
414 pass
415
416 def dump_ranges(self):
417 pass
418
419 def dump_v4_rangelist(self, rangelist, cu_map):
420 cu = cu_map[rangelist[0].entry_offset]
421 addr_str_len = cu.header.address_size*2
422 base_ip = _get_cu_base(cu)
423 for entry in rangelist:
424 if isinstance(entry, RangeEntry):
425 self._emitline("[0x%0*x, 0x%0*x)" % (
426 addr_str_len,
427 (0 if entry.is_absolute else base_ip) + entry.begin_offset,
428 addr_str_len,
429 (0 if entry.is_absolute else base_ip) + entry.end_offset))
430 elif isinstance(entry, elftools.dwarf.ranges.BaseAddressEntry):
431 base_ip = entry.base_address
432 else:
433 raise NotImplementedError("Unknown object in a range list")
434
435 def dump_rnglists(self):
436 self._emitline(".debug_rnglists contents:")
437 ranges_sec = self._dwarfinfo.range_lists()
438 if ranges_sec.version < 5:
439 return
440
441 cu_map = {die.attributes['DW_AT_ranges'].value : cu # Dict from range offset to home CU
442 for cu in self._dwarfinfo.iter_CUs()
443 for die in cu.iter_DIEs()
444 if 'DW_AT_ranges' in die.attributes}
445
446 for cu in ranges_sec.iter_CUs():
447 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" % (
448 cu.cu_offset,
449 cu.unit_length,
450 64 if cu.is64 else 32,
451 cu.version,
452 cu.address_size,
453 cu.segment_selector_size,
454 cu.offset_count))
455 self._emitline("ranges:")
456 if cu.offset_count > 0:
457 rangelists = [ranges_sec.get_range_list_at_offset_ex(offset) for offset in cu.offsets]
458 else:
459 rangelists = list(ranges_sec.iter_CU_range_lists_ex(cu))
460 # We have to parse it completely before dumping, because dwarfdump aligns columns,
461 # no way to do that without some lookahead
462 max_type_len = max(len(entry.entry_type) for rangelist in rangelists for entry in rangelist)
463 for rangelist in rangelists:
464 self.dump_v5_rangelist(rangelist, cu_map, max_type_len)
465
466 def dump_v5_rangelist(self, rangelist, cu_map, max_type_len):
467 cu = cu_map[rangelist[0].entry_offset]
468 addr_str_len = cu.header.address_size*2
469 base_ip = _get_cu_base(cu)
470 for entry in rangelist:
471 type = entry.entry_type
472 self._emit("0x%08x: [%s]: " % (entry.entry_offset, type.ljust(max_type_len)))
473 if type == 'DW_RLE_base_address':
474 base_ip = entry.address
475 self._emitline("0x%0*x" % (addr_str_len, base_ip))
476 elif type == 'DW_RLE_offset_pair':
477 self._emitline("0x%0*x, 0x%0*x => [0x%0*x, 0x%0*x)" % (
478 addr_str_len, entry.start_offset,
479 addr_str_len, entry.end_offset,
480 addr_str_len, entry.start_offset + base_ip,
481 addr_str_len, entry.end_offset + base_ip))
482 elif type == 'DW_RLE_start_length':
483 self._emitline("0x%0*x, 0x%0*x => [0x%0*x, 0x%0*x)" % (
484 addr_str_len, entry.start_address,
485 addr_str_len, entry.length,
486 addr_str_len, entry.start_address,
487 addr_str_len, entry.start_address + entry.length))
488 elif type == 'DW_RLE_start_end':
489 self._emitline("0x%0*x, 0x%0*x => [0x%0*x, 0x%0*x)" % (
490 addr_str_len, entry.start_address,
491 addr_str_len, entry.end_address,
492 addr_str_len, entry.start_address,
493 addr_str_len, entry.end_address))
494 else:
495 raise NotImplementedError()
496 last = rangelist[-1]
497 self._emitline("0x%08x: [DW_RLE_end_of_list ]" % (last.entry_offset + last.entry_length,))
498
499 SCRIPT_DESCRIPTION = 'Display information about the contents of ELF format files'
500 VERSION_STRING = '%%(prog)s: based on pyelftools %s' % __version__
501
502 def main(stream=None):
503 # parse the command-line arguments and invoke ReadElf
504 argparser = argparse.ArgumentParser(
505 usage='usage: %(prog)s [options] <elf-file>',
506 description=SCRIPT_DESCRIPTION,
507 add_help=False,
508 prog='readelf.py')
509 argparser.add_argument('file',
510 nargs='?', default=None,
511 help='ELF file to parse')
512 argparser.add_argument('-H', '--help',
513 action='store_true', dest='help',
514 help='Display this information')
515 argparser.add_argument('--verbose',
516 action='store_true', dest='verbose',
517 help=('For compatibility with dwarfdump. Non-verbose mode is not implemented.'))
518
519 # Section dumpers
520 sections = ('info', 'loclists', 'rnglists') # 'loc', 'ranges' not implemented yet
521 for section in sections:
522 argparser.add_argument('--debug-%s' % section,
523 action='store_true', dest=section,
524 help=('Display the contents of DWARF debug_%s section.' % section))
525
526 args = argparser.parse_args()
527
528 if args.help or not args.file:
529 argparser.print_help()
530 sys.exit(0)
531
532 # A compatibility hack on top of a compatibility hack :(
533 del ENUM_DW_TAG["DW_TAG_template_type_param"]
534 del ENUM_DW_TAG["DW_TAG_template_value_param"]
535 ENUM_DW_TAG['DW_TAG_template_type_parameter'] = 0x2f
536 ENUM_DW_TAG['DW_TAG_template_value_parameter'] = 0x30
537
538 with open(args.file, 'rb') as file:
539 try:
540 readelf = ReadElf(args.file, file, stream or sys.stdout)
541 if args.info:
542 readelf.dump_info()
543 if args.loclists:
544 readelf.dump_loclists()
545 if args.rnglists:
546 readelf.dump_rnglists()
547 #if args.loc:
548 # readelf.dump_loc()
549 #if args.ranges:
550 # readelf.dump_ranges()
551 except ELFError as ex:
552 sys.stdout.flush()
553 sys.stderr.write('ELF error: %s\n' % ex)
554 if args.show_traceback:
555 traceback.print_exc()
556 sys.exit(1)
557
558 #-------------------------------------------------------------------------------
559 if __name__ == '__main__':
560 main()
561 #profile_main()