【问题标题】:How to tell gcc to keep my unused labels?如何告诉 gcc 保留我未使用的标签?
【发布时间】:2022-01-02 07:16:32
【问题描述】:

编辑:感谢@NateEldredge,我在How to 'tag' a location in a C source file for a later breakpoint definition?中更好地定义了我的问题


我使用这些标签在 gdb 中设置断点。所以无论我在标签之后/之前添加/删除代码行,断点仍然是正确的。

如果我在编译选项中添加-Wno-error=unused-label,编译器不会对我大喊大叫,但标签会从程序集中消失。

如果相反,我在代码中使用__attribute__((unused)),结果是一样的:没有抱怨,但标签不见了。

是否有正确的方法来完成这项工作(而不仅仅是 hack)?

这是我的玩具示例:

int main(void){
    int a = 15;
 label: __attribute__((unused))
    a = a + 23;
    return a;
}

编译后得到:

main:
        push    ebp
        mov     ebp, esp
        sub     esp, 16
        mov     DWORD PTR [ebp-4], 15
        add     DWORD PTR [ebp-4], 23
        mov     eax, DWORD PTR [ebp-4]
        leave
        ret

这里是同一示例的交互式版本:https://godbolt.org/z/zTqd9bM6q


$ gcc --version
gcc (GCC) 10.3.1 20210422 (Red Hat 10.3.1-1)
Copyright (C) 2020 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

【问题讨论】:

  • 你可以在标签前goto
  • __asm__("label:");怎么样
  • 这可能是个好问题。但是:您的示例代码应该在 here ,恕我直言。
  • 由于这仅用于调试,您可以将标签的使用放在有条件编译的语句中(在 #if DEBUG … #endif 之类的内部),例如 volatile int x; switch(x) { case 0: goto foo; case 1: goto bar; … }
  • 请注意__asm__("label:") 可能会失败。在优化过程中,编译器可能会决定重复代码,导致标签在汇编代码中出现两次,这通常会导致编译失败。或者,当编译器确定执行永远无法通过它时,它可能会被删除(尽管在那里放置断点无论如何都是无用的,因为它永远不会被命中)。

标签: c assembly gcc compiler-flags


【解决方案1】:

如果您希望标签不会被删除或重命名,请尝试以下操作:

    asm volatile("mylabel:");

请注意,使用此标签可能会影响 GCC 优化您的功能的方式。但是,volatile 关键字可能有助于防止它做任何会导致问题的事情。

另请注意,您可以使用__asm__ 代替asm。两者似乎都适用于 GCC。

【讨论】:

  • __asm__ 甚至可以与-std=c++11 一起使用。 asm 仅适用于默认的 -std=gnu11,即允许 GNU 扩展在全局命名空间中不保留用于实现定义的东西的部分。
  • 正如问题下的 cmets 中所讨论的,这在内联或循环展开时会出现问题,因为标签将位于一个 .s 文件中的多个位置(这是一个错误)。另外,不要使用 GNU C Basic Asm;最近的 GCC 版本将非空的基本 asm 模板字符串视为具有 "memory" clobber,因此会比必要的更损害优化。如果要强制编译器使内存(非转义的本地变量除外)与 C 抽象机同步,则显式使用 asm("mylabel:" ::: "memory");,否则使用 asm("mylabel:" :::);
  • 并且可以选择在这些扩展语句中使用"mylabel%=:" 让GCC 发明唯一编号作为标签名称的一部分;它可能仍然在 GDB 中完成制表符。 How Do I Use Labels In GCC Inline Assembly?
【解决方案2】:

您可以通过使用它来使未使用的标签警告静音:

int main(int argc, char** argv) {
    int a = 15;
    goto label; label:
    a = a + 23;
    return a;
}

这会将标签保留在程序集中(尽管使用内部名称):

main:
.LFB0:
        push    ebp
        mov     ebp, esp
        sub     esp, 16
        mov     DWORD PTR [ebp-4], 15
        nop
.L2:
        add     DWORD PTR [ebp-4], 23
        mov     eax, DWORD PTR [ebp-4]
        leave
        ret

我建议使用宏来减少工作量并更好地显示意图:

#define DEBUG_LABEL(x) goto x; x:

【讨论】:

  • nop 不是为了对齐;当 GCC 想要对齐时,它使用.p2align,因为它不跟踪指令大小。它把它留给汇编程序。即使没有 goto 恶作剧,GCC -O0 确实 有时也会在微小的函数中包含 nop,例如在 void bar(){} 中。 godbolt.org/z/9jGrGveYd。当然,这个技巧只适用于-O0,任何优化标签都会消失(godbolt.org/z/hfeeGzcd5),所以效率几乎无关紧要。
  • 这不会破坏我用例的标签的全部意义:在 gdb 中用于设置断点。如果名称不同,则无法在我的.gdbinit 文件中引用它。
  • @onlycparra:那么 C 标签一直对您的用例毫无用处,因为它们不会在汇编中转换为具有相同名称的标签。 (如果你想一想,那是行不通的:不同的函数可以包含同名的标签,更不用说内联了。)
  • @onlycparra:因此,我认为这是一个 XY 问题。真正的问题,我认为这是一个很好的问题,只是“我如何'标记'源文件中的位置以便能够在那里设置断点”。
  • @onlycparra:在这一点上,我可能会将其作为一个全新的问题发布,可能带有指向此问题的链接。
猜你喜欢
  • 2014-11-04
  • 1970-01-01
  • 2011-08-30
  • 2019-02-26
  • 2019-03-16
  • 1970-01-01
  • 2012-05-08
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多