@@ -24,37 +24,140 @@ ADD config.toml /usr/local/cargo/
2424RUN cargo install --root /cargo-cross cargo-chef@0.1.71 cargo-sbom@0.9.1
2525
2626
27- FROM rust:${RUST_VERSION}-alpine3.20 AS tools-target-base
27+ # ---------------------------------------------------------------------------
28+ # Extract the riscv64 linker inputs that rustup's tier-1 musl targets
29+ # normally ship in <sysroot>/lib/rustlib/<target>/lib/self-contained/:
30+ # - musl startup objects: Scrt1.o, crt1.o, crti.o, crtn.o, rcrt1.o
31+ # - libc.a (from musl-dev)
32+ # - libunwind.a (from llvm-libunwind-static)
33+ # - libgcc runtime startup: crtbegin.o, crtbeginS.o, crtend.o, crtendS.o
34+ # (from Alpine gcc — rustc's linux_musl target spec emits these as
35+ # bare filenames to the linker, so they must be findable via -L at link
36+ # time; rustup's self-contained dir is added to -L automatically.)
37+ # None of these come from `-Z build-std` (which only builds Rust crates).
38+ # Without this bundle, rustc's linker invocation falls back to clang's host
39+ # library search (/usr/lib on amd64), silently picking up amd64 artifacts.
40+ # ---------------------------------------------------------------------------
41+ FROM --platform=linux/riscv64 alpine:3.22 AS riscv64-musl-crt
42+ RUN apk add --no-cache musl-dev llvm-libunwind-static gcc
43+ RUN mkdir -p /crt-out && \
44+ cp /usr/lib/Scrt1.o /usr/lib/crt1.o /usr/lib/crti.o /usr/lib/crtn.o /usr/lib/rcrt1.o \
45+ /usr/lib/libc.a /usr/lib/libunwind.a \
46+ /crt-out/ && \
47+ cp /usr/lib/gcc/riscv64-alpine-linux-musl/*/crtbegin.o \
48+ /usr/lib/gcc/riscv64-alpine-linux-musl/*/crtbeginS.o \
49+ /usr/lib/gcc/riscv64-alpine-linux-musl/*/crtend.o \
50+ /usr/lib/gcc/riscv64-alpine-linux-musl/*/crtendS.o \
51+ /crt-out/ && \
52+ ls -la /crt-out
53+
54+
55+ # ---------------------------------------------------------------------------
56+ # Build std/core/alloc/proc_macro for the tier-3 riscv64gc-unknown-linux-musl
57+ # target. The resulting rlibs are copied into the final image's rustup sysroot
58+ # so downstream builds see the target as fully installed (no -Z build-std,
59+ # no RUSTC_BOOTSTRAP, no rust-src needed downstream).
60+ # ---------------------------------------------------------------------------
61+ FROM --platform=$BUILDPLATFORM rust:${RUST_VERSION}-alpine3.22 AS riscv64-std-builder
62+ RUN rustup target add riscv64gc-unknown-linux-musl && \
63+ rustup component add rust-src
64+ RUN apk add musl-dev linux-headers make clang llvm lld mold
65+
66+ ENV PATH=/usr/lib/llvm/bin:$PATH
67+ ENV CC_riscv64gc_unknown_linux_musl=clang \
68+ CFLAGS_riscv64gc_unknown_linux_musl="--target=riscv64-unknown-linux-musl" \
69+ AR_riscv64gc_unknown_linux_musl=llvm-ar \
70+ RANLIB_riscv64gc_unknown_linux_musl=llvm-ranlib
71+ # allow `-Z` flags on stable rust
72+ ENV RUSTC_BOOTSTRAP=1
73+
74+ # Build std (and its deps: core, alloc, proc_macro) for the riscv64 musl
75+ # target by compiling a tiny dummy crate with `-Z build-std`. The compiled
76+ # rlibs end up under target/<triple>/release/deps and we then assemble them
77+ # into a rustup-style sysroot layout at /sysroot-out.
78+ WORKDIR /build-std
79+ RUN cargo init --lib --name dummy_std_build .
80+ # Configure cross-compilation for the dummy crate (mirrors final image config)
81+ RUN mkdir -p .cargo && printf '%s\n ' \
82+ '[target.riscv64gc-unknown-linux-musl]' \
83+ 'linker = "/usr/bin/clang"' \
84+ 'rustflags = [' \
85+ ' "-C", "link-arg=--ld-path=/usr/bin/mold",' \
86+ ' "-C", "link-arg=--target=riscv64-unknown-linux-musl",' \
87+ ']' \
88+ > .cargo/config.toml
89+
90+ # Build std with:
91+ # - `-C embed-bitcode=yes` so downstream fat-LTO builds (e.g. vector with
92+ # `-C lto=fat`) can perform cross-crate LTO including std. Without this,
93+ # the linker fails with: "failed to get bitcode from object file for
94+ # LTO (Can't find section .llvmbc)".
95+ # - `-C target-feature=+crt-static` to match how consumers will be built.
96+ # Upstream rustc's tier-3 riscv64gc-unknown-linux-musl target spec is
97+ # missing `crt-static-default: true` (present on x86_64/aarch64 musl),
98+ # so we bake it in both here and in /usr/local/cargo/config.toml.
99+ # Mismatched std vs user crt-static can produce TLS/panic-runtime ABI
100+ # drift at link time.
101+ ENV CARGO_PROFILE_RELEASE_LTO=off
102+ RUN RUSTFLAGS="-C embed-bitcode=yes -C target-feature=+crt-static" \
103+ cargo build --release \
104+ --target riscv64gc-unknown-linux-musl \
105+ -Z build-std=std,panic_abort,core,alloc,proc_macro
106+
107+ # Assemble a rustup-style sysroot fragment containing the freshly built std
108+ # rlibs PLUS the musl libc startup objects that rustc expects under
109+ # self-contained/. rustc looks for these under:
110+ # <sysroot>/lib/rustlib/<target>/lib/ (rlibs)
111+ # <sysroot>/lib/rustlib/<target>/lib/self-contained/ (Scrt1.o, crt1.o, ...)
112+ COPY --from=riscv64-musl-crt /crt-out/ /sysroot-out/lib/rustlib/riscv64gc-unknown-linux-musl/lib/self-contained/
113+ RUN SYSROOT_LIB=/sysroot-out/lib/rustlib/riscv64gc-unknown-linux-musl/lib && \
114+ mkdir -p "${SYSROOT_LIB}" && \
115+ cp /build-std/target/riscv64gc-unknown-linux-musl/release/deps/*.rlib "${SYSROOT_LIB}/" && \
116+ cp /build-std/target/riscv64gc-unknown-linux-musl/release/deps/*.rmeta "${SYSROOT_LIB}/" 2>/dev/null || true && \
117+ ls -la "${SYSROOT_LIB}" && \
118+ ls -la "${SYSROOT_LIB}/self-contained/"
119+
120+
121+ FROM rust:${RUST_VERSION}-alpine3.22 AS tools-target-base
122+ ARG RUST_VERSION
28123ENV TARGETS="x86_64-unknown-linux-musl aarch64-unknown-linux-musl x86_64-unknown-linux-gnu aarch64-unknown-linux-gnu riscv64gc-unknown-linux-gnu"
29124RUN rustup target add ${TARGETS}
30125
31126# needed for cargo-chef and cargo-sbom, as well as many other compilations
32127RUN apk add musl-dev linux-headers make clang llvm lld mold python3 git perl protoc
33128
129+ # Stage the prebuilt riscv64gc-unknown-linux-musl std rlibs into a known
130+ # location, then install them into whichever host-arch toolchain dir rustup
131+ # actually created in this image. The target rlibs are host-independent, so
132+ # the same artifacts work regardless of the final image's host architecture.
133+ COPY --from=riscv64-std-builder /sysroot-out/ /tmp/riscv64-musl-sysroot/
134+ RUN HOST_TOOLCHAIN_DIR="$(ls -d /usr/local/rustup/toolchains/${RUST_VERSION}-* | head -n1)" && \
135+ echo "Installing riscv64-musl std into ${HOST_TOOLCHAIN_DIR}" && \
136+ cp -r /tmp/riscv64-musl-sysroot/lib "${HOST_TOOLCHAIN_DIR}/" && \
137+ rm -rf /tmp/riscv64-musl-sysroot && \
138+ ls -la "${HOST_TOOLCHAIN_DIR}/lib/rustlib/riscv64gc-unknown-linux-musl/lib/"
139+
34140ENV PATH=/usr/lib/llvm/bin:$PATH
35- # export cross-compilation vars for both musl targets
141+ # export cross-compilation vars for all musl targets
36142ENV CC_aarch64_unknown_linux_musl=clang \
37143 CFLAGS_aarch64_unknown_linux_musl="--target=aarch64-unknown-linux-musl" \
38144 AR_aarch64_unknown_linux_musl=llvm-ar \
39145 RANLIB_aarch64_unknown_linux_musl=llvm-ranlib \
40146 CC_x86_64_unknown_linux_musl=clang \
41147 CFLAGS_x86_64_unknown_linux_musl="--target=x86_64-unknown-linux-musl" \
42148 AR_x86_64_unknown_linux_musl=llvm-ar \
43- RANLIB_x86_64_unknown_linux_musl=llvm-ranlib
149+ RANLIB_x86_64_unknown_linux_musl=llvm-ranlib \
150+ CC_riscv64gc_unknown_linux_musl=clang \
151+ CFLAGS_riscv64gc_unknown_linux_musl="--target=riscv64-unknown-linux-musl" \
152+ AR_riscv64gc_unknown_linux_musl=llvm-ar \
153+ RANLIB_riscv64gc_unknown_linux_musl=llvm-ranlib
44154
45155# copy the cargo plugins from the tools stage
46156COPY --from=tools /cargo-cross /usr/local/cargo
47- # we define target specific rustc flags for cross-compilation
48- ADD config.toml /usr/local/cargo/
49-
50- FROM tools-target-base AS tools-target-amd64
51- ENV CARGO_BUILD_TARGET="x86_64-unknown-linux-musl"
52-
53- FROM tools-target-base AS tools-target-arm64
54- ENV CARGO_BUILD_TARGET="aarch64-unknown-linux-musl"
55157
56- FROM tools-target-base AS tools-target-riscv64
57- ENV CARGO_BUILD_TARGET="riscv64gc-unknown-linux-gnu"
58-
59- FROM tools-target-$TARGETARCH AS tools-target
60- RUN echo "Cargo target: $CARGO_BUILD_TARGET"
158+ # Per-target cross-compile config. Consumers that need to add their own
159+ # rustflags should either (a) not override RUSTFLAGS, or (b) ship their own
160+ # cargo config.toml that uses `[target.<triple>]` or `[target.'cfg(...)']`
161+ # rustflags so cargo merges them with the entries below (rustflags from the
162+ # RUSTFLAGS env var fully replaces config.toml rustflags and is discouraged).
163+ ADD config.toml /usr/local/cargo/
0 commit comments