在彙編中使用linux系統調用

在前面章節我們已經看到,啟動系統調用需要使用INT指令。linux系統調用位於中斷0x80,執行INT指令時,所有操作轉移到內核中的系統調用處理程序,完成後執行轉移到INT指令之後的下一條指令。 linux的系統調用在如下文件(32位系統)可以查看:

$ cat /usr/include/asm/unistd_32.h
#ifndef _ASM_X86_UNISTD_32_H
#define _ASM_X86_UNISTD_32_H

/*
 * This file contains the system call numbers.
 */

#define __NR_restart_syscall      0
#define __NR_exit  1
#define __NR_fork  2
#define __NR_read  3
#define __NR_write  4
#define __NR_open  5
#define __NR_close  6
#define __NR_waitpid  7
#define __NR_creat  8
#define __NR_link  9
#define __NR_unlink 10
......

上面文件中系統調用名稱後面的整數就是系統調用值,每個系統調用都被分配了唯一的數字以便標識它。在前面章節的範例中我們已經知道eax寄存器用於保存系統調用值。在執行INT指令之前,期望的值被傳送到eax寄存器中,這時,系統調用已經準備好了,接下來就是給系統調用準備參數的時候了。和我們自己寫的函數輸入值存放在堆棧中不同,系統調用的輸入參數存放在寄存器中。輸入值存放到寄存器中的順序也是很重要的,系統調用期望的輸入值順序(第1到第5個參數)如下:ebx、ecx、edx、esi、edi。需要超過6個輸入參數的系統調用使用不同方法吧參數傳遞給系統調用,使用ebx寄存器用於保存指向輸入參數的內存位置的指針。 在本系列文章前面我們打印“hello world!”一節中使用到write系統調用代碼如下:

 _start:                                 #函數在屏幕上輸出hello world!
movl $len, %edx               #第三個參數: 字符串長度
movl $msg, %ecx             #第二個參數: hello world!字符串
movl $1, %ebx                  #第一個參數: 輸出文件描述符
movl $4, %eax                  #系統調用號sys_write
int $0x80                            #調用內核功能

輸入值被依次分配到了ebx、ecx、edx寄存器中,而系統調用號4被分配到了eax寄存器中。

系統調用的返回值存放在eax寄存器中,在實際使用時,可以判斷系統調用的返回值。

注意到系統調用函數和C庫函數的是由區別的,C庫函數的輸入參數是在堆棧中,而系統調用的輸入參數在寄存器中。在是使用C庫函數還是使用系統調用函數問題上,原則是看哪一種方法更適合時間的應用。C庫的優點是在操作系統之間方便移植、C庫可以在程序之間利用共享庫,減少內存需求。linux系統調用的優點是不需要吧外部庫鏈接到程序中,可以儘可能創建最短、最快的代碼。


书籍推荐