From 1fceb6209801fc3cecc16583e2c8982e89a16b04 Mon Sep 17 00:00:00 2001 From: Ihor Solodrai Date: Tue, 11 Aug 2026 12:22:13 -0700 Subject: [PATCH] setup-build-env: Fix cross-compilation dependencies Cross-compilation is broken for every Debian target. glibc 2.43-3 entered unstable on 2026-08-10, and libc6-dev: from 2.43 onwards declares Breaks: libc6-dev--cross (<< 2.43~) while src:cross-toolchain-base is still at version 80 and ships libc6-dev--cross 2.42-12cross1. g++---linux-gnu depends on it through libstdc++--dev--cross, so installing the cross toolchain and the multiarch : -dev libraries in a single apt transaction is now unsatisfiable. Waiting for the archive is not an option: cross-toolchain-base cannot be rebuilt at all until its Build-Depends stop naming linux-source-6.18, which sid left behind long ago (Debian #1130544, open since 2026-03-12; the glibc skew itself is #1144098). The root problem is that the target libraries are installed into the host dpkg database. That subjects them to Multi-Arch: same version equality against the host-arch copies, and to the Breaks above, neither of which has anything to do with cross-compiling. Version skew between the builder image and the archive is already fatal for a second, independent reason: the image carries libssl-dev 3.6.2-1 while the archive offers 3.6.3-1. Download the target libraries and unpack them into the cross toolchain's own triplet directory instead. apt-get download resolves no dependencies and writes no dpkg state, so neither constraint can apply, and /usr/-linux-gnu is already on the cross gcc's default search path, so no build flags change. Drop linux-libc-dev:: it is Architecture: all, so the qualifier was a no-op, and the target UAPI headers come from linux-libc-dev--cross. Verified on Debian sid with gcc-15, Ubuntu noble with gcc-14 and Ubuntu plucky with gcc-15, building libbpf, a static cross bpftool, sign-file, and clang's s390x userspace targets. Signed-off-by: Ihor Solodrai --- .../install_cross_compilation_toolchain.sh | 91 ++++++++++++++++++- 1 file changed, 86 insertions(+), 5 deletions(-) diff --git a/setup-build-env/install_cross_compilation_toolchain.sh b/setup-build-env/install_cross_compilation_toolchain.sh index 79ba982..c88dda3 100755 --- a/setup-build-env/install_cross_compilation_toolchain.sh +++ b/setup-build-env/install_cross_compilation_toolchain.sh @@ -46,14 +46,95 @@ fi sudo dpkg --add-architecture "$DEB_ARCH" sudo apt-get update -y + sudo apt-get install -y --no-install-recommends \ binfmt-support qemu-user-static \ "gcc-${GCC_VERSION}-${TARGET_ARCH}-linux-gnu" \ - "g++-${GCC_VERSION}-${TARGET_ARCH}-linux-gnu" \ - "linux-libc-dev:${DEB_ARCH}" \ - "libelf-dev:${DEB_ARCH}" \ - "libssl-dev:${DEB_ARCH}" \ - "zlib1g-dev:${DEB_ARCH}" + "g++-${GCC_VERSION}-${TARGET_ARCH}-linux-gnu" + +# Target libraries are downloaded and unpacked rather than installed as +# :${DEB_ARCH} multiarch packages, so that they never enter the host dpkg +# database. Being in it would subject them to Multi-Arch: same version equality +# against the host-arch copies, and to +# libc6-dev:${DEB_ARCH} Breaks libc6-dev-${DEB_ARCH}-cross (<< ~) +# which makes the multiarch and cross package sets mutually uninstallable +# whenever the archive's glibc moves ahead of src:cross-toolchain-base. +# See https://bugs.debian.org/1144098 and https://bugs.debian.org/1130544. +# +# ${SYSROOT} is already on the cross gcc's default search path, so the build +# needs no -I/-L/--sysroot. selftests/bpf shares one $(EXTRA_CFLAGS) between the +# host and target bpftool sub-makes, so a target-only flag would have nowhere to +# live anyway. +# +# Both shared objects and static archives are needed: test binaries link +# dynamically, but CI passes EXTRA_LDFLAGS=-static and the cross bpftool is +# linked statically. +SYSROOT="/usr/${TARGET_ARCH}-linux-gnu" +TARGET_LIBS=( + "libelf1t64:${DEB_ARCH}" "libelf-dev:${DEB_ARCH}" + "zlib1g:${DEB_ARCH}" "zlib1g-dev:${DEB_ARCH}" + "libzstd1:${DEB_ARCH}" "libzstd-dev:${DEB_ARCH}" + "libssl3t64:${DEB_ARCH}" "libssl-dev:${DEB_ARCH}" +) + +STAGE="$(mktemp -d)" +trap 'rm -rf "${STAGE}"' EXIT +( + cd "${STAGE}" + apt-get download "${TARGET_LIBS[@]}" + mkdir -p x + for deb in *.deb; do + dpkg-deb -x "${deb}" x + done +) + +sudo mkdir -p "${SYSROOT}/include" "${SYSROOT}/lib" + +# Arch-specific headers are copied second so they win. +if [ -d "${STAGE}/x/usr/include" ]; then + sudo cp -a "${STAGE}/x/usr/include/." "${SYSROOT}/include/" +fi +if [ -d "${STAGE}/x/usr/include/${TARGET_ARCH}-linux-gnu" ]; then + sudo cp -a "${STAGE}/x/usr/include/${TARGET_ARCH}-linux-gnu/." \ + "${SYSROOT}/include/" +fi + +for libdir in "usr/lib/${TARGET_ARCH}-linux-gnu" "lib/${TARGET_ARCH}-linux-gnu"; do + if [ -d "${STAGE}/x/${libdir}" ]; then + sudo cp -a "${STAGE}/x/${libdir}/." "${SYSROOT}/lib/" + fi +done + +# -dev packages may ship .so as absolute symlinks, which dangle once relocated. +for link in "${SYSROOT}"/lib/*; do + if [ ! -L "${link}" ]; then continue; fi + if [ -e "${link}" ]; then continue; fi + linktarget="$(basename "$(readlink "${link}")")" + if [ -e "${SYSROOT}/lib/${linktarget}" ]; then + sudo ln -sfn "${linktarget}" "${link}" + else + echo "WARNING: dangling symlink ${link} -> $(readlink "${link}")" + fi +done + +# Surface a stale TARGET_LIBS here rather than as a link error much later. +sysroot_missing=0 +for hdr in libelf.h gelf.h zlib.h openssl/evp.h; do + if [ ! -e "${SYSROOT}/include/${hdr}" ]; then + echo "ERROR: missing target header ${SYSROOT}/include/${hdr}" + sysroot_missing=1 + fi +done +for lib in libelf.so libelf.a libz.so libz.a libzstd.a libcrypto.so libcrypto.a; do + if [ ! -e "${SYSROOT}/lib/${lib}" ]; then + echo "ERROR: missing target library ${SYSROOT}/lib/${lib}" + sysroot_missing=1 + fi +done +if [ "${sysroot_missing}" -ne 0 ]; then + echo "Target sysroot assembly failed; TARGET_LIBS is probably stale." + exit 1 +fi sudo update-alternatives --install \ /usr/bin/${TARGET_ARCH}-linux-gnu-gcc \