接着上一篇<<linux内核启动过程>>继续分析setup.bin和vmlinux.bin执行过程。

setup.bin

OBJCOPYFLAGS_setup.bin  := -O binary
$(obj)/setup.bin: $(obj)/setup.elf FORCE$(call if_changed,objcopy)

call if_changed,objcopy这里找到arch/x86/boot/.setup.elf.cmd文件:

cmd_arch/x86/boot/setup.elf := ld -m elf_x86_64   -m elf_i386 -T arch/x86/boot/setup.ld arch/x86/boot/a20.o arch/x86/boot/bioscall.o arch/x86/boot/cmdline.o arch/x86/boot/copy.o arch/x86/boot/cpu.o arch/x86/boot/cpuflags.o arch/x86/boot/cpucheck.o arch/x86/boot/early_serial_console.o arch/x86/boot/edd.o arch/x86/boot/header.o arch/x86/boot/main.o arch/x86/boot/memory.o arch/x86/boot/pm.o arch/x86/boot/pmjump.o arch/x86/boot/printf.o arch/x86/boot/regs.o arch/x86/boot/string.o arch/x86/boot/tty.o arch/x86/boot/video.o arch/x86/boot/video-mode.o arch/x86/boot/version.o arch/x86/boot/video-vga.o arch/x86/boot/video-vesa.o arch/x86/boot/video-bios.o -o arch/x86/boot/setup.elf

这里通过ld链接生成arch/x86/boot/setup.elf文件,其中.o文件是main函数中用到函数及依赖函数、变量等生成的二进制文件。

vmlinux.bin

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

vmlinux.bin依赖于vmlinux,查看vmlinux分支:

$(obj)/compressed/vmlinux: FORCE$(Q)$(MAKE) $(build)=$(obj)/compressed $@

查看arch/x86/boot/compressed/vmlinux/Makefile:

# create a compressed vmlinux image from the original vmlinux
#
# vmlinuz is:
#       decompression code (*.o)
#       asm globals (piggy.S), including:
#               vmlinux.bin.(gz|bz2|lzma|...)
#
# vmlinux.bin is:
#       vmlinux stripped of debugging and comments
# vmlinux.bin.all is:
#       vmlinux.bin + vmlinux.relocs
# vmlinux.bin.(gz|bz2|lzma|...) is:
#       (see scripts/Makefile.lib size_append)
#       compressed vmlinux.bin.all + u32 size of vmlinux.bin.all# Sanitizer runtimes are unavailable and cannot be linked for early boot code.
KASAN_SANITIZE                  := n
KCSAN_SANITIZE                  := n
OBJECT_FILES_NON_STANDARD       := y...KBUILD_LDFLAGS := -m elf_$(UTS_MACHINE)...hostprogs       := mkpiggy
HOST_EXTRACFLAGS += -I$(srctree)/tools/include...$(obj)/../voffset.h: vmlinux FORCE$(call if_changed,voffset)$(obj)/misc.o: $(obj)/../voffset.hvmlinux-objs-$(CONFIG_EFI_MIXED) += $(obj)/efi_thunk_$(BITS).o
efi-obj-$(CONFIG_EFI_STUB) = $(objtree)/drivers/firmware/efi/libstub/lib.a# The compressed kernel is built with -fPIC/-fPIE so that a boot loader
# can place it anywhere in memory and it will still run. However, since
# it is executed as-is without any ELF relocation processing performed
# (and has already had all relocation sections stripped from the binary),
# none of the code can use data relocations (e.g. static assignments of
# pointer values), since they will be meaningless at runtime. This check
# will refuse to link the vmlinux if any of these relocations are found.
quiet_cmd_check_data_rel = DATAREL $@
define cmd_check_data_relfor obj in $(filter %.o,$^); do \$(READELF) -S $$obj | grep -qF .rel.local && { \echo "error: $$obj has data relocations!" >&2; \exit 1; \} || true; \done
endef$(obj)/vmlinux: $(vmlinux-objs-y) $(efi-obj-y) FORCE$(call if_changed,check-and-link-vmlinux)OBJCOPYFLAGS_vmlinux.bin :=  -R .comment -S
$(obj)/vmlinux.bin: vmlinux FORCE$(call if_changed,objcopy)$(obj)/vmlinux.relocs: vmlinux FORCE$(call if_changed,relocs)vmlinux.bin.all-y := $(obj)/vmlinux.bin
vmlinux.bin.all-$(CONFIG_X86_NEED_RELOCS) += $(obj)/vmlinux.relocs$(obj)/vmlinux.bin.gz: $(vmlinux.bin.all-y) FORCE //默认配置只开启gzip$(call if_changed,gzip)
$(obj)/vmlinux.bin.bz2: $(vmlinux.bin.all-y) FORCE$(call if_changed,bzip2)
$(obj)/vmlinux.bin.lzma: $(vmlinux.bin.all-y) FORCE$(call if_changed,lzma)
$(obj)/vmlinux.bin.xz: $(vmlinux.bin.all-y) FORCE$(call if_changed,xzkern)
$(obj)/vmlinux.bin.lzo: $(vmlinux.bin.all-y) FORCE$(call if_changed,lzo)
$(obj)/vmlinux.bin.lz4: $(vmlinux.bin.all-y) FORCE$(call if_changed,lz4)
$(obj)/vmlinux.bin.zst: $(vmlinux.bin.all-y) FORCE$(call if_changed,zstd22)...quiet_cmd_mkpiggy = MKPIGGY $@cmd_mkpiggy = $(obj)/mkpiggy $< > $@targets += piggy.S
$(obj)/piggy.S: $(obj)/vmlinux.bin.$(suffix-y) $(obj)/mkpiggy FORCE$(call if_changed,mkpiggy)

