Added GnuDemanglerFormat, changed analyzer options

All available demangler formats have been added to GnuDemanglerFormat.
The options in GnuDemanglerAnalyzer now only reflect the available
formats to remove any user error when specifying a format. This also
prevents a format from being used on a demangler which doesn't support
it.
This commit is contained in:
astrelsky 2020-08-19 14:27:57 -04:00 committed by dragonmacher
parent ce78b860be
commit 0fcab24073
9 changed files with 454 additions and 365 deletions

View File

@ -388,32 +388,15 @@
that Ghidra contains (<B>version 2.33.1</B> as of this writing). Turning this that Ghidra contains (<B>version 2.33.1</B> as of this writing). Turning this
option on will also invoke the now deprecated previous version of the demangler option on will also invoke the now deprecated previous version of the demangler
(<B>version 2.24</B>) if the preferred demangler cannot demangle a given symbol. (<B>version 2.24</B>) if the preferred demangler cannot demangle a given symbol.
This option only has an effect when the demangler format is available in both
the deprecated and modern demanglers.
</P> </P>
<P> <P>
Support for older demangling styles was removed in <CODE>c++filt (v2.32)</CODE>. Support for older demangling styles was removed in <CODE>c++filt (v2.32)</CODE>.
Specifically, the following formats are no longer supported: Specifically, the following formats are no longer supported:
<CODE>Lucid, ARM, HP, and EDG</CODE>. To use these formats, you must enable <CODE>Lucid, ARM, HP, GNU, and EDG</CODE>. For these formats, the deprecated
usage of the deprecated demangler, which is <B>version 2.24</B>. Further, you demangler, which is <B>version 2.24</B>, will automatically be used.
may have to pass the required external demangler options using the Ghidra
option below.
</P> </P>
<P>
<U><B>Use External Demangler Options</B></U> -
This allows users to pass settings to the demangler. As an example, you can enter
in this Ghidra option text field the following text to use the <CODE>rust</CODE>
format: <CODE>-s rust</CODE>. This is not needed for
normal operation. To see a full list of supported options, query each demangler
directly using the <CODE>--help</CODE> switch.
</P>
<P>
The GNU demanglers can be found at:
<CODE CLASS="path">
&lt;GHIDRA_INSTALL_DIR&gt;/GPL/DemanglerGnu/build/os/&lt;OS&gt;/
</CODE><BR>
</P>
<BLOCKQUOTE> <BLOCKQUOTE>
<P> <P>

View File

@ -15,10 +15,6 @@
*/ */
package ghidra.app.plugin.core.analysis; 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.*;
import ghidra.app.util.demangler.gnu.*; import ghidra.app.util.demangler.gnu.*;
import ghidra.app.util.importer.MessageLog; import ghidra.app.util.importer.MessageLog;
@ -51,15 +47,14 @@ public class GnuDemanglerAnalyzer extends AbstractDemanglerAnalyzer {
"Signals to use the deprecated demangler when the modern demangler cannot demangle a " + "Signals to use the deprecated demangler when the modern demangler cannot demangle a " +
"given string"; "given string";
static final String OPTION_NAME_DEMANGLER_PARAMETERS = static final String OPTION_NAME_DEMANGLER_FORMAT = "Demangler Format";
"Use External Demangler Options"; private static final String OPTION_DESCRIPTION_DEMANGLER_FORMAT =
private static final String OPTION_DESCRIPTION_DEMANGLER_PARAMETERS = "The demangling format to use";
"Signals to use pass the given parameters to the demangler program";
private boolean doSignatureEnabled = true; private boolean doSignatureEnabled = true;
private boolean demangleOnlyKnownPatterns = false; private boolean demangleOnlyKnownPatterns = false;
private GnuDemanglerFormat demanglerFormat = GnuDemanglerFormat.AUTO;
private boolean useDeprecatedDemangler = false; private boolean useDeprecatedDemangler = false;
private String demanglerParameters = "";
private GnuDemangler demangler = new GnuDemangler(); private GnuDemangler demangler = new GnuDemangler();
@ -84,11 +79,11 @@ public class GnuDemanglerAnalyzer extends AbstractDemanglerAnalyzer {
help, help,
OPTION_DESCRIPTION_USE_KNOWN_PATTERNS); OPTION_DESCRIPTION_USE_KNOWN_PATTERNS);
options.registerOption(OPTION_NAME_DEMANGLER_FORMAT, demanglerFormat, help,
OPTION_DESCRIPTION_DEMANGLER_FORMAT);
options.registerOption(OPTION_NAME_USE_DEPRECATED_DEMANGLER, useDeprecatedDemangler, help, options.registerOption(OPTION_NAME_USE_DEPRECATED_DEMANGLER, useDeprecatedDemangler, help,
OPTION_DESCRIPTION_DEPRECATED_DEMANGLER); OPTION_DESCRIPTION_DEPRECATED_DEMANGLER);
options.registerOption(OPTION_NAME_DEMANGLER_PARAMETERS, demanglerParameters, help,
OPTION_DESCRIPTION_DEMANGLER_PARAMETERS);
} }
@Override @Override
@ -96,94 +91,28 @@ public class GnuDemanglerAnalyzer extends AbstractDemanglerAnalyzer {
doSignatureEnabled = options.getBoolean(OPTION_NAME_APPLY_SIGNATURE, doSignatureEnabled); doSignatureEnabled = options.getBoolean(OPTION_NAME_APPLY_SIGNATURE, doSignatureEnabled);
demangleOnlyKnownPatterns = demangleOnlyKnownPatterns =
options.getBoolean(OPTION_NAME_DEMANGLE_USE_KNOWN_PATTERNS, demangleOnlyKnownPatterns); options.getBoolean(OPTION_NAME_DEMANGLE_USE_KNOWN_PATTERNS, demangleOnlyKnownPatterns);
demanglerFormat = options.getEnum(OPTION_NAME_DEMANGLER_FORMAT, GnuDemanglerFormat.AUTO);
useDeprecatedDemangler = if (demanglerFormat.isDeprecatedFormat() && demanglerFormat.isModernFormat()) {
options.getBoolean(OPTION_NAME_USE_DEPRECATED_DEMANGLER, useDeprecatedDemangler); useDeprecatedDemangler =
options.getBoolean(OPTION_NAME_USE_DEPRECATED_DEMANGLER, useDeprecatedDemangler);
demanglerParameters = } else {
options.getString(OPTION_NAME_DEMANGLER_PARAMETERS, demanglerParameters); useDeprecatedDemangler = demanglerFormat.isDeprecatedFormat();
}
} }
@Override @Override
protected DemanglerOptions getOptions() { protected DemanglerOptions getOptions() {
GnuDemanglerOptions options = new GnuDemanglerOptions(); GnuDemanglerOptions options = new GnuDemanglerOptions(demanglerFormat, useDeprecatedDemangler);
options.setDoDisassembly(true); options.setDoDisassembly(true);
options.setApplySignature(doSignatureEnabled); options.setApplySignature(doSignatureEnabled);
options.setDemangleOnlyKnownPatterns(demangleOnlyKnownPatterns); options.setDemangleOnlyKnownPatterns(demangleOnlyKnownPatterns);
options.setDemanglerApplicationArguments(demanglerParameters);
return options; 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.appendMsg(getName(), "Invalid options for GNU dangler '" + demanglerName +
"': " + applicationArguments);
log.appendException(e);
}
if (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.appendMsg(getName(),
"Invalid options for GNU dangler '" + deprecatedName + "': " +
applicationArguments);
log.appendException(e);
}
}
return false;
}
@Override @Override
protected DemangledObject doDemangle(String mangled, DemanglerOptions demanglerOtions, protected DemangledObject doDemangle(String mangled, DemanglerOptions demanglerOtions,
MessageLog log) MessageLog log) throws DemangledException {
throws DemangledException { return demangler.demangle(mangled, (GnuDemanglerOptions) demanglerOtions);
GnuDemanglerOptions options = (GnuDemanglerOptions) demanglerOtions;
DemangledObject demangled = null;
try {
demangled = demangler.demangle(mangled, options);
}
catch (DemangledException e) {
if (!useDeprecatedDemangler) {
throw e; // let our parent handle this
}
}
if (demangled != null) {
return demangled;
}
if (useDeprecatedDemangler) {
GnuDemanglerOptions newOptions = options.withDeprecatedDemangler();
demangled = demangler.demangle(mangled, newOptions);
}
return demangled;
} }
} }

