[binutils/readelf] Fix printing of dwarf5 .debug_info.dwo

Message ID 20240520073218.22297-1-tdevries@suse.de
State New
Headers
Series [binutils/readelf] Fix printing of dwarf5 .debug_info.dwo |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_binutils_build--master-arm success Testing passed
linaro-tcwg-bot/tcwg_binutils_build--master-aarch64 success Testing passed
linaro-tcwg-bot/tcwg_binutils_check--master-aarch64 success Testing passed
linaro-tcwg-bot/tcwg_binutils_check--master-arm success Testing passed

Commit Message

Tom de Vries May 20, 2024, 7:32 a.m. UTC
  When compiling a hello world like this:
...
$ gcc ~/data/hello.c -gsplit-dwarf -gdwarf-5
...
we get with readelf -w a-hello.dwo for the split full compilation unit entry
incorrect strings, for instance an incorrect producer string:
...
    <15>   DW_AT_producer    : (indexed string: 0xb): /home/vries/data/hello.c
...

The problem is that a str_offsets_base of 0 is used, so the header of the
.debug_str_offsets section of size 8 is treated as two offsets, and the index
0xb is mapped to entry 9 instead of 11:
...
           9 0000006c  /home/vries/data/hello.c
          ...
          11 00000097  GNU C17 15.0.0 -gsplit-dwarf -gdwarf-5
...

The DW_AT_str_offsets_base attribute is inherited from the corresponding
skeleton compilation unit in a.out, but:
- that's outside the scope of the .dwo file, and
- it's also not there (though it is there when using clang instead).

Fix this by assuming a sane default for str_offsets_base for dwarf5: the size
of the header of the .debug_str_offsets section, which is 8 for 32-bit dwarf
and 16 for 64-bit dwarf.

Implement this by moving the reading of DW_AT_str_offsets_base to read_bases,
which also fixes the printing of DW_FORM_strx strings in the skeleton
compilation unit entries for attributes before the DW_AT_str_offsets_base
attribute.

Conforming with the other code in read_bases, we only handle
DW_FORM_sec_offset, which means the code dealing with negative
str_offsets_base can be dropped.

Tested on x86_64-linux.
---
 binutils/dwarf.c                              | 38 ++++++++++++-------
 .../binutils-all/readelf-str-offsets-base.d   | 17 +++++++++
 .../binutils-all/readelf-str-offsets-base.s   | 30 +++++++++++++++
 binutils/testsuite/binutils-all/readelf.exp   |  1 +
 4 files changed, 72 insertions(+), 14 deletions(-)
 create mode 100644 binutils/testsuite/binutils-all/readelf-str-offsets-base.d
 create mode 100644 binutils/testsuite/binutils-all/readelf-str-offsets-base.s


base-commit: 84f7f8940760d0eaeb51a38b345db369b4207e5b
  

Comments

Nick Clifton May 30, 2024, 11:03 a.m. UTC | #1
Hi Tom,

> Fix this by assuming a sane default for str_offsets_base for dwarf5: the size
> of the header of the .debug_str_offsets section, which is 8 for 32-bit dwarf
> and 16 for 64-bit dwarf.
> 
> Implement this by moving the reading of DW_AT_str_offsets_base to read_bases,
> which also fixes the printing of DW_FORM_strx strings in the skeleton
> compilation unit entries for attributes before the DW_AT_str_offsets_base
> attribute.
> 
> Conforming with the other code in read_bases, we only handle
> DW_FORM_sec_offset, which means the code dealing with negative
> str_offsets_base can be dropped.
> 
> Tested on x86_64-linux.

The patch itself looks fine, but the new test fails for some targets, such as:

   alpha-linux-gnu
   mipsisa64r6-elf
   nios2-elf

The failure message looks like this:

   output: readelf: Warning: DIE at offset 0x14 refers to abbreviation number 15349 which does not exist

Also the sh-elf target fails but for a different reason:

   readelf-str-offsets-base.s: Assembler messages:
   readelf-str-offsets-base.s:17: Error: misaligned data

Would you mind taking a look at these please ?

It is OK to skip the test for a particular architecture if there
is a good reason for it.  But if you do decide to do that, please
add the reason into the test driver file as a comment.

Cheers
   Nick
  
