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

news2024/10/1 3:21:49

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.coloradmin.cn/o/1395537.html

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

相关文章

分享 GitHub 上的敏感词汇工具类:sensitive-word

&#x1f604; 19年之后由于某些原因断更了三年&#xff0c;23年重新扬帆起航&#xff0c;推出更多优质博文&#xff0c;希望大家多多支持&#xff5e; &#x1f337; 古之立大事者&#xff0c;不惟有超世之才&#xff0c;亦必有坚忍不拔之志 &#x1f390; 个人CSND主页——Mi…

快慢指针-Floyd判圈算法

对于环形链表是否存在环的做法&#xff0c;普通算法可以通过额外Hash数组来存储链表元素&#xff0c;直到Hash数组中出现重复元素。时间复杂度O(n)&#xff0c;空间复杂度O(n) Floyd判圈算法通过利用快慢指针的移动来实现&#xff0c;时间复杂度O&#xff08;n&#xff09;&am…

09 STM32 - PWM

9.1 PWM简介 脉冲宽度调制(Pulse Width Modulation,简称PWM)&#xff0c;是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术。简单一点&#xff0c;就是对脉冲宽度的控制。 9.2 PWM波原理 如下图所示&#xff0c;使用定时器定时&#xff0c;从0开始&#x…

Linux grep命令(grep指令)grep --help各选项介绍(待更)

文章目录 grep --help英文中文 使用示例-E, --extended-regexp&#xff1a;此选项表示模式为扩展正则表达式。-F, --fixed-strings&#xff1a;此选项表示模式被视为固定字符串而不是正则表达式。-G, --basic-regexp&#xff1a;此选项表示模式为基础正则表达式。这是默认的模式…

HFSS笔记/信号完整性分析(二)——软件仿真设置大全

文章目录 1、多核运算设置1.1 如何设置1.2 如何查看自己电脑的core呢&#xff1f;1.3 查看求解的频点 2、求解模式设置Driven Terminal vs Driven modal 3、Design settings4、自适应网格划分5、更改字体设置 仅做笔记整理与分享。 1、多核运算设置 多核运算只对扫频才有效果&…

如何用GPT进行数据处理?

详情点击链接&#xff1a;如何用GPT进行数据处理&#xff1f; 一OpenAI 1.最新大模型GPT-4 Turbo 2.最新发布的高级数据分析&#xff0c;AI画图&#xff0c;图像识别&#xff0c;文档API 3.GPT Store 4.从0到1创建自己的GPT应用 5. 模型Gemini以及大模型Claude2二定制自己…

HarmonyOS NEXT鸿蒙星河版发布

1月18日,在深圳举行的“鸿蒙生态千帆启航仪式”上,华为常务董事、终端BG CEO余承东宣布HarmonyOS NEXT鸿蒙星河版面向开发者开放申请。鸿蒙星河版将实现原生精致、原生易用、原生流畅、原生安全、原生智能、原生互联6大极致原生体验。 并且,华为在 1 月 15 日开启了HarmonyO…

稳定币智能分析:未来加密领域策略的关键!

随着 DeFi 和代币化改变金融领域&#xff0c;稳定币成为传统金融系统与新兴区块链之间的关键桥梁。但要在新兴的加密经济中准确评估稳定币的影响&#xff0c;需要一种全新的分析工具。 Token Explorer 是一款强大的稳定币分析工具&#xff0c;它能够追踪关键稳定币指标&#x…

50道SQL练习题及答案与详细分析

数据表介绍 --1.学生表 Student(SId,Sname,Sage,Ssex) --SId 学生编号,Sname 学生姓名,Sage 出生年月,Ssex 学生性别 --2.课程表 Course(CId,Cname,TId) --CId 课程编号,Cname 课程名称,TId 教师编号 --3.教师表 Teacher(TId,Tname) --TId 教师编号,Tname 教师姓名 --4.成绩…

抽象类(没有对象)之引用对象失败之谜

&#x1f468;‍&#x1f4bb;作者简介&#xff1a;&#x1f468;&#x1f3fb;‍&#x1f393;告别&#xff0c;今天 &#x1f4d4;高质量专栏 &#xff1a;☕java趣味之旅 欢迎&#x1f64f;点赞&#x1f5e3;️评论&#x1f4e5;收藏&#x1f493;关注 &#x1f496;衷心的希…

