c - 将 __attribute__((used)) 设置为 C 变量/常量无效

标签 c gcc arm nxp-microcontroller

在 ARM GCC(纯 C 代码)上,当我声明一个常量时

__attribute__((used,section(".rodata.$AppID")))
const uint8_t   ApplicationID[16] = {
        0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00,
        0x12, 0x34, 0x00, 0x00
};

我在代码中没有提到它 ,是优化出来 , 并列在 map 文件的丢弃输入部分中。
只有当我引用它时,它才包含在二进制输出中,在源的其他地方。

难道只有“used”标签就够了吗?在 GCC 手册(6.32.1 Common Variable Attributes)中,我读到:

used

This attribute, attached to a variable with static storage, means that the variable must be emitted even if it appears that the variable is not referenced.



意思是把它放在一个固定的内存地址,在指定的section里,供单独的应用程序去检查

我正在运行 NXP MCUXpresso 11.1 提供的 ARM GCC,报告详细版本为
GNU C17 (GNU Tools for Arm Embedded Processors 8-2019-q3-update) version 8.3.1 20190703 (release) [gcc-8-branch revision 273027] (arm-none-eabi)
compiled by GNU C version 5.3.1 20160211, GMP version 6.1.0, MPFR version 3.1.4, MPC version 1.0.3, isl version isl-0.18-GMP

最佳答案

Shouldn't the "used" tag alone be enough?



这是不够的,也没有必要。它不相关。

根据您引用的 GCC 文档,属性 used
适用于静态变量的定义。作为现在的答案
作者指出删除,你的ApplicationID不是静态的,所以属性 used没有效果。

这里:
/* app_id_extern.c */

#include <stdint.h>

const uint8_t   ApplicationID[16] = {
        0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00,
        0x12, 0x34, 0x00, 0x00
};

我们有 ApplicationID默认情况下,定义为 extern多变的。默认存储类
文件范围变量,如 ApplicationID , 是 extern .编译器会
相应地生成一个目标文件,其中定义了ApplicationID为链接公开,我们可以看到:
$ gcc -c app_id_extern.c
$ readelf -s app_id_extern.o

Symbol table '.symtab' contains 10 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     ...
     9: 0000000000000000    16 OBJECT  GLOBAL DEFAULT    4 ApplicationID

在目标文件中,ApplicationID是一个 16 字节 GLOBAL第 4 部分中的符号
(在这种情况下恰好是 .rodata )。 GLOBAL绑定(bind)意味着静态链接器可以看到这个符号。

和这里:
/* app_id_static.c */

#include <stdint.h>

static const uint8_t   ApplicationID[16] = {
        0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00,
        0x12, 0x34, 0x00, 0x00
};

我们有 ApplicationID明确定义为 static多变的。编译器
将相应地生成一个目标文件,其中定义了 ApplicationID没有公开链接,我们也可以看到:
$ gcc -c app_id_static.c
$ readelf -s app_id_static.o

Symbol table '.symtab' contains 10 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     ...
     6: 0000000000000000    16 OBJECT  LOCAL  DEFAULT    4 ApplicationID
     ...

在这个目标文件中,ApplicationID是一个 16 字节 LOCAL .rodata 中的符号部分LOCAL绑定(bind)意味着静态链接器看不到这个符号。

编译器将始终在目标文件中发出 extern 的定义。多变的,
就像 ApplicationIDapp_id_extern.c ,即使该定义不是
在目标文件中引用,因为外部定义将可用于
链接器,因此可能在链接时从其他目标文件中引用,对于所有这些
编译器可能知道。

但是如果一个变量是 static ,那么编译器就知道它的定义不可用
用于联动。因此,如果它可以确定定义未在
目标文件本身,它可能会得出结论,该定义是多余的,而不是发出它
在目标文件中。像这样:
$ gcc -O1 -c app_id_static.c

这一次,我们要求编译器执行最少的优化。进而
$ readelf -s app_id_static.o

Symbol table '.symtab' contains 8 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND
     1: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS app_id_static.c
     2: 0000000000000000     0 SECTION LOCAL  DEFAULT    1
     3: 0000000000000000     0 SECTION LOCAL  DEFAULT    2
     4: 0000000000000000     0 SECTION LOCAL  DEFAULT    3
     5: 0000000000000000     0 SECTION LOCAL  DEFAULT    5
     6: 0000000000000000     0 SECTION LOCAL  DEFAULT    6
     7: 0000000000000000     0 SECTION LOCAL  DEFAULT    4
ApplicationID 的未引用定义不再存在于目标文件中
根本。它被优化了。

现在对于某些不寻常的应用程序,我们可能希望编译器发出定义
对象文件中不引用它的符号,并对静态链接器隐藏它。那是
其中属性 used发挥作用:
/* app_id_static_used .c */

#include <stdint.h>

