鲜为人知的__attribute__((__section__(section_name))),你知道怎么用吗?

小飞哥玩嵌入式 2023-04-15 17:01

简介

__attribute__ 是gcc编译器支持的一个编译特性(arm编译器也支持此特性,比如我们常用的keil就是用的ARMGCC编译器),也就是通过给函数或者变量声明属性值,以便让编译器能够对要编译的程序进行优化处理。

更多详细内容,请看这篇官网文档:《Unixwiz.net - Software Consulting Central -- Using GNU C __attribute__》:http://www.unixwiz.net/techtips/gnu-c-attributes.html而对于 section 这个关键字,我们可以通过它将指定的变量定义到指定的输入段中。keil中对于此用法的描述是:ARM Compiler v5.06 for uVision armcc User Guide

section 属性指定变量必须放置在特定数据部分中,通常,ARM 编译器将其生成的对象放在 .data 和 .bss 等部分中。但是,您可能需要其他数据部分,或者您可能希望变量出现在特殊部分中,例如,映射到特殊硬件。

如果使用 section 属性,则只读变量将放置在 RO 数据部分中,读写变量将放置在 RW 数据部分中,除非您使用 zero_init 属性。在这种情况下,变量放置在 ZI 部分中。

/* in RO section */
const int descriptor[3] __attribute__((section ("descr"))) = { 1,2,3 };
/* in RW section */
long long rw_initialized[10] __attribute__((section ("INITIALIZED_RW"))) = {5};
/* in RW section */
long long rw[10] __attribute__((section ("RW")));
/* in ZI section */
long long altstack[10] __attribute__((section ("STACK"), zero_init));

用法详解

先来看一段代码(摘自CSDN,如有侵权,联系删除):

#include 

#define SEC __attribute__((__section__("ss")aligned(sizeof(void *))))

void func_1(int a, int b)
{
    printf("%s %d %d\n", __func__, __LINE__, a + b);
}
void func_2(int a, int b)
{
    printf("%s %d %d\n", __func__, __LINE__, a * b);
}

// 编译器会自动提供__start_ss,__stop_ss标志段ss的起止地址
extern size_t __start_ss;
extern size_t __stop_ss;

typedef struct
{
    void (*p)(intint);
} node_t;

// 结构体变量a位于自定义段ss
SEC node_t a = {
    .p = func_1,
};
SEC node_t b = {
    .p = func_2,
};
int main(int argc, char **argv)
{
    int a = 3, b = 4;
    node_t *p;
    // 遍历段ss,执行node_t结构中的p指向的函数
    for (p = (node_t *)&__start_ss; p < (node_t *)&__stop_ss; p++)
    {
        p->p(a, b);
        a += 1;
        b += 2;
    }
}

来看一下运行的结果:

1、section关键字可以将变量定义到指定的输入段中,

#define  SECTION(level)                  __attribute__((used,__section__(".fn_cmd."level)))
#define     CMD_START_EXPORT(func,func_s)   const struct CMD_LIST cmd_fn_##func SECTION("0.end") 
= {func,func_s}
CMD_START_EXPORT(start_fun,"start_fun");

const struct CMD_LIST cmd_fn_##func SECTION("0.end") = {func,func_s}

const struct CMD_LIST cmd_fn_func SECTION("0.end") = {start_fun,start_fun}

const struct CMD_LIST cmd_fn_func  __attribute__((used,__section__(".fn_cmd.""0.end"))) = {start_fun,start_fun}

这个时候再来看会发现其实就是定义了一个struct CMD_LIST 类型的变量,变量的名字是cmd_fn_start_fun,并且这个变量被放到了我们所希望的一个输入段.fn_cmd.0.end中了


typedef void (*fun)();
struct CMD_LIST
{
 fun funs;
 const INT8 *cmd;
};

2、使用section将变量放到我们自定义的输入段中有什么意义呢?

#define SECTION(level)  __attribute__((used,__section__(".fn_cmd."level)))
#define CMD_START_EXPORT(func,func_s) const struct CMD_LIST cmd_fn_##func SECTION("0.end") 
= {func,func_s}
#define CMD_EXPORT(func,func_s) const struct CMD_LIST cmd_fn_##func SECTION("1") = {func,func_s}
#define CMD_END_EXPORT(func,func_s) const struct CMD_LIST cmd_fn_##func SECTION("1.end") = {func,func_s}

当这几个宏被调用时将会产生名为cmd_fn_xx的变量,并且这个变量根据被调用的宏来把这个变量放到相应的输入段。例如CMD_START_EXPORT这个宏,这个宏其实上面已经讲过了, 调用这个宏的时候会产生一个名为cmd_fn_xx的变量,并且把这个变量放到了我们自定义的输入段.fn_cmd.0.end中了。

