boot 位置无关,加载地址运行地址理解

uboot理解

前言

一、位置有关和无关?

大体介绍

代码的运行分为三个阶段:取指,译码,执行,而取指令肯定要知道代码地址,执行过程又包含对变量的读写,对变量的读写也得知道变量的地址吧,至于代码的地址和变量的地址怎么确定呢?

答:由编译器确定,编译器确定地址分为两种形式,1、根据当前PC+偏移地址确定地址。2、直接用链接地址

对于第一种方式,地址是动态的,会根据PC的值变化而变化,对于第二种方式就是固定为我们指定的链接地址

问题:上面的两种方式和位置有关码与位置无关码有什么联系呢?

答:第一种方式对应的就是位置无关码,第二种方式对应的就是位置有关码

总结:

顾名思义,位置无关码肯定是和位置无关呀,这种代码放在什么位置都能正常运行,所以地址肯定是动态的不能固定的啊。而位置有关码肯定是和某个具体位置有关的代码啊,而这个具体位置就是我们的链接地址

解释

位置无关代码:即该段代码无论放在内存的哪个地址,都能正确运行。究其原因,是因为代码里没有使用绝对地址,都是相对地址。
位置相关码:即它的地址与代码处于的位置相关,是绝对地址,如:mov PC ,#0xff;ldr pc,=0xffff等。

ldr指令

当加标号时,LDR可以用于伪指令,也可以真指令。 真指令: (标号前不加=号,表示取标号处的值)
LDR R0, SDRDATA 实际被编译为LDR R0, [PC, #NN],其中NN是目标的相对距离 伪指令: (标号前加=号,取标号的地址)
LDR R0, = SDRDATA 实际编译的时候的时候,会在某位置存储SDRDATA的值,然后用一个LDR取出来。 显然,用LDR时,加不加=号有很大区别。 无=号:取该标号处的值,位置无关 有=号:取该标号的地址,位置相关

B指令

跳转指令,B指令接受一个相对地址,因此在汇编里用B跳转到一个标号时,实际编译的结果是一个相对跳转。相对地址有个范围限制,即目标不能太远,一般目标放在同一个文件里是肯定可以的。
Offset must IN 32Mbit
_start:

b ResetReset:

_reset:

二、位置有关无关具体代码分析

1.code

链接地址为0x33f80000

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

c语言main如下:

int a;
void abc(){
	a = 2;
}
int main(){
	int b;
	a =1 ;
	b =1 ;
	abc();
	return 0;

编译后的反汇编代码如下,0x33f80000代表链接地址
如果我们把代码加载到0x33f80000处运行,此时运行的地址和链接地址完全一样,也就是下面的反汇编图,

此时,链接地址 = 运行地址

33f80000 <_start>:
33f80000:	eb000006 	bl	33f80020 <close_watch_dog>
33f80004:	ebfffffd 	bl	33f80000 <_start>
33f80008:	e28f0010 	add	r0, pc, #16
33f8000c:	e59f0018 	ldr	r0, [pc, #24]	; 33f8002c <SMRDATA>
33f80010:	e59f0018 	ldr	r0, [pc, #24]	; 33f80030 <SMRDATA+0x4>
33f80014:	e59f0018 	ldr	r0, [pc, #24]	; 33f80034 <SMRDATA+0x8>
33f80018:	e59f0018 	ldr	r0, [pc, #24]	; 33f80038 <SMRDATA+0xc>
33f8001c:	e59f0018 	ldr	r0, [pc, #24]	; 33f8003c <SMRDATA+0x10>
 
33f80020 <close_watch_dog>:
33f80020:	e3a01000 	mov	r1, #0
33f80024:	e5801000 	str	r1, [r0]
33f80028:	e1a0f00e 	mov	pc, lr
 
33f8002c <SMRDATA>:
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 <abc>:
33f80040:	e52db004 	push	{fp}		; (str fp, [sp, #-4]!)
33f80044:	e28db000 	add	fp, sp, #0
33f80048:	e59f3010 	ldr	r3, [pc, #16]	; 33f80060 <abc+0x20>
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 <main>:
33f80064:	e92d4800 	push	{fp, lr}
33f80068:	e28db004 	add	fp, sp, #4
33f8006c:	e24dd008 	sub	sp, sp, #8
33f80070:	e59f3024 	ldr	r3, [pc, #36]	; 33f8009c <main+0x38>
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 <abc>
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 <a>:
33f800a0:	00000000 	andeq	r0, r0, r0

如果我们把代码加载到0x00000000处运行,此时实际运行的情况应该是下面这样的:此时 链接地址≠运行地址
注意:下面的不是反汇编的到的,反汇编得到的还是上面那样的,下面的只是我们根据实际运行情况描绘的
0x00000000代表的是实际运行地址

00000000 <_start>:
00000000:	eb000006 	bl	33f80020 <close_watch_dog>
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 <close_watch_dog>:
00000020:	e3a01000 	mov	r1, #0
00000024:	e5801000 	str	r1, [r0]
00000028:	e1a0f00e 	mov	pc, lr
 
0000002c <SMRDATA>:
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 <abc>:
00000040:	e52db004 	push	{fp}		; (str fp, [sp, #-4]!)
00000044:	e28db000 	add	fp, sp, #0
00000048:	e59f3010 	ldr	r3, [pc, #16]	; 33f80060 <abc+0x20>
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 <main>:
00000064:	e92d4800 	push	{fp, lr}
00000068:	e28db004 	add	fp, sp, #4
0000006c:	e24dd008 	sub	sp, sp, #8
00000070:	e59f3024 	ldr	r3, [pc, #36]	; 33f8009c <main+0x38>
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 <abc>
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

指令分析

B BL指令

bl close_watch_dog //位置无关码
33f80000 : eb000006 bl 33f80020

b 是相对跳转:跳转地址 = PC (PC=当前地址+8)+ 偏移值
偏移值:机器码 0xeb000006 低 24位 0x000006 按符号为扩展为 32 位 0x00000006 正数,向后跳转 0x6 * 4(32位位4字节)=(24)字节 也就是 0x18
1、起始运行地址为0x0000000时:    0x00000000(当前运行地址) + 8 + 0x18(偏移地址) = 0x00000020 跳转到正确位置
2、起始运行地址为0x3ff80000时:    0x3ff80000(当前运行地址)   + 8 + 0x18(偏移地址) = 0x33f80020  跳转到正确位置

ADR

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

33f80008: e28f0010 add r0, pc, #16
跳转地址 = PC (PC=当前地址+8)+ 偏移值
1、运行地址为0: 0 + 8 + 16 = 0x20 正确
2、运行地址为0x3ff80000: 0x3ff80008 + 8 + 16 = 0x33f80020 正确

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

LDR

ldr r0, SMRDATA       /* 获取标号处的值,位置无关 伪指令和下面的中间加上=号的不同*/

33f8000c:e59f0018  ldr   r0, [pc, #24]; 

1、起始运行地址地址0:         r0 = c + 8 + 24 = 0x2c                            处   22111120                      
2、起始运行地址0x3ff80000:   r0 = 0x3ff8000c + 8 + 24 = 0x33f8002c  处  22111120      和上面得到的一样,所以和位置无关                 
ldr r0, = SMRDATA           /* 获取标号地址,位置有关 这个中间加了= 不是伪指令,和上面的不同 */
33f80014: e59f0018ldrr0,  [pc, #24];  33f80034 <SMRDATA+0x8>

1、起始运行地址0: r0 = 0x14 + 8 + 24 = 0x34 处的值 33f8002c 与实际运行地址0x0000002c不同 ,所以不正确
假设我要用这个地址去取值,或者用这个地址去跳转函数,而此时实际的变量或者函数根本不在链接地址处,这样肯定就出错了

2、起始运行地址0x3ff80000: r0 = 0x3ff80014 + 8 + 24 = 0x33f80034 处的值 33f8002c和实际运行地址33f8002c相同 正确
ldr r0, =main/* 获取函数名的地址,位置有关 / ldr r0 ,=__bss_start / 获取链接脚本里标号的地址,位置有关 */
这俩和 ldr r0, =SMRDATA 一致,位置有关,在0地址处运行不正确。
一般C函数中调用全局变量是位置有关码。

链接地址和运行地址图解

运行地址链接地址

标题RISC-V code

main函数会把 flash 中 app 加载进去内存,然后开始执行
所以呢,编译 app 的时候需要把链接脚本 ilm 设置为25000
.stack ORIGIN(ram) + LENGTH(ram) - __stack_size :
{
PROVIDE( _heap_end = . );
. = __stack_size;
PROVIDE( _sp = . );
} >ram AT>ram

怎么理解呢
ORIGIN(ram) + LENGTH(ram) - __stack_size  这个是 vma 地址,在这里是这个section的起始地址
PROVIDE( _heap_end = . ); 导出符号
. = __stack_size; 原来定位符号在里面和在外面是不一样的。在section外面进行赋值,表示绝对地址,在里面进行
赋值代表的是基于section首地址的偏移,所以上面的意思就是 .定位到了
ORIGIN(ram) + LENGTH(ram) - __stack_size + __stack_size 的位置,就是
ORIGIN(ram) + LENGTH(ram)

参考链接:
初始化SDRAM需要保证code 位置无关
位置有关无关解释
详细code分析
大神制作
裸机开发lds详细解释
lds详解
"."定位符号详解
asm

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值