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;
}