Linux动态库soname的使用(转载)

通过一个简单的例子,体验一下Linux动态库soname的使用。

假设有一个动态库:libbar.so.1.1.0,其对应的三个名称如下。

1 realname:libbar.so.1.1.0
2 soname:libbar.so.1
3 linkname:libbar.so

先生成一个libbar.so,通过-Wl,-soname指定soname为libbar.so.1。

$ g++ -fPIC -shared -Wl,-soname,libbar.so.1 -o libbar.so.1.1.0

$ readelf -d libbar.so.1.1.0

Dynamic section at offset 0x520 contains 24 entries:
  Tag        Type                         Name/Value
 0x0000000000000001 (NEEDED)             Shared library: [libstdc++.so.6]
 0x0000000000000001 (NEEDED)             Shared library: [libm.so.6]
 0x0000000000000001 (NEEDED)             Shared library: [libgcc_s.so.1]
 0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]
 0x000000000000000e (SONAME)             Library soname: [libbar.so.1]
 0x000000000000000c (INIT)               0x398
 0x000000000000000d (FINI)               0x4d8
 0x000000006ffffef5 (GNU_HASH)           0x120
 0x0000000000000005 (STRTAB)             0x248
 0x0000000000000006 (SYMTAB)             0x158
 0x000000000000000a (STRSZ)              160 (bytes)
 0x000000000000000b (SYMENT)             24 (bytes)
 0x0000000000000003 (PLTGOT)             0x2006f8
 0x0000000000000002 (PLTRELSZ)           24 (bytes)
 0x0000000000000014 (PLTREL)             RELA
 0x0000000000000017 (JMPREL)             0x380
 0x0000000000000007 (RELA)               0x320
 0x0000000000000008 (RELASZ)             96 (bytes)
 0x0000000000000009 (RELAENT)            24 (bytes)
 0x000000006ffffffe (VERNEED)            0x300
 0x000000006fffffff (VERNEEDNUM)         1
 0x000000006ffffff0 (VERSYM)             0x2e8
 0x000000006ffffff9 (RELACOUNT)          1
 0x0000000000000000 (NULL)               0x0

使用ldconfig生成soname对应的符号链接文件。

$ ldconfig -n /tmp/

$ ll libbar.so.1
lrwxrwxrwx 1 henshao users 15 Nov 28 01:00 libbar.so.1 -> libbar.so.1.1.0

接着写一个程序使用libbar.so

#链接需要linkname,否则会提示找不到动态库。
$ gcc foo.c -o foo -L. -lbar
/usr/bin/ld: cannot find -lbar
collect2: ld returned 1 exit status

#生成linkname的服务链接文件
$ ln -s libbar.so.1 libbar.so

#这次编译能成功
$ gcc foo.c -o foo -L. -lbar

#可见foo依赖的是soname,而不是linkname。
$ ldd foo
    linux-vdso.so.1 =>  (0x00007fffa27ff000)
    /opt/alisentry/$LIB/alisentry_exec.so => /opt/alisentry/lib64/alisentry_exec.so (0x00007fabfda17000)
    /opt/alisentry/$LIB/alisentry_kill.so => /opt/alisentry/lib64/alisentry_kill.so (0x00007fabfd814000)
    libbar.so.1 => not found
    libc.so.6 => /lib64/libc.so.6 (0x00007fabfd4b0000)
    libdl.so.2 => /lib64/libdl.so.2 (0x00007fabfd2ac000)
    /lib64/ld-linux-x86-64.so.2 (0x00007fabfdc1d000)

为什么主程序会依赖soname,而不是linkname和realname?这是因为Linux的动态库的命名格式是libbar.so.x.y.z,最后一个z版本的变动一定是兼容的。y版本升级一般向前兼容。所以这个y和z不能写死。x版本变动一般是不兼容升级。所以使用soname是最为合理的。

再做一个有意思的测试。将libbar.so.1重命名为aaa,然后同样创建aaa的符号链接为libbar.so。

