分享

GDB调试二进制和符号表symbol分开的程序

 点点阅 2020-06-09

GDB支持将程序调试信息放在独立的文件里,与可执行程序分离,其可以自动查找和自动加载调试信息。
由于调试信息比较大,甚至比可执行程序还要大,通常将可执行程序的调试信息以单独文件的形式发布,需要调试时可以再安装这些文件。

GDB支持两种设置单独调试信息文件的方式:
第一种,可执行程序含调试链接,该链接指定单独的调试信息文件名。单独调试文件名通常是executable.debug,executable是相应的可执行程序名,
不带路径(比如:ls.debug是/usr/bin/ls的调试信息文件)。
此外,调试链接为调试文件设置了CRC32的校验和,GDB用此校验和来确保可执行文件和调试文件是同一个版本的。CRC可能校验失败,可以参考下面单独挂载方法。
第二种,可执行文件含版本ID号和唯一的bit串,而相应的调试信息文件也包含该bit串。该方式只在某些系统上支持,特别是那些在二进制文件里使用ELF格式和GNU Binutils的系统。更多关于此功能的细节,可以参见–build-id命令行选项的介绍,在GNU连接器“命令行选项”节中。
虽然版本ID号没有直接指出调试信息文件名,但是可以从版本ID号里计算出来,参见下面。

GDB也提供两种不同的方式查找调试信息文件:
第一种,对于调试链接方式,GDB在可执行文件的目录里查找对应名字的文件,接着在此目录下的子目录.debug下查找,最后在全局调试目录下的一个子目录里查找,此子目录的名字和可执行文件的绝对文件名的先导目录名相同。
第二种,对于版本ID方式,GDB在全局调试目录下的.build-id子目录下查找名为nn/nnnnnnnn.debug的文件,这里nn是版本ID字符串的头两个16进制字符,nnnnnnnn是余下的字符。真正的版本ID字符串是32个,或更多的16进制字符,不是10个。
举个例子,假设要调试/usr/bin/ls,此程序有个调试链接指定了调试文件ls.debug,且其版本ID是是16进制的abcdef1234。如果全局调试目录是/usr/lib/debug,那么GDB按顺序查找下列调试信息文件:

点击(此处)折叠或打开

  1. /usr/lib/debug/.build-id/ab/cdef1234.debug
  2. /usr/bin/ls.debug
  3. /usr/bin/.debug/ls.debug
  4. /usr/lib/debug/usr/bin/ls.debug

同时可以设置全局调试信息目录的名称,并查看当前GDB所使用的名称。

点击(此处)折叠或打开

  1. set debug-file-directory directory // 将directory设置为GDB搜索单独调试信息文件的目录。
  2. show debug-file-directory          // 显示搜索单独调试信息文件的目录。

Gdb 调试与符号表分离的二进制程序
通常当没有在程序spec中明确指定不进行strip时,缺省打rpm包都会把二进制程序或动态库的符号表等debuginfo信息与执行程序分离,生成一个debuginfo的 rpm包。比如localagent打rpm包时,会生成如下3个rpm 包:

点击(此处)折叠或打开

  1. local_agent-0.7.1-rc_1.x86_64.rpm
  2. local_agent-debuginfo-0.7.1-rc_1.x86_64.rpm
  3. local_agent-devel-0.7.1-rc_1.x86_64.rpm

将local_agent-debuginfo-0.7.1-rc_1.x86_64.rpm解压,可以看到相应的debug info文件。
点击(此处)折叠或打开

  1. [tmp]$ rpm2cpio local_agent-debuginfo-0.7.1-rc_1.x86_64.rpm |cpio –idv
  2. ./usr/local/bin/.debug/local_agent_client.debug
  3. ./usr/local/bin/.debug/local_agent_server.debug
  4. ./usr/local/lib64/.debug/liblocal_agent.so.debug
  5. ./usr/src/debug/local_agent-0.7.1
  6. ./usr/src/debug/local_agent-0.7.1/build
  7. ./usr/src/debug/local_agent-0.7.1/build/release64
  8. ./usr/src/debug/local_agent-0.7.1/build/release64/local_agent
  9. ./usr/src/debug/local_agent-0.7.1/build/release64/local_agent/ClientParamParser.cpp
  10. .......
  11. ./usr/src/debug/local_agent-0.7.1/build/release64/local_agent/local_agent.pb.cc
  12. .......

