在NASM程序集中,有db
和dw
伪指令来声明数据。 NASM Manual提供了几个例子,但没有直接说明它们之间的区别。我已经尝试了以下两个“hello world”代码,结果发现没有可观察到的差异。我怀疑明显与内部数据格式有关,但我不知道如何检查它。
section .data
msg db "hello world",10,13,0
msg2 dw "hello world",10,13,0
section .text
global _start
_start:
mov rax, 1
mov rdi, 1
mov rsi, msg ; or use msg2
mov rdx, 14
syscall
jmp .exit
.exit:
mov rax, 60
mov rdi, 0
syscall
无论如何,NASM生产WORD ;-)
dw 'a'
相当于dw 0x61
并将WORD 0x0061(big-endian)存储为61 00
(little-endian)。
dw 'ab'
(little-endian)相当于dw 0x6261
(big-endian)并存储61 62
(little-endian)。
dw 'abc'
(一个字,一个字节)相当于dw 0x6261, 0x63
并存储两个WORDS(little-endian):61 62 63 00
。
dw 'abcd'
(两个字)存储两个字:61 62 63 64
。
msg2 dw "hello world",10,13,0
将字符串转换为6个单词,将数字转换为3个单词并存储它:68 65 6C 6C 6F 20 77 6F 72 6C 64 00 0A 00 0D 00
。在您的示例中,msg将在结束前不会打印。
NASM手册章节3.2.1 DB and Friends: Declaring Initialized Data和3.4.2 Character Strings表明,当单个字符串短于元素大小时,存在差异。每个元素用零字节填充到其原始大小。
为确保数据中没有非预期的字符,请始终使用DB作为8位字符串。 DW可能适用于UTF-16,也可能不适用,具体取决于机器字节顺序和代码中的任何假设。
使用DW伪指令肯定会导致数值的意外值,因为这些值将被解释为16位字,将意外的空字符引入字符串。
使用2.1.3 The -l Option: Generating a Listing File查看输出的实际内存图像,以查看您正在生成的内容。
NASM的db
,dw
,dd
等接受整数列表,并将它们编码为输出为little-endian,例如: dw 0x1234, 0x5678
组装到34 12 78 56
。
NASM还支持多字符字符文字,例如'ab'
在任何接受整数的上下文中,例如add ax, '00'
与0x3030
相同。 (可能是解压缩的BCD-> ASCII转换。)
NASM对多字符文字的字节顺序在内存中产生与little-endian x86上的源顺序相同的顺序。因此,例如,mov eax, '1234'
/ mov [buf], eax
将在内存中生成与buf: db '1', '2', '3', '4'
相同的4字节序列。 mov
-immediate指令编码为b8 31 32 33 34
,因为x86立即操作数使用little-endian,就像数据加载/存储一样。
对于db
/ dw
/ dd
/ etc有一个特殊情况:不是像add ax, '123456'
那样截断,(foo.asm:1: warning: word data exceeds bounds [-w+number-overflow]
)只保留整数值的低字节/字/双字,处理ASCII或UTF-8字符串作为多个元素。
但是最后一个元素用零填充(最后因为小端)使总大小为元素大小的倍数(dd / dword的dw / dword等)
所以这些都完全相同
db 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x00
db 'a', 'b', 'c', 'd', 'e', 'f', 'g', 0
db 'abcdefg', 0
db `abcdefg\0` ; C-style escapes like \n or \0 work inside backtick strings only. (NASM only, not YASM)
dw 'abcdefg' ; 7 bytes padded to 4 words = 8
dw 'ab', 'cd', 'ef', 'g' ; 'g' is a small WORD value
dw 0x6261, 0x6463, 0x6665, 0x0067 ; x86 is little-endian (LSB first) but we write integer values with MSD on the left
dd 'abcdefg' ; 7 bytes padded to 2 dwords = 8
使用dd
和dq
(以及do
和其他更宽泛的类型),你可以有超过1个字节的零,但总是在最后。例如dd 'abcde'
是db 'abcde', 0,0,0
。
参见NASM手册,3.2.1 DB and Friends: Declaring Initialized Data和3.4.2 Character Strings
源值和整数值(如0x123456
)没有“字节序”。这是对术语的滥用。
Endianness是在将多字节整数序列化到内存中然后按增加地址的顺序检查各个字节时看到的效果。
我们从MSD到LSD约定从左到右的阿拉伯数字与内存中的字节顺序是分开的。我们通常使用从左到右增加地址的内存图表,以及我们编写数字的方式看起来像Big Endian,这也是大多数任意的。
我们也可以使用罗马数字来表示源代码和/或伪代码中的数字,或任何其他计数系统。 (例如,一元,其中3 = 111)。
然而,记住“小端将数字倒退”并不是一个坏的心理捷径。但它在字节边界上,而不是十六进制(4位)边界。
但是,不要陷入认为寄存器中的值是“大端”的陷阱。除非存储在内存中,否则它们没有字节序。