从汇编层看64位程序运行——参数传递的底层实现

大纲

  • 小于等于6个参数
    • 一个参数
      • 总结
    • 两个参数
      • 总结
    • 三个参数
      • 总结
    • 四个参数
      • 总结
    • 五个参数
      • 总结
    • 六个参数
      • 总结
  • 大于6个参数
    • 七个参数
    • 总结

在32位系统中,参数的传递主要依靠栈来进行。那么64位系统上,是否依旧符合这个规则呢?答案是“不是”。64位系统使用了寄存器和栈结合的方案。当参数比较少的时候,使用寄存器传递参数;当参数比较多时,前几个参数仍然采用寄存器传递,但是后几个参数会采用栈传递。
本文我们将探测从1到10个参数传递的汇编实现。
我们在main函数中准备10个栈上变量

int main() {int a = 10;int b = 20;int c = 30;int d = 40;int e = 50;int f = 60;int g = 70;int h = 80;int i = 90;int j = 100;

每个int型占4个字节,所以一共需要40个字节,即0x28。但是编译器为了让内存对齐,多分配了8个字节。于是我们看到编译器直接通过下面的语句表达栈上变量空间是0x30(即栈增长了0x30)。

0x000000000000135a <+8>:     sub    $0x30,%rsp

在这里插入图片描述
离rbp最远的是a变量的地址,即-0x28(%rbp)。它表达的是该地址是%rbp-0x28。使用减法的原因是,栈的增长方向是向地址空间低的方向。具体这块知识见《从汇编层看64位程序运行——程序中的栈(Stack)结构及其产生的历史原因》。
在这里插入图片描述

小于等于6个参数

如果参数的个数小于等于6个,则采用寄存器传递。

一个参数

void foo1(int a) {a = a + 5;
}

调用foo1处的汇编如下
在这里插入图片描述
它会将栈中a变量的值放到eax寄存器中,然后将eax寄存器的值放到edi寄存器中。edi就充当了参数传递的“使者”。
在这里插入图片描述
我们在foo1的汇编代码处可以看到,它将edi寄存器中的值保存到它的栈帧的地址空间中(rbp-0x04),然后才可开始做计算。
可能有人注意到,为什么调用处要先将变量值保存到eax,然后再保存到edi中,而不是直接保存到edi中呢?在这个案例中,的确是没有必要的。但是后面涉及栈传递参数时,这种设计就很有必要了。

总结

一个参数时直接使用edi寄存器传递参数。

两个参数

void foo2(int a, int b)

在这里插入图片描述
a(-0x28(%rbp))被先保存到eax寄存器中,然后eax寄存器的值保存到edi寄存器中;
b(-0x24(%rbp))被先保存到edx寄存器中,然后edx寄存器的值保存到esi寄存器中;
这意味着a的值被保存到edi,b的值被保存到esi中,然后借用这两个寄存器进行参数传递。
在这里插入图片描述

总结

两个参数时,参数分别通过edi、esi寄存器传递。

三个参数

void foo3(int a, int b, int c) 

在这里插入图片描述
第一个参数a和之前的规则一样,先保存到eax,然后再保存到edi中。
但是这次由于edx寄存器要参与参数传递,即foo3函数要使用edx寄存器。于是第二个参数值先被保存到ecx寄存器中,然后再传递给esi寄存器。
在这里插入图片描述

总结

三个参数时,参数分别通过edi、esi和edx寄存器传递。

四个参数

void foo4(int a, int b, int c, int d)

在这里插入图片描述
第一个参数a和之前的规则一样,先保存到eax,然后再保存到edi中。
而这次由于ecx也要参与参数传递,于是b(-0x24(%rbp))被直接保存到esi中、c(-0x20(%rbp))被直接保存到edx中、d(-0x1c(%rbp))被直接保存到ecx中。这次参数的传递没有经过太多中间寄存器,相对高效。
在这里插入图片描述

总结

四个参数时,参数分别通过edi、esi、edx和ecx寄存器传递。

五个参数

void foo5(int a, int b, int c, int d, int e)

在这里插入图片描述
这次针对第五个参数e,它会先被保存到edi寄存器中,然后edi寄存器的值会保存到r8d寄存器。这就意味着e被保存到r8d寄存器中。由于edi寄存器最终要传递第一个参数a,于是在调用foo5前,会将临时存储a的eax寄存器的值设置到edi寄存器中。
我们发现edi寄存器被频繁使用,而它又被当做帮助第一个参数传递的寄存器,于是编译器会先见第一个参数保存到其他寄存器中,然后在调用函数之前在将临时存储第一个参数的寄存器的值保存到edi中。
在这里插入图片描述

总结

五个参数时,参数分别通过edi、esi、edx、ecx和r8d寄存器传递。

六个参数

void foo6(int a, int b, int c, int d, int e, int f)

在这里插入图片描述
具体的方式和之前类似,只是r8d和edi都作为临时寄存器保存栈上数据,然后在调用foo6函数前,将它们还原成它们理应要去代表的参数。
在这里插入图片描述

总结

六个参数时,参数分别通过edi、esi、edx、ecx、r8d和r9d寄存器传递。

大于6个参数

如果参数的个数大于6个,则前6个仍然采用寄存器传递,后面的使用栈传递。

七个参数

void foo7(int a, int b, int c, int d, int e, int f, int g)

在这里插入图片描述
前6个参数使用edi、esi、edx、ecx、r8d和r9d寄存器传递到子函数。
第7个参数g(-0x10(%rbp))被先保存到edi(32位,属于64位rdi),然后被push到栈中。
在这里插入图片描述
在foo7函数中,栈上的变量会直接参与计算,而不用再拷贝到foo7的栈帧中。
在这里插入图片描述
8,9,10个参数和7个参数是类似的,过程就不写了。

总结

  • 参数不超过6个时,参数按需使用edi、esi、edx、ecx、r8d和r9d寄存器传递到子函数。
  • 参数超过6个参数时,前6个参数使用edi、esi、edx、ecx、r8d和r9d寄存器传递到子函数;从第7个参数起,后面的参数都通过栈传递。

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

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

相关文章

Redis6.2.1版本集群新加副本

测试数据 通过redis-benchmark生成测试数据 ./bin/redis-benchmark -h 172.31.4.18 -p 6381 -a Redis_6.2.1_Sc --cluster -t set -d 128 -n 10000000 -r 100000000 -c 200新加节点 172.31.4.18:6381> AUTH Redis_6.2.1_Sc OK172.31.4.18:6381> cluster meet 172.31.4…

Vue 组件之间的通信方式

Vue 组件之间的通信方式有多种&#xff0c;可以根据具体场景和需求选择合适的方式&#xff1a; Props / Events&#xff1a; 父组件通过 props 向子组件传递数据&#xff0c;子组件通过事件&#xff08;$emit&#xff09;向父组件发送消息。 适用于父子组件之间…

FFmpeg开发环境搭建

FFmpeg是音视频开发必备的库&#xff0c;也是唯一的库。本文主要讲解在ubuntu22和macOS14环境下的编译安装。 为什么要自己编译呢&#xff1f;其中一个很重要的原因就是ffmpeg在编译时可以加入很多插件&#xff0c;这种特定的库网络上可能找不到编译好的版本&#xff0c;另外如…

uniapp引入 uview( HBuilder 和 npm 两种安装方式) #按需引入

方式一、HBuilder 安装 uview 1.1. HBuider安装-链接-》》 1.2. 在uni.scss 中引入 import "uni_modules/uview-ui/theme.scss";1.3. main.js 引入&#xff08;import Vue from ‘vue’ 下面&#xff09; import uView from "uni_modules/uview-ui"; V…

在自定义总线下注册设备

1、自定义总线下注册设备 //my_bus_dev.c #include<linux/module.h> #include<linux/init.h> #include<linux/kernel.h> #include<linux/kobject.h> #include<linux/slab.h> #include<linux/sysfs.h> #include<linux/device.h> #in…

solidity实战练习3——荷兰拍卖

//SPDX-License-Identifier:MIT pragma solidity ^0.8.24; interface IERC721{function transFrom(address _from,address _to,uint nftid) external ; }contract DutchAuction { address payable immutable seller;//卖方uint immutable startTime;//拍卖开始时间uint immut…

Eureka 介绍与使用

Eureka 是一个开源的服务发现框架&#xff0c;它主要用于在分布式系统中管理和发现服务实例。它由 Netflix 开发并开源&#xff0c;是 Netflix OSS 中的一部分。 使用 Eureka 可以方便地将新的服务实例注册到 Eureka 服务器&#xff0c;并且让其他服务通过 Eureka 服务器来发现…

今日科技圈最新时事新闻(2024年7月12日

一、智能硬件与电子产品 小米Redmi G Pro 2024游戏本新版本发布 发布时间&#xff1a;7月12日上午10点产品亮点&#xff1a; 搭载英特尔酷睿i7-14650HX处理器&#xff0c;拥有16个核心和24个线程&#xff0c;性能释放高达130W。配备140W满血释放的RTX 4060显卡&#xff0c;提…

属于马云的时代结束了

关注卢松松&#xff0c;会经常给你分享一些我的经验和观点。 马云突然回国了&#xff0c;还出现在阿里巴巴的大厦里。大家都非常激动&#xff0c;阿里沸腾了&#xff0c;整个杭州&#xff0c;甚至全网都沸腾了&#xff0c;日本慌了&#xff0c;美国坐不住了&#xff0c;欧洲陷…

目前分布式光纤测温系统的主流架构有哪些?

分布式光纤测温技术的主流架构&#xff0c;历经多个阶段的发展和演变&#xff0c;每种架构都有其独特的特点和优势。回顾过去的发展历程&#xff0c;我们可以看到三种主要架构的演进&#xff0c;每一次创新都在不同程度上推动了技术的进步和市场的发展。 首先&#xff0c;2005…

基于节点嵌入的链接预测(暂时这样吧)

基于节点嵌入的链接预测 补充知识&#xff1a;图自编码器&#xff08;Graph Autoencoder, GAE&#xff09;:变分图自编码器&#xff08;Variational Graph Autoencoder ,VGAE&#xff09;:二级目录三级目录 补充知识&#xff1a; 用一个例子来帮助理解&#xff0c;假设我们有一…

CSS技巧专栏:一日一例 5-纯CSS实现背景色从四周向中心填充的按钮特效

特此说明 本专题专注于讲解如何使用CSS制作按钮特效。前置的准备工作和按钮的基本样式,都在本专栏第一篇文章中又详细说明。自本专栏第四篇文章起,本专栏都将直接跳过前面的内容,不再重复复制,需要了解按钮基础样式的同学,请移步:《CSS技巧 - 一日一例 (1):会讨好的热…

关键路径-matlab

路径上边的数目称为路径长度 图的基本知识 求最短路径&#xff08;Dijkstra算法&#xff09; 2. 待继续尝试 ①Dijkstra ②floyd_all.m 一 二 ③ LeetCode [329. 矩阵中的最长递增路径]

SpringCloud---zuul路由网关

zuul网关 zuul网关定义 Zuul 是netflix开源的一个API Gateway 服务器, 本质上是一个web servlet(filter)应用。Zuul 在云平台上提供动态路由&#xff0c;监控&#xff0c;弹性&#xff0c;安全等边缘服务的框架。Zuul 相当于是设备和 Netflix 流应用的 Web 网站后端所有请求的…

Kithara与OpenCV (二)

Kithara使用OpenCV QT 进行特征检测 目录 Kithara使用OpenCV QT 进行特征检测OpenCV 特征检测简介Qt应用框架简介项目说明关键代码抖动测试测试平台&#xff1a;测试结果&#xff1a;结论 OpenCV 特征检测简介 OpenCV是一个开源的计算机视觉库&#xff0c;提供了各种图像处理…

一图展示免费开源的分布式版本控制系统​Git

文章目录 前言一、安装Git二、Git配置三、git命令 前言 Git是一个开源的分布式版本控制系统&#xff0c;可以有效、高速地处理从很小到非常大的项目版本管理。也是Linus Torvalds为了帮助管理Linux内核开发而开发的一个开放源码的版本控制软件。 一、安装Git Windows操作系统…

如何更好的优化 ListView 控件的性能

&#x1f604;作者简介&#xff1a; 小曾同学.com,一个致力于测试开发的博主⛽️&#xff0c;主要职责&#xff1a;测试开发、CI/CD&#xff0c;日常还会涉及Android开发工作。 如果文章知识点有错误的地方&#xff0c;还请大家指正&#xff0c;让我们一起学习&#xff0c;一起…

MongoDB教程(四):mongoDB索引

&#x1f49d;&#x1f49d;&#x1f49d;首先&#xff0c;欢迎各位来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里不仅可以有所收获&#xff0c;同时也能感受到一份轻松欢乐的氛围&#xff0c;祝你生活愉快&#xff01; 文章目录 引言一、MongoD…

windows查看局域网所有设备ip

windows如何查看局域网所有设备ip 操作方法 一 . 在搜索栏里输入cmd 二 .在命令行黑窗口输入arp -a 三 . 最上面显示的动态地址就是所有设备ip

Redis:高性能的开源缓存数据库

简介&#xff1a; Redis&#xff08;Remote Dictionary Server&#xff09;是一个基于内存的开源缓存数据库&#xff0c;常用于缓存、消息队列、分布式锁等场景。它被设计成快速、可靠且易于使用的数据库系统&#xff0c;具有高性能、高可用、可扩展性等特点。本篇博客将介绍Re…