编译动态链接库.so和静态链接库.a

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/lylwo317/article/details/51285404

名词介绍


  1. 静态函数库
    这类库的名字一般是libxxx.a;利用静态函数库编译成的文件比较大,因为整个 函数库的所有数据都会被整合进目标代码中,他的优点就显而易见了,即编译后的执行程序不需要外部的函数库支持,因为所有使用的函数都已经被编译进去了。当然这也会成为他的缺点,因为如果静态函数库改变了,那么你的程序必须重新编译。

  2. 动态函数库
    这类库的名字一般是libxxx.so;相对于静态函数库,动态函数库在编译的时候 并没有被编译进目标代码中,你的程序执行到相关函数时才调用该函数库里的相应函数,因此动态函数库所产生的可执行文件比较小。由于函数库没有被整合进你的程序,而是程序运行时动态的申请并调用,所以程序的运行环境中必须提供相应的库。动态函数库的改变并不影响你的程序,所以动态函数库的升级比较方便。
    linux系统有几个重要的目录存放相应的函数库,如/lib /usr/lib

静态库在程序编译时会被连接到目标代码中,程序运行时将不再需要该静态库。

动态库在程序编译时并不会被连接到目标代码中,而是在程序运行是才被载入,因此在程序运行时还需要动态库存在。

本文主要通过举例来说明在Linux中如何创建静态库和动态库,以及使用它们。

创建源文件


在创建函数库前,我们先来准备举例用的源程序,并将函数库的源程序编译成.o文件。

编辑以下几个文件

hello.c

#include<stdio.h>
void hello(const char *name){
    printf("Hello %s!\n",name);
}

hello.h

#ifndef HELLO_H
#define HELLO_H

void hello(const char *name);

#endif

main.c

#include<hello.h>
int main(){
    hello("Kevin");
    return 0;
}

编译和使用静态链接库.a


  1. 生成.o目标文件
    gcc -c hello.c

  2. 生成.a静态链接库文件(打包成一个压缩包,压缩包里面就是hello.o)
    ar cr libmyhello.a hello.o
    注:ar是用来创建,修改,和解压归档;c是ar的参数,表示创建一个归档;r是ar的参数,表示插入文件到归档里面。所以最终创建了libmyhello.o的归档,并往里面插入hello.o

    扫描二维码关注公众号,回复: 4971712 查看本文章
  3. 使用静态链接库
    gcc -o hello main.c -static -L. -lmyhello
    注:-o 输出文件名
    -static 表示链接静态链接库。如果不使用该参数,而-L指定的目录下同时拥有静态链接库和动态链接库,gcc会默认使用动态链接库。如果只有静态链接库,则会使用静态链接库。
    -L 表示添加一个目录到目录列表,使得可以被-l用来搜索。
    -l libary,当链接时,查找该library。

本质上,静态链接库就是.o文件的集合

编译和使用动态链接库.so


  1. 编译动态链接库.so
    gcc -shared -fPIC -o libmyhello.so hello.c

注:-shared 表示生成共享库(也就是动态链接库)
-fPIC 作用于编译阶段,告诉编译器产生与位置无关代码(Position-Independent Code),则产生的代码中,没有绝对地址,全部使用相对地址,故而代码可以被加载器加载到内存的任意位置,都可以正确的执行。这正是共享库所要求的。共享库被加载时,在内存的位置不是固定的。
PIC就是position independent code PIC使.so文件的代码段变为真正意义上的共享
如果不加-fPIC,则加载.so文件的代码段时,代码段引用的数据对象需要重定位,
重定位会修改代码段的内容,这就造成每个使用这个.so文件代码段的进程在内核里都会生成这个.so文件代码段的copy.每个copy都不一样,取决于这个.so文件代码段和数据段内存映射的位置.
不加fPIC编译出来的so,是要再加载时根据加载到的位置再次重定位的.(因为它里面的代码并不是位置无关代码)
如果被多个应用程序共同使用,那么它们必须每个程序维护一份so的代码副本了.(因为so被每个程序加载的位置都不同,显然这些重定位后的代码也不同,当然不能共享)
-fPIC 的使用,会生成 PIC 代码,.so 要求为 PIC,以达到动态链接的目的,否则,无法实现动态链接。 non-PIC 与 PIC 代码的区别主要在于 access global data, jump label 的不同。 比如一条 access global data
的指令, non-PIC 的形势是:ld r3, var1 PIC的形式则是:ld r3, var1-offset@GOT,意思是从
GOT 表的 index 为 var1-offset 的地方处 指示的地址处装载一个值,即var1-offset@GOT处的4个 byte
其实就是 var1 的地址。这个地址只有在运行的时候才知道,是由 dynamic-loader(ld-linux.so) 填进去的。 再比如
jump label 指令 non-PIC 的形势是:jump printf ,意思是调用 printf。 PIC 的形式则是:jump
printf-offset@GOT, 意思是跳到 GOT 表的 index 为 printf-offset 的地方处指示的地址去执行,
这个地址处的代码摆放在 .plt section,
每个外部函数对应一段这样的代码,其功能是呼叫dynamic-loader(ld-linux.so) 来查找函数的地址(本例中是 printf),然后将其地址写到 GOT 表的 index 为 printf-offset 的地方, 同时执行这个函数。这样,第2次呼叫printf 的时候,就会直接跳到 printf 的地址,而不必再查找了。 GOT 是 data section, 是一个 table,除专用的几个 entry,每个 entry 的内容可以再执行的时候修改; PLT 是 text section, 是一段一段的
code,执行中不需要修改。 每个 target 实现 PIC 的机制不同,但大同小异。比如 MIPS 没有 .plt, 而是叫
.stub,功能和 .plt 一样。 可见,动态链接执行很复杂,比静态链接执行时间长;但是,极大的节省了 size,PIC和动态链接技术是计算机发展史上非常重要的一个里程碑。

  1. 使用动态链接库
