linux库文件

转载地址 http://blog.csdn.net/high_high/article/details/7193264

 

先说说库文件是怎么来的吧。

以C为例,我们写一个程序,一般都不会把所有的函数都写在一个文件里面,通常都是划分模块,然后一个模块若干个文件,然后在main文件里面调用这些模块。我这里用一个magic.c文件代替实际程序中的所有的文件,你就当这个magic.c文件非常神奇,你调用里面的magic()函数后,它会自动按你的想法把剩下的工作都完成。下面是两个文件:

mian.c

  1. int main(){  
  2.   magic();  
  3. }  


magic.c:

  1. #include <stdio.h>  
  2. void magic(){  
  3.   printf("This is a magic function\n");  
  4. }  


main.c文件里面没有包括任何头文件,因为我们的编译是手动按步骤的,实际编码不推荐这样做,这里可以更加清楚的知道头文件的作用。

一般从C源文件到可执行代码要经过以下4个步骤:预编译(preprocess  gcc -E,生成.i文件),编译(compile gcc -S,生成.s文件),汇编(assemble  gcc -c,生成.o文件),链接(link  生成可执行文件)。这里只讨论最后两个步骤,汇编和链接。

汇编后的结果是每个源文件都有了对应的二进制代码;链接是把所有的二进制代码打包成一个文件,最后得到可执行文件。

使用这个命令汇编main.c:gcc -Wall -c main.c 

-Wall 是列出警告的开关,如果没有这个开关,汇编成功,什么提示都没有,如果打开这个开关,会得到如下一个警告:

main.c:2:3: warning: implicit declaration of function ‘magic’ [-Wimplicit-function-declaration]

要消除这个警告很容易,有两个办法:

1)前面加个声明就好了,void magic();  

2)写个magic.h的头文件:

  1. void magic();  


然后在main.c里面包含进来:#include "magic.h"。

这两种办法的作用是告诉main,现在没有magic的实现不要紧,我确定一定以及肯定这个magic函数是存在的,放心使用就好了,而且告诉了main这个magic该怎么用,参数是什么,返回值是什么。

但是没有magic的声明也没有问题,只是警告,不是错误,因为我们知道我们的magic是怎么定义的,而且我们也确定会在后面链接magic函数。

但是问题就出在这里了,比如我们写了一个超级牛逼的函数想让别人使用,但是这个超级牛逼的函数要下个礼拜才能给别人怎么办呢?我们可以先给个头文件,然后拍着胸脯说你就按照我这个函数声明写,只要你的调用(call)没问题,程序运行的结果就没问题。

一个礼拜后到了可以给别人我们的超级牛逼函数的时候了,这时候我们也有两种选择可以选。

第一,发源文件,这个没话说了,很棒,互相学习进步,让别人自己把源文件汇编成二进制代码,然后再和他的main代码链接成可执行代码就好了。

第二,如果涉及商业或者版权因素,那么我们就只能自己把汇编后的二进制代码给别人,然后让别人再去链接。这里又体现了头文件的好处,只需要知道函数怎么调用就可以了,不用去知道函数内部怎么写的。比如刚开始学C只需要知道怎么使用 fopen, fclose, fprintf, fscanf 等等就好了,等以后慢慢深入再去探个究竟。


好了,那么库文件究竟是什么呢?其实就是汇编后的二进制代码,是别人写好的超级牛逼的代码汇编以后放在那里让我们使用的。但是这个汇编后的二进制代码和我们自己生成的还有些不同,具体来说有两种。

库文件本身分为两种:静态库文件(static library)和动态库文件(dynamic library),linux下,静态库文件以.a结尾(archive),动态库文件以.so结尾(shared object)。

静态库文件其实就是汇编后的二进制代码的一个压缩文件(archive),里面可以有一个或者N个.o文件。比如上面的magic.c,可以这样生成静态库文件:

首先汇编:gcc -c magic.c ,生成magic.o,然后使用压缩文件命令(ar)生成静态库文件:ar rc libmagic.a magic.o ,其中rc是压缩选项,libmagic.a是给静态库文件起的名字,magic.o 就是用来生成静态库文件的二进制文件,当然后面可以接很多个.o文件,把他们压缩成一个静态库文件给别人使用。

libmagic.a 弄好以后就可以和main的二进制文件链接在一起了: gcc -o main main.o -lmagic -L.

-o main 选项是把产生的可执行文件命名为main,没有的话默认名字是a.out,-lmagic表示使用库文件libmagic.so(后面介绍)或者libmagic.a,如果都存在的话使用.so文件。现在还没有libmagic.so文件,所以链接了libmagic.a里面的magic函数。

动态库文件有些不同,直接使用gcc生成,首先还是汇编:gcc -fPIC -c magic.c ,因为汇编后的二进制代码要拿去做动态库,所以多了一个-fPIC选项,用来确定库中函数的链接位置。然后从二进制文件生成动态库文件:gcc -shared -o libmagic.so magic.o。后面可以接很多.o文件。

动态库的链接的命令和静态库一样,但是做的事情不一样。链接静态库文件是把库里面的函数复制了到了可执行文件里面,所以可执行文件生成以后有没有静态库文件就不重要了。而动态链接只是在可执行文件里面记录了那个函数需要使用的动态库文件,真正的链接是在运行(run)的时候,只有运行我们生成的可执行文件,到了要使用动态库文件里面函数的时候,那个函数才会被加载到内存中再执行。所以涉及到了操作系统寻找动态链接库的路径问题。可以使用ldd程序查看我们的程序都用到了哪些动态库文件: ldd main,输出如下:

