-
-
Notifications
You must be signed in to change notification settings - Fork 2.9k
zig cc
: Respect Clang's -static
and -dynamic
flags
#23572
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
They are, themselves, static libraries even if the resulting artifact strictly speaking requires dynamic linking to the corresponding system DLLs to run. Note, though, that there's no libc-provided dynamic linker on Windows like on POSIX, so this isn't particularly problematic. This matches x86_64-w64-mingw32-gcc behavior.
Before: ❯ zig cc main.c -target x86_64-linux-musl && musl-ldd ./a.out musl-ldd: ./a.out: Not a valid dynamic program ❯ zig cc main.c -target x86_64-linux-musl -static && musl-ldd ./a.out musl-ldd: ./a.out: Not a valid dynamic program ❯ zig cc main.c -target x86_64-linux-musl -dynamic && musl-ldd ./a.out musl-ldd: ./a.out: Not a valid dynamic program After: ❯ zig cc main.c -target x86_64-linux-musl && musl-ldd ./a.out musl-ldd: ./a.out: Not a valid dynamic program ❯ zig cc main.c -target x86_64-linux-musl -static && musl-ldd ./a.out musl-ldd: ./a.out: Not a valid dynamic program ❯ zig cc main.c -target x86_64-linux-musl -dynamic && musl-ldd ./a.out /lib/ld-musl-x86_64.so.1 (0x72c10019e000) libc.so => /lib/ld-musl-x86_64.so.1 (0x72c10019e000) Closes ziglang#11909.
07c731b
to
6a82286
Compare
It would be nice if https://ziglang.org/download/ mentioned the release cadence for the master builds? I'm assuming weekly (seems to be on Sundays)? The current master build is from Sunday 27th April, but was a couple commits before this one was merged: I wanted to test this feature out and also verify that it'd work with glibc (I'm aware of the caveats of static glibc), but I'll need to wait until the next build is available it seems.
ARG BASE_IMAGE=fedora
# Alternatively use a release version like `0.14.0`:
ARG ZIG_VERSION=master
# Separate stage (base image agnostic) for caching Zig releases:
FROM alpine AS get-zig
RUN apk --no-cache add curl jq
ARG ZIG_VERSION
RUN <<"HEREDOC"
# Grab a Zig release (jq retrieves the URL from the JSON data, eg: `.master.x86_64-linux.tarball):
ZIG_RELEASE=$(curl -fsSL https://ziglang.org/download/index.json | jq -rc ".[\"${ZIG_VERSION}\"][\"x86_64-linux\"].tarball")
ZIG_DIR="/opt/zig-${ZIG_VERSION}"
# Prefer mkdir with `--strip-components=1` otherwise archive top-level directory is the longer archive filename from URL
# Use `--no-same-owner` because ownership is otherwise 1000:1000
mkdir "${ZIG_DIR}"
curl -fsSL "${ZIG_RELEASE}" | tar --extract --xz --directory "${ZIG_DIR}" --strip-components=1 --no-same-owner
HEREDOC
# Base images:
FROM fedora:42 AS base-fedora
# For `zig cc` to support static glibc builds these files are required:
# https://github.com/ziglang/zig/issues/4986#issuecomment-1982362679
# - `glibc-static` provides: /usr/lib64/libc.a
# - `gcc` provides: /usr/lib/gcc/x86_64-redhat-linux/15/libgcc_eh.a
# `musl-libc` provides the musl libc + interpreter to run the `musl-ldd` script
RUN dnf --setopt=install_weak_deps=0 install -yq file jq gcc glibc-static musl-libc
FROM debian:12-slim AS base-debian
ARG DEBIAN_FRONTEND=noninteractive
# `musl` for `musl-ldd` + dynamic link musl
# `gcc` + `libc6-dev` for static link glibc
RUN apt-get -qq update && apt-get -qq install file gcc libc6-dev musl
# NOTE: Alpine is not supported for static glibc builds:
FROM alpine:3.21 AS base-alpine
RUN apk add bash file
# Final image stage:
FROM base-"${BASE_IMAGE}"
ARG ZIG_VERSION
COPY --link --from=get-zig "/opt/zig-${ZIG_VERSION}" /opt/ziglang
RUN ln -s /opt/ziglang/zig /usr/local/bin/zig
# Defaults for convenience:
WORKDIR /example
CMD ["zig-test"]
# Remainder of this file embeds content:
# Example program to test with:
COPY <<"HEREDOC" /example/main.c
#include <stdio.h>
int main()
{
printf("Hello World!\n");
}
HEREDOC
# Various shell scripts for convenience:
COPY --chmod=755 <<"HEREDOC" /usr/local/bin/zig-test
#!/bin/bash
echo "Testing builds via Zig $(zig version)"
# Basic build:
echo -e '\nRunning: zig cc main.c -o example'
zig cc main.c -o example
inspect-bin example
echo -e '\n'
# Build with specific target libc + `-static`/-dynamic` flag variants:
zig-test-arch musl
zig-test-arch gnu
# One final attempt with glibc static:
echo 'Running a glibc static build with explicit inputs to link for glibc-static'
echo -e "\n..with '-static':"
zig cc main.c -o example-gnu-inputs-static -target x86_64-linux-gnu -static \
/usr/lib64/libc.a \
/usr/lib/gcc/x86_64-redhat-linux/15/libgcc_eh.a
[[ ${?} -eq 0 ]] && inspect-bin example-gnu-inputs-static
echo -e "\n..without '-static':"
zig cc main.c -o example-gnu-inputs -target x86_64-linux-gnu \
/usr/lib64/libc.a \
/usr/lib/gcc/x86_64-redhat-linux/15/libgcc_eh.a
[[ ${?} -eq 0 ]] && inspect-bin example-gnu-inputs
# Build static libc from host (this one actually works):
# https://github.com/ziglang/zig/pull/23752#issuecomment-2849743043
# NOTE: Alpine/musl-based build host is not supported by zig for glibc static builds
# Regardless Alpine lacks the equivalent glibc-static package required.
echo -e "\n..with '-static' but without '-target' or inputs:"
echo 'Running: zig cc main.c -o example-static -static'
zig cc main.c -o example-static -static
[[ ${?} -eq 0 ]] && inspect-bin example-static
HEREDOC
COPY --chmod=755 <<"HEREDOC" /usr/local/bin/zig-test-arch
#!/bin/bash
TARGET_ARCH=${1?must provide an arch}
BUILD_TARGET="x86_64-linux-${TARGET_ARCH}"
for VARIANT in default static dynamic; do
BUILD_FLAGS=()
# Append the `-static` or `-dynamic` flags:
[[ "${VARIANT}" != 'default' ]] && BUILD_FLAGS+=("-${VARIANT}")
# NOTE: Command stored in a list for printing to stdout via echo before running it:
# Example: `zig cc main.c -o example-gnu-static -target x86_64-linux-gnu -static`
RUN_COMMAND=(zig cc main.c -o "example-${TARGET_ARCH}-${VARIANT}" -target "${BUILD_TARGET}" "${BUILD_FLAGS[@]}")
echo "Running: ${RUN_COMMAND[@]}"
"${RUN_COMMAND[@]}"
# If status code is 0 (success) inspect the bin:
[[ ${?} -eq 0 ]] && inspect-bin "example-${TARGET_ARCH}-${VARIANT}" "${TARGET_ARCH}"
echo -e '\n'
done
HEREDOC
COPY --chmod=755 <<"HEREDOC" /usr/local/bin/inspect-bin
#!/bin/bash
BIN=${1?must provide path to binary}
TARGET_ARCH="${2:-gnu}"
case "${TARGET_ARCH}" in
( 'gnu' ) export LDD='ldd' ;;
( 'musl' ) export LDD='musl-ldd' ;;
( '*' ) echo 'Unsupported ARCH'; return 1 ;;
esac
function _inspect() {
local BIN=${1}
echo "Inspecting '${BIN}'"
file "${BIN}"
"${LDD}" "${BIN}"
}
_inspect $1
HEREDOC
COPY --chmod=755 <<"HEREDOC" /usr/local/bin/musl-ldd
#!/bin/sh
/lib/ld-musl-x86_64.so.1 --list "${@}"
HEREDOC Original Dockerfile for this commentThis was the version used for output reported. The newer version of the Dockerfile is a bit more thorough in coverage and better handles output with build failures. FROM fedora:42
RUN <<"HEREDOC"
# Only `file` + `jq` are probably relevant?:
dnf --setopt=install_weak_deps=0 install -yq file jq gcc glibc-static musl-libc nano
# Install Zig (latest master branch build)
ZIG_AMD64_MASTER=$(curl -fsSL https://ziglang.org/download/index.json | jq -rc '.master["x86_64-linux"].tarball')
# Prefer mkdir with `--strip-components=1` otherwise archive top-level directory is the longer archive filename from URL
# Use `--no-same-owner` because ownership is otherwise 1000:1000
mkdir /opt/zig-master
curl -fsSL "${ZIG_AMD64_MASTER}" | tar --extract --xz --directory /opt/zig-master --strip-components=1 --no-same-owner
ln -s /opt/zig-master/zig /usr/local/bin/zig
HEREDOC
# Defaults for convenience:
WORKDIR /example
CMD ["zig-test"]
# Remainder of this file embeds content:
# Example program to test with:
COPY <<"HEREDOC" /example/main.c
#include <stdio.h>
int main()
{
printf("Hello World!\n");
}
HEREDOC
# Various shell scripts for convenience:
COPY --chmod=755 <<HEREDOC /usr/local/bin/zig-test
#!/bin/sh
zig cc main.c -o example
inspect-bin example
zig-test-arch musl
zig-test-arch gnu
HEREDOC
COPY --chmod=755 <<"HEREDOC" /usr/local/bin/zig-test-arch
#!/bin/sh
TARGET_ARCH=${1?must provide an arch}
BUILD_TARGET="x86_64-linux-${TARGET_ARCH}"
zig cc main.c -o "example-${TARGET_ARCH}-default" -target "${BUILD_TARGET}"
zig cc main.c -o "example-${TARGET_ARCH}-static" -target "${BUILD_TARGET}" -static
zig cc main.c -o "example-${TARGET_ARCH}-dynamic" -target "${BUILD_TARGET}" -dynamic
for VARIANT in default static dynamic; do
inspect-bin "example-${TARGET_ARCH}-${VARIANT}" "${TARGET_ARCH}"
done
HEREDOC
COPY --chmod=755 <<"HEREDOC" /usr/local/bin/inspect-bin
#!/bin/bash
BIN=${1?must provide path to binary}
TARGET_ARCH="${2:-gnu}"
case "${TARGET_ARCH}" in
( 'gnu' ) export LDD='ldd' ;;
( 'musl' ) export LDD='musl-ldd' ;;
( '*' ) echo 'Unsupported ARCH'; return 1 ;;
esac
function _inspect() {
local BIN=${1}
echo "Inspecting '${BIN}'"
file "${BIN}"
"${LDD}" "${BIN}"
echo -e '\n'
}
_inspect $1
HEREDOC
COPY --chmod=755 <<"HEREDOC" /usr/local/bin/musl-ldd
#!/bin/sh
/usr/lib/ld-musl-x86_64.so.1 --list "${@}"
HEREDOC Prior to this PR, the current output from the container is: $ docker build --tag localhost/zig:master .
# Just run this and it'll build all variants for musl and glibc:
$ docker run --rm -it localhost/zig:master
Inspecting 'example'
example: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, with debug_info, not stripped
linux-vdso.so.1 (0x00007fbc0e34d000)
libc.so.6 => /lib64/libc.so.6 (0x00007fbc0e153000)
/lib64/ld-linux-x86-64.so.2 (0x00007fbc0e34f000)
Inspecting 'example-musl-default'
example-musl-default: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, with debug_info, not stripped
/usr/lib/ld-musl-x86_64.so.1: example-musl-default: Not a valid dynamic program
Inspecting 'example-musl-static'
example-musl-static: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, with debug_info, not stripped
/usr/lib/ld-musl-x86_64.so.1: example-musl-static: Not a valid dynamic program
Inspecting 'example-musl-dynamic'
example-musl-dynamic: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, with debug_info, not stripped
/usr/lib/ld-musl-x86_64.so.1: example-musl-dynamic: Not a valid dynamic program
Inspecting 'example-gnu-default'
example-gnu-default: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.0.0, with debug_info, not stripped
linux-vdso.so.1 (0x00007f417404c000)
libc.so.6 => /lib64/libc.so.6 (0x00007f4173e52000)
/lib64/ld-linux-x86-64.so.2 (0x00007f417404e000)
Inspecting 'example-gnu-static'
example-gnu-static: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.0.0, with debug_info, not stripped
linux-vdso.so.1 (0x00007f99e7de2000)
libc.so.6 => /lib64/libc.so.6 (0x00007f99e7be8000)
/lib64/ld-linux-x86-64.so.2 (0x00007f99e7de4000)
Inspecting 'example-gnu-dynamic'
example-gnu-dynamic: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.0.0, with debug_info, not stripped
linux-vdso.so.1 (0x00007f2601f2a000)
libc.so.6 => /lib64/libc.so.6 (0x00007f2601d30000)
/lib64/ld-linux-x86-64.so.2 (0x00007f2601f2c000) |
Making a separate comment just to highlight that slight difference from using
vs
Not sure if that's relevant, but something must be slightly different for that difference 😅 |
The release cadence is approximately every 8 hours, however the nightly CI has been broken for a few days: |
Aren't these opt-in? ( The breakage isn't a surprise elsewhere AFAIK when you choose non-default linking for a target? Why require linking inputs manually instead? Is Zig unable to locate system libraries available itself? I was hoping From the linked commit message:
For comparison with Rust, Rust doesn't provide static libc for the linux gnu target either, so when you do the equivalent of For musl, Rust provides files in it's toolchain to support that by the default but opt-in to dynamic linking likewise requires extra files as shown below. Perhaps Zig differs a little here, I remember when I tried static linking glibc, I had to explicitly provide two files, it wasn't able to locate them like Rust does when they're available. Ideally Reproduction (Rust equivalents for reference)Collapsed for brevity (Click to view)# Build both Fedora (glibc) and Alpine (musl) image variants:
docker build --tag localhost/example:glibc --build-arg WITH_LIBC=glibc .
docker build --tag localhost/example:musl --build-arg WITH_LIBC=musl . ARG WITH_LIBC=glibc
FROM fedora:42 AS rust-glibc
RUN <<HEREDOC
# NOTE: `glibc-static` would be needed for static linking of glibc:
dnf --setopt=install_weak_deps=0 install -yq file gcc nano rustup
rustup-init -y --profile minimal --default-toolchain stable --target x86_64-unknown-linux-musl
dnf clean all
HEREDOC
FROM alpine:3.21 AS rust-musl
RUN <<HEREDOC
# NOTE: `musl-dev` is needed for `Scrt1.o` + `crti.o` files when dynamic linking:
apk --no-cache add file gcc nano rustup
rustup-init -y --profile minimal --default-toolchain stable --target x86_64-unknown-linux-musl
HEREDOC
FROM rust-${WITH_LIBC}
ENV PATH="/root/.cargo/bin:$PATH"
WORKDIR /example
RUN cargo init . Rust musl dynamicFor dynamically linked musl, you would need to use # NOTE: The `example` volume is for transferring between glibc/musl containers
$ docker run --rm -it --volume example:/tmp/data localhost/example:glibc
$ Build with musl target and dynamic linking:
$ RUSTFLAGS='-C target-feature=-crt-static' cargo build --release --target x86_64-unknown-linux-musl
Compiling example v0.1.0 (/example)
Finished `release` profile [optimized] target(s) in 1.32s
# Links the same way as you'd get with glibc:
$ ldd target/x86_64-unknown-linux-musl/release/example
linux-vdso.so.1 (0x00007f726514a000)
libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007f72650bc000)
libc.so.6 => /lib64/libc.so.6 (0x00007f7264eca000)
/lib64/ld-linux-x86-64.so.2 (0x00007f726514c000)
# Interpreter is glibc:
$ file target/x86_64-unknown-linux-musl/release/example
target/x86_64-unknown-linux-musl/release/example: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=ed074b72ac8f274ca3f836c7383db58178d46d3c, for GNU/Linux 3.2.0, not stripped Resolving this will require $ dnf install -yq musl-libc patchelf
$ patchelf --set-interpreter /lib/ld-musl-x86_64.so.1 target/x86_64-unknown-linux-musl/release/example
# Corrected:
$ file target/x86_64-unknown-linux-musl/release/example
target/x86_64-unknown-linux-musl/release/example: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib/ld-musl-x86_64.so.1, BuildID[sha1]=ed074b72ac8f274ca3f836c7383db58178d46d3c, for GNU/Linux 3.2.0, not stripped
# The interpreter for some reason resolves to glibc dynamic loader 🤷♂️
$ ldd target/x86_64-unknown-linux-musl/release/example
linux-vdso.so.1 (0x00007f15e2b2f000)
libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007f15e2a9a000)
libc.so.6 => /lib64/libc.so.6 (0x00007f15e28a8000)
/lib/ld-musl-x86_64.so.1 => /lib64/ld-linux-x86-64.so.2 (0x00007f15e2b31000)
# This should be equivalent to `musl-ldd` but seems to be resolving for glibc?:
# NOTE: The musl dynamic loader is effectively checking for these as if you had
# run it with the ENV `LD_LIBRARY_PATH=/usr/x86_64-linux-musl/lib64`
$ /lib/ld-musl-x86_64.so.1 --list target/x86_64-unknown-linux-musl/release/example
/lib/ld-musl-x86_64.so.1 (0x7f6bbd8d3000)
Error loading shared library libgcc_s.so.1: No such file or directory (needed by target/x86_64-unknown-linux-musl/release/example)
libc.so.6 => /lib/ld-musl-x86_64.so.1 (0x7f6bbd8d3000)
Error loading shared library ld-linux-x86-64.so.2: No such file or directory (needed by target/x86_64-unknown-linux-musl/release/example)
Error relocating target/x86_64-unknown-linux-musl/release/example: _Unwind_Resume: symbol not found
Error relocating target/x86_64-unknown-linux-musl/release/example: _Unwind_Backtrace: symbol not found
Error relocating target/x86_64-unknown-linux-musl/release/example: _Unwind_GetRegionStart: symbol not found
Error relocating target/x86_64-unknown-linux-musl/release/example: _Unwind_GetTextRelBase: symbol not found
Error relocating target/x86_64-unknown-linux-musl/release/example: _Unwind_RaiseException: symbol not found
Error relocating target/x86_64-unknown-linux-musl/release/example: _Unwind_GetIPInfo: symbol not found
Error relocating target/x86_64-unknown-linux-musl/release/example: _Unwind_GetLanguageSpecificData: symbol not found
Error relocating target/x86_64-unknown-linux-musl/release/example: _Unwind_GetIP: symbol not found
Error relocating target/x86_64-unknown-linux-musl/release/example: _Unwind_GetDataRelBase: symbol not found
Error relocating target/x86_64-unknown-linux-musl/release/example: _Unwind_SetGR: symbol not found
Error relocating target/x86_64-unknown-linux-musl/release/example: _Unwind_DeleteException: symbol not found
Error relocating target/x86_64-unknown-linux-musl/release/example: _Unwind_SetIP: symbol not found Bring it over to Alpine: # Transfer to the volume to make accessible to musl container:
$ cp target/x86_64-unknown-linux-musl/release/example /tmp/data/example
$ exit
# Run a musl container:
$ docker run --rm -it --volume example:/tmp/data localhost/example:musl
# Glibc interpreter still linked for some reason 🤷♂️
$ ldd /tmp/data/example
/lib/ld-musl-x86_64.so.1 (0x7fe7076d0000)
libgcc_s.so.1 => /usr/lib/libgcc_s.so.1 (0x7fe707643000)
libc.so.6 => /lib/ld-musl-x86_64.so.1 (0x7fe7076d0000)
Error loading shared library ld-linux-x86-64.so.2: No such file or directory (needed by /tmp/data/example)
$ file /tmp/data/example
/tmp/data/example: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib/ld-musl-x86_64.so.1, BuildID[sha1]=ed074b72ac8f274ca3f836c7383db58178d46d3c, for GNU/Linux 3.2.0, not stripped
# Fix it by removing the invalid dependency:
$ apk add patchelf
$ patchelf --remove-needed ld-linux-x86-64.so.2 /tmp/data/example
# Now we have the expected interpreter `/lib/ld-musl-x86_64.so.1` working:
# NOTE: Alpine symlinks `/usr/lib/libc.so` to the interpreter.
$ ldd /tmp/data/example
/lib/ld-musl-x86_64.so.1 (0x7fbd087d0000)
libgcc_s.so.1 => /usr/lib/libgcc_s.so.1 (0x7fbd08743000)
libc.so.6 => /lib/ld-musl-x86_64.so.1 (0x7fbd087d0000)
$ /lib/ld-musl-x86_64.so.1 --list /tmp/data/example
/lib/ld-musl-x86_64.so.1 (0x7fbd087d0000)
libgcc_s.so.1 => /usr/lib/libgcc_s.so.1 (0x7fbd08743000)
libc.so.6 => /lib/ld-musl-x86_64.so.1 (0x7fbd087d0000)
# It works:
$ /tmp/data/example
Hello, world! Build it on Alpine instead$ docker run --rm -it localhost/example:musl
$ Build with musl target and dynamic linking:
$ RUSTFLAGS='-C target-feature=-crt-static' cargo build --release --target x86_64-unknown-linux-musl
Compiling example v0.1.0 (/example)
error: linking with `cc` failed: exit status: 1
|
= note: "cc" "-m64" "/tmp/rustciFelTn/symbols.o" "<2 object files omitted>" "-Wl,--as-needed" "-Wl,-Bstatic" "<sysroot>/lib/rustlib/x86_64-unknown-linux-musl/lib/{libstd-*,libpanic_unwind-*,libobject-*,libmemchr-*,libaddr2line-*,libgimli-*,librustc_demangle-*,libstd_detect-*,libhashbrown-*,librustc_std_workspace_alloc-*,libminiz_oxide-*,libadler2-*,libunwind-*,libcfg_if-*,liblibc-*,liballoc-*,librustc_std_workspace_core-*,libcore-*,libcompiler_builtins-*}.rlib" "-Wl,-Bdynamic" "-lgcc_s" "-lc" "-Wl,--eh-frame-hdr" "-Wl,-z,noexecstack" "-L" "<sysroot>/lib/rustlib/x86_64-unknown-linux-musl/lib" "-o" "/example/target/x86_64-unknown-linux-musl/release/deps/example-c9cd3c828926ce86" "-Wl,--gc-sections" "-pie" "-Wl,-z,relro,-z,now" "-Wl,-O1" "-Wl,--strip-debug" "-nodefaultlibs"
= note: some arguments are omitted. use `--verbose` to show all linker arguments
= note: /usr/lib/gcc/x86_64-alpine-linux-musl/14.2.0/../../../../x86_64-alpine-linux-musl/bin/ld: cannot find Scrt1.o: No such file or directory
/usr/lib/gcc/x86_64-alpine-linux-musl/14.2.0/../../../../x86_64-alpine-linux-musl/bin/ld: cannot find crti.o: No such file or directory
collect2: error: ld returned 1 exit status
error: could not compile `example` (bin "example") due to 1 previous error To avoid that failure, add $ RUSTFLAGS='-C target-feature=-crt-static' cargo build --release --target x86_64-unknown-linux-musl
Compiling example v0.1.0 (/example)
Finished `release` profile [optimized] target(s) in 0.31s
# NOTE: Rather than `libc.so` we have `libc.musl-x86_64.so.1` which is also a symlink to the musl interpreter.
$ ldd target/x86_64-unknown-linux-musl/release/example
/lib/ld-musl-x86_64.so.1 (0x7f74ab741000)
libgcc_s.so.1 => /usr/lib/libgcc_s.so.1 (0x7f74ab6b6000)
libc.musl-x86_64.so.1 => /lib/ld-musl-x86_64.so.1 (0x7f74ab741000) Rust glibc staticFor statically linked glibc, you would need to use $ docker run --rm -it localhost/example:glibc
$ RUSTFLAGS='-C target-feature=+crt-static' cargo build --release --target x86_64-unknown-linux-gnu
Compiling example v0.1.0 (/example)
error: linking with `cc` failed: exit status: 1
|
= note: "cc" "-m64" "/tmp/rustcEOi7A9/symbols.o" "<2 object files omitted>" "-Wl,--as-needed" "-Wl,-Bstatic" "<sysroot>/lib/rustlib/x86_64-unknown-linux-gnu/lib/{libstd-*,libpanic_unwind-*,libobject-*,libmemchr-*,libaddr2line-*,libgimli-*,librustc_demangle-*,libstd_detect-*,libhashbrown-*,librustc_std_workspace_alloc-*,libminiz_oxide-*,libadler2-*,libunwind-*,libcfg_if-*,liblibc-*}.rlib" "-lutil" "-lrt" "-lpthread" "-lm" "-ldl" "-lc" "-lgcc_eh" "-lgcc" "-lc" "<sysroot>/lib/rustlib/x86_64-unknown-linux-gnu/lib/{liballoc-*,librustc_std_workspace_core-*,libcore-*,libcompiler_builtins-*}.rlib" "-Wl,-Bdynamic" "-Wl,--eh-frame-hdr" "-Wl,-z,noexecstack" "-L" "<sysroot>/lib/rustlib/x86_64-unknown-linux-gnu/lib" "-o" "/example/target/x86_64-unknown-linux-gnu/release/deps/example-92a7e02a6848a45b" "-Wl,--gc-sections" "-static-pie" "-Wl,-z,relro,-z,now" "-Wl,-O1" "-Wl,--strip-debug" "-nodefaultlibs"
= note: some arguments are omitted. use `--verbose` to show all linker arguments
= note: /usr/sbin/ld: cannot find -lm: No such file or directory
/usr/sbin/ld: have you installed the static version of the m library ?
/usr/sbin/ld: cannot find -lc: No such file or directory
/usr/sbin/ld: have you installed the static version of the c library ?
/usr/sbin/ld: cannot find -lc: No such file or directory
/usr/sbin/ld: have you installed the static version of the c library ?
collect2: error: ld returned 1 exit status
error: could not compile `example` (bin "example") due to 1 previous error To avoid that failure, add
|
Yes, and zig-bootstrap builds LLVM statically. But LLVM conflates library and executable linkage, hence that patch being necessary.
I don't really understand this question. But I'm referring to this logic in the compiler: zig/src/Compilation/Config.zig Lines 343 to 380 in a843be4
|
TL;DR: Since Zig
Static glibc attempt
I linked to a comment for context, basically I have this:
#include <stdio.h>
int main()
{
printf("Hello World!\n");
} # Build static glibc:
zig cc main.c -static -target x86_64-linux-gnu -o example
error: libc of the specified target requires dynamic linking
# Ok... you said I just need to provide the link inputs instead so...:
$ zig cc main.c -static -target x86_64-linux-gnu -o example /usr/lib64/libc.a /usr/lib/gcc/x86_64-redhat-linux/15/libgcc_eh.a
error: libc of the specified target requires dynamic linking
# Ok... without `-static` flag then? (on Zig 0.14.0, this is also the output with `-static` flag):
$ zig cc main.c -target x86_64-linux-gnu -o example /usr/lib64/libc.a /usr/lib/gcc/x86_64-redhat-linux/15/libgcc_eh.a
ld.lld: error: undefined hidden symbol: _init
>>> referenced by libc-start.o:(__libc_start_main_impl) in archive /usr/lib64/libc.a
ld.lld: error: undefined hidden symbol: _fini
>>> referenced by libc-start.o:(call_fini) in archive /usr/lib64/libc.a It is perhaps an exception, but it will not allow me to build that basic "hello world" example statically with glibc, even though that should be possible? (it is possible with Rust at least when I ensure I have In Zig $ ./example
Segmentation fault (core dumped)
$ ldd example
statically linked
$ file example
example: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.0.0, with debug_info, not stripped, too many notes (256) Musl +
|
Adding support for static glibc was not a goal of this PR. That will require changes in other places, likely to do with which CRT objects need to be linked in: zig/lib/std/zig/LibCInstallation.zig Lines 753 to 778 in c0ec264
The check above that actually emits the error would also need to be tightened to only happen when using Zig-provided glibc. I'm not opposed to supporting the static glibc use case when using non-Zig-provided glibc, but that just isn't what this PR was about; it was about fixing the dynamic musl use case which was broken both for native and cross builds.
I would at least consider the new behavior strictly better since you don't get the false impression that it worked. |
That'd be fantastic! ❤️ UPDATE: This may have been handled by #23752 (comment) (I've not yet verified a Rust build with Eyra) That's also another failure I recall (possible two scenarios I encountered with something like Zig adding One of those failures I think was possibly with a Eyra "hello world" example (or maybe the I think when I tried to look into how to do this on Zig it was not obvious to me. I believe there was a different type of target that had to be used but I was unsuccessful figuring out what I needed to do. I remember I was attempting to compare how small of a "hello world" I could get with C / Zig without it being overly complicated. I had a 344 bytes hello world (456 bytes with text output) as the smallest result in Rust: #![no_std]
#![no_main]
#[no_mangle]
pub extern "C" fn _start() -> ! {
exit(); // +8 bytes to size vs using `loop() {}`
}
fn exit() -> ! { unsafe { rustix::runtime::exit_thread(42) } }
#[panic_handler]
fn panic(_info: &core::panic::PanicInfo) -> ! { loop {} } I don't seem to have the equivalent C / Zig code on me. Might have been Zig from some community member. Anyway, if you make any improvements on that front I'd be happy to reproduce the problems again and verify if |
Hopefully more correct take on #12894.
Before:
After:
Closes #11909.
Release Notes
zig cc
now properly respects the-static
and-dynamic
flags. Most notably, this allows statically linking native glibc, and dynamically linking cross-compiled musl.