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

    在移植 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 = .;
}
.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;
}
如果加载地址为 0 ,那么代码将按照下面的顺序排放
00000000 <_start>:
00000000:	eb000006 	bl	33f80020 
00000004:	ebfffffd 	bl	33f80000 <_start>
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 <_start>:
33f80000:	eb000006 	bl	33f80020 
33f80004:	ebfffffd 	bl	33f80000 <_start>
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_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

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

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

    

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

    33f80010: e59f0018ldrr0, [pc, #24];  33f80030

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

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


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

    33f80014: e59f0018ldrr0,  [pc, #24];  33f80034

    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、全局变量

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

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

    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]

    位置无关。



你可能感兴趣的:(Bootloader,bootloader学习之路)