qld 0.1.1

A fast, parallel linker compatible with GNU ld, gold, lld and mold
Documentation
# Optimizations and symbol matching

## Garbage collection (tree shaking)

Enabled with `--gc-sections` (GNU flavor, including MinGW PE) or `-dead_strip`
(ld64). Off by default, as in the linkers qld replaces.

**Roots:** entry symbol · `-u`/`--undefined`/`--require-defined` · symbols
exported to the dynamic symbol table (`-shared`, `--export-dynamic`,
`--dynamic-list`) · `KEEP(...)` in linker scripts · `.init_array`, `.fini_array`,
`.preinit_array`, `.ctors`, `.dtors`, `.init`, `.fini` · `SHF_GNU_RETAIN` ·
non-`SHF_ALLOC` sections · `.note.*` · sections referenced through
`__start_<sec>`/`__stop_<sec>` (with lld's `-z start-stop-gc` semantics
available).

**Edges:** relocations from a live section, `SHF_LINK_ORDER` dependencies, and
section group membership (a live member keeps its whole COMDAT group alive).
Non-allocated sections are roots but contribute **no** edges: otherwise
`.debug_info` would keep every function it describes alive.

**Granularity:** the unit is the input section. ELF code needs
`-ffunction-sections -fdata-sections` to be removed per function. For Mach-O,
`MH_SUBSECTIONS_VIA_SYMBOLS` lets qld split sections into per-symbol atoms,
which is safe for that format. qld does not split ELF sections at symbol
boundaries, because nothing in the format guarantees that splitting is safe.

**Removed along with dead sections:**

- `.eh_frame` FDEs of dead functions, and CIEs no FDE uses any more
- GOT, PLT and dynamic symbol entries that only dead code needed
- `DT_NEEDED` entries under `--as-needed` when no live reference remains
- `.debug_*` references to dead code, which resolve to tombstones

**Diagnostics:** `--print-gc-sections`, and `--why-live=<symbol>` prints the
reference chain from a root. Marking walks relocations directly; the full
reference graph is built only when `--why-live` needs it.

## Identical code folding

- `--icf=all`: fold any sections with identical contents and equivalent
  relocations, even if their addresses are compared somewhere.
- `--icf=safe`: fold only sections whose address is not significant, according
  to `.llvm_addrsig`. Clang emits that table by default; GCC does not, so for
  GCC objects `safe` folds only read-only data and functions that are not
  address-taken.
- **Algorithm:** a parallel initial hash over contents, flags and relocation
  shapes, followed by iterative refinement: each round rehashes every section
  using the current equivalence class of its relocation targets, until no
  class splits. Within a class, the section that appears first in input order
  is kept, so the result is deterministic.
- `--print-icf-sections`.

## Merge sections

`SHF_MERGE` sections are split into pieces (NUL-terminated strings for
`SHF_STRINGS`, fixed-size entries otherwise) in parallel. Deduplication is lock-free: runs of input sections bucket
their live pieces by hash shard in parallel, then one task per shard fills
that shard's table in piece order, so the first occurrence always leads.
Offsets of groups without tail merging are assigned in parallel over runs of
sections, each laid out from 0 and shifted by its aligned start.

- `-O2` enables **tail merging** of strings (`"bar"` shares the storage of
  `"foobar"`). It costs a suffix sort per output section, so it is off at `-O1`.
- Splitting happens while objects are parsed, so relocations that point into
  merge sections can be expressed as (piece, addend-within-piece) during the
  relocation scan. Deduplication and offset assignment run after GC, and
  before ICF. Splitting stores each piece's hash; deduplication mixes it with
  the output group, confirms equality by bytes, and accepts an optional
  per-piece liveness bitmap so dead pieces take no space (piece-level GC is a
  later optimization).

## Relocation relaxation

qld rewrites instructions when the final layout allows it. `--no-relax`
disables this.

| Arch | Relaxations |
| --- | --- |
| x86-64 | `GOTPCRELX`/`REX_GOTPCRELX` → direct `lea`/`mov`/`call`/`jmp`; TLS GD/LD/IE/TLSDESC → LE (and GD → IE for shared libraries) |
| i386 | `GOT32X` → direct; TLS relaxations |
| AArch64 | TLS GD/LD/IE → LE, TLSDESC → IE/LE (done); ADRP+LDR GOT → ADRP+ADD and ADRP+ADD → ADR+NOP (not yet: they need relocation-pair lookahead, and GNU ld does not do them either) |
| RISC-V | `CALL` → `JAL`, `LUI`+`ADDI` → GP-relative, `ALIGN` handling; section sizes shrink, so layout iterates |
| LoongArch | PCALA/GOT/call relaxations (size-changing) |

## Dynamic relocation compaction

- `-z pack-relative-relocs`: `DT_RELR` for relative relocations
  (with `GLIBC_ABI_DT_RELR` version need)
- `-z combreloc` (default): sort `.rela.dyn` so the relative relocations come
  first, and emit `DT_RELACOUNT`
- `--hash-style=gnu` (default for new outputs) / `sysv` / `both`

