【问题标题】:"unsupported for mov" GCC inline assembler“mov 不支持”GCC 内联汇编程序
【发布时间】:2013-09-14 07:52:24
【问题描述】:

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

【问题讨论】:

    标签: c optimization gcc inline-assembly


    【解决方案1】:

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

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

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

    【讨论】:

    • 这很酷,谢谢你的提示;我没有意识到 GCC 会自动为我提供mov 的东西。适合我不阅读文档...
    • 这是迄今为止最好的方法(在使用 gcc 的内联 asm 时将特定值放入特定寄存器)。
    【解决方案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) { ...
    

    【讨论】:

    • 嗨。不敢相信我没想到。干杯!然而,paste.ubuntu.com/6088323 给了我error: invalid 'asm': invalid expression as operand 优化构建。有什么想法吗?
    • 有趣,icc sais “不能有空的clobber”。 gcc 对此很好,但不喜欢空约束字符串。 :: "g" (_code) 两者都适用。
    • 啊,谢谢。我必须设法用 clang 构建这个东西以获得“第二意见”。
    • 另一件在技术上可行的事情是说mov %k0, %%rdi 明确告诉GCC 使用“32 位寄存器别名”('SImode' 名称),请参阅stackoverflow.com/questions/118730/… 以获取示例/指针。不过,Chris Dodd 在下面的回答提供了一种更好的方式来表达这个特定的程序集。
    猜你喜欢
    • 1970-01-01
    • 2012-10-20
    • 2014-05-21
    • 1970-01-01
    • 1970-01-01
    • 2011-03-08
    • 1970-01-01
    • 2010-12-05
    • 2011-04-23
    相关资源
    最近更新 更多