1. 程式人生 > >使用gdb除錯段錯誤(segment fault)

使用gdb除錯段錯誤(segment fault)

我們打算使用gdb去解決為什麼下面的程式(檔案為segfault.c)引起了段錯誤的問題。下面的這段程式是從使用者那裡讀入一行文字字串然後顯示在螢幕上。然而,如下當前的程式並不會如期執行...

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char **argv)
{
   char *buf;

   buf = malloc(1<<31);

   fgets(buf, 1024, stdin);
   printf("%s\n", buf);

   return 1;
}

 第一步是使用帶有除錯標誌(debugging flags)的方式編譯這段程式碼,如下:

~# gcc -g segfault.c

然後執行:

~# a.out

Hello World!

Segmentation fault

這並不是我們所期待的。是時候啟動強大的gdb了。

~# gdb a.out
GNU gdb 5.0
Copyright 2000 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "i686-pc-linux-gnu"...
(gdb) 

我們直接執行就來看看到底發生了什麼:

(gdb) run
Starting program: /home/dgawd/cpsc/363/a.out 
test string

Program received signal SIGSEGV, Segmentation fault.
0x4007fc13 in _IO_getline_info () from /lib/libc.so.6

我們收到了來自作業系統的SIGSEGV訊號。這就意味著我們試圖去訪問一段非法的記憶體,讓我們試試backtrace(= bt)命令:

(gdb) backtrace
#0  0x4007fc13 in _IO_getline_info () from /lib/libc.so.6
#1  0x4007fb6c in _IO_getline () from /lib/libc.so.6
#2  0x4007ef51 in fgets () from /lib/libc.so.6
#3  0x80484b2 in main (argc=1, argv=0xbffffaf4) at segfault.c:10
#4  0x40037f5c in __libc_start_main () from /lib/libc.so.6

這裡我們只關心我們自己的程式碼,因此我們就切換到3號堆疊幀(stack frame3)來看看程式在哪裡崩潰的:

(gdb) frame 3
#3  0x80484b2 in main (argc=1, argv=0xbffffaf4) at segfault.c:10
10        fgets(buf, 1024, stdin)

哦,原來是呼叫fgets引起的崩潰。一般的,我們都假設庫函式比如fgets都可以正確地工作(如果不是這樣的話,我們的麻煩就大了)。因此這個問題的原因就一定是其中我們的一個引數的問題。你也許不知道‘stdin’是一個全域性的變數,它是被stdio 庫建立的。因此我們假定這個引數是正確的。那麼剩下的就只能是‘buf’了,然後檢視buf當前的值:

(gdb) print buf
$1 = 0x0

buf的值是0x0,也就是NULL指標。這並不是我們鎖期待的 —— buf應該指向第8行程式碼分配到的記憶體。因此我們需要返回到第8行並看看在哪裡發生了什麼。首先kill掉我們程式當前執行的呼叫:

(gdb) kill
Kill the program being debugged? (y or n) y

