Linux的动态库与静态库

1.动态库与静态库简介

    在实际的软件开发中,为了方便使用一些被重复调用的公共代码,我们经常将这些公共的函数编译成动态库或静态库。我们知道程序一般要经过预处理、编译、汇编和链接这几个步骤才能变成可执行的文件,程序的静态库是在做链接的步骤时,通过连接器将静态库的代码copy到可执行文件中,目标文件包含所有的静态库代码。因此目标文件在执行的时候不需要静态库的支持,但是在当静态库改变时则需要重启编译可执行文件。在Linux中,一般静态库是以 XXX.a命名。而动态库不会被编译到目标代码中,而是在执行到库的相关函数时才会去调用。因此当动态库函数改变时,只要保证动态库所存放的路径不变,我们将新的动态库覆盖旧的动态库就OK了, 在Linux中,动态库一般以XXX.so命名。

2.动态库与静态库的实现

    2.1 静态库在可以通过gcc编译源代码文件而得到,在Linux中我们可以通过file xxx.a查看其文件属性,用ar -t xxx.a 查看这个静态库包含的目标,如下所示:

1 /lib64> file libapparmor.a
2 libapparmor.a: current ar archive
3 
4 /lib64> ar -t libapparmor.a
5 grammar.o
6 libaalogparse.o
7 kernel_interface.o
8 scanner.o

    2.2 静态库目前在Linux中很少见了,更多用的是动态库, 动态库可以通过 “gcc -shared -fPIC XXX.so xxx.o”编译而成。

     -shared该选项指定生成动态连接库(让连接器生成T类型的导出符号表,有时候也生成弱连接W类型的导出符号),不用该标志外部程序无法连接。相当于一个可执行文件。

     -fPIC:表示编译为位置独立的代码,不用此选项的话编译后的代码是位置相关的所以动态载入时是通过代码拷贝的方式来满足不同进程的需要,而不能达到真正代码段共享的目的。

    我们可以用两个方式来查看该目标文件所引用的动态库(ldd & readelf):

    ELF 文件包含了共享库文件信息,我们可以注意到ldd列出来的比readelf列出来的库文件多,这是因为ldd列出来的是实际运行加载的库文件, 而readelf列出来的是编译时链接的库文件。

 1 ldd /usr/bin/ls                        
 2         linux-vdso.so.1 (0x00007ffe655c0000)
 3         libselinux.so.1 => /lib64/libselinux.so.1 (0x00007fdeeee47000)
 4         libcap.so.2 => /lib64/libcap.so.2 (0x00007fdeeec42000)
 5         libc.so.6 => /lib64/libc.so.6 (0x00007fdeee89e000)
 6         libpcre.so.1 => /usr/lib64/libpcre.so.1 (0x00007fdeee62f000)
 7         libdl.so.2 => /lib64/libdl.so.2 (0x00007fdeee42b000)
 8         /lib64/ld-linux-x86-64.so.2 (0x0000556694a1a000)
 9         libpthread.so.0 => /lib64/libpthread.so.0 (0x00007fdeee20d000)
10 
11 
12 readelf -d /usr/bin/ls
13 
14 Dynamic section at offset 0x1cd98 contains 27 entries:
15   Tag        Type                         Name/Value
16  0x0000000000000001 (NEEDED)             Shared library: [libselinux.so.1]
17  0x0000000000000001 (NEEDED)             Shared library: [libcap.so.2]
18  0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]
19  0x000000000000000c (INIT)               0x402770
20  0x000000000000000d (FINI)               0x4141ac
21  0x0000000000000019 (INIT_ARRAY)         0x61c368
22  0x000000000000001b (INIT_ARRAYSZ)       8 (bytes)
23  0x000000000000001a (FINI_ARRAY)         0x61c370
24  0x000000000000001c (FINI_ARRAYSZ)       8 (bytes)
25  0x0000000000000004 (HASH)               0x400298
26  0x000000006ffffef5 (GNU_HASH)           0x4006e0
27  0x0000000000000005 (STRTAB)             0x401468
28  0x0000000000000006 (SYMTAB)             0x400748
29  0x000000000000000a (STRSZ)              1546 (bytes)
30  0x000000000000000b (SYMENT)             24 (bytes)
31  0x0000000000000015 (DEBUG)              0x0
32  0x0000000000000003 (PLTGOT)             0x61d000
33  0x0000000000000002 (PLTRELSZ)           2688 (bytes)
34  0x0000000000000014 (PLTREL)             RELA
35  0x0000000000000017 (JMPREL)             0x401cf0
36  0x0000000000000007 (RELA)               0x401c00
37  0x0000000000000008 (RELASZ)             240 (bytes)
38  0x0000000000000009 (RELAENT)            24 (bytes)
39  0x000000006ffffffe (VERNEED)            0x401b90
40  0x000000006fffffff (VERNEEDNUM)         1
41  0x000000006ffffff0 (VERSYM)             0x401a72
42  0x0000000000000000 (NULL)               0x0

动态库的加载有两个,一种是按照库的路径搜索并加载,加载的优先级如下:

  1、RUNPATH 优先级最高    
  2、RPATH   其次               编译时指定
  3、LD_LIBRARY_PATH    环境变量指定
  4、/etc/ld.so.cache            可以通过ldconfig修改
  5、/usr/lib/ /lib/                  

另一种加载方式是依赖于Linux提供的API 来完成, API提供的函数声明,包含头文件:dlfcn.h.

关于动态库和静态库就说这么多了,水平有限,错误请指正,谢谢!

posted on 2018-03-26 21:32  Liquan2005  阅读(315)  评论(0编辑  收藏  举报