【问题标题】:Making assembly function inline in x64 Visual Studio在 x64 Visual Studio 中内联汇编函数
【发布时间】:2017-05-03 15:35:43
【问题描述】:

我知道 x64 模式下的 MSVC 编译器不支持代码的内联汇编 sn-ps,为了使用汇编代码,您必须在一些外部 my_asm_funcs.asm 文件中定义函数:

my_asm_func PROC
    mov rax, rcx
    ret
my_asm_func ENDP

然后在您的 .c 或 .h 文件中为函数定义一个头文件,如下所示:

int my_asm_func(int x);

虽然该解决方案解决了许多问题,但我仍然有兴趣使该汇编代码函数内联,换句话说 - 编译后我不希望对 my_asm_func 有任何“调用”,我只想将这段程序集粘合到我最终编译的代码中。我尝试使用 inline__forceinline 关键字声明该函数,但似乎没有任何帮助。还有什么方法可以做我想做的事吗?

【问题讨论】:

  • 你搜索过编译器内部函数吗?这可能是机会,有一些东西,涵盖了my_asm_func 的功能。通常,将您自己的程序集内联与进行大量优化(取决于优化级别)的编译器混合起来可能很难工作,更不用说它的维护了。
  • 不,不可能。出于某些原因,请参阅此answer to a similar question

标签: c visual-studio x86-64 masm


【解决方案1】:

不,没有办法做你想做的事。

正如您所说,Microsoft 的编译器不支持 x86-64 目标的内联汇编。这迫使您在外部代码模块 (*.asm) 中定义汇编函数,使用 MASM 汇编它们,并将结果与​​您单独编译的 C/C++ 代码链接在一起。

所需的步骤分离意味着 C/C++ 编译器无法内联您的汇编函数,因为它们在编译时对其不可见。

即使启用了链接时代码生成 (LTCG),您的汇编模块也不会内联,因为链接器根本不支持这一点。

绝对没有办法将编写在单独模块中的汇编函数直接内联到 C 或 C++ 代码中。

inline__forceinline 关键字不可能做任何事情。事实上,如果没有编译器错误(或至少是警告),您就无法使用它们。这些注释必须放在函数的 definition 上(对于内联函数,它与它的声明相同),但你不能把它放在函数的定义上,因为它是在单独的*.asm 文件。这些不是 MASM 关键字,因此尝试将它们添加到定义中必然会导致错误。并且将它们放在 C 标头中汇编函数的前向声明中同样会失败,因为那里没有可内联的代码——只是一个原型。

这就是微软推荐使用intrinsics的原因。你可以直接在你的C或C++代码中使用这些,编译器会自动生成相应的汇编代码。这不仅实现了所需的内联,而且内在函数甚至允许优化器发挥作用,进一步改善结果。不,内在函数不会导致完美的代码,也没有适用于所有事物的内在函数,但这是您可以使用 Microsoft 编译器做的最好的事情。

您唯一的其他选择是坐下来玩各种 C/C++ 代码排列,直到您让编译器生成所需的目标代码。如果内在函数不适用于您希望生成的指令,这可能非常强大,但是确实需要花费大量时间来坐立不安,并且您必须重新访问它以确保它继续执行您的操作升级编译器版本时需要。

【讨论】:

  • 您可以标记函数naked。这可能会回避函数调用并简单地执行跳转到独立的 asm 代码。另请参阅How to do a naked function and inline assembler in x64 Visual C++
  • @jww __declspec(naked) 也不会导致函数被内联。事实上,如果我没记错我的测试,naked 注释实际上会让优化器更加悲观。另外...这个问题是关于 x86-64 目标的,__declspec(naked) 甚至不存在 x86-64 目标。
【解决方案2】:

由于标题提到了 Visual Studio 而不是 MSVC,我建议通过 Visual Studio 安装程序安装 Clang。它可以像 MSVC 一样使用,无需配置自定义构建任务或任何东西,它支持使用 Intel 语法和变量作为操作数的内联汇编。

只需从项目属性页的General 部分的Platform Toolset 中选择“LLVM (clang-cl)”即可。

【讨论】:

    【解决方案3】:

    是的,你可以。将您的过程组装为 shellcode 并提取字节,然后将其包含在代码中具有 RWX 内存保护的缓冲区中。调用代码。

    【讨论】:

    • 调用站点仍将使用call 指令。这避免了一个单独的 .asm 文件,但它对性能没有好处——它不像uint64_t foo(uint64_t x) { return x;} 那样内联。 (实际上会降低性能,因为您需要通过函数指针而不是普通的call rel32 调用,以及分配缓冲区,这与单独的.asm 文件不同,它只是被组装并正常链接到您的可执行文件中,旁边编译器生成的代码)。
    • 可维护性也更差;编辑 asm 需要重新生成 static char machine_code[] = { ... }; 数组或您使用的任何内容,而不仅仅是在单独的文件中编辑 asm 源。尽管如此,值得一提的是作为将 asm 放入单个文件的技巧,例如对于在线 IDE 或其他有限的情况,但前提是您的答案清楚地表明它做什么/不做什么。 (即回答与所提出的问题略有不同的问题。)
    猜你喜欢
    • 2014-12-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-19
    • 2011-07-29
    • 2010-12-05
    相关资源
    最近更新 更多