1. 问题的引入
led.imx = 头部 + led.bin
或led.stm32 = 头部 + led.bin
- 头部里含有位置信息(addr):固件要把led.bin复制到哪里去
- 链接程序时,指定了链接地址,一般来说头部信息的addr就等于链接地址
- 如果,偏要修改头部信息的addr,让它不等于链接地址,会发生什么事?
- 头部里含有长度信息(len):led.bin多大。
- 头部里含有位置信息(addr):固件要把led.bin复制到哪里去
如上所示,我们知道,如果我们要将编译生成得到的bin文件烧写到单片机中运行,就必须在bin文件的头部加上头部信息,这样ROM中的固件就会根据头部信息把bin文件拷贝到指定地址,这个地址就叫做下载地址,与下载地址对应的就是链接地址。
注意:链接地址与下载地址没有关系,两个不一定相等
1.1. 链接地址
链接地址又叫做中断向量表的首地址,又叫做程序的入口地址,是程序在链接的时候就已经确认下来的。
1.2. 下载地址
下载地址就是程序下载到单片机上的地址。又叫做加载地址。
1.3. 两者的关系
大部分人都认为,这两这不就是相等的吗。当然不是。像IMX6ULL或STM32MP157,由于芯片内部含有强大的ROM,可以从EMMC或SD卡中将程序拷贝到下载地址(下载地址信息存储在bin文件的头部信息中),但是像STM32F103或三星的2440,它们内部的芯片就没有那么强大的ROM,因此就需要在自己的代码中添加一些重定位相关的代码。
比如,就拿103为例,代码下载到0x0800 0000处,也就是下载到FLASH上,代码的DATA段是可读可写的,而FLASH是需要密钥才可以写入数据,因此就需要将DATA段移到SRAM上,这些技术细节都是keilIDE帮你完成了,所以你不会意识到。而像2440,你就要自己编写重定位相关的代码( 内存拷贝)。
1.4. 重定位
重定位就是把程序的逻辑地址空间变换成内存中的实际物理地址空间的过程。这句话的意思就是:使逻辑地址与实际物理地址一一对应的过程。
程序的逻辑地址就是链接地址,由于程序的下载地址不一定是逻辑地址,所以将程序重新定位(拷贝)到逻辑地址所对应的物理地址处就叫做重定位,这是一个过程。
将逻辑空间地址重定位到物理空间地址的时机有三种:
-
程序编译链接时。
-
程序装入内存时。
-
程序执行时。
2. 段的概念
代码段、只读数据段、可读可写的数据段、BSS段。
char g_Char = 'A'; // 可读可写,不能放在ROM上,应该放在RAM里
const char g_Char2 = 'B'; // 只读变量,可以放在ROM上
int g_A = 0; // 初始值为0,干嘛浪费空间保存在ROM上?没必要
int g_B; // 没有初始化,干嘛浪费空间保存在ROM上?没必要
所以,程序分为这几个段:
- 代码段(RO-CODE):就是程序本身,不会被修改
- 可读可写的数据段(RW-DATA):有初始值的全局变量、静态变量,需要从ROM上复制到内存(对于程序可以在flash执行的芯片)
- 只读的数据段(RO-DATA):可以放在ROM上,不需要复制到内存
- BSS段或ZI段:
- 初始值为0的全局变量或静态变量,没必要放在ROM上,使用之前清零就可以
- 未初始化的全局变量或静态变量,没必要放在ROM上,使用之前清零就可以
- 局部变量:保存在栈中,运行时生成
- 堆:一块空闲空间,使用malloc函数来管理它,malloc函数可以自己写