diff --git a/cpython-unix/build-cpython.sh b/cpython-unix/build-cpython.sh index 1c98755af..1f60e51c8 100755 --- a/cpython-unix/build-cpython.sh +++ b/cpython-unix/build-cpython.sh @@ -692,6 +692,16 @@ if [ -n "${CROSS_COMPILING}" ]; then arm64*|aarch64*|armv7*|thumb7*|ppc64*|s390*|x86*) CONFIGURE_FLAGS="${CONFIGURE_FLAGS} ac_cv_aligned_required=no" ;; + riscv64*) + # Cross-configure defaults to FNV on RISC-V. SipHash uses memcpy + # for unaligned reads, so select it without changing the target's + # alignment requirements. Preserve each Python version's algorithm. + if [[ -n "${PYTHON_MEETS_MAXIMUM_VERSION_3_10}" ]]; then + CONFIGURE_FLAGS="${CONFIGURE_FLAGS} --with-hash-algorithm=siphash24" + else + CONFIGURE_FLAGS="${CONFIGURE_FLAGS} --with-hash-algorithm=siphash13" + fi + ;; esac # When cross-compiling, configure defaults to assuming `sem_getvalue` is broken, @@ -1098,10 +1108,14 @@ if xcode_path: # -fdebug-default-version is Clang only. Strip so compiling works on GCC. replace_in_all("-fdebug-default-version=4", "") -# Target sysroots only exist in the build container. Keeping their paths in -# sysconfig would make downstream extension builds search a nonexistent root. +# Target sysroots and GCC installations only exist in the build container. +# Downstream native extension builds must select their own compiler and linker, +# without inheriting Clang's cross-compilation flags or build-only paths. for flag in os.environ.get("EXTRA_TARGET_CFLAGS", "").split(): - if flag.startswith("--sysroot="): + if ( + flag.startswith(("--sysroot=", "--gcc-install-dir=", "--target=")) + or flag in ("-fuse-ld=lld", "-Wno-unused-command-line-argument") + ): replace_in_all(flag, "") # Remove some build environment paths. @@ -1131,9 +1145,20 @@ import importlib.machinery import importlib.util import json import os +import runpy import sys import sysconfig +# The cross-build helper reads sysconfig from the unmodified build tree. +# Use the cleaned installed values for PYTHON.json as well. +lib_suffix = "t" if os.environ.get("CPYTHON_FREETHREADED") else "" +sysconfig_data = os.path.join( + os.environ["ROOT"], "out", "python", "install", "lib", + "python%s%s" % (sysconfig.get_python_version(), lib_suffix), + sysconfig._get_sysconfigdata_name() + ".py", +) +installed_config_vars = runpy.run_path(sysconfig_data)["build_time_vars"] + # When doing cross builds, sysconfig still picks up abiflags from the # host Python, which is never built in debug or free-threaded mode. Patch abiflags accordingly. if os.environ.get("CPYTHON_FREETHREADED") and "t" not in sysconfig.get_config_var("abiflags"): @@ -1160,6 +1185,11 @@ extension_suffixes.append(".abi3.so") extension_suffixes.append(".so") +# Merge into a separate dictionary: newer Python versions can reinitialize +# sysconfig's cache and restore the unmodified build-tree values. +config_vars = dict(sysconfig.get_config_vars()) +config_vars.update(installed_config_vars) + metadata = { "python_abi_tag": sys.abiflags, "python_implementation_cache_tag": sys.implementation.cache_tag, @@ -1180,7 +1210,7 @@ metadata = { "python_exe": "install/bin/python%s%s" % (sysconfig.get_python_version(), sys.abiflags), "python_major_minor_version": sysconfig.get_python_version(), "python_stdlib_platform_config": sysconfig.get_config_var("LIBPL").lstrip("/"), - "python_config_vars": {k: str(v) for k, v in sysconfig.get_config_vars().items()}, + "python_config_vars": {k: str(v) for k, v in config_vars.items()}, } # When cross-compiling, we use a host Python to run this script. There are @@ -1214,7 +1244,7 @@ ${BUILD_PYTHON} "${ROOT}/generate_metadata.py" "${ROOT}/metadata.json" cat "${ROOT}/metadata.json" if [ "${CC}" != "musl-clang" ]; then - objdump -T "${LIBPYTHON_SHARED_LIBRARY}" | grep GLIBC_ | awk '{print $5}' | awk -F_ '{print $2}' | sort -V | tail -n 1 > "${ROOT}/glibc_version.txt" + objdump -T "${LIBPYTHON_SHARED_LIBRARY}" | grep -oE 'GLIBC_[0-9]+(\.[0-9]+)*' | cut -d_ -f2 | sort -V | tail -n 1 > "${ROOT}/glibc_version.txt" cat "${ROOT}/glibc_version.txt" fi diff --git a/cpython-unix/build-libX11.sh b/cpython-unix/build-libX11.sh index 66883c1a9..82d0548af 100755 --- a/cpython-unix/build-libX11.sh +++ b/cpython-unix/build-libX11.sh @@ -108,5 +108,9 @@ CFLAGS="${EXTRA_TARGET_CFLAGS} -fPIC -I/tools/deps/include" \ --disable-silent-rules \ ${EXTRA_FLAGS} +# Libtool's shared-library link filter drops these Clang cross-compilation +# flags, which would otherwise select host startup objects and runtimes. +sed -i 's/|--sysroot=\*|/|--sysroot=*|--target=*|--gcc-install-dir=*|/g' libtool + make -j "$(nproc)" make -j "$(nproc)" install DESTDIR="${ROOT}/out" diff --git a/cpython-unix/build-libXau.sh b/cpython-unix/build-libXau.sh index d5f5175f5..de74a804b 100755 --- a/cpython-unix/build-libXau.sh +++ b/cpython-unix/build-libXau.sh @@ -23,5 +23,9 @@ CFLAGS="${EXTRA_TARGET_CFLAGS} -fPIC" CPPFLAGS="${EXTRA_TARGET_CFLAGS} -fPIC" LD --prefix=/tools/deps \ ${EXTRA_FLAGS} +# Libtool's shared-library link filter drops these Clang cross-compilation +# flags, which would otherwise select host startup objects and runtimes. +sed -i 's/|--sysroot=\*|/|--sysroot=*|--target=*|--gcc-install-dir=*|/g' libtool + make -j "$(nproc)" make -j "$(nproc)" install DESTDIR="${ROOT}/out" diff --git a/cpython-unix/build-libxcb.sh b/cpython-unix/build-libxcb.sh index 5d77c47e9..18d7b4b9a 100755 --- a/cpython-unix/build-libxcb.sh +++ b/cpython-unix/build-libxcb.sh @@ -29,5 +29,9 @@ CFLAGS="${EXTRA_TARGET_CFLAGS} -fPIC" CPPFLAGS="${EXTRA_TARGET_CFLAGS} -fPIC" L --prefix=/tools/deps \ ${EXTRA_FLAGS} +# Libtool's shared-library link filter drops these Clang cross-compilation +# flags, which would otherwise select host startup objects and runtimes. +sed -i 's/|--sysroot=\*|/|--sysroot=*|--target=*|--gcc-install-dir=*|/g' libtool + make -j "$(nproc)" make -j "$(nproc)" install DESTDIR="${ROOT}/out" diff --git a/cpython-unix/build.cross-riscv64.Dockerfile b/cpython-unix/build.cross-riscv64.Dockerfile index 2e43a1521..f4077b2f1 100644 --- a/cpython-unix/build.cross-riscv64.Dockerfile +++ b/cpython-unix/build.cross-riscv64.Dockerfile @@ -1,6 +1,6 @@ -# Debian Buster. -FROM debian@sha256:2a0c1b9175adf759420fe0fbd7f5b449038319171eb76554bb76cbe172b62b42 -LABEL org.opencontainers.image.authors="Gregory Szorc " +# Debian Trixie +FROM debian@sha256:3352c2e13876c8a5c5873ef20870e1939e73cb9a3c1aeba5e3e72172a85ce9ed +LABEL org.opencontainers.image.authors="Jonathan J. Helmus " RUN groupadd -g 1000 build && \ useradd -u 1000 -g 1000 -d /build -s /bin/bash -m build && \ @@ -17,9 +17,19 @@ ENV HOME=/build \ CMD ["/bin/bash", "--login"] WORKDIR '/build' +# curl +RUN apt-get update && apt-get install --yes ca-certificates curl + +# Add the LLVM 23 repository. +RUN curl -fsSL https://apt.llvm.org/llvm-snapshot.gpg.key \ + -o /etc/apt/trusted.gpg.d/apt.llvm.org.asc \ + && echo "deb https://apt.llvm.org/trixie/ llvm-toolchain-trixie-23 main" \ + > /etc/apt/sources.list.d/llvm.list + +# Add buster as a source for riscv sysroot packages RUN for s in debian_buster debian_buster-updates debian-security_buster/updates; do \ - echo "deb http://snapshot.debian.org/archive/${s%_*}/20250109T084424Z/ ${s#*_} main"; \ - done > /etc/apt/sources.list && \ + echo "deb https://snapshot.debian.org/archive/${s%_*}/20250109T084424Z/ ${s#*_} main"; \ + done > /etc/apt/sources.list.d/buster.list && \ ( echo 'quiet "true";'; \ echo 'APT::Get::Assume-Yes "true";'; \ echo 'APT::Install-Recommends "false";'; \ @@ -27,13 +37,15 @@ RUN for s in debian_buster debian_buster-updates debian-security_buster/updates; echo 'Acquire::Retries "5";'; \ ) > /etc/apt/apt.conf.d/99cpython-portable +# Pin the riscv sysroot packages to buster +RUN printf 'Package: *-riscv64-cross\nPin: release n=buster\nPin-Priority: 900\n' \ + > /etc/apt/preferences.d/buster-cross + RUN apt-get update # Host building. RUN apt-get install \ bzip2 \ - gcc \ - g++ \ libc6-dev \ libffi-dev \ make \ @@ -46,32 +58,27 @@ RUN apt-get install \ zip \ zlib1g-dev -# Cross-building. +# LLVM +RUN apt-get install \ + clang-23 \ + lld-23 \ + llvm-23 + RUN apt-get install \ - g++-aarch64-linux-gnu \ - g++-arm-linux-gnueabi \ - g++-arm-linux-gnueabihf \ - g++-mips-linux-gnu \ - g++-mips64el-linux-gnuabi64 \ - g++-mipsel-linux-gnu \ - g++-powerpc64le-linux-gnu \ - g++-riscv64-linux-gnu \ - g++-s390x-linux-gnu \ - gcc-aarch64-linux-gnu \ - gcc-arm-linux-gnueabi \ - gcc-arm-linux-gnueabihf \ - gcc-mips-linux-gnu \ - gcc-mips64el-linux-gnuabi64 \ - gcc-mipsel-linux-gnu \ - gcc-powerpc64le-linux-gnu \ - gcc-riscv64-linux-gnu \ - gcc-s390x-linux-gnu \ - libc6-dev-arm64-cross \ - libc6-dev-armel-cross \ - libc6-dev-armhf-cross \ - libc6-dev-mips-cross \ - libc6-dev-mips64el-cross \ - libc6-dev-mipsel-cross \ - libc6-dev-ppc64el-cross \ libc6-dev-riscv64-cross \ - libc6-dev-s390x-cross + libc6-riscv64-cross \ + linux-libc-dev-riscv64-cross \ + libgcc1-riscv64-cross \ + libgcc-8-dev-riscv64-cross + +# Cross libc linker scripts use absolute /usr/riscv64-linux-gnu/lib paths, +# which the linker resolves relative to --sysroot. Mirror that prefix and +# provide the standard include/library directories in the flat cross sysroot. +RUN sysroot=/usr/riscv64-linux-gnu && \ + mkdir "${sysroot}/usr" && \ + ln -s .. "${sysroot}/usr/riscv64-linux-gnu" && \ + ln -s ../include "${sysroot}/usr/include" && \ + ln -s ../lib "${sysroot}/usr/lib" + +# CPython's configure searches for a target-prefixed archiver when cross-building. +RUN ln -s /usr/lib/llvm-23/bin/llvm-ar /usr/bin/riscv64-unknown-linux-gnu-llvm-ar diff --git a/cpython-unix/build.py b/cpython-unix/build.py index cf4d7afd3..1d819e1f4 100755 --- a/cpython-unix/build.py +++ b/cpython-unix/build.py @@ -249,8 +249,10 @@ def toolchain_archive_path(package_name, host_platform): return BUILD / basename -def install_binutils(platform): - return not platform.startswith("macos_") +def install_binutils(host_platform, target_triple): + if "riscv64" in target_triple: + return False + return not host_platform.startswith("macos_") def simple_build( @@ -275,7 +277,7 @@ def simple_build( host_platform, target_triple, binutils_image=docker_image_names(settings)["gcc"], - binutils=install_binutils(host_platform), + binutils=install_binutils(host_platform, target_triple), clang=True, musl="musl" in target_triple, static="static" in build_options, @@ -399,7 +401,7 @@ def build_libedit( host_platform, target_triple, binutils_image=docker_image_names(settings)["gcc"], - binutils=install_binutils(host_platform), + binutils=install_binutils(host_platform, target_triple), clang=True, musl="musl" in target_triple, static="static" in build_options, @@ -452,7 +454,7 @@ def build_cpython_host( host_platform, target_triple, binutils_image=docker_image_names(settings)["gcc"], - binutils=install_binutils(host_platform), + binutils=install_binutils(host_platform, target_triple), clang=True, static="static" in build_options, ) @@ -791,7 +793,7 @@ def build_cpython( host_platform, target_triple, binutils_image=docker_image_names(settings)["gcc"], - binutils=install_binutils(host_platform), + binutils=install_binutils(host_platform, target_triple), clang=True, musl="musl" in target_triple, static="static" in build_options, diff --git a/cpython-unix/targets.yml b/cpython-unix/targets.yml index 9ebe96884..14b8fed95 100644 --- a/cpython-unix/targets.yml +++ b/cpython-unix/targets.yml @@ -523,6 +523,8 @@ riscv64-unknown-linux-gnu: linux_uapi_include_arch: riscv64-linux-gnu host_platforms: - linux_x86_64 + - linux_aarch64 + - macos_arm64 pythons_supported: - '3.10' - '3.11' @@ -531,16 +533,29 @@ riscv64-unknown-linux-gnu: - '3.14' - '3.15' docker_image_suffix: .cross-riscv64 - host_cc: /usr/bin/x86_64-linux-gnu-gcc - host_cxx: /usr/bin/x86_64-linux-gnu-g++ - target_cc: /usr/bin/riscv64-linux-gnu-gcc - target_cxx: /usr/bin/riscv64-linux-gnu-g++ + host_cc: clang-23 + host_cxx: clang++-23 + target_cc: clang-23 + target_cxx: clang++-23 target_cflags: + - '--target=riscv64-unknown-linux-gnu' + - '--sysroot=/usr/riscv64-linux-gnu' + # Debian installs GCC startup objects outside the libc sysroot. + - '--gcc-install-dir=/usr/lib/gcc-cross/riscv64-linux-gnu/8' + # Some dependencies also use CFLAGS when linking. Suppress the unused + # linker argument warning in compile-only checks that enable -Werror. + - '-fuse-ld=lld' + - '-Wno-unused-command-line-argument' # Hardening - '-fstack-protector' - '-U_FORTIFY_SOURCE' - '-D_FORTIFY_SOURCE=2' target_ldflags: + - '--target=riscv64-unknown-linux-gnu' + - '--sysroot=/usr/riscv64-linux-gnu' + # Debian installs GCC startup objects outside the libc sysroot. + - '--gcc-install-dir=/usr/lib/gcc-cross/riscv64-linux-gnu/8' + - '-fuse-ld=lld' # Hardening - '-Wl,-z,noexecstack' - '-Wl,-z,relro' @@ -549,7 +564,6 @@ riscv64-unknown-linux-gnu: needs: - autoconf - bdb - - binutils - bzip2 - expat - libedit diff --git a/src/validation.rs b/src/validation.rs index 7cdabb378..f39547433 100644 --- a/src/validation.rs +++ b/src/validation.rs @@ -277,7 +277,11 @@ static ELF_ALLOWED_LIBRARIES_BY_TRIPLE: Lazy