首先执行$(obj)/…/voffset.h:

nm vmlinux | sed -n -e 's/^\([0-9a-fA-F]*\) [ABCDGRSTVW] \(_text\|__bss_start\|_end\)$$/$(pound)define VO_\2 _AC(0x\1,UL)/p' > arch/x86/boot/compressed/../voffset.h#define VO___bss_start _AC(0xffffffff81c0c000,UL)
#define VO__end _AC(0xffffffff81e2c000,UL)
#define VO__text _AC(0xffffffff81000000,UL)

从vmlinux中拿到VO___bss_start _AC、VO__end _AC和VO__text _AC地址

objcopy  -O binary -R .note -R .comment -S arch/x86/boot/compressed/vmlinux arch/x86/boot/vmlinux.bin

vmlinux.bin:由vmlinux生成二进制文件vmlinux.bin(去除.note和.comment段内容)

$(obj)/vmlinux.relocs: vmlinux FORCE$(call if_changed,relocs)

生成arch/x86/boot/compressed/vmlinux.relocs

$(obj)/vmlinux.bin.gz: $(vmlinux.bin.all-y) FORCE$(call if_changed,gzip)

.config默认开启gzip:

cat arch/x86/boot/compressed/vmlinux.bin arch/x86/boot/compressed/vmlinux.relocs | gzip -n -f -9 > arch/x86/boot/compressed/vmlinux.bin.gz

使用gzip(最高压缩比)把vmlinux.bin和vmlinux.relocs压缩到vmlinux.bin.gz

$(obj)/piggy.S: $(obj)/vmlinux.bin.$(suffix-y) $(obj)/mkpiggy FORCE$(call if_changed,mkpiggy)
gcc -Wp,-MMD,arch/x86/boot/compressed/.mkpiggy.d -Wall -Wmissing-prototypes -Wstrict-prototypes -O2 -fomit-frame-pointer -std=gnu89     -I./tools/include    -o arch/x86/boot/compressed/mkpiggy arch/x86/boot/compressed/mkpiggy.c

生成mkpiggy工具

quiet_cmd_check-and-link-vmlinux = LD      $@cmd_check-and-link-vmlinux = $(cmd_check_data_rel); $(cmd_ld)$(obj)/vmlinux: $(vmlinux-objs-y) $(efi-obj-y) FORCE$(error check-and-link-vmlinux,$(vmlinux-objs-y),$(efi-obj-y)) $(call if_changed,check-and-link-vmlinux)
for obj in ; do readelf -S $obj | grep -qF .rel.local && { echo "error: $obj has data relocations!" >&2; exit 1; } || true; done

检查是否有数据重载地址(通过.rel.local),如果有提示"error: $obj has data relocations!"

build

$(obj)/bzImage: $(obj)/setup.bin $(obj)/vmlinux.bin $(obj)/tools/build FORCE$(call if_changed,image)@$(kecho) 'Kernel: $@ is ready' ' (#'`cat .version`')'

现在开始分析build,查看arch/x86/boot/.bzImage.cmd:

arch/x86/boot/tools/build arch/x86/boot/setup.bin arch/x86/boot/vmlinux.bin arch/x86/boot/zoffset.h arch/x86/boot/bzImage

这里执行build setup.bin vmlinux.bin zoffset.h bzImage,进入arch/x86/boot/tools/build.c中的main函数:

int main(int argc, char ** argv)
{unsigned int i, sz, setup_sectors, init_sz;int c;u32 sys_size;struct stat sb;FILE *file, *dest;int fd;void *kernel;u32 crc = 0xffffffffUL;efi_stub_defaults();

efi_stub_defaults中32位定义efi_pe_entry = 0x10;,64位定义efi_pe_entry = 0x210;及startup_64 = 0x200;(起始地址512字节)

if (argc != 5)        //参数不是5将显示帮助信息,build setup.bin vmlinux.bin zoffset.h bzImageusage();parse_zoffset(argv[3]);#define ZO__ehead 0x0000000000000305
#define ZO__end 0x000000000021a000
#define ZO__text 0x00000000001e5ea0
#define ZO_input_data 0x0000000000000305
#define ZO_kernel_info 0x00000000001ecce3
#define ZO_startup_32 0x0000000000000000
#define ZO_startup_64 0x0000000000000200
#define ZO_z_input_len 0x00000000001e5b90
#define ZO_z_output_len 0x0000000000e27bfc

解析zoffset.h中的定义地址,上述列表进行对应项赋值

dest = fopen(argv[4], "w");if (!dest)die("Unable to write `%s': %m", argv[4]);/* Copy the setup code */file = fopen(argv[1], "r"); /* 读方式打开 setup.bin */if (!file)die("Unable to open `%s': %m", argv[1]);/* This must be large enough to hold the entire setup */
/* u8 buf[SETUP_SECT_MAX*512];  64 * 512  32K*/c = fread(buf, 1, sizeof(buf), file);if (ferror(file))die("read-error on `setup'");if (c < 1024)die("The setup must be at least 1024 bytes");if (get_unaligned_le16(&buf[510]) != 0xAA55)die("Boot block hasn't got boot flag (0xAA55)");fclose(file);

读取setup.bin内容,必须大于1024字节(最大32K),如果第511-512字节不是0xAA55表示没有启动标志

/*
#define PECOFF_RELOC_RESERVE 0x20#ifdef CONFIG_EFI_MIXED
#define PECOFF_COMPAT_RESERVE 0x20
#else
#define PECOFF_COMPAT_RESERVE 0x0
#endif
*/c += reserve_pecoff_compat_section(c); //为.compat段预留0x20个字节,setup.bin之后的空间c += reserve_pecoff_reloc_section(c); //为.reloc段预留0x20个字节/* Pad unused space with zeros */setup_sectors = (c + 511) / 512;   //计算占用了多少个扇区空间大小/*
Minimal number of setup sectors
#define SETUP_SECT_MIN 5
#define SETUP_SECT_MAX 64
*/if (setup_sectors < SETUP_SECT_MIN) //如果占用扇区空间小于5,设置为5setup_sectors = SETUP_SECT_MIN;i = setup_sectors*512;memset(buf+c, 0, i-c);  //未使用的内存空间补零(一般最后一个扇区空间未完全使用)update_pecoff_setup_and_reloc(i);//填充.setup和.reloc段,如果是IA-32架构需要额外填充.compat段

1. 读取setup.bin内容,必须大于1024字节(最大32K),如果第511-512字节不是0xAA55表示没有启动标志
  2. 为.compat段预留0x20个字节(setup.bin之后的空间),为.reloc段预留0x20个字节
  3. 计算占用了多少个扇区空间大小,预设扇区范围内未使用的内存补零
  4. 填充.setup和.reloc段,如果是IA-32架构需要额外填充.compat段


/*
#define DEFAULT_MAJOR_ROOT 0
#define DEFAULT_MINOR_ROOT 0
#define DEFAULT_ROOT_DEV (DEFAULT_MAJOR_ROOT << 8 | DEFAULT_MINOR_ROOT)*//* Set the default root device */put_unaligned_le16(DEFAULT_ROOT_DEV, &buf[508]);//509-510字节设置root路径printf("Setup is %d bytes (padded to %d bytes).\n", c, i);/* Open and stat the kernel file */fd = open(argv[2], O_RDONLY); //只读方式打开vmlinux.binif (fd < 0)die("Unable to open `%s': %m", argv[2]);if (fstat(fd, &sb))die("Unable to stat `%s': %m", argv[2]);sz = sb.st_size;printf("System is %d kB\n", (sz+1023)/1024); //输出vmlinux.bin大小kernel = mmap(NULL, sz, PROT_READ, MAP_SHARED, fd, 0); //映射只读内存if (kernel == MAP_FAILED)die("Unable to mmap '%s': %m", argv[2]);

1. 509-510字节设置root路径
  2. 只读方式打开vmlinux.bin
  3. 输出vmlinux.bin大小
  4. 映射只读内存

 /* Number of 16-byte paragraphs, including space for a 4-byte CRC */sys_size = (sz + 15 + 4) / 16; //计算有多少个16字节,包括4字节CRC
#ifdef CONFIG_EFI_STUB/** COFF requires minimum 32-byte alignment of sections, and* adding a signature is problematic without that alignment.*/sys_size = (sys_size + 1) & ~1;
#endif/* Patch the setup code with the appropriate size parameters */buf[0x1f1] = setup_sectors-1; //设置setup(扇区)长度put_unaligned_le32(sys_size, &buf[0x1f4]); init_sz = get_unaligned_le32(&buf[0x260]);
#ifdef CONFIG_EFI_STUB/** The decompression buffer will start at ImageBase. When relocating* the compressed kernel to its end, we must ensure that the head* section does not get overwritten.  The head section occupies* [i, i + _ehead), and the destination is [init_sz - _end, init_sz).** At present these should never overlap, because 'i' is at most 32k* because of SETUP_SECT_MAX, '_ehead' is less than 1k, and the* calculation of INIT_SIZE in boot/header.S ensures that* 'init_sz - _end' is at least 64k.** For future-proofing, increase init_sz if necessary.*/if (init_sz - _end < i + _ehead) {init_sz = (i + _ehead + _end + 4095) & ~4095;put_unaligned_le32(init_sz, &buf[0x260]); //向buf内写入数据长度}
#endif update_pecoff_text(setup_sectors * 512, i + (sys_size * 16), init_sz);efi_stub_entry_update();//写入efi入口地址
/* Update kernel_info offset. */put_unaligned_le32(kernel_info, &buf[0x268]);crc = partial_crc32(buf, i, crc);if (fwrite(buf, 1, i, dest) != i)die("Writing setup failed");/* Copy the kernel code */crc = partial_crc32(kernel, sz, crc);if (fwrite(kernel, 1, sz, dest) != sz)die("Writing kernel failed");/* Add padding leaving 4 bytes for the checksum */while (sz++ < (sys_size*16) - 4) {crc = partial_crc32_one('\0', crc);if (fwrite("\0", 1, 1, dest) != 1)die("Writing padding failed");}

写入kernel_info地址(按顺序写入),写入内核编码,为校验和留下4个字节,剩下未使用的内存空间填充零

 /* Write the CRC */printf("CRC %x\n", crc);put_unaligned_le32(crc, buf);if (fwrite(buf, 1, 4, dest) != 4)die("Writing CRC failed");/* Catch any delayed write failures */if (fclose(dest))die("Writing image failed");close(fd);/* Everything is OK */return 0;

写入CRC校验,关闭fd,退出main函数

build执行过程:
    1. 创建bzImage文件;
    2. 写入数据头部;
    3. 写入setup.bin和vmlinux.bin数据;
    4. 写入efi入口;
    5. 写入kernel_info地址等数据和CRC4字节校验;
    6. 关闭文件描述符,退出main函数。
    部分细节(如各种段赋值,固定字节写入特定信息)参考代码中添加的注释信息,到这里bzImage创建流程执行完成。

linux内核压缩制作bzImage相关推荐

  1. (二)linux内核镜像制作

    (一)目的 在进行嵌入式开发的时候,我们往往会先在电脑上安装交叉编译器,然后编译目标板上的代码,最后把代码下载到电路板中,嵌入式系统组成包括:BootLoader+kernel+filesystem+ ...

  2. linux内核 绿皮书,制作Linux的U盘启动盘

    转载自互联网: USB闪存存储装置(优盘)一般只有钥匙扣大小,存储容量多为32MB和64MB.由于具有轻巧精致.使用方便.便于携带等优点,特别是对比软盘具有容量较大.安全可靠.读取速度较快等特性,越来 ...

  3. linux内核启动过程5:启动用户空间

    上一篇<<linux内核启动过程4:内核运行时>>分析到了内核进入运行时状态(不退出),本篇分析用户空间(用户层)的加载过程. 启动应用空间 进入kernel_init函数,在 ...

  4. linux内核启动过程2:保护模式执行流程

    上一篇<<linux内核压缩制作bzImage>>分析了bzImage制作流程,本篇继续分析内核启动过程,从实模式跳转到保护模式及后续执行流程. protected_mode_ ...

  5. Linux内核开发_将Linux内核打包成img文件

    接着之前两个文章我们已经编译了Linux内核和制作了一个文件系统 这一步我们将它们打包成一个img软盘文件 1.使用DD命令创建一个镜像的img文件 sudo dd if=/dev/zero of=m ...

  6. linux内核优盘配置,mini2440建立交叉编译环境+配置linux内核

    mini2440建立交叉编译环境+配置linux内核 来源:IT165收集  发布日期:2014-09-22 20:37:16 (mini2440)建立交叉编译环境+配置linux内核 系统ubunt ...

  7. Linux内核文件vmlinux 和压缩后的bzImage文件格式分析

    Linux内核文件vmlinux 和压缩后的bzImage文件格式分析 ================= 1. 需要使用的命令 ================ readelf    -- 显示el ...

  8. linux内核做界面,基于linux内核制作linux系统

    作者:哈尔滨师范大学  计算机学院 2013级 学生 刘聘婷 环境和工具: 编译环境: Ubuntu 15.04 工具:Buildroot-2015.08.1 (http://buildroot.uc ...

  9. linux 内核移植和根文件系统的制作【转载】

    原文地址:http://www.cnblogs.com/hnrainll/archive/2011/06/09/2076214.html 1.1 Linux内核基础知识 在动手进行Linux内核移植之 ...

最新文章

  1. 为什么程序员都不写文档?
  2. 动态追踪技术思想及应用
  3. java中Map的用法(HaspMap用法)
  4. Qt Creator与调试器进行交互
  5. 1000 驱动_华为海思自研OLED驱动芯片已流片:最高28nm、可完全去美化
  6. Spark _02SparkCore_RDD
  7. 使用CmakeLists应该知道的一些知识
  8. 哇靠靠,这也行?零基础DIY无人驾驶小车(三)
  9. c 指针打印变量_C程序打印不同类型的指针变量的大小。
  10. oracle之trunc(sysdate)
  11. find、grep,which、whereis
  12. 2017.10.24 上升序列 思考记录
  13. 1.5多媒体技术的应用领域
  14. 计算机网络之广域网基础以及华为eNSP实验配置
  15. idea繁体字-中文输入法变繁体字
  16. 分节符、分页符及页眉设置方法
  17. 6.29--6.30郭天祥老师课程中的一些错误与我的存疑
  18. App推广渠道统计二维码扫码统计 - Xinstall
  19. YTU----1329: 手机尾号评分
  20. Bootstrap相关整理、免费主题整理

热门文章

  1. 中国最大字幕组“人人影视”凉了!警方通报:因盗版视频14人被抓
  2. 小白都能看懂的缓存入门
  3. 第一批90后已经30岁了,更扎心的是…
  4. JS中的防抖跟节流(DebounceThrottle)
  5. jeecg怎么样好用吗?
  6. jeewx 微信管家 - 举办商业版本免费试用活动
  7. java虚拟机参数详解
  8. Python DbUtil操作数据
  9. 辟谣|bitcoin core0.16.1版本没有删除区块大小限制——带你认识区块大小限制参数...
  10. Flex 4 NativeWindow 中添加Flex组件(问题很多,尚不完善)