Linux下除錯段錯誤的方法[Segmentation Fault]--GDB
4. 段錯誤的除錯方法
4.1 使用printf輸出資訊
這個是看似最簡單但往往很多情況下十分有效的除錯方式,也許可以說是程式設計師用的最多的除錯方式。簡單來說,就是在程式的重要程式碼附近加上像printf這類輸出資訊,這樣可以跟蹤並打印出段錯誤在程式碼中可能出現的位置。為了方便使用這種方法,可以使用條件編譯指令#ifdef DEBUG和#endif把printf函式包起來。這樣在程式編譯時,如果加上-DDEBUG引數就能檢視除錯資訊;否則不加該引數就不會顯示除錯資訊。
4.2 使用gcc和gdb
4.2.1 除錯步驟
1、為了能夠使用gdb除錯程式,在編譯階段加上-g引數,以程式2.3為例:[email protected]:~/segfault$ gcc -g -o segfault3 segfault3.c2、使用gdb命令除錯程式:
[email protected]:~/segfault$ gdb ./segfault3 GNU gdb (GDB) 7.0-ubuntu Copyright (C) 2009 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Type3、進入gdb後,執行程式:"show copying" and "show warranty" for details. This GDB was configured as "i486-linux-gnu". For bug reporting instructions, please see: <http://www.gnu.org/software/gdb/bugs/>... Reading symbols from /home/panfeng/segfault/segfault3...done. (gdb)
(gdb) run
Starting program: /home/panfeng/segfault/segfault3
Program received signal SIGSEGV, Segmentation fault.
0x001a306a in memcpy () from /lib/tls/i686/cmov/libc.so.6
(gdb)
4、完成除錯後,輸入quit命令退出gdb:
(gdb) quit A debugging session is active. Inferior 1 [process 3207] will be killed. Quit anyway? (y or n) y
4.2.2 適用場景
1、僅當能確定程式一定會發生段錯誤的情況下使用。
2、當程式的原始碼可以獲得的情況下,使用-g引數編譯程式。
3、一般用於測試階段,生產環境下gdb會有副作用:使程式執行減慢,執行不夠穩定,等等。
4、即使在測試階段,如果程式過於複雜,gdb也不能處理。
4.3 使用core檔案和gdb
在4.2節中提到段錯誤會觸發SIGSEGV訊號,通過man 7 signal,可以看到SIGSEGV預設的handler會列印段錯誤出錯資訊,併產生core檔案,由此我們可以藉助於程式異常退出時生成的core檔案中的除錯資訊,使用gdb工具來除錯程式中的段錯誤。
4.3.1 除錯步驟
1、在一些Linux版本下,預設是不產生core檔案的,首先可以檢視一下系統core檔案的大小限制:
[email protected]:~/segfault$ ulimit -c 0
2、可以看到預設設定情況下,本機Linux環境下發生段錯誤時不會自動生成core檔案,下面設定下core檔案的大小限制(單位為KB):
[email protected]:~/segfault$ ulimit -c 1024 [email protected]:~/segfault$ ulimit -c 1024
3、執行程式2.3,發生段錯誤生成core檔案:
[email protected]:~/segfault$ ./segfault3 段錯誤 (core dumped)
4、載入core檔案,使用gdb工具進行除錯:
[email protected]:~/segfault$ gdb ./segfault3 ./core GNU gdb (GDB) 7.0-ubuntu Copyright (C) 2009 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Type "show copying" and "show warranty" for details. This GDB was configured as "i486-linux-gnu". For bug reporting instructions, please see: <http://www.gnu.org/software/gdb/bugs/>... Reading symbols from /home/panfeng/segfault/segfault3...done. warning: Can't read pathname for load map: 輸入/輸出錯誤. Reading symbols from /lib/tls/i686/cmov/libc.so.6...(no debugging symbols found)...done. Loaded symbols for /lib/tls/i686/cmov/libc.so.6 Reading symbols from /lib/ld-linux.so.2...(no debugging symbols found)...done. Loaded symbols for /lib/ld-linux.so.2 Core was generated by `./segfault3'. Program terminated with signal 11, Segmentation fault. #0 0x0018506a in memcpy () from /lib/tls/i686/cmov/libc.6
從輸出看出,同4.2.1中一樣的段錯誤資訊。
5、完成除錯後,輸入quit命令退出gdb:
(gdb) quit
4.3.2 適用場景
1、適合於在實際生成環境下除錯程式的段錯誤(即在不用重新發生段錯誤的情況下重現段錯誤)。
2、當程式很複雜,core檔案相當大時,該方法不可用。
4.4 使用objdump
4.4.1 除錯步驟
1、使用dmesg命令,找到最近發生的段錯誤輸出資訊:
[email protected]:~/segfault$ dmesg ... ... [17257.502808] segfault3[3320]: segfault at 80484e0 ip 0018506a sp bfc1cd6c error 7 in libc-2.10.1.so[110000+13e000]
其中,對我們接下來的除錯過程有用的是發生段錯誤的地址:80484e0和指令指標地址:0018506a。
2、使用objdump生成二進位制的相關資訊,重定向到檔案中:
[email protected]:~/segfault$ objdump -d ./segfault3 > segfault3Dump
其中,生成的segfault3Dump檔案中包含了二進位制檔案的segfault3的彙編程式碼。
3、在segfault3Dump檔案中查詢發生段錯誤的地址:
[email protected]:~/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 <[email protected]> 140- 8048412: c9 leave 141- 8048413: c3 ret
通過對以上彙編程式碼分析,得知段錯誤發生main函式,對應的彙編指令是movl $0x80484e0,0x1c(%esp),接下來開啟程式的原始碼,找到彙編指令對應的原始碼,也就定位到段錯誤了。
4.4.2 適用場景
1、不需要-g引數編譯,不需要藉助於core檔案,但需要有一定的組合語言基礎。
2、如果使用了gcc編譯優化引數(-O1,-O2,-O3)的話,生成的彙編指令將會被優化,使得除錯過程有些難度。
4.5 使用catchsegv
catchsegv命令專門用來撲獲段錯誤,它通過動態載入器(ld-linux.so)的預載入機制(PRELOAD)把一個事先寫好的庫(/lib/libSegFault.so)載入上,用於捕捉斷錯誤的出錯資訊。
[email protected]:~/segfault$ catchsegv ./segfault3 Segmentation fault (core dumped) *** Segmentation fault Register dump: EAX: 00000000 EBX: 00fb3ff4 ECX: 00000002 EDX: 00000000 ESI: 080484e5 EDI: 080484e0 EBP: bfb7ad38 ESP: bfb7ad0c EIP: 00ee806a EFLAGS: 00010203 CS: 0073 DS: 007b ES: 007b FS: 0000 GS: 0033 SS: 007b Trap: 0000000e Error: 00000007 OldMask: 00000000 ESP/signal: bfb7ad0c CR2: 080484e0 Backtrace: /lib/libSegFault.so[0x3b606f] ??:0(??)[0xc76400] /lib/tls/i686/cmov/libc.so.6(__libc_start_main+0xe6)[0xe89b56] /build/buildd/eglibc-2.10.1/csu/../sysdeps/i386/elf/start.S:122(_start)[0x8048351] Memory map: 00258000-00273000 r-xp 00000000 08:01 157 /lib/ld-2.10.1.so 00273000-00274000 r--p 0001a000 08:01 157 /lib/ld-2.10.1.so 00274000-00275000 rw-p 0001b000 08:01 157 /lib/ld-2.10.1.so 003b4000-003b7000 r-xp 00000000 08:01 13105 /lib/libSegFault.so 003b7000-003b8000 r--p 00002000 08:01 13105 /lib/libSegFault.so 003b8000-003b9000 rw-p 00003000 08:01 13105 /lib/libSegFault.so 00c76000-00c77000 r-xp 00000000 00:00 0 [vdso] 00e0d000-00e29000 r-xp 00000000 08:01 4817 /lib/libgcc_s.so.1 00e29000-00e2a000 r--p 0001b000 08:01 4817 /lib/libgcc_s.so.1 00e2a000-00e2b000 rw-p 0001c000 08:01 4817 /lib/libgcc_s.so.1 00e73000-00fb1000 r-xp 00000000 08:01 1800 /lib/tls/i686/cmov/libc-2.10.1.so 00fb1000-00fb2000 ---p 0013e000 08:01 1800 /lib/tls/i686/cmov/libc-2.10.1.so 00fb2000-00fb4000 r--p 0013e000 08:01 1800 /lib/tls/i686/cmov/libc-2.10.1.so 00fb4000-00fb5000 rw-p 00140000 08:01 1800 /lib/tls/i686/cmov/libc-2.10.1.so 00fb5000-00fb8000 rw-p 00000000 00:00 0 08048000-08049000 r-xp 00000000 08:01 303895 /home/panfeng/segfault/segfault3 08049000-0804a000 r--p 00000000 08:01 303895 /home/panfeng/segfault/segfault3 0804a000-0804b000 rw-p 00001000 08:01 303895 /home/panfeng/segfault/segfault3 09432000-09457000 rw-p 00000000 00:00 0 [heap] b78cf000-b78d1000 rw-p 00000000 00:00 0 b78df000-b78e1000 rw-p 00000000 00:00 0 bfb67000-bfb7c000 rw-p 00000000 00:00 0 [stack]
5. 一些注意事項
1、出現段錯誤時,首先應該想到段錯誤的定義,從它出發考慮引發錯誤的原因。
2、在使用指標時,定義了指標後記得初始化指標,在使用的時候記得判斷是否為NULL。
3、在使用陣列時,注意陣列是否被初始化,陣列下標是否越界,陣列元素是否存在等。
4、在訪問變數時,注意變數所佔地址空間是否已經被程式釋放掉。
5、在處理變數時,注意變數的格式控制是否合理等。
相關推薦
Linux下的段錯誤(Segmentation fault)產生的原因及除錯方法
段錯誤 就是訪問了錯誤的記憶體段,一般是你沒有許可權,或者根本就不存在對應的實體記憶體,尤其常見的是訪問0地址. 一 般來說,段錯誤就是指訪問的記憶體超出了系統所給這個程式的記憶體空間,通常這個值是由gdtr來儲存的是一個48位的暫存器, 前32位是儲存由它指
Linux下除錯段錯誤的方法[Segmentation Fault]--GDB
4. 段錯誤的除錯方法 4.1 使用printf輸出資訊 這個是看似最簡單但往往很多情況下十分有效的除錯方式,也許可以說是程式設計師用的最多的除錯方式。簡單來說,就是在程式的重要程式碼附近加上像printf這類輸出資訊,這樣可以跟蹤並打印出段錯誤在程式碼中可能出現的位置。為了方便使用這種方法,可以使用
Linux下的段錯誤(Segmentation fault)產生的原因及除錯方法(經典)
編譯執行效果如下: [email protected] test $ gcc -g -rdynamic f.c [email protected] test $ ./a.out GNU gdb 6.5 Copyright (C) 2006 Free Software Foundation,
Linux 下的段錯誤(Segmentation fault)除錯方法(轉)
轉自:http://blog.sina.com.cn/s/blog_3e28c8a50100bnin.html 我們在用C/C++語言寫程式的時侯,記憶體管理的絕大部分工作都是需要我們來做的。實際上,記憶體管理是一個比
Linux下使用-static -lpthread靜態編譯出現段錯誤(Segmentation fault)
最近在看golang,感覺go的靜態編譯思想很不錯。於是準備把手頭的幾個專案靜態編譯一下,結果編譯的時候沒報任何警告及錯誤,一執行就報段錯誤(Segmentation fault)。gdb逐步除錯,發現問題出在std::thread那裡。仔細檢查了下所有語法,沒
Linux下的段錯誤產生的原因及除錯方法
系統保護的記憶體地址寫資料 最常見就是給一個指標以0地址 2)記憶體越界(陣列越界,變數型別不一致等)訪問到不屬於你的記憶體區域 解決方法 我們在用C/C++語言寫程式的時侯,記憶體管理的絕大部分工作都是需要我們來做的。實際上,記憶體管理是一個比較繁瑣的工作,無論你多高明,經驗多豐富,難免會在此處犯些小錯
Linux下的段錯誤分析
2. 段錯誤產生的原因 2.1 訪問不存在的記憶體地址 #include #includevoid main() {int *ptr = NULL;*ptr = 0; } 2.2 訪問系統保護的記憶體地址 #include #includevoid main() {
嵌入式 使用gdb除錯段錯誤(segment fault)
我們打算使用gdb去解決為什麼下面的程式(檔案為segfault.c)引起了段錯誤的問題。下面的這段程式是從使用者那裡讀入一行文字字串然後顯示在螢幕上。然而,如下當前的程式並不會如期執行... [cpp] view plaincopyprint? <s
使用gdb除錯段錯誤(segment fault)
我們打算使用gdb去解決為什麼下面的程式(檔案為segfault.c)引起了段錯誤的問題。下面的這段程式是從使用者那裡讀入一行文字字串然後顯示在螢幕上。然而,如下當前的程式並不會如期執行... #include <stdio.h> #include <
C/C++中的段錯誤(Segmentation fault)
}3)其他其實大概的原因都是一樣的,就是段錯誤的定義。但是更多的容易出錯的地方就要自己不斷積累,不段發現,或者吸納前人已經積累的經驗,並且注意避免再次發生。例如:<1>定義了指標後記得初始化,在使用的時候記得判斷是否為NULL<2>在使用陣列的時候是否被初始化,陣列下標是否越界,陣列元
你的java/c/c++程式崩潰了?揭祕段錯誤(Segmentation fault)(3)
前言 接上兩篇: 寫到這裡,越跟,越發現真的是核心上很白,非一般的白。 但是既然是研究,就定住心,把段錯誤搞到清楚明白。 本篇將作為終篇,來結束這個系列,也算是對段錯誤和程式除錯、尋找崩潰原因(通常不會給你那麼完美的stackstrace和人性化的錯
linux核心發生段錯誤時進行除錯
發生段錯誤原因就是訪問了不該訪問的地址,例如訪問了不存在的記憶體地址、訪問了系統保護的記憶體地址、訪問了只讀的記憶體地址等。 下面根據Oops資訊來分析一下段錯誤 first_drv.c 點選(此處)摺疊或開啟 #include <linux/module.h
linux下除錯方法記錄
1、segment fault segment fault是幾乎多有C程式設計師都會碰到的問題,多為記憶體問題,因為glibc庫中基本所有的函式都預設形參指標是非空的,這樣以下原因就可能導致段錯誤: (1)引用一個包含非法值的指標(當然包括空指標)。 (2)未得到正確的許可
Linux 程式設計段錯誤(segmentation error)總結
最近一段時間在linux下用C做一些學習和開發,但是由於經驗不足,問題多多。而段錯誤就是讓我非常頭痛的一個問題。不過,目前寫一個一千行左右的程式碼,也很少出現段錯誤,或者是即使出現了,也很容易找出來,並且處理掉。 那什麼是段錯誤?段錯誤為什麼是個麻煩事?以及怎麼發現程
Linux下Java調試方法
java linux 參數 調試 jdb 1、如何開啟一個Java進程的調試選項?替換原有java程序:mv /usr/bin/java /usr/bin/java_true在/usr/bin/目錄下生成一個java文件,其內容如下:java_true -Xdebug -Xrunjdwp:
linux下安裝mongodb的方法和終端下基本操作
base 當前 操作 god --help 客戶 drop tar -s 在linux環境安裝mongoDB: 一般認為偶數版本為穩定版 如 1.6.x,奇數版本為開發版如1.7.x 32bit的mongoDB最大能存放2g的數據,64bit沒有限制 方法
Ubuntu Linux下安裝軟件方法
打開 right upload 提示 決定 .com 比較 .rpm .gz Linux系統中,軟件通常以源代碼或者預編譯包的形式提供。(1)軟件源代碼需要編譯為二進制的機器代碼才能夠使用,安裝比較耗時,不過您可以自行調節編譯選項,決定需要的功能或組件,或者針對硬件平臺作
linux下的QT打包方法
路徑 plist 方法 bug linux下 %s 文件的 linu 執行文件 一句話很簡單,一個shell腳本搞定,不跟你嘻嘻哈哈 #!/bin/shexe="ThorIceLocker"#存放你的可執行文件的名字des="/home/ninetripod/Desktop
linux下mycat自啟動方法
rc.d bsp home port shell 實現 都是 程序 start 每次開機都要啟動mycat,網上看了好多都是用shell腳本來實現mycat開機自啟動,後來看到一種方法,直接修改系統文件來實現,已經實踐過,方法有效。 1.修改腳本文件rc.local:vi
linux下解除安裝apache方法小結
先檢查是否安裝了apache 1)yum安裝檢查:yum list installed |grep httpd 2)rpm安裝檢查:rpm -qa |grep httpd 3)自己編譯安裝的有多種方式查詢,例如查詢httpd的執行程式,或者檢查程序ps -ef |grep httpd等方