我对组装很陌生,所以请对我宽容一点,但我对标签很困惑。我只是不明白它们是怎么工作的。它们是像函数一样,还是一端开始?我的意思是说有两个标签
Label1:
; random code
Label2:
; some more random code
Label1会执行然后结束,还是会移动到Label2并执行?很抱歉,如果这是一个非常基本的问题,但我只是没有真正理解它。
标签是地址(请注意,高级语言中的函数名基本上是标签)。
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>
所以第二列数字(两列都是十六进制)是机器代码。你可以看到,标签没有机器代码,它只是一个标签——盒子上的一个标记——它描述的是盒子上的东西,而不是它自己盒子里的东西。
我只告诉你,对低位进行循环编码的分支是一个偏移量,这个值中有很多个1,因为它是一个负数(向后的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标签一样或者switch
中的case 'x':
标签-请记住,您需要break
来而不是进入下一个案例。
函数(和作用域)是高级概念。标签(用于定义符号)是asm提供的工具之一,用于实现函数。(还有像call
和ret
这样的指令来跳转和保存返回地址;结构化编程";指出在函数和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"
。
相关问答;As:标签失效的示例
如果汇编程序中的CALLed代码块中没有返回语句怎么办
如果函数没有显式使用';ret';-从函数的末尾脱落
代码执行条件错误?-if体落入else体的if/else。
组装中的功能是什么?