发新帖

程序真的是从main函数开始执行的吗?main函数之前究竟发生了什么?

[复制链接]
10782 0

本文包含源代码、原理图、PCB、封装库、中英文PDF等资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
学习单片机的同学都知道,程序是从main函数开始执行的,那么上电后真的是从main函数开始执行吗?在这之前都干了些什么呢?看完下面的文章,你会了解更多。当然这个问题也是很多公司的笔试题,希望你能好好了解一下。

当使用MFC时,我们会认为入口函数是:: InitInstance;当使用WIN32 API时,我们会认为入口函数是WinMain;当我们写个纯粹的C++程序时,入口函数又变成了main;可当我们进入到嵌入式领域,却发现main函数之前还有一段启动代码!

     究竟在main函数之前,发生了什么?如果你觉得已经明白了这个过程,那么请试着回答这个问题:程序是存储到FLASH中的,运行时static变量地址是指向RAM,那么这些static变量的初始值是如何映射到RAM中的?

     我们以STM32F10x的启动代码为例,先看看其完整的源码:
  1. ;/*****************************************************************************/
  2. ;/* STM32F10x.s: Startup file for ST STM32F10x device series                  */
  3. ;/*****************************************************************************/
  4. ;/* <<< Use Configuration Wizard in Context Menu >>>                          */
  5. ;/*****************************************************************************/
  6. ;/* This file is part of the uVision/ARM development tools.                   */
  7. ;/* Copyright (c) 2005-2007 Keil Software. All rights reserved.               */
  8. ;/* This software may only be used under the terms of a valid, current,       */
  9. ;/* end user licence from KEIL for a compatible version of KEIL software      */
  10. ;/* development tools. Nothing else gives you the right to use this software. */
  11. ;/*****************************************************************************/
  12. ;// <h> Stack Configuration
  13. ;//   <o> Stack Size (in Bytes) <0x0-0xFFFFFFFF:8>
  14. ;// </h>
  15. Stack_Size      EQU     0x00000200
  16.                 AREA    STACK, NOINIT, READWRITE, ALIGN=3
  17. Stack_Mem       SPACE   Stack_Size
  18. __initial_sp
  19. ;// <h> Heap Configuration
  20. ;//   <o>  Heap Size (in Bytes) <0x0-0xFFFFFFFF:8>
  21. ;// </h>
  22. Heap_Size       EQU     0x00000000
  23.                 AREA    HEAP, NOINIT, READWRITE, ALIGN=3
  24. __heap_base
  25. Heap_Mem        SPACE   Heap_Size
  26. __heap_limit
  27.                 PRESERVE8
  28.                 THUMB
  29. ; Vector Table Mapped to Address 0 at Reset
  30.                 AREA    RESET, DATA, READONLY
  31.                 EXPORT  __Vectors
  32. __Vectors       DCD     __initial_sp              ; Top of Stack
  33.                 DCD     Reset_Handler             ; Reset Handler
  34.                 DCD     NMI_Handler               ; NMI Handler
  35.                 DCD     HardFault_Handler         ; Hard Fault Handler
  36.                 DCD     MemManage_Handler         ; MPU Fault Handler
  37.                 DCD     BusFault_Handler          ; Bus Fault Handler
  38.                 DCD     UsageFault_Handler        ; Usage Fault Handler
  39.                 DCD     0                         ; Reserved
  40.                 DCD     0                         ; Reserved
  41.                 DCD     0                         ; Reserved
  42.                 DCD     0                         ; Reserved
  43.                 DCD     SVC_Handler               ; SVCall Handler
  44.                 DCD     DebugMon_Handler          ; Debug Monitor Handler
  45.                 DCD     0                         ; Reserved
  46.                 DCD     PendSV_Handler            ; PendSV Handler
  47.                 DCD     SysTick_Handler           ; SysTick Handler
  48.                 ; External Interrupts
  49.                 DCD     WWDG_IRQHandler           ; Window Watchdog
  50.                 DCD     PVD_IRQHandler            ; PVD through EXTI Line detect
  51.                 DCD     TAMPER_IRQHandler         ; Tamper
  52.                 DCD     RTC_IRQHandler            ; RTC
  53.                 DCD     FLASH_IRQHandler          ; Flash
  54.                 DCD     RCC_IRQHandler            ; RCC
  55.                 DCD     EXTI0_IRQHandler          ; EXTI Line 0
  56.                 DCD     EXTI1_IRQHandler          ; EXTI Line 1
  57.                 DCD     EXTI2_IRQHandler          ; EXTI Line 2
  58.                 DCD     EXTI3_IRQHandler          ; EXTI Line 3
  59.                 DCD     EXTI4_IRQHandler          ; EXTI Line 4
  60.                 DCD     DMAChannel1_IRQHandler    ; DMA Channel 1
  61.                 DCD     DMAChannel2_IRQHandler    ; DMA Channel 2
  62.                 DCD     DMAChannel3_IRQHandler    ; DMA Channel 3
  63.                 DCD     DMAChannel4_IRQHandler    ; DMA Channel 4
  64.                 DCD     DMAChannel5_IRQHandler    ; DMA Channel 5
  65.                 DCD     DMAChannel6_IRQHandler    ; DMA Channel 6
  66.                 DCD     DMAChannel7_IRQHandler    ; DMA Channel 7
  67.                 DCD     ADC_IRQHandler            ; ADC
  68.                 DCD     USB_HP_CAN_TX_IRQHandler  ; USB High Priority or CAN TX
  69.                 DCD     USB_LP_CAN_RX0_IRQHandler ; USB Low  Priority or CAN RX0
  70.                 DCD     CAN_RX1_IRQHandler        ; CAN RX1
  71.                 DCD     CAN_SCE_IRQHandler        ; CAN SCE
  72.                 DCD     EXTI9_5_IRQHandler        ; EXTI Line 9..5
  73.                 DCD     TIM1_BRK_IRQHandler       ; TIM1 Break
  74.                 DCD     TIM1_UP_IRQHandler        ; TIM1 Update
  75.                 DCD     TIM1_TRG_COM_IRQHandler   ; TIM1 Trigger and Commutation
  76.                 DCD     TIM1_CC_IRQHandler        ; TIM1 Capture Compare
  77.                 DCD     TIM2_IRQHandler           ; TIM2
  78.                 DCD     TIM3_IRQHandler           ; TIM3
  79.                 DCD     TIM4_IRQHandler           ; TIM4
  80.                 DCD     I2C1_EV_IRQHandler        ; I2C1 Event
  81.                 DCD     I2C1_ER_IRQHandler        ; I2C1 Error
  82.                 DCD     I2C2_EV_IRQHandler        ; I2C2 Event
  83.                 DCD     I2C2_ER_IRQHandler        ; I2C2 Error
  84.                 DCD     SPI1_IRQHandler           ; SPI1
  85.                 DCD     SPI2_IRQHandler           ; SPI2
  86.                 DCD     USART1_IRQHandler         ; USART1
  87.                 DCD     USART2_IRQHandler         ; USART2
  88.                 DCD     USART3_IRQHandler         ; USART3
  89.                 DCD     EXTI15_10_IRQHandler      ; EXTI Line 15..10
  90.                 DCD     RTCAlarm_IRQHandler       ; RTC Alarm through EXTI Line
  91.                 DCD     USBWakeUp_IRQHandler      ; USB Wakeup from suspend
  92.                 AREA    |.text|, CODE, READONLY
  93. ; Reset Handler
  94. Reset_Handler   PROC
  95.                 EXPORT  Reset_Handler             [WEAK]
  96.                 IMPORT  __main
  97.                 LDR     R0, =__main
  98.                 BX      R0
  99.                 ENDP
  100. ; Dummy Exception Handlers (infinite loops which can be modified)               
  101. NMI_Handler     PROC
  102.                 EXPORT  NMI_Handler               [WEAK]
  103.                 B       .
  104.                 ENDP
  105. HardFault_Handler/
  106.                 PROC
  107.                 EXPORT  HardFault_Handler         [WEAK]
  108.                 B       .
  109.                 ENDP
  110. MemManage_Handler/
  111.                 PROC
  112.                 EXPORT  MemManage_Handler         [WEAK]
  113.                 B       .
  114.                 ENDP
  115. BusFault_Handler/
  116.                 PROC
  117.                 EXPORT  BusFault_Handler          [WEAK]
  118.                 B       .
  119.                 ENDP
  120. UsageFault_Handler/
  121.                 PROC
  122.                 EXPORT  UsageFault_Handler        [WEAK]
  123.                 B       .
  124.                 ENDP
  125. SVC_Handler     PROC
  126.                 EXPORT  SVC_Handler               [WEAK]
  127.                 B       .
  128.                 ENDP
  129. DebugMon_Handler/
  130.                 PROC
  131.                 EXPORT  DebugMon_Handler          [WEAK]
  132.                 B       .
  133.                 ENDP
  134. PendSV_Handler  PROC
  135.                 EXPORT  PendSV_Handler            [WEAK]
  136.                 B       .
  137.                 ENDP
  138. SysTick_Handler PROC
  139.                 EXPORT  SysTick_Handler           [WEAK]
  140.                 B       .
  141.                 ENDP
  142. Default_Handler PROC
  143.                 EXPORT  WWDG_IRQHandler           [WEAK]
  144.                 EXPORT  PVD_IRQHandler            [WEAK]
  145.                 EXPORT  TAMPER_IRQHandler         [WEAK]
  146.                 EXPORT  RTC_IRQHandler            [WEAK]
  147.                 EXPORT  FLASH_IRQHandler          [WEAK]
  148.                 EXPORT  RCC_IRQHandler            [WEAK]
  149.                 EXPORT  EXTI0_IRQHandler          [WEAK]
  150.                 EXPORT  EXTI1_IRQHandler          [WEAK]
  151.                 EXPORT  EXTI2_IRQHandler          [WEAK]
  152.                 EXPORT  EXTI3_IRQHandler          [WEAK]
  153.                 EXPORT  EXTI4_IRQHandler          [WEAK]
  154.                 EXPORT  DMAChannel1_IRQHandler    [WEAK]
  155.                 EXPORT  DMAChannel2_IRQHandler    [WEAK]
  156.                 EXPORT  DMAChannel3_IRQHandler    [WEAK]
  157.                 EXPORT  DMAChannel4_IRQHandler    [WEAK]
  158.                 EXPORT  DMAChannel5_IRQHandler    [WEAK]
  159.                 EXPORT  DMAChannel6_IRQHandler    [WEAK]
  160.                 EXPORT  DMAChannel7_IRQHandler    [WEAK]
  161.                 EXPORT  ADC_IRQHandler            [WEAK]
  162.                 EXPORT  USB_HP_CAN_TX_IRQHandler  [WEAK]
  163.                 EXPORT  USB_LP_CAN_RX0_IRQHandler [WEAK]
  164.                 EXPORT  CAN_RX1_IRQHandler        [WEAK]
  165.                 EXPORT  CAN_SCE_IRQHandler        [WEAK]
  166.                 EXPORT  EXTI9_5_IRQHandler        [WEAK]
  167.                 EXPORT  TIM1_BRK_IRQHandler       [WEAK]
  168.                 EXPORT  TIM1_UP_IRQHandler        [WEAK]
  169.                 EXPORT  TIM1_TRG_COM_IRQHandler   [WEAK]
  170.                 EXPORT  TIM1_CC_IRQHandler        [WEAK]
  171.                 EXPORT  TIM2_IRQHandler           [WEAK]
  172.                 EXPORT  TIM3_IRQHandler           [WEAK]
  173.                 EXPORT  TIM4_IRQHandler           [WEAK]
  174.                 EXPORT  I2C1_EV_IRQHandler        [WEAK]
  175.                 EXPORT  I2C1_ER_IRQHandler        [WEAK]
  176.                 EXPORT  I2C2_EV_IRQHandler        [WEAK]
  177.                 EXPORT  I2C2_ER_IRQHandler        [WEAK]
  178.                 EXPORT  SPI1_IRQHandler           [WEAK]
  179.                 EXPORT  SPI2_IRQHandler           [WEAK]
  180.                 EXPORT  USART1_IRQHandler         [WEAK]
  181.                 EXPORT  USART2_IRQHandler         [WEAK]
  182.                 EXPORT  USART3_IRQHandler         [WEAK]
  183.                 EXPORT  EXTI15_10_IRQHandler      [WEAK]
  184.                 EXPORT  RTCAlarm_IRQHandler       [WEAK]
  185.                 EXPORT  USBWakeUp_IRQHandler      [WEAK]
  186. WWDG_IRQHandler
  187. PVD_IRQHandler
  188. TAMPER_IRQHandler
  189. RTC_IRQHandler
  190. FLASH_IRQHandler
  191. RCC_IRQHandler
  192. EXTI0_IRQHandler
  193. EXTI1_IRQHandler
  194. EXTI2_IRQHandler
  195. EXTI3_IRQHandler
  196. EXTI4_IRQHandler
  197. DMAChannel1_IRQHandler
  198. DMAChannel2_IRQHandler
  199. DMAChannel3_IRQHandler
  200. DMAChannel4_IRQHandler
  201. DMAChannel5_IRQHandler
  202. DMAChannel6_IRQHandler
  203. DMAChannel7_IRQHandler
  204. ADC_IRQHandler
  205. USB_HP_CAN_TX_IRQHandler
  206. USB_LP_CAN_RX0_IRQHandler
  207. CAN_RX1_IRQHandler
  208. CAN_SCE_IRQHandler
  209. EXTI9_5_IRQHandler
  210. TIM1_BRK_IRQHandler
  211. TIM1_UP_IRQHandler
  212. TIM1_TRG_COM_IRQHandler
  213. TIM1_CC_IRQHandler
  214. TIM2_IRQHandler
  215. TIM3_IRQHandler
  216. TIM4_IRQHandler
  217. I2C1_EV_IRQHandler
  218. I2C1_ER_IRQHandler
  219. I2C2_EV_IRQHandler
  220. I2C2_ER_IRQHandler
  221. SPI1_IRQHandler
  222. SPI2_IRQHandler
  223. USART1_IRQHandler
  224. USART2_IRQHandler
  225. USART3_IRQHandler
  226. EXTI15_10_IRQHandler
  227. RTCAlarm_IRQHandler
  228. USBWakeUp_IRQHandler
  229.                 B       .
  230.                 ENDP
  231.                 ALIGN
  232. ; User Initial Stack & Heap
  233.                 IF      :DEF:__MICROLIB
  234.                
  235.                 EXPORT  __initial_sp
  236.                 EXPORT  __heap_base
  237.                 EXPORT  __heap_limit
  238.                
  239.                 ELSE
  240.                
  241. ;                IMPORT  __use_two_region_memory
  242.                 EXPORT  __user_initial_stackheap
  243. __user_initial_stackheap
  244.                 LDR     R0, =  Heap_Mem
  245.                 LDR     R1, =(Stack_Mem + Stack_Size)
  246.                 LDR     R2, = (Heap_Mem +  Heap_Size)
  247.                 LDR     R3, = Stack_Mem
  248.                 BX      LR
  249.                 ALIGN
  250.                 ENDIF
  251.                 END
