Skip to content

swapping_into_crossover

runner edited this page Oct 5, 2026 · 15 revisions

Swapping a Custom Wine into CrossOver

Section: Engineering Reference → Swapping Wine into CrossOver

Status: ✅ Verified (2026-09, CrossOver 26.3.0 / macOS 27.0 / M4 Pro)

Purpose: This page documents how to inject a custom-built Wine tree into a CrossOver installation so that patched modules (the ACE anti-cheat fixes, Rosetta 2 signal handling, and ntoskrnl.exe backports) can be deployed into a running CrossOver environment.


1. Overview

Endfield_FineWine does not redistribute CrossOver. Instead, it builds a minimal, patched Wine from CrossOver's own source and surgically swaps only the modules that need modification into a copy of CrossOver.app. This keeps the rest of CrossOver's proprietary graphics, audio, and framework layers intact.

The swap process has three distinct phases:

Phase What happens Why it matters
1. Copy Duplicate CrossOver.app to a staging location Preserves the original install; avoids SIP/Gatekeeper conflicts
2. Swap Replace 3 Wine modules (ntdll.so, kernel32.dll, ntoskrnl.exe) These carry the anti-cheat and Rosetta fixes
3. Re-seal Re-sign the bundle ad-hoc and strip quarantine macOS will refuse to run a modified bundle without a valid signature
flowchart TD
    A[Stock CrossOver 26.3.app] --> B[Copy to staging]
    B --> C[Swap 3 patched Wine modules]
    C --> D[Add LC_RPATH to ntdll.so]
    D --> E[Ad-hoc re-sign bundle]
    E --> F[Verify signature]
    F --> G[Launch CrossOver_Endfield_Patch.app]
```html

---

## 2. Understanding the Barriers

Before attempting the swap, it helps to understand what macOS is actually protecting and what will block you.

### 2.1 System Integrity Protection (SIP)

**Myth:** SIP prevents you from modifying `CrossOver.app`.

**Reality:** SIP filesystem protection in `/Applications` covers only **Apple's preinstalled apps** (Safari, Terminal, Console, App Store, Notes, etc.). User-installed applications like CrossOver are freely modifiable with normal permissions.

```bash

# SIP does NOT protect CrossOver.app

csrutil status          # You can keep this enabled
ls -la /Applications/CrossOver.app   # Readable/writable by your user
```text

**Bottom line:** You do **not** need to disable SIP (`csrutil disable`) to patch CrossOver.

### 2.2 Code-Signing Integrity

This is the **real** barrier. When you edit binaries inside a signed `.app`, you invalidate the bundle seal (`Contents/CodeResources` + `Contents/_CodeSignature`). Under the hardened runtime with library validation, replacement dylibs not signed by Apple or the same Team ID will not load.

There are two approaches:

| Approach | Mechanism | Reliability on current macOS |
|---|---|---|
| **(A) Strip the signature** | Remove/disable `Contents/CodeResources` + `Contents/_CodeSignature` | ❌ **Fails** if the copy carries a `com.apple.provenance` xattr |
| **(B) Ad-hoc re-sign** | `codesign --force --sign -` on the whole bundle | ✅ **Works** either way |

**Why (B) is preferred:** On Apple Silicon, every Mach-O must carry at least an ad-hoc signature to execute. Approach (B) re-seals the outer bundle while preserving CodeWeavers' nested signatures and entitlements.

### 2.3 Quarantine and Provenance

macOS attaches two special xattrs that block modification:

| xattr | Meaning | How to remove |
|---|---|---|
| `com.apple.quarantine` | Gatekeeper check flag from download | `xattr -drs com.apple.quarantine` |
| `com.apple.provenance` | Attached to files created by Gatekeeper-checked apps | `xattr -rd com.apple.provenance` |
| `com.apple.FinderInfo` | Finder metadata detritus | `xattr -rd com.apple.FinderInfo` |

⚠️ **Critical:** If you attempt a swap on a bundle that has already been launched once, macOS tags it with `com.apple.macl` and makes the bundle **read-only**. You must stage the copy in a fresh location (e.g. `$TMPDIR`) and only move it into `/Applications` after it verifies.

---

## 3. CXPatcher: What It Does (and Doesn't Do)

[CXPatcher](https://github.com/italomandara/CXPatcher) is a proven community tool for editing CrossOver bundles. Understanding its mechanism helps you decide what to build on top of it.

### 3.1 Verified Mechanism

Reading `Utils.swift` and `Config.swift` directly:

- Copies bundled replacement resources into the CrossOver tree via `safeResCopy` / `safeFileCopy`, **renaming any pre-existing target to `<name>_orig`** first.
- Disables files by renaming to `<name>_disabled` (`disable()` helper; `restoreFile`/`enable` revert).
- **Strips the bundle's code signature by default** (`var removeSignaure = true`): moves/disables `Contents/CodeResources` and `Contents/_CodeSignature` — **no `codesign`/`xattr`/`spctl` call anywhere** in its source.
- Overrides the bottle path to a `CXP`-prefixed folder by editing the embedded `CrossOver.conf`.
- Outputs **`CrossOver_patched.app`**, leaving the original untouched.

```swift
// Verified constants from Config.swift
SUPPORTED_CROSSOVER_VERSION = "23.7"
DEFAULT_CX_BOTTLES_PATH     = /Users/${USER}/CXPBottles
EXTERNAL_RESOURCES_ROOT     = /lib64/apple_gpt        # GPTK / D3DMetal payload
WINE_RESOURCES_ROOT         = Crossover
```javascript

### 3.2 Why CXPatcher Alone Is Insufficient

CXPatcher **does not replace the Wine binary** or inject custom low-level components. Its maintainer confirmed (Discussion #239) there is **no supported path for injecting low-level components like ntsync**; a custom sync/kernel implementation requires **building a hybrid Wine**, not dropping in a DLL.

**Implication for Endfield_FineWine:** The ACE fixes we need — custom `ntoskrnl.exe` functions, the int3 `kernel32` hack, Wine-hiding — **cannot** be delivered via CXPatcher. You must build a custom Wine and swap the actual binaries/libraries. CXPatcher remains useful as:

1. A proven *pattern* for editing the bundle and stripping the signature.
2. The tool for the *graphics* layer (D3DMetal/DXMT/DXVK) once the game launches.

---

## 4. Manual Swap Procedure (Scripted — Recommended)

The repository automates the entire swap in [`scripts/swap-into-crossover.sh`](../scripts/swap-into-crossover.sh). Here is what it does, step by step.

### 4.1 Prerequisites

```bash

# Ensure CrossOver 26.3 is installed (the swapped modules must match the build's Wine ABI)

defaults read /Applications/CrossOver.app/Contents/Info CFBundleShortVersionString

# Expected: 26.3.x

# Verify Rosetta 2 is installed (the patched Wine is x86_64)

softwareupdate --list | grep -i rosetta
```bash

### 4.2 Running the Swap Script

```bash
cd Endfield_FineWine
./scripts/swap-into-crossover.sh
```bash

The script performs:

1. **Stage a fresh copy** of `CrossOver.app` in `$TMPDIR` (never edits the live install).
2. **Swap the 3 patched modules** into `Contents/SharedSupport/CrossOver/lib/wine/`:
  - `x86_64-unix/ntdll.so` — Rosetta 2 signal fixes + QPC timing
  - `x86_64-windows/kernel32.dll` — `KiUser*Dispatcher` int3 spoof
  - `x86_64-windows/ntoskrnl.exe` — 17 `ntoskrnl.exe` em-backports
3. **Inject the `LC_RPATH`** into `ntdll.so` so `cxcompatdb.so` can find `libgnutls.30.dylib` in `lib64/`.
4. **Re-seal the bundle** ad-hoc with `--preserve-metadata=entitlements`.
5. **Verify** the signature before moving into `/Applications`.
6. **Move** the verified bundle to `/Applications/CrossOver_Endfield_Patch.app`.

### 4.3 Manual Equivalent (For Auditing)

If you prefer to do it by hand to understand or audit each step:

```bash

# 1. Copy CrossOver (must be 26.3) so the original stays intact

APP="/Applications/CrossOver_Endfield_Patch.app"
ditto --noextattr --noqtn /Applications/CrossOver.app "$APP"
CXR="$APP/Contents/SharedSupport/CrossOver"
B="$PWD/build/wine-build64"

# 2. Swap the 3 patched modules (move originals aside as backups)

for f in x86_64-unix/ntdll.so x86_64-windows/kernel32.dll x86_64-windows/ntoskrnl.exe; do
  mv "$CXR/lib/wine/$f" "$CXR/lib/wine/$f.cxorig"
done
cp "$B/dlls/ntdll/ntdll.so"                           "$CXR/lib/wine/x86_64-unix/ntdll.so"
cp "$B/dlls/kernel32/x86_64-windows/kernel32.dll"     "$CXR/lib/wine/x86_64-windows/kernel32.dll"
cp "$B/dlls/ntoskrnl.exe/x86_64-windows/ntoskrnl.exe" "$CXR/lib/wine/x86_64-windows/ntoskrnl.exe"

# 3. ntdll.so needs CrossOver's lib64 rpath (cxcompatdb -> gnutls -> D3DMetal)

install_name_tool -add_rpath "@loader_path/../../../lib64" "$CXR/lib/wine/x86_64-unix/ntdll.so"

# 4. Re-seal the outer bundle ad-hoc (nested CodeWeavers signatures + entitlements kept)

