Source

FROM stagex/bootstrap-stage3 AS build
ARG TARGETARCH
ARG VERSION

ADD fetch/musl-${VERSION}.tar.gz .
COPY --from=stagex/core-mimalloc / /

WORKDIR /musl-${VERSION}
ADD *.patch .
RUN --network=none <<-EOF
	set -eux
	ARCH=$(uname -m)
	export CFLAGS="-Wformat -Werror=format-security -fstack-protector-strong -fstack-clash-protection"
	export LDFLAGS="-Wl,--as-needed,-O1,--sort-common -Wl,-soname,libc.musl-${ARCH}.so.1"

	patch -p1 < CVE-2025-26519.patch

	# Double the amount of TLS keys from 128 (default) to 256 (default in `glibc`)
	#
	# When `mrustc` builds Rust 1.90.0, the `mrustc`-built `rustc` panics when ran due to running out
	# of TLS keys. Unfortunately, that can't reasonably be fixed downstream in the `core-rust` package
	# because this limit is defined in `libc` and compiled into the `libc` artifacts. Rust 1.90.0 also
	# had issues with Termux's packaging for Android, their solution being to emulate TLS instead of
	# using the `libc`'s, adding evidence of `rustc` using _more and more_ TLS keys. A `rustc`-built
	# `rustc 1.90.0` didn't have these issues within StageX however, suggesting while `rustc` uses
	# many TLS keys, it's `mrustc`'s compilation of it adding the overhead which is our killer blow.
	#
	# This should be acceptable here as a patch for greater compatibility with various
	# programs/packages even if ideally, `core-rust` sorted this out on its end and this was removed
	# so `core-musl` could be more stock. Note this does increase the size of a static allocation, so
	# this does have a performance penalty. It shouldn't be unacceptable though as the limit in
	# `glibc` is 1024. `musl` is generally much more conservative regarding thread overhead though.
	sed -i -E s/"PTHREAD_KEYS_MAX[ ]+128"/"PTHREAD_KEYS_MAX 256"/ include/limits.h

	# Remove the existing allocator
	rm -rf src/malloc
	rm src/legacy/valloc.c
	rm src/string/strdup.c
	rm src/string/strndup.c
	# Stub the `mallocng` folder so `musl` doesn't realize it's been removed
	mkdir -p src/malloc/mallocng

	# Stub the APIs `musl` requires but `mimalloc` doesn't provide
	# These are constants which trigger some conditional branches re: assumptions made
	echo "int __malloc_replaced = 1;" >> src/malloc/mallocng/stub.c
	echo "int __aligned_alloc_replaced = 1;" >> src/malloc/mallocng/stub.c
	# This allows a `mmap`'d page to be given to the allocator for use, which isn't possible with `mimalloc`
	# TODO: Should we do _something_ with these pointers, like unmap the page?
	echo "void __malloc_donate(char *a, char *b) {}" >> src/malloc/mallocng/stub.c

	# Link in `mimalloc`
	MIMALLOC_O=$(find /usr/lib -name mimalloc-secure.o)
	# Strip the `mimalloc` files linked in to only what's required and what we're replacing in `musl`
	KEEP="malloc __libc_malloc malloc_usable_size"
	KEEP="$KEEP calloc __libc_calloc"
	KEEP="$KEEP realloc __libc_realloc"
	KEEP="$KEEP free __libc_free"
	KEEP="$KEEP aligned_alloc memalign posix_memalign"
	KEEP="$KEEP reallocarray"
	KEEP="$KEEP valloc strdup strndup"
	for symbol in $(nm -f just-symbols $MIMALLOC_O); do
		EMPTY_IF_NOT_PRESENT=$(echo " $KEEP " | grep " $symbol " || true)
		if [ "$EMPTY_IF_NOT_PRESENT" = "" ]; then
			# This `strip` may not work if this symbol is still relied upon in a relocation
			strip --strip-symbol="$symbol" $MIMALLOC_O || true
		fi
	done
	# For whatever reason, this adds `mimalloc`'s object file only to the shared object...
	export LDFLAGS="$LDFLAGS -Wl,--push-state,--whole-archive,$MIMALLOC_O,--pop-state"

	./configure \
		--prefix=/usr \
		--sysconfdir=/etc \
		--mandir=/usr/share/man \
		--infodir=/usr/share/info \
		--localstatedir=/var \
		--enable-optimize
	make -j "$(nproc)"

	make DESTDIR=/rootfs install
	mkdir -p /rootfs/usr/bin /rootfs/usr/lib
	rm -rf /rootfs/lib
	ln -sf /usr/lib/ld-musl-${ARCH}.so.1 /rootfs/usr/bin/ldd
	mv -f /rootfs/usr/lib/libc.so /rootfs/usr/lib/ld-musl-${ARCH}.so.1
	ln -sf ld-musl-${ARCH}.so.1 /rootfs/usr/lib/libc.musl-${ARCH}.so.1
	ln -sf /usr/lib/ld-musl-${ARCH}.so.1 /rootfs/usr/lib/libc.so

	# Manually add `mimalloc`'s static archive to the static `libc`
	ar r /rootfs/usr/lib/libc.a $MIMALLOC_O

	# `mimalloc` also depends on `__popcountdi2` from `libgcc` or `compiler-rt`.
	# Frustratingly, `mimalloc` only does this if it detects GCC and assumes it can rely on it.
	# It doesn't allow disabling it for those who use GCC but don't want to require `libgcc`.
	# Thankfully, that also means this block of code should disappear if built by LLVM.
	mkdir c_runtime
	cd c_runtime
	ar x $(find /usr/lib -type f -name libgcc.a)
	for file in $(ls); do
		if [ ! "$(nm -f just-symbols $file | grep __popcountdi2)" = "" ]; then
			ar r /rootfs/usr/lib/libc.a $file
			break
		fi
	done
EOF
FROM stagex/core-filesystem AS package
COPY --from=build /rootfs/ /
Copied to clipboard!