Skip to content

Commit

Permalink
separate machine state from cpu state
Browse files Browse the repository at this point in the history
Use a new enum for the total machine state, separately from per-CPU state.
Drop the "Exit" state for a CPU, as that is a process-wide thing.
  • Loading branch information
evmar committed Sep 25, 2024
1 parent 8c5491b commit c1735a7
Show file tree
Hide file tree
Showing 7 changed files with 43 additions and 41 deletions.
17 changes: 7 additions & 10 deletions cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -188,19 +188,16 @@ fn main() -> anyhow::Result<ExitCode> {
while machine.run() {}
}

match &machine.emu.x86.cpu().state {
x86::CPUState::Error(error) => {
log::error!("{:?}", error);
machine.dump_state(0);
exit_code = 1;
}
x86::CPUState::Exit(code) => {
match &machine.status {
win32::Status::Exit(code) => {
exit_code = *code;
}
x86::CPUState::Running | x86::CPUState::Blocked(_) | x86::CPUState::SysCall => {
unreachable!()
win32::Status::Error { message } => {
log::error!("{}", message);
machine.dump_state(0);
exit_code = 1;
}
x86::CPUState::DebugBreak => todo!(),
_ => unreachable!(),
}

let millis = start.elapsed().as_millis() as usize;
Expand Down
12 changes: 6 additions & 6 deletions web/emulator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ export class Emulator extends JsHost {
private runBatch() {
const startTime = performance.now();
const startSteps = this.emu.instr_count;
const cpuState = this.emu.run(this.stepSize) as wasm.CPUState;
const cpuState = this.emu.run(this.stepSize) as wasm.Status;
const endTime = performance.now();
const endSteps = this.emu.instr_count;

Expand Down Expand Up @@ -94,9 +94,9 @@ export class Emulator extends JsHost {
this.breakpoints.uninstall(this.emu);

switch (cpuState) {
case wasm.CPUState.Running:
case wasm.Status.Running:
return true;
case wasm.CPUState.DebugBreak: {
case wasm.Status.DebugBreak: {
const bp = this.breakpoints.isAtBreakpoint(this.emu.eip);
if (bp) {
if (!bp.oneShot) {
Expand All @@ -106,9 +106,9 @@ export class Emulator extends JsHost {
}
return false;
}
case wasm.CPUState.Blocked:
case wasm.CPUState.Error:
case wasm.CPUState.Exit:
case wasm.Status.Blocked:
case wasm.Status.Error:
case wasm.Status.Exit:
this.emuHost.onStopped();
return false;
}
Expand Down
18 changes: 10 additions & 8 deletions web/glue/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ pub struct Emulator {
}

#[wasm_bindgen]
pub enum CPUState {
pub enum Status {
Running,
Blocked,
Error,
Expand Down Expand Up @@ -82,7 +82,7 @@ impl Emulator {

/// Run code until at least count instructions have run.
/// This exists to avoid many round-trips from JS to Rust in the execution loop.
pub fn run(&mut self, count: usize) -> JsResult<CPUState> {
pub fn run(&mut self, count: usize) -> JsResult<Status> {
if count == 1 {
self.machine.single_step();
} else {
Expand All @@ -96,12 +96,14 @@ impl Emulator {
}
}

Ok(match &self.machine.emu.x86.cpu().state {
x86::CPUState::Running | x86::CPUState::SysCall => CPUState::Running,
x86::CPUState::Blocked(_) => CPUState::Blocked,
x86::CPUState::Error(msg) => return Err(JsError::new(msg)),
x86::CPUState::DebugBreak => CPUState::DebugBreak,
x86::CPUState::Exit(_) => CPUState::Exit,
Ok(match &self.machine.status {
win32::Status::Running => Status::Running,
win32::Status::Blocked => Status::Blocked,
win32::Status::Error { message } => return Err(JsError::new(message)),
win32::Status::Exit(_code) => {
// TODO: use exit code
Status::Exit
}
})
}

Expand Down
6 changes: 6 additions & 0 deletions win32/src/machine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,9 @@ pub enum Status {
/// Process exited.
Exit(u32),
}

impl Status {
pub fn is_running(&self) -> bool {
matches!(self, Status::Running)
}
}
23 changes: 12 additions & 11 deletions win32/src/machine_emu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
use crate::{
host,
machine::{LoadedAddrs, MachineX},
machine::{LoadedAddrs, MachineX, Status},
pe,
shims::{Handler, Shims},
winapi,
Expand Down Expand Up @@ -135,6 +135,7 @@ impl MachineX<Emulator> {
x86::CPUState::Blocked(_) | x86::CPUState::DebugBreak
) {
cpu.state = x86::CPUState::Running;
self.status = Status::Running;
}
}
}
Expand All @@ -146,6 +147,7 @@ impl MachineX<Emulator> {
x86::CPUState::Blocked(_) | x86::CPUState::DebugBreak
) {
cpu.state = x86::CPUState::Running;
self.status = Status::Running;
}
}

Expand All @@ -159,12 +161,17 @@ impl MachineX<Emulator> {
if self.host.block(wait) {
self.unblock();
} else {
return false;
self.status = Status::Blocked;
}
}
_ => return false,
x86::CPUState::Error(message) => {
self.status = Status::Error {
message: message.clone(),
};
}
_ => unimplemented!(),
}
true
self.status.is_running()
}

fn execute_block(&mut self) {
Expand Down Expand Up @@ -264,12 +271,6 @@ impl MachineX<Emulator> {
}

pub fn exit(&mut self, exit_code: u32) {
// Set the CPU state immediately here because otherwise the CPU will
// continue executing instructions after the exit call.
// TODO: this is unsatisfying.
// Maybe better is to generate a hlt instruction somewhere and jump to it?
// Note also we need a mechanism to exit a completed thread without stopping the whole
// program.
self.emu.x86.cpu_mut().state = x86::CPUState::Exit(exit_code);
self.status = Status::Exit(exit_code);
}
}
2 changes: 1 addition & 1 deletion win32/src/machine_unicorn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,7 @@ impl MachineX<Emulator> {

fn run(&mut self, eip: u32) {
let mut eip = eip as u64;
while matches!(self.status, Status::Running) {
while self.status.is_running() {
if let Err(err) = self.emu.unicorn.emu_start(eip, MAGIC_ADDR, 0, 0) {
self.status = Status::Error {
message: format!("unicorn: {:?}", err),
Expand Down
6 changes: 1 addition & 5 deletions x86/src/x86.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ pub enum CPUState {
DebugBreak,
SysCall,
Error(String),
Exit(u32),
}

impl CPUState {
Expand Down Expand Up @@ -246,10 +245,7 @@ impl X86 {
for (i, cpu) in self.cpus.iter().enumerate() {
match cpu.state {
CPUState::Running => {}
CPUState::DebugBreak
| CPUState::Error(_)
| CPUState::Exit(_)
| CPUState::SysCall => {
CPUState::DebugBreak | CPUState::Error(_) | CPUState::SysCall => {
self.cur_cpu = i;
return;
}
Expand Down

0 comments on commit c1735a7

Please sign in to comment.