【问题标题】:Where does the stack pointer start for RISC-V? and where does the stack pointer point at?RISC-V 的堆栈指针从哪里开始?堆栈指针指向哪里?
【发布时间】:2021-08-04 04:28:19
【问题描述】:

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

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

非常感谢您提供指向 this 定义位置的指针。

【问题讨论】:

  • 在大多数操作系统下的用户空间程序中,内核已经设置SP指向已经分配的堆栈内存区域。 crt.S 不应该改变 SP(除了推动某些东西),除非你谈论的是裸机,而不是操作系统下的用户空间。

标签: assembly riscv stack-pointer


【解决方案1】:

这不是由指令集架构指定的,就 ISA 而言,堆栈可以是满的或空的,降序或升序 - RISC V 指令集不支持特定的排列。这些术语(full/empty、ascending/descending)在 ARM 指令集中常用:full 意味着堆栈指针指向一个占用/非空闲位置,而 empty 意味着堆栈指针指向第一个可用字。

但是,堆栈方向和堆栈指针是指第一个正在使用的字还是第一个空闲字由 RISC V 调用约定指定的,它是 ABI(应用程序二进制接口)的一部分)。

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

如果你想使用堆栈空间,那么减少堆栈指针,新堆栈指针值和旧堆栈指针值之间的新内存是你使用的;只需在返回给调用者之前释放它,以便调用者具有与它给您相同的堆栈指针值。 (也不要写入初始未递减指针指向的内存,因为它指向的位置及以上都被认为是在使用中。)

因此,初始堆栈指针可以指向刚刚超过堆栈末尾的单词,该单词不能使用,也不会被预期使用,软件应该首先递减,以找到要使用的堆栈空间。

发件人:https://github.com/riscv/riscv-elf-psabi-doc/blob/master/riscv-elf.md

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

还有:

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

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

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

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

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

(RISC V 也没有提供red-zone,对于其他 ABI,它是堆栈指针下方的一些空间,保证在没有信号处理程序或其他任何异步覆盖它的情况下可用。)


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

【讨论】:

    猜你喜欢
    • 2012-12-13
    • 1970-01-01
    • 2013-01-20
    • 2020-03-09
    • 1970-01-01
    • 2015-11-16
    • 2014-08-14
    • 2016-01-21
    • 1970-01-01
    相关资源
    最近更新 更多