- 在汇编中,我们很容易使用链接脚本lds内的值;因为对于汇编来说,这些都是符号标志而已,直接使用ldr rx, =xxx获取即可
那么在C语言中,我们如何得到链接脚本里的值呢?
可以在C语言中将链接脚本的值声明为外部变量(声明类型随意,可以声明为unsigned int/char甚至是指针变量),使用时需要使用取址符
extern unsigned int __bss_start;
extern unsigned int __bss_end;
unsigned int len;
len = (unsigned int)&__bss_end - (unsigned int)&__bss_start;
memset(&__bss_start, 0, len);
还可以将链接脚本的值声明为外部数组,使用时无需取址符,直接使用
extern char __bss_start[];
extern char __bss_end[];
unsigned int len;
len = __bss_end - __bss_start;
memset(__bss_start, 0, len);
总结: 声明为一般变量,使用需要加取址符;声明为数组,使用无需加取址符
纯C代码的重定位函数 ```c
if 0
/ 声明为外部变量 / / 源地址(加载地址)在汇编中通过传参方式得到: adr r0, _start / void SystemInit(void loadaddr) { extern char __text_start; / C中没法像汇编一样使用_start的链接地址,所以直接在链接脚本定义一个链接的起始地址 */ extern char bss_start; extern char bss_end;
unsigned int len;
/ text/rodata/data重定位 / len = &bss_start - &text_start; / 如果bss_start和text_start声明为int型, 则此处相减时需要强制类型转换成int来相减 /
/* 主要是因为char指针相减单位是1byte,int指针相减单位是4byte */
memcpy(&__text_start, loadaddr, len); / 目的, 源, 长度 /
/ clear bss / len = &bss_end - &bss_start; memset(&__bss_start, 0, len); / dest, val(0), len / }
else
/ 声明为外部数组 / / 源地址(加载地址)在汇编中通过传参方式得到: adr r0, _start / void SystemInit(void *loadaddr) { extern char text_start[]; extern char bss_start[]; extern char __bss_end[];
unsigned int len;
/* text/rodata/data重定位 */
len = __bss_start - __text_start;
memcpy(__text_start, loadaddr, len); /* 目的, 源, 长度 */
/* clear bss */
len = __bss_end - __bss_start;
memset(__bss_start, 0, len); /* dest, val(0), len */
}
endif
- 在汇编中调用重定位代码,并在重定位后跳转到链接地址上执行程序
```c
.text
.global _start
_start:
/* 设置sp */
ldr sp, =(0x80000000+0x100000)
adr r0, _start
bl SystemInit
/* 调用main函数 */
//bl main
ldr pc, =main
对于把链接脚本的值声明为外部变量/数组来使用的理解
在C语言中,定义一个全局变量,编译时会在符号表中出现;符号表中会出现该全局变量的地址 | Name | Address | | —- | —- | | g_a | xxxxxxxx |
对于链接脚本的符号,被声明为外部变量,则该变量在符号表中也会有一项,其地址就是其在链接脚本中表示的地址 | Name | Address | | —- | —- | | g_a | xxxxxxxx | | __bss_start | yyyyyyyy |
与全局变量相比,如何得到(链接脚本符号的)外部变量在符号表中对应的地址值呢?
- 全局变量可以用取址符&得到地址,当然我们外部变量也可以用取址符&得到地址
- 对于外部数组,当然也可以参照正常数组来,数组名=地址
- 对于
int g_a
变量- 使用
&g_a
得到符号表里的地址。
- 使用
- 对于
对于
extern unsigned int __bss_start
变量
- 要得到符号表中的地址,也是使用
&__bss_start
。对于
extern char __bss_start[]
变量
- 要得到符号表中的地址,直接使用
__bss_start[]
,不需要加&
- 为什么?`__bss_start本身就表示地址啊