Hi!
Currently there is an incompatibility between Clang and GDB for
location list entries that are call-clobbered. Variables that are
expressed in terms of scratch registers may be printed using incorrect
register values when standing in outer frames. Here is an x86-64
example:
main.c:
1 extern int value(void);
2 extern void call(int);
3
4 int main() {
5 int local = value();
6 call(local);
7 return 0;
8 }
callee.c:
1 int value(void) {
2 return 123;
3 }
4
5 int call(int param) {
6 // Thrash $eax.
7 __asm volatile ("movl $999, %%eax" : : : "%eax");
8 return 0;
9 }
Compiled with Clang (I used 7.0 here, but it's the same on trunk):
$ clang -O1 -g caller.c callee.c
will produce the following output when run in GDB (8.2.1):
(gdb) b callee.c:8
Breakpoint 1 at 0x201125: file callee.c, line 8.
(gdb) run
Starting program: /home/david/clang.out
Breakpoint 1, call (param=123) at callee.c:8
8 return 0;
(gdb) up
#1 0x00000000002010fd in main () at main.c:6
6 call(local);
(gdb) print local
$1 = 999
As seen, local is printed with the incorrect value (999 rather than
123).
If the file is compiled with GCC (8.3) the outer frame's variable is
instead correctly printed as <optimized out>:
(gdb) b callee.c:8
Breakpoint 1 at 0x110a: file callee.c, line 8.
(gdb) run
Starting program: /home/david/gcc.out
Breakpoint 1, call (param=123) at callee.c:8
8 return 0;
(gdb) up
#1 0x00005555555550f5 in main () at main.c:6
6 call(local);
(gdb) print local
$1 = <optimized out>
GCC emits a location list entry for `local' that ends one byte inside
the call:
(gdb) info addr local
Symbol "local" is multi-location:
Range 0x5555555550ee-0x5555555550f4: a variable in $rax
.
(gdb) disas main
Dump of assembler code for function main:
=> 0x00005555555550e5 <+0>: sub $0x8,%rsp
0x00005555555550e9 <+4>: callq 0x5555555550ff <value>
0x00005555555550ee <+9>: mov %eax,%edi
0x00005555555550f0 <+11>: callq 0x555555555105 <call>
0x00005555555550f5 <+16>: mov $0x0,%eax
0x00005555555550fa <+21>: add $0x8,%rsp
0x00005555555550fe <+25>: retq
End of assembler dump.
whereas Clang emits a location list entry which covers the whole call:
(gdb) info addr local
Symbol "local" is multi-location:
Range 0x2010f6-0x2010fd: a variable in $rax
.
(gdb) disas main
Dump of assembler code for function main:
0x00000000002010f0 <+0>: push %rax
=> 0x00000000002010f1 <+1>: callq 0x201110 <value>
0x00000000002010f6 <+6>: mov %eax,%edi
0x00000000002010f8 <+8>: callq 0x201120 <call>
0x00000000002010fd <+13>: xor %eax,%eax
0x00000000002010ff <+15>: pop %rcx
0x0000000000201100 <+16>: retq
End of assembler dump.
As I've understood it, GCC emits such location list entry ranges since
it knows that when performing virtual unwinding, GDB (and other
consumers) will subtract one byte from the return address in order to
get the right scope for the call. Utilizing that fact allows for
`local' to be printable before executing the call, but not when
unwinding back to the frame during the call. In the case of Clang, the
location list entry covers the whole call, and the variable is thus
printed with the register's unwound value, which is considered the same
since there is no CFI that says otherwise.
I'm trying to figure out how to solve this incompatibility between
Clang and GDB.
I sent a mail to the dwarf-discuss mailing list about GCC's behavior at
the end of last year [0]. I got some push-back, with some comments
that the consumer should know which registers are preserved and which
are not.
LLDB is aware of which registers are preserved in the ABI, and other
registers are considered undefined when unwinding, meaning that the
same issue is not seen when debugging Clang-built binaries in LLDB.
Unless I have misunderstood something, it appears that the same is
done for some targets in GDB (e.g. rs6000 and s390), by setting the
frame register rule to DWARF2_FRAME_REG_UNDEFINED for the scratch
registers in {target}_dwarf2_frame_init_reg(). Is that correct, or
have I misunderstood what that function does? If that is correct,
would you be open to such information being added to other targets,
or are there some reasons for not wanting that? (Personally I'm
mostly interested in 32- and 64-bit x86.)
Best regards,
David
[0]
http://lists.dwarfstd.org/pipermail/dwarf-discuss-dwarfstd.org/2018-December/004500.html