2012-04-08 76 views
15

我正在寫一個將從彙編代碼中調用的C函數。從程序集中調用c函數需要「asmlinkage」嗎?

(具體來說,我想要做的系統調用Linux內核處理的路徑一些檢查工作,所以我會調用C功能的系統調用在entry_32.S派遣之前)

我很困惑在定義我的c函數時使用「asmlinkage」修飾符。

我知道asmlinkage是告訴編譯器參數將通過堆棧傳遞。

#define asmlinkage CPP_ASMLINKAGE __attribute__((regparm(0)))

問題:

(1)定義將在彙編代碼來調用這樣的功能時,需要asmlinkage?

(2)gcc中的默認調用約定是什麼?如果我在定義c函數時省略「asmlinkage」,它是否暗示_cdecl或fastcall? (3)如果默認的調用約定是cdecl,爲什麼需要asmlinkage,考慮到cdecl等於asmlinkage修飾符? (我在這裏糾正?)

(4)爲什麼這些系統調用函數都是用asmlinkage聲明的。我們可以先將參數複製到寄存器,然後再調用這些系統調用函數嗎?從我的角度來看,在x86中,發出系統調用時,參數很容易保存在寄存器中;那麼爲什麼還要在堆棧中保存以通過堆棧約定來強制執行這樣的傳遞參數呢?

最後,有人可以推薦一些我可以參考的混合裝配/ c編程資源/書籍嗎?

回答

15

幾個小時的研究後,我得到了以下經驗點:

(1)defning將從彙編代碼中調用這樣的功能時,需要asmlinkage?

不,實際上快速調用是經常使用的。

例如,在entry_32.S中,如果搜索「call」,則可以獲取從該程序集文件中調用的所有c函數。然後你可以看到,許多使用fastcall而不是asmlinkage作爲調用約定。例如,

/*in entry_32.S*/ 
    movl PT_OLDESP(%esp), %eax 
    movl %esp, %edx 
    call patch_espfix_desc 

    /*in traps_32.c*/ 
    fastcall unsigned long patch_espfix_desc(unsigned long uesp, 
        unsigned long kesp) 

(2)gcc中的默認調用約定是什麼?如果我在定義c函數時省略「asmlinkage」,它是否暗示_cdecl或fastcall? (3)如果默認調用約定是cdecl,爲什麼需要asmlinkage,考慮到cdecl等於asmlinkage修飾符? (我是正確的嗎?)

對於不從彙編代碼中調用C函數,我們可以放心地假設默認調用約定CDECL(或快速呼叫,也沒關係,因爲gcc將呼叫者的護理和被調用者傳遞參數,編譯時可以指定默認調用約定)。但是,對於從彙編代碼調用的C函數,我們應該顯式聲明函數的調用約定,因爲彙編端的參數傳遞代碼已經修復。例如,如果patch_espfix_desc被聲明爲asmlinkage,那麼gcc將編譯該函數以從堆棧中檢索參數。這與裝配端不一致,它將參數放入寄存器。

但是我仍然不清楚何時使用asmlinkage和何時使用fastcall。我真的需要一些指導和資源來提及。

+0

在32位x86上,內核中使用的調用約定似乎不完全是GNU fastcall(即前'2個參數 - '%ecx'和'%edx')。它受'-mregparm = 3' [GCC選項](http://gcc.gnu.org/onlinedocs/gcc/i386-and-x86_002d64-Options.html)的控制,它指示編譯器使用'%eax', '%edx','%ecx'爲前3個參數,依次。這與Borland fastcall約定相似,但仍然如此。 – Eugene 2012-04-09 07:27:32

3

我認爲這個想法是允許使用gcc選項編譯內核,這會將默認調用約定更改爲更高效的(即在寄存器中傳遞更多參數)。但是,根據所使用的gcc選項,不能允許需要從asm調用的函數在調用約定中有所不同,或者每個受支持的gcc選項集都必須有單獨的asm版本。因此,需要使用固定調用約定(恰好匹配默認值,沒有特殊的gcc選項)的函數被聲明爲具有特殊屬性,因此它們的調用約定將保持不變。

11

我想嘗試回答的問題(4)自己:

爲什麼所有的系統調用函數SYS_ *,例如sys_gettimeofday,使用堆棧來傳遞參數?

原因在於,無論如何,內核在處理來自用戶空間的系統調用請求時,需要將所有寄存器保存到堆棧(以便在返回到用戶空間之前恢復環境),因此在此之後參數可用疊加。即,它不需要額外的努力。

另一方面,如果您想爲調用約定使用fastcall,則需要完成更多工作。我們首先需要知道的是,當用戶程序發出系統調用時,在x86-linux中,%eax是系統調用號碼,而%將6個參數傳遞給系統調用(在「int 80h」或「sysenter」之前)。但是,fastcall的調用約定是傳遞%eax中的第一個參數,%edx中的第二個參數,第三個%ecx,其他人從右向左推入堆棧。通過這種方式,爲了在內核中實施這種快速調用約定,除了將所有寄存器保存在堆棧上之外,還需要以某種方式安排這些約定。