保持导出的函数不被链接器删除

时间:2013-06-12 16:22:32

标签: c++ visual-c++ gcc dll shared-libraries

我有一个静态链接到几个导出一些函数的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导出的函数。

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

@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;

由于编译器是优化的genuis,在FORCE_UNDEFINED_SYMBOLS中,我将竭尽全力欺骗编译器链接未引用的函数。该宏仅适用于函数内部。所以我必须创建这个虚假的Magic类。必须有更好的方法。

无论如何 - 它确实有用。

3 个答案:

答案 0 :(得分:3)

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

  1. 例如,在Qt中,您可以通过调用特定的宏来将静态插件“导入”到主可执行文件中:

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

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

  2. Poco家伙使用Linux上的extern "C"声明和Windows上的pragma强制从静态库导出特定符号:

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

    在Linux上强制使用相同的extern "C"声明并在Windows上使用链接器编译指示强制链接到静态库:

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

    您可以在此blog post

  3. 中找到详细信息

答案 1 :(得分:0)

无法为主可执行文件链接器提供.def文件吗?该文件应该导出有问题的函数,这将使它们不被删除。

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

答案 2 :(得分:0)

问题:在Windows上,STATIC LIB包含一个OBJ文件,该文件具有标记为__decl-spec(dll-export)的函数,但如果在EXE中未使用该函数,则不会从EXE导出函数。在其他平台上我们也有同样的问题但是我们有编译器选项,如--whole-archive / -force_load,确实可以使用。

链接:Link1 Link2

我想到的唯一解决方案是不创建STATIC库,而是在可执行文件中包含所有代码(静态LIBS):1。它适用于Windows 2.它适用于没有--whole-archive 3的Linux。它适用于没有-force_load 4的Mac OS X.我们也不用担心2&amp; 3包括死代码,exe膨胀等。

这是唯一的解决方案,直到连接器变得聪明并丢弃所有未使用的符号,除了那些专门标记为外部消耗的符号,即标记为要导出的符号。