diff --git a/.github/actions/setup/action.yml b/.github/actions/setup/action.yml
new file mode 100644
index 0000000000000000000000000000000000000000..49caf464aa591abfefda8d7218242256a09ec73b
--- /dev/null
+++ b/.github/actions/setup/action.yml
@@ -0,0 +1,60 @@
+name: 'Install LLVM'
+description: 'Install LLVM'
+inputs:
+  clang_version:
+    description: 'version string of Clang to download'
+    required: true
+  llvm_asset_suffix:
+    description: 'extra info about how to download from LLVM binaries'
+    required: false
+    default: ''
+
+runs:
+  using: composite
+  steps:
+    - name: Install LLVM tools (Windows)
+      shell: bash
+      run: |
+        curl -fsSLO https://github.com/llvm/llvm-project/releases/download/llvmorg-${{ inputs.clang_version }}/LLVM-${{ inputs.clang_version }}-win64.exe
+        7z x LLVM-${{ inputs.clang_version }}-win64.exe -y -o"llvm"
+        echo "$(pwd)/llvm/bin" >> $GITHUB_PATH
+        echo "CC=$(pwd)/llvm/bin/clang.exe" >> $GITHUB_ENV
+        echo "AR=$(pwd)/llvm/bin/llvm-ar.exe" >> $GITHUB_ENV
+        echo "NM=$(pwd)/llvm/bin/llvm-nm.exe" >> $GITHUB_ENV
+      if: runner.os == 'Windows'
+
+    - name: Override llvm-nm with one from rustup (Windows)
+      shell: bash
+      run: |
+        rustup update stable
+        rustup default stable
+        rustup component add llvm-tools-preview
+        echo "NM=$(rustc --print sysroot|sed 's|C:|/c|'|sed 's|\\|/|g')/lib/rustlib/x86_64-pc-windows-msvc/bin/llvm-nm.exe" >> $GITHUB_ENV
+      if: runner.os == 'Windows'
+
+    - name: Install LLVM tools (MacOS)
+      shell: bash
+      run: |
+        curl -sSfL https://github.com/llvm/llvm-project/releases/download/llvmorg-${{ inputs.clang_version }}/clang+llvm-${{ inputs.clang_version }}-${{ inputs.llvm_asset_suffix }}.tar.xz | tar xJf -
+        export CLANG_DIR=`pwd`/clang+llvm-${{ inputs.clang_version }}-${{ inputs.llvm_asset_suffix }}/bin
+        echo "$CLANG_DIR" >> $GITHUB_PATH
+        echo "CC=$CLANG_DIR/clang" >> $GITHUB_ENV
+        echo "AR=$CLANG_DIR/llvm-ar" >> $GITHUB_ENV
+        echo "NM=$CLANG_DIR/llvm-nm" >> $GITHUB_ENV
+      if: runner.os == 'macOS'
+
+    # Note that this uses apt-based packages for installing Clang/tools to
+    # ensure that all various dependencies are also installed. Binaries from
+    # llvm-project depend on libtinfo.so and TBH I don't know what that is.
+    # Using apt-get should basically serve the same purpose though.
+    - name: Install LLVM tools (Linux)
+      shell: bash
+      run: |
+        set -ex
+        v=${{ inputs.clang_version }}
+        sudo apt-get update
+        sudo apt-get install -y clang-$v clang-tools-$v
+        echo "CC=clang-$v" >> $GITHUB_ENV
+        echo "AR=llvm-ar-$v" >> $GITHUB_ENV
+        echo "NM=llvm-nm-$v" >> $GITHUB_ENV
+      if: runner.os == 'Linux'
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 040900a037076aad7eb16a1ff7307f4458ecb6bf..ae0cc1b5d7e77bcd566843d05840bac1bcc3c94b 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -2,165 +2,163 @@ name: CI
 on: [push, pull_request]
 
 concurrency:
-  group: ${{ github.workflow }}-${{ github.event.pull_request.number }}
+  group: ${{ github.workflow }}-${{ github.ref }}
   cancel-in-progress: true
 
+defaults:
+  run:
+    shell: bash
+
 jobs:
   buildlibc:
-    name: Build libc
+    name: ${{ matrix.name }}
     runs-on: ${{ matrix.os }}
+    env: ${{ matrix.env || fromJSON('{}') }}
     strategy:
