You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 16:20:31