2012年12月18日 星期二

x86-64 GNU/Linux Application Binary Interface懶人包

這裡是x86-64 GNU/Linux ABI的重點整理。


核心界面(系統呼叫)

函數呼叫
參數傳遞

系統呼叫號碼(system call number)以rax來傳遞。參數依照下面的順序傳遞:

rdi, rsi, rdx, r10, r8, r9

暫存器的使用

用作參數傳遞的六個暫存器中的資料不會被kernel保留,此外,rcxr11的資料不會被kernel保留。

資料回傳

System call回傳的資料由rax傳回。小於0的數值代表在system call的過程有錯誤,錯誤碼為rax * -1

使用者界面(User-level applications)

函數呼叫
參數傳遞

指標或整數型態的參數由下列暫存器傳遞:

rdi, rsi, rdx, rcx, r8, r9

暫存器的使用

被呼叫者只有義務保存r12~r15, rbx, rsp, rbp以及x87 control word中的資料。其他暫存器中資料的命運未定。所謂的保存義務是說,函數回傳後,這些必須被保存的暫存器的數字必須和呼叫前一樣。

堆疊管理

ABI規定在進行函數呼叫的時候,rsp必須是16的倍數。換句話說,堆疊記憶體必須和16-byte對齊。見下:

xor %rax, %rax
/**此時rsp % 16 == 0*/
call foo
根據觀察:
start:
  /*此時rsp % 16 == 0*/
  ...
  call __libc_start_main
  ...
以及
main: /*或其他C語言相容的函數*/
  /*此時(rsp + 8) % 16 == 0*/
對於大部分讀者們會經手的user-mode組合語言撰寫過程,會對rsp做下面的處理。
        /*這是方法一。傳統的stack frame*/
        .equ main_frame_size, 32 /*是16的倍數的話,會比較方便管理*/
main:
        push %rbp
        sub $main_frame_size, %rsp
        /*~做點事情,像是泡咖啡~*/
        pop %rbp
        ret
另一種處理方法是:
        /*
        這是方法二,比較新被提出的方法。
        好處是,多了rbp可以利用。
        壞處是,*據說*這種方法比較慢(一丁點)
        */
        .equ main_frame_size, 40 /*是16n + 8的話,會比較方便管理*/
main:
        sub $main_frame_size, %rsp
        /*~做點事情,像是泡牛奶~*/
        add $main_frame_size %rsp
        ret
這兩種方法要用哪一種都可以。

總結

總之在撰寫C語言相容函數的時候一定要注意:

  • 在呼叫其他函數之前,請記得保存在暫存器中的資料,以免發生遺憾。
  • 在撰寫一個C語言相容的函數時,如果要使用r12~r15, rbx, rsp, rbp,記得先做備份。在離開函數之前請務必把裡面的資料還原回去。

沒有留言:

張貼留言