裸机——重定位

自古美人都是妖i 提交于 2020-02-07 19:29:04

1.重定位:

       就是用地址无关码,把地址有关码加载到正确物理地址上。

       地址有关码就是使用地址有关的汇编命令的代码。

2.地址无关码与地址有关码

  地址无关码是运行地址与连接地址无关,也可以正确运行。

  地址有关吗是运行地址必须匹配链接地址才能运行。

3.连接地址与运行地址

  链接地址,是用来指导链接器连接的,本身可能会以立即数形式出现在代码中。(下面会有事例)

  运行地址,也是物理地址,是硬件设计时就决定了的。

4.为什么地址有关码必须要链接地址与运行地址匹配?

  因为地址有关码使用了地址有关的汇编指令,如长跳转 ldr(后面会分析)。

5.链接地址的指定方式

  (1)默认指定

    gcc test.c -o test.c   // 链接地址默认从 0 开始

  (2)-Ttext直接指定

    ld -o test.elf -Ttext 0x10 a.o b.o c.o // 从 0x10 开始

  (3)使用连接脚本

 

SECTIONS{
   . = 0xd0020010;
  
   .text : {
      start.o
      * (.text)  
   }        

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

}

   这时连接地址从 0xd0020010开始

6. 代码分析

.global _start
_start:

    bl relocate
    
    b .

relocate:
    // 通过比较_start符号的实际运行地址与连接地址决定是否重定位
    adr r0, _start    // run addr
    ldr r1, =_start  // link addr
    cmp r0, r1    
    beq run_dram    
    ldr r2, =bss_start
        // 进行重定位,拷贝 .text , .data
relocate_loop:    
    ldr r3, [r0], #4
    str r3, [r1], #4
    cmp r1, r2
    bne relocate_loop
            // clear bss
    ldr r0, =bss_start
    ldr r1, =bss_end
    mov r2, #0
        // 进行重定位, 清 .bss
clear_bss_loop:
    cmp r0, r1
    beq run_dram
    str r2, [r0], #4        
    b clear_bss_loop

 run_dram:
   ldr pc, =led_after

 

  理解代码的关键是:

    adr r0, _start    // run addr
    ldr r1, =_start  // link addr

  首先 _start 是标号,本质就是地址的别名。

  adr 是短跳转,本质是通过pc计算出 _start 标号的值(也就是程序的起始地址)。

  ldr 是长跳转,本质是通过链接器根据连接脚本对代码进行了修改,这里加载的 _start是连接脚本给的值。

  下面给出反汇编:

首先我的连接脚本为:

SECTIONS{
    . = 0xd0024000;
    
    .text : {
        start.o
        * (.text)
    }
    
    .data : {
        * (.data)
    }
    
    bss_start = .;
    .bss : {
        * (.bss)
    }
    bss_end = .;
}

  说明 _start 的连接地址为 0xd0024000

  下面汇编,

  第一列为使用链接地址时,没32位代码的地址,注意,这并不存在于运行的程序中。

  第二列为32位代码,后面是对应的汇编。

d002402c <relocate>:
d002402c:    e24f0034     sub    r0, pc, #52    ; 0x34              // 这句为 adr 
d0024030:    e59f1040     ldr    r1, [pc, #64]    ; d0024078 <run_dram+0xc>  // 这句是 ldr
d0024034:    e1500001     cmp    r0, r1
d0024038:    0a00000b     beq    d002406c <run_dram>
d002403c:    e59f2038     ldr    r2, [pc, #56]    ; d002407c <run_dram+0x10>

d0024040 <relocate_loop>:
d0024040:    e4903004     ldr    r3, [r0], #4
d0024044:    e4813004     str    r3, [r1], #4
d0024048:    e1510002     cmp    r1, r2
d002404c:    1afffffb     bne    d0024040 <relocate_loop>
d0024050:    e59f0024     ldr    r0, [pc, #36]    ; d002407c <run_dram+0x10>
d0024054:    e59f1024     ldr    r1, [pc, #36]    ; d0024080 <run_dram+0x14>
d0024058:    e3a02000     mov    r2, #0

d002405c <clear_bss_loop>:
d002405c:    e1500001     cmp    r0, r1
d0024060:    0a000001     beq    d002406c <run_dram>
d0024064:    e4802004     str    r2, [r0], #4
d0024068:    eafffffb     b    d002405c <clear_bss_loop>

d002406c <run_dram>:
d002406c:    e59ff010     ldr    pc, [pc, #16]    ; d0024084 <run_dram+0x18>
d0024070:    e2700000     rsbs    r0, r0, #0
d0024074:    d0037d80     andle    r7, r3, r0, lsl #27
d0024078:    d0024000     andle    r4, r2, r0
d002407c:    d0024190     mulle    r2, r0, r1
d0024080:    d0024190     mulle    r2, r0, r1
d0024084:    d0024114     andle    r4, r2, r4, lsl r1
d0024088:    00001a41     andeq    r1, r0, r1, asr #20
d002408c:    61656100     cmnvs    r5, r0, lsl #2
d0024090:    01006962     tsteq    r0, r2, ror #18
d0024094:    00000010     andeq    r0, r0, r0, lsl r0
d0024098:    45543505     ldrbmi    r3, [r4, #-1285]    ; 0x505
d002409c:    08040600     stmdaeq    r4, {r9, sl}
d00240a0:    00010901     andeq    r0, r1, r1, lsl #18

分析这句:

d002402c:    e24f0034     sub    r0, pc, #52

  程序运行,将 pc 值减 52,放到 r0,注意这里pc是运行地址,减52后正好就是程序的起始地址 _start处。

  所以,adr 的本质是通过 运行地址计算得到加载到寄存器的值。

再分析这句:

d0024030:    e59f1040     ldr    r1, [pc, #64]    ; d0024078 <run_dram+0xc> 

  ldr说明是将内存中的值放到 r1,具体是 pc+64 指向的内存,这里的 pc 装的也是运行地址,但由于二级流水线技术(取指,解析,运行),运行这句时,pc已经后移了8字节,所以实际上是 后移(64 + 8)/4 行,也就是这一行:

d0024078:    d0024000     andle    r4, r2, r0

  将这一行的数据放到 r1 中,这一行的数据是 0xd0024000 ,这是链接器根据链接脚本对代码进行修改的结果。

  所以,ldr的本质是使用 链接地址给的立即数。

  

  最后看这句

 run_dram:
   ldr pc, =led_after

  这是完成重定位执行的一句,

  是一句长跳转,通过上面的分析知道了这句会使用链接地址,程序会直接到 物理地址 == led_after 处运行,这就要求连接地址与物理地址匹配,这也正是前面进行重定位的意义。

 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!