【问题标题】:Linking to shared and static libraries with c++ on a Linux system在 Linux 系统上使用 c++ 链接到共享库和静态库
【发布时间】:2017-12-15 13:38:10
【问题描述】:

我正在处理一个测试项目,我们称之为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 目标,它基本上将标头复制到 /usr/local/include 并将所有这些库文件复制到 /usr/local/lib

然后我制作了另一个名为 usertest.cpp 的测试 cpp 文件(对不起,不是很有想象力/描述性的名称),它链接到库文件。

我以各种方式编译:

  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 文件所以我只有 /usr/local/lib 中的 libmytest.so* 库文件然后我做了同样的测试:

  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)是什么?所以我有问题:

  • 对于 1. 我假设系统首先查找 .so 库,然后查找 .a 库?
  • 对于 3. 这里发生了什么? - 它是否动态链接系统库,但是当它看到我的 .a 库时,它会动态链接它?

注意所有输出都运行良好并调用库中的函数。

【问题讨论】:

  • 也许是为什么 5 个链接更有趣 - 你已经删除了所有的库,但它仍然可以工作
  • @UKMonkey 啊,不-我可能没有解释得那么好....我的意思是我删除了 .a 文件和 put back .so 文件....我会重新措辞那一点……

标签: c++ shared-libraries static-libraries lib


【解决方案1】:

对于 1. 我假设系统首先查找 .so 库,然后查找 .a 库?

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

对于 3. 这里发生了什么? - 它是否动态链接系统库,但是当它看到我的 .a 库时它会动态链接它?

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

默认情况下,链接选项-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

【讨论】:

  • 感谢您花时间以易于理解的细节进行解释:)
【解决方案2】:

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

您可以使用ldd 调查二进制文件的所有动态依赖项:

 ldd a.out

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多