STM32_从SystemInit、__main到main()
STM32 的 SystemInit() 和 __main
Author by [YuCloud](https://www.cnblogs.com/yucloud/)
上篇文章 STM32启动代码分析及其汇编学习-ARM 分析了 .S 启动文件 ,这次来研究一下 .S 启动文件之后执行到 main() 的流程
STM32 总体启动顺序
.s启动文件 -> 中断处理函数外部定义 -> SystemInit() -> __main -> SystemCoreClockUpdate -> SetSysClock -> main()
其中
- 截图里的中断处理函数外部定义位于 stm32f4xx_it.c(当然这个什么名字都可以,只要在 Keil 项目里且函数名为
启动文件里定义的中断处理符号名) SystemInit()、SystemCoreClockUpdate、SetSysClock三个函数均位于 system_stm32f4xx.c

SystemInit()
用 VSCode+Keil 编程环境,在项目里全局搜索 SystemInit
找到4个文件有用的

- 这是个 map 文件,里面是链接编译后对应的索引/映射

- 这是 list 文件,也就是链接后的汇编信息列表

- 这是头文件的声明

- 上面头文件对应的C源文件,那么函数定义就在这里了

其中还调用了 SetSysClock
SystemInit 很简单,查看一下 STM32 手册里的寄存器即可
__main
回顾一下

这里的
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()。

但由于网上根本搜不到中文资料,因此我决定逆向
把Object目录里需要的文件复制出来,用 IDA 打开axf文件,格式选ARM小端(STM32是小端的)

确认后会提示ARM有两种指令集,并告诉你如何用 IDA 操作 (ALT+G切换模式)

找到 Reset_Handler,这里二进制丢失了汇编的Label信息,所以反汇编后,名字有所不同

Enter 进入查看函数定义,由于上面有SystemInit()的源码,所以用不着反编译,我们来看 __main 也就是这里的 _main_stk 函数的定义
__main本体
虽然是 __main ,反编译名为 _main_stk()。
因为二进制并没有包含汇编所有东西,所以反汇编的时候,有些名字只能通过智能推断。

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 一下,发现是这些:


F5 反编译成c源码(当然反编译只是根据反汇编结果再反编译成c,汇编里可没有保留c的所有东西(变量名、指针都是没有的)

这里介绍一下:IDA 的标签页名: IDA View 就是反汇编结果(按空格可切换流程图和文本模式),Pseudocode 就是反编译结果(c源码模式)

main_init(v0)
继续看

这里 IDA 给我们注释了,是把 __scatterload_rt2 程序的返回值写入 R0寄存器,
__scatterload_rt2
用 Enter 进入不了 __scatterload_rt2 程序,IDA直接显示汇编给我们,也就是说这是一段汇编

这里还调用了两个程序 Region$$Table$$Base 和 Region$$Table$$Limit


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 callingexpand_main_function), it inserts a procedure call to__mainas the first executable code after the function prologue. The__mainfunction 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

这个 Region$$Table 正是包含了本节提到的两个看不懂的程序
根据网上的资料,得出 Region$$Table$$Base 是Region$$Table的起始地址, Region$$Table$$Limit是Region$$Table的末尾地址。这两个共同组合了 Region$$Table 本体。
关于 Region$$Table 本体的说明
Region$$Tablesection- [英] 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

; *************************************************************
; *** 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)
}
}
总之这部分超出了我的能力,本文纯作抛砖引玉,这里提供一些关键资料:
- FSL之深入理解ARM:Cortex核MCU Region Table存储格式是什么? - 阿莫电子论坛
- RealView Compilation Tools Linker and Utilities Guide - ARM Developer
只能说和 RW Data 压缩息息相关,DCD那里应该就是ARM的压缩指令。
实在着急着秋招,没心思研究这些指令的含义了...
_main_init
反汇编:

反编译:

