Linux 动态库与静态库的使用

库是一种可执行代码的二进制格式,可以载入内存中执行,分为静态库和动态库两种。

静态库:一般是 libxxx.a, xxx 是库的名字。利用静态函数库编译成的文件比较大,因为整个函数库的所有数据都会被整合到目标代码中。优点是执行程序不需要外部的函数库支持。缺点是如果静态函数库改变了,那么程序需要重新编译。

动态库:一般是 libxxx.M.N.so,xxx 是库的名字,M 是库的主版本号,N 是库的副版本号 (版本号不是必须的)。动态库是在程序运行时动态申请并加载的,需要程序的运行环境中提供相应的库。

要使用静态的程序库时,连接器会找出程序所需的函数,然后将它们拷贝到执行文件,由于这种拷贝是完整的,所以一旦连接成功,静态程序库也就不再需要了。动态库会在执行程序内留下一个标记指明当程序执行时,首先必须载入这个库。

由于动态库节省空间,Linux 下进行连接的缺省操作是首先连接动态库。如果同时存在静态和动态库,不特别指定的话,将与动态库相连接。

-L 表示编译器需要链接的库文件的目录,-L. 表示编译器需要链接的库文件在当前目录。即使库文件在当前目录,编译器默认也不进行搜索,-L. 选项不能少。

-lstack 表示编译器要链接 libstack 库,-I 表示编译器需要的头文件的目录。

编译器默认搜索的目录可以用 -print-search-dirs 选项查看。编译器会在这些搜索路径以及 -L 选项指定的路径中查找用 -l 选项指定的库,例如 -lstack,编译器会首先查找动态库 libstack.so,如果有就链接它。如果没有动态库 libstack.so,就查找静态库 libstack.a,如果有就链接它。编译器优先考虑动态库,如果希望编译器只链接静态库,可以指定 -static 选项。

在 Linux 系统中,动态链接文件称为动态共享对象 (DSO,Dynamic Shared Objects),一般是以 .so 为扩展名。
在 Windows 系统中,动态链接文件称为动态链接库 (Dynamic Linking Library),一般是以 .dll 为扩展名。

1. 静态库

strong@foreverstrong:~/Desktop/makefile_work$ ls -l
total 20
-rw-rw-r-- 1 strong strong 196 Sep 30 11:20 function_qiang.c
-rw-rw-r-- 1 strong strong  31 Sep 30 11:20 function_qiang.h
-rw-rw-r-- 1 strong strong 197 Sep 30 11:20 function_yong.c
-rw-rw-r-- 1 strong strong  30 Sep 30 11:20 function_yong.h
-rw-rw-r-- 1 strong strong 179 Sep 30 11:20 main_function.c
strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ cat function_qiang.c
#include <stdio.h>    /* printf */

void print_function_qiang()
{
    double angle, value;

    angle = 30.0;
    value = 0.5;
    printf ("The sine of %f degrees is %f.\n", angle, value);
}

strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ cat function_qiang.h

void print_function_qiang();

strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ cat function_yong.c
#include <stdio.h>    /* printf */

void print_function_yong()
{
    double angle, value;

    angle = 60.0;
    value = 0.5;
    printf ("The cosine of %f degrees is %f.\n", angle, value);
}

strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ cat function_yong.h

void print_function_yong();

strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ cat main_function.c
#include <stdio.h>    /* printf */

#include "function_yong.h"
#include "function_qiang.h"

int main ()
{
    print_function_yong();
    print_function_qiang();

    return 0;
}

strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ gcc -c function_yong.c function_qiang.c
strong@foreverstrong:~/Desktop/makefile_work$ ls -l
total 28
-rw-rw-r-- 1 strong strong  196 Sep 30 11:20 function_qiang.c
-rw-rw-r-- 1 strong strong   31 Sep 30 11:20 function_qiang.h
-rw-rw-r-- 1 strong strong 1672 Sep 30 11:26 function_qiang.o
-rw-rw-r-- 1 strong strong  197 Sep 30 11:20 function_yong.c
-rw-rw-r-- 1 strong strong   30 Sep 30 11:20 function_yong.h
-rw-rw-r-- 1 strong strong 1680 Sep 30 11:26 function_yong.o
-rw-rw-r-- 1 strong strong  179 Sep 30 11:20 main_function.c
strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ ar -rsv libyongqiang.a function_yong.o function_qiang.o
ar: creating libyongqiang.a
a - function_yong.o
a - function_qiang.o
strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ ls -l
total 32
-rw-rw-r-- 1 strong strong  196 Sep 30 11:20 function_qiang.c
-rw-rw-r-- 1 strong strong   31 Sep 30 11:20 function_qiang.h
-rw-rw-r-- 1 strong strong 1672 Sep 30 11:26 function_qiang.o
-rw-rw-r-- 1 strong strong  197 Sep 30 11:20 function_yong.c
-rw-rw-r-- 1 strong strong   30 Sep 30 11:20 function_yong.h
-rw-rw-r-- 1 strong strong 1680 Sep 30 11:26 function_yong.o
-rw-rw-r-- 1 strong strong 3672 Sep 30 11:27 libyongqiang.a
-rw-rw-r-- 1 strong strong  179 Sep 30 11:20 main_function.c
strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ ls -l
total 32
-rw-rw-r-- 1 strong strong  196 Sep 30 11:20 function_qiang.c
-rw-rw-r-- 1 strong strong   31 Sep 30 11:20 function_qiang.h
-rw-rw-r-- 1 strong strong 1672 Sep 30 11:26 function_qiang.o
-rw-rw-r-- 1 strong strong  197 Sep 30 11:20 function_yong.c
-rw-rw-r-- 1 strong strong   30 Sep 30 11:20 function_yong.h
-rw-rw-r-- 1 strong strong 1680 Sep 30 11:26 function_yong.o
-rw-rw-r-- 1 strong strong 3672 Sep 30 11:27 libyongqiang.a
-rw-rw-r-- 1 strong strong  179 Sep 30 11:20 main_function.c
strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ gcc main_function.c -static -L./ -lyongqiang
strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ ls -l
total 924
-rwxrwxr-x 1 strong strong 912880 Sep 30 11:36 a.out
-rw-rw-r-- 1 strong strong    196 Sep 30 11:20 function_qiang.c
-rw-rw-r-- 1 strong strong     31 Sep 30 11:20 function_qiang.h
-rw-rw-r-- 1 strong strong   1672 Sep 30 11:26 function_qiang.o
-rw-rw-r-- 1 strong strong    197 Sep 30 11:20 function_yong.c
-rw-rw-r-- 1 strong strong     30 Sep 30 11:20 function_yong.h
-rw-rw-r-- 1 strong strong   1680 Sep 30 11:26 function_yong.o
-rw-rw-r-- 1 strong strong   3672 Sep 30 11:27 libyongqiang.a
-rw-rw-r-- 1 strong strong    179 Sep 30 11:20 main_function.c
strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ file a.out
a.out: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, for GNU/Linux 2.6.32, BuildID[sha1]=2b58211aeb23ebf98d23241bf700a917c74e95ac, not stripped
strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ ldd a.out
        not a dynamic executable
strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ ./a.out
The cosine of 60.000000 degrees is 0.500000.
The sine of 30.000000 degrees is 0.500000.
strong@foreverstrong:~/Desktop/makefile_work$

2. 动态库

编译链接命令:

$ gcc -fPIC -shared func.c -o libfunc.so

或者为

$ gcc -fPIC -c func.c -o func.o
$ gcc -shared func.o -o libfunc.so

-shared 表明产生共享库,而 -fPIC 表明使用地址无关代码。
-fPIC 选项作用于编译阶段,告诉编译器产生与位置无关代码 (Position-Independent Code)。

