您的位置:首页 > 大数据 > 人工智能

main函数之前究竟发生了什么?

2014-08-13 14:50 253 查看


/article/1457415.html






main函数之前究竟发生了什么?

分类: STM32F 开发工具2010-12-03
10:12 5878人阅读 评论(11) 收藏 举报

flashimportwizardlinker编译器exception

//=====================================================================

//TITLE:

// main函数之前究竟发生了什么?

//AUTHOR:

// norains

//DATE:

// Friday 3-December-2010

//Environment:

// MDK 4.1

//=====================================================================

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

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

我们以STM32F10x的启动代码为例,先看看其完整的源码:

[c-sharp] view
plaincopy

;/*****************************************************************************/

;/* STM32F10x.s: Startup file for ST STM32F10x device series */

;/*****************************************************************************/

;/* <<< Use Configuration Wizard in Context Menu >>> */

;/*****************************************************************************/

;/* This file is part of the uVision/ARM development tools. */

;/* Copyright (c) 2005-2007 Keil Software. All rights reserved. */

;/* This software may only be used under the terms of a valid, current, */

;/* end user licence from KEIL for a compatible version of KEIL software */

;/* development tools. Nothing else gives you the right to use this software. */

;/*****************************************************************************/

;// <h> Stack Configuration

;// <o> Stack Size (in Bytes) <0x0-0xFFFFFFFF:8>

;// </h>

Stack_Size EQU 0x00000200

AREA STACK, NOINIT, READWRITE, ALIGN=3

Stack_Mem SPACE Stack_Size

__initial_sp

;// <h> Heap Configuration

;// <o> Heap Size (in Bytes) <0x0-0xFFFFFFFF:8>

;// </h>

Heap_Size EQU 0x00000000

AREA HEAP, NOINIT, READWRITE, ALIGN=3

__heap_base

Heap_Mem SPACE Heap_Size

__heap_limit

PRESERVE8

THUMB

; Vector Table Mapped to Address 0 at Reset

AREA RESET, DATA, READONLY

EXPORT __Vectors

__Vectors DCD __initial_sp ; Top of Stack

DCD Reset_Handler ; Reset Handler

DCD NMI_Handler ; NMI Handler

DCD HardFault_Handler ; Hard Fault Handler

DCD MemManage_Handler ; MPU Fault Handler

DCD BusFault_Handler ; Bus Fault Handler

DCD UsageFault_Handler ; Usage Fault Handler

DCD 0 ; Reserved

DCD 0 ; Reserved

DCD 0 ; Reserved

DCD 0 ; Reserved

DCD SVC_Handler ; SVCall Handler

DCD DebugMon_Handler ; Debug Monitor Handler

DCD 0 ; Reserved

DCD PendSV_Handler ; PendSV Handler

DCD SysTick_Handler ; SysTick Handler

; External Interrupts

DCD WWDG_IRQHandler ; Window Watchdog

DCD PVD_IRQHandler ; PVD through EXTI Line detect

DCD TAMPER_IRQHandler ; Tamper

DCD RTC_IRQHandler ; RTC

DCD FLASH_IRQHandler ; Flash

DCD RCC_IRQHandler ; RCC

DCD EXTI0_IRQHandler ; EXTI Line 0

DCD EXTI1_IRQHandler ; EXTI Line 1

DCD EXTI2_IRQHandler ; EXTI Line 2

DCD EXTI3_IRQHandler ; EXTI Line 3

DCD EXTI4_IRQHandler ; EXTI Line 4

DCD DMAChannel1_IRQHandler ; DMA Channel 1

DCD DMAChannel2_IRQHandler ; DMA Channel 2

DCD DMAChannel3_IRQHandler ; DMA Channel 3

DCD DMAChannel4_IRQHandler ; DMA Channel 4

DCD DMAChannel5_IRQHandler ; DMA Channel 5

DCD DMAChannel6_IRQHandler ; DMA Channel 6

DCD DMAChannel7_IRQHandler ; DMA Channel 7

DCD ADC_IRQHandler ; ADC

DCD USB_HP_CAN_TX_IRQHandler ; USB High Priority or CAN TX

DCD USB_LP_CAN_RX0_IRQHandler ; USB Low Priority or CAN RX0

DCD CAN_RX1_IRQHandler ; CAN RX1

DCD CAN_SCE_IRQHandler ; CAN SCE

DCD EXTI9_5_IRQHandler ; EXTI Line 9..5

DCD TIM1_BRK_IRQHandler ; TIM1 Break

DCD TIM1_UP_IRQHandler ; TIM1 Update

DCD TIM1_TRG_COM_IRQHandler ; TIM1 Trigger and Commutation

DCD TIM1_CC_IRQHandler ; TIM1 Capture Compare

DCD TIM2_IRQHandler ; TIM2

DCD TIM3_IRQHandler ; TIM3

DCD TIM4_IRQHandler ; TIM4

DCD I2C1_EV_IRQHandler ; I2C1 Event

DCD I2C1_ER_IRQHandler ; I2C1 Error

DCD I2C2_EV_IRQHandler ; I2C2 Event

DCD I2C2_ER_IRQHandler ; I2C2 Error

DCD SPI1_IRQHandler ; SPI1

DCD SPI2_IRQHandler ; SPI2

DCD USART1_IRQHandler ; USART1

DCD USART2_IRQHandler ; USART2

DCD USART3_IRQHandler ; USART3

DCD EXTI15_10_IRQHandler ; EXTI Line 15..10

DCD RTCAlarm_IRQHandler ; RTC Alarm through EXTI Line

DCD USBWakeUp_IRQHandler ; USB Wakeup from suspend

AREA |.text|, CODE, READONLY

; Reset Handler

Reset_Handler PROC

EXPORT Reset_Handler [WEAK]

IMPORT __main

LDR R0, =__main

BX R0

ENDP

; Dummy Exception Handlers (infinite loops which can be modified)

NMI_Handler PROC

EXPORT NMI_Handler [WEAK]

B .

ENDP

HardFault_Handler/

PROC

EXPORT HardFault_Handler [WEAK]

B .

ENDP

MemManage_Handler/

PROC

EXPORT MemManage_Handler [WEAK]

B .

ENDP

BusFault_Handler/

PROC

EXPORT BusFault_Handler [WEAK]

B .

ENDP

UsageFault_Handler/

PROC

EXPORT UsageFault_Handler [WEAK]

B .

ENDP

SVC_Handler PROC

EXPORT SVC_Handler [WEAK]

B .

ENDP

DebugMon_Handler/

PROC

EXPORT DebugMon_Handler [WEAK]

B .

ENDP

PendSV_Handler PROC

EXPORT PendSV_Handler [WEAK]

B .

ENDP

SysTick_Handler PROC

EXPORT SysTick_Handler [WEAK]

B .

ENDP

Default_Handler PROC

EXPORT WWDG_IRQHandler [WEAK]

EXPORT PVD_IRQHandler [WEAK]

EXPORT TAMPER_IRQHandler [WEAK]

EXPORT RTC_IRQHandler [WEAK]

EXPORT FLASH_IRQHandler [WEAK]

EXPORT RCC_IRQHandler [WEAK]

EXPORT EXTI0_IRQHandler [WEAK]

EXPORT EXTI1_IRQHandler [WEAK]

EXPORT EXTI2_IRQHandler [WEAK]

EXPORT EXTI3_IRQHandler [WEAK]

EXPORT EXTI4_IRQHandler [WEAK]

EXPORT DMAChannel1_IRQHandler [WEAK]

EXPORT DMAChannel2_IRQHandler [WEAK]

EXPORT DMAChannel3_IRQHandler [WEAK]

EXPORT DMAChannel4_IRQHandler [WEAK]

EXPORT DMAChannel5_IRQHandler [WEAK]

EXPORT DMAChannel6_IRQHandler [WEAK]

EXPORT DMAChannel7_IRQHandler [WEAK]

EXPORT ADC_IRQHandler [WEAK]

EXPORT USB_HP_CAN_TX_IRQHandler [WEAK]

EXPORT USB_LP_CAN_RX0_IRQHandler [WEAK]

EXPORT CAN_RX1_IRQHandler [WEAK]

EXPORT CAN_SCE_IRQHandler [WEAK]

EXPORT EXTI9_5_IRQHandler [WEAK]

EXPORT TIM1_BRK_IRQHandler [WEAK]

