本篇介绍
本篇作为汇编系列的开篇,就先研究下汇编如何写helloworld。
内容介绍
首先新建一个asm,写入内容如下:
代码语言:javascript复制 ; hello.asm
section .data
msg db "hello, world",0
section .bss
section .text
global main
main:
mov rax,1
mov rdi,1
mov rsi, msg
mov rdx,12
syscall
mov rax,60
mov rdi,0
syscall
代码的含义稍后介绍。 再新建一个makefile文件,内容如下:
代码语言:javascript复制hello: hello.o
gcc -o hello hello.o -no-pie
hello.o: hello.asm
nasm -f elf64 -g -F dwarf hello.asm -l hello.lst
makefile可以这样看,目标hello依赖hello.o, 而hello.o又依赖hello.asm, 如果hello.asm的修改时间大于hello.o,那么hello.o下一行的命令就需要执行。
nasm就是汇编器,如果系统没有的话,需要安装一下:
代码语言:javascript复制sudo apt install build-essential nasm
-f elf64
用来指定输出文件的格式是elf64,-g
表示需要包含debug信息,-F dwarf
用来指定debug信息格式是dwarf,-l
用来生成机器码和汇编的对应文件。
这时候执行make,就会看到生成了hello程序。
-rwxrwxr-x 1 shanks shanks 16192 11月 15 00:38 hello*
-rw-rw-r-- 1 shanks shanks 190 11月 15 00:38 hello.asm
-rw-rw-r-- 1 shanks shanks 862 11月 15 00:38 hello.lst
-rw-rw-r-- 1 shanks shanks 2240 11月 15 00:38 hello.o
-rw-rw-r-- 1 shanks shanks 139 11月 15 00:36 makefile
直接执行hello, 就会看到如下输出:
代码语言:javascript复制shanks@shanks-ThinkPad-T460s:~/Documents/04workspace/02asm/ch01$ ./hello
hello, world
接下来看下代码的含义吧。 汇编程序一般由data,bss,text 3个段构成, 前面加section就是定义这是某个段。 在section .data段用来定义变量,格式如下
代码语言:javascript复制<variable name> <type> <value>
类型如下:
type | length | name |
---|---|---|
db | 8 bits | Byte |
dw | 16 bits | Word |
dd | 32 bits | Double word |
dq | 64 bits | Quaword |
名字用来引用对应内存的首地址,这样实际上可以分配的空间就可以是多个。比如代码中的msg,指向的是h地址,而h又是整个字符串的首地址,这样用msg就可以访问整个字符串了。可能已经注意到了在代码中,后面还加了一个0,整个主要是为了表示字符串结束了,并无其他含义。
text段也可以定义常量,格式如下:
代码语言:javascript复制<constant name> equ <value>
比如
pi equ 3.14
section .bss用来定义没初始化的变量,格式如下:
代码语言:javascript复制<variable name> <type> <name>
比如下面是定义了一个 20字的数组
dArray resd 20
类型如下:
type | length | names |
---|---|---|
resb | 8 bits | Byte mov rax,1 |
mov rdi,1
mov rsi, msg
mov rdx,12
resw|16 bits|Word resd|32 bits|Double word resq|64 bits|Quaword
section .txt就是代码段了,名字后加冒号就是定义了一个label,这样在代码中就可以作为跳转目标了。 再继续看显示代码:
代码语言:javascript复制 mov rax,1
mov rdi,1
mov rsi, msg
mov rdx,12
syscall
rax 是系统调用编号,1对应的正好是write,rdi是write的第一个参数,也就是显示的目的,1对应的是标准输出,rsi是需要显示的字符串,对应write的第二个参数,rdx是write的第三个参数,也就是显示的数量。 syscall就是触发系统调用。
代码语言:javascript复制 mov rax,60
mov rdi,0
syscall
接下来就是程序退出了,60对应的是exit,rdi是返回值。 这儿就能发现,系统调用的传参顺序是rdi,rsi,rdx,对应的系统调用编号查询网址。 最后再看下生成的lst文件:
代码语言:javascript复制 1 ; hello.asm
2 section .data
3 00000000 68656C6C6F2C20776F- msg db "hello, world",0
3 00000009 726C6400
4 section .bss
5 section .text
6 global main
7 main:
8 00000000 B801000000 mov rax,1
9 00000005 BF01000000 mov rdi,1
10 0000000A 48BE- mov rsi, msg
10 0000000C [0000000000000000]
11 00000014 BA0C000000 mov rdx,12
12 00000019 0F05 syscall
13 0000001B B83C000000 mov rax,60
14 00000020 BF00000000 mov rdi,0
15 00000025 0F05 syscall
最左边是地址,中间是机器码,右边是汇编指令。
这样就完成了汇编helloworld的学习了。