2012年12月14日 星期五

x86-64 組合語言中使用 system call

有天,突然臨時起意想要學習組合語言,所以呢首先來熟悉一下一些基本的東西。本程式唯一的功能,是利用sys_exit,一個system call,來結束程式,並把exit code設定為0。(OS:好心酸)。程式碼如下:

.text
        .global _start
        .type _start, @function
        /*
        _start是真正的程式進入點
        在C語言中,編譯器會幫你產生一個可以導向到main的_start函數
        */
_start:
        /*
        注意:
        要把常數輸送到暫存器,使用
        mov $60, %rax
        
        使用下面的指令會把記憶體位置 60 (0x3C)的資料傳送到rax中。
        mov 60, %rax

        sys_exit, syscall number為60  (依據asm/unistd_64.h)
        第一個參數為exit code
        */
        mov $60, %rax /*syscall number傳送到 rax*/
        mov $0, %rdi  /*第一號參數傳送到 rdi*/
        syscall       /*調用sys_exit。程式在此領便當。*/
        /*
        不能用ret來結束。
        _start是「創世函數」,而ret是用來把控制權轉交給呼叫此函數的函數
        所以ret會導致不可預期的結果發生
        C語言的main可以用return(被翻譯成ret),是因為main可以回到_start
        */

想要編譯它的話請使用下面的指令:

gcc -nostartfiles exit.s

這份程式碼使用的x86-64語法是AT&T的語法(AT&T syntax)和Intel官方的語法有很大的不同。其中最重要也是最主要的差異是在運算為的排放。舉例來說,Intel syntax下,把60放到rax暫存器為:
mov rax, 60
但在AT&T syntax下則是:
mov $60, %rax
大部分的x86-64程式設計人員使用的是Intel syntax。但私心覺得AT&T syntax看起來很順眼,而且GNU assembler使用這樣的語法,所以就決定學這一套了。

回到本程式的另一個重點,system call的使用。System call是直接和Linux kernel溝通的方法,可以調用kernel所提供的一些基本功能。依據x86-64 ABI(詳細見這裡),要進行system call,首先要在rax暫存器上面填入system call number,接著,在依照rdi, rsi, rdx, r10, r8, r9的順序把參數填入。在這個程式裡面,因為sys_exit的參數只有一個,所以把它填入rdi中。完成上面的佈置後,利用syscall指令來進行system call。

沒有留言:

張貼留言