首页 > 解决方案 > 如何区分同名的共享库和静态库

问题描述

我有一个由两部分组成的库。一种是直接链接到应用程序的静态“存根”(并包含“主”功能 - 应用程序由那里的回调组成)。其余的库模块包含在共享库中。这一切都是在 AIX 上设置的,其中共享库通过引用“导出”文件来链接。所以第二个库没有遵循正常的 lib.so 命名约定。实际名称是在链接时从导出文件中提取的。所以在 AIX 上,库是 libemapp.a 和 shlibemapp.o。makefile 中仅引用了 libemapp.a - 通过此变量:

LINKEMAPP = -L$(LIBDIR) -lemapp -bI:$(LIBDIR)/shlibemapp.exp

现在我将这个烂摊子移植到 linux,并且需要对共享部分使用普通的 lib.so 命名约定(我假设)。我的第一直觉是为静态存根和共享主库使用相同的名称。而且我看到 /usr/lib/... 中有两个库使用相同名称的情况。我假设这些实际上是相同代码的静态和共享版本 - 而不是两个不同的库。

-rw-r--r-- 1 root root 103370 May 23  2018 libtic.a
lrwxrwxrwx 1 root root     11 May 23  2018 libtic.so -> libtic.so.5
lrwxrwxrwx 1 root root     13 May 23  2018 libtic.so.5 -> libtic.so.5.9
-rw-r--r-- 1 root root  63488 May 23  2018 libtic.so.5.9

我可以轻松地使用不同的名称,但我只是想知道。您如何在链接时指定两个库 -ltic 中的哪一个?你能覆盖任何默认选择吗?

如果可能的话,我可以拥有一个静态的 libemapp.a 和一个共享的 libemapp.so,并将它们都链接到一个应用程序。应该不会吧?

标签: shared-libraries

解决方案


您如何在链接时指定两个库 -ltic 中的哪一个?

(当libtic.alibtic.so位于同一个链接器搜索目录中时)。

默认情况下,GNU binutils 链接器ld 遵循该选项-ltic,首先在 -Ldir命令行顺序中指定的目录中搜索,然后按照配置顺序在其默认搜索目录中搜索文件libtic.so(共享库)或libtic.a (静态图书馆)。它找到的第一个被选择并输入到链接中,除非它们都在同一个搜索目录中找到,在这种情况下libtic.so被选择。

你能覆盖任何默认选择吗?

是的。有三种可能的方法:-

1

如果以通常的方式调用链接器,通过 GCC 前端之一(gccg++gfortran等),那么您可以将选项传递-static给前端。这将指示它在任何输入文件之前将选项-static(aka -Bstatic)插入到它为它生成的命令行中 。ld链接器将通过忽略随后通过库搜索发现的共享库来服从-(static|Bstatic),直到并且除非它使用了抵消-Bdynamic选项。所以:

gcc ... -static -ltic ...

将强制libtic.a优先于libtic.so,同样适用-lname于链接中的每个选项(包括由前端静默附加的默认值,而不仅仅是您指定的那些)

2

任何选项opt0[,...,optN]ld可以通过带有-Wl选项的 GCC 前端传递给链接,该选项具有用法;

-Wl,opt0[,...,optN]

所以:

gcc ... Wl,-Bstatic -ltic ...

相当于1中的图示(前提是命令的尾部...部分不包含Wl,-Bdynamic)。

3

ld选项-l:name是 GCC 前端的一个变体,-lname它同样被 GCC 前端所接受,并被原封不动地传递给链接。-l:name指示链接器在每个目录中搜索具有确切名称的文件name。所以

gcc ... -l:libtic.a ...

将导致libtic.a优先于 输入libtic.so,即使后者存在于同一目录中并且有资格链接。然而,

gcc ... -static -l:libtic.so

会导致联动错误:

attempted static link of dynamic object `./libtic.so'

即使libtic.so被找到,因为-Bstatic在考虑时生效-l:libtic.so

除了-l选项的这三个变体之外,您当然可以简单地将 aribitary 指定[path/]name为链接参数,并且链接器将尝试解析为绝对文件名并输入该文件,前提是它在有效[path/]name时不解析为共享库-Bstatic.

我可以有一个静态的 libemapp.a 和一个共享的 libemapp.so,并将它们都链接到一个应用程序。

是的。每次出现时,链接器都会重新考虑每个-l选项。

gcc ... -ltic ... -ltic ......

只是一个实例:

gcc ... -lx ... -ly ......

并且很可能导致与以下不同的联系:

gcc ... -ltic ......

即使-ltic每次都解析为同一个文件,因为当在链接中的不同点考虑同一个库时,链接器尝试解析为库提供的定义的未定义引用集很可能是不同的未定义引用集.

-ltic不必每次都解析到同一个文件。因此,只要 libtic.a和都libtic.so存在于链接器可以找到它们的位置,无论是否在同一个搜索目录中:

gcc -o prog .... -Wl,-Bstatic -ltic ... -Wl,-Bdynamic -ltic

将导致libtic.a首先输入到链接中,然后libtic.so.

这种联系的效果将是:

  • 当第一次看到时,程序中存在未解析引用的符号 将在可能的情况下静态绑定到1-ltic中存档的目标文件提供的定义。libtic.a

  • 之后,当-ltic看到第二个符号时,程序中将保留 0 个或多个具有未解析引用的符号。那些未解析的符号将在可能的情况下动态绑定到libtic.so2提供的定义。链接器不会审查或修改它已经建立的任何静态或动态绑定。

显然,如果它们分别不是同一库的静态和动态版本,则链接和 then可能有用,反之亦然,具体而言,由 定义的全局符号集 与由 定义的动态符号集不同。但显然,当它们甚至没有相同的 API 时调用它们也是不正当的。libtic.alibtic.solibtic.alibtic.solibtic.?

tic可以想象,链接同一库的静态和动态版本也可能很有用,例如,以以下形式的链接:

gcc -o prog main.o ... -Wl,-Bstatic -ltic ... -Wl,-Bdynamic -lfoo -ltic

假设在这种情况下,它x是一个符号libtic,未解析的引用由该符号定义,main.o并且在该符号libtic.a 中定义在 member 中libtic.a(x.o)。然后libtic.a(x.o)将被合并到prog并且对的引用x将静态绑定到该定义。并且假设这个定义引入了对共享库中定义的符号的新引用libfoo.so,并且其中一些定义 又引入了在libtic.

然后,链接将导致在从目标文件静态解析 libtic之前对该累积的所有引用,而 在. 链接将与以下内容相同:-Wl,-Bstaticlibtic.a(x.o), libtic.a(y.o)...-Wl,-Bdynamiclibtic.so

gcc -o prog main.o ... x.o y.o... -lfoo -ltic

有朝一日,您会遇到一个建议以这种方式链接两者libtic.a的链接场景,这并非超出可能性范围。libtic.so但我对此表示怀疑,即使您这样做,您也应该支持任何与正常做法不太相悖的合理替代方案。


[1] 提供这些定义的目标文件,而不是其他的,将从程序中提取libtic.a并物理合并到程序中,就像在链接命令中单独命名这些目标文件一样,而不是-ltic

[2] 程序将使用这些动态绑定进行注释,使运行时加载程序能够加载libtic.so到程序的进程中,并使用其定义的运行时地址修补动态引用。


推荐阅读