# How ipatool's Runtime Patch System Dynamically Patches Unicorn Library Functions

> Discover how ipatool's runtime_patch system dynamically patches Unicorn library functions by identifying and modifying specific 64-bit mask patterns in memory for improved performance.

- Repository: [Majd/ipatool](https://github.com/majd/ipatool)
- Tags: internals
- Published: 2026-09-06

---

**The runtime_patch system in ipatool loads the Unicorn CPU-emulation library into memory, searches for known 64-bit mask patterns that were truncated by the Windows 32-bit `unsigned long` type, flips the register-width bit in those masks, and persists the patched binary to disk for subsequent runs.**

The open-source tool **ipatool** (by majd/ipatool) includes a sophisticated runtime patching mechanism that fixes compatibility issues in the bundled Unicorn emulation library without requiring users to rebuild from source. This system specifically addresses a Windows ARM64 architecture bug where the Tiny Code Generator (TCG) backend was compiled with incorrect 32-bit type assumptions.

## How the Runtime Patch System Works

The patching pipeline operates in four distinct phases, each implemented across dedicated source files in `internal/sap/unicorn/`.

### Step 1: Load the Binary into Memory

The entry point `prepareRuntimeLibrary` in [`runtime_library_windows_arm64.go`](https://github.com/majd/ipatool/blob/main/runtime_library_windows_arm64.go) reads the original Unicorn DLL using `os.ReadFile`, loading the entire binary into a byte slice for in-memory manipulation.

```go
// From internal/sap/unicorn/runtime_library_windows_arm64.go
func prepareRuntimeLibrary(origPath string) (string, error) {
    data, err := os.ReadFile(origPath)
    if err != nil {
        return "", fmt.Errorf("read library: %w", err)
    }
    // ... patching logic follows
}

```

This approach avoids modifying the original distributed file and enables checksum validation before and after patching.

### Step 2: Locate the Mask Patterns with Pattern Matching

The core search logic lives in [`runtime_patch.go`](https://github.com/majd/ipatool/blob/main/runtime_patch.go). The `patternOffsets` function scans the binary image for two specific 8-byte sequences using `bytes.Index` in a loop to find all occurrences:

- `tcgMaskFirstPattern` — the first mask constant
- `tcgMaskSecondPattern` — the second mask constant (must appear exactly 24 bytes after its paired first pattern)

```go
// From internal/sap/unicorn/runtime_patch.go
func patternOffsets(image []byte, pattern []byte) []int {
    var offsets []int
    start := 0
    for {
        idx := bytes.Index(image[start:], pattern)
        if idx == -1 {
            break
        }
        offset := start + idx
        offsets = append(offsets, offset)
        start = offset + 1
    }
    return offsets
}

```

The function collects **every offset** where each pattern appears, returning a slice of positions for validation in the next step.

### Step 3: Apply the Register-Width Patch

The `patchWindowsARM64TCGMasks` function performs the actual bytecode modification. For each valid pair of offsets (where second offset = first offset + 24), it sets the high-order bit (`0x80`) in specific byte positions:

| Byte Position | Operation | Effect |
|-------------|-----------|--------|
| `first+3`  | `image[first+3] \|= 0x80` | Flips register-width bit in first mask |
| `first+7`  | `image[first+7] \|= 0x80` | Completes first mask correction |
| `second+3` | `image[second+3] \|= 0x80` | Flips register-width bit in second mask |
| `second+7` | `image[second+7] \|= 0x80` | Completes second mask correction |

This surgical modification changes only the "register-width" flag without altering surrounding instruction bytes, preserving the original code structure while correcting the 64-bit semantics.

The patch validates two invariants before proceeding:
- **Exactly `windowsARM64TCGPatternCount` (16) matches** must exist for each pattern
- **Spacing must be exactly 24 bytes** between paired first and second patterns

Violation of either condition causes immediate abort with an error, protecting against application to unknown or corrupted builds.

### Step 4: Persist and Cache the Patched Library

After successful modification, `prepareRuntimeLibrary` writes the patched image to a temporary file, syncs and closes it, then renames to `libunicorn-windows-arm64.dll`. A checksum of the patched binary is cached to eliminate redundant patching on subsequent executions.

```go
// Simplified flow from runtime_library_windows_arm64.go
patchedPath := filepath.Join(cacheDir, "libunicorn-windows-arm64.dll")
if checksumMatches(patchedPath, expectedChecksum) {
    return patchedPath, nil // Already patched, skip work
}

// ... patch application ...

if err := installLibrary(tempFile, patchedPath); err != nil {
    return "", err
}

```

## Integrating Patched Libraries in Go Code

The patching happens transparently during engine initialization. Application code never calls the patch functions directly:

```go
// Load the original Windows-ARM64 Unicorn DLL.
origPath := "/path/to/unicorn.dll"
patchedPath, err := prepareRuntimeLibrary(origPath)
if err != nil {
    log.Fatalf("failed to prepare Unicorn library: %v", err)
}

// `patchedPath` now points to libunicorn-windows-arm64.dll
// with the ARM64 TCG masks correctly patched.
engine, err := unicorn.NewEngine(patchedPath)

```

In practice, ipatool's [`library_windows.go`](https://github.com/majd/ipatool/blob/main/library_windows.go) invokes `prepareRuntimeLibrary` automatically when constructing the emulation engine on Windows ARM64 hosts.

## Source File Architecture

| File | Responsibility |
|------|--------------|
| [`internal/sap/unicorn/runtime_patch.go`](https://github.com/majd/ipatool/blob/main/internal/sap/unicorn/runtime_patch.go) | Pattern search (`patternOffsets`) and mask patching logic (`patchWindowsARM64TCGMasks`) |
| [`internal/sap/unicorn/runtime_library_windows_arm64.go`](https://github.com/majd/ipatool/blob/main/internal/sap/unicorn/runtime_library_windows_arm64.go) | DLL loading, checksum validation, patched library installation |
| [`internal/sap/unicorn/runtime_patch_test.go`](https://github.com/majd/ipatool/blob/main/internal/sap/unicorn/runtime_patch_test.go) | Unit tests verifying patch success on valid images and proper failure on malformed data |
| [`internal/sap/unicorn/library_windows.go`](https://github.com/majd/ipatool/blob/main/internal/sap/unicorn/library_windows.go) | Platform-specific loader integrating the patch system |

## Why This Approach Matters

**Binary patching at runtime** solves a distribution problem: ipatool can ship a single pre-compiled Unicorn library while still functioning correctly across architectures with incompatible ABI assumptions. The Windows ARM64 case specifically stems from the TCG backend using `unsigned long` (32-bit on Windows LLP64, 64-bit on Linux LP64) for mask calculations, causing silent truncation of 64-bit constants.

Rather than maintaining separate build pipelines or requiring users to compile from source, ipatool's runtime_patch system:
- Detects the specific buggy pattern signatures
- Applies minimal, verified corrections
- Caches results for performance
- Fails safely on unexpected binaries

This pattern of dynamic library patching demonstrates how Go-based tools can adapt native dependencies to heterogeneous deployment environments without sacrificing user experience.

## Summary

- **Runtime patching enables architecture adaptation** without source rebuilds or manual intervention
- **Pattern-based search locates specific bytecode sequences** using `bytes.Index` loops in `patternOffsets`
- **Surgical bit-flipping corrects register-width flags** at calculated byte offsets without disturbing surrounding instructions
- **Validation guards** ensure the patch only applies to known-good binaries with expected pattern counts and spacing
- **Checksum caching eliminates redundant work** across process restarts
- **Transparent integration** means callers use standard engine initialization while the patch system handles compatibility automatically

## Frequently Asked Questions

### How does ipatool verify the patch won't corrupt the Unicorn library?

The patch system enforces two strict invariants: exactly 16 matches for each pattern (`windowsARM64TCGPatternCount`) and exact 24-byte spacing between paired patterns. If either check fails, `patchWindowsARM64TCGMasks` returns an error and the original binary remains unmodified. See [`internal/sap/unicorn/runtime_patch_test.go`](https://github.com/majd/ipatool/blob/main/internal/sap/unicorn/runtime_patch_test.go) for test coverage of these failure modes.

### Can the runtime_patch system be used for other platforms or libraries?

The current implementation is specialized for Windows ARM64 TCG masks, but the pattern-matching architecture in [`runtime_patch.go`](https://github.com/majd/ipatool/blob/main/runtime_patch.go) is generic. The `patternOffsets` function accepts arbitrary byte slices and patterns, making it reusable for similar binary-correction scenarios requiring runtime adaptation of pre-compiled libraries.

### Why modify bytes at offset +3 and +7 specifically?

These positions correspond to the high-order byte of 32-bit words within the 64-bit mask constants. Setting bit 7 (`0x80`) at these offsets flips the effective register-width flag in the TCG's internal representation, promoting the truncated 32-bit values back to proper 64-bit semantics without changing the instruction encoding structure.

### Where does the patched library get stored on disk?

`prepareRuntimeLibrary` writes to a platform-appropriate cache directory as `libunicorn-windows-arm64.dll`. The function uses atomic rename operations (write to temp, sync, close, then move) to prevent corrupted partial files, and checksum comparison prevents unnecessary re-patching on subsequent runs.