Linux下静态库_库的基本概念;如何生成静态库动态库;nm查看库中包含那些函数、ar生成静态库,查看库中包含那些.o文件、ldd查看程序依赖的.so文件;gcc/g++与库相关的参数-L,-l,-f

来源:互联网 发布:军训鞋垫 知乎 编辑:程序博客网 时间:2024/05/22 06:25

转载于:http://blog.163.com/xychenbaihu@yeah/blog/static/13222965520101023104745738/

一、基本概念

1.1、什么是库

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

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

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

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

1.2、库的种类

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

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

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

  静态用.a为后缀, 例如: libhello.a

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

   动态通常用.so为后缀, 例如:libhello.so

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

      为了在同一系统中使用不同版本的库,可以在库文件名后加上版本号为后缀,例如: libhello.so.1.0,由于程序连接默认以.so为文件后缀名。所以为了使用这些库,通常使用建立符号连接的方式。

      ln -s libhello.so.1.0 libhello.so.1  ln -s libhello.so.1 libhello.so

1.3、静态库,动态库文件在linux下是如何生成的:
以下面的代码为例,生成上面用到的hello库:
/* hello.c */  
#include "hello.h"  
void sayhello()  
{      
    printf("hello,world ");  
}

首先用gcc编绎该文件,在编绎时可以使用任何合法的编绎参数,例如-g加入调试代码等:

$gcc -c hello.c -o hello.o

1、生成静态库 生成静态库使用ar工具,其实ar是archive的意思

$ar cqs libhello.a hello.o

2、生成动态库 用gcc来完成,由于可能存在多个版本,因此通常指定版本号:

$gcc -shared -o libhello.so.1.0 hello.o
 
1.4、库文件是如何命名的,有没有什么规范: 
在 linux 下,库文件一般放在/usr/lib和/lib下, 
静态库的名字一般为libxxxx.a,其中 xxxx 是该lib的名称;
动态库的名字一般为libxxxx.so.major.minor,xxxx 是该lib的名称,major是主版本号,minor是副版本号 
 
