3

現在、RISC-V プロセッサの実装に取り​​組んでいます。部分的に手作りのアセンブリ コードを実行する必要があります。(最後に、動的コード インジェクションがあります。) この目的のために、RISC-V アセンブリ内の関数呼び出しの基本を理解する必要があります。

このトピックは非常に役に立ちました:関数呼び出しスタックに関する混乱

しかし、私はまだ関数呼び出しのスタック レイアウトに苦労しています。次の C コードを検討してください。

void some_func(int a, int b, int* c){
   int cnt = a;
   for(;cnt > 0;cnt--){
      *c += b;
   }
}

void main(){
   int a = 5;
   int b = 6;
   int c = 0;

   some_func(a,b,&c);
}

このプログラムは、一連の加算による基本的な乗算を実装します。派生アセンブリ コード ( riscv64-unknown-elf-gcc -nostartfiles mul.c -o mul && riscv64-unknown-elf-objdump -D mul) は次のようになります。

0000000000010000 <some_func>:
   10000:   fd010113            addi    sp,sp,-48
   10004:   02813423            sd  s0,40(sp)
   10008:   03010413            addi    s0,sp,48
   1000c:   fca42e23            sw  a0,-36(s0)
   10010:   fcb42c23            sw  a1,-40(s0)
   10014:   fcc43823            sd  a2,-48(s0)
   10018:   fdc42783            lw  a5,-36(s0)
   1001c:   fef42623            sw  a5,-20(s0)
   10020:   0280006f            j   10048 <some_func+0x48>
   10024:   fd043783            ld  a5,-48(s0)
   10028:   0007a703            lw  a4,0(a5)
   1002c:   fd842783            lw  a5,-40(s0)
   10030:   00f7073b            addw    a4,a4,a5
   10034:   fd043783            ld  a5,-48(s0)
   10038:   00e7a023            sw  a4,0(a5)
   1003c:   fec42783            lw  a5,-20(s0)
   10040:   fff7879b            addiw   a5,a5,-1
   10044:   fef42623            sw  a5,-20(s0)
   10048:   fec42783            lw  a5,-20(s0)
   1004c:   fcf04ce3            bgtz    a5,10024 <some_func+0x24>
   10050:   00000013            nop
   10054:   02813403            ld  s0,40(sp)
   10058:   03010113            addi    sp,sp,48
   1005c:   00008067            ret

0000000000010060 <main>:
   10060:   fe010113            addi    sp,sp,-32
   10064:   00113c23            sd  ra,24(sp)
   10068:   00813823            sd  s0,16(sp)
   1006c:   02010413            addi    s0,sp,32
   10070:   00500793            li  a5,5
   10074:   fef42623            sw  a5,-20(s0)
   10078:   00600793            li  a5,6
   1007c:   fef42423            sw  a5,-24(s0)
   10080:   fe042223            sw  zero,-28(s0)
   10084:   fe440793            addi    a5,s0,-28
   10088:   00078613            mv  a2,a5
   1008c:   fe842583            lw  a1,-24(s0)
   10090:   fec42503            lw  a0,-20(s0)
   10094:   f6dff0ef            jal 10000 <some_func>
   10098:   00000013            nop
   1009c:   01813083            ld  ra,24(sp)
   100a0:   01013403            ld  s0,16(sp)
   100a4:   02010113            addi    sp,sp,32
   100a8:   00008067            ret

説明が必要な重要な手順は次のとおりです: ( some_func(int,int,int))

   10060:   fe010113            addi    sp,sp,-32
   10064:   00113c23            sd  ra,24(sp)
   10068:   00813823            sd  s0,16(sp)
   1006c:   02010413            addi    s0,sp,32

および: ( main())

   10000:   fd010113            addi    sp,sp,-48
   10004:   02813423            sd  s0,40(sp)
   10008:   03010413            addi    s0,sp,48

私の理解から:スタックポインタは、リターンアドレスとパラメータのためのスペースを作るために移動されます。(mainここでは特殊なケースかもしれません。) 渡された引数がスタックにある場合、どのように扱われますか? それらはどのように取得されますか?一般に、方法論は私には明らかですが、このセグメントを機能させるためには、どのように手作業でコーディングすればよいでしょうか。

関連トピックに関しては、スタックは次のようになります。

| ???                            |
| params for some_func() <???>   |
| ra of some_func()              |
| locals of main()       <int c> |
| locals of main()       <int b> |
| locals of main()       <int a> |
| params for main()      <None>  |

しかし、それだけです。これがどのように配置され、これら 2 つのリスト (関数呼び出し) がどのように関連しているか、誰か指摘できますか?

4

2 に答える 2

2

知りたいことは、RISC-V呼び出し規約で指定されています。

主なポイント:

関数の引数は通常、スタックではなくレジスタに渡さa0れます。レジスタにa7空きがない場合、引数はスタック経由でのみ渡されます。a*

一部のレジスタは呼び出し元が保存され、他のレジスタは呼び出し先が保存されます ( 2019 年 6 月 8 日に承認されたRISC-V ベース仕様の表 26.1、第 26 章 RISC-V Assembly Programmer's Handbook を参照)。つまり、関数を呼び出す前に、内容を保持したい場合、呼び出し元は呼び出し元が保存したすべてのレジスタをスタックに保存する必要があります。同様に、呼び出された関数は、呼び出し先で保存されたすべてのレジスタを独自の目的で使用する場合、それらをスタックに保存する必要があります。

于 2020-02-16T18:00:08.817 に答える
1

最初のいくつかのパラメーターは、型が許す限り、レジスターに渡されるため、スタックには表示されません。それ以外は、あなたが本当に知りたいことは不明です。スタック上にあるいくつかの引数を取得した場合、それらはスタック ポインターを調整した後もそこにとどまるため、調整されたスタック ポインターまたはフレーム ポインター (ここでは$s0明らかに) に対して相対的にそれらをアドレス指定できます。

説明が必要な重要な手順は次のとおりです。

   10060:   fe010113            addi    sp,sp,-32  # allocate space
   10064:   00113c23            sd  ra,24(sp)      # save $ra
   10068:   00813823            sd  s0,16(sp)      # save $s0
   1006c:   02010413            addi    s0,sp,32   # set up $s0 as frame pointer
于 2015-12-09T15:41:43.307 に答える