课程地址:https://www.bilibili.com/video/BV1f7411D7P6

一、实验目的

在PA2-1中,我们实现了了解了程序的装载和对指令的解码和执行,在这一章节我们将继续深入了解程序的装载,以不同于原本直接copy的方式实现对程序的装载。

二、实验步骤

首先我们熟悉ELF可执行目标文件的结构,即认识ELF文件和ELF Header:

其中比较重要的有:

  • 节头表,这个表记录了文件分为多少个节,每个节(如.symtab节)在文件中的什么地方;
  • 程序头表,这个表记录了文件的哪一部分应该被搬到内存的哪个位置,我们在后面装载的实现中需要用到程序头表给出的信息。
  • 符号表和字符串表,PA2-3符号解析中需要用到,它们记录了某全局变量或函数对应内存的哪个地址。

我们在这一章需要做的,就是改变原本程序的装载方式,通过对ELF文件中程序头表的解析来装载程序。

  • 原本的装载方式:把ELF文件和镜像文件分别copy到DAMdisk区和物理内存区;

  • 新的装载方式:通过NEMU的操作系统,来将(已经copy到)DAMdisk区的ELF文件装载到物理内存区。这意味着我们不仅要装载样例文件,还要先把操作系统(kernel文件)先装载到内存中。(kernel的装载就直接copy,同原本的样例镜像文件的装载方式)

装载的步骤为:

  1. 由kernel解析测试用例的ELF文件并装载,装载起始位置为0X100000;
  2. 由kernel调整CPU的EIP到装载的起始位置0X100000;

现在,我们可以进行一下要点的梳理

  • 现阶段哪些文件是待装载的ELF文件?样例ELF文件。
  • ELF文件是由谁装载的?Kernel负责。
  • 现阶段在NEMU启动后待装载的ELF文件位于何处?Kernel的后面,0X100000处。
  • NEMU开机后执行的第一条指令是哪个地址?是哪个软件的指令?Kernel的起始地址,0X30000。
  • ELF文件被装载到哪个地址?0X100000。
  • 为了保证上述装载和执行地址的正确,需要修改Makefile。

接下来是重头戏,我们要实现对程序头表的解析:

  • 首先,我们可以通过readelf命令来观察需要装载的ELF文件的程序头表;

  • 上文我们提到,程序头表记录了文件的哪一部分应该被搬到内存的哪个位置,我们观察readelf给出的信息:

    可以发现Type为LOAD的节是需要装载的——ELF文件中从文件Offset开始位置,连续FileSiz个字节的内容需要被装载,装载到内存VirtAddr开始,连续MemSiz个字节的区域中,其中MemSiz可能大于FileSiz(.bss节的原因,这时候需要把内存中 VirtAddr + [FileSiz, MemSiz] 的区域清零)。

  • 好,我们现在进行装载。装载由kernel的loader程序来完成。其装载逻辑如下:

  • 通过阅读kernel/src/elf/elf.c处的代码(主要是loader()函数),我们可以再进行一次要点的梳理

    • Kernel中访问变量语句编译后对应什么指令?mov指令或者lea指令。

    • 这条指令由谁执行?访问的地址是多少?由cpu执行,访问地址需要看变量类型,但都在hw_mem[]数组地址的范围内。

    • 指令中的地址如何转换成对hw_mem[]的访问的?

      首先kernel里面的代码会编译成一条条指令(如memcpy会被编译成一条条mov指令),而这些指令操作的执行不是通过现实的机器来执行,而是像PA2-1一样,是依靠你自己实现的虚拟CPU来执行的,由你手搓的CPU进行指令的解码和执行。然后解码执行过程中,对地址的操作是由自己实现的operand_read/write来读/写的,operand_read/write调用了memory.c中封装好的读写函数进行转换(看最底层的hw_mem_read/write,这个时候的读写操作的解码执行才是现实CPU做的,要分清实机和虚拟机),避免了直接访问当前地址而造成的访问到了实际机器上的内存的问题。

      在这里我还要提醒自己:ELF文件放在实际机器可以靠实际机器的CPU运行,放在手搓虚拟机上就需要靠手搓的CPU运行。kernel也一样,这里的kernel也是一个img文件,它的执行环境决定了它需要靠你自己手搓的对ISA的实现上运行。一定要注意运行环境的不同!不然容易混糊,就像这一问。

  • 我们需要实现的代码部分位于该loader()函数中:

