本章记录grub2中的boot.img是如何生成的,分三部分看。
第一部分-boot_image_OBJECTS
boot_image_OBJECTS
grub-core/Makefile
boot_image_OBJECTS = $(am_boot_image_OBJECTS) \
$(nodist_boot_image_OBJECTS)
am_boot_image_OBJECTS = \
boot/i386/pc/boot_image-boot.$(OBJEXT)
nodist_boot_image_OBJECTS定义空。因此boot_image_OBJECTS只依赖于am_boot_image_OBJECTS。变量OBJEXT为o,boot/i386/pc/boot_image-boot.$(OBJEXT)有两个依赖关系,下面依次来看。
第一个依赖boot_image-boot.$(OBJEXT)
boot/i386/pc/boot_image-boot.$(OBJEXT)
grub-core/Makefile
boot/i386/pc/boot_image-boot.$(OBJEXT): boot/i386/pc/$(am__dirstamp) \
boot/i386/pc/$(DEPDIR)/$(am__dirstamp)
其中的变量定义如下,
am__dirstamp = $(am__leading_dot)dirstamp
am__leading_dot = .
DEPDIR = .deps-core
再往下看,
boot/i386/pc/$(am__dirstamp):
@$(MKDIR_P) boot/i386/pc
@: > boot/i386/pc/$(am__dirstamp)
@表示不向控制台输出信息。MKDIR_P就是创建目录命令。
MKDIR_P = /bin/mkdir -p
-p选项表示可以创建子目录。因此该依赖就是在boot/i386/pc/目录下创建.dirstamp文件。
同理,第二个依赖如下,
boot/i386/pc/$(DEPDIR)/$(am__dirstamp):
@$(MKDIR_P) boot/i386/pc/$(DEPDIR)
@: > boot/i386/pc/$(DEPDIR)/$(am__dirstamp)
该依赖就是在boot/i386/pc/.deps-core目录下创建.dirstamp文件。
因为两个am__dirstamp没有依赖,因此创建.dirstamp文件的目的是保证文件夹boot/i386/pc/或boot/i386/pc/.deps-core/始终存在。
第二个依赖boot_image-boot.o
boot/i386/pc/boot_image-boot.o
grub-core/Makefile
boot/i386/pc/boot_image-boot.o: boot/i386/pc/boot.S
$(AM_V_CPPAS)$(CCAS) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(boot_image_CPPFLAGS) $(CPPFLAGS) $(boot_image_CCASFLAGS) $(CCASFLAGS) -MT boot/i386/pc/boot_image-boot.o -MD -MP -MF boot/i386/pc/$(DEPDIR)/boot_image-boot.Tpo -c -o boot/i386/pc/boot_image-boot.o `test -f 'boot/i386/pc/boot.S' || echo '$(srcdir)/'`boot/i386/pc/boot.S
$(AM_V_at)$(am__mv) boot/i386/pc/$(DEPDIR)/boot_image-boot.Tpo boot/i386/pc/$(DEPDIR)/boot_image-boot.Po
boot_image-boot.o由boot.S文件编译成。
AM_V_CPPAS变量最终为空定义,CCAS变量为TARGET_CC,最终对应gcc。DEFS定义如下,
DEFS = -DHAVE_CONFIG_H
表示引入configure的头文件。
DEFAULT_INCLUDES定义了头文件的默认搜索路径,定义如下,
DEFAULT_INCLUDES = -I. -I$(top_builddir)
top_builddir = ..
注意当前路径为grub-core,因此DEFAULT_INCLUDES表示在grub-core文件夹以及根文件夹(grub-2.02)中搜索头文件。
INCLUDES为空定义,boot_image_CPPFLAGS后面分析。CPPFLAGS定义了宏_FILE_OFFSET_BITS为64。
CPPFLAGS = -D_FILE_OFFSET_BITS=64
boot_image_CCASFLAGS后面来看。CCASFLAGS定义为空
-MT指定目标文件名为boot_image-boot.o。-MD -MP -MF选项指定将依赖关系写进boot_image-boot.Tpo中。
再指定原文件名为boot.S。
AM_V_at为空定义,am__mv即mv命令。
am__mv = mv -f
表示将依赖文件boot/i386/pc/.deps-core/boot_image-boot.Tpo重命名为boot/i386/pc/.deps-core/boot_image-boot.Po文件。
boot_image_CPPFLAGS
boot_image_CPPFLAGS
grub-core/Makefile
boot_image_CPPFLAGS = $(AM_CPPFLAGS) $(CPPFLAGS_IMAGE)
AM_CPPFLAGS = $(TARGET_CPPFLAGS) $(CPPFLAGS_DEFAULT)
其中CPPFLAGS_IMAGE最终无定义,因此只看TARGET_CPPFLAGS和CPPFLAGS_DEFAULT的定义。
TARGET_CPPFLAGS
grub-core/Makefile
TARGET_CPPFLAGS = -Wall -W -DGRUB_MACHINE_PCBIOS=1 -DGRUB_MACHINE=I386_PC -m32 -nostdinc -isystem /usr/lib/gcc/x86_64-linux-gnu/4.8/include -I$(top_srcdir)/include -I$(top_builddir)/include
Wall、W两个选项表示显示所有警告。D选项设置宏定义GRUB_MACHINE_PCBIOS为1,GRUB_MACHINE为I386_PC。m32表示生成32位代码。nostdinc表示只在-I指定的目录中搜索,而不在标准系统目录中搜索头文件。
isystem选项指定的目录在-I之后查找,这里表示在最后查找,但是如果I选项指定了相同的目录,则忽略I选项提供的目录,也即将该目录放在最后查找,总之isystem选项指定的目录是搜索头文件最后的救命稻草。
接下来继续添加头文件的搜索目录,
top_builddir = ..
top_srcdir = ..
两者指向同一个目录,即根文件夹下的include目录。
CPPFLAGS_DEFAULT
grub-core/Makefile
CPPFLAGS_DEFAULT = -DGRUB_FILE=\"$(subst $(srcdir)/,,$<)\" \
-I$(builddir) -I$(srcdir) -I$(top_builddir) -I$(top_srcdir) \
-I$(top_srcdir)/include -I$(top_builddir)/include \
-I$(top_srcdir)/grub-core/lib/libgcrypt-grub/src/
首先定义了GRUB宏,其值为通过subst函数将第一个依赖$<中的所有./替换成空字符。srcdir和builddir的定义如下,根据前面的分析,这里其实最终就引入了一个新的搜索目录,即/grub-core/lib/libgcrypt-grub/src/文件夹。
srcdir=.
builddir = .
boot_image_CCASFLAGS
boot_image_CCASFLAGS
grub-core/Makefile
boot_image_CCASFLAGS = $(AM_CCASFLAGS) $(CCASFLAGS_IMAGE)
AM_CCASFLAGS = $(TARGET_CCASFLAGS) $(CCASFLAGS_DEFAULT)
CCASFLAGS_IMAGE = $(CCASFLAGS_CPU) $(CCASFLAGS_PLATFORM)
CCASFLAGS_IMAGE最终定义为空。
TARGET_CCASFLAGS = -g -m32 -msoft-float
g选项表示生成调试信息,在编译阶段生成的调试信息会在链接阶段再去掉。m32表示生成32位代码,soft-float 表示使用软件库模拟浮点运算,对应的hard-float则是直接生成浮点运算的指令,使用硬件进行浮点运算。
CCASFLAGS_DEFAULT = $(CPPFLAGS_DEFAULT) -DASM_FILE=1
CPPFLAGS_DEFAULT的定义和前面重复,最后将宏定义ASM_FILE设为1。
第二部分-boot.image
第一部分最终输出了boot_image-boot.o文件,下面来看它的链接过程。
boot.image
grub-core/Makefile
boot.image$(EXEEXT): $(boot_image_OBJECTS) $(boot_image_DEPENDENCIES) $(EXTRA_boot_image_DEPENDENCIES)
@rm -f boot.image$(EXEEXT)
$(AM_V_CCLD)$(boot_image_LINK) $(boot_image_OBJECTS) $(boot_image_LDADD) $(LIBS)
EXEEXT变量,boot_image_DEPENDENCIES和EXTRA_boot_image_DEPENDENCIES都为空定义。首先通过rm命令删除grub-core文件夹下可能已经存在的boot.image文件。
最后除了boot_image_LINK变量,剩下所有变量的定义最终都为空,下面来看boot_image_LINK的依赖关系。
boot_image_LINK
boot_image_LINK
grub-core/Makefile
boot_image_LINK = $(CCLD) $(boot_image_CFLAGS) $(CFLAGS) \
$(boot_image_LDFLAGS) $(LDFLAGS) -o $@
CCLD变量最终的定义为gcc,boot_image_CFLAGS包含了主要的链接参数,后面分析。CFLAGS和LDFLAGS都为空定义,boot_image_LDFLAGS后面分析。
下面首先来看boot_image_CFLAGS的定义:
boot_image_CFLAGS = $(AM_CFLAGS) $(CFLAGS_IMAGE)
首先看CFLAGS_IMAGE,其定义如下,
CFLAGS_IMAGE = $(CFLAGS_PLATFORM) -fno-builtin
CFLAGS_PLATFORM最终定义为空,-fno-builtin表示使用C语言的buildin函数时,需要带上前缀_builtin。AM_CFLAGS定义为TARGET_CFLAGS,如下
TARGET_CFLAGS = -Os -Wall -W -Wshadow -Wpointer-arith -Wundef -Wchar-subscripts -Wcomment -Wdeprecated-declarations -Wdisabled-optimization -Wdiv-by-zero -Wfloat-equal -Wformat-extra-args -Wformat-security -Wformat-y2k -Wimplicit -Wimplicit-function-declaration -Wimplicit-int -Wmain -Wmissing-braces -Wmissing-format-attribute -Wmultichar -Wparentheses -Wreturn-type -Wsequence-point -Wshadow -Wsign-compare -Wswitch -Wtrigraphs -Wunknown-pragmas -Wunused -Wunused-function -Wunused-label -Wunused-parameter -Wunused-value -Wunused-variable -Wwrite-strings -Wnested-externs -Wstrict-prototypes -g -Wredundant-decls -Wmissing-prototypes -Wmissing-declarations -Wextra -Wattributes -Wendif-labels -Winit-self -Wint-to-pointer-cast -Winvalid-pch -Wmissing-field-initializers -Wnonnull -Woverflow -Wvla -Wpointer-to-int-cast -Wstrict-aliasing -Wvariadic-macros -Wvolatile-register-var -Wpointer-sign -Wmissing-include-dirs -Wmissing-prototypes -Wmissing-declarations -Wformat=2 -march=i386 -m32 -mrtd -mregparm=3 -falign-jumps=1 -falign-loops=1 -falign-functions=1 -freg-struct-return -mno-mmx -mno-sse -mno-sse2 -mno-sse3 -mno-3dnow -msoft-float -fno-dwarf2-cfi-asm -mno-stack-arg-probe -fno-asynchronous-unwind-tables -fno-unwind-tables -Qn -fno-stack-protector -Wtrampolines -Werror
这里的选项太多,懒着一一看了。
再看一下boot_image_LDFLAGS的定义,
boot_image_LDFLAGS = $(AM_LDFLAGS) $(LDFLAGS_IMAGE) $(TARGET_IMG_LDFLAGS) $(TARGET_IMG_BASE_LDOPT),0x7C00
AM_LDFLAGS定义为TARGET_LDFLAGS,如下
TARGET_LDFLAGS = -m32 -Wl,-melf_i386 -Wl,--build-id=none
m32表示生成32位代码。Wl选项告诉编译器将后面的参数传递给链接器,melf_i386选项用来解决链接中兼容问题,具体不清楚。–build-id=style表示在elf文件中创建.note.gnu.build-id段,内部存储了文件标识码用来标识链接文件,–build-id=none表示忽略之前的–build-id配置。
继续看LDFLAGS_IMAGE的定义,
LDFLAGS_IMAGE = $(LDFLAGS_PLATFORM) -nostdlib $(TARGET_LDFLAGS_OLDMAGIC) -Wl,-S
LDFLAGS_PLATFORM定义为空,nostdlib表示链接过程中只使用指定的文件,不使用系统启动文件和标注库文件。
TARGET_LDFLAGS_OLDMAGIC定义如下,
TARGET_LDFLAGS_OLDMAGIC = -Wl,-N
N选项表示链接后需要把text和data段的属性设置为可读写,取消data段的对齐。
S选项表示链接后忽略输出文件中所有的调试符号信息。TARGET_IMG_LDFLAGS变量的定义和TARGET_LDFLAGS_OLDMAGIC一致。最后TARGET_IMG_BASE_LDOPT变量的定义很重要,如下
TARGET_IMG_BASE_LDOPT = -Wl,-Ttext
-Ttext 0x7c00等同于–section-start=text=0x7c00,表示将代码段放到绝对地址0x7C00上,这也是为什么当boot.img被安装在第一个扇区后,开机时,BIOS将其装载在0x7c00,并且第一条指令地址为0x7c00。
第三部分boot.img
boot.img
grub-core/Makefile
boot.img: boot.image$(EXEEXT)
if test x$(TARGET_APPLE_LINKER) = x1; then $(MACHO2IMG) $< $@; else $(TARGET_OBJCOPY) $(boot_image_OBJCOPYFLAGS) --strip-unneeded -R .note -R .comment -R .note.gnu.build-id -R .MIPS.abiflags -R .reginfo -R .rel.dyn -R .note.gnu.gold-version -R .ARM.exidx $< $@; fi
TARGET_APPLE_LINKER在x86下定义为0,因此只看else部分。TARGET_OBJCOPY变量定义为objcopy指令,boot_image_OBJCOPYFLAGS的定义如下,
boot_image_OBJCOPYFLAGS = $(OBJCOPYFLAGS_IMAGE) -O binary
OBJCOPYFLAGS_IMAGE为空定义,O选项表示使用指定的格式来写输出目标文件,binary表示生成的是二进制文件。–strip-unneeded用于删除所有重定位中不需要的符号信息,R选项表示移除对应名字的section。最终将boot.image文件修改为boot.img文件。
总结
第一步:根据boot.S生成boot_image-boot.o文件。
首先在对应目录下创建.dirstamp文件,然后将boot.S汇编文件编译成boot_image-boot.o,在编译过程中,将依赖关系写进boot_image-boot.Po文件中。
第二步:将boot_image-boot.o文件链接成boot.image文件。
链接选项中最重要的是将代码段定义在内存的绝对地址0x7C00上。
第三步:利用objcopy命令根据boot.image生成boot.img文件。
其中利用-R参数删除一些不必要的section。