dlopen动态链接库操作

void *dlopen(const char *filename, int flag);   //打开一个动态链接库,并返回动态链接库的句柄
char *dlerror(void);
void *dlsym(void *handle, const char *symbol);  //根据动态链接库操作句柄与符号,返回符号对应的地址
int dlclose(void *handle);

glibc还增加了两个POSIX标准中没有的API:

  • dladdr,从函数指针解析符号名称和所在的文件
  • dlvsym,与dlsym类似,只是多了一个版本字符串参数

初始化(initializing)和终止化(finalizing)函数

void _init(void);
void _fini(void);
  1. 当一个库通过dlopen()动态打开或以共享库的形式打开时,如果_init在该库中存在且被输出出来,则_init函数会被调用。如果一个库通过dlclose()动态关闭或因为没有应用程序引用其符号而被卸载时,_fini函数会在库卸载前被调用
  2. 当使用你自己的_init和_fini函数时,需要注意不要与系统启动文件一起链接。可以使用GCC选项 -nostartfiles 做到这一点
  3. 但是,使用上面的函数或GCC的-nostartfiles选项并不是很好的习惯,因为这可能会产生一些意外的结果。相反,库应该使用attribute函数属性来输出它的构造函数和析构函数
void __attribute__((constructor)) x_init(void)
void __attribute__((destructor)) x_fini(void)

例一:

//主程序 main.c

//申明结构体
typedef struct __test {
    int i;
    void (* echo_fun)(struct __test *p);
}Test;

//供动态库使用的注册函数
void __register(Test *p) {
    p->i = 1;
    p->echo_fun(p);
}

int main(void) {

    void *handle = NULL;
    char *myso = "./mylib.so";

    if((handle = dlopen(myso, RTLD_NOW)) == NULL) {
        printf("dlopen - %sn", dlerror());
        exit(-1);
    }

    return 0;
}
//动态库 mylib.c

//申明结构体类型
typedef struct __test {
    int i;
    void (*echo_fun)(struct __test *p);
}Test;

//申明注册函数原型
void __register(Test *p);

static void __printf(Test *p) {
    printf("i = %d
", p->i);
}

//动态库申请一个全局变量空间
//这种 ".成员"的赋值方式为c99标准
static Test config = {
    .i = 0,
    .echo_fun = __printf,
};

//加载动态库的自动初始化函数
void _init(void) {
    printf("init
");
    //调用主程序的注册函数
    __register(&config);
}
# gcc main.c -ldl -rdynamic
# gcc -shared -fPIC -nostartfiles -o mylib.so mylib.c

gcc参数 -rdynamic 用来通知链接器将所有符号添加到动态符号表中(目的是能够通过使用 dlopen 来实现向后跟踪).
gcc参数 -fPIC 作用: 当使用.so等类的库时,当遇到多个可执行文件共用这一个库时, 在内存中,这个库就不会被复制多份,让每个可执行文件一对一的使用,而是让多个可执行文件指向一个库文件,达到共用. 宗旨:节省了内存空间,提高了空间利用率

例二:

//主程序 main.c

int main(int argc, char **argv)
{
    void *handle;
    void (*callfun)();
    char *error;
    handle = dlopen("/root/tmp/hello.so",RTLD_LAZY);  //如果hello.so不是在LD_LIBRARY_PATH所申明
                                                      //的路径中必须使用全路径名
    if(!handle)
    {
        printf("%s 
",dlerror());
        exit(1);
    }
    callfun = (void(*)())dlsym(handle,"hello");    //为函数指针赋值
    if(callfun == NULL)
    {
        printf("%s 
",dlerror());
        exit(1);
    }
    callfun();                          //调用
    dlclose(handle);
//动态库 hello.c

void hello(void)
{
    printf("hello
");
}
# gcc main.c -ldl
# gcc -shared -fPIC -o hello.so hello.c

例三:

//动态链接库依赖了主程序中的symbol

int day (int a);
int life(int a)
{
    return day(a)+8;
}
int day(int a)
{
   printf("this is day function
");
   return a+2;
}

int main(int argc, char **argv) {
   void *handle;
   int (*life)(int a);
   char *error;
   handle = dlopen ("liblife.so", RTLD_LAZY);
   if (!handle) {
       fprintf (stderr, "%s
", dlerror());
       exit(1);
   }
   dlerror();    /* Clear any existing error */
   life  = dlsym(handle, "liblife.so");
   if ((error = dlerror()) != NULL)  {
       fprintf (stderr, "%s
", error);
       exit(1);
   }
   printf("%d
", life(1));
   dlclose(handle);
   return 0;
}
# gcc -shared -fpic -o liblife.so life.c
# gcc -rdynamic main.c -ldl
原文地址:https://www.cnblogs.com/zhangxuechao/p/11709975.html