Ucore Lab1(上)
前情提要:
Vx6太难了, 👴🏻跑路了
日, 怎么Ubuntu里敲出来的中文这么怪
ppt来源于清华大学的操作系统网课
练习1:
Q1: ucore.img是如何一步一步生成的?
先看一下makefile(选择编译器和编译器选项), 经过对比, 发现使用的是LLVM makefile系统, cc实际上为gcc编译 / ld链接选项有如下:
-g可调试
-Wall会列举编译文件中的一些常见错误
-O2为优化程度
-march(架构?不会翻译)为i686
-fno-builtin若自己的函数与库函数重名也可以通过编译
-fno-PIC不使用位置无关代码(因为开启pic会为got设定32k的限制, 所以可能会导致编译失败
-ggdb倾向gdb的可调试
-m32生成32位程序
-gstabs以stabs格式声称调试信息但是不包括gdb调试信息(.s文件中的信息)
-nostdinc不要在标准系统目录中寻找头文件
-fno-stack-protector关闭canary
-E仅作预处理、不编译、汇编或链接
-x c选定语言为c语言
vim看了一下Makefile, 主要目的还是生成kernel, bootblock, ucore.img, sign工具
链接kernel情况如下(编译文件太多就不截图了):
+ ld bin/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
其在makefile中的代码如下:
kernel = $(call totarget,kernel) #将/bin前缀添加到kernel中
$(kernel): tools/kernel.ld #kernel的依赖项为tools/kernel.ld
$(kernel): $(KOBJS) #kernel的依赖项为$(KOBJS), 在非"V="不打印@后的内容
@echo + ld $@
$(V)$(LD) $(LDFLAGS) -T tools/kernel.ld -o $@ $(KOBJS) #变量自己看咯,-T为选定指定ld文件作为完全替换
@$(OBJDUMP) -S $@ > $(call asmfile,kernel)
@$(OBJDUMP) -t $@ | $(SED) '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > $(call symfile,kernel)
$(call create_target,kernel) #生成kernel
编译bootblock的依赖项情况如下:
+ cc boot/bootasm.S
gcc -Iboot/ -march=i686 -fno-builtin -fno-PIC -Wall -ggdb -m32 -gstabs -nostdinc -fno-stack-protector -Ilibs/ -Os -nostdinc -c boot/bootasm.S -o obj/boot/bootasm.o
+ cc boot/bootmain.c
gcc -Iboot/ -march=i686 -fno-builtin -fno-PIC -Wall -ggdb -m32 -gstabs -nostdinc -fno-stack-protector -Ilibs/ -Os -nostdinc -c boot/bootmain.c -o obj/boot/bootmain.o
链接bootblock情况如下;
+ ld bin/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: 484 bytes
build 512 bytes boot sector: 'bin/bootblock' success!
其在makefile中代码如下:
bootfiles = $(call listf_cc,boot)
$(foreach f,$(bootfiles),$(call cc_compile,$(f),$(CC),$(CFLAGS) -Os -nostdinc))
#上面一句的意思是, 先用listf_cc函数列举出所用boot文件夹中的c语言文件到变量bootfiles中
#再用foreach函数循环, 将bootfiles中的文件枚举到临时变量f中, 然后依次对f中的文件以CFLAGS的选项
#再加上-Os, -nostdinc(不用标准库)以gcc进行编译
bootblock = $(call totarget,bootblock) #先增加/bin前缀到bootblock中
$(bootblock): $(call toobj,$(bootfiles)) | $(call totarget,sign)
#上面的意思是获得/obj前缀到生成的.oqi依赖项中, 并且将sign(后面会讲)添加/bin前缀
@echo + ld $@
$(V)$(LD) $(LDFLAGS) -N -e start -Ttext 0x7C00 $^ -o $(call toobj,bootblock)
#-N把text和data节设置为可读写.同时,取消数据节的页对齐,同时,取消对共享库的连接. -e start将符号start设置为程序起始位置.
#-Ttext 0x7C00将该地址设置为.bss或.text或.data
@$(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
最终生成了一个512字节大小的boot扇区
编译sign小工具情况如下:
+ cc tools/sign.c
gcc -Itools/ -g -Wall -O2 -c tools/sign.c -o obj/sign/tools/sign.o
gcc -g -Wall -O2 obj/sign/tools/sign.o -o bin/sign
#太短, 懒得备注了属于是
其在makefile中如下
# create 'sign' tools
$(call add_files_host,tools/sign.c,sign,sign)
$(call create_target_host,sign,sign)
#用tools/sign.c创建一个sign/sign
此举用于生成一个符合规范的硬盘主引导扇区
生成ucore.img情况如下:
dd if=/dev/zero of=bin/ucore.img count=10000
10000+0 records in
10000+0 records out
5120000 bytes (5.1 MB, 4.9 MiB) copied, 0.0225575 s, 227 MB/s
dd if=bin/bootblock of=bin/ucore.img conv=notrunc
1+0 records in
1+0 records out
512 bytes copied, 0.000109965 s, 4.7 MB/s
dd if=bin/kernel of=bin/ucore.img seek=1 conv=notrunc
146+1 records in
146+1 records out
74828 bytes (75 kB, 73 KiB) copied, 0.000395085 s, 189 MB/s
其在makefile中如下:
UCOREIMG := $(call totarget,ucore.img) #定义变量, 且将ucore.img添加/bin前缀
$(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
#创建一个10000块将/dev/zero拷贝进去
#将$(bootblock)拷贝到同一个位置(不截短输出文件 )
#将$(kernel)拷贝到同一位置(从输出文件开头跳过1个块后再开始拷贝)
$(call create_target,ucore.img) #生成ucore.img
Q2: 一个被系统认为是符合规范的硬盘主引导扇区的特征是什么?
通过分析sign.c文件我们可以得到答案如下:
- block.out的大小小于510字节
- 扇区大小为512字节
- 扇区结尾为0x55AA
练习二:
大概就是总结总结自己gdb的使用吧:
- pwndbg中的类会报错
- gdb原来的操作界面实在太丑了
- peda可用, 但显示源码不方便
- 同时修改lab1文件夹tools/gdbinit也无法使得make debug出来的gdb变为peda, 所以就只能使用qemu-system-i386来开启后在用gdb给连上去了
- 在启动后的第一条指令被卡住过, 我的界面如下:
可用看到执行的命令为add…但是分析一下明显不对, 因为eax为0, 若add ptr [eax]则肯定报错, 但是程序没有报错, 同时eip发生了改变, 所以看了一眼官方文档:
然后开动了一下猪脑才发现此时的cs设置为了0xf000, 所以真正的执行地址为0xffff0, 指令为上面的ljmp, 呜呜呜, 我好笨(这个玩意是16位! 所以要改变自己平时32位和64位的想法啦~), 之后开始执行bootblock.o是从0x07c00开始的, 因为之前在ld的时候, 有参数 -Ttext 0x7c00, 详情可man ld查看
- gdb的汇编显示语法在配置文件中要把set写在peda的配置下面, 因为peda默认是intel
其实还是有关于分析的内容的:
-
0x7c00基本上才算是真正的启动内核初始化地址(bootloader)
-
待解决(1)
-
设置重要的段地址DS, ES, SS为0
-
开启A20接口, 向后兼容的一种, 使得物理地址线20被绑定到低电平, 因此高于1MB的地址线会被置为0
-
从0x64端口读取信号, 一开始我读入的elfags, 如果读入是0x1c则说明0x64端口不忙碌(注: test执行的是and操作, 我记错了…), 不忙碌通常是这个结果PF+ZF+2(2是之前产生的, 其不是标志)
-
将0xd1传给0x64端口, 代表将从P2端口写数据
-
再次等待0x64端口不忙碌, 发送0xdf给0x60端口
即打开A20地址线
这里是打开cr0的pe位置
至此经过7, 8两步, 实际上实模式切换到保护模式的准备工作就完成了, 后面一个跳转就跑到保护模式了
- 保护模式在执行bootmain之前在ucore中就只是设置设置寄存器, 设置设置栈啥的
学堂在线第三讲:
3.1:
-
cpu加电以后会先执行BIOS, 其作用为:
- 将加载程序(0000: 0x7c00)加载到引导扇区
- 跳转到加载程序
之后加载程序的作用为:
- 将操作系统的代码和数据加载到内存中
- 跳转到操作系统开头
所以说BIOS和加载程序感觉就像两个跳转一样, 而其还有一个作用为识别操作系统格式
-
实模式: 执行BIOS和加载程序的程序, 此时是固定为20位地址线, 寻址方式为 基址*0x10000+偏移, 最大寻址空间为1MB, 此时所有段可读可写可执行, 而且在某些实模式中会有一些只能在实模式中使用的中断(如intel)
3.2:
- BIOS的其它一些作用: 自检类似显卡等关键部件的工作状态, 然后继续部件初始化, 进行系统检测(包括一些U盘中即插即用的系统), 更新CMOS中的拓展系统配置数据ESCD
- 还讲到多分区的东东, 由于ucore好像不涉及这玩意, 所以看图就行了:
最后一幅图是我根据《鸟哥的Linux私房菜》总结出来的…
待解决的问题:
什么是string operations increment