Tom de Vries May 30, 2024, 12:22 p.m. UTC | #2
On 5/30/24 13:03, Nick Clifton wrote:
> Hi Tom,
> 
>> Fix this by assuming a sane default for str_offsets_base for dwarf5: 
>> the size
>> of the header of the .debug_str_offsets section, which is 8 for 32-bit 
>> dwarf
>> and 16 for 64-bit dwarf.
>>
>> Implement this by moving the reading of DW_AT_str_offsets_base to 
>> read_bases,
>> which also fixes the printing of DW_FORM_strx strings in the skeleton
>> compilation unit entries for attributes before the DW_AT_str_offsets_base
>> attribute.
>>
>> Conforming with the other code in read_bases, we only handle
>> DW_FORM_sec_offset, which means the code dealing with negative
>> str_offsets_base can be dropped.
>>
>> Tested on x86_64-linux.
> 
> The patch itself looks fine, but the new test fails for some targets, 
> such as:
> 
>    alpha-linux-gnu
>    mipsisa64r6-elf
>    nios2-elf
> 
> The failure message looks like this:
> 
>    output: readelf: Warning: DIE at offset 0x14 refers to abbreviation 
> number 15349 which does not exist
> 
> Also the sh-elf target fails but for a different reason:
> 
>    readelf-str-offsets-base.s: Assembler messages:
>    readelf-str-offsets-base.s:17: Error: misaligned data
> 
> Would you mind taking a look at these please ?
> 
> It is OK to skip the test for a particular architecture if there
> is a good reason for it.  But if you do decide to do that, please
> add the reason into the test driver file as a comment.
> 

Hi Nick,

thanks for the review.

