Repository navigation
swapping_into_crossover
runner edited this page Oct 5, 2026
·
15 revisions
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.exebackports) can be deployed into a running CrossOver environment.
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