Chinaunix首页 | 论坛 | 博客
  • 博客访问: 3881720
  • 博文数量: 146
  • 博客积分: 3918
  • 博客等级: 少校
  • 技术积分: 8585
  • 用 户 组: 普通用户
  • 注册时间: 2010-10-17 13:52
个人简介

个人微薄: weibo.com/manuscola

文章分类

全部博文(146)

文章存档

2016年(3)

2015年(2)

2014年(5)

2013年(42)

2012年(31)

2011年(58)

2010年(5)

分类: LINUX

2012-07-27 20:02:32

    linux下的库文件分成静态库和动态库。库文件的产生也是有道理的。比如某些函数我经常用到,最好的办法就是放入库文件中。比如我是做科学运算的,计算最大公约数,最小公倍数,阶乘能运算经常遇到,但是每次做一个项目,我就写一份函数,那太麻烦了。那我们就可以把这些公用的,经常需要调用的函数,封装成库,供不同的项目使用。

    下面是一个计算阶乘的函数,文件名为factorial.c

  1. int factorial( int n)
  2. {
  3.    if(n <= 1)
  4.    {
  5.      return 1;
  6.    }
  7.    else
  8.    {
  9.      return n*factorial(n-1);
  10.    }
  11. }
   
第二个文件是cmp.c,比较两个int型的大小。

  1. int cmp(int a,int b)
  2. {
  3.     return a-b;
  4. }
我们通过gcc生成对应的obj文件。

  1. root@libin:~/program/C/testlib/lib# ll
  2. 总用量 16
  3. drwxr-xr-x 2 root root 4096 2012-07-27 20:08 ./
  4. drwxr-xr-x 5 root root 4096 2012-07-27 19:05 ../
  5. -rw-r--r-- 1 root root 41 2012-07-26 22:50 cmp.c
  6. -rw-r--r-- 1 root root 115 2012-07-27 20:07 factorial.c
  7. root@libin:~/program/C/testlib/lib# gcc -c *.c
  8. root@libin:~/program/C/testlib/lib# ll
  9. 总用量 24
  10. drwxr-xr-x 2 root root 4096 2012-07-27 20:08 ./
  11. drwxr-xr-x 5 root root 4096 2012-07-27 19:05 ../
  12. -rw-r--r-- 1 root root 41 2012-07-26 22:50 cmp.c
  13. -rw-r--r-- 1 root root 679 2012-07-27 20:08 cmp.o
  14. -rw-r--r-- 1 root root 115 2012-07-27 20:07 factorial.c
  15. -rw-r--r-- 1 root root 764 2012-07-27 20:08 factorial.o
看到了我们生成了cmp.o和factoria.o文件。现在我们用ar命令将两个obj文件打包。

  1. root@libin:~/program/C/testlib/lib# ar rs libmymath.a *.o
  2. ar: creating libmymath.a
  3. root@libin:~/program/C/testlib/lib# ll
  4. 总用量 28
  5. drwxr-xr-x 2 root root 4096 2012-07-27 20:19 ./
  6. drwxr-xr-x 5 root root 4096 2012-07-27 19:05 ../
  7. -rw-r--r-- 1 root root 41 2012-07-26 22:50 cmp.c
  8. -rw-r--r-- 1 root root 679 2012-07-27 20:08 cmp.o
  9. -rw-r--r-- 1 root root 115 2012-07-27 20:07 factorial.c
  10. -rw-r--r-- 1 root root 764 2012-07-27 20:08 factorial.o
  11. -rw-r--r-- 1 root root 1658 2012-07-27 20:19 libmymath.a
