mips递归如何正确存储函数的返回地址
我在mips中的递归函数中传递正确的地址时遇到问题 我有一个无法更改的主函数,它基本上设置值并调用我的函数,如下所示:mips递归如何正确存储函数的返回地址,mips,Mips,我在mips中的递归函数中传递正确的地址时遇到问题 我有一个无法更改的主函数,它基本上设置值并调用我的函数,如下所示: jal findX my函数对浮点数执行计算,并执行以下操作: findX: addi $sp, $sp, -4 #make room on stack sw $ra, 0($sp) #save ra #a series of calculations that are not part of the problem jal evaluate #gives a
jal findX
my函数对浮点数执行计算,并执行以下操作:
findX:
addi $sp, $sp, -4 #make room on stack
sw $ra, 0($sp) #save ra
#a series of calculations that are not part of the problem
jal evaluate #gives an output based on the calculations before that triggers various branches
bc1t x_return
jal findX
x_return
mov.s $f0,$f17 #holds the value to return
lw $ra, 0($sp) #restore $ra
addi $sp, $sp, 4 #restore stack pointer
jr $ra
问题是,一旦找到正确的值,我的返回地址就会出现混乱,“jr$ra”再次调用函数而不是返回。如何修复此问题我认为我正确地遵循了mips递归约定 您在
findX
中的推送/弹出代码很好,符合ABI标准。而且,无论ABI如何,$sp
的预减法始终有效
您没有显示evaluate
,但它可能是嫌疑犯。如果有,那么检查代码和诊断问题就相当容易了
当返回真值时,evaluate
要么更改了$sp
,要么覆盖了findX
堆栈帧的一部分
对于真实情况,您可以在evaluate
中添加断点。大多数模拟器都会保存最近N条指令的历史记录,因此您可以查看sp的更改或与之相关的存储。这也许足够了
但是,另一种方法是将一些调试代码添加到findX
,该代码与调试器一起工作。也就是说,故障条件可能太复杂,调试器无法停止(即,它没有复杂的监视条件,如gdb
),因此我们可以添加一些“助手”代码
我已经在findX
堆栈框架中添加了一些额外的值,允许一些一致性和交叉检查,以及额外的代码来检查这些值
下面是为调试而修改的代码
使用调试器,在所有nop
指令上设置断点。当你点击一个时,检查历史记录、值等。这应该有助于隔离你的问题
# recursive debug
findX:
addi $sp,$sp,-12 # make room on stack
sw $ra,8($sp) # save ra
sw $sp,4($sp) # save sp
li $t7,0xdeadbeef # save a "magic" number
sw $t7,0($sp) # we want this at the old offset for sp
# a series of calculations that are not part of the problem
# gives an output based on the calculations before that triggers various
# branches
jal evaluate
# NOTE/BUG: if this returns "true", then one of two things happened:
# (1) evaluate changed the sp register contents
# (2) evaluate overwrote the the return value we stored above
bc1t matchfound
jal findX
x_return:
li $t7,0xdeadbeef # get the expected magic number
lw $t6,0($sp) # get the stored value
bne $t6,$t7,badnews1 # did it get trashed? if yes, fly
ignore1:
# the sp value must be the same as when we called the function
lw $t6,4($sp) # get the stored value
bne $t6,$sp,badnews2 # did it get trashed? if yes, fly
ignore2:
# NOTE: we take advantage of the fact that evaluate is called from only
# _one_ place within findX, so we _know_ the ra value
la $t7,x_return # get the expected return value
bne $ra,$t7,badnews3 # did it get trashed? if yes, fly
ignore3:
# NOTE: we take advantage of the fact that evaluate is called from only
# _one_ place within findX, so we _know_ the ra value
la $t7,x_return # get the expected return value
lw $t6,8($sp) # get the saved return value
bne $t6,$t7,badnews4 # did it get trashed? if yes, fly
ignore4:
mov.s $f0,$f17 # holds the value to return
lw $ra,8($sp) # restore $ra
addi $sp,$sp,12 # restore stack pointer
jr $ra
# trap for success
matchfound:
nop # put a breakpoint here
j x_return
# trap for error
badnews1:
nop # put a breakpoint here
j ignore1
# trap for error
badnews2:
nop # put a breakpoint here
j ignore2
# trap for error
badnews3:
nop # put a breakpoint here
j ignore3
# trap for error
# NOTE: this one is special
# we may get a false positive on the first call to findX
# that is, (e.g.) main does "jal findX" and so we'd fail here
# for that case, just continue
badnews4:
nop # put a breakpoint here
j ignore4
ABI的堆栈指针指向堆栈上的第一个空插槽还是最后使用的插槽?应用程序二进制接口。除此之外,它还指定了函数如何相互调用、要保留哪些寄存器、如何传递对寄存器来说太大的参数。