我有一个 dummy.hpp
#ifndef DUMMY
#define DUMMY
void dummy();
#endif
和一个 dummy.cpp
#include <iostream>
void dummy() {
std::cerr << "dummy" << std::endl;
}
和使用 dummy() 的 main.cpp
#include "dummy.hpp"
int main(){
dummy();
return 0;
}
然后我把dummy.cpp编译成三个库,
libdummy1.a
, libdummy2.a
, libdummy.so
:g++ -c -fPIC dummy.cpp
ar rvs libdummy1.a dummy.o
ar rvs libdummy2.a dummy.o
g++ -shared -fPIC -o libdummy.so dummy.cpp
g++ -o main main.cpp -L. -ldummy1 -ldummy2
链接器没有产生重复的符号错误。为什么当我静态链接两个相同的库时会发生这种情况?
g++ -o main main.cpp -L. -ldummy1 -ldummy
也没有重复符号错误,为什么?
加载器似乎总是选择动态库而不是在
.o
中编译的代码。文件。这是否意味着总是从
.so
加载相同的符号?如果文件都在 .a
中和一个 .so
文件?是否意味着静态库中的静态符号表中的符号永远不会与
.so
中的动态符号表中的符号冲突?文件?
最佳答案
在场景 1(双静态库)或场景 2(静态和共享库)中都没有错误,因为链接器从静态库或第一个共享库中获取第一个目标文件,它遇到了提供符号定义的它尚未得到定义。它只是忽略同一符号的任何后续定义,因为它已经有了一个好的定义。通常,链接器只从库中获取它需要的东西。对于静态库,这是完全正确的。对于共享库,如果满足任何缺失的符号,则共享库中的所有符号都可用;对于某些链接器,共享库的符号可能无论如何都可用,但其他版本仅记录共享库的使用,前提是该共享库提供了至少一个定义。
这也是您需要在目标文件之后链接库的原因。您可以添加 dummy.o
到您的链接命令,只要它出现在库之前,就不会有问题。添加 dummy.o
库之后的文件,你会得到双重定义的符号错误。
唯一一次遇到这种双重定义问题的情况是,库 1 中是否有一个目标文件同时定义了 dummy
和 extra
,并且在库 2 中有一个目标文件定义了 dummy
和 alternative
,并且代码需要 extra
的定义和 alternative
— 那么你有 dummy
的重复定义造成麻烦。实际上,目标文件可能位于单个库中并且会引起麻烦。
考虑:
/* file1.h */
extern void dummy();
extern int extra(int);
/* file1.cpp */
#include "file1.h"
#include <iostream>
void dummy() { std::cerr << "dummy() from " << __FILE__ << '\n'; }
int extra(int i) { return i + 37; }
/* file2.h */
extern void dummy();
extern int alternative(int);
/* file2.cpp */
#include "file2.h"
#include <iostream>
void dummy() { std::cerr << "dummy() from " << __FILE__ << '\n'; }
int alternative(int i) { return -i; }
/* main.cpp */
#include "file1.h"
#include "file2.h"
int main()
{
return extra(alternative(54));
}
由于
dummy
的双重定义,您将无法从显示的三个源文件中链接目标文件。 ,即使主代码没有调用 dummy()
.关于:
The loader seems always to choose dynamic libs and not compiled in the .o files.
不;链接器总是尝试无条件加载目标文件。它在命令行上遇到库时扫描库,收集所需的定义。如果目标文件在库之前,则没有问题,除非两个目标文件定义了相同的符号(“一个定义规则”有没有响起?)。如果某些目标文件遵循库,如果库定义了后来的目标文件定义的符号,您可能会遇到冲突。请注意,当它开始时,链接器正在寻找
main
的定义。 .它从每个被告知的目标文件中收集定义的符号和引用的符号,并不断添加代码(来自库),直到定义了所有引用的符号。Does it means the same symbol is always loaded from
.so
file, if it is both in.a
and.so
file?
不;这取决于首先遇到的是哪个。如果
.a
第一次遇到,.o
文件被有效地从库复制到可执行文件中(并且共享库中的符号被忽略,因为在可执行文件中已经有它的定义)。如果.so
第一次遇到,定义在.a
被忽略,因为链接器不再寻找那个符号的定义——它已经有了。Does it mean that symbols in static symbol table in a static library are never in conflict with those in dynamic symbol table in
.so
file?
您可能会遇到冲突,但遇到的第一个定义会解析链接器的符号。只有当满足引用的代码通过定义其他需要的符号而导致冲突时,它才会遇到冲突。
If I link 2 shared libs, can I get conflicts and the link phase failed?
正如我在评论中指出的那样:
My immediate reaction is "Yes, you can". It would depend on the content of the two shared libraries, but you could run into problems, I believe. […cogitation…] How would you show this problem? … It's not as easy as it seems at first sight. What is required to demonstrate such a problem? … Or am I overthinking this? … […time to go play with some sample code…]
经过一些实验,我的临时经验答案是“不,你不能”(或“不,至少在某些系统上,你不会遇到冲突”)。我很高兴我支支吾吾。
使用上面显示的代码(2 个标题,3 个源文件),并在 Mac OS X 10.10.5 (Yosemite) 上使用 GCC 5.3.0 运行,我可以运行:
$ g++ -O -c main.cpp
$ g++ -O -c file1.cpp
$ g++ -O -c file2.cpp
$ g++ -shared -o libfile2.so file2.o
$ g++ -shared -o libfile1.so file1.o
$ g++ -o test2 main.o -L. -lfile1 -lfile2
$ ./test2
$ echo $?
239
$ otool -L test2
test2:
libfile2.so (compatibility version 0.0.0, current version 0.0.0)
libfile1.so (compatibility version 0.0.0, current version 0.0.0)
/opt/gcc/v5.3.0/lib/libstdc++.6.dylib (compatibility version 7.0.0, current version 7.21.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1213.0.0)
/opt/gcc/v5.3.0/lib/libgcc_s.1.dylib (compatibility version 1.0.0, current version 1.0.0)
$
常规使用
.so
作为 Mac OS X 上的扩展(通常是 .dylib
),但它似乎有效。然后我修改了
.cpp
中的代码文件以便 extra()
电话dummy()
之前return
,alternative()
也是如此和 main()
.重新编译并重建共享库后,我运行了程序。第一行输出来自 dummy()
由 main()
调用.然后你得到由 alternative()
产生的另外两行和 extra()
按照这个顺序,因为 return extra(alternative(54));
的调用序列要求。$ g++ -o test2 main.o -L. -lfile1 -lfile2
$ ./test2
dummy() from file1.cpp
dummy() from file2.cpp
dummy() from file1.cpp
$ g++ -o test2 main.o -L. -lfile2 -lfile1
$ ./test2
dummy() from file2.cpp
dummy() from file2.cpp
dummy() from file1.cpp
$
注意
main()
调用的函数是第一个出现在与其链接的库中的。但是(至少在 Mac OS X 10.10.5 上)链接器不会遇到冲突。但是请注意,每个共享对象中的代码都调用了 dummy()
的“自己的”版本。 — 两个共享库之间关于哪个函数是dummy()
存在分歧. (将 dummy()
函数放在共享库中的单独目标文件中会很有趣;然后会调用哪个版本的 dummy()
?)但是在所示的极其简单的场景中,main()
函数设法只调用 dummy()
之一职能。 (请注意,对于这种行为,发现平台之间的差异我不会感到惊讶。我已经确定了我测试代码的位置。如果您在某些平台上发现不同的行为,请告诉我。)
关于c++ - 为什么链接器不提示重复的符号?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34454355/