8

私のアセンブリ関数には、6 つの引数があります。4 番目と 5 番目の引数にアクセスしようとすると、それらが間違っています。arm cortex-8a のコードは次のとおりです。

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

[sp] メモリを確認しましたが、r4 ~ r8 の値が間違っています。ただし、引数が 3 つ以下の場合、[sp] は正しい r4-r8 値を返します。私は何かが恋しいですか?

4

3 に答える 3

12

試してみませんか?

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

最初の4つのオペランドは、予想どおりレジスタr0〜r3にあります。ただし、5番目のオペランドはスタックに配置されます。コンパイラがオペランドに4バイトではなく12バイトを割り当てる理由、それは謎です...おそらく関数を見る方が理にかなっているでしょう:

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

したがって、呼び出し先は、オペランドがスタックの最初のものであることを単に知っており、呼び出し元によって作成されたスタックフレームを気にしません。したがって、この場合、呼び出し元が4バイトではなく12バイトを割り当てたのは謎です。

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.

コンパイラが実際に呼び出し規約を実装しているのを見ると、呼び出し規約自体の読み方がわかりやすくなります。または、興味のあるコンパイラで興味のある特定の関数プロトタイプのこのような例を作成する場合は、規則を読む必要はありません。興味のある呼び出し元または呼び出し先を、コンパイラの実行内容と一致させるだけです。それ自体のために。

于 2013-02-25T19:05:40.580 に答える
10

追加の引数はスタックに渡されますが、関数のエントリでSPそれらを指します。プロローグでは、保存するレジスタをプッシュしています。これにより が変更されるため、それを考慮する必要があります。SP

SPr4、r5、r6、r7、r8、および lr は 6 つのレジスタであるため、オフセットを 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、たとえばスタック変数にスペースを割り当てる場合は、それも考慮する必要があります。

于 2013-02-25T16:46:05.320 に答える
5

AAPCS 規格

http://infocenter.arm.com/help/topic/com.arm.doc.ihi0042f/IHI0042F_aapcs.pdf 5.5 Parameter Passing に答えが含まれています。

アルゴリズムであるため、理解するのはそれほど簡単ではありませんが、質問に答える重要な部分は次のようです。

C.5 NCRN が r4 未満であり、NSAA が SP と等しい場合、引数はコア レジスタとスタックの間で分割されます。引数の最初の部分は、NCRN から r3 までのコア レジスタにコピーされます。引数の残りは、NSAA から始まるスタックにコピーされます。NCRN は r4 に設定され、NSAA は引数のサイズからレジスタに渡された量を差し引いた分だけインクリメントされます。引数が割り当てられました。

他の人が言ったように、これは 4 つのレジスタ + スタックを意味します。

于 2016-06-24T15:30:17.650 に答える