[ARM入门] C关键字section的作用

[复制链接]
 楼主| 我喜欢打游戏 发表于 2022-5-3 11:32 | 显示全部楼层 |阅读模式
1、section的作用

section主要作用是将函数或者变量放在指定段中,这样就可在指定的位置取出。

  1. //section demo with gcc
  2. #include "stdio.h"  
  3.    
  4. int __attribute__((section("my_fun"))) test1(int a,int b)  
  5. {  
  6. return (a+b);  
  7. }

  8. int test(int b)  
  9. {  
  10. return 2*b;  
  11. }

  12. int __attribute__((section("my_fun"))) test0(int a,int b)  
  13. {  
  14. return (a*b);  
  15. }  
  16.    
  17. int __attribute__((section("my_val"))) chengi;  
  18. int __attribute__((section("my_val"))) chengj;  
  19.    
  20. int main(void)  
  21. {  
  22. int sum,c,j;   
  23. chengi=1,chengj=2;  
  24.    
  25. sum=test1(chengi,chengj);  
  26. c=test(100);  
  27. j=test0(chengi,chengj);  
  28.       
  29.   printf("sum=%d,c=%d,j=%d\r\n",sum,c,j);  

  30.   return 0;  
  31. }  

编译生成map文件:

  1. gcc -o main.exe main.c -Wl,-Map,my_test.map

my_test.map 文件片段如下:

  1. .text  0x00401460 0xa0 C:\\Users\\think\\ccmGLaeH.o
  2.      0x00401460   test
  3.      0x0040146a   main

  4.    .text 0x00401500 0x0 c:/mingw/bin/../libmingw32.a(CRTglob.o)

  5.    ......
  6.    my_fun 0x00404000 0x200
  7.    [!provide] PROVIDE (___start_my_fun, .)

  8.    my_fun  0x00404000 0x1c C:\\Users\\think\\ccmGLaeH.o

  9.   0x00404000 test1
  10.   0x0040400d test0

  11.   [!provide] PROVIDE (___stop_my_fun, .)

  12.   .data 0x00405000 0x200

  13.   0x00405000 \__data_start_\_ = .
  14.   ......
  15.   \*(.data_cygwin_nocopy)
  16.   my_val 0x00406000 0x200

  17.   [!provide] PROVIDE (___start_my_val, .)

  18.   my_val  0x00406000 0x8 C:\\Users\\think\\ccdMcTrl.o
  19.      0x00406000 chengi
  20.      0x00406004 chengj

  21.   [!provide] PROVIDE (___stop_my_val, .)

  22.   .rdata 0x00407000 0x400

分析可见,使用section修饰的函数和变量在自定义的片段,而且是连续存放在___start_xx到___stop_xx之间,这样可根据变量的地址得出与其同段变量的地址,为后续自动初始化等功能提供了基础。



 楼主| 我喜欢打游戏 发表于 2022-5-3 11:34 | 显示全部楼层
本帖最后由 我喜欢打游戏 于 2022-5-3 11:35 编辑

2、 自动初始化

基于前面section的作用,可以将同类函数指针全部使用同一个段名修饰,然后开机后系统自动检索段内函数指针,逐个执行,对上层应用就是无需主动调用,系统自动初始化。

考虑到硬件初始化与应用功能初始化的先后顺序,可以对段名进行分配,map文件按段名排序。自动初始化主体是OS_INIT_EXPORT宏。

范例代码出自中国移动的oneos开源版本,使用gcc,方案和国产RT-Thread类似。

  1. typedef os_err_t (*os_init_fn_t)(void);

  2. #define OS_INIT_EXPORT(fn, level) \
  3. const os_init_fn_t __os_call_##fn OS_SECTION(".init_call."level) = fn

  4. #define OS_BOARD_INIT(fn) OS_INIT_EXPORT(fn, "1")

  5. #define OS_PREV_INIT(fn) OS_INIT_EXPORT(fn, "2")

  6. #define OS_DEVICE_INIT(fn) OS_INIT_EXPORT(fn, "3")

  7. #define OS_CMPOENT_INIT(fn) OS_INIT_EXPORT(fn, "4")

  8. #define OS_ENV_INIT(fn) OS_INIT_EXPORT(fn, "5")

  9. #define OS_APP_INIT(fn) OS_INIT_EXPORT(fn, "6")

例如shell初始化函数,定义如下:

  1. OS_APP_INIT(sh_system_init);

