细说linux系统调用--使用c和汇编进行系统调用

来源:互联网 发布:电脑屏幕视频录制软件 编辑:程序博客网 时间:2024/06/05 05:01

linux系统如何打印”Hello World”

1. fprintf

使用fprintf输出到stdout

#include <stdio.h>int main(int argc, char *argv[]) {    fprintf(stdout, "Hello World!\n");    return 0;}

2. write

使用write将字符串输出到STDOUT_FILENO

#include <unistd.h>int main(int argc, char *argv) {    const char msg[] = "Hello World!\n";    write(STDOUT_FILENO, msg, sizeof(msg)-1);    return 0;}

3. syscall

使用glibc提供的syscall进行系统调用SYS_write将字符串输出到STDOUT_FILENO

#define _GNU_SOURCE         /* See feature_test_macros(7) */#include <unistd.h>#include <sys/syscall.h>   /* For SYS_xxx definitions */int main(int argc, char *argv[]) {    const char msg[] = "Hello World!\n";    syscall(SYS_write, STDOUT_FILENO, msg, sizeof(msg)-1);    return 0;}

汇编格式对比

Intel Code AT&T Code mov eax,1 movl $1,%eax mov ebx,0ffh movl $0xff,%ebx int 80h int $0x80 mov ebx, eax movl %eax, %ebx mov eax,[ecx] movl (%ecx),%eax mov eax,[ebx+3] movl 3(%ebx),%eax mov eax,[ebx+20h] movl 0x20(%ebx),%eax add eax,[ebx+ecx*2h] addl (%ebx,%ecx,0x2),%eax lea eax,[ebx+ecx] leal (%ebx,%ecx),%eax sub eax,[ebx+ecx*4h-20h] subl -0x20(%ebx,%ecx,0x4),%eax

4. nasm格式汇编

使用nasm格式汇编进行int 80h系统调用,nasm_write.s内容如下

; nasm -f elf64 nasm_write.s && ld -s -o nasm_write nasm_write.osection .data    hello db 'Hello world!', 10 ; ‘Hello world!’加一个换行符(10)    len equ $-hello             ; 字符串长度section .text    global _start_start:    mov eax, 4      ; 系统调用sys_write    mov ebx, 1      ; 文件描述符,标准输出    mov ecx, hello  ; 设置字符串偏移地址到ecx    mov edx, len    ; 设置字符串长度    int 80h         ; 中断0x80, kernel系统调用syscall    mov eax, 1      ; exit的系统调用(sys_exit)    mov ebx, 0      ; 退出值我为0    int 80h

编译

nasm -f elf64 nasm_write.s && ld -s -o nasm_write nasm_write.o

5. int 80h

在Linux x86和Linux x86_64系统上可以使用int $0x80制造中断0x80进行系统调用。调用参数:

Syscall # Param 1 Param 2 Param 3 Param 4 Param 5 Param 6 eax ebx ecx edx esi edi ebp Return value eax

在arch/x86/include/asm/unistd_32.h中,exit和write的syscall number为

#define __NR_exit 1#define __NR_write 4

AT&T格式汇编通过int $0x80进行系统调用

# as -o gas_int.o gas_int.s && ld -s -o gas_int gas_int.o.data   # section声明    msg: .ascii "Hello World!\n"    len = . - msg   # 字符串长度.text    .global _start_start:    movl $4, %eax    movl $1, %ebx    movl $msg, %ecx    movl $len, %edx    int $0x80    movl $1, %eax    movl $0, %ebx    int $0x80

编译:

as -o gas_int.o gas_int.s && ld -s -o gas_int gas_int.o

6. syscall

在x86_64上有专用指令进行系统调用

Syscall # Param 1 Param 2 Param 3 Param 4 Param 5 Param 6 rax rdi rsi rdx r10 r8 r9 Return value rax

在arch/x86/include/asm/unistd_64.h中相应的syscall number

#define __NR_write 1#define __NR_exit 60

文件gas_syscall.s

# as -o gas_syscall.o gas_syscall.s && ld -s -o gas_syscall gas_syscall.o.data    msg: .ascii "Hello World!\n"    len = . - msg   # 字符串长度.text    .global _start_start:    movq $1, %rax    movq $1, %rdi    movq $msg, %rsi    movq $len, %rdx    syscall    movq $60, %rax    movq $0, %rdi    syscall

编译

as -o gas_syscall.o gas_syscall.s && ld -s -o gas_syscall gas_syscall.o

7. 内联方式调用syscall

gcc -nostdlib不连接glibc库

/** * 在c语言中内联汇编,使用AT&T风格 * 编译: gcc -nostdlib inline.c -o inline *//** * @brief 通过系统调用打印字符串 */void my_printf(char *s, int len) {    long ret;    __asm__ volatile(        "int $0x80"     /* 系统调用 */        : "=a" (ret)    /* 返回值eax("a") */        : "0"(4),       /* 系统调用号sys_write */          "b"(1),       /* 参数ebx,文件句柄1=标准输出 */          "c"(s),       /* 参数ecx, 字符串 */          "d"(len)      /* 参数edx, 字符长度 */    );}/** * @brief 程序入口 */void _start() {    /* main body of program: call main(), etc */    char *s = "Hello world!\n";    my_printf(s, 13);    /* exit system call */    asm("movl $1,%eax;"        "xorl %ebx,%ebx;"        "int  $0x80"    );}

参考
+ Hello, world!
+ Linux System Call Table
+ Linux Assembly HOWTO 6.2. Hello, world!
+ X86 Assembly/Interfacing with Linux
+ X86 Assembly/NASM Syntax
+ What is better “int 0x80” or “syscall”?
+ Linux C中内联汇编的语法格式及使用方法(Inline Assembly in Linux C)

0 0
原创粉丝点击