AliOS Things的啓動過程分析(一)
在本篇文章中,我們以developerkit開發板爲例,介紹AliOS Things的啓動過程。AliOS Things支持多種工具鏈進行編譯鏈接的方式生成可執行文件,在這裏主要是以GCC的方式(更加通用)來進行介紹。建議可以先閱讀AliOS Things的編譯體系,對整個的編譯流程有個簡單的瞭解。鏈接
一個可執行文件生成首先需要確定宿主平臺(windows、linux等)、再確定所使用的工具鏈、確定所有使用的組件源文件等等、最後根據指定的鏈接腳本進行連接生成可執行文件。而啓動過程的理解,也需要從連接腳本和工具鏈講起。
以developerkit爲例,在board/developer目錄下的developerkit.mk文件當中會指定所使用的啓動文件以及選擇指定的編譯工具鏈。
如下所示,爲截取的少量代碼,主要是指定所使用的啓動文件以及根據條件來選擇所使用的鏈接腳本文件。在developerkit.mk文件中還定義了SoC的相關信息,指定了所使用的源文件等,詳情請自行參考該文件。
ifeq ($(COMPILER), armcc)
$(NAME)_SOURCES += startup_stm32l496xx_keil.s
else ifeq ($(COMPILER), iar)
$(NAME)_SOURCES += startup_stm32l496xx_iar.s
else
$(NAME)_SOURCES += startup_stm32l496xx.s
endif
#以上爲確定所使用的編譯工具,是armcc / iar /或者是gcc
#並選擇所使用的啓動文件
#下述代碼是根據編譯工具來指定其加載文件,在該文件中定義了一些基本的啓動加載信息
ifeq ($(COMPILER),armcc)
GLOBAL_LDFLAGS += -L --scatter=board/developerkit/STM32L496.sct
else ifeq ($(COMPILER),iar)
GLOBAL_LDFLAGS += --config board/developerkit/STM32L496.icf
else
ifeq ($(post_run),1)
GLOBAL_LDFLAGS += -T board/developerkit/STM32L496VGTx_FLASH.ld
GLOBAL_DEFINES += VECT_TAB_OFFSET=0x4000
GLOBAL_DEFINES += USING_FLAT_FLASH
else
ifeq ($(MBINS),)
GLOBAL_LDFLAGS += -T board/developerkit/STM32L496VGTx_FLASH.ld
else ifeq ($(MBINS),app)
GLOBAL_LDFLAGS += -T board/developerkit/STM32L496VGTx_FLASH_app.ld
else ifeq ($(MBINS),kernel)
GLOBAL_LDFLAGS += -T board/developerkit/STM32L496VGTx_FLASH_kernel.ld
endif
endif
endif
接下來,我們將以gcc編譯工具和STM32L496VGTx_FLASH.ld鏈接腳本爲例來進行理解其啓動過程。以下代碼是
STM32L496VGTx_FLASH.ld鏈接腳本文件的部分代碼。該文件所完成的最終目的是將編譯後生成的文件進行連接生成最終的可執行文件。在該文件中定義了堆棧大小,存儲區域的分配大小、以及各種段的定義和存儲。本篇文章中只分析和啓動過程相關的部分代碼定義。
在鏈接文件的起始部分,指定了程序的入口位置是:Reset_Handler。該函數位於startup_stm32l496xx.s文件中,後續會進行講解。並且在後面定義了RAM區的結束地址、堆以及棧的大小,RAM 區域和FLASH區域的地址等。
/* Entry Point */
ENTRY(Reset_Handler)
/* Highest address of the user mode stack */
_estack = 0x20050000; /* 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 */
MEMORY
{
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 320K
FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 1024K
}
接下來分析一些後續啓動過程可能會用到的段定義,詳細的文件內容可以實際參考.ld鏈接腳本。在此只介紹和啓動過程相關的定義。在段定義的開始,首先存儲到FLASH區域的是向量表,向量表的定義也位於
startup_stm32l496xx.s文件中。接下來是常見的代碼段的存儲、只讀數據段的存儲以及數據段和bss段的存儲(中間忽略了很多段定義,可以自行參考)
/* Define output sections */
SECTIONS
{
/* The startup code goes first into FLASH */
.isr_vector :
{
. = ALIGN(8);
KEEP(*(.isr_vector)) /* Startup code */
. = ALIGN(8);
} >FLASH
/* The program code and other data goes into FLASH */
.text :
{
. = ALIGN(8);
*(.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(8);
_etext = .; /* define a global symbols at end of code */
} >FLASH
/* Constant data goes into FLASH */
.rodata :
{
. = ALIGN(8);
*(.rodata) /* .rodata sections (constants, strings, etc.) */
*(.rodata*) /* .rodata* sections (constants, strings, etc.) */
. = ALIGN(8);
} >FLASH
/* used by the startup to initialize data */
_sidata = LOADADDR(.data);
/* Initialized data sections goes into RAM, load LMA copy after code */
.data :
{
. = ALIGN(8);
_sdata = .; /* create a global symbol at data start */
*(.data) /* .data sections */
*(.data*) /* .data* sections */
. = ALIGN(8);
_edata = .; /* define a global symbol at data end */
} >RAM AT> FLASH
/* Uninitialized data section */
. = 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
在startup_stm32l496xx.s文件中和啓動過程相關的代碼如下所示:
Reset_Handler是程序啓動後最先執行的代碼(在ld文件中ENTRY指定),這段代碼是由彙編語言編寫的完成的主要功能是:
- 設置sp堆棧指針。將其設置爲_estack 在ld鏈接腳本中將其指定爲
RAM
的截止地址 - 將
.data數據段
的內容拷貝到RAM
區,.data數據段
:用於存放在編譯階段(而非運行時)就能確定的數據,可讀可寫。也是通常所說的靜態存儲區,賦了初值的全局變量、常量和靜態變量都存放在這個域。.data數據段
的定義在ld文件中。 - 清除
bss段
。通常是指用來存放程序中未初始化的全局變量的一塊內存區域。BSS是英文Block Started by Symbol的簡稱。BSS段屬於靜態內存分配。裸機程序需要手動清零。 - 跳轉到SystemInit函數中進行時鐘的初始化。該函數位於board\developerkit\Src\system_stm32l4xx.c文件中。
- 調用應用程序入口函數main函數,main函數位於AliOS-Things\platform\mcu\stm32l4xx_cube\aos\aos.c文件中。
.section .text.Reset_Handler
.weak Reset_Handler
.type Reset_Handler, %function
Reset_Handler:
ldr sp, =_estack /* Atollic update: set stack pointer */
/* Copy the data segment initializers from flash to SRAM */
movs r1, #0
b LoopCopyDataInit
CopyDataInit:
ldr r3, =_sidata
ldr r3, [r3, r1]
str r3, [r0, r1]
adds r1, r1, #4
LoopCopyDataInit:
ldr r0, =_sdata
ldr r3, =_edata
adds r2, r0, r1
cmp r2, r3
bcc CopyDataInit
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.*/
bl main
LoopForever:
b LoopForever
.size Reset_Handler, .-Reset_Handler
以上就是developerkit從系統上電開始運行到系統進入應用程序主代碼(main函數)所要經歷的一些步驟,接下來會針對main函數的一些初始化工作進行講解。因爲是閱讀源碼進行分析的,如果有任何錯誤之處敬請諒解。