Releases: zachgenius/LDB
v1.6.2 — Field-report follow-ups + ARM64 xref correctness
Closes a 6-item field report from an RE engineer driving LDB against a 503 MB iOS arm64 Mach-O, plus the phase-3/phase-4 hardening cycle that surfaced from deep code review of each fix.
Field-report items closed
| # | Problem | Fix |
|---|---|---|
| 1 | target.open pathologically slow on a 503 MB iOS Mach-O (RSS hit 36 GB, never returned in 14+ min) |
target.preload-symbols=false daemon-wide; symbol queries trigger LLDB parse lazily |
| 2 | target.open response was 2.2 MB / 587 inline sections |
Summary-by-default; view.include_sections=true opts back into the full section walk |
| 3 | target_id didn't survive across CLI invocations |
ldbd --listen unix:PATH persistent socket daemon + multi-client routing in phase 2 |
| 4 | --repl < cmds.txt only ran the first command |
Symptom of #1; closed by the lazy-load fix |
| 5 | --ldbd PATH had to be passed on every in-tree dev call |
CLI sibling lookup via __file__ |
| 6 | iOS ARM64e chained-fixup slots silently produced wrong xrefs | Parser + Mach-O loader + ADRP-pair resolver with function-boundary reset, AAPCS64 + PAC call clobber, ADD/SUB/MOV write tracking, pre/post-indexed LDR writeback, STR-family xref support, FAT slice selection, provenance.warnings field |
Highlights
Performance
target.openon the canonical fixture: 21,913 → 1,712 tokens (12.8× shrink).- Full agent workflow: 51,280 → 32,627 tokens (−36%).
New endpoints / surface
ldbd --listen unix:PATH+ldb --socket PATH(single-client phase 1 + multi-client phase 2).daemon.shutdownRPC.--listen-idle-timeout Nopt-in idle exit.xref.address+string.xrefnow returnprovenance.warningsdocumenting heuristic skips.view.include_sectionsontarget.open.
Correctness
- ARM64e chained-fixup parser (formats 1, 6, 9, 12, auth-rebase) — phase 1.
- Mach-O loader (thin + FAT +
fat_arch_64triple-aware) — phases 1, 2, 4. - ADRP-pair resolver with full clobber semantics: function-boundary reset, AAPCS64 + PAC call clobber, ADD/SUB/MOV/CSEL/LDP destination clobbering, pre/post-indexed LDR writeback — phases 2, 3, 4.
- TSan-confirmed UAF in
NonStopRuntimefixed viashared_ptr-owned notification sinks. Dispatcher::dispatch_mu_recursive mutex serialising multi-client dispatch.
Security
- Socket inode 0600; parent dir 0700;
O_NOFOLLOWlockfile;lstatparent-dir symlink check;getpeereid/SO_PEERCREDpeer-uid rejection;SO_RCVTIMEO+SO_SNDTIMEO; absolute--listenpaths required.
Process notes
Five branches were developed and reviewed via a deliberate fan-out pattern:
- Each item got an implementation agent (worktree-isolated).
- Each branch went through
linus-code-reviewer(andsecurity-auditorfor the socket exposure;opus-model linus with explicit max-effort framing on the deepest changes). - Each review produced a precise punch list addressed by a cleanup agent.
The opus reviewer on phase 2 caught a TSan-confirmed use-after-free in the notification fan-out path. The opus reviewer on phase 3 caught the SUB / PAC / writeback / STR family of false-positives the original whitelist of clobber-source mnemonics missed. The opus reviewer on phase 4 caught the CSEL / LDP regression class and demanded the architectural shift from whitelist to clobber-by-default destination tracking — which is what shipped.
Phase-5 deferrals (enhancement, not fix)
Documented in docs/35-field-report-followups.md:
- §2 phase 3: server-side
target_id-aware notification routing, per-connection dispatch parallelism, workers list reaping. - §6 phase 5: full bind imports-table walk, function_starts backward boundary detection, complete clobber-by-default audit across remaining ARM64 instructions, indirect-dispatch entry points, real iOS .ipa CI smoke.
Commits
Per-PR shortlog:
- #20 (merged 2026-05-16): field-report follow-ups — target.open lazy load + §1 CLI + §2 socket daemon + §6 chained-fixups phases 1-3
- #21 (merged 2026-05-16): §2 multi-client socket daemon + §6 ARM64e xref coverage extension + CI fix
🤖 Generated with Claude Code
v1.6.1 — re-analyze skill repackaged
Patch release: re-analyze ships as a proper Claude Code Skill
v1.6.0 shipped the re-analyze skill as a flat skills/re-analyze.md file
without YAML frontmatter. Claude Code's skill catalog reads the frontmatter to
populate name / description / argument-hint / allowed-tools, so without
it the skill description rendered as a truncated slice of the prose body
("You are an expert reverse engineer…") and the skill couldn't auto-discover
when installed under ~/.claude/skills/.
Changes
- Moved
skills/re-analyze.md→skills/re-analyze/SKILL.md(standard
Claude Code Skill folder layout). - Added YAML frontmatter:
name,description,argument-hint,
allowed-tools: Bash, Read, Write, Grep, Glob, Agent,effort: high. - Rewrote
skills/README.mdfor the new install path (clone+symlink into
~/.claude/skills/<name>/is now the recommended install). - Bumped
CMakeLists.txtto 1.6.1.
Install (new path)
git clone https://github.com/zachgenius/LDB ~/ldb
mkdir -p ~/.claude/skills
ln -s ~/ldb/skills/re-analyze ~/.claude/skills/re-analyzeClaude Code will auto-discover the skill on next session start. Body content of
the skill is unchanged from v1.6.0 — full 5-phase RE workflow (static
orientation → dynamic probes + tracepoints + predicates → network / BPF / perf
observers → artifact capture → session record / replay / export).
Verified
ctest— 68/68 passing on macOS arm64.ldbd --versionreports1.6.1.
Upgrading from v1.6.0
If you previously copied the skill into ~/.claude/commands/re-analyze.md,
remove it and follow the install snippet above. The slash-command path no
longer carries the latest skill body.
🤖 Generated with Claude Code
v1.6.0 — Non-stop chain + V1 arc complete
v1.6.0 closes the V1 planned arc (v1.3 → v1.4 → v1.5 → v1.6 per docs/17-version-plan.md). The protocol contract is stable; everything in the wire surface remains additive.
Highlights
Own GDB Remote Serial Protocol transport
target.connect_remote_rspopens a native TCP connection to any gdb-remote server (lldb-server gdbserver,gdbserver, QEMU, OpenOCD, rr) — parallel path to LLDB's gdb-remote plugin- Async reader thread with two-mutex split (write_mu_ / byte_mu_) — see
docs/25-own-rsp-client.md vContwrite path forthread.continue/thread.suspendagainst RSP-backed targets
Non-stop runtime
- Per-thread state machine + monotonic
stop_event_seqper target —docs/26-nonstop-runtime.md - Listener thread polls RspChannels, fires
thread.event{kind:"stopped"}push notifications —docs/27-nonstop-listener.md thread.list_statequery,thread.suspendhonoured for both RSP-backed (viavCont;t) and LLDB-backed (viaSBThread::Suspend()) targetshello.capabilities.non_stop_runtime: truefor agent negotiation- JSON-RPC 2.0 §4.1 notification frames over the same stdio channel (stream-locked
OutputChannel)
Agent-expression VM + predicates
- Stack-based bytecode VM, signed int64, ~25 opcodes including the new
if_goto/goto(post-#25 phase-3) —docs/28-agent-expressions.md - S-expression DSL (
(eq (reg "rax") (const 42))) compiles in a single pass —docs/29-predicate-compiler.md - New
predicate.compileendpoint returns{bytecode_b64, mnemonics, reg_table}for agent pre-flight - Anti-DoS caps: 4 KiB program / 64 stack depth / 10K instruction count
Tracepoints (no-stop probes)
- New
tracepoint.*family (create / list / enable / disable / delete / frames) —docs/30-tracepoints.md - Rate-limit grammar:
<int>/{s,ms,us,total}enforced at the orchestrator (fixed-pivot window; total = lifetime cap) - Predicate filtering reuses the agent-expression VM; per-probe
predicate_dropped/predicate_errored/rate_limitedcounters - QTDP / QTStart / QTStop / QTFrame wire vocabulary added to
src/transport/rsp/packets.{h,cpp}—docs/34-tracepoints-in-target.md. Orchestrator integration is phase-2.5.
Other refinements
- Typed
backend::NotImplementedErrorsubclass; dispatcher catches by type rather than substring-matching error messages SOCK_CLOEXECDarwin portability fix in the RSP socket open path- Design notes for deferred items: own ptrace (
docs/31), hardware tracing (docs/32), criu (docs/33) - Skill
skills/re-analyze.mdrewritten to match the V1 endpoint surface (fixed 8 stale names) - README simplified (488 → 289 lines)
V1 surface at a glance
| Area | What |
|---|---|
| Endpoints | 100+ across target / process / thread / frame / value / memory / probe / tracepoint / predicate / session / artifact / recipe / correlate / observer / perf / agent |
| Wire formats | Line-delimited JSON (default); length-prefixed CBOR (--format=cbor) |
| Schema | JSON Schema draft 2020-12 for every endpoint via describe.endpoints |
| Backends | LLDB SBAPI + GDB/MI subprocess via one DebuggerBackend interface |
| Transports | Local liblldb, SSH-tunneled lldb-server platform, native GDB-RSP TCP |
| Determinism | Per-endpoint provenance audit, session.fork / session.replay byte-identity gate, ed25519-signed .ldbpack portable bundles |
Validation
- CI green on Linux x86-64, Linux arm64, macOS arm64, plus the opt-in Capstone leg (
gh run 25732148368) ctest: 70/70 passing with SKIP-gated live-attach tests whenkernel.yama.ptrace_scope=0or with CAP_SYS_PTRACE- Build warning-clean under
-Wall -Wextra -Wpedantic -Wshadow -Wnon-virtual-dtor -Wold-style-cast -Wcast-align -Wunused -Woverloaded-virtual -Wconversion -Wsign-conversion -Wnull-dereference -Wdouble-promotion -Wformat=2 -Wmisleading-indentation
Post-V1 / deferred
These are explicitly out of v1.6 and on the "demand-driven / opt-in" track:
- LldbBackend
SBDebugger::SetAsync(true)flip — would drop the final sync-mode constraint on LLDB-side per-thread observation. Defer because of the test cascade. - QTDP phase-2.5 — orchestrator integration on top of the wire vocabulary now in place. Tracepoints currently run daemon-side.
- #20 Own Linux ptrace driver, #22 hardware tracing (Intel PT / ARM ETM), #24 criu snapshot/fork — design notes shipped in
docs/31-33. None scheduled.
Future v1.x releases will be additive patches; v2.0 is reserved for breaking wire changes per the V1 contract.
Install
git clone https://github.com/zachgenius/LDB ~/ldb
cd ~/ldb
cmake -B build -G Ninja -DLDB_LLDB_ROOT=/path/to/llvm-prefix
cmake --build build
ctest --test-dir build --output-on-failure
build/bin/ldbd --version # → 1.6.0See README.md and docs/00-README.md.
🤖 Generated with Claude Code
v1.1.0 — agent-RE dogfood fixes
v1.1.0 — agent-RE dogfood fixes
Five fixes that landed after closing the V1 gates, surfaced by a
real reverse-engineering pass driven through the /re-analyze skill
(see https://github.com/zachgenius/lf_brokers_RE — CFFEX cffex_server
binary RE round).
Changes:
- fix(symbol.find): resolve qualified C++ names and bare method names
via a 3-pass cascade (exact -> FindFunctions(eFunctionNameTypeAuto)
-> demangledendsWith("::"+name)). - fix(symbol.find): also try the arg-stripped form when the query is
Class::Method(args). Portable across upstream LLDB and Apple
libLLDB on arm64. - fix(type.layout): emit a
warningsarray when DWARF
DW_AT_byte_sizeis smaller than the last field's end. Surfaces
toolchain-emitted DWARF inconsistencies as data, not error. - fix(disasm): emit
addressalongsideaddron every disassembled
instruction so--view fields=address,...projections work. - docs(skill): clarify
value.read(path/frame-relative, requires a
stopped thread) vsmem.read(address-based, works against
binaries / cores) in the re-analyze skill's Phase 2.
No breaking changes. Adds two response fields (warnings on
type.layout, address on disasm). 591 unit-test cases (up from 588),
all platforms green.
Source-only release; no prebuilt binaries.