How to Cross-Compile the Objective-C Guest Daemon for vphone-cli

Cross-compile the vphoned daemon for iOS using xcrun -sdk iphoneos and sign it with ldid-procursus before bundling into the macOS host app.

The vphone-cli project by Lakr233 runs a lightweight iOS virtual machine on Apple Silicon Macs. The guest-side daemon vphoned — written in Objective-C — must be built on the host macOS machine but target the arm64 iphoneos SDK. This article walks through the exact cross-compilation pipeline implemented in the repository, including the Makefile, signing process, and bundling steps.

Prerequisites for Cross-Compilation

The toolchain requires specific Apple and open-source components:

  • Apple Silicon macOS 15+ — Required for Virtualization.framework and for xcrun -sdk iphoneos to function
  • Xcode with iOS SDK — Provides the clang cross-compiler targeting iphoneos
  • ldid-procursus — Installed via Homebrew; performs ad-hoc code signing with custom entitlements
  • libarchive and sqlite3 — Linked by the daemon at build time
  • signcert.p12 — A self-signed certificate shipped in scripts/vphoned/ for ldid signing

Install all host dependencies with:

brew install python@3.13 aria2 wget gnu-tar openssl@3 ldid-procursus \
            keystone cmake libusb ipsw zstd

The Cross-Compilation Pipeline

The complete build flow is orchestrated by scripts/build.sh. When invoked, it performs three distinct phases for the guest daemon.

Phase 1: Compile with the Makefile

The Makefile at scripts/vphoned/Makefile drives the actual cross-compilation:

xcrun -sdk iphoneos clang -arch arm64 -Os -fobjc-arc \
    -I. -Ivendor/libarchive \
    -DVPHONED_BUILD_HASH='"$(GIT_HASH)"' \
    -o vphoned *.m \
    -larchive -lsqlite3 \
    -framework Foundation \
    -framework Security \
    -framework CoreServices

Key flags explained:

Flag Purpose
-sdk iphoneos Targets the iOS SDK, not macOS
-arch arm64 Builds for Apple Silicon iOS devices
-fobjc-arc Enables Automatic Reference Counting for Objective-C
-framework Foundation/Security/CoreServices Links iOS system frameworks

Phase 2: Sign with Entitlements

The daemon requires extensive entitlements to function inside the iOS VM. The build script signs the binary using ldid-procursus (lines 64-71 of scripts/build.sh):

ldid -Sscripts/vphoned/entitlements.plist \
     -M "-Kscripts/vphoned/signcert.p12" \
     .build/vphoned.signed

The -S flag applies entitlements from scripts/vphoned/entitlements.plist. The -M -K combination uses the self-signed signcert.p12 for ad-hoc signature generation.

Phase 3: Bundle into the Host App

The signed binary is copied to .build/vphoned.signed, then placed within the final app bundle:

cp .build/vphoned.signed \
    .build/vphone-cli.app/Contents/Resources/vphoned.signed

The host vphone-cli binary later loads this resource and injects it into the iOS guest via vsock.

Step-by-Step Build Instructions

Full Automated Build

Clone with submodules and run the top-level script:

git clone --recurse-submodules https://github.com/Lakr233/vphone-cli.git
cd vphone-cli
./scripts/build.sh

This builds the host binary, cross-compiles the daemon, signs it, and bundles everything.

Daemon-Only Build (Skip Host Binary)

./scripts/build.sh --no-vphoned

Manual Cross-Compilation (Exact Commands)

For custom toolchains or debugging, replicate the script's behavior manually:


# 1. Build the daemon

make -C scripts/vphoned \
    GIT_HASH="$(git rev-parse --short HEAD 2>/dev/null || echo unknown)"

# 2. Prepare output directory

mkdir -p .build
cp scripts/vphoned/vphoned .build/vphoned.signed

# 3. Sign with entitlements

ldid -Sscripts/vphoned/entitlements.plist \
     -M "-Kscripts/vphoned/signcert.p12" \
     .build/vphoned.signed

# 4. Verify architecture

file .build/vphoned.signed

# Expected: Mach-O 64-bit executable arm64

Key Implementation Files

Understanding these source files clarifies the cross-compilation architecture:

  • scripts/vphoned/Makefile — Encapsulates the xcrun clang invocation with iOS-specific flags and framework linking
  • scripts/vphoned/*.m — Objective-C implementation files including vphoned.m and vphoned_location.m that compile to the daemon
  • scripts/vphoned/entitlements.plist — Extensive entitlements granting the daemon capabilities like networking, location, and system service access inside the iOS VM
  • scripts/vphoned/signcert.p12 — Binary certificate for ldid signing; enables the ad-hoc signature without Apple Developer membership
  • scripts/build.sh — Top-level orchestrator that sequences host build, guest cross-compilation, signing, and final app bundle re-signing

Why Cross-Compilation is Required

The vphoned daemon executes inside the iOS virtual machine, not on the host macOS system. This architectural requirement drives several technical decisions:

  • SDK Selection — xcrun -sdk iphoneos ensures the binary links against iOS system libraries (Foundation, Security, CoreServices) rather than their macOS equivalents
  • Architecture Match — The -arch arm64 flag produces code compatible with the virtualized iOS environment on Apple Silicon
  • Mandatory Signing — iOS kernel refuses to execute unsigned binaries; ldid-procursus satisfies this without requiring Apple's codesign tool or developer certificates
  • Bundler Re-signing — After copying vphoned.signed into Resources, the build script re-signs the entire .app bundle to seal the modified Resources tree (see scripts/build.sh lines 9-13)

Summary

  • Use make -C scripts/vphoned to cross-compile the Objective-C daemon with xcrun -sdk iphoneos
  • Apply entitlements with ldid-procursus using the provided signcert.p12 and entitlements.plist
  • Bundle the signed binary into .build/vphone-cli.app/Contents/Resources/
  • Run ./scripts/build.sh for the complete automated pipeline including host and guest builds

Frequently Asked Questions

What is vphoned and why does it need cross-compilation?

vphoned is the Objective-C guest daemon that runs inside the iOS virtual machine managed by vphone-cli. It requires cross-compilation because it executes in the iOS environment, not on the host macOS system. The binary must target the iphoneos SDK with arm64 architecture to be compatible with the virtualized iOS guest.

Can I use Apple's codesign instead of ldid-procursus?

The repository specifically implements ldid-procursus for ad-hoc signing. While codesign could theoretically work, the build script and entitlements are designed around ldid's behavior. Using ldid-procursus as specified ensures compatibility with the self-signed signcert.p12 certificate and the project's entitlements format without requiring an Apple Developer account.

How do I verify the daemon was built correctly?

Run file .build/vphoned.signed after building. The output should report Mach-O 64-bit executable arm64. Additionally, check that the binary has a valid code signature with codesign -dvv .build/vphoned.signed or ldid -e .build/vphoned.signed to verify entitlements were applied.

What happens if I skip the signing step?

iOS will refuse to execute an unsigned vphoned binary. The host vphone-cli would fail to establish communication with the guest daemon via vsock, causing the virtual machine to lack essential services like location simulation, file operations, or archive extraction that the daemon provides.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →