linux X64函数参数传递过程研究

news/2025/2/7 13:43:20/文章来源:https://www.cnblogs.com/peifx/p/18702430

linux X64函数参数传递过程研究 - ZhaoKevin - 博客园

基础知识

函数传参存在两种方式,一种是通过栈,一种是通过寄存器。对于x64体系结构,如果函数参数不大于6个时,使用寄存器传参,对于函数参数大于6个的函数,前六个参数使用寄存器传递,后面的使用栈传递。参数传递的规律是固定的,即前6个参数从左到右放入寄存器: rdi, rsi, rdx, rcx, r8, r9,后面的依次从 “右向左” 放入栈中。
例如:
H(a, b, c, d, e, f, g, h);

a->%rdi, b->%rsi, c->%rdx, d->%rcx, e->%r8, f->%r9

h->8(%rsp)

g->(%rsp)

实验验证

源码示例

     1	#include <stdio.h>2	#include <stdlib.h>3	int test_pass_parm(int a, int b, int c, int d, int e, int f, int g, int h)4	{5		printf("a:%0x, b:%0x c:%0x d:%0x e:%0x f:%0x g;%0x h:%0x\n", a,b,c,d,e,f,g,h);6		return 0;7	}8	int main(int argc, char **argv)9	{10		int a = 1, b= 2, c=3, d = 4, e =5, f=6,  g= 7, h =8;11		test_pass_parm(a,b,c,d,e,f,g,h);12		return 0;13	}

使用 gcc pass_parm.c -o pass_parm -g生成可执行程序 pass_parm.

反汇编 pass_parm

从中我们取出来main函数以及test_pass_parm 汇编进行分析验证。

