C++函数在库中的地址

本文讲述C++如何直接调用动态库dll或者so中的函数。

首先我们准备一个被调用库,这个库里面有两个函数,分别是C++98 与 C++11 下的,名称是run2和run1。

被调用库

相关介绍请看之前的文章《函数指针与库之间的通信讲解》。

//dll_ex_im.h
#ifndef __DLL_EX_IM_H__
#define __DLL_EX_IM_H__
#include <functional>
#include <string>
#include <iostream>
#include <thread>
#include <chrono>
#ifdef _WINDOWS
#ifdef DLLProvider
#define DLL_EXPORT_IMPORT		 __declspec(dllexport)  
#else  
#define DLL_EXPORT_IMPORT		 __declspec(dllimport)  
#endif  
#else
#define DLL_EXPORT_IMPORT __attribute__((visibility("default")))
#endif//typedef class DLL_EXPORT_IMPORT std::function< void(std::string)> output_to_caller;
DLL_EXPORT_IMPORT void run1(int a, std::function< void(std::string)> output);
DLL_EXPORT_IMPORT void run2(int a, void(*output)(std::string));#endif //__DLL_EX_IM_H__
//dll_ex_im.cpp
#include "dll_ex_im.h"void run1(int a, std::function< void(std::string)> output)
{std::cout << "run1" << std::endl;std::cout << "got parametrer: " << a << std::endl;std::cout << "return signal: " << std::endl;while (true) {output("run1: " + std::to_string(a++));std::this_thread::sleep_for(std::chrono::milliseconds(1000));}
}void run2(int a, void(*output)(std::string)) {std::cout << "run2" << std::endl;std::cout << "got parametrer: " << a << std:bian

编译一下,出来的动态库在Windows下是Reflection-DLL_TEST.dll,在Linux下是libReflection-DLL_TEST.so。

Windows下直接调用

我们用BinaryViewer这款二进制查看器看看函数run1和run2在Reflection-DLL_TEST.dll长什么样子。

查找函数run1的位置:

找到三个位置,前两个应该都是函数的名称指引(知道的同学可以介绍下前两个是做啥的)。
第三个是run1函数的地址:
在这里插入图片描述
我把这个run1地址写下来:

?run1@@YAXHV?$function@$$A6AXV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z@std@@@Z

原来的函数run1长这样:

void run1(int a, std::function< void(std::string)> output);

查找函数run2的位置:

找到三个位置,前两个应该都是函数的名称指引(知道的同学可以介绍下前两个是做啥的)。
第三个是run2函数的地址:
在这里插入图片描述
我把这个run2地址写下来:

?run2@@YAXHP6AXV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z@Z

原来的函数run2长这样:

void run2(int a, void(*output)(std::string));

地址解释

以上run1和run2函数在dll二进制文件中的地址是一个经过VS C++编译器名称修饰后的函数名,这种修饰是编译器用来区分具有相同名称但不同参数或返回类型的函数的方法。
由于我不是做编译器的,下面我用聊天机器人查了一下,仅给出以上run1函数地址的解释:
?run1@@:这是函数名run1的开头部分,其中?和@@是Microsoft编译器特有的名称修饰符号。
YAXH:这表示函数的返回类型和参数类型。在这个例子中,Y 表示返回类型为 void,AX 通常表示没有通过值传递的参数(但在这个特定情况下,由于后面有V,它实际上表示有一个通过引用或指针传递的参数),H 是参数列表的开始。不过,这里的AX和H的具体含义可能因编译器的具体实现而略有不同,重要的是理解整体结构。
V:这表示接下来的部分是一个通过引用或指针传递的参数。
?$function@…@std@@:这是对 std::function 模板的修饰表示,其中省略号(…)代表模板参数的具体类型,即 void(std::string)。
KaTeX parse error: Can't use function '$' in math mode at position 7: A6AXV?$̲basic_string@..…A6 是与调用约定相关的(可能是 __cdecl 的某种变体,但具体取决于编译器和平台),AXV 表示函数接受一个参数(V 表示通过引用或指针),?$basic_string@…@std@@@Z 是对 std::string 类型的修饰表示。

Windows调用程序

#include <iostream>
//#include <list>
#include <functional>
#ifdef _WINDOWS 
#include <shlwapi.h>
#include <Psapi.h> 
#include <codecvt> 
#else
#include <dlfcn.h>
#include <codecvt>
#endifvoid callback(std::string info) {std::cout << info << std::endl;
}void Run1(const std::string& dllpath, const std::string& initFuncName)
{std::string funName = initFuncName;
#ifdef _WINDOWStypedef void(_stdcall* pfnInitPlugin) (int, std::function< void(std::string)>);funName = "?" + funName + "@@YAXHV?$function@$$A6AXV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z@std@@@Z";auto module = LoadLibrary(dllpath.c_str());//寻找初始化函数,并执行pfnInitPlugin initfunc = (pfnInitPlugin)GetProcAddress(module, funName.c_str());if (initfunc){//常规方式//std::function< void(std::string)> cb = callback;//initfunc(4, cb);//lambda方式initfunc(4, [](std::string info) {std::cout << info << std::endl; });}else {std::cout << "未找到函数地址" << std::endl;}
#elsetypedef  void(__attribute__((__stdcall__))* pfnInitPlugin) (int, std::function< void(std::string));funName = "_Z22" + funName + "v";auto dp = dlopen(p.c_str(), RTLD_LAZY | RTLD_GLOBAL);if (dp){pfnInitPlugin initfunc = (pfnInitPlugin)dlsym(dp, funName.c_str());if (initfunc){initfunc(4, [](std::string info) {std::cout << info << std::endl;});}//dlclose(dp);}else {std::cout << "未找到函数地址" << std::endl;}
#endif
}void Run2(const std::string& dllpath, const std::string& initFuncName)
{std::string funName = initFuncName;
#ifdef _WINDOWStypedef void(_stdcall* pfnInitPlugin) (int, void(*output)(std::string));//run1funName = "?" + funName + "@@YAXHP6AXV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z@Z";auto module = LoadLibrary(dllpath.c_str());//寻找初始化函数,并执行pfnInitPlugin initfunc = (pfnInitPlugin)GetProcAddress(module, funName.c_str());if (initfunc){//void(*cb)(std::string);//cb = callback;//initfunc(4, cb);initfunc(4, [](std::string info) {std::cout << info << std::endl;});}else {std::cout << "未找到函数地址" << std::endl;}
#elsetypedef  void(__attribute__((__stdcall__))* pfnInitPlugin) (int, void(*output)(std::string));funName = "_Z22" + funName + "v";auto dp = dlopen(p.c_str(), RTLD_LAZY | RTLD_GLOBAL);if (dp){pfnInitPlugin initfunc = (pfnInitPlugin)dlsym(dp, funName.c_str());if (initfunc){initfunc(4, [](std::string info) {std::cout << info << std::endl;});}//dlclose(dp);}else {std::cout << "未找到函数地址" << std::endl;}
#endif
}int main(int argc, char* argv[]) {std::string argv1 = argv[1];if (argc == 2 && argv1 == "-h"){std::cout << "用法:\n【exe-name】【dll-path】【func-name】" << std::endl;return 0;}if (argc != 3){std::cerr << "传入的参数数量不对,应该是俩,检查检查!!" << std::endl;return -1;}std::string dllPath = argv[1];if (dllPath.find(".dll") == dllPath.npos){std::cerr << "传入的文件没有dll,检查检查!!" << std::endl;return -1;}std::string argv2 = argv[2];if (argv2 == "run1"){Run1(argv[1], "run1");}else if (argv2 == "run2") {Run2(argv[1], "run2");}else {std::cerr << "传入的函数名既不是 run1 也不是 run2 ,检查检查!!" << std::endl;return -1;}return 0;
}

在Windows下,核心代码是下面这几句:

	typedef void(_stdcall* pfnInitPlugin) (int, std::function< void(std::string)>);//run1funName = "?" + funName + "@@YAXHV?$function@$$A6AXV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z@std@@@Z";auto module = LoadLibrary(dllpath.c_str());//寻找初始化函数,并执行pfnInitPlugin initfunc = (pfnInitPlugin)GetProcAddress(module, funName.c_str());if (initfunc){//常规方式//std::function< void(std::string)> cb = callback;//initfunc(4, cb);//lambda方式initfunc(4, [](std::string info) {std::cout << info << std::endl; });}else {std::cout << "未找到函数地址" << std::endl;}

程序读入动态库,通过函数在动态库中的地址进行直接调用。
下面是调用结果:
在这里插入图片描述

Linux下直接调用

我们用BinaryViewer这款二进制查看器看看函数run1和run2在libReflection-DLL_TEST.so长什么样子。

查找函数run1的位置:

找到4个位置,前两个应该都是函数的名称指引(知道的同学可以介绍下前3个是做啥的)。
第4个是run1函数的地址:
在这里插入图片描述
我把这个run1地址写下来:

_Z4run1iSt8functionIFvNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEEE

原来的函数run1长这样:

void run1(int a, std::function< void(std::string)> output);

查找函数run2的位置:

找到4个位置,前两个应该都是函数的名称指引(知道的同学可以介绍下前3个是做啥的)。
第4个是run2函数的地址:
在这里插入图片描述
我把这个run2地址写下来:

_Z4run2iPFvNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEE

原来的函数run2长这样:

void run2(int a, void(*output)(std::string));

地址解释

以上run1和run2函数在so二进制文件中的地址是一个由 GCC编译器生成的 mangled(修饰)名称。这种名称用于在编译后的代码中唯一标识函数、变量等符号,同时包含类型信息。Mangled 名称对于人类来说通常是不直观的,但它们对于编译器和链接器来说是必要的,以确保在复杂的程序中正确地解析和链接符号。
由于我不是做编译器的,下面我用聊天机器人查了一下,仅给出以上run1函数地址的解释:
_Z 前缀是 GCC 编译器用于 mangled 名称的标识。
4run1i 部分是函数名称的编码,其中 run 是函数名,1 表示该函数接受一个参数,i 表示该参数的类型(在这个上下文中,它实际上是指接下来的类型信息,而不是直接的类型)。
St8functionIFvNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEEE 是参数类型的 mangled 表示。这个类型是一个 std::function,它包装了一个可调用对象,该对象接受一个 std::string 类型的参数(没有返回值,因为 Fv 表示一个函数类型,没有返回类型)。
St8function 表示 std::function。
IFv 表示一个函数(F)没有返回值(v,即 void),并且接下来是参数类型。
NSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE 是 std::__cxx11::basic_string<char, std::char_traits, std::allocator> 的 mangled 表示,即 std::string。

Linux调用程序

#include <iostream>
//#include <list>
#include <functional>
#ifdef _WINDOWS 
#include <shlwapi.h>
#include <Psapi.h> 
#include <codecvt> 
#else
#include <dlfcn.h>
#include <codecvt>
#endifvoid callback(std::string info) {std::cout << info << std::endl;
}void Run1(const std::string& dllpath, const std::string& initFuncName)
{std::string funName = initFuncName;
#ifdef _WINDOWStypedef void(_stdcall* pfnInitPlugin) (int, std::function< void(std::string)>);funName = "?" + funName + "@@YAXHV?$function@$$A6AXV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z@std@@@Z";auto module = LoadLibrary(dllpath.c_str());//寻找初始化函数,并执行pfnInitPlugin initfunc = (pfnInitPlugin)GetProcAddress(module, funName.c_str());if (initfunc){//常规方式//std::function< void(std::string)> cb = callback;//initfunc(4, cb);//lambda方式initfunc(4, [](std::string info) {std::cout << info << std::endl; });}else{std::cout<<"not find function name"<<std::endl;}
#elsetypedef  void(* pfnInitPlugin) (int, std::function< void(std::string)>);funName = "_Z4" + funName + "iSt8functionIFvNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEEE";auto dp = dlopen(dllpath.c_str(), RTLD_LAZY | RTLD_GLOBAL);if (dp){pfnInitPlugin initfunc = (pfnInitPlugin)dlsym(dp, funName.c_str());if (initfunc){initfunc(4, [](std::string info) {std::cout << info << std::endl;});}dlclose(dp);}else{std::cout<<"not find function name"<<std::endl;}
#endif
}void Run2(const std::string& dllpath, const std::string& initFuncName)
{std::string funName = initFuncName;
#ifdef _WINDOWStypedef void(_stdcall* pfnInitPlugin) (int, void(*output)(std::string));funName = "?" + funName + "@@YAXHP6AXV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z@Z";auto module = LoadLibrary(dllpath.c_str());//寻找初始化函数,并执行pfnInitPlugin initfunc = (pfnInitPlugin)GetProcAddress(module, funName.c_str());if (initfunc){//常规方式//void(*cb)(std::string);//cb = callback;//initfunc(4, cb);//lambda方式initfunc(4, [](std::string info) {std::cout << info << std::endl;});}else{std::cout<<"not find function name"<<std::endl;}
#elsetypedef  void(* pfnInitPlugin) (int, void(*output)(std::string));funName = "_Z4" + funName + "iPFvNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEE";auto dp = dlopen(dllpath.c_str(), RTLD_LAZY | RTLD_GLOBAL);if (dp){pfnInitPlugin initfunc = (pfnInitPlugin)dlsym(dp, funName.c_str());if (initfunc){initfunc(4, [](std::string info) {std::cout << info << std::endl;});}dlclose(dp);}else{std::cout<<"not find function name"<<std::endl;}
#endif
}int main(int argc, char* argv[]) {//C++ 98//Lambda回调函数//Run1("./Reflection-DLL_TEST.dll","run1");std::string argv1 = argv[1];if (argc == 2 && argv1 == "-h"){std::cout << "用法:\n【exe-name】【dll-path】【func-name】" << std::endl;return 0;}if (argc != 3){std::cerr << "传入的参数数量不对,应该是俩,检查检查!!" << std::endl;return -1;}std::string dllPath = argv[1];if (dllPath.find(".so") == dllPath.npos){std::cerr << "传入的文件没有so,检查检查!!" << std::endl;return -1;}std::string argv2 = argv[2];if (argv2 == "run1"){Run1(argv[1], "run1");}else if (argv2 == "run2") {Run2(argv[1], "run2");}else {std::cerr << "传入的函数名既不是 run1 也不是 run2 ,检查检查!!" << std::endl;return -1;}//system("pause");return 0;
}

