PA——2.1

版权声明:本文为DmrfCoder原创作品,如有转载请注明出处 https://blog.csdn.net/qq_36982160/article/details/80119189

##讲义中提到的问题:立即数背后的故事
Motorola 68k系列的处理器都是大端架构的, 现在问题来了, 考虑以下两种情况:

  • 假设我们需要将NEMU运行在Motorola 68k的机器上(把NEMU的源代码编译成Motorola 68k的机器码)
  • 假设我们需要编写一个新的模拟器NEMU-Motorola-68k, 模拟器本身运行在x86架构中, 但它模拟的是Motorola 68k程序的执行

在这两种情况下, 你需要注意些什么问题? 为什么会产生这些问题? 怎么解决它们?

###答案

  • 需要注意的问题:大小端的兼容问题
  • 产生这个问题的原因:大端和小端机器的字节码存放顺序(原则)是相反的
  • 解决方案: 写一个专门处理大小端字节转换的函数
    ##实现新的指令
    ###在opcode_table中填写正确的译码函数,执行函数和操作数宽度

这里主要通过查阅i386手册P414页,查看指令所对应的地址以及译码函数,或者查看反汇编文件dummy-x86-nemu.txt来查看指令对应地址,主要修改两部分:

####make_group

/* 0x80, 0x81, 0x83 */
make_group(gp1,
    EMPTY, EMPTY, EMPTY, EMPTY,
    EMPTY, EXW(sub,1), EMPTY, EMPTY)

####opcode_entry opcode_table [512]

/* 0x30 */  IDEX(G2E, xor), IDEX(G2E, xor), IDEX(E2G, xor), IDEX(E2G, xor),
/* 0x34 */  IDEX(I2a, xor), IDEX(I2r, xor), EMPTY, EMPTY,
/* 0x50 */  IDEXW(r,push,4), IDEXW(r,push,4), IDEXW(r,push,4), IDEXW(r,push,4),
/* 0x54 */  IDEXW(r,push,4), IDEXW(r,push,4), IDEXW(r,push,4), IDEXW(r,push,4),
/* 0x58 */  IDEXW(r,pop,4), IDEXW(r,pop,4), IDEXW(r,pop,4), IDEXW(r,pop,4),
/* 0x5c */  IDEXW(r,pop,4), IDEXW(r,pop,4), IDEXW(r,pop,4), IDEXW(r,pop,4),
/* 0xc0 */  IDEXW(gp2_Ib2E, gp2, 1), IDEX(gp2_Ib2E, gp2), EMPTY, EX(ret),
/* 0xe8 */  IDEX(I, call), EMPTY, EMPTY, EMPTY,

###使用RTL实现正确的执行函数
首先进入rtl.h,需要实现的框架函数都已经给出,并且内部都有注释,只需要根据注释填充对应内容即可,一些读取和写入操作需要使用rtl.h内部已经定义写好的rtl函数,具体实现如下:
####将传入的形参赋给cpu结构体中的eflags成员
#define make_rtl_setget_eflags(f)
static inline void concat(rtl_set_, f) (const rtlreg_t* src) {
cpu.eflags.f = src;
}
static inline void concat(rtl_get_, f) (rtlreg_t
dest) {
*dest = cpu.eflags.f;
}

利用rtl_subi和rtl_sm实现减法和寄存器写入

static inline void rtl_push(const rtlreg_t* src1) {
  // esp <- esp - 4
  // M[esp] <- src1
  rtl_subi(&cpu.esp, &cpu.esp, 4);
  rtl_sm(&cpu.esp, 4, src1);
}

####利用rtl_addi和rtl_lm实现加法和寄存器读取

static inline void rtl_pop(rtlreg_t* dest) {
  // dest <- M[esp]
  // esp <- esp + 4
  rtl_lm(dest, &cpu.esp, 4);
  rtl_addi(&cpu.esp, &cpu.esp, 4);
}

####三元表达式判断src1的值然后赋值给dest

static inline void rtl_eq0(rtlreg_t* dest, const rtlreg_t* src1) {
  // dest <- (src1 == 0 ? 1 : 0)
  *dest = *src1 == 0 ? 1 : 0;
}

// 用定义好的rtl_li读入立即数赋值给dest

static inline void rtl_msb(rtlreg_t* dest, const rtlreg_t* src1, int width) {
  // dest <- src1[width * 8 - 1]
  rtl_li(dest, src1[width * 8 - 1]);
}

####利用rtl_eq0赋值给&t1,然后用写好的定义来设置ZF位

static inline void rtl_update_ZF(const rtlreg_t* result, int width) {
  // eflags.ZF <- is_zero(result[width * 8 - 1 .. 0])
  rtl_eq0(&t1, result);
  rtl_set_ZF(&t1);
}

####利用rtl_msb赋值给&t1,然后用写好的定义来设置SF位

static inline void rtl_update_SF(const rtlreg_t* result, int width) {
  // eflags.SF <- is_sign(result[width * 8 - 1 .. 0])
  rtl_msb(&t1, result, width);
  rtl_set_SF(&t1);
}

##运行第一个C程序
按照讲义的提示首先执行:

make	ARCH=x86-nemu	ALL=dummy	run

执行完之后可以直接进入nemu,然后执行c可以看到类似讲义上的提示,相关截图如下:

这里写图片描述

这里写图片描述

###遇到的问题
####找不到头文件
报错信息如下:

/usr/include/features.h:367:25: fatal error: sys/cdefs.h: 没有那个文件或目录

分析了一下缺少的头文件路径在user/include下,说明不是pa代码缺少的问题,那只可能是系统缺少相应的c库,google找到cdefs.h所属的包为gcc-multilib,命令行安装即可:

这里写图片描述
####找不到Makefile文件
报错信息如下:
这里写图片描述
这个error很常见,一般十有八九是环境变量的问题,按照提示的error路径找到Makefile文件找到其用到的环境变量然后命令行无脑导入即可:
这里写图片描述
###完成代码
首先在nemu/src/cpu/exec/all-instr.h中定义所有的指令:

#include "cpu/exec.h"
make_EHelper(mov);
make_EHelper(operand_size);
make_EHelper(inv);
make_EHelper(nemu_trap);
make_EHelper(call);
make_EHelper(push);
make_EHelper(sub);
make_EHelper(pop);
make_EHelper(xor);
make_EHelper(ret);

在nemu/sec/cpu/exec/exec.c中实现:
####call

  • 代码所在位置:nemu/sec/cpu/exec/control.c

  • 实现思路:读取要压栈的eip值——>rtl_push进栈——>更新jmp_eip——>设置decoding.is_jmp1进行跳转

    make_EHelper(call) {
    // the target address is calculated at the decode stage
    rtl_push(eip);
    rtl_addi(&decoding.jmp_eip, eip, id_dest->val);
    decoding.is_jmp = 1;
    print_asm(“call %x”, decoding.jmp_eip);
    }

####push&pop

  • 代码所在位置:nemu/src/cpu/exec/data-mov.c

  • 实现思路:pushpop利用rtl函数完成,使用operand_write将值写回:

    make_EHelper(push) {
    rtl_push(&t2);
    operand_write(id_dest, &t2);
    print_asm_template1(push);
    }

    make_EHelper(pop) {
    rtl_pop(&t2);
    operand_write(id_dest, &t2);
    print_asm_template1(pop);
    }
    ####sub

  • 代码所在位置:nemu/src/cpu/exec/arith.c

  • 实现思路:在实现sub之前根据讲义给出的结构图完成EFLAGS寄存器,编辑 nemu/include/cpu/reg.h中的CPU_state增加如下代码:

    union {
    uint32_t val;
    struct {
    uint32_t CF:1;
    unsigned : 5;
    uint32_t ZF:1;
    uint32_t SF:1;
    unsigned : 1;
    uint32_t IF:1;
    unsigned : 1;
    uint32_t OF:1;
    unsigned:20;
    };
    }eflags;

定义之后在src/monitor/monitor.c中的restart中依据I386手册要求赋初值0x00000002

static inline void restart() {
  /* Set the initial instruction pointer. */
  cpu.eip = ENTRY_START;

  /* Set the initial EFLAGS */
  cpu.eflags.val = 0x00000002;
#ifdef DIFF_TEST
  init_qemu_reg();
#endif
}

实现sub:

make_EHelper(sub) {
  rtl_sub(&t2, &id_dest->val, &id_src->val);
  rtl_sltu(&t3, &id_dest->val, &t2);
  operand_write(id_dest, &t2);

  rtl_update_ZFSF(&t2, id_dest->width);

  rtl_sltu(&t0, &id_dest->val, &t2);
  rtl_or(&t0, &t3, &t0);
  rtl_set_CF(&t0);

  rtl_xor(&t0, &id_dest->val, &id_src->val);
  rtl_xor(&t1, &id_dest->val, &t2);
  rtl_and(&t0, &t0, &t1);
  rtl_msb(&t0, &t0, id_dest->width);
  rtl_set_OF(&t0);

  print_asm_template2(sub);
}

####xor

  • 代码所在位置:nemu/src/cpu/exec/logic.c

  • 实现思路: xor指令通过查询i386手册明白是对srcdest通过rtl_xor函数进行异或运算并写入操作数,用rtl_set_函数将标志位设为0

    make_EHelper(xor) {
    rtl_xor(&t2, &id_dest->val, &id_src->val);
    operand_write(id_dest, &t2);
    rtl_set_CF(&tzero);
    rtl_set_OF(&tzero);
    print_asm_template2(xor);
    }

####ret

  • 代码所在路径:nemu/src/cpu/exec/control.c

  • 实现思路: 从栈顶弹出eip然后设置跳转即可

    make_EHelper(ret) {
    rtl_pop(&decoding.jmp_eip);
    decoding.is_jmp = 1;
    print_asm(“ret”);
    }

然后大概代码就完成了,但是运行时又遇到了以下错误:
这里写图片描述
意思是说static inline make_DopHelper(SI)这个函数没实现,进去代码文件按照提示实现即可:

static inline make_DopHelper(SI) {
  assert(op->width == 1 || op->width == 4);

  op->type = OP_TYPE_IMM;

  /* TODO: Use instr_fetch() to read `op->width' bytes of memory
   * pointed by `eip'. Interpret the result as a signed immediate,
   * and assign it to op->simm.
   *
   op->simm = ???
   */
  op->simm=instr_fetch(eip,op->width);

  rtl_li(&op->val, op->simm);

#ifdef DEBUG
  snprintf(op->str, OP_STR_SIZE, "$0x%x", op->simm);
#endif
}

然后就可以正确执行:
这里写图片描述
##git log截图
这里写图片描述

猜你喜欢

转载自blog.csdn.net/qq_36982160/article/details/80119189
pa
2.1