Bug 18218

Summary: bad handling of .debug_str_offsets section
Product: binutils Reporter: dje
Component: binutilsAssignee: Not yet assigned to anyone <unassigned>
Status: RESOLVED FIXED    
Severity: normal CC: hjl.tools
Priority: P2    
Version: 2.26   
Target Milestone: 2.26   
Host: Target:
Build: Last reconfirmed:
Attachments: testcase
testcase #2, .c file
testcase #2, .s file
testcase #2, .dwo file
A patch

Description dje 2015-04-08 20:10:50 UTC
Problem:

readelf -w readelf-str-offsets.dwo
->
    <c>   DW_AT_producer    : (indexed string: 0x5): <no .debug_str_offsets.dwo section>

However, this works:

readelf -wi readelf-str-offsets.dwo
->
    <c>   DW_AT_producer    : (indexed string: 0x5): GNU C ...

The appended testcase, readelf-str-offsets-bug.s, was created by compiling
this file:

---snip---
/* We need enough entries in .debug_str_offsets so that                                                                                                          
   gas/write.c:compress_debug will compress it.                                                                                                                  
   Threshold is 32 bytes.  */

int var1;
int var2;
int var3;
int var4;

int
main ()
{
  return 0;
}
---snip---

thusly:

gcc -m64 -gdwarf-4 -gsplit-dwarf -c readelf-str-offsets-bug.c -save-temps -Wa,--compress-debug-sections

and then manually editing the .s file so that .debug_str_offsets appears before .debug_info, and then recompiling thusly:

gcc -m64 -gdwarf-4 -gsplit-dwarf -c readelf-str-offsets-bug.s -Wa,--compress-debug-sections

[The system where I tripped over this bug just happened to put .debug_str_offsets ahead of .debug_info.]

The bug occurs because .debug_str_offsets is loaded and decompressed twice:
1) to display .zdebug_str_offsets itself
2) to display .zdebug_info

The first time through we hit this code in readelf.c:load_specific_debug_section

  if (section->start == NULL)
    section->size = 0;
  else
    {
      section->size = sec->sh_size;
      if (uncompress_section_contents (&section->start, &section->size))
        sec->sh_size = section->size;   <<<<<<<<<<<<<
    }

Note that we've set sec->sh_size to the new, uncompressed value.

Then we free the section here in display_debug_section:

            if (secondary || (i != info && i != abbrev))
              free_debug_section ((enum dwarf_section_display_enum) i);

Then we'll go to display .debug_info.
But because we no longer have .debug_str_offsets we reload it.
However, it's sh_size field is now bad - it no longer contains the compressed size.  So we reload the section with the newer larger size thinking all of that is compressed data, the read fails, and the section is left as marked as unloaded.

We then get here in fetch_indexed_string:

  if (index_section->start == NULL)
    return (dwo ? _("<no .debug_str_offsets.dwo section>")
                : _("<no .debug_str_offsets section>"));

---

N.B. The above written prior to commit 77115a4a.

2015-04-05  H.J. Lu  <hongjiu.lu@intel.com>

        * readelf.c (get_elf_section_flags): Support SHF_COMPRESSED.
        (get_compression_header): New.
        (process_section_headers): Dump compression header if needed.
        (uncompress_section_contents): Don't free compressed_buffer here.
        (load_specific_debug_section): Free the compressed buffer, update
        the section buffer and the section size if uncompress is
        successful.

There is still a bug though.
Now I'm getting:

readelf: Warning: DW_FORM_GNU_str_index indirect offset too big: 76207962
and
    <c>   DW_AT_producer    : (indexed string: 0x5): <indirect index offset is too big>

It seems that when fetch_indexed_string is called, the contents are compressed.

(gdb) p *index_section
$46 = {
  uncompressed_name = 0x47ffab ".debug_str_offsets.dwo",
  compressed_name = 0x47ffc2 ".zdebug_str_offsets.dwo",
  name = 0x47ffc2 ".zdebug_str_offsets.dwo",
  start = 0x68f430 "ZLIB", <<<<<<<<<<<<<<<<<
  address = 0,
  size = 32,
  abbrev_sec = abbrev,
  user_data = 0x0
}
Comment 1 dje 2015-04-08 20:16:49 UTC
Created attachment 8230 [details]
testcase
Comment 2 H.J. Lu 2015-04-08 20:45:30 UTC
Please provide readelf-str-offsets.dwo.
Comment 3 dje 2015-04-08 21:45:19 UTC
Bleah.  I needed to make .debug_str_offsets.dwo larger so that it actually would get compressed.

gcc -m64 -gdwarf-4 -gsplit-dwarf -c readelf-str-offsets-bug.c -Wa,--compress-debug-sections --save-temps

<
edit readelf-str-offsets-bug.s and move .debug_str_offsets.dwo ahead of .debug_info.
This might no longer be necessary, but it's useful to exercise the original bug.
>

gcc -m64 -gdwarf-4 -gsplit-dwarf -c readelf-str-offsets-bug.s -Wa,--compress-debug-sections
readelf -w readelf-str-offsets-bug.dwo
Comment 4 dje 2015-04-08 21:46:16 UTC
Created attachment 8231 [details]
testcase #2, .c file
Comment 5 dje 2015-04-08 21:46:51 UTC
Created attachment 8232 [details]
testcase #2, .s file
Comment 6 dje 2015-04-08 21:47:34 UTC
Created attachment 8233 [details]
testcase #2, .dwo file
Comment 7 dje 2015-04-08 21:47:56 UTC
testcase uploaded
Comment 8 H.J. Lu 2015-04-08 23:55:00 UTC
Created attachment 8234 [details]
A patch

This works on the testcase.  But it may not be correct.
Comment 9 H.J. Lu 2015-04-09 14:03:16 UTC
A patch is posted at

https://sourceware.org/ml/binutils/2015-04/msg00155.html
Comment 10 Sourceware Commits 2015-04-13 19:52:44 UTC
The master branch has been updated by Doug Evans <devans@sourceware.org>:

https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=0d2a7a932244fab27d6c4ce211ea8f7708a1a9cc

commit 0d2a7a932244fab27d6c4ce211ea8f7708a1a9cc
Author: Doug Evans <dje@google.com>
Date:   Mon Apr 13 12:50:17 2015 -0700

    Fix reading of .debug_str_offsets{,.dwo} twice.
    
    	PR binutils/18218
    	* readelf.c (printable_section_name): Constify sec argument.
    	(apply_relocations): Ditto.  New arg "size".  All callers updated.
    	(load_specific_debug_section): Constify sec argument.
    	Remove side-effect of modifying sec->sh_size.
Comment 11 dje 2015-04-13 19:53:25 UTC
Fix committed.