对于符号表与二进程序分离的程序,该如何调试呢?  
点击(此处)折叠或打开

  1. [local]$ gdb bin/local_agent_server
  2. GNU gdb Fedora (6.8-37.el5)
  3. Copyright (C) 2008 Free Software Foundation, Inc.
  4. License GPLv3+: GNU GPL version 3 or later <http:///licenses/gpl.html>
  5. This is free software: you are free to change and redistribute it.
  6. There is NO WARRANTY, to the extent permitted by law. Type "show copying" and "show warranty" for details.
  7. This GDB was configured as "x86_64-redhat-linux-gnu"...
  8. (no debugging symbols found)
  9. (gdb) b main
  10. Breakpoint 1 at 0x401a80
  11. (gdb) r
  12. Starting program: /home/admin/tmp/usr/local/bin/local_agent_server
  13. [Thread debugging using libthread_db enabled]
  14. [New Thread 0x2ad3e6935860 (LWP 27869)]
  15. Breakpoint 1, 0x0000000000401a80 in main ()
  16. (gdb) bt
  17. #0 0x0000000000401a80 in main ()
  18. (gdb)
  19. .......

可以看出,由于gdb没有符号表,所以显示的都是二进制地址。

方法一:gdb 启动时通过 –s 指定
当gdb启动时,通过–s指定符号表文件来解决。如下:
点击(此处)折叠或打开

  1. [ local]$ gdb -e bin/local_agent_server -s debug/local_agent_server.debug
  2. GNU gdb Fedora (6.8-37.el5)
  3. Copyright (C) 2008 Free Software Foundation, Inc.
  4. License GPLv3+: GNU GPL version 3 or later <http:///licenses/gpl.html>
  5. This is free software: you are free to change and redistribute it.
  6. There is NO WARRANTY, to the extent permitted by law. Type "show copying"
  7. and "show warranty" for details.
  8. This GDB was configured as "x86_64-redhat-linux-gnu"...
  9. (gdb) b main
  10. Breakpoint 1 at 0x401a80: file build/release64/local_agent/LocalAgentMain.cpp, line 34.
  11. (gdb) bt
  12. No stack.
  13. (gdb) r
  14. Starting program: /home/admin/tmp/usr/local/bin/local_agent_server
  15. [Thread debugging using libthread_db enabled]
  16. [New Thread 0x2af3399a7860 (LWP 27862)]
  17. Breakpoint 1, main (argc=1, argv=0x7fff5728f0b8) at build/release64/local_agent/LocalAgentMain.cpp:34 
  18. 34 build/release64/local_agent/LocalAgentMain.cpp: No such file or directory.
  19. in build/release64/local_agent/LocalAgentMain.cpp
  20. (gdb) quit

如果是符号表与二进程序分离的程序进行所产生的core,可以通过以下方式调试:
gdb -c core.1234 -e bin/local_agent_server -s debug/local_agent_server.debug
其中:
-c 指定core文件
-e 指定binary,用线上的binary即可
-s 指定符号表,也就是我们新生成的符号表

