Author-------Dansen-----xzd2734@163.com
從後往前看下編譯產生zImage的過程,我們可以找到程式的入口還是那個很重要
連結檔案,找到它,產生zImage所在的目錄是kernel/arch/arm/boot/compressed/
Make過程為....ld -p -X -T vmlinux.lds head.o misc.o head-s3c2410.o piggy.o
libgcc.o -o vmlinux
然後是用二進位工具objcopy把vmlinux製作成可執行檔二進位映像檔案zImage
這樣在我們就去kernel/arch/arm/boot/compressed/目錄下去找到vmlinux.lds檔案
如果沒有編譯就不會有這個檔案,因為它也是在編譯過程產生的,由同一目錄下的
vmlinux.lds.in產生,開啟這個檔案
ENTRY(_start)
SECTIONS
{
. = LOAD_ADDR;
_load_addr = .;
. = TEXT_START;
_text = .;
.text : {
_start = .;
*(.start)
*(.text)
........
入口是_start,而且入口就直接定義在這個檔案中了
入口直接接著.start段,所以程式開始是從.start段開始執行的
如果看看vmlinux.lds的產生過程就應該能找到LOAD_ADDR和TEXT_START的值
實際上這兩個值是由其他兩個變數賦給的 ZRELADDR 和 ZTEXTADDR
在kernel/arch/arm/boot/Makefile中我們可以找到這兩個變數的值
ifeq ($(CONFIG_ARCH_S3C2410),y)
ZTEXTADDR = 0x30008000
ZRELADDR = 0x30008000
endif
所以
LOAD_ADDR = 0x30008000
TEXT_START = 0x30008000
看一下vmlinux.lds吧
ENTRY(_start)
SECTIONS
{
. = 0x30008000;
_load_addr = .;
. = 0;
_text = .;
顯然LOAD_ADDR被賦值了0x30008000
看一下TEXT_START怎麼成0了,我想這應該是一個位移吧,位移是0
所以它還是0x30008000
接著下來就從head.s來開始看代碼吧
.section ".start", #alloc, #execinstr
/*
* sort out different calling conventions
*/
.align
start:
.type start,#function
.rept 8
mov r0, r0
.endr
b 1f
.word 0x016f2818 @ Magic numbers to help the loader
.word start @ absolute load/run zImage address
.word _edata @ zImage end address
1: mov r7, r1 @ save architecture ID
這裡一定就是程式的入口了,一般組譯工具的含義就看看英文注釋就是了
有一個要注意的地方,不是一個彙編檔案就是屬於一個段的,不是說先執行完了
head.s再去執行head-s3c2410.s,還是要注意連結的段,顯然head.s
不一會就開始了另一個段.text
.text
adr r0, LC0
ldmia r0, {r1, r2, r3, r4, r5, r6, ip, sp}
subs r0, r0, r1 @ calculate the delta offset
而我們的head-s3c2410.s呢
.section ".start", #alloc, #execinstr
__S3C2410_start:
bic r2, pc, #0x1f
add r3, r2, #0x4000 @ 16 kb is quite enough...
還是屬於.start段的,所以順序執行下來時先執行head-s3c2410.s,然後再去執行
.text段。head-s3c2410.s主要是cpu的一些初始化工作。接著下來我們會需要把核心
接壓縮,先說說為什麼吧。還是注意到上面產生zImage的檔案中有一個piggy.o,往上
追尋可以看到是piggy.o由那個真正的核心vmlinux產生的,這個vmlinux才是啟動後一直在
啟動並執行核心,原本很大,壓縮以後可以方便地放在flash中,當然其實不壓縮跳到它的
入口也就可以運行了。解壓的核心是準備從LOAD_ADDR = 0x30008000開始的4M空間,會覆蓋
我們的當前啟動並執行代碼,那樣就先把核心解壓到我們這個zImage+分配堆棧0x10000的最後
cmp r4, r2 //r4 是LOAD_ADDR=0x30008000
bhs wont_overwrite //r2 是當前代碼的最底部 這裡當然不會跳轉
add r0, r4, #4096*1024 @ 4MB largest kernel size
cmp r0, r5 //r5 也是0x30008000
bls wont_overwrite //不會跳轉
mov r5, r2 //r2是(user_stack+4096)在zImage的最後+0x10000
mov r0, r5
mov r3, r7 //machine type
bl decompress_kernel
有了r5,r0,r7作為參數,就可以調用misc.c中的decompress_kernel函數進行解壓縮了
這個函數調用的gunzip函數時gcc的庫函數,所以在源碼中找不到的
解壓在r5開始的地方,函數返回的是r0解壓得到的長度。這時候我們需要對代碼經行調整
add r1, r5, r0 @ end of decompressed kernel
adr r2, reloc_start
ldr r3, LC1 //LC1: .word reloc_end - reloc_start
add r3, r2, r3
1: ldmia r2!, {r8 - r13} @ copy relocation code
stmia r1!, {r8 - r13}
ldmia r2!, {r8 - r13}
stmia r1!, {r8 - r13}
cmp r2, r3 //這裡就把從reloc_start到reloc_end這段我們需要的代碼放到了
blo 1b //解壓核心的最後,而在下面我們會將zImage都覆蓋掉
bl cache_clean_flush
add pc, r5, r0 //調到調整後的reloc_start,在decompressed kernel後
reloc_start: add r8, r5, r0 //r5解壓核心開始的地方 r0解壓核心的長度
debug_reloc_start
mov r1, r4 //r4=0x30008000
1:
.rept 4
ldmia r5!, {r0, r2, r3, r9 - r13} @ relocate kernel
stmia r1!, {r0, r2, r3, r9 - r13}
.endr
cmp r5, r8
blo 1b //這樣就又把解壓的真正核心移到了0x30008000處
call_kernel: bl cache_clean_flush
bl cache_off
mov r0, #0
mov r1, r7 @ restore architecture number
mov pc, r4 @ call kernel
上面就是跳到0x30008000這裡去執行真正的核心了吧