描述:
一个。 X类包含静态私有(private)数据成员ptr和静态公共(public)函数成员getptr()/setptr()。
在 X.cpp 中,ptr 设置为 NULL。
libXYZ.so(共享对象)包含类 X 的对象(即 libXYZ.so 包含 X.o)。
libVWX.so(共享对象)包含类 X 的对象(即 libVWX.so 包含 X.o)。
可执行文件 a.exe 包含 X.cpp 作为翻译单元的一部分,最后链接到 libXYZ.so、libVWX.so
附言:
1. 任何类都没有涉及用户命名空间。
2.库和可执行文件还包含许多其他类。
3. 没有执行 dlopen()。所有库都在编译期间使用 -L 和 -l 标志进行链接。
问题陈述:
当编译 a.exe 并将其与其他库(即 libXYZ.so 和 libVWX.so)链接时,我预计会出现链接器错误(多次出现相同符号的冲突/出现),但并没有出现。
程序执行时 - 在 SUSE 10 Linux 和 HP-UX 11 IA64 中行为异常。
在 Linux 中,当执行流被推送到不同库中的所有对象时,效果仅在 X 的一个拷贝中注册。
在 HPUX 中,当执行流被推送到不同库中的所有对象时,效果在 X 的 3 个不同拷贝中注册(2 个属于每个库,1 个用于可执行文件)
PS:我的意思是在运行程序期间,流程确实通过了属于 a.exe、libXYZ.so 和 libVWX.so 的多个对象,这些对象与属于 X 的静态指针交互。
问题:
- Expecting linker error 是否不正确?由于两个编译器默默地通过了编译,所以在这种情况下可能有一个标准规则,我错过了。如果是这样,请让我知道。
- 编译器(Linux 中的 gcc 和 HPUX 中的 aCC)如何决定在最终可执行文件中保留多少 X 拷贝并在这种情况下引用它们。
- 在这种情况下,gcc 和 aCC 是否支持向用户发出警告/停止编译的标志?
提前感谢您的帮助。
最佳答案
我不太确定我是否完全理解了这个场景。然而,
在 Linux 下加载动态对象的默认行为(和其他
Unices) 是为了使库中的所有符号都可用,并且只使用
第一次遇到。因此,如果您同时使用 libXYZ.so
和 libVWX.so
包含符号 X::ourData
,这不是错误;如果你加载它们
该顺序,libVWX.so
将使用 libXYZ.so
中的 X::ourData
,
而不是它自己的。从逻辑上讲,这很像模板定义
在 header 中:编译器或多或少随机选择一个,如果
任何定义都与其他定义不同,它是
未定义的行为。这种行为可以是
通过将标志 RTLD_LOCAL
传递给 dlopen
来覆盖。
关于您的问题:
链接器只是实现了
dlopen
的默认行为(系统隐式加载库时得到的行为)。因此,没有错误(但如果任何定义不相同,则逻辑上等同于未定义行为)。编译器不做决定。该决定是在加载
.so
时做出的,具体取决于您在调用dlopen
时指定的是RTLD_GLOBAL
还是RTLD_LOCAL
.当运行时隐式调用dlopen
来解决依赖关系时,如果在加载主可执行文件时发生这种情况,它将使用RTLD_GLOBAL
,以及在加载主可执行文件时用于加载库的内容依赖来自库。 (当然,这意味着RTLD_GLOBAL
将传播,直到您显式调用dlopen
。)
关于C++编译静态变量和共享对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7792235/