【问题标题】:Stack memory operations at the beginning of main function in assembly汇编中 main 函数开头的堆栈内存操作
【发布时间】:2020-04-05 16:56:46
【问题描述】:

我把一个很简单的C程序转成汇编文件(这里是RISC-V ISA),对栈指针做了一些我看不懂的操作。

C 程序:

int main()
{
        int a = 10;
        return 0;
}

相关的汇编代码:

        .file   "alternating_branches_2.c"
        .option nopic
        .text
        .align  1
        .globl  main
        .type   main, @function
main:
        addi    sp,sp,-32
        sd      s0,24(sp)
        addi    s0,sp,32
        li      a5,10
        sw      a5,-20(s0)
        li      a5,0
        mv      a0,a5
        ld      s0,24(sp)
        addi    sp,sp,32
        jr      ra
        .size   main, .-main
        .ident  "GCC: (GNU) 8.3.0"

这是我的理解。

sp 包含栈内存的最后地址。因此,压入堆栈会降低 sp 的值。 s0是帧指针,指向sp之前的值。

在第一行中,从堆栈指针开始减少 32 的偏移量。这是要创建堆栈帧吗?通常,在堆栈中,推送操作会减少堆栈指针。但是,由于已经创建了一个栈帧,并且 sp 现在指向栈的较低内存,所以 push 会增加 sp 的值吗?

-------------| <--sp
-------------|
-------------|
-------------|
-------------|
-------------|
-------------|

栈帧创建后:

-------------| <--s0
-------------|
-------------|
-------------|
-------------|
-------------|
-------------| <--sp

现在,压入堆栈必须导致 sp 增加,对吗?或者也可以使用帧指针 s0 来完成推送?如果这是一个非常基本的问题,我深表歉意。谢谢。

【问题讨论】:

  • risc-v 没有推送指令,编译器无论如何都无法使用push 来初始化变量。它只是通过移动sp 为堆栈框架创建空间,然后定期存储到堆栈框架中,而不是推送或弹出。
  • @Peter Cordes 我明白了,谢谢。第一条指令:addi sp, sp, -32 创建一个堆栈帧吗?另外我很抱歉没有在问题中提出这个问题,但是 OFFSET(SP) 是否将数据从偏移存储到更高的内存或更低的内存?
  • 寻址方式一如既往的offset + register。是加的,不是减的。是的,移动sp 会分配或释放堆栈上的空间。在这种情况下,为堆栈帧分配 32 个字节。
  • @Peter Cordes 谢谢。此外,由于已经创建了堆栈帧,创建局部变量(在堆栈帧内)是否会增加 sp,而不是通常将数据“推送”到堆栈以减少 sp?
  • 不,当然不是。这有什么意义?如果 SP 增加,它将缩小堆栈帧。相反,本地变量被放置在编译器希望堆栈帧中的任何地方。带有gcc -fverbose-asm 的编译器让它在每条指令上放置 cmets。 How to remove "noise" from GCC/clang assembly output?(对于您的情况,您可能确实想要未优化的-O0 代码,或者在您的变量上使用volatile,这样它们就不会被优化。)

标签: assembly callstack riscv


【解决方案1】:

与 x86 ISA 不同——它具有用于压入和弹出堆栈的专用指令,RISC-V ISA 只能通过 loadstore 指令访问内存。它没有访问堆栈(即内存)和修改堆栈指针的 push 和 pop 指令。


由于堆栈向下增长,减少堆栈指针sp 会在堆栈上分配空间,而增加它会释放空间。也就是说,addi sp,sp,-32 在堆栈上分配 32 个字节,addi sp,sp,32 从堆栈中释放 32 个字节。前者创建新的栈帧,后者销毁它。

s0 寄存器 - 与 fp 相同 - 是 帧指针 并指向堆栈帧的开头。它的当前值保存在新创建的堆栈帧的最开始处 (sd s0,24(sp))。然后,将其设置为指向堆栈帧开头的地址(addi s0,sp,32)。最后,在离开函数之前,帧指针恢复为之前的值(ld s0,24(sp))。

堆栈帧创建和帧指针设置后的堆栈表示为:

|     ...     |
|-------------|<- s0 | beginning of stack frame
| previous s0 |
|-------------|<- sp-24
|-------------|
|-------------|
|-------------|
|-------------|
|-------------|
|-------------|
|-------------|<- sp | end of stack frame

【讨论】:

    猜你喜欢
    • 2018-07-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-11
    • 2014-12-19
    • 1970-01-01
    • 2017-11-18
    • 1970-01-01
    相关资源
    最近更新 更多