2011-11-16 91 views
0

冒落代码我需要在运行时生成的代码,执行以下操作:与异常支持

auto v_cleanup = std::shared_ptr<void>(nullptr, [](void *){ cleanup(); }); 

//... 
do_some_danger_thing(); 
//... 

或C当量:

__try { 
    //... 
    do_some_danger_thing(); 
    //... 
} __finally { 
    cleanup(); 
} 

的清理()函数保证无例外,但do_some_danger_thing()可能会抛出异常。此运行时代码不得使用堆栈,这意味着在调用do_some_danger_thing()时,堆栈必须与我们输入运行时代码时的状态相同,除了设置为运行时代码的返回地址(原始值保存为“jmp “目标,以便返回给调用者)。

由于我们使用的是动态机器代码,目标平台固定在x86 CPU上的WIN32上,所以x64 CPU当前未处于焦点状态。

要做到这一点,我们必须处理任何例外。在WIN32中,C++异常是基于SEH的,所以我们必须对此负责。麻烦的是,我们无法找到一种方法来做到这一点,并使其与其他代码兼容。我们尝试了几个解决方案,但都没有工作,有时用户安装的异常处理程序从未被调用,有时外部异常处理程序被绕过,并且我们收到“未处理的异常”错误。

UPDATE:

这似乎是SEH异常处理程序链只支持EXE图像内代码的情况。如果异常处理程序指向我生成的代码,它将永远不会被调用。我必须做的是创建一个静态异常处理函数存根,然后让它调用生成的处理函数。

+0

这两个代码片段并不相同 - 首先,如果引发异常,您的第一个片段只会调用cleanup();你的第二个片段将永远称之为它。另外,为什么没有堆栈,还是只能恢复,或...?为什么要实现自己的异常处理?我认为这个问题需要更多关于你想要实现的细节。请解释你在做什么,为什么等。 –

+0

你是正确的代码片段...我会解决它。关于“为什么实现你自己”的问题,我相信我已经解释了:我将在运行时生成机器代码,异常处理只是故事的一部分。是的,您可以在调用do_some_danger_thing之前恢复堆栈,并且如果您决定这样做,则必须支付更多生成的代码的成本。 –

回答

1

我现在有一个与上面略有不同的实现。实际上,伪代码如下所示(在C++ 11):

std::exception_ptr ex; 
try { 
    //... 
    do_some_danger_things(); 
    //... 
} catch (...) { 
    ex = std::current_exception(); 
} 
cleanup(); 
if(ex)rethrow_exception(ex); 

这不是100%相同于上述C等效由于cleanup()调用堆栈退绕之前发生,通常这是没有问题的,但确切的异常情况可能会丢失。

我实现的内部异常处理程序作为助手功能,如下所示:

_declspec(thread) void *real_handler = nullptr; 

void **get_real_handler_addr(){ 
    return &real_handler; 
} 

__declspec(naked) int exception_handler(...){ 
    __asm { 
     call get_real_handler_addr; 
     mov eax, [eax]; 
     jmp eax; 
    } 
} 

这里的诀窍是,这个处理程序不能在运行时生成的,所以存根必须找出其中“真正的“处理程序是。我们使用线程本地存储来执行此操作。

现在生成的代码将从FS获取异常处理程序链:[0]。然而,链必须基于所以我用下面的代码替换处理程序堆栈:然后

void **exception_chain; 
__asm { 
    mov eax, fs:[0] 
    mov exception_chain, eax 
} 
//... 
void *saved_handler = exception_chain[1]; 
exception_chain[1] = exception_handler; 
*get_real_handler_addr() = generated_code->get_exception_handler(); 

产生的异常处理程序可以做清理工作。但是,如果任何当前异常处理程序返回EXCEPTION_CONTINUE_SEARCH,处理程序将被调用两次。我的策略是恢复第一次调用中的原始异常处理程序。