Skip to content

Commit c2b87e1

Browse files
authored
Merge pull request #21 from rucoder/rucoder/riscv-support
Enable riscv support
2 parents 2f8b817 + d410be7 commit c2b87e1

2 files changed

Lines changed: 138 additions & 34 deletions

File tree

Dockerfile

Lines changed: 119 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -24,37 +24,140 @@ ADD config.toml /usr/local/cargo/
2424
RUN 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
28123
ENV TARGETS="x86_64-unknown-linux-musl aarch64-unknown-linux-musl x86_64-unknown-linux-gnu aarch64-unknown-linux-gnu riscv64gc-unknown-linux-gnu"
29124
RUN rustup target add ${TARGETS}
30125

31126
# needed for cargo-chef and cargo-sbom, as well as many other compilations
32127
RUN 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+
34140
ENV PATH=/usr/lib/llvm/bin:$PATH
35-
# export cross-compilation vars for both musl targets
141+
# export cross-compilation vars for all musl targets
36142
ENV 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
46156
COPY --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/

config.toml

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,29 @@
11
[target.aarch64-unknown-linux-musl]
22
linker = "/usr/bin/clang"
33
rustflags = [
4-
"-C",
5-
"link-arg=--ld-path=/usr/bin/mold",
6-
"-C",
7-
"link-arg=--target=aarch64-unknown-linux-musl",
4+
"-C", "link-arg=--ld-path=/usr/bin/mold",
5+
"-C", "link-arg=--target=aarch64-unknown-linux-musl",
6+
"-C", "link-self-contained=yes",
87
]
98

109
[target.x86_64-unknown-linux-musl]
1110
linker = "/usr/bin/clang"
1211
rustflags = [
13-
"-C",
14-
"link-arg=--ld-path=/usr/bin/mold",
15-
"-C",
16-
"link-arg=--target=x86_64-unknown-linux-musl",
12+
"-C", "link-arg=--ld-path=/usr/bin/mold",
13+
"-C", "link-arg=--target=x86_64-unknown-linux-musl",
14+
"-C", "link-self-contained=yes",
1715
]
1816

19-
# FIXME: riscv64 is not yet available in rust:1.80.1-alpine3.20
20-
# but both clang and mold support it. Also musl-dev is available in Alpine 3.20
21-
# [target.riscv64gc-unknown-linux-gnu]
22-
# linker = "/usr/bin/clang"
23-
# rustflags = [
24-
# "-C",
25-
# "link-arg=--ld-path=/usr/bin/mold",
26-
# "-C",
27-
# "link-arg=--target=riscv64-unknown-linux-musl",
28-
# ]
17+
[target.riscv64gc-unknown-linux-musl]
18+
linker = "/usr/bin/clang"
19+
# NOTE: riscv64gc-unknown-linux-musl is tier-3 in upstream rustc and its
20+
# target spec is missing `crt-static-default: true` (which x86_64/aarch64
21+
# musl have). Without `+crt-static` here, rustc picks the dynamic-unwind
22+
# path (`-lgcc_s` + `-pie`) instead of static (`-lunwind` + `-static`),
23+
# and the build fails because there is no riscv64 libgcc_s.so in the image.
24+
rustflags = [
25+
"-C", "link-arg=--ld-path=/usr/bin/mold",
26+
"-C", "link-arg=--target=riscv64-unknown-linux-musl",
27+
"-C", "link-self-contained=yes",
28+
"-C", "target-feature=+crt-static",
29+
]

0 commit comments

Comments
 (0)