Rtthread源码分析<1>启动文件和链接脚本

启动文件和链接脚本

1)启动文件

​ 启动文件里面使用的是汇编语言,汇编语言常常可以分为两个部分语法风格和而不同的toolchain有不同的汇编语法风格,通常分配unified 和 非 unified。常见的工具包有 ARM toolchains 和 GNU toolchains 。比如 keil中使用的就是 ARM toolchains 也就是 MDK-ARM,而在一些开源的平台比如espidf,platform,rtthread-studio, stm32cube-ide等等使用的是开源的工具包 ARM tool chains。

/**
  ******************************************************************************
  * @file      startup_stm32f407xx.s
  * @author    MCD Application Team
  * @brief     STM32F407xx Devices vector table for GCC based toolchains. 
  *            This module performs:
  *                - Set the initial SP
  *                - Set the initial PC == Reset_Handler,
  *                - Set the vector table entries with the exceptions ISR address
  *                - Branches to main in the C library (which eventually
  *                  calls main()).
  *            After Reset the Cortex-M4 processor is in Thread mode,
  *            priority is Privileged, and the Stack is set to Main.
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; COPYRIGHT 2017 STMicroelectronics</center></h2>
  *
  * Redistribution and use in source and binary forms, with or without modification,
  * are permitted provided that the following conditions are met:
  *   1. Redistributions of source code must retain the above copyright notice,
  *      this list of conditions and the following disclaimer.
  *   2. Redistributions in binary form must reproduce the above copyright notice,
  *      this list of conditions and the following disclaimer in the documentation
  *      and/or other materials provided with the distribution.
  *   3. Neither the name of STMicroelectronics nor the names of its contributors
  *      may be used to endorse or promote products derived from this software
  *      without specific prior written permission.
  *
  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
  * 通过查阅Cortex M4内核手册 的指令集,
  ******************************************************************************
  */
  // unified assembler language 简称为UAL 是为了兼容THUMB 和 ARM两种指令集而产生的一种统一汇编语言
  // 汇编语言分为(pre-unified 和 unified),pre-unified指的是unified之前的汇编语法风格。
  // 所需的编译语言的语法取决于所选择的开发工具,keil 中 MDK-ARM 同时支持上述两种语法。
  // 所以启动文件的编写与开发平台有关 
  // 但是指令集与处理器的架构有关
  // 所以汇编语言分为:语法风格和指令集两个部分

  .syntax unified
  .cpu cortex-m4
  .fpu softvfp
  .thumb
/*定义一个全局性的符号 */
.global  g_pfnVectors
.global  Default_Handler

/* start address for the initialization values of the .data section. 
defined in linker script */
.word  _sidata // 定义一个字的变量 , 这个变量的具体值在链接脚本之中

/* start address for the .data section. defined in linker script */  
.word  _sdata
/* end address for the .data section. defined in linker script */
.word  _edata

/* start address for the .bss section. defined in linker script */
.word  _sbss
/* end address for the .bss section. defined in linker script */
.word  _ebss

/* stack used for SystemInit_ExtMemCtl; always internal RAM used */

/**
 * @brief  This is the code that gets called when the processor first
 *          starts execution following a reset event. Only the absolutely
 *          necessary set is performed, after which the application
 *          supplied main() routine is called. 
 * @param  None
 * @retval : None
 * 程序刚上电时调用的第一个程序,也就是复位
*/
      .section  .text.Reset_Handler
  /*这里的weak 类似于c语言中的weak 如果c中没有定义,那么这里默认的Reset_Handler将被调用 */
  .weak  Reset_Handler
  .type  Reset_Handler, %function
Reset_Handler:  
  ldr   sp, =_estack     /* set stack pointer */

/* Copy the data segment initializers from flash to SRAM */  
/*
    MEMORY
    ROM (rx) : ORIGIN = 0x08000000, LENGTH =  1024k / 1024K flash /
    RAM (rw) : ORIGIN = 0x20000000, LENGTH =  128k  / 128K sram /
    
 */
  movs  r1, #0
  b  LoopCopyDataInit /* 无条件跳转到对应的标签 */

