summaryrefslogtreecommitdiffstats
path: root/userspace/cuda/tools/ghidra/BatchDecompile.java
blob: 03196817f6fd1ef405e98155606b0ab24571043e (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
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
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()));
    }
}