位置无关码、位置有关码、链接地址、加载地址

在移植 uboot 时,接触到一个概念叫做 位置无关码,那么与它对应的就是位置有关码。提到这两个概念就还得提一提链接地址、加载地址
链接地址,链接脚本里指定的,理论上程序运行时所处的地址。在编译时,编译器会根据链接地址来翻译位置有关码。
加载地址,程序运行时,实际所处的地址。
位置无关码,位置有关码,是相对于一条指令的正常目的来说的。比如 ldr r0 ,=标号,它的正常目的是取得标号处的地址,对于这个目的,它是位置有关码,运行的地址不对就获取不到正确的标号地址,其实它无论在哪都是获取的程序加载地址等于链接地址时,标号的地址,如果你就是想要这个值,那么用这条指令是非常正确的,就不用理会什么位置无关码,位置有关码的概念了,这一点非常重要。
因此,当加载地址不等于链接地址时,并不是不可以用位置无关码,而是要看你用位置无关码是否达到了你想要的目的。
位置无关码,依赖于程序当前运行的pc值,进行相对的跳转,导致的结果就是,无论代码在哪,总能达到指令的正常目的,因此是位置无关的。
位置有关码,不依赖当前pc值,是绝对跳转,只有程序运行在链接地址处时,才能达到指令的正常目的,因此是位置有关系的。
下面,我们来看常用的汇编指令以及c语言中哪些操作是位置有关码,哪些是位置无关码。
sections { . = 0x33f80000; .text : { *(.text) } . = align(4); .rodata : {*(.rodata*)} . = align(4); .data : { *(.data) } . = align(4); __bss_start = .; .bss : { *(.bss) *(common) } __bss_end = .; }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
sections {  
    . = 0x33f80000;  
    .text : { *(.text) }
. = align(4);  
    .rodata : {*(.rodata*)}
. = align(4);  
    .data : { *(.data) }
. = align(4);  
    __bss_start = .;  
    .bss : { *(.bss)  *(common) }  
    __bss_end = .;  
}
.text .global _start _start: bl close_watch_dog /* 相对跳转,位置无关 */ bl _start adr r0, close_watch_dog /* 获取标号地址,位置无关 */ ldr r0, smrdata /* 获取标号处的值,位置无关 */ ldr r0, =0x12345678 ldr r0, =smrdata /* 获取标号地址,位置有关 */ ldr r0, =main /* 获取函数名的地址,位置有关 */ ldr r0 ,=__bss_start /* 获取链接脚本里标号的地址,位置有关 */ close_watch_dog: mov r1, #0 str r1, [r0] mov pc, lr smrdata: .word 0x22111120
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
.text  
.global _start  
_start:
bl close_watch_dog      /* 相对跳转,位置无关 */  
    bl _start  
    adr r0, close_watch_dog /* 获取标号地址,位置无关 */
ldr r0, smrdata         /* 获取标号处的值,位置无关 */
ldr r0, =0x12345678  
    ldr r0, =smrdata        /* 获取标号地址,位置有关 */  
    ldr r0, =main           /* 获取函数名的地址,位置有关 */  
    ldr r0 ,=__bss_start    /* 获取链接脚本里标号的地址,位置有关 */
close_watch_dog:  
    mov r1, #0  
    str r1, [r0]  
    mov pc, lr
smrdata:  
    .word  0x22111120
