exe1

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。