方法二:将.debug 目录放到可执行程序所在目录
除了通过–s指定debuginfo文件外,还可以将.debug目录复制到可执行程序所在目录或者将local_agent_server.debug复制到可执行程序所在的目录。
点击(此处)折叠或打开

  1. [admin@s002182.cm6 local]$ pwd /home/admin/tmp/usr/local
  2. [admin@s002182.cm6 local]$ cp -r debug/ bin/.debug
  3. [admin@s002182.cm6 local]$ ls -lha bin/
  4. total 44K 
  5. drwxr-xr-x 3 admin admin 4.0K Mar 21 18:41 .
  6. drwxr-xr-x 6 admin admin 4.0K Mar 21 17:47 ..
  7. drwxr-xr-x 3 admin admin 4.0K Mar 21 18:49 .debug
  8. -rwxr-xr-x 1 admin admin 14K Mar 21 17:44 local_agent_client
  9. -rwxr-xr-x 1 admin admin 14K Mar 21 17:44 local_agent_server

此时,可以看到符号表了。

点击(此处)折叠或打开

  1. [local]$ gdb bin/local_agent_server
  2. GNU gdb Fedora (6.8-37.el5) 
  3. Copyright (C) 2008 Free Software Foundation, Inc.
  4. License GPLv3+: GNU GPL version 3 or later <http:///licenses/gpl.html> 
  5. This is free software: you are free to change and redistribute it. 
  6. There is NO WARRANTY, to the extent permitted by law. Type "show copying" 
  7. and "show warranty" for details. 
  8. This GDB was configured as "x86_64-redhat-linux-gnu"... 
  9. (gdb) b main 
  10. Breakpoint 1 at 0x401a80: file build/release64/local_agent/LocalAgentMain.cpp, line 34. 
  11. (gdb) q

set debug-file-directory 方式没有生效
尝试用set debug-file-directory方式来设置debug路径,目前看还没有生效,需要再确定一下。
点击(此处)折叠或打开

  1. [admin@s002182.cm6 local]$ gdb bin/local_agent_server
  2.  
  3. GNU gdb Fedora (6.8-37.el5)
  4. Copyright (C) 2008 Free Software Foundation, Inc.
  5. License GPLv3+: GNU GPL version 3 or later <http:///licenses/gpl.html>
  6. This is free software: you are free to change and redistribute it.
  7. There is NO WARRANTY, to the extent permitted by law. Type "show copying 
  8. and "show warranty" for details.
  9. This GDB was configured as "x86_64-redhat-linux-gnu"... 
  10. (no debugging symbols found) 
  11. (gdb) b main
  12. Breakpoint 1 at 0x401a80
  13. (gdb) show debug-file-directory
  14. The directory where separate debug symbols are searched for is "/usr/lib/debug".
  15. (gdb) set debug-file-directory /usr/lib/debug:/home/admin/tmp/usr/local/debug
  16. (gdb) show debug-file-directory
  17. The directory where separate debug symbols are searched for is "/usr/lib/debug:/home/admin/tmp/usr/local/debug

gdb 调试与符号表分离的动态库
当二进制程序所依赖的动态库是符号表等调试信息与动态库二进制分离的时,该如何让 gdb 加载动态库的符号表呢?

可执行程序运行时,依赖的动态库是通过 rpm 包安装的情况
比较简单,只要将debuginfo的rpm包将通过rpm安装即可,因为.debug目录缺省会安装在动态库所在的目录下。
下面的库的rpm包解压后,可以清楚的看到这一点:

点击(此处)折叠或打开

  1. [admin@s002182.cm6 tmp]$ rpm2cpio anet-1.3.2-rc_2.x86_64.rpm |cpio -idv
  2. ./usr/local/lib64/libanet.so
  3. ./usr/local/lib64/libanet.so.3
  4. ./usr/local/lib64/libanet.so.3.0.0
  5. 563 blocks
  6.  
  7. [admin@s002182.cm6 tmp]$ rpm2cpio anet-debuginfo-1.3.2-rc_2.x86_64.rpm |cpio -idv
  8. ./usr/local/lib64/.debug/libanet.so.3.0.0.debug
  9. ./usr/src/debug/anet-1.3.2
  10. ./usr/src/debug/anet-1.3.2/anet
  11. ./usr/src/debug/anet-1.3.2/anet/addrspec.h
  12. ./usr/src/debug/anet-1.3.2/anet/adminclient.h

