C++ Linux动态库的编译和调用

C++动态库编译

采用g++编译C++动态库,命令如下:

g++ -fPIC -shared -o 动态库名 cpp文件名

1.1 关于fPIC选项 

首先了解动态库的载入时重定位。

一般linux的可执行文件都是elf格式(一种二进制文件格式),在可执行文件的头部包含了文件格式、加载地址、符号表等信息。当连接器链接生成可执行文件时,会将程序的加载地址写入到可执行文件的头中。在程序运行时,动态加载器将可执行文件载入文件头指定的加载地址位置,并加载该地址,开始从该地址处运行。由此可见,可执行文件的起始地址是在编译时就决定的。

#define EI_NIDENT 16
typedef struct{unsigned char e_ident[EI_NIDENT];Elf32_Half e_type;Elf32_Half e_machine;Elf32_Word e_version;Elf32_Addr e_entry;Elf32_Off e_phoff;Elf32_Off e_shoff;Elf32_Word e_flags;Elf32_Half e_ehsize;Elf32_Half e_phentsize;Elf32_Half e_phnum;Elf32_Half e_shentsize;Elf32_Half e_shnum;Elf32_Half e_shstrndx;
} Elf32_Ehdr;

最开头是16个字节的e_ident, 其中包含用以表示ELF文件的字符,以及其他一些与机器无关的信息。开头的4个字节值固定不变,为0x7f和ELF三个字符。

e_type 它标识的是该文件的类型。

e_machine 表明运行该程序需要的体系结构。

e_version 表示文件的版本。

e_entry 程序的入口地址。

e_phoff 表示Program header table 在文件中的偏移量(以字节计数)。

e_shoff 表示Section header table 在文件中的偏移量(以字节计数)。

e_flags 对IA32而言,此项为0。

e_ehsize 表示ELF header大小(以字节计数)。

e_phentsize 表示Program header table中每一个条目的大小。

e_phnum 表示Program header table中有多少个条目。

e_shentsize 表示Section header table中的每一个条目的大小。

e_shnum 表示Section header table中有多少个条目。

e_shstrndx 包含节名称的字符串是第几个节(从零开始计数)。

elf的依赖库查看

readelf -d main1 | grep NEEDED

elf各个section的header信息

readelf -S --wide main

以二进制方式打开某个可执行程序,可以看到开头就是ELF头信息

载入时重定位的缺点:

1、动态库的代码段不能在进程间共享:多个进程加载同一个动态库到各自不同的地址空间,导致代码段需要不同的重定位,所以最终每个引用该动态库的进程拥有一份该动态库代码段的不同拷贝。

2、代码段必须是可写的,增加了被攻击风险。

为了解决载入时重定位的问题,引入了PIC的概念,即位置无关代码。

1.2 关于shared选项

-shared用来创建动态库

1.3 测试demo

#include <iostream>
using namespace std;void Get123Info()
{cout << "get info call success" << endl;
}
编译当前demo : g++ -fPIC -shared -o libtest1.so test.cpp

1.3.1 C++名字改编问题

生成的库进行查看当前库的符号,发现当前函数名被g++编译器改名了

这里涉及一个问题需要注意

名字改编(Name Mangling,或Name Decoration):在C++中,有函数重载的特性,所以编译器在编译时会出现符号名称相同的问题,为了解决这个问题,就有了名字改编。它将一些函数的额外信息加入到符号名中。

1.3.2 常规的动态库接口处理手段

如果我们在动态库的制作中,接受了这个名字改编,那对方调用加载这个符号时,就需要根据提供的so的实际符号名进行加载,那将很麻烦,所以一般都是采用C语言的规则来解决这个问题,即采用extern "C"的方式。

修改后的代码如下

#include <iostream>
using namespace std;#ifdef __cplusplus
extern "C"__attribute__((visibility("default"))) void Get123Info(){cout << "get info call success" <<endl;}
#endif
再进行编译,查看符号和函数名一致了

二、C++动态库的调用

2.1 系统显式调用库

linux下C++动态库的加载和调用是采用<dlfcn.h>库进行显式调用

其中包括系统函数如下:

2.1.1 dlopen()

函数功能:打开一个动态库,并返回动态库的句柄

函数定义如下:

void * dlopen( const char * pathname, int mode);
其中第一个参数是库路径名称;

第二个参数是加载库的模式:

RTLD_LAZY:暂缓决定,在dlopen返回前,对于动态库中的未定义的符号不执行解析(只对函数引用有效,对于变量引用总是立即解析)

RTLD_NOW:立即决定,在dlopen返回前,解析出所有未定义的符号,如果解析不出来,在dlopen会返回NULL,错误为 undefined symbol:XXX...

作用范围:

RTLD_GLOBAL: 动态库中定义的符号可被其后打开的其他库重定位

RTLD_LOCAL: 与RTLD_GLOBAL作用相反,动态库中定义的符号不能被其后打开的其他库重定位。如果没有指明是RTLD_GLOBAL还是RTLD_LOCAL,那么

默认是RTLD_LOCAL。

返回值:

成功返回库引用的handle,失败返回NULL

2.1.2 dlsym()

函数功能:从动态库中获取符号(全局变量与函数符号)地址,通常用于获取函数符号地

址。

函数定义:

void *dlsym(void *handle, const char *symbol);
其中第一个参数为动态库的句柄,第二个参数为符号名(可以理解为函数名)

返回值:

成功返回函数符号地址,失败返回NULL

2.1.3 dlclose()

函数功能:关闭动态库句柄,只有当此动态库的使用计数为0时,才会被真正的卸载。

函数定义:

int dlclose(void *handle);
其中入参为动态库句柄

返回值:

成功返回0,失败返回非0

2.1.4 dlerror()

函数功能:当动态链接库操作函数执行失败时,dlerror可以返回出错信息,返回值为 NULL时表示操作函数执行成功。

函数定义:

char *dlerror(void);
返回值:返回为空,表示执行成功,返回值不为空,返回具体报错信息

2.2 测试demo

g++编译命令如下:

g++ -o main1 main1.cpp -ldl -g

2.2.1 关于ldl选项

-ldl 是 g++ 编译器链接选项,它会将动态链接库 libdl.so 链接进可执行文件中,以便程序 可以调用 libdl 中定义的函数。使用该命令即可

2.2.2 demo代码

#include <iostream>
#include <dlfcn.h>
using namespace std;
typedef void (*Getinfo)();
int main()
{void* handle = dlopen("./libtest1.so", RTLD_LAZY);if (!handle){return 0;}void* temp = dlsym(handle, "Get1234Info");if (temp){Getinfo getinfo = reinterpret_cast<Getinfo>(temp);(*getinfo)();}else{cout << "dlsym error: " << dlerror() << endl;}dlclose(handle);return 0;
}

代码测试

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/414447.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C++进阶(五)二叉搜索树

&#x1f4d8;北尘_&#xff1a;个人主页 &#x1f30e;个人专栏:《Linux操作系统》《经典算法试题 》《C》 《数据结构与算法》 ☀️走在路上&#xff0c;不忘来时的初心 文章目录 一、二叉搜索树概念二、二叉搜索树操作三、二叉搜索树的实现四、二叉搜索树的应用五、二叉搜索…

Maxwell介绍

一、介绍 介绍&#xff1a;它读取MySQL binlog并将数据更改作为JSON写入Kafka、Kinesis和其他流媒体平台&#xff08;目前支持&#xff1a;kafka、RabbitMQ、Redis、file、Kinesis、Nats、Google Cloud Pub/Sub、Google Cloud Bigquery、SNS&#xff09; 版本&#xff1a;从v1.…

electron-builder打包过程中报错的处理

electron在使用electron-builder打包过程中需要用到四个包&#xff0c;但是由于内网的网络限制&#xff0c;下载不下来&#xff0c;会导致报错。下面即为具体的包&#xff1a; 通过镜像 https://registry.npmmirror.com/ https://registry.npmmirror.com/ 去下载相关的资…

智慧公厕:利用物联网、云计算和人工智能实现智能化管理与控制

智慧公厕是指利用传感感知、物联网、互联网、大数据、云计算、自动化控制等先进技术&#xff0c;实现对公厕的智能化管理与控制。通过以上高精尖的信息技术手段&#xff0c;可以实时监测厕所内人体活动状态、人体存在状态、空气质量情况、环境变化情况、设施设备运行状态等信息…

CentOS 7上安装Anaconda 详细教程

目录 1. 下载Anaconda安装脚本2. 校验数据完整性&#xff08;可选&#xff09;3. 运行安装脚本4. 遵循安装指南5. 选择安装位置6. 初始化Anaconda7. 激活安装8. 测试安装9. 更新Anaconda10. 使用Anaconda 1. 下载Anaconda安装脚本 首先需要从Anaconda的官方网站下载最新的Anac…

模型Model:字符串列表模型QStringListModel

一、QStringListModel &#xff08;1&#xff09;功能&#xff1a;处理字符串列表的数据模型&#xff0c;可作为QListView的数据模型&#xff0c;在界面上显示和编辑字符串列表。 二、QStringListModel 类中的函数 1)、 QStringListModel(QObject *parent Q_NULLPTR) //构造函…

基于android的违章处理APP 前后端服务 -毕业设计

基于android的违章处理APP 该项目是基于android版本的违章处理APP&#xff0c;系统包含前端android服务和后端web服务&#xff0c;内容和技术都是目前比较流行的架构。 技术介绍 前端android端&#xff1a; jdk17 gradle8.0 android studio 采用2023版本 后端web端&#xff…

基于局部信息提取的人脸标志检测算法matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 4.1 人脸检测 4.2 局部区域选择 4.3 特征提取 5.算法完整程序工程 1.算法运行效果图预览 2.算法运行软件版本 matlab2022a 3.部分核心程序 .........................................…

情人节专属--HTML制作情人节告白爱心

💕效果展示 💕html展示 <!DOCTYPE html> <html lang="en" > <head>

如何在扫描电子显微镜中选择合适的扫描速度

在扫描电子显微镜&#xff08;SEM&#xff09;中&#xff0c;选择合适的扫描速度对于获得高质量的图像至关重要。扫描速度直接影响图像的分辨率、细节和清晰度。本文将泽攸小编将详细介绍在选择扫描速度时需要考虑的各种因素&#xff0c;并提供一些建议&#xff0c;以帮助您在S…

FPGA引脚选择(Select IO)--认知1

主要考虑功能角度&#xff08;速度&#xff0c;电平匹配&#xff0c;内部程序编写&#xff09;去找研究芯片内部资源 1. 关键字 HP I/O Banks, High performance The HP I/O banks are deisgned to meet the performance requirements of high-speed memory and other chip-to-…

2018年认证杯SPSSPRO杯数学建模C题(第二阶段)机械零件加工过程中的位置识别全过程文档及程序

2018年认证杯SPSSPRO杯数学建模 基于轮廓提取与图像配准的零件定位问题研究 C题 机械零件加工过程中的位置识别 原题再现&#xff1a; 在工业制造自动生产线中&#xff0c;在装夹、包装等工序中需要根据图像处理利用计算机自动智能识别零件位置&#xff0c;并由机械手将零件…