Question

I have the following three projects:

  • Host: An executable that exports a global variable (declared extern)
  • Plugin: A runtime library that is loaded by Host and references the global variable
  • Tool: An executable that links against the Plugin and uses some functionality of it. It doesn't reference the global variable in any way.

Now if I build this on windows everything is fine. The Tool will only link to the export library of the Plugin and will not try to resolve the global variable.

On linux I'm facing a problem. The Tool tries to link against the Plugin .so library (because there is no export library) and will find the reference to the global variable in Host which it can't resolve.

How to solve this problem?


Edit:

The following is a compilable example of the problem using CMake.

CMakeLists.txt

SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_CURRENT_SOURCE_DIR}/bin")
SET(CMAKE_LIBRARY_OUTPUT_DIRECTORY_DEBUG "${CMAKE_CURRENT_SOURCE_DIR}/bin")

SET(CMAKE_POSITION_INDEPENDENT_CODE ON)

ADD_SUBDIRECTORY(Host)
ADD_SUBDIRECTORY(Plugin)
ADD_SUBDIRECTORY(Tool)

Host/Host.h

#ifndef HOST_H
#define HOST_H

#ifdef _MSC_VER

#ifdef COMPILE_HOST
#define HOST_EXPORT __declspec(dllexport)
#else
#define HOST_EXPORT __declspec(dllimport)
#endif

#else
#define HOST_EXPORT
#endif


class HOST_EXPORT Host
{
public:
    int getAnswer();

};


extern HOST_EXPORT Host g_host;


#endif

Host/Host.cpp

#include "Host.h"
#include "../Plugin/Plugin.h"
#include <iostream>


Host g_host;


int Host::getAnswer()
{
    return 42;
}


int main()
{
    std::cout << g_host.getAnswer() << std::endl;

    // load plugin and use it
}

Host/CMakeLists.txt

PROJECT(Host)

ADD_EXECUTABLE(Host Host.cpp Host.h)

ADD_DEFINITIONS(-DCOMPILE_HOST)

SET_TARGET_PROPERTIES(Host PROPERTIES ENABLE_EXPORTS ON)

Plugin/Plugin.h

#ifndef PLUGIN_H
#define PLUGIN_H


class Plugin
{
public:
    Plugin();

};

#endif

Plugin/Plugin.cpp

#include "Plugin.h"
#include "../Host/Host.h"
#include <iostream>


Plugin::Plugin()
{
    std::cout << g_host.getAnswer() << std::endl;
}

Plugin/PluginFunc.h

#ifndef PLUGINFUNC_H
#define PLUGINFUNC_H

#ifdef _MSC_VER
#define PLUGIN_EXPORT __declspec(dllexport)
#else
#define PLUGIN_EXPORT
#endif


namespace plug
{
    int PLUGIN_EXPORT getRandomNumber();
}

#endif

Plugin/PluginFunc.cpp

#include "PluginFunc.h"


int plug::getRandomNumber()
{
    return 4;
}

Plugin/CMakeLists.txt

PROJECT(Plugin)

ADD_LIBRARY(Plugin SHARED Plugin.cpp Plugin.h PluginFunc.cpp PluginFunc.h)

TARGET_LINK_LIBRARIES(Plugin Host)

Tool/Tool.cpp

#include "../Plugin/PluginFunc.h"
#include <iostream>


int main()
{
    std::cout << plug::getRandomNumber() << std::endl;
}

Tool/CMakeLists.txt

PROJECT(Tool)

ADD_EXECUTABLE(Tool Tool.cpp)

TARGET_LINK_LIBRARIES(Tool Plugin)

On Windows it builds and runs. Host.exe displays "42" and Tool.exe displays "4".

On Linux I get the following linking error:

usr@debian64:~/vbox/testlink/build$ make
Scanning dependencies of target Host
[ 25%] Building CXX object Host/CMakeFiles/Host.dir/Host.o
Linking CXX executable Host
[ 25%] Built target Host
Scanning dependencies of target Plugin
[ 50%] Building CXX object Plugin/CMakeFiles/Plugin.dir/Plugin.o
[ 75%] Building CXX object Plugin/CMakeFiles/Plugin.dir/PluginFunc.o
Linking CXX shared library libPlugin.so
[ 75%] Built target Plugin
Scanning dependencies of target Tool
[100%] Building CXX object Tool/CMakeFiles/Tool.dir/Tool.o
Linking CXX executable Tool
../Plugin/libPlugin.so: undefined reference to `Host::getAnswer()'
../Plugin/libPlugin.so: undefined reference to `g_host'
collect2: error: ld returned 1 exit status
make[2]: *** [Tool/Tool] Error 1
make[1]: *** [Tool/CMakeFiles/Tool.dir/all] Error 2
make: *** [all] Error 2
Was it helpful?

Solution

Two other approaches are:

  • Define the global variable in Tool. It doesn't matter what the value is since it isn't used.
  • Use linker commands to define the symbol with a made up value. With GCC and GNU ld it should be something like gcc -Wl,--defsym=GlobalSymbol=0

OTHER TIPS

There are two possible approaches:

  1. Move the globals into a separate DLL that is referenced by both Host and Plugin.
  2. Split Plugin into library functionality provided to Tool and plugin functionality provided to Host (implementing the plugin by invoking the library).

Either of these should work for you.

The link fails because you are linking the Tool program against the Plugin DLL, at which point the linker enforces that all symbols are resolvable. While lazy lookup would in principle avoid the problem at runtime, system policy could inhibit lazy lookup for security reasons, which would terminate your Tool immediately after invocation as there are undefined symbols in the process.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top