diff --git a/Ghidra/Features/GnuDemangler/src/main/java/ghidra/app/plugin/core/analysis/GnuDemanglerAnalyzer.java b/Ghidra/Features/GnuDemangler/src/main/java/ghidra/app/plugin/core/analysis/GnuDemanglerAnalyzer.java index 0a68ff0120..2068b1a7ff 100644 --- a/Ghidra/Features/GnuDemangler/src/main/java/ghidra/app/plugin/core/analysis/GnuDemanglerAnalyzer.java +++ b/Ghidra/Features/GnuDemangler/src/main/java/ghidra/app/plugin/core/analysis/GnuDemanglerAnalyzer.java @@ -15,13 +15,19 @@ */ package ghidra.app.plugin.core.analysis; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.util.Arrays; + import ghidra.app.util.demangler.*; import ghidra.app.util.demangler.gnu.*; import ghidra.app.util.importer.MessageLog; -import ghidra.framework.options.Options; +import ghidra.framework.options.*; import ghidra.program.model.listing.Program; import ghidra.util.HelpLocation; +import docking.options.editor.BooleanEditor; + /** * A version of the demangler analyzer to handle GNU GCC symbols */ @@ -70,20 +76,23 @@ public class GnuDemanglerAnalyzer extends AbstractDemanglerAnalyzer { @Override public void registerOptions(Options options, Program program) { + BooleanEditor editor = new BooleanEditor(); + editor.setValue(Boolean.valueOf(useDeprecatedDemangler)); + FormatEditor formatEditor = new FormatEditor(demanglerFormat, editor); + editor.addPropertyChangeListener(formatEditor); HelpLocation help = new HelpLocation("AutoAnalysisPlugin", "Demangler_Analyzer"); options.registerOption(OPTION_NAME_APPLY_SIGNATURE, doSignatureEnabled, help, OPTION_DESCRIPTION_APPLY_SIGNATURE); options.registerOption(OPTION_NAME_DEMANGLE_USE_KNOWN_PATTERNS, demangleOnlyKnownPatterns, - help, - OPTION_DESCRIPTION_USE_KNOWN_PATTERNS); - - options.registerOption(OPTION_NAME_DEMANGLER_FORMAT, demanglerFormat, help, - OPTION_DESCRIPTION_DEMANGLER_FORMAT); + help, OPTION_DESCRIPTION_USE_KNOWN_PATTERNS); - options.registerOption(OPTION_NAME_USE_DEPRECATED_DEMANGLER, useDeprecatedDemangler, help, - OPTION_DESCRIPTION_DEPRECATED_DEMANGLER); + options.registerOption(OPTION_NAME_USE_DEPRECATED_DEMANGLER, OptionType.BOOLEAN_TYPE, + useDeprecatedDemangler, help, OPTION_DESCRIPTION_DEPRECATED_DEMANGLER, editor); + + options.registerOption(OPTION_NAME_DEMANGLER_FORMAT, OptionType.ENUM_TYPE, + demanglerFormat, help, OPTION_DESCRIPTION_DEMANGLER_FORMAT, formatEditor); } @Override @@ -92,18 +101,14 @@ public class GnuDemanglerAnalyzer extends AbstractDemanglerAnalyzer { demangleOnlyKnownPatterns = options.getBoolean(OPTION_NAME_DEMANGLE_USE_KNOWN_PATTERNS, demangleOnlyKnownPatterns); demanglerFormat = options.getEnum(OPTION_NAME_DEMANGLER_FORMAT, GnuDemanglerFormat.AUTO); - if (demanglerFormat.isDeprecatedFormat() && demanglerFormat.isModernFormat()) { - useDeprecatedDemangler = + useDeprecatedDemangler = options.getBoolean(OPTION_NAME_USE_DEPRECATED_DEMANGLER, useDeprecatedDemangler); - } else { - useDeprecatedDemangler = demanglerFormat.isDeprecatedFormat(); - } } @Override protected DemanglerOptions getOptions() { - - GnuDemanglerOptions options = new GnuDemanglerOptions(demanglerFormat, useDeprecatedDemangler); + GnuDemanglerOptions options = + new GnuDemanglerOptions(demanglerFormat, useDeprecatedDemangler); options.setDoDisassembly(true); options.setApplySignature(doSignatureEnabled); options.setDemangleOnlyKnownPatterns(demangleOnlyKnownPatterns); @@ -115,4 +120,86 @@ public class GnuDemanglerAnalyzer extends AbstractDemanglerAnalyzer { MessageLog log) throws DemangledException { return demangler.demangle(mangled, (GnuDemanglerOptions) demanglerOtions); } + + private static class FormatEditor extends EnumEditor implements PropertyChangeListener { + + private final FormatSelector selector; + private final BooleanEditor isDeprecated; + + FormatEditor(GnuDemanglerFormat value, BooleanEditor isDeprecated) { + setValue(value); + this.isDeprecated = isDeprecated; + this.selector = new FormatSelector(this); + } + + @Override + public boolean supportsCustomEditor() { + return true; + } + + @Override + public FormatSelector getCustomEditor() { + return selector; + } + + @Override + public GnuDemanglerFormat[] getEnums() { + return Arrays.stream(GnuDemanglerFormat.values()) + .filter(this::filter) + .toArray(GnuDemanglerFormat[]::new); + } + + @Override + public String[] getTags() { + return Arrays.stream(GnuDemanglerFormat.values()) + .filter(this::filter) + .map(GnuDemanglerFormat::name) + .toArray(String[]::new); + } + + @Override + public void propertyChange(PropertyChangeEvent evt) { + GnuDemanglerFormat format = selector.getFormat(); + selector.reset(getTags()); + if (format.isAvailable(isDeprecatedDemangler())) { + setValue(format); + selector.setFormat(format); + } else { + setValue(GnuDemanglerFormat.AUTO); + } + } + + private boolean isDeprecatedDemangler() { + return (Boolean) isDeprecated.getValue(); + } + + private boolean filter(GnuDemanglerFormat f) { + return f.isAvailable(isDeprecatedDemangler()); + } + } + + @SuppressWarnings("serial") + private static class FormatSelector extends PropertySelector { + + public FormatSelector(FormatEditor fe) { + super(fe); + } + + @SuppressWarnings("unchecked") + void reset(String[] tags) { + removeAllItems(); + for (int i = 0; i < tags.length; i++) { + addItem(tags[i]); + } + } + + GnuDemanglerFormat getFormat() { + return GnuDemanglerFormat.valueOf((String) getSelectedItem()); + } + + void setFormat(GnuDemanglerFormat format) { + setSelectedItem(format.name()); + } + + } } diff --git a/Ghidra/Features/GnuDemangler/src/main/java/ghidra/app/util/demangler/gnu/GnuDemanglerFormat.java b/Ghidra/Features/GnuDemangler/src/main/java/ghidra/app/util/demangler/gnu/GnuDemanglerFormat.java index 44d51ebacc..9f3ffb7de3 100644 --- a/Ghidra/Features/GnuDemangler/src/main/java/ghidra/app/util/demangler/gnu/GnuDemanglerFormat.java +++ b/Ghidra/Features/GnuDemangler/src/main/java/ghidra/app/util/demangler/gnu/GnuDemanglerFormat.java @@ -69,6 +69,15 @@ public enum GnuDemanglerFormat { public boolean isModernFormat() { return version >= 0; } + + /** + * Checks if this format is available for the specified demangler + * @param isDeprecated true for the deprecated demangler, false for the modern demangler. + * @return true if the format is available + */ + public boolean isAvailable(boolean isDeprecated) { + return isDeprecated ? isDeprecatedFormat() : isModernFormat(); + } /** * Gets the format option to be passed to the demangler via the -s option diff --git a/Ghidra/Features/GnuDemangler/src/main/java/ghidra/app/util/demangler/gnu/GnuDemanglerOptions.java b/Ghidra/Features/GnuDemangler/src/main/java/ghidra/app/util/demangler/gnu/GnuDemanglerOptions.java index f8f14b619e..5e045ec836 100644 --- a/Ghidra/Features/GnuDemangler/src/main/java/ghidra/app/util/demangler/gnu/GnuDemanglerOptions.java +++ b/Ghidra/Features/GnuDemangler/src/main/java/ghidra/app/util/demangler/gnu/GnuDemanglerOptions.java @@ -57,10 +57,10 @@ public class GnuDemanglerOptions extends DemanglerOptions { this.isDeprecated = !format.isModernFormat(); } - public GnuDemanglerOptions(GnuDemanglerFormat format, boolean deprecated) { + public GnuDemanglerOptions(GnuDemanglerFormat format, boolean isDeprecated) { this.format = format; - this.isDeprecated = deprecated; - if (!isValidFormat(format, deprecated)) { + this.isDeprecated = isDeprecated; + if (!format.isAvailable(isDeprecated)) { throw new IllegalArgumentException( format.name() + " is not available in the "+getDemanglerName()); } @@ -109,23 +109,13 @@ public class GnuDemanglerOptions extends DemanglerOptions { if (this.format == format && this.isDeprecated == isDeprecated) { return this; } - if (isValidFormat(format, isDeprecated)) { + if (format.isAvailable(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; - } - /** * Returns the current arguments to be passed to the external demangler executable * @return the arguments diff --git a/Ghidra/Features/GnuDemangler/src/test/java/ghidra/app/util/demangler/GnuDemanglerParserTest.java b/Ghidra/Features/GnuDemangler/src/test/java/ghidra/app/util/demangler/GnuDemanglerParserTest.java index 50b908baff..4aaf0fd0a0 100644 --- a/Ghidra/Features/GnuDemangler/src/test/java/ghidra/app/util/demangler/GnuDemanglerParserTest.java +++ b/Ghidra/Features/GnuDemangler/src/test/java/ghidra/app/util/demangler/GnuDemanglerParserTest.java @@ -1477,8 +1477,11 @@ public class GnuDemanglerParserTest extends AbstractGenericTest { String mangled = "uv__dup"; GnuDemangler demangler = new GnuDemangler(); - DemangledObject res = demangler.demangle(mangled); - assertNull(res); + try { + demangler.demangle(mangled); + } catch (DemangledException e) { + assertTrue(e.isInvalidMangledName()); + } } @Test diff --git a/Ghidra/Features/GnuDemangler/src/test/java/ghidra/app/util/demangler/gnu/GnuDemanglerTest.java b/Ghidra/Features/GnuDemangler/src/test/java/ghidra/app/util/demangler/gnu/GnuDemanglerTest.java index b83188a47c..91f51fbd73 100644 --- a/Ghidra/Features/GnuDemangler/src/test/java/ghidra/app/util/demangler/gnu/GnuDemanglerTest.java +++ b/Ghidra/Features/GnuDemangler/src/test/java/ghidra/app/util/demangler/gnu/GnuDemanglerTest.java @@ -55,7 +55,11 @@ public class GnuDemanglerTest extends AbstractGenericTest { demangler.canDemangle(program);// this perform initialization // this throws an exception with the bug in place - demangler.demangle(mangled); + try { + demangler.demangle(mangled); + } catch (DemangledException e) { + assertTrue(e.isInvalidMangledName()); + } } @Test