linux内核压缩制作bzImage
接着上一篇<<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相关推荐
- (二)linux内核镜像制作
(一)目的 在进行嵌入式开发的时候,我们往往会先在电脑上安装交叉编译器,然后编译目标板上的代码,最后把代码下载到电路板中,嵌入式系统组成包括:BootLoader+kernel+filesystem+ ...
- linux内核 绿皮书,制作Linux的U盘启动盘
转载自互联网: USB闪存存储装置(优盘)一般只有钥匙扣大小,存储容量多为32MB和64MB.由于具有轻巧精致.使用方便.便于携带等优点,特别是对比软盘具有容量较大.安全可靠.读取速度较快等特性,越来 ...
- linux内核启动过程5:启动用户空间
上一篇<<linux内核启动过程4:内核运行时>>分析到了内核进入运行时状态(不退出),本篇分析用户空间(用户层)的加载过程. 启动应用空间 进入kernel_init函数,在 ...
- linux内核启动过程2:保护模式执行流程
上一篇<<linux内核压缩制作bzImage>>分析了bzImage制作流程,本篇继续分析内核启动过程,从实模式跳转到保护模式及后续执行流程. protected_mode_ ...
- Linux内核开发_将Linux内核打包成img文件
接着之前两个文章我们已经编译了Linux内核和制作了一个文件系统 这一步我们将它们打包成一个img软盘文件 1.使用DD命令创建一个镜像的img文件 sudo dd if=/dev/zero of=m ...
- linux内核优盘配置,mini2440建立交叉编译环境+配置linux内核
mini2440建立交叉编译环境+配置linux内核 来源:IT165收集 发布日期:2014-09-22 20:37:16 (mini2440)建立交叉编译环境+配置linux内核 系统ubunt ...
- Linux内核文件vmlinux 和压缩后的bzImage文件格式分析
Linux内核文件vmlinux 和压缩后的bzImage文件格式分析 ================= 1. 需要使用的命令 ================ readelf -- 显示el ...
- linux内核做界面,基于linux内核制作linux系统
作者:哈尔滨师范大学 计算机学院 2013级 学生 刘聘婷 环境和工具: 编译环境: Ubuntu 15.04 工具:Buildroot-2015.08.1 (http://buildroot.uc ...
- linux 内核移植和根文件系统的制作【转载】
原文地址:http://www.cnblogs.com/hnrainll/archive/2011/06/09/2076214.html 1.1 Linux内核基础知识 在动手进行Linux内核移植之 ...
最新文章
- 为什么程序员都不写文档?
- 动态追踪技术思想及应用
- java中Map的用法(HaspMap用法)
- Qt Creator与调试器进行交互
- 1000 驱动_华为海思自研OLED驱动芯片已流片:最高28nm、可完全去美化
- Spark _02SparkCore_RDD
- 使用CmakeLists应该知道的一些知识
- 哇靠靠,这也行?零基础DIY无人驾驶小车(三)
- c 指针打印变量_C程序打印不同类型的指针变量的大小。
- oracle之trunc(sysdate)
- find、grep,which、whereis
- 2017.10.24 上升序列 思考记录
- 1.5多媒体技术的应用领域
- 计算机网络之广域网基础以及华为eNSP实验配置
- idea繁体字-中文输入法变繁体字
- 分节符、分页符及页眉设置方法
- 6.29--6.30郭天祥老师课程中的一些错误与我的存疑
- App推广渠道统计二维码扫码统计 - Xinstall
- YTU----1329: 手机尾号评分
- Bootstrap相关整理、免费主题整理
热门文章
- 中国最大字幕组“人人影视”凉了!警方通报:因盗版视频14人被抓
- 小白都能看懂的缓存入门
- 第一批90后已经30岁了,更扎心的是…
- JS中的防抖跟节流(DebounceThrottle)
- jeecg怎么样好用吗?
- jeewx 微信管家 - 举办商业版本免费试用活动
- java虚拟机参数详解
- Python DbUtil操作数据
- 辟谣|bitcoin core0.16.1版本没有删除区块大小限制——带你认识区块大小限制参数...
- Flex 4 NativeWindow 中添加Flex组件(问题很多,尚不完善)