Skip to content

Commit

Permalink
Fix ranges autotest take 2 (#505)
Browse files Browse the repository at this point in the history
* Updated readelf

* Misc API, enums fixes

* Readelf in line with latest binutils

* Autotest exception removed

* Readelf from binutils commit 84102ebc29a1ea531e7fe78bd841bfb2fe501dc2

---------

Co-authored-by: Seva <[email protected]>
  • Loading branch information
sevaa and Seva authored Oct 20, 2023
1 parent b0d7a76 commit 8c2f14e
Show file tree
Hide file tree
Showing 6 changed files with 66 additions and 42 deletions.
2 changes: 2 additions & 0 deletions elftools/dwarf/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,8 @@
DW_CC_normal = 0x1
DW_CC_program = 0x2
DW_CC_nocall = 0x3
DW_CC_pass_by_reference = 0x4
DW_CC_pass_by_valuee = 0x5


# Ordering
Expand Down
2 changes: 2 additions & 0 deletions elftools/dwarf/descriptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,8 @@ def _describe_attr_block(attr, die, section_offset):
DW_CC_normal: '(normal)',
DW_CC_program: '(program)',
DW_CC_nocall: '(nocall)',
DW_CC_pass_by_reference: '(pass by ref)',
DW_CC_pass_by_valuee: '(pass by value)',
}

_DESCR_DW_ORD = {
Expand Down
5 changes: 5 additions & 0 deletions elftools/dwarf/ranges.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,11 @@ def iter_CU_range_lists_ex(self, cu):
section dumper should check if one is present.
"""
return self._rnglists.iter_CU_range_lists_ex(cu)

def translate_v5_entry(self, entry, cu):
"""Forwards a V5 entry translation request to the V5 section
"""
return self._rnglists.translate_v5_entry(entry, cu)

class RangeLists(object):
""" A single range list is a Python list consisting of RangeEntry or
Expand Down
94 changes: 57 additions & 37 deletions scripts/readelf.py
Original file line number Diff line number Diff line change
Expand Up @@ -1631,6 +1631,18 @@ def _dump_debug_ranges(self):
else:
self._dump_debug_rangesection(di, range_lists_sec)

def _dump_debug_rnglists_CU_header(self, cu):
self._emitline(' Table at Offset: %s:' % self._format_hex(cu.cu_offset, alternate=True))
self._emitline(' Length: %s' % self._format_hex(cu.unit_length, alternate=True))
self._emitline(' DWARF version: %d' % cu.version)
self._emitline(' Address size: %d' % cu.address_size)
self._emitline(' Segment size: %d' % cu.segment_selector_size)
self._emitline(' Offset entries: %d\n' % cu.offset_count)
if cu.offsets and len(cu.offsets):
self._emitline(' Offsets starting at 0x%x:' % cu.offset_table_offset)
for i_offset in enumerate(cu.offsets):
self._emitline(' [%6d] 0x%x' % i_offset)

def _dump_debug_rangesection(self, di, range_lists_sec):
# Last amended to match readelf 2.41
ver5 = range_lists_sec.version >= 5
Expand All @@ -1639,53 +1651,48 @@ def _dump_debug_rangesection(self, di, range_lists_sec):
addr_width = addr_size * 2 # In hex digits, 8 or 16
line_template = " %%08x %%0%dx %%0%dx %%s" % (addr_width, addr_width)
base_template = " %%08x %%0%dx (base address)" % (addr_width)
base_template_indexed = " %%08x %%0%dx (base address index) %%0%dx (base address)" % (addr_width, addr_width)

# In order to determine the base address of the range
# We need to know the corresponding CU.
cu_map = {die.attributes['DW_AT_ranges'].value : cu # Range list offset => CU
for cu in di.iter_CUs()
for die in cu.iter_DIEs()
if 'DW_AT_ranges' in die.attributes}

if ver5: # Dump by CUs - unsure at this point what does readelf do, ranges dump is buggy in 2.41
self._emitline('Contents of the %s section:\n\n\n' % section_name)
for cu in range_lists_sec.iter_CUs():
self._emitline(' Table at Offset: %s:' % self._format_hex(cu.cu_offset, alternate=True))
self._emitline(' Length: %s' % self._format_hex(cu.unit_length, alternate=True))
self._emitline(' DWARF version: %d' % cu.version)
self._emitline(' Address size: %d' % cu.address_size)
self._emitline(' Segment size: %d' % cu.segment_selector_size)
self._emitline(' Offset entries: %d\n' % cu.offset_count)
# Is the offset table dumped too?
for (i, range_list) in enumerate(range_lists_sec.iter_CU_range_lists_ex(cu)):
list_offset = range_list[0].entry_offset
range_list = list(range_lists_sec.translate_v5_entry(entry, cu_map[list_offset]) for entry in range_list)
self._emitline(' Offset: %s, Index: %d' % (self._format_hex(list_offset, alternate=True), i))
self._emitline(' Offset Begin End')
self._dump_rangelist(range_list, cu_map, ver5, line_template, base_template)
else: # Dump by DIE reference offset
range_lists = list(range_lists_sec.iter_range_lists())
if len(range_lists) == 0:
# Present but empty ranges section - readelf outputs a message
self._emitline("\nSection '%s' has no debugging data." % section_name)
return

self._emitline('Contents of the %s section:\n\n\n' % section_name)
if 'DW_AT_ranges' in die.attributes}

rcus = list(range_lists_sec.iter_CUs()) if ver5 else None
rcu_index = 0
next_rcu_offset = 0

range_lists = list(range_lists_sec.iter_range_lists())
if len(range_lists) == 0:
# Present but empty ranges section - readelf outputs a message
self._emitline("\nSection '%s' has no debugging data." % section_name)
return

self._emitline('Contents of the %s section:\n\n\n' % section_name)
if not ver5:
self._emitline(' Offset Begin End')

for range_list in range_lists:
if len(range_list) == 0: # working around a bogus behavior in readelf 2.41
# No entries means no offset. Dirty hack: peek the stream position
range_list_offset = range_lists_sec.stream.tell() - self._dwarfinfo.config.default_address_size*2
self._emitline(' %08x <End of list>' % (range_list_offset))
else:
self._dump_rangelist(range_list, cu_map, ver5, line_template, base_template)
for range_list in range_lists:
# Emit CU headers before the curernt rangelist
if ver5 and range_list[0].entry_offset > next_rcu_offset:
while range_list[0].entry_offset > next_rcu_offset:
rcu = rcus[rcu_index]
self._dump_debug_rnglists_CU_header(rcu)
next_rcu_offset = rcu.offset_after_length + rcu.unit_length
rcu_index += 1
self._emitline(' Offset Begin End')
self._dump_rangelist(range_list, cu_map, ver5, line_template, base_template, base_template_indexed, range_lists_sec)

# TODO: trailing empty CUs, if any?

def _dump_rangelist(self, range_list, cu_map, ver5, line_template, base_template):
def _dump_rangelist(self, range_list, cu_map, ver5, line_template, base_template, base_template_indexed, range_lists_sec):
# Weird discrepancy in binutils: for DWARFv5 it outputs entry offset,
# for DWARF<=4 list offset.
first = range_list[0]
base_ip = _get_cu_base(cu_map[first.entry_offset])
raw_v5_rangelist = None
for entry in range_list:
if isinstance(entry, RangeEntry):
postfix = ' (start == end)' if entry.begin_offset == entry.end_offset else ''
Expand All @@ -1696,9 +1703,22 @@ def _dump_rangelist(self, range_list, cu_map, ver5, line_template, base_template
postfix))
elif isinstance(entry,RangeBaseAddressEntry):
base_ip = entry.base_address
self._emitline(base_template % (
entry.entry_offset if ver5 else first.entry_offset,
entry.base_address))
# V5 base entries with index are reported differently in readelf - need to go back to the raw V5 format
# Maybe other subtypes too, but no such cases in the test corpus
raw_v5_entry = None
if ver5:
if not raw_v5_rangelist:
raw_v5_rangelist = range_lists_sec.get_range_list_at_offset_ex(range_list[0].entry_offset)
raw_v5_entry = next(re for re in raw_v5_rangelist if re.entry_offset == entry.entry_offset)
if raw_v5_entry and raw_v5_entry.entry_type == 'DW_RLE_base_addressx':
self._emitline(base_template_indexed % (
entry.entry_offset,
raw_v5_entry.index,
entry.base_address))
else:
self._emitline(base_template % (
entry.entry_offset if ver5 else first.entry_offset,
entry.base_address))
else:
raise NotImplementedError("Unknown object in a range list")
last = range_list[-1]
Expand Down
Binary file modified test/external_tools/readelf
Binary file not shown.
5 changes: 0 additions & 5 deletions test/run_readelf_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,11 +72,6 @@ def run_test_on_file(filename, verbose=False, opt=None):
else:
options = [opt]

# TODO(sevaa): excluding two files from the --debug-dump=Ranges test until the maintainers
# of GNU binutils fix https://sourceware.org/bugzilla/show_bug.cgi?id=30781
if filename.endswith('dwarf_test_versions_mix.elf') or filename.endswith('dwarf_v5ops.so.elf'):
options.remove('--debug-dump=Ranges')

for option in options:
if verbose: testlog.info("..option='%s'" % option)

Expand Down

0 comments on commit 8c2f14e

Please sign in to comment.