Linux与Windows x86汇编调用约定及printf符号问题咨询
x86汇编调用约定与函数名修饰的常见问题解答
背景与代码示例
用户编写的Windows汇编代码如下:
section .data msg: db "my message", 0ah, 0 section .text _main: push msg call _printf add esp, 4
核心问题解答
1. Windows是否支持两种调用约定?
是的,Windows同时支持栈传参和寄存器传参的调用约定。32位平台主流使用栈传参的cdecl/stdcall约定,也存在fastcall这类允许部分参数通过寄存器传递的约定;64位平台则统一采用基于寄存器的Microsoft x64调用约定。
2. 如何确定应使用哪种调用约定?
- 先看目标平台位数:64位Windows程序必须遵循Microsoft x64寄存器传参约定;32位平台可选择,但必须与调用的函数约定匹配。
- 匹配目标函数的规范:比如标准C库的
printf,32位Windows下遵循cdecl(栈传参,调用者清理栈),64位下遵循Microsoft x64约定(寄存器传参)。 - 课堂场景优先遵循课程要求;自定义函数时,需明确约定并保证调用方与被调用方规则一致。
3. 32位与64位x86程序的调用约定是否存在差异?
差异非常显著:
- 32位x86:以栈传参为主,主流约定是
cdecl(调用者清栈)和stdcall(被调用者清栈);fastcall作为补充,用ECX/EDX传递前两个参数,剩余参数入栈。 - 64位x86(AMD64):Windows和Linux均采用寄存器传参,但规则不同:
- Windows:前4个整数/指针参数用RCX、RDX、R8、R9传递,浮点参数用XMM0-XMM3,剩余参数入栈,由调用者负责清理栈空间。
- Linux:前6个整数/指针参数用RDI、RSI、RDX、RCX、R8、R9传递,浮点参数用XMM0-XMM7,由被调用者清理栈。
关于printf函数名下划线的问题
这种差异是函数名修饰规则导致的:
- 32位Windows的C编译环境中,
cdecl约定的函数会被自动添加前缀下划线,比如printf会被修饰为_printf,因此汇编代码中必须调用_printf才能匹配标准库的符号。 - 无需加下划线的场景,大多是因为使用了64位环境:64位Windows的函数名修饰规则去掉了前缀下划线,直接使用
printf即可;少数情况是工具链默认配置不同,但你提到未使用NASM选项,基本可以确定是64位环境的原因。
内容的提问来源于stack exchange,提问作者Tim
相关产品推荐
相关产品推荐

