MDK __main()代碼執行分析

__main()代碼執行分析

STM32啓動代碼主要是分配堆棧及設置向量表,然後跳轉到__main函數。

跳轉具體到代碼段部分如下:

Reset_Handler   PROC
                EXPORT  Reset_Handler             [WEAK]
                IMPORT  __main
                LDR     R0, = __main
                BX      R0
                ENDP

當您看到__main函數時,估計應該有不少人認爲這個是main函數的別名或是編譯之後的名字,否則在啓動代碼中再也無法找到和main相關的字眼了。可事實是,__main和main是完全兩個不同的函數,並且你無法找到__main代碼,因爲這個是編譯器自動創建的。

     查看MDK的文檔,會發現有這麼一句說明:It is automatically created by the linker when it sees a definition of main()。簡單點來說,當編譯器發現定義了main函數,那麼就會自動創建__main。

 


程序經過彙編啓動代碼,執行到__main()後,可以看出有兩個大的函數:

__scatterload():負責RW/RO輸出段從裝載域地址複製到運行域地址,並完成了ZI運行域的初始化工作。

__rt_entry():負責初始化堆棧,完成庫函數的初始化,最後自動跳轉向main()函數。

 

分析__scatterload()函數

執行到__main(),先跳轉到_scatterload下圖紅框框中代碼所示,執行完後,R10和R11就被賦給成了下面兩個值。

Map文件中的symbol




然後執行_scatterload_null代碼,將R10對應地址存放的的4個字copy到R0~R3中,可以看出

R0:0x1000表示的是keyled.o加載域起始地址

R1:0x30000100爲keyled.o運行域地址

R2:0X160爲copy的大小,keyled.o的大小從map文件中得知就是0x160 Byte

R3:0X1E4 是_scatterload_copy 代碼的起始地址,實用BXR3 就能跳轉到_scatterload_copy來複制代碼。

 

跳到_scatterload_copy,開始copy,循環0x16次,每次搬移4個字(16Byte),共搬移0x16*0x10=0x160


複製完keyled.o代碼後,進一步循環到_scatterload_null準備好,ZI段需要清零的地址和範圍

執行完這個循環後

R1:0x30050000 爲ZI段的起始地址

R2:0x618爲ZI段大小,換成十進制是1560.從map文件得知ZI大小就是1560Byte

R3:0x20c 爲_scatterload_zeroinit 的地址


執行下面紅框框中循環體,共清零0x610Byte範圍,然後再執行藍框框中代碼,清零8Byte,總共0x618

ZI段清零(0x30050000~0x30050618)


然後使用BX R14跳轉到0x000001BC處,順序執行到BL  __rt_enty 指令

成功跳轉到__rt_enty函數

 

分析__rt_entry()函數

先調用__user_setup_stackheap()函數來建立堆棧


可以看出在這個函數中,會執行到BL__user_initial_stackheap()函數,這樣也就明白了,爲什麼使用分散加載文件,需要設置__user_initial_stackheap這個函數來設置堆棧空間。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章