linux內核zImage詳解


參考文檔:https://blog.csdn.net/haoge921026/article/details/46785995

以下內容基於s5pv210進行分析

  zImage由head.o,piggy.gzip.o,misc等鏈接組成,piggy.gzip.o中包含壓縮的內核鏡像,zImage的作用實際上就是對內核進行解碼。

  zImage還是位置無關碼,它的鏈接地址為0,可以在任何地址運行,因為在對其源文件進行編譯時編譯器參數設置了-fpic,通過反匯編看到編譯生成了.got和.got.plt段。.dot.plt為空,查看反匯編得知編譯器對c語言函數的調用是通過bl指令實現的,所以c的函數調用是位置無關碼;而對於c中全局變量的處理是通過相對尋址找到全局變量一一對應的.got地址(這里的相對尋址是:在每個函數段中如果使用了全局變量都會存放.got首地址相對運行pc的偏移量以及全局變量在.got中的偏移),所以無論運行地址和鏈接地址匹不匹配,代碼都能正確找到全局變量的.got地址。.got地址中存放了全局變量的鏈接地址,所以只要在zImage的初始化c語言運行環境部分增加對.got部分全局變量的重定位則代碼將正確運行,因此zImage成為了位置無關碼


  現在開始分析arch/arm/boot/compressed/head.s進行代碼分析:

start:
		.type	start,#function        //用於指定標號start為函數                      
		.rept	8                           //指定.endr以前的指令循環8次
		mov	r0, r0                    
		.endr                             

		b	1f
		.word	0x016f2818		@魔數用於表示zImage的身份
		.word	start			@ zImage的鏈接地址
		.word	_edata			@ zImage的鏈接結束地址
1:		mov	r7, r1			@ save architecture ID
		mov	r8, r2			@ save atags pointer     
#ifndef __ARM_ARCH_2__
/*用於判斷是不是angel啟動,我們是u-boot啟動進來時已經是svc模式了所以直接跳到
not_angel  */
		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
 THUMB(		svc	0xab		)	@ angel_SWI_THUMB
not_angel:
		mrs	r2, cpsr		@ turn off interrupts to
		orr	r2, r2, #0xc0		@ prevent angel from running
		msr	cpsr_c, r2
#else
		teqp	pc, #0x0c000003		@ turn off interrupts
#endif
		.text
		adr	r0, LC0      //將LC0的運行地址加載到r0,
 ARM(		ldmia	r0, {r1, r2, r3, r4, r5, r6, r11, ip, sp})
