【问题标题】:How to access more than 4 arguments in an ARM assembly function?如何在 ARM 汇编函数中访问 4 个以上的参数?
【发布时间】:2013-02-25 16:22:58
【问题描述】:

在我的汇编函数中,有 6 个参数。当我尝试访问第四个和第五个参数时,它们是错误的,这是我在 arm cortex-8a 上的代码

push {r4-r8,lr}
ldr r6, [sp]
ldr r7, [sp, #4]

我检查了 [sp] 内存,r4-r8 的值有误。但是,如果有 3 个或更少的参数,[sp] 会给出正确的 r4-r8 值。我错过了什么吗?

【问题讨论】:

    标签: assembly arm


    【解决方案1】:

    为什么不试试呢?

    unsigned int fun ( unsigned int, unsigned int, unsigned int, unsigned int, unsigned int );
    unsigned int myfun ( void )
    {
    return(fun(1,2,3,4,5));
    }
    

    组装然后拆卸

    > arm-none-eabi-gcc -O2 -c fun.c -o fun.o
    > arm-none-eabi-objdump -D fun.o 
    

    汇编输出包含

    00000000 <myfun>:
       0:   e52de004    push    {lr}        ; (str lr, [sp, #-4]!)
       4:   e3a03005    mov r3, #5
       8:   e24dd00c    sub sp, sp, #12
       c:   e58d3000    str r3, [sp]
      10:   e3a01002    mov r1, #2
      14:   e3a02003    mov r2, #3
      18:   e3a03004    mov r3, #4
      1c:   e3a00001    mov r0, #1
      20:   ebfffffe    bl  0 <fun>
      24:   e28dd00c    add sp, sp, #12
      28:   e49de004    pop {lr}        ; (ldr lr, [sp], #4)
      2c:   e12fff1e    bx  lr
    

    前四个操作数按预期在寄存器 r0-r3 中。尽管第五个操作数被放置在堆栈上。为什么编译器为操作数分配 12 个字节而不是 4 个字节,这是一个谜……也许看到函数会更有意义:

    unsigned int fun ( unsigned int a, unsigned int b, unsigned int c, unsigned int d, unsigned int e )
    {
        return(a+b+c+d-e);
    }
    

    组装和拆卸

    arm-none-eabi-gcc -O2 -c fun.c -o fun.o
    arm-none-eabi-objdump -D fun.o 
    
    00000000 <fun>:
       0:   e0811000    add r1, r1, r0
       4:   e0812002    add r2, r1, r2
       8:   e59d0000    ldr r0, [sp]
       c:   e0823003    add r3, r2, r3
      10:   e0600003    rsb r0, r0, r3
      14:   e12fff1e    bx  lr
    

    所以被调用者只知道操作数是堆栈上的第一件事,而不关心调用者创建的堆栈帧。所以在这种情况下调用者分配 12 个字节而不是 4 个字节是个谜。

    arm-none-eabi-gcc --version
    arm-none-eabi-gcc (GCC) 4.7.2
    Copyright (C) 2012 Free Software Foundation, Inc.
    This is free software; see the source for copying conditions.  There is NO
    warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
    

    看到编译器实际实现调用约定可以使调用约定本身的阅读更容易理解。或者,如果你在你感兴趣的编译器上为你感兴趣的特定函数原型制作这样的例子,你不必阅读约定,你只需让你的调用者或被调用者无论你感兴趣,匹配编译器正在做什么为自己。

    【讨论】:

    • 额外的 12 个字节用于保持 sp 与 ABI 要求的 16 个字节的倍数对齐。
    【解决方案2】:

    额外的参数在堆栈上传递,然而SP 指向它们函数的入口处。在您的序言中,您正在推送要保存的寄存器,这会更改 SP,因此您需要考虑它。

    r4,r5,r6,r7,r8 和 lr 是 6 个寄存器,因此您需要将 SP 偏移量调整 6*4 = 24 字节。因此,请尝试以下操作:

    push {r4-r8,lr}        // 6 regs are pushed
                           // SP is decremented by 6*4 = 24 bytes
    ldr r6, [sp, #(0+24)]  // get first stack arg
    ldr r7, [sp, #(4+24)]  // get second stack arg
    

    如果您对SP 进行更多操作,例如为堆栈变量分配空间,您可能也必须考虑到这一点。

    【讨论】:

      【解决方案3】:

      AAPCS 标准

      http://infocenter.arm.com/help/topic/com.arm.doc.ihi0042f/IHI0042F_aapcs.pdf 5.5 参数传递包含答案。

      它是一种算法,不太容易理解,但回答问题的关键部分似乎是:

      C.5 如果 NCRN 小于 r4 并且 NSAA 等于 SP,则参数在核心之间拆分 寄存器和堆栈。论证的第一部分是 c opied 到核心寄存器开始在 NCRN 直至并包括 r3。参数的其余部分被复制到堆栈中,从 国家安全局。 NCRN 设置为 r4 并且 NSAA 增加了参数的大小减去 通过的金额 在寄存器中。现在已分配参数。

      正如其他人所说,意味着 4 个寄存器 + 堆栈。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-04-23
        • 1970-01-01
        • 2013-06-25
        • 1970-01-01
        • 2019-08-26
        • 1970-01-01
        相关资源
        最近更新 更多