STM32裸机开发(6) — Keil-MDK下散列文件的分析

STM32裸机开发(6) — Keil-MDK下散列文件的分析

一、什么是散列文件

我们可以看到,在编译过程中有多个.o文件,而最后生成的只是一个文件,那么这些文件要怎么以什么方式生成一个文件呢?说的专业一点,这个过程就是链接,而在Keil-MDK下就是使用散列文件来指导链接的。
如图所示,将【Use Memory Layout from Target Dialog】勾选上
STM32裸机开发(6) — Keil-MDK下散列文件的分析_第1张图片
然后重新编译,我们就可以在【Objects】目录下得到一个.sct文件如下所示
STM32裸机开发(6) — Keil-MDK下散列文件的分析_第2张图片

二、散列文件的格式

如下所示,一个散列文件由一个或多个Load region组成,一个Load region中含有一个或多个Execution region,一个Execution region中又含有一个或多个Input section
首先,LR_IROM1是Load region的区域名,紧接着0x08000000是其加载地址,然后0x00080000是其区域最大容量;
然后,ER_IROM1是Execution region的区域名,紧接着0x08000000是其可执行地址,也可以叫链接地址,然后0x00080000也是其区域最大容量;
STM32裸机开发(6) — Keil-MDK下散列文件的分析_第3张图片
接下来就是各种文件的链接方式了,解释如下

LR_IROM1 0x08000000 0x00080000  {    ; load region size_region
  ER_IROM1 0x08000000 0x00080000  {  ; load address = execution address
   *.o (RESET, +First)	;所有的.o文件里的RESET段抽取出来放在最开始的位置
   *(InRoot$$Sections)	;所有的文件包括库,keil添加的可执行文件,看不到源码
   .ANY (+RO)			;等同于*,优先级比*低,这里表示所有的只读数据段
   .ANY (+XO)			;这里表示所有的只可执行段
  }
  RW_IRAM1 0x20000000 0x00010000  {  ; RW data
   .ANY (+RW +ZI)		;所有的可读可写数据段
  }
}

三、分析散列文件

打开uart.dis文件,可以看到有个__main,这就是keil添加的可执行代码,因为我们使用了main函数,所以这段代码被添加进去了,
STM32裸机开发(6) — Keil-MDK下散列文件的分析_第4张图片
我们将我们代码中的main改为mymain,可以看到,这段代码没有了
STM32裸机开发(6) — Keil-MDK下散列文件的分析_第5张图片
我们在main.c中添加如下所示变量定义,
STM32裸机开发(6) — Keil-MDK下散列文件的分析_第6张图片
然后编译,打开uart.dis文件,可以看到,myconst变量被链接到0x08…开头的地址,即ER_IROM1 区域,而mymydatemyzero即被链接到0x20…开头的地址,即RW_IRAM1 区域;与之相对应的,ER_IROM1 区域为该STM32的Flash地址,RW_IRAM1 区域为该STM32的RAM地址。
STM32裸机开发(6) — Keil-MDK下散列文件的分析_第7张图片
然后我们再看一下,uart.dis文件中,ER_IROM1 区域的最后一个位置0x080001ac存放的即我们定义的只读变量myconst
STM32裸机开发(6) — Keil-MDK下散列文件的分析_第8张图片
但我们使用STM32Cubeprg打开uart.hex文件可以看到,在0x080001ac后还有三个位置,而这三个位置存放的刚好就是我们定义的三个mymydatemyzero变量。
STM32裸机开发(6) — Keil-MDK下散列文件的分析_第9张图片
实际上,对于在STM32F103这类资源紧缺的单片机芯片中:

  • 代码段保存在Flash上,直接在Flash上运行(当然也可以重定位到内存里)
  • 数据段暂时先保存在Flash上,然后在使用前被复制到内存里(只读数据段不复制)

四、验证数据段的存放

修改main.c如下所示

#include "uart.h"
#include "led.h"

int mydata = 0x12315;
const int myconst = 0x22315;
int myzero = 0x0;
int my;

int mymain(void)
{
    uart_init();
    led_init();
    putstring("stm32f103zet6\r\n");
	putstring("mydata\t:");
    puthex((unsigned int)mydata);
    putstring("\r\nmyconst\t:");
    puthex((unsigned int)myconst);
	putstring("\r\nmyzero\t:");
    puthex((unsigned int)myzero);
    putstring("\r\nmy\t:");
    puthex((unsigned int)my);
    putstring("\r\n");

    while(1)
    {
		putstring("led on\r\n");
        led_on();
        delay(1000000);
		
		putstring("led off\r\n");
        led_off();
        delay(1000000);
    }
}

编译烧录运行,可以看到,只有myconst变量值是正确的,其他三个变量值都是错误的,这是因为我们并没有将那三个变量的值从Flash上复制到内存里,所以读取出来的只是上电后该RAM地址的一个随机值。
STM32裸机开发(6) — Keil-MDK下散列文件的分析_第10张图片
然后再添加一下地址的打印
STM32裸机开发(6) — Keil-MDK下散列文件的分析_第11张图片
编译烧录运行,可以看到,确实只有myconst变量值是保存在Flash的,其他三个变量值都是在内存里的

STM32裸机开发(6) — Keil-MDK下散列文件的分析_第12张图片

另外我们也可以顺便看一下栈地址,在mymain函数里定义一个变量并打印其地址

	int val = 3;
	
	putstring("val\t:");
    puthex((unsigned int)val);
	puthex((unsigned int)&val);
	putstring("\r\n");

编译烧录运行,可以看到,其地址确实在我们设置的栈中
STM32裸机开发(6) — Keil-MDK下散列文件的分析_第13张图片

五、附录

上一篇:STM32裸机开发(5) — 在Keil-MDK下编写uart串口打印程序
下一篇:STM32裸机开发(7) — 复制data段和清除BSS段(ZI段)
代码存放:https://gitee.com/william_william/stm32f103_noos/tree/master/keil-mdk/03_links

你可能感兴趣的:(STM32)