LAB1_Part2 The Boot Loader
前言
记录一下自己的学习过程
实验内容翻译:
https://gitee.com/cherrydance/mit6.828
该翻译仅供参考
练习4
练习4. 请阅读有关在C语言中使用指针的资料。C语言的最佳参考资料是Brian Kernighan和Dennis Ritchie所著的《C程序设计语言》(通常称为“K&R”)。
请阅读K&R的5.1节(指针和地址)到5.5节(字符指针和函数)。然后下载pointers.c的代码,运行它,并确保你理解所有打印出的值的来源。特别是要确保你理解打印行1和6中指针地址的来源,以及打印行2到4中所有值是如何得到的,以及为什么打印行5中的值似乎被破坏了。
在C语言中,还有其他关于指针的参考资料(例如,Ted Jensen的教程引用了大量K&R的内容),尽管没有被强烈推荐。
警告:除非你已经对C语言非常熟悉,否则不要跳过或仅是浏览这个阅读练习。如果你对C语言中的指针不真正理解,那么在接下来的实验中你将遭受难以言喻的痛苦和困苦,最终只能通过艰难的方式来理解它们。相信我们,你不想知道什么是“艰难的方式”。
先下载pointers.c的代码https://pdos.csail.mit.edu/6.828/2018/labs/lab1/pointers.c
int a[4];
int *b = malloc(16);
int *c;
int i;
printf("1: a = %p, b = %p, c = %p\n", a, b, c);
打印a,b,c的地址
c = a;
for (i = 0; i < 4; i++)
a[i] = 100 + i;
c[0] = 200;
printf("2: a[0] = %d, a[1] = %d, a[2] = %d, a[3] = %d\n",
a[0], a[1], a[2], a[3]);
将指针a赋值给c,所以c和a指向同一块地址
四个值分别为a[0] =200, a[1] = 101, a[2] = 102, a[3] = 103
c[1] = 300;
*(c + 2) = 301;
3[c] = 302;
printf("3: a[0] = %d, a[1] = %d, a[2] = %d, a[3] = %d\n",
a[0], a[1], a[2], a[3]);
将c[1]的值改为300,*(c+2)=c[2],3[c]=c[3]
所以四个值分别为a[0] =200, a[1] = 300, a[2] = 301, a[3] = 302
c = c + 1;
*c = 400;
printf("4: a[0] = %d, a[1] = %d, a[2] = %d, a[3] = %d\n",
a[0], a[1], a[2], a[3]);
c=c+1,其实就是将指针c从a[0]的地址变成指向a[1]
所以四个值分别为a[0] =200, a[1] = 400, a[2] = 301, a[3] = 302
c = (int *) ((char *) c + 1);
*c = 500;
printf("5: a[0] = %d, a[1] = %d, a[2] = %d, a[3] = %d\n",
a[0], a[1], a[2], a[3]);
将指针c指向的int强制转换char,再加1,则它指的位置后移一个字节,再把指针转换回int*。首先我们要知道存储方式是小端存储。变化如下图所示:
所以四个值分别为a[0] =200, a[1] = 128144, a[2] = 256, a[3] = 302
b = (int *) a + 1;
c = (int *) ((char *) a + 1);
printf("6: a = %p, b = %p, c = %p\n", a, b, c);
}
b的地址为a加4,一个int大小的值,c为a+1。运行结果如下图所示:
练习5
在再次跟踪引导加载程序的前几条指令时,找到如果引导加载程序的链接地址设置错误会导致第一条指令“中断”或发生其他错误的指令。然后在boot/Makefrag中将链接地址更改为错误的值,运行make clean,使用make重新编译实验,并再次跟踪进入引导加载程序以查看发生了什么。之后不要忘记将链接地址改回,并再次运行make clean!
根据实验前文描述“在boot/Makefrag中传递"-Ttext 0x7C00"参数给链接器来设置链接地址”,我们得到练习需要更改的地方,将0x7c00改为0x7d00,运行make clean再make。使用gdb跟踪程序。首先查看boot.asm文件,发现地址由之前的0x7c00变成了0x7d00。
由于bios将bootloader默认加载在0x7c00处,所以还是在0x7c00设置断点。
我们使用si单步执行,可以发现前几条命令依然正确执行
在执行到命令ljmp $0x8,$0x7d32
之后就发生了错误
而且在另一个终端可以看到make qemu-gdb发生了严重错误。
练习6
我们可以使用GDB的x命令来检查内存。GDB手册中有详细的说明,但现在只需要知道x/Nx ADDR命令会打印在ADDR处的N个字的内存内容。(请注意,命令中的两个’x’都是小写。)警告:字的大小并没有统一的标准。在GNU汇编中,一个字是两个字节(xorw中的’w’代表字,表示2个字节)。
重置机器(退出QEMU/GDB并重新启动它们)。在BIOS进入引导加载程序的位置,检查0x00100000处的8个字的内存内容,然后在引导加载程序进入内核的位置再次检查。为什么它们是不同的?在第二个断点上有什么内容?(你不需要真正使用QEMU来回答这个问题,只需思考即可。)
在这之前先将链接地址改回0x7c00,make clean之后make。
根据要求打印出0x00100000处的内容,bios进入bootloader是0x7c00,进入内核的地址我们在之前的实验中已经得到,如果不记得了可以打开kern.asm文件,里面可以看到内核的开始地址是0x10000c。
我们打开kern.asm文件,查看开头几行,发现后面的内容就是下图红框中的内容。
为什么会出现这种情况呢?为什么在bios进入bootloader的时候是0,进入内核值就变了?
我想应该是因此bootmain函数中的循环将各个段加载到了指定位置,其中的详细需要更深层次的理解。
总结
Part2终于写完了!!!!!
后面继续加油。
有问题欢迎各位指出。