averyleigh 发表于 2024-6-23 23:21

KEIL MDK输出map文件分析

零、前言

前面写了一篇文章对__main函数的执行过程做了一个粗略的跟踪描叙,对一个烧录了程序的STM32开发板从启动复位到进入用户main函数的过程有了一个大概的了解,但是有很多问题感觉还是模模糊糊,因此,今天又把KEIL MDK编译、链接后生成的map文件简单分析一下,加深对链接器、嵌入式系统可执行映像特点的了解。、


一、文件分析流程

1、第一部分:Section Cross References

主要是各个源文件生成的模块之间相互引用的关系。

stm32f10x.o(STACK) refers (Special) to stkheap2.o(.text) for __use_two_region_memory

比如上面这句话,stm32f10x.o是stm32f10x.s生成的目标文件模块,(STACK)是文件内定义的一个段,链接器把它视为一个Section,输入节。它引用了模块stkheap2.o输入节(.text)里面的一个全局符号__use_two_region_memory(可能是一个函数或变量)。这个(Special)不知道是什么含义。

剩下的基本都是这用的意思。

stm32f10x_vector.o(.text) refers to __main.o(!!!main) for __main

__main.o(!!!main) refers to kernel.o(.text) for __rt_entry

kernel.o(.text) refers to usertask.o(.text) for main

上面这几个对于程序意义比较重大用户在启动代码中调用了__main.o模块中的__main函数,__main又调用了kernel.o中的__rt_entry函数,最后kernel.o又调用了用户定义的main主函数。



2、第二部分:Removing Unused input sections from the image.

就是将库中没有用到的函数从可执行映像中删除掉,减小程序的体积。

    Removing os_mbox.o(.text), (1094 bytes).

    Removing os_mutex.o(.text), (1744 bytes).

    Removing os_sem.o(.text), (1016 bytes).



3、第三部分:Image Symbol Table

Local Symbols

符号表里的局部符号。

../../angel/boardlib.s0x00000000   Number         0boardinit1.o ABSOLUTE

../../angel/handlers.s0x00000000   Number   0__scatter_copy.o ABSOLUTE

../../angel/kernel.s   0x00000000   Number       0kernel.o ABSOLUTE

../../angel/rt.s    0x00000000   Number         0rt_raise.o ABSOLUTE

../../angel/scatter.s   0x00000000   Number         0__scatter.o ABSOLUTE

../../angel/startup.s   0x00000000   Number         0__main.o ABSOLUTE

../../angel/sys.s    0x00000000   Number         0sys_exit.o ABSOLUTE

../../angel/sysapp.c    0x00000000   Number         0sys_wrch.o ABSOLUTE

../../armsys.c       0x00000000   Number         0_get_argv.o ABSOLUTE

../../division_7m.s0x00000000   Number         0rtudiv10.o ABSOLUTE

../../fpinit.s   0x00000000   Number         0fpinit.o ABSOLUTE

../../heapalloc.c   0x00000000   Number         0hrguard.o ABSOLUTE

../../printf.c   0x00000000   Number   0_printf_outstr_char.o ABSOLUTE

../../signal.c   0x00000000   Number         0defsig_exit.o ABSOLUTE

../../stdlib.c   0x00000000   Number         0exit.o ABSOLUTE

../../stkheap.s      0x00000000   Number         0heapext.o ABSOLUTE



以上是一些系统内部的局部符号,还有用户的一些局部符号



4、第四部分:Global Symbols

全局符号

   _terminate_user_alloc                      - Undefined Weak Reference

   _terminateio                              - Undefined Weak Reference

   __Vectors       0x08000000   Data         4stm32f10x_vector.o(RESET)

   __main         0x08000131   Thumb Code   8__main.o(!!!main)

   __scatterload    0x08000139   Thumb Code   0__scatter.o(!!!scatter)

    __scatterload_rt20x08000139   Thumb Code    44__scatter.o(!!!scatter)



这些是一些系统的全局符号

   Font8x16   0x08001a82   Data      2048tft018.o(.constdata)

   Font8x8    0x08002282   Data      2056tft018.o(.constdata)

   codeGB_160x08002a8a   Data         770tft018.o(.constdata)

Region
Table
Base0x08002dc0   Number0anon
obj.o(Region
Table)

Region
Table
Limit0x08002de0   Number   0anon
obj.o(Region
Table)



后面这两个符号我认为很重要,在运行库代码将可执行映像从加载视图转变为可执行视图的过程中起到了关键作用。Number是指它并不占据程序空间,而只是一个具有一定数值的符号,类似于程序中用define和EQU定义的。所以这里,我先放下map文件的分析,先通过仿真调试,看这两个数值在程序中怎么用。


点击看大图

果然,在刚开始执行程序时,R10和R11的值就已经被赋值成了这两个值。


