C关键字section的作用
来源:一口Linux发布时间:2023-03-26955浏览
询问 AI<摘要> 展示section关键字的作用,以及在SDK实现开机自启动的应用
1、section的作用
section主要作用是将函数或者变量放在指定段中,这样就可在指定的位置取出。
//sectiondemowithgcc
#include"stdio.h"
int__attribute__((section("my_fun")))test1(inta,intb)
{
return(a+b);
}
inttest(intb)
{
return2*b;
}
int__attribute__((section("my_fun")))test0(inta,intb)
{
return(a*b);
}
int__attribute__((section("my_val")))chengi;
int__attribute__((section("my_val")))chengj;
intmain(void)
{
intsum,c,j;
chengi=1,chengj=2;
sum=test1(chengi,chengj);
c=test(100);
j=test0(chengi,chengj);
printf("sum=%d,c=%d,j=%d\r\n",sum,c,j);
return0;
}
编译生成map文件:
gcc-omain.exemain.c-Wl,-Map,my_test.map
my_test.map 文件片段如下:
.text0x004014600xa0C:\\Users\\think\\ccmGLaeH.o
0x00401460test
0x0040146amain
.text0x004015000x0c:/mingw/bin/../libmingw32.a(CRTglob.o)
......
my_fun0x004040000x200
[!provide]PROVIDE(___start_my_fun,.)
my_fun0x004040000x1cC:\\Users\\think\\ccmGLaeH.o
0x00404000test1
0x0040400dtest0
[!provide]PROVIDE(___stop_my_fun,.)
.data0x004050000x200
0x00405000\__data_start_\_=.
......
\*(.data_cygwin_nocopy)
my_val0x004060000x200
[!provide]PROVIDE(___start_my_val,.)
my_val0x004060000x8C:\\Users\\think\\ccdMcTrl.o
0x00406000chengi
0x00406004chengj
[!provide]PROVIDE(___stop_my_val,.)
.rdata0x004070000x400
分析可见,使用section修饰的函数和变量在自定义的片段,而且是连续存放在___start_xx到___stop_xx之间,这样可根据变量的地址得出与其同段变量的地址,为后续自动初始化等功能提供了基础。
2、 自动初始化
基于前面section的作用,可以将同类函数指针全部使用同一个段名修饰,然后开机后系统自动检索段内函数指针,逐个执行,对上层应用就是无需主动调用,系统自动初始化。
考虑到硬件初始化与应用功能初始化的先后顺序,可以对段名进行分配,map文件按段名排序。自动初始化主体是OS_INIT_EXPORT宏。
范例代码出自中国移动的oneos开源版本,使用gcc,方案和国产RT-Thread类似。
typedefos_err_t(*os_init_fn_t)(void);
#defineOS_INIT_EXPORT(fn,level)\
constos_init_fn_t__os_call_##fnOS_SECTION(".init_call."level)=fn
#defineOS_BOARD_INIT(fn)OS_INIT_EXPORT(fn,"1")
#defineOS_PREV_INIT(fn)OS_INIT_EXPORT(fn,"2")
#defineOS_DEVICE_INIT(fn)OS_INIT_EXPORT(fn,"3")
#defineOS_CMPOENT_INIT(fn)OS_INIT_EXPORT(fn,"4")
#defineOS_ENV_INIT(fn)OS_INIT_EXPORT(fn,"5")
#defineOS_APP_INIT(fn)OS_INIT_EXPORT(fn,"6")
例如shell初始化函数,定义如下:
OS_APP_INIT(sh_system_init);
将宏定义展开
/*含义是函数指针__os_call_sh_system_init
*其指向sh_system_init函数,且该指针编译后放在".init_call.6"段
*/
constos_init_fn_t__os_call_sh_system_init
__attribute__((section((".init_call.6"))))=sh_system_init
系统自身也有自定义函数,用来标记起止点函数
OS_INIT_EXPORT(os_init_start,"0");//段起点__start
OS_INIT_EXPORT(os_board_init_start,"0.end");
OS_INIT_EXPORT(os_board_init_end,"1.end");
OS_INIT_EXPORT(os_init_end,"6.end");//段终点__stop
最终生成的map文件如下图:
//系统底层在合适的时机调用如下两函数,将指定段区间内的所有函数自动执行
voidos_board_auto_init(void)
{
constos_init_fn_t*fn_ptr_board_init_start;
constos_init_fn_t*fn_ptr_board_init_end;
constos_init_fn_t*fn_ptr;
fn_ptr_board_init_start=__os_call_os_board_init_start+1;
fn_ptr_board_init_end=__os_call_os_board_init_end-1;
//将段首尾区间内的函数全部遍历执行
for(fn_ptr=fn_ptr_board_init_start;fn_ptr<=fn_ptr_board_init_end;fn_ptr++)
{
(void)(*fn_ptr)();
}
return;
}
staticvoidos_other_auto_init(void)
{
constos_init_fn_t*fn_ptr_other_init_start;
constos_init_fn_t*fn_ptr_other_init_end;
constos_init_fn_t*fn_ptr;
fn_ptr_other_init_start=__os_call_os_board_init_end+1;
fn_ptr_other_init_end=__os_call_os_init_end-1;
for(fn_ptr=fn_ptr_other_init_start;fn_ptr<=fn_ptr_other_init_end;fn_ptr++)
{
(void)(*fn_ptr)();
}
return;
}
系统执行os_other_auto_init时实现了sh_system_init的自动执行,即使应用层没有显示的去调用它。使用符号段的方式实现初始化函数自动执行,应用层修改软件,增加功能启动或者裁剪,对底层代码无需任何改动。更多信息请关注微信公众号【嵌入式系统】。
注意:段中函数类型都是一样的,范例是同一类函数指针,也可以是结构体,需要确保每个成员占用空间大小相同,这样才能逐个遍历。
3、总结
不同编译器对section属性的定义略有差异,但效果相同。
/*CompilerRelatedDefinitions*/
#ifdefined(__CC_ARM)||defined(__CLANG_ARM) /*ARMCompiler*/
#defineSECTION(x)__attribute__((section(x)))
#elifdefined(__IAR_SYSTEMS_ICC__)/*forIARCompiler*/
#defineSECTION(x)@x
#elifdefined(__GNUC__)/*GNUGCCCompiler*/
#defineSECTION(x)__attribute__((section(x)))
#elifdefined(__ADSPBLACKFIN__)/*forVisualDSP++Compiler*/
#defineSECTION(x)__attribute__((section(x)))
#elifdefined(_MSC_VER)
#defineSECTION(x)
#elifdefined(__TI_COMPILER_VERSION__)
/*
*ThewaythatTIcompilersetsectionisdifferentfromother(atleast
*GCCandMDK)compilers.SeeARMOptimizingC/C++Compiler5.9.3formore
*details.
*/
#defineSECTION(x)
#else
#errornotsupportedtoolchain
#endif
上面的#error也是个应用技巧,配搭 #if / #else / #endif 在编译阶段即可发现代码问题,一般用于判断宏定义的配置是否在预期之外,编译报错必须修改。
配合C关键字,对代码的安全校验、扩展移植都会有很好的效果,可参考前文 C语言关键字技巧。对小型项目、个人独立开发看不出效果,但对复杂的多人合作的项目,合适的关键字对代码的稳定性和架构是锦上添花。
一口Linux
关注,回复【1024】海量Linux资料赠送
新闻来源:一口Linux,文中所述为作者独立观点,不代表icspec立场。更多精彩资讯请下载icspec App。如对本稿件有异议,请联系微信客服specltkj。

