MSVC X64 函数中的 RSP, RBP 和 Calling Convention
上一篇 博文提到了 X64 下 MSVC 如何传递参数,但是没有涉及到当参数个数大于 4 的时候如何分配内存空间的问题,接下来我们来探究这个问题。
RSP 和 RBP
按照上面提到的博文,我们进行如下实验:
- 所有参数都是
struct Arg
, 并且sizeof(Arg) == 16
。 - 实验中,参数的个数
k = 6, 7, 8
,先观察汇编后rsp
和rbp
寄存器的变化。
实验:
1. k = 6
; 28 : void Call() {$LN3:
00000 40 55 push rbp
00002 56 push rsi
00003 57 push rdi
00004 48 81 ec f0 03
00 00 sub rsp, 1008 ; 000003f0H
0000b 48 8d 6c 24 30 lea rbp, QWORD PTR [rsp+48]
00010 48 8b fc mov rdi, rsp
- k = 7
; 28 : void Call() {
$LN3:00000 40 55 push rbp00002 56 push rsi00003 57 push rdi00004 48 81 ec 60 0400 00 sub rsp, 1120 ; 00000460H0000b 48 8d 6c 24 40 lea rbp, QWORD PTR [rsp+64]00010 48 8b fc mov rdi, rsp
- k = 8
; 28 : void CallFuck() {$LN3:00000 40 55 push rbp00002 56 push rsi00003 57 push rdi00004 48 81 ec c0 0400 00 sub rsp, 1216 ; 000004c0H0000b 48 8d 6c 24 40 lea rbp, QWORD PTR [rsp+64]
这里我们需要明确一点,就是 rbp
的意义和 rsp
的意义是什么。
rbp
是属于当前函数的栈空间基地址,rsp
是包含当前函数为被调用函数准备的栈空间的基地址。
这点可以在汇编代码中看出来。
我们可以看出,当 k=6 的时候,MSVC 利用 lea rbp, QWORD PTR [rsp+48]
使得 rbp == rsp + 48
k = 7, 8 时 rbp == rsp + 64
根据 X64 下的传参规则,当 sizeof(struct)
不为 8, 16, 32, 64 bits 时,将指向参数本体的指针放在对应的位置,因此,一个参数(这里指这个指针,64 bits = 8 bytes)在栈上所占用的内存应该为 M = 8 * (#arg - 4)
。
所以, k = 6, M = 16; k = 7, M = 24; k = 8, M = 32; 再加上分配的 32 bytes 影子空间,那么应该是
k = 6, rbp == rsp + 48
k = 7, rbp == rsp + 56
k = 8, rbp == rsp + 64·
实际情况呢,k = 7 我们和编译器结果不一样,实际情况是 rbp == rsp + 64
,原因在于
为这些聚合类型作为指针的传递 (包括__m128),调用方分配的临时内存将是 16 字节对齐。
因此这些指针虽然单个大小是 8,所以在奇数个的时候为了对齐要额外增加 8 !
Calling Convention
根据这个简单的调用约定,我们可以画出64位下函数调用时到底是个怎么样的内存结构。(时机应该是 call
指令执行完毕)
这张图片认真理解,就解决了所有调用时对于内存空间如何分配的疑问。
See also:
https://docs.microsoft.com/zh-cn/cpp/build/stack-allocation
本篇基于实验得出,除了某些叫法不同,和微软的官方文档是一致的。
MSVC X64 函数中的 RSP, RBP 和 Calling Convention相关推荐
- 函数的隐式声明 及 rsp,rbp,被调用者和调用者保存的寄存器standerd manuel(Caller/Callee - saved registers)
函数的隐式声明 https://blog.csdn.net/liangbo930522/article/details/73733415 mpx-linux64-abi.pdf https://sof ...
- rsp rbp 寄存器用途
概念 在最新的 x86_x64 架构中,通常用 rbp.rsp 这两个寄存器来保存进程栈的状态(需要硬件支持). 其中 rbp 保存的是栈中当前执行函数的基本地址,当前执行函数所有存储在栈上的数据都要 ...
- C#利用lambda在函数中创建内部函数
有使用过JS的朋友,相信都知道function.JS中的function是可以在里面在定义一个作为内部使用的.有时为了控制作用域,或者这种小函数只在这个函数体内会使用,所以就不希望在外部在作额外的定义 ...
- 在c语言中,函数中的自动变量可以赋初值,每调用一次,赋一次初值,计算机二级考试 程序设计基础试题及答案三...
<程序设计基础>考试试卷三 1. 判断下面标识符中哪个是C语言中合法的用户定义标识符: A. a-b B. #abd C. typedef D. _max 2. -b?在内存中占个字节,& ...
- c语言实验至少包括四个函数中,C语言实验报告《函数》
学号:__________ 姓名:__________ 班级:__________ 日期:__________ 指导教师:__________ 成绩:__________ 实验 ...
- java用if语句调用方法_J2SE中main函数中的if语句想要调用另一个类的方法怎么能实现?...
日常生活中,要完成一件复杂的功能,我们总是习惯把"大功能"分解为多个"小功能"以实现.在C++程序的世界里,"功能"可称呼为"函数 ...
- ACMNO.41C语言-数字调序 有n个整数,使前面各数顺序向后移m个位置,最后m个数变成前面m个数,见图。写一函数:实现以上功能,在主函数中输入n个数和输出调整后的n个数
题目描述 有n个整数,使前面各数顺序向后移m个位置,最后m个数变成前面m个数,见图. 写一函数:实现以上功能,在主函数中输入n个数和输出调整后的n个数. 输入 输入数据的个数n n个整数 移动的位置m ...
- ACMNO.27 Python的两行代码解决 C语言-字符逆序 写一函数。使输入的一个字符串按反序存放,在主函数中输入输出反序后的字符串。 输入 一行字符 输出 逆序后的字符串
题目描述 写一函数,使输入的一个字符串按反序存放,在主函数中输入输出反序后的字符串. 输入 一行字符 输出 逆序后的字符串 样例输入 123456abcdef 样例输出 fedcba654321 来源 ...
- ACMNO.26 C语言-字符统计2 编写一函数,由实参传来一个字符串,统计此字符串中字母、数字、空格和其它字符的个数,在主函数中输入字符串以及输出上述结果。 只要结果,别输出什么提示信息。
题目描述 编写一函数,由实参传来一个字符串,统计此字符串中字母.数字.空格和其它字符的个数, 在主函数中输入字符串以及输出上述结果. 只要结果,别输出什么提示信息. 输入 一行字符串 输出 统计数据, ...
最新文章
- Self Ogannizing Maps(SOM)自组织映射
- golang重复声明变量
- 非监督HMP算法的物体识别
- SQL Server 监控统计阻塞脚本信息
- java 连接linux失败,Jenkins连接Linux失败
- 【渝粤题库】国家开放大学2021春1070组织行为学题目
- GCC笔记 命令行分析
- ZXing生成二维码
- C++新特性探究(18.2):C++11 unique_ptr智能指针详解
- 我和Django那些事儿(8)----相册django插件photologue,jQuery插件Slides
- redis 集群_Redis集群部署
- 五分钟带你玩转rabbitmq(八)【真实案例】消息消费失败如何处理?
- 计算机无法启动怎么重装系统,韩博士教你如何为系统崩溃无法开机的电脑重装系统...
- 如何用美剧真正提升你的英语水平?
- 【排序算法】基数排序:LSD 与 MSD
- matlab含未知数矩阵方程组,solve函数求解含有未知数的矩阵方程问题
- linux查找文件中的字符串
- DRAG DROP
- 几种常见的软件授权模式
- Groups CodeForces - 1598B