从技术大会到面试舞台:程序猿的蜕变之旅!

在这个技术日新月异的时代&#xff0c;程序员们需要不断地学习和提升自己的技能。 参加技术大会&#xff0c;无疑是程序员们拓宽视野、提升技能的重要途径之一。然而&#xff0c;技术大会只是程序员成长的一部分&#xff0c;掌握面试技巧同样至关重要。只有将这两者完美结合&a…

2024年回炉计划之排序算法(一)

算法是计算机科学和信息技术中的重要领域&#xff0c;涉及到问题求解和数据处理的方法。要学习算法&#xff0c;你可能需要掌握以下一些基本知识&#xff1a; 基本数据结构&#xff1a; 了解和熟练使用各种数据结构&#xff0c;如数组、链表、栈、队列、树和图等。数据结构是算…

[一]ffmpeg音视频解码

[一]ffmpeg音视频解码 一.编译ffmpeg1.安装vmware虚拟机2.vmware虚拟机安装linux操作系统3.安装ftp和fshell软件4.在Ubuntu&#xff08;Linux&#xff09;中编译Android平台的FFmpeg&#xff08; arm和x86 &#xff09;5.解压FFmpeg6.Android编译脚本&#xff08;1&#xff09;…

军事智能中的深度强化学习不同于传统的深度强化学习

在军事智能中&#xff0c;“诡”和“诈”是两个最重要的概念。 “诡”变指的是智能体通过采取一些不可预测或复杂的变化策略来获得优势。诡变可能包括逃避对手的观察或引诱对手采取不利的行动。智能体可以使用诡变来欺骗对手&#xff0c;使其做出错误的决策或暴露其策略。 “诈…

C# WebApi传参及Postman调试

概述 欢迎来到本文&#xff0c;本篇文章将会探讨C# WebApi中传递参数的方法。在WebApi中&#xff0c;参数传递是一个非常重要的概念&#xff0c;因为它使得我们能够从客户端获取数据&#xff0c;并将数据传递到服务器端进行处理。WebApi是一种使用HTTP协议进行通信的RESTful服…

网络安全产品之认识WEB应用防火墙

随着B/S架构的广泛应用&#xff0c;Web应用的功能越来越丰富&#xff0c;蕴含着越来越有价值的信息&#xff0c;应用程序漏洞被恶意利用的可能性越来越大&#xff0c;因此成为了黑客主要的攻击目标。传统防火墙无法解析HTTP应用层的细节&#xff0c;对规则的过滤过于死板&#…

【面试合集】说说什么是进程?什么是线程?区别?

面试官&#xff1a;说说什么是进程&#xff1f;什么是线程&#xff1f;区别&#xff1f; 一、进程 操作系统中最核心的概念就是进程&#xff0c;进程是对正在运行中的程序的一个抽象&#xff0c;是系统进行资源分配和调度的基本单位 操作系统的其他所有内容都是围绕着进程展开…

[足式机器人]Part2 Dr. CAN学习笔记- Kalman Filter卡尔曼滤波器Ch05

本文仅供学习使用 本文参考&#xff1a; B站&#xff1a;DR_CAN Dr. CAN学习笔记 - Kalman Filter卡尔曼滤波器 Ch05 1. Recursive Algirithm 递归算法2. Data Fusion 数据融合Covarince Matrix协方差矩阵State Space状态空间方程 Observation观测器3. Step by step : Deriatio…

【JVM】强软弱虚引用详细解释

​ &#x1f34e;个人博客&#xff1a;个人主页 &#x1f3c6;个人专栏&#xff1a;JVM ⛳️ 功不唐捐&#xff0c;玉汝于成 ​ 目录 前言 正文 1、强引用 2、软引用 3、弱引用 4、虚引用 结语 我的其他博客 前言 在Java中&#xff0c;引用类型对于对象的生命周期管…

SG-9101CGA(汽车+125°C可编程晶体振荡器)

SG-9101CGA是用于汽车CMOS输出的可编程晶体振荡器&#xff0c;彩用2.5 x 2.0 (mm)封装&#xff0c;0.67 MHz至170 MHz频率范围、工作温度范围为-40℃~125℃&#xff0c;符合车规级晶振&#xff0c;无铅&#xff0c;绿色环保&#xff0c;满足汽车工业标准&#xff0c;电源电压范…