【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】
学习stm32代码的时候,关于汇编文件,大家一般都会参考官方给出的汇编文件。通常情况下,不会自己去写汇编文件。特别是汇编文件的最后一行,大家都会把__main看成是直接进入main函数。后面通过反汇编,发现情况并不是这样的。我们编写代码,除了keil工程中的内容,还有MicroLib库,这一点常常被我们忽视。
1、之前的汇编代码
; Reset handler
Reset_Handler PROC
EXPORT Reset_Handler [WEAK]
IMPORT __main
IMPORT SystemInit
LDR R0, =SystemInit
BLX R0
LDR R0, =__main
BX R0
ENDP
汇编代码这部分呢,大家一般都不会陌生,我们也通常都认为是__main直接跳转到了自定义的main函数。但是实际情况,我们可以通过反汇编来确认。
2、如何生成反汇编文件
在项目的User tab配置中,添加这样的命令,就可以在project下面生成dis文件,
fromelf --text -a -c --output=Fire_F103VE.dis ../Output/Fire_F103VE.axf
这样在最后的axf生成的同时,也能生成dis文件。如果需要生成bin文件,也是类似的做法。
fromelf --bin --output Fire_F103VE.bin ../Output/Fire_F103VE.axf
3、找到Reset_Handler位置
首先我们找到Reset_Handler位置,
__Vectors
0x08000000: 20000410 ... DCD 536871952
0x08000004: 08000145 E... DCD 134218053
0x08000008: 08000d81 .... DCD 134221185
一般向量的第二个选项就是reset入口,不过mcu很奇怪,入口地址都要减去1才是正确的地址。也就是说,这里的Reset_Handler其实是08000144,
Reset_Handler
0x08000144: 4806 .H LDR r0,[pc,#24] ; [0x8000160] = 0x8000e17
0x08000146: 4780 .G BLX r0
0x08000148: 4806 .H LDR r0,[pc,#24] ; [0x8000164] = 0x8000131
0x0800014a: 4700 .G BX r0
我们发现了,这边pc最后跳转的地址是8000131,因为需要减去1,那就是8000130,
_main_stk
0x08000130: f8dfd00c .... LDR sp,__lit__00000000 ; [0x8000140] = 0x20000410
.ARM.Collect$$$$00000004
_main_scatterload
0x08000134: f000f82a ..*. BL __scatterload ; 0x800018c
这边执行不久,就跳转到了__scatterload,
__scatterload
__scatterload_rt2
0x0800018c: 4c06 .L LDR r4,[pc,#24] ; [0x80001a8] = 0x8000ec4
0x0800018e: 4d07 .M LDR r5,[pc,#28] ; [0x80001ac] = 0x8000ee4
0x08000190: e006 .. B 0x80001a0 ; __scatterload + 20
0x08000192: 68e0 .h LDR r0,[r4,#0xc]
0x08000194: f0400301 @... ORR r3,r0,#1
0x08000198: e8940007 .... LDM r4,{r0-r2}
0x0800019c: 4798 .G BLX r3
0x0800019e: 3410 .4 ADDS r4,r4,#0x10
0x080001a0: 42ac .B CMP r4,r5
0x080001a2: d3f6 .. BCC 0x8000192 ; __scatterload + 6
0x080001a4: f7ffffc8 .... BL __main_after_scatterload ; 0x8000138
快结束的时候,又跳转到了__main_after_scatterload,
__main_after_scatterload
_main_clock
_main_cpp_init
_main_init
0x08000138: 4800 .H LDR r0,[pc,#0] ; [0x800013c] = 0x8000e75
0x0800013a: 4700 .G BX r0
这边貌似要回到main函数了,看这里的地址是8000e75,那就是8000e74,
i.main
main
0x08000e74: bf00 .. NOP
0x08000e76: f7ffff9e .... BL SystemClock_Config ; 0x8000db6
0x08000e7a: f7ffff2b ..+. BL LED_GPIO_Config ; 0x8000cd4
0x08000e7e: e012 .. B 0x8000ea6 ; main + 50
0x08000e80: 2200 ." MOVS r2,#0
0x08000e82: f44f5100 O..Q MOV r1,#0x2000
0x08000e86: 4808 .H LDR r0,[pc,#32] ; [0x8000ea8] = 0x40011000
0x08000e88: f7fffb4a ..J. BL HAL_GPIO_WritePin ; 0x8000520
0x08000e8c: f44f707a O.zp MOV r0,#0x3e8
0x08000e90: f7fff992 .... BL HAL_Delay ; 0x80001b8
0x08000e94: 2201 ." MOVS r2,#1
0x08000e96: 0351 Q. LSLS r1,r2,#13
0x08000e98: 4803 .H LDR r0,[pc,#12] ; [0x8000ea8] = 0x40011000
0x08000e9a: f7fffb41 ..A. BL HAL_GPIO_WritePin ; 0x8000520
0x08000e9e: f44f707a O.zp MOV r0,#0x3e8
0x08000ea2: f7fff989 .... BL HAL_Delay ; 0x80001b8
0x08000ea6: e7eb .. B 0x8000e80 ; main + 12
看到上面的代码,大约可以验证到我们的猜测了。
4、总结
之前在做linux soc芯片汇编文件编写的时候,中断初始化、bss初始化、sp初始化、已初始化全局变量copy、main函数跳转,这些都是基本的内容。后面自己看mcu代码的时候,却没有发现这部分内容,当时觉得很诧异,直到后来自己看了axf文件的反汇编内容之后,才晓得ide和背后的编译器帮我们做了很多事情。刚才说的这一切,都在main函数调用之前准备好了。但恰恰这一点,对于我们debug调试、分析和boot 加载研究很有帮助。