8

我正在尝试使用 GCC 在 ARM 中进行一些裸机编程并在 QEMU 上进行测试。每当我从 C 调用 ARM 标签时,我的程序就会挂起。我有一个简单的代码示例,它在https://gist.github.com/1654392上显示了问题——当我在该代码中调用 activate() 时,它挂起。

我用 objdump 观察到,当我从汇编代码到 C 代码(从 _start 开始)执行 bl 时,它会生成一个切换到拇指指令的小包装器。似乎 C 代码都是在拇指指令中生成的,但我所有的程序集都是在 ARM(32 位)指令中生成的。我无法弄清楚为什么会这样或如何解决它。

4

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
于 2013-04-20T07:13:38.773 回答
3

请参阅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,如果你不能,请地址。

于 2012-01-22T15:43:54.500 回答
0

为了消除混淆:

问题是 Ubuntu 的 ARM GCC 交叉编译器默认生成 thumb(16 位)指令。正如此处的其他答案所示,两者之间的调用是可能的,但是当 GNU 汇编器检测到 C 代码正在生成拇指指令并因此愉快地使用 bx 生成垫片以正确设置调用 C 的模式时我无法控制什么GCC 本身为调用函数生成,它只用 bl 调用它们,因为我的汇编代码需要是 ARM 指令(32 位),所以它坏了。

解决方案(文档很少)是发送 gcc -marm,这至少会使所有代码的类型相同。

如果有一个开关可以让 gcc 为函数生成 bx 调用,那可能也可以。

于 2012-01-31T16:28:32.253 回答
0

如果将 asm 代码汇编为 Thumb,则需要将该函数标记为 Thumb 函数,以便链接器在跳转到它时使用正确的指令(例如 BLX 或 BX 到设置了低位的地址)。这是通过 .thumb_func 指令完成的:

.global activate
.thumb_func
activate:
    b test

另一种选择是明确要求汇编器生成 ARM 代码:

.code 32
.global activate
activate:
    b test

也请查看这篇文章,尽管请记住当前的处理器不需要 ARMv4 中所需的许多变通方法,因此您可能不应该盲目地遵循它。

于 2012-01-23T11:30:43.903 回答