我正在尝试用GCC在ARM中进行一些裸机编程,并在QEMU上进行测试。每当我从C调用ARM标签时,我的程序就会挂起。我有一个简单的代码示例,显示了https://gist.github.com/1654392--当我在该代码中调用activate()时,它会挂起。
我在objdump中观察到,当我从汇编到C代码(如从_start)执行bl时,它会生成一个切换到thumb指令的小包装器。看起来C代码都是用拇指指令生成的,但我的所有程序集都是用ARM(32位)指令生成的。我不知道为什么会这样,也不知道如何修复。
为了从C中定义的THUMB模式函数调用汇编中定义的ARM模式函数,您需要将汇编中的符号定义为函数,工具(Linaro-gcc)将生成blx
指令而不是bl
指令。
示例:
@ Here, we suppose that this part of code is inside of .code 32
.type fn, %function
fn:
mov pc, lr
请参阅http://github.com/dwelch67/yagbatqemu目录。
以下是从手臂调用手臂或拇指的几个例子
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加载here的地址:这是我们的返回地址,bxr1以独立于状态的方式调用函数。lr地址的lsbit表示要返回的模式,您需要始终使用bx返回
pre_thumb:
ldr pc,lr
thumb_capable:
bx lr
编译器为调用函数分配一条bl指令,链接器稍后填充其余指令,如果它太远,则需要链接器自己添加的蹦床函数。同样,如果你需要改变模式,bl会调用蹦床函数来实现这一点。我在上面的一个例子中对此进行了模拟,你可以看到这有点浪费,希望我对编译器只为bl分配空间的解释能让我更清楚、更浪费的是始终计划模式更改,并且必须为代码中的大多数函数调用插入nop。
该代码还包括汇编程序中拇指对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,如果你不能,地址。
如果将asm代码组装为Thumb,则需要将该函数标记为Thumb函数,以便链接器在分支到它时使用正确的指令(例如,BLX或BX到具有低位集的地址)。这是通过.tumb_func指令完成的:
.global activate
.thumb_func
activate:
b test
另一种选择是明确要求汇编程序生成ARM代码:
.code 32
.global activate
activate:
b test
也可以查看这篇文章,尽管请记住,当前的处理器不需要ARMv4中所需的许多变通方法,所以您可能不应该盲目遵循它。
要消除混淆:
问题是Ubuntu的GCC ARM交叉编译器默认生成thumb(16位)指令。正如这里的其他答案所示,在两者之间调用是可能的,但尽管GNU汇编程序检测到C代码正在生成拇指指令,并很高兴地使用bx生成垫片,以正确设置将调用到C的模式,但我无法控制GCC本身为调用函数生成的内容,它只使用bl调用它们,它坏了,因为我的汇编代码需要是ARM指令(32位)。
解决方案(文档记录不足)是发送gcc-marm,这至少会使所有代码的类型相同。
如果有一个开关让gcc为函数生成bx调用,这可能也会起作用。