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

> Easily cross-compile the Objective-C guest daemon for vphone-cli. Learn to use xcrun and ldid-procursus for signing and bundling into your macOS app.

- Repository: [Lakr/vphone-cli](https://github.com/Lakr233/vphone-cli)
- Tags: how-to-guide
- Published: 2026-09-06

---

**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:

```bash
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`](https://github.com/Lakr233/vphone-cli/blob/main/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:

```make
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`](https://github.com/Lakr233/vphone-cli/blob/main/scripts/build.sh)):

```bash
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:

```bash
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:

```bash
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)

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

```

### Manual Cross-Compilation (Exact Commands)

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

```bash

# 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`](https://github.com/Lakr233/vphone-cli/blob/main/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`](https://github.com/Lakr233/vphone-cli/blob/main/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`](https://github.com/Lakr233/vphone-cli/blob/main/./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.