gcc的used属性有什么用途?

14
#include <stdio.h>

// xyz will be emitted with -flto (or if it is static) even when
// the function is unused

__attribute__((__used__))
void xyz() {
  printf("Hello World!\n");
}

int main() {
  return 0;
}

我需要这个的原因是什么?

除了直接调用函数,还有没有其他方法可以以某种方式到达xyz,就像一些dlsym() 魔术一样?


6
dlsym不是你要寻找的答案吗?在问题中提到答案会让人感到困惑,对吧。 - Potatoswatter
1
我猜你可能在内联汇编中调用了xyz,而编译器不知道这一点。 - melpomene
@Potatoswatter:不,它不是。dlsym()对于可执行文件无效,这就是为什么我写了“像某些”。 - Thomas
@Thomas 我非常确定一个可执行文件可以dlopen自己。 - melpomene
@melpomene:我试过了,但是找不到这个符号。 - Thomas
5个回答

23

used属性在需要强制编译器发出符号的情况下很有用,否则它可能会被忽略。正如GCC文档所述(重点是我的):

该属性应用于函数,意味着即使似乎未引用函数,也必须对函数发出代码。这很有用,例如当函数仅在内联汇编中引用时

例如,如果您有以下代码:

#include <iostream>

static int foo(int a, int b)
{
    return a + b;
}

int main()
{
   int result = 0;

   // some inline assembly that calls foo and updates result

   std::cout << result << std::endl;
}

您可能会注意到,在使用优化级别-O1时,没有符号foo存在于-O标志中:
g++ -O -pedantic -Wall check.cpp -c
check.cpp:3: warning: ‘int foo(int, int)’ defined but not used
nm check.o | c++filt | grep foo

因此,您无法在这个(想象中的)内联汇编中引用 foo

通过添加以下内容:

__attribute__((__used__))

它变成了:

g++ -O -pedantic -Wall check.cpp -c
nm check.o | c++filt | grep foo
00000000 t foo(int, int)

因此,现在可以在其中引用 foo

您可能已经注意到,gcc的警告已经消失了,因为您已经告诉编译器,您确定 foo 实际上是在“幕后”使用的。


7
在C++中,另一个使用案例是用于调试辅助工具。例如,您可能有一个“void dump()”方法,可以漂亮地打印内部对象。您不会在程序的任何地方调用它,但您希望能够在调试器中暂停时调用它。“attribute((used))”将确保它不被优化为死代码。 - Becca Royal-Gordon

3
一个特殊的用例是静态库中的中断服务例程。例如,定时器溢出中断:

void __attribute__((interrupt(TIMERA_VECTOR),used)) timera_isr(void)

这个timera_isr从未被用户代码中的任何函数调用,但它可能是库的一个重要部分。为确保它被链接并且没有指向空节的中断向量,关键字确保链接器不会将其优化掉。
最初的回答:
一个特殊的应用场景是在静态库中使用中断服务程序。例如,一个定时器溢出中断:
```c void __attribute__((interrupt(TIMERA_VECTOR), used)) timera_isr(void) ```
这个`timera_isr`从未被用户代码中的任何函数调用,但它可能构成库的一个重要部分。为了确保它被链接并且没有指向一个空节的中断向量,`used`关键字确保链接器不会将其优化掉。

2
我认为那不是真的。当您获取ISR函数地址并将其放入中断向量表中时,这些函数就被引用了。虽然可能会以间接的方式进行(例如使用编译器特定的语法,例如__attribute__((interrupt(X)))),但是不需要添加__attribute__((__used__)) - Dmitry Grigoryev
当静态库被汇编时,不要认为向量表已经填充,这是在将库链接到最终程序时发生的。由于程序未使用库中的函数,否则我认为它不会被添加到向量表中。 - EdL
当静态库被汇编时,它所导出的函数都没有被使用,这是在将库链接到最终程序时发生的。汇编器(或编译器)不会丢弃任何未使用的函数,这是链接器的工作。 - Dmitry Grigoryev

1
如果您声明了一个未使用的全局变量或函数,gcc会将其优化掉(带有警告),但是如果您使用'__attribute__((used))'声明了全局变量或函数,gcc会将其包含在目标文件中(以及链接的可执行文件中)。

https://gcc.gnu.org/legacy-ml/gcc-help/2013-09/msg00108.html


0
另一个用例是为头文件生成正确的覆盖率信息。在头文件中声明的函数通常在未被引用时会被编译器删除。因此,即使您忘记调用位于头文件中的某些函数,您的覆盖率报告也会显示100%的覆盖率。为了防止这种情况发生,您可以在覆盖率构建中使用__attribute__((used))标记您的函数。

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接