我目前正在破解解决方案,使错误更易于调试。
所以我已经定义了main
,用户需要定义wrappermain
来取代main
的使用:
int main(int ac, char** av)
{
__try {
return wrappermain(ac, av);
} __except(HandleException(GetExceptionCode(), GetExceptionInformation()) {
return 1;
}
}
现在在HandleException
函数中我加载了符号信息并设置了一些选项:
if (!SymInitialize(process, symSearchPath, false))
return;
DWORD symOptions = SymGetOptions();
symOptions |= SYMOPT_LOAD_LINES | SYMOPT_UNDNAME;
SymSetOptions(symOptions);
这个构造就像一个魅力,但是当我用无尽的递归引起堆栈溢出时,我在调用SymSetOptions(symOptions)
时遇到访问冲突
那可能是什么以及如何摆脱那次崩溃?
答案 0 :(得分:1)
这里的核心问题显然是堆栈。您有堆栈溢出(损坏/不可用),并且您希望使用基于堆栈的异常处理程序来处理它。这不会很好。我甚至惊讶你这么远;必须通过遍历调用堆栈找到处理程序来找到您的异常处理程序。如果存在溢出并且该堆栈的一部分在操作中缺失,那么行走堆栈将成为一个问题。
无论如何,我怀疑直接原因是堆栈上没有防护页面。堆栈未预先完全分配。相反,在分配结束时有一个保护页面。推送过多会导致页面错误,这通常会导致堆栈成功增长。但是当没有剩余空间时,溢出是致命的,并且防护页面消失了。
修复很可能是另一个线程。它将轮询一个全局标志(DWORD
如此原子)并在没有任何反应时休眠。您的异常处理程序只需要设置标志,并且不需要工作堆栈。这解锁了帮助程序线程,然后(使用其新堆栈)调用相关方法。
向量异常处理可能看起来像一种变通方法,但它对您在处理程序中可以执行的操作有严格的限制。
答案 1 :(得分:1)
“那可能是什么” - 这一定是必须的。因为没有足够的堆栈空间。在HandleException中,您在发生异常的相同堆栈点运行。你只有1页(4096字节)的堆栈空间。因此需要大堆栈空间的函数调用再次引发异常。而这个新的已经是致命的 - 因为根本没有堆栈空间,甚至1页也不存在。所以,如果你查看STATUS_STACK_OVERFLOW - 你必须只返回EXCEPTION_EXECUTE_HANDLER并且已经在__except(){..}块中(此时堆栈指针已经将恢复)再次检查if(GetExceptionCode()== STATUS_STACK_OVERFLOW) - de allocate some stack空格并在底部分配的页面中再次设置PAGE_GUARD。如果不这样做 - 你的应用程序的下一个堆栈溢出已经是致命的(你的堆栈中没有1页)
答案 2 :(得分:0)
EXCEPTION_POINTERS ep;
__try {
return wrappermain(ac, av);
}
__except(memcpy(&ep,GetExceptionInformation(), sizeof(EXCEPTION_POINTERS)), HandleException(GetExceptionCode(),GetExceptionInformation())){
if (GetExceptionCode() == STATUS_STACK_OVERFLOW)
{
#ifdef _WIN64
#define GUARD_PAGE_COUNT 3
#define COMMIT_PAGE_COUNT 6
#else
#define GUARD_PAGE_COUNT 1
#define COMMIT_PAGE_COUNT 2
#endif
//in ntdll exist special api for this - RtlResetStackOverflow, but it not exported
BOOL fOk = FALSE;
::MEMORY_BASIC_INFORMATION mbi;
if (VirtualQuery(&mbi, &mbi, sizeof(mbi)))
{
if ((GUARD_PAGE_COUNT + COMMIT_PAGE_COUNT) << PAGE_SHIFT < RtlPointerToOffset(mbi.AllocationBase, mbi.BaseAddress))
{
mbi.BaseAddress = (PBYTE)mbi.BaseAddress - ((GUARD_PAGE_COUNT + COMMIT_PAGE_COUNT) << PAGE_SHIFT);
if (mbi.AllocationBase < mbi.BaseAddress)
{
ULONG OldProtect;
fOk = VirtualFree(mbi.AllocationBase, RtlPointerToOffset(mbi.AllocationBase, mbi.BaseAddress), MEM_DECOMMIT)
&&
VirtualProtect(mbi.BaseAddress, GUARD_PAGE_COUNT << PAGE_SHIFT, PAGE_READWRITE|PAGE_GUARD, &OldProtect);
}
}
}
}
}