[英]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
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 中的默认设置。
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.
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.