Skip to main content

Custom Backends

Sogen supports multiple CPU emulation backends through a pluggable architecture. Currently, two backends are available:
  1. Unicorn Engine (default) - Stable, widely-used CPU emulator based on QEMU
  2. Icicle (experimental) - High-performance Rust-based emulator with JIT compilation
The backend architecture allows Sogen to abstract away the underlying emulation engine, making it possible to switch backends or add new ones without changing higher-level Windows emulation code.

Architecture Overview

Emulator Abstraction Hierarchy

Sogen uses a layered abstraction for CPU emulation:

x86-64 Traits

x86 Emulator Interface

Backend Selection

Runtime Selection

Backends are selected at runtime through the create_x86_64_emulator() factory function:

Switching Backends

To use Icicle instead of Unicorn: Windows:
Linux/macOS:
To force Unicorn:

Unicorn Engine Backend

Overview

The default backend based on Unicorn Engine, which itself is based on QEMU. Advantages:
  • Stable and battle-tested
  • Wide architecture support
  • Mature ecosystem
  • Well-documented
Disadvantages:
  • Slower than JIT-based emulators
  • Based on older QEMU version
  • Limited optimization opportunities

Interface

Build Requirements

Unicorn is always available - it’s the default backend and has no special build requirements.

Icicle Backend

Overview

The experimental backend based on icicle-emu, a high-performance emulator written in Rust. Advantages:
  • Significantly faster than Unicorn (2-10x in many workloads)
  • Modern JIT compilation
  • Better suited for fuzzing
  • Active development
Disadvantages:
  • Experimental and less stable
  • Does not support WOW64 (x86 on x64)
  • Requires Rust toolchain
  • Less mature than Unicorn

Interface

Build Requirements

Icicle requires:
  1. Rust toolchain (cargo, rustc)
  2. Build flag: MOMO_ENABLE_RUST_CODE=ON
Installing Rust: Windows:
Linux/macOS:
Building with Icicle:

WOW64 Limitation

Icicle does not support automatic cross-architecture conversion:
If you need to run 32-bit Windows applications on 64-bit Sogen, use the Unicorn backend.

Fuzzing with Icicle

The fuzzer requires Icicle:
You cannot use the fuzzer without building with MOMO_ENABLE_RUST_CODE=ON.

Performance Comparison

Benchmark Results

Typical performance differences (highly workload-dependent): MIPS = Million Instructions Per Second

When to Use Icicle

Use Icicle for:
  • Fuzzing (significantly faster iteration)
  • Performance-critical analysis
  • Long-running emulation
  • Modern 64-bit applications

When to Use Unicorn

Use Unicorn for:
  • Stability-critical work
  • 32-bit applications (WOW64)
  • Environments without Rust
  • Production/stable analysis

Creating a Custom Backend

To implement a new backend (e.g., for a different emulator):

1. Implement the Interface

Create a class implementing x86_64_emulator:

2. Add Factory Function

3. Update Backend Selection

4. Build Integration

Add to CMakeLists.txt:

Backend Capabilities

Required Features

All backends must support:
  • x86-64 instruction emulation
  • Memory read/write
  • Register access
  • Execution control (start/stop)
  • Basic block hooks
  • Memory access hooks
  • Instruction hooks
  • Exception handling
  • Segment registers (FS/GS for TLS)
  • GDT loading

Optional Features

Backends may optionally support:
  • Hardware breakpoints
  • Watchpoints
  • Performance counters
  • Trace logging
  • JIT compilation
  • Multiple architectures

Debugging Backend Issues

Backend Not Loading

Check:
  1. Build flags are correct
  2. Environment variables are set
  3. Backend library is in the right location

Performance Issues

Profile to identify bottlenecks:

Instruction Emulation Errors

Some backends may not support all x86-64 instructions:

Source Code Reference

Key files:
  • src/backend-selection/backend_selection.hpp - Backend factory interface
  • src/backend-selection/backend_selection.cpp - Backend selection logic
  • src/emulator/arch_emulator.hpp - Emulator abstraction hierarchy
  • src/backends/unicorn-emulator/ - Unicorn implementation
  • src/backends/icicle-emulator/ - Icicle implementation

Best Practices

1. Default to Unicorn

Use Unicorn for general-purpose work:

2. Explicitly Select for Fuzzing

Always use Icicle for fuzzing:

3. Handle Backend Unavailability

4. Document Backend Requirements

In your tool’s documentation:

Next Steps