PIC(与位置无关代码)在u-boot上的实现
来源:互联网 发布:淘宝1元秒杀攻略 编辑:程序博客网 时间:2024/05/16 12:07
1.1 原理介绍
u-boot通常都是存在ROM或者Flash上,以保证CPU启动后可以直接运行u-boot。但ROM的问题是只能读不能写,不利于程序的执行。如:全局变量读写,地址空间限制等问题。因此u-boot会先把自己拷贝到RAM中去执行。这一拷贝带来的问题是执行地址的混乱。代码的执行地址通常都是在编译时有链接地址指定的,如何保证拷贝前后都可以执行呢?
一个办法是使用拷贝到RAM后的地址作为编译时的链接地址,拷贝前所有函数与全局变量的调用都增加偏移量。(如VxWorks的bootloader)尽量减少拷贝前需要执行的代码量。
两一个地址是把image编译成与地址无关的程需,也就是PIC - Position independent code。编译器无法保证代码的独立性,它需要与加载器配合起来。U-boot自己加载自己,所以她自己就是加载器。
域代码无关代码依赖于下面两种技术:
1) 使用相对地址
2) 加载器可以自动更新涉及到绝对地址的指令
PIC的实现方式不止一种,不对CPU架构下的实现也有区别。这里主要结合搜啊ARM架构下u-boot中PIC的实现方式
1.2 一个简单例子
先通过一个简单例子介绍编译成PIC与非PIC的区别
1.2.1 源码
test0.1c
int foo(int a);
int ttt = 1;
int xxx = 5;
int boo()
{
foo(xxx);
return foo(ttt);
}
这是一个简单的程序。Foo函数的实现在另一个文件,boo函数调用了foo函数与全局变量ttt与xxx。
1.2.2 Compile
对于PowerPC架构,u-boot只是在编译时使用了-fpic。这种方式会生成一个.got段来存储绝对地址符号(位置无关(PIC)代码原理剖析)。对与ARM架构,则是在编译时使用-mword-relocations,生成与位置无关代码;链接时使用-pie生成.rel.dyn段,该段中的每个条目被称为一个LABEL,用来存储绝对地址符号的地址。
1.2.3 PIC如何找到所有的绝对地址符号
Non-PIC
PIC
arm-qhao-linux-gnueabi-gcc -c test01.c
arm-qhao-linux-gnueabi-ld -o a.out test01.o test02.o
arm-qhao-linux-gnueabi-gcc -c -mword-relocations test01.c
arm-qhao-linux-gnueabi-ld -pie -o a.out test01.o test02.o
SYMBOL TABLE:
00008094 l d .text 00000000 .text
000100f8 l d .data 00000000 .data
000100fc g O .data 00000004 xxx
000100f8 g O .data 00000004 ttt
SYMBOL TABLE:
00000220 l d .rel.dyn 00000000 .rel.dyn
00000230 l d .text 00000000 .text
00008320 l d .data 00000000 .data
00008324 g O .data 00000004 xxx
00008320 g O .data 00000004 ttt
Disassembly of section .text:
00008094 :
8094: e92d4800 push {fp, lr}
8098: e28db004 add fp, sp, #4
809c: e30030fc movw r3, #252 ; 0xfc
80a0: e3403001 movt r3, #1
80a4: e5933000 ldr r3, [r3]
80a8: e1a00003 mov r0, r3
80ac: eb000007 bl 80d0
80b0: e30030f8 movw r3, #248 ; 0xf8
80b4: e3403001 movt r3, #1
80b8: e5933000 ldr r3, [r3]
80bc: e1a00003 mov r0, r3
80c0: eb000002 bl 80d0
80c4: e1a03000 mov r3, r0
80c8: e1a00003 mov r0, r3
80cc: e8bd8800 pop {fp, pc}
Disassembly of section .text:
00000230 :
230: e92d4800 push {fp, lr}
234: e28db004 add fp, sp, #4
238: e59f3024 ldr r3, [pc, #36] ; 264
23c: e5933000 ldr r3, [r3]
240: e1a00003 mov r0, r3
244: eb000008 bl 26c
248: e59f3018 ldr r3, [pc, #24] ; 268
24c: e5933000 ldr r3, [r3]
250: e1a00003 mov r0, r3
254: eb000004 bl 26c
258: e1a03000 mov r3, r0
25c: e1a00003 mov r0, r3
260: e8bd8800 pop {fp, pc}
264: 00008324 andeq r8, r0, r4, lsr #6 ; this is called as Lable
268: 00008320 andeq r8, r0, r0, lsr #6
Disassembly of section .data:
000100f8 :
100f8: 00000001 andeq r0, r0, r1
000100fc :
100fc: 00000005 andeq r0, r0, r5
Disassembly of section .data:
00008320 :
8320: 00000001 andeq r0, r0, r1
00008324 :
8324: 00000005 andeq r0, r0, r5
Disassembly of section .rel.dyn:
00000220 <.rel.dyn>:
220: 00000264 andeq r0, r0, r4, ror #4
224: 00000017 andeq r0, r0, r7, lsl r0
228: 00000268 andeq r0, r0, r8, ror #4
22c: 00000017 andeq r0, r0, r7, lsl r0
以符号ttt为例,在non-PIC中,#809c与#80a通过绝对地址获得xxx的地址,如果要搬移到新地址,则必须找到并修改调用绝对地址的指令。PIC中,每个函数在最后都附加了绝对地址表,#238通过PC寄存器加偏移的方式找到地址表,根据地址表中存储的绝对地址找到xxx实际的地址。如果要搬移到新地址,需要修改每一个函数后面的绝对地址表。1.2.4 加载器如何发现绝对地址符号
下图展示了加载器如何对代码进行重定向:
从前一节的表格中可以发现,PIC代码多了一个 。.rel.dyn段,该段有-pic参数产生,被称为LABLE表格,表格中每一项对应着一个函数后绝对地址表中的LABEL。程序被拷贝到新地址后,加载器通过.rel.dyn段找到所有的LABEL,利用新的启示地址来更新所有的LABEL。
1.3 U-boot中加载器的实现
u-boot加载自身的过程有被称为重定向(relocate)
下表中左侧是u-boot中的源码,右侧是用C语言写的伪代码。
Assembly
Pseudo C code
ENTRY(_main)
……
adr lr, here
ldr r0, [r9, #GD_RELOC_OFF] /* r0 = gd->reloc_off */
add lr, lr, r0
ldr r0, [r9, #GD_RELOCADDR] /* r0 = gd->relocaddr */
b relocate
here:
void _main(void)
{
lr = here;
r0 = gd->relocaddr
relocate(here, gd->relocaddr)
}
ENTRY(relocate_code)
ldr r1, =__image_copy_start /* r1 <- SRC &__image_copy_start */
subs r4, r0, r1 /* r4 <- relocation offset */
beq relocate_done /* skip relocation */
ldr r2, =__image_copy_end /* r2 <- SRC &__image_copy_end */
copy_loop:
ldmia r1!, {r10-r11} /* copy from source address [r1] */
stmia r0!, {r10-r11} /* copy to target address [r0] */
cmp r1, r2 /* until source end address [r2] */
blo copy_loop
/*
* fix .rel.dyn relocations
*/
ldr r2, =__rel_dyn_start /* r2 <- __rel_dyn_start */
ldr r3, =__rel_dyn_end /* r3 <- __rel_dyn_end */
fixloop:
ldmia r2!, {r0-r1} /* (r0,r1) <- (location,fixup) */
and r1, r1, #0xff
cmp r1, #23 /* relative fixup? */
bne fixnext
/* relative fix: increase location by offset */
add r0, r0, r4
ldr r1, [r0]
add r1, r1, r4
str r1, [r0]
fixnext:
cmp r2, r3
blo fixloop
relocate_done:
bx lr
ENDPROC(relocate_code)
#define R_ARM_RELATIVE 0x17
typedef struct tagRelocItem
{
unsigned long address;
unsigned long reloc_code;
} RelocItem;
void relocate(int lr, int relocaddr)
{
RelocItem *relocItem;
unsigned long offset;
unsigned long addr; /* new address of Label */
/* copy image */
memcpy(__image_copy_start, __image_copy_end,
(__image_copy_end - __image_copy_start));
/* fix .rel.dyn relocations */
relocItem = __rel_dyn_start;
offset = relocaddr - __image_copy_start;
while (relocItem >= __rel_dyn_start) {
if (relocItem->reloc_code == R_ARM_RELATIVE) {
addr = relocItem ->address + offset;
*(unsigned long *)addr += offset;
}
relocItem++;
}
goto lr;
}
/* clear .bss segment */
ldr r0, =__bss_start /* this is auto-relocated! */
ldr r1, =__bss_end /* this is auto-relocated! */
mov r2, #0x00000000 /* prepare zero to clear BSS */
clbss_l: cmp r0, r1 /* while not at end of BSS */
strlo r2, [r0] /* clear 32-bit BSS word */
addlo r0, r0, #4 /* move to next */
blo clbss_l
memset(__bss_start, 0, __bss_end);
/* attention: that will cover .rel.dyn section */
/* call board_init_r(gd_t *id, ulong dest_addr) */
mov r0, r9 /* gd_t */
ldr r1, [r9, #GD_RELOCADDR] /* dest_addr */
/* call board_init_r */
ldr pc, =board_init_r /* this is auto-relocated! */
/* we should not return here. */
board_init_r(gd, gd->dest_addr);
上述实现的最后一步是.bss段清零。看下面的u-boot段列表:
6 .rel.dyn 000040f8 0003155c 0003155c 0002955c 2**2
8 .bss 000358a0 0003155c 0003155c 00000000 2**6
.rel.dyn与.bss段起始地址是相同的(通过链接脚本u-boot.lds实现)。这是因为.rel.dyn段只用于把u-boot自己加载到内存,之后就没有了。在.bss段清零时,实际上也就把.rel.dyn段去掉了。
这带来的一个问题是,u-boot在ROM运行时,.bss段是不为零的。.bss段存着未初始化的全局变量,因此此时使用变量时也不能假设变量初值为0。所幸大部分编程规范都要求全局变量初始化要显示初始化。
下面是加载示意图。可以看到加载前只使用.rel.dyn段,加载后只使用.bss段。
本文乃fireaxe原创,使用GPL发布,可以自由拷贝,转载。但转载请保持文档的完整性,并注明原作者及原链接。内容可任意使用,但对因使用该内容引起的后果不做任何保证。
作者:fireaxe.hq@outlook.com博客:fireaxe.blog.chinaunix.net
阅读全文 0 0
- PIC(与位置无关代码)在u-boot上的实现
- 位置无关代码(PIC)的思考
- 位置无关代码(PIC)的思考
- 位置无关代码(PIC)的思考
- 位置无关代码(PIC)的思考
- 位置无关代码(PIC)的思考
- 位置无关代码(PIC)的思考
- 位置无关代码(PIC)的思考
- 位置无关代码PIC
- ARM 位置无关代码(PIC)的分析理解
- 位置无关代码(PIC)的分析…
- 基于位置无关代码PIC的程序设计
- bootloader与linux中位置无关代码(PIC)的分析理解
- bootloader与linux中位置无关代码(PIC)的分析理解
- bootloader与linux中位置无关代码(PIC)的分析理解
- bootloader与linux中位置无关代码(PIC)的分析理解
- bootloader与linux中位置无关代码(PIC)的分析理解
- bootloader与linux中位置无关代码(PIC)的分析理解
- 关于商城网站商品sku选择的js简易实现
- kernel编译生成Image zImage uImage的区别
- linux kernel虚拟内存地址耗尽一个实例分析
- Java与Mysql数据库零基础开篇
- ARM linux内核启动时几个关键地址
- PIC(与位置无关代码)在u-boot上的实现
- 位置无关(PIC)代码原理剖析
- vmlinux.lds.s文件分析
- 是先访问TLB(MMU)还是先访问Cache
- What are legacy interrupts?
- Device Tree说明
- linux内存布局及页面映射(x86架构!与arm在某些内存使用上有差异)
- arm linux 内存分布总结
- source insight 加入arm汇编文件
原创粉丝点击
热门IT博客
热门问题
老师的惩罚
人脸识别
我在镇武司摸鱼那些年
重生之率土为王
我在大康的咸鱼生活
盘龙之生命进化
天生仙种
凡人之先天五行
春回大明朝
姑娘不必设防,我是瞎子
初中生头顶脱发怎么办
蓬头发要怎么办
睡觉时头发怎么办
高中脱发严重怎么办
头发很碎很乱怎么办
头发杂毛太多怎么办
额头杂毛太多怎么办
小学生伏天发烧怎么办
头顶出现斑秃怎么办
头发乱飞怎么办
头发毛毛躁躁的怎么办
额头秃怎么办
头发不肯长怎么办
碎头发长不长怎么办
头发不爱长怎么办
头发停止生长怎么办
前额头发卷曲怎么办
宝宝发际线太高怎么办
头顶皮脂腺囊肿怎么办
头顶掉发怎么办
头发干燥怎么办
头发过于浓密怎么办
怀孕五个月还吐怎么办
孕妇胆结石发作怎么办
发根干就掉了怎么办
抖音号丢了怎么办
cad2017激活失败怎么办
裙子拖地怎么办
眼镜框变绿怎么办
睫毛进入眼睛怎么办
眼睫毛老是掉怎么办
总想污的怎么办
医院人满为患怎么办
画不好眉毛怎么办
孕妇脚抽筋怎么办
墨水弄脏衣服怎么办
素描排线排不直怎么办
fps帧数低怎么办
狗狗吃了尿不湿怎么办
小狗乱吃东西怎么办
小狗不爱喝水怎么办
本文乃fireaxe原创,使用GPL发布,可以自由拷贝,转载。但转载请保持文档的完整性,并注明原作者及原链接。内容可任意使用,但对因使用该内容引起的后果不做任何保证。
- PIC(与位置无关代码)在u-boot上的实现
- 位置无关代码(PIC)的思考
- 位置无关代码(PIC)的思考
- 位置无关代码(PIC)的思考
- 位置无关代码(PIC)的思考
- 位置无关代码(PIC)的思考
- 位置无关代码(PIC)的思考
- 位置无关代码(PIC)的思考
- 位置无关代码PIC
- ARM 位置无关代码(PIC)的分析理解
- 位置无关代码(PIC)的分析…
- 基于位置无关代码PIC的程序设计
- bootloader与linux中位置无关代码(PIC)的分析理解
- bootloader与linux中位置无关代码(PIC)的分析理解
- bootloader与linux中位置无关代码(PIC)的分析理解
- bootloader与linux中位置无关代码(PIC)的分析理解
- bootloader与linux中位置无关代码(PIC)的分析理解
- bootloader与linux中位置无关代码(PIC)的分析理解
- 关于商城网站商品sku选择的js简易实现
- kernel编译生成Image zImage uImage的区别
- linux kernel虚拟内存地址耗尽一个实例分析
- Java与Mysql数据库零基础开篇
- ARM linux内核启动时几个关键地址
- PIC(与位置无关代码)在u-boot上的实现
- 位置无关(PIC)代码原理剖析
- vmlinux.lds.s文件分析
- 是先访问TLB(MMU)还是先访问Cache
- What are legacy interrupts?
- Device Tree说明
- linux内存布局及页面映射(x86架构!与arm在某些内存使用上有差异)
- arm linux 内存分布总结
- source insight 加入arm汇编文件