Merge remote-tracking branch 'origin/GP-4901_ghizard_MDMang_output_options--SQUASHED'

This commit is contained in:
Ryan Kurtz
2026-02-27 05:04:48 -05:00
28 changed files with 1444 additions and 126 deletions
@@ -422,7 +422,7 @@
</TD> </TD>
</TR> </TR>
<TR> <TR>
<TD>Only Demangle Known Mangled Symbols <TD>Demangle Only Known Mangled Symbols
</TD> </TD>
<TD> <TD>
Only demangle symbols that follow known compiler mangling patterns. Only demangle symbols that follow known compiler mangling patterns.
@@ -509,7 +509,7 @@
<BR> <BR>
<BR> <BR>
<P><A name="Microsoft_Demangler_Options"> <P><A name="Microsoft_Demangler_Options">
<B>The Microsoft Demangler</B></H4> adds the following analysis option: <B>The Microsoft Demangler</B></H4> adds the following analysis options:
<BLOCKQUOTE> <BLOCKQUOTE>
<P> <P>
@@ -536,6 +536,33 @@
</P> </P>
</BLOCKQUOTE> </BLOCKQUOTE>
<BLOCKQUOTE>
<P>
<U><B>Use Encoded Anonymous Namespace</B></U> -
This output option is used to create a more unique namespace node from the
standard output for anonymous namespaces, thus helping to prevent collisions
between multiple types of the same name, but each in their own unique anonymous
namespace. When turned on, variations of the standard namespace
<B>`anonymous namespace'</B> will be replaced with a more specific namespace of
the form <B>_anon_ABCD1234</B> where the hexadecimal number comes from a number
encoded in the mangled string. The trade-off is that when this option is turned on,
the resultant namespace will not match non-mangled names that have the standard,
non-specific anonymous namespace.
</P>
</BLOCKQUOTE>
<BLOCKQUOTE>
<P>
<U><B>Apply Argument UDT Tags</B></U> -
This output option controls whether user-defined type tags (e.g., class, enum,
struct, union) are placed in front of the named type when the type is used as a
template or function argument. When turned on, the demangled string matches the
common demangler standard. When turned off, the standard demangled strings of
<B>struct AAA&lt;class BBB&gt;</B> and <B>struct AAA&lt;struct BBB&gt;</B> would
both be reduced to <B>struct AAA&lt;BBB&gt;</B>.
</P>
</BLOCKQUOTE>
</BLOCKQUOTE> </BLOCKQUOTE>
@@ -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.
@@ -15,15 +15,17 @@
*/ */
package ghidra.app.util.demangler; package ghidra.app.util.demangler;
import generic.json.Json;
/** /**
* A simple class to contain the various settings for demangling * A simple class to contain the various settings for demangling
*/ */
public class DemanglerOptions { public class DemanglerOptions {
private boolean applyCallingConvention = true; protected boolean applyCallingConvention = true;
private boolean applySignature = true; protected boolean applySignature = true;
private boolean doDisassembly = true; protected boolean doDisassembly = true;
private boolean demangleOnlyKnownPatterns = true; protected boolean demangleOnlyKnownPatterns = true;
public DemanglerOptions() { public DemanglerOptions() {
// use default values // use default values
@@ -118,12 +120,6 @@ public class DemanglerOptions {
@Override @Override
public String toString() { public String toString() {
//@formatter:off return Json.toString(this);
return "{\n" +
"\tdoDisassembly: " + doDisassembly + ",\n" +
"\tapplySignature: " + applySignature + ",\n" +
"\tdemangleOnlyKnownPatterns: " + demangleOnlyKnownPatterns + ",\n" +
"}";
//@formatter:on
} }
} }
@@ -17,7 +17,9 @@ package ghidra.app.plugin.core.analysis;
import ghidra.app.util.demangler.*; import ghidra.app.util.demangler.*;
import ghidra.app.util.demangler.microsoft.*; import ghidra.app.util.demangler.microsoft.*;
import ghidra.app.util.demangler.microsoft.options.*;
import ghidra.app.util.importer.MessageLog; import ghidra.app.util.importer.MessageLog;
import ghidra.framework.options.OptionType;
import ghidra.framework.options.Options; import ghidra.framework.options.Options;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
import ghidra.util.HelpLocation; import ghidra.util.HelpLocation;
@@ -58,6 +60,12 @@ public class MicrosoftDemanglerAnalyzer extends AbstractDemanglerAnalyzer {
private boolean demangleOnlyKnownPatterns = false; private boolean demangleOnlyKnownPatterns = false;
private MsCInterpretation interpretation = MsCInterpretation.FUNCTION_IF_EXISTS; private MsCInterpretation interpretation = MsCInterpretation.FUNCTION_IF_EXISTS;
private static final String APPLY_OPTIONS_LABEL = "msdApplyOptions";
private static final String OUTPUT_OPTIONS_LABEL = "msdOutputOptions";
private MsdApplyOption applyOption;
private MsdOutputOption outputOption;
public MicrosoftDemanglerAnalyzer() { public MicrosoftDemanglerAnalyzer() {
super(NAME, DESCRIPTION); super(NAME, DESCRIPTION);
demangler = new MicrosoftDemangler(); demangler = new MicrosoftDemangler();
@@ -71,43 +79,36 @@ public class MicrosoftDemanglerAnalyzer extends AbstractDemanglerAnalyzer {
@Override @Override
public void registerOptions(Options options, Program program) { public void registerOptions(Options options, Program program) {
HelpLocation help = new HelpLocation("AutoAnalysisPlugin", "Demangler_Analyzer"); HelpLocation help = new HelpLocation("AutoAnalysisPlugin", "Demangler_Analyzer");
options.registerOption(OPTION_NAME_APPLY_SIGNATURE, applyFunctionSignature, help, options.registerOption(APPLY_OPTIONS_LABEL, OptionType.CUSTOM_TYPE,
OPTION_DESCRIPTION_APPLY_SIGNATURE); new MsdApplyOption(), help, "Configures how demangling is applied",
() -> new MsdApplyOptionsEditor());
applyOption =
(MsdApplyOption) options.getCustomOption(APPLY_OPTIONS_LABEL, null);
options.registerOption(OPTION_NAME_APPLY_CALLING_CONVENTION, applyCallingConvention, help, options.registerOption(OUTPUT_OPTIONS_LABEL, OptionType.CUSTOM_TYPE,
OPTION_DESCRIPTION_APPLY_CALLING_CONVENTION); new MsdOutputOption(), help, "Controls demangled output",
() -> new MsdOutputOptionsEditor());
options.registerOption(OPTION_NAME_DEMANGLE_USE_KNOWN_PATTERNS, demangleOnlyKnownPatterns, outputOption = (MsdOutputOption) options.getCustomOption(OUTPUT_OPTIONS_LABEL, null);
help, OPTION_DESCRIPTION_USE_KNOWN_PATTERNS);
options.registerOption(OPTION_NAME_MS_C_INTERPRETATION, interpretation, help,
OPTION_DESCRIPTION_MS_C_INTERPRETATION);
} }
@Override @Override
public void optionsChanged(Options options, Program program) { public void optionsChanged(Options options, Program program) {
applyFunctionSignature = applyOption = (MsdApplyOption) options.getCustomOption(APPLY_OPTIONS_LABEL, applyOption);
options.getBoolean(OPTION_NAME_APPLY_SIGNATURE, applyFunctionSignature); outputOption =
(MsdOutputOption) options.getCustomOption(OUTPUT_OPTIONS_LABEL, outputOption);
applyCallingConvention =
options.getBoolean(OPTION_NAME_APPLY_CALLING_CONVENTION, applyCallingConvention);
demangleOnlyKnownPatterns =
options.getBoolean(OPTION_NAME_DEMANGLE_USE_KNOWN_PATTERNS, demangleOnlyKnownPatterns);
interpretation = options.getEnum(OPTION_NAME_MS_C_INTERPRETATION, interpretation);
} }
@Override @Override
protected DemanglerOptions getOptions() { protected DemanglerOptions getOptions() {
MicrosoftDemanglerOptions options = new MicrosoftDemanglerOptions(); MicrosoftDemanglerOptions options = new MicrosoftDemanglerOptions();
options.setApplySignature(applyFunctionSignature); options.setApplySignature(applyOption.applySignature());
options.setApplyCallingConvention(applyCallingConvention); options.setApplyCallingConvention(applyOption.applyCallingConvention());
options.setDemangleOnlyKnownPatterns(demangleOnlyKnownPatterns); options.setDemangleOnlyKnownPatterns(applyOption.demangleOnlyKnownPatterns());
options.setInterpretation(interpretation); options.setInterpretation(applyOption.getInterpretation());
options.setUseEncodedAnonymousNamespace(outputOption.getUseEncodedAnonymousNamespace());
options.setApplyUdtArgumentTypeTag(outputOption.getApplyUdtArgumentTypeTag());
options.setErrorOnRemainingChars(true); options.setErrorOnRemainingChars(true);
return options; return options;
} }
@@ -64,13 +64,24 @@ public class MicrosoftDemangler implements Demangler {
demangler.setDemangleOnlyKnownPatterns(options.demangleOnlyKnownPatterns()); demangler.setDemangleOnlyKnownPatterns(options.demangleOnlyKnownPatterns());
demangler.setArchitectureSize(mContext.getArchitectureSize()); demangler.setArchitectureSize(mContext.getArchitectureSize());
demangler.setIsFunction(mContext.shouldInterpretAsFunction()); demangler.setIsFunction(mContext.shouldInterpretAsFunction());
try { try {
item = demangler.demangle(); item = demangler.demangle();
if (item == null) { if (item == null) {
return null; return null;
} }
// The item.toString() method is influenced by the demangler output options, so we get
// the originalDemangled string before we change the output options to what we desire
// for Ghidra processing.
String originalDemangled = item.toString(); String originalDemangled = item.toString();
demangler.getOutputOptions().setUseEncodedAnonymousNamespace(true); // Now we set the particular output options that we didt't want to affect what was
// in the originalDemangled string above. Once set, then when
// MicrosoftDemanglerUtil.convertToDemangledObject() method is called, the newly
// set output options affect the object result.
demangler.getOutputOptions()
.setUseEncodedAnonymousNamespace(options.getUseEncodedAnonymousNamespace());
demangler.getOutputOptions()
.setApplyUdtArgumentTypeTag(options.getApplyUdtArgumentTypeTag());
object = object =
MicrosoftDemanglerUtil.convertToDemangledObject(item, mangled, originalDemangled); MicrosoftDemanglerUtil.convertToDemangledObject(item, mangled, originalDemangled);
if (object != null) { if (object != null) {
@@ -112,8 +123,18 @@ public class MicrosoftDemangler implements Demangler {
if (mdType == null) { if (mdType == null) {
return null; return null;
} }
// The item.toString() method is influenced by the demangler output options, so we get
// the originalDemangled string before we change the output options to what we desire
// for Ghidra processing.
String originalDemangled = mdType.toString(); String originalDemangled = mdType.toString();
demangler.getOutputOptions().setUseEncodedAnonymousNamespace(true); // Now we set the particular output options that we didt't want to affect what was
// in the originalDemangled string above. Once set, then when
// MicrosoftDemanglerUtil.convertToDemangledObject() method is called, the newly
// set output options affect the object result.
demangler.getOutputOptions()
.setUseEncodedAnonymousNamespace(options.getUseEncodedAnonymousNamespace());
demangler.getOutputOptions()
.setApplyUdtArgumentTypeTag(options.getApplyUdtArgumentTypeTag());
dataType = MicrosoftDemanglerUtil.convertToDemangledDataType(mdType, mangled, dataType = MicrosoftDemanglerUtil.convertToDemangledDataType(mdType, mangled,
originalDemangled); originalDemangled);
if (dataType != null) { if (dataType != null) {
@@ -15,6 +15,7 @@
*/ */
package ghidra.app.util.demangler.microsoft; package ghidra.app.util.demangler.microsoft;
import generic.json.Json;
import ghidra.app.util.demangler.DemanglerOptions; import ghidra.app.util.demangler.DemanglerOptions;
/** /**
@@ -22,24 +23,29 @@ import ghidra.app.util.demangler.DemanglerOptions;
*/ */
public class MicrosoftDemanglerOptions extends DemanglerOptions { public class MicrosoftDemanglerOptions extends DemanglerOptions {
// Processing options
private boolean errorOnRemainingChars; private boolean errorOnRemainingChars;
private MsCInterpretation interpretation; private MsCInterpretation interpretation;
// Output options:
private boolean useEncodedAnonymousNamespace;
private boolean applyUdtArgumentTypeTag; // specific to MS for now
/**
* Constructor for MicrosoftDemanglerOptions
* @param errorOnRemainingCharsArg {@code true} to error on remaining characters
*/
public MicrosoftDemanglerOptions(boolean errorOnRemainingCharsArg) {
this();
errorOnRemainingChars = errorOnRemainingCharsArg; // override defaultInits()
}
/** /**
* Default constructor for MicrosoftDemanglerOptions * Default constructor for MicrosoftDemanglerOptions
*/ */
public MicrosoftDemanglerOptions() { public MicrosoftDemanglerOptions() {
this(true);
interpretation = MsCInterpretation.FUNCTION_IF_EXISTS;
}
/**
* Constructor for MicrosoftDemanglerOptions
* @param errorOnRemainingChars {@code true} to error on remaining characters
*/
public MicrosoftDemanglerOptions(boolean errorOnRemainingChars) {
super(); super();
this.errorOnRemainingChars = errorOnRemainingChars; defaultInits();
} }
/** /**
@@ -48,17 +54,24 @@ public class MicrosoftDemanglerOptions extends DemanglerOptions {
*/ */
public MicrosoftDemanglerOptions(DemanglerOptions copy) { public MicrosoftDemanglerOptions(DemanglerOptions copy) {
super(copy); super(copy);
if (copy instanceof MicrosoftDemanglerOptions mCopy) { if (copy instanceof MicrosoftDemanglerOptions mCopy) {
errorOnRemainingChars = mCopy.errorOnRemainingChars; errorOnRemainingChars = mCopy.errorOnRemainingChars;
interpretation = mCopy.interpretation; interpretation = mCopy.interpretation;
useEncodedAnonymousNamespace = mCopy.useEncodedAnonymousNamespace;
applyUdtArgumentTypeTag = mCopy.applyUdtArgumentTypeTag;
} }
else { else {
errorOnRemainingChars = true; defaultInits();
interpretation = MsCInterpretation.FUNCTION_IF_EXISTS;
} }
} }
private void defaultInits() {
errorOnRemainingChars = true;
interpretation = MsCInterpretation.FUNCTION_IF_EXISTS;
useEncodedAnonymousNamespace = true;
applyUdtArgumentTypeTag = true;
}
/** /**
* Sets the control for erroring on remaining characters at demangler completion * Sets the control for erroring on remaining characters at demangler completion
* @param errorOnRemainingCharsArg {@code true} to error when remaining characters exist * @param errorOnRemainingCharsArg {@code true} to error when remaining characters exist
@@ -94,16 +107,46 @@ public class MicrosoftDemanglerOptions extends DemanglerOptions {
return interpretation; return interpretation;
} }
/**
* Sets the output flag to use an anonymous namespace's encoded number to craft a namespace
* containing this number instead of using the generic "`anonymous namespace'" name. Default
* is true (to create a namespace containing the encoded number)
* @param useEncodedAnonymousNamespaceArg {@code true} to use
*/
public void setUseEncodedAnonymousNamespace(boolean useEncodedAnonymousNamespaceArg) {
useEncodedAnonymousNamespace = useEncodedAnonymousNamespaceArg;
}
/**
* Returns {@code true} if the output flag is set to use an anonymous namespace's encoded
* number to craft a namespace containing the number instead of using the generic
* "`anonymous namespace'" name.
* @return {@code true} if encoded number is used to craft a namespace
*/
public boolean getUseEncodedAnonymousNamespace() {
return useEncodedAnonymousNamespace;
}
/**
* Sets the output flag for applying user-defined tags (e.g., class, struct, union, enum)
* within template and function arguments. Default is {@code true} (to apply)
* @param applyUdtArgumentTypeTagArg {@code true} to apply the tags
*/
public void setApplyUdtArgumentTypeTag(boolean applyUdtArgumentTypeTagArg) {
applyUdtArgumentTypeTag = applyUdtArgumentTypeTagArg;
}
/**
* Returns {@code true} if the output interpretation is set to apply user-defined type
* tags (e.g., class, struct, union, enum) within template and function arguments.
* @return {@code true} if applying the tags
*/
public boolean getApplyUdtArgumentTypeTag() {
return applyUdtArgumentTypeTag;
}
@Override @Override
public String toString() { public String toString() {
//@formatter:off return Json.toString(this);
return "{\n" +
"\tdoDisassembly: " + doDisassembly() + ",\n" +
"\tapplySignature: " + applySignature() + ",\n" +
"\terrorOnRemainingChars: " + errorOnRemainingChars + ",\n" +
"\tinterpretation: " + interpretation + ",\n" +
"\tdemangleOnlyKnownPatterns: " + demangleOnlyKnownPatterns() + ",\n" +
"}";
//@formatter:on
} }
} }
@@ -37,6 +37,15 @@ public class MicrosoftMangledContext extends MangledContext {
super(program, options, mangled, address); super(program, options, mangled, address);
} }
/**
* Returns the demangler options
* @return the options
*/
@Override
public MicrosoftDemanglerOptions getOptions() {
return (MicrosoftDemanglerOptions) options;
}
/** /**
* Returns the program architecture size * Returns the program architecture size
* @return the architecture size or zero if not known (program is null) * @return the architecture size or zero if not known (program is null)
@@ -0,0 +1,118 @@
/* ###
* 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.microsoft.options;
import java.util.Objects;
import ghidra.app.plugin.core.analysis.MicrosoftDemanglerAnalyzer;
import ghidra.app.util.demangler.DemanglerOptions;
import ghidra.app.util.demangler.microsoft.MicrosoftDemanglerOptions;
import ghidra.app.util.demangler.microsoft.MsCInterpretation;
import ghidra.framework.options.CustomOption;
import ghidra.framework.options.GProperties;
/**
* Option class that is paired with the {@link MsdApplyOptionsEditor} so that we can have a
* custom editor for an "Apply" option panel for the {@link MicrosoftDemanglerAnalyzer}.
* Also see {@link MsdOutputOption}, which is another panel.
* The results of both get pushed into the {@link MicrosoftDemanglerOptions} to control the
* analyzer and underlying demangler.
*/
public class MsdApplyOption extends DemanglerOptions implements CustomOption {
private static final String DEMANGLE_USE_KNOWN_PATTERNS = "demangleOnlyKnownMangledSymbols";
private static final String APPLY_SIGNATURE = "applyFunctionSignatures";
private static final String APPLY_CALLING_CONVENTION = "applyFunctionCallingConventions";
private static final String MS_C_INTERPRETATION = "C-StyleSymbolInterpretation";
private static boolean DEFAULT_DEMANGLE_USE_KNOWN_PATTERNS = false;
private static boolean DEFAULT_APPLY_SIGNATURE = true;
private static boolean DEFAULT_APPLY_CALLING_CONVENTION = true;
private static MsCInterpretation DEFAULT_MS_C_INTERPRETATION =
MsCInterpretation.FUNCTION_IF_EXISTS;
private MsCInterpretation interpretation;
public MsdApplyOption() {
// required for persistence, but also using for overriding initializations of parent
setDemangleOnlyKnownPatterns(DEFAULT_DEMANGLE_USE_KNOWN_PATTERNS);
setApplySignature(DEFAULT_APPLY_SIGNATURE);
setApplyCallingConvention(DEFAULT_APPLY_CALLING_CONVENTION);
interpretation = DEFAULT_MS_C_INTERPRETATION;
}
/**
* Sets the interpretation for processing a C-style mangled symbol if there could be multiple
* interpretations
* @param interpretationArg the interpretation to use
*/
public void setInterpretation(MsCInterpretation interpretationArg) {
interpretation = interpretationArg;
}
/**
* Returns the interpretation for processing a C-style mangled symbol if there could be multiple
* interpretations
* @return the interpretation used
*/
public MsCInterpretation getInterpretation() {
return interpretation;
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof MsdApplyOption other)) {
return false;
}
if (this == obj) {
return true;
}
return demangleOnlyKnownPatterns == other.demangleOnlyKnownPatterns &&
applyCallingConvention == other.applyCallingConvention &&
applySignature == other.applySignature &&
interpretation == other.interpretation;
}
@Override
public int hashCode() {
return Objects.hash(demangleOnlyKnownPatterns, applyCallingConvention,
applySignature, interpretation);
}
//==================================================================================================
// Persistence
//==================================================================================================
@Override
public void readState(GProperties properties) {
demangleOnlyKnownPatterns =
properties.getBoolean(DEMANGLE_USE_KNOWN_PATTERNS, demangleOnlyKnownPatterns);
applySignature = properties.getBoolean(APPLY_SIGNATURE, applySignature);
applyCallingConvention =
properties.getBoolean(APPLY_CALLING_CONVENTION, applyCallingConvention);
interpretation = properties.getEnum(MS_C_INTERPRETATION, interpretation);
}
@Override
public void writeState(GProperties properties) {
properties.putBoolean(DEMANGLE_USE_KNOWN_PATTERNS, demangleOnlyKnownPatterns);
properties.putBoolean(APPLY_SIGNATURE, applySignature);
properties.putBoolean(APPLY_CALLING_CONVENTION, applyCallingConvention);
properties.putEnum(MS_C_INTERPRETATION, interpretation);
}
}
@@ -0,0 +1,282 @@
/* ###
* 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.microsoft.options;
import java.awt.*;
import java.beans.PropertyEditorSupport;
import javax.swing.*;
import javax.swing.border.Border;
import javax.swing.border.TitledBorder;
import docking.options.editor.OptionsEditorAlignable;
import docking.widgets.checkbox.GCheckBox;
import docking.widgets.combobox.GComboBox;
import ghidra.app.util.demangler.microsoft.MsCInterpretation;
import ghidra.framework.options.CustomOptionsEditor;
import ghidra.util.HTMLUtilities;
import ghidra.util.layout.PairLayout;
/**
* Editor used presenting and receiving GUI changes to {@link MsdApplyOption}
*/
public class MsdApplyOptionsEditor extends PropertyEditorSupport
implements CustomOptionsEditor {
private static final String USE_KNOWN_PATTERNS_LABEL = "Demangle Only Known Mangled Symbols";
public static final String APPLY_SIGNATURE_LABEL = "Apply Function Signatures";
public static final String APPLY_CALLING_CONVENTION_LABEL =
"Apply Function Calling Conventions";
public static final String MS_C_INTERPRETATION_LABEL = "C-Style Symbol Interpretation";
private static final String[] NAMES = { USE_KNOWN_PATTERNS_LABEL, APPLY_SIGNATURE_LABEL,
APPLY_CALLING_CONVENTION_LABEL, MS_C_INTERPRETATION_LABEL };
// help tooltips
private static final String USE_KNOWN_PATTERNS_TOOLTIP = HTMLUtilities.toWrappedHTML(
"Only demangle symbols that follow known compiler mangling patterns. " +
"Leaving this option off may cause non-mangled symbols to get demangled.",
75);
private static final String APPLY_SIGNATURE_TOOLTIP = HTMLUtilities.toWrappedHTML(
"Apply any recovered function signature, in addition to the function name",
75);
private static final String APPLY_CALLING_CONVENTION_TOOLTIP = HTMLUtilities.toWrappedHTML(
"Apply any recovered function signature calling convention",
75);
private static final String MS_C_INTERPRETATION_TOOLTIP = HTMLUtilities.toWrappedHTML(
"When ambiguous, treat C-Style mangled symbol as: function, variable," +
" or function if a function exists",
75);
private static final String[] DESCRIPTIONS = { USE_KNOWN_PATTERNS_TOOLTIP,
APPLY_SIGNATURE_TOOLTIP, APPLY_CALLING_CONVENTION_TOOLTIP, MS_C_INTERPRETATION_TOOLTIP };
private MsdApplyOption applyOption;
private Component editorComponent;
private JLabel interpretationLabel;
private JLabel callingConventionLabel;
private JLabel signatureLabel;
private JLabel knownPatternsLabel;
private JCheckBox knownPatternsCb;
private JCheckBox signatureCb;
private JCheckBox callingConventionCb;
private JComboBox<MsCInterpretation> interpretationComboBox;
public MsdApplyOptionsEditor() {
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 PairLayout(0, 6));
knownPatternsCb = new GCheckBox();
knownPatternsCb.setSelected(false);
knownPatternsCb.setToolTipText(USE_KNOWN_PATTERNS_TOOLTIP);
knownPatternsLabel = new JLabel(USE_KNOWN_PATTERNS_LABEL, SwingConstants.RIGHT);
knownPatternsLabel.setLabelFor(knownPatternsCb);
knownPatternsLabel.setToolTipText(USE_KNOWN_PATTERNS_TOOLTIP);
panel.add(knownPatternsLabel);
panel.add(knownPatternsCb);
signatureCb = new GCheckBox();
signatureCb.setSelected(false);
signatureCb.setToolTipText(APPLY_SIGNATURE_TOOLTIP);
signatureLabel = new JLabel(APPLY_SIGNATURE_LABEL, SwingConstants.RIGHT);
signatureLabel.setLabelFor(signatureCb);
signatureLabel.setToolTipText(APPLY_SIGNATURE_TOOLTIP);
panel.add(signatureLabel);
panel.add(signatureCb);
callingConventionCb = new GCheckBox();
callingConventionCb.setSelected(false);
callingConventionCb.setToolTipText(APPLY_CALLING_CONVENTION_TOOLTIP);
callingConventionLabel = new JLabel(APPLY_CALLING_CONVENTION_LABEL, SwingConstants.RIGHT);
callingConventionLabel.setLabelFor(callingConventionCb);
callingConventionLabel.setToolTipText(APPLY_CALLING_CONVENTION_TOOLTIP);
panel.add(callingConventionLabel);
panel.add(callingConventionCb);
interpretationComboBox = new GComboBox<>(MsCInterpretation.values());
interpretationComboBox.setSelectedItem(false);
interpretationComboBox.setToolTipText(MS_C_INTERPRETATION_TOOLTIP);
interpretationLabel = new JLabel(MS_C_INTERPRETATION_LABEL, SwingConstants.RIGHT);
interpretationLabel.setLabelFor(interpretationComboBox);
interpretationLabel.setToolTipText(MS_C_INTERPRETATION_TOOLTIP);
panel.add(interpretationLabel);
panel.add(interpretationComboBox);
knownPatternsCb.addItemListener(e -> firePropertyChange());
signatureCb.addItemListener(e -> {
signatureChangeListener();
firePropertyChange();
});
callingConventionCb.addItemListener(e -> firePropertyChange());
interpretationComboBox.addItemListener(e -> firePropertyChange());
Border emptyBorder = BorderFactory.createEmptyBorder(0, 0, 0, 0);
TitledBorder titledNoLineBorder = BorderFactory.createTitledBorder(
emptyBorder, // The invisible base border
"Apply Options", // The title text
TitledBorder.LEADING, // Title justification (e.g., LEADING, CENTER, TRAILING)
TitledBorder.TOP, // Title position (e.g., TOP, BOTTOM)
null, // Optional: Font
null // Optional: Title color
);
// Use an outer panel so we can offset the main panel
JPanel outerPanel = new AlignablePanel(new BorderLayout());
panel.setBorder(BorderFactory.createEmptyBorder(5, 20, 0, 0));
outerPanel.add(panel, BorderLayout.CENTER);
outerPanel.setBorder(BorderFactory.createCompoundBorder(
BorderFactory.createEmptyBorder(10, 0, 10, 0), titledNoLineBorder));
return outerPanel;
}
@Override
public void setValue(Object value) {
if (!(value instanceof MsdApplyOption option)) {
return;
}
applyOption = option;
setLocalValues(applyOption);
firePropertyChange();
}
private void setLocalValues(MsdApplyOption applyOptions) {
boolean knownPatterns = applyOptions.demangleOnlyKnownPatterns();
if (knownPatterns != knownPatternsCb.isSelected()) {
knownPatternsCb.setSelected(knownPatterns);
}
boolean applySignature = applyOptions.applySignature();
if (applySignature != signatureCb.isSelected()) {
signatureCb.setSelected(applySignature);
}
boolean applyCc = applyOptions.applyCallingConvention();
if (applyCc != callingConventionCb.isSelected()) {
callingConventionCb.setSelected(applyCc);
}
MsCInterpretation interpretation = applyOptions.getInterpretation();
if (interpretation != interpretationComboBox.getSelectedItem()) {
interpretationComboBox.setSelectedItem(interpretation);
}
signatureChangeListener();
}
private void signatureChangeListener() {
// Calling convention enabled only if signature is selected
boolean signatureEnabled = signatureCb.isSelected();
callingConventionCb.setEnabled(signatureEnabled);
}
private MsdApplyOption cloneNamespaceValues() {
MsdApplyOption newOptions = new MsdApplyOption();
newOptions.setApplySignature(signatureCb.isSelected());
newOptions.setApplyCallingConvention(callingConventionCb.isSelected());
newOptions.setDemangleOnlyKnownPatterns(knownPatternsCb.isSelected());
newOptions.setInterpretation((MsCInterpretation) interpretationComboBox.getSelectedItem());
return newOptions;
}
@Override
public String[] getOptionDescriptions() {
return DESCRIPTIONS;
}
@Override
public String[] getOptionNames() {
return NAMES;
}
@Override
public Object getValue() {
return cloneNamespaceValues();
}
@Override
public Component getCustomEditor() {
return editorComponent;
}
@Override
public boolean supportsCustomEditor() {
return true;
}
// Allows us to mimic the alignment of the normal options for this custom editor and any other
// custom editor in the same view as us
private class AlignablePanel extends JPanel implements OptionsEditorAlignable {
AlignablePanel(LayoutManager layout) {
super(layout);
}
@Override
public Dimension getPreferredAlignmentSize() {
//
// Use all labels and components to find the overall preferred size.
//
int maxWidth = 0;
int maxHeight = 0;
Dimension size = getPairDimension(knownPatternsLabel, knownPatternsCb);
maxWidth = Math.max(size.width, maxWidth);
maxHeight = Math.max(size.height, maxHeight);
size = getPairDimension(signatureLabel, signatureCb);
maxWidth = Math.max(size.width, maxWidth);
maxHeight = Math.max(size.height, maxHeight);
size = getPairDimension(callingConventionLabel, callingConventionCb);
maxWidth = Math.max(size.width, maxWidth);
maxHeight = Math.max(size.height, maxHeight);
size = getPairDimension(interpretationLabel, interpretationComboBox);
maxWidth = Math.max(size.width, maxWidth);
maxHeight = Math.max(size.height, maxHeight);
return new Dimension(maxWidth, maxHeight);
}
private Dimension getPairDimension(JLabel label, JComponent c) {
// Note: this code is taken from DefaultOptionComponent
Dimension dimension = label.getPreferredSize();
int labelWidth = dimension.width;
int labelHeight = dimension.height;
int maxHeight = Math.max(labelHeight, c.getPreferredSize().height);
return new Dimension(labelWidth, maxHeight);
}
@Override
public void setPreferredAlignmentSize(Dimension size) {
// This is called after all preferred sizes have been retrieved and combined
knownPatternsLabel.setPreferredSize(size);
signatureLabel.setPreferredSize(size);
callingConventionLabel.setPreferredSize(size);
interpretationLabel.setPreferredSize(size);
}
}
}
@@ -0,0 +1,120 @@
/* ###
* 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.microsoft.options;
import java.util.Objects;
import ghidra.app.plugin.core.analysis.MicrosoftDemanglerAnalyzer;
import ghidra.framework.options.CustomOption;
import ghidra.framework.options.GProperties;
/**
* Options class that is paired with the {@link MsdOutputOptionsEditor} so that we can have a
* custom editor for an "Output" options panel for the {@link MicrosoftDemanglerAnalyzer}.
* Also see {@link MsdApplyOption}, which is another panel.
* The results of both get pushed into the {@link MicrosoftDemanglerOptions} to control the
* analyzer and underlying demangler.
*/
public class MsdOutputOption implements CustomOption {
private static final String USE_ENCODED_ANONYMOUS_NAMESPACE = "useEncodedAnonymousNamespace";
private static final String APPLY_TEMPLATE_ARG_TAGS = "applyTagsTemplateArgumentTags";
private static boolean DEFAULT_USE_ENCODED_ANONYMOUS_NAMESPACE = true;
private static boolean DEFAULT_APPLY_TEMPLATE_ARG_TAGS = true;
private boolean useEncodedAnonymousNamespace = DEFAULT_USE_ENCODED_ANONYMOUS_NAMESPACE;
private boolean applyUdtArgumentTypeTag = DEFAULT_APPLY_TEMPLATE_ARG_TAGS;
public MsdOutputOption() {
// required for persistence
}
/**
* Sets the output flag to use an anonymous namespace's encoded number to craft a namespace
* containing this number instead of using the generic "`anonymous namespace'" name. Default
* is true (to create a namespace containing the encoded number)
* @param useEncodedAnonymousNamespaceArg {@code true} to use
*/
public void setUseEncodedAnonymousNamespace(boolean useEncodedAnonymousNamespaceArg) {
useEncodedAnonymousNamespace = useEncodedAnonymousNamespaceArg;
}
/**
* Returns {@code true} if the output flag is set to use an anonymous namespace's encoded
* number to craft a namespace containing the number instead of using the generic
* "`anonymous namespace'" name.
* @return {@code true} if encoded number is used to craft a namespace
*/
public boolean getUseEncodedAnonymousNamespace() {
return useEncodedAnonymousNamespace;
}
/**
* Sets the output flag for applying user-defined tags (e.g., class, struct, union, enum)
* within template and function arguments. Default is {@code true} (to apply)
* @param applyUdtArgumentTypeTagArg {@code true} to apply the tags
*/
public void setApplyUdtArgumentTypeTag(boolean applyUdtArgumentTypeTagArg) {
applyUdtArgumentTypeTag = applyUdtArgumentTypeTagArg;
}
/**
* Returns {@code true} if the output interpretation is set to apply user-defined type
* tags (e.g., class, struct, union, enum) within template and function arguments.
* @return {@code true} if applying the tags
*/
public boolean getApplyUdtArgumentTypeTag() {
return applyUdtArgumentTypeTag;
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof MsdOutputOption other)) {
return false;
}
if (this == obj) {
return true;
}
return useEncodedAnonymousNamespace == other.useEncodedAnonymousNamespace &&
applyUdtArgumentTypeTag == other.applyUdtArgumentTypeTag;
}
@Override
public int hashCode() {
return Objects.hash(applyUdtArgumentTypeTag, useEncodedAnonymousNamespace);
}
//==================================================================================================
// Persistence
//==================================================================================================
@Override
public void readState(GProperties properties) {
useEncodedAnonymousNamespace =
properties.getBoolean(USE_ENCODED_ANONYMOUS_NAMESPACE, useEncodedAnonymousNamespace);
applyUdtArgumentTypeTag =
properties.getBoolean(APPLY_TEMPLATE_ARG_TAGS, applyUdtArgumentTypeTag);
}
@Override
public void writeState(GProperties properties) {
properties.putBoolean(USE_ENCODED_ANONYMOUS_NAMESPACE, useEncodedAnonymousNamespace);
properties.putBoolean(APPLY_TEMPLATE_ARG_TAGS, applyUdtArgumentTypeTag);
}
}
@@ -0,0 +1,211 @@
/* ###
* 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.microsoft.options;
import java.awt.*;
import java.beans.PropertyEditorSupport;
import javax.swing.*;
import javax.swing.border.Border;
import javax.swing.border.TitledBorder;
import docking.options.editor.OptionsEditorAlignable;
import docking.widgets.checkbox.GCheckBox;
import ghidra.framework.options.CustomOptionsEditor;
import ghidra.util.HTMLUtilities;
import ghidra.util.layout.PairLayout;
/**
* Editor used presenting and receiving GUI changes to {@link MsdOutputOption}
*/
public class MsdOutputOptionsEditor extends PropertyEditorSupport implements CustomOptionsEditor {
private static final String USE_ENCODED_ANON_NS_LABEL = "Use Encoded Anonymous Namespace";
private static final String APPLY_UDT_ARG_TAGS_LABEL = "Apply UDT Argument Type Tags";
private static final String[] NAMES = { USE_ENCODED_ANON_NS_LABEL, APPLY_UDT_ARG_TAGS_LABEL };
// help tooltips
private static final String USE_ENCODED_ANON_NS_TOOLTIP =
HTMLUtilities.toWrappedHTML(
"Instead of a variation of <B>Anonymous Namespace</B>, uses the encoded numeric " +
"identifier to output an <B>_anon_ABCD1234</B> name form.",
75);
private static final String APPLY_UDT_ARG_TAGS_TOOLTIP = HTMLUtilities.toWrappedHTML(
"Applies a user-defined type's (UDT) class/enum/struct/union tag when the UDT is a " +
"template or function argument.",
75);
private static final String[] DESCRIPTIONS = { USE_ENCODED_ANON_NS_TOOLTIP,
APPLY_UDT_ARG_TAGS_TOOLTIP };
private MsdOutputOption msOutputOption;
private Component editorComponent;
private JCheckBox useEncodedAnonNsCb;
private JCheckBox useUdtTagsCb;
private JLabel useEncodedAnonNsLabel;
private JLabel useUdtTagsLabel;
public MsdOutputOptionsEditor() {
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 PairLayout(0, 6));
useEncodedAnonNsCb = new GCheckBox();
useEncodedAnonNsCb.setSelected(true);
useEncodedAnonNsCb.setToolTipText(USE_ENCODED_ANON_NS_TOOLTIP);
useEncodedAnonNsLabel = new JLabel(USE_ENCODED_ANON_NS_LABEL, SwingConstants.RIGHT);
useEncodedAnonNsLabel.setLabelFor(useEncodedAnonNsCb);
useEncodedAnonNsLabel.setToolTipText(USE_ENCODED_ANON_NS_TOOLTIP);
panel.add(useEncodedAnonNsLabel);
panel.add(useEncodedAnonNsCb);
useUdtTagsCb = new GCheckBox();
useUdtTagsCb.setSelected(false);
useUdtTagsCb.setToolTipText(APPLY_UDT_ARG_TAGS_TOOLTIP);
useUdtTagsLabel = new JLabel(APPLY_UDT_ARG_TAGS_LABEL, SwingConstants.RIGHT);
useUdtTagsLabel.setLabelFor(useUdtTagsCb);
useUdtTagsLabel.setToolTipText(APPLY_UDT_ARG_TAGS_TOOLTIP);
panel.add(useUdtTagsLabel);
panel.add(useUdtTagsCb);
useEncodedAnonNsCb.addItemListener(e -> firePropertyChange());
useUdtTagsCb.addItemListener(e -> firePropertyChange());
Border emptyBorder = BorderFactory.createEmptyBorder(0, 0, 0, 0);
TitledBorder titledNoLineBorder = BorderFactory.createTitledBorder(
emptyBorder, // The invisible base border
"Output Options", // The title text
TitledBorder.LEADING, // Title justification (e.g., LEADING, CENTER, TRAILING)
TitledBorder.TOP, // Title position (e.g., TOP, BOTTOM)
null, // Optional: Font
null // Optional: Title color
);
// Use an outer panel so we can offset the main panel
JPanel outerPanel = new AlignablePanel(new BorderLayout());
panel.setBorder(BorderFactory.createEmptyBorder(5, 20, 0, 0));
outerPanel.add(panel, BorderLayout.CENTER);
outerPanel.setBorder(BorderFactory.createCompoundBorder(
BorderFactory.createEmptyBorder(10, 0, 10, 0), titledNoLineBorder));
return outerPanel;
}
@Override
public void setValue(Object value) {
if (!(value instanceof MsdOutputOption option)) {
return;
}
msOutputOption = option;
setLocalValues(msOutputOption);
firePropertyChange();
}
private void setLocalValues(MsdOutputOption outputOptions) {
boolean useEncoded = outputOptions.getUseEncodedAnonymousNamespace();
if (useEncoded != useEncodedAnonNsCb.isSelected()) {
useEncodedAnonNsCb.setSelected(useEncoded);
}
boolean applyComplexTag = outputOptions.getApplyUdtArgumentTypeTag();
if (applyComplexTag != useUdtTagsCb.isSelected()) {
useUdtTagsCb.setSelected(applyComplexTag);
}
}
private MsdOutputOption cloneNamespaceValues() {
MsdOutputOption newOption = new MsdOutputOption();
newOption.setUseEncodedAnonymousNamespace(useEncodedAnonNsCb.isSelected());
newOption.setApplyUdtArgumentTypeTag(useUdtTagsCb.isSelected());
return newOption;
}
@Override
public String[] getOptionDescriptions() {
return DESCRIPTIONS;
}
@Override
public String[] getOptionNames() {
return NAMES;
}
@Override
public Object getValue() {
return cloneNamespaceValues();
}
@Override
public Component getCustomEditor() {
return editorComponent;
}
@Override
public boolean supportsCustomEditor() {
return true;
}
// Allows us to mimic the alignment of the normal options for this custom editor and any other
// custom editor in the same view as us
private class AlignablePanel extends JPanel implements OptionsEditorAlignable {
AlignablePanel(LayoutManager layout) {
super(layout);
}
@Override
public Dimension getPreferredAlignmentSize() {
//
// Use all labels and components to find the overall preferred size.
//
int maxWidth = 0;
int maxHeight = 0;
Dimension size = getPairDimension(useEncodedAnonNsLabel, useEncodedAnonNsCb);
maxWidth = Math.max(size.width, maxWidth);
maxHeight = Math.max(size.height, maxHeight);
size = getPairDimension(useUdtTagsLabel, useUdtTagsCb);
maxWidth = Math.max(size.width, maxWidth);
maxHeight = Math.max(size.height, maxHeight);
return new Dimension(maxWidth, maxHeight);
}
private Dimension getPairDimension(JLabel label, JComponent c) {
// Note: this code is taken from DefaultOptionComponent
Dimension dimension = label.getPreferredSize();
int labelWidth = dimension.width;
int labelHeight = dimension.height;
int maxHeight = Math.max(labelHeight, c.getPreferredSize().height);
return new Dimension(labelWidth, maxHeight);
}
@Override
public void setPreferredAlignmentSize(Dimension size) {
// This is called after all preferred sizes have been retrieved and combined
useEncodedAnonNsLabel.setPreferredSize(size);
useUdtTagsLabel.setPreferredSize(size);
}
}
}
@@ -0,0 +1,135 @@
/* ###
* 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.
*/
import java.io.*;
import java.util.List;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import docking.widgets.values.GValuesMap;
import ghidra.app.script.GhidraScript;
import ghidra.app.util.SymbolPath;
import ghidra.features.base.values.GhidraValuesMap;
import ghidra.util.MessageType;
import ghidra.util.StatusListener;
import mdemangler.*;
import utilities.util.FileUtilities;
public class MDMangDeveloperDemangleNamesScript extends GhidraScript {
private static final String TITLE = "Demangle Names";
private static final String INPUT_PROMPT = "Choose an input file";
private static final String OUTPUT_PROMPT = "Choose an output file";
private static boolean validateInputFile(GValuesMap valueMap, StatusListener status) {
File file = valueMap.getFile(INPUT_PROMPT);
if (file == null) {
status.setStatusText("Input file must be selected.", MessageType.ERROR);
return false;
}
if (!file.exists()) {
status.setStatusText(file.getAbsolutePath() + " is not a valid file.",
MessageType.ERROR);
return false;
}
return true;
}
private static boolean validateOutputFile(GValuesMap valueMap, StatusListener status) {
File fileIn = valueMap.getFile(INPUT_PROMPT);
File fileOut = valueMap.getFile(OUTPUT_PROMPT);
String fileNameIn = fileIn.getAbsolutePath();
String fileNameOut = fileOut.getAbsolutePath();
if (fileNameOut.equals(fileNameIn)) {
status.setStatusText("Output file cannot be same as input file '" + fileNameOut + "').",
MessageType.ERROR);
return false;
}
return true;
}
@Override
protected void run() throws Exception {
GhidraValuesMap values = new GhidraValuesMap();
values.defineFile(INPUT_PROMPT, null);
values.setValidator((valueMap, status) -> {
return validateInputFile(valueMap, status);
});
values = askValues(TITLE, null, values);
File inputFile = values.getFile(INPUT_PROMPT);
String inputFileName = inputFile.getAbsolutePath();
// creating a default output and asking again, to include output file query
String outputFileName = FilenameUtils.removeExtension(inputFileName) + ".out." +
FilenameUtils.getExtension(inputFileName);
values.defineFile(OUTPUT_PROMPT, new File(outputFileName));
values.setValidator((valueMap, status) -> {
return validateInputFile(valueMap, status) && validateOutputFile(valueMap, status);
});
setReusePreviousChoices(false); // false for second pass... want our default output
values = askValues(TITLE, null, values);
inputFile = values.getFile(INPUT_PROMPT); // might have changed
inputFileName = inputFile.getAbsolutePath(); // might have changed
File outputFile = values.getFile(OUTPUT_PROMPT);
if (outputFile.exists()) {
if (!askYesNo("Confirm Overwrite", "Overwrite file: " + outputFile.getName())) {
println("Operation canceled");
return;
}
}
FileWriter fileWriter = new FileWriter(outputFile);
try (BufferedWriter bufferedWriter = new BufferedWriter(fileWriter)) {
String message = "Processing " + inputFileName;
monitor.setMessage(message);
println(message);
List<String> lines = FileUtilities.getLines(inputFile);
for (String name : lines) {
monitor.checkCancelled();
String output = getProcessedName(name);
bufferedWriter.append(output);
bufferedWriter.append("\n");
}
message = "Results located in: " + outputFile.getAbsolutePath();
monitor.setMessage(message);
println(message);
}
}
private String getProcessedName(String name) {
if (StringUtils.containsWhitespace(name)) {
return getError(name, "contains white space");
}
MDMang demangler = new MDMang();
demangler.setMangledSymbol(name);
try {
MDParsableItem item = demangler.demangle();
SymbolPath sp = MDMangUtils.getSymbolPath(item);
return sp.toString();
}
catch (MDException e) {
return getError(name, e.getMessage());
}
}
private String getError(String name, String reason) {
return "!Failed(" + reason + "): " + name;
}
}
@@ -21,7 +21,9 @@ package mdemangler;
*/ */
public class MDOutputOptions { public class MDOutputOptions {
private boolean useEncodedAnonymousNamespaceNumber; // These defaults match standard output
private boolean useEncodedAnonymousNamespaceNumber = false;
private boolean applyUdtArgumentTypeTag = true;
/** /**
* Constructor * Constructor
@@ -48,4 +50,23 @@ public class MDOutputOptions {
return useEncodedAnonymousNamespaceNumber; return useEncodedAnonymousNamespaceNumber;
} }
/**
* Sets the option for whether to apply user-defined type tags when found as template
* or function arguments
* @param applyUdtArgumentTypeTag {@code true} to apply the tag on a complex type when
* used as a template or function argument
*/
public void setApplyUdtArgumentTypeTag(boolean applyUdtArgumentTypeTag) {
this.applyUdtArgumentTypeTag = applyUdtArgumentTypeTag;
}
/**
* Returns {@code true} if the demangler will apply user-defined type tags when found as
* template or function arguments, such as the "struct" in "templateName<struct A,int>"
* @return {@code true} if the flag is set to apply
*/
public boolean applyUdtArgumentTypeTag() {
return applyUdtArgumentTypeTag;
}
} }
@@ -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.
@@ -64,4 +64,13 @@ public class MDComplexType extends MDDataType {
qualifiedName.insert(builder); qualifiedName.insert(builder);
super.insert(builder); super.insert(builder);
} }
public void insertWithoutComplexTag(StringBuilder builder) {
// TODO: look at what needs to be done to get rid of this?
if ((builder.length() != 0) && (builder.charAt(0) != ' ')) {
dmang.insertString(builder, " ");
}
qualifiedName.insert(builder);
}
} }
@@ -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.
@@ -119,7 +119,13 @@ public class MDArgumentsList extends MDParsableItem {
} }
firstArgDone = true; firstArgDone = true;
StringBuilder argBuilder = new StringBuilder(); StringBuilder argBuilder = new StringBuilder();
arg.insert(argBuilder); if (arg instanceof MDComplexType ct &&
!dmang.getOutputOptions().applyUdtArgumentTypeTag()) {
ct.insertWithoutComplexTag(argBuilder);
}
else {
arg.insert(argBuilder);
}
dmang.appendString(builder, argBuilder.toString().trim()); dmang.appendString(builder, argBuilder.toString().trim());
// doing toString() allows the Based5 "bug" to be cleaned per parameter. // doing toString() allows the Based5 "bug" to be cleaned per parameter.
// possible: dmang.appendString(builder, arg.toString().trim()); // possible: dmang.appendString(builder, arg.toString().trim());
@@ -137,7 +137,7 @@ public class MDFunctionType extends MDType {
// output on underscore-based access-level types (from MDTypeInfoParser '_' prefix), // output on underscore-based access-level types (from MDTypeInfoParser '_' prefix),
// specifically based5 variants. This could possibly be put into the MDMangVS2015 // specifically based5 variants. This could possibly be put into the MDMangVS2015
// demangler, but then we would probably need to describe the standard MDMang output // demangler, but then we would probably need to describe the standard MDMang output
// as "invalid," as based-on-basedptr is supposed to be invalid. // as "invalid," as based-on-basedptr is supposed to be invalid.
StringBuilder conventionBuilder = new StringBuilder(); StringBuilder conventionBuilder = new StringBuilder();
convention.insert(conventionBuilder); convention.insert(conventionBuilder);
if (based != null) { if (based != null) {
@@ -179,6 +179,7 @@ public class MDFunctionType extends MDType {
} }
} }
} }
} }
/******************************************************************************/ /******************************************************************************/
@@ -17,6 +17,7 @@ package mdemangler.naming;
import ghidra.util.Msg; import ghidra.util.Msg;
import mdemangler.*; import mdemangler.*;
import mdemangler.datatype.MDDataType;
import mdemangler.object.MDObjectCPP; import mdemangler.object.MDObjectCPP;
import mdemangler.template.MDTemplateNameAndArguments; import mdemangler.template.MDTemplateNameAndArguments;
@@ -131,6 +132,20 @@ public class MDBasicName extends MDParsableItem {
} }
} }
public void setXtorQual(MDQualifier qual) {
// We should only get a call for setName() due to a contructor or destructor,
// which come from MDSpecialName or from MDTemplateNameAndArguments.
if (specialName != null) {
specialName.setXtorQual(qual);
}
else if (templateNameAndArguments != null) {
templateNameAndArguments.setXtorQual(qual);
}
else {
Msg.warn(this, "name cannot be set");
}
}
// This needs to be separate from nameModifier. The contrived example that follows // This needs to be separate from nameModifier. The contrived example that follows
// shows that both a nameModifier as well as a castTypeString should be considered // shows that both a nameModifier as well as a castTypeString should be considered
// separately, as both can exist. Trying to manage multiple calls to // separately, as both can exist. Trying to manage multiple calls to
@@ -150,6 +165,18 @@ public class MDBasicName extends MDParsableItem {
} }
} }
public void setCastType(MDDataType castType) {
if (specialName != null) {
specialName.setCastType(castType);
}
else if (templateNameAndArguments != null) {
templateNameAndArguments.setCastType(castType);
}
else {
Msg.warn(this, "castType cannot be set");
}
}
@Override @Override
public void insert(StringBuilder builder) { public void insert(StringBuilder builder) {
if (reusableName != null) { if (reusableName != null) {
@@ -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.
@@ -94,6 +94,13 @@ public class MDQualification extends MDParsableItem implements Iterable<MDQualif
} }
} }
public MDQualifier getHead() {
if (quals.size() == 0) {
return null;
}
return quals.get(0);
}
@Override @Override
protected void parseInternal() throws MDException { protected void parseInternal() throws MDException {
// TODO: consider a do-while loop so we do not need this initial test for an empty // TODO: consider a do-while loop so we do not need this initial test for an empty
@@ -16,6 +16,7 @@
package mdemangler.naming; package mdemangler.naming;
import mdemangler.*; import mdemangler.*;
import mdemangler.datatype.MDDataType;
/** /**
* This class represents a qualified name (wiki page parlance) within a name of a * This class represents a qualified name (wiki page parlance) within a name of a
@@ -85,6 +86,10 @@ public class MDQualifiedBasicName extends MDParsableItem {
basicName.setCastTypeString(castTypeString); basicName.setCastTypeString(castTypeString);
} }
public void setCastType(MDDataType castType) {
basicName.setCastType(castType);
}
public MDBasicName getBasicName() { public MDBasicName getBasicName() {
return basicName; return basicName;
} }
@@ -105,12 +110,16 @@ public class MDQualifiedBasicName extends MDParsableItem {
StringBuilder builder = new StringBuilder(); StringBuilder builder = new StringBuilder();
qualification.insertHeadQualifier(builder); qualification.insertHeadQualifier(builder);
basicName.setName(builder.toString()); basicName.setName(builder.toString());
MDQualifier head = qualification.getHead();
basicName.setXtorQual(head);
} }
else if (basicName.isDestructor()) { else if (basicName.isDestructor()) {
StringBuilder builder = new StringBuilder(); StringBuilder builder = new StringBuilder();
qualification.insertHeadQualifier(builder); qualification.insertHeadQualifier(builder);
dmang.insertString(builder, "~"); dmang.insertString(builder, "~");
basicName.setName(builder.toString()); basicName.setName(builder.toString());
MDQualifier head = qualification.getHead();
basicName.setXtorQual(head);
} }
} }
} }
@@ -25,71 +25,110 @@ public class MDQualifier extends MDParsableItem {
private static final String ANONYMOUS_NAMESPACE = "`anonymous namespace'"; private static final String ANONYMOUS_NAMESPACE = "`anonymous namespace'";
private static final String UNKNOWN_NAMESPACE = "MDMANG_UNK_QUALIFICATION"; private static final String UNKNOWN_NAMESPACE = "MDMANG_UNK_QUALIFICATION";
private MDReusableName name; private MDReusableName name;
private MDReusableName templateName;
private MDReusableName nameAnonymous; private MDReusableName nameAnonymous;
private MDReusableName nameInterface; private MDReusableName nameInterface;
private MDNestedName nameNested; private MDNestedName nameNested;
private MDNumberedNamespace nameNumbered; private MDNumberedNamespace nameNumbered;
private String nameQ; private MDQualification nameQ;
private String nameC; // Windows 10 stuff private MDFragmentName nameC; // Windows 10 stuff
public MDQualifier(MDMang dmang) { public MDQualifier(MDMang dmang) {
super(dmang); super(dmang);
} }
public boolean isInterface() { //====
return (nameInterface != null);
public boolean isName() {
return name != null;
} }
public boolean isNested() { public boolean isTemplate() {
return (nameNested != null); return templateName != null;
} }
public boolean isAnon() { public boolean isAnon() {
return (nameAnonymous != null); return nameAnonymous != null;
} }
public boolean isInterface() {
return nameInterface != null;
}
public boolean isNested() {
return nameNested != null;
}
// possibly delete this one and use isNameNumbered() instead; TODO
public boolean isLocalNamespace() { public boolean isLocalNamespace() {
return (nameNumbered != null); return nameNumbered != null;
} }
public boolean isNameC() { public boolean isNameNumbered() {
return (nameC != null); return nameNumbered != null;
} }
public boolean isNameQ() { public boolean isNameQ() {
return (nameQ != null); return nameQ != null;
} }
public MDNestedName getNested() { public boolean isNameC() {
return nameNested; return nameC != null;
}
//====
public MDReusableName getName() {
return name;
}
public MDReusableName getTemplate() {
return templateName;
} }
public String getAnonymousName() { public String getAnonymousName() {
return nameAnonymous.getName(); return nameAnonymous.getName();
} }
public MDReusableName getInterface() {
return nameInterface;
}
public MDNestedName getNested() {
return nameNested;
}
// possibly delete this one and use getNameNumbered() instead; TODO
public String getLocalNamespace() { public String getLocalNamespace() {
return nameNumbered.getName(); return nameNumbered.getName();
} }
// possibly delete this one and use getNameNumbered() instead; TODO
public String getLocalNamespaceNumber() { public String getLocalNamespaceNumber() {
return nameNumbered.getNumber().toString(); return nameNumbered.getNumber().toString();
} }
public String getNameC() { public MDNumberedNamespace getNameNumbered() {
return nameC; return nameNumbered;
} }
public String getNameQ() { public MDQualification getNameQ() {
return nameQ; return nameQ;
} }
public MDFragmentName getNameC() {
return nameC;
}
@Override @Override
public void insert(StringBuilder builder) { public void insert(StringBuilder builder) {
// Only one of these will hit. // Only one of these will hit.
if (name != null) { if (name != null) {
name.insert(builder); name.insert(builder);
} }
else if (templateName != null) {
templateName.insert(builder);
}
else if (nameAnonymous != null) { else if (nameAnonymous != null) {
if (dmang.getOutputOptions().useEncodedAnonymousNamespace()) { if (dmang.getOutputOptions().useEncodedAnonymousNamespace()) {
dmang.insertString(builder, dmang.insertString(builder,
@@ -109,10 +148,21 @@ public class MDQualifier extends MDParsableItem {
nameNumbered.insert(builder); nameNumbered.insert(builder);
} }
else if (nameQ != null) { else if (nameQ != null) {
dmang.insertString(builder, nameQ); // Could create a new object for this type and modify its insert method (and getName
// method) to return the right things. As it is, the above access getNameQ method
// will return an MDQualification that has no concept of the brackets here. But
// it could be remedied with the separate object. Similarly (but no bracket issue),
// the "nameC" could also get its own type to represent its object, but we don't
// quite know what that is at this time.
StringBuilder nameQBuilder = new StringBuilder();
nameQ.insert(nameQBuilder);
dmang.insertString(nameQBuilder, "[");
dmang.appendString(nameQBuilder, "]");
String str = nameQBuilder.toString();
dmang.insertString(builder, str);
} }
else if (nameC != null) { else if (nameC != null) {
dmang.insertString(builder, nameC); nameC.insert(builder);
} }
else { else {
dmang.insertString(builder, UNKNOWN_NAMESPACE); dmang.insertString(builder, UNKNOWN_NAMESPACE);
@@ -129,8 +179,8 @@ public class MDQualifier extends MDParsableItem {
break; break;
case '$': case '$':
// This is a template, but it will get processed through MDReusableName. // This is a template, but it will get processed through MDReusableName.
name = new MDReusableName(dmang); templateName = new MDReusableName(dmang);
name.parse(); templateName.parse();
break; break;
case 'A': // Anonymous namespace case 'A': // Anonymous namespace
// 20140522 found that we should Keep the 'A' as part of the name // 20140522 found that we should Keep the 'A' as part of the name
@@ -165,7 +215,7 @@ public class MDQualifier extends MDParsableItem {
MDFragmentName fragName = new MDFragmentName(dmang); MDFragmentName fragName = new MDFragmentName(dmang);
fragName.keepTerminator(); // keeps the terminating '@' fragName.keepTerminator(); // keeps the terminating '@'
fragName.parse(); fragName.parse();
nameC = fragName.toString(); nameC = fragName;
dmang.parseInfoPop(); dmang.parseInfoPop();
break; break;
} }
@@ -214,11 +264,7 @@ public class MDQualifier extends MDParsableItem {
dmang.increment(); // skip the 'Q' dmang.increment(); // skip the 'Q'
MDQualification qualName = new MDQualification(dmang); MDQualification qualName = new MDQualification(dmang);
qualName.parse(); qualName.parse();
StringBuilder nameQBuilder = new StringBuilder(); nameQ = qualName;
qualName.insert(nameQBuilder);
dmang.insertString(nameQBuilder, "[");
dmang.appendString(nameQBuilder, "]");
nameQ = nameQBuilder.toString();
dmang.parseInfoPop(); dmang.parseInfoPop();
break; break;
default: // special name default: // special name
@@ -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.
@@ -32,6 +32,58 @@ public class MDReusableName extends MDParsableItem {
super(dmang); super(dmang);
} }
/**
* Return whether the name is a fragment name. Note that there can still be a special name
* interpretation of a fragment, so both can be true
* @return {@code true} if is a fragment name
*/
public boolean isFragment() {
return fragment != null;
}
/**
* Returns whether the name is a template name with arguments. This is exclusive; if
* {@code true} the other two tests on name will be {@code false}
* @return {@code true} if is a template with arguments
*/
public boolean isTemplate() {
return templateName != null;
}
/**
* Returns whether the name has a special name representation. This is the interpretation
* of a fragment name, so if {@code true} then {@code isFragment} is also {@code true}
* @return {@code true} if the name is a fragment with a special interpretation
*/
public boolean isSpecialName() {
return specialName != null;
}
/**
* Returns the MDFragmentName
* @return the fragment name or {@code null} if the name is not a fragment name
*/
public MDFragmentName getFragmentName() {
return fragment;
}
/**
* Returns the Template Name And Arguments
* @return the template information or {@code null} if the name is not a template with arguments
*/
public MDTemplateNameAndArguments getTemplateName() {
return templateName;
}
/**
* Returns the special name interpretation of a fragment name
* @return the special name or {@code null} if there is no fragment name or no special
* interpretation of it
*/
public String getSpecialName() {
return specialName;
}
public String getName() { public String getName() {
if (specialName != null) { if (specialName != null) {
return specialName; return specialName;
@@ -57,9 +109,10 @@ public class MDReusableName extends MDParsableItem {
// else if (qualifiedName != null) { //TODO: do we need this 20140520 // else if (qualifiedName != null) { //TODO: do we need this 20140520
// qualifiedName.setName(name); // qualifiedName.setName(name);
// } // }
else if (templateName != null) { // else if (templateName != null) {
templateName.setName(name); // // eliminating this call... think it existed for ctor/dtor reason; refactoring
} // templateName.setName(name);
// }
return; return;
} }
@@ -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.
@@ -16,6 +16,7 @@
package mdemangler.naming; package mdemangler.naming;
import mdemangler.*; import mdemangler.*;
import mdemangler.datatype.MDDataType;
import mdemangler.datatype.MDDataTypeParser; import mdemangler.datatype.MDDataTypeParser;
import mdemangler.object.MDObjectCPP; import mdemangler.object.MDObjectCPP;
@@ -53,6 +54,8 @@ public class MDSpecialName extends MDParsableItem {
private int rttiNumber = -1; private int rttiNumber = -1;
private MDString mstring; private MDString mstring;
private String castTypeString; private String castTypeString;
private MDQualifier xtorQual;
private MDDataType castType;
public MDSpecialName(MDMang dmang, int startIndexOffset) { public MDSpecialName(MDMang dmang, int startIndexOffset) {
super(dmang, startIndexOffset); super(dmang, startIndexOffset);
@@ -66,10 +69,18 @@ public class MDSpecialName extends MDParsableItem {
return name; return name;
} }
public void setXtorQual(MDQualifier xtorQual) {
this.xtorQual = xtorQual;
}
public void setCastTypeString(String castTypeString) { public void setCastTypeString(String castTypeString) {
this.castTypeString = castTypeString; this.castTypeString = castTypeString;
} }
public void setCastType(MDDataType castType) {
this.castType = castType;
}
public boolean isConstructor() { public boolean isConstructor() {
return isConstructor; return isConstructor;
} }
@@ -108,10 +119,36 @@ public class MDSpecialName extends MDParsableItem {
@Override @Override
public void insert(StringBuilder builder) { public void insert(StringBuilder builder) {
dmang.insertString(builder, name); if (isConstructor) {
if (isTypeCast && castTypeString != null) { if (xtorQual != null) {
dmang.appendString(builder, " "); xtorQual.insert(builder);
dmang.appendString(builder, castTypeString); }
else {
dmang.insertString(builder, "ctor");
}
}
else if (isDestructor) {
if (xtorQual != null) {
xtorQual.insert(builder);
}
else {
dmang.insertString(builder, "dtor");
}
dmang.insertString(builder, "~");
}
else {
dmang.insertString(builder, name);
}
if (isTypeCast) {
if (castType != null) {
dmang.appendString(builder, " ");
castType.insert(builder);
}
else if (castTypeString != null) {
dmang.appendString(builder, " ");
dmang.appendString(builder, castTypeString);
}
} }
} }
@@ -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.
@@ -208,7 +208,13 @@ public class MDTemplateArgumentsList extends MDParsableItem {
} }
// firstArgDone = true; // firstArgDone = true;
StringBuilder argBuilder = new StringBuilder(); StringBuilder argBuilder = new StringBuilder();
arg.insert(argBuilder); if (arg instanceof MDComplexType ct &&
!dmang.getOutputOptions().applyUdtArgumentTypeTag()) {
ct.insertWithoutComplexTag(argBuilder);
}
else {
arg.insert(argBuilder);
}
dmang.appendString(builder, argBuilder.toString()); dmang.appendString(builder, argBuilder.toString());
} }
} }
@@ -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.
@@ -16,7 +16,9 @@
package mdemangler.template; package mdemangler.template;
import mdemangler.*; import mdemangler.*;
import mdemangler.datatype.MDDataType;
import mdemangler.naming.MDBasicName; import mdemangler.naming.MDBasicName;
import mdemangler.naming.MDQualifier;
/** /**
* This class represents the template name and arguments list portion of a * This class represents the template name and arguments list portion of a
@@ -46,6 +48,10 @@ public class MDTemplateNameAndArguments extends MDParsableItem {
templateName.setName(name); templateName.setName(name);
} }
public void setXtorQual(MDQualifier qual) {
templateName.setXtorQual(qual);
}
public String getName() { public String getName() {
return templateName.getName(); return templateName.getName();
} }
@@ -57,6 +63,13 @@ public class MDTemplateNameAndArguments extends MDParsableItem {
templateName.setCastTypeString(castTypeString); templateName.setCastTypeString(castTypeString);
} }
public void setCastType(MDDataType castType) {
if (templateName == null) {
return;
}
templateName.setCastType(castType);
}
public MDTemplateArgumentsList getArgumentsList() { public MDTemplateArgumentsList getArgumentsList() {
return args; return args;
} }
@@ -137,6 +137,69 @@ public class MDMangExtraTest extends AbstractGenericTest {
assertEquals("_anon_FEDCBA98::a", qualifications.get(1).toString()); assertEquals("_anon_FEDCBA98::a", qualifications.get(1).toString());
} }
// Eliminate complex tag in template arguments
@Test
public void testDemangleNameWithComplexTagInTemplateArgumentsWithBackRef() throws Exception {
String mangled = "?Ti@@3V?$Tc@V?$Tb@H@@0@@A";
String truth = "class Tc<class Tb<int>,class Tb<int> > Ti";
String truth2 = "class Tc<Tb<int>,Tb<int> > Ti";
MDMangGhidra demangler = new MDMangGhidra();
demangler.setMangledSymbol(mangled);
demangler.setErrorOnRemainingChars(true);
demangler.setDemangleOnlyKnownPatterns(true);
MDParsableItem item = demangler.demangle();
String demangled = item.toString();
assertEquals(truth, demangled);
demangler.getOutputOptions().setApplyUdtArgumentTypeTag(false);
demangled = item.toString();
assertEquals(truth2, demangled);
}
// Eliminate complex tag in template arguments
@Test
public void testDemangleTypeWithComplexTagInTemplateArguments() throws Exception {
String mangled = ".?AV?$name0@Uname1@@Uname2@@@@";
String truth = "class name0<struct name1,struct name2>";
String truth2 = "class name0<name1,name2>";
MDMangGhidra demangler = new MDMangGhidra();
demangler.setMangledSymbol(mangled);
demangler.setErrorOnRemainingChars(true);
MDParsableItem item = demangler.demangleType(); // note demangleType()
String demangled = item.toString();
assertEquals(truth, demangled);
demangler.getOutputOptions().setApplyUdtArgumentTypeTag(false);
demangled = item.toString();
assertEquals(truth2, demangled);
}
// Eliminate complex tag in template arguments
@Test
public void testDemangleTemplateConstructorWithTagInTemplateAndFunctionArguments()
throws Exception {
String mangled = "??0?$AAA@VBBB@@@ANS@@QAE@VBBB@@@Z";
String truth = "public: __thiscall ANS::AAA<class BBB>::AAA<class BBB>(class BBB)";
String truth2 = "public: __thiscall ANS::AAA<BBB>::AAA<BBB>(BBB)";
MDMangGhidra demangler = new MDMangGhidra();
demangler.setMangledSymbol(mangled);
demangler.setErrorOnRemainingChars(true);
demangler.setDemangleOnlyKnownPatterns(true);
MDParsableItem item = demangler.demangle();
String demangled = item.toString();
assertEquals(truth, demangled);
demangler.getOutputOptions().setApplyUdtArgumentTypeTag(false);
demangled = item.toString();
assertEquals(truth2, demangled);
}
@Test @Test
public void testSimpleDemangleType() throws Exception { public void testSimpleDemangleType() throws Exception {
String mangled = ".?AUname0@name1@@"; String mangled = ".?AUname0@name1@@";
@@ -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.
@@ -15,6 +15,7 @@
*/ */
package docking.options.editor; package docking.options.editor;
import java.awt.Component;
import java.awt.Dimension; import java.awt.Dimension;
import ghidra.framework.options.EditorState; import ghidra.framework.options.EditorState;
@@ -26,17 +27,33 @@ import ghidra.util.layout.HorizontalLayout;
*/ */
public class CustomOptionComponent extends GenericOptionsComponent { public class CustomOptionComponent extends GenericOptionsComponent {
private Component editorComponent;
protected CustomOptionComponent(EditorState editorState) { protected CustomOptionComponent(EditorState editorState) {
// this layout allows us to easily left-align the single component in this container // this layout allows us to easily left-align the single component in this container
setLayout(new HorizontalLayout(0)); setLayout(new HorizontalLayout(0));
// this class is designed to let the editor component handle the display and editing // this class is designed to let the editor component handle the display and editing
add(editorState.getEditorComponent()); editorComponent = editorState.getEditorComponent();
add(editorComponent);
} }
@Override @Override
protected Dimension getPreferredAlignmentSize() { protected Dimension getPreferredAlignmentSize() {
if (editorComponent instanceof OptionsEditorAlignable alignable) {
return alignable.getPreferredAlignmentSize();
}
return new Dimension(0, 0); return new Dimension(0, 0);
} }
@Override
protected void setPreferredAlignmentSize(Dimension dimension) {
if (editorComponent instanceof OptionsEditorAlignable alignable) {
alignable.setPreferredAlignmentSize(dimension);
return;
}
super.setPreferredAlignmentSize(dimension);
}
} }
@@ -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.
@@ -78,8 +78,10 @@ public class DefaultOptionComponent extends GenericOptionsComponent {
@Override // overridden to get the size based upon this class's two components @Override // overridden to get the size based upon this class's two components
protected Dimension getPreferredAlignmentSize() { protected Dimension getPreferredAlignmentSize() {
Dimension dimension = label.getPreferredSize(); Dimension dimension = label.getPreferredSize();
int maxHeight = Math.max(dimension.height, component.getPreferredSize().height); int labelWidth = dimension.width;
return new Dimension(dimension.width, maxHeight); int labelHeight = dimension.height;
int maxHeight = Math.max(labelHeight, component.getPreferredSize().height);
return new Dimension(labelWidth, maxHeight);
} }
public String getLabelText() { public String getLabelText() {
@@ -0,0 +1,38 @@
/* ###
* 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 docking.options.editor;
import java.awt.Dimension;
import java.beans.PropertyEditor;
/**
* A simple interface for options {@link PropertyEditor}s to signal that they provide a custom
* option editor that would like to be aligned with other options in the same view.
*/
public interface OptionsEditorAlignable {
/**
* {@return Gets the preferred alignment size of this class.}
*/
public Dimension getPreferredAlignmentSize();
/**
* Sets the final preferred alignment size after merging all preferred sizes from all options
* components in the view.
* @param size the size
*/
public void setPreferredAlignmentSize(Dimension size);
}
@@ -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.
@@ -88,12 +88,11 @@ public class OptionsEditorPanel extends JPanel {
editorPropertyChangeListener); editorPropertyChangeListener);
editorInfoList.add(editorState); editorInfoList.add(editorState);
HelpLocation helpLoc = options.getHelpLocation(optionName);
GenericOptionsComponent component = GenericOptionsComponent component =
GenericOptionsComponent.createOptionComponent(editorState); GenericOptionsComponent.createOptionComponent(editorState);
add(component); add(component);
HelpLocation helpLoc = options.getHelpLocation(optionName);
if (helpLoc == null) { if (helpLoc == null) {
help.excludeFromHelp(component); help.excludeFromHelp(component);
} }
@@ -119,6 +118,7 @@ public class OptionsEditorPanel extends JPanel {
state.applyValue(); state.applyValue();
} }
} }
public void setOptionsPropertyChangeListener(PropertyChangeListener listener) { public void setOptionsPropertyChangeListener(PropertyChangeListener listener) {
this.propertyChangeListener = listener; this.propertyChangeListener = listener;
} }