STM32_從SystemInit、__main到main() 已修正


STM32 的 SystemInit() 和 __main

Author by [YuCloud](https://www.cnblogs.com/yucloud/)

上篇文章 STM32啟動代碼分析及其匯編學習-ARM 分析了 .S 啟動文件 ,這次來研究一下 .S 啟動文件之后執行到 main() 的流程

STM32 總體啟動順序

.s啟動文件 -> 中斷處理函數外部定義 -> SystemInit() -> SetSysClock -> __main -> main()

其中,段的鏈接順序:

  • 截圖里的中斷處理函數外部定義位於 stm32f4xx_it.c(當然這個什么名字都可以,只要在 Keil 項目里且函數名為啟動文件里定義的中斷處理符號名
  • SystemInit()SystemCoreClockUpdateSetSysClock 三個函數均位於 system_stm32f4xx.c

image-20210817103220988
函數 SystemCoreClockUpdate 是用來在時鍾配置改變后刷新時鍾的.
(注意:這只是段連接順序,不是函數調用,之前犯了錯誤,調試后發現應該按照實際匯編跳轉執行順序,這里向各位道歉.但段必須在函數之前才能使用,這也是C語言函數為什么需要先聲明后使用

各函數作用 see: https://www.keil.com/pack/doc/CMSIS/Core/html/group__system__init__gr.html

SystemInit()

用 VSCode+Keil 編程環境,在項目里全局搜索 SystemInit

找到4個文件有用的

image-20210816163618415

  1. 這是個 map 文件,里面是鏈接編譯后對應的索引/映射

image-20210816162744108

  1. 這是 list 文件,也就是鏈接后的匯編信息列表

image-20210816163814583

  1. 這是頭文件的聲明

image-20210816163105826

  1. 上面頭文件對應的C源文件,那么函數定義就在這里了

image-20210816163413317

其中還調用了 SetSysClock

SystemInit 很簡單,查看一下 STM32 手冊里的寄存器即可

SetSysClock

顧名思義

__main

回顧一下

image-20210817103220988

這里的

startup_stm32f401xx.o(.text) refers to entry.o(.ARM.Collect$$$$00000000) for __main

是個 .text 段,應該是 Keil 通過ARM匯編成 entry.o 鏈接到二進制文件里

總之這個 __main 會調用 system_stm32f4xx.c 里的 SystemCoreClockUpdate 函數,然后再調用我們c語言里的 main()。

image-20210817105141824

但由於網上根本搜不到中文資料,因此我決定逆向

把Object目錄里需要的文件復制出來,用 IDA 打開axf文件,格式選ARM小端(STM32是小端的)

image-20210817110146752

確認后會提示ARM有兩種指令集,並告訴你如何用 IDA 操作 (ALT+G切換模式)

image-20210817110316822

找到 Reset_Handler,這里二進制丟失了匯編的Label信息,所以反匯編后,名字有所不同

image-20210817111533119

Enter 進入查看函數定義,由於上面有SystemInit()的源碼,所以用不着反編譯,我們來看 __main 也就是這里的 _main_stk 函數的定義

__main本體

雖然是 __main ,反編譯名為 _main_stk()。

因為二進制並沒有包含匯編所有東西,所以反匯編的時候,有些名字只能通過智能推斷。

image-20210817112421632

LDR.W           SP, =BuildAttributes$$THM_ISAv4$E$P$D$K$B$S$7EM$VFPi3$EXTD16$VFPS$VFMA$PE$A_L22UL41UL21$X_L11$S22US41US21$IEEE1$IW$USESV6$_STKCKD$USESV7$_SHL$OTIME$ROPI$EBA8$MICROLIB$REQ8$PRES8$EABIv2

人為斷句一下,方便閱讀

BuildAttributes$
$THM_ISAv4
$E$P$D
$K$B$S
$7EM
$VFPi3
$EXTD16
$VFPS
$VFMA
$PE
$A_L22UL41UL21
$X_L11
$S22US41US21
$IEEE1
$IW
$USESV6
$_STKCKD
$USESV7
$_SHL
$OTIME
$ROPI
$EBA8
$MICROLIB
$REQ8
$PRES8
$EABIv2

看起來是一些編譯宏的拼接,Enter 一下,發現是這些:

image-20210817130802910

image-20210817130827967

F5 反編譯成c源碼(當然反編譯只是根據反匯編結果再反編譯成c,匯編里可沒有保留c的所有東西(變量名、指針都是沒有的)

image-20210817112339036

這里介紹一下:IDA 的標簽頁名: IDA View 就是反匯編結果(按空格可切換流程圖和文本模式),Pseudocode 就是反編譯結果(c源碼模式)

image-20210817111943645

main_init(v0)

繼續看

image-20210817112503379

這里 IDA 給我們注釋了,是把 __scatterload_rt2 程序的返回值寫入 R0寄存器,

__scatterload_rt2

用 Enter 進入不了 __scatterload_rt2 程序,IDA直接顯示匯編給我們,也就是說這是一段匯編

image-20210817114511599

這里還調用了兩個程序 Region$$Table$$BaseRegion$$Table$$Limit

image-20210817114013886

image-20210817113954063

The DCD directive allocates one or more words of memory, aligned on four-byte boundaries, and defines the initial runtime contents of the memory.

DCD 指令:為一或多個 Word 分配內存,四字節對齊,並定義初始運行時的內存內容(也就是向內存填充 4字節32位 的內容)

又根據網上的資料和上文,

main is your main procedure form main.c file, once __main is an internal procedure created by Keil toolchain which is calling at the end your main, but before it is initializing all variables (copying variables from FLASH to proper positions in RAM). In gcc it is seen explicitly, in Keil you can see it within debug process.

__main 是由 Keil 工具鏈創建的內部過程,它初始化所有變量(將變量從 FLASH 復制到 RAM 中的適當位置),並在最后調用您的 main,

在 gcc 中它是明確可見的,在 Keil 中你可以在調試過程中看到它

Keil 也是用了gcc,因此我們參考 GCC 的文檔-Initialization

If no init section is available, when GCC compiles any function called main (or more accurately, any function designated as a program entry point by the language front end calling expand_main_function), it inserts a procedure call to __main as the first executable code after the function prologue. The __main function is defined in libgcc2.c and runs the global constructors.

The compiler in Arm® Compiler 6 is based on Clang and LLVM technology. As such, it provides a high degree of compatibility with GCC.

當然 Keil 也可以使用 GCC,見 Home » Creating Applications » Tips and Tricks » GNU C Compiler Support

也就是說,__main 是庫自帶的東西,在編譯時會由編譯器鏈接到二進制程序里

猜測:Keil 把用到的變量編譯在了 entry.o,然后把其二進制直接用DCD寫入內存(因此我們看不到其原始信息)。

官方文檔 https://www.keil.com/support/man/docs/armclang_intro/armclang_intro_pge1362066004603.htm

image-20210817124747625

這個 Region$$Table 正是包含了本節提到的兩個看不懂的程序

根據網上的資料,得出 Region$$Table$$Base 是Region$$Table的起始地址, Region$$Table$$Limit是Region$$Table的末尾地址。這兩個共同組合了 Region$$Table 本體。

關於 Region$$Table 本體的說明

  • Region$$Table section
    • [英] containing the addresses of the code and data to be copied or decompressed.
    • [中] 包含了要被復制或解壓縮的代碼和數據的地址

一些概念:

Code (代碼段)

ZI (Zero-Inintialize Data段)

RO (ReadOnly Data段)

RW (ReacWrite Data段)

占用計算:

FLASH 儲存:Code + RO + RW

RAM 內存: RW + ZI

在項目的 Object 目錄下的 sct 文件可見:

其中 InRoot$$Sections 包含了 Region$$Table

image-20210817133806694

; *************************************************************
; *** Scatter-Loading Description File generated by uVision ***
; *************************************************************

LR_IROM1 0x08000000 0x00080000  {    ; load region size_region
  ER_IROM1 0x08000000 0x00080000  {  ; load address = execution address
   *.o (RESET, +First)
   *(InRoot$$Sections)
   .ANY (+RO)
   .ANY (+XO)
  }
  RW_IRAM1 0x20000000 0x00018000  {  ; RW data
   .ANY (+RW +ZI)
  }
}


總之這部分超出了我的能力,本文純作拋磚引玉,這里提供一些關鍵資料:

只能說和 RW Data 壓縮息息相關,DCD那里應該就是ARM的壓縮指令。

實在着急着秋招,沒心思研究這些指令的含義了...

_main_init

反匯編:

image-20210817113139674

反編譯:

image-20210817113129770

反編譯名字有些不一樣,容易混淆,但根據調用流程來看,

這里的 a2,a3是R1 R2寄存器用於存放和傳遞主函數參數,a1 是 R0寄存器用於函數調用

main()

也就是我們 C語言世界的 main 函數,你自己寫的包含 main() 的 c源碼文件

當然我寫完了,才發現網上也有類似的文章

https://blog.csdn.net/hgsdfghdfsd/article/details/103812484

在這里插入圖片描述

但是我的和它不太一樣,區別在於版本

In RVCT v2.0 and earlier, only the __main section and the region tables had to be placed in a root region.

In RVCT v2.1 and above, RW data compression requires that additional sections (such as __dc*.o sections) be placed in a root region.

see: https://developer.arm.com/documentation/dui0206/h/Bhccgbbe

可能因為我是從 STM32 官方手動下載STM32F4xx DSP and Standard Peripherals Library 1.4.0 庫,而不是 Keil 自帶最新的,問題不大,都是對的


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM