ARM技术论坛
直播中

王桂兰

7年用户 1135经验值
私信 关注
[经验]

一文搞清ARM程序的加载和执行过程

STM32F072为例, 其启动过程适用于大多数通用的ARM 核MCU
从Image文件说起
Image 文件是程序经过编译链接后生成的固件,我们常见的有几种格式,不同格式所包含固件信息有差别,通常有以下几类:
hex文件
HEX文件由记录(RECORD)组成。在HEX文件里面,每一行代表一个记录。
形如
:BBAAAATTHHHH…HHHHCC
BB:字节个数。
AAAA:数据记录的开始地址,高位在前,低位在后。
TT: Type
00数据记录,用来记录数据。
01记录结束,放在文件末尾,用来标识文件结束。
02用来标识扩展段地址的记录
04扩展地址记录(表示32位地址的前缀)
HHHH:一个字(Word)的数据记录,高字节在前,低字节在后。TT之后共有 BB/2 个字的数据 。
CC: 占据一个Byte的CheckSum
举例分析:
:020000040000FA
:10000400FF00A0E314209FE5001092E5011092E5A3
:00000001FF         
第1条记录长度为0x02,LOAD OFFSET为0000,RECTYPE为04,说明该记录为扩展段地址记录。数据为0000,校验和为FA。从这个记录的长度和数据,我们可以计算出基地址为0X0000。后面的数据记录都以此地址为基地址。
第2条记录长度为0x10(16),LOAD OFFSET为0004,RECTYPE为00,说明该记录为数据记录。数据为FF00A0E314209FE5001092E5011092E5,共16个字节,记录的校验和为A3。此时的基地址为0X0000,加上OFFSET,这个记录里的16BYTE的数据的起始地址就是0x0000 + 0x0004 = 0x0004. 其实际的数据只有16个BYTE:FF00A0E314209FE5001092E5011092E5。
第3条记录的长度为00,LOAD OFFSET为0000,TYPE= 01,校验和为FF。类型为01,说明这个是一个END OF FILE RECORD,标识文件的结尾。HEX结束符一般以:00000001FF结尾。
bin文件
Bin文件是最纯粹的二进制机器代码, 或者说是"顺序格式"。按照assembly code顺序翻译成binary machine code,内部没有地址标记。Bin是直接的内存映象表示,二进制文件大小即为文件所包含的数据的实际大小。
简单总结一下这2种文件格式的区别:
HEX文件包含地址信息而BIN文件只包含数据本身,烧写或下载HEX文件时,一般不需要用户指定地址,因为HEX文件内部已经包含了地址信息。烧写BIN文件时则需要用户指定烧录的地址信息。
HEX文件是用ASCII码来表示二进制的数值。例如8-BIT的二进制数值0x4E,用ASCII来表示就需要分别表示字符‘4’和字符‘E’,每个字符均需要一个字节,因此HEX文件至少需要2倍BIN文件的空间。
axf 文件
Axf文件由ARM编译器产生,除了包含bin的内容之外,还附加其他调试信息,这些调试信息加在可执行的二进制数据之前。调试时这些调试信息不会下载到RAM中,真正下载到RAM中的信息仅仅是可执行代码。因此,如果ram的大小小于axf文件的大小,程序是完全有可能在ram中调试的,只要axf除去调试信息后文件大小小于ram的大小即可。
调试信息有以下功用:
1、 可将源代码包括注释夹在反汇编代码中,这样我们可随时切换到源代码中进行调试。
2、 我们还可以对程序中的函数调用情况进行跟踪(通过Watch & Call Stack Window查看)。
3、对变量进行跟踪(利用Watch & Call Stack Window)。
调试信息虽然有用,但程序功能实现后,在目标文件和库中减少调试信息却是非常有益的。减少调试信息可减少目标文件和库大小、加快链接速度、减小最终镜象代码。以下几种方法可用来减少每个源文件产生的调试信息:
1、避免在头文件中条件性使用#define,链接器不能移除共用的调试部分,除非这些部分是完全一样的。
2、更改C/C++源文件,使#included包含的所有头文件有相同顺序。
3、尽量使用数量较多的小头文件而不是较大的单一头文件,这有利于链接器获取更多的通用块。
4、程序中最好只包含必须用到的头文件。避免重复包含头文件,可使用编译器选项–remarks来产生警告信息;
5、使用编译命令行选项–no_debug_macros,从调试表中丢弃预处理宏定义。
elf 文件
ELF(Executableand linking format)文件是x86 Linux系统下的一种常用目标文件(objectfile)格式,有三种主要类型:
(1)适于连接的可重定位文件(relocatablefile),可与其它目标文件一起创建可执行文件和共享目标文件。
(2)适于执行的可执行文件(executable file),用于提供程序的进程映像,加载到内存执行。
(3)共享目标文件(shared object file),连接器可将它与其它可重定位文件和共享目标文件连接成其它的目标文件,动态连接器又可将它与可执行文件和其它共享目标文件结合起来创建一个进程映像。
文件的转换
可由elf文件转化为hex和bin两种文件,hex也可以直接转换为bin文件,但是bin要转化为hex文件必须要给定一个基地址。而hex和bin不能转化为elf文件,因为elf的信息量要大。Axf文件可以转化为bin文件,
其中keil 提供了工具箱进行相关转换,在keil 安装目录下:Keil_v5ARMARMCCbin中通过
fromelf -nodebug xx.axf -bin xx.bin即可。
Image 文件信息
通过dump elf 文件或者分析keil文件生成的MAP文件,我们可以得到Image中的相关信息,以Map文件为例:
Memory Map of the image
  Image Entry point : 0x080000c1
  Load Region LR_IROM1 (Base: 0x08000000, Size: 0x00002d70, Max: 0x00005000, ABSOLUTE, COMPRESSED[0x00002cc8])
    Execution Region ER_IROM1 (Base: 0x08000000, Size: 0x00002c1c, Max: 0x00005000, ABSOLUTE)
    Base Addr    Size         Type   Attr      Idx    E Section Name        Object
    0x08000000   0x000000c0   Data   RO         1613    RESET               startup_stm32f072.o
    0x080000c0   0x00000000   Code   RO         5355  * .ARM.Collect$$$$00000000  mc_p.l(entry.o)
    0x080000c0   0x00000004   Code   RO         5364    .ARM.Collect$$$$00000001  mc_p.l(entry2.o)
    0x080000c4   0x00000004   Code   RO         5367    .ARM.Collect$$$$00000004  mc_p.l(entry5.o)
    0x080000c8   0x00000000   Code   RO         5369    .ARM.Collect$$$$00000008  mc_p.l(entry7b.o)
    0x080000c8   0x00000000   Code   RO         5371    .ARM.Collect$$$$0000000A  mc_p.l(entry8b.o)
    0x080000c8   0x00000008   Code   RO         5372    .ARM.Collect$$$$0000000B  mc_p.l(entry9a.o)
    0x080000d0   0x00000000   Code   RO         5374    .ARM.Collect$$$$0000000D  mc_p.l(entry10a.o)
    0x080000d0   0x00000000   Code   RO         5376    .ARM.Collect$$$$0000000F  mc_p.l(entry11a.o)
    0x080000d0   0x00000004   Code   RO         5365    .ARM.Collect$$$$00002712  mc_p.l(entry2.o)
    0x080000d4   0x0000001c   Code   RO         1614    .text               startup_stm32f072.o
    0x080000f0   0x0000002c   Code   RO         5358    .text               mc_p.l(uidiv.o)
    0x0800011c   0x00000028   Code   RO         5360    .text               mc_p.l(idiv.o)
    0x08000144   0x00000024   Code   RO         5378    .text               mc_p.l(init.o)
    0x08000168   0x00000056   Code   RO         5388    .text               mc_p.l(__dczerorl2.o)
    0x080001be   0x00000002   PAD
    0x080001c0   0x00000018   Code   RO         1805    i.CRS_AutomaticCalibrationCmd  stm32f0xx_crs.o
    0x080001d8   0x00000018   Code   RO         1809    i.CRS_FrequencyErrorCounterCmd  stm32f0xx_crs.o
    0x080001f0   0x00000018   Code   RO         1822    i.CRS_SynchronizationSourceConfig  stm32f0xx_crs.o
    0x08000208   0x00000284   Code   RO         1345    i.CTR               u***_dcd_int.o
    0x0800048c   0x00000028   Code   RO          864    i.ClearDTOG_RX      u***_core.o
    0x080004b4   0x00000028   Code   RO          865    i.ClearDTOG_TX      u***_core.o
    0x080004dc   0x00000014   Code   RO         1244    i.DCD_DevConnect    u***_dcd.o
    0x080004f0   0x00000014   Code   RO         1245    i.DCD_DevDisconnect  u***_dcd.o
    0x08000504   0x0000007e   Code   RO         1246    i.DCD_EP_Close      u***_dcd.o
    0x08000582   0x00000048   Code   RO         1247    i.DCD_EP_ClrStall   u***_dcd.o
    0x080005ca   0x00000100   Code   RO         1248    i.DCD_EP_Open       u***_dcd.o
    0x080006ca   0x00000054   Code   RO         1249    i.DCD_EP_PrepareRx  u***_dcd.o
