# DF-5596 — genassym.c does not emit GD_CURRENTLDT consumed by global.s (silent C↔asm contract break)

## What

`sys/platform/pc64/x86_64/genassym.c` (the ASSYM generator for the pc64
platform) never emits `GD_CURRENTLDT`, although:

- `struct mdglobaldata` still has the field:
  `sys/platform/pc64/include/globaldata.h:78` — `int gd_currentldt;`
- a **standard-built** assembly file still consumes the macro:
  `sys/platform/pc64/x86_64/global.s:66-67`
  ```asm
          .globl  gd_currentldt
          .set    gd_currentldt,globaldata + GD_CURRENTLDT
  ```
  (`sys/platform/pc64/conf/files:129` — `platform/pc64/x86_64/global.s standard`)

## Observed effect (proven on the shipped kernel, not simulated)

The assembler accepts `.set sym, <undefined>` and silently drops the symbol
definition; the kernel link (EXEC, `-Bdynamic --export-dynamic`, custom
`-T` ldscript) tolerates the resulting unreferenced undefined symbol:

```
$ nm /usr/obj/usr/src/sys/X86_64_GENERIC/global.o
                 U GD_CURRENTLDT        <-- dangling
$ nm /boot/kernel/kernel | grep ' U '
                 U GD_CURRENTLDT        <-- in the SHIPPED kernel
$ nm /boot/kernel/kernel | grep -c gd_currentldt
0                               <-- the promised .globl export silently DOES NOT EXIST
$ readelf -r /boot/kernel/kernel | grep -cE 'DMAP_|addr_P|GD_CURRENTLDT'
0                               <-- no relocation consumes it (why ld let it pass)
```

So the build succeeds and the boot works, but the genassym safety contract
("any struct change either regenerates matching offsets or breaks the
build") was silently defeated. The same mechanism leaves seven more
dangling undefined symbols from `locore.s:41-47`
(`loc_PTmap..loc_PML4pml4e`, `dmapbase`, `dmapend` — extern variables from
`machine/pmap.h` that nothing defines) — see VERDICT.md for the sweep.

## Reproduce (on the audit QEMU guest)

1. `sh build.sh` — compiles, with the exact kernel build flags:
   - pristine `genassym.c` → `genassym.o` → regenerated `assym.s`
     (byte-identical to the build tree's — `diff.buildtree_vs_regen.txt`),
   - `probe_gdcl.s` (consumes `GD_CURRENTLDT` exactly like `global.s:67`)
     against that assym → `nm` shows `U GD_CURRENTLDT` and **no**
     `gd_currentldt_probe` symbol: **the bug, reproduced under control**.
2. Fix validation: `genassym_fixed.c` (one added ASSYM line) → regenerated
   assym gains `#define GD_CURRENTLDT 0x42d4` (`diff.fix_delta.txt`) and the
   same probe now yields `00000000000042d4 A gd_currentldt_probe`
   (`nm_probe_fixed.txt`): **fixed**.
3. `offset_check.c` independently recomputes all 176 emitted values from
   the same headers as a separate TU — 176/176 match
   (`diff.assym_vs_checker.txt`; the only two extra lines are an artifact
   of the checker generator emitting the `#if 0` pair).

## Expected output

- `nm_probe_unpatched.txt`: `U GD_CURRENTLDT`, missing probe symbol.
- `nm_probe_fixed.txt`: `00000000000042d4 A gd_currentldt_probe`.

No panic, no memory corruption — this is a build-contract defect whose
security relevance is the proven silent-failure mode of the contract
enforcement itself (the DF-5586/DF-5335 exception.S family shows what
happens when this contract drifts in a field that *is* consumed).