View File

@ -72,11 +72,11 @@ public class GnuDemangler implements Demangler {
public DemangledObject demangle(String mangled, DemanglerOptions demanglerOtions) public DemangledObject demangle(String mangled, DemanglerOptions demanglerOtions)
throws DemangledException { throws DemangledException {
if (skip(mangled, demanglerOtions)) { GnuDemanglerOptions options = getGnuOptions(demanglerOtions);
if (skip(mangled, options)) {
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)) {
@ -163,7 +163,7 @@ public class GnuDemangler implements Demangler {
applicationOptions); applicationOptions);
} }
private boolean skip(String mangled, DemanglerOptions options) { private boolean skip(String mangled, GnuDemanglerOptions 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
@ -179,21 +179,28 @@ public class GnuDemangler implements Demangler {
return false; // let it go through return false; // let it go through
} }
// add to this list if we find any other known GNU start patterns // TODO provide some checks specific to the other formats
if (mangled.startsWith("_Z")) { GnuDemanglerFormat format = options.getDemanglerFormat();
if (format == GnuDemanglerFormat.AUTO) {
return false; return false;
} }
else if (mangled.startsWith("__Z")) { if (format == GnuDemanglerFormat.GNUV3) {
return false; // add to this list if we find any other known GNU start patterns
} if (mangled.startsWith("_Z")) {
else if (mangled.startsWith("h__")) { return false;
return false; // not sure about this one }
} if (mangled.startsWith("__Z")) {
else if (mangled.startsWith("?")) { return false;
return false; // not sure about this one }
} if (mangled.startsWith("h__")) {
else if (isGnu2Or3Pattern(mangled)) { return false; // not sure about this one
return false; }
if (mangled.startsWith("?")) {
return false; // not sure about this one
}
if (isGnu2Or3Pattern(mangled)) {
return false;
}
} }
return true; return true;

View File

@ -0,0 +1,80 @@
/* ###
* 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;
/**
* Enum representation of the available GnuDemangler formats
*/
public enum GnuDemanglerFormat {
// OLD: none,auto,gnu,lucid,arm,hp,edg,gnu-v3,java,gnat
// NEW: none,auto,gnu-v3,java,gnat,dlang,rust
/** Automatic mangling format detection */
AUTO("", 0),
/** GNUv2 mangling format */
GNU("gnu", -1),
/** lucid mangling format */
LUCID("lucid", -1),
/** arm mangling format */
ARM("arm", -1),
/** hp mangling format */
HP("hp", -1),
/** mangling format used by the Edison Design Group (EDG) compiler */
EDG("edg", -1),
/** GNUv3 mangling format */
GNUV3("gnu-v3", 0),
/** Java mangling format */
JAVA("java", 0),
/** GNAT Ada compiler mangling format */
GNAT("gnat", 0),
/** D mangling format */
DLANG("dlang", 1),
/** Rust mangling format */
RUST("rust", 1);
/** the format option string */
private final String format;
/** private sentinal. deprecated = -1, both = 0, new = 1 */
private final byte version;
private GnuDemanglerFormat(String format, int version) {
this.format = format;
this.version = (byte) version;
}
/**
* Checks if this format is available in the deprecated gnu demangler
* @return true if this format is available in the deprecated gnu demangler
*/
public boolean isDeprecatedFormat() {
return version <= 0;
}
/**
* Checks if this format is available in a modern version of the gnu demangler
* @return true if this format is available in a modern version of the gnu demangler.
*/
public boolean isModernFormat() {
return version >= 0;
}
/**
* Gets the format option to be passed to the demangler via the <code>-s</code> option
* @return the format option to be passed to the demangler
*/
public String getFormat() {
return format;
}
}

View File

@ -43,11 +43,27 @@ public class GnuDemanglerOptions extends DemanglerOptions {
*/ */
public static final String GNU_DEMANGLER_DEFAULT = GNU_DEMANGLER_V2_33_1; public static final String GNU_DEMANGLER_DEFAULT = GNU_DEMANGLER_V2_33_1;
private String demanglerName = GNU_DEMANGLER_DEFAULT; private final GnuDemanglerFormat format;
private String demanglerApplicationArguments; private final boolean isDeprecated;
public GnuDemanglerOptions() { public GnuDemanglerOptions() {
// use default values // use default values
this(GnuDemanglerFormat.AUTO);
}
public GnuDemanglerOptions(GnuDemanglerFormat format) {
this.format = format;
// default to the "new" demangler if the format is available in both
this.isDeprecated = !format.isModernFormat();
}
public GnuDemanglerOptions(GnuDemanglerFormat format, boolean deprecated) {
this.format = format;
this.isDeprecated = deprecated;
if (!isValidFormat(format, deprecated)) {
throw new IllegalArgumentException(
format.name() + " is not available in the "+getDemanglerName());
}
} }
public GnuDemanglerOptions(DemanglerOptions copy) { public GnuDemanglerOptions(DemanglerOptions copy) {
@ -55,26 +71,59 @@ public class GnuDemanglerOptions extends DemanglerOptions {
if (copy instanceof GnuDemanglerOptions) { if (copy instanceof GnuDemanglerOptions) {
GnuDemanglerOptions gCopy = (GnuDemanglerOptions) copy; GnuDemanglerOptions gCopy = (GnuDemanglerOptions) copy;
demanglerName = gCopy.demanglerName; format = gCopy.format;
demanglerApplicationArguments = gCopy.demanglerApplicationArguments; isDeprecated = gCopy.isDeprecated;
} else {
format = GnuDemanglerFormat.AUTO;
isDeprecated = false;
} }
} }
private GnuDemanglerOptions(GnuDemanglerOptions copy, GnuDemanglerFormat format,
boolean deprecated) {
super(copy);
this.format = format;
this.isDeprecated = deprecated;
}
/** /**
* Returns the external demangler executable name to be used for demangling. The * Returns the external demangler executable name to be used for demangling. The
* default value is {@link #GNU_DEMANGLER_DEFAULT}. * default value is {@link #GNU_DEMANGLER_DEFAULT}.
* @return the name * @return the name
*/ */
public String getDemanglerName() { public String getDemanglerName() {
return demanglerName; return isDeprecated ? GNU_DEMANGLER_V2_24 : GNU_DEMANGLER_V2_33_1;
} }
/** /**
* Sets the external demangler executable name to be used for demangling * A convenience method to copy the state of this options object, changing the
* @param name the name * demangler executable name and demangler format to the specified values.
* @param format the demangling format to use
* @param isDeprecated true to use the deprecated gnu demangler, else false
* @return the new options
* @throws IllegalArgumentException if the current format is not available in the
* selected demangler.
*/ */
public void setDemanglerName(String name) { public GnuDemanglerOptions withDemanglerFormat(GnuDemanglerFormat format, boolean isDeprecated)
this.demanglerName = name; throws IllegalArgumentException {
if (this.format == format && this.isDeprecated == isDeprecated) {
return this;
}
if (isValidFormat(format, isDeprecated)) {
return new GnuDemanglerOptions(this, format, isDeprecated);
}
throw new IllegalArgumentException(
format.name() + " is not available in the "+getDemanglerName());
}
private static boolean isValidFormat(GnuDemanglerFormat format, boolean isDeprecated) {
if (isDeprecated && format.isDeprecatedFormat()) {
return true;
}
if (!isDeprecated && format.isModernFormat()) {
return true;
}
return false;
} }
/** /**
@ -82,26 +131,19 @@ public class GnuDemanglerOptions extends DemanglerOptions {
* @return the arguments * @return the arguments
*/ */
public String getDemanglerApplicationArguments() { public String getDemanglerApplicationArguments() {
return demanglerApplicationArguments; if (format == GnuDemanglerFormat.AUTO) {
// no format argument
return "";
}
return "-s " + format.getFormat();
} }
/** /**
* Sets the arguments to be passed to the external demangler executable * Gets the current demangler format
* @param args the arguments * @return the demangler format
*/ */
public void setDemanglerApplicationArguments(String args) { public GnuDemanglerFormat getDemanglerFormat() {
this.demanglerApplicationArguments = args; return format;
}
/**
* A convenience method to copy the state of this options object, changing the
* demangler executable name to the deprecated demangler
* @return the new options
*/
public GnuDemanglerOptions withDeprecatedDemangler() {
GnuDemanglerOptions newOptions = new GnuDemanglerOptions(this);
newOptions.setDemanglerName(GNU_DEMANGLER_V2_24);
return newOptions;
} }
@Override @Override
@ -111,8 +153,8 @@ public class GnuDemanglerOptions extends DemanglerOptions {
"\tdoDisassembly: " + doDisassembly() + ",\n" + "\tdoDisassembly: " + doDisassembly() + ",\n" +
"\tapplySignature: " + applySignature() + ",\n" + "\tapplySignature: " + applySignature() + ",\n" +
"\tdemangleOnlyKnownPatterns: " + demangleOnlyKnownPatterns() + ",\n" + "\tdemangleOnlyKnownPatterns: " + demangleOnlyKnownPatterns() + ",\n" +
"\tdemanglerName: " + demanglerName + ",\n" + "\tdemanglerName: " + getDemanglerName() + ",\n" +
"\tdemanglerApplicationArguments: " + demanglerApplicationArguments + ",\n" + "\tdemanglerApplicationArguments: " + getDemanglerApplicationArguments() + ",\n" +
"}"; "}";
//@formatter:on //@formatter:on
} }

View File

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -43,12 +43,8 @@ public class GnuDemanglerParser {
private static final String TYPEINFO_FOR = "typeinfo for "; private static final String TYPEINFO_FOR = "typeinfo for ";
private static final String COVARIANT_RETURN_THUNK = "covariant return thunk"; private static final String COVARIANT_RETURN_THUNK = "covariant return thunk";
private static final Set<String> ADDRESS_TABLE_PREFIXES = Set.of( private static final Set<String> ADDRESS_TABLE_PREFIXES =
CONSTRUCTION_VTABLE_FOR, Set.of(CONSTRUCTION_VTABLE_FOR, VTT_FOR, VTABLE_FOR, TYPEINFO_FN_FOR, TYPEINFO_FOR);
VTT_FOR,
VTABLE_FOR,
TYPEINFO_FN_FOR,
TYPEINFO_FOR);
private static final String OPERATOR = "operator"; private static final String OPERATOR = "operator";
private static final String LAMBDA = "lambda"; private static final String LAMBDA = "lambda";
@ -64,23 +60,23 @@ public class GnuDemanglerParser {
* *
* Pattern: name(([const] [params])) * Pattern: name(([const] [params]))
* *
* Parts: -optional spaces * Parts: -optional spaces
* -optional (const) (non-capture group) * -optional (const) (non-capture group)
* -followed by '()' with optional parameter text (capture group 1) * -followed by '()' with optional parameter text (capture group 1)
* *
* Note: this pattern is used for matching the arguments string, in the above examples it * Note: this pattern is used for matching the arguments string, in the above examples it
* would be: * would be:
* Rect &, unsigned long * Rect &, unsigned long
* and * and
* Rect &, bool * Rect &, bool
* *
*/ */
private static final Pattern UNNECESSARY_PARENS_PATTERN = private static final Pattern UNNECESSARY_PARENS_PATTERN =
Pattern.compile("\\s*(?:const){0,1}\\((.*)\\)\\s*"); Pattern.compile("\\s*(?:const){0,1}\\((.*)\\)\\s*");
/** /**
* Captures the contents of a varargs parameter that is inside of parentheses. * Captures the contents of a varargs parameter that is inside of parentheses.
* *
* Sample: (NS1::type&&)... * Sample: (NS1::type&&)...
* *
* Pattern: (namespace::name[modifiers])... * Pattern: (namespace::name[modifiers])...
@ -89,7 +85,7 @@ public class GnuDemanglerParser {
* -contents (capture group 1) * -contents (capture group 1)
* -close paren * -close paren
* -varargs * -varargs
* *
*/ */
private static final Pattern VARARGS_IN_PARENS = private static final Pattern VARARGS_IN_PARENS =
Pattern.compile("\\((.*)\\)" + Pattern.quote("...")); Pattern.compile("\\((.*)\\)" + Pattern.quote("..."));
@ -107,7 +103,7 @@ public class GnuDemanglerParser {
* -*optional: any other text (e.g., const[8]) (non-capture group) * -*optional: any other text (e.g., const[8]) (non-capture group)
* -followed by '()' that contain a '&' or a '*' (capture group 2) * -followed by '()' that contain a '&' or a '*' (capture group 2)
* -followed by one or more '[]' with optional interior text (capture group 3) * -followed by one or more '[]' with optional interior text (capture group 3)
* *
* Group Samples: * Group Samples:
* short (&)[7] * short (&)[7]
* 1 short * 1 short
@ -118,7 +114,7 @@ public class GnuDemanglerParser {
* 1 CanRxItem * 1 CanRxItem
* 2 & * 2 &
* 3 [2][64u] * 3 [2][64u]
* *
*/ */
private static final Pattern ARRAY_POINTER_REFERENCE_PATTERN = private static final Pattern ARRAY_POINTER_REFERENCE_PATTERN =
Pattern.compile("([\\w:]+)\\*?\\s(?:.*)\\(([&*])\\)\\s*((?:\\[.*?\\])+)"); Pattern.compile("([\\w:]+)\\*?\\s(?:.*)\\(([&*])\\)\\s*((?:\\[.*?\\])+)");
@ -128,8 +124,8 @@ public class GnuDemanglerParser {
* *
* Pattern: (*|&)[optional spaces][optional value] * Pattern: (*|&)[optional spaces][optional value]
* *
* Parts: * Parts:
* -'()' that contain a '&' or a '*' * -'()' that contain a '&' or a '*'
* -followed by '[]' with optional text * -followed by '[]' with optional text
* </pre> * </pre>
*/ */
@ -159,7 +155,7 @@ public class GnuDemanglerParser {
* 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&)
* bool myContainer<int>::operator<< <double>(double) * bool myContainer<int>::operator<< <double>(double)
* bool operator< <myContainer<int> >(myContainer<int> const&) * bool operator< <myContainer<int> >(myContainer<int> const&)
* *
* Pattern: [return_type] operator operator_character(s) (opeartor_params) [trailing text] * Pattern: [return_type] operator operator_character(s) (opeartor_params) [trailing text]
* *
* Parts: * Parts:
@ -168,7 +164,7 @@ public class GnuDemanglerParser {
* -optional space * -optional space
* -optional templates (capture group 3) * -optional templates (capture group 3)
* -parameters (capture group 4) * -parameters (capture group 4)
* *
* Note: this regex is generated from all known operator patterns and looks like: * Note: this regex is generated from all known operator patterns and looks like:
* (.*operator(generated_text).*)\s*(\(.*\))(.*) * (.*operator(generated_text).*)\s*(\(.*\))(.*)
*/ */
@ -178,9 +174,9 @@ public class GnuDemanglerParser {
/* /*
* Sample: std::integral_constant<bool, false>::operator bool() const * Sample: std::integral_constant<bool, false>::operator bool() const
* Magick::Color::operator std::basic_string<char, std::char_traits<char>, std::allocator<char> >() const * Magick::Color::operator std::basic_string<char, std::char_traits<char>, std::allocator<char> >() const
* *
* Pattern: operator type() [trailing text] * Pattern: operator type() [trailing text]
* *
* Parts: * Parts:
* -operator (capture group 1) * -operator (capture group 1)
* -space * -space
@ -211,13 +207,13 @@ public class GnuDemanglerParser {
/* /*
* Pattern for newer C++ lambda syntax: * Pattern for newer C++ lambda syntax:
* *
* Sample: {lambda(void const*, unsigned int)#1} * Sample: {lambda(void const*, unsigned int)#1}
* {lambda(NS1::Class1 const&, int, int)#1} const& * {lambda(NS1::Class1 const&, int, int)#1} const&
* {lambda(auto:1&&)#1}<NS1::NS2>&& * {lambda(auto:1&&)#1}<NS1::NS2>&&
* *
* Pattern: [optional text] brace lambda([parameters])#digits brace [trailing text] * Pattern: [optional text] brace lambda([parameters])#digits brace [trailing text]
* *
* Parts: * Parts:
* -full text without leading characters (capture group 1) * -full text without leading characters (capture group 1)
* -parameters of the lambda function (capture group 2) * -parameters of the lambda function (capture group 2)
@ -229,9 +225,9 @@ public class GnuDemanglerParser {
/* /*
* Sample: {unnamed type#1} * Sample: {unnamed type#1}
* *
* Pattern: [optional text] brace unnamed type#digits brace * Pattern: [optional text] brace unnamed type#digits brace
* *
* Parts: * Parts:
* -full text without leading characters (capture group 1) * -full text without leading characters (capture group 1)
*/ */
@ -239,18 +235,18 @@ public class GnuDemanglerParser {
/* /*
* Sample: covariant return thunk to Foo::Bar::copy(Foo::CoolStructure*) const * Sample: covariant return thunk to Foo::Bar::copy(Foo::CoolStructure*) const
* *
* Pattern: text for|to text * Pattern: text for|to text
* *
* Parts: * Parts:
* -required text (capture group 2) * -required text (capture group 2)
* -a space * -a space
* -'for' or 'to' (capture group 3) * -'for' or 'to' (capture group 3)
* -a space * -a space
* -optional text (capture group 4) * -optional text (capture group 4)
* *
* Note: capture group 1 is the combination of groups 2 and 3 * Note: capture group 1 is the combination of groups 2 and 3
* *
* Examples: * Examples:
* construction vtable for * construction vtable for
* vtable for * vtable for
@ -258,33 +254,33 @@ public class GnuDemanglerParser {
* typeinfo for * typeinfo for
* guard variable for * guard variable for
* covariant return thunk to * covariant return thunk to
* virtual thunk to * virtual thunk to
* non-virtual thunk to * non-virtual thunk to
*/ */
private static final Pattern DESCRIPTIVE_PREFIX_PATTERN = private static final Pattern DESCRIPTIVE_PREFIX_PATTERN =
Pattern.compile("((.+ )+(for|to) )(.+)"); Pattern.compile("((.+ )+(for|to) )(.+)");
/** /**
* The c 'decltype' keyword pattern * The c 'decltype' keyword pattern
*/ */
private static final Pattern DECLTYPE_RETURN_TYPE_PATTERN = private static final Pattern DECLTYPE_RETURN_TYPE_PATTERN =
Pattern.compile("decltype \\(.*\\)"); Pattern.compile("decltype \\(.*\\)");
private static Pattern createOverloadedOperatorNamePattern() { private static Pattern createOverloadedOperatorNamePattern() {
// note: the order of these matters--the single characters must come after the // note: the order of these matters--the single characters must come after the
// multi-character entries; otherwise, the single characters will match before // multi-character entries; otherwise, the single characters will match before
// longer matches // longer matches
//@formatter:off //@formatter:off
List<String> operators = new LinkedList<>(List.of( List<String> operators = new LinkedList<>(List.of(
"++", "--", "++", "--",
">>=", "<<=", ">>=", "<<=",
"->*", "->", "->*", "->",
"==", "!=", ">=", "<=", "==", "!=", ">=", "<=",
"&&", "||", ">>", "<<", "&&", "||", ">>", "<<",
"+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=",
"+", "-", "*", "/", "%", "+", "-", "*", "/", "%",
"~", "^", "&", "|", "!", "<", ">", "=", "~", "^", "&", "|", "!", "<", ">", "=",
",", "()" ",", "()"
)); ));
//@formatter:on //@formatter:on
@ -294,12 +290,12 @@ public class GnuDemanglerParser {
// //
// We have some extra 'operator' style constructs to add to the normal operator overloading // We have some extra 'operator' style constructs to add to the normal operator overloading
// //
// User Defined Literal // User Defined Literal
// Sample: operator"" _init(char const*, unsigned long) // Sample: operator"" _init(char const*, unsigned long)
// //
// Pattern: operator"" _someText(opeartor_params) // Pattern: operator"" _someText(opeartor_params)
// //
String userDefinedLiteral = "\"\"\\s_.+"; String userDefinedLiteral = "\"\"\\s_.+";
String extra = userDefinedLiteral; String extra = userDefinedLiteral;
alternated += '|' + extra; alternated += '|' + extra;
@ -319,7 +315,7 @@ public class GnuDemanglerParser {
/** /**
* Parses the given demangled string and creates a {@link DemangledObject} * Parses the given demangled string and creates a {@link DemangledObject}
* *
* @param mangled the original mangled text * @param mangled the original mangled text
* @param demangled the demangled text * @param demangled the demangled text
* @return the demangled object * @return the demangled object
@ -341,9 +337,9 @@ public class GnuDemanglerParser {
private DemangledObjectBuilder getSpecializedBuilder(String demangled) { private DemangledObjectBuilder getSpecializedBuilder(String demangled) {
// //
// Note: we check for the 'special handlers' first, since they are more specific than // Note: we check for the 'special handlers' first, since they are more specific than
// the other handlers here. Checking for the operator handler first can produce // the other handlers here. Checking for the operator handler first can produce
// errors, since some 'special handler' strings actually contain 'operator' // errors, since some 'special handler' strings actually contain 'operator'
// signatures. In those cases, the operator handler will incorrectly match on the // signatures. In those cases, the operator handler will incorrectly match on the
// operator text. Since the 'special handlers' perform more specific checks, it is // operator text. Since the 'special handlers' perform more specific checks, it is
// safe to do those first. // safe to do those first.
@ -464,7 +460,7 @@ public class GnuDemanglerParser {
if (DECLTYPE_RETURN_TYPE_PATTERN.matcher(returnType).matches()) { if (DECLTYPE_RETURN_TYPE_PATTERN.matcher(returnType).matches()) {
// Not sure yet if there is any information we wish to recover from this pattern. // Not sure yet if there is any information we wish to recover from this pattern.
// Sample: decltype (functionName({parm#1}, (float)[42c80000])) // Sample: decltype (functionName({parm#1}, (float)[42c80000]))
return; return;
} }
@ -523,10 +519,10 @@ public class GnuDemanglerParser {
} }
/** /**
* Removes spaces from unwanted places. For example, all spaces internal to templates and * Removes spaces from unwanted places. For example, all spaces internal to templates and
* parameter lists will be removed. Also, other special cases may be handled, such as when * parameter lists will be removed. Also, other special cases may be handled, such as when
* the 'unnamed type' construct is found. * the 'unnamed type' construct is found.
* *
* @param text the text to fix * @param text the text to fix
* @return the fixed text * @return the fixed text
*/ */
@ -735,7 +731,7 @@ public class GnuDemanglerParser {
// lambda function // lambda function
// e.g., {lambda(NS1::Class1 const&, int, int)#1} const& // e.g., {lambda(NS1::Class1 const&, int, int)#1} const&
// {lambda(auto:1&&)#1}<NS1::NS2>>&& // {lambda(auto:1&&)#1}<NS1::NS2>>&&
// //
LambdaName lambdaName = getLambdaName(datatype); LambdaName lambdaName = getLambdaName(datatype);
@ -898,30 +894,30 @@ public class GnuDemanglerParser {
private boolean isDataTypeNameCharacter(char ch) { private boolean isDataTypeNameCharacter(char ch) {
/* /*
Note: really, this should just be checking a list of known disallowed characters, Note: really, this should just be checking a list of known disallowed characters,
which is something like: which is something like:
<,>,(,),&,*,[,] <,>,(,),&,*,[,]
It seems like the current code below is unnecessarily restrictive It seems like the current code below is unnecessarily restrictive
*/ */
//@formatter:off //@formatter:off
return Character.isLetter(ch) || return Character.isLetter(ch) ||
Character.isDigit(ch) || Character.isDigit(ch) ||
ch == ':' || ch == ':' ||
ch == '_' || ch == '_' ||
ch == '$'; ch == '$';
//@formatter:on //@formatter:on
} }
/** /**
* Scans the given string from the given offset looking for a balanced {@code close} * Scans the given string from the given offset looking for a balanced {@code close}
* character. This algorithm will not report a match for the end character until the * character. This algorithm will not report a match for the end character until the
* {@code open} character has first been found. This allows clients to scan from anywhere * {@code open} character has first been found. This allows clients to scan from anywhere
* in a string to find an open and start character combination, including at or before the * in a string to find an open and start character combination, including at or before the
* desired opening character. * desired opening character.
* *
* @param string the input string * @param string the input string
* @param start the start position within the string * @param start the start position within the string
* @param open the open character (e.g, '(' or '<') * @param open the open character (e.g, '(' or '<')
@ -952,12 +948,12 @@ public class GnuDemanglerParser {
} }
/** /**
* Scans the given string from the given offset looking for a balanced {@code open} * Scans the given string from the given offset looking for a balanced {@code open}
* character. This algorithm will not report a match for the open character until the * character. This algorithm will not report a match for the open character until the
* {@code end} character has first been found. This allows clients to scan from anywhere * {@code end} character has first been found. This allows clients to scan from anywhere
* in a string to find an open and start character combination, including at or before the * in a string to find an open and start character combination, including at or before the
* desired opening character. * desired opening character.
* *
* @param string the input string * @param string the input string
* @param start the start position within the string * @param start the start position within the string
* @param open the open character (e.g, '(' or '<') * @param open the open character (e.g, '(' or '<')
@ -998,11 +994,11 @@ public class GnuDemanglerParser {
/** /**
* Walks backward from the given start position to find the next namespace separator. This * Walks backward from the given start position to find the next namespace separator. This
* allows clients to determine if a given position is inside of a namespace. * allows clients to determine if a given position is inside of a namespace.
* *
* @param text the text to search * @param text the text to search
* @param start the start position * @param start the start position
* @param stop the stop position * @param stop the stop position
* @return the start index of the namespace entry containing the current {@code start} * @return the start index of the namespace entry containing the current {@code start}
* index; -1 if no namespace start is found * index; -1 if no namespace start is found
*/ */
private int findNamespaceStart(String text, int start, int stop) { private int findNamespaceStart(String text, int start, int stop) {
@ -1191,9 +1187,9 @@ public class GnuDemanglerParser {
/* /*
Examples: Examples:
NS1::Function<>()::StructureName::StructureConstructor() NS1::Function<>()::StructureName::StructureConstructor()
*/ */
String nameString = removeBadSpaces(demangled).trim(); String nameString = removeBadSpaces(demangled).trim();
@ -1208,10 +1204,10 @@ public class GnuDemanglerParser {
* 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.
* *
* <p>This method will also escape spaces separators inside of templates * <p>This method will also escape spaces separators inside of templates
* (see {@link #removeBadSpaces(String)}). * (see {@link #removeBadSpaces(String)}).
* *
* @param names the names to convert * @param names the names to convert
* @return the newly created type * @return the newly created type
*/ */
@ -1374,15 +1370,15 @@ public class GnuDemanglerParser {
/* /*
Samples: Samples:
prefix: construction vtable for prefix: construction vtable for
name: construction-vtable name: construction-vtable
prefix: vtable for prefix: vtable for
name: vtable name: vtable
prefix: typeinfo name for prefix: typeinfo name for
name: typeinfo-name name: typeinfo-name
prefix: covariant return thunk prefix: covariant return thunk
name: covariant-return name: covariant-return
*/ */
@ -1439,9 +1435,9 @@ public class GnuDemanglerParser {
int end = matcher.end(2); // operator chars start int end = matcher.end(2); // operator chars start
// //
// The 'operator' functions have symbols that confuse our default function parsing. // The 'operator' functions have symbols that confuse our default function parsing.
// Specifically, operators that use shift symbols (<, <<, >, >>) will cause our // Specifically, operators that use shift symbols (<, <<, >, >>) will cause our
// template parsing to fail. To defeat the failure, we will install a temporary // template parsing to fail. To defeat the failure, we will install a temporary
// function name here and then restore it after parsing is finished. // function name here and then restore it after parsing is finished.
// //
@ -1722,7 +1718,7 @@ public class GnuDemanglerParser {
} }
/** /**
* An object that will parse a function signature string into parts: return type, name, * An object that will parse a function signature string into parts: return type, name,
* and parameters. {@link #isValidFunction()} can be called to check if the given sting is * and parameters. {@link #isValidFunction()} can be called to check if the given sting is
* indeed a function signature. * indeed a function signature.
*/ */
@ -1791,8 +1787,8 @@ public class GnuDemanglerParser {
* remove bad spaces, which is all whitespace that is not needed to separate distinct objects * remove bad spaces, which is all whitespace that is not needed to separate distinct objects
* inside of a demangled string. * inside of a demangled string.
* *
* <p>Generally, this class removes spaces within templates and parameter lists. It will * <p>Generally, this class removes spaces within templates and parameter lists. It will
* remove some spaces, while converting some to underscores. * remove some spaces, while converting some to underscores.
*/ */
private class CondensedString { private class CondensedString {
@ -1862,7 +1858,7 @@ public class GnuDemanglerParser {
} }
/** /**
* Returns the original string value that has been 'condensed', which means to remove * Returns the original string value that has been 'condensed', which means to remove
* internal spaces * internal spaces
* @return the condensed string * @return the condensed string
*/ */

View File

@ -17,13 +17,11 @@ package ghidra.app.plugin.core.analysis;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import java.util.Arrays;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import ghidra.app.cmd.label.AddLabelCmd; import ghidra.app.cmd.label.AddLabelCmd;
import ghidra.app.util.demangler.gnu.GnuDemanglerOptions; import ghidra.app.util.demangler.gnu.GnuDemanglerFormat;
import ghidra.app.util.importer.MessageLog; import ghidra.app.util.importer.MessageLog;
import ghidra.framework.options.Options; import ghidra.framework.options.Options;
import ghidra.program.database.ProgramBuilder; import ghidra.program.database.ProgramBuilder;
@ -34,7 +32,6 @@ import ghidra.program.model.symbol.*;
import ghidra.test.AbstractGhidraHeadlessIntegrationTest; import ghidra.test.AbstractGhidraHeadlessIntegrationTest;
import ghidra.test.ToyProgramBuilder; import ghidra.test.ToyProgramBuilder;
import ghidra.util.Msg; import ghidra.util.Msg;
import ghidra.util.StringUtilities;
import ghidra.util.task.TaskMonitor; import ghidra.util.task.TaskMonitor;
public class GnuDemanglerAnalyzerTest extends AbstractGhidraHeadlessIntegrationTest { public class GnuDemanglerAnalyzerTest extends AbstractGhidraHeadlessIntegrationTest {
@ -99,6 +96,7 @@ public class GnuDemanglerAnalyzerTest extends AbstractGhidraHeadlessIntegrationT
Address addr = addr("0x110"); Address addr = addr("0x110");
createSymbol(addr, mangled); createSymbol(addr, mangled);
setFormat(GnuDemanglerFormat.AUTO);
setOption(GnuDemanglerAnalyzer.OPTION_NAME_USE_DEPRECATED_DEMANGLER, true); setOption(GnuDemanglerAnalyzer.OPTION_NAME_USE_DEPRECATED_DEMANGLER, true);
analyze(); analyze();
@ -106,24 +104,6 @@ public class GnuDemanglerAnalyzerTest extends AbstractGhidraHeadlessIntegrationT
assertDemangled(addr, "__dt"); assertDemangled(addr, "__dt");
} }
@Test
public void testMangledString_WithArguments_Valid() {
//
// The below demangles to std::io::Read::read_to_end
//
String mangled = "_ZN3std2io4Read11read_to_end17hb85a0f6802e14499E";
Address addr = addr("0x110");
createSymbol(addr, mangled);
setOption(GnuDemanglerAnalyzer.OPTION_NAME_DEMANGLER_PARAMETERS, "-s rust");
analyze();
assertDemangled(addr, "read_to_end");
}
@Test @Test
public void testMangledString_WithArguments_ValidButWrongFormat() { public void testMangledString_WithArguments_ValidButWrongFormat() {
@ -135,7 +115,7 @@ public class GnuDemanglerAnalyzerTest extends AbstractGhidraHeadlessIntegrationT
Address addr = addr("0x110"); Address addr = addr("0x110");
createSymbol(addr, mangled); createSymbol(addr, mangled);
setOption(GnuDemanglerAnalyzer.OPTION_NAME_DEMANGLER_PARAMETERS, "-s dlang"); setFormat(GnuDemanglerFormat.DLANG);
analyze(); analyze();
@ -143,105 +123,36 @@ public class GnuDemanglerAnalyzerTest extends AbstractGhidraHeadlessIntegrationT
} }
@Test @Test
public void testMangledString_WithArguments_Invalid() { public void testUseDeprecatedOptionRemoval_WithDeprecatedFormat() {
setFormat(GnuDemanglerFormat.GNU);
// Options options = program.getOptions("Analyzers");
// The below demangles to std::io::Read::read_to_end assertFalse(options.contains(GnuDemanglerAnalyzer.OPTION_NAME_USE_DEPRECATED_DEMANGLER));
//
String mangled = "_ZN3std2io4Read11read_to_end17hb85a0f6802e14499E";
Address addr = addr("0x110");
createSymbol(addr, mangled);
setOption(GnuDemanglerAnalyzer.OPTION_NAME_DEMANGLER_PARAMETERS, "-s badformatname");
analyze();
assertNotDemangled(addr, "read_to_end");
assertMessageLogLine("java.io.IOException: Error starting demangler with command");
assertMessageLogLine("Invalid options", GnuDemanglerOptions.GNU_DEMANGLER_V2_33_1);
} }
@Test @Test
public void testDeprecatedMangledString_WithArguments_Invalid() { public void testUseDeprecatedOptionRemoval_WithRecentFormat() {
setFormat(GnuDemanglerFormat.RUST);
// Options options = program.getOptions("Analyzers");
// The below demangles to std::io::Read::read_to_end assertFalse(options.contains(GnuDemanglerAnalyzer.OPTION_NAME_USE_DEPRECATED_DEMANGLER));
//
String mangled = "_ZN3std2io4Read11read_to_end17hb85a0f6802e14499E";
Address addr = addr("0x110");
createSymbol(addr, mangled);
setOption(GnuDemanglerAnalyzer.OPTION_NAME_USE_DEPRECATED_DEMANGLER, true);
setOption(GnuDemanglerAnalyzer.OPTION_NAME_DEMANGLER_PARAMETERS, "-s badformatname");
analyze();
assertNotDemangled(addr, "read_to_end");
assertMessageLogLine("java.io.IOException: Error starting demangler with command");
assertMessageLogLine("Invalid options", GnuDemanglerOptions.GNU_DEMANGLER_V2_33_1);
assertMessageLogLine("Invalid options", GnuDemanglerOptions.GNU_DEMANGLER_V2_24);
} }
@Test @Test
public void testDeprecatedMangledString_WithArguments_InvalidModernArguments_ValidDeprecatedArguments() { public void testUseDeprecatedOptionAddition() {
// remove it first
// testUseDeprecatedOptionRemoval_WithDeprecatedFormat();
// The below demangles to std::io::Read::read_to_end setFormat(GnuDemanglerFormat.AUTO);
// Options options = program.getOptions("Analyzers");
String mangled = "_ZN3std2io4Read11read_to_end17hb85a0f6802e14499E"; assertTrue(options.contains(GnuDemanglerAnalyzer.OPTION_NAME_USE_DEPRECATED_DEMANGLER));
Address addr = addr("0x110");
createSymbol(addr, mangled);
setOption(GnuDemanglerAnalyzer.OPTION_NAME_USE_DEPRECATED_DEMANGLER, true);
setOption(GnuDemanglerAnalyzer.OPTION_NAME_DEMANGLER_PARAMETERS, "-s arm");
analyze();
assertNotDemangled(addr, "read_to_end");
assertMessageLogLine("java.io.IOException: Error starting demangler with command");
assertMessageLogLine("Invalid options", GnuDemanglerOptions.GNU_DEMANGLER_V2_33_1);
assertMessageNotInLogLine("Invalid options", GnuDemanglerOptions.GNU_DEMANGLER_V2_24);
} }
// things missed: // things missed:
// -demangle error case in base class...this is OK // -demangle error case in base class...this is OK
// -error case in applyTo method in base class // -error case in applyTo method in base class
// -use deprecated demangler case in validateOptions
//================================================================================================== //==================================================================================================
// Private Methods // Private Methods
//================================================================================================== //==================================================================================================
private void assertMessageLogLine(String... expected) {
String allMessages = log.toString();
String[] logLines = allMessages.split("\n");
for (String line : logLines) {
if (StringUtilities.containsAllIgnoreCase(line, expected)) {
return;
}
}
fail("The folllowing source text did not have a line containing:\n" +
Arrays.toString(expected) + "\n\nActual Text:\n" + allMessages);
}
private void assertMessageNotInLogLine(String... expected) {
String allMessages = log.toString();
String[] logLines = allMessages.split("\n");
for (String line : logLines) {
if (StringUtilities.containsAllIgnoreCase(line, expected)) {
fail("The folllowing source text unexpectedly has a line containing:\n" +
Arrays.toString(expected) + "\n\nActual Text:\n" + allMessages);
}
}
}
private void analyze() { private void analyze() {
tx(program, () -> analyzer.added(program, program.getMemory(), TaskMonitor.DUMMY, log)); tx(program, () -> analyzer.added(program, program.getMemory(), TaskMonitor.DUMMY, log));
} }
@ -288,14 +199,15 @@ public class GnuDemanglerAnalyzerTest extends AbstractGhidraHeadlessIntegrationT
fail("Could not find option '" + optionName + "'"); fail("Could not find option '" + optionName + "'");
} }
private void setOption(String optionName, String value) { private void setFormat(GnuDemanglerFormat format) {
String optionName = GnuDemanglerAnalyzer.OPTION_NAME_DEMANGLER_FORMAT;
String fullOptionName = analyzer.getName() + Options.DELIMITER_STRING + optionName; String fullOptionName = analyzer.getName() + Options.DELIMITER_STRING + optionName;
Options options = program.getOptions("Analyzers"); Options options = program.getOptions("Analyzers");
for (String name : options.getOptionNames()) { for (String name : options.getOptionNames()) {
if (name.equals(fullOptionName)) { if (name.equals(fullOptionName)) {
tx(program, () -> options.setString(optionName, value)); tx(program, () -> options.setEnum(optionName, format));
// we must call this manually, since we are not using a tool // we must call this manually, since we are not using a tool
analyzer.optionsChanged(options, program); analyzer.optionsChanged(options, program);

View File

@ -0,0 +1,160 @@
/* ###
* 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 ghidra.test.AbstractGhidraHeadlessIntegrationTest;
import org.junit.Test;
import static ghidra.app.util.demangler.gnu.GnuDemanglerFormat.*;
import java.io.IOException;
public class GnuDemanglerOptionsTest extends AbstractGhidraHeadlessIntegrationTest {
@Test
public void testAuto_withDeprecated() throws IOException {
GnuDemanglerOptions options = new GnuDemanglerOptions(AUTO, true);
getNativeProcess(options);
}
@Test
public void testAuto_withModern() throws IOException {
GnuDemanglerOptions options = new GnuDemanglerOptions(AUTO, false);
getNativeProcess(options);
}
@Test
public void testGnu_withDeprecated() throws IOException {
GnuDemanglerOptions options = new GnuDemanglerOptions(GNU, true);
getNativeProcess(options);
}
@Test(expected = IllegalArgumentException.class)
public void testGnu_withModern() {
new GnuDemanglerOptions(GNU, false);
}
@Test
public void testLucid_withDeprecated() throws IOException {
GnuDemanglerOptions options = new GnuDemanglerOptions(LUCID, true);
getNativeProcess(options);
}
@Test(expected = IllegalArgumentException.class)
public void testLucid_withModern() {
new GnuDemanglerOptions(LUCID, false);
}
@Test
public void testArm_withDeprecated() throws IOException {
GnuDemanglerOptions options = new GnuDemanglerOptions(ARM, true);
getNativeProcess(options);
}
@Test(expected = IllegalArgumentException.class)
public void testArm_withModern() {
new GnuDemanglerOptions(ARM, false);
}
@Test
public void testHp_withDeprecated() throws IOException {
GnuDemanglerOptions options = new GnuDemanglerOptions(HP, true);
getNativeProcess(options);
}
@Test(expected = IllegalArgumentException.class)
public void testHp_withModern() {
new GnuDemanglerOptions(HP, false);
}
@Test
public void testEdg_withDeprecated() throws IOException {
GnuDemanglerOptions options = new GnuDemanglerOptions(EDG, true);
getNativeProcess(options);
}
@Test(expected = IllegalArgumentException.class)
public void testEdg_withModern() {
new GnuDemanglerOptions(EDG, false);
}
@Test
public void testGnuV3_withDeprecated() throws IOException {
GnuDemanglerOptions options = new GnuDemanglerOptions(GNUV3, true);
getNativeProcess(options);
}
@Test
public void testGnuV3_withModern() throws IOException {
GnuDemanglerOptions options = new GnuDemanglerOptions(GNUV3, false);
getNativeProcess(options);
}
@Test
public void testJava_withDeprecated() throws IOException {
GnuDemanglerOptions options = new GnuDemanglerOptions(JAVA, true);
getNativeProcess(options);
}
@Test
public void testJava_withModern() throws IOException {
GnuDemanglerOptions options = new GnuDemanglerOptions(JAVA, false);
getNativeProcess(options);
}
@Test
public void testGnat_withDeprecated() throws IOException {
GnuDemanglerOptions options = new GnuDemanglerOptions(GNAT, true);
getNativeProcess(options);
}
@Test
public void testGnat_withModern() throws IOException {
GnuDemanglerOptions options = new GnuDemanglerOptions(GNAT, false);
getNativeProcess(options);
}
@Test(expected = IllegalArgumentException.class)
public void testDlang_withDeprecated() {
new GnuDemanglerOptions(DLANG, true);
}
@Test
public void testDlang_withModern() throws IOException {
GnuDemanglerOptions options = new GnuDemanglerOptions(DLANG, false);
getNativeProcess(options);
}
@Test(expected = IllegalArgumentException.class)
public void testRust_withDeprecated() {
new GnuDemanglerOptions(RUST, true);
}
@Test
public void testRust_withModern() throws IOException {
GnuDemanglerOptions options = new GnuDemanglerOptions(RUST, false);
getNativeProcess(options);
}
private static GnuDemanglerNativeProcess getNativeProcess(GnuDemanglerOptions options)
throws IOException {
String demanglerName = options.getDemanglerName();
String applicationOptions = options.getDemanglerApplicationArguments();
return GnuDemanglerNativeProcess.getDemanglerNativeProcess(demanglerName,
applicationOptions);
}
}

View File

@ -157,23 +157,6 @@ public class GnuDemanglerTest extends AbstractGenericTest {
assertEquals("ABC", d.getValue()); assertEquals("ABC", d.getValue());
} }
@Test
public void testDemangler_Format_EDG_DemangleOnlyKnownPatterns_False()
throws DemangledException {
String mangled = "_$_10MyFunction";
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 MyFunction::~MyFunction(void)", result.getSignature(false));
}
@Test @Test
public void testDemangler_Format_EDG_DemangleOnlyKnownPatterns_True() public void testDemangler_Format_EDG_DemangleOnlyKnownPatterns_True()
throws DemangledException { throws DemangledException {
@ -192,16 +175,15 @@ 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
GnuDemanglerOptions options = new GnuDemanglerOptions(); GnuDemanglerOptions options = new GnuDemanglerOptions(GnuDemanglerFormat.EDG);
options.setDemangleOnlyKnownPatterns(true); // do not try
options.setDemanglerName(GnuDemanglerOptions.GNU_DEMANGLER_V2_24);
DemangledObject result = demangler.demangle(mangled, options); DemangledObject result = demangler.demangle(mangled, options);
assertNull(result); assertNull(result);
} }
@Test @Test
public void testDemangler_Format_CodeWarrior_MacOS8or9() throws DemangledException { public void testDemangler_Format_CodeWarrior_MacOS8or9() throws DemangledException {
// NOTE: mangled CodeWarrior format symbols with templates will fail
// This is because the GNU demangler does not support CodeWarrior
// .scroll__10TTextPanelFUcsi // .scroll__10TTextPanelFUcsi
String mangled = ".scroll__10TTextPanelFUcsi"; String mangled = ".scroll__10TTextPanelFUcsi";
@ -209,9 +191,7 @@ 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
GnuDemanglerOptions options = new GnuDemanglerOptions(); GnuDemanglerOptions options = new GnuDemanglerOptions(GnuDemanglerFormat.AUTO, true);
options.setDemangleOnlyKnownPatterns(false);
options.setDemanglerName(GnuDemanglerOptions.GNU_DEMANGLER_V2_24);
DemangledObject result = demangler.demangle(mangled, options); DemangledObject result = demangler.demangle(mangled, options);
assertNotNull(result); assertNotNull(result);
assertEquals("undefined TTextPanel::scroll(unsigned char,short,int)", assertEquals("undefined TTextPanel::scroll(unsigned char,short,int)",