2 #-------------------------------------------------------------------------------
5 # A clone of 'llvm-dwarfdump' in Python, based on the pyelftools library
6 # Roughly corresponding to v15
8 # Sources under https://github.com/llvm/llvm-project/tree/main/llvm/tools/llvm-dwarfdump
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.
13 # Eli Bendersky (eliben@gmail.com)
14 # This code is in the public domain
15 #-------------------------------------------------------------------------------
17 import os
, sys
, posixpath
20 # For running from development directory. It should take precedence over the
21 # installed pyelftools.
22 sys
.path
.insert(0, '.')
24 from elftools
import __version__
25 from elftools
.common
.exceptions
import DWARFError
, ELFError
26 from elftools
.common
.py3compat
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
36 # ------------------------------
37 # ------------------------------
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
47 raise ValueError("Can't find the base IP (low_pc) for a CU")
49 def _addr_str_length(die
):
50 return die
.cu
.header
.address_size
*2
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
)
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
)
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
)
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
)
84 def _desc_ref(attr
, die
, extra
=''):
86 extra
= " \"%s\"" % extra
87 return "cu + 0x%04x => {0x%08x}%s" % (
89 die
.cu
.cu_offset
+ attr
.raw_value
,
92 def _desc_data(attr
, die
):
93 """ Hex with length driven by form
95 len = int(attr
.form
[12:]) * 2
96 return "0x%0*x" % (len, attr
.value
,)
98 def _desc_strx(attr
, die
):
99 return "indexed (%08x) string = \"%s\"" % (attr
.raw_value
, bytes2str(attr
.value
).replace("\\", "\\\\"))
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
)
127 def _desc_enum(attr
, enum
):
128 """For attributes like DW_AT_language, physically
129 int, logically an enum
131 return next((k
for (k
, v
) in enum
.items() if v
== attr
.value
), str(attr
.value
))
133 def _cu_comp_dir(cu
):
134 return bytes2str(cu
.get_top_DIE().attributes
['DW_AT_comp_dir'].value
)
136 def _desc_decl_file(attr
, die
):
137 # Filename/dirname arrays are 0 based in DWARFv5
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
148 dir = bytes2str(includes
[dir_index
])
149 if dir.startswith('.'):
150 dir = posixpath
.join(_cu_comp_dir(cu
), dir)
152 dir = _cu_comp_dir(cu
)
153 file_name
= bytes2str(file_entry
.name
)
155 raise DWARFError("Invalid source filename entry index in a decl_file attribute")
156 return "\"%s\"" % (posixpath
.join(dir, file_name
),)
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
)
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)" % (
171 (0 if entry
.is_absolute
else base_ip
) + entry
.begin_offset
,
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
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
)
181 def _desc_locations(attr
, die
):
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
)
192 base_ip
= _get_cu_base(cu
)
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" % (
199 (0 if entry
.is_absolute
else base_ip
) + entry
.begin_offset
,
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
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
)
210 # By default, numeric arguments are spelled in hex with a leading 0x
211 def _desc_operationarg(s
, cu
):
212 if isinstance(s
, str):
214 elif isinstance(s
, int):
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
) + ')'
220 return " ".join((hex(len(s
)),) + tuple("0x%02x" % b
for b
in s
))
223 return cu
.dwarfinfo
.config
.machine_arch
225 def _desc_reg(reg_no
, cu
):
226 return describe_reg_name(reg_no
, _arch(cu
), True).upper()
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' % (
235 _desc_reg(op
- 0x70, cu
),
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\"" % (
246 _desc_reg(args
[0], cu
),
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\"" % (
254 args
[0] + cu
.cu_offset
,
255 _DIE_name(cu
._get
_cached
_DIE
(args
[0] + cu
.cu_offset
)))
257 return op_name
+ ' ' + ', '.join(_desc_operationarg(s
, cu
) for s
in args
)
261 # TODO: remove this once dwarfdump catches up
263 'DW_OP_implicit_pointer',
265 'DW_OP_GNU_parameter_ref',
266 'DW_OP_GNU_deref_type',
267 'DW_OP_GNU_implicit_pointer',
269 'DW_OP_GNU_regval_type')
271 def _desc_expression(expr
, die
):
273 if not hasattr(cu
, '_exprparser'):
274 cu
._exprparser
= DWARFExprParser(cu
.structs
)
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
]
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
)
287 def _desc_datatype(attr
, die
):
290 return _desc_ref(attr
, die
, describe_cpp_datatype(die
))
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
, '')
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
)
302 def _desc_origin(attr
, die
):
303 return _desc_ref(attr
, die
, _get_origin_name(die
))
305 def _desc_spec(attr
, die
):
306 return _desc_ref(attr
, die
,
307 _DIE_linkage_name(die
.get_DIE_from_attribute('DW_AT_specification')))
309 def _desc_value(attr
, die
):
310 return str(attr
.value
)
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
331 class ReadElf(object):
332 """ dump_xxx is used to dump the respective section.
333 Mimics the output of dwarfdump with --verbose
335 def __init__(self
, filename
, file, output
):
337 stream object with the ELF file to read
340 output stream to write to
342 self
.elffile
= ELFFile(file)
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
))
350 def _emit(self
, s
=''):
351 """ Emit an object to output
353 self
.output
.write(str(s
))
355 def _emitline(self
, s
=''):
356 """ Emit an object to output, followed by a newline
358 self
.output
.write(str(s
).rstrip() + '\n')
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
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)" %(
372 cu
.header
.unit_length
,
373 cu
.structs
.dwarf_format
,
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
))
380 parent
= cu
.get_top_DIE()
381 for die
in cu
.iter_DIEs():
382 if die
.get_parent() == parent
:
384 if not die
.is_null():
385 self
._emitline
("0x%08x: %s [%d] %s %s" % (
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
)))
395 self
._emitline
("0x%08x: NULL" % (die
.offset
,))
396 parent
= die
.get_parent()
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
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
)
408 return str(attr
.value
)
413 def dump_loclists(self
):
416 def dump_ranges(self
):
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)" % (
427 (0 if entry
.is_absolute
else base_ip
) + entry
.begin_offset
,
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
433 raise NotImplementedError("Unknown object in a range list")
435 def dump_rnglists(self
):
436 self
._emitline
(".debug_rnglists contents:")
437 ranges_sec
= self
._dwarfinfo
.range_lists()
438 if ranges_sec
.version
< 5:
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
}
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" % (
450 64 if cu
.is64
else 32,
453 cu
.segment_selector_size
,
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
]
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
)
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
))
495 raise NotImplementedError()
497 self
._emitline
("0x%08x: [DW_RLE_end_of_list ]" % (last
.entry_offset
+ last
.entry_length
,))
499 SCRIPT_DESCRIPTION
= 'Display information about the contents of ELF format files'
500 VERSION_STRING
= '%%(prog)s: based on pyelftools %s' % __version__
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
,
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.'))
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
))
526 args
= argparser
.parse_args()
528 if args
.help or not args
.file:
529 argparser
.print_help()
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
538 with
open(args
.file, 'rb') as file:
540 readelf
= ReadElf(args
.file, file, stream
or sys
.stdout
)
544 readelf
.dump_loclists()
546 readelf
.dump_rnglists()
550 # readelf.dump_ranges()
551 except ELFError
as ex
:
553 sys
.stderr
.write('ELF error: %s\n' % ex
)
554 if args
.show_traceback
:
555 traceback
.print_exc()
558 #-------------------------------------------------------------------------------
559 if __name__
== '__main__':