/* 数据段初始化,将代码段中的rw-data和zi-data段加载到sram中 为程序运行做准备 */
CopyDataInit:
  ldr  r3, =_sidata
  ldr  r3, [r3, r1]   // 加载地址 [r3+r1] 中的数据到 r3寄存器
  str  r3, [r0, r1]   // 将寄存器r3的值 存储到[r0+r1]地址中
  adds  r1, r1, #4    // r1 移动4个字节,也就是32bits
    
LoopCopyDataInit:
  ldr  r0, =_sdata
  ldr  r3, =_edata
  adds  r2, r0, r1 /* r2 = r0 + r1 三操作数是在这种汇编语法风格下才有的 */
  cmp  r2, r3
  bcc  CopyDataInit
  
/*初始化bss段中的 静态变量和全局变量,也就是全为0 */
  ldr  r2, =_sbss
  b  LoopFillZerobss
/* Zero fill the bss segment. */  
FillZerobss:
  movs  r3, #0
  str  r3, [r2], #4
    
LoopFillZerobss:
  ldr  r3, = _ebss
  cmp  r2, r3
  bcc  FillZerobss


/* Call the clock system intitialization function.*/
  bl  SystemInit   
/* Call static constructors */
    /* bl __libc_init_array */
/* Call the application's entry point.
  * 这里调用的Entry是 c语言的入口 进入这个函数之后就是c语言了
*/
  bl  entry
  /* main 函数结束后 如果没有死循环 就会运行到这里 */
  bx  lr    
.size  Reset_Handler, .-Reset_Handler // 告诉连接器的位置

/**
 * @brief  This is the code that gets called when the processor receives an 
 *         unexpected interrupt.  This simply enters an infinite loop, preserving
 *         the system state for examination by a debugger.
 * @param  None     
 * @retval None       
*/
    .section  .text.Default_Handler,"ax",%progbits
Default_Handler:
Infinite_Loop:
  b  Infinite_Loop
  .size  Default_Handler, .-Default_Handler


/******************************************************************************
*
* The minimal vector table for a Cortex M3. Note that the proper constructs
* must be placed on this to ensure that it ends up at physical address
* 0x0000.0000.
* 在指定的内存区域定义中断向量表
*******************************************************************************/
   .section  .isr_vector,"a",%progbits
  .type  g_pfnVectors, %object
  .size  g_pfnVectors, .-g_pfnVectors
    
    
