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

news2024/11/26 7:45:17

启动文件和链接脚本

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等等。对于在同一区域初始化而不同顺序初始化的部分按照编号顺序来进行。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1170725.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

微信小程序 uCharts的使用方法

一、背景 微信小程序项目需要渲染一个柱状图&#xff0c;使用uCharts组件完成 uCharts官网指引&#x1f449;&#xff1a;uCharts官网 - 秋云uCharts跨平台图表库 二、实现效果 三、具体使用 进入官网查看指南&#xff0c;有两种方式进行使用&#xff1a;分别是原生方式与组…

代码随想录 Day37 完全背包理论基础 卡码网T52 LeetCode T518 零钱兑换II T377 组合总和IV

完全背包理论基础 0-1背包理论基础:0-1背包理论基础 完全背包就是在0-1背包的基础上加上了一个条件,0-1背包中每个物品只能选择一次,而在完全背包上一个物品可以选择多次,其实也很简单,只需要修改一部分的代码就可以实现,没了解过0-1背包的友友可以去看我的0-1背包理论基础,下面…

雷池WAF社区版的使用教程

最近听说了一款免费又好用的WAF软件&#xff0c;雷池社区版&#xff0c;体验了一下虽然还有很多改进的空间 但是总体来说很适合小站长使用&#xff0c;和学习使用 也建议所有想学防火墙和红队&#xff08;攻击队&#xff09;练习使用&#xff0c;听说给官网提交绕过还有额外的…

策略模式在数据接收和发送场景的应用

在本篇文章中&#xff0c;我们介绍了策略模式&#xff0c;并在数据接收和发送场景中使用了策略模式。 背景 在最近项目中&#xff0c;需要与外部系统进行数据交互&#xff0c;刚开始交互的系统较为单一&#xff0c;刚开始设计方案时打算使用了if else 进行判断&#xff1a; if(…

uniapp原生插件之安卓文字转拼音原生插件

插件介绍 安卓文字转拼音插件&#xff0c;支持转换为声调模式和非声调模式&#xff0c;支持繁体和简体互相转换 插件地址 安卓文字转拼音原生插件 - DCloud 插件市场 超级福利 uniapp 插件购买超级福利 详细使用文档 uniapp 安卓文字转拼音原生插件 用法 在需要使用插…

新兴初创企业参展招募

一般来说&#xff0c;创业公司的生存率较低&#xff0c;失败率较高。根据不同的数据来源&#xff0c;创业公司的失败率高达 80%-90%。据统计&#xff0c;在中国每年新注册的企业数量超过 100 万家&#xff0c;但能够存活到 5 年以上的企业不足 7%&#xff0c;10 年以上不足 2%。…

Win10系统下查询WiFi强度信息

netsh wlan show networks modebssid 查询周围所有WiFi 可以获取到信号的强度 netsh wlan show interface查询当前网卡连接的wifi 对应的信号强度 具体见图

Hadoop学习总结(Shell操作)

HDFS Shell 参数 命令参数功能描述-ls查看指定路径的目录结构-du统计目录下所有文件大小-mv移动文件-cp复制文件-rm删除文件 / 空白文件夹-put上传文件-cat查看内容文件-text将源文件输出文本格式-mkdir创建空白文件夹-help帮助 一、ls 命令 ls 命令用于查看指定路径的当前目录…

稳定性测试—fastboot和monkey区别

一、什么是稳定性测试 稳定性测试是指检验程序在一定时间内能否稳定地运行&#xff0c;在不同的场景下能否正常地工作的过程。主要目的是检测崩溃、内存泄漏、堆栈错误等缺陷。 二、Monkey 1.什么是Monkey 是一个命令行工具&#xff0c;通常在adb安卓调试运行&#xff0c;模…

智能化审批:低代码平台助力招聘管理进程

都说流程很重要&#xff0c;确实如此。 企业运营中的内部流程是否高效&#xff0c;很大程度上决定了业务能否获得成功。不过&#xff0c;在各项流程中&#xff0c;还有一个重要“角色”不容忽略——审批&#xff0c;它就像是企业版的“开关按钮”&#xff0c;无论是报销、请假…

