Windows x64环境下函数调用前后push/pop操作的正确实现方案
Windows x64 汇编调用printf异常问题解决方案
错误原因
你遇到的输出异常本质是两个问题共同导致的:
- Windows x64调用约定明确要求,调用任意系统函数(包括
printf)前,调用者必须预留32字节的影子空间,供被调用函数临时保存寄存器使用,这部分空间的内容在函数调用后会被污染,不能用来存储你需要持久保留的数据。 - 你的代码中
push的420刚好落在了printf会写入的影子空间范围内,第一次调用printf时该值被直接覆盖,所以第二次pop拿到的是垃圾值4199744。
是否需要每次push都预留32字节?
完全不需要,你的临时解决方案属于硬对齐的写法,不符合常规汇编编码规范,有更简洁优雅的实现方式。
推荐实现方式
方案1:直接使用预留栈空间存储数据(最常用)
你一开始已经通过subq $32, %rsp预留了32字节空间,这部分空间在你手动恢复rsp之前完全归你支配,直接把要打印的数值存在这里即可,完全不需要用push/pop,代码逻辑更清晰:
.data form: .asciz "%d\n" ; 用.asciz伪指令自动添加字符串结束符,不用手动写\0 .text .globl main main: pushq %rbp movq %rsp, %rbp subq $32, %rsp ; 预留32字节影子空间,此时栈满足16字节对齐要求 ; 直接将要打印的数值存入预留的栈空间 movq $420, 8(%rsp) movq $69, (%rsp) ; 第一次打印69 lea form(%rip), %rcx movq (%rsp), %rdx xor %eax, %eax call printf ; 第二次打印420 lea form(%rip), %rcx movq 8(%rsp), %rdx xor %eax, %eax call printf ; 恢复栈帧退出 mov %rbp, %rsp popq %rbp ret
方案2:保留push/pop逻辑的优化写法
如果你一定要用push/pop的实现逻辑,可以先把两个值弹出到寄存器暂存,再调整栈指针到符合调用约定的位置再调用函数,不需要反复加减32字节栈空间:
.data form: .asciz "%d\n" .text .globl main main: pushq %rbp movq %rsp, %rbp subq $32, %rsp pushq $420 pushq $69 ; 先把两个值弹出到寄存器暂存,避免被printf覆盖 popq %r10 popq %r11 ; 恢复栈到预留32字节后的对齐位置 addq $16, %rsp ; 第一次打印 lea form(%rip), %rcx movq %r10, %rdx xor %eax, %eax call printf ; 第二次打印 lea form(%rip), %rcx movq %r11, %rdx xor %eax, %eax call printf mov %rbp, %rsp popq %rbp ret
内容的提问来源于stack exchange,提问作者user2519656
相关产品推荐
相关产品推荐

