在玩GCC的内联汇编程序功能时,我尝试创建一个立即退出流程的函数,类似于C标准库中的_Exit
。
以下是相关的源代码:
void immediate_exit(int code)
{
#if defined(__x86_64__)
asm (
//Load exit code into %rdi
"mov %0, %%rdi\n\t"
//Load system call number (group_exit)
"mov $231, %%rax\n\t"
//Linux syscall, 64-bit version.
"syscall\n\t"
//No output operands, single unrestricted input register, no clobbered registers because we're about to exit.
:: "" (code) :
);
//Skip other architectures here, I'll fix these later.
#else
# error "Architecture not supported."
#endif
}
这适用于调试版本(使用-O0
),但只要我在任何级别启用优化,我都会收到以下错误:
immediate_exit.c: Assembler messages:
immediate_exit.c:4: Error: unsupported for `mov'
所以我查看了两个版本的汇编程序输出(为了清楚起见,我删除了.cfi*
指令和其他内容,如果这是一个问题,我可以再次添加它)。调试版本:
immediate_exit:
.LFB0:
pushq %rbp
movq %rsp, %rbp
movl %edi, -4(%rbp)
mov -4(%rbp), %rdi
mov $231, %rax
syscall
popq %rbp
ret
优化版本:
immediate_exit:
.LFB0:
mov %edi, %rdi
mov $231, %rax
syscall
ret
因此,优化版本试图将32位寄存器edi
放入64位寄存器rdi
,而不是从rbp
加载,我认为这是什么导致错误。
现在,我可以通过将“m”指定为code
的寄存器约束来解决此问题,这会导致GCC从rbp
加载,而不管优化级别如何。但是,我宁愿不这样做,因为我认为编译器及其作者对于放置东西比我更好的想法。
所以(最后!)我的问题是:我如何说服GCC使用rdi
而不是edi
来组装输出?
答案 0 :(得分:7)
总的来说,使用约束将值放入正确的寄存器而不是显式移动会更好:
asm("syscall" :: "D" ((uint64_t)code), "a" ((uint64_t)231));
如果有用的话,编译器可以在代码中提前提升移动,如果值可以安排在正确的寄存器中,甚至可以完全避免移动...
答案 1 :(得分:2)
将变量转换为适当的长度类型。
#include <stdint.h>
asm (
//Load exit code into %rdi
"mov %0, %%rdi\n\t"
//Load system call number (group_exit)
"mov $231, %%rax\n\t"
//Linux syscall, 64-bit version.
"syscall\n\t"
//No output operands, single unrestricted input register, no clobbered registers because we're about to exit.
:: "g" ((uint64_t)code)
);
或者更好地将您的操作数类型直接放在正确的大小上:
void immediate_exit(uint64_t code) { ...