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.hqwc.cn/news/123913.html

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

相关文章

《2023年中国科技论文报告》解读:高校-浙大TOP1,企业-华为TOP1

最近小编看到中国科学技术信息研究所发布了《2023年中国科技论文统计报告》&#xff0c;里面有很多有趣的数据&#xff0c;摘取部分跟大家分享&#xff0c;更多详细内容&#xff0c;请点击文章底部“阅读原文”下载原文件。 第一点&#xff1a;在高水平国际期刊论文排名中&…

【数据结构】排序(2)—冒泡排序 快速排序

目录 一. 冒泡排序 基本思想 代码实现 时间和空间复杂度 稳定性 二. 快速排序 基本思想 代码实现 hoare法 挖坑法 前后指针法 时间和空间复杂度 稳定性 一. 冒泡排序 基本思想 冒泡排序是一种交换排序。两两比较数组元素&#xff0c;如果是逆序(即排列顺序与排序后…

基于matlab创作简易表白代码

一、程序 以下是一个基于MATLAB的简单表白代码&#xff1a; % 表白代码 clc; % 清除命令行窗口 clear; % 清除所有变量 close all; % 关闭所有图形窗口 % 输入被表白者的名字 name input(请输入被表白者的名字&#xff1a;, s); % 显示表白信息 fprintf(\n); fprintf(亲爱的…

visual studio禁用qt-vsaddin插件更新

visual studio里qt-vsaddin插件默认是自动更新的&#xff0c;由于qt-vsaddin插件新版本的操作方式与老版本相差较大&#xff0c;且新版本不稳定&#xff0c;容易出Bug&#xff0c;所以需要禁用其自动更新&#xff0c;步骤如下&#xff1a;     点击VS2019菜单栏上的【扩展】–…

SDL2绘制ffmpeg解析的mp4文件

文章目录 1.FFMPEG利用命令行将mp4转yuv4202.ffmpeg将mp4解析为yuv数据2.1 核心api: 3.SDL2进行yuv绘制到屏幕3.1 核心api 4.完整代码5.效果展示 本项目采用生产者消费者模型&#xff0c;生产者线程&#xff1a;使用ffmpeg将mp4格式数据解析为yuv的帧&#xff0c;消费者线程&am…

Unity实现设计模式——状态模式

Unity实现设计模式——状态模式 状态模式最核心的设计思路就是将对象的状态抽象出一个接口&#xff0c;然后根据它的不同状态封装其行为&#xff0c;这样就可以实现状态和行为的绑定&#xff0c;最终实现对象和状态的有效解耦。 在实际开发中一般用到FSM有限状态机的实现&…

以太网基础学习(二)——ARP协议

一、什么是MAC地址 MAC地址&#xff08;英语&#xff1a;Media Access Control Address&#xff09;&#xff0c;直译为媒体访问控制位址&#xff0c;也称为局域网地址&#xff08;LAN Address&#xff09;&#xff0c;MAC位址&#xff0c;以太网地址&#xff08;Ethernet Addr…

231003-四步MacOS-iPadOS设置无线竖屏随航SideCar

Step 0&#xff1a;MacOS到iPad无线竖屏随航显示&#xff0c;最终效果 Step 1&#xff1a; 下载 Better Display Step 2&#xff1a;在设置中新建虚拟屏幕&#xff0c;创建虚拟屏幕 Step 3&#xff1a;进行如下设置 Step 4&#xff1a;注意事项 ⚠️ 设置后的虚拟屏幕与Sideca…

【Java 进阶篇】JDBC查询操作详解

在数据库编程中&#xff0c;查询是一项非常常见且重要的操作。JDBC&#xff08;Java Database Connectivity&#xff09;提供了丰富的API来执行各种类型的查询操作。本篇博客将详细介绍如何使用JDBC进行查询操作&#xff0c;包括连接数据库、创建查询语句、执行查询、处理结果集…

辅助驾驶功能开发-测试篇(2)-真值系统介绍

1 真值系统概述 1.1 真值评测系统核心应用 快速构建有效感知真值,快速完成感知性能评估,快速分析感知性能缺陷。 主要应用场景包括: 1. 感知算法开发验证: 在算法开发周期中,评测结果可以作为测试报告的一部分,体现算法性能的提升。 2. 遴选供应…

asp.net core 远程调试

大概说下过程&#xff1a; 1、站点发布使用Debug模式 2、拷贝到远程服务器&#xff0c;以及iis创建站点。 3、本地的VS2022的安装目录&#xff1a;C:\Program Files\Microsoft Visual Studio\2022\Professional\Common7\IDE下找Remote Debugger 你的服务器是64位就拷贝x64的目…

数据挖掘实验(一)数据规范化【最小-最大规范化、零-均值规范化、小数定标规范化】

一、数据规范化的原理 数据规范化处理是数据挖掘的一项基础工作。不同的属性变量往往具有不同的取值范围&#xff0c;数值间的差别可能很大&#xff0c;不进行处理可能会影响到数据分析的结果。为了消除指标之间由于取值范围带来的差异&#xff0c;需要进行标准化处理。将数据…