从C语言中调用ARM汇编,使用GCC(裸机)

8
我正在尝试使用GCC在ARM上进行裸机编程,并在QEMU上进行测试。但是,每当我从C中调用ARM标签时,程序就会卡住。我有一个简单的代码示例,可以在https://gist.github.com/1654392找到,当我调用该代码中的activate()函数时,它就会卡住。
我观察了objdump,发现当我从汇编代码向C代码(如从_start)进行bl时,它会生成一个小包装器,将其切换为Thumb指令。似乎所有的C代码都是以Thumb指令生成的,但我的所有汇编代码都是以ARM(32位)指令生成的。我无法弄清楚原因或如何解决这个问题。

kernel.o:(.ARM.exidx+0x0): 对 `__aeabi_unwind_cpp_pr1' 的引用未定义 make: *** [kernel.elf] 错误 1 - old_timer
你能提供一些汇编转储数据吗?这些数据是你感到困惑的区域的。 - old_timer
我在http://github.com/dwelch67/上有许多这方面的示例。 - old_timer
2
所以,看起来Ubuntu的gcc ARM交叉编译器默认生成thumb指令集。我不确定是否有办法解决这个问题,但我终于找到了-marm选项,并使用它消除了所有的thumb指令输出。接下来是下一个问题 :) - singpolyma
那么真正的问题,这也是一个有趣的SO问题,就是如何从ARM汇编中调用Thumb C函数。我认为已经有一个SO答案,因为我为某人编写、测试和发布了一些代码。 - old_timer
显示剩余4条评论
4个回答

7
为了在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

谢谢!这似乎完美地工作,并且允许我链接库代码! - singpolyma

3

请查看http://github.com/dwelch67/yagbat qemu目录。

下面是从arm调用arm或thumb的几个示例:

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在ARM和Thumb状态之间切换。BLX不如BX得到广泛支持。
从定义的角度来看,程序计数器PC在执行指令时比指令提前两个指令。对于Thumb来说是4个字节,对于ARM来说是8个字节。无论哪种情况都是两条指令。为了模拟不能用于更改状态的bl,您需要将链接寄存器加载返回地址,并使用bx跳转到函数,根据地址的lsbit更改状态。

mov lr,pc
bx r1
here:

mov指令将here的地址(即返回地址)加载到寄存器lr中,bx r1以与状态无关的方式调用函数。lr地址的最低有效位表示要返回的模式,必须始终使用bx返回。

pre_thumb:
ldr pc,lr

thumb_capable:
bx lr

编译器为调用函数分配了一个 bl 指令,链接器稍后填充其余部分,如果距离太远,则需要一个跳板函数,链接器自己添加。同样,如果需要更改模式,则 bl 调用调用执行该操作的跳板函数。我在上面的代码中对其进行了建模,您可以看到它有些浪费。希望我对编译器只为 bl 分配空间的解释更加清晰。浪费是为大多数代码中的函数调用始终计划模式更改并必须插入 nops。

代码还包括从汇编语言中的 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

除了在Thumb模式下无法弹出到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,address 指令。这些指令涉及到 IT 技术领域。

0
为了消除混淆:
问题在于Ubuntu的GCC交叉编译器默认生成Thumb(16位)指令。正如其他答案所示,两者之间的调用是可能的,但是GNU汇编程序检测到C代码正在生成Thumb指令,因此愉快地使用bx生成shim以正确设置模式以调用C,我无法控制GCC本身生成的调用函数,它只是使用bl进行调用,这会导致我的汇编代码需要ARM指令(32位)而出现错误。
解决方案(文档不好)是发送gcc -marm,这将至少使所有代码具有相同的类型。
如果有一个开关可以让gcc为函数生成bx调用,那也可能起作用。

gcc -marm 对我不起作用。我需要 arm-v4t-linux-gnueabi 的汇编代码。 - Trouble-lling

0
如果您将汇编代码组装为Thumb,则需要将函数标记为Thumb函数,以便链接器在分支到该函数时使用正确的指令(例如,BLX或BX到低位设置的地址)。这可以通过使用“.thumb_func”指令来完成:
.global activate
.thumb_func
activate:
    b test

另一个选择是显式要求汇编器生成ARM代码:
.code 32
.global activate
activate:
    b test

也可以查看this article,但请记住,现在的处理器不需要ARMv4中必须要的很多变通方法,因此您可能不应该完全照搬。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接