MDK工程的文件类型:
1 下图为 Project 目录下的工程文件
实际上,只需要有 unprojx 文件,就可以恢复整个工程。
具体描述:
2 源文件
3 output 和 list 文件
详细分析:
1 uvprojx 文件
uvprojx 文件就是我们平时双击打开的工程文件,它记录了整个工程的结构,如芯片类型、工程包含了哪些源文件等内容。
可以使用vscode直接打开 uvprojx ,来看一下里面的具体内容:
2 uvoptx 文件
uvoptx 文件记录了工程的配置选项,如下载器的类型、变量跟踪配置、断点位置以及当前已打开的文件等。
我们在程序 main.c 的第81行设置断点,观察uvoptx的输出情况:
结果如下:
包括下载器类型等配置:
3 uvguix 文件
uvguix 文件记录了 MDK 软件的 GUI 布局,如代码编辑区窗口的大小、编译输出提示窗口的位置等。
uvprojx、 uvoptx 及 uvguix 都是使用 XML 格式记录的文件,若使用记事本打开可以看到 XML 代码。而当使用 MDK 软件打开时,它根据这些文件的 XML 记录加载工程的各种参数,使得我们每次重新打开工程时,都能恢复上一次的工作环境。这些工程参数都是当 MDK 正常退出时才会被写入保存,所以若 MDK 错误退出时(如使用 Windows 的任务管理器强制关闭),工程配置参数的最新更改是不会被记录的,重新打开工程时要再次配置。根据这几个文件的记录类型,可以知道 uvprojx 文件是最重要的,
删掉它我们就无法再正常打开工程了,而 uvoptx 及 uvguix 文件并不是必须的,可以删除,重新使用 MDK 打开 uvprojx 工程文件后,会以默认参数重新创建 uvoptx 及 uvguix 文件。(所以当使用 Git/SVN 等代码管理的时候,往往只保留 uvprojx 文件)。
4 源文件
5 output 目录下生成的文件
在mdk中,可以指定output文件的输出路径:
output生成的文件如下所示:
Ⅰ 静态链接库 .lib文件
得到生成的*.lib 文件后,可把它像 C 文件一样添加到其它工程中,并在该工程调用 lib提供的函数接口,除了不能看到*.lib 文件的源码,在应用方面它跟 C 源文件没有区别。
Ⅱ .dep .d依赖文件
*.dep 和*.d 文件(Dependency file)记录的是工程或其它文件的依赖, 主要记录了引用的头文件路径, 其中*.dep 是整个工程的依赖, 它以工程名命名, 而*.d 是单个源文件的依赖,它们以对应的源文件名命名。
先看一下.d文件,我们在整个文件夹中选一个.d文件:
直接vscode打开来看一下内容,可以看到,bsp_led.d文件,其实就是记录了bsp_led这个源文件包含的头文件路径。
知道单个文件的依赖以后,那么我们再看下 .dep 文件,记录了整个工程的依赖(记录了所有源文件所包含的头文件路径):
Ⅲ .crf 交叉引用文件
打开 .crf 乱码如下:
6 .o .axf .elf文件
ELF 是 Executable and Linking Format 的缩写,译为可执行链接格式,该格式用于记录目标文件的内容。在 Linux 及 Windows 系统下都有使用该格式的文件用于记录应用程序的内容,告诉操作系统如何链接、加载及执行该应用程序。
目标文件主要有 3 种类型:
6 o 文件与 axf 文件的关系
根据上面的分类,我们了解到, *.axf 文件是由多个*.o 文件链接而成的,而*.o 文件由相应的源文件编译而成,一个源文件对应一个*.o 文件。
例如:当我们在main.c中进行修改,如下所示,再点击编译时,由于没有改动其他的源文件,实际上只重新编译了main.c,而原来就生成的其他 .o 文件不需要再参与编译,链接器把新的 .o文件和之前其他 .o文件重新连接成 .elf文件。
然后再进行编译,结果如下:
链接器的作用:
代码如下:
图解过程如下:
7 ELF文件头
接下来我们看看具体文件的内容,使用 fromelf 文件可以查看*.o、 *.axf 及*.lib 文件的ELF 信息。
首先使用windows powershell,查看 fromelf 工具的帮助信息:
根据命令提示,接下来我们使用命令语句来查看bsp_led.o文件的基本信息:
fromelf --text -v .\bsp_led.o
打印信息如下,可以看到,一下子打印出来了很多信息。
为了方便查看,我们可以使用windows 重定向符号 > 将命令行中输出的内容存储到文件中,具体用法如下:
我们使用如下的命令语句,输出详细信息到txt:
fromelf --text -v .\bsp_led.o > bsp_led_info.txt
可以看到,在当前目录下新生成了 bsp_led_info.txt 的文件:
同样的,我们也可以生成整个 .axf 文件的详细信息:
fromelf --text -v .\流水灯.axf > flow_light_info.txt
结果如下:
在之前有分析过 .o 文件的结构如下,包括文件头、程序头(可选)、节区、节区头部表。
现在来看一下 bsp_led.o的文件内容:
接下来是节区信息:
关于节区信息的描述:
接下来看一下 .o文件 和 .elf 文件的对比:
再进行一下对比,可以看到,在节区前,elf文件确实存在程序头:
看一下程序头的具体内容:
下面是 MDK 的编译信息:
对照着 MDK 的编译信息,我们来分析一下这个文件:
1 首先是地址,程序头的物理地址和虚拟地址均为0X0800 0000,这是stm32内部flash的起始地址。而由于stm32不带有MMU内存管理单元,因此物理地址就等于虚拟地址;
2 Size in file:1440字节,表示程序在文件中占据的大小。对比上图,我们发现:
Size in file = Code + RO-data + RW-data = 1096 + 336 + 8 = 1440;
3 Size in memory:3976 字节,表示若程序加载到内存,占据的内存空间。
Size in memory = Code + RO-data + RW-data + ZI-data = 1440 + 2536 = 3976;
4 8字节对齐,这个在freertos中非常常用。
接下来看一下节区信息:
反汇编代码
使用以下语句生成反汇编代码:
fromelf --text -c .\bsp_led.o > bsp_led_o_info.txt
生成的反汇编代码如下:
经过链接器生成的elf文件,来和.o文件对比看一下:
现在我们可以从汇编代码中清晰的看到,链接后的elf文件,为每个函数都分配了地址,可以调用正确的指令来执行。
分散加载代码
学习至此,还有一个疑问,前面提到程序有存储态及运行态,它们之间应有一个转化过程,把存储在 FLASH 中的 RW-data 数据拷贝至 SRAM。然而我们的工程中并没有编写这样的代码,在汇编文件中也查不到该过程,芯片是如何知道 FLASH 的哪些数据应拷贝到 SRAM 的哪些区域呢?
其实主要是下面这段代码: