如何从ARM Assembly调用C函数?

Pho*_*non 10 c assembly arm gnu-assembler cortex-a8

我正在Android设备上编写针对ARM Cortex-A的代码(使用GNU汇编器和编译器),我正在尝试在Assembly和C之间进行接口.特别是,我有兴趣调用用C语言编写的函数.我尝试了很多东西,包括.extern指令,用asm__asm__等声明C函数,但是没有一个工作,所以我正在寻找一个这样做的最小例子.对这样的例子的引用同样值得欢迎.

old*_*mer 6

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

asm:

bl cfun

c:
void cfun ( void )
{

}
Run Code Online (Sandbox Code Playgroud)

你可以自己试试.对于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)
Run Code Online (Sandbox Code Playgroud)

知道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.
Run Code Online (Sandbox Code Playgroud)

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

mov lr,pc
ldr pc,=cfun
Run Code Online (Sandbox Code Playgroud)

你得到

d6008034:   e1a0e00f    mov lr, pc
d6008038:   e51ff000    ldr pc, [pc, #-0]   ; d6008040 
...
d6008040:   d60084c4    strle   r8, [r0], -r4, asr #9
Run Code Online (Sandbox Code Playgroud)

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

如果你不想玩这样的汇编游戏并希望控制,那么你创建位置来保存函数的地址并自己加载到电脑中:

    mov lr,pc
    ldr pc,cfun_addr

...

cfun_addr:
    .word cfun
Run Code Online (Sandbox Code Playgroud)

编译:

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
Run Code Online (Sandbox Code Playgroud)

最后,如果你想进入ARM和拇指混合或可以的现代ARM世界(例如使用bx lr而不是mov pc,lr)那么你会想要使用bx

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

cfun_addr:
    .word cfun
Run Code Online (Sandbox Code Playgroud)

当然,如果你想保留它们,你需要另外一个寄存器才能这样做,并记得在你打电话给C之前和之后推送和弹出你的链接寄存器和另一个寄存器.

  • 如果您还不知道调用约定以了解用于传递参数的寄存器,例如,请执行以下两项操作之一,编写一些C代码来调用函数并反汇编以查看编译器执行的操作.正确的答案是查找其他人指出的调用约定. (2认同)
  • 并且你是对的我只展示了如何让ARM指令调用一个函数,不要让拇指指令调用C函数我可以编辑答案并告诉你如何做到这一点...你提到了一个皮质A所以我假设在大多数情况下,你正在运行手臂指令而不是拇指,利用你拥有的马力,而不是一些总线受限的东西. (2认同)
  • 正如Brett提到的可重定位对象可以使它更复杂,你需要使cfun_addr全局化,并且任何加载可重定位对象的人都需要在使用之前修改cfun_addr. (2认同)