Chinaunix首页 | 论坛 | 博客
  • 博客访问: 90972
  • 博文数量: 66
  • 博客积分: 110
  • 博客等级: 民兵
  • 技术积分: 410
  • 用 户 组: 普通用户
  • 注册时间: 2012-06-18 09:34
文章分类
文章存档

2012年(66)

我的朋友

分类:

2012-07-26 11:29:31

从后往前看下编译生成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这里去执行真正的内核了吧 

阅读(517) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~