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

x64 Windows下NASM汇编栈操作触发访问违例,求正确实现方案

问题:x64 Windows汇编调用ExitProcess触发段错误的原因与修复方案

问题背景

自研语言编译器采用栈存储变量(压入值+记录偏移),针对代码:

num x = 10
exit(x)

生成NASM汇编后运行触发错误码-1073741819(段错误/访问违例)。尝试调整偏移、添加default rel、简化代码均未解决问题。

初始生成的汇编代码:

extern ExitProcess
section .text
global _start
_start:
        push rbp            ;adding base pointer to stack 
        mov rbp, rsp        ;getting new base pointer
        sub rsp, 32         ;allocating shadow space

        mov rax, 10         ;x = 10, 
        push rax            ;push rax to stack, offset is 0
        push QWORD [rsp+0]  ;push the top of stack pointer to stack because x is needed
        pop rcx             ;top of stack used for exit(x)

        mov ecx, ecx        ;clear top 32bits of rcx
        call ExitProcess    ;exit program

简化后的测试汇编代码:

default rel
extern ExitProcess
section .text
global _start
_start:
    push rbp
    mov rbp, rsp
    sub rsp, 32

    mov rax, 10
    push rax
    mov rcx, rsp
    mov ecx, ecx

    add rsp, 32
    call ExitProcess

错误原因

  1. 栈平衡破坏:x64 Windows调用约定中,call指令会自动将返回地址压栈(占8字节)。你在调用ExitProcess前执行add rsp,32,直接让rsp偏离了正确的栈顶位置,函数返回时会尝试从非法地址读取返回地址,触发访问违例。
  2. 参数传递错误:简化代码中mov rcx, rsp是把栈指针本身的值传给ExitProcess,而非栈中存储的变量值10,完全不符合exit(x)的需求。
  3. 偏移计算混乱:初始代码中push rax后rsp已变化,后续虽能正确读取变量值,但栈操作冗余且未维护最终的栈平衡。

正确实现方式

方案1:用rbp基址存储变量(规范栈帧用法)

x64 Windows下推荐用rbp定位栈变量,避免rsp随push/pop动态变化导致偏移计算错误:

extern ExitProcess
section .text
global _start
_start:
    push rbp
    mov rbp, rsp
    sub rsp, 32         ; 分配x64 Windows要求的32字节影子空间

    ; 将变量x存储到栈帧的rbp-8位置(push rbp后rbp指向旧rbp,sub rsp32后,rbp-8为第一个可用栈位置)
    mov QWORD [rbp-8], 10

    ; 调用ExitProcess,将x的值传入rcx(x64 Windows调用约定:第一个参数放rcx)
    mov rcx, [rbp-8]
    mov ecx, ecx        ; 清除rcx高32位,确保是合法的32位退出码

    ; 无需手动调整rsp,调用时会自动维护栈平衡
    call ExitProcess

方案2:用rsp直接操作(需严格跟踪rsp变化)

如果坚持用rsp偏移,必须精准计算每次栈操作后的rsp位置:

extern ExitProcess
section .text
global _start
_start:
    push rbp
    mov rbp, rsp
    sub rsp, 32         ; 分配影子空间

    mov rax, 10
    push rax            ; rsp当前为rbp-32-8 = rbp-40

    ; 读取栈中存储的x值到rcx
    mov rcx, [rsp]
    mov ecx, ecx

    ; 恢复因push rax占用的8字节栈空间,保持影子空间的完整性
    add rsp, 8
    call ExitProcess

关键注意事项

  • x64 Windows调用约定:调用函数前必须分配32字节影子空间,调用过程中不能随意修改rsp(除非手动维护完整的栈平衡)。
  • ExitProcess接收32位退出码,需确保rcx高32位为0,mov ecx, ecx是高效的实现方式。
  • 用rbp作为基址访问栈变量更稳定,rsp会随push/pop动态变化,极易出现偏移计算错误。

内容的提问来源于stack exchange,提问作者Natrome tex

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 06:54:53