x64汇编实现阶乘触发Stack overflow错误,求问题排查
栈溢出问题分析与修复方案
兄弟,你的栈溢出根源是无限递归,而导致无限递归的核心是没遵守Windows x64的调用约定,参数传递全错了,让递归终止条件根本触发不了,最终把栈空间耗尽了。下面一步步给你拆解问题和修复方案:
核心问题拆解
1. x64调用约定完全搞错了
你用的是MSVC的x64汇编,Windows x64的调用约定(fastcall)明确规定:
- 前4个整数参数通过寄存器传递,第一个参数用
RCX,不是像x86那样压栈传递 - 调用函数前必须保证栈是16字节对齐(
call指令会压入8字节返回地址,所以调用前RSP % 16 == 0)
你在main里用push num传参数给factorial,然后在factorial里试图从[rbp+16]取参数——这位置根本不是你传的num值,而是栈上的垃圾数据,导致每次递归拿到的参数都不对,cmp rax,1永远跳不到quit标签,无限递归下去,栈直接爆了。
另外你原来的mov rdx,num也是错误的:你要取的是num存储的数值,不是它的内存地址,这会导致你打印的是地址而非用户输入的数字。
2. 递归栈管理有漏洞
就算参数传递对了,你在factorial里push rax后调用递归,返回后没把这个8字节弹出栈,栈指针会持续向下偏移,递归次数多了也会溢出(不过这次是无限递归先触发了溢出)。
修正后的代码
INCLUDELIB libcmt.lib INCLUDELIB legacy_stdio_definitions.lib EXTERN printf:PROC EXTERN scanf:PROC .DATA prompt BYTE "Enter a number: ", 0 inFmt BYTE "%d", 0 prompt3 BYTE "The factorial is: %d",10,0 disp BYTE "You Entered %d", 10,0 num QWORD ? .CODE main PROC C ; x64调用需要32字节shadow space,sub rsp,40保证栈对齐+预留空间 sub rsp, 40 ; 提示用户输入 lea rcx, prompt call printf ; 读取输入到num变量 lea rdx, num lea rcx, inFmt call scanf ; 打印用户输入的数字(取num的内容而非地址) mov rdx, [num] lea rcx, disp call printf ; 调用阶乘函数,用RCX传递参数(x64第一个整数参数约定) mov rcx, [num] call factorial ; 打印阶乘结果 mov rdx, rax lea rcx, prompt3 call printf add rsp, 40 mov rax, 0 ret main ENDP factorial PROC push rbp mov rbp, rsp ; 从RCX取参数(符合x64调用约定) mov rax, rcx cmp rax, 1 jle quit ; 终止条件:n<=1时直接返回1 ; 递归调用factorial(n-1) dec rax push rcx ; 保存原n值,避免递归修改RCX导致丢失 mov rcx, rax call factorial pop rcx ; 恢复原n值 ; 计算n * factorial(n-1) imul rcx ; rax = rax * rcx(rax是递归返回的阶乘结果,rcx是原n) quit: mov rsp, rbp pop rbp ret factorial ENDP END
关键修改点说明
- 参数传递修正:用
RCX传递阶乘函数的参数,完全符合x64调用约定 - 栈对齐修正:
main开头sub rsp,40,满足32字节shadow space要求同时保证栈16字节对齐 - 变量取值修正:把
mov rdx,num改成mov rdx,[num],正确读取用户输入的数值 - 递归栈管理:递归前保存原参数到栈,调用后恢复,避免寄存器值丢失
注意:如果输入的数字超过20,会触发整数溢出(64位无符号整数的阶乘最大值是20!),但这和本次的栈溢出是不同类型的问题。
内容的提问来源于stack exchange,提问作者cheezt
相关产品推荐
相关产品推荐

