NASM汇编发起系统调用输出Hello World
文章目录
1. 汇编程序输出Hello World的4种方式
- 向显存中直接写入数据
- 调用DOS中断
- 调用BIOS中断
- 发起系统调用(System Call)
对于第一种方式,直接操作显存,进程应该是不允许直接操作显存的。我有使用代码尝试,如下代码:
SECTION .data
msg db 'Hello, World!', 0xa
len equ $ - msg
SECTION .text
global _start
_start:
mov ax, 0xb800
mov gs, ax
mov byte [gs:0x00],'h'
mov byte [gs:0x02],'e'
mov byte [gs:0x04],'l'
mov byte [gs:0x06],'l'
mov byte [gs:0x08],'o'
jmp $
可以编译和链接,但是在执行时,会报错:
Segmentation fault (core dumped)
应该是保护模式下操作系统不会让进程直接操作显存。
对于第二种方式,调用DOS中断,只能在DOS系统下使用,本质上也是向显存写入数据。
对于第三种方式,调用BIOS中断,只能在实模式下使用,本质上也是向显存中写入数据,因此也应该是不行的。
这里主要讲讲第四种方式,用汇编语言发起系统调用,在控制台输出Hello, World。
2. 汇编发起Linux系统调用
2.1 发起系统调用的步骤
汇编发起Linux系统调用,有一下几个步骤:
- 给ax(eax)寄存器赋值系统调用号
- 把参数赋值到其他寄存器,如bx(ebx),cx(ecx)等。
- 使用
int 0x80
发起中断,0x80
该中断号表示系统调用。 - 如果有返回值,结果会保存到ax(eax)。
2.2 系统调用号对应表
给ax/eax赋值不同的系统调用号,发起的是不同的系统调用,下面是不同的系统调用号对应的不同的系统调用:
系统调用名称 | eax应存放的系统调用号 | ebx寄存器参数 | ecx寄存器参数 | edx寄存器参数 |
---|---|---|---|---|
sys_exit | 1 | |||
sys_fork | 2 | struct pt_regs | ||
sys_read | 3 | unsigned int | char * | size_t |
sys_write | 4 | unsigned int | const char * | size_t |
sys_open | 5 | const char * | int | int |
sys_close | 6 | unsigned int |
2.3 发起系统调用输出Hello, World
例如下面代码Hello.asm
,调用sys_write
系统调用,输出信息:
; Hello.asm
section .data
msg db "Hello, world", 10 ; 定义字符串,字符串起始地址用符号msg表示
len equ $-msg ; 表示字符串的长度
section .text
global _start ; 声明该程序入口是_start处
_start:
mov edx, len ; 把要打印的字符串长度保存到edx寄存器
mov ecx, msg ; 把要输出的字符串地址放入ecx寄存器
mov ebx, 1 ; 1表示stdout标准输出
mov eax, 4 ; 发起sys_write系统调用
int 0x80
mov ax, 1
int 0x80 ; 发起系统调用,sys_exit
输入下面命令,将源文件Hello.asm
,编译,会生成Hello.o
文件:
nasm -f elf Hello.asm
然后输入下面命令,把Hello.o
文件进行链接,生成可执行文件Hello
:
ld -m elf_i386 Hello.o -o Hello
然后输入下面命令,执行Hello
可执行文件,成功输出Hello, World:
./Hello
过程图片如下图:
2.4 更多代码练习
如下面的代码,发起系统调用读取用户输入的数据,并输出到控制台:
;
section .data ;Data segment
userMsg db 'Please enter a number: ' ;Ask the user to enter a number
lenUserMsg equ $-userMsg ;The length of the message
dispMsg db 'You have entered: '
lenDispMsg equ $-dispMsg
section .bss ;Uninitialized data
num resb 5
section .text ;Code Segment
global _start
_start: ;User prompt
mov eax, 4
mov ebx, 1
mov ecx, userMsg
mov edx, lenUserMsg
int 80h
;Read and store the user input
mov eax, 3
mov ebx, 2
mov ecx, num
mov edx, 5 ;读取5个字符(包括回车)
int 80h
;Output the message 'The entered number is: '
mov eax, 4
mov ebx, 1
mov ecx, dispMsg
mov edx, lenDispMsg
int 80h
;Output the number entered
mov eax, 4
mov ebx, 1
mov ecx, num
mov edx, 5
int 80h
; Exit code
mov eax, 1
mov ebx, 0
int 80h