说明:
一个。类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标志链接所有库。
问题陈述:
在编译和链接a.exe与其他库(即libXYZ.so和libVWX.so)时,我预计会发生链接器错误(多次冲突/出现相同的符号),但没有得到一个。
程序执行时 - SUSE 10 Linux和HP-UX 11 IA64中的行为很奇怪。
在Linux中,当执行流被推送到不同库中的所有对象时,效果仅在X的一个副本中注册
在HPUX中,当执行流被推送到不同库中的所有对象时,效果被注册在3个不同的X副本中(2个属于每个库,1个用于可执行文件)
PS:我的意思是在运行程序时,流程确实通过了属于a.exe,libXYZ.so和libVWX.so的多个对象,这些对象与属于X的静态指针进行交互。
问题:
提前感谢您的帮助。
答案 0 :(得分:1)
我不太确定我完全理解了这个场景。然而,
在Linux下加载动态对象的默认行为(和其他
Unices)是使库中的所有符号可用,并且仅使用
第一次遇到。因此,如果您同时libXYZ.so
和libVWX.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可能没有该功能,因此每个库最终都会调用它自己的实现,而任何其他具有未定义符号的对象都会看到其中一个。