(注意:不用使用quit直接退出gdb,這樣比較麻煩。直接kill掉當前的程式呼叫即可

然後在第8行設定一個斷點:

(gdb) break segfault.c:8
Breakpoint 1 at 0x8048486: file segfault.c, line 8.

再次執行程式:

(gdb) run
Starting program: /home/dgawd/cpsc/363/a.out 

Breakpoint 1, main (argc=1, argv=0xbffffaf4) at segfault.c:8
8         buf = malloc(1<<31);

我們檢查malloc呼叫前後buf值的變化。初始化buf以前,其值應該是一個隨機雜亂值(garbage),就像這裡的:

(gdb) print buf
$2 = 0xbffffaa8 "鰓?\177\[email protected]`\[email protected]\001"

我們step over(單步執行)malloc呼叫然後再次檢查buf的值:

(gdb) next
10        fgets(buf, 1024, stdin);
(gdb) print buf
$3 = 0x0

可見呼叫了malloc之後,buf是NULL。如果你檢視malloc的手冊頁(man page),你就會發現malloc在不能分配夠所需的記憶體的時候就會返回NULL。因此確定是我們的malloc失敗了。讓我們返回到程式碼再次看看:

7 :   buf = malloc(1<<31);

哦,表示式1<<31(整型1左移31次,原文錯寫為右移)是429497295, 或4GB (gigabytes).很少有機器會有這樣的記憶體——大多數只有256MB(顯然這篇文章有年頭了,都什麼年代了,這點記憶體作業系統估計啟動一半就掛了)。因此malloc必然會失敗。此外,在fgets中我們只讀入1024位元組。所有的額外空間都會白白浪費掉,儘管我們可以分配到。這裡我們將1<<31改為1024(或者1<<9),這樣程式就會按照我們的期望運行了:

~# a.out
Hello World!
Hello World!

這樣你就可以知道怎樣使用gdb來除錯段錯誤了,這是非常有用的。這個例子同時也說明了一個非常重要的準則:總是檢查malloc的返回值!擁有美好的一天(說實在,我讓段錯誤噁心了一天。但以後就應該不太噁心了,以後每一天都美好吧 ^_^)。

相關推薦

嵌入式 使用gdb除錯錯誤segment fault

我們打算使用gdb去解決為什麼下面的程式(檔案為segfault.c)引起了段錯誤的問題。下面的這段程式是從使用者那裡讀入一行文字字串然後顯示在螢幕上。然而,如下當前的程式並不會如期執行... [cpp] view plaincopyprint? <s

使用gdb除錯錯誤segment fault

我們打算使用gdb去解決為什麼下面的程式(檔案為segfault.c)引起了段錯誤的問題。下面的這段程式是從使用者那裡讀入一行文字字串然後顯示在螢幕上。然而,如下當前的程式並不會如期執行... #include <stdio.h> #include <

Linux下的錯誤Segmentation fault產生的原因及除錯方法

 段錯誤 就是訪問了錯誤的記憶體段,一般是你沒有許可權,或者根本就不存在對應的實體記憶體,尤其常見的是訪問0地址.         一 般來說,段錯誤就是指訪問的記憶體超出了系統所給這個程式的記憶體空間,通常這個值是由gdtr來儲存的是一個48位的暫存器,              前32位是儲存由它指

Linux下使用-static -lpthread靜態編譯出現錯誤Segmentation fault

最近在看golang,感覺go的靜態編譯思想很不錯。於是準備把手頭的幾個專案靜態編譯一下,結果編譯的時候沒報任何警告及錯誤,一執行就報段錯誤(Segmentation fault)。gdb逐步除錯,發現問題出在std::thread那裡。仔細檢查了下所有語法,沒

C/C++中的錯誤Segmentation fault

}3)其他其實大概的原因都是一樣的,就是段錯誤的定義。但是更多的容易出錯的地方就要自己不斷積累,不段發現,或者吸納前人已經積累的經驗,並且注意避免再次發生。例如:<1>定義了指標後記得初始化,在使用的時候記得判斷是否為NULL<2>在使用陣列的時候是否被初始化,陣列下標是否越界,陣列元

你的java/c/c++程式崩潰了?揭祕錯誤Segmentation fault3

