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