位置无关码介绍

1. 基本概念

    应用程序必须经过编译、汇编和链接后才变成可执行文件,在链接时,要对所有目标文件进行地址重定位,建立符号引用规则,同时为变量、函数等分配运行地址。当程序执行时,系统必须把代码加载到链接时所指定的地址空间即链接地址(链接地址介绍在链接地址与运行地址,以保证程序在执行过程中对变量、函数等符号的正确引用,使程序正常运行。
    通常情况下,将bootloader程序下载到ROM0x0地址进行启动(比如固化到NorFlash中,再把链接起始地址指定为0,这样PC运行地址与链接地址就一样,代码能正常运行)。然而在很多的设计中,比如将bootloader固化在NAND中,在系统NAND启动复位后S3C2440ANAND控制器自动读取NAND中存储的前4K的代码到s3c2440A中称之为steppingstone4K片内RAM中,steppingstone中的代码用来进行一些非核心的硬件初始化,并把剩下的bootloader拷贝到SDRAM中运行,这样代码就既要能在片内RAM的0地址执行,也要能在SDRAM的地址上执行,这就需要位置无关代码来完成了。
    位置无关代码指代码不在链接时指定的运行地址空间也可以执行,即一段加载到任意地址空间都能执行的特殊代码。通常在像文件的前4K代码要用位置无关码设计。 
    位置无关代码可以用于以下场合:
      1. 程序在运行期间动态加载到内存; 
      2. 程序在不同场合与不同程序组合后加载到内存(共享的动态链接库);  
      3. 在运行期间不同地址相互之间的映射(如bootloader)。
   
    总结:位置无关代码,即该段代码无论放在内存的哪个地址,都能正确运行。究其原因,是因为代码里没有使用绝对地址,都是相对地址。而位置相关码,即它的地址与代码处于的位置相关,是绝对地址,如:mov PC ,#0xff;ldr  pc,=0xffff等。如果你的这段代码需要实现位置无关,那么你就不能使用绝对寻址指令,否则的话就是位置有关。

2. 位置无关码的写法

2.1 BL

    BL:带链接分支跳转指令,也是位置无关码(相对位置),用于调用函数用的

2.2 B

    B:分支跳转指令,指目标不能太远,一般用于同一个文件下的目标地址跳转

2.3 LDR

    LDR:通常都是作加载指令的,但是它也可以作伪指令,通常有两种不同的表示:

2.3.1 加载指令: (不加=号,位置无关)

LDR R0,Label 

    表示把Label表示的地址上的值存到R0(Label为程序标号,Label必须是当前指令的-4~4KB范围内),CPU通过计算偏移值得到,也就是相对地址,是位置无关码。

2.3.2 伪指令: (加=号,位置相关)

LDR r0, =Label

   如果Label是标号,表示把Label的地址存到R0中,这里面出现的“=”预示着这是一条伪指令,这个是取得标号Label绝对地址”,这个绝对地址就是在link的时候确定的,也就是链接地址或叫做编译地址
    如果Label是立即数,表示把立即数存入R0中。

bl  main /* 位置无关 */
ldr pc, =main /* 把main的地址放在pc,位置相关 */

    显然,用LDR时,加不加=号有很大区别:
      无=号表示取该标号处的值,位置无关;
      有=号表示取该标号的地址,位置相关。

2.4  bl/b调用的c语言函数里面也不要使用全局变量,因为c里面的全局变量的地址是根据链接地址生成的

3. 位置相关码示例

    汇编文件部分代码如下:

Reset:                  
    ldr sp, =4096           @ 设置栈指针,以下都是C函数,调用前需要设好栈
    bl  disable_watch_dog   @ 关闭WATCHDOG,否则CPU会不断重启
    // bl是位置无关码,相当于:PCnew = PC + 偏移
    //                         PCnew = (4+8) + 0x28 = 0x34
    
    //ldr pc, =disable_watch_dog  /* 这样写将出错 */
    
    bl  clock_init          @ 设置MPLL,改变FCLK、HCLK、PCLK
    bl  memsetup            @ 设置存储控制器以使用SDRAM
    bl  copy_steppingstone_to_sdram     @ 复制代码到SDRAM中
    ldr pc, =on_sdram                   @ 跳到SDRAM中继续执行
on_sdram:
    ldr sp, =0x34000000     @ 设置栈指针
    ldr lr, =halt_loop      @ 设置返回地址
    ldr pc, =main           @ 调用main函数
halt_loop:
    b   halt_loop

    链接脚本如下,链接地址在0X30000000:

SECTIONS {
    . = 0x30000000;
    .text          :   { *(.text) }
    .rodata ALIGN(4) : {*(.rodata)} 
    .data ALIGN(4) : { *(.data) }
    .bss ALIGN(4)  : { *(.bss)  *(COMMON) }
}

    反汇编如下:

30000000 <_start>:
30000000:    e3a0da01     mov    sp, #4096    ; 0x1000
30000004:    eb00000a     bl    30000034 <disable_watch_dog>
30000008:    eb00000d     bl    30000044 <clock_init>
3000000c:    eb000026     bl    300000ac <memsetup>
30000010:    eb000040     bl    30000118 <copy_steppingstone_to_sdram>
30000014:    e59ff00c     ldr    pc, [pc, #12]    ; 30000028 <.text+0x28>

30000018 <on_sdram>:
30000018:    e3a0d30d     mov    sp, #872415232    ; 0x34000000
3000001c:    e59fe008     ldr    lr, [pc, #8]    ; 3000002c <.text+0x2c>
30000020:    e59ff008     ldr    pc, [pc, #8]    ; 30000030 <.text+0x30>

30000024 <halt_loop>:
30000024:    eafffffe     b    30000024 <halt_loop>
30000028:    30000018     andcc    r0, r0, r8, lsl r0
3000002c:    30000024     andcc    r0, r0, r4, lsr #32
30000030:    30000200     andcc    r0, r0, r0, lsl #4

30000034 <disable_watch_dog>:
   ...  ...

    在汇编文件中有如下代码:   
      ldr pc, =on_sdram
    这一句等于反汇编中如下代码:
      30000014:    e59ff00c     ldr    pc, [pc, #12]    ; 30000028 <.text+0x28>
    其中30000014表示链接地址,e59ff00c表示指令机器码。下面进行分析,首先ARM中有两级流水线,如下图所示:
   

    PC指向正被取指的指令,而非正在执行的指令(也就是说PC的地址值是正在执行代码的地址值加上8)。故PC的地址值应该是此时的指令地址值0x30000014+8,而[pc, #12]表示0x30000014+8+12=0x30000028(8+12=20(十进制)=0x14(十六进制))。查看反汇编中的指令地址为0x30000028所对应的指令机器码如下:
      30000028:    30000018     andcc    r0, r0, r8, lsl r0
,可以看到0x30000028所对应指令机器码是30000018。当执行ldr    pc, [pc, #12]就令pc=0x30000018了。当0x30000018地址上没有对应的代码,而PC执行到该地址时,就会出错,这就是位置相关码(执行位置相关码时链接地址与运行地址必须一样)

  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值