Skip to content

Pa2

pa2-1

题目

1.使用hexdump命令查看测试用例的.img文件,所显示的.img文件的内容对应模拟内存的哪一个部分?指令在机器中表示的形式是什么?

  • 使用hexdump汇编观察发现,测试案例(img)文件的开头如下

  • 0000000 8ce9 0000 5500 e589 ec83 8310 087d 7f01 0000010 b807 0000 0000 2deb 45c7 02fc 0000 eb00 0000020 8b17 0845 f799 fc7d d089 c085 0775 00b8 0000030 0000 eb00 ff10 fc45 458b 3bfc 0845 e17c 0000040 01b8 0000 c900 55c3 e589 ec83 c710 fc45 0000050 0002 0000 2ceb 75ff e8fc ffa7 ffff c483 0000060 8504 74c0 8b1a 0845 452b 50fc 94e8 ffff 0000070 83ff 04c4 c085 0774 01b8 0000 eb00 ff10 0000080 fc45 458b 3bfc 0845 cc7c 00b8 0000 c900 0000090 55c3 e589 ec83 c710 fc45 0004 0000 1aeb 00000a0 75ff e8fc ff9f ffff c483 8304 01f8 0674 00000b0 01b8 0000 8200 4583 02fc 7d83 64fc e07e 00000c0 00b8 0000 8200 00b8 0000 c900 00c3 0000 00000d0 0000 0000 0000 0000 0000 0000 0000 0000 *

  • 结合pptimage-20230407174412698

  • 可以知道img文件的存储地址从0x0开始,即虚拟内存中RAM Disk的区域

  • 对测试案例的二进制可执行文件(.o)进行反汇编可以得到:

  • ``` 00030000 : 30000: e9 00 00 00 00 jmp 30005

    00030005

    : 30005: b8 00 00 00 00 mov $0x0,%eax 3000a: bb 00 00 00 00 mov $0x0,%ebx ```

  • 可以知道.o文件的地址从0x30000开始

  • 指令在机器中以二进制表示(但通常以十六进制来显示),指令由操作码和操作数组成。

  • 汇编与机器语言本质上是相同的,只不过用助记符来代替部分数字,便于阅读理解。

2.如果去掉instr_execute_2op()函数前面的static关键字会发生什么情况?为什么?

  • static有两种用法;
  • 声明静态变量(修饰局部变量)
  • 限制作用域(修饰群全局变量、函数等)
  • 本函数使用static是属于第二种用法,目的是为了隐藏函数,使得函数只对本文件可见,而不能在其他文件中被引用。
  • instr_execute_2op()作为make_instr_impl_2op()中的一个函数存在,而不同指令在不同文件中都会有instr_execute_2op()这一函数,如果去掉static那么相当与在不同文件中对同一函数做出了不同定义
  • 因此会出现函数重定义的问题而报错。

3.为什么test-floatfail?以后在写和浮点数相关的程序的时候要注意什么?

  • 测试代码如下:

  • ```c int main() {

    float a = 1.2, b = 1;
    float c = a + b;
    if (c == 2.2)
        ;
    else
        HIT_BAD_TRAP;
    c = a * b;
    if (c == 1.2)
        ;
    else
        HIT_BAD_TRAP;
    
    c = a / b;
    if (c == 1.2)
        ;
    else
        HIT_BAD_TRAP;
    
    c = a - b;
    if (c == 0.2) // this will fail, and also fails for native program, interesting, can be used as a quiz
        ;
    else
        HIT_BAD_TRAP;
    
    HIT_GOOD_TRAP;
    return 0;
    

    } ```

  • 十进制有限位小数不一定是二进制有限位小数,因此二进制可能不能精确表示十进制小数

  • 比如代码中的1.2转化为二进制是1.0011001100110011001100110011...也就是说无法精确表示,需要进行舍入。

  • 计算1.2-1时在对阶、做差之后需要进行浮点数规格化以及舍入,会造成精度上的损失,从而导致1.2-1!=0.2因此会失败

  • 这种情况在浮点数计算、表较中经常出现,应该使用一个很小的值来比较是否相同,如:1.2-1-0.2<=10^-8,从而避免精度损失造成的问题

总结

  • 这一部分相对pa1来说难度大了不少,阅读、理解框架代码就花费了很多的时间,由于阅读不够全面、理解错误还造成了多次错误。不过使用框架代码定义的宏能很有效的帮助简化代码,减少了复制粘贴,在使得代码更加简洁的同时也尽可能避免错误。
  • 整个实验大约花费了2天的时间,基本重复着10分钟写指令2小时debug的过程。经常会因为对框架代码和i386手册的理解不透彻造成问题。并且由于一条指令出现错误后不会立刻被发现,而且汇编形成的代码往往又很长,因此给debug造成了很大的困难。
  • 经过这次实验,对i386指令及其实现有了更深刻的理解,并且也入门了使用gdb等工具调试汇编程序,也能更好的理解和阅读汇编代码了。总的来说,虽然pa的难度比较高,但还是非常有趣并且伴随着许多收获的!

pa2-2

题目

1.为什么在装载时要把内存中剩余的p_memsz - p_filesz字节的内容清零?

  • 这一段区域的内存是分配给.bss节的,而这一部分空间主要用于存储未初始化的全局变量,因此如果不对这段内存清零,那么可能会导致未初始化的变量具有一个未知的数值,这可能会导致出现某些错误。因此要对这些内存进行清零,也就实现对这些变量的初始化,防止为一个位置的值。

总结

  • 这部分内容比较简单,但在实现过程中遇到了一些困难:
  • 完成2-2的内容后发现test2-2运行后直接报错,最初一直以为是2-2部分实现的问题
  • 经过gdb调试时发现在kernel执行最初的部分,经过call指令后eip的值与推测的值相差1,导致程序出现异常
  • 经过检查发现确实是call和ret指令实现出现了错误,在call中对push的返回地址少加了1而ret中多加了1,这恰好又通过了2-1而没有出现任何问题!

pa2-3