将VC++的__try/__except EXCEPTION_STACK_OVERFLOW移植到MinGW
Porting VC++'s __try/__except EXCEPTION_STACK_OVERFLOW to MinGW
我正在尝试使用vc++的try-except语句移植一些代码到MinGW:
bool success = true;
__try {
//...
} __except ((EXCEPTION_STACK_OVERFLOW == GetExceptionCode())
? EXCEPTION_EXECUTE_HANDLER
: EXCEPTION_CONTINUE_SEARCH) {
success = false;
_resetstkoflw();
}
return success;
是否可以使用mingw++编写捕获堆栈溢出异常的代码?
你需要手动调用注册异常处理的Windows API函数;也就是说,AddVectoredExceptionHandler。注意,通过使用不尊重SEH异常的MinGW,抛出任何SEH异常或试图捕获任何此类异常将导致未定义的行为,因为正常的c++堆栈展开语义没有完成。(Windows怎么知道销毁堆栈上的所有std::string
?)
您还需要在希望调用SEH异常处理程序的时间结束时调用RemoveVectoredExceptionHandler
。
这一点并不为人所知,但是MinGW和MinGW-w64中的头文件<excpt.h>
提供了宏__try1
和__except1
来生成用于处理异常的gcc内联程序集。这些宏没有文档记录,也不容易使用。更糟的是。__try1
和__except1
的x86_64版本与32位版本不兼容。它们使用不同的回调函数,带有不同的参数和不同的返回值。
几个小时后,我几乎有了x86_64上的工作代码。我需要在MinGW的运行时中声明一个具有与_gnu_exception_handler
相同原型的回调。我的回调是
long CALLBACK
ehandler(EXCEPTION_POINTERS *pointers)
{
switch (pointers->ExceptionRecord->ExceptionCode) {
case EXCEPTION_STACK_OVERFLOW:
return EXCEPTION_EXECUTE_HANDLER;
default:
return EXCEPTION_CONTINUE_SEARCH;
}
}
我的try-except代码是
__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 %un", n, sum);
return 0;
它工作,直到我启用优化与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, @exceptnt"
".seh_handlerdatant"
".long 1nt"
".rva .l_startw, .l_endw, ehandler, .l_exceptwnt"
".section .text.startup, "x"n"
".l_startw:"
:::: except );
sum = sum1to(n);
__asm__ (".l_endw:");
printf("The sum from 1 to %u is %un", n, sum);
return 0;
except:
__asm__ (".l_exceptw:");
printf("Stack overflow!n");
return 1;
}
你可能想知道Windows如何在全栈上调用ehandler()
。所有这些对sum1to()
的递归调用必须留在堆栈上,直到我的处理程序决定做什么。Windows内核中有一些神奇的东西;当它报告堆栈溢出时,它还映射一个额外的堆栈页,以便ntdll.exe可以调用我的处理程序。如果我在处理程序上放置一个断点,我可以在gdb中看到这一点。在我的机器上,堆栈增长到地址0x54000。来自sum1to()
的堆栈帧在0x54000处停止,但是异常处理程序在从0x53000到0x54000的堆栈的额外页面上运行。Unix信号没有这样的魔力,这就是为什么Unix程序需要sigaltstack()
来处理堆栈溢出。
您可能需要查看LibSEH,以便为MinGW添加结构化异常处理兼容性。
MinGW不支持结构化异常的关键字;但是,正如Billy O'Neal在他的回答中所说,你可以调用某些本机函数来获得相同的效果。
问题是你是否想要同样的效果。我坚信结构化异常是一个错误。操作系统将告诉您的结构化异常列表包括"试图将整数除以0","无法使用传递给函数的HANDLE
参数","试图执行非法机器码指令"answers"试图在未经许可的情况下访问内存"。对于这些错误,您确实不能做任何明智的事情,但是结构化异常给了您机会:(1)声称您已经有了,(2)允许程序再多走一段时间。最好找出为什么代码试图除以0,传递一个无效的HANDLE
参数,试图访问内存未经允许这样做,等等,并修复代码,使其永远不会做。
有一种观点认为,您可以使用结构化异常来检测问题、显示对话框并退出。我不确定这是否比让操作系统显示一个对话框并退出程序更好(特别是如果操作系统在进程中向您发送一个迷你转储),这是未处理异常的默认行为。
- std::stack 是连续的吗?
- 为什么我会收到"Run-Time Check Failure #2 - Stack around the variable 'pr' was corrupted"错误?
- Incomings Call with Android Sip stack in Embarcadero C++ bui
- 堆叠协程 + gdb = "previous frame inner to this frame (corrupt stack)?"
- 生成质数的程序,错误:"Stack overflow"
- 如何在 x64 上"stack oveflow"例外
- 为什么此代码中显示"*** stack smashing detected ***: <unknown> terminated Aborted (core dumped) "错误?
- Qt Creator 在执行步骤 "make" 时出现编译错误,-fno-stack-limit
- 无法在基于 DFS 的任务排序程序中填充"stack"
- std::stack的奇怪行为,pop()返回相同的值
- 使用双LL在C++中实现Stack失败,出现异常"EXC_BAD_ACCESS(代码=2,地址=0x7fff5
- Ctypes: OSError: exception: stack overflow
- 使用 CFFI 释放内存时"MemoryError: Stack overflow"是什么意思?
- "recursive on all control paths, function will cause runtime stack overflow"超载<<运算符
- C语言中有没有类似'minimizing stack overflow of an array'的东西?
- "Stack overflow"错误。指针可能不在列表中
- std::map 导致内存不足情况下出现"stack overflow"
- "Lane User Stack Overflow"调试 CUDA 程序
- 递归函数错误:"stack overflow"
- 如何避免大矢量初始化"compiler limit: compiler stack overflow"?