实验手册:https://chyyuu.gitbooks.io/ucore_os_docs/content/

练习1:理解通过make生成执行文件的过程

1. 操作系统镜像文件ucore.img是如何一步一步生成的?

首先进入目录 home/moocos/ucore_lab/labcodes_answer/lab1_result

make:根据Makefile文件编译源代码、连接、生成目标文件、可执行文件。
make clean:清除上次的make命令所产生的object文件(后缀为“.o”的文件)及可执行文件。
make V=:显示make具体执行了哪些命令。

在linux下.c只是简单的文本文件,.o是编译之后的二进制文件也称对象文件。

以下是 make V= 显示的结果:
+ cc kern/init/init.c
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.o
+ cc kern/libs/readline.c
gcc -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.o
+ cc kern/libs/stdio.c
gcc -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.o
+ cc kern/debug/kdebug.c
gcc -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.o
+ cc kern/debug/kmonitor.c
gcc -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.o
+ cc kern/debug/panic.c
gcc -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.o
+ cc kern/driver/clock.c
gcc -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.o
+ cc kern/driver/console.c
gcc -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.o
+ cc kern/driver/intr.c
gcc -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.o
+ cc kern/driver/picirq.c
gcc -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.o
+ cc kern/trap/trap.c
gcc -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.o
+ cc kern/trap/trapentry.S
gcc -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.o
+ cc kern/trap/vectors.S
gcc -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.o
+ cc kern/mm/pmm.c
gcc -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.o
+ cc libs/printfmt.c
gcc -Ilibs/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc  -fno-stack-protector -Ilibs/  -c libs/printfmt.c -o obj/libs/printfmt.o
+ cc libs/string.c
gcc -Ilibs/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc  -fno-stack-protector -Ilibs/  -c libs/string.c -o obj/libs/string.o
+ ld bin/kernel
ld -m    elf_i386 -nostdlib -T tools/kernel.ld -o bin/kernel  obj/kern/init/init.o obj/kern/libs/readline.o obj/kern/libs/stdio.o obj/kern/debug/kdebug.o obj/kern/debug/kmonitor.o obj/kern/debug/panic.o obj/kern/driver/clock.o obj/kern/driver/console.o obj/kern/driver/intr.o obj/kern/driver/picirq.o obj/kern/trap/trap.o obj/kern/trap/trapentry.o obj/kern/trap/vectors.o obj/kern/mm/pmm.o  obj/libs/printfmt.o obj/libs/string.o
+ cc boot/bootasm.S
gcc -Iboot/ -fno-builtin -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/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc  -fno-stack-protector -Ilibs/ -Os -nostdinc -c boot/bootmain.c -o obj/boot/bootmain.o
+ 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
+ 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: 488 bytes
build 512 bytes boot sector: 'bin/bootblock' success!
dd if=/dev/zero of=bin/ucore.img count=10000
10000+0 records in
10000+0 records out
5120000 bytes (5.1 MB) copied, 0.052428 s, 97.7 MB/s
dd if=bin/bootblock of=bin/ucore.img conv=notrunc
1+0 records in
1+0 records out
512 bytes (512 B) copied, 8.8643e-05 s, 5.8 MB/s
dd if=bin/kernel of=bin/ucore.img seek=1 conv=notrunc
146+1 records in
146+1 records out
74923 bytes (75 kB) copied, 0.00101346 s, 73.9 MB/s

cc来自于Unix的c语言编译器,是 c compiler 的缩写;gcc来自Linux世界,是GNU compiler collection 的缩写,是编译器集合。

makefile中,命令行第一个字符为加号+,则执行命令时不受到make的-n -t -q三个参数的影响。

上面命令前部分都是cc和gcc,用于编译.c文件生成.o文件,这里cc指向gcc,因此在Linux下cc等价于gcc。

如果不加V=,也会输出+cc,+ld等内容,可能这里是一些提示信息。

gcc中-c和-o是编译时可选的参数
-加-c, (compile)只编译生成中间同名目标文件,不链接,生成.o文件,不产生执行文件
-加-o,(output)指定输出文件名,该文件为可执行文件,不加-o会默认生成filemame.out
举例:
gcc -c hello.c 编译生成hello.o文件
gcc -o hello hello.c 生成可执行文件hello