# gcc -o main main.c -L. -lmyhello
# ./main
# ./main: error while loading shared libraries: libmyhello.so: cannot open shared object file: No such file or directory

出错了。快看看错误提示,原来是找不到动态库文件libmyhello.so。程序在运行时,会在/usr/lib和/lib等目录中查找需要的动态库文件。若找到,则载入动态库,否则将提示类似上述错误而终止程序运行。我们将文件libmyhello.so复制到目录/usr/lib中,再试试。(使用”-lmyhello”标记来告诉GCC驱动程序在连接阶段引用共享函数库libmyhello.so。”-L.”标记告诉GCC函数库可能位于当前目录。否则GNU连接器会查找标准系统函数目录:它先后搜索1.elf文件的 DT_RPATH段—2.环境变量LD_LIBRARY_PATH—3./etc/ld.so.cache文件列表—4./lib/,/usr/lib目录找到库文件后将其载入内存,但是我们生成的共享库在当前文件夹下,并没有加到上述的4个路径的任何一个中,因此,执行后会出现错误)

# mv libmyhello.so /usr/lib
# ./main
# Hello Kevin!

没有报错,运行成功。

把/usr/lib/libmyhello.so删除。

第二种方法:
既然连接器会搜寻LD_LIBRARY_PATH所指定的目录,那么我们可以将这个环境变量设置成当前目录:

先执行:

export LD_LIBRARY_PATH=$(pwd)

再执行:

./main

成功!

第三种方法,就是使用ldconfig
注: 当用户在某个目录下面创建或拷贝了一个动态链接库,若想使其被系统共享,可以执行一下”ldconfig 目录名”这个命令.此命令的功能在于让ldconfig将指定目录下的动态链接库被系统共享起来,意即:在缓存文件/etc/ld.so.cache中追加进指定目录下的共享库.本例让系统共享了/home/kevin/clib目录下的动态链接库.该命令会重建/etc/ld.so.cache文件

# ldconfig /home/kevin/clib
# ./main
# Hello Kevin!

运行成功

可以查看程序执行时调用动态库的过程:

# ldd main
linux-vdso.so.1 =>  (0x00007fff88dcf000)
libmyhello.so => /usr/lib/libmyhello.so (0x00007f9104ed6000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f9104b11000)
/lib64/ld-linux-x86-64.so.2 (0x00007f9105100000)

静态库链接时搜索路径顺序:

  1. ld会去找GCC命令中的参数-L

  2. 再找gcc的环境变量LIBRARY_PATH

  3. 再找内定目录 /lib /usr/lib /usr/local/lib 这是当初compile gcc时写在程序内的

动态链接时、执行时搜索路径顺序:

  1. 编译目标代码时指定的动态库搜索路径;

  2. 环境变量LD_LIBRARY_PATH指定的动态库搜索路径;

  3. 配置文件/etc/ld.so.conf中指定的动态库搜索路径;

  4. 默认的动态库搜索路径/lib;

  5. 默认的动态库搜索路径/usr/lib。

有关环境变量:

LIBRARY_PATH环境变量:指定程序静态链接库文件搜索路径

LD_LIBRARY_PATH环境变量:指定程序动态链接库文件搜索路径

参考:http://www.cnblogs.com/laojie4321/archive/2012/03/28/2421056.html

猜你喜欢

转载自blog.csdn.net/lylwo317/article/details/51285404
今日推荐