- 重定位的实质就是:移动数据(数据拷贝)
- 把代码段、只读数据段、数据段,移动到它的链接地址处。也就是复制!
- 拷贝数据怎么知道源、目的、长度?
- 在GCC中,使用链接脚本来描述。
- 在keil中,跟链接脚本对应的是散列文件,散列的意思就是”分散排列“(代码段和数据段分开存储)
- 在STM32F103这类资源紧缺的单片机芯片中:
- 代码段保存在Flash上,直接在Flash上运行(当然也可以重定位到内存里)
- 数据段保存在Flash上,使用前被复制到内存里
- 代码段保存在Flash上,直接在Flash上运行(当然也可以重定位到内存里)
- 在资源丰富的MPU板子上:
- 内存很大,几十M、几百M,甚至几G
- 可能没有XIP设备(XIP: eXecute In Place,原地执行)
- 没有类似STM32F103上的Flash,代码无法在存储芯片上直接运行
- 没有类似STM32F103上的Flash,代码无法在存储芯片上直接运行
- 基于这些特点,在MPU板子上
- 代码段、数据段、BSS段等等,运行时没有必要分开存放
- 重定位时,把整个程序(包括代码段、数据段等),一起复制到它的链接地址去
- 代码段、数据段、BSS段等等,运行时没有必要分开存放
- 内存很大,几十M、几百M,甚至几G
链接脚本示例
/* 一个链接脚本可以由多个SECTION来组成, 示例中有4个section: .text .rodata .data .bss */
SECTIONS {
/* . : 表示当前地址; 指定当前地址为0xc0200000 */
/* 程序运行时应该位于这个链接地址上 */
. = 0xC0200000; /* 对于STM32MP157设置链接地址为0xC0200000, 对于IMX6ULL设为0x80200000 */
. = ALIGN(4); // 当前地址向4取整
.text : // .text : 段名; 存放所有文件的代码段
{
*(.text) // * : 表示所有文件; 放所有文件的代码段(从所有的.o文件中抽出代码段放在一起)
}
. = ALIGN(4);
.rodata : { *(.rodata) }
. = ALIGN(4);
.data : { *(.data) }
. = ALIGN(4);
// 链接脚本中想得到更多的信息(地址信息), 可以添加一些符号使其等于当前地址
// . 表示当前地址, 当前地址可读可写
__bss_start = .;
.bss : { *(.bss) *(.COMMON) } // .bss段将所有文件的.bss段和.COMMON段存放在一起
__bss_end = .;
}
链接脚本语法
一个链接脚本由一个SECTIONS组成。一个SECTIONS里面,含有一个或多个section
SECTIONS {
...
secname start BLOCK(align) (NOLOAD) : AT ( ldadr )
{ contents } >region :phdr =fill
...
}
section是链接脚本的核心
- secname:段名
- start:段的起始地址,不写时为当前地址
- BLOCK(align): 指定如何对齐
- AT ( ldadr ): 加载地址,一般不需要写,不写时加载地址就等于该段的起始地址(链接地址)
- contents: 段的内容
- (NOLOAD) 和 >region :phdr =fill暂时用不到,可以先不管