【问题标题】:How to force inclusion of an object file in a static library when linking into executable?链接到可执行文件时如何强制将目标文件包含在静态库中?
【发布时间】:2011-02-28 20:00:36
【问题描述】:

我有一个 C++ 项目,由于其目录结构设置为静态库A,它链接到共享库B,它链接到可执行文件C。 (这是一个使用 CMake 的跨平台项目,所以在 Windows 上我们得到A.libB.dllC.exe,在Linux 上我们得到libA.alibB.soC。)库@ 987654330@有一个init函数(A_init,定义在A/initA.cpp),它是从库B的init函数(B_init,定义在B/initB.cpp)调用的,它是从C调用的s 主要。因此,在链接B 时,A_init(以及initA.cpp 中定义的所有符号)会链接到B(这是我们想要的行为)。

问题在于A 库还定义了一个函数(Af,在A/Afort.f 中定义),该函数旨在通过动态加载(即Windows 上的LoadLibrary/GetProcAddressdlopen /dlsym 在 Linux 上)。由于库B 中没有对Af 的引用,因此A/Afort.o 中的符号不​​包含在B 中。在 Windows 上,我们可以使用 pragma 人为地创建引用:

#pragma comment (linker, "/export:_Af")

由于这是一个编译指示,它仅适用于 Windows(使用 Visual Studio 2008)。为了让它在 Linux 上运行,我们尝试将以下内容添加到 A/initA.cpp

extern void Af(void);
static void (*Af_fp)(void) = &Af;

这不会导致符号Af 包含在B 的最终链接中。我们如何强制将符号Af 链接到B

【问题讨论】:

    标签: c++ linux linker static-libraries


    【解决方案1】:

    事实证明,我最初的尝试主要是在那里。以下作品:

    extern "C" void Af(void);
    void (*Af_fp)(void) = &Af;
    

    对于那些想要一个自包含的预处理器宏来封装它的人:

    #if defined(_WIN32)
    # if defined(_WIN64)
    #  define FORCE_UNDEFINED_SYMBOL(x) __pragma(comment (linker, "/export:" #x))
    # else
    #  define FORCE_UNDEFINED_SYMBOL(x) __pragma(comment (linker, "/export:_" #x))
    # endif
    #else
    # define FORCE_UNDEFINED_SYMBOL(x) extern "C" void x(void); void (*__ ## x ## _fp)(void)=&x;
    #endif
    

    这样用的:

    FORCE_UNDEFINED_SYMBOL(Af)
    

    【讨论】:

      【解决方案2】:

      MSVC#pragma comment(linker, "/include:__mySymbol")

      gcc-u symbol

      【讨论】:

        【解决方案3】:

        有更好的方法来编写 FORCE_UNDEFINED_SYMBOL 宏。只需将该函数指针转换为 void*。然后它适用于任何功能 - 或数据。此外,当宏的 gcc 部分也适用于 MSVC 时,为什么还要使用 MSVC 编译指示。所以我的简化版本是:

        #define FORCE_UNDEFINED_SYMBOL(x) void* __ ## x ## _fp =(void*)&x;
        

        这样用的:

        FORCE_UNDEFINED_SYMBOL(Af)
        

        但它必须在包含被剥离符号的库的程序中使用。

        【讨论】:

        • 使用 C++11,您可能可以使用 auto 而无需强制转换!
        【解决方案4】:

        您可以在构建 B 时使用--undefined 选项:

        g++ -Wl,--undefined,Af -o libB.so ...
        

        【讨论】:

          【解决方案5】:

          尝试将这些行放入B/initB.cpp,以便在链接时(希望)将它们强制放入libB.so 库中。

          但是为什么你必须这样做呢?您不能将其设置为使可执行文件引用该函数(或它的调用者),从而使链接器自动执行正确的操作吗?

          【讨论】:

          • C其实是脚本语言前端,B是语言引擎,A是引擎使用的一套原生代码方法。我们正在实现一种预先存在的语言,该语言具有定义良好的外部函数接口。 A 和 B 由不同的团队制作;我们更愿意将 A 团队编写的所有内容都保留在 A 目录中。
          【解决方案6】:

          如果您可以使用 gcc 的 C++0x 特性 (-std=c++0x),那么 函数默认模板参数 可以解决问题。在当前的 c++ 标准中,函数模板不允许使用默认参数。在 c++0x 中启用这些功能后,您可以执行以下操作:-

          在一些静态库的头文件中...

          template< class T = int >
          void Af()
          {
          }
          

          然后在其对应的 cpp 文件中使用显式模板实例化...

          template void Af();
          

          这将为函数Af 生成符号,尽管它尚未被调用/引用。 这不会影响调用者,因为由于默认模板参数,您不需要指定类型。只需在函数声明前添加template &lt;class T = int &gt;,并在其实现文件中显式实例化即可。

          HTH,

          【讨论】:

          • 很遗憾,这不起作用,因为 Af 实际上是一个 Fortran 过程。
          • 那么,制作一个调用 Af_(...) 的“C”包装器 [你可以从“C”调用 fortran 函数,即函数名后加下划线;谷歌关于参数的细节,我不知道 C++ 调用 fortran]。从 C++ 调用这个“C”包装器。顺便说一句,这就是我工作的领先金融公司之一如何管理其古董/蹩脚代码:-)
          • 这是一个非常不明显的代码,可能会在 2 年后被新手维护者删除。至少宏清晰到可以理解。
          • @xryl669,这就是为什么您应该在此类定义之前/之后添加评论(并希望新手也可以阅读 cmets。)
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2015-04-16
          • 2013-06-24
          • 1970-01-01
          • 2018-04-13
          • 2018-12-28
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多