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, @function sys_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.c herbert@Lenovo: /work/assembly/i386/syscall $ . /a .out open ok fd 3 herbert@Lenovo: /work/assembly/i386/syscall $ ls -l total 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