x86 中的下划线前缀问题:从 C++ 函数调用 NASM 函数在 x64 中有效但在 x86 中失败
Underscore prefix problem in x86: Calling NASM function from C++ function works in x64 but fails in x86
我在 Windows 10 中使用 Visual studio 2019,我想在 x86 中使用 MSVC(平台工具集 142)和下一个 NASM(版本 2.14.02)进行编译代码:
foo.asm
section .text
global foo
foo:
mov eax, 123
ret
main.cpp
extern "C" int foo(void);
int main()
{
int x = foo();
return 0;
}
但是我得到了错误:
在 x64 中运行良好,在 x86 中,生成的文件 main.obj 在函数名 foo 前添加了一个前导下划线,结果为 _foo。这在 x64 中不会发生,但将符号保留为 foo,而不是 _foo。
那么,有没有同时适用于 x86 和 x64 平台的解决方案(最好不修改源代码,可能有一些 compiler/linker MSVS 编译器标志)?
非常感谢任何帮助。
正确的解决方案是为每个体系结构使用不同的 asm 文件:x86、amd64 等,因为您将完全使用不同的汇编程序指令集。 select make 文件或环境配置将编译哪个文件取决于构建架构。
因此,您可以对 x86 使用 '_foo' 函数名称,对 x86_64
使用 'foo'
_
前缀是名称修改的结果,这取决于目标平台 ABI(OS、位数、调用约定)。
根据 Microsoft 的说法,_
前缀用于 32 位 Windows cdecl
调用约定,但不用于 64 位 (source):
Format of a C decorated name
The form of decoration for a C function depends on the calling convention used in its declaration, as shown in the following table. This is also the decoration format that is used when C++ code is declared to have extern "C" linkage. The default calling convention is __cdecl. Note that in a 64-bit environment, functions are not decorated.
Calling convention — Decoration
__cdecl
Leading underscore (_
)
__stdcall
Leading underscore (_
) and a trailing at sign (@
) followed by the number of bytes in the parameter list in decimal
__fastcall
Leading and trailing at signs (@
) followed by a decimal number representing the number of bytes in the parameter list
__vectorcall
Two trailing at signs (@@
) followed by a decimal number of bytes in the parameter list
背后的原因可能是 32/64 位 Windows 调用约定并不真正兼容。例如,64 位模式下的函数参数传递方式不同,调用之间必须保留不同的寄存器。
无论如何,为了回答这个问题,我可以想到几个解决方法:
解决方案 1
生成 .asm
的代码应该仅在 32 位模式下添加前导 _
(生成的程序集可能也必须在其他方面有所不同,特别是如果涉及指针) .
解决方案 2
使用预处理器在 64 位模式下添加前导 _
:
#ifdef _WIN64
# define foo _foo
#endif
extern "C" int foo(void);
int main()
{
int x = foo();
return 0;
}
我在 Windows 10 中使用 Visual studio 2019,我想在 x86 中使用 MSVC(平台工具集 142)和下一个 NASM(版本 2.14.02)进行编译代码:
foo.asm
section .text
global foo
foo:
mov eax, 123
ret
main.cpp
extern "C" int foo(void);
int main()
{
int x = foo();
return 0;
}
但是我得到了错误:
在 x64 中运行良好,在 x86 中,生成的文件 main.obj 在函数名 foo 前添加了一个前导下划线,结果为 _foo。这在 x64 中不会发生,但将符号保留为 foo,而不是 _foo。
那么,有没有同时适用于 x86 和 x64 平台的解决方案(最好不修改源代码,可能有一些 compiler/linker MSVS 编译器标志)?
非常感谢任何帮助。
正确的解决方案是为每个体系结构使用不同的 asm 文件:x86、amd64 等,因为您将完全使用不同的汇编程序指令集。 select make 文件或环境配置将编译哪个文件取决于构建架构。 因此,您可以对 x86 使用 '_foo' 函数名称,对 x86_64
使用 'foo'_
前缀是名称修改的结果,这取决于目标平台 ABI(OS、位数、调用约定)。
根据 Microsoft 的说法,_
前缀用于 32 位 Windows cdecl
调用约定,但不用于 64 位 (source):
Format of a C decorated name
The form of decoration for a C function depends on the calling convention used in its declaration, as shown in the following table. This is also the decoration format that is used when C++ code is declared to have extern "C" linkage. The default calling convention is __cdecl. Note that in a 64-bit environment, functions are not decorated.
Calling convention — Decoration
__cdecl
Leading underscore (_
)
__stdcall
Leading underscore (_
) and a trailing at sign (@
) followed by the number of bytes in the parameter list in decimal
__fastcall
Leading and trailing at signs (@
) followed by a decimal number representing the number of bytes in the parameter list
__vectorcall
Two trailing at signs (@@
) followed by a decimal number of bytes in the parameter list
背后的原因可能是 32/64 位 Windows 调用约定并不真正兼容。例如,64 位模式下的函数参数传递方式不同,调用之间必须保留不同的寄存器。
无论如何,为了回答这个问题,我可以想到几个解决方法:
解决方案 1
生成 .asm
的代码应该仅在 32 位模式下添加前导 _
(生成的程序集可能也必须在其他方面有所不同,特别是如果涉及指针) .
解决方案 2
使用预处理器在 64 位模式下添加前导 _
:
#ifdef _WIN64
# define foo _foo
#endif
extern "C" int foo(void);
int main()
{
int x = foo();
return 0;
}