我目前正在学习汇编编程,遵循Kip Irvine的"汇编语言x86编程"一书.
在这本书中,作者试图解释这个概念 data label
数据标签标识变量的位置,提供了在代码中引用变量的便捷方式.例如,以下定义了一个名为count的变量:
Run Code Online (Sandbox Code Playgroud)count DWORD 100
汇编程序为每个标签分配一个数字地址.
所以我理解的data label
是:数据标签count
是一个包含数值的变量,其中数值是内存中的一个位置.当我count
在我的代码中使用时,我实际上是在内存中使用该位置中包含的值,在本例中为100.
我对数据标签的理解是否正确?如果有些不正确,有人可以指出错误吗?
标签是编写内存地址的象征性方式,仅此而已.标签本身不占用空间,只是让您稍后在内存中引用该位置的便捷方式.
(好吧,它们也可以变成目标文件中的符号,以允许在链接时计算数字地址,而不是在汇编时计算.但对于在同一文件中定义和引用的标签,这种额外的复杂性大多是不可见的;请参见下文关于地址是链接时常数,而不是汇编时间.)
例如
; NASM syntax, but the concepts apply exactly to MASM as well
; For MASM, you may need BYTE PTR or whatever size overrides in loads.
section .rodata ; or section .data if you want to be able to store here, too.
COUNT:
db 0x12
FOO:
db 0
BAR:
dw 0x80FF ; same as db 0xff, 0x80
Run Code Online (Sandbox Code Playgroud)
一个4字节的负载mov eax, [COUNT]
就会得到0x80FF0012(因为x86是little-endian).来自FOO
like的2字节负载mov cx, [FOO]
将获得0xFF00.
实际上,您可能会以这种方式使用常量的重叠加载,例如,使用字符串,其中一些是其他的子串.对于以null结尾的字符串,只有通用后缀可以通过这种方式组合到同一存储空间中.
现在这意味着这COUNT
是一个4字节变量还是1字节变量?不,不.汇编语言实际上没有"变量".
变量是一个更高级别的概念,您可以使用标签和汇编程序指令以汇编语言实现,该指令保留一些静态空间.请注意,标签db
与上例中的指令是分开的.
但是变量不需要具有任何静态存储空间:例如,您的循环计数器变量可以(并且通常应该)仅存在于寄存器中.
变量甚至不需要具有单个固定位置.它可以在不使用它的函数的一部分中溢出到堆栈,但是存在于函数的另一部分的寄存器中.在编译器生成的代码中,变量经常在寄存器之间移动,因为编译器甚至不会尝试对同一个变量使用相同的寄存器.
请注意,MASM会根据后面的指令隐式地将标签与操作数大小相关联.因此,mov eax, dword ptr [count]
如果mov eax, [count]
给出操作数大小不匹配错误,则可能必须写入.
有些人认为这是一个功能,但其他人认为这个神奇的操作数大小的东西是完全奇怪的.NASM语法没有任何这种魔力.您可以了解线条的组装方式,而无需查找标签的定位位置. add [count], 1
在NASM中是一个错误,因为没有什么意味着操作数大小.
不要以为你在C中使用变量的所有内容必须在汇编语言程序中具有带标签的静态存储.但是如果你确实想要使用"变量"一词来表示静态数据存储+像Kip Irvine这样的标签,那就继续吧.
另请注意,数据标签与代码标签并不特殊或不同.没有什么能阻止你写作jmp COUNT
.将12 00 FF 80解码为(序列)x86指令留作读者的练习,但是(如果它在具有执行权限的页面中),它将被CPU取出并解码.
同样,没有什么能阻止您将代码标签中的数据作为内存操作数加载.出于性能原因混合代码和数据通常不是一个好主意(所有CPU都使用拆分L1D和L1I缓存),但这也有效.在典型的操作系统(如Linux)中,可执行文件的文本段包含代码和只读数据部分,并使用读取和执行权限进行映射.(但不是写权限,因此除非您修改了权限,否则尝试存储将会出错.)
JIT编译器将机器代码写入缓冲区然后跳转到那里.它可以是带有标签的静态缓冲区,但通常它是动态分配的缓冲区,其地址是变量.
静态地址通常是链接时常量,但通常不是汇编时常量.(除非你正在编写一个引导程序,或者其他一些肯定在已知地址加载的东西,否则org 0x100
可能会有用.)这意味着你可以这样做mov al, [COUNT+2]
,但不是mov al, [COUNT*2]
.(对象文件格式支持整数位移,但不支持其他数学运算符).
在PIC代码中,标签地址甚至不是链路时间常数,但至少在64位PIC代码中,从代码到数据标签的偏移是链路时间常数,因此可以在没有额外级别的情况下使用RIP相对寻址间接(通过全球抵消表).