关于
vmlinux -> arch/arm/boot/Image -> arch/arm/boot/compressed/vmlinux -> arm/arch/boot/zImage
linux/arch/arm/boot/compressed/head.S
start:
/*
* 从反汇编可以看到,开头有8句 NOP
*/
.type start,#function
.rept 7
mov r0, r0
.endr
ARM( mov r0, r0 )
ARM( b 1f )
/*
* Magic会被bootloader读取并判断是否是zImage
* start 和 _edata 是编译后确定的地址,由这两个值便可以确定编
* 译后的 Image 的长度,bootloader 可以根据这个长度来决定要复
* 制的内核映像大小。
*/
.word 0x016f2818 @ Magic numbers to help the loader
.word start @ absolute load/run zImage address
.word _edata @ zImage end address
/*
* r1, r2 分别为 bootloader 传入的参数
*/
1: mov r7, r1 @ save architecture ID
mov r8, r2 @ save atags pointer
/*
* Booting from Angel - need to enter SVC mode and disable
* FIQs/IRQs (numeric definitions from angel arm.h source).
* We only do this if we were in user mode on entry.
*
* 确保此时 CPU 为 SVC 模式,如果不是则主动使用SWI指令来切换到
* SVC 模式(特权模式)
*/
mrs r2, cpsr @ get current mode
tst r2, #3 @ not user?
bne not_angel
mov r0, #0x17 @ angel_SWIreason_EnterSVC
ARM( swi 0x123456 ) @ angel_SWI_ARM
{{{
上面这段反汇编如下:
00000000 :
0: e1a00000 nop ; (mov r0, r0)
4: e1a00000 nop ; (mov r0, r0)
8: e1a00000 nop ; (mov r0, r0)
c: e1a00000 nop ; (mov r0, r0)
10: e1a00000 nop ; (mov r0, r0)
14: e1a00000 nop ; (mov r0, r0)
18: e1a00000 nop ; (mov r0, r0)
1c: e1a00000 nop ; (mov r0, r0)
20: ea000002 b 30 <_text+0x30>
24: 016f2818 cmneq pc, r8, lsl r8 ;
28: 00000000 andeq r0, r0, r0
2c: 000b7d68 andeq r7, fp, r8, ror #26
30: e1a07001 mov r7, r1
34: e1a08002 mov r8, r2
38: e10f2000 mrs r2, CPSR
3c: e3120003 tst r2, #3
40: 1a000001 bne 4c
44: e3a00017 mov r0, #23
48: ef123456 svc 0x00123456
}}}
not_angel:
/*
* 关闭中断
*/
mrs r2, cpsr @ turn off interrupts to
orr r2, r2, #0xc0 @ prevent angel from running
msr cpsr_c, r2
/*
* 见 arch/arm/Kconfig, zreladdr 是在 make menuconfig 是设定的
* 对于 smdk2410 它设置为 0x30008000
*/
ldr r4, =zreladdr
/*
* 打开数据和指定cache,见其注释。
* FIXME: 这里打开 cache 的目的?
*/
bl cache_on
/*
* 这里用的是 adr,这个伪指令与 ldr 不同,adr是根据当前PC位置来获取某个内存值,
* 利用它来编写位置无关代码。ldr则刚好相反,ldr 引用的地址在编译后就已确定
*/
restart: adr r0, LC0
ldmia r0, {r1, r2, r3, r5, r6, r9, r11, r12}
ldr sp, [r0, #32]
{{{
将 vmlinux load 到 SDRAM 的 0x30400000 处,经过上面的代码后,寄存器的值如下
System and User mode registers
r0: 30400150 r1: 00000150 r2: 000b7d68 r3: 000b7d8c
r4: 30008000 r5: 00000000 r6: 000b7d68 r7: 0000000b
r8: 33f986e4 r9: 00174d04 r10: 40000000 r11: 000b7d34
r12: 000b7d5c sp_usr: ffffffff lr_usr: ffffffff pc: 30400074
cpsr: 600000d3
Supervisor mode shadow registers
sp_svc: 000b8d8c lr_svc: 304002b0 spsr_svc: f00000fb
下面几句是计算当前偏移地址。编译后,LC0 地址已经确定,见上面的 r1,
即LC0标号编译后指定的地址是 0x00000150。
而当前运行时,通过adr伪指令,根据当前PC偏移得到的LC0地址却是0x30400150,
将这个地址值放到r0中。
}}}
/*
* We might be running at a different address. We need
* to fix up various pointers.
*/
sub r0, r0, r1 @ calculate the delta offset
add r5, r5, r0 @ _start
add r6, r6, r0 @ _edata
/* malloc space is above the relocated stack (64k max) */
add sp, sp, r0
add r10, sp, #0x10000
链接后,sp在最后 bss 后预留了4k空间作为栈,再往高地址预留64k
.align 2
.type LC0, #object
LC0: .word LC0 @ r1
.word __bss_start @ r2
.word _end @ r3
.word _start @ r5
.word _edata @ r6
.word _image_size @ r9
.word _got_start @ r11
.word _got_end @ ip
.word user_stack_end @ sp
.size LC0, . - LC0
这里的代码目的就是要将压缩后的Image重新解压到指定地址上,所以在解压之前要保证当
前所在的地址空间范围与解压后的地址空间范围不会重叠。如果有重叠则要先处理重叠。
无重叠则直接跳到 wont_overwrite , 见下面代码。
cmp r4, r10
bhs wont_overwrite
add r10, r4, r9
cmp r10, r5
bls wont_overwrite
接下来就是校正GOT表中的地址了。如果地址偏移 r0 不移0, 则将 GOT 表中每个值都加上
偏移。
teq r0, #0
beq not_relocated
add r11, r11, r0
add r12, r12, r0
/*
* 校正BSS地址
*/
add r2, r2, r0
add r3, r3, r0
/*
* 校正GOT表的地址
*/
1: ldr r1, [r11, #0] @ relocate entries in the GOT
add r1, r1, r0 @ table. This fixes up the
str r1, [r11], #4 @ C references.
cmp r11, r12
blo 1b
/*
* BSS 段清0
*/
not_relocated: mov r0, #0
1: str r0, [r2], #4 @ clear bss
str r0, [r2], #4
str r0, [r2], #4
str r0, [r2], #4
cmp r2, r3
blo 1b
开始真正的解压工作:
mov r0, r4
mov r1, sp @ malloc space above stack
add r2, sp, #0x10000 @ 64k max
mov r3, r7
bl decompress_kernel
bl cache_clean_flush
bl cache_off
mov r0, #0 @ must be zero
mov r1, r7 @ restore architecture number
mov r2, r8 @ restore atags pointer
mov pc, r4 @ call kernel
可以看到最后 pc 将跳到 r4 地址处,r4 的值从一开始就没变过,一直是 zreladdr