MIPS32 Stackframe坏了?

MIPS32 Stackframe broken?

所以这里的代码如愿:

计划 1

.text
  .globl main
  main: 
    li     $t0, 10
    mtc1    $t0, $f12
    cvt.s.w $f12, $f12               # 10.0 as single in $f12
    jal     printFloat      

    li      $v0, 4001               #sys_exit
    syscall

printFloat:
        addi    $sp, $sp, -4            #opens the stack frame            
        sw      $ra, 0($sp)             #saves the return adress

        cvt.d.s $f12, $f12              #converts the single to double
        la      $a0, strDouble          #arguments needed for printf ("%f" in $a0, upper 32 bit of the float in $a2, lower ones in $a1)
        mfc1    $a1, $f12               
        mfc1    $a2, $f13               
        jal     printf                  
        jal     fflush                  

        la      $a1, strBreakLine       #arguments needed for printf (Adress of String in $a1, "%s" in $a0)    
        la      $a0, strStringOut       
        jal     printf                  
        jal     fflush                  

        lw      $ra, 0($sp)             #restores the return adress
        addi    $sp, $sp, 4             #pops the stack frame
        jr      $ra

.data
strDouble:      .asciiz "%f"
strStringOut:   .asciiz "%s"
strBreakLine:   .asciiz "\n"


phm15fix@ci20:~$ gcc -o test -g test1.s
phm15fix@ci20:~$ ./test
10.000000

计划 2

.text
.globl main
main: 
        li     $t0, 10
        mtc1    $t0, $f12
        cvt.s.w $f12, $f12               # 10.0 as single in $f12
        jal     printFloat      

        li      $v0, 4001               #sys_exit
        syscall

printFloat:
        addi    $sp, $sp, -4            #opens the stack frame            
        sw      $ra, 0($sp)             #saves the return adress

        cvt.d.s $f12, $f12              #converts the single to double
        la      $a0, strDouble          #arguments needed for printf ("%f" in $a0, upper 32 bit of the float in $a2, lower ones in $a1)
        mfc1    $a1, $f12               
        mfc1    $a2, $f13               
        jal     printf                  
        jal     fflush
        jal     printNewLine                  

        lw      $ra, 0($sp)             #restores the return adress
        addi    $sp, $sp, 4             #pops the stack frame
        jr      $ra

printNewLine:
        addi    $sp, $sp, -4            #opens the stack frame            
        sw      $ra, 0($sp)             #saves the return adress

        la      $a1, strBreakLine       #arguments needed for printf (Adress of String in $a1, "%s" in $a0)    
        la      $a0, strStringOut       
        jal     printf                  
        jal     fflush 

        lw      $ra, 0($sp)             #restores the return adress
        addi    $sp, $sp, 4             #pops the stack frame
        jr      $ra

.data
strDouble:      .asciiz "%f"
strStringOut:   .asciiz "%s"
strBreakLine:   .asciiz "\n"


phm15fix@ci20:~$ gcc -o test -g test1.s
phm15fix@ci20:~$ ./test
10.000000
Bus error

计划 3

.text
.globl main
main:
        li     $t0, 10
        mtc1    $t0, $f12
        cvt.s.w $f12, $f12               # 10.0 as single in $f12
        jal     function

        li      $v0, 4001               #sys_exit
        syscall

function:
        addi    $sp, $sp, -4            #opens the stack frame
        sw      $ra, 0($sp)             #saves the return adress

        jal     printFloat

        lw      $ra, 0($sp)             #restores the return adress
        addi    $sp, $sp, 4             #pops the stack frame
        jr      $ra

printFloat:
        addi    $sp, $sp, -4            #opens the stack frame
        sw      $ra, 0($sp)             #saves the return adress

        cvt.d.s $f12, $f12              #converts the single to double
        la      $a0, strDouble          #arguments needed for printf ("%f" in $a0, upper 32 bit of the float in $a2, lower ones in $a1)
        mfc1    $a1, $f12
        mfc1    $a2, $f13
        jal     printf
        jal     fflush
        jal     printNewLine

        lw      $ra, 0($sp)             #restores the return adress
        addi    $sp, $sp, 4             #pops the stack frame
        jr      $ra

printNewLine:
        addi    $sp, $sp, -4            #opens the stack frame
        sw      $ra, 0($sp)             #saves the return adress

        la      $a1, strBreakLine       #arguments needed for printf (Adress of String in $a1, "%s" in $a0)
        la      $a0, strStringOut
        jal     printf
        jal     fflush

        lw      $ra, 0($sp)             #restores the return adress
        addi    $sp, $sp, 4             #pops the stack frame
        jr      $ra

.data
strDouble:      .asciiz "%f"
strStringOut:   .asciiz "%s"
strBreakLine:   .asciiz "\n"

每个程序的最后都有具体的输出。

第一个程序运行良好。 在第二个程序中,我做了一个额外的功能来打印一个新行。如果我 运行 这个我得到一个 "bus error".

在第三个程序中,我创建了一个虚拟函数来模拟另一个堆栈级别。 还有一个"bus error"我要打印的号码打印不出来

我认为我们的栈有问题。

