爲什麼32位下的系統調用要用asmlinkage
asmlinkage是告訴編譯器參數將通過堆棧傳遞。kernel 裏的system call函數中(C 函數),爲什麼每一個函數原型的前面都有一個"asmlinkage" 的tag?例如:
asmlinkage long sys_nice(int increment)
“asmlinkage” 是在i386 system call 實現中相當重要的一個gcc 標籤(tag)。
當system call handler(系統調用處理程序)要call相對應的system call routine(系統調用例程)時,便將一般用途的暫存器的值push 到stack 裏,因此system call routine 就要從stack來讀取system call handler 傳遞的參數。這就是asmlinkage 標籤的用意。因爲32體系結構,
system call handler 是assembly code,system call routine(例如:sys_nice)是C code,當assembly code 調用C function,並且是以stack 方式傳參數(parameter)時,在C function 的prototype(原型)前面就要加上"asmlinkage "。
加上"asmlinkage" 後,C function 就會由stack 取參數,而不是從register 取參數。
對於未從彙編代碼調用的C函數,我們可以安全地假定默認調用約定爲cdecl(或快速調用stacall ,這無關緊要,因爲gcc會做好調用方和被調用方參數傳遞的工作。默認調用約定可以是在編譯時指定)。但是,對於從彙編代碼調用的C函數,我們應該顯式聲明該函數的調用約定,因爲在彙編端傳遞參數的代碼已固定。例如,如果將patch_espfix_desc聲明爲asmlinkage,則gcc將編譯該函數以從堆棧中檢索參數,這與將參數放入寄存器的彙編端不一致。
爲什麼所有系統調用函數都使用堆棧來傳遞參數?
原因是在處理來自用戶空間的系統調用請求時,內核無論如何都需要將所有寄存器保存到堆棧中(以便在返回用戶空間之前恢復環境),所以之後參數在堆棧上可用,不需要再做其他的工作。
另一方面,如果要將fastcall用於調用約定,則需要完成更多工作。我們首先需要知道,當用戶程序發出系統調用時,在x86-linux中,%eax保存系統調用號,%ebx,%ecx,%edx,%esi,%edi,%ebp用於將6個參數傳遞給系統調用(在“ int 80h”或“ sysenter”之前)。但是,fastcall的調用約定是在%eax中傳遞第一個參數,在%edx中傳遞第二個參數,在%ecx中傳遞第三個,其他參數從右到左推入堆棧。這樣,爲了在內核中實施這種快速調用約定,除了將所有寄存器保存在堆棧上之外,還需要做一些其他的工作。
80x86 的 assembly 有2種傳遞參數的方法:
- register method
- stack method
Register method 大多使用通用寄存器組(general-purpose)傳遞參數,這種方法的好處是簡單快速。另外一種傳遞參數的做法是使用stack(堆棧),assembly code 的模式如下:
- push number1
- push number2
- push number3
- call sum
在 ‘sum’ procedure 裏取值的方法,最簡單的做法是:
- pop ax
- pop bx
- pop cx
其它有關asmlinkage
- asmlinkage是一個定義
- "asmlinkage"被定義在/usr/include/linux/linkage.h
- 如果您看了linkage.h,會發現"attribute"這個語法,這是gcc用來定義function attribute(功能屬性)的語法。