这篇文章介绍了如何使用nasm开始你的汇编之路,测试平台包括Mac Catalina和Ubuntu 20.04。
世界上大概没有比写汇编语言更机械性的工作了,如果有,那大概是在期末考试中默写它们。
汇编器
汇编器是将汇编语言转化为机器码的程序。或许你会以为汇编转化到机器码没什么大不了的,毕竟几乎是一对一的转换。但nasm存在的意义在于它可以很好的适应多种处理器平台,让编写汇编这件事都变得可移植了。
nasm可以在Ubuntu下汇编,使用elf32或者elf64格式(具体取决于你的机器,下同),在Mac下使用Macho64格式,在Win下使用Win64格式。在不同的机器下写汇编的影响不是很大,这多亏了nasm所追求的跨平台特性。
NASM的安装与指令
在Mac下安装nasm可以使用brew
,brew
请从tuna的国内镜像安装,方法如下
![37386b2b6f5cf5a44b6d17aa4dd14e96.png](https://i-blog.csdnimg.cn/blog_migrate/9987df6f81f53e08cb6bd8730ee4e73f.jpeg)
之后只需要
brew install nasm
在Ubuntu下可以通过apt安装
sudo apt install nasm
安装后就可以对文件进行汇编了,以helloworld.asm为例
在Mac下
nasm -f macho64 helloworld.asm
ld -e _main helloworld.o -macosx_version_min 10.13 -lSystem
在Ubuntu下
nasm -f elf64 helloworld.asm
ld -e _main helloworld.o
简单解释一下,helloworld.o
是目标文件,它几乎就是可执行文件,它离我们完整的可执行程序只差了链接这一步。
而ld
正是GUN自带的链接工具,可以将目标文件链接起来,我们这里因为只有一个文件因此不需要额外的指令。
复习一下,我们将一个写好的C程序转化为一个可以在Unix内核机器上执行的文件,需要经历下面四个步骤:
- 预处理:处理C中的预处理命令,也就是#开头的那些,默认的生成文件格式为
.i
- 编译:将C程序编译为汇编语言, 默认的生成文件格式为
.s
,这里的.s
和我们的.asm
没什么区别 - 汇编:将汇编语言转化为机器码,默认的生成文件格式为
.o
- 链接:链接动态库和静态库
因为我们直接在写汇编程序,当然就不需要第一步和第二步了。
说到GUN,不得不提今天的另一个主角,那就是GDB,GNU symbolic debugger,它是一个在Unix内核中广受好评的调试工具。
我们可以使用GDB来调试我们的汇编代码,ld
指令的默认输出文件是.out
格式的,我们可以使用GDB调试它,不过要保证你在汇编和链接的时候使用了-g
指令
nasm -f elf64 helloworld.asm -gshiy
ld -e _main helloworld.o -g
sudo gdb helloworld.out
接下来进入gdb界面,开始你的调试工作。
NASM的使用方法
nasm作为一个汇编器,定义了自己独特的汇编语言,具体的文档在它的官网可以找到
NASMwww.nasm.us总的来说,它和我们平时使用的汇编语言(我是指C和C++编译后的那个)差不多。当然它也有一些比较独特的风格,比如
- 使用
[]
来表示你的操作数是一个引用,而不是一个具体的指,这会让nasm在汇编你的代码的时候去内存找到相应的值。 - 将文件分成三部分,
.text,.data
和.bss
,分别表示代码, 常量和变量。 - 伪指令:
- 使用
RESx
和Dx
灵活的声明常量和变量。 - 使用
times
重复声明。
- 使用
- 还有一些有用的预处理指令:
- 使用宏
%macro
定义自己的指令。 - 使用
%ifdef
自定义代码段,结合Makefile生成不同版本的文件。
- 使用宏
然后就是常见的汇编指令,分支跳转、移位、加减乘除什么的。
使用GDB调试
和前面说到的一样,使用gdb
打开你的文件就好了,因为gdb
要控制另一个进程,所以别忘了给它开权限。
可以在汇编文件中设置断点,并在你想停止的地方call
它,比如
b:
ret
然后只要在gdb
中
b b
就可以让它在执行到b
的时候中止程序。
可以使用指令
layout regs
查看代码运行的位置和寄存器状态。
如果你的代码有stdout
的输出,可能会破坏这个layout的格局,这时候使用refresh
指令刷新它。
最后,可以使用x
指令查看你的内存状况,比如查看0x40201c
开始的20个bits的内存
x 20b 0x40201c
简单说这些,其实想写好汇编还是要深入的了解计算机内部的结构,以及一些约定俗成的机制,例如程序之间传递参数的方式。