其中,有些符号需要我们知道其含义:
.data:这些部分保存有助于程序内存映像的已初始化数据
.text:本节包含程序的文本或可执行指令
.bss:本节保存有助于程序内存映像的未初始化数据
.ARM.exidx*:以.ARM.exidx开头的节包含部分展开的索引条目
i.xxxx: i 是 interface 的意思,i.xxxx 就表示 xxx 接口(一般就是指函数名)
Section Cross References
该部分显示了节区之间的交叉引用,指的是各个源文件生成的模块之间相互引用的关系,是由 armlink 的参数 --xref 生成的。主要分为以下几种情况:
用户代码间接口的相互引用: 例如:stm32f4xx_ll_flash.o(i.LL_FLASH_EraseChip) refers to stm32f4xx_ll_flash.o(i.LL_FLASH_IsActiveFlag_BSY) for LL_FLASH_IsActiveFlag_BSY 表示 stm32f4xx_ll_flash.o 中的函数 LL_FLASH_EraseChip 引用了 stm32f4xx_ll_flash.o 中的 函数 LL_FLASH_IsActiveFlag_BSY,其中的 stm32f4xx_ll_flash.o 是由用户代码 stm32f4xx_ll_flash.c 生成的模块
用户接口引用用户数据:主要有两类,例如:lora.o(i.LoRaRcvProcess) refers to lora.o(.data) for Radio 表示 lora.o 中的接口 LoRaRcvProcess 引用了 lora.o 中的已初始化数据 Radio;lora.o(i.LoRaRcvProcess) refers to lora.o(.bss) for ProcLoRa 表示 lora.o 中的接口 LoRaRcvProcess 引用了 lora.o 中的未初始化默认初始化为0数据 ProcLoRa
用户数据引用接口: 这个要是函数指针的使用。例如:pnwz.o(.data) refers to pnwzuif.o(i.PNWZ_USER_RespGetLife) for PNWZ_USER_RespGetLife
用户接口引用 C 库接口: 例如:pnwzuif.o(i.PNWZ_USER_ReqGetVerHW) refers to strlen.o(.text) for strlen
代码引用接口: 主要发生于 C 库之间(ARM C 库是不开源的,仅提供二进制文件),例如:``
C 库接口之间的引用: 主要发生于 C 库之间(ARM C 库是不开源的,仅提供二进制文件),例如:__2sprintf.o(.text) refers to _sputc.o(.text) for _sputc 及 __printf_flags_ss_wp.o(.text) refers to __printf_wp.o(i._is_digit) for _is_digit
C库引用连接器符号: 例如:exit.o(.text) refers to rtexit.o(.ARM.Collect$$rtexit$$00000000) for __rt_exit
符号之间的引用: 例如:retnan.o(x$fpl$retnan) refers to trapv.o(x$fpl$trapveneer) for __fpl_cmpreturn
在 ARM 编译套件中,所有的 C 库由工具armar来管理,位于 ARM 编译器目录 lib 下。使用 armar 即可从指定的库文件中解压出 __main.o 等模块。至于如何操作,参见博文ARM 之九 Cortex-M/R 内核启动过程 / 程序启动流程。
Removing Unused input sections from the image
这部分列出了链接器移除的我们源码中实际未使用的数据和函数。其中包含移除数据的大小。例如Removing flash.o(.rrx_text), (6 bytes)。 表示移除 flash.o 中的 6 字节的代码;Removing virtualuart.o(i.VirtualUartBufClear), (92 bytes)。 表示移除 virtualuart.o 中的函数 VirtualUartBufClear,共 92 字节。
需要注意的是,被移除的函数在调试时将无法进行调试。如果不注意,在调试时很容易造成困扰。如下图所示:
Image Symbol Table
镜像符号映射表。就是每个符号(这里的符号可以指模块、变量、函数)实际的地址等信息。分为以下三部分:
Mapping Symbols
这一部分只有在指定了连接器参数 --list_mapping_symbols 才会有!下面是一个对比:
各列的含义如下:
Sym: 连接器定义的各种符号
Value: 符号表示的地址
Execution Region: 符号所在的执行域
Local Symbols
不知道 ARMCC 是怎么分的 Local 和 Global。各列含义如下:
Symbol Name
Value
Ov Type
Size
Object(Section)
Global Symbols
不知道 ARMCC 是怎么分的 Local 和 Global。
Memory Map of the image
该映射包含镜像文件中每个加载域,执行域和输入节(包括连接器生成的输入节)的地址和大小。由连接器 armlink 通过参数 --map 生成。这部分与 分散加载文件(Scatter File) 有密切的关系。如果使用 Keil,则在 Keil 的配置界面中有如下配置:
这里的设置就是对应的分散加载文件中的内容。如果我们在链接器的配置页面不选择 Use Memory Layout from Taget Dialog,则需要如上图自己指定一个分散加载文件。其实,如果选择 Use Memory Layout from Taget Dialog,Keil 会根据我们左边的配置自行生成一个分散加载文件来给链接器使用(这个文件就在我们的编译输出指定的目录中)。上图的示例就是没有使用 Keil 默认,而是通过自己指定的分散加载文件生成镜像文件。
以上的 Keil 配置,最终是通过链接器的参数 --scatter=filename 来让链接器使用该文件的。分散加载文件一次性描述了我们的镜像文件怎么布局。了解链接器的应该知道,链接器还有一些独立使用的和镜像文件生成有关的参数:--first, --last, --partial, --reloc, --ro_base, --ropi,--rosplit, --rw_base, --rwpi, --split, --startup, --xo_base, and --zi_base.,如果使用了 --scatter=filename,则以上参数就不可再用了! Keil 就是直接使用的 --scatter=filename。(默认下,Keil 根据配置界面的配置,会成一个分散加载文件)。
接下来以一个示例来看看 map 文件中关于镜像内存映射的内容,如下图:
其中最下面的三行数据是汇总的再汇总,以方便我们的使用
Total RO Size: 就是我们的可执行程序中常量数据(代码和只读数据)的大小。
Total RW Size: 就是我们的可执行程序中需要占用的内存的大小。
Total ROM Size: 就是我们的可执行程序本身的大小。这个大小就等于我们的可执行文件的大小。
需要特殊注意的是,armlink 输出的是 .axf 文件,这个文件中包含调试信息,并不是我们需要使用的可执行文件,我们使用 fromelf 工具从中提取的文件才是真正的可执行文件。这里的汇总大小指定是实际使用的可执行文件的大小。
ARM 库文件
我们可以在 ARM 编译套件的目录下找到这两个文件,路径如下图所示:
map 文件是什么
map 文件对应的中文名应该是映射文件,用来展示(映射)项目构建的链接阶段的细节。通常包含程序的全局符号、交叉引用和内存映射等等信息。目前,大多数编译套件(主要是其中的链接器)都可以生成 Map 文件。常见的 GCC、VC、IAR 都可以输出 map 文件(PC平台的 map 文件与 ARM 平台的差别较大)。
在 ARM 的官方文档中,并没有找到有关于 ARM 内核的 map 文件的介绍文档。不过倒是有个 C51 生成的 map 文件的说明文档:Listing (MAP) File。但是 C51 的 map 文件和 ARM 核的 map 文件差别比较大,也没啥参考价值!
map 文件就是用来展示链接器工作过程的东西。想要了解 map 文件还需要对 ARM ELF 文件有一定的了解以及需要对于编译过程有一定的了解。下图显示了链接器在软件开发过程中的角色。链接器接受几种类型的文件作为输入,包括对象文件、命令文件、库和部分链接的文件,创建一个可执行对象模块。
map 文件从哪来
map 文件是由编译套件中的链接器产生的。ARM 的编译套件中链接器为armlink,map 文件中的各信息均由 armlink 的各参数(–info topic、–map、–symbols等)控制输出(由 --list=filename 文件名输出到文件)。关于 ARM 编译套件的详细信息。下面是 armlink 的和 map 文件有关的参数介绍
--list=filename
将诊断输出重定向到指定名字为 filename 文件,即输出 filename.map 文件。
语法
--list=filename。其中 filename 是用于保存诊断输出的文件。 文件名可以包含路径。
使用
将参数 --info, --map, --symbol, --verbose, --xref, --xreffrom 和 --xrefto 的诊断信息输出重定向到文件。
输出诊断信息时将创建指定的文件。 如果已存在同名文件,则会覆盖该文件。 但是,如果未输出诊断,则不会创建文件。 在这种情况下,具有相同名称的任何现有文件的内容保持不变。如果指定了 filename 而没有路径,则会放在与链接器生成的可执行文件的相同目录下。
--info=topic[,topic,…]
打印有关指定主题的信息。通常与上面的 --list=file 一起使用,将输出内容写入指定的文本文件中。
语法
--info=topic[,topic,…]。其中 topic 是以下关键字以逗号分隔的组合(也可以每次只用一个关键字,然后写多个 --info=关键字):
any:对于使用 .ANY 模块选择器的节区,列出:
排序顺序
放置算法
The sections that are assigned to each execution region in the order they are assigned by the placement algorithm.
有关每个域使用的应急空间和策略的信息。
在分散加载文件中使用执行域属性 ANY_SIZE 时,此关键字还会显示其他信息。
用法
--info=sizes,totals 的输出始终包含输入对象和库的总计中的填充值。
如果使用 RW 数据压缩(默认值),或者使用 --datacompressor=id 选项指定了压缩器,则 --info=sizes,totals 的输出包括 Grand Totals 下的条目以反映真实镜像大小。
列表中的关键字之间不允许有空格。
Keil 配置
链接器列表文件或 Map 文件包含有关链接/定位过程的大量信息。在 Keil 中,需要通过 Project -》 Options for Target -》 Listing 界面如下的配置才可以输出 map 文件:
其中,有些符号需要我们知道其含义:
.data:这些部分保存有助于程序内存映像的已初始化数据
.text:本节包含程序的文本或可执行指令
.bss:本节保存有助于程序内存映像的未初始化数据
.ARM.exidx*:以.ARM.exidx开头的节包含部分展开的索引条目
i.xxxx: i 是 interface 的意思,i.xxxx 就表示 xxx 接口(一般就是指函数名)
Section Cross References
该部分显示了节区之间的交叉引用,指的是各个源文件生成的模块之间相互引用的关系,是由 armlink 的参数 --xref 生成的。主要分为以下几种情况:
用户代码间接口的相互引用: 例如:stm32f4xx_ll_flash.o(i.LL_FLASH_EraseChip) refers to stm32f4xx_ll_flash.o(i.LL_FLASH_IsActiveFlag_BSY) for LL_FLASH_IsActiveFlag_BSY 表示 stm32f4xx_ll_flash.o 中的函数 LL_FLASH_EraseChip 引用了 stm32f4xx_ll_flash.o 中的 函数 LL_FLASH_IsActiveFlag_BSY,其中的 stm32f4xx_ll_flash.o 是由用户代码 stm32f4xx_ll_flash.c 生成的模块
用户接口引用用户数据:主要有两类,例如:lora.o(i.LoRaRcvProcess) refers to lora.o(.data) for Radio 表示 lora.o 中的接口 LoRaRcvProcess 引用了 lora.o 中的已初始化数据 Radio;lora.o(i.LoRaRcvProcess) refers to lora.o(.bss) for ProcLoRa 表示 lora.o 中的接口 LoRaRcvProcess 引用了 lora.o 中的未初始化默认初始化为0数据 ProcLoRa
用户数据引用接口: 这个要是函数指针的使用。例如:pnwz.o(.data) refers to pnwzuif.o(i.PNWZ_USER_RespGetLife) for PNWZ_USER_RespGetLife
用户接口引用 C 库接口: 例如:pnwzuif.o(i.PNWZ_USER_ReqGetVerHW) refers to strlen.o(.text) for strlen
代码引用接口: 主要发生于 C 库之间(ARM C 库是不开源的,仅提供二进制文件),例如:``
C 库接口之间的引用: 主要发生于 C 库之间(ARM C 库是不开源的,仅提供二进制文件),例如:__2sprintf.o(.text) refers to _sputc.o(.text) for _sputc 及 __printf_flags_ss_wp.o(.text) refers to __printf_wp.o(i._is_digit) for _is_digit
C库引用连接器符号: 例如:exit.o(.text) refers to rtexit.o(.ARM.Collect$$rtexit$$00000000) for __rt_exit
符号之间的引用: 例如:retnan.o(x$fpl$retnan) refers to trapv.o(x$fpl$trapveneer) for __fpl_cmpreturn
在 ARM 编译套件中,所有的 C 库由工具armar来管理,位于 ARM 编译器目录 lib 下。使用 armar 即可从指定的库文件中解压出 __main.o 等模块。至于如何操作,参见博文ARM 之九 Cortex-M/R 内核启动过程 / 程序启动流程。
Removing Unused input sections from the image
这部分列出了链接器移除的我们源码中实际未使用的数据和函数。其中包含移除数据的大小。例如Removing flash.o(.rrx_text), (6 bytes)。 表示移除 flash.o 中的 6 字节的代码;Removing virtualuart.o(i.VirtualUartBufClear), (92 bytes)。 表示移除 virtualuart.o 中的函数 VirtualUartBufClear,共 92 字节。
需要注意的是,被移除的函数在调试时将无法进行调试。如果不注意,在调试时很容易造成困扰。如下图所示:
Image Symbol Table
镜像符号映射表。就是每个符号(这里的符号可以指模块、变量、函数)实际的地址等信息。分为以下三部分:
Mapping Symbols
这一部分只有在指定了连接器参数 --list_mapping_symbols 才会有!下面是一个对比:
各列的含义如下:
Sym: 连接器定义的各种符号
Value: 符号表示的地址
Execution Region: 符号所在的执行域
Local Symbols
不知道 ARMCC 是怎么分的 Local 和 Global。各列含义如下:
Symbol Name
Value
Ov Type
Size
Object(Section)
Global Symbols
不知道 ARMCC 是怎么分的 Local 和 Global。
Memory Map of the image
该映射包含镜像文件中每个加载域,执行域和输入节(包括连接器生成的输入节)的地址和大小。由连接器 armlink 通过参数 --map 生成。这部分与 分散加载文件(Scatter File) 有密切的关系。如果使用 Keil,则在 Keil 的配置界面中有如下配置:
这里的设置就是对应的分散加载文件中的内容。如果我们在链接器的配置页面不选择 Use Memory Layout from Taget Dialog,则需要如上图自己指定一个分散加载文件。其实,如果选择 Use Memory Layout from Taget Dialog,Keil 会根据我们左边的配置自行生成一个分散加载文件来给链接器使用(这个文件就在我们的编译输出指定的目录中)。上图的示例就是没有使用 Keil 默认,而是通过自己指定的分散加载文件生成镜像文件。
以上的 Keil 配置,最终是通过链接器的参数 --scatter=filename 来让链接器使用该文件的。分散加载文件一次性描述了我们的镜像文件怎么布局。了解链接器的应该知道,链接器还有一些独立使用的和镜像文件生成有关的参数:--first, --last, --partial, --reloc, --ro_base, --ropi,--rosplit, --rw_base, --rwpi, --split, --startup, --xo_base, and --zi_base.,如果使用了 --scatter=filename,则以上参数就不可再用了! Keil 就是直接使用的 --scatter=filename。(默认下,Keil 根据配置界面的配置,会成一个分散加载文件)。
接下来以一个示例来看看 map 文件中关于镜像内存映射的内容,如下图:
其中最下面的三行数据是汇总的再汇总,以方便我们的使用
Total RO Size: 就是我们的可执行程序中常量数据(代码和只读数据)的大小。
Total RW Size: 就是我们的可执行程序中需要占用的内存的大小。
Total ROM Size: 就是我们的可执行程序本身的大小。这个大小就等于我们的可执行文件的大小。
需要特殊注意的是,armlink 输出的是 .axf 文件,这个文件中包含调试信息,并不是我们需要使用的可执行文件,我们使用 fromelf 工具从中提取的文件才是真正的可执行文件。这里的汇总大小指定是实际使用的可执行文件的大小。
ARM 库文件
我们可以在 ARM 编译套件的目录下找到这两个文件,路径如下图所示: