RISC-V汇编 - 堆栈布局 - 函数调用

时间:2015-12-09 15:15:13

标签: c assembly riscv isa

目前我正在使用RISC-V处理器实现。我需要运行部分手工编写的汇编代码。 (最后会有动态代码注入。)为此,我必须了解RISC-V汇编中函数调用的基础知识。

我发现此主题非常有用:confusion about function call stack

但我仍然在努力调用函数调用的堆栈布局。请考虑以下c代码:

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

该程序通过一系列添加实现基本乘法。派生的汇编代码(riscv64-unknown-elf-gcc -nostartfiles mul.c -o mul&amp;&amp; riscv64-unknown-elf-objdump -D mul)如下所示:

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

需要澄清的重要步骤是:(some_func(int,int,int))

   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>  |

根据我的理解:移动stackpointer为返回地址和参数腾出空间。 (主要可能是这里的一个特例。)当位于堆栈上时,如何处理传递的参数?他们是如何获得的?一般来说,这种方法对我来说很清楚,但是为了工作,我将如何手工编写这段代码。

关于相关主题,堆栈应该看起来有点像

/index/home/?id=1

但这就是它。任何人都可以指出,这是如何安排的,以及这两个列表(函数调用)如何相关?

2 个答案:

答案 0 :(得分: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

答案 1 :(得分:1)

您想知道的由RISC-V calling conventions指定。

要点:

函数参数通常在a0中传递给a7寄存器,而不是堆栈中。仅当a*寄存器中没有剩余空间时,才会通过堆栈传递参数。

某些寄存器被保存为调用方,而另一些被保存为被调用方(参见批准日期为RISC-V base specification,第26章RISC-V汇编程序员手册表26.1,已批准,2019年6月)。这意味着在调用函数之前,如果调用者想要保留其内容,则必须将所有调用者保存的寄存器保存到堆栈中。同样,如果要使用被调用方保存的寄存器用于自己的目的,则必须将所有被调用方保存的寄存器保存到堆栈中。