Binary Translation型のエミュレータを開発している。変数の定義方法抜本的に作り直したので、命令の生成方法を修正している。ロードストア命令については修正が完了したので、次はジャンプ命令などの修正を行っていく。
JALR
命令
JALR
命令は書き込みレジスタにx0
が指定してあるかどうかで動作を変える。
pub fn translate_jalr(&mut self, inst: &InstrInfo) -> Vec<TCGOp> { let rs1_addr = get_rs1_addr!(inst.inst); let imm_const: u64 = ((inst.inst as i32) >> 20) as u64; let rd_addr = get_rd_addr!(inst.inst); let mut tcg_lists = vec![]; let source1 = self.tcg_temp_new(); let dest = self.tcg_temp_new(); tcg_lists.push(TCGOp::tcg_get_gpr(source1, rs1_addr)); let imm = TCGv::new_imm(imm_const); if rd_addr != 0 { let zero = self.tcg_temp_new(); tcg_lists.push(TCGOp::tcg_get_gpr(zero, 0)); let next_pc = TCGv::new_imm((inst.addr as u64).wrapping_add(4)); tcg_lists.push(TCGOp::new_2op(TCGOpcode::MOV_IMM_64BIT, dest, next_pc)); self.tcg_temp_free(zero); tcg_lists.push(TCGOp::tcg_set_gpr(rd_addr, dest)); } tcg_lists.push(TCGOp::new_3op(TCGOpcode::JMPR, dest, source1, imm)); tcg_lists.push(TCGOp::new_0op(TCGOpcode::EXIT_TB, None)); self.tcg_temp_free(source1); self.tcg_temp_free(dest); tcg_lists }
JMPR
のTCGは、PCにimm
で指定してある値を設定するTCGである。x86側の命令生成は以下の通りとなる。
fn tcg_gen_jmpr(emu: &EmuEnv, pc_address: u64, tcg: &TCGOp, mc: &mut Vec<u8>) -> usize { let op = tcg.op.unwrap(); let dest = tcg.arg0.unwrap(); let src1 = tcg.arg1.unwrap(); let src2 = tcg.arg2.unwrap(); assert_eq!(dest.t, TCGvType::TCGTemp); assert_eq!(src1.t, TCGvType::TCGTemp); assert_eq!(src2.t, TCGvType::Immediate); assert_eq!(op, TCGOpcode::JMPR); let mut gen_size: usize = pc_address as usize; let src1_x86reg = Self::convert_x86_reg(src1.value); gen_size += Self::tcg_modrm_64bit_raw_out(X86Opcode::MOV_GV_EV, X86ModRM::MOD_11_DISP_RAX as u8 + src1_x86reg as u8, X86TargetRM::RAX as u8, mc); gen_size += Self::tcg_64bit_out(X86Opcode::ADD_EAX_IV, mc); gen_size += Self::tcg_out(src2.value as u64, 4, mc); // RAX --> PC gen_size += Self::tcg_modrm_64bit_out(X86Opcode::MOV_EV_GV, X86ModRM::MOD_10_DISP_RBP, X86TargetRM::RAX, mc); gen_size += Self::tcg_out(emu.calc_pc_address() as u64, 4, mc); // Set Program Counter return gen_size; }
JALR
命令で生成されるx86命令は以下のようになる。
00007FE701EC0000 488B9538000000 mov 0x38(%rbp),%rdx 00007FE701EC0007 488B8D08000000 mov 8(%rbp),%rcx 00007FE701EC000E 48B81001008000000000 movabs $0x8000_0110,%rax 00007FE701EC0018 488BD8 mov %rax,%rbx 00007FE701EC001B 48899D30000000 mov %rbx,0x30(%rbp) 00007FE701EC0022 488BC2 mov %rdx,%rax 00007FE701EC0025 480500000000 add $0,%rax 00007FE701EC002B 48898508020000 mov %rax,0x208(%rbp) 00007FE701EC0032 E9D8FF2600 jmp 0x0000_7FE7_0213_000F
JAL命令
pub fn translate_jal(&mut self, inst: &InstrInfo) -> Vec<TCGOp> { let imm_const = extract_j_field!(inst.inst); let rd_addr = get_rd_addr!(inst.inst); let imm_const = ((imm_const as i32) << (32 - 21)) >> (32 - 21); let imm = TCGv::new_imm(((imm_const as i64).wrapping_add(inst.addr as i64)) as u64); let mut tcg_lists = vec![]; let dest_temp = self.tcg_temp_new(); let next_pc = TCGv::new_imm(inst.addr.wrapping_add(4)); if rd_addr != 0 { tcg_lists.push(TCGOp::new_2op(TCGOpcode::MOV_IMM_64BIT, dest_temp, next_pc)); tcg_lists.push(TCGOp::tcg_set_gpr(rd_addr, dest_temp)); } tcg_lists.push(TCGOp::new_2op(TCGOpcode::JMPIM, dest_temp, imm)); tcg_lists.push(TCGOp::new_0op(TCGOpcode::EXIT_TB, None)); self.tcg_temp_free(dest_temp); tcg_lists }
以下のようなx86命令に変換される。
00007FE872DA0000 48B80801008000000000 movabs $0x8000_0108,%rax 00007FE872DA000A 488BD0 mov %rax,%rdx 00007FE872DA000D 48899528000000 mov %rdx,0x28(%rbp) 00007FE872DA0014 48B81401008000000000 movabs $0x8000_0114,%rax 00007FE872DA001E 48898508020000 mov %rax,0x208(%rbp) 00007FE872DA0025 E9E5FF2400 jmp 0x0000_7FE8_72FF_000F