代码重定位和清除BSS段的C实现

文章目录

    • 1 使用汇编传参和C语言实现重定位及BSS段
    • 2 直接使用C语言进行代码重定位和BSS段清除
    • 3 C函数如何使用链接脚本中的变量

1 使用汇编传参和C语言实现重定位及BSS段

在前面,我们使用汇编程序来实现了重定位和清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 */

2. 在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。 编译,烧写运行没有问题。


2 直接使用C语言进行代码重定位和BSS段清除

我们假设不想汇编传入参数,而是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;
	}
}

编译烧写运行 ,没有问题。


3 C函数如何使用链接脚本中的变量

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符号表:

代码重定位和清除BSS段的C实现_第1张图片

如何使用symbol table符号表?

  • 对于常规变量g_i,得到里面的值,使用&g_i得到addr;
  • 为了保持代码的一致,对于lds中的a1,使用&a1得到里面的值;这只是一个编译器的小技巧,不用深究。

结论:
C程序中不保存lds文件中的变量,lds再大也不影响;
借助symbol table保存lds的变量,使用时加上"&"得到它的值,链接脚本的变量要在C程序中声明为外部变量,任何类型都可以。

你可能感兴趣的:(Linux)