如何在Windows x64 C ++应用程序中捕获堆栈溢出

时间:2015-03-30 14:47:19

标签: c++ windows visual-studio-2012

我正在尝试将应用程序编译到Windows中的x64平台体系结构。处理脚本语言解析的几个线程使用此代码recommended by Microsoft to trap stack overflows and avoid access violation exceptions

__try
{
    DoSomethingThatMightUseALotOfStackMemory();
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
    LPBYTE lpPage;
    static SYSTEM_INFO si;
    static MEMORY_BASIC_INFORMATION mi;
    static DWORD dwOldProtect;

    // Get page size of system
    GetSystemInfo(&si);

    // Find SP address
    _asm mov lpPage, esp;

    // Get allocation base of stack
    VirtualQuery(lpPage, &mi, sizeof(mi));

    // Go to page beyond current page
    lpPage = (LPBYTE)(mi.BaseAddress)-si.dwPageSize;

    // Free portion of stack just abandoned
    if (!VirtualFree(mi.AllocationBase,
                    (LPBYTE)lpPage - (LPBYTE)mi.AllocationBase,
                     MEM_DECOMMIT))
    {
        exit(1);
    }

    // Reintroduce the guard page
    if (!VirtualProtect(lpPage, si.dwPageSize, 
                        PAGE_GUARD | PAGE_READWRITE, 
                        &dwOldProtect))
    {
        exit(1);
    }
    Sleep(2000);
}

不幸的是,它使用一行内联汇编程序来获取堆栈指针。 Visual Studio不支持x64模式的内联汇编,我也找不到compiler intrinsic来获取堆栈指针。

是否可以以x64友好的方式执行此操作?

1 个答案:

答案 0 :(得分:2)

正如对该问题的评论所指出的,上面的整个“黑客”可以被_resetstkoflw函数取代。这在x86和x64模式下均可正常工作。

上面的代码段变为:

// Filter for the stack overflow exception. This function traps
// the stack overflow exception, but passes all other exceptions through. 
int stack_overflow_exception_filter(int exception_code)
{
    if (exception_code == EXCEPTION_STACK_OVERFLOW)
    {
        // Do not call _resetstkoflw here, because at this point
        // the stack is not yet unwound. Instead, signal that the
        // handler (the __except block) is to be executed.
        return EXCEPTION_EXECUTE_HANDLER;
    }
    else
        return EXCEPTION_CONTINUE_SEARCH;
}

void example()
{
    int result = 0;
    __try
    {
        DoSomethingThatMightUseALotOfStackMemory();
    }
    __except(stack_overflow_exception_filter(GetExceptionCode()))
    {
        // Here, it is safe to reset the stack.
        result = _resetstkoflw();
    }

    // Terminate if _resetstkoflw failed (returned 0)
    if (!result)
        return 3;

    return 0;
}