企业建设营销型网站的目的有,wordpress好用的模板,株洲新站建设,如何创造一款游戏.map 文件和 .lst 文件是嵌入式开发中最有用的俩调试辅助文件。现在主要从事 RISC-V 架构#xff0c;开始与 GCC 打交道#xff0c;今天就重点学习一下 GCC 的 .map 文件、.lst 文件#xff0c;并辅助以 ARMCC 和 IAR 作为对比。
编译工具链 .map 文件和 .lst 文件都是由编… .map 文件和 .lst 文件是嵌入式开发中最有用的俩调试辅助文件。现在主要从事 RISC-V 架构开始与 GCC 打交道今天就重点学习一下 GCC 的 .map 文件、.lst 文件并辅助以 ARMCC 和 IAR 作为对比。
编译工具链 .map 文件和 .lst 文件都是由编译工具链中相应的工具产生的。软件程序的编译过程由一系列的步骤完成每一个步骤都有一个对应的工具。这些工具紧密地工作在一起前一个工具的输出是后一个工具的输入像一根链条一样我们称这一系列工具为编译工具链。编译工具链主要包含 编译器等可执行程序 与 标准库常用函数通用实现 两大部分。 能独立提供编译工具链的厂家并不多嵌入式平台则更少主要就是 ARM、IAR、GNU、LLVM。其中ARM、IAR 是收费的专用软件其支持的架构有限而 GNU 的 GCC 则是一款支持众多架构的开源编译套件LLVM 则是后起之秀同样也支持众多架构目前用的不如 GCC 广泛如下是常用编译器 第一代编程语言的是汇编语言因此第一代编译器是编译汇编语言的。后来高级语言逐步流行但是编译工具链中仍然保留了高级语言到汇编语言这一步骤。而原来的汇编语言的编译器被称为汇编器高级语言的编译器则就称为编译器。除了编译器、链接器等编译工具链中通常还会包含一些用来处理编译文件相关的辅助工具。例如以 GCC 中的 objdump、readelf 为代表的的用来处理对象文件的工具。 至于标准库绝大多数工具链都是提供一些预编译好的二进制文件.o 文件当我们编译自己的程序时工具链自动以静态链接库的形式引入到我们的最终可执行程序中。通常这些预编译的文件还会被打包成 存档文件.a 文件 来提供编译工具链中有相应工具来解析执行存档文件例如GCC 中的 ar。 要编译出最终的可执行程序通常需要编译、链接、转换这三个阶段。其中编译即编译器将源码翻译成对象文件链接即链接器将各个对象文件组合成最终可执行程序。现代编译器通常产生一个通用格式通常是带有调试信息的最终可执行程序ELF 文件然后使用相应的工具从中提取出实际的纯可执行程序。 ELF 文件参见博文 Linux 之二十 详解 ELF 文件链接脚本文件见独立博文 Linux 之二十一 链接脚本文件 GCC 的.ld、ARMCC 的 .sct、IAR 的 .icf 有个问题需要注意GCC 中的 gcc 实际是一个包装器其会根据输入文件调用实际的编译器链接器。我们通过相应的命令就可以打断这个流程例如-c 选项表示不运行链接器 具体到编译器其编译过程通常也是分为多个阶段的。在编译原理这门课程中我们学过三段式编译器架构其在编译时要依次经过词法分析、句法分析、语义分析、中间代码生成、代码优化、代码生成 六大阶段。 在编译工具链中构建编译工具链使用的平台、编译出的编译工具链运行的平台、使用编译出的编译工具链编译出的程序运行的平台三者可以完全不同。其中关键的一步是设置 configure 的参数该命令有三个参数 --build、--host、--target 非常重要下图是 Windows 上的 MinGW 编译器配置信息
--build这个参数指出了构建编译工具链使用的平台。如果我们不显示指定这个参数的值那么这个参数的值就会由 config.guess 自动识别。--host这个参数指出了编译出的编译工具链运行的平台。这个参数的值一般就等于 --build 的值。--target这个参数指出了使用编译出的编译工具链编译出的程序运行的平台。 通常本地编译工具链一般就是指的 --build --host --target 的情况交叉编译工具链一般是--build --host ≠ --target 的情况。不过基本很少出现--build ≠ --host 的情况。
本地编译工具链 在当前平台例如 x86 架构的 PC下直接编译出来程序或者库文件其可以直接在当前的平台运行或使用。这个过程就叫做本地编译使用的编译工具叫做本地编译工具链简称编译工具链。例如 PC 上的 VC、GCC、LLVM、TCC 等。
交叉编译工具链 在当前平台下例如 x86 架构的 PC下直接编译出来程序或者库文件其不可以直接在当前的平台运行或使用必须放到目标平台上例如 ARM才可以运行或使用这个过程就叫做交叉编译使用的编译工具叫做交叉编译工具链。例如 PC 中的 armcc、iar、特定架构的 GCC、特定架构的 LLVM 等。 交叉编译工具链又可以根据是否支持 Linux 系统分为 裸机程序交叉编译工具链 和 Linux 程序交叉编译工具链 这两大类。我们上面的举例中armcc、iar 都属于裸机交叉编译工具链而特定架构的 GCC、特定架构的 LLVM 则根据需要可以支持 Linux 系统也可以不支持 Linux 系统因此它既有裸机程序交叉编译工具链也有 Linux 程序交叉编译工具链。
裸机程序交叉编译工具链不能编译 Linux 应用程序但是可以用于编译一些嵌入式实时操作系统FreeRTOS、RT-Thread 等Linux 程序交叉编译工具链不止可以编译 linux 应用程序也可以编译裸机程序
The xPack Project The xPack Project 是一个开源项目其提供了一些列开发工具重点是裸机下的 C/C 相关的在不同平台的下的构建实例其中就包含各平台的 GCC 交叉编译工具链它使用一个多版本依赖管理器来管理各个实例。 实际开发中我们经常会独立使用它提供的某些工具。例如Eclipse 的嵌入式 C/C 插件 Eclipse Embedded CDT 就包含一些 xPack 提供的工具以此来实现创建、构建、调试和管理 Arm 和 RISC-V 项目。 xPack Windows Build Tools包括在 Windows 上执行构建所需的额外工具( make、rm 等) xPack GNU Arm Embedded GCCARM 维护的官方 GNU ARM 嵌入工具链的一个代替可以用于 WindowsMacOS和 GNU/Linux 平台。 xPack GNU RISC-V Embedded GCC裸机 RISC-V GCC 发行版由SiFive 维护。Windows、macOS 和 GNU/Linux 都有可用的二进制文件。 xPack OpenOCDOpenOCD 的一个新发行版为更好/更方便地与 OpenOCD 调试插件集成而定制。Windows、macOS 和GNU/Linux 都有可用的二进制文件。 xPack QEMU ArmQEMU开源机器仿真器的一个分支旨在为 Eclipse Embedded CDT 中 的Cortex-M 仿真提供支持。Windows、macOS 和 GNU/Linux都有可用的二进制文件。
.map 文件 .map 文件对应的中文名应该是映射文件用来展示映射项目构建的链接阶段的细节。通常包含程序的全局符号、交叉引用和内存映射等等信息。目前常见的编译器套件实际是其中的链接器例如 GCC、ARMCC、IAR 都可以生成 .map 文件。 MAP 文件是一个与链接脚本文件密切相关的文件其内容取决于链接脚本文件链接脚本文件的详细介绍见独立博文 Linux 之二十一 链接脚本文件 GCC 的.ld、ARMCC 的 .sct、IAR 的 .icf。
GCC GCC 编译工具链中的链接器、汇编器、objdump 等工具都位于 GNU Binutils 中他们的相关文档可以直接在官网下载。相比于 ARMCC 和 IARGCC 的 MAP 文件中的内容比较少
产生方式 在 GCC 中MAP 文件就是由链接器 ld 通过使用命令 -Mapmapfile 来输出 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 之十 ARMCCKeil map 文件映射文件详解。
内容介绍 参见博文 ARM 之十 ARMCCKeil map 文件映射文件详解。
IAR
产生方式 由链接器参数 --map 来产生。当启用 MAP 文件后链接器支持的其他一些参数的输出就会输出到 MAP 文件中。
内容介绍 暂无后续有需要再补充
.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 HeaderSections 以及 SYMBOL TABLE 这个三部分的内容。编译工具产生的最终可执行文件是符合 ELF 规范的二进制文件这里的输出内容其实都是 ELF 文件的内容关于 ELF 文件可以参考 ARM 之一 ELF 文件、镜像Image文件、可执行文件、对象文件 详解。
Program Header这个就是 ELF 文件的程序头 Sections 这个就是从 ELF 文件中的提取的节区信息共由 8 列组成每列的含义说明如下 第一列Idx从 0 开始的索引号第二列Name 节的名字第三列Size节的大小字节第四列VMAVirtual Memory Address 的缩写表示该节在运行时的地址第五列LMALoad Memory Address 的缩写表示该节的加载地址第六列File off该节在文件中的偏移第七列Algn对齐字节数只能是 2 的正整数次幂由于纯文本没办法显示幂这里使用 ** 表示第八列Flags该节的属性标志 SYMBOL TABLE这个就是从 ELF 文件中的提取的符号表由 5 列组成每列的含义说明如下 第一列符号的值通常就是符号的物理地址第二列这是一组符号标识共由 7 个标识符组成某些标识符可以是空格。 第一个标识符取值如下 l local Symbolg Global Symbolu Unique global Symbol! 其他情况 第二个标识符取值为 w 表示弱符号Weak否则为空格表示强符号Strong第三个标识符取值为 C 表示构造函数Constructor 否则为空格表示普通符号第四个标识符取值为 W 表示警告符号Warning否则为空格表示普通符号。警告符号的名称是一条消息当警告符号后面的符号被引用时显示出来。第五个标识符 I 该符号是对另一个符号的间接引用i 在 reloc 处理期间计算的函数 空格表示普通符号 第六个标识符 d 调试符号D 动态符号 空格表示普通符号 第七个标识符 F 符号是函数的名称Function f 一个文件fileO 一个对象Object 空格表示普通符号 第三列符号对应的节区。如果该节是绝对的即没有与任何节连接则是 *ABS*如果该节在被转储的文件中被引用但在那里没有定义则是 *UND*。第四列对于普通符号是对齐方式对于其他符号则是大小第五列符号名字
ARM
产生方式 使用编译器 armcc 的参数 -asm 以及汇编器 armasm的参数 --listfile 来输出 lst 文件两者分别针对 C 语言源文件和汇编源文件生效
内容介绍 暂无后续有需要再补充
IAR
产生方式 使用编译器 iccarm 的参数 -l 以及使用汇编器 iasmarm 的参数 -L 来输出 lst 文件两者分别针对 C 语言源文件和汇编源文件生效
内容介绍 暂无后续有需要再补充
参考
https://dzone.com/articles/creating-disassembly-listings-with-gnu-tools-and-ehttps://interrupt.memfault.com/blog/get-the-most-out-of-the-linker-map-filehttps://sourceware.org/binutils/docs-2.40/binutils.html#index-objdump