Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Linking a shared library with unresolved symbols on linux

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
like image 735
typ1232 Avatar asked Nov 02 '22 05:11

typ1232


1 Answers

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
like image 79
Zan Lynx Avatar answered Nov 15 '22 05:11

Zan Lynx