其他两个宏的其实也是差不多的,不同之处就是输入段有些区别。言归正传,现在继续来讲如何使用section将不同的函数放到我们想要的输入段中,并且获得他们的起始地址和结束地址。

我们可以在每个XXX_Init函数后面都调用宏CMD_EXPORT,在调用这个宏时编译器会将XXX_init这个函数加入到输入段中,由于变量在输入段中的地址是连续的,并且顺序先按 section 名 01234排一遍,section 内再按函数名称排。

所以可以按照输入段中顺序来逐个调用这些初始化函数来完成系统的初始化。具体实现我会根据我的一个自定义命令行的应用来进行部分的说明。

/*命令函数段起始位置*/
int cmd_start(void)
{
 return 0;
}
CMD_START_EXPORT(cmd_start,"int cmd_start(void)");
 
/*命令函数段结束位置*/
int cmd_end(void)
{
 return 0;
}
CMD_END_EXPORT(cmd_end,"int cmd_end(void)");
 
void test(void)
{
 printf("hello world\r\n");
}
CMD_EXPORT(test,"void test(void)");
 
void demo(void)
{
 printf("hello world\r\n");
}
CMD_EXPORT(demo,"void demo(void)");

先定义start和end函数并且分别使用CMD_START_EXPORT和CMD_END_EXPORT来将其放到输入段.fn_cmd.0.end和.fn_cmd.1.end中,按照上面的说明输入段.fn_cmd.0.end是排在输入段.fn_cmd.1.end前面的,而使用的CMD_EXPORT这个宏对应的输入段.fn_cmd.1是排在.fn_cmd.0.end和.fn_cmd.1.end之间的,这里可以看一下编译产生的.map会更加的形象一些。具体在MAP文件的位置如下所示

 cmd_fn_cmd_start                         0x080042f0   Data           8  serialcmd.o(.fn_cmd.0.end)
 cmd_fn_test                              0x080042f8   Data           8  application.o(.fn_cmd.1)
 cmd_fn_demo                              0x08004300   Data           8  application.o(.fn_cmd.1)
 cmd_fn_cmd_end                           0x08004308   Data           8  serialcmd.o(.fn_cmd.1.end)

typedef void (*fun)();
struct CMD_LIST
{
 fun funs;
 const INT8 *cmd;
};

const static struct CMD_LIST *CmdList;
static UINT8 CmdSize = 0;
 
/*命令函数初始化*/
void SerialCmdInit(void)
{
 const struct CMD_LIST *cmd_ptr;
 CmdList = &cmd_fn_cmd_start;
 CmdList++;
 for (cmd_ptr = CmdList; cmd_ptr < &cmd_fn_cmd_end;cmd_ptr++)
 {
        /*这里如果用于初始化的话可以使用下面这种方式来执行初始化函数,因为我的应用并不是用于初始            
         化,所以就没有进行函数调用。
         (*cmd_ptr->fun)();
         */

  CmdSize++;
 }
}

其中cmd_fn_cmd_start是在.fn_cmd.0.end这个输入段中的,而各个要执行的函数是在.fn_cmd.1这个输入段中的,cmd_fn_cmd_end作为结束的标志在.fn_cmd.1.end输入段中。

其余的就不做过多讲解了,从上面的.map文件中的地址很容易就可以看出在得到了起始地址和结束地址之后怎样一个个遍历这些函数。

然而上述功能只能对 GCC 平台有效, 如果是 ARMCC 或是其他平台, 因为编译器不同, 方法可能不一样, 为了跨平台, 就不得不添加平台检测的宏, 比如将下面的代码替换获取 myfun_section 所在的内存区间部分即可支持 ARMCC 平台。

#ifdef __ARMCC_VERSION  /* ARM C Compiler */
    extern test_command_t myfun_section$$Base;
    extern test_command_t myfun_section$$Limit;
    test_command_t *myfun_section_begin = &(myfun_section$$Base);
    test_command_t *myfun_section_end = &(myfun_section$$Limit);
#elif defined (__GNUC__)
    extern test_command_t __start_myfun_section
;
    extern test_command_t __stop_myfun_section;
    test_command_t *myfun_section_begin = &__start_myfun_section;
    test_command_t *myfun_section_end = &__stop_myfun_section;
#else
    #error "The platform is not supported"
#endif

结语

以上就是关于attribute(section)在GCC和ARMGCC中的使用,下章节小飞哥会带大家如何利用这个功能实现代码的分模块设计,值得期待,欢迎关注小飞哥

