.map
文件和 .lst
文件是嵌入式开发中最有用的俩调试辅助文件。现在主要从事 RISC-V 架构,主要与 GCC 打交道,今天就重点学习一下 GCC 的 .map
文件、.lst
文件,并辅助以 ARMCC 和 IAR 作为对比。
本文中的 DEMO 主要是使用 https://gitee.com/itexp/STM32_BareMetal 这个代码,其中配置好了 ARM-MDK、IAR、ECLIPSE 等众多常用开发工具的项目工程。
编译
.map
文件和 .lst
文件就是编译工具链给出的构建过程的一些 LOG 文件。要编译出最终的可执行程序,通常需要编译、链接、转换这三个阶段。其中,编译即编译器将源码翻译成对象文件,链接即链接器将各个对象文件组合成最终可执行程序。现代编译器通常产生一个通用格式(通常是带有调试信息)的最终可执行程序(ELF 文件),然后使用相应的工具从中提取出实际的纯可执行程序。
能独立提供编译工具链的厂家并不多,嵌入式平台则更少,主要就是 ARM、IAR、GNU、LLVM。其中,ARM、IAR 是收费的专用软件,其支持的架构有限,而 GNU 的 GCC 则是一款支持众多架构的开源编译套件;LLVM 则是后起之秀,同样也支持众多架构,目前用的不如 GCC 广泛!如下是常用编译器:
编译工具链及基本概念参见独立博文 编译工具链 之一 基本概念、组成部分、交叉编译工具链、命名规则
.map 文件
.map
文件对应的中文名应该是映射文件,用来展示(映射)项目构建的链接阶段的细节。通常包含程序的全局符号、交叉引用和内存映射等等信息。目前,常见的编译器套件(实际是其中的链接器),例如, GCC、ARMCC、IAR 都可以生成 .map
文件。
GCC
GCC 编译工具链中的链接器、汇编器、objdump
等工具都位于 GNU Binutils 中,他们的相关文档可以直接在官网下载。相比于 ARMCC 和 IAR,GCC 的 MAP 文件中的内容比较少!
产生方式
在 GCC 中,MAP 文件就是由链接器 ld
通过使用命令 -Map=mapfile
来输出 map 文件。需要注意,如果同时使用了 --print-map
则会只将 MAP 内容输出到编译窗口,而不会生成 MAP 文件。
需要注意的是,如果使用了编译器 gcc
作为入口(通常都是这么做,不会直接调用链接器),则需要使用 -Wl,-Map,FILE
的方式来将参数传递给链接器。编译器参数 -Wl
是专门用来传递参数给链接器的。同样,如果使用了 -Xlinker --print-map
则只会将 MAP 信息输出到编译窗口,而不会再生成 MAP 文件!
在实际开发中,ECLIPSE 的相关配置如下所示。目前没有该明白,为何它要以 g++ 作为链接器的入口!
内容介绍
在理解内容之前,有必要先了解一下工具链提供的存档文件(Archive File)。前面说过,工具链提供了 lib
这一部分, lib
中的各个函数的实现是已经进行了预编译的,并且把预编译的各种 .o
文件打包成为存档文件(.a
文件)。编译单元即一个 .o
文件。
GCC 的 MAP 文件相比于 ARMCC 和 IAR 内容少了很多
存档文件引用关系
MAP 文件的第一部分内容列出了我们项目中所使用的标准库中提供的函数的基本调用情况,基本格式如下:
存档文件 (编译单元)
调用存档文件中接口的文件(调用的符号)
如下图所示的示例,表示 crt0.o 中正在调用 exit 这个接口,而 exit 位于 lib_a-exit.o 这个文件中,而 lib_a-exit.o 这个文件存在于 libg.a 这个存档文件中!
丢弃的输入段
MAP 文件的第二部分内容列出了链接器在生成最终可执行文件时丢弃的输入段,每行一个,四列的含义从左到右依次为节区名、地址、节区大小、节区所在的编译单元。注意,如果节区位于存档文件中,则最后一列是存档文件 + (编译单元)。
内存配置
MAP 文件的第三部分内容列出了我们的内存(存储)配置情况,每行一个,四列的含义从左到右依次为存储的名字、起始地址、大小(字节)、属性(r:读、x:执行、w:写)。
链接脚本和内存映射
关于链接脚本我们后面在详细介绍,这部分链接脚本就是一些 MRI 兼容的链接脚本命令,简单来说,使用 MRI 兼容的链接脚本命令引入一些对象文件(.o
)。 MRI 兼容的链接脚本命令是用于早期链接器的,在 ld
手册中有说明,现在存在的唯一的目的就是兼容!
这部分最有用的是紧随其后的内存映射,这部分列出了所有符号在内存(存储)中的物理地址,这部分就是在辅助调试时最常用的!
ARM
产生方式
参见博文 ARM 之十 ARMCC(Keil) map 文件(映射文件)详解。
内容介绍
参见博文 ARM 之十 ARMCC(Keil) map 文件(映射文件)详解。
IAR
产生方式
由链接器参数 --map
来产生。当启用 MAP 文件后,链接器支持的其他一些参数的输出就会输出到 MAP 文件中。
内容介绍
暂无,后续有需要再补充!文章来源地址https://www.toymoban.com/news/detail-717646.html
.lst 文件
.lst
文件全称是 Assembler list file,主要用来存储汇编程序列表数据,它通常会拥有比 .map
文件更详细的信息。借助 .lst
文件,同时通过查看栈帧结构(可以通过查看相应的手册来确定栈帧的组成),通过在 .lst
文件中查找 lr
的地址所在的位置,就能立刻定位到问题。
GCC
GCC 编译工具链中的链接器、汇编器、objdump
等工具都位于 GNU Binutils 中,他们的相关文档可以直接在官网下载。
产生方式
.lst
文件通常是由汇编器产生的,对于 GCC 的汇编器 as
,通过使用命令 -a[cdghlmns]=[FILE]
来输出 lst 文件。但需要注意的是,-a[cdghlmns]=[FILE]
参数是独立使用汇编器 as
的时候才生效的。
与上面说的 MAP 文件产生一样,如果使用了编译器 gcc
作为入口(通常都是这么做,不会直接调用汇编器),则需要使用 -Wa,-a,FILE
的方式来将参数传递给汇编器。编译器参数 -Wa
是专门用来传递参数给汇编器的。
在实际开发中,我们经常写 -Wa,-adhlns="$@.lst"
,这样,每编译一个源文件就会产生一个对应的 .lst
文件。需要注意的是,如果是高级语言文件(例如 C 语言),我们需要配置编译器的参数,不要以为仅仅配置汇编器 as
就行(在多数工具中编译器和汇编器是独立配置的)!
注意:在 ECLIPSE 中,它把 objdump
输出文件名也定为了 .lst
。objdump
实际是解析 ELF 文件的工具,与 LIST 文件本没啥关系。 在 ECLIPSE 中,通过如下配置就会默认启用命令 objdump --source --all-headers --demangle --line-numbers --wide "xxx.elf" > "xxx.lst"
来生成最终调试文件对应的 lst 文件,以此来辅助调试。
内容介绍
在实际开发中,汇编器 as
产生的 LST 文件基本很少使用,使用最多的是使用 objdump
工具产生的最终调试程序对应的 LST 文件。这里我们主要就介绍 objdump
产生的 LST 文件。上面说过了, objdump
输出的实际是 ELF 文件内容,详细介绍见独立博文 Linux 之二十 详解 ELF 文件。
–source、–demangle、 --line-numbers、–wide
就是将反汇编与 C 源码混合在一起显示出来。并且尽可能的翻译成人能看懂的代码(翻译符号名、对应行号)!
–all-headers
该命令会输出 Program Header,Sections 以及 SYMBOL TABLE 这个三部分的内容。编译工具产生的最终可执行文件是符合 ELF 规范的二进制文件,这里的输出内容其实都是 ELF 文件的内容,关于 ELF 文件,可以参考 ARM 之一 ELF 文件、镜像(Image)文件、可执行文件、对象文件 详解。
-
Program Header:这个就是 ELF 文件的程序头
-
Sections: 这个就是从 ELF 文件中的提取的节区信息,共由 8 列组成,每列的含义说明如下:
- 第一列(Idx):从 0 开始的索引号
- 第二列(Name): 节的名字
- 第三列(Size):节的大小(字节)
- 第四列(VMA):Virtual Memory Address 的缩写,表示该节在运行时的地址
- 第五列(LMA):Load Memory Address 的缩写,表示该节的加载地址
- 第六列(File off):该节在文件中的偏移
- 第七列(Algn):对齐字节数,只能是 2 的正整数次幂,由于纯文本没办法显示幂,这里使用
**
表示! - 第八列(Flags):该节的属性标志
-
SYMBOL TABLE:这个就是从 ELF 文件中的提取的符号表,由 5 列组成,每列的含义说明如下:
- 第一列:符号的值,通常就是符号的物理地址
- 第二列:这是一组符号标识,共由 7 个标识符组成,某些标识符可以是空格。
- 第一个标识符:取值如下
-
l
: Local Symbol -
g
: Global Symbol -
u
: Unique global Symbol -
!
:其他情况
-
- 第二个标识符:取值为
w
表示弱符号(Weak),否则为空格表示强符号(Strong) - 第三个标识符:取值为
C
表示构造函数(Constructor ),否则为空格表示普通符号 - 第四个标识符:取值为
W
表示警告符号(Warning),否则为空格表示普通符号。警告符号的名称是一条消息,当警告符号后面的符号被引用时显示出来。 - 第五个标识符:
-
I
: 该符号是对另一个符号的间接引用 -
i
: 在 reloc 处理期间计算的函数 -
-
- 第六个标识符:
-
d
: 调试符号 -
D
: 动态符号 -
-
- 第七个标识符:
-
F
: 符号是函数的名称(Function ) -
f
: 一个文件(file) -
O
: 一个对象(Object) -
-
- 第一个标识符:取值如下
- 第三列:符号对应的节区。如果该节是绝对的(即没有与任何节连接),则是
*ABS*
,如果该节在被转储的文件中被引用,但在那里没有定义,则是*UND*
。 - 第四列:对于普通符号是对齐方式,对于其他符号则是大小
- 第五列:符号名字
ARM
产生方式
使用编译器 armcc
的参数 -asm
以及汇编器 armasm
的参数 --list=file
来输出 lst 文件,两者分别针对 C 语言源文件和汇编源文件生效!
内容介绍
暂无,后续有需要再补充!
IAR
产生方式
使用编译器 iccarm
的参数 -l
以及使用汇编器 iasmarm
的参数 -L
来输出 lst 文件,两者分别针对 C 语言源文件和汇编源文件生效!
文章来源:https://www.toymoban.com/news/detail-717646.html
内容介绍
暂无,后续有需要再补充!
参考
- https://dzone.com/articles/creating-disassembly-listings-with-gnu-tools-and-e
- https://interrupt.memfault.com/blog/get-the-most-out-of-the-linker-map-file
- https://sourceware.org/binutils/docs-2.40/binutils.html#index-objdump
到了这里,关于编译工具链 之四 .MAP 文件、.LST 文件的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!