Merge remote-tracking branch 'origin/GP-4123_ryanmkurtz_cppexporter'

(Closes #2635)
This commit is contained in:
Ryan Kurtz
2025-04-14 08:11:26 -04:00
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>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
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");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* 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 USE_CPP_STYLE_COMMENTS = "Use C++ Style Comments (//)";
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_EXCLUDE = "Function Tags Excluded";
@@ -56,6 +57,7 @@ public class CppExporter extends Exporter {
private boolean isCreateCFile = true;
private boolean isUseCppStyleComments = true;
private boolean emitDataTypeDefinitions = true;
private boolean emitReferencedGlobals = true;
private String tagOptions = "";
private Set<FunctionTag> functionTagSet = new HashSet<>();
@@ -69,7 +71,7 @@ public class CppExporter extends Exporter {
}
public CppExporter(DecompileOptions options, boolean createHeader, boolean createFile,
boolean emitTypes, boolean excludeTags, String tags) {
boolean emitTypes, boolean emitGlobals, boolean excludeTags, String tags) {
this();
this.options = options;
if (options != null) {
@@ -78,6 +80,7 @@ public class CppExporter extends Exporter {
isCreateHeaderFile = createHeader;
isCreateCFile = createFile;
emitDataTypeDefinitions = emitTypes;
emitReferencedGlobals = emitGlobals;
excludeMatchingTags = excludeTags;
if (tags != null) {
tagOptions = tags;
@@ -164,13 +167,14 @@ public class CppExporter extends Exporter {
Listing listing = program.getListing();
FunctionIterator iterator = listing.getFunctions(addrSet, true);
List<Function> functions = new ArrayList<>();
Set<String> processedGlobals = new HashSet<>();
for (int i = 0; iterator.hasNext(); i++) {
//
// Write results every so many items so that we don't blow out memory
//
if (i % 10000 == 0) {
List<CPPResult> results = parallelDecompiler.decompileFunctions(functions);
writeResults(results, headerWriter, cFileWriter, chunkingMonitor);
writeResults(results, processedGlobals, headerWriter, cFileWriter, chunkingMonitor);
functions.clear();
}
@@ -184,7 +188,7 @@ public class CppExporter extends Exporter {
// handle any remaining functions
List<CPPResult> results = parallelDecompiler.decompileFunctions(functions);
writeResults(results, headerWriter, cFileWriter, chunkingMonitor);
writeResults(results, processedGlobals, headerWriter, cFileWriter, chunkingMonitor);
}
private boolean excludeFunction(Function currentFunction) {
@@ -205,26 +209,37 @@ public class CppExporter extends Exporter {
return excludeMatchingTags == hasTag;
}
private void writeResults(List<CPPResult> results, PrintWriter headerWriter,
PrintWriter cFileWriter, TaskMonitor monitor) throws CancelledException {
private void writeResults(List<CPPResult> results, Set<String> processedGlobals,
PrintWriter headerWriter, PrintWriter cFileWriter, TaskMonitor monitor)
throws CancelledException {
monitor.checkCancelled();
Collections.sort(results);
StringBuilder globalDecls = new StringBuilder();
StringBuilder headers = new StringBuilder();
StringBuilder bodies = new StringBuilder();
for (CPPResult result : results) {
monitor.checkCancelled();
if (result == null) {
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) {
headers.append(headerCode);
headers.append(EOL);
}
String bodyCode = result.getBodyCode();
String bodyCode = result.bodyCode();
if (bodyCode != null) {
bodies.append(bodyCode);
bodies.append(EOL);
@@ -237,6 +252,7 @@ public class CppExporter extends Exporter {
headerWriter.println(headers.toString());
}
if (cFileWriter != null) {
cFileWriter.print(globalDecls.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(USE_CPP_STYLE_COMMENTS, Boolean.valueOf(isUseCppStyleComments)));
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_EXCLUDE, Boolean.valueOf(excludeMatchingTags)));
return list;
@@ -365,6 +382,9 @@ public class CppExporter extends Exporter {
else if (optName.equals(EMIT_TYPE_DEFINITONS)) {
emitDataTypeDefinitions = ((Boolean) option.getValue()).booleanValue();
}
else if (optName.equals(EMIT_REFERENCED_GLOBALS)) {
emitReferencedGlobals = ((Boolean) option.getValue()).booleanValue();
}
else if (optName.equals(FUNCTION_TAG_FILTERS)) {
tagOptions = (String) option.getValue();
}
@@ -452,31 +472,12 @@ public class CppExporter extends Exporter {
// Inner Classes
//==================================================================================================
private class CPPResult 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;
}
private record CPPResult(Address address, String headerCode, String bodyCode,
List<String> globals) implements Comparable<CPPResult> {
@Override
public int compareTo(CPPResult other) {
return address.compareTo(other.address);
}
}
private class DecompilerFactory extends CountingBasicFactory<DecompInterface> {
@@ -492,7 +493,7 @@ public class CppExporter extends Exporter {
DecompInterface decompiler = new DecompInterface();
decompiler.setOptions(options);
decompiler.openProgram(program);
decompiler.toggleSyntaxTree(false); // Don't need syntax tree
decompiler.toggleSyntaxTree(true);
return decompiler;
}
@@ -532,7 +533,7 @@ public class CppExporter extends Exporter {
CodeUnit codeUnitAt = function.getProgram().getListing().getCodeUnitAt(entryPoint);
if (codeUnitAt == null || !(codeUnitAt instanceof Instruction)) {
return new CPPResult(entryPoint, function.getPrototypeString(false, false) + ';',
null);
null, List.of());
}
monitor.setMessage("Decompiling " + function.getName());
@@ -546,14 +547,24 @@ public class CppExporter extends Exporter {
monitor.incrementProgress(1);
return new CPPResult(entryPoint, null,
"/*" + EOL + "Unable to decompile '" + function.getName() + "'" + EOL +
"Cause: " + errorMessage + EOL + "*/" + EOL);
"Cause: " + errorMessage + EOL + "*/" + EOL,
List.of());
}
return null;
}
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(),
decompiledFunction.getC());
decompiledFunction.getC(), globals);
}
}