ILD

i386汇编实战:系统调用
作者:Herbert Yuan 邮箱:yuanjp89@163.com
发布时间:2017-7-3 站点:Inside Linux Development

1 软件中断 Software Interrupt

i386系统共有256个中断,中断号0-255。内存中有一块地址存放中断描述符表 Interrupt Descriptor Table (IDT),中断发生时,系统根据中断描述符表跳转到指定的地址执行。如何处理中断那是内核的事情。

中断分为硬件中断、软件中断和异常。它们共用中断描述符表,也即是256个中断中的一个。

2 系统调用 System Call

Linux的系统调用使用0x80号软中断。下述指令触发系统调用软中断。

1
int $0x80

Linux有很多系统调用,每个系统调用都有一个id,通过%EAX寄存器存储该id。

参数通过寄存器传递,依次存在下述寄存器中。

ebx    ecx    edx    esi    edi    ebp    

如果参数超过6个,则将第一个参数的地址存储在EBX寄存器中。

返回值存储在EAX寄存器中。

3 sys_open系统调用实现

直接贴代码,文件名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/syscallls -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

Copyright © linuxdev.cc 2017-2024. Some Rights Reserved.