make
make命令执行需要一个makefile文件,以告诉make命令需要如何去编译和链接程序。
- 如果工程没有被编译过,所有的c文件都要编译并被链接。
- 如果某几个c文件被修改,那么只编译被修改的c文件,并链接目标程序。
- 如果工程的头文件被修改了,那么需要编译引用了这几个头文件的c文件,并链接目标程序
1
2
3
4
| target... : prerequisites...
command
...
...
|
target也就是一个目标文件,可以是object file,也可以是执行文件。还可以是一个label。prerequisites就是要生成target所需要的文件或是目标。command就是make需要执行的命令。target这一个或多个的目标文件依赖于prerequisites中的文件,其生成规则定义在command中。如果prerequisites中有一个以上的文件比target文件要新,那么command所定义的命令就会被执行。
问题 1
操作系统镜像文件ucore.img是如何一步一步生成的?(需要比较详细地解释Makefile中每
一条相关命令和命令参数的含义,以及说明命令导致的结果)
ucore.img
makefile中生成ucore.img的代码为:
1
2
3
4
5
6
7
8
| UCOREIMG := $(call totarget,ucore.img)
$(UCOREIMG): $(kernel) $(bootblock)
$(V)dd if=/dev/zero of=$@ count=10000
$(V)dd if=$(bootblock) of=$@ conv=notrunc
$(V)dd if=$(kernel) of=$@ seek=1 conv=notrunc
$(call create_target,ucore.img)
|
将ucore.img传入totarget表达式调用call函数结果赋值给变量UCOREIMG,UCOREIMG作为target,其依赖于两个文件,一个是kernel,一个是bootblock。接下来给出make需要执行的命令。首先从/dev/zero中读了10000*512块的空字节,生成空文件,接着将bootlock中的内容拷贝到目标文件,然后从输文件的512字节后继续写入kernel的内容。makefile的第六行V := @
将@赋值给变量V,所以$(V)代指@,表示命令不回显。conv=notrunc代表不截断输出文件,count=n’ 代表从输入文件中拷贝n个大小为ibs byte的块,ibs默认为512字节。seek=n代表在拷贝前输出文件时跳过n 个‘obs’-byte的块。obs默认为512字节。所以seek=1代表跳过输出文件的512个字节。
kenel:
1
2
3
4
5
6
7
8
9
10
11
| kernel = $(call totarget,kernel)
$(kernel): tools/kernel.ld
$(kernel): $(KOBJS)
@echo + ld $@
$(V)$(LD) $(LDFLAGS) -T tools/kernel.ld -o $@ $(KOBJS)
@$(OBJDUMP) -S $@ > $(call asmfile,kernel)
@$(OBJDUMP) -t $@ | $(SED) '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > $(call symfile,kernel)
$(call create_target,kernel)
|
kernel的生成依赖于KOBJS和tools/kernel.ld,生成命令依赖于i386-elf-objdump 、ld和objdump等。
第五行链接各种文件输出给目标文件,第六行反汇编目标文件输出给asmfile这个变量。第七行输出目标文件的符号表并进行文本替换。最后写入symfile这个变量。
bootblock
1
2
3
4
5
6
7
8
9
10
| bootfiles = $(call listf_cc,boot)
$(foreach f,$(bootfiles),$(call cc_compile,$(f),$(CC),$(CFLAGS) -Os -nostdinc))
bootblock = $(call totarget,bootblock)
$(bootblock): $(call toobj,$(bootfiles)) | $(call totarget,sign)
@echo + ld $@
$(V)$(LD) $(LDFLAGS) -N -e start -Ttext 0x7C00 $^ -o $(call toobj,bootblock)
@$(OBJDUMP) -S $(call objfile,bootblock) > $(call asmfile,bootblock)
@$(OBJCOPY) -S -O binary $(call objfile,bootblock) $(call outfile,bootblock)
@$(call totarget,sign) $(call outfile,bootblock) $(bootblock)
$(call create_target,bootblock)
|
bootblock 依赖于bootasm.o、bootmain.o、sign生成bootblock的编译指令为:
1
| ld -m elf_i386 -nostdlib -N -e start -Ttext 0x7C00 obj/boot/bootasm.o obj/boot/bootmain.o -o obj/bootblock.o
|
- -m 模拟为i386上的连接器
- -nostdlib 不使用标准库
- -N 设置代码段和数据段均可读写
- -e 指定入口
- -Ttext 制定代码段开始位置
- -fno-builtin:除非用__builtin_前缀,否则不进行builtin函数的优化
问题 2
一个被系统认为是符合规范的硬盘主引导扇区的特征是什么?
sign:外部执行程序,用来生成虚拟的硬盘主引导扇区
从sign.c代码中:
1
2
3
4
5
6
7
| if (st.st_size > 510) {
fprintf(stderr, "%lld >> 510!!\n", (long long)st.st_size);
return -1;
}
...
buf[510] = 0x55;
buf[511] = 0xAA;
|
主引导扇区有512个字节,第511字节写入0x55,第512字节写入0xAA。