共享库的使用
一、打开共享库:dlopen()
此函数将名为libfilename的共享库加载进调用进程的虚拟地址空间并增加该库的打开引用计数。
#include<dlfcn.h>
void *dlopen(const char *libfilename,int flags);
-
libfilename包含斜杠(/),则此函数将解析成一个绝对路径或相对路径名,否则动态链接库就按照规则搜索共享库。
-
此函数成功返回一个局部,供函数调用可以使用句柄来引用这个库。发生错误,则此函数返回NULL。
- 若libfilename指定的共享库依赖其他共享库,则此函数自动加载那些库,若有必要,这一过程会递归进行。这种加载进来的库被称为这个库的依赖树。
- 在同一个库文件可以多次调用此函数,但将库加载进内存的操作只会发生一次,所有的调用都返回同样的句柄值。但会增加引用计数。
-
flags位掩码,取值是下列前两个中的一个:
RTLD_LAZY
:只有当代码被执行的时候才解析库中未定义的函数符合。RTLD_NOW
:在dlopen函数结束之前立即加载库中所有的未定义符合,不管是否需要用到这些符合,使打开库变慢,但能检测到任何未定义的函数符合。RTLD_GLOBAL
:这个库及其依赖树中的符合在解析由这个进程加载的其他库中的引用和通过dlsym函数查找。RTLD_LOCAL
:与RTLD_GLOBAL相反,若不指定任何常量,就取这个默认值。RTLD_NODELETE
:在dlclose函数调用中不要卸载库,即使其引用计数已经变为0了。RTLD_NOLOAD
:不加载库。RTLD_DEEPBIND
:在解析这个库中的符合引用时先搜索库中的定义,然后再搜索已加载的库中的定义。
libfilename
指定为NULL时dlopen函数会返回主程序的句柄。在后续dlsym函数的调用中使用这个句柄会导致首先在主程序值搜索符合,然后在程序启动时加载的共享库中进行搜索,最后在所有使用了RTLD_GLOBAL
标记的动态加载库中进行搜索。
二、错误判断dlerror函数
若在dlopen函数调用或dloen API的其他函数调用中得到了一个错误,可以使用此函数来获取一个指向表明错误原因的字符串指针。
#include<dlfcn.h>
const char *dlerror(void);
若从上一次调用dlerror函数到现在没有错误,此函数返回NULL。
三、获取符合地址:dlsym函数
此函数在handle执向的库以及该库的依赖树中的库中搜索名为symbol的符合(函数或变量)。
#include<dlfcn.h>
void *dlsym(void *handle,char *symbol);
-
若找到symbol,则此函数返回其地址,否则返回NULL。handle指上一个dlopen函数调用返回的库句柄,或者伪句柄。
-
此函数返回的符合值可能为NULL,这点与"找不到符合"的返回是无法区分的。
可以在调用dlsym函数之后dlerror函数返回一个非NULL值,就可以得出发生错误的结论了。
symbol
是变量的名称,可以将dlsym函数返回值赋给一个合适的指针类型,并通过反引用该指针来得到变量的值。
int *ip;
ip = (int *)dlsym(symol,"myvar");
if(ip !=NULL)
printf("Value is %d\n",*ip);
symbol
是函数名称,可以使用dlsym函数返回的指针来调用该函数,可以将dlsym函数返回的值存储到一个类型合适的指针中。
int (*funcp)(int);
*(void **)(&funcp) = dlsym(handle,symbol);
通过dlsym
函数得到了指向函数的指针之后就能够通过反引用函数指针来调用这个函数。
res = (*funcp)(somearg);
1.dlsym函数使用库伪句柄
-
RTLD_DEFAULT
:从主程序开始查找symbol,接着按序在所有已加载的共享库中查找,包括那些通过使用RTLD_GLOBAL
标记的dlopen函数调用动态加载的库,这个标记对应于动态链接器所采用的默认搜索模型。 -
RTLD_NEXT
:调用dlsym函数之后加载的共享库中搜索symbol,这个标记适用于需要创建与在其他地方定义的函数同名的包装函数的情况。
四、使用 dlopen API
此程序接收两个命令行参数:需加载的共享库名称和需执行的库中函数的名称。
#include <dlfcn.h>
int main(int argc, char *argv[])
{
void *libHandle; /* 共享库句柄 */
void (*funcp)(void); /* 指向不带参数的函数的指针*/
const char *err;
if (argc != 3 || strcmp(argv[1], "--help") == 0)
usageErr("%s lib-path func-name\n", argv[0]);
/*加载共享库并获取句柄以供以后使用 */
libHandle = dlopen(argv[1], RTLD_LAZY);
if (libHandle == NULL)
fatal("dlopen: %s", dlerror());
/* 在库中搜索argv[2]中命名的符号*/
(void) dlerror(); /* 清除错误() */
*(void **) (&funcp) = dlsym(libHandle, argv[2]);
err = dlerror();
if (err != NULL)
fatal("dlsym: %s", err);
/* 尝试将dlsym()返回的地址作为函数调用不需要争论 */
(*funcp)();
dlclose(libHandle); /* 关闭库*/
exit(EXIT_SUCCESS);
}
五、关闭共享库:dlclose函数
此函数用于关闭一个库。
#include<dlfcn.h>
int dlclose(void *handle);
//减小handle所引用的库的打开引用的系统计数。
//计数变为0并且其他库已经不需要用到该库中的符合了,会卸载这个库。
六、获取与加载的符合相关的信息:dladdr函数
此函数返回一个包含地址addr的相关信息的地址。
#include<dlfcn.h>
int dladdr(cosnt void *addr,D1_info *info);
//info指向由调用者分配的结构的指针:
typedef struct{
const char *dli_fname;//路径名
void *dli_fbase;//运行时基地地址
//返回地址相关信息
const char *dli_sname;
void *dli_saddr;
};D1_info;