Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
905 views
in Technique[技术] by (71.8m points)

c++ - GDB shows incorrect arguments of functions for stack frames

Whenever GDB steps in functions, instead of showing correct arguments in frame info, it prints garbage data

gcc (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0
$ gcc t.c -g #no other flags are used
GNU gdb (Ubuntu 9.2-0ubuntu1~20.04) 9.2

The program (t.c):

#include<stdio.h>

void foo(int v){
    printf("  BAR = %d
", v);
    }

int main(){
    int a = 8;
    foo(a);
    a = 33;
    foo(a);
    foo(85);
    }

GDB output:

Breakpoint 1, main () at t.c:7
7   int main(){
(gdb) step
8       int a = 8;
(gdb) step
9       foo(a);
(gdb) step
foo (v=21845) at t.c:3
3   void foo(int v){
(gdb) finish
Run till exit from #0  foo (v=21845) at t.c:3
  BAR = 8
main () at t.c:10
10      a = 33;
(gdb) s
11      foo(a);
(gdb) 
foo (v=8) at t.c:3
3   void foo(int v){
(gdb) fin
Run till exit from #0  foo (v=8) at t.c:3
  BAR = 33
main () at t.c:12
12      foo(85);
(gdb) s
foo (v=33) at t.c:3
3   void foo(int v){
(gdb) fin
Run till exit from #0  foo (v=33) at t.c:3
  BAR = 85
0x00005555555551a9 in main () at t.c:12
12      foo(85);
(gdb) s
13      }

But after performing a step after stepping in functions, arguments are written with correct data:

Breakpoint 1, main () at t.c:7
7   int main(){
(gdb) s
8       int a = 8;
(gdb) 
9       foo(a);
(gdb) 
foo (v=21845) at t.c:3
3   void foo(int v){
(gdb) info args
v = 21845
(gdb) s
4       printf("  BAR = %d
", v);
(gdb) info args
v = 8

Is there any way to fix this so GDB show correct arguments of functions?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Looking at the disassembly, gdb is stopped at the first instruction of function foo, before the function prologue (which sets up the stack and frame pointers) has been run:

(gdb) step
9           foo(a);
(gdb) step
foo (v=21845) at t.c:3
3       void foo(int v){
(gdb) disas
Dump of assembler code for function foo:
=> 0x0000555555555149 <+0>:     endbr64
   0x000055555555514d <+4>:     push   %rbp
   0x000055555555514e <+5>:     mov    %rsp,%rbp
   0x0000555555555151 <+8>:     sub    $0x10,%rsp
   0x0000555555555155 <+12>:    mov    %edi,-0x4(%rbp)
   0x0000555555555158 <+15>:    mov    -0x4(%rbp),%eax
   0x000055555555515b <+18>:    mov    %eax,%esi
   0x000055555555515d <+20>:    lea    0xea0(%rip),%rdi        # 0x555555556004
   0x0000555555555164 <+27>:    mov    $0x0,%eax
   0x0000555555555169 <+32>:    callq  0x555555555050 <printf@plt>
   0x000055555555516e <+37>:    nop
   0x000055555555516f <+38>:    leaveq
   0x0000555555555170 <+39>:    retq
End of assembler dump.

Gdb's step command normally steps over a function's prologue, that is, it stops the program after the prologue has run. Here, gdb apparently doesn't recognize the instruction endbr64 as being part of any known prologue.

We can see that &v is beyond the bounds of the current stack frame:

(gdb) p &v
$1 = (int *) 0x7fffffffe3fc
(gdb) i r rbp rsp
rbp            0x7fffffffe420
rsp            0x7fffffffe408

Since the new stack frame hasn't been set up yet, gdb will read a garbage value for v.

Stepping a few more instructions will set up the stack frame and spill v from %edi to -0x4(%rbp):

(gdb) stepi
=> 0x000055555555514d <foo+4>:  push   %rbp
(gdb) stepi
=> 0x000055555555514e <foo+5>:  mov    %rsp,%rbp
(gdb) stepi
=> 0x0000555555555151 <foo+8>:  sub    $0x10,%rsp
(gdb) stepi
=> 0x0000555555555155 <foo+12>: mov    %edi,-0x4(%rbp)
(gdb) stepi
4           printf("  BAR = %d
", v);
=> 0x0000555555555158 <foo+15>: mov    -0x4(%rbp),%eax

Verify that &v is now within the stack frame, and examine v's value:

(gdb) p &v
$2 = (int *) 0x7fffffffe3fc
(gdb) i r rbp rsp
rbp            0x7fffffffe400
rsp            0x7fffffffe3f0
(gdb) p v
$3 = 8

Why did this happen

Gcc emits endbr64 when given the -fcf-protection option, which has been the default in Ubuntu's gcc since version 19.10.

One workaround

If you compile your program with -fcf-protection=none, gdb can recognize and run the prologue before stopping, and it will show the correct value of v:

(gdb) step
9           foo(a);
(gdb) step
foo (v=8) at t.c:4
4           printf("  BAR = %d
", v);
(gdb) disas
Dump of assembler code for function foo:
   0x0000555555555139 <+0>:     push   %rbp
   0x000055555555513a <+1>:     mov    %rsp,%rbp
   0x000055555555513d <+4>:     sub    $0x10,%rsp
   0x0000555555555141 <+8>:     mov    %edi,-0x4(%rbp)
=> 0x0000555555555144 <+11>:    mov    -0x4(%rbp),%eax
   0x0000555555555147 <+14>:    mov    %eax,%esi
   0x0000555555555149 <+16>:    lea    0xeb4(%rip),%rdi        # 0x555555556004
   0x0000555555555150 <+23>:    mov    $0x0,%eax
   0x0000555555555155 <+28>:    callq  0x555555555030 <printf@plt>
   0x000055555555515a <+33>:    nop
   0x000055555555515b <+34>:    leaveq
   0x000055555555515c <+35>:    retq
End of assembler dump.

Fixed in the new gdb

It looks like support for the endbr instructions was added to gdb in March 2020, so things should be fine if you can use gdb 10.1 or later:

$ ~/gdb10.1/bin/gdb -q t
...
(gdb) step
9           foo(a);
(gdb) step
foo (v=8) at t.c:4
4           printf("  BAR = %d
", v);
(gdb) disas
Dump of assembler code for function foo:
   0x0000555555555149 <+0>:     endbr64
   0x000055555555514d <+4>:     push   %rbp
   0x000055555555514e <+5>:     mov    %rsp,%rbp
   0x0000555555555151 <+8>:     sub    $0x10,%rsp
   0x0000555555555155 <+12>:    mov    %edi,-0x4(%rbp)
=> 0x0000555555555158 <+15>:    mov    -0x4(%rbp),%eax
   0x000055555555515b <+18>:    mov    %eax,%esi
   0x000055555555515d <+20>:    lea    0xea0(%rip),%rdi        # 0x555555556004
   0x0000555555555164 <+27>:    mov    $0x0,%eax
   0x0000555555555169 <+32>:    call   0x555555555050 <printf@plt>
   0x000055555555516e <+37>:    nop
   0x000055555555516f <+38>:    leave
   0x0000555555555170 <+39>:    ret
End of assembler dump.

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...