从C、GCC(裸金属)调用ARM程序集



我正在尝试用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调用,这可能也会起作用。

最新更新