Skip to content

Releases: zachgenius/LDB

v1.6.2 — Field-report follow-ups + ARM64 xref correctness

16 May 11:59

Choose a tag to compare

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.open on 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.shutdown RPC.
  • --listen-idle-timeout N opt-in idle exit.
  • xref.address + string.xref now return provenance.warnings documenting heuristic skips.
  • view.include_sections on target.open.

Correctness

  • ARM64e chained-fixup parser (formats 1, 6, 9, 12, auth-rebase) — phase 1.
  • Mach-O loader (thin + FAT + fat_arch_64 triple-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 NonStopRuntime fixed via shared_ptr-owned notification sinks.
  • Dispatcher::dispatch_mu_ recursive mutex serialising multi-client dispatch.

Security

  • Socket inode 0600; parent dir 0700; O_NOFOLLOW lockfile; lstat parent-dir symlink check; getpeereid / SO_PEERCRED peer-uid rejection; SO_RCVTIMEO + SO_SNDTIMEO; absolute --listen paths 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 (and security-auditor for 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

13 May 00:26

Choose a tag to compare

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.mdskills/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.md for the new install path (clone+symlink into
    ~/.claude/skills/<name>/ is now the recommended install).
  • Bumped CMakeLists.txt to 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-analyze

Claude 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 --version reports 1.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

12 May 12:07
e010faf

Choose a tag to compare

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_rsp opens 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
  • vCont write path for thread.continue / thread.suspend against RSP-backed targets

Non-stop runtime

  • Per-thread state machine + monotonic stop_event_seq per target — docs/26-nonstop-runtime.md
  • Listener thread polls RspChannels, fires thread.event{kind:"stopped"} push notifications — docs/27-nonstop-listener.md
  • thread.list_state query, thread.suspend honoured for both RSP-backed (via vCont;t) and LLDB-backed (via SBThread::Suspend()) targets
  • hello.capabilities.non_stop_runtime: true for 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.compile endpoint 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_limited counters
  • 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::NotImplementedError subclass; dispatcher catches by type rather than substring-matching error messages
  • SOCK_CLOEXEC Darwin 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.md rewritten 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 when kernel.yama.ptrace_scope=0 or 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.0

See README.md and docs/00-README.md.

🤖 Generated with Claude Code

v1.1.0 — agent-RE dogfood fixes

11 May 00:16

Choose a tag to compare

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)
    -> demangled endsWith("::"+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 warnings array when DWARF
    DW_AT_byte_size is smaller than the last field's end. Surfaces
    toolchain-emitted DWARF inconsistencies as data, not error.
  • fix(disasm): emit address alongside addr on every disassembled
    instruction so --view fields=address,... projections work.
  • docs(skill): clarify value.read (path/frame-relative, requires a
    stopped thread) vs mem.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.