【问题标题】:What is the function of a "data label" in an x86 assembler?x86 汇编器中“数据标签”的功能是什么?
【发布时间】:2017-06-25 03:14:04
【问题描述】:

我目前正在按照 Kip Irvine 的“汇编语言 x86 编程”一书学习汇编编程。

在书中,作者试图解释data label的概念

数据标签标识变量的位置,提供 在代码中引用变量的便捷方式。以下,对于 例如,定义一个名为 count 的变量:

count DWORD 100

汇编器 为每个标签分配一个数字地址。

所以我对data label所做的理解是:数据标签count是一个包含数值的变量,其中数值是内存中的一个位置。当我在代码中使用count 时,我实际上使用的是内存中该位置中包含的值,在本例中为 100。

我对数据标签的理解正确吗?如果它有些不正确,有人可以指出错误吗?

【问题讨论】:

  • 数据标签是对保存数据的内存地址的引用(别名)。 count DWORD 100 创建一个具有偏移量的标签,该偏移量最终会在程序运行时知道。 count 是标签。它最终会有一个地址。在该地址有一个 32 位值 (DWORD) 等于 100
  • 在masm中可以使用offset关键字获取count的地址。如果你有一个 32 位程序 mov eax, offset count 会将 count 的 32 位地址移动到 eax 中。 mov eax, [count] 会将 32 位值移动到与 EAX 中的计数相关联的地址。您还可以使用类似lea eax, [count] 的方式获取带有LEA 的标签的地址。使用 LEA(加载有效地址),您无需使用 offset 关键字。
  • @Michael 由于我们这里的好队长发现您的 cmets 很有帮助,您应该考虑将他们提升为答案。如果我要发布自己的内容,我不确定我还会添加什么。老实说,Irvine 的解释似乎还不错;如果我编辑他的书,我不确定我会如何清除它。对于已经了解其他高级编程语言的人来说,将其称为“变量”可能会感到困惑,在这种情况下完全避免使用这个术语会更好吗?
  • OP: "在那个地址有一个 32 位值 (DWORD) 等于 100" .. 标签实际上指向那个 dword 的第一个字节。您可以使用它来访问任意数量的字节,例如新 asm 程序员的常见错误是为某些变量 count db 10 ; reserve+define 1 byte 分配错误的内存量,然后覆盖更多内存 mov [count],ebx ; writes 4 bytes。 MASM 是很少有的 x86 汇编器之一,它试图真正跟踪标签的“类型”,但它很少有帮助,其他汇编器也不这样做。所以不要依赖它,把标签记在心里。
  • 为了更好地理解为什么这些细微的差异(标签与变量)很重要,您应该在组装特定源代码期间切换“列表”选项,并检查输出的机器代码以更好地了解哪些内存内容构成了计算机的代码。然后,您将认识到标签只是汇编符号,在编译和链接期间有效,但不是目标机器代码的一部分,即 mov eax,[count] 不会首先获取一些 count 标签变量,但具有直接编码的正确内存地址指令操作码,即mov eax,[<some 32bit number as address>].

标签: assembly x86 masm


【解决方案1】:

标签是写入内存地址的一种符号方式,仅此而已。标签本身不占用空间,只是方便您稍后在内存中引用该位置的一种方式。

(嗯,它们也可以在目标文件中转换为符号,以允许在链接时而不是在汇编时计算数字地址。但是对于在同一文件中定义和引用的标签,这种额外的复杂性几乎是不可见的; 请参阅下面关于地址是链接时间常量,而不是汇编时间的内容。)

例如

; 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

mov eax, [COUNT] 这样的 4 字节加载将得到 0x80FF0012(因为 x86 是小端序)。从FOOmov cx, [FOO] 这样的2 字节加载将得到0xFF00。

您实际上可能会以这种方式使用来自常量的重叠负载,例如带有字符串,其中一些是其他字符串的子字符串。对于以null结尾的字符串,这种方式只能将常见的后缀组合到同一个存储空间中。


现在这是否意味着COUNT 是一个 4 字节变量或一个 1 字节变量?不,也不是。汇编语言并没有真正的“变量”。

变量是一个更高级的概念,您可以在汇编语言中实现标签一个保留一些静态空间的汇编指令 .请注意,标签与上例中的 db 指令是分开的。

但变量不需要有任何静态存储空间:例如您的循环计数器变量可以(并且通常应该)仅存在于寄存器中。

变量甚至不需要有一个固定的位置。它可以在未使用的部分函数中溢出到堆栈中,但存在于函数另一部分的寄存器中。在编译器生成的代码中,变量经常无缘无故地在寄存器之间移动,因为编译器甚至不会尝试为同一个变量使用同一个寄存器。


请注意,MASM 确实根据其后面的指令将标签与操作数大小隐式关联。因此,如果 mov eax, [count] 给出操作数大小不匹配错误,您可能必须编写 mov eax, dword ptr [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 相对寻址而不额外的间接级别(通过全局偏移表)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-09-17
    • 2011-06-12
    • 2018-07-14
    • 1970-01-01
    • 2010-09-17
    • 1970-01-01
    相关资源
    最近更新 更多