在Linux下,核心代码是下面这几句:

	typedef  void(* pfnInitPlugin) (int, std::function< void(std::string)>);funName = "_Z4" + funName + "iSt8functionIFvNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEEE";auto dp = dlopen(dllpath.c_str(), RTLD_LAZY | RTLD_GLOBAL);if (dp){pfnInitPlugin initfunc = (pfnInitPlugin)dlsym(dp, funName.c_str());if (initfunc){initfunc(4, [](std::string info) {std::cout << info << std::endl;});}dlclose(dp);}else{std::cout<<"not find function name"<<std::endl;}

程序读入动态库,通过函数在动态库中的地址进行直接调用。
下面是调用结果:
在这里插入图片描述

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

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

相关文章

蓝牙耳机是入耳式的好还是开放式的好?2024开放式耳机推荐

个人推荐入开放式耳机&#xff0c;戴起来更舒服&#xff0c;主要有以下几方面原因&#xff1a; 减少对耳部的压迫&#xff1a; 不入耳设计&#xff1a;开放式耳机通常不需要插入耳道&#xff0c;避免了对耳道的直接压迫。入耳式耳机的耳塞长时间塞在耳道内&#xff0c;会对耳…

校园水电费管理|基于java的校园水电费管理小程序系统 (源码+数据库+文档)

校园水电费管理 目录 基于java的校园水电费管理小程序系统 一、前言 二、系统设计 三、系统功能设计 小程序端 后台功能模块 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取&#xff1a; 博主介绍&#xff1a;✌️大厂码农|毕…

Python基础知识学习(2)

一&#xff0c;分支条件判断语句 在python中&#xff0c;分支条件语句如下&#xff1a; 1&#xff0c;判断条件&#xff1a;if exception: 2&#xff0c;接着判断的语句为&#xff1a;elif exception: 3&#xff0c;最后的条件为&#xff1a;else: 4&#xff0c;通过缩进来表示…

46.面向对象综合训练-文字版格斗游戏