linux-vdso.so.1 =>  (0x00007fff8c9dd000)

libmagic.so => not found

libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fb0f846c000)

/lib64/ld-linux-x86-64.so.2 (0x00007fb0f8829000)

 

可以看到libmagic.so没有找到,所以如果运行main也会报错:./main: error while loading shared libraries: libmagic.so: cannot open shared object file: No such file or directory   。要让系统找到动态库文件就必须设置动态库文件的路径了(不同于可执行文件路径PATH,头文件路径INCLUDE 哦),如果要在动态库文件的搜寻路径里面加上当前路径,可以如下设置:

LD_LIBRARY_PATH=$LD_LIBRARY_PATH:.       /* 当前路径是. */

export LD_LIBRARY_PATH

设置好路径以后动态库文件就能被找到了,再运行ldd main,输出下面的结果:

linux-vdso.so.1 =>  (0x00007fffb55ff000)

libmagic.so (0x00007ff515fa7000)

libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007ff515bec000)

/lib64/ld-linux-x86-64.so.2 (0x00007ff5161ab000)

 

关于链接再多说一句吧,gcc的链接是调用了另一个程序:ld。可以自己手动调用ld或者写在Makefile里面,不过一般不用那么麻烦,用gcc就好了。

由于链接方法不用,所以链接静态库文件后的可执行文件一般都比较大,比如上面的例子,链接静态库文件后的main大小是8498个字节,而链接动态库文件后的main的大小是8401字节。好像文件大小没少多少,那是因为我们的magic函数小,大一点区别就很明显了。


谢谢观赏,欢迎点评。


参考文献:

The art of debugging with gdb,ddd, and eclipse. Norman Matloff and Peter Jay Salzman.

 

附录 :https://www.cnblogs.com/yangg518/p/5842651.html

一、基本概念

1.1什么是库

在windows平台和linux平台下都大量存在着库。

本质上来说库是一种可执行代码的二进制形式,可以被操作系统载入内存执行。

由于windows和linux的平台不同(主要是编译器、汇编器和连接器的不同),因此二者库的二进制是不兼容的。

本文仅限于介绍linux下的库。

 

 

1.2库的种类

linux下的库有两种:静态库和共享库(动态库)。

二者的不同点在于代码被载入的时刻不同。

静态库的代码在编译过程中已经被载入可执行程序,因此体积较大。

共享库的代码是在可执行程序运行时才载入内存的,在编译过程中仅简单的引用,因此代码体积较小。

 

 

1.3库存在的意义

库是别人写好的现有的,成熟的,可以复用的代码,你可以使用但要记得遵守许可协议。

现实中每个程序都要依赖很多基础的底层库,不可能每个人的代码都从零开始,因此库的存在意义非同寻常。

共享库的好处是,不同的应用程序如果调用相同的库,那么在内存里只需要有一份该共享库的实例。

 

 

1.4库文件是如何产生的在linux下

静态库的后缀是.a,它的产生分两步

Step 1.由源文件编译生成一堆.o,每个.o里都包含这个编译单元的符号表

Step 2.ar命令将很多.o转换成.a,成为静态库

动态库的后缀是.so,它由gcc加特定参数编译产生。

具体方法参见后文实例。

 

1.5库文件是如何命名的,有没有什么规范

在linux下,库文件一般放在/usr/lib和/lib下,

静态库的名字一般为libxxxx.a,其中xxxx是该lib的名称

动态库的名字一般为libxxxx.so.major.minor,xxxx是该lib的名称,major是主版本号, minor是副版本号

 

 

1.6如何知道一个可执行程序依赖哪些库

ldd命令可以查看一个可执行程序依赖的共享库,

例如# ldd /bin/lnlibc.so.6

=> /lib/libc.so.6 (0×40021000)/lib/ld-linux.so.2

=> /lib/ld- linux.so.2 (0×40000000)

可以看到ln命令依赖于libc库和ld-linux库

 

 

1.7可执行程序在执行的时候如何定位共享库文件

当系统加载可执行代码时候,能够知道其所依赖的库的名字,但是还需要知道绝对路径。

此时就需要系统动态载入器(dynamic linker/loader)

对于elf格式的可执行程序,是由ld-linux.so*来完成的,它先后搜索elf文件的 DT_RPATH段—环境变量LD_LIBRARY_PATH—/etc/ld.so.cache文件列表—/lib/,/usr/lib目录找到库文件后将其载入内存

如:export LD_LIBRARY_PATH=’pwd’

将当前文件目录添加为共享目录

 

1.8在新安装一个库之后如何让系统能够找到他

如果安装在/lib或者/usr/lib下,那么ld默认能够找到,无需其他操作。

如果安装在其他目录,需要将其添加到/etc/ld.so.cache文件中,步骤如下

1.编辑/etc/ld.so.conf文件,加入库文件所在目录的路径

2.运行ldconfig,该命令会重建/etc/ld.so.cache文件

 

posted @ 2017-11-24 11:17  夜月色下  阅读(4913)  评论(0编辑  收藏  举报