S3c2440代碼重定位詳解6---重定位清除BSS段的C函數實現
2021-09-10 來源:eefocus
在前面,我們使用匯編程序來實現了重定位和清bss段,本節我們將使用C語言,實現重定位和清除bss段。
1.打開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 */
1、在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語言傳入的參數,依次就是R1、R2、R3。
編譯,燒寫運行沒有問題。
我們假設不想匯編傳入參數,而是C語言直接取參數。
1、修改start.S 跳轉到C函數不需要任何參數
bl sdram_init
//bl sdram_init2 /* 用到有初始值的數組, 不是位置無關碼 */
/* 重定位text, rodata, data段整個程序 */
bl copy2sdram
/* 清除BSS段 */
bl clean_bss
2、修改鏈接腳本,讓__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 = .;
}
3、修改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?
1、在C函數中聲明改變量為extern外部變量類型,比如extern int abc;
2、使用時,要取址,比如: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符號表?
1、對于常規變量g_i,得到里面的值,使用&g_i得到addr;
2、為了保持代碼的一致,對于lds中的a1,使用&a1得到里面的值;
結論:
1、C程序中不保存lds文件中的變量,lds再大也不影響;
2、借助symbol table保存lds的變量,使用時加上”&”得到它的值,鏈接腳本的變量要在C程序中聲明為外部變量,任何類型都可以;