zoukankan      html  css  js  c++  java
  • 位置无关码、位置有关码、链接地址、加载地址

    转自:http://blog.csdn.net/lizuobin2/article/details/52049892

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

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

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

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

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

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

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

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

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

    [cpp] view plain copy
    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  
    [cpp] view plain copy
    1. 000000a0 <a>:  
    2. 000000a0:   00000000    andeq   r0, r0, r0  

    [cpp] view plain copy
    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  

    [cpp] view plain copy
    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 strr3, [fp, #-8]

        位置无关。

  • 相关阅读:
    SpringRMI解析3-RmiServiceExporter逻辑细节
    SpringRMI解析2-RmiServiceExporter逻辑脉络
    SpringRMI解析1-使用示例
    SpringMVC解析5-DispatcherServlet逻辑细节
    SpringMVC解析4-DispatcherServlet逻辑脉络
    SpringMVC解析3-DispatcherServlet组件初始化
    SpringMVC解析2-ContextLoaderListener
    算法笔记_074:子集和问题(Java)
    算法笔记_073:哈密顿回路问题(Java)
    算法笔记_072:N皇后问题(Java)
  • 原文地址:https://www.cnblogs.com/alan666/p/8312065.html
Copyright © 2011-2022 走看看