我们在利用gdb调试时通常需要符号表才可以方便地打断点、查看详细变量、位置信息。为了获取对应的可执行文件/动态库的符号表,我们在编译时必须加上-g参数生成对应的符号信息。

 通常,我们有两种方式获取对应的符号。下面以实例进行说明。

main.c文件:

     1  #include <stdio.h>
     2  #include <stdlib.h>
     3  #include <unistd.h>
     4
     5  #include "SendMessage.h"
     6
     7  void CreateMem(void)
     8  {
     9          int *piData = NULL;
    10
    11          piData = malloc(9 * sizeof(int));
    12
    13          printf("CreateMme piData:%p\n", piData);
    14
    15          piData[9] = 0x10;
    16
    17          return;
    18  }
    19
    20  int main(void)
    21  {
    22          CreateMem();
    23
    24          StartSendMessage();
    25
    26          while (1)
    27          {
    28                  sleep(50);
    29          }
    30
    31          return 0;
    32  }

main.c调用了SendMessage.c文件,如下:

     1  #include <stdio.h>
     2  #include <stdlib.h>
     3
     4  void StartSendMessage(void)
     5  {
     6          int a = 1;
     7          int b = 3;
     8
     9          printf("Start send message---%d.\n", a + b);
    10
    11          return;
    12  }

一、分离独立符号表方法

1. 首先将SendMessage.c编译成带符号信息的动态库libSendMessage.so

gcc -o libSendMessage.so -fPIC -shared -g SendMessage.c

2. 利用objcopy命令从带符号信息的动态库libSendMessage.so中分理出单独的符号表libSendMessage.so.debug

objcopy --only-keep-debug libSendMessage.so libSendMessage.so.debug

在当前进程执行目录下创建.debug文件夹。将符号表libSendMessage.so.debug挪到.debug文件夹下。

注意,符号表可以放在三个路径下,(假设当前进程执行路径为/home/TestDemon)

  • /home/TestDemon,即进程的执行路径
  • /home/TestDemon/.debug/,即进程执行路径下的.debug文件夹
  • /usr/lib/debug/home/TestDemon,即通用查找符号表路径/usr/loacal/debug下的和进程执行路径的同名路径下。

gdb也是按照上面顺序依次查找符号表文件,因此我们可以把我们的符号表libSendMessage.so.debug文件放在其中任何一个。

另外在gdb调试过程中我们可以直接通过设置debug-file-directory变量值来设置符号表查找路径。命令如下;(假设我们把符号表放在了/home/TestDemon/symbol路径下)

set debug-file-directory /home/TestDemon/symbol

3. 利用strip命令剔除libSendMessage.so的符号信息生成不带符号的动态库libSendMessage_strip.so

strip libSendMessage.so -o libSendMessage_strip.so

4. 建立调试信息符号表和不带符号信息动态库之间的链接

objcopy --add-gnu-debuglink=./.debug/libSendMessage.so.debug libSendMessage_strip.so

5. 查看是否链接成功

objdump -s -j .gnu_debuglink libSendMessage_strip.so

 6. 利用不带符号信息的动态库生成最终的可执行文件

gcc -o test main.c -L ./ -lSendMessage_strip -Wl,-rpath=./

7. 启动gdb进行调试

显示栈回溯、单步调试、展示函数、显示局部变量值等均正常。

 从上可知,符号表可正常读取。

二、未strip动态库符号表

我们也可以直接利用没有strip的动态库当做符号表使用。

1. 编译SendMessage.c生成带符号的动态库libSendMessage.so。

2. 利用strip剥离libSendMessage.so生成不带符号的动态库libSendMessage_strip.so。

3. 编译main.c生成最终的可执行程序test

gcc -o test main.c -L ./ -lSendMessage_strip -Wl,-rpath=/home/TestDemon/

注意:为了后面方便构建符号表路径,-rpath尽量使用绝对路径。

4. 利用ldd查看test依赖的动态库及对应的路径

 从上面可知,test依赖的动态库libSendMessage_strip.so链接的路径为/home/TestDemon/下,因此我们下一步构造符号表时libSendMessage.so务必保存在对应的home/TestDemon路径下。

5. 利用未strip的动态库构造符号表

为了创建一致的路径,在当前路径下递归创建目录home/TestDemon。将未strip的动态库libSendMessage.so拷贝到home/TestDemon下,并改名为libSendMessage_strip.so。

 6. 启动gdb调试

利用set solib-absolute-prefix设置系统的根目录,这样后面其他绝对路径下的动态库符号文件的查找即基于该目录查找。

 可以正常显示栈回溯、单独调试、展示局部变量值等。

 

 从上面可知,上述符号表可正常使用。

Logo

更多推荐