在[0x02]和[0x03]中,举了一个打印hello, world!的例子,然后介绍了在nasm中的寄存器,以及一些特定的寄存器。这节介绍数据的定义和声明。
首先,要知道数据的长度,这就最基本的,就像在c语言中,char,short,int,long,long long,float,double,这些类型的字节长度是必须要知道的,在汇编中也一样。下面的表格展示了常用的数据类型。
类型 | 含义 | 长度(字节) |
db | 字节 | 1 |
dw | 字 | 2 |
dd | 双字 | 4 |
dq | 四字 | 8 |
上面这些数据类型在nasm汇编中是最常用的。后面的文章会讲到浮点数,不过这是不常用的,因此,这里先不介绍。
下面是数据定义的例子test1.asm:
; test1.asm
; nasm -f elf64 -o test1.o test1.asm
; ld -o test1 test1.o
section .data
var1 db 0x12 ; byte
var2 db 0x12,0x34,0x56 ; 3 bytes
var3 db 'hello, world!',0 ; string bytes
var4 dw 0x1234 ; word
var5 dd 0x12345678 ; double word
var6 dq 0x1122334455667788 ; quad word
section .text
global _start
_start:
mov rax, 60
syscall
这里需要注意的是,声明的变量代表的是地址,而不是值,比如上面的var3,它的内容是后面字符串的首地址。var4也是一个地址,如果要使用0x1234,在汇编中,使用中括号,如mov ax, [var4]
var1是一个字节的整数
var2是包含3个字节整数的“数组”,这3个数在内存中是连续的
var3是一个字符串,每个字符占1个字节
var4是一个字的整数
var5是一个双字整数
var6是一个8字整数
这些是在数据段中声明和定义的。
在代码段中,调用了系统调用号为60的系统调用,这是退出调用。
有时候我们需要定义初始化的数组或未初始化的数组,test2.asm展示了如何做。
; test2.asm
; nasm -f elf64 -o test2.o test2.asm
; ld -o test2 test2.o
section .data
var1: times 10 db 0
var2: times 100 dw 0
section .bss
buffer1: resb 64
buffer2: resw 50
buffer3: resd 10
buffer4: resq 20
section .text
mov rax, 60
syscall
test2.asm中,在data段中,使用了times,times的作用是重复数据或指令。
var1: times 10 db 0 ; 表示重复10次字节,并且值为0
var2: times 100 dw 0 ; 表示重复100次字,并且值为0
这样可以解决定义初始化的数组。
而在.bss段中,使用了resb,resw这样的指令,它的作用也是重复,这是未对数据进行初始化。代码的具体含义很容易理解这里不再赘述。
为什么两种不同类型的数据存放在不同的段中呢?
这里我们要知道:.data段保存的是那些已经初始化了的全局静态变量和局部静态变量,而.bss段保存的是未初始化的全局变量和局部静态变量。(参考《程序员的自我修养--链接、装载与库》中的3.3.2和3.3.3节)
EQU: EQU的作用是定义常量,例如下面的部分代码
section .data
var1 equ 2
使用equ声明的变量不能再次赋值,因为它定义的是常量,而且该变量不占用内存空间。如何验证?使用[readelf -t 可执行文件],-t选项展示各个段的细节,会发现,使用equ定义的变量不占用内存空间。