C/C++与汇编混合编程

1. C/C++调用汇编

C/C++想调用汇编代码必须要注意名称修饰的问题

名称修饰(name decoration): 一种标准的C/C++编译技术, 通过添加字符来修改函数名, 添加的字符指明了每个函数参数的确切类型。主要是为了支持函数重载, 但对于汇编来说其问题在于, C/C++编译器让链接器去找被修饰过的名称而非原始名称

名称修饰说白了就是C/C++源代码经过编译器编译后, 函数和变量名称发生了变化, 链接器会去找变化后的名字而非源码中定义的名字。
影响名称修饰的主要因素如下:

  • 编程语言, 比如C和C++名称修饰就不同
  • 调用约定, 比如C和STDCALL的名称修饰不同

举个例子, C和C++之间的名称修饰就是不同

a. C语言中的名称修饰格式以及C与汇编混合编程

源码如下:
在这里插入图片描述
看一下其生成obj文件:
在这里插入图片描述
发现经过VS的C编译器编译后main和Add名称前多了下划线。由于C语言默认的函数调用约定是C调用约定。所以这就是C语言使用C调用约定下的名称修饰。

_name

接下去将Add的调用约定改成STDCALL
在这里插入图片描述
再来查看一下生成的obj文件, 发现其名称修饰方式发生了改变:
在这里插入图片描述
所以C语言STDCALL调用约定下的名称修饰方式是:

_name@n

这里的n代表压入栈帧参数的大小, 由于Add参数参别是2个int类型, 在x86下也就是8字节大小, 所以经过C编译器名称修饰后就变成了

_Add@8

总结一下, C编译器编译C源码时, 默认情况下函数使用C调用约定, 除非强制指定其他调用约定, 其编译后生成的obj文件中, 名称修饰的方法如下:

C stdcall: _name@n
C cdecl: _name

接下来看一下汇编语言, 首先使用C调用约定, 经过汇编生成obj文件。
在这里插入图片描述
查看winhex发现:
在这里插入图片描述
不出乎意料, FindMax和main函数都使用了C调用方式的名称修饰, 但是ExitProcess却使用了STDCALL的名称修饰规则, 这很正常, 因为微软的API都使用STDCALL。
由于C语言和汇编生成的obj目标文件中的名称修饰都是相同的, 所以可以得出结论:

C可以直接调用使用C调用约定下的汇编代码, 无需任何改变

下面实验一下:
这是汇编源码:
在这里插入图片描述
将汇编生成的obj文件包含到项目内, 测试发现C语言调用C调用约定下的汇编没有问题:
在这里插入图片描述
如果说汇编使用STDCALL调用约定汇编生成对应的obj文件, 然后让C来进行调用, 结果就会发现这个熟悉的链接错误:
在这里插入图片描述
这个原因已经很明显了, 因为C语言默认使用C调用约定, 编译后生成的是_name格式的函数名, 当汇编使用STDCALL调用约定时, 生成的是_name@n格式的函数名, 当链接时自然就无法找到名称了。
所以可以得出结论:

C可以直接调用使用STDCALL调用约定下的汇编代码, 会出现无法解析外部符号的链接错误

b. C++中的名称修饰格式以及C++与汇编混合编程

使用C++编写一个程序, C++默认也使用C调用约定:
在这里插入图片描述
查看其生成的obj目标文件
在这里插入图片描述
可以发现经过C++编译器编译后, C调用约定下Add函数生成了一种非常奇怪的形式, 这主要是为了实现重载而做的。而main函数永远是C调用约定不会被C++编译器改变。

这也就意味着C和C++其实也使不可以互相调用的

因为如果你要用C++调用C代码。假设都使用C调用约定, C代码经C编译器生成了obj文件, 里面的名称修饰是:

_name

而C++编译器编译代码生成的obj里面使用的名称修饰是:

?Add@@YAHHH@Z

当链接时, C++去找**?Add@@YAHHH@Z**结果只有_Add, 这是肯定不可能找到的。

下面把Add函数变成STDCALL函数调用约定试试看:
在这里插入图片描述
可以发现其名称修饰变成了如下:

?Add@@YGHHH@Z // stdcall
?Add@@YAHHH@Z // C

得出结论:

C++下C和STDCALL调用约定有区别, 但是区别不大, 把A变成了G

如果想要在C++下调用汇编代码, 只要把C++的名称修饰转换成C的就可以了。
所以只要C++能调用C, 也就意味着C++可以调用汇编, 事实上也确实如此, 在C++源文件中, 函数添加extern "C"即可让C++函数使用C的名称修饰方法
在这里插入图片描述
C++源码生成obj, 放入winhex内便可发现, C++使用了C的名称修饰方式, 不管是什么调用约定, extern "C"都会让C++使用C的调用约定。
在这里插入图片描述
使用同样的方法就可以实现C++调用汇编代码了。
在这里插入图片描述

2. C/C++调用汇编的另一种方式: 内联汇编

这种方式只能在x86下进行, 并没有什么特别的地方:
在这里插入图片描述
这里给一个例子, 是一个对称xor加密的小例子:

#include <iostream>
#include <windows.h>
#include <tchar.h>#define FILEBLK		(0x1000)using namespace std;VOID CryptoBlock(PBYTE pbBuf, DWORD dwBufSize, UCHAR bKey)
{__asm{mov esi, pbBufmov ecx, dwBufSize mov bl, bKeyL0:xor BYTE PTR [esi], blinc esi loop L0}return;
}BOOLEAN SymFileCrypto(LPCSTR pcszFilePathName, UCHAR bKey)
{HANDLE hFile = INVALID_HANDLE_VALUE;HANDLE hNewFile = INVALID_HANDLE_VALUE;BOOLEAN fOk = FALSE;LARGE_INTEGER liFileSize = { 0 };int iTotalBlk = 0;BYTE bBuf[FILEBLK] = { 0 };char szTmpFileName[] = "TmpFile";DWORD dwReaded = 0;DWORD dwWritten = 0;__asm{// 参数检测mov esi, pcszFilePathNametest esi, esijz Ending// 打开文件push NULLpush FILE_ATTRIBUTE_NORMALpush OPEN_EXISTINGpush NULLpush 0push FILE_ALL_ACCESSpush esicall CreateFile// 文件句柄检查cmp eax, -1jne Next1 jmp Ending Next1:// 保存文件句柄mov hFile, eax// 获取文件大小lea eax, liFileSizepush eaxpush hFilecall GetFileSizeEx// 计算总块数mov eax, liFileSize.LowPartmov edx, liFileSize.HighPartmov ebx, FILEBLKdiv ebxtest edx, edx jz Next2inc eax Next2:mov iTotalBlk, eax// 创建新文件push NULLpush FILE_ATTRIBUTE_NORMALpush CREATE_ALWAYSpush NULLpush 0push FILE_ALL_ACCESSlea eax, szTmpFileNamepush eaxcall CreateFile// 文件句柄检查cmp eax, -1jne Next3jmp EndingNext3:mov hNewFile, eax Crypto:// 读取文件内容push NULL lea eax, dwReadedpush eax mov eax, FILEBLKpush eax lea eax, bBuf push eaxpush hFile call ReadFile test eax, eax jz Ending // 加解密xor eax, eax mov al, bKey push eax push dwReaded lea eax, bBufpush eaxcall CryptoBlock// 写入文件内容push NULLlea eax, dwWritten push eax push dwReaded lea eax, bBuf push eaxpush hNewFile call WriteFiletest eax, eax jz Ending mov ecx, iTotalBlkdec ecx mov iTotalBlk, ecxtest ecx, ecx jnz Cryptomov fOk, TRUE}Ending:__asm{cmp hNewFile, -1jz Next4 push hNewFile call CloseHandle mov hNewFile, NULLNext4:cmp hFile, -1jz Next5push hFile call CloseHandle mov hFile, NULLNext5:xor eax, eax mov al, fOktest eax, eax jz Next6// 删除源文件mov eax, pcszFilePathNamepush eaxcall DeleteFile // 改名mov eax, pcszFilePathNamepush eax lea eax, szTmpFileNamepush eax call MoveFile Next6:mov esp, ebppop ebp ret }
}int main(int argc, char **argv)
{if (argc != 3){printf("usage: %s file key\r\n", argv[0]);return(-1);}if (SymFileCrypto(argv[1], argv[2][0])){printf("成功\r\n");}else{printf("失败\r\n");}return(0);
}

(完)

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

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

相关文章

K8S-EverNote同步

Node污点 释义看文档就好 https://kubernetes.io/zh-cn/docs/concepts/scheduling-eviction/taint-and-toleration/ 污点是Node的属性 容忍度是Pod的属性 用来标记各自特征的&#xff0c;通常协同工作。 举个例子&#xff0c; 一个Node的污点 kubectl taint nodes node1 key1v…

算法练习10——数组为空的最少操作次数

LeetCode 100032 使数组为空的最少操作次数 给你一个下标从 0 开始的正整数数组 nums 。 你可以对数组执行以下两种操作 任意次 &#xff1a; 从数组中选择 两个 值 相等 的元素&#xff0c;并将它们从数组中 删除 。 从数组中选择 三个 值 相等 的元素&#xff0c;并将它们从数…

cadence SPB17.4 S032 - 使用room来放置元件

文章目录 cadence SPB17.4 S032 - 使用room来放置元件概述笔记在orcad中设置子原理图的ROOM号码在空的Allegro工程中, 放入板框在allegro中建立room备注补充 - ROOM还得留着END cadence SPB17.4 S032 - 使用room来放置元件 概述 如果在allegro中直接手工或自动放置元件, 放好…

C++ —— 单机软件加入Licence许可权限流程(附详细流程图、详细代码已持续更新..)

单机版许可证简介 笼统的说:实现一个生成授权Lic文件应用程序(我们使用),生成的Lic文件给应用程序(客户使用)启动时读取一下对比加密后的字符串或自定义格式的密钥判断是否正确。 单机版许可证执行流程 第一级比对:发布的加密许可证文件,该加密许可证文件仅可用使用的软…

计算机网络各层设备

计算机网络通常被分为七层&#xff0c;每一层都有对应的设备。以下是各层设备的简要介绍&#xff1a; 物理层&#xff08;Physical Layer&#xff09;&#xff1a;负责传输二进制数据位流的物理媒体和设备&#xff0c;例如网线、光纤、中继器、集线器等。 数据链路层&#xf…

LeNet网络复现

文章目录 1. LeNet历史背景1.1 早期神经网络的挑战1.2 LeNet的诞生背景 2. LeNet详细结构2.1 总览2.2 卷积层与其特点2.3 子采样层&#xff08;池化层&#xff09;2.4 全连接层2.5 输出层及激活函数 3. LeNet实战复现3.1 模型搭建model.py3.2 训练模型train.py3.3 测试模型test…

【网络协议】传输层协议

目录 传输层协议 1.传输层的两个协议 1.1TCP和UDP的应用场景 1.2传输层协议和应用层协议的关系 2.TCP和UDP的对比&#xff08;重点&#xff09; (1)无连接的UDP和面向连接的TCP (2)UDP和TCP对单播、多播和广播的支持情况 (3)UDP和TCP对应用层报文的处理 (4)UDP和TCP对数…

React实现多图片预览功能、预览图上下张切换(实战示例)

前言 在React项目中&#xff0c;展示和预览多张图片是一种常见的需求。本篇帖子将介绍如何使用React和antd库来实现这一功能&#xff0c;并探讨如何在预览模态框中切换到前一张或后一张图片。 背景 我们将以一个OCR图像列表展示的示例来演示代码的运用。假设我们有一个OCR系…

Linux系统编程系列之进程间通信-信号量组

一、什么是信号量组 信号量组是信号量的一种&#xff0c; 是system-V三种IPC对象之一&#xff0c;是进程间通信的一种方式。 二、信号量组的特性 信号量组不是用来传输数据的&#xff0c;而是作为“旗语”&#xff0c;用来协调各进程或者线程工作的。信号量组可以一次性在其内…

【LeetCode】滑动窗口妙解无重复字符的最长子串

Problem: 3. 无重复字符的最长子串 文章目录 思路算法原理分析暴力枚举 哈希表滑动窗口 复杂度Code 思路 首先我们来分析一下本题的思路 如果读者有看过 长度最小的子数组 的话就可以清楚这个子串其实和子数组是一个道理&#xff0c;都是 连续的一段区间但是呢它们本质上还是存…

应用架构的演进:亚马逊的微服务实践

当你在亚马逊上购物时,或许不会想到,你看到的这个购物网站,其背后技术架构经历了什么样的变迁与升级。 还记得上世纪 90 年代,那个只卖书的网上书店吗?那时的亚马逊,不过是一个架构简单的网站,所有的功能都堆积在一个庞大的软件堡垒里。随着更多业务的增加、更新和迭代,这个软…

【小程序 - 基础】页面导航、页面事件、生命周期、WXS脚本_04

目录 一、页面导航 1. 什么是页面导航 2. 小程序中实现页面导航的两种方式 2.1 声明式导航 2.1.1 导航到 tabBar 页面 2.1.2 导航到非 tabBar 页面 2.1.3 后退导航 2.2 编程式导航 2.2.1 导航到 tabBar 页面 2.2.2 导航到非 tabBar 页面 2.2.3 后退导航 2.3. 导航…

从1开始的Matlab(快速入门)

MATLAB软件版本&#xff1a;MATLAB R2016b 本文是博主从零开始学Matlab的记录&#xff0c;适合第一次接触Matlab的同学阅读。 一、基础介绍 1.1界面认识 1.2变量命名 注&#xff1a;Matlab中的注释 %% 独占一行的注释&#xff08;有上下横线分割&#xff09; % 普通注释 …

C语言:选择+编程(每日一练Day9)

目录 选择题&#xff1a; 题一&#xff1a; 题二&#xff1a; 题三&#xff1a; 题四&#xff1a; 题五&#xff1a; 编程题&#xff1a; 题一&#xff1a;自除数 思路一&#xff1a; 题二&#xff1a;除自身以外数组的乘积 思路二&#xff1a; 本人实力有限可能对…

stm32 - 串口

stm32 - 串口 OLED显示屏 OLED显示屏 oled.h #ifndef __OLED_H #define __OLED_Hvoid OLED_Init(void); void OLED_Clear(void); void OLED_ShowChar(uint8_t Line, uint8_t Column, char Char); void OLED_ShowString(uint8_t Line, uint8_t Column, char *String); void OLE…

深入理解 Swift 新并发模型中 Actor 的重入(Reentrancy)问题

问题现象 我们知道,Swift 5.5 引入的新并发模型极大简化了并行逻辑代码的开发,更重要的是:使用新并发模型中的 Actor 原语可以大大降低并发数据竞争的可能性。 不过,即便 Actor 有如此神奇之功效,它也不是“万能药”,仍不能防止误用带来的问题。比如:Actor 重入(Reen…

283. 多边形,《算法竞赛进阶指南》,

283. 多边形 - AcWing题库 “多边形游戏”是一款单人益智游戏。 游戏开始时&#xff0c;给定玩家一个具有 N 个顶点 N 条边&#xff08;编号 1∼N&#xff09;的多边形&#xff0c;如图 1 所示&#xff0c;其中 N4 每个顶点上写有一个整数&#xff0c;每个边上标有一个运算符…

数据分析方法:RFM模型

一、RFM基本原理 RFM是三个单词的缩写&#xff1a; 最近一次消费时间&#xff08;Recency&#xff09;&#xff0c;取数的时候一般取最近一次消费记录到当前时间的间隔&#xff0c;比如&#xff1a;7天、30天、90天未到店消费&#xff1b;直观上&#xff0c;一个用户太久不到…

MySql进阶篇---006:存储引擎,索引,SQL优化,视图、存储过程、变量、流程控制、游标、存储函数、触发器

1. 存储引擎 1.1 MySQL体系结构 1).连接层 最上层是一些客户端和链接服务&#xff0c;包含本地sock 通信和大多数基于客户端/服务端工具实现的类似于TCP/IP的通信。主要完成一些类似于连接处理、授权认证、及相关的安全方案。在该层上引入了线程池的概念&#xff0c;为通过认证…

DETR中的问题汇总(代码)

一、讲述一下torch.tensor()和torch.as_tensor()的区别 torch.tensor() 和 torch.as_tensor() 都是 PyTorch 中用于创建张量&#xff08;Tensor&#xff09;的函数&#xff0c;但它们有一些区别&#xff0c;主要涉及到张量的内存管理方式和数据拷贝。以下是它们的主要区别&…