Linux Segmentation Fault 段錯誤 產生原因除錯方法
轉載至:http://www.cnblogs.com/panfeng412/archive/2011/11/06/segmentation-fault-in-linux.html
http://blog.sina.com.cn/s/blog_69cc2f0b0100qglr.html1. 段錯誤是什麼
一句話來說,段錯誤是指訪問的記憶體超出了系統給這個程式所設定的記憶體空間,例如訪問了不存在的記憶體地址、訪問了系統保護的記憶體地址、訪問了只讀的記憶體地址等等情況。這裡貼一個對於“段錯誤”的準確定義(參考Answers.com):
A segmentation fault (often shortened to segfault) is a particular error condition that can occur during the operation of computer software. In short, a segmentation fault occurs whena program attempts to access a memory location that it is not allowed to access, or attempts to access a memory location in a way that is not allowed (e.g., attempts to write to a read-only location, or to overwrite part of the operating system). Systems based on processors like the Motorola 68000 tend to refer to these events as Address or Bus errors. Segmentation is one approach to memory management and protection in the operating system. It has been superseded by paging formost purposes, but much of the terminology of segmentation is still used, "segmentation fault" being an example. Some operating systems still have segmentation at some logical level although paging is used as the main memory management policy. On Unix-like operating systems, a process that accesses invalid memory receives the SIGSEGV signal. OnMicrosoft Windows, a process that accesses invalid memory receives the STATUS_ACCESS_VIOLATION exception.
2. 段錯誤產生的原因
2.1 訪問不存在的記憶體地址
#include<stdio.h> #include<stdlib.h> void main() { int *ptr = NULL; *ptr = 0; }
2.2 訪問系統保護的記憶體地址
#include<stdio.h> #include<stdlib.h> void main() { int *ptr = (int *)0; *ptr = 100; }
2.3 訪問只讀的記憶體地址
#include<stdio.h> #include<stdlib.h> #include<string.h> void main() { char *ptr = "test"; strcpy(ptr, "TEST"); }
2.4 棧溢位
#include<stdio.h>
#include<stdlib.h>
void main()
{
main();
}
等等其他原因。
3. 段錯誤資訊的獲取
程式發生段錯誤時,提示資訊很少,下面有幾種檢視段錯誤的發生資訊的途徑。
3.1 dmesg
dmesg可以在應用程式crash掉時,顯示核心中儲存的相關資訊。如下所示,通過dmesg命令可以檢視發生段錯誤的程式名稱、引起段錯誤發生的記憶體地址、指令指標地址、堆疊指標地址、錯誤程式碼、錯誤原因等。以程式2.3為例:
[email protected]:~/segfault$ dmesg [ 2329.479037] segfault3[2700]: segfault at 80484e0 ip 00d2906a sp bfbbec3c error 7 in libc-2.10.1.so[cb4000+13e000]
3.2 -g
使用gcc編譯程式的原始碼時,加上-g引數,這樣可以使得生成的二進位制檔案中加入可以用於gdb除錯的有用資訊。以程式2.3為例:
[email protected]:~/segfault$ gcc -g -o segfault3 segfault3.c
3.3 nm
使用nm命令列出二進位制檔案中的符號表,包括符號地址、符號型別、符號名等,這樣可以幫助定位在哪裡發生了段錯誤。以程式2.3為例:
[email protected]:~/segfault$ nm segfault3 08049f20 d _DYNAMIC 08049ff4 d _GLOBAL_OFFSET_TABLE_ 080484dc R _IO_stdin_used w _Jv_RegisterClasses 08049f10 d __CTOR_END__ 08049f0c d __CTOR_LIST__ 08049f18 D __DTOR_END__ 08049f14 d __DTOR_LIST__ 080484ec r __FRAME_END__ 08049f1c d __JCR_END__ 08049f1c d __JCR_LIST__ 0804a014 A __bss_start 0804a00c D __data_start 08048490 t __do_global_ctors_aux 08048360 t __do_global_dtors_aux 0804a010 D __dso_handle w __gmon_start__ 0804848a T __i686.get_pc_thunk.bx 08049f0c d __init_array_end 08049f0c d __init_array_start 08048420 T __libc_csu_fini 08048430 T __libc_csu_init U [email protected]@GLIBC_2.0 0804a014 A _edata 0804a01c A _end 080484bc T _fini 080484d8 R _fp_hw 080482bc T _init 08048330 T _start 0804a014 b completed.6990 0804a00c W data_start 0804a018 b dtor_idx.6992 080483c0 t frame_dummy 080483e4 T main U [email protected]@GLIBC_2.0
3.4 ldd
使用ldd命令檢視二進位制程式的共享連結庫依賴,包括庫的名稱、起始地址,這樣可以確定段錯誤到底是發生在了自己的程式中還是依賴的共享庫中。以程式2.3為例:
[email protected]:~/segfault$ ldd ./segfault3 linux-gate.so.1 => (0x00e08000) libc.so.6 => /lib/tls/i686/cmov/libc.so.6 (0x00675000) /lib/ld-linux.so.2 (0x00482000)
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.c
2、使用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. 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. (gdb)
3、進入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)
從輸出看出,程式2.3收到SIGSEGV訊號,觸發段錯誤,並提示地址0x001a306a、呼叫memcpy報的錯,位於/lib/tls/i686/cmov/libc.so.6庫中。
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、在處理變數時,注意變數的格式控制是否合理等。
6. 參考資料列表
1、http://www.docin.com/p-105923877.html
2、http://blog.chinaunix.net/space.php?uid=317451&do=blog&id=92412
7. Error number
這種資訊一般都是由記憶體訪問越界造成的,不管是使用者態程式還是核心態程式訪問越界都會出core, 並在系統日誌裡面輸出一條這樣的資訊。這條資訊的前面分別是訪問越界的程式名,程序ID號,訪問越界的地址以及當時程序堆疊地址等資訊,比較有用的資訊是 最後的error number. 在上面的資訊中,error number是4 ,下面詳細介紹一下error number的資訊:在上面的例子中,error number是6, 轉成二進位制就是110, 即bit2=1, bit1=1, bit0=0, 按照上面的解釋,我們可以得出這條資訊是由於使用者態程式讀操作訪問越界造成的。
error number是由三個字位組成的,從高到底分別為bit2 bit1和bit0,所以它的取值範圍是0~7.
bit2: 值為1表示是使用者態程式記憶體訪問越界,值為0表示是核心態程式記憶體訪問越界
bit1: 值為1表示是寫操作導致記憶體訪問越界,值為0表示是讀操作導致記憶體訪問越界
bit0: 值為1表示沒有足夠的許可權訪問非法地址的內容,值為0表示訪問的非法地址根本沒有對應的頁面,也就是無效地址
相關推薦
Linux Segmentation Fault 段錯誤 產生原因除錯方法
轉載至:http://www.cnblogs.com/panfeng412/archive/2011/11/06/segmentation-fault-in-linux.html http://blog.sina.com.cn/s/blog_69cc2f0b0100qgl
Segmentation Fault 段錯誤的原因分析
1 訪問不存在的記憶體地址 在一個三個節點的連結串列中,有pHeader->pNext->pNext我們將pHeader->pNext後面的節點全部刪除,因此只有pHeader->pNext 程式碼中,使用 pHeader-
Linux下的段錯誤產生的原因及除錯方法
系統保護的記憶體地址寫資料 最常見就是給一個指標以0地址 2)記憶體越界(陣列越界,變數型別不一致等)訪問到不屬於你的記憶體區域 解決方法 我們在用C/C++語言寫程式的時侯,記憶體管理的絕大部分工作都是需要我們來做的。實際上,記憶體管理是一個比較繁瑣的工作,無論你多高明,經驗多豐富,難免會在此處犯些小錯
Segmentation fault段錯誤除錯總結
Segmetation fault也叫做段錯誤,引發的原因有好多,這裡我們只說一下段錯誤發生時的除錯方法。 方法1:加列印printf。這是最基本的往往也很有效的方法,在哪裡Core掉就會在哪裡停止列印--一目瞭然。同時這種方法也存在一個致命缺陷:如果恰巧Core掉的地方沒
GDB遠端除錯(二)之用gdb解決segmentation-fault段錯誤,看gdb的使用(嵌入式)
最近在除錯程式碼的時候鬧人的segmentation-fault段錯誤,又來煩人了,不過程式碼都是自己寫的 ,有事也是作繭自縛,自作自受,先自責下。 來看看在串列埠段錯誤給了我們什麼資訊: 可以看出 Fault addr=0x328,出錯的地址在 0x328,這個哪 啊
關於Segmentation fault(段錯誤)探究
在編寫演算法競賽習題2.5時,資料無法正常輸入到檔案data1.out,用gdb除錯的時候,報錯: Program received signal SIGSEGV, Segmentation fault. __chkstk_ms () at …/…/…/src
linux核心發生段錯誤時進行除錯
發生段錯誤原因就是訪問了不該訪問的地址,例如訪問了不存在的記憶體地址、訪問了系統保護的記憶體地址、訪問了只讀的記憶體地址等。 下面根據Oops資訊來分析一下段錯誤 first_drv.c 點選(此處)摺疊或開啟 #include <linux/module.h
Linux下的段錯誤(Segmentation fault)產生的原因及除錯方法
段錯誤 就是訪問了錯誤的記憶體段,一般是你沒有許可權,或者根本就不存在對應的實體記憶體,尤其常見的是訪問0地址. 一 般來說,段錯誤就是指訪問的記憶體超出了系統所給這個程式的記憶體空間,通常這個值是由gdtr來儲存的是一個48位的暫存器, 前32位是儲存由它指
Linux環境下段錯誤(Segmentation fault)的產生原因及除錯方法小結
最近在Linux環境下做C語言專案,由於是在一個原有專案基礎之上進行二次開發,而且專案工程龐大複雜,出現了不少問題,其中遇到最多、花費時間最長的問題就是著名的“段錯誤”(Segmentation Fault)。藉此機會系統學習了一下,這裡對Linux環境下的段錯誤做個小結,
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環境下段錯誤的產生原因及除錯方法小結(轉)
轉自 最近在Linux環境下做C語言專案,由於是在一個原有專案基礎之上進行二次開發,而且專案工程龐大複雜,出現了不少問題,其中遇到最多、花費時間最長的問題就是著名的“段錯誤”(Segmentation Fault)。藉此機會系統學習了一下,這裡對Linux環境下的段錯誤
Linux環境下段錯誤的產生原因及除錯方法小結
最近在Linux環境下做C語言專案,由於是在一個原有專案基礎之上進行二次開發,而且專案工程龐大複雜,出現了不少問題,其中遇到最多、花費時間最長的問題就是著名的“段錯誤”(Segmentation Fault)。藉此機會系統學習了一下,這裡對Linux環境下的段錯誤做個小結
Linux 下的段錯誤(Segmentation fault)除錯方法(轉)
轉自:http://blog.sina.com.cn/s/blog_3e28c8a50100bnin.html 我們在用C/C++語言寫程式的時侯,記憶體管理的絕大部分工作都是需要我們來做的。實際上,記憶體管理是一個比
嵌入式 Linux下段錯誤的原因和除錯方式
[email protected]:~/segfault$ catchsegv ./segfault3 Segmentation fault (core dumped) *** Segmentation fault Register dump: EAX: 00000000 EBX: 00fb3f
Linux下如何生成core dump 文件(解決segment fault段錯誤的問題)
http alt 系統設置 images mit 只讀 功能 lin 設置 Linux下的C程序常常會因為內存訪問等原因造成segment fault(段錯誤),如果此時core dump 的功能是打開的,在運行我們的可執行程序時就會生成一個名為core的文件,然後我們就可
Linux環境下段錯誤查錯
段錯誤是指訪問的記憶體超出了系統給這個程式所設定的記憶體空間,例如訪問了不存在的記憶體地址、訪問了系統保護的記憶體地址、訪問了只讀的記憶體地址等等情況。 1 、訪問不存在的記憶體地址 #include<stdio.h> #include<s
Linux下的段錯誤分析
2. 段錯誤產生的原因 2.1 訪問不存在的記憶體地址 #include #includevoid main() {int *ptr = NULL;*ptr = 0; } 2.2 訪問系統保護的記憶體地址 #include #includevoid main() {
PHP請求https域名發生segment fault段錯誤
PHP使用file_get_contents或curl請求https的域名均會發生segment fault的錯誤。 問題PHP程式碼如下,執行該PHP命令會發生segment fault: var_dump(file_get_contents("https://www.
linux中的段錯誤
1. 段錯誤是什麼? 段錯誤:Segmentation Fault,簡單說,段錯誤是指訪問的記憶體超出了系統給這個程式所設定的記憶體空間,例如訪問了不存在的記憶體地址、訪問了系統保護的記憶體地址、訪問了只讀的記憶體地址等情況。這裡貼出“段錯誤的準確定義: A segmentation fau
gcc編譯出現segmentation fault的錯誤
segmentation fault 即段錯誤,一般都是出現了非法的地址寫操作導致的。 常見的有如下幾種情況: 1、空指標訪問。 如果指標為空(NULL), 那麼對空指標的讀寫操作都會導致segmentation fault。 如 1 2 3 ch