在 Linux 系统上使用 C++ 链接到共享库和静态库

Linking to shared and static libraries with c++ on a Linux system

我正在搞一个测试项目,我们称它为 mytest,它有一个 .cpp 和一个 .h 文件,内容并不重要 - 假设它包含一些简单的 hello_world() 类型函数...

所以,我正在制作一个通用的 makefile 来将其编译到各种库输出中,其中输出文件夹中的 ls -l 给出:

libmytest.a
libmytest.so -> libmytest.so.1.0
libmytest.so.1 -> libmytest.so.1.0
libmytest.so.1.0

到目前为止一切正常,我的共享/静态库已创建。

现在我的 make 文件中有一个 make install 目标,它基本上将 header 复制到 /usr/local/include 并将所有这些库文件复制到 /usr/local/lib

然后我制作了另一个名为 usertest.cpp 的测试 cpp 文件(对不起 not-very-imaginative/descriptive 名称),它链接到库文件。

我通过各种方式编译:

  1. g++ -Wall -Werror -I. -lmytest
  2. g++ -Wall -Werror -I. -lmytest -static

然后我删除了libmytest.so*文件所以我在/usr/local/lib中只有libmytest.a库文件然后我做了同样的测试:

  1. g++ -Wall -Werror -I. -lmytest
  2. g++ -Wall -Werror -I. -lmytest -static

最后我删除了 libmytest.a 文件并 复制回 .so 文件 所以我只有 libmytest.so* 库文件在 /usr/local/lib 然后我做了同样的测试:

  1. g++ -Wall -Werror -I. -lmytest
  2. g++ -Wall -Werror -I. -lmytest -static

文件大小结果(以字节为单位)为:

1. 7736        - Makes sense, all libs dynamically linked
2. 19674488    - Makes sense, all libs statically linked
3. 64908       - hmm... not really sure why
4. 19674488    - Makes sense, same as 2.
5. 7736        - Makes sense, same as 1.
6. failed      - Makes sense, no .so files!

我有三种文件大小,小的 (7736) 是完全动态链接的。大号是静态链接的……这个中号 (64908) 是什么?所以我有疑问:

注意 所有输出 运行 正常并从库中调用函数。

很可能 libmytest.a 不是那个在二进制文件大小增加中起主要作用的人,而是更大的标准库(这解释了为什么大小在 3. 中没有增加太多)。

您可以使用 ldd:

调查二进制文件的所有动态依赖关系
 ldd a.out

(使用 -static 后它们中的哪些正在消失)。

For 1. I assume the system looks for .so libraries first and .a libraries second?

大致正确,但请继续阅读。

For 3. What happened here? - is it dynamically linking the system libs but when it sees my .a lib it dynamically links it?

静态库不能动态链接:它是静态链接的。共享(=动态)系统库是链接的, 假设链接器找到并喜欢的系统库实际上是共享库。

默认情况下,链接选项 -lmytest 指示链接器搜索名为 libmytest.so 的输入文件(共享库) 或 libmytest.a(静态库),首先在您在命令行中指定的搜索目录中 -Ldirname 选项,按照指定的顺序,然后在其默认搜索目录中,按照配置的顺序。 当它在其中一个目录中找到这些文件中的任何一个时,它就会停止搜索。如果它发现它们都在 相同的目录,然后它选择共享库 libmytest.so。所选文件(如果有)被输入到链接中。 如果搜索不成功,链接器会给出一个错误:cannot find -lmytest.

此默认行为可以通过选项 -static 更改。如果它出现在命令行的任何地方,链接器 忽略所有共享库:那么-lmytest只能找libmytest.a满足,静态系统库也必须找

/usr/local/lib 是链接器的默认搜索目录之一。所以当你执行:

g++ -Wall -Werror -I. -lmytest

在场景 (3) 中,/usr/local/lib/libmytest.a 被链接器找到而 /usr/local/lib/libmytest.so 没有, libmytest.a满足-lmytest,输入联动。链接器对共享库的默认 首选项 不受影响。

libmytest.a 的链接对可执行文件大小的贡献并不明显。

静态库——与共享库非常不同——不是链接器生成的 ELF 二进制文件。这是 ar archive 个目标文件,由 ar 生成:它只是一包文件 恰好是目标文件。

默认情况下,当 ar 存档被输入到链接器时,它会在包中查找任何目标文件 为已经从目标文件中产生的任何未定义符号引用提供定义 检查存档时链接到输出文件(程序或共享库)。如果它发现任何 这样的目标文件,它从存档中提取它们并将 它们 链接到输出文件中,就像它们一样 已在命令行中单独列出,而根本未提及存档。除了作为一个袋子 可以选择目标文件,存档对链接没有任何贡献。

因此,如果 libmytest.a 中有 N 个目标文件,将该存档输入链接可能 将 0 到 N 个目标文件贡献给输出文件,具体取决于对成员的未定义引用 该组目标文件在链接中较早产生,哪些目标文件为这些文件提供定义 参考资料。

即使您确切知道 libmytest.a 中的哪些目标文件将在您的链接中需要,您也不能 得出结论,它们的大小之和将添加到输出文件的大小中。目标文件是 由编译器分成,节是链接器输入和输出的最小单位 认识。默认情况下,仅当该部分提供链接器对链接必须定义的某些符号的选定定义时,链接器才会保留用于输出的输入部分。如果输入部分没有这样的用途,则 链接器只会丢弃它。因此,即使链接了目标文件,链接器也可能会忽略冗余部分 在输出文件中。

-l | --library 链接器选项的行为记录在 2.1 Command Line OptionsGNU ld manual