This is the mail archive of the gdb@sources.redhat.com mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

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


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


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]