summaryrefslogtreecommitdiffstats
path: root/userspace/cuda/tools/ghidra/BatchDecompile.java
diff options
context:
space:
mode:
authorArthur Hanson Rasmusson <arthur@vgpu.io>2026-09-23 12:11:04 -0400
committerArthur Hanson Rasmusson <arthur@vgpu.io>2026-09-23 12:11:04 -0400
commitf874d709e5a45d63c8ee9ab6b0300195aa5d20a5 (patch)
tree2d07208f789e402686ac960e5ad7456b6fd21cfd /userspace/cuda/tools/ghidra/BatchDecompile.java
parentae18e08d02500579f084616874d8f2ae57f8281b (diff)
downloadopen-iov-f874d709e5a45d63c8ee9ab6b0300195aa5d20a5.tar.gz
open-iov-f874d709e5a45d63c8ee9ab6b0300195aa5d20a5.zip
userspace/cuda: add CUDA userspace driver analysis, tooling and name maps
Add the analysis of the NVIDIA CUDA userspace driver used by Open-IOV VMs: * docs/01-08: architecture (layers, object model, threads, sync, memory, channels, launch), build and execution model, the driver/kernel/GPU interface contract, virtualization approaches, the 570.144 decompilation map with the corrected libcuda crash path and the current engine-topology blocker, tooling, and a version-upgrade playbook. * tools/: version-agnostic toolchain - stdlib ELF/interface toolkit (exports, unwind-table function map, wrapper resolution, version delta, crash address decode), callgraph walker, corpus annotator, and Ghidra analysis/decompilation/name-map scripts with resumable parallel drivers. * 570.144/annotations/: our name map (exports + resolved wrappers + verified internals) and subsystem index, tracked so naming work accumulates. * READ-ME-FIRST.md: reading guide with the fast lookup recipes. The full decompiled corpus and annotated corpus stay untracked (vendor- derived, gitignored under the version run/ tree).
Diffstat (limited to 'userspace/cuda/tools/ghidra/BatchDecompile.java')
-rw-r--r--userspace/cuda/tools/ghidra/BatchDecompile.java141
1 files changed, 141 insertions, 0 deletions
diff --git a/userspace/cuda/tools/ghidra/BatchDecompile.java b/userspace/cuda/tools/ghidra/BatchDecompile.java
new file mode 100644
index 0000000..0319681
--- /dev/null
+++ b/userspace/cuda/tools/ghidra/BatchDecompile.java
@@ -0,0 +1,141 @@
+// SPDX-License-Identifier: BSD-3-Clause
+//
+// BatchDecompile.java - Ghidra post-script: decompile every function whose
+// entry address lies in [start, end) and write the C output plus a
+// machine-readable index.
+//
+// Designed for a very large stripped vendor library (~100k functions, 70 MB).
+// The caller splits the code address range into chunks and runs one
+// analyzeHeadless process per chunk (all with -readOnly against the same
+// analyzed project), which parallelizes and makes the run resumable: a chunk
+// whose output already exists is skipped.
+//
+// Usage (analyzeHeadless, on an already analyzed program):
+// -process <program> -readOnly -noanalysis \
+// -postScript BatchDecompile.java <startAddrHex> <endAddrHex> <outdir> [timeoutSecs]
+//
+// Outputs:
+// <outdir>/buckets/bucket_<start>_<end>.c decompiled C, functions in order
+// <outdir>/index/bucket_<start>_<end>.tsv addr size name file line status
+//
+// Decompiled output is vendor-derived analysis output: it stays in the
+// version's gitignored run/ tree and is NEVER committed.
+
+import ghidra.app.script.GhidraScript;
+import ghidra.app.decompiler.DecompInterface;
+import ghidra.app.decompiler.DecompileResults;
+import ghidra.program.model.listing.Function;
+import ghidra.program.model.listing.FunctionIterator;
+import ghidra.program.model.listing.Listing;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.PrintWriter;
+
+public class BatchDecompile extends GhidraScript {
+
+ private static int countLines(String s) {
+ int n = 0;
+ for (int i = 0; i < s.length(); i++) {
+ if (s.charAt(i) == '\n') n++;
+ }
+ return n;
+ }
+
+ @Override
+ public void run() throws Exception {
+ String[] args = getScriptArgs();
+ if (args.length < 3) {
+ println("usage: BatchDecompile.java <startAddrHex> <endAddrHex> <outdir> [timeoutSecs]");
+ return;
+ }
+ long start = Long.parseUnsignedLong(args[0].replaceFirst("^0x", ""), 16);
+ long end = Long.parseUnsignedLong(args[1].replaceFirst("^0x", ""), 16);
+ String outdir = args[2];
+ int timeout = args.length > 3 ? Integer.parseInt(args[3]) : 120;
+
+ File buckets = new File(outdir, "buckets");
+ File indexDir = new File(outdir, "index");
+ buckets.mkdirs();
+ indexDir.mkdirs();
+ String tag = String.format("%08x_%08x", start, end);
+ File outFile = new File(buckets, "bucket_" + tag + ".c");
+ File idxFile = new File(indexDir, "bucket_" + tag + ".tsv");
+
+ if (outFile.isFile() && idxFile.isFile()) {
+ println("[BatchDecompile] chunk " + tag + " already done, skipping");
+ return;
+ }
+
+ Listing listing = currentProgram.getListing();
+ long imageBase = currentProgram.getImageBase().getOffset();
+ DecompInterface dif = new DecompInterface();
+ dif.toggleCCode(true);
+ dif.toggleSyntaxTree(false);
+ dif.openProgram(currentProgram);
+
+ int n = 0, fail = 0, skip = 0, lines = 0;
+
+ try (PrintWriter idx = new PrintWriter(new BufferedWriter(new FileWriter(idxFile)));
+ PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(outFile)))) {
+
+ String banner = "/* CUDA userspace driver decompilation - Open-IOV project\n"
+ + " * program: " + currentProgram.getName() + "\n"
+ + " * range: [0x" + Long.toHexString(start) + ", 0x"
+ + Long.toHexString(end) + ")\n"
+ + " * Machine-generated analysis output; not part of the public tree.\n"
+ + " */\n";
+ out.print(banner);
+ lines += countLines(banner);
+ idx.println("addr\tfile_addr\tsize\tname\tfile\tline\tstatus");
+
+ FunctionIterator fit = listing.getFunctions(true);
+ while (fit.hasNext() && !monitor.isCancelled()) {
+ Function f = fit.next();
+ if (f.isExternal()) continue;
+ long addr = f.getEntryPoint().getOffset();
+ if (addr < start || addr >= end) continue;
+
+ long size = f.getBody().getNumAddresses();
+ String header = String.format(
+ "\n/* ------------------------------------------------------------------"
+ + "----------\n"
+ + " * %s\n"
+ + " * addr=0x%08x file_addr=libcuda+0x%x size=%d\n"
+ + " *-------------------------------------------------------------------"
+ + "-------- */\n",
+ f.getName(), addr, addr - imageBase, size);
+ out.print(header);
+ lines += countLines(header);
+
+ String status = "ok";
+ String code;
+ if (size > 200000) {
+ status = "skipped-huge";
+ skip++;
+ code = "/* skipped: body too large (" + size + " bytes) */\n";
+ } else {
+ DecompileResults res = dif.decompileFunction(f, timeout, monitor);
+ if (res != null && res.decompileCompleted()) {
+ code = res.getDecompiledFunction().getC();
+ } else {
+ status = "failed";
+ fail++;
+ code = "/* decompilation failed */\n";
+ }
+ }
+ out.print(code);
+ out.print("\n");
+ lines += countLines(code) + 1;
+ idx.printf("0x%08x\t0x%08x\t%d\t%s\t%s\t%d\t%s%n",
+ addr, addr - imageBase, size, f.getName(),
+ outFile.getName(), lines - countLines(code), status);
+ n++;
+ }
+ }
+
+ println(String.format("[BatchDecompile] %s: functions=%d failed=%d skipped=%d -> %s",
+ tag, n, fail, skip, outFile.getAbsolutePath()));
+ }
+}