__x86.get_pc_thunk.bx对GDB打印局部变量的影响

描述__x86.get_pc_thunk.bx对GDB打印局部变量的影响,顺便简单介绍gdb的原理。

问题简要描述

最近乘着项目的空余时间在做《MIT6.828》这个作业,久闻其名之后,我开始了真正的作业。 但是在做作业的过程中,却发现了一个异常场景,通过GDB打印函数中的局部变量的时候,却发现获取的变量的值并不是真正的变量地址。我目前使用的GDB版本号和GCC版本号如下: gcc版本号是7.5.0,gdb版本号是8.1.1。

问题详细描述

  1. 编写代码如下
    //test1.c
    int add(int lhs, int rhs) {
        return lhs + rhs;
    }
    int glob = 400;
    void main() {
        int i = 0;
        int test123 = 200;
        int c = add(i, test123);
        for (; i < 300; ++i) {
            glob = glob + i;
        }
        return;
    }
    
  2. 编译为32位,且使用stabs调试信息 gcc -gstabs -o test1 teest1.c -m32

  3. 通过gdb对test1程序进行分析,打印局部变量i后,发现i的值并不是0,而是打印了test123的值,同时,test123对应的值也不再正确

  1. gcc加入编译命令,-no-pie -fno-pic,去除了内部函数__x86.get_pc_thunk.bx,之后,gdb能够正确打印局部变量。去除编译器为我们生成的__x86.get_pc_thunk.ax gcc -gstabs -no-pie -fno-pic -o test1 test1.c -m32

原因分析

GDB原理简述

原来gdb的底层调试原理这么简单 How debugger works

GDB启动过程

系统首先会启动gdb进程,这个进程会调用系统函数fork()来创建一个子进程,这个子进程做两件事情: 1. 调用系统函数ptrace(PTRACE_TRACEME,[其他参数]); 2. 通过execc来加载、执行可执行程序test,那么test程序就在这个子进程中开始执行了。

ptrace

ptrace系统函数是Linux内核提供的一个用于进程跟踪的系统调用,通过它,一个进程(gdb)可以读写另外一个进程(test)的指令空间、数据空间、堆栈和寄存器的值。而且gdb进程接管了test进程的所有信号,也就是说系统向test进程发送的所有信号,都被gdb进程接收到,这样一来,test进程的执行就被gdb控制了,从而达到调试的目的。 正是在ptrace的帮助下,gdb才拥有了强大的调试能力。函数原型是:

#include <sys/ptrace.h>
long ptrace(enum __ptrace_request request, pid_t pid, void *addr, void *data);

gdb如何确定局部变量的值

以调试格式stab为例。objdump -G test可以获取stab信息。 我们关注.stab节中的n_value列,对于局部变量itest123来说,其n_type的类型是LSYM表示这是一个局部变量,其n_value的值表示的是其距离栈底的距离,以负数显示。从调试信息来看,i距离栈底为-12,test123距离栈底为-8。因此当我们打印局部变量itest123的时候,会取出寄存器ebp的值,并加上n_value的值,然后调用ptrace系统调用,传入对应的地址,就可以获取得到相应的局部变量的值了。

test@iZuf6bogmr00a004vw0sbeZ:~/ctest$ objdump -G test1

test1:     文件格式 elf32-i386

.stab 节的内容:

Symnum n_type n_othr n_desc n_value  n_strx String

