gpt4 book ai didi

c - "unsupported for mov"GCC 内联汇编器

转载 作者:太空狗 更新时间:2023-10-29 15:52:34 25 4
gpt4 key购买 nike

在使用 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 进行汇编输出?

最佳答案

总的来说,使用约束将值放入正确的寄存器比显式移动要好得多:

#include <asm/unistd.h>

asm volatile("syscall"
: // no outputs. Other syscalls need an "=a"(retval) to tell the compiler RAX is modified, whether you actually use the retval or not.
: "D" ((uint64_t)code), "a" ((uint64_t)__NR_exit_group) // 231
: "rcx", "r11" // syscall itself clobbers these. exit can't fail and return; mostly here as an example for other syscalls
, "memory" // make sure any stores, e.g. to mmapped files, are done before this
);
__builtin_unreachable(); // tell the compiler execution doesn't come out the bottom of the asm statement. Maybe have the same effect as a "memory" clobber of making sure not to delay stores which could potentially be to mmapped files or shared memory.

如果有用的话,这可以让编译器提升代码中较早的移动,或者如果可以将值安排为已经在正确的寄存器中,甚至可以完全避免移动...

例如 code 如果此函数不内联,则将在 EDI 中; Linux 系统调用约定被选择为尽可能接近 x86-64 System V 函数调用约定,除了使用 R10 而不是 RCX 因为 syscall 指令本身用保存的覆盖它 - RIP 和带有保存的 RFLAGS 的 R11。

(不过,在这种情况下,不必要地转换 (uint64_t)code 会强制编译器使用 mov %edi, %edi 重做零扩展。电话号码确实需要零扩展到 64 位,这几乎肯定会免费发生,即使您没有手动转换它(因为编译器将使用 mov $231, %eax ), 但明确说明所需的内容并没有坏处。exit_group 系统调用采用 32 位 int arg,因此内核保证忽略 RDI 中的高垃圾。)

关于c - "unsupported for mov"GCC 内联汇编器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18721713/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com