blob: bce8cb90992e427eb75574f72c337d3ac571f5bd (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
|
#!/bin/bash
# SPDX-License-Identifier: BSD-3-Clause
#
# ghidra_batch_decompile.sh - decompile the whole code range of an analyzed
# CUDA program in parallel. Chunks already present are skipped, so the command
# is safe to re-run and to interrupt.
#
# Ghidra takes an exclusive project lock even in -readOnly mode, so a single
# project cannot be opened by several processes. To parallelize, this script
# makes one copy of the analyzed project per worker under <outdir>/wk/ and
# round-robins the chunks across the copies. Copies are kept (they make
# resuming cheap) and can be removed with: rm -rf <outdir>/wk
#
# Usage:
# ghidra_batch_decompile.sh <project_dir> <project_name> <program> <outdir> \
# <start_hex> <end_hex> [chunk_size_hex] [jobs] [timeout_s]
#
# Defaults: chunk 0x100000, jobs 5, timeout 120.
# env: GHIDRA_HOME, GHIDRA_HEADLESS_MAXMEM (default 4G for batch jobs), MAX_CPU
#
# After it completes, merge per-chunk indexes with:
# ghidra_index_merge.sh <outdir>
set -euo pipefail
HERE="$(cd "$(dirname "$0")" && pwd)"
GHIDRA_HOME="${GHIDRA_HOME:-/snap/ghidra/current/ghidra}"
ANALYZE="$GHIDRA_HOME/support/analyzeHeadless"
export GHIDRA_HEADLESS_MAXMEM="${GHIDRA_HEADLESS_MAXMEM:-4G}"
PROJECT_DIR="${1:?usage: $0 <project_dir> <project_name> <program> <outdir> <start_hex> <end_hex> [chunk_hex] [jobs] [timeout_s]}"
PROJECT_NAME="${2:?}"
PROGRAM="${3:?}"
OUTDIR="${4:?}"
START="${5:?}"
END="${6:?}"
CHUNK="${7:-0x100000}"
JOBS="${8:-5}"
TIMEOUT="${9:-120}"
mkdir -p "$OUTDIR/buckets" "$OUTDIR/index" "$OUTDIR/logs" "$OUTDIR/wk"
START_D=$((START)); END_D=$((END)); CHUNK_D=$((CHUNK))
# Build the chunk list first: every chunk gets its OWN project copy, so two
# concurrent jobs can never race on the same Ghidra project lock.
ranges=""
n=0
for ((a=START_D; a<END_D; a+=CHUNK_D)); do
b=$((a+CHUNK_D)); [ "$b" -gt "$END_D" ] && b=$END_D
ranges+=$(printf "0x%x 0x%x %d\n" "$a" "$b" "$n")$'\n'
n=$((n+1))
done
# Create project copies for the chunks that still need work.
while read -r _a _b w; do
[ -z "$w" ] && continue
out="$OUTDIR/buckets/bucket_${_a#0x}_${_b#0x}.c"
idx="$OUTDIR/index/bucket_${_a#0x}_${_b#0x}.tsv"
if [ -f "$out" ] && [ -f "$idx" ]; then continue; fi
wdir="$OUTDIR/wk"
if [ ! -d "$wdir/$PROJECT_NAME.w$w.rep" ]; then
echo "[*] creating project copy $w"
cp -a "$PROJECT_DIR/$PROJECT_NAME.gpr" "$wdir/$PROJECT_NAME.w$w.gpr"
cp -a "$PROJECT_DIR/$PROJECT_NAME.rep" "$wdir/$PROJECT_NAME.w$w.rep"
fi
done <<< "$ranges"
echo "[*] decompiling $PROGRAM in $n chunks across $JOBS workers"
echo "$ranges" | xargs -P "$JOBS" -n 3 bash -c '
set -e
a="$1"; b="$2"; w="$3"
tag="${a#0x}_${b#0x}"
log="'$OUTDIR'/logs/decompile_${tag}.log"
GHIDRA_HEADLESS_MAXMEM="'$GHIDRA_HEADLESS_MAXMEM'" "'$ANALYZE'" "'$OUTDIR'/wk" "'$PROJECT_NAME'.w$w" \
-process "'$PROGRAM'" -readOnly -noanalysis \
-scriptPath "'$HERE'/ghidra" \
-postScript BatchDecompile.java "$a" "$b" "'$OUTDIR'" "'$TIMEOUT'" \
> "$log" 2>&1 || { echo "[FAIL] $a-$b (see $log)"; exit 1; }
tail -1 "$log"
' _
echo "[ok] batch decompilation done -> $OUTDIR/buckets"
echo " merge indexes: $HERE/ghidra_index_merge.sh $OUTDIR"
|