动态加载动态链接库
在linux操作系统中,linux提供了加载和处理动态链接库的系统调用函数
函数原型
//函数所在头文件位置
#include <dlfcn.h>
//dlopen以指定模式打开指定的动态连接库文件,并返回一个句柄给调用进程
void *dlopen(const char *filename, int flag);
//dlsym通过handle句柄和连接符名称获取函数名或者变量名
void *dlsym(void *handle, const char *symbol);
//当动态链接库操作函数执行失败时,dlerror可以返回出错信息,返回值为NULL时表示没有错误信息。
char *dlerror(void);
//dlclose来卸载打开的库
int dlclose(void *handle);
函数功能
- dlopen
dlopen以指定模式打开指定的动态连接库文件,并返回一个句柄给调用进程,该句柄为 ELF 对象的句柄。filename为文件所在目录,若非系统特定目录的动态库,则必须带上目录路径,否则运行时会找不到动态库。flag为打开的模式,RTLD_NOW,它表明动态链接器将会在调用 dlopen 时完成所有必要的再定位。 RTLD_LAZY,它只在需要时执行再定位。 - dlsym
通过调用 dlsym 来识别这个handle句柄对象内的符号的地址了。可以返回动态库中的函数或全局变量。symbol为想要调用的函数名称或变量名。 - dlerror
如果调用dlopen、dlsym API 时发生了错误,dlerror 函数返回一个字符串,在没有错误发生时返回 NULL。 - dlclose
dlclose 来通知操作系统不再需要句柄和对象引用了,任何通过已关闭的对象的 dlsym 解析的符号都将不再可用。
实例
my.c
int number = 100;
int add(int a, int b)
{
return (a + b);
}
main.c
#include <dlfcn.h>
#include <stdio.h>
typedef int (*func)(int , int );
int main()
{
void *handle;
int *num = NULL;
int sum = 0;
func p;
handle = dlopen("./my.so", RTLD_LAZY);
if (!handle) {
fprintf(stderr, "%s", dlerror()); // 打开my.so失败时,输出错误信息到stderr
return -1;
}
p = (func)dlsym(handle, "add"); // 从my.so中获取到add函数
num = (int*)dlsym(handle, "number"); // 从my.so获取到number变量
sum = p(10, *num); // 执行add函数
printf("sum = %d\n", sum);
dlclose(handle); // 关闭handle
return 0;
}
makefile
all:
gcc -fPIC -shared my.c -o my.so
gcc -rdynamic -o main main.c -ldl
./main
使用gcc -fPIC -shared my.c -o my.so生成动态库
-fPIC 表示编译为位置独立的代码,不用此选项的话编译后的代码是位置相关的所以动态载入时是通过代码拷贝的方式来满足不同进程的需要,而不能达到真正代码段共享的目的。
-shared 生成动态库
gcc -rdynamic -o main main.c -ldl
-rdynamic 用来通知链接器将所有符号添加到动态符号表中,目的是能够通过使用 dlopen 来实现向后跟踪。
-ldl 表明一定要将 dllib 链接于该程序,不然编译器会报dlopen、dlsym、dlerror、dlclose等找不到