学习笔记 --- LINUX内核的配置与编译分析

从网上下载一个内核,需要如下步骤操作:

1 解压

2 打补丁(针对内核做的修改,diff工具生成)

3 配置(make menuconfig)

4 编译

这里重点说明配置和编译;

1 进入/arch/arm/configs 查看和自己目标板相似的配置文件: s3c2410_defconfig

2 进入内核根目录下面执行 make s3c2410_defconfig 进行初步配置,这一步完成后会在根目录生成一个.config文件

3 再 make menuconfig 进行详细配置,他去读.config文件,在这个基础上再我们手动配置详细的内容

   选择每一项,输入Y表示编译进内核,输入M表示编译为模块,以后再动态加载,输入N表示不使用。

4 设置好之后按enter确认保存就配置好了

5 在根目录 make uImage 就可以编译了,自动生成uboot支持的文件 uImage.bin

   在make uImage 最开始做的事是通过.config生成下面两个文件:

      include/linux/autoconf.h   这个文件把.config里面的Y和M定义项都宏定义为1,用于对应C源码的预处理,源码都include这个文件

      include/config/auto.conf   这个文件把.config里面的设置项详细列出是=y还是=m,用于对应子目录下的makefile选择性编译,所有makefile都包含这个文件


下面分析下makefile :

makefile从顶层开始,在根目录下,下面所有makefile都为顶层makefile服务的,查看顶层makefile可看到arch/arm目录下的makefile被包含进来:

include $(srctree)/arch/$(SRCARCH)/Makefile

然后看下之前的auto.conf也被包含进来:

ifeq ($(dot-config),1)
# Read in config
-include include/config/auto.conf

所以顶层makefile包含了他们。

在执行make uImage的时候,执行的arch/arm目录下的makefile,查找uImage:

zImage Image xipImage bootpImage uImage: vmlinux

可知uImage 依赖于 vmlinux,就是说生成uImage之前必须得生成vmlinux;

再查看vmlinux编译入口在顶层makefile里面:

all: vmlinux

然后查看vmlinux的依赖:

vmlinux: $(vmlinux-lds) $(vmlinux-init) $(vmlinux-main) vmlinux.o $(kallsyms.o) FORCE

再列出其他的依赖:

vmlinux-init := $(head-y) $(init-y)
vmlinux-main := $(core-y) $(libs-y) $(drivers-y) $(net-y)
vmlinux-all  := $(vmlinux-init) $(vmlinux-main)
vmlinux-lds  := arch/$(SRCARCH)/kernel/vmlinux.lds
export KBUILD_VMLINUX_OBJS := $(vmlinux-all)

再列出其他的依赖:

head-y := arch/arm/kernel/head$(MMUEXT).o arch/arm/kernel/init_task.o

MMUEXT未定义,所以:

head-y := arch/arm/kernel/head.o arch/arm/kernel/init_task.o

init-y := init/

init-y := $(patsubst %/, %/built-in.o, $(init-y))
相当于:  init/built-in.o


drivers-y         := drivers/  sound/  firmware/

drivers-y := $(patsubst %/, %/built-in.o, $(drivers-y))

相当于:  drivers/built-in.o   sound/built-in.o   firmware/built-in.o


net-y := net/

net-y := $(patsubst %/, %/built-in.o, $(net-y))

相当于:  net/built-in.o


libs-y := lib/

libs-y1 := $(patsubst %/, %/lib.a, $(libs-y))  
libs-y2 := $(patsubst %/, %/built-in.o, $(libs-y))
libs-y := $(libs-y1) $(libs-y2)

相当于:  lib/lib.a  lib/built-in.o


core-y := usr/

core-y += kernel/ mm/ fs/ ipc/ security/ crypto/ block/

core-y := $(patsubst %/, %/built-in.o, $(core-y))

相当于:  usr/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 

上面的.o就是组成内核的原材料,他们的存放规则根据arch/arm/kernel/vmlinux.lds链接文件来指定的,而arch/arm/kernel/vmlinux.lds是由arch/arm/kernel/vmlinux.lds.S生成的,其生成规则在scripts/Makefile.build的第236行开始定义;

看下makefile里面的链接规则:

# 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) vmlinux.o FORCE ,$^)

--------- 注释:

cmd_vmlinux__ ?= $(LD) $(LDFLAGS) $(LDFLAGS_vmlinux) -o $@     指定链接工具与参数

-T $(vmlinux-lds) $(vmlinux-init)    指定链接文件 ,开始放第一段 vmlinux-init := $(head-y) $(init-y)

--start-group $(vmlinux-main) --end-group   放主段vmlinux-main := $(core-y) $(libs-y) $(drivers-y) $(net-y)

$(filter-out $(vmlinux-lds) $(vmlinux-init) $(vmlinux-main) vmlinux.o FORCE ,$^)    最后链接生成vmlinux.o文件;

这个是链接的.o文件顺序规则,而.o文件内的段链接规则就通过链接文件vmlinux.lds指定:


SECTIONS
{
#ifdef CONFIG_XIP_KERNEL
. = XIP_VIRT_ADDR(CONFIG_XIP_PHYS_ADDR);
#else
. = PAGE_OFFSET + TEXT_OFFSET;
#endif
.text.head : {
_stext = .;
_sinittext = .;
*(.text.head)
}

从上面可知最开始是存放所有文件的.text.head段,而第一个文件是head.o,所以最开始存放head.o的.text.head段,查看head.s文件:

.section ".text.head", "ax"
ENTRY(stext)

可以知道确实有这个段,那么这个就是内核的入口;

下面看下如何具体生成uImage的:

1 生成的vmlinux.o文件在内核根目录下面,vmlinux属于未压缩,带注释,调试信息、符号表的最初的内核,Image的大小约3.2MB; 
2 将上面的vmlinux去除调试信息、注释、符号表等内容,生成arch/arm/boot/Image,这是不带多余信息的linux内核
命令:arm-linux-gnu-objcopy -O binary -R .note -R .comment -S  vmlinux arch/arm/boot/Image
3. 将 arch/arm/boot/Image 用gzip -9 压缩生成arch/arm/boot/compressed/piggy.gz大小约1.5MB; 
命令:gzip -f -9 < arch/arm/boot/compressed/../Image > arch/arm/boot/compressed/piggy.gz 
4. 编译arch/arm/boot/compressed/piggy.S 生成arch/arm/boot/compressed/piggy.o大小约1.5MB,这里实际上是将piggy.gz通过piggy.S编译进piggy.o文件中。而piggy.S文件仅有6行,只是包含了文件piggy.gz; 
 命令:arm-linux-gnu-gcc -Wp,-MD,arch/arm/boot/compressed/.piggy.o.d  -nostdinc -isystem /home/justin/crosstool/gcc-3.4.5-glibc-2.3.6/arm-linux-gnu/lib/gcc/arm-linux-gnu/3.4.5/include -D__KERNEL__ -Iinclude  -mlittle-endian -D__ASSEMBLY__ -Wa,-L -gdwarf-2 -mapcs-32 -mno-thumb-interwork -D__LINUX_ARM_ARCH__=4 -march=armv4 -mtune=arm9tdmi -msoft-float    -c -o arch/arm/boot/compressed/piggy.o arch/arm/boot/compressed/piggy.S 
5. 依据arch/arm/boot/compressed/vmlinux.lds 将arch/arm/boot/compressed/目录下的文件head.o 、piggy.o 、misc.o链接生成 arch/arm/boot/compressed/vmlinux,这个vmlinux是经过压缩且含有自解压代码的内核,大小约1.5MB; 
命令:arm-linux-gnu-ld -EL   --defsym zreladdr=0x30008000 --defsym params_phys=0x30000100 -p --no-undefined -X /home/justin/crosstool/gcc-3.4.5-glibc-2.3.6/arm-linux-gnu/lib/gcc/arm-linux-gnu/3.4.5/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 -o arch/arm/boot/compressed/vmlinux 
6. 将arch/arm/boot/compressed/vmlinux去除调试信息、注释、符号表等内容,生成arch/arm/boot/zImage大小约1.5MB;这已经是一个可以使用的linux内核映像文件了; 
命令:arm-linux-gnu-objcopy -O binary -R .note -R .comment -S  arch/arm/boot/compressed/vmlinux arch/arm/boot/zImage 

上面六步总结:


最后将arch/arm/boot/zImage添加64Bytes的相关信息打包为arch/arm/boot/uImage大小约1.5MB; 
命令:/bin/sh /home/farsight/Resources/kernel/linux-2.6.14/scripts/mkuboot.sh -A arm -O linux -T kernel -C none -a 0x30008000 -e 0x30008000 -n 'Linux-2.6.14' -d arch/arm/boot/zImage arch/arm/boot/uImage

这个的具体位置在arch/arm/boot/makefile里面:

quiet_cmd_uimage = UIMAGE  $@
      cmd_uimage = $(CONFIG_SHELL) $(MKIMAGE) -A arm -O linux -T kernel \
		   -C none -a $(LOADADDR) -e $(STARTADDR) \
		   -n 'Linux-$(KERNELRELEASE)' -d $< $@

ifeq ($(CONFIG_ZBOOT_ROM),y)
$(obj)/uImage: LOADADDR=$(CONFIG_ZBOOT_ROM_TEXT)
else
$(obj)/uImage: LOADADDR=$(ZRELADDR)
endif


ifeq ($(CONFIG_THUMB2_KERNEL),y)
# Set bit 0 to 1 so that "mov pc, rx" switches to Thumb-2 mode
$(obj)/uImage: STARTADDR=$(shell echo $(LOADADDR) | sed -e "s/.$$/1/")
else
$(obj)/uImage: STARTADDR=$(LOADADDR)
endif

/home/farsight/Resources/kernel/linux-2.6.14/scripts/mkuboot.sh 表示执行mkimage工具(内部会去工具链或系统里面搜索这个工具),后面一堆都是mkimage参数,这些参数组成一个64Bytes头部和zImage组成uImage;

我们系统是SDRAM里面装载,ARM指令集,所以:

LOADADDR=$(ZRELADDR)
STARTADDR=$(LOADADDR)

所以这个装载地址和入口地址,在我们的系统环境下(NANDFLASH启动,ARM指令集),内核默认就是相等的,都为ZRELADDR,而ZRELADDR是什么?看这个makefile的前面:

MKIMAGE         := $(srctree)/scripts/mkuboot.sh
ifneq ($(MACHINE),)
include $(srctree)/$(MACHINE)/Makefile.boot
endif

# Note: the following conditions must always be true:
#   ZRELADDR == virt_to_phys(PAGE_OFFSET + TEXT_OFFSET)
#   PARAMS_PHYS must be within 4MB of ZRELADDR
#   INITRD_PHYS must be in RAM
ZRELADDR    := $(zreladdr-y)
PARAMS_PHYS := $(params_phys-y)
INITRD_PHYS := $(initrd_phys-y)
可以看到:
ZRELADDR    := $(zreladdr-y)
而包括 zreladdr-y定义在第三行包含的那个文件里面了,2410的路径为arm/arch/mach-s3c2410/Makefile.boot ,里面为:

zreladdr-y	:= 0x30008000
params_phys-y	:= 0x30000100

所以启动地址和装载地址都是0x30008000;之前mkImage参数有-C none,标记的是未压缩内核(因为makefile生成zImage是一个已经压缩了的内核),这个未压缩内核的标记只是给uboot看的,这样uboot就不会去解压了,而把解压工作交给内核自解压,看uboot代码判断:

switch (hdr->ih_comp) {
	case IH_COMP_NONE:    //未压缩内核
		if(ntohl(hdr->ih_load) == addr) {
			printf ("   XIP %s ... ", name);
		} else {
			memmove ((void *) ntohl(hdr->ih_load), (uchar *)data, len);
		}
		break;
	case IH_COMP_GZIP:    //zip压缩型内核,内核解压到加载地址 
		printf ("   Uncompressing %s ... ", name);
		if (gunzip ((void *)ntohl(hdr->ih_load), unc_len,
			    (uchar *)data, &len) != 0) {
			puts ("GUNZIP ERROR - must RESET board to recover\n");
			SHOW_BOOT_PROGRESS (-6);
			do_reset (cmdtp, flag, argc, argv);
		}
		break;

1 如果nand read 下载到sdram的下载地址和bootm地址相同,那么这个地址对应的uImage的头部起始;

这里如果是未压缩内核则判断bootm地址是否和装载地址一致,如果一致则不移动内核数据到装载地址,如果不一致则移动,也就是说 在这个条件下

如果bootm地址 = 装载地址 ,那么 入口地址=装载地址-0x40(64字节头部)

如果bootm地址!=装载地址,那么入口地址=装载地址

而上面分析Makefile可知道入口地址和装载地址是相等的,那么必须满足:

下载地址 = bootm地址 != 装载地址 = 入口地址

2 如果nand read 下载到sdram的地址和bootm地址不同;假设nand read 下载到sdram的地址为0x30007fc0,而bootm地址为0x30008000,那么这个bootm地址刚好对应的是内核起始;此时bootm地址 = 装载地址,那么内核数据不移动;那么必须满足:

下载地址 +0x40 = bootm地址 = 装载地址 入口地址







  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值