在编写linux程序时,程序崩溃,用dmesg查看到如下信息:
[7470411.358752] nmsrvd[10715]: segfault at a ip 00007f4220309664 sp 00007fff580042b0 error 4 in librt-2.17.so[7f4220305000+7000]
信息分析
at a :程序崩溃的内存指针变量的值,该示例的变量值为a(十进制 10),显然不是一个合法的内存地址。
ip 00007f4220309664 :指令所在的地址
sp 00007fff580042b0:当前的栈寄存器的值
error 4 :错误类型,当前示例的值为 4,表示访问违规,说明at 指向的值不是一个合法的内存地址
librt-2.17.so :崩溃所在的模块名,当前示例为动态库 /usr/lib64/librt-2.17.so
[7f4220305000+7000] 7f4220305000是librt-2.17.so在加载到内存中的起始地址(基地址),同一个程序在不同时刻启动加载的librt-2.17.so的基地址可能是不同的。
error 错误类型 的定义如下:
* 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
当前示例的error值为 4 为二进制 100,表示:user-mode access read access, no page found, 即用户模式读访问一个不存在的内存地址,根据这个推断,肯定是访问了一个不合法的内存地址。
那么通过该信息,怎样定位到崩溃位置的函数或行号呢?
用 ip 的值 00007f4220309664 减去 基地址 7f4220305000,可以获得错误在librt-2.17.so 中的偏移指令的地址,该示例:
00007f4220309664 - 7f4220305000 = 4664 (十六进制)
linux有一个工具 addr2line命令,可以帮助定位,通过如下命令行定位:
whereis librt-2.17.so
librt-2.17: /usr/lib/librt-2.17.so /usr/lib64/librt-2.17.so
addr2line -f -C -e /usr/lib64/librt-2.17.so 4664
shm_unlink
??:?
可以看到,是在调用 librt-2.17.so 库函数 shm_unlink 时发生了错误,笔者查看程序源代码,果然发现有一处调用了 shm_unlink 函数,没有进行相关指针检查而导致了segfault。