i386系统共有256个中断,中断号0-255。内存中有一块地址存放中断描述符表 Interrupt Descriptor Table (IDT),中断发生时,系统根据中断描述符表跳转到指定的地址执行。如何处理中断那是内核的事情。
中断分为硬件中断、软件中断和异常。它们共用中断描述符表,也即是256个中断中的一个。
Linux的系统调用使用0x80号软中断。下述指令触发系统调用软中断。
1 | int $0x80 |
Linux有很多系统调用,每个系统调用都有一个id,通过%EAX寄存器存储该id。
参数通过寄存器传递,依次存在下述寄存器中。
ebx ecx edx esi edi ebp
如果参数超过6个,则将第一个参数的地址存储在EBX寄存器中。
返回值存储在EAX寄存器中。
直接贴代码,文件名syscall.s
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | .text .globl sys_open .type sys_open, @functionsys_open: pushl %ebp movl %esp, %ebp pushl %ebx # push callee-saved register to stack movl $0x05, %eax # syscall id saved at EAX movl 8(%ebp), %ebx # arg 1 pathname saved at EBX movl 12(%ebp), %ecx # arg 2 flags saved at ECX movl 16(%ebp), %edx # arg 3 mode saved at EDX int $0x80 # software interrupt popl %ebx # restore callee-saved register leave ret |
关键代码已经注释了,首先将EBX寄存器压入栈中,因为EBX是被调用者维护寄存器,而后面我们使用了,必须先保护起来,在返回之前再恢复。接着将EAX的值设置为5,open系统调用的编号为5。接着将参数拷贝到相关寄存器。这样一切准备就绪,执行int指令陷入内核。接下来就准备返回了,系统调用的返回值内核已经帮我们存到EAX寄存器了,而我们也正想返回系统调用的值,所以啥也不用做。接着就是恢复EBX寄存器,然后就是普通的返回指令了。
为了证明上述汇编代码的正确性,添加调试c代码main.c,直接贴代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | #include <stdio.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <unistd.h>extern int sys_open(char *path, int flags, mode_t mode);void main(){ int fd = sys_open("a", O_WRONLY | O_CREAT, 0600); if (fd < 0) { printf("open failed\n"); return; } printf("open ok fd %d\n", fd); close(fd);} |
它调用sys_open在当前目录创建一个a文件,权限为600。编译并执行:
1 2 3 4 5 6 7 8 9 | herbert@Lenovo:/work/assembly/i386/syscall$ cc -m32 syscall.s main.cherbert@Lenovo:/work/assembly/i386/syscall$ ./a.outopen ok fd 3herbert@Lenovo:/work/assembly/i386/syscall$ ls -ltotal 16-rw------- 1 herbert herbert 0 Jul 3 23:13 a-rwxrwxr-x 1 herbert herbert 7444 Jul 3 23:11 a.out-rw-rw-r-- 1 herbert herbert 357 Jul 3 23:12 main.c-rw-rw-r-- 1 herbert herbert 520 Jul 3 23:15 syscall.s |
可以看到创建了一个空文件a,权限是600。
其它系统调用,依葫芦画瓢即可。
[1] http://www.delorie.com/djgpp/doc/ug/interrupts/inthandlers1.html
[2] The i386 Interrupt Descriptor Table. https://www-s.acm.illinois.edu/sigops/2007/roll_your_own/i386/idt.html
[3] Chapter 9 Exceptions and Interrupts. https://pdos.csail.mit.edu/6.828/2005/readings/i386/c09.htm
[4] List of Linux/i386 system calls. http://asm.sourceforge.net/syscall.html
[5] Linux Syscall Reference. http://syscalls.kernelgrok.com/
[6] X86 Assembly/Interfacing with Linux. https://en.wikibooks.org/wiki/X86_Assembly/Interfacing_with_Linux
[7] Assembly - System Calls. https://www.tutorialspoint.com/assembly_programming/assembly_system_calls.htm