I came across the following statement from Bootstrappable.org when I clicked Michael’s link:


“Compilers are often written in the language they are compiling. This creates a chicken-and-egg problem that leads users and distributors to rely on opaque, pre-built binaries of those compilers that they use to build newer versions of the compiler.”


At first, I wasn’t sure I agreed with the premise because I had always assumed that most compilers were ultimately written in assembly language. After looking into it more, I realized the situation is more nuanced and actually touches on a fascinating aspect of compiler history.


Historically, many early compilers were indeed written largely in assembly language. In the early days of computing, there was no alternative. If you wanted a compiler, assembler, or operating system, someone first had to write machine code or assembly code to make it happen.


However, once a language became mature enough, compiler developers often rewrote the compiler in the language itself. This is known as a self-hosting compiler.

Some modern examples include:

  • GCC is written primarily in C and C++.
  • Clang/LLVM is written primarily in C++.
  • The Go compiler is written primarily in Go.
  • The Rust compiler (rustc) is written primarily in Rust.


This raises an obvious question:

How was the first compiler for a language built if no compiler for that language existed yet?


Rust provides a good example. The first Rust compiler was not written in Rust. It was written primarily in OCaml by Graydon Hoare and early contributors. Once that compiler became capable enough, it was used to compile a new implementation of the Rust compiler written in Rust itself. Eventually the OCaml implementation was retired.

The evolution looked roughly like this:

OCaml compiler

      

Early Rust compiler (written in OCaml)

      

Compiles

      

Rust compiler (written in Rust)

      

Compiles

      

Future Rust compilers


If we keep following the chain backward, we eventually arrive at something like:


Modern Rust compiler

      

Older Rust compiler

      

OCaml Rust compiler

      

OCaml compiler

      

C compiler

      

Older C compiler

      

Assembly-language compiler

      

Assembler

      

Hand-entered machine code


This is the bootstrap chain that Bootstrappable.org is concerned about.

The issue is not that modern compilers are untrustworthy. Rather, their point is that at some stage everyone relies on a pre-built binary that they did not personally create. Most of us accept this as a practical necessity. We trust compiler binaries provided by established projects and distributions.


However, researchers interested in software supply-chain integrity ask a deeper question:

“Can we reproduce the entire toolchain from source all the way back to a tiny, auditable starting point?”


This is closely related to the famous “Trusting Trust” problem described by Ken Thompson. Even if source code appears clean, a malicious compiler binary could theoretically inject unwanted behavior during compilation.


That is why projects such as Stage0, GNU Mes, and the broader bootstrappable-builds effort exist. Their goal is to minimize the amount of binary software that must be trusted at the beginning of the build process.


As someone interested in TinyCC, I find this particularly interesting because TinyCC occupies a unique place in the compiler ecosystem. Its relatively small size makes it easier to study, understand, and potentially use as part of a bootstrap path compared to the massive codebases of GCC or LLVM.

What’s especially fascinating about TinyCC is that it appears to trace its roots to Fabrice Bellard’s remarkably small self-hosting OTCC compiler. OTCC was only a few kilobytes of source code, yet it could already compile itself. Bellard then expanded that foundation into the much more capable TinyCC we know today.


If I were to speculate on the likely bootstrap path, it might have looked something like:


Hand-written machine code

      

Assembler

      

Early Unix C compiler

      

GCC

      

OTCC (first version)

      

Self-hosting OTCC

      

TinyCC

      

Modern TinyCC


In the end, I came away with two conclusions:

  1. My assumption that most compilers are written in assembly language is historically true for many early compilers, but not for most modern compilers.
  2. The “chicken-and-egg” problem described by Bootstrappable.org is real, although whether it is an important practical concern depends on one’s threat model and goals.


I’m curious how the TinyCC community views this issue and whether anyone here has explored compiler bootstrapping, trusted build chains, or participation in the bootstrappable-builds effort. More specifically, has Fabrice Bellard ever documented exactly which compiler built the first OTCC release? If so, does a reproducible bootstrap path from that original version still exist today?


I suspect some long-time TinyCC developers may know the answer from old mailing-list discussions, historical source archives, or Bellard’s original development notes.


Best regards,


John M.




On Jun 2, 2026, at 5:15 AM, Michael Ackermann via Tinycc-devel <[email protected]> wrote:

Hi,

