韦东山新一期视频-代码重定位

1、由于在nor flash上启动,无法使用全局变量,因此希望能使用全局变量,所以需要重定位

2、重定位方法有两种,一种是只把变量重定位到sdram,另一种是把整个程序重定位到sdram

******************************

1、对于第一种,需要做的工作如下:

①在链接脚本中声名,需要将rodata段放到某个地址,但是如果只是这样写一下地址,那么中间的空间将会被占用导致文件过大。

②因此,需要达到这样的目的,既能告诉程序,rodata段在sdram,又能减小 文件的最终大小。需要采用如下格式:

SECTIONS {
   .text   0  : { *(.text) }
   .rodata  : { *(.rodata) }
   .data 0x30000000 : AT(0x800) { *(.data) }
   .bss  : { *(.bss) *(.COMMON) }
}

③上面只是一个声明,程序虽然知道应该从0x30000000读数据,但是我们并没有将数据放到这儿,因此读出来的必然是乱码。所以还需要把数据手动的放到这里。——当然,在这之前,应该先初始化sdram。

	/* 重定位data段 */
	mov r1, #0x800
	ldr r0, [r1]
	mov r1, #0x30000000
	str r0, [r1]

关键在于,我们怎么知道的全局变量保存到了0x800(上面是先反汇编然后知道的)?想要重定位的地址,是我们知道的,或者说知道重定位开始的地址。

①为了能够自动辨别拷贝的只读数据段的长度,所以在链接脚本中,应该添加一些变量——或者标签,使得我们可以引用这些地址。

因此,上面的链接脚本应该修改,使其更加智能

SECTIONS {
   .text   0  : { *(.text) }
   .rodata  : { *(.rodata) }
   .data 0x30000000 : AT(0x800) 
   { 
      data_load_addr = LOADADDR(.data);
      data_start = . ;
      *(.data) 
      data_end = . ;
   }
   .bss  : { *(.bss) *(.COMMON) }
}

②为了使得能自动初始化bss段,所以还需要修改——即获取开始和结束的运行地址。

   bss_start = .;
   .bss  : { *(.bss) *(.COMMON) }
   bss_end = .;

③但是仍然是问题,0x800应该怎么处理?猜测后面肯定要用到上一个段结束的地址。——并且需要对齐。

结果定义一个变量,等于当前地址即可。

 

 

 

 

 

 

 

 

链接脚本的解析

SECTIONS {
   .text   0  : { *(.text) }   
   .data 0x30000000 : AT(0x800) //.text是段的名字,0是运行时地址,AT(0x800)是加载地址,
								//如果没有此项,则默认和运行时地址相同
								//后面的括号中,可以指定存放的内容
   { 
      data_load_addr = LOADADDR(.data);
      data_start = . ;
      *(.data) 
      data_end = . ;
   }
   .bss  : { *(.bss) *(.COMMON) }
}

①链接得到elf格式的文件,该文件中含有地址信息——加载地址。

②使用加载器将该文件加载到内存——对于裸板为jtag调试工具,对于应用程序,则加载器也是应用程序。没懂

加载器对elf格式的文件进行解析,读入到内存——读到加载地址上

③如果运行时地址,和加载地址不同,则程序本身需要重定位,将程序重定位到运行地址。

其实,上面三点和之前的两点相同——先声明运行地址,再真正的去将程序搬运到运行地址

④需要注意的是,由于data段已经声明了放到0x30000000,所以其后的bss段也会跟着放到0x3000000后,data段结束的地址。

 

 

链接脚本和重定位代码的改进

①拷贝代码的改进,2440外接16位的nor flash和32位的sdram。所以拷贝的时候,如果按照单字节搬运,那么效率不高。

//旧代码使用ldrb和strb,按照1字节进行拷贝
cpy:
	ldrb r4, [r1]
	strb r4, [r2]
	add r1, r1, #1
	add r2, r2, #1
	cmp r2, r3
	bne cpy
//新代码可以使用ldr和str,按照字长4字节进行拷贝
cpy:
	ldr r4, [r1]
	str r4, [r2]
	add r1, r1, #4
	add r2, r2, #4
	cmp r2, r3
	ble cpy

但是发现结果却和预期不同,经过检查是因为bss段清除时,也把数据段给清楚了。

原因是,链接时地址未对齐,所以就会出错。

SECTIONS {
   .text   0  : { *(.text) }
   .rodata  : { *(.rodata) }
   .data 0x30000000 : AT(0x800) 
   { 
      data_load_addr = LOADADDR(.data);
	  . = ALIGN(4);
      data_start = . ;
      *(.data) 
      data_end = . ;
   }
   
   . = ALIGN(4);
   bss_start = .;
   .bss  : { *(.bss) *(.COMMON) }
   bss_end = .;
}

 

 

位置无关码

如果是和之前的做法一样,只拷贝数据段,那么就无所谓位置无关不无关,但是如果程序很大,代码段都放不下,那么就需要将整个程序重定位到运行时地址即链接地址,查看uboot中的代码,发现,程序是从0x30000000开始的,那么一上电的时候,为什么就可以从0运行呢?

原因是重定位之前的代码用位置无关码写成

如果拷贝整个程序,就需要从第一条指令运行时的地址开始拷贝,一直到bss段的开始

所以,在汇编文件中,就和地址解耦了,地址只需要在链接文件中指定。

需要注意的是,如果重定位完毕,就不应再使用相对跳转b/bl指令,否则程序将会再次返回,应该使用绝对跳转指令。

 

分析反汇编时发现,在sdram_init之前,就会使用sdram的地址,所以是使用的偏移。

那么怎么去分析跳转时使用的偏移地址还是绝对地址呢?

我们可以通过代码的编写,来保证是相对跳转,即位置无关码

①使用相对跳转指令b/bl

②不能访问全局变量和静态变量,也不能访问有初始值的数组

如果重定位完毕后,要使用绝对跳转,即ldr pc,=main

 

 

用c语言实现重定位和清楚bss段

数据搬运需要知道源,目的,长度

这里就涉及到了汇编如何向c函数传递参数。

①对于arm32,参数由r0到r3共四个通用寄存器来传递,所以需要把上面三个参数依次放入r0到r2三个寄存器,然后在c函数中用形参去接收。

②如果不通过寄存器来进行参数传递,而是调用汇编(链接脚本)中的变量,那么应该如何进行操作?

首先,需要声明变量为外部变量,然后再用一个指针去接收变量的地址。

根据操作,可以认为,汇编中的变量也只是一个普通变量,使用指针获取其地址后,就可以使用*操作符去获取值,也就是说,只要声明为外部变量就可以直接使用。

extern int __code_start,  __bss_start;
volatile unsigned int * dest = (volatile unsigned int *)&__code_start; 

符号表:

对于c语言的函数:name和addr

对于汇编/链接文件:name和addr

 

 

 

 

 

 

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值