在Linux上链接具有未解决符号的共享库

5
我有以下三个项目:
  • Host:一个可执行文件,导出一个全局变量(声明为extern)
  • Plugin:运行时库,由Host加载并引用全局变量
  • Tool:一个可执行文件,链接到Plugin并使用一些其功能。它不以任何方式引用全局变量。
  • 现在,在Windows上构建这些内容一切正常。 Tool将仅链接到Plugin的导出库,并且不会尝试解析全局变量。
    在Linux上,我遇到了一个问题。 Tool尝试链接到Plugin的.so库(因为没有导出库),并找到Host中对全局变量的引用,但无法解析。
    如何解决这个问题?
    编辑:
    下面是使用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)
    

    在Windows上,它能够构建和运行。Host.exe 显示“42”,Tool.exe 显示“4”。

    在Linux上,我遇到了以下链接错误:

    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
    

    你能发布一些代码/构建脚本,以便我们可以看到你在Win/Linux上的构建过程吗? - txtechhelp
    @txtechhelp 我更新了我的问题,并提供了完整的示例。 - typ1232
    静态链接到可执行文件是非常不寻常的。你需要这样做有什么原因,还是只是出于好奇? - ComicSansMS
    2个回答

    1
    有两种可能的方法:
    1. 将全局变量移动到一个单独的 DLL 中,该 DLL 被 HostPlugin 引用。
    2. Plugin 分成库功能和插件功能,分别提供给 ToolHost(通过调用库来实现插件)。
    任何一种方法都应该适用于您。
    链接失败是因为您正在将 Tool 程序与 Plugin DLL 链接在一起,此时链接器强制要求所有符号都可解析。虽然懒惰查找原则上可以避免运行时出现问题,但系统策略可能会出于安全原因禁止懒惰查找,这将在进程中存在未定义的符号后立即终止您的 Tool

    谢谢。这是我应该朝着的长期解决方案,但我已经知道了。我正在寻找一个能够在不需要太多工作的情况下解决我的问题的答案。 - typ1232

    1

    另外两种方法是:

    • 在工具中定义全局变量。由于它没有被使用,因此值无关紧要。
    • 使用链接器命令来定义符号和虚构的值。对于GCC和GNU ld,应该是类似于gcc -Wl,--defsym=GlobalSymbol=0

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接