Linux上可以使用的系统调用有很多。通常随着每个新的内核版本的发布,都会有新的系统调用添加到这个清单中。在/usr/include/asm/unistd.h
中记录了当前系统所支持的系统调用号,当然目前一般是根据宏定义include
/usr/include/asm/unistd_32.h
和/usr/include/asm/unistd_64.h
中的调用号。
以/usr/include/asm/unistd_64.h
中的部分内容为例:
#ifndef _ASM_X86_UNISTD_64_H
#define _ASM_X86_UNISTD_64_H 1
#define __NR_read 0
#define __NR_write 1
#define __NR_open 2
#define __NR_close 3
#define __NR_stat 4
#define __NR_fstat 5
#define __NR_lstat 6
#define __NR_poll 7
#define __NR_lseek 8
#define __NR_mmap 9
#define __NR_mprotect 10
#define __NR_munmap 11
#define __NR_brk 12
....
每个系统调用都被定义为一个名称(前面加上NR),和它的系统调用号。
在系统上的系统调用清单中找到需要的系统调用之后,通常可以在系统的man 页中找到定义。例如:
man 2 read
man 页包含4个主要部分:
- Name (名称) 显示这个系统调用的名称
- Synopsis (提要):显示如何使用这个系统调用
- Description (描述):对这个系统调用的简要描述
- Return Value (返回值) 系统调用完成时返回的值
启动系统调用,需要使用INT指令。Linux 系统调用位于中断0x80 。执行INT指令时,所有操作转移到内核中的系统调用处理程序。系统调用完成时,执行转移回INT指令之后的下一条指令(当然,除非执行了exit系统调用)。
unistd.h
文件中系统调用名称旁边列出的整数就是系统调用号。每个系统调用都被分配了唯一的数字以便标识它。在执行INT指令之前需要将期望执行的系统调用号传送到EAX寄存器中。
例如exit系统调用:
movl $1, %eax
int Ox80
unistd.h 文件中exit 系统调用的定义如下
#define _NR_exit 1
在__cdecl
函数中,输入值被存放在堆栈中,系统调用与之不同,需要输入值被存放在寄存器中。每个输入值要按照特定的顺序存放到寄存器中。把错误的输入值存放在错误的寄存器中可能导致灾难性的结果。
EAX寄存器用于保存要执行的系统调用值。不能使用EIP、EBP和ESP寄存器,因为这样可能对程序操作产生有害影响。这样就只剩下5个寄存器可以用于保存输入值。
系统调用期望的输入值顺序如下:
- EBX (第1 个参数)
- ECX (第2 个参数)
- EDX (第3 个参数)
- ESI (第4 个参数)
- EDI (第5 个参数)
需要超过6个输入参数的系统调用使用不同的方法把参数传递给系统调用。EBX 寄存器用于保存指向输入参数的内存位置的指针,输入参数按照连续的顺序存储。系统调用使用这个指针访问内存位置以便读取参数。
系统调用的返回值存放在EAX 寄存器中。
###复杂的系统调用###
系统调用sysinfo 可以用于返回关于系统如何配置以及有什么可用资源的信息。系统调用sysinfo使用单一输入值,它指向保存包含返回数据的结构的内存位置。
struct sysinfo {
long uptime; /* Seconds since boot */
unsigned long loads[3]; /* 1, 5, and 15 minute load averages */
unsigned long totalram; /* Total usable main memory size */
unsigned long freeram; /* Available memory size */
unsigned long sharedram; /* Amount of shared memory */
unsigned long bufferram; /* Memory used by buffers */
unsigned long totalswap; /* Total swap space size */
unsigned long freeswap; /* swap space still available */
unsigned short procs; /* Number of current processes */
unsigned long totalhigh; /* Total high memory size */
unsigned long freehigh; /* Available high memory size */
unsigned int mem_unit; /* Memory unit size in bytes */
char _f[20-2*sizeof(long)-sizeof(int)]; /* Padding to 64 bytes */
};
每个系统值被返回到结构内的特定位置中。必须在一个内存位置创建这个结构,以便值可以被返回到这里:
.section .data
result:
uptime :
.int 0
loadl:
.int 0
load5:
.int 0
loadl5:
.int 0
totalram:
.int 0
freeram:
.int 0
sharedram:
.int 0
bufferram:
.int 0
total swap:
.int 0
freeswap:
.int 0
totalhigh:
.int 0
freehigh:
.int 0
memunit:
.int 0
在数据定义的开头有两个标签。当对程序进行汇编时,它们都指向相同的内存位置。标签result可以用于引用整个结构,标签uptime可以用于引用结构中的第一个值。
上述笔记内容学习自 AT&T Professional Assembly Language — Chapter XII