kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

commit 5d4228cb2de9464069e87e01ca86b62df9ca2b0a
parent 45d883cd4bfea07e86ecb7bb678d74d461d708d1
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Sun, 14 Jun 2026 13:15:59 -0700

doc: consolidate linker compatibility roadmap

Merge the ELF as-needed and TLSDESC planning into a single support-set roadmap for system-linker compatibility.

Diffstat:
Adoc/plan/LINKER-COMPAT.md | 240+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdoc/plan/README.md | 1+
2 files changed, 241 insertions(+), 0 deletions(-)

diff --git a/doc/plan/LINKER-COMPAT.md b/doc/plan/LINKER-COMPAT.md @@ -0,0 +1,240 @@ +# System-linker compatibility + +This roadmap tracks the remaining linker work needed for `kit ld` to act as the +system linker over kit's supported target set. The near-term driver is Rust +using `kit ld` through `rustc`, but the target is broader: C, Rust, assembler +objects, archives, DSOs/dylibs/import libraries, PIE executables, shared +libraries, and relocatable links should all behave like the platform linker for +the supported arch/OS pairs. + +Related: [LINKER.md](LINKER.md), [../LINK.md](../LINK.md), +[../OBJ.md](../OBJ.md), [../DRIVER.md](../DRIVER.md), +[SYSROOTS.md](SYSROOTS.md). + +## Scope + +The support set is format-driven: + +- Mach-O: macOS `aarch64` and `x86_64`. +- ELF: Linux glibc/musl `aarch64`, `x86_64`, `riscv64`; FreeBSD where Rust and + sysroot artifacts exist; freestanding ELF for `aarch64`, `x86_64`, `riscv64`, + and `riscv32`. +- COFF/PE: Windows UCRT MinGW (`*-pc-windows-gnullvm`) for `x86_64` and + `aarch64`. + +The linker should preserve format boundaries. ELF `--as-needed` and TLSDESC +rules must not leak into Mach-O dylib or COFF import-library semantics. COFF's +archive search behavior, Mach-O atom/GOT/TLV behavior, and ELF dynamic-section +rules stay target-specific policy behind shared object and relocation APIs. + +## Current state + +The Rust-driven compatibility pass established these paths: + +- `kit ld` accepts the common system-linker flags Rust passes, including GNU + `-Wl,`/`-z` forms, target/sysroot flags, linker scripts, rlibs, exact + `-l:name` libraries, `-static-pie`, `-nodefaultlibs`, hosted/no-startfile + combinations, PE/MinGW flags, and Mach-O version/platform flags. +- Rust std links through `kit ld` for macOS `aarch64`/`x86_64`, Linux glibc + `aarch64`/`x86_64`/`riscv64`, Linux musl `aarch64`/`x86_64`/`riscv64`, + FreeBSD `x86_64`, and Windows UCRT MinGW `aarch64`/`x86_64`. +- Rust no-std links through `kit ld` for `aarch64-unknown-none`, + `x86_64-unknown-none`, `riscv64gc-unknown-none-elf`, and + `riscv32imac-unknown-none-elf`. +- ELF symbol-version imports can resolve explicit `name@VERSION` references, + which FreeBSD Rust uses for libc compatibility symbols. +- COFF archive libraries use global fixed-point search, matching the behavior + Rust/MinGW expects from the library set. +- RISC-V TLS-GD and x86_64 TLS-LD can relax local executable TLS cases; AArch64 + ELF TLSDESC relocation spellings are accepted and currently relax only for + local defined TLS. + +This is a useful system-linker subset, not completion. The open work below is +the gap between "Rust examples link" and "the platform linker can be replaced +over the support set." + +## Workstreams + +### 1. Ordered input and dependency selection + +ELF `--as-needed` must become real linker semantics, not a driver/script +special case. + +The linker needs to model, per ordered DSO input: + +- current link mode (`default`, `--as-needed`, `--no-as-needed`), +- selected/not-selected state, +- why it was selected, +- SONAME or fallback identity for `DT_NEEDED`, +- the exports made available to later inputs. + +ELF behavior: + +- default/no-as-needed DSOs are selected as explicit dependencies, +- as-needed DSOs are selected only when they satisfy an eligible strong + undefined reference at their position, +- unselected as-needed DSOs do not satisfy later references, +- selected DSOs are the source of `DT_NEEDED`, +- duplicate SONAMEs are suppressed while preserving first selected order. + +GNU linker scripts should lower into the same ordered input model: + +- `INPUT(...)` inserts normal inputs with the current mode, +- `GROUP(...)` preserves archive group behavior, +- `AS_NEEDED(...)` temporarily pushes as-needed mode for nested DSO or `-l` + tokens, +- nested scripts inherit and restore mode correctly. + +Mach-O and COFF need separate parity checks rather than ELF semantics: + +- Mach-O dylib load commands and weak/imported symbol behavior should follow + Darwin linker expectations. +- COFF import libraries, auto-imports, weak externals, and library-set archive + search should remain COFF-specific. + +### 2. ELF TLS model planning + +ELF TLS must be planned before final relocation application and dynamic-section +synthesis. The planner should classify each TLS relocation or relocation +sequence by: + +- symbol locality and visibility, +- imported/preemptible vs. non-preemptible, +- weak undefined behavior, +- executable/PIE/shared/relocatable output, +- static vs. dynamic link, +- target ABI TLS variant and thread-pointer bias. + +The action should be explicit: + +- relax to local-exec, +- synthesize initial-exec, +- preserve or synthesize dynamic TLS/TLSDESC, +- preserve relocations for `-r`, +- reject with a precise diagnostic. + +This pass should own the decision; architecture code should own only the +instruction byte rewrite once a sequence has been validated. + +### 3. TLSDESC completion + +The current AArch64 TLSDESC path accepts Rust/LLVM executable-local cases by +relaxing the four instruction relocations to materialize the local-exec offset. +Full TLSDESC support needs more: + +- complete per-architecture relocation surfaces in `RelocKind`, ELF mapping, + relocation names, descriptor metadata, object read/write, and `objdump`; +- descriptor GOT/data allocation, normally two machine words per unique + `(symbol, addend)` descriptor; +- TLSDESC dynamic relocation emission in `.rela.dyn`; +- integration with `.dynsym`, symbol versions, `DT_NEEDED`, PIE address shifts, + section GC, and DSO selection; +- checked sequence relaxation for AArch64, then x86_64, then other ELF + architectures as ABI support lands; +- shared-library output that preserves or synthesizes dynamic TLS where final + executable layout is not known. + +Imported TLSDESC references are real DSO uses and must select their provider +under `--as-needed`. + +### 4. Architecture relocation and relaxation parity + +Each supported architecture needs both relocation coverage and checked +relaxations for the code shapes produced by modern toolchains. + +Open areas include: + +- AArch64 ELF: complete TLSDESC dynamic path, validate local sequence + relaxation as a unit, cover host-toolchain GOT/TLS/unwind relocation shapes. +- x86_64 ELF: complete TLS-LD/GD/IE/LE relaxation behavior and add TLSDESC + once modeled. +- RISC-V ELF: keep expanding TLS-GD/LD pattern coverage conservatively, handle + compressed-instruction variants, and preserve dynamic TLS paths where local + relaxation is illegal. +- Mach-O AArch64/x86_64: finish GOT/TLV/unwind relocation parity against + Apple-produced objects and static archives. +- COFF AArch64/x86_64: continue matching MinGW/LLVM relocation spellings, + especially SECREL, unwind, pdata/xdata, and CRT object patterns. + +Relocation tests should pin both the descriptor table and representative byte +patches so enum additions are covered immediately. + +### 5. Shared libraries and relocatable links + +Replacing a system linker requires more than executable links: + +- `-shared` should emit correct dynamic symbols, relocations, SONAME/install + names/import tables, and TLS behavior for each format. +- `-r` should preserve relocations and avoid making final TLS/layout decisions. +- PIE and non-PIE executable behavior must stay distinct. +- Copy relocations, protected visibility, weak imports, COMDAT/section groups, + init/fini arrays, unwind tables, and build IDs must compose with GC and + dynamic linking. + +The near-term Rust path mostly exercises final executables; shared-library and +relocatable coverage should be broadened deliberately. + +### 6. Runtime/sysroot interoperability + +System-linker replacement depends on finding the same runtime inputs as the +platform toolchain: + +- crt objects and default startup ordering, +- libc/libm/libpthread/librt/libdl/libutil and libc-specific linker scripts, +- compiler builtins and unwind libraries (`libgcc_s`, compiler-rt, Rust + `compiler_builtins`), +- platform dynamic loaders/interpreters, +- MinGW UCRT CRT and import libraries, +- FreeBSD versioned libc and auxiliary runtime libraries. + +The driver should prefer explicit command-line inputs, then sysroot/target +layout, and finally host defaults only where that is safe. Runtime validation +should distinguish linker failure from a too-minimal test container, such as an +Alpine image missing `libgcc_s.so.1`. + +### 7. Diagnostics and tracing + +The linker needs precise diagnostics for unsupported system-linker cases: + +- target, input file/member, symbol, relocation kind, and output mode; +- why a DSO was selected or skipped; +- which symbol caused an as-needed DSO to be kept; +- why a TLS access cannot be relaxed or represented dynamically; +- which runtime library or sysroot path was searched. + +Use `KIT_TRACE` for opt-in structured tracing. Do not add global state. + +## Validation matrix + +For each supported arch/OS/libc family, track these lanes: + +- Rust std executable link, +- Rust std runtime execution where a runner exists, +- Rust no-std/freestanding link, +- C/assembler object corpus link, +- archive order and group behavior, +- DSO/dylib/import-library dependency selection, +- TLS local and imported access, +- shared-library output, +- relocatable `-r` output, +- debug/unwind preservation. + +The first priority remains the verified Rust support set, because it is a good +system-linker driver and produces real toolchain objects. The second priority is +small focused fixtures for every feature above, so failures are explainable and +do not depend on large external runtimes. + +## Acceptance criteria + +`kit ld` is complete enough to be the system linker for a target when: + +- Rust and C toolchains can use it directly with only target/sysroot/runtime + configuration, not target-specific wrapper rewrites; +- executables and shared libraries run under the target runtime loader; +- `readelf`/`otool`/`llvm-readobj` dynamic metadata matches the platform + linker's dependency and relocation shape for covered cases; +- relocatable links preserve the relocation surface correctly; +- local TLS relaxations are legal and checked, and dynamic TLS is emitted when + required; +- archive/DSO/import-library selection follows target format semantics; +- negative cases fail early with actionable diagnostics. diff --git a/doc/plan/README.md b/doc/plan/README.md @@ -12,6 +12,7 @@ shrinks to whatever remains open. | [OPTIMIZER.md](OPTIMIZER.md) | Completing the O2 SSA mid-end, expanded inlining, -O0/-O1 performance work, machine register-constraint improvements. | [../OPT.md](../OPT.md) | | [PERF.md](PERF.md) | Making kit the fastest `-O0` C compiler with code as dense as tcc: current compile-speed + code-size standings, how to reproduce them (macOS instruction counts, Linux callgrind, the `make bench-cc` scaling guard), and the ranked forward-looking levers on both axes. | [../ARCH.md](../ARCH.md) | | [LINKER.md](LINKER.md) | Incremental linking: the file-based object-link redesign and remaining non-ELF format coverage. | [../LINK.md](../LINK.md) | +| [LINKER-COMPAT.md](LINKER-COMPAT.md) | Completing system-linker compatibility across the support set: ordered DSO selection, ELF TLS/TLSDESC, shared libraries, relocatable links, runtime/sysroot interoperability, and Rust/toolchain validation. | [../LINK.md](../LINK.md), [../OBJ.md](../OBJ.md), [../DRIVER.md](../DRIVER.md) | | [JIT.md](JIT.md) | Function-level hot reload, Go-runtime-style codegen support, and remaining JIT host-portability work. | [../JIT.md](../JIT.md) | | [DEBUG.md](DEBUG.md) | The Windows debugger host adapter, x64/rv64 displaced single-step, profiling, and DWARF gaps. | [../DBG.md](../DBG.md), [../DWARF.md](../DWARF.md) | | [INSTRUMENT.md](INSTRUMENT.md) | Debugging/profiling tools in the Valgrind/callgrind/asan/ubsan tradition: the emulator as a dynamic-binary-instrumentation substrate (the `EmuToolHooks` ABI, shadow planes, the memory seam, a guest debugger), plus the cross-frontend compiler-sanitizer story. | [../EMU.md](../EMU.md) |