引言
最近在研究IAP时需要弄清楚CM3单片机的启动流程,恰巧手边有一块野火的STM32F103开发板在吃灰,就拿它做了一些实验,并把实验结果分享出来,作为学习笔记。
一、第一条指令在哪里?
CPU需要找到第一条指令,才能够开始执行之后的程序。实际上,在离开复位状态(硬件时序)后,CM3做的第一件事就是读取下列两个32位整数的值:
-
从地址0x0000,0000处取出MSP的初始值。
-
从地址0x0000,0004处取出PC的初始值——这个值是复位向量,LSB必须是1。然后从这个值所对应的地址处取指。
请注意,这与传统的ARM架构不同——其实也和绝大多数的其它单片机不同。传统的ARM架构总是从0地址开始执行第一条指令。它们的0地址处总是一条跳转指令。在CM3中,在0地址处提供MSP的初始值,然后紧跟着就是向量表(向量表在以后还可以被移至其它位置)。向量表中的数值是32位的地址,而不是跳转指令。向量表的第一个条目指向复位后应执行的第一条指令。
对于STM32F10x,参考手册得知内部SRAM和Flash的存储器分布:
因为固定的存储器映像,代码区始终从地址0x00000000开始(通过ICode和DCode总线访问),而数据区(SRAM)始终从地址0x2000 0000开始(通过系统总线访问)。Cortex-M3的CPU始终从ICode总线获取复位向量,即启动仅适合于从代码区开始(典型地从Flash启动)。STM32F10xxx微控制器实现了一个特殊的机制,系统可以不仅仅从Flash存储器或系统存储器启动,还可以从内置SRAM启动。 根据选定的启动模式,主闪存存储器、系统存储器或SRAM可以按照以下方式访问:
-
从主闪存存储器启动:主闪存存储器被映射到启动空间(0x00000000),但仍然能够在它原有的地址(0x08000000)访问它,即闪存存储器的内容可以在两个地址区域访问,0x00000000或0x08000000。
-
从系统存储器启动:系统存储器被映射到启动空间(0x00000000),但仍然能够在它原有的地址(互联型产品原有地址为0x1FFFB000,其它产品原有地址为0x1FFFF000)访问它。
-
从内置SRAM启动:只能在0x20000000开始的地址区访问SRAM。
二、启动文件(startup_xx.s)
在每一个完整的stm32工程中都可以找到该启动文件,基本上所有的CM0,CM3,CM4等基于ARM核的单片机启动文件都差不多,熟悉ARM汇编同学们应该对此文件的阅读没有压力,下面为该文件每一条指令的进行注释解析。
;******************** (C) COPYRIGHT 2011 STMicroelectronics ********************
;* 文件名 : startup_stm32f10x_hd.s
;* 作者 : MCD Application Team
;* 版本 : V3.5.0
;* 日期 : 11-March-2011
;* 描述 : STM32F10x高密度设备中断向量表,使用MDK-ARM 工具链
;* 本模块将执行以下功能:
;* - 设置初始化SP寄存器
;* - 设置初始化PC寄存器=Reset_Handler
;* - 设置中断表中各个ISR的中断入口地址
;* - 设置系统时钟并且配置外挂在STM3210E-EVAL板上的SRAM作为数据内存(可选,如果用户使能)
;* - 跳转至__main(其最终会调用main)
;* 复位之后Cortex-M3处理器处于线程模式,特权级访问,使用主堆栈
;*******************************************************************************
; 评估你自己的应用程序需求来为栈分配内存(字节)
Stack_Size EQU 0x00000400 ;Stack_Size = 0x00000400
AREA STACK, NOINIT, READWRITE, ALIGN=3 ;声明STACK段,不初始化,RW,8字节对齐
Stack_Mem SPACE Stack_Size ;通过SPACE指令分配Stack_Size内存作为栈,Stack_Mem = 栈底地址
__initial_sp ;__initial_sp = 栈顶地址
Heap_Size EQU 0x00000200 ;Heap_Size = 0x00000400
AREA HEAP, NOINIT, READWRITE, ALIGN=3 ;声明HEAP段,不初始化,RW,8字节对齐
__heap_base
Heap_Mem SPACE Heap_Size ;通过SPACE指令分配Heap_Size内存作为堆,Heap_Mem = 堆底地址
__heap_limit ;__heap_limit = 堆顶地址
PRESERVE8 ;保留8字节对齐
THUMB ;切换到THUMB指令集
;复位时,向量表被映射到0地址处
AREA RESET, DATA, READONLY ;声明RESET段,不初始化,RW
EXPORT __Vectors ;导出符号__Vectors,表示向量表基地址
EXPORT __Vectors_End ;导出符号__Vectors_End表示向量表尾地址
EXPORT __Vectors_Size ;导出符号__Vectors_Size表示向量表长度
;内核中断
__Vectors DCD __initial_sp ;栈顶(复位后取出该值给SP寄存器)
DCD Reset_Handler ; Reset Handler地址,复位后第二条指令会取出该地址的内容,并跳转至该地址执行
DCD NMI_Handler ; NMI Handler
DCD HardFault_Handler ; Hard Fault Handler
DCD MemManage_Handler ; MPU Fault Handler
DCD BusFault_Handler ; Bus Fault Handler
DCD UsageFault_Handler ; Usage Fault Handler
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD SVC_Handler ; SVCall Handler
DCD DebugMon_Handler ; Debug Monitor Handler
DCD 0 ; Reserved
DCD PendSV_Handler ; PendSV Handler
DCD SysTick_Handler ; SysTick Handler
;外设中断
DCD WWDG_IRQHandler ; Window Watchdog
DCD PVD_IRQHandler ; PVD through EXTI Line detect
DCD TAMPER_IRQHandler ; Tamper
DCD RTC_IRQHandler ; RTC
DCD FLASH_IRQHandler ; Flash
DCD RCC_IRQHandler ; RCC
DCD EXTI0_IRQHandler ; EXTI Line 0
DCD EXTI1_IRQHandler ; EXTI Line 1
DCD EXTI2_IRQHandler ; EXTI Line 2
DCD EXTI3_IRQHandler ; EXTI Line 3
DCD EXTI4_IRQHandler ; EXTI Line 4
DCD DMA1_Channel1_IRQHandler ; DMA1 Channel 1
DCD DMA1_Channel2_IRQHandler ; DMA1 Channel 2
DCD DMA1_Channel3_IRQHandler ; DMA1 Channel 3
DCD DMA1_Channel4_IRQHandler ; DMA1 Channel 4
DCD DMA1_Channel5_IRQHandler ; DMA1 Channel 5
DCD DMA1_Channel6_IRQHandler ; DMA1 Channel 6
DCD DMA1_Channel7_IRQHandler ; DMA1 Channel 7
DCD ADC1_2_IRQHandler ; ADC1 & ADC2
DCD USB_HP_CAN1_TX_IRQHandler ; USB High Priority or CAN1 TX
DCD USB_LP_CAN1_RX0_IRQHandler ; USB Low Priority or CAN1 RX0
DCD CAN1_RX1_IRQHandler ; CAN1 RX1
DCD CAN1_SCE_IRQHandler ; CAN1 SCE
DCD EXTI9_5_IRQHandler ; EXTI Line 9..5
DCD TIM1_BRK_IRQHandler ; TIM1 Break
DCD TIM1_UP_IRQHandler ; TIM1 Update
DCD TIM1_TRG_COM_IRQHandler ; TIM1 Trigger and Commutation
DCD TIM1_CC_IRQHandler ; TIM1 Capture Compare
DCD TIM2_IRQHandler ; TIM2
DCD TIM3_IRQHandler ; TIM3
DCD TIM4_IRQHandler ; TIM4
DCD I2C1_EV_IRQHandler ; I2C1 Event
DCD I2C1_ER_IRQHandler ; I2C1 Error
DCD I2C2_EV_IRQHandler ; I2C2 Event
DCD I2C2_ER_IRQHandler ; I2C2 Error
DCD SPI1_IRQHandler ; SPI1
DCD SPI2_IRQHandler ; SPI2
DCD USART1_IRQHandler ; USART1
DCD USART2_IRQHandler ; USART2
DCD USART3_IRQHandler ; USART3
DCD EXTI15_10_IRQHandler ; EXTI Line 15..10
DCD RTCAlarm_IRQHandler ; RTC Alarm through EXTI Line
DCD USBWakeUp_IRQHandler ; USB Wakeup from suspend
DCD TIM8_BRK_IRQHandler ; TIM8 Break
DCD TIM8_UP_IRQHandler ; TIM8 Update
DCD TIM8_TRG_COM_IRQHandler ; TIM8 Trigger and Commutation
DCD TIM8_CC_IRQHandler ; TIM8 Capture Compare
DCD ADC3_IRQHandler ; ADC3
DCD FSMC_IRQHandler ; FSMC
DCD SDIO_IRQHandler ; SDIO
DCD TIM5_IRQHandler ; TIM5
DCD SPI3_IRQHandler ; SPI3
DCD UART4_IRQHandler ; UART4
DCD UART5_IRQHandler ; UART5
DCD TIM6_IRQHandler ; TIM6
DCD TIM7_IRQHandler ; TIM7
DCD DMA2_Channel1_IRQHandler ; DMA2 Channel1
DCD DMA2_Channel2_IRQHandler ; DMA2 Channel2
DCD DMA2_Channel3_IRQHandler ; DMA2 Channel3
DCD DMA2_Channel4_5_IRQHandler ; DMA2 Channel4 & Channel5
__Vectors_End
__Vectors_Size EQU __Vectors_End - __Vectors ;向量表大小 = __Vectors_End - __Vectors
AREA |.text|, CODE, READONLY ;声明以下属于|.text|段,只读
; Reset handler异常处理函数
Reset_Handler PROC ;PROC表明函数开始
EXPORT Reset_Handler [WEAK] ;导出弱符号Reset_Handler
IMPORT __main ;导入符号__main
IMPORT SystemInit ;导入符号SystemInit
LDR R0, =SystemInit ;R0 = SystemInit
BLX R0 ;跳转至SystemInit执行,并保存LR
LDR R0, =__main ;R0 =__main
BX R0 ;无返回跳转__main
ENDP ;ENDP表明函数结束
; 空异常处理函数(可以被修改的无限循环)
NMI_Handler PROC
EXPORT NMI_Handler [WEAK]
B .
ENDP
HardFault_Handler\
PROC
EXPORT HardFault_Handler [WEAK]
B .
ENDP
MemManage_Handler\
PROC
EXPORT MemManage_Handler [WEAK]
B .
ENDP
BusFault_Handler\
PROC
EXPORT BusFault_Handler [WEAK]
B .
ENDP
UsageFault_Handler\
PROC
EXPORT UsageFault_Handler [WEAK]
B .
ENDP
SVC_Handler PROC
EXPORT SVC_Handler [WEAK]
B .
ENDP
DebugMon_Handler\
PROC
EXPORT DebugMon_Handler [WEAK]
B .
ENDP
PendSV_Handler PROC
EXPORT PendSV_Handler [WEAK]
B .
ENDP
SysTick_Handler PROC
EXPORT SysTick_Handler [WEAK]
B .
ENDP
Default_Handler PROC
EXPORT WWDG_IRQHandler [WEAK]
EXPORT PVD_IRQHandler [WEAK]
EXPORT TAMPER_IRQHandler [WEAK]
EXPORT RTC_IRQHandler [WEAK]
EXPORT FLASH_IRQHandler [WEAK]
EXPORT RCC_IRQHandler [WEAK]
EXPORT EXTI0_IRQHandler [WEAK]
EXPORT EXTI1_IRQHandler [WEAK]
EXPORT EXTI2_IRQHandler [WEAK]
EXPORT EXTI3_IRQHandler [WEAK]
EXPORT EXTI4_IRQHandler [WEAK]
EXPORT DMA1_Channel1_IRQHandler [WEAK]
EXPORT DMA1_Channel2_IRQHandler [WEAK]
EXPORT DMA1_Channel3_IRQHandler [WEAK]
EXPORT DMA1_Channel4_IRQHandler [WEAK]
EXPORT DMA1_Channel5_IRQHandler [WEAK]
EXPORT DMA1_Channel6_IRQHandler [WEAK]
EXPORT DMA1_Channel7_IRQHandler [WEAK]
EXPORT ADC1_2_IRQHandler [WEAK]
EXPORT USB_HP_CAN1_TX_IRQHandler [WEAK]
EXPORT USB_LP_CAN1_RX0_IRQHandler [WEAK]
EXPORT CAN1_RX1_IRQHandler [WEAK]
EXPORT CAN1_SCE_IRQHandler [WEAK]
EXPORT EXTI9_5_IRQHandler [WEAK]
EXPORT TIM1_BRK_IRQHandler [WEAK]
EXPORT TIM1_UP_IRQHandler [WEAK]
EXPORT TIM1_TRG_COM_IRQHandler [WEAK]
EXPORT TIM1_CC_IRQHandler [WEAK]
EXPORT TIM2_IRQHandler [WEAK]
EXPORT TIM3_IRQHandler [WEAK]
EXPORT TIM4_IRQHandler [WEAK]
EXPORT I2C1_EV_IRQHandler [WEAK]
EXPORT I2C1_ER_IRQHandler [WEAK]
EXPORT I2C2_EV_IRQHandler [WEAK]
EXPORT I2C2_ER_IRQHandler [WEAK]
EXPORT SPI1_IRQHandler [WEAK]
EXPORT SPI2_IRQHandler [WEAK]
EXPORT USART1_IRQHandler [WEAK]
EXPORT USART2_IRQHandler [WEAK]
EXPORT USART3_IRQHandler [WEAK]
EXPORT EXTI15_10_IRQHandler [WEAK]
EXPORT RTCAlarm_IRQHandler [WEAK]
EXPORT USBWakeUp_IRQHandler [WEAK]
EXPORT TIM8_BRK_IRQHandler [WEAK]
EXPORT TIM8_UP_IRQHandler [WEAK]
EXPORT TIM8_TRG_COM_IRQHandler [WEAK]
EXPORT TIM8_CC_IRQHandler [WEAK]
EXPORT ADC3_IRQHandler [WEAK]
EXPORT FSMC_IRQHandler [WEAK]
EXPORT SDIO_IRQHandler [WEAK]
EXPORT TIM5_IRQHandler [WEAK]
EXPORT SPI3_IRQHandler [WEAK]
EXPORT UART4_IRQHandler [WEAK]
EXPORT UART5_IRQHandler [WEAK]
EXPORT TIM6_IRQHandler [WEAK]
EXPORT TIM7_IRQHandler [WEAK]
EXPORT DMA2_Channel1_IRQHandler [WEAK]
EXPORT DMA2_Channel2_IRQHandler [WEAK]
EXPORT DMA2_Channel3_IRQHandler [WEAK]
EXPORT DMA2_Channel4_5_IRQHandler [WEAK]
WWDG_IRQHandler
PVD_IRQHandler
TAMPER_IRQHandler
RTC_IRQHandler
FLASH_IRQHandler
RCC_IRQHandler
EXTI0_IRQHandler
EXTI1_IRQHandler
EXTI2_IRQHandler
EXTI3_IRQHandler
EXTI4_IRQHandler
DMA1_Channel1_IRQHandler
DMA1_Channel2_IRQHandler
DMA1_Channel3_IRQHandler
DMA1_Channel4_IRQHandler
DMA1_Channel5_IRQHandler
DMA1_Channel6_IRQHandler
DMA1_Channel7_IRQHandler
ADC1_2_IRQHandler
USB_HP_CAN1_TX_IRQHandler
USB_LP_CAN1_RX0_IRQHandler
CAN1_RX1_IRQHandler
CAN1_SCE_IRQHandler
EXTI9_5_IRQHandler
TIM1_BRK_IRQHandler
TIM1_UP_IRQHandler
TIM1_TRG_COM_IRQHandler
TIM1_CC_IRQHandler
TIM2_IRQHandler
TIM3_IRQHandler
TIM4_IRQHandler
I2C1_EV_IRQHandler
I2C1_ER_IRQHandler
I2C2_EV_IRQHandler
I2C2_ER_IRQHandler
SPI1_IRQHandler
SPI2_IRQHandler
USART1_IRQHandler
USART2_IRQHandler
USART3_IRQHandler
EXTI15_10_IRQHandler
RTCAlarm_IRQHandler
USBWakeUp_IRQHandler
TIM8_BRK_IRQHandler
TIM8_UP_IRQHandler
TIM8_TRG_COM_IRQHandler
TIM8_CC_IRQHandler
ADC3_IRQHandler
FSMC_IRQHandler
SDIO_IRQHandler
TIM5_IRQHandler
SPI3_IRQHandler
UART4_IRQHandler
UART5_IRQHandler
TIM6_IRQHandler
TIM7_IRQHandler
DMA2_Channel1_IRQHandler
DMA2_Channel2_IRQHandler
DMA2_Channel3_IRQHandler
DMA2_Channel4_5_IRQHandler
B . ;死循环
ENDP ;结束函数
ALIGN ;填充字节使地址对齐
;*******************************************************************************
; 用户堆栈初始化
;*******************************************************************************
IF :DEF:__MICROLIB ;如果用户使用了MircoLib
EXPORT __initial_sp ;导出符号__initial_sp,表示栈顶地址
EXPORT __heap_base ;导出符号__heap_base,表示堆底地址
EXPORT __heap_limit ;导出符号__heap_limit,表示堆顶地址
ELSE ;没定义__MICROLIB,则使用默认C库
IMPORT __use_two_region_memory ;用于指定存储器模式为双段模式,即一部分储存区用于栈空间,其他的存储区用于堆空间,
堆区空间可以为0,但是,这样就不能调用malloc()内存分配函数;堆区空间也可以由存储
器分配,也可以从执行环境中继承。在汇编代码中,通过 IMPORT __use_two_region_memory
表明使用双段模式;在C语言中,通过 #pragma import(__use_two_region_memory)语句表明使用双段模式。
EXPORT __user_initial_stackheap __user_initial_stackheap,外界通过该函数获取堆栈参数
__user_initial_stackheap
LDR R0, = Heap_Mem ;R0 - 堆底
LDR R1, =(Stack_Mem + Stack_Size) ;R1 = 栈顶
LDR R2, = (Heap_Mem + Heap_Size) ;R2 = 堆顶
LDR R3, = Stack_Mem ;R3 = 栈底
BX LR ;返回
ALIGN ;填充字节使地址对齐
ENDIF ;结束IF
END ;结束程序
;******************* (C) COPYRIGHT 2011 STMicroelectronics *****END OF FILE*****
三、__main
3.1 重定位的作用
由启动文件可以看到,在用户main函数被正式调用前,实际上还有其它函数被调用了,分别是SystemInit()与__main。SystemInit()负责配置MCU的内部时钟,可根据用户需求将主频配置到预设时钟频率,之后重新设置向量表偏移到0x08000000。完成这些配置后进入__main。
此处以一个简单的用户main函数为例,简单说明__main函数中具体做了哪些事。
#include "stm32f10x.h"
volatile int g_a = 1;
volatile int g_b = 2;
volatile int g_c = 3;
int main(void)
{
g_a = 4;
g_b = 5;
g_c = 6;
while(1);
return 0;
}
该程序在编译好后的输出如下
Build started: Project: Project
*** Using Compiler 'V5.06 update 7 (build 960)', folder: 'C:\Keil_v5\ARM\ARMCC\Bin'
Build target 'Target 1'
linking...
Program Size: Code=520 RO-data=336 RW-data=12 ZI-data=1028
RW-data = 12 即g_a, g_c, g_c,ZI-data=1028 ,即为栈大小Stack_Size = 0x400,多出4字节为对齐。
问题在于,RW-data与ZI-data的执行地址(Exec Addr)应该位于RAM区0x20000000,而用户所编写的程序都被保存在Flash上,由谁来负责将RW与ZI中的数据从Flash搬运到RAM上,换句话说,谁来保证0x20000000处的g_a = 1,0x20000004处的g_b = 2?STACK既然被存放在ZI段,谁来负责上电后0x20000010 ~0x20000410这段内存的值为0?如果没有__main函数,直接进入main函数,这些内存中的内容是否还会按照程序员预想的方式存在?
这里笔者做了一个实验,在启动文件中直接将__main改为main,并进入调试模式来查看内存中关于变量g_a,g_b,g_c的数值。
可以看到一开始g_a,g_b,g_c的值并不是我们给予的初始值,此处都是0是因为使用模拟器环境,在真实情况下应该为随机值。
3.2得到反汇编码
由于看不到__main函数的源代码,需要借助反汇编工具来查看__main为我们的程序做了什么。
通过powershell可以查看armcc工具链中的fromelf工具的使用帮助
其中-a选项可以打印数据的地址,-c选项可以得到反汇编代码,--output选项指明输出的文件名。
在如下的keil配置界面中输入
fromelf -a -c ./Objects/Template.axf --output ./Objects/Template.asm
再次编译即可在Objects文件目录中看到Template.asm文件。打开后找到程序的反汇编码如下。
RESET
__Vectors
0x08000000: 20000410 ... DCD 536871952
0x08000004: 08000149 I... DCD 134218057
0x08000008: 0800019f .... DCD 134218143
0x0800000c: 08000197 .... DCD 134218135
0x08000010: 0800019b .... DCD 134218139
0x08000014: 08000191 .... DCD 134218129
0x08000018: 080002f1 .... DCD 134218481
0x0800001c: 00000000 .... DCD 0
0x08000020: 00000000 .... DCD 0
0x08000024: 00000000 .... DCD 0
0x08000028: 00000000 .... DCD 0
0x0800002c: 080001a3 .... DCD 134218147
0x08000030: 08000195 .... DCD 134218133
0x08000034: 00000000 .... DCD 0
0x08000038: 080001a1 .... DCD 134218145
0x0800003c: 0800028d .... DCD 134218381
0x08000040: 08000163 c... DCD 134218083
0x08000044: 08000163 c... DCD 134218083
0x08000048: 08000163 c... DCD 134218083
0x0800004c: 08000163 c... DCD 134218083
0x08000050: 08000163 c... DCD 134218083
0x08000054: 08000163 c... DCD 134218083
0x08000058: 08000163 c... DCD 134218083
0x0800005c: 08000163 c... DCD 134218083
0x08000060: 08000163 c... DCD 134218083
0x08000064: 08000163 c... DCD 134218083
0x08000068: 08000163 c... DCD 134218083
0x0800006c: 08000163 c... DCD 134218083
0x08000070: 08000163 c... DCD 134218083
0x08000074: 08000163 c... DCD 134218083
0x08000078: 08000163 c... DCD 134218083
0x0800007c: 08000163 c... DCD 134218083
0x08000080: 08000163 c... DCD 134218083
0x08000084: 08000163 c... DCD 134218083
0x08000088: 08000163 c... DCD 134218083
0x0800008c: 08000163 c... DCD 134218083
0x08000090: 08000163 c... DCD 134218083
0x08000094: 08000163 c... DCD 134218083
0x08000098: 08000163 c... DCD 134218083
0x0800009c: 08000163 c... DCD 134218083
0x080000a0: 08000163 c... DCD 134218083
0x080000a4: 08000163 c... DCD 134218083
0x080000a8: 08000163 c... DCD 134218083
0x080000ac: 08000163 c... DCD 134218083
0x080000b0: 08000163 c... DCD 134218083
0x080000b4: 08000163 c... DCD 134218083
0x080000b8: 08000163 c... DCD 134218083
0x080000bc: 08000163 c... DCD 134218083
0x080000c0: 08000163 c... DCD 134218083
0x080000c4: 08000163 c... DCD 134218083
0x080000c8: 08000163 c... DCD 134218083
0x080000cc: 08000163 c... DCD 134218083
0x080000d0: 08000163 c... DCD 134218083
0x080000d4: 08000163 c... DCD 134218083
0x080000d8: 08000163 c... DCD 134218083
0x080000dc: 08000163 c... DCD 134218083
0x080000e0: 08000163 c... DCD 134218083
0x080000e4: 08000163 c... DCD 134218083
0x080000e8: 08000163 c... DCD 134218083
0x080000ec: 08000163 c... DCD 134218083
0x080000f0: 08000163 c... DCD 134218083
0x080000f4: 08000163 c... DCD 134218083
0x080000f8: 08000163 c... DCD 134218083
0x080000fc: 08000163 c... DCD 134218083
0x08000100: 08000163 c... DCD 134218083
0x08000104: 08000163 c... DCD 134218083
0x08000108: 08000163 c... DCD 134218083
0x0800010c: 08000163 c... DCD 134218083
0x08000110: 08000163 c... DCD 134218083
0x08000114: 08000163 c... DCD 134218083
0x08000118: 08000163 c... DCD 134218083
0x0800011c: 08000163 c... DCD 134218083
0x08000120: 08000163 c... DCD 134218083
0x08000124: 08000163 c... DCD 134218083
0x08000128: 08000163 c... DCD 134218083
0x0800012c: 08000163 c... DCD 134218083
$t
.ARM.Collect$$$$00000000
.ARM.Collect$$$$00000001
__Vectors_End
__main
_main_stk
0x08000130: f8dfd010 .... LDR sp,__lit__00000000 ; [0x8000144] = 0x20000410
.ARM.Collect$$$$00000004
_main_scatterload
0x08000134: f000f81a .... BL __scatterload ; 0x800016c
.ARM.Collect$$$$00000008
.ARM.Collect$$$$0000000A
.ARM.Collect$$$$0000000B
__main_after_scatterload
_main_clock
_main_cpp_init
_main_init
0x08000138: 4800 .H LDR r0,[pc,#0] ; [0x800013c] = 0x8000315
0x0800013a: 4700 .G BX r0
$d
0x0800013c: 08000315 .... DCD 134218517
$t
.ARM.Collect$$$$0000000E
__rt_lib_shutdown_fini
0x08000140: f3af8000 .... NOP.W
$d
.ARM.Collect$$$$00002712
__lit__00000000
.ARM.Collect$$$$0000000F
.ARM.Collect$$$$00000011
__rt_final_cpp
__rt_final_exit
0x08000144: 20000410 ... DCD 536871952
$t
.text
Reset_Handler
0x08000148: 4806 .H LDR r0,[pc,#24] ; [0x8000164] = 0x8000291
0x0800014a: 4780 .G BLX r0
0x0800014c: 4806 .H LDR r0,[pc,#24] ; [0x8000168] = 0x8000131
0x0800014e: 4700 .G BX r0
0x08000150: e7fe .. B 0x8000150 ; Reset_Handler + 8
0x08000152: e7fe .. B 0x8000152 ; Reset_Handler + 10
0x08000154: e7fe .. B 0x8000154 ; Reset_Handler + 12
0x08000156: e7fe .. B 0x8000156 ; Reset_Handler + 14
0x08000158: e7fe .. B 0x8000158 ; Reset_Handler + 16
0x0800015a: e7fe .. B 0x800015a ; Reset_Handler + 18
0x0800015c: e7fe .. B 0x800015c ; Reset_Handler + 20
0x0800015e: e7fe .. B 0x800015e ; Reset_Handler + 22
0x08000160: e7fe .. B 0x8000160 ; Reset_Handler + 24
ADC1_2_IRQHandler
ADC3_IRQHandler
CAN1_RX1_IRQHandler
CAN1_SCE_IRQHandler
DMA1_Channel1_IRQHandler
DMA1_Channel2_IRQHandler
DMA1_Channel3_IRQHandler
DMA1_Channel4_IRQHandler
DMA1_Channel5_IRQHandler
DMA1_Channel6_IRQHandler
DMA1_Channel7_IRQHandler
DMA2_Channel1_IRQHandler
DMA2_Channel2_IRQHandler
DMA2_Channel3_IRQHandler
DMA2_Channel4_5_IRQHandler
EXTI0_IRQHandler
EXTI15_10_IRQHandler
EXTI1_IRQHandler
EXTI2_IRQHandler
EXTI3_IRQHandler
EXTI4_IRQHandler
EXTI9_5_IRQHandler
FLASH_IRQHandler
FSMC_IRQHandler
I2C1_ER_IRQHandler
I2C1_EV_IRQHandler
I2C2_ER_IRQHandler
I2C2_EV_IRQHandler
PVD_IRQHandler
RCC_IRQHandler
RTCAlarm_IRQHandler
RTC_IRQHandler
SDIO_IRQHandler
SPI1_IRQHandler
SPI2_IRQHandler
SPI3_IRQHandler
TAMPER_IRQHandler
TIM1_BRK_IRQHandler
TIM1_CC_IRQHandler
TIM1_TRG_COM_IRQHandler
TIM1_UP_IRQHandler
TIM2_IRQHandler
TIM3_IRQHandler
TIM4_IRQHandler
TIM5_IRQHandler
TIM6_IRQHandler
TIM7_IRQHandler
TIM8_BRK_IRQHandler
TIM8_CC_IRQHandler
TIM8_TRG_COM_IRQHandler
TIM8_UP_IRQHandler
UART4_IRQHandler
UART5_IRQHandler
USART1_IRQHandler
USART2_IRQHandler
USART3_IRQHandler
USBWakeUp_IRQHandler
USB_HP_CAN1_TX_IRQHandler
USB_LP_CAN1_RX0_IRQHandler
WWDG_IRQHandler
0x08000162: e7fe .. B ADC1_2_IRQHandler ; 0x8000162
$d
0x08000164: 08000291 .... DCD 134218385
0x08000168: 08000131 1... DCD 134218033
$t
.text
__scatterload
__scatterload_rt2
0x0800016c: 4c06 .L LDR r4,[pc,#24] ; [0x8000188] = 0x8000338
0x0800016e: 4d07 .M LDR r5,[pc,#28] ; [0x800018c] = 0x8000358
0x08000170: e006 .. B 0x8000180 ; __scatterload + 20
0x08000172: 68e0 .h LDR r0,[r4,#0xc]
0x08000174: f0400301 @... ORR r3,r0,#1
0x08000178: e8940007 .... LDM r4,{r0-r2}
0x0800017c: 4798 .G BLX r3
0x0800017e: 3410 .4 ADDS r4,r4,#0x10
0x08000180: 42ac .B CMP r4,r5
0x08000182: d3f6 .. BCC 0x8000172 ; __scatterload + 6
0x08000184: f7ffffd8 .... BL __main_after_scatterload ; 0x8000138
$d
0x08000188: 08000338 8... DCD 134218552
0x0800018c: 08000358 X... DCD 134218584
$t
i.BusFault_Handler
BusFault_Handler
0x08000190: bf00 .. NOP
0x08000192: e7fe .. B 0x8000192 ; BusFault_Handler + 2
i.DebugMon_Handler
DebugMon_Handler
0x08000194: 4770 pG BX lr
i.HardFault_Handler
HardFault_Handler
0x08000196: bf00 .. NOP
0x08000198: e7fe .. B 0x8000198 ; HardFault_Handler + 2
i.MemManage_Handler
MemManage_Handler
0x0800019a: bf00 .. NOP
0x0800019c: e7fe .. B 0x800019c ; MemManage_Handler + 2
i.NMI_Handler
NMI_Handler
0x0800019e: 4770 pG BX lr
i.PendSV_Handler
PendSV_Handler
0x080001a0: 4770 pG BX lr
i.SVC_Handler
SVC_Handler
0x080001a2: 4770 pG BX lr
i.SetSysClock
SetSysClock
0x080001a4: b510 .. PUSH {r4,lr}
0x080001a6: f000f801 .... BL SetSysClockTo72 ; 0x80001ac
0x080001aa: bd10 .. POP {r4,pc}
i.SetSysClockTo72
SetSysClockTo72
0x080001ac: b50c .. PUSH {r2,r3,lr}
0x080001ae: 2000 . MOVS r0,#0
0x080001b0: 9001 .. STR r0,[sp,#4]
0x080001b2: 9000 .. STR r0,[sp,#0]
0x080001b4: 4833 3H LDR r0,[pc,#204] ; [0x8000284] = 0x40021000
0x080001b6: 6800 .h LDR r0,[r0,#0]
0x080001b8: f4403080 @..0 ORR r0,r0,#0x10000
0x080001bc: 4931 1I LDR r1,[pc,#196] ; [0x8000284] = 0x40021000
0x080001be: 6008 .` STR r0,[r1,#0]
0x080001c0: bf00 .. NOP
0x080001c2: 4830 0H LDR r0,[pc,#192] ; [0x8000284] = 0x40021000
0x080001c4: 6800 .h LDR r0,[r0,#0]
0x080001c6: f4003000 ...0 AND r0,r0,#0x20000
0x080001ca: 9000 .. STR r0,[sp,#0]
0x080001cc: 9801 .. LDR r0,[sp,#4]
0x080001ce: 1c40 @. ADDS r0,r0,#1
0x080001d0: 9001 .. STR r0,[sp,#4]
0x080001d2: 9800 .. LDR r0,[sp,#0]
0x080001d4: b918 .. CBNZ r0,0x80001de ; SetSysClockTo72 + 50
0x080001d6: 9801 .. LDR r0,[sp,#4]
0x080001d8: f5b06fa0 ...o CMP r0,#0x500
0x080001dc: d1f1 .. BNE 0x80001c2 ; SetSysClockTo72 + 22
0x080001de: 4829 )H LDR r0,[pc,#164] ; [0x8000284] = 0x40021000
0x080001e0: 6800 .h LDR r0,[r0,#0]
0x080001e2: f4003000 ...0 AND r0,r0,#0x20000
0x080001e6: b110 .. CBZ r0,0x80001ee ; SetSysClockTo72 + 66
0x080001e8: 2001 . MOVS r0,#1
0x080001ea: 9000 .. STR r0,[sp,#0]
0x080001ec: e001 .. B 0x80001f2 ; SetSysClockTo72 + 70
0x080001ee: 2000 . MOVS r0,#0
0x080001f0: 9000 .. STR r0,[sp,#0]
0x080001f2: 9800 .. LDR r0,[sp,#0]
0x080001f4: 2801 .( CMP r0,#1
0x080001f6: d143 C. BNE 0x8000280 ; SetSysClockTo72 + 212
0x080001f8: 4823 #H LDR r0,[pc,#140] ; [0x8000288] = 0x40022000
0x080001fa: 6800 .h LDR r0,[r0,#0]
0x080001fc: f0400010 @... ORR r0,r0,#0x10
0x08000200: 4921 !I LDR r1,[pc,#132] ; [0x8000288] = 0x40022000
0x08000202: 6008 .` STR r0,[r1,#0]
0x08000204: 4608 .F MOV r0,r1
0x08000206: 6800 .h LDR r0,[r0,#0]
0x08000208: f0200003 ... BIC r0,r0,#3
0x0800020c: 6008 .` STR r0,[r1,#0]
0x0800020e: 4608 .F MOV r0,r1
0x08000210: 6800 .h LDR r0,[r0,#0]
0x08000212: f0400002 @... ORR r0,r0,#2
0x08000216: 6008 .` STR r0,[r1,#0]
0x08000218: 481a .H LDR r0,[pc,#104] ; [0x8000284] = 0x40021000
0x0800021a: 6840 @h LDR r0,[r0,#4]
0x0800021c: 4919 .I LDR r1,[pc,#100] ; [0x8000284] = 0x40021000
0x0800021e: 6048 H` STR r0,[r1,#4]
0x08000220: 4608 .F MOV r0,r1
0x08000222: 6840 @h LDR r0,[r0,#4]
0x08000224: 6048 H` STR r0,[r1,#4]
0x08000226: 4608 .F MOV r0,r1
0x08000228: 6840 @h LDR r0,[r0,#4]
0x0800022a: f4406080 @..` ORR r0,r0,#0x400
0x0800022e: 6048 H` STR r0,[r1,#4]
0x08000230: 4608 .F MOV r0,r1
0x08000232: 6840 @h LDR r0,[r0,#4]
0x08000234: f420107c .|. BIC r0,r0,#0x3f0000
0x08000238: 6048 H` STR r0,[r1,#4]
0x0800023a: 4608 .F MOV r0,r1
0x0800023c: 6840 @h LDR r0,[r0,#4]
0x0800023e: f44010e8 @... ORR r0,r0,#0x1d0000
0x08000242: 6048 H` STR r0,[r1,#4]
0x08000244: 4608 .F MOV r0,r1
0x08000246: 6800 .h LDR r0,[r0,#0]
0x08000248: f0407080 @..p ORR r0,r0,#0x1000000
0x0800024c: 6008 .` STR r0,[r1,#0]
0x0800024e: bf00 .. NOP
0x08000250: 480c .H LDR r0,[pc,#48] ; [0x8000284] = 0x40021000
0x08000252: 6800 .h LDR r0,[r0,#0]
0x08000254: f0007000 ...p AND r0,r0,#0x2000000
0x08000258: 2800 .( CMP r0,#0
0x0800025a: d0f9 .. BEQ 0x8000250 ; SetSysClockTo72 + 164
0x0800025c: 4809 .H LDR r0,[pc,#36] ; [0x8000284] = 0x40021000
0x0800025e: 6840 @h LDR r0,[r0,#4]
0x08000260: f0200003 ... BIC r0,r0,#3
0x08000264: 4907 .I LDR r1,[pc,#28] ; [0x8000284] = 0x40021000
0x08000266: 6048 H` STR r0,[r1,#4]
0x08000268: 4608 .F MOV r0,r1
0x0800026a: 6840 @h LDR r0,[r0,#4]
0x0800026c: f0400002 @... ORR r0,r0,#2
0x08000270: 6048 H` STR r0,[r1,#4]
0x08000272: bf00 .. NOP
0x08000274: 4803 .H LDR r0,[pc,#12] ; [0x8000284] = 0x40021000
0x08000276: 6840 @h LDR r0,[r0,#4]
0x08000278: f000000c .... AND r0,r0,#0xc
0x0800027c: 2808 .( CMP r0,#8
0x0800027e: d1f9 .. BNE 0x8000274 ; SetSysClockTo72 + 200
0x08000280: bd0c .. POP {r2,r3,pc}
$d
0x08000282: 0000 .. DCW 0
0x08000284: 40021000 ...@ DCD 1073876992
0x08000288: 40022000 . .@ DCD 1073881088
$t
i.SysTick_Handler
SysTick_Handler
0x0800028c: 4770 pG BX lr
0x0800028e: 0000 .. MOVS r0,r0
i.SystemInit
SystemInit
0x08000290: b510 .. PUSH {r4,lr}
0x08000292: 4813 .H LDR r0,[pc,#76] ; [0x80002e0] = 0x40021000
0x08000294: 6800 .h LDR r0,[r0,#0]
0x08000296: f0400001 @... ORR r0,r0,#1
0x0800029a: 4911 .I LDR r1,[pc,#68] ; [0x80002e0] = 0x40021000
0x0800029c: 6008 .` STR r0,[r1,#0]
0x0800029e: 4608 .F MOV r0,r1
0x080002a0: 6840 @h LDR r0,[r0,#4]
0x080002a2: 4910 .I LDR r1,[pc,#64] ; [0x80002e4] = 0xf8ff0000
0x080002a4: 4008 .@ ANDS r0,r0,r1
0x080002a6: 490e .I LDR r1,[pc,#56] ; [0x80002e0] = 0x40021000
0x080002a8: 6048 H` STR r0,[r1,#4]
0x080002aa: 4608 .F MOV r0,r1
0x080002ac: 6800 .h LDR r0,[r0,#0]
0x080002ae: 490e .I LDR r1,[pc,#56] ; [0x80002e8] = 0xfef6ffff
0x080002b0: 4008 .@ ANDS r0,r0,r1
0x080002b2: 490b .I LDR r1,[pc,#44] ; [0x80002e0] = 0x40021000
0x080002b4: 6008 .` STR r0,[r1,#0]
0x080002b6: 4608 .F MOV r0,r1
0x080002b8: 6800 .h LDR r0,[r0,#0]
0x080002ba: f4202080 .. BIC r0,r0,#0x40000
0x080002be: 6008 .` STR r0,[r1,#0]
0x080002c0: 4608 .F MOV r0,r1
0x080002c2: 6840 @h LDR r0,[r0,#4]
0x080002c4: f42000fe ... BIC r0,r0,#0x7f0000
0x080002c8: 6048 H` STR r0,[r1,#4]
0x080002ca: f44f001f O... MOV r0,#0x9f0000
0x080002ce: 6088 .` STR r0,[r1,#8]
0x080002d0: f7ffff68 ..h. BL SetSysClock ; 0x80001a4
0x080002d4: f04f6000 O..` MOV r0,#0x8000000
0x080002d8: 4904 .I LDR r1,[pc,#16] ; [0x80002ec] = 0xe000ed08
0x080002da: 6008 .` STR r0,[r1,#0]
0x080002dc: bd10 .. POP {r4,pc}
$d
0x080002de: 0000 .. DCW 0
0x080002e0: 40021000 ...@ DCD 1073876992
0x080002e4: f8ff0000 .... DCD 4177461248
0x080002e8: fef6ffff .... DCD 4277600255
0x080002ec: e000ed08 .... DCD 3758157064
$t
i.UsageFault_Handler
UsageFault_Handler
0x080002f0: bf00 .. NOP
0x080002f2: e7fe .. B 0x80002f2 ; UsageFault_Handler + 2
i.__scatterload_copy
__scatterload_copy
0x080002f4: e002 .. B 0x80002fc ; __scatterload_copy + 8
0x080002f6: c808 .. LDM r0!,{r3}
0x080002f8: 1f12 .. SUBS r2,r2,#4
0x080002fa: c108 .. STM r1!,{r3}
0x080002fc: 2a00 .* CMP r2,#0
0x080002fe: d1fa .. BNE 0x80002f6 ; __scatterload_copy + 2
0x08000300: 4770 pG BX lr
i.__scatterload_null
__scatterload_null
0x08000302: 4770 pG BX lr
i.__scatterload_zeroinit
__scatterload_zeroinit
0x08000304: 2000 . MOVS r0,#0
0x08000306: e001 .. B 0x800030c ; __scatterload_zeroinit + 8
0x08000308: c101 .. STM r1!,{r0}
0x0800030a: 1f12 .. SUBS r2,r2,#4
0x0800030c: 2a00 .* CMP r2,#0
0x0800030e: d1fb .. BNE 0x8000308 ; __scatterload_zeroinit + 4
0x08000310: 4770 pG BX lr
0x08000312: 0000 .. MOVS r0,r0
i.main
main
0x08000314: 2004 . MOVS r0,#4
0x08000316: 4905 .I LDR r1,[pc,#20] ; [0x800032c] = 0x20000000
0x08000318: 6008 .` STR r0,[r1,#0]
0x0800031a: 2005 . MOVS r0,#5
0x0800031c: 4904 .I LDR r1,[pc,#16] ; [0x8000330] = 0x20000004
0x0800031e: 6008 .` STR r0,[r1,#0]
0x08000320: 2006 . MOVS r0,#6
0x08000322: 4904 .I LDR r1,[pc,#16] ; [0x8000334] = 0x20000008
0x08000324: 6008 .` STR r0,[r1,#0]
0x08000326: bf00 .. NOP
0x08000328: e7fe .. B 0x8000328 ; main + 20
$d
0x0800032a: 0000 .. DCW 0
0x0800032c: 20000000 ... DCD 536870912
0x08000330: 20000004 ... DCD 536870916
0x08000334: 20000008 ... DCD 536870920
$d.realdata
Region$$Table$$Base
0x08000338: 08000358 X... DCD 134218584
0x0800033c: 20000000 ... DCD 536870912
0x08000340: 0000000c .... DCD 12
0x08000344: 080002f4 .... DCD 134218484
0x08000348: 08000364 d... DCD 134218596
0x0800034c: 2000000c ... DCD 536870924
0x08000350: 00000404 .... DCD 1028
0x08000354: 08000304 .... DCD 134218500
Region$$Table$$Limit
3.3通过反汇编分析__main
找到Reset_Handler段后,先从0x08000291进入SystemInit(),之后将0x000168地址处的内容加载到R0,跳转到R0所指向的地址处,即__main
在0x0x8000130处,将0x0x000144地址处的内容设置到SP寄存器中,再次初始化栈。之后相对跳转至__scatterload。
在__scatterload开头处,将0x08000188地址里的内容与0x0800018c地址里的内容分别加载到R4与R5,为了搞清楚0x08000338和0x08000358这两个地址是什么含义,查看反汇编在0x08000338处对内存的定义即可知晓答案。
可以知道是一个名为Region$$Table的数组,观察数据内容可以知道其具体含义如下:
| 内容 | 值 |
|---|---|
| RW段在FLASH的起始地址 | 0x08000358 |
| RW段在RAM的起始地址 | 0x20000000 |
| RW段长度 | 0x0000000c |
| RW段拷贝函数地址 | 0x080002F4 |
| RW段在FLASH的结束地址 | 0x08000364 |
| ZI段在RAM的起始地址 | 0x2000000C |
| ZI段长度 | 0x00000404 |
| ZI段初始化函数地址 | 0x08000304 |
因此,LDR R4,[PC, #24] 和 LDR R5,[PC, #28] 意在得到Region$$Table的起始长度与终止长度。回到刚才的地方接着往下分析__scatterload,PC指向0x08000180,比较R4与R5,若结果不等,跳转到0x08000172
此处将RW段拷贝函数地址存入R0,再往bit0写入1后存入R3,并将R0-R2处的内容设置为Region$$Table的前三项。这里介绍一下CM3下的一些细节。
1.ARM与THUMB指令集的表示:
CM3架构以bit0表示该指令为ARM指令集还是THUMB指令集。由于CM3架构只支持THUMB-2指令集,故BLX/BX指令的Bit0应该被事先置高。
2.AAPCS(Procedure Call Standard for the ARM Architecture):
详细可以参考STM32程序调用规则(AAPCS):最新的ARM子程序调用规则-CSDN博客
此处的{R0-R2}被用作函数参数进行传递,给到R3所指向的函数地址__scatterload_copy(0x080002F4)。
在__scatterload_copy中R2作为计数器,R0作为指向加载地址的指针,R2作为指向链接地址的指针,通过循环的方式将Flash上的RW内容搬运到RAM中。完成后通过LR返回。
如此一来还不够,由于返回后R4=R4+0x10仍然不等于R5,因此还会再次回到0x08000172,从Region$$Table中取出函数地址__scatterload_zeroinit(0x08000304)并跳转执行。
这段代码的作用即清空ZI段,R0 = 0,R1指向要清空的RAM地址,R2作为计数器,逐个清空指定RAM区的内容。
完成上述操作后即可进入下一阶段__main_after_scatterload。
在__main_after_scatterload中跳转进入0x08000314,正式进入main函数
四、scatter分散加载
4.1修改ZI段的链接地址
通过修改./Objects/xxx.sct可以为自己的程序指定各个段的链接地址,此处将原先RW_IRAM1中的.ANY(+ZI)段放到ZI_IRAM1,中,其起始地址变为从0x20000800开始。
编译程序,查看.map文件可以看到栈被链接至0x20000800地址处,合理猜测此时__scatterload_zeroinit会将0x20000800到0x20000c00处内容清空。
4.2多段下scatter的行为
使用__attribute__((at(x)))
此处通过__attribute__((at(x)))的编译器指令可以将变量定义到指定内存处。
定义完毕后编译程序,查看.map文件。
发现g_c,g_d,g_e的链接地址确实在0x20000500~0x20000508,此时通过反汇编分析scatter的行为是否发生变化:
似乎并没有发生什么变化,再来看看Region$$Table,RW-data的大小居然变成了0x050c!
由此可见使用__attribute__((at(x)))的方法在RAM中任意地址定义变量时,如果定义变量的地址远离RW段的起始定义地址,一定会导致大片的空闲空间参与分散加载,不仅在加载时造成时间上的浪费,“小碎片”形式的变量定义更是会要命的占据Flash空间,从编译结果输出中便可以看出来。
Build started: Project: Project
*** Using Compiler 'V5.06 update 7 (build 960)', folder: 'C:\Keil_v5\ARM\ARMCC\Bin'
Build target 'Target 1'
linking...
Program Size: Code=600 RO-data=336 RW-data=1292 ZI-data=1024
这就是三个int变量引起的flash血案... 那么有没有一种更好的方式让这些变量参与分散加载,而不要去浪费宝贵的flash,同时也节约scatter的时间,答案是肯定的,我们只需要在xxx.sct文件中额外指定新的段。
使用__attribute__((at(x)))配合xxx.sct
如图指定新的执行域TEST_IRAM 0x20000500 0x100,并将所有.o文件中的TEST段链接到此处,再次编译查看.map文件。
可以看到工具链自动为我们把g_c,g_d,g_e并入了TEST段,此时的程序后的编译大小为
.\Objects\Template.sct(22): warning: L6314W: No section matches pattern *.o(TEST).
Program Size: Code=536 RO-data=352 RW-data=20 ZI-data=1024
Finished: 0 information, 1 warning and 0 error messages.
回归正常,再看scatter的反汇编行为。
看似没有发生什么变化,再来看Region$$Table,发现Table整体长度从原先的0x20变为了0x30,多出的0x10是什么呢,仔细看看0x08000188地址处的定义
可以知道是一个名为Region$$Table的数组,观察数据内容可以知道其具体含义如下:
| 内容 | 值 |
|---|---|
| RW段在FLASH的起始地址 | 0x08000378 |
| RW段在RAM的起始地址 | 0x20000000 |
| RW段长度 | 0x00000008 |
| RW段拷贝函数地址 | 0x080002F4 |
| RW段在FLASH的结束地址 | 0x08000380 |
| TEST段在RAM的起始地址 | 0x20000500 |
| TEST段数据长度 | 0x0000000C |
| TEST段拷贝函数地址 | 0x080002F4 |
| TEST段在FLASH的起始地址 | 0x08000380 |
| ZI段在RAM的起始地址 | 0x20000800 |
| ZI段长度 | 0x00000400 |
| ZI段初始化函数地址 | 0x08000304 |
可以在这种情况下scatter会去拷贝一个新的段,这样一来在既节省空间又节省时间的情况下完成了数据的分散加载,岂不美哉?
使用__attribute__((section("TEST")))配合xxx.sct
采用上述__attribute__((at(x)))配合xxx.sct的方式已经可以解决问题,但是却产生了其它几个名为.ARM.__AT_0x20000500,.ARM.__AT_0x20000504,.ARM.__AT_0x20000508的段,这会降低链接器的链接的效率,因此,最好的解决方法是使用 __attribute __((section("TEST")))。
如此一来,生成的.map文件也清爽很多。
五、Mircolib
实际上,上述代码是在用户勾选了“USE Mircolib”选项前提下得到的,而在取消使用mirco lib,使用默认arm lib时,__main函数的情况又有所不同,然而具体的分散加载方式却与使用“USE Mircolib”时基本一致,此处不再赘述。