分享方式:


x64 初構和終解

配置堆疊空間、呼叫其他函式、儲存非揮發性暫存器,或使用例外狀況處理的每個函式都必須有一個 prolog,其位址限制會在與個別函式資料表專案相關聯的回溯資料中描述。 如需詳細資訊,請參閱 x64 例外狀況處理 。 Prolog 會視需要將引數暫存器儲存在其主位址中、在堆疊上推送非揮發暫存器、為區域變數和暫時配置堆疊的固定部分,並選擇性地建立框架指標。 相關聯的回溯資料必須描述初構的動作,而且必須提供復原初構程式碼效果所需的資訊。

如果堆疊中的固定配置是一個以上的頁面(也就是大於 4096 個位元組),則堆疊配置可能會跨越一個以上的虛擬記憶體頁面,因此,配置必須在配置之前加以檢查。 從初構呼叫且不會終結任何引數暫存器的特殊常式會針對此目的提供。

儲存非揮發性暫存器慣用的方法,是在固定堆疊配置之前將它們移至堆疊。 如果在儲存非揮發性暫存器之前執行固定堆疊配置,則最可能需要 32 位位移才能處理儲存的暫存器區域。 (據報導,儘管推播之間隱含相依性,但註冊的推送速度與移動速度一樣快,在可預見的未來應該保持如此。非volatile 暫存器可以依任何順序儲存。 不過,第一次在初構中使用非volatiatile 暫存器必須是儲存它。

Prolog 程式碼

一般初構的程式碼可能是:

    mov    [RSP + 8], RCX
    push   R15
    push   R14
    push   R13
    sub    RSP, fixed-allocation-size
    lea    R13, 128[RSP]
    ...

此初構會將引數暫存器 RCX 儲存在其主位置、儲存非揮發暫存器 R13-R15、配置堆疊框架的固定部分,並建立框架指標,指向固定配置區域 128 個位元組。 使用位移可讓更多固定配置區域使用一位元組位移來處理。

如果固定配置大小大於或等於一頁記憶體,則必須在修改 RSP 之前呼叫協助程式函式。 此協助程式 __chkstk 會探查要配置的堆疊範圍,以確保堆疊已正確擴充。 在此情況下,先前的初構範例會改為:

    mov    [RSP + 8], RCX
    push   R15
    push   R14
    push   R13
    mov    RAX,  fixed-allocation-size
    call   __chkstk
    sub    RSP, RAX
    lea    R13, 128[RSP]
    ...

協助 __chkstk 程式不會修改 R10、R11 和條件碼以外的任何暫存器。 特別是,它會傳回 RAX 不變,並保留所有非揮發暫存器和引數傳遞暫存器未修改。

Epilog 程式碼

在函式的每個結束處都有 Epilog 程式碼。 雖然通常只有一個初構,但有許多表結。 Epilog 程式碼會將堆疊修剪為其固定配置大小(如有必要),解除配置固定堆疊配置、藉由從堆疊擷取其儲存的值來還原非揮發暫存器,並傳回 。

epilog 程式碼必須遵循一組嚴格的規則,讓回溯程式碼可靠地透過例外狀況和中斷來回溯。 這些規則可減少所需的回溯資料量,因為不需要額外的資料來描述每個表結。 相反地,回溯程式碼可以藉由透過程式碼資料流程向前掃描以識別 epilog 來判斷要執行的 epilog。

如果函式中未使用框架指標,則 epilog 必須先解除配置堆疊的固定部分、非揮發暫存器會彈出,並將控制項傳回給呼叫的函式。 例如,

    add      RSP, fixed-allocation-size
    pop      R13
    pop      R14
    pop      R15
    ret

如果函式中使用框架指標,則堆疊必須修剪成其固定配置,再執行 epilog。 此動作在技術上不是表結的一部分。 例如,下列 epilog 可用來復原先前使用的初構:

    lea      RSP, -128[R13]
    ; epilogue proper starts here
    add      RSP, fixed-allocation-size
    pop      R13
    pop      R14
    pop      R15
    ret

在實務上,使用框架指標時,沒有充分的理由在兩個步驟中調整 RSP,因此會改用下列結尾:

    lea      RSP, fixed-allocation-size - 128[R13]
    pop      R13
    pop      R14
    pop      R15
    ret

這些形式是表結的唯一法律形式。 它必須包含 add RSP,constantlea RSP,constant[FPReg] ,後面接著一連串零或多個 8 位元組暫存器快顯和 returnjmp 。 (在表結中,只能允許語句的 jmp 子集。子集是具有 ModRM 記憶體參考的 jmp 語句類別,其中 ModRM mod 域值為 00。 jmp 禁止在 epilog 中使用具有 ModRM mod 域值 01 或 10 的語句。如需允許的 ModRM 參考詳細資訊,請參閱 AMD x86-64 架構程式設計人員手冊第 3 卷:一般用途和系統指示中的表格 A-15。沒有其他程式碼可以出現。 特別是,在表結內無法排程任何專案,包括載入傳回值。

未使用框架指標時,epilog 必須使用 add RSP,constant 來解除配置堆疊的固定部分。 它可能不會改用 lea RSP,constant[RSP] 。 此限制存在,因此回溯程式碼在搜尋表結時可辨識的模式較少。

遵循這些規則可讓回溯程式碼判斷目前正在執行 epilog,並模擬執行其餘的 epilog,以允許重新建立呼叫函式的內容。

另請參閱

x64 軟體慣例