spss chi-square test

实验卡方检验_chi-square independence-CSDN博客 VAR01类别

前端难学还是后端难学?系统安全,web安全,网络安全是什么区别?

系统安全&#xff0c;web安全&#xff0c;网络安全是什么区别&#xff1f;三无纬度安全问题 系统安全&#xff0c;可以说是电脑软件的安全问题&#xff0c;比如windows经常提示修复漏洞&#xff0c;是一个安全问题 网页安全&#xff0c;网站安全&#xff0c;比如&#xff0c;…

你知道Python、Pycharm、Anaconda 三者之间的关系吗?

哈喽~大家好呀 Python作为深度学习和人工智能学习的热门语言&#xff0c;你知道Python、Pycharm、Anaconda 三者之间的关系吗&#xff1f;学习一门语言&#xff0c;除了学会其简单的语法之外还需要对其进行运行和实现&#xff0c;才能实现和发挥其功能和作用。下面来介绍运行P…

antv/g6元素之combo

介绍 在 G6 中&#xff0c;“Combo” 是一种特殊的元素&#xff0c;用于组合和展示多个节点元素的一种方式。它通常用于表示一个组或子图&#xff0c;将多个相关节点组织在一起&#xff0c;并在图形中以单一的形状显示。 属性 type&#xff1a;Combo 的类型&#xff0c;通常是…

地理信息系统原理-空间数据结构(7)

​四叉树编码 1.四叉树编码定义 四叉树数据结构是一种对栅格数据的压缩编码方法&#xff0c;其基本思想是将一幅栅格数据层或图像等分为四部分&#xff0c;逐块检查其格网属性值&#xff08;或灰度&#xff09;&#xff1b;如果某个子区的所有格网值都具有相同的值&#xff0…

SOLIDWORKS 2024新功能--SOLIDWORKS Electrical篇

SOLIDWORKS Electrical 对齐零部件 在设计 3D 机柜布局时使用对齐零部件时&#xff0c;可以在图形区域中预览更改。这大大减少了在 3D 机柜布局中对齐 SOLIDWORKS 零部件所需的工作量。对齐零部件 PropertyManager 简化并改进了工作流程。 SOLIDWORKS Electrical 更改多个导…

Java 基础知识:面试官必问的问题

本文重点关注Java编程语言的基础知识&#xff0c;并针对求职面试中常见的问题进行了总结。希望帮助读者准备面试&#xff0c;了解常见的Java基础问题 数据类型 基本类型 byte/8char/16short/16int/32float/32long/64double/64boolean/~ boolean 只有两个值&#xff1a;true、…

海外媒体发稿:如何利用8种出口贸易媒体发稿实现销售突破-华媒舍

出口贸易是许多企业追求业务增长的重要途径。在全球市场上突出自己并吸引潜在客户并非易事。幸运的是&#xff0c;利用出口贸易媒体发稿的机会可以成为推动销售突破的有效策略。本文将介绍8种出口贸易媒体以及如何利用它们发稿推广&#xff0c;从而实现销售突破。 1. 行业媒体…

魔术《4 Kings 折纸》的三重境界(三)——群论描述

早点关注我&#xff0c;精彩不错过&#xff01; 前面两篇讲完了基于奇偶性和集合论基础解释《4 Kings 折纸》的方法&#xff0c;详情请戳&#xff1a; 魔术《4 Kings 折纸》的三重境界&#xff08;二&#xff09;——集合语言和数理逻辑 魔术《4 Kings 折纸》的三重境界&#x…

overleaf里插入中文语句

作业要求是需要插入中文 我直接插入中文生成pdf会报错&#xff1a; 解决办法&#xff1a; overleaf官网里提供了教程&#xff1a;https://www.overleaf.com/learn/latex/Chinese 使用XeLaTeX或者LuaLaTeX进行编译是支持UTF-8编码。所以改变编译器的步骤如下&#xff1a; 点击…