From 03adf0e7c1ad6c5bd14fd69fa304433e184c03be Mon Sep 17 00:00:00 2001 From: Thomas Gazagnaire Date: Mon, 6 Jul 2026 15:27:49 -0700 Subject: [PATCH] Support cross-building aarch64 unikernels on macOS Add a macOS/arm64 host case: configure.sh accepts a clang / ld.lld / llvm-objcopy ELF cross toolchain and resolves the keg-only Homebrew llvm-objcopy to an absolute path. solo5-cross-aarch64 cross-compiles with those tools, declares the llvm + lld depexts, and a CI job builds it on macos-latest. The toolchain is pure LLVM: no libgcc is bundled. The downstream link needs none: ocaml-solo5's nolibc no longer pulls long double soft-float and its wrapper does not pass -lgcc (mirage/ocaml-solo5#171). --- .github/workflows/macos.yml | 19 +++++++++++++++++++ GNUmakefile | 5 +++++ configure.sh | 25 +++++++++++++++++++++++++ opam/solo5-cross-aarch64.opam | 15 ++++++++++----- 4 files changed, 59 insertions(+), 5 deletions(-) create mode 100644 .github/workflows/macos.yml diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml new file mode 100644 index 000000000..72df576ec --- /dev/null +++ b/.github/workflows/macos.yml @@ -0,0 +1,19 @@ +name: macOS +on: [push, pull_request] +jobs: + Build: + name: ARM64 + runs-on: macos-15 + steps: + - name: Checkout code + uses: actions/checkout@v4 + - name: Install dependencies + run: brew install llvm lld + - name: Build + run: | + export PATH="$(brew --prefix llvm)/bin:$PATH" + env TARGET_CC="clang -target aarch64-linux-gnu" \ + TARGET_LD=ld.lld TARGET_OBJCOPY=llvm-objcopy \ + ./configure.sh --prefix="$PWD/_install" + make V=1 + make V=1 install diff --git a/GNUmakefile b/GNUmakefile index 70eca1094..4fc4e66be 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -32,6 +32,9 @@ tests: bindings elftool .PHONY: build ifdef CONFIG_DISABLE_TOOLCHAIN build: elftool tenders +else ifeq ($(CONFIG_HOST),Darwin) +# macOS builds only the cross toolchain + bindings (tenders/elftool are Linux/BSD-only). +build: toolchain bindings else build: $(SUBDIRS) endif @@ -184,6 +187,8 @@ endif install: MAKECMDGOALS := ifdef CONFIG_DISABLE_TOOLCHAIN install: install-tools install-tenders +else ifeq ($(CONFIG_HOST),Darwin) +install: install-headers install-toolchain else install: install-tools install-tenders install-headers install-toolchain endif diff --git a/configure.sh b/configure.sh index 2ad57263a..5de26e8eb 100755 --- a/configure.sh +++ b/configure.sh @@ -313,6 +313,13 @@ case ${HOST_CC_MACHINE} in echo "#undef HAVE_VMM_H" >tenders/hvt/hvt_openbsd_config.h fi ;; + arm64-*darwin*|aarch64-*darwin*) + CONFIG_HOST_ARCH=aarch64 CONFIG_HOST=Darwin + [ -z "${TARGET_CC}" ] && \ + die "macOS host requires an ELF cross toolchain: set TARGET_CC" \ + "(e.g. TARGET_CC='clang -target aarch64-linux-gnu')," \ + "TARGET_LD and TARGET_OBJCOPY" + ;; *) die "Unsupported host toolchain: ${HOST_CC_MACHINE}" ;; @@ -526,6 +533,24 @@ case ${CONFIG_HOST} in fi fi ;; + Darwin) + # llvm-objcopy is keg-only / off PATH on macOS; pin its absolute path. + TARGET_LD="${TARGET_LD:-ld.lld}" + TARGET_OBJCOPY="${TARGET_OBJCOPY:-llvm-objcopy}" + if ! command -v "${TARGET_OBJCOPY}" >/dev/null 2>&1; then + for _llvmbin in \ + "$(command -v brew >/dev/null 2>&1 && brew --prefix llvm 2>/dev/null)/bin" \ + /opt/homebrew/opt/llvm/bin /usr/local/opt/llvm/bin \ + /opt/local/libexec/llvm-*/bin; do + if [ -x "${_llvmbin}/${TARGET_OBJCOPY}" ]; then + TARGET_OBJCOPY="${_llvmbin}/${TARGET_OBJCOPY}" + break + fi + done + unset _llvmbin + fi + TARGET_CC_LDFLAGS="-Wl,--build-id=none,-no-pie" + ;; *) die "Unsupported host system: ${CONFIG_HOST}" ;; diff --git a/opam/solo5-cross-aarch64.opam b/opam/solo5-cross-aarch64.opam index 515035d67..ae55c0ada 100644 --- a/opam/solo5-cross-aarch64.opam +++ b/opam/solo5-cross-aarch64.opam @@ -10,14 +10,18 @@ bug-reports: "https://github.com/solo5/solo5/issues" license: "ISC" dev-repo: "git+https://github.com/solo5/solo5.git" build: [ - ["env" "TARGET_CC=aarch64-linux-gnu-gcc" "TARGET_LD=aarch64-linux-gnu-ld" "TARGET_OBJCOPY=aarch64-linux-gnu-objcopy" "./configure.sh" "--prefix=%{prefix}%"] + ["env" "TARGET_CC=aarch64-linux-gnu-gcc" "TARGET_LD=aarch64-linux-gnu-ld" "TARGET_OBJCOPY=aarch64-linux-gnu-objcopy" "./configure.sh" "--prefix=%{prefix}%"] {os != "macos"} + ["env" "TARGET_CC=clang -target aarch64-linux-gnu" "TARGET_LD=ld.lld" "TARGET_OBJCOPY=llvm-objcopy" "./configure.sh" "--prefix=%{prefix}%"] {os = "macos"} [make "V=1"] ] -install: [make "V=1" "install-toolchain"] +install: [ + [make "V=1" "install-toolchain"] + [make "V=1" "install-headers"] {os = "macos"} +] depends: [ "conf-pkg-config" {build & os = "linux"} "conf-libseccomp" {build & os = "linux"} - "solo5" {= version} + "solo5" {= version & os != "macos"} ] depexts: [ ["linux-headers"] {os-distribution = "alpine"} @@ -25,10 +29,11 @@ depexts: [ ["kernel-headers"] {os-distribution = "rhel"} ["linux-libc-dev"] {os-family = "debian"} ["gcc-aarch64-linux-gnu"] {os-family = "debian"} + ["llvm" "lld"] {os = "macos" & os-distribution = "homebrew"} ] available: [ - (arch != "arm64") & - (os = "linux" & os-family = "debian") + ((arch != "arm64") & (os = "linux" & os-family = "debian")) + | (os = "macos" & arch = "arm64") ] synopsis: "Solo5 sandboxed execution environment" description: """