请参阅http://github.com/dwelch67/yagbat qemu 目录。
以下是几个从手臂调用手臂或拇指的示例
start_vector:
mov sp,#0x20000
;@ call an arm function from arm
bl notmain
;@ call a thumb function frm arm
ldr r0,=0xAABBAABB
bl hexstring_trampoline
;@ call a thumb function frm arm
ldr r0,=0x12341234
ldr r1,hexstring_addr
mov lr,pc
bx r1
;@ call a thumb function frm arm
ldr r0,=0x12312344
bl hexstring_trampoline
hang:
b hang
hexstring_trampoline:
ldr r1,hexstring_addr
bx r1
hexstring_addr: .word hexstring
如果您查看指令集参考,您会发现您需要使用 BX 或 BLX 在手臂和拇指状态之间切换。BLX 不像 BX 那样受到广泛的支持。
从程序计数器的定义的角度来看,pc 在执行一条指令期间提前两条指令。拇指是 4 个字节,手臂是 8 个字节。两种情况都有两条指令。要模拟一个不能用来改变状态的bl,你需要用返回地址加载链接寄存器,并使用bx根据地址的lsbit跳转到函数改变状态。所以
mov lr,pc
bx r1
here:
上面的 mov lr,pc 加载这里的地址:这是我们的返回地址,bx r1 以状态无关的方式调用函数。lr地址的lsbit表示返回的模式,需要一直使用bx返回
pre_thumb:
ldr pc,lr
thumb_capable:
bx lr
编译器为调用函数分配一条 bl 指令,链接器稍后填写其余指令,如果它太远,则需要链接器自己添加的蹦床函数。同样,如果您需要更改模式,则 bl 会调用执行此操作的蹦床函数。我已经在上面的一个模型中模拟了它,你可以看到它有点浪费,希望我对编译器只为 bl 分配空间的解释使总是计划模式更改和浪费更清楚,浪费必须为代码中的大多数函数调用插入 nop。
该代码还包括在汇编程序中从 thumb 对 arm 的调用:
.thumb
.thumb_func
.globl XPUT32
XPUT32:
push {lr}
;@ call an arm function from thumb asm
ldr r2,=PUT32
mov lr,pc
bx r2
pop {r2}
bx r2
基本上是一样的,除了你不能在拇指模式下弹出到 lr,你可以弹出到 pc,但我不认为切换模式,所以你不能使用它,你再次需要一个备用寄存器。您当然需要知道调用约定才能知道可以使用哪些寄存器,或者您可以包装另一组推送和弹出以保留除 lr 之外的所有内容
push {r2,lr}
;@ call an arm function from thumb asm
ldr r2,=PUT32
mov lr,pc
bx r2
pop {r2}
mov lr,r2
pop {r2}
bx lr
拇指对拇指或手臂对臂,如果你能够到,你只需使用 bl。ldr pc,如果你不能,请地址。