这里首先先说明一下装载时重定位的问题,一个程序如果没有用到任何动态库,由于已经知道了所有的代码,那么装载器在把程序载入内存的过程中就可以直接安装静态库在链接的时候定好的代码段位置直接加载进内存中的对应位置就可以了。
但是在面对动态的库的时候 ,这种方式就不行了。假设需要载入共享库A,但是在编译链接的时候使用的共享库和最后运行的不一定是同一个库,在编译期就没办法知道具体的库长度,在链接的时候就没办法确定它或者其他动态库的具体位置。另一个方面动态库中也会用到一些全局的符号,这些符号可能是来自其他的动态库,这在编译器是没办法确定的 (如果可以确定那就全是静态库了)
基于上面的原因,就要求在载入动态库的时候对于使用到的符号地址实现重定位。在实现上在编译链接的时候不做重定位操作,地址都采用相对地址,一但到了需要载入的时候,根据相对地址的偏移计算出最后的绝对地址载入内存中。
/*
共享库的创建比较简单,基本有两步。首先使用-fPIC或-fpic创建目标文件,PIC或 pic表示位置无关代码。所谓的位置无关,是指共享库载入到内存中时,不需要在特定的地址执行,地址都采用相对地址。
*/
# gcc -fPIC -c a.c
# gcc -fPIC -c b.c
# gcc -shared -Wl -o libmyab.so a.o b.o
gcc -shared -Wl,-soname,libmyab.so.1 -o libmyab.so.1.0.1 a.o b.o 命令解析:
按照共享库的命名惯例,每个共享库有三个文件名:real name、soname和linker name。真正的库文件(而不是符号链接)的名字是real name,包含完整的共享库版本号。
soname是一个符号链接的名字,只包含共享库的主版本号,主版本号一致即可保证库函数的接口一致,因此应用程序的.dynamic段只记录共享库的soname,只要soname一致,这个 共享库就可以用。如libmyab.so.1和libmyab.so.2是两个主版本号不同的libmyab,有些应用程序依赖于libmyab.so.1,有些应用程序依赖于libmyab.so.2,但对于依赖libmyab.so.1 的应用程序来说,真正的库文件不管是libmyab.so.1.10还是libmyab.so.1.11都可以用,所 以使用共享库可以很方便地升级库文件而不需要重新编译应用程序,这是静态库所没有的优点。注意libc的版本编号有一点特殊,libc-2.8.90.so的主版本号是6而不是2或2.8。
linker name仅在编译链接时使用,gcc的-L选项应该指定linker name所在的目录。有的linker name是库文件的一个符号链接,有的linker name是一段链接脚本。
- 演示
包含的文件:
# ls
add.c common.h dive.c mul.c sub.c
//add.c
1
2 int add(int a,int b){
3
4 return a+b;
5 }
//sub.c
1
2
3 int sub(int a,int b){
4
5 return a-b;
6 }
//dive.c
1
2
3 float dive(int a,int b){
4
5 return a/(float)b;
6 }
//mul.c
1
2
3 int mul(int a,int b){
4
5 return a*b;
6 }
头文件
//common.h
1 #ifndef COMMON_H_
2 #define COMMON_H_
3 int add(int a,int b);
4 int sub(int a,int b);
5 int dive(int a,int b);
6 int mul(int a,int b);
7 #endif
生成共享库
//生成.o文件
#gcc -fPIC -c *.c
#ls
add.c common.h dive.o mul.c sub.c
add.o dive.c libmycal.so.1.10 mul.o sub.o
//生成共享库
#gcc -shared -Wl,-soname,libmycal.so.1 -o libmycal.so.1.10 add.o sub.o dive.o mul.o
#ls
add.c common.h dive.o main.c mul.o sub.o
add.o dive.c libmycal.so.1.10 mul.c sub.c
使用共享库
main文件
//main.c
1 #include <stdio.h>
2 #include "common.h"
3
4 int main(void){
5
6 printf("%d\n",add(5,4));
7 return 0;
8 }
#gcc main.c libmycal.so.1.10 -o app
#./app
./app: error while loading shared libraries: libmycal.so.1: cannot open shared object file: No such file or directory
//因为当前目录没有在系统路径中,所以无法找到该共享库
//查看程序运行时依赖的库
#ldd app
linux-vdso.so.1 => (0x00007ffdb8ba1000)
libmycal.so.1 => not found
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f3a2f30d000)
/lib64/ld-linux-x86-64.so.2 (0x00005598e8e3c000)
共享库加载
在所有基于GNUglibc的系统中,在启动一个ELF二进制执行程序时,一个特殊的程序“程序装载器”会被自动装载并运行。在linux中,这个程序装载器就是/lib/ld- linux.so.X(X是版本号)。它会查找并装载应用程序所依赖的所有共享库。被搜索的目录保存在/etc/ld.so.conf文件中。当然,如果程序的每次启动,都要去搜索一番,势必效率不堪忍受。Linux系统已经考虑这一点,对共享库采用了缓存管理。
ldconfig就是实现这一功能的工具,其缺省读取/etc/ld.so.conf文件,对所有共享库按照一定规范建立符号连接,然后将信息写入/etc/ld.so.cache。 /etc/ld.so.cache的存在大大加快了程序的启动速度。
1. 修改/etc/ld.so.conf(添加你的共享库路径)
#sudo vim /etc/ld.so.conf
include /etc/ld.so.conf.d/*.conf
/home/liyongfeng/code/(添加的路径)
2. 更新查找共享库的路径
#sudo ldconfig -v
3.测试你的程序可否找到共享库
#ldd app
linux-vdso.so.1 => (0x00007ffffcfb2000)
libmycal.so.1 => /home/liyongfeng/code/libmycal.so.1 (0x00007fe264479000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fe2640b0000)
/lib64/ld-linux-x86-64.so.2 (0x000055bc9c033000)
执行程序
#./app
9
关于共享库名
成功链接共享库后,可以发现该目录下多了一个符号链接文件libmycal.so.1,该文件就是前面命令解析中的soname,而libmycal.so.1.10即为real name。
#ls -l
total 60
-rw-rw-r-- 1 liyongfeng liyongfeng 39 Jun 13 00:03 add.c
-rw-rw-r-- 1 liyongfeng liyongfeng 1240 Jun 13 00:25 add.o
-rwxrwxr-x 1 liyongfeng liyongfeng 8632 Jun 13 00:43 app
-rw-rw-r-- 1 liyongfeng liyongfeng 132 Jun 13 00:19 common.h
-rw-rw-r-- 1 liyongfeng liyongfeng 50 Jun 13 00:08 dive.c
-rw-rw-r-- 1 liyongfeng liyongfeng 1256 Jun 13 00:25 dive.o
lrwxrwxrwx 1 root root 16 Jun 13 00:56 libmycal.so.1 -> libmycal.so.1.10
-rwxrwxr-x 1 liyongfeng liyongfeng 8040 Jun 13 00:29 libmycal.so.1.10
-rw-rw-r-- 1 liyongfeng liyongfeng 94 Jun 13 00:22 main.c
-rw-rw-r-- 1 liyongfeng liyongfeng 40 Jun 13 00:05 mul.c
-rw-rw-r-- 1 liyongfeng liyongfeng 1240 Jun 13 00:25 mul.o
-rw-rw-r-- 1 liyongfeng liyongfeng 40 Jun 13 00:04 sub.c
-rw-rw-r-- 1 liyongfeng liyongfeng 1240 Jun 13 00:25 sub.o
创建linker name (符号链接)
#ln -s libmycal.so.1.10 libmycal.so
#ls -l
total 60
-rw-rw-r-- 1 liyongfeng liyongfeng 39 Jun 13 00:03 add.c
-rw-rw-r-- 1 liyongfeng liyongfeng 1240 Jun 13 00:25 add.o
-rwxrwxr-x 1 liyongfeng liyongfeng 8632 Jun 13 00:43 app
-rw-rw-r-- 1 liyongfeng liyongfeng 132 Jun 13 00:19 common.h
-rw-rw-r-- 1 liyongfeng liyongfeng 50 Jun 13 00:08 dive.c
-rw-rw-r-- 1 liyongfeng liyongfeng 1256 Jun 13 00:25 dive.o
lrwxrwxrwx 1 liyongfeng liyongfeng 16 Jun 13 01:09 libmycal.so -> libmycal.so.1.10
lrwxrwxrwx 1 root root 16 Jun 13 00:56 libmycal.so.1 -> libmycal.so.1.10
-rwxrwxr-x 1 liyongfeng liyongfeng 8040 Jun 13 00:29 libmycal.so.1.10
-rw-rw-r-- 1 liyongfeng liyongfeng 94 Jun 13 00:22 main.c
-rw-rw-r-- 1 liyongfeng liyongfeng 40 Jun 13 00:05 mul.c
-rw-rw-r-- 1 liyongfeng liyongfeng 1240 Jun 13 00:25 mul.o
-rw-rw-r-- 1 liyongfeng liyongfeng 40 Jun 13 00:04 sub.c
-rw-rw-r-- 1 liyongfeng liyongfeng 1240 Jun 13 00:25 sub.o