@@ -59,6 +59,95 @@ Cortex-M needs no `lldEmulation` column entry: clang has a *BareMetal* toolchain
5959for arm, so these triples reach ` ld.lld ` through the driver as the RISC-V and
6060aarch64 rows do. 32-bit ARM has no ` -mcmodel ` axis, so that column is empty too.
6161
62+ ### Dead-section elimination
63+
64+ Freestanding builds compile with ` -ffunction-sections -fdata-sections ` and link
65+ with ` --gc-sections ` . Both halves belong to the engine rather than to a project
66+ because a dependency's translation units must carry them, and a project cannot
67+ reach those.
68+
69+ The flags became necessary rather than merely economical when a C library began
70+ arriving from the dependency graph. A dependency's object files enter the link
71+ unconditionally, unlike an archive member, which is pulled only while its symbol
72+ is undefined. That costs nothing when the C library is a prebuilt archive and
73+ the target has megabytes; a Cortex-M part has kilobytes, and without dead-section
74+ elimination every image would carry the whole of the C library.
75+
76+ ⚠️ ** A linker script becomes load-bearing in a new way.** An interrupt vector
77+ table is referenced by nothing — the hardware reads it by address — so
78+ ` --gc-sections ` collects it. A board's script must say ` KEEP(*(.vectors)) ` .
79+ Measured: with the ` KEEP ` present, a function nothing calls is dropped, the
80+ table survives, and the image boots.
81+
82+ ⚠️ The last two rows default to no C library, and that is a statement rather
83+ than an omission: the first consumer of both rows — the ` openarch ` layer of
84+ machine mechanism — references no C library symbol, and if no row defaulted to
85+ this tier there would be nothing demonstrating the tier works.
86+ ⭐ ** A build for those rows is declarable, not absent** (mcpp 2026.8.21.3+).
87+ ` xim:picolibc-aarch64 ` and ` xim:picolibc-x86 ` are in the index; a project that
88+ wants one names it the same way it would choose a different one:
89+
90+ ``` toml
91+ [target .aarch64-none-elf ]
92+ sysroot = " xim:picolibc-aarch64@1.8.12"
93+ ``` An empty column means exactly what
94+ `[target.<triple>].sysroot = ""` means in a manifest, so a project targeting one
95+ begins on the zero-libc tier without asking. A project that wants a C library on
96+ those targets declares one, which is also how it would choose a different one.
97+
98+ Such a target needs no per-host cross toolchain. clang and lld are
99+ cross-compilers by construction — one binary emits every target it was built
100+ with — so the target table pins `llvm@22.1.8` on every host, and any machine
101+ that can install the LLVM payload can produce an image for any of the four.
102+
103+ # ## The x86_64 row is not four strings
104+
105+ ⚠️ **A target row is normally an entry in two tables and nothing else. This one
106+ needed engine code, and the reason is a property of clang rather than of the
107+ instruction set.**
108+
109+ clang selects a toolchain from the triple. It has a *BareMetal* toolchain for
110+ arm, aarch64 and riscv, which links with `ld.lld` directly; it has none for
111+ x86_64, so every spelling of a bare x86_64 triple falls through to the generic
112+ GCC toolchain — whose linker is the **host's `g++`**:
113+
114+ ```
115+ g++: error: unrecognized command-line option '-fuse-ld=/…/llvm/22.1.8/bin/ld.lld'
116+ ```
117+
118+ Measured for `x86_64-none-elf`, `x86_64-unknown-none-elf`, `x86_64-unknown-none`,
119+ `x86_64-elf`, `x86_64-none-none` and `x86_64-unknown-unknown`, and unchanged by
120+ `-fuse-ld=lld`, `--ld-path=`, `--gcc-toolchain=` or `-B`. The one thing that
121+ does change it is putting `linux` in the OS position, which makes clang link
122+ directly and adds eight host `-L` paths to a bare-metal link.
123+
124+ Neither outcome is acceptable: routing through a host `g++` makes the row work
125+ on a Linux host and nowhere else, and host search paths on a freestanding link
126+ are the hermeticity this engine exists to keep. So the row carries a fifth
127+ column, `lldEmulation`, and when it is set the engine drives the link with
128+ `ld.lld` itself. The flag vocabulary changes with the tool — `-Map=` rather than
129+ `-Wl,-Map=`, `-m elf_x86_64` rather than `--target=` — and the driver-only flags
130+ (`-nostdlib++`, the loader tag) are dropped rather than translated.
131+
132+ The column is empty for the riscv and aarch64 rows. Their driver already reaches
133+ lld, and changing a working link to make three rows look alike is how a
134+ regression is introduced.
135+
136+ ### `-mno-red-zone` is part of the target, not a preference
137+
138+ The System V x86-64 ABI reserves 128 bytes below `rsp` that a leaf function may
139+ use without adjusting the stack pointer, because on a hosted system nothing else
140+ writes there. On bare metal the processor pushes an interrupt frame at `rsp` —
141+ into the red zone — and the interrupted leaf resumes to find its locals
142+ overwritten. There is no fault and no diagnostic, and it happens only when an
143+ interrupt arrives inside a leaf.
144+
145+ There is no bare-metal x86_64 program for which the red zone is safe, so the
146+ flag is a property of the row rather than something a project remembers. It
147+ reaches the command line through a new `extra` column in the ISA-profile table,
148+ which exists because `-march`/`-mabi`/`-mcmodel` could not express it. RISC-V
149+ and aarch64 have no equivalent, which is why the column did not exist before.
150+
62151Three things a bare-metal build requires are not properties of the ISA, and
63152mcpp does not attempt to derive them: which startup object and libraries to
64153select, which linker script describes the machine's memory, and how to execute
0 commit comments