x64汇编:分配16字节栈空间触发段错误,24字节却无异常?
问题根源:x86-64 System V调用约定的栈对齐要求
你的问题本质是违反了**x86-64 System V应用二进制接口(ABI)**的栈对齐规则,具体拆解如下:
核心规则
x86-64 System V ABI明确要求:在执行call指令调用任何函数之前,栈指针%rsp必须是16字节对齐的。
- 执行
call时,会自动将8字节的返回地址压入栈,因此被调用函数的入口处,%rsp会变成16n - 8(即8字节不对齐)——这是正常状态,函数内部可通过调整%rsp或使用基指针%rbp重新对齐栈。
第一个代码段触发段错误的原因
程序启动时,启动代码会保证调用main前%rsp是16字节对齐的。执行call main后,8字节返回地址被压栈,因此main入口处的%rsp是8字节不对齐的(16n - 8)。
看你第一个main的操作:
main: subq $16, %rsp # 此时%rsp = (16n-8) -16 = 16(n-2)+8 → 仍然是8字节不对齐 movq %rsp, %rsi leaq infmt, %rdi movq $0, %rax call scanf # 调用前%rsp未满足16字节对齐要求,违反ABI规则 ...
在调用scanf前,%rsp仍处于8字节不对齐状态,直接违反调用约定。scanf内部会假设栈是16字节对齐的,执行向量指令操作、栈上变量对齐访问等逻辑时,就会触发段错误。
而分配8字节或24字节时能正常运行:
- 分配8字节:
subq $8, %rsp→%rsp = (16n-8)-8 =16(n-1)→ 16字节对齐,满足调用scanf的要求。 - 分配24字节:
subq $24, %rsp→%rsp=(16n-8)-24=16(n-2)→ 16字节对齐,同样符合规则。
第二个代码段正常工作的原因
加入基指针操作后,栈对齐状态被修正:
main: pushq %rbp # 压入8字节的%rbp,%rsp从(16n-8)变为16n-16 → 16字节对齐 movq %rsp, %rbp subq $16, %rsp # %rsp变为16(n-2) → 仍然保持16字节对齐 movq %rsp, %rsi leaq infmt, %rdi movq $0, %rax call scanf # 调用前%rsp是16字节对齐,符合ABI规则 ...
pushq %rbp将%rsp调整为16字节对齐,后续subq $16也维持了对齐状态,调用scanf时完全符合约定,因此不会触发段错误。
替代解决方案
如果不想使用基指针%rbp,只需确保调用任何函数前%rsp是16字节对齐即可。针对main入口的8字节不对齐状态,你可以:
- 总共分配24字节栈空间(8字节用于对齐,16字节用于局部变量),即执行
subq $24, %rsp; - 先执行
andq $-16, %rsp强制将%rsp对齐到16字节边界,再分配所需的16字节局部变量空间。
内容的提问来源于stack exchange,提问作者TheSinisterStone
相关产品推荐
相关产品推荐

