GDB 显示堆栈帧的函数参数不正确 [英] GDB shows incorrect arguments of functions for stack frames

查看:13
本文介绍了GDB 显示堆栈帧的函数参数不正确的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

每当 GDB 进入函数时,它不会在帧信息中显示正确的参数,而是打印垃圾数据

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 #不使用其他标志
GNU gdb (Ubuntu 9.2-0ubuntu1~20.04) 9.2

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

程序(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 输出:

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

有没有办法解决这个问题,让 GDB 显示正确的函数参数?

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

推荐答案

看反汇编,gdb 停在函数 foo 的第一条指令处,在函数序言(设置堆栈)之前和帧指针)已运行:

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 的 step 命令通常会跳过函数的序言,也就是说,它会在序言运行后停止程序.在这里,gdb 显然没有将指令 endbr64 识别为任何已知序言的一部分.

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.

我们可以看到&v超出了当前栈帧的边界:

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

由于尚未建立新的堆栈帧,gdb 将读取 v 的垃圾值.

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

再执行一些指令将设置堆栈帧并将 v%edi 溢出到 -0x4(%rbp):

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

验证 &v 现在是否在堆栈帧中,并检查 v 的值:

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

为什么会这样

当给定 -fcf-protection 选项时,Gcc 会发出 endbr64,这是 Ubuntu 的 gcc 从 19.10 版开始.

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

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.

在新的 gdb 中修复

看起来对 endbr 指令的支持已添加到 gdb 2020 年 3 月,所以如果你可以使用 gdb 10.1 或更高版本应该没问题:

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.

这篇关于GDB 显示堆栈帧的函数参数不正确的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