首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何从ARM程序集调用C函数?

如何从ARM程序集调用C函数?
EN

Stack Overflow用户
提问于 2011-12-07 20:45:45
回答 4查看 23.1K关注 0票数 16

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

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2011-12-07 22:44:27

您需要阅读手臂和/或知道指令集是全部,通常您会想要这样做

代码语言:javascript
复制
asm:

bl cfun

c:
void cfun ( void )
{

}

你自己试试吧。对于gnu和gcc,如果您使用clang将c代码获取到一个对象,而gnu作为汇编程序,它也应该工作得很好。不知道你在用什么。

上面提到的问题是bl的范围有限,

代码语言:javascript
复制
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指令之后的指令,那么如果您读到程序计数器寄存器:

代码语言:javascript
复制
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看起来像这样:

代码语言:javascript
复制
mov lr,pc
ldr pc,=cfun

你会得到

代码语言:javascript
复制
d6008034:   e1a0e00f    mov lr, pc
d6008038:   e51ff000    ldr pc, [pc, #-0]   ; d6008040 
...
d6008040:   d60084c4    strle   r8, [r0], -r4, asr #9

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

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

代码语言:javascript
复制
    mov lr,pc
    ldr pc,cfun_addr

...

cfun_addr:
    .word cfun

汇编:

代码语言:javascript
复制
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

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

代码语言:javascript
复制
    add lr,pc,#4
    ldr r1,cfun_addr
    bx r1
...

cfun_addr:
    .word cfun

当然,您需要另一个寄存器才能做到这一点,如果您想要保存链接寄存器,请记住在调用C之前和之后推动和弹出链接寄存器和其他寄存器。

票数 8
EN

Stack Overflow用户

发布于 2016-10-14 12:00:40

最小可运行armv7示例

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

代码语言:javascript
复制
/* 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上:

代码语言:javascript
复制
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

输出:

代码语言:javascript
复制
hello world

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

代码语言:javascript
复制
push {ip, lr}

而不是:

代码语言:javascript
复制
push {lr}

GitHub上的推广样板:https://github.com/cirosantilli/arm-assembly-cheat/blob/82e915e1dfaebb80683a4fd7bba57b0aa99fda7f/c_from_arm.S的例子

票数 2
EN

Stack Overflow用户

发布于 2011-12-07 21:01:59

您需要armeabi-v7a的规范,描述调用堆栈、寄存器(被调用者和调用者)等等。然后查看编译的C代码的程序集输出的语法等等。当尝试在共享库或可重定位的对象中调用函数时,事情要复杂得多。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/8422287

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档