@henols/c64-re-tools 1.1.0 → 2.0.0-rc.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +154 -37
- package/distribution/package.ts +21 -0
- package/distribution/plugin.ts +122 -0
- package/package.json +34 -40
- package/skills/c64-assembler/SKILL.md +76 -0
- package/skills/c64-assembler/scripts/assemble.ts +110 -0
- package/skills/c64-assembler/scripts/compare.ts +84 -0
- package/skills/c64-basic/SKILL.md +66 -0
- package/skills/c64-basic/scripts/basic.ts +46 -0
- package/skills/c64-disk/SKILL.md +93 -0
- package/skills/c64-disk/scripts/containers.ts +124 -0
- package/skills/c64-disk/scripts/disk.ts +120 -0
- package/skills/c64-emulator/SKILL.md +134 -0
- package/skills/c64-knowledge/SKILL.md +166 -0
- package/skills/c64-knowledge/scripts/knowledge.ts +492 -0
- package/skills/c64-memory-map/SKILL.md +79 -0
- package/skills/c64-memory-map/scripts/memmap.ts +92 -0
- package/skills/c64-provenance/SKILL.md +110 -0
- package/skills/c64-provenance/scripts/compare.ts +152 -0
- package/skills/c64-provenance/scripts/provenance.ts +334 -0
- package/skills/c64-provenance/scripts/runtime.ts +191 -0
- package/skills/c64-provenance/scripts/separate.ts +140 -0
- package/skills/c64-reverse-engineering/SKILL.md +144 -0
- package/skills/c64-source/SKILL.md +126 -0
- package/skills/c64-source/scripts/generate.ts +490 -0
- package/skills/c64-source/scripts/loader.ts +571 -0
- package/skills/c64-source/scripts/opcodes.ts +61 -0
- package/skills/c64-source/scripts/source.ts +595 -0
- package/skills/c64-static-analysis/SKILL.md +119 -0
- package/skills/c64-static-analysis/scripts/analyze.ts +275 -0
- package/skills/c64-static-analysis/scripts/coverage.ts +78 -0
- package/skills/c64-static-analysis/scripts/findings.ts +75 -0
- package/skills/c64-static-analysis/scripts/idioms.ts +149 -0
- package/skills/c64-static-analysis/scripts/instructions.ts +105 -0
- package/skills/c64-testing/SKILL.md +103 -0
- package/skills/c64-testing/references/scenario-format.md +116 -0
- package/skills/c64-testing/scripts/checklist.ts +51 -0
- package/skills/c64-testing/scripts/run.ts +189 -0
- package/skills/c64-testing/scripts/scenario.ts +493 -0
- package/skills/c64-testing/scripts/test.ts +65 -0
- package/skills/c64-unpacker/SKILL.md +135 -0
- package/skills/c64-unpacker/scripts/evidence.ts +207 -0
- package/skills/c64-unpacker/scripts/loads.ts +209 -0
- package/skills/c64-unpacker/scripts/machine.ts +80 -0
- package/skills/c64-unpacker/scripts/stages.ts +130 -0
- package/skills/c64-unpacker/scripts/unpack.ts +683 -0
- package/src/c64.ts +411 -0
- package/src/cli/ap-sdk.ts +44 -0
- package/src/cli/cleanup.ts +194 -0
- package/src/cli/main.ts +125 -0
- package/src/cli/status.ts +61 -0
- package/src/host/main.ts +134 -0
- package/src/host/server.ts +392 -0
- package/src/host/tools/c1541.ts +284 -0
- package/src/host/tools/index.ts +35 -0
- package/src/host/tools/petcat.ts +77 -0
- package/src/host/tools/status.ts +47 -0
- package/src/host/tools/version.ts +41 -0
- package/src/host/vice/adapter.ts +804 -0
- package/src/host/vice/binary-monitor.ts +389 -0
- package/src/host/vice/chips.ts +118 -0
- package/src/host/vice/connect.ts +34 -0
- package/src/host/vice/process.ts +413 -0
- package/src/host/vice/screen.ts +184 -0
- package/src/host/vice/session.ts +1426 -0
- package/src/host/vice/text-monitor.ts +223 -0
- package/src/host-client/autostart.ts +55 -0
- package/src/host-client/connect.ts +241 -0
- package/src/host-client/coverage.ts +63 -0
- package/src/host-client/steps.ts +170 -0
- package/src/host-client/tools.ts +89 -0
- package/src/host-client/transfer.ts +126 -0
- package/src/host-client/vice-session.ts +316 -0
- package/src/knowledge/database.ts +132 -0
- package/src/knowledge/history.ts +163 -0
- package/src/knowledge/import.ts +391 -0
- package/src/knowledge/read.ts +342 -0
- package/src/knowledge/schema.ts +229 -0
- package/src/knowledge/write.ts +663 -0
- package/src/mcp/main.ts +134 -0
- package/src/mcp/output.ts +32 -0
- package/src/mcp/server.ts +316 -0
- package/src/mcp/tools/debug.ts +327 -0
- package/src/mcp/tools/execution.ts +117 -0
- package/src/mcp/tools/input.ts +72 -0
- package/src/mcp/tools/machine.ts +100 -0
- package/src/mcp/tools/media.ts +129 -0
- package/src/mcp/tools/memory.ts +250 -0
- package/src/mcp/tools/video.ts +271 -0
- package/src/native/acme.ts +116 -0
- package/src/native/discover.ts +179 -0
- package/src/native/dxa.ts +166 -0
- package/src/native/ghidra/analyze.ts +86 -0
- package/src/native/ghidra/index.ts +173 -0
- package/src/native/ghidra/language/c64rt_6510.cspec +34 -0
- package/src/native/ghidra/language/c64rt_6510.ldefs +15 -0
- package/src/native/ghidra/language/c64rt_6510.pspec +15 -0
- package/src/native/ghidra/language/c64rt_6510.slaspec +596 -0
- package/src/native/ghidra/scripts/C64Export.java +274 -0
- package/src/native/ghidra/scripts/C64Prepare.java +60 -0
- package/src/native/local.ts +34 -0
- package/src/native/processes.ts +350 -0
- package/src/native/run.ts +172 -0
- package/src/native/staging.ts +83 -0
- package/src/native/status.ts +113 -0
- package/src/native/types.ts +257 -0
- package/src/native/watchdog.ts +78 -0
- package/src/package.ts +21 -0
- package/src/platform.ts +441 -0
- package/src/project.ts +39 -0
- package/src/protocol/framing.ts +132 -0
- package/src/protocol/messages.ts +259 -0
- package/src/protocol/tools.ts +266 -0
- package/src/protocol/vice.ts +1348 -0
- package/src/script.ts +60 -0
- package/src/time.ts +15 -0
- package/src/trace.ts +194 -0
- package/THIRD-PARTY-NOTICES.md +0 -26
- package/bin/cli.mjs +0 -283
- package/skills/acme-build/SKILL.md +0 -260
- package/skills/acme-build/scripts/acme.mjs +0 -341
- package/skills/acme-build/template.a +0 -53
- package/skills/c64-disk-access/SKILL.md +0 -159
- package/skills/c64-disk-access/scripts/c1541.mjs +0 -569
- package/skills/c64-memory-mapping/SKILL.md +0 -631
- package/skills/c64-memory-mapping/memmap.json +0 -8800
- package/skills/c64-memory-mapping/scripts/driver.mjs +0 -553
- package/skills/c64-petcat/SKILL.md +0 -87
- package/skills/c64-petcat/scripts/petcat.mjs +0 -221
- package/skills/c64-program-recon/SKILL.md +0 -706
- package/skills/c64-program-recon/references/control-flow.md +0 -180
- package/skills/c64-program-recon/references/graphics.md +0 -79
- package/skills/c64-program-recon/references/observation-hazards.md +0 -143
- package/skills/c64-program-recon/references/reconstruction.md +0 -133
- package/skills/c64-program-recon/references/sound-and-input.md +0 -74
- package/skills/c64-program-recon/references/tool-selection.md +0 -78
- package/skills/c64-program-recon/scripts/derive.mjs +0 -364
- package/skills/c64-program-recon/scripts/packer-finding.mjs +0 -709
- package/skills/c64-program-recon/templates/memory-map.template.md +0 -101
- package/skills/c64-provenance-diff/SKILL.md +0 -292
- package/skills/c64-provenance-diff/scripts/diff-images.mjs +0 -984
- package/skills/c64-provenance-diff/scripts/recovery-schema.mjs +0 -395
- package/skills/c64-ram-capture/RELEASES.json.example +0 -17
- package/skills/c64-ram-capture/SKILL.md +0 -474
- package/skills/c64-ram-capture/scripts/compare-cross-binary.mjs +0 -525
- package/skills/c64-ram-capture/scripts/compare.mjs +0 -258
- package/skills/c64-ram-capture/scripts/derive-transients.mjs +0 -575
- package/skills/c64-ram-capture/scripts/dump-artifacts.mjs +0 -317
- package/skills/c64-ram-capture/scripts/mcp-module.mjs +0 -174
- package/skills/c64-ram-capture/scripts/project-paths.mjs +0 -81
- package/skills/c64-ram-capture/scripts/releases.mjs +0 -109
- package/skills/c64-ram-capture/scripts/vsf-slice.mjs +0 -147
- package/skills/c64-ram-capture/scripts/watch-loads.mjs +0 -581
- package/skills/c64-ram-capture/templates/capture-record.template.md +0 -99
- package/skills/c64-ram-capture/transients/README.md +0 -137
- package/skills/routine-queue-walker/SKILL.md +0 -394
- package/skills/routine-queue-walker/scripts/completeness-report.mjs +0 -465
- package/skills/vice-wedge-triage/SKILL.md +0 -269
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
// Runs after Ghidra's analysis. Arguments: <request.json> <out.json>.
|
|
2
|
+
//
|
|
3
|
+
// Writes the structural result for the loaded image: functions with the
|
|
4
|
+
// source of their names, code and defined-data regions, memory references
|
|
5
|
+
// from instructions, every instruction with its bytes, mnemonic, operand
|
|
6
|
+
// text and flow targets (D44), and the requested decompilations within their bounds.
|
|
7
|
+
//@category c64-re-tools
|
|
8
|
+
|
|
9
|
+
import java.io.FileReader;
|
|
10
|
+
import java.io.FileWriter;
|
|
11
|
+
import java.util.LinkedHashSet;
|
|
12
|
+
import java.util.Set;
|
|
13
|
+
|
|
14
|
+
import com.google.gson.Gson;
|
|
15
|
+
import com.google.gson.JsonArray;
|
|
16
|
+
import com.google.gson.JsonElement;
|
|
17
|
+
import com.google.gson.JsonObject;
|
|
18
|
+
|
|
19
|
+
import ghidra.app.decompiler.DecompInterface;
|
|
20
|
+
import ghidra.app.decompiler.DecompileResults;
|
|
21
|
+
import ghidra.app.script.GhidraScript;
|
|
22
|
+
import ghidra.program.model.address.Address;
|
|
23
|
+
import ghidra.program.model.address.AddressSet;
|
|
24
|
+
import ghidra.program.model.listing.CodeUnit;
|
|
25
|
+
import ghidra.program.model.listing.Data;
|
|
26
|
+
import ghidra.program.model.listing.Function;
|
|
27
|
+
import ghidra.program.model.listing.Instruction;
|
|
28
|
+
import ghidra.program.model.symbol.RefType;
|
|
29
|
+
import ghidra.program.model.symbol.Reference;
|
|
30
|
+
import ghidra.program.model.symbol.SourceType;
|
|
31
|
+
|
|
32
|
+
public class C64Export extends GhidraScript {
|
|
33
|
+
private static final java.util.regex.Pattern NAME = java.util.regex.Pattern.compile("^\\.?[A-Za-z_][A-Za-z0-9_]{0,63}$");
|
|
34
|
+
/** The longest reason that the result check accepts. */
|
|
35
|
+
private static final int MAX_REASON = 1000;
|
|
36
|
+
/** The longest time for one decompilation, in seconds. */
|
|
37
|
+
private static final int MAX_DECOMPILE_SECONDS = 60;
|
|
38
|
+
|
|
39
|
+
@Override
|
|
40
|
+
public void run() throws Exception {
|
|
41
|
+
String[] args = getScriptArgs();
|
|
42
|
+
JsonObject request;
|
|
43
|
+
try (FileReader reader = new FileReader(args[0])) {
|
|
44
|
+
request = new Gson().fromJson(reader, JsonObject.class);
|
|
45
|
+
}
|
|
46
|
+
long start = request.get("start").getAsLong();
|
|
47
|
+
long end = request.get("end").getAsLong();
|
|
48
|
+
AddressSet coverage = new AddressSet(toAddr(start), toAddr(end));
|
|
49
|
+
|
|
50
|
+
JsonObject out = new JsonObject();
|
|
51
|
+
JsonArray coverageOut = new JsonArray();
|
|
52
|
+
coverageOut.add(range(start, end));
|
|
53
|
+
out.add("coverage", coverageOut);
|
|
54
|
+
out.add("functions", functions(coverage));
|
|
55
|
+
out.add("regions", regions(coverage));
|
|
56
|
+
out.add("references", references(coverage));
|
|
57
|
+
out.add("instructions", instructions(coverage));
|
|
58
|
+
JsonArray notDecompiled = new JsonArray();
|
|
59
|
+
out.add("decompilations", decompile(request, notDecompiled));
|
|
60
|
+
out.add("notDecompiled", notDecompiled);
|
|
61
|
+
JsonObject completeness = new JsonObject();
|
|
62
|
+
completeness.addProperty("functions", true);
|
|
63
|
+
completeness.addProperty("regions", true);
|
|
64
|
+
completeness.addProperty("references", true);
|
|
65
|
+
out.add("completeness", completeness);
|
|
66
|
+
try (FileWriter writer = new FileWriter(args[1])) {
|
|
67
|
+
new Gson().toJson(out, writer);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
private static JsonObject range(long start, long end) {
|
|
72
|
+
JsonObject range = new JsonObject();
|
|
73
|
+
range.addProperty("start", start);
|
|
74
|
+
range.addProperty("end", end);
|
|
75
|
+
return range;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
private JsonArray functions(AddressSet coverage) {
|
|
79
|
+
JsonArray functions = new JsonArray();
|
|
80
|
+
for (Function function : currentProgram.getFunctionManager().getFunctions(coverage, true)) {
|
|
81
|
+
String name = function.getName();
|
|
82
|
+
SourceType source = function.getSymbol().getSource();
|
|
83
|
+
// Only the seeds carry user-defined names in this disposable project.
|
|
84
|
+
String nameSource = source == SourceType.USER_DEFINED ? "seed" : source == SourceType.DEFAULT ? "generated" : "native";
|
|
85
|
+
if (!NAME.matcher(name).matches()) {
|
|
86
|
+
// A name that this script makes is generated, whatever name it replaces.
|
|
87
|
+
name = String.format("FUN_%04x", function.getEntryPoint().getOffset());
|
|
88
|
+
nameSource = "generated";
|
|
89
|
+
}
|
|
90
|
+
JsonObject item = new JsonObject();
|
|
91
|
+
item.addProperty("entry", function.getEntryPoint().getOffset());
|
|
92
|
+
item.addProperty("name", name);
|
|
93
|
+
item.addProperty("nameSource", nameSource);
|
|
94
|
+
functions.add(item);
|
|
95
|
+
}
|
|
96
|
+
return functions;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/** Instructions are code and defined data is data; undefined bytes are not classified. */
|
|
100
|
+
private JsonArray regions(AddressSet coverage) {
|
|
101
|
+
JsonArray regions = new JsonArray();
|
|
102
|
+
String kind = null;
|
|
103
|
+
long first = 0;
|
|
104
|
+
long last = -2;
|
|
105
|
+
for (CodeUnit unit : currentProgram.getListing().getCodeUnits(coverage, true)) {
|
|
106
|
+
String unitKind = unit instanceof Instruction ? "code" : (unit instanceof Data && ((Data) unit).isDefined()) ? "data" : null;
|
|
107
|
+
long unitStart = unit.getMinAddress().getOffset();
|
|
108
|
+
long unitEnd = Math.min(unit.getMaxAddress().getOffset(), coverage.getMaxAddress().getOffset());
|
|
109
|
+
if (unitKind != null && unitKind.equals(kind) && unitStart == last + 1) {
|
|
110
|
+
last = unitEnd;
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
if (kind != null) {
|
|
114
|
+
JsonObject region = range(first, last);
|
|
115
|
+
region.addProperty("classification", kind);
|
|
116
|
+
regions.add(region);
|
|
117
|
+
}
|
|
118
|
+
kind = unitKind;
|
|
119
|
+
first = unitStart;
|
|
120
|
+
last = unitEnd;
|
|
121
|
+
}
|
|
122
|
+
if (kind != null) {
|
|
123
|
+
JsonObject region = range(first, last);
|
|
124
|
+
region.addProperty("classification", kind);
|
|
125
|
+
regions.add(region);
|
|
126
|
+
}
|
|
127
|
+
return regions;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
private JsonArray references(AddressSet coverage) {
|
|
131
|
+
Set<String> seen = new LinkedHashSet<>();
|
|
132
|
+
JsonArray references = new JsonArray();
|
|
133
|
+
for (Instruction instruction : currentProgram.getListing().getInstructions(coverage, true)) {
|
|
134
|
+
for (Reference reference : instruction.getReferencesFrom()) {
|
|
135
|
+
RefType type = reference.getReferenceType();
|
|
136
|
+
Address to = reference.getToAddress();
|
|
137
|
+
if (!reference.isMemoryReference() || !to.isMemoryAddress() || type == RefType.FALL_THROUGH) {
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
long from = instruction.getMinAddress().getOffset();
|
|
141
|
+
if (type.isCall()) {
|
|
142
|
+
add(references, seen, from, to.getOffset(), "call");
|
|
143
|
+
} else if (type.isJump() || type.isFlow()) {
|
|
144
|
+
add(references, seen, from, to.getOffset(), "jump");
|
|
145
|
+
} else {
|
|
146
|
+
if (type.isRead()) {
|
|
147
|
+
add(references, seen, from, to.getOffset(), "read");
|
|
148
|
+
}
|
|
149
|
+
if (type.isWrite()) {
|
|
150
|
+
add(references, seen, from, to.getOffset(), "write");
|
|
151
|
+
}
|
|
152
|
+
if (!type.isRead() && !type.isWrite()) {
|
|
153
|
+
add(references, seen, from, to.getOffset(), "reference");
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return references;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/** Every instruction in the coverage, in address order, as Ghidra decoded it with the 6510 language. */
|
|
162
|
+
private JsonArray instructions(AddressSet coverage) throws Exception {
|
|
163
|
+
JsonArray instructions = new JsonArray();
|
|
164
|
+
for (Instruction instruction : currentProgram.getListing().getInstructions(coverage, true)) {
|
|
165
|
+
JsonObject item = new JsonObject();
|
|
166
|
+
item.addProperty("address", instruction.getMinAddress().getOffset());
|
|
167
|
+
StringBuilder bytes = new StringBuilder();
|
|
168
|
+
for (byte value : instruction.getBytes()) {
|
|
169
|
+
bytes.append(String.format("%02x", value & 0xff));
|
|
170
|
+
}
|
|
171
|
+
item.addProperty("bytes", bytes.toString());
|
|
172
|
+
item.addProperty("mnemonic", instruction.getMnemonicString().toLowerCase());
|
|
173
|
+
StringBuilder operands = new StringBuilder();
|
|
174
|
+
for (int index = 0; index < instruction.getNumOperands(); index++) {
|
|
175
|
+
if (index > 0) {
|
|
176
|
+
operands.append(",");
|
|
177
|
+
}
|
|
178
|
+
operands.append(instruction.getDefaultOperandRepresentation(index));
|
|
179
|
+
}
|
|
180
|
+
item.addProperty("operands", operands.toString());
|
|
181
|
+
JsonArray flows = new JsonArray();
|
|
182
|
+
for (Address flow : instruction.getFlows()) {
|
|
183
|
+
flows.add(flow.getOffset());
|
|
184
|
+
}
|
|
185
|
+
item.add("flows", flows);
|
|
186
|
+
instructions.add(item);
|
|
187
|
+
}
|
|
188
|
+
return instructions;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
private static void add(JsonArray references, Set<String> seen, long from, long to, String type) {
|
|
192
|
+
if (!seen.add(from + ":" + to + ":" + type)) {
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
JsonObject reference = new JsonObject();
|
|
196
|
+
reference.addProperty("from", from);
|
|
197
|
+
reference.addProperty("to", to);
|
|
198
|
+
reference.addProperty("type", type);
|
|
199
|
+
references.add(reference);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/** A requested routine without decompiled code, and the decompiler's reason. */
|
|
203
|
+
private static void notDecompiled(JsonArray list, long entry, String reason) {
|
|
204
|
+
JsonObject item = new JsonObject();
|
|
205
|
+
item.addProperty("entry", entry);
|
|
206
|
+
String text = reason == null || reason.isBlank() ? "the decompiler gave no reason" : reason.strip();
|
|
207
|
+
item.addProperty("reason", text.length() > MAX_REASON ? text.substring(0, MAX_REASON) : text);
|
|
208
|
+
list.add(item);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
private JsonArray decompile(JsonObject request, JsonArray failures) {
|
|
212
|
+
JsonArray decompilations = new JsonArray();
|
|
213
|
+
JsonArray entries = request.getAsJsonArray("decompile");
|
|
214
|
+
if (entries.size() == 0) {
|
|
215
|
+
return decompilations;
|
|
216
|
+
}
|
|
217
|
+
int perFunction = request.get("maxChars").getAsInt();
|
|
218
|
+
int remaining = request.get("maxTotalChars").getAsInt();
|
|
219
|
+
// The time that is left for the decompilations, as a clock time in milliseconds.
|
|
220
|
+
long deadline = request.get("decompileDeadline").getAsLong();
|
|
221
|
+
int routinesLeft = entries.size();
|
|
222
|
+
DecompInterface decompiler = new DecompInterface();
|
|
223
|
+
try {
|
|
224
|
+
// Ghidra ships no native decompiler for some platforms (macOS): it then does not start.
|
|
225
|
+
if (!decompiler.openProgram(currentProgram)) {
|
|
226
|
+
String reason = "the Ghidra decompiler did not start: " + decompiler.getLastMessage();
|
|
227
|
+
for (JsonElement element : entries) {
|
|
228
|
+
notDecompiled(failures, element.getAsLong(), reason);
|
|
229
|
+
}
|
|
230
|
+
return decompilations;
|
|
231
|
+
}
|
|
232
|
+
for (JsonElement element : entries) {
|
|
233
|
+
// Each routine gets an equal part of the time that is left, so every routine gets a turn.
|
|
234
|
+
long secondsLeft = (deadline - System.currentTimeMillis()) / 1000;
|
|
235
|
+
int seconds = (int) Math.min(MAX_DECOMPILE_SECONDS, secondsLeft / routinesLeft);
|
|
236
|
+
routinesLeft--;
|
|
237
|
+
Function function = getFunctionAt(toAddr(element.getAsLong()));
|
|
238
|
+
if (function == null) {
|
|
239
|
+
notDecompiled(failures, element.getAsLong(), "no function starts at this address");
|
|
240
|
+
continue;
|
|
241
|
+
}
|
|
242
|
+
if (remaining <= 0) {
|
|
243
|
+
notDecompiled(failures, element.getAsLong(), "the decompiled text of the other routines used the whole limit");
|
|
244
|
+
continue;
|
|
245
|
+
}
|
|
246
|
+
if (seconds < 1) {
|
|
247
|
+
notDecompiled(failures, element.getAsLong(), "the time for the Ghidra run ended before this routine");
|
|
248
|
+
continue;
|
|
249
|
+
}
|
|
250
|
+
DecompileResults results = decompiler.decompileFunction(function, seconds, monitor);
|
|
251
|
+
if (!results.decompileCompleted() || results.getDecompiledFunction() == null) {
|
|
252
|
+
notDecompiled(failures, element.getAsLong(), results.getErrorMessage());
|
|
253
|
+
continue;
|
|
254
|
+
}
|
|
255
|
+
String text = results.getDecompiledFunction().getC();
|
|
256
|
+
int limit = Math.min(perFunction, remaining);
|
|
257
|
+
boolean truncated = text.length() > limit;
|
|
258
|
+
if (truncated) {
|
|
259
|
+
text = text.substring(0, limit);
|
|
260
|
+
}
|
|
261
|
+
remaining -= text.length();
|
|
262
|
+
JsonObject item = new JsonObject();
|
|
263
|
+
item.addProperty("entry", function.getEntryPoint().getOffset());
|
|
264
|
+
item.addProperty("text", text);
|
|
265
|
+
item.addProperty("truncated", truncated);
|
|
266
|
+
decompilations.add(item);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
finally {
|
|
270
|
+
decompiler.dispose();
|
|
271
|
+
}
|
|
272
|
+
return decompilations;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// Runs before Ghidra's analysis. Arguments: <seeds.json>.
|
|
2
|
+
//
|
|
3
|
+
// Fills the rest of the 64 KiB address space with uninitialized memory, so
|
|
4
|
+
// references to I/O and other RAM resolve, then applies the knowledge seeds:
|
|
5
|
+
// labels (user-defined names), data ranges (defined bytes that analysis does
|
|
6
|
+
// not disassemble) and entry points (disassembled functions).
|
|
7
|
+
//@category c64-re-tools
|
|
8
|
+
|
|
9
|
+
import java.io.FileReader;
|
|
10
|
+
|
|
11
|
+
import com.google.gson.Gson;
|
|
12
|
+
import com.google.gson.JsonElement;
|
|
13
|
+
import com.google.gson.JsonObject;
|
|
14
|
+
|
|
15
|
+
import ghidra.app.script.GhidraScript;
|
|
16
|
+
import ghidra.program.model.address.Address;
|
|
17
|
+
import ghidra.program.model.data.ArrayDataType;
|
|
18
|
+
import ghidra.program.model.data.ByteDataType;
|
|
19
|
+
import ghidra.program.model.mem.Memory;
|
|
20
|
+
import ghidra.program.model.symbol.SourceType;
|
|
21
|
+
|
|
22
|
+
public class C64Prepare extends GhidraScript {
|
|
23
|
+
@Override
|
|
24
|
+
public void run() throws Exception {
|
|
25
|
+
JsonObject seeds;
|
|
26
|
+
try (FileReader reader = new FileReader(getScriptArgs()[0])) {
|
|
27
|
+
seeds = new Gson().fromJson(reader, JsonObject.class);
|
|
28
|
+
}
|
|
29
|
+
long start = seeds.get("start").getAsLong();
|
|
30
|
+
long end = seeds.get("end").getAsLong();
|
|
31
|
+
Memory memory = currentProgram.getMemory();
|
|
32
|
+
if (start > 0) {
|
|
33
|
+
memory.createUninitializedBlock("RAM_LOW", toAddr(0), start, false);
|
|
34
|
+
}
|
|
35
|
+
if (end < 0xffff) {
|
|
36
|
+
memory.createUninitializedBlock("RAM_HIGH", toAddr(end + 1), 0xffff - end, false);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
for (JsonElement element : seeds.getAsJsonArray("labels")) {
|
|
40
|
+
JsonObject label = element.getAsJsonObject();
|
|
41
|
+
createLabel(toAddr(label.get("address").getAsLong()), label.get("name").getAsString(), true, SourceType.USER_DEFINED);
|
|
42
|
+
}
|
|
43
|
+
for (JsonElement element : seeds.getAsJsonArray("dataRanges")) {
|
|
44
|
+
JsonObject range = element.getAsJsonObject();
|
|
45
|
+
Address first = toAddr(range.get("start").getAsLong());
|
|
46
|
+
Address last = toAddr(range.get("end").getAsLong());
|
|
47
|
+
int length = (int) (last.getOffset() - first.getOffset() + 1);
|
|
48
|
+
currentProgram.getListing().clearCodeUnits(first, last, false);
|
|
49
|
+
currentProgram.getListing().createData(first, new ArrayDataType(ByteDataType.dataType, length, 1));
|
|
50
|
+
}
|
|
51
|
+
for (JsonElement element : seeds.getAsJsonArray("entryPoints")) {
|
|
52
|
+
Address entry = toAddr(element.getAsLong());
|
|
53
|
+
addEntryPoint(entry);
|
|
54
|
+
disassemble(entry);
|
|
55
|
+
if (getFunctionAt(entry) == null) {
|
|
56
|
+
createFunction(entry, null);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// The context for running native tools inside a skill script or the CLI:
|
|
2
|
+
// one supervisor whose exit guard stops every tool process group when
|
|
3
|
+
// the script ends, also on Ctrl+C or SIGTERM, and whose watchdog does the same
|
|
4
|
+
// when the script dies without its exit guard, for example by SIGKILL.
|
|
5
|
+
// A line that a tool adapter logs goes to the trace of the script.
|
|
6
|
+
|
|
7
|
+
import { trace } from "../trace.ts";
|
|
8
|
+
import { ProcessSupervisor } from "./processes.ts";
|
|
9
|
+
|
|
10
|
+
export interface LocalToolContext {
|
|
11
|
+
supervisor: ProcessSupervisor;
|
|
12
|
+
signal: AbortSignal;
|
|
13
|
+
log: (line: string) => void;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
let shared: LocalToolContext | undefined;
|
|
17
|
+
|
|
18
|
+
/** The script's one tool context; created on first use. */
|
|
19
|
+
export function localToolContext(): LocalToolContext {
|
|
20
|
+
if (shared !== undefined) return shared;
|
|
21
|
+
const supervisor = new ProcessSupervisor();
|
|
22
|
+
// The watchdog also installs the exit guard.
|
|
23
|
+
supervisor.startWatchdog("skill-script");
|
|
24
|
+
const controller = new AbortController();
|
|
25
|
+
// A signal ends the script through process.exit, so the exit guard runs.
|
|
26
|
+
for (const [signal, code] of [["SIGINT", 130], ["SIGTERM", 143]] as const) {
|
|
27
|
+
process.once(signal, () => {
|
|
28
|
+
controller.abort();
|
|
29
|
+
process.exit(code);
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
shared = { supervisor, signal: controller.signal, log: (line) => trace().log(line) };
|
|
33
|
+
return shared;
|
|
34
|
+
}
|