Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
201 changes: 201 additions & 0 deletions doc/technical/DarwinArmRunnableBuild.wiki
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
Please feel free to send documentation patches to the vbox-dev [https://www.virtualbox.org/wiki/Mailing_lists mailing list].

= Local macOS/Arm runnable build notes =

These notes describe a local workflow for building and running a test
!VirtualBox.app on macOS/Arm while iterating on host-side code. This is not a
distribution packaging recipe. The official packaging and release signing path
remains in `src/VBox/Installer/darwin/Makefile.kmk`.

The workflow below is intended for non-hardened OSE developer builds using
Xcode command line tools, Homebrew packages and ad-hoc or local code signing.

== Configure ==

Configure once and source the generated environment file before running `kmk`.

{{{
./configure.py \
--disable-hardening \
--with-kbuild-path="$PWD/kBuild/kBuild" \
--with-macossdk-path="$(xcrun --sdk macosx --show-sdk-path)" \
--with-xcode-path=/Library/Developer/CommandLineTools \
--with-qt-path=/opt/homebrew

source ./env.sh
}}}

The generated `env.sh` must be valid to source even when the host environment
contains paths with spaces.

== Local tool overrides ==

The source tree normally expects Oracle-style bundled tool trees under
`tools/darwin.arm64`, including a `clang/v*` tree and SDK directory names such
as `MacOSX11.0.sdk` and `MacOSX14.5.sdk`. A local Xcode-only machine can still
build selected host-side targets by making those paths explicit.

{{{
SDK="$(xcrun --sdk macosx --show-sdk-path)"
XCBIN="$(dirname "$(xcrun --find clang)")"
QTBIN=/opt/homebrew/share/qt/libexec

KMK_DARWIN_ARM_LOCAL="
PATH_SDK_MACOSX110=$SDK
PATH_SDK_MACOSX145=$SDK
PATH_TOOL_VBoxXClangMacho=$XCBIN
TOOL_VBoxXClangMacho_AR=$XCBIN/ar
TOOL_VBoxXClangMacho_STRIP=$XCBIN/strip
PATH_TOOL_QT6_BIN=$QTBIN
"
}}}

Some Homebrew Qt installations expose `moc`, `rcc` and `uic` through
`/opt/homebrew/share/qt/libexec` rather than `/opt/homebrew/bin`. If `kmk`
fails while generating Qt resources with `/opt/homebrew/bin/rcc: Command not
found`, keep `PATH_TOOL_QT6_BIN` pointed at the libexec directory above.

== Build a target ==

For example, to build the host-side DXMT library with a specific LLVM
installation:

{{{
source ./env.sh

kmk VBoxDxMt \
VBOX_LLVM_PATH=/opt/homebrew/opt/llvm@15 \
VBOX_LLVM_CONFIG=/opt/homebrew/opt/llvm@15/bin/llvm-config \
$KMK_DARWIN_ARM_LOCAL
}}}

The resulting library is installed into:

{{{
out/darwin.arm64/release/dist/VirtualBox.app/Contents/MacOS/VBoxDxMt.dylib
}}}

== Stage a runnable app ==

For a fresh local run, copy the staged app bundle out of the build tree:

{{{
RUNROOT=/tmp/vbox-local-run
APP="$RUNROOT/VirtualBox.app"

rm -rf "$RUNROOT"
mkdir -p "$RUNROOT"
ditto out/darwin.arm64/release/dist/VirtualBox.app "$APP"
rm -rf "$APP/Contents/MacOS/testcase"
}}}

The testcase payload is not needed for a copied GUI VM run. Removing it keeps
the local app-signing step focused on runnable product binaries and avoids
recursive signing failures on non-code testcase directories.

For a fast single-library refresh into an already copied app, replace only the
rebuilt image:

{{{
install -m 755 \
out/darwin.arm64/release/dist/VirtualBox.app/Contents/MacOS/VBoxDxMt.dylib \
"$APP/Contents/MacOS/VBoxDxMt.dylib"
}}}

Do not replace binaries in a running VM process. Shut the VM down first.

== Signing and entitlements ==

macOS VM execution needs the VM process entitlements generated by the build
system:

{{{
out/darwin.arm64/release/Entitlements.plist
out/darwin.arm64/release/EntitlementsVM.plist
}}}

For normal ad-hoc kBuild signing, use `VBOX_SIGNING_MODE=adhoc`. The build
system signs VM process binaries with `EntitlementsVM.plist` and the Darwin
installer makefile signs bundles from the inside out. The installer makefile
also documents the important hardened-runtime detail: `--deep` can lose
entitlements, so VM and app bundles are signed again without `--deep`.

For a local copied test app that loads locally rebuilt dylibs under hardened
runtime, the VM process may also need library validation disabled. Use a local
VM entitlement file like this one:

{{{
cat > /tmp/vbox-local-vm.entitlements.plist <<'EOF'
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
"https://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.cs.allow-jit</key><true/>
<key>com.apple.security.cs.allow-unsigned-executable-memory</key><true/>
<key>com.apple.security.cs.disable-executable-page-protection</key><true/>
<key>com.apple.security.cs.disable-library-validation</key><true/>
<key>com.apple.security.device.audio-input</key><true/>
<key>com.apple.security.device.camera</key><true/>
<key>com.apple.security.device.usb</key><true/>
<key>com.apple.security.hypervisor</key><true/>
</dict>
</plist>
EOF
}}}

Do not add `com.apple.vm.device-access` or `com.apple.vm.networking` to ad-hoc
or ordinary local test entitlements. They are release-signing entitlements
reserved for specifically provisioned vendors and can make local builds fail
with code signing errors.

Sign changed Mach-O images first, then nested VM bundles, then the outer app
bundle. Prefer a stable local code-signing identity for repeated local runs.
Identity-aware network filters such as Little Snitch track ad-hoc-signed
programs by code hash, so every rebuilt `VirtualBoxVM` can otherwise look like
a modified program and trigger a network identity warning. If no local signing
identity is available, the snippet falls back to ad-hoc signing.

{{{
IDENTITY="${IDENTITY:-$(security find-identity -v -p codesigning | \
sed -n 's/.*"\([^"]*\)".*/\1/p' | head -n 1)}"
: "${IDENTITY:=-}"
VM_ENT=/tmp/vbox-local-vm.entitlements.plist
APP_ENT=out/darwin.arm64/release/Entitlements.plist

codesign --force --options runtime --timestamp=none \
--sign "$IDENTITY" \
"$APP/Contents/MacOS/VBoxDxMt.dylib"

codesign --force --options runtime --timestamp=none \
--sign "$IDENTITY" \
--entitlements "$VM_ENT" \
"$APP/Contents/Resources/VirtualBoxVM.app/Contents/MacOS/VirtualBoxVM"

codesign --force --options runtime --timestamp=none \
--sign "$IDENTITY" \
--entitlements "$VM_ENT" \
"$APP/Contents/Resources/VirtualBoxVM.app"

codesign --force --options runtime --timestamp=none \
--sign "$IDENTITY" \
--entitlements "$APP_ENT" \
"$APP"
}}}

== Verify and run ==

Inspect the final signatures and selected dynamic library links:

{{{
codesign -d -vv --entitlements :- \
"$APP/Contents/Resources/VirtualBoxVM.app/Contents/MacOS/VirtualBoxVM"

otool -L "$APP/Contents/MacOS/VBoxDxMt.dylib"
}}}

Then start a test VM from the copied app bundle:

{{{
"$APP/Contents/MacOS/VBoxManage" startvm "<vm-name>" --type gui
}}}