- 所谓重定位就是把一块数据从这个地址移动到另外一个地址,怎么确定这块数据的源、目的、长度呢?
- 想要重定位.data段和.rodata段,就要知道两段源(起始地址)、目的(目的地址:链接地址)、长度(结束地址)
- 怎么确定源(加载地址)和目的地址(链接地址)?
- 从链接脚本中可以获取_start、_rodata_start的链接地址
- 编写代码时可以直接获取_start的加载地址(因为_start是在汇编文件.s中指定的),但无法直接获取_rodata_start的加载地址
- 所有在链接脚本中定义的参数都是用的链接地址
- _start链接地址与加载地址的偏差就等于_rodata_start链接地址与加载地址的偏差,从而可以算出_rodata_start的加载地址
怎么获取_start的加载地址和链接地址?
- _start参数是在汇编文件中指定的,当使用ldr伪指令获取时得到的是链接地址,当使用adr伪指令获取时得到的是基于PC+offset的加载地址(adr取出当前标号的地址)
用ADR伪指令获得当前代码(当前代码位于加载地址处)的地址:
.text
.global _start
_start:
......
adr r0, _start
adr是伪指令,它最终要转换为真实的指令。它怎么获得
_start
代码的当前所处地址呢?实际上,adr r0, _start
指令的本质是r0 = pc - offset
,offset是在链接时就确定了- offset链接时确定,因此当程序还位于加载地址处时得到的_start地址就是其加载地址了
用LDR伪指令确定链接地址
.text
.global _start
_start:
......
ldr r0, =_start
ldr是伪指令,它最终要转换为真实的指令。它怎么获得
_start
的链接地址呢?_start的链接地址在链接时,由链接脚本确定。
在链接脚本里可以定义各类符号,在代码里读取这些符号的值
修改链接脚本
SECTIONS {
. = 0xC0200000; /* 对于STM32MP157设置链接地址为0xC0200000, 对于IMX6ULL设为0x80200000 */
. = ALIGN(4);
.text :
{
*(.text)
}
. = ALIGN(4);
__rodata_start = .;
.rodata : { *(.rodata) }
. = ALIGN(4);
.data : { *(.data) }
. = ALIGN(4);
__bss_start = .;
.bss : { *(.bss) *(.COMMON) }
__bss_end = .;
}
修改start.S
.text
.global _start
_start:
ldr r0, =__data_start /* r0 : 目的: 链接地址 */
/* 计算data段的当前地址:
* _start的链接地址 - _start的当前地址 = __rodata_start的链接地址 - rodata段的当前地址
* data段的当前地址 = __rodata_start的链接地址 - (_start的链接地址 - _start的当前地址)
*/
ldr r0, =__rodata_start
ldr r2, =_start /* link addr */
adr r3, _start /* load addr */
sub r2, r2, r3
sub r1, r0, r2 /* r1 : 源 */
ldr r3, =__bss_start
sub r2, r3, r0 /* r2: 长度 */
bl memcpy /* r0: 目的, r1: 源, r2:长度 */