目前我正在使用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
但这就是它。任何人都可以指出,这是如何安排的,以及这两个列表(函数调用)如何相关?
答案 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月)。这意味着在调用函数之前,如果调用者想要保留其内容,则必须将所有调用者保存的寄存器保存到堆栈中。同样,如果要使用被调用方保存的寄存器用于自己的目的,则必须将所有被调用方保存的寄存器保存到堆栈中。