zoukankan      html  css  js  c++  java
  • 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的启动过程,接下来将跳转内核。

  • 相关阅读:
    【PC Basic】CPU、核、多线程的那些事儿
    为什么要使用 do while(0)?
    DPDK CAS(compare and set)操作
    编程中Foo,Bar 到底是什么意思
    如何用Python进行ARP攻击?
    有哪些有趣的化学方程式?
    1636. 按照频率将数组升序排序
    1046. 最后一块石头的重量
    1122. 数组的相对排序
    459. 重复的子字符串
  • 原文地址:https://www.cnblogs.com/genshu123/p/11210990.html
Copyright © 2011-2022 走看看