【问题标题】:Calling ARM assembly from C, GCC (bare metal)从 C、GCC(裸机)调用 ARM 程序集
【发布时间】:2012-02-15 23:12:05
【问题描述】:

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

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

【问题讨论】:

  • kernel.o:(.ARM.exidx+0x0): undefined reference to `__aeabi_unwind_cpp_pr1' make: *** [kernel.elf] Error 1
  • 您是否正在尝试构建一个 arm linux 应用程序?还是嵌入式(无操作系统)应用程序?如果是 linux 应用程序,那么您不需要启动代码,工具链应该为您完成所有这些,您担心 main() 作为您的入口点。
  • 你能提供一些你感到困惑的地方的汇编转储吗?
  • 使用 codesourcery 的 (lite) 工具链(现在是指导图形)我没有看到任何拇指代码,它正在生成 32 位臂代码。 armv6 确实支持拇指模式。
  • 看来,Ubuntu的gcc ARM交叉编译器默认生成thumb。我不确定是否有办法使用它,但我终于找到了 -marm 选项,并使用它摆脱了所有 thumb 指令输出。下一个问题:)

标签: gcc arm qemu thumb armv6


【解决方案1】:

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

【讨论】:

  • 谢谢!这似乎工作得很好,并允许我链接库代码!
【解决方案2】:

查看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加载了这里的地址:这是我们的返回地址,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,但我不认为切换模式,所以你不能使用它,你再次需要一个备用寄存器。您当然需要知道调用约定才能知道可以使用哪些寄存器,或者您可以包装另一组 push 和 pop 以保留除 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,如果你不能地址。

【讨论】:

    【解决方案3】:

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

    .global activate
    .thumb_func
    activate:
        b test
    

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

    .code 32
    .global activate
    activate:
        b test
    

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

    【讨论】:

      【解决方案4】:

      为了消除混淆:

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

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

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

      【讨论】:

      • gcc -marm 对我不起作用。我需要 arm-v4t-linux-gnueabi 的汇编代码
      猜你喜欢
      • 2013-07-04
      • 2016-01-23
      • 1970-01-01
      • 1970-01-01
      • 2015-10-12
      • 1970-01-01
      • 2011-01-30
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多