Table of Contents
Here's how to update the libc files that Zig bundles.
glibc
Warning: Last time I went through this guide, I experienced a bug in glibc/scripts/build-many-glibcs.py that made it incorrectly mark compilers which had successfully finished building as "UNRESOLVED", which ruined everything. I worked around the issue by doing this process on Debian instead of on NixOS.
Make sure these dependencies are installed. If the scripts below fail, I'm not aware of a way to make them resume; each command deletes everything and starts over.
- python3
- subversion
- gawk
- autoconf
- automake
- libtool
- flex
- bison
- build-essential
- texinfo
Next, git clone glibc.
git clone git://sourceware.org/git/glibc.git
cd glibc
git checkout glibc-2.40 # the tag of the version to update to
Assuming the path of that is ~/glibc
, make a new directory and go to it. Then run the Python commands, each of which uses all CPU cores and takes a long time. If any of them fail, look at the logs to find out why, correct it, and then start the command again. Unfortunately each command will delete its own previous progress and start over.
mkdir multi
cd multi
python3 ~/glibc/scripts/build-many-glibcs.py . checkout
cd src/glibc
git checkout glibc-2.40 # the tag of the version to update to
cd -
python3 ~/glibc/scripts/build-many-glibcs.py . host-libraries # took 51s with 32 CPU cores
python3 ~/glibc/scripts/build-many-glibcs.py . compilers # took 2h11m with 64 CPU cores
python3 ~/glibc/scripts/build-many-glibcs.py . glibcs # took 2h with 64 CPU cores
Next, make sure that the list of architectures in tools/process_headers.zig
is complete in that it lists all of the glibc targets and maps them to Zig targets. Any additional targets you add, add to the available_libcs
global constant in lib/std/zig/target.zig
.
Next, from the "build" directory of zig git source, use tools/process_headers.zig
:
zig run ../tools/process_headers.zig -- --search-path ~/glibc/multi/install/glibcs --out hdrs --abi glibc
Inspect the hdrs
directory that the tool just created. If it looks good, then:
rm -rf $(find ../lib/libc/include/ -name "*linux-gnu*")
rm -rf ../lib/libc/include/generic-glibc
mv hdrs/* ../lib/libc/include/
Inspect git status
and make sure the changes look good. Make a commit with only the updated glibc headers in it.
Next, use glibc-abi-tool to update the lib/libc/glibc/abilists
file. The instructions for how to do that are in the README.
Finally, update the rest of the files in lib/libc/glibc/
besides abilists
, to match the new glibc version, using the tool:
./zig run ../tools/update_glibc.zig -- ~/src/glibc ..
You definitely need to inspect the full diff and look for patches that Zig has on top of these files and put them back in place.
If any new features were added to features.h they probably need to be gated by the glibc version with an #ifdef
.
If you keep your glibc build artifacts, you can use it with zig build test -fqemu --glibc-runtimes /foo/glibc/multi/install/glibcs
.
musl
git clone git://git.musl-libc.org/musl
git checkout v1.2.5 # the tag of the version to update to
rm -rf obj/ && make DESTDIR=build-all/aarch64 install-headers ARCH=aarch64 prefix=/usr/local/musl
rm -rf obj/ && make DESTDIR=build-all/arm install-headers ARCH=arm prefix=/usr/local/musl
rm -rf obj/ && make DESTDIR=build-all/i386 install-headers ARCH=i386 prefix=/usr/local/musl
rm -rf obj/ && make DESTDIR=build-all/loongarch64 install-headers ARCH=loongarch64 prefix=/usr/local/musl
rm -rf obj/ && make DESTDIR=build-all/m68k install-headers ARCH=m68k prefix=/usr/local/musl
rm -rf obj/ && make DESTDIR=build-all/mips install-headers ARCH=mips prefix=/usr/local/musl
rm -rf obj/ && make DESTDIR=build-all/mips64 install-headers ARCH=mips64 prefix=/usr/local/musl
rm -rf obj/ && make DESTDIR=build-all/mipsn32 install-headers ARCH=mipsn32 prefix=/usr/local/musl
rm -rf obj/ && make DESTDIR=build-all/powerpc install-headers ARCH=powerpc prefix=/usr/local/musl
rm -rf obj/ && make DESTDIR=build-all/powerpc64 install-headers ARCH=powerpc64 prefix=/usr/local/musl
rm -rf obj/ && make DESTDIR=build-all/riscv32 install-headers ARCH=riscv32 prefix=/usr/local/musl
rm -rf obj/ && make DESTDIR=build-all/riscv64 install-headers ARCH=riscv64 prefix=/usr/local/musl
rm -rf obj/ && make DESTDIR=build-all/s390x install-headers ARCH=s390x prefix=/usr/local/musl
rm -rf obj/ && make DESTDIR=build-all/x32 install-headers ARCH=x32 prefix=/usr/local/musl
rm -rf obj/ && make DESTDIR=build-all/x86_64 install-headers ARCH=x86_64 prefix=/usr/local/musl
Make sure the list of architectures in tools/process_headers.zig
is complete
in that it lists all of the musl targets which have corresponding Zig targets.
Any additional targets you add, add to the available_libcs
variable in
src/target.zig
.
Next, use tools/process_headers.zig
, with these parameters:
--abi musl
--search-path
parameter will be the path to thebuild-all
directory.--out
to a temporary directory if you want to inspect the output first, orlib/libc/include
if you are confident in the output.
To update musl source code:
cd lib/libc/musl
rm -rf arch crt compat src include
cp -r ~/src/musl/arch ./
cp -r ~/src/musl/crt ./
cp -r ~/src/musl/compat ./
cp -r ~/src/musl/src ./
cp -r ~/src/musl/include ./
cp ~/src/musl/COPYRIGHT .
Remove the non-supported architectures:
rm -rf arch/microblaze
rm -rf arch/or1k
rm -rf arch/sh
Next, look at a git status
and git diff
and make sure everything looks OK.
There are some chores to do below:
Update the contents of libc/musl/src/internal/version.h
to the correct musl
version number. This file will have been deleted by the above process and you
will need to create it now (look at the git diff
to see the contents).
Take note of the .mak
files. These will show up in the "Untracked files"
section, and should be deleted:
rm arch/arm/arch.mak
rm arch/i386/arch.mak
rm arch/m68k/arch.mak
rm arch/mips/arch.mak
rm arch/mipsn32/arch.mak
rm arch/powerpc/arch.mak
If there are any new ones not covered in this list, support needs to be added
in src/musl.zig
, where there is special handling for these. Look for
time32_compat_arch_list
.
Update src_files
in src/musl.zig
to be a complete list, e.g. with
find musl/src -type f -name "*.c" -o -iname "*.s"
. Similarly, update
compat_time32_files
, e.g. with find musl/compat/time32 -type f -name "*.c" -o -iname "*.s"
. Lexically sort the resulting lists in src/musl.zig
to keep
the diff clean.
If musl added any new architectures, add them to musl_arch_names
in
src/musl.zig
. These can be found by ls arch/
in the musl source directory.
Updating the libc.S file
lib/libc/musl/libc.S
contains stubs for all the dynamic symbols of musl's libc.so
. It is generated from tools/gen_stubs.zig
.
First, apply this patch to musl's build system:
diff --git a/Makefile b/Makefile
index 3ad88b35..e1c6e55e 100644
--- a/Makefile
+++ b/Makefile
@@ -40,11 +40,11 @@ IMPH = $(addprefix $(srcdir)/, src/internal/stdio_impl.h src/internal/pthread_im
LDFLAGS =
LDFLAGS_AUTO =
-LIBCC = -lgcc
+#LIBCC = -lgcc
CPPFLAGS =
CFLAGS =
CFLAGS_AUTO = -Os -pipe
diff --git a/configure b/configure
index bc9fbe48..fe86ba5c 100755
--- a/configure
+++ b/configure
@@ -600,7 +600,7 @@ tryldflag LDFLAGS_AUTO -Wl,--hash-style=both
# libc.so will crash at runtime during relocation processing.
# The common way this can happen is failure to link the compiler
# runtime library; implementation error is also a possibility.
-tryldflag LDFLAGS_AUTO -Wl,--no-undefined
+#tryldflag LDFLAGS_AUTO -Wl,--no-undefined
# Avoid exporting symbols from compiler runtime libraries. They
# should be hidden anyway, but some toolchains including old gcc
@@ -615,13 +615,13 @@ tryldflag LDFLAGS_AUTO -Wl,--exclude-libs=ALL
tryldflag LDFLAGS_AUTO -Wl,--dynamic-list="$srcdir/dynamic.list"
# Find compiler runtime library
-test -z "$LIBCC" && tryldflag LIBCC -lgcc && tryldflag LIBCC -lgcc_eh
-test -z "$LIBCC" && tryldflag LIBCC -lcompiler_rt
-test -z "$LIBCC" && try_libcc=`$CC -print-libgcc-file-name 2>/dev/null` \
- && tryldflag LIBCC "$try_libcc"
-test -z "$LIBCC" && try_libcc=`$CC -print-file-name=libpcc.a 2>/dev/null` \
- && tryldflag LIBCC "$try_libcc"
-printf "using compiler runtime libraries: %s\n" "$LIBCC"
+#test -z "$LIBCC" && tryldflag LIBCC -lgcc && tryldflag LIBCC -lgcc_eh
+#test -z "$LIBCC" && tryldflag LIBCC -lcompiler_rt
+#test -z "$LIBCC" && try_libcc=`$CC -print-libgcc-file-name 2>/dev/null` \
+# && tryldflag LIBCC "$try_libcc"
+#test -z "$LIBCC" && try_libcc=`$CC -print-file-name=libpcc.a 2>/dev/null` \
+# && tryldflag LIBCC "$try_libcc"
+#printf "using compiler runtime libraries: %s\n" "$LIBCC"
# Figure out arch variants for archs with variants
SUBARCH=
This ensures that musl can build with -rtlib=none
, despite the fact that this results in undefined symbols that would normally be fulfilled by compiler-rt. (We don't want those symbols showing up as defined in the resulting libc.so
.)
Next, cross-compile musl for all the supported architectures:
mkdir -p build-all
echo -e '#!/bin/sh\nzig cc -target aarch64-linux-musl $@' > $(pwd)/build-all/zcc-aarch64
echo -e '#!/bin/sh\nzig cc -target arm-linux-musleabi $@' > $(pwd)/build-all/zcc-arm
echo -e '#!/bin/sh\nzig cc -target x86-linux-musl $@' > $(pwd)/build-all/zcc-i386
echo -e '#!/bin/sh\nzig cc -target loongarch64-linux-musl $@' > $(pwd)/build-all/zcc-loongarch64
echo -e '#!/bin/sh\nzig cc -target mips-linux-musleabi $@' > $(pwd)/build-all/zcc-mips
echo -e '#!/bin/sh\nzig cc -target mips64-linux-muslabi64 $@' > $(pwd)/build-all/zcc-mips64
echo -e '#!/bin/sh\nzig cc -target mips64-linux-muslabin32 $@' > $(pwd)/build-all/zcc-mipsn32
echo -e '#!/bin/sh\nzig cc -target powerpc-linux-musleabi $@' > $(pwd)/build-all/zcc-powerpc
echo -e '#!/bin/sh\nzig cc -target powerpc64-linux-musl $@' > $(pwd)/build-all/zcc-powerpc64
echo -e '#!/bin/sh\nzig cc -target riscv32-linux-musl $@' > $(pwd)/build-all/zcc-riscv32
echo -e '#!/bin/sh\nzig cc -target riscv64-linux-musl $@' > $(pwd)/build-all/zcc-riscv64
echo -e '#!/bin/sh\nzig cc -target s390x-linux-musl $@' > $(pwd)/build-all/zcc-s390x
echo -e '#!/bin/sh\nzig cc -target x86_64-linux-muslx32 $@' > $(pwd)/build-all/zcc-x32
echo -e '#!/bin/sh\nzig cc -target x86_64-linux-musl $@' > $(pwd)/build-all/zcc-x86_64
chmod +x $(pwd)/build-all/zcc-*
export PATH=$(pwd)/build-all:$PATH
make distclean && \
./configure --prefix=$(pwd)/build-all/aarch64 --target=aarch64-linux-musl --disable-static AR="zig ar" CC="zcc-aarch64" RANLIB="zig ranlib" CFLAGS="-rtlib=none" && \
make -j$(nproc) && \
make install
make distclean && \
./configure --prefix=$(pwd)/build-all/arm --target=arm-linux-musleabi --disable-static AR="zig ar" CC="zcc-arm" RANLIB="zig ranlib" CFLAGS="-rtlib=none" && \
make -j$(nproc) && \
make install
make distclean && \
./configure --prefix=$(pwd)/build-all/i386 --target=i386-linux-musl --disable-static AR="zig ar" CC="zcc-i386" RANLIB="zig ranlib" CFLAGS="-rtlib=none" && \
make -j$(nproc) && \
make install
make distclean && \
./configure --prefix=$(pwd)/build-all/loongarch64 --target=loongarch64-linux-musl --disable-static AR="zig ar" CC="zcc-loongarch64" RANLIB="zig ranlib" CFLAGS="-rtlib=none" && \
make -j$(nproc) && \
make install
make distclean && \
./configure --prefix=$(pwd)/build-all/mips --target=mips-linux-musleabi --disable-static AR="zig ar" CC="zcc-mips" RANLIB="zig ranlib" CFLAGS="-rtlib=none" && \
make -j$(nproc) && \
make install
make distclean && \
./configure --prefix=$(pwd)/build-all/mips64 --target=mips64-linux-muslabi64 --disable-static AR="zig ar" CC="zcc-mips64" RANLIB="zig ranlib" CFLAGS="-rtlib=none" && \
make -j$(nproc) && \
make install
make distclean && \
./configure --prefix=$(pwd)/build-all/mipsn32 --target=mips64-linux-muslabin32 --disable-static AR="zig ar" CC="zcc-mipsn32" RANLIB="zig ranlib" CFLAGS="-rtlib=none" && \
make -j$(nproc) && \
make install
make distclean && \
./configure --prefix=$(pwd)/build-all/powerpc --target=powerpc-linux-musleabi --disable-static AR="zig ar" CC="zcc-powerpc" RANLIB="zig ranlib" CFLAGS="-rtlib=none" && \
make -j$(nproc) && \
make install
make distclean && \
./configure --prefix=$(pwd)/build-all/powerpc64 --target=powerpc64-linux-musl --disable-static AR="zig ar" CC="zcc-powerpc64" RANLIB="zig ranlib" CFLAGS="-rtlib=none" && \
make -j$(nproc) && \
make install
make distclean && \
./configure --prefix=$(pwd)/build-all/riscv32 --target=riscv32-linux-musl --disable-static AR="zig ar" CC="zcc-riscv32" RANLIB="zig ranlib" CFLAGS="-rtlib=none" && \
make -j$(nproc) && \
make install
make distclean && \
./configure --prefix=$(pwd)/build-all/riscv64 --target=riscv64-linux-musl --disable-static AR="zig ar" CC="zcc-riscv64" RANLIB="zig ranlib" CFLAGS="-rtlib=none" && \
make -j$(nproc) && \
make install
make distclean && \
./configure --prefix=$(pwd)/build-all/s390x --target=s390x-linux-musl --disable-static AR="zig ar" CC="zcc-s390x" RANLIB="zig ranlib" CFLAGS="-rtlib=none" && \
make -j$(nproc) && \
make install
# x32 is currently broken due to relocation errors.
make distclean && \
./configure --prefix=$(pwd)/build-all/x32 --target=x86_64-linux-muslx32 --disable-static AR="zig ar" CC="zcc-x32" RANLIB="zig ranlib" CFLAGS="-rtlib=none" && \
make -j$(nproc) && \
make install
make distclean && \
./configure --prefix=$(pwd)/build-all/x86_64 --target=x86_64-linux-musl --disable-static AR="zig ar" CC="zcc-x86_64" RANLIB="zig ranlib" CFLAGS="-rtlib=none" && \
make -j$(nproc) && \
make install
Some of these didn't work last time I tried them, as you can see with the
comments that contain error messages. These are bugs that should be fixed in
zig or in clang. However, fixing those bugs is a separate issue that won't
block the musl upgrade process. If any of them start working, then add the
newly working architecture to the arches
global variable in
tools/gen_stubs.zig
.
Anyway you should now have libc.so
built for multiple architectures:
andy@ark ~/src/musl ((v1.2.5))> find -name "libc.so"
./build-all/aarch64/lib/libc.so
./build-all/arm/lib/libc.so
./build-all/i386/lib/libc.so
./build-all/loongarch64/lib/libc.so
./build-all/mips/lib/libc.so
./build-all/mips64/lib/libc.so
./build-all/mipsn32/lib/libc.so
./build-all/powerpc/lib/libc.so
./build-all/powerpc64/lib/libc.so
./build-all/riscv32/lib/libc.so
./build-all/riscv64/lib/libc.so
./build-all/s390x/lib/libc.so
./build-all/x86_64/lib/libc.so
From the root of the zig source repository:
zig run tools/gen_stubs.zig -- ~/src/musl/build-all >lib/libc/musl/libc.S
Pay attention to the stderr output of this command. It may reveal an issue has occurred that will require you to massage the data by editing gen_stubs.zig.
Check the git diff
to make sure everything seems OK. In particular, look for wrongly included compiler-rt symbols that should be added to the blacklist in tools/gen_stubs.zig
.
To verify that the stub libc.so
matches the "real" musl libc.so
first build a zig hello world that dynamically links musl:
zig build-exe -lc -dynamic -target x86_64-linux-musl hello.zig --verbose-link
Copy the path to the stub libc.so
in the global cache from the link command logged and then run the following commands (with the proper paths):
objdump --dynamic-syms /home/ifreund/.cache/zig/o/94cd8ea1da001f912f2f7d259446424d/libc.so | sed -E -e 's/[0-9a-f]{16}//g' | sort > stubs.txt
objdump --dynamic-syms /path/to/musl/lib/libc.so | sed -E -e 's/[0-9a-f]{16}//g' | sort > musl.txt
diff musl.txt stubs.txt
If all is well, the only output of the diff command should be the line containing the filename. For example:
1702c1702
< ../musl/lib/libc.so: file format elf64-x86-64
---
> /home/ifreund/.cache/zig/o//libc.so: file format elf64-x86-64
Linux
Install rsync
and build-essential
.
Clone the linux stable tree (note that it is different from the torvalds tree):
git clone https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
... and checkout the latest stable patch release. Stable kernel version is prominently displayed in the front-page of kernel.org. Run the following commands:
make SHELL=/bin/sh ARCH=alpha INSTALL_HDR_PATH=dest/alpha headers_install
make SHELL=/bin/sh ARCH=arc INSTALL_HDR_PATH=dest/arc headers_install
make SHELL=/bin/sh ARCH=arm INSTALL_HDR_PATH=dest/arm headers_install
make SHELL=/bin/sh ARCH=arm64 INSTALL_HDR_PATH=dest/arm64 headers_install
make SHELL=/bin/sh ARCH=csky INSTALL_HDR_PATH=dest/csky headers_install
make SHELL=/bin/sh ARCH=hexagon INSTALL_HDR_PATH=dest/hexagon headers_install
make SHELL=/bin/sh ARCH=loongarch INSTALL_HDR_PATH=dest/loongarch headers_install
make SHELL=/bin/sh ARCH=m68k INSTALL_HDR_PATH=dest/m68k headers_install
make SHELL=/bin/sh ARCH=microblaze INSTALL_HDR_PATH=dest/microblaze headers_install
make SHELL=/bin/sh ARCH=mips INSTALL_HDR_PATH=dest/mips headers_install
make SHELL=/bin/sh ARCH=nios2 INSTALL_HDR_PATH=dest/nios2 headers_install
make SHELL=/bin/sh ARCH=openrisc INSTALL_HDR_PATH=dest/openrisc headers_install
make SHELL=/bin/sh ARCH=parisc INSTALL_HDR_PATH=dest/parisc headers_install
make SHELL=/bin/sh ARCH=powerpc INSTALL_HDR_PATH=dest/powerpc headers_install
make SHELL=/bin/sh ARCH=riscv INSTALL_HDR_PATH=dest/riscv headers_install
make SHELL=/bin/sh ARCH=s390 INSTALL_HDR_PATH=dest/s390 headers_install
make SHELL=/bin/sh ARCH=sh INSTALL_HDR_PATH=dest/sh headers_install
make SHELL=/bin/sh ARCH=sparc INSTALL_HDR_PATH=dest/sparc headers_install
make SHELL=/bin/sh ARCH=x86 INSTALL_HDR_PATH=dest/x86 headers_install
make SHELL=/bin/sh ARCH=xtensa INSTALL_HDR_PATH=dest/xtensa headers_install
Eyeball the linux_targets
variable inside tools/update-linux-headers.zig
.
rm -rf lib/libc/include/*-linux-any
zig run tools/update-linux-headers.zig -- --search-path ~/Downloads/linux/dest --out lib/libc/include
freebsd
TODO
openbsd
TODO
netbsd
TODO
macOS
Follow the directions on the README of ziglang/fetch-them-macos-headers.
The "fetch" command has to be run natively on 6 different macOS computers:
- x86_64-macos-11.x.x (Big Sur)
- x86_64-macos-12.x.x (Monterey)
- x86_64-macos-13.x.x (Ventura)
- aarch64-macos-11.x.x (Big Sur)
- aarch64-macos-12.x.x (Monterey)
- aarch64-macos-13.x.x (Ventura)
Be sure to run all system updates (other than upgrading to the next major OS version) before running the "fetch" command.
After each fetch command, commit the changes to the repository. Finally, after all fetches have been completed, run the "generate" command, using the destination path of the Zig source repository.
mingw-w64 (Windows)
The process-headers tool is not needed for these headers because mingw-w64 headers are already multi-architecture.
git clone git://git.code.sf.net/p/mingw-w64/mingw-w64
Check out the latest master branch commit.
ZIGSRC=$HOME/Downloads/zig
rm -rf $ZIGSRC/lib/libc/include/any-windows-any /tmp/prefix
cd mingw-w64-headers
./configure --prefix=/tmp/prefix --with-default-msvcrt=ucrt
make install
mv /tmp/prefix/include $ZIGSRC/lib/libc/include/any-windows-any
Next, navigate back to your Zig checkout and update the CRT files:
zig build update-mingw -Dmingw-src=$HOME/Downloads/mingw-w64
wasi-libc
Fetch the wasi-libc
repository:
git clone https://github.com/WebAssembly/wasi-libc
The src/wasi_libc.zig
contains the code to build it from Zig. It should mirror the wasi-libc
Makefile
. The order of include directories is especially important.
So, start by inspecting the Makefile
's history and update wasi_libc.zig
accordingly.
Also check for breaking changes in libc-bottom-half/crt
.
Then, copy the content of wasi-libc
's libc-bottom-half/headers/public/wasi
into lib/zig/libc/include/wasm-wasi-musl/wasi
.
Next, sync the content of the following directories into lib/libc/wasi
:
emmalloc
libc-bottom-half
libc-top-half
The libc-bottom-half/headers/public
subdirectory should ignored, as its content was already copied to lib/zig/libc/include/wasm-wasi-musl/wasi
.
Finally, run the usual test suite and watch for regressions.
By the way, wasi-libc
can be compiled with zig cc
(make sure that Zig's wasm-wasi-musl/wasi
directory is up to date beforehand):
env AR="zig ar" CC="zig cc -target wasm32-freestanding" make