0、说明
LPC1115,keil。
本文主要分析启动文件中__main函数的功能。
1、函数入口
文件:startup_LPC11xx.s
1 | Reset_Handler PROC |
这里的__main并不是main()函数,而是在进入C环境之前的预备工作,包括将RW变量从rom复制到ram指定位置,将.bss段清零,最后才跳转到用户的main函数。
可以将编译完成的.axf反汇编,结合map文件来分析__main函数。反汇编配置如下图所示。
“4”框是生成bin文件的命令,也就是最终下载到单片机中的文件。
2、反汇编
1 | __rt_entry |
__decompress函数作用就是将RW数据从rom复制到ram,这些数据在运行过程中会改变,需要在ram中运行。
__scatterload_zeroinit函数的作用是将未初始化的全局变量区域清零。在rom代码区不存在该部分,当程序启动时才会出现在ram区域。Remember, this is the section we put uninitialized static memory in. .bss must be reserved in the memory map, but there is nothing to load, as all variables are initialized to zero.
3、map文件
再来看map文件中关于栈的部分,如下。这里设置heap大小为0,stack大小为0x600,即1536字节。
HEAP 0x10001660 Section 0 startup_lpc11xx.o(HEAP)
STACK 0x10001660 Section 1536 startup_lpc11xx.o(STACK)
Heap_Mem 0x10001660 Data 0 startup_lpc11xx.o(HEAP)
Stack_Mem 0x10001660 Data 1536 startup_lpc11xx.o(STACK)
__initial_sp 0x10001c60 Data 0 startup_lpc11xx.o(STACK)
Code (inc. data) RO Data RW Data ZI Data Debug
20824 2016 7016 1768 5496 160460 Grand Totals
20824 2016 7016 496 5496 160460 ELF Image Totals (compressed)
20824 2016 7016 496 0 0 ROM Totals
=========================================================
Total RO Size (Code + RO Data) 27840 ( 27.19kB)
Total RW Size (RW Data + ZI Data) 7264 ( 7.09kB)
Total ROM Size (Code + RO Data + RW Data) 28336 ( 27.67kB)
“Grand Totals”行显示的RW数据时1768字节长度,为何下面两行是496字节?这可能就是压缩(compressed)的缘故。既然有压缩,那么就要解压缩,也就是上面__decompress函数的作用。
最终的bin文件大小就是28336字节。可以通过生成放的bin文件确认,如下图。
也可以打开bin文件,如下图所示,可以看出文件长度就是0x6eb0(28336).
有些程序会是这样的情况:
这是个演示程序,可以看到RW是8字节。此时的处理函数是__scatterload_copy,与上面所说的__decompress功能并不相同。
1 | 0x000000e8: cc0f .. LDM r4!,{r0-r3} |
4、空间分布
按照上面的分析,得到下图,其中PAD是填充字节,为了保证对齐。
图中右侧可以理解为程序在rom中的存储分布,而左侧是上电后的数据分布。代码区和只读区位置不变,可以直接运行,RW区在初始化时会复制到0x10000000(ram起始地址)开始的区域,接着是.bss区域、堆区、栈区。
一句话解释:
开机上电后,从Region$$Table区域获取相关参数(数据的源地址、目标地址,长度,处理函数),将RW区域数据解压(复制)到ram区域,接着将.bss区域清零,建立堆栈,之后跳转到用户main函数执行。
5、其它
Reset_Handler 0x00000185 Thumb Code 8 startup_lpc11xx.o(.text)
NMI_Handler 0x0000018d Thumb Code 2 startup_lpc11xx.o(.text)
HardFault_Handler 0x0000018f Thumb Code 2 startup_lpc11xx.o(.text)
SVC_Handler 0x00000191 Thumb Code 2 startup_lpc11xx.o(.text)
PendSV_Handler 0x00000193 Thumb Code 2 startup_lpc11xx.o(.text)
下图来自bin文件
前4字节时栈指针,即0x1c60,接着4字节时入口函数,地址为0x0185。栈指针与上图中的__initial_sp一致,而0x0185处的函数为Reset_Handler,如上面map文件所示。继续看bin文件,后续的每4字节为一个地址,与各个中断函数地址相对应,具体含义可以看启动文件中的描述,如下。
1 | __Vectors DCD __initial_sp ; Top of Stack |
* 参考
参考网上诸多资料,没办法一一列出,在此一并致谢。
https://bbs.21ic.com/icview-2934034-1-1.html?fromuser=,该资料比较详细,本文最开始就是参考该文的。
https://blog.csdn.net/weixin_39869569/article/details/104851263
https://blog.csdn.net/shenlong1356/article/details/104597292
https://interrupt.memfault.com/blog/how-to-write-linker-scripts-for-firmware
https://blog.csdn.net/weixin_39869569/article/details/104851263