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

本篇基於實驗得出,除了某些叫法不同,和微軟的官方文檔是一致的。

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