__attribute__
是GNU对标准C的扩展,可以用来设置函数属性(Function Attribute)、变量属性(Variable Attribute)和类型属性(Type Attribute)。
__attribute__
书写特征为:__attribute__
前后都有两个下划线,并切后面会紧跟一对原括弧,括弧里面是相应的参数。
__attribute__
语法格式为:__attribute__
((attribute-list))。
其位置约束为:放于声明的尾部";"之前。
used
标记为__attribute__((used))
的函数被标记在目标文件中,以避免链接器删除未使用的节。
weak
__attribute__((weak)) int main(int argc, char **argv)
{
return tbox::main::Main(argc, argv);
}
如上所示,main函数被定义为弱符号。
如果用户没有定义main函数,则会使用默认的main函数。
如果用户定义了自己的main函数,则会使用用户自定义的main函数。
packed
使用该属性可以使得变量或者结构体成员使用最小的对齐方式,即对变量是一字节对齐。
struct student
{
char name[7];
uint32_t id;
char subject[5];
} __attribute__ ((packed));
section
section关键字可以将变量或者函数定义到可执行文件特定段中:
int test __attribute__((section("show"))) = 0;
static elab_export_poll_data_t poll_shell_poll_data = { .timeout_ms = 0, };
__attribute__((used)) const elab_export_t poll_shell_poll __attribute__((section("expoll"))) =
{
.name = "poll",
.func = &shell_poll,
.data = (void *)&poll_shell_poll_data,
.level = EXPORT_POLL,
.period_ms = (uint32_t)((10)),
.magic_head = (0xbeefbeef),
.magic_tail = (0xbeefbeef),
};
使用section可以使我们如在初始化函数时,不用在主函数中去添加一个新的初始化程序,只需要在自己的函数模块内注册就好了。
或者在实现某些命令时,添加或删除该命令的支持,会方便很多。
以cpost为例,在使用section进行属性设置后,用户自定义的函数、const修饰的变量会被放到Flash指定的section内,以下定义了一个名为cEvent
的section。
#define CEVENT_EXPORT(_event, _func, ...) \
const void *cEventParam##_event##_func[] = {(void *)_func, ##__VA_ARGS__}; \
const CEvent SECTION("cEvent") cEvent##_event##_func = \
{ \
.param = cEventParam##_event##_func, \
.paramNum = sizeof(cEventParam##_event##_func) / sizeof(void *), \
.event = _event, \
}
查看MDK生成的map文件,可以看到该section在flash中的位置:
Memory Map of the image
Image Entry point : 0x08000131
Load Region LR_IROM1 (Base: 0x08000000, Size: 0x000042ac, Max: 0x00080000, ABSOLUTE)
Execution Region ER_IROM1 (Exec base: 0x08000000, Load base: 0x08000000, Size: 0x0000422c, Max: 0x00080000, ABSOLUTE)
Exec Addr Load Addr Size Type Attr Idx E Section Name Object
0x08000000 0x08000000 0x00000130 Data RO 3 RESET startup_stm32f10x_hd.o
0x08000130 0x08000130 0x00000000 Code RO 351 * .ARM.Collect$$$$00000000 mc_w.l(entry.o)
0x08000130 0x08000130 0x00000004 Code RO 631 .ARM.Collect$$$$00000001 mc_w.l(entry2.o)
0x08000134 0x08000134 0x00000004 Code RO 634 .ARM.Collect$$$$00000004 mc_w.l(entry5.o)
0x08000138 0x08000138 0x00000000 Code RO 636 .ARM.Collect$$$$00000008 mc_w.l(entry7b.o)
0x08000138 0x08000138 0x00000000 Code RO 638 .ARM.Collect$$$$0000000A mc_w.l(entry8b.o)
0x08000138 0x08000138 0x00000008 Code RO 639 .ARM.Collect$$$$0000000B mc_w.l(entry9a.o)
0x08000140 0x08000140 0x00000000 Code RO 641 .ARM.Collect$$$$0000000D mc_w.l(entry10a.o)
0x08000140 0x08000140 0x00000000 Code RO 643 .ARM.Collect$$$$0000000F mc_w.l(entry11a.o)
0x08000140 0x08000140 0x00000004 Code RO 632 .ARM.Collect$$$$00002712 mc_w.l(entry2.o)
0x08000144 0x08000144 0x00000030 Code RO 4 .text startup_stm32f10x_hd.o
0x08000174 0x08000174 0x00000174 Code RO 10 .text main.o
0x080002e8 0x080002e8 0x00000030 Code RO 83 .text stm32f10x_it.o
0x08000318 0x08000318 0x0000011c Code RO 104 .text usart1.o
0x08000434 0x08000434 0x000000bc Code RO 123 .text exti_pa0.o
0x080004f0 0x080004f0 0x00000330 Code RO 142 .text stm32f10x_gpio.o
0x08000820 0x08000820 0x000003a0 Code RO 154 .text stm32f10x_rcc.o
0x08000bc0 0x08000bc0 0x00000370 Code RO 168 .text stm32f10x_usart.o
0x08000f30 0x08000f30 0x000000dc Code RO 180 .text misc.o
0x0800100c 0x0800100c 0x00000140 Code RO 192 .text stm32f10x_exti.o
0x0800114c 0x0800114c 0x00000138 Code RO 214 .text system_stm32f10x.o
0x08001284 0x08001284 0x00000138 Code RO 232 .text cevent.o
0x080013bc 0x080013bc 0x000001b8 Code RO 252 .text cpost.o
0x08001574 0x08001574 0x0000130c Code RO 270 .text shell.o
0x08002880 0x08002880 0x000003b2 Code RO 321 .text shell_ext.o
0x08002c32 0x08002c32 0x00000002 PAD
0x08002c34 0x08002c34 0x00000074 Code RO 333 .text shell_port.o
0x08002ca8 0x08002ca8 0x00000024 Code RO 354 .text mc_w.l(memseta.o)
0x08002ccc 0x08002ccc 0x0000000e Code RO 356 .text mc_w.l(strlen.o)
0x08002cda 0x08002cda 0x0000001c Code RO 358 .text mc_w.l(strcmp.o)
0x08002cf6 0x08002cf6 0x0000001e Code RO 360 .text mc_w.l(strncmp.o)
0x08002d14 0x08002d14 0x00000064 Code RO 623 .text mf_w.l(fmul.o)
0x08002d78 0x08002d78 0x0000007c Code RO 625 .text mf_w.l(fdiv.o)
0x08002df4 0x08002df4 0x00000012 Code RO 627 .text mf_w.l(fflti.o)
0x08002e06 0x08002e06 0x0000000a Code RO 629 .text mf_w.l(ffltui.o)
0x08002e10 0x08002e10 0x0000002c Code RO 646 .text mc_w.l(uidiv.o)
0x08002e3c 0x08002e3c 0x00000062 Code RO 648 .text mc_w.l(uldiv.o)
0x08002e9e 0x08002e9e 0x00000000 Code RO 650 .text mc_w.l(iusefp.o)
0x08002e9e 0x08002e9e 0x0000006e Code RO 651 .text mf_w.l(fepilogue.o)
0x08002f0c 0x08002f0c 0x0000014e Code RO 653 .text mf_w.l(dadd.o)
0x0800305a 0x0800305a 0x000000e4 Code RO 655 .text mf_w.l(dmul.o)
0x0800313e 0x0800313e 0x000000de Code RO 657 .text mf_w.l(ddiv.o)
0x0800321c 0x0800321c 0x00000030 Code RO 659 .text mf_w.l(dfixul.o)
0x0800324c 0x0800324c 0x00000030 Code RO 661 .text mf_w.l(cdrcmple.o)
0x0800327c 0x0800327c 0x00000024 Code RO 663 .text mc_w.l(init.o)
0x080032a0 0x080032a0 0x0000001e Code RO 665 .text mc_w.l(llshl.o)
0x080032be 0x080032be 0x00000020 Code RO 667 .text mc_w.l(llushr.o)
0x080032de 0x080032de 0x00000024 Code RO 669 .text mc_w.l(llsshr.o)
0x08003302 0x08003302 0x000000ba Code RO 671 .text mf_w.l(depilogue.o)
0x080033bc 0x080033bc 0x00000020 Code RO 595 i.__0printf mc_w.l(printfa.o)
0x080033dc 0x080033dc 0x0000002c Code RO 600 i.__0vsnprintf mc_w.l(printfa.o)
0x08003408 0x08003408 0x0000000e Code RO 675 i.__scatterload_copy mc_w.l(handlers.o)
0x08003416 0x08003416 0x00000002 Code RO 676 i.__scatterload_null mc_w.l(handlers.o)
0x08003418 0x08003418 0x0000000e Code RO 677 i.__scatterload_zeroinit mc_w.l(handlers.o)
0x08003426 0x08003426 0x00000002 PAD
0x08003428 0x08003428 0x00000184 Code RO 602 i._fp_digits mc_w.l(printfa.o)
0x080035ac 0x080035ac 0x000006dc Code RO 603 i._printf_core mc_w.l(printfa.o)
0x08003c88 0x08003c88 0x00000024 Code RO 604 i._printf_post_padding mc_w.l(printfa.o)
0x08003cac 0x08003cac 0x0000002e Code RO 605 i._printf_pre_padding mc_w.l(printfa.o)
0x08003cda 0x08003cda 0x00000016 Code RO 606 i._snputc mc_w.l(printfa.o)
0x08003cf0 0x08003cf0 0x00000030 Data RO 11 .constdata main.o
0x08003d20 0x08003d20 0x00000010 Data RO 124 .constdata exti_pa0.o
0x08003d30 0x08003d30 0x000000e8 Data RO 272 .constdata shell.o
0x08003e18 0x08003e18 0x000002a1 Data RO 273 .conststring shell.o
0x080040b9 0x080040b9 0x00000003 PAD
0x080040bc 0x080040bc 0x00000020 Data RO 673 Region$$Table anon$$obj.o
0x080040dc 0x080040dc 0x00000010 Data RO 13 cEvent main.o
0x080040ec 0x080040ec 0x00000020 Data RO 14 shellCommand main.o
0x0800410c 0x0800410c 0x00000120 Data RO 275 shellCommand shell.o
每个section在可执行文件中都会有对应的起始、结束地址,不同编译器对此表示不一样:
#if defined(__CC_ARM) || (defined(__ARMCC_VERSION) && __ARMCC_VERSION >= 6000000)
extern const size_t cEvent$$Base; // MDK
extern const size_t cEvent$$Limit;
#elif defined(__ICCARM__) || defined(__ICCRX__)
#pragma section="cEvent" // IAR
#elif defined(__GNUC__)
extern const size_t _cevent_start; // gcc
extern const size_t _cevent_end;
#endif
#if CEVENT_SPEED_OPTIMIZE == 1 && CEVENT_BUFFER_SIZE > 0
static size_t ceventBuffer[CEVENT_BUFFER_SIZE] = {0};
#endif
我们可以通过起始、结束地址,对定义的变量进行访问:
CEvent *base;
size_t count;
#if defined(__CC_ARM) || (defined(__ARMCC_VERSION) && __ARMCC_VERSION >= 6000000)
base = (CEvent *)(&cEvent$$Base);
count = ((size_t)(&cEvent$$Limit) - (size_t)(&cEvent$$Base)) / sizeof(CEvent);
#elif defined(__ICCARM__) || defined(__ICCRX__)
base = (CEvent *)(__section_begin("cEvent"));
count = ((size_t)(__section_end("cEvent")) - (size_t)(__section_begin("cEvent")))
/ sizeof(CEvent);
#elif defined(__GNUC__)
base = (CEvent *)(&_cevent_start);
count = ((size_t)(&_cevent_end) - (size_t)(&_cevent_start)) / sizeof(CEvent);
#else
#error not supported compiler, please use command table mode
#endif
letter shell在注册命令时也使用了这种技巧,感兴趣可以看看对应的代码。