簡體   English   中英

SIGSEGV為什么不使過程崩潰?

[英]Why doesn't SIGSEGV crash the process?

我正在嘗試實現Breakpad來獲取跨平台Qt應用程序的崩潰報告和堆棧跟蹤。 我想我實現了所有必要的代碼,但是我無法使應用程序在Windows上可靠地崩潰。

我使用MinGW gcc編譯器和Qt。

我在用戶界面中創建了一個按鈕。

void crash() {
    printf(NULL);
    int* x = 0;
     *x = 1;
    int a = 1/0;
}
/* .... */
connect(ui->btnCrash, SIGNAL(clicked()),this,SLOT(crash()));

當單擊按鈕時,什么也沒有發生。 但是,在調試模式下運行時,調試器(gdb)在第一個函數調用時檢測到SIGSEGV,然后放棄運行該方法的其余部分。 當我故意在代碼中的其他地方進行非法操作時,我會注意到相同的行為。 這會導致意外/不確定的行為。

現在,此行為不同於Linux,后者在調用該crash()時,該進程已正確崩潰,並創建了轉儲。

那有什么區別呢? 我如何在平台之間具有相同的行為?

您的代碼中存在未定義的行為

*x = 1;

因為您不應取消引用空指針。 實際上,我不確定是否要除以零,但是一旦您脫離困境,所有賭注都將變為無效。

如果要發信號SIGSEGV請執行此操作,但不要使用未定義的行為,這可能會使您的代碼執行任何操作。 您不應期望您的代碼有任何輸出,而應該對其進行修復;)。

這是嘗試取消引用空指針的最小控制台程序的源代碼

main.c中

#include <stdio.h>

int shoot_my_foot() {
    int* x = 0;
    return *x;
}

int main()
{
    int i = shoot_my_foot();
    printf("%d\n",i);
    return 0;
}

我將在(Ubuntu 18.04)Linux上編譯並運行它:

$ gcc -Wall -Wextra -o prog main.c
$ ./prog
Segmentation fault (core dumped)

系統返回碼是什么?

$ echo $?
139

當程序因致命信號而被殺死時,Linux將128 +信號號返回給調用者。 就是128 + 11,即128 + SIGSEGV

在Linux上,當程序嘗試取消引用空指針時,就會發生這種情況。 這就是Linux對行為異常的程序所做的 :它殺死了該程序 ,並向我們退回了128 + SIGSEGV 這不是程序執行的操作 :它不處理任何信號。

現在,我跳入Windows 10 VM,並使用Microsoft C編譯器編譯並運行相同的程序:

>cl /Feprog /W4 main.c
Microsoft (R) C/C++ Optimizing Compiler Version 19.11.25547 for x64
Copyright (C) Microsoft Corporation.  All rights reserved.

main.c
Microsoft (R) Incremental Linker Version 14.11.25547.0
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:prog.exe
main.obj

>prog

>

沒有。 因此程序崩潰了,並且:

>echo %errorlevel%
-1073741819

系統返回碼是-1073741819 ,這是帶符號的整數值0xc0000005 ,著名的Windows錯誤代碼表示訪問沖突

仍然在Windows中,我現在將使用GCC編譯並運行該程序:

>gcc --version
gcc (x86_64-posix-seh-rev0, Built by MinGW-W64 project) 7.2.0

>gcc -Wall -Wextra -o prog.exe main.c

>prog

>echo %errorlevel%
-1073741819

和以前一樣,程序崩潰,系統代碼0xc0000005

從頂部再來一次:

>gcc -Wall -Wextra -o prog.exe main.c

>prog

>echo %errorlevel%
-1073741819

沒變。

Windows上 ,當程序嘗試取消引用空指針時,就會發生這種情況。 這就是Windows對行為異常的程序所做的:它殺死它並返回0xc0000005

對於表現不佳的C程序,我們沒有任何感謝,因為無論我們用MinGW-W64 gcc還是MS cl編譯,Windows都會對Windows進行相同的處理。 對於Windows與Linux所做的不同,我們對此無可厚非。

的確,我們什至沒有什么要感謝的事實,就是當我們剛運行GCC時,用GCC編譯的行為異常的程序也發生了同樣的事情。 因為C(或C ++)標准不保證解引用空指針將導致SIGSEGV升高(或者被0除將導致SIGFPE ,依此類推)。 它只是保證該操作會導致未定義的行為 ,包括在星期二在gdb下運行該程序時可能導致SIGSEGV ,否則不會這樣做。

實際上,該程序確實在我們的所有三種編譯方案中都導致了SIGSEGV ,正如我們可以通過為該程序提供該信號的處理程序來觀察到的:

main_1.c

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

static void handler(int sig)
{
    assert(sig == SIGSEGV);
    fputs("Caught SIGSEGV\n", stderr);
    exit(128 + SIGSEGV);
}

int shoot_my_foot(void) {
    int* x = 0;
    return *x;
}

int main(void)
{
    int i;
    signal(SIGSEGV, handler);
    i = shoot_my_foot();
    printf("%d\n",i);
    return 0;
}

在Linux上:

$ gcc -Wall -Wextra -o prog main_1.c
$ ./prog
Caught SIGSEGV
$ echo $?
139

在Windows上,使用MinGW-W64 gcc`:

>gcc -Wall -Wextra -o prog.exe main_1.c

>prog
Caught SIGSEGV

>echo %errorlevel%
139

在Windows上,使用MS cl

>cl /Feprog /W4 main_1.c
Microsoft (R) C/C++ Optimizing Compiler Version 19.11.25547 for x64
Copyright (C) Microsoft Corporation.  All rights reserved.

main_1.c
Microsoft (R) Incremental Linker Version 14.11.25547.0
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:prog.exe
main_1.obj

>prog
Caught SIGSEGV

>echo %errorlevel%
139

這種一致的行為不同於我們在gdb下的原始程序所觀察到的行為:

>gcc -Wall -Wextra -g -o prog.exe main.c

>gdb -ex run prog.exe
GNU gdb (GDB) 8.0.1
Copyright (C) 2017 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 "x86_64-w64-mingw32".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from prog.exe...done.
Starting program: C:\develop\so\scrap\prog.exe
[New Thread 6084.0x1e98]
[New Thread 6084.0x27b8]

Thread 1 received signal SIGSEGV, Segmentation fault.
0x0000000000401584 in shoot_my_foot () at main.c:5
5               return *x;
(gdb)

原因是gdb默認情況下會為所有致命信號安裝信號處理程序,並且其SIGSEGV處理程序的行為是輸出以下內容:

Thread 1 received signal SIGSEGV, Segmentation fault.
0x0000000000401584 in shoot_my_foot () at main.c:5
5               return *x;

並轉到gdb提示符,這與我們在main_1.c安裝的SIGSEGV處理程序的行為不同。

因此,您對問題有一個答案:

我如何在平台之間具有相同的行為?

在實踐中所獲得的好處:

您可以在程序中處理信號,並將信號處理程序限制為在相同平台的首選含義范圍內,其行為在平台之間相同的代碼

在實踐中,這個答案只能說是正確的,因為原則上 ,根據語言標准,您不能依賴於導致未定義行為發出任何特定信號或具有任何特定甚至一致結果的操作。 如果實際上您的目標是實現對致命信號的一致跨平台處理 ,則標准標頭<signal.h> (在C ++中, <csignal> )提供用於測試目的的<csignal>信號sig的適當函數調用。 :

int raise( int sig )

發送信號sig到程序。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM