由段错误信息定位错误代码¶
@2014-04-17 新版功能: 创建
段错误的定义请参考维基。 对应用层代码而言,一般来说,当进程试图访问未被操作系统允许访问(或映射)的地址 或者尝试更新只读内存区时,操作系统会执行相应的保护,发送信号(SIGSEGV) 给该进程。如果进程未安装处理该信号的句柄,则缺省动作是程序非正常退出。对 x86 下的Linux系统,如果设置了 sysctl 变量 debug.exception-trace 为 1,则内核同时会 输出如下的类似信息:
slapd[6293]: segfault at 0 ip 00007ff6877cb1ed sp 00007ff6477fd4d0 error 4 in libldap_r-2.4.so.2.9.1[7ff687789000+56000]
该信息的各字段解释如下:
slapd[6293]:进程程序名及 pid
address:进程尝试访问的内存地址
ip:指令指针地址
sp:栈指针
error:错误码,参考 "arch/x86/mm/fault.c" 中的枚举变量 'x86_pf_error_code'
Page fault error code bits: bit 0 0: no page found 1: protection fault bit 1 0: read access 1: write access bit 2 0: kernel-mode access 1: user-mode access bit 3 1: use of reserved bit detected bit 4 1: fault was an instruction fetch
- [7ff687789000+56000]:产生段错误的代码所属目标(共享库等)被映射到虚存的 起始地址和大小。ip 的值应该在该范围之内。
因此,上面的信息解释可以解释为:pid 为 6293 的 slapd 进程在尝试访问地址 0 时出错,出错代码为 libldap_r-2.4.so.2.9.1 内的函数,其起始地址为 0x00007ff6877cb1ed - 0x7ff687789000 = 0x421ed。该地址对应的函数可以通过 objdump -d /usr/lib/libldap_r-2.4.so.2.9.1 | less 搜索 421ed 定位到:
0000000000040a70 <ldap_pvt_tls_get_peer_dn>:
.............
421e7: 75 71 jne 4225a <ldap_pvt_tls_get_peer_dn+0x17ea>
421e9: 48 8b 6f 18 mov 0x18(%rdi),%rbp
421ed: 48 8b 7d 00 mov 0x0(%rbp),%rdi
421f1: e8 3a fb fc ff callq 11d30 <SSL_write@plt>
对于有 coredump 文件生成的代码通过 gdb 能很快定位到相应的代码。 然而有时程序崩溃时会没有 coredump 文件生成,如果在 gdb 环境中,可以使用 gdb 命令 generate-core-file 来生成该文件供事后进一步的分析。
(gdb) help generate-core-file
Save a core file with the current state of the debugged process.
Argument is optional filename. Default filename is 'core.<process_id>'.
(gdb) break main
Breakpoint 1 at 0x400570
(gdb) r
Starting program: /home/users/liuzx/./a.out
Breakpoint 1, 0x0000000000400570 in main ()
(gdb) generate-core-file
Saved corefile core.9420
另外,glibc中也提供了一个脚本 catchsegv 来辅助捕获发生段错误时的信息。 其原理是利用 LD_PRELOAD 预加载 /lib64/libSegFault.so 来实现的。
# catchsegv ./a.out
*** Segmentation fault
Register dump:
RAX: 00007fe07e50a9c4 RBX: 0000000000000000 RCX: 00007fe07e50a8d0
...............
Backtrace:
./a.out(main+0x26)[0x7fe07e50a8f6]
/lib64/libc.so.6(__libc_start_main+0xf5)[0x7fe07dd5ab95]
./a.out(+0x7e9)[0x7fe07e50a7e9]
Memory map:
7fe07db1f000-7fe07db34000 r-xp 00000000 08:02 1576197 /usr/lib64/gcc/x86_64-pc-linux-gnu/4.7.3/libgcc_s.so.1
......
7fe07e0e1000-7fe07e0e5000 r-xp 00000000 08:02 1592614 /lib64/libSegFault.so
.......