/usr/bin/ld: function_yong.o: relocation R_X86_64_32 against `.rodata' can not be used when making a shared object; recompile with -fPIC
function_yong.o: error adding symbols: Bad value
collect2: error: ld returned 1 exit status
strong@foreverstrong:~/Desktop/makefile_work$ ls -l
total 20
-rw-rw-r-- 1 strong strong 196 Sep 30 11:20 function_qiang.c
-rw-rw-r-- 1 strong strong  31 Sep 30 11:20 function_qiang.h
-rw-rw-r-- 1 strong strong 197 Sep 30 11:20 function_yong.c
-rw-rw-r-- 1 strong strong  30 Sep 30 11:20 function_yong.h
-rw-rw-r-- 1 strong strong 179 Sep 30 11:20 main_function.c
strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ gcc -c function_yong.c function_qiang.c
strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ ls -l
total 28
-rw-rw-r-- 1 strong strong  196 Sep 30 11:20 function_qiang.c
-rw-rw-r-- 1 strong strong   31 Sep 30 11:20 function_qiang.h
-rw-rw-r-- 1 strong strong 1672 Sep 30 11:40 function_qiang.o
-rw-rw-r-- 1 strong strong  197 Sep 30 11:20 function_yong.c
-rw-rw-r-- 1 strong strong   30 Sep 30 11:20 function_yong.h
-rw-rw-r-- 1 strong strong 1680 Sep 30 11:40 function_yong.o
-rw-rw-r-- 1 strong strong  179 Sep 30 11:20 main_function.c
strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ gcc -shared -fPIC -o libyongqiang.so function_yong.o function_qiang.o
/usr/bin/ld: function_yong.o: relocation R_X86_64_32 against `.rodata' can not be used when making a shared object; recompile with -fPIC
function_yong.o: error adding symbols: Bad value
collect2: error: ld returned 1 exit status
strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ gcc -shared -fPIC function_yong.o function_qiang.o -o libyongqiang.so
/usr/bin/ld: function_yong.o: relocation R_X86_64_32 against `.rodata' can not be used when making a shared object; recompile with -fPIC
function_yong.o: error adding symbols: Bad value
collect2: error: ld returned 1 exit status
strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ gcc -fPIC -shared function_yong.o function_qiang.o -o libyongqiang.so
/usr/bin/ld: function_yong.o: relocation R_X86_64_32 against `.rodata' can not be used when making a shared object; recompile with -fPIC
function_yong.o: error adding symbols: Bad value
collect2: error: ld returned 1 exit status
strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~$ cd Desktop/makefile_work/
strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ ls
function_qiang.c  function_qiang.h  function_yong.c  function_yong.h  main_function.c
strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ gcc -fPIC -c function_yong.c function_qiang.c
strong@foreverstrong:~/Desktop/makefile_work$ ls -l
total 28
-rw-rw-r-- 1 strong strong  196 Sep 30 11:20 function_qiang.c
-rw-rw-r-- 1 strong strong   31 Sep 30 11:20 function_qiang.h
-rw-rw-r-- 1 strong strong 1720 Oct  7 10:39 function_qiang.o
-rw-rw-r-- 1 strong strong  197 Sep 30 11:20 function_yong.c
-rw-rw-r-- 1 strong strong   30 Sep 30 11:20 function_yong.h
-rw-rw-r-- 1 strong strong 1728 Oct  7 10:39 function_yong.o
-rw-rw-r-- 1 strong strong  179 Sep 30 11:20 main_function.c
strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ gcc -shared function_yong.o function_qiang.o -o libyongqiang.so
strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ ls -l
total 40
-rw-rw-r-- 1 strong strong  196 Sep 30 11:20 function_qiang.c
-rw-rw-r-- 1 strong strong   31 Sep 30 11:20 function_qiang.h
-rw-rw-r-- 1 strong strong 1720 Oct  7 10:39 function_qiang.o
-rw-rw-r-- 1 strong strong  197 Sep 30 11:20 function_yong.c
-rw-rw-r-- 1 strong strong   30 Sep 30 11:20 function_yong.h
-rw-rw-r-- 1 strong strong 1728 Oct  7 10:39 function_yong.o
-rwxrwxr-x 1 strong strong 8224 Oct  7 10:41 libyongqiang.so
-rw-rw-r-- 1 strong strong  179 Sep 30 11:20 main_function.c
strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ gcc main_function.c -lyongqiang
/usr/bin/ld: cannot find -lyongqiang
collect2: error: ld returned 1 exit status
strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ gcc main_function.c -L./ -lyongqiang
strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ ls -l
total 52
-rwxrwxr-x 1 strong strong 8664 Oct  7 10:45 a.out
-rw-rw-r-- 1 strong strong  196 Sep 30 11:20 function_qiang.c
-rw-rw-r-- 1 strong strong   31 Sep 30 11:20 function_qiang.h
-rw-rw-r-- 1 strong strong 1720 Oct  7 10:39 function_qiang.o
-rw-rw-r-- 1 strong strong  197 Sep 30 11:20 function_yong.c
-rw-rw-r-- 1 strong strong   30 Sep 30 11:20 function_yong.h
-rw-rw-r-- 1 strong strong 1728 Oct  7 10:39 function_yong.o
-rwxrwxr-x 1 strong strong 8224 Oct  7 10:41 libyongqiang.so
-rw-rw-r-- 1 strong strong  179 Sep 30 11:20 main_function.c
strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ ./a.out
The cosine of 60.000000 degrees is 0.500000.
The sine of 30.000000 degrees is 0.500000.
strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ file libyongqiang.so
libyongqiang.so: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, BuildID[sha1]=970bcd242e27c721a72ed0c336f60d71079fc0a9, not stripped
strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ ldd libyongqiang.so
        linux-vdso.so.1 =>  (0x00007fff44b89000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fc0d2c32000)
        /lib64/ld-linux-x86-64.so.2 (0x00007fc0d31fe000)
strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ file a.out
a.out: 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]=abd77e91ebcb83c9faac39c0add3e20bea5fb7a1, not stripped
strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ ldd a.out
        linux-vdso.so.1 =>  (0x00007fff6a571000)
        libyongqiang.so (0x00007ff2c47ad000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007ff2c43e3000)
        /lib64/ld-linux-x86-64.so.2 (0x00007ff2c49af000)
strong@foreverstrong:~/Desktop/makefile_work$

共享对象可能会被不同的进程加载到不同的位置上。如果共享对象中的指令使用了绝对地址、外部模块地址,那么在共享对象被加载时就必须根据相关模块的加载位置对这个地址做调整,也就是修改这些地址,让它在对应进程中能正确访问,而被修改的段就不能实现多进程共享一份物理内存,它们在每个进程中都必须有一份物理内存的拷贝。-fPIC 指令就是为了让使用到同一个共享对象的多个进程能尽可能多的共享物理内存,它把那些涉及到绝对地址、外部模块地址访问的地方都抽离出来,保证代码段的内容可以多进程相同,实现共享。

抽离出这部分特殊的指令、地址之后,放到了一个叫做 GOT (Global Offset Table) 的地方,它放在数据段中,每一个进程都有独立的一份,里面的内容可能是变量的地址、函数的地址,不同进程的内容很可能是不同的,这部分就是被隔离开的地址相关内容。模块被加载的时候,会把 GOT 表的内容填充好 (在没有延迟绑定的情况下)。

对于模块中全局变量的访问,为了解决可执行文件跟模块可能拥有同一个全局变量的问题 (此时,模块内的全局变量会被覆盖为可执行文件中的全局变量),对模块中的全局变量访问也通过 GOT 间接访问。

每一次访问全局变量、外部函数都需要去计算在 GOT 中的位置,然后再通过对应项的值访问变量、调用函数。从运行性能上来说,比装载时重定位要差点。装载时重定位就是不使用 -fPIC 参数,代码段需要一个重定位表,在装载时修正所有特殊地址,以后运行时不需要再有 GOT 位置计算和间接访问。

如果在装载时就去计算 GOT 的内容,那么会影响加载速度,于是就有了延迟绑定 (Lazy Binding),直到用时才去填充 GOT。它使用到了 PLT (Procedure Linkage Table):每一项都是一小段代码,对应于本运行模块要引用的函数。函数调用时,先到这里,然后再到 GOT。在函数第一次被调用时,进入 PLT 跳到加载器,加载器计算函数的真正地址,然后将地址写入 GOT 对应项,以后的调用就直接从 PLT 跳到 GOT 记录的函数位置。这样也减少了运行时多次调用多次计算 GOT 位置。

