第十三课(4)实现一体式代码重定位与位置无关码

参考韦东山和http://www.bubuko.com/infodetail-2504561.html

1、实现一体式代码重定位

把整个程序复制到SDRAM需要哪些技术细节:

  1. 把程序从Flash复制到运行地址,链接脚本中就要指定运行地址为SDRAM地址;
  2. 编译链接生成的bin文件,需要在SDRAM地址上运行,但上电后却必须先在0地址运行,这就要求重定位之前的代码与位置无关(是位置无关码);

参考Uboot修改链接脚本:

SECTIONS
{
    . = 0x30000000;

    . = ALIGN(4);
    .text      :
    {
      *(.text)
    }

    . = ALIGN(4);
    .rodata : { *(.rodata) }

    . = ALIGN(4);
    .data : { *(.data) }

    . = ALIGN(4);
    __bss_start = .;
    .bss : { *(.bss) *(.COMMON) }
    _end = .;
}

修改start.S段:
注意0x30000000的位置就是汇编文件里定义的_start

    /* 重定位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

    bl main  

halt:
    b halt

烧写到Nor Flash,启动情况:

在这里插入图片描述
运行结果:
代码是正常运行的
g_A = 0x00000000
AaBbCcDdEe

疑惑:为什么可以运行正常呢,main的跳转是位置无关的,所以是跳转到Nor上的,获取的应该是Nor上的data段,Nor上的数据是不可写的,应该是不停写出AaAa才对吧。

代码已经拷贝到SDRAM上了,所以可以正常运行

2、从分析反汇编引入位置无关码的概念

查看反汇编:
bl 30000490 <sdram_init>的疑惑,这行代码是在cpy之前执行的,因此我们还没有将代码复制到SDRAM上,而这时候却去SDRAM上执行sdram_init,理论上是执行不了这个函数的。

30000058:	05810000 	streq	r0, [r1]
3000005c:	eb00010b 	bl	30000490 <sdram_init>
30000060:	e3a01000 	mov	r1, #0	; 0x0
30000064:	e59f204c 	ldr	r2, [pc, #76]	; 300000b8 <halt+0x14>
30000068:	e59f304c 	ldr	r3, [pc, #76]	; 300000bc <halt+0x18>

我们把链接脚本的起始地址改为0x320000000,发现机器码与之前起始地址为0x300000000是一样的,都是eb00010b,

32000058:   05810000    streq   r0, [r1]
 3200005c:   eb00010b    bl  32000490 <sdram_init>
 32000060:   e3a01000    mov r1, #0  ; 0x0
32000064:   e59f204c    ldr r2, [pc, #76]   ; 320000b8 <halt+0x14>
 32000068:   e59f304c    ldr r3, [pc, #76]   ; 320000bc <halt+0x18>

说明不是真的跳到30000490 的地址,实际是跳到pc+off_set的地址:
从0x30000000执行,当前指令地址为 3000005c,跳到30000490
从0执行,当前指令地址为 5c,则跳到490

总结下,位置无关什么意思呢?

既然这个时候SDRAM还没有代码,那就不去那里找了嘛,我们去别的地方找。别的地方是哪里呢?这个时候代码是运行在SRAM上的,处理器的PC寄存器也是指向这个地方的,有4K代码也是在这个地方的。那我就不用链接指定的地址来寻找想要的代码,我设计一些是与PC寄存器指向的位置有联系的寻址指令来寻址,这样PC寄存器指向哪里,我就在哪里找我想要的代码,而不是去链接指定的地址去找。

位置无关指令就是这样的指令,他不去链接指定位置寻找代码,而是在PC寄存器指向的位置相对它前后32M的范围寻找代码,也就是说只要我想要调用的代码在PC指向位置前后的32M范围内,就能找到(这是硬件完成的,我们知道位置无关指令就是这个意思就行)。当然,我们的SRAM只有4K,所以寻找的范围肯定就只能在这4K里面才行了,超过了也是找不到的。所以要求我们的重定位代码必须在这4K里面,必要的硬件初始化代码,内存初始化代码,nand初始化代码等等,必须在4K以内完成,否则就会出问题。所以位置无关指令的寻址是基于当前PC寄存器位置来寻址的。
因此要写位置无关码的时候,我们都要用位置无关的指令。

位置无关/相关指令:

B BL ADR MOV ADD等,这些指令都是位置无关指令

LDR STR等指令是位置有关指令

怎么写位置无关码?

  • 使用相对跳转命令 b或bl;
  • 重定位之前,不可使用绝对地址,不可访问全局变量/静态变量,也不可访问有初始值的数组(因为初始值放在rodata里,是使用绝对地址来访问);
  • 重定位之后,使用ldr pc = xxx,跳转到/runtime地址;
    写位置无关码,其实就是不使用绝对地址,判断有没有使用绝对地址,除了前面的几个规则,最根本的办法看反汇编。

在这里插入图片描述
bl main的时候 是跳转到Nor上的地址,是跳不到SDRAM上的main函数

要想让main函数在SDRAM执行,需要修改代码:

 //bl main  /*bl相对跳转,程序仍在NOR/sram执行*/
 ldr pc, =main/*绝对跳转,跳到SDRAM*/

你写的的sdram_init是与位置无关的代码,我们可以写一个与位置有关的——访问有初始值的数组,看看运行结果:

/**************************************************************************   
* 设置控制SDRAM的13个寄存器
* 使用位置无关代码
**************************************************************************/   
void memsetup(void)
{
	unsigned long *p = (unsigned long *)MEM_CTL_BASE;	
	p[0] = 0x22111110;		//BWSCON
	p[1] = 0x00000700;		//BANKCON0
	p[2] = 0x00000700;		//BANKCON1
	p[3] = 0x00000700;		//BANKCON2
	p[4] = 0x00000700;		//BANKCON3	
	p[5] = 0x00000700;		//BANKCON4
	p[6] = 0x00000700;		//BANKCON5
	p[7] = 0x00018005;		//BANKCON6
	p[8] = 0x00018005;		//BANKCON7
	p[9] = 0x008e07a3;		//REFRESH,HCLK=12MHz:0x008e07a3,HCLK=100MHz:0x008e04f4
	p[10] = 0x000000b2;		//BANKSIZE
	p[11] = 0x00000030;		//MRSRB6
	p[12] = 0x00000030;		//MRSRB7
}

void sdram_init2(void)
{
	unsigned int arr[] = {
		0x22000000, 	//BWSCON
		0x00000700, 	//BANKCON0
		0x00000700, 	//BANKCON1
		0x00000700, 	//BANKCON2
		0x00000700, 	//BANKCON3	
		0x00000700, 	//BANKCON4
		0x00000700, 	//BANKCON5
		0x18001, 	//BANKCON6
		0x18001, 	//BANKCON7
		0x8404f5, 	//REFRESH,HCLK=12MHz:0x008e07a3,HCLK=100MHz:0x008e04f4
		 0xb1,	//BANKSIZE
		 0x20,	//MRSRB6
		 0x20,	//MRSRB7

		};
	volatile unsigned int * p = (volatile unsigned int *)0x48000000;
	int i;

	for (i = 0; i < 13; i++)
	{
		*p = arr[i];
		p++;
	}
	
}

运行结果:
没有任何输出

因为我们知道,数组的初始值是保存在rodata段(绝对地址),后面才加载到栈上的。因此在Nor/SRAM上运行sdram_init2函数是需要访问SDRAM上的rodata地址,我们还没拷贝过去,是取不到我们要的数据的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值