在前面,我们使用汇编程序来实现了重定位和清bss段,本节我们将使用C语言,实现重定位和清除bss段。
1、使用C函数实现拷贝和bss段清除——汇编寄存器传入参数
- 打开start.S把原来的汇编代码删除改为调用C函数
/* 重定位text, rodata, data段整个程序 */
mov r1, #0
ldr r2, =_start /* 第1条指令运行时的地址 */
ldr r3, =__bss_start /* bss段的起始地址 */
cpy:
ldr r4, [r1]
str r4, [r2]
add r1, r1, #4
add r2, r2, #4
cmp r2, r3
ble cpy
/* 清除BSS段 */
ldr r1, =__bss_start
ldr r2, =_end
mov r3, #0
clean:
str r3, [r1]
add r1, r1, #4
cmp r1, r2
ble clean
改为:
/* 重定位text, rodata, data段整个程序 */
mov r0, #0
ldr r1, =_start /* 第1条指令运行时的地址 */
ldr r2, =__bss_start /* bss段的起始地址 */
sub r2, r2, r1 /*长度*/
bl copy2sdram /* src, dest, len */
/* 清除BSS段 */
ldr r0, =__bss_start
ldr r1, =_end
bl clean_bss /* start, end */
- 在init.c 实现如上两个C函数
void copy2sdram(volatile unsigned int *src, volatile unsigned int *dest, unsigned int len) /* src, dest, len */
{
unsigned int i = 0;
while (i < len)
{
*dest++ = *src++;
i += 4;
}
}
void clean_bss(volatile unsigned int *start, volatile unsigned int *end) /* start, end */
{
while (start <= end)
{
*start++ = 0;
}
}
汇编中,为C语言传入的参数,依次就是R0、R1、R2。
{
之前学过
(3)调用者(main函数)如何传参给被调用者(子函数)?
通过r0-r3寄存器。
(4)被调用者如何传返回值给调用者?
通过r0-r3寄存器。
}
编译,烧写运行没有问题。
2、使用C函数实现拷贝和bss段清除——C语言从链接脚本中直接取参数
- 修改start.S 跳转到C函数不需要任何参数
bl sdram_init
//bl sdram_init2 /* 用到有初始值的数组, 不是位置无关码 */
/* 重定位text, rodata, data段整个程序 */
bl copy2sdram
/* 清除BSS段 */
bl clean_bss
- 修改链接脚本,让__code_start 等于当前地址,也就是这里的0x30000000
SECTIONS
{
. = 0x30000000;
__code_start = .; //定义__code_start地址位当前地址
. = ALIGN(4);
.text :
{
*(.text)
}
. = ALIGN(4);
.rodata : { *(.rodata) }
. = ALIGN(4);
.data : { *(.data) }
. = ALIGN(4);
__bss_start = .;
.bss : { *(.bss) *(.COMMON) }
_end = .;
}
- 修改init.c 用函数来获取参数
void copy2sdram(void)
{
/* 要从lds文件中获得 __code_start, __bss_start
* 然后从0地址把数据复制到__code_start
*/
extern int __code_start, __bss_start;//声明外部变量
volatile unsigned int *dest = (volatile unsigned int *)&__code_start;
volatile unsigned int *end = (volatile unsigned int *)&__bss_start;
volatile unsigned int *src = (volatile unsigned int *)0;
while (dest < end)
{
*dest++ = *src++;
}
}
void clean_bss(void)
{
/* 要从lds文件中获得 __bss_start, _end
*/
extern int _end, __bss_start;
volatile unsigned int *start = (volatile unsigned int *)&__bss_start;
volatile unsigned int *end = (volatile unsigned int *)&_end;
while (start <= end)
{
*start++ = 0;
}
}
总结:
C函数怎么使用lds文件总的变量abc?
- 在C函数中声明改变量为extern外部变量类型,比如:
extern int abc;
使用时,要取址,比如:int *p = &abc;
//p的只即为lds文件中abc的值 - 汇编文件中可以直接使用外部链接脚本中的变量,但C函数中要加上取址符号。
解释一下原因:
C函数中,定义一个全局变量int g_i;,程序中必然有4字节的空间留出来给这个变量g_i。
假如我们的lds文件中有很多变量:
lds{
a1 = ;
a2 = ;
a3 = ;
...
}
如果我们C程序只用到几个变量,完全没必要全部存储lds里面的所有变量,C程序是不保存lds中的变量的。
对于万一要用到的变量,编译程序时,有一个symbol table符号表:
如何使用symbol table符号表?
- 对于常规变量g_i,得到里面的值,使用&g_i得到addr;
- 为了保持代码的一致,对于lds中的a1,使用&a1得到里面的值;
这只是一个编译器的小技巧,不用深究。
结论:
- C程序中不保存lds文件中的变量,lds再大也不影响;
- 借助symbol table保存lds的变量,使用时加上”&”得到它的值,链接脚本的变量要在C程序中声明为外部变量,任何类型都可以;