2013-06-12 33 views
2

我有一个程序,静态与出口的一些功能几个C++库的链接被删除:保持导出函数由链接器

extern "C" 
{ 
    KSrvRequestHandler* CreateRequestHandler(const char* name); 
    bool    DestroyRequestHandler(KSrvRequestHandler* handler); 
    const char**  ListRequestHandlerTypes(); 
} 

主程序然后调用使用GetProcAddress的/ dlsym进行这些功能:

#ifdef WIN32 

    HINSTANCE hDll = GetModuleHandle(NULL); 

    mCreateHandler = GetProcAddress(hDll, createFuncName ); 
    mDestroyHandler = GetProcAddress(hDll, destroyFuncName); 
    mGetHandlerTypes = GetProcAddress(hDll, listFuncName ); 

#else // POSIX 

    void* handle = dlopen(NULL, 0); 

    mCreateHandler = dlsym(handle, createFuncName ); 
    mDestroyHandler = dlsym(handle, destroyFuncName); 
    mGetHandlerTypes = dlsym(handle, listFuncName ); 
    dlclose(handle); 

#endif // !POSIX 

所以这里的关键是我使用动态链接在我自己的主程序中调用一个函数。 (为什么我这样做超出了问题的范围,但简短的回答:这是一个插件架构,但我有一些标准的插件直接链接到主要的二进制文件 - 但我仍然想通过它来加载它们同样的插件加载界面。例如,对于内置插件,我通过传入当前可执行文件作为插件接口的源代码来加载它们。)

这里是问题:链接器不知道我要去需要这些功能,并且不会链接它们。

如何强制这些函数被链接?对于动态库,导出它们就足够了。但是对于一个exe文件,甚至dll导出的函数都会被链接器删除。

我知道我可以强制链接,让主要的二进制文件将这些函数地址分配给某些东西或其他类似的黑客。有没有正确的方法来做到这一点?

@UPDATE:所以我有一个解决方案,但它确实是丑陋的内部。仍在寻找更好的方法。

所以我必须以某种方式在加载内置接口的对象中定义我需要的符号。我不认为有强制链接器链接符号的方法。例如。我无法知道如何构建一个函数库,该函数始终与其看起来需要或不需要的函数相关联。这完全取决于可执行文件的链接步骤。

所以在可执行文件中,我有一个宏定义了我需要的内置接口。每个内置插件有一个前缀,其所有的接口函数,因此,在我做文件的顶部:

DEFINE_BUILT_IN_PLUGIN(PluginOne) 
DEFINE_BUILT_IN_PLUGIN(PluginTwo) 

这将迫使我所需要的功能的定义。但宏做,这是这么丑,我充满了愤怒和自我怀疑的感觉(我已经删除了尾随从宏观的可读性斜杠):

#define FORCE_UNDEFINED_SYMBOL(x) 
    void* _fp_ ## x ## _fp =(void*)&x; 
    if (((ptrv) _fp_ ## x ##_fp * (rand() | 1)) < 1) 
     exit(0); 

#define DEFINE_BUILT_IN_PLUGIN(PREFIX) 

extern "C" 
{                         
    KSrvRequestHandler* PREFIX ## CreateRequestHandler(const char* name); 
    bool    PREFIX ## DestroyRequestHandler(KSrvRequestHandler* handler);  
    const char**  PREFIX ## ListRequestHandlerTypes(); 
} 

class PREFIX ## HandlerInterfaceMagic 
{  
public: 
    PREFIX ## HandlerInterfaceMagic() 
    { 
     FORCE_UNDEFINED_SYMBOL(PREFIX ## CreateRequestHandler); 
     FORCE_UNDEFINED_SYMBOL(PREFIX ## DestroyRequestHandler); 
     FORCE_UNDEFINED_SYMBOL(PREFIX ## ListRequestHandlerTypes); 
    } 
};    
PREFIX ## HandlerInterfaceMagic PREFIX ## HandlerInterfaceMagicInstance; 

由于编译器是一种优化的天才, ,在FORCE_UNDEFINED_SYMBOLS我会竭尽全力欺骗编译器链接一个未引用的函数。该宏只适用于某个功能。所以我必须创造这个假魔术课。一定会有更好的办法。

无论如何 - 它确实有效。

+0

我可能会误解你的情况,但是你可以将导出的函数移动到单独编译的库中,然后在需要时将它们从库中链接起来? –

+0

如果我将内置的插件插入到它们自己的动态库中,那么这就行得通了 - 基本上只是将它们编写为常规插件,而且它们总是附带可执行文件。但是这不是最理想的,因为我希望他们都在同一个文件中。但更大的问题是,内置插件需要访问主程序的内部。例如。其中一个是内部程序状态报告的诊断插件。我必须创建一个完整的反向界面(例如exe->插件),以将diag插件作为单独的dyn来完成此操作。库。 –

+0

请不要使用符号名称的前导双下划线,它们是为编译器保留的。如果您担心名称冲突,请将它们放在名称空间中。 –

回答

2

我已经看到至少有两种不同的方法来解决类似的任务。

  1. 在Qt例如,你可以有静态的插件,它需要通过调用特定的宏被“进口”到主可执行文件:

    https://qt-project.org/doc/qt-4.8/qtplugin.html#Q_IMPORT_PLUGIN

    它创建一个静态实例自定义类的构造函数调用从静态插件导出的初始化函数。

  2. 的波科家伙强制特定符号从静态库导出在Linux上使用的extern "C"声明,并在Windows上编译:

    __pragma(comment (linker, "/export:CreateRequestHandler"))

    到静态库联动被迫与同样extern "C"声明在Linux和Windows上的接头编译:

    __pragma(comment (linker, "/include:CreateRequestHandler"))

    你可以在这个blog post找到详细信息。

+0

感谢您的支持。 Qt解决这个问题的方式类似于我在我自己非常丑陋的导入宏中所做的解释。 –

+0

是的,但如果我正确理解你的情况,你的外部“C”功能就是一个虚拟。在Qt中,实际需要调用函数(在导入可执行文件或共享库的静态初始化过程中)并注册静态插件。 – Sascha

+0

似乎在QT示例中,QT向插件注册一个注册api,并且在静态init时调用它们来让插件自行安装。在我的情况下,系统加载配置文件中配置的插件。所以这个系统是在控制或不是插件的负载。所以它需要的只是插件功能可以在插件动态库中使用,或者编译到主系统中。我现在通过使用函数指针地址来强制它们被包含来强制链接。如果优化器变得更聪明,它将停止再次链接它们。 –

0

你不能提供一个.def文件到你的主要可执行文件链接器吗?该文件应导出有问题的功能,这将防止它们被删除。

我似乎记得我很久以前就做过这样的事情。

0

问题:在windows上,STATIC LIB包含一个带有标记为__decl-spec(dll-export)功能的OBJ文件,但如果在EXE中未使用相同的功能,则函数不会从EXE导出。在其他平台上,我们也遇到了同样的问题,但是我们有编译选项,例如--whole-archive/-force_load,确实能够工作。

链接:Link1Link2

只有解决方案,来我的心是不是创建静态库,而包括在可执行的所有代码(静态LIBS),则:1,适用于Windows 2.它适用于没有--whole-archive的Linux 3.它在Mac OS X上无需使用-force_load 4.我们也不用担心是否包含死码,exe膨胀等。

这是唯一的解决方案,直到连接器变得聪明并丢弃每一个未使用的符号,除了特别标记为外部消费的那些符号,即标记为要出口的符号。