转自:Linux系统中“动态库”和“静态库”那点事儿
前言
在调试lua脚本的时候,报错。
我已经再lua脚本中更改了cpath
package.cpath = package.cpath .. ";/usr/local/lib/?.so"
而且在/usr/local/lib有liblualongnumber.so.0啊,怎么会找不到呢?
下文将介绍此问题的解决过程
理论部分
参考:
剖析可执行文件ELF组成
剖析.o文件ELF组成
目标文件
.symtab
编译
剖析gcc -v输出
静态库 VS 动态库
静态链接 VS 动态链接
Linux系统中“动态库”和“静态库”那点事儿
从源代码到进程
静态链接(Static Linking)
动态链接/动态加载
库
库从本质上来说是一种可执行代码的二进制格式,可以被载入内存中执行。库分静态库和动态库两种。
静态库
这类库的名字一般是libxxx.a,xxx为库的名字。利用静态函数库编译成的文件比较大,因为整个函数库的所有数据都会被整合进目标代码中,他的优点就显而易见了,即编译后的执行程序不需要外部的函数库支持,因为所有使用的函数都已经被编译进去了。当然这也会成为他的缺点,因为如果静态函数库改变了,那么你的程序必须重新编译。
动态库
这类库的名字一般是libxxx.M.N.so,同样的xxx为库的名字,M是库的主版本号,N是库的副版本号。当然也可以不要版本号,但名字必须有。相对于静态函数库,动态函数库在编译的时候并没有被编译进目标代码中,你的程序执行到相关函数时才调用该函数库里的相应函数,因此动态函数库所产生的可执行文件比较小。由于函数库没有被整合进你的程序,而是程序运行时动态的申请并调用,所以程序的运行环境中必须提供相应的库。动态函数库的改变并不影响你的程序,所以动态函数库的升级比较方便。linux系统有几个重要的目录存放相应的函数库,如/lib /usr/lib。
当要使用静态的程序库时,连接器会找出程序所需的函数,然后将它们拷贝到执行文件,由于这种拷贝是完整的,所以一旦连接成功,静态程序库也就不再需要了。然而,对动态库而言,就不是这样。动态库会在执行程序内留下一个标记指明当程序执行时,首先必须载入这个库。由于动态库节省空间,linux下进行连接的缺省操作是首先连接动态库,也就是说,如果同时存在静态和动态库,不特别指定的话,将与动态库相连接。
有时候当我们的应用程序无法运行时,它会提示我们说它找不到什么样的库,或者哪个库的版本又不对等等之类的问题。那么应用程序它是怎么知道需要哪些库的呢?ldd命令(下文有介绍)就可以查看动态文件(可执行文件,动态库)依赖了那些文件。
Linux系统中动态链接库的配置文件一般在/etc/ld.so.conf文件内。这个文件内容比较有意思
include /etc/ld.so.conf.d/*.conf
他指向/etc/ld.so.conf.d/目录下所有conf文件。这些文件有志向了具体目录,那些目录下面就是Linux系统要找的共享对象。
root@ubuntu:/etc/ld.so.conf.d# ls fakeroot-x86_64-linux-gnu.conf libc.conf x86_64-linux-gnu.conf root@ubuntu:/etc/ld.so.conf.d# cat * /usr/lib/x86_64-linux-gnu/libfakeroot # libc default configuration /usr/local/lib # Multiarch support /lib/x86_64-linux-gnu /usr/lib/x86_64-linux-gnu root@ubuntu:/etc/ld.so.conf.d# ls -m /lib/x86_64-linux-gnu device-mapper, ld-2.23.so, ld-linux-x86-64.so.2, libacl.so.1, libacl.so.1.1.0, libaio.so.1, libaio.so.1.0.1, libanl-2.23.so, libanl.so.1, libapparmor.so.1, libapparmor.so.1.4.0, libatm.so.1, libatm.so.1.0.0, libattr.so.1, libattr.so.1.1.0, libaudit.so.1, libaudit.so.1.0.0, libblkid.so.1, libblkid.so.1.1.0, libBrokenLocale-2.23.so, libBrokenLocale.so.1, libbsd.so.0, libbsd.so.0.8.2, libbz2.so.1, libbz2.so.1.0, libbz2.so.1.0.4, libc-2.23.so, libcap.so.2, libcap.so.2.24, libcidn-2.23.so, libcidn.so.1, libcom_err.so.2, libcom_err.so.2.1, libcrypt-2.23.so, libcrypto.so.1.0.0, libcryptsetup.so.4, libcryptsetup.so.4.6.0, libcrypt.so.1, libc.so.6, libdbus-1.so.3, libdbus-1.so.3.14.6, libdevmapper-event-lvm2mirror.so, libdevmapper-event-lvm2raid.so, libdevmapper-event-lvm2snapshot.so, libdevmapper-event-lvm2.so.2.02, libdevmapper-event-lvm2thin.so, libdevmapper-event.so.1.02.1, libdevmapper.so.1.02.1, libdl-2.23.so, libdl.so.2, libdns-export.so.162, libdns-export.so.162.1.3, libe2p.so.2, libe2p.so.2.3, libexpat.so.1, libexpat.so.1.6.0, libext2fs.so.2, libext2fs.so.2.4, libfdisk.so.1, libfdisk.so.1.1.0, libfuse.so.2, libfuse.so.2.9.4, libgcc_s.so.1, libgcrypt.so.20, libgcrypt.so.20.0.5, libglib-2.0.so.0, libglib-2.0.so.0.4800.2, libgpg-error.so.0, libgpg-error.so.0.17.0, libhistory.so.5, libhistory.so.5.2, libhistory.so.6, libhistory.so.6.3, libip4tc.so.0, libip4tc.so.0.1.0, libip6tc.so.0, libip6tc.so.0.1.0, libiptc.so.0, libiptc.so.0.0.0, libisc-export.so.160, libisc-export.so.160.0.0, libjson-c.so.2, libjson-c.so.2.0.0, libkeyutils.so.1, libkeyutils.so.1.5, libkmod.so.2, libkmod.so.2.3.0, liblvm2app.so.2.2, liblvm2cmd.so.2.02, liblzma.so.5, liblzma.so.5.0.0, liblzo2.so.2, liblzo2.so.2.0.0, libm-2.23.so, libmemusage.so, libmnl.so.0, libmnl.so.0.1.0, libmount.so.1, libmount.so.1.1.0, libm.so.6, libmvec-2.23.so, libmvec.so.1, libncurses.so.5, libncurses.so.5.9, libncursesw.so.5, libncursesw.so.5.9, libnewt.so.0.52, libnewt.so.0.52.18, libnih.so.1, libnih.so.1.0.0, libnl-3.so.200, libnl-3.so.200.22.0, libnl-genl-3.so.200, libnl-genl-3.so.200.22.0, libnsl-2.23.so, libnsl.so.1, libnss_compat-2.23.so, libnss_compat.so.2, libnss_dns-2.23.so, libnss_dns.so.2, libnss_files-2.23.so, libnss_files.so.2, libnss_hesiod-2.23.so, libnss_hesiod.so.2, libnss_nis-2.23.so, libnss_nisplus-2.23.so, libnss_nisplus.so.2, libnss_nis.so.2, libntfs-3g.so.861, libntfs-3g.so.861.0.0, libpamc.so.0, libpamc.so.0.82.1, libpam_misc.so.0, libpam_misc.so.0.82.0, libpam.so.0, libpam.so.0.83.1, libparted.so.2, libparted.so.2.0.1, libpci.so.3, libpci.so.3.3.1, libpcprofile.so, libpcre.so.3, libpcre.so.3.13.2, libply-boot-client.so.4, libply-boot-client.so.4.0.0, libply.so.4, libply.so.4.0.0, libply-splash-core.so.4, libply-splash-core.so.4.0.0, libply-splash-graphics.so.4, libply-splash-graphics.so.4.0.0, libpng12.so.0, libpng12.so.0.54.0, libpopt.so.0, libpopt.so.0.0.0, libprocps.so.4, libprocps.so.4.0.0, libpthread-2.23.so, libpthread.so.0, libreadline.so.5, libreadline.so.5.2, libreadline.so.6, libreadline.so.6.3, libresolv-2.23.so, libresolv.so.2, librt-2.23.so, librt.so.1, libseccomp.so.2, libseccomp.so.2.2.3, libSegFault.so, libselinux.so.1, libsepol.so.1, libslang.so.2, libslang.so.2.3.0, libsmartcols.so.1, libsmartcols.so.1.1.0, libssl.so.1.0.0, libss.so.2, libss.so.2.0, libsystemd.so.0, libsystemd.so.0.14.0, libthread_db-1.0.so, libthread_db.so.1, libtinfo.so.5, libtinfo.so.5.9, libudev.so.1, libudev.so.1.6.4, libulockmgr.so.1, libulockmgr.so.1.0.1, libusb-0.1.so.4, libusb-0.1.so.4.4.4, libusb-1.0.so.0, libusb-1.0.so.0.1.0, libutil-2.23.so, libutil.so.1, libuuid.so.1, libuuid.so.1.3.0, libwrap.so.0, libwrap.so.0.7.6, libxtables.so.11, libxtables.so.11.0.0, libz.so.1, libz.so.1.2.8, security
细心的你可能会发现,在/etc目录下还存在一个名叫ld.so.cache的文件。从名字来看,估计和动态链接库缓存有关。的确,为了使得动态链接库可以被系统使用,当我们修改了/etc/ld.so.conf或/etc/ld.so.conf.d/目录下的任何文件,或者往那些目录下拷贝了新的动态链接库文件时,都需要运行一个很重要的命令:ldconfig,该命令位于/sbin目录下,主要的用途就是负责搜索/lib和/usr/lib,以及配置文件/etc/ld.so.conf里所列的目录下搜索可用的动态链接库文件,然后创建出动态加载程序/lib/ld-linux.so.2所需要的链接和(默认)缓存文件/etc/ld.so.cache(此文件里保存着已经排好序的动态链接库名字列表)。
也就是说:当用户在某个目录下面创建或拷贝了一个动态链接库,若想使其被系统共享,可以执行一下"ldconfig目录名"这个命令。此命令的功能在于让ldconfig将指定目录下的动态链接库被系统共享起来,即:在缓存文件/etc/ld.so.cache中追加进指定目录下的共享库。请注意:如果此目录不在/lib,/usr/lib及/etc/ld.so.conf文件所列的目录里面,则再次单独运行ldconfig时,此目录下的动态链接库可能不被系统共享了。单独运行ldconfig时,它只会搜索/lib、/usr/lib以及在/etc/ld.so.conf文件里所列的目录,用它们来重建/etc/ld.so.cache。
因此,对于我们自己开发的共享库可以将其拷贝到/lib、/etc/lib目录里,或者修改/etc/ld.so.conf文件将我们自己的库路径添加到该文件中,再执行ldconfig命令。
动态库使用参考:Linux下动态库的制作与使用
ldd(List Dynamic Dependencies)
功能
列出可执行文件或共享库的依赖文件,这里的依赖指的是动态库
语法
ldd(选项)(参数)
选项
--version:打印指令版本号;
-v:Print all information, including, for example, symbol versioning information.
[root@localhost lib]# ldd -v /usr/bin/ls linux-vdso.so.1 => (0x00007ffc28747000) libselinux.so.1 => /lib64/libselinux.so.1 (0x00007fa1d1a37000) libcap.so.2 => /lib64/libcap.so.2 (0x00007fa1d1832000) libacl.so.1 => /lib64/libacl.so.1 (0x00007fa1d1629000) libc.so.6 => /lib64/libc.so.6 (0x00007fa1d125c000) libpcre.so.1 => /lib64/libpcre.so.1 (0x00007fa1d0ffa000) libdl.so.2 => /lib64/libdl.so.2 (0x00007fa1d0df6000) /lib64/ld-linux-x86-64.so.2 (0x00007fa1d1c5e000) libattr.so.1 => /lib64/libattr.so.1 (0x00007fa1d0bf1000) libpthread.so.0 => /lib64/libpthread.so.0 (0x00007fa1d09d5000) Version information: /usr/bin/ls: libacl.so.1 (ACL_1.0) => /lib64/libacl.so.1 libc.so.6 (GLIBC_2.14) => /lib64/libc.so.6 libc.so.6 (GLIBC_2.4) => /lib64/libc.so.6 libc.so.6 (GLIBC_2.17) => /lib64/libc.so.6 libc.so.6 (GLIBC_2.3.4) => /lib64/libc.so.6 libc.so.6 (GLIBC_2.2.5) => /lib64/libc.so.6 libc.so.6 (GLIBC_2.3) => /lib64/libc.so.6 /lib64/libselinux.so.1: libdl.so.2 (GLIBC_2.2.5) => /lib64/libdl.so.2 ld-linux-x86-64.so.2 (GLIBC_2.3) => /lib64/ld-linux-x86-64.so.2 libc.so.6 (GLIBC_2.14) => /lib64/libc.so.6 libc.so.6 (GLIBC_2.8) => /lib64/libc.so.6 libc.so.6 (GLIBC_2.4) => /lib64/libc.so.6 libc.so.6 (GLIBC_2.7) => /lib64/libc.so.6 libc.so.6 (GLIBC_2.3) => /lib64/libc.so.6 libc.so.6 (GLIBC_2.2.5) => /lib64/libc.so.6 libc.so.6 (GLIBC_2.3.4) => /lib64/libc.so.6 /lib64/libcap.so.2: libc.so.6 (GLIBC_2.3.4) => /lib64/libc.so.6 libc.so.6 (GLIBC_2.3) => /lib64/libc.so.6 libc.so.6 (GLIBC_2.8) => /lib64/libc.so.6 libc.so.6 (GLIBC_2.4) => /lib64/libc.so.6 libc.so.6 (GLIBC_2.2.5) => /lib64/libc.so.6 libattr.so.1 (ATTR_1.0) => /lib64/libattr.so.1 /lib64/libacl.so.1: libc.so.6 (GLIBC_2.14) => /lib64/libc.so.6 libc.so.6 (GLIBC_2.3.4) => /lib64/libc.so.6 libc.so.6 (GLIBC_2.4) => /lib64/libc.so.6 libc.so.6 (GLIBC_2.2.5) => /lib64/libc.so.6 libattr.so.1 (ATTR_1.0) => /lib64/libattr.so.1 /lib64/libc.so.6: ld-linux-x86-64.so.2 (GLIBC_2.3) => /lib64/ld-linux-x86-64.so.2 ld-linux-x86-64.so.2 (GLIBC_PRIVATE) => /lib64/ld-linux-x86-64.so.2 /lib64/libpcre.so.1: libpthread.so.0 (GLIBC_2.2.5) => /lib64/libpthread.so.0 libc.so.6 (GLIBC_2.14) => /lib64/libc.so.6 libc.so.6 (GLIBC_2.3.4) => /lib64/libc.so.6 libc.so.6 (GLIBC_2.4) => /lib64/libc.so.6 libc.so.6 (GLIBC_2.2.5) => /lib64/libc.so.6 libc.so.6 (GLIBC_2.3) => /lib64/libc.so.6 /lib64/libdl.so.2: ld-linux-x86-64.so.2 (GLIBC_PRIVATE) => /lib64/ld-linux-x86-64.so.2 libc.so.6 (GLIBC_PRIVATE) => /lib64/libc.so.6 libc.so.6 (GLIBC_2.2.5) => /lib64/libc.so.6 /lib64/libattr.so.1: libc.so.6 (GLIBC_2.4) => /lib64/libc.so.6 libc.so.6 (GLIBC_2.2.5) => /lib64/libc.so.6 /lib64/libpthread.so.0: ld-linux-x86-64.so.2 (GLIBC_2.2.5) => /lib64/ld-linux-x86-64.so.2 ld-linux-x86-64.so.2 (GLIBC_2.3) => /lib64/ld-linux-x86-64.so.2 ld-linux-x86-64.so.2 (GLIBC_PRIVATE) => /lib64/ld-linux-x86-64.so.2 libc.so.6 (GLIBC_2.14) => /lib64/libc.so.6 libc.so.6 (GLIBC_2.3.2) => /lib64/libc.so.6 libc.so.6 (GLIBC_PRIVATE) => /lib64/libc.so.6 libc.so.6 (GLIBC_2.2.5) => /lib64/libc.so.6
-u:Print unused direct dependencies. (Since glibc 2.3.4.)
[root@localhost lib]# ldd -u /usr/bin/ls Unused direct dependencies: /lib64/libselinux.so.1 /lib64/libcap.so.2 /lib64/libacl.so.1
-d:Perform relocations and report any missing objects (ELF only).
[root@localhost lib]# ldd -d /usr/bin/ls linux-vdso.so.1 => (0x00007ffd88755000) libselinux.so.1 => /lib64/libselinux.so.1 (0x00007f284ddcd000) libcap.so.2 => /lib64/libcap.so.2 (0x00007f284dbc8000) libacl.so.1 => /lib64/libacl.so.1 (0x00007f284d9bf000) libc.so.6 => /lib64/libc.so.6 (0x00007f284d5f2000) libpcre.so.1 => /lib64/libpcre.so.1 (0x00007f284d390000) libdl.so.2 => /lib64/libdl.so.2 (0x00007f284d18c000) /lib64/ld-linux-x86-64.so.2 (0x00007f284dff4000) libattr.so.1 => /lib64/libattr.so.1 (0x00007f284cf87000) libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f284cd6b000)
-r:Perform relocations for both data objects and functions, and report any missing objects or functions (ELF only).
[root@localhost lib]# ldd -r /usr/bin/ls linux-vdso.so.1 => (0x00007fff60bf9000) libselinux.so.1 => /lib64/libselinux.so.1 (0x00007fd8560df000) libcap.so.2 => /lib64/libcap.so.2 (0x00007fd855eda000) libacl.so.1 => /lib64/libacl.so.1 (0x00007fd855cd1000) libc.so.6 => /lib64/libc.so.6 (0x00007fd855904000) libpcre.so.1 => /lib64/libpcre.so.1 (0x00007fd8556a2000) libdl.so.2 => /lib64/libdl.so.2 (0x00007fd85549e000) /lib64/ld-linux-x86-64.so.2 (0x00007fd856306000) libattr.so.1 => /lib64/libattr.so.1 (0x00007fd855299000) libpthread.so.0 => /lib64/libpthread.so.0 (0x00007fd85507d000)
--help:显示帮助信息。
参数
文件:指定可执行程序或者文库。
使用
最简单粗暴的使用方式,不带任何参数
查看共享库的依赖文件
root@ubuntu:/usr/lib# file libevent_openssl.so libevent_openssl.so: symbolic link to libevent_openssl-2.1.so.6.0.2 root@ubuntu:/usr/lib# ldd libevent_openssl.so linux-vdso.so.1 => (0x00007fffeb79d000) libssl.so.1.0.0 => /lib/x86_64-linux-gnu/libssl.so.1.0.0 (0x00007f65cb531000) libcrypto.so.1.0.0 => /lib/x86_64-linux-gnu/libcrypto.so.1.0.0 (0x00007f65cb0ed000) libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f65caed0000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f65cab05000) libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f65ca901000) /lib64/ld-linux-x86-64.so.2 (0x000055a2680a3000) root@ubuntu:/usr/lib# ldd libevent_openssl-2.1.so.6.0.2 linux-vdso.so.1 => (0x00007ffe80873000) libssl.so.1.0.0 => /lib/x86_64-linux-gnu/libssl.so.1.0.0 (0x00007f9e350a1000) libcrypto.so.1.0.0 => /lib/x86_64-linux-gnu/libcrypto.so.1.0.0 (0x00007f9e34c5d000) libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f9e34a40000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f9e34675000) libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f9e34471000) /lib64/ld-linux-x86-64.so.2 (0x000055d3f064b000)
查看可执行文件的依赖文件
root@ubuntu:/DNXY/projects/dnxy_bike/src# file brks brks: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=9d70993a3c150c156164b1bd88927f27651987e5, not stripped root@ubuntu:/DNXY/projects/dnxy_bike/src# ldd brks linux-vdso.so.1 => (0x00007ffecf0d9000) liblog4cpp.so.5 => ../third/lib/log4cpp/liblog4cpp.so.5 (0x00007fc23c150000) libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007fc23bf29000) libjsoncpp.so => ../third/lib/log4cpp/libjsoncpp.so (0x00007fc23bcdf000) libcurl.so.4 => ../third/lib/log4cpp/libcurl.so.4 (0x00007fc23ba7f000) libthrift-0.11.0.so => /usr/local/lib/libthrift-0.11.0.so (0x00007fc23b7b8000) libthriftnb-0.11.0.so => /usr/local/lib/libthriftnb-0.11.0.so (0x00007fc23b597000) libevent-2.1.so.6 => ../third/lib/log4cpp/libevent-2.1.so.6 (0x00007fc23b341000) libmysqlclient.so.20 => /usr/lib/x86_64-linux-gnu/libmysqlclient.so.20 (0x00007fc23ad30000) libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007fc23a9ad000) libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007fc23a6a4000) libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007fc23a48e000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fc23a0c3000) /lib64/ld-linux-x86-64.so.2 (0x000055f814b36000) libz.so.1 => /lib/x86_64-linux-gnu/libz.so.1 (0x00007fc239ea9000) libssl.so.1.0.0 => /lib/x86_64-linux-gnu/libssl.so.1.0.0 (0x00007fc239c40000) libcrypto.so.1.0.0 => /lib/x86_64-linux-gnu/libcrypto.so.1.0.0 (0x00007fc2397fb000) libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007fc2395f7000)
不可以查看静态库的依赖文件
root@ubuntu:/usr/lib# ldd libcurl.a
not a dynamic executable
查看静态库的依赖文件(说依赖不太恰当,应该是包含)。参考:Linux命令——ar
root@ubuntu:/usr/lib# ar -t libcurl.a libcurl_la-file.o libcurl_la-timeval.o libcurl_la-base64.o libcurl_la-hostip.o libcurl_la-progress.o libcurl_la-formdata.o libcurl_la-cookie.o libcurl_la-http.o libcurl_la-sendf.o libcurl_la-ftp.o libcurl_la-url.o libcurl_la-dict.o libcurl_la-if2ip.o libcurl_la-speedcheck.o libcurl_la-ldap.o libcurl_la-version.o libcurl_la-getenv.o libcurl_la-escape.o libcurl_la-mprintf.o libcurl_la-telnet.o libcurl_la-netrc.o libcurl_la-getinfo.o libcurl_la-transfer.o libcurl_la-strcase.o libcurl_la-easy.o libcurl_la-security.o libcurl_la-curl_fnmatch.o libcurl_la-fileinfo.o libcurl_la-ftplistparser.o libcurl_la-wildcard.o libcurl_la-krb5.o libcurl_la-memdebug.o libcurl_la-http_chunks.o libcurl_la-strtok.o libcurl_la-connect.o libcurl_la-llist.o libcurl_la-hash.o libcurl_la-multi.o libcurl_la-content_encoding.o libcurl_la-share.o libcurl_la-http_digest.o libcurl_la-md4.o libcurl_la-md5.o libcurl_la-http_negotiate.o libcurl_la-inet_pton.o libcurl_la-strtoofft.o libcurl_la-strerror.o libcurl_la-amigaos.o libcurl_la-hostasyn.o libcurl_la-hostip4.o libcurl_la-hostip6.o libcurl_la-hostsyn.o libcurl_la-inet_ntop.o libcurl_la-parsedate.o libcurl_la-select.o libcurl_la-tftp.o libcurl_la-splay.o libcurl_la-strdup.o libcurl_la-socks.o libcurl_la-ssh.o libcurl_la-ssh-libssh.o libcurl_la-curl_addrinfo.o libcurl_la-socks_gssapi.o libcurl_la-socks_sspi.o libcurl_la-curl_sspi.o libcurl_la-slist.o libcurl_la-nonblock.o libcurl_la-curl_memrchr.o libcurl_la-imap.o libcurl_la-pop3.o libcurl_la-smtp.o libcurl_la-pingpong.o libcurl_la-rtsp.o libcurl_la-curl_threads.o libcurl_la-warnless.o libcurl_la-hmac.o libcurl_la-curl_rtmp.o libcurl_la-openldap.o libcurl_la-curl_gethostname.o libcurl_la-gopher.o libcurl_la-idn_win32.o libcurl_la-http_proxy.o libcurl_la-non-ascii.o libcurl_la-asyn-ares.o libcurl_la-asyn-thread.o libcurl_la-curl_gssapi.o libcurl_la-http_ntlm.o libcurl_la-curl_ntlm_wb.o libcurl_la-curl_ntlm_core.o libcurl_la-curl_sasl.o libcurl_la-rand.o libcurl_la-curl_multibyte.o libcurl_la-hostcheck.o libcurl_la-conncache.o libcurl_la-pipeline.o libcurl_la-dotdot.o libcurl_la-x509asn1.o libcurl_la-http2.o libcurl_la-smb.o libcurl_la-curl_endian.o libcurl_la-curl_des.o libcurl_la-system_win32.o libcurl_la-mime.o libcurl_la-sha256.o libcurl_la-setopt.o libcurl_la-curl_path.o libcurl_la-curl_ctype.o libcurl_la-curl_range.o libcurl_la-psl.o libcurl_la-vauth.o libcurl_la-cleartext.o libcurl_la-cram.o libcurl_la-digest.o libcurl_la-digest_sspi.o libcurl_la-krb5_gssapi.o libcurl_la-krb5_sspi.o libcurl_la-ntlm.o libcurl_la-ntlm_sspi.o libcurl_la-oauth2.o libcurl_la-spnego_gssapi.o libcurl_la-spnego_sspi.o libcurl_la-openssl.o libcurl_la-gtls.o libcurl_la-vtls.o libcurl_la-nss.o libcurl_la-polarssl.o libcurl_la-polarssl_threadlock.o libcurl_la-axtls.o libcurl_la-cyassl.o libcurl_la-schannel.o libcurl_la-schannel_verify.o libcurl_la-darwinssl.o libcurl_la-gskit.o libcurl_la-mbedtls.o
在 ldd 命令打印的结果中,“=>”左边的表示该程序需要连接的共享库之 so 名称,右边表示由 Linux 系统找到的对应共享库在文件系统中的具体位置。默认情况下,/etc/ld.so.conf 文件中包含有默认的共享库搜索路径。
一个更好的替代
ldd本质上是一个脚本,并不是一个ELF格式的可执行文件。file使用
root@ubuntu:/# file /usr/bin/ldd /usr/bin/ldd: Bourne-Again shell script, ASCII text executable
ldd能够显示可执行模块的dependency,其原理是通过设置一系列的环境变量,如下:LD_TRACE_LOADED_OBJECTS、LD_WARN、LD_BIND_NOW、LD_LIBRARY_VERSION、LD_VERBOSE
等。当LD_TRACE_LOADED_OBJECTS
环境变量不为空时,任何可执行程序在运行时,它都会只显示模块的dependency,而程序并不真正执行。要不你可以在shell终端测试一下,如下:
export LD_TRACE_LOADED_OBJECTS=1
root@ubuntu:/usr/lib# export LD_TRACE_LOADED_OBJECTS=1 root@ubuntu:/usr/lib# ls linux-vdso.so.1 => (0x00007ffcd59fe000) libselinux.so.1 => /lib/x86_64-linux-gnu/libselinux.so.1 (0x00007f775f7f6000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f775f42c000) libpcre.so.3 => /lib/x86_64-linux-gnu/libpcre.so.3 (0x00007f775f1bc000) libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f775efb8000) /lib64/ld-linux-x86-64.so.2 (0x00007f775fa18000) libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f775ed9b000) root@ubuntu:/usr/lib# unset LD_TRACE_LOADED_OBJECTS root@ubuntu:/usr/lib# ls accountsservice libevent_openssl.so libopen-pal.so apt libevent_pthreads-2.1.so.6 libopen-pal.so.13 binfmt.d libevent_pthreads-2.1.so.6.0.2 libopen-pal.so.13.0.2 byobu libevent_pthreads.a libopen-rte.so command-not-found libevent_pthreads.la libopen-rte.so.12 compat-ld libevent_pthreads.so libopen-rte.so.12.0.2 dbus-1.0 libevent.so liboshmem.so dpkg libguestlib.so.0 libtinyxml.a dracut libguestlib.so.0.0.0 libvmtools.so.0 eject libhgfs.so.0 libvmtools.so.0.0.0 emacsen-common libhgfs.so.0.0.0 linux-boot-probes gcc libibverbs.a locale git-core libibverbs.so lxcfs gnupg libibverbs.so.1 lxd gold-ld libibverbs.so.1.0.0 man-db groff libjsoncpp.a mime grub libjsoncpp.so modules-load.d grub-legacy libjson_linux-gcc-5.4.0_libmt.a mysql initcpio libjson_linux-gcc-5.4.0_libmt.so openmpi initramfs-tools liblog4cpp.a openssh insserv liblog4cpp.la open-vm-tools klibc liblog4cpp.so os-prober language-selector liblog4cpp.so.5 os-probes ldscripts liblog4cpp.so.5.0.6 os-release libcurl.a libmca_common_sm.so perl5 libcurl.la libmca_common_sm.so.4 pkgconfig libcurl.so libmca_common_sm.so.4.0.4 pkg-config.multiarch libcurl.so.4 libmca_common_verbs.so pm-utils libcurl.so.4.5.0 libmca_common_verbs.so.7 policykit-1 libDeployPkg.so.0 libmca_common_verbs.so.7.0.0 python2.7 libDeployPkg.so.0.0.0 libmpi_cxx.so python3 libevent-2.1.so.6 libmpi_cxx.so.1 python3.5 libevent-2.1.so.6.0.2 libmpi_cxx.so.1.1.3 rsyslog libevent.a libmpi_mpifh.so sasl2 libevent_core-2.1.so.6 libmpi_mpifh.so.12 scons libevent_core-2.1.so.6.0.2 libmpi_mpifh.so.12.0.0 sftp-server libevent_core.a libmpi.so snapd libevent_core.la libmpi++.so software-properties libevent_core.so libmpi.so.12 ssl libevent_extra-2.1.so.6 libmpi.so.12.0.2 sudo libevent_extra-2.1.so.6.0.2 libmpi_usempif08.so systemd libevent_extra.a libmpi_usempif08.so.11 tar libevent_extra.la libmpi_usempif08.so.11.1.0 tasksel libevent_extra.so libmpi_usempi_ignore_tkr.so tc libevent.la libmpi_usempi_ignore_tkr.so.6 tmpfiles.d libevent_openssl-2.1.so.6 libmpi_usempi_ignore_tkr.so.6.1.0 ubuntu-release-upgrader libevent_openssl-2.1.so.6.0.2 libompitrace.so update-notifier libevent_openssl.a libompitrace.so.0 valgrind libevent_openssl.la libompitrace.so.0.0.0 x86_64-linux-gnu
ldd显示可执行模块的dependency的工作原理,其实质是通过ld-linux.so(elf动态库的装载器)来实现的。我们知道,ld-linux.so模块会先于executable模块程序工作,并获得控制权,因此当上述的那些环境变量被设置时,ld-linux.so选择了显示可执行模块的dependency。
实际上可以直接执行ld-linux.so模块,如:/lib/ld-linux.so.2 --list program
(这相当于ldd program)
某些环境下,某些ldd版本可能会试图执行ldd参数中对应的程序。因此对于那些不受信任的可执行文件或动态库请不要使用ldd,因为有更好的替代方案
objdump -p /path/to/program | grep NEEDED
root@ubuntu:/DNXY/projects/dnxy_bike/src# objdump -p brks | grep NEEDED NEEDED liblog4cpp.so.5 NEEDED libpthread.so.0 NEEDED libjsoncpp.so NEEDED libcurl.so.4 NEEDED libthrift-0.11.0.so NEEDED libthriftnb-0.11.0.so NEEDED libevent-2.1.so.6 NEEDED libmysqlclient.so.20 NEEDED libstdc++.so.6 NEEDED libm.so.6 NEEDED libgcc_s.so.1 NEEDED libc.so.6 root@ubuntu:/DNXY/projects/dnxy_bike/src# ldd brks linux-vdso.so.1 => (0x00007fff36d70000) liblog4cpp.so.5 => ../third/lib/log4cpp/liblog4cpp.so.5 (0x00007ffb5bf49000) libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007ffb5bd22000) libjsoncpp.so => ../third/lib/log4cpp/libjsoncpp.so (0x00007ffb5bad8000) libcurl.so.4 => ../third/lib/log4cpp/libcurl.so.4 (0x00007ffb5b878000) libthrift-0.11.0.so => /usr/local/lib/libthrift-0.11.0.so (0x00007ffb5b5b1000) libthriftnb-0.11.0.so => /usr/local/lib/libthriftnb-0.11.0.so (0x00007ffb5b390000) libevent-2.1.so.6 => ../third/lib/log4cpp/libevent-2.1.so.6 (0x00007ffb5b13a000) libmysqlclient.so.20 => /usr/lib/x86_64-linux-gnu/libmysqlclient.so.20 (0x00007ffb5ab29000) libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007ffb5a7a6000) libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007ffb5a49d000) libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007ffb5a287000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007ffb59ebc000) /lib64/ld-linux-x86-64.so.2 (0x0000559ef684b000) libz.so.1 => /lib/x86_64-linux-gnu/libz.so.1 (0x00007ffb59ca2000) libssl.so.1.0.0 => /lib/x86_64-linux-gnu/libssl.so.1.0.0 (0x00007ffb59a39000) libcrypto.so.1.0.0 => /lib/x86_64-linux-gnu/libcrypto.so.1.0.0 (0x00007ffb595f4000) libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007ffb593f0000)
但是从输出结果看,objdump比ldd输出的依赖少了点。
ldconfig
功能
配置动态链接器运行时绑定(configure dynamic linker run-time bindings)
说明
对命令行指定的目录,文件/etc/ld.so.conf中指定的目录,/usr/lib和/lib等受新任的目录下面找到的最新共享库,ldconfig为他们创建必要的链接和缓存。链接和缓存给动态链接器ld.so使用。在确定哪些版本的链接应更新时,ldconfig会检查它遇到的库的头和文件名。 扫描库时,ldconfig忽略符号链接。
当任何库被连接的时候,ldconfig将尝试根据C库来推断ELF库的类型(eg:libc 5.x或libc 6.x(glibc))。因此在制作动态库时,明智的做法使用参数-lc显式连接到libc。 ldconfig能够将多个ABI类型的库存储单个缓存中,这些缓存允许本地运行多个ABI,如ia32 / ia64 / x86_64或sparc32 / sparc64。
现有的某些库没有包含足够的信息用于推导他们的类型,因此/etc/ld.so.conf文件格式允许指定期望的类型。但这仅限于对ELF类型,格式类似于“dirname = TYPE”,其中type可以是libc4,libc5或libc6。(此语法也适用于命令行)。不允许有空格。
包含=的目录名称不再合法,除非它们也具有预期的类型说明符。
通常情况下,ldconfig以root身份运行运行,因为它可能需要对某些目录和文件有root的写入权限。 如果使用-r选项更改根目录,只要您对该目录树有足够的权限,可以不必是root用户。
语法
ldconfig [OPTION...]
选项
-v --verbose
详细模式。在扫描动态库时,会显示动态库当前版本号,库在那个目录下,以及指向该库的链接。
root@ubuntu:~# ldconfig -v ... /usr/lib/x86_64-linux-gnu/libfakeroot: libfakeroot-0.so -> libfakeroot-tcp.so /usr/local/lib: libluasocket.so.0 -> libluasocket.so.0.0.0 libthrift-0.11.0.so -> libthrift.so libthriftz-0.11.0.so -> libthriftz.so libluabitwise.so.0 -> libluabitwise.so.0.0.0 libthrift_c_glib.so.0 -> libthrift_c_glib.so.0.0.0 liblua.so -> liblua.so libthriftnb-0.11.0.so -> libthriftnb.so libluabpack.so.0 -> libluabpack.so.0.0.0 liblualongnumber.so.0 -> liblualongnumber.so.0.0.0 ...
->前面是后面的符号链接
-n
ldconfig仅扫描命令行指定下的目录,不扫描受信目录(/lib,/usr/lib)和配置文件/etc/ld.so.conf指定的目录。
-N
ldconfig不重建缓存文件(/etc/ld.so.cache),但是还会跟新文件的链接。除非指定-X
-X
ldconfig不更新文件的连接,但是还会重建缓存文件(/etc/ld.so.cache)。除非指定-N
-f conf
使用conf文件,而不是默认的/etc/ld.so.conf
-C cache
Use cache instead of /etc/ld.so.cache.
-r root
此选项改变应用程序的根目录为ROOT(是调用chroot函数实现的).选择此项时,系统默认的配置文件/etc/ld.so.conf,实际对应的为ROOT/etc/ld.so.conf.如用-r/usr/zzz时,打开配置文件/etc/ld.so.conf时,实际打开的是/usr/zzz/etc/ld.so.conf文件.用此选项,可以大大增加动态链接库管理的灵活性.
-l
通常情况下,ldconfig搜索动态链接库时将自动建立动态链接库的连接.选择此项时,将进入专家模式,需要手工设置连接.一般用户不用此项.
-p 或 --print-cache
此选项指示ldconfig打印出当前缓存文件所保存的所有共享库的名字。
-c FORMAT 或--format=FORMAT
此选项用于指定缓存文件所使用的格式,共有三种:ld(老格式),new(新格式)和compat(兼容格式,此为默认格式)。
-V
此选项打印出ldconfig的版本信息,而后退出.
--help 或--usage
这三个选项作用相同,都是让ldconfig打印出其帮助信息,而后退出。
ldconfig需要注意的地方
- 往/lib和/usr/lib里面加东西,是不用修改/etc/ld.so.conf文件的,但是添加完后需要调用下ldconfig,不然添加的library会找不到。
- 如果添加的library不在/lib和/usr/lib里面的话,就一定要修改/etc/ld.so.conf文件,往该文件追加library所在的路径,然后也需要重新调用下ldconfig命令。比如在安装MySQL的时候,其库文件/usr/local/mysql/lib,就需要追加到/etc/ld.so.conf文件中。命令如下:
# echo "/usr/local/mysql/lib" >> /etc/ld.so.conf
# ldconfig -v | grep mysql
- 如果添加的library不在/lib或/usr/lib下,但是却没有权限操作写/etc/ld.so.conf文件的话,这时就需要往export里写一个全局变量LD_LIBRARY_PATH,就可以了。但是这种方法有个缺点,就是环境变量都是局部于进程的,也就是说你在一个shell终端export LD_LIBRARY_PATH,可以找到动态库。但是当shell终端关闭再打开的时候还是会报找不到动态库。这是和可以把export LD_LIBRARY_PATH写在.bashrc里面,这样只要一执行shell就能找到动态库。
- 除了export LD_LIBRARY_PATH方法外,把我们自己 写的动态库手动拷贝到/lib目录下也可以。/lib是系统动态链接库所在位置。一般不使用这种方法。
一个很好的中文在线man手册
Linux命令大全
最后说明最开始的那个问题是怎么解决的
其实就是执行了一遍ldconfig命令,命令虽简单,但是背后原理深刻,理解很重要。