g_pfnVectors:
  .word  _estack
  .word  Reset_Handler
  .word  NMI_Handler
  .word  HardFault_Handler
  .word  MemManage_Handler
  .word  BusFault_Handler
  .word  UsageFault_Handler
  .word  0
  .word  0
  .word  0
  .word  0
  .word  SVC_Handler
  .word  DebugMon_Handler
  .word  0
  .word  PendSV_Handler
  .word  SysTick_Handler
  
  /* External Interrupts */
  .word     WWDG_IRQHandler                   /* Window WatchDog              */                                        
  .word     PVD_IRQHandler                    /* PVD through EXTI Line detection */                        
  .word     TAMP_STAMP_IRQHandler             /* Tamper and TimeStamps through the EXTI line */            
  .word     RTC_WKUP_IRQHandler               /* RTC Wakeup through the EXTI line */                      
  .word     FLASH_IRQHandler                  /* FLASH                        */                                          
  .word     RCC_IRQHandler                    /* RCC                          */                                            
  .word     EXTI0_IRQHandler                  /* EXTI Line0                   */                        
  .word     EXTI1_IRQHandler                  /* EXTI Line1                   */                          
  .word     EXTI2_IRQHandler                  /* EXTI Line2                   */                          
  .word     EXTI3_IRQHandler                  /* EXTI Line3                   */                          
  .word     EXTI4_IRQHandler                  /* EXTI Line4                   */                          
  .word     DMA1_Stream0_IRQHandler           /* DMA1 Stream 0                */                  
  .word     DMA1_Stream1_IRQHandler           /* DMA1 Stream 1                */                   
  .word     DMA1_Stream2_IRQHandler           /* DMA1 Stream 2                */                   
  .word     DMA1_Stream3_IRQHandler           /* DMA1 Stream 3                */                   
  .word     DMA1_Stream4_IRQHandler           /* DMA1 Stream 4                */                   
  .word     DMA1_Stream5_IRQHandler           /* DMA1 Stream 5                */                   
  .word     DMA1_Stream6_IRQHandler           /* DMA1 Stream 6                */                   
  .word     ADC_IRQHandler                    /* ADC1, ADC2 and ADC3s         */                   
  .word     CAN1_TX_IRQHandler                /* CAN1 TX                      */                         
  .word     CAN1_RX0_IRQHandler               /* CAN1 RX0                     */                          
  .word     CAN1_RX1_IRQHandler               /* CAN1 RX1                     */                          
  .word     CAN1_SCE_IRQHandler               /* CAN1 SCE                     */                          
  .word     EXTI9_5_IRQHandler                /* External Line[9:5]s          */                          
  .word     TIM1_BRK_TIM9_IRQHandler          /* TIM1 Break and TIM9          */         
  .word     TIM1_UP_TIM10_IRQHandler          /* TIM1 Update and TIM10        */         
  .word     TIM1_TRG_COM_TIM11_IRQHandler     /* TIM1 Trigger and Commutation and TIM11 */
  .word     TIM1_CC_IRQHandler                /* TIM1 Capture Compare         */                          
  .word     TIM2_IRQHandler                   /* TIM2                         */                   
  .word     TIM3_IRQHandler                   /* TIM3                         */                   
  .word     TIM4_IRQHandler                   /* TIM4                         */                   
  .word     I2C1_EV_IRQHandler                /* I2C1 Event                   */                          
  .word     I2C1_ER_IRQHandler                /* I2C1 Error                   */                          
  .word     I2C2_EV_IRQHandler                /* I2C2 Event                   */                          
  .word     I2C2_ER_IRQHandler                /* I2C2 Error                   */                            
  .word     SPI1_IRQHandler                   /* SPI1                         */                   
  .word     SPI2_IRQHandler                   /* SPI2                         */                   
  .word     USART1_IRQHandler                 /* USART1                       */                   
  .word     USART2_IRQHandler                 /* USART2                       */                   
  .word     USART3_IRQHandler                 /* USART3                       */                   
  .word     EXTI15_10_IRQHandler              /* External Line[15:10]s        */                          
  .word     RTC_Alarm_IRQHandler              /* RTC Alarm (A and B) through EXTI Line */                 
  .word     OTG_FS_WKUP_IRQHandler            /* USB OTG FS Wakeup through EXTI line */                       
  .word     TIM8_BRK_TIM12_IRQHandler         /* TIM8 Break and TIM12         */         
  .word     TIM8_UP_TIM13_IRQHandler          /* TIM8 Update and TIM13        */         
  .word     TIM8_TRG_COM_TIM14_IRQHandler     /* TIM8 Trigger and Commutation and TIM14 */
  .word     TIM8_CC_IRQHandler                /* TIM8 Capture Compare         */                          
  .word     DMA1_Stream7_IRQHandler           /* DMA1 Stream7                 */                          
  .word     FSMC_IRQHandler                   /* FSMC                         */                   
  .word     SDIO_IRQHandler                   /* SDIO                         */                   
  .word     TIM5_IRQHandler                   /* TIM5                         */                   
  .word     SPI3_IRQHandler                   /* SPI3                         */                   
  .word     UART4_IRQHandler                  /* UART4                        */                   
  .word     UART5_IRQHandler                  /* UART5                        */                   
  .word     TIM6_DAC_IRQHandler               /* TIM6 and DAC1&2 underrun errors */                   
  .word     TIM7_IRQHandler                   /* TIM7                         */
  .word     DMA2_Stream0_IRQHandler           /* DMA2 Stream 0                */                   
  .word     DMA2_Stream1_IRQHandler           /* DMA2 Stream 1                */                   
  .word     DMA2_Stream2_IRQHandler           /* DMA2 Stream 2                */                   
  .word     DMA2_Stream3_IRQHandler           /* DMA2 Stream 3                */                   
  .word     DMA2_Stream4_IRQHandler           /* DMA2 Stream 4                */                   
  .word     ETH_IRQHandler                    /* Ethernet                     */                   
  .word     ETH_WKUP_IRQHandler               /* Ethernet Wakeup through EXTI line */                     
  .word     CAN2_TX_IRQHandler                /* CAN2 TX                      */                          
  .word     CAN2_RX0_IRQHandler               /* CAN2 RX0                     */                          
  .word     CAN2_RX1_IRQHandler               /* CAN2 RX1                     */                          
  .word     CAN2_SCE_IRQHandler               /* CAN2 SCE                     */                          
  .word     OTG_FS_IRQHandler                 /* USB OTG FS                   */                   
  .word     DMA2_Stream5_IRQHandler           /* DMA2 Stream 5                */                   
  .word     DMA2_Stream6_IRQHandler           /* DMA2 Stream 6                */                   
  .word     DMA2_Stream7_IRQHandler           /* DMA2 Stream 7                */                   
  .word     USART6_IRQHandler                 /* USART6                       */                    
  .word     I2C3_EV_IRQHandler                /* I2C3 event                   */                          
  .word     I2C3_ER_IRQHandler                /* I2C3 error                   */                          
  .word     OTG_HS_EP1_OUT_IRQHandler         /* USB OTG HS End Point 1 Out   */                   
  .word     OTG_HS_EP1_IN_IRQHandler          /* USB OTG HS End Point 1 In    */                   
  .word     OTG_HS_WKUP_IRQHandler            /* USB OTG HS Wakeup through EXTI */                         
  .word     OTG_HS_IRQHandler                 /* USB OTG HS                   */                   
  .word     DCMI_IRQHandler                   /* DCMI                         */                   
  .word     0                                 /* CRYP crypto                  */                   
  .word     HASH_RNG_IRQHandler               /* Hash and Rng                 */
  .word     FPU_IRQHandler                    /* FPU                          */
                         
                         