xattr -drs com.apple.quarantine "$APP"
xattr -rd com.apple.FinderInfo "$APP"
codesign --force --sign - --preserve-metadata=entitlements "$APP"
codesign --verify --deep --strict "$APP" && echo "patched app verifies"
```bash

### 4.4 Why Re-Seal Instead of Stripping

Stripping the signature (approach A) **fails** once the copy carries a `com.apple.provenance` xattr. macOS attaches this to files created by apps that were Gatekeeper-checked after download — observed with shells spawned by coding agents; Terminal.app (an Apple app) isn't provenance-tracked, which is why stripping may appear to work there.

With the seal stripped and a provenance tag present, **every binary inside the bundle is SIGKILLed** (`wineserver --version` exits 137, even unmodified ones) and a *"CrossOver_Endfield_Patch is damaged and can't be opened"* dialog appears on every attempt. After that first failure, macOS also tags the bundle with `com.apple.macl` and blocks further edits inside it.

Re-sealing (approach B) with `--preserve-metadata=entitlements`:

- Keeps nested CodeWeavers signatures intact.
- `bin/wineloader` and `bin/wineserver` carry `com.apple.security.cs.disable-library-validation`, so they load the ad-hoc-signed `ntdll.so`.
- The swapped PE modules (`kernel32.dll`, `ntoskrnl.exe`) need no signature of their own — stock CrossOver's PE files have none; the bundle seal covers them.
- The main executable keeps its entitlements (`apple-events`, `allow-unsigned-executable-memory`, camera/mic) but loses the hardened runtime — acceptable since no restricted entitlements are involved.
- GPTK's `D3DMetal.framework` / `libd3dshared.dylib` keep Apple's signature ("Software Signing") when copied with `ditto --noextattr`.

---

## 5. The RPATH Fix: A Common Pitfall

One of the most subtle issues in this project is the `LC_RPATH` on `ntdll.so`.

### 5.1 The Problem

CrossOver's `ntdll.so` dynamically loads `cxcompatdb.so` at process startup to configure graphics backends (`CX_GRAPHICS_BACKEND=d3dmetal`). `cxcompatdb.so` depends on `@rpath/libgnutls.30.dylib` in `lib64/`.

dyld resolves rpaths through the **calling binary** (`ntdll.so`). Because the minimal build did not carry the custom rpath, `cxcompatdb.so` silently failed to load, falling back to WineD3D and producing error `80004005` (DirectX device creation failure).

### 5.2 The Fix

```bash
install_name_tool -add_rpath "@loader_path/../../../lib64" \
  "$CXR/lib/wine/x86_64-unix/ntdll.so"
```bash

**Verification:** After the fix, every game process logs:

```bash
set_graphics_backend using d3dmetal as the graphics backend
```text

Without it, you see:

```text
warn:module:start_main_thread error loading cxcompatdb.so: Library not loaded: @rpath/libgnutls.30.dylib
err:winediag:wined3d_adapter_create Using the Vulkan renderer for d3d10/11 applications
```text

⚠️ **Do not "fix" graphics failures by overwriting `lib/wine/x86_64-windows/{d3d11,d3d12,dxgi}.dll` with the `apple_gptk` copies.** Those are only meant to load through CrossOver's own D3DMetal backend path; dropping them in as defaults makes `unityplayer.dll` fail to initialize (**Windows error 1114**, "missing or corrupt").

---

## 6. Verification Checklist

Before launching the game, run through this checklist to ensure the swap is valid.

```bash
APP="/Applications/CrossOver_Endfield_Patch.app"
CXR="$APP/Contents/SharedSupport/CrossOver"

# 1. Bundle signature verifies

codesign --verify --deep --strict "$APP" && echo "✅ bundle verifies"

# 2. Patched modules are in place

ls -la "$CXR/lib/wine/x86_64-unix/ntdll.so"
ls -la "$CXR/lib/wine/x86_64-windows/kernel32.dll"
ls -la "$CXR/lib/wine/x86_64-windows/ntoskrnl.exe"

# 3. RPATH is set on ntdll.so

otool -L "$CXR/lib/wine/x86_64-unix/ntdll.so" | grep -i rpath

# 4. Original modules backed up

ls "$CXR/lib/wine/"*.cxorig

# 5. wineserver responds (exit 137 = killed by signature failure)

"$CXR/bin/wineserver" --version

# 6. No quarantine/provenance/FinderInfo on the bundle

xattr -l "$APP" | grep -E 'quarantine|provenance|FinderInfo' || echo "✅ no blocking xattrs"
```bash

---

## 7. Troubleshooting

### 7.1 "CrossOver_Endfield_Patch is damaged and can't be opened" (repeated)

**Symptom:** The dialog appears on every launch, and binaries are killed with exit 137.

**Cause:** The patched bundle's signature seal is missing or broken, or the copy carries a `com.apple.provenance` xattr.

**Fix:** Click **Cancel** (not *Move to Trash*), then re-run the swap script, which re-seals it:

```bash
./scripts/swap-into-crossover.sh
Loading

Clone this wiki locally