已经生成了静态库。如果我们拿到了一个静态库,想知道它是哪些obj文件打包而成,可以使用如下命令:

  1. root@libin:~/program/C/testlib/lib# ar tv libmymath.a 
  2. rw-r--r-- 0/0    679 Jul 27 20:08 2012 cmp.o
  3. rw-r--r-- 0/0    764 Jul 27 20:08 2012 factorial.o
    用户如果libpthread.a的是有那些库打包生成的可以使用同样的命令观看。太多了,我就不列了。

    ar是打包的命令,既然打包,必然也有对应的解包:

  1. root@libin:~/program/C/testlib/lib# rm *.o
  2. root@libin:~/program/C/testlib/lib# ll
  3. 总用量 20
  4. drwxr-xr-x 2 root root 4096 2012-07-27 20:24 ./
  5. drwxr-xr-x 5 root root 4096 2012-07-27 19:05 ../
  6. -rw-r--r-- 1 root root 41 2012-07-26 22:50 cmp.c
  7. -rw-r--r-- 1 root root 115 2012-07-27 20:07 factorial.c
  8. -rw-r--r-- 1 root root 1658 2012-07-27 20:19 libmymath.a
我们看到,已经将所有的obj文件删除。 现在从静态库中将obj文件解出来

  1. root@libin:~/program/C/testlib/lib# ar x libmymath.a
  2. root@libin:~/program/C/testlib/lib# ll
  3. 总用量 28
  4. drwxr-xr-x 2 root root 4096 2012-07-27 20:42 ./
  5. drwxr-xr-x 5 root root 4096 2012-07-27 19:05 ../
  6. -rw-r--r-- 1 root root 41 2012-07-26 22:50 cmp.c
  7. -rw-r--r-- 1 root root 679 2012-07-27 20:42 cmp.o
  8. -rw-r--r-- 1 root root 115 2012-07-27 20:07 factorial.c
  9. -rw-r--r-- 1 root root 764 2012-07-27 20:42 factorial.o
  10. -rw-r--r-- 1 root root 1658 2012-07-27 20:19 libmymath.a
如果要保留obj文件的原始属性,比如时间那么需要o选项 ,即:

  1. root@libin:~/program/C/testlib/lib# ar xo libmymath.a
  2. root@libin:~/program/C/testlib/lib# ll
  3. 总用量 28
  4. drwxr-xr-x 2 root root 4096 2012-07-27 20:42 ./
  5. drwxr-xr-x 5 root root 4096 2012-07-27 19:05 ../
  6. -rw-r--r-- 1 root root 41 2012-07-26 22:50 cmp.c
  7. -rw-r--r-- 1 root root 679 2012-07-27 20:08 cmp.o
  8. -rw-r--r-- 1 root root 115 2012-07-27 20:07 factorial.c
  9. -rw-r--r-- 1 root root 764 2012-07-27 20:08 factorial.o
  10. -rw-r--r-- 1 root root 1658 2012-07-27 20:19 libmymath.a
OK,我们已经得到了静态库,那么开始调用静态库,来做一些事情。

  1. #include <stdio.h>
  2. #include <stdlib.h>

  3. int main(int argc ,char* argv[])
  4. {
  5.                 int a = 4;
  6.                 int b = 5;

  7.                 int f_a = factorial(a);
  8.                 int f_b = factorial(b);
  9.                 while(1)
  10.                 {
  11.                                 if(cmp(f_a,f_b))
  12.                                 {
  13.                                                 printf("f_a is bigger than f_b\n");
  14.                                 }
  15.                                 else
  16.                                 {
  17.                                                 printf("f_a is not bigger than f_b\n");
  18.                                 }

  19.                                 sleep(100);
  20.                 }

  21.                 return 0;
  22. }
面我们看下不使用静态库能否编译通过。

  1. root@libin:~/program/C/testlib/use# gcc -o test test.c
  2. /tmp/ccZM4LMy.o: In function `main':
  3. test.c:(.text+0x21): undefined reference to `factorial'
  4. test.c:(.text+0x31): undefined reference to `factorial'
  5. test.c:(.text+0x49): undefined reference to `cmp'
  6. collect2: ld returned 1 exit status
