32位(或64位)的系统只有有限的寄存器。忽略用于追踪栈的特殊寄存器和通用寄存器(如esp和ebp)后,只剩下六个可用于一般计算的寄存器(如eax、ebx、ecx、edx、esi和edi),这就是程序还需要能够读取内存数据和向内存中写入数据的原因。
在x86汇编Intel语法中,内存访问是使用[]符号来表示的。例如,存储在0x12345678地址的数据可以通过[0x12345678]来访问。内存地址也可以存储在寄存器中,如指令[eax]。
指定数据长度
当从内存获取数据时,不仅需要知道数据的存储地址,还需要知道要访问的内存量。例如,指令[0x12345678]并没有指明程序需要一个字节、一个字、一个双字,还是更多。
图2.7 x64常用的寄存器
来源:Bobmon/Wikimedia Commons/CC BY-SA 4.0.
图2.8 r8寄存器的各个部分
在某些情况下,可以从上下文中推断出需要访问的数据的长度。例如,在指令mov eax,[0x12345678]中,从内存中获取的数据将被储存在eax中。由于eax是一个32位寄存器,程序必然需要请求32位的数据。
事实并非总是这样。例如,考虑指令mov[0x12345678], 1,它会将值1放到内存中的特定地址。但是,这个指令并没有明确被设定的值的长度。我们应该将1看作是一个字节(00000001)、一个字(0000000000000001),还是一个双字(00000000000000000000000000000001)呢?为了明确和精简,我们经常会去除前导零,所以以上都是有效解释。
提示: 传统上,32位的x86架构应该有32位的字。然而,为了向后兼容16位的x86架构,所以字的长度是16位,而双字则是32位。
当内存访问的大小没有被隐式地指出时,必须在指令内明确指明。例如,指令byte[100]访问位于地址100的字节,word[ebx]访问ebx指向的字,dword[ax]访问ax指向的双字。图2.9展示了以下三条指令之间的区别。
图2.9 比较不同大小的mov指令