/*******************************************************************************
*
* Provide weak aliases for each Exception handler to the Default_Handler. 
* As they are weak aliases, any function with the same name will override 
* this definition.
* 
*******************************************************************************/
   .weak      NMI_Handler
   .thumb_set NMI_Handler,Default_Handler
    .........
    .........
                                   
   .weak      HASH_RNG_IRQHandler                  
   .thumb_set HASH_RNG_IRQHandler,Default_Handler   

   .weak      FPU_IRQHandler                  
   .thumb_set FPU_IRQHandler,Default_Handler  

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

2)链接脚本

​ 链接脚本的语法通常是特定于链接器的,不同的链接器可能具有不同的语法。在嵌入式开发中,常见的链接器包括GNU ld(GNU链接器)和Keil MDK等,它们具有不同的链接脚本语法。

​ 链接脚本的目的是确保生成的可执行文件在目标设备上正确运行,包括正确加载代码、数据和堆栈,并保证程序的入口点正确。链接脚本是嵌入式系统中非常重要的一部分,它确保程序能够有效地利用设备的存储器资源。不同的嵌入式开发工具和平台可能有不同的链接脚本语法,因此需要根据具体工具和目标设备来编写或配置链接脚本。

/*
 * linker script for STM32F407VG with GNU ld
 */

