一、so文件介绍
so文件是一种在Linux系统中被广泛使用的共享库文件。共享库的本质是为了避免代码重复,将公共的部分集中在一起,以达到节省空间和运行时间的目的。因此,so文件在Linux系统中有着举足轻重的地位。
所谓共享库,就是在库的使用者和库的实现者之间,共享一个动态链接库文件,而不是每个库都拥有一个独立的库文件。共享库采用动态加载的方式,在程序运行时运行时载入内存中。它的 VMA 范围可以是进程映像中的一个共享段。
二、so文件的创建
创建so文件的方式有很多,常用的有两种:静态链接和动态链接。
1、静态链接
静态链接是指在编译时将库文件进行链接的过程。使用静态链接的优点是对于使用者来说不需要依赖库文件,同时也可以更好地保证程序的稳定性;缺点是会占用更多的空间。
gcc -o main main.c libtest.a
2、动态链接
动态链接是指在程序运行时动态加载库文件的过程。使用动态链接的优点是可以减小程序文件大小,同时对库的更新也比较友好;缺点是需要依赖库文件的存在,如果库文件被删除或者移动,程序将无法正常运行。
gcc -o main main.c -L. -ltest
三、so文件加载
so文件的加载分为两种方式:显式加载和隐式加载。
1、显式加载
显式加载是指使用dlopen()函数加载动态链接库。dlopen()函数是以一种独立于编译时链接的方式加载一个共享库。dlopen()函数支持的选项有RTLD_NOW、RTLD_LAZY等。其中RTLD_NOW立即进行符号解析,并在打开库时对它们立即进行重定位;而RTLD_LAZY则进行符号解析,但仅在使用这些符号时才对它们进行重定位。
void *handle = dlopen("libtest.so", RTLD_NOW);
if (!handle) {
fprintf(stderr, "dlopen error: %sn", dlerror());
exit(EXIT_FAILURE);
}
2、隐式加载
隐式加载是指在程序运行时,动态链接器根据程序的需要在系统默认路径中查找并加载共享库文件。在程序内部并没有显式使用dlopen()函数加载共享库,而是通过一些约定俗成的标准库函数,如dlsym()函数,对共享库中的函数进行调用。
void *handle = dlopen(NULL, RTLD_NOW);
if (!handle) {
fprintf(stderr, "dlopen error: %sn", dlerror());
exit(EXIT_FAILURE);
}
int (*add)(int,int);
add = dlsym(handle, "add");
printf("%dn", (*add)(1, 2));
dlclose(handle);
四、so文件的重定位
so文件在加载时需要进行重定位,这是因为代码中引用的函数和变量地址都是相对于模块基地址的偏移量,而实际地址并未确定。重定位就是将这些地址确定下来。
在so文件中,重定位分为两种:PLT重定位和 GOT重定位。
1、PLT重定位
PLT(Global Procedure Linkage Table)是为了解决函数调用延迟绑定问题而生的。PLT中存放的是对函数局部描述符PLT项的偏移量,PLT项就是程序对相应函数调用的入口。当程序第一次对函数进行调用时,PLT项会被执行,并把函数真实地址填入GOT表中。当程序下次调用该函数时,可以直接从GOT表中获取函数的地址,而不需要再执行PLT项。
2、GOT重定位
GOT(Global Offset Table)存放全局变量在程序中的地址。GOT中存放的是相应函数实际地址的指针,因为函数的实际地址要到重定位时才可以确定。未进行重定位的这些指针中都存放着一个固定的值,因此GOT表中的值是可以被修改的。
五、so文件的调试
so文件可以通过gdb来进行调试。
gdb ./program (gdb) set libpath /path/to/libso (gdb) run
六、so文件的使用
使用so文件的方式也有很多种,常用的有两种:使用LD_PRELOAD环境变量和使用dlopen()函数。
1、LD_PRELOAD环境变量
使用LD_PRELOAD环境变量可以在不修改已有程序源代码的情况下,加载一个新的共享库。
$ export LD_PRELOAD=/path/to/so $ ./program
2、dlopen()函数
dlopen()函数也可以在程序运行时加载共享库。
void *handle = dlopen("/path/to/so", RTLD_NOW);
if (!handle) {
fprintf(stderr, "dlopen error: %sn", dlerror());
exit(EXIT_FAILURE);
}
七、so文件使用注意事项
使用so文件时需要注意以下问题:
1、so文件的版本必须和应用程序的版本匹配;
2、so文件一旦使用,就必须确保它可以长期地保持稳定;
3、动态加载so文件时,需要保证共享库文件的存在,否则会导致程序无法正常运行;
4、当程序中出现多个so文件时,需要注意so文件之间的依赖关系,确保依赖关系正确,否则会导致程序无法正常运行。
