BUAA-OS-LAB1
BUAA OS LAB1 实验报告
Thinking
Thinking 1.1
Q:
在阅读附录中的编译链接详解以及本章内容后,尝试分别使用实验环境中 的原生 x86 工具链(gcc、ld、readelf、objdump 等)和 MIPS 交叉编译工具链(带有 mips-linux-gnu- 前缀,如 mips-linux-gnu-gcc、mips-linux-gnu-ld),重复其中的编 译和解析过程,观察相应的结果,并解释其中向objdump传入的参数的含义。
A:
编写程序hello.c
1 |
|
这里只截取.text部分
x86 .o
1 | am.o: 文件格式 elf64-x86-64 |
x86
1 | a: 文件格式 elf64-x86-64 |
mips .o
1 | a.o: 文件格式 elf32-tradbigmips |
mips
1 | a: 文件格式 elf32-tradbigmips |
objdump 格式:
objdump -DS 要反汇编的目标文件名 > 导出文本文件名
-D:反汇编所有的section;
-d:反汇编那些特定指令机器码的section;
-S :尽可能反汇编出源代码,尤其当编译的时候指定了-g 这种调试参数时,效果比较明显,隐含了-d参数;
-s:显示指定section的完整内容。默认所有的非空section都会被显示。
Thinking 1.2
Q:
• 尝试使用我们编写的readelf程序,解析之前在target目录下生成的内核ELF文 件。
• 也许你会发现我们编写的readelf程序是不能解析readelf 文件本身的,而我们刚 才介绍的系统工具readelf 则可以解析,这是为什么呢?(提示:尝试使用readelf-h,并阅读tools/readelf 目录下的 Makefile,观察 readelf 与 hello 的不同)
A:
运行 ./tools/readelf/readelf ./target/mos 指令得:
1 | 0:0x0 |
hello:
1 | git@23371468:~/23371468/tools/readelf (lab1)$ readelf -h hello |
readelf:
1 | git@23371468:~/23371468/tools/readelf (lab1)$ readelf -h readelf |
Makefile 中对于 hello readelf 的生成分别是:
1 | readelf: main.o readelf.o |
hello 是静态的 -static,将生成的可执行程序识别为 “executable” + “statically linked”,
而gcc 默认情况下生成 PIE,会将生成的可执行程序直接识别为 “shared object” + “dynamically linked”,PIE 文件设计为支持 地址空间布局随机化
因此与hello不同,他不能解析自己。
Thinking 1.3
Q:
在理论课上我们了解到,MIPS体系结构上电时,启动入口地址为0xBFC00000 (其实启动入口地址是根据具体型号而定的,由硬件逻辑确定,也有可能不是这个地址,但 一定是一个确定的地址),但实验操作系统的内核入口并没有放在上电启动地址,而是按照 内存布局图放置。思考为什么这样放置内核还能保证内核入口被正确跳转到? (提示:思考实验中启动过程的两阶段分别由谁执行。)
A:
在MIPS体系结构中,虽然硬件上电后的启动入口地址固定为0xBFC00000(或其他硬件指定地址),但操作系统内核仍能正确跳转到其实际入口地址的原因在于启动过程分为两个阶段:
- 第一阶段:Bootloader执行
硬件启动后,首先执行位于0xBFC00000地址的代码(通常为Bootloader或固件)。这段代码负责:- 初始化硬件(如CPU、内存控制器等)。
- 将操作系统内核从存储设备(如ROM)加载到内存中预定义的位置(由内存布局图决定)。
- 通过跳转指令(如
jr
或jal
)将控制权移交到内核的实际入口地址。
- 第二阶段:内核执行
内核的入口地址由链接脚本kernel.lds
定义,通常位于内存布局图中。Bootloader通过硬编码或动态加载的方式跳转到该地址,从而启动内核。
lab0难点
T1
Elf32_Ehdr是ELF文件头;Elf32_Shdr是section节头表表项
一个节头表由多个节头表表项组成;
文件头的e_shoff是节头表所在处与文件头的偏移;
节头表表项的sh_offset是节的文件内偏移;
文件头的e_shentsize是节头表表项的大小;
1 | //...... |
T2
通过查看内存布局图,能找到.text节的加载地址,.data和 .bss只需要紧随其后即可。
注意 LinkerScript文件编辑时“=”两边的空格
1 | SECTIONS { |
T3
从内存示意图可见栈起始为
0x80400000
这里做一个提醒,请注意栈的增长方向
1 |
|
T4
注意负数的取值,和各个判断变量的初始化,这里考虑0-和-0的情况。
1 | for (;;) { |
实验体会总结
Lab1主要学习
操作系统启动的基本流程
ELF文件的结构和功能
完成一个printf
函数的书写
在完成第一部分时,主要的时间花费在了理解定义的结构体的各个属性,以及指针的使用上,而地址的分配和start.S
的书写则十分简单。
第二部分的printk书写的时候需要先理清解析函数的执行顺序,对前置函数的参数需要足够熟悉。
这次实验学习了多种文件的操作包括lds,c,makefile,shell,需要多种文件之间的相互配合,这锻炼了我们的系统能力和综合能力,为之后的实验奠定了良好的基础。
附录
OS内存地址图示
1 | /* |