Fork me on GitHub

【linux】masm汇编系统调用说明

写在前面的话

高级语言有Java golang C等,通过系统调用访问系统的资源,那底层的汇编代码是如何运行的,此文通过汇编语言简单的说明系统调用。

 

环境准备安装nasm

osx系统通过brew安装

brew install nasm

CentOS7环境下源码安装

下载汇编编译器nasm:https://www.nasm.us/

wget https://www.nasm.us/pub/nasm/releasebuilds/2.15.05/nasm-2.15.05.tar.gz
tar -xvf nasm-2.15.05.tar.gz && cd nasm-2.15.05.tar.gz && ./configure && make && make install

Unbuntu环境下安装

sudo apt-get install nasm

 

汇编说明

一个汇编的简单例子 hello.asm

section .data
msg: db "hello world", 0x0a
len: equ $-msg
    SYS_WRITE equ 1
    STD_OUT equ 1
    SYS_EXIT equ 60

section .text
    global _start
_start:
    mov rax, SYS_WRITE
    mov rdi,STD_OUT
    mov rsi,msg
    mov rdx,len
    syscall
    jmp exit

exit:
    mov rax,SYS_EXIT
    mov rdi,0
    syscall

 

编译如下汇编文件

hello:
    nasm -f elf64 -o hello.o hello.asm
    ld -o hello -e _start hello.o

clean:
    rm hello hello.o

nasm支持的输出文件格式包括 linux的elf64 elf32以及macox的macho32 mach64等

 

使用C代码解析

#include <stdio.h>

const char *msg= "hello world\n";
const int len = 12;
int main() {
    write(1, msg, len);
    exit(0);
    return 0;
}

 

将如下C代码编译成汇编

gcc -S hello.c产生hello.s汇编文件内容如下

    .file    "hello.c"
    .globl    msg
    .section    .rodata
.LC0:
    .string    "hello world\n"
    .data
    .align 8
    .type    msg, @object
    .size    msg, 8
msg:
    .quad    .LC0
    .globl    len
    .section    .rodata
    .align 4
    .type    len, @object
    .size    len, 4
len:
    .long    12
    .text
    .globl    main
    .type    main, @function
main:
.LFB0:
    .cfi_startproc
    pushq    %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset 6, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register 6
    movl    $12, %edx
    movq    msg(%rip), %rax
    movq    %rax, %rsi
    movl    $1, %edi
    movl    $0, %eax
    call    write
    movl    $0, %edi
    call    exit
    .cfi_endproc

对比如上两个汇编,基本一致。

 

数据段 .data

数据段用于定义常量,在运行时不可改变,定义语法如下

section .data
msg: db "hello world", 0x0a
len: equ $-msg

解析:

定义数据段: section .data 

 

声明一个字符串,以换行结尾:msg: db "hello world", 0x0a

对应的C代码的

const char *msg= "hello world\n";

db的含义是定义字节 byte,每个字符是一个字节。

另外还有两个字节的dw,以及其他的

dx := DB | DW | DD | DQ | DT | DO | DY | DZ

type := BYTE | WORD | DWORD | QWORD | TWORD | OWORD | YWORD | ZWORD

声明一个长度常量,取值字符串的长度:len: equ $-msg

equ的含义是定义一个常量的符号,取值是一个常量。

对应的C代码

const int len = 12;

 

代码段 .text

代码段用于代码,代码段需要以global _start开头,告诉系统这是代码的入口,定义语法如下:

section .text
    global _start
_start:

解析:

定义代码段:section .text

定义代码的全局入口标签:global _start

代码标签开始:_start:

 

从CPU运行的角度分析代码段

CPU简洁执行步骤是 加载指令,解码指令,运行指令

 

 

 CPU在时钟驱动下,从内存加载,解码和运行指令顺序如下所示:

 

控制台标准输出字符串的汇编解析

mov rax,1
mov rdi,1
mov rsi,msg
mov rdx,len
syscall

对应C代码

write(1, hello, 12);

解析:

mov rax,1 表示将__NR_write的系统调用号赋值给寄存器RAX,对应write系统调用函数,#define __NR_write 1
mov rdi,1 表示给系统调用write传递第一个参数,参数值等于1,表示控制台标准输出stdout
mov rsi, msg 表示给系统调用write传递第二个参数,参数值等于msg字符串指针,即"hello world\n"
mov rdx, len 表示给系统调用write传递第三个参数,参数值等于msg字符串的长度,即12
syscall 表示执行系统调用write

本文使用osx和Centos7系统实验。

 

退出程序的汇编

    mov rax,60
    mov rdi,0
    syscall

对应C代码

exit(0);

解析:

mov rax,60 表示将__NR_exit的系统调用号赋值给寄存器RAX,对应exit系统调用函数,#define __NR_exit 60
mov rdi,0 表示给系统调用exit传递第一个参数,参数值等于0
syscall 表示执行系统调用exit

在linux系统上可以查看/usr/include/asm/unistd_64.h获取常用的系统调用函数的系统调用号

#define __NR_read 0
#define __NR_write 1
#define __NR_open 2
#define __NR_close 3

// ...
#define __NR_exit 60
// ...
#define __NR_pkey_free 331

 

如上就将简单的helloworld的汇编解析完毕,那么为什么要使用到rax rdi rsi rdx这些寄存器呢,

原因是CPU规定64位系统函数调用的参数传递使用的寄存器如下

RDI 传递第一个参数
RSI 传递第二个参数
RDX 传递第三个参数
RCX 传递第四个参数
R8 传递第五个参数
R9 传递第六个参数

 

对应的macos的代码如下,区别是不同系统的系统调用号不同

hello.asm

section .data
msg: db "hello world", 0x0a
len: equ $-msg
    SYS_WRITE equ 0x2000004
    STD_OUT equ 1
    SYS_EXIT equ 0x2000001

section .text
    global _start
_start:
    mov rax, SYS_WRITE
    mov rdi,STD_OUT
    mov rsi,msg
    mov rdx,len
    syscall
    jmp exit

exit:
    mov rax,SYS_EXIT
    mov rdi,0
    syscall

 

编译的Makefile

hello:
    nasm -f macho64 -o hello.o hello.asm
    ld -o hello -e _start hello.o -L /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib -lSystem

clean:
    rm hello hello.o

在macos系统上生成二进制文件需要链接 -lSystem库才可以执行。

 

更多的汇编代码可以学习nasm的汇编文档说明:

https://www.nasm.us/pub/nasm/releasebuilds/2.15.05 

参考材料:

https://0xax.blogspot.com/2014/08/say-hello-to-x64-assembly-part-1.html

https://github.com/0xAX/asm

 

祝玩的开心~

done.

posted @ 2021-08-24 15:42  Mr.YF  阅读(534)  评论(0编辑  收藏  举报