Linux中的动态库和静态库(.a/.la/.so/.o)

本文由乌合之众 lym瞎编,欢迎转载 http://my.oschina.net/oloroso
本文由乌合之众 lym瞎编,欢迎转载 http://www.cnblogs.com/oloroso/

在windows下,一般可以通过文件的后缀名来识别文件的类型。在Linux下大致上也是可以的。但是要明确的一点是,在linux下,文件的后缀与文件的类型是没有必然的联系的。这只是约定俗称的习惯罢了。

在linux 下进行C/C++开发,一般都是使用的gcc编译器,所以本文的讲解以gcc为主。

  • .o文件,即目标文件。一般通过.c或者.cpp文件编译而来,相当于VC编译出来的obj文件
  • .so文件,shared object 共享库(对象),相当于windows下的dll。
  • .a文件,archive 归档包,即静态库。其实质是多个.o文件打包的结果,相当于VC下的.lib文件
  • .la文件,libtool archive 文件,是libtool自动生成的共享库文件。

下面对这四种文件进行逐个说明。


C/C++程序编译的过程

先说一下C/C++编译的几个过程。

  1. 预处理,展开头文件,宏定义,条件编译处理等。通过gcc -E source.c -o source.i或者cpp source.c生成。
  2. 编译。这里是一个狭义的编译意义,指的是将预处理后的文件翻译成汇编代码的过程。通过gcc -S source.i生成。默认生成source.s文件。
  3. 汇编。汇编即将上一步生成的汇编代码翻译成对应的二进制机器码的过程。通过gcc -c source.s来生成source.o文件。
  4. 链接。链接是将生成目标文件和其引用的各种符号等生成一个完整的可执行程序的过程。链接的时候会进行虚拟内存的重定向操作。

上面四个步骤就是C/C++程序编译的几个基本步骤。前面三个步骤都是很简单,大多时候会合并为一个步骤。只有第四个步骤链接是复杂一点的。很多时候我们编译比较大的项目,报错的往往是在链接的时候缺少某些库,或者某些符号找不到定义,重定义等。


.o文件(目标文件)

.o文件就是C/C++源码编译的结果。即上面所说的C/C++编译过程中的前三步。一般开发中很少将这三步分开来做,通常的做法是一步生成。

这里举个例子,我们来写一个函数int atoi(const char* str)

头文件atoi.h

.#ifndef ATOI_H
.#define ATOI_H
int atoi(const char* str);
.#endif //! ATOI_H

源文件atoi.c

.#include <stdio.h>
.#include "atoi.h"
int atoi(const char* str)
{
    int ret = 0;
    if(str != NULL){
        sscanf(str,"%d",&ret);
    }
    return ret;
}

创建atoi.o

直接使用命令gcc -c atoi.c -o atoi.ogcc -c atoi.c来生成目标文件。
上面我们函数中调用了sscanf这个C标准库中的函数,那么它在.o文件中会记录这个存在,我们可以使用readelf工具来查看一下。

o@Neo-kylin:~/lib_a_so$ ls
atoi.c  atoi.h
o@Neo-kylin:~/lib_a_so$ gcc -c atoi.c
o@Neo-kylin:~/lib_a_so$ ll
总用量 20
drwxr-xr-x 2 o    o    4096 10月 10 15:11 ./
drwxrwxr-x 5 1000 1000 4096 10月 10 14:32 ../
-rw-rw-r-- 1 o    o     140 10月 10 15:07 atoi.c
-rw-rw-r-- 1 o    o      75 10月 10 15:07 atoi.h
-rw-rw-r-- 1 o    o    1536 10月 10 15:11 atoi.o
o@Neo-kylin:~/lib_a_so$ readelf -s atoi.o 

Symbol table '.symtab' contains 11 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND
     1: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS atoi.c
     2: 0000000000000000     0 SECTION LOCAL  DEFAULT    1
     3: 0000000000000000     0 SECTION LOCAL  DEFAULT    3
     4: 0000000000000000     0 SECTION LOCAL  DEFAULT    4
     5: 0000000000000000     0 SECTION LOCAL  DEFAULT    5
     6: 0000000000000000     0 SECTION LOCAL  DEFAULT    7
     7: 0000000000000000     0 SECTION LOCAL  DEFAULT    8
     8: 0000000000000000     0 SECTION LOCAL  DEFAULT    6
     9: 0000000000000000    60 FUNC    GLOBAL DEFAULT    1 atoi
    10: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND __isoc99_sscanf

这就是.o文件了。它保存了编译的时候引用到的符号(函数,全局变量等),这些符号,在链接的时候需要使用到。

使用atoi.o

使用atoi.o的地方有很多,就不一一列举了。这里先不说怎么用,后面生成.a文件的时候用到了。


.a文件(静态库文件)

静态库是多个.o文件的打包的结果,前面已经说过了,其实不一定非要多个文件,一个.o文件也可以打包为.a文件。
这一步使用ar工具来操作。ar工具是用来创建, 修改和提取archives归档文件的工具,具体使用可以看manpages

ar [emulation options] [-]{dmpqrstx}[abcfilNoPsSuvV] [member-name] [count] archive-file file...

这个工具的作用看起来很简单,但是其是很强大,且参数的设置很复杂的。这里不是为了介绍这个工具,不细说了。

创建atoi.a

我们先使用上面生成的atoi.o文件来生成一个atoi.a文件。

o@Neo-kylin:~/lib_a_so$ ls
atoi.c  atoi.h  atoi.o
o@Neo-kylin:~/lib_a_so$ ar -r atoi.a atoi.o
ar: creating atoi.a
o@Neo-kylin:~/lib_a_so$ ll
总用量 24
drwxr-xr-x 2 o    o    4096 10月 10 15:35 ./
drwxrwxr-x 5 1000 1000 4096 10月 10 14:32 ../
-rw-rw-r-- 1 o    o    1678 10月 10 15:35 atoi.a
-rw-rw-r-- 1 o    o     140 10月 10 15:07 atoi.c
-rw-rw-r-- 1 o    o      75 10月 10 15:07 atoi.h
-rw-rw-r-- 1 o    o    1536 10月 10 15:11 atoi.o

-r参数的意思是替换已存在的或插入新的文件到archive包中。

使用atoi.a

创建了atoi.a文件后,我们就可以来使用它了。这里我们写一个main函数来调用atoi

main.c文件

int main()
{
    return atoi("5");
}

这一次我们先把main.c编译为main.o文件。

o@Neo-kylin:~/lib_a_so$ ls
atoi.a  atoi.c  atoi.h  atoi.o  main.c
o@Neo-kylin:~/lib_a_so$ gcc -c main.c
o@Neo-kylin:~/lib_a_so$ ls
atoi.a  atoi.c  atoi.h  atoi.o  main.c  main.o

然后使用ld程序来链接main.oatoi.a

o@Neo-kylin:~/lib_a_so$ ld main.o atoi.a -o main
ld: warning: cannot find entry symbol _start; defaulting to 00000000004000b0
atoi.a(atoi.o): In function `atoi':
atoi.c:(.text+0x33): undefined reference to `__isoc99_sscanf'`

上面报了一个错误,原因是在atoi函数中使用未定义的引用 __isoc99_sscanf,这个问题我们可以通过链接上libc.a或者libc.so来解决这个问题。通常的情况下,都是链接libc.so来解决的,如果使用glibc的静态库,那么你也必须将你的程序开源,不然这应该算是违反GPL协议的约定。

o@Neo-kylin:~/lib_a_so$ ld main.o atoi.a /lib64/libc.so.6 -o main
ld: warning: cannot find entry symbol _start; defaulting to 0000000000400288

这里又报了一个警告,是没有发现_start符号的意思。这是因为没有发现程序主入口点的原因。在C语言中,程序的入口函数是main,但是在汇编中,程序的主入口函数是_start

这里我们可以把main.c文件中的main函数改为_start函数,然后再编译为main.o再链接就没有问题了。但是这不是正确的做法,这样做虽然使用ld来链接是不会报错了,但是程序是运行不了的。会报错误

o@Neo-kylin:~/lib_a_so$ ld main.o atoi.a /lib64/libc.so.6 -o main
o@Neo-kylin:~/lib_a_so$ ./main
-bash: ./main: /lib/ld64.so.1: bad ELF interpreter: 没有那个文件或目录

正确的做法是链接上crt0.o、crti.o、crtn.o等很多个文件就行了,不同的机器,需要链接的文件的位置可能不一样。这个参数可能非常长,普通人记不住。
这个是可以怎么得到呢?我肯定不知道这些文件都在什么位置,但是gcc编译环境知道,我们可以使用gcc来获取。

o@Neo-kylin:~/lib_a_so$ gcc -v -o main main.o atoi.a
使用内建 specs。
目标:x86_64-redhat-linux
配置为:../configure --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --with-bugurl=http://bugzilla.redhat.com/bugzilla --enable-bootstrap --enable-shared --enable-threads=posix --enable-checking=release --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-gnu-unique-object --enable-languages=c,c++,objc,obj-c++,java,fortran,ada --enable-java-awt=gtk --disable-dssi --with-java-home=/usr/lib/jvm/java-1.5.0-gcj-1.5.0.0/jre --enable-libgcj-multifile --enable-java-maintainer-mode --with-ecj-jar=/usr/share/java/eclipse-ecj.jar --disable-libjava-multilib --with-ppl --with-cloog --with-tune=generic --with-arch_32=i686 --build=x86_64-redhat-linux
线程模型:posix
gcc 版本 4.4.7 20120313 (Red Hat 4.4.7-16) (GCC)
COMPILER_PATH=/usr/libexec/gcc/x86_64-redhat-linux/4.4.7/:/usr/libexec/gcc/x86_64-redhat-linux/4.4.7/:/usr/libexec/gcc/x86_64-redhat-linux/:/usr/lib/gcc/x86_64-redhat-linux/4.4.7/:/usr/lib/gcc/x86_64-redhat-linux/:/usr/libexec/gcc/x86_64-redhat-linux/4.4.7/:/usr/libexec/gcc/x86_64-redhat-linux/:/usr/lib/gcc/x86_64-redhat-linux/4.4.7/:/usr/lib/gcc/x86_64-redhat-linux/
LIBRARY_PATH=/usr/lib/gcc/x86_64-redhat-linux/4.4.7/:/usr/lib/gcc/x86_64-redhat-linux/4.4.7/:/usr/lib/gcc/x86_64-redhat-linux/4.4.7/../../../../lib64/:/lib/../lib64/:/usr/lib/../lib64/:/usr/lib/gcc/x86_64-redhat-linux/4.4.7/../../../:/lib/:/usr/lib/
COLLECT_GCC_OPTIONS='-v' '-o' 'main' '-mtune=generic'
 /usr/libexec/gcc/x86_64-redhat-linux/4.4.7/collect2 --eh-frame-hdr --build-id -m elf_x86_64 --hash-style=gnu -dynamic-linker /lib64/ld-linux-x86-64.so.2 -o main /usr/lib/gcc/x86_64-redhat-linux/4.4.7/../../../../lib64/crt1.o /usr/lib/gcc/x86_64-redhat-linux/4.4.7/../../../../lib64/crti.o /usr/lib/gcc/x86_64-redhat-linux/4.4.7/crtbegin.o -L/usr/lib/gcc/x86_64-redhat-linux/4.4.7 -L/usr/lib/gcc/x86_64-redhat-linux/4.4.7 -L/usr/lib/gcc/x86_64-redhat-linux/4.4.7/../../../../lib64 -L/lib/../lib64 -L/usr/lib/../lib64 -L/usr/lib/gcc/x86_64-redhat-linux/4.4.7/../../.. main.o atoi.a -lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed /usr/lib/gcc/x86_64-redhat-linux/4.4.7/crtend.o /usr/lib/gcc/x86_64-redhat-linux/4.4.7/../../../../lib64/crtn.o

编译之后我们可以来看以下程序运行结果是否正确。

o@Neo-kylin:~/lib_a_so$ ./main
o@Neo-kylin:~/lib_a_so$ echo $?
5

结果为5,与预期一致。


.so文件(共享库文件)

共享库文件和windows下的dll文件(dynamic link library)的概念是一样的,都是在程序运行的时候进行动态链接,供程序调用的。
在linux 下可以使用ldd命令来查看某个可执行文件需要链接哪些共享库(动态库),并可以确定这些要链接的共享库在本机中的位置。

o@Neo-kylin:~/lib_a_so$ ldd main
    linux-vdso.so.1 =>  (0x00007fffab1ff000)
    libc.so.6 => /lib64/libc.so.6 (0x000000305d800000)
    /lib64/ld-linux-x86-64.so.2 (0x000000305d000000)

