动态库的两种调用方式_dlopen函数

动态库的两种调用方式_dlopen函数一、函数说明 #include <dlfcn.h> void *dlopen(const char *filename, int flag);//dlopen用于打开指定名字(filename)的动态链接库,并返回操作句柄 void *dlsym(void *handle, const

动态库的两种调用方式_dlopen函数"

一、函数说明

#include <dlfcn.h>

void *dlopen(const char *filename, int flag);
//dlopen用于打开指定名字(filename)的动态链接库,并返回操作句柄
void *dlsym(void *handle, const char *symbol);
//根据动态链接库操作句柄与符号,返回符号对应的地址。使用这个函数不但可以获取函数地址,也可以获取变量地址
。handle是由dlopen打开动态链接库后返回的指针,symbol就是要求获取的函数或全局变量的名称.

int dlclose(void *handle);
//用于关闭指定句柄的动态链接库,只有当此动态链接库的使用计数为0时,才会真正被系统卸载。
char *dlerror(void);
//当动态链接库操作函数执行失败时,dlerror可以返回出错信息,返回值为NULL时表示操作函数执行成功。

 

二、生成动态链接库

caculate.c

gcc -fPIC -shared caculate.c -o libcaculate.so

int add(int a, int b)
{
       return (a + b);
}

 

三、动态链接库的加载

dltest.c

gcc -rdynamic -o main dltest.c -ldl

-ldl表示:显示加载动态库libdl.so

#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>

#define LIB_CACULATE_PATH "./libcaculate.so"
typedef int (*CAC_FUNC) (int, int);

int main()
{
    void *handle;
    char *error;
    CAC_FUNC cac_func = NULL;

//打开动态链接库 handle
= dlopen(LIB_CACULATE_PATH, RTLD_LAZY); if (!handle) { fprintf(stderr, "%s\n", dlerror()); exit(EXIT_FAILURE); }

//清除之前存在的错误
dlerror();
//获取一个函数
*(void **) (&cac_func) = dlsym(handle, "add"); if ((error = dlerror()) != NULL) { fprintf(stderr, "%s\n", error); exit(EXIT_FAILURE); } printf("add:%d\n", (*cac_func)(2,7));
//关闭动态链接库 dlclose(handle); exit(EXIT_FAILURE);
return 0; }

 原文链接:动态链接库dlopen的函数的使用_A493203176的博客-CSDN博客_dlopen

四、dlsym函数的使用

比如,假设在so中定义了一个void mytest()函数,那在使用so时先声明一个函数指针:void (*pMytest)(int),

然后使用dlsym函数指针pMytest指向mytest函数,pMytest = (void (*)(int))dlsym(handle, “mytest”);

 

 

=============================================================

补充

一、编译参数-fPIC

linux下使用gcc -fPIC作用于编译阶段,告诉编译器产生与位置无关代码(Position-Independent Code).

如果不加-fPIC,则加载.so文件的代码段时,代码段引用的数据对象需要重定位, 重定位会修改代码段的内容,这就造成每个使用这个.so文件代码段的进程在内核里都会生成这个.so文件代码段的copy。每个copy都不一样,取决于这个.so文件代码段和数据段内存映射的位置。不加fPIC编译出来的so,是要再加载时根据加载到的位置再次重定位的.(因为它里面的代码并不是位置无关代码) 如果被多个应用程序共同使用,那么它们必须每个程序维护一份so的代码副本了.(因为so被每个程序加载的位置都不同,显然这些重定位后的代码也不同,当然不能共享)

我们总是用fPIC来生成so,也从来不用fPIC来生成a。

fPIC与动态链接可以说基本没有关系,libc.so一样可以不用fPIC编译,只是这样的so必须要在加载到用户程序的地址空间时重定向所有表目. 因此,不用fPIC编译so并不总是不好。

如果你满足以下4个需求/条件时,可以不使用-fPIC选项。

1.该库可能需要经常更新

2.该库需要非常高的效率(尤其是有很多全局量的使用时)

3.该库并不很大.

4.该库基本不需要被多个应用程序共享

原文链接:gcc -fPIC – 时令 – 博客园 (cnblogs.com)

二、编译参数-rdynamic

1、是一个链接器选项,当将所有的*.o和库链接到最终可执行文件中时,实际上就使用了它。

2、该参数的作用是:将指示连接器把所有符号(而不仅仅只是程序已使用到的外部符号,但不包括静态符号,比如被static修饰的函数)都添加到动态符号表(即.dynsym表)里,以便那些通过dlopen()或backtrace()(这一系列函数使用.dynsym表内符号)这样的函数使用。

三、符号和符号表

符号就是其实程序中的变量名、函数名。本质是:指被分配了存储空间。如果是函数名则指代码所在区;如果是变量名则指其所在的静态数据区。

所有定义的符号的值就是其目标所在的首地址。因此,符号的解析就是将符号引用和符号定义建立关联后,将引用符号的地址重定位为相关联的符号定义的地址。

每个可重定位目标模块m都有一个符号表,它包含了在m中定义和引用的符号。

原文链接:程序的链接(三):符号和符号表 – 简书 (jianshu.com)

 四、函数指针

函数指针是指向函数的指针变量

#include <stdio.h>

int MyFun(int x)
{
    fprintf(stdout, "%d\n", x);
}

int main()
{
    int (*FunP)(int);  //声明函数指针
int a = 2; FunP = MyFun; FunP(a); return 0; }

说明:
1)MyFun的函数名与FunP函数指针都是一样的,即都是函数指针。MyFun函数名是一个函数指针常量,而FunP是一个函数数指针变量,这是它们的关系。

2)但函数名调用如果都如(*MyFun)(10)这样,那书写与读起来都是不方便和不习惯的,所以C语言的设计者们才会设计成又可允许MyFun(10)这种形式的调用

3)为统一起见,FunP函数指针变量也可以FunP(10)的形式来调用

4)赋值时,即可FunP = &MyFun形式,也可FunP = MyFun

5)在函数的声明处:
      void MyFun(int);    /* 不能写成void (*MyFun)(int) */
      void (*FunP)(int);   /* 不能写成void FunP(int) */

今天的文章动态库的两种调用方式_dlopen函数分享到此就结束了,感谢您的阅读,如果确实帮到您,您可以动动手指转发给其他人。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/52940.html

(0)
编程小号编程小号
上一篇 2023-08-31 07:06
下一篇 2023-08-31

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注