uboot专题:位置无关码、位置有关码、链接地址、加载地址

在移植 uboot 时,接触到一个概念叫做 位置无关码,那么与它对应的就是位置有关码。提到这两个概念就还得提一提链接地址、加载地址。

链接地址,链接脚本里指定的,理论上程序运行时所处的地址。在编译时,编译器会根据链接地址来翻译位置有关码。

加载地址,程序运行时,实际所处的地址。

位置无关码,位置有关码,是相对于一条指令的正常目的来说的。比如 ldr r0 ,=标号,它的正常目的是取得标号处的地址,对于这个目的,它是位置有关码,运行的地址不对就获取不到正确的标号地址,其实它无论在哪都是获取的程序加载地址等于链接地址时,标号的地址,如果你就是想要这个值,那么用这条指令是非常正确的,就不用理会什么位置无关码,位置有关码的概念了,这一点非常重要。

因此,当加载地址不等于链接地址时,并不是不可以用位置无关码,而是要看你用位置无关码是否达到了你想要的目的。

位置无关码,依赖于程序当前运行的PC值,进行相对的跳转,导致的结果就是,无论代码在哪,总能达到指令的正常目的,因此是位置无关的。

位置有关码,不依赖当前PC值,是绝对跳转,只有程序运行在链接地址处时,才能达到指令的正常目的,因此是位置有关系的。

下面,我们来看常用的汇编指令以及C语言中哪些操作是位置有关码,哪些是位置无关码。

  1. SECTIONS {
  2. . = 0x33f80000;
  3. .text : { *(.text) }
  4. . = ALIGN( 4);
  5. .rodata : {*(.rodata*)}
  6. . = ALIGN( 4);
  7. .data : { *(.data) }
  8. . = ALIGN( 4);
  9. __bss_start = .;
  10. .bss : { *(.bss) *(COMMON) }
  11. __bss_end = .;
  12. }
  1. .text
  2. .global _start
  3. _start:
  4. bl close_watch_dog /* 相对跳转,位置无关 */
  5. bl _start
  6. adr r0, close_watch_dog /* 获取标号地址,位置无关 */
  7. ldr r0, SMRDATA /* 获取标号处的值,位置无关 */
  8. ldr r0, = 0x12345678
  9. ldr r0, =SMRDATA /* 获取标号地址,位置有关 */
  10. ldr r0, =main /* 获取函数名的地址,位置有关 */
  11. ldr r0 ,=__bss_start /* 获取链接脚本里标号的地址,位置有关 */
  12. close_watch_dog:
  13. mov r1, # 0
  14. str r1, [r0]
  15. mov pc, lr
  16. SMRDATA:
  17. .word 0x22111120
  1. int a;
  2. void abc(){
  3. a = 2;
  4. }
  5. int main(){
  6. int b;
  7. a = 1 ;
  8. b = 1 ;
  9. abc();
  10. return 0;
  11. }