/*將r0指定的地址中的數據依次加載到括號里的寄存器中:
    r1 : LC0的鏈接地址
    r2 : BSS 起始鏈接地址
    r3 : BSS 結束鏈接地址
    r4 : 內核的鏈接地址
    r5 : zImage的鏈接地址
    r6 : 內核的大小
    r11 :.got的起始鏈接地址,
    ip :.got的結束鏈接地址
    sp :鏈接下的棧頂
    r0 : LC0的運行運行地址*/
 THUMB(		ldmia	r0, {r1, r2, r3, r4, r5, r6, r11, ip}	) //無效
 THUMB(		ldr	sp, [r0, #32]				)  //無效

		subs	r0, r0, r1		//r0成為運行地址與鏈接地址的偏移量						
		beq	not_relocated	//運行地址與連接地址相同跳轉該語句						
		
		add	r5, r5, r0          //r5 : zImage的運行地址
		add	r11, r11, r0       //r11:.got的起始運行地址
		add	ip, ip, r0            //ip:.got的結束運行地址
    
#ifndef CONFIG_ZBOOT_ROM
		add	r2, r2, r0     //r2 :bss的運行起始地址
		add	r3, r3, r0    //r3:bss的運行結束地址
		add	sp, sp, r0   //sp:運行的棧頂地址
		/*
		 * 將.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, ip
		blo	1b
#else

		/*
		未編譯
		 */
1:		ldr	r1, [r11, #0]		@ relocate entries in the GOT
		cmp	r1, r2			@ entry < bss_start ||
		cmphs	r3, r1			@ _end < entry
		addlo	r1, r1, r0		@ table.  This fixes up the
		str	r1, [r11], #4		@ C references.
		cmp	r11, ip
		blo	1b
#endif
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
bl	cache_on     

		mov	r1, sp			//r1 : 運行的棧頂地址
		add	r2, sp, #0x10000	//r2: 堆結束地址64k
/*堆的結束地址大於內核的起始地址或者內核的結束地址大於zImage的運行起始地址將發生覆蓋,我們這邊會發生覆蓋所以不跳轉繼續往下執行*/              
                cmp	r4, r2
		bhs	wont_overwrite
		add	r0, r4, r6
		cmp	r0, r5
		bls	wont_overwrite
/*
r0:堆結束的地址 
r1:堆起始的地址 
r2:堆結束的地址  
r3:機器ID 
*/
		mov	r5, r2			@ decompress after malloc space
		mov	r0, r5
		mov	r3, r7
		bl	decompress_kernel

 這里看看 decompress_kernel中的傳入參數

unsigned long decompress_kernel(
unsigned long output_start, //r0    解壓內核輸出地址
unsigned long free_mem_ptr_p,//r1 堆起始地址
unsigned long free_mem_ptr_end_p,//r2  堆結束地址
int arch_id//r3 機器ID
)

解壓后返回到head中繼續執行

                add	r0, r0, #127 + 128	@ alignment + stack
		bic	r0, r0, #127		@ align the kernel length
分析如下:
r0為decompress_kernel()函數的返回值,它的返回值最終為Linux內核解壓后的長度,
這里的第一條指令完成的功能是在解壓后的Linux內核后面預留128字節的棧空間,
第二條指令使最終r0的值為128字節對齊 

此時我們的內存空間分布如下:

|                | 
|                |   
|                |
|----------------|---- 
|  128byte       |  |
|                |  |
|    +           |  |   
|                |  r0 
| 解壓后的內核   |  |
|                |  |
|                |  |
|----------------|<-------r5 
|    堆64k       |   
|----------------|
|    棧4k        |
|----------------|
|                |
|  壓縮的內核    |
|  當前運行的代碼|      
|                |
|----------------|0x30008000 zImage的加載地址 
|                |  

---------------------

/*
 * r0     = decompressed kernel length
 * r1-r3  = unused
 * r4     = kernel execution address
 * r5     = decompressed kernel start
 * r7     = architecture ID
 * r8     = atags pointer
 * r9-r12,r14 = corrupted
 */
		add	r1, r5, r0		@ end of decompressed kernel
		adr	r2, reloc_start
		ldr	r3, LC1
		add	r3, r2, r3
1:		ldmia	r2!, {r9 - r12, r14}	@ copy relocation code
		stmia	r1!, {r9 - r12, r14}
		ldmia	r2!, {r9 - r12, r14}
		stmia	r1!, {r9 - r12, r14}
		cmp	r2, r3
		blo	1b
		mov	sp, r1
		add	sp, sp, #128		@ relocate the stack

		bl	cache_clean_flush
 ARM(		add	pc, r5, r0		) @ call relocation code
 THUMB(		add	r12, r5, r0		)
 THUMB(		mov	pc, r12			) @ call relocation code
解析如下: 
 r1 = r5 + r0 = 解壓后內核存放的地址 + 內核大小 
 r2 = 當前reloc_start標簽所在的地址 
 r3 = *LC1 
LC1: .word reloc_end - reloc_start
 所以r3 為重定位代碼段的大小 
 r3 = r2 + r3 =重定位代碼段的結束地址  
 接下來的指令就是將重定位的代碼段搬移到解壓的Linux內核后面 並且重定義了棧最后跳轉重定義代碼

|                |           
|----------------|<---sp  
|    128byte     |
|----------------|<---r1  
|                | 
| 重定位代碼段   |   
|                |
|----------------|<---pc  
|  128byte       |  |
|                |  |
|    +           |  |   
|                |  r0 
| 解壓后的內核   |  |
|                |  |
|                |  |
|----------------|------->r5 
|    堆64k       |   
|----------------|
|    棧4k        |
|----------------|
|                |
|  壓縮的內核    |
|  當前運行的代碼|      
|                |
|----------------|0x30008000 zImage的加載地址 
|                |  
---------------------

/ * r0     = decompressed kernel length
 * r1-r3  = unused
 * r4     = kernel execution address
 * r5     = decompressed kernel start
 * r7     = architecture ID
 * r8     = atags pointer
 * r9-r12,r14 = corrupted
 */
		.align	5
reloc_start:	add	r9, r5, r0         //內核的結束地址
		sub	r9, r9, #128		//減掉棧部分
		debug_reloc_start              
		mov	r1, r4
1:
		.rept	4
		ldmia	r5!, {r0, r2, r3, r10 - r12, r14}	//一次copy28個字
		stmia	r1!, {r0, r2, r3, r10 - r12, r14}
		.endr

		cmp	r5, r9
		blo	1b
		mov	sp, r1
		add	sp, sp, #128		@ relocate the stack
		debug_reloc_end

call_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

  以上就是zImage的啟動過程,接下來將跳轉內核。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM