Skip to content

Link everything that boots with rust-lld, and freeze toyos-ld - #532

Merged
Japabu merged 29 commits into
mainfrom
wt/toyos-lld
Sep 27, 2026
Merged

Japabu merged 29 commits into
mainfrom
wt/toyos-lld

Conversation

@Japabu

@Japabu Japabu commented Sep 26, 2026 •

Copy link
Copy Markdown
Collaborator

Milestone M0 of the toolchain strategy: everything that boots links with rust-lld, and toyos-ld is frozen.

What changed, and why

The linker.

  • Both ToyOS targets link with rust-lld, flavor Gnu(Cc::No, Lld::Yes), and pass -Bsymbolic. All three are declared once in base::toyos; neither x86_64-unknown-toyos nor aarch64-unknown-toyos overrides them. The pin is ToyOSOrg/rust 209f10ded88 on wt-toyos-lld:
    • 3980fb86829 merges main's pin c34ecdf0ab6 into this branch's a84b4aa849c, and nothing else of toyos-inbox's;
    • 209f10ded88 moves the three settings into base::toyos;
    • toyos-inbox carries it as 8ae98485fb8.
  • -Bsymbolic: toyos-ld bound a shared object's references to its own definitions. lld routes them through the PLT, and the loader resolves a dlopened object's slots only against the other objects in the process. On x86-64, five dlopen/TLS/unwind tests jumped through unbound slots without it. AArch64 had rust-lld without it, so it had the same defect; it now has -Bsymbolic from the base. Both architectures' init carry DT_FLAGS SYMBOLIC (llvm-readobj --dynamic-table).
  • kernel/ and bootloader/ drop linker = "toyos-ld". Their upstream targets already name rust-lld.
  • The bootloader adds /Brepro and /DEBUG:NONE. Without them, one tree built into two target directories gave two images differing in 20 bytes: a clock stamp, and a PDB GUID that hashes target-directory paths.
  • The toolchain follows ARM64 port, stages 0 to 3: shared groundwork, aarch64-unknown-toyos with std and the userland, and the loader and kernel reach the PL011 on virt #524's write_config shape:
    • Arch::ALL guest targets, each with linker = "rust-lld" and rpath = false;
    • HOST_LINKER_PIN;
    • lld = true, which puts rust-lld in every stage's sysroot;
    • the hosted build under lld = false, then the host-only reassembly with its rust-lld assertion.
  • The hosted build. Its sysroots carry no rust-lld, so it names ci-llvm's lld by path for the hosted architecture only. aarch64-unknown-toyos links nothing during it.
  • The hosted build's archiver. It also names ci-llvm's llvm-ar, which bootstrap hands to cc-rs as AR_<target>.
    • Nightly run 36281465192's portability-macos failed linking the hosted rustc-main: librustc_driver.so left seven blake3 SIMD symbols undefined, which lld refuses (--no-allow-shlib-undefined). portability-linux linked the same build.
    • Cause: blake3 assembles its x86-64 code with cc-rs and archives it with the target's ar. macOS's ar indexes only Mach-O members, so lld pulls none of the ELF ones.
    • Measured here with blake3 1.8.2 built into an x86_64-unknown-toyos dylib: /usr/bin/ar leaves the same seven symbols undefined, and ci-llvm's llvm-ar leaves none.
    • toyos-ld never checked a shared object's undefined references, so every macOS-built hosted rustc was linked with these unresolved.
  • Why the nightly failed. Run 36260357158 built std for x86_64-unknown-toyos with cc -flavor gnu. The branch had dropped the target's linker line, and bootstrap then passes cc. Every userland target now names its linker.
  • std_config(compiler, …) names the resolved compiler's rust-lld by path, for every architecture. sysroot::RECIPE moves to …, linked by rust-lld; 3.
  • Arch::links_through_toyos_ld is deleted, since no architecture links through toyos-ld. assert_toolchain_is_honest refuses a toolchain without rust-lld, by name.

toyos-ld's other duties.

  • The allocator shim: the C tests link a libc staticlib with rust-lld, and the staticlib carries rustc's shim.
  • The libc note: toyos-cc no longer emits .note.toyos.libc. Doom's build script names toyos_c.
  • Crosses the brief's fence: userland/libc/src/memory.rs no longer defines malloc/free/calloc/realloc under std-runtime, and calls std's instead.
    • std defines the same four, and rust-lld refused the duplicate in doom.
    • toyos-ld had silently kept std's: the disassembly showed doom's calloc was std's saturating one.
    • The reviewer judged this correct for linking. Its debt, two C allocators, is issues/design-debt/std-calloc-overflow-writes-past-a-fifteen-byte-block.md, which now exits only on one C allocator.

toyos-ld, frozen.

  • The host no longer builds or ships it:
    • it is out of PUBLISHED, the release tag's TREES and the tarball;
    • it is publish = false.
  • It stays a [programs] guest binary, because the in-guest toyos-cc links through it until LLD runs on ToyOS.
  • Two things in it stopped being true, and both are dealt with:
    • TLS executables are refused. It resolves an executable's local-exec accesses as sym - (tls_start + memsz) with p_align 64. The loader now ends that block at round(memsz, p_align). LinkError::TlsExecutable refuses such an executable by name. A shared object's TLS links as before, and toyos-ld/tests/tls.rs holds all three cases.
    • The libc note's auto-link is deleted. Its implicit -ltoyos_c for .note.toyos.libc objects can never fire, because nothing emits the note any more.
  • crates.io is the owner's: 0.2.0 is yanked, and 0.3.0 is yanked by hand.

Defects the switch exposed (boot chain).

  1. RELR refusal. toyos_elf::SectionTable::rela_sections is the loader's only way to the SHT_RELA sections it applies. It refuses a table that also names SHT_REL or SHT_RELR (Unapplied, by name). Before, the loader applied no relocations and jumped.
  2. Kernel stack alignment. toyos_elf::StackedImage::place starts the stack on the next page after vaddr_max. ARM64 port, stages 0 to 3: shared groundwork, aarch64-unknown-toyos with std and the userland, and the loader and kernel reach the PL011 on virt #524 landed the same rounding inline in the loader; there is now one copy, in the pure crate.
    • At this head the kernel's vaddr_max is 0x1f9740 + 0x21b148 = 0x414888, which is off 16 by 8. The fast tier's boot log reads stack 0x415000+0x800000.
    • toyos-ld ended images on a page. lld does not.
  3. The loader's own image.
    • lld-link's default ImageBase is 0x140000000. OVMF honours it on a 4 GiB guest, which puts the image above the boot map.
    • toyos_bootmap::Plan::new(scanout, loader, typing) maps the loader's image, read off LoadedImage, on both architectures. It is counted against one directory budget with the scanout, and a GiB both need is counted once. A loader no map can hold is refused by name.
    • On AArch64 the switch runs in the kernel. There the mapping is carried for one shape across both architectures, not because a fetch needs it.
  4. Executable TLS placement. The psABI places variant II's executable block at tp - round(p_memsz, p_align). toyos_elf::tls::exe_extent is that rounding, and build_tls_layout places the executable by it, under variant II only. std_tls declares its alignment by its own type.

DWARF. Every guest profile strips DWARF at the link (strip = "debuginfo"). Cargo drops a profile's strip for a package with an override of its own, so toyos-cc and toyos-ld state it again. The bootloader's line is gone: it did nothing for lld-link, and /DEBUG:NONE does the work.

Cranelift retired as the north star. issues/build/toyos-cc-has-never-compiled-tcc.md is closed as superseded. The hosted-rustc configuration says Cranelift is interim.

Merging main (#524)

b92b5454 merges 5e446e5c. Every conflict carries both sides:

  • src/toolchain.rs, src/sysroot.rs, src/arch.rs, src/build.rs and src/release.rs: as above. GuestEnv has no PATH, and the tarball has no toyos-ld witness.
  • kernel/src/loader/tls.rs, toyos-elf/src/tls.rs: main's variant I/II layout, with exe_extent in variant II.
  • toyos-bootmap: main's typed Plan, with the loader as a third input.
  • bootloader/src/main.rs: main's enter_kernel, and the loader image read into the Plan.
  • kernel/src/arch/x86_64/paging.rs: main's. Its WindowProt comment moved to kernel/src/mm/policy.rs and takes this branch's wording there.

Round 1, finding by finding

  • BLOCKER, the final compiler never ran. It is built here through ARM64 port, stages 0 to 3: shared groundwork, aarch64-unknown-toyos with std and the userland, and the loader and kernel reach the PL011 on virt #524's per-worktree path: key dd66c3b297d9d946, Build completed successfully in 0:11:37. Every guest number below comes from it. The review's re-runs are in Gates.
  • BLOCKER, no T14 reading. The orchestrator's T14 run 147 (comment above) booted the pre-merge rust-lld image with no panic or fault. A new case is staged from this head (Metal).
  • BLOCKER, the stack fix survived its revert. The placement is now StackedImage::place. the_stack_after_an_image_is_aligned_wherever_the_image_ends places a stack after an image ending at 0x410008. The review's patch reds it (Two checks). The head's own kernel ends at …888, so the fix is also live.
  • BLOCKER, no RELR test. The loader cannot reach its SHT_RELA sections except through rela_sections, so its refusal cannot be deleted and the loop kept. a_relocation_form_a_rela_loader_would_not_apply_is_refused crafts SHT_RELR and SHT_REL tables. Deleting the SHT_RELR arm reds it.
  • BLOCKER, linker settings in one arch. Moved to base::toyos, as above.
  • NOTE, 7 vs 6 <&T as Debug>::fmt slots. It does not reproduce at this head. This branch's kernel sources were linked twice with one compiler: by rust-lld, and by a host toyos-ld through CARGO_TARGET_X86_64_UNKNOWN_NONE_LINKER.
    • lld's kernel has 1071 R_X86_64_RELATIVE slots whose target is a function (llvm-readobj -r, llvm-nm). Keyed by every alias at the target, they reach 1052 names.
    • No name has more slots under lld than under toyos-ld (4283 slots, 1799 names). There are no seven sites to name.
  • NOTE, calloc. The issue exits on one C allocator.
  • NOTE, TLS in the frozen toyos-ld. Refused, as above.
  • NOTE, the libc note. The dead auto-link is deleted.
  • NOTE, bootloader strip. Deleted.
  • NOTE, toyos-bootmap:186. The double count past MAX_DIRECTORIES is gone. fresh_directories counts the scanout's and the loader's GiBs as two intervals less their overlap, and a test holds the shared case.
  • NOTE, check_installed_toolchain. It already refuses a missing rust-lld: it calls assert_toolchain_is_honest, which asserts it.
  • NOTE, kernel/src/main.rs:393. Filed as issues/kernel/the-kernel-reserves-its-stack-offset-as-a-physical-region.md.
  • NOTE, the stale citation. issues/build/every-worktree-builds-its-own-copy-of-the-same-crates.md no longer cites toolchain::toyos_ld_binary.
  • REMOVE, all done:
    • tests/common/compile.rs's spec copy;
    • bootloader/Cargo.toml's DWARF comment;
    • toyos-elf/tests/tls.rs's provenance line;
    • toyos-bootmap's "four numbers in";
    • this body's wrong pin sentence and the done yank step.

Gates, each with its exit code

The head is 65e389ae. a7f423b5 is the hosted build's archiver, which no local build runs, and the commits after it add only issue text. Gates run at e4317d3f unless marked; e4317d3f to 845b2abe adds only issue files.

  • cargo run -- --build-only: EXIT=0, at 845b2abe. The first run this round, EXIT=0 too, built the worktree's compiler and sysroot 147c42b970843c4b.
  • cargo run -- --arch aarch64 --build-only: EXIT=0.
  • .comment on every guest ELF, at 845b2abe: Linker: LLD 22.1.8 in 26/26. These are the 25 binaries on ROOT, whose sizes match the build's root: adding lines, and the kernel.
  • cargo test --test toyos-build -- std_: 14 passed, EXIT=0. That is std_tls, std_tls_cranelift, std_tls_dlopen, std_tls_multi_crate, std_unwind, std_unwind_so and the other eight.
  • cargo test --test toyos-build -- dlopen: 2 passed, EXIT=0.
  • cargo test -- virt_: 3 passed, EXIT=0.
  • Reproducibility. One checkout built into two target directories, with the sysroot's toolchain and --profile toyos: cmp EXIT=0 for each of kernel, bootloader and snake.
    • sha256: kernel a606b4e4…, bootloader 310a8eac…, snake f9e3e8eb….
    • The kernel is byte-identical to the one --build-only put in the image.
  • cargo run -- --ci host: 45 steps, all green, EXIT=0, at e4317d3f and again at a7f423b5. The whole output was read each time. Its FAILED lines are the loom controls' expected reds, and its error:/fatal: lines are the worktree tests' planted refusals.
  • cargo run -- --ci abi-split: EXIT=0 ("this branch changes none of the published crates").
  • The fast tier at a7f423b5, cargo test: 399 passed, 2 failed, 7 quarantined and green, EXIT=1. The host's load averages read 5.7, 6.2 and 14.4 as it started. --known-red answers NO for both reds:
    • lan_mdns_answer: path must be shorter than SUN_LEN, main's issues/build/a-lane-s-tap-socket-path-is-past-sun-len-on-the-dev-host.md. This branch's duplicate of that issue is deleted.
    • quiesce_wakes_on_the_last_park: QEMU died before ===READY=== after Rebooting., with the uart holding nothing at all. It was ALONE: GREEN. That is issues/build/quiesce-wakes-on-the-last-park-lost-its-serial-ready-beside-other-guests.md's signature, from before this branch, and the recurrence is appended there.
    • That red's console is the merged loader at the head, booted on OVMF above 4 GiB: Loader image: 0x140000000+0x55000, mapped at identity as 0x140000000+0x200000, then Applied 5353 relocations and the kernel's boot.
  • The fast tier at e4317d3f: 399 passed, 2 failed, EXIT=1, at load averages 39.8, 31.1 and 37.5 from other worktrees. The reds were lan_mdns_answer, as above, and swap_netd.
    • swap_netd failed on the stream's redial was turned away 64 time(s), with init's words on netd ending at accepted, and was ALONE: GREEN.
    • That signature is in issues/build/parallel-tests-red-under-other-suites.md from before this branch, and the recurrence is appended there.
    • The branch touches neither netd, swap nor init.
  • Host crates, run directly: toyos-elf, toyos-bootmap and toyos-ld cargo test, EXIT=0 each.
  • The nightly at e4317d3f (run 36281465192): build success (the toolchain published with rust-lld, the hosted rustc linked on Linux), host success, portability-linux success. portability-windows failure, the declared continue-on-error frontier.
    • portability-macos failed linking the hosted rustc-main. That is the archiver defect above, fixed in a7f423b5.
    • Every guest, tcg and audio lane is red on main's nothing left in $TMPDIR: … toyos-toolchain.tar.zst.
    • Suite reds that main's nightly carries (ARM64 port, stages 0 to 3: shared groundwork, aarch64-unknown-toyos with std and the userland, and the loader and kernel reach the PL011 on virt #524's body): home_budget_refusal_retried, fpu_isolation, metal_job_reboot, usb_transport_break and screen_diag_boot.
    • handle_transfer is quarantined for another signature, and was ALONE: GREEN: a rate.
    • exit_wait_storm: the kernel accounted code 0 twice and one code not at all. Main's nightly at fd62f567 (run 36278449733) carries the same signature. Locally it is red 3 of 3 alone and green in both wide fast tiers.
    • guest (1)'s C-corpus shared boot could not be built: src/image.rs:77 ran out of space on the second ROOT format, so its 50 tests failed without a guest. Filed as issues/build/a-root-image-rebuilt-at-its-used-blocks-can-run-out-by-one.md. The defect is main's sizing code; this branch's binary sizes are what reach it.
  • The nightly at a55d62c6 (run 36287592139, https://github.com/ToyOSOrg/ToyOS/actions/runs/36287592139). The head, 65e389ae, adds one issue file after it.
    • Green: build (the toolchain reused from run 36281465192's publication), host, portability-linux, and portability-macos, whose hosted rustc now links with llvm-ar.
    • portability-windows: failure, the declared continue-on-error frontier.
    • Every guest, tcg and audio lane is red on main's nothing left in $TMPDIR: … toyos-toolchain.tar.zst. Lanes 4, 5, 6 and 8 and tcg have green suites besides.
    • Suite reds that main's nightly at fd62f567 (run 36278449733) carries on the same signatures:
      • quiesce_leaves_the_volume_whole, exit_wait_storm (guest 1);
      • metal_job_reboot, usb_transport_break (guest 7);
      • screen_diag_boot (guest 9);
      • log_flush_retry (guest 10);
      • fpu_isolation, home_budget_refusal_retried (guest 12).
    • audio (2): gate A, audio_tone_load.smp1 wake lateness median 5765 -> 17592 µs. Main's nightly at e8d7c9c0 (run 36228604597) failed the same statistic (median 41087). Run 36281465192 passed it on the same guest binaries.
    • Rates (red, then ALONE: GREEN): xhci_flap (guest 1), quiesce_wakes_on_the_last_exit (guest 3), update_grant_refuses_a_stray_partition (guest 7), job_deadline_reboots (guest 11). All four are green on main's fd62f567.
    • blockd_survives_its_death (guest 2), red wide and alone.
    • guest (1)'s C-corpus shared boot, in both runs: src/image.rs:77, NoSpace { requested: 1, available: 0 } on the second ROOT format, so the 47 tests on that boot (C-corpus tests and debug_float) failed without a guest. Alone, each was green.
      • The sizing code is main's, and main's corpus packs differently (59 binaries, 63 MiB). This branch's rust-lld binaries are what reach it (47 binaries, 40 MiB).
      • Filed as issues/build/a-root-image-rebuilt-at-its-used-blocks-can-run-out-by-one.md. Landing this makes guest (1) red on main until that is fixed.

The two checks (high risk: the boot chain and the ABI shape)

Negative controls. Each was applied as a checked patch, built (cargo test --no-run EXIT=0), run and restored. The tree is clean after each.

control test EXIT
StackedImage::place without the page rounding (the review's patch) toyos-elf --test crafted 101
rela_sections' SHT_RELR arm deleted toyos-elf --test tables 101
exe_extent returning memsz toyos-elf --test tls 101
the loader's pages left out of Plan::entries toyos-bootmap --test plan (4 tests) 101
toyos-ld's TlsExecutable refusal made a no-op toyos-ld --test tls 101

These are earlier on this branch and unchanged by this round, each booted:

  • the whole RELR refusal reverted, with a kernel linked --pack-dyn-relocs=relr: Applied 0 relocations, then #UD;
  • a5adb888 reverted, booted at 4 GiB: #PF I:1 RIP 0x14000CE8A;
  • the stack unrounded, at vaddr_max 0x410008: #GP in fpu::self_check;
  • no TLS extent: std_tls red.

Independent oracles.

  • LLD is an independent implementation, compared against toyos-ld linking the same kernel sources with the same compiler. The function-pointer comparison is above.
  • The loader's own decoder (toyos_elf::Layout::parse) accepts every lld output.
  • llvm-readobj independently reads the .comment, the dynamic flags and the relocations.
  • The T14 (run 147) for the loader above 4 GiB on real firmware.

What I am unsure of

  • The hosted build's linker. It names build/<host>/ci-llvm/bin/lld, which exists only where bootstrap downloads CI LLVM. The nightly's build and portability-* lanes are the evidence; nothing local runs the hosted build.
  • -Bsymbolic in the base is shown by DT_FLAGS SYMBOLIC on both architectures' init, not by a mutation. Reverting it means rebuilding the compiler (11:37 here) and the sysroot. The x86 dlopen failures without it were measured earlier on this branch. AArch64 runs no userland yet, so no AArch64 test can red on it.
  • The loader mapping on AArch64 is typed by firmware's map like the rest. A loader whose 2 MiB page above 4 GiB is part memory would now be refused there, although nothing on AArch64 fetches from it after the switch. It is not seen on virt, whose RAM ends below 4 GiB.
  • Two duplicate SUN_LEN issues on main, a-lane-s-tap-socket-path-is-past-sun-len-on-the-dev-host.md and a-lane-s-tap-socket-path-outgrows-sun-len-on-the-dev-host.md. They are not this branch's, and are left.

Filed

  • issues/filesystem/blockd-survives-its-death-reds-on-a-replacement-that-reorders-acknowledged-writes.md
  • issues/build/a-root-image-rebuilt-at-its-used-blocks-can-run-out-by-one.md
  • issues/design-debt/std-calloc-overflow-writes-past-a-fifteen-byte-block.md (its exit rewritten this round)
  • issues/kernel/the-program-loader-runs-a-binary-whose-relr-table-it-never-reads.md
  • issues/kernel/the-kernel-reserves-its-stack-offset-as-a-physical-region.md
  • issues/build/the-hosted-rustc-names-a-linker-toyos-does-not-have.md
  • issues/build/two-checkouts-of-one-tree-build-different-guest-bytes.md

🤖 Generated with Claude Code

https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK

Japabu and others added 11 commits September 26, 2026 18:24
… a page

Two defects the kernel's move to rust-lld exposes, fixed before it.

A kernel linked with `--pack-dyn-relocs=relr` keeps its relative
relocations in an SHT_RELR section the SHT_RELA loop never reads. The
loader applied none of them and jumped: measured with a RELR kernel and
this refusal reverted, `Applied 0 relocations`, then `#UD` at an
unrelocated RIP. With it, the boot stops at
`kernel.elf: SHT_RELR (packed relative relocations) is not supported`.

The kernel stack began at `vaddr_max`, which has whatever alignment the
last segment's end has. toyos-ld ended every image on a page; rust-lld
ends it where `.bss` ends (0x410008 here), so the stack top sat 8 bytes
off 16 and the first `fxsave64` on it took a #GP in `fpu::self_check`.
The stack now starts on the next page.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
The kernel, the bootloader and every userland program link through the
toolchain's own `rust-lld` (LLD 22.1.8, the LLVM rustc carries). toyos-ld
stays in the tree, frozen, as the linker toyos-cc links through inside
ToyOS until LLD runs there; the host no longer builds it, ships it or
publishes it.

- kernel/ and bootloader/ drop `linker = "toyos-ld"`: their upstream
  targets already name rust-lld, and the UEFI one passes lld-link
  `/OPT:REF,ICF`, which toyos-ld ignored.
- The toolchain is bootstrapped with `lld = true`, which puts rust-lld in
  every stage's sysroot; `assert_toolchain_is_honest` refuses one without
  it. The hosted rustc's build cannot have it (bootstrap would build LLD for
  a ToyOS host from C++), so that build links through the LLD bootstrap
  downloaded with LLVM and the host-only build then reassembles stage2.
- Bootstrap spells an rpath as a C driver's `-Wl,` arguments, which a linker
  run directly refuses; toyos-ld dropped them silently, so no ToyOS binary
  ever carried one, and `rpath = false` says so.
- The std build names the compiler's rust-lld; `sysroot::RECIPE` moves.
- The C tests link their libc `staticlib` with rust-lld, with the entry and
  the library named: the staticlib carries the allocator shim toyos-ld used
  to synthesise. toyos-cc no longer emits `.note.toyos.libc`, which only
  toyos-ld read; doom's build script names `toyos_c` itself.
- userland/libc defines no `malloc`/`free`/`calloc`/`realloc` when built
  beside std (`std-runtime`): std defines the same four, and rust-lld
  refused the duplicate in doom where toyos-ld had silently kept std's
  (disassembled: doom's `calloc` was std's saturating one). The four now
  resolve to exactly what doom linked before.
- Every guest profile strips DWARF at the link: shipped, it made the image
  532 MB and the boot 0.46 s slower. Cargo drops a profile's `strip` for a
  package with an override of its own, so toyos-cc and toyos-ld state it.
- toyos-ld leaves `PUBLISHED`, the release tag and the tarball, and says
  `publish = false`.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
- The TinyCC track goes: clang is the C compiler ToyOS is headed for, and
  tinycc's place in a chain was the seed-to-GCC bridge of a bootstrap the
  owner has ruled out of scope. Its entry leaves the swarm file's list.
- std's C `calloc` answers an overflowing product with a pointer past a
  15-byte block (found reading which `calloc` doom links).
- The program loader drops `DT_RELR` and runs the binary unrelocated; the
  bootloader now refuses the kernel's equivalent, the loader does not.
- The hosted rustc names `rust-lld`, which ToyOS does not have.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
The corpus check reads a failed link's first line for the symbol a declared
case stops on, and rust-lld writes one error per line: the first run named
none of them and reported five cases as moved.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
…are put it

rust-lld links the bootloader at lld-link's default ImageBase,
0x140000000, where toyos-ld linked it at 0. A firmware that can honour the
base loads it there: OVMF does on the harness's 4 GiB q35 guest, whose RAM
continues above the PCI hole. The boot map covered the low 4 GiB only, so
the instruction after `mov cr3` was fetched from an unmapped page: every
guest of the first fast-tier run died there, `#PF` with `I:1` at
RIP 0x14000CE8A, and printed nothing past the loader's handoff.

`toyos_bootmap::Plan` now takes the loader's image, read off its own
LoadedImage protocol, and maps its pages at identity (and at PHYS_OFFSET,
as every directory is) beside the low map and the scanout, against the same
directory budget: two more directories, since a loader, like a scanout,
lies in one GiB or straddles two. A machine whose loader no map can hold is
refused by name before the exit, as a scanout is. The loader's pages are
rounded out both ways and typed as plain memory; a page the scanout holds
is not written twice.

Measured: the same image boots to `Boot: devices ready` at 2 GiB (loader
at 0x7e011000) and at 4 GiB (loader at 0x140000000).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
… extent

psABI variant II puts the executable's TLS block at
`tp - round(p_memsz, p_align)`, and a linker bakes that into every
local-exec access. toyos-ld subtracted the raw `p_memsz` (and aligned
every TLS segment to 64), and the loader was written to match: it ended
the executable's module at `tp` by its `memsz`. rust-lld follows the
psABI, so an lld-linked `std_tls` (`p_memsz` 0xa8, `p_align` 0x40) read
its initial value 0x18 bytes off and panicked in its own panic handler.

`toyos_elf::tls::exe_extent` is that rounding, and the loader places the
executable's module by it. Measured: `std_tls` red at line 33 without it,
and `std_`/`dlopen` green with it.

`std_tls` asserted a 64-byte alignment it did not declare: toyos-ld
imposed it. Its datum now asks for 64 by its own type, so the test
measures that the loader honours what the program declared, whatever the
linker would have chosen.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
… pid

Found in this branch's fast tier: `lan_mdns_answer` red twice under a
harness running as pid 10033, green under pid 3345, and the alone run
named `SUN_LEN`.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
lld-link stamps a PE with the clock and, under the `/DEBUG` rustc passes,
names a PDB whose GUID hashes object paths under the target directory:
the same bootloader built into two target directories differed in 20
bytes. `/Brepro` stamps it with a hash of itself and `/DEBUG:NONE` writes
no PDB, and the two builds are now identical — as the kernel and `snake`
already were.

Two checkouts at different paths still differ, in every guest artifact and
not by the linker; that is filed.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
Moves the pin to ToyOSOrg/rust a84b4aa849c, on toyos-inbox, where the
target names rust-lld with `LinkerFlavor::Gnu(Cc::No, Lld::Yes)` and
passes `-Bsymbolic`: a shared object binds its own definitions at link
time, as toyos-ld's did, because the loader resolves a `dlopen`ed object's
GOT and PLT slots only against the other objects in the process. Without
it `dlopen_dedup`, `std_tls_dlopen`, `std_tls_cranelift`,
`std_tls_multi_crate` and `std_unwind_so` jumped through unbound PLT slots.
rustc_target's own consistency test for the target passes.

A compiler change is built by the primary's next build after this lands:
a linked worktree refuses a fork whose `compiler/` the shared compiler was
not built from, so this branch's guests were built and tested with the
shared compiler told the same linker, flavour and argument.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
`assert_toolchain_is_honest` refuses a toolchain without rust-lld, and
the layout test built one without it: it now shows that refusal, names the
linker in it, and passes once the linker is there.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
@Japabu
Japabu marked this pull request as ready for review September 26, 2026 17:35
…d toyos-ld frozen

Owner rulings: self-hosting means ToyOS rebuilds itself on ToyOS and
reproduces the host's bytes, a seedless bootstrap being out of scope;
no upstream pull requests for now, because ToyOS needs more attention and
contributors before upstreams take it seriously and upstreams tend to
refuse AI-first work, so a fork carries a change written to upstream
quality; a source ToyOS cannot build unchanged is carried as an
unmodified mirror behind a byte-identity gate; and toyos-ld is frozen now
that everything links with rust-lld.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
@Japabu

Japabu commented Sep 26, 2026

Copy link
Copy Markdown
Collaborator Author

T14 run 147 (orchestrator) of the staged rust-lld image (built before #525's merge): booted and ran to its tests with no panic or fault; 'Boot: complete' at 1170 ms. Loader 1680 ms with ROOT read 583 ms, against 2270 ms / 1158 ms on main's toyos-ld image (run 145) — the smaller ROOT halves the read. The loader above 4 GiB (lld-link's ImageBase) is covered on this firmware. The automated judge could not run from this worktree (check_compiler refuses its build at the moved pin), so the verdict here is read from kernel.log by hand; it is re-run once the branch builds its own compiler after #524.

@Japabu

Japabu commented Sep 26, 2026

Copy link
Copy Markdown
Collaborator Author

Review of #532 at a028e6fb. CI: abi-split pass; host skipped on a pull request by design. Two things are missing: a reading from the T14, and any run of the compiler this pin names. Either one alone makes this NOT READY FOR REVIEW. The findings below are given so the round is not wasted.

Net lines (git diff --shortstat origin/main...HEAD): 46 files, +642/−446.

  • Production: +322/−348, net −26 (this counts src/toolchain.rs's in-file tests).
  • Tests: +186/−52, net +134.
  • Issues and prose: +133/−45.

BLOCKER

  • rust (pin a84b4aa849c), src/toolchain.rs:707-792 — the final compiler has never been built or run, and it can be measured before landing. gh workflow run nightly.yml --ref wt/toyos-lld builds this pin's toolchain in build: lld = true, rpath = false, the hosted rustc linked by ci-llvm/bin/lld, and the reassembly at :741. It then boots every guest shard with that toolchain.
    • Spec fidelity, checked against the fork source:
      • The flavor matches. The name rust-lld infers Gnu(Cc::No, Lld::Yes) (infer_linker_hints).
      • -Bsymbolic reaches the active flavor. add_pre_link_args(Gnu(No,No)) is copied to Gnu(No,Yes) (add_link_args_iter), and pre-link versus -Clink-arg position does not matter to lld's -Bsymbolic.
      • The binary is the same one. Bootstrap's Lld step returns ci-llvm when bin/lld exists.
    • What does not transfer:
      • the toolchain build itself;
      • std's dylib and librustc_driver.so linked under the new spec;
      • the body never names how -Clink-arg=-Bsymbolic was injected. cargo_build removes RUSTFLAGS, and a CARGO_TARGET_X86_64_UNKNOWN_TOYOS_RUSTFLAGS would have replaced userland's build.rustflags.
    • Re-run with the real compiler:
      • the build job's exit;
      • every guest shard's conclusion, named per test against main's nightly, which is red itself;
      • -- std_ and -- dlopen;
      • .comment on the 24 ELFs;
      • the kernel, bootloader and snake built into two target directories, compared.
  • bootloader/src/main.rs:644-652 and toyos-bootmap/src/lib.rs:537-591 — no reading from the T14. The loader now runs from wherever firmware put it, and the body says the T14's firmware poses the same risk. That case is staged, not run, and QEMU is not the hardware. Wanted: the T14's loader.log, showing its Loader image: line and a boot past Boot: devices ready.
  • bootloader/src/main.rs:382-387 — the stack-alignment fix survives its own revert. At head the rust-lld kernel's vaddr_max is 0x40fc30, which is already 16-aligned, so this patch passes every committed test:
    -        .checked_next_multiple_of(4096)
    -        .and_then(|image_end| image_end.checked_add(stack_size as u64))
    +        .checked_add(stack_size as u64)
    
    • Wanted: a check that goes red whenever the entry stack is off 16. One option is _start refusing rsp & 0xf != 0 before the call. The other is to move the placement into a pure crate and host-test it at vaddr_max 0x410008.
  • bootloader/src/main.rs:408-411 — no committed test covers the RELR refusal. Deleting those four lines leaves every test green; the negative control was a hand patch.
    • Wanted: a committed case that goes red on that deletion. Either put the refused section kinds in toyos-elf as a pure decision with a crafted SHT_RELR table, or add a harness case that links the kernel with -z pack-relative-relocs and matches the refusal in loader.log.
  • rust/compiler/rustc_target/src/spec/targets/x86_64_unknown_toyos.rs:8-12 — the linker, the flavor and -Bsymbolic are OS properties declared in one arch's spec.
    • base::toyos still names toyos-ld with Gnu(Cc::No, Lld::No). No target uses that default any more, and it misleads.
    • aarch64-unknown-toyos on toyos-inbox (c3cb792df3f) names rust-lld without -Bsymbolic, which is the dlopen defect this branch found. That defect is known, and nothing but this PR's "Unsure" section records it.
    • Move all three into base::toyos once, and delete the x86 override.

NOTE

  • The 7-vs-6 slot count for <&T as Debug>::fmt cannot produce a wrong call, because every target exists in toyos-ld's set. It matters only to code that compares vtable or function pointers for identity, which Rust does not guarantee. Name the seven sites once: llvm-readobj -r plus the section that holds each.

  • userland/libc/src/memory.rs:7-15 — resolving to std's four is correct for linking. It is also what doom ran before, by the body's disassembly. But libc now defers to the defective one of two sibling C allocators, and its own calloc, which does checked_mul, is dead under std-runtime. The filed calloc issue should exit on one C allocator, not on patching the second.

  • toyos-ld/src/reloc.rs:88 and emit_elf.rs:1047-1056 — the frozen toyos-ld, which still ships in the image, computes TPOFF = sym - (tls_start + memsz) with p_align 64 and an unrounded memsz. The loader now places the executable at round(memsz, p_align), so every local-exec access in a toyos-ld executable with memsz % 64 != 0 is off. This is latent today, since toyos-cc emits no TLS and libc has none. Make toyos-ld refuse a PT_TLS output, or file it.

  • toyos-ld/src/lib.rs:363-374 — nothing produces .note.toyos.libc any more, so the automatic -ltoyos_c is dead. An in-guest toyos-ld CLI link now needs -ltoyos_c. No guest test compiles or links anything with the in-guest toyos-cc or toyos-ld.

  • bootloader/Cargo.toml:46 — strip = "debuginfo" does nothing for lld-link: MsvcLinker::debuginfo ignores strip, and /DEBUG:NONE does the work. Delete the line.

  • /Brepro /DEBUG:NONE hide nothing: toyos-ld wrote no PDB, and nothing reads one.

  • The reproducibility issue is honest. It names both causes (panic-location paths and .llvm.<n> suffixes) and gates its exit on a comparison.

  • Backtraces: --strip-debug keeps .symtab and .strtab. The symbolized assertions (tests/toyos.rs:3039-3043, check_disk_backtrace) were green in the imitation run. Re-confirm them under the first BLOCKER.

  • The size wins are real. toyos-ld shipped no DWARF, so the file sizes compare stripped with stripped, and the loaded sizes (vaddr_max) do not depend on DWARF at all.

  • toyos-ld is gone from the published set: it is out of PUBLISHED and TREES, and publish = false. On crates.io (curl at review time) 0.2.0 is yanked and 0.3.0 is not. Yank 0.3.0 only after the upstream-target branch (f74cadc2b8c) stops naming toyos-ld.

  • toyos-bootmap/src/lib.rs:186 — past MAX_DIRECTORIES, a GiB that is already counted can be counted again. That only inflates the number in Refusal::Directories.

  • src/toolchain.rs:505 — check_installed_toolchain never asserts that rust-lld is present. Only Owner::Us is refused by name.

  • kernel/src/main.rs:393 — this predates the branch. kernel_stack_addr is an offset into the image, but it is reserved as a physical region. File it.

  • issues/build/every-worktree-builds-its-own-copy-of-the-same-crates.md:98 cites the deleted toolchain::toyos_ld_binary.

  • Sequencing: land Link everything that boots with rust-lld, and freeze toyos-ld #532 first, once the nightly on this branch is green. Then ARM64 port, stages 0 to 3: shared groundwork, aarch64-unknown-toyos with std and the userland, and the loader and kernel reach the PL011 on virt #524 merges main and must:

    • delete Arch::links_through_toyos_ld;
    • keep one of the two page-rounding stack fixes, which are semantically identical;
    • port the loader mapping into its reshaped Plan::new(scanout, typing), for AArch64 as well, since AAVMF places the loader too;
    • repin to a fork commit that holds both a84b4aa849c and c3cb792df3f (80e13957806 does).

    If the nightly cannot build the toolchain, land ARM64 port, stages 0 to 3: shared groundwork, aarch64-unknown-toyos with std and the userland, and the loader and kernel reach the PL011 on virt #524 first and measure this branch with its own compiler.

REMOVE

  • tests/common/compile.rs:120-122 — "the ones rustc passes rust-lld" is false: there is no -Bsymbolic, and it is a second copy of the spec.
  • bootloader/Cargo.toml:44-45 — "DWARF stays out of the image" says a no-op does something.
  • toyos-elf/tests/tls.rs:114-115 — a measurement's provenance in source.
  • toyos-bootmap/src/lib.rs:8 — "four numbers in" is a count.
  • PR body: "merged into toyos-inbox as 80e13957806; the pin moves to it" is wrong, since the pin is a84b4aa849c. The cargo yank --version 0.2.0 step is already done.

SEND BACK

Japabu added a commit that referenced this pull request Sep 26, 2026
…mbled with it

`lld = true` in the hosted-rustc config made bootstrap's Assemble for the
x86_64-unknown-toyos stage-2 compiler ensure LldWrapper, hence llvm::Lld and a
cmake LLVM for a ToyOS host. `lld = false` there instead wipes the host
stage2 (Sysroot::run removes it on every assemble), and with it the rust-lld
AArch64's std_config names.

So the hosted build writes `lld = false`, and build_hosted_rustc then runs the
host-only build once more under `lld = true`, which reassembles the host stage2
with rust-lld and compiles nothing new. This is #532's shape, so the two
reconcile on it.

The host's `default-linker-linux-override` is pinned off in every config that
builds a host compiler: bootstrap ties it to `lld` on x86_64-unknown-linux-gnu,
so without the pin the toggle would rebuild the host rustc in each of the two
builds on Linux. Off is what main's `lld = false` already gave, so no host
rustc changes. The worktree compiler's recipe moves with the pin.

assert_toolchain_is_honest now refuses a toolchain with no rust-lld, by name,
and its test holds that refusal.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
Japabu and others added 5 commits September 27, 2026 01:42
Every conflict carries both sides:

- rust: pinned to 209f10ded88 on the fork's wt-toyos-lld, which holds this
  branch's a84b4aa849c and main's c34ecdf0ab6 (merged as 3980fb86829, with
  none of toyos-inbox's other lines) and moves the linker, its flavor and
  -Bsymbolic into base::toyos for both ToyOS targets.
- src/toolchain.rs: #524's write_config shape (Arch::ALL guest targets,
  HOST_LINKER_PIN, the reassembly and its rust-lld assertion), with every
  userland target naming rust-lld and rpath = false; the hosted build names
  ci-llvm's lld for the hosted architecture. toyos-ld's build, witness and
  install path stay deleted.
- src/sysroot.rs: std_config(compiler, ...) names the compiler's rust-lld for
  every architecture; RECIPE keeps main's text and this branch's "linked by
  rust-lld; 3".
- src/arch.rs: links_through_toyos_ld is deleted, since no architecture does.
- src/build.rs: main's GuestEnv fields without PATH; src/release.rs: main's
  HOSTED_ARCH paths without the toyos-ld witness.
- kernel/src/loader/tls.rs, toyos-elf/src/tls.rs: main's variant I/II layout,
  with variant II's executable placed at its extent (exe_extent).
- toyos-bootmap: main's typed Plan, with the loader's image as a third input
  on both architectures, counted against one directory budget.
- bootloader/src/main.rs: main's stack rounding kept as the one fix, main's
  enter_kernel, and the loader image read off LoadedImage into the Plan.
- kernel/, bootloader/.cargo/config.toml: main's, without toyos-ld; the
  bootloader keeps /Brepro and /DEBUG:NONE.
- kernel/src/arch/x86_64/paging.rs: main's; its WindowProt comment moved to
  kernel/src/mm/policy.rs and takes this branch's wording there.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
…rms are pure decisions

Both were loader code no committed test could red: the stack's page rounding
survived its own revert because the rust-lld kernel's vaddr_max (0x40fc30)
happens to be 16-aligned, and deleting the SHT_RELR assertion left every test
green.

- StackedImage::place is where the loader's image-plus-stack allocation comes
  from. Its test places a stack after an image ending at 0x410008, where the
  unrounded stack is off 16 by 8.
- SectionTable::rela_sections is the loader's only way to its SHT_RELA
  sections, and refuses a table that also names SHT_REL or SHT_RELR (Unapplied,
  by name). The loader has no other path to the tables it applies, so the
  refusal cannot be skipped without deleting the loop; its test crafts both.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
…et spec

- bootloader/Cargo.toml's `strip = "debuginfo"` does nothing for lld-link
  (rustc's MSVC linker ignores `strip`); `/DEBUG:NONE` is what keeps DWARF
  and the PDB out of the image. The line and its comment go.
- tests/common/compile.rs said its link arguments were the ones rustc passes
  rust-lld; they are not (no -Bsymbolic), and the spec is the one place that
  says what rustc passes.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
…uto-link

toyos-ld is frozen, and still ships as the in-guest toyos-cc's linker. Two
things in it stopped being true when everything else moved to rust-lld:

- It resolves an executable's local-exec accesses as `sym - (tls_start +
  memsz)` with `p_align` 64, while the loader now ends the executable's block
  at `round(memsz, p_align)`. Every such access in an executable whose TLS is
  not a multiple of 64 would be off. Nothing links one today (toyos-cc emits no
  TLS, libc has none), so it is refused by name (LinkError::TlsExecutable)
  rather than linked wrong. A shared object's accesses go through the loader
  and link as before; tests/tls.rs holds all three cases.
- Nothing emits `.note.toyos.libc` any more, so resolve_libs' implicit
  `-ltoyos_c` for an object carrying it could never fire. It is deleted; a CLI
  link names `-ltoyos_c`.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
…k offset reserved as physical memory

- std-calloc-overflow-writes-past-a-fifteen-byte-block: the exit was a patch to
  std's copy, which would leave libc's correct calloc compiled out beside it.
  It now exits on one C allocator every program links, with the other deleted.
- the-kernel-reserves-its-stack-offset-as-a-physical-region: filed from the
  review of #532; it predates the branch.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
@Japabu
Japabu marked this pull request as draft September 27, 2026 00:03
Japabu and others added 7 commits September 27, 2026 02:05
The merge took #524's `pub(crate)`; tests/common/compile.rs links every C test
with the sysroot's rust-lld through it, from outside the crate.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
…N_LEN issue goes

- parallel-tests-red-under-other-suites: swap_netd red in this branch's fast
  tier on the signature the file already records, green alone.
- a-lan-tests-socket-path-outgrows-sun-path-when-the-harness-pid-has-five-digits
  is deleted: main's a-lane-s-tap-socket-path-is-past-sun-len-on-the-dev-host
  names the same defect, five-digit pid included. Nothing cited it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
Nightly run 36281465192's portability-macos failed linking the hosted
rustc-main: librustc_driver.so for x86_64-unknown-toyos left seven blake3
symbols undefined (blake3_hash_many_{sse2,sse41,avx2},
blake3_compress_{in_place,xof}_{sse2,sse41}), which lld refuses under
--no-allow-shlib-undefined. portability-linux linked the same build.

blake3's build script assembles its x86-64 SIMD code with cc-rs and archives
it with the target's `ar`. On macOS that is Apple's, which indexes only
Mach-O members, so the ELF objects sit in an archive lld pulls nothing from.
Measured with blake3 1.8.2 built into an x86_64-unknown-toyos dylib here:
`AR_x86_64_unknown_toyos=/usr/bin/ar` leaves the same seven undefined, and
ci-llvm's `llvm-ar` leaves none. toyos-ld never checked a shared object's
undefined references, so a macOS-built hosted rustc has linked with these
unresolved all along.

The hosted build now names ci-llvm's llvm-ar beside its lld for the hosted
architecture; bootstrap hands it to cc-rs as AR_<target>.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
…fast tier

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
Found by this branch's nightly (run 36281465192, guest (1)); the defect is in
src/image.rs, outside this branch's brief, so it is filed rather than fixed.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
Japabu and others added 2 commits September 27, 2026 05:41
…nightly and this branch's

Found in this branch's nightly run 36287592139; the same signature is in
#524's branch nightly, so it predates the linker switch.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
…her issues

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
@Japabu

Japabu commented Sep 27, 2026

Copy link
Copy Markdown
Collaborator Author

Hand-back for round 1, at 65e389ae. The pin is ToyOSOrg/rust 209f10ded88, which holds both a84b4aa849c and c34ecdf0ab6; toyos-inbox carries it as 8ae98485fb8.

Merge. origin/main (5e446e5c, #524) is merged as b92b5454. It adopts #524's write_config/std_config shape, and every userland target names rust-lld with rpath = false. Arch::links_through_toyos_ld is deleted.

Findings

  • B1, the compiler never ran. It is built here: key dd66c3b297d9d946, 0:11:37.
    • --build-only EXIT=0, --arch aarch64 --build-only EXIT=0.
    • -- std_: 14 passed, EXIT=0. -- dlopen: 2 passed, EXIT=0. -- virt_: 3 passed, EXIT=0.
    • .comment: Linker: LLD 22.1.8 in 26/26 ELFs.
    • The kernel, bootloader and snake, each built into two target directories, are identical (cmp EXIT=0).
  • B2, T14. A case is staged, with its request at /private/tmp/claude-502/-Users-jan-Dev-jan-toyos/2280e09e-428b-4b81-bc00-1ede594b7247/scratchpad/lld532b/metal/request.txt. The orchestrator runs it.
  • B3, the stack. The placement is toyos_elf::StackedImage::place, kept as the one fix. Its test at 0x410008 goes red under the review's patch: EXIT=101. The head's kernel ends at …888.
  • B4, RELR. SectionTable::rela_sections is the loader's only way to its SHT_RELA sections, and it refuses SHT_REL/SHT_RELR. The crafted-table test goes red with the SHT_RELR arm deleted: EXIT=101.
  • B5, the spec. The linker, the flavor and -Bsymbolic are in base::toyos. Both architectures' init carry DT_FLAGS SYMBOLIC.
  • NOTEs:
    • toyos-ld refuses a TLS executable. Its test goes red with the refusal disabled: EXIT=101.
    • The dead libc-note auto-link is deleted.
    • The bootloader's strip is gone.
    • The calloc issue exits on one C allocator.
    • The bootmap double count is gone.
    • The kernel_stack_addr defect is filed.
    • 7 vs 6 does not reproduce at this head: no function has more slots under lld than under toyos-ld linking the same sources.
    • check_installed_toolchain already refuses a missing rust-lld, through assert_toolchain_is_honest.
  • REMOVE: all done.

Found this round. The first nightly's portability-macos failed: the hosted rustc had seven blake3 symbols undefined. macOS's ar indexes no ELF members.

  • The hosted build now names ci-llvm's llvm-ar.
  • Reproduced here: /usr/bin/ar leaves the seven undefined, and llvm-ar leaves none.
  • portability-macos is green at the head.
  • toyos-ld had hidden this in every macOS-built hosted rustc.

Gates

  • --ci host: EXIT=0, 45 steps, at e4317d3f and at a7f423b5.
  • --ci abi-split: EXIT=0.
  • Fast tier at a7f423b5: 399 passed, 2 failed, EXIT=1. --known-red answers NO for both:
    • lan_mdns_answer is main's SUN_LEN issue;
    • quiesce_wakes_on_the_last_park has its recorded signature and was ALONE green.

Nightly at a55d62c6: https://github.com/ToyOSOrg/ToyOS/actions/runs/36287592139

  • build success, host success, portability-linux success, portability-macos success. portability-windows failure, the declared frontier.
  • Every guest, tcg and audio lane is red on main's $TMPDIR tarball check.
  • These suite reds are main's, on main's own signatures (run 36278449733 at fd62f567): quiesce_leaves_the_volume_whole, exit_wait_storm, metal_job_reboot, usb_transport_break, screen_diag_boot, log_flush_retry, fpu_isolation and home_budget_refusal_retried.
  • audio (2) gate A: main's e8d7c9c0 failed the same statistic.
  • Four rates were each ALONE green.
  • blockd_survives_its_death carries ARM64 port, stages 0 to 3: shared groundwork, aarch64-unknown-toyos with std and the userland, and the loader and kernel reach the PL011 on virt #524's toyos-ld signature and is filed.
  • The one red this branch reaches is guest (1)'s C-corpus shared boot. src/image.rs's second ROOT format runs out by one block on this branch's binaries (NoSpace { requested: 1, available: 0 }, in both runs). The defect is in main's sizing code, outside the brief, so it is filed (issues/build/a-root-image-rebuilt-at-its-used-blocks-can-run-out-by-one.md), not fixed. Landing before it is fixed turns main's guest (1) red. The PR stays draft for that reason.

@Japabu

Japabu commented Sep 27, 2026

Copy link
Copy Markdown
Collaborator Author

T14 run 151 (orchestrator) of the image staged at 65e389a, built by this branch's own final compiler: booted ToyOS in 1178 ms, ran its jobs, stopped and rebooted cleanly, no panic; the log partition passes toyos-fat32-check. power-on to loader 9299 ms, loader 2186 ms (ROOT read 577 ms), kernel to Boot: complete 1219 ms. (The automated judge was interrupted while another agent built in this worktree; the verdict is read from run.log and kernel.log.)

Japabu and others added 2 commits September 27, 2026 06:08
mkfs draws its btree's hash seed from the device's block_count, so
create_root_image's second build — a from-scratch mkfs at the first
build's used-block count — keyed every name differently from the
spacious build and could shape a bigger btree than the first build's
headroom ever measured. On PR #532's rust-lld binaries it ran out by
one block; a host sweep over synthetic file sets found the same
NoSpace panic without a guest, confirmed the mechanism by forcing the
second mkfs onto the spacious build's own hash seed (which then fit),
and left a regression test on the boundary case.

The fix keeps the spacious build's bytes and trims the trailing blocks
mkfs never claimed, so there is no second mkfs and nothing to reseed:
mkfs never frees a block once allocated, so every allocation already
sits below next_alloc with no hole beneath it.

Closes issues/build/a-root-image-rebuilt-at-its-used-blocks-can-run-out-by-one.md.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
The previous commit closed the issue file but missed staging this: the
create_root_image change that trims the spacious build to its used
blocks instead of reformatting at that size, plus
a_second_build_at_the_boundary_does_not_run_out, the regression test
that reds with NoSpace on the old reformat and is green here.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
@Japabu

Japabu commented Sep 27, 2026

Copy link
Copy Markdown
Collaborator Author

ROOT's second-build NoSpace: root cause and fix

Root cause. Formatted::format (bcachefs) draws its btree's siphash seed
from the device's block_count ("deterministic for reproducible builds").
create_root_image's second build reformatted from scratch at the first
build's used-block count — a different block_count, hence a different
hash seed, hence a different key order into the btree. That can shape a
bigger tree than the first build's estimate ever measured, independent of the
bytes: on PR #532's rust-lld binaries it needed one block more than the
rounded-up "used" figure allowed.

Confirmed empirically, not just by inspection: a host sweep over synthetic
file sets reproduced the same NoSpace panic in seconds (no guest needed),
and forcing the second mkfs to reuse the spacious build's own hash seed
made the identical file set fit — isolating the mechanism to the reseed
rather than to sizing arithmetic.

Fix. trim_root (src/image.rs) replaces the second mkfs with an
in-place trim of the spacious build's own bytes: truncate to
align_up(next_alloc + 1, PARTITION_ALIGN) blocks, flip the one bitmap bit
for the relocated backup superblock, and rewrite the superblock. mkfs never
frees a block once claimed, so every allocation already sits below
next_alloc with no hole beneath it — nothing needs reformatting, so nothing
can reseed.

Regression test. image::tests::a_second_build_at_the_boundary_does_not_run_out
builds a file set whose spacious build lands exactly on this boundary.
Checked mutation: reverting create_root_image to the old reformat-based
second build turns this test red with the identical NoSpace panic
(exit 101); the fix in the tree is green (exit 0).

Gates run, with exit codes:

  • cargo test --lib image:: (13 tests, including the new boundary test): EXIT=0
  • cargo test --test toyos-build -- --shard 1/12 --jobs 1 --nightly (the
    guest (1) nightly lane, locally): 215/216 pass; the one failure,
    lan_mdns_answer, is the pre-existing, unrelated
    issues/build/a-lane-s-tap-socket-path-is-past-sun-len-on-the-dev-host.md
    (macOS $TMPDIR + SUN_LEN) — not touched by this change. The 41 MiB/48-binary
    tinycc shared boot in that shard (matching the nightly failure's shape) is
    fully green, 30_hanoi included.
  • cargo run -- --ci host: 45 steps, all green, EXIT=0

Closed issues/build/a-root-image-rebuilt-at-its-used-blocks-can-run-out-by-one.md.

@Japabu
Japabu marked this pull request as ready for review September 27, 2026 04:09
…-lld switch

The rust submodule moves to fbf6ad143d8, a fork merge of this branch's
209f10ded88 (every ToyOS target links with rust-lld and -Bsymbolic) and
main's 64a2050c484 (#527's stream sinks and clock page); it merged without
conflict and is on toyos-inbox through 56f00d51770.

Every other overlapping file merged hunk by hunk with no textual conflict:
the two sides touch disjoint regions of src/build.rs, src/release.rs,
src/sourcegate.rs, kernel/Cargo.toml, kernel/src/loader/tls.rs,
fpu_isolation.rs and the two issue files. No published SDK crate changes on
this branch, so no version moves; every tracked lockfile resolves --locked.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
@Japabu

Japabu commented Sep 27, 2026

Copy link
Copy Markdown
Collaborator Author

Merged origin/main at 1ce7183 (#527, #534) without rebasing. The branch head is now 7b07271.

rust fork: the pin is now fbf6ad143d8, which merges this branch's 209f10ded88 with main's 64a2050c484. The merge had no conflicts and touches only library/std. It was pushed to wt-toyos-lld and merged into toyos-inbox as 56f00d51770. git merge-base --is-ancestor exits 0 for both old pins, and git ls-tree HEAD rust gives fbf6ad143d8.

Superproject: every overlapping file merged without a textual conflict because the two sides edit different regions. This branch changes no published SDK crate, so no version moves. Every tracked lockfile resolves with --locked and nothing changed in them.

Gates on 7b07271:

  • --ci abi-split: 0
  • --check-forks: 0 (23 branches, all current)
  • --build-only: 0
  • --arch aarch64 --build-only: 0
  • --ci host: 0 (48 steps)
  • cargo test --test toyos-build -- std_: 0 (14/14)
  • -- dlopen: 0 (2/2)
  • -- --nightly log_: 0 (25/25)
  • cargo test --lib image::: 0 (13)

@Japabu
Japabu added this pull request to the merge queue Sep 27, 2026
Merged via the queue into main with commit 16d2e64 Sep 27, 2026
2 checks passed
Japabu added a commit that referenced this pull request Sep 27, 2026
…, #531, #534) into the Netstack3 netd

Two conflicts. tests/common/origin.rs: main added `volumes` to the imports
this branch had split to name `segment`'s NEIGHBOUR and NEIGHBOUR_MAC; both
kept. userland/Cargo.lock: main's lock taken whole and re-locked against this
branch's manifests (`cargo metadata`), which adds the mirrored crates and their
dependencies and drops smoltcp and its defmt.

The rust gitlink fast-forwarded to main's fbf6ad143d8; this branch's pin
(7a809b7591f) is its ancestor, and the branch made no fork commit of its own.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
@Japabu
Japabu deleted the wt/toyos-lld branch September 28, 2026 09:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant