mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-05 10:49:34 +02:00
GT-3481 - Gnu Demangler - Checkpoint 1 - Separation of demangler
analyzers; gnu options in analyzer; still require javadocs, help and tests
This commit is contained in:
parent
b6fb46f5df
commit
b774ecb2d6
22 changed files with 1231 additions and 267 deletions
|
@ -0,0 +1,177 @@
|
||||||
|
/* ###
|
||||||
|
* IP: GHIDRA
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ghidra.app.plugin.core.analysis;
|
||||||
|
|
||||||
|
import ghidra.app.services.*;
|
||||||
|
import ghidra.app.util.demangler.*;
|
||||||
|
import ghidra.app.util.importer.MessageLog;
|
||||||
|
import ghidra.program.model.address.Address;
|
||||||
|
import ghidra.program.model.address.AddressSetView;
|
||||||
|
import ghidra.program.model.listing.*;
|
||||||
|
import ghidra.program.model.symbol.*;
|
||||||
|
import ghidra.util.exception.CancelledException;
|
||||||
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
|
// TODO docme
|
||||||
|
public abstract class AbstractDemanglerAnalyzer extends AbstractAnalyzer {
|
||||||
|
|
||||||
|
public AbstractDemanglerAnalyzer(String name, String description) {
|
||||||
|
super(name, description, AnalyzerType.BYTE_ANALYZER);
|
||||||
|
setPriority(AnalysisPriority.DATA_TYPE_PROPOGATION.before().before().before());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canAnalyze(Program program) {
|
||||||
|
// override this to be enable for a binary containing symbols you wisht to process
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean added(Program program, AddressSetView set, TaskMonitor monitor, MessageLog log)
|
||||||
|
throws CancelledException {
|
||||||
|
|
||||||
|
DemanglerOptions options = getOptions();
|
||||||
|
if (!validateOptions(options, log)) {
|
||||||
|
log.error(getName(), "Invalid demangler options--cannot demangle");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
monitor.initialize(100);
|
||||||
|
|
||||||
|
SymbolTable symbolTable = program.getSymbolTable();
|
||||||
|
SymbolIterator it = symbolTable.getPrimarySymbolIterator(set, true);
|
||||||
|
while (it.hasNext()) {
|
||||||
|
monitor.checkCanceled();
|
||||||
|
|
||||||
|
Symbol symbol = it.next();
|
||||||
|
if (skipSymbol(symbol)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Address address = symbol.getAddress();
|
||||||
|
String mangled = cleanSymbol(address, symbol.getName());
|
||||||
|
DemangledObject demangled = demangle(mangled, options, log);
|
||||||
|
if (demangled != null) {
|
||||||
|
apply(program, address, demangled, options, log, monitor);
|
||||||
|
}
|
||||||
|
|
||||||
|
Address min = set.getMinAddress();
|
||||||
|
Address max = set.getMaxAddress();
|
||||||
|
int distance = (int) (address.getOffset() - min.getOffset());
|
||||||
|
int percent = (int) ((distance / max.getOffset()) * 100);
|
||||||
|
monitor.setProgress(percent);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO callback before demangling begins...
|
||||||
|
protected boolean validateOptions(DemanglerOptions options, MessageLog log) {
|
||||||
|
// override to validate custom options for a particular demangler
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean skipSymbol(Symbol symbol) {
|
||||||
|
if (symbol.getSource() == SourceType.DEFAULT) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only demangle global or external symbols when directly parented to a Library namespace
|
||||||
|
Namespace parentNamespace = symbol.getParentNamespace();
|
||||||
|
if (symbol.isExternal()) {
|
||||||
|
if (!(parentNamespace instanceof Library)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (!parentNamespace.isGlobal()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Someone has already added arguments or return to the function signature
|
||||||
|
if (symbol.getSymbolType() == SymbolType.FUNCTION) {
|
||||||
|
Function function = (Function) symbol.getObject();
|
||||||
|
if (function.getSignatureSource() != SourceType.DEFAULT) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract DemangledObject doDemangle(String mangled, DemanglerOptions options,
|
||||||
|
MessageLog log) throws DemangledException;
|
||||||
|
|
||||||
|
protected DemanglerOptions getOptions() {
|
||||||
|
// note: these can be stored in the analyzer subclass and updated when the
|
||||||
|
// analysis options change
|
||||||
|
DemanglerOptions options = new DemanglerOptions();
|
||||||
|
options.setApplySignature(true);
|
||||||
|
options.setDoDisassembly(true);
|
||||||
|
options.setDemangleOnlyKnownPatterns(false);
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected DemangledObject demangle(String mangled, DemanglerOptions options,
|
||||||
|
MessageLog log) {
|
||||||
|
|
||||||
|
DemangledObject demangled = null;
|
||||||
|
try {
|
||||||
|
demangled = doDemangle(mangled, options, log);
|
||||||
|
}
|
||||||
|
catch (Throwable e) {
|
||||||
|
|
||||||
|
if (e instanceof DemangledException) {
|
||||||
|
if (((DemangledException) e).isInvalidMangledName()) {
|
||||||
|
//ignore invalid names, consider as not an error
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.appendMsg(getName(),
|
||||||
|
"Unable to demangle symbol: " + mangled + ". Message: " + e.getMessage());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return demangled;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void apply(Program program, Address address, DemangledObject demangled,
|
||||||
|
DemanglerOptions options, MessageLog log, TaskMonitor monitor) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (demangled.applyTo(program, address, options, monitor)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e) {
|
||||||
|
String message = e.getMessage();
|
||||||
|
message = message == null ? "" : ". Message: " + message;
|
||||||
|
log.appendMsg(getName(),
|
||||||
|
"Unable to demangle symbol at " + address + "; name: " +
|
||||||
|
demangled.getMangledName() + message);
|
||||||
|
}
|
||||||
|
|
||||||
|
log.appendMsg(getName(),
|
||||||
|
"Failed to apply mangled symbol at " + address + "; name: " +
|
||||||
|
demangled.getMangledName() + " (" +
|
||||||
|
getName() + "/" + demangled.getClass().getName() + ")");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String cleanSymbol(Address address, String name) {
|
||||||
|
return SymbolUtilities.getCleanSymbolName(name, address);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,138 +0,0 @@
|
||||||
/* ###
|
|
||||||
* IP: GHIDRA
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package ghidra.app.plugin.core.analysis;
|
|
||||||
|
|
||||||
import ghidra.app.cmd.label.DemanglerCmd;
|
|
||||||
import ghidra.app.services.*;
|
|
||||||
import ghidra.app.util.demangler.DemanglerOptions;
|
|
||||||
import ghidra.app.util.importer.MessageLog;
|
|
||||||
import ghidra.framework.options.Options;
|
|
||||||
import ghidra.program.model.address.Address;
|
|
||||||
import ghidra.program.model.address.AddressSetView;
|
|
||||||
import ghidra.program.model.listing.*;
|
|
||||||
import ghidra.program.model.symbol.*;
|
|
||||||
import ghidra.util.exception.CancelledException;
|
|
||||||
import ghidra.util.task.TaskMonitor;
|
|
||||||
|
|
||||||
public class DemanglerAnalyzer extends AbstractAnalyzer {
|
|
||||||
private static final String NAME = "Demangler";
|
|
||||||
private static final String DESCRIPTION =
|
|
||||||
"After a function is created, this analyzer will attempt to demangle " +
|
|
||||||
"the name and apply datatypes to parameters.";
|
|
||||||
|
|
||||||
private static final String OPTION_NAME_DEMANGLE_USE_KNOWN_PATTERNS =
|
|
||||||
"Only Demangle Known Mangled Symbols";
|
|
||||||
private static final String OPTION_DESCRIPTION_USE_KNOWN_PATTERNS =
|
|
||||||
"Only demangle " + "symbols that follow known compiler mangling patterns. " +
|
|
||||||
"Leaving this option off may cause non-mangled symbols to get demangled.";
|
|
||||||
|
|
||||||
private final static String OPTION_NAME_COMMIT_SIGNATURE = "Commit Function Signatures";
|
|
||||||
private static final String OPTION_DESCRIPTION_COMMIT_SIGNATURE =
|
|
||||||
"Apply any recovered function signature, in addition to the function name";
|
|
||||||
|
|
||||||
private boolean doSignatureEnabled = true;
|
|
||||||
private boolean demangleAllSymbols = false;
|
|
||||||
|
|
||||||
public DemanglerAnalyzer() {
|
|
||||||
super(NAME, DESCRIPTION, AnalyzerType.BYTE_ANALYZER);
|
|
||||||
setPriority(AnalysisPriority.DATA_TYPE_PROPOGATION.before().before().before());
|
|
||||||
setDefaultEnablement(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean added(Program program, AddressSetView set, TaskMonitor monitor, MessageLog log)
|
|
||||||
throws CancelledException {
|
|
||||||
SymbolTable symbolTable = program.getSymbolTable();
|
|
||||||
|
|
||||||
int progress = 0;
|
|
||||||
monitor.initialize(symbolTable.getNumSymbols());
|
|
||||||
monitor.setShowProgressValue(false);
|
|
||||||
|
|
||||||
SymbolIterator symiter = symbolTable.getPrimarySymbolIterator(set, true);
|
|
||||||
while (symiter.hasNext()) {
|
|
||||||
monitor.checkCanceled();
|
|
||||||
|
|
||||||
Symbol symbol = symiter.next();
|
|
||||||
Address address = symbol.getAddress();
|
|
||||||
if (address.compareTo(set.getMaxAddress()) > 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (symbol.getSource() == SourceType.DEFAULT) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only demangle global memory symbols or external
|
|
||||||
// symbols directly parented to a Library namespace
|
|
||||||
Namespace parentNamespace = symbol.getParentNamespace();
|
|
||||||
if (symbol.isExternal()) {
|
|
||||||
if (!(parentNamespace instanceof Library)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (!parentNamespace.isGlobal()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Someone has already added arguments or return to the function
|
|
||||||
// signature.
|
|
||||||
if (symbol.getSymbolType() == SymbolType.FUNCTION) {
|
|
||||||
Function function = (Function) symbol.getObject();
|
|
||||||
if (function.getSignatureSource() != SourceType.DEFAULT) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// retrieve symbol count each time, as the number of symbols changes while we are working
|
|
||||||
int count = symbolTable.getNumSymbols();
|
|
||||||
monitor.setMaximum(count);
|
|
||||||
monitor.setProgress((int) ((progress++ / (double) count) * count));
|
|
||||||
|
|
||||||
DemanglerOptions options = new DemanglerOptions();
|
|
||||||
options.setDoDisassembly(true);
|
|
||||||
options.setApplySignature(doSignatureEnabled);
|
|
||||||
options.setDemangleOnlyKnownPatterns(demangleAllSymbols);
|
|
||||||
|
|
||||||
DemanglerCmd cmd = new DemanglerCmd(address, symbol.getName(), options);
|
|
||||||
if (!cmd.applyTo(program)) {
|
|
||||||
String message = cmd.getStatusMsg();
|
|
||||||
if (message != null) {
|
|
||||||
log.appendMsg(cmd.getName(), message);
|
|
||||||
log.setStatus(message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
monitor.setShowProgressValue(true);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void registerOptions(Options options, Program program) {
|
|
||||||
options.registerOption(OPTION_NAME_COMMIT_SIGNATURE, doSignatureEnabled, null,
|
|
||||||
OPTION_DESCRIPTION_COMMIT_SIGNATURE);
|
|
||||||
|
|
||||||
options.registerOption(OPTION_NAME_DEMANGLE_USE_KNOWN_PATTERNS, false, null,
|
|
||||||
OPTION_DESCRIPTION_USE_KNOWN_PATTERNS);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void optionsChanged(Options options, Program program) {
|
|
||||||
doSignatureEnabled = options.getBoolean(OPTION_NAME_COMMIT_SIGNATURE, doSignatureEnabled);
|
|
||||||
demangleAllSymbols =
|
|
||||||
options.getBoolean(OPTION_NAME_DEMANGLE_USE_KNOWN_PATTERNS, demangleAllSymbols);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -115,6 +115,14 @@ public abstract class DemangledObject {
|
||||||
return demangledName;
|
return demangledName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the original mangled name
|
||||||
|
* @return the name
|
||||||
|
*/
|
||||||
|
public String getMangledName() {
|
||||||
|
return originalMangled;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the demangled name of this object.
|
* Returns the demangled name of this object.
|
||||||
* NOTE: unsupported symbol characters, like whitespace, will be
|
* NOTE: unsupported symbol characters, like whitespace, will be
|
||||||
|
@ -495,8 +503,11 @@ public abstract class DemangledObject {
|
||||||
|
|
||||||
List<Symbol> symbols = symbolTable.getSymbols(namespaceName, namespace);
|
List<Symbol> symbols = symbolTable.getSymbols(namespaceName, namespace);
|
||||||
Symbol namespaceSymbol =
|
Symbol namespaceSymbol =
|
||||||
symbols.stream().filter(s -> (s.getSymbolType() == SymbolType.NAMESPACE ||
|
symbols.stream()
|
||||||
s.getSymbolType() == SymbolType.CLASS)).findFirst().orElse(null);
|
.filter(s -> (s.getSymbolType() == SymbolType.NAMESPACE ||
|
||||||
|
s.getSymbolType() == SymbolType.CLASS))
|
||||||
|
.findFirst()
|
||||||
|
.orElse(null);
|
||||||
if (namespaceSymbol == null) {
|
if (namespaceSymbol == null) {
|
||||||
try {
|
try {
|
||||||
namespace =
|
namespace =
|
||||||
|
|
|
@ -23,8 +23,19 @@ import ghidra.util.classfinder.ExtensionPoint;
|
||||||
* the ClassSearcher will not find them.
|
* the ClassSearcher will not find them.
|
||||||
*/
|
*/
|
||||||
public interface Demangler extends ExtensionPoint {
|
public interface Demangler extends ExtensionPoint {
|
||||||
|
|
||||||
public boolean canDemangle(Program program);
|
public boolean canDemangle(Program program);
|
||||||
|
|
||||||
|
// TODO deprecate
|
||||||
|
@Deprecated
|
||||||
public DemangledObject demangle(String mangled, boolean demangleOnlyKnownPatterns)
|
public DemangledObject demangle(String mangled, boolean demangleOnlyKnownPatterns)
|
||||||
throws DemangledException;
|
throws DemangledException;
|
||||||
|
|
||||||
|
// TODO docme
|
||||||
|
public DemangledObject demangle(String mangled, DemanglerOptions options)
|
||||||
|
throws DemangledException;
|
||||||
|
|
||||||
|
public default DemanglerOptions createDefaultOptions() {
|
||||||
|
return new DemanglerOptions();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -92,4 +92,14 @@ public class DemanglerOptions {
|
||||||
this.demangleOnlyKnownPatterns = demangleOnlyKnownPatterns;
|
this.demangleOnlyKnownPatterns = demangleOnlyKnownPatterns;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
//@formatter:off
|
||||||
|
return "{\n" +
|
||||||
|
"\tdoDisassembly: " + doDisassembly + ",\n" +
|
||||||
|
"\tapplySignature: " + applySignature + ",\n" +
|
||||||
|
"\tdemangleOnlyKnownPatterns: " + demangleOnlyKnownPatterns + ",\n" +
|
||||||
|
"}";
|
||||||
|
//@formatter:on
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,30 +0,0 @@
|
||||||
/* ###
|
|
||||||
* IP: GHIDRA
|
|
||||||
* REVIEWED: YES
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package ghidra.app.util.demangler;
|
|
||||||
|
|
||||||
|
|
||||||
public interface DemanglerParser {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parses the output of the demangler process and converts
|
|
||||||
* it into a demangled object.
|
|
||||||
* @param mangled the original mangled string - e.g., "_ZdaPv"
|
|
||||||
* @param demangled the demangled string - e.g., "operator_delete[](void*)"
|
|
||||||
* @return a demangled object
|
|
||||||
*/
|
|
||||||
public DemangledObject parse(String mangled, String demangled);
|
|
||||||
}
|
|
|
@ -32,7 +32,13 @@ public class DemanglerUtil {
|
||||||
private static final Pattern TRAILING_PARAMETER_SPACE_PATTERN = Pattern.compile("([\\(\\,]) ");
|
private static final Pattern TRAILING_PARAMETER_SPACE_PATTERN = Pattern.compile("([\\(\\,]) ");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Locates all available demanglers, then it attempts to demangle.
|
* Locates all available demanglers, then it attempts to demangle. This method will
|
||||||
|
* query all demanglers regardless of architecture.
|
||||||
|
*
|
||||||
|
* <p>This method will use only the default options for demangling. If you need to
|
||||||
|
* specify options, then you will have to call each specific demangler directly, creating
|
||||||
|
* the options specifically needed for each demangler. See
|
||||||
|
* {@link Demangler#createDefaultOptions()}.
|
||||||
*
|
*
|
||||||
* @param mangled the mangled name
|
* @param mangled the mangled name
|
||||||
* @return the demangled object or null
|
* @return the demangled object or null
|
||||||
|
@ -59,6 +65,11 @@ public class DemanglerUtil {
|
||||||
* Locates all available demanglers and checks to see if the supplied program is
|
* Locates all available demanglers and checks to see if the supplied program is
|
||||||
* supported, then it attempts to demangle.
|
* supported, then it attempts to demangle.
|
||||||
*
|
*
|
||||||
|
* <p>This method will use only the default options for demangling. If you need to
|
||||||
|
* specify options, then you will have to call each specific demangler directly, creating
|
||||||
|
* the options specifically needed for each demangler. See
|
||||||
|
* {@link Demangler#createDefaultOptions()}.
|
||||||
|
*
|
||||||
* @param program the program containing the mangled name
|
* @param program the program containing the mangled name
|
||||||
* @param mangled the mangled name
|
* @param mangled the mangled name
|
||||||
* @return the demangled object or null
|
* @return the demangled object or null
|
||||||
|
@ -95,12 +106,14 @@ public class DemanglerUtil {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts the list of names into a namespace linked list.
|
* Converts the list of names into a namespace demangled type.
|
||||||
* Given names = { "A", "B", "C" }, which represents "A::B::C".
|
* Given names = { "A", "B", "C" }, which represents "A::B::C".
|
||||||
* The following will be created {@literal "Namespace{A}->Namespace{B}->Namespace{C}"}
|
* The following will be created {@literal "Namespace{A}->Namespace{B}->Namespace{C}"}
|
||||||
* and Namespace{C} will be returned.
|
* and Namespace{C} will be returned.
|
||||||
*
|
*
|
||||||
* NOTE: the list will be empty after the call.
|
* NOTE: the list will be empty after the call.
|
||||||
|
* @param names the names to convert
|
||||||
|
* @return the newly created type
|
||||||
*/
|
*/
|
||||||
public static DemangledType convertToNamespaces(List<String> names) {
|
public static DemangledType convertToNamespaces(List<String> names) {
|
||||||
if (names.size() == 0) {
|
if (names.size() == 0) {
|
||||||
|
@ -129,13 +142,13 @@ public class DemanglerUtil {
|
||||||
|
|
||||||
private static String replace(String str, Pattern spaceCleanerPattern) {
|
private static String replace(String str, Pattern spaceCleanerPattern) {
|
||||||
Matcher matcher = spaceCleanerPattern.matcher(str);
|
Matcher matcher = spaceCleanerPattern.matcher(str);
|
||||||
StringBuffer buf = new StringBuffer();
|
StringBuilder buffy = new StringBuilder();
|
||||||
while (matcher.find()) {
|
while (matcher.find()) {
|
||||||
String captureGroup = matcher.group(1);
|
String captureGroup = matcher.group(1);
|
||||||
matcher.appendReplacement(buf, captureGroup);
|
matcher.appendReplacement(buffy, captureGroup);
|
||||||
}
|
}
|
||||||
matcher.appendTail(buf);
|
matcher.appendTail(buffy);
|
||||||
return buf.toString();
|
return buffy.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void setNamespace(DemangledType dt, DemangledType namespace) {
|
public static void setNamespace(DemangledType dt, DemangledType namespace) {
|
||||||
|
|
|
@ -83,11 +83,11 @@ public class NamespacePropertyEditor extends PropertyEditorSupport implements Cu
|
||||||
|
|
||||||
showLibraryInNamespaceCheckBox = new GCheckBox(DISPLAY_LIBRARY_IN_NAMESPACE_LABEL);
|
showLibraryInNamespaceCheckBox = new GCheckBox(DISPLAY_LIBRARY_IN_NAMESPACE_LABEL);
|
||||||
showLibraryInNamespaceCheckBox.setSelected(true);
|
showLibraryInNamespaceCheckBox.setSelected(true);
|
||||||
showLocalCheckBox.setToolTipText(SHOW_LIBRARY_IN_NAMESPACE_TOOLTIP);
|
showLibraryInNamespaceCheckBox.setToolTipText(SHOW_LIBRARY_IN_NAMESPACE_TOOLTIP);
|
||||||
|
|
||||||
panel.add(showNonLocalCheckBox);
|
panel.add(showNonLocalCheckBox);
|
||||||
panel.add(showLocalCheckBox);
|
|
||||||
panel.add(showLibraryInNamespaceCheckBox);
|
panel.add(showLibraryInNamespaceCheckBox);
|
||||||
|
panel.add(showLocalCheckBox);
|
||||||
|
|
||||||
localPrefixField =
|
localPrefixField =
|
||||||
createLocalPrefixTextField(LOCAL_NAMESPACE_PREFIX_LABEL, LOCAL_PREFIX_TOOLTIP, panel);
|
createLocalPrefixTextField(LOCAL_NAMESPACE_PREFIX_LABEL, LOCAL_PREFIX_TOOLTIP, panel);
|
||||||
|
@ -169,7 +169,8 @@ public class NamespacePropertyEditor extends PropertyEditorSupport implements Cu
|
||||||
if (namespaceOption.isShowLocalNamespace() != showLocalCheckBox.isSelected()) {
|
if (namespaceOption.isShowLocalNamespace() != showLocalCheckBox.isSelected()) {
|
||||||
showLocalCheckBox.setSelected(namespaceOption.isShowLocalNamespace());
|
showLocalCheckBox.setSelected(namespaceOption.isShowLocalNamespace());
|
||||||
}
|
}
|
||||||
if (namespaceOption.isShowLibraryInNamespace() != showLibraryInNamespaceCheckBox.isSelected()) {
|
if (namespaceOption.isShowLibraryInNamespace() != showLibraryInNamespaceCheckBox
|
||||||
|
.isSelected()) {
|
||||||
showLibraryInNamespaceCheckBox.setSelected(namespaceOption.isShowLibraryInNamespace());
|
showLibraryInNamespaceCheckBox.setSelected(namespaceOption.isShowLibraryInNamespace());
|
||||||
}
|
}
|
||||||
if (namespaceOption.isUseLocalPrefixOverride() != useLocalPrefixCheckBox.isSelected()) {
|
if (namespaceOption.isUseLocalPrefixOverride() != useLocalPrefixCheckBox.isSelected()) {
|
||||||
|
|
|
@ -75,6 +75,7 @@ public class DemangleElfWithOptionScript extends GhidraScript {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO change to GnuDemanglerOptions
|
||||||
DemanglerOptions options = new DemanglerOptions();
|
DemanglerOptions options = new DemanglerOptions();
|
||||||
options.setDoDisassembly(false);
|
options.setDoDisassembly(false);
|
||||||
options.setApplySignature(true);
|
options.setApplySignature(true);
|
||||||
|
@ -113,6 +114,7 @@ public class DemangleElfWithOptionScript extends GhidraScript {
|
||||||
return executableFormat != null && executableFormat.indexOf(MachoLoader.MACH_O_NAME) != -1;
|
return executableFormat != null && executableFormat.indexOf(MachoLoader.MACH_O_NAME) != -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// TODO this is here because we did not support program arguments. replace this code
|
||||||
private Process createProcess(String executableName) throws Exception {
|
private Process createProcess(String executableName) throws Exception {
|
||||||
|
|
||||||
String demanglerName = GnuDemanglerNativeProcess.DEMANGLER_GNU;
|
String demanglerName = GnuDemanglerNativeProcess.DEMANGLER_GNU;
|
||||||
|
|
|
@ -0,0 +1,195 @@
|
||||||
|
/* ###
|
||||||
|
* IP: GHIDRA
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ghidra.app.plugin.core.analysis;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
import ghidra.app.util.demangler.*;
|
||||||
|
import ghidra.app.util.demangler.gnu.*;
|
||||||
|
import ghidra.app.util.importer.MessageLog;
|
||||||
|
import ghidra.framework.options.*;
|
||||||
|
import ghidra.program.model.listing.Program;
|
||||||
|
import ghidra.util.HelpLocation;
|
||||||
|
import ghidra.util.Msg;
|
||||||
|
|
||||||
|
public class GnuDemanglerAnalyzer extends AbstractDemanglerAnalyzer {
|
||||||
|
|
||||||
|
private static final String NAME = "Demangler GNU";
|
||||||
|
private static final String DESCRIPTION =
|
||||||
|
"After a function is created, this analyzer will attempt to demangle " +
|
||||||
|
"the name and apply datatypes to parameters.";
|
||||||
|
|
||||||
|
private static final String OPTION_NAME_DEMANGLE_USE_KNOWN_PATTERNS =
|
||||||
|
"Only Demangle Known Mangled Symbols";
|
||||||
|
private static final String OPTION_DESCRIPTION_USE_KNOWN_PATTERNS =
|
||||||
|
"Only demangle symbols that follow known compiler mangling patterns. " +
|
||||||
|
"Leaving this option off may cause non-mangled symbols to get demangled.";
|
||||||
|
|
||||||
|
private static final String OPTION_NAME_APPLY_SIGNATURE = "Apply Function Signatures";
|
||||||
|
private static final String OPTION_DESCRIPTION_APPLY_SIGNATURE =
|
||||||
|
"Apply any recovered function signature, in addition to the function name";
|
||||||
|
|
||||||
|
// note: we use 'Z' as a trick to be below the other options
|
||||||
|
private static final String OPTION_NAME_GNU_DEMANGLER = "Z GNU Demangler";
|
||||||
|
|
||||||
|
private boolean doSignatureEnabled = true;
|
||||||
|
private boolean demangleOnlyKnownPatterns = false;
|
||||||
|
private GnuDemanglerOptionsPropertyEditor gnuOptionsEditor =
|
||||||
|
new GnuDemanglerOptionsPropertyEditor();
|
||||||
|
private GnuDemanglerWrappedOption gnuWrappedOptions;
|
||||||
|
|
||||||
|
private GnuDemangler demangler = new GnuDemangler();
|
||||||
|
|
||||||
|
public GnuDemanglerAnalyzer() {
|
||||||
|
super(NAME, DESCRIPTION);
|
||||||
|
setDefaultEnablement(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canAnalyze(Program program) {
|
||||||
|
return demangler.canDemangle(program);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void registerOptions(Options options, Program program) {
|
||||||
|
options.registerOption(OPTION_NAME_APPLY_SIGNATURE, doSignatureEnabled, null,
|
||||||
|
OPTION_DESCRIPTION_APPLY_SIGNATURE);
|
||||||
|
|
||||||
|
options.registerOption(OPTION_NAME_DEMANGLE_USE_KNOWN_PATTERNS, false, null,
|
||||||
|
OPTION_DESCRIPTION_USE_KNOWN_PATTERNS);
|
||||||
|
|
||||||
|
options.registerOptionsEditor(null);
|
||||||
|
|
||||||
|
HelpLocation help = new HelpLocation("AutoAnalysisPlugin", "Demangler_Analyzer");
|
||||||
|
options.registerOption(OPTION_NAME_GNU_DEMANGLER, OptionType.CUSTOM_TYPE,
|
||||||
|
new GnuDemanglerWrappedOption(), help, "Advanced GNU demangler options",
|
||||||
|
gnuOptionsEditor);
|
||||||
|
|
||||||
|
CustomOption customOption = options.getCustomOption(OPTION_NAME_GNU_DEMANGLER,
|
||||||
|
new GnuDemanglerWrappedOption());
|
||||||
|
if (!(customOption instanceof GnuDemanglerWrappedOption)) {
|
||||||
|
customOption = new GnuDemanglerWrappedOption();
|
||||||
|
Msg.debug(this, "Unexpected custom option type for GNU Demangler: " +
|
||||||
|
customOption.getClass());
|
||||||
|
}
|
||||||
|
gnuWrappedOptions = (GnuDemanglerWrappedOption) customOption;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void optionsChanged(Options options, Program program) {
|
||||||
|
doSignatureEnabled = options.getBoolean(OPTION_NAME_APPLY_SIGNATURE, doSignatureEnabled);
|
||||||
|
demangleOnlyKnownPatterns =
|
||||||
|
options.getBoolean(OPTION_NAME_DEMANGLE_USE_KNOWN_PATTERNS, demangleOnlyKnownPatterns);
|
||||||
|
|
||||||
|
gnuWrappedOptions =
|
||||||
|
(GnuDemanglerWrappedOption) options.getCustomOption(OPTION_NAME_GNU_DEMANGLER,
|
||||||
|
new GnuDemanglerWrappedOption());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected DemanglerOptions getOptions() {
|
||||||
|
|
||||||
|
GnuDemanglerOptions options = new GnuDemanglerOptions();
|
||||||
|
options.setDoDisassembly(true);
|
||||||
|
options.setApplySignature(doSignatureEnabled);
|
||||||
|
options.setDemangleOnlyKnownPatterns(demangleOnlyKnownPatterns);
|
||||||
|
|
||||||
|
options.setUseDeprecatedDemangler(gnuWrappedOptions.useDeprecatedDemangler());
|
||||||
|
|
||||||
|
String text = null;
|
||||||
|
if (gnuWrappedOptions.useDemanglerParameters()) {
|
||||||
|
text = gnuWrappedOptions.getDemanglerParametersText();
|
||||||
|
}
|
||||||
|
options.setDemanglerApplicationArguments(text);
|
||||||
|
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean validateOptions(DemanglerOptions demanglerOtions, MessageLog log) {
|
||||||
|
|
||||||
|
GnuDemanglerOptions options = (GnuDemanglerOptions) demanglerOtions;
|
||||||
|
String applicationArguments = options.getDemanglerApplicationArguments();
|
||||||
|
if (StringUtils.isBlank(applicationArguments)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that the supplied arguments will work with at least one of the requested
|
||||||
|
// demanglers. (Different versions of the GNU demangler support different arguments.)
|
||||||
|
String demanglerName = options.getDemanglerName();
|
||||||
|
try {
|
||||||
|
GnuDemanglerNativeProcess.getDemanglerNativeProcess(demanglerName,
|
||||||
|
applicationArguments);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (IOException e) {
|
||||||
|
log.error(getName(), "Invalid options for GNU dangler '" + demanglerName +
|
||||||
|
"': " + applicationArguments);
|
||||||
|
log.appendException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.useDeprecatedDemangler()) {
|
||||||
|
// see if the options work in the deprecated demangler
|
||||||
|
GnuDemanglerOptions deprecatedOptions = options.withDeprecatedDemangler();
|
||||||
|
String deprecatedName = deprecatedOptions.getDemanglerName();
|
||||||
|
try {
|
||||||
|
GnuDemanglerNativeProcess.getDemanglerNativeProcess(deprecatedName,
|
||||||
|
applicationArguments);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (IOException e) {
|
||||||
|
log.error(getName(),
|
||||||
|
"Invalid options for GNU dangler '" + deprecatedName + "': " +
|
||||||
|
applicationArguments);
|
||||||
|
log.appendException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected DemangledObject doDemangle(String mangled, DemanglerOptions demanglerOtions,
|
||||||
|
MessageLog log)
|
||||||
|
throws DemangledException {
|
||||||
|
|
||||||
|
GnuDemanglerOptions options = (GnuDemanglerOptions) demanglerOtions;
|
||||||
|
DemangledObject demangled = null;
|
||||||
|
try {
|
||||||
|
demangled = demangler.demangle(mangled, options);
|
||||||
|
}
|
||||||
|
catch (DemangledException e) {
|
||||||
|
if (!options.useDeprecatedDemangler()) {
|
||||||
|
throw e; // let our parent handle this
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (demangled != null) {
|
||||||
|
return demangled;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.useDeprecatedDemangler()) {
|
||||||
|
GnuDemanglerOptions newOptions = options.withDeprecatedDemangler();
|
||||||
|
demangled = demangler.demangle(mangled, newOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
return demangled;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,179 @@
|
||||||
|
/* ###
|
||||||
|
* IP: GHIDRA
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ghidra.app.plugin.core.analysis;
|
||||||
|
|
||||||
|
import java.awt.Component;
|
||||||
|
import java.awt.Container;
|
||||||
|
import java.beans.PropertyEditorSupport;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
import javax.swing.event.DocumentEvent;
|
||||||
|
import javax.swing.event.DocumentListener;
|
||||||
|
|
||||||
|
import docking.widgets.checkbox.GCheckBox;
|
||||||
|
import ghidra.framework.options.CustomOptionsEditor;
|
||||||
|
import ghidra.util.layout.HorizontalLayout;
|
||||||
|
import ghidra.util.layout.VerticalLayout;
|
||||||
|
|
||||||
|
public class GnuDemanglerOptionsPropertyEditor extends PropertyEditorSupport
|
||||||
|
implements CustomOptionsEditor {
|
||||||
|
|
||||||
|
private static final String USE_DEPRECATED_DEMANGLER = "Use Deprecated Demangler";
|
||||||
|
private static final String USE_DEMANGLER_PARAMETERS = "Use Demangler Program Parameters";
|
||||||
|
|
||||||
|
private static final String USE_DEPRECATED_DEMANGLER_TOOLTIP =
|
||||||
|
"Signals to use the deprecated demangler when the modern demangler cannot demangle a " +
|
||||||
|
"given string";
|
||||||
|
private static final String USE_DEMANGLER_PARAMETERS_TOOLTIP =
|
||||||
|
"Signals to use pass the given parameters to the demangler program";
|
||||||
|
|
||||||
|
private static final String[] NAMES =
|
||||||
|
{ USE_DEPRECATED_DEMANGLER, USE_DEMANGLER_PARAMETERS };
|
||||||
|
|
||||||
|
private static final String[] DESCRIPTIONS = { USE_DEPRECATED_DEMANGLER_TOOLTIP,
|
||||||
|
USE_DEMANGLER_PARAMETERS_TOOLTIP };
|
||||||
|
|
||||||
|
private GnuDemanglerWrappedOption wrappedOption;
|
||||||
|
|
||||||
|
private Component editorComponent;
|
||||||
|
|
||||||
|
private GCheckBox useDeprecatedDemanglerBox;
|
||||||
|
private GCheckBox useDemanglerParametersBox;
|
||||||
|
private JTextField demanglerParametersTextField;
|
||||||
|
|
||||||
|
public GnuDemanglerOptionsPropertyEditor() {
|
||||||
|
editorComponent = buildEditor();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Component buildEditor() {
|
||||||
|
|
||||||
|
// we want to have a panel with our options so that we may group them together
|
||||||
|
JPanel panel = new JPanel(new VerticalLayout(3));
|
||||||
|
|
||||||
|
useDeprecatedDemanglerBox = new GCheckBox(USE_DEPRECATED_DEMANGLER);
|
||||||
|
useDeprecatedDemanglerBox.setSelected(false);
|
||||||
|
useDeprecatedDemanglerBox.setToolTipText(USE_DEPRECATED_DEMANGLER_TOOLTIP);
|
||||||
|
useDeprecatedDemanglerBox.addItemListener(e -> firePropertyChange());
|
||||||
|
panel.add(useDeprecatedDemanglerBox);
|
||||||
|
|
||||||
|
createParameterComponent(panel);
|
||||||
|
|
||||||
|
return panel;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createParameterComponent(Container parent) {
|
||||||
|
|
||||||
|
JPanel textFieldPanel = new JPanel(new HorizontalLayout(0));
|
||||||
|
JTextField textField = new JTextField(15);
|
||||||
|
useDemanglerParametersBox = new GCheckBox(USE_DEMANGLER_PARAMETERS);
|
||||||
|
useDemanglerParametersBox.setToolTipText(USE_DEMANGLER_PARAMETERS_TOOLTIP);
|
||||||
|
useDemanglerParametersBox.addItemListener(e -> {
|
||||||
|
textField.setEnabled(useDemanglerParametersBox.isSelected());
|
||||||
|
firePropertyChange();
|
||||||
|
});
|
||||||
|
|
||||||
|
textField.getDocument().addDocumentListener(new DocumentListener() {
|
||||||
|
@Override
|
||||||
|
public void changedUpdate(DocumentEvent e) {
|
||||||
|
firePropertyChange();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void insertUpdate(DocumentEvent e) {
|
||||||
|
firePropertyChange();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeUpdate(DocumentEvent e) {
|
||||||
|
firePropertyChange();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
textField.setEnabled(false);
|
||||||
|
|
||||||
|
textFieldPanel.add(useDemanglerParametersBox);
|
||||||
|
textFieldPanel.add(Box.createHorizontalStrut(10));
|
||||||
|
textFieldPanel.add(textField);
|
||||||
|
|
||||||
|
parent.add(textFieldPanel);
|
||||||
|
|
||||||
|
demanglerParametersTextField = textField;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setValue(Object value) {
|
||||||
|
|
||||||
|
if (!(value instanceof GnuDemanglerWrappedOption)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
wrappedOption = (GnuDemanglerWrappedOption) value;
|
||||||
|
setLocalValues(wrappedOption);
|
||||||
|
firePropertyChange();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setLocalValues(GnuDemanglerWrappedOption newOption) {
|
||||||
|
|
||||||
|
if (newOption.useDeprecatedDemangler() != useDeprecatedDemanglerBox.isSelected()) {
|
||||||
|
useDeprecatedDemanglerBox.setSelected(newOption.useDeprecatedDemangler());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newOption.useDemanglerParameters() != useDemanglerParametersBox.isSelected()) {
|
||||||
|
useDemanglerParametersBox.setSelected(newOption.useDemanglerParameters());
|
||||||
|
}
|
||||||
|
|
||||||
|
String newText = newOption.getDemanglerParametersText();
|
||||||
|
String currentText = demanglerParametersTextField.getText();
|
||||||
|
if (!Objects.equals(newText, currentText)) {
|
||||||
|
demanglerParametersTextField.setText(newText);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getValue() {
|
||||||
|
return cloneNamespaceValues();
|
||||||
|
}
|
||||||
|
|
||||||
|
private GnuDemanglerWrappedOption cloneNamespaceValues() {
|
||||||
|
|
||||||
|
GnuDemanglerWrappedOption newOption = new GnuDemanglerWrappedOption();
|
||||||
|
newOption.setUseDeprecatedDemangler(useDeprecatedDemanglerBox.isSelected());
|
||||||
|
newOption.setUseDemanglerParameters(useDemanglerParametersBox.isSelected());
|
||||||
|
newOption.setDemanglerParametersText(demanglerParametersTextField.getText());
|
||||||
|
return newOption;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String[] getOptionNames() {
|
||||||
|
return NAMES;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String[] getOptionDescriptions() {
|
||||||
|
return DESCRIPTIONS;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Component getCustomEditor() {
|
||||||
|
return editorComponent;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsCustomEditor() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,125 @@
|
||||||
|
/* ###
|
||||||
|
* IP: GHIDRA
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ghidra.app.plugin.core.analysis;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import ghidra.framework.options.CustomOption;
|
||||||
|
import ghidra.framework.options.SaveState;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A simple java bean adapted to the {@link CustomOption} interface. The public
|
||||||
|
* getters and setters are self-documenting.
|
||||||
|
*/
|
||||||
|
public class GnuDemanglerWrappedOption implements CustomOption {
|
||||||
|
|
||||||
|
private static final String USE_DEPRECATED_DEMANGLER = "USE_DEPRECATED_DEMANGLER";
|
||||||
|
private static final String USE_DEMANGLER_PARAMETERS = "USE_DEMANGLER_PARAMETERS";
|
||||||
|
private static final String DEMANGLER_PARAMETERS = "DEMANGLER_PARAMETERS";
|
||||||
|
|
||||||
|
private boolean useDeprecatedDemangler = false;
|
||||||
|
private boolean useDemanglerParameters = false;
|
||||||
|
private String demanglerParametersText = null;
|
||||||
|
|
||||||
|
public void setUseDeprecatedDemangler(boolean doUse) {
|
||||||
|
this.useDeprecatedDemangler = doUse;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean useDeprecatedDemangler() {
|
||||||
|
return useDeprecatedDemangler;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDemanglerParametersText(String text) {
|
||||||
|
this.demanglerParametersText = text;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDemanglerParametersText() {
|
||||||
|
return demanglerParametersText;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUseDemanglerParameters(boolean doUse) {
|
||||||
|
this.useDemanglerParameters = doUse;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean useDemanglerParameters() {
|
||||||
|
return useDemanglerParameters;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void readState(SaveState state) {
|
||||||
|
useDeprecatedDemangler =
|
||||||
|
state.getBoolean(USE_DEPRECATED_DEMANGLER, useDemanglerParameters);
|
||||||
|
useDemanglerParameters =
|
||||||
|
state.getBoolean(USE_DEPRECATED_DEMANGLER, useDemanglerParameters);
|
||||||
|
demanglerParametersText =
|
||||||
|
state.getString(DEMANGLER_PARAMETERS, demanglerParametersText);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeState(SaveState state) {
|
||||||
|
state.putBoolean(USE_DEPRECATED_DEMANGLER, useDeprecatedDemangler);
|
||||||
|
state.putBoolean(USE_DEMANGLER_PARAMETERS, useDemanglerParameters);
|
||||||
|
state.putString(USE_DEMANGLER_PARAMETERS, demanglerParametersText);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
final int prime = 31;
|
||||||
|
int result = 1;
|
||||||
|
result = prime * result +
|
||||||
|
((demanglerParametersText == null) ? 0 : demanglerParametersText.hashCode());
|
||||||
|
result = prime * result + (useDemanglerParameters ? 1231 : 1237);
|
||||||
|
result = prime * result + (useDeprecatedDemangler ? 1231 : 1237);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (obj == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (getClass() != obj.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
GnuDemanglerWrappedOption other = (GnuDemanglerWrappedOption) obj;
|
||||||
|
if (!Objects.equals(demanglerParametersText, other.demanglerParametersText)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (useDemanglerParameters != other.useDemanglerParameters) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (useDeprecatedDemangler != other.useDeprecatedDemangler) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
//@formatter:off
|
||||||
|
return "{\n" +
|
||||||
|
"\tuseDeprecatedDemangler: " + useDeprecatedDemangler + ",\n" +
|
||||||
|
"\tuseDemanglerParameters: " + useDemanglerParameters + ",\n" +
|
||||||
|
"\tdemanglerParametersText: " + demanglerParametersText + ",\n" +
|
||||||
|
"}";
|
||||||
|
//@formatter:on
|
||||||
|
}
|
||||||
|
}
|
|
@ -38,17 +38,22 @@ public class GnuDemangler implements Demangler {
|
||||||
// needed to instantiate dynamically
|
// needed to instantiate dynamically
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DemanglerOptions createDefaultOptions() {
|
||||||
|
return new GnuDemanglerOptions();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canDemangle(Program program) {
|
public boolean canDemangle(Program program) {
|
||||||
|
|
||||||
String executableFormat = program.getExecutableFormat();
|
String executableFormat = program.getExecutableFormat();
|
||||||
if (isELF(executableFormat) || isMacho(executableFormat)) {
|
if (isELF(executableFormat) || isMacho(executableFormat)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
//check if language is GCC
|
CompilerSpec spec = program.getCompilerSpec();
|
||||||
CompilerSpec compilerSpec = program.getCompilerSpec();
|
String specId = spec.getCompilerSpecID().getIdAsString();
|
||||||
if (compilerSpec.getCompilerSpecID().getIdAsString().toLowerCase().indexOf(
|
if (!specId.toLowerCase().contains("windows")) {
|
||||||
"windows") == -1) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
@ -57,13 +62,21 @@ public class GnuDemangler implements Demangler {
|
||||||
@Override
|
@Override
|
||||||
public DemangledObject demangle(String mangled, boolean demangleOnlyKnownPatterns)
|
public DemangledObject demangle(String mangled, boolean demangleOnlyKnownPatterns)
|
||||||
throws DemangledException {
|
throws DemangledException {
|
||||||
|
GnuDemanglerOptions options = new GnuDemanglerOptions();
|
||||||
|
options.setDemangleOnlyKnownPatterns(demangleOnlyKnownPatterns);
|
||||||
|
return demangle(mangled, options);
|
||||||
|
}
|
||||||
|
|
||||||
if (skip(mangled, demangleOnlyKnownPatterns)) {
|
@Override
|
||||||
|
public DemangledObject demangle(String mangled, DemanglerOptions demanglerOtions)
|
||||||
|
throws DemangledException {
|
||||||
|
|
||||||
|
if (skip(mangled, demanglerOtions)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GnuDemanglerOptions options = getGnuOptions(demanglerOtions);
|
||||||
String originalMangled = mangled;
|
String originalMangled = mangled;
|
||||||
|
|
||||||
String globalPrefix = null;
|
String globalPrefix = null;
|
||||||
if (mangled.startsWith(GLOBAL_PREFIX)) {
|
if (mangled.startsWith(GLOBAL_PREFIX)) {
|
||||||
int index = mangled.indexOf("_Z");
|
int index = mangled.indexOf("_Z");
|
||||||
|
@ -84,15 +97,16 @@ public class GnuDemangler implements Demangler {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
GnuDemanglerNativeProcess process = GnuDemanglerNativeProcess.getDemanglerNativeProcess();
|
|
||||||
String demangled = process.demangle(mangled).trim();
|
|
||||||
|
|
||||||
|
GnuDemanglerNativeProcess process = getNativeProcess(options);
|
||||||
|
String demangled = process.demangle(mangled).trim();
|
||||||
if (mangled.equals(demangled) || demangled.length() == 0) {
|
if (mangled.equals(demangled) || demangled.length() == 0) {
|
||||||
throw new DemangledException(true);
|
throw new DemangledException(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boolean onlyKnownPatterns = options.demangleOnlyKnownPatterns();
|
||||||
DemangledObject demangledObject =
|
DemangledObject demangledObject =
|
||||||
parse(mangled, process, demangled, demangleOnlyKnownPatterns);
|
parse(mangled, process, demangled, onlyKnownPatterns);
|
||||||
if (demangledObject == null) {
|
if (demangledObject == null) {
|
||||||
return demangledObject;
|
return demangledObject;
|
||||||
}
|
}
|
||||||
|
@ -107,6 +121,7 @@ public class GnuDemangler implements Demangler {
|
||||||
else {
|
else {
|
||||||
demangledObject.setSignature(demangled);
|
demangledObject.setSignature(demangled);
|
||||||
}
|
}
|
||||||
|
|
||||||
demangledObject.setOriginalMangled(originalMangled);
|
demangledObject.setOriginalMangled(originalMangled);
|
||||||
|
|
||||||
if (isDwarf) {
|
if (isDwarf) {
|
||||||
|
@ -121,7 +136,7 @@ public class GnuDemangler implements Demangler {
|
||||||
return demangledObject;
|
return demangledObject;
|
||||||
}
|
}
|
||||||
catch (IOException e) {
|
catch (IOException e) {
|
||||||
if (e.getMessage().endsWith("14001")) {//missing runtime dlls, prolly
|
if (e.getMessage().endsWith("14001")) {
|
||||||
ResourceFile installationDir = Application.getInstallationDirectory();
|
ResourceFile installationDir = Application.getInstallationDirectory();
|
||||||
throw new DemangledException("Missing runtime libraries. " + "Please install " +
|
throw new DemangledException("Missing runtime libraries. " + "Please install " +
|
||||||
installationDir + File.separatorChar + "support" + File.separatorChar +
|
installationDir + File.separatorChar + "support" + File.separatorChar +
|
||||||
|
@ -131,7 +146,25 @@ public class GnuDemangler implements Demangler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean skip(String mangled, boolean demangleOnlyKnownPatterns) {
|
private GnuDemanglerOptions getGnuOptions(DemanglerOptions options) {
|
||||||
|
|
||||||
|
if (options instanceof GnuDemanglerOptions) {
|
||||||
|
return (GnuDemanglerOptions) options;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new GnuDemanglerOptions(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
private GnuDemanglerNativeProcess getNativeProcess(GnuDemanglerOptions options)
|
||||||
|
throws IOException {
|
||||||
|
|
||||||
|
String demanglerName = options.getDemanglerName();
|
||||||
|
String applicationOptions = options.getDemanglerApplicationArguments();
|
||||||
|
return GnuDemanglerNativeProcess.getDemanglerNativeProcess(demanglerName,
|
||||||
|
applicationOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean skip(String mangled, DemanglerOptions options) {
|
||||||
|
|
||||||
// Ignore versioned symbols which are generally duplicated at the same address
|
// Ignore versioned symbols which are generally duplicated at the same address
|
||||||
if (mangled.indexOf("@") > 0) { // do not demangle versioned symbols
|
if (mangled.indexOf("@") > 0) { // do not demangle versioned symbols
|
||||||
|
@ -143,7 +176,7 @@ public class GnuDemangler implements Demangler {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!demangleOnlyKnownPatterns) {
|
if (!options.demangleOnlyKnownPatterns()) {
|
||||||
return false; // let it go through
|
return false; // let it go through
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -167,24 +200,20 @@ public class GnuDemangler implements Demangler {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private DemangledObject parse(String mangled, GnuDemanglerNativeProcess process, String demangled,
|
private DemangledObject parse(String mangled, GnuDemanglerNativeProcess process,
|
||||||
|
String demangled,
|
||||||
boolean demangleOnlyKnownPatterns) {
|
boolean demangleOnlyKnownPatterns) {
|
||||||
|
|
||||||
if (demangleOnlyKnownPatterns && !isMangledString(mangled, demangled)) {
|
if (demangleOnlyKnownPatterns && !isKnownMangledString(mangled, demangled)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
|
||||||
GnuDemanglerParser parser = new GnuDemanglerParser(process);
|
GnuDemanglerParser parser = new GnuDemanglerParser(process);
|
||||||
DemangledObject demangledObject = parser.parse(mangled, demangled);
|
DemangledObject demangledObject = parser.parse(mangled, demangled);
|
||||||
return demangledObject;
|
return demangledObject;
|
||||||
}
|
}
|
||||||
catch (Exception e) {
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isMangledString(String mangled, String demangled) {
|
private boolean isKnownMangledString(String mangled, String demangled) {
|
||||||
//
|
//
|
||||||
// We get requests to demangle strings that are not mangled. For newer mangled strings
|
// We get requests to demangle strings that are not mangled. For newer mangled strings
|
||||||
// we know how to avoid that. However, older mangled strings can be of many forms. To
|
// we know how to avoid that. However, older mangled strings can be of many forms. To
|
||||||
|
|
|
@ -16,29 +16,68 @@
|
||||||
package ghidra.app.util.demangler.gnu;
|
package ghidra.app.util.demangler.gnu;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
import org.apache.commons.io.FilenameUtils;
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.apache.commons.lang3.ArrayUtils;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
import ghidra.framework.Application;
|
import ghidra.framework.Application;
|
||||||
import ghidra.framework.Platform;
|
import ghidra.framework.Platform;
|
||||||
|
|
||||||
public class GnuDemanglerNativeProcess {
|
public class GnuDemanglerNativeProcess {
|
||||||
public static final String DEMANGLER_GNU = "demangler_gnu_v2.33.1";
|
public static final String DEMANGLER_GNU = GnuDemanglerOptions.GNU_DEMANGLER_DEFAULT;
|
||||||
|
|
||||||
private static GnuDemanglerNativeProcess demanglerNativeProcess;
|
private static final String DEFAULT_NATIVE_OPTIONS = "";
|
||||||
|
private static final Map<String, GnuDemanglerNativeProcess> processesByName =
|
||||||
|
new HashMap<>();
|
||||||
|
|
||||||
|
private String applicationName;
|
||||||
|
private String options;
|
||||||
|
|
||||||
private boolean isDisposed;
|
private boolean isDisposed;
|
||||||
private Process process;
|
private Process process;
|
||||||
private BufferedReader reader;
|
private BufferedReader reader;
|
||||||
private PrintWriter writer;
|
private PrintWriter writer;
|
||||||
|
|
||||||
|
// TODO docme
|
||||||
public static synchronized GnuDemanglerNativeProcess getDemanglerNativeProcess()
|
public static synchronized GnuDemanglerNativeProcess getDemanglerNativeProcess()
|
||||||
throws IOException {
|
throws IOException {
|
||||||
if (demanglerNativeProcess == null) {
|
return getDemanglerNativeProcess(DEMANGLER_GNU);
|
||||||
demanglerNativeProcess = new GnuDemanglerNativeProcess();
|
|
||||||
}
|
|
||||||
return demanglerNativeProcess;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private GnuDemanglerNativeProcess() throws IOException {
|
// TODO docme
|
||||||
|
public static synchronized GnuDemanglerNativeProcess getDemanglerNativeProcess(String name)
|
||||||
|
throws IOException {
|
||||||
|
|
||||||
|
return getDemanglerNativeProcess(name, DEFAULT_NATIVE_OPTIONS);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO docme
|
||||||
|
// TODO we should probably age-off all demanglers by access time
|
||||||
|
public static synchronized GnuDemanglerNativeProcess getDemanglerNativeProcess(String name,
|
||||||
|
String nativeOptions)
|
||||||
|
throws IOException {
|
||||||
|
|
||||||
|
String options = nativeOptions;
|
||||||
|
if (StringUtils.isBlank(options)) {
|
||||||
|
options = DEFAULT_NATIVE_OPTIONS;
|
||||||
|
}
|
||||||
|
|
||||||
|
String key = name + nativeOptions;
|
||||||
|
GnuDemanglerNativeProcess nativeProcess = processesByName.get(key);
|
||||||
|
if (nativeProcess == null) {
|
||||||
|
nativeProcess = new GnuDemanglerNativeProcess(name, options);
|
||||||
|
processesByName.put(key, nativeProcess);
|
||||||
|
}
|
||||||
|
return nativeProcess;
|
||||||
|
}
|
||||||
|
|
||||||
|
private GnuDemanglerNativeProcess(String applicationName, String options) throws IOException {
|
||||||
|
this.applicationName = applicationName;
|
||||||
|
this.options = options;
|
||||||
createProcess();
|
createProcess();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,22 +90,25 @@ public class GnuDemanglerNativeProcess {
|
||||||
|
|
||||||
private String demangle(String mangled, boolean restart) throws IOException {
|
private String demangle(String mangled, boolean restart) throws IOException {
|
||||||
try {
|
try {
|
||||||
writer.println(mangled);
|
return doDemangle(mangled);
|
||||||
writer.flush();
|
|
||||||
|
|
||||||
return reader.readLine();
|
|
||||||
}
|
}
|
||||||
catch (IOException e) {
|
catch (IOException e) {
|
||||||
dispose();
|
dispose();
|
||||||
if (!restart) {
|
if (!restart) {
|
||||||
demanglerNativeProcess = null;
|
processesByName.remove(applicationName);
|
||||||
throw new IOException("Demangler process is not running.");
|
throw new IOException("Demangler process is not running.", e);
|
||||||
}
|
}
|
||||||
createProcess();
|
createProcess();
|
||||||
return demangle(mangled, false);
|
return demangle(mangled, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String doDemangle(String mangled) throws IOException {
|
||||||
|
writer.println(mangled);
|
||||||
|
writer.flush();
|
||||||
|
return reader.readLine();
|
||||||
|
}
|
||||||
|
|
||||||
private void dispose() {
|
private void dispose() {
|
||||||
try {
|
try {
|
||||||
if (process != null) {
|
if (process != null) {
|
||||||
|
@ -86,19 +128,66 @@ public class GnuDemanglerNativeProcess {
|
||||||
|
|
||||||
private void createProcess() throws IOException {
|
private void createProcess() throws IOException {
|
||||||
|
|
||||||
String executableName = DEMANGLER_GNU + Platform.CURRENT_PLATFORM.getExecutableExtension();
|
String[] command = buildCommand();
|
||||||
File commandPath = Application.getOSFile(executableName);
|
|
||||||
|
|
||||||
String[] command = new String[] { commandPath.getAbsolutePath() };
|
|
||||||
|
|
||||||
process = Runtime.getRuntime().exec(command);
|
process = Runtime.getRuntime().exec(command);
|
||||||
|
|
||||||
InputStream in = process.getInputStream();
|
InputStream in = process.getInputStream();
|
||||||
OutputStream out = process.getOutputStream();
|
OutputStream out = process.getOutputStream();
|
||||||
|
|
||||||
reader = new BufferedReader(new InputStreamReader(in));
|
reader = new BufferedReader(new InputStreamReader(in));
|
||||||
writer = new PrintWriter(out);
|
writer = new PrintWriter(out);
|
||||||
|
|
||||||
|
checkForError(command);
|
||||||
|
|
||||||
isDisposed = false;
|
isDisposed = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String[] buildCommand() throws FileNotFoundException {
|
||||||
|
|
||||||
|
String executableName =
|
||||||
|
applicationName + Platform.CURRENT_PLATFORM.getExecutableExtension();
|
||||||
|
File commandPath = Application.getOSFile(executableName);
|
||||||
|
|
||||||
|
String[] command = new String[] { commandPath.getAbsolutePath() };
|
||||||
|
if (!StringUtils.isBlank(options)) {
|
||||||
|
String[] optionsArray = options.split("\\s");
|
||||||
|
command = ArrayUtils.addAll(command, optionsArray);
|
||||||
|
}
|
||||||
|
return command;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkForError(String[] command) throws IOException {
|
||||||
|
|
||||||
|
//
|
||||||
|
// We do not want to read the error stream in the happy path case, as that will block.
|
||||||
|
// Send a test string over and read the result. If the test string is blank, then
|
||||||
|
// there was an error.
|
||||||
|
//
|
||||||
|
String testResult = doDemangle("test");
|
||||||
|
if (!StringUtils.isBlank(testResult)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
InputStream err = process.getErrorStream();
|
||||||
|
String error = null;
|
||||||
|
try {
|
||||||
|
List<String> errorLines = IOUtils.readLines(err, Charset.defaultCharset());
|
||||||
|
error = StringUtils.join(errorLines, '\n');
|
||||||
|
}
|
||||||
|
catch (IOException e) {
|
||||||
|
throw new IOException("Unable to read process error stream: ", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (StringUtils.isBlank(error)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String executable = command[0];
|
||||||
|
String baseName = FilenameUtils.getBaseName(executable);
|
||||||
|
command[0] = baseName;
|
||||||
|
|
||||||
|
// cleanup full path, as it is ugly in the error message
|
||||||
|
error = error.replace(executable, "");
|
||||||
|
throw new IOException("Error starting demangler with command: '" +
|
||||||
|
Arrays.toString(command) + "' " + error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,115 @@
|
||||||
|
/* ###
|
||||||
|
* IP: GHIDRA
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ghidra.app.util.demangler.gnu;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
import ghidra.app.util.demangler.DemanglerOptions;
|
||||||
|
|
||||||
|
public class GnuDemanglerOptions extends DemanglerOptions {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Version 2.24 of the GNU demangler. This version supports older formats and older bugs.
|
||||||
|
*/
|
||||||
|
public static final String GNU_DEMANGLER_V2_24 = "demangler_gnu_v2_24";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Version 2.33.1 of the GNU demangler. This version supports less formats than older versions.
|
||||||
|
*/
|
||||||
|
public static final String GNU_DEMANGLER_V2_33_1 = "demangler_gnu_v2_33_1";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default version to use of the GNU demangler
|
||||||
|
*/
|
||||||
|
public static final String GNU_DEMANGLER_DEFAULT = GNU_DEMANGLER_V2_33_1;
|
||||||
|
|
||||||
|
private String demanglerName = GNU_DEMANGLER_DEFAULT;
|
||||||
|
private String demanglerApplicationArguments;
|
||||||
|
private boolean useDeprecatedDemangler;
|
||||||
|
|
||||||
|
public GnuDemanglerOptions() {
|
||||||
|
// use default values
|
||||||
|
}
|
||||||
|
|
||||||
|
public GnuDemanglerOptions(DemanglerOptions copy) {
|
||||||
|
super(copy);
|
||||||
|
|
||||||
|
if (copy instanceof GnuDemanglerOptions) {
|
||||||
|
GnuDemanglerOptions gCopy = (GnuDemanglerOptions) copy;
|
||||||
|
demanglerName = gCopy.demanglerName;
|
||||||
|
demanglerApplicationArguments = gCopy.demanglerApplicationArguments;
|
||||||
|
useDeprecatedDemangler = gCopy.useDeprecatedDemangler;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO docme
|
||||||
|
public String getDemanglerName() {
|
||||||
|
return demanglerName;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO docme
|
||||||
|
// TODO should we validate and or log a message it the name is unknown?
|
||||||
|
public void setDemanglerName(String name) {
|
||||||
|
this.demanglerName = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO docme
|
||||||
|
public String getDemanglerApplicationArguments() {
|
||||||
|
return demanglerApplicationArguments;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO docme
|
||||||
|
public void setDemanglerApplicationArguments(String args) {
|
||||||
|
this.demanglerApplicationArguments = args;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO docme
|
||||||
|
// TODO mabye rename to hasNativeApplicationOptions()
|
||||||
|
public boolean hasDemanglerApplicationArguments() {
|
||||||
|
return !StringUtils.isBlank(demanglerApplicationArguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO docme
|
||||||
|
public void setUseDeprecatedDemangler(boolean doUse) {
|
||||||
|
this.useDeprecatedDemangler = doUse;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO docme
|
||||||
|
public boolean useDeprecatedDemangler() {
|
||||||
|
return useDeprecatedDemangler;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO docme
|
||||||
|
public GnuDemanglerOptions withDeprecatedDemangler() {
|
||||||
|
GnuDemanglerOptions newOptions = new GnuDemanglerOptions(this);
|
||||||
|
newOptions.setDemanglerName(GNU_DEMANGLER_V2_24);
|
||||||
|
return newOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
//@formatter:off
|
||||||
|
return "{\n" +
|
||||||
|
"\tdoDisassembly: " + doDisassembly() + ",\n" +
|
||||||
|
"\tapplySignature: " + applySignature() + ",\n" +
|
||||||
|
"\tdemangleOnlyKnownPatterns: " + demangleOnlyKnownPatterns() + ",\n" +
|
||||||
|
"\tdemanglerName: " + demanglerName + ",\n" +
|
||||||
|
"\tuseDeprecatedDemangler: " + useDeprecatedDemangler + ",\n" +
|
||||||
|
"\tdemanglerApplicationArguments: " + demanglerApplicationArguments + ",\n" +
|
||||||
|
"}";
|
||||||
|
//@formatter:on
|
||||||
|
}
|
||||||
|
}
|
|
@ -27,7 +27,7 @@ import ghidra.app.util.demangler.*;
|
||||||
import ghidra.program.model.lang.CompilerSpec;
|
import ghidra.program.model.lang.CompilerSpec;
|
||||||
import ghidra.util.StringUtilities;
|
import ghidra.util.StringUtilities;
|
||||||
|
|
||||||
public class GnuDemanglerParser implements DemanglerParser {
|
public class GnuDemanglerParser {
|
||||||
|
|
||||||
private static final String CONSTRUCTION_VTABLE_FOR = "construction vtable for ";
|
private static final String CONSTRUCTION_VTABLE_FOR = "construction vtable for ";
|
||||||
private static final String VTT_FOR = "VTT for ";
|
private static final String VTT_FOR = "VTT for ";
|
||||||
|
@ -154,7 +154,6 @@ public class GnuDemanglerParser implements DemanglerParser {
|
||||||
this.process = process;
|
this.process = process;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public DemangledObject parse(String mangled, String demangled) {
|
public DemangledObject parse(String mangled, String demangled) {
|
||||||
try {
|
try {
|
||||||
return doParse(mangled, demangled);
|
return doParse(mangled, demangled);
|
||||||
|
|
|
@ -33,7 +33,8 @@ public class GnuDemanglerParserTest extends AbstractGenericTest {
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() throws Exception {
|
public void setUp() throws Exception {
|
||||||
process = GnuDemanglerNativeProcess.getDemanglerNativeProcess();
|
process = GnuDemanglerNativeProcess
|
||||||
|
.getDemanglerNativeProcess(GnuDemanglerOptions.GNU_DEMANGLER_V2_33_1);
|
||||||
parser = new GnuDemanglerParser(process);
|
parser = new GnuDemanglerParser(process);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,38 +42,37 @@ public class GnuDemanglerParserTest extends AbstractGenericTest {
|
||||||
public void test() throws Exception {
|
public void test() throws Exception {
|
||||||
long start = System.currentTimeMillis();
|
long start = System.currentTimeMillis();
|
||||||
|
|
||||||
demangle(process, parser, "_ZTVN6Magick21DrawableTextAntialiasE");
|
demangle("_ZTVN6Magick21DrawableTextAntialiasE");
|
||||||
demangle(process, parser, "_ZGVZN10KDirLister11emitChangesEvE3dot");//guard variables
|
demangle("_ZGVZN10KDirLister11emitChangesEvE3dot");//guard variables
|
||||||
|
|
||||||
demangle(process, parser, "_ZZ18__gthread_active_pvE20__gthread_active_ptr");
|
demangle("_ZZ18__gthread_active_pvE20__gthread_active_ptr");
|
||||||
|
|
||||||
demangle(process, parser, "_ZNSt10_List_baseIN6Magick5VPathESaIS1_EE5clearEv");
|
demangle("_ZNSt10_List_baseIN6Magick5VPathESaIS1_EE5clearEv");
|
||||||
demangle(process, parser, "_ZTISt14unary_functionIPN9MagickLib12_DrawContextEvE");
|
demangle("_ZTISt14unary_functionIPN9MagickLib12_DrawContextEvE");
|
||||||
demangle(process, parser, "_ZTSSt14unary_functionIPN9MagickLib12_DrawContextEvE");
|
demangle("_ZTSSt14unary_functionIPN9MagickLib12_DrawContextEvE");
|
||||||
demangle(process, parser, "_ZTCN4Arts17StdoutWriter_implE68_NS_11Object_skelE");
|
demangle("_ZTCN4Arts17StdoutWriter_implE68_NS_11Object_skelE");
|
||||||
demangle(process, parser, "_ZN6Magick5ImageD1Ev");
|
demangle("_ZN6Magick5ImageD1Ev");
|
||||||
demangle(process, parser,
|
demangle(
|
||||||
"_ZN6Magick19matteFloodfillImageC2ERKNS_5ColorEjiiN9MagickLib11PaintMethodE");
|
"_ZN6Magick19matteFloodfillImageC2ERKNS_5ColorEjiiN9MagickLib11PaintMethodE");
|
||||||
demangle(process, parser, "_ZThn8_N14nsPrintSession6AddRefEv");// non-virtual thunk
|
demangle("_ZThn8_N14nsPrintSession6AddRefEv");// non-virtual thunk
|
||||||
demangle(process, parser,
|
demangle(
|
||||||
"_ZTv0_n24_NSt19basic_ostringstreamIcSt11char_traitsIcE14pool_allocatorIcEED0Ev");// virtual thunk
|
"_ZTv0_n24_NSt19basic_ostringstreamIcSt11char_traitsIcE14pool_allocatorIcEED0Ev");// virtual thunk
|
||||||
demangle(process, parser, "_ZTch0_h16_NK8KHotKeys13WindowTrigger4copyEPNS_10ActionDataE");// covariant return thunk
|
demangle("_ZTch0_h16_NK8KHotKeys13WindowTrigger4copyEPNS_10ActionDataE");// covariant return thunk
|
||||||
|
|
||||||
demangle(process, parser, "_ZNK2cc14ScrollSnapTypeneERKS0_");
|
demangle("_ZNK2cc14ScrollSnapTypeneERKS0_");
|
||||||
|
|
||||||
List<String> list = loadTextResource(GnuDemanglerParserTest.class, "libMagick.symbols.txt");
|
List<String> list = loadTextResource(GnuDemanglerParserTest.class, "libMagick.symbols.txt");
|
||||||
for (String mangled : list) {
|
for (String mangled : list) {
|
||||||
if (mangled == null) {
|
if (mangled == null) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
demangle(process, parser, mangled);
|
demangle(mangled);
|
||||||
}
|
}
|
||||||
|
|
||||||
System.out.println("Elapsed Time: " + (System.currentTimeMillis() - start));
|
System.out.println("Elapsed Time: " + (System.currentTimeMillis() - start));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void demangle(GnuDemanglerNativeProcess process, GnuDemanglerParser parser,
|
private void demangle(String mangled) throws IOException {
|
||||||
String mangled) throws IOException {
|
|
||||||
String demangled = process.demangle(mangled);
|
String demangled = process.demangle(mangled);
|
||||||
assertNotNull(demangled);
|
assertNotNull(demangled);
|
||||||
assertNotEquals(mangled, demangled);
|
assertNotEquals(mangled, demangled);
|
||||||
|
@ -81,14 +81,12 @@ public class GnuDemanglerParserTest extends AbstractGenericTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testParsingBug() {
|
public void testOverloadedShiftOperatorParsingBug() {
|
||||||
// std::basic_istream<char, std::char_traits<char> >& std::operator>><char, std::char_traits<char> >(std::basic_istream<char, std::char_traits<char> >&, char&)
|
parser = new GnuDemanglerParser(null);
|
||||||
|
|
||||||
// _ZStrsIcSt11char_traitsIcEERSt13basic_istreamIT_T0_ES6_RS3_
|
|
||||||
// _ZStrsIcSt11char_traitsIcESaIcEERSt13basic_istreamIT_T0_ES7_RSbIS4_S5_T1_E
|
|
||||||
GnuDemanglerParser parser = new GnuDemanglerParser(null);
|
|
||||||
DemangledObject object = parser.parse(null,
|
DemangledObject object = parser.parse(null,
|
||||||
"std::basic_istream<char, std::char_traits<char> >& std::operator>><char, std::char_traits<char> >(std::basic_istream<char, std::char_traits<char> >&, char&)");
|
"std::basic_istream<char, std::char_traits<char> >& " +
|
||||||
|
"std::operator>><char, std::char_traits<char> >" +
|
||||||
|
"(std::basic_istream<char, std::char_traits<char> >&, char&)");
|
||||||
String name = object.getName();
|
String name = object.getName();
|
||||||
assertEquals("operator>><char,std--char_traits<char>>", name);
|
assertEquals("operator>><char,std--char_traits<char>>", name);
|
||||||
}
|
}
|
||||||
|
@ -210,6 +208,9 @@ public class GnuDemanglerParserTest extends AbstractGenericTest {
|
||||||
public void testFunctionPointers() throws Exception {
|
public void testFunctionPointers() throws Exception {
|
||||||
String mangled = "__t6XpsMap2ZlZP14CORBA_TypeCodePFRCl_UlUlUlf";
|
String mangled = "__t6XpsMap2ZlZP14CORBA_TypeCodePFRCl_UlUlUlf";
|
||||||
|
|
||||||
|
process = GnuDemanglerNativeProcess
|
||||||
|
.getDemanglerNativeProcess(GnuDemanglerOptions.GNU_DEMANGLER_V2_24);
|
||||||
|
|
||||||
String demangled = process.demangle(mangled);
|
String demangled = process.demangle(mangled);
|
||||||
|
|
||||||
DemangledObject object = parser.parse(mangled, demangled);
|
DemangledObject object = parser.parse(mangled, demangled);
|
||||||
|
@ -431,6 +432,9 @@ public class GnuDemanglerParserTest extends AbstractGenericTest {
|
||||||
//
|
//
|
||||||
String mangled = "CalcPortExposedRect__13LScrollerViewCFR4Rectb";
|
String mangled = "CalcPortExposedRect__13LScrollerViewCFR4Rectb";
|
||||||
|
|
||||||
|
process = GnuDemanglerNativeProcess
|
||||||
|
.getDemanglerNativeProcess(GnuDemanglerOptions.GNU_DEMANGLER_V2_24);
|
||||||
|
|
||||||
String demangled = process.demangle(mangled);
|
String demangled = process.demangle(mangled);
|
||||||
|
|
||||||
DemangledObject object = parser.parse(mangled, demangled);
|
DemangledObject object = parser.parse(mangled, demangled);
|
||||||
|
@ -452,6 +456,9 @@ public class GnuDemanglerParserTest extends AbstractGenericTest {
|
||||||
//
|
//
|
||||||
String mangled = "__dt__Q26MsoDAL9VertFrameFv";
|
String mangled = "__dt__Q26MsoDAL9VertFrameFv";
|
||||||
|
|
||||||
|
process = GnuDemanglerNativeProcess
|
||||||
|
.getDemanglerNativeProcess(GnuDemanglerOptions.GNU_DEMANGLER_V2_24);
|
||||||
|
|
||||||
String demangled = process.demangle(mangled);
|
String demangled = process.demangle(mangled);
|
||||||
|
|
||||||
DemangledObject object = parser.parse(mangled, demangled);
|
DemangledObject object = parser.parse(mangled, demangled);
|
||||||
|
@ -491,6 +498,9 @@ public class GnuDemanglerParserTest extends AbstractGenericTest {
|
||||||
//
|
//
|
||||||
String mangled = "GetColWidths__13CDataRendererCFRA7_s";
|
String mangled = "GetColWidths__13CDataRendererCFRA7_s";
|
||||||
|
|
||||||
|
process = GnuDemanglerNativeProcess
|
||||||
|
.getDemanglerNativeProcess(GnuDemanglerOptions.GNU_DEMANGLER_V2_24);
|
||||||
|
|
||||||
String demangled = process.demangle(mangled);
|
String demangled = process.demangle(mangled);
|
||||||
|
|
||||||
DemangledObject object = parser.parse(mangled, demangled);
|
DemangledObject object = parser.parse(mangled, demangled);
|
||||||
|
@ -512,6 +522,9 @@ public class GnuDemanglerParserTest extends AbstractGenericTest {
|
||||||
//
|
//
|
||||||
String mangled = "GetColWidths__13CDataRendererCFPA7_s";
|
String mangled = "GetColWidths__13CDataRendererCFPA7_s";
|
||||||
|
|
||||||
|
process = GnuDemanglerNativeProcess
|
||||||
|
.getDemanglerNativeProcess(GnuDemanglerOptions.GNU_DEMANGLER_V2_24);
|
||||||
|
|
||||||
String demangled = process.demangle(mangled);
|
String demangled = process.demangle(mangled);
|
||||||
|
|
||||||
DemangledObject object = parser.parse(mangled, demangled);
|
DemangledObject object = parser.parse(mangled, demangled);
|
||||||
|
@ -567,6 +580,9 @@ public class GnuDemanglerParserTest extends AbstractGenericTest {
|
||||||
//
|
//
|
||||||
String mangled = "_gmStage2__FP12SECTION_INFOPiPA12_iiPCs";
|
String mangled = "_gmStage2__FP12SECTION_INFOPiPA12_iiPCs";
|
||||||
|
|
||||||
|
process = GnuDemanglerNativeProcess
|
||||||
|
.getDemanglerNativeProcess(GnuDemanglerOptions.GNU_DEMANGLER_V2_24);
|
||||||
|
|
||||||
String demangled = process.demangle(mangled);
|
String demangled = process.demangle(mangled);
|
||||||
|
|
||||||
DemangledObject object = parser.parse(mangled, demangled);
|
DemangledObject object = parser.parse(mangled, demangled);
|
||||||
|
@ -598,6 +614,9 @@ public class GnuDemanglerParserTest extends AbstractGenericTest {
|
||||||
//
|
//
|
||||||
String mangled = "__ct__Q24CStr6BufferFR4CStrUl";
|
String mangled = "__ct__Q24CStr6BufferFR4CStrUl";
|
||||||
|
|
||||||
|
process = GnuDemanglerNativeProcess
|
||||||
|
.getDemanglerNativeProcess(GnuDemanglerOptions.GNU_DEMANGLER_V2_24);
|
||||||
|
|
||||||
String demangled = process.demangle(mangled);
|
String demangled = process.demangle(mangled);
|
||||||
|
|
||||||
DemangledObject object = parser.parse(mangled, demangled);
|
DemangledObject object = parser.parse(mangled, demangled);
|
||||||
|
@ -1085,6 +1104,7 @@ public class GnuDemanglerParserTest extends AbstractGenericTest {
|
||||||
// Mangled: _ZN12uavcan_stm329CanDriverC1ILj64EEERA2_AT__NS_9CanRxItemE
|
// Mangled: _ZN12uavcan_stm329CanDriverC1ILj64EEERA2_AT__NS_9CanRxItemE
|
||||||
//
|
//
|
||||||
// Demangled: uavcan_stm32::CanDriver::CanDriver<64u>(uavcan_stm32::CanRxItem (&) [2][64u])
|
// Demangled: uavcan_stm32::CanDriver::CanDriver<64u>(uavcan_stm32::CanRxItem (&) [2][64u])
|
||||||
|
//
|
||||||
|
|
||||||
String mangled = "_ZN12uavcan_stm329CanDriverC1ILj64EEERA2_AT__NS_9CanRxItemE";
|
String mangled = "_ZN12uavcan_stm329CanDriverC1ILj64EEERA2_AT__NS_9CanRxItemE";
|
||||||
|
|
||||||
|
@ -1113,4 +1133,25 @@ public class GnuDemanglerParserTest extends AbstractGenericTest {
|
||||||
DemangledObject res = demangler.demangle(mangled, true);
|
DemangledObject res = demangler.demangle(mangled, true);
|
||||||
assertNull(res);
|
assertNull(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFunctionWithLambda_WrappingAnotherFunctionCall() throws Exception {
|
||||||
|
|
||||||
|
//
|
||||||
|
// Mangled: _Z11wrap_360_cdIiEDTcl8wrap_360fp_Lf42c80000EEET_
|
||||||
|
//
|
||||||
|
// Demangled: (wrap_360({parm#1}, (float)[42c80000])) wrap_360_cd<int>(int)
|
||||||
|
//
|
||||||
|
|
||||||
|
String mangled = "_Z11wrap_360_cdIiEDTcl8wrap_360fp_Lf42c80000EEET_";
|
||||||
|
String demangled = process.demangle(mangled);
|
||||||
|
|
||||||
|
DemangledObject object = parser.parse(mangled, demangled);
|
||||||
|
assertNotNull(object);
|
||||||
|
assertTrue(object instanceof DemangledFunction);
|
||||||
|
|
||||||
|
// TODO maybe put full output in setUtilDemangled()
|
||||||
|
String signature = object.getSignature(false);
|
||||||
|
assertEquals("undefined wrap_360_cd<int>(int)", signature);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,11 +17,12 @@ package ghidra.app.util.demangler.gnu;
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import generic.test.AbstractGenericTest;
|
import generic.test.AbstractGenericTest;
|
||||||
import ghidra.app.cmd.label.DemanglerCmd;
|
|
||||||
import ghidra.app.util.demangler.*;
|
import ghidra.app.util.demangler.*;
|
||||||
import ghidra.program.database.ProgramDB;
|
import ghidra.program.database.ProgramDB;
|
||||||
import ghidra.program.model.address.Address;
|
import ghidra.program.model.address.Address;
|
||||||
|
@ -30,6 +31,7 @@ import ghidra.program.model.listing.CodeUnit;
|
||||||
import ghidra.program.model.listing.Data;
|
import ghidra.program.model.listing.Data;
|
||||||
import ghidra.program.model.symbol.*;
|
import ghidra.program.model.symbol.*;
|
||||||
import ghidra.test.ToyProgramBuilder;
|
import ghidra.test.ToyProgramBuilder;
|
||||||
|
import ghidra.util.Msg;
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
public class GnuDemanglerTest extends AbstractGenericTest {
|
public class GnuDemanglerTest extends AbstractGenericTest {
|
||||||
|
@ -161,7 +163,10 @@ public class GnuDemanglerTest extends AbstractGenericTest {
|
||||||
GnuDemangler demangler = new GnuDemangler();
|
GnuDemangler demangler = new GnuDemangler();
|
||||||
demangler.canDemangle(program);// this perform initialization
|
demangler.canDemangle(program);// this perform initialization
|
||||||
|
|
||||||
DemangledObject result = demangler.demangle(mangled, false);
|
GnuDemanglerOptions options = new GnuDemanglerOptions();
|
||||||
|
options.setDemangleOnlyKnownPatterns(false);
|
||||||
|
options.setDemanglerName(GnuDemanglerOptions.GNU_DEMANGLER_V2_24);
|
||||||
|
DemangledObject result = demangler.demangle(mangled, options);
|
||||||
assertNotNull(result);
|
assertNotNull(result);
|
||||||
assertEquals("undefined MyFunction::~MyFunction(void)", result.getSignature(false));
|
assertEquals("undefined MyFunction::~MyFunction(void)", result.getSignature(false));
|
||||||
}
|
}
|
||||||
|
@ -184,10 +189,63 @@ public class GnuDemanglerTest extends AbstractGenericTest {
|
||||||
GnuDemangler demangler = new GnuDemangler();
|
GnuDemangler demangler = new GnuDemangler();
|
||||||
demangler.canDemangle(program);// this perform initialization
|
demangler.canDemangle(program);// this perform initialization
|
||||||
|
|
||||||
DemangledObject result = demangler.demangle(mangled, true);
|
GnuDemanglerOptions options = new GnuDemanglerOptions();
|
||||||
|
options.setDemangleOnlyKnownPatterns(true); // do not try
|
||||||
|
options.setDemanglerName(GnuDemanglerOptions.GNU_DEMANGLER_V2_24);
|
||||||
|
DemangledObject result = demangler.demangle(mangled, options);
|
||||||
assertNull(result);
|
assertNull(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDemangler_Format_CodeWarrior_MacOS8or9() throws DemangledException {
|
||||||
|
|
||||||
|
// .scroll__10TTextPanelFUcsi
|
||||||
|
|
||||||
|
String mangled = ".scroll__10TTextPanelFUcsi";
|
||||||
|
|
||||||
|
GnuDemangler demangler = new GnuDemangler();
|
||||||
|
demangler.canDemangle(program);// this perform initialization
|
||||||
|
|
||||||
|
GnuDemanglerOptions options = new GnuDemanglerOptions();
|
||||||
|
options.setDemangleOnlyKnownPatterns(false);
|
||||||
|
options.setDemanglerName(GnuDemanglerOptions.GNU_DEMANGLER_V2_24);
|
||||||
|
DemangledObject result = demangler.demangle(mangled, options);
|
||||||
|
assertNotNull(result);
|
||||||
|
assertEquals("undefined TTextPanel::scroll(unsigned char,short,int)",
|
||||||
|
result.getSignature(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGnuNativeProcessWithValidArguments() {
|
||||||
|
|
||||||
|
String demanglerName = GnuDemanglerOptions.GNU_DEMANGLER_DEFAULT;
|
||||||
|
String applicationArguments = "-s auto";
|
||||||
|
try {
|
||||||
|
GnuDemanglerNativeProcess.getDemanglerNativeProcess(demanglerName,
|
||||||
|
applicationArguments);
|
||||||
|
}
|
||||||
|
catch (IOException e) {
|
||||||
|
fail("Expected an exception when passing unknown arguments to the native demangler");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGnuNativeProcessWithUnknownArguments() {
|
||||||
|
|
||||||
|
String demanglerName = GnuDemanglerOptions.GNU_DEMANGLER_DEFAULT;
|
||||||
|
String applicationArguments = "-s MrBob";
|
||||||
|
try {
|
||||||
|
GnuDemanglerNativeProcess.getDemanglerNativeProcess(demanglerName,
|
||||||
|
applicationArguments);
|
||||||
|
fail("Expected an exception when passing unknown arguments to the native demangler");
|
||||||
|
}
|
||||||
|
catch (IOException e) {
|
||||||
|
// expected
|
||||||
|
Msg.error(this, "Test error", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
private Address addr(String address) {
|
private Address addr(String address) {
|
||||||
return program.getAddressFactory().getAddress(address);
|
return program.getAddressFactory().getAddress(address);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,65 @@
|
||||||
|
/* ###
|
||||||
|
* IP: GHIDRA
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ghidra.app.plugin.core.analysis;
|
||||||
|
|
||||||
|
import ghidra.app.util.demangler.*;
|
||||||
|
import ghidra.app.util.demangler.microsoft.MicrosoftDemangler;
|
||||||
|
import ghidra.app.util.importer.MessageLog;
|
||||||
|
import ghidra.framework.options.Options;
|
||||||
|
import ghidra.program.model.listing.Program;
|
||||||
|
|
||||||
|
public class MicrosoftDemanglerAnalyzer extends AbstractDemanglerAnalyzer {
|
||||||
|
|
||||||
|
private static final String NAME = "Demangler Microsoft";
|
||||||
|
private static final String DESCRIPTION =
|
||||||
|
"After a function is created, this analyzer will attempt to demangle " +
|
||||||
|
"the name and apply datatypes to parameters.";
|
||||||
|
|
||||||
|
private final static String OPTION_NAME_APPLY_SIGNATURE = "Apply Function Signatures";
|
||||||
|
private static final String OPTION_DESCRIPTION_APPLY_SIGNATURE =
|
||||||
|
"Apply any recovered function signature, in addition to the function name";
|
||||||
|
private boolean applyFunctionSignature = true;
|
||||||
|
private MicrosoftDemangler demangler = new MicrosoftDemangler();
|
||||||
|
|
||||||
|
public MicrosoftDemanglerAnalyzer() {
|
||||||
|
super(NAME, DESCRIPTION);
|
||||||
|
setDefaultEnablement(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canAnalyze(Program program) {
|
||||||
|
return demangler.canDemangle(program);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void registerOptions(Options options, Program program) {
|
||||||
|
options.registerOption(OPTION_NAME_APPLY_SIGNATURE, applyFunctionSignature, null,
|
||||||
|
OPTION_DESCRIPTION_APPLY_SIGNATURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void optionsChanged(Options options, Program program) {
|
||||||
|
applyFunctionSignature =
|
||||||
|
options.getBoolean(OPTION_NAME_APPLY_SIGNATURE, applyFunctionSignature);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected DemangledObject doDemangle(String mangled, DemanglerOptions options, MessageLog log)
|
||||||
|
throws DemangledException {
|
||||||
|
DemangledObject demangled = demangler.demangle(mangled, options);
|
||||||
|
return demangled;
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,8 +15,6 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.app.util.demangler.microsoft;
|
package ghidra.app.util.demangler.microsoft;
|
||||||
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
import ghidra.app.util.demangler.*;
|
import ghidra.app.util.demangler.*;
|
||||||
import ghidra.app.util.opinion.MSCoffLoader;
|
import ghidra.app.util.opinion.MSCoffLoader;
|
||||||
import ghidra.app.util.opinion.PeLoader;
|
import ghidra.app.util.opinion.PeLoader;
|
||||||
|
@ -30,15 +28,6 @@ import util.demangler.GenericDemangledException;
|
||||||
*/
|
*/
|
||||||
public class MicrosoftDemangler implements Demangler {
|
public class MicrosoftDemangler implements Demangler {
|
||||||
|
|
||||||
/**
|
|
||||||
* This represents an odd symbol that looks mangled, but we don't know what to do with. It
|
|
||||||
* is of the form:
|
|
||||||
* ?BobsStuffIO@344text__@@U_text@@?W
|
|
||||||
*
|
|
||||||
* where the last character is preceded by a special character, such as ?, *, -, etc
|
|
||||||
*/
|
|
||||||
private static Pattern INVALID_TRAILING_CHARS_PATTERN = Pattern.compile(".*@@[?*`%~+/-][A-Z]");
|
|
||||||
|
|
||||||
public MicrosoftDemangler() {
|
public MicrosoftDemangler() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,6 +50,19 @@ public class MicrosoftDemangler implements Demangler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DemangledObject demangle(String mangled, DemanglerOptions options)
|
||||||
|
throws DemangledException {
|
||||||
|
|
||||||
|
try {
|
||||||
|
DemangledObject demangled = demangleMS(mangled, options.demangleOnlyKnownPatterns());
|
||||||
|
return demangled;
|
||||||
|
}
|
||||||
|
catch (GenericDemangledException e) {
|
||||||
|
throw new DemangledException(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private DemangledObject demangleMS(String mangled, boolean demangleOnlyKnownPatterns)
|
private DemangledObject demangleMS(String mangled, boolean demangleOnlyKnownPatterns)
|
||||||
throws GenericDemangledException {
|
throws GenericDemangledException {
|
||||||
if (mangled == null || mangled.length() == 0) {
|
if (mangled == null || mangled.length() == 0) {
|
||||||
|
@ -69,7 +71,7 @@ public class MicrosoftDemangler implements Demangler {
|
||||||
|
|
||||||
MDMangGhidra demangler = new MDMangGhidra();
|
MDMangGhidra demangler = new MDMangGhidra();
|
||||||
try {
|
try {
|
||||||
demangler.demangle(mangled, demangleOnlyKnownPatterns); //not using return type here.
|
demangler.demangle(mangled, demangleOnlyKnownPatterns);
|
||||||
DemangledObject object = demangler.getObject();
|
DemangledObject object = demangler.getObject();
|
||||||
return object;
|
return object;
|
||||||
}
|
}
|
||||||
|
@ -81,6 +83,15 @@ public class MicrosoftDemangler implements Demangler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * This represents an odd symbol that looks mangled, but we don't know what to do with. It
|
||||||
|
// * is of the form:
|
||||||
|
// * ?BobsStuffIO@344text__@@U_text@@?W
|
||||||
|
// *
|
||||||
|
// * where the last character is preceded by a special character, such as ?, *, -, etc
|
||||||
|
// */
|
||||||
|
// private static Pattern INVALID_TRAILING_CHARS_PATTERN = Pattern.compile(".*@@[?*`%~+/-][A-Z]");
|
||||||
|
|
||||||
// private boolean isMangled(String mangled) {
|
// private boolean isMangled(String mangled) {
|
||||||
// int atpos = mangled.indexOf("@");
|
// int atpos = mangled.indexOf("@");
|
||||||
// boolean isMangled = mangled.charAt(0) == '?' && atpos != -1;
|
// boolean isMangled = mangled.charAt(0) == '?' && atpos != -1;
|
||||||
|
|
|
@ -82,7 +82,8 @@ class LoadPdbTask extends Task {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
AutoAnalysisManager.getAnalysisManager(program).scheduleWorker(worker, null, true,
|
AutoAnalysisManager.getAnalysisManager(program)
|
||||||
|
.scheduleWorker(worker, null, true,
|
||||||
monitor);
|
monitor);
|
||||||
}
|
}
|
||||||
catch (InterruptedException | CancelledException e1) {
|
catch (InterruptedException | CancelledException e1) {
|
||||||
|
@ -117,7 +118,7 @@ class LoadPdbTask extends Task {
|
||||||
|
|
||||||
private void analyzeSymbols(TaskMonitor monitor, MessageLog log) {
|
private void analyzeSymbols(TaskMonitor monitor, MessageLog log) {
|
||||||
|
|
||||||
DemanglerAnalyzer demanglerAnalyzer = new DemanglerAnalyzer();
|
MicrosoftDemanglerAnalyzer demanglerAnalyzer = new MicrosoftDemanglerAnalyzer();
|
||||||
String analyzerName = demanglerAnalyzer.getName();
|
String analyzerName = demanglerAnalyzer.getName();
|
||||||
|
|
||||||
Options analysisProperties = program.getOptions(Program.ANALYSIS_PROPERTIES);
|
Options analysisProperties = program.getOptions(Program.ANALYSIS_PROPERTIES);
|
||||||
|
|
|
@ -15,8 +15,6 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.framework.options;
|
package ghidra.framework.options;
|
||||||
|
|
||||||
import ghidra.util.HelpLocation;
|
|
||||||
|
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
import java.awt.Font;
|
import java.awt.Font;
|
||||||
import java.beans.PropertyEditor;
|
import java.beans.PropertyEditor;
|
||||||
|
@ -26,6 +24,8 @@ import java.util.List;
|
||||||
|
|
||||||
import javax.swing.KeyStroke;
|
import javax.swing.KeyStroke;
|
||||||
|
|
||||||
|
import ghidra.util.HelpLocation;
|
||||||
|
|
||||||
public interface Options {
|
public interface Options {
|
||||||
public static final char DELIMITER = '.';
|
public static final char DELIMITER = '.';
|
||||||
public final static String DELIMITER_STRING = new String(new char[] { DELIMITER });
|
public final static String DELIMITER_STRING = new String(new char[] { DELIMITER });
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue