测试代码
本文不介绍栈回溯的原理,只对如何实现以及使用栈回溯来定位问题做一个简单的介绍,纯属个人理解!!!
signal_test.c
#include <stdio.h>
#include <execinfo.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
/*
编译命令:gcc signal_test.c -rdynamic -fexceptions -g -o signal_test
*/
void signal_func(int sig) {
void *array[10];
size_t size;
// get void*'s for all entries on the stack
size = backtrace(array, 10);
// print out all the frames to stderr
fprintf(stderr, "Error: signal %d:\n", sig);
backtrace_symbols_fd(array, size, STDERR_FILENO);
exit(1);
}
int main(int argc, char **argv) {
signal(SIGSEGV, signal_func); // install our handler
int *a = NULL;
*a = 123;
}
编译命令可以上上图代码中的"gcc signal_test.c -rdynamic -fexceptions -g -o signal_test",编译工具链使用自己测试平台对应的工具链
编译完之后生成signal_test之后直接运行,会有如下打印:
上图的打印可以看到三个"./signal_test()",
从上至下的第一个表示signal_func中执行backtrace的地址
第二个就是导致出问题的地址
第三个就是之前第二步之前执行命令的地址。如果打印的信息较多就依次类推
如何解读以上地址信息
就拿第二个异常地址来解读: 0x5578aa0ca8就是出异常的地址,如果我们直接使用addr2line命令是得不到对应的结果的,因为这个地址是整个板子的地址,即这个地址是一个绝对地址,而不是一个相对地址。 使用addr2line是需要一个相对地址,相较于signal_test程序启动的偏移地址。也就是我们需要将0x5578aa0ca8减去signal_test程序启动的地址才是对的。
其实(main+0x30)也可以读取偏移地址,这个的含义就是:出异常的地址相较于main函数的起始地址偏移了0x30。我们读出main函数的起始地址,然后进行再偏移0x30就可以得到出异常的相对地址了。
借助编译工具链的相关工具进一步解读(选用与自己平台对应的工具链,x86_64-linux-gnu-、aarch64-linux-gnu-等)
使用readelf命令读取程序的符号表;readelf -s signal_test,下图是使用上述命令的截图,其中main函数的起始地址是0xc78,加上0x30得出0xca8相对的偏移地址
再借助addr2line工具;addr2line -e ./signal_test 0xca8,根据下图的结果就可以知道是signal_test.c文件的29行出了问题。