From ffa8b498a3b0861952f6b2eb8275d0a46a350434 Mon Sep 17 00:00:00 2001 From: Fimeg Date: Sun, 12 Jul 2026 18:00:31 -0400 Subject: [PATCH] build: make aarch64 linker sysroot-safe --- .cargo/aarch64-pkg-config | 5 +++-- .gitea/workflows/ci.yml | 11 ++------- scripts/aarch64-linker | 17 ++++++++++++++ scripts/build-cross.sh | 47 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 69 insertions(+), 11 deletions(-) create mode 100755 scripts/aarch64-linker create mode 100755 scripts/build-cross.sh diff --git a/.cargo/aarch64-pkg-config b/.cargo/aarch64-pkg-config index 7068b07..66cfe3a 100755 --- a/.cargo/aarch64-pkg-config +++ b/.cargo/aarch64-pkg-config @@ -4,7 +4,8 @@ # so alsa-sys's built-in cross guard trips. This wrapper sets the vars itself # and is referenced via PKG_CONFIG in .cargo/config.toml — every pkg-config # probe in the tree (alsa-sys today, any future -sys crate) goes through it. +SYSROOT="${SOUVERAINE_AARCH64_SYSROOT:-/usr/aarch64-linux-gnu}" export PKG_CONFIG_ALLOW_CROSS=1 -export PKG_CONFIG_LIBDIR=/usr/aarch64-linux-gnu/lib/pkgconfig -export PKG_CONFIG_SYSROOT_DIR=/usr/aarch64-linux-gnu +export PKG_CONFIG_LIBDIR="$SYSROOT/usr/lib/pkgconfig" +export PKG_CONFIG_SYSROOT_DIR="$SYSROOT" exec /usr/bin/pkg-config "$@" diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index fa2631a..f8590f9 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -100,17 +100,10 @@ jobs: run: | set -euo pipefail cd "$GITHUB_WORKSPACE/src/Souveraine" - export PKG_CONFIG_ALLOW_CROSS=1 - export PKG_CONFIG_LIBDIR="$HOME/aarch64-sysroot/usr/lib/pkgconfig" - export PKG_CONFIG_SYSROOT_DIR="$HOME/aarch64-sysroot" - export CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc - # Without --sysroot the cross gcc's default sysroot (/usr/aarch64-linux-gnu, - # sparse) has no libmvec, so the linker falls back to the host x86_64 one - # and fails. Point it at the full chroot sysroot where aarch64 libmvec lives. - export CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_RUSTFLAGS="-C link-arg=--sysroot=$HOME/aarch64-sysroot" # Persistent target dir: fresh clones per run, warm compile cache. export CARGO_TARGET_DIR="$HOME/.cache/souveraine-ci-target" - cargo build --release --target aarch64-unknown-linux-gnu + export SOUVERAINE_AARCH64_SYSROOT="$HOME/aarch64-sysroot" + ./scripts/build-cross.sh BIN="$CARGO_TARGET_DIR/aarch64-unknown-linux-gnu/release/souveraine" file "$BIN" | grep -q aarch64 || { echo "built binary is not aarch64" >&2; exit 1; } cp "$BIN" souveraine-aarch64 diff --git a/scripts/aarch64-linker b/scripts/aarch64-linker new file mode 100755 index 0000000..f2c8080 --- /dev/null +++ b/scripts/aarch64-linker @@ -0,0 +1,17 @@ +#!/usr/bin/env bash +# aarch64-linker — Cargo linker wrapper for the SouveraineOS ARM sysroot. +# +# GCC's default aarch64 linker scripts contain absolute library paths. Cargo +# supplies -lm before its late rustflags, so --sysroot alone is insufficient: +# ld can select the host's x86 library. build-cross.sh supplies an overlay +# containing sysroot-relative copies of those scripts; put it first. +set -euo pipefail + +SYSROOT="${SOUVERAINE_AARCH64_SYSROOT:-/usr/aarch64-linux-gnu}" +SCRIPTS="${SOUVERAINE_AARCH64_LINKER_SCRIPTS:?run scripts/build-cross.sh, not this wrapper directly}" + +exec aarch64-linux-gnu-gcc \ + --sysroot="$SYSROOT" \ + -L"$SCRIPTS" \ + -L"$SYSROOT/usr/lib" \ + "$@" diff --git a/scripts/build-cross.sh b/scripts/build-cross.sh new file mode 100755 index 0000000..f282a76 --- /dev/null +++ b/scripts/build-cross.sh @@ -0,0 +1,47 @@ +#!/usr/bin/env bash +# build-cross.sh — cross-compile souveraine for aarch64 (Pixel 3 / Arch ARM). +# +# The sysroot is /usr/aarch64-linux-gnu on the laptop and +# ~/aarch64-sysroot on ArchDev. Set SOUVERAINE_AARCH64_SYSROOT for the latter. +# PKG_CONFIG_* is exported here, not in .cargo/config.toml — cargo's [env] +# block doesn't reliably reach build-script probes. The aarch64 linker scripts +# need a small unprivileged overlay so absolute /usr/lib paths stay inside the +# target sysroot rather than resolving to the x86 host. +set -euo pipefail + +cd "$(dirname "$0")/.." + +SYSROOT="${SOUVERAINE_AARCH64_SYSROOT:-/usr/aarch64-linux-gnu}" +TARGET_DIR="${CARGO_TARGET_DIR:-$PWD/target}" +LINKER_SCRIPTS="$TARGET_DIR/aarch64-linker-scripts" + +[[ -d "$SYSROOT/usr/lib" ]] || { + echo "aarch64 sysroot is missing: $SYSROOT" >&2 + exit 1 +} + +mkdir -p "$LINKER_SCRIPTS" +while IFS= read -r script; do + name="${script##*/}" + tmp="$LINKER_SCRIPTS/.${name}.$$" + # '=' tells GNU ld to resolve this path below --sysroot. Only linker + # scripts (plain-text *.so files) are copied; actual shared objects stay + # in the sysroot. + sed -E 's#([ (])/(usr/)?lib/#\1=/\2lib/#g' "$script" > "$tmp" + mv "$tmp" "$LINKER_SCRIPTS/$name" +done < <(find "$SYSROOT/usr/lib" -maxdepth 1 -type f -name '*.so' \ + -exec grep -lE '(^|[ (])/(usr/)?lib/' {} +) + +export PKG_CONFIG_ALLOW_CROSS=1 +export PKG_CONFIG_LIBDIR="$SYSROOT/usr/lib/pkgconfig" +export PKG_CONFIG_SYSROOT_DIR="$SYSROOT" +export PKG_CONFIG="$PWD/.cargo/aarch64-pkg-config" +export SOUVERAINE_AARCH64_SYSROOT="$SYSROOT" +export SOUVERAINE_AARCH64_LINKER_SCRIPTS="$LINKER_SCRIPTS" +export CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER="$PWD/scripts/aarch64-linker" + +cargo build --release --target aarch64-unknown-linux-gnu "$@" + +BIN="$TARGET_DIR/aarch64-unknown-linux-gnu/release/souveraine" +echo "== built: $BIN ==" +file "$BIN"