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 iphoneosto function - Xcode with iOS SDK — Provides the
clangcross-compiler targetingiphoneos - 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/forldidsigning
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 thexcrun clanginvocation with iOS-specific flags and framework linkingscripts/vphoned/*.m— Objective-C implementation files includingvphoned.mandvphoned_location.mthat compile to the daemonscripts/vphoned/entitlements.plist— Extensive entitlements granting the daemon capabilities like networking, location, and system service access inside the iOS VMscripts/vphoned/signcert.p12— Binary certificate forldidsigning; enables the ad-hoc signature without Apple Developer membershipscripts/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 iphoneosensures the binary links against iOS system libraries (Foundation,Security,CoreServices) rather than their macOS equivalents - Architecture Match — The
-arch arm64flag produces code compatible with the virtualized iOS environment on Apple Silicon - Mandatory Signing — iOS kernel refuses to execute unsigned binaries;
ldid-procursussatisfies this without requiring Apple's codesign tool or developer certificates - Bundler Re-signing — After copying
vphoned.signedinto Resources, the build script re-signs the entire.appbundle to seal the modified Resources tree (seescripts/build.shlines 9-13)
Summary
- Use
make -C scripts/vphonedto cross-compile the Objective-C daemon withxcrun -sdk iphoneos - Apply entitlements with
ldid-procursususing the providedsigncert.p12andentitlements.plist - Bundle the signed binary into
.build/vphone-cli.app/Contents/Resources/ - Run
./scripts/build.shfor 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →