GDB如何从Coredump文件恢复动态库信息

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了GDB如何从Coredump文件恢复动态库信息相关的知识,希望对你有一定的参考价值。

查看 error log:

我们拿到了崩溃位置0xee36f1,如何找到与之相对的代码位置呢?

找台测试机,获取对应版本的安装包:

解压:

然后用 GDB 打开 mysqld:

在 0xee36f1 位置打一个断点:

我们可以看到,gdb 将崩溃位置的文件名和行号都打印出来,

剩下的事情,就可以交给开发工程师,按照这个崩溃堆栈来进行问题排查。

赠送章节

红框内的这串信息是什么?我们来解开看一下,

这段信息分为两段,"+0x71" 是一个偏移量,前面是一串文字,我们将文字解析出来:

可以看到前面这串文字是一个函数签名的编码,用 c++filt 还原编码以后,可以看到完整的函数签名。

红框内的这串信息的意思就是崩溃位置是 一个函数起始位置 + 偏移量。

我们大概可以猜到,这个 MySQL 的缺陷是在为 binlog 产生新的文件名时发生的。

小贴士:

函数起始位置 + 偏移量 是一种内存位置的表示方法,但该位置不一定是这个函数内的代码。

以本例来说,0xee36f1 这个位置,程序找到了就近的函数 generate_new_name 的起始位置,计算出有 0x71 这么多偏移,就表示成了 generate_new_name+0x71 这种形式。

但 0xee36f1 这个位置的代码,大概率是,但,不一定是 generate_new_name 这个函数内部的一段代码。

参考技术A 对于如何恢复动态链接库信息,我们需要关注的是post_create_inferior函数。在这个函数里,如果在core指令之前已执行了file或exec_file命令,即已拥有了主执行程序的信息,那么就会调用solib_add来添加所有的so库。
可见,恢复动态链接库信息的前提是必须拥有Coredump文件和原始主执行程序的Binary文件,如果只有其中一个,是不能恢复动态链接库信息的。
继续看solib_add函数,它主要调用update_solib_list来更新所有的so库列表,在update_solib_list函数里,关键的地方是调用ops->current_sos函数来获取so库信息列表,而current_sos函数总是根据当前信息重建so库列表。
在不同的操作系统和体系架构上,会有不同的current_sos实现。对于工程中通常使用的ARM指令和MIPS指令上的Linux系统,会由svr4_current_sos函数来实现重建功能。
进入svr4_current_sos函数,首先调用locate_base获取调试信息的基址。它调用elf_locate_base分析主执行程序的ELF文件得到该信息。elf_locate_base先调用scan_dyntag查找类型为DT_MIPS_RLD_MAP(0x70000016)的动态信息,如果失败再调用scan_dyntag查找类型为DT_DEBUG(21)的动态信息。对于MIPS,编译器用DT_MIPS_RLD_MAP信息存放调试信息,而DT_DEBUG信息是无意义的,对于其他平台如ARM,则用DT_DEBUG信息存放调试信息,没有DT_MIPS_RLD_MAP信息。san_dyntag读取名为".dynamic”的section并逐一扫描,该section的内容由dynamic section structure数组组成,每个structure由两个整数组成,第一个整数是dynamic的类型(例如DT_DEBUG),第二个整数是dynamic的值,值的意义与类型相关。scan_dyntag逐一扫描,找到类型为DT_MIPS_RLD_MAP的动态信息,然后返回其值。该值是在编译时已经计算好的,实际上其值总是名为".rld_map”的section的地址。elf_locate_base会读取scan_dyntag返回的值所指向的内容,也就是".rld_map” section的内容。".rld_map” section的长度只有4字节,其内容是调试信息的基址,指向dynamic linker structs。在编译时,".rld_map”的值为0,在运行时,由加载器填写其值,加载器会维护一个dynamic linker structs,地址就放在".rld_map”中。在linux中,加载器通常是ld.so或者ld_linux.so。locate_base将elf_locate_base返回的值赋给全局变量debug_base,这样debug_base就指向了dynamic linker structs。由于这个信息是运行时才有的,所以GDB只有在同时载入主执行文件和Coredump文件后才能恢复这个链表。
svr4_current_sos再调用solib_svr4_r_map从dynamic linker structs中获取link map list链表,由于不同平台上数据的组织不同,GDB在读取信息时会调用svr4_fetch_link_map_offsets等函数来获取各变量的偏移地址和尺寸,在mips中,它最终会通过svr4_ilp32_fetch_link_map_offsets提供的信息来解析结构体的数据。在这里r_map_offset的信息为4,所以solib_svr4_r_map从debug_base + 4的地方读取link map list信息,这样就得到了整个链接映射表的头指针。

利用gdb调试coredump

1. 怎么生成coredump文件?

检查两个条件

1) core文件限制的大小,如果要生成的core文件超过设定的Core文件大小,则无法生成。

2) 编译的过程中需要使用 -g 参数。把调试信息加入到可执行文件。

修改core限制的大小命令:

ulimit -c unlimited

 

以上是关于GDB如何从Coredump文件恢复动态库信息的主要内容,如果未能解决你的问题,请参考以下文章

Linux C/C++代码 使用gdb进行coredump调试

利用gdb调试coredump

coredump时如何过滤掉共享内存

如何生成coredump文件

Linux应用开发

Linux应用开发