Fix ranges autotest take 2 (#505)
authorSeva Alekseyev <sevaa@yarxi.ru>
Fri, 20 Oct 2023 13:16:35 +0000 (09:16 -0400)
committerGitHub <noreply@github.com>
Fri, 20 Oct 2023 13:16:35 +0000 (06:16 -0700)
* Updated readelf

* Misc API, enums fixes

* Readelf in line with latest binutils

* Autotest exception removed

* Readelf from binutils commit 84102ebc29a1ea531e7fe78bd841bfb2fe501dc2

---------

Co-authored-by: Seva <seva@sandbox.home>
elftools/dwarf/constants.py
elftools/dwarf/descriptions.py
elftools/dwarf/ranges.py
scripts/readelf.py
test/external_tools/readelf
test/run_readelf_tests.py

index 6542095801005ae66a7bf22978b2e6b0f1f1dfb3..b14977a297dbd9bca749adf3898ee84f4d4d3648 100644 (file)
@@ -131,6 +131,8 @@ DW_ID_case_insensitive = 3
 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
index 0b409c7296a9ccbfe42cb096681b895e409a9564..5c261b3f6e2eab9e03c5bd0e24691ad993fd99db 100644 (file)
@@ -403,6 +403,8 @@ _DESCR_DW_CC = {
     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 = {
index 43ab293d1f738e11baf887ae1be6ad14e8a3a682..4491918f47a6649941991e7047d7c40a4e27921a 100644 (file)
@@ -77,6 +77,11 @@ class RangeListsPair(object):
         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
index 6bd5cf3c9f3c1485a3259df874c998bee06c1284..36b6e6f73074a1cad5e9a619ffb9ae189a4bcb2b 100755 (executable)
@@ -1631,6 +1631,18 @@ class ReadElf(object):
         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
@@ -1639,53 +1651,48 @@ class ReadElf(object):
         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 ''
@@ -1696,9 +1703,22 @@ class ReadElf(object):
                     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]
index e23d301565e2d5cac71e3d66d95aa7a1c4b6a93c..6f364e97ea73bf612de3e78a0bdf50bf176d0374 100755 (executable)
Binary files a/test/external_tools/readelf and b/test/external_tools/readelf differ
index f94c42ebec146e2cb637c5d1abd22a509c12077c..c1fc48c2c8cd5d40417dfa4e5000a44d3d4b1293 100755 (executable)
@@ -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)