/* Program Entry, set to mark it as "used" and avoid gc */
MEMORY
{
    ROM (rx) : ORIGIN = 0x08000000, LENGTH =  1024k /* 1024K flash */
    RAM (rw) : ORIGIN = 0x20000000, LENGTH =  128k /* 128K sram */
}
/* 指定c程序运行的第一个函数 从Reset_Handler开始运行  即会加载到单片机的启动地址 0x8000 0000上*/
ENTRY(Reset_Handler)
_system_stack_size = 0x400;

SECTIONS
{
    /*代码段*/
    .text :
    {
        . = ALIGN(4);
        _stext = .;
        KEEP(*(.isr_vector))            /* Startup code */

        . = ALIGN(4);
        *(.text)                        /* remaining code */
        *(.text.*)                      /* remaining code */
        *(.rodata)                      /* read-only data (constants) */
        *(.rodata*)
        *(.glue_7)
        *(.glue_7t)
        *(.gnu.linkonce.t*)

        /* section information for finsh shell */
        . = ALIGN(4);
        __fsymtab_start = .;
        KEEP(*(FSymTab))
        __fsymtab_end = .;

        . = ALIGN(4);
        __vsymtab_start = .;
        KEEP(*(VSymTab))
        __vsymtab_end = .;

        /* section information for utest */
        . = ALIGN(4);
        __rt_utest_tc_tab_start = .;
        KEEP(*(UtestTcTab))
        __rt_utest_tc_tab_end = .;

        /* section information for at server */
        . = ALIGN(4);
        __rtatcmdtab_start = .;
        KEEP(*(RtAtCmdTab))
        __rtatcmdtab_end = .;
        . = ALIGN(4);

        /* section information for initial. 
            通过宏定义实现的系统初始化 !!!! 
            在c语言中 以 rti_fn 开头的宏定义
        */
        . = ALIGN(4);
        __rt_init_start = .;
        KEEP(*(SORT(.rti_fn*)))
        __rt_init_end = .;

        . = ALIGN(4);

        /*__ctors_start__ 分配了 C++ 全局构造函数段的起始地址, __ctors_end__ 分配了 C++ 全局构造函数段的结束地址*/
        PROVIDE(__ctors_start__ = .);
        KEEP (*(SORT(.init_array.*)))
        KEEP (*(.init_array))
        PROVIDE(__ctors_end__ = .);

        . = ALIGN(4);

        _etext = .; // _etext 存放代码段text的结束地址。
    } > ROM = 0

    /*异常*/
    /* .ARM.exidx is sorted, so has to go in its own output section.  */
    __exidx_start = .;
    .ARM.exidx :
    {
        /* *表示 通配符 表示以.ARM.exidx 或 .gnu.linkonce.armexidx 开头的段名称 */
        *(.ARM.exidx* .gnu.linkonce.armexidx.*)

        /* This is used by the startup in order to initialize the .data secion */
        _sidata = .;
    } > ROM
    __exidx_end = .;

    /* .data section which is used for initialized data */

    /*数据段*/
    .data : AT (_sidata)    // 使用 AT 指定.data段的起始地址为 _sidata 标识符所表示的地址
    {
        . = ALIGN(4);
        /* This is used by the startup in order to initialize the .data secion */
        _sdata = . ;

        *(.data)
        *(.data.*)
        *(.gnu.linkonce.d*)

    /*
        给全局析构函数分配的段
        在 C++ 程序中,析构函数用于对象的清理和资源释放。这些函数的指针通常存储在特定的段中,以便在程序退出时执行这些析构函数,
        确保资源的正确释放。这段链接脚本的作用是将析构函数相关信息正确地放置在存储器中,以便在程序退出时执行这些析构函数

    */
        PROVIDE(__dtors_start__ = .);
        KEEP(*(SORT(.dtors.*)))
        KEEP(*(.dtors))
        PROVIDE(__dtors_end__ = .);

        . = ALIGN(4);
        /* This is used by the startup in order to initialize the .data secion */
        _edata = . ;
    } >RAM

    /*堆栈段*/
    .stack : 
    {
        . = ALIGN(4);
        _sstack = .;
        . = . + _system_stack_size; // 即之前设置的系统堆栈段大小
        . = ALIGN(4);
        _estack = .;
    } >RAM

    /*全局变量和静态变量初始化为0的段*/
    __bss_start = .;
    .bss :
    {
        . = ALIGN(4);
        /* This is used by the startup in order to initialize the .bss secion */
        _sbss = .;

        *(.bss)
        *(.bss.*)
        *(COMMON)

        . = ALIGN(4);
        /* This is used by the startup in order to initialize the .bss secion */
        _ebss = . ;
        
        *(.bss.init)
    } > RAM
    __bss_end = .;

    _end = .;

    /* Stabs debugging sections.  */
    .stab          0 : { *(.stab) }
    .stabstr       0 : { *(.stabstr) }
    .stab.excl     0 : { *(.stab.excl) }
    .stab.exclstr  0 : { *(.stab.exclstr) }
    .stab.index    0 : { *(.stab.index) }
    .stab.indexstr 0 : { *(.stab.indexstr) }
    .comment       0 : { *(.comment) }
    /* DWARF debug sections.
     * Symbols in the DWARF debugging sections are relative to the beginning
     * of the section so we begin them at 0.  */
    /* DWARF 1 */
    .debug          0 : { *(.debug) }
    .line           0 : { *(.line) }
    /* GNU DWARF 1 extensions */
    .debug_srcinfo  0 : { *(.debug_srcinfo) }
    .debug_sfnames  0 : { *(.debug_sfnames) }
    /* DWARF 1.1 and DWARF 2 */
    .debug_aranges  0 : { *(.debug_aranges) }
    .debug_pubnames 0 : { *(.debug_pubnames) }
    /* DWARF 2 */
    .debug_info     0 : { *(.debug_info .gnu.linkonce.wi.*) }
    .debug_abbrev   0 : { *(.debug_abbrev) }
    .debug_line     0 : { *(.debug_line) }
    .debug_frame    0 : { *(.debug_frame) }
    .debug_str      0 : { *(.debug_str) }
    .debug_loc      0 : { *(.debug_loc) }
    .debug_macinfo  0 : { *(.debug_macinfo) }
    /* SGI/MIPS DWARF 2 extensions */
    .debug_weaknames 0 : { *(.debug_weaknames) }
    .debug_funcnames 0 : { *(.debug_funcnames) }
    .debug_typenames 0 : { *(.debug_typenames) }
    .debug_varnames  0 : { *(.debug_varnames) }
}

