裸板:JZ2440
一、可执行程序的5个段
代码段.text:顾名思义,代码段就是用来存放代码的段。
只读数据段.rodata:只读数据段就是在程序运行期间只能读不能写的数据段,const修饰的全局变量的可能存放在只读数据段。
数据段.data:显示初始化为非0的全局变量和显示初始化为非0的static局部变量存放在数据段。
.bss段:显示初始化为0或者未显示初始化的全局变量和显示初始化为0或者未显示初始化的static局部变量存放在dss段。
.comment段:用来存放相关gcc编译链接指令的段。
二、如何实现代码重定位
实现代码重定位有两种方法,一种是将.text代码段和.data数据段分开存储,这种一般用在单片机,另一种是将.text代码段和.data数据段一起放到SDRAM中,CPU可以读写SDRAM,那么就可以实现程序对.data段的读写。
三、实现重定位
1.代码段和数据段分开存储,数据段内容放到SDRAM种,实现程序的读写
(1)用lds文件实现对各个Section的管理
SECTIONS {
.text 0 : { *(.text) }
.rodata : { *(.rodata) }
.data run address : AT(load address) { *(.data) }
.bss : { *(.bss) *(.COMMENT) }
}
用汇编实现单个地址的重定位
ldr r0, =load address
ldr r1, =run address
ldr r2, [r1]
strb r2, [r0]
run address是程序运行地址,是.data数据段要拷贝到SDRAM的地址,load address是程序加载地址,是原本.data段的地址。这就实现了.data数据段的重定位
(2)程序改进实现对整个.data数据段的重定位
lds文件
SECTIONS {
.text 0 : { *(.text) }
.rodata : { *(.rodata) }
.data run address : AT(load address)
{
data_load_add = LOADADDR(.data) ;
data_start = . ;
*(.data)
data_end = . ;
}
bss_start = . ;
.bss : { *(.bss) *(.COMMENT) }
bss_end = . ;
}
汇编
ldr r1, =data_load_add
ldr r2, =data_start
ldr r3, =data_end
cpy:
ldrb r4, [r1]
strb r4, [r2]
add r1, r1, #1
add r2, r2, #1
cmp r2, r3
bne cpy
说明:在lds文件中,’.'符号代表的是当前程序的运行地址 run address,LOADADDR宏用来求程序的加载地址 load address
2.代码段和数据段存储在连续的SDRAM中,CPU可以通过程序来对数据段进行读写
(1)lds文件配置各个段的地址
SECTIONS {
. = 0x30000000 ;
start_add = . ;
. = ALIGN(4);
.text : { *(.text) }
. = ALIGN(4);
.rodata : { *(.rodata) }
. = ALIGN(4);
.data : { *(.data) }
. = ALIGN(4);
bss_start = . ;
.bss : { *(.bss) *(.COMMENT) }
end_add = . ;
}
说明:ALIGN(4)宏表示4字节对齐地址,n表示n字节对齐,0x30000000为SDRAM地址。
(2)汇编和C实现代码重定位
1.汇编实现重定位和清除bss段:
mov r1, #0
ldr r2, =start_add
ldr r3, =end_add
cpy:
ldr r4, [r1]
str r4, [r2]
add r1, r1, #4
add r2, r2, #4
cmp r2, r3
bne cpy
ldr r1, =bss_start
ldr r2, =end_add
mov r3, #0
clean:
str r3, [r1]
add r1, r1, #4
cmp r1, r2
bne clean
2.用C语言传参方式进行重定位和清除bss段:
注:c语言函数被汇编启动代码调用时要通过通用寄存器 r0~r15传参
汇编:
bl sdram_init
mov r0, #0
ldr r1, =start_add
ldr r2, =bss_start
sub r2, r2, r1
bl cpy_src_dest
ldr r0, =bss_start
ldr r1, =end_add
bl clean_bss
C:
void cpy_src_dest(volatile unsigned int *src, volatile unsigned int *dest, unsigned int lenth)
{
unsigned int i = 0;
while(i <= lenth)
{
*dest++ = *src++;
i = i+4;
}
}
void clean_bss(volatile unsigned int *start, volatile unsigned int *end)
{
while(start <= end)
{
*start++ = 0;
}
}
3.用C语言无参数方式进行重定位和清除bss段:
汇编:
bl sdram_init
bl cpy_src_dest
C:
void cpy_src_dest(void)
{
extern int start_add;
extern int bss_start;
volatile unsigned int *src = (volatile unsigned int *)0;
volatile unsigned int *dest = (volatile unsigned int *)&start_add;
volatile unsigned int *end = (volatile unsigned int *)&bss_start;
while(dest <= end)
{
*dest++ = *src++;
}
}
void clean_bss(void)
{
extern int bss_start;
extern int end_add;
volatile unsigned int *start = (volatile unsigned int *)&bss_start;
volatile unsigned int *end = (volatile unsigned int *)&end_add;
while(start <= end)
{
*start++ = 0;
}
}
说明:start_add/end_add/bss_start/end_add都是在lds文件中编译器帮我们算出来的,要在C中使用,要用extern声明为外部变量
volatile关键字防止代码被优化