EXPORT TIM1_UP_IRQHandler [WEAK]

EXPORT TIM1_TRG_COM_IRQHandler [WEAK]

EXPORT TIM1_CC_IRQHandler [WEAK]

EXPORT TIM2_IRQHandler [WEAK]

EXPORT TIM3_IRQHandler [WEAK]

EXPORT TIM4_IRQHandler [WEAK]

EXPORT I2C1_EV_IRQHandler [WEAK]

EXPORT I2C1_ER_IRQHandler [WEAK]

EXPORT I2C2_EV_IRQHandler [WEAK]

EXPORT I2C2_ER_IRQHandler [WEAK]

EXPORT SPI1_IRQHandler [WEAK]

EXPORT SPI2_IRQHandler [WEAK]

EXPORT USART1_IRQHandler [WEAK]

EXPORT USART2_IRQHandler [WEAK]

EXPORT USART3_IRQHandler [WEAK]

EXPORT EXTI15_10_IRQHandler [WEAK]

EXPORT RTCAlarm_IRQHandler [WEAK]

EXPORT USBWakeUp_IRQHandler [WEAK]

WWDG_IRQHandler

PVD_IRQHandler

TAMPER_IRQHandler

RTC_IRQHandler

FLASH_IRQHandler

RCC_IRQHandler

EXTI0_IRQHandler

EXTI1_IRQHandler

EXTI2_IRQHandler

EXTI3_IRQHandler

EXTI4_IRQHandler

DMAChannel1_IRQHandler

DMAChannel2_IRQHandler

DMAChannel3_IRQHandler

DMAChannel4_IRQHandler

DMAChannel5_IRQHandler

DMAChannel6_IRQHandler

DMAChannel7_IRQHandler

ADC_IRQHandler

USB_HP_CAN_TX_IRQHandler

USB_LP_CAN_RX0_IRQHandler

CAN_RX1_IRQHandler

CAN_SCE_IRQHandler

EXTI9_5_IRQHandler

TIM1_BRK_IRQHandler

TIM1_UP_IRQHandler

TIM1_TRG_COM_IRQHandler

TIM1_CC_IRQHandler

TIM2_IRQHandler

TIM3_IRQHandler

TIM4_IRQHandler

I2C1_EV_IRQHandler

I2C1_ER_IRQHandler

I2C2_EV_IRQHandler

I2C2_ER_IRQHandler

SPI1_IRQHandler

SPI2_IRQHandler

USART1_IRQHandler

USART2_IRQHandler

USART3_IRQHandler

EXTI15_10_IRQHandler

RTCAlarm_IRQHandler

USBWakeUp_IRQHandler

B .

ENDP

ALIGN

; User Initial Stack & Heap

IF :DEF:__MICROLIB

EXPORT __initial_sp

EXPORT __heap_base

EXPORT __heap_limit

ELSE

; IMPORT __use_two_region_memory

EXPORT __user_initial_stackheap

__user_initial_stackheap

LDR R0, = Heap_Mem

LDR R1, =(Stack_Mem + Stack_Size)

LDR R2, = (Heap_Mem + Heap_Size)

LDR R3, = Stack_Mem

BX LR

ALIGN

ENDIF

END

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

[c-sharp] view
plaincopy

Reset_Handler PROC

EXPORT Reset_Handler [WEAK]

IMPORT __main

LDR R0, =__main

BX R0

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++所需的资源,如果脱离了具体情况,实在很难解释清楚,还是先看看编译出来的汇编代码片段:





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

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

[cpp] view
plaincopy

static int g_iVal = 12;

当我们程序开始跑起来的时候,通过IDE发现,g_iVal被映射到内存地址0x20000000,数值为一个随机数0xFFFFBE00,而不是代码中设置的12,如图:



我们让程序继续往下执行,当执行完毕__scatterload_copy之后,我们发现g_iVal这时候已经变成我们所需要的初始值了:



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

如果大家只是局限于桌面应用的开发,因为编译出来的程序带有很多操作系统的特性,所以会给我们理解程序的运行带来很大的迷惑,也只有步入嵌入式领域,没有操作系统的支持下赤裸裸地奔跑在CPU之上,才能更好地理解软件是如何运行起来的,也只有这时候我们才能够更清楚知道,原来main函数并不是起点。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: