Permit DW_OP_GNU_uninit to be used with DW_OP_piece
authorKevin Buettner <kevinb@redhat.com>
Sun, 11 Jun 2023 22:01:49 +0000 (15:01 -0700)
committerKevin Buettner <kevinb@redhat.com>
Sun, 11 Jun 2023 22:41:42 +0000 (15:41 -0700)
commitd65befc15220572d9ff6eebbe26c709c306042ba
tree3c4c958dead1266dc31f2587e9194ba78d66c90b
parent5bd107317840397c3170a9f5a3d8ecc144988cfd
Permit DW_OP_GNU_uninit to be used with DW_OP_piece

This commit implements a fix for a bug reported against GDB on
Fedora bugzilla...

https://bugzilla.redhat.com/show_bug.cgi?id=2166796

The test case in that bug report involved running gdb against the 'jq'
program (which is a command-line JSON processor) on Fedora 37.  Since
the debug info is compiler (and compile-time option) dependent, it
won't necessarily show up in other distributions or even past or
future versions of Fedora.  (E.g. when trying the example shown below
on Fedora 38, GDB says that the value of 'value' has been optimized
out.  I.e. it does not demonstrate the same DWARF error that can be
see when using Fedora 37.)

That said, on Fedora 37, the bug could be reproduced as follows:

[kev@f37-1 ~]$ gdb jq -q -ex 'b src/util.c:415' -ex 'r </dev/null'
Reading symbols from jq...

This GDB supports auto-downloading debuginfo from the following URLs:
  <https://debuginfod.fedoraproject.org/>
Enable debuginfod for this session? (y or [n]) y
Debuginfod has been enabled.
To make this setting permanent, add 'set debuginfod enabled on' to .gdbinit.
Reading symbols from /home/kev/.cache/debuginfod_client/9d3c8b4197350a190a74972d481de32abf641aa4/debuginfo...
No source file named src/util.c.
Make breakpoint pending on future shared library load? (y or [n]) y
Breakpoint 1 (src/util.c:415) pending.
Starting program: /usr/bin/jq </dev/null
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".

Breakpoint 1, jq_util_input_next_input (state=0x55555555d7f0) at src/util.c:416
416     if (state->parser == NULL) {
(gdb) p value
DWARF-2 expression error: DW_OP_GNU_uninit must always be the very last op.

This is undesirable - rather than output an error about the DWARF
info, we'd prefer to see a value, even if it is uninitialized.

Examination of the debuginfo showed the following:

 <1><468f1>: Abbrev Number: 112 (DW_TAG_subprogram)
    <468f2>   DW_AT_external    : 1
    <468f2>   DW_AT_name        : (indirect string, offset: 0x4781): jq_util_input_next_input
    <468f6>   DW_AT_decl_file   : 10
    <468f6>   DW_AT_decl_line   : 411
    <468f8>   DW_AT_decl_column : 4
    <468f9>   DW_AT_prototyped  : 1
    <468f9>   DW_AT_type        : <0x3f2>
    <468fd>   DW_AT_sibling     : <0x4692e>
...
 <2><46921>: Abbrev Number: 102 (DW_TAG_variable)
    <46922>   DW_AT_name        : (indirect string, offset: 0x8cb): value
    <46926>   DW_AT_decl_file   : 10
    <46926>   DW_AT_decl_line   : 414
    <46928>   DW_AT_decl_column : 6
    <46929>   DW_AT_type        : <0x3f2>

Note that there's no DW_AT_location, so I looked for an abstract origin entry:

 <2><2dfa0>: Abbrev Number: 90 (DW_TAG_variable)
    <2dfa1>   DW_AT_abstract_origin: <0x46921>
    <2dfa5>   DW_AT_location    : 0x27cf1 (location list)
    <2dfa9>   DW_AT_GNU_locviews: 0x27ce1

(Note that the DW_AT_abstract_origin attribute's value is 0x46921 which
is the DIE for the local variable "value".)

Looking at the location list, I see:

    00027cf1 v000000000000000 v000000000000000 views at 00027ce1 for:
             000000000002f8fe 000000000002f92e (DW_OP_reg13 (r13); DW_OP_GNU_uninit; DW_OP_piece: 8; DW_OP_reg12 (r12); DW_OP_GNU_uninit; DW_OP_piece: 8)

While DW_OP_GNU_uninit is not the very last op, it is the last op
prior to DW_OP_piece.  The fix involved changing the DW_OP_GNU_uninit
case in dwarf_expr_context::execute_stack_op in gdb/dwarf2/expr.c so
that DW_OP_GNU_uninit may appear just before DW_OP_piece.

With the fix in place, attempting to print 'value' now looks like
this:

(gdb) p value
$1 =  [uninitialized] {kind_flags = 0 '\000', pad_ = 0 '\000', offset = 0,
  size = 0, u = {ptr = 0x0, number = 0}}

Note that "[uninitialized]" is part of the output.  (But also note
that there's an extra space character.)

I've made a new test case,
gdb.dwarf2/DW_OP_piece_with_DW_OP_GNU_uninit.exp, by adapting an
existing one, gdb.dwarf2/opt-out-not-implptr.exp.  Since it uses the
DWARF assembler, the test case does not depend on a specific compiler
version or compiler options.

Tested on Fedora 37 and Fedora 38.
gdb/dwarf2/expr.c
gdb/testsuite/gdb.dwarf2/DW_OP_piece_with_DW_OP_GNU_uninit.exp [new file with mode: 0644]