练习一 Makefile
1.1 OS镜像文件ucore.img 是如何一步步生成的?
+ cc kern/init/init.c+ cc kern/libs/readline.c+ cc kern/libs/stdio.c+ cc kern/debug/kdebug.c+ cc kern/debug/kmonitor.c+ cc kern/debug/panic.ckern/debug/panic.c: In function '__panic':kern/debug/panic.c:27:5: warning: implicit declaration of function 'print_stackframe' [-Wimplicit-function-declaration] print_stackframe(); ^+ cc kern/driver/clock.c+ cc kern/driver/console.c+ cc kern/driver/intr.c+ cc kern/driver/picirq.c+ cc kern/trap/trap.c+ cc kern/trap/trapentry.S+ cc kern/trap/vectors.S+ cc kern/mm/pmm.c+ cc libs/printfmt.c+ cc libs/string.c+ ld bin/kernel+ cc boot/bootasm.S+ cc boot/bootmain.c+ cc tools/sign.ctools/sign.c: In function 'main':tools/sign.c:17:5: warning: unknown conversion type character 'l' in format [-Wformat=] printf("'%s' size: %lld bytes\n", argv[1], (long long)st.st_size); ^tools/sign.c:17:5: warning: too many arguments for format [-Wformat-extra-args]tools/sign.c:19:9: warning: unknown conversion type character 'l' in format [-Wformat=] fprintf(stderr, "%lld >> 510!!\n", (long long)st.st_size); ^tools/sign.c:19:9: warning: too many arguments for format [-Wformat-extra-args]+ ld bin/bootblock'obj/bootblock.out' size: 480 bytesbuild 512 bytes boot sector: 'bin/bootblock' success!
其中Makefile文件中include tools/function.mk 其中定义了一些Makefile中用到的函数
生成ucore.img 需要kernel和bootblock 如下:
# create ucore.imgUCOREIMG := $(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)
kernel编译链接:
# -------------------------------------------------------------------# kernel# kernel中头文件目录KINCLUDE += kern/debug/ \ kern/driver/ \ kern/trap/ \ kern/mm/# kernel的源代码目录KSRCDIR += kern/init \ kern/libs \ kern/debug \ kern/driver \ kern/trap \ kern/mm# 在编译选项中添加头文件包含目录KCFLAGS += $(addprefix -I,$(KINCLUDE))# 调用function.mk中的add_files_cc函数,将kernel的全部源文件编译,将源文件和编译生成的OBJ文件加入kernel包(packet)中$(call add_files_cc,$(call listf_cc,$(KSRCDIR)),kernel,$(KCFLAGS))# 将KOBJS定义为编译生成的.o文件列表(大概??)KOBJS = $(call read_packet,kernel libs)# create kernel target# (在kernel前面加上bin/目录名)kernel = $(call totarget,kernel)# kernel目标依赖于tools/kernel.ld文件$(kernel): tools/kernel.ld# kernel目标依赖于编译生成的OBJ文件$(kernel): $(KOBJS) # 输出"+ ld bin/kernel"到控制台 @echo + ld $@ # 即命令"ld -m elf_i386 -nostdlib -T tools/kernel.ld -o bin/kernel obj/kern/init/init.o ... obj/libs/printfmt.o" $(V)$(LD) $(LDFLAGS) -T tools/kernel.ld -o $@ $(KOBJS) # 将OBJ文件全部反编译为汇编文件 @$(OBJDUMP) -S $@ > $(call asmfile,kernel) # 输出OBJ文件对应的符号表 @$(OBJDUMP) -t $@ | $(SED) '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > $(call symfile,kernel)# 将kernel包和OBJ文件添加到目标依赖$(call create_target,kernel)
bootblock:
# -------------------------------------------------------------------# create bootblock# bootfiles为boot/文件夹下的全部文件列表bootfiles = $(call listf_cc,boot)# 编译boot/文件夹下的全部文件$(foreach f,$(bootfiles),$(call cc_compile,$(f),$(CC),$(CFLAGS) -Os -nostdinc))# (在bootblock前面加上bin/目录名)bootblock = $(call totarget,bootblock)# bootblock目标的依赖项为源文件对应的OBJ文件和bin/sign$(bootblock): $(call toobj,$(bootfiles)) | $(call totarget,sign) # 输出"+ ld bin/bootblock"到控制台 @echo + ld $@ # 将OBJ文件链接为bin/bootblock $(V)$(LD) $(LDFLAGS) -N -e start -Ttext 0x7C00 $^ -o $(call toobj,bootblock) # 将bin/bootblock文件反编译 @$(OBJDUMP) -S $(call objfile,bootblock) > $(call asmfile,bootblock) # 将bin/bootblock转换成bin/bootblock.out二进制文件 @$(OBJCOPY) -S -O binary $(call objfile,bootblock) $(call outfile,bootblock) # 用链接出的bin/sign工具将bin/bootblock.out再转换回bin/bootblock二进制文件 @$(call totarget,sign) $(call outfile,bootblock) $(bootblock)# 将bootblock包添加到bootblock目标$(call create_target,bootblock)
sign:
# -------------------------------------------------------------------# create 'sign' tools# 将tools/sign.c编译到OBJ文件,将源文件和中间文件添加到sign包$(call add_files_host,tools/sign.c,sign,sign)# 将sign包添加到sign目标$(call create_target_host,sign,sign)
ucore.img:
# -------------------------------------------------------------------# create ucore.img# 在ucore.img前面加上bin/UCOREIMG := $(call totarget,ucore.img)$(UCOREIMG): $(kernel) $(bootblock) # 创建一个大小为10000字节的空白文件 $(V)dd if=/dev/zero of=$@ count=10000 # 向上述文件中拷贝bin/bootblock $(V)dd if=$(bootblock) of=$@ conv=notrunc # 向上述文件中继续(第二个块)拷贝bin/kernel $(V)dd if=$(kernel) of=$@ seek=1 conv=notrunc# 将ucore.img包添加到ucore.img目标$(call create_target,ucore.img)
dd的一些参数的含义:()
-if表示输入文件,如果不指定,那么会默认从stdin中读取输入-of表示输出文件,如果不指定,那么会stdoutbs表示以字节为单位的块大小count表示被赋值的块数/dev/zero是一个字符设备,会不断返回0值字节\0conv = notrunc 不截短输出文件seek=blocks 从输出文件开头跳过blocks个块后再开始复制
生成kernel的.obj实际命令:
gcc -Ikern/init/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/init/init.c -o obj/kern/init/init.ogcc -Ikern/libs/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/libs/stdio.c -o obj/kern/libs/stdio.ogcc -Ikern/libs/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/libs/readline.c -o obj/kern/libs/readline.ogcc -Ikern/debug/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/debug/panic.c -o obj/kern/debug/panic.ogcc -Ikern/debug/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/debug/kdebug.c -o obj/kern/debug/kdebug.ogcc -Ikern/debug/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/debug/kmonitor.c -o obj/kern/debug/kmonitor.ogcc -Ikern/driver/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/driver/clock.c -o obj/kern/driver/clock.ogcc -Ikern/driver/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/driver/console.c -o obj/kern/driver/console.ogcc -Ikern/driver/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/driver/picirq.c -o obj/kern/driver/picirq.ogcc -Ikern/driver/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/driver/intr.c -o obj/kern/driver/intr.ogcc -Ikern/trap/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/trap/trap.c -o obj/kern/trap/trap.ogcc -Ikern/trap/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/trap/vectors.S -o obj/kern/trap/vectors.ogcc -Ikern/trap/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/trap/trapentry.S -o obj/kern/trap/trapentry.ogcc -Ikern/mm/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/mm/pmm.c -o obj/kern/mm/pmm.ogcc -Ilibs/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc -fno-stack-protector -Ilibs/ -c libs/string.c -o obj/libs/string.ogcc -Ilibs/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc -fno-stack-protector -Ilibs/ -c libs/printfmt.c -o obj/libs/printfmt.o
参数及其意义:()
-
- -I:添加包含目录
- -fno-builtin:只接受以“__builtin_”开头的名称的内建函数
- -Wall:开启全部警告提示
- -ggdb:生成GDB需要的调试信息
- -m32:为32位环境生成代码,int、long和指针都是32位
- -gstab:生成stab格式的调试信息,仅用于gdb
- -nostdinc:不扫描标准系统头文件,只在-I指令指定的目录中扫描
- -fno-stack-protector:生成用于检查栈溢出的额外代码,如果发生错误,则打印错误信息并退出
- -c:编译源文件但不进行链接
- -o:结果的输出文件
链接生成kernel二进制文件的命令为:
-
-
ld -m elf_i386 -nostdlib -T tools/kernel.ld -o bin/kernel obj/kern/init/init.o obj/kern/libs/stdio.o obj/kern/libs/readline.o obj/kern/debug/panic.o obj/kern/debug/kdebug.o obj/kern/debug/kmonitor.o obj/kern/driver/clock.o obj/kern/driver/console.o obj/kern/driver/picirq.o obj/kern/driver/intr.o obj/kern/trap/trap.o obj/kern/trap/vectors.o obj/kern/trap/trapentry.o obj/kern/mm/pmm.o obj/libs/string.o obj/libs/printfmt.o
参数及其意义:()
- -m elf_i386:使用elf_i386模拟器
- -nostdlib:只查找命令行中明确给出的库目录,不查找链接器脚本中给出的(即使链接器脚本是在命令行中给出的)
- -T tools/kernel.ld:将tools/kernel.ld作为链接器脚本
- -o bin/kernel:输出到bin/kernel文件
生成bootblock和sign工具所需全部OBJ文件的实际命令包括:
gcc -Iboot/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc -fno-stack-protector -Ilibs/ -Os -nostdinc -c boot/bootasm.S -o obj/boot/bootasm.ogcc -Iboot/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc -fno-stack-protector -Ilibs/ -Os -nostdinc -c boot/bootmain.c -o obj/boot/bootmain.ogcc -Itools/ -g -Wall -O2 -c tools/sign.c -o obj/sign/tools/sign.ogcc -g -Wall -O2 obj/sign/tools/sign.o -o bin/sign
参数及其意义(重复的参数不再列出):
- -Os:对输出文件大小进行优化,开启全部不增加代码大小的-O2优化
- -g:以操作系统原生格式输出调试信息,gdb可以处理这一信息
- -O2:进行大部分不以空间换时间的优化
链接生成bootblock二进制文件的命令为:
ld -m elf_i386 -nostdlib -N -e start -Ttext 0x7C00 obj/boot/bootasm.o obj/boot/bootmain.o -o obj/bootblock.o'obj/bootblock.out' size: 488 bytesbuild 512 bytes boot sector: 'bin/bootblock' success!(这两行是sign的输出)
参数及其意义:
-
- -N:将文字和数据部分置为可读写,不将数据section置为与页对齐, 不链接共享库
- -e start:将start符号置为程序起始点
- -Ttext 0x7C00:链接时将".bss"、".data"或".text"置于绝对地址0x7C00处
-
1.2 一个被系统认为是符合规范的硬盘主引导扇区的特征是什么?
tool/sign.c
输入的主引导扇区记录小于等于510字节(446+64)
最后两个字节是0x55AA
练习2:
使用Qemu和gdb调试lab1 实验目的:熟悉单步调试gdb方法 进一步了解os启动流程
练习3:
bootloader进入保护模式的过程
BIOS通过读取硬盘主引导扇区到内存,并跳转到对应内存中的位置执行bootloader,从实模式转换到保护模式。
bootloader启动过程主要工作:1切换到保护模式,启用分段机制
2读磁盘中ELF执行文件格式的ucore到内存
3显示字符串信息
4把控制权交给ucore操作系统
1.开启A20地址线
2.初始化GDT表
3.进入保护模式