Skip to main content

Crate ud_emulator

Crate ud_emulator 

Source
Expand description

Pure-Rust 32-bit x86 emulator + PE loader + Video for Windows host. Lets oxideav delegate decoding (and eventually encoding) to legitimately-licensed Windows codec DLLs on any platform.

Round 1 — “Load + DllMain + clean exit”. The crate ships:

  • emulator::mmu — flat 4 GiB virtual address space, sparse 4 KiB pages with R/W/X permission bits.
  • emulator::regs, emulator::decode, emulator::isa_int — i386 register file, instruction decoder, executor for the integer base ISA.
  • pe — PE32-only loader: DOS + PE header parse, section mapping into the MMU, base relocation, IAT resolution against the Win32 stub registry, export-by-name lookup.
  • win32::kernel32 — minimum stub set to satisfy a Cinepak-class DLL: GetProcessHeap / HeapAlloc / HeapFree / HeapReAlloc / LocalAlloc / LocalFree / OutputDebugStringA / GetTickCount / InterlockedIncrement / InterlockedDecrement / LoadLibraryA / GetProcAddress.

Round 2 — “Decode one Cinepak frame”. Adds:

  • Sandbox::call_export — generic stdcall guest-call helper.
  • win32::vfw32BITMAPINFOHEADER marshalling, ICDECOMPRESS layout, and the IC* host surface (ICOpen, ICClose, ICGetInfo, ICDecompressBegin, ICDecompressQuery, ICDecompress, ICDecompressEnd) that drives the codec DLL’s DriverProc end-to-end.
  • Sandbox::install_codec / Sandbox::ic_open etc — the ergonomic Rust-side wrappers the integration test uses.

Round 3 — “Real-codec smoke against IR32_32.DLL”. Adds:

  • tests/common/mod.rs — fixture-discovery helper: OXIDEAV_VFW_FIXTURE_DIR env var → Wine prefix → Windows system32 → on-disk cache → HTTPS fetch from samples.oxideav.org. CI=true bypasses the cache.
  • Round-3 m1 test asserted the exact set of 49 Win32 imports (gdi32 / user32 / winmm + 24 extra kernel32) the round-1+2 stub registry did not satisfy — round 4’s concrete dispatch budget. Round 4 closed every gap; the m1 test now asserts zero unresolved imports.
  • tests/m2_indeo3_driverproc.rs retained the synthetic-codec walkthrough; a forward-compatible Indeo 3 DllMain → ICOpen → ICGetInfo → ICClose walkthrough that activated once round 4 closed the import gaps.

Round 4 — “Close the 49 round-3 import gaps”. Adds the 49 stubs round 3 surfaced:

  • win32::gdi32 — 8 fail-soft stubs for BitBlt / CreateCompatibleDC / DeleteDC / GetDeviceCaps / GetNearestColor / GetObjectA / GetSystemPaletteEntries / SelectObject.
  • win32::kernel32 — 24 round-4 stubs covering the CRT init surface (ExitProcess, GetACP / GetOEMCP / GetCPInfo, GetCommandLineA / GetEnvironmentStrings / GetFileType, GetLastError / SetLastError, GetModuleFileNameA / GetModuleHandleA, GetStartupInfoA / GetStdHandle / GetSystemInfo / GetVersion, GlobalAlloc / GlobalFree / GlobalLock / GlobalUnlock, MultiByteToWideChar / WideCharToMultiByte, RtlUnwind, VirtualAlloc / VirtualFree, WriteFile).
  • win32::user32 — 16 fail-soft stubs covering the dialog / paint surface; MessageBoxA logs to stderr + host.message_box_log; wsprintfA is a real cdecl variadic implementation.
  • win32::winmmDefDriverProc (returns 0 / DRVCNF_OK).
  • emulator::mmu::Mmu::unmap + emulator::mmu::Mmu::find_free_range for the VirtualAlloc / VirtualFree family.

With round 4 in place, IR32_32.DLL loads cleanly and DllMain runs until it hits the first ISA opcode our integer interpreter does not yet decode: ADD AL, imm8 (opcode 0x04) at eip = 0x1000_612A. That was round-4’s hand-off to round 5.