这里要说以下动态库的查找路径。对于程序需要链接的动态库xxx.so,如果它在当前目录下有,那么链接当前目录下的。如果没有,那么就链接系统/etc/ld.so.cache(可通过ldconfig来更新)文件中查找xxx.so的路径,如果都没有,那么就会报错啦。
我们在当前目录创建一个libc.so.6文件,然后再使用ldd看一下。

o@Neo-kylin:~/lib_a_so$ touch libc.so.6 && chmod +x libc.so.6
o@Neo-kylin:~/lib_a_so$ ls -l libc.so.6
-rwxrwxr-x 1 o o 0 10月 10 17:15 libc.so.6
o@Neo-kylin:~/lib_a_so$ ldd main
./main: error while loading shared libraries: ./libc.so.6: file too short

可以看到,这时候是链接的当前目录下的libc.so.6这个文件,很可惜,出错了。
其实在链接的时候,我们可以通过-Wl,-rpath=sopath来指定运行时加载动态库的路径。这样做的好处是可以把一些动态库的位置信息不加入到/etc/ld.so.cache中,已经避免和系统已有动态库产生冲突的情况。(例如目标机器的glibc库版本太低,而编译程序的时候使用的高版本的,而出现”libc.so.6: version `GLIBC_2.14’ not found”类似的错误的情况)

注: -Wl: 表示后面的参数将传给link程序ld,gcc编译时候的链接实际上是调用ld进行的.

创建atoi.so

这里还是使用前面创建的atoi.c文件创建atoi.so文件。实际上我们这里创建atoi.so.1文件,文件名后面的.1代表的是版本号。动态库因为使用的时候是动态链接的,而不是直接链接到目标程序文件中的。所以可能同时存在多个版本的情况,一般都会指定版本号。
通常使用libxxx.so.主版本号.副版本号的形式来命名。

o@Neo-kylin:~/lib_a_so$ gcc -shared -o atoi.so.1 atoi.c
/usr/bin/ld: /tmp/ccLK0pLC.o: relocation R_X86_64_32 against `.rodata' can not be used when making a shared object; recompile with -fPIC
/tmp/ccLK0pLC.o: could not read symbols: Bad value
collect2: ld 返回 1
o@Neo-kylin:~/lib_a_so$ gcc -fPIC -shared -o atoi.so.1 atoi.c
o@Neo-kylin:~/lib_a_so$ ls
atoi.a  atoi.c  atoi.h  atoi.o  atoi.so.1  main.c  main.o

-share该选项指定生成动态连接库(让连接器生成T类型的导出符号表,有时候也生成弱连接W类型的导出符号,后面介绍nm工具的时候再说),不用该标志外部程序无法连接。相当于一个可执行文件。
-fPIC表示编译为位置独立的代码,不用此选项的话编译后的代码是位置相关的所以动态载入时是通过代码拷贝的方式来满足不同进程的需要,而不能达到真正代码段共享的目的。

第一次没有指定-fPIC的时候出错了,原因是针对可迁移R_X86_64_32平台,只读数据段’.rodata’不能创建成共享对象,原因是在动态链接动态库的时候,如果没有编译成位置无关代码,那么链接的时候可能因为某些代码的位置具有相关性,而在执行时出现错误。可执行文件在链接时就知道每一行代码、每一个变量会被放到线性地址空间的什么位置,因此这些地址可以都作为常数写到代码里面。对于动态库,只有加载的时候才知道。

如果代码中没有只读数据段,那么就不会有这个问题。例如

o@Neo-kylin:~/lib_a_so$ cat >val.c
int a= 100;
o@Neo-kylin:~/lib_a_so$ gcc -shared -o val.so val.c

使用atoi.so

使用.so文件的形式和使用.a也差不多,也是使用ld来进行链接。因为这过于复杂,还是使用gcc来做这个操作(实际上gcc也是使用的ld)。

o@Neo-kylin:~/lib_a_so$ gcc -o main main.o atoi.so.1
o@Neo-kylin:~/lib_a_so$ ldd main
    linux-vdso.so.1 =>  (0x00007fff56eaf000)
    atoi.so.1 => not found
    libc.so.6 => /lib64/libc.so.6 (0x000000305d800000)
    /lib64/ld-linux-x86-64.so.2 (0x000000305d000000)
o@Neo-kylin:~/lib_a_so$ ./main
./main: error while loading shared libraries: atoi.so.1: cannot open shared object file: No such file or directory

上面执行的时候报错,意思是找不到atoi.so.1这个文件。原因是共享库的查找目录没有当前目录,我们可以添加环境变量LD_LIBRARY_PATH来使系统动态载入器 (dynamic linker/loader)在当前目录也查找。

o@Neo-kylin:~/lib_a_so$ export LD_LIBRARY_PATH=.
o@Neo-kylin:~/lib_a_so$ ldd main
    linux-vdso.so.1 =>  (0x00007fff08fff000)
    atoi.so.1 => ./atoi.so.1 (0x00007f9ed7ac6000)
    libc.so.6 => /lib64/libc.so.6 (0x000000305d800000)
    /lib64/ld-linux-x86-64.so.2 (0x000000305d000000)
o@Neo-kylin:~/lib_a_so$ ./main
o@Neo-kylin:~/lib_a_so$ echo $?
5

还有一种办法,比添加环境变量更好使,也更具有可移植性,那就是编译的时候指定运行的时候共享库的加载路径。gcc使用-Wl,-rpath=sopath来指定,其中sopath是共享库放置的路径(可以是绝对路径,也可以是相对路径)。

o@Neo-kylin:~/lib_a_so$ gcc -o main main.o -Wl,-rpath=. atoi.so.1
o@Neo-kylin:~/lib_a_so$ ldd main
    linux-vdso.so.1 =>  (0x00007fff457ff000)
    atoi.so.1 => ./atoi.so.1 (0x00007fb946d56000)
    libc.so.6 => /lib64/libc.so.6 (0x000000305d800000)
    /lib64/ld-linux-x86-64.so.2 (0x000000305d000000)
o@Neo-kylin:~/lib_a_so$ ./main
o@Neo-kylin:~/lib_a_so$ echo $?
5

动态库还可以通过dlopen/dlsym等来使用,这里就不介绍了。


.la文件(libtool archive)

以下的内容,参考使用 GNU Libtool 创建库
这里先要说以下libtool这个工具。
libtool是GNU的一个用来解决各个平台创建动态/静态库文件的不同操作的过于复杂的工具。它提供了使用抽象的接口进行动态/静态库的方法。
使用GNU Libtool可以容易的在不同的系统中建立动态链接库。它通过一个称为Libtool库的抽象,隐藏了不同系统之间的差异,给开发人员提供了一致的的接口。对于大部分情况,开发人员甚至不用去查看相应的系统手册,只需要掌握GNU Libtool的用法就可以了。并且,使用 LibtoolMakefile也只需要编写一次就可以在多个系统上使用。

libtool的使用

libtool的使用一般分为以下几个步骤
1. 创建 Libtool 对象文件
2. 创建 Libtool 库
3. 安装 Libtool 库
4. 使用 Libtool 库
5. 卸载 Libtool 库

1. 创建 Libtool 对象文件

o@Neo-kylin:~/lib_a_so$ ls
atoi.a  atoi.c  atoi.h  main  main.c
o@Neo-kylin:~/lib_a_so$ libtool --mode=compile gcc -c atoi.c
libtool: compile:  gcc -c atoi.c  -fPIC -DPIC -o .libs/atoi.o
libtool: compile:  gcc -c atoi.c -o atoi.o >/dev/null 2>&1
o@Neo-kylin:~/lib_a_so$ ls -aR
.:
.  ..  atoi.a  atoi.c  atoi.h  atoi.lo  atoi.o  .libs  main  main.c  main.lo  main.o

./.libs:
.  ..  atoi.o  libatoi.a  libatoi.la  libatoi.lai  libatoi.so  libatoi.so.0  libatoi.so.0.0.0  main.o

创建libtool对象文件的过程,实际上是生成.o.so.a文件的过程,同时还生成了一个.lo文件。.lo文件里面描述了两个.o文件的路径。这一步,就已经生成了相应的动态库和静态库。

o@Neo-kylin:~/lib_a_so$ cat atoi.lo
# atoi.lo - a libtool object file
# Generated by ltmain.sh (GNU libtool) 2.2.6b
#
# Please DO NOT delete this file!
# It is necessary for linking the library.

# Name of the PIC object.
pic_object='.libs/atoi.o'

# Name of the non-PIC object
non_pic_object='atoi.o'

其中一个是用于生成静态库的,一个是用于生产动态库的。

2. 创建 Libtool 库

o@Neo-kylin:~/lib_a_so$ libtool --mode=link gcc -o libatoi.la atoi.lo -rpath /home/o/lib_a_so/lib -lc
libtool: link: rm -fr  .libs/libatoi.a .libs/libatoi.la .libs/libatoi.lai .libs/libatoi.so .libs/libatoi.so.0 .libs/libatoi.so.0.0.0
libtool: link: gcc -shared  .libs/atoi.o   -lc    -Wl,-soname -Wl,libatoi.so.0 -o .libs/libatoi.so.0.0.0
libtool: link: (cd ".libs" && rm -f "libatoi.so.0" && ln -s "libatoi.so.0.0.0" "libatoi.so.0")
libtool: link: (cd ".libs" && rm -f "libatoi.so" && ln -s "libatoi.so.0.0.0" "libatoi.so")
libtool: link: ar cru .libs/libatoi.a  atoi.o
libtool: link: ranlib .libs/libatoi.a
libtool: link: ( cd ".libs" && rm -f "libatoi.la" && ln -s "../libatoi.la" "libatoi.la" )

注意这里使用atoi.lo作为输入文件,并指定生成的目标文件为libatoi.la
-rpath选项是指定Libtool将这个库安装到的位置,如果省略了-rpath选项,那么不生成动态链接库。
因为在atoi函数中使用了标准C库函数sscanf,所以带上-lc选项,Libtool 会记住这个依赖关系,后续在使用我们的库时自动的将依赖的库链接进来。

o@Neo-kylin:~/lib_a_so$ ls -aR
.:
.  ..  atoi.a  atoi.c  atoi.h  atoi.lo  atoi.o  libatoi.la  .libs  main  main.c  main.lo  main.o

./.libs:
.  ..  atoi.o  libatoi.a  libatoi.la  libatoi.lai  libatoi.so  libatoi.so.0  libatoi.so.0.0.0  main.o

可以看到这次在当前目录下生成了libatoi.la文件,而.libs目录下的那个是一个符号链接,指向当前目录下的这个文件。这其实是一个文本文件,里面的内容比较长,就不贴了。贴几个比较重要的。

o@Neo-kylin:~/lib_a_so$ cat libatoi.la
dlname='libatoi.so.0'
library_names='libatoi.so.0.0.0 libatoi.so.0 libatoi.so'
old_library='libatoi.a'
dependency_libs=' -lc'
installed=no
shouldnotlink=no
dlopen=''
dlpreopen=''
libdir='/home/o/lib_a_so/lib'

3. 安装 Libtool 库

o@Neo-kylin:~/lib_a_so$ libtool --mode=install install -c libatoi.la /home/o/lib_a_so/lib
libtool: install: install -c .libs/libatoi.so.0.0.0 /home/o/lib_a_so/libatoi.so.0.0.0
libtool: install: (cd /home/o/lib_a_so && { ln -s -f libatoi.so.0.0.0 libatoi.so.0 || { rm -f libatoi.so.0 && ln -s libatoi.so.0.0.0 libatoi.so.0; }; })
libtool: install: (cd /home/o/lib_a_so && { ln -s -f libatoi.so.0.0.0 libatoi.so || { rm -f libatoi.so && ln -s libatoi.so.0.0.0 libatoi.so; }; })
libtool: install: install -c .libs/libatoi.lai /home/o/lib_a_so/libatoi.la
libtool: install: install -c .libs/libatoi.a /home/o/lib_a_so/libatoi.a
libtool: install: chmod 644 /home/o/lib_a_so/libatoi.a
libtool: install: ranlib /home/o/lib_a_so/libatoi.a
libtool: install: warning: remember to run `libtool --finish /home/o/lib_a_so/lib'

上面操作报了一个warning,提示我们去运行 libtool --finish /home/o/lib_a_so/lib,这个操作是使用libtool进行一个正确配置环境变量LD_LIBRARY_PATHLD_RUN_PATH等的过程。如果不能正常的使用安装好的库,运行这个命令。

ls查看一下,在当前目录生成了 libatoi.a、libatoi.so、libatoi.so.0、libatoi.so.0.0.0这几个文件。

o@Neo-kylin:~/lib_a_so$ ls
atoi.a  atoi.c  atoi.h  atoi.lo  atoi.o  libatoi.a  libatoi.la  libatoi.so  libatoi.so.0  libatoi.so.0.0.0  main  main.c

4. 使用 Libtool 库

先编译main.c文件

o@Neo-kylin:~/lib_a_so$ libtool --mode=compile gcc -c main.c
libtool: compile:  gcc -c main.c  -fPIC -DPIC -o .libs/main.o
libtool: compile:  gcc -c main.c -o main.o >/dev/null 2>&1

然后使用Libtool来进行链接操作。

o@Neo-kylin:~/lib_a_so$ libtool --mode=link gcc -o main main.lo /home/o/lib_a_so/libatoi.la
libtool: link: warning: library `/home/o/lib_a_so/libatoi.la' was moved.
libtool: link: warning: library `/home/o/lib_a_so/libatoi.la' was moved.
libtool: link: gcc -o main .libs/main.o  /home/o/lib_a_so/libatoi.so -lc  -Wl,-rpath -Wl,/home/o/lib_a_so -Wl,-rpath -Wl,/home/o/lib_a_so

从上面的输出可以看到,实际上它还是调用的gcc来进行的操作,但是它添加了选项-lc,这就是Libtool做的事情之一,它会解决依赖的问题。

o@Neo-kylin:~/lib_a_so$ ls
atoi.a  atoi.h   atoi.o     libatoi.la  libatoi.so.0      main    main.lo
atoi.c  atoi.lo  libatoi.a  libatoi.so  libatoi.so.0.0.0  main.c  main.o
o@Neo-kylin:~/lib_a_so$ ./main
o@Neo-kylin:~/lib_a_so$ echo $?
5

上面的操作默认使用的动态库,可以使用-static-libtool-libs选项来指定使用静态库。

o@Neo-kylin:~/lib_a_so$ ldd main
    linux-vdso.so.1 =>  (0x00007fffd3bff000)
    libatoi.so.0 => /home/o/lib_a_so/libatoi.so.0 (0x00007f984bdd0000)
    libc.so.6 => /lib64/libc.so.6 (0x000000305d800000)
    /lib64/ld-linux-x86-64.so.2 (0x000000305d000000)

使用-static-libtool-libs选项重新生成。

o@Neo-kylin:~/lib_a_so$ libtool --mode=link gcc -o main main.lo /home/o/lib_a_so/libatoi.la -static-libtool-libs
libtool: link: warning: library `/home/o/lib_a_so/libatoi.la' was moved.
libtool: link: warning: library `/home/o/lib_a_so/libatoi.la' was moved.
libtool: link: gcc -o main .libs/main.o  /home/o/lib_a_so/libatoi.a -lc

已经不不需要libatoi.so.0

o@Neo-kylin:~/lib_a_so$ ldd main
    linux-vdso.so.1 =>  (0x00007fff1f3ac000)
    libc.so.6 => /lib64/libc.so.6 (0x000000305d800000)
    /lib64/ld-linux-x86-64.so.2 (0x000000305d000000)

5. 卸载 Libtool 库

这是对安装操作的反操作,会删除安装的所有库文件。

o@Neo-kylin:~/lib_a_so$ libtool --mode=uninstall rm /home/o/lib_a_so/libatoi.la
libtool: uninstall: rm /home/o/lib_a_so/libatoi.la /home/o/lib_a_so/libatoi.so.0.0.0 /home/o/lib_a_so/libatoi.so.0 /home/o/lib_a_so/libatoi.so /home/o/lib_a_so/libatoi.a

Linux中的动态库和静态库(.a/.la/.so/.o)的更多相关文章

  1. Linux系统中“动态库”和“静态库”那点事儿 /etc/ld.so.conf 动态库的后缀为*.so 静态库的后缀为 libxxx.a ldconfig 目录名

    Linux系统中“动态库”和“静态库”那点事儿 /etc/ld.so.conf  动态库的后缀为*.so  静态库的后缀为 libxxx.a   ldconfig   目录名 转载自:http://b ...

  2. Linux系统中“动态库”和“静态库”那点事儿【转】

    转自:http://blog.chinaunix.net/uid-23069658-id-3142046.html 今天我们主要来说说Linux系统下基于动态库(.so)和静态(.a)的程序那些猫腻. ...

  3. Linux系统中“动态库”和“静态库”那点事儿

    摘自http://blog.chinaunix.net/uid-23069658-id-3142046.html 今天我们主要来说说Linux系统下基于动态库(.so)和静态(.a)的程序那些猫腻.在 ...

  4. Linux下Gcc生成和使用静态库和动态库详解(转)

    一.基本概念 1.1什么是库 在windows平台和linux平台下都大量存在着库. 本质上来说库是一种可执行代码的二进制形式,可以被操作系统载入内存执行. 由于windows和linux的平台不同( ...

  5. Linux下Gcc生成和使用静态库和动态库详解

    参考文章:http://blog.chinaunix.net/uid-23592843-id-223539.html 一.基本概念 1.1什么是库 在windows平台和linux平台下都大量存在着库 ...

  6. [转]Linux下用gcc/g++生成静态库和动态库(Z)

    Linux下用gcc/g++生成静态库和动态库(Z) 2012-07-24 16:45:10|  分类: linux |  标签:链接库  linux  g++  gcc  |举报|字号 订阅     ...

  7. gcc中动态库和静态库的链接顺序

    so文件:动态库a文件: 静态库exe文件:可执行程序(linux下以文件属性来标示是否是可执行文件,与后缀名无关) 经过自己写的一些测试程序,大致了解了下gcc中链接顺序问题,总结出以下几点:1,动 ...

  8. 技巧:Linux 动态库与静态库制作及使用详解

    技巧:Linux 动态库与静态库制作及使用详解 标准库的三种连接方式及静态库制作与使用方法 Linux 应用开发通常要考虑三个问题,即:1)在 Linux 应用程序开发过程中遇到过标准库链接在不同 L ...

  9. Linux的动态库与静态库

    1.动态库与静态库简介 在实际的软件开发中,为了方便使用一些被重复调用的公共代码,我们经常将这些公共的函数编译成动态库或静态库.我们知道程序一般要经过预处理.编译.汇编和链接这几个步骤才能变成可执行的 ...

随机推荐

  1. /etc/profile、/etc/bashrc、~/.bash_profile、~/.bashrc 的区别(转)

    /etc/profile:此文件为系统的每个用户设置环境信息,当用户第一次登录时,该文件被执行并从/etc/profile.d目录的配置文件中搜集shell的设置. /etc/bashrc:为每一个运 ...

  2. Java递归算法——变位字

    轮换的含义 1.c ats --> 2.ca st 3.c tsa --> 4.ct as 5.c sat --> 6.cs ta 7. atsc import java.io.Bu ...

  3. JavaScript学习笔记——基本知识

    JavaScript学习的教程来自后盾网 1>JavaScript的放置和注释 1.输出工具 A.alert(); B.document.write(); C.prompt("&quo ...

  4. 11 Clever Methods of Overfitting and how to avoid them

    11 Clever Methods of Overfitting and how to avoid them Overfitting is the bane of Data Science in th ...

  5. Cloudservice程序设置Idle timeout

    部署的云服务程序,默认的idle timeout是4分钟,意味着如果你通过一个workerrole发布了wcf服务,客户端第一次调用服务方法后,再过4分钟尝试去重新调用服务,会报错,具体测试如下: 1 ...

  6. Unity逻辑热更新

    http://www.xuanyusong.com/archives/3075 http://www.unitymanual.com/thread-36503-1-1.html http://www. ...

  7. js确定要删除吗

    js代码 function confirm_redirect(msg, url) { if (confirm(msg)) { location.href=url; } } html <a hre ...

  8. ecshop 如果缩略图为空,使用默认图片

    引用:$row['goods_img'] = get_image_path($row['goods_id'], $row['goods_img']); lib_common.php /** * 重新获 ...

  9. JavaScript正则表达式方法总结

    str.match(reg) 1.reg没有全局标志g,match将只执行一次匹配.匹配成功返回一个数组,arr = [$0,$1,$2,...,index,str],匹配失败返回null. arr中 ...

  10. QQ空间HD(4)-设置左侧菜单栏属性

    DJHomeViewController.m #import "DJHomeViewController.h" #import "DJMenuView.h" ; ...