linux下的库文件分成静态库和动态库。库文件的产生也是有道理的。比如某些函数我经常用到,最好的办法就是放入库文件中。比如我是做科学运算的,计算最大公约数,最小公倍数,阶乘能运算经常遇到,但是每次做一个项目,我就写一份函数,那太麻烦了。那我们就可以把这些公用的,经常需要调用的函数,封装成库,供不同的项目使用。
下面是一个计算阶乘的函数,文件名为factorial.c
int factorial( int n)
{
if(n <= 1)
{
return 1;
}
else
{
return n*factorial(n-1);
}
}第二个文件是cmp.c,比较两个int型的大小。
int cmp(int a,int b)
{
return a-b;
}我们通过gcc生成对应的obj文件。
root@libin:~/program/C/testlib/lib# ll
总用量 16
drwxr-xr-x 2 root root 4096 2012-07-27 20:08 ./
drwxr-xr-x 5 root root 4096 2012-07-27 19:05 ../
-rw-r--r-- 1 root root 41 2012-07-26 22:50 cmp.c
-rw-r--r-- 1 root root 115 2012-07-27 20:07 factorial.c
root@libin:~/program/C/testlib/lib# gcc -c *.c
root@libin:~/program/C/testlib/lib# ll
总用量 24
drwxr-xr-x 2 root root 4096 2012-07-27 20:08 ./
drwxr-xr-x 5 root root 4096 2012-07-27 19:05 ../
-rw-r--r-- 1 root root 41 2012-07-26 22:50 cmp.c
-rw-r--r-- 1 root root 679 2012-07-27 20:08 cmp.o
-rw-r--r-- 1 root root 115 2012-07-27 20:07 factorial.c
-rw-r--r-- 1 root root 764 2012-07-27 20:08 factorial.o看到了我们生成了cmp.o和factoria.o文件。现在我们用ar命令将两个obj文件打包。
root@libin:~/program/C/testlib/lib# ar rs libmymath.a *.o
ar: creating libmymath.a
root@libin:~/program/C/testlib/lib# ll
总用量 28
drwxr-xr-x 2 root root 4096 2012-07-27 20:19 ./
drwxr-xr-x 5 root root 4096 2012-07-27 19:05 ../
-rw-r--r-- 1 root root 41 2012-07-26 22:50 cmp.c
-rw-r--r-- 1 root root 679 2012-07-27 20:08 cmp.o
-rw-r--r-- 1 root root 115 2012-07-27 20:07 factorial.c
-rw-r--r-- 1 root root 764 2012-07-27 20:08 factorial.o
-rw-r--r-- 1 root root 1658 2012-07-27 20:19 libmymath.a已经生成了静态库。如果我们拿到了一个静态库,想知道它是哪些obj文件打包而成,可以使用如下命令:
root@libin:~/program/C/testlib/lib# ar tv libmymath.a
rw-r--r-- 0/0 679 Jul 27 20:08 2012 cmp.o
rw-r--r-- 0/0 764 Jul 27 20:08 2012 factorial.o
用户如果libpthread.a的是有那些库打包生成的可以使用同样的命令观看。太多了,我就不列了。
ar是打包的命令,既然打包,必然也有对应的解包:
root@libin:~/program/C/testlib/lib# rm *.o
root@libin:~/program/C/testlib/lib# ll
总用量 20
drwxr-xr-x 2 root root 4096 2012-07-27 20:24 ./
drwxr-xr-x 5 root root 4096 2012-07-27 19:05 ../
-rw-r--r-- 1 root root 41 2012-07-26 22:50 cmp.c
-rw-r--r-- 1 root root 115 2012-07-27 20:07 factorial.c
-rw-r--r-- 1 root root 1658 2012-07-27 20:19 libmymath.a我们看到,已经将所有的obj文件删除。 现在从静态库中将obj文件解出来
root@libin:~/program/C/testlib/lib# ar x libmymath.a
root@libin:~/program/C/testlib/lib# ll
总用量 28
drwxr-xr-x 2 root root 4096 2012-07-27 20:42 ./
drwxr-xr-x 5 root root 4096 2012-07-27 19:05 ../
-rw-r--r-- 1 root root 41 2012-07-26 22:50 cmp.c
-rw-r--r-- 1 root root 679 2012-07-27 20:42 cmp.o
-rw-r--r-- 1 root root 115 2012-07-27 20:07 factorial.c
-rw-r--r-- 1 root root 764 2012-07-27 20:42 factorial.o
-rw-r--r-- 1 root root 1658 2012-07-27 20:19 libmymath.a如果要保留obj文件的原始属性,比如时间那么需要o选项 ,即:
root@libin:~/program/C/testlib/lib# ar xo libmymath.a
root@libin:~/program/C/testlib/lib# ll
总用量 28
drwxr-xr-x 2 root root 4096 2012-07-27 20:42 ./
drwxr-xr-x 5 root root 4096 2012-07-27 19:05 ../
-rw-r--r-- 1 root root 41 2012-07-26 22:50 cmp.c
-rw-r--r-- 1 root root 679 2012-07-27 20:08 cmp.o
-rw-r--r-- 1 root root 115 2012-07-27 20:07 factorial.c
-rw-r--r-- 1 root root 764 2012-07-27 20:08 factorial.o
-rw-r--r-- 1 root root 1658 2012-07-27 20:19 libmymath.aOK,我们已经得到了静态库,那么开始调用静态库,来做一些事情。
#include
#include
int main(int argc ,char* argv[])
{
int a = 4;
int b = 5;
int f_a = factorial(a);
int f_b = factorial(b);
while(1)
{
if(cmp(f_a,f_b))
{
printf("f_a is bigger than f_b\n");
}
else
{
printf("f_a is not bigger than f_b\n");
}
sleep(100);
}
return 0;
}下面我们看下不使用静态库能否编译通过。
root@libin:~/program/C/testlib/use# gcc -o test test.c
/tmp/ccZM4LMy.o: In function `main':
test.c:(.text+0x21): undefined reference to `factorial'
test.c:(.text+0x31): undefined reference to `factorial'
test.c:(.text+0x49): undefined reference to `cmp'
collect2: ld returned 1 exit status链接的时候报错了,原因是找不到factorial和cmp两个函数的定义。下面我们使用静态库
root@libin:~/program/C/testlib/use# gcc -o test test.c -L ../lib/ -lmymath
root@libin:~/program/C/testlib/use# ll
总用量 20
drwxr-xr-x 2 root root 4096 2012-07-27 21:01 ./
drwxr-xr-x 5 root root 4096 2012-07-27 19:05 ../
-rwxr-xr-x 1 root root 7307 2012-07-27 21:01 test*
-rw-r--r-- 1 root root 405 2012-07-26 23:12 test.c-L选项是告诉gcc,去什么目录下找库文件,默认查找路径是
/lib/
/usr/lib
/usr/local/lib
对这个问题感兴趣的可以去看俞甲子的程序员的自我修养第八章的内容。如果我们不使用-L就会出现:
root@libin:~/program/C/testlib/use# gcc -o test test.c -lmymath
/usr/bin/ld: cannot find -lmymath
collect2: ld returned 1 exit status
root@libin:~/program/C/testlib/use#链接器找不到这个库文件。
当然,链接器并不知道它找的是libmymath.a,它会优先找动态库libmymath.so,找不到了,才会寻找libmymath.a。不管怎么说,我们使用静态库生成了可执行文件.
不用静态库,直接使用obj文件,一样也是可以生成最终的可执行文件。
root@libin:~/program/C/testlib/use# rm test
root@libin:~/program/C/testlib/use# ll
总用量 12
drwxr-xr-x 2 root root 4096 2012-07-27 21:29 ./
drwxr-xr-x 5 root root 4096 2012-07-27 19:05 ../
-rw-r--r-- 1 root root 405 2012-07-26 23:12 test.c
root@libin:~/program/C/testlib/use# gcc -o test test.c ../lib/cmp.o ../lib/factorial.o
root@libin:~/program/C/testlib/use# ll
总用量 20
drwxr-xr-x 2 root root 4096 2012-07-27 21:29 ./
drwxr-xr-x 5 root root 4096 2012-07-27 19:05 ../
-rwxr-xr-x 1 root root 7307 2012-07-27 21:29 test*
-rw-r--r-- 1 root root 405 2012-07-26 23:12 test.c 注意,对于静态库生成的可执行文件,里面包含了静态库打包的obj文件对应汇编代码换句话说,就是你删除了静态库,生成的test一样可以顺利的执行。因为,factorial.c和cmp.c定义的函数,在test可执行文件里面都有汇编代码。这也是使用静态库被人诟病的地方。如果静态库非常大,那么生成的可执行文件也会比较大。
我们证明下test中一定会有cmp函数和factorial函数的汇编指令。
root@libin:~/program/C/testlib/use# gcc -o test test.c -L ../lib/ -lmymath
root@libin:~/program/C/testlib/use# ll
总用量 20
drwxr-xr-x 2 root root 4096 2012-07-27 21:36 ./
drwxr-xr-x 5 root root 4096 2012-07-27 19:05 ../
-rwxr-xr-x 1 root root 7307 2012-07-27 21:36 test*
-rw-r--r-- 1 root root 405 2012-07-26 23:12 test.c
root@libin:~/program/C/testlib/use# objdump -S test
。。。
08048490 :
8048490:55 push %ebp
8048491:89 e5 mov %esp,%ebp
8048493:8b 45 0c mov 0xc(%ebp),%eax
8048496:8b 55 08 mov 0x8(%ebp),%edx
8048499:89 d1 mov %edx,%ecx
804849b:29 c1 sub %eax,%ecx
804849d:89 c8 mov %ecx,%eax
804849f:5d pop %ebp
80484a0:c3 ret
80484a1:90 nop
80484a2:90 nop
80484a3:90 nop
080484a4 :
80484a4:55 push %ebp
80484a5:89 e5 mov %esp,%ebp
80484a7:83 ec 18 sub $0x18,%esp
80484aa:83 7d 08 01 cmpl $0x1,0x8(%ebp)
80484ae:7f 07 jg 80484b7
80484b0:b8 01 00 00 00 mov $0x1,%eax
80484b5:eb 12 jmp 80484c9
80484b7:8b 45 08 mov 0x8(%ebp),%eax
80484ba:83 e8 01 sub $0x1,%eax
80484bd:89 04 24 mov %eax,(%esp)
80484c0:e8 df ff ff ff call 80484a4
80484c5:0f af 45 08 imul 0x8(%ebp),%eax
80484c9:c9 leave
80484ca:c3 ret
80484cb:90 nop
80484cc:90 nop
80484cd:90 nop
80484ce:90 nop
80484cf:90 nop删除了libmymath.a,一样可以正常的跑
root@libin:~/program/C/testlib/use# rm ../lib/libmymath.a
root@libin:~/program/C/testlib/use# ./test
f_a is bigger than f_b----------------------------------------------------------------------------------------------------------------------
OK,我们可以考虑下动态库了。动态库出现的要比静态库晚,是为了解决每个使用到静态库的程序,都会有的一份库拷贝,我们上面也看到了,删掉静态库也不影响程序的执行,原因就是程序中已经有了静态库的拷贝,objdump出的内容可以证明这一点。
动态库可以被多个程序共享,如果程序是链接动态库生成的,如果动态库被删除,那么自己的程序就无法运行。
先生成动态库再说。
root@libin:~/program/C/testlib/lib# ll
总用量 16
drwxr-xr-x 2 root root 4096 2012-07-27 21:44 ./
drwxr-xr-x 5 root root 4096 2012-07-27 19:05 ../
-rw-r--r-- 1 root root 41 2012-07-26 22:50 cmp.c
-rw-r--r-- 1 root root 115 2012-07-27 20:07 factorial.c
root@libin:~/program/C/testlib/lib# gcc -shared -o libmymath.so *.c
root@libin:~/program/C/testlib/lib# ll
总用量 24
drwxr-xr-x 2 root root 4096 2012-07-27 21:44 ./
drwxr-xr-x 5 root root 4096 2012-07-27 19:05 ../
-rw-r--r-- 1 root root 41 2012-07-26 22:50 cmp.c
-rw-r--r-- 1 root root 115 2012-07-27 20:07 factorial.c
-rwxr-xr-x 1 root root 6688 2012-07-27 21:44 libmymath.so*还有一个选项是地址无关选项-fPIC。打开这个选项生成的动态库具有地址无关的特点,方便多个进程共享一份动态库对应的指令。这个不是我们关心的内容,如果看官感兴趣的话,可以阅读俞甲子的著作。
下面用动态库来生成我们的程序,还是使用test.c
root@libin:~/program/C/testlib/use# ll
总用量 12
drwxr-xr-x 2 root root 4096 2012-07-27 21:57 ./
drwxr-xr-x 5 root root 4096 2012-07-27 19:05 ../
-rw-r--r-- 1 root root 405 2012-07-26 23:12 test.c
root@libin:~/program/C/testlib/use# gcc -o test test.c -L ../lib/ -lmymath
root@libin:~/program/C/testlib/use# ll
总用量 20
drwxr-xr-x 2 root root 4096 2012-07-27 21:57 ./
drwxr-xr-x 5 root root 4096 2012-07-27 19:05 ../
-rwxr-xr-x 1 root root 7265 2012-07-27 21:57 test*
-rw-r--r-- 1 root root 405 2012-07-26 23:12 test.c
root@libin:~/program/C/testlib/use# ./test
./test: error while loading shared libraries: libmymath.so: cannot open shared object file: No such file or directory前面提到了,用动态库生成的程序,程序中并没有动态库中函数的汇编指令。看下面的证明:
root@libin:~/program/C/testlib/use# objdump -d test
test: file format elf32-i38
....
08048564 :
8048564: 55 push %ebp
8048565: 89 e5 mov %esp,%ebp
8048567: 83 e4 f0 and $0xfffffff0,%esp
804856a: 83 ec 20 sub $0x20,%esp
804856d: c7 44 24 1c 04 00 00 movl $0x4,0x1c(%esp)
8048574: 00
8048575: c7 44 24 18 05 00 00 movl $0x5,0x18(%esp)
804857c: 00
804857d: 8b 44 24 1c mov 0x1c(%esp),%eax
8048581: 89 04 24 mov %eax,(%esp)
8048584: e8 f3 fe ff ff call 804847c
8048589: 89 44 24 14 mov %eax,0x14(%esp)
804858d: 8b 44 24 18 mov 0x18(%esp),%eax
8048591: 89 04 24 mov %eax,(%esp)
8048594: e8 e3 fe ff ff call 804847c
8048599: 89 44 24 10 mov %eax,0x10(%esp)
804859d: 8b 44 24 10 mov 0x10(%esp),%eax
80485a1: 89 44 24 04 mov %eax,0x4(%esp)
80485a5: 8b 44 24 14 mov 0x14(%esp),%eax
80485a9: 89 04 24 mov %eax,(%esp)
80485ac: e8 bb fe ff ff call 804846c
80485b1: 85 c0 test %eax,%eax
80485b3: 74 0e je 80485c3
80485b5: c7 04 24 a0 86 04 08 movl $0x80486a0,(%esp)
80485bc: e8 db fe ff ff call 804849c
80485c1: eb 0c jmp 80485cf
80485c3: c7 04 24 b7 86 04 08 movl $0x80486b7,(%esp)
80485ca: e8 cd fe ff ff call 804849c
80485cf: c7 04 24 64 00 00 00 movl $0x64,(%esp)
80485d6: e8 b1 fe ff ff call 804848c
80485db: eb c0 jmp 804859d
80485dd: 90 nop
80485de: 90 nop
80485df: 90 nop
....cmp@plt这个表示的是我的这个指令码不在本文件,plt是延迟绑定,感兴趣的可以阅读本人的。
现在的问题是,找不到动态库。系统搜索动态库的默认是:
/lib/
/usr/lib
将我们的动态库搬到默认搜索路径,这是一个办法:
root@libin:~/program/C/testlib/use# mv ../lib/libmymath.so /usr/lib/
root@libin:~/program/C/testlib/use# ./test
f_a is bigger than f_b另一种可行的办法是修改/etc/ld.so.conf文件,在文件中加入我们动态库所在的目录。
root@libin:~/program/C/testlib/use# cat /etc/ld.so.conf
include /etc/ld.so.conf.d/*.conf
/home/libin/program/C/testlib/lib/
root@libin:~/program/C/testlib/use# mv /usr/lib/libmymath.so ../lib/
root@libin:~/program/C/testlib/use# ll ../lib/
cmp.c factorial.c libmymath.so
root@libin:~/program/C/testlib/use# ll ../lib/
总用量 24
drwxr-xr-x 2 root root 4096 2012-07-27 22:25 ./
drwxr-xr-x 5 root root 4096 2012-07-27 19:05 ../
-rw-r--r-- 1 root root 41 2012-07-26 22:50 cmp.c
-rw-r--r-- 1 root root 115 2012-07-27 20:07 factorial.c
-rwxr-xr-x 1 root root 6688 2012-07-27 21:44 libmymath.so*
root@libin:~/program/C/testlib/use# ll
总用量 20
drwxr-xr-x 2 root root 4096 2012-07-27 22:05 ./
drwxr-xr-x 5 root root 4096 2012-07-27 19:05 ../
-rwxr-xr-x 1 root root 7265 2012-07-27 21:57 test*
-rw-r--r-- 1 root root 405 2012-07-26 23:12 test.c执行以下,看下结果:
root@libin:~/program/C/testlib/use# ll
总用量 20
drwxr-xr-x 2 root root 4096 2012-07-27 22:05 ./
drwxr-xr-x 5 root root 4096 2012-07-27 19:05 ../
-rwxr-xr-x 1 root root 7265 2012-07-27 21:57 test*
-rw-r--r-- 1 root root 405 2012-07-26 23:12 test.c
root@libin:~/program/C/testlib/use# ./test
./test: error while loading shared libraries: libmymath.so: cannot open shared object file: No such file or directory还是不行,原因是执行ldconfig,刚才的修改没有生效:
root@libin:~/program/C/testlib/use# ldconfig
root@libin:~/program/C/testlib/use# ./test
f_a is bigger than f_b还有在一种方式是修改LD_LIBRARY_PATH,这种方式不被推荐使用,我就不写它了。最后一种方式是最好的,编译链接的时候,指定Run-time path.
gcc 有选项为 -Wl,-rpath, 这个是指定Run-time path的,注意是小写的字母L,不是数字1. 另外,-Wl,rpath之间不能有空格。
root@libin:~/program/C/testlib/use# rm test
root@libin:~/program/C/testlib/use# ll
总用量 12
drwxr-xr-x 2 root root 4096 2012-07-28 00:30 ./
drwxr-xr-x 5 root root 4096 2012-07-27 19:05 ../
-rw-r--r-- 1 root root 405 2012-07-26 23:12 test.c
root@libin:~/program/C/testlib/use# gcc -o test test.c -L ../lib/ -lmymath -Wl,-rpath, ../lib/libmymath.so
root@libin:~/program/C/testlib/use# ll
总用量 20
drwxr-xr-x 2 root root 4096 2012-07-28 00:30 ./
drwxr-xr-x 5 root root 4096 2012-07-27 19:05 ../
-rwxr-xr-x 1 root root 7265 2012-07-28 00:30 test*
-rw-r--r-- 1 root root 405 2012-07-26 23:12 test.c
root@libin:~/program/C/testlib/use# ./test
f_a is bigger than f_b想深入了解这个共享库的可以阅读Ulrich Drepper大神的How to write shared library。
给出搜索路径的优先级
1.编译目标代码时指定的动态库搜索路径;//-wl.-rpath
2.环境变量LD_LIBRARY_PATH指定的动态库搜索路径;//不推荐使用
3.配置文件/etc/ld.so.conf中指定的动态库搜索路径;//本文提到了
4.默认的动态库搜索路径/lib;
5.默认的动态库搜索路径/usr/lib。
注:64位机器自动找lib64库
如何查看一个可执行文件需要那些动态库的支持呢?ldd命令
root@libin:~/program/C/testlib/use# ldd test
linux-gate.so.1 => (0x002dd000)
libmymath.so => /home/libin/program/C/testlib/lib/libmymath.so (0x00d81000)
libc.so.6 => /lib/tls/i686/cmov/libc.so.6 (0x00110000)
/lib/ld-linux.so.2 (0x00f9e000)
————————————————————————————————————————————
题外话,不知道各位看管注意到没有,本文没有头文件的包含,也就说说我虽然定义了factorial 函数和cmp函数,但是我在test.c并没有包含头文件,也没有声明这两个函数。这和我们平时的编程习惯是不符合的。想想我们编写多线程代码,一般都为#include ,链接的时候 -lpthread。这才是common的流程。
WHY?这是下一篇文章的主题。我们真的需要头文件吗?
参考文献:
1 俞甲子等的程序员的自我修养
2 Ulrich Drepper大神的 How to write shared library
3 Why LD_LIBRARY_PATH is bad