hypervisor: x86: Emulate MOV

And add a few unit tests based on a Mock platform.

Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
This commit is contained in:
Samuel Ortiz 2020-11-19 20:20:23 +01:00
parent 12b7a498d9
commit fe5401223b
4 changed files with 858 additions and 1 deletions

38
Cargo.lock generated
View File

@ -320,6 +320,19 @@ dependencies = [
"winapi 0.3.9",
]
[[package]]
name = "env_logger"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26ecb66b4bdca6c1409b40fb255eefc2bd4f6d135dab3c3124f80ffa2a9661e"
dependencies = [
"atty",
"humantime",
"log 0.4.11",
"regex",
"termcolor",
]
[[package]]
name = "epoll"
version = "4.3.1"
@ -496,12 +509,19 @@ dependencies = [
"libc",
]
[[package]]
name = "humantime"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c1ad908cc71012b7bea4d0c53ba96a8cba9962f048fa68d143376143d863b7a"
[[package]]
name = "hypervisor"
version = "0.1.0"
dependencies = [
"anyhow",
"arc-swap",
"env_logger",
"iced-x86",
"kvm-bindings",
"kvm-ioctls",
@ -1381,6 +1401,15 @@ dependencies = [
"winapi 0.3.9",
]
[[package]]
name = "termcolor"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4"
dependencies = [
"winapi-util",
]
[[package]]
name = "textwrap"
version = "0.11.0"
@ -1788,6 +1817,15 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-util"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
dependencies = [
"winapi 0.3.9",
]
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"

View File

@ -30,3 +30,6 @@ features = ["elf", "bzimage"]
version = "1.9.1"
default-features = false
features = ["std", "decoder", "op_code_info", "instr_info"]
[dev-dependencies]
env_logger = "0.8.2"

View File

@ -6,13 +6,43 @@
extern crate iced_x86;
use crate::arch::emulator::{EmulationError, PlatformEmulator};
use crate::arch::emulator::{EmulationError, PlatformEmulator, PlatformError};
use crate::arch::x86::emulator::CpuStateManager;
use crate::arch::x86::Exception;
use iced_x86::*;
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
pub mod mov;
// Returns the linear a.k.a. virtual address for a memory operand.
fn memory_operand_address<T: CpuStateManager>(
insn: &Instruction,
state: &T,
) -> Result<u64, PlatformError> {
let mut address: u64 = 0;
// Get the DS or override segment base first
let segment_base = state.read_segment(insn.memory_segment())?.base;
address += segment_base;
if insn.memory_base() != iced_x86::Register::None {
let base: u64 = state.read_reg(insn.memory_base())?;
address += base;
}
if insn.memory_index() != iced_x86::Register::None {
let mut index: u64 = state.read_reg(insn.memory_index())?;
index *= insn.memory_index_scale() as u64;
address += index;
}
address += insn.memory_displacement() as u64;
Ok(address)
}
pub trait InstructionHandler<T: CpuStateManager> {
fn emulate(
&self,

View File

@ -0,0 +1,786 @@
//
// Copyright © 2020 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
//
#![allow(non_camel_case_types)]
//
// MOV-Move
// SDM Volume 1, Chapter 4.3
// Copies the second operand (source operand) to the first operand (destination operand).
//
extern crate iced_x86;
use crate::arch::emulator::{EmulationError, PlatformEmulator};
use crate::arch::x86::emulator::instructions::*;
use crate::arch::x86::Exception;
use std::mem;
use std::sync::{Arc, Mutex};
macro_rules! mov_rm_r {
($bound:ty) => {
fn emulate(
&self,
insn: &Instruction,
state: &mut T,
platform: Arc<Mutex<dyn PlatformEmulator<CpuState = T>>>,
) -> Result<(), EmulationError<Exception>> {
let src_reg_value = state
.read_reg(insn.op1_register())
.map_err(EmulationError::PlatformEmulationError)?;
let addr = memory_operand_address(insn, state)
.map_err(EmulationError::PlatformEmulationError)?;
match insn.op0_kind() {
OpKind::Register => state
.write_reg(insn.op0_register(), src_reg_value)
.map_err(EmulationError::PlatformEmulationError)?,
OpKind::Memory => {
let src_reg_value_type: $bound = src_reg_value as $bound;
platform
.lock()
.unwrap()
.write_memory(addr, &src_reg_value_type.to_le_bytes())
.map_err(EmulationError::PlatformEmulationError)?
}
k => return Err(EmulationError::InvalidOperand(anyhow!("{:?}", k))),
}
state.set_ip(insn.ip());
Ok(())
}
};
}
macro_rules! mov_rm_imm {
($type:tt) => {
fn emulate(
&self,
insn: &Instruction,
state: &mut T,
platform: Arc<Mutex<dyn PlatformEmulator<CpuState = T>>>,
) -> Result<(), EmulationError<Exception>> {
let imm = imm_op!($type, insn);
let addr = memory_operand_address(insn, state)
.map_err(EmulationError::PlatformEmulationError)?;
match insn.op0_kind() {
OpKind::Register => state
.write_reg(insn.op0_register(), imm as u64)
.map_err(EmulationError::PlatformEmulationError)?,
OpKind::Memory => platform
.lock()
.unwrap()
.write_memory(addr, &imm.to_le_bytes())
.map_err(EmulationError::PlatformEmulationError)?,
k => return Err(EmulationError::InvalidOperand(anyhow!("{:?}", k))),
}
state.set_ip(insn.ip());
Ok(())
}
};
}
macro_rules! mov_r_rm {
($bound:ty) => {
fn emulate(
&self,
insn: &Instruction,
state: &mut T,
platform: Arc<Mutex<dyn PlatformEmulator<CpuState = T>>>,
) -> Result<(), EmulationError<Exception>> {
let src_value: $bound = match insn.op1_kind() {
OpKind::Register => state
.read_reg(insn.op1_register())
.map_err(EmulationError::PlatformEmulationError)?
as $bound,
OpKind::Memory => {
let target_address = memory_operand_address(insn, state)
.map_err(EmulationError::PlatformEmulationError)?;
let mut memory: [u8; mem::size_of::<$bound>()] = [0; mem::size_of::<$bound>()];
platform
.lock()
.unwrap()
.read_memory(target_address, &mut memory)
.map_err(EmulationError::PlatformEmulationError)?;
<$bound>::from_le_bytes(memory)
}
k => return Err(EmulationError::InvalidOperand(anyhow!("{:?}", k))),
};
state
.write_reg(insn.op0_register(), src_value as u64)
.map_err(EmulationError::PlatformEmulationError)?;
state.set_ip(insn.ip());
Ok(())
}
};
}
macro_rules! mov_r_imm {
($type:tt) => {
fn emulate(
&self,
insn: &Instruction,
state: &mut T,
_platform: Arc<Mutex<dyn PlatformEmulator<CpuState = T>>>,
) -> Result<(), EmulationError<Exception>> {
state
.write_reg(insn.op0_register(), imm_op!($type, insn) as u64)
.map_err(EmulationError::PlatformEmulationError)?;
state.set_ip(insn.ip());
Ok(())
}
};
}
macro_rules! imm_op {
(u8, $insn:ident) => {
$insn.immediate8()
};
(u16, $insn:ident) => {
$insn.immediate16()
};
(u32, $insn:ident) => {
$insn.immediate32()
};
(u64, $insn:ident) => {
$insn.immediate64()
};
(u32tou64, $insn:ident) => {
$insn.immediate32to64()
};
}
pub struct Mov_r8_rm8 {}
impl<T: CpuStateManager> InstructionHandler<T> for Mov_r8_rm8 {
mov_r_rm!(u8);
}
pub struct Mov_r8_imm8 {}
impl<T: CpuStateManager> InstructionHandler<T> for Mov_r8_imm8 {
mov_r_imm!(u8);
}
pub struct Mov_r16_rm16 {}
impl<T: CpuStateManager> InstructionHandler<T> for Mov_r16_rm16 {
mov_r_rm!(u16);
}
pub struct Mov_r16_imm16 {}
impl<T: CpuStateManager> InstructionHandler<T> for Mov_r16_imm16 {
mov_r_imm!(u16);
}
pub struct Mov_r32_rm32 {}
impl<T: CpuStateManager> InstructionHandler<T> for Mov_r32_rm32 {
mov_r_rm!(u32);
}
pub struct Mov_r32_imm32 {}
impl<T: CpuStateManager> InstructionHandler<T> for Mov_r32_imm32 {
mov_r_imm!(u32);
}
pub struct Mov_r64_rm64 {}
impl<T: CpuStateManager> InstructionHandler<T> for Mov_r64_rm64 {
mov_r_rm!(u64);
}
pub struct Mov_r64_imm64 {}
impl<T: CpuStateManager> InstructionHandler<T> for Mov_r64_imm64 {
mov_r_imm!(u64);
}
pub struct Mov_rm8_imm8 {}
impl<T: CpuStateManager> InstructionHandler<T> for Mov_rm8_imm8 {
mov_rm_imm!(u8);
}
pub struct Mov_rm8_r8 {}
impl<T: CpuStateManager> InstructionHandler<T> for Mov_rm8_r8 {
mov_rm_r!(u8);
}
pub struct Mov_rm16_imm16 {}
impl<T: CpuStateManager> InstructionHandler<T> for Mov_rm16_imm16 {
mov_rm_imm!(u16);
}
pub struct Mov_rm16_r16 {}
impl<T: CpuStateManager> InstructionHandler<T> for Mov_rm16_r16 {
mov_rm_r!(u16);
}
pub struct Mov_rm32_imm32 {}
impl<T: CpuStateManager> InstructionHandler<T> for Mov_rm32_imm32 {
mov_rm_imm!(u32);
}
pub struct Mov_rm32_r32 {}
impl<T: CpuStateManager> InstructionHandler<T> for Mov_rm32_r32 {
mov_rm_r!(u32);
}
pub struct Mov_rm64_imm32 {}
impl<T: CpuStateManager> InstructionHandler<T> for Mov_rm64_imm32 {
mov_rm_imm!(u32tou64);
}
pub struct Mov_rm64_r64 {}
impl<T: CpuStateManager> InstructionHandler<T> for Mov_rm64_r64 {
mov_rm_r!(u64);
}
#[cfg(test)]
mod tests {
#![allow(unused_mut)]
extern crate env_logger;
use super::*;
use crate::arch::emulator::{EmulationError, PlatformEmulator};
use crate::arch::x86::emulator::{Emulator, EmulatorCpuState as CpuState};
use crate::arch::x86::gdt::{gdt_entry, segment_from_gdt};
use crate::arch::x86::Exception;
use std::collections::HashMap;
#[derive(Debug, Clone)]
struct MockVMM {
memory: Vec<u8>,
state: Arc<Mutex<CpuState>>,
}
unsafe impl Sync for MockVMM {}
type MockResult = Result<(), EmulationError<Exception>>;
macro_rules! hashmap {
($( $key: expr => $val: expr ),*) => {{
let mut map = ::std::collections::HashMap::new();
$( map.insert($key, $val); )*
map
}}
}
impl MockVMM {
pub fn new(state: CpuState) -> MockVMM {
MockVMM {
memory: vec![0; 4096],
state: Arc::new(Mutex::new(state)),
}
}
}
impl PlatformEmulator for MockVMM {
type CpuState = CpuState;
fn read_memory(&self, gva: u64, data: &mut [u8]) -> Result<(), PlatformError> {
debug!(
"Memory read {} bytes from [{:#x} -> {:#x}]",
data.len(),
gva,
gva
);
data.copy_from_slice(&self.memory[gva as usize..gva as usize + data.len()]);
Ok(())
}
fn write_memory(&mut self, gva: u64, data: &[u8]) -> Result<(), PlatformError> {
debug!(
"Memory write {} bytes at [{:#x} -> {:#x}]",
data.len(),
gva,
gva
);
self.memory[gva as usize..gva as usize + data.len()].copy_from_slice(data);
Ok(())
}
fn cpu_state(&self, _cpu_id: usize) -> Result<CpuState, PlatformError> {
Ok(self.state.lock().unwrap().clone())
}
fn set_cpu_state(
&self,
_cpu_id: usize,
state: Self::CpuState,
) -> Result<(), PlatformError> {
*self.state.lock().unwrap() = state;
Ok(())
}
fn gva_to_gpa(&self, gva: u64) -> Result<u64, PlatformError> {
Ok(gva)
}
}
fn _init_and_run(
cpu_id: usize,
ip: u64,
insn: &[u8],
regs: HashMap<Register, u64>,
memory: Option<(u64, &[u8])>,
num_insn: Option<usize>,
) -> Arc<Mutex<MockVMM>> {
let _ = env_logger::try_init();
let cs_reg = segment_from_gdt(gdt_entry(0xc09b, 0, 0xffffffff), 1);
let ds_reg = segment_from_gdt(gdt_entry(0xc093, 0, 0xffffffff), 2);
let mut initial_state = CpuState::default();
initial_state.set_ip(ip);
initial_state.write_segment(Register::CS, cs_reg).unwrap();
initial_state.write_segment(Register::DS, ds_reg).unwrap();
for (reg, value) in regs {
initial_state.write_reg(reg, value).unwrap();
}
let vmm = Arc::new(Mutex::new(MockVMM::new(initial_state)));
if let Some(mem) = memory {
vmm.lock().unwrap().write_memory(mem.0, &mem.1).unwrap();
}
let mut emulator = Emulator::new(vmm.clone());
let new_state = emulator
.emulate_insn_stream(cpu_id, &insn, num_insn)
.unwrap();
if num_insn.is_none() {
assert_eq!(ip + insn.len() as u64, new_state.ip());
}
vmm.lock()
.unwrap()
.set_cpu_state(cpu_id, new_state)
.unwrap();
vmm
}
fn init_and_run(
cpu_id: usize,
ip: u64,
insn: &[u8],
regs: HashMap<Register, u64>,
memory: Option<(u64, &[u8])>,
) -> Arc<Mutex<MockVMM>> {
_init_and_run(cpu_id, ip, insn, regs, memory, None)
}
#[test]
// mov rax,rbx
fn test_mov_r64_r64() -> MockResult {
let rbx: u64 = 0x8899aabbccddeeff;
let ip: u64 = 0x1000;
let cpu_id = 0;
let insn = [0x48, 0x89, 0xd8];
let vmm = init_and_run(cpu_id, ip, &insn, hashmap![Register::RBX => rbx], None);
let rax: u64 = vmm
.lock()
.unwrap()
.cpu_state(cpu_id)
.unwrap()
.read_reg(Register::RAX)
.unwrap();
assert_eq!(rax, rbx);
Ok(())
}
#[test]
// mov rax,0x1122334411223344
fn test_mov_r64_imm64() -> MockResult {
let imm64: u64 = 0x1122334411223344;
let ip: u64 = 0x1000;
let cpu_id = 0;
let insn = [0x48, 0xb8, 0x44, 0x33, 0x22, 0x11, 0x44, 0x33, 0x22, 0x11];
let vmm = init_and_run(cpu_id, ip, &insn, hashmap![], None);
let rax: u64 = vmm
.lock()
.unwrap()
.cpu_state(cpu_id)
.unwrap()
.read_reg(Register::RAX)
.unwrap();
assert_eq!(rax, imm64);
Ok(())
}
#[test]
// mov rax, [rax+rax]
fn test_mov_r64_m64() -> MockResult {
let target_rax: u64 = 0x1234567812345678;
let mut rax: u64 = 0x100;
let ip: u64 = 0x1000;
let cpu_id = 0;
let memory: [u8; 8] = target_rax.to_le_bytes();
let insn = [0x48, 0x8b, 0x04, 0x00];
let vmm = init_and_run(
cpu_id,
ip,
&insn,
hashmap![Register::RAX => rax],
Some((rax + rax, &memory)),
);
rax = vmm
.lock()
.unwrap()
.cpu_state(cpu_id)
.unwrap()
.read_reg(Register::RAX)
.unwrap();
assert_eq!(rax, target_rax);
Ok(())
}
#[test]
// mov al,0x11
fn test_mov_r8_imm8() -> MockResult {
let imm8: u8 = 0x11;
let ip: u64 = 0x1000;
let cpu_id = 0;
let insn = [0xb0, 0x11];
let vmm = init_and_run(cpu_id, ip, &insn, hashmap![], None);
let al = vmm
.lock()
.unwrap()
.cpu_state(cpu_id)
.unwrap()
.read_reg(Register::AL)
.unwrap();
assert_eq!(al as u8, imm8);
Ok(())
}
#[test]
// mov eax,0x11
fn test_mov_r32_imm8() -> MockResult {
let imm8: u8 = 0x11;
let ip: u64 = 0x1000;
let cpu_id = 0;
let insn = [0xb8, 0x11, 0x00, 0x00, 0x00];
let vmm = init_and_run(cpu_id, ip, &insn, hashmap![], None);
let eax = vmm
.lock()
.unwrap()
.cpu_state(cpu_id)
.unwrap()
.read_reg(Register::EAX)
.unwrap();
assert_eq!(eax as u8, imm8);
Ok(())
}
#[test]
// mov rax,0x11223344
fn test_mov_r64_imm32() -> MockResult {
let imm32: u32 = 0x11223344;
let ip: u64 = 0x1000;
let cpu_id = 0;
let insn = [0x48, 0xc7, 0xc0, 0x44, 0x33, 0x22, 0x11];
let vmm = init_and_run(cpu_id, ip, &insn, hashmap![], None);
let rax: u64 = vmm
.lock()
.unwrap()
.cpu_state(cpu_id)
.unwrap()
.read_reg(Register::RAX)
.unwrap();
assert_eq!(rax, imm32 as u64);
Ok(())
}
#[test]
// mov byte ptr [rax],dh
fn test_mov_m8_r8() -> MockResult {
let rax: u64 = 0x100;
let dh: u8 = 0x99;
let ip: u64 = 0x1000;
let cpu_id = 0;
let insn = [0x88, 0x30];
let vmm = init_and_run(
cpu_id,
ip,
&insn,
hashmap![Register::RAX => rax, Register::DH => dh.into()],
None,
);
let mut memory: [u8; 1] = [0; 1];
vmm.lock().unwrap().read_memory(rax, &mut memory).unwrap();
assert_eq!(u8::from_le_bytes(memory), dh);
Ok(())
}
#[test]
// mov dword ptr [rax],esi
fn test_mov_m32_r32() -> MockResult {
let rax: u64 = 0x100;
let esi: u32 = 0x8899;
let ip: u64 = 0x1000;
let cpu_id = 0;
let insn = [0x89, 0x30];
let vmm = init_and_run(
cpu_id,
ip,
&insn,
hashmap![Register::RAX => rax, Register::ESI => esi.into()],
None,
);
let mut memory: [u8; 4] = [0; 4];
vmm.lock().unwrap().read_memory(rax, &mut memory).unwrap();
assert_eq!(u32::from_le_bytes(memory), esi);
Ok(())
}
#[test]
// mov dword ptr [rax+0x00000001],edi
fn test_mov_m32imm32_r32() -> MockResult {
let rax: u64 = 0x100;
let displacement: u64 = 0x1;
let edi: u32 = 0x8899;
let ip: u64 = 0x1000;
let cpu_id = 0;
let insn = [0x89, 0x3c, 0x05, 0x01, 0x00, 0x00, 0x00];
let vmm = init_and_run(
cpu_id,
ip,
&insn,
hashmap![Register::RAX => rax, Register::EDI => edi.into()],
None,
);
let mut memory: [u8; 4] = [0; 4];
vmm.lock()
.unwrap()
.read_memory(rax + displacement, &mut memory)
.unwrap();
assert_eq!(u32::from_le_bytes(memory), edi);
Ok(())
}
#[test]
// mov eax,dword ptr [rax+10h]
fn test_mov_r32_m32imm32() -> MockResult {
let rax: u64 = 0x100;
let displacement: u64 = 0x10;
let eax: u32 = 0xaabbccdd;
let memory: [u8; 4] = eax.to_le_bytes();
let ip: u64 = 0x1000;
let cpu_id = 0;
let insn = [0x8b, 0x40, 0x10];
let vmm = init_and_run(
cpu_id,
ip,
&insn,
hashmap![Register::RAX => rax],
Some((rax + displacement, &memory)),
);
let new_eax = vmm
.lock()
.unwrap()
.cpu_state(cpu_id)
.unwrap()
.read_reg(Register::EAX)
.unwrap();
assert_eq!(new_eax, eax as u64);
Ok(())
}
#[test]
// mov al,byte ptr [rax+10h]
fn test_mov_r8_m32imm32() -> MockResult {
let rax: u64 = 0x100;
let displacement: u64 = 0x10;
let al: u8 = 0xaa;
let ip: u64 = 0x1000;
let cpu_id = 0;
let insn = [0x8a, 0x40, 0x10];
let memory: [u8; 1] = al.to_le_bytes();
let vmm = init_and_run(
cpu_id,
ip,
&insn,
hashmap![Register::RAX => rax],
Some((rax + displacement, &memory)),
);
let new_al = vmm
.lock()
.unwrap()
.cpu_state(cpu_id)
.unwrap()
.read_reg(Register::AL)
.unwrap();
assert_eq!(new_al, al as u64);
Ok(())
}
#[test]
// mov rax, 0x100
// mov rbx, qword ptr [rax+10h]
fn test_mov_r64_imm64_and_r64_m64() -> MockResult {
let target_rax: u64 = 0x1234567812345678;
let rax: u64 = 0x100;
let displacement: u64 = 0x10;
let ip: u64 = 0x1000;
let cpu_id = 0;
let memory: [u8; 8] = target_rax.to_le_bytes();
let insn = [
0x48, 0xc7, 0xc0, 0x00, 0x01, 0x00, 0x00, // mov rax, 0x100
0x48, 0x8b, 0x58, 0x10, // mov rbx, qword ptr [rax+10h]
];
let vmm = init_and_run(
cpu_id,
ip,
&insn,
hashmap![],
Some((rax + displacement, &memory)),
);
let rbx: u64 = vmm
.lock()
.unwrap()
.cpu_state(cpu_id)
.unwrap()
.read_reg(Register::RBX)
.unwrap();
assert_eq!(rbx, target_rax);
Ok(())
}
#[test]
// mov rax, 0x100
// mov rbx, qword ptr [rax+10h]
fn test_mov_r64_imm64_and_r64_m64_first_insn() -> MockResult {
let target_rax: u64 = 0x1234567812345678;
let rax: u64 = 0x100;
let displacement: u64 = 0x10;
let ip: u64 = 0x1000;
let cpu_id = 0;
let memory: [u8; 8] = target_rax.to_le_bytes();
let insn = [
0x48, 0xc7, 0xc0, 0x00, 0x01, 0x00, 0x00, // mov rax, 0x100
0x48, 0x8b, 0x58, 0x10, // mov rbx, qword ptr [rax+10h]
];
// Only run the first instruction.
let vmm = _init_and_run(
cpu_id,
ip,
&insn,
hashmap![],
Some((rax + displacement, &memory)),
Some(1),
);
assert_eq!(
ip + 7 as u64,
vmm.lock().unwrap().cpu_state(cpu_id).unwrap().ip()
);
let new_rax: u64 = vmm
.lock()
.unwrap()
.cpu_state(cpu_id)
.unwrap()
.read_reg(Register::RAX)
.unwrap();
assert_eq!(rax, new_rax);
Ok(())
}
#[test]
// mov rax, 0x100
// mov rbx, qword ptr [rax+10h]
// mov rax, 0x200
fn test_mov_r64_imm64_and_r64_m64_two_insns() -> MockResult {
let target_rax: u64 = 0x1234567812345678;
let rax: u64 = 0x100;
let displacement: u64 = 0x10;
let ip: u64 = 0x1000;
let cpu_id = 0;
let memory: [u8; 8] = target_rax.to_le_bytes();
let insn = [
0x48, 0xc7, 0xc0, 0x00, 0x01, 0x00, 0x00, // mov rax, 0x100
0x48, 0x8b, 0x58, 0x10, // mov rbx, qword ptr [rax+10h]
0x48, 0xc7, 0xc0, 0x00, 0x02, 0x00, 0x00, // mov rax, 0x200
];
// Only run the first instruction.
let vmm = _init_and_run(
cpu_id,
ip,
&insn,
hashmap![],
Some((rax + displacement, &memory)),
Some(2),
);
assert_eq!(
ip + 7 + 4 as u64,
vmm.lock().unwrap().cpu_state(cpu_id).unwrap().ip()
);
let rbx: u64 = vmm
.lock()
.unwrap()
.cpu_state(cpu_id)
.unwrap()
.read_reg(Register::RBX)
.unwrap();
assert_eq!(rbx, target_rax);
// Check that rax is still at 0x100
let new_rax: u64 = vmm
.lock()
.unwrap()
.cpu_state(cpu_id)
.unwrap()
.read_reg(Register::RAX)
.unwrap();
assert_eq!(rax, new_rax);
Ok(())
}
}