1.首先创建标准的Javabean类 import java.util.Random;public class 格斗游戏 {private String name;private int blood;public 格斗游戏() {}public 格斗游戏(String name, int blood) {this.name name;this.blood blood;}public String getName() {return name;}public vo…

基于A2C与超启发式的航天器星载自主任务规划算法-笔记

1. Actor-Critic 模块 主要文件&#xff1a;AC.py, PolicyNet.py, ValueNet.py作用&#xff1a;该模块实现了 A2C&#xff08;Advantage Actor-Critic&#xff09;强化学习算法。其中&#xff0c;ActorCritic 类是核心&#xff0c;它同时管理策略网络&#xff08;Actor&#x…

2-95 基于matlab的模板定位

基于matlab的模板定位。利用①相关匹配&#xff08;Correlation Matching&#xff09;、②基于Hausdorff距离匹配方法 及③考虑对场景图象距离变换&#xff08;Distance Transform&#xff09;的Hausdorff距离匹配方法,实现模板目标在场景图象中的定位。程序已调通&#xff0c;…

PMP--一模--解题--81-90

文章目录 4.整合管理81、 [单选] 一位先前不活跃的干系人参与程度突然增加&#xff0c;这种意外的参与导致了一些变更请求。项目经理应该做什么&#xff1f; 4.整合管理82、 [单选] 公司的新产品系列将在两个月内发布&#xff0c;95%的项目任务均已完成。但是&#xff0c;管理层…

开发一款通过蓝牙连接控制水电表的微信小程序

增强软硬件交互 为了更好的解决师生生活中的实际问题&#xff0c;开发蓝牙小程序加强了和校区硬件的交互。 比如通过蓝牙连接控制水电表&#xff0c;减少实体卡片的使用。添加人脸活体检测功能&#xff0c;提高本人认证效率&#xff0c;减少师生等待时间。 蓝牙水电控展示 蓝…

jdk相关介绍

JDK&#xff0c;全称Java Development Kit&#xff0c;是Java语言开发的基础工具包。它包含了Java运行时环境&#xff08;JRE&#xff09;以及用于开发Java应用程序的各种工具和库。JDK为Java程序员提供了编译、调试和运行Java应用程序所需的全部环境。 JDK的主要组成部分包括&…

瞳孔检测系统源码分享

瞳孔检测检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Computer Vision …

EMT-DAVT--基于子空间分布对齐和决策变量转移的多目标多任务优化

EMT-DAVT–基于子空间分布对齐和决策变量转移的多目标多任务优化 title&#xff1a; Multiobjective Multitasking Optimization With Subspace Distribution Alignment and Decision Variable Transfer author&#xff1a; Weifeng Gao, Jiangli Cheng, Maoguo Gong, Hong L…

Python酷库之旅-第三方库Pandas(118)

目录 一、用法精讲 521、pandas.DataFrame.drop_duplicates方法 521-1、语法 521-2、参数 521-3、功能 521-4、返回值 521-5、说明 521-6、用法 521-6-1、数据准备 521-6-2、代码示例 521-6-3、结果输出 522、pandas.DataFrame.duplicated方法 522-1、语法 522-2…

III 网络诈骗也玩区块链?加密货币的分类小知识!

大家好啊&#xff0c;我是豆小匠。 区块链第三期&#xff0c;这期拓展一下加密货币的话题。 如果要用加密货币来发工资&#xff0c;你希望用哪个&#xff1f; 你大概不会希望是比特币&#xff0c;比特币的波动性太高&#xff0c;可能刚发下来工资&#xff0c;亏损就超过了10%…

Java小区物业管理系统

技术架构&#xff1a; springboot mybatis thymeleaf Mysql5.7 有需要该项目的小伙伴可以添加我Q&#xff1a;598748873&#xff0c;备注&#xff1a;CSDN 功能描述&#xff1a; 控制台、数据库、楼栋管理、单元管理、房屋管理、车位管理、缴费类型、缴费管理、公告管理…

Linux下进程间的通信--共享内存

共享内存概述&#xff1a; 共享内存是进程间通信的一种方式&#xff0c;它允许两个或多个进程共享一个给定的存储区。共享内存是最快的一种IPC形式&#xff0c;因为它允许进程直接对内存进行读写操作&#xff0c;而不需要数据在进程之间复制。 共享内存是进程间通信&#xff…

Python基础语法(3)下

列表和元组 列表是什么&#xff0c;元组是什么 编程中&#xff0c;经常需要使用变量&#xff0c;来保存/表示数据。变量就是内存空间&#xff0c;用来表示或者存储数据。 如果代码中需要表示的数据个数比较少&#xff0c;我们直接创建多个变量即可。 num1 10 num2 20 num3…

2024.9.12(k8s环境搭建2)

一、接9.11 19、部署calico的pod 4. 查看容器和节点状态 异常处理&#xff1a; 出现Init:0/3&#xff0c;查看node节点 /var/log/messages是否有除网络异常之外的报错信息 三台机器执行&#xff1a;&#xff08;更新版本&#xff09; yum list kernel yum update kernel reb…

【ArcGIS】栅格计算器原理及案例介绍

ArcGIS&#xff1a;栅格计算器原理及案例介绍 栅格计算器&#xff08;Raster Calculator&#xff09;原理介绍案例案例1&#xff1a;计算栅格数据平均值 参考 栅格计算器&#xff08;Raster Calculator&#xff09;原理介绍 描述&#xff1a;在类似计算器的界面中&#xff0c;…

Linux进程概念(下)

前言 上文介绍了进程的基本概念&#xff0c;进程内核数据结构可执行程序&#xff0c;查看进程的方式ps 和/proc指令。 又熟悉常见的进程状态&#xff0c;状态修改的本质就是将PCB标志位更改&#xff0c;然后放到指定的队列中。 本文继续介绍进程的概念&#xff0c;将介绍进程…

网络安全学习(四)渗透工具msf

本文简要介绍metasploit framework&#xff0c;是一款渗透工具。官网地址&#xff1a;Metasploit | Penetration Testing Software, Pen Testing Security | Metasploit msf是一个框架&#xff0c;可以加载各种模块&#xff0c;这是它的最强大之处。 kali中有此工具。 点击即…