《ARM裸机》5--重定位

文章讲述了位置无关编码与位置相关编码的区别,以及在编译和链接程序时如何处理链接地址和运行地址。着重介绍了重定位的概念,如为何需要、如何在MakeFile中指定地址,以及如何通过编写连接脚本和代码搬运进行复杂项目的重定位。
摘要由CSDN通过智能技术生成

位置无关编码:汇编源文件被编码成二进制的可执行程序时,编码方式与位置(内存地址)无关

位置有关编码:汇编源文件被编码成二进制的可执行程序时,编码方式与位置(内存地址)有关

在设计程序时,会给程序指定一个运行地址。在编译程序时,其实知道被运行时的地址,而且必须给编译器连接器指定这个地址(链接地址),这就是与运行地址有关。但是有的特别的指令可以与运行地址无光,也就是这个指令运行时不管放到哪都能运行。而大部分的代码都是位置有关的。

对于位置有关代码来说:最终执行时的运行地址必须与编译链接时给定的链接地址一致。否则不能运行。

在MakeFile中用-Ttext 0X80100000来指定地址。

链接地址:链接时指定的地址。

运行地址:程序实际运行的地址。

重定位

        原因:链接地址和运行地址有时候必须不相同,而且还不能全部使用位置无关码,这时候就要用重定位。

        有的时候需要用iRAM,但是因为IRAM的空间又很小,所以只能把代码的部分重定位到iROM上。

 1.链接地址:链接时指定的地址,也是我们要指定程序最后的运行位置。很明显,我们利用imxdownload将程序烧录到SD卡中,而最终无论是在IROM还是在DDR中运行,加载地址(存储地址)和运行地址都是不同的。

        所以上电后,BootRom会自动对我们的程序进行重定位,而烧录imx文件的头部信息,是包含初始化ddr、地址信息的。就是将程序拷贝到对应的内存位置。

        简单一点的链接,就是直接在连接时直接指定连接地址。

arm-buildroot-linux-gnueabihf-ld -Ttext 0X80100000 led.o -o led.elf

         这样程序最后的运行地址就是连续的,从汇编文件的start开始,往后一次连续。

2.为了提高程序或者其他目的时,使得部分代码或数据需要放到别的位置,就需要对在拷贝到内存的部分代码或数据再进行重定位。

       我们可以通过编写连接脚本来实现更复杂的项目。比如上图,我们需要对data段进行从定位。

         0X00900000~0X0091FFFF是i.MX6ULL的IROM(internal ROM),这段内存是CPU的内部内存,所以运行速度要比DDR快得多,所以将部分代码放到这一段来运行会比在DDR上快。

        需要做如下操作:
        (1)编写.lds连接脚本

SECTIONS {
    . = 0x80100000;

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

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

    . = ALIGN(4);
    /*对data段进行重定位,将.data 段的运行地址(runtime address)
    设定为 0x900000。加载地址由变量 data_load_addr 确定。这样设置后,
    在.bin 文件中“ .data”段仍旧存储在“.rodata”段之后。但在程序运行时, 
    CPU 会从 0x900000 开始的空间内读取“.data 段”的值。*/
    data_load_addr = .;
    .data 0x900000 : AT(data_load_addr)
    {
      data_start = . ;
      *(.data) 
      data_end = . ;
    }

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

        (2)搬运代码,因为第一次的重定位后,bootROM会自动帮我们将程序拷贝到要运行的地址上去,但是之后我们在进行重定,CPU只是知道了要在新的地址上去找变量或者指令,所以我们要进行手动搬运。

        可以用在汇编里搬,也可以在用C程序搬。

        汇编:

bl  copy_data                /*跳到copy_data并保存下一句到lr*/
copy_data:
	/* 重定位data段 */
	ldr r1, =data_load_addr  /* data段的加载地址 (0x8010....) */
	ldr r2, =data_start 	 /* data段重定位地址, 0x900000 */
	ldr r3, =data_end 	     /* data段结束地址(重定位后地址 0x90....) */
cpy:
	ldr r4, [r1] 			 /* 从r1读到r4 */
	str r4, [r2] 			 /* r4存放到r2 */
	add r1, r1, #4 			 /* r1+1 */
	add r2, r2, #4			 /* r2+1 */
	cmp r2, r3 				 /* r2 r3比较 */
	bne cpy 				 /* 如果不等则继续拷贝 */

	mov pc, lr               /*搬运结束,利用lr回到原来位置的*/

       C语言:

        上面的搬运是用汇编里实现的,也可以在汇编里直接调用C语言函数,传入的参数,比如是3个参数,传入的参数就是r0,r1,r2。

void copy_data (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 copy_data (void)
{
	/* 从链接脚本中获得参数 data_load_addr, data_start, data_end */
	extern int data_load_addr, data_start, data_end;

	volatile unsigned int *dest = (volatile unsigned int *)&data_start;
	volatile unsigned int *end = (volatile unsigned int *)&data_end;
	volatile unsigned int *src = (volatile unsigned int *)&data_load_addr;

	/* 重定位数据 */
	while (dest < end)
	{
		*dest++ = *src++;
	}
}

        (3)编写Makefile

arm-buildroot-linux-gnueabihf-ld -T imx6ull.lds -o init.elf $^

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值