-1     HdrSym 0      51     000003d1 1     
0      SO     0      2      000004ed 1      test1.c
1      OPT    0      0      00000000 9      gcc2_compiled.
2      LSYM   0      0      00000000 24     int:t(0,1)=r(0,1);-2147483648;2147483647;
3      LSYM   0      0      00000000 66     char:t(0,2)=r(0,2);0;127;
4      LSYM   0      0      00000000 92     long int:t(0,3)=r(0,3);-2147483648;2147483647;
5      LSYM   0      0      00000000 139    unsigned int:t(0,4)=r(0,4);0;4294967295;
6      LSYM   0      0      00000000 180    long unsigned int:t(0,5)=r(0,5);0;4294967295;
7      LSYM   0      0      00000000 226    __int128:t(0,6)=r(0,6);0;-1;
8      LSYM   0      0      00000000 255    __int128 unsigned:t(0,7)=r(0,7);0;-1;
9      LSYM   0      0      00000000 293    long long int:t(0,8)=r(0,8);-0;4294967295;
10     LSYM   0      0      00000000 336    long long unsigned int:t(0,9)=r(0,9);0;-1;
11     LSYM   0      0      00000000 379    short int:t(0,10)=r(0,10);-32768;32767;
12     LSYM   0      0      00000000 419    short unsigned int:t(0,11)=r(0,11);0;65535;
13     LSYM   0      0      00000000 463    signed char:t(0,12)=r(0,12);-128;127;
14     LSYM   0      0      00000000 501    unsigned char:t(0,13)=r(0,13);0;255;
15     LSYM   0      0      00000000 538    float:t(0,14)=r(0,1);4;0;
16     LSYM   0      0      00000000 564    double:t(0,15)=r(0,1);8;0;
17     LSYM   0      0      00000000 591    long double:t(0,16)=r(0,1);12;0;
18     LSYM   0      0      00000000 624    _Float32:t(0,17)=r(0,1);4;0;
19     LSYM   0      0      00000000 653    _Float64:t(0,18)=r(0,1);8;0;
20     LSYM   0      0      00000000 682    _Float128:t(0,19)=r(0,1);16;0;
21     LSYM   0      0      00000000 713    _Float32x:t(0,20)=r(0,1);8;0;
22     LSYM   0      0      00000000 743    _Float64x:t(0,21)=r(0,1);12;0;
23     LSYM   0      0      00000000 774    _Decimal32:t(0,22)=r(0,1);4;0;
24     LSYM   0      0      00000000 805    _Decimal64:t(0,23)=r(0,1);8;0;
25     LSYM   0      0      00000000 836    _Decimal128:t(0,24)=r(0,1);16;0;
26     LSYM   0      0      00000000 869    void:t(0,25)=(0,25)
27     FUN    0      0      000004ed 889    add:F(0,1)
28     PSYM   0      0      00000008 900    lhs:p(0,1)
29     PSYM   0      0      0000000c 911    rhs:p(0,1)
30     SLINE  0      8      00000000 0      
31     SLINE  0      9      0000000d 0      
32     SLINE  0      10     00000015 0      
33     GSYM   0      0      00000000 922    glob:G(0,1)
34     FUN    0      0      00000504 934    main:F(0,25)
35     SLINE  0      13     00000000 0      
36     SLINE  0      14     00000012 0      
37     SLINE  0      15     00000019 0      
38     SLINE  0      16     00000020 0      
39     SLINE  0      17     00000031 0      
40     SLINE  0      18     00000033 0      
41     SLINE  0      17     00000044 0      
42     SLINE  0      17     00000048 0      
43     SLINE  0      20     00000051 0      
44     SLINE  0      21     00000052 0      
45     LSYM   0      0      fffffff4 947    i:(0,1)
46     LSYM   0      0      fffffff8 955    test123:(0,1)
47     LSYM   0      0      fffffffc 969    c:(0,1)
48     LBRAC  0      0      00000000 0      
49     RBRAC  0      0      00000057 0      
50     SO     0      0      0000055b 0   

原因定位

.stab节中可以看出局部变量itest123距离栈底的位置。我们反汇编test1程序,发现,i和test123局部变量的地址和stab节中的地址不同。局部变量i的地址距离栈底为-16,test123距离栈底的距离为-12。所以我们通过gdb打印的时候,打印局部变量i的值,结果就会显示局部变量test123的值。

00000504 <main>:
int glob = 400;

void main() {
 504:   55                      push   %ebp
 505:   89 e5                   mov    %esp,%ebp
 507:   53                      push   %ebx
 508:   83 ec 10                sub    $0x10,%esp
 50b:   e8 e0 fe ff ff          call   3f0 <__x86.get_pc_thunk.bx>
 510:   81 c3 cc 1a 00 00       add    $0x1acc,%ebx
    int i = 0;
 516:   c7 45 f0 00 00 00 00    movl   $0x0,-0x10(%ebp)
    int test123 = 200;
 51d:   c7 45 f4 c8 00 00 00    movl   $0xc8,-0xc(%ebp)
    int c = add(i, test123);
 524:   ff 75 f4                pushl  -0xc(%ebp)
 527:   ff 75 f0                pushl  -0x10(%ebp)
 52a:   e8 be ff ff ff          call   4ed <add>
 52f:   83 c4 08                add    $0x8,%esp
 532:   89 45 f8                mov    %eax,-0x8(%ebp)
    for (; i < 300; ++i) {
 535:   eb 15                   jmp    54c <main+0x48>
        glob = glob + i;
 537:   8b 93 2c 00 00 00       mov    0x2c(%ebx),%edx
 53d:   8b 45 f0                mov    -0x10(%ebp),%eax
 540:   01 d0                   add    %edx,%eax
 542:   89 83 2c 00 00 00       mov    %eax,0x2c(%ebx)
    for (; i < 300; ++i) {
 548:   83 45 f0 01             addl   $0x1,-0x10(%ebp)
 54c:   81 7d f0 2b 01 00 00    cmpl   $0x12b,-0x10(%ebp)
 553:   7e e2                   jle    537 <main+0x33>
    }
    return;
 555:   90                      nop
}
 556:   8b 5d fc                mov    -0x4(%ebp),%ebx
 559:   c9                      leave  
 55a:   c3                      ret  
Tags: problems
Share: X (Twitter) Facebook LinkedIn