Introduction

You have used GCC for years. You know what -O2 does, roughly. You have fought cannot find crt1.o at two in the morning. You have built a cross toolchain by copying somebody's shell script and changing the triple, and it worked, and you never found out why.

This book is about what is actually in there.

It is not a manual. GCC already has excellent manuals — several of them — and where they cover a mechanism this book tells you which node to read. It is also not a wiki: there is an order to the chapters, each one assumes the ones before it, and each one is about a single mechanism named in its title.

What the book does that the manuals do not is show you the code. Every concrete claim here carries a link to the line of GCC source that proves it, so that when you disagree with a sentence you are one click from settling the argument. GCC's own comments are unusually good, and where a comment explains a decision better than prose could, the comment is quoted rather than paraphrased.

Who this is for

Someone comfortable with C, ELF, readelf, and the general shape of a linker. You do not need to have read a compiler book. You do not need to know what SSA is; when it matters, you will be told.

You do not need to build GCC to use most of this book. Nearly every mechanism described has a command that lets you watch it happen on a compiler you already have installed — -###, -v, -print-search-dirs, -dumpspecs, -fdump-tree-all. Chapters that describe something you cannot observe from outside say so explicitly.

How the source is cited

Every source reference in this book points at an immutable upstream release tag on the official read-only mirror at github.com/gcc-mirror/gcc. At the time of writing that tag is releases/gcc-15.2.0, and every line number was read out of that tag rather than out of anybody's working tree.

Each chapter opens with a note naming its pin and ends with a reminder of it. That matters because line numbers rot: the code around them almost never does, but the numbers themselves move with every patch. If you are reading a GCC 12 tree, the file names alone will differ — see below.

When a mechanism genuinely changed between major releases, rather than merely shifting a few lines down, that is flagged inline, at the point of the claim, so a reader on an older compiler is warned by the sentence they are reading rather than by an appendix they will never reach.

If you are on GCC 11 or earlier, every filename in this book is wrong. GCC 12 renamed nearly every .c file in the compiler to .cc in a single commit, 5c69acb3 (Martin Liska, 2022-01-14). So gcc.c became gcc.cc, gimplify.c became gimplify.cc, aarch64.c became aarch64.cc. Every pre-12 blog post, mailing-list thread and Stack Overflow answer on the internet uses the old names. Mentally add or drop the second c as needed.

The shape of the book

Part I, "The driver is not the compiler", is about the program you actually type. gcc compiles nothing at all; it is a program whose entire job is to decide which other programs to run and to build each one's command line. That single fact explains most of what looks arbitrary about GCC from outside, and Part I is the longest part of the book because almost every path, prefix, library and header decision lives there.

Part II is about configuring and building GCC itself, and Part III about the specific problem of bootstrapping a cross toolchain, where the compiler and the C library each need the other to exist first.

Part IV goes inside cc1 — the three intermediate representations, the pass manager, and how a machine description becomes an instruction. Part V covers the runtime libraries GCC ships. Part VI is about working on GCC as a project: the testsuite, the conventions, and how a patch gets proposed.

Four things to keep straight from the start

These recur in every part and they are where most published explanations of GCC go wrong.

The driver never opens a library file. It emits -l and -L and lets ld resolve them. A "library search path" in GCC's sense is a list of strings the driver hands to the linker, nothing more.

Link-time search and run-time search are unrelated systems. A successful link says nothing whatever about whether the resulting binary will start. That is ld.so's business — DT_RUNPATH, ld.so.conf, LD_LIBRARY_PATH — and no chapter in Part I touches it.

Header search and library search are separate mechanisms. They happen to be relocated by the same prefix, which makes them look like one thing. They are implemented in different files by different code with different sysroot rules.

Anything under libexec/gcc/ is a host program; anything under <target>/lib/ is a target artifact. A host program runs on your machine. A target artifact never runs at all on your machine; it only gets linked into what your compiler produces. Blurring these two makes cross-compilation incoherent, and Chapter 1.2 is largely about keeping them apart.