可执行程序运行时,依赖的动态库是通过rpm2cipo解压到某个目录的情况
如果可执行程序运行时,依赖的动态库是用户自已通过rpm2cpio解压后复制到某个用户自定义的目录的,那么,拿到该动态库的debuginfo rpm后,
相应的用 rpm2cpio 解压,并把.debug目录复制到相应的动态库所在的目录。
比如以下示例,sap_server依赖libarpc.so.1,但其debuginfo信息分离在单独的debuginfo rpm包中的,所以导致gdb core时,看不到libarpc.so.1的符号表。
点击(此处)折叠或打开

  1. gdb bin/sap_server_d core.17131
  2. (gdb) bt
  3. #0 0x0000003959230265 in raise (sig=<value optimized out>) at ../nptl/sysdeps/unix/sysv/linux/raise.c:64
  4. #1 0x0000003959231d10 in abort () at abort.c:88
  5. #2 0x000000395d6bec44 in __gnu_cxx::__verbose_terminate_handler () at ../../../../libstdc++-v3/libsupc++/vterminate.cc:97
  6. #3 0x000000395d6bcdb6 in __cxxabiv1::__terminate (handler=<value optimized out>) at ../../../../libstdc++-v3/libsupc++/eh_terminate.cc:4
  7. #4 0x000000395d6bcde3 in std::terminate () at ../../../../libstdc++-v3/libsupc++/eh_terminate.cc:53
  8. #5 0x000000395d6bd2ef in __cxa_pure_virtual () at ../../../../libstdc++-v3/libsupc++/pure.cc:5 
  9. #6 0x00002b8729bfc933 in arpc::ClientPacketHandler::handlePacket () from /home/admin/sap/lib/libarpc.so.1 
  10. #7 0x00002b872a0ddcd8 in anet::Connection::handlePacket () from /home/admin/sap/lib/libanet.so.3
  11. #8 0x00002b872a0e75fb in anet::TCPConnection::readData () from /home/admin/sap/lib/libanet.so.3
  12. #9 0x00002b872a0e624b in anet::TCPComponent::handleReadEvent () from /home/admin/sap/lib/libanet.so.3
  13. #10 0x00002b872a0e8c0b in anet::Transport::eventIteration () from /home/admin/sap/lib/libanet.so.3
  14. #11 0x00002b872a0e8cf2 in anet::Transport::eventLoop () from /home/admin/sap/lib/libanet.so.3
  15. #12 0x00002b872a0e8d47 in anet::Transport::run () from /home/admin/sap/lib/libanet.so.3
  16. #13 0x00002b872a0ea02d in anet::Thread::hook () from /home/admin/sap/lib/libanet.so.3
  17. #14 0x0000003959e064a7 in start_thread (arg=<value optimized out>) at pthread_create.c:297
  18. #15 0x00000039592d3c2d in clone () from /lib64/libc.so.6
  19. (gdb) thread 2

