grub-mkimage源码分析---3

grub-mkimage源码分析—3

上一章分析了如何将kernel.img文件以及各个的模块和对应的依赖模块读取并写入内存映像kernel_img中,本章分析如何将kernel_img中的数据以及解压缩程序写入最终的core.img文件中。

grub_install_generate_image第四部分
util/mkimage.c

    ...

    compress_kernel (image_target, kernel_img, layout.kernel_size + total_module_size,
           &core_img, &core_size, comp);
    free (kernel_img);

    if (image_target->flags & PLATFORM_FLAGS_DECOMPRESSORS) {
        char *full_img;
        size_t full_size;
        char *decompress_path, *decompress_img;
        const char *name;

        switch (comp) {
        case GRUB_COMPRESSION_XZ:
            name = "xz_decompress.img";
            break;
        case GRUB_COMPRESSION_LZMA:
            name = "lzma_decompress.img";
            break;
        case GRUB_COMPRESSION_NONE:
            name = "none_decompress.img";
            break;
        default:
            grub_util_error (_("unknown compression %d"), comp);
        }

        decompress_path = grub_util_get_path (dir, name);
        decompress_size = grub_util_get_image_size (decompress_path);
        decompress_img = grub_util_read_image (decompress_path);

        if ((image_target->id == IMAGE_I386_PC
            || image_target->id == IMAGE_I386_PC_PXE
            || image_target->id == IMAGE_I386_PC_ELTORITO)
            && decompress_size > GRUB_KERNEL_I386_PC_LINK_ADDR - 0x8200)
            grub_util_error ("%s", _("Decompressor is too big"));

        *((grub_uint32_t *) (decompress_img + image_target->decompressor_compressed_size))
            = grub_host_to_target32 (core_size);

        *((grub_uint32_t *) (decompress_img + image_target->decompressor_uncompressed_size))
            = grub_host_to_target32 (layout.kernel_size + total_module_size);

        full_size = core_size + decompress_size;
        full_img = xmalloc (full_size);

        memcpy (full_img, decompress_img, decompress_size);
        memcpy (full_img + decompress_size, core_img, core_size);

        free (core_img);
        core_img = full_img;
        core_size = full_size;
        free (decompress_img);
        free (decompress_path);
    }

    ...

首先通过compress_kernel函数将内核映像kernel_img压缩成core_img,返回的core_size中保存了core_img映像的大小,compress_kernel函数往下涉及到压缩方面的知识,这里就不往下分析了。传入的参数comp默认为GRUB_COMPRESSION_LZMA,因此解压缩模块的名称为lzma_decompress.img。lzma_decompress.img也由grub自身编译而成,查看grub-core下的makefile文件可知该文件由boot/i386/pc/startup_raw.S文件编译而来。

接下来计算lzma_decompress.img文件所在路径decompress_path,文件大小decompress_size,并通过grub_util_read_image 函数将该文件载入内存decompress_img。
对于i386而言,lzma_decompress.img文件的大小不能大于0x9000减去0x8200的值,其中0x8200是最终core.img的装载起始地址,头部就是lzma_decompress.img文件对应的映像,0x9000是core.img中kernel.img的装载起始地址,因此lzma_decompress.img的大小不能大于两者之差。

再往下,在lzma_decompress.img文件对应的内存映像decompress_img中设置压缩文件的大小core_size,以及解压缩后文件的大小layout.kernel_size + total_module_size,可以查看对应的源文件startup_raw.S,在该文件的头部预留了该空间。对应的文件偏移decompressor_compressed_size和decompressor_uncompressed_size分别由下列宏定义确定,

#define GRUB_DECOMPRESSOR_I386_PC_COMPRESSED_SIZE   0x08
#define GRUB_DECOMPRESSOR_I386_PC_UNCOMPRESSED_SIZE 0x0c

准备工作完成后,接下来要合并两个文件,full_size计算压缩映像core_img和解压缩映像decompress_img大小的和,然后分配该大小的内存空间,并通过memcpy依次存入映像decompress_img和core_img,最后更新core_img和core_size的值。

grub_install_generate_image第五部分
util/mkimage.c

    ...

    switch (image_target->id) {
    case IMAGE_I386_PC:
    case IMAGE_I386_PC_PXE:
    case IMAGE_I386_PC_ELTORITO:
        unsigned num;
        char *boot_path, *boot_img;
        size_t boot_size;

        num = ((core_size + GRUB_DISK_SECTOR_SIZE - 1) >> GRUB_DISK_SECTOR_BITS);

        boot_path = grub_util_get_path (dir, "diskboot.img");
        boot_size = grub_util_get_image_size (boot_path);
        if (boot_size != GRUB_DISK_SECTOR_SIZE)
            grub_util_error ();

        boot_img = grub_util_read_image (boot_path);


        struct grub_pc_bios_boot_blocklist *block;
        block = (struct grub_pc_bios_boot_blocklist *) (boot_img
                              + GRUB_DISK_SECTOR_SIZE
                              - sizeof (*block));
        block->len = grub_host_to_target16 (num);


        grub_util_write_image (boot_img, boot_size, out, outname);
        free (boot_img);
        free (boot_path);
        break;

    ...

    }

    grub_util_write_image (core_img, core_size, out, outname);

    free (core_img);
    free (kernel_path);
    free (layout.reloc_section);
    grub_util_free_path_list (path_list);
}

首先计算core_img大小对应的扇区数num,然后获取diskboot.img文件路径boot_path,获取该文件大小boot_size,其必须为一个扇区的大小GRUB_DISK_SECTOR_BITS,默认为512字节,接着读取该文件至内存boot_img,
num为扇区数,向上取整。GRUB_DISK_SECTOR_BITS表示每个扇区的字节数,为512字节。然后设置diskboot.img文件中block列表,最后一项block的字段len(对应diskboot.S文件的blocklist_default_len标号处)设置为core_img对应的扇区大小num。

再往下通过grub_util_write_image将该diskboot.img文件对应的内存映像写入out中,最后继续调用grub_util_write_image将core_img也写入out中。

diskboot.img由boot/i386/pc/diskboot.S编译而来,由第一扇区的boot.S源码对应的内存映像负责装入内存。

总结

这里简单总结一下grub-mkimage生成的文件core.img的结构,首先是diskboot.img,接下来是解压缩程序lzma_decompress.img,再往下是kernel.img,最后是各个模块module对应的映像。

下面其实也可以猜到计算机启动后是怎么加载这些文件的了,首先读取第一扇区的boot.img,该文件处的某处一定保存了整个core.img的起始扇区,然后从该起始扇区处首先读取一个扇区,即diskboot.img,该映像的结尾处保存了后续映像的长度,根据该长度读取这些数据,数据的头部即lzma_decompress.img映像又保存了加压缩的参数,例如压缩文件的大小,以及解压缩后文件的大小,根据这些参数对后续数据进行解压缩,得到kernel.img以及各个module模块的映像起始地址,最后进入kernel.img的入口函数继续执行。

你可能感兴趣的:(glibc+linux源码分析,linux逆向编程)