----------------------------------------- JOS' makefile 的分析
------------------------------------------------------
得到源码后要做的第一件事情就是编译出kernel,运行make即可。
那么,make调用了哪些命令,提供了什么参数,生成了什么最终文件呢?那就需要看看Makefile的内容了。
lab1中与make相关的一共3个文件,分别是主目录下的GNUMakefile,kern/Makefrag 和
boot/Makefrag;由于“Recursive Make Considered
Harmful”,所以用的乃非递归方面的make,后二者用include的方式直接包含到GNUMakefile之中。
因此,为了便于分析,将GNUMakefile中的119和120行分别用相应的文件内容替换之,得到一个308行的GNUMakefile,以下分析将针对该文件进行。
line 10:-include conf/lab.mk,在文件lab.mk中定义了几个常量;而line
12里则定义了另外几个常量,用来控制输出的详细程度(V =
@),当qemu按照在非标准路径时提供qemu的具体路径等等。而一般都是用的x86的PC编译,因此不会定义GCCPREFIX,则line
30-41会赋值为GCCPREFIX=""或者GCCPREFIX="elf32-i386".
类似的,line 45-56赋值给QEMU,在make qemu/qemu-nox/...的时候需要用到这一变量。line
59定义了GDBPORT, line 61-63定义了QEMUGDB参数;三者的值可以通过make print-qemu和make
print-gdbport以及make print-qemugdb得到(line 262-269)。
line 81: -MD是什么意思呢?先了解一下gcc的-M参数,它的意思是:生成文件关联的信息。包含目标文件所依赖的所有源代码。假如有一个hello.c文件,内容为:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
printf("Hello world\n");
return 0;
}
那么调用gcc -M hello.c, 则会得到类似如下输出:
hello.o: hello.c /usr/include/stdio.h /usr/include/features.h \
/usr/include/bits/predefs.h /usr/include/sys/cdefs.h \
/usr/include/bits/wordsize.h /usr/include/gnu/stubs.h \
/usr/include/gnu/stubs-32.h \
/usr/lib/gcc/i486-linux-gnu/4.3.5/include/stddef.h \
/usr/include/bits/types.h /usr/include/bits/typesizes.h \
/usr/include/libio.h /usr/include/_G_config.h /usr/include/wchar.h \
/usr/lib/gcc/i486-linux-gnu/4.3.5/include/stdarg.h \
/usr/include/bits/stdio_lim.h /usr/include/bits/sys_errlist.h \
/usr/include/stdlib.h /usr/include/sys/types.h /usr/include/time.h \
/usr/include/endian.h /usr/include/bits/endian.h \
/usr/include/bits/byteswap.h /usr/include/sys/select.h \
/usr/include/bits/select.h /usr/include/bits/sigset.h \
/usr/include/bits/time.h /usr/include/sys/sysmacros.h \
/usr/include/bits/pthreadtypes.h /usr/include/alloca.h
而-MD的意思是生成hello.d文件,其内容就是上面的输出。显然,这个参数的作用是可以生成源文件的依赖,供make使用。
如果运行make编译出了kernel,可以发现在obj/kern和obj/boot目录下有一些XXX.d文件,就是因为-MD而生成的。
line 82: -gstabs:
在glibc的手册上说,关于gdb的-g参数:Produce debugging information in the operating
system’s native format (stabs, COFF, XCOFF, or DWARF 2). GDB can work
with this debugging information. On most systems that use stabs
format, ‘-g’ enables use of extra debugging information that only GDB
can use; this extra information makes debugging work better in GDB but
will probably make other debuggers crash or refuse to read the
program. If you want to control for certain whether to generate the
extra information, use ‘-gstabs+’, ‘-gstabs’, ‘-gxcoff+’, ‘-gxcoff’,
or ‘-gvms’ (see below).
-gstabs Produce debugging information in stabs format (if that is
supported), without GDB extensions. This is the format used by DBX on
most BSD systems.
line 82: -m32/-m64: Generate code for a 32-bit or 64-bit environment.
The 32-bit environment sets int, long and pointer to 32 bits and
generates code that runs on any i386 system. Generate code for a
32-bit or 64-bit environment. The 32-bit environment sets int, long
and pointer to 32 bits and generates code that runs on any i386
system.
line 91 定义了ULDFLAGS,lab1中并没用到,但是将用于后面的lab中。
line 96 定义了OBJDIRS,在此变量中会包含一些子目录名称,每个子目录下会有一个Makefrag文件;在lab1中,这个变量只包含了2个子目录,分别为boot和kern,在line
126和line 158中加入。
从line 112和113可以看出,kern和user部分分别使用了不同的宏定义:JOS_KERNEL和JOS_USER。文件kern/console.h中,line5-7会判断,由于这个文件仅用于kernel中,如果是用户程序包含了这个头文件,则不会定义JOS_KERNEL,因此会提示error:"This
is a JOS kernel header; user programs should not #include it".
line 131,打印"+ cc -Os boot/main.c".
line 132,$(@D)对应line 130的"$(OBJDIR)/boot/%.o"的目录部分,因此其命令为:mkdir -p obj/boot
line 133,-nostdinc意为不包含头文件的标准路径(/usr/include/和/usr/local/include/)。因为kernel是完全自含的,不需要外部的库文件。它的头文件路径为:$(KERN_CFLAGS)
(line 112) -> $(CFLAGS) (line 81) -> -I$(TOP) -> . (line
18),也就是说,kernel包含的头文件的搜索路径是当前目录,也就是./inc。 再看看$(V),是什么意思呢?
查看文件conf/env.mk的line 9:“V = @”,
所以也即是在该命令前添加了一个@符号。在makefile中,@的意思是不打印命令的内容,只执行。所以在make的时候,你不会看到类似于"gcc
-pipe -nostdinc -O1 -fno-builtin -I. -MD -Wall -Wno-format
-Wno-unused -Werror -gstabs -m32 -fno-stack-protector -DJOS_KERNEL
-gstabs -c -o obj/kern/init.o kern/init.c"这样的输出信息,只能看到"+ cc
kern/init.c"。 修改conf/env.mk, 将之改为"V = ",则能得到详细的make输出。
line 140生成boot/main.o的部分有点奇怪,因为有了line 130的规则,则line
140的规则已经被包括进去了,所以是不需要的,同时,删除这个规则,仍能正常的编译出kernel。所以不明白为什么这一规则会出现在这里。
line 144-149运行的命令为:
ld -m elf_i386 -N -e start -Ttext 0x7C00 -o obj/boot/boot.out
obj/boot/boot.o obj/boot/main.o
objdump -S obj/boot/boot.out >obj/boot/boot.asm
objcopy -S -O binary obj/boot/boot.out obj/boot/boot
perl boot/sign.pl obj/boot/boot
其中,-e start指定了entry为start,说明boot的入口是start,定义在boot/boot.S文件的line 13。
-Ttext 0x7C00指定了代码将置于内存地址0x7C00处,这是BIOS自检结束后跳转到的位置。ld生成的文件为obj/boot/boot.out。
objdump对obj/boot/boot.out返汇编,生成汇编代码obj/boot/boot.asm;而objcopy将obj/boot/boot.out里真正可执行的部分转换为binary格式,生成obj/boot/boot。此文件将不能大于510个字节,然后由boot/sign.pl将之填充到512个字节,并且最后两个字节为0x55aa。
line 160的kern/kernel.ld文件有什么作用?我将在后续文章里说明;简言之,就是用来指导ld生成需要的kernel。
-nostdlib 类似于-nostdinc,不包括标准库文件的搜索路径。
line 166显然包括了一些后续lab才提供的源代码文件,所以在line 185进行了过滤,只留下了现在存在的源码文件。
line 189将$(KERN_SRCFILES)包含的*.c文件名替换成了*.o,所以$(KERN_OBJFILES)只包括.o和.S文件名;而line
190将.S文件名也替换成了.o文件名;line
191将lib目录名替换成了kern目录名;所以最终$(KERN_OBJFILES)的内容就是obj/kern/*.o。
line 193将KERN_BINFILES的值添加了前缀目录obj。由于lab1中暂时还没有KERN_BINFILES文件,所以暂不讨论。
line 212-216对应的命令为:
+ ld obj/kern/kernel
ld -o obj/kern/kernel -m elf_i386 -T kern/kernel.ld -nostdlib
obj/kern/entry.o obj/kern/init.o obj/kern/console.o obj/kern/monitor.o
obj/kern/printf.o obj/kern/kdebug.o obj/kern/printfmt.o
obj/kern/readline.o obj/kern/string.o
/usr/lib/gcc/i486-linux-gnu/4.3.5/libgcc.a -b binary
objdump -S obj/kern/kernel > obj/kern/kernel.asm
nm -n obj/kern/kernel > obj/kern/kernel.sym
将生成汇编代码obj/kern/kernel.asm和符号表obj/kern/kernel.sym。
line 218-224, 首先建立了一个大小为10000个block =
5120000字节大小的全0文件,然后用obj/boot/boot和obj/kern/kernel分别覆盖第一block和后续block。实际boot+kernel才几十K而已,5120000字节纯属为将来扩充考虑。如果是当年,内存寻址才1M大小的时候,怎么也不至于如此浪费的。
(:
line 230-232生成了一个用于grub的kernel,可以用grub引导;由于有了grub的引导功能,所以用于引导的boot也就不需要了。不过实验中并没有提到如何使用生成的kernerl,有兴趣的可以试试。
line 298-302的作用就是将obj目录下的子目录下的XXX.d文件的内容聚集到一起放到文件ojb/.deps中,然后将这个文件包含进去,作为make时的一个依赖。这一目标是通过mergedep.pl来实现的。
line 304-305: 我只想说:我也不知道啥意思! ):