要让gdb调试core时,能显示动态库的符号表,可以用如下方法:
a). 先解压debuginfo  rpm包
rpm2cpio arpc-debuginfo-0.14.1-rc_1.x86_64.rpm |cipo -idv
b). 将 ./usr/local/lib64/.debug复制到 libarpc.so.1所在目录:
/home/admin/sap/lib/  cp -r  ./usr/local/lib64/.debug  /home/admin/sap/lib/
点击(此处)折叠或打开

  1. [admin@s007238.cm6 sap]$ gdb bin/sap_server_d core.17131
  2. GNU gdb Fedora (6.8-37.el5)
  3. Copyright (C) 2008 Free Software Foundation, Inc.
  4. License GPLv3+: GNU GPL version 3 or later <http:///licenses/gpl.html>
  5. This is free software: you are free to change and redistribute it.
  6. There is NO WARRANTY, to the extent permitted by law. Type "show copying" and "show warranty" for details.
  7. This GDB was configured as "x86_64-redhat-linux-gnu"...
  8. Reading symbols from /home/admin/sap/lib/libhb_node.so...done.
  9. Loaded symbols for /home/admin/sap/lib/libhb_node.so
  10. Reading symbols from /home/admin/sap/lib/libcm_basic.so...done.
  11. Loaded symbols for /home/admin/sap/lib/libcm_basic.so
  12. Reading symbols from /home/admin/sap/lib/libarpc.so.1...Reading symbols from /home/admin/sap/lib/.debug/libarpc.so.1.0.0.debug...done.
  13. ... ...
  14. #0 0x0000003959230265 in raise (sig=<value optimized out>) at ../nptl/sysdeps/unix/sysv/linux/raise.c:64
  15. 64 return INLINE_SYSCALL (tgkill, 3, pid, selftid, sig);
  16. (gdb) bt
  17. #0 0x0000003959230265 in raise (sig=<value optimized out>) at ../nptl/sysdeps/unix/sysv/linux/raise.c:64
  18. #1 0x0000003959231d10 in abort () at abort.c:88
  19. #2 0x000000395d6bec44 in __gnu_cxx::__verbose_terminate_handler () at ../../../../libstdc++-v3/libsupc++/vterminate.cc:97
  20. #3 0x000000395d6bcdb6 in __cxxabiv1::__terminate (handler=<value optimized out>) at ../../../../libstdc++-v3/libsupc++/eh_terminate.cc:4#4 0x000000395d6bcde3 in std::terminate () at ../../../../libstdc++-v3/libsupc++/eh_terminate.cc:53
  21. #5 0x000000395d6bd2ef in __cxa_pure_virtual () at ../../../../libstdc++-v3/libsupc++/pure.cc:55
  22. #6 0x00002b8729bfc933 in arpc::ClientPacketHandler::handlePacket (this=0x2aac6df6ee38, packet=0x2aac6df6fc30, args=<value optimized out>) at build/release64/arpc/ClientPacketHandler.cpp:35
  23. #7 0x00002b872a0ddcd8 in anet::Connection::handlePacket () from /home/admin/sap/lib/libanet.so.3
  24. #8 0x00002b872a0e75fb in anet::TCPConnection::readData () from /home/admin/sap/lib/libanet.so.3
  25. #9 0x00002b872a0e624b in anet::TCPComponent::handleReadEvent () from /home/admin/sap/lib/libanet.so.3
  26. #10 0x00002b872a0e8c0b in anet::Transport::eventIteration () from /home/admin/sap/lib/libanet.so.3
  27. #11 0x00002b872a0e8cf2 in anet::Transport::eventLoop () from /home/admin/sap/lib/libanet.so.3
  28. #12 0x00002b872a0e8d47 in anet::Transport::run () from /home/admin/sap/lib/libanet.so.3
  29. #13 0x00002b872a0ea02d in anet::Thread::hook () from /home/admin/sap/lib/libanet.so.3
  30. #14 0x0000003959e064a7 in start_thread (arg=<value optimized out>) at pthread_create.c:297
  31. #15 0x00000039592d3c2d in clone () from /lib64/libc.so.6
  32. Current language: auto; currently c
  33. (gdb)

 

单独挂载方法:

对于某些so,可能出现CRC校验失败的情况,这时可以用尝试add-symbol-file命令直接挂载,方法如下

#gdb -p PID

(gdb)add-symbol-file FILE ADDR [-s <SECT> <SECT_ADDR> -s <SECT> <SECT_ADDR> ...]
Load the symbols from FILE, assuming FILE has been dynamically loaded.
ADDR is the starting address of the file's text.
The optional arguments are section-name section-address pairs and
should be specified if the data and bss segments are not contiguous
with the text.  SECT is a section name to be loaded at SECT_ADDR.

