From f874d709e5a45d63c8ee9ab6b0300195aa5d20a5 Mon Sep 17 00:00:00 2001 From: Arthur Hanson Rasmusson Date: Wed, 23 Sep 2026 12:11:04 -0400 Subject: 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). --- userspace/cuda/tools/ghidra/DecompileAt.java | 86 ++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 userspace/cuda/tools/ghidra/DecompileAt.java (limited to 'userspace/cuda/tools/ghidra/DecompileAt.java') diff --git a/userspace/cuda/tools/ghidra/DecompileAt.java b/userspace/cuda/tools/ghidra/DecompileAt.java new file mode 100644 index 0000000..ed8cf5d --- /dev/null +++ b/userspace/cuda/tools/ghidra/DecompileAt.java @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// DecompileAt.java - Ghidra post-script: decompile the function containing a +// given file virtual address. Version-agnostic copy of the per-version +// helper used with ghidra_decompile_at.sh. +// +// Usage: +// analyzeHeadless -process -readOnly -noanalysis \ +// -postScript DecompileAt.java [outfile] +// +// If the address does not fall inside a known function, the script tries to +// disassemble at the address; if the target is outside any function body it +// prints a comment with the nearest preceding function instead of failing. + +import ghidra.app.script.GhidraScript; +import ghidra.app.decompiler.DecompInterface; +import ghidra.app.decompiler.DecompileResults; +import ghidra.program.model.address.Address; +import ghidra.program.model.listing.Function; +import ghidra.program.model.listing.FunctionIterator; +import ghidra.program.model.listing.Listing; + +import java.io.FileWriter; + +public class DecompileAt extends GhidraScript { + @Override + public void run() throws Exception { + String[] args = getScriptArgs(); + if (args.length < 1) { + println("usage: DecompileAt.java [outfile]"); + return; + } + long fileAddr = Long.parseUnsignedLong(args[0].replaceFirst("^0x", ""), 16); + // Arguments are FILE virtual addresses (libcuda+0x...). Ghidra may have + // loaded the program at a non-zero image base (0x100000 in this build), + // so translate before resolving. If image base is 0 this is a no-op. + long imageBase = currentProgram.getImageBase().getOffset(); + Address addr = toAddr(fileAddr + imageBase); + long target = addr.getOffset(); + + Function f = getFunctionContaining(addr); + if (f == null) { + Listing listing = currentProgram.getListing(); + FunctionIterator it = listing.getFunctions(addr, false); + if (it.hasNext()) f = it.next(); + } + if (f == null) { + disassemble(addr); + f = createFunction(addr, null); + } + StringBuilder sb = new StringBuilder(); + if (f == null) { + sb.append("// no function at 0x").append(Long.toHexString(target)).append("\n"); + } else { + DecompInterface dif = new DecompInterface(); + dif.toggleCCode(true); + dif.toggleSyntaxTree(true); + dif.openProgram(currentProgram); + DecompileResults res = dif.decompileFunction(f, 300, monitor); + sb.append("// function ").append(f.getName()) + .append(" at libcuda+0x") + .append(Long.toHexString(f.getEntryPoint().getOffset() - imageBase)) + .append(" (program 0x").append(Long.toHexString(f.getEntryPoint().getOffset())) + .append(", query file 0x").append(Long.toHexString(fileAddr)).append(")\n"); + if (res != null && res.decompileCompleted()) { + sb.append("#include \n") + .append("typedef unsigned char undefined1;\n") + .append("typedef unsigned short undefined2;\n") + .append("typedef unsigned int undefined4;\n") + .append("typedef unsigned long long undefined8;\n") + .append("typedef long long longlong;\n") + .append("typedef unsigned long long ulonglong;\n\n") + .append(res.getDecompiledFunction().getC()); + } else { + sb.append("// decompilation failed\n"); + } + } + String text = sb.toString(); + if (args.length > 1) { + try (FileWriter w = new FileWriter(args[1])) { + w.write(text); + } + } + println(text); + } +} -- cgit v1.2.3