C++ 为什么';t SIGSEV会崩溃进程?

C++ 为什么';t SIGSEV会崩溃进程?,c++,exception,mingw,C++,Exception,Mingw,我正在尝试实现breakpad,以获取跨平台Qt应用程序的崩溃报告和堆栈跟踪。我想我实现了所有必要的代码,但我无法让应用程序在Windows上可靠崩溃 我使用mingwgcc编译器和Qt 我在UI中创建了一个按钮 void crash() { printf(NULL); int* x = 0; *x = 1; int a = 1/0; } /* .... */ connect(ui->btnCrash, SIGNAL(clicked()),this,SLO

我正在尝试实现breakpad,以获取跨平台Qt应用程序的崩溃报告和堆栈跟踪。我想我实现了所有必要的代码,但我无法让应用程序在Windows上可靠崩溃

我使用mingwgcc编译器和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在调用this 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;
}
#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;
}
系统返回码是什么

$ echo $?
139
当一个程序因致命信号而被终止时,Linux将向调用者返回128+的信号号。所以 这是128+11,即128+
SIGSEGV

这就是在Linux上,当程序尝试取消对空指针的引用时所发生的情况。 这就是Linux对这个行为不端的程序所做的:它杀死了它并返回给我们 128+
SIGSEGV
。这不是程序所做的:它不处理任何信号

现在,我将跳入Windows10VM,用 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
与以前一样,程序崩溃,系统代码
0xc000005

再上一次:

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

>prog

>echo %errorlevel%
-1073741819
没有变化

在Windows上,当程序尝试取消对空指针的引用时,就会发生这种情况。 这就是Windows对行为不端的程序所做的:它杀死它并返回给我们
0xc0000005

对于行为不端的C程序,我们没有什么可以感谢的 无论我们用它进行编译,Windows都会用它做同样的事情MinGW-W64
gcc
或MS
cl
。我们也不能因为Windows 它与Linux做的事情不同

事实上,同样的事情发生了,这一点我们都不能感谢 在我们刚刚运行它的时候,使用GCC编译的行为不端的程序。因为C (或C++)标准不承诺取消对空指针的引用将导致
SIGSEGV
失效 被提升(或者除以0将导致
SIGFPE
,依此类推)。它只是承诺 此操作导致未定义的行为,包括可能导致
SIGSEGV
当程序在
gdb
下运行时,在周二,否则不运行

事实上,该程序确实在我们的三个系统中导致了
SIGSEGV
编译场景,我们可以通过给程序一个相应的处理程序来观察 信号:

main_1.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;
}
#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;
}
在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
处理程序的行为不同
main_1.c

所以你对这个问题有了答案:

我如何在不同平台上有相同的行为

在实践中,这是最好的:-

您可以在程序中处理信号,并将信号处理程序限制为 在您的首选范围内,跨平台行为相同的代码 同样的意思

这个答案在实践中是最好的,因为在原则上, 根据语言标准,您不能依赖导致未定义错误的操作 引起任何特定信号或产生任何特定或一致结果的行为。 如果事实上您的目标是实现一致的跨平台处理 致命信号,然后进行相应的函数调用,以激发测试信号
sig
目的是由标准头(在C++中)提供的,
):

向程序发送信号
sig


问题是为什么SIGSEGV、SIGFPE会在Linux上崩溃进程,而在Windows上不会。我怎么会有同样的行为?实现崩溃转储报告程序的目的是从这些bug中获取崩溃报告,这样我就可以修复它们,而不是忽略它们。@AdrianSuciu是的,我试图向您解释,没有可靠的方法可以从未定义的行为中获取崩溃报告,如果你想防止撤销对空指针的引用,你需要在之前而不是之后进行检查。一个Optimizer很可能会将该函数的主体替换为零。正如我所说,当用gdb运行同一个可执行文件时。它抓住了西格夫。当我在没有调试器的情况下运行它时,什么都不会发生。即使在调试器中捕获SIGSEGV也不会使进程崩溃。为什么?
Thread 1 received signal SIGSEGV, Segmentation fault.
0x0000000000401584 in shoot_my_foot () at main.c:5
5               return *x;
int raise( int sig )