课程地址: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. @Import 源码解析

    转发请注明出处: @Import通过快速导入的方式实现把实例加入spring的IOC容器中:一般@EnableXXX注解是通过@Import实现具体的功能(@EnableXXX注解上加个@Import ...

  2. AI毕业设计生成器(基于AI大模型技术开发)

    这是一个辅助生成计算机毕业设计的工具,可以自动完成毕业设计的源码.它基于几百个github上面开源的java和python项目,运用tengsorflow技术,训练出了AI大模型.基本实现了计算机毕业 ...

  3. zookeeper源码(01)集群启动

    本文介绍一下zookeeper-3.5.7集群安装. 解压安装 tar zxf apache-zookeeper-3.5.7-bin.tar.gz 创建数据.日志目录: mv apache-zooke ...

  4. 9 时序数据库M3DB架构与原理

    一.M3DB介绍 M3DB是Uber开源的一款分布式时序数据库,已在Uber内部使用多年.M3DB有以下特性: 分布式的时序数据库,可以水平扩展存储. 支持Pormetheus的查询语言PromQL, ...

  5. 问题--VSCODE编写C含scanf无终端跳出

    1.问题 在VSCODE中编写C程序,用到scanf输入时,发现无终端输入数据 2.解决方法 在设置里搜索RunInTerminal,勾选该选项即可

  6. [转帖]Linux nice和renice命令:改变进程优先级

    https://c.biancheng.net/view/1074.html 当 Linux 内核尝试决定哪些运行中的进程可以访问 CPU 时,其中一个需要考虑的因素就是进程优先级的值(也称为 nic ...

  7. [转帖]基于MySQL8.0存储过程实现myawr平台的top sql功能

    概述 众所周知,MySQL数据库中的performance_schema的事件统计表中的统计数据计算的是累计值,如果想要计算某段时间的TOP SQL是不行的,这里考虑用函数定期取值存进中间表定期将累计 ...

  8. [转帖]TiDB 中的各种超时

    https://docs.pingcap.com/zh/tidb/stable/dev-guide-timeouts-in-tidb 本章将介绍 TiDB 中的各种超时,为排查错误提供依据. GC 超 ...

  9. 全球 IPv4 耗尽,下个月开始收费!

    哈喽大家好,我是咸鱼 IPv4(Internet Protocol version 4)是互联网上使用最广泛的网络层协议之一,于1981年在 RFC 791 中发布,它定义了 32 位的IP地址结构和 ...

  10. 深入浅出RPC服务 | 不同层的网络协议

    导读: 本系列文章从RPC产生的历史背景开始讲解,涉及RPC核心原理.RPC实现.JSF的实现等,通过图文类比的方式剖析它的内部世界,让大家对RPC的设计思想有一个宏观的认识. 作者:王禹展   京东 ...