我对组装很陌生,所以请放轻松,但我对标签感到困惑。我只是不明白它们是如何工作的。它们是函数,还是一个结束另一个开始?我的意思是说有 2 个标签
Label1:
; random code
Label2:
; some more random code
Label1 会执行然后结束,还是会移动到 Label2 并执行它?对不起,如果这是一个非常基本的问题,但 io 并没有真正理解它。
我对组装很陌生,所以请放轻松,但我对标签感到困惑。我只是不明白它们是如何工作的。它们是函数,还是一个结束另一个开始?我的意思是说有 2 个标签
Label1:
; random code
Label2:
; some more random code
Label1 会执行然后结束,还是会移动到 Label2 并执行它?对不起,如果这是一个非常基本的问题,但 io 并没有真正理解它。
标签是地址(请注意,高级语言中的函数名称基本上是标签)。
loop:
nop
nop
jump loop
这允许您编写具有两个功能的代码。一是,循环的地址是什么,是0x12345还是0x8000等。二是如果我这里的伪代码指令集跳转指令在固定地址上操作,那么它需要知道0x12345或0x8000才能完全编码指令还是相对于没有标签程序员必须计算指令字节的地方。如果 nops 是一个字节,并且跳转本身是相对的,假设是三个字节并且 pc 偏移量是相对于指令末尾的,那么你必须在代码中说 jump -5 然后如果你在代码中添加或删除指令循环,您将不得不重新计算跳跃偏移量而不会出错。
另一种情况,也是地址的事情,是用于外部引用:
fun:
call more_fun
...
如果您在现代,您可以将多个源代码文件变成对象然后链接起来。(一些工具仍然支持这一点,当我开始能够在其中拥有一个带有 .org 等的 asm 文件并且没有外部引用只有前向和后向引用时更常见)然后你可以有外部引用,无论指令集使用绝对或相对寻址,以便完成此调用的机器代码的创建,该地址需要被解析。因此,使用标签使程序员更容易做到这一点,并且与本地引用一样,工具可以为您计算所有这些偏移量或地址。
.thumb
nop
nop
nop
loop:
nop
nop
nop
b loop
Disassembly of section .text:
00000000 <loop-0x6>:
0: 46c0 nop ; (mov r8, r8)
2: 46c0 nop ; (mov r8, r8)
4: 46c0 nop ; (mov r8, r8)
00000006 <loop>:
6: 46c0 nop ; (mov r8, r8)
8: 46c0 nop ; (mov r8, r8)
a: 46c0 nop ; (mov r8, r8)
c: e7fb b.n 6 <loop>
所以第二列数字(两列都是十六进制)是机器代码。你可以看到标签没有机器代码,它只是一个标签,一个盒子上的标记,它描述了盒子上的东西,而不是它自己盒子的内容。
我只会告诉你循环编码低位的分支是一个偏移量,并且该值中有很多偏移量,因为它是一个负数(pc相对分支向后)。
上述地址未链接。
一个文件:
.thumb
nop
nop
loop:
bl more_fun
b loop
其他:
.thumb
.thumb_func
.globl more_fun
more_fun:
bx lr
unlinked 我们看到调用的反汇编(本指令集中的 bl 分支链接)基本上有一个偏移量的占位符。
Disassembly of section .text:
00000000 <loop-0x4>:
0: 46c0 nop ; (mov r8, r8)
2: 46c0 nop ; (mov r8, r8)
00000004 <loop>:
4: f7ff fffe bl 0 <more_fun>
8: e7fc b.n 4 <loop>
一旦链接
Disassembly of section .text:
00001000 <loop-0x4>:
1000: 46c0 nop ; (mov r8, r8)
1002: 46c0 nop ; (mov r8, r8)
00001004 <loop>:
1004: f000 f801 bl 100a <more_fun>
1008: e7fc b.n 1004 <loop>
0000100a <more_fun>:
100a: 4770 bx lr
bl 指令已更改为具有 pc 相对偏移量。
所有这些魔法都是由工具完成的,我们只需要跟踪标签。对于我们可以使用人类可读/可写助记符的指令本身也是如此:
bx lr
而不是机器码:
0x4770
在我们的节目中。
标签不会执行,它们是元数据,可让您从其他地方引用该位置。例如作为分支目标,或从那里加载数据。它们不是机器代码的一部分,CPU 也不知道它们。将它们视为零宽度标记,可让您从其他地方引用此字节位置。
它们与以下字节或直到下一个标签的间隔没有任何隐式关联。如果你愿意,你甚至可以在同一个地方有多个标签。(编译器在自动生成标签时可能会这样做,比如在一个简单的函数中,它的主体简化为一个循环,它们会
执行将简单地通过一个标签,就像 C 函数中的 C goto 标签一样。 或 acase 'x':
中的标签switch
- 请记住,您需要 abreak
才能不陷入下一个案例。
函数(和作用域)是高级概念。标签(用于定义符号)是 asm 提供的工具之一,可以实现功能。call
(以及诸如和跳转并保存返回地址之类的指令ret
。)与仅在任意点之间跳转的一大堆意大利面条代码相反,例如在一个巨大的函数中使用 goto - 显然这在支持者之前的糟糕旧时代是典型的“结构化编程”的作者指出,就函数和 if/else 块而言,设计更大的程序要容易得多,从而限制了您在 asm 中使用跳转以符合这些概念的方式。“函数”不是原始机器代码或大多数汇编语言中的一流概念。(MASM 有一个proc
您可以使用关键字,而不仅仅是一个标签。)
对于数据:在 C 中,类似这样的数组static char foo[] = {1,2,3};
将编译为如下内容:
foo:
.byte 0, 1, 2
请注意,标签地址与第一个元素具有相同的地址,并且foo+1
是第二个字节的地址。
但同样地,
foo: .byte 0, 1
foo2: .byte 2
.byte 3
这样做会添加一个标签,&foo[2]
以便您可以根据需要直接引用它,但您也可以将整个 0..3 字节范围视为一个数组。这对于您可能希望单独处理一个字符串的后缀的字符串可能更有用。.asciz "\n"
例如,您可能只是在另一个字符串末尾的换行符上粘贴一个标签,而不是一个单独的标签。
相关问答:标签失效示例
如果函数没有明确使用“ret”,为什么没有返回值- 从函数的末尾脱落
代码执行条件错误?- if/else if 主体落入 else 主体。