- 前面重定位了数据段、清除了BSS段,但是并没有重定位代码段,为什么程序也可以正常运行?
- 前面是说了,当程序使用位置无关码编写时可以放在任意地址运行
- 所以当我们使用位置无关码编写程序时,就可以不在链接地址上运行
如果程序使用了位置相关的指令码来编写时,就必须进行重定位,否则无法再对应的地址找到程序内容
- 如果代码段没有重定位,则不能使用链接地址来调用函数
汇编中
ldr pc, =main ; 这样调用函数时,用到main函数的链接地址,如果代码段没有重定位,则跳转失败
C语言中
void (*funcptr)(const char *s, unsigned int val);
funcptr = put_s_hex; /* 函数指针用的是函数的链接地址 */
funcptr("hello, test function ptr", 123);
使用函数指针时,实际上等于函数的链接地址
链接脚本中,将代码段text/只读数据段rodata/数据段data放在了一起,所以我们可以一次性重定位这三个段
. = ALIGN(4);
.text :
{
*(.text)
}
. = ALIGN(4);
__rodata_start = .;
.rodata : { *(.rodata) }
. = ALIGN(4);
.data : { *(.data) }
. = ALIGN(4);
__bss_start = .;
.bss : { *(.bss) *(.COMMON) }
__bss_end = .;
- 代码段放在最前面,所以其起始地址就等于_start地址
- 因而可以确定重定位的源、目的:
- ldr r0, =_start //确定目的
- adr r1, _start //确定长度
- data段的结束位置就是bss段的起始位置,所以可以算出重定位的数据长度
- ldr r3, =__bss_start
- sub r2, r3, r0 //确定长度
- 使用位置无关码需要注意什么?
- 只使用相对跳转指令:b、bl
- 不只用绝对跳转指令:
ldr pc, =main
- 不访问全局变量、静态变量、字符串、数组
**
- 重定位完后,使用绝对跳转指令跳转到XXX函数的链接地址去 ``` bl main // bl相对跳转,程序仍在原来的区域运行
ldr pc, =main // 绝对跳转,跳到链接地址去运行
ldr r0, =main // 更规范的写法,支持指令集切换 blx r0
- 重定位代码
/ text/rodata/data重定位 / ldr r0, =_start
adr r1, _start /* 源 */
ldr r3, =__bss_start
sub r2, r3, r0
bl memcpy /* r0: 目的, r1: 源, r2:长度 */
/* clear bss */
ldr r0, =__bss_start
mov r1, #0
ldr r2, =__bss_end
sub r2, r2, r0
bl memset /* r0: dest, r1: val(0), r2: len */
/* 调用main函数, 要使用绝对跳转指令,否则无法跳转到链接地址去执行 */
//bl main
ldr pc, =main
```