【娱乐向】教你用汇编语言写一个 Hello World
教你用汇编语言写一个Hello World
平台
OS: Ubuntu 18.04 STL
Platform: i386
Editor:vim
Get Started
我们知道,高级语言的源码文件都有自己的格式,比如,C的后缀:.c ,java的后缀: .java , C++的后缀:.cpp等等。
在汇编语言程序设计时,我们采用 .asm后缀的格式(代表assembly)来编辑源码。
vim hello_world.asm
编辑源码
在i386汇编中,不同的section有不同的作用,
它们在被打包成时被链接(linked)
.asm文件中, 表示注释采用 ;符号
我们来看一下要用到的代码块(section):
section .text:
.text区域存放所有将会被执行的指令
section .data:
.data区域存放用户自定义的变量
变量
接下来,我们在.data中定义需要输出的变量
section .data: message: db "Hello World!" , 0xA message_length equ $-message
解释一下这段代码:我们定义了一个变量叫做 message
db意思是 define byte,这相当于某些高级语言的类型声明,只不过这里更像是一个函数调用(function call),因为有参数紧跟其后 。
我们 输入我们自定义的值:"Hello World!"
这相当于第一个参数。
输入第二个参数,这会是一个16进制数(hex),用来代表回车。
我们输入0xA,这在高级语言中相当于 作为换行符(newline)
程序结构
定义好变量后,该动手写主体逻辑了:
之前讲过,.text代码块规定了主体逻辑,
我们需要定义在寄存器register(register见计算机组织架构)的内容来完成这个程序
mov指令参数: register , value,就是告诉cpu:“把value值搬进对应的寄存器register中。”
简单说明一下这里的系统调用,这些系统调用的hex值可以在manual上查到 Linux 输入指令 locate unistd_32.h找到这个文件 随后我们vim打开这个文件查看系统调用的编码 #define __NR_write 4 查到write的编码为4 其他调用的查询以此类推
关于这里调用的原理,可以看这篇:
我们完成如下的代码:
section .text:
mov eax, 0x4 ;调用system call write()
mov ebx, 1 ;定义标准输出stdout为返回值,1是stdout的file describer
mov ecx, message ;在缓冲区存放变量message
mov edx, message_length ;存放变量长度
int 0x80 ;调用系统调用 write()
;int 代表了interrupt系统中断,此时中断返回值为1
mov eax, 0x1 ;调用system call exit()
mov ebx, 0 ;返回一个0,这里可以是任意值
int 0x80 ;中断指令
定义入口
global _start
这里的入口相当于 C / C++ 语言的main函数,告诉CPU程序的入口。
我们把_start放到逻辑开始位置
section .text:
_start ;定义程序入口
mov eax, 0x4 ;调用system call write()
mov ebx, 1 ;定义标准输出stdout为返回值,1是stdout的file describer
mov ecx, message ;在缓冲区存放变量message
mov edx, message_length ;存放变量长度
int 0x80 ;调用系统调用 write()
;int 代表了interrupt系统中断,此时中断返回值为1
mov eax, 0x1 ;调用system call exit()
mov ebx, 0 ;返回一个0,这里可以是任意值
int 0x80 ;中断指令
汇编、链接
vim输入:wq退出到shell命令行中,并保存所写的内容
命令行输入
nasm -f elf32 -o hello_world.o hello_world.asm # 汇编源码,-f 指定了目标文件的类型 -o 即output指定了输出的对象文件名
如果一切顺利,则没有任何输出
随后我们链接文件,生成可执行文件
ld -m elf_i386 -o hello_world hello_world.o
我们执行
./hello_world
如果输出
Hello World!
那就大功告成了!
致谢
John Hammond