都到这一步了,我们来看看Kernel是如何执行loader()的:

kernel/start/start.S开始 --> 跳转到kernel/src/main.c中的init() --> 跳转到kernel/src/main.c中的init_cond()

--> 执行loader()函数,并由其返回可执行文件的EIP(通过ELF头得到) --> 得到EIP后我们就可以直接将它包装成函数指针,去跑装载好的测试用例。

整体的PA2-2代码要求如下:

  1. 修改testcase/MakefileLDFLAGS并make clean

    #LDFLAGS := -m elf_i386 -e start -Ttext=0x30000

    LDFLAGS := -m elf_i386 -e start -Ttext=0x100000

    -Ttext的功能就是指定程度段的起始地址。程序链接后的地址空间要与其运行环境的地址空间相匹配,现在将样例文件的起始地址指定为0x100000,那么在kernel根据ELF程序头表来装载文件的时候,这个样例文件程序头表项就会显示应该从0x100000处开始装载。

    可以看一下博文:https://blog.csdn.net/chuanzhilong/article/details/53020351

  2. 实现Kernel中的loader();该代码位于kernel/src/elf/elf.c

  3. 使用make test_pa-2-2执行测试用例并通过。

三、思考题

这章节没有明确需要回答的思考题,以下是理解PA2-2过程中一些给出的要点的解答:

  • 现阶段哪些文件是待装载的ELF文件?样例ELF文件。

  • ELF文件是由谁装载的?Kernel负责。

  • 现阶段在NEMU启动后待装载的ELF文件位于何处?Kernel的后面,0X100000处。

  • NEMU开机后执行的第一条指令是哪个地址?是哪个软件的指令?Kernel的起始地址,0X30000。

  • ELF文件被装载到哪个地址?0X100000。

  • Kernel中访问变量语句编译后对应什么指令?mov指令或者lea指令。

  • 这条指令由谁执行?访问的地址是多少?由cpu执行,访问地址需要看变量类型,但都在hw_mem[]数组地址的范围内。

  • 指令中的地址如何转换成对hw_mem[]的访问的?

    首先kernel里面的代码会编译成一条条指令(如memcpy会被编译成一条条mov指令),而这些指令操作的执行不是通过现实的机器来执行,而是像PA2-1一样,是依靠你自己实现的虚拟CPU来执行的,由你手搓的CPU进行指令的解码和执行。然后解码执行过程中,对地址的操作是由自己实现的operand_read/write来读/写的,operand_read/write调用了memory.c中封装好的读写函数进行转换(看最底层的hw_mem_read/write,这个时候的读写操作的解码执行才是现实CPU做的,要分清实机和虚拟机),避免了直接访问当前地址而造成的访问到了实际机器上的内存的问题。

