【问题标题】:C: Weird label behavior with -O3?C: -O3 的奇怪标签行为?
【发布时间】:2020-10-26 12:24:06
【问题描述】:

鉴于此代码(最小示例;其中没有更深层次的含义):

#include <stdio.h>

int main() {
    start:
    asm volatile(
        ".code64\n\t"
        "push %rax\n\t"
        "pop %rax"
    );
    end:
    printf("%ld\n", (const char *)&&end - (const char *)&&start);
}

我们通过执行gcc -O3 -S dummy.c -o -获得汇编代码。可以看到,包括以下几行:

        subq    $8, %rsp
        .cfi_def_cfa_offset 16
#APP
# 5 "dummy.c" 1
        .code64
        push %rax
        pop %rax
# 0 "" 2
#NO_APP
        leaq    .L2(%rip), %rax

这实际上意味着原始的汇编代码仍在二进制文件中(这是预期的)。

但是如果程序被执行,那么它输出0。这基本上意味着开始标签等于结束标签。另一方面,如果程序用-O0编译,它确实输出2。

为什么gcc -O3 dummy.c &amp;&amp; ./a.out即使包含汇编代码也输出0?

谢谢

【问题讨论】:

  • 谢谢:)!这似乎是正确的答案!
  • 这真的可以编译吗?使用&amp;&amp;(双元地址运算符)合法吗?获取运算符结果的地址是什么意思?没有对象可以取地址?!?
  • @Ruslan 请考虑将此作为答案发布。
  • 为什么是疯狂的 asm 模板字符串?如果还没有,你切换到 64 位代码生成,破坏 32 位构建。然后你通过使用堆栈指令来保存/恢复 RAX 来破坏 RSP 下面的红色区域。 (Using base pointer register in C++ inline asm)。如果您只想要虚拟填充物,nop 将是显而易见的选择。或者lock addb $0, (%rsp) 是其他对架构状态没有影响的东西,您可以在没有任何clobber 声明的情况下使用它。

标签: c gcc optimization inline-assembly goto


【解决方案1】:

请参阅GCC bug 40078,尤其是其中的comment 1

<...> 标签如果在程序中没有正常使用,可以移动。 <...> 标签地址仅用于计算 goto,任何其他用途都会导致其放置的未定义行为。

进一步的评论表明,GCC 的asm goto 功能(从 4.5 版开始)可能会对记者有所帮助。

确实,asm goto 可以帮助您修复标签的位置。我猜asm goto 是标签的“正常用途”之一。请参阅以下示例。请注意我如何将asm goto 用作单独的语句,因为它后面的“有用”语句有一个输出,而asm goto 语句没有。

#include <stdio.h>

int main()
{
start:
    asm goto(""::::start,end);

    unsigned random=0;
    asm volatile("rdrand %0\n" : "=r"(random));
end:
    printf("Random number: %u; label difference: %ld\n",
           random, (long)(&&end - &&start));
}

Play with it online!

【讨论】:

  • rdrand 应该始终使用asm volatile 只是一般原则,以防它被复制/粘贴到循环或其他东西中。您不希望编译器在 asm 语句的多次调用之间进行 CSE。
猜你喜欢
  • 1970-01-01
  • 2012-10-30
  • 1970-01-01
  • 1970-01-01
  • 2020-04-16
  • 2017-11-22
  • 2013-05-08
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多