链接的时候报错了,原因是找不到factorial和cmp两个函数的定义。下面我们使用静态库

  1. root@libin:~/program/C/testlib/use# gcc -o test test.c -L ../lib/ -lmymath
  2. root@libin:~/program/C/testlib/use# ll
  3. 总用量 20
  4. drwxr-xr-x 2 root root 4096 2012-07-27 21:01 ./
  5. drwxr-xr-x 5 root root 4096 2012-07-27 19:05 ../
  6. -rwxr-xr-x 1 root root 7307 2012-07-27 21:01 test*
  7. -rw-r--r-- 1 root root 405 2012-07-26 23:12 test.c
-L选项是告诉gcc,去什么目录下找库文件,默认查找路径是
/lib/
/usr/lib
/usr/local/lib

对这个问题感兴趣的可以去看俞甲子的程序员的自我修养第八章的内容。如果我们不使用-L就会出现:
  1. root@libin:~/program/C/testlib/use# gcc -o test test.c -lmymath
  2. /usr/bin/ld: cannot find -lmymath
  3. collect2: ld returned 1 exit status
  4. root@libin:~/program/C/testlib/use#
    链接器找不到这个库文件。
    当然,链接器并不知道它找的是libmymath.a,它会优先找动态库libmymath.so,找不到了,才会寻找libmymath.a。不管怎么说,我们使用静态库生成了可执行文件.

    不用静态库,直接使用obj文件,一样也是可以生成最终的可执行文件。

  1. root@libin:~/program/C/testlib/use# rm test
  2. root@libin:~/program/C/testlib/use# ll
  3. 总用量 12
  4. drwxr-xr-x 2 root root 4096 2012-07-27 21:29 ./
  5. drwxr-xr-x 5 root root 4096 2012-07-27 19:05 ../
  6. -rw-r--r-- 1 root root 405 2012-07-26 23:12 test.c
  7. root@libin:~/program/C/testlib/use# gcc -o test test.c ../lib/cmp.o ../lib/factorial.o
  8. root@libin:~/program/C/testlib/use# ll
  9. 总用量 20
  10. drwxr-xr-x 2 root root 4096 2012-07-27 21:29 ./
  11. drwxr-xr-x 5 root root 4096 2012-07-27 19:05 ../
  12. -rwxr-xr-x 1 root root 7307 2012-07-27 21:29 test*
  13. -rw-r--r-- 1 root root 405 2012-07-26 23:12 test.c
     注意,对于静态库生成的可执行文件,里面包含了静态库打包的obj文件对应汇编代码换句话说,就是你删除了静态库,生成的test一样可以顺利的执行。因为,factorial.c和cmp.c定义的函数,在test可执行文件里面都有汇编代码。这也是使用静态库被人诟病的地方。如果静态库非常大,那么生成的可执行文件也会比较大。

    我们证明下test中一定会有cmp函数和factorial函数的汇编指令。

  1. root@libin:~/program/C/testlib/use# gcc -o test test.c -L ../lib/ -lmymath
  2. root@libin:~/program/C/testlib/use# ll
  3. 总用量 20
  4. drwxr-xr-x 2 root root 4096 2012-07-27 21:36 ./
  5. drwxr-xr-x 5 root root 4096 2012-07-27 19:05 ../
  6. -rwxr-xr-x 1 root root 7307 2012-07-27 21:36 test*
  7. -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,一样可以正常的跑

  1. root@libin:~/program/C/testlib/use# rm ../lib/libmymath.a
  2. root@libin:~/program/C/testlib/use# ./test
  3. f_a is bigger than f_b
----------------------------------------------------------------------------------------------------------------------
    OK,我们可以考虑下动态库了。动态库出现的要比静态库晚,是为了解决每个使用到静态库的程序,都会有的一份库拷贝,我们上面也看到了,删掉静态库也不影响程序的执行,原因就是程序中已经有了静态库的拷贝,objdump出的内容可以证明这一点。

    动态库可以被多个程序共享,如果程序是链接动态库生成的,如果动态库被删除,那么自己的程序就无法运行。

    先生成动态库再说。


  1. root@libin:~/program/C/testlib/lib# ll
  2. 总用量 16
  3. drwxr-xr-x 2 root root 4096 2012-07-27 21:44 ./
  4. drwxr-xr-x 5 root root 4096 2012-07-27 19:05 ../
  5. -rw-r--r-- 1 root root 41 2012-07-26 22:50 cmp.c
  6. -rw-r--r-- 1 root root 115 2012-07-27 20:07 factorial.c
  7. root@libin:~/program/C/testlib/lib# gcc -shared -o libmymath.so *.c
  8. root@libin:~/program/C/testlib/lib# ll
  9. 总用量 24
  10. drwxr-xr-x 2 root root 4096 2012-07-27 21:44 ./
  11. drwxr-xr-x 5 root root 4096 2012-07-27 19:05 ../
  12. -rw-r--r-- 1 root root 41 2012-07-26 22:50 cmp.c
  13. -rw-r--r-- 1 root root 115 2012-07-27 20:07 factorial.c
  14. -rwxr-xr-x 1 root root 6688 2012-07-27 21:44 libmymath.so*
    还有一个选项是地址无关选项-fPIC。打开这个选项生成的动态库具有地址无关的特点,方便多个进程共享一份动态库对应的指令。这个不是我们关心的内容,如果看官感兴趣的话,可以阅读俞甲子的著作。

   下面用动态库来生成我们的程序,还是使用test.c

  1. root@libin:~/program/C/testlib/use# ll
  2. 总用量 12
  3. drwxr-xr-x 2 root root 4096 2012-07-27 21:57 ./
  4. drwxr-xr-x 5 root root 4096 2012-07-27 19:05 ../
  5. -rw-r--r-- 1 root root 405 2012-07-26 23:12 test.c
  6. root@libin:~/program/C/testlib/use# gcc -o test test.c -L ../lib/ -lmymath
  7. root@libin:~/program/C/testlib/use# ll
  8. 总用量 20
  9. drwxr-xr-x 2 root root 4096 2012-07-27 21:57 ./
  10. drwxr-xr-x 5 root root 4096 2012-07-27 19:05 ../
  11. -rwxr-xr-x 1 root root 7265 2012-07-27 21:57 test*
  12. -rw-r--r-- 1 root root 405 2012-07-26 23:12 test.c


  13. root@libin:~/program/C/testlib/use# ./test
  14. ./test: error while loading shared libraries: libmymath.so: cannot open shared object file: No such file or directory
    前面提到了,用动态库生成的程序,程序中并没有动态库中函数的汇编指令。看下面的证明:

  1. root@libin:~/program/C/testlib/use# objdump -d test

  2. test: file format elf32-i38
  3. ....

  4. 08048564 <main>:
  5.  8048564:    55     push %ebp
  6.  8048565:    89 e5     mov %esp,%ebp
  7.  8048567:    83 e4 f0     and $0xfffffff0,%esp
  8.  804856a:    83 ec 20     sub $0x20,%esp
  9.  804856d:    c7 44 24 1c 04 00 00     movl $0x4,0x1c(%esp)
  10.  8048574:    00
  11.  8048575:    c7 44 24 18 05 00 00     movl $0x5,0x18(%esp)
  12.  804857c:    00
  13.  804857d:    8b 44 24 1c     mov 0x1c(%esp),%eax
  14.  8048581:    89 04 24     mov %eax,(%esp)
  15.  8048584:    e8 f3 fe ff ff     call 804847c <factorial@plt>
  16.  8048589:    89 44 24 14     mov %eax,0x14(%esp)
  17.  804858d:    8b 44 24 18     mov 0x18(%esp),%eax
  18.  8048591:    89 04 24     mov %eax,(%esp)
  19.  8048594:    e8 e3 fe ff ff     call 804847c <factorial@plt>
  20.  8048599:    89 44 24 10     mov %eax,0x10(%esp)
  21.  804859d:    8b 44 24 10     mov 0x10(%esp),%eax
  22.  80485a1:    89 44 24 04     mov %eax,0x4(%esp)
  23.  80485a5:    8b 44 24 14     mov 0x14(%esp),%eax
  24.  80485a9:    89 04 24     mov %eax,(%esp)
  25.  80485ac:    e8 bb fe ff ff     call 804846c <cmp@plt>
  26.  80485b1:    85 c0     test %eax,%eax
  27.  80485b3:    74 0e     je 80485c3 <main+0x5f>
  28.  80485b5:    c7 04 24 a0 86 04 08     movl $0x80486a0,(%esp)
  29.  80485bc:    e8 db fe ff ff     call 804849c <puts@plt>
  30.  80485c1:    eb 0c     jmp 80485cf <main+0x6b>
  31.  80485c3:    c7 04 24 b7 86 04 08     movl $0x80486b7,(%esp)
  32.  80485ca:    e8 cd fe ff ff     call 804849c <puts@plt>
  33.  80485cf:    c7 04 24 64 00 00 00     movl $0x64,(%esp)
  34.  80485d6:    e8 b1 fe ff ff     call 804848c <sleep@plt>
  35.  80485db:    eb c0     jmp 804859d <main+0x39>
  36.  80485dd:    90     nop
  37.  80485de:    90     nop
  38.  80485df:    90     nop

  39. ....
    cmp@plt这个表示的是我的这个指令码不在本文件,plt是延迟绑定,感兴趣的可以阅读本人的。

    现在的问题是,找不到动态库。系统搜索动态库的默认是:
    /lib/
    /usr/lib
    将我们的动态库搬到默认搜索路径,这是一个办法:

  1. root@libin:~/program/C/testlib/use# mv ../lib/libmymath.so /usr/lib/
  2. root@libin:~/program/C/testlib/use# ./test
  3. f_a is bigger than f_b
    另一种可行的办法是修改/etc/ld.so.conf文件,在文件中加入我们动态库所在的目录。

  1. root@libin:~/program/C/testlib/use# cat /etc/ld.so.conf
  2. include /etc/ld.so.conf.d/*.conf
  3. /home/libin/program/C/testlib/lib/

  1. root@libin:~/program/C/testlib/use# mv /usr/lib/libmymath.so ../lib/
  2. root@libin:~/program/C/testlib/use# ll ../lib/
  3. cmp.c factorial.c libmymath.so
  4. root@libin:~/program/C/testlib/use# ll ../lib/
  5. 总用量 24
  6. drwxr-xr-x 2 root root 4096 2012-07-27 22:25 ./
  7. drwxr-xr-x 5 root root 4096 2012-07-27 19:05 ../
  8. -rw-r--r-- 1 root root 41 2012-07-26 22:50 cmp.c
  9. -rw-r--r-- 1 root root 115 2012-07-27 20:07 factorial.c
  10. -rwxr-xr-x 1 root root 6688 2012-07-27 21:44 libmymath.so*
  11. root@libin:~/program/C/testlib/use# ll
  12. 总用量 20
  13. drwxr-xr-x 2 root root 4096 2012-07-27 22:05 ./
  14. drwxr-xr-x 5 root root 4096 2012-07-27 19:05 ../
  15. -rwxr-xr-x 1 root root 7265 2012-07-27 21:57 test*
  16. -rw-r--r-- 1 root root 405 2012-07-26 23:12 test.c


    执行以下,看下结果:
  1. root@libin:~/program/C/testlib/use# ll
  2. 总用量 20
  3. drwxr-xr-x 2 root root 4096 2012-07-27 22:05 ./
  4. drwxr-xr-x 5 root root 4096 2012-07-27 19:05 ../
  5. -rwxr-xr-x 1 root root 7265 2012-07-27 21:57 test*
  6. -rw-r--r-- 1 root root 405 2012-07-26 23:12 test.c
  7. root@libin:~/program/C/testlib/use# ./test
  8. ./test: error while loading shared libraries: libmymath.so: cannot open shared object file: No such file or directory
还是不行,原因是执行ldconfig,刚才的修改没有生效:

  1. root@libin:~/program/C/testlib/use# ldconfig
  2. root@libin:~/program/C/testlib/use# ./test
  3. f_a is bigger than f_b
    还有在一种方式是修改LD_LIBRARY_PATH,这种方式不被推荐使用,我就不写它了。最后一种方式是最好的,编译链接的时候,指定Run-time path.

    gcc 有选项为 -Wl,-rpath,  这个是指定Run-time path的,注意是小写的字母L,不是数字1. 另外,-Wl,rpath之间不能有空格。

  1. root@libin:~/program/C/testlib/use# rm test
  2. root@libin:~/program/C/testlib/use# ll
  3. 总用量 12
  4. drwxr-xr-x 2 root root 4096 2012-07-28 00:30 ./
  5. drwxr-xr-x 5 root root 4096 2012-07-27 19:05 ../
  6. -rw-r--r-- 1 root root 405 2012-07-26 23:12 test.c
  7. root@libin:~/program/C/testlib/use# gcc -o test test.c -L ../lib/ -lmymath -Wl,-rpath, ../lib/libmymath.so
  8. root@libin:~/program/C/testlib/use# ll
  9. 总用量 20
  10. drwxr-xr-x 2 root root 4096 2012-07-28 00:30 ./
  11. drwxr-xr-x 5 root root 4096 2012-07-27 19:05 ../
  12. -rwxr-xr-x 1 root root 7265 2012-07-28 00:30 test*
  13. -rw-r--r-- 1 root root 405 2012-07-26 23:12 test.c
  14. root@libin:~/program/C/testlib/use# ./test
  15. 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命令


  1. root@libin:~/program/C/testlib/use# ldd test
  2. linux-gate.so.1 => (0x002dd000)
  3. libmymath.so => /home/libin/program/C/testlib/lib/libmymath.so (0x00d81000)
  4. libc.so.6 => /lib/tls/i686/cmov/libc.so.6 (0x00110000)
  5. /lib/ld-linux.so.2 (0x00f9e000)


————————————————————————————————————————————
    题外话,不知道各位看管注意到没有,本文没有头文件的包含,也就说说我虽然定义了factorial 函数和cmp函数,但是我在test.c并没有包含头文件,也没有声明这两个函数。这和我们平时的编程习惯是不符合的。想想我们编写多线程代码,一般都为#include ,链接的时候 -lpthread。这才是common的流程。

    WHY?这是下一篇文章的主题。我们真的需要头文件吗?


参考文献:
1 俞甲子等的程序员的自我修养
2 Ulrich Drepper大神的 How to write shared library
Why LD_LIBRARY_PATH is bad






阅读(8021) | 评论(2) | 转发(3) |
给主人留下些什么吧!~~

Bean_lee2012-08-09 17:06:36

horizon_me: 尝试了下,还是很受益的,补充下shared library的另一种用法:
typedef void (*FP_println)(const char*);
int main()
{
        int mode = RTLD_NOW | RTLD_G.....
这个是动态库的显式调用。呵呵

horizon_me2012-08-09 16:59:59

尝试了下,还是很受益的,补充下shared library的另一种用法:
typedef void (*FP_println)(const char*);
int main()
{
        int mode = RTLD_NOW | RTLD_GLOBAL;
        void *handle = dlopen("libprint.so",mode);
        FP_println println = (FP_println)dlsym(handle,"println");
        println("hello world");
  &nbs