gcc -c a.c 编译成目标文件a.o
gcc a.c 生成执行文件a.exe
gcc -o a -c a.c 编译成目标文件a
gcc -o a a.c 生成执行文件a.exe
在a.c中引用test.c中的一个函数后:
gcc -c test.c 编译成目标文件test.o
gcc -c a.c 编译成目标文件a.o
gcc -o a test.o a.o 生成执行文件a.exe
gcc -o a test.o a.c 生成执行文件a.exe
gcc -o a test.c a.c 生成执行文件a.exe
总结:只要参数中有-c,总是生成目标文件;只要参数中无-c而只有-o,则总是生成执行文件。

-fno-builtin:允许定义函数的时候和C语言的内建函数重名。

-Wall:该选项能发现程序中一系列的常见错误警告。虽然GCC提供了非常丰富的警告,但前提是你已经启用了它们,否则它不会报告这些检测到的警告。

-ggdb:-ggdb产生的debug信息更倾向于给GDB使用的。如果用的GDB调试器,那么使用-ggdb选项,如果是其他调试器,则使用-g。

-m32:-m32 生成32位机器的汇编代码;-m64则生成64位机器汇编代码。

-I(大写i):指定头文件路径(相对路径或绝对路径,建议相对路径)。gcc/g++ 会先在当前目录查找你所制定的头文件,如果没有找到,他回到默认的头文件目录找,如果使用 -I 制定了目录,他会先在你所制定的目录查找, 然后再按常规的顺序去找。

-gstabs:-gstabs,以stabs格式声称调试信息,但是不包括gdb调试信息;-gstabs+,以stabs格式声称调试信息,并且包含仅供gdb使用的额外调试信息。

-nostdinc:不在标准系统文件夹寻找头文件,只在 -I 等参数指定的文件夹中搜索头文件。

-fno-stack-protector:禁用堆栈保护机制。

ld:ld 命令是二进制工具集 GNU Binutils 的一员,是 GNU 链接器,用于将目标文件与库链接为可执行文件或库文件。在上面的ld命令的作用就是分别生成了kernel和bootblock文件。

命令格式:ld [OPTIONS] OBJFILES-b <input-format>指定目标代码输入文件的格式
-Bstatic只使用静态库
-Bdynamic只使用动态库
-Bsymbolic把引用捆绑到共享库中的全局符号
-c <MRI-commandfile>, --mri-script=<MRI-commandfile>为与 MRI 链接器兼容,ld 接受由 MRI 命令语言编写的脚本文件
--cref创建跨引用表
-d,-dc,-dp即使指定了可重定位的输出文件(使用-r),也会为公共符号分配空间。脚本命令“FORCE_COMMON_ALLOCATION”具有相同的效果
-defsym在输出文件中创建指定的全局符号
-demangle在错误消息中还原符号名称
-e <entry>使用指定的符号作为程序的初始执行点
-E,--export-dynamic对于ELF格式文件,创建动态链接的可执行文件时,把所有符号添加到动态符号表
-f <name>, --auxiliary=<name>对于 ELF 格式共享对象,设置 DT_AUXILIARY 名称
-F <name>, --filter=<name>对于ELF格式共享对象,设置 DT_FILTER 名称。这告诉动态链接器,正在创建的共享对象的符号表应该用作共享对象名称的符号表的筛选器。
-g被忽略。用于提供和其他工具的兼容性
-h对于 ELF 格式共享对象,设置 DT_SONAME 名称
-I<file>, -dynamic-linker <file>, --dynamic-linker=<file>指定动态链接器。这仅在生成依赖动态链接库的 ELF 可执行文件时才有意义。默认的动态链接器通常是正确的,除非您知道正在做什么,否则不要使用该选项。
-l <namespec>, --library=<namespec>把指定的库文件添加到要链接的文件清单
-L <searchdir>, --library-path=searchdir把指定的路径添加添加到搜索库的目录清单
-M, --print-map显示链接映射,用于诊断目的
-Map=<mapfile>:将链接映射输出到指定的文件
-m <emulation>模拟指定的链接器
-N,--omagic指定读取/写入文本和数据段
-n,--nmagic关闭节的页面对齐,并禁用对共享库的链接。如果输出格式支持Unix样式的幻数,则将输出标记为"NMAGIC"
-noinhibit-exec生成输出文件,即使出现非致命链接错误。通常,如果链接器在链接过程中遇到错误,它将不会生成输出文件。
-no-keep-memoryld 通常在内存中缓存输入文件的符号表来优化内存使用速度。此选项告诉 ld 不要缓存符号表。当链接大型可执行文件时,如果ld耗尽内存空间,则可能需要使用该选项
-O <level>对于非零的优化等级,ld将优化输出。此操作会比较耗时,应该在生成最终的结果时使用。
-o <output>, --output=<output>指定输出文件的名称
-oformat=<output-format>指定输出文件的二进制格式
-R <filename>,--just-symbols=<filename>从指定的文件读取符号名称和地址
-r,--relocatable生成可重定位的输出(称为部分连接)
-rpath=<dir>把指定的目录添加到运行时库搜索路径
-rpath-link=<dir>指定搜索运行时共享库的目录
-S,--strip-debug忽略来自输出文件的调试器符号信息
-s,--strip-all忽略来自输出文件的所有符号信息
-shared, -Bshareable创建共享库
-split-by-file[=size]为每个目标文件在输出文件中创建额外的段大小达到size。size默认为1
-split-by-reloc[=count]按照指定的长度在输出文件中创建额外的段
--section-start=<sectionname>=<org>在输出文件中指定的地址定位指定的段
-T <scriptfile>, --script=<scriptfile>使用 scriptfile 作为链接器脚本。此脚本将替换 ld 的默认链接器脚本(而不是添加到其中),因此脚本必须指定输出文件所需的所有内容。如果当前目录中不存在脚本文件,ld 会在 -L 选项指定的目录中查找
-Ttext=<org>使用指定的地址作为文本段的起始点
-Tdata=<org>使用指定的地址作为数据段的起始点
-Tbss=<org>使用指定的地址作为bss段的起始点
-t,--trace在处理输入文件时显示它们的名称
-u <symbol>, --undefined=<symbol>强制指定符号在输出文件中作为未定义符号
-v, -V, --version示ld版本号
-warn-common当一个通用符号和另一个通用符号结合时发出警告
-warn-constructors如果没有使用任何全局构造器,则发出警告
-warn-once对于每个未定义的符号只发出一次警告
-warn-section-align如果为了对齐而改动了输出段地址,则发出警告
--whole-archive对于指定的存档文件,在存档中包含所有文件
-X, --discard-locals删除所有本地临时符号
-x, --discard-al删除所有本地符号

实例:ld -m elf_i386 -nostdlib -T tools/kernel.ld -o bin/kernel obj/kern/init/init.o

-m elf_i386:指定模拟仿真链接器为 elf_i386;-nostdlib:只搜索命令行上显式指定的库目录,在链接器脚本中指定的库目录(包括在命令行中指定的链接器脚本)将被忽略;-T tools/kernel.ld:指定链接器的脚本为 tools 目录下的 kernel.ld 文件;-o bin/kernel obj/kern/init/init.o:将转化好的可执行文件放到 bin/ 目录下,并命名为 kernel,链接的文件为 obj/kern/init/ 目录中的 init.o。

dd:用指定大小的块拷贝一个文件,并在拷贝的同时进行指定的转换。上面的dd命令将dev/zero,,bin/bootblock,bin/kernel 写入到bin/ucore.img。

注意:指定数字的地方若以下列字符结尾,则乘以相应的数字:b=512;c=1;k=1024;w=2
参数注释:
if=文件名:输入文件名,缺省为标准输入。即指定源文件。< if=input file >
of=文件名:输出文件名,缺省为标准输出。即指定目的文件。< of=output file >
ibs=bytes:一次读入bytes个字节,即指定一个块大小为bytes个字节。
obs=bytes:一次输出bytes个字节,即指定一个块大小为bytes个字节。
bs=bytes:同时设置读入/输出的块大小为bytes个字节。
cbs=bytes:一次转换bytes个字节,即指定转换缓冲区大小。
skip=blocks:从输入文件开头跳过blocks个块后再开始复制。
seek=blocks:从输出文件开头跳过blocks个块后再开始复制。
注意:通常只用当输出文件是磁盘或磁带时才有效,即备份到磁盘或磁带时才有效。
count=blocks:仅拷贝blocks个块,块大小等于ibs指定的字节数。
conv=conversion:用指定的参数转换文件。
ascii:转换ebcdic为ascii
ebcdic:转换ascii为ebcdic
ibm:转换ascii为alternate ebcdic
block:把每一行转换为长度为cbs,不足部分用空格填充
unblock:使每一行的长度都为cbs,不足部分用空格填充
lcase:把大写字符转换为小写字符
ucase:把小写字符转换为大写字符
swab:交换输入的每对字节
noerror:出错时不停止
notrunc:不截短输出文件
sync:将每个输入块填充到ibs个字节,不足部分用空(NUL)字符补齐。

实例:dd if=/dev/zero of=bin/ucore.img count=10000
从 /dev/zero 文件中读取内容来替代标准输入stdin,将输出的内容写入到 bin 目录下的 ucore.img,替代标准的输出 stdout,复制 10000 个输入块。

2.一个被系统认为是符合规范的硬盘主引导扇区的特征是什么?

参考其它博文,发现都是从sign.c文件里面找到如下代码段:

 buf[510] = 0x55;buf[511] = 0xAA;FILE *ofp = fopen(argv[2], "wb+");size = fwrite(buf, 1, 512, ofp);if (size != 512) {fprintf(stderr, "write '%s' error, size is %d.\n", argv[2], size);return -1;}

判断出硬盘主引导扇区的特征是大小为512字节,并且最后两个字节是0x55AA。

练习2:使用qemu执行并调试lab1中的软件。

通过查看Makefile,发现lab1-mon内容如下:

 lab1-mon: $(UCOREIMG)$(V)$(TERMINAL) -e "$(QEMU) -S -s -d in_asm -D $(BINDIR)/q.log -m    202 onitor stdio -hda $< -serial null"$(V)sleep 2$(V)$(TERMINAL) -e "gdb -q -x tools/lab1init"

第一条指令是让qemu把执行的指令log信息记录下来放入q.log,第二条是用gdb调试正在执行的Bootloader。

查看 labcodes_answer/lab1_result/tools/lab1init 文件内容:

file bin/kernel
target remote :1234
set architecture i8086
b *0x7c00
continue
x /2i $pc

这里都是gdb能识别的指令,第一行是加载bin/kernel,第二行是与qemu连接,第三行设置当前调试的CPU是8086,第四行是在0x7c00处设置一个断点,第五行继续运行,第六行是显示PC处的两条指令。

1.从CPU加电后执行的第一条指令开始,单步跟踪BIOS的执行。

关于qemu这里摘录实验指导书的内容:

运行参数
如果 qemu 使用的是默认 /usr/local/bin 安装路径,则在命令行中可以直接使用 qemu 命令运行程序。qemu 运行可以有多参数,格式如:qemu [options] [disk_image]
其中 disk_image 即硬盘镜像文件。部分参数说明:`-hda file'        `-hdb file' `-hdc file' `-hdd file'使用 file  作为硬盘0、1、2、3镜像。
`-fda file'  `-fdb file'使用 file  作为软盘镜像,可以使用 /dev/fd0 作为 file 来使用主机软盘。
`-cdrom file'使用 file  作为光盘镜像,可以使用 /dev/cdrom 作为 file 来使用主机 cd-rom。
`-boot [a|c|d]'从软盘(a)、光盘(c)、硬盘启动(d),默认硬盘启动。
`-snapshot'写入临时文件而不写回磁盘镜像,可以使用 C-a s 来强制写回。
`-m megs'设置虚拟内存为 msg M字节,默认为 128M 字节。
`-smp n'设置为有 n 个 CPU 的 SMP 系统。以 PC 为目标机,最多支持 255 个 CPU。
`-nographic'禁止使用图形输出。
其他:可用的主机设备 dev 例如:vc虚拟终端。null空设备/dev/XXX使用主机的 tty。file: filename将输出写入到文件 filename 中。stdio标准输入/输出。pipe:pipename命令管道 pipename。等。使用 dev 设备的命令如:`-serial dev'重定向虚拟串口到主机设备 dev 中。`-parallel dev'重定向虚拟并口到主机设备 dev 中。`-monitor dev'重定向 monitor 到主机设备 dev 中。其他参数:`-s'等待 gdb 连接到端口 1234。`-p port'改变 gdb 连接端口到 port。`-S'在启动时不启动 CPU, 需要在 monitor 中输入 'c',才能让qemu继续模拟工作。`-d'输出日志到 qemu.log 文件。

在可以使用 gdb 调试程序之前,必须使用 -g 或 –ggdb编译选项编译源文件。gdb 调试程序时通常使用的命令:gdb progname
这里是常用的一些指令:https://blog.csdn.net/q357010621/article/details/80244789

从lab0的内容中可以找到,要进入bin目录下,执行qemu -S -s -hda ./ucore.img -monitor stdio指令。

再打开一个terminal,输入以下指令:

gdb kernel
target remote 127.0.0.1:1234
si
x /2i $pc

si是stepi的简写,单步执行一条机械指令(包括进入函数)。

2.在初始化位置0x7c00设置实地址断点,测试断点正常。

(gdb) b*0x7c00
Breakpoint 1 at 0x7c00
(gdb) c
Continuing.Breakpoint 1, 0x00007c00 in ?? ()
(gdb) x /2i $pc
=> 0x7c00:  cli    0x7c01:  cld

3.从0x7c00开始跟踪代码运行,将单步跟踪反汇编得到的代码与bootasm.S和 bootblock.asm进行比较。

反汇编(Disassembly):把目标代码转为汇编代码的过程,也可以说是把机器语言转换为汇编语言代码、低级转高级的意思。

(gdb) b*0x7c00
Breakpoint 1 at 0x7c00
(gdb) c
Continuing.Breakpoint 1, 0x00007c00 in ?? ()
(gdb) x /2i $pc
=> 0x7c00:  cli    0x7c01:  cld
(gdb) si
0x00007c01 in ?? ()
(gdb) si
0x00007c02 in ?? ()
(gdb) x /10i $pc
=> 0x7c02:  xor    %eax,%eax0x7c04: mov    %eax,%ds0x7c06:  mov    %eax,%es0x7c08:  mov    %eax,%ss0x7c0a:  in     $0x64,%al0x7c0c: test   $0x2,%al0x7c0e:  jne    0x7c0a0x7c10:    mov    $0xd1,%al0x7c12: out    %al,$0x640x7c14: in     $0x64,%al

与bootasm.S和 bootblock.asm进行比较后发现相应位置的代码一致。

bootblock.S:

#include <asm.h># Start the CPU: switch to 32-bit protected mode, jump into C.
# The BIOS loads this code from the first sector of the hard disk into
# memory at physical address 0x7c00 and starts executing in real mode
# with %cs=0 %ip=7c00..set PROT_MODE_CSEG,        0x8                     # kernel code segment selector
.set PROT_MODE_DSEG,        0x10                    # kernel data segment selector
.set CR0_PE_ON,             0x1                     # protected mode enable flag# start address should be 0:7c00, in real mode, the beginning address of the running bootloader
.globl start
start:
.code16                                             # Assemble for 16-bit modecli                                             # Disable interruptscld                                             # String operations increment# Set up the important data segment registers (DS, ES, SS).xorw %ax, %ax                                   # Segment number zeromovw %ax, %ds                                   # -> Data Segmentmovw %ax, %es                                   # -> Extra Segmentmovw %ax, %ss                                   # -> Stack Segment# Enable A20:#  For backwards compatibility with the earliest PCs, physical#  address line 20 is tied low, so that addresses higher than#  1MB wrap around to zero by default. This code undoes this.
seta20.1:inb $0x64, %al                                  # Wait for not busy(8042 input buffer empty).testb $0x2, %aljnz seta20.1movb $0xd1, %al                                 # 0xd1 -> port 0x64outb %al, $0x64                                 # 0xd1 means: write data to 8042's P2 portseta20.2:inb $0x64, %al                                  # Wait for not busy(8042 input buffer empty).testb $0x2, %aljnz seta20.2movb $0xdf, %al                                 # 0xdf -> port 0x60outb %al, $0x60                                 # 0xdf = 11011111, means set P2's A20 bit(the 1 bit) to 1# Switch from real to protected mode, using a bootstrap GDT# and segment translation that makes virtual addresses# identical to physical addresses, so that the# effective memory map does not change during the switch.lgdt gdtdescmovl %cr0, %eaxorl $CR0_PE_ON, %eaxmovl %eax, %cr0# Jump to next instruction, but in 32-bit code segment.# Switches processor into 32-bit mode.ljmp $PROT_MODE_CSEG, $protcseg.code32                                             # Assemble for 32-bit mode
protcseg:# Set up the protected-mode data segment registersmovw $PROT_MODE_DSEG, %ax                       # Our data segment selectormovw %ax, %ds                                   # -> DS: Data Segmentmovw %ax, %es                                   # -> ES: Extra Segmentmovw %ax, %fs                                   # -> FSmovw %ax, %gs                                   # -> GSmovw %ax, %ss                                   # -> SS: Stack Segment# Set up the stack pointer and call into C. The stack region is from 0--start(0x7c00)movl $0x0, %ebpmovl $start, %espcall bootmain# If bootmain returns (it shouldn't), loop.
spin:jmp spin# Bootstrap GDT
.p2align 2                                          # force 4 byte alignment
gdt:SEG_NULLASM                                     # null segSEG_ASM(STA_X|STA_R, 0x0, 0xffffffff)           # code seg for bootloader and kernelSEG_ASM(STA_W, 0x0, 0xffffffff)                 # data seg for bootloader and kernelgdtdesc:.word 0x17                                      # sizeof(gdt) - 1.long gdt                                       # address gdt

bootblock.asm:


obj/bootblock.o:     file format elf32-i386Disassembly of section .text:00007c00 <start>:# start address should be 0:7c00, in real mode, the beginning address of the running bootloader
.globl start
start:
.code16                                             # Assemble for 16-bit modecli                                             # Disable interrupts7c00: fa                      cli    cld                                             # String operations increment7c01:   fc                      cld    # Set up the important data segment registers (DS, ES, SS).xorw %ax, %ax                                   # Segment number zero7c02:    31 c0                   xor    %eax,%eaxmovw %ax, %ds                                   # -> Data Segment7c04:   8e d8                   mov    %eax,%dsmovw %ax, %es                                   # -> Extra Segment7c06:   8e c0                   mov    %eax,%esmovw %ax, %ss                                   # -> Stack Segment7c08:   8e d0                   mov    %eax,%ss00007c0a <seta20.1>:# Enable A20:#  For backwards compatibility with the earliest PCs, physical#  address line 20 is tied low, so that addresses higher than#  1MB wrap around to zero by default. This code undoes this.
seta20.1:inb $0x64, %al                                  # Wait for not busy(8042 input buffer empty).7c0a: e4 64                   in     $0x64,%altestb $0x2, %al7c0c:    a8 02                   test   $0x2,%aljnz seta20.17c0e:    75 fa                   jne    7c0a <seta20.1>movb $0xd1, %al                                 # 0xd1 -> port 0x647c10: b0 d1                   mov    $0xd1,%aloutb %al, $0x64                                 # 0xd1 means: write data to 8042's P2 port7c12:    e6 64                   out    %al,$0x6400007c14 <seta20.2>:seta20.2:inb $0x64, %al                                  # Wait for not busy(8042 input buffer empty).7c14:   e4 64                   in     $0x64,%altestb $0x2, %al7c16:    a8 02                   test   $0x2,%aljnz seta20.27c18:    75 fa                   jne    7c14 <seta20.2>movb $0xdf, %al                                 # 0xdf -> port 0x607c1a: b0 df                   mov    $0xdf,%aloutb %al, $0x60                                 # 0xdf = 11011111, means set P2's A20 bit(the 1 bit) to 17c1c:    e6 60                   out    %al,$0x60......

4.自己找一个bootloader或内核中的代码位置,设置断点并进行测试。

(gdb) b* 0x7c14
Breakpoint 2 at 0x7c14
(gdb) c
Continuing.Breakpoint 2, 0x00007c14 in ?? ()
(gdb) x /2i $pc
=> 0x7c14:  in     $0x64,%al0x7c16: test   $0x2,%al

参考

  • [从 0 开始写一个操作系统] 六、编译过程分析
  • linux命令总结dd命令详解
  • Linux 命令(65)—— ld 命令
  • 操作系统Lab1实验

学堂在线-清华大学-操作系统实验Lab1【练习1-2】相关推荐

  1. 学堂在线-清华大学-操作系统实验Lab1【练习5-6】

    练习5:实现函数调用堆栈跟踪函数 (需要编程) 我们需要在lab1中完成kdebug.c中函数print_stackframe的实现,可以通过函数print_stackframe来跟踪函数调用堆栈中记 ...

  2. 学堂在线-清华大学-操作系统实验Lab1【练习3-4】

    练习3:分析bootloader进入保护模式的过程. BIOS将通过读取硬盘主引导扇区到内存,并转跳到对应内存中的位置执行bootloader.请分析bootloader是如何完成从实模式进入保护模式 ...

  3. 学堂在线_操作系统_notes_第0-2讲_OS概述、OS实验环境准备

    学堂在线_操作系统_notes_第0-2讲_OS概述.OS实验环境准备 - 20220626.No.1821 - 操作系统OS 综合了 C语言 + 数据结构与算法DSA + 计算机组成. OS 是 控 ...

  4. 北航linux内核编译及烧录实验报告,北航操作系统实验Lab1笔记

    Loading... # 北航操作系统实验Lab1 ## Exercise 1.1 - **修改交叉编译路径为 `/OSLAB/compiler/usr/bin/mips_4KC-`** ![ex1_ ...

  5. 清华操作系统实验lab1

    第一次写的lab1练习1太冗杂,没有重点,理解不到位,后续进一步研究后感觉务必重新写一篇...... [练习1.1] 操作系统镜像文件 ucore.img 是如何一步一步生成的 生成ucore.img ...

  6. 计算机控制系统期末测试,学堂在线计算机操作系统考试题及答案

    一.单项选择题(每题1分,共20分)(更多试题及答案,尽在优题宝) 1.操作系统的发展过程是(  C    ) A.原始操作系统,管理程序,操作系统 B.原始操作系统,操作系统,管理程序 C.管理程序 ...

  7. MIT操作系统实验lab1(pingpong案例:附代码、详解)

    1.题目描述:在xv6上实现pingpong程序,即两个进程在管道两侧来回通信.父进程将"ping"写入管道,子进程从管道将其读出并打印<pid>:received p ...

  8. ucore操作系统实验笔记 - Lab1

    最近一直都在跟清华大学的操作系统课程,这个课程最大的特点是有一系列可以实战的操作系统实验.这些实验总共有8个,我在这里记录实验中的一些心得和总结. Task1 这个Task主要是为了熟悉Makfile ...

  9. 中断处理过程示意图_ucore操作系统实验笔记 - Lab1

    最近一直都在跟清华大学的操作系统课程,这个课程最大的特点是有一系列可以实战的操作系统实验.这些实验总共有8个,我在这里记录实验中的一些心得和总结. Task1 这个Task主要是为了熟悉Makfile ...

最新文章

  1. 基于光照的物理模型(一)
  2. 一文看懂最近70年的人工智能简史!中国已经领先全球!
  3. linux fdisk等命令,Linux fdisk命令操作磁盘(添加、删除、转换分区等)
  4. 13.jQuery选择器
  5. 传智C++课程笔记-1
  6. 静态链表和动态链表 区别
  7. 自制串口示波器小工具
  8. jdbc oracle 删除数据库连接,如何使用JDBC API从Oracle数据库中删除表?
  9. cp和scp复制命令
  10. VBA连接MySQL数据库以及ODBC的配置(ODBC版本和MySQL版本如果不匹配会出现驱动和应用程序的错误)...
  11. Winform使用FTP实现自动更新
  12. 架构 理论 定律 总结
  13. transition天坑
  14. c语言排快速排序过程,C语言中快速排序法怎么排
  15. 阿里双十一,3分01秒破百亿;乐视网称贾跃亭无力履行承诺;法乐第未来宣布解职CFO和CTO丨价值早报
  16. 5.2 PMBOK--收集需求
  17. [渝粤教育] 中国矿业大学 货币金融学 参考 资料
  18. anki怎么设置学习计划_Anki:用这套记忆卡片,学习一切你背不下来的知识 #iOS #Android #PC...
  19. 计算机 smb共享,文件大师SMB共享使用方法
  20. java什么是线程安全_什么是线程安全?

热门文章

  1. handle与handler的区别
  2. Python实战之小说下载神器(二)整本小说下载:看小说不用这个程序,我实在替你感到可惜*(小说爱好者必备)
  3. 新开课day21+day22总结
  4. 证明:不同特征值对应的特征向量线性无关
  5. 打开5555调试端口
  6. 一夜之间收到上百条短信,账户空了... 这种诈骗方式的背后技术原理
  7. 强制弹窗被判刑,内因是“不知”还是“不愿”守法?
  8. 最短路径问题(Dijkstra常用用法总结)
  9. IMP ORA-20005: object statistics are locked(二)
  10. html div布局位置横,div位置布局 CSS实现DIV居中对齐 div居右对齐 div居左对齐