将_属性_((使用))设置为C变量/常量无效
在ARM GCC(普通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 }; 而我在代码中
__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")))