## Section ordering

- `--symbol-ordering-file` (lld) and `--section-ordering-file` (gold)
- `--call-graph-profile-sort` using `.llvm.call-graph-profile` or a supplied
  call graph, with the C³ heuristic
- `.text.hot.*`, `.text.unlikely.*` and `.text.startup.*` grouping, as in
  GNU ld's default script (`-z keep-text-section-prefix`)

## LTO

The GNU flavor implements the **GNU linker plugin API** (`plugin-api.h`), the
interface GCC and Clang expect when they run the linker with
`-plugin <path>`:

- **GCC:** `liblto_plugin.so`, which runs `lto-wrapper`
- **LLVM:** `LLVMgold.so` (full LTO and ThinLTO)

**Flow:** plugins are loaded when the first IR input appears, so a link with
no IR loads nothing and is byte-identical with or without `-plugin`. IR is
recognized while objects are parsed (LLVM bitcode magic, or `.gnu.lto_*`
sections in a GCC object). Inputs are claimed inside the resolution rounds,
in input-position order, so claims are deterministic; the claimed symbols
take part in resolution exactly like an object's, and comdat keys deduplicate
IR against native copies. When resolution settles, qld reports each symbol's
status (`LDPR_PREVAILING_DEF_IRONLY`, …) through `get_symbols`. The plugin
compiles the IR (`all_symbols_read_handler`) and adds native objects
(`add_input_file`). Resolution then runs a **second** time with those objects
in place of the claimed files, keeping earlier archive extractions live;
libraries the plugin asks for are appended. Plugin cleanup runs after the
output is written, since it deletes those objects.

A GCC "fat" object (`-ffat-lto-objects`) links as native code when no plugin
claims it. Other IR without a plugin is an error naming the file and the
driver that would supply the plugin.

**Pure Rust note:** building qld needs no C code. The plugin itself is native
code that the compiler toolchain provides, and it is loaded with `dlopen` at
run time, only when `-plugin` is given. This lives in `src/plugin/` behind the
`plugin` cargo feature. That feature is the only place qld uses FFI.

**Environment and options.** GCC's plugin reads `COLLECT_GCC` and
`COLLECT_GCC_OPTIONS`, which `collect2` sets when `gcc` runs the linker;
like GNU ld, qld inherits them and never sets them. Running
`qld -plugin liblto_plugin.so` by hand needs them, plus the `lto-wrapper`
path and `-fresolution=` options collect2 passes. Some LLVMgold options end
the process from inside the plugin (`thinlto-index-only`, `emit-llvm`,
`emit-asm`, `disable-output`), exactly as under GNU ld.

**Lifetime.** Plugins are never unloaded (LLVMgold registers destructors
and GCC's plugin keeps process state), and each plugin library can be used
by one session per process.

The ld64 flavor uses `libLTO` (`-lto_library`) through a thin adapter in the
same crate.

Because the plugin API is a C callback interface with global state, a
process can run only one plugin-based link at a time. The library API returns
an error if a second one is attempted concurrently.

## Intelligent library symbol matching

"Intelligent library symbol matching" covers these features:

1. **Order-independent archive resolution.** Archive order on the command line
   does not affect whether a symbol resolves; precedence is still decided by
   input order. See [compatibility.md]compatibility.md#archive-resolution-order.
2. **Missing-library suggestions.** When a symbol stays undefined, qld looks it
   up in the `.dynsym` and `.gnu.hash` tables, or the archive symbol indexes,
   of libraries in the `-L` paths that were not linked:
   ```
   qld: error: undefined symbol: cos
   >>> referenced by main.c:12 (main.o:(.text.main+0x1a))
   >>> note: 'cos' is defined in libm.so.6 (/usr/lib64/libm.so); did you forget -lm?
   ```
   The index of search-path libraries is built lazily, only after an error has
   already happened, so successful links pay nothing for it. It reads each
   shared library's `.dynsym` and each archive's symbol index, follows
   `GROUP`/`INPUT` linker scripts, and takes about 20 ms for all of
   `/usr/lib64` on this project's development machine. Suggestions prefer
   earlier search paths, then shared over static, and name the flag that
   would actually find the file (`-lm`, else `-l:file`, else the path). A
   library that is on the link line but was dropped by `--as-needed`, or
   linked static-only, gets its own explanation.
3. **Version-aware matching.** A reference to `memcpy@GLIBC_2.14` against a
   library that only provides `memcpy@GLIBC_2.2.5` gets a message that names
   the versions available, not a bare "undefined symbol".
4. **Near-miss hints.** qld suggests candidates that differ only in C++
   mangling (a C/C++ `extern "C"` mismatch), in namespace, in
   const/volatile/reference qualifiers, or in a leading underscore (a Mach-O
   or i386 COFF convention error). Names are shown demangled (Itanium and Rust
   v0/legacy) unless `--no-demangle` is given.
5. **Duplicate definition explanations.** When two definitions collide, qld
   reports which archive member was pulled in and which reference caused the
   extraction.