rtld-dispatch is a custom glibc dynamic linker that makes Claude Code work
on WSL1, where the CLI otherwise dies with Exec format error and — via the
common ld-linux workaround — breaks every grep/find/rg tool call.
It lives in loader/: a ~2-line patch to glibc's dl_main plus a
small no_std Rust dispatch object, building a drop-in ld.so. It fixes the
upstream issue anthropics/claude-code#38788.
It's generic — install it as claude.rtld to run claude, agy.rtld to run
Google's Antigravity CLI, etc. (agy also needs a one-off binary patch, see
loader/patch_agy_wsl1.py).
This repository is also an archival mirror of Claude Code's leaked source under
src/— see About the mirror below.
- On WSL1, Claude Code
>= 2.1.83won't exec:cannot execute binary file: Exec format error. - The community workaround launches it through the dynamic linker
(
ld-linux … claude). But claude multiplexes its bundled search tools (ugrep/rg/bfs) offargv[0], and the shims it injects run"$CLAUDE_CODE_EXECPATH" -G …. Under the linker launch,/proc/self/exe— henceCLAUDE_CODE_EXECPATH— is the linker, so the shim runsld.so -G …and dies with-G: cannot open shared object file.
A dynamic linker (ld.so) is itself a kernel-executable ELF. rtld-dispatch
is a custom glibc ld.so: the kernel execs it, so /proc/self/exe — hence
CLAUDE_CODE_EXECPATH — genuinely is the loader, never a separate linker.
A tiny hook in glibc's dl_main (the "run as a program" path) calls our Rust
claude_dispatch, which:
- derives the program to load from its own install name (
/proc/self/exe), in one of two shapes: launcher — strip a trailing.rtld(claude.rtld→claude, installed beside it); or transparent — installed under the program's own name with the real binary moved to<name>.real(loads<name>.real). The transparent shape is for programs spawned by a fixed path that a.rtldlauncher can't wrap and that also read/proc/self/exeto find themselves — e.g. Antigravity's Golanguage_server_linux_x64(os.Executable()); being kernel-exec'd,/proc/self/exestays<name>, so its execPath resolves correctly; - forwards
argv[0]to that program unless we were invoked under our own name — so a normal launch sees the program's own name, while a bundled-tool invocation (claude re-execs itself withargv[0]=ugrep/rg/bfs) passes the tool name straight through for claude's own multiplexer to dispatch.
The rest of rtld runs untouched — it loads the program and transfers control normally.
No LD_PRELOAD, no readlink hook, no env var, no execve. grep/find/rg work, and
subagents work automatically (claude self-spawns via execPath = …/claude.rtld).
The final link is driven by cargo (nix-ld style): glibc is built only up to
librtld.os, then a no_std bin crate links it — rtld supplies the _start entry
and the libc (our hook resolves only memcpy/memset/memcmp against rtld) — into
the -shared ld.so. So the whole glibc change is rtld.c-only
(loader/glibc/rtld-dispatch.patch, no elf/Makefile hunk);
the logic is loader/src/main.rs and build.rs owns the link recipe.
Needs a Rust toolchain, gcc, and patchelf — all in the bundled pixi
env. The glibc source is committed in-repo as a plain unextracted tree (no Git LFS), pruned to
an rtld-minimal ~1 MB closure — only what the build reads to produce ld.so
(loader/glibc/prune-glibc.sh).
pixi run install-loader # patch + build glibc, install ~/.local/bin/claude.rtld
# for several programs at once: make -C loader install PROGS="claude agy"This compiles glibc once (~10–20 min), then installs a <prog>.rtld next to each
real <prog>. Add the launcher(s) to your ~/.bashrc / ~/.zshrc and reload:
claude() { "$HOME/.local/bin/claude.rtld" "$@"; }
agy() { "$HOME/.local/bin/agy.rtld" "$@"; } # if you installed it for agyVerify in a fresh shell: echo "$CLAUDE_CODE_EXECPATH" ends in …/claude.rtld,
echo "$LD_PRELOAD" is empty, and grep/find/rg work. See
loader/ for build internals.
Some programs are launched by a fixed path you don't control — e.g. the Antigravity
IDE's node server spawns …/extensions/antigravity/bin/language_server_linux_x64
directly (a Go binary that also os.Executable()s to find its data dir). A .rtld
launcher can't wrap that, so install the loader transparently with
loader/transparent_shim.sh:
loader/transparent_shim.sh "$HOME/.antigravity-ide-server/bin/<ver>/extensions/antigravity/bin/language_server_linux_x64"It moves the real binary to <name>.real, drops the loader in its place (and applies
patch_agy_wsl1.py if the binary carries the tcmalloc
issue). execve of the fixed path then succeeds and /proc/self/exe stays <name>.
It's idempotent — re-run after each Antigravity upgrade (the installer redownloads
the real binary).
Version note:
rtld-dispatchis built from the same glibc version as your system (here 2.42) so the linker it replaces stays in step with thelibc.so.6it loads the program against. Rebuild after a system glibc upgrade.
This repo began as — and still contains — an archival mirror of Claude Code's
leaked source (the TypeScript/TSX under src/), recovered from a
.map sourcemap accidentally published to npm in March 2026 (discovered by
Chaofan Shou; originally mirrored by
Yasas Banu). It is study material, not a buildable
project. The untouched files remain the exact 2.1.88 outer source-map inputs;
the shared tree now carries cumulative, verified source-facing recovery content for
2.1.89, 2.1.90, 2.1.91, 2.1.92, 2.1.94, 2.1.96, 2.1.97, 2.1.98, 2.1.100,
2.1.101, 2.1.104, 2.1.105, 2.1.107, 2.1.108, 2.1.109, 2.1.110, 2.1.111,
2.1.112, 2.1.113, 2.1.114, 2.1.116, 2.1.117, 2.1.118, 2.1.119, 2.1.120,
2.1.121, 2.1.122, 2.1.123, 2.1.124, and 2.1.126; upstream did not publish
2.1.93, 2.1.95, 2.1.99, 2.1.102, 2.1.103, 2.1.106, 2.1.115, or 2.1.125.
This does not claim that any complete original authored TypeScript tree is
observable. A short tour of what's inside is in CLAUDE.md.
The evidence-first adjacent-published-release recoveries live in
recovery/: they reconstruct the complete published wrapper
and every plain JavaScript entry in the authenticated Linux x64 Bun graph
exactly through 2.1.126, while keeping the necessarily partial authored-source
reconstruction labeled separately. The signed native executable
itself is authenticated and container-verified; it is not claimed as
baseline-derived source or as an ELF reconstructed from the prior release.
The current frozen release case is 2.1.124→2.1.126. Its release-local source
target contains 2,166 files and 32,823,496 bytes at Git tree
9c7c4f699cd0cc740dcb5e5341aeb026d4bc2263; its focused tests pass 10/10,
all five changed non-deleted source paths syntax-build, and its structural
ledger closes 4,405,944 tokens across 22,358 units with zero residue. Shared
main is a selective cumulative merge guarded by
recovery/test/cumulative-2.1.126-merged-source-retention.test.mjs; it is
intentionally not byte-identical to that frozen release-local source tree.
See the current 2.1.126 report,
manifest, and
complete recovery runbook.
rtld-dispatch patches and links against glibc, so the built binary is
glibc-derived (LGPL-2.1-or-later); this repo ships only the patch + Rust source,
not a binary (see loader/NOTICE). Original work in this repo —
the loader patch, Rust dispatch, recovery tooling, and documentation — is
WTFPL. The mirrored source under src/ and Anthropic-derived
recovery artifacts under recovery/cases/ are the proprietary property of
Anthropic PBC, included for educational/archival purposes only — this is not an
official Anthropic product.