linux内核构建过程

内核文件是raw binary格式,只包含机器码和数据。主要由setup和vmlinux两部分构成。

内核的构建过程:
1、生成内核链接脚本vmlinux.lds
2、内核子目录built-in.o、模块、符号表的生成
3、elf格式vmlinux与vmlinux.bin的生成
4、压缩elf格式vmlinux与压缩vmlinux.bin生成
5、setup.elf与setup.bin生成
6、setup.bin和vmlinux.bin生成最终的内核镜像bzImage
7、最终vmlinuz的生成

2、各级built-in.o与模块的生成:
内核的Makefile系统
linux kernel的Makefile系统提供简单的编程接口,各级内核子目录或模块的Makefile只需指定obj-y,obj-m等变量即可完成编译。
obj-y是指定内核.o,obj-m指定模块.o,obj-n不编译。

对子目录的递归处理是由scripts/Makefile.build完成的,主要功能为:
1、include子目录的Kbuild或Makefile,读取obj-y,obj-m等变量
2、include scripts/Makefile.lib,对obj-y,obj-m等变量处理,并生成新的变量如subdir-y等
3、built-in.o的生成,根据变量obj-y的值,递归子目录生成的built-in.o与当前目录的.o链接生成
4、module的处理,根据obj-m中的.o文件并递归子目录,生成modules.order,.tmp_versions/*.mod;以便顶级Makefile对模块的生成
5、其它处理,如lib-y的处理等


3、elf格式vmlinux与vmlinux.bin的生成
vmlinux生成:
vmlinux是由各级子目录的built-in.o根据链接脚本arch/x86/kernel/vmlinux.lds链接生成

Makefile:

845 # vmlinux image - including updated kernel symbols
 846 vmlinux: $(vmlinux-lds) $(vmlinux-init) $(vmlinux-main) vmlinux.o $(kallsyms.o) FORCE
 847 ifdef CONFIG_HEADERS_CHECK
 848         $(Q)$(MAKE) -f $(srctree)/Makefile headers_check
 849 endif
 850 ifdef CONFIG_SAMPLES
 851         $(Q)$(MAKE) $(build)=samples
 852 endif
 853 ifdef CONFIG_BUILD_DOCSRC
 854         $(Q)$(MAKE) $(build)=Documentation
 855 endif
 856         $(call vmlinux-modpost)
 857         $(call if_changed_rule,vmlinux__)
 858         $(Q)rm -f .old_version
 
 726 # Link of vmlinux
 727 # If CONFIG_KALLSYMS is set .version is already updated
 728 # Generate System.map and verify that the content is consistent
 729 # Use + in front of the vmlinux_version rule to silent warning with make -j2
 730 # First command is ':' to allow us to use + in front of the rule
 731 define rule_vmlinux__
 732         :
 733         $(if $(CONFIG_KALLSYMS),,+$(call cmd,vmlinux_version))
 734 
 735         $(call cmd,vmlinux__)
 736         $(Q)echo 'cmd_$@ := $(cmd_vmlinux__)' > $(@D)/.$(@F).cmd
 737 
 738         $(Q)$(if $($(quiet)cmd_sysmap),                                      \
 739           echo '  $($(quiet)cmd_sysmap)  System.map' &&)                     \
 740         $(cmd_sysmap) $@ System.map;                                         \
 741         if [ $$? -ne 0 ]; then                                               \
 742                 rm -f $@;                                                    \
 743                 /bin/false;                                                  \
 744         fi;
 745         $(verify_kallsyms)
 746 endef

 702 # Rule to link vmlinux - also used during CONFIG_KALLSYMS
 703 # May be overridden by arch/$(ARCH)/Makefile
 704 quiet_cmd_vmlinux__ ?= LD      $@
 705       cmd_vmlinux__ ?= $(LD) $(LDFLAGS) $(LDFLAGS_vmlinux) -o $@ \
 706       -T $(vmlinux-lds) $(vmlinux-init)                          \
 707       --start-group $(vmlinux-main) --end-group                  \
 708       $(filter-out $(vmlinux-lds) $(vmlinux-init) $(vmlinux-main) vmlinux.o FORCE ,$^)
 709 


vmlinux.bin的生成:
将vmlinux用objcopy将删掉.comment节、符号表、重定位表
arch/x86/boot/compressed/Makefile:

 29 OBJCOPYFLAGS_vmlinux.bin :=  -R .comment -S
 30 $(obj)/vmlinux.bin: vmlinux FORCE
 31 $(call if_changed,objcopy)
scripts/Makefile.lib:
195 quiet_cmd_objcopy = OBJCOPY $@
196 cmd_objcopy = $(OBJCOPY) $(OBJCOPYFLAGS) $(OBJCOPYFLAGS_$(@F)) $< $@


4、压缩elf格式vmlinux与压缩vmlinux.bin生成
压缩的vmlinux的生成:
将自解压代码,mkpiggy根据vmlinux.bin生成的piggy.o,链接生成压缩的vmlinux(ELF格式)
piggy.o的生成:
1、将vmlinux.bin压缩生成压缩文件
2、根据压缩文件,由mkpiggy产生piggy.S文件,数据为压缩文件
3、编译piggy.S生成piggy.o
4、将piggy.o与自解压代码链接生成elf格式的vmlinux
arch/x86/boot/compressed/Makefile:

42 vmlinux.bin.all-y := $(obj)/vmlinux.bin
 43 vmlinux.bin.all-$(CONFIG_X86_NEED_RELOCS) += $(obj)/vmlinux.relocs
 44 
 45 $(obj)/vmlinux.bin.gz: $(vmlinux.bin.all-y) FORCE
 46         $(call if_changed,gzip)
 47 $(obj)/vmlinux.bin.bz2: $(vmlinux.bin.all-y) FORCE
 48         $(call if_changed,bzip2)
 49 $(obj)/vmlinux.bin.lzma: $(vmlinux.bin.all-y) FORCE
 50         $(call if_changed,lzma)
 51 
 52 suffix-$(CONFIG_KERNEL_GZIP)    := gz
 53 suffix-$(CONFIG_KERNEL_BZIP2)   := bz2
 54 suffix-$(CONFIG_KERNEL_LZMA)    := lzma
 55 
 56 quiet_cmd_mkpiggy = MKPIGGY $@
 57       cmd_mkpiggy = $(obj)/mkpiggy $< > $@ || ( rm -f $@ ; false )
 58 
 59 targets += piggy.S
 60 $(obj)/piggy.S: $(obj)/vmlinux.bin.$(suffix-y) $(obj)/mkpiggy FORCE
 61         $(call if_changed,mkpiggy)


arch/x86/boot/compressed/piggy.S:

  1 .section ".rodata.compressed","a",@progbits
  2 .globl z_input_len
  3 z_input_len = 3495594
  4 .globl z_output_len
  5 z_output_len = 7618944
  6 .globl z_extract_offset
  7 z_extract_offset = 0x3f8000
  8 .globl z_extract_offset_negative
  9 z_extract_offset_negative = -0x3f8000
 10 .globl input_data, input_data_end
 11 input_data:
 12 .incbin "arch/x86/boot/compressed/vmlinux.bin.gz"
 13 input_data_end:


arch/x86/boot/compressed/Makefile:

 25 $(obj)/vmlinux: $(obj)/vmlinux.lds $(obj)/head_$(BITS).o $(obj)/misc.o $(obj)/piggy.o FORCE
 26         $(call if_changed,ld)
 27         @:

 


压缩的vmlinux.bin生成:
objcopy将压缩的elf格式vmlinux转成binary格式vmlinux.bin
arch/x86/boot/Makefile 

 85 OBJCOPYFLAGS_vmlinux.bin := -O binary -R .note -R .comment -S
 86 $(obj)/vmlinux.bin: $(obj)/compressed/vmlinux FORCE
 87         $(call if_changed,objcopy)
 88 


5、setup.elf与setup.bin生成
setup.elf的构建:
arch/x86/boot/Makefile

 29 setup-y         += a20.o bioscall.o cmdline.o copy.o cpu.o cpucheck.o edd.o
 30 setup-y         += header.o main.o mca.o memory.o pm.o pmjump.o
 31 setup-y         += printf.o regs.o string.o tty.o video.o video-mode.o
 32 setup-y         += version.o
 33 setup-$(CONFIG_X86_APM_BOOT) += apm.o
 34 
 35 # The link order of the video-*.o modules can matter.  In particular,
 36 # video-vga.o *must* be listed first, followed by video-vesa.o.
 37 # Hardware-specific drivers should follow in the order they should be
 38 # probed, and video-bios.o should typically be last.
 39 setup-y         += video-vga.o
 40 setup-y         += video-vesa.o
 41 setup-y         += video-bios.o

 89 SETUP_OBJS = $(addprefix $(obj)/,$(setup-y))

113 LDFLAGS_setup.elf       := -T
114 $(obj)/setup.elf: $(src)/setup.ld $(SETUP_OBJS) FORCE
115         $(call if_changed,ld)
scripts/Makefile.lib
quiet_cmd_ld = LD      $@
cmd_ld = $(LD) $(LDFLAGS) $(ldflags-y) $(LDFLAGS_$(@F)) \
               $(filter-out FORCE,$^) -o $@


根据setup.ld链接脚本将SETUP_OBJS链接成setup.elf;
arch/x86/boot/setup.ld

 10 SECTIONS
 11 {
 12         . = 0;
 13         .bstext         : { *(.bstext) }
 14         .bsdata         : { *(.bsdata) }
 15 
 16         . = 497;
 17         .header         : { *(.header) }
 18         .entrytext      : { *(.entrytext) }
 19         .inittext       : { *(.inittext) }
 20         .initdata       : { *(.initdata) }
 21         __end_init = .;


可以得出实模式内核第一条执行指令位于.header块中

The kernel is started by jumping to the kernel entry point, which is
located at *segment* offset 0x20 from the start of the real mode
kernel.  This means that if you loaded your real-mode kernel code at
0x90000, the kernel entry point is 9020:0000.
arch/x86/boot/header.S:

 94         .section ".header", "a"
 95         .globl  hdr
 96 hdr:
 97 setup_sects:    .byte 0                 /* Filled in by build.c */
 98 root_flags:     .word ROOT_RDONLY
 99 syssize:        .long 0                 /* Filled in by build.c */
100 ram_size:       .word 0                 /* Obsolete */
101 vid_mode:       .word SVGA_MODE
102 root_dev:       .word 0                 /* Filled in by build.c */
103 boot_flag:      .word 0xAA55
104 
105         # offset 512, entry point
106 
107         .globl  _start
108 _start:
109                 # Explicitly enter this as bytes, or the assembler
110                 # tries to generate a 3-byte jump here, which causes
111                 # everything else to push off to the wrong offset.
112                 .byte   0xeb            # short (2-byte) jump
113                 .byte   start_of_setup-1f


setup是16位实模式代码
arch/x86/boot/Makefile:

 58 # How to compile the 16-bit code.  Note we always compile for -march=i386,
 59 # that way we can complain to the user if the CPU is insufficient.
 60 KBUILD_CFLAGS   := $(LINUXINCLUDE) -g -Os -D_SETUP -D__KERNEL__ \
 61                    -DDISABLE_BRANCH_PROFILING \
 62                    -Wall -Wstrict-prototypes \
 63                    -march=i386 -mregparm=3 \
 64                    -include $(srctree)/$(src)/code16gcc.h \
 65                    -fno-strict-aliasing -fomit-frame-pointer \
 66                    $(call cc-option, -ffreestanding) \
 67                    $(call cc-option, -fno-toplevel-reorder,\
 68                         $(call cc-option, -fno-unit-at-a-time)) \
 69                    $(call cc-option, -fno-stack-protector) \
 70                    $(call cc-option, -mpreferred-stack-boundary=2)


arch/x86/boot/code16gcc.h:

  1 /*
  2  * code16gcc.h
  3  *
  4  * This file is -include'd when compiling 16-bit C code.
  5  * Note: this asm() needs to be emitted before gcc emits any code.
  6  * Depending on gcc version, this requires -fno-unit-at-a-time or
  7  * -fno-toplevel-reorder.
  8  *
  9  * Hopefully gcc will eventually have a real -m16 option so we can
 10  * drop this hack long term.
 11  */
 12 
 13 #ifndef __ASSEMBLY__
 14 asm(".code16gcc");
 15 #endif



setup.bin的构建:
arch/x86/boot/Makefile

117 OBJCOPYFLAGS_setup.bin  := -O binary
118 $(obj)/setup.bin: $(obj)/setup.elf FORCE
119         $(call if_changed,objcopy)
scripts/Makefile.lib
195 quiet_cmd_objcopy = OBJCOPY $@
196 cmd_objcopy = $(OBJCOPY) $(OBJCOPYFLAGS) $(OBJCOPYFLAGS_$(@F)) $< $@



 

将setup由elf文件格式转换成binary格式

setup.bin是实模式的16位代码,bootloader装入实模式和保护模式代码后,首先将控制权交给实模式:
Documentation/x86/boot.txt

    143 The first step in loading a Linux kernel should be to load the
    144 real-mode code (boot sector and setup code) and then examine the
    145 following header at offset 0x01f1.  The real-mode code can total up to
    146 32K, although the boot loader may choose to load only the first two
    147 sectors (1K) and then examine the bootup sector size.
    
    910 **** RUNNING THE KERNEL
    911 
    912 The kernel is started by jumping to the kernel entry point, which is
    913 located at *segment* offset 0x20 from the start of the real mode
    914 kernel.  This means that if you loaded your real-mode kernel code at
    915 0x90000, the kernel entry point is 9020:0000.



 

setup相应的处理完成后会切换成保护模式,并将跳转到startup_32/startup_64执行
start_of_setup->main->go_to_protected_mode->startup_BITS
其中startup_BITS位置存放在实模式内核头的code32_start字段里,bzImage的默认值是0x100000

6、setup.bin和vmlinux.bin生成最终的内核镜像bzImage
内核bzImage的构建:
bzImage是由实模式setup.bin和保护模式内核vmlinux.bin组成
arch/x86/boot/Makefile

 77 quiet_cmd_image = BUILD   $@
 78 cmd_image = $(obj)/tools/build $(obj)/setup.bin $(obj)/vmlinux.bin \
 79         $(ROOT_DEV) > $@
 80 
 81 $(obj)/bzImage: $(obj)/setup.bin $(obj)/vmlinux.bin $(obj)/tools/build FORCE
 82         $(call if_changed,image)
 83         @echo 'Kernel: $@ is ready' ' (#'`cat .version`')'



 

bzImage是arch/x86/boot/tools/build.c将setup.bin和vmlinux.bin组装而成,按512对齐后的setup.bin(以0补齐)和vmlinuz.bin合并成新文件bzImage,并设置相应的头信息,如setup.bin的大小、vmlinuz.bin的大小、CRC等


7、最终vmlinuz的生成
arch/x86/boot/Makefile:install->arch/x86/boot/install.sh
arch/x86/boot/install.sh:

 49 cat $2 > $4/vmlinuz
 50 cp $3 $4/System.map



其中make install还可以用/sbin/installkernel生成initramfs、System.map、启动项等

你可能感兴趣的:(linux,kernel)