dlopen、dlsym和dlclose的使用和举例

本文详细介绍了如何使用dlopen、dlsym和dlclose三个函数进行动态库的加载、符号解析和卸载。并通过实例展示了如何生成动态库并利用dlopen进行加载及调用库中的函数。

   之前用过这三个函数一直没时间整理一下。今天抽时间整理一下。

1、函数简介

dlopen

基本定义

功能:打开一个动态链接库 
  包含头文件: 
  #include <dlfcn.h> 
  函数定义: 
  void * dlopen( const char * pathname, int mode ); 
  函数描述: 
  在dlopen的()函数以指定模式打开指定的动态连接库文件,并返回一个句柄给调用进程。使用dlclose()来卸载打开的库。 
  mode:分为这两种 
  RTLD_LAZY 暂缓决定,等有需要时再解出符号 
  RTLD_NOW 立即决定,返回前解除所有未决定的符号。 
  RTLD_LOCAL 
  RTLD_GLOBAL 允许导出符号 
  RTLD_GROUP 
  RTLD_WORLD 


  返回值
  打开错误返回NULL 
  成功,返回库引用 
  编译时候要加入 -ldl (指定dl

dlsym()

 
 功能:

根据动态链接库操作句柄与符号,返回符号对应的地址。
包含头文件:
#include <dlfcn.h>
函数定义:
void*dlsym(void* handle,const char* symbol)
函数描述:
dlsym根据动态链接库操作句柄(handle)与符号(symbol),返回符号对应的地址。使用这个函数不但可以获取函数地址,也可以获取变量地址。
handle是由 dlopen打开 动态链接库后返回的 指针,symbol就是要求获取的函数或 全局变量的名称。

dlclose()

 

dlclose用于关闭指定句柄的动态链接库,只有当此动态链接库的使用计数为0时,才会真正被系统卸载。
 
上述都是摘抄,总结为链接的时候需要用到dl库,编译的时候需要加上dlfcn.h头文件。才能保证编译不会报错。
 
2、生成动态库
hello.c函数原型:
  

#include <sys/types.h>
#include <signal.h>
#include <stdio.h>
#include <unistd.h>

typedef struct {
 const char *module;
 int  (*GetValue)(char *pszVal);
 int   (*PrintfHello)();
} hello_ST_API;


int GetValue(char *pszVal)
{
 int retval = -1;
 
 if (pszVal)
  retval = sprintf(pszVal, "%s", "123456");
  printf("%s, %d, pszVer = %s\n", __FUNCTION__, __LINE__, pszVal);
 return retval;
}

int PrintfHello()
{
 int retval = -1;
 
 printf("%s, %d, hello everyone\n", __FUNCTION__, __LINE__);
 return 0;
}

const hello_ST_API  Hello = {
     .module = "hello",
   GetValue,
   PrintfHello,
};

 

编译的时候用指令:

gcc -shared -o hello.so hello.c

上面的函数是用一个全局结构体hello来指向。在dlsym定义中说不仅可以获取函数的地址,还可以获取全局变量的地址。所以此处是想通过dlsym来获取全局变量的地址。好处自己慢慢体会。

3、dlopen代码

#include <sys/types.h>
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
#include <dlfcn.h>

typedef struct {
 const char *module;
 int  (*GetValue)(char *pszVal);
 int   (*PrintfHello)();
} hello_ST_API;


int main(int argc, char **argv)
{
 hello_ST_API *hello;
 int i = 0;
 void *handle;
 char psValue[20] = {0};
 
 handle = dlopen(“库存放的绝对路径,你可以试试相对路径是不行的", RTLD_LAZY);
 if (! handle) {
  printf("%s,%d, NULL == handle\n", __FUNCTION__, __LINE__);
  return -1;
 }
 dlerror();

 hello = dlsym(handle, "Hello");
 if (!hello) {
  printf("%s,%d, NULL == handle\n", __FUNCTION__, __LINE__);
  return -1;
 }

 if (hello && hello->PrintfHello)
  i = hello->PrintfHello();
  printf("%s, %d, i = %d\n", __FUNCTION__, __LINE__, i);
 if (hello && hello->GetValue)
  i = hello->GetValue(psValue);

 if (hello && hello->module)
  {
   printf("%s, %d, module = %s\n", __FUNCTION__, __LINE__, hello->module);
  }

    dlclose(handle);
    return 0;
}

 

编译指令:gcc -o test hello_dlopen.c -ldl

 

运行./test结果如下。

PrintfHello, 27, hello everyone
main, 36, i = 0
GetValue, 19, pszVer = 123456
main, 42, module = hello

可以看到结果正常出来了。

 

看到没用?dlsym找到全局结构体hello后,可以直接用这个全局结构体指针来使用库里面的函数了,因为我们有时候提供的库不仅仅是一个两个函数的,一般的一个库都会存在多个函数,用这种方式就可以直接使用了。不然找函数名称的话要写多少个dlsym啊?

 



 

 

 


 

   

<think>好的,我现在需要帮助用户解决关于dlopendlsym函数的使用示例解决方案的问题。首先,我得回顾一下用户之前的问题之前的回答,确保这次回答符合之前的上下文。用户之前提到过在Docker环境中运行程序时遇到的动态库问题,并且引用了关于ldd命令、显式控制规则以及C++ Name Mangling的内容。 用户现在明确要求dlopendlsym使用示例解决方案。我需要结合之前的引用内容,尤其是引用3引用4,其中提到了使用dlopendlsym时的注意事项,比如符号导出C++名称修饰的问题。首先,我应该提供一个基本的C示例,展示如何正确加载动态库并调用函数。这包括使用extern "C"来避免名称混淆,并处理可能的错误情况。 接下来,用户可能需要处理C++的情况,这时候名称修饰(Name Mangling)会导致问题。根据引用4中的信息,解决方案可以是使用extern "C"来导出函数,或者直接使用nm命令查找修饰后的名称。这里需要给出两种方法的示例,比如在C++库中使用extern "C"包裹函数,或者在动态加载时使用修饰后的名称。 然后,用户可能遇到符号覆盖的问题,比如动态库中的符号被可执行文件中的同名符号覆盖。根据引用3提到的--export-dynamic选项,我需要解释如何通过编译选项来导出可执行文件的符号,或者使用RTLD_DEEPBIND标志来优先使用库中的符号。 此外,用户可能在Docker环境中遇到路径问题,所以需要提醒检查库路径,使用ldd命令验证依赖关系,并设置LD_LIBRARY_PATH环境变量。这部分可以结合引用2中的ldd使用建议。 最后,要确保回答结构清晰,分点说明常见问题及解决方案,并提供示例代码。同时,生成的相关问题需要围绕用户可能遇到的后续问题展开,比如C++类的动态加载、调试技巧或Docker中的路径配置等。 总结步骤: 1. 提供基础C示例,使用extern "C"错误处理。 2. 展示C++的两种解决方法:extern "C"直接使用修饰名称。 3. 解释符号覆盖问题及解决方案(编译选项RTLD_DEEPBIND)。 4. 提及Docker环境中的路径问题排查命令。 5. 生成相关问题,覆盖C++类加载、错误处理、Docker配置等。</think>以下为关于$dlopen$与$dlsym$函数的使用详解及典型问题解决方案,结合动态库加载核心场景分析: --- ### 一、基础使用框架(C语言示例) ```c #include <dlfcn.h> int main() { // 加载动态库(RTLD_LAZY表示延迟绑定) void* handle = dlopen("./libmath.so", RTLD_LAZY); if (!handle) { fprintf(stderr, "加载失败: %s\n", dlerror()); // 输出错误信息[^2] return 1; } // 获取函数指针(需强制类型转换) typedef int (*add_func)(int, int); add_func add = (add_func)dlsym(handle, "add"); char* error = dlerror(); if (error) { fprintf(stderr, "符号查找错误: %s\n", error); dlclose(handle); return 1; } printf("3+5=%d\n", add(3, 5)); // 调用动态库函数 dlclose(handle); // 释放资源 return 0; } ``` **配套动态库编译**: ```bash gcc -shared -fPIC -o libmath.so math.c # 需确保math.c中的add函数用extern "C"包裹[^4] ``` --- ### 二、C++符号处理方案 当加载C++库时,需处理**Name Mangling**问题,两种解决方式: **方案1:强制C符号规范** ```cpp // math.cpp extern "C" { // 禁止名称修饰 int multiply(int a, int b) { return a * b; } } ``` 加载时直接使用原函数名: ```c auto func = (int(*)(int,int))dlsym(handle, "multiply"); ``` **方案2:解析修饰后名称** 通过$nm$命令查询实际符号: ```bash nm libmath.so | grep multiply # 输出类似 _Z8multiplyii ``` 代码中直接使用修饰名: ```c auto func = (int(*)(int,int))dlsym(handle, "_Z8multiplyii"); // [^4] ``` --- ### 三、典型问题解决方案 1. **符号覆盖冲突** - 现象:动态库符号被可执行文件中的同名符号覆盖 - 解决:编译时添加`-Wl,--export-dynamic`选项导出可执行文件符号,或使用`RTLD_DEEPBIND`标志优先使用库内符号[^3] ```c void* handle = dlopen("./libmath.so", RTLD_LAZY | RTLD_DEEPBIND); ``` 2. **路径加载失败** - 验证库路径:使用`ldd`检查依赖关系[^2] - 设置环境变量:`export LD_LIBRARY_PATH=/your/lib/path:$LD_LIBRARY_PATH` 3. **多线程安全** - 使用`RTLD_NODELETE`防止库被意外卸载 - 在多线程环境中使用`pthread_mutex_lock`保护`dlopen`操作 --- ### 四、增强型错误检测模板 ```c #define CHECK_DLERROR(msg) \ do { \ if (dlerror() != NULL) { \ fprintf(stderr, "[%s] %s\n", msg, dlerror()); \ exit(EXIT_FAILURE); \ } \ } while(0) // 使用示例 void* handle = dlopen("libcustom.so", RTLD_NOW); CHECK_DLERROR("dlopen失败"); ``` --- --相关问题--: 1. [如何动态加载C++类对象并调用成员函数?] 2. [dlopendlsym在多线程环境中的线程安全机制是怎样的?] 3. [使用dlopen时如何处理依赖库的版本冲突问题?] 4. [在Docker容器中如何正确配置动态库搜索路径?]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值