C++编译静态变量和共享对象
描述:
类 X 包含静态私有数据成员 ptr 和静态公共函数成员 getptr()/setptr()。
在 X.cpp 中,ptr 设置为 NULL。
b. libXYZ.so(共享对象)包含类X的对象(即libXYZ.so包含Xo)。
c. libVWX.so(共享对象)包含类X的对象(即libVWX.so包含Xo)。
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 中,当执行流被推送到不同库中的所有对象时,效果被注册在 X 的 3 个不同副本中(2 个属于每个库,1 个用于可执行文件)
PS:我的意思是在运行程序期间,流程确实通过了通过属于 a.exe、libXYZ.so 和 libVWX.so 的多个对象,这些对象与属于 X 的静态指针进行交互。
问题:
- 预期链接器错误是否不正确?由于两个编译器默默地通过了编译,因此对于我所缺少的这种情况,可能有一个标准规则。如果是这样,请让我知道。
- 编译器(Linux 中的 gcc 和 HPUX 中的 aCC)如何决定在最终可执行文件中保留多少个 X 副本并在这种情况下引用它们。
- gcc 和 aCC 是否支持任何标志,在这种情况下会向用户发出警告/停止编译?
提前感谢您的帮助。
Description :
a. Class X contains a static private data member ptr and static public function member getptr()/setptr().
In X.cpp, the ptr is set to NULL.
b. libXYZ.so (shared object) contains the object of class X (i.e libXYZ.so contains X.o).
c. libVWX.so (shared object) contains the object of class X (i.e libVWX.so contains X.o).
d. Executable a.exe contains X.cpp as part of translation units and finally is linked to libXYZ.so, libVWX.so
PS:
1. There are no user namespaces involved in any of the classes.
2. The libraries and executable contain many other classes also.
3. no dlopen() has been done. All libraries are linked during compile time using -L and -l flags.
Problem Statement:
When compiling and linking a.exe with other libraries (i.e libXYZ.so and libVWX.so), I expected a linker error (conflict/occurance of same symbol multiple times) but did not get one.
When the program was executed - the behavior was strange in SUSE 10 Linux and HP-UX 11 IA64.
In Linux, when execution flow was pushed across all the objects in different libraries, the effect was registered in only one copy of X.
In HPUX, when execution flow was pushed across all the objects in different libraries, the effect was registered in 3 differnt copies of X (2 belonging to each libraries and 1 for executable)
PS : I mean during running the program, the flow did passed thourgh multiple objects belonging to a.exe, libXYZ.so and libVWX.so) which interacted with static pointer belonging to X.
Question:
- Is Expecting linker error not correct? Since two compilers passed through compilation silently, May be there is a standard rule in case of this type of scenario which I am missing. If so, Please let me know the same.
- How does the compiler (gcc in Linux and aCC in HPUX) decide how many copies of X to keep in the final executable and refer them in such scenarios.
- Is there any flag supported by gcc and aCC which will warn/stop compilation to the users in these kind of scenarios?
Thanks for your help in advance.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
我不太确定我是否完全理解了这个场景。然而,
Linux 下加载动态对象的默认行为(以及其他
Unices) 是让库中的所有符号都可用,并且只使用
第一次遇到的。因此,如果您同时使用
libXYZ.so
和libVWX.so
包含符号
X::ourData
,这不是错误;如果你加载它们按照该顺序,
libVWX.so
将使用libXYZ.so
中的X::ourData
,而不是它自己的。从逻辑上讲,这很像模板定义
在标头中:编译器或多或少偶然地选择一个,并且如果
任何定义都与其他定义不同,它是
未定义的行为。这种行为可以是
通过将标志
RTLD_LOCAL
传递给dlopen
来覆盖。关于您的问题:
链接器只是实现 dlopen 的默认行为(系统隐式加载库时获得的行为)。因此,没有错误(但如果任何定义不相同,则逻辑上相当于未定义的行为)。
编译器不会决定。该决定是在加载
.so
时做出的,具体取决于您在调用dlopen
时指定RTLD_GLOBAL
还是RTLD_LOCAL
。当运行时隐式调用dlopen
来解决依赖关系时,如果在加载主可执行文件时发生这种情况,以及在加载主可执行文件时使用什么来加载库,它将使用RTLD_GLOBAL
依赖项来自库。 (当然,这意味着RTLD_GLOBAL
将传播,直到您显式调用dlopen
。)I'm not too sure that I've completely understood the scenario. However,
the default behavior on loading dynamic objects under Linux (and other
Unices) is to make all symbols in the library available, and to only use
the first encountered. Thus, if you both
libXYZ.so
andlibVWX.so
contain a symbol
X::ourData
, it is not an error; if you load them inthat order,
libVWX.so
will use theX::ourData
fromlibXYZ.so
,instead of its own. Logically, this is a lot like a template definition
in a header: the compiler chooses one, more or less by chance, and if
any of the definitions is not the same as all of the others, it's
undefined behavior. This behavior can be
overridden by passing the flag
RTLD_LOCAL
todlopen
.With regards to your questions:
The linker is simply implementing the default behavior of
dlopen
(that which you get when the system loads the library implicitely). Thus, no error (but the logical equivalent of undefined behavior if any of the definitions isn't the same).The compiler doesn't decide. The decision is made when the
.so
is loaded, depending on whether you specifyRTLD_GLOBAL
orRTLD_LOCAL
when callingdlopen
. When the runtime callsdlopen
implicitly, to resolve a dependency, it will useRTLD_GLOBAL
if this occurs when loading the main executable, and what ever was used to load the library when the dependency comes from a library. (This means, of course, thatRTLD_GLOBAL
will propagate until you invokedlopen
explicitly.)该函数是“public static”,所以我假设它是“static”的 OOP 含义(不需要实例),而不是静态的 C 含义(文件静态;编译单元本地)。因此这些函数是外部的。
现在,在 Linux 中,您有明确的权利覆盖库符号,无论是使用另一个库还是在可执行文件中。库中的所有外部符号都使用全局偏移表进行解析,即使是库实际定义的符号也是如此。虽然可执行文件中定义的函数通常不会像这样解析,但链接器注意到符号将从库中获取符号表,并将对可执行文件定义的函数的引用放在那里。因此,如果您生成了可执行文件,则库将看到可执行文件中定义的符号。
这是显式功能,旨在让您可以执行诸如替换内存分配函数或包装文件系统操作之类的操作。 HP-UX 可能没有该功能,因此每个库最终都会调用它自己的实现,而任何其他未定义符号的对象都会看到其中之一。
The function is "public static", so I assume it's OOP-meaning of "static" (does not need instance), not C meaning of static (file-static; local to compilation unit). Therefore the functions are extern.
Now in Linux you have explicit right to override library symbols, both using another library or in the executable. All extern symbols in libraries are resolved using the global offset table, even the one the library actually defines itself. And while functions defined in the executable are normally not resolved like this, but the linker notices the symbols will get to the symbol table from the libraries and puts the reference to the executable-defined one there. So the libraries will see the symbol defined in the executable, if you generated it.
This is explicit feature, designed so you can do things like replace memory allocation functions or wrap filesystem operations. HP-UX probably does not have the feature, so each library ends up calling it's own implementation, while any other object that would have the symbol undefined will see one of them.
“extern”符号(C++ 中的默认符号)和“共享库 extern”之间存在差异。默认情况下,符号只是“外部”,这意味着一个“链接单元”的范围,例如可执行文件或库。
因此,预期的行为是:没有编译器错误,每个模块都使用自己的副本。
当然,在内联编译等情况下,这会导致问题......等等......
要声明符号“共享库 extern”,您必须使用“.def”文件或编译器声明。
例如,在 Visual C++ 中,这将是“_declspec(dllexport)”和“_declspec(dllimport)”。
我目前不知道 gcc 的声明,但我确信有人知道:-)
There is a difference beetween "extern" symbols (which is the default in c++) and "shared libary extern". By default symbols are only "extern" which means the scope of one "link unit" e.g. an executable or a library.
So the expected behaviour would be: no compiler error and every module works with its own copy.
That leads to problems of course in case of inline compiling etc...etc...
To declare a symbol "shared library extern" you have to use a ".def" file or an compiler declaration.
e.g. in visual c++ that would be "_declspec(dllexport)" and "_declspec(dllimport)".
I do not know the declarations for gcc at the moment but I am sure someone does :-)