从MAP 文件我们可以看到几个信息:
程序的加载地址:
Load Region LR_IROM1 (Base: 0x08000000, Size: 0x00002d70, Max: 0x00005000, ABSOLUTE, COMPRESSED[0x00002cc8])
程序的执行地址:
    Execution Region ER_IROM1 (Base: 0x08000000, Size: 0x00002c1c, Max: 0x00005000, ABSOLUTE)
程序代码段和数据段的映射地址
从MAP文件的分析,我们会好奇的引申出几个问题:
Load address 如何设定?
Execution address如何设定?
load 和Execution address是否可以设置为不同的地址?
程序的入口地址是什么?
以下,将通过分析相关文件一一解答这些问题。
startup.s file
;
; Amount of memory (in bytes) allocated for Stack
; Tailor this value to your application needs
; Stack Configuration
;    Stack Size (in Bytes) <0x0-0xFFFFFFFF:8>
;

Stack_Size      EQU     0x00000400
                AREA    STACK, NOINIT, READWRITE, ALIGN=3
Stack_Mem       SPACE   Stack_Size
__initial_sp
; Heap Configuration
;     Heap Size (in Bytes) <0x0-0xFFFFFFFF:8>
;

Heap_Size       EQU     0x00000200
                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
                EXPORT  __Vectors_End
                EXPORT  __Vectors_Size
