GCC STM32链接文件和启动文件分析

STM32链接文件和启动文件分析

SOC: STM32F401CCU6 256K flash 64k ram

Mem map:

在这里插入图片描述

链接文件部分:

. = ALIGN(4);是指4字节对齐
.,小数点表示当前的地址位置,例如__MALLOC_SYMBOLS = .;的意思是`__MALLOC_SYMBOLS 的地址就是.malloc段的地址
一般的程序中包含常见的几个段:
text(存放程序)
rodata(存放被初始化的数据)
data(表示初始化不为0的变量)
bss(表示初始化值为默认的全局变量)
text,rodata放在flash中,而data中的初始化值作为rodata放在flash中,变量在ram中占有空间,bss占ram空间
如果没有AT> FLASH,那么编译bin文件时地址是连续的

/*
*****************************************************************************
**

**  File        : stm32_flash.ld
**
**  Abstract    : Linker script for STM32F401VC Device with
**                256KByte FLASH, 64KByte RAM
**
**                Set heap size, stack size and stack location according
**                to application requirements.
**
**                Set memory bank area and size if external memory is used.
**
**  Target      : STMicroelectronics STM32
**
**  Environment : Atollic TrueSTUDIO(R)
**
**  Distribution: The file is distributed as is, without any warranty
**                of any kind.
**
**  (c)Copyright Atollic AB.
**  You may use this file as-is or modify it according to the needs of your
**  project. This file may only be built (assembled or compiled and linked)
**  using the Atollic TrueSTUDIO(R) product. The use of this file together
**  with other tools than Atollic TrueSTUDIO(R) is not permitted.
**
*****************************************************************************
*/
/* Entry Point */
//指定入口地址为Reset_Handler
//ENTRY(SYMBOL) :将符号SYMBOL的值设置成入口地址。
//入口地址(entry point)是指进程执行的第一条用户空间的指令在进程地址空间的地址
//ld有多种方法设置进程入口地址, 按一下顺序: (编号越前, 优先级越高)
//1, ld命令行的-e选项
//2, 连接脚本的ENTRY(SYMBOL)命令
//3, 如果定义了start符号, 使用start符号值
//4, 如果存在.text section, 使用.text section的第一字节的位置值
//5, 使用值0
ENTRY(Reset_Handler)

/* Highest address of the user mode stack */
//ram的结束地址
_estack = 0x20010000;    /* end of RAM */
/* Generate a link error if heap and stack don't fit into RAM */
//指定的最小的堆栈大小
_Min_Heap_Size = 0x200;      /* required amount of heap  */
_Min_Stack_Size = 0x400; /* required amount of stack */

/* Specify the memory areas */
//指定flash和sram大小
MEMORY
{
FLASH (rx)      : ORIGIN = 0x08000000, LENGTH = 256K
RAM (xrw)      : ORIGIN = 0x20000000, LENGTH = 64K
}

/* Define output sections */
// 输出文件组合布局
SECTIONS
{
  /* The startup code goes first into FLASH */
  //将向量表放在最前面,上电即可执行
  .isr_vector :
  {
    . = ALIGN(4);
    KEEP(*(.isr_vector)) /* Startup code */
    . = ALIGN(4);
  } >FLASH

  /* The program code and other data goes into FLASH */
  //指定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 (*(.init))
    KEEP (*(.fini))

    . = ALIGN(4);
    _etext = .;        /* define a global symbols at end of code */
  } >FLASH

  /* Constant data goes into FLASH */
  //指定只读段
  .rodata :
  {
    . = ALIGN(4);
    *(.rodata)         /* .rodata sections (constants, strings, etc.) */
    *(.rodata*)        /* .rodata* sections (constants, strings, etc.) */
    . = ALIGN(4);
  } >FLASH

  .ARM.extab   : { *(.ARM.extab* .gnu.linkonce.armextab.*) } >FLASH
  .ARM : {
    __exidx_start = .;
    *(.ARM.exidx*)
    __exidx_end = .;
  } >FLASH

  .preinit_array     :
  {
    PROVIDE_HIDDEN (__preinit_array_start = .);
    KEEP (*(.preinit_array*))
    PROVIDE_HIDDEN (__preinit_array_end = .);
  } >FLASH
  .init_array :
  {
    PROVIDE_HIDDEN (__init_array_start = .);
    KEEP (*(SORT(.init_array.*)))
    KEEP (*(.init_array*))
    PROVIDE_HIDDEN (__init_array_end = .);
  } >FLASH
  .fini_array :
  {
    PROVIDE_HIDDEN (__fini_array_start = .);
    KEEP (*(SORT(.fini_array.*)))
    KEEP (*(.fini_array*))
    PROVIDE_HIDDEN (__fini_array_end = .);
  } >FLASH

  /* used by the startup to initialize data */
  //_sidata 保存.data的地址相当于_sidata = & .data
  _sidata = LOADADDR(.data);

  /* Initialized data sections goes into RAM, load LMA copy after code */
  //指定数据段在ram中,但是数据保存在flash中,在启动的时候需要将数据拷贝到ram中去。
  .data : 
  {
    . = ALIGN(4);
    _sdata = .;        /* create a global symbol at data start */
    *(.data)           /* .data sections */
    *(.data*)          /* .data* sections */

    . = ALIGN(4);
    _edata = .;        /* define a global symbol at data end */
  } >RAM AT> FLASH

  
  /* Uninitialized data section */
  //指定bss段
  . = ALIGN(4);
  .bss :
  {
    /* This is used by the startup in order to initialize the .bss secion */
    _sbss = .;         /* define a global symbol at bss start */
    __bss_start__ = _sbss;
    *(.bss)
    *(.bss*)
    *(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 left */
  //指定堆栈段
  ._user_heap_stack :
  {
    . = ALIGN(4);
    PROVIDE ( end = . );
    PROVIDE ( _end = . );
    . = . + _Min_Heap_Size;
    . = . + _Min_Stack_Size;
    . = ALIGN(4);
  } >RAM

  /* Remove information from the standard libraries */
  /DISCARD/ :
  {
    libc.a ( * )
    libm.a ( * )
    libgcc.a ( * )
  }
  .ARM.attributes 0 : { *(.ARM.attributes) }
}

启动文件

/**
  ******************************************************************************
  * @file      startup_stm32f401xx.s
  * @author    MCD Application Team
  * @version   V1.8.0
  * @date      09-November-2016
  * @brief     STM32F401xx Devices vector table for Atollic TrueSTUDIO toolchain.
  *            This module performs:
  *                - Set the initial SP
  *                - Set the initial PC == Reset_Handler,
  *                - Set the vector table entries with the exceptions ISR address
  *                - Configure the clock system  
  *                - 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 2016 STMicroelectronics</center></h2>
  *
  * Licensed under MCD-ST Liberty SW License Agreement V2, (the "License");
  * You may not use this file except in compliance with the License.
  * You may obtain a copy of the License at:
  *
  *        http://www.st.com/software_license_agreement_liberty_v2
  *
  * Unless required by applicable law or agreed to in writing, software 
  * distributed under the License is distributed on an "AS IS" BASIS, 
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  *
  ******************************************************************************
  */
   
  .syntax unified //统一汇编语法-UAL
  .cpu cortex-m4  //CPU类型
  .fpu softvfp    //fpu 类型
  .thumb          //thumb指令

.global  g_pfnVectors //全局变量,外部可见
.global  Default_Handler //全局变量

/* start address for the initialization values of the .data section. 
defined in linker script */
.word  _sidata //保存.data地址的变量
/* start address for the .data section. defined in linker script */  
.word  _sdata //data 段的起始地址,_sidata 4字节对齐后的地址,在链接脚本中定义
/* end address for the .data section. defined in linker script */
.word  _edata //data段的结束地址
/* start address for the .bss section. defined in linker script */
.word  _sbss  //bss的起始地址
/* end address for the .bss section. defined in linker script */
.word  _ebss  //bss的结束地址
/* 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  Reset_Handler  //.weak 声明其他的同名标号优先于该标号被引用。
  .type  Reset_Handler, %function //.type将Reset_Handler 指定为函数
Reset_Handler:  

/* Copy the data segment initializers from flash to SRAM */  
  movs  r1, #0 //将立即数0存放到r1
  b  LoopCopyDataInit //跳转到LoopCopyDataInit 执行
//下面是将初始化的data数据拷贝到sram中去
CopyDataInit:
  ldr  r3, =_sidata //r3 = _sidata (flash中存放data的地址)
  ldr  r3, [r3, r1] //r3 = *(r3+r1) 取出该位置的数据
  str  r3, [r0, r1] //*(r0 + r1) = r3 将数据写入sram中r0 + r1的地方
  adds  r1, r1, #4  //r1 += 4
    
LoopCopyDataInit:
  ldr  r0, =_sdata //r0 = _sdata ld文件中定义
  ldr  r3, =_edata //r3 = _edata  ld文件中定义
  adds  r2, r0, r1 //r2 = r0+r1
  cmp  r2, r3      //比较r2和r3更新标志位
  bcc  CopyDataInit //C is clear 就跳转(无符号数小r2 < r3)因为汇编是顺序执行,所以这里会被循环执行,直到r2 == r3
  ldr  r2, =_sbss //r2 = _sbss ld文件中定义
  b  LoopFillZerobss
/* Zero fill the bss segment. */  
FillZerobss:
  movs  r3, #0 //r3 = 0
  str  r3, [r2], #4 //*r2 = 0; r2 += 4;
    
LoopFillZerobss: //将bss数据初始化为0
  ldr  r3, = _ebss //r3 = _ebss ld文件中定义
  cmp  r2, r3 //比较r2 r3
  bcc  FillZerobss //r2 < r3跳转

/* Call the clock system intitialization function.*/
  bl  SystemInit   //跳转到SystemInit
/* Call static constructors */
//__libc_init_array 这个函数中执行的关键过程如下:
//调用 .preinit_array 段中的预初始化函数
//调用 .init 段中的 _init 函数
//调用 .init_array 中的所有函数
  bl __libc_init_array
/* Call the application's entry point.*/
  bl  main
  bx  lr  
 // .size name,expression:将符号name所占空间设为expression(就是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是个死循环
    .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
    
// _estack是栈顶的值,这个值保存在flash的0地址处,flash的地址为0x08000000,
// 所以0x08000000保存_estack的值
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                   */                          
  //略...
                          
/*******************************************************************************
*
* 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      HardFault_Handler
   .thumb_set HardFault_Handler,Default_Handler
  
   .weak      MemManage_Handler
   .thumb_set MemManage_Handler,Default_Handler
  
   .weak      BusFault_Handler
   .thumb_set BusFault_Handler,Default_Handler

   .weak      UsageFault_Handler
   .thumb_set UsageFault_Handler,Default_Handler

   .weak      SVC_Handler
   .thumb_set SVC_Handler,Default_Handler

   .weak      DebugMon_Handler
   .thumb_set DebugMon_Handler,Default_Handler

   .weak      PendSV_Handler
   .thumb_set PendSV_Handler,Default_Handler

   .weak      SysTick_Handler
   .thumb_set SysTick_Handler,Default_Handler              
  
   .weak      WWDG_IRQHandler                   
   .thumb_set WWDG_IRQHandler,Default_Handler      
                  
   .weak      PVD_IRQHandler      
   .thumb_set PVD_IRQHandler,Default_Handler
               
   .weak      TAMP_STAMP_IRQHandler            
   .thumb_set TAMP_STAMP_IRQHandler,Default_Handler
            
   //略...
           
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

总结:stm32链接文件链接后布局如下,可以看到跟链接文件的配置是一致的,Flash的起始地址
放的是.isr_vector,isr_vector的第一个地址放的是_estack,也就是msp(栈顶指针的值)

tony@DESKTOP-VP4O54O:~/workdir/tony_linux_stm32_cmake/build$ readelf -S USER.elf 
There are 23 section headers, starting at offset 0x95690:

Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            00000000 000000 000000 00      0   0  0
  [ 1] .isr_vector       PROGBITS        08000000 010000 000194 00   A  0   0  1
  [ 2] .text             PROGBITS        08000194 010194 000afc 00  AX  0   0  4
  [ 3] .rodata           PROGBITS        08000c90 020010 000000 00  WA  0   0  1
  [ 4] .init_array       INIT_ARRAY      08000c90 010c90 000004 04  WA  0   0  4
  [ 5] .fini_array       FINI_ARRAY      08000c94 010c94 000004 04  WA  0   0  4
  [ 6] .data             PROGBITS        20000000 020000 000010 00  WA  0   0  4
  [ 7] .bss              NOBITS          20000010 020010 0000e8 00  WA  0   0  4
  [ 8] ._user_heap_stack NOBITS          200000f8 0200f8 000600 00  WA  0   0  1
  [ 9] .ARM.attributes   ARM_ATTRIBUTES  00000000 020010 000030 00      0   0  1
  [10] .debug_line       PROGBITS        00000000 020040 003936 00      0   0  1
  [11] .debug_info       PROGBITS        00000000 023976 003df2 00      0   0  1
  [12] .debug_abbrev     PROGBITS        00000000 027768 000ace 00      0   0  1
  [13] .debug_aranges    PROGBITS        00000000 028238 000470 00      0   0  8
  [14] .debug_str        PROGBITS        00000000 0286a8 064708 01  MS  0   0  1
  [15] .debug_ranges     PROGBITS        00000000 08cdb0 0003f8 00      0   0  8
  [16] .debug_loc        PROGBITS        00000000 08d1a8 0027c4 00      0   0  1
  [17] .debug_macro      PROGBITS        00000000 08f96c 00345e 00      0   0  1
  [18] .comment          PROGBITS        00000000 092dca 000049 01  MS  0   0  1
  [19] .debug_frame      PROGBITS        00000000 092e14 00121c 00      0   0  4
  [20] .symtab           SYMTAB          00000000 094030 000d00 10     21 105  4
  [21] .strtab           STRTAB          00000000 094d30 000869 00      0   0  1
  [22] .shstrtab         STRTAB          00000000 095599 0000f6 00      0   0  1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
  L (link order), O (extra OS processing required), G (group), T (TLS),
  C (compressed), x (unknown), o (OS specific), E (exclude),
  y (purecode), p (processor specific)

所以上电后会从08000000 执行,对应的异常处理函数是Reset_Handler,Reset_Handler
函数做的工作首先将flash中存放的初始化的data数据拷贝到sram中(0x20000000),然后
将Bss段中的数据全部初始化为0,调用SystemInit 函数初始化时钟,最后跳转到Main函数执行。
startup.s中开头注释也有总结:
- Set the initial SP
- Set the initial PC == Reset_Handler,
- Set the vector table entries with the exceptions ISR address
- Configure the clock system
- Branches to main in the C library (which eventually calls main()).
1.初始化堆栈寄存器,这里msp寄存器时机器上电后就由硬件将flash 08000000 中存的栈顶的值load到msp
寄存器中。
2.调用08000004 Reset_Handler异常处理函数
3.设置异常向量表
4.配置系统时钟,SystemInit
5.调用Main

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

tony++

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值