因此,这里的代码如下所愿:
程序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"在每个程序的末尾都是特定的输出。
第一个程序运行得很好。在第二个程序中,我增加了一个打印新行的功能。如果我运行这个,我会得到一个“公共汽车错误”。
在第三个程序中,我制作了一个虚拟函数来模拟另一个堆栈级别。也有一个“总线错误”,我想打印的数字不是打印。
我想我们的堆叠有问题。
我使用调试器,如果我从堆栈加载返回地址,我的$ra中有错误的地址,我不知道为什么。如果它跳到这个地址,我会得到“总线错误”。
以下是功能:
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发布于 2016-05-17 17:46:10
我看过你的节目了,大部分都没问题。我认为您的堆栈保存/还原是好的。但是,我至少看到了另外一个问题。
在每个jal printf之后,您将立即执行jal fflush,因此fflush将获得printf的第一个参数,即字符串指针,而不是文件描述符指针(例如stdout)。
根据mips,$a0-$a3寄存器可以被被调用者修改/销毁。所以,在printf返回之后,$a0可以是任何东西。
这三个程序似乎都有这个问题。国际海事组织,如果程序1起作用,那只是抽签的运气(即)在$a0中结束的一切都是无害的。也就是说,不管里面有什么,都指向一个不是文件描述符的内存位置,但是fflush试图将其解释为文件描述符,并从中获益。
另外,对于fflush,$a0应该指向一个地址,即单词4字节对齐。如果不是,那可能是总线错误的根源。
要解决这个问题,请将所有fflush调用更改为:
lw $a0,stdout
jal fflush这可能会起作用,但是,根据gcc汇编程序所做的工作,您可能必须这样做:
la $a0,stdout
lw $a0,0($a0)
jal fflush我已经看到,如果我尝试从一个函数跳回(例如,我跳到PRINT_DOUBLE,在那里跳到BREAK_LINE,然后跳到PRINT_STRING,如果我跳回BREAK_LINE,一切都很好),那么就会出现总线错误,但是从BREAK_LINE跳到PRINT_DOUBLE,我得到了buserror
我已经查过你的密码了,又一次看上去很好。调试这一点的一种方法是使用gdb,在函数中单步使用stepi,并在jal printf或jal fflush之后放置断点。
在每个jal之前和之后,注意$sp值。一定是一样的。为了你所有的功能。另外,当从函数返回时,请注意$sp值,然后是从lw返回到$ra的值。他们都应该符合“预期”
此外,$sp必须在任何时候都对齐4字节。实际上,根据我看过的一份mips文档,它说堆栈帧必须对齐8字节。在这一点上这可能有点过分,但我要提一提。
如果您从未对齐处执行lw,则这是一个对齐异常,它可能会显示为SIGBUS。此外,在执行$ra之前检查jr $ra值。它还必须是“预期”值,并对齐4字节。
换句话说,到底哪条指令会产生异常?
您可以做的另一件事是注释掉一些函数调用。从jal fflush开始。然后,在您的子函数中注释掉jal printf。我注意到,您在一开始就做了一个“裸”printf调用,这似乎很好。继续这样做,直到程序停止故障。这应该有助于使这个地区[呼叫]本地化。
您没有说明您是在像spim或mars这样的模拟器中运行,还是在真正的H/W (可能是在运行linux )中运行。我怀疑真实的H/W.但是,您可以通过在我更喜欢的mars模拟器下运行来验证您的逻辑,并为只执行jr $ra的printf和fflush提供虚拟函数。请注意,spim和mars都不能链接到.o文件。它们完全来源于工作,所以您只能使用.s。
如果您在实际的H/W上运行,gdb应该能够提供有关异常源的详细信息。如果没有,请创建一个C函数,该函数使用SIGBUS为sigaction设置信号处理程序(参见手册)。然后,在信号处理程序上放置一个断点。
信号处理程序的一个参数是指向具有其他信息的siginfo_t结构的指针。注意,对于SIGBUS,si_code字段将有一个可能有更多information.The第三参数的BUS_*值,尽管void *是指向在异常发生时可以为您提供寄存器值的指针。
在我给出的另一个答案中:mips recursion how to correctly store return address for a function,另一个OP,也有类似的问题。我的答案添加了一些特殊的堆栈对齐和检查代码,这可能给你一些想法。
https://stackoverflow.com/questions/37271004
复制相似问题