__Vectors       DCD     __initial_sp                   ; Top of Stack
                        DCD     Reset_Handler                  ; Reset Handler
                        DCD     NMI_Handler                    ; NMI Handler
                        DCD     HardFault_Handler              ; Hard Fault Handler
                        DCD     0                              ; Reserved
                        DCD     0                              ; Reserved
                        DCD     0                              ; Reserved
                        DCD     0                              ; Reserved
                        DCD     0                              ; Reserved
                        DCD     0                              ; Reserved
                        DCD     0                              ; Reserved
                        DCD     SVC_Handler                    ; SVCall Handler
                        DCD     0                              ; Reserved
                        DCD     0                              ; Reserved
                        DCD     PendSV_Handler                 ; PendSV Handler
                        DCD     SysTick_Handler                ; SysTick Handler
...
__Vectors_End
__Vectors_Size  EQU  __Vectors_End - __Vectors
                AREA    |.text|, CODE, READONLY
; Reset handler routine
Reset_Handler    PROC
                 EXPORT  Reset_Handler                 [WEAK]
        IMPORT  __main
        IMPORT  SystemInit  
                 LDR     R0, =SystemInit
                 BLX     R0
                 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
SVC_Handler     PROC
                EXPORT  SVC_Handler                    [WEAK]
                B       .
                ENDP
PendSV_Handler  PROC
                EXPORT  PendSV_Handler                 [WEAK]
                B       .
                ENDP
SysTick_Handler PROC
                EXPORT  SysTick_Handler                [WEAK]
                B       .
                ENDP
                ...
                ...
                B       .
                ENDP
                ALIGN
;*******************************************************************************
; User Stack and Heap initialization
;*******************************************************************************
                 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
Scatter file
; *************************************************************
; *** Scatter-Loading Description File generated by uVision ***
; *************************************************************
LR_IROM1 0x08000000 0x00005000  {    ; load region size_region
  ER_IROM1 0x08000000 0x00005000  {  ; load address = execution address
   *.o (RESET, +First)
   *(InRoot$$Sections)
   .ANY (+RO)
  }
  RW_IRAM1 0x20000000 0x00001800  {  ; RW data
   .ANY (+RW +ZI)
  }
}
LOAD_FLASH 0x04000000 0x80000   ;start address and length
{
    EXEC_FLASH 0x04000000 0x80000
    {
        init.o (Init,+FIRST)    ; remap & init code
        __main.o (+RO)          ; copy code
        * (Region$$Table)       ; RO/RW addresses to copy
        * (ZISection$$Table)    ; ZI addresses to zero
    }
    EXEC_32bitRAM 0x0000 0x2000
    {
        vectors.o (Vect,+FIRST) ; vector table
        int_handler.o (+RO)     ; interrupt handler
    }
    EXEC_16bitRAM 0x2000 0x80000
    {
        * (+RO)                 ; ## all other RO areas ##
        * (+RW,+ZI)             ; program variables
    }
}
For RVCT 2.1 and later, an alternative way is to specify these sections using InRoot$$Sections:
LOAD_FLASH 0x04000000 0x80000   ;start address and length
{
    EXEC_FLASH 0x04000000 0x80000
    {
        init.o (Init,+FIRST)    ; remap & init code
        * (InRoot$$Sections)    ; using InRoot$$Sections
    }
    ...
}
An application's initial entry point must also be rooted. If the initial entry point is not in a root region, the link will fail with an error such as:
Error: L6203E: Entry point (0x08000000) lies within non-root region EXE_FLASH.

原作者:huntershuai

更多回帖

发帖
×
20
完善资料,
赚取积分