[RFC] Possible gcc/DWARF or gdb/DWARF problem

Fred Fish
2003-11-29T20:26:10Z

While chasing a gdb testsuite failure for the sh-elf toolchain, I
discovered a problem printing a local variable that appears to be
either a generic gcc bug in how it generates DWARF2 info for the local
variable, or perhaps a gdb bug in how gdb interprets the DWARF2
information.

I've done a quick check of the 2.0.0 revision of the DWARF industry
review draft of the DWARF standard and I didn't see anything obvious
that relates to this particular situation. But it has been years
since I did any serious low level DWARF hacking so I could have easily
missed something.

Consider the following code, simplified from some "scope" tests in the
gdb testsuite:

signed char bar (register signed char u)
{
return (u + 1);
}

signed char foo (register signed char u)
{
register signed char l = u;

l = bar (l);
return (l);
}

main ()
{
foo (3);
}

Note that:

(1) the register declarations are required to expose the bug. Without
them, the generated code and debug information is different and the
problem goes away.

(2) The example should be compiled without optimization.

(3) The problem is masked if the target is little endian. Toolchains
such as sh-elf or mipsisa32-elf which are bi-endian help to
demonstrate this.

Here is an example using the sh-elf toolchain in big endian mode.
Note that the value of 'l' after being assigned from 'u' and before
calling bar() is printed as 0 and should be 3, the same as 'u':

$ sh-elf-gcc -g -m4 -mb -o b b.c
$ sh-elf-gdb b
GNU gdb 2003-11-29-cvs
Copyright 2003 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "--host=i686-pc-linux-gnu --target=sh-elf"...
(gdb) tar sim
Connected to the simulator.
(gdb) load
Loading section .init, size 0x36 vma 0x1000
Loading section .text, size 0x1900 vma 0x1040
Loading section .fini, size 0x2a vma 0x2940
Loading section .rodata, size 0x1e vma 0x296c
Loading section .data, size 0x798 vma 0x2a0c
Loading section .eh_frame, size 0x4 vma 0x31a4
Loading section .ctors, size 0x8 vma 0x31a8
Loading section .dtors, size 0x8 vma 0x31b0
Loading section .jcr, size 0x4 vma 0x31b8
Loading section .got, size 0xc vma 0x31bc
Loading section .stack, size 0x4 vma 0x30000
Start address 0x1040
Transfer rate: 68080 bits/sec.
(gdb) br foo
Breakpoint 1 at 0x1186: file b.c, line 8.
(gdb) run
Starting program: /tmp/b

Breakpoint 1, foo (u=3 '\003') at b.c:8
8 register signed char l = u;
(gdb) n
10 l = bar (l);
(gdb) p l
$1 = 0 '\0'
(gdb)

If however we use little endian mode, the value is printed correctly:

$ sh-elf-gcc -g -m4 -ml -o b b.c
$ sh-elf-gdb b
GNU gdb 2003-11-29-cvs
Copyright 2003 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "--host=i686-pc-linux-gnu --target=sh-elf"...
(gdb) tar sim
Connected to the simulator.
(gdb) load
Loading section .init, size 0x36 vma 0x1000
Loading section .text, size 0x1900 vma 0x1040
Loading section .fini, size 0x2a vma 0x2940
Loading section .rodata, size 0x1e vma 0x296c
Loading section .data, size 0x798 vma 0x2a0c
Loading section .eh_frame, size 0x4 vma 0x31a4
Loading section .ctors, size 0x8 vma 0x31a8
Loading section .dtors, size 0x8 vma 0x31b0
Loading section .jcr, size 0x4 vma 0x31b8
Loading section .got, size 0xc vma 0x31bc
Loading section .stack, size 0x4 vma 0x30000
Start address 0x1040
Transfer rate: 68080 bits/sec.
(gdb) br foo
Breakpoint 1 at 0x1186: file b.c, line 8.
(gdb) run
Starting program: /tmp/b

Breakpoint 1, foo (u=3 '\003') at b.c:8
8 register signed char l = u;
(gdb) n
10 l = bar (l);
(gdb) p l
$1 = 3 '\003'
(gdb)

The problem is that the live value of 'l' at the time we print it is
on the stack, and the address computed from the dwarf information is
for the lowest address of the 4 byte stack slot that holds the
character value. In the big endian case, this is for the most
significant byte, which is zero. In the little endian case, this is
for the least significant byte, which is 3.

Note also that the dwarf info is identical for either endian:

$ sh-elf-gcc -g -m4 -mb -c b.c
$ dwarfdump -a b.o >b.o.mb
$ sh-elf-gcc -g -m4 -ml -c b.c
$ dwarfdump -a b.o >b.o.ml
$ diff b.o.mb b.o.ml

The code is also identical except for a pseudo that indicates the
endianness:

$ sh-elf-gcc -g -m4 -mb -S b.c
$ mv b.s b.s.mb
$ sh-elf-gcc -g -m4 -ml -S b.c
$ mv b.s b.s.ml
$ diff b.s.mb b.s.ml
2a3
> .little
$ diff -c b.s.mb b.s.ml
*** b.s.mb 2003-11-29 13:00:08.000000000 -0700
--- b.s.ml 2003-11-29 13:00:16.000000000 -0700
***************
*** 1,5 ****
--- 1,6 ----
.file "b.c"
.text
+ .little
.section .debug_abbrev,"",@progbits
.Ldebug_abbrev0:
.section .debug_info,"",@progbits
$

For reference, here are the relevant parts of the dwarfdump output:

<1>< 107> DW_TAG_base_type
DW_AT_name signed char
DW_AT_byte_size 1
DW_AT_encoding DW_ATE_signed_char

<2>< 160> DW_TAG_variable
DW_AT_name l
DW_AT_decl_file 1 /tmp/b.c
DW_AT_decl_line 8
DW_AT_type <107>
DW_AT_location DW_OP_fbreg 0

I'd like to get some comments on whether this is a gcc/DWARF or
gdb/DWARF issue. If the consensus is that gdb is not doing the right
thing, then I'll chase the problem in gdb. If the consensus is that
it is gcc, then I'll file a formal gcc bug report. My guess is that
since gcc is generating the memory offset of a 1 byte object relative
to the frame pointer that the DW_AT_location for the big endian case
should be:

DW_AT_location DW_OP_fbreg 3

-Fred