段错误定位方法
段错误一般指程序访问到非法内存地址,造成程序异常退出。如若是必现的段错误,通过加printf定位调试即可定位,这里提供两种快速定位段错误的方法。
- core dumped文件结合gdb定位
- backtrace定位
core dumped文件结合gdb定位
一般会在在代码中做Core文件记录,用于分析段错误位置。
core文件生成
以 demo.c为例:
#include <stdio.h>
#include <stdlib.h>
int main()
{
#ifdef CORE_DUMP
system("ulimit -c unlimited"); /* 设置产生core文件时不限制文件大小 */
system("echo \"/tmp/debug/core-%e-%p-%t\" > /proc/sys/kernel/core_pattern");/* 设置core文件的保存路径以及文件命名方式*/
#endif
int i=0;
scanf("%d",i);
printf("%d\n",i);
return 0;
}
core 文件参数说明:
%p 段错误发生所在的PID
%u 段错误发生所在的实际UID
%G 段错误发生所在的实际GID
%s 引发段错误发生的信号
%t 段错误发生的时间戳,表示为自1970年1月1日00:00:00 +0000(UTC)以来的秒数
%H 主机名(与uname(2)返回的节点名相同)
%e 可执行文件名(无路径前缀)
%E 可执行文件的路径名
%C 崩溃过程的核心文件大小软资源限制
在程序执行前先手动运行ulimit -c unlimited,避免因为shell上下文环境不同无法生成文件
Makefile:
all:
gcc -g -gdwarf-2 -DCORE_DUMP demo.c -o demo
编译选项说明:
-g: 生成调试信息
-gwarf-2: 正常显示函数参数的值
编译后执行:

目录下已生成core文件

其中1656223395是时间戳,可以知道段错误何时发生。可以使用在线工具转换,如下:

gdb分析
PC Linux程序
如果是纯PC Linux程序,使用以下命令直接查看:
gdb exe.bin core_file #gdb 可执行程序 core文件
在gbd环境内,使用bt查看堆栈信息,如下:

从上面可以看出,段错误发生在第10行。
嵌入式程序
嵌入式程序需要设置运行环境所用的库路径,类似下面截图:

同样可以获取到段错误位置。
backtrace定位
这种方法则通过在代码中加入相关函数,用于捕获相关信号,分析得到段错误位置。
以下bt_test.c程序为例:
#include <stdio.h>
#include <signal.h>
#include <execinfo.h>
#include <stdlib.h>
#include <string.h>
void print_trace(void)
{
void* array[300];
size_t size;
char** strings;
size_t i;
size = backtrace(array, 300);
strings = backtrace_symbols(array, size);
if (NULL == strings)
{
perror("backtrace_symbols");
exit(-1);
}
printf("Obtained %zd stack frames.\n", size);
for(i = 0 ; i < size; i++)
{
printf("%s\n", strings[i]);
}
free(strings);
strings = NULL;
exit(0);
}
void sighandler_dump_stack(int sig)
{
(void)sig;
print_trace();
}
int main()
{
signal(SIGSEGV, sighandler_dump_stack);
int i = 0;
scanf("%d",i);
printf("%d\n",i);
return 0;
}
编译
gcc -rdynamic bt_test.c -g
其中-rdynamic用来通知链接器将所有符号添加到动态符号表中
执行程序触发段错误
root@VM-0-15-ubuntu:~/bt_test# ./a.out
1
Obtained 8 stack frames.
./a.out(print_trace+0x32) [0x5592f8fee27b]
./a.out(sighandler_dump_stack+0x14) [0x5592f8fee361]
/lib/x86_64-linux-gnu/libc.so.6(+0x43090) [0x7efce512b090]
/lib/x86_64-linux-gnu/libc.so.6(+0x68335) [0x7efce5150335]
/lib/x86_64-linux-gnu/libc.so.6(__isoc99_scanf+0xb2) [0x7efce514b162]
./a.out(main+0x3a) [0x5592f8fee39e]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf3) [0x7efce510c083]
./a.out(_start+0x2e) [0x5592f8fee18e]
root@VM-0-15-ubuntu:~/bt_test#
可以看到程序在main函数+0x3a触发信号,反汇编分析
objdump -d a.out
root@VM-0-15-ubuntu:~/bt_test# objdump -d a.out
a.out: file format elf64-x86-64
Disassembly of section .init:
0000000000001000 <_init>:
1000: f3 0f 1e fa endbr64
1004: 48 83 ec 08 sub $0x8,%rsp
1008: 48 8b 05 d9 2f 00 00 mov 0x2fd9(%rip),%rax # 3fe8 <__gmon_start__>
100f: 48 85 c0 test %rax,%rax
1012: 74 02 je 1016 <_init+0x16>
1014: ff d0 callq *%rax
1016: 48 83 c4 08 add $0x8,%rsp
101a: c3 retq
... //省略无用信息
0000000000001364 <main>:
1364: f3 0f 1e fa endbr64
1368: 55 push %rbp
1369: 48 89 e5 mov %rsp,%rbp
136c: 48 83 ec 10 sub $0x10,%rsp
1370: 48 8d 35 d6 ff ff ff lea -0x2a(%rip),%rsi # 134d <sighandler_dump_stack>
1377: bf 0b 00 00 00 mov $0xb,%edi
137c: e8 9f fd ff ff callq 1120 <signal@plt>
1381: c7 45 fc 00 00 00 00 movl $0x0,-0x4(%rbp)
1388: 8b 45 fc mov -0x4(%rbp),%eax
138b: 89 c6 mov %eax,%esi
138d: 48 8d 3d 9e 0c 00 00 lea 0xc9e(%rip),%rdi # 2032 <_IO_stdin_used+0x32>
1394: b8 00 00 00 00 mov $0x0,%eax
1399: e8 a2 fd ff ff callq 1140 <__isoc99_scanf@plt>
139e: 8b 45 fc mov -0x4(%rbp),%eax
13a1: 89 c6 mov %eax,%esi
13a3: 48 8d 3d 8b 0c 00 00 lea 0xc8b(%rip),%rdi # 2035 <_IO_stdin_used+0x35>
13aa: b8 00 00 00 00 mov $0x0,%eax
13af: e8 5c fd ff ff callq 1110 <printf@plt>
13b4: b8 00 00 00 00 mov $0x0,%eax
13b9: c9 leaveq
13ba: c3 retq
13bb: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1)
... //省略无用信息
Disassembly of section .fini:
0000000000001438 <_fini>:
1438: f3 0f 1e fa endbr64
143c: 48 83 ec 08 sub $0x8,%rsp
1440: 48 83 c4 08 add $0x8,%rsp
1444: c3 retq
root@VM-0-15-ubuntu:~/bt_test#
可以看到main函数入口地址是0x1364,0x1364+0x3a=0x139e为段错误发生的位置。可以结合汇编分析段错误原因,这里不展开。
最后使用addr2line查看段错误函数和相关代码函数
addr2line -e a.out 0x139e -afs
root@VM-0-15-ubuntu:~/bt_test# addr2line -e a.out 0x139e -afs
0x000000000000139e
main
test.c:41
root@VM-0-15-ubuntu:~/bt_test#
本文来自博客园,作者:刘锐滨,转载请注明原文链接:https://www.cnblogs.com/liuruibin/articles/16413573.html

浙公网安备 33010602011771号