所以这里是希望的代码:
计划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"
每个节目的结尾都是具体的输出。
第一个程序运行正常。 在第二个程序中,我为打印新行创建了一个额外的功能。如果我运行这个,我会得到一个"总线错误"。
在第三个程序中,我创建了一个虚拟函数来模拟另一个stacklevel。 还有一个"总线错误"我要打印的号码不打印。
我认为我们的筹码存在一些问题。
我使用了调试器,如果我从堆栈加载我的返回地址,我的$ 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
答案 0 :(得分:2)
我查看了你的程序,大部分都看起来不错。我认为你的堆栈保存/恢复很好。但是,我至少看到了另外一个问题。
每次jal printf
后,您都会立即执行 jal fflush
,因此fflush
将获得printf的第一个参数[这是字符串指针]而不是文件描述符指针(例如stdout
)。
在mip ABI下,{em>被调用者可能会修改/销毁$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
我已经看到如果我尝试从函数中跳回来会出现buserror例如我跳转到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字节对齐。实际上,根据我见过的[可能已过时]的mip ABI文件,它表示堆栈帧必须是8字节对齐的。在这一点上这可能有点过头了,但我提到了它。
如果你从未对齐的lw
进行,那就是对齐例外,它可能会显示为SIGBUS
。此外,检查$ra
值[之前执行jr $ra
]。它也必须是"预期"值和4字节对齐。
换句话说,完全哪条指令会产生异常?
你可以做的另一件事是注释掉一些函数调用。从jal fflush
开始。然后,在您的子功能中注释jal printf
[。我注意到你做了一个"裸体" printf
一开始就打电话,这似乎很好。继续这样做,直到程序停止故障。这应该有助于本地化/呼叫。
您没有说明是否在spim
或mars
或真实H / W [可能正在运行Linux]等模拟器中运行此功能。我怀疑是真正的H / W.但是,您可以通过在模拟器[我更喜欢mars
]下运行验证您的逻辑,并为printf
和fflush
提供虚拟函数{{1} }}。请注意,jr $ra
和spim
都无法链接到mars
个文件。它们纯粹来自源代码,因此您只能使用.o
如果您在真实的H / W上运行,.s
应该能够提供有关异常来源的详细信息。如果没有,请创建一个C函数,使用gdb
为SIGBUS
设置信号处理程序[参见联机帮助页]。然后,在信号处理程序上放置一个断点。
信号处理程序的一个参数是指向具有附加信息的sigaction
结构的指针。请注意,对于siginfo_t
,SIGBUS
字段将具有可能包含更多信息的si_code
值。第三个参数,尽管BUS_*
是指向可以为您提供的内容的指针异常时的寄存器值。
在另一个答案中我给出了:mips recursion how to correctly store return address for a function另一个OP有类似的问题。我的回答添加了一些特殊的堆栈对齐和检查代码,可能会给你一些想法。