C ++编译静态变量和共享对象

时间:2011-10-17 10:08:44

标签: c++ static compilation shared-libraries

说明:

一个。类X包含静态私有数据成员ptr和静态公共函数成员getptr()/ setptr() 在X.cpp中,ptr设置为NULL。

湾libXYZ.so(共享对象)包含类X的对象(即libXYZ.so包含X.o)。

℃。 libVWX.so(共享对象)包含类X的对象(即libVWX.so包含X.o)。

d。可执行文件a.exe包含X.cpp作为翻译单元的一部分,最后链接到libXYZ.so,libVWX.so

PS:
 1.任何类都没有涉及用户名称空间  2.库和可执行文件也包含许多其他类  3.没有完成dlopen()。在编译期间使用-L和-l标志链接所有库。

问题陈述:

  1. 在编译和链接a.exe与其他库(即libXYZ.so和libVWX.so)时,我预计会发生链接器错误(多次冲突/出现相同的符号),但没有得到一个。

  2. 程序执行时 - SUSE 10 Linux和HP-UX 11 IA64中的行为很奇怪。
    在Linux中,当执行流被推送到不同库中的所有对象时,效果仅在X的一个副本中注册 在HPUX中,当执行流被推送到不同库中的所有对象时,效果被注册在3个不同的X副本中(2个属于每个库,1个用于可执行文件)

  3. PS:我的意思是在运行程序时,流程确实通过了属于a.exe,libXYZ.so和libVWX.so的多个对象,这些对象与属于X的静态指针进行交互。

    问题:

    • 是否期望链接器错误不正确?由于两个编译器以静默方式通过编译,可能存在一个标准规则,以防这种类型的情况我不知道。如果是的话,请让我知道。
    • 编译器(Linux中的gcc和HPUX中的aCC)如何决定在最终的可执行文件中保留多少个X副本,并在这种情况下引用它们。
    • gcc和aCC是否支持任何标志,这些标志会在这种情况下警告/停止向用户进行编译?

    提前感谢您的帮助。

3 个答案:

答案 0 :(得分:1)

我不太确定我完全理解了这个场景。然而, 在Linux下加载动态对象的默认行为(和其他 Unices)是使库中的所有符号可用,并且仅使用 第一次遇到。因此,如果您同时libXYZ.solibVWX.so 包含符号X::ourData,这不是错误;如果你加载它们 该订单libVWX.so将使用X::ourData中的libXYZ.so, 而不是自己的。从逻辑上讲,这很像模板定义 在标题中:编译器选择一个,或多或少偶然,如果 任何定义都与其他定义不同,它是 未定义的行为。这种行为可以 通过将标志RTLD_LOCAL传递给dlopen来覆盖。

关于你的问题:

  • 链接器只是实现dlopen的默认行为(当系统隐含地加载库时会获得该行为)。因此,没有错误(但如果任何定义不相同,则是未定义行为的逻辑等价物。)

  • 编译器没有决定。加载.so时会做出决定,具体取决于您在调用RTLD_GLOBAL时是指定RTLD_LOCAL还是dlopen。当运行时隐式调用dlopen来解析依赖关系时,如果在加载主可执行文件时发生这种情况,它将使用RTLD_GLOBAL,当依赖关系来自库时,它将用于加载库。 (当然,这意味着RTLD_GLOBAL会传播,直到您明确调用dlopen。)

答案 1 :(得分:0)

" extern"之间存在差异。符号(这是c ++中的默认值)和"共享库外部"。默认情况下,符号仅为" extern"这意味着一个"链接单元的范围"例如可执行文件或库。 所以预期的行为是:没有编译器错误,每个模块都使用自己的副本。 在内联编译等情况下,这会导致问题......等等...... 声明符号"共享库extern"你必须使用" .def"文件或编译器声明。 例如在visual c ++中,它将是" _ declspec(dllexport)"和" _declspec(dllimport)"。 我目前不知道gcc的声明,但我确信有人这样做: - )

答案 2 :(得分:0)

该函数是“公共静态”,所以我假设它是OOP-意思是“静态”(不需要实例),而不是静态的C意思(文件静态;编译单元的本地)。因此功能是外部的。

现在,在Linux中,您有权使用其他库或可执行文件覆盖库符号。库中的所有外部符号都使用全局偏移表来解析,即使是库实际定义的也是如此。虽然可执行文件中定义的函数通常不会像这样解析,但链接器会注意到符号将从库中获取符号表,并将引用放在那里的可执行文件中。因此,如果您生成了符号,库将会看到可执行文件中定义的符号。

这是显式功能,旨在让您可以执行更换内存分配函数或包装文件系统操作等操作。 HP-UX可能没有该功能,因此每个库最终都会调用它自己的实现,而任何其他具有未定义符号的对象都会看到其中一个。