RISC-V汇编运行时异常:地址越界(0x00000004)问题排查求助
解决RISC-V汇编阶乘程序的地址越界异常
核心错误分析
地址越界(0x00000004)的直接原因是Factorial函数的栈帧操作顺序错误:
- 递归返回后,你先读取
a1,紧接着回收栈空间addi sp, sp, 8,之后再尝试读取返回地址ra——此时栈指针已指向栈帧上方的无效区域,访问4(sp)必然触发越界。 - 阶乘返回值的寄存器传递逻辑混乱:
returnFactorial把结果存在a0,但递归计算时用a1做乘法,最后main里又错误地把a1移到a0,和printf的参数要求不匹配。 - printf调用存在参数错误:没有将格式字符串地址传入
a0,且.include "printf.s"放在代码段中间会导致符号冲突或代码重复。
修正方案
1. 修复Factorial函数的栈操作逻辑
必须先恢复返回地址ra,再回收栈空间,同时统一用a0传递阶乘结果:
Factorial: beqz a0, returnFactorial addi sp, sp, -8 sw ra, 4(sp) sw a0, 0(sp) addi a0, a0, -1 jal ra, Factorial # 先恢复ra和原输入值,再计算乘积 lw ra, 4(sp) lw t0, 0(sp) addi sp, sp, 8 mul a0, a0, t0 # 用a0保存最终结果 jr ra returnFactorial: li a0, 1 # 0! = 1,结果存入a0 jr ra
2. 修正printf的参数传递
RISC-V调用约定中,printf要求a0为格式字符串地址,a1为第一个格式化参数。调整main中的参数准备逻辑:
main: la a0, ask1 li a7, 4 ecall li a7, 5 ecall jal ra, Factorial # 准备printf参数:a0=格式字符串,a1=阶乘结果 la a0, msg mv a1, a0 jal ra, printf # 正确退出程序 li a7, 10 ecall
3. 调整printf的引入方式
将.include "printf.s"放在文件最开头,确保函数被正确加载:
.include "printf.s" # 放在文件头部 .data ask1: .string "Please enter a number to compute its factorial value: " msg: .string "The result is: %d\n" .text .globl main # ... 后续代码 ...
完整修正代码
.include "printf.s" .data ask1: .string "Please enter a number to compute its factorial value: " msg: .string "The result is: %d\n" .text .globl main main: la a0, ask1 li a7, 4 ecall li a7, 5 ecall jal ra, Factorial la a0, msg mv a1, a0 jal ra, printf li a7, 10 ecall Factorial: beqz a0, returnFactorial addi sp, sp, -8 sw ra, 4(sp) sw a0, 0(sp) addi a0, a0, -1 jal ra, Factorial lw ra, 4(sp) lw t0, 0(sp) addi sp, sp, 8 mul a0, a0, t0 jr ra returnFactorial: li a0, 1 jr ra
额外注意事项
- RISC-V要求栈指针保持16字节对齐,当前8字节栈帧在递归深度较大时可能触发对齐错误,建议改为
addi sp, sp, -16并调整存储位置。 - 若输入数字过大,阶乘结果会溢出32位寄存器,可改用64位寄存器(如用
mulw或mul指令适配位数)。
内容的提问来源于stack exchange,提问作者Cocomelon
相关产品推荐
相关产品推荐