1.5、可执行程序在执行的时候如何定位共享库(动态库)文件 :
    当系统加载可执行代码(即库文件)的时候,能够知道其所依赖的库的名字,但是还需要知道绝对路径,此时就需要系统动态载入器 (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.6、使用ldd工具,查看可执行程序依赖那些动态库或着动态库依赖于那些动态库
   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、使用nm工具,查看静态库和动态库中有那些函数名(T类表示函数是当前库中定义的,U类表示函数是被调用的,在其它库中定义的,W类是当前库中定义,被其它库中的函数覆盖)。
    有时候可能需要查看一个库中到底有哪些函数,nm工具可以打印出库中的涉及到的所有符号,这里的库既可以是静态的也可以是动态的。

nm列出的符号有很多, 常见的有三种::

一种是在库中被调用,但并没有在库中定义(表明需要其他库支持),用U表示

一种是在库中定义的函数,用T表示,这是最常见的

另外一种是所 谓的“弱态”符号,它们虽然在库中被定义,但是可能被其他库中的同名符号覆盖,用W表示

例如,假设开发者希望知道上文提到的hello库中是否引用了 printf():

    $nm libhello.so | grep printf 

发现printf是U类符号,说明printf被引用,但是并没有在库中定义。

由此可以推断,要正常使用hello库,必须有其它库支持,使用ldd工具查看hello依赖于哪些库:

$ldd hello libc.so.6=>/lib/libc.so.6(0x400la000) /lib/ld-linux.so.2=>/lib/ld-linux.so.2 (0x40000000)

从上面的结果可以继续查看printf最终在哪里被定义,有兴趣可以go on

 

1.8、使用ar工具,可以生成静态库,同时可以查看静态库中包含那些.o文件,即有那些源文件构成

可以使用 ar -t libname.a 来查看一个静态库由那些.o文件构成。

可以使用 ar q libname.a xxx1.o xxx2.o xxx3.o ... xxxn.o 生成静态库

 

 

Linux下进行程序设计时,关于库的使用:
 
一、gcc/g++命令中关于库的参数:-shared: 该选项指定生成动态连接库(让连接器生成T类型的导出符号表,有时候也生成弱连接W类型的导出符号),不用该标志外部程序无法连接。相当于一个可执行文件-fPIC:表示编译为位置独立(地址无关)的代码,不用此选项的话,编译后的代码是位置相关的,所以动态载入时,是通过代码拷贝的方式来满足不同进程的需要,而不能达到真正代码段共享的目的。-L:指定链接库的路径,-L. 表示要连接的库在当前目录中-ltest:指定链接库的名称为test,编译器查找动态连接库时有隐含的命名规则,即在给出的名字前面加上lib,后面加上.so来确定库的名称LD_LIBRARY_PATH:这个环境变量指示动态连接器可以装载动态库的路径。   当然如果有root权限的话,可以修改/etc/ld.so.conf文件,然后调用 /sbin/ldconfig来达到同样的目的,
   不过如果没有root权限,那么只能采用修改LD_LIBRARY_PATH环境变量的方法了。 
调用动态库的时候,有几个问题会经常碰到:
    1、有时,明明已经将库的头文件所在目录 通过 “-I” include进来了,库所在文件通过 “-L”参数引导,并指定了“-l”的库名,但通过ldd命令察看时,就是死活找不到你指定链接的so文件,这时你要作的就是通过修改 LD_LIBRARY_PATH或者/etc/ld.so.conf文件来指定动态库的目录。通常这样做就可以解决库无法链接的问题了。
 
二、静态库链接时搜索路径的顺序:    1. ld会去找gcc/g++命令中的参数-L;   2. 再找gcc的环境变量LIBRARY_PATH,它指定程序静态链接库文件搜索路径;
      export LIBRARY_PATH=$LIBRARY_PATH:data/home/billchen/lib    3. 再找默认库目录 /lib  /usr/lib  /usr/local/lib,这是当初compile gcc时写在程序内的。 
   4. 注意:当一个库同时存在静态库和动态库时,比如libmysqlclient.a和libmysqlclient.so同时存在时:
      gcc和g++默认优先使用动态库,如果想强制使用静态库,需要加参数:-WI,-Bstatic 
   例如:  g++ test.o -o test -Lmysqllibpath -WI,Bstatic -lmysql
  ( 
    注:这个特别的”-WI,-Bstatic”参数,实际上是传给了连接器ld,指示它与静态库连接,如果系统中只有静态库当然就不需要这个参数了。
    如果有的库使用静态库,有的库使用动态库,那应该是 -WI,Bstatic用静态库,-WI,Bdynamic用动态库
    例如:  g++ test.o -o test -Llibpath -WI,Bstatic libname1  -WI,Bdynamic libname2
    表示库1使用静态库,库2使用动态库(前提是动态库和静态库都是存在的)。
    即:
      -WI,-Bstatic   -l库名                       //如果一个库动态库和静态库同时存在,则加载静态库。 
       -WI,Bdynamic   -l库名                          //如果一个库动态库和静态库同时存在,则加载动态库。
  )
 
三、动态链接时、执行时搜索路径顺序:    1. 编译目标代码时指定的动态库搜索路径;   2. 环境变量LD_LIBRARY_PATH指定动态库搜索路径,它指定程序动态链接库文件搜索路径;
      export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:data/home/billchen/lib    3. 配置文件/etc/ld.so.conf中指定的动态库搜索路径;   4. 默认的动态库搜索路径/lib;   5. 默认的动态库搜索路径/usr/lib。 
 
四、有关环境变量:     LIBRARY_PATH环境变量:指定程序静态链接库文件搜索路径    LD_LIBRARY_PATH环境变量:指定程序动态链接库文件搜索路径 
 
五、动态库升级问题:
   在动态链接库升级时,   不能使用cp newlib.so oldlib.so,这样有可能会使程序core掉;   而应该使用:   rm oldlib.so 然后 cp newlib.so oldlib.so   或者   mv oldlib.so oldlib.so_bak 然后 cp newlib.so oldlib.so
 
原创粉丝点击