Round 5 — “DllMain + ICOpen + ICGetInfo + ICClose against Intel IR32_32.DLL”. Adds:

  • The 8-bit primary ALU opcodes (0x00..=0x05 ADD, 0x08..=0x0D OR, 0x10..=0x15 ADC, …, 0x38..=0x3D CMP) plus r/m8 imm8 group-1 (0x80), r/m8 group-3 (0xF6), r/m8 group-4 (0xFE).
  • Group-2 r/m8 shifts (0xC0/0xD0/0xD2) plus the r/m32 1/cl variants (0xD1/0xD3).
  • IMUL r32, r/m32, imm32/imm8 (0x69/0x6B), XCHG r/m, r (0x86/0x87), SAHF/LAHF (0x9E/0x9F), CMC (0xF5), PUSHAD/POPAD (0x60/0x61), ENTER (0xC8), the full MOVS/CMPS/STOS/LODS/SCAS family with REP / REPE / REPNE prefixes.
  • 0F 40..4F CMOVcc, 0F A3 BT, 0F AB BTS, 0F A4..A5 SHLD, 0F AC..AD SHRD, 0F BA group-8 (BT/BTS/BTR/BTC imm8), 0F B1 CMPXCHG, 0F C1 XADD, 0F C8..CF BSWAP.
  • Per-instruction segment-override prefix routing through emulator::Cpu::set_fs_base / set_gs_base. The runtime maps a 4 KiB TEB at 0x7FFD_E000, primes FS:[0] (SEH chain end-of-list = -1) and FS:[0x18] (TEB self-pointer), and points FS at it.
  • Corrected vfw32::ICM_* numeric values (ICM_GETINFO = 0x5002, ICM_DECOMPRESS = 0x400D, etc).
  • win32::vfw32::ic_open now stages a real 36-byte ICOPEN so the codec’s DRV_OPEN allocates per-instance state (round 4 passed NULL).
  • win32::vfw32::ic_get_info falls back to an fcc-derived szName when the codec leaves it NUL (real vfw32!ICGetInfo fills it from the registry).
  • Bug-fix: round-4’s 0xC6 (MOV r/m8, imm8) handler fetched the immediate BEFORE resolving the displacement.

Round 6 — “Drive the full IC decode pipeline end-to-end against Intel IR32_32.DLL”.* No new emulator code needed: round-5’s ISA + segment-prefix coverage is sufficient for the ICDecompressQuery → ICDecompressBegin → ICDecompress → ICDecompressEnd sequence to walk cleanly. The tests/m2_indeo3_driverproc.rs::indeo3_decompress_one_keyframe integration test exercises the whole sequence against a synthetic IV31 keyframe (64×48). The codec accepts the input / output formats, sets up internal state, rejects the synthetic NULL-data-size sync frame at the bitstream-header validation step (returns ICERR_BADIMAGE = -100), and tears down cleanly. SPECGAP: the IV5PLAY fixture bundle ships only DLLs, no .avi payloads, so round-6 cannot exercise a real keyframe end-to-end. Round 7+ swaps the synthetic input for a real keyframe once one becomes available.

Round 7 — “Real IV31 keyframe decode through cubes.mov, plus MMX scaffolding”. Twin deliverables:

  • Part A — Real keyframe decode. Adds a test-side QuickTime / ISO BMFF chunk walker (tests/common/mov_extractor.rs, ~270 LOC, authored from ISO/IEC 14496-12 alone) that locates sample 0 in cubes.mov (160×120 yuv410p, 121 KB) from samples.oxideav.org/ffmpeg/V-codecs/IV32/. The new tests/round7_cubes_mov.rs::cubes_mov_first_keyframe_decodes_through_ir32_32_dll feeds the real 3079-byte IV31 keyframe through the IC* sequence; ICDecompress returns ICERR_OK and ~30 K of the 57.6 KB RGB24 output is non-zero — the first real pixel decode through IR32_32.DLL. The bug fix that unblocks this: ICM_DECOMPRESS_BEGIN was at ICM_USER + 16 = 0x4010 (round-5 typo) — an unmapped slot in IR32_32.DLL’s dispatch table. The canonical vfw.h value is ICM_USER + 12 = 0x400C. Without the BEGIN handler running, ICDecompress bailed early at a [state2_ptr] != 0 sentinel check. While here, ICM_DECOMPRESS_GET_FORMAT corrected from 0x40080x400A.
  • Part B — MMX scaffolding for round 8. emulator::Cpu grows an mmx: [u64; 8] register file (mm0..mm7, per Intel SDM Vol. 1 §9.2.1). A new emulator::Trap::UnimplementedMmx variant carries the 2-byte opcode + EIP + an SDM-derived mnemonic hint ("PADDB MMX", "PXOR MMX", "EMMS", …). The 0x0F 0x60..0x6F, 0x0F 0x70..0x7F, and 0x0F 0xD0..0xFF opcode blocks (per SDM Vol. 2 Appendix A Table A-3) now route through emulator::isa_int::dispatch_mmx to the structured trap instead of the generic UndefinedOpcode. Round 8 reads the trap log to land MMX semantics opcode-by-opcode.