NEMU PA 2-2 实验报告的更多相关文章

  1. ucoreOS_lab3 实验报告

    所有的实验报告将会在 Github 同步更新,更多内容请移步至Github:https://github.com/AngelKitty/review_the_national_post-graduat ...

  2. ucoreOS_lab2 实验报告

    所有的实验报告将会在 Github 同步更新,更多内容请移步至Github:https://github.com/AngelKitty/review_the_national_post-graduat ...

  3. JAVA实验报告及第七周总结

    JAVA第六周作业 实验报告五 第一题 1.设计一个类层次,定义一个抽象类--形状,其中包括有求形状的面积的抽象方法. 继承该抽象类定义三角型.矩形.圆. 分别创建一个三角形.矩形.圆存对象,将各类图 ...

  4. 第五次java实验报告

    Java实验报告 班级 计科二班 学号 20188437 姓名 何磊 完成时间2019/10/10 评分等级 实验四 类的继承 实验目的 理解抽象类与接口的使用: 了解包的作用,掌握包的设计方法. 实 ...

  5. 第七周总结&第五次实验报告

    学习总结 这周我们加深了对抽象类与接口的学习,获得的知识点也比上周多了许多,抽象类与接口很相似,就比如别人还没有做完的是交给你来做,而他那些样式都做好了,你只需要完善即可 但也有不同点. 区别点 抽象 ...

  6. 第七周课程总结 & 实验报告(五)

    第七周课程总结 一.抽象类与接口的应用 1.实例化 2.实际应用 ---模板设计(抽象类) ---制定标准(接口) 3.设计模式 ---工厂设计 ---代理设计 ---适配器设计 二.抽象类与接口之间 ...

  7. 第七周&实验报告五

    实验四 类的继承 •实验目的 •理解抽象类与接口的使用: •了解包的作用,掌握包的设计方法. •实验要求 •掌握使用抽象类的方法. •掌握使用系统接口的技术和创建自定义接口的方法. •了解 Java ...

  8. 2019JAVA第五次实验报告

    Java实验报告 班级 计科二班 学号 20188442 姓名 吴怡君 完成时间2019/10/11 评分等级 实验四 类的继承 实验目的 理解抽象类与接口的使用: 了解包的作用,掌握包的设计方法. ...

  9. 实验报告5&第七周课程总结

    实验四 类的继承 实验目的 理解抽象类与接口的使用: 了解包的作用,掌握包的设计方法. 实验要求 掌握使用抽象类的方法. 掌握使用系统接口的技术和创建自定义接口的方法. 了解 Java 系统包的结构. ...

  10. 北京电子科技学院(BESTI)实验报告5

    北京电子科技学院(BESTI)实验报告5 课程: 信息安全系统设计基础 班级:1452.1453 姓名:(按贡献大小排名) 郑凯杰.周恩德 学号:(按贡献大小排名) 20145314.20145217 ...

随机推荐

  1. SD Host控制器的datasheet

    SD-Host控制器的datasheet更多的是给嵌入式软件工作人员使用,datasheet中主要包含一些寄存器以及读写擦除流程 寄存器主要有: 控制寄存器 状态寄存器 配置寄存器 软件和硬件进行交互 ...

  2. Redis-有序集合-zset

  3. [转帖]是的你没看错,HTTP3来了

    https://www.jianshu.com/p/288ce6a8ab88 简介 很多小伙伴可能还沉浸在HTTP1.1的世界无法自拔,但是时代的洪流已经带领我们来到了HTTP3的世界了.是的,你在桥 ...

  4. [转帖]关于https://goproxy.cn,direct与https://proxy.golang.org的问题,国内无法访问https://proxy.golang.org设置了GOPROXY仍不可行

    关于https://goproxy.cn,direct与https://proxy.golang.org的问题,国内无法访问https://proxy.golang.org设置了GOPROXY仍不可行 ...

  5. [转帖]CPU的制造和概念

    https://plantegg.github.io/2021/06/01/CPU%E7%9A%84%E5%88%B6%E9%80%A0%E5%92%8C%E6%A6%82%E5%BF%B5/ 为了让 ...

  6. UOS关闭激活提示: Your system is not activated. Please activate as soon as possible for normal use.

    最近公司里面进行UOS的兼容性验证,但是系统总是会提示: Your system is not activated. Please activate as soon as possible for n ...

  7. NOI Linux 下 Geany 配置教程

    没有括号补全? 现在有自动括号补全了! 红色的 a.cpp 的意思是 a.cpp 没有保存. 现在来设置编译运行的快捷键. 不难推测 "%e" 是可执行文件的意思,"%f ...

  8. Spring Boot接口设计

    项目文件结构 编写示例代码 添加lombok的依赖 新建DemoController,用于提供RESTful接口.增加相关注解:@RestController,@RequestMapping(&quo ...

  9. 重新学习一下new Date()

    new Date()你知道多少 很多小伙伴可能都知道, Date是js中的一个内置对象,用于处理日期和时间. 当你调用 new Date() 时,它会创建一个新的日期(Date) 对象. 表示当前本地 ...

  10. 在K8S中,Pod创建过程包括什么?

    在Kubernetes(K8s)中,Pod的创建过程通常包括以下步骤: 提交Pod定义: 用户通过kubectl命令行工具或者调用API Server接口,提交一个包含Pod配置信息的YAML或JSO ...