反编译名字有些不一样,容易混淆,但根据调用流程来看,
这里的 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
__mainsection 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*.osections) 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 自带最新的,问题不大,都是对的
STM32_从SystemInit、__main到main()的更多相关文章
- __main() 和 main() 【转】
因为我们通常在BOOTLOADER中都已做好了比较细致的初始化工作,包括代码的搬运,所以我们最好别再调用库函数__main(),因为__main()作为ADS集成好的库函数,会对系统进行初始化设置,可 ...
- STM32 Startup**.s文件中使用的 __main C函数入口
代码: ; Reset handler Reset_Handler PROC EXPORT Reset_Handler [WEAK] IMPORT SystemInit IMPORT __main L ...
- STM32的例程GPIO的汇编指令初探
任务一:寻找main函数的汇编指令集 任务二:寻找main函数中的SystemClock_Config函数的汇编指令集 寻找main函数的汇编指令集 运行例程中GPIO工程时,总会加载startup_ ...
- 第7章 使用寄存器点亮LED灯
第7章 使用寄存器点亮LED灯 全套200集视频教程和1000页PDF教程请到秉火论坛下载:www.firebbs.cn 野火视频教程优酷观看网址:http://i.youku.com/fir ...
- 第7章 使用寄存器点亮LED灯—零死角玩转STM32-F429系列
第7章 使用寄存器点亮LED灯 全套200集视频教程和1000页PDF教程请到秉火论坛下载:www.firebbs.cn 野火视频教程优酷观看网址:http://i.youku.com/fir ...
- STM32的启动过程分析
对于stm32的启动过程一直心存疑惑.今天找了很多资料,进行了一个大致的分析. 1.cortex M3的复位过程(来自官方资料) 上述开机启动流程比较详细,内容较为全面,但部分步骤可以省略(红字可省略 ...
- 启动文件startup_stm32f40_41xxx.s
一.启动文件,startup_stm32f40x_41xx.s 1.定义 启动文件由汇编编写,是系统上电复位后第一执行的程序. Stack_Size EQU 0x00000400 // 栈的大小可以调 ...
- STM32启动代码分析及其汇编学习-ARM
STM32 启动代码 Author By YuCloud 边看启动文件边学汇编 汇编 see ARM: Assembler User Guide see: https://blog.csdn.net/ ...
- i.MX rt 系列微控制器的学习记录
杂记 前言 我总是很希望自己能产生一种感知电压变化的能力,就像B站上的教学动图中,电流从电源流出时导线就像LED亮起来一样,我将指尖触到导线上就能感受到实时的电压变化.我在上学和工作时经常由于无法理解 ...
随机推荐
- 01_JVM与Java体系结构
JVM发展历程 Sun Classic VM Exact VM 为了解决上一个虚拟机问题,jdk1.2时,sun提供了此虚拟机. Exact Memory Management:准确式内存管理 SUN ...
- 再看Lambda架构
博客原文地址 最*看了一本<大数据系统构建>的书,发现之前对于Lambda架构的理解还是不够深入和清晰. 之前对Lambda架构的理解 Azure文档上有一张Lambda架构的图, 同时也 ...
- 使用Azure Congnitive Services 技术制作AI故事机
引言 前一段时间有幸参加了微软MVP的AI方面的学习挑战赛,对于AI 这个新的领域的技术瞬间勾起了我的学习兴趣. 最近几年,不管是国内还是国外,AI都是一个异常火热的词.比如现在的自动驾驶技术,其实就 ...
- Docker原理:Cgroup
目录 Cgroup 主要功能 术语 参考 Cgroup 全称Linux Control Group, 是Linux内核的一个功能,用来限制.控制与分离一个进程组群的资源(如CPU.内存.磁盘输入输出等 ...
- Linux磁盘管理与文件系统
文章目录一.硬盘结构二.MBR与磁盘分区表示三.磁盘分区结构四.文件系统类型●1.XFS文件系统●2.SWAP,交换文件系统●3.Linux支持的其他文件系统类型五.命令部分--检测并确认新硬盘●1. ...
- Antilibrary能拯救稍后不读吗
从「稍后再读」到「再也不读」 上学时,我有一套自认为很高效的资料搜集工作流.大致流程是浏览到感兴趣或可能有用的信息时,粗略扫过一眼后即用 Pocket 将其保存为稍后再读,随后借助 IFTTT 的某个 ...
- 北京大公司:你是熟悉Map集合吗?
<对线面试官>系列目前已经连载30篇啦,这是一个讲人话面试系列 [对线面试官]Java注解 [对线面试官]Java泛型 [对线面试官] Java NIO [对线面试官]Java反射 &am ...
- 数据结构与算法-排序(二)选择排序(Selection Sort)
摘要 选择排序的逻辑是先遍历比较出序列中最大的,然后把最大的放在最后位置. 遵循这个逻辑,用代码实现时,做到1.减少比较次数之外,这里引入一个新的指标 - 稳定性,2.保证排序过程中的稳定性也是一个优 ...
- C++第三十八篇 -- 研究一下Windows驱动开发(二)--WDM式驱动的加载
基于Windows驱动开发技术详解这本书 一.简单的INF文件剖析 INF文件是一个文本文件,由若干个节(Section)组成.每个节的名称用一个方括号指示,紧接着方括号后面的就是节内容.每一行就是一 ...
- python之 数据类型限制
问题增加类型限制 NameError: name 'List' is not defined def twoSum(self, nums: List[int], target: int) -> ...