2017-02-10 120 views
0

我正在用C++写一个Windows DLL。这个库只有C接口,并且只使用标准的Windows库,这些库也有C接口。因此,将所有C++库静态链接到库中似乎是安全的,因为我仅在使用C接口时不依赖于C++ ABI版本。在Windows DLL上静态链接libgcc

不幸的是,当我用-static-stdc++ -static-libgcc编译我的库时,我的库停止处理异常,并且当引发一些异常时,DLL调用其静态链接的_Unwind_RaiseException函数,该函数中止整个应用程序。

我认为这可能是一个损坏的libgtcc.a所以我试图更新我的编译器。但MinGW 4.8和MinGW 6.3的结果是一样的。

请有谁能够解释我在这里发生了什么?

Klasyc

+2

使用TDM-GCC。请参阅[简介](http://tdm-gcc.tdragon.net/quirks) – Ripi2

+0

或MinGW,它支持SEH。 – rustyx

+0

@RustyX OP已经尝试了MinGW,正如你所说的那样支持SEH,但是如果库是静态链接的并且异常是通过DLL边界引发的,那么不会。这是TDM-GCC解决的问题之一。如果运行时库是动态链接的(即在进程中只有一个版本),MinGW很好。 –

回答

0

你是否正在处理DLL中的所有异常?如果任何异常使用C调用约定在函数外部“泄漏”,则会使应用程序崩溃。

我有C++异常下MinGW的x86_64-5.3.0-Win32的SEH-rt_v4-REV0和没有问题的mingw32 4.8.1 DWARF2具有静态的libstdC++/libgcc中。

DLL源(dll.cpp):

#include <windows.h> 
#include <exception> 
#include <iostream> 

extern "C" { 
__declspec(dllexport) int __stdcall test() { 
    try { 
     new int[-1]; 
     return 123; 
    } catch (const std::exception& ex) { 
     std::cerr << "Exception:" << ex.what() << std::endl; 
     return 456; 
    } 
} 
} 

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD  fdwReason, LPVOID lpvReserved) 
{ 
    return TRUE; 
} 

应用源(app.c):

#include <stdio.h> 

#ifdef __cplusplus 
extern "C" 
#endif 
__declspec(dllimport) int __stdcall test(); 

int main() { 
    printf("result: %d\n", test()); 
    return 0; 
} 

编译:

g++ -O2 -static-libgcc -static-libstdc++ -shared -Wl,--out-implib=dtest.lib -s -o dtest.dll dll.cpp

g++ -O2 -static-libgcc -static-libstdc++ -L. -s -o app app.c -ldtest

输出:

Exception:std::bad_alloc 
result: 456 

为了回答您的评论“为什么我需要SEH支持的编译器?我认为SEH允许我捕捉Windows例外“ - 在Windows上,SEH是的所有例外的事实标准,包括C++异常(当使用Visual C++编译时)MinGW对于C++异常有不同的实现(SEH,SJLJ和DWARF),但是SEH是唯一没有开销的机制,所以如果它可用,你应该更喜欢它通过SJLJ和DWARF。

+0

谢谢你的例子。当我尝试像你一样编译它时,它确实有效。但是当我尝试用g ++编译'app.c'(我必须将'extern“C''添加到'test()'函数头)时,应用程序会崩溃。因此,似乎使用静态libgcc的两个副本会造成问题。请注意我正在使用DWARF-2例外,因为我必须生成32位二进制文​​件。 – klasyc

+0

我没有这个问题。看到我更新的答案。 – rustyx

+0

对不起,延迟回答。请尝试在没有此开关的情况下使用'-static-libgcc'和应用程序编译DLL。静态DLL和动态应用程序的结合似乎打破了事情。 – klasyc

0

感谢RastyX在他的答案中的那个简单的例子经过一些研究,我发现应用程序崩溃时,我编译DLL部分与-static-libgcc和应用程序部分没有此开关(两部分编译相同的g ++)

也感谢Ripi2的TDM-GCC sugg estion。我尝试了SJLJ和DWARF-2异常处理,并且都可以工作。

总而言之,似乎MinGW的libgcc不喜欢每个进程实例化多次。在我的情况下,我使用DLL中的一个副本(静态链接)和一个副本在应用程序中,这可能会打破异常处理。另一方面,TDM-GCC侧重于基本库的静态链接,因为它的网页说,因此即使没有-static-stdc++ -static-libgcc命令行开关(这是我想要的),也只产生依赖于Windows库的二进制文件。此外,异常处理在这里工作,所以更改工具链对我来说是正确的。