由ADR/LDR看U-boot结构

LDR 与ADR解析

LDR与STR 是ARM汇编中的一对指令,用于RAM与寄存器之间的数据交换,常见形式为
LDR R1,[R0] 将RAM中地址空间为R0中的值载入到R1
STR R1, [R0] 将R1中的值存储到RAM地址为R0的空间

这对指令相对与其他指令的不同在于,LDR指令的源操作数在后面,STR的源操作数在前面,不一致。

这是最常见的间接寻址方式,对于ARM体系来说,LDR还是一条伪指令,我们知道,不能直接编译成机器码
的指令就是伪指令,通常伪指令还需要编译器解释成多步,再进行汇编。
先看u-boot中LDR伪指令的用法:
163         ldr     r0, =pWTCON
164         mov     r1, #0x0
165         str     r1, [r0]
166 
167         /*
168          * mask all IRQs by setting all bits in the INTMR - default
169          */
170         mov     r1, #0xffffffff
171         ldr     r0, =INTMSK
172         str     r1, [r0]

其反汇编代码为:
   59 33f00064:       e3a00453        mov     r0, #1392508928 ; 0x53000000
   60 33f00068:       e3a01000        mov     r1, #0  ; 0x0
   61 33f0006c:       e5801000        str     r1, [r0]
   62 33f00070:       e3e01000        mvn     r1, #0  ; 0x0
   63 33f00074:       e59f040c        ldr     r0, [pc, #1036] ; 33f00488 <fiq+0x48>
   64 33f00078:       e5801000        str     r1, [r0]
注意63行这里:
ldr     r0, =INTMSK 被汇编解释为了
ldr     r0, [pc, #1036] ; 33f00488 <fiq+0x48>
这条只看指令从意思来看,是以PC(R15)作为基址寄存器偏移1036(0x40c)地址处空间的值复制给R0,

我们知道,ARM指令集为32位,低12为用来存储数据(高8为是数据,低4为偏移量),这对数据有限制,必须能通过偏移得到
的数据才能使用mov指令加载。而ldr伪指令能解决任何数据的加载。
根据上面的反汇编代码来看,fiq标号地址为:0x33f00440+0x48 = 0x33f00488(这里是绝对地址),而基于arm9的5级流水线取址,
在当前执行指令时,已经预取了两条指令,即PC = 当前地址(0x33f00074)+ 0x8(预取两条指令)+ 0x40c(偏移)= 0x33f00488.
  370 33f00488:       4a000008        .word   0x4a000008
  371 33f0048c:       000003ff        .word   0x000003ff
0x33f00488处定义了一个字类型,值为INTMSK,这样在加载INTMSK时,通过基于PC的间接寻址,得到值,这是在编译时确定的。

这是LDR伪指令的一种用法,在ARM汇编中非常常见,用于普通值加载基本形式:
LDR R1,=0xffcff  或者LDR R1,=DEFINE ‘不能用#0xffcff’

另一种用法:

151         ldr     r0, =SMRDATA
152         ldr     r1, _TEXT_BASE
153         sub     r0, r0, r1

这里的SMRDATA和_TEXT_BASE不是宏定义,而是标签,上面已经知道,如果是宏定义,编译器会自动分配一个缓冲字来存这个宏定义,但有时候
需要一段缓冲区来初始化某个控制器,比如这里的RAM,也或者我们要加载的值不是确定的,需要在链接时确定,那就必须预先声明,明确的告诉
编译器,需要把哪段区域作为缓冲池。
关键字.ltorg,正是起到这个作用,告诉编译器,从这里开始是用户定义的一个文字缓冲池,SMRDATA是缓冲池标号。

165         .ltorg
166 /* the literal pools origin */
167 
168 SMRDATA:
.ltorg
会被汇编成:
58760 33f37308:       33f3730c        .word   0x33f3730c
58762 33f3730c <SMRDATA>:
58763 33f3730c:       2211d120        .word   0x2211d120
在看上面151行的反汇编代码:
58750 33f372e0:       e59f0020        ldr     r0, [pc, #32]   ; 33f37308 <lowlevel_init+0x28>
58751 33f372e4:       e51f1010        ldr     r1, [pc, #-16]  ; 33f372dc <_TEXT_BASE>
58752 33f372e8:       e0400001        sub     r0, r0, r1
这种方式和直接常量的载入是一样的,只是需要手动定义一个缓冲池,pc + 0x32  = 0x33f37308(lowlevel_init_ 0x28)定义到缓冲区,缓冲区的
内容为0x33f3730c,标签SMRDATA为0x33f3730c,由此ldr     r0, =SMRDATA 即把SMRDATA标签地址(非标签内容)装入R0。

上面描述了LDR伪指令的两种用法,还一种用法贯穿u-boot中,即上面的152行:
152         ldr     r1, _TEXT_BASE
反汇编:
58751 33f372e4:       e51f1010        ldr     r1, [pc, #-16]  ; 33f372dc <_TEXT_BASE>
注意看与前面一种的区别
39 33f00040 <_TEXT_BASE>:
40 33f00040:       33f00000        .word   0x33f00000

58746 33f372dc <_TEXT_BASE>:
58747 33f372dc:       33f00000        .word   0x33f00000

看反汇编代码来说是没有什么区别的,都是以PC做偏移取内容。
上面的反汇编代码中贴出了两个_TEXT_BASE字,只是为了对比。ldr     r1, _TEXT_BASE执行后r1的值始终为0x33f00000,
而不是33f00040 或者33f372dc,这也说明是直接取的标签处内容。当然这也是间接寻址的一种,也是以PC做为基址寄存器。
这里引申出来的两个_TEXT_BASE,也说明了使用lDR伪指令时,寻址空间有限制(4K)。

解析了LDR的几种用法,这里进行总结,同时也引出代码位置无关的限制。
以coder角度:
1.进行常量加载时,使用ldr r0, =0xfffff或者ldr r0, = DEFINE_REG即可,不用关心具体加载过程,常量所存储位置不用关心。
2.需要使用大的文字池时,只需声明一个文字池.ltorg并定义文字池标签POOLS_LABLE,使用ldr r0, =POOLS_LABLE,即可得到文字池首地址。也不用关心具体存储位置
3.定义跳转参数或者链接时才确定的参数也或者使用的全局参数,用户需要自己定义所在位置,常见形式:
_lable :
    .word __label //
函数名
_lable :
    .word CONDIF_... //
常量定义
_lable :
    .word __end - __start //
链接参数等

如果是全局使用还需要加上
.globl lable
这样在定义位置处就是整个程序链接生成地址。

载入内容:
1.ldr r0 , =POOLS_LABLE 载入内容为地址
2.另外两种用法载入内容由编写代码决定(值或者地址)

现在可以来理解代码位置无关了,在arm-linux编译U-BOOT时,通常会使用-pie参数进行链接,目的是生成位置无关可执行文件。
-pie  position independent exeutable
位置无关代码分为:位置无关代码和位置无关数据,而数据无关一般不用考虑,只需要考虑代码位置无关。
而上面解析的ldr指令,严格意义上来讲是与代码有关的,比如:
ldr pc,_Lable 这是绝对地址跳转。为什么这里要用'严格意义'来修饰,虽然代码编译链接完以后,所有的标签都是绝对地址,但使用ldr的
大多时候都是用来间接载入值,是一些常量的访问,是位置无关的。

而另一些ARM指令才是真正位置无关的:
B/BL Lable
ADR/ADRL r0,_lable
B/BL
跳转指令是基于当前PC的偏移量而跳转
ADR伪指令的反汇编:
242 stack_setup:
243 
244         adr     r0, _start

86 33f000b8:       e24f00c0        sub     r0, pc, #192    ; 0xc0
被解释成了当前PC的偏移,不管哪个地址运行,都不会影响结果,这才是真是代码位置无关。通常这个指令用于读取当前
某个标号的地址,与该标号的绝对地址(链接)地址比较,判断程序的运行位置,u-boot正式这种方式来确定是否需要搬运代码。
B/BL/ADR指令的跳转范围都只有4K,所以局限很大,通常在大范围跳转时,可使用ldr指令,首先预先在某个位置(与该ldr指令不超过4K)设置声明定义
一个字标号_lable,内容为跳转的函数名,然后执行ldr pc,_lable,即可实现大范围的跳转,这里也是绝对地址的跳转,可实现从NOR跳入指定RAM地址。





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值