复制代码

一些旁枝末节和本文的主题无关,我们先不要去理会,只需要知道这个启动代码是设置向量表,然后跳转到__main函数。跳转具体到代码段部分如下:


  1. Reset_Handler   PROC
  2.                 EXPORT  Reset_Handler             [WEAK]
  3.                 IMPORT  __main
  4.                 LDR     R0, =__main
  5.                 BX      R0
  6.                 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函数的出身我们基本搞清楚了,那么现在的问题是,它和main又有什么关系呢?其实__main主要做这么两件事:初始化C/C++所需的资源,调用main函数。初始化先暂时不说,但“调用main函数”这个功能能够让我们解决为什么之前的启动代码调用的是__main,最后却能转到main函数的疑惑。


初始化C/C++所需的资源,如果脱离了具体情况,实在很难解释清楚,还是先看看编译出来的汇编代码片段:
0_1291342277YA6l.png
0_1291342281WF22.png

凡是以__rt开头的,都是用来初始化C/C++运行库的;而以__scatterload开头,则是根据离散文件的定义,将代码中的变量映射到相应的内存位置。而回答本文开头的问题,关键就在于__scatterload_copy函数!

     我们在STM32F10x平台举个简单的例子,首先要明白一点是,该平台的的flash地址以0x08000000为起始,主要是存储代码;而SRAM是以0x20000000为起始,也就是内存。然后C/C++有这么一行代码:
  1. static int g_iVal = 12;
复制代码


当我们程序开始跑起来的时候,通过IDE发现,g_iVal被映射到内存地址0x20000000,数值为一个随机数0xFFFFBE00,而不是代码中设置的12,如图:
0_1291342285pK8o.png
我们让程序继续往下执行,当执行完毕__scatterload_copy之后,我们发现g_iVal这时候已经变成我们所需要的初始值了:
0_1291342288fGJ2.png

接下来就是C/C++库的初始化,最后就是进入到main函数,而此时已经是万事俱备。

     如果大家只是局限于桌面应用的开发,因为编译出来的程序带有很多操作系统的特性,所以会给我们理解程序的运行带来很大的迷惑,也只有步入嵌入式领域,没有操作系统的支持下赤裸裸地奔跑在CPU之上,才能更好地理解软件是如何运行起来的,也只有这时候我们才能够更清楚知道,原来main函数并不是起点。



*滑块验证:
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

更多

客服中心

微信扫描二维码 服务时间:周一至周日 8:30-22:00
快速回复 返回顶部 返回列表