A C++ toolchain from 357 bytes, in Bazel

ingve1 pts0 comments

A C++ toolchain from 357 bytes, in Bazel | Farid Zakaria’s Blog

Skip to content

I have been fascinated and amazed by stage0 for a while now ever since I learnt about it via Guix using it to provide twenty two thousand packages source-bootstrapped from the 357-byte seed.

What is stage0?

It is a chain of compilers and assemblers that can be built from source, starting from a 357-byte program that can eventually build a recent GCC.11Once you can reach a recent-enough GCC, you can build any C/C++ program and beyond easily.

Since then, NixOS and other distributions have also adopted the same approach to minimize their binary seed which makes it possible to onboard new architectures and platforms much simpler.

What’s always frustrated me as a Bazel (& Buck) user is the reliance on prebuilt toolchains even for things that should be built from source easily like protoc.

Bazel has given up trying to provide a hermetic C++ toolchain and the upstream rules_cc ruleset just points you elsewhere:

Configuring a hermetic toolchain makes your build more deterministic. rules_cc itself does not yet offer a hermetic toolchain distribution

I had attempted to provide a stage0 hermetic C++ toolchain in October 2024 via https://github.com/fzakaria/stage0-bazel. I made substantial process through the bootstrap process but I did not make it far enought to be usabale.

To be honest, I was also a little disheartened that no one else in the community thought it was the greatest thing since slice bread. Everyone seems to be content with using prebuilt toolchains as they go deeper into MODULE.bzl madness.

I had put it aside for a while, but I have been thinking about it again recently. The steps are mechanical and the process imitates existing distributions, so this became a perfect project for me to throw at an LLM to finish.22Consider this the disclosure that I used an LLM to help me write the remainder of the toolchain.

You can now leverage the toolchain to build cc_binary in Bazel and have it compiled by a toolchain whose entire ancestry is in the repository from that same 357-byte seed . 🎆

How complete is this toolchain?

I pointed the toolchain at Abseil and GoogleTest straight from the Bazel Central Registry without any patches .

bazel_dep(name = "stage0-bazel", version = "0.1.0")<br>bazel_dep(name = "abseil-cpp", version = "20260107.1")<br>bazel_dep(name = "googletest", version = "1.17.0.bcr.2")

register_toolchains(<br>"@stage0-bazel//toolchain:clang",<br>"@stage0-bazel//toolchain:cc",

We then can build and run their testsuite to provide a sanity check that the toolchain is working correctly.

$ bazel test --target_pattern_file=absl-tests.txt<br>Executed 236 out of 236 tests: 236 tests pass

We use a --target_pattern_file to filter tests that require google_benchmark. Abseil marks google_benchmark as a dev_dependency, and Bzlmod drops dev dependencies of non-root modules.

That is us building Abseil and GoogleTest, from the registry, unpatched, compiled by a toolchain that began as 357 bytes of hex.

bootstrap

seed

hex0

357 bytes

stage0

hex1 → hex2 → M0 → cc_x86<br>M2-Planet, kaem, M1

stage0 -->

seed->stage0

mes

GNU Mes

mescc

mes -->

stage0->mes

tccmes

tcc-mes

tccmes -->

mes->tccmes

tcc

tinycc

self-hosted

tcc -->

tccmes->tcc

musl

musl

musl -->

tcc->musl

bin

binutils

bin -->

musl->bin

gcc46

GCC 4.6.4

gcc46 -->

bin->gcc46

gcc10

GCC 10.4.0

C++17

gcc10 -->

gcc46->gcc10

extras

tar, findutils<br>Linux UAPI headers

extras -->

gcc10->extras

llvm

clang 22.1.8<br>lld 22.1.8

llvm -->

gcc10->llvm

llvm -->

extras->llvm

absl

Abseil + GoogleTest<br>236 tests pass

absl -->

llvm->absl

How can I be so sure this is a hermetic toolchain?

The toolchain includes an audit report that uses Bazel’s aspects to inspect every action in the build graph and verify that it only executes programs built by the toolchain itself. The report is generated by running bazel build //:trust-report and will fail if any action executes a program outside of the Bazel output tree.33We also set BAZEL_DO_NOT_DETECT_CPP_TOOLCHAIN=1 to disable Bazel’s built-in C++ host toolchain detection.

The report is two lines long:

Bootstrap trust report

Every action in the checked graph runs a program built by this<br>repository, except for these audited seed binaries:

external/+_repo_rules+hex0-seeds/POSIX/x86/hex0-seed<br>/nix/store/…-bash-interactive-5.3p3/bin/bash

Unfortunately, since genrule runs a shell it takes as an absolute system path that is also listed as a seed binary. sh_toolchain’s path attribute is a string, and the shell is not a declared input of the action, so no artifact this repository built can provide it.

Building toolchains from bootstrap seeds was never a priority for companies like Google where they control the entire build environment.<br>However we seemed to have adopted the same approach as Bazel and similar build systems have become more popular in the open-source community. We should strive to make our builds more...

toolchain bazel stage0 from build seed

Related Articles