So you're referring to the standards compliance level for tcc implemention itself?
That's a rather important question in this context, because there is a fundemental
tradeoff between the language feature set chosen for tcc implementation itself which
affects the bootstrapping of it.
(bootstrapping a C compiler is far more challenging than merely compiling it let's
say self-hosting or compiling it with gcc/clang)
Depends if you consider bootrapping tcc a relevant criteria, myself does.
Shall be no criticism at all, merely a proposal to keep bootstrapping criteria of and
with tcc in mind.

Currently, you may have a look at live-bootstrap project from bootstrappable.org
They had forked some tcc-0.9.26 version and patched it to be even more conservative
with language constructs that a much simpler C-compiler during the early stages
can process, then re-compile tcc/libc several times, it's rather complicated
already. And there has been a discussion before on tinycc-devel about this, which i
didn't follow at that time. Maybe someone else remembers to pick up this topic
with the necessary expertise and insight.

Few weeks ago i tried to transition this tcc-0.9.26 and tcc-0.9.27 to bootstrap
tcc-head directly instead from a compiler known as pnut-cc (some excellent project
from laurenth, too see codeberg.org/aggi/tiny-bootstrap if you wish, and the github repo
from laurenth, janneke, and a few other people) So far I only succeeded to swap
tcc-0.9.27 with tcc-head in this case, but do need the intermediate forked/patched
0.9.26 version still because it's a non-trivial task to rebase some changes from
janneke/0.9.26 onto tcc-head to make that "bootstrappable" directly again.
So far i did an extensive review with tiny-bootstrap and might help a little only too
coordinate this, if anyone wishes. To keep it brief, it would be a nice-to-have
if tcc-mob/HEAD could be bootstrapped/compiled with pnut-cc/mes-cc (and some other
mes-replacement project from a developer named Frans Faase, many people tried to
cleanup at this frontline including myself but noone succeeded yet to rebase for
tcc-head without several other intermediate steps and tcc/libc versions involved)

Long story short, there's a tradeoff between language-constructs chosen and
remaining bootstrappable. I think stikonas and janneke might explain in detail
tcc already is affected by some issues which blocks it at 0.9.26, tcc-head cannot
yet directly be re-integrated with the live-bootstrap procedure, besides some libc
issues.

Regards
Michael

On 2026-06-02 13:59, Mounir IDRASSI via Tinycc-devel wrote:
Hi Meng,

To me, the current policy for tcc source is described in CodingStyle: tcc is
mostly implemented in C90, and new non-C90 constructs shouldn't be introduced
unless already used. The current "-Wdeclaration-after-statement" check helps
preserve that style while still leaving GCC/Clang in their normal GNU mode.

Replacing it with "-std=c99" would weaken that check because it permits mixed
declarations and statements. It also changes the host compiler into strict ISO
C mode, which affects system header visibility: the need to add _DEFAULT_SOURCE
for usleep in your patch is a symptom of that.

So I wouldn't replace "-Wdeclaration-after-statement" with "-std=c99" just
because the CI passes. If there is a specific build issue with the current flag,
we should address that directly. If the goal is to check that tcc can also be
built under a C99 host compiler mode, that could be considered as an optional
CI and configuration test, but it shouldn't replace the current style check.

Regards,

Mounir IDRASSI



From: Meng Zhuo <[email protected]>
To: <[email protected]>
Date: Mon, 01 Jun 2026 18:06:11 +0900
Subject: [Tinycc-devel] Changing std to C99?

Hi, everyone

I notice TinyCC should follow C99 instead of GNU99 according to
https://bellard.org/tcc , is it designed by that way or something that I
missed?


I have tested C99 on my Github and all testes passed

https://github.com/mengzhuo/tinycc/actions/runs/26745551862


_______________________________________________
Tinycc-devel mailing list
[email protected]
https://lists.nongnu.org/mailman/listinfo/tinycc-devel




_______________________________________________
Tinycc-devel mailing list
[email protected]
https://lists.nongnu.org/mailman/listinfo/tinycc-devel

--

_______________________________________________
Tinycc-devel mailing list
[email protected]
https://lists.nongnu.org/mailman/listinfo/tinycc-devel

Attachment: signature.asc
Description: Binary data

_______________________________________________
Tinycc-devel mailing list
[email protected]
https://lists.nongnu.org/mailman/listinfo/tinycc-devel

Reply via email to