将宏定义展开


  1. /* 含义是函数指针 __os_call_sh_system_init
  2. *  其指向sh_system_init函数,且该指针编译后放在".init_call.6"段
  3. */
  4. const os_init_fn_t __os_call_sh_system_init
  5.     __attribute__((section((".init_call.6")))) = sh_system_init

系统自身也有自定义函数,用来标记起止点函数

  1. OS_INIT_EXPORT(os_init_start, "0");//段起点__start
  2. OS_INIT_EXPORT(os_board_init_start, "0.end");
  3. OS_INIT_EXPORT(os_board_init_end, "1.end");
  4. OS_INIT_EXPORT(os_init_end, "6.end");//段终点__stop

最终生成的map文件如下图:

  1. //系统底层在合适的时机调用如下两函数,将指定段区间内的所有函数自动执行

  2. void os_board_auto_init(void)
  3. {
  4.     const os_init_fn_t *fn_ptr_board_init_start;
  5.     const os_init_fn_t *fn_ptr_board_init_end;
  6.     const os_init_fn_t *fn_ptr;

  7.     fn_ptr_board_init_start = &__os_call_os_board_init_start + 1;
  8.     fn_ptr_board_init_end   = &__os_call_os_board_init_end - 1;
  9.    
  10.     //将段首尾区间内的函数全部遍历执行
  11.     for (fn_ptr = fn_ptr_board_init_start; fn_ptr <= fn_ptr_board_init_end; fn_ptr++)
  12.     {
  13.         (void)(*fn_ptr)();
  14.     }
  15.     return;
  16. }

  17. static void os_other_auto_init(void)
  18. {
  19.     const os_init_fn_t *fn_ptr_other_init_start;
  20.     const os_init_fn_t *fn_ptr_other_init_end;
  21.     const os_init_fn_t *fn_ptr;

  22.     fn_ptr_other_init_start = &__os_call_os_board_init_end + 1;
  23.     fn_ptr_other_init_end   = &__os_call_os_init_end - 1;

  24.     for (fn_ptr = fn_ptr_other_init_start; fn_ptr <= fn_ptr_other_init_end; fn_ptr++)
  25.     {
  26.         (void)(*fn_ptr)();
  27.     }
  28.     return;
  29. }

系统执行os_other_auto_init时实现了sh_system_init的自动执行,即使应用层没有显示的去调用它。使用符号段的方式实现初始化函数自动执行,应用层修改软件,增加功能启动或者裁剪,对底层代码无需任何改动。更多信息请关注微信公众号【嵌入式系统】。

注意:段中函数类型都是一样的,范例是同一类函数指针,也可以是结构体,需要确保每个成员占用空间大小相同,这样才能逐个遍历。


本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

×
 楼主| 我喜欢打游戏 发表于 2022-5-3 11:36 | 显示全部楼层
3、总结

不同编译器对section属性的定义略有差异,但效果相同。

  1. /* Compiler Related Definitions */
  2. #if defined(__CC_ARM) || defined(__CLANG_ARM)           /* ARM Compiler */
  3.     #define SECTION(x)    __attribute__((section(x)))
  4. #elif defined (__IAR_SYSTEMS_ICC__)                     /* for IAR Compiler */
  5.     #define SECTION(x)     [url=home.php?mod=space&uid=72445]@[/url] x
  6. #elif defined (__GNUC__)                                /* GNU GCC Compiler */
  7.     #define SECTION(x)    __attribute__((section(x)))
  8. #elif defined (__ADSPBLACKFIN__)                        /* for VisualDSP++ Compiler */
  9.     #define SECTION(x)     __attribute__((section(x)))
  10. #elif defined (_MSC_VER)
  11.     #define SECTION(x)
  12. #elif defined (__TI_COMPILER_VERSION__)
  13.     /*
  14.      * The way that TI compiler set section is different from other(at least
  15.      * GCC and MDK) compilers. See ARM Optimizing C/C++ Compiler 5.9.3 for more
  16.      * details.
  17.      */
  18.     #define SECTION(x)
  19. #else
  20.     #error not supported tool chain
  21. #endif

上面的 #error 也是个应用技巧,配搭 #if  / #else / #endif 在编译阶段即可发现代码问题,一般用于判断宏定义的配置是否在预期之外,编译报错必须修改。

配合C关键字,对代码的安全校验、扩展移植都会有很好的效果,可参考前文 C语言关键字技巧。对小型项目、个人独立开发看不出效果,但对复杂的多人合作的项目,合适的关键字对代码的稳定性和架构是锦上添花。



您需要登录后才可以回帖 登录 | 注册

本版积分规则

80

主题

626

帖子

1

粉丝
快速回复 在线客服 返回列表 返回顶部