14

我正在 Android 设备上编写针对 ARM Cortex-A 的代码(使用 GNU 汇编器和编译器),并且我正在尝试在 Assembly 和 C 之间进行接口。特别是,我对从 Assembly 调用用 C 编写的函数感兴趣。我尝试了很多东西,包括.extern指令、用asm等声明 C 函数__asm__,但它们都不起作用,所以我正在寻找一个最小的例子。对此类示例的引用同样受欢迎。

4

4 回答 4

8

你需要阅读 ARM ARM 和/或知道指令集是全部,通常你会想做这样的事情

asm:

bl cfun

c:
void cfun ( void )
{

}

你可以自己试试这个。对于 gnu as 和 gcc 这工作得很好,如果你使用 clang 将 c 代码获取到一个对象和 gnu 作为汇编程序,它也应该工作得很好。不确定您使用的是什么。

上述问题是 bl 范围有限,

if ConditionPassed(cond) then
  if L == 1 then
    LR = address of the instruction after the branch instruction
    PC = PC + (SignExtend_30(signed_immed_24) << 2)

知道 bl 指令将链接寄存器设置为 bl 指令之后的指令,那么如果您阅读了程序计数器寄存器:

For an ARM instruction, the value read is the address of the instruction
plus 8 bytes. Bits [1:0] of this
value are always zero, because ARM instructions are always word-aligned.

所以如果你让你的 asm 看起来像这样:

mov lr,pc
ldr pc,=cfun

你得到

d6008034:   e1a0e00f    mov lr, pc
d6008038:   e51ff000    ldr pc, [pc, #-0]   ; d6008040 
...
d6008040:   d60084c4    strle   r8, [r0], -r4, asr #9

汇编器将在 ldr pc 指令的范围内保留一个内存位置(如果可能,否则会产生错误),它将放置指令的完整 32 位地址。链接器稍后将使用外部地址填充此地址。这样您就可以到达地址空间中的任何地址。

如果你不想玩这样的汇编游戏并且想要控制,那么你创建一个位置来保存函数的地址并将它自己加载到 pc 中:

    mov lr,pc
    ldr pc,cfun_addr

...

cfun_addr:
    .word cfun

编译:

d6008034:   e1a0e00f    mov lr, pc
d6008038:   e51ff000    ldr pc, [pc, #-0]   ; d6008040 <cfun_addr>
...

d6008040 <cfun_addr>:
d6008040:   d60084c4    strle   r8, [r0], -r4, asr #9

最后,如果您想进入现代 ARM 世界,其中 ARM 和 thumb 混合使用或可以混合使用(例如使用 bx lr 而不是 mov pc,lr),那么您将需要使用 bx

    add lr,pc,#4
    ldr r1,cfun_addr
    bx r1
...

cfun_addr:
    .word cfun

当然,您需要另一个寄存器来执行此操作,并且如果您想保留它们,请记住在调用 C 之前和之后推送和弹出您的链接寄存器和另一个寄存器。

于 2011-12-07T22:44:27.660 回答
2

最小可运行 armv7 示例

这个问题归结为“什么是ARM调用约定(AAPCS)”。一个例子a.S

/* Make the glibc symbols visible. */
.extern exit, puts
.data
    msg: .asciz "hello world"
.text
.global main
main:
    /* r0 is the first argument. */
    ldr r0, =msg
    bl puts
    mov r0, #0
    bl exit

然后在 Ubuntu 16.04 上:

sudo apt-get install gcc-arm-linux-gnueabihf qemu-user-static
# Using GCC here instead of as + ld without arguments is needed
# because GCC knows where the C standard library is.
arm-linux-gnueabihf-gcc -o a.out a.S
qemu-arm-static -L /usr/arm-linux-gnueabihf a.out

输出:

hello world

在更复杂的示例中最容易犯的错误是忘记堆栈必须是 8 字节对齐的。例如,你想要:

push {ip, lr}

代替:

push {lr}

GitHub 上通用样板的示例:https ://github.com/cirosantilli/arm-assembly-cheat/blob/82e915e1dfaebb80683a4fd7bba57b0aa99fda7f/c_from_arm.S

于 2016-10-14T12:00:40.080 回答
1

您需要armeabi-v7a描述调用堆栈、寄存器(被调用者与调用者)等的规范。然后查看编译后的 C 代码的汇编输出以了解语法等。尝试调用共享库或可重定位函数中的函数时,事情会更加复杂对象。

于 2011-12-07T21:01:59.190 回答
-4

正如布雷特所说,您真正需要做的就是将正确的值放入正确的寄存器中,并通过链接分支到函数的地址。您需要知道编译后的函数将覆盖哪些寄存器,以及在返回之前将恢复哪些寄存器——这些都写在 infocentre.arm.com 上的 ABI 文档中。您还需要确保堆栈寄存器设置为编译器所期望的,也许还有其他寄存器(对于 PIC 模式?)

但是,您真的需要在汇编文件中编写代码吗?

如果您使用 GCC “asm” 功能,那么您可以将汇编程序片段(只要您喜欢)嵌入到常规 C 函数中,并在更方便时退回到 C 中。

在某些情况下,周围有 C gubbins 是行不通的,但如果你可以调用 C 函数,我猜你不在其中。

说到这里,你为什么需要使用汇编程序......无论如何,C 基本上是高级汇编程序?

于 2011-12-07T22:08:08.597 回答