FILE:编译好的带调试信息的debug文件
ADDR:代码段的起始地址
如何计算代码段的起始地址:
1、使用objdump -h libxx.so  |grep text或者 readelf -S libxx.so  .text 表示代码段
Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            00000000 000000 000000 00      0   0  0
  [ 1] .hash             HASH            000000b4 0000b4 0011b4 04   A  2   0  4
  [ 2] .dynsym           DYNSYM          00001268 001268 002620 10   A  3  17  4
  [ 3] .dynstr           STRTAB          00003888 003888 001ed2 00   A  0   0  1
  [ 4] .gnu.version      VERSYM          0000575a 00575a 0004c4 02   A  2   0  2
  [ 5] .gnu.version_r    VERNEED         00005c20 005c20 000090 00   A  3   2  4
  [ 6] .rel.dyn          REL             00005cb0 005cb0 000160 08   A  2   0  4
  [ 7] .rel.plt          REL             00005e10 005e10 0009c0 08   A  2   9  4
  [ 8] .init             PROGBITS        000067d0 0067d0 000017 00  AX  0   0  4
  [ 9] .plt              PROGBITS        000067e8 0067e8 001390 04  AX  0   0  4
  [10] .text             PROGBITS        00007b78 007b78 01f32c 00  AX  0   0  4
  [11] .fini             PROGBITS        00026ea4 026ea4 00001b 00  AX  0   0  4
  [12] .rodata           PROGBITS        00026ec0 026ec0 003b48 00   A  0   0 32
  [13] .eh_frame_hdr     PROGBITS        0002aa08 02aa08 00002c 00   A  0   0  4
  [14] .eh_frame         PROGBITS        0002aa34 02aa34 00010c 00   A  0   0  4
  [15] .data             PROGBITS        0002b000 02b000 0052a8 00  WA  0   0 32
  [16] .dynamic          DYNAMIC         000302a8 0302a8 000100 08  WA  3   0  4
  [17] .ctors            PROGBITS        000303a8 0303a8 000008 00  WA  0   0  4
  [18] .dtors            PROGBITS        000303b0 0303b0 000008 00  WA  0   0  4
  [19] .jcr              PROGBITS        000303b8 0303b8 000004 00  WA  0   0  4
  [20] .got              PROGBITS        000303bc 0303bc 000554 04  WA  0   0  4
  [21] .bss              NOBITS          00030920 030920 000228 00  WA  0   0 32
  [22] .comment          PROGBITS        00000000 030920 00092a 00      0   0  1
  [23] .shstrtab         STRTAB          00000000 03124a 0000b6 00      0   0  1

Addr表示该块起始地址相对于文件起始地址的偏移

2、查看模块在进程空间中的起始地址
[sangfor]# cat /proc/9300/maps | grep libxx
2ae93000-2aebe000 r-xp 00000000 03:02 259        /usr/lib/libxx.so   <---带x的是代码段
2aebe000-2aec4000 rw-p 0002b000 03:02 259        /usr/lib/libxx.so

2ae93000是文件libxx.so的起始地址
所以代码段的真实地址是2ae93000 + 00007b78 = ADDR

add-symbol-file path/libxx.so ADDR 
这样代码段的符号信息就被加载进去了,现在可以正常设置断点

(gdb) info thread

(gdb)t threadID-----想要查看的线程ID

(gdb)f xxx------想要查看的层级

(gdb)list ----该层级附近的代码

(gdb)b xxx---设置断点

3、如果需要访问代码段之外数据可以通过 -s指定其他块的地址,比如指定data段地址
add-symbol-file path/libxx.so ADDR  -s (2ae93000+0002b000)

 

参考
http:///text/2010/01/gdb%E6%89%8B%E5%86%8C15gdb%E6%96%87%E4%BB%B6/
http://searchwiki.taobao./index.php/%E7%BA%BF%E4%B8%8Aisearch%E8%B0%83%E8%AF%95%E6%96%B9%E6%B3%95

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多