From 8689e50cbed0393cf70771603a0e6a0ac8a63fb5 Mon Sep 17 00:00:00 2001 From: Aleksa Sarai Date: Tue, 3 Feb 2026 21:31:12 +0100 Subject: [PATCH] build: enable builds with libpathrs pathrs-lite supports transparently switching to libpathrs.so as the backend with the "libpathrs" build tag. In order to make this work properly with our CI and release build scripts, we we need to have a similar setup as with we do with libseccomp. Signed-off-by: Aleksa Sarai --- Dockerfile | 28 +++++-- script/build-libpathrs.sh | 151 ++++++++++++++++++++++++++++++++++++++ script/build-seccomp.sh | 2 +- script/lib.sh | 47 ++++++++---- script/release_build.sh | 5 +- script/release_sign.sh | 6 ++ 6 files changed, 213 insertions(+), 26 deletions(-) create mode 100755 script/build-libpathrs.sh diff --git a/Dockerfile b/Dockerfile index 829acc42c..0864fbab4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,7 @@ ARG GO_VERSION=1.25 ARG BATS_VERSION=v1.12.0 ARG LIBSECCOMP_VERSION=2.6.0 +ARG LIBPATHRS_VERSION=0.2.4 FROM golang:${GO_VERSION}-trixie ARG DEBIAN_FRONTEND=noninteractive @@ -9,10 +10,13 @@ ARG CRIU_REPO=https://download.opensuse.org/repositories/devel:/tools:/criu/Debi RUN KEYFILE=/usr/share/keyrings/criu-repo-keyring.gpg; \ wget -nv $CRIU_REPO/Release.key -O- | gpg --dearmor > "$KEYFILE" \ && echo "deb [signed-by=$KEYFILE] $CRIU_REPO/ /" > /etc/apt/sources.list.d/criu.list \ - && dpkg --add-architecture i386 \ + && printf "%s\n" i386 armel armhf arm64 ppc64el s390x riscv64 | xargs -t -n1 -- dpkg --add-architecture \ && apt-get update \ && apt-get install -y --no-install-recommends \ build-essential \ + cargo \ + cargo-auditable \ + clang \ criu \ gcc \ gcc-multilib \ @@ -22,6 +26,7 @@ RUN KEYFILE=/usr/share/keyrings/criu-repo-keyring.gpg; \ iptables \ jq \ kmod \ + lld \ pkg-config \ python3-minimal \ sshfs \ @@ -29,13 +34,13 @@ RUN KEYFILE=/usr/share/keyrings/criu-repo-keyring.gpg; \ uidmap \ iproute2 \ && apt-get install -y --no-install-recommends \ - libc-dev:i386 libgcc-s1:i386 \ - gcc-aarch64-linux-gnu libc-dev-arm64-cross \ - gcc-arm-linux-gnueabi libc-dev-armel-cross \ - gcc-arm-linux-gnueabihf libc-dev-armhf-cross \ - gcc-powerpc64le-linux-gnu libc-dev-ppc64el-cross \ - gcc-s390x-linux-gnu libc-dev-s390x-cross \ - gcc-riscv64-linux-gnu libc-dev-riscv64-cross \ + libc-dev:i386 libgcc-s1:i386 gcc-i686-linux-gnu libstd-rust-dev:i386 \ + gcc-aarch64-linux-gnu libc-dev-arm64-cross libstd-rust-dev:arm64 \ + gcc-arm-linux-gnueabi libc-dev-armel-cross libstd-rust-dev:armel \ + gcc-arm-linux-gnueabihf libc-dev-armhf-cross libstd-rust-dev:armhf \ + gcc-powerpc64le-linux-gnu libc-dev-ppc64el-cross libstd-rust-dev:ppc64el \ + gcc-s390x-linux-gnu libc-dev-s390x-cross libstd-rust-dev:s390x \ + gcc-riscv64-linux-gnu libc-dev-riscv64-cross libstd-rust-dev:riscv64 \ && apt-get clean \ && rm -rf /var/cache/apt /var/lib/apt/lists/* /etc/apt/sources.list.d/*.list @@ -64,6 +69,13 @@ RUN mkdir -p $DYLIB_DIR \ && /tmp/script/build-seccomp.sh "$LIBSECCOMP_VERSION" $DYLIB_DIR $RELEASE_ARCHES ENV LIBSECCOMP_VERSION=$LIBSECCOMP_VERSION +# install libpathrs +ARG LIBPATHRS_VERSION +COPY script/build-libpathrs.sh /tmp/script/ +RUN mkdir -p $DYLIB_DIR \ + && /tmp/script/build-libpathrs.sh "$LIBPATHRS_VERSION" $DYLIB_DIR $RELEASE_ARCHES +ENV LIBPATHRS_VERSION=$LIBPATHRS_VERSION + ENV LD_LIBRARY_PATH=$DYLIB_DIR/lib ENV PKG_CONFIG_PATH=$DYLIB_DIR/lib/pkgconfig diff --git a/script/build-libpathrs.sh b/script/build-libpathrs.sh new file mode 100755 index 000000000..28bdc67f4 --- /dev/null +++ b/script/build-libpathrs.sh @@ -0,0 +1,151 @@ +#!/bin/bash +# Copyright (C) 2026 Open Containers Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set -xEeuo pipefail + +# shellcheck source=./script/lib.sh +source "$(dirname "${BASH_SOURCE[0]}")/lib.sh" + +PLATFORM="$(get_platform)" + +declare -A GOARCH_TO_RUST_TARGET=( + ["386"]=i686-unknown-linux-gnu + ["amd64"]=x86_64-unknown-linux-gnu + ["arm64"]=aarch64-unknown-linux-gnu + ["armel"]=armv5te-unknown-linux-gnueabi + ["armhf"]=armv7-unknown-linux-gnueabihf + ["ppc64le"]=powerpc64le-unknown-linux-gnu + ["s390x"]=s390x-unknown-linux-gnu + ["riscv64"]=riscv64gc-unknown-linux-gnu +) + +declare -A RUST_TARGET_TO_CC=( + ["i686-unknown-linux-gnu"]="i686-${PLATFORM}-gcc" + ["x86_64-unknown-linux-gnu"]="x86_64-${PLATFORM}-gcc" + ["aarch64-unknown-linux-gnu"]="aarch64-${PLATFORM}-gcc" + ["armv5te-unknown-linux-gnueabi"]="arm-${PLATFORM}eabi-gcc" + ["armv7-unknown-linux-gnueabihf"]="arm-${PLATFORM}eabihf-gcc" + ["powerpc64le-unknown-linux-gnu"]="powerpc64le-${PLATFORM}-gcc" + ["s390x-unknown-linux-gnu"]="s390x-${PLATFORM}-gcc" + ["riscv64gc-unknown-linux-gnu"]="riscv64-${PLATFORM}-gcc" +) + +# sha256 checksums for libpathrs release tarballs. +declare -A LIBPATHRS_SHA256=( + ["0.2.4"]=45aca68e698b844fae0cf7c459bc441519b0e7b48397caa7d3936cfc68d73f77 +) + +function generate_cargo_config() { + for rust_target in "${GOARCH_TO_RUST_TARGET[@]}"; do + local target_gcc="${RUST_TARGET_TO_CC[$rust_target]}" + + # Based on . + cat <<-EOF + [target.$rust_target] + linker = "$target_gcc" + rustflags = ["-L", "$(rustc --print sysroot)/lib/rustlib/$rust_target/lib"] + EOF + done +} + +# Due to libpathrs being MPLv2/LGPLv3 we must include its sources, so +# download, install and build against it. +# Parameters: +# $1 -- libpathrs version to download and build. +# $2 -- destination directory. +# $@ -- additional architectures to cross-compile for (optional) +function build_libpathrs() { + local ver="$1" + shift + local dest="$1" + shift + local go_arches=("$@") + local tar="libpathrs-${ver}.tar.xz" + + # Download, check, and extract. + wget "https://github.com/cyphar/libpathrs/releases/download/v${ver}/${tar}"{,.asc} + sha256sum --strict --check - <<<"${LIBPATHRS_SHA256[${ver}]} *${tar}" + + local srcdir + srcdir="$(mktemp -d)" + tar xf "$tar" -C "$srcdir" + pushd "$srcdir/libpathrs-$ver" || return + + # Use cargo-auditable if available. + if cargo auditable --version &>/dev/null; then + export CARGO="cargo auditable" + fi + local extra_cargo_flags=("--locked") + + # If we are being asked to install this in a system library directory + # (i.e., --prefix=/usr or something similar), the correct place to put + # libpathrs.so depends very strongly on the distro we are running on, and + # detecting this in a generic way is quite difficult. + # + # The simplest solution is to use a disto-packaged binary to detect where + # libc.so is installed and use the same import path, so we look at + # /proc/self/maps and parse out the parent directory of the libc.so being + # used. + local native_libdir libdir= + native_libdir="$(awk '$NF ~ /\/libc\>.*\.so/ { print $NF; }' /proc/self/maps | + sort -u | head -n1 | xargs dirname)" + if [[ "$native_libdir" == "$dest/"* ]]; then + libdir="$native_libdir" + fi + + # Install native version for Dockerfile builds. + make \ + EXTRA_CARGO_FLAGS="${extra_cargo_flags[*]}" \ + release + ./install.sh \ + --prefix="$dest" \ + --libdir="$libdir" + cargo clean + + local cargo_config + cargo_config="$(mktemp --tmpdir runc-libpathrs-cargo.toml.XXXXXX)" + # shellcheck disable=SC2064 # We want to resolve the path here. + trap "rm -f '$cargo_config'" EXIT + + # Only use the cross-compile config when we actually need to cross-compile. + # RedHat-based distros insist on calling their targets "$ARCH-redhat-linux" + # which breaks our above logic, but we don't ever need to cross-compile on + # RedHat distros so we can ignore this. + generate_cargo_config >"$cargo_config" + extra_cargo_flags+=("--config=$cargo_config") + + for go_arch in "${go_arches[@]}"; do + local rust_target="${GOARCH_TO_RUST_TARGET[$go_arch]}" + make \ + EXTRA_CARGO_FLAGS="${extra_cargo_flags[*]} --target=$rust_target" \ + release + ./install.sh \ + --rust-target="$rust_target" \ + --prefix="$dest/$go_arch" + cargo clean + done + + # Place the source tarball to $dest/src. + popd || return + mkdir -p "$dest"/src + mv "$tar"{,.asc} "$dest"/src +} + +if [ $# -lt 2 ]; then + echo "Usage: $0 [ ...]" >&2 + exit 1 +fi + +build_libpathrs "$@" diff --git a/script/build-seccomp.sh b/script/build-seccomp.sh index d8a3f9a0e..1c1b9671e 100755 --- a/script/build-seccomp.sh +++ b/script/build-seccomp.sh @@ -60,7 +60,7 @@ function build_libseccomp() { # Place the source tarball to $dest/src. popd || return - mkdir "$dest"/src + mkdir -p "$dest"/src mv "$tar"{,.asc} "$dest"/src } diff --git a/script/lib.sh b/script/lib.sh index 89ce8c6c3..e8683ab1b 100644 --- a/script/lib.sh +++ b/script/lib.sh @@ -1,41 +1,56 @@ #!/bin/bash +# get_platform computes the platform section of target triples on this OS. +function get_platform() { + # Fedora doesn't have ID_LIKE and only has ID=fedora, so we need to + # construct a fake ID_LIKE to treat AlmaLinux and Fedora the same way. + local ID_LIKE + # shellcheck source=/etc/os-release disable=SC1091 # outside our sources + ID_LIKE="$( + source /etc/os-release + echo "${ID:-} ${ID_LIKE:-}" + )" + + local PLATFORM + case "$ID_LIKE" in + *suse*) + PLATFORM=suse-linux + ;; + *rhel* | *fedora* | *centos*) + PLATFORM=redhat-linux + ;; + *) + PLATFORM=linux-gnu + ;; + esac + echo "$PLATFORM" +} + # set_cross_vars sets a few environment variables used for cross-compiling, # based on the architecture specified in $1. function set_cross_vars() { GOARCH="$1" # default, may be overridden below unset GOARM - PLATFORM=linux-gnu - # openSUSE has a custom PLATFORM - if grep -iq "ID_LIKE=.*suse" /etc/os-release; then - PLATFORM=suse-linux - is_suse=1 - fi + PLATFORM="$(get_platform)" + [[ "$PLATFORM" == *suse* ]] && is_suse=1 - case $1 in + case "$1" in 386) # Always use the 64-bit compiler to build the 386 binary, which works # for the more common cross-build method for x86 (namely, the # equivalent of dpkg --add-architecture). local cpu_type if [ -v is_suse ]; then - # There is no x86_64-suse-linux-gcc, so use the native one. - HOST= cpu_type=i586 else - HOST=x86_64-${PLATFORM} cpu_type=i686 fi + HOST=x86_64-${PLATFORM} CFLAGS="-m32 -march=$cpu_type ${CFLAGS[*]}" ;; amd64) - if [ -n "${is_suse:-}" ]; then - # There is no x86_64-suse-linux-gcc, so use the native one. - HOST= - else - HOST=x86_64-${PLATFORM} - fi + HOST=x86_64-${PLATFORM} ;; arm64) HOST=aarch64-${PLATFORM} diff --git a/script/release_build.sh b/script/release_build.sh index f84bd5041..e6d3e64a3 100755 --- a/script/release_build.sh +++ b/script/release_build.sh @@ -20,6 +20,7 @@ set -e # Project-specific options and functions. In *theory* you shouldn't need to # touch anything else in this script in order to use this elsewhere. : "${LIBSECCOMP_VERSION:=2.6.0}" +: "${LIBPATHRS_VERSION:=0.2.4}" project="runc" root="$(readlink -f "$(dirname "${BASH_SOURCE[0]}")/..")" @@ -48,6 +49,8 @@ function build_project() { dylibdir="$(mktemp -d)" # Download and build libseccomp. "$root/script/build-seccomp.sh" "$LIBSECCOMP_VERSION" "$dylibdir" "${arches[@]}" + # Download and build libpathrs. + "$root/script/build-libpathrs.sh" "$LIBPATHRS_VERSION" "$dylibdir" "${arches[@]}" fi # For reproducible builds, add these to EXTRA_LDFLAGS: @@ -84,7 +87,7 @@ function build_project() { exit 1 fi - # Copy libseccomp source tarball. + # Copy dylib source tarballs. cp "$dylibdir"/src/* "$builddir" } diff --git a/script/release_sign.sh b/script/release_sign.sh index 80f8c0731..444720bf0 100755 --- a/script/release_sign.sh +++ b/script/release_sign.sh @@ -112,6 +112,9 @@ tmp_seccomp_gpgflags=("--homedir=$tmp_gpgdir" "--no-default-keyring" "--keyring= gpg "${tmp_seccomp_gpgflags[@]}" --recv-keys 0x47A68FCE37C7D7024FD65E11356CE62C2B524099 gpg "${tmp_seccomp_gpgflags[@]}" --recv-keys 0x7100AADFAE6E6E940D2E0AD655E45A5AE8CA7C8A +tmp_libpathrs_gpgflags=("--homedir=$tmp_gpgdir" "--no-default-keyring" "--keyring=libpathrs.keyring" "--keyserver=keys.openpgp.org") +gpg "${tmp_libpathrs_gpgflags[@]}" --recv-keys 0xC9C370B246B09F6DBCFC744C34401015D1D2D386 + gpg "${gpgflags[@]}" --clear-sign <<<"[This is test text used for $project release scripts. $(date --rfc-email)]" | gpg "${tmp_runc_gpgflags[@]}" --verify || bail "Signing key ${keyid:-DEFAULT} is not in trusted $project.keyring list!" @@ -156,4 +159,7 @@ gpg "${tmp_runc_gpgflags[@]}" --verify "$project.$hashcmd" # Verify seccomp tarball. gpg "${tmp_seccomp_gpgflags[@]}" --verify libseccomp*.asc +# Verify libpathrs tarball. +gpg "${tmp_libpathrs_gpgflags[@]}" --verify libpathrs*.asc + popd