These failures where caused by using directives that were not a fixed 
amount of bytes.  Fixed in a v2 submitted here ( 
https://sourceware.org/pipermail/binutils/2024-May/134421.html ).

Thanks,
- Tom

> Cheers
>    Nick
> 
>
  

Patch

diff --git a/binutils/dwarf.c b/binutils/dwarf.c
index d375148dc26..3fde898602e 100644
--- a/binutils/dwarf.c
+++ b/binutils/dwarf.c
@@ -2874,20 +2874,7 @@  read_and_display_attr_value (unsigned long attribute,
  	  break;
 
 	case DW_AT_str_offsets_base:
-	  if (debug_info_p->str_offsets_base)
-	    warn (_("CU @ %#" PRIx64 " has multiple str_offsets_base values "
-		    "%#" PRIx64 " and %#" PRIx64 ")\n"),
-		  debug_info_p->cu_offset,
-		  debug_info_p->str_offsets_base, uvalue);
-	  svalue = uvalue;
-	  if (svalue < 0)
-	    {
-	      warn (_("CU @ %#" PRIx64 " has has a negative stroffsets_base "
-		      "value of %#" PRIx64 " - treating as zero\n"),
-		    debug_info_p->cu_offset, svalue);
-	      uvalue = 0;
-	    }
-	  debug_info_p->str_offsets_base = uvalue;
+	  /* Assignment to debug_info_p->str_offsets_base is now elsewhere.  */
 	  break;
 
 	case DW_AT_frame_base:
@@ -3685,6 +3672,8 @@  read_bases (abbrev_entry *   entry,
 {
   abbrev_attr *attr;
 
+  uint64_t initial_str_offsets_base = debug_info_p->str_offsets_base;
+
   for (attr = entry->first_attr;
        attr && attr->attribute;
        attr = attr->next)
@@ -3711,10 +3700,31 @@  read_bases (abbrev_entry *   entry,
 	  else
 	    warn (_("Unexpected form of DW_AT_addr_base in the top DIE\n"));
 	}
+      if (attr->attribute == DW_AT_str_offsets_base)
+	{
+	  if (attr->form == DW_FORM_sec_offset)
+	    {
+	      SAFE_BYTE_GET_AND_INC (uvalue, data, offset_size, end);
+	      if (initial_str_offsets_base == 0 && debug_info_p->str_offsets_base != 0)
+		warn (_("CU @ %#" PRIx64 " has multiple str_offsets_base values "
+			"%#" PRIx64 " and %#" PRIx64 ")\n"),
+		      debug_info_p->cu_offset,
+		      debug_info_p->str_offsets_base, uvalue);
+	      debug_info_p->str_offsets_base = uvalue;
+	    }
+	  else
+	    warn (_("Unexpected form of DW_AT_str_offsets_base in the top DIE\n"));
+	}
       else
 	data = skip_attribute (attr->form, data, end, pointer_size,
 			       offset_size, dwarf_version);
     }
+
+  /* Provide a sane default for str_offsets_base: the size of the header of
+     the .debug_str_offsets section.  */
+  if (dwarf_version >= 5 && initial_str_offsets_base == 0
+      && debug_info_p->str_offsets_base == 0)
+    debug_info_p->str_offsets_base = offset_size == 4 ? 8 : 16;
 }
 
 /* Process the contents of a .debug_info section.
diff --git a/binutils/testsuite/binutils-all/readelf-str-offsets-base.d b/binutils/testsuite/binutils-all/readelf-str-offsets-base.d
new file mode 100644
index 00000000000..063c7ef8b44
--- /dev/null
+++ b/binutils/testsuite/binutils-all/readelf-str-offsets-base.d
@@ -0,0 +1,17 @@ 
+#name: readelf -wi readelf-str-offsets-base
+#source:  readelf-str-offsets-base.s
+#readelf: -wi
+
+Contents of the .debug_info.dwo section:
+
+  Compilation Unit @ offset 0:
+   Length:        0x13 \(32-bit\)
+   Version:       5
+   Unit Type:     DW_UT_split_compile \(5\)
+   Abbrev Offset: 0
+   Pointer Size:  8
+   DWO ID:        0xba7e77f573c097b6
+ <0><14>: Abbrev Number: 1 \(DW_TAG_compile_unit\)
+    <15>   DW_AT_producer    : \(indexed string: 0\): FIRST
+ <1><16>: Abbrev Number: 0
+
diff --git a/binutils/testsuite/binutils-all/readelf-str-offsets-base.s b/binutils/testsuite/binutils-all/readelf-str-offsets-base.s
new file mode 100644
index 00000000000..6e21e6dc0b1
--- /dev/null
+++ b/binutils/testsuite/binutils-all/readelf-str-offsets-base.s
@@ -0,0 +1,30 @@ 
+	.section	.debug_str.dwo,"MS",%progbits,1
+	.asciz	"FIRST"
+	.asciz	"SECOND"
+	.section	.debug_str_offsets.dwo,"MS",%progbits,1
+        .long   16
+        .short  5
+        .short  0
+	.4byte 0
+	.4byte 6
+	.section        .debug_info.dwo,"e",%progbits
+        .long   .Ldebug_info_dwo_end0-.Ldebug_info_dwo_start0 /* Length of Unit */
+.Ldebug_info_dwo_start0:
+	.2byte  0x5     /* DWARF macro version number.  */
+        .byte   5                               /* DWARF Unit Type */
+        .byte   8                               /* Address Size (in bytes) */
+        .long   0                               /* Offset Into Abbrev. Section */
+        .quad   -5008433839496718410		/* DWO ID */
+        .byte   1                               /* Abbrev [1] 0x14:0x1a DW_TAG_compile_unit */
+        .byte   0                               /* DW_AT_producer */
+        .byte   0                               /* End Of Children Mark */
+.Ldebug_info_dwo_end0:
+	.section        .debug_abbrev.dwo,"e",%progbits
+        .byte   1                               /* Abbreviation Code */
+        .byte   17                              /* DW_TAG_compile_unit */
+        .byte   1                               /* DW_CHILDREN_yes */
+        .byte   37                              /* DW_AT_producer */
+        .byte   37                              /* DW_FORM_strx1 */
+        .byte   0                               /* EOM(1) */
+        .byte   0                               /* EOM(2) */
+        .byte   0                               /* EOM(3) */
diff --git a/binutils/testsuite/binutils-all/readelf.exp b/binutils/testsuite/binutils-all/readelf.exp
index 9f20cb42812..b381562dbc6 100644
--- a/binutils/testsuite/binutils-all/readelf.exp
+++ b/binutils/testsuite/binutils-all/readelf.exp
@@ -403,6 +403,7 @@  if {[which $AS] != 0} then {
     run_dump_test "readelf-maskos-1a"
     run_dump_test "readelf-maskos-1b"
     run_dump_test "readelf-debug-str-offsets-dw4"
+    run_dump_test "readelf-str-offsets-base"
     if {![istarget *-*-hpux*]} then {
 	run_dump_test pr26548
 	if {![binutils_assemble_flags $srcdir/$subdir/pr26548.s tmpdir/pr26548e.o {--defsym ERROR=1}]} then {