19

编译器通常会提供一个开关来在代码不可访问时发出警告。我还看到了一些库的宏,它们为无法访问的代码提供断言

是否有提示,例如通过编译指示或我可以传递给 GCC(或任何其他编译器)的内置函数,如果确定实际上可以到达预期无法访问的行,则会在编译期间发出警告或错误?

这是一个例子:

    if (!conf->devpath) {
        conf->devpath = arg;
        return 0;
    } // pass other opts into fuse
    else {
        return 1;
    }
    UNREACHABLE_LINE();

其价值在于,在超出预期的不可达线的条件发生变化后,检测到该线实际上是可达的。

4

4 回答 4

21

gcc 4.5 支持__builtin_unreachable()编译器内联,结合它-Wunreachable-code可能会做你想做的事,但可能会导致虚假警告

于 2010-08-01T11:37:24.763 回答
6

__builtin_unreachable()据我在 GCC 7.3.0 上看到的,不会生成任何编译时警告

我在文档中也找不到任何暗示它会的东西。

例如,以下示例编译时没有任何警告:

#include <stdio.h>

int main(void) {
    __builtin_unreachable();
    puts("hello")
    return 0;
}

和:

gcc -ggdb3 -O0 -std=c99 -Wall -Wextra -Wunreachable-code main.c

我认为它做的唯一一件事就是允许编译器根据永远不会到达某行代码的事实进行某些优化,如果你犯了编程错误并且它曾经犯过,则会给出未定义的行为。

例如,执行上述示例似乎正常退出,但未按hello预期打印。然后,我们的装配分析表明,正常外观的出口只是 UB 巧合。

GCC的-fsanitize=unreachable标志将 转换为__builtin_unreachable();在运行时失败的断言:

<stdin>:1:17: runtime error: execution reached a __builtin_unreachable() call

但是,该标志在 Ubuntu 16.04 中已损坏:ld: unrecognized option '--push-state--no-as-needed'

对可执行文件有什么作用__builtin_unreachable()

如果我们使用和不__builtin_unreachable使用 with 来反汇编代码:

objdump -S a.out

我们看到没有它的那个调用puts

000000000000063a <main>:
#include <stdio.h>

int main(void) {
 63a:   55                      push   %rbp
 63b:   48 89 e5                mov    %rsp,%rbp
    puts("hello");
 63e:   48 8d 3d 9f 00 00 00    lea    0x9f(%rip),%rdi        # 6e4 <_IO_stdin_used+0x4>
 645:   e8 c6 fe ff ff          callq  510 <puts@plt>
    return 0;
 64a:   b8 00 00 00 00          mov    $0x0,%eax
}
 64f:   5d                      pop    %rbp
 650:   c3                      retq
 651:   66 2e 0f 1f 84 00 00    nopw   %cs:0x0(%rax,%rax,1)
 658:   00 00 00
 65b:   0f 1f 44 00 00          nopl   0x0(%rax,%rax,1)

而没有的只做:

int main(void) {
 5fa:   55                      push   %rbp
 5fb:   48 89 e5                mov    %rsp,%rbp
 5fe:   66 90                   xchg   %ax,%ax

甚至没有返回,所以我认为这只是一个未定义的行为巧合,它并没有爆炸。

为什么 GCC 不能确定某些代码是否无法访问?

我收集了以下答案:

  • 由于某种原因,自动确定无法访问的代码对于 GCC 来说太难了,这就是为什么多年来现在-Wunreachable-code什么都不做的原因:gcc 不会对无法访问的代码发出警告

  • 用户可能会使用内联汇编,这意味着不可达性,但 GCC 无法确定这一点。GCC 手册中提到了这一点:

    一种这样的情况是紧跟在一个永远不会终止的 asm 语句之后,或者是一个将控制权转移到其他地方并且永远不会返回的情况。在此示例中,如果没有 __builtin_unreachable,GCC 会发出警告,指出控制已到达非 void 函数的末尾。它还生成代码以在 asm 之后返回。

    int f (int c, int v)
    {
      if (c)
        {
          return v;
        }
      else
        {
          asm("jmp error_handler");
          __builtin_unreachable ();
        }
    }
    

在 GCC 7.3.0、Ubuntu 18.04 上测试。

于 2018-10-09T13:57:41.543 回答
6

使用 gcc 4.4.0 Windows 交叉编译器到使用 -O2 或 -O3 编译的 PowerPC,以下对我有用:

#define unreachable asm("unreachable\n")

如果编译器没有优化它,汇编器会因未知操作而失败,因为它已经断定它无法访问。

是的,它很可能是“在不同的优化选项下高度不可预测”,并且在我最终更新编译器时可能会中断,但目前它总比没有好。

于 2018-01-22T16:27:58.587 回答
2

如果您的编译器没有您需要的警告,则可以使用静态分析器对其进行补充。我正在谈论的那种分析器将有自己的注释语言和/或识别 C assert,并将这些用于提示在执行的特定点应该为真的属性。如果没有针对无法访问的语句的特定注释,您可以使用assert (false);.

我个人对它们并不熟悉,但 Klokwork 和 CodeSonar 是两个著名的分析器。戈安娜是第三个。

于 2010-08-01T10:13:10.327 回答