使用链接脚本+宏函数实现函数的初始化

rtthread_startup.png

在rtthread的启动流程当中,其系统初始化模块可以通过一个宏定义

/* in file rtdef.h	*/
typedef int (*init_fn_t)(void);

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};

整体来看其中INIT_EXPORT时一个宏函数,fn为函数名,level为初始化优先级,数字越小优先级越高。

const char __rti_##fn##_name[] = #fn;

##fn## 表示 宏定义中字符串的连接,#fn 表示 获取其变量名的字符串 比如函数名为 my_function,那么上面一条语句将被翻译为

const char __rti_my_function_name[] = "my_function";

这意味在使用这个宏函数的地方定义了一个字符串常量 __rti_my_function_name 内容为 “my_function”。下面一句语句主要是定义了一个结构体变量,记录了要用于初始化的函数指针和函数名。

RT_USED const struct rt_init_desc __rt_init_desc_##fn SECTION(".rti_fn." level) = \
            { __rti_##fn##_name, fn};

如果传入的参数为fn为 my_function 上述语句将被翻译为:

RT_USED const struct rt_init_desc __rt_init_desc_myfunction SECTION(".rti_fn." level) = \
            { __rti_myfunction_name, fn};

意味着还定义了另一个结构体变量__rt_init_desc_myfunction,SECTION(“.rti_fn.” level) 的作用是确保初始化函数描述信息结构体被放置在与初始化函数描述信息相关的段中,以便在程序启动时正确执行这些初始化函数。具体的段名称和放置位置通常由链接脚本中的规则和约定确定。链接脚本会指定哪些数据应该放在哪些段中,以确保数据在内存中的正确位置。

总的来说上述宏函数干了两件事情,定义了两个变量一个是 ___rti_my_function_name 表示以字符串的形式存储函数名称,一个是结构体变量_____rt_init_desc_myfunction存储了函数的名字和其函数指针。

在链接脚本的代码段定义中有如下代码:

        . = ALIGN(4);
        __rt_init_start = .;
        KEEP(*(SORT(.rti_fn*)))
        __rt_init_end = .;

KEEP(*(SORT(.rti_fn*))) 中的 SORT 是链接脚本中的一个命令,它用于对输入节(sections)进行排序。在这个上下文中,.rti_fn* 是一个通配符,用于匹配以 .rti_fn 开头的节名称。

具体来说,这一行的作用是:

  1. 匹配以 .rti_fn 开头的所有节(sections),这些节通常用于存储初始化函数的描述信息。
  2. 使用 SORT 命令对这些匹配的节进行排序。

排序的目的是确保初始化函数按特定顺序执行,以便满足程序的初始化要求。通常,初始化函数的执行顺序可能会对程序的正确性产生影响,因此需要确保它们按照正确的顺序执行。

SORT 命令可以按字母顺序对节进行排序,或者按照链接脚本中的规则来排序。这可以确保初始化函数按照预定义的顺序执行,以满足程序的要求。

综上所述,SORT 命令用于链接脚本中对初始化函数描述信息的节进行排序,以确保初始化函数按照正确的顺序执行,排序的依据也就是在c语言的宏函数的level。也就是说SORT会按照在c语言中section函数中传入的参数对其进行排序,然后按照排好的顺序将其存储在对应的代码段中。但此时仅仅完成了不同级别需要初始化函数的存储位置,还没有真正的进行初始化。以 INIT_BOARD_EXPORT(fn)为例,由官方给出的系统初始化的流程图可知。在函数rt_components_board_init中对其导入的函数进行初始化。

void rt_components_board_init(void)
{
    int result;
    const struct rt_init_desc *desc;
    for (desc = &__rt_init_desc_rti_board_start; desc < &__rt_init_desc_rti_board_end; desc ++)
    {
        rt_kprintf("initialize %s", desc->fn_name);
        result = desc->fn();
        rt_kprintf(":%d done\n", result);
    }
}

接下来是最奇妙的地方,可见上述代码中存在 ____rt_init_desc_rti_board_start 和 ____rt_init_desc_rti_board_end两个变量,这两个变量也是通过INIT_EXPORT宏函数进行导入的。但是最重要的是其level值。见如下代码:

static int rti_start(void)
{
    return 0;
}
/* 相当于一个地址的界定符,用于区分不同level的函数 用于for循环的比较*/
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");

通过上述代码定义了如下两个变量

RT_USED const struct rt_init_desc __rt_init_desc_rti_board_start;
RT_USED const struct rt_init_desc __rt_init_desc_rti_board_end;

另外我们查看代码

/* 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")

可知所有的rt_components_board_init后板层需要初始化的函数的level都是1,所以根据链接文件的内存管理,会按照如下顺序进行分配空间:

image-20231104132859064

其实,总的来说系统初始化的过程就是对内存空间的操作,利用对需要初始化的函数进行编号,然后通过链接脚本按照编号分配至连续的内存空间,其中对于需要在不同区域初始化的部分在内存中打标签也就是1.end,0.end,6.end等等。对于在同一区域初始化而不同顺序初始化的部分按照编号顺序来进行。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值