英文原版地址:NASM Assembly Language Tutorials - asmtutor.com
介绍BSS段
目前为止我们已经用了text段和data段,现在介绍下BSS段。BSS代表以符号开始的块。它是我们程序中用于在内存中为未初始化变量保留空间的一个区域。我们将使用它在内存中保留一些空间来获得用户输入,因为我们不知道多少字节需要存储。
声明变量的语法如下:
SECTION .bss
variableName1: RESB 1 ; 保留空间 1 字节
variableName2: RESW 1 ; 保留空间 1 个字(2字节)
variableName3: RESD 1 ; 保留空间 1 个双字(4字节)
variableName4: RESQ 1 ; 保留空间 1 个双精度浮点数(8字节)
variableName5: REST 1 ; 保留空间 1 个扩展精度浮点数
写代码
我们将使用系统调用sys_read来接受和处理来自用户的输入。在linux系统调用表中这个函数分配的操作码为3。就像sys_write一样,在调用该函数的软件中断之前也需要将三个参数加载进EDX,ECX和EBX。
传递的参数如下:
-
EDX将被存入内存空间的最大长度(字节数)
-
ECX将被存入在BSS段中创建的变量的地址
-
EBX将被存入我们想要写入的文件——在本例中时STDIN(标准输入)
传递参数的数据类型和定义可以在函数定义中找到。
当sys_read检测到换行符,控制返回到程序,用户输入位于你传到ECX的内存地址。
functions.asm文件
;------------------------------------------
; int slen(String message)
; 字符串长度计算函数
slen:
push ebx
mov ebx, eax
nextchar:
cmp byte [eax], 0
jz finished
inc eax
jmp nextchar
finished:
sub eax, ebx
pop ebx
ret
;------------------------------------------
; void sprint(String message)
; 字符串打印函数
sprint:
push edx
push ecx
push ebx
push eax
call slen
mov edx, eax
pop eax
mov ecx, eax
mov ebx, 1
mov eax, 4
int 80h
pop ebx
pop ecx
pop edx
ret
;------------------------------------------
; void sprintLF(String message)
; 打印字符串和换行符函数
sprintLF:
call sprint
push eax ; 当我们在这个函数中使用EAX时通过将EAX压入栈来进行保护
mov eax, 0AH ; 将0AH移到EAX中 - 0AH是换行符的ascii码
push eax ; 将换行符放到栈上,以便我们获取地址
mov eax, esp ; 将当前栈指针的地址放到EAX寄存器中给sprint函数
call sprint ; 调用sprint函数
pop eax ; 从栈上移除换行符
pop eax ; 恢复调用函数前EAX原本的值
ret ; 返回程序
;------------------------------------------
; void exit()
; 退出程序并复原资源
quit:
mov ebx, 0
mov eax, 1
int 80h
ret
hello.asm文件
%include 'functions.asm'
SECTION .data
msg1 db 'Please enter your name: ', 0h ; 告诉用户输入的消息字符串
msg2 db 'Hello, ', 0h ; 用户输入姓名后使用的消息字符串
SECTION .bss
sinput: resb 255 ; 在内存中为用户输入保留255字节空间
SECTION .text
global _start
_start:
mov eax, msg1
call sprint
mov edx, 255 ; 读取的字节数
mov ecx, sinput ; 将输入存入保留空间 (称为缓冲区)
mov ebx, 0 ; 写入标准化输入
mov eax, 3 ; 调用 SYS_READ (kernel opcode 3)
int 80h
mov eax, msg2
call sprint
mov eax, sinput ; 将缓冲区存入EAX (注: 输入包含一个换行符)
call sprint ; 调用我们的打印函数
call quit
编译命令
nasm -f elf hello.asm
链接命令
ld -m elf_i386 hello.o -o hello
执行命令
./hello