程式segmentation fault後, 用dmesg和addr2line來除錯

本篇紀錄如何從程式的 segmentation fault,配合 dmesg 和 addr2line 來除錯,
查出程式是掛(死)在哪個原始碼的第幾行。

首先先來寫一個會讓程式崩潰的程式,
再來配合使用 dmesg 查 kernel log 看是掛在哪個記憶體位置,
再來配合使用 addr2line 查出該記憶體位置是在程式碼的那一行
開始吧!

寫一個會讓程式崩潰的程式

讓一個 ptr 指標指向 NULL, 使用 printf 印出 ptr 指向的值,
使用 g++ cpp-crash.cpp -o a.out 進行編譯,
之後再執行 a.out 就會發現程式執行到一半發生segmentation fault (core dumped) 了!

cpp-crash.cpp
1
2
3
4
5
6
7
8
9
10
11
12
// g++ cpp-crash.cpp -o a.out
#include <stdio.h>

void myprint(int* ptr) {
printf("%d\n", *ptr);
}

int main() {
int *ptr = NULL;
myprint(ptr);
return 0;
}

輸出

1
Segmentation fault (core dumped)

使用 dmesg 查 kernel log 看是掛在哪個記憶體位置

接著 dmesg 指令可以查看發生段錯誤的程式名稱、造成錯誤發生的記憶體地址、指令指標地址、堆疊指標地址、錯誤程式碼、錯誤原因等,
接著使用 dmesg 看看記憶體位置,找到顯示的 ip 為 0000000000400536

1
a.out[14306]: segfault at 0 ip 0000000000400536 sp 00007ffce9c58050 error 4 in a.out[400000+1000]

也可用 Android logcat 上 backtrace 的 pc 記憶體位置來去查。

配合使用 addr2line 查出該記憶體位置是在程式碼的那一行

最後使用 addr2line 找出 ip 0000000000400536 在程式碼第幾行

1
$ addr2line -Cfie ./a.out 0000000000400536

輸出結果如下

1
2
myprint(int*)
??:?

上列資訊顯示程式死在 myprint 這個函式裡,但是看不到行數,
原因是因為使用 g++ 時沒用 -g 參數讓它編譯出有除錯的資訊
使用 g++ cpp-crash.cpp -o a.out -g 再編譯一次吧!
之後在執行一次,將 ip 拿去用 addr2line 查出的結果如下,

1
2
myprint(int*)
/home/xxx/cpp-crash.cpp:5

這次就可以很清楚看到原始檔名稱與行數了!
這樣就可以回去程式碼裡好好看看是哪裡寫錯了。

使用 nm 查詢

使用 nm 指令 nm -gC a.out 可以導出 symbol,
如下所示,發現 myprint 的位置是 0000000000400526 跟上面的 ip 很接近

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
0000000000601038 B __bss_start
0000000000601028 D __data_start
0000000000601028 W data_start
0000000000601030 D __dso_handle
0000000000601038 D _edata
0000000000601040 B _end
00000000004005e4 T _fini
w __gmon_start__
00000000004003c8 T _init
00000000004005f0 R _IO_stdin_used
w _ITM_deregisterTMCloneTable
w _ITM_registerTMCloneTable
w _Jv_RegisterClasses
00000000004005e0 T __libc_csu_fini
0000000000400570 T __libc_csu_init
U __libc_start_main@@GLIBC_2.2.5
000000000040054c T main
U printf@@GLIBC_2.2.5
0000000000400430 T _start
0000000000601038 D __TMC_END__
0000000000400526 T myprint(int*)

參考
[1] 使用dmesg和addr2line查找程序崩潰後的現場報告 | cpper
http://www.cpper.cn/2016/09/09/develop/dmesg-addr2line/

[2] gdb調試core dump入門實踐(順便複習一下之前介紹過的addr2line命令調試) - stpeace的專欄
https://blog.csdn.net/stpeace/article/details/49806473

[3] 關於Segmentation fault (core dumped)幾個簡單問題的整理
https://codertw.com/%E7%A8%8B%E5%BC%8F%E8%AA%9E%E8%A8%80/560380/
什麼是Core Dump,可以看看這篇的介紹

[4]【已解決】Linux下出現Segmentation Fault(core dump)錯誤 - YSBJ123的博客
https://blog.csdn.net/YSBJ123/article/details/50035169
其他也不錯的文章,這篇有好幾種讓你產生 Segmentation Fault 的程式
https://www.cnblogs.com/panfeng412/archive/2011/11/06/segmentation-fault-in-linux.html 轉載

[5] 内核 segfault 报错分析 - Jamin Zhang
https://jaminzhang.github.io/linux/Kernel-Segfault-Analysis/
解釋 error 4 代表什麼意思

其它相關文章推薦
addr2line 用法
nm 用法與範例