section的含义和用法

先来一副整体的结构图

 下面是keil生成的链接文件

LR_IROM1 0x00026000 0x0005A000  {    ; load region size_region
  ER_IROM1 0x00026000 0x0005A000  {  ; load address = execution address
   *.o (RESET, +First)
   *(InRoot$$Sections)
   .ANY (+RO)
   .ANY (+XO)
  }
  RW_IRAM1 0x20002BE0 0x0000D420  {  ; RW data
   .ANY (+RW +ZI)
  }
}

链接文件可以对应到上图中的结构层级:load region-->exeution region-->section。

那么在链接文件中的RESET和+First是什么呢?

在芯片的启动文件里有如下代码:

; Vector Table Mapped to Address 0 at Reset

                AREA    RESET, DATA, READONLY
                EXPORT  __Vectors
                EXPORT  __Vectors_End
                EXPORT  __Vectors_Size

__Vectors       DCD     __initial_sp              ; Top of Stack
                DCD     Reset_Handler
                DCD     NMI_Handler
                DCD     HardFault_Handler
                DCD     MemoryManagement_Handler
                DCD     BusFault_Handler
                DCD     UsageFault_Handler

在arm的汇编手册里有这么一句话:Object files produced by the assembler are divided into sections. In assembly source code, you use the AREA directive to mark the start of a section。RESET就是被定义的一个section name,后面的DATA、READONLY是这个section的属性。

+First就是把这个section放在这个exection region的最前面,这是芯片启动之后最先运行到的地方。

section怎么用呢?

1.在rtthread中用使用INIT_BOARD_EXPORT一类的宏把函数定义在指定的section中,实现让rtthread按照顺序调用的功能,有点类似回调函数。

/* rtdef.h */
/* initialization export */
#ifdef RT_USING_COMPONENTS_INIT
typedef int (*init_fn_t)(void);
#ifdef _MSC_VER /* we do not support MS VC++ compiler */
    #define INIT_EXPORT(fn, level)
#else
    #if RT_DEBUG_INIT
        struct rt_init_desc
        {
            const char* fn_name;
            const init_fn_t fn;
        };
        #define INIT_EXPORT(fn, level)                                                       \
            const char __rti_##fn##_name[] = #fn;                                            \
            RT_USED const struct rt_init_desc __rt_init_desc_##fn SECTION(".rti_fn." level) = \
            { __rti_##fn##_name, fn};
    #else
        #define INIT_EXPORT(fn, level)                                                       \
            RT_USED const init_fn_t __rt_init_##fn SECTION(".rti_fn." level) = fn
    #endif
#endif
#else
#define INIT_EXPORT(fn, level)
#endif

/* board init routines will be called in board_init() function */
#define INIT_BOARD_EXPORT(fn)           INIT_EXPORT(fn, "1")

/* pre/device/component/env/app init routines will be called in init_thread */
/* components pre-initialization (pure software initilization) */
#define INIT_PREV_EXPORT(fn)            INIT_EXPORT(fn, "2")
/* device initialization */
#define INIT_DEVICE_EXPORT(fn)          INIT_EXPORT(fn, "3")
/* components initialization (dfs, lwip, ...) */
#define INIT_COMPONENT_EXPORT(fn)       INIT_EXPORT(fn, "4")
/* environment initialization (mount disk, ...) */
#define INIT_ENV_EXPORT(fn)             INIT_EXPORT(fn, "5")
/* appliation initialization (rtgui application etc ...) */
#define INIT_APP_EXPORT(fn)             INIT_EXPORT(fn, "6")

/* components.c */
/*
 * Components Initialization will initialize some driver and components as following
 * order:
 * rti_start         --> 0
 * BOARD_EXPORT      --> 1
 * rti_board_end     --> 1.end
 *
 * DEVICE_EXPORT     --> 2
 * COMPONENT_EXPORT  --> 3
 * FS_EXPORT         --> 4
 * ENV_EXPORT        --> 5
 * APP_EXPORT        --> 6
 *
 * rti_end           --> 6.end
 *
 * These automatically initialization, the driver or component initial function must
 * be defined with:
 * INIT_BOARD_EXPORT(fn);
 * INIT_DEVICE_EXPORT(fn);
 * ...
 * INIT_APP_EXPORT(fn);
 * etc.
 */
static int rti_start(void)
{
    return 0;
}
INIT_EXPORT(rti_start, "0");

static int rti_board_start(void)
{
    return 0;
}
INIT_EXPORT(rti_board_start, "0.end");

static int rti_board_end(void)
{
    return 0;
}
INIT_EXPORT(rti_board_end, "1.end");

static int rti_end(void)
{
    return 0;
}
INIT_EXPORT(rti_end, "6.end");

2.在nrf53832的实现里,使用NRF_SDH_BLE_OBSERVER一类的宏把event handler定义在对应的section内,产生事件之后sdh去调用event handler,实现代码的解耦合。

比如下面的宏展开

// Register a handler for BLE events.
NRF_SDH_BLE_OBSERVER(m_ble_observer, APP_BLE_OBSERVER_PRIO, ble_evt_handler, NULL);

#define NRF_SDH_BLE_OBSERVER(_name, _prio, _handler, _context)                                      \
STATIC_ASSERT(NRF_SDH_BLE_ENABLED, "NRF_SDH_BLE_ENABLED not set!");                                 \
STATIC_ASSERT(_prio < NRF_SDH_BLE_OBSERVER_PRIO_LEVELS, "Priority level unavailable.");             \
NRF_SECTION_SET_ITEM_REGISTER(sdh_ble_observers, _prio, static nrf_sdh_ble_evt_observer_t _name) =  \
{                                                                                                   \
    .handler   = _handler,                                                                          \
    .p_context = _context                                                                           \
}

#if defined(__CC_ARM)
#define NRF_SECTION_ITEM_REGISTER(section_name, section_var) \
    section_var __attribute__ ((section(STRINGIFY(section_name)))) __attribute__((used))

是把函数放在对应的section中了,那rtthread和sdh是怎么知道函数的地址呢?在rtthread中定义了rti_start、rti_end等函数,知道了这些函数的地址,就可以已知的类型对地址自增就可以知道用户定义的函数的地址了。

用地址自增的方法确定函数的地址,前提是要这些section是按照这个顺序排列的。section又不是数组,怎么确定这些section会按照我们想的顺序排列呢?在armlink手册中又下面的描述:

3.3.1 Default section placement
By default, the linker places input sections in a specific order within an execution region.
The sections are placed in the following order:
1. By attribute as follows:
a. Read-only code.
b. Read-only data.
c. Read-write code.
d. Read-write data.
e. Zero-initialized data.
2. By input section name if they have the same attributes. Names are considered to be case-sensitive and are compared in alphabetical order using the ASCII collation sequence for characters.
3. By a tie-breaker if they have the same attributes and section names. By default, it is the order that armlink processes the section. You can override this with the FIRST or LAST execution region attribute.
那就容易理解了,默认情况下section是会按照名称的字母顺序来排列的。再看rtthread中对section名称的定义就清楚了,按照1、2、3...的顺序命名,地址也自然是按照这个顺序排列的。看一下生成的map文件印证一下。

 灵活的运用section,降低代码的耦合度。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值