Keep an exported function from being deleted by th

2019-02-24 12:02发布

问题:

I have a program that statically links with several c++ libraries that export a few functions:

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

The main program then calls these functions using 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

So the key here is that I'm calling a function in my own main program using dynamic linking.

( Why I do this is beyond the scope of the question, but short answer: this is a plugin architecture, but I have some standard plugins that are linked directly into the main binary - but I still want to load them through the same plugin loading interface. E.g. for the built-in plugins I load them by passing in the current executable as the source of the plugin interfaces. )

Here is the problem: the linker doesn't know I'm going to need these functions and doesn't link them in.

How do I force these functions to be linked in? For a dynamic lib, exporting them is enough. But for an exe, even dll exported function are deleted by the linker.

I know I can probably force linking by making the main binary assign these function addresses to something or some other similar hack. Is there a right way to do this?

@UPDATE: So I have a solution that works - but it sure is ugly on the inside. Still looking for a better way.

So I have to somehow define the symbols I need in the object that loads the built-in interfaces. I don't think there is a way to force the linker to link in a symbol otherwise. E.g. There is no way that I know of to build a library with a function that is always linked wether it looks needed or not. This is entirely at the discretion of the link step for the executable.

So in the executable I have a macro that defines the built-in interfaces I need. Each built-in plugin has a prefix to all of its interface functions so, at the top of the file I do:

DEFINE_BUILT_IN_PLUGIN( PluginOne )
DEFINE_BUILT_IN_PLUGIN( PluginTwo )

This will force the definitions of the functions I need. But the macro to do this is so ugly that I'm filled with feelings of rage and self doubt ( I've removed the trailing slashes from the macro for readability ):

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

Since the compiler is an optimizing genuis, in FORCE_UNDEFINED_SYMBOLS I'm going to great lengths to trick the compiler into linking an unreferenced function. That macro only works inside a function. So I have to create this bogus Magic class. There must be a better way.

Anyway - it does work.

回答1:

I have seen at least two different approaches to solve similar tasks.

  1. In Qt for example, you can have static plug-ins which need to be "imported" into the main executable by calling a specific macro:

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

    It creates a static instance of a custom class whose constructor calls an initialization function exported from the static plug-in.

  2. The Poco guys force the export of a specific symbol from the static library using an extern "C" declaration on Linux and a pragma on Windows:

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

    The linkage to the static library is forced with the same extern "C" declaration on Linux and with a linker pragma on Windows:

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

    You can find the details in this blog post.



回答2:

Can't you provide a .def file to your main executable linker? That file should export the functions in question, which would keep them from being deleted.

I seem to remember that I did something like this long ago.



回答3:

Problem: On windows, STATIC LIB which contains an OBJ file that has a function marked __decl-spec(dll¬export) but if the same is not used in the EXE, function does not get exported from the EXE. On other platforms also we have the same problem BUT there we have compiler options like --whole-archive / -force_load, do make it work.

Links: Link1 Link2

Only solution that come to my mind is to not create STATIC libs, rather include all code (static LIBS) in the executable then: 1. It works on Windows 2. It works on Linux without --whole-archive 3. It works on Mac OS X without -force_load 4. We also need not worry about if 2 & 3 include the dead code, exe bloat etc.

This is the only solution till the linkers become smart and throw out every unused symbol, except those marked specifically for external consumption i.e. marked to be exported.