ARM Linux编译链接过程分析

ARM 131浏览

 

.

.

编译生成过程

首先,根目录下面生成了 vmlinux ,这个可以从根目录下的 Makefile 过程看到。当然,简单一些,也可以看根目录下的 .vmlinux.cmd 文件,其内容如下:

cmd_vmlinux := arm-iwmmxt-linux-gnueabi-ld -EL  -p --no-undefined -X -o vmlinux -T arch/arm/kernel/vmlinux.lds arch/arm/kernel/head.o arch/arm/kernel/init_task.o  init/built-in.o --start-group  usr/built-in.o  arch/arm/kernel/built-in.o  arch/arm/mm/built-in.o  arch/arm/common/built-in.o  arch/arm/mach-pxa/built-in.o  kernel/built-in.o  mm/built-in.o  fs/built-in.o  ipc/built-in.o  security/built-in.o  crypto/built-in.o  block/built-in.o  arch/arm/lib/lib.a  lib/lib.a  arch/arm/lib/built-in.o  lib/built-in.o  drivers/built-in.o  sound/built-in.o  net/built-in.o --end-group .tmp_kallsyms2.o

从这个文件里可以看到 vmlinux 是依赖于哪些文件的,这里我们可以看到每个目录下面都有一个 built-in.o ,这是因为系统对于每个目录会递归调用 scripts/Makefile.build ,而它里面定义的 builtin-target 就是 built-in.o ,如下:

builtin-target := $(obj)/built-in.o

平台相关过程

首先,编译链接过程会执行到: arch/arm/boot

分析它下面的 Makefile 文件,可知:

$(obj)/Image: vmlinux FORCE

       $(call if_changed,objcopy)

       @echo '  Kernel: $@ is ready'

故而, Image 是从根目录下的 vmlinux 直接拷贝生成的。

首先,

 

如果是非压缩内核,则这一步骤就没有了。

当是压缩内核时,首先,编译链接过程还是会先执行到: arch/arm/boot

分析它下面的 Makefile 文件,可知:

$(obj)/compressed/vmlinux: $(obj)/Image FORCE

       $(Q)$(MAKE) $(build)=$(obj)/compressed $@

故而可知, arch/arm/boot/vmlinux 会依赖于 arch/arm/boot/Image ,其实就是非压缩内核,也就是根目录下的 vmlinux ;而后,又会调用 kbuild 来执行 arch/arm/boot/compressed 下的 Makefile:

分析它下面的 Makefile 文件,可知:

$(obj)/vmlinux: $(obj)/vmlinux.lds $(obj)/$(HEAD) $(obj)/piggy.o /

              $(addprefix $(obj)/, $(OBJS)) FORCE

即: compressed/vmlinux 除了依赖于 arch/arm/boot/Imag 外,还依赖于 vmlinux.lds  piggy.o 等,它们都在 compressed 目录下。

其实,如果看 compressed 目录下的 .vmlinux.cmd 文件,可以清楚知道 compressed/vmlinux 还依赖的文件有: libgcc.a vmlinux.lds head.o piggy.o misc.o head-xscale.o ;注意,因为对 arch/arm/boot/Image 的依赖是在 arch/arm/boot/ 目录里说明的,故而 compressed 目录下的 .vmlinux.cmd 文件没有包括这一项。

回到 arch/arm/boot/Makefile 文件,可以看到如下内容:

$(obj)/zImage:       $(obj)/compressed/vmlinux FORCE

       $(call if_changed,objcopy)

       @echo '  Kernel: $@ is ready'

也就是说,压缩内核 zImage 就是从 compressed/vmlinux 直接拷贝生成的。

因为我们的内核要把开机 Logo 加进去,故而做了最后的处理,在 scripts/ mkImageLogo.sh 文件里:

imagefile=ImageLogo

logofile=./scripts/logo.bin

dd if=/dev/zero of=t0.img bs=1024k count=2

cp arch/arm/boot/zImage  t1.img

cat t0.img >> t1.img

dd if=t1.img of=t2.img bs=512 count=6293 #4M:7317 3.5M: 6293 3M:5269,2M:3221

cat  $logofile  >> t2.img

mv t2.img  ${imagefile}

可见,要么是从 Image 里生成 (M200 项目 ) ,要么是从 zImage 里生成的 (M201 项目 ) 

链接过程

这一段指的就是非压缩内核了,因为非压缩内核直接拷贝的就是根目录里的 vmlinux ,故而只要看根目录里的 vmlinux 的生成过程就可以了。

首先,从根目录里 Makefile 可以得知,链接过程如下:

vmlinux-lds  := arch/$(ARCH)/kernel/vmlinux.lds

# Rule to link vmlinux - also used during CONFIG_KALLSYMS

# May be overridden by arch/$(ARCH)/Makefile

quiet_cmd_vmlinux__ ?= LD      $@

      cmd_vmlinux__ ?= $(LD) $(LDFLAGS) $(LDFLAGS_vmlinux) -o $@ /

       -T $(vmlinux-lds) $(vmlinux-init)                          /

      --start-group $(vmlinux-main) --end-group                  /

      $(filter-out $(vmlinux-lds) $(vmlinux-init) $(vmlinux-main) FORCE ,$^)

从以上黑体的部分可以看出,链接过程的依据文件是: arch/arm/kernel/vmlinux.lds ,在该文件中,可以看到如下内容:

ENTRY(stext)

jiffies = jiffies_64;

SECTIONS

{

  . = (0xc0000000) + 0x00008000;

  .init : { /* Init code and data        */

  _stext = .;

   _sinittext = .;

   *(.init.text)

   _einittext = .;

也就是说,我们的非压缩内核要被装载到虚拟地址 0xc0008000 的地方,即物理地址 0x00008000 的地方 ( 当然也取决于我们设置的虚拟地址和物理地址的片选 ) 。同时, ENTRY  stext 就说明入口点就是代码段的开头,即 _stext 的起始点,从上面可以看到它就是 0xc0008000 这个地址;而从根目录的 .vmlinux.cmd 文件中,我们可以得知,第一个被链接的文件是: arch/arm/kernel/head.o

cmd_vmlinux := arm-iwmmxt-linux-gnueabi-ld -EL  -p --no-undefined -X -o vmlinux -T arch/arm/kernel/vmlinux.lds arch/arm/kernel/head.o arch/arm/kernel/init_task.o  init/built-in.o --start-group  usr/built-in.o  arch/arm/kernel/built-in.o  arch/arm/mm/built-in.o  arch/arm/common/built-in.o  arch/arm/mach-pxa/built-in.o  kernel/built-in.o  mm/built-in.o  fs/built-in.o  ipc/built-in.o  security/built-in.o  crypto/built-in.o  block/built-in.o  arch/arm/lib/lib.a  lib/lib.a  arch/arm/lib/built-in.o  lib/built-in.o  drivers/built-in.o  sound/built-in.o  net/built-in.o --end-group .tmp_kallsyms2.o

故而,非压缩内核被 Boot Loader 装载后,从 arch/arm/kernel/head.S 开始执行。

压缩内核涉及到再次链接过程,故而就要看 vmlinux ,故而只要看目录 arch/arm/boot/compressed 下的 vmlinux.lds 文件了(因为这个目录下的 Makefile 文件在链接地时候,使用了它),在这个文件里,我们看到:

ENTRY(_start)

SECTIONS

{

  . = 0;

  _text = .;

 

  .text : {

    _start = .;

    *(.start)

    *(.text)

    *(.text.*)

    *(.fixup)

    *(.gnu.warning)

    *(.rodata)

    *(.rodata.*)

    *(.glue_7)

    *(.glue_7t)

    *(.piggydata)

    . = ALIGN(4);

  }

故而入口点是 _start ,而他就是代码段的开始处。

察看 arch/arm/boot/compressed 下的 .vmlinux.cmd 文件中,我们可以得知,第一个被链接的文件是: arch/arm/boot/compressed/head.o 

cmd_arch/arm/boot/compressed/vmlinux := arm-iwmmxt-linux-gnueabi-ld -EL   --defsym zreladdr=0xb0008000 -p --no-undefined -X /opt/marvell/toolschain/arm-linux-4.1.1/bin/../lib/gcc/arm-iwmmxt-linux-gnueabi/4.1.1/libgcc.a -T arch/arm/boot/compressed/vmlinux.lds arch/arm/boot/compressed/head.o arch/arm/boot/compressed/piggy.o arch/arm/boot/compressed/misc.o arch/arm/boot/compressed/head-xscale.o -o arch/arm/boot/compressed/vmlinux

故而,压缩内核被 Boot Loader 装载后,是从 arch/arm/boot/compressed/head.S 开始执行的。