【问题标题】:How to tell compiler to not generate "retq" after inline asm in a _Noreturn function?如何告诉编译器在 _Noreturn 函数中的内联 asm 之后不生成“retq”?
【发布时间】:2018-12-29 04:09:48
【问题描述】:

我编写了以下代码来调用 syscall exit 而不与 glibc 链接:

// a.c
#include <stdlib.h>
#include <sys/syscall.h>
#include <unistd.h>

_Noreturn void _start()
{
    register int syscall_num asm ("rax") = __NR_exit;
    register int exit_code   asm ("rdi") = 0;

    // The actual syscall to exit
    asm volatile ("syscall"
        : /* no output Operands */
        : "r" (syscall_num), "r" (exit_code));
}

Makefile:

.PHONY: clean

a.out: a.o
    $(CC) -nostartfiles -nostdlib -Wl,--strip-all a.o

a.o: a.c
    $(CC) -Oz -c a.c

clean:
    rm a.o a.out

我将makeCC=clang-7 一起使用,它工作得非常好,除了当我检查objdump -d a.out 生成的程序集时:

a.out:     file format elf64-x86-64


Disassembly of section .text:

0000000000201000 <.text>:
  201000:   6a 3c                   pushq  $0x3c
  201002:   58                      pop    %rax
  201003:   31 ff                   xor    %edi,%edi
  201005:   0f 05                   syscall 
  201007:   c3                      retq   

syscall 之后有一个无用的retq。我想知道,有没有办法在不诉诸于在汇编中编写整个函数的情况下删除它?

【问题讨论】:

  • 有趣的事实:gcc 已经忽略了 ret 只是因为 _Noreturn 声明。 godbolt.org/z/OLuoUO。但它仍然警告_Noreturn 函数返回,即使它忽略了ret。但是,clang 和 ICC 需要__builtin_unreachable,并且还需要警告。顺便说一句,我简化了 asm 语句以使用 "a""D" 约束而不是 register-asm 本地变量。一个"memory" clobber 可以确保在 asm 语句之后没有任何东西可以重新排序,即使这可能已经不可能了。
  • @PeterCordes 我认为值得注意的是,使用 _Noreturn 时,ret 只有在使用 GCC 8.x+ 时才会消失,但在这些版本之前它不会消失。

标签: c assembly inline-assembly noreturn


【解决方案1】:

在不返回的系统调用后添加:

    __builtin_unreachable();

【讨论】:

  • 仅供参考,您可以直接设置寄存器:asm( "syscall" :: "a" (SYS_exit), "D" (0) );
  • 有趣的事实:在 ICC 上,if(x) __builtin_unreachable() 实际上编译成 asm,它会进行有条件的跳转到函数末尾。但是在这个函数的末尾添加它确实避免了ret,所以英特尔编译器开发人员可能忽略了分支内部的情况,因为它与帮助优化器相反。
猜你喜欢
  • 2010-12-01
  • 1970-01-01
  • 2011-11-03
  • 1970-01-01
  • 2015-08-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-02-13
相关资源
最近更新 更多