mirror of
https://github.com/Druthulu/BFM-decomp
synced 2026-10-03 00:05:11 -04:00
936d7d741c
- ImportAnnotations.java: the S86 OSGi-bundle blocker was 3 javac errors (Long->int unboxing x2, a nonexistent LocalVariableImpl ctor -> VariableStorage); "/undefined" resolves to DataType.DEFAULT (it lives in neither type manager — main's first proof passed the cmp with failed=13 because the plate-comment rows had set the same function comments); ghidra_rebuild.sh now dies unless the import printed failed=0 (R49), writes .proof markers - ghidra_annotations_delta.py: analysis drift measured and encoded as three counted classes — Error/Analysis bookmarks; auto-named DEFAULT functions the rebuild did not create (29 in main's LINKED regions); auto-named rows lagging the curated symbol file (10 sep8 + 5 aug31, R15). Result: main 38 hand-authored rows (13 annotated fns incl. 3 the ELF does not define, 22 comments, 3 labels); resident/overlays/protos container rows only; the DB holds no hand-authored types - controls (R39): mutated block row -> PROOF FAIL; synthetic comment/bookmark/label/signature round-trip -> PROOF PASS twice (idempotent); the filter keeps the synthetic rows and a hand-renamed name-only diff; fake failed=2 refused, resident re-proven; roster --check controls both ways - proofs, all PASS failed=0: resident 65s, ov_SC01_077 169s, ov_SC06_018 173s, SLUS_007.26 210s, sep8 202s, aug31 206s - tools/ghidra_roster.py -> config/ghidra/ROSTER.md (--check in tools-health, ignores the per-machine proof column) - .claude/settings.json hooks $CLAUDE_PROJECT_DIR-relative; ghidra_mcp_start.sh is a silent exit 0 without Ghidra or the project (both controlled); SETUP P33 B5 section + 5 inventory rows + §2.8 (R21); CURRENT_PHASE log + checkpoint
63 lines
3.0 KiB
Bash
63 lines
3.0 KiB
Bash
#!/usr/bin/env bash
|
|
# tools/ghidra_mcp_start.sh
|
|
# Idempotent, fully-detached start of the headless GhidrAssistMCP server for BFM,
|
|
# using BfmMcpServer.java (server + clean save-on-shutdown — see that file).
|
|
# Serves a program from the "bfm" Ghidra project over MCP on 127.0.0.1:8080.
|
|
# Program defaults to SLUS_007.26 (retail); pass an alternate as $1 to serve a
|
|
# prototype, e.g. tools/ghidra_mcp_start.sh sep8_SLUS_007.26
|
|
# (only ONE program is served per session; stop with ghidra_mcp_stop.sh to switch).
|
|
#
|
|
# Designed to run from a SessionStart hook:
|
|
# * no-op if the server is already serving on :8080 (safe to run every session),
|
|
# * the launched server is fully detached (setsid+nohup+disown) so it OUTLIVES the
|
|
# launching shell and the Claude Code session (persists across sessions in a phase).
|
|
#
|
|
# After "serving" appears in the log, run `/mcp` in Claude Code once per session to
|
|
# attach the mcp__ghidra__* tools (a running session cannot self-reconnect).
|
|
#
|
|
# PERSISTENCE: MCP writes are saved only on CLEAN SHUTDOWN (analyzeHeadless commits the
|
|
# open transaction + saves on close). Save+stop with tools/ghidra_mcp_stop.sh; resume by
|
|
# re-running this script. There is no mid-session save (GhidrAssist holds an open txn).
|
|
set -uo pipefail
|
|
REPO="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" # P33 B5: repo-relative, no $HOME/bfm-decomp assumption
|
|
|
|
GHIDRA="${GHIDRA_INSTALL_DIR:-$HOME/ghidra_12.1_PUBLIC}"
|
|
PROJ_DIR="${BFM_GHIDRA_PROJ:-$REPO/ghidra}"
|
|
PROJ="bfm"
|
|
PROG="${1:-SLUS_007.26}" # program to serve; default retail, pass a proto name to override
|
|
PORT="8080"
|
|
RUNDIR="$REPO/.run"; mkdir -p "$RUNDIR" # project-local runtime scratch (never /tmp)
|
|
LOG="$RUNDIR/ghidra-mcp.log"
|
|
SCRIPTS="$REPO/tools/ghidra_scripts"
|
|
STOPREQ="${BFM_MCP_STOPREQ:-$RUNDIR/mcp-stop.req}"
|
|
|
|
# 0) Not this machine's job? A contributor's clone has no Ghidra install and no ghidra/ project
|
|
# (the DB left git at P33 B5); the SessionStart hook must then be a silent, successful no-op.
|
|
if [ ! -x "$GHIDRA/support/analyzeHeadless" ] || [ ! -d "$PROJ_DIR/$PROJ.rep" ]; then
|
|
exit 0
|
|
fi
|
|
|
|
# 1) Already serving? no-op (idempotent across sessions).
|
|
if ss -tln 2>/dev/null | grep -qE ":$PORT([^0-9]|$)"; then
|
|
echo "ghidra-mcp: already serving on :$PORT — no-op"
|
|
exit 0
|
|
fi
|
|
|
|
# 2) Clear stale stop sentinel + a stale project lock (only when no analyzer is alive).
|
|
rm -f "$STOPREQ" 2>/dev/null || true
|
|
if ! pgrep -f 'ghidra.app.util.headless.AnalyzeHeadless' >/dev/null 2>&1; then
|
|
rm -f "$PROJ_DIR/$PROJ.lock" "$PROJ_DIR/$PROJ.lock~" 2>/dev/null || true
|
|
fi
|
|
|
|
# 3) Launch fully detached so it survives this shell and the Claude session.
|
|
echo "ghidra-mcp: starting headless server (detached) -> $LOG"
|
|
setsid nohup "$GHIDRA/support/analyzeHeadless" "$PROJ_DIR" "$PROJ" \
|
|
-process "$PROG" -noanalysis \
|
|
-scriptPath "$SCRIPTS" \
|
|
-preScript BfmMcpServer.java host=127.0.0.1 "port=$PORT" "stopreq=$STOPREQ" \
|
|
>"$LOG" 2>&1 </dev/null &
|
|
disown 2>/dev/null || true
|
|
|
|
echo "ghidra-mcp: launched (pid $!). Project load takes ~10-60s."
|
|
echo "ghidra-mcp: wait for 'serving' in $LOG, then run /mcp in Claude Code."
|