我们首先来看函数的调用过程,下面的代码属于调用者。
la $t9, cgibin_parse_request
lui $a0, 0x41 # 'A'
lui $a2, 2
li $a0, sub_409A6C
jalr $t9 ; cgibin_parse_request
move $a1, $zero
lui
用于向一个寄存器的高位存储数,且其低位会被清0,因此lui $a0, 0x41
和li $a0, sub_409A6C
共同完成了$a0
参数的传递。被调用函数cgibin_parse_request
被保存在了寄存器$t9
中,然后通过jalr $t9
完成跳转。由于流水线效应,$a1
参数传递指令的取指在jalr
指令取指之后。
我们再来看被调用函数的开头部分
lui $gp, 0x43 # 'C'
addiu $sp, -0x50
li $gp, 0x4346D0
sw $ra, 0x30+var_s1C($sp)
sw $s6, 0x30+var_s18($sp)
sw $s5, 0x30+var_s14($sp)
sw $s4, 0x30+var_s10($sp)
sw $s3, 0x30+var_sC($sp)
sw $s2, 0x30+var_s8($sp)
sw $s1, 0x30+var_s4($sp)
sw $s0, 0x30+var_s0($sp)
sw $gp, 0x30+var_20($sp)
首当其冲的就是$gp
全局寄存器,这个寄存器是为了简化数据的访问的,访问一段区域内的数据,只需要通过该寄存器做小范围的加减即可。这里通过lui
和li
给$gp
赋值,同样因为流水线的原因,两条指令被错开了。
紧接着延伸了栈顶,$ra
保存在离$sp
最远的地方$sp+0x4C
,$gp
保存在离$sp
最近的地方$sp+0x10
,$sp+0x10
到$sp+0x30
被空了出来。
接下来是被调用函数的结束部分
lw $ra, 0x30+var_s1C($sp)
move $v0, $s0
lw $s6, 0x30+var_s18($sp)
lw $s5, 0x30+var_s14($sp)
lw $s4, 0x30+var_s10($sp)
lw $s3, 0x30+var_sC($sp)
lw $s2, 0x30+var_s8($sp)
lw $s1, 0x30+var_s4($sp)
lw $s0, 0x30+var_s0($sp)
jr $ra
addiu $sp, 0x50
可以观察到$s0
~$s6
以及$ra
都成功的重原位恢复,跳转到返回地址$ra
,并将$sp
回缩。还剩一个$gp
,在函数返回之后可以找到lw $gp, 0x4C0+var_4B0($sp)
,即将$sp+0x10
恢复给$gp
,可能会有疑惑返回之后$sp
不是已经+0x50了吗,这里应该还是因为流水线吧,执行恢复$gp
指令时,$sp
还未加上。
本来还想找一个叶子函数的,结果没有找到,挖个坑