Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions .github/Dockerfile-rust-musl
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Checks that the rust-musl image links a C dependency for both musl targets.
# aws-lc-sys compiled against glibc >= 2.38 headers emits __isoc23_* symbols
# that musl lacks; calling into it keeps the linker from dropping its archive.
# CI passes the freshly built rust-musl image so the check tests this PR's image.
ARG RUST_MUSL_IMAGE=ghcr.io/linkerd/dev:ci-rust-musl
FROM ${RUST_MUSL_IMAGE}
# aws-lc-rs pulls in aws-lc-sys, a C library sensitive to which libc headers it sees.
ARG AWS_LC_RS_VERSION=1.18.2 # repo=aws/aws-lc-rs,prefix=v
WORKDIR /musl-link
RUN cargo init --quiet --name musl-link && \
cargo add --quiet "aws-lc-rs@=${AWS_LC_RS_VERSION}"
# Calls into aws-lc so the linker must resolve its symbols; an unused dependency would be dropped.
COPY <<EOF src/main.rs
fn main() {
let d = aws_lc_rs::digest::digest(&aws_lc_rs::digest::SHA256, b"musl");
println!("{:02x?}", d.as_ref());
}
EOF
# just-cargo sets the musl CC, linker and flags, as the consuming repos' builds do.
RUN for target in x86_64-unknown-linux-musl aarch64-unknown-linux-musl ; do \
just-cargo target="$target" build || exit 1 ; \
done
# The musl headers must come from the musl release Rust links, or C dependencies
# can declare symbols the toolchain's libc.a lacks. Runs after the link so a
# header mismatch with the host libc is reported first.
RUN headers=$(cat /usr/local/musl/VERSION) && \
for target in x86_64-unknown-linux-musl aarch64-unknown-linux-musl ; do \
# musl embeds its release string in libc.a; this is the version Rust links.
toolchain=$(strings "$(rustc --print target-libdir --target "$target")/self-contained/libc.a" | grep -m1 -xE '[0-9]+\.[0-9]+\.[0-9]+') && \
if [ "$headers" != "$toolchain" ]; then \
echo "musl headers $headers do not match $target libc.a $toolchain" >&2 ; \
exit 1 ; \
fi ; \
done
12 changes: 12 additions & 0 deletions .github/workflows/devcontainer.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ on:
pull_request:
paths:
- .devcontainer/**
- .github/Dockerfile-rust-musl
- .github/workflows/devcontainer.yml
- bin/just-cargo
- Dockerfile
- justfile

Expand All @@ -19,6 +21,16 @@ jobs:
- uses: ./actions/setup-tools
- run: just build

# Builds the rust-musl image, then links a C dependency with it for both musl
# targets (see .github/Dockerfile-rust-musl).
dev-musl-link:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
- uses: ./actions/setup-tools
- run: just load=true version=ci targets=rust-musl build
- run: docker build --build-arg RUST_MUSL_IMAGE=ghcr.io/linkerd/dev:ci-rust-musl -f .github/Dockerfile-rust-musl .github

dev-shellcheck:
runs-on: ubuntu-latest
steps:
Expand Down
39 changes: 39 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,40 @@ ENV CARGO_INCREMENTAL=0 \
RUSTUP_MAX_RETRIES=10
ENTRYPOINT ["/usr/local/bin/just-cargo"]

# Builds musl C headers that match the musl release this Rust toolchain links.
#
# Why: musl targets compile C (e.g. `aws-lc-sys`) with clang, which otherwise
# uses the host glibc headers. Since glibc 2.38 those redirect `sscanf`/`strtol`
# to `__isoc23_*` symbols that musl lacks, so links fail. Debian's `musl-dev`
# doesn't help: clang ignores its include dir and its release can differ from
# the musl Rust links (trixie 1.2.5 vs Rust 1.90's 1.2.3).
#
# Caveats:
# - The version is read with `strings` from the toolchain's `libc.a`; if musl
# stops embedding it, `grep` finds no match and the build fails before the fetch.
# - Headers are fetched from musl.libc.org at build time, like other tools here.
# - Both musl targets are assumed to ship the same musl release.
FROM rust as rust-musl-patch
# Only needed to read the bundled musl version; both targets ship the same release.
RUN rustup target add x86_64-unknown-linux-musl
# musl embeds its release string (e.g. 1.2.3) in libc.a; there is no rustc flag for it.
# Saved to /musl/VERSION for the steps below and for CI to check against libc.a.
# grep exits non-zero if no version is found, so a format change fails here.
RUN musl_target_libdir=$(rustc --print target-libdir --target x86_64-unknown-linux-musl) && \
mkdir -p /musl && \
strings "$musl_target_libdir/self-contained/libc.a" | grep -m1 -xE '[0-9]+\.[0-9]+\.[0-9]+' > /musl/VERSION
# Fetch the musl source for that release.
RUN musl_version=$(cat /musl/VERSION) && \
scurl -o /musl.tar.gz "https://musl.libc.org/releases/musl-${musl_version}.tar.gz"
RUN mkdir -p /src && \
tar xzf /musl.tar.gz --strip-components=1 -C /src
# Installs only headers (copy plus sed-generated bits/*.h); nothing is compiled.
RUN make -C /src ARCH=x86_64 prefix=/musl/x86_64 install-headers
# bits/*.h are generated per arch under obj/; clear x86_64's so aarch64 regenerates its own.
RUN rm -rf /src/obj
# Installs the aarch64 headers.
RUN make -C /src ARCH=aarch64 prefix=/musl/aarch64 install-headers

FROM rust as rust-musl
RUN rustup target add \
aarch64-unknown-linux-musl \
Expand All @@ -359,6 +393,11 @@ RUN --mount=type=cache,from=apt-base,source=/etc/apt,target=/etc/apt,sharing=loc
g++-aarch64-linux-gnu \
gcc-aarch64-linux-gnu \
libc6-dev-arm64-cross
COPY --link --from=rust-musl-patch /musl /usr/local/musl
# cc-rs reads these per-target flags; `-isystem` puts musl's headers ahead of glibc's (see rust-musl-patch).
ENV CFLAGS_x86_64_unknown_linux_musl="-isystem /usr/local/musl/x86_64/include"
# Same for aarch64, with the aarch64 musl headers.
ENV CFLAGS_aarch64_unknown_linux_musl="-isystem /usr/local/musl/aarch64/include"

##
## Devcontainer
Expand Down
Loading