对于 bx(和 blx)指令,目标地址需要 lsbit 为 1,当它进入 pc 时,1 被剥离。b 指令是相对于 pc 的,arm 文档中显示的数学清楚地表明它是偶数。
通常,如果您让工具完成工作,您在任何时候都不必担心这一点。
拇指.s
.thumb
.globl _start
_start:
b reset
nop
nop
.thumb_func
reset:
nop
nop
nop
nop
ldr r0,=reset
bx r0
然后
arm-none-eabi-as thumb.s -o thumb.o
arm-none-eabi-ld -Ttext=0x1000 thumb.o -o thumb.elf
arm-none-eabi-objdump -D thumb.elf
这使
thumb.elf: file format elf32-littlearm
Disassembly of section .text:
00001000 <_start>:
1000: e001 b.n 1006 <reset>
1002: 46c0 nop ; (mov r8, r8)
1004: 46c0 nop ; (mov r8, r8)
00001006 <reset>:
1006: 46c0 nop ; (mov r8, r8)
1008: 46c0 nop ; (mov r8, r8)
100a: 46c0 nop ; (mov r8, r8)
100c: 46c0 nop ; (mov r8, r8)
100e: 4801 ldr r0, [pc, #4] ; (1014 <reset+0xe>)
1010: 4700 bx r0
1012: 10070000 andne r0, r7, r0
...
分支会照顾自己
1000: e001 b.n 1006 <reset>
...
00001006 <reset>:
分支中的编码是以 16 位数量为单位而不是以字节为单位,然后将其乘以 2(移位)以获得始终为偶数的字节地址。pc 永远不会是奇数,它是您输入 bx 或 blx 的值是奇数。
现在因为我在重置之前使用 .thumb_func 告诉汇编器这是一个拇指标签而不是手臂标签。因此,当我说请将重置的地址加载到 r0 时,汇编器然后为值 0x00001007 分配了一些数据,这在反汇编中显得很奇怪,但它就在那里。他们已经为我们设置了 lsbit
00001006 <reset>:
...
100e: 4801 ldr r0, [pc, #4] ; (1014 <reset+0xe>)
1010: 4700 bx r0
1012: 10070000 andne r0, r7, r0
现在,如果您要删除 .thumb_func
100c: 46c0 nop ; (mov r8, r8)
100e: 4801 ldr r0, [pc, #4] ; (1014 <reset+0xe>)
1010: 4700 bx r0
1012: 10060000 andne r0, r6, r0
汇编器认为它是一个 arm 地址并且没有设置 lsbit 并且此代码会崩溃。现在,如果您担心它,您可以随时添加额外的 orr r0,#1 但这实际上只是一个 hack。了解您使用的任何汇编程序如何将标签声明为拇指标签而不是手臂。是的,gnu 汇编器知道这个代码段是 thumb 似乎很愚蠢,因为我们告诉它但它无法弄清楚拇指代码中的标签是......拇指标签。非常愚蠢的工具。
而且我会假设还有其他更详细的 gnu 汇编器指令也允许您将其声明为函数或拇指标签或其他任何东西。当然,每个汇编器都是不同的,所以不要假设 gnu 汇编器指令适用于其他汇编器指令。
如果您将 C 和 asm 混合使用,C 编译器并不愚蠢,它知道 -mthumb 使所有函数和全局变量(标签)成为拇指,并且取决于您在代码中使用它们的方式和位置,链接器会放置正确的值。它甚至可以为您正确切换模式,bl main in thumb code 其中 main 是 arm 代码,它在代码中为您放置一个蹦床来切换模式。反之亦然,至少我已经看到该工具执行此操作(并在堆栈溢出答案中多次演示)。我不记得让它工作是否很棘手,你应该总是定期反汇编并确保链接器正在为你做这件事,否则让它去做,或者你总是可以自己做。
所以
请记住,只有 bx 和 blx 需要为 thumb 设置 lsbit 并为分支到 arm 重置两个 lsbit。blx 和 bx 指令将删除该 lsbit 并在 pc 中留下一个偶数编号的 pc(非常简单,执行 mov r0,pc 然后在拇指代码中查看它)。
理想情况下,无条件和条件分支(不是 bx)不应该切换模式 arm to arm 和 thumb to thumb。bl 也一样,但我已经看到 gnu 工具可以帮助解决这个问题,如果你想要你的代码纯然后将地址加载到工具必须正确执行的寄存器中,否则整个工具链都会失败,并且 blx 而不是 bl该标签而不是依赖工具链为您做蹦床。