很快就将 0x08002dc0 到 0x08002dcf 处的 16 个字节, 4 个双字加载到了 R0-R3 ,我们可以分析一下里面的内容, R0 就是程序加载视图的 RW 区的起始地址( 0x08002de0 ), R1 就是要输出的执行视图的 RW 区的地址( 0x20000000 ), R2 就是要复制的 RW 数据的个数, R3 是复制函数 ( __scatterload_copy ) 的地址,类似于一个回调函数。接下来就要用了: 0x0800011E 4718BXr3 这条指令去执行复制工作。

接下来又将0x08002dd0到0x08002ddf处的16个字节,4个双字加载到了R0-R3,我们可以分析一下里面的内容,R0就是程序加载视图的RW区的起始地址(0x08002de0+0x20=0x08002e00),R1就是要输出的执行视图的RW区的地址(0x20000020),R2就是要复制的RW数据的个数,R3是ZI区域建立函数(__scatterload_zeroinit )的地址。


执行完成后,程序就会进入BL.W__rt_entry处进行库的初始化工作。

经过这么一分析,现在我对于程序的加载映像和执行映像有了较深的理解:程序的RO_Code加上RO_Data总共是0x2dc0这么大,地址范围0x0800,0000到0x8000,2dbf。然后在0x0800,2dc0-2dcf共16个字节放了RW加载映像地址(0x0800,2de0)、执行映像地址(0x2000,0000)、RW长度(0x20)和将该段数据从加载映像复制到执行映像的函数地址。在0x0800,2dd0-2ddf共16个字节放了ZI加载映像地址(0x0800,2e00)、执行映像地址(0x2000,0020)、ZI长度(0x480)和建立ZI、HEAP和STACK执行映像的函数地址。

在上面的第二个阶段,将ZI清零阶段,程序的ZI长度实际上只有0x20,而库代码留出了0x60的长度。因此数据区的顶端为0x2000,00a0-1。接下来从0x2000,00a0开始为堆的起始地址,堆长度加上程序栈长度为0x2000,04a0,这就是堆栈顶端,也是__initial_SP的初始值。

程序进入_rt_entry后,还要对heapstack进行处理,但我没有看到有什么用的变化。从中对库留出的ZI数据区进行了一些处理,我暂时也看不明白。好了,调试就到这里,回到map文件分析的正途。



5、第五部分:

Memory Map of the image

//映像的内存分布

   Image Entry point : 0x080000ed

//程序的入口点:这里应该是RESET_Handler的地址

Load Region LR_IROM1 (Base: 0x08000000, Size: 0x00002e00, Max: 0x00020000, ABSOLUTE)

//程序的加载映像地址和长度,2e00=2dc0(代码和常数)+0x20(Region Table是RW的加载和执行地址、ZI与HEAPSTACK的执行地址)+0x20(已经初始化的数据)。

   Execution Region ER_IROM1 (Base: 0x08000000, Size: 0x00002de0, Max: 0x00020000, ABSOLUTE)//这段RO区域的加载映像和执行映像一致。

   Base Addr    Size         Type   Attr      Idx    E Section Name      Object

   0x08000000 0x000000ec   Data   RO      3    RESET               stm32f10x.o

   0x080000ec 0x00000008Code   RO191* !!!main             __main.o(c_w.l)

   Execution Region RW_IRAM1 (Base: 0x20000000, Size: 0x000004a0, Max: 0x00005000, ABSOLUTE)//RW数据区 ZI数据区 Heap和Stack数据区。

   Base Addr    Size         Type   Attr      Idx    E Section Name      Object

   0x20000000   0x00000001   Data   RW   100    .data            tft018.o

   x20000040   0x00000060   Zero   RW212.bss                libspace.o(c_w.l)

   0x200000a0   0x00000000   Zero   RW2    HEAP          stm32f10x.o

   0x200000a0   0x00000400   Zero   RW    1STACK               stm32f10x.o



6、第六部分:Image component sizes

这是指出各个模块的输入节的大小

       Code (inc. data)   RO Data    RW Data    ZI Data      Debug   Object Name

      972         58          0         10         32       2416   can.o

      824      168          0         15          0       1791   candemo.o

      928         88          0          0          0       4529   stm32_init.o

         52         18      236          0       1024       2700   stm32f10x.o

       1836         32       4874          1          0       8076   tft018.o



最后给出总长度:这个11744应该=0x2dc0,1184应该0x4a0。11776应该是=0x2e00。

   Total ROSize (Code + RO Data)                11744 (11.47kB)

   Total RWSize (RW Data + ZI Data)            1184 (   1.16kB)

Total ROM Size (Code + RO Data + RW Data)      11776 (11.50kB)



黑心单片机 发表于 2024-6-27 16:56

我只会看内存占用

埃娃 发表于 2024-6-28 17:33

这个map文件里的数据都是什么意思啊
页: [1]
查看完整版本: KEIL MDK输出map文件分析