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.mzph.cn/news/633389.shtml

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

相关文章

在 PyTorch 中,怎么指定程序使用的 GPU。

在 PyTorch 中&#xff0c;你可以使用 CUDA_VISIBLE_DEVICES 环境变量来指定程序使用的 GPU。在你的命令行中运行脚本之前&#xff0c;你可以设置该环境变量。以下是如何指定显卡的一种方法&#xff1a; 假设使用第一张 GPU&#xff0c;可以运行以下命令&#xff1a; CUDA_VI…

【GaussDB数据库】序

参考链接1&#xff1a;国产数据库华为高斯数据库&#xff08;GaussDB&#xff09;功能与特点总结 参考链接2&#xff1a;GaussDB(DWS)介绍 GaussDB简介 官方网站&#xff1a;云数据库GaussDB GaussDB是华为自主创新研发的分布式关系型数据库。该产品支持分布式事务&#xff0c;…

Unity之射线检测

不知道大家有没有玩过红色警戒 —— 一款即时战略游戏&#xff0c;和罪恶都市一样小编小学的时候就开始玩了&#xff0c;这款游戏控制单位角色移动是通过鼠标的点击来实现。 同样的操作方法还有英雄联盟等很多游戏&#xff0c;那本篇文章小编就通过简单小实例来讲解这种操作在U…

2024最新Java高频面试题总结(附答案PDF)春招面试必备!

《Java面试全解析》1000道 面试题大全详解 本人是 2009 年参加编程工作的&#xff0c;一路上在技术公司摸爬滚打&#xff0c;前几年一直在上海&#xff0c;待过的公司有 360 和游久游戏&#xff0c;因为自己家庭的原因&#xff0c;放弃了阿里钉钉团队的 offer 回到了西安。 从…

openfire源码篇(一)检出源码并运行

openfire源码篇&#xff08;一&#xff09;检出源码并运行 源码检出 官方github地址 https://github.com/igniterealtime/Openfire 检出源码到本地&#xff08;请注意你的java版本&#xff0c;我检出的openfire 为 4.9.0-SNAPSHOT 此时jdk版本应为11&#xff09; 将源码检出…

API接口指南:打造高效开发流程的秘密武器

IP应用场景-IPv4&#xff0c;IPv4应用场景是获取IP场景属性的在线调用接口&#xff0c;具备识别IP真人度&#xff0c;提升风控和反欺诈等业务能力。IP应用场景基于地理和网络特征的IP场景划分技术&#xff0c;将IP划分为含数据中心、交换中心、家庭宽带、CDN、云网络等共计18类…

通过OpenIddict设计一个授权服务器02-创建asp.net项目

在这一部分中&#xff0c;我们将创建一个ASPNET核心项目&#xff0c;作为我们授权服务器的最低设置。我们将使用MVC来提供页面&#xff0c;并将身份验证添加到项目中&#xff0c;包括一个基本的登录表单。 创建一个空的asp.net core项目 正如前一篇文章中所说&#xff0c;授权…

苹果Find My可查找添加32件物品,伦茨科技ST17H6x芯片加速产品赋能

苹果最近更新的支持文档证实&#xff0c;从 iOS 16 开始&#xff0c;"Find My"可查找添加物品从16件增加到32件&#xff0c;AirTag 和“查找”网络中的物品利用“查找”网络的强大功能来发挥作用&#xff0c;这个网络由数亿台加密的匿名 Apple 设备构成。“查找”网络…

【MySQL】临时变量用法

力扣题 1、题目地址 2388. 将表中的空值更改为前一个值 2、模拟表 表&#xff1a;CoffeeShop Column NameTypeidintdrinkvarchar id 是该表的主键&#xff08;具有唯一值的列&#xff09;。该表中的每一行都显示了订单 id 和所点饮料的名称。一些饮料行为 null。 3、要求…

数据结构之栈的基本操作

该顺序栈涉及到了存储整型数据的顺序栈还有存储字符型数据的顺序栈 实现的功能有&#xff1a;入栈、出栈、判断是否为空栈、求栈的长度、清空栈、销毁栈、得到栈顶元素 此外根据上述功能&#xff0c;编写了数值转换&#xff08;十进制转化八进制&#xff09;方法、括号匹配方法…

Wav2Lip视频人脸口型同步(Win10)

最近比较火的一个AI场景&#xff0c;就是用原声讲外语&#xff0c;嘴型同步&#xff0c;网上找了些资料&#xff0c;今天也跑起来了&#xff0c;推荐 Wav2Lip&#xff0c;官网地址&#xff1a;Github Wav2Lip 环境准备 Python3.6ffmpeggit clone https://github.com/Rudrabha/…

Mysql Explain各字段说明

id &#xff1a;同一个select&#xff0c;id都是1&#xff1b;不同的select&#xff0c;id才不同。 select_type: 查询类型 SIMPLE&#xff1a;普通查询 PRIMARY:主查询 SUBQUERY&#xff1a;子查询 DEPENDENT SUBQUERY&#xff1a;子查询&#xff0c;不能被优化为连接查询…

Spring Boot - 利用Resilience4j-Circuitbreaker实现断路器模式_防止级联故障

文章目录 PreResilience4j概述Resilience4j官方地址Resilience4j-Circuitbreaker应用场景微服务演示Address servicePOMModelRepositoryServiceControllerData InitProperties测试 Order serviceModelRepositoryServiceSet UpProperties测试 探究断路器调用order-service API 2…

Centost7中Redis源码编译与安装

Centost7中Redis源码编译与安装 文章目录 Centost7中Redis源码编译与安装1.下载与安装1.下载2.安装3 .解决gcc未找到的问题1.能连网直接使用命令安装(本文的安装方式)&#xff1a;2.不能连网&#xff1a;从安装镜像中找到对应的包进行安装 2.redis.conf及hello world1.redis.co…

卷积和滤波对图像操作的区别

目录 问题引入 解释 卷积 滤波 问题引入 卷积和滤波是很相似的&#xff0c;都是利用了卷积核进行操作 那么他们之间有什么区别呢&#xff1f; 卷积&#xff1a;会影响原图大小 滤波&#xff1a;不会影响原图大小 解释 卷积 我们用这样一段代码来看 import torch.nn as …

黑豹程序员-MyBatisPlus封装SQL的where条件的对象 QueryWrapper

说明 我们使用MybatisPlus时&#xff0c;我们可以不直接通过SQL操作数据库&#xff0c;而使用面向对象的方式。 其中遇到一个问题&#xff0c;就是如何用面向对象的方式实现 SQL中的where条件拼接。 MybatisPlus很体贴&#xff0c;它提供了一个QueryWrapper&#xff0c;查询包…

【AI接口】语音版、文心一言大模型和AI绘图、图片检测API

文章目录 一、语音版大模型AI1、接口2、请求参数3、请求参数示例4、接口返回示例 二、AI图片鉴黄合规检测API1、接口2、请求参数3、请求参数示例4、接口返回示例5、报错说明6、代码开源 三、人工智能AI绘画API1、接口2、请求参数3、请求参数示例4、接口返回示例5、AI绘画成果展…

Gin 框架之用户密码加密

文章目录 一、引入二、密码加密位置三、如何加密四、bcrypt 库加密4.1 介绍4.2 优点&#xff1a;4.3 使用 五、小黄书密码加密实践 一、引入 Gin是一个用Go语言编写的Web框架&#xff0c;而用户密码的加密通常是在应用程序中处理用户身份验证时的一个重要问题。 通常敏感信息…

3D可视化:陶瓷烧制的未来之路

陶瓷&#xff0c;这一古老的艺术形式&#xff0c;见证了中华文明的辉煌。然而&#xff0c;随着时代的变迁&#xff0c;传统的陶瓷烧制过程正面临着诸多挑战。如何将这门千年技艺传承下去&#xff0c;并在现代社会中焕发新的光彩&#xff1f;3D可视化技术为我们打开了一扇通往未…

如何使用c++max函数

在 C 中&#xff0c;使用 max 函数通常意味着你想比较两个值并返回其中较大的一个。这个函数是标准库中 <algorithm> 头文件的一部分。下面是如何使用它的基本步骤&#xff1a; 包含 <algorithm> 头文件&#xff1a;这是使用 max 函数的先决条件&#xff0c;因为它…