πΌ Lighthouse β boot a Linux tinyconfig kernel built with PXX's C frontend
- Type: goal (lighthouse / end-goal β NOT a sprint ticket)
- Track: C (C frontend) + A (backend/ELF/codegen where noted)
- Status: rainy-day
- Opened: 2026-07-18
- Nature: a fixed point on the horizon to steer by, not a task to schedule. Attempting it will avalanche into many concrete tickets; those are the work, this is the bearing. Do not "start" this ticket β sub-tickets get crafted when we get there.
The goal
PXX (as CC=pxx in the standard kernel make toolchain) compiles an x86-64
tinyconfig Linux kernel β mitigations off, frame-pointer unwinder, GNU
as+ld doing assemble/link β and the resulting vmlinux/bzImage boots
to init in qemu. The kernel is the hairiest GNU-C corpus in existence; the
point is the same as the FPC lighthouse
(goal-compile-fpc-compiler.md): conformance proven at industrial scale, this
time on the C/GNU side.
Strategic decisions (settled in the 2026-07-18 gap analysis)
These shape everything below; they are why the scope is bounded:
- Be a
.oproducer in the standard toolchain, not a whole-program compiler. The kernel is thousands of separately-compiled TUs linked by GNUldagainstvmlinux.lds. We do NOT reimplement that: emit object files (--emit-objalready exists), let the kernel's own Makefile,as, andld -Tdo their jobs. Bonus: per-TU bisection β mix gcc-built and pxx-built.os to isolate miscompiles. - Emit
.stext and let GNUasassemble (at least for this target). Kernel inline-asm strings are full of assembler directives (.pushsection .altinstructions,.popsection,.long, macros). With a text-asm emission path, inline asm reduces to string paste + operand substitution + constraint-driven register choice β the directives areas's problem, we never parse AT&T ourselves. - Config the hardening away. objtool/ORC, retpoline, kCFI, SLS, stack
protector are all off-able (
CONFIG_UNWINDER_FRAME_POINTER=y,CONFIG_MITIGATIONS=n,CONFIG_STACKPROTECTOR=n; kCFI is clang-only). A boot goal owes none of them.
Where the C frontend stands (2026-07-18)
Strong ISO-C + light-GNU base, proven on userspace corpora:
- zlib 1.3.1 compiles, program OUTPUT byte-identical to the gcc-built oracle
(
done/feature-c-corpus-zlib.md); SQLite and Lua compile and run; csmith differential fuzzing live (Track T); c-conformance 195/0 across x86-64, i386, aarch64, arm32, riscv32. - Working: designated initializers, compound literals,
_Generic(full structural type descriptors), bitfields incl. packed layout, VLAs, alloca, varargs, function-pointer declarator zoo, GNU statement expressions({...}),packed/alignedattributes,#pragma pack,__builtin_expect/clz/ctz/popcount/va_*. - Preprocessor: variadic macros,
__VA_ARGS__,##paste with rescan, include search paths, LP64 predefines.
The gap β everything the kernel needs that we lack
Ordered roughly easy β hard. Most items are legwork; one is a real subsystem.
Preprocessor (each ~an afternoon)
__COUNTER__β kernel headers won't preprocess without it (BUILD_BUG_ON,__UNIQUE_ID, lockdep).#include_nextβ kernel/compiler header wrapping._Pragma(...)operator;#pragma once.__VA_OPT__β used in newer trees.
Builtins (mostly trivial)
__builtin_offsetof,__builtin_unreachable,__builtin_choose_expr,__builtin_types_compatible_p,__builtin_memcpy/memsetintrinsics.__builtin_constant_pβ subtle one: kernel relies on it folding to 1 for constants to pick immediate-operand asm variants. Conservative always-0 compiles fine but takes slow paths β acceptable for boot.
Language / semantics (legwork, days each)
typeof/__typeof__β pervasive (container_of,min/max,READ_ONCE). Machinery mostly exists via_Generic's type descriptors.- Flexible array members (
type name[];trailing member). - Computed goto / labels-as-values (
&&label,goto *p) β BPF interpreter, some fast paths; tinyconfig may dodge most of it. _Atomicβ kernel uses its own asm-based atomics, so likely NOT needed for boot; noted for completeness.- Full pointer-depth β₯ 2 element typing (
not-implemented.mdC-interop gap). - Forced inliner for
__attribute__((always_inline))β no heuristics, no cost model, mechanical call-site substitution. Required for correctness, not perf: asm-bearing inline helpers need their"i"immediate constraints const-propagated. (There is currently NO inliner β IR pipeline does zero optimization.)
Attributes (mechanical once named sections exist)
__attribute__((section("...")))β the kernel image IS section attributes (__init, initcalls,__ksymtab,__ex_table). Currently onlypacked/alignedare honored; everything else silently dropped (cparser.incattribute skip).weak,alias,used,noinline,cold,constructor.
Object emission / toolchain (Track A; legwork given decision #1)
- More relocation types in the ELF
.owriter β currently only 4 (R_X86_64_PLT32/GLOB_DAT/RELATIVE,R_386_GLOB_DAT); needR_X86_64_64/PC32/32/32Sat minimum. - Arbitrary named sections β
elfwriter.inchardcodes.text/.data/.bss. - Weak/alias/local symbol binding in the symbol table.
.stext emission path next to the binary emitter (decision #2) β mechanical but real work; also independently useful (debugging, other targets).
THE wall β GNU inline asm constraint engine (weeks, the one subsystem)
asm/__asm__ is currently in the parser's statement skip list β zero
support. The kernel is saturated with it. With decision #2 the AT&T/directive
part vanishes; what remains is the genuinely hard core:
- Constraint letters and modifiers:
"r","m"(real address modes),"i"(needs const-prop / forced inlining),"=&r"early-clobber, matching constraints ("0"),"+m"read-write. - Clobber lists incl.
"memory"(a codegen barrier) and"cc". - Operand modifiers (
%b0,%w1,%z2) and symbolic operand names. - Integration with register assignment β the one place the kernel meets our codegen intimately.
asm goto(jump labels / static keys /__get_user) β asm with branch targets into C labels, incl. the output-operands variant. Moderate once the constraint engine exists.
Kernel codegen gates (bounded, Track A)
-mcmodel=kernelβ kernel lives at the top of the address space; sign-extended 32-bit addressing (R_X86_64_32S). Small if codegen is already RIP-relative.- Never emit SSE/x87 in kernel code (
-mno-sseequivalent) β kernel has no FP context. pxx does FP via SSE2; any helper/memcpy-style codegen touching XMM must be gated off. - No red zone (
-mno-red-zoneequivalent) β interrupts trash it. -ffreestandingmindset: no libc/crtl assumptions, no magic startup.
Explicitly out of scope (config'd away, per decision #3)
- objtool validation, ORC unwind tables, retpoline, kCFI, SLS, stack
protector, kernel modules (
=yeverything), non-x86-64 arches, non-tiny configs, unpatched-mainline purity (patching the kernel a little is fine β tccboot patched 2.4 heavily and still counted).
Acceptance ladder
- Preprocess: one kernel TU survives
pxx -E(preprocessor items). - Compile one TU: an allnoconfig/tinyconfig TU compiles to
.o(typeof, builtins, attributes, sections, relocs). - Link: full tinyconfig build where pxx compiles a growing subset of TUs, gcc the rest β per-TU mix is the bisection tool, not a compromise.
- All TUs:
CC=pxxfor every C file;as/lduntouched. - Boot: qemu +
earlyprintk=serial,ttyS0reaches init. THE bar.
Risk notes
- The listed features are not the tail β the tail is the thousand small divergences the kernel's macro soup will surface, same shape as the SQLite/csmith campaigns. The differential-fuzzing + agent-fleet loop that crushed those is the mitigation; it is proven.
- Debugging silent miscompiles pre-console is the nasty part: no stdout, triple faults. Tooling answer: qemu early serial, plus gcc/pxx per-TU bisection from ladder step 3.
- Velocity calibration: clangβkernel took ~a decade, but they insisted on zero kernel patches, all configs, all arches. tccboot booted a (patched) 2.4 kernel in 2004 with far less compiler than we have. At our measured pace the honest estimate is order 1β3 months of focused campaign, not years.
Relationship to other lighthouses
Sibling of goal-compile-fpc-compiler.md β same "conformance at industrial
scale" motive, C-side instead of Pascal-side. Several sub-goals here are
independently valuable regardless of the kernel: .s emission, more reloc
types, named sections, typeof, the preprocessor items, the forced inliner.