Files
steam/tools/decompile_batch.sh
SalastilandClaude Opus 5.5 32a19883d5 Decompile libsteam.so and linux32 steamclient.so; add batch decompile runner
linux32/steamclient.so is the 32-bit build games load via ~/.steam/sdk32.
tools/decompile_batch.sh batches many binaries per Ghidra JVM for the Steam
runtimes, mirroring their paths under decomp/runtime/.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 14:53:02 -04:00

50 lines
2.1 KiB
Bash
Executable File

#!/usr/bin/env bash
# Batch-decompile many binaries listed in a file (paths under client/), several
# per Ghidra JVM and several JVMs in parallel. Output mirrors the path under
# client/, e.g. client/runtime/x/lib/libc.so.6 -> decomp/runtime/x/lib/libc.so.6/.
# Ghidra databases are not kept (-readOnly), and binaries whose output already
# exists are skipped, so re-running resumes. Chunks that die (e.g. OOM) are
# retried one binary per JVM at the end.
# Usage: tools/decompile_batch.sh <list-file>
# Knobs: CHUNK (binaries per JVM, 40), PARALLEL (JVMs at once, 4), plus the
# per-JVM WORKERS / GHIDRA_HEADLESS_MAXMEM / MEMLIMIT from ghidra-env.sh.
set -uo pipefail
source "$(dirname "$0")/ghidra-env.sh"
LIST="$1"
CHUNK="${CHUNK:-40}" PARALLEL="${PARALLEL:-4}"
TAG="$(basename "$LIST" .txt)"
WORK="$ROOT/decomp/logs/batch_$TAG"
mkdir -p "$WORK"
pending() {
while read -r bin; do
[ -n "$bin" ] && [ ! -f "$ROOT/decomp/${bin#client/}/functions.tsv" ] && echo "$bin"
done < "$LIST"
}
run_chunk() { # $1 = chunk file
local name; name="batch_${TAG}_$(basename "$1")"
mapfile -t bins < "$1"
run_headless "$ROOT/ghidra_projects" "$name" \
-import "${bins[@]/#/$ROOT/}" -readOnly -deleteProject \
-scriptPath "$ROOT/tools/ghidra" \
-preScript SetAnalysisOptions.java "${ANALYSIS_OPTS:-GCC Exception Handlers=false}" \
-postScript RecoverClassesFromRTTIScript.java \
-postScript RecoverNamesFromStrings.java \
-postScript ExportDecompiled.java "$ROOT/decomp" "$ROOT/client" \
-analysisTimeoutPerFile 86400 \
-log "$WORK/$(basename "$1").log" > "$WORK/$(basename "$1").out" 2>&1
echo "$(basename "$1") rc=$? $(date +%T)" >> "$WORK/chunks.log"
}
export -f run_chunk run_headless
export ROOT GHIDRA WORK TAG MEMLIMIT
for pass in batch single; do
rm -f "$WORK"/list_*
[ "$pass" = single ] && CHUNK=1 PARALLEL=1
pending | split -l "$CHUNK" -d -a 5 - "$WORK/list_${pass}_"
ls "$WORK"/list_* >/dev/null 2>&1 || break
ls "$WORK"/list_* | xargs -P "$PARALLEL" -I{} bash -c 'run_chunk "$1"' _ {}
done
echo "$TAG: $(pending | wc -l) binaries still without output" | tee -a "$WORK/chunks.log"