SIGSEGV为什么不使过程崩溃?

时间:2019-04-05 10:00:12

标签: c++ exception mingw

我正在尝试实现Breakpad来获取跨平台Qt应用程序的崩溃报告和堆栈跟踪。我想我已经实现了所有必需的代码,但是我无法使应用程序在Windows上可靠地崩溃。

我使用MinGW gcc编译器和Qt。

我在UI中创建了一个按钮。

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

单击按钮时,什么都没有发生。但是,在调试模式下运行时,调试器(gdb)在第一个函数调用时检测到SIGSEGV,然后放弃运行该方法的其余部分。当我故意在代码中的其他地方进行非法操作时,我会注意到相同的行为。这会导致意外/不确定的行为。

现在,这种行为与Linux不同,在Linux上,当调用该crash()时,进程将完全崩溃,并创建转储。

那有什么区别?我如何在跨平台上具有相同的行为?

2 个答案:

答案 0 :(得分:1)

您的代码在

中具有未定义的行为
*x = 1;

因为您不应取消引用空指针。实际上,我不确定是否要除以零,但是一旦您脱离困境,所有赌注都将变为无效。

如果您想发信号SIGSEGV,请这样做,但不要使用未定义的行为,这可能会导致您的代码执行任何操作。您不应该期望代码有任何输出,而应该对其进行修复;)。

答案 1 :(得分:1)

这里是最小的尝试控制台程序的源 取消引用空指针

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进行编译,Windows都会对它执行相同的操作 或MS cl。对于Windows,我们对此无可厚非。 与Linux的功能不同。

的确,我们没有什么可以感谢的,因为同一件事发生了 到我们刚刚运行它的时候,都使用GCC编译的行为异常的程序。因为C (或C ++)标准不保证取消引用空指针将导致SIGSEGV 被提高(否则除以0将导致SIGFPE,依此类推)。它只是承诺 此操作导致不确定的行为,包括可能导致SIGSEGV 该程序在gdb下运行,则在星期二运行,否则不运行。

事实上,程序确实在我们的所有三个中都导致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提示符,这与我们安装在其中的SIGSEGV处理程序的行为不同 main_1.c

因此,您对这个问题有一个答案:

  

如何在跨平台上具有相同的行为?

在实践中表现得最好:-

您可以在程序中处理信号,并将信号处理程序限制为 行为在您偏好的平台之间相同的代码 相同的意思。

在实践中,这个答案只能说是正确的,因为在原理中, 根据语言标准,您不能依赖导致 undefined的操作 行为以发出任何特定信号,或产生任何特定甚至一致的结果。 如果实际上您的目标是实施一致的跨平台处理 致命信号,然后调用适当的功能以激发信号sig进行测试 目的由标准标头<signal.h>提供(在C ++中, <csignal>):

int raise( int sig )
  

将信号sig发送到程序。