简体   繁体   English

GDB 显示堆栈帧的函数参数不正确

[英]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每当 GDB 进入函数时,它不会在帧信息中显示正确的参数,而是打印垃圾数据

gcc (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0 gcc (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0
$ gcc tc -g #no other flags are used $ gcc tc -g #不使用其他标志
GNU gdb (Ubuntu 9.2-0ubuntu1~20.04) 9.2 GNU gdb (Ubuntu 9.2-0ubuntu1~20.04) 9.2

The program (tc):程序(tc):

#include<stdio.h>

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

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

GDB output: GDB 输出:

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\n", v);
(gdb) info args
v = 8

Is there any way to fix this so GDB show correct arguments of functions?有什么办法可以解决这个问题,以便 GDB 显示正确的函数参数?

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 在函数foo的第一条指令处停止,在函数序言(设置堆栈和帧指针)运行之前:

(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. Gdb 的 step 命令通常会跳过函数的 prologue ,也就是说,它会在 prologue 运行后停止程序。 Here, gdb apparently doesn't recognize the instruction endbr64 as being part of any known prologue.在这里,gdb 显然没有将指令endbr64识别为任何已知序言的一部分。

We can see that &v is beyond the bounds of the current stack frame:我们可以看到&v超出了当前堆栈帧的边界:

(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 .由于尚未建立新的堆栈帧,gdb 将读取v的垃圾值。

Stepping a few more instructions will set up the stack frame and spill v from %edi to -0x4(%rbp) :多执行一些指令将设置堆栈帧并将v%edi溢出到-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\n", v);
=> 0x0000555555555158 <foo+15>: mov    -0x4(%rbp),%eax

Verify that &v is now within the stack frame, and examine v 's value:验证&v现在是否在堆栈帧中,并检查v的值:

(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 .当给定-fcf-protection选项时,gcc 会发出endbr64 ,这是自 19.10 版以来Ubuntu 的 gcc 中的默认设置。

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 :如果你用-fcf-protection=none编译你的程序,gdb 可以在停止前识别并运行序言,它会显示正确的v值:

(gdb) step
9           foo(a);
(gdb) step
foo (v=8) at t.c:4
4           printf("  BAR = %d\n", 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在新的 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:似乎在 2020 年 3 月向 gdb 添加了对endbr指令的支持,因此如果您可以使用 gdb 10.1 或更高版本,应该没问题:

$ ~/gdb10.1/bin/gdb -q t
...
(gdb) step
9           foo(a);
(gdb) step
foo (v=8) at t.c:4
4           printf("  BAR = %d\n", 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.

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM