main.c
cpp
#define _GNU_SOURCE
#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <execinfo.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <execinfo.h>
void print_addr_info(void *addr) {
Dl_info info;
if (dladdr(addr, &info)) {
printf(" Function: %s\n", info.dli_sname ? info.dli_sname : "??");
printf(" Library: %s\n", info.dli_fname ? info.dli_fname : "??");
// 计算偏移量
if (info.dli_saddr) {
printf(" Offset: %p\n", (void*)((char*)addr - (char*)info.dli_saddr));
}
}
}
void signal_handle(int signal) {
void *buffer[100];
char **strings;
int nptrs;
printf("\n==========> catch signal %d <==========\n", signal);
printf("Dump stack start...\n");
// 获取调用栈信息
nptrs = backtrace(buffer, 100);
strings = backtrace_symbols(buffer, nptrs);
if (strings == NULL) {
perror("backtrace_symbols");
exit(EXIT_FAILURE);
}
// 打印调用栈信息
for (int i = 0; i < nptrs; i++) {
printf("%s\n", strings[i]);
print_addr_info(buffer[i]);
printf("\n");
}
printf("Dump stack end...\n");
free(strings);
exit(EXIT_FAILURE);
}
// 模拟空指针引用的函数
void null_pointer_dereference() {
int *ptr = NULL;
*ptr = 10; // 空指针引用,会触发SIGSEGV信号
}
// 模拟除零操作的函数
void division_by_zero() {
int a = 10;
int b = 0;
int result = a / b; // 除零操作,会触发SIGFPE信号
}
int main() {
// 注册SIGSEGV信号的处理函数
signal(SIGSEGV, signal_handle);
// 注册SIGFPE信号的处理函数
signal(SIGFPE, signal_handle);
// 测试空指针引用
null_pointer_dereference();
// 测试除零操作
// division_by_zero();
return 0;
}
编译指令:
gcc main.c -o main -g -rdynamic -ldl
其中-g -rdynamic是backtrace需要的,-ldl是dladdr函数需要的。
执行可执行文件:
bash
ubuntu@ubuntu:/tmp$ ./main
==========> catch signal 11 <==========
Dump stack start...
./main(signal_handle+0x59) [0x55dcf2b36d18]
Function: signal_handle
Library: ./main
Offset: 0x59
/lib/x86_64-linux-gnu/libc.so.6(+0x3ef10) [0x7fb5978a0f10]
Function: ??
Library: /lib/x86_64-linux-gnu/libc.so.6
./main(null_pointer_dereference+0x10) [0x55dcf2b36df9]
Function: null_pointer_dereference
Library: ./main
Offset: 0x10
./main(main+0x30) [0x55dcf2b36e51]
Function: main
Library: ./main
Offset: 0x30
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xe7) [0x7fb597883c87]
Function: __libc_start_main
Library: /lib/x86_64-linux-gnu/libc.so.6
Offset: 0xe7
./main(_start+0x2a) [0x55dcf2b36b1a]
Function: _start
Library: ./main
Offset: 0x2a
Dump stack end...
从这个打印来看,0x55dcf2b36df9是null_pointer_dereference中失败的地方,但是这是程序执行地址,不能用于addr2line,我们需要找到null_pointer_dereference在可执行文件中的偏移地址。
bash
ubuntu@ubuntu:/tmp$ objdump -t main | grep null_pointer_dereference
0000000000000de9 g F .text 0000000000000019 null_pointer_dereference
从上面知道可执行文件中null_pointer_dereference偏移为de9,出错的地方是null_pointer_dereference+0x10,即出错的偏移为de9+10=0xdf9。现在用addr2line找到具体的行数:
bash
ubuntu@ubuntu:/tmp$ addr2line -e main 0xdf9 -f
null_pointer_dereference
/tmp/main.c:59
看到在main.c的59行,没错:
