将_属性_((使用))设置为C变量/常量无效

将_属性_((使用))设置为C变量/常量无效,c,gcc,arm,nxp-microcontroller,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 }; 而我在代码中

在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
};
我在代码中没有提到它,它被优化了,并列在地图文件中丢弃的输入部分。 只有当我在源代码的其他地方引用它时,它才会包含在二进制输出中

单凭“
used
”标签就足够了吗?在GCC手册(6.32.1通用变量属性)中,我读到:

使用

此属性附加到具有静态存储的变量,意味着即使该变量似乎未被引用,也必须发出该变量

其含义是将其放在指定部分的固定内存地址,以便单独的应用程序检查它

我正在运行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
单凭“已使用”标签就够了吗

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

根据您引用的GCC文档,使用了属性
适用于静态变量的定义。作为现在的答案 作者指出,您的
应用程序ID
不是静态的,因此使用了属性
没有效果

在这里:

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

在这里:

/* 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
.rodata
部分中的一个16字节的
LOCAL
符号
LOCAL
绑定意味着静态链接器无法看到此符号

编译器将始终在对象文件中发出
extern
变量的定义, 与
app\u id\u extern.c
中的
ApplicationID
类似,即使该定义不正确 在对象文件中引用,因为外部定义将可用于 链接器,因此可能在linktime从其他对象文件中引用 编译器可能知道

但是如果变量是静态的,那么编译器知道它的定义不可用 用于链接。因此,如果它可以确定该定义未在 对象文件本身,它可能会得出结论,该定义是冗余的,并且不会发出它 在所有的对象文件中。像这样:

$ 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
定义 完全它被优化了

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

/* 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
};
但这一次,由于使用了属性
LOCAL
定义了
ApplicationID
重新出现在第4节中(此对象文件中的该节为
.rodata.$AppID

这就是属性
的使用方式。它影响编译器的行为:它没有
对链接器的影响

我们还没有做任何联系。让我们现在做一些

/* hello_world.c */

#include <stdio.h>

int main(void)
{
    puts("Hello world!")
    return 0;
}
在链接中,我要求删除未使用的输入部分,并创建一个映射文件 待输出(
-Wl,-gc节,-Map=mapfile.txt

在地图文件中,我们可以找到:

Mapfile.txt

...
Discarded input sections
  ...
  .rodata.$AppID
                0x0000000000000000       0x10 app_id_static_used.o
  ...
链接器已丢弃
.rodata.$AppID
来自
app\u id\u static\u的输入。o
因为程序中未引用该节中定义的符号。具有 属性
已使用
,我们强制编译器发出该
静态
符号的定义 在
应用程序中\u id\u static\u used.o
。这并不会迫使链接器需要它,或者保留它 它在可执行文件中

在中,我们从
app\u id\u static\u 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
};
然后你需要做的是通知链接器你想要 符号
ApplicationID
保留,即使您的程序未引用该符号,以及 即使删除了未使用的部分

要实现这一点,请使用链接器选项
--undefined=ApplicationID
。这将 指示链接器从一开始就假定程序的链接 遇到了对
ApplicationID
的未定义引用,并强制链接器 查找并链接其定义(如果任何输入文件提供)。因此:

$ 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
最后,请注意,
app\u id\u extern\u section.o
已合并到输出部分
.rodata
,因为链接器的默认 链接器脚本指定:

.rodata         : { *(.rodata .rodata.* .gnu.linkonce.r.*) }
i、 e.匹配
.rodata
.rodata.
的所有输入节.gnu.linkonce.r.*
将输出到
.rodata
。这意味着,即使:

__attribute__((section(".rodata.$AppID")))
这是多余的。因此,应用程序id源文件也可能只是其中之一 我从,
app\u id\u extern.c
和链接选项
--undefined=ApplicationID
是在程序中保留未引用符号所需的全部。除非
您的链接器在这方面是不同的,您会发现它们是相同的。

您引用的映射文件,是链接器的映射文件吗?--您是否通过
objdump
查看了对象文件,变量是否包含?@thebusybee编译器发出警告,警告“./src/protocol.c:150:23:warning:unused variable'ApplicationID'[-Wunused variable]extern const uint8_t ApplicationID[16];”,链接器将其列在丢弃的部分中。它的值未发出,因此在导出时不存在
/* 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
};
$ gcc -O1 -c app_id_extern_section.c
$ gcc -o hello hello_world.o app_id_extern_section.o -Wl,-gc-sections,--undefined=ApplicationID
$ readelf -s hello | grep ApplicationID
    58: 0000000000002010    16 OBJECT  GLOBAL DEFAULT   18 ApplicationID
$ readelf --sections hello | grep '.rodata'
  [18] .rodata           PROGBITS         0000000000002000  00002000
.rodata         : { *(.rodata .rodata.* .gnu.linkonce.r.*) }
__attribute__((section(".rodata.$AppID")))