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

为何NASM汇编代码与C程序编译后运行崩溃?

问题:Win64下NASM汇编与C程序链接后修改栈指针时崩溃

单独运行NASM生成的.exe文件时一切正常,但将其与C程序编译链接后,执行到sub rsp,8或push这类修改栈指针的指令时程序崩溃。运行环境为Windows 10 x86-64。

原NASM代码

section .text
extern GetStdHandle
extern WriteFile
;extern ExitProcess
global hw
hw:
    mov rcx,-11
    call GetStdHandle
    mov r12,rax

    mov rcx,r12
    lea rdx,[rel msg]
    mov r8,16
    mov r9,rsp
    push 0
    call WriteFile

    ;mov rcx,0
    ;call ExitProcess

    ret

section .data
msg: db "hello"
msglen: equ $-msg

原C代码

#include <stdio.h>
extern int hw();
int main(){
    printf("before\n");
    printf("hw: %d\n", hw());
    printf("after\n");
    //hw
}

编译执行命令

nasm -f win64 test2c.asm
gcc main.c test2c.obj -o test.exe
test

问题现象

程序输出before后直接崩溃。另外若移除rel关键字,gcc会报错:relocation truncated to fit: IMAGE_REL_AMD64_ADDR32 against .data'`。尝试过手动栈对齐,但任何修改栈指针的指令都会触发崩溃。


修复后的NASM代码

section .text
extern GetStdHandle
extern WriteFile
;extern ExitProcess
global hw
hw:
    push rbp                ; 将栈重新对齐到16字节边界
    mov rbp, rsp
    sub rsp, 32+8+8         ; 为调用Windows函数分配栈空间
                            ; 本次调用需要1个栈参数(共8字节)
                            ; 预留32字节影子空间,额外减8字节维持16字节对齐
    mov rcx,-11
    call GetStdHandle
 
    mov rcx,rax             ; 无需使用R12,若使用则必须保存(非易失性寄存器需遵守调用约定)
    lea rdx,[rel msg]
    mov r8,16
    mov r9,rsp
    mov qword [rsp+32], 0   ; RSP+0到RSP+31为影子空间
                            ; 第一个栈参数(WriteFile的第5个参数)存放在RSP+32位置
    call WriteFile
 
    leave
    ret
 
section .data
msg: db "hello"
msglen: equ $-msg

问题原因

崩溃的核心是未遵守Win64调用约定:

  1. Win64要求调用函数前必须为被调用方分配32字节的影子空间,供被调用的Windows API使用;
  2. 调用任何函数前,栈指针必须保持16字节对齐,原代码中直接push 0破坏了栈对齐状态;
  3. 原代码使用了非易失性寄存器R12却未保存,违反了调用约定中对寄存器保护的要求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 22:02:10