int a; void abc(){ a = 2; } int main(){ int b; a =1 ; b =1 ; abc(); return 0; }
1
2
3
4
5
6
7
8
9
10
11
int a;  
void abc(){  
    a = 2;  
}  
int main(){  
    int b;  
    a =1 ;  
    b =1 ;  
    abc();  
    return 0;  
}
如果加载地址为 0 ,那么代码将按照下面的顺序排放
00000000 : 00000000: eb000006 bl 33f80020 00000004: ebfffffd bl 33f80000 00000008: e28f0010 add r0, pc, #16 0000000c: e59f0018 ldr r0, [pc, #24] ; 00000010: e59f0018 ldr r0, [pc, #24] ; 00000014: e59f0018 ldr r0, [pc, #24] ; 00000018: e59f0018 ldr r0, [pc, #24] ; 0000001c: e59f0018 ldr r0, [pc, #24] ; 00000020 : 00000020: e3a01000 mov r1, #0 00000024: e5801000 str r1, [r0] 00000028: e1a0f00e mov pc, lr 0000002c : 0000002c: 22111120 andscs r1, r1, #8 00000030: 12345678 eorsne r5, r4, #125829120 ; 0x7800000 00000034: 33f8002c mvnscc r0, #44 ; 0x2c 00000038: 33f80064 mvnscc r0, #100 ; 0x64 0000003c: 33f800a0 mvnscc r0, #160 ; 0xa0 00000040 : 00000040: e52db004 push {fp} ; (str fp, [sp, #-4]!) 00000044: e28db000 add fp, sp, #0 00000048: e59f3010 ldr r3, [pc, #16] ; 33f80060 0000004c: e3a02002 mov r2, #2 00000050: e5832000 str r2, [r3] 00000054: e28bd000 add sp, fp, #0 00000058: e8bd0800 pop {fp} 0000005c: e12fff1e bx lr 00000060: 33f800a0 mvnscc r0, #160 ; 0xa0 00000064 : 00000064: e92d4800 push {fp, lr} 00000068: e28db004 add fp, sp, #4 0000006c: e24dd008 sub sp, sp, #8 00000070: e59f3024 ldr r3, [pc, #36] ; 33f8009c 00000074: e3a02001 mov r2, #1 00000078: e5832000 str r2, [r3] 0000007c: e3a03001 mov r3, #1 00000080: e50b3008 str r3, [fp, #-8] 00000084: ebffffed bl 33f80040 00000088: e3a03000 mov r3, #0 0000008c: e1a00003 mov r0, r3 00000090: e24bd004 sub sp, fp, #4 00000094: e8bd4800 pop {fp, lr} 00000098: e12fff1e bx lr 0000009c: 33f800a0 mvnscc r0, #160 ; 0xa0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
00000000 :  
00000000:   eb000006    bl  33f80020   
00000004:   ebfffffd    bl  33f80000   
00000008:   e28f0010    add r0, pc, #16  
0000000c:   e59f0018    ldr r0, [pc, #24]   ;  
00000010:   e59f0018    ldr r0, [pc, #24]   ;  
00000014:   e59f0018    ldr r0, [pc, #24]   ;  
00000018:   e59f0018    ldr r0, [pc, #24]   ;  
0000001c:   e59f0018    ldr r0, [pc, #24]   ;
00000020 :  
00000020:   e3a01000    mov r1, #0  
00000024:   e5801000    str r1, [r0]  
00000028:   e1a0f00e    mov pc, lr
0000002c :  
0000002c:   22111120    andscs  r1, r1, #8  
00000030:   12345678    eorsne  r5, r4, #125829120  ; 0x7800000  
00000034:   33f8002c    mvnscc  r0, #44 ; 0x2c  
00000038:   33f80064    mvnscc  r0, #100    ; 0x64  
0000003c:   33f800a0    mvnscc  r0, #160    ; 0xa0
00000040 :  
00000040:   e52db004    push    {fp}        ; (str fp, [sp, #-4]!)  
00000044:   e28db000    add fp, sp, #0  
00000048:   e59f3010    ldr r3, [pc, #16]   ; 33f80060   
0000004c:   e3a02002    mov r2, #2  
00000050:   e5832000    str r2, [r3]  
00000054:   e28bd000    add sp, fp, #0  
00000058:   e8bd0800    pop {fp}  
0000005c:   e12fff1e    bx  lr  
00000060:   33f800a0    mvnscc  r0, #160    ; 0xa0
00000064 :  
00000064:   e92d4800    push    {fp, lr}  
00000068:   e28db004    add fp, sp, #4  
0000006c:   e24dd008    sub sp, sp, #8  
00000070:   e59f3024    ldr r3, [pc, #36]   ; 33f8009c   
00000074:   e3a02001    mov r2, #1  
00000078:   e5832000    str r2, [r3]  
0000007c:   e3a03001    mov r3, #1  
00000080:   e50b3008    str r3, [fp, #-8]  
00000084:   ebffffed    bl  33f80040   
00000088:   e3a03000    mov r3, #0  
0000008c:   e1a00003    mov r0, r3  
00000090:   e24bd004    sub sp, fp, #4  
00000094:   e8bd4800    pop {fp, lr}  
00000098:   e12fff1e    bx  lr  
0000009c:   33f800a0    mvnscc  r0, #160    ; 0xa0
如果加载地址为0x33f80000 则按照下边的顺序排放
33f80000 : 33f80000: eb000006 bl 33f80020 33f80004: ebfffffd bl 33f80000 33f80008: e28f0010 add r0, pc, #16 33f8000c: e59f0018 ldr r0, [pc, #24] ; 33f8002c 33f80010: e59f0018 ldr r0, [pc, #24] ; 33f80030 33f80014: e59f0018 ldr r0, [pc, #24] ; 33f80034 33f80018: e59f0018 ldr r0, [pc, #24] ; 33f80038 33f8001c: e59f0018 ldr r0, [pc, #24] ; 33f8003c 33f80020 : 33f80020: e3a01000 mov r1, #0 33f80024: e5801000 str r1, [r0] 33f80028: e1a0f00e mov pc, lr 33f8002c : 33f8002c: 22111120 andscs r1, r1, #8 33f80030: 12345678 eorsne r5, r4, #125829120 ; 0x7800000 33f80034: 33f8002c mvnscc r0, #44 ; 0x2c 33f80038: 33f80064 mvnscc r0, #100 ; 0x64 33f8003c: 33f800a0 mvnscc r0, #160 ; 0xa0 33f80040 : 33f80040: e52db004 push {fp} ; (str fp, [sp, #-4]!) 33f80044: e28db000 add fp, sp, #0 33f80048: e59f3010 ldr r3, [pc, #16] ; 33f80060 33f8004c: e3a02002 mov r2, #2 33f80050: e5832000 str r2, [r3] 33f80054: e28bd000 add sp, fp, #0 33f80058: e8bd0800 pop {fp} 33f8005c: e12fff1e bx lr 33f80060: 33f800a0 mvnscc r0, #160 ; 0xa0 33f80064 : 33f80064: e92d4800 push {fp, lr} 33f80068: e28db004 add fp, sp, #4 33f8006c: e24dd008 sub sp, sp, #8 33f80070: e59f3024 ldr r3, [pc, #36] ; 33f8009c 33f80074: e3a02001 mov r2, #1 33f80078: e5832000 str r2, [r3] 33f8007c: e3a03001 mov r3, #1 33f80080: e50b3008 str r3, [fp, #-8] 33f80084: ebffffed bl 33f80040 33f80088: e3a03000 mov r3, #0 33f8008c: e1a00003 mov r0, r3 33f80090: e24bd004 sub sp, fp, #4 33f80094: e8bd4800 pop {fp, lr} 33f80098: e12fff1e bx lr 33f8009c: 33f800a0 mvnscc r0, #160 ; 0xa0 disassembly of section .bss: 33f800a0 : 33f800a0: 00000000 andeq r0, r0, r0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
33f80000 :  
33f80000:   eb000006    bl  33f80020   
33f80004:   ebfffffd    bl  33f80000   
33f80008:   e28f0010    add r0, pc, #16  
33f8000c:   e59f0018    ldr r0, [pc, #24]   ; 33f8002c   
33f80010:   e59f0018    ldr r0, [pc, #24]   ; 33f80030   
33f80014:   e59f0018    ldr r0, [pc, #24]   ; 33f80034   
33f80018:   e59f0018    ldr r0, [pc, #24]   ; 33f80038   
33f8001c:   e59f0018    ldr r0, [pc, #24]   ; 33f8003c
33f80020 :  
33f80020:   e3a01000    mov r1, #0  
33f80024:   e5801000    str r1, [r0]  
33f80028:   e1a0f00e    mov pc, lr
33f8002c :  
33f8002c:   22111120    andscs  r1, r1, #8  
33f80030:   12345678    eorsne  r5, r4, #125829120  ; 0x7800000  
33f80034:   33f8002c    mvnscc  r0, #44 ; 0x2c  
33f80038:   33f80064    mvnscc  r0, #100    ; 0x64  
33f8003c:   33f800a0    mvnscc  r0, #160    ; 0xa0
33f80040 :  
33f80040:   e52db004    push    {fp}        ; (str fp, [sp, #-4]!)  
33f80044:   e28db000    add fp, sp, #0  
33f80048:   e59f3010    ldr r3, [pc, #16]   ; 33f80060   
33f8004c:   e3a02002    mov r2, #2  
33f80050:   e5832000    str r2, [r3]  
33f80054:   e28bd000    add sp, fp, #0  
33f80058:   e8bd0800    pop {fp}  
33f8005c:   e12fff1e    bx  lr  
33f80060:   33f800a0    mvnscc  r0, #160    ; 0xa0
33f80064 :  
33f80064:   e92d4800    push    {fp, lr}  
33f80068:   e28db004    add fp, sp, #4  
33f8006c:   e24dd008    sub sp, sp, #8  
33f80070:   e59f3024    ldr r3, [pc, #36]   ; 33f8009c   
33f80074:   e3a02001    mov r2, #1  
33f80078:   e5832000    str r2, [r3]  
33f8007c:   e3a03001    mov r3, #1  
33f80080:   e50b3008    str r3, [fp, #-8]  
33f80084:   ebffffed    bl  33f80040   
33f80088:   e3a03000    mov r3, #0  
33f8008c:   e1a00003    mov r0, r3  
33f80090:   e24bd004    sub sp, fp, #4  
33f80094:   e8bd4800    pop {fp, lr}  
33f80098:   e12fff1e    bx  lr  
33f8009c:   33f800a0    mvnscc  r0, #160    ; 0xa0
disassembly of section .bss:
33f800a0 :  
33f800a0:   00000000    andeq   r0, r0, r0
一、b bl指令bl close_watch_dog33f80000:eb000006bl33f80020
b 是相对跳转:pc + 偏移值 (pc值等于当前地址+8)
偏移值:机器码 0xeb000006 低 24位 0x000006 按符号为扩展为 32 位 0x00000006 正数,向后跳转 0x6 个 4字节 也就是 0x1c
加载地址0:0 + 8 + 0x1c = 0x20 正确跳转加载地址0x3ff80000:0x3ff80000 + 8 + 0x1c = 0x33f80020 正确跳转bl _start33f80004:ebfffffdbl33f80000
偏移值:机器码 0xebfffffd 低 24位 fffffd 按符号位扩展为 32 位 0xfffffffd 负数(-3),向前跳转 0x3 个 4字节 也就是 0xc
加载地址0:4 + 8 – 0xc = 0 正确跳转加载地址0x3ff80000:0x3ff80004 + 8 + 0xc = 0x33f80000 正确跳转通过以上分析,我们知道b是相对跳转,位置无关码,也可以知道为什么32为arm指令集,b的范围为正负32m了,24位去掉1位符号位,恰好等于32m。
二、adradr r0, close_watch_dog /* 获取标号处的地址,位置无关 */33f80008:e28f0010addr0, pc, #16
加载地址0:0 + 8 + 16 = 0x20 正确加载地址0x3ff80000:0x3ff80008 + 8 + 16 = 0x33f80020 正确adr 获取的是标号处的“实际”地址,标号在哪就是哪个地址,跟位置无关,总能获得想要的值。
三、ldrldr r0, smrdata /* 获取标号处的值,位置无关 */33f8000c:e59f0018ldrr0, [pc, #24]; 33f8002c
加载地址0:r0 = c + 8 + 24 = 0x2c 处的值0x22111120 正确加载地址0x3ff80000:r0 = 0x3ff8000c + 8 + 24 = 0x33f8002c处的值0x22111120 正确ldr r0, =0x12345678 /* 常数赋值,位置无关*/33f80010:e59f0018ldrr0, [pc, #24]; 33f80030
加载地址0:r0 = 0x10 + 8 + 24 = 0x30 处的值0x12345678 正确加载地址0x3ff80000:r0 = 0x3ff80010 + 8 + 24 = 0x33f80030处的值0x12345678 正确ldr r0, =smrdata /* 获取标号地址,位置有关 */33f80014:e59f0018ldrr0, [pc, #24]; 33f80034
加载地址0:r0 = 0x14 + 8 + 24 = 0x34 处的值33f8002c与标号实际地址(2c)不符合,不正确加载地址0x3ff80000:r0 = 0x3ff80014 + 8 + 24 = 0x33f80034 处的值33f8002c 正确ldr r0, =main/* 获取函数名的地址,位置有关 */ldr r0 ,=__bss_start /* 获取链接脚本里标号的地址,位置有关 */这俩和ldr r0, =smrdata 一致,位置有关,在0地址处运行不正确。
四、c函数1、全局变量
00000040 : 00000040: e52db004 push {fp} ; (str fp, [sp, #-4]!) 00000044: e28db000 add fp, sp, #0 00000048: e59f3010 ldr r3, [pc, #16] ; 33f80060 0000004c: e3a02002 mov r2, #2 00000050: e5832000 str r2, [r3] 00000054: e28bd000 add sp, fp, #0 00000058: e8bd0800 pop {fp} 0000005c: e12fff1e bx lr 00000060: 33f800a0 mvnscc r0, #160 ; 0xa0
1
2
3
4
5
6
7
8
9
10
00000040 :  
00000040:   e52db004    push    {fp}        ; (str fp, [sp, #-4]!)  
00000044:   e28db000    add fp, sp, #0  
00000048:   e59f3010    ldr r3, [pc, #16]   ; 33f80060   
0000004c:   e3a02002    mov r2, #2  
00000050:   e5832000    str r2, [r3]  
00000054:   e28bd000    add sp, fp, #0  
00000058:   e8bd0800    pop {fp}  
0000005c:   e12fff1e    bx  lr  
00000060:   33f800a0    mvnscc  r0, #160    ; 0xa0
000000a0 : 000000a0: 00000000 andeq r0, r0, r0
1
2
000000a0 :  
000000a0:   00000000    andeq   r0, r0, r0
33f80040 : 33f80040: e52db004 push {fp} ; (str fp, [sp, #-4]!) 33f80044: e28db000 add fp, sp, #0 33f80048: e59f3010 ldr r3, [pc, #16] ; 33f80060 33f8004c: e3a02002 mov r2, #2 33f80050: e5832000 str r2, [r3] 33f80054: e28bd000 add sp, fp, #0 33f80058: e8bd0800 pop {fp} 33f8005c: e12fff1e bx lr 33f80060: 33f800a0 mvnscc r0, #160 ; 0xa0
1
2
3
4
5
6
7
8
9
10
33f80040 :  
33f80040:   e52db004    push    {fp}        ; (str fp, [sp, #-4]!)  
33f80044:   e28db000    add fp, sp, #0  
33f80048:   e59f3010    ldr r3, [pc, #16]   ; 33f80060   
33f8004c:   e3a02002    mov r2, #2  
33f80050:   e5832000    str r2, [r3]  
33f80054:   e28bd000    add sp, fp, #0  
33f80058:   e8bd0800    pop {fp}  
33f8005c:   e12fff1e    bx  lr  
33f80060:   33f800a0    mvnscc  r0, #160    ; 0xa0
33f800a0 : 33f800a0: 00000000 andeq r0, r0, r0
1
2
33f800a0 :  
33f800a0:   00000000    andeq   r0, r0, r0
r3 为全局变量 a 的地址,a 是存放在 0起始的地址还是0x33f80000起始的地址,它都认为 a 的地址是 0x33f800a0 。因此,c函数中调用全局变量是位置有关码。
2、函数调用33f80084:ebffffedbl33f80040
由于 main 函数和 abc 函数挨得比较近,在32m范围之内,因此被翻译成了一条 bl 指令,那么与位置无关。
如果,调用的函数比较远,大于32m的话,我认为是与位置有关系的,这个不再验证了。
3、局部变量局部变量在函数刚开始的地方被压入栈,赋值语句被翻译成:
33f8007c:e3a03001movr3, #1
33f80080: e50b3008 str r3, [fp, #-8]
位置无关。

适用于便携式可穿戴设备的高性价比超小封装ESD介绍
深圳数字资产交易系统种类,源中瑞胡大帅
SINOV-810P 8口FXO/FXS模拟卡特点及应用
74hc04中文资料详细介绍(引脚功能_内部结构_逻辑图_参数及应用电路)
NVIDIA紧急放出GeForce 445.78版修复驱动 解决开启图像锐化之后导致DX11游戏无法正常启动问题
位置无关码、位置有关码、链接地址、加载地址
日本白电市场反弹 引中国家电企业深思
OLED与LED的发光原理有什么不一样的
Arduino激光投影仪和控制应用程序的制作
全彩堆叠结构Micro LED,靠什么“打天下”?
智能语音助手的时代 我们还有这样的一股清流
回顾联发科的深夜微博 中兴通讯的向好是必然的
无人机助力抗击疫情 未来3-5年内具有较大投资价值
喜报!祝贺我司成功中标脉冲步进调制(PSM)整流叠加电源模块系统项目!
首个“量子计算产业知识产权联盟”成立,对中国量子产业意味着什么?
集成的半导体光电智能探测器SOC研究
Protel 99 se之PLD设计
德赛电池:订单考验下的高质量交付保障
火花机加工原理及作用
光纤最大损耗值是多少?