电子说
1、section的作用
section主要作用是将函数或者变量放在指定段中,这样就可在指定的位置取出。
//section demo with gcc #include "stdio.h" int __attribute__((section("my_fun"))) test1(int a,int b) { return (a+b); } int test(int b) { return 2*b; } int __attribute__((section("my_fun"))) test0(int a,int b) { return (a*b); } int __attribute__((section("my_val"))) chengi; int __attribute__((section("my_val"))) chengj; int main(void) { int sum,c,j; chengi=1,chengj=2; sum=test1(chengi,chengj); c=test(100); j=test0(chengi,chengj); printf("sum=%d,c=%d,j=%d ",sum,c,j); return 0; }
编译生成map文件:
gcc -o main.exe main.c -Wl,-Map,my_test.map
my_test.map 文件片段如下:
.text 0x00401460 0xa0 C:\Users\think\ccmGLaeH.o 0x00401460 test 0x0040146a main .text 0x00401500 0x0 c:/mingw/bin/../libmingw32.a(CRTglob.o) ...... my_fun 0x00404000 0x200 [!provide] PROVIDE (___start_my_fun, .) my_fun 0x00404000 0x1c C:\Users\think\ccmGLaeH.o 0x00404000 test1 0x0040400d test0 [!provide] PROVIDE (___stop_my_fun, .) .data 0x00405000 0x200 0x00405000 \__data_start_\_ = . ...... *(.data_cygwin_nocopy) my_val 0x00406000 0x200 [!provide] PROVIDE (___start_my_val, .) my_val 0x00406000 0x8 C:\Users\think\ccdMcTrl.o 0x00406000 chengi 0x00406004 chengj [!provide] PROVIDE (___stop_my_val, .) .rdata 0x00407000 0x400
分析可见,使用section修饰的函数和变量在自定义的片段,而且是连续存放在___start_xx到___stop_xx之间,这样可根据变量的地址得出与其同段变量的地址,为后续自动初始化等功能提供了基础。
2、 自动初始化
基于前面section的作用,可以将同类函数指针全部使用同一个段名修饰,然后开机后系统自动检索段内函数指针,逐个执行,对上层应用就是无需主动调用,系统自动初始化。
考虑到硬件初始化与应用功能初始化的先后顺序,可以对段名进行分配,map文件按段名排序。自动初始化主体是OS_INIT_EXPORT宏。
范例代码出自中国移动的oneos开源版本,使用gcc,方案和国产RT-Thread类似。
typedef os_err_t (*os_init_fn_t)(void); #define OS_INIT_EXPORT(fn, level) const os_init_fn_t __os_call_##fn OS_SECTION(".init_call."level) = fn #define OS_BOARD_INIT(fn) OS_INIT_EXPORT(fn, "1") #define OS_PREV_INIT(fn) OS_INIT_EXPORT(fn, "2") #define OS_DEVICE_INIT(fn) OS_INIT_EXPORT(fn, "3") #define OS_CMPOENT_INIT(fn) OS_INIT_EXPORT(fn, "4") #define OS_ENV_INIT(fn) OS_INIT_EXPORT(fn, "5") #define OS_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"段 */ const os_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文件,如下图所示:
//系统底层在合适的时机调用如下两函数,将指定段区间内的所有函数自动执行 void os_board_auto_init(void) { const os_init_fn_t *fn_ptr_board_init_start; const os_init_fn_t *fn_ptr_board_init_end; const os_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; } static void os_other_auto_init(void) { const os_init_fn_t *fn_ptr_other_init_start; const os_init_fn_t *fn_ptr_other_init_end; const os_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属性的定义略有差异,但效果相同。
/* Compiler Related Definitions */ #if defined(__CC_ARM) || defined(__CLANG_ARM) /* ARM Compiler */ #define SECTION(x) __attribute__((section(x))) #elif defined (__IAR_SYSTEMS_ICC__) /* for IAR Compiler */ #define SECTION(x) @ x #elif defined (__GNUC__) /* GNU GCC Compiler */ #define SECTION(x) __attribute__((section(x))) #elif defined (__ADSPBLACKFIN__) /* for VisualDSP++ Compiler */ #define SECTION(x) __attribute__((section(x))) #elif defined (_MSC_VER) #define SECTION(x) #elif defined (__TI_COMPILER_VERSION__) /* * The way that TI compiler set section is different from other(at least * GCC and MDK) compilers. See ARM Optimizing C/C++ Compiler 5.9.3 for more * details. */ #define SECTION(x) #else #error not supported tool chain #endif
上面的 #error 也是个应用技巧,配搭 #if / #else / #endif 在编译阶段即可发现代码问题,一般用于判断宏定义的配置是否在预期之外,编译报错必须修改。
配合C关键字,对代码的安全校验、扩展移植都会有很好的效果。对小型项目、个人独立开发看不出效果,但对复杂的多人合作的项目,合适的关键字对代码的稳定性和架构是锦上添花。
审核编辑:刘清
全部0条评论
快来发表一下你的评论吧 !