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

2023-11-05 01:36

本文主要是介绍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 */  
/*MEMORYROM (rx) : ORIGIN = 0x08000000, LENGTH =  1024k / 1024K flash /RAM (rw) : ORIGIN = 0x20000000, LENGTH =  128k  / 128K sram /*/movs  r1, #0b  LoopCopyDataInit /* 无条件跳转到对应的标签 *//* 数据段初始化,将代码段中的rw-data和zi-data段加载到sram中 为程序运行做准备 */
CopyDataInit:ldr  r3, =_sidataldr  r3, [r3, r1]   // 加载地址 [r3+r1] 中的数据到 r3寄存器str  r3, [r0, r1]   // 将寄存器r3的值 存储到[r0+r1]地址中adds  r1, r1, #4    // r1 移动4个字节,也就是32bitsLoopCopyDataInit:ldr  r0, =_sdataldr  r3, =_edataadds  r2, r0, r1 /* r2 = r0 + r1 三操作数是在这种汇编语法风格下才有的 */cmp  r2, r3bcc  CopyDataInit/*初始化bss段中的 静态变量和全局变量,也就是全为0 */ldr  r2, =_sbssb  LoopFillZerobss
/* Zero fill the bss segment. */  
FillZerobss:movs  r3, #0str  r3, [r2], #4LoopFillZerobss:ldr  r3, = _ebsscmp  r2, r3bcc  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_pfnVectorsg_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等等。对于在同一区域初始化而不同顺序初始化的部分按照编号顺序来进行。

这篇关于Rtthread源码分析<1>启动文件和链接脚本的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/346518

相关文章

性能分析之MySQL索引实战案例

文章目录 一、前言二、准备三、MySQL索引优化四、MySQL 索引知识回顾五、总结 一、前言 在上一讲性能工具之 JProfiler 简单登录案例分析实战中已经发现SQL没有建立索引问题,本文将一起从代码层去分析为什么没有建立索引? 开源ERP项目地址:https://gitee.com/jishenghua/JSH_ERP 二、准备 打开IDEA找到登录请求资源路径位置

MySQL数据库宕机,启动不起来,教你一招搞定!

作者介绍:老苏,10余年DBA工作运维经验,擅长Oracle、MySQL、PG、Mongodb数据库运维(如安装迁移,性能优化、故障应急处理等)公众号:老苏畅谈运维欢迎关注本人公众号,更多精彩与您分享。 MySQL数据库宕机,数据页损坏问题,启动不起来,该如何排查和解决,本文将为你说明具体的排查过程。 查看MySQL error日志 查看 MySQL error日志,排查哪个表(表空间

JAVA智听未来一站式有声阅读平台听书系统小程序源码

智听未来,一站式有声阅读平台听书系统 🌟&nbsp;开篇:遇见未来,从“智听”开始 在这个快节奏的时代,你是否渴望在忙碌的间隙,找到一片属于自己的宁静角落?是否梦想着能随时随地,沉浸在知识的海洋,或是故事的奇幻世界里?今天,就让我带你一起探索“智听未来”——这一站式有声阅读平台听书系统,它正悄悄改变着我们的阅读方式,让未来触手可及! 📚&nbsp;第一站:海量资源,应有尽有 走进“智听

springboot3打包成war包,用tomcat8启动

1、在pom中,将打包类型改为war <packaging>war</packaging> 2、pom中排除SpringBoot内置的Tomcat容器并添加Tomcat依赖,用于编译和测试,         *依赖时一定设置 scope 为 provided (相当于 tomcat 依赖只在本地运行和测试的时候有效,         打包的时候会排除这个依赖)<scope>provided

内核启动时减少log的方式

内核引导选项 内核引导选项大体上可以分为两类:一类与设备无关、另一类与设备有关。与设备有关的引导选项多如牛毛,需要你自己阅读内核中的相应驱动程序源码以获取其能够接受的引导选项。比如,如果你想知道可以向 AHA1542 SCSI 驱动程序传递哪些引导选项,那么就查看 drivers/scsi/aha1542.c 文件,一般在前面 100 行注释里就可以找到所接受的引导选项说明。大多数选项是通过"_

安卓链接正常显示,ios#符被转义%23导致链接访问404

原因分析: url中含有特殊字符 中文未编码 都有可能导致URL转换失败,所以需要对url编码处理  如下: guard let allowUrl = webUrl.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) else {return} 后面发现当url中有#号时,会被误伤转义为%23,导致链接无法访问

Java ArrayList扩容机制 (源码解读)

结论:初始长度为10,若所需长度小于1.5倍原长度,则按照1.5倍扩容。若不够用则按照所需长度扩容。 一. 明确类内部重要变量含义         1:数组默认长度         2:这是一个共享的空数组实例,用于明确创建长度为0时的ArrayList ,比如通过 new ArrayList<>(0),ArrayList 内部的数组 elementData 会指向这个 EMPTY_EL

如何在Visual Studio中调试.NET源码

今天偶然在看别人代码时,发现在他的代码里使用了Any判断List<T>是否为空。 我一般的做法是先判断是否为null,再判断Count。 看了一下Count的源码如下: 1 [__DynamicallyInvokable]2 public int Count3 {4 [__DynamicallyInvokable]5 get

用命令行的方式启动.netcore webapi

用命令行的方式启动.netcore web项目 进入指定的项目文件夹,比如我发布后的代码放在下面文件夹中 在此地址栏中输入“cmd”,打开命令提示符,进入到发布代码目录 命令行启动.netcore项目的命令为:  dotnet 项目启动文件.dll --urls="http://*:对外端口" --ip="本机ip" --port=项目内部端口 例: dotnet Imagine.M

SWAP作物生长模型安装教程、数据制备、敏感性分析、气候变化影响、R模型敏感性分析与贝叶斯优化、Fortran源代码分析、气候数据降尺度与变化影响分析

查看原文>>>全流程SWAP农业模型数据制备、敏感性分析及气候变化影响实践技术应用 SWAP模型是由荷兰瓦赫宁根大学开发的先进农作物模型,它综合考虑了土壤-水分-大气以及植被间的相互作用;是一种描述作物生长过程的一种机理性作物生长模型。它不但运用Richard方程,使其能够精确的模拟土壤中水分的运动,而且耦合了WOFOST作物模型使作物的生长描述更为科学。 本文让更多的科研人员和农业工作者