$ ll libbar.so* aaa
lrwxrwxrwx 1 henshao users    3 Nov 28 01:28 libbar.so -> aaa
lrwxrwxrwx 1 henshao users   15 Nov 28 01:27 aaa -> libbar.so.1.1.0
-rwxr-xr-x 1 henshao users 5301 Nov 28 01:27 libbar.so.1.1.0

有趣的是,foo依然知道依赖libbar.so.1,而不是aaa。

$ gcc foo.c -o foo -L. -lbar

$ ldd foo
    linux-vdso.so.1 =>  (0x00007fff19ce7000)
    /opt/alisentry/$LIB/alisentry_exec.so => /opt/alisentry/lib64/alisentry_exec.so (0x00007fb07c389000)
    /opt/alisentry/$LIB/alisentry_kill.so => /opt/alisentry/lib64/alisentry_kill.so (0x00007fb07c186000)
    libbar.so.1 => not found
    libc.so.6 => /lib64/libc.so.6 (0x00007fb07be22000)
    libdl.so.2 => /lib64/libdl.so.2 (0x00007fb07bc1e000)
    /lib64/ld-linux-x86-64.so.2 (0x00007fb07c58f000)

在Linux系统里面找个实际的例子看看。wget依赖libz.so.1。

$ ldd /usr/bin/wget
    linux-vdso.so.1 =>  (0x00007fff631ff000)
    /opt/alisentry/$LIB/alisentry_exec.so => /opt/alisentry/lib64/alisentry_exec.so (0x00007fe75c93e000)
    /opt/alisentry/$LIB/alisentry_kill.so => /opt/alisentry/lib64/alisentry_kill.so (0x00007fe75c73b000)
    libssl.so.6 => /lib64/libssl.so.6 (0x00007fe75c4e1000)
    libcrypto.so.6 => /lib64/libcrypto.so.6 (0x00007fe75c190000)
    libdl.so.2 => /lib64/libdl.so.2 (0x00007fe75bf8c000)
    libz.so.1 => /usr/lib64/libz.so.1 (0x00007fe75bd78000)
    librt.so.1 => /lib64/librt.so.1 (0x00007fe75bb6f000)
    libc.so.6 => /lib64/libc.so.6 (0x00007fe75b816000)
    libgssapi_krb5.so.2 => /usr/lib64/libgssapi_krb5.so.2 (0x00007fe75b5e8000)
    libkrb5.so.3 => /usr/lib64/libkrb5.so.3 (0x00007fe75b353000)
    libcom_err.so.2 => /lib64/libcom_err.so.2 (0x00007fe75b150000)
    libk5crypto.so.3 => /usr/lib64/libk5crypto.so.3 (0x00007fe75af2b000)
    /lib64/ld-linux-x86-64.so.2 (0x00007fe75cb44000)
    libpthread.so.0 => /lib64/libpthread.so.0 (0x00007fe75ad10000)
    libkrb5support.so.0 => /usr/lib64/libkrb5support.so.0 (0x00007fe75ab07000)
    libkeyutils.so.1 => /lib64/libkeyutils.so.1 (0x00007fe75a905000)
    libresolv.so.2 => /lib64/libresolv.so.2 (0x00007fe75a6f0000)
    libselinux.so.1 => /lib64/libselinux.so.1 (0x00007fe75a4d7000)
    libsepol.so.1 => /lib64/libsepol.so.1 (0x00007fe75a291000)

$ll /usr/lib64/libz.so*
lrwxrwxrwx 1 root root 19 Dec  3  2015 /usr/lib64/libz.so -> ../../lib64/libz.so
lrwxrwxrwx 1 root root 21 Dec  3  2015 /usr/lib64/libz.so.1 -> ../../lib64/libz.so.1
lrwxrwxrwx 1 root root 25 Dec  3  2015 /usr/lib64/libz.so.1.2.3 -> ../../lib64/libz.so.1.2.3