static const uint8_t   ApplicationID[16] __attribute__((used,section(".rodata.$AppID"))) = {
        0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00,
        0x12, 0x34, 0x00, 0x00
};

我们再次使用 -O1 优化进行编译:
$ gcc -O1 -c app_id_static_used.c
$ readelf -s app_id_static_used.o

Symbol table '.symtab' contains 10 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     ...
     6: 0000000000000000    16 OBJECT  LOCAL  DEFAULT    4 ApplicationID
     ...

但这一次,感谢属性 used , LOCAL ApplicationID 的定义
再次出现在第 #4 节(在这个目标文件中是 .rodata.$AppID )

这就是属性 used作品。它影响编译器的行为:它没有
对链接器的影响。

我们还没有做任何关联。让我们现在做一些。
/* hello_world.c */

#include <stdio.h>

int main(void)
{
    puts("Hello world!")
    return 0;
}

该程序未引用 ApplicationID ,但我们将输入 app_id_static_used.o链接,无论:
$ gcc -O1 -c hello_world.c
$ gcc -o hello hello_world.o app_id_static_used.o -Wl,-gc-sections,-Map=mapfile.txt

在链接中,我要求删除未使用的输入部分,并要求一个 mapfile
要输出 ( -Wl,-gc-sections,-Map=mapfile.txt )

在 map 文件中,我们发现:

map 文件.txt
...
Discarded input sections
  ...
  .rodata.$AppID
                0x0000000000000000       0x10 app_id_static_used.o
  ...

链接器已丢弃部分 .rodata.$AppID来自 app_id_static_used.o 的输入
因为程序中没有引用该部分中定义的符号。和
属性 used ,我们强制编译器发出 static 的定义象征
app_id_static_used.o .这不会强制链接器需要它,或者保持
它在可执行文件中。

在我们从 app_id_static_used.c 切换到:
/* app_id_extern_used.c */

#include <stdint.h>

const uint8_t   ApplicationID[16] __attribute__((used,section(".rodata.$AppID"))) = {
        0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00,
        0x12, 0x34, 0x00, 0x00
};

那么我们正在做你所做的,应用属性 usedextern定义。属性used在这种情况下没有效果,因为编译器必然会发出 extern任何情况下的定义。链接器仍然会丢弃 .rodata.$AppID输入部分来自
如果程序不引用其中的任何内容,则可执行。

到目前为止,您的 app-id 源文件也可能是:
/* app_id_extern_section.c */

#include <stdint.h>

const uint8_t   ApplicationID[16] __attribute__((section(".rodata.$AppID"))) = {
        0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00,
        0x12, 0x34, 0x00, 0x00
};

然后你需要做的是通知链接器你想要的定义
符号ApplicationID保留,即使它没有被您的程序引用,并且
即使未使用的部分被丢弃。

为此,请使用链接器选项 --undefined=ApplicationID .这会
指示链接器从一开始就假定程序的链接
遇到了对 ApplicationID 的 undefined reference 并强制链接器
查找并链接其定义(如果有任何输入文件提供)。因此:
$ gcc -O1 -c app_id_extern_section.c
$ gcc -o hello hello_world.o app_id_extern_section.o -Wl,-gc-sections,--undefined=ApplicationID

现在程序包含了 ApplicationID 的定义,尽管没有提到它:
$ readelf -s hello | grep ApplicationID
    58: 0000000000002010    16 OBJECT  GLOBAL DEFAULT   18 ApplicationID

第 18 节是 .rodata程序部分:
$ readelf --sections hello | grep '.rodata'
  [18] .rodata           PROGBITS         0000000000002000  00002000

最后,请注意输入部分 .rodata.$AppID来自 app_id_extern_section.o已合并到输出部分 .rodata , 因为链接器的默认
链接描述文件指定:
.rodata         : { *(.rodata .rodata.* .gnu.linkonce.r.*) }

即所有匹配的输入部分 .rodata , .rodata.*.gnu.linkonce.r.*将输出到 .rodata .这意味着甚至:
__attribute__((section(".rodata.$AppID")))

是多余的。所以 app-id 源文件也可能只是一个
我开始于,app_id_extern.c ,以及链接选项 --undefined=ApplicationID这就是在程序中保留未引用符号所需的全部内容。除非
您的链接器在这方面有所不同,您会发现相同。

关于c - 将 __attribute__((used)) 设置为 C 变量/常量无效,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61255179/

相关文章:

assembly - 为什么 x86 和 ARM 之间的 GNU 语法不同?

c - 串行端口不发送 NULL 值

c - VS2005+WinSock : warning C4018: '<' : signed/unsigned mismatch

linux - 遗留的 gcc 编译器问题

c++ - C++ 中的纯/常量函数

arm - 如何确定处理器运行的字节序模式?

c - 模拟ARM代码

c - 如何从数字中提取某些位并将它们放入新变量中?

c - srand 函数返回相同的值

c++ - 共享内存中的队列正在运行