GCC Inline assembly error: invalid instruction suffix for `lidt'

GCC Inline assembly error: invalid instruction suffix for `lidt'

我正在尝试从 C 调用一些汇编代码。我最近将程序从 x86 切换到 x86-64,然后才开始工作。我有这个代码:

__asm__ __volatile__("lidtl (%0)" : : "r" (&idt_reg));

其中 &idtreg 是对结构的引用。用 GCC 编译它给我这个错误:

invalid instruction suffix for `lidt'

当我添加 $ 令牌时:

__asm__ __volatile__("lidtl $(%0)" : : "r" (&idt_reg));

我收到这个错误:

illegal immediate register operand (%rax)

为什么会出现这个问题,我该如何解决?

在 32 位代码中,LIDT 的内存操作数是 32 位。在 AT&T 语法中,L 指令后缀强制汇编程序始终采用 long(32 位)。在 64 位代码中,内存操作数是 64 位的。如果您使用指令后缀,则它必须是 Q(四字)。一个四字是 64 位。

汇编器足够聪明,可以根据它生成的是 32 位代码还是 64 位代码来了解 LIDT 的大小。更好的选择是让汇编程序通过在指令后缀中省略大小来推断大小。只需使用 LIDT 即可。代码可能如下所示:

__asm__ ("lidt %0" : : "m" (idt_reg));

我删除了 volatile,因为它在没有输出操作数时是隐式的。我使用 m(内存操作数)作为约束,以避免通过寄存器传递内存地址时出现问题。通过寄存器传递地址需要 memory 破坏器或类似机制,以确保在发出内联汇编之前该地址处的数据在内存中可用。来自 GCC documentation:

The "memory" clobber tells the compiler that the assembly code performs memory reads or writes to items other than those listed in the input and output operands (for example, accessing the memory pointed to by one of the input parameters). To ensure memory contains correct values, GCC may need to flush specific register values to memory before executing the asm.

如果您确实使用了 r 约束(没有必要),那么正确的代码应该是:

__asm__ ("lidt (%0)" : : "r" (&idt_reg) : "memory");

脚注

  • 通过删除 LQ 指令后缀,无论生成 32 位还是 64 位程序,此版本都应该编译。
  • GCC 的内联汇编是 tricky to get right,如果你弄错了,生成的代码可能并不总是你所期望的。