我使用了调试器,如果我从堆栈加载我的 return-地址,我的 $ra 中的地址是错误的,我不知道为什么。如果它跳转到这个地址,我会得到 "bus error".

函数如下:

PRINT_DOUBLE:
        addi    $sp, $sp, -4            
        sw      $ra, 0($sp)             

        la      $a0, strDouble          
        mfc1    $a1, $f12               
        mfc1    $a2, $f13               
        jal     printf                  
        nop
#       lw      $a0, stdout
#        jal     fflush                  
        jal     BREAK_LINE

        lw      $ra, 0($sp)             
        addi    $sp, $sp, 4             
        jr      $ra


BREAK_LINE:
        addi    $sp, $sp, -4            
        sw      $ra, 0($sp)             

        la      $a0, strBreakLine
        jal     PRINT_STRING

        lw      $ra, 0($sp)             
        addi    $sp, $sp, 4             
        nop

        jr      $ra

PRINT_STRING:
        addi    $sp, $sp, -4            
        sw      $ra, 0($sp)             

        add     $a1, $a0, $zero         
        la      $a0, strStringOut       
        jal     printf                  
        la      $a0, stdout
        lw      $a0, 0($a0)
        jal     fflush                  

        lw      $ra, 0($sp)             
        addi    $sp, $sp, 4             
        nop
        jr      $ra

我查看了您的程序,大部分看起来都不错。我认为你的堆栈 save/restore 很好。但是,我至少看到了另一个问题。

在每个 jal printf 之后,你正在做一个 立即 jal fflush,所以 fflush 将得到 printf 的第一个参数 [这是一个字符串指针] 而不是文件描述符指针(例如 stdout)。

在 mips ABI 下,$a0-$a3 寄存器可能被 callee modified/destroyed。所以,在 printf returns $a0 之后可以是任何东西。

三个程序好像都有这个问题。 IMO,如果程序 1 正在运行,那只是平局的运气(即)在 $a0 中结束的任何东西都是无害的。也就是说,无论其中有什么,都指向一个不是文件描述符的内存位置,但 fflush 试图将其解释为一个,但幸运的是。

此外,对于 fflush$a0 应该指向一个字 [4 字节] 对齐的地址。如果不是,那可能是总线错误的来源。

要解决此问题,请将所有 fflush 调用更改为:

lw     $a0,stdout
jal    fflush

这应该可行,但是,根据 gcc 汇编程序的作用,您可能必须这样做:

la     $a0,stdout
lw     $a0,0($a0)
jal    fflush

I've seen that the buserror appears if I try to jump back from a function For example I jump to PRINT_DOUBLE, there I jump to BREAK_LINE and there I jump to PRINT_STRING If I jump then back to BREAK_LINE all is fine, but back from BREAK_LINE to PRINT_DOUBLE I get the buserror

我已经检查过您的代码,[再次] 看起来不错。调试它的一种方法是 [using gdb] 是在您的函数中单步执行 [with stepi] 并在 jal printf [或 jal fflush] 之后放置断点。

在每个 jal 前后,记下 $sp 值。 必须相同。为您的所有功能执行此操作。此外,当从函数返回时,请注意 $sp 值,然后是 lw 中的值 [进入 $ra]。他们应该都匹配 "expected"

此外,$sp 必须始终为 4 字节对齐。实际上,根据我看过的 mips ABI 文档 [可能已过时],它说堆栈帧必须是 8 字节对齐的。在这一点上这可能有点矫枉过正,但我​​提到了它。

如果你在未对齐的情况下执行 lw,这是一个对齐异常,它可能会显示为 SIGBUS。此外,在 执行 jr $ra] 之前检查 $ra 值 [。它还必须是 "expected" 值并且是 4 字节对齐的。

换句话说,究竟哪条指令产生了异常?

您可以做的另一件事是注释掉一些函数调用。从 jal fflush 开始。然后,注释掉 jal printf [在你的子功能中]。我注意到您在一开始就进行了 "naked" printf 调用,这看起来不错。继续这样做,直到程序停止出错。这应该有助于本地化 area/call.

你没有说明你是 运行 在像 spimmars 这样的模拟器中还是真实的 H/W [可能 运行 linux]。我怀疑是真的H/W。但是,您可以在模拟器 [我更喜欢 mars] 下通过 运行 验证 您的 逻辑,并为 printf 和 [=14= 提供虚拟函数] 就是这样 jr $ra。请注意,spimmars 都不能 link 到 .o 文件。它们完全从源代码开始工作,因此您只能使用您的 .s

如果您 运行 是真正的 H/W,gdb 应该能够提供有关异常来源的详细信息。如果不是,请创建一个 C 函数,使用 sigactionSIGBUS 设置信号处理程序 [请参阅联机帮助页]。然后,在信号处理程序上放置一个断点。

信号处理程序的参数之一是指向具有附加信息的 siginfo_t 结构的指针。请注意,对于 SIGBUSsi_code 字段将有一个 BUS_* 值,该值可能有更多 information.The 第三个参数,尽管 void * 是指向可以给你异常时的寄存器值。

在我给出的另一个答案中:mips recursion how to correctly store return address for a function另一个 OP 也有类似的问题。我的回答添加了一些特殊的堆栈对齐和检查代码,可能会给你一些想法。