strong@foreverstrong:~/Desktop/makefile_work$ ls -l
total 52
-rwxrwxr-x 1 strong strong 8664 Oct  7 10:45 a.out
-rw-rw-r-- 1 strong strong  196 Sep 30 11:20 function_qiang.c
-rw-rw-r-- 1 strong strong   31 Sep 30 11:20 function_qiang.h
-rw-rw-r-- 1 strong strong 1720 Oct  7 10:39 function_qiang.o
-rw-rw-r-- 1 strong strong  197 Sep 30 11:20 function_yong.c
-rw-rw-r-- 1 strong strong   30 Sep 30 11:20 function_yong.h
-rw-rw-r-- 1 strong strong 1728 Oct  7 10:39 function_yong.o
-rwxrwxr-x 1 strong strong 8224 Oct  7 10:41 libyongqiang.so
-rw-rw-r-- 1 strong strong  179 Sep 30 11:20 main_function.c
strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ readelf -d libyongqiang.so | grep TEXTREL
strong@foreverstrong:~/Desktop/makefile_work$
readelf -d libyongqiang.so | grep TEXTREL

如果上边的 shell 有任何输出,则说明这 libyongqiang.so 不是 PIC。TEXTREL 表示代码段重定位表地址,PIC 的共享对象不会包含任何代码段重定位表。

PIC 的共享对象也会有重定位表,数据段中的 GOT、数据的绝对地址引用,这些都是需要重定位的。

strong@foreverstrong:~/Desktop/makefile_work$ ls -l
total 52
-rwxrwxr-x 1 strong strong 8664 Oct  7 10:45 a.out
-rw-rw-r-- 1 strong strong  196 Sep 30 11:20 function_qiang.c
-rw-rw-r-- 1 strong strong   31 Sep 30 11:20 function_qiang.h
-rw-rw-r-- 1 strong strong 1720 Oct  7 10:39 function_qiang.o
-rw-rw-r-- 1 strong strong  197 Sep 30 11:20 function_yong.c
-rw-rw-r-- 1 strong strong   30 Sep 30 11:20 function_yong.h
-rw-rw-r-- 1 strong strong 1728 Oct  7 10:39 function_yong.o
-rwxrwxr-x 1 strong strong 8224 Oct  7 10:41 libyongqiang.so
-rw-rw-r-- 1 strong strong  179 Sep 30 11:20 main_function.c
strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ readelf -d libyongqiang.so | grep TEXTREL
strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ readelf -r libyongqiang.so

Relocation section '.rela.dyn' at offset 0x4b0 contains 8 entries:
  Offset          Info           Type           Sym. Value    Sym. Name + Addend
000000200e00  000000000008 R_X86_64_RELATIVE                    6b0
000000200e08  000000000008 R_X86_64_RELATIVE                    670
000000201020  000000000008 R_X86_64_RELATIVE                    201020
000000200fd8  000200000006 R_X86_64_GLOB_DAT 0000000000000000 _ITM_deregisterTMClone + 0
000000200fe0  000400000006 R_X86_64_GLOB_DAT 0000000000000000 __gmon_start__ + 0
000000200fe8  000500000006 R_X86_64_GLOB_DAT 0000000000000000 _Jv_RegisterClasses + 0
000000200ff0  000600000006 R_X86_64_GLOB_DAT 0000000000000000 _ITM_registerTMCloneTa + 0
000000200ff8  000700000006 R_X86_64_GLOB_DAT 0000000000000000 __cxa_finalize@GLIBC_2.2.5 + 0

Relocation section '.rela.plt' at offset 0x570 contains 1 entries:
  Offset          Info           Type           Sym. Value    Sym. Name + Addend
000000201018  000300000007 R_X86_64_JUMP_SLO 0000000000000000 printf@GLIBC_2.2.5 + 0
strong@foreverstrong:~/Desktop/makefile_work$

共享对象的重定位表,.rel.dyn 是对数据引用的修正,.rel.plt 是对函数引用的修正。