为何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调用约定:
- Win64要求调用函数前必须为被调用方分配32字节的影子空间,供被调用的Windows API使用;
- 调用任何函数前,栈指针必须保持16字节对齐,原代码中直接
push 0破坏了栈对齐状态; - 原代码使用了非易失性寄存器R12却未保存,违反了调用约定中对寄存器保护的要求。
内容的提问来源于stack exchange,提问作者Catharsis
相关产品推荐
相关产品推荐

