共享库基础
目标库
构建程序得到一种简单方式是间每一个源文件编译成目标文件,然后将这些目标文件链接在一起形成可执行文件
使用gcc的 -c
选项即可将.c文件编译成.o文件
链接的实际过程实际上是由一个单独的链接器ld来完成的,不过在Linux中应该总是使用gcc来间接地调用ld,因为gcc能够确保以正确的顺序来调用ld
对象库
在很多情况下,源代码会被多个程序共享,此时可以将他们编译一次,然后在需要的时候将他们链接进入不同的可执行文件中,一个.c文件就会生成一个.o目标文件,大量的目标文件势必会造成组织上的混乱,因此,对象库产生了,对象库是一组目标文件的集合,以一个文件表示
对象库分为两种:静态共享库,动态共享库,下面进行介绍
静态库
静态库也被称为归档文件(archive),它有以下的优点:
- 可以将一组经常被用到的目标文件组织进单个库文件,这样就可以使用它来构建多个可执行文件而无需重新编译
- 链接命令更加简单,无需列出多个目标文件,只需要列出一个静态库文件即可
静态库的名称常常为lib+name+.a
使用ar指令创建并维护静态库
静态库中不仅包含着各个目标文件的副本,并且还记录了这些文件的一些特性,比如文件权限,uid, gid等
-
创建静态库
ar options archive p1.o p2.o ..
options选项十分丰富,这里只介绍一些基本选项,注意这些选项不需要加上
-
-
r(替换)
将一个目标文件插入到静态库中并替换同名文件(如果静态库不存在则自动创建)
-
t(目录表)
显式静态库中的目录表,在默认情况下会列出名称,可以加上v选项来提供更多信息
-
d(删除)
从静态库中删除一个目标文件
-
-
gcc静态库的链接
使用gcc连接静态库有3种方式:
-
直接在命令中指定静态库的名称
gcc -o prog prog.o libdemo.a
-
将静态库放在标准目录/usr/lib下,然后使用
-l
选项指定库名,这样在链接时就会自动在/usr/lib/目录下进行寻找mv libdemo.a /usr/lib gcc -o prog prog.o -ldemo
注意这里-l选项后面紧跟的只有name,而不是静态库的全名
-
将静态库放在任意目录,在链接时使用
-L
选项将其指出即可mv libdemo.a /dir gcc -o prog prog.o -Ldir -ldemo //-l选项不要漏掉
-
共享库
静态库的缺点
-
空间浪费
在使用静态链接时,所有可执行文件中都会包含一份被链接进程序的目标文件的副本,由此造成了大量的空间浪费(磁盘空间和内存空间)
-
更新困难
如果需要修改静态库中的一个目标模块,那么所有使用了哪个目标模块的文件都必须重新连接该静态库,十分麻烦
共享库
-
关键思想
共享库的关键思想是目标模块的单个副本由所有的这些模块共享,当第一个需要共享库中的某个模块运行时,共享库的单个副本就会被加载进内存中,如果还有其他程序需要使用该共享库,那么它们会在内存中共享该共享库
共享库完美解决了静态库的缺点,首先是空间浪费,显而易见,由于共享机制,空间浪费大大减少,其次,当需要升级程序共享的某个模块时,理论上只需要将旧的目标文件覆盖掉就行,而不必要进行应用程序的重新连接,在进行下一次程序运行时,新的目标文件会被自动装载到内存中并连接起来,这样就完成了升级
创建和使用共享库
创建共享库
gcc -c -fPIC mod{1,2,3}.c
gcc -shared -o libfoo.so mod{1,2,3}.o
使用-shared选项来创建共享库,依据惯例共享库的命名为lib+name+.so
位置无关代码
使用-fPIC选项时可以创建位置无关程序,具体可以看我的这篇文章[位置无关代码]((56条消息) 位置无关代码_永远的纸条的博客-CSDN博客)
简单来说就是不使用-fPIC选项时**包含依赖于位置的内存引用的程序文本页面**将不会共享
使用-fPIC选项时会产生位置无关代码,这样只会有个别指令被复制,大部分的程序文本依旧会被共享
确定已有目标文件在编译时是否使用了-fPIC选项
nm mod1.o | grep _GLOBAL_OFFSET_TABLE //这个_GLOBAL_OFFSET_TABLE即GOT表
readelf -s mod1.o | grep _GLOBAL_OFFSET_TABLE
有输出则为位置无关代码
objdump --all-headers libfoo.so | grep TEXREL
readelf -d libfoo.so | grep TEXTREL
有输出则不是位置无关代码
TEXTREL表示存在一个目标模块,其文本段中包含需要进行重定位的引用(感觉翻译的挺烂…,原文如下)
使用共享库
直接链接即可
gcc -o prog prog.c libfoo.so
需要注意的是,链接过程由动态链接器完成,动态链接器本身也是一个动态链接库,名称为/lib/ld-linux.so.2
(各个版本可能会有差异)
动态链接器会检查系统中的库文件,看其中有没有命令中的动态链接库,所以在执行过程中如果发现了链接出错的问题,则需要修改环境变量
LD_LIBRARY_PATH环境变量
通过将指定工作目录添加到该环境变量中就能够让动态链接器在该目录下查找所需的动态链接库
LD_LIBRARY_PATH=. ./prog //将环境变量指定为当前目录,然后执行prog程序