diff --git a/patches/pkgbuild/coreutils.sh b/patches/pkgbuild/coreutils.sh new file mode 100755 index 0000000..ea1eafc --- /dev/null +++ b/patches/pkgbuild/coreutils.sh @@ -0,0 +1,44 @@ +#!/usr/bin/env bash +# coreutils: --without-selinux. The host has libselinux; Arch does not. +# +# THE TRAP: the chroot test reported "coreutils links against libselinux", +# and the obvious reading -- build libselinux next -- is wrong twice over. +# +# $ readelf -d repo/s390x/.../usr/bin/ls | grep NEEDED +# (NEEDED) Shared library: [libselinux.so.1] +# +# There is no libselinux package in Arch. The clone of it 404s, and +# gitlab.archlinux.org answers a 404 by asking for a login, which is why the +# driver log said "could not read Username" rather than "no such project". +# Arch's own coreutils declares depends=(acl attr glibc gmp libcap openssl) +# -- no selinux, because Arch's build chroot has no libselinux to find. +# +# Ours found one. Ubuntu 25.10 carries libselinux1-dev (it arrives with +# libmount-dev / libgio-2.0-dev), coreutils' configure probes for +# selinux/selinux.h unconditionally, and thirteen binaries -- ls, cp, mv, +# install, id, stat, mkdir, mknod, mkfifo, dir, vdir, chcon, runcon -- came +# out linked to a library the target rootfs will never contain. +# +# Uninstalling the header on the host is not the way out: apt takes +# libmount-dev, libglib2.0-dev, libcryptsetup-dev and libdevmapper-dev with +# it, which is what systemd and util-linux still need. +# +# So this is not disabling a feature. It is removing a HOST artefact that +# stage 1 exists to keep out, and the result matches Arch's binary exactly: +# Arch's coreutils ships neither chcon nor runcon, for this very reason. +# Losing them converges with Arch, it does not diverge from it. +# +# The same probe catches findutils, sed and tar -- each has its own hook -- +# and glibc's makedb, which deliberately does NOT. Nothing in the bootstrap +# runs makedb, and rebuilding glibc for it would relink the foundation under +# all sixty-nine other packages. Stage 2 rebuilds glibc in a chroot that has +# no libselinux, where the problem cannot recur. +# +# Only one ./configure in this PKGBUILD (verified), so the anchor is safe. +set -euo pipefail +grep -c -- '--with-openssl' PKGBUILD | grep -qx 1 || { + echo "coreutils: expected exactly one --with-openssl anchor" >&2; exit 1; } +sed -i 's|^\(\s*\)--with-openssl$|\1--with-openssl \\\n\1--without-selinux|' PKGBUILD +grep -q -- '--without-selinux' PKGBUILD || { + echo "coreutils: --without-selinux not inserted" >&2; exit 1; } +echo "coreutils: --without-selinux (Arch has no libselinux; the host does)" diff --git a/patches/pkgbuild/findutils.sh b/patches/pkgbuild/findutils.sh new file mode 100755 index 0000000..47a4b54 --- /dev/null +++ b/patches/pkgbuild/findutils.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash +# findutils: --without-selinux. Same host artefact as coreutils. +# +# THE TRAP: fixing coreutils alone looks like the job is done. It is not. +# +# $ readelf -d repo/s390x/.../usr/bin/find | grep NEEDED +# (NEEDED) Shared library: [libselinux.so.1] +# +# and stage 2 runs makepkg INSIDE the stage-1 rootfs, where makepkg calls +# find. A find that will not load is a hard stop, not a cosmetic one. +# +# Arch's findutils declares depends=(glibc) and nothing else, because Arch's +# build chroot has no selinux/selinux.h for gnulib to probe. Ubuntu 25.10 +# does, so ours linked it. patches/pkgbuild/coreutils.sh carries the full +# account, including why removing the header from the host is not available. +# +# One ./configure in this PKGBUILD (verified), so the anchor is unambiguous. +set -euo pipefail +grep -c -- '^ \./configure --prefix=/usr$' PKGBUILD | grep -qx 1 || { + echo "findutils: expected exactly one bare ./configure --prefix=/usr" >&2; exit 1; } +sed -i 's|^\(\s*\)\(\./configure --prefix=/usr\)$|\1\2 --without-selinux|' PKGBUILD +grep -c -- '--without-selinux' PKGBUILD | grep -qx 1 || { + echo "findutils: --without-selinux not inserted exactly once" >&2; exit 1; } +echo "findutils: --without-selinux (Arch has no libselinux; the host does)" diff --git a/patches/pkgbuild/sed.sh b/patches/pkgbuild/sed.sh new file mode 100755 index 0000000..450ee8e --- /dev/null +++ b/patches/pkgbuild/sed.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env bash +# sed: --without-selinux. Same host artefact as coreutils. +# +# $ readelf -d repo/s390x/.../usr/bin/sed | grep NEEDED +# (NEEDED) Shared library: [libselinux.so.1] +# +# sed matters for the same reason find does: every PKGBUILD hook in this +# repository is a sed, and stage 2 runs them inside the stage-1 rootfs. +# +# Arch's sed declares depends=(acl glibc). The full account of why the host +# supplies a header Arch's build chroot does not is in +# patches/pkgbuild/coreutils.sh. +# +# One ./configure in this PKGBUILD (verified), so the anchor is unambiguous. +set -euo pipefail +grep -c -- '^ \./configure --prefix=/usr$' PKGBUILD | grep -qx 1 || { + echo "sed: expected exactly one bare ./configure --prefix=/usr" >&2; exit 1; } +sed -i 's|^\(\s*\)\(\./configure --prefix=/usr\)$|\1\2 --without-selinux|' PKGBUILD +grep -c -- '--without-selinux' PKGBUILD | grep -qx 1 || { + echo "sed: --without-selinux not inserted exactly once" >&2; exit 1; } +echo "sed: --without-selinux (Arch has no libselinux; the host does)" diff --git a/patches/pkgbuild/tar.sh b/patches/pkgbuild/tar.sh new file mode 100755 index 0000000..c36ff71 --- /dev/null +++ b/patches/pkgbuild/tar.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash +# tar: --without-selinux. Same host artefact as coreutils. +# +# $ readelf -d repo/s390x/.../usr/bin/tar | grep NEEDED +# (NEEDED) Shared library: [libselinux.so.1] +# +# This is the one with the sharpest consequence. makepkg extracts every +# source with tar, so in stage 2 -- which runs makepkg inside the stage-1 +# rootfs -- a tar that will not load stops the entire stage before its first +# package. Not cosmetic, and not something a later fix can route around. +# +# Arch's tar declares depends=(acl glibc). The full account of why the host +# supplies a header Arch's build chroot does not is in +# patches/pkgbuild/coreutils.sh. +# +# One ./configure in this PKGBUILD (verified), and it carries its own +# --sbindir/--libexecdir, so the anchor is the whole line rather than the +# bare form the other three match. +set -euo pipefail +grep -c -- '^ \./configure --prefix=/usr --sbindir=/usr/bin --libexecdir=/usr/lib/tar$' PKGBUILD | grep -qx 1 || { + echo "tar: expected exactly one ./configure line" >&2; exit 1; } +sed -i 's|^\(\s*\)\(\./configure --prefix=/usr --sbindir=/usr/bin --libexecdir=/usr/lib/tar\)$|\1\2 --without-selinux|' PKGBUILD +grep -c -- '--without-selinux' PKGBUILD | grep -qx 1 || { + echo "tar: --without-selinux not inserted exactly once" >&2; exit 1; } +echo "tar: --without-selinux (Arch has no libselinux; the host does)" diff --git a/scripts/build-stage1.sh b/scripts/build-stage1.sh index 0aa4e61..fb8e491 100755 --- a/scripts/build-stage1.sh +++ b/scripts/build-stage1.sh @@ -48,12 +48,19 @@ STAGE1_PACKAGES=( filesystem iana-etc tzdata licenses shadow util-linux # Named by the chroot test, not guessed. Installing the repo into a # rootfs and entering it turned "does it work?" into a precise list: - # - coreutils links against libselinux, and would not start without it # - libcap needs pam; openssl needs brotli; libarchive needs libxml2 # - pacman itself asks for systemd, pacman-mirrorlist and # libmakepkg-dropins # Sixty-eight successful builds proved none of this. One chroot did. - libselinux pam brotli libxml2 systemd pacman-mirrorlist libmakepkg-dropins + # + # The chroot also named libselinux, and libselinux is NOT on this line, + # because that reading of it was wrong. Arch has no libselinux package at + # all -- the clone 404s. What the chroot saw was a HOST artefact: Ubuntu + # carries libselinux1-dev, coreutils probes for selinux/selinux.h + # unconditionally, and ours came out linked to a library the target will + # never contain. The answer is --without-selinux per package, which is + # what Arch's own build chroot gets for free by not having the header. + pam brotli libxml2 systemd pacman-mirrorlist libmakepkg-dropins # And finally the package manager itself, built as an Arch package. pacman )