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

2023-05-16

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

原文地址:https://www.cnblogs.com/findumars/p/5421910.html

在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 1010 15:11 ./
drwxrwxr-x 5 1000 1000 4096 1010 14:32 ../
-rw-rw-r-- 1 o    o     140 1010 15:07 atoi.c
-rw-rw-r-- 1 o    o      75 1010 15:07 atoi.h
-rw-rw-r-- 1 o    o    1536 1010 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 1010 15:35 ./
drwxrwxr-x 5 1000 1000 4096 1010 14:32 ../
-rw-rw-r-- 1 o    o    1678 1010 15:35 atoi.a
-rw-rw-r-- 1 o    o     140 1010 15:07 atoi.c
-rw-rw-r-- 1 o    o      75 1010 15:07 atoi.h
-rw-rw-r-- 1 o    o    1536 1010 15:11 atoi.o

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

说明:上面的参数也可以是ar -cr atoi.a atoi.o

-c的意思是只编译不链接

/******下面的是我参考博主学习的时候的 ************开始*
注意:
1.这里一定要是从.o 文件编译成.a文件,我自己在学习操作的时候直接从.c编译的,虽然编译成功了,也没有报错,但是在链接的时候报错了
这里写图片描述

在网上找了好久才把问题解决

  • [ ] 2.所要生成的.a文件的名字前三位最好是lib,否则在链接的时候,就可能导致找不到这个库

    *下面的是我参考博主学习的时候的********结束************/

使用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,与预期一致。

/*下面的是我参考博主学习的时候的 *开始************

补充说明:

1.一般的静态库可以放在和工程里面,也可以放在linux为用户准备的目录下/user/local/lib下。这样就不需要链接绝对路径

2.如果.a文件不再指定位置(/user/lib /uer/local/lib ./)加绝对路径的时候就不需要 -L. 和-l参数了,需要指定路径和文件名的全称
这里写图片描述

3.参数含义:

-L.:表示要连接的库在当前目录中
-lmd5.a:编译器查找动态连接库时有隐含的命名规则,即在给出的名字前面加上lib,后
面加上.so来确定库的名称

4.所以通过上面的解释,那么编译的时候直接使用当前文件下的.a文件(前提是.a文件在)应该也是可以的
这里写图片描述

果然可以,执行程序,结果正确

2.所要生成的.a文件的名字前三位最好是lib,否则在链接的时候,就可能导致找不到这个库

******下面的是我参考博主学习的时候的****结束*******************/

### 使用atoi.a


.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的路径,如果都没有,那么就会报错啦。

这里应该也可以链接用户目录下的/user/local/lib64文件下的.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 1010 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等来使用,这里就不介绍了。

/**************下面的是我参考博主学习的时候的 ********************开始**********************************************************************************

1.前面的不加-wl,-rpath参数时和博主的现象一样,加了参数后正常,得到正确的结果了

这里写图片描述

思考:

a.如果该.so文件不在当前路径下是否可以指定路径后正确编译?

这里写图片描述

可以看到这样是可以的,注意-Wl中的w是大写

如果动态库在当前路径下,是否可以直接用gcc编译,而不需要指定路径
这里写图片描述

也是可以的

b.是否可以在编译的时候就加上-wl,-rpath参数,然后后面链接的时候就不需要再加上参数了?
这里写图片描述

看来是不行

c.是否可以将.so文件放在/user/local/lib目录下,效果和放在当前目录下的是一样的?

我的环境是centos7,并不可以

***下面的是我参考博主学习的时候的**********************结束*************************************************************************************/

下面的内容也分享下


.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

