乐趣区

韦东山一期视频学习笔记代码重定位学习

一、反汇编

简单代码分析

把以下代码生成的 elf 文件使用命令 arm-linux-objdump -D led_on.elf > led_on.dis 反汇编
原始文件

.text
.global _start
_start:
    ldr r0, =0x56000010
    mov r1, #0x00015400
    str r1, [r0]

    ldr r0, =0x56000014
    ldr r1, =0x00000040
    str r1, [r0]
halt:
    b halt

反汇编文件

led_on_elf:     file format elf32-littlearm

Disassembly of section .text:

00000000 <_start>:
   0:    e59f0014     ldr    r0, [pc, #20]    ; 1c <.text+0x1c>
   4:    e3a01b55     mov    r1, #87040    ; 0x15400
   8:    e5801000     str    r1, [r0]
   c:    e59f000c     ldr    r0, [pc, #12]    ; 20 <.text+0x20>
  10:    e3a01040     mov    r1, #64    ; 0x40
  14:    e5801000     str    r1, [r0]

00000018 <halt>:
  18:    eafffffe     b    18 <halt>
  1c:    56000010     undefined
  20:    56000014     undefined

sublime 可以安装 HexViewer 查看 bin 二进制文件 HexViewer 的使用
二进制码

1400 9fe5 551b a0e3 0010 80e5 0c00 9fe5
4010 a0e3 0010 80e5 feff ffea 1000 0056
1400 0056 
  • ldr r0, [pc, #20] :表示 PC 的地址(当前地址也就是第一列值换算成十进制 + 十进制 8)加上 #20(十进制 20)地址 0x1c 上的数据 0x56000010 存入 r0
  • 可以看出开头的 1 列 0: 4: .. 这些表示机器码所在的起始地址是第几个字节,一行指令是 4 字节 32 位,第二列表示的是储存在内存中实际的字节数据(机器码),第三列表示的是字节数据对应翻译成的汇编指令
  • 第一列最大值为 0x20,0x20 处存在 4 字节数据所以一直到 0x23,所以 bin 程序一共有 36 字节(0x0~0x23)

一、代码段重定位

  • Norflash 可以直接读但是无法直接写入
  • Norflash 启动的栈设置与 Nand 启动不同
    mov r1, #0
    ldr r0, [r1] /* 读出原来的值备份 */
    str r1, [r1] /* 0->[0] */ 
    ldr r2, [r1] /* r2=[0] */
    cmp r1, r2   /* r1==r2? 如果相等表示是 NAND 启动 */
    ldr sp, =0x40000000+4096 /* 先假设是 nor 启动 */
    moveq sp, #4096  /* nand 启动 */
    streq r0, [r1]   /* 恢复原来的值 */
  • arm-linux-ld -Ttext 0x00000000 $^ -o main.elf 中加入 -Tdata 位置 可以简单指定.data 段位置
  • 从 Nor 启动片内 SRAM 起始地址为0x4000_0000,从 Nand 启动片内 SRAM 起始地址为0x0
名称 中文名称 存放类型 保存位置
.text 代码段 存放代码部分 bin 文件中
.data 数据段 存放全局变量 bin 文件中
.rodata 只读数据段 存放 const 只读变量 bin 文件中
.bss bss 段 存放无初始值或初始值为 0 的全局变量 不在 bin 文件中
.comment 注释段 存放命令 ascii 码 不在 bin 文件中
关于指定.data 段

因为如果 data 段存在于 norflash 中全局变量无法修改,所以我们需要把全局变量放入 SDRAM 中。
可以通过在 Makefile 中加入 -Tdata 位置 简单的指定 data 段位置但是会存在数据段和代码段之间有一个巨大的间隙空间最终导致 bin 文件可能非常的巨大, 例如 0~0x3000_000,可以通过两种办法解决

  1. 重定位.data 段
  2. 重定位整个代码数据段
链接脚本 lds 文件
简单示例
SECTIONS {.text 0 : {*(.text)}
    .rodata : {*(.rodata)}
    .data 0x3000000 : AT(0x800) {*(.data)}
    .bss : {*(.bss) *(.COMMON)}
}
  • AT(0x800)指定 data实际位置 为 0x800,声明位置为 0x3000000,需要把值真正拷贝到 0x3000000 处 CPU 才可以访问到,因为 CPU 只会去 0x3000000 处找变量,所以要重定位把值拷贝到 0x3000000 吗,注意这里需要初始化 sdram
mov r1, #0x800
ldr r0, [r1]
mov r1, #0x3000000
str r0, [r1]
通用智能动态重定位示例
SECTIONS {.text 0 : {*(.text)}
    .rodata : {*(.rodata)}
    .data 0x3000000 : AT(0x800) {data_load_addr = LOADADDR(.data)
        = .;
        *(.data)
        data_end = .;
    }
    .bss : {*(.bss) *(.COMMON)}
}
  • LOADADDR(.data):data 段在 bin 文件的地址
  • data_start:CPU 访问的开始地址
  • data_end:CPU 访问的结束地址

重定位代码(低效率)

ldr r1, =data_load_addr
ldr r2, =data_start 
ldr r3, =data_end 
cpy:
  ldrb r4, [r1] #从 data_load_addr 拷贝 1 个字节 效率低
  strb r4, [r2]
  add r1, r1, #1
  add r2, r2, #1
  cmp r2, r3
  bne cpy #不等 继续 copy

重定位代码(高效率)

ldr r1, =data_load_addr
ldr r2, =data_start 
ldr r3, =data_end 
cpy:
  ldr r4, [r1] #从 data_load_addr 拷贝 1 个字节 效率低
  str r4, [r2]
  add r1, r1, #4
  add r2, r2, #4
  cmp r2, r3
  ble cpy #不等 继续 copy
退出移动版