GP-4123: CppExporter can now emit referenced globals

This commit is contained in:
Ryan Kurtz
2025-04-11 06:47:31 -04:00
parent 13834fabaa
commit 7559acf524
3 changed files with 47 additions and 34 deletions
@@ -275,6 +275,8 @@
<LI><B>Use C++ Style Comments (//)</B> - Select to use // or /* style comments.</LI> <LI><B>Use C++ Style Comments (//)</B> - Select to use // or /* style comments.</LI>
<LI><B>Emit Data-type Definitions</B> - Select to export a C/C++ definition for each data-type.</LI> <LI><B>Emit Data-type Definitions</B> - Select to export a C/C++ definition for each data-type.</LI>
<LI><B>Emit Referenced Globals</B> - Select to export a C/C++ declaration for referended global variable.</LI>
<LI><B>Function Tags to Filter</B> - Optional list of function tags to filter which <LI><B>Function Tags to Filter</B> - Optional list of function tags to filter which
functions are exported. Multiple tags must be comma separated. Any tags listed will functions are exported. Multiple tags must be comma separated. Any tags listed will
Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 9.7 KiB

@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -47,6 +47,7 @@ public class CppExporter extends Exporter {
public static final String CREATE_HEADER_FILE = "Create Header File (.h)"; public static final String CREATE_HEADER_FILE = "Create Header File (.h)";
public static final String USE_CPP_STYLE_COMMENTS = "Use C++ Style Comments (//)"; public static final String USE_CPP_STYLE_COMMENTS = "Use C++ Style Comments (//)";
public static final String EMIT_TYPE_DEFINITONS = "Emit Data-type Definitions"; public static final String EMIT_TYPE_DEFINITONS = "Emit Data-type Definitions";
public static final String EMIT_REFERENCED_GLOBALS = "Emit Referenced Globals";
public static final String FUNCTION_TAG_FILTERS = "Function Tags to Filter"; public static final String FUNCTION_TAG_FILTERS = "Function Tags to Filter";
public static final String FUNCTION_TAG_EXCLUDE = "Function Tags Excluded"; public static final String FUNCTION_TAG_EXCLUDE = "Function Tags Excluded";
@@ -56,6 +57,7 @@ public class CppExporter extends Exporter {
private boolean isCreateCFile = true; private boolean isCreateCFile = true;
private boolean isUseCppStyleComments = true; private boolean isUseCppStyleComments = true;
private boolean emitDataTypeDefinitions = true; private boolean emitDataTypeDefinitions = true;
private boolean emitReferencedGlobals = true;
private String tagOptions = ""; private String tagOptions = "";
private Set<FunctionTag> functionTagSet = new HashSet<>(); private Set<FunctionTag> functionTagSet = new HashSet<>();
@@ -69,7 +71,7 @@ public class CppExporter extends Exporter {
} }
public CppExporter(DecompileOptions options, boolean createHeader, boolean createFile, public CppExporter(DecompileOptions options, boolean createHeader, boolean createFile,
boolean emitTypes, boolean excludeTags, String tags) { boolean emitTypes, boolean emitGlobals, boolean excludeTags, String tags) {
this(); this();
this.options = options; this.options = options;
if (options != null) { if (options != null) {
@@ -78,6 +80,7 @@ public class CppExporter extends Exporter {
isCreateHeaderFile = createHeader; isCreateHeaderFile = createHeader;
isCreateCFile = createFile; isCreateCFile = createFile;
emitDataTypeDefinitions = emitTypes; emitDataTypeDefinitions = emitTypes;
emitReferencedGlobals = emitGlobals;
excludeMatchingTags = excludeTags; excludeMatchingTags = excludeTags;
if (tags != null) { if (tags != null) {
tagOptions = tags; tagOptions = tags;
@@ -164,13 +167,14 @@ public class CppExporter extends Exporter {
Listing listing = program.getListing(); Listing listing = program.getListing();
FunctionIterator iterator = listing.getFunctions(addrSet, true); FunctionIterator iterator = listing.getFunctions(addrSet, true);
List<Function> functions = new ArrayList<>(); List<Function> functions = new ArrayList<>();
Set<String> processedGlobals = new HashSet<>();
for (int i = 0; iterator.hasNext(); i++) { for (int i = 0; iterator.hasNext(); i++) {
// //
// Write results every so many items so that we don't blow out memory // Write results every so many items so that we don't blow out memory
// //
if (i % 10000 == 0) { if (i % 10000 == 0) {
List<CPPResult> results = parallelDecompiler.decompileFunctions(functions); List<CPPResult> results = parallelDecompiler.decompileFunctions(functions);
writeResults(results, headerWriter, cFileWriter, chunkingMonitor); writeResults(results, processedGlobals, headerWriter, cFileWriter, chunkingMonitor);
functions.clear(); functions.clear();
} }
@@ -184,7 +188,7 @@ public class CppExporter extends Exporter {
// handle any remaining functions // handle any remaining functions
List<CPPResult> results = parallelDecompiler.decompileFunctions(functions); List<CPPResult> results = parallelDecompiler.decompileFunctions(functions);
writeResults(results, headerWriter, cFileWriter, chunkingMonitor); writeResults(results, processedGlobals, headerWriter, cFileWriter, chunkingMonitor);
} }
private boolean excludeFunction(Function currentFunction) { private boolean excludeFunction(Function currentFunction) {
@@ -205,26 +209,37 @@ public class CppExporter extends Exporter {
return excludeMatchingTags == hasTag; return excludeMatchingTags == hasTag;
} }
private void writeResults(List<CPPResult> results, PrintWriter headerWriter, private void writeResults(List<CPPResult> results, Set<String> processedGlobals,
PrintWriter cFileWriter, TaskMonitor monitor) throws CancelledException { PrintWriter headerWriter, PrintWriter cFileWriter, TaskMonitor monitor)
throws CancelledException {
monitor.checkCancelled(); monitor.checkCancelled();
Collections.sort(results); Collections.sort(results);
StringBuilder globalDecls = new StringBuilder();
StringBuilder headers = new StringBuilder(); StringBuilder headers = new StringBuilder();
StringBuilder bodies = new StringBuilder(); StringBuilder bodies = new StringBuilder();
for (CPPResult result : results) { for (CPPResult result : results) {
monitor.checkCancelled(); monitor.checkCancelled();
if (result == null) { if (result == null) {
continue; continue;
} }
String headerCode = result.getHeaderCode(); if (emitReferencedGlobals) {
for (String global : result.globals()) {
if (processedGlobals.add(global)) {
globalDecls.append(global);
globalDecls.append(EOL);
}
}
}
String headerCode = result.headerCode();
if (headerCode != null) { if (headerCode != null) {
headers.append(headerCode); headers.append(headerCode);
headers.append(EOL); headers.append(EOL);
} }
String bodyCode = result.getBodyCode(); String bodyCode = result.bodyCode();
if (bodyCode != null) { if (bodyCode != null) {
bodies.append(bodyCode); bodies.append(bodyCode);
bodies.append(EOL); bodies.append(EOL);
@@ -237,6 +252,7 @@ public class CppExporter extends Exporter {
headerWriter.println(headers.toString()); headerWriter.println(headers.toString());
} }
if (cFileWriter != null) { if (cFileWriter != null) {
cFileWriter.print(globalDecls.toString());
cFileWriter.print(bodies.toString()); cFileWriter.print(bodies.toString());
} }
} }
@@ -343,6 +359,7 @@ public class CppExporter extends Exporter {
list.add(new Option(CREATE_C_FILE, Boolean.valueOf(isCreateCFile))); list.add(new Option(CREATE_C_FILE, Boolean.valueOf(isCreateCFile)));
list.add(new Option(USE_CPP_STYLE_COMMENTS, Boolean.valueOf(isUseCppStyleComments))); list.add(new Option(USE_CPP_STYLE_COMMENTS, Boolean.valueOf(isUseCppStyleComments)));
list.add(new Option(EMIT_TYPE_DEFINITONS, Boolean.valueOf(emitDataTypeDefinitions))); list.add(new Option(EMIT_TYPE_DEFINITONS, Boolean.valueOf(emitDataTypeDefinitions)));
list.add(new Option(EMIT_REFERENCED_GLOBALS, Boolean.valueOf(emitReferencedGlobals)));
list.add(new Option(FUNCTION_TAG_FILTERS, tagOptions)); list.add(new Option(FUNCTION_TAG_FILTERS, tagOptions));
list.add(new Option(FUNCTION_TAG_EXCLUDE, Boolean.valueOf(excludeMatchingTags))); list.add(new Option(FUNCTION_TAG_EXCLUDE, Boolean.valueOf(excludeMatchingTags)));
return list; return list;
@@ -365,6 +382,9 @@ public class CppExporter extends Exporter {
else if (optName.equals(EMIT_TYPE_DEFINITONS)) { else if (optName.equals(EMIT_TYPE_DEFINITONS)) {
emitDataTypeDefinitions = ((Boolean) option.getValue()).booleanValue(); emitDataTypeDefinitions = ((Boolean) option.getValue()).booleanValue();
} }
else if (optName.equals(EMIT_REFERENCED_GLOBALS)) {
emitReferencedGlobals = ((Boolean) option.getValue()).booleanValue();
}
else if (optName.equals(FUNCTION_TAG_FILTERS)) { else if (optName.equals(FUNCTION_TAG_FILTERS)) {
tagOptions = (String) option.getValue(); tagOptions = (String) option.getValue();
} }
@@ -452,31 +472,12 @@ public class CppExporter extends Exporter {
// Inner Classes // Inner Classes
//================================================================================================== //==================================================================================================
private class CPPResult implements Comparable<CPPResult> { private record CPPResult(Address address, String headerCode, String bodyCode,
List<String> globals) implements Comparable<CPPResult> {
private Address address;
private String bodyCode;
private String headerCode;
CPPResult(Address address, String headerCode, String bodyCode) {
this.address = address;
this.headerCode = headerCode;
this.bodyCode = bodyCode;
}
String getHeaderCode() {
return headerCode;
}
String getBodyCode() {
return bodyCode;
}
@Override @Override
public int compareTo(CPPResult other) { public int compareTo(CPPResult other) {
return address.compareTo(other.address); return address.compareTo(other.address);
} }
} }
private class DecompilerFactory extends CountingBasicFactory<DecompInterface> { private class DecompilerFactory extends CountingBasicFactory<DecompInterface> {
@@ -492,7 +493,7 @@ public class CppExporter extends Exporter {
DecompInterface decompiler = new DecompInterface(); DecompInterface decompiler = new DecompInterface();
decompiler.setOptions(options); decompiler.setOptions(options);
decompiler.openProgram(program); decompiler.openProgram(program);
decompiler.toggleSyntaxTree(false); // Don't need syntax tree decompiler.toggleSyntaxTree(true);
return decompiler; return decompiler;
} }
@@ -532,7 +533,7 @@ public class CppExporter extends Exporter {
CodeUnit codeUnitAt = function.getProgram().getListing().getCodeUnitAt(entryPoint); CodeUnit codeUnitAt = function.getProgram().getListing().getCodeUnitAt(entryPoint);
if (codeUnitAt == null || !(codeUnitAt instanceof Instruction)) { if (codeUnitAt == null || !(codeUnitAt instanceof Instruction)) {
return new CPPResult(entryPoint, function.getPrototypeString(false, false) + ';', return new CPPResult(entryPoint, function.getPrototypeString(false, false) + ';',
null); null, List.of());
} }
monitor.setMessage("Decompiling " + function.getName()); monitor.setMessage("Decompiling " + function.getName());
@@ -546,14 +547,24 @@ public class CppExporter extends Exporter {
monitor.incrementProgress(1); monitor.incrementProgress(1);
return new CPPResult(entryPoint, null, return new CPPResult(entryPoint, null,
"/*" + EOL + "Unable to decompile '" + function.getName() + "'" + EOL + "/*" + EOL + "Unable to decompile '" + function.getName() + "'" + EOL +
"Cause: " + errorMessage + EOL + "*/" + EOL); "Cause: " + errorMessage + EOL + "*/" + EOL,
List.of());
} }
return null; return null;
} }
DecompiledFunction decompiledFunction = dr.getDecompiledFunction(); DecompiledFunction decompiledFunction = dr.getDecompiledFunction();
List<String> globals =
CollectionUtils.asStream(dr.getHighFunction().getGlobalSymbolMap().getSymbols())
.map(hsym -> {
String dt = hsym.getDataType().getDisplayName();
String name = hsym.getName();
String space = dt.endsWith("*") ? "" : " ";
return "%s%s%s;".formatted(dt, space, name);
})
.toList();
return new CPPResult(entryPoint, decompiledFunction.getSignature(), return new CPPResult(entryPoint, decompiledFunction.getSignature(),
decompiledFunction.getC()); decompiledFunction.getC(), globals);
} }
} }