lab1 中的 Makefile 主要是根目录下的 GNUMakefile, kern/Makefrag, boot/Makefrag, 后两者通过 include 直接包含到 GNUMakefile 中。
#
# This makefile system follows the structuring conventions
# recommended by Peter Miller in his excellent paper:
#
# Recursive Make Considered Harmful
# http://aegis.sourceforge.net/auug97.pdf
#
OBJDIR := obj
# 10-16行, 如果编译时使用make V=1,则会输出详细编译信息,否则输出类似 “CC MAIN”的简单信息
# 很多Makefile都采用这种方式, 包括Linux kernel.
# Run 'make V=1' to turn on verbose commands, or 'make V=0' to turn them off.
ifeq ($(V),1)
override V =
endif
ifeq ($(V),0)
override V = @
endif
-include conf/lab.mk
-include conf/env.mk
LABSETUP ?= ./
TOP = .
# Cross-compiler jos toolchain
#
# This Makefile will automatically use the cross-compiler toolchain
# installed as 'i386-jos-elf-*', if one exists. If the host tools ('gcc',
# 'objdump', and so forth) compile for a 32-bit x86 ELF target, that will
# be detected as well. If you have the right compiler toolchain installed
# using a different name, set GCCPREFIX explicitly in conf/env.mk
# 34-48, 通过执行shell objdump 命令得到GCCPREFIX, 即如果 i386-jos-elf-objdump存在
# GCCPREFIX=i386-jos-elf-objdump, 否则GCCPREFIX=""
# try to infer the correct GCCPREFIX
ifndef GCCPREFIX
GCCPREFIX := $(shell if i386-jos-elf-objdump -i 2>&1 | grep '^elf32-i386$$' >/dev/null 2>&1; \
then echo 'i386-jos-elf-'; \
elif objdump -i 2>&1 | grep 'elf32-i386' >/dev/null 2>&1; \
then echo ''; \
else echo "***" 1>&2; \
echo "*** Error: Couldn't find an i386-*-elf version of GCC/binutils." 1>&2; \
echo "*** Is the directory with i386-jos-elf-gcc in your PATH?" 1>&2; \
echo "*** If your i386-*-elf toolchain is installed with a command" 1>&2; \
echo "*** prefix other than 'i386-jos-elf-', set your GCCPREFIX" 1>&2; \
echo "*** environment variable to that prefix and run 'make' again." 1>&2; \
echo "*** To turn off this error, run 'gmake GCCPREFIX= ...'." 1>&2; \
echo "***" 1>&2; exit 1; fi)
endif
# 50-64, 和上面类似, 得到QEMU=qemu,或者QEMU=qemu-system-i386
# try to infer the correct QEMU
ifndef QEMU
QEMU := $(shell if which qemu > /dev/null; \
then echo qemu; exit; \
elif which qemu-system-i386 > /dev/null; \
then echo qemu-system-i386; exit; \
else \
qemu=/Applications/Q.app/Contents/MacOS/i386-softmmu.app/Contents/MacOS/i386-softmmu; \
if test -x $$qemu; then echo $$qemu; exit; fi; fi; \
echo "***" 1>&2; \
echo "*** Error: Couldn't find a working QEMU executable." 1>&2; \
echo "*** Is the directory containing the qemu binary in your PATH" 1>&2; \
echo "*** or have you tried setting the QEMU variable in conf/env.mk?" 1>&2; \
echo "***" 1>&2; exit 1)
endif
# try to generate a unique GDB port
GDBPORT := $(shell expr `id -u` % 5000 + 25000)
CC := $(GCCPREFIX)gcc -pipe
AS := $(GCCPREFIX)as
AR := $(GCCPREFIX)ar
LD := $(GCCPREFIX)ld
OBJCOPY := $(GCCPREFIX)objcopy
OBJDUMP := $(GCCPREFIX)objdump
NM := $(GCCPREFIX)nm
# Native commands
NCC := gcc $(CC_VER) -pipe
# 79行, -I表示 include 包含路径, -M表示生成以来关系,比如:
#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的意思是将依赖关系保存在相应的.d文件. -Wall表示显示所有warning信息.
NATIVE_CFLAGS := $(CFLAGS) $(DEFS) $(LABDEFS) -I$(TOP) -MD -Wall
TAR := gtar
PERL := perl
# 83-91, -O1表示编译器优化级别, 一般有-O0(没有优化)/O1/O2/O3, 如果同时启用多个-O选项,无论有没有级别数字,只有最后一个选项有效。
# -fno-buildin: 不接受没有 __builtin_ 前缀的函数作为内建函数。
# -W前缀表示Warning, -Werror:把warning当做error.
# -gstabs:生成汇编代码中包含stab格式的调试信息
# Compiler flags
# -fno-builtin is required to avoid refs to undefined functions in the kernel.
# Only optimize to -O1 to discourage inlining, which complicates backtraces.
CFLAGS := $(CFLAGS) $(DEFS) $(LABDEFS) -O1 -fno-builtin -I$(TOP) -MD
CFLAGS += -fno-omit-frame-pointer
CFLAGS += -Wall -Wno-format -Wno-unused -Werror -gstabs -m32
# -fno-tree-ch prevented gcc from sometimes reordering read_ebp() before
# mon_backtrace()'s function prologue on gcc version: (Debian 4.7.2-5) 4.7.2
CFLAGS += -fno-tree-ch
# Add -fno-stack-protector if the option exists.
CFLAGS += $(shell $(CC) -fno-stack-protector -E -x c /dev/null >/dev/null 2>&1 && echo -fno-stack-protector)
# 模拟器类型为i386
# Common linker flags
LDFLAGS := -m elf_i386
# Linker flags for JOS user programs
ULDFLAGS := -T user/user.ld
GCC_LIB := $(shell $(CC) $(CFLAGS) -print-libgcc-file-name)
# Lists that the */Makefrag makefile fragments will add to
OBJDIRS :=
# Make sure that 'all' is the first target
# 默认目标
all:
# Eliminate default suffix rules
.SUFFIXES:
# Delete target files if there is an error (or make is interrupted)
.DELETE_ON_ERROR:
# make it so that no intermediate .o files are ever deleted
.PRECIOUS: %.o $(OBJDIR)/boot/%.o $(OBJDIR)/kern/%.o \
$(OBJDIR)/lib/%.o $(OBJDIR)/fs/%.o $(OBJDIR)/net/%.o \
$(OBJDIR)/user/%.o
KERN_CFLAGS := $(CFLAGS) -DJOS_KERNEL -gstabs
USER_CFLAGS := $(CFLAGS) -DJOS_USER -gstabs
# Update .vars.X if variable X has changed since the last make run.
#
# Rules that use variable X should depend on $(OBJDIR)/.vars.X. If
# the variable's value has changed, this will update the vars file and
# force a rebuild of the rule that depends on it.
$(OBJDIR)/.vars.%: FORCE
$(V)echo "$($*)" | cmp -s $@ || echo "$($*)" > $@
.PRECIOUS: $(OBJDIR)/.vars.%
.PHONY: FORCE
# 包含的两个makefile, 后续分析
# Include Makefrags for subdirectories
include boot/Makefrag
include kern/Makefrag
# QEMU执行参数
QEMUOPTS = -hda $(OBJDIR)/kern/kernel.img -serial mon:stdio -gdb tcp::$(GDBPORT)
QEMUOPTS += $(shell if $(QEMU) -nographic -help | grep -q '^-D '; then echo '-D qemu.log'; fi)
IMAGES = $(OBJDIR)/kern/kernel.img
QEMUOPTS += $(QEMUEXTRA)
# sed 将 .gdbinit.teml中1234端口替换为GDBPORT导入.gdbinit中.
.gdbinit: .gdbinit.tmpl
sed "s/localhost:1234/localhost:$(GDBPORT)/" < $^ > $@
gdb:
gdb -x .gdbinit
# 以下为qemu启动命令.
pre-qemu: .gdbinit
qemu: $(IMAGES) pre-qemu
$(QEMU) $(QEMUOPTS)
qemu-nox: $(IMAGES) pre-qemu
@echo "***"
@echo "*** Use Ctrl-a x to exit qemu"
@echo "***"
$(QEMU) -nographic $(QEMUOPTS)
qemu-gdb: $(IMAGES) pre-qemu
@echo "***"
@echo "*** Now run 'make gdb'." 1>&2
@echo "***"
$(QEMU) $(QEMUOPTS) -S
qemu-nox-gdb: $(IMAGES) pre-qemu
@echo "***"
@echo "*** Now run 'make gdb'." 1>&2
@echo "***"
$(QEMU) -nographic $(QEMUOPTS) -S
print-qemu:
@echo $(QEMU)
print-gdbport:
@echo $(GDBPORT)
# clean, realclean, distclean依次向上依赖, 删除逐渐彻底,distclean回复成直接git下来时的状态.
# For deleting the build
clean:
rm -rf $(OBJDIR) .gdbinit jos.in qemu.log
realclean: clean
rm -rf lab$(LAB).tar.gz \
jos.out $(wildcard jos.out.*) \
qemu.pcap $(wildcard qemu.pcap.*) \
myapi.key
distclean: realclean
rm -rf conf/gcc.mk
ifneq ($(V),@)
GRADEFLAGS += -v
endif
grade:
@echo $(MAKE) clean
@$(MAKE) clean || \
(echo "'make clean' failed. HINT: Do you have another running instance of JOS?" && exit 1)
./grade-lab$(LAB) $(GRADEFLAGS)
git-handin: handin-check
@if test -n "`git config remote.handin.url`"; then \
echo "Hand in to remote repository using 'git push handin HEAD' ..."; \
if ! git push -f handin HEAD; then \
echo ; \
echo "Hand in failed."; \
echo "As an alternative, please run 'make tarball'"; \
echo "and visit http://pdos.csail.mit.edu/6.828/submit/"; \
echo "to upload lab$(LAB)-handin.tar.gz. Thanks!"; \
false; \
fi; \
else \
echo "Hand-in repository is not configured."; \
echo "Please run 'make handin-prep' first. Thanks!"; \
false; \
fi
WEBSUB = https://ccutler.scripts.mit.edu/6.828/handin.py
handin: tarball-pref myapi.key
@curl -f -F file=@lab$(LAB)-handin.tar.gz -F key=\<myapi.key $(WEBSUB)/upload \
> /dev/null || { \
echo ; \
echo Submit seems to have failed.; \
echo Please go to $(WEBSUB)/ and upload the tarball manually.; }
handin-check:
@if ! test -d .git; then \
echo No .git directory, is this a git repository?; \
false; \
fi
@if test "$$(git symbolic-ref HEAD)" != refs/heads/lab$(LAB); then \
git branch; \
read -p "You are not on the lab$(LAB) branch. Hand-in the current branch? [y/N] " r; \
test "$$r" = y; \
fi
@if ! git diff-files --quiet || ! git diff-index --quiet --cached HEAD; then \
git status; \
echo; \
echo "You have uncomitted changes. Please commit or stash them."; \
false; \
fi
@if test -n "`git ls-files -o --exclude-standard`"; then \
git status; \
read -p "Untracked files will not be handed in. Continue? [y/N] " r; \
test "$$r" = y; \
fi
tarball: handin-check
git archive --format=tar HEAD | gzip > lab$(LAB)-handin.tar.gz
tarball-pref: handin-check
git archive --prefix=lab$(LAB)/ --format=tar HEAD | gzip > lab$(LAB)-handin.tar.gz
myapi.key:
@echo Get an API key for yourself by visiting $(WEBSUB)
@read -p "Please enter your API key: " k; \
if test `echo -n "$$k" |wc -c` = 32 ; then \
TF=`mktemp -t tmp.XXXXXX`; \
if test "x$$TF" != "x" ; then \
echo -n "$$k" > $$TF; \
mv -f $$TF $@; \
else \
echo mktemp failed; \
false; \
fi; \
else \
echo Bad API key: $$k; \
echo An API key should be 32 characters long.; \
false; \
fi;
handin-prep:
@./handin-prep
# This magic automatically generates makefile dependencies
# for header files included from C source files we compile,
# and keeps those dependencies up-to-date every time we recompile.
# See 'mergedep.pl' for more information.
$(OBJDIR)/.deps: $(foreach dir, $(OBJDIRS), $(wildcard $(OBJDIR)/$(dir)/*.d))
@mkdir -p $(@D)
@$(PERL) mergedep.pl $@ $^
-include $(OBJDIR)/.deps
always:
@:
.PHONY: all always \
handin git-handin tarball tarball-pref clean realclean distclean grade handin-prep handin-check
kern/makefrag 内容如下:
#
# Makefile fragment for JOS kernel.
# This is NOT a complete makefile;
# you must run GNU make in the top-level directory
# where the GNUmakefile is located.
#
OBJDIRS += kern
KERN_LDFLAGS := $(LDFLAGS) -T kern/kernel.ld -nostdlib
# entry.S must be first, so that it's the first code in the text segment!!!
#
# We also snatch the use of a couple handy source files
# from the lib directory, to avoid gratuitous code duplication.
# 需要编译的源文件.
KERN_SRCFILES := kern/entry.S \
kern/entrypgdir.c \
kern/init.c \
kern/console.c \
kern/monitor.c \
kern/pmap.c \
kern/env.c \
kern/kclock.c \
kern/picirq.c \
kern/printf.c \
kern/trap.c \
kern/trapentry.S \
kern/sched.c \
kern/syscall.c \
kern/kdebug.c \
lib/printfmt.c \
lib/readline.c \
lib/string.c
# Only build files if they exist.
# wildcard:扩展通配符
KERN_SRCFILES := $(wildcard $(KERN_SRCFILES))
# Binary program images to embed within the kernel.
KERN_BINFILES :=
KERN_OBJFILES := $(patsubst %.c, $(OBJDIR)/%.o, $(KERN_SRCFILES))
KERN_OBJFILES := $(patsubst %.S, $(OBJDIR)/%.o, $(KERN_OBJFILES))
KERN_OBJFILES := $(patsubst $(OBJDIR)/lib/%, $(OBJDIR)/kern/%, $(KERN_OBJFILES))
KERN_BINFILES := $(patsubst %, $(OBJDIR)/%, $(KERN_BINFILES))
# How to build kernel object files
# 48-61,把kern下的.c和.S, lib/下的.c编译成.o 放在kern目录下
# V如果是 @ ,表示这句只执行不显示.
$(OBJDIR)/kern/%.o: kern/%.c $(OBJDIR)/.vars.KERN_CFLAGS
@echo + cc $<
@mkdir -p $(@D)
$(V)$(CC) -nostdinc $(KERN_CFLAGS) -c -o $@ $<
$(OBJDIR)/kern/%.o: kern/%.S $(OBJDIR)/.vars.KERN_CFLAGS
@echo + as $<
@mkdir -p $(@D)
$(V)$(CC) -nostdinc $(KERN_CFLAGS) -c -o $@ $<
$(OBJDIR)/kern/%.o: lib/%.c $(OBJDIR)/.vars.KERN_CFLAGS
@echo + cc $<
@mkdir -p $(@D)
$(V)$(CC) -nostdinc $(KERN_CFLAGS) -c -o $@ $<
# Special flags for kern/init
$(OBJDIR)/kern/init.o: override KERN_CFLAGS+=$(INIT_CFLAGS)
$(OBJDIR)/kern/init.o: $(OBJDIR)/.vars.INIT_CFLAGS
# How to build the kernel itself
# 71: 把编译产物用ld命令连接成kernel
# 72: 将kernel反汇编
# 73: 到处符号表
$(OBJDIR)/kern/kernel: $(KERN_OBJFILES) $(KERN_BINFILES) kern/kernel.ld \
$(OBJDIR)/.vars.KERN_LDFLAGS
@echo + ld $@
$(V)$(LD) -o $@ $(KERN_LDFLAGS) $(KERN_OBJFILES) $(GCC_LIB) -b binary $(KERN_BINFILES)
$(V)$(OBJDUMP) -S $@ > $@.asm
$(V)$(NM) -n $@ > $@.sym
# How to build the kernel disk image
# 上面的kernel仅是elf文件, 此处将boot,kernel导成镜像.
$(OBJDIR)/kern/kernel.img: $(OBJDIR)/kern/kernel $(OBJDIR)/boot/boot
@echo + mk $@
$(V)dd if=/dev/zero of=$(OBJDIR)/kern/kernel.img~ count=10000 2>/dev/null
$(V)dd if=$(OBJDIR)/boot/boot of=$(OBJDIR)/kern/kernel.img~ conv=notrunc 2>/dev/null
$(V)dd if=$(OBJDIR)/kern/kernel of=$(OBJDIR)/kern/kernel.img~ seek=1 conv=notrunc 2>/dev/null
$(V)mv $(OBJDIR)/kern/kernel.img~ $(OBJDIR)/kern/kernel.img
all: $(OBJDIR)/kern/kernel.img
grub: $(OBJDIR)/jos-grub
$(OBJDIR)/jos-grub: $(OBJDIR)/kern/kernel
@echo + oc $@
$(V)$(OBJCOPY) --adjust-vma=0x10000000 $^ $@
xxxxx
#
# Makefile fragment for the JOS kernel.
# This is NOT a complete makefile;
# you must run GNU make in the top-level directory
# where the GNUmakefile is located.
#
OBJDIRS += boot
BOOT_OBJS := $(OBJDIR)/boot/boot.o $(OBJDIR)/boot/main.o
$(OBJDIR)/boot/%.o: boot/%.c
@echo + cc -Os $<
@mkdir -p $(@D)
$(V)$(CC) -nostdinc $(KERN_CFLAGS) -Os -c -o $@ $<
$(OBJDIR)/boot/%.o: boot/%.S
@echo + as $<
@mkdir -p $(@D)
$(V)$(CC) -nostdinc $(KERN_CFLAGS) -c -o $@ $<
$(OBJDIR)/boot/main.o: boot/main.c
@echo + cc -Os $<
$(V)$(CC) -nostdinc $(KERN_CFLAGS) -Os -c -o $(OBJDIR)/boot/main.o boot/main.c
# 将目标文件连接成elf文件.
# -e start: 入口函数为start.
# -Ttext 0x7C00:代码段入口地址为0x7C00. BIOS加电自检后会自动跳到次地址来执行,相当于开机后软件第一条指令地址.
$(OBJDIR)/boot/boot: $(BOOT_OBJS)
@echo + ld boot/boot
$(V)$(LD) $(LDFLAGS) -N -e start -Ttext 0x7C00 -o $@.out $^
$(V)$(OBJDUMP) -S $@.out >$@.asm
$(V)$(OBJCOPY) -S -O binary -j .text $@.out $@
$(V)perl boot/sign.pl $(OBJDIR)/boot/boot