N64平台MIPS汇编分析中,函数序言可操作自身栈帧范围外的内存吗?
N64 MIPS汇编栈帧疑问解答
先修正核心认知偏差
你对MIPS栈的边界计算方向完全搞反了,这是所有疑问的根源:
- MIPS栈是向下增长(向低地址延伸),函数执行序言调整SP后,当前函数的合法栈帧范围,是「调整后的SP(低地址边界)」到「刚进入函数时的原始SP(高地址边界)」之间的所有空间,而非从调整后的SP继续向低地址减预留大小。
- 你提到的
ADDIR SP, SP, -0x030是反汇编字体识别错误,实际是MIPS常用的不触发溢出的立即数加指令ADDIU SP, SP, -0x30,这条指令的作用就是从原始SP往低地址挪0x30,划出当前函数的栈空间。 - 换算关系:把调整后的SP记为
SP_new,刚进入函数还没改SP的原始值记为SP_old,则SP_new = SP_old - 0x30,移项可得SP_old = SP_new + 0x30。 - 也就是说,当前函数可合法使用的栈地址范围是
[SP_new, SP_new + 0x2F],你目前看到的除A1外的所有栈写入操作(RA 0x24、S0 0x20、V0 0x2C、R0 0x10)的偏移,都落在这个区间内,完全符合规范,不需要往SP_new更低的地址找空间。
两个核心疑问的具体解释
1. 仅用到少量空间,为什么要预留0x30?
预留栈空间的大小不是只按要保存的寄存器数量算,还要满足MIPS ABI的栈对齐要求:
- N64常用的O32或原生N64 ABI要求栈必须是16字节对齐,保证浮点寄存器存取、栈上变量访问的硬件效率。
- 你目前看到的最大栈偏移是存V0的
0x2C(SP),如果只预留刚好覆盖的空间,最终栈对齐会被破坏,编译器或手写汇编都会向上取整到最近的16字节倍数,0x30刚好是16的整数倍,完全符合规范。 - 剩下没用到的栈空间,要么是给函数后续的局部变量预留的(你目前只跟踪了序言和两次写操作,后面大概率还会用到),要么是对齐填充的冗余空间。
2. A1存到SP+0x34真的是越界修改其他函数栈吗?
先换算这个地址的实际位置:SP_new + 0x34 = (SP_old - 0x30) + 0x34 = SP_old + 0x4
这个位置确实在常规栈范围外,通常是抄录或反汇编的信息存在小误差,常见原因有两种:
- 指令顺序抄错:
SW A1, 0x0034(SP)实际执行在ADDIU SP, SP, -0x30之前,此时SP还等于SP_old,SP_old + 0x34属于O32 ABI规定的调用方栈上的参数溢出区(调用方必须提前在栈上预留至少4字的溢出空间,供被调用函数保存入参),这种写入完全合法。 - 偏移识别错误:如果反汇编的序言第一句实际是
ADDIU SP, SP, -0x34而非-0x30,那SP_new + 0x34刚好等于SP_old,也就是栈帧的最高边界,完全在合法范围内,大概率是看反汇编时漏看了偏移最后一位的4。
补充:就算真的写到
SP_old+0x4,N64早期的手写SDK代码偶尔会直接复用调用方栈上提前留好的未使用空间,只要调用方提前标记这块空间可复用,就不属于非法栈覆盖。
内容的提问来源于stack exchange,提问作者Ploufy
相关产品推荐
相关产品推荐

