Linux平臺Segmentation fault(段錯誤)調(diào)試過程
1. 段錯誤是什么
一句話來說,段錯誤是指訪問的內(nèi)存超出了系統(tǒng)給這個程序所設定的內(nèi)存空間,
例如訪問了不存在的內(nèi)存地址、訪問了系統(tǒng)保護的內(nèi)存地址、訪問了只讀的內(nèi)存地址等等情況。
2. 段錯誤的原因
段錯誤,英文segmentation fault
段錯誤的定義
segementation fault (often shortened to segfault) or access violation
often raised by hardware with memory protection, notifying an operation system(OS) the software has attempted to access a restricted area of memory.
段錯誤常見于提供低級別的內(nèi)存訪問機制(如C語言)的語言程序中,通常由非法訪問導致,即錯誤地使用指針訪問虛擬內(nèi)存。
原因列舉
- 對空指針賦值——由內(nèi)存管理硬件所導致
- 嘗試去訪問一個不存在的內(nèi)存地址(超出進程分配的地址)
- 嘗試去訪問程序無權限訪問的地址(如進程上下文中的內(nèi)核結構(kernel structures))
- 嘗試去訪問只讀的內(nèi)存(如代碼段)
對應著程序中的情況分別為:
- 引用或者賦值一個為初始化的指針(野指針(wild pointer),隨機指向一個內(nèi)存地址)
- 引用或者賦值一個已經(jīng)被free的指針(dangling pointer,指向一個已經(jīng)被freed/deallocated/deleted的內(nèi)存地址)
- A buffer overflow
- A stack overflow
3. 段錯誤信息的獲取
3.1 dmesg
dmesg可以在應用程序crash掉時,顯示內(nèi)核中保存的相關信息。
如下所示,通過dmesg命令可以查看發(fā)生段錯誤的程序名稱、引起段錯誤發(fā)生的內(nèi)存地址、指令指針地址、堆棧指針地址、錯誤代碼、錯誤原因等。
3.2 -g
使用gcc編譯程序的源碼時,加上-g參數(shù),這樣可以使得生成的二進制文件中加入可以用于gdb調(diào)試的有用信息。
3.3 nm
使用nm命令列出二進制文件中的符號表,包括符號地址、符號類型、符號名等,這樣可以幫助定位在哪里發(fā)生了段錯誤。
3.4 ldd
使用ldd命令查看二進制程序的共享鏈接庫依賴,包括庫的名稱、起始地址,這樣可以確定段錯誤到底是發(fā)生在了自己的程序中還是依賴的共享庫中。
4. 段錯誤的調(diào)試方法
4.1 使用gcc和gdb
4.1.1 調(diào)試步驟
為了能夠使用gdb調(diào)試程序,在編譯階段加上-g參數(shù),以程序segfault3.c為例。
root@twq2018:~/segfault$ gcc -g -o segfault3 segfault3.c root@twq2018:~/segfault$ gdb ./segfault3 (gdb) run Starting program: /home/twq/segfault/segfault3 Program received signal SIGSEGV, Segmentation fault. 0x001a306a in memcpy () from /lib/tls/i686/cmov/libc.so.6 (gdb)
從輸出看出,程序segfault3.c收到SIGSEGV信號,觸發(fā)段錯誤,并提示地址0x001a306a、調(diào)用memcpy報的錯,位于/lib/tls/i686/cmov/libc.so.6庫中。
4.1.2 適用場景
1、僅當能確定程序一定會發(fā)生段錯誤的情況下使用。
2、當程序的源碼可以獲得的情況下,使用-g參數(shù)編譯程序。
3、一般用于測試階段,生產(chǎn)環(huán)境下gdb會有副作用:使程序運行減慢,運行不夠穩(wěn)定,等等。
4、即使在測試階段,如果程序過于復雜,gdb也不能處理。
4.2 使用gdb和core文件
在4.1節(jié)中提到段錯誤會觸發(fā)SIGSEGV信號,通過man 7 signal,可以看到SIGSEGV默認的handler會打印段錯誤出錯信息,并產(chǎn)生core文件,由此我們可以借助于程序異常退出時生成的core文件中的調(diào)試信息。
使用gdb工具來調(diào)試程序中的段錯誤,用bt命令查看backtrace以檢查發(fā)生程序運行到哪里, 來定位core dump的文件->行.。
4.2.1 調(diào)試步驟
1、在一些Linux版本下,默認是不產(chǎn)生core文件的,首先可以查看一下系統(tǒng)core文件的大小限制:
root@twq2018:~/segfault$ ulimit -c 0
2、可以看到默認設置情況下,本機Linux環(huán)境下發(fā)生段錯誤時不會自動生成core文件,下面設置下core文件的大小限制(單位為KB),其中使用ulimit -c unlimited來設置無限大,則任意情況下都會產(chǎn)生core文件。
root@twq2018:~/segfault$ ulimit -c 1024 root@twq2018:~/segfault$ ulimit -c 1024
3、運行程序segfault3.c,發(fā)生段錯誤生成core文件:
root@twq2018:~/segfault$ ./segfault3 段錯誤 (core dumped)
4、加載core文件,使用gdb工具進行調(diào)試:
root@twq2018:~/segfault$ gdb ./segfault3 ./core
4.2.2 適用場景
1、適合于在實際生成環(huán)境下調(diào)試程序的段錯誤(即在不用重新發(fā)生段錯誤的情況下重現(xiàn)段錯誤)。
2、當程序很復雜,core文件相當大時,該方法不可用。
4.3 使用objdump
4.3.1 調(diào)試步驟
1、使用dmesg命令,找到最近發(fā)生的段錯誤輸出信息:
root@twq2018:~/segfault$ dmesg ... ... [17257.502808] segfault3[3320]: segfault at 80484e0 ip 0018506a sp bfc1cd6c error 7 in libc-2.10.1.so[110000+13e000]
其中,對我們接下來的調(diào)試過程有用的是發(fā)生段錯誤的地址:80484e0和指令指針地址:0018506a。
2、使用objdump生成二進制的相關信息,重定向到文件中:
root@twq2018:~/segfault$ objdump -d ./segfault3 > segfault3Dump
其中,生成的segfault3Dump文件中包含了二進制文件的segfault3的匯編代碼。
3、在segfault3Dump文件中查找發(fā)生段錯誤的地址:
root@twq2018:~/segfault$ grep -n -A 10 -B 10 "80484e0" ./segfault3Dump 121- 80483df: ff d0 call *%eax 122- 80483e1: c9 leave 123- 80483e2: c3 ret 124- 80483e3: 90 nop 125- 126-080483e4 <main>: 127- 80483e4: 55 push %ebp 128- 80483e5: 89 e5 mov %esp,%ebp 129- 80483e7: 83 e4 f0 and $0xfffffff0,%esp 130- 80483ea: 83 ec 20 sub $0x20,%esp 131: 80483ed: c7 44 24 1c e0 84 04 movl $0x80484e0,0x1c(%esp) 132- 80483f4: 08 133- 80483f5: b8 e5 84 04 08 mov $0x80484e5,%eax 134- 80483fa: c7 44 24 08 05 00 00 movl $0x5,0x8(%esp) 135- 8048401: 00 136- 8048402: 89 44 24 04 mov %eax,0x4(%esp) 137- 8048406: 8b 44 24 1c mov 0x1c(%esp),%eax 138- 804840a: 89 04 24 mov %eax,(%esp) 139- 804840d: e8 0a ff ff ff call 804831c <memcpy@plt> 140- 8048412: c9 leave 141- 8048413: c3 ret
通過對以上匯編代碼分析,得知段錯誤發(fā)生main函數(shù),對應的匯編指令是movl $0x80484e0,0x1c(%esp),接下來打開程序的源碼,找到匯編指令對應的源碼,也就定位到段錯誤了。
4.3.2 適用場景
1、不需要-g參數(shù)編譯,不需要借助于core文件,但需要有一定的匯編語言基礎。
2、如果使用了gcc編譯優(yōu)化參數(shù)(-O1,-O2,-O3)的話,生成的匯編指令將會被優(yōu)化,使得調(diào)試過程有些難度。
總結
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
Linux、ubuntu系統(tǒng)下查看顯卡型號、顯卡信息詳解
這篇文章主要介紹了如何在Linux、ubuntu系統(tǒng)下查看顯卡型號、顯卡信息的方法,需要的朋友可以參考下2022-04-04