Round 18 — trace Cargo feature. Resolves the planned “trace mode” milestone documented in docs/winmf/winmf-emulator.md §“Trace mode”. A new feature gate trace (off by default) adds #[cfg]’d probe sites at the four natural choke points: every dispatch_stub call (kind=win32_call), every guest memory access overlapping a registered watchpoint (kind=mem_write / kind=mem_read), every trap that bubbles out of the run loop (kind=trap), and — under the trace-exec sub-feature plus Sandbox::set_exec_trace(true) — every executed instruction (kind=exec). Output is JSONL on a sink configured via OXIDEAV_VFW_TRACE_FILE=<path|2> or Sandbox::set_trace_sink. With the feature off, every probe compiles away; release builds are bit-identical to the round-17 baseline. Companion CLI is oxideav-tracevfw.

Modern codecs (H.264 / HEVC / AV1 / Opus / AAC / …) are decoded natively elsewhere in the workspace; this crate exists for rare/legacy codecs the project would otherwise permanently shelve. Codec DLLs never execute on the host CPU; they run through the bounded-MMU interpreter.

See OxideAV/docs/winmf/winmf-emulator.md (659 lines, 13 sections) for the full design contract.

Re-exports§

pub use context::Context;
pub use context::FileAccess;
pub use context::FileHandle;
pub use context::OpenKey;
pub use context::RegistryKey;
pub use context::RegistryValue;
pub use context::VirtualFs;
pub use context::VirtualRegistry;
pub use context::HKCR;
pub use context::HKCU;
pub use context::HKEY_CLASSES_ROOT;
pub use context::HKEY_CURRENT_USER;
pub use context::HKEY_LOCAL_MACHINE;
pub use context::HKEY_USERS;
pub use context::HKLM;
pub use context::HKU;
pub use coverage::CoverageMap;
pub use ffi::CallArgs;
pub use ffi::Dword;
pub use ffi::FromRet;
pub use ffi::Guest;
pub use com::Guid;
pub use com::GuidParseError;
pub use com::CLSID_MEMORY_ALLOCATOR;
pub use com::IID_IBASEFILTER;
pub use com::IID_ICLASSFACTORY;
pub use com::IID_IENUMPINS;
pub use com::IID_IFILTERGRAPH;
pub use com::IID_IMEDIAFILTER;
pub use com::IID_IMEDIASAMPLE;
pub use com::IID_IMEMALLOCATOR;
pub use com::IID_IMEMINPUTPIN;
pub use com::IID_IPERSIST;
pub use com::IID_IPIN;
pub use com::IID_IUNKNOWN;
pub use com::MSADDS_AUDIO_DECODER_CLSID;
pub use com::MSADDS_AUDIO_PROPERTY_PAGE_CLSID;
pub use runtime::Sandbox;
pub use runtime::DLL_PROCESS_ATTACH;
pub use trace::TraceState;
pub use trace::WatchMode;
pub use trace::Watchpoint;
pub use win32::vfw32::Bih;

Modules§

com
COM (Component Object Model) scaffolding — round 25, stage 1.
context
Optional emulation-context layer.
coverage
Execution coverage tracker.
emulator
The 32-bit x86 software interpreter.
ffi
FFI-style front end for calling into an emulated guest.
pe
PE32 loader — parses Microsoft Portable Executable images, maps them into the emulator MMU, applies base relocations, resolves imports against the crate::win32::Registry, and exposes export-by-name lookup.
runtime
Top-level Sandbox — owns the MMU, the CPU, the Win32 stub registry, and the per-emulator host state, and exposes the “load this DLL and call its DllMain” workflow that the integration tests + future codec wrapper layers drive.
sched
Preemptive scheduler for the Win32 emulator.
trace
Reverse-engineering trace surface (gated on the trace Cargo feature).
win32
Win32 stub registry + per-DLL host implementations of the functions the loaded codec DLLs import.

Enums§

Error
Crate-local error type. Each layer (MMU / decoder / executor / PE loader / Win32 stub) has its own variant; sublayers nest their detail enums.

Type Aliases§

Result
Crate-local Result alias.