$ ll /lib64/libz*
lrwxrwxrwx 1 root root    13 Dec  3  2015 /lib64/libz.so -> libz.so.1.2.3
lrwxrwxrwx 1 root root    13 Dec  3  2015 /lib64/libz.so.1 -> libz.so.1.2.3
-rwxr-xr-x 1 root root 83280 May 28  2014 /lib64/libz.so.1.2.3

$ readelf -d /usr/lib64/libz.so.1.2.3

Dynamic section at offset 0x139f8 contains 21 entries:
  Tag        Type                         Name/Value
 0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]
 0x000000000000000e (SONAME)             Library soname: [libz.so.1]
 0x000000000000000c (INIT)               0x1b48
 0x000000000000000d (FINI)               0xc958
 0x000000006ffffef5 (GNU_HASH)           0x158
 0x0000000000000005 (STRTAB)             0xdd8
 0x0000000000000006 (SYMTAB)             0x3e8
 0x000000000000000a (STRSZ)              1148 (bytes)
 0x000000000000000b (SYMENT)             24 (bytes)
 0x0000000000000003 (PLTGOT)             0x213bc8
 0x0000000000000002 (PLTRELSZ)           1200 (bytes)
 0x0000000000000014 (PLTREL)             RELA
 0x0000000000000017 (JMPREL)             0x1698
 0x0000000000000007 (RELA)               0x1368
 0x0000000000000008 (RELASZ)             816 (bytes)
 0x0000000000000009 (RELAENT)            24 (bytes)
 0x000000006ffffffe (VERNEED)            0x1328
 0x000000006fffffff (VERNEEDNUM)         1
 0x000000006ffffff0 (VERSYM)             0x1254
 0x000000006ffffff9 (RELACOUNT)          26
 0x0000000000000000 (NULL)               0x0
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1. 什么是静态库?什么是动态库? - 静态库是在编译时被链接到可执行文件中的库,它包含了所有的函数和数据,因此可执行文件较大,但运行时不需要加载额外的库文件。 - 动态库是在程序运行时被加载的库,它包含了多个可执行文件可以共享的函数和数据,因此可执行文件较小,但需要在运行时加载额外的库文件。 2. 静态库和动态库的优缺点是什么? - 静态库的优点是可靠性高,因为所有的代码都被编译到可执行文件中,不需要额外的库文件。缺点是可执行文件较大,占用磁盘空间较大,且无法在运行时更新库文件。 - 动态库的优点是可执行文件较小,因为共享库文件可以被多个可执行文件共享。缺点是依赖性高,因为需要在运行时加载库文件,如果缺少或版本不匹配会导致程序无法运行。 3. 动态库的加载过程是怎样的? - 当程序需要访问动态库中的函数或数据时,操作系统会检查可执行文件的依赖关系,加载动态库文件到内存中,并在符号表中查找相应的函数或数据。 - 如果找到了相应的函数或数据,程序就可以调用库函数或访问库数据。如果没有找到,操作系统会抛出未定义符号的错误。 4. 如何编译静态库和动态库? - 编译静态库可以使用命令"ar"和"ranlib",例如: ``` gcc -c file1.c file2.c ar rcs libmylib.a file1.o file2.o ``` - 编译动态库可以使用命令"gcc"和"-shared"选项,例如: ``` gcc -c -fpic file1.c file2.c gcc -shared -o libmylib.so file1.o file2.o ``` 5. 如何使用静态库和动态库? - 使用静态库可以在编译时链接库文件,例如: ``` gcc -o myprog main.c -L/path/to/lib -lmylib ``` - 使用动态库可以在程序运行时加载库文件,例如: ``` LD_LIBRARY_PATH=/path/to/lib ./myprog ``` 6. 如何避免动态库版本不匹配的问题? - 在编译动态库时,可以使用版本号来标识不同的库版本,例如: ``` gcc -shared -Wl,-soname,libmylib.so.1 -o libmylib.so.1.0.0 file1.o file2.o ``` - 在程序中调用库函数时,可以使用版本号来指定库版本,例如: ``` dlopen("libmylib.so.1", RTLD_NOW); ``` 这样可以确保程序使用正确的库版本,并避免版本不匹配的问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值