Win64汇编连续调用函数仅执行第一个的问题排查与解决
Windows 10 x64 NASM汇编:连续调用函数仅第一个执行的问题解决
在Windows 10 64位环境下编写NASM汇编代码,尝试连续调用fun1和fun2两个简单函数时,发现只有fun1被执行。疑问:是否需要在调用前后保存/加载寄存器?
原运行结果
hello 1 hello 2 test 1
原代码
segment .data hello1 db "hello 1", 0xd, 0xa, 0 hello2 db "hello 2", 0xd, 0xa, 0 test1 db "test 1", 0xd, 0xa, 0 test2 db "test 2", 0xd, 0xa, 0 segment .text global main extern ExitProcess extern printf main: push rbp mov rbp, rsp sub rsp, 32 ; Call printf to print the main message lea rcx, [hello1] call printf lea rcx, [hello2] call printf call fun1 call fun2 ; Exit the program xor rax, rax call ExitProcess ; Define additional functions section .text fun1: lea rcx, [test1] call printf ret fun2: lea rcx, [test2] call printf ret
编译链接命令
nasm -f win64 -g -o main.o main.s link /LARGEADDRESSAWARE:NO main.o kernel32.lib kernel32legacylib.lib legacy_stdio_definitions.lib legacy_stdio_wide_specifiers.lib ucrt.lib /subsystem:console /entry:main /out:main.exe /DEBUG
问题解决
通过在函数中分配栈空间(阴影空间)解决了问题,更新后代码如下:
更新后代码
segment .data hello1 db "hello 1", 0xd, 0xa, 0 hello2 db "hello 2", 0xd, 0xa, 0 test1 db "test 1", 0xd, 0xa, 0 test2 db "test 2", 0xd, 0xa, 0 segment .text global main extern ExitProcess extern printf main: push rbp mov rbp, rsp sub rsp, 32 ; Call printf to print the main message lea rcx, [hello1] call printf lea rcx, [hello2] call printf call fun1 call fun2 ; Exit the program xor rax, rax call ExitProcess ; Define additional functions section .text fun1: sub rsp, 32 ; 分配阴影空间 lea rcx, [test1] call printf add rsp, 32 ; 释放阴影空间 ret fun2: sub rsp, 32 ; 分配阴影空间 lea rcx, [test2] call printf add rsp, 32 ; 释放阴影空间 ret
更新后运行结果
hello 1 hello 2 test 1 test 2
问题原因说明
问题核心并非寄存器保存/加载,而是Windows x64调用约定(FastCall)的栈要求:
- 调用外部函数(如
printf)前,必须保证栈是16字节对齐的; - 调用者需要为被调用函数预留32字节的“阴影空间”(shadow space),用于被调用函数保存RCX/RDX/R8/R9这4个寄存器参数。
原代码中fun1和fun2调用printf前未分配阴影空间,导致栈对齐被破坏,printf执行完毕返回后,栈指针状态错误,使得fun1的ret指令跳转到了错误地址,fun2因此无法被执行。在函数中添加sub rsp,32和add rsp,32后,满足了调用约定的栈要求,函数调用流程恢复正常。
内容的提问来源于stack exchange,提问作者jinreal
相关产品推荐
相关产品推荐

