2011-11-16 94 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,處理程序將被調用兩次。我的策略是恢復第一次調用中的原始異常處理程序。