目录
本文是基于韦东山视频的学习笔记
首先要通过 c 语言实现,就要搞清楚一个问题:怎么才能在 c 语言中获得代码的运行地址/加载地址和bss段开始结束地址?
两种方法
- 通过汇编传入函数参数
- 通过链接脚本获取
然而第一种方法是不完全的 c 语言实现,我们来实现第二种,在连接脚本里
SECTIONS
{
. = 0X30000000;__code_start = .; //我们加入这句话,以方便 c 语言获取代码运行地址
. = ALIGN(4);
.text : {*(.text)}. = ALIGN(4);
.rodata : {*(.rodata)}. = ALIGN(4);
.data : {*(.data)}. = ALIGN(4);
__bss_start = .;
.bss : {*(.bss) *(.COMMON)}
_end = .;}
我们需要从链接脚本获取这几个“变量”
- __code_start
- __bss_start
- _end
但是要怎么获取呢。在此之前,先来说说符号表(The symbol table)。在编译时,比如全局变量等都会存进符号表里,而这个符号表不会写进程序。假如有全局变量 g_A, g_B, g_C等等,而链接脚本也有“变量” __code_start,__bss_start ,_end,符号表就会这样
The symbol table |
---|
变量名:g_A |
g_A的地址 |
变量名:g_B |
g_B的地址 |
变量名:g_C |
g_C的地址 |
…… |
“变量”名:__code_start |
__code_start 的值 |
“变量”名:__bss_start |
__bss_start 的值 |
“变量”名:_end |
_end的值 |
…… |
如果在使用指针指向g_A 的时候,我们需要这样:
unsigned int* p = (unsigned int*)&g_A;
于是乎,为了保持代码的一致性,就算时用指针指向取链接脚本的“变量”,我们一样地写出代码:
unsigned int* p = (unsigned int* )&__code_start;
说到这里,怎么写出代码就简单了。
void copy2sdram()
{
extern int __code_start, __bss_start, _end; //外部变量声明,类型是什么不重要,因为都是取值
volatile unsigned int *src = (volatile unsigned int*)0;
volatile unsigned int *dest = (volatile unsigned int*)&__code_start;
volatile unsigned int *end = (volatile unsigned int*)&__bss_start;
while (dest <= end) //dest在这个例子为0x30000000,end为bss段的起始地址,即运行代码的结束地址
{
*dest++ = *src++; //逐一赋值
}
}
void clean_bss()
{
extern int __bss_start, _end; //外部变量声明,类型是什么不重要,因为都是取值
volatile unsigned int *start = (volatile unsigned int*)&__bss_start;
volatile unsigned int *end = (volatile unsigned int*)&_end;
while (start <= end) //当bss段开始地址小于等于bss段结束地址时
{
*start++ = 0; //逐一清零
}
}