+      fail-fast: false
       matrix:
-        os: [ubuntu-22.04, macos-15, windows-2025]
-        clang_version: [10.0.0]
-        # use different LLVM versions among oses because of the lack of
-        # official assets on github.
         include:
-          - os: ubuntu-22.04
-            clang_version: 10.0.0
-            llvm_asset_suffix: x86_64-linux-gnu-ubuntu-18.04
-          - os: macos-15
-            clang_version: 10.0.0
-            llvm_asset_suffix: x86_64-apple-darwin
-          - os: windows-2025
-            clang_version: 10.0.0
-          - os: ubuntu-22.04
-            clang_version: 16.0.0
-            llvm_asset_suffix: x86_64-linux-gnu-ubuntu-18.04
-            enable_pic: true
-          - os: macos-15
+          # Test a number of operating systems and architectures to make sure
+          # wasi-libc builds on these platforms by default.
+          - name: Build on Linux x86_64
+            os: ubuntu-24.04
+            clang_version: 16
+            upload: linux-x86_64-clang-16
+          - name: Build on Linux aarch64
+            os: ubuntu-24.04-arm
+            clang_version: 16
+            upload: linux-aarch64-clang-16
+          - name: Build on macOS aarch64
+            os: macos-15
             clang_version: 15.0.7
-            llvm_asset_suffix: x86_64-apple-darwin21.0
-          - os: windows-2025
-            clang_version: 16.0.0
-            enable_pic: true
-          - os: ubuntu-24.04-arm
+            llvm_asset_suffix: arm64-apple-darwin22.0
+            upload: macos-clang-15
+          - name: Build on Windows x86_64
+            os: windows-2025
             clang_version: 16.0.0
-            llvm_asset_suffix: aarch64-linux-gnu
-            enable_pic: true
-            
+            upload: windows-clang-16
+
+          # Historical versions of LLVM (11.0.0 currently)
+          - name: Build with LLVM 11
+            os: ubuntu-22.04
+            clang_version: 11
+            upload: linux-x86_64-clang-11
+            env:
+              BUILD_LIBSETJMP: no
+
+          # Test various combinations of targets triples.
+          #
+          # Configuration here can happen through `env` which is inherited to
+          # jobs below. For now this only runs tests on Linux with Clang 16,
+          # but that can be expanded as necessary in the future too. Note that
+          # some targets run the build for the `libc_so` makefile target to
+          # ensure the PIC build works.
+          - name: Test wasm32-wasi
+            os: ubuntu-24.04
+            clang_version: 16
+            test: true
+            upload: wasm32-wasi
+            env:
+              TARGET_TRIPLE: wasm32-wasi
+              MAKE_TARGETS: "default libc_so"
+          - name: Test wasm32-wasip1
+            os: ubuntu-24.04
+            clang_version: 16
+            test: true
+            upload: wasm32-wasip1
+            env:
+              TARGET_TRIPLE: wasm32-wasip1
+              MAKE_TARGETS: "default libc_so"
+          - name: Test wasm32-wasip2
+            os: ubuntu-24.04
+            clang_version: 16
+            test: true
+            upload: wasm32-wasip2
+            env:
+              TARGET_TRIPLE: wasm32-wasip2
+              WASI_SNAPSHOT: p2
+              MAKE_TARGETS: "default libc_so"
+          - name: Test wasm32-wasi-threads
+            os: ubuntu-24.04
+            clang_version: 16
+            test: true
+            upload: wasm32-wasi-threads
+            env:
+              TARGET_TRIPLE: wasm32-wasi-threads
+              THREAD_MODEL: posix
+          - name: Test wasm32-wasip1-threads
+            os: ubuntu-24.04
+            clang_version: 16
+            test: true
+            upload: wasm32-wasip1-threads
+            env:
+              TARGET_TRIPLE: wasm32-wasip1-threads
+              THREAD_MODEL: posix
+          - name: Test wasm32-wasip1 in V8
+            os: ubuntu-24.04
+            clang_version: 16
+            test: true
+            test_with_v8: true
+            env:
+              TARGET_TRIPLE: wasm32-wasip1
+          - name: Test wasm32-wasip1-threads in V8
+            os: ubuntu-24.04
+            clang_version: 16
+            test: true
+            test_with_v8: true
+            env:
+              TARGET_TRIPLE: wasm32-wasip1-threads
+              THREAD_MODEL: posix
+
     steps:
     - uses: actions/checkout@v4.1.7
       with:
         submodules: true
 
