WIE

Run 64-bit Windows binaries on Apple Silicon macOS without Windows, Wine, or virtual machines.

What it is

I wanted to see if I could run 64-bit Windows executables on Apple Silicon without launching a heavy virtual machine or setting up Wine prefixes. WIE (Wie Is Emulator) is a user-mode emulator written in Rust. It compiles guest x86-64 code to ARM64 on the fly with Cranelift, intercepts Windows API calls, and handles them directly on macOS.

GUI apps open real macOS windows with native Cocoa menus, system fonts, and file dialogs. Guest file operations map to a clean app bottle or straight to host files picked in native Open/Save dialogs.

Architecture

The workspace is split into focused Rust crates:

  • PE64 loader & memory: Parses PE headers, maps sections, resolves imports/exports, and manages guest virtual address space with structured exception handling (SEH).
  • Cranelift block JIT: Translates basic blocks of x86-64 instructions into native ARM64 machine code at runtime.
  • Win32 host runtime: Re-implements Kernel32, User32, and GDI32 calls in Rust, routing them to macOS system APIs.
  • Direct3D 9 renderer: Software rasterizer with vertex and pixel shaders (VS 2.0 / PS 2.0 with flow control), mipmapped textures, and Metal presentation via wgpu.
flowchart TD
    subgraph Host["Host macOS (ARM64)"]
        CLI["wie CLI / Runner"]
        API["Win32 Host Handlers (Kernel32, User32, GDI32)"]
        UI["Native Cocoa Windows & Dialogs"]
        D3D["Direct3D 9 Software Renderer"]
        GPU["wgpu (Metal Backend)"]
    end
    subgraph Guest["Guest x86-64 PE64"]
        PE["PE64 Binary Loader"]
        JIT["Cranelift Block JIT (x86-64 to ARM64)"]
        MEM["Guest Memory / Bottle Filesystem"]
    end
    PE --> JIT
    JIT <--> MEM
    JIT -->|WinAPI Import Thunks| API
    API --> UI
    API --> D3D
    D3D --> GPU

Seeing a real Windows binary open a native Cocoa window without Wine felt like magic the first time it worked.

What runs today

The engine already runs several real-world binaries:

  • Windows Notepad: Full UI with menus, Find & Replace, Go To, font picker, and printing via macOS print panels.
  • 7-Zip Console: Creating, listing, and extracting .7z archives.
  • Threads & sync: 1:1 guest threads, critical sections, Windows events, semaphores, and Thread Local Storage (TLS).
  • Terminal games: Snake and 2048 compiled with mingw-w64.

The hard parts

Calling conventions and ABI bridging. Windows x64 uses RCX, RDX, R8, R9 with a 32-byte shadow space on the stack; macOS ARM64 uses AAPCS64. Preserving non-volatile registers, aligning stack frames, and keeping register state coherent during JIT-to-host transitions took weeks in lldb.

Direct3D 9 shader rasterizer. Building a software renderer with full mip chains, depth testing, and programmable pixel shaders with branching in pure Rust was a massive math workout.

What I learned

Emulation is 10% clean architecture and 90% edge cases. Real Windows binaries rely on undocumented Win32 behaviors, unaligned memory accesses, and weird CPUID flags. You can't just follow a specification; you have to run real software and fix what breaks.