如何在GCC中链接到共享库的特定版本

53

我正在编译使用libcurl的代码,运行环境是Debian Linux系统。我的开发机器运行Debian 5,但我希望生成的二进制文件也能在旧的Debian 4系统上使用。

我发现如果我指定-lcurl,它会链接到libcurl.so.4,但是Debian 4系统仅有libcurl.so.3。

有没有办法告诉GCC链接到libcurl.so.3(在Debian 4和5中都存在)或者只链接到libcurl.so,以便使用任何可用的版本?


2
在旧版的Debian中,libcurl.so不是一个指向libcurl.so.3的符号链接吗?我的意思是,默认情况下-lcurl不能正常工作,这看起来很奇怪。 - Laurynas Biveinis
kastauyra:版本不具备二进制兼容性,或者至少不能假定具有该兼容性。因此,在链接时会记录二进制文件中链接的主要版本:如果在较新系统上编译,则需要第4版,而在旧系统上则无法运行。(实际上,它记录的是 soname,这是一个字符串,存储在库文件中,按照惯例但不一定是“libcurl.so.3”之类的东西) - Mark Baker
4个回答

94

使用-l:libcurl.so.3替代-lcurl 当然还需要使用-L _installed_path_


2
当它被符号链接时,出现相同的问题。-l:libX.so链接到libX.so.Y而不是libX.so。 - Gregory
1
即使使用了rpath,这对我来说也不起作用。ldd显示它链接了一件事情。链接的输出显示了正确的文件名。但是strace确认它正在加载file.2而不是file.2.3,而且它不是一个符号链接。 - jgmjgm
无论我用符号链接替换实际文件并删除所有其他库,它仍然链接到特定版本,这可能是从SONAME获取的原因,所以它对我来说并不真正起作用。 - igor

32
你可以使用语法-l:libfoo.so.1来传递特定版本的共享库文件,而不是使用-lfoo这种指定库名称的语法,遵循链接器命令行上的约定libfoo.so。根据选项--library=namespec链接器文档部分可以看到,它应该能够满足你的需求。

如果namespec的形式为:filename,ld将在库路径中搜索名为filename的文件

为了更详细地说明如何通过示例链接到特定版本,请考虑一个包含两个版本相同的库的系统,即libfoo.so.1.0libfoo.so.2.0,安装在其中一个库目录中,例如/lib
$ ls -l /lib/libfoo*

lrwxrwxrwx root root /lib/libfoo.so     -> /lib/libfoo.so.2
lrwxrwxrwx root root /lib/libfoo.so.1   -> /lib/libfoo.so.1.1
-rwxr-xr-x root root /lib/libfoo.so.1.0
-rwxr-xr-x root root /lib/libfoo.so.1.1
lrwxrwxrwx root root /lib/libfoo.so.2   -> /lib/libfoo.so.2.2
-rwxr-xr-x root root /lib/libfoo.so.2.0
-rwxr-xr-x root root /lib/libfoo.so.2.1
-rwxr-xr-x root root /lib/libfoo.so.2.2

# ldconfig -p | grep libfoo
libfoo.so.2 (libc6,x86-64) => /lib/libfoo.so.2
libfoo.so.1 (libc6,x86-64) => /lib/libfoo.so.1
libfoo.so (libc6,x86-64)   => /lib/libfoo.so

使用选项-lfoo编译的程序将使链接器寻找一个依赖于命名约定的文件,并解析为/lib/libfoo.so(用于共享库对象)或/lib/libfoo.a(用于静态库对象)。

库使用一种特殊的文件名约定:一个名为foo的库应该存在于文件libfoo.so或libfoo.a中。

与此相反,使用选项-l:libfoo.so.1编译的程序将链接到/lib/libfoo.so.1,它本身是一个指向libfoo.so.1.1的符号链接,如上面的列表所示,这是从1.0进行的次要更新。
最后,使用选项-l:libfoo.so.2编译的程序将与/lib/libfoo.so.2进行链接,而该文件本身目前是指向libfoo.so.2.2的符号链接,正如上面的列表所示,这是从2.0和2.1进行的次要更新。
如果您安装了此类库的新版本,只要它是次要更新,就无需重新编译与之链接的程序,因为兼容版本应具有相同的soname,并且符号链接应相应更新。

实际库foo的版本X.Y存在于文件libfoo.so.x.y中。在库文件内部,记录了一个名为libfoo.so.x的soname,以表示其兼容性。

$ ls -l /lib/libfoo.so.2*

lrwxrwxrwx root root /lib/libfoo.so.2   -> /lib/libfoo.so.2.3
[...]
-rwxr-xr-x root root /lib/libfoo.so.2.3

3
几乎可以实现,但在 Debian 上,libcurl.so 只是指向 lubcurl.so.4 的符号链接,因此它仍然链接到 libcurl.so.4。 - Adam Pierce
尽管这并不完全适用于Debian 5上的libcurl,但我会将其标记为已接受的答案。我最终所做的是从Debian 4系统获取libcurl.so.3的副本,并通过指定.so文件名直接链接到它,就像bdonlan建议的那样。 - Adam Pierce
不知道为什么对我不起作用。我执行 gcc -L. test.c -l:libavs.so.0.2.0,但是 ldd 显示引用的是 libavs.so.0。我能告诉它链接到确切的文件名吗? - Osman-pasha

1
我认为正确的方法是使用链接器的--filter--auxiliary标志。这些标志并没有很好地记录,但应该允许您根据您所安装的机器从不同版本的同一库中加载符号。

1

你可以在项目中创建一个符号链接,将其链接到 .3 版本,这样你就可以在编译时使用 -L 参数了。不过我不确定是否会有名称冲突,但你可以把它命名为 libcurl-old.so 以防万一。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接