CM0+启动文件及ld文件分析

一、.S文件的作用

启动文件.s文件的作用:
设置堆栈指针SP
将程序计数器指针PC 指向 Reset_Handler
设置中断向量表的对应中断地址

配置时钟系统(之前还以为GPIO不需要时钟,原来是在启动文件就已经有了默认时钟)
设置程序入口函数为main
个人理解:在进入main函数前所进行的操作,主要是初始化SP与PC指针,以及中断向量表。之前刚接触bootloader以为这一块也需要自己去写。

二、.S文件代码解析

(当然汇编语言注释可不是//,而是;)

第一段代码:

  .syntax unified	// 意思是下面的指令是ARM和THUMB通用格式的 
  .cpu cortex-m0plus//CPU内核型号
  .fpu softvfp//选择使用的浮点单元,与-mfpu命令行选项的相同
  .thumb// 选择使用thumb指令

第二段

//global使得可以如下函数被其他目标文件使用
.global  g_pfnVectors    //在文件末尾定义的中断向量
.global  Default_Handler//是一个死循环,用来处理异常情况
.global __Vectors  

第三段

//指示data bss段的起始位置,赋值在ld script文件中
.word  _sidata//初始值地址  放置一个标识data段在RAM中开始地址的word型内存位置

//.data对应初始化了的全局变量,编译后将位于可执行文件中,由启动代码负责加载到数据区中(在单片机中这部分数据会存于flash中,
//需要由启动代码把这部分内容拷贝到ram中
.word  _sdata//.data开始地址
.word  _edata//.data块的结束地址
//.bss块的起始地址,.bss段是没有初始值的全局变量,由启动代码把这 部分内容全初始化为0 
.word  _sbss
.word  _ebss//.bss块的结束地址

第四段,定义Reset_Handler函数

这是处理器在重置事件后首次开始执行时调用的代码。只执行绝对必要的设置,然后调用应用程序提供的main()例程。

/*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.*/
//定义Reset_Handler函数,该函数的作用1.设置堆栈指针;2.全局变量的初始化
//这个text.Reset_Handler很有迷惑性,它就是一个段名,在ld script中有 *(.text*)
 .section  .text.Reset_Handler//定义了一个代码段,名字叫 .Reset_Handler
 .weak  Reset_Handler//弱定义一个符号,名字叫 Reset_Handler
//声明  Reset_Handler是一个函数类型 (同时表示段以  Reset_Handler符号位开始)
 .type  Reset_Handler, %function
Reset_Handler:  //标记 Reset_Handler符号位置
/* Copy the data segment initializers from flash to SRAM */  
// ldr   sp, =_estack      /* set stack pointer */   /*设置栈指针*/

//将data段从rom中拷贝到RAM中
  movs  r1, #0//将r1初始化为0
  b  LoopCopyDataInit

CopyDataInit:
  ldr  r3, =_sidata//使用ldr伪指令将初始数据地址加载到r3中 
  ldr  r3, [r3, r1]//寄存器间接寻址,将r3 + r1地址中的数据加载到r3中
  str  r3, [r0, r1]//将r3的内容写入 r0 + r1地址中 
  adds  r1, r1, #4//后变址,将r1地址中的内容写入r1,然后令r1 + 4
    
LoopCopyDataInit:
  ldr  r0, =_sdata//使用ldr伪指令,在r0中写入.data的起始地址
  ldr  r3, =_edata//在r3中写入.data的末尾地址
  adds  r2, r0, r1//r2 = r0 + r1 ,操作影响条件标志位 
  cmp  r2, r3//比较r2和r3寄存器中的地址大小
  bcc  CopyDataInit//如果r2 < r3,也就是还没有到达data数据段的末尾,则转跳CopyDataInit 
  // 因为汇编语言顺序执行,上面代码会循环执行,直到复制到.data数据段结束 
  ldr  r2, =_sbss// r2中存储.bss数据区的首地址
  b  LoopFillZerobss
/* Zero fill the bss segment. */  
//将bss段RAM清零
FillZerobss:
  movs  r3, #0//将寄存器r3写0
  str  r3, [r2]
  movs  r3, #4
  add r2, r2, r3
    
LoopFillZerobss:
  ldr  r3, = _ebss
  cmp  r2, r3
  bcc  FillZerobss

/* Call the clock system intitialization function.*/
//进入用户程序准备阶段
  bl  SystemInit   
  
  ldr     R0, =0x20080014
  ldr     R1, =0xFFFF
  strh    R1, [R0]

  ldr     R0, =0x20080044
  ldr     R1, =0xFFFF
  strh    R1, [R0]

  ldr     R0, =0x20080074
  ldr     R1, =0x3FF
  strh    R1, [R0]
		
/* Call static constructors */
/*    bl __libc_init_array    */
/* Call the application's entry point.'*/
  bl  main//转跳main函数执行 
  bx  lr   
//计算.Reset_Handler段的长度,同时表示.Reset_Handler段结束 
.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       
*/
//定义了一个代码段,名字叫.Default_Handler,该段a可分配,x可执行,段内包含数据
.section  .text.Default_Handler,"ax",%progbits
Default_Handler://标记 Default_Handler符号位置
Infinite_Loop:
  b  Infinite_Loop
//标记 Default_Handler符号位置
  .size  Default_Handler, .-Default_Handler

第6段:

/******************************************************************************
*
* 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.
* 
*******************************************************************************/
//定义了一个段,名字叫.isr_vector,该段a可分配,段内包含数据
  .section  .isr_vector,"a",%progbits
//声明 g_pfnVectors是一个数据对象 ,同时表示段以 g_pfnVectors符号位开始
  .type  g_pfnVectors, %object
//计算.isr_vector,同时表示.isr_vector段结束
  .size  g_pfnVectors, .-g_pfnVectors
    
g_pfnVectors://标记 g_pfnVectors符号位置
//当前位置放置一个word型的值,这个值为_estack;已下同理
	.word  _estack//ld script赋值,RAM的结尾地址
	.word  Reset_Handler
	.word  NMI_Handler
	.word  HardFault_Handler
	.word  0
	.word  0
	.word  0
	.word  0
	.word  0
	.word  0
	.word  0
	.word  SVC_Handler
	.word  0
	.word  0
	.word  PendSV_Handler
	.word  SysTick_Handler
   
/* External interrupts */
	.word     PWDT0_IRQHandler
	.word     PWDT1_IRQHandler
	.word     PWM0_IRQHandler
	.word     PWM1_IRQHandler
	.word     ACMP0_IRQHandler
	.word     UART0_IRQHandler
	.word     UART1_IRQHandler
	.word     UART2_IRQHandler
	.word     WDG_IRQHandler
	.word     SPI0_IRQHandler
	.word     SPI1_IRQHandler
	.word     I2C0_IRQHandler
	.word     I2C1_IRQHandler
	.word     DMA0_Channel0_IRQHandler
	.word     DMA0_Channel1_IRQHandler
	.word     DMA0_Channel2_IRQHandler
	.word     DMA0_Channel3_IRQHandler
	.word     TIMER_Channel0_IRQHandler
	.word     TIMER_Channel1_IRQHandler
	.word     TIMER_Channel2_IRQHandler
	.word     TIMER_Channel3_IRQHandler
	.word     RTC_IRQHandler
	.word     PVD_IRQHandler
	.word     SPM_IRQHandler
	.word     CAN0_Handler
	.word     ADC0_IRQHandler
	.word     ECC_SRAM_IRQHandler
	.word     EXTI0_IRQHandler
	.word     EXTI1_IRQHandler
    .word     EXTI2_IRQHandler
    .word     EXTI3_8_IRQHandler
    .word	  EXTI9_15_IRQHandler
__Vectors_End = .

__Vectors     =   g_pfnVectors

第7段

/*******************************************************************************
*
* 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.
* 
*******************************************************************************/
//弱定义一个符号,名字叫 NMI_Handler
 //如果没有重写这个弱定义的符号,则执行 Default_Handler,反之则执行重写的NMI_Handler,已下同理
  .weak      NMI_Handler
   .thumb_set NMI_Handler,Default_Handler
  
   .weak      HardFault_Handler
   .thumb_set HardFault_Handler,Default_Handler

   .weak      SVC_Handler
   .thumb_set SVC_Handler,Default_Handler

   .weak      PendSV_Handler
   .thumb_set PendSV_Handler,Default_Handler

   .weak      SysTick_Handler
   .thumb_set SysTick_Handler,Default_Handler              
  
   .weak      PWDT0_IRQHandler                   
   .thumb_set PWDT0_IRQHandler,Default_Handler      
                  
   .weak      PWDT1_IRQHandler      
   .thumb_set PWDT1_IRQHandler,Default_Handler
               
   .weak      PWM0_IRQHandler            
   .thumb_set PWM0_IRQHandler,Default_Handler
            
   .weak      PWM1_IRQHandler                  
   .thumb_set PWM1_IRQHandler,Default_Handler
            
   .weak      ACMP0_IRQHandler         
   .thumb_set ACMP0_IRQHandler,Default_Handler
                  
   .weak      UART0_IRQHandler      
   .thumb_set UART0_IRQHandler,Default_Handler
                  
   .weak      UART1_IRQHandler      
   .thumb_set UART1_IRQHandler,Default_Handler
   
   .weak      UART2_IRQHandler         
   .thumb_set UART2_IRQHandler,Default_Handler
                  
   .weak      WDG_IRQHandler         
   .thumb_set WDG_IRQHandler,Default_Handler
                     
   .weak      SPI0_IRQHandler         
   .thumb_set SPI0_IRQHandler,Default_Handler 
                 
   .weak      SPI1_IRQHandler         
   .thumb_set SPI1_IRQHandler,Default_Handler
                        
   .weak      I2C0_IRQHandler         
   .thumb_set I2C0_IRQHandler,Default_Handler

   .weak      I2C1_IRQHandler         
   .thumb_set I2C1_IRQHandler,Default_Handler   
   
   .weak      DMA0_Channel0_IRQHandler               
   .thumb_set DMA0_Channel0_IRQHandler,Default_Handler
         
   .weak      DMA0_Channel1_IRQHandler               
   .thumb_set DMA0_Channel1_IRQHandler,Default_Handler
                  
   .weak      DMA0_Channel2_IRQHandler               
   .thumb_set DMA0_Channel2_IRQHandler,Default_Handler
                  
   .weak      DMA0_Channel3_IRQHandler               
   .thumb_set DMA0_Channel3_IRQHandler,Default_Handler 
                 
   .weak      TIMER_Channel0_IRQHandler              
   .thumb_set TIMER_Channel0_IRQHandler,Default_Handler
                  
   .weak      TIMER_Channel1_IRQHandler               
   .thumb_set TIMER_Channel1_IRQHandler,Default_Handler
                  
   .weak      TIMER_Channel2_IRQHandler               
   .thumb_set TIMER_Channel2_IRQHandler,Default_Handler
                  
   .weak      TIMER_Channel3_IRQHandler              
   .thumb_set TIMER_Channel3_IRQHandler,Default_Handler
                  
   .weak      RTC_IRQHandler               
   .thumb_set RTC_IRQHandler,Default_Handler
                  
   .weak      PVD_IRQHandler               
   .thumb_set PVD_IRQHandler,Default_Handler

   .weak      SPM_IRQHandler              
   .thumb_set SPM_IRQHandler,Default_Handler
                  
   .weak      CAN0_Handler               
   .thumb_set CAN0_Handler,Default_Handler
                  
   .weak      ADC0_IRQHandler               
   .thumb_set ADC0_IRQHandler,Default_Handler
   
   .weak      ECC_SRAM_IRQHandler              
   .thumb_set ECC_SRAM_IRQHandler,Default_Handler
                  
   .weak      EXTI0_IRQHandler               
   .thumb_set EXTI0_IRQHandler,Default_Handler
                  
   .weak      EXTI1_IRQHandler               
   .thumb_set EXTI1_IRQHandler,Default_Handler

   .weak      EXTI2_IRQHandler            
   .thumb_set EXTI2_IRQHandler,Default_Handler
                        
   .weak      EXTI3_8_IRQHandler            
   .thumb_set EXTI3_8_IRQHandler,Default_Handler   
   
   .weak      EXTI9_15_IRQHandler      
   .thumb_set EXTI9_15_IRQHandler,Default_Handler

参考资料:gcc编译环境下stm32H750 startup.S汇编文件注解_yoyotansa的博客-CSDN博客_stratup.s .thumb .thumb_func .syntax unifiedhttps://blog.csdn.net/oDongQing/article/details/104188494

第十六章:STM32处理器启动代码的理解_qq_33553024的博客-CSDN博客_g_pfnvectors

三、ld文件作用

1.定义程序入口地址
2.定义Flash、RAM中代码和数据的存放位置

四、ld文件代码解释

/* Entry Point */
/* 程序入口——程序将从Reset Handler开始执行,而该函数定义在stm32fxxx.s启动文件中。
ENTRY(Reset_Handler)

/* Highest address of the user mode stack */
/* end of stack 堆栈末尾 = RAM起始地址 + RAM空间大小 */
_estack = ORIGIN(RAM) + LENGTH(RAM);	/* end of "RAM" Ram type memory */

/* 程序所必须的堆、栈空间大小定义 */
_Min_Heap_Size = 0x200 ;	/* required amount of heap  */
_Min_Stack_Size = 0x400 ;	/* required amount of stack */

/* Memories definition */
/* 单片机内部存储空间 RAM FLASH起始位置和大小的声明 */
MEMORY
{
  RAM	(xrw)	: ORIGIN = 0x20000000,	LENGTH = 512K
  FLASH	(rx)	: ORIGIN = 0x8000000,	LENGTH = 2048K
}

/* Sections */
SECTIONS
{
  /* The startup code into "FLASH" Rom type memory */
  /* 中断向量表定义于 .s启动文件中,应位于Flash的最前端,也就是从0x8000000的位置开始 */
  .isr_vector :
  {
  /* 字对齐 */
    . = ALIGN(4);
    /* 将中断向量的内容链接到该地址 */
    KEEP(*(.isr_vector)) /* Startup code */
    . = ALIGN(4);
  } >FLASH

  /* The program code and other data into "FLASH" Rom type memory */
  /* .text对应程序的可执行代码 */
  .text :
  {
  /* 字对齐 */
    . = ALIGN(4);
    *(.text)           /* .text sections (code) */
    *(.text*)          /* .text* sections (code) */
    *(.glue_7)         /* glue arm to thumb code */
    *(.glue_7t)        /* glue thumb to arm code */
    *(.eh_frame)
    
	/* KEEP() 的作用是当启用连接器的--gc-sections垃圾回收选项时,这部分不能被回收  
	参考链接 https://blog.csdn.net/wangbinyantai/article/details/79001303 */
    KEEP (*(.init))
    KEEP (*(.fini))

    . = ALIGN(4);
    /* _etext是链接器的预定义变量,代表程序正文段结束的第一个地址 */
    _etext = .;        /* define a global symbols at end of code */
  } >FLASH

  /* Constant data into "FLASH" Rom type memory */
  /* .rodata代表程序中使用的常量表格数据等 */
  .rodata :
  {
  /* 前后字对齐 */
    . = ALIGN(4);
    *(.rodata)         /* .rodata sections (constants, strings, etc.) */
    *(.rodata*)        /* .rodata* sections (constants, strings, etc.) */
    . = ALIGN(4);
  } >FLASH
  
/* ?如果有朋友清楚下面这段定义的含义请评论告诉我,多谢!*/
  .ARM.extab   : { 
    . = ALIGN(4);
    *(.ARM.extab* .gnu.linkonce.armextab.*)
    . = ALIGN(4);
  } >FLASH
  
  .ARM : {
    . = ALIGN(4);
    __exidx_start = .;
    *(.ARM.exidx*)
    __exidx_end = .;
    . = ALIGN(4);
  } >FLASH
  
/* .preinit_array和.init_array部分包含指向将在初始化时调用的函数的指针数组。
参考链接:https://stackoverflow.com/questions/40532180/understanding-the-linkerscript-for-an-arm-cortex-m-microcontroller */

  .preinit_array     :
  {
    . = ALIGN(4);
    PROVIDE_HIDDEN (__preinit_array_start = .);
    KEEP (*(.preinit_array*))
    PROVIDE_HIDDEN (__preinit_array_end = .);
    . = ALIGN(4);
  } >FLASH
  
  .init_array :
  {
    . = ALIGN(4);
    PROVIDE_HIDDEN (__init_array_start = .);
    KEEP (*(SORT(.init_array.*)))
    KEEP (*(.init_array*))
    PROVIDE_HIDDEN (__init_array_end = .);
    . = ALIGN(4);
  } >FLASH
  
  .fini_array :
  {
    . = ALIGN(4);
    
    /* PROVIDE_HIDDEN 表示 :Similar to PROVIDE. For ELF targeted ports, 
    the symbol will be hidden and won’t be exported. 
    表示符号在目标文件中被定义但不会被导出 */
    
    PROVIDE_HIDDEN (__fini_array_start = .);
    KEEP (*(SORT(.fini_array.*)))
    KEEP (*(.fini_array*))
    PROVIDE_HIDDEN (__fini_array_end = .);
    . = ALIGN(4);
  } >FLASH

  /* Used by the startup to initialize data */
  /* 以变量_sidata存储.data块的起始地址,.data对应初始化了的全局变量 */
  
  _sidata = LOADADDR(.data);

  /* Initialized data sections into "RAM" Ram type memory */
  /* .data对应初始化了的全局变量,编译后将位于可执行文件中,由启动代码负责加载到数据区中(在单片机中这部分数据会存于flash中,需要由启动代码把这部分内容拷贝到ram中)*/
  .data : 
  {
    . = ALIGN(4);
    _sdata = .;        /* create a global symbol at data start */
    *(.data)           /* .data sections */
    *(.data*)          /* .data* sections */

    . = ALIGN(4);
    
    /* edata的地址是初始化数据区后面的第1个地址 */
    _edata = .;        /* define a global symbol at data end */
    
  } >RAM AT> FLASH

  /* Uninitialized data section into "RAM" Ram type memory */
  . = ALIGN(4);
  
  /* .bss段是没有初始值的全局变量,由启动代码把这部分内容全初始化为0 */
  .bss :
  {
    /* This is used by the startup in order to initialize the .bss section */
    _sbss = .;         /* define a global symbol at bss start */
    __bss_start__ = _sbss;
    *(.bss)
    *(.bss*)
    /* COMMON数据段仅在.o文件中存在,当代码中的全局变量没有赋初值时存储在该数据段中 */
    https://stackoverflow.com/questions/16835716/bss-vs-common-what-goes-where */
    
    *(COMMON)

    . = ALIGN(4);
    _ebss = .;         /* define a global symbol at bss end */
    __bss_end__ = _ebss;
  } >RAM

  /* User_heap_stack section, used to check that there is enough "RAM" Ram  type memory left */
  ._user_heap_stack :
  {
    . = ALIGN(8);
    
    /* 同PROVIDE_HIDDEN的作用类似,定义内部变量而不导出 */
    PROVIDE ( end = . );
    PROVIDE ( _end = . );
    . = . + _Min_Heap_Size;
    . = . + _Min_Stack_Size;
    . = ALIGN(8);
  } >RAM

  /* Remove information from the compiler libraries */
  /DISCARD/ :
  {
    libc.a ( * )
    libm.a ( * )
    libgcc.a ( * )
  }

  .ARM.attributes 0 : { *(.ARM.attributes) }
}


 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

qq_42475191

谢谢老板

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值