内联汇编没有用 clang 产生我想要的结果

Inline assembly is not producing my desired result with clang

我正在使用带 clang 的内联汇编 x86_64。我希望将 exact 程序集编译到我在源代码中指定的二进制文件中,但由于某种原因,clang 不断更改它。 这只发生在我使用 AT&T GCC 风格的程序集时,这不会发生在 MSVC __asm我想使用 AT&T 风格的程序集,因为我的代码的其余部分大量使用 AT&T 风格我想保持一致。我的汇编代码是这样声明的:

__attribute__((naked)) void XxInternalOperation()
{
    asm volatile("mov %%rcx, %%rax\n\t"
        "mov %0, %%r11\n\t"
        "jmpq *%%r11"
        :
        : "r" (jumpAddrAbsolute)
        : "r11");
}

我希望我的汇编代码代表这个英特尔代码:

mov rax, rcx
mov r11, jmpAddrAbsolute ; <-- this is an unsigned long long variable
jmp r11

但 IDA 中的快速反汇编表明了其他情况:

mov rax, cs:jumpAddrAbsolute
mov rax, rcx
mov r11, rax
jmp r11

有没有办法得到我想要的确切汇编代码?

Clang 不会“改变”您的程序集。它完全按照你的要求去做,那就是:它选择一个 register(这就是 r 约束的意思)并用 %0 代替它。请注意,此寄存器也可以是 rax,在这种情况下您的代码将不起作用。如果您希望 clang 选择内存操作数,请改用 m 约束。有关 gcc 样式内联汇编的详细信息,请参阅 the gcc manual。这是一个例子:

extern unsigned long long jumpAddrAbsolute;

__attribute__((naked)) void XxInternalOperation()
{
    asm volatile("mov %%rcx, %%rax\n\t"
        "mov %0, %%r11\n\t"
        "jmpq *%%r11"
        :
        : "m" (jumpAddrAbsolute)
        : "r11", "rax");
}

编译为:

movq    %rcx, %rax
movq    jumpAddrAbsolute(%rip), %r11
jmpq    *%r11

这似乎是你想要的。

请注意,我已将 rax 标记为已损坏,因为您在第一条指令中使用了它。请进一步注意,当执行此内联汇编语句时,无法保证 rcx 保留任何特定值。编译器可以随意将其设置为任何它喜欢的值。

另请注意,编译器可能决定内联 XxInternalOperation,在这种情况下,您的间接跳转将产生在调用者中执行尾调用的意外副作用。考虑将函数标记为 noinline 以避免这种情况。

但一般来说,在内联汇编中执行跳转或调用通常是错误方法的标志,通常会导致各种问题。如果您对决定使用此内联程序集解决的问题有一些详细信息,我也许可以向您建议更好的解决方案。