如果加载地址为 0 ,那么代码将按照下面的顺序排放
  1. 00000000 <_start>:
  2. 00000000: eb000006 bl 33f80020 <close_watch_dog>
  3. 00000004: ebfffffd bl 33f80000 <_start>
  4. 00000008: e28f0010 add r0, pc, # 16
  5. 0000000c: e59f0018 ldr r0, [pc, # 24] ;
  6. 00000010: e59f0018 ldr r0, [pc, # 24] ;
  7. 00000014: e59f0018 ldr r0, [pc, # 24] ;
  8. 00000018: e59f0018 ldr r0, [pc, # 24] ;
  9. 0000001c: e59f0018 ldr r0, [pc, # 24] ;
  10. 00000020 <close_watch_dog>:
  11. 00000020: e3a01000 mov r1, # 0
  12. 00000024: e5801000 str r1, [r0]
  13. 00000028: e1a0f00e mov pc, lr
  14. 0000002c <SMRDATA>:
  15. 0000002c: 22111120 andscs r1, r1, # 8
  16. 00000030: 12345678 eorsne r5, r4, # 125829120 ; 0x7800000
  17. 00000034: 33f8002c mvnscc r0, # 44 ; 0x2c
  18. 00000038: 33f80064 mvnscc r0, # 100 ; 0x64
  19. 0000003c: 33f800a0 mvnscc r0, # 160 ; 0xa0
  20. 00000040 <abc>:
  21. 00000040: e52db004 push {fp} ; (str fp, [sp, # -4]!)
  22. 00000044: e28db000 add fp, sp, # 0
  23. 00000048: e59f3010 ldr r3, [pc, # 16] ; 33f80060 <abc+ 0x20>
  24. 0000004c: e3a02002 mov r2, # 2
  25. 00000050: e5832000 str r2, [r3]
  26. 00000054: e28bd000 add sp, fp, # 0
  27. 00000058: e8bd0800 pop {fp}
  28. 0000005c: e12fff1e bx lr
  29. 00000060: 33f800a0 mvnscc r0, # 160 ; 0xa0
  30. 00000064 <main>:
  31. 00000064: e92d4800 push {fp, lr}
  32. 00000068: e28db004 add fp, sp, # 4
  33. 0000006c: e24dd008 sub sp, sp, # 8
  34. 00000070: e59f3024 ldr r3, [pc, # 36] ; 33f8009c <main+ 0x38>
  35. 00000074: e3a02001 mov r2, # 1
  36. 00000078: e5832000 str r2, [r3]
  37. 0000007c: e3a03001 mov r3, # 1
  38. 00000080: e50b3008 str r3, [fp, # -8]
  39. 00000084: ebffffed bl 33f80040 <abc>
  40. 00000088: e3a03000 mov r3, # 0
  41. 0000008c: e1a00003 mov r0, r3
  42. 00000090: e24bd004 sub sp, fp, # 4
  43. 00000094: e8bd4800 pop {fp, lr}
  44. 00000098: e12fff1e bx lr
  45. 0000009c: 33f800a0 mvnscc r0, # 160 ; 0xa0
如果加载地址为0x33f80000 则按照下边的顺序排放
  1. 33f80000 <_start>:
  2. 33f80000: eb000006 bl 33f80020 <close_watch_dog>
  3. 33f80004: ebfffffd bl 33f80000 <_start>
  4. 33f80008: e28f0010 add r0, pc, # 16
  5. 33f8000c: e59f0018 ldr r0, [pc, # 24] ; 33f8002c <SMRDATA>
  6. 33f80010: e59f0018 ldr r0, [pc, # 24] ; 33f80030 <SMRDATA+ 0x4>
  7. 33f80014: e59f0018 ldr r0, [pc, # 24] ; 33f80034 <SMRDATA+ 0x8>
  8. 33f80018: e59f0018 ldr r0, [pc, # 24] ; 33f80038 <SMRDATA+ 0xc>
  9. 33f8001c: e59f0018 ldr r0, [pc, # 24] ; 33f8003c <SMRDATA+ 0x10>
  10. 33f80020 <close_watch_dog>:
  11. 33f80020: e3a01000 mov r1, # 0
  12. 33f80024: e5801000 str r1, [r0]
  13. 33f80028: e1a0f00e mov pc, lr
  14. 33f8002c <SMRDATA>:
  15. 33f8002c: 22111120 andscs r1, r1, # 8
  16. 33f80030: 12345678 eorsne r5, r4, # 125829120 ; 0x7800000
  17. 33f80034: 33f8002c mvnscc r0, # 44 ; 0x2c
  18. 33f80038: 33f80064 mvnscc r0, # 100 ; 0x64
  19. 33f8003c: 33f800a0 mvnscc r0, # 160 ; 0xa0
  20. 33f80040 <abc>:
  21. 33f80040: e52db004 push {fp} ; (str fp, [sp, # -4]!)
  22. 33f80044: e28db000 add fp, sp, # 0
  23. 33f80048: e59f3010 ldr r3, [pc, # 16] ; 33f80060 <abc+ 0x20>
  24. 33f8004c: e3a02002 mov r2, # 2
  25. 33f80050: e5832000 str r2, [r3]
  26. 33f80054: e28bd000 add sp, fp, # 0
  27. 33f80058: e8bd0800 pop {fp}
  28. 33f8005c: e12fff1e bx lr
  29. 33f80060: 33f800a0 mvnscc r0, # 160 ; 0xa0
  30. 33f80064 <main>:
  31. 33f80064: e92d4800 push {fp, lr}
  32. 33f80068: e28db004 add fp, sp, # 4
  33. 33f8006c: e24dd008 sub sp, sp, # 8
  34. 33f80070: e59f3024 ldr r3, [pc, # 36] ; 33f8009c <main+ 0x38>
  35. 33f80074: e3a02001 mov r2, # 1
  36. 33f80078: e5832000 str r2, [r3]
  37. 33f8007c: e3a03001 mov r3, # 1
  38. 33f80080: e50b3008 str r3, [fp, # -8]
  39. 33f80084: ebffffed bl 33f80040 <abc>
  40. 33f80088: e3a03000 mov r3, # 0
  41. 33f8008c: e1a00003 mov r0, r3
  42. 33f80090: e24bd004 sub sp, fp, # 4
  43. 33f80094: e8bd4800 pop {fp, lr}
  44. 33f80098: e12fff1e bx lr
  45. 33f8009c: 33f800a0 mvnscc r0, # 160 ; 0xa0
  46. Disassembly of section .bss:
  47. 33f800a0 <a>:
  48. 33f800a0: 00000000 andeq r0, r0, r0

一、B BL指令

bl close_watch_dog

33f80000:eb000006bl33f80020

b 是相对跳转:PC + 偏移值 (PC值等于当前地址+8)

偏移值:机器码 0xeb000006 低 24位 0x000006 按符号为扩展为 32 位 0x00000006 正数,向后跳转 0x6 个 4字节 也就是 0x1c

1、加载地址0:0 + 8 + 0x1c = 0x20 正确跳转

2、加载地址0x3ff80000:0x3ff80000 + 8 + 0x1c = 0x33f80020 正确跳转

bl _start

33f80004:ebfffffdbl33f80000 <_start>

偏移值:机器码 0xebfffffd 低 24位 fffffd 按符号位扩展为 32 位 0xfffffffd 负数(-3),向前跳转 0x3 个 4字节 也就是 0xc

1、加载地址0:4 + 8 - 0xc = 0 正确跳转

2、加载地址0x3ff80000:0x3ff80004 + 8 + 0xc = 0x33f80000 正确跳转

通过以上分析,我们知道B是相对跳转,位置无关码,也可以知道为什么32为arm指令集,B的范围为正负32M了,24位去掉1位符号位,恰好等于32M。


二、ADR

adr r0, close_watch_dog /* 获取标号处的地址,位置无关 */

33f80008: e28f0010addr0, pc, #16

1、加载地址0:0 + 8 + 16 = 0x20 正确

2、加载地址0x3ff80000:0x3ff80008 + 8 + 16 = 0x33f80020 正确

adr 获取的是标号处的“实际”地址,标号在哪就是哪个地址,跟位置无关,总能获得想要的值。


三、LDR

ldr r0, SMRDATA /* 获取标号处的值,位置无关 */

33f8000c:e59f0018ldrr0, [pc, #24]; 33f8002c <SMRDATA>

1、加载地址0:r0 = c + 8 + 24 = 0x2c 处的值 0x22111120 正确

2、加载地址0x3ff80000:r0 = 0x3ff8000c + 8 + 24 = 0x33f8002c处的值 0x22111120 正确

ldr r0, =0x12345678 /* 常数赋值,位置无关 */

33f80010: e59f0018ldrr0, [pc, #24]; 33f80030 <SMRDATA+0x4>

1、加载地址0:r0 = 0x10 + 8 + 24 = 0x30 处的值 0x12345678 正确

2、加载地址0x3ff80000:r0 = 0x3ff80010 + 8 + 24 = 0x33f80030处的值 0x12345678 正确


ldr r0, =SMRDATA /* 获取标号地址,位置有关 */

33f80014: e59f0018ldrr0, [pc, #24]; 33f80034 <SMRDATA+0x8>

1、加载地址0:r0 = 0x14 + 8 + 24 = 0x34 处的值 33f8002c 与标号实际地址(2c)不符合,不正确

2、加载地址0x3ff80000:r0 = 0x3ff80014 + 8 + 24 = 0x33f80034 处的值 33f8002c 正确


ldr r0, =main/* 获取函数名的地址,位置有关 */
ldr r0 ,=__bss_start /* 获取链接脚本里标号的地址,位置有关 */

这俩和 ldr r0, =SMRDATA 一致,位置有关,在0地址处运行不正确。


四、C函数

1、全局变量

  1. 00000040 <abc>:
  2. 00000040: e52db004 push {fp} ; (str fp, [sp, # -4]!)
  3. 00000044: e28db000 add fp, sp, # 0
  4. 00000048: e59f3010 ldr r3, [pc, # 16] ; 33f80060 <abc+ 0x20>
  5. 0000004c: e3a02002 mov r2, # 2
  6. 00000050: e5832000 str r2, [r3]
  7. 00000054: e28bd000 add sp, fp, # 0
  8. 00000058: e8bd0800 pop {fp}
  9. 0000005c: e12fff1e bx lr
  10. 00000060: 33f800a0 mvnscc r0, # 160 ; 0xa0
  1. 000000a0 <a>:
  2. 000000a0: 00000000 andeq r0, r0, r0

  1. 33f80040 <abc>:
  2. 33f80040: e52db004 push {fp} ; (str fp, [sp, # -4]!)
  3. 33f80044: e28db000 add fp, sp, # 0
  4. 33f80048: e59f3010 ldr r3, [pc, # 16] ; 33f80060 <abc+ 0x20>
  5. 33f8004c: e3a02002 mov r2, # 2
  6. 33f80050: e5832000 str r2, [r3]
  7. 33f80054: e28bd000 add sp, fp, # 0
  8. 33f80058: e8bd0800 pop {fp}
  9. 33f8005c: e12fff1e bx lr
  10. 33f80060: 33f800a0 mvnscc r0, # 160 ; 0xa0

  1. 33f800a0 <a>:
  2. 33f800a0: 00000000 andeq r0, r0, r0

r3 为全局变量 a 的地址,a 是存放在 0起始的地址还是0x33f80000起始的地址,它都认为 a 的地址是 0x33f800a0 。因此,C函数中调用全局变量是位置有关码。


2、函数调用

33f80084:ebffffedbl33f80040 <abc>

由于 main 函数和 abc 函数挨得比较近,在32M范围之内,因此被翻译成了一条 bl 指令,那么与位置无关。

如果,调用的函数比较远,大于32M的话,我认为是与位置有关系的,这个不再验证了。


3、局部变量

局部变量在函数刚开始的地方被压入栈,赋值语句被翻译成:

33f8007c: e3a03001movr3, #1
33f80080: e50b3008 str r3, [fp, #-8]

位置无关。

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值