小飞哥玩嵌入式 分享嵌入式开发相关知识,喜欢DIY分享
评论 (0)
  •     前几天同事问我,电压到多少伏就不安全了?考虑到这位同事的非电专业背景,我做了最极端的答复——多少伏都不安全,非专业人员别摸带电的东西。    那么,是不是这么绝对呢?我查了一下标准,奇怪的知识增加了。    标准的名字值得玩味——《电流对人和家畜的效应》,GB/T 13870.5 (IEC 60749-5)。里面对人、牛、尸体分类讨论(搞硬件的牛马一时恍惚,不知道自己算哪种)。    触电是电流造成的生理效应
    电子知识打边炉 2025-04-09 22:35 263浏览
  • ‌亥姆霍兹线圈‌是由两组相同的线圈组成,线圈之间的距离等于它们的半径。当电流同时流过这两个线圈时,会在它们中间形成一个几乎均匀的磁场。这种设计克服了普通线圈磁场不均匀的缺陷,能够在中心区域形成稳定、均匀的磁场‌。‌亥姆霍兹线圈的应用领域‌包括材料、电子、生物、医疗、航空航天、化学、应用物理等各个学科。由于其操作简便且能够提供极微弱的磁场直至数百高斯的磁场,亥姆霍兹线圈在各研究所、高等院校及企业中被广泛用于物质磁性或检测实验。‌亥姆霍兹线圈可以根据不同的标准进行分类‌:‌按磁场方向分类‌:‌一维亥
    锦正茂科技 2025-04-09 17:20 171浏览
  • 技术原理:非扫描式全局像的革新Flash激光雷达是一种纯固态激光雷达技术,其核心原理是通过面阵激光瞬时覆盖探测区域,配合高灵敏度传感器实现全局三维成像。其工作流程可分解为以下关键环节:1. 激光发射:采用二维点阵光源(如VCSEL垂直腔面发射激光器),通过光扩散器在单次脉冲中发射覆盖整个视场的面阵激光,视场角通常可达120°×75°,部分激光雷达产品可以做到120°×90°的超大视场角。不同于传统机械扫描或MEMS微振镜方案,Flash方案无需任何移动部件,直接通过电信号控制激光发射模式。2.
    robolab 2025-04-10 15:30 144浏览
  • 什么是车用高效能运算(Automotive HPC)?高温条件为何是潜在威胁?作为电动车内的关键核心组件,由于Automotive HPC(CPU)具备高频高效能运算电子组件、高速传输接口以及复杂运算处理、资源分配等诸多特性,再加上各种车辆的复杂应用情境等等条件,不难发见Automotive HPC对整个平台讯号传输实时处理、系统稳定度、耐久度、兼容性与安全性将造成多大的考验。而在各种汽车使用者情境之中,「高温条件」就是你我在日常生活中必然会面临到的一种潜在威胁。不论是长时间将车辆停放在室外的高
    百佳泰测试实验室 2025-04-10 15:09 123浏览
  •   卫星故障预警系统软件:卫星在轨安全的智能护盾   北京华盛恒辉卫星故障预警系统软件,作为确保卫星在轨安全运行的关键利器,集成前沿的监测、诊断及预警技术,对卫星健康状况予以实时评估,提前预判潜在故障。下面将从核心功能、技术特性、应用场景以及发展走向等方面展开详尽阐述。   应用案例   目前,已有多个卫星故障预警系统在实际应用中取得了显著成效。例如,北京华盛恒辉和北京五木恒润卫星故障预警系统。这些成功案例为卫星故障预警系统的推广和应用提供了有力支持。   核心功能   实时状态监测:
    华盛恒辉l58ll334744 2025-04-09 19:49 193浏览
  •   天空卫星健康状况监测维护管理系统:全方位解析  在航天技术迅猛发展的当下,卫星在轨运行的安全与可靠至关重要。整合多种技术,实现对卫星的实时监测、故障诊断、健康评估以及维护决策,有力保障卫星长期稳定运转。  应用案例       系统软件供应可以来这里,这个首肌开始是幺伍扒,中间是幺幺叁叁,最后一个是泗柒泗泗,按照数字顺序组合就可以找到。  一、系统架构与功能模块  数据采集层  数据处理层  智能分析层  决策支持层  二、关键技术  故障诊断技术  
    华盛恒辉l58ll334744 2025-04-10 15:46 124浏览
  • 行业痛点:电动车智能化催生语音交互刚需随着全球短途出行市场爆发式增长,中国电动自行车保有量已突破3.5亿辆。新国标实施推动行业向智能化、安全化转型,传统蜂鸣器报警方式因音效单一、缺乏场景适配性等问题,难以满足用户对智能交互体验的需求。WT2003HX系列语音芯片,以高性能处理器架构与灵活开发平台,为两轮电动车提供从基础报警到智能交互的全栈语音解决方案。WT2003HX芯片技术优势深度解读1. 高品质硬件性能,重塑语音交互标准搭载32位RISC处理器,主频高达120MHz,确保复杂算法流畅运行支持
    广州唯创电子 2025-04-10 09:12 213浏览
  • 背景近年来,随着国家对资源、能源有效利用率的要求越来越高,对环境保护和水处理的要求也越来越严格,因此有大量的固液分离问题需要解决。真空过滤器是是由负压形成真空过滤的固液分离机械。用过滤介质把容器分为上、下两层,利用负压,悬浮液加入上腔,在压力作用下通过过滤介质进入下腔成为滤液,悬浮液中的固体颗粒吸附在过滤介质表面形成滤饼,滤液穿过过滤介质经中心轴内部排出,达到固液分离的目的。目前市面上的过滤器多分为间歇操作和连续操作两种。间歇操作的真空过滤机可过滤各种浓度的悬浮液,连续操作的真空过滤机适于过滤含
    宏集科技 2025-04-10 13:45 125浏览
  • 文/Leon编辑/侯煜‍关税大战一触即发,当地时间4月9日起,美国开始对中国进口商品征收总计104%的关税。对此,中国外交部回应道:中方绝不接受美方极限施压霸道霸凌,将继续采取坚决有力措施,维护自身正当权益。同时,中国对原产于美国的进口商品加征关税税率,由34%提高至84%。随后,美国总统特朗普在社交媒体宣布,对中国关税立刻提高至125%,并暂缓其他75个国家对等关税90天,在此期间适用于10%的税率。特朗普政府挑起关税大战的目的,实际上是寻求制造业回流至美国。据悉,特朗普政府此次宣布对全球18
    华尔街科技眼 2025-04-10 16:39 154浏览
  • 政策驱动,AVAS成新能源车安全刚需随着全球碳中和目标的推进,新能源汽车产业迎来爆发式增长。据统计,2023年中国新能源汽车渗透率已突破35%,而欧盟法规明确要求2024年后新能效车型必须配备低速提示音系统(AVAS)。在此背景下,低速报警器作为车辆主动安全的核心组件,其技术性能直接关乎行人安全与法规合规性。基于WT2003H芯片开发的AVAS解决方案,以高可靠性、强定制化能力及智能场景适配特性,正成为行业技术升级的新标杆。WT2003H方案技术亮点解析全场景音效精准触发方案通过多传感器融合技术
    广州唯创电子 2025-04-10 08:53 238浏览
  •   海上电磁干扰训练系统:全方位解析      海上电磁干扰训练系统,作为模拟复杂海上电磁环境、锻炼人员应对电磁干扰能力的关键技术装备,在军事、科研以及民用等诸多领域广泛应用。接下来从系统构成、功能特点、技术原理及应用场景等方面展开详细解析。   应用案例   系统软件供应可以来这里,这个首肌开始是幺伍扒,中间是幺幺叁叁,最后一个是泗柒泗泗,按照数字顺序组合就可以找到。   一、系统构成   核心组件   电磁信号模拟设备:负责生成各类复杂的电磁信号,模拟海上多样
    华盛恒辉l58ll334744 2025-04-10 16:45 207浏览
  • 行业变局:从机械仪表到智能交互终端的跃迁全球两轮电动车市场正经历从“功能机”向“智能机”的转型浪潮。数据显示,2024年智能电动车仪表盘渗透率已突破42%,而传统LED仪表因交互单一、扩展性差等问题,难以满足以下核心需求:适老化需求:35%中老年用户反映仪表信息辨识困难智能化缺口:78%用户期待仪表盘支持手机互联与语音交互成本敏感度:厂商需在15元以内BOM成本实现功能升级在此背景下,集成语音播报与蓝牙互联的WT2605C-32N芯片方案,以“极简设计+智能交互”重构仪表盘技术生态链。技术破局:
    广州唯创电子 2025-04-11 08:59 180浏览
  • 由西门子(Siemens)生产的SIMATIC S7 PLC在SCADA 领域发挥着至关重要的作用。在众多行业中,SCADA 应用都需要与这些 PLC 进行通信。那么,有哪些高效可行的解决方案呢?宏集为您提供多种选择。传统方案:通过OPC服务器与西门子 PLC 间接通信SIMATIC S7系列的PLC是工业可编程控制器,能够实现对生产流程的实时SCADA监控,提供关于设备和流程状态的准确、最新数据。S7Comm(全称S7 Communication),也被称为工业以太网或Profinet,是西门
    宏集科技 2025-04-10 13:44 148浏览
我要评论
0
0
点击右上角,分享到朋友圈 我知道啦
请使用浏览器分享功能 我知道啦