通過一個簡單的例子,體驗一下Linux動態(tài)庫soname的使用。
假設(shè)有一個動態(tài)庫:libbar.so.1.1.0,其對應(yīng)的三個名稱如下。
- realname:libbar.so.1.1.0
- soname:libbar.so.1
- 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對應(yīng)的符號鏈接文件。
$ 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,否則會提示找不到動態(tài)庫。
$ gcc foo.c -o foo -L. -lbar
/usr/bin/ld: cannot find -lbar
collect2: ld returned 1 exit status
#生成linkname的服務(wù)鏈接文件
$ 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的動態(tài)庫的命名格式是libbar.so.x.y.z,最后一個z版本的變動一定是兼容的。y版本升級一般向前兼容。所以這個y和z不能寫死。x版本變動一般是不兼容升級。所以使用soname是最為合理的。
再做一個有意思的測試。將libbar.so.1重命名為aaa,然后同樣創(chuàng)建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系統(tǒng)里面找個實際的例子看看。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