前言 接上兩篇: 寫到這裡,越跟,越發現真的是核心上很白,非一般的白。 但是既然是研究,就定住心,把段錯誤搞到清楚明白。 本篇將作為終篇,來結束這個系列,也算是對段錯誤和程式除錯、尋找崩潰原因(通常不會給你那麼完美的stackstrace和人性化的錯

Gdb除錯斷點問題思考記錄

以 例: 輸出1-100之間的素數  為例 //func.c 在儲存退出後首先使用 Gcc 對 func.c 進行編譯,注意一定要加上選項“-g” ,這樣編譯出的可執行程式碼中才包含除錯資訊,否則之後 Gdb 無法載入該可執行檔案。 gcc -g func.c -o test

使用gdb除錯錯誤

我們打算使用gdb去解決為什麼下面的程式(檔案為segfault.c)引起了段錯誤的問題。下面的這段程式是從使用者那裡讀入一行文字字串然後顯示在螢幕上。然而,如下當前的程式並不會如期執行... #include <stdio.h> #include <st

Linux 程式設計錯誤segmentation error總結

最近一段時間在linux下用C做一些學習和開發,但是由於經驗不足,問題多多。而段錯誤就是讓我非常頭痛的一個問題。不過,目前寫一個一千行左右的程式碼,也很少出現段錯誤,或者是即使出現了,也很容易找出來,並且處理掉。     那什麼是段錯誤?段錯誤為什麼是個麻煩事?以及怎麼發現程

GDB除錯core檔案樣例如何定位Segment fault

core dump又叫核心轉儲, 當程式執行過程中發生異常, 程式異常退出時, 由作業系統把程式當前的記憶體狀況儲存在一個core檔案中, 叫core dump. (linux中如果記憶體越界會收到SIGSEGV訊號,然後就會core dump)在程式執行的過程中,有的時候我們會遇到Segment fault

Linux下除錯錯誤的方法[Segmentation Fault]--GDB

4. 段錯誤的除錯方法 4.1 使用printf輸出資訊 這個是看似最簡單但往往很多情況下十分有效的除錯方式,也許可以說是程式設計師用的最多的除錯方式。簡單來說,就是在程式的重要程式碼附近加上像printf這類輸出資訊,這樣可以跟蹤並打印出段錯誤在程式碼中可能出現的位置。為了方便使用這種方法,可以使用

錯誤核心已轉儲問題的分析方法(未成功)

是否產生core dump ulimit -c 0 #unlimited太大,沒有必要。 ulimit -c 10000 ulimit -c unlimited 用上面命令只會對當前的終端環境有效.如果想需要永久生效,修改檔案 /etc/security/limit

系統技術非業餘研究 » 如何用gdb除錯erlang執行期高階

前些天在erlang的原始碼裡面閒逛的時候發現了 bin目錄下的cerl,一看原來是個除錯的高階貨。 我之前寫過一篇文章http://mryufeng.javaeye.com/blog/113852 如何除錯erlang 但是這是土八路的方法, cerl才是現代化工業。 # This is a s

C語言 --錯誤核心已轉儲

文章原地址:https://blog.csdn.net/qq_29350001/article/details/53780697 (侵刪欠)一、什麼是段錯誤?一旦一個程式發生了越界訪問,cpu 就會產生相應的保護,於是 segmentation fault 就出現了,通過上面

C語言再學習 -- 錯誤核心已轉儲

一、什麼是段錯誤?一旦一個程式發生了越界訪問,cpu 就會產生相應的保護,於是 segmentation fault 就出現了,通過上面的解釋,段錯誤應該就是訪問了不可訪問的記憶體,這個記憶體區要麼是不存在的,要麼是受到系統保護的,還有可能是缺少檔案或者檔案損壞。二、段錯誤產

錯誤核心已轉儲問題的分析方法

問題現象 今天在研究linux kernel中typedef的用法時,寫了一個程式test_typedef.c,內容如下: #include<stdio.h> typedef int size; typedef unsigned int wor

創建maven項目pom.xml出現錯誤依賴Missing

可能 現在 發現 block cnblogs 分享 version 但是 模塊 Maven的依賴問題 在聚合模塊時候,發現在父工程目錄中的依賴存在一些問題。一開始是${pagehelper.version},後來將版本直接填寫相應的版本如圖下 依賴添加失敗 *

C++學習1:最大子多種解法

多少 問題: code namespace 數據 組成 amp using () 問題:給定由n個數(可能為負數)組成的序列a1,a2,a3,...,an,求該序列子段和的最大值。 第一種解法:(最容易考慮的方法,將所有的子段一一相加,然後比較) 1 #include&

C相關的常見錯誤更新ing

錯誤 nbsp http 相關 post 指定 strong 常見 log 01.只允許在C99模式下使用 for 循環初始化申明 解決:指定下即可 gcc -o xxx -std=c99 02. C相關的常見錯誤(更新ing)

codevs 3981 動態最大子線段樹

輸入 typedef fault namespace 一行 scrip img sum spl 題目傳送門:codevs 3981 動態最大子段和 題目描述 Description 題目還是簡單一點好... 有n個數,a[1]到a[n]。 接下來q次查詢,每次動