main 汇编分析

   119	int main(int argc, char **argv)120	{121	  40057b:	55                   	push   %rbp122	  40057c:	48 89 e5             	mov    %rsp,%rbp123	  40057f:	48 83 ec 40          	sub    $0x40,%rsp //rsp栈指针下移0x40(64)个字节124	  400583:	89 7d dc             	mov    %edi,-0x24(%rbp) // main函数的第一个参数argc放置在距离栈底0x24字节处125	  400586:	48 89 75 d0          	mov    %rsi,-0x30(%rbp) // main函数的第一个参数argv放置在距离栈底0x30字节处126		int a = 1, b= 2, c=3, d = 4, e =5, f=6,  g= 7, h =8;127	  40058a:	c7 45 fc 01 00 00 00 	movl   $0x1,-0x4(%rbp) //变量a128	  400591:	c7 45 f8 02 00 00 00 	movl   $0x2,-0x8(%rbp)//变量b129	  400598:	c7 45 f4 03 00 00 00 	movl   $0x3,-0xc(%rbp)//变量c130	  40059f:	c7 45 f0 04 00 00 00 	movl   $0x4,-0x10(%rbp)//变量d131	  4005a6:	c7 45 ec 05 00 00 00 	movl   $0x5,-0x14(%rbp)//变量e132	  4005ad:	c7 45 e8 06 00 00 00 	movl   $0x6,-0x18(%rbp)//变量f133	  4005b4:	c7 45 e4 07 00 00 00 	movl   $0x7,-0x1c(%rbp) //变量g134	  4005bb:	c7 45 e0 08 00 00 00 	movl   $0x8,-0x20(%rbp) //变量h135	 // 以上汇编将main函数的局部a, b, c, d, e, f, g, h变量从左到右依次入栈136		test_pass_parm(a,b,c,d,e,f,g,h);137	  4005c2:	44 8b 4d e8          	mov    -0x18(%rbp),%r9d //传送-0x18(%rbp)(变量f) 位置的值到r9寄存器中138	  4005c6:	44 8b 45 ec          	mov    -0x14(%rbp),%r8d //传送-0x14(%rbp)(变量e) 位置的值到r8寄存器中139	  4005ca:	8b 4d f0             	mov    -0x10(%rbp),%ecx //传送-0x10(%rbp)(变量d) 位置的值到cx寄存器中140	  4005cd:	8b 55 f4             	mov    -0xc(%rbp),%edx //传送-0xc(%rbp)(变量c) 位置的值到dx寄存器中141	  4005d0:	8b 75 f8             	mov    -0x8(%rbp),%esi //传送-0x8(%rbp)(变量b) 位置的值到si寄存器中142	  4005d3:	8b 45 fc             	mov    -0x4(%rbp),%eax //暂存-0x4(%rbp)(变量a) 位置的值到ax寄存器中143	  4005d6:	8b 7d e0             	mov    -0x20(%rbp),%edi //传送-0x20(%rbp)(变量h) 位置的值到di寄存器中中转144	  4005d9:	89 7c 24 08          	mov    %edi,0x8(%rsp) //传送di寄存器的值到(变量h) 0x8(%rsp)位置145	  4005dd:	8b 7d e4             	mov    -0x1c(%rbp),%edi //传送-0x1c(%rbp)(变量g) 位置的值到di寄存器中中转146	  4005e0:	89 3c 24             	mov    %edi,(%rsp) //传送di寄存器的值到(变量g) (%rsp)位置147	  4005e3:	89 c7                	mov    %eax,%edi //最后将ax寄存器保存的a变量的值传送到di寄存器148	 // 以上汇编准备传给test_pass_parm函数的参数,然后调用test_pass_parm149	  4005e5:	e8 33 ff ff ff       	callq  40051d <test_pass_parm>150		return 0;151	  4005ea:	b8 00 00 00 00       	mov    $0x0,%eax152	}153	  4005ef:	c9                   	leaveq 154	  4005f0:	c3                   	retq   155	  4005f1:	66 2e 0f 1f 84 00 00 	nopw   %cs:0x0(%rax,%rax,1)156	  4005f8:	00 00 00 157	  4005fb:	0f 1f 44 00 00       	nopl   0x0(%rax,%rax,1)

test_pass_parm 汇编分析

    82	int test_pass_parm(int a, int b, int c, int d, int e, int f, int g, int h)83	{84	  40051d:	55                   	push   %rbp85	  40051e:	48 89 e5             	mov    %rsp,%rbp86	  400521:	48 83 ec 30          	sub    $0x30,%rsp87	  400525:	89 7d fc             	mov    %edi,-0x4(%rbp) //a88	  400528:	89 75 f8             	mov    %esi,-0x8(%rbp) //b89	  40052b:	89 55 f4             	mov    %edx,-0xc(%rbp) //c90	  40052e:	89 4d f0             	mov    %ecx,-0x10(%rbp) //d91	  400531:	44 89 45 ec          	mov    %r8d,-0x14(%rbp) //e92	  400535:	44 89 4d e8          	mov    %r9d,-0x18(%rbp) /f93	  //从寄存器恢复实参到当前函数的栈中94		printf("a:%0x, b:%0x c:%0x d:%0x e:%0x f:%0x g;%0x h:%0x\n", a,b,c,d,e,f,g,h);95	  400539:	44 8b 45 ec          	mov    -0x14(%rbp),%r8d96	  40053d:	8b 7d f0             	mov    -0x10(%rbp),%edi97	  400540:	8b 4d f4             	mov    -0xc(%rbp),%ecx98	  400543:	8b 55 f8             	mov    -0x8(%rbp),%edx99	  400546:	8b 45 fc             	mov    -0x4(%rbp),%eax100	  400549:	8b 75 18             	mov    0x18(%rbp),%esi //0x18(%rbp)的地址是上一个函数的栈顶位置 + 8,从这拿出h101	  40054c:	89 74 24 10          	mov    %esi,0x10(%rsp)102	  400550:	8b 75 10             	mov    0x10(%rbp),%esi //0x10(%rbp)的地址是上一个函数的栈顶位置,从这拿出g103	  400553:	89 74 24 08          	mov    %esi,0x8(%rsp)104	  400557:	8b 75 e8             	mov    -0x18(%rbp),%esi105	  40055a:	89 34 24             	mov    %esi,(%rsp)106	  40055d:	45 89 c1             	mov    %r8d,%r9d107	  400560:	41 89 f8             	mov    %edi,%r8d108	  400563:	89 c6                	mov    %eax,%esi109	  400565:	bf 90 06 40 00       	mov    $0x400690,%edi110	  40056a:	b8 00 00 00 00       	mov    $0x0,%eax111	 // 以上汇编准备传给printf函数的参数,然后调用printf112	  40056f:	e8 8c fe ff ff       	callq  400400 <printf@plt>113		return 0;114	  400574:	b8 00 00 00 00       	mov    $0x0,%eax115	}116	  400579:	c9                   	leaveq 117	  40057a:	c3                   	retq   

实验

使用gdb进行汇编代码调试,分别在执行汇编指令的0x4005c2、0x4005e5、0x400539、0x40056f处设置断点,查看寄存器的值以及函数栈帧中的值,验证分析的结果。

设置断点

断点1处的栈数值


断点2处的栈数值以及寄存器值

断点3处的栈数值以及寄存器值

断点4处的栈数值以及寄存器值


结论

实验结果完全验证了所分析的结果,从这次实践中可以更好地了解函数参数的传递过程以及函数调用所引起的堆栈变化,完整的调试过程见附录。

附录 完整汇编与调试过程

完整汇编代码

 

完整调试过程

 

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

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

相关文章

使用systemback封装Ubuntu系统iso镜像

25年小橘祝亲们钱财发发发,好运来来来。小橘初八就已经开工了,不知道家人们是不是也像小橘一样苦哈哈。今天给亲们分享使用systemback封装Ubuntu系统iso镜像。 一、环境部署 1.安装systembackecho "deb [arch=amd64] http://mirrors.bwbot.org/ stable main" > …

免费+数据安全!手把手教你在PC跑DeepSeek-R1大模型,小白也能秒变AI大神!

0 为啥本地部署? 在本地运行 AI 模型具有以下优势:隐私:你的数据保留在你的机器上 — — 不存在共享敏感信息的风险 成本: DeepSeek R1 可免费使用,无需订阅费或使用费 控制:无需外部依赖即可进行微调和实验1 使用Ollama 1.1 下载并运行应用程序 直达官网:1.2 选择你的平…

mkv和ass字幕文件合并

主要使用两种工具:ffmpeg和mkvmerge 操作系统:windows10及以上 ffmpeg下载:https://github.com/BtbN/FFmpeg-Builds/releases/download/latest/ffmpeg-master-latest-win64-gpl-shared.zip mkvmerge下载:https://mkvtoolnix.download/windows/releases/89.0/mkvtoolnix-64-…

【日记】我已经穿越了那层屏障(1903 字)

正文每次节后都知道,能说 “节后再说” 这句话当时有多爽(笑。现在节后有好多好多事情要做——年后再说的事,袭来!昨天是收假之后第一天,开了个会,我没去。结果他们下来都一幅晴天霹雳的样子,我十分诧异。结果一问,基本上全部的人都要轮岗了。我三月份又要跑去业务线,…

洛谷题单指南-线段树的进阶用法-P5445 [APIO2019] 路灯

原题链接:https://www.luogu.com.cn/problem/P5445 题意解读:给定一个长度为n的01串,一共有q个时刻,对于每个时刻,可能有两种操作:1. 把第x个位置取反 2. 查询a ~ b - 1之间的串在过去有多少个时刻都为1。 解题思路: 一、朴素想法 每个时刻对路灯的状态建立线段树,可以…

[LLM] ZeRO-DP技术简析

[LLM] ZeRO-DP技术简析 本文对ZeRO: Memory Optimizations Toward Training Trillion Parameter Models中提出的ZeRO-DP进行简要总结。相关的讲解其实网上也有很多了,不过只看网上的终究还是有点走马观花,所以我还是决定自己写一篇博客,记录一下我自己的理解。这篇博客讲的不…

人工智能训练线上算力实验环境

语音识别,转文字。from modelscope.pipelines import pipeline from modelscope.utils.constant import Tasksinference_pipeline = pipeline(task=Tasks.auto_speech_recognition,model=iic/speech_paraformer-large-vad-punc_asr_nat-zh-cn-16k-common-vocab8404-pytorch,mo…

腾讯云HAI与DeepSeek携手打造私有化高效代码助手

今天,我们依然以DeepSeek-R1大模型为核心,继续探索其在实际场景中的可用性。今天的重点将放在基于DeepSeek-R1大模型,结合JetBrains IDEA 插件代码助手(CodeGPT)进行的场景应用探索。 我们将利用腾讯云HAI服务器进行私有化部署,以确保数据安全和模型的高效运行。让我们一…

技术博客架构升级:解锁高效写作新体验

最近我对自己的技术博客架构做了一次重要升级,实现了文章内容与静态网站生成器的完全解耦。这个方案让写作回归纯粹,同时保持了自动化部署的优势。以下是具体的实现方案: 🛠️ 方案架构主仓库:flowstone/flowstone.github.io​ 主仓库仅保留静态网站生成器的相关配置,果…

window10本地搭建DeepSeek R1(三) NoneType object has no attribute encode

上面两章介绍了在本地安装DeepSeek+OpenWebUI。这里介绍一下几个需要注意的地方。 1:文件上传失败,上传文件是报错:python "NoneType object has no attribute encode" 在网上找了个方法,能解决:设置语言模型:安装如图箭头的步骤安装一个语义向量模型:sentenc…

如何用sockpef测试实例之间的网络时延

本文分享自天翼云开发者社区《如何用sockpef测试实例之间的网络时延》,作者:j****n网络时延:指数据(报文/分组、比特流)从网络的一端发送到另一端所需的时间。也叫延迟或迟延。 操作步骤:分别在测试机和辅助测试机上执行一下命令,安装sockperfyum install -y autoconf a…

Orleans7.0 游戏服务器全栈开发实战

本课程目标是从零开始搭建一套基于微软Orleans和DotNetty开源方案的游戏服务器框架,框架遵守Actor模型,可以充分利用多核,方便水平扩展。并且使用.NET平台,开发和部署都非常便捷。为了演示功能,也实现了一个简单的Unity客户端框架,最后使用这个框架实现了一个井字棋的对战…