将除了EXCEPTION_STACK_OVERFLOW之外的VC++的__try/__移植到MinGW

Dan*_*ien 7 c++ mingw visual-c++ try-except

我试图使用VC++的try-except语句将一些代码移植到MinGW:

bool success = true;

__try {
    //...
} __except ((EXCEPTION_STACK_OVERFLOW == GetExceptionCode())
            ? EXCEPTION_EXECUTE_HANDLER
            : EXCEPTION_CONTINUE_SEARCH) {
    success = false;
    _resetstkoflw();
}
return success;
Run Code Online (Sandbox Code Playgroud)

是否可以编写使用MinGW g ++捕获堆栈溢出异常的代码?

Bil*_*eal 9

您需要手动调用注册异常处理的Windows API函数; 即AddVectoredExceptionHandler.请注意,通过使用不遵守SEH异常的MinGW,抛出任何SEH异常或尝试捕获任何此类异常将导致未定义的行为,因为未完成正常的C++堆栈展开语义.(Windows如何知道核对std::string堆栈中的所有内容?)

您还需要RemoveVectoredExceptionHandler在您希望调用SEH异常处理程序的时间结束时调用.

通常MinGW缺乏对SEH和COM等Windows功能的支持.您尝试使用它而不是MSVC++的任何原因(假设两个编译器都是免费的?)


Geo*_*ler 7

这是众所周知的,但<excpt.h>MinGW和MinGW-w64中的头文件提供了宏__try1__except1生成用于处理异常的gcc内联汇编.这些宏没有记录,也不容易使用.它变得更糟.的x86_64的版本__try1,并__except1没有与32位版本兼容.它们使用具有不同参数和不同返回值的不同回调.

几个小时后,我几乎在x86_64上运行了代码.我需要使用与_gnu_exception_handlerMinGW运行时相同的原型声明回调.我的回调是

long CALLBACK
ehandler(EXCEPTION_POINTERS *pointers)
{
    switch (pointers->ExceptionRecord->ExceptionCode) {
    case EXCEPTION_STACK_OVERFLOW:
        return EXCEPTION_EXECUTE_HANDLER;
    default:
        return EXCEPTION_CONTINUE_SEARCH;
    }
}
Run Code Online (Sandbox Code Playgroud)

而我的尝试 - 除了代码是

    __try1 (ehandler) {
        sum = sum1to(n);
        __asm__ goto ( "jmp %l[ok]\n" :::: ok);
    } __except1 {
        printf("Stack overflow!\n");
        return 1;
    }
ok:
    printf("The sum from 1 to %u is %u\n", n, sum);
    return 0;
Run Code Online (Sandbox Code Playgroud)

它一直工作,直到我启用优化gcc -O2.这导致汇编程序错误,因此我的程序不再编译.的__try1__except1宏由在GCC 5.0.2从移动功能的优化破碎.text到一个不同的部分.

当宏工作时,控制流程是愚蠢的.如果发生堆栈溢出,程序就会跳过__except1.如果堆栈溢出没有发生,程序就会落到__except1同一个地方.我需要我的怪异__asm__ goto才能跳到ok:并防止坠落.我无法使用,goto ok;因为gcc会__except1因无法访问而删除.

再过几个小时,我修好了我的程序.我复制并修改了汇编代码以改进控制流程(不再跳转到ok:)并在gcc -O2优化后继续存在.这段代码很难看,但它对我有用:

/* gcc except-so.c -o except-so */
#include <windows.h>
#include <excpt.h>
#include <stdio.h>

#ifndef __x86_64__
#error This program requires x86_64
#endif

/* This function can overflow the call stack. */
unsigned int
sum1to(unsigned int n)
{
    if (n == 0)
        return 0;
    else {
        volatile unsigned int m = sum1to(n - 1);
        return m + n;
    }
}

long CALLBACK
ehandler(EXCEPTION_POINTERS *pointers)
{
    switch (pointers->ExceptionRecord->ExceptionCode) {
    case EXCEPTION_STACK_OVERFLOW:
        return EXCEPTION_EXECUTE_HANDLER;
    default:
        return EXCEPTION_CONTINUE_SEARCH;
    }
}

int main(int, char **) __attribute__ ((section (".text.startup")));

/*
 * Sum the numbers from 1 to the argument.
 */
int
main(int argc, char **argv) {
    unsigned int n, sum;
    char c;

    if (argc != 2 || sscanf(argv[1], "%u %c", &n, &c) != 1) {
        printf("Argument must be a number!\n");
        return 1;
    }

    __asm__ goto (
        ".seh_handler __C_specific_handler, @except\n\t"
        ".seh_handlerdata\n\t"
        ".long 1\n\t"
        ".rva .l_startw, .l_endw, ehandler, .l_exceptw\n\t"
        ".section .text.startup, \"x\"\n"
        ".l_startw:"
            :::: except );
    sum = sum1to(n);
    __asm__ (".l_endw:");
    printf("The sum from 1 to %u is %u\n", n, sum);
    return 0;

except:
    __asm__ (".l_exceptw:");
    printf("Stack overflow!\n");
    return 1;
}
Run Code Online (Sandbox Code Playgroud)

您可能想知道Windows如何调用ehandler()完整堆栈.所有那些递归调用sum1to()必须保留在堆栈中,直到我的处理程序决定要做什么.Windows内核中有一些魔力; 当它报告堆栈溢出时,它还会映射一个额外的堆栈页面,以便ntdll.exe可以调用我的处理程序.如果我在处理程序上放置一个断点,我可以在gdb中看到这个.堆栈增长到我的机器上的地址0x54000.堆栈帧sum1to()停止在0x54000,但异常处理程序在堆栈的额外页面上运行,从0x53000到0x54000.Unix信号没有这样的魔力,这就是Unix程序需要sigaltstack()处理堆栈溢出的原因.

  • 有趣.成为[真正的男人](http://www.ee.ryerson.ca/~elf/hack/realmen.html)和黑客攻击的+1;) (4认同)