1 问题简介

正常情况下,dlopen 和 dlsym 是用来处理 C 库中的函数的,但对 C++ 来说,情况稍微复杂,如在 Android framework media 框架中加载 C++ 软解库组件时使用到 dlsym 来链接函数符号

typedef SoftOMXComponent *(*CreateSoftOMXComponentFunc)(const char *, const OMX_CALLBACKTYPE *,OMX_PTR, OMX_COMPONENTTYPE **);CreateSoftOMXComponentFunc createSoftOMXComponent =(CreateSoftOMXComponentFunc)dlsym(libHandle,"_Z22createSoftOMXComponentPKcPK16OMX_CALLBACKTYPE""PvPP17OMX_COMPONENTTYPE");

这个函数符号名 “_Z22createSoftOMXComponentPKcPK16OMX_CALLBACKTYPE” 跟 C++ 库中实际定义的函数名 “createSoftOMXComponent” 有很大的不同,这是为什么呢?这节主要来探究这个问题。

2. 原因分析

在 C/C++ 程序(库、目标文件)中,所有非静态的(non-static)函数在二进制文件中都是以 “符号(symbol)” 形式出现的。这些符号都是唯一的字符串,从而把各个函数在程序、库、目标文件中区分开来。

我们可以使用 nm 或者 readelf -s 命令来查看二进制文件中的符号信息,如 libffmpeg C 库的符号信息。

$ readelf -s libffmpeg.so
...
// 一些方法的符号信息
1065: 000969ac   540 FUNC    GLOBAL DEFAULT    7 av_packet_merge_side_data
1066: 00096bc8   556 FUNC    GLOBAL DEFAULT    7 av_packet_split_side_data
1067: 00096df4   112 FUNC    GLOBAL DEFAULT    7 av_packet_shrink_side_dat// 一些变量的符号信息
2559: 006b9848    72 OBJECT  GLOBAL DEFAULT   12 ff_vqf_demuxer
2560: 006b9890    72 OBJECT  GLOBAL DEFAULT   12 ff_w64_demuxer
2561: 006b98d8    72 OBJECT  GLOBAL DEFAULT   12 ff_wav_demuxer
...

在 C 中,符号名正是函数名,strcpy 函数的符号名就是 “strcpy”,因为在 C 中两个非静态函数的名字各不相同。

但是在 C++ 中允许重载,不同的函数可能有相同的函数名但不同的参数,并且有很多 C 所没有的特征,比如类、成员函数、异常说明等等,因此不可能直接用函数名来作为符号名。

为了解决这个问题,C++ 采用了所谓的 name mangling(名字混淆),它把函数名和参数信息杂糅在一起,改造成只有编译器才懂的符号,例如前面的 “_Z22createSoftOMXComponentPKcPK16OMX_CALLBACKTYPE” 符号即是 createSoftOMXComponent 函数名带了些参数信息。

每个编译器都按自己的方式来进行 Name Mangling 。所以各个编译器给二进制文件生成的符号可能都不相同,如用 Android 编译生成的 C++ 二进制文件符号都带前缀 “_ZNxandroid”。

$ readelf -s libstagefright.so
5497: 000ae647    24 FUNC    WEAK   DEFAULT   12 _ZNK7android6VectorINS_4S
5498: 0012dd15    22 FUNC    GLOBAL DEFAULT   12 _ZNK9mkvparser4Tags3Tag12
5499: 0013d4f9    56 FUNC    WEAK   DEFAULT   12 _ZNSt3__16vectorIhNS_9all
5500: 0008f01d     6 FUNC    GLOBAL DEFAULT   12 _ZThn8_N7android6ACodec25
5501: 000ba611   316 FUNC    GLOBAL DEFAULT   12 _ZN7android10JPEGSource4r

3 解决方案

3.1 完整符号名传递

明白了 C++ 编译器 Name Mangling 的原理,自然而然有了第一种解决方案,就跟引言中 Google 的做法是一样的。使用 nm 命令获取库函数的完整符号名,直接传给 dlsym 即可。

$ nm xxx.so | grep Fun_Name

3.2 extern “C”

C++ 有个特定的关键字用来声明采用 C binding 的函数:extern “C” 。 用 extern “C” 声明的函数将使用函数名作符号名,就像 C 函数一样。因此,只有非成员函数才能被声明为 extern “C”,并且不能被重载。

尽管限制多多,extern “C” 函数还是非常有用,因为它可以像 C 函数一样被 dlopen 动态加载。冠以 extern “C” 限定符后,并不意味着函数中无法使用 C++ 代码了,相反,它仍然是一个完全的 C++ 函数,可以使用任何 C++ 特性和各种类型的参数。

extern “C” 有两种声明方式,下方第一个示例使用内联(inline)形式还有就是使用 extern “C” { … } 花括号包含这种。

extern "C" int FunA;
extern "C" void FunB();

extern "C" {extern int FunA;extern void FunB();
}
加载类

冠以 extern “C” 限定符后,并不意味着函数中无法使用 C++ 代码了,相反,它仍然是一个完全的 C++ 函数,可以使用任何 C++ 特性和各种类型的参数。

我们利用函数 new 一个对象返回即可。

extern "C" polygon* create() {return new polygon;
}extern "C" void destroy(polygon* p) {delete p;
}

使用 dlopen 和 dlsym 来使用 C++ 中的函数、类相关推荐

  1. Linux 中 dlopen、dlsym、dlclose、dlerror函数

    编译时候要加入 -ldl (指定dl库) dlopen 基本定义 功能:打开一个动态链接库  [喝小酒的网摘]http://blog.const.net.cn/a/17154.htm 包含头文件:  ...

  2. dlsym 如何查看一个so里面的_用dlopen,dlsym加载动态链接库.so中函数

    代码如下 static void *findSymbol(const char *path, const char *symbol) { void *handle = dlopen(path, RTL ...

  3. 采用dlopen、dlsym、dlclose dlopen dlerror加载动态链接库【总结】

    1.前言为了使程序方便扩展,具备通用性,可以采用插件形式.采用异步事件驱动模型,保证主程序逻辑不变,将各个业务已动态链接库的形式加载进来,这就是所谓的插件.linux提供了加载和处理动态链接库的系统调 ...

  4. 【转】采用dlopen、dlsym、dlclose加载动态链接库

    1.前言 为了使程序方便扩展,具备通用性,可以采用插件形式.采用异步事件驱动模型,保证主控制逻辑不变,将各个业务以动态链接库的形式加载进来,这就是所谓的插件.linux提供了加载和处理动态链接库的系统 ...

  5. linux dlopen 源码,采用dlopen、dlsym、dlclose加载动态链接库

    采用dlopen.dlsym.dlclose加载动态链接库 转载请标注,熬夜写的文章,挺辛苦 ... 环境 系统: 16.04.1-Ubuntu 编译器: gnu 5.4.0 python: 2.7. ...

  6. dlopen和dlsym

    dlopen 目录 基本定义 使用 dlopen 编辑本段基本定义 功能:打开一个动态链接库 包含头文件: #include <dlfcn.h> 函数定义: void * dlopen( ...

  7. dlopen与dlsym用法

    dlopen和dlsym是用于打开动态链接库中的函数,将动态链接库中的函数或类导入到本程序中: dlopen函数: 功能:打开一个动态链接库 包含头文件: #include <dlfcn.h&g ...

  8. C语言dlopen()和dlsym()获取函数的运行时地址

    在C语言中,可以使用库函数dlopen()和dlsym()来获取函数的运行时地址.dlopen()函数用于打开共享库,dlsym()函数用于查找共享库中的符号(函数名),并返回其地址. 下面是一个示例 ...

  9. linux dlopen函数,dlopen 和 dlsym 动态调用函数

    Linux/unix 提供了使用 dlopen 和 dlsym 方法动态加载库和调用函数,这套方法在 macOS 和 iOS 上也支持. dlopen 打开一个库,获取句柄. dlsym 在打开的库中 ...

最新文章

  1. CDN监控系统(一)
  2. Java 多线程三大核心
  3. ABP框架连接Mysql数据库
  4. 单端 平衡 音质区别_听上去「高大上」的平衡接口,到底有什么门道?
  5. 关系型数据库全表扫描分片详解
  6. oracle中的常用函数
  7. Nginx支持比Apache高并发的原因
  8. oracle账户解锁28000,oracle 下载 账号密码ORA-28000账户被锁和解锁
  9. Java基础之字符串详细比较
  10. 世界编程语言2008年初排行榜
  11. chrome 长截屏插件
  12. 如何实现一个 Email HTML 邮件模板
  13. 简述计算机硬盘常见故障及处理方法,常见计算机硬盘故障的解决方法
  14. python卖水果_用Python解决一个简单的水果分类问题
  15. javascript中的交互效果
  16. 【CS学习笔记】26、杀毒软件
  17. 写专利的一点小小心得
  18. 作为3-5年的iOS开发者:你为什么迟迟进不去大厂呢?
  19. Gateway/Zuul + OpenApi 集中管理 API 资源
  20. PAT 1069.微博转发抽奖

热门文章

  1. 如何高效学习python
  2. 机械过滤器(石英砂过滤器)和多介质过滤器的区别
  3. 单片机 STM32 HAL PCF8574 例子代码
  4. 大专学历销售转行,从三线的4K走到一线20K+,我对自己很满意
  5. Python实现数独游戏(一)—— 效果展示
  6. 图灵机器人SDK接入指南
  7. 【总结】反欺诈(Fraud Detection)中所用到的机器学习模型
  8. 计算机配置高低怎么看,怎么看电脑配置高低
  9. 网络七层协议具体是什么?
  10. python 散点图点击链接图片_Python散点图。 标记的大小和样式