-    - name: Install libtinfo5
-      run: |
-        set -ex
-        sudo apt-get update
-        sudo apt-get install -y libtinfo5
-      if: startsWith(matrix.os, 'ubuntu-22')
-
-    - name: Install LLVM tools (Windows)
-      shell: bash
-      run: |
-        curl -fsSLO https://github.com/llvm/llvm-project/releases/download/llvmorg-${{ matrix.clang_version }}/LLVM-${{ matrix.clang_version }}-win64.exe
-        7z x LLVM-${{ matrix.clang_version }}-win64.exe -y -o"llvm"
-        echo "$(pwd)/llvm/bin" >> $GITHUB_PATH
-        echo "CC=$(pwd)/llvm/bin/clang.exe" >> $GITHUB_ENV
-        echo "AR=$(pwd)/llvm/bin/llvm-ar.exe" >> $GITHUB_ENV
-        echo "NM=$(pwd)/llvm/bin/llvm-nm.exe" >> $GITHUB_ENV
-      if: matrix.os == 'windows-2025'
-
-    - name: Override llvm-nm with one from rustup (Windows)
-      run: |
-        rustup update stable
-        rustup default stable
-        rustup component add llvm-tools-preview
-        echo "NM=$(rustc --print sysroot|sed 's|C:|/c|'|sed 's|\\|/|g')/lib/rustlib/x86_64-pc-windows-msvc/bin/llvm-nm.exe" >> $GITHUB_ENV
-      if: matrix.os == 'windows-2025'
-
-    - name: Install LLVM tools (MacOS)
-      shell: bash
-      run: |
-        curl -sSfL https://github.com/llvm/llvm-project/releases/download/llvmorg-${{ matrix.clang_version }}/clang+llvm-${{ matrix.clang_version }}-${{ matrix.llvm_asset_suffix }}.tar.xz | tar xJf -
-        export CLANG_DIR=`pwd`/clang+llvm-${{ matrix.clang_version }}-${{ matrix.llvm_asset_suffix }}/bin
-        echo "$CLANG_DIR" >> $GITHUB_PATH
-        echo "CC=$CLANG_DIR/clang" >> $GITHUB_ENV
-        echo "AR=$CLANG_DIR/llvm-ar" >> $GITHUB_ENV
-        echo "NM=$CLANG_DIR/llvm-nm" >> $GITHUB_ENV
-      if: matrix.os == 'macos-15'
-
-    - name: Install LLVM tools (Linux)
-      shell: bash
-      run: |
-        curl -sSfL https://github.com/llvm/llvm-project/releases/download/llvmorg-${{ matrix.clang_version }}/clang+llvm-${{ matrix.clang_version }}-${{ matrix.llvm_asset_suffix }}.tar.xz | tar xJf -
-        export CLANG_DIR=`pwd`/clang+llvm-${{ matrix.clang_version }}-${{ matrix.llvm_asset_suffix }}/bin
-        echo "$CLANG_DIR" >> $GITHUB_PATH
-        echo "CLANG_DIR=$CLANG_DIR" >> $GITHUB_ENV
-        echo "CC=$CLANG_DIR/clang" >> $GITHUB_ENV
-        echo "AR=$CLANG_DIR/llvm-ar" >> $GITHUB_ENV
-        echo "NM=$CLANG_DIR/llvm-nm" >> $GITHUB_ENV
-      if: startsWith(matrix.os, 'ubuntu-')
-
-    - name: Disable libsetjmp for old LLVM
-      shell: bash
-      run: |
-        echo "BUILD_LIBSETJMP=no" >> $GITHUB_ENV
-      if: matrix.clang_version == '10.0.0'
-
-    - name: Enable PIC build for new LLVM
-      shell: bash
-      run: |
-        echo "MAKE_TARGETS=default libc_so" >> $GITHUB_ENV
-      if: matrix.enable_pic
+    - uses: ./.github/actions/setup
+      with:
+        clang_version: ${{ matrix.clang_version }}
+        llvm_asset_suffix: ${{ matrix.llvm_asset_suffix }}
 
     - name: Build libc
