我正在尝试从C调用一些汇编代码。最近,我在将程序从x86切换到x86-64之前开始工作。我有以下代码:
__asm__ __volatile__("lidtl (%0)" : : "r" (&idt_reg));
&idtreg
是对结构的引用。用GCC编译会给我这个错误:
'lidt'的无效指令后缀
当我添加$
令牌时:
__asm__ __volatile__("lidtl $(%0)" : : "r" (&idt_reg));
我收到此错误:
非法立即寄存器操作数(%rax)
为什么会出现此问题,我该如何解决?
答案 0 :(得分:4)
在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:
“内存”容器告诉编译器汇编代码 执行内存读取或写入中所列项目以外的项目 输入和输出操作数(例如,,访问内存 指向其中一个输入参数)。确保内存包含 正确的值,GCC可能需要刷新特定的寄存器值以 执行asm之前先存储内存。
如果您确实使用了r
约束(没有必要),那么正确的代码应该是:
__asm__ ("lidt (%0)" : : "r" (&idt_reg) : "memory");
L
或Q
指令后缀,此版本可以编译生成32位还是64位程序。