一、开发流程

1. 编译可执行文件

 1 #include <stdio.h>
2 #include <unistd.h>
3
4 void test()
5 {
6 char * s = "hello world\n";
7 while(1){
8 //int v = 0/0;
9 printf("%s\n", s);
10 sleep(1);
11 }
12 }
13
14 int main()
15 {
16 test();
17 }
2. 生成独立的调试符号文件
gcc -ggdb3 test.c -o test 
3. 删除可执行文件内的调试符号
objcopy --only-keep-debug test test.debug strip --strip-debug --strip-unneeded test 
4. 一般情况
在 ELF 文件内埋桩指定符号文件,gdb 就可以在调试的时候自动加载调试符号了:
objcopy --add-gnu-debuglink=test.debug test
在 ELF文件内也可以看到 .gnu_debuglink 段内保存的调试符号文件。

但是,当我们换了调试环境,或者符号文件路径和 ELF 文件内的路径不匹配的时候,就会报符号找不到的错,这时候仍然需要手动处理符号文件。

二、开始gdb调试

未通过 .gnu_debuglink 指定符号文件时,通过 bt 查看进程的调用栈,我们会看到缺失的符号无法解析。gdb 执行 bt 指令时, 会拿地址信息在已知的符号表内搜索最接近的地址。显示问号,就是说 gdb 完全找不到接近的符号,这时候,我们通过 info symbol [address] 指令也是得不到地址对应的符号信息的。
 
尝试手动加载调试符号文件
gdb 需要知道 [symbol] -> [address] 的映射信息,而 gdb 正好提供了 symbol-file 和 add-symbol-file 指令来手动添加这个信息。因此,我们要做的就是把 .debug 文件内的符号信息告诉 gdb 。先看下 .debug 文件内保存的符号信息:
 
其中 _start 符号是个特殊的符号,一般是 ELF 文件的入口。通过 readelf -h 命令,我们可以看到 ELF 文件的入口地址与 _start 符号地址是相同的。
该地址也是 readelf -S 输出的 .text 段的地址

很明显,这里的地址和 bt trace 里的地址相差甚远,由于操作系统的保护机制(ASLR) ,ELF 文件并不会直接加载到这个地址,而是会加载到一个随机地址上。因此,我们需要找到这个地址,并把符号表内所有符号的地址都加上这个偏移量(gdb 应该也是可以自动完成这个操作的?)

找到 .text 段加载的位置:

通过 cat /proc/[pid]/maps 我们可以获得进程内存镜像内所有段信息,其中以 ELF 文件名命名并且带有可执行权限的段,就是该 ELF 文件 .text 段加载的地址,这也是我们希望 gdb: info address _start 指令得到的地址。
 

gdb 加载调试符号:

由于我们的目标是让 gdb 解析 .text 段地址为 addr(.text) , 也就是这里的 0x56266ea06000, 而当前符号文件内的 .text 段地址是 addr(elf.entry_point),也就是 0x1060,所以我们偏移量的计算方法是:
addr(.text) - addr(elf.entry_point)
添加符号文件指令: symbol-file xxx.debug -o offset
给个调试符号文件,给这个调试文件一个偏移地址,gdb加载符号文件的时候,会自动把符号对应的地址都加上这个偏移量。
ok, 符号解析出来了,查看对应内存上的数据和 ELF文件内的数据是对的上的。
 
看下 trace,诡异,仍然不对...

detach & attach 后一切正常了,似乎是 gdb 的一个bug,先attach后加载符号文件会有问题。

 
如果需要解析 ELF 的其它 section 同理 ... 似乎写个脚本自动处理更好~

三、动态加载的SO添加符号文件:

代码:

 1 #include <stdio.h>
2 #include <unistd.h>
3 #include "test.h"
4
5 static void test()
6 {
7 char * s = "hello so\n";
8 while(1){
9 printf("%s\n", s);
10 sleep(1);
11 }
12 }
13
14 void test_so()
15 {
16 test();
17 }

1 #include "test.h"
2
3 int main()
4 {
5 test_so();
6 }

编译 & 链接 & strip:

gcc test.c -ggdb3 -shared -fPIC -o libtest.so
gcc test.c -ggdb3 -shared -o libtest.so
objcopy --only-keep-debug libtest.so libtest.so.debug
strip --strip-debug --strip-unneeded libtest.so 

开始加载调试符号:

我最开始是按照上面主程序的方法,从 /proc/pid/maps 找到动态库加载的地址来计算偏移量的,但是失败了,符号解析的地址和内存内的地址总是差几十个字节。
最后发现,通过 gdb info shared 获得的链接库地址和 maps 内的地址有点不同:
用这个地址计算得到的符号文件偏移量是正确的。
/proc/pid/maps 提示 .text 段在 0x7f931b532000,info shared 提示动态库被加载的地址是 0x7f931b532060,看下这部分内存放的到底是啥。.text 段确实加载到了 0x7f931b532060,那么在这之间的内存放的是什么呢?其实,从名字上也应该猜出来了,应该是链接的时候添加的 plt 相关的处理代码。
 

googles 上有好多处理这种自动计算符号文件偏移量的代码,例如
https://stackoverflow.com/questions/20380204/how-to-load-multiple-symbol-files-in-gdb。但是我试了下,在我的环境下都不太可行。这些代码,有的只拿 elf 文件的 .text 段地址来计算,有的只拿进程内的段加载地址,很明显和我的环境都不搭。这个可能是和 gdb 实现的具体逻辑有关?毕竟 gdb 是能够获取这些地址信息并自动帮我们处理的。

总结:

gdb 单独指定符号文件时,原则是要 ELF 文件的 .text 段和进程镜像里的 .text 段加载地址匹配上,需要根据具体环境计算相关的偏移量,不可盲信 google 上的相关代码啊~

参考:

 

GDB 调试 - 正确地加载调试符号文件的更多相关文章

  1. ASP.NET MVC3 Razor 调试与预加载

    目录(?)[-] 获取服务器信息 FormsAuthenticationSlidingExpiration 属性 MVC3预加载   在ASP.NET MVC3开发中,调试中怎么也是不可缺少的,那对于 ...

  2. 提示“应用程序无法启动,因为应用程序的并行配置不正确”不能加载 System.Data.SQLite.dll

    新版本SQLITE,如果下载Precompiled Binaries版会出现提示“应用程序无法启动,因为应用程序的并行配置不正确”不能加载 System.Data.SQLite.dll. 下载Prec ...

  3. Android 多种方式正确的加载图像,有效避免oom

    图像加载的方式: Android开发中消耗内存较多一般都是在图像上面,本文就主要介绍怎样正确的展现图像减少对内存的开销,有效的避免oom现象.首先我们知道我的获取图像的来源一般有三种源头:1.从网络加 ...

  4. [原]排错实战——解救加载调试符号失败的IDA

    原调试IDA排错troubleshootsymbolspdbsysinternalprocess monitor 缘起 最近想借助IDA逆向一个函数.在windows下,调试器(比如vs, windb ...

  5. u-boot移植总结(二)LED点灯调试 和 u-boot加载地址

    (一)LED点灯调试 FL2440电路总共有4个LED0,LED1,LED2,LED3,分别接到板子GPB5,GPB6,GPB8,GPB10引脚.通过设置三个寄存器GPBCON(0x56000010) ...

  6. Windows下pycharm远程连接服务器调试-tensorflow无法加载问题

    最近打算在win系统下使用pycharm开发程序,并远程连接服务器调试程序,其中在import tensorflow时报错如图所示(在远程服务器中执行程序正常): 直观错误为: ImportError ...

  7. 【Atheros】内核调试及网卡加载等问题小结

    我做的其他很多工作就比较有针对性了,不是什么大众性的问题,比如加统计代码.实现自己的速率调整算法或者加一些自己的控制什么的,就不再单独介绍了,最后呢再罗列一些小问题,供参考. 1. 加载模块(执行wi ...

  8. GDB调试工具、动态加载、内存管理(day04)

    一.程序中的错误处理 在系统中定义了一个全局变量errno.在这个全局变量中存放着系统调用或者库函数出错的信息(错误编号).然后根据错误编号获取错误信息. 举例说明: 打开一个文件,如果这个文件不存在 ...

  9. [转]IE9.0或者360下js(JavaScript、jQuery)不能正确执行(加载),按F12后执行正常;Firefox下ajax的success返回数据data(json、string)无法获取

    兼容问题1: 页面的分享等插件加载不全,并无法点击. 兼容问题2: IE下页面选择器(#id..class.etc.)绑定click事件无法访问到,后台springmvc方法,也无法获取ajax的su ...

  10. 如何正确的加载和执行 JavaScript 代码

    无论当前 JavaScript 代码是内嵌还是在外链文件中,页面的下载和渲染都必须停下来等待脚本执行完成.JavaScript 执行过程耗时越久,浏览器等待响应用户输入的时间就越长.浏览器在下载和执行 ...

随机推荐

  1. 修改 npm 全局模块及模块缓存存放位置

    说明 npm 全局模块安装将包安装到 C:\Users\%user%\AppData\Roaming\npm 目录下,缓存则放于 C:\Users\%user%\AppData\Roaming\npm ...

  2. kali 子域名搜集工具学习记录

    # fierce 主要是对子域名进行扫描和收集信息,并且它还可以测试区域传送漏洞. kali 2022.1 apt-get 安装的fierce并没有 -dns参数,需要手动下载,下载后有个fierce ...

  3. 【Ubuntu】Ubuntu 技巧集锦

    『Ubuntu 22.04 国内镜像 阿里云/163源/清华大学/中科大』 『各种 Proxy 设置 GUI/Terminal/APT』 『设置 wget Proxy』 『设置右键菜单-新建文档』 『 ...

  4. MYSQL5.7索引异常引发的锁超时处理记录

    原始sql: update a set a.x=x where a.xid in (select b.xid from b inner join c on b.xxx = c.xxx) and a.x ...

  5. 字符串散列成GUID

    https://stackoverflow.com/questions/2190890/how-can-i-generate-a-guid-for-a-string

  6. 记一次mysql5.7保存Emoji表情

    1.错误:SQLException; SQL state [HY000]; error code [1366]; Incorrect string value: '\xF0\x9F\x90\x96 \ ...

  7. Vuex4.x 简介及state、getters、mutations、actions详解(OptionApi 和 CompositionApi)

    http://t.zoukankan.com/yaopengfei-p-15439203.html 作       者 : Yaopengfei(姚鹏飞) 博客地址 : http://www.cnbl ...

  8. 钉钉机器人webhook的使用

    1.群启动webhook机器人:右上角设置--智能助手---添加机器人---其他机器人 2.获取webhook地址 3.调用方式不一样,则访问方式就不一样 ---------------------- ...

  9. zTree异步获取,默认展开一级节点

    zTree官网 https://treejs.cn/v3/api.php 涉及到zTree的异步获取 这里主要是在setting部分的设置: setting: { ..., async: {enabl ...

  10. PAT-basic-1022 D进制的A+B java

    一.题目 输入两个非负 10 进制整数 A 和 B (≤230−1),输出 A+B 的 D (1<D≤10)进制数. 输入格式: 输入在一行中依次给出 3 个整数 A.B 和 D. 输出格式: ...