链接器为什么不抱怨重复的符号? [英] Why doesn't the linker complain of duplicate symbols?

查看:74
本文介绍了链接器为什么不抱怨重复的符号?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个dummy.hpp

I have a dummy.hpp

#ifndef DUMMY
#define DUMMY
void dummy();
#endif

和一个dummy.cpp

and a dummy.cpp

#include <iostream>
void dummy() {
      std::cerr << "dummy" << std::endl;
}

和使用dummy()的main.cpp

and a main.cpp which use dummy()

#include "dummy.hpp"
int main(){

    dummy();
    return 0;
}

然后我将dummy.cpp编译为三个库libdummy1.alibdummy2.alibdummy.so:

Then I compiled dummy.cpp to three libraries, 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

  1. 当我尝试编译main并链接虚拟库时

  1. When I try compile main and link the dummy libs

g++ -o main main.cpp -L. -ldummy1 -ldummy2

链接器没有产生重复的符号错误.当我静态链接两个相同的库时,为什么会发生这种情况?

There is no duplicate symbol error produced by linker. Why does this happen when I link two identical libraries statically?

当我尝试

g++ -o main main.cpp -L. -ldummy1 -ldummy

也没有重复的符号错误,为什么?

There is also no duplicate symbol error, Why?

加载程序似乎总是选择动态库,而不是选择.o文件中编译的代码.

The loader seems always to choose dynamic libs and not the code compiled in the .o files.

这是否意味着如果同时在.a.so文件中同时从.so文件中加载相同的符号?

Does it mean the same symbol is always loaded from the .so file if it is both in a .a and a .so file?

这是否意味着静态库中的静态符号表中的符号永远不会与.so文件中的动态符号表中的符号冲突?

Does it mean symbols in the static symbol table in static library never conflict with those in the dynamic symbol table in a .so file?

推荐答案

在方案1(双静态库)或方案2(静态和共享库)中都没有错误,因为链接器从静态库中获取了第一个对象文件或它遇到的第一个共享库,它提供了尚未为其定义的符号的定义.它只是忽略了以后对同一符号的任何定义,因为它已经有了一个好的符号.通常,链接器仅从库中获取所需的内容.对于静态库,这是完全正确的.对于共享库,如果共享库满足任何缺少的符号,则所有符号都可用.对于某些链接器,共享库的符号无论如何都可以使用,但是其他版本仅在共享库提供至少一个定义的情况下记录使用共享库的情况.

There's no error in either Scenario 1 (dual static libraries) or Scenario 2 (static and shared libraries) because the linker takes the first object file from a static library, or the first shared library, that it encounters that provides a definition of a symbol it has not yet got a definition for. It simply ignores any later definitions of the same symbol because it already has a good one. In general, the linker only takes what it needs from a library. With static libraries, that's strictly true. With shared libraries, all the symbols in the shared library are available if it satisfied any missing symbol; with some linkers, the symbols of the shared library may be available regardless, but other versions only record the use a shared library if that shared library provides at least one definition.

这也是为什么您需要在目标文件之后链接库.您可以在链接命令中添加dummy.o,只要出现在库之前,就不会有麻烦.在库之后添加dummy.o文件,您将得到双重定义的符号错误.

It's also why you need to link libraries after object files. You could add dummy.o to your linking commands and as long as that appears before the libraries, there'll be no trouble. Add the dummy.o file after libraries and you'll get doubly-defined symbol errors.

唯一一次遇到这种双重定义问题的情况是,库1中有一个同时定义dummyextra的目标文件,而库2中有一个定义了dummy和<的目标文件. c13>,并且代码需要同时包含extraalternative的定义-那么您有重复的dummy定义会引起麻烦.实际上,目标文件可能在单个库中,并且会造成麻烦.

The only time you run into problems with this double definitions is if there's an object file in Library 1 that defines both dummy and extra, and there's an object file in Library 2 that defines both dummy and alternative, and the code needs the definitions of both extra and alternative — then you have duplicate definitions of dummy that cause trouble. Indeed, the object files could be in a single library and would cause trouble.

考虑:

/* 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().

You won't be able to link the object files from the three source files shown because of the double-definition of dummy, even though the main code does not call dummy().

关于:

加载器似乎总是选择动态库,而不是在.o文件中编译.

The loader seems always to choose dynamic libs and not compiled in the .o files.

否;链接器始终尝试无条件加载目标文件.当在命令行遇到库时,它将扫描库,并收集所需的定义.如果目标文件在库之前,则没有问题,除非两个目标文件定义相同的符号(一个定义规则"会响起钟声吗?).如果某些目标文件遵循库文件,那么如果库文件定义了后来的目标文件所定义的符号,则可能会发生冲突.请注意,当它开始时,链接器正在寻找main的定义.它从被告知的每个目标文件中收集定义的符号和引用的符号,并不断添加代码(从库中),直到定义了所有引用的符号为止.

No; the linker always attempts to load object files unconditionally. It scans libraries as it encounters them on the command line, collecting definitions it needs. If the object files precede the libraries, there's not a problem unless two of the object files define the same symbol (does 'one definition rule' ring any bells?). If some of the object files follow libaries, you can run into conflicts if libraries define symbols that the later object files define. Note that when it starts out, the linker is looking for a definition of main. It collects the defined symbols and referenced symbols from each object file it is told about, and keeps adding code (from libraries) until all the referenced symbols are defined.

这是否意味着如果同时在.a.so文件中都从.so文件中加载了相同的符号?

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中的定义,因为链接器不再在寻找该符号的定义-它已经有一个.

No; it depends which was encountered first. If the .a was encountered first, the .o file is effectively copied from the library into the executable (and the symbol in the shared library is ignored because there's already a definition for it in the executable). If the .so was encountered first, the definition in the .a is ignored because the linker is no longer looking for a definition of that symbol — it's already got one.

这是否意味着静态库中的静态符号表中的符号永远不会与.so文件中的动态符号表中的符号冲突?

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?

您可能会有冲突,但是遇到的第一个定义将解析链接器的符号.如果满足引用的代码通过定义所需的其他符号引起冲突,则只会发生冲突.

You can have conflicts, but the first definition encountered resolves the symbol for the linker. It only runs into conflicts if the code that satisfies the reference causes a conflict by defining other symbols that are needed.

如果我链接了2个共享库,是否会发生冲突并且链接阶段失败?

If I link 2 shared libs, can I get conflicts and the link phase failed?

正如我在评论中指出的那样:

As I noted in a comment:

我的直接反应是是的,你可以".这将取决于两个共享库的内容,但是我相信您可能会遇到问题. […沉思…] 您将如何显示此问题? ……这并不像乍看起来那样容易.证明这种问题需要什么? …或者我是否对此思考过多? … […该玩一些示例代码了……]

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…]

经过一些试验,我的临时经验性回答是不,你不能"(或不,至少在某些系统上,你不会发生冲突").我很高兴我发了言.

After some experimentation, my provisional, empirical answer is "No, you can't" (or "No, on at least some systems, you don't run into a conflict"). I'm glad I prevaricated.

采用上面显示的代码(2个标头,3个源文件),并在Mac OS X 10.10.5(Yosemite)上与GCC 5.3.0一起运行,我可以运行:

Taking the code shown above (2 headers, 3 source files), and running with GCC 5.3.0 on Mac OS X 10.10.5 (Yosemite), I can run:

$ 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)
$

在Mac OS X上使用.so作为扩展名(通常为.dylib)是一种常规做法,但似乎可以使用.

It is aconventional to use .so as the extension on Mac OS X (it's usually .dylib), but it seems to work.

然后,我修改了.cpp文件中的代码,以便extra()return之前调用dummy()alternative()main()也是如此.重新编译并重建共享库后,我运行了程序.输出的第一行来自main()调用的dummy().然后,您将按顺序获得alternative()extra()产生的其他两行,因为return extra(alternative(54));的调用序列需要这样做.

Then I revised the code in the .cpp files so that extra() calls dummy() before the return, and so does alternative() and main(). After recompiling and rebuilding the shared libraries, I ran the programs. The first line of output is from the dummy() called by main(). Then you get the other two lines produced by alternative() and extra() in that order because the calling sequence for return extra(alternative(54)); demands that.

$ 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()功能. (请注意,对于这种行为,发现平台之间的差异并不奇怪.我已经确定了在哪里测试代码.如果您在某些平台上发现不同的行为,请告诉我.)

Note that the function called by main() is the first one that appears in the libraries it is linked with. But (on Mac OS X 10.10.5 at least) the linker does not run into a conflict. Note, though, that the code in each shared object calls 'its own' version of dummy() — there is disagreement between the two shared libraries about which function is dummy(). (It would be interesting to have the dummy() function in separate object files in the shared libraries; then which version of dummy() gets called?) But in the extremely simple scenario shown, the main() function manages to call just one of the dummy() functions. (Note that I'd not be surprised to find differences between platforms for this behaviour. I've identified where I tested the code. Please let me know if you find different behaviour on some platform.)

这篇关于链接器为什么不抱怨重复的符号?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