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,我想使用 MSVC(平台工具集 142)和 NASM(版本 2.14.02)在 x86 中编译下一个代码:
I am using Visual studio 2019 in Windows 10, and I want to compile in x86 using MSVC(platform toolset 142) and NASM(version 2.14.02) the next code:
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.
In x64 works well, in x86 the generated file main.obj adds a leading underscore to the function name foo, resulting in _foo. This does not happen in x64 but keeps the symbol as foo, not _foo.
那么,有没有适用于 x86 和 x64 平台的解决方案(最好不要修改源代码,也许是 MSVS 编译器的一些编译器/链接器标志)?
So, is there any solution that works for both x86 and x64 platforms (preferably without modify source code, maybe some compiler/linker flag for MSVS compiler)?
我非常感谢您的帮助.
推荐答案
_
前缀是名称修改的结果,这取决于目标平台 ABI(操作系统、位数、调用约定).
The _
prefix is a result of name mangling, which depends on the target platform ABI (OS, bitness, calling convention).
根据 Microsoft 的说法,_
前缀用于 32 位 Windows cdecl
调用约定,但不用于 64 位 (source):
According to Microsoft, the _
prefix is used in a 32-bit Windows cdecl
calling convention, but not in 64-bit (source):
C 修饰名的格式
C 函数的修饰形式取决于其声明中使用的调用约定,如下表所示.这也是声明 C++ 代码具有 extern "C" 时使用的装饰格式.连锁.默认调用约定是 __cdecl.请注意,在 64 位环境中,函数不会被修饰.
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.
调用约定 — 装饰__cdecl
前导下划线 (_
)__stdcall
前导下划线 (_
) 和尾随符号 (@
) 后跟以十进制表示的参数列表中的字节数__fastcall
前导和尾随符号 (@
) 后跟一个表示参数列表中字节数的十进制数__vectorcall
两个尾随符号 (@@
) 后跟参数列表中的十进制字节数
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 位模式下的函数参数以不同方式传递,并且在调用之间必须保留不同的寄存器.
The reason behind could be that 32/64-bit Windows calling conventions aren't really compatible. For example, function arguments in 64-bit mode are passed differently and different registers have to be preserved between calls.
无论如何,为了回答这个问题,我可以想到几个变通的解决方案:
Anyway, to answer the question, I can think of a couple of workaround solutions:
解决方案 1
生成 .asm
的代码应该只在 32 位模式下添加前导 _
(生成的程序集也可能在其他方面有所不同,尤其是如果涉及指针).
The code that generates the .asm
should add the leading _
only in 32-bit mode (the generated assembly will probably have to differ in other ways too, especially if pointers are involved).
解决方案 2
使用预处理器在 64 位模式下添加前导 _
:
Use a preprocessor to add the leading _
in 64-bit mode:
#ifdef _WIN64
# define foo _foo
#endif
extern "C" int foo(void);
int main()
{
int x = foo();
return 0;
}
这篇关于x86 中的下划线前缀问题:从 C++ 函数调用 NASM 函数在 x64 中有效但在 x86 中失败的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!