http://www.cnblogs.com/oloroso/p/4874801.html

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

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

  • 如何在 Linux 中使用单行命令获取 Java 版本

    我想通过单个命令获取 Linux 中的 Java 版本 我是 awk 的新手 所以我正在尝试类似的事情 java version awk print 3 但这不会返回版本 我将如何获取1 6 0 21从下面的Java版本输出 java ve
  • grails 上的同步块在 Windows 上有效,但在 Linux 上无效

    我有一个 grails 应用程序 它依赖于服务中的同步块 当我在 Windows 上运行它时 同步按预期工作 但当我在 ams linux 上运行时 会出现 StaleObjectStateException 该问题在以下示例中重现 cla
  • linux下如何获取昨天和前天?

    我想在变量中获取 sysdate 1 和 sysdate 2 并回显它 我正在使用下面的查询 它将今天的日期作为输出 bin bash tm date Y d m echo tm 如何获取昨天和前天的日期 这是另一种方法 对于昨天来说 da
  • Bash 脚本 - 迭代 find 的输出

    我有一个 bash 脚本 其中需要迭代 find 命令输出的每一行 但似乎我正在迭代 find 命令中的每个单词 以空格分隔 到目前为止我的脚本看起来像这样 folders find maxdepth 1 type d for i in f
  • 在 shell 脚本中查找和替换

    是否可以使用 shell 在文件中搜索然后替换值 当我安装服务时 我希望能够在配置文件中搜索变量 然后在该值中替换 插入我自己的设置 当然 您可以使用 sed 或 awk 来完成此操作 sed 示例 sed i s Andrew James
  • 如何从 Linux 命令行获取视频文件的分辨率(宽度和高度)?

    我一直在挖掘 mplayer mencoder 和 ffmpeg 文档 但我似乎无法想出anything 我对输出格式不是特别挑剔 因为我可以使用正则表达式将其拉出来 我只是似乎无法首先获取数据 Use ffprobe https ffmp
  • 套接字:监听积压并接受

    listen sock backlog 在我看来 参数backlog限制连接数量 这是我的测试代码 server initialize the sockaddr of server server sin family AF INET ser
  • 如何调用位于其他目录的Makefile?

    我正在尝试这样做 我想打电话给 make Makefile存在于其他目录中 abc可以使用位于不同目录中的 shell 脚本的路径 我该怎么做呢 由于 shell 脚本不允许我cd进入Makefile目录并执行make 我怎样才能编写she
  • 如何成功使用RDAP协议代替whois

    我对新的 RDAP 协议有点困惑 也不知道何时进一步追求它有意义 在我看来 每个人都同意它是 whois 的继承者 但他们的数据库似乎是空的 在 ubuntu 上我尝试了 rdapper nicinfo 甚至他们的 RESTful API
  • 如何用X11复制到剪贴板?

    使用 OS X 上的框架 我可以使用以下命令将 PNG 复制到粘贴板 在 C 中 显然我可以将 NSPasteboard 与 Cocoa 一起使用 include
  • 如何在我的 AWS EC2 实例上安装特定字体?

    我有一个在 AWS EC2 Amazon Linux Elastic Beanstalk 实例上运行的 Python 应用程序 该实例需要某些特定字体才能生成输出 并且想知道如何在部署或实例启动过程中安装它们 我的代码在本地计算机 OS X
  • InstaPy:“错误,无法确定 64 位 Linux 的正确文件名”

    有人知道如何解决或解决这个问题吗 来自控制台的堆栈跟踪 执行后报告错误 InstaPy Version 0 6 9 Workspace in use home zanettra InstaPy Error unable to determi
  • 无法安装 WWW::Curl::Easy: SZBALINT/WWW-Curl-4.17.tar.gz : make NO

    我正在尝试在我的 Fedora 26 机器上安装 WWW Curl Easy gcc c I usr include D REENTRANT D GNU SOURCE O2 g pipe Wall Werror format securit
  • Linux 中热插拔设备时检测设备是否存在

    我正在运行 SPIcode http lxr free electrons com source drivers spi spi omap2 mcspi c在熊猫板上 我想知道其中的哪个功能code http lxr free electr
  • 如何不断刷新屏幕并实时更新[关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 我想在linux上写一个C程序 不断刷新屏幕并实时更新 例如类似于top终端中的命令 谁能指出我正确的方向 为了保持它跨终端类型的可移
  • 如何在Python中独立于语言安装(linux)获取用户桌面路径

    我找到了 如何找到用户桌面的路径 的几个问题和答案 但在我看来它们都已失效 至少我找到的那些 原因是 如果用户安装的 Linux 不是英语 他或她的桌面很可能位于除 Desktop 例如 对于瑞典语 我相信它是在 Skrivbord 谁知道
  • 在 Ubuntu 16.04 上找不到 printf.c

    我最近切换到Ubuntu 16 04 我在用vscode作为 Ubuntu 上的 IDE 我配置了其他语言 但我无法做到这一点C C 我创建c cpp properties json launch json tasks json 当我开始编
  • 如何让“grep”从文件中读取模式?

    假设有一个很大的文本文件 我只想打印与某些模式不匹配的行 显然 我可以使用egrep v patter1 pattern2 pattern3 现在 如果所有这些模式都在一个文本文件中怎么办 最好的制作方法是什么egrep从文件中读取模式 g
  • 从 TypeScript 运行任何 Linux 终端命令?

    有没有办法直接从 TypeScript 类中执行 Linux 终端命令 这个想法是做类似的事情 let myTerminal new LinuxTerminal let terminalResult myTerminal run sudo
  • 适用于 KDE 和 Gnome 的 Gui [重复]

    这个问题在这里已经有答案了 我想为一个现在是 CLI 的应用程序编写一个 gui 它需要在 KDE 和 Gnome DE 中 看起来不错 充分利用用户的外观设置 如果我选择 Qt 或 GTK 我能够做到这一点吗 它们与两个 DE 集成良好吗

随机推荐

  • Linux中模拟GET、POST请求

    1 概述 在Linux系统中 xff0c 可以利用命令来模拟HTTP请求中的GET POST PUT等请求 xff0c 本文将阐述基于curl命令来模拟GET与POST请求 xff0c PUT DELETE等请求与POST类似 xff0c
  • yolo自带标注工具yolo_mark下载及使用说明

    官网写的比较详细 xff0c 下载参考 https github com AlexeyAB Yolo mark 双击运行windows命令脚本 xff0c 而不是exe 将要标注的样本路径 xff0c 写入train txt文件中 上面这个
  • C++语法(二十)常函数、常对象

    1 常函数 常函数无法修改成员变量 xff0c 除非这个成员变量用mutable修饰了 include lt iostream gt using namespace std class Person public void change c
  • rplidar_ros 报错:can‘t bind 和Operation Time Out的解决

    我使用的思岚A2的雷达在ros下运行 1 can t bind无法连接的错误 xff0c 一种是设备号不匹配引起的错误 xff0c 首先可以使用ll dev grep ttyUSB查看一下设备的dev号 xff0c 再检查一下rplidar
  • 串口通信与网络通信

    上一篇文章记录了使用C Winform开发串口通讯的上位机软件 xff0c 而笔者在整个职业经历中开发得较多的还是网络通讯软件 xff0c 通过以太网TCP IP UDP协议实现不同服务器应用程序之间数据传送与接收 xff1b 而随着公司业
  • Unity URP自学笔记四 ShaderGraph

    在ShaderGraph中自定义光照计算 xff0c 主要需要获取光照的颜色和方向 xff0c 这些需要自己通过脚本来获取 例如通过CustomFunction结点来处理 xff1a 下面创建了一个半兰伯特SubGraph xff0c 便于
  • [笔记]STM32基于HAL库实现STM32串口中断接收数据

    这里使用USART1串口 usart c中添加 xff08 1 xff09 添加全局变量 uint8 t USART1 Buff 100 61 0 接收帧缓存 xff0c 自己定义大小 uint8 t USART1 STA 61 0 boo
  • matlab--UDP发送接收

    m函数中UDP接收和发送 接收 ipA 192 168 0 5 portA 8080 ipB 192 168 0 3 portB 8080 handles udpB udp ipA portA LocalPort portB 远程ip 远程
  • mysql--日志

    转载自 xff1a https www cnblogs com f ck need u p 9001061 html 日志刷新 mysql gt FLUSH LOGS 错误日志 简介 错误日志记录了MySQL Server每次启动和关闭的详
  • osg--读写

    文件I O 命名规则 osgdb xxx 比如 osgdb osg osgdb jpeg 关联文件后缀和加载器 osgDB Registry instance gt addFileExtensionAlias jpeg jpeg osgDB
  • osg--几种效果

    billboards 适用于小草等的绘制 osg BillBoard继承自osg Geode 其下所有osg Drawable面向观察者 旋转行为通过setMode 设置 分别为 POINT ROT EYE 几何体z轴旋转到窗口y轴 POI
  • osg--提高效率

    多线程 OpenThreads Thread 虚函数 cancel run OpenThreads Mutex OpenThreads Barrier OpenThreads Condition 线程管理 GetNumberOfProces
  • torch在ubuntu16.04下的搭建(cuda9.0+cudnn7.0)

    希望外婆身体越来越好 参考 xff1a http blog csdn net chenhaifeng2016 article details 68957732 http www 52nlp cn E6 B7 B1 E5 BA A6 E5 A
  • LSTM文本分类(tensorflow)

    1 xff09 LSTM介绍 转载自https www csdn net article 2015 09 14 2825693 Gates xff1a 输入变换 xff1a 状态更新 xff1a 使用图片描述类似下图 xff1a 输入 首先
  • ArcMap安装与使用入门

    一 安装 https malagis com arcgis 10 4 full ios download html from 61 singlemessage amp isappinstalled 61 0 二 使用 1 添加数据 2 新建
  • srtm数据格式.hgt读取

    srtm数据格式 hgt读取 转载自https librenepal com article reading srtm data with python python读取 import os import json import numpy
  • Unity Pahfinding 插件中直接用RVOController来移动角色

    span class token keyword using span UnityEngine span class token punctuation span span class token keyword using span Sy
  • gdal用法总结

    USAGE OF GDAL RASTER API Import gdal from osgeo import gdal Open the file Dataset gdal Open filename Getting dataset inf
  • C语言中的__FILE__、__LINE__和__func__等预定义跟踪调试

    标准C语言预处理要求定义某些对象宏 xff0c 每个预定义宏的名称一两个下划线字符开头和结尾 xff0c 这些预定义宏不能被取消定义 xff08 undef xff09 或由编程人员重新定义 下面预定义宏表 xff0c 被我抄了下来 LIN
  • Linux中的动态库和静态库(.a.la.so.o)

    Linux中的动态库和静态库 a la so o 原文地址 xff1a https www cnblogs com findumars p 5421910 html 在windows下 xff0c 一般可以通过文件的后缀名来识别文件的类型