MSVC X64 函数中的 RSP, RBP 和 Calling Convention

上一篇 博文提到了 X64 下 MSVC 如何传递参数,但是没有涉及到当参数个数大于 4 的时候如何分配内存空间的问题,接下来我们来探究这个问题。

RSP 和 RBP

按照上面提到的博文,我们进行如下实验:

  1. 所有参数都是 struct Arg, 并且 sizeof(Arg) == 16
  2. 实验中,参数的个数 k = 6, 7, 8,先观察汇编后 rsprbp 寄存器的变化。

实验:
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
  1. k = 7
; 28   : void Call() {
$LN3:
    00000   40 55        push    rbp
    00002   56       push    rsi
    00003   57       push    rdi
    00004   48 81 ec 60 04
    00 00        sub     rsp, 1120      ; 00000460H
    0000b   48 8d 6c 24 40   lea     rbp, QWORD PTR [rsp+64]
    00010   48 8b fc     mov     rdi, rsp
  1. k = 8
; 28   : void CallFuck() {

$LN3:
    00000   40 55        push    rbp
    00002   56       push    rsi
    00003   57       push    rdi
    00004   48 81 ec c0 04
    00 00        sub     rsp, 1216      ; 000004c0H
    0000b   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

本篇基于实验得出,除了某些叫法不同,和微软的官方文档是一致的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章