Skip to content

Commit 50ae119

Browse files
authored
Refactor how compiler-rt is managed in CI and locally (#591)
Building the PIC version of libc.a is currently broken with LLVM 19+, and it's due to how this repository handles the compiler-rt library. This PR refactors the handling to automatically download an appropriate copy and handle it internally or otherwise use a `BUILTINS_LIB` from the environment. This means that it's now always available for `libc.so` which is needed to fix the build issue with LLVM 19+
1 parent 88e4427 commit 50ae119

File tree

5 files changed

+103
-40
lines changed

5 files changed

+103
-40
lines changed

.github/actions/setup/action.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ runs:
5353
set -ex
5454
v=${{ inputs.clang_version }}
5555
sudo apt-get update
56-
sudo apt-get install -y clang-$v clang-tools-$v
56+
sudo apt-get install -y clang-$v clang-tools-$v lld-$v
5757
echo "CC=clang-$v" >> $GITHUB_ENV
5858
echo "AR=llvm-ar-$v" >> $GITHUB_ENV
5959
echo "NM=llvm-nm-$v" >> $GITHUB_ENV

.github/workflows/main.yml

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,25 @@ jobs:
3838
clang_version: 16.0.0
3939
upload: windows-clang-16
4040

41-
# Historical versions of LLVM (11.0.0 currently)
41+
# Other versions of LLVM
4242
- name: Build with LLVM 11
4343
os: ubuntu-22.04
4444
clang_version: 11
4545
upload: linux-x86_64-clang-11
4646
env:
4747
BUILD_LIBSETJMP: no
48+
- name: Build with LLVM 19
49+
os: ubuntu-24.04
50+
clang_version: 19
51+
upload: linux-x86_64-clang-19
52+
env:
53+
MAKE_TARGETS: "default libc_so"
54+
- name: Build with LLVM 18
55+
os: ubuntu-24.04
56+
clang_version: 18
57+
upload: linux-x86_64-clang-18
58+
env:
59+
MAKE_TARGETS: "default libc_so"
4860

4961
# Test various combinations of targets triples.
5062
#
@@ -145,21 +157,7 @@ jobs:
145157
146158
- name: Test
147159
if: matrix.test
148-
# For Clang linking to work correctly, we need to place Clang's runtime
149-
# library for `wasm32-wasi` in the right location (i.e., the `mkdir` and
150-
# `cp` below).
151-
run: |
152-
cd test
153-
mkdir resource-dir
154-
export WASI_DIR=./resource-dir/lib/wasi/
155-
export WASIP1_DIR=./resource-dir/lib/wasip1/
156-
export WASIP2_DIR=./resource-dir/lib/wasip2/
157-
mkdir -p $WASI_DIR $WASIP1_DIR $WASIP2_DIR
158-
cp build/download/libclang_rt.builtins-wasm32.a $WASI_DIR
159-
cp build/download/libclang_rt.builtins-wasm32.a $WASIP1_DIR
160-
cp build/download/libclang_rt.builtins-wasm32.a $WASIP2_DIR
161-
export LDFLAGS="-resource-dir $(pwd)/resource-dir"
162-
make test
160+
run: make -C test test
163161

164162
- uses: actions/[email protected]
165163
if: matrix.upload

Makefile

Lines changed: 59 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -556,11 +556,48 @@ PIC_OBJS = \
556556
$(LIBC_BOTTOM_HALF_CRT_OBJS) \
557557
$(FTS_SO_OBJS)
558558

559+
# Figure out what to do about compiler-rt.
560+
#
561+
# The compiler-rt library is not built here in the wasi-libc repository, but it
562+
# is required to link artifacts. Notably `libc.so` and test and such all require
563+
# it to exist. Currently the ways this is handled are:
564+
#
565+
# * If `BUILTINS_LIB` is defined at build time then that's assumed to be a path
566+
# to the libcompiler-rt.a. That's then ingested into the build here and copied
567+
# around to special locations to get the `*.so` rules below to work (see docs
568+
# there).
569+
#
570+
# * If `BUILTINS_LIB` is not defined then a known-good copy is downloaded from
571+
# wasi-sdk CI and used instead.
572+
#
573+
# In the future this may also want some form of configuration to support
574+
# assuming the system compiler has a compiler-rt, e.g. if $(SYSTEM_BUILTINS_LIB)
575+
# exists that should be used instead.
559576
SYSTEM_BUILTINS_LIB := $(shell ${CC} ${CFLAGS} --print-libgcc-file-name)
560577
SYSTEM_RESOURCE_DIR := $(shell ${CC} ${CFLAGS} -print-resource-dir)
561578
BUILTINS_LIB_REL := $(subst $(SYSTEM_RESOURCE_DIR),,$(SYSTEM_BUILTINS_LIB))
562-
RESOURCE_DIR := $(OBJDIR)/resource-dir
563-
BUILTINS_LIB ?= $(RESOURCE_DIR)/$(BUILTINS_LIB_REL)
579+
TMP_RESOURCE_DIR := $(OBJDIR)/resource-dir
580+
BUILTINS_LIB_PATH := $(TMP_RESOURCE_DIR)/$(BUILTINS_LIB_REL)
581+
BUILTINS_LIB_DIR := $(dir $(BUILTINS_LIB_PATH))
582+
583+
ifneq ($(BUILTINS_LIB),)
584+
$(BUILTINS_LIB_PATH): $(BUILTINS_LIB)
585+
mkdir -p $(BUILTINS_LIB_DIR)
586+
cp $(BUILTINS_LIB) $(BUILTINS_LIB_PATH)
587+
else
588+
589+
BUILTINS_URL := https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-25/libclang_rt.builtins-wasm32-wasi-25.0.tar.gz
590+
591+
$(BUILTINS_LIB_PATH):
592+
mkdir -p $(BUILTINS_LIB_DIR)
593+
curl -sSfL $(BUILTINS_URL) | \
594+
tar xzf - -C $(BUILTINS_LIB_DIR) --strip-components 1
595+
if [ ! -f $(BUILTINS_LIB_PATH) ]; then \
596+
mv $(BUILTINS_LIB_DIR)/*.a $(BUILTINS_LIB_PATH); \
597+
fi
598+
endif
599+
600+
builtins: $(BUILTINS_LIB_PATH)
564601

565602
# TODO: Specify SDK version, e.g. libc.so.wasi-sdk-21, as SO_NAME once `wasm-ld`
566603
# supports it.
@@ -570,22 +607,34 @@ BUILTINS_LIB ?= $(RESOURCE_DIR)/$(BUILTINS_LIB_REL)
570607
# to CC. This is a workaround for a Windows command line size limitation. See
571608
# the `%.a` rule below for details.
572609

573-
# Note: libc.so is special because it shouldn't link to libc.so.
610+
# Note: libc.so is special because it shouldn't link to libc.so, and the
611+
# -nodefaultlibs flag here disables the default `-lc` logic that clang
612+
# has. Note though that this also disables linking of compiler-rt
613+
# libraries so that is explicitly passed in via `$(BUILTINS_LIB_PATH)`
614+
#
574615
# Note: --allow-undefined-file=linker-provided-symbols.txt is
575616
# a workaround for https://github.com/llvm/llvm-project/issues/103592
576-
$(SYSROOT_LIB)/libc.so: $(OBJDIR)/libc.so.a $(BUILTINS_LIB)
577-
$(CC) $(EXTRA_CFLAGS) --target=${TARGET_TRIPLE} -nodefaultlibs \
617+
$(SYSROOT_LIB)/libc.so: $(OBJDIR)/libc.so.a $(BUILTINS_LIB_PATH)
618+
$(CC) --target=${TARGET_TRIPLE} -nodefaultlibs \
578619
-shared --sysroot=$(SYSROOT) \
579-
-o $@ -Wl,--whole-archive $< -Wl,--no-whole-archive $(BUILTINS_LIB) \
620+
-o $@ -Wl,--whole-archive $< -Wl,--no-whole-archive \
580621
-Wl,--allow-undefined-file=linker-provided-symbols.txt \
581-
-resource-dir $(RESOURCE_DIR)
582-
622+
$(BUILTINS_LIB_PATH) \
623+
$(EXTRA_CFLAGS) $(LDFLAGS)
624+
625+
# Note that unlike `libc.so` above this rule does not pass `-nodefaultlibs`
626+
# which means that libc will be linked by default. Additionally clang will try
627+
# to find, locate, and link compiler-rt. To get compiler-rt to work a
628+
# `-resource-dir` argument is passed to ensure that our custom
629+
# `TMP_RESOURCE_DIR` built here locally is used instead of the system directory
630+
# which may or may not already have compiler-rt.
583631
$(SYSROOT_LIB)/%.so: $(OBJDIR)/%.so.a $(SYSROOT_LIB)/libc.so
584-
$(CC) $(EXTRA_CFLAGS) --target=${TARGET_TRIPLE} \
632+
$(CC) --target=${TARGET_TRIPLE} \
585633
-shared --sysroot=$(SYSROOT) \
586634
-o $@ -Wl,--whole-archive $< -Wl,--no-whole-archive \
587635
-Wl,--allow-undefined-file=linker-provided-symbols.txt \
588-
-resource-dir $(RESOURCE_DIR)
636+
-resource-dir $(TMP_RESOURCE_DIR) \
637+
$(EXTRA_CFLAGS) $(LDFLAGS)
589638

590639
$(OBJDIR)/libc.so.a: $(LIBC_SO_OBJS) $(MUSL_PRINTSCAN_LONG_DOUBLE_SO_OBJS)
591640

README.md

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,25 @@ To use the sysroot, use the `--sysroot=` option:
4040

4141
to run the compiler using the newly built sysroot.
4242

43+
## Other default libraries
44+
4345
Note that Clang packages typically don't include cross-compiled builds of
4446
compiler-rt, libcxx, or libcxxabi, for `libclang_rt.builtins-wasm32.a`,
45-
`libc++.a`, or `libc++abi.a`, respectively, so they may not be usable without
46-
extra setup. This is one of the things [wasi-sdk] simplifies, as it includes
47-
cross-compiled builds of compiler-rt, `libc++.a`, and `libc++abi.a`.
47+
`libc++.a`, or `libc++abi.a`, respectively. This means that by default just
48+
passing `--sysroot` may not be enough as Clang will also look for some of these
49+
libraries, notably `libclang_rt.builtins-wasm32.a`, by default. This is one of
50+
the things [wasi-sdk] simplifies, as it includes cross-compiled builds of
51+
compiler-rt, `libc++.a`, and `libc++abi.a`.
52+
53+
The build of wasi-libc itself may depend on compiler-rt, or
54+
`libclang_rt.builtins-wasm32.a`. This manifests in error messages such as
55+
`undefined symbol: __muloti4` for example. Specifically the PIC build of
56+
wasi-libc will require that Clang can locate `libclang_rt.builtins-wasm32.a`,
57+
meaning that `libc.so` requires this library to be present. To handle this the
58+
default behavior of the build system is to download the latest version of
59+
compiler-rt from [wasi-sdk] and use that for the build of `libc.so`. You can
60+
also set `BUILTINS_LIB` as a path to `libclang_rt.builtins-wasm32.a` as well to
61+
use that instead of downloading a version.
4862

4963
## Building in pthread support
5064

test/Makefile

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,6 @@ $(SYSROOT):
3535

3636
LIBC_TEST_URL ?= https://github.com/bytecodealliance/libc-test
3737
LIBC_TEST = $(DOWNDIR)/libc-test
38-
LIBRT_URL ?= https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-25/libclang_rt.builtins-wasm32-wasi-25.0.tar.gz
39-
LIBRT = $(DOWNDIR)/libclang_rt.builtins-wasm32.a
4038
ARCH := $(shell uname -m)
4139
WASMTIME_URL ?= https://github.com/bytecodealliance/wasmtime/releases/download/v29.0.1/wasmtime-v29.0.1-$(ARCH)-linux.tar.xz
4240
WASMTIME = $(abspath $(DOWNDIR)/$(shell basename $(WASMTIME_URL) .tar.xz)/wasmtime)
@@ -51,12 +49,6 @@ $(DOWNDIR):
5149
$(LIBC_TEST): | $(DOWNDIR)
5250
git clone --depth 1 $(LIBC_TEST_URL) $@
5351

54-
# TODO: add install target to copy builtins library directly into a Clang
55-
# installation.
56-
$(LIBRT): | $(DOWNDIR)
57-
wget --no-clobber --directory-prefix=$(DOWNDIR) $(LIBRT_URL)
58-
tar --extract --file=$(DOWNDIR)/$(shell basename $(LIBRT_URL)) --strip-components=1 --directory=$(DOWNDIR)/
59-
6052
$(WASMTIME): | $(DOWNDIR)
6153
wget --no-clobber --directory-prefix=$(DOWNDIR) $(WASMTIME_URL)
6254
tar --extract --file=$(DOWNDIR)/$(shell basename $(WASMTIME_URL)) --directory=$(DOWNDIR)/
@@ -69,7 +61,7 @@ $(ADAPTER): | $(DOWNDIR)
6961
wget --no-clobber --directory-prefix=$(DOWNDIR) $(ADAPTER_URL)
7062

7163
# Target to download all necessary dependencies.
72-
TO_DOWNLOAD = $(LIBC_TEST) $(LIBRT) $(WASMTIME)
64+
TO_DOWNLOAD = $(LIBC_TEST) $(WASMTIME)
7365
ifeq ($(TARGET_TRIPLE), wasm32-wasip2)
7466
TO_DOWNLOAD += $(ADAPTER) $(WASM_TOOLS)
7567
endif
@@ -133,6 +125,16 @@ ifneq ($(findstring -threads,$(TARGET_TRIPLE)),)
133125
CFLAGS += -pthread
134126
endif
135127

128+
# Handle compiler-rt which is required for tests. This is done by requesting
129+
# that the parent directory, the main wasi-libc directory, fetch its compiler-rt
130+
# which will create a `resource-dir` argument which we can then add to LDFLAGS
131+
# which gets fed down below into the actual linking of wasms.
132+
LDFLAGS += -resource-dir ../build/$(TARGET_TRIPLE)/resource-dir
133+
BUILTINS_STAMP := $(OBJDIR)/builtins.stamp
134+
$(BUILTINS_STAMP):
135+
make -C .. builtins
136+
touch $@
137+
136138
# Build up all the `*.wasm.o` object files; these are the same regardless of
137139
# whether we're building core modules or components.
138140
$(WASM_OBJS): $(INFRA_HEADERS)
@@ -142,7 +144,7 @@ $(OBJDIR)/%.wasm.o: $(SRCDIR)/%.c $(DOWNLOADED) $(SYSROOT)
142144

143145
# Build up all the `*.wasm` files.
144146
obj_to_c = $(patsubst $(OBJDIR)/%.wasm.o,$(SRCDIR)/%.c,$1)
145-
$(OBJDIR)/%.core.wasm: $(OBJDIR)/%.wasm.o $(INFRA_WASM_OBJS)
147+
$(OBJDIR)/%.core.wasm: $(OBJDIR)/%.wasm.o $(INFRA_WASM_OBJS) | $(BUILTINS_STAMP)
146148
@mkdir -p $(@D)
147149
$(CC) $(CFLAGS) $(LDFLAGS) $(shell scripts/add-flags.py LDFLAGS $(call obj_to_c,$<)) $^ -o $@
148150

0 commit comments

Comments
 (0)