x64 堆疊使用方式
RSP 目前位址以外的所有記憶體都會被視為揮發性:OS 或調試程式可能會在使用者偵錯會話或中斷處理程式期間覆寫此記憶體。 因此,必須先設定 RSP,才能嘗試讀取或寫入堆疊框架的值。
本節討論局部變數和 alloca 內部函數的堆疊空間配置。
堆疊配置
函式的初構負責配置局部變數的堆疊空間、儲存的緩存器、堆疊參數和緩存器參數。
參數區域一律位於堆棧底部(即使 alloca
已使用),因此在任何函式呼叫期間,它一律會與傳回位址相鄰。 它至少包含四個專案,但一律有足夠的空間來保存任何可能呼叫的函式所需的所有參數。 請注意,即使參數本身從未回到堆棧,也一律會為緩存器參數配置空間;被呼叫者保證已為其所有參數配置空間。 緩存器自變數需要住家位址,因此當呼叫的函式需要取得自變數清單位址(va_list)或個別自變數時,可以使用連續區域。 此區域也提供方便的位置,可在 Thunk 執行期間儲存緩存器自變數,並做為偵錯選項(例如,如果自變數儲存在初構程式代碼中的主位址,在偵錯期間很容易找到這些自變數)。 即使呼叫的函式少於 4 個參數,這些 4 個堆疊位置仍由呼叫的函式有效擁有,而且除了儲存參數緩存器值之外,呼叫的函式也可以用於其他用途。 因此,呼叫端可能不會在函式呼叫的堆疊區域中儲存資訊。
如果在函式中動態配置空間 (alloca
),則必須使用非volatiatile 緩存器做為框架指標,以標記堆疊固定部分的基底,而且該緩存器必須在初構中儲存和初始化。 請注意,使用 時 alloca
,來自相同呼叫端的相同被呼叫端的呼叫可能會有不同的住家位址,以取得其緩存器參數。
堆疊一律會保持 16 位元組的對齊,除了在 prolog 內以外(例如,在推送傳回地址之後),以及特定框架函式類別的函式函式的函式類型中所指出的位置除外。
以下是堆疊配置範例,其中函式 A 會呼叫非分葉函式 B。函式 A 的初構已為 B 在堆疊底部所需的所有緩存器和堆疊參數配置空間。 呼叫會推送傳回位址,而 B 的初構會為其局部變數、非揮發緩存器配置空間,以及呼叫函式所需的空間。 如果 B 使用 alloca
,則會在局部變數 / 非volatile 快取器儲存區域與參數堆疊區域之間配置空間。
當函式 B 呼叫另一個函式時,傳回位址會推送到 RCX 的主位址下方。
動態參數堆疊區域建構
如果使用框架指標,則選項會以動態方式建立參數堆棧區域。 這目前在 x64 編譯程式中尚未完成。
函數類型
基本上有兩種類型的函式。 需要堆疊框架的函式稱為 框架函式。 不需要堆疊框架的函式稱為 分葉函式。
框架函式是配置堆疊空間、呼叫其他函式、儲存非大量緩存器或使用例外狀況處理的函式。 它也需要函式數據表專案。 框架函式需要初構和表結。 框架函式可以動態配置堆疊空間,並採用框架指標。 框架函式具有此呼叫標準的完整功能。
如果框架函式未呼叫另一個函式,則不需要對齊堆疊(在區段 堆疊配置中參考)。
分葉函式是不需要函式數據表專案的函式。 它無法變更任何非揮發性緩存器,包括 RSP,這表示它無法呼叫任何函式或配置堆棧空間。 允許在堆疊執行時保持未對齊。
malloc 對齊
malloc 保證會傳回適當對齊的記憶體,以儲存任何具有基本對齊且可能符合所配置記憶體數量的物件。 基本對齊方式是一種對齊方式,小於或等於實作所支援的最大對齊方式,而不需要對齊規格。 (在 Visual C++ 中,這是 或 8 個字節所需的 double
對齊方式。在以64位平臺為目標的程式代碼中,它是16個字節。例如,四位元組配置會在支援任何四位元組或更小物件的界限上對齊。
視覺C++允許具有 延伸對齊的類型,也稱為 過度對齊 的類型。 例如,SSE 型 別__m128 和 __m256
,以及使用 __declspec(align( n ))
where n
大於 8 宣告的型別已擴充對齊方式。 不保證 malloc
界限上的記憶體對齊方式適用於需要延伸對齊的物件。 若要配置過度對齊類型的記憶體,請使用 _aligned_malloc 和相關函式。
alloca
_alloca必須對齊 16 位元組,而且需要另外才能使用框架指標。
如堆疊配置中所述,所配置的堆疊必須包含後續呼叫函式之參數之後的空間。