Status: draft / proposal. This documents the target layering for splitting
libmwemu into focused, reusable crates, and the order to get there without
hitting circular dependencies.
libmwemu has grown into a monolith that mixes several independent concerns:
- a binary loader/parser (PE32/PE64, ELF, Mach-O),
- the CPU engines (x86/x64 and AArch64 instruction emulation),
- OS API emulation (Windows / Linux / macOS userland + syscalls + structures),
- the memory model (maps), registers, config, serialization,
- and the orchestration that ties it all into the
Emuobject.
Splitting these makes each piece reusable on its own (e.g. the PE parser for any tool, not just mwemu), shrinks the compile/test surface per crate, clarifies ownership, and lets the engines/OS layers evolve independently.
mwemu (CLI)
│
libmwemu ── thin orchestrator: the Emu that wires
│ a Cpu + an Os + memory, plus the run
┌──────────┬─────────┼───────────┬──────────┐ loop, serialization,
mwemu-x86 mwemu-aarch64 mwemu-win32 mwemu-pe … and the public API.
└──────────┴─────────┼───────────┴──────────┘
mwemu-core ── shared contract: memory (Maps) + Memory
trait, register banks, Config, errors,
shared structures, and the Cpu/Os traits.
The one rule that makes this work: every crate depends downward on
mwemu-core. Nothing depends on libmwemu except the CLI. That breaks the
otherwise-circular Emu ↔ engine ↔ Emu relationship.
| Crate | Responsibility | Depends on |
|---|---|---|
| mwemu-core | Memory model + Memory trait, registers, Config, error types, shared structures, the Cpu / Os traits. The shared contract. |
— |
| mwemu-pe | Pure PE32/PE64 parsing → data structs (headers, sections, import/export/reloc/resource tables). No emulator state. | (nothing / core for a memory-writer trait if needed) |
| mwemu-x86 | x86/x64 decode + execute. Implements Cpu. |
mwemu-core |
| mwemu-aarch64 | AArch64 decode + execute. Implements Cpu. |
mwemu-core |
| mwemu-win32 | Windows API/syscall emulation + loading. Implements Os. |
mwemu-core, mwemu-pe |
| (later) mwemu-linux / mwemu-macos | Linux / macOS OS layers. Implement Os. |
mwemu-core, mwemu-pe(elf/macho) |
| libmwemu | The Emu orchestrator: core state + Box<dyn Cpu> + Box<dyn Os>, the run loop, serialization, the public API consumed by pymwemu/cmwemu/mwemu-mcp. |
all of the above |
| mwemu | Command-line front end. | libmwemu |
This is the hard part — deciding the boundary. Candidates to live here:
- Memory: the
Mapsmodel + aMemorytrait (read/write_*,is_mapped,get_mem_by_addr_mut, …) so engines and loaders touch memory through an interface, not the concreteEmu. - Registers: the x86
Regs64andRegsAarch64banks (or a shared trait). - Config, error types (
MwemuError). - Shared Windows structures — open question (see below).
- Traits:
trait Cpu—decode,step, register access, current PC/SP. Implemented by mwemu-x86 and mwemu-aarch64.trait Os— syscall/API dispatch, process/PEB/TEB setup. Implemented by mwemu-win32 (and later linux/macos).
The exact trait signatures are the key design work; they must be expressive enough for libmwemu to drive any engine/OS without depending on the concrete implementation.
Today the PE layer mixes two concerns in the same structs:
- Parsing (headers, sections, the import/export/reloc/resource tables as
data) — already free of
Emu. → goes tomwemu-pe. - Binding into the emulator (
iat_binding,apply_relocations,delay_load_binding,get_dependencies) — takes&mut Emu, writes toemu.maps, resolves APIs via winapi. → stays in libmwemu/win32, now consumingmwemu-pe's parsed output + aMemorywriter.
So mwemu-pe is not a copy-paste of the existing files; the binding/reloc
methods must be peeled off the PE structs first. The payoff is a PE parser any
project can use standalone.
mwemu-x86/mwemu-aarch64are the bulk of today'slibmwemu. They implementCpuagainstmwemu-core's memory + register types.mwemu-win32implementsOs; it owns the syscall table, API stubs, PEB/TEB, and the PE→memory binding (usingmwemu-pe).
- Design (this doc) — agree the
mwemu-coreboundary and theCpu/Ostrait shapes. - Extract
mwemu-core— the shared types + traits. Biggest enabler; until it exists, any satellite extraction hits the circular-dep wall. mwemu-pe— do the parse/bind split. Good pilot of the pattern.mwemu-x86behind theCputrait — proves the trait is expressive.mwemu-aarch64— secondCpuimpl.mwemu-win32(then linux/macos) behind theOstrait.libmwemubecomes the thin orchestrator.
Each phase publishes to crates.io and the ones above re-pin (the
version-lockstep cost we already know — keep = "0.x" caret ranges).
- Hot-loop dispatch cost.
Box<dyn Cpu>in the per-instructionsteploop adds dynamic dispatch. Consider enum dispatch (enum Cpu { X86(..), Aarch64(..) }) or generics/monomorphization instead of trait objects in the hot path. - Where do Windows structures live? They're used by both
mwemu-win32and parts of loading. Options: inmwemu-core, inmwemu-pe, or a dedicatedmwemu-structurescrate. Decide before phase 1. - Serialization spans all state. Save/restore touches core + engine + OS; needs a cross-crate strategy (likely traits in core, impls per crate).
- Trait granularity. Too fine = churn; too coarse = leaks implementation.
Iterate on
Cpu/Osagainst one real engine (phase 3) before committing. - Keep the dependency rule strict. The moment a satellite imports
libmwemu, the cycle returns.
mwemu-linux / mwemu-macos OS layers and any LIEF-backed loader work — revisit after the Windows/x86 path proves the layering.