栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 系统运维 > 运维 > Linux

linux内核压缩制作bzImage

Linux 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

linux内核压缩制作bzImage

接着上一篇<>继续分析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.h

vmlinux-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_rel
        for 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(0x1,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 bzImage
                usage();
        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]);

        
        file = fopen(argv[1], "r"); 
        if (!file)
                die("Unable to open `%s': %m", argv[1]);




        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表示没有启动标志


 c += reserve_pecoff_compat_section(c); //为.compat段预留0x20个字节,setup.bin之后的空间
        c += reserve_pecoff_reloc_section(c); //为.reloc段预留0x20个字节

        
        setup_sectors = (c + 511) / 512;   //计算占用了多少个扇区空间大小



        if (setup_sectors < SETUP_SECT_MIN) //如果占用扇区空间小于5,设置为5
                setup_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段


 
 
        put_unaligned_le16(DEFAULT_ROOT_DEV, &buf[508]);
        //509-510字节设置root路径

        printf("Setup is %d bytes (padded to %d bytes).n", c, i);

        
        fd = open(argv[2], O_RDONLY); //只读方式打开vmlinux.bin
        if (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 kBn", (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. 映射只读内存

 
        sys_size = (sz + 15 + 4) / 16; //计算有多少个16字节,包括4字节CRC
#ifdef CONFIG_EFI_STUB
        
        sys_size = (sys_size + 1) & ~1;
#endif

        
        buf[0x1f1] = setup_sectors-1; //设置setup(扇区)长度
        put_unaligned_le32(sys_size, &buf[0x1f4]); 

        init_sz = get_unaligned_le32(&buf[0x260]);
#ifdef CONFIG_EFI_STUB
        

        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入口地址
        put_unaligned_le32(kernel_info, &buf[0x268]);

        crc = partial_crc32(buf, i, crc);
        if (fwrite(buf, 1, i, dest) != i)
                die("Writing setup failed");

        
        crc = partial_crc32(kernel, sz, crc);
        if (fwrite(kernel, 1, sz, dest) != sz)
                die("Writing kernel failed");

        
        while (sz++ < (sys_size*16) - 4) {
                crc = partial_crc32_one('', crc);
                if (fwrite("", 1, 1, dest) != 1)
                        die("Writing padding failed");
        }

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

 
        printf("CRC %xn", crc);
        put_unaligned_le32(crc, buf);
        if (fwrite(buf, 1, 4, dest) != 4)
                die("Writing CRC failed");

        
        if (fclose(dest))
                die("Writing image failed");

        close(fd);

        
        return 0;

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


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

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/741486.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号