RISC-V 的堆栈指针从哪里开始?堆栈指针指向哪里?

bna*_*vnh 7 assembly riscv stack-pointer

对于 RISC-V,堆栈指针是否指向压入堆栈的最后一个数据,或者堆栈的下一个空闲地址位置?

当堆栈指针在程序的最开始处初始化时(例如crt.S)(即堆栈为空),堆栈指针是否应该初始化为指向第一个字将被压入的内存位置或之前的地址?(例如,假设堆栈的第一个元素将在 4092 处压入。那么,堆栈指针是从 4096 还是 4092 开始?)

指向其定义位置的指针将不胜感激。

Eri*_*idt 11

这不是由指令集架构指定的,就 ISA 而言,堆栈可以是满的或空的,降序或升序 \xe2\x80\x94 RISC V 指令集不支持任何特定的排列。\xc2\xa0 这些ARM 指令集中常用术语(满/空、升序/降序):满表示堆栈指针指向已占用/非空闲位置,而空表示堆栈指针指向第一个可用字。

\n

然而,堆栈方向以及堆栈指针是否引用正在使用的第一个字或第一个空闲字由 RISC V 调用约定指定的,该约定是 ABI(应用程序二进制接口)的一部分。

\n

RISC V的标准调用约定像MIPS一样使用堆栈,这是一种完全降序的方法,这意味着堆栈指针指向使用的最后一个字,并且空闲内存位于低于堆栈中当前值的地址指针寄存器。

\n

如果您想使用堆栈空间,则递减堆栈指针,新堆栈指针值和旧堆栈指针值之间的新内存即可供您使用;只需在返回调用者之前释放它,以便调用者具有与它给你的相同的堆栈指针值。(也不要写入初始未递减指针所指向的内存,因为从它指向的位置开始,以上的位置都被视为正在使用。)

\n

因此,初始堆栈指针可以指向刚刚过去的堆栈末尾,指向不可使用、预计不会使用的字,并且软件应首先递减,以找到要使用的堆栈空间。

\n

来自: https: //github.com/riscv/riscv-elf-psabi-doc/blob/master/riscv-elf.md

\n
\n

堆栈向下增长(朝向较低地址),并且堆栈指针应在过程入口处与 128 位边界对齐。传递到堆栈的第一个参数位于函数入口处堆栈指针的偏移量零处;以下参数存储在相应的较高地址。

\n
\n

并进一步:

\n
\n

过程不得依赖地址位于堆栈指针下方的堆栈分配数据的持久性。

\n
\n

可以说,相对于堆栈指针寄存器中保存的地址的偏移量 0 属于调用者,因为谁拥有该内存(因此负责该内存的释放),无论是否实际在堆栈上传递任何参数.\xc2\xa0 (如果堆栈上传递了一个或多个参数,则允许被调用者随意修改这些内存值,但内存及其释放仍然属于调用者;显然,被调用者不得修改非位于堆栈指针或上方的参数内存。)

\n

要在堆栈上分配单个字,首先递减堆栈指针寄存器 ,sp然后使用相对于 的位置 0 sp。\xc2\xa0 其他调用者将保留此内存。\xc2\xa0 返回时应sp返回其原始值在函数入口处。

\n

通常在 MIPS 和 RISC V 上,当需要堆栈空间时,汇编语言程序员或编译器将计算所有本地存储和执行函数调用(即多余参数)所需的最大堆栈空间,并在序言中的单个指令和尾声中的单个指令释放。

\n

调用者将在序言中仅分配一次堆栈空间,并在尾声中仅释放一次,而不是重复递减堆栈指针寄存器。\xc2\xa0

\n

(RISC V 还提供了 no red-zone,对于其他 ABI 来说,这是堆栈指针下方的一些空间,保证可用,而无需信号处理程序或其他任何东西异步覆盖它。)

\n
\n

一些模拟器以奇怪的方式管理堆栈空间(例如更改堆栈指针寄存器值 \xe2\x80\x94 而不是实际写入堆栈空间,这更多是硬件的工作方式),因此这些模拟器选择浪费一个词而不是让初始堆栈指针引用不同页面/部分上的内存地址,甚至可能引用不存在的内存,即使在模拟器上运行的软件应该在sp使用堆栈空间之前递减,这会导致堆栈指向有效内存区域的指针,以便实际上仅访问有效内存。

\n