库的动态加载
一般用于实现插件功能,可通过调用函数来加载、卸载共享库。可以查找共享库中函数名、变量名所对应的指针。
# include <dlfcn.h>
void *dlopen(const char *libfilename, int flags);
int dlclose(void *handle);
const char *dlerror(void);
void *dlsym(void *handle, char *symbol);
注意:使用共享库的动态加载,在gcc链接时,要加入 -ldl 选项。
dlopen() 打开共享库
void *dlopen(const char *libfilename, int flags);
- void *返回值:成功返回共享库句柄,失败返回NULL
- const char *libfilename:共享库名称
- 名字包含’/’:按照绝对路径或相对路径使用
- 名字不包含’/’:按照连接器默认方式搜索共享库
- int flags:选项
- RTLD_LAZY:不解析共享库中未定义函数符号的内存地址,使用时再解析
- RTLD_NOW:解析共享库库中未定义函数符号的内存地址,可用于调试
注:以上两个选项为必选选项,二选一
- RTLD_GLOBAL:这个共享库中解析的函数符号可以被后面打开的库使用
- RTLD_LOCAL:这个共享库中解析的函数符号不能被后面打开的库使用
注:以上两个选项为共享库的使用范围
- RTLD_NODELETE:不卸载共享库,即使dlclose()后引用计数为0。此时,dlopen()不会重新初始化库中静态变量(glibc2.2可用)
- RTLD_NOLOAD:不加载共享库,作用1:可用与测试共享库是否被加载,如果加载返回共享库句柄,不加载返回NULL。作用2:可再已打开的共享库上添加新的flags。(glibc2.2可用)
- RTLD_DEEPBIND:解析库中符号引用时,先搜索库中的定义,如果库中没有,再搜索全局定义。(这个很有用,可无脑加上,glibc2.3.4可用)
dlclose() 关闭共享库
int dlclose(void *handle);
- int返回值:成功返回0,失败返回非0
- void *handle:共享库句柄
dlerror() 错误原因
const char *dlerror(void);
- const char *返回值:如果dl系列函数出错,可调用dlerror()获取出错原因字符串
dlsym():获取符号的地址
void *dlsym(void *handle, char *symbol);
- void *返回值:成功返回符号的地址,失败返回NULL
- void *handle:共享库句柄
- char *symbol:符号名称(函数名、变量名)
例子
root@jing-VirtualBox:~
a.out libt.so main.c t.c
root@jing-VirtualBox:~
void f()
{
printf("lib t\n");
}
root@jing-VirtualBox:~
int main(void)
{
void *hander = dlopen("./libt.so", RTLD_LAZY);
if (hander == NULL) {
printf("%s\n", dlerror());
}
void (*f)() = dlsym(hander, "f");
f(); //调用函数f
dlclose(hander);
return 0;
}
root@jing-VirtualBox:~
root@jing-VirtualBox:~
lib t
共享库的设计
链接器版本脚本
作用:确保连接器只导出指定符号。
gcc -shared a.o b.o c.o -o libabc.so -Wl,--version-script,scriptfile.map
-Wl,–version-script,scriptfile.map:指定一个脚本,规定可以导出的符号,脚本如下
VER_1 {
global:
f_a;
f_b;
f_c;
local:
*;
};
共享库的初始化函数和终止函数
初始化函数会在共享库加载的时候自动执行,终止函数会在共享库卸载的时候自动执行,代码如下。
void __attribute__ ((constructor)) 初始化函数名(void)
void __attribute__ ((destructor)) 终止函数名(void)
注:以上均出自 Linux/UNIX系统编程手册,只选了感觉有用的。 |