-      shell: bash
-      run: |
-        make -j4 TARGET_TRIPLE=wasm32-wasi $MAKE_TARGETS
-        make -j4 TARGET_TRIPLE=wasm32-wasip1 $MAKE_TARGETS
-        make -j4 TARGET_TRIPLE=wasm32-wasip2 WASI_SNAPSHOT=p2 $MAKE_TARGETS
+      run: make -j4 $MAKE_TARGETS
+
+    - name: Download Test dependencies
+      if: matrix.test
+      run: cd test && make download
 
-    - name: Build libc + threads
-      # Only build the thread-capable wasi-libc in the latest supported Clang
-      # version; the earliest version does not have all necessary builtins
-      # (e.g., `__builtin_wasm_memory_atomic_notify`).
-      if: matrix.clang_version != '10.0.0'
-      shell: bash
+    - name: Install V8 dependencies
+      if: matrix.test_with_v8
       run: |
-        make -j4 THREAD_MODEL=posix TARGET_TRIPLE=wasm32-wasi-threads
-        make -j4 THREAD_MODEL=posix TARGET_TRIPLE=wasm32-wasip1-threads
+        npm -C test/scripts/browser-test install
+        npx -C test/scripts/browser-test playwright install chromium-headless-shell
+        echo ENGINE="$PWD/test/scripts/browser-test/harness.mjs" >> $GITHUB_ENV
 
     - name: Test
-      shell: bash
+      if: matrix.test
       # For Clang linking to work correctly, we need to place Clang's runtime
       # library for `wasm32-wasi` in the right location (i.e., the `mkdir` and
       # `cp` below).
       run: |
         cd test
-        make download
-        export WASI_DIR=$(realpath $($CLANG_DIR/clang -print-resource-dir)/lib/wasi/)
-        export WASIP1_DIR=$(realpath $($CLANG_DIR/clang -print-resource-dir)/lib/wasip1/)
-        export WASIP2_DIR=$(realpath $($CLANG_DIR/clang -print-resource-dir)/lib/wasip2/)
+        mkdir resource-dir
+        export WASI_DIR=./resource-dir/lib/wasi/
+        export WASIP1_DIR=./resource-dir/lib/wasip1/
+        export WASIP2_DIR=./resource-dir/lib/wasip2/
         mkdir -p $WASI_DIR $WASIP1_DIR $WASIP2_DIR
         cp build/download/libclang_rt.builtins-wasm32.a $WASI_DIR
         cp build/download/libclang_rt.builtins-wasm32.a $WASIP1_DIR
         cp build/download/libclang_rt.builtins-wasm32.a $WASIP2_DIR
-        TARGET_TRIPLE=wasm32-wasi make test
-        TARGET_TRIPLE=wasm32-wasip1 make test
-        TARGET_TRIPLE=wasm32-wasip2 make test
-        TARGET_TRIPLE=wasm32-wasi-threads make test
-        TARGET_TRIPLE=wasm32-wasip1-threads make test
-
-        npm -C scripts/browser-test install
-        npx -C scripts/browser-test playwright install chromium-headless-shell
-        ENGINE="$PWD/scripts/browser-test/harness.mjs" TARGET_TRIPLE=wasm32-wasip1 make test
-        ENGINE="$PWD/scripts/browser-test/harness.mjs" TARGET_TRIPLE=wasm32-wasip1-threads make test
-      # The older version of Clang does not provide the expected symbol for the
-      # test entrypoints: `undefined symbol: __main_argc_argv`.
-      # The older (<15.0.7) version of wasm-ld does not provide `__heap_end`,
-      # which is required by our malloc implementation.
-      if: startsWith(matrix.os, 'ubuntu-') && matrix.clang_version != '10.0.0'
+        export LDFLAGS="-resource-dir $(pwd)/resource-dir"
+        make test
 
     - uses: actions/upload-artifact@v4.4.0
+      if: matrix.upload
       with:
-        # Upload the sysroot folder. To avoid action erros, we give it a unique
-        # name using the OS it was built for and the Clang version it was built
-        # with.
-        name: ${{ format( 'sysroot-{0}-clang-{1}.tgz', matrix.os, matrix.clang_version) }}
+        name: ${{ format( 'sysroot-{0}.tgz', matrix.upload) }}
         path: sysroot
 
+
   # Disable the headerstest job for now, while WASI transitions from the
   # witx snapshots to wit proposals, and we have a few manual edits to the
   # generated header to make life easier for folks.
@@ -176,7 +174,6 @@ jobs:
       with:
         submodules: true
     - name: Install Rust (rustup)
-      shell: bash
       run: rustup update stable --no-self-update && rustup default stable
       if: matrix.os != 'macos-15'
     - name: Install Rust (macos)
diff --git a/Makefile b/Makefile
index 8696e7ea2b96d22a9c661182d6ac6ab27039a733..4f9147cf1102d5947cc6ad03022172006921baba 100644
--- a/Makefile
+++ b/Makefile
@@ -47,24 +47,22 @@ BULK_MEMORY_THRESHOLD ?= 32
 # make command-line.
 
 # Set the default WASI target triple.
-TARGET_TRIPLE = wasm32-wasi
+TARGET_TRIPLE ?= wasm32-wasi
 
 # Threaded version necessitates a different target, as objects from different
 # targets can't be mixed together while linking.
 ifeq ($(THREAD_MODEL), posix)
-TARGET_TRIPLE = wasm32-wasi-threads
+TARGET_TRIPLE ?= wasm32-wasi-threads
 endif
 
 ifeq ($(WASI_SNAPSHOT), p2)
-TARGET_TRIPLE = wasm32-wasip2
+TARGET_TRIPLE ?= wasm32-wasip2
 endif
 
 # These artifacts are "stamps" that we use to mark that some task (e.g., copying
 # files) has been completed.
 INCLUDE_DIRS := $(OBJDIR)/copy-include-headers.stamp
 
-BUILTINS_LIB ?= $(shell ${CC} ${CFLAGS} --print-libgcc-file-name)
-
 # These variables describe the locations of various files and directories in
 # the source tree.
 DLMALLOC_DIR = dlmalloc
@@ -557,6 +555,12 @@ PIC_OBJS = \
 	$(LIBC_BOTTOM_HALF_CRT_OBJS) \
 	$(FTS_SO_OBJS)
 
+SYSTEM_BUILTINS_LIB := $(shell ${CC} ${CFLAGS} --print-libgcc-file-name)
+SYSTEM_RESOURCE_DIR := $(shell ${CC} ${CFLAGS} -print-resource-dir)
+BUILTINS_LIB_REL := $(subst $(SYSTEM_RESOURCE_DIR),,$(SYSTEM_BUILTINS_LIB))
+RESOURCE_DIR := $(OBJDIR)/resource-dir
+BUILTINS_LIB ?= $(RESOURCE_DIR)/$(BUILTINS_LIB_REL)
+
 # TODO: Specify SDK version, e.g. libc.so.wasi-sdk-21, as SO_NAME once `wasm-ld`
 # supports it.
 #
@@ -572,13 +576,15 @@ $(SYSROOT_LIB)/libc.so: $(OBJDIR)/libc.so.a $(BUILTINS_LIB)
 	$(CC) $(EXTRA_CFLAGS) --target=${TARGET_TRIPLE} -nodefaultlibs \
 	-shared --sysroot=$(SYSROOT) \
 	-o $@ -Wl,--whole-archive $< -Wl,--no-whole-archive $(BUILTINS_LIB) \
-	-Wl,--allow-undefined-file=linker-provided-symbols.txt
+	-Wl,--allow-undefined-file=linker-provided-symbols.txt \
+	-resource-dir $(RESOURCE_DIR)
 
 $(SYSROOT_LIB)/%.so: $(OBJDIR)/%.so.a $(SYSROOT_LIB)/libc.so
 	$(CC) $(EXTRA_CFLAGS) --target=${TARGET_TRIPLE} \
 	-shared --sysroot=$(SYSROOT) \
 	-o $@ -Wl,--whole-archive $< -Wl,--no-whole-archive \
-	-Wl,--allow-undefined-file=linker-provided-symbols.txt
+	-Wl,--allow-undefined-file=linker-provided-symbols.txt \
+	-resource-dir $(RESOURCE_DIR)
 
 $(OBJDIR)/libc.so.a: $(LIBC_SO_OBJS) $(MUSL_PRINTSCAN_LONG_DOUBLE_SO_OBJS)