x86 中的下划线前缀问题:从 C++ 函数调用 NASM 函数在 x64 中有效但在 x86 中失败 [英] Underscore prefix problem in x86: Calling NASM function from C++ function works in x64 but fails in x86

查看:37
本文介绍了x86 中的下划线前缀问题:从 C++ 函数调用 NASM 函数在 x64 中有效但在 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

ma​​in.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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