Bug 26987 - GCC 11 Ada binaries contain unknown tags
Summary: GCC 11 Ada binaries contain unknown tags
Status: RESOLVED FIXED
Alias: None
Product: dwz
Classification: Unclassified
Component: default (show other bugs)
Version: unspecified
: P2 normal
Target Milestone: ---
Assignee: Nobody
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2020-12-01 11:22 UTC by Martin Liska
Modified: 2021-02-18 21:19 UTC (History)
6 users (show)

See Also:
Host:
Target:
Build:
Last reconfirmed: 2020-12-02 00:00:00


Attachments
Binary (3.76 MB, application/zstd)
2020-12-02 10:27 UTC, Martin Liska
Details
Problematic binary 2 (2.71 MB, application/x-executable)
2020-12-02 12:31 UTC, Martin Liska
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Martin Liska 2020-12-01 11:22:06 UTC
I see it here:
https://build.opensuse.org/package/show/devel:gcc/gcc11

[ 1637s] dwz: Multi-file optimization not allowed for differentpointer sizes or endianity
[ 1637s] dwz: ./usr/bin/gnatfind-11-11.0.0+git181757-0.x86_64.debug: Unknown DWARF DW_OP_255
[ 1637s] dwz: ./usr/bin/gnatname-11-11.0.0+git181757-0.x86_64.debug: Unknown DWARF DW_OP_255
[ 1638s] dwz: ./usr/bin/gnatclean-11-11.0.0+git181757-0.x86_64.debug: Unknown DWARF DW_OP_255
[ 1638s] dwz: ./usr/bin/gnatls-11-11.0.0+git181757-0.x86_64.debug: Unknown DWARF DW_OP_255
[ 1638s] dwz: ./usr/bin/gnatprep-11-11.0.0+git181757-0.x86_64.debug: Unknown DWARF DW_OP_255
[ 1638s] dwz: ./usr/bin/gnatmake-11-11.0.0+git181757-0.x86_64.debug: Unknown DWARF DW_OP_255
[ 1639s] dwz: ./usr/bin/gnatchop-11-11.0.0+git181757-0.x86_64.debug: Unknown DWARF DW_OP_255
[ 1639s] dwz: ./usr/bin/gnatxref-11-11.0.0+git181757-0.x86_64.debug: Unknown DWARF DW_OP_255
[ 1797s] dwz: ./usr/lib64/libgnat-11.so-11.0.0+git181757-0.x86_64.debug: Unknown DWARF DW_OP_0
Comment 1 Jakub Jelinek 2020-12-01 11:38:19 UTC
The differentpointer bug seems to be fixed already in d70cbeaa
Otherwise, is e.g. readelf -wi happy with the debug info?
DWARF_OP_ 0 is certainly not valid, 0xff could be valid, but it hasn't been assigned yet (and I bet we'll want to use it as a multiplexor for further ops, i.e. use 0xff followed by uleb128 extended code followed by optional operands depending on the extended code, when we run out of DW_OP_* in the user range.
Comment 2 Martin Liska 2020-12-02 08:25:12 UTC
(In reply to Jakub Jelinek from comment #1)
> The differentpointer bug seems to be fixed already in d70cbeaa

All right.

> Otherwise, is e.g. readelf -wi happy with the debug info?

No, it's not happy:

$ readelf --debug=info ./gcc/gnatmake >/dev/null
readelf: Error: ../../binutils/dwarf.c:1989: read LEB value is too large to store in destination variable

Should I create an upstream GCC bug for it?
Comment 3 Mark Wielaard 2020-12-02 10:06:23 UTC
(In reply to Martin Liska from comment #2)
> (In reply to Jakub Jelinek from comment #1)
> > Otherwise, is e.g. readelf -wi happy with the debug info?
> 
> No, it's not happy:
> 
> $ readelf --debug=info ./gcc/gnatmake >/dev/null
> readelf: Error: ../../binutils/dwarf.c:1989: read LEB value is too large to
> store in destination variable
> 
> Should I create an upstream GCC bug for it?

Maybe, depends on how binutils readelf reads the LEB value. That warning is produced if the variable type used cannot contain the (signed) value of the (s)leb128. It might be that binutils readelf uses a too small (or signed instead of unsigned) type here. Is the gnatmake binary (or the debuginfo) available somewhere for inspection?
Comment 4 Martin Liska 2020-12-02 10:27:48 UTC
Created attachment 13020 [details]
Binary

The binary can be easily built as it's part of the GCC:

$ ../configure --enable-languages=c,c++,fortran,ada --prefix=/home/marxin/bin/gcc --disable-bootstrap --disable-multilib --disable-libsanitizer
$ make -j123
Comment 5 Mark Wielaard 2020-12-02 10:56:25 UTC
My local build of binutils readelf doesn't show this issue. Might it be https://sourceware.org/bugzilla/show_bug.cgi?id=26548 (which should be part of 2.35.1, but was a bug in 2.35)

Also dwz gnatmake doesn't show any issues.
So I assume it is an issue when doing dwz -m ?
Comment 6 Martin Liska 2020-12-02 12:29:14 UTC
(In reply to Mark Wielaard from comment #5)
> My local build of binutils readelf doesn't show this issue. Might it be
> https://sourceware.org/bugzilla/show_bug.cgi?id=26548 (which should be part
> of 2.35.1, but was a bug in 2.35)

It's actually the following commit:

commit ef5e3e92dc7234f64e77e8c8af5d61685d96263b
Author: Nick Clifton <nickc@redhat.com>
Date:   Tue Oct 27 16:17:13 2020 +0000

    Fix the decoding of DW_FORM_ref_addr DWARF attribute.
    
            * dwarf.c (struct abbrev_list): New structure.  Used to collect
            lists of abbreviation sets.
            (struct abbrev_map): New structure.  Used to map CU offsets to
            abbreviation offsets.
            (record_abbrev_list): New function.  A new entry to an
            abbreviation list.
            (free_all_abbrevs): Update to free abbreviation lists.
            (new_abbrev_list): New function.  Start a new abbreviation
            list.
            (find_abbrev_list_by_abbrev_offset): New function.
            (find_abbrev_map_by_offset): New function.
            (add_abbrev): Add abbrev_list parameter.
            (add_abbrev_attr): Likewise.
            (process_abbrev_section): Rename to process_abbrev_set and add
            list parameter.
            (get_type_abbrev_from_form): New function.  Attempts to decode the
            forms used by DW_AT_type attributes.
            (get_type_signedness): Display type names if operating in wide
            mode.  Use get_type_abbrev_from_form.
            (read_and_display_attr_value): Use get_type_abbrev_from_form.
            (process_debug_info): Pre-parse the CU headers to collate all the
            abbrevs before starting the main scan.
            (process_debug_abbrev): Do not free any loaded abbrevs.
            (free_debug_memory): Free the abbrev maps.
    
    (cherry picked from commit bcd213b2cfbca2df53fb7e5d187fd67ea8eb7185)

The commit was installed after 2.35.1 was released.

> 
> Also dwz gnatmake doesn't show any issues.
> So I assume it is an issue when doing dwz -m ?

Yes, I'll attach you a file from gcc11 package build that suffers from that.
Comment 7 Martin Liska 2020-12-02 12:31:19 UTC
Created attachment 13021 [details]
Problematic binary 2

$ cp gnatname-11-11.0.0+git181796-0.x86_64.debug 1
$ cp gnatname-11-11.0.0+git181796-0.x86_64.debug 2
$ ./dwz -m o 1 2
./dwz: 1: Unknown DWARF DW_OP_255
./dwz: 2: Unknown DWARF DW_OP_255
./dwz: Too few files for multifile optimization

error is reported here:

#0  0x00007ffff7ea5920 in error () from /lib64/libc.so.6
#1  0x0000000000405c9b in error (__format=0x433134 "%s: Unknown DWARF %s", __errnum=0, __status=0) at /usr/include/bits/error.h:42
#2  read_exprloc (dso=0x445660, die=0x7ffff65955e0, ptr=0x7ffff7506d04 '\377' <repeats 15 times>, "\366^\n", len=16, need_adjust=0x0) at dwz.c:1844
#3  0x00000000004082d4 in checksum_die (dso=0x445660, cu=0x7ffff65951c0, top_die=0x7ffff65955e0, die=0x7ffff65955e0) at dwz.c:2717
#4  0x000000000040849a in checksum_die (dso=0x445660, cu=0x7ffff65951c0, top_die=0x0, die=0x7ffff6595220) at dwz.c:2748
#5  0x0000000000411fb7 in read_debug_info (dso=0x445660, kind=0, die_count=0x4432c0) at dwz.c:5805
#6  0x0000000000424b55 in read_dwarf (dso=0x445660, quieter=false, die_count=0x4432c0) at dwz.c:10992
#7  0x000000000042adac in dwz (file=0x7fffffffe3e1 "1", outfile=0x0, res=0x4432a0, resa=0x0, files=0x7fffffffdfd0) at dwz.c:12540
#8  0x000000000042db05 in main (argc=5, argv=0x7fffffffdfb8) at dwz.c:13562
Comment 8 Mark Wielaard 2020-12-02 12:51:16 UTC
I believe this comes from:

 <1><54630>: Abbrev Number: 32 (DW_TAG_subrange_type)
    <54631>   DW_AT_lower_bound : 16 byte block: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 80       ((Unknown location op 0x0))
    <54642>   DW_AT_upper_bound : 16 byte block: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 7f        ((User defined location op 0xff))
    <54653>   DW_AT_name        : (indirect string, offset: 0xa6088): system__put_images__lll_integer_images__integer_typeXn
    <54657>   DW_AT_type        : <0x54660>

Where it seems that gnat thinks it is emitting a "constant" defined as block, but dwz (and readelf) interpret it as expression block.
Comment 9 Mark Wielaard 2020-12-02 13:09:45 UTC
This is what eu-readelf says about the same:

 [ 54630]    subrange_type        abbrev: 32
             lower_bound          (block1) 
              [ 0] ??? (0)
              [ 1] ??? (0)
              [ 2] ??? (0)
              [ 3] ??? (0)
              [ 4] ??? (0)
              [ 5] ??? (0)
              [ 6] ??? (0)
              [ 7] ??? (0)
              [ 8] ??? (0)
              [ 9] ??? (0)
              [10] ??? (0)
              [11] ??? (0)
              [12] ??? (0)
              [13] ??? (0)
              [14] ??? (0)
              [15] breg16  <TRUNCATED>
             upper_bound          (block1) 
              [ 0] lo_user+0x1f
              [ 1] lo_user+0x1f
              [ 2] lo_user+0x1f
              [ 3] lo_user+0x1f
              [ 4] lo_user+0x1f
              [ 5] lo_user+0x1f
              [ 6] lo_user+0x1f
              [ 7] lo_user+0x1f
              [ 8] lo_user+0x1f
              [ 9] lo_user+0x1f
              [10] lo_user+0x1f
              [11] lo_user+0x1f
              [12] lo_user+0x1f
              [13] lo_user+0x1f
              [14] lo_user+0x1f
              [15] breg15  <TRUNCATED>
             name                 (strp) "system__put_images__lll_integer_images__integer_typeXn"
             type                 (ref4) [ 54660]

So this is encoded as DW_FORM_block1, which is not a valid form for DW_AT_upper_bound or DW_AT_lower_bound. According the the DWARF spec only constant, exprloc, reference classes are.

Both readelfs and dwz seem to interpret this DW_FORM_block1 as an exprloc, which is arguably wrong (since DWARF4 there is DW_FORM_exprloc).

In DWARF3 a DW_AT_upperbound could be represented by block, constant, reference. But then section 2.19 said: "For a block, the value is interpreted as a DWARF expression; evaluation of the expression yields the value of the attribute."

In DWARF5 there is DW_FORM_data16, which could be used as a constant to express really big constants.
Comment 10 Tom Tromey 2020-12-02 14:19:19 UTC
(In reply to Mark Wielaard from comment #9)

> So this is encoded as DW_FORM_block1, which is not a valid form for
> DW_AT_upper_bound or DW_AT_lower_bound. According the the DWARF spec only
> constant, exprloc, reference classes are.

My understanding is that this is a small extension to DWARF, because...

> In DWARF5 there is DW_FORM_data16, which could be used as a constant to
> express really big constants.

... there's no other way to do this in earlier versions of DWARF.
I think if you use -gdwarf-5, GNAT will switch to DW_FORM_data16.

I have a gdb patch to deal with this for fixed-point (not yet submitted).
I haven't made gdb handle the bounds case, because gdb generally doesn't
handle 128-bit types there, so it would be a much larger change.
Comment 11 Jakub Jelinek 2020-12-02 14:26:25 UTC
(In reply to Tom Tromey from comment #10)
> (In reply to Mark Wielaard from comment #9)
> 
> > So this is encoded as DW_FORM_block1, which is not a valid form for
> > DW_AT_upper_bound or DW_AT_lower_bound. According the the DWARF spec only
> > constant, exprloc, reference classes are.
> 
> My understanding is that this is a small extension to DWARF, because...

If it is DWARF4 only, then it could be considered an extension, but if it applies to DWARF3 too, then it is certainly invalid.
I don't speak Ada, do you have some minimal testcase?
I've tried to write it in Fortran, but it seems to ignore it alltogether (probably a bug):
  integer(kind=16), parameter :: a = 18014398509481984_16
  integer(kind=16), parameter :: b = a * a
  integer(kind=4) :: c (b : b + 32)
  c(b + 2) = 1
end
Comment 12 Mark Wielaard 2020-12-02 14:35:10 UTC
I am working on a workaround for DWARF4+. But the obvious one (if (cu->cu_version < 4 && form == DW_FORM_block1)) doesn't work because at least some older versions of g++ (but not gcc!?!) do emit DW_AT_location as DW_FORM_block even when emitting DWARF4, so I need to figure out which DWARF4+ blocks are really (buggy?) exprlocs and which are (extension) "constants".
Comment 13 Jakub Jelinek 2020-12-02 14:40:05 UTC
I think we should just fix GCC, not add workarounds.
Comment 14 Jakub Jelinek 2020-12-02 15:08:15 UTC
From DWARF POV, I think the right solution is to use DW_FORM_sdata or DW_FORM_udata depending on whether the value is signed or unsigned.
Which means
      else
        /* Otherwise represent the bound as an unsigned value with
           the precision of its type.  The precision and signedness
           of the type will be necessary to re-interpret it
           unambiguously.  */
        add_AT_wide (die, attr, wi::to_wide (value));
needs to be replaced by add_AT_{signed,unsigned}_wide with a new classes that will also have wide_int argument, but will emit DW_FORM_sdata or DW_FORM_udata from the wide_int, and will be able to deal with values that don't fit into HOST_WIDE_INT/unsigned HOST_WIDE_INT.
On the consumer side the question is if consumers will be able to cope with such large leb128 values.
Another option (for DWARF4+ or non-strict DWARF2/3) is for these large constants in attributes that interpret in DWARF3 block class as a DWARF expression to use
DW_FORM_block (DW_FORM_exprloc for DWARF4/5) and in there after the uleb128 size put DW_OP_implicit_value with operands the size of the constant and the constant.
Yes, it will be 3 bytes larger, but it will not be invalid for DWARF3 and very questionable extension for DWARF4+.
Comment 15 Mark Wielaard 2020-12-02 15:14:54 UTC
BTW. I was wrong about gcc/g++ emitting a DW_AT_location with DW_FORM_block for DWARF4. We have a testcase testsuite/dwz.tests/varval.S which was generated using gdb/testsuite/gdb.dwarf2/varval.exp from repo git://sourceware.org/git/binutils-gdb.git which does this.

It has a special rule that says: 

# If FORM is 'SPECIAL_expr', then VALUE is treated as a location
# expression.  The effective form is then DW_FORM_block, and VALUE
# is passed to the (internal) '_location' proc to be translated.
# This proc implements a miniature DW_OP_ assembler.

Which is obviously wrong for DWARF version 4+.
Comment 16 Mark Wielaard 2020-12-02 15:17:16 UTC
> On the consumer side the question is if consumers will be able to cope with such large leb128 values.

In general no. At least elfutils and binutils only deal with max 64bit leb128 values. Both also treat the new DWARF5 DW_FORM_data16 as a block class (and not a constant class as a strict reading of the spec would imply).
Comment 17 Jakub Jelinek 2020-12-02 15:18:25 UTC
For the DW_FORM_{block,exprloc} case, I'd think something like:
--- gcc/dwarf2out.c	2020-12-01 14:39:32.175882586 +0100
+++ gcc/dwarf2out.c	2020-12-02 16:16:44.189189583 +0100
@@ -20780,7 +20780,14 @@ add_scalar_info (dw_die_ref die, enum dw
 	   the precision of its type.  The precision and signedness
 	   of the type will be necessary to re-interpret it
 	   unambiguously.  */
-	add_AT_wide (die, attr, wi::to_wide (value));
+	{
+	  rtx v = immed_wide_int_const (value, TYPE_MODE (TREE_TYPE (value)));
+	  dw_loc_descr_ref loc
+	    = loc_descriptor (v, TYPE_MODE (TREE_TYPE (value)),
+			      VAR_INIT_STATUS_INITIALIZED);
+	  if (loc)
+	    add_AT_loc (die, attr, loc);
+	}
       return;
     }
 
could do it, but as I don't speak Ada I can't verify easily.
Comment 18 Jakub Jelinek 2020-12-02 15:21:34 UTC
(In reply to Mark Wielaard from comment #16)
> > On the consumer side the question is if consumers will be able to cope with such large leb128 values.
> 
> In general no. At least elfutils and binutils only deal with max 64bit
> leb128 values. Both also treat the new DWARF5 DW_FORM_data16 as a block
> class (and not a constant class as a strict reading of the spec would imply).

Strict reading of the spec says that constant class is DW_FORM_data{1,2,4,8,16},
DW_FORM_{s,u}data and DW_FORM_implicit_const, at least in DWARF5.
Comment 19 Mark Wielaard 2020-12-02 15:27:17 UTC
(In reply to Jakub Jelinek from comment #18)
> (In reply to Mark Wielaard from comment #16)
> > > On the consumer side the question is if consumers will be able to cope with such large leb128 values.
> > 
> > In general no. At least elfutils and binutils only deal with max 64bit
> > leb128 values. Both also treat the new DWARF5 DW_FORM_data16 as a block
> > class (and not a constant class as a strict reading of the spec would imply).
> 
> Strict reading of the spec says that constant class is
> DW_FORM_data{1,2,4,8,16},
> DW_FORM_{s,u}data and DW_FORM_implicit_const, at least in DWARF5.

Right, but given that most/all consumers represent constants with max 64bit signed/unsigned types, they cannot easily deal with DW_FORM_data16 or excessively large leb128 values.
Comment 20 Tom Tromey 2020-12-02 15:35:34 UTC
(In reply to Jakub Jelinek from comment #18)

> Strict reading of the spec says that constant class is
> DW_FORM_data{1,2,4,8,16},
> DW_FORM_{s,u}data and DW_FORM_implicit_const, at least in DWARF5.

I'd rather we not do strict readings of the spec, though,
unless the strict-dwarf setting is enabled.  DWARF extensibility
is useful.  In this case it is just for backward compatibility
of some kind, since the problem is solved in DWARF 5.

The problem with using sdata/udata is that it's unpredictable
when a 128-bit value might be needed.  This means all the
readers will have to handle this at the lowest level.  Using a
block form avoids this, because gdb can decide at certain points
to just drop things it doesn't understand.

Here's the internal test case that shows the issue.
A similar, but slightly different, test case is in gdb, see
gdb/testsuite/gdb.ada/fixed_points/fixed_points.adb.

Anyway save this as "test_case.adb" and then do:
$ gnatmake -g test_case

I see the subrange DIE in the test_case.abd CU.
I used today's gcc trunk.

with System;

procedure Test_Case is

   type Base_Fixed_Point_Type is
     delta 1.0 / 16.0
       range (System.Min_Int / 2) * 1.0 / 16.0 ..
       (System.Max_Int / 2) * 1.0 / 16.0;

     subtype Fixed_Point_Subtype is
       Base_Fixed_Point_Type range -50.0 .. 50.0;

     type New_Fixed_Point_Type is
       new Base_Fixed_Point_Type range -50.0 .. 50.0;

     Base_Object            : Base_Fixed_Point_Type := -50.0;
     Subtype_Object         : Fixed_Point_Subtype := -50.0;
     New_Type_Object        : New_Fixed_Point_Type := -50.0;
begin
   Base_Object := 1.0/16.0; -- BREAK
   Subtype_Object := 1.0/16.0;
end Test_Case;
Comment 21 Eric Botcazou 2020-12-02 16:05:35 UTC
My understanding is that in most cases these 128-bit subtypes with problematic DW_AT_upper_bound & DW_AT_lower_bound are useless so we could try to drop the bounds if DW_FORM_data16 is not available.
Comment 23 Mark Wielaard 2021-02-13 22:52:29 UTC
Although this was fixed in gcc we can make dwz more robust by not trying to interpret a block as locexpr for DWARF version 4 or higher.

https://sourceware.org/pipermail/dwz/2021q1/000859.html
Comment 24 Mark Wielaard 2021-02-18 21:19:41 UTC
commit bd3c7500ef144480bff353064195212f2e330e3d
Author: Mark Wielaard <mark@klomp.org>
Date:   Sat Feb 13 23:34:55 2021 +0100

    Don't handle blocks as exprlocs for DWARF version 4 or higher.
    
    Since DWARF version 4 blocks just contain bytes, trying to interpret
    them as exprlocs will most likely fail. Also make sure block1 form
    and len are correctly passed to add_locexpr_dummy_dies.
    
         * dwz.c (add_locexpr_dummy_dies): Only handle block as exprloc
         for cu_version < 4.
         (checksum_die): Likewise.
         (write_die): Likewise.
         (read_debug_info): Get block length before calling
         add_locexpr_dummy_dies.
    
    https://sourceware.org/bugzilla/show_bug.cgi?id=26987