i386 ABI之暫存器保護規則

2021-05-18 01:07:36 字數 2565 閱讀 9154

一、保護原則

被作為函式返回值。對於呼叫者來說,如果在呼叫後必須用到呼叫前的eax的值,則呼叫者必須自己事先儲存eax,即使被呼叫函式沒有返回值也必須遵守這個原則。

而對於被呼叫函式,可以隨意的使用eax,不需要對其作任何保護。

2.ebx

對於位址無關**(position-independent code),該暫存器用被來存放全域性偏移表基位址,並可以在函式間傳遞。

但不管是否位址無關**,abi都規定呼叫者不需要保護ebx暫存器,如果被呼叫函式用到了該暫存器,則被呼叫函式必須自己保護它。

3.ecxedx

類似於eax,呼叫者在呼叫後如果需要用到呼叫前的這些暫存器的值,則呼叫者必須自己負責保護它們。

被呼叫函式可以隨意使用這些暫存器。

4.esi,edi

呼叫者不需要保護它們。如果被呼叫函式需要占用這些暫存器則必須對其進行保護。

5.ebp,esp

被用來維護調前後的堆疊平衡,因此被呼叫函式必須負責保護它們。

6.eflags

abi規定進入乙個函式前呼叫者必須保證方向標誌df必須為0(正向),而乙個函式在返回前該函式也必須保證df為0。

而對於其他的標誌位同eax一樣,被呼叫者不需要保護它們。

其他諸如浮點暫存器的規則就不介紹了。

二、呼叫規約(calling convention)

注意本文僅限於介紹c/c++的呼叫規約(包含唯一已知的通用規約__stdcall),由於個人能力有限,對於其他語言尚無法給出結論。如果有這方面的高手望賜教!

1. 微軟的__cdecl與gnu的__attribute__ ((cdecl))

__cdecl想必大家都不陌生了,gcc出於相容,可以使用__attribute__ ((cdecl))達到同樣的效果。

該種規約不使用任何暫存器傳遞引數,引數全部在棧上(從右到左依次入棧),其規則完全符合abi規定。

2. 微軟的__stdcall與gnu的__attribute__ ((stdcall))

同__attribute__ ((cdecl)),__attribute__ ((stdcall))也是用來相容微軟的。

在暫存器保護問題上同__cdecl,不使用任何暫存器傳遞引數且符合abi。

3. 微軟的__fastcall與gnu的__attribute__ ((fastcall))

同前,__attribute__ ((fastcall))用來相容微軟。

該方法使用ecx和edx傳遞前兩個引數(從左到右),其餘引數在棧上(從右到左依次入棧)。

根據abi,呼叫者不需要保護ecx和edx,因此該規約並不違反abi。

4.thiscall呼叫規約(僅c++)

必須注意,微軟的thiscall和g++的thiscall在二進位製上是不相容的。

微軟的thiscall採用ecx作為this指標,其餘引數全部在棧上(從右到左依次入棧)。

而g++的thiscall就是__cdecl,它把this指標當作第乙個引數,連同其他引數一起被放在棧上(從右到左依次入棧)。

顯然這兩種方法都不違背abi。

5. 微軟__declspec(naked)與gnu的__attribute__ ((naked))

__attribute__ ((naked))是g++對微軟的相容。

對於這種型別的函式,對於呼叫者(c/c++程式),編譯器保證其生成**是符合abi規定的。

但是被呼叫函式也就是naked函式本身,編寫者必須自己實現對abi的相容。

6. gnu的__attribute__ ((regparm(n)))

在i386下這裡的n取值只能是0、1、2、3。它表示該函式使用幾個暫存器來傳遞引數。

當n=0時,所有引數都在棧上(從右到左依次入棧)。也就是__cdecl。

當n=1時,第1個引數在eax,其餘引數在棧上(從右到左依次入棧)。

當n=2時,第1個引數在eax,第2個引數在edx,其餘引數在棧上(從右到左依次入棧)。

當n=3時,第1個引數在eax,第2個引數在edx,第3個引數在ecx,其餘引數在棧上(從右到左依次入棧)。

由於eax、edx、ecx都是不需要被呼叫者保護的暫存器,所以這裡也不違背abi規定。

7. linux核心(i386)的asmlinkagefastcall

asmlinkage被定義為__attribute__ ((regparm(0)))。

fastcall被定義為__attribute__ ((regparm(3)))。

暫存器 之 SI DI

si源變址暫存器,di目地變址暫存器,都是變址暫存器,都是在某個位址的基礎上進行偏移變化,因此都需要基址暫存器。1 si di 一般與資料段暫存器ds聯用,用來確定資料段中某一儲存單元的位址。這兩個暫存器有自動增量和自動減量的功能所以用於變址是很方便的。2 在串處理指令中si 和di作為隱含的源變址...

從暫存器看I386和x64位中函式呼叫中引數傳遞

x86 64基本使用暫存器儲存函式引數,暫存器不夠才入棧 而i386將所有引數儲存在棧上,通過gcc的擴充套件功能 attribute regparm 即可實現部分引數的暫存器傳遞。1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23...

UVM暫存器篇之五 暫存器模型的整合(下)

本文 前門訪問 利用暫存器模型,我們可以更方便地對暫存器做操作。接下來我們分別兩種訪問暫存器的方式,即前門訪問 front door 和後門訪問 back door 前門訪問,顧名思義指的是在暫存器模型上做的讀寫操作,最終會通過匯流排uvc來實現匯流排上的物理時序訪問,因此是真實的物理操作 而後門訪...