4

在使用 GCC 的内联汇编器功能时,我尝试制作一个立即退出进程的函数,类似于_ExitC 标准库中的函数。

这是相关的源代码:

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汇编输出?

4

2 回答 2

9

总的来说,最好使用约束将值放入正确的寄存器而不是显式移动:

asm("syscall" :: "D" ((uint64_t)code), "a" ((uint64_t)231));

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

于 2013-09-10T17:26:18.823 回答
3

将您的变量转换为适当的长度类型。

#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) { ...
于 2013-09-10T14:42:42.153 回答