arm重定位

首先看两行汇编代码:

   1:  adr r0, _start
   2:  ldr r1, =_start

同样是加载一个标号的地址值,adr和ldr有什么区别呢?注意这里的ldr不是命令ldr,而是伪指令ldr,若想区分它们请参看我的一篇博文《adr adrl ldr mov总结整理》。

要区分它们,就需要引入4个概念:

1、运行时地址起始位置:它芯片公司指定的一开始运行代码的位置。这个位置和芯片本身有关,不可改动。对于2440来说一般就是片内SRAM的首地址0x0;对于210来说就是片内SRAM中的地址0xD0020010。

2、链接地址起始位置:它是由程序员指定的,或者说是有链接脚本设定。是可以变动的。但是这个位置在程序链接之后,就会确定下来。

3、运行时地址:就在从运行时地址起始位置(包括起始位置)往后排都是运行时地址。

4、链接地址:就是从链接地址起始位置(包括起始位置)往后排都是链接地址。

说明了以上4点内容之后,我需要铺垫一些前提内容,adr r0, _start ; ldr r1, =_start

这两句代码是从朱老师的一个实验程序里直接截取出来,这实验的目的是演示重定位。之后这段代码我会贴到文章的最后。因为开发板是210的板子所以运行时地址是从0xd0020010开始的,链接地址设置为0xd0024000开始的。

整个程序编译之后,在进行反编译,我们查找adr r0, _start ; ldr r1, =_start 对应的反汇编内容:

1、adr r0, _start 对应的是:
d002401c: e24f0024  sub r0, pc, #36 ; 0x24

2、ldr r1, =_start对应的是:
d0024020: e59f1048  ldr r1, [pc, #72] ; d0024070

同样是加载_start的地址,反汇编之后却是截然不同的命令。首先我们需要会看反汇编,最左边的是链接地址,第二个是机器码,第三个是反汇编得到的内容,最右边分号之后的是反汇编编译器额外帮我们注释了一些内容方便我们阅读。

我们发现反汇编之后,有一个地方很不同,就是pc指针。ldr r1, =_start对于的反汇编pc指针被放到的了[]里面,而另一条反汇编没有。我们知道对于汇编而言,放到[]里面代表是取得寄存器的值并且将寄存器的值当作地址,来访问地址中存储的值。

而对于pc而言,当你直接读取pc的值时访问的是运行时地址,而当你读取[pc]的值时访问的是链接地址。

反观adr r0, _start 和ldr r1, =_start它们都是伪指令,意思也分别是读取运行时地址和读取链接地址。和反汇编意义吻合。

我们现在来验证,我们前面分析的是否正确。首先_start作为程序的最开始,所以_start如果对应运行时地址,那么读取的_start的值应该是运行时地址起始位置及0xd0020010。

观察反汇编及对应的汇编

1、adr r0, _start 
d002401c:   e24f0024  sub r0, pc, #36 ; 0x24

由于此时该句代码的链接地址是d002401c链接地址的起始位置设定的是0xd0024000,偏移量是0x1c,根据这个便宜量可以算出该句代码的运行时地址为0xd0020010 +0x1c = d002002C,前面提到pc的值对应的就是运行时地址所以pc = d002002C。

d002002C - (36 十进制)+ 8 (流水线)= D002 0010 ;正好得到了_start的运行时地址完全没错。

再看链接地址是否算错,首先_start作为程序的最开始,所以_start如果对应链接地址,那么读取的_start的值应该是链接地址起始位置及之前设定的0xd0024000。

2、ldr r1, =_start
 d0024020:   e59f1048  ldr r1, [pc, #72] ; d0024070

根据偏移量,这句的运行时地址是d0020030,如果说是运行时地址 + 偏移量(72十进制),得到的是D002 0078,再加8(流水线)等于D002 0080,显然不对。

明显这里的[pc]的值得到的是当前语句对应的链接地址,d0024020 + 偏移量(72十进制)+ 8 才等于D002 4070(这个值也正好是注释里的值)大家是不是奇怪,为啥值不是0xd0024000?是不是算错了?其实不是,你到D002 4070这个链接地址看看就会发现这里存放的值正好就是D002 4070。

代码如下:d0024070: d0024000  andle r4, r2, r0

这里符合ldr  r1, [pc, #72]这句指令的本意,他访问的就是这个值代表的地址中的值。(这种跳转的方法其实就是为了应对非法立即数,导致在一个机器码里放不下命令和数据的情况)

/*

* 文件名:led.s

* 作者:朱老师

* 描述:演示重定位(在SRAM内部重定位)

*/

#define WTCON0xE2700000

#define SVC_STACK0xd0037d80

.global _start// 把_start链接属性改为外部,这样其他文件就可以看见_start了

_start:

// 第1步:关看门狗(向WTCON的bit5写入0即可)

ldr r0, =WTCON

ldr r1, =0x0

str r1, [r0]

// 第2步:设置SVC栈

ldr sp, =SVC_STACK

// 第3步:开/关icache

mrc p15,0,r0,c1,c0,0;// 读出cp15的c1到r0中

//bic r0, r0, #(1<<12)// bit12 置0  关icache

orr r0, r0, #(1<<12)// bit12 置1  开icache

mcr p15,0,r0,c1,c0,0;

// 第4步:重定位

// adr指令用于加载_start当前运行地址

adr r0, _start  // adr加载时就叫短加载

// ldr指令用于加载_start的链接地址:0xd0024000

ldr r1, =_start // ldr加载时如果目标寄存器是pc就叫长跳转,如果目标寄存器是r1等就叫长加载

// bss段的起始地址

ldr r2, =bss_start// 就是我们重定位代码的结束地址,重定位只需重定位代码段和数据段即可

cmp r0, r1// 比较_start的运行时地址和链接地址是否相等

beq clean_bss// 如果相等说明不需要重定位,所以跳过copy_loop,直接到clean_bss

// 如果不相等说明需要重定位,那么直接执行下面的copy_loop进行重定位

// 重定位完成后继续执行clean_bss。

// 用汇编来实现的一个while循环

copy_loop:

ldr r3, [r0], #4    // 源

str r3, [r1], #4// 目的   这两句代码就完成了4个字节内容的拷贝

cmp r1, r2// r1和r2都是用ldr加载的,都是链接地址,所以r1不断+4总能等于r2

bne copy_loop

// 清bss段,其实就是在链接地址处把bss段全部清零

clean_bss:

ldr r0, =bss_start

ldr r1, =bss_end

cmp r0, r1// 如果r0等于r1,说明bss段为空,直接下去

beq run_on_dram// 清除bss完之后的地址

mov r2, #0

clear_loop:

str r2, [r0], #4// 先将r2中的值放入r0所指向的内存地址(r0中的值作为内存地址),

cmp r0, r1// 然后r0 = r0 + 4

bne clear_loop

run_on_dram:

// 长跳转到led_blink开始第二阶段

ldr pc, =led_blink// ldr指令实现长跳转

// 从这里之后就可以开始调用C程序了

//bl led_blink// bl指令实现短跳转

// 汇编最后的这个死循环不能丢

b .

时间: 2024-10-03 15:09:04

arm重定位的相关文章

ARM ELF函数重定位

ARM ELF的函数重定位与x86是一致的,但由于汇编指令不同,再鼓捣一遍. 示例代码: #include <stdio.h> #include <stdlib.h> int main () { puts ("Hello world"); sleep (1); FILE *fp = fopen ("1.c", "r"); fclose (fp); exit (0); } 通过 readelf -r 可以查看ELF中所有需要

s5pv210重定位

1:上一节解释了什么是位置无关码.位置有关码.当程序的加载地址与链接地址不一致的时候,如果使用位置有关码则需要重定位. 2:重定位的操作实质就是把链接脚本中的想要重定位的代码段如:.text. .data段的内容复制到链接地址处. 3:具体分析一下位置有关码,位置无关码:位置无关码和位置有关码实质的区别是操作使用的是绝对地址,还是PC + offset 的相对地址,主要集中在取址.和跳转两个操作上: b.BL.都是用的相对地址,所以是位置无关码, ldr pc =main 因为main标志在编译

Tiny6410之重定位代码到SRAM+6096

重定位代码 两个不同的地址概念: 对于程序而言,需要理解两个地址,一个是程序当前所处的地址,即程序运行时所处的当前地址.二是程序应该位于的运行地址,即编译程序时所指定的程序的链接地址.在Tiny6410中板子上电启动时只会从NAND Flash/MMC等启动设备中拷贝前8K的代码到SRAM中,然后跳转到SRAM中运行代码.那么问题就来了,如果我们的程序超过8K会出现什么问题呢?程序拷贝不完整运行当然出错.所以就需要我们在前8K的代码中实现将整个程序完整的拷贝到DRAM等其他更大的存储空间,然后在

u-boot移植(三)---修改前工作:代码流程分析3---代码重定位

一.重定位 1.以前版本的重定位 2.新版本 我们的程序不只涉及一个变量和函数,我们若想访问程序里面的地址,则必须使用SDRAM处的新地址,即我们的程序里面的变量和函数必须修改地址.我们要修改地址,则必须知道程序的地址,就需要在链接的时候加上PIE选项: 加上PIE选项后,链接时候的地址就会生成,然后存储在段里面,如下段(u-boot.lds): 然后我们根据这些地址的信息来修改代码,程序就可以复制到SDRAM的任何地方去. 二.代码流程 start.S中执行到了 bl _main,跳转到_ma

[PE结构分析] 10.基址重定位

源代码如下: typedef struct _IMAGE_BASE_RELOCATION { DWORD VirtualAddress; DWORD SizeOfBlock; // WORD TypeOffset[1]; } IMAGE_BASE_RELOCATION; typedef IMAGE_BASE_RELOCATION UNALIGNED * PIMAGE_BASE_RELOCATION; 重定位表是一个数组,这个数组的大小记载在 _IMAGE_OPTIONAL_HEADER 的 .D

Android漫游记(3)---重定位之GOT &amp; PLT &amp; R_ARM_JUMP_SLOT

Android系统的动态链接工具是/system/bin/linker(一般的Linux系统是ld.so),虽然名字不同,但是基本的动态链接过程是类似的.需要注意的一点是,Linux一般是Lazy,即所谓的"懒"加载方式,但是Android系统有点区别,是非Lazy方式,即所有的重定位操作,在进程首次执行以前已经全部完成.这大概也是Android应用首次启动比较慢的原因之一吧! 关于Android系统的PLT和GOT可以写上一篇高考作为,在这里就不提概念性的东西了,网上有一篇博文:ht

链接脚本与重定位

目录 链接脚本与重定位 总结 链接脚本格式 COMM段BSS段 elf和bin文件 获得链接脚本的值 重定位 1-直接指定数据段位置(代码黑洞) 2-分散加载(数据段) 3-全局重定位(一体式) BL跳转指令 bss段处理 汇编处理 C处理 位置无关码 title: 链接脚本与重定位 tags: ARM date: 2018-10-12 19:25:53 --- 链接脚本与重定位 学习视频 韦东山 总结 尽量使用一体式的链接脚本,方便简单,灵活 学会使用链接脚本的值 bss段和comm段是需要我

u-boot-2014.10移植(8)重定位,支持NAND启动

前面链接地址都是0x0,可以从nor正常启动内核和文件系统.现在把链接地址改成0x33f00000 一旦更改了链接地址, u-boot从nor flash加载时,串口没有任何输出 添加文件boot_init.c #define NFCONF (*((volatile unsigned long *)0x4E000000)) #define NFCONT (*((volatile unsigned long *)0x4E000004)) #define NFCMMD (*((volatile un

关于重定位代码的浅显理解

首先需要朱有鹏老师,这是在学习了朱老师的课程之后的一点理解,代码是根据朱老师的源码学习之后编写的. 根据反汇编代码  d0024010:     e24f0018       sub      r0, pc, #24 可以看出通过adr汇编伪指令将加载地址写入r0寄存器,此时因为adr指令采用相对寻址的寻址方式所以adr实际写入r0寄存器的地址为程序的加载地址而非反汇编代码所指示的0xd0024000,通过ldr伪指令将所需重定位的地址写入r1寄存器,此时r1所存的地址为0xd0024064,该