diff --git a/Ghidra/Features/Base/src/main/help/help/topics/AutoAnalysisPlugin/AutoAnalysis.htm b/Ghidra/Features/Base/src/main/help/help/topics/AutoAnalysisPlugin/AutoAnalysis.htm index 809c9ee989..2e40e0162c 100644 --- a/Ghidra/Features/Base/src/main/help/help/topics/AutoAnalysisPlugin/AutoAnalysis.htm +++ b/Ghidra/Features/Base/src/main/help/help/topics/AutoAnalysisPlugin/AutoAnalysis.htm @@ -498,6 +498,38 @@

Started By: New defined functions

+ +
+
+

+ The Microsoft Demangler adds the following analysis option: + +

+

+ C-Style Symbol Interpretation - + This option is used to help direct processing of certain C-style symbols that + could have C-style interpretations. The Microsoft C-Style mangling scheme + permits a simple encoding of some functions, to include calling convention + and number of bytes in the arguments list. This is mainly for 32-bit programs, + but the convention is also used for at least one calling convention for 64-bit + programs. When a symbol can be interpreted as both a C-style function and as + some other C++-style object, this option controls which is chosen. +

The choices are: +

+

+

The user should generally not change this + option except for trying to debug the results of this new scheme. This option may + be removed in a future release. +

+

+
+ +

Entry Point Analyzer

diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/label/DemanglerCmd.java b/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/label/DemanglerCmd.java index e76a9f0135..35f031a453 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/label/DemanglerCmd.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/label/DemanglerCmd.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -70,8 +70,10 @@ public class DemanglerCmd extends BackgroundCommand { private boolean doDemangle(Demangler demangler, Program program, TaskMonitor monitor) { + MangledContext mangledContext = + demangler.createMangledContext(mangled, options, program, addr); try { - demangledObject = demangler.demangle(mangled, options); + demangledObject = demangler.demangle(mangledContext); } catch (DemangledException e) { if (e.isInvalidMangledName()) { @@ -85,13 +87,13 @@ public class DemanglerCmd extends BackgroundCommand { return false; // error // This produces too many messages for non-demangled symbols. If we could - // figure out a way to tell which symbol are those which are mangled and - // failing, then we should print those. The problem is not knowing how to + // figure out a way to tell which symbol are those which are mangled and + // failing, then we should print those. The problem is not knowing how to // tell a mangled from a non-mangled symbol. // Msg.debug(this, "Unable to demangle name: " + mangled); } catch (Exception e) { - // Demangler IndexOutOfBoundsException that we're not sure how to fix + // Demangler IndexOutOfBoundsException that we're not sure how to fix setStatusMsg("Unable to demangle symbol: " + mangled + " at " + addr + ". Message: " + e.getMessage()); return false; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/AbstractDemanglerAnalyzer.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/AbstractDemanglerAnalyzer.java index 0375029c89..70d3dae2a5 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/AbstractDemanglerAnalyzer.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/AbstractDemanglerAnalyzer.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -37,7 +37,7 @@ import ghidra.util.task.TaskMonitor; * the analyzer UI. * *

This analyzer will call each implementation's - * {@link #doDemangle(String, DemanglerOptions, MessageLog)} method for each symbol. + * {@link #doDemangle(MangledContext, MessageLog)} method for each symbol. * See the various protected methods of this class for points at which behavior can be overridden. * */ @@ -46,6 +46,8 @@ public abstract class AbstractDemanglerAnalyzer extends AbstractAnalyzer { private static final AddressSetView EXTERNAL_SET = new AddressSet( AddressSpace.EXTERNAL_SPACE.getMinAddress(), AddressSpace.EXTERNAL_SPACE.getMaxAddress()); + protected Demangler demangler; + public AbstractDemanglerAnalyzer(String name, String description) { super(name, description, AnalyzerType.BYTE_ANALYZER); setPriority(AnalysisPriority.DATA_TYPE_PROPOGATION.before().before().before()); @@ -102,6 +104,23 @@ public abstract class AbstractDemanglerAnalyzer extends AbstractAnalyzer { return true; } + /** + * Creates a mangled context + * @param program the program + * @param options the demangler options + * @param symbol the symbol to demangle + * @return the mangled context + */ + private MangledContext createMangledContext(Program program, DemanglerOptions options, + Symbol symbol) { + Address address = symbol.getAddress(); + String mangled = cleanSymbol(address, symbol.getName()); + return demangler.createMangledContext(mangled, options, program, address); + } + + /** + * Demangles and applies the program's symbols + */ private int demangleSymbols(Program program, AddressSetView set, int initialCount, DemanglerOptions options, MessageLog log, TaskMonitor monitor) throws CancelledException { @@ -124,10 +143,10 @@ public abstract class AbstractDemanglerAnalyzer extends AbstractAnalyzer { } Address address = symbol.getAddress(); - String mangled = cleanSymbol(address, symbol.getName()); - DemangledObject demangled = demangle(mangled, address, options, log); + MangledContext mangledContext = createMangledContext(program, options, symbol); + DemangledObject demangled = demangle(mangledContext, log); if (demangled != null) { - apply(program, address, demangled, options, log, monitor); + apply(mangledContext, demangled, log, monitor); continue; } @@ -141,10 +160,10 @@ public abstract class AbstractDemanglerAnalyzer extends AbstractAnalyzer { if (altSym.isPrimary() || skipSymbol(altSym)) { continue; } - mangled = cleanSymbol(address, altSym.getName()); - demangled = demangle(mangled, address, options, log); + mangledContext = createMangledContext(program, options, altSym); + demangled = demangle(mangledContext, log); if (demangled != null) { - apply(program, address, demangled, options, log, monitor); + apply(mangledContext, demangled, log, monitor); break; } } @@ -156,14 +175,13 @@ public abstract class AbstractDemanglerAnalyzer extends AbstractAnalyzer { /** * The implementation-specific demangling callback * - * @param mangled the mangled string - * @param options the demangler options + * @param mangledContext the demangler context * @param log the error log * @return the demangled object; null if demangling was unsuccessful * @throws DemangledException if there is a problem demangling or building the result */ - protected abstract DemangledObject doDemangle(String mangled, DemanglerOptions options, - MessageLog log) throws DemangledException; + protected abstract DemangledObject doDemangle(MangledContext mangledContext, MessageLog log) + throws DemangledException; /** * Called before each analysis request to ensure that the current options (which may have @@ -233,21 +251,18 @@ public abstract class AbstractDemanglerAnalyzer extends AbstractAnalyzer { } /** - * This calss's default demangle method. This may be overridden to change how errors are + * This class's default demangle method. This may be overridden to change how errors are * handled. * - * @param mangled the mangled string - * @param address the symbol address - * @param options the demangler options + * @param mangledContext the mangled context * @param log the error log * @return the demangled object; null if unsuccessful */ - protected DemangledObject demangle(String mangled, Address address, DemanglerOptions options, - MessageLog log) { + protected DemangledObject demangle(MangledContext mangledContext, MessageLog log) { DemangledObject demangled = null; try { - demangled = doDemangle(mangled, options, log); + demangled = doDemangle(mangledContext, log); } catch (Throwable e) { @@ -258,8 +273,8 @@ public abstract class AbstractDemanglerAnalyzer extends AbstractAnalyzer { } } - log.appendMsg(getName(), "Unable to demangle symbol: " + mangled + " at " + address + - ". Message: " + e.getMessage()); + log.appendMsg(getName(), "Unable to demangle symbol: " + mangledContext.getMangled() + + " at " + mangledContext.getAddress() + ". Message: " + e.getMessage()); return null; } @@ -269,24 +284,24 @@ public abstract class AbstractDemanglerAnalyzer extends AbstractAnalyzer { /** * Applies the given demangled object to the program * - * @param program the program - * @param address the apply address + * @param mangledContext the mangled context * @param demangled the demangled object - * @param options the options used during the apply * @param log the error log * @param monitor the task monitor */ - protected void apply(Program program, Address address, DemangledObject demangled, - DemanglerOptions options, MessageLog log, TaskMonitor monitor) { + protected void apply(MangledContext mangledContext, DemangledObject demangled, MessageLog log, + TaskMonitor monitor) { try { - if (demangled.applyTo(program, address, options, monitor)) { + if (demangled.applyTo(mangledContext.getProgram(), mangledContext.getAddress(), + mangledContext.getOptions(), monitor)) { return; } String errorString = demangled.getErrorMessage(); - logApplyErrorMessage(log, demangled, address, null, errorString); + logApplyErrorMessage(log, demangled, mangledContext.getAddress(), null, + errorString); } catch (Exception e) { - logApplyErrorMessage(log, demangled, address, e, null); + logApplyErrorMessage(log, demangled, mangledContext.getAddress(), e, null); } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/rust/RustDemanglerAnalyzer.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/rust/RustDemanglerAnalyzer.java index 534a781ff4..7111c4e1d9 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/rust/RustDemanglerAnalyzer.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/rust/RustDemanglerAnalyzer.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -25,7 +25,6 @@ import ghidra.app.services.AnalysisPriority; import ghidra.app.util.demangler.*; import ghidra.app.util.importer.MessageLog; import ghidra.framework.options.*; -import ghidra.program.model.address.Address; import ghidra.program.model.listing.Program; import ghidra.util.HelpLocation; import ghidra.util.SystemUtilities; @@ -64,10 +63,9 @@ public class RustDemanglerAnalyzer extends AbstractDemanglerAnalyzer { private RustDemanglerFormat demanglerFormat = RustDemanglerFormat.AUTO; private boolean useDeprecatedDemangler = false; - private RustDemangler demangler = new RustDemangler(); - public RustDemanglerAnalyzer() { super(NAME, DESCRIPTION); + demangler = new RustDemangler(); // Set priority to one before the default AbstractDemanglerAnalyzer priority setPriority(AnalysisPriority.DATA_TYPE_PROPOGATION.before().before().before().before()); setDefaultEnablement(true); @@ -121,17 +119,18 @@ public class RustDemanglerAnalyzer extends AbstractDemanglerAnalyzer { } @Override - protected DemangledObject doDemangle(String mangled, DemanglerOptions demanglerOptions, - MessageLog log) throws DemangledException { - return demangler.demangle(mangled, demanglerOptions); + protected DemangledObject doDemangle(MangledContext mangledContext, MessageLog log) + throws DemangledException { + return demangler.demangle(mangledContext); } @Override - protected void apply(Program program, Address address, DemangledObject demangled, - DemanglerOptions options, MessageLog log, TaskMonitor monitor) { + protected void apply(MangledContext mangledContext, DemangledObject demangled, MessageLog log, + TaskMonitor monitor) { try { if (demangled instanceof DemangledFunction defunc) { - defunc.applyTo(program, address, options, monitor); + defunc.applyTo(mangledContext.getProgram(), mangledContext.getAddress(), + mangledContext.getOptions(), monitor); } } catch (Exception e) { @@ -148,7 +147,7 @@ public class RustDemanglerAnalyzer extends AbstractDemanglerAnalyzer { DemangledVariable demangledVariable = new DemangledVariable(mangled, original, name); demangledVariable.setNamespace(namespace); - super.apply(program, address, demangledVariable, options, log, monitor); + super.apply(mangledContext, demangledVariable, log, monitor); } //================================================================================================== diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/rust/demangler/RustDemangler.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/rust/demangler/RustDemangler.java index 2deb5abb2c..4031ca6281 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/rust/demangler/RustDemangler.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/rust/demangler/RustDemangler.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -40,16 +40,18 @@ public class RustDemangler implements Demangler { } @Override - @Deprecated(since = "9.2", forRemoval = true) - public DemangledObject demangle(String mangled, boolean demangleOnlyKnownPatterns) - throws DemangledException { - return null; + @Deprecated(since = "11.3", forRemoval = true) + public DemangledObject demangle(String mangled, DemanglerOptions options) { + MangledContext mangledContext = createMangledContext(mangled, options, null, null); + return demangle(mangledContext); } @Override - public DemangledObject demangle(String mangled, DemanglerOptions options) { + public DemangledObject demangle(MangledContext context) { + DemanglerOptions options = context.getOptions(); RustDemanglerOptions rustOptions = getRustOptions(options); + String mangled = context.getMangled(); if (skip(mangled, rustOptions)) { return null; } @@ -74,6 +76,7 @@ public class RustDemangler implements Demangler { demangledFunction.setCallingConvention(CompilerSpec.CALLING_CONVENTION_rustcall); } + demangledObject.setMangledContext(context); return demangledObject; } @@ -88,7 +91,7 @@ public class RustDemangler implements Demangler { /** * Determines if the given mangled string should not be demangled, on the basis * of if it has a known start pattern - * + * * @param mangled the mangled string * @param options the options * @return true if the string should not be demangled @@ -106,7 +109,7 @@ public class RustDemangler implements Demangler { /** * Return true if the string is a mangled rust string in a rust program - * + * * @param mangled potential mangled string * @return true if the string could be a mangled string in a rust program */ diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/Demangled.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/Demangled.java index 212f401ab7..f006adb4c3 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/Demangled.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/Demangled.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -17,8 +17,8 @@ package ghidra.app.util.demangler; /** * A unifying top-level interface for all {@link DemangledObject}s and {@link DemangledType}s - * - *

This class and its children have many overlapping concepts that we wish to refine at a + * + *

This class and its children have many overlapping concepts that we wish to refine at a * future date. Below is a listing of known uses: * * @@ -26,6 +26,22 @@ package ghidra.app.util.demangler; * * * + * + * + * + * + * + * + * + * * * * * @@ -46,7 +62,7 @@ package ghidra.app.util.demangler; * {@link #getNamespaceName()} * * * * * * @@ -90,6 +106,35 @@ package ghidra.app.util.demangler; */ public interface Demangled { + /** + * Sets the mangled context + *

+ * This method currently has a {@code default} implementation so not to break existing + * class implementations. However, at some point the {@code default} tag and implementation, + * which is empty, will be removed. Thus, all implementers need to implement this method + * before the removal of the {@code default} + * @param mangledContextArg the mangled context + * @since 11.3 + */ + public default void setMangledContext(MangledContext mangledContextArg) { + // currently this does nothing; implementers must override this even though marked as + // default... the default implementation will be removed in a future release + } + + /** + * Returns the mangled context + *

+ * This method currently has a {@code default} implementation so not to break existing + * class implementations. However, at some point the {@code default} tag and implementation, + * which returns null, will be removed. Thus, all implementers need to implement this method + * before the removal of the {@code default} + * @return the context or null if no context + * @since 11.3 + */ + public default MangledContext getMangledContext() { + return null; + } + /** * Returns the original mangled string * @return the string @@ -102,7 +147,7 @@ public interface Demangled { */ public String getOriginalDemangled(); - /** + /** * Returns the demangled name of this object. * NOTE: unsupported symbol characters, like whitespace, will be converted to an underscore. * @return name of this DemangledObject with unsupported characters converted to underscore @@ -116,9 +161,9 @@ public interface Demangled { */ public void setName(String name); - /** - * Returns the unmodified demangled name of this object. This name may contain whitespace - * and other characters not supported for symbol or data type creation. See {@link #getName()} + /** + * Returns the unmodified demangled name of this object. This name may contain whitespace + * and other characters not supported for symbol or data type creation. See {@link #getName()} * for the same name modified for use within Ghidra. * @return name of this DemangledObject */ @@ -137,7 +182,7 @@ public interface Demangled { public void setNamespace(Demangled ns); /** - * Returns a representation of this object as fully-qualified namespace. The + * Returns a representation of this object as fully-qualified namespace. The * value returned here may have had some special characters replaced, such as ' ' replaced * with '_' and '::' replaced with '--'. * @return the full namespace @@ -145,17 +190,17 @@ public interface Demangled { public String getNamespaceString(); /** - * Returns this object's namespace name without the fully-qualified parent path. The + * Returns this object's namespace name without the fully-qualified parent path. The * value returned here may have had some special characters replaced, such as ' ' replaced * with '_' and '::' replaced with '--'. - * + * * @return the name */ public String getNamespaceName(); /** * Generates a complete representation of this object to include all know attributes of this - * object + * object * @return the signature */ public String getSignature(); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/DemangledObject.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/DemangledObject.java index 0173aa710d..cf1b01e898 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/DemangledObject.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/DemangledObject.java @@ -44,37 +44,38 @@ public abstract class DemangledObject implements Demangled { /* The following names probably need to be refactored. Until then, this is how the following fields are used. - + mangled - Source: The original mangled string as seen in the program Usage: Can be used to see if a program symbol has already been demangled - + originalDemangled - Source: The raw demangled string returned from the demangler Usage: for display - + demangledName - Source: The name as created by the parser which may transform or even replace the string returned from the demangler Usage: for display - + name - Source: This is derived from the 'demangledName' This is updated to be suitable for use as a symbol name. This may be null while building, but is expected to be non-null when applyTo() is called Usage: The name that will be applied when applyTo() is called. - - - + + + Future: These variables should be refactored and renamed to be clearer and more cohesive, something like: - + mangled rawDemangled escapedDemangled symbolName - + */ + protected MangledContext mangledContext; // the mangled context, which includes mangled string protected final String mangled; // original mangled string protected String originalDemangled; // raw demangled string private String demangledName; // updated demangled string @@ -110,11 +111,29 @@ public abstract class DemangledObject implements Demangled { private boolean demangledNameSucceeded = false; private String errorMessage = null; + /** + * Constructor. This is the older constructor that does not take a mangled context + * @param mangled the mangled string + * @param originalDemangled the raw demangled string; usually what comes from the upstream + * demangler process, if there is one + */ DemangledObject(String mangled, String originalDemangled) { this.mangled = mangled; this.originalDemangled = originalDemangled; } + /** + * Constructor. + * @param mangledContext the context, which includes the mangled string + * @param originalDemangled the raw demangled string; usually what comes from the upstream + * demangler process, if there is one + */ + DemangledObject(MangledContext mangledContext, String originalDemangled) { + this.mangledContext = mangledContext; + this.mangled = mangledContext.getMangled(); + this.originalDemangled = originalDemangled; + } + @Override public String getDemangledName() { return demangledName; @@ -230,6 +249,16 @@ public abstract class DemangledObject implements Demangled { return demangledNameSucceeded; } + @Override + public void setMangledContext(MangledContext mangledContextArg) { + mangledContext = mangledContextArg; + } + + @Override + public MangledContext getMangledContext() { + return mangledContext; + } + @Override public String getMangledString() { return mangled; @@ -378,7 +407,7 @@ public abstract class DemangledObject implements Demangled { * Apply this demangled object detail to the specified program. *
* NOTE: An open Program transaction must be established prior to invoking this method. - * + * * @param program program to which demangled data should be applied. * @param address address which corresponds to this demangled object * @param options options which control how demangled data is applied @@ -391,6 +420,25 @@ public abstract class DemangledObject implements Demangled { return applyPlateCommentOnly(program, address); } + /** + * Apply this demangled object detail to the specified program. This method only works + * if the {@link MangledContext} was set with the appropriate constructor or with the + * {@link #setMangledContext(MangledContext)} method + *
+ * NOTE: An open Program transaction must be established prior to invoking this method. + * + * @param monitor task monitor + * @return true if successfully applied, else false + * @throws Exception if an error occurs during the apply operation or if the context is null + */ + public boolean applyUsingContext(TaskMonitor monitor) throws Exception { + if (mangledContext == null) { + throw new DemangledException("Null context found for: " + mangled); + } + return applyTo(mangledContext.getProgram(), mangledContext.getAddress(), + mangledContext.getOptions(), monitor); + } + /** * @param program The program for which to apply the comment * @param address The address for the comment diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/Demangler.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/Demangler.java index ac0519e7aa..e2204baa6b 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/Demangler.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/Demangler.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -15,6 +15,7 @@ */ package ghidra.app.util.demangler; +import ghidra.program.model.address.Address; import ghidra.program.model.listing.Program; import ghidra.util.classfinder.ExtensionPoint; @@ -24,46 +25,53 @@ import ghidra.util.classfinder.ExtensionPoint; */ public interface Demangler extends ExtensionPoint { + // Note: Consider deprecating this method and creating one that takes the MangledContext. + // Another option might be to find a smarter utility method that contains the complete + // knowledge of when a particular demangler is appropriate.. but that would have to consider + // demanglers written by others. public boolean canDemangle(Program program); /** - * Deprecated. Use {@link #demangle(String)} or - * {@link #demangle(String, DemanglerOptions)}. + * Attempts to demangle the given string using a context + * ({@link #createMangledContext(String, DemanglerOptions, Program, Address)} with + * default options ({@link #createDefaultOptions()}. * * @param mangled the mangled string - * @param demangleOnlyKnownPatterns true signals to avoid demangling strings that do - * not fit known demangled patterns for this demangler - * @return the result - * @throws DemangledException if the string cannot be demangled - * @deprecated see above - */ - @Deprecated(since = "9.2", forRemoval = true) - public DemangledObject demangle(String mangled, boolean demangleOnlyKnownPatterns) - throws DemangledException; - - /** - * Attempts to demangle the given string using the default options - * ({@link #createDefaultOptions()} - * - * @param mangled the mangled string * @return the result * @throws DemangledException if the string cannot be demangled */ public default DemangledObject demangle(String mangled) throws DemangledException { - return demangle(mangled, createDefaultOptions()); + MangledContext mangledContext = createMangledContext(mangled, null, null, null); + return demangle(mangledContext); } /** + * Deprecated. Use {@link #demangle(String)} or + * {@link #demangle(MangledContext)}. + * * Attempts to demangle the given string using the given options - * + * * @param mangled the mangled string * @param options the options * @return the result * @throws DemangledException if the string cannot be demangled + * @deprecated see above */ + @Deprecated(since = "11.3", forRemoval = true) public DemangledObject demangle(String mangled, DemanglerOptions options) throws DemangledException; + /** + * Attempts to demangle the string of the mangled context + * + * @param context the mangled context + * @return the result + * @throws DemangledException if the string cannot be demangled + */ + public default DemangledObject demangle(MangledContext context) throws DemangledException { + return demangle(context.getMangled(), context.getOptions()); + } + /** * Creates default options for this particular demangler * @return the options @@ -71,4 +79,21 @@ public interface Demangler extends ExtensionPoint { public default DemanglerOptions createDefaultOptions() { return new DemanglerOptions(); } + + /** + * Creates a mangled context + * @param mangled the mangled name + * @param options the demangler options; if null, the default options are created + * @param program the program; can be null + * @param address the address for the name in the program; can be null + * @return the mangled context + */ + public default MangledContext createMangledContext(String mangled, DemanglerOptions options, + Program program, Address address) { + if (options == null) { + options = createDefaultOptions(); + } + return new MangledContext(program, options, mangled, address); + } + } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/DemanglerUtil.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/DemanglerUtil.java index 7517ae882f..d869d36432 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/DemanglerUtil.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/DemanglerUtil.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -15,39 +15,59 @@ */ package ghidra.app.util.demangler; +import java.util.ArrayList; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; +import ghidra.program.model.address.Address; import ghidra.program.model.listing.Program; import ghidra.util.classfinder.ClassSearcher; +/** + * Demangler Utility class. For version 11.3, we have migrated to a new Demangler API that + * requires a {@link MangledContext} be passed to the demangler. This provides more information + * for properly demangling symbols. + *

+ * Two methods below have been deprecated, as they do not provide enough information to produce + * the {@link MangledContext}. A new method @link demangle(Program, String, Address) is provided + * to permit proper operation using a completed context. Moreover, this new method returns all + * results instead of the first one found, as is how the deprecated methods work. + */ public class DemanglerUtil { // - // Patterns used to remove superfluous spaces within parameter list. + // Patterns used to remove superfluous spaces within parameter list. // private static final Pattern LEADING_PARAMETER_SPACE_PATTERN = Pattern.compile(" ([\\*\\&\\)])"); private static final Pattern TRAILING_PARAMETER_SPACE_PATTERN = Pattern.compile("([\\(\\,]) "); /** + * Deprecated. Use {@link #demangle(Program, String, Address)}. See class header for more + * details. + * * Locates all available demanglers, then it attempts to demangle. This method will - * query all demanglers regardless of architecture. - * - *

This method will use only the default options for demangling. If you need to + * query all demanglers regardless of architecture. + * + *

This method will use only the default options for demangling. If you need to * specify options, then you will have to call each specific demangler directly, creating - * the options specifically needed for each demangler. See + * the options and mangled context specifically needed for each demangler. See + * {@link Demangler#createMangledContext(String, DemanglerOptions, Program, Address)} and * {@link Demangler#createDefaultOptions()}. - * + * * @param mangled the mangled name * @return the demangled object or null + * @deprecated see above */ + @Deprecated(since = "11.3", forRemoval = true) public static DemangledObject demangle(String mangled) { List demanglers = getDemanglers(); for (Demangler demangler : demanglers) { try { - DemangledObject demangledObject = demangler.demangle(mangled); + MangledContext mangledContext = + demangler.createMangledContext(mangled, null, null, null); + DemangledObject demangledObject = demangler.demangle(mangledContext); if (demangledObject != null) { return demangledObject; } @@ -60,18 +80,24 @@ public class DemanglerUtil { } /** - * Locates all available demanglers and checks to see if the supplied program is + * Deprecated. Use {@link #demangle(Program, String, Address)}. See class header for more + * details. + * + * Locates all available demanglers and checks to see if the supplied program is * supported, then it attempts to demangle. - * - *

This method will use only the default options for demangling. If you need to + * + *

This method will use only the default options for demangling. If you need to * specify options, then you will have to call each specific demangler directly, creating - * the options specifically needed for each demangler. See + * the options and mangled context specifically needed for each demangler. See + * {@link Demangler#createMangledContext(String, DemanglerOptions, Program, Address)} and * {@link Demangler#createDefaultOptions()}. - * + * * @param program the program containing the mangled name * @param mangled the mangled name * @return the demangled object or null + * @deprecated see above */ + @Deprecated(since = "11.3", forRemoval = true) public static DemangledObject demangle(Program program, String mangled) { List demanglers = getDemanglers(); for (Demangler demangler : demanglers) { @@ -80,7 +106,9 @@ public class DemanglerUtil { continue; } - DemangledObject demangledObject = demangler.demangle(mangled); + MangledContext mangledContext = + demangler.createMangledContext(mangled, null, null, null); + DemangledObject demangledObject = demangler.demangle(mangledContext); if (demangledObject != null) { return demangledObject; } @@ -92,9 +120,48 @@ public class DemanglerUtil { return null; } + /** + * Locates all available demanglers and checks to see if the supplied program is + * supported, then it attempts to demangle. Returns a list of {@link DemangledObject} of + * successful demanglings + * + *

This method will use only the default options for demangling. If you need to + * specify options, then you will have to call each specific demangler directly, creating + * the options and mangled context specifically needed for each demangler. See + * {@link Demangler#createMangledContext(String, DemanglerOptions, Program, Address)} and + * {@link Demangler#createDefaultOptions()}. + * + * @param program the program containing the mangled name + * @param mangled the mangled name + * @param address the address of the mangled name + * @return the list of {@link DemangledObject} + */ + public static List demangle(Program program, String mangled, Address address) { + List results = new ArrayList<>(); + List demanglers = getDemanglers(); + for (Demangler demangler : demanglers) { + try { + if (!demangler.canDemangle(program)) { + continue; + } + + MangledContext mangledContext = + demangler.createMangledContext(mangled, null, program, address); + DemangledObject demangledObject = demangler.demangle(mangledContext); + if (demangledObject != null) { + results.add(demangledObject); + } + } + catch (DemangledException e) { + // ignore + } + } + return results; + } + /** * Dynamically locates all available demangler implementations. - * + * * @return a list of all demanglers */ private static List getDemanglers() { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/MangledContext.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/MangledContext.java new file mode 100644 index 0000000000..6c0cbc781c --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/MangledContext.java @@ -0,0 +1,80 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.util.demangler; + +import java.util.Objects; + +import ghidra.program.model.address.Address; +import ghidra.program.model.listing.Program; + +/** + * A simple class to contain the context of a mangled symbol for demangling + */ +public class MangledContext { + + protected Program program; + protected DemanglerOptions options; + protected String mangled; + protected Address address; + + /** + * Constructor for mangled context + * @param program the program; can be null + * @param options the demangler options + * @param mangled the mangled string + * @param address the address; can be null + */ + public MangledContext(Program program, DemanglerOptions options, String mangled, + Address address) { + this.program = program; + this.options = Objects.requireNonNull(options, "Options cannot be null"); + this.mangled = Objects.requireNonNull(mangled, "Mangled cannot be null"); + this.address = address; + } + + /** + * Returns the program + * @return the program; can be null + */ + public Program getProgram() { + return program; + } + + /** + * Returns the demangler options + * @return the options + */ + public DemanglerOptions getOptions() { + return options; + } + + /** + * Returns the mangled string + * @return the mangled string + */ + public String getMangled() { + return mangled; + } + + /** + * Returns the address + * @return the address; can be null + */ + public Address getAddress() { + return address; + } + +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/AbstractPeDebugLoader.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/AbstractPeDebugLoader.java index 8dbed8d3db..813204067f 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/AbstractPeDebugLoader.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/AbstractPeDebugLoader.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -259,15 +259,21 @@ abstract class AbstractPeDebugLoader extends AbstractOrdinalSupportLoader { } private void demangle(Address address, String name, Program program) { - DemangledObject demangledObj = null; + StringBuilder builder = new StringBuilder(); try { - demangledObj = DemanglerUtil.demangle(program, name); + List demangledObjects = DemanglerUtil.demangle(program, name, address); + for (DemangledObject demangledObj : demangledObjects) { + if (builder.length() > 0) { + builder.append("\t"); + } + builder.append(demangledObj.getSignature(true)); + } } catch (Exception e) { //log.appendMsg("Unable to demangle: "+name); } - if (demangledObj != null) { - setComment(CodeUnit.PLATE_COMMENT, address, demangledObj.getSignature(true)); + if (builder.length() > 0) { + setComment(CodeUnit.PLATE_COMMENT, address, builder.toString()); } } @@ -426,7 +432,7 @@ abstract class AbstractPeDebugLoader extends AbstractOrdinalSupportLoader { Symbol newSymbol = program.getSymbolTable().createLabel(address, sym, SourceType.IMPORTED); - // Force non-section symbols to be primary. We never want section symbols (.text, + // Force non-section symbols to be primary. We never want section symbols (.text, // .text$func_name) to be primary because we don't want to use them for function names // or demangling. if (!sym.equals(section.getName()) && !sym.startsWith(section.getName() + "$")) { diff --git a/Ghidra/Features/GnuDemangler/ghidra_scripts/DemangleElfWithOptionScript.java b/Ghidra/Features/GnuDemangler/ghidra_scripts/DemangleElfWithOptionScript.java index 82dc4981c7..7d5db9e56a 100644 --- a/Ghidra/Features/GnuDemangler/ghidra_scripts/DemangleElfWithOptionScript.java +++ b/Ghidra/Features/GnuDemangler/ghidra_scripts/DemangleElfWithOptionScript.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -21,6 +21,7 @@ //@category Examples.Demangler import ghidra.app.script.GhidraScript; import ghidra.app.util.demangler.DemangledObject; +import ghidra.app.util.demangler.MangledContext; import ghidra.app.util.demangler.gnu.*; import ghidra.program.model.symbol.Symbol; @@ -56,7 +57,9 @@ public class DemangleElfWithOptionScript extends GhidraScript { options = options.withDemanglerFormat(GnuDemanglerFormat.ARM, true); */ - DemangledObject demangledObject = demangler.demangle(mangled, options); + MangledContext mangledContext = + demangler.createMangledContext(mangled, options, currentProgram, currentAddress); + DemangledObject demangledObject = demangler.demangle(mangledContext); if (demangledObject == null) { println("Could not demangle: " + mangled); return; diff --git a/Ghidra/Features/GnuDemangler/ghidra_scripts/VxWorksSymTab_5_4.java b/Ghidra/Features/GnuDemangler/ghidra_scripts/VxWorksSymTab_5_4.java index f0af2fe43e..759e8e824f 100644 --- a/Ghidra/Features/GnuDemangler/ghidra_scripts/VxWorksSymTab_5_4.java +++ b/Ghidra/Features/GnuDemangler/ghidra_scripts/VxWorksSymTab_5_4.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -53,6 +53,7 @@ import java.util.List; import ghidra.app.cmd.label.DemanglerCmd; import ghidra.app.script.GhidraScript; import ghidra.app.util.demangler.DemangledException; +import ghidra.app.util.demangler.MangledContext; import ghidra.app.util.demangler.gnu.GnuDemangler; import ghidra.program.model.address.Address; import ghidra.program.model.mem.Memory; @@ -143,7 +144,9 @@ public class VxWorksSymTab_5_4 extends GhidraScript { String symDemangledName = null; try { // if successful, symDemangledName will be non-NULL - symDemangledName = demangler.demangle(symName).getSignature(false); + MangledContext mangledContext = demangler.createMangledContext(symDemangledName, + null, currentProgram, symNameAddr); + symDemangledName = demangler.demangle(mangledContext).getSignature(false); } catch (DemangledException e) { // if symName wasn't a mangled name, silently continue diff --git a/Ghidra/Features/GnuDemangler/ghidra_scripts/VxWorksSymTab_6_1.java b/Ghidra/Features/GnuDemangler/ghidra_scripts/VxWorksSymTab_6_1.java index beeeab2a94..2bcc6678e5 100644 --- a/Ghidra/Features/GnuDemangler/ghidra_scripts/VxWorksSymTab_6_1.java +++ b/Ghidra/Features/GnuDemangler/ghidra_scripts/VxWorksSymTab_6_1.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -51,6 +51,7 @@ import java.util.List; import ghidra.app.cmd.label.DemanglerCmd; import ghidra.app.script.GhidraScript; import ghidra.app.util.demangler.DemangledException; +import ghidra.app.util.demangler.MangledContext; import ghidra.app.util.demangler.gnu.GnuDemangler; import ghidra.program.model.address.Address; import ghidra.program.model.mem.Memory; @@ -139,7 +140,9 @@ public class VxWorksSymTab_6_1 extends GhidraScript { String symDemangledName = null; try { // if successful, symDemangledName will be non-NULL - symDemangledName = demangler.demangle(symName).getSignature(false); + MangledContext mangledContext = demangler.createMangledContext(symDemangledName, + null, currentProgram, symNameAddr); + symDemangledName = demangler.demangle(mangledContext).getSignature(false); } catch (DemangledException e) { // if symName wasn't a mangled name, silently continue diff --git a/Ghidra/Features/GnuDemangler/ghidra_scripts/VxWorksSymTab_Finder.java b/Ghidra/Features/GnuDemangler/ghidra_scripts/VxWorksSymTab_Finder.java index 9988dfc075..1fae02a542 100644 --- a/Ghidra/Features/GnuDemangler/ghidra_scripts/VxWorksSymTab_Finder.java +++ b/Ghidra/Features/GnuDemangler/ghidra_scripts/VxWorksSymTab_Finder.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -49,6 +49,7 @@ import ghidra.app.plugin.core.analysis.AutoAnalysisManager; import ghidra.app.script.GhidraScript; import ghidra.app.services.DataTypeManagerService; import ghidra.app.util.demangler.DemangledException; +import ghidra.app.util.demangler.MangledContext; import ghidra.app.util.demangler.gnu.GnuDemangler; import ghidra.program.model.address.Address; import ghidra.program.model.address.AddressSet; @@ -604,7 +605,7 @@ public class VxWorksSymTab_Finder extends GhidraScript { /** * Look before/after the table to see if there is a size value there and mark it if it agrees with TableLen - * + * * @param symTbl * @param vxSymbol * @param tableLen @@ -784,7 +785,9 @@ public class VxWorksSymTab_Finder extends GhidraScript { // Demangle symName String symDemangledName = null; try { - symDemangledName = demangler.demangle(symName).getSignature(false); + MangledContext mangledContext = + demangler.createMangledContext(symName, null, currentProgram, symNameAddr); + symDemangledName = demangler.demangle(mangledContext).getSignature(false); } catch (DemangledException e) { // report demangling error if (!e.isInvalidMangledName()) { 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 8cd55ecd91..31beb384f0 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 @@ -73,10 +73,9 @@ public class GnuDemanglerAnalyzer extends AbstractDemanglerAnalyzer { private GnuDemanglerFormat demanglerFormat = GnuDemanglerFormat.AUTO; private boolean useDeprecatedDemangler = false; - private GnuDemangler demangler = new GnuDemangler(); - public GnuDemanglerAnalyzer() { super(NAME, DESCRIPTION); + demangler = new GnuDemangler(); setDefaultEnablement(true); } @@ -142,9 +141,9 @@ public class GnuDemanglerAnalyzer extends AbstractDemanglerAnalyzer { } @Override - protected DemangledObject doDemangle(String mangled, DemanglerOptions demanglerOtions, - MessageLog log) throws DemangledException { - return demangler.demangle(mangled, demanglerOtions); + protected DemangledObject doDemangle(MangledContext mangledContext, MessageLog log) + throws DemangledException { + return demangler.demangle(mangledContext); } //================================================================================================== diff --git a/Ghidra/Features/GnuDemangler/src/main/java/ghidra/app/util/demangler/gnu/GnuDemangler.java b/Ghidra/Features/GnuDemangler/src/main/java/ghidra/app/util/demangler/gnu/GnuDemangler.java index 2e84fcb1f1..48a8098322 100644 --- a/Ghidra/Features/GnuDemangler/src/main/java/ghidra/app/util/demangler/gnu/GnuDemangler.java +++ b/Ghidra/Features/GnuDemangler/src/main/java/ghidra/app/util/demangler/gnu/GnuDemangler.java @@ -66,19 +66,27 @@ public class GnuDemangler implements Demangler { } @Override - @Deprecated(since = "9.2", forRemoval = true) - public DemangledObject demangle(String mangled, boolean demangleOnlyKnownPatterns) + @Deprecated(since = "11.3", forRemoval = true) + public DemangledObject demangle(String mangled, DemanglerOptions demanglerOptions) throws DemangledException { - GnuDemanglerOptions options = new GnuDemanglerOptions(); - options.setDemangleOnlyKnownPatterns(demangleOnlyKnownPatterns); - return demangle(mangled, options); + MangledContext mangledContext = createMangledContext(mangled, demanglerOptions, null, null); + return demangle(mangledContext); } @Override - public DemangledObject demangle(String mangled, DemanglerOptions demanglerOtions) + public DemangledObject demangle(MangledContext mangledContext) + throws DemangledException { + DemangledObject demangled = demangleInternal(mangledContext); + demangled.setMangledContext(mangledContext); + return demangled; + } + + private DemangledObject demangleInternal(MangledContext mangledContext) throws DemangledException { - GnuDemanglerOptions options = getGnuOptions(demanglerOtions); + DemanglerOptions demanglerOptions = mangledContext.getOptions(); + String mangled = mangledContext.getMangled(); + GnuDemanglerOptions options = getGnuOptions(demanglerOptions); if (skip(mangled, options)) { return null; } diff --git a/Ghidra/Features/MicrosoftCodeAnalyzer/build.gradle b/Ghidra/Features/MicrosoftCodeAnalyzer/build.gradle index aef84ea4de..d02f4f681b 100644 --- a/Ghidra/Features/MicrosoftCodeAnalyzer/build.gradle +++ b/Ghidra/Features/MicrosoftCodeAnalyzer/build.gradle @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -22,6 +22,6 @@ apply plugin: 'eclipse' eclipse.project.name = 'Features MicrosoftCodeAnalyzer' dependencies { - api project(":MicrosoftDmang") + api project(":MicrosoftDemangler") api project(":Base") } diff --git a/Ghidra/Features/MicrosoftCodeAnalyzer/src/main/java/ghidra/app/cmd/data/TypeDescriptorModel.java b/Ghidra/Features/MicrosoftCodeAnalyzer/src/main/java/ghidra/app/cmd/data/TypeDescriptorModel.java index 7815cfcfa3..8502b5ca43 100644 --- a/Ghidra/Features/MicrosoftCodeAnalyzer/src/main/java/ghidra/app/cmd/data/TypeDescriptorModel.java +++ b/Ghidra/Features/MicrosoftCodeAnalyzer/src/main/java/ghidra/app/cmd/data/TypeDescriptorModel.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -19,6 +19,7 @@ import ghidra.app.cmd.data.rtti.RttiUtil; import ghidra.app.util.datatype.microsoft.DataValidationOptions; import ghidra.app.util.datatype.microsoft.MSDataTypeUtils; import ghidra.app.util.demangler.*; +import ghidra.app.util.demangler.microsoft.MicrosoftDemangler; import ghidra.docking.settings.SettingsImpl; import ghidra.program.model.address.*; import ghidra.program.model.data.*; @@ -33,8 +34,6 @@ import ghidra.util.Msg; import ghidra.util.exception.AssertException; import ghidra.util.exception.CancelledException; import ghidra.util.task.TaskMonitor; -import mdemangler.MDException; -import mdemangler.MDMangGhidra; /** * Model for the TypeDescriptor data type. @@ -446,7 +445,8 @@ public class TypeDescriptorModel extends AbstractCreateDataTypeModel { Object value = terminatedStringDt.getValue(nameMemBuffer, SettingsImpl.NO_SETTINGS, 1); if (value instanceof String) { originalTypeName = (String) value; - demangledDataType = getDemangledDataType(originalTypeName); // Can be null + // The returned demangledDataType an be null + demangledDataType = getDemangledDataType(originalTypeName, program, nameAddress); } hasProcessedName = true; return originalTypeName; @@ -597,20 +597,22 @@ public class TypeDescriptorModel extends AbstractCreateDataTypeModel { /** * Gets a DemangledDataType for the indicated mangled string * @param mangledString the mangled string to be demangled + * @param program the program + * @param address address of the mangled string * @return the DemangledDataType or null if couldn't demangle or is not a class type */ - private static DemangledDataType getDemangledDataType(String mangledString) { - MDMangGhidra demangler = new MDMangGhidra(); + private static DemangledDataType getDemangledDataType(String mangledString, Program program, + Address address) { + MicrosoftDemangler demangler = new MicrosoftDemangler(); try { - // Note that we could play with the return value, but it is not needed; instead, we - // get the DemangledDataType by calling the appropriate method - demangler.demangleType(mangledString, true); - DemangledDataType demangledType = demangler.getDataType(); + MangledContext mangledContext = + demangler.createMangledContext(mangledString, null, program, address); + DemangledDataType demangledType = demangler.demangleType(mangledContext); if (isPermittedType(demangledType)) { return demangledType; } } - catch (MDException e) { + catch (DemangledException e) { // Couldn't demangle. } return null; diff --git a/Ghidra/Features/MicrosoftDemangler/developer_scripts/MicrosoftDemanglerScript.java b/Ghidra/Features/MicrosoftDemangler/developer_scripts/MicrosoftDemanglerScript.java index 97b1eae8ec..a74b8b034a 100644 --- a/Ghidra/Features/MicrosoftDemangler/developer_scripts/MicrosoftDemanglerScript.java +++ b/Ghidra/Features/MicrosoftDemangler/developer_scripts/MicrosoftDemanglerScript.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -17,6 +17,7 @@ //@category Symbol import ghidra.app.script.GhidraScript; import ghidra.app.util.demangler.DemangledObject; +import ghidra.app.util.demangler.MangledContext; import ghidra.app.util.demangler.microsoft.MicrosoftDemangler; public class MicrosoftDemanglerScript extends GhidraScript { @@ -33,7 +34,7 @@ public class MicrosoftDemanglerScript extends GhidraScript { demangle("??$?0G@?$allocator@U_Container_proxy@std@@@std@@QAE@ABV?$allocator@G@1@@Z"); /* - + demangle("??0__non_rtti_object@@QAE@PBD@Z"); demangle("??0bad_cast@@AAE@PBQBD@Z"); demangle("??0bad_cast@@QAE@ABQBD@Z"); @@ -94,7 +95,11 @@ public class MicrosoftDemanglerScript extends GhidraScript { } private void demangle(String mangled) throws Exception { - DemangledObject demangled = demangler.demangle(mangled); - printf("magled %s\ndemangled %s", mangled, demangled); + // Using a null address instead of currentAddress because we are not demangling a symbol + // at the address... just a symbol to dump to the output + MangledContext mangledContext = + demangler.createMangledContext(mangled, null, currentProgram, null); + DemangledObject demangled = demangler.demangle(mangledContext); + printf("mangled %s\ndemangled %s", mangled, demangled); } } diff --git a/Ghidra/Features/MicrosoftDemangler/src/main/java/ghidra/app/plugin/core/analysis/MicrosoftDemanglerAnalyzer.java b/Ghidra/Features/MicrosoftDemangler/src/main/java/ghidra/app/plugin/core/analysis/MicrosoftDemanglerAnalyzer.java index 7cc6e2ecd0..ef10a5c9c5 100644 --- a/Ghidra/Features/MicrosoftDemangler/src/main/java/ghidra/app/plugin/core/analysis/MicrosoftDemanglerAnalyzer.java +++ b/Ghidra/Features/MicrosoftDemangler/src/main/java/ghidra/app/plugin/core/analysis/MicrosoftDemanglerAnalyzer.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -16,10 +16,11 @@ package ghidra.app.plugin.core.analysis; import ghidra.app.util.demangler.*; -import ghidra.app.util.demangler.microsoft.MicrosoftDemangler; +import ghidra.app.util.demangler.microsoft.*; import ghidra.app.util.importer.MessageLog; import ghidra.framework.options.Options; import ghidra.program.model.listing.Program; +import ghidra.util.HelpLocation; /** * A version of the demangler analyzer to handle microsoft symbols @@ -40,12 +41,26 @@ public class MicrosoftDemanglerAnalyzer extends AbstractDemanglerAnalyzer { private static final String OPTION_DESCRIPTION_APPLY_CALLING_CONVENTION = "Apply any recovered function signature calling convention"; + private static final String OPTION_NAME_DEMANGLE_USE_KNOWN_PATTERNS = + "Demangle Only Known Mangled Symbols"; + private static final String OPTION_DESCRIPTION_USE_KNOWN_PATTERNS = + "Only demangle symbols that follow known compiler mangling patterns. " + + "Leaving this option off may cause non-mangled symbols to get demangled."; + + public static final String OPTION_NAME_MS_C_INTERPRETATION = + "C-Style Symbol Interpretation"; + private static final String OPTION_DESCRIPTION_MS_C_INTERPRETATION = + "When ambiguous, treat C-Style mangled symbol as: function, variable," + + " or function if a function exists"; + private boolean applyFunctionSignature = true; private boolean applyCallingConvention = true; - private MicrosoftDemangler demangler = new MicrosoftDemangler(); + private boolean demangleOnlyKnownPatterns = false; + private MsCInterpretation interpretation = MsCInterpretation.FUNCTION_IF_EXISTS; public MicrosoftDemanglerAnalyzer() { super(NAME, DESCRIPTION); + demangler = new MicrosoftDemangler(); setDefaultEnablement(true); } @@ -56,11 +71,20 @@ public class MicrosoftDemanglerAnalyzer extends AbstractDemanglerAnalyzer { @Override public void registerOptions(Options options, Program program) { - options.registerOption(OPTION_NAME_APPLY_SIGNATURE, applyFunctionSignature, null, + + HelpLocation help = new HelpLocation("AutoAnalysisPlugin", "Demangler_Analyzer"); + + options.registerOption(OPTION_NAME_APPLY_SIGNATURE, applyFunctionSignature, help, OPTION_DESCRIPTION_APPLY_SIGNATURE); - options.registerOption(OPTION_NAME_APPLY_CALLING_CONVENTION, applyCallingConvention, null, + options.registerOption(OPTION_NAME_APPLY_CALLING_CONVENTION, applyCallingConvention, help, OPTION_DESCRIPTION_APPLY_CALLING_CONVENTION); + + options.registerOption(OPTION_NAME_DEMANGLE_USE_KNOWN_PATTERNS, demangleOnlyKnownPatterns, + help, OPTION_DESCRIPTION_USE_KNOWN_PATTERNS); + + options.registerOption(OPTION_NAME_MS_C_INTERPRETATION, interpretation, help, + OPTION_DESCRIPTION_MS_C_INTERPRETATION); } @Override @@ -70,20 +94,28 @@ public class MicrosoftDemanglerAnalyzer extends AbstractDemanglerAnalyzer { 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 protected DemanglerOptions getOptions() { - DemanglerOptions options = new DemanglerOptions(); + MicrosoftDemanglerOptions options = new MicrosoftDemanglerOptions(); options.setApplySignature(applyFunctionSignature); options.setApplyCallingConvention(applyCallingConvention); + options.setDemangleOnlyKnownPatterns(demangleOnlyKnownPatterns); + options.setInterpretation(interpretation); + options.setErrorOnRemainingChars(true); return options; } @Override - protected DemangledObject doDemangle(String mangled, DemanglerOptions options, MessageLog log) + protected DemangledObject doDemangle(MangledContext context, MessageLog log) throws DemangledException { - DemangledObject demangled = demangler.demangle(mangled, options); + DemangledObject demangled = demangler.demangle(context); return demangled; } } diff --git a/Ghidra/Features/MicrosoftDemangler/src/main/java/ghidra/app/util/demangler/microsoft/MicrosoftDemangler.java b/Ghidra/Features/MicrosoftDemangler/src/main/java/ghidra/app/util/demangler/microsoft/MicrosoftDemangler.java index 2e879d17b1..4ba4ff5b6a 100644 --- a/Ghidra/Features/MicrosoftDemangler/src/main/java/ghidra/app/util/demangler/microsoft/MicrosoftDemangler.java +++ b/Ghidra/Features/MicrosoftDemangler/src/main/java/ghidra/app/util/demangler/microsoft/MicrosoftDemangler.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -18,18 +18,29 @@ package ghidra.app.util.demangler.microsoft; import ghidra.app.util.demangler.*; import ghidra.app.util.opinion.MSCoffLoader; import ghidra.app.util.opinion.PeLoader; +import ghidra.program.model.address.Address; import ghidra.program.model.listing.Program; -import mdemangler.MDException; -import mdemangler.MDMangGhidra; +import mdemangler.*; +import mdemangler.datatype.MDDataType; /** * A class for demangling debug symbols created using Microsoft Visual Studio. */ public class MicrosoftDemangler implements Demangler { + private MDMangGhidra demangler; + private MDParsableItem item; + private DemangledObject object; + private MDDataType mdType; + private DemangledDataType dataType; + public MicrosoftDemangler() { } + // Note: Consider deprecating this method and creating one that takes the MangledContext. + // Another option might be to find a smarter, utility method that contains the complete + // knowledge of when a particular demangler is appropriate.. but that would have to consider + // demanglers written by others. @Override public boolean canDemangle(Program program) { String executableFormat = program.getExecutableFormat(); @@ -38,47 +49,128 @@ public class MicrosoftDemangler implements Demangler { } @Override - @Deprecated(since = "9.2", forRemoval = true) - public DemangledObject demangle(String mangled, boolean demangleOnlyKnownPatterns) + @Deprecated(since = "11.3", forRemoval = true) + public DemangledObject demangle(String mangled, DemanglerOptions options) throws DemangledException { - try { - DemangledObject demangled = demangleMS(mangled, demangleOnlyKnownPatterns); - return demangled; - } - catch (DemangledException e) { - throw new DemangledException(true); - } + MangledContext mangledContext = new MangledContext(null, options, mangled, null); + return demangle(mangledContext); } @Override - public DemangledObject demangle(String mangled, DemanglerOptions options) - throws DemangledException { + public DemangledObject demangle(MangledContext context) throws DemangledException { + if (!(context instanceof MicrosoftMangledContext mContext)) { + throw new DemangledException("Wrong context type"); + } + if (!(context.getOptions() instanceof MicrosoftDemanglerOptions options)) { + throw new DemangledException("MicrosoftDemanglerOptions expected"); + } + String mangled = context.getMangled(); + demangler = new MDMangGhidra(); + demangler.setMangledSymbol(mangled); + demangler.setErrorOnRemainingChars(options.errorOnRemainingChars()); + demangler.setDemangleOnlyKnownPatterns(options.demangleOnlyKnownPatterns()); + demangler.setArchitectureSize(mContext.getArchitectureSize()); + demangler.setIsFunction(mContext.shouldInterpretAsFunction()); try { - DemangledObject demangled = demangleMS(mangled, options.demangleOnlyKnownPatterns()); - return demangled; - } - catch (DemangledException e) { - throw new DemangledException(true); - } - } - - private DemangledObject demangleMS(String mangled, boolean demangleOnlyKnownPatterns) - throws DemangledException { - if (mangled == null || mangled.length() == 0) { - throw new DemangledException(true); - } - - MDMangGhidra demangler = new MDMangGhidra(); - try { - demangler.demangle(mangled, true, demangleOnlyKnownPatterns); - DemangledObject object = demangler.getObject(); + item = demangler.demangle(); + object = MicrosoftDemanglerUtil.convertToDemangledObject(item, mangled); + if (object != null) { + object.setMangledContext(context); + } return object; } catch (MDException e) { - DemangledException de = new DemangledException("Unable to demangle symbol: " + mangled); + DemangledException de = new DemangledException(true); de.initCause(e); throw de; } } + + /** + * Attempts to demangle the type string of the mangled context into a type + * + * @param context the mangled context + * @return the result + * @throws DemangledException if the string cannot be demangled + */ + public DemangledDataType demangleType(MangledContext context) throws DemangledException { + if (!(context instanceof MicrosoftMangledContext mContext)) { + throw new DemangledException("Wrong context type"); + } + if (!(context.getOptions() instanceof MicrosoftDemanglerOptions options)) { + throw new DemangledException("MicrosoftDemanglerOptions expected"); + } + String mangled = context.getMangled(); + + demangler = new MDMangGhidra(); + demangler.setMangledSymbol(mangled); + demangler.setErrorOnRemainingChars(options.errorOnRemainingChars()); + demangler.setDemangleOnlyKnownPatterns(options.demangleOnlyKnownPatterns()); + demangler.setArchitectureSize(mContext.getArchitectureSize()); + demangler.setIsFunction(mContext.shouldInterpretAsFunction()); + try { + mdType = demangler.demangleType(); + dataType = MicrosoftDemanglerUtil.convertToDemangledDataType(mdType, mangled); + if (dataType != null) { + dataType.setMangledContext(context); + } + return dataType; + } + catch (MDException e) { + DemangledException de = new DemangledException(true); + de.initCause(e); + throw de; + } + } + + /** + * Returns the {@link MDParsableItem} used in demangling to a {@link DemangledObject} + * @return the item; can be null if item wasn't demangled + */ + public MDParsableItem getMdItem() { + return item; + } + + /** + * Returns the {@link MDDataType} used in demangling to a @link DemangledDataType} + * @return the type; can be null if type wasn't demangled + */ + public MDDataType getMdType() { + return mdType; + } + + /** + * Creates default options for microsoft demangler + * @return the options + */ + @Override + public MicrosoftDemanglerOptions createDefaultOptions() { + return new MicrosoftDemanglerOptions(); + } + + /** + * Creates a microsoft mangled context + * @param mangled the mangled name + * @param options the demangler options; if null, the default options are created + * @param program the program; can be null + * @param address the address for the name in the program; can be null + * @return the mangled context + */ + @Override + public MicrosoftMangledContext createMangledContext(String mangled, DemanglerOptions options, + Program program, Address address) { + return new MicrosoftMangledContext(program, getMicrosoftOptions(options), mangled, address); + } + + private MicrosoftDemanglerOptions getMicrosoftOptions(DemanglerOptions options) { + if (options instanceof MicrosoftDemanglerOptions mOptions) { + return mOptions; + } + if (options == null) { + return createDefaultOptions(); + } + return new MicrosoftDemanglerOptions(options); + } + } diff --git a/Ghidra/Features/MicrosoftDemangler/src/main/java/ghidra/app/util/demangler/microsoft/MicrosoftDemanglerOptions.java b/Ghidra/Features/MicrosoftDemangler/src/main/java/ghidra/app/util/demangler/microsoft/MicrosoftDemanglerOptions.java new file mode 100644 index 0000000000..ecf8360f8c --- /dev/null +++ b/Ghidra/Features/MicrosoftDemangler/src/main/java/ghidra/app/util/demangler/microsoft/MicrosoftDemanglerOptions.java @@ -0,0 +1,109 @@ +/* ### + * 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; + +import ghidra.app.util.demangler.DemanglerOptions; + +/** + * Microsoft demangler options + */ +public class MicrosoftDemanglerOptions extends DemanglerOptions { + + private boolean errorOnRemainingChars; + private MsCInterpretation interpretation; + + /** + * Default constructor for 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(); + this.errorOnRemainingChars = errorOnRemainingChars; + } + + /** + * Copy constructor to create a version of this class from a more generic set of options + * @param copy the options to copy + */ + public MicrosoftDemanglerOptions(DemanglerOptions copy) { + super(copy); + + if (copy instanceof MicrosoftDemanglerOptions mCopy) { + errorOnRemainingChars = mCopy.errorOnRemainingChars; + interpretation = mCopy.interpretation; + } + else { + errorOnRemainingChars = true; + interpretation = MsCInterpretation.FUNCTION_IF_EXISTS; + } + } + + /** + * Sets the control for erroring on remaining characters at demangler completion + * @param errorOnRemainingCharsArg {@code true} to error when remaining characters exist + */ + public void setErrorOnRemainingChars(boolean errorOnRemainingCharsArg) { + errorOnRemainingChars = errorOnRemainingCharsArg; + } + + /** + * Returns {@code true} if the process will error when remaining characters exist at the end + * of processing + * @return {@code true} if will error + */ + public boolean errorOnRemainingChars() { + return errorOnRemainingChars; + } + + /** + * 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 String toString() { + //@formatter:off + return "{\n" + + "\tdoDisassembly: " + doDisassembly() + ",\n" + + "\tapplySignature: " + applySignature() + ",\n" + + "\terrorOnRemainingChars: " + errorOnRemainingChars + ",\n" + + "\tinterpretation: " + interpretation + ",\n" + + "\tdemangleOnlyKnownPatterns: " + demangleOnlyKnownPatterns() + ",\n" + + "}"; + //@formatter:on + } +} diff --git a/Ghidra/Features/MicrosoftDemangler/src/main/java/ghidra/app/util/demangler/microsoft/MicrosoftDemanglerUtil.java b/Ghidra/Features/MicrosoftDemangler/src/main/java/ghidra/app/util/demangler/microsoft/MicrosoftDemanglerUtil.java new file mode 100644 index 0000000000..d31121fe2a --- /dev/null +++ b/Ghidra/Features/MicrosoftDemangler/src/main/java/ghidra/app/util/demangler/microsoft/MicrosoftDemanglerUtil.java @@ -0,0 +1,975 @@ +/* ### + * 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; + +import java.util.Iterator; + +import ghidra.app.util.demangler.*; +import ghidra.program.model.lang.CompilerSpec; +import ghidra.program.model.symbol.SourceType; +import mdemangler.*; +import mdemangler.datatype.MDDataType; +import mdemangler.datatype.MDVarArgsType; +import mdemangler.datatype.complex.*; +import mdemangler.datatype.extended.MDArrayReferencedType; +import mdemangler.datatype.modifier.*; +import mdemangler.functiontype.*; +import mdemangler.naming.*; +import mdemangler.object.*; +import mdemangler.template.MDTemplateNameAndArguments; +import mdemangler.typeinfo.*; + +/** + * A utility class to aid the MicrosoftDemangler + *

+ * The contents of this class that do the processing came from {@link MDMangGhidra}, and will + * likely go through future rounds of clean-up. {@link MDMangGhidra}, an extension of + * {@link MDMang}, might eventually be removed. + */ +public class MicrosoftDemanglerUtil { + + private MicrosoftDemanglerUtil() { + // purposefully empty + } + + /** + * Method to convert an {@link MDParsableItem} into a {@link DemangledObject}. This method + * is not appropriate for {@link MDDataType} and some other types of {@link MDParsableItem} + * @param item the item to convert + * @param mangled the original mangled string + * @return the {@link DemangledObject} result + * @throws DemangledException up issue converting to a {@link DemangledObject} + */ + static DemangledObject convertToDemangledObject(MDParsableItem item, String mangled) + throws DemangledException { + return processItem(item, mangled, item.toString()); + } + + /** + * Method to convert an {@link MDDataType} into a {@link DemangledDataType} + * @param type the type to convert + * @param mangled the original mangled string + * @return the result + */ + static DemangledDataType convertToDemangledDataType(MDDataType type, String mangled) { + return processDataType(null, type, mangled, type.toString()); + } + + //============================================================================================== + + private static Demangled processNamespace(MDQualifiedName qualifiedName, String mangled, + String demangledSource) { + return processNamespace(qualifiedName.getQualification(), mangled, demangledSource); + } + + private static Demangled processNamespace(MDQualification qualification, String mangled, + String demangledSource) { + Iterator it = qualification.iterator(); + if (!it.hasNext()) { + return null; + } + + MDQualifier qual = it.next(); + Demangled type = getDemangled(qual, mangled, demangledSource); + Demangled current = type; + // Note that qualifiers come in reverse order, from most refined to root being the last + while (it.hasNext()) { + qual = it.next(); + Demangled parent = getDemangled(qual, mangled, demangledSource); + current.setNamespace(parent); + current = parent; + } + return type; + } + + private static Demangled getDemangled(MDQualifier qual, String mangled, + String demangledSource) { + Demangled demangled = null; + if (qual.isNested()) { + String subMangled = qual.getNested().getMangled(); + MDObjectCPP obj = qual.getNested().getNestedObject(); + if (!obj.isHashObject()) { + MDTypeInfo typeInfo = obj.getTypeInfo(); + MDType type = typeInfo.getMDType(); + if (type instanceof MDDataType dt) { + demangled = new DemangledType(subMangled, qual.toString(), qual.toString()); + } + else if (type instanceof MDFunctionType ft) { + // We currently cannot handle functions as part of a namespace, so we will just + // treat the demangled function namespace string as a plain namespace. + //demangled = new DemangledFunction(subMangled, qual.toString(), qual.toString()); + demangled = + new DemangledNamespaceNode(subMangled, qual.toString(), qual.toString()); + } + } + if (demangled == null) { + demangled = + new DemangledNamespaceNode(subMangled, qual.toString(), qual.toString()); + } + } + else if (qual.isAnon()) { + // Instead of using the standard qual.toString() method, which returns + // "`anonymous namespace'" for anonymous qualifiers, we use qual.getAnonymousName() + // which will have the underlying anonymous name of the form "A0xfedcba98" to create + // a standardized anonymous name that is distinguishable from other anonymous names. + // The standardized name comes from createStandardAnonymousNamespaceNode(). This + // is especially important when there are sibling anonymous names. + String anon = MDMangUtils.createStandardAnonymousNamespaceNode(qual.getAnonymousName()); + demangled = new DemangledNamespaceNode(mangled, qual.toString(), anon); + } + else if (qual.isInterface()) { + // TODO: need to do better; setting namespace for now + demangled = new DemangledNamespaceNode(mangled, qual.toString(), qual.toString()); + } + else if (qual.isNameQ()) { + // TODO: need to do better; setting namespace for now, as it looks like interface + demangled = new DemangledNamespaceNode(mangled, qual.toString(), qual.toString()); + } + else if (qual.isNameC()) { + // TODO: need to do better; setting type for now, but not processed yet and not sure + // what it is + demangled = new DemangledType(mangled, qual.toString(), qual.toString()); + } + else if (qual.isLocalNamespace()) { + String local = + MDMangUtils.createStandardLocalNamespaceNode(qual.getLocalNamespaceNumber()); + demangled = new DemangledNamespaceNode(mangled, qual.toString(), local); + } + else { + demangled = new DemangledNamespaceNode(mangled, qual.toString(), qual.toString()); + } + return demangled; + } + + private static DemangledObject processItem(MDParsableItem item, String mangled, + String demangledSource) throws DemangledException { + DemangledObject result = null; + if (item instanceof MDObjectReserved) { + result = processObjectReserved((MDObjectReserved) item, mangled, demangledSource); + } + else if (item instanceof MDObjectCodeView codeView) { + result = processObjectCPP(codeView, mangled, demangledSource); + result.setSpecialPrefix(codeView.getPrefix()); + } + else if (item instanceof MDObjectCPP objCpp) { // Base class of MDObjectBracket/MDObjectCodeView. + result = processObjectCPP(objCpp, mangled, demangledSource); + } + else if (item instanceof MDObjectC objC) { + result = processObjectC(objC, mangled, demangledSource); + } + else if (item instanceof MDDataType dataType) { + // TODO: how do we fix this? DemangledDataType extends DemangledType, but not + // DemangleObject... + throw new DemangledException("DemangledDataType instead of DemangledObject"); + //result = processDataType(null, dataType, mangled, demangledSource); + // object = getDemangledDataType(); + } + else if (item instanceof MDTemplateNameAndArguments templateNameAndArgs) { + result = processTemplate(templateNameAndArgs, mangled, demangledSource); + } + return result; + } + + private static DemangledObject processObjectReserved( + MDObjectReserved objectReserved, + String mangled, String demangledSource) { + DemangledObject object = null; + if (objectReserved.getClass().equals(MDObjectReserved.class)) { + //Testing if the class is not a derived class of MDObjectReserved; + // In other words, is it exactly a MDObjectReserved? + // If so, then return null, which will allow it to get processed + // outside of the demangler. + return null; + } + if (objectReserved instanceof MDObjectBracket objectBracket) { + MDObjectCPP objectCPP = objectBracket.getObjectCPP(); + object = processObjectCPP(objectCPP, mangled, demangledSource); + object.setSpecialPrefix(objectBracket.getPrefix()); + } + //TODO: put other objectReserved derivative types here and return something that Ghidra + // can use. + else { + object = + new DemangledUnknown(mangled, demangledSource, objectReserved.toString()); + } + return object; + } + + private static DemangledObject processObjectC(MDObjectC objectC, String mangled, + String demangledSource) { + // 20240905: modification to MDObjectC to processing C-style mangling has been added. + // If null is returned here, then we have a standard variable. + DemangledFunction demangledFunction = + processObjectCFunction(objectC, mangled, demangledSource); + if (demangledFunction != null) { + return demangledFunction; + } + // We are returning null here because we do not want Ghidra to put up a plate + // comment for a standard C symbol. + // FUTURE WORK: After discussion, easiest way to deal with this for now (without + // exploding work into other demanglers) is to keep the "return null" for now. + // The problem is with the DemangledObject making a revision of the + // success/failure of demangling by doing a comparison of the input string to + // the output string in the applyTo() method. In a previous encoding, I moved + // this logic into other demanglers and set a flag in DemangledObject, that way + // my MDMangGhidra could set a flag to succeed when we have a C-language variable + // (vs. C++) where the input == output is valid. We didn't like this pattern. + // The better way forward, which will require digging into the other demanglers + // further (keeping this for future work), is to throw an exception on failure + // instead of returning null as well as pushing this success/failure logic + // upstream (where I was attempting to put it) and removing the input == output + // test from DemangledObject; an object is only returned upon success and no + // rescinding of the success determination is made later. + return null; + // Following is the code that we had originally intended to use. + // DemangledVariable variable = new DemangledVariable(objectC.toString()); + // return variable; + } + + private static DemangledFunction processObjectCFunction(MDObjectC objectC, String mangled, + String demangledSource) { + String callingConvention = objectC.getCallingConvention(); + if (callingConvention == null) { + // null means it is a standard variable; not a function + return null; + } + DemangledFunction function = + new DemangledFunction(mangled, demangledSource, objectC.getName()); + // Setting the signature SourceType to DEFAULT allows us to set the calling convention + // without changing or locking in parameters or return type. + function.setSignatureSourceType(SourceType.DEFAULT); + function.setCallingConvention(callingConvention); + return function; + } + + private static DemangledObject processObjectCPP(MDObjectCPP objectCPP, String mangled, + String demangledSource) { + MDTypeInfo typeinfo = objectCPP.getTypeInfo(); + DemangledObject result = null; + if (typeinfo != null) { + if (typeinfo instanceof MDVariableInfo) { + DemangledVariable variable; + MDVariableInfo variableInfo = (MDVariableInfo) typeinfo; + MDType mdtype = variableInfo.getMDType(); + DemangledDataType dt = + processDataType(null, (MDDataType) mdtype, mangled, demangledSource); + if ("std::nullptr_t".equals(dt.getName())) { + variable = new DemangledVariable(mangled, demangledSource, ""); + } + else { + variable = + new DemangledVariable(mangled, demangledSource, objectCPP.getName()); + variable.setNamespace( + processNamespace(objectCPP.getQualification(), mangled, demangledSource)); + } + variable.setDatatype(dt); + result = variable; + variable.setConst(variableInfo.isConst()); + variable.setVolatile(variableInfo.isVolatile()); + variable.setPointer64(variableInfo.isPointer64()); + if (variableInfo.isRestrict()) { + variable.setRestrict(); + } + if (variableInfo.isUnaligned()) { + variable.setUnaligned(); + } + variable.setBasedName(variableInfo.getBasedName()); + if (variableInfo.isMember()) { + variable.setMemberScope(variableInfo.getMemberScope()); + } + } + else if (typeinfo instanceof MDFunctionInfo) { + if (typeinfo.getSpecialHandlingCode() == 'F') { + result = new DemangledUnknown(mangled, demangledSource, null); + } + else { + DemangledFunction function = + new DemangledFunction(mangled, demangledSource, objectCPP.getName()); + function.setSignatureSourceType(SourceType.IMPORTED); + function.setNamespace( + processNamespace(objectCPP.getQualification(), mangled, demangledSource)); + result = function; + processFunction((MDFunctionInfo) typeinfo, function, mangled, + demangledSource); + // Any other special values to be set? + if (typeinfo instanceof MDMemberFunctionInfo) { + if (typeinfo instanceof MDVCall) { + // Empty for now--placeholder for possible future logic. + } + else if (typeinfo instanceof MDVFAdjustor) { + // Empty for now--placeholder for possible future logic. + } + else if (typeinfo instanceof MDVtordisp) { + // Empty for now--placeholder for possible future logic. + } + else if (typeinfo instanceof MDVtordispex) { + // Empty for now--placeholder for possible future logic. + } + else { + // plain member function + } + } + else { + // global function + } + } + } + else if (typeinfo instanceof MDVxTable) { //Includes VFTable, VBTable, and RTTI4 + MDVxTable vxtable = (MDVxTable) typeinfo; + DemangledVariable variable = + new DemangledVariable(mangled, demangledSource, objectCPP.getName()); + variable.setNamespace( + processNamespace(objectCPP.getQualification(), mangled, demangledSource)); + variable.setConst(vxtable.isConst()); + variable.setVolatile(vxtable.isVolatile()); + variable.setPointer64(vxtable.isPointer64()); + result = variable; + // The following code would be an alternative, depending on whether we get + // customer complaints or other fall-out from having created a variable here. + //resultObject = new DemangledUnknown(); + } + else if (typeinfo instanceof AbstractMDMetaClass) { //Includes all RTTI, except RTTI4 + DemangledVariable variable = + new DemangledVariable(mangled, demangledSource, objectCPP.getName()); + variable.setNamespace( + processNamespace(objectCPP.getQualification(), mangled, demangledSource)); + result = variable; + // The following code would be an alternative, depending on whether we get + // customer complaints or other fall-out from having created a variable here. + //resultObject = new DemangledUnknown(); + } + else if (typeinfo instanceof MDGuard) { + DemangledVariable variable = + new DemangledVariable(mangled, demangledSource, objectCPP.getName()); + variable.setNamespace( + processNamespace(objectCPP.getQualification(), mangled, demangledSource)); + result = variable; + // The following code would be an alternative, depending on whether we get + // customer complaints or other fall-out from having created a variable here. + //resultObject = new DemangledUnknown(); + } + else { + // Any others (e.g., case '9') + DemangledVariable variable = + new DemangledVariable(mangled, demangledSource, objectCPP.getName()); + variable.setNamespace( + processNamespace(objectCPP.getQualification(), mangled, demangledSource)); + result = variable; + // The following code would be an alternative, depending on whether we get + // customer complaints or other fall-out from having created a variable here. + //resultObject = new DemangledUnknown(); + } + if (typeinfo.isPrivate()) { + result.setVisibilty("private"); + } + else if (typeinfo.isProtected()) { + result.setVisibilty("protected"); + } + else if (typeinfo.isPublic()) { + result.setVisibilty("public"); + } + result.setStatic(typeinfo.isStatic()); + result.setVirtual(typeinfo.isVirtual()); + result.setThunk(typeinfo.isThunk()); + if (typeinfo.isExternC()) { + result.setSpecialPrefix("extern \"C\""); + } + } + else { + String baseName = objectCPP.getName(); + if (objectCPP.isString()) { + MDString mstring = objectCPP.getMDString(); + DemangledString demangledString = + new DemangledString(mangled, demangledSource, mstring.getName(), + mstring.toString(), mstring.getLength(), mstring.isUnicode()); + result = demangledString; + } + else if (baseName.length() != 0) { + DemangledVariable variable; + variable = new DemangledVariable(mangled, demangledSource, baseName); + variable.setNamespace( + processNamespace(objectCPP.getQualification(), mangled, demangledSource)); + result = variable; + } + } + return result; + // //Various RTTI types (MDType '8' or '9') + // DemangledVariable variable = + // new + // DemangledVariable(objectCPP.getQualifiedName().getBasicName().toString()); + // variable.setNamespace(processNamespace(objectCPP.getQualifiedName())); + // return variable; + // TODO: fill in lots of object.____ items + // object.setVisibilty(typeinfo.g); + // object.setConst(isConst); + } + + // I think that this is a kludge. The mapping of MDTemplateNameAndArguments + // doesn't match + // well to the current DemangledObject hierarchy. + private static DemangledVariable processTemplate(MDTemplateNameAndArguments template, + String mangled, String demangledSource) { + DemangledVariable variable = + new DemangledVariable(mangled, demangledSource, template.toString()); + // NO NAMESPACE for high level template: variable.setNamespace(XXX); + // DemangledTemplate objectTemplate = new DemangledTemplate(); + // DemangledDataType dataType = new DemangledDataType((String) null); + // MDTemplateArgumentsList args = template.getArgumentsList(); + // if (args != null) { + // for (int index = 0; index < args.getNumArgs(); index++) { + // objectTemplate.addParameter(processDataType(null, (MDDataType) + // args.getArg(index))); + // } + // } + // dataType.setTemplate(objectTemplate); + // variable.setDatatype(dataType); + return variable; + } + + private static DemangledFunction processFunction(MDFunctionInfo functionInfo, + DemangledFunction function, String mangled, String demangledSource) { + MDFunctionType functionType = (MDFunctionType) functionInfo.getMDType(); + String convention = functionType.getCallingConvention().toString(); + if ("__cdecl".equals(convention) && functionInfo.isMember() && !functionInfo.isStatic()) { + // TODO: ultimately the presence of a 'this' parareter will not be keyed + // to the calling convention, but for now we need to force it + convention = CompilerSpec.CALLING_CONVENTION_thiscall; + } + function.setCallingConvention(convention); + if (functionType.hasReturn() && functionType.getReturnType() != null) { + MDDataType retType = functionType.getReturnType(); + if (!retType.toString().isEmpty()) { + function.setReturnType(processDataType(null, retType, mangled, demangledSource)); + } + } + MDArgumentsList args = functionType.getArgumentsList(); + if (functionType.hasArgs() && args != null) { + for (int index = 0; index < args.getNumArgs(); index++) { + function.addParameter( + new DemangledParameter( + processDataType(null, args.getArg(index), mangled, demangledSource))); + } + } + if (functionType.isTypeCast()) { + function.setTypeCast(); + } + // function.setVirtual(functionType.isVirtual()); + // function.setStatic(functionType.isStatic()); + // if (functionType.isPrivate()) { + // function.setVisibilty("private"); + // } + // else if (functionType.isProtected()) { + // function.setVisibilty("protected"); + // } + // else if (functionType.isPublic()) { + // function.setVisibilty("public"); + // } + + // TODO: fix this kludge. Need to add appropriate suffixes to DemangledFunction (look + // at DemangledFunctionPointer?). Missing other possible suffixes from + // functionType.getCVMod(). + // String suffix = ""; + MDCVMod thisPointerCVMod = functionType.getThisPointerCVMod(); + if (thisPointerCVMod != null) { + if (thisPointerCVMod.isConst()) { + function.setTrailingConst(); + } + if (thisPointerCVMod.isVolatile()) { + function.setTrailingVolatile(); + } + if (thisPointerCVMod.isPointer64()) { + function.setTrailingPointer64(); + } + if (thisPointerCVMod.isRestricted()) { + function.setTrailingRestrict(); + } + if (thisPointerCVMod.isUnaligned()) { + function.setTrailingUnaligned(); + } + } + MDThrowAttribute ta = functionType.getThrowAttribute(); + if (ta != null) { + function.setThrowAttribute(ta.toString()); + } + + // TODO: fill in lots of function.____ items + return function; + } + + private static DemangledFunctionPointer processDemangledFunctionPointer( + MDPointerType pointerType, String mangled, String demangledSource) { + DemangledFunctionPointer functionPointer = + new DemangledFunctionPointer(mangled, demangledSource); + MDFunctionType functionType = (MDFunctionType) pointerType.getReferencedType(); + functionPointer.setCallingConvention(functionType.getCallingConvention().toString()); + functionPointer.setModifier(pointerType.getCVMod().toString()); + if (functionType.hasReturn() && functionType.getReturnType() != null) { + functionPointer.setReturnType( + processDataType(null, functionType.getReturnType(), mangled, demangledSource)); + } + MDArgumentsList args = functionType.getArgumentsList(); + if (functionType.hasArgs() && args != null) { + for (int index = 0; index < args.getNumArgs(); index++) { + functionPointer.addParameter( + processDataType(null, args.getArg(index), mangled, demangledSource)); + } + } + MDCVMod thisPointerCVMod = functionType.getThisPointerCVMod(); + if (thisPointerCVMod != null) { + if (thisPointerCVMod.isConst()) { + functionPointer.setConst(); + } + if (thisPointerCVMod.isVolatile()) { + functionPointer.setVolatile(); + } + if (thisPointerCVMod.isPointer64()) { + functionPointer.setTrailingPointer64(); + } + if (thisPointerCVMod.isRestricted()) { + functionPointer.setTrailingRestrict(); + } + if (thisPointerCVMod.isUnaligned()) { + functionPointer.setTrailingUnaligned(); + } + } + // TODO: fill in lots of functionPointer.____ items + return functionPointer; + } + + private static DemangledFunctionReference processDemangledFunctionReference( + MDModifierType refType, String mangled, String demangledSource) { + if (!((refType instanceof MDReferenceType) || + (refType instanceof MDDataRightReferenceType))) { + return null; // Not planning on anything else yet. + } + DemangledFunctionReference functionReference = + new DemangledFunctionReference(mangled, demangledSource); + MDFunctionType functionType = (MDFunctionType) refType.getReferencedType(); + functionReference.setCallingConvention(functionType.getCallingConvention().toString()); + functionReference.setModifier(refType.getCVMod().toString()); + if (functionType.hasReturn() && functionType.getReturnType() != null) { + functionReference.setReturnType( + processDataType(null, functionType.getReturnType(), mangled, demangledSource)); + } + MDArgumentsList args = functionType.getArgumentsList(); + if (functionType.hasArgs() && args != null) { + for (int index = 0; index < args.getNumArgs(); index++) { + functionReference.addParameter( + processDataType(null, args.getArg(index), mangled, demangledSource)); + } + } + // TODO: fill in lots of functionReference.____ items + return functionReference; + } + + private static DemangledFunctionIndirect processDemangledFunctionIndirect( + MDFunctionIndirectType functionIndirectType, String mangled, String demangledSource) { + DemangledFunctionIndirect functionDefinition = + new DemangledFunctionIndirect(mangled, demangledSource); + MDFunctionType functionType = (MDFunctionType) functionIndirectType.getReferencedType(); + functionDefinition.setCallingConvention(functionType.getCallingConvention().toString()); + functionDefinition.setModifier(functionIndirectType.getCVMod().toString()); + functionDefinition.incrementPointerLevels(); + if (functionType.hasReturn() && functionType.getReturnType() != null) { + functionDefinition.setReturnType( + processDataType(null, functionType.getReturnType(), mangled, demangledSource)); + } + MDArgumentsList args = functionType.getArgumentsList(); + if (functionType.hasArgs() && args != null) { + for (int index = 0; index < args.getNumArgs(); index++) { + functionDefinition.addParameter( + processDataType(null, args.getArg(index), mangled, demangledSource)); + } + } + // TODO: fill in lots of functionIndirect.____ items + return functionDefinition; + } + + // The following is/might be a kludge: using DemangledFunctionIndirect to see if it will + // hold the things that we need; regardless, the follow-on use of the DemangledFunction + // indirect might be clouded between the real, two underlying types. + private static DemangledFunctionIndirect processDemangledFunctionQuestion( + MDModifierType modifierType, String mangled, String demangledSource) { + DemangledFunctionIndirect functionDefinition = + new DemangledFunctionIndirect(mangled, demangledSource); + MDFunctionType functionType = (MDFunctionType) modifierType.getReferencedType(); + functionDefinition.setCallingConvention(functionType.getCallingConvention().toString()); + functionDefinition.setModifier(modifierType.getCVMod().toString()); + functionDefinition.incrementPointerLevels(); + if (functionType.hasReturn() && functionType.getReturnType() != null) { + functionDefinition.setReturnType( + processDataType(null, functionType.getReturnType(), mangled, demangledSource)); + } + MDArgumentsList args = functionType.getArgumentsList(); + if (functionType.hasArgs() && args != null) { + for (int index = 0; index < args.getNumArgs(); index++) { + functionDefinition.addParameter( + processDataType(null, args.getArg(index), mangled, demangledSource)); + } + } + // TODO: fill in lots of functionIndirect.____ items + return functionDefinition; + } + + // Passing "DemangledDataType resultDataType" in is a kludge, as this is done so + // incrementPointerLevels() can be used, but doing this recursion like this loses all + // storageClass information from the various nested pointers and such. TODO: need to add + // a "pointer type" with a contained "referenced data type" to DemangledObject (perhaps + // PointerObject?) + private static DemangledDataType processDataType(DemangledDataType resultDataType, + MDDataType datatype, String mangled, String demangledSource) { + if (resultDataType == null) { + resultDataType = + new DemangledDataType(mangled, demangledSource, getDataTypeName(datatype)); + } + if (datatype.isSpecifiedSigned()) { + // Returns true if default signed or specified signed. TODO: There is no place to + // capture default signed versus specified signed (i.e., there are three types of + // char: default signed, specified signed, and unsigned) + resultDataType.setSigned(); + } + if (datatype.isUnsigned()) { + resultDataType.setUnsigned(); + } + + // Bunch of else-ifs for exclusive types + if (datatype instanceof MDModifierType) { + MDModifierType modifierType = (MDModifierType) datatype; + // if (modifierType.isBased()) { + // resultDataType.set___(); + // modifierType.getCVMod().getBasedName(); + // } + if (modifierType.isConst()) { + resultDataType.setConst(); + } + if (modifierType.isVolatile()) { + resultDataType.setVolatile(); + } + if (modifierType.isPointer64()) { + resultDataType.setPointer64(); + } + if (modifierType.isRestrict()) { + resultDataType.setRestrict(); + } + if (modifierType.isUnaligned()) { + resultDataType.setUnaligned(); + } + resultDataType.setBasedName(modifierType.getBasedName()); + // if (modifierType.isMember()) { + resultDataType.setMemberScope(modifierType.getMemberScope()); + // } + // TODO: fix. Following is a kludge because DemangledObject has no DemangledReference + // with corresponding referencedType. + if (modifierType instanceof MDArrayBasicType) { + resultDataType.setArray(1); + if ((modifierType.getReferencedType() instanceof MDFunctionType)) { + // MDType ref = modifierType.getReferencedType(); + // TODO: A demangled function reference is needed here. + // DemangledFunction function = new + // DemangledFunction(objectCPP.getQualifiedName().getBasicName().toString()); + // function.setNamespace(processNamespace(objectCPP.getQualifiedName())); + // //resultObject = function; + // return processFunction(ref, resultDataType); + } + else if (modifierType.getReferencedType() instanceof MDDataType) { + return processDataType(resultDataType, + (MDDataType) modifierType.getReferencedType(), mangled, demangledSource); + } + else { + // Empty for now--placeholder for possible future logic. + } + } + else if (modifierType instanceof MDPointerType) { + if ((modifierType.getReferencedType() instanceof MDFunctionType)) { + // TODO---------what are we returning... need to work on called routine. + DemangledFunctionPointer fp = + processDemangledFunctionPointer((MDPointerType) modifierType, mangled, + demangledSource); + // TODO: fix. Following is a kludge because DemangledObject has no + // DemangledPointer with corresponding referencedType. + for (int i = 0; i < resultDataType.getPointerLevels(); i++) { + fp.incrementPointerLevels(); + } + if (resultDataType.isConst()) { + fp.setConst(); + } + if (resultDataType.isVolatile()) { + fp.setVolatile(); + } + if (resultDataType.isPointer64()) { + fp.setPointer64(); + } + return fp; + } + // modifierType.getArrayString(); + // resultDataType.setArray(); + //Processing the referenced type (for Ghidra, and then setting attributes on it) + DemangledDataType newResult = + processDataType(resultDataType, (MDDataType) modifierType.getReferencedType(), + mangled, demangledSource); + newResult.incrementPointerLevels(); + if (modifierType.getCVMod().isConst()) { + newResult.setConst(); + } + if (modifierType.getCVMod().isVolatile()) { + newResult.setVolatile(); + } + if (modifierType.getCVMod().isPointer64()) { + newResult.setPointer64(); + } + return newResult; + } + // TODO: fix. Following is a kludge because DemangledObject has no + // DemangledReference + // with corresponding referencedType. + else if (modifierType instanceof MDReferenceType) { + // TODO---------what are we returning... need to work on called + // routine. + if ((modifierType.getReferencedType() instanceof MDFunctionType)) { + DemangledFunctionReference fr = + processDemangledFunctionReference(modifierType, mangled, demangledSource); + // TODO: fix. Following is a kludge because DemangledObject has no + // DemangledPointer with corresponding referencedType. + for (int i = 0; i < resultDataType.getPointerLevels(); i++) { + fr.incrementPointerLevels(); + } + if (resultDataType.isConst()) { + fr.setConst(); + } + if (resultDataType.isVolatile()) { + fr.setVolatile(); + } + if (resultDataType.isPointer64()) { + fr.setPointer64(); + } + return fr; + } + //Processing the referenced type (for Ghidra, and then setting attributes on it) + DemangledDataType newResult = + processDataType(resultDataType, (MDDataType) modifierType.getReferencedType(), + mangled, demangledSource); + newResult.setLValueReference(); + if (modifierType.getCVMod().isConst()) { + newResult.setConst(); + } + if (modifierType.getCVMod().isVolatile()) { + newResult.setVolatile(); + } + if (modifierType.getCVMod().isPointer64()) { + newResult.setPointer64(); + } + return newResult; + } + // TODO: fix. Following is a kludge because DemangledObject has no DemangledReference + // with corresponding referencedType. + else if (modifierType instanceof MDFunctionIndirectType) { + // TODO---------what are we returning... need to work on called routine. + DemangledFunctionIndirect fd = + processDemangledFunctionIndirect((MDFunctionIndirectType) modifierType, mangled, + demangledSource); + for (int i = 0; i < resultDataType.getPointerLevels(); i++) { + fd.incrementPointerLevels(); + } + if (resultDataType.isConst()) { + fd.setConst(); + } + if (resultDataType.isVolatile()) { + fd.setVolatile(); + } + if (resultDataType.isPointer64()) { + fd.setPointer64(); + } + return fd; + } + else if (modifierType instanceof MDPointerRefDataType) { + resultDataType.setName(getDataTypeName(datatype)); + // Not sure if this is the correct thing to do for MDPointerRefDataType, but we + // are just going to assign the referred-to type: + //Processing the referenced type (for Ghidra, and then setting attributes on it) + return processDataType(resultDataType, + (MDDataType) modifierType.getReferencedType(), mangled, demangledSource); + } + else if (modifierType instanceof MDDataReferenceType) { + // Not sure if this is the correct thing to do for MDDataReferenceType, but we + // are just going to assign the referred-to type: + //Processing the referenced type (for Ghidra, and then setting attributes on it) + processDataType(resultDataType, (MDDataType) modifierType.getReferencedType(), + mangled, demangledSource); + if (modifierType.getCVMod().isConst()) { + resultDataType.setConst(); + } + if (modifierType.getCVMod().isVolatile()) { + resultDataType.setVolatile(); + } + return resultDataType; + } + else if (modifierType instanceof MDDataRightReferenceType) { + if ((modifierType.getReferencedType() instanceof MDFunctionType)) { + resultDataType.setName(getDataTypeName(datatype)); + // TODO---------what are we returning... need to work on called routine. + DemangledFunctionReference fr = + processDemangledFunctionReference(modifierType, mangled, demangledSource); + // TODO: fix. Following is a kludge because DemangledObject has no + // DemangledPointer with corresponding referencedType. + for (int i = 0; i < resultDataType.getPointerLevels(); i++) { + fr.incrementPointerLevels(); + } + if (resultDataType.isConst()) { + fr.setConst(); + } + if (resultDataType.isVolatile()) { + fr.setVolatile(); + } + if (resultDataType.isPointer64()) { + fr.setPointer64(); + } + return fr; + } + //Processing the referenced type (for Ghidra, and then setting attributes on it) + processDataType(resultDataType, (MDDataType) modifierType.getReferencedType(), + mangled, demangledSource); + resultDataType.setRValueReference(); + if (modifierType.getCVMod().isConst()) { + resultDataType.setConst(); + } + if (modifierType.getCVMod().isVolatile()) { + resultDataType.setVolatile(); + } + if (modifierType.getCVMod().isPointer64()) { + resultDataType.setPointer64(); + } + return resultDataType; + } + else { + // not pointer, reference, or array type + if ((modifierType.getReferencedType() instanceof MDFunctionType)) { + // TODO---------what are we returning... need to work on called routine. + DemangledFunctionIndirect fx = + processDemangledFunctionQuestion(modifierType, mangled, demangledSource); + // TODO: fix. Following is a kludge because DemangledObject has no + // DemangledPointer with corresponding referencedType. + if (resultDataType.isConst()) { + fx.setConst(); + } + if (resultDataType.isVolatile()) { + fx.setVolatile(); + } + if (resultDataType.isPointer64()) { + fx.setPointer64(); + } + return fx; + } + // resultDataType.incrementPointerLevels();//Not sure if we should do/use this. + DemangledDataType dataType = + processDataType(resultDataType, (MDDataType) modifierType.getReferencedType(), + mangled, demangledSource); + if (modifierType.getCVMod().isConst()) { + resultDataType.setConst(); + } + if (modifierType.getCVMod().isVolatile()) { + resultDataType.setVolatile(); + } + if (modifierType.getCVMod().isPointer64()) { + resultDataType.setPointer64(); + } + return dataType; + } + } + else if (datatype instanceof MDComplexType) { + MDComplexType complexType = (MDComplexType) datatype; + // Hope this is correct... will return "class" or other + resultDataType.setName(complexType.getNamespace().getName()); + // TODO: setNamespace() wants a "DemangledType" for a namespace. + // Two problems: + // 1) we don't have an appropriate method to use + // 2) not sure DemangledType is appropriate; in MDComplexType we have an + // MDQualification--not an MDQualifiedName + resultDataType.setNamespace( + processNamespace(complexType.getNamespace(), mangled, demangledSource)); + + // Bunch of else-ifs for exclusive types + if (datatype instanceof MDEnumType) { + resultDataType.setEnum(); + // Put in underlying type (for sizing too). + MDEnumType enumType = (MDEnumType) datatype; + resultDataType.setEnumType(enumType.getUnderlyingFullTypeName()); + } + else if (datatype instanceof MDClassType) { + resultDataType.setClass(); + } + else if (datatype instanceof MDStructType) { + resultDataType.setStruct(); + } + else if (datatype instanceof MDUnionType) { + resultDataType.setUnion(); + } + else if (datatype instanceof MDCoclassType) { + resultDataType.setCoclass(); + } + else if (datatype instanceof MDCointerfaceType) { + resultDataType.setCointerface(); + } + } + else if (datatype instanceof MDReferenceType) { + resultDataType.setLValueReference(); + } + else if (datatype instanceof MDArrayBasicType) { + resultDataType.setArray(1); + } + else if (datatype instanceof MDVarArgsType) { + resultDataType.setVarArgs(); + } + else if (datatype instanceof MDArrayReferencedType arrRefType) { + return processDataType(resultDataType, arrRefType.getReferencedType(), mangled, + demangledSource); + } + else if (datatype instanceof MDStdNullPtrType) { + resultDataType.setName(datatype.toString()); + } + else { + // MDDataType + // TODO MDW64Type needs repeated reference type parsing, just as modifier types need + // them. + resultDataType.setName(getDataTypeName(datatype)); + } + // TODO: No place to indicate a general pointer--we can indicate Pointer64 + // TODO: Not sure if anything fits this: resultDataType.setComplex(); + // TODO: resultDataType.setTemplate(); //TODO: Not sure templates are data types + // according to how MSFT demangles them. + // TODO: resultDataType.setTemplate(null); //TODO: Not sure templates are data types + // according to how MSFT demangles them. + + return resultDataType; + } + + /** + * Returns either a formal type name or a representative type name to fill into a + * MangledDataType if the formal name is blank + * @return the name + */ + private static String getDataTypeName(MDDataType dataType) { + String name = dataType.getName(); + if (!name.isBlank()) { + return name; + } + name = dataType.getTypeName(); + if (!name.isBlank()) { + return name; + } + return dataType.toString(); + } + +} diff --git a/Ghidra/Features/MicrosoftDemangler/src/main/java/ghidra/app/util/demangler/microsoft/MicrosoftMangledContext.java b/Ghidra/Features/MicrosoftDemangler/src/main/java/ghidra/app/util/demangler/microsoft/MicrosoftMangledContext.java new file mode 100644 index 0000000000..e260ef8a37 --- /dev/null +++ b/Ghidra/Features/MicrosoftDemangler/src/main/java/ghidra/app/util/demangler/microsoft/MicrosoftMangledContext.java @@ -0,0 +1,77 @@ +/* ### + * 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; + +import ghidra.app.util.demangler.MangledContext; +import ghidra.program.model.address.Address; +import ghidra.program.model.listing.Function; +import ghidra.program.model.listing.Program; + +/** + * A simple class to contain the context of a mangled symbol for demangling + */ +public class MicrosoftMangledContext extends MangledContext { + + /** + * Constructor for mangled context + * @param program the program; can be null + * @param options the demangler options + * @param mangled the mangled string + * @param address the address; can be null + */ + public MicrosoftMangledContext(Program program, MicrosoftDemanglerOptions options, + String mangled, Address address) { + super(program, options, mangled, address); + } + + /** + * Returns the program architecture size + * @return the architecture size or zero if not known (program is null) + */ + public int getArchitectureSize() { + if (program == null) { + return 0; + } + return program.getAddressFactory().getDefaultAddressSpace().getSize(); + } + + /** + * Returns whether the symbol should be interpreted as a function + * @return {@code true} if should be interpreted as a function + */ + boolean shouldInterpretAsFunction() { + MsCInterpretation control = + ((MicrosoftDemanglerOptions) options).getInterpretation(); + return switch (control) { + case FUNCTION -> true; + case NON_FUNCTION -> false; + case FUNCTION_IF_EXISTS -> getExistingFunction() != null; + default -> throw new AssertionError("Invalid case"); + }; + } + + /** + * Returns the function at the context address + * @return the function or null if program or address is null + */ + private Function getExistingFunction() { + if (program == null || address == null) { + return null; + } + return program.getFunctionManager().getFunctionAt(address); + } + +} diff --git a/Ghidra/Features/MicrosoftDemangler/src/main/java/ghidra/app/util/demangler/microsoft/MsCInterpretation.java b/Ghidra/Features/MicrosoftDemangler/src/main/java/ghidra/app/util/demangler/microsoft/MsCInterpretation.java new file mode 100644 index 0000000000..3010ead1a2 --- /dev/null +++ b/Ghidra/Features/MicrosoftDemangler/src/main/java/ghidra/app/util/demangler/microsoft/MsCInterpretation.java @@ -0,0 +1,37 @@ +/* ### + * 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; + +/** + * Class to control whether a symbol should be demangled as a function symbols or a some other + * (e.g., variable) symbol + */ +public enum MsCInterpretation { + /** + * Forces processing as a function symbol if there are multiple symbol interpretations + */ + FUNCTION, + /** + * Forces processing as a non-function (e.g., variable) if there are multiple symbol + * interpretations + */ + NON_FUNCTION, + /** + * If there are multiple symbol interpretations, forces processing as a function only if + * there is already a function at the address + */ + FUNCTION_IF_EXISTS +} diff --git a/Ghidra/Features/MicrosoftDemangler/src/test/java/ghidra/app/util/demangler/microsoft/MicrosoftDemanglerExtraTest.java b/Ghidra/Features/MicrosoftDemangler/src/test/java/ghidra/app/util/demangler/microsoft/MicrosoftDemanglerExtraTest.java new file mode 100644 index 0000000000..6470c8f337 --- /dev/null +++ b/Ghidra/Features/MicrosoftDemangler/src/test/java/ghidra/app/util/demangler/microsoft/MicrosoftDemanglerExtraTest.java @@ -0,0 +1,428 @@ +/* ### + * 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; + +import static org.junit.Assert.*; + +import org.junit.*; + +import generic.test.AbstractGenericTest; +import ghidra.app.util.demangler.*; +import ghidra.program.database.ProgramBuilder; +import ghidra.program.database.ProgramDB; +import ghidra.program.model.address.Address; +import ghidra.program.model.data.VoidDataType; +import mdemangler.MDMangBaseTest; +import mdemangler.object.MDObjectC; + +/** + * This class performs extra demangler testing for special cases that do not fit + * the testing pattern found in {@link MDMangBaseTest} and {@link MicrosoftDemanglerTest} + */ +public class MicrosoftDemanglerExtraTest extends AbstractGenericTest { + + ProgramBuilder builder32; + ProgramBuilder builder64; + private ProgramDB program32; + private ProgramDB program64; + + private Address address32; + private Address functionAddress32; + private Address address64; + private Address functionAddress64; + + @Before + public void setUp() throws Exception { + String blockAddress = "0x01001000"; + String nonFunctionAddress = "0x01001000"; + String functionAddress = "0x01001010"; + + builder32 = new ProgramBuilder("test32", "x86:LE:32:default"); + builder32.createMemory(".text", blockAddress, 0x100); + builder32.createEmptyFunction("function", functionAddress, 1, VoidDataType.dataType); + program32 = builder32.getProgram(); + address32 = program32.getAddressFactory().getAddress(nonFunctionAddress); + functionAddress32 = program32.getAddressFactory().getAddress(functionAddress); + + builder64 = new ProgramBuilder("test64", "x86:LE:64:default"); + builder64.createMemory(".text", blockAddress, 0x100); + builder64.createEmptyFunction("function", functionAddress, 1, VoidDataType.dataType); + program64 = builder64.getProgram(); + address64 = program64.getAddressFactory().getAddress(nonFunctionAddress); + functionAddress64 = program64.getAddressFactory().getAddress(functionAddress); + } + + @After + public void tearDown() throws Exception { + builder32.dispose(); + builder64.dispose(); + } + + //============================================================================================== + // Helpers + + private void processWith32Function(String mangled, String expectDemangled, + String expectedFunction, String expectedConvention, int expectedNumBytes) + throws DemangledException { + MicrosoftDemangler demangler = new MicrosoftDemangler(); + + MicrosoftMangledContext context = + demangler.createMangledContext(mangled, null, program32, functionAddress32); + // We do not need to do this here: options.setErrorOnRemainingChars(false); + + // Testing Demangled hierarchy + DemangledFunction demangledFunction = (DemangledFunction) demangler.demangle(context); + assertEquals(expectedFunction, + demangledFunction == null ? null : demangledFunction.toString()); + + // Testing MDMang hierarchy + MDObjectC objc = (MDObjectC) demangler.getMdItem(); + String convention = objc.getCallingConvention(); + int numParameterBytes = objc.getNumParameterBytes(); + assertEquals(expectedConvention, convention); + assertEquals(expectedNumBytes, numParameterBytes); + assertEquals(expectDemangled, objc.toString()); + } + + private void processWith32NonFunction(String mangled, String expectDemangled) + throws DemangledException { + MicrosoftDemangler demangler = new MicrosoftDemangler(); + + MicrosoftMangledContext context = + demangler.createMangledContext(mangled, null, program32, address32); + MicrosoftDemanglerOptions options = (MicrosoftDemanglerOptions) context.getOptions(); + // Important to set to false to standardize our test results to simple expected + // results. When the C-style symbols do not create function results either because + // there is not a function at the address or because of the architecture, we might end + // up with remaining charactes because the demangler sets its index back tot he start. + options.setErrorOnRemainingChars(false); + + // Testing Demangled hierarchy + DemangledFunction demangledFunction = (DemangledFunction) demangler.demangle(context); + assertEquals(null, demangledFunction); + + // Testing MDMang hierarchy + MDObjectC objc = (MDObjectC) demangler.getMdItem(); + String convention = objc.getCallingConvention(); + int numParameterBytes = objc.getNumParameterBytes(); + assertEquals(null, convention); + assertEquals(0, numParameterBytes); + assertEquals(expectDemangled, objc.toString()); + } + + private void processWith64Function(String mangled, String expectDemangled, + String expectedFunction, String expectedConvention, int expectedNumBytes) + throws DemangledException { + MicrosoftDemangler demangler = new MicrosoftDemangler(); + + MicrosoftMangledContext context = + demangler.createMangledContext(mangled, null, program64, functionAddress64); + MicrosoftDemanglerOptions options = (MicrosoftDemanglerOptions) context.getOptions(); + // Important to set to false to standardize our test results to simple expected + // results. When the C-style symbols do not create function results either because + // there is not a function at the address or because of the architecture, we might end + // up with remaining charactes because the demangler sets its index back tot he start. + options.setErrorOnRemainingChars(false); + + // Testing Demangled hierarchy + DemangledFunction demangledFunction = (DemangledFunction) demangler.demangle(context); + assertEquals(expectedFunction, + demangledFunction == null ? null : demangledFunction.toString()); + + // Testing MDMang hierarchy + MDObjectC objc = (MDObjectC) demangler.getMdItem(); + String convention = objc.getCallingConvention(); + int numParameterBytes = objc.getNumParameterBytes(); + assertEquals(expectedConvention, convention); + assertEquals(expectedNumBytes, numParameterBytes); + assertEquals(expectDemangled, objc.toString()); + } + + private void processWith64NonFunction(String mangled, String expectDemangled) + throws DemangledException { + MicrosoftDemangler demangler = new MicrosoftDemangler(); + + MicrosoftMangledContext context = + demangler.createMangledContext(mangled, null, program64, address64); + MicrosoftDemanglerOptions options = (MicrosoftDemanglerOptions) context.getOptions(); + // Important to set to false to standardize our test results to simple expected + // results. When the C-style symbols do not create function results either because + // there is not a function at the address or because of the architecture, we might end + // up with remaining charactes because the demangler sets its index back tot he start. + options.setErrorOnRemainingChars(false); + + // Testing Demangled hierarchy + DemangledFunction demangledFunction = (DemangledFunction) demangler.demangle(context); + assertEquals(null, demangledFunction); + + // Testing MDMang hierarchy + MDObjectC objc = (MDObjectC) demangler.getMdItem(); + String convention = objc.getCallingConvention(); + int numParameterBytes = objc.getNumParameterBytes(); + assertEquals(null, convention); + assertEquals(0, numParameterBytes); + assertEquals(expectDemangled, objc.toString()); + } + + //============================================================================================== + + @Test + //This test checks that we can provide a mangled string for a function namespace. + // The return String from getOriginalMangled() is not null only for this special + // circumstance. So, in normal processing, we should check it for non-null to + // determine that we have a result of this form. + // The symbol here is from our cn3.cpp source target. + public void testFunctionNamespace() throws Exception { + String mangled = "?fn3@?2??Bar3@Foo2b@@SAHXZ@4HA"; + String wholeTruth = "int `public: static int __cdecl Foo2b::Bar3(void)'::`3'::fn3"; + String functionNamespaceMangledTruth = "?Bar3@Foo2b@@SAHXZ"; + String functionNamespaceTruth = "public: static int __cdecl Foo2b::Bar3(void)"; + + MicrosoftDemangler demangler = new MicrosoftDemangler(); + + MicrosoftMangledContext context = + demangler.createMangledContext(mangled, null, program32, address32); + DemangledObject obj = demangler.demangle(context); + String demangled = demangler.getMdItem().toString(); + assertEquals(wholeTruth, demangled); + + String mangledFunctionNS = obj.getNamespace().getNamespace().getMangledString(); + assertEquals(functionNamespaceMangledTruth, mangledFunctionNS); + + context = demangler.createMangledContext(mangledFunctionNS, null, program32, address32); + demangler.demangle(context); + demangled = demangler.getMdItem().toString(); + assertEquals(functionNamespaceTruth, demangled); + } + + //============================================================================================== + /* + * Follow are C-style mangling scheme under 32-bit model; __vectorcall also valid for 64-bit + * __cdecl: '_' prefix; no suffix; example "_name" + * __stdcall: '_' prefix; "@" suffix; example "_name@12" + * __fastcall: '@' prefix; "@" suffix; example "@name@12" + * __vectorcall: no prefix; "@@" suffix; example "name@@12" + */ + + @Test + //Uses additional context information for demangling: architecture size and whether + // demangling symbol for a known function + // Scheme: cdecl; Architecture size: 32; Function present: yes + // Result: cdecl function (stripped '_'); 0 bytes + public void testCStyleCdeclWith32Function() throws Exception { + String mangled = "_func_cdecl"; + String expectedDemangled = "func_cdecl"; + String expectedFunction = "__cdecl func_cdecl(void)"; + String expectedConvention = "__cdecl"; + int expectedNumBytes = 0; + processWith32Function(mangled, expectedDemangled, expectedFunction, expectedConvention, + expectedNumBytes); + } + + @Test + //Uses additional context information for demangling: architecture size and whether + // demangling symbol for a known function + // Scheme: cdecl; Architecture size: 32; Function present: no + // Result: no function; 0 bytes + public void testCStyleCdeclWith32NoFunction() throws Exception { + String mangled = "_func_cdecl"; + String expectedDemangled = "_func_cdecl"; + processWith32NonFunction(mangled, expectedDemangled); + } + + @Test + //Uses additional context information for demangling: architecture size and whether + // demangling symbol for a known function + // Scheme: cdecl; Architecture size: 64; Function present: yes + // Result: no function; 0 bytes + public void testCStyleCdeclWith64Function() throws Exception { + String mangled = "_func_cdecl"; + String expectedDemangled = "_func_cdecl"; + String expectedFunction = null; + String expectedConvention = null; + int expectedNumBytes = 0; + processWith64Function(mangled, expectedDemangled, expectedFunction, expectedConvention, + expectedNumBytes); + } + + @Test + //Uses additional context information for demangling: architecture size and whether + // demangling symbol for a known function + // Scheme: cdecl; Architecture size: 64; Function present: no + // Result: no function; 0 bytes + public void testCStyleCdeclWith64NoFunction() throws Exception { + String mangled = "_func_cdecl"; + String expectedDemangled = "_func_cdecl"; + processWith64NonFunction(mangled, expectedDemangled); + } + + @Test + //Uses additional context information for demangling: architecture size and whether + // demangling symbol for a known function + // Scheme: stdcall; Architecture size: 32; Function present: yes + // Result: stdcall function; 12 bytes + public void testCStyleStdcallWith32Function() throws Exception { + String mangled = "_func_stdcall@12"; + String expectedDemangled = "func_stdcall"; + String expectedFunction = "__stdcall func_stdcall(undefined4,undefined4,undefined4)"; + String expectedConvention = "__stdcall"; + int expectedNumBytes = 12; + processWith32Function(mangled, expectedDemangled, expectedFunction, expectedConvention, + expectedNumBytes); + } + + @Test + //Uses additional context information for demangling: architecture size and whether + // demangling symbol for a known function + // Scheme: stdcall; Architecture size: 32; Function present: no + // Result: no function; 0 bytes + public void testCStyleStdcallWith32NoFunction() throws Exception { + String mangled = "_func_stdcall@12"; + String expectedDemangled = "_func_stdcall"; + processWith32NonFunction(mangled, expectedDemangled); + } + + @Test + //Uses additional context information for demangling: architecture size and whether + // demangling symbol for a known function + // Scheme: stdcall; Architecture size: 64; Function present: yes + // Result: no function; 0 bytes + public void testCStyleStdcallWith64Function() throws Exception { + String mangled = "_func_stdcall@12"; + String expectedDemangled = "_func_stdcall"; + String expectedFunction = null; + String expectedConvention = null; + int expectedNumBytes = 0; + processWith64Function(mangled, expectedDemangled, expectedFunction, expectedConvention, + expectedNumBytes); + } + + @Test + //Uses additional context information for demangling: architecture size and whether + // demangling symbol for a known function + // Scheme: stdcall; Architecture size: 64; Function present: no + // Result: no function; 0 bytes + public void testCStyleStdcallWith64NoFunction() throws Exception { + String mangled = "_func_stdcall@12"; + String expectedDemangled = "_func_stdcall"; + processWith64NonFunction(mangled, expectedDemangled); + } + + @Test + //Uses additional context information for demangling: architecture size and whether + // demangling symbol for a known function + // Scheme: fastcall; Architecture size: 32; Function present: yes + // Result: fastcall function (stripped '@'); 12 bytes + public void testCStyleFastcallWith32Function() throws Exception { + String mangled = "@func_fastcall@12"; + String expectedDemangled = "func_fastcall"; + String expectedFunction = "__fastcall func_fastcall(undefined4,undefined4,undefined4)"; + String expectedConvention = "__fastcall"; + int expectedNumBytes = 12; + processWith32Function(mangled, expectedDemangled, expectedFunction, expectedConvention, + expectedNumBytes); + } + + @Test + //Uses additional context information for demangling: architecture size and whether + // demangling symbol for a known function + // Scheme: fastcall; Architecture size: 32; Function present: no + // Result: no function; 12 bytes + public void testCStyleFastcallWith32NoFunction() throws Exception { + String mangled = "@func_fastcall@12"; + String expectedDemangled = ""; // empty because the prefix '@' causes an empty name + processWith32NonFunction(mangled, expectedDemangled); + } + + @Test + //Uses additional context information for demangling: architecture size and whether + // demangling symbol for a known function + // Scheme: fastcall; Architecture size: 64; Function present: yes + // Result: no function; 12 bytes + public void testCStyleFastcallWith64Function() throws Exception { + String mangled = "@func_fastcall@12"; + String expectedDemangled = ""; // empty because the prefix '@' causes an empty name + String expectedFunction = null; + String expectedConvention = null; + int expectedNumBytes = 0; + processWith64Function(mangled, expectedDemangled, expectedFunction, expectedConvention, + expectedNumBytes); + } + + @Test + //Uses additional context information for demangling: architecture size and whether + // demangling symbol for a known function + // Scheme: fastcall; Architecture size: 64; Function present: no + // Result: fastcall function; 12 bytes + public void testCStyleFastcallWith64NoFunction() throws Exception { + String mangled = "@func_fastcall@12"; + String expectedDemangled = ""; // empty because the prefix '@' causes an empty name + processWith64NonFunction(mangled, expectedDemangled); + } + + @Test + //Uses additional context information for demangling: architecture size and whether + // demangling symbol for a known function + // Scheme: vectorcall; Architecture size: 32; Function present: yes + // Result: vectorcall function; 12 bytes + public void testCStyleVectorcallWith32Function() throws Exception { + String mangled = "func_vectorcall@@12"; + String expectedDemangled = "func_vectorcall"; + String expectedFunction = "__vectorcall func_vectorcall(undefined4,undefined4,undefined4)"; + String expectedConvention = "__vectorcall"; + int expectedNumBytes = 12; + processWith32Function(mangled, expectedDemangled, expectedFunction, expectedConvention, + expectedNumBytes); + } + + @Test + //Uses additional context information for demangling: architecture size and whether + // demangling symbol for a known function + // Scheme: vectorcall; Architecture size: 32; Function present: no + // Result: no function; 0 bytes + public void testCStyleVectorcallWith32NoFunction() throws Exception { + String mangled = "func_vectorcall@@12"; + String expectedDemangled = "func_vectorcall"; + processWith32NonFunction(mangled, expectedDemangled); + } + + @Test + //Uses additional context information for demangling: architecture size and whether + // demangling symbol for a known function + // Scheme: vectorcall; Architecture size: 32; Function present: yes + // Result: vectorcall function; 12 bytes + public void testCStyleVectorcallWith64Function() throws Exception { + String mangled = "func_vectorcall@@12"; + String expectedDemangled = "func_vectorcall"; + String expectedFunction = "__vectorcall func_vectorcall(undefined4,undefined4,undefined4)"; + String expectedConvention = "__vectorcall"; + int expectedNumBytes = 12; + processWith64Function(mangled, expectedDemangled, expectedFunction, expectedConvention, + expectedNumBytes); + } + + @Test + //Uses additional context information for demangling: architecture size and whether + // demangling symbol for a known function + // Scheme: vectorcall; Architecture size: 64; Function present: no + // Result: no function; 0 bytes + public void testCStyleVectorcallWith64NoFunction() throws Exception { + String mangled = "func_vectorcall@@12"; + String expectedDemangled = "func_vectorcall"; + processWith64NonFunction(mangled, expectedDemangled); + } + +} diff --git a/Ghidra/Features/MicrosoftDemangler/src/test/java/ghidra/app/util/demangler/microsoft/MicrosoftDemanglerTest.java b/Ghidra/Features/MicrosoftDemangler/src/test/java/ghidra/app/util/demangler/microsoft/MicrosoftDemanglerTest.java index 82432229f8..e7fecec70f 100644 --- a/Ghidra/Features/MicrosoftDemangler/src/test/java/ghidra/app/util/demangler/microsoft/MicrosoftDemanglerTest.java +++ b/Ghidra/Features/MicrosoftDemangler/src/test/java/ghidra/app/util/demangler/microsoft/MicrosoftDemanglerTest.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -45,21 +45,24 @@ public class MicrosoftDemanglerTest extends AbstractGenericTest { public void testUnsignedShortParameter() throws Exception { String mangled = "?InvokeHelperV@COleDispatchDriver@@QAEXJGGPAXPBEPAD@Z"; - + Address address = addr("01001000"); MicrosoftDemangler demangler = new MicrosoftDemangler(); - DemangledObject demangledObject = demangler.demangle(mangled); + DemanglerOptions options = new MicrosoftDemanglerOptions(); + MangledContext mangledContext = + demangler.createMangledContext(mangled, options, program, address); + DemangledObject demangledObject = demangler.demangle(mangledContext); int txID = program.startTransaction("Test"); SymbolTable st = program.getSymbolTable(); - st.createLabel(addr("01001000"), mangled, SourceType.ANALYSIS); + st.createLabel(address, mangled, SourceType.ANALYSIS); + + demangledObject.applyTo(program, address, options, TaskMonitor.DUMMY); - DemanglerOptions options = new DemanglerOptions(); - demangledObject.applyTo(program, addr("01001000"), options, TaskMonitor.DUMMY); program.endTransaction(txID, true); FunctionManager fm = program.getFunctionManager(); - Function function = fm.getFunctionAt(addr("01001000")); + Function function = fm.getFunctionAt(address); Parameter[] parameters = function.getParameters(); // this was broken at one point, returning 'unsigned_short' @@ -69,17 +72,19 @@ public class MicrosoftDemanglerTest extends AbstractGenericTest { @Test public void testArrayVariable() throws Exception { // NullPointerException String mangled = "?Te@NS1@BobsStuff@@0QAY0BAA@$$CBIA"; - + Address address = addr("01001000"); MicrosoftDemangler demangler = new MicrosoftDemangler(); - DemangledObject demangledObject = demangler.demangle(mangled); + DemanglerOptions options = new MicrosoftDemanglerOptions(); + MangledContext mangledContext = + demangler.createMangledContext(mangled, options, program, address); + DemangledObject demangledObject = demangler.demangle(mangledContext); int txID = program.startTransaction("Test"); SymbolTable st = program.getSymbolTable(); - st.createLabel(addr("01001000"), mangled, SourceType.ANALYSIS); + st.createLabel(address, mangled, SourceType.ANALYSIS); - DemanglerOptions options = new DemanglerOptions(); - demangledObject.applyTo(program, addr("01001000"), options, TaskMonitor.DUMMY); + demangledObject.applyTo(program, address, options, TaskMonitor.DUMMY); program.endTransaction(txID, false); } @@ -203,8 +208,11 @@ public class MicrosoftDemanglerTest extends AbstractGenericTest { String mangled = "?BobsStuffIO@344GPAUHINSTANCE__@@U_COMMPROP@@+W"; MicrosoftDemangler demangler = new MicrosoftDemangler(); + + MangledContext mangledContext = + demangler.createMangledContext(mangled, null, program, null); try { - demangler.demangle(mangled); + demangler.demangle(mangledContext); } catch (DemangledException e) { // Expected @@ -218,8 +226,10 @@ public class MicrosoftDemanglerTest extends AbstractGenericTest { String mangled = "?BobsStuffIO@344GPAUHINSTANCE__@@U_COMMPROP@@/W"; MicrosoftDemangler demangler = new MicrosoftDemangler(); + MangledContext mangledContext = + demangler.createMangledContext(mangled, null, program, null); try { - demangler.demangle(mangled); + demangler.demangle(mangledContext); } catch (DemangledException e) { // Expected diff --git a/Ghidra/Features/MicrosoftDmang/developer_scripts/DeveloperDumpMDMangParseInfoScript.java b/Ghidra/Features/MicrosoftDmang/developer_scripts/DeveloperDumpMDMangParseInfoScript.java index 1c14b01111..ba9c788d30 100644 --- a/Ghidra/Features/MicrosoftDmang/developer_scripts/DeveloperDumpMDMangParseInfoScript.java +++ b/Ghidra/Features/MicrosoftDmang/developer_scripts/DeveloperDumpMDMangParseInfoScript.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -93,8 +93,9 @@ public class DeveloperDumpMDMangParseInfoScript extends GhidraScript { StringBuilder builder = new StringBuilder(); builder.append("\nName: " + name + "\n"); MDMangParseInfo demangler = new MDMangParseInfo(); + demangler.setMangledSymbol(name); try { - demangler.demangle(name, false); + demangler.demangle(); String parseInfo = demangler.getParseInfoIncremental(); builder.append(parseInfo); builder.append("Num remaining chars:" + demangler.getNumCharsRemaining() + "\n"); diff --git a/Ghidra/Features/MicrosoftDmang/developer_scripts/MDMangDeveloperGenericizeMangledNamesScript.java b/Ghidra/Features/MicrosoftDmang/developer_scripts/MDMangDeveloperGenericizeMangledNamesScript.java index 4258dbbb97..4cb8aefe81 100644 --- a/Ghidra/Features/MicrosoftDmang/developer_scripts/MDMangDeveloperGenericizeMangledNamesScript.java +++ b/Ghidra/Features/MicrosoftDmang/developer_scripts/MDMangDeveloperGenericizeMangledNamesScript.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -125,8 +125,9 @@ public class MDMangDeveloperGenericizeMangledNamesScript extends GhidraScript { return getError(name, "contains white space"); } MDMangGenericize demangler = new MDMangGenericize(); + demangler.setMangledSymbol(name); try { - demangler.demangle(name, false); + demangler.demangle(); } catch (MDException e) { return getError(name, e.getMessage()); diff --git a/Ghidra/Features/MicrosoftDmang/src/main/java/mdemangler/MDCharacterIterator.java b/Ghidra/Features/MicrosoftDmang/src/main/java/mdemangler/MDCharacterIterator.java index 53509bb92a..7c8746409c 100644 --- a/Ghidra/Features/MicrosoftDmang/src/main/java/mdemangler/MDCharacterIterator.java +++ b/Ghidra/Features/MicrosoftDmang/src/main/java/mdemangler/MDCharacterIterator.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -17,13 +17,13 @@ package mdemangler; /** * A class for bidirectional iteration over a string. - * + * * Iterators maintain a current character index, whose valid range is from * 0 to string.length()-1. - * + * * The current index can be retrieved by calling getIndex() and set directly * by calling setIndex(). - * + * * The methods previous() and next() are used for iteration. They return DONE if * they would move outside the range from 0 to string.length()-1. */ @@ -71,13 +71,13 @@ public class MDCharacterIterator { } /** - * Sets the position to the specified position in the text. - * @param index the position within the text. - * @return the character at the specified position - * @throws IllegalArgumentException if index is not in range from 0 to string.length()-1 + * Sets the position to the specified position in the text. Can set index to just beyond + * the text to represent the iterator being at the end of the text + * @param index the position within the text. + * @throws IllegalArgumentException if index is not in range from 0 to string.length() */ public void setIndex(int index) { - if (index < 0 || index > string.length() - 1) { + if (index < 0 || index > string.length()) { throw new IllegalArgumentException(); } this.index = index; @@ -92,7 +92,7 @@ public class MDCharacterIterator { } /** - * Returns the next character without incrementing the current index. + * Returns the next character without incrementing the current index. * @return the next character without incrementing the current index */ public char peek() { @@ -137,7 +137,7 @@ public class MDCharacterIterator { } /** - * Returns the character at the current index and then increments the index by one. + * Returns the character at the current index and then increments the index by one. * If the resulting index is greater or equal * to the end index, the current index is reset to the end index and * a value of DONE is returned. @@ -154,7 +154,7 @@ public class MDCharacterIterator { } /** - * Increments the index by one. + * Increments the index by one. * Does no testing for whether the index surpasses the length of the string. */ public void increment() { @@ -162,7 +162,7 @@ public class MDCharacterIterator { } /** - * Increments the index by the amount of count. + * Increments the index by the amount of count. * Does no testing for whether the index surpasses the length of the string. */ public void increment(int count) { diff --git a/Ghidra/Features/MicrosoftDmang/src/main/java/mdemangler/MDDotSeparatedItem.java b/Ghidra/Features/MicrosoftDmang/src/main/java/mdemangler/MDDotSeparatedItem.java index 12452fec20..ad6bc314e2 100644 --- a/Ghidra/Features/MicrosoftDmang/src/main/java/mdemangler/MDDotSeparatedItem.java +++ b/Ghidra/Features/MicrosoftDmang/src/main/java/mdemangler/MDDotSeparatedItem.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -59,7 +59,10 @@ public class MDDotSeparatedItem extends MDParsableItem { try { Constructor ctor = dmang.getClass().getDeclaredConstructor(); MDMang subDmang = ctor.newInstance(); - subItem = subDmang.demangle(sub, false); + subDmang.setArchitectureSize(dmang.getArchitectureSize()); + subDmang.setIsFunction(dmang.isFunction); + subDmang.setMangledSymbol(sub); + subItem = subDmang.demangle(); } // might want to handle these separately for now... later can possibly group all // together diff --git a/Ghidra/Features/MicrosoftDmang/src/main/java/mdemangler/MDMang.java b/Ghidra/Features/MicrosoftDmang/src/main/java/mdemangler/MDMang.java index 8dfda5c9a3..fcc8cc4d92 100644 --- a/Ghidra/Features/MicrosoftDmang/src/main/java/mdemangler/MDMang.java +++ b/Ghidra/Features/MicrosoftDmang/src/main/java/mdemangler/MDMang.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -19,6 +19,8 @@ import java.nio.charset.Charset; import java.util.ArrayList; import java.util.List; +import org.apache.commons.lang3.StringUtils; + import mdemangler.MDContext.MDContextType; import mdemangler.datatype.MDDataType; import mdemangler.datatype.MDDataTypeParser; @@ -38,6 +40,9 @@ import mdemangler.template.MDTemplateArgumentsList; public class MDMang { public static final char DONE = MDCharacterIterator.DONE; + protected int architectureSize = 32; + protected boolean isFunction = false; + protected String mangled; protected MDCharacterIterator iter; protected String errorMessage; @@ -45,12 +50,147 @@ public class MDMang { protected List contextStack; + protected boolean errorOnRemainingChars = false; + public enum ProcessingMode { DEFAULT_STANDARD, LLVM } private ProcessingMode processingMode; + //============================================================================================== + // Mangled Context + + /** + * Sets the mangled string to be demangled + * @param mangledIn the string to be demangled + */ + public void setMangledSymbol(String mangledIn) { + this.mangled = mangledIn; + } + + /** + * Gets the mangled string being demangled + * @return the string being demangled + */ + public String getMangledSymbol() { + return mangled; + } + + /** + * Sets the architecture size. Default is 64 bits + * @param size the architecture size + */ + public void setArchitectureSize(int size) { + architectureSize = size; + } + + /** + * Returns the architecture size (bits) + * @return the architecture size + */ + public int getArchitectureSize() { + return architectureSize; + } + + /** + * Sets whether the symbol is known to be for a function + * @param isFunction {@code true} if known to be a symbol for a function + */ + public void setIsFunction(boolean isFunction) { + this.isFunction = isFunction; + } + + /** + * Returns whether the symbol is known to be for a function + * @return {@code true} if known to be a symbol for a function + */ + public boolean isFunction() { + return isFunction; + } + + //============================================================================================== + // Control + + /** + * Controls whether an exception is thrown if there are remaining characters after demangling. + * Default is {@code false} + * @param errorOnRemainingCharsArg {@code true} to error if characters remaining + */ + public void setErrorOnRemainingChars(boolean errorOnRemainingCharsArg) { + errorOnRemainingChars = errorOnRemainingCharsArg; + } + + /** + * Returns {@code true} if the process will throw an exception if characters remain after + * demangling + * @return {@code true} if errors will occur on remaining characters + */ + public boolean errorOnRemainingChars() { + return errorOnRemainingChars; + } + + /** + * Returns the error message when demangle() returns null. + * @return the error message for the demangle() call. + */ + public String getErrorMessage() { + return errorMessage; + } + + /** + * Returns the number of unprocessed mangled characters. Note that + * demangle() has a flag controlling whether remaining characters causes an + * error + * @return the integer number of characters that remain + */ + public int getNumCharsRemaining() { + return iter.getLength() - iter.getIndex(); + } + + //============================================================================================== + // Processing + + /** + * Demangles the string already stored and returns a parsed item + * @return item detected and parsed + * @throws MDException upon error parsing item + */ + public MDParsableItem demangle() throws MDException { + initState(); + item = MDMangObjectParser.determineItemAndParse(this); + if (item instanceof MDObjectCPP) { + // MDMANG SPECIALIZATION USED. + item = getEmbeddedObject((MDObjectCPP) item); + } + int numCharsRemaining = getNumCharsRemaining(); + if (errorOnRemainingChars && (numCharsRemaining > 0)) { + throw new MDException( + "MDMang: characters remain after demangling: " + numCharsRemaining + "."); + } + return item; + } + + /** + * Demangles the mangled "type" name already stored and returns a parsed MDDataType + * @return the parsed MDDataType + * @throws MDException upon parsing error + */ + public MDDataType demangleType() throws MDException { + initState(); + MDDataType mdDataType = MDDataTypeParser.determineAndParseDataType(this, false); + item = mdDataType; + int numCharsRemaining = getNumCharsRemaining(); + if (errorOnRemainingChars && (numCharsRemaining > 0)) { + throw new MDException( + "MDMang: characters remain after demangling: " + numCharsRemaining + "."); + } + return mdDataType; + } + + //============================================================================================== + // Internal processing control + public void setProcessingMode(ProcessingMode processingMode) { this.processingMode = processingMode; } @@ -67,33 +207,13 @@ public class MDMang { return processingMode == ProcessingMode.LLVM; } - /** - * Demangles the string passed in and returns a parsed item. - * - * @param mangledIn - * the string to be demangled. - * @param errorOnRemainingChars - * boolean flag indicating whether remaining characters causes an - * error. - * @return the item that has been parsed. - * @throws MDException upon parsing error - */ - public MDParsableItem demangle(String mangledIn, boolean errorOnRemainingChars) - throws MDException { - if (mangledIn == null || mangledIn.isEmpty()) { - throw new MDException("Invalid mangled symbol."); - } - setMangledSymbol(mangledIn); - return demangle(errorOnRemainingChars); - } - /** * Variables that get set at the very beginning. * @throws MDException if mangled name is not set */ protected void initState() throws MDException { - if (mangled == null) { - throw new MDException("MDMang: Mangled string is null."); + if (StringUtils.isBlank(mangled)) { + throw new MDException("MDMang: Mangled string is null or blank."); } errorMessage = ""; processingMode = ProcessingMode.DEFAULT_STANDARD; @@ -109,108 +229,7 @@ public class MDMang { setIndex(0); } - /** - * Demangles the string already stored and returns a parsed item. - * - * @param errorOnRemainingChars - * boolean flag indicating whether remaining characters causes an - * error. - * @return item detected and parsed - * @throws MDException upon error parsing item - */ - public MDParsableItem demangle(boolean errorOnRemainingChars) throws MDException { - initState(); - item = MDMangObjectParser.determineItemAndParse(this); - if (item instanceof MDObjectCPP) { - // MDMANG SPECIALIZATION USED. - item = getEmbeddedObject((MDObjectCPP) item); - } - int numCharsRemaining = getNumCharsRemaining(); - if (errorOnRemainingChars && (numCharsRemaining > 0)) { - throw new MDException( - "MDMang: characters remain after demangling: " + numCharsRemaining + "."); - } - return item; - } - - /** - * Demangles the mangled "type" name and returns a parsed MDDataType - * - * @param mangledIn the mangled "type" string to be demangled - * @param errorOnRemainingChars - * boolean flag indicating whether remaining characters causes an - * error - * @return the parsed MDDataType - * @throws MDException upon parsing error - */ - public MDDataType demangleType(String mangledIn, boolean errorOnRemainingChars) - throws MDException { - if (mangledIn == null || mangledIn.isEmpty()) { - throw new MDException("Invalid mangled symbol."); - } - setMangledSymbol(mangledIn); - return demangleType(errorOnRemainingChars); - } - - /** - * Demangles the mangled "type" name already stored and returns a parsed MDDataType - * - * @param errorOnRemainingChars - * boolean flag indicating whether remaining characters causes an - * error - * @return the parsed MDDataType - * @throws MDException upon parsing error - */ - public MDDataType demangleType(boolean errorOnRemainingChars) throws MDException { - initState(); - MDDataType mdDataType = MDDataTypeParser.determineAndParseDataType(this, false); - item = mdDataType; - int numCharsRemaining = getNumCharsRemaining(); - if (errorOnRemainingChars && (numCharsRemaining > 0)) { - throw new MDException( - "MDMang: characters remain after demangling: " + numCharsRemaining + "."); - } - return mdDataType; - } - - /** - * Sets the mangled string to be demangled. - * - * @param mangledIn - * the string to be demangled. - */ - public void setMangledSymbol(String mangledIn) { - this.mangled = mangledIn; - } - - /** - * Gets the mangled string being demangled. - * - * @return the string being demangled. - */ - public String getMangledSymbol() { - return mangled; - } - - /** - * Returns the error message when demangle() returns null. - * - * @return the error message for the demangle() call. - */ - public String getErrorMessage() { - return errorMessage; - } - - /** - * Returns the number of unprocessed mangled characters. Note that - * demangle() has a flag controlling whether remaining characters causes an - * error. - * - * @return the integer number of characters that remain. - */ - public int getNumCharsRemaining() { - return iter.getLength() - iter.getIndex(); - } + //============================================================================================== /******************************************************************************/ /******************************************************************************/ @@ -252,9 +271,10 @@ public class MDMang { } /** - * Sets the current index. - * @param index the position to set. - * @throws IllegalArgumentException if index is not in range from 0 to string.length()-1 + * Sets the current index. Can set index to just beyond the text to represent the iterator + * being at the end of the text + * @param index the position to set + * @throws IllegalArgumentException if index is not in range from 0 to string.length() */ public void setIndex(int index) { iter.setIndex(index); diff --git a/Ghidra/Features/MicrosoftDmang/src/main/java/mdemangler/MDMangGenericize.java b/Ghidra/Features/MicrosoftDmang/src/main/java/mdemangler/MDMangGenericize.java index d0f35de0bd..05f728c01f 100644 --- a/Ghidra/Features/MicrosoftDmang/src/main/java/mdemangler/MDMangGenericize.java +++ b/Ghidra/Features/MicrosoftDmang/src/main/java/mdemangler/MDMangGenericize.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -67,7 +67,7 @@ public class MDMangGenericize extends MDMang { // return item; // } @Override - public MDParsableItem demangle(boolean errorOnRemainingChars) throws MDException { + public MDParsableItem demangle() throws MDException { // ignoring the 'errorOnRemainingChars' parameter (for now) initState(); item = MDMangObjectParser.determineItemAndParse(this); diff --git a/Ghidra/Features/MicrosoftDmang/src/main/java/mdemangler/MDMangGhidra.java b/Ghidra/Features/MicrosoftDmang/src/main/java/mdemangler/MDMangGhidra.java index cbba084a63..b2943bb366 100644 --- a/Ghidra/Features/MicrosoftDmang/src/main/java/mdemangler/MDMangGhidra.java +++ b/Ghidra/Features/MicrosoftDmang/src/main/java/mdemangler/MDMangGhidra.java @@ -15,918 +15,64 @@ */ package mdemangler; -import java.util.Iterator; - -import ghidra.app.util.demangler.*; -import ghidra.program.model.lang.CompilerSpec; -import ghidra.program.model.symbol.SourceType; import mdemangler.datatype.MDDataType; -import mdemangler.datatype.MDVarArgsType; -import mdemangler.datatype.complex.*; -import mdemangler.datatype.extended.MDArrayReferencedType; -import mdemangler.datatype.modifier.*; -import mdemangler.functiontype.*; -import mdemangler.naming.*; -import mdemangler.object.*; -import mdemangler.template.MDTemplateNameAndArguments; -import mdemangler.typeinfo.*; /** * A new built-from-scratch class for demangling debug symbols created using * Microsoft Visual Studio. + *

+ * Note: the processing of {@link MDParsableItem} that was in this class was moved to a + * package-projected utility class of the MicrosoftDemangler. Ghidra users should defer to + * using the MicrosoftDemangler. + *

+ * This {@link MDMangGhidra} class might be removed in the future, with deferred use to MDMang. */ public class MDMangGhidra extends MDMang { - private DemangledObject objectResult; - private DemangledDataType dataTypeResult; + private boolean demangleOnlyKnownPatterns = false; - private String mangledSource; - private String demangledSource; + //============================================================================================== + // Control - public DemangledObject getObject() { - return objectResult; + /** + * Controls whether a symbol is skipped (returns null) if it doesn't match a known mangling + * pattern, which is generally the start pattern of a symbol. Default is {@code false} + * @param demangleOnlyKnownPatternsArg {@code true} to skip a symbol that doesn't match a + * known pattern + */ + public void setDemangleOnlyKnownPatterns(boolean demangleOnlyKnownPatternsArg) { + demangleOnlyKnownPatterns = demangleOnlyKnownPatternsArg; } - public DemangledDataType getDataType() { - return dataTypeResult; + /** + * Returns {@code true} if the process will skip a symbol that doesn't match a known pattern + * @return {@code true} if a symbol that doesn't a known pattern will be skipped + */ + public boolean demangleOnlyKnownPatterns() { + return demangleOnlyKnownPatterns; } - public MDParsableItem demangle(String mangledArg, boolean errorOnRemainingChars, - boolean demangleOnlyKnownPatterns) throws MDException { - // TODO: Could possibly just ignore "demangleOnlyKnownpatterns" + //============================================================================================== + @Override + public MDParsableItem demangle() throws MDException { if (demangleOnlyKnownPatterns) { - if (!(mangledArg.startsWith("?") || mangledArg.startsWith(".") || - mangledArg.startsWith("__") || (mangledArg.charAt(0) < 'a') || - (mangledArg.charAt(0) > 'z') || (mangledArg.charAt(0) < 'A') || - (mangledArg.charAt(0) > 'Z'))) { + if (!(mangled.startsWith("?") || mangled.startsWith(".") || mangled.startsWith("_") || + (mangled.charAt(0) < 'a') || + (mangled.charAt(0) >= 'a') && (mangled.charAt(0) <= 'z') || + (mangled.charAt(0) >= 'A') && (mangled.charAt(0) <= 'Z'))) { return null; } } - - return demangle(mangledArg, errorOnRemainingChars); - } - - @Override - public MDParsableItem demangle(String mangledArg, boolean errorOnRemainingChars) - throws MDException { - - this.mangledSource = mangledArg; - - MDParsableItem returnedItem = super.demangle(mangledArg, true); - - this.demangledSource = item.toString(); - - objectResult = processItem(); + MDParsableItem returnedItem = super.demangle(); + // TODO: Investigate... seems that mangledSource should be eliminated throughout and + // that mangled should be used instead. return returnedItem; } @Override - public MDDataType demangleType(String mangledArg, boolean errorOnRemainingChars) - throws MDException { - - this.mangledSource = mangledArg; - - MDDataType returnedType = super.demangleType(mangledArg, errorOnRemainingChars); - - this.demangledSource = returnedType.toString(); - - dataTypeResult = processDataType(null, returnedType); + public MDDataType demangleType() throws MDException { + MDDataType returnedType = super.demangleType(); return returnedType; } - public Demangled processNamespace(MDQualifiedName qualifiedName) { - return processNamespace(qualifiedName.getQualification()); - } - - private Demangled processNamespace(MDQualification qualification) { - Iterator it = qualification.iterator(); - if (!it.hasNext()) { - return null; - } - - MDQualifier qual = it.next(); - Demangled type = getDemangled(qual); - Demangled current = type; - // Note that qualifiers come in reverse order, from most refined to root being the last - while (it.hasNext()) { - qual = it.next(); - Demangled parent = getDemangled(qual); - current.setNamespace(parent); - current = parent; - } - return type; - } - - private Demangled getDemangled(MDQualifier qual) { - Demangled demangled; - if (qual.isNested()) { - String subMangled = qual.getNested().getMangled(); - MDObjectCPP obj = qual.getNested().getNestedObject(); - MDTypeInfo typeInfo = obj.getTypeInfo(); - MDType type = typeInfo.getMDType(); - if (type instanceof MDDataType dt) { - demangled = new DemangledType(subMangled, qual.toString(), qual.toString()); - } - else if (type instanceof MDFunctionType ft) { - // We currently cannot handle functions as part of a namespace, so we will just - // treat the demangled function namespace string as a plain namespace. - //demangled = new DemangledFunction(subMangled, qual.toString(), qual.toString()); - demangled = - new DemangledNamespaceNode(subMangled, qual.toString(), qual.toString()); - } - else { - demangled = - new DemangledNamespaceNode(subMangled, qual.toString(), qual.toString()); - } - } - else if (qual.isAnon()) { - // Instead of using the standard qual.toString() method, which returns - // "`anonymous namespace'" for anonymous qualifiers, we use qual.getAnonymousName() - // which will have the underlying anonymous name of the form "A0xfedcba98" to create - // a standardized anonymous name that is distinguishable from other anonymous names. - // The standardized name comes from createStandardAnonymousNamespaceNode(). This - // is especially important when there are sibling anonymous names. - String anon = MDMangUtils.createStandardAnonymousNamespaceNode(qual.getAnonymousName()); - demangled = new DemangledNamespaceNode(mangledSource, qual.toString(), anon); - } - else if (qual.isInterface()) { - // TODO: need to do better; setting namespace for now - demangled = new DemangledNamespaceNode(mangledSource, qual.toString(), qual.toString()); - } - else if (qual.isNameQ()) { - // TODO: need to do better; setting namespace for now, as it looks like interface - demangled = new DemangledNamespaceNode(mangledSource, qual.toString(), qual.toString()); - } - else if (qual.isNameC()) { - // TODO: need to do better; setting type for now, but not processed yet and not sure - // what it is - demangled = new DemangledType(mangledSource, qual.toString(), qual.toString()); - } - else if (qual.isLocalNamespace()) { - String local = - MDMangUtils.createStandardLocalNamespaceNode(qual.getLocalNamespaceNumber()); - demangled = new DemangledNamespaceNode(mangledSource, qual.toString(), local); - } - else { - demangled = new DemangledNamespaceNode(mangledSource, qual.toString(), qual.toString()); - } - return demangled; - } - - private DemangledObject processItem() { - objectResult = null; - if (item instanceof MDObjectReserved) { - objectResult = processObjectReserved((MDObjectReserved) item); - } - else if (item instanceof MDObjectCodeView codeView) { - objectResult = processObjectCPP(codeView); - objectResult.setSpecialPrefix(codeView.getPrefix()); - } - else if (item instanceof MDObjectCPP objCpp) { // Base class of MDObjectBracket/MDObjectCodeView. - objectResult = processObjectCPP(objCpp); - } - else if (item instanceof MDObjectC objC) { - objectResult = processObjectC(objC); - } - else if (item instanceof MDDataType dataType) { - // TODO: how do we fix this? DemangledDataType extends DemangledType, but not - // DemangleObject... - dataTypeResult = processDataType(null, dataType); - // object = getDemangledDataType(); - } - else if (item instanceof MDTemplateNameAndArguments templateNameAndArgs) { - objectResult = processTemplate(templateNameAndArgs); - } - return objectResult; - } - - private DemangledObject processObjectReserved(MDObjectReserved objectReserved) { - DemangledObject object = null; - if (objectReserved.getClass().equals(MDObjectReserved.class)) { - //Testing if the class is not a derived class of MDObjectReserved; - // In other words, is it exactly a MDObjectReserved? - // If so, then return null, which will allow it to get processed - // outside of the demangler. - return null; - } - if (objectReserved instanceof MDObjectBracket) { - MDObjectBracket objectBracket = (MDObjectBracket) objectReserved; - MDObjectCPP objectCPP = objectBracket.getObjectCPP(); - object = processObjectCPP(objectCPP); - object.setSpecialPrefix(((MDObjectBracket) item).getPrefix()); - } - //TODO: put other objectReserved derivative types here and return something that Ghidra - // can use. - else { - object = - new DemangledUnknown(mangledSource, demangledSource, objectReserved.toString()); - } - return object; - } - - private DemangledObject processObjectC(MDObjectC objectC) { - // We are returning null here because we do not want Ghidra to put up a plate - // comment for a standard C symbol. - // FUTURE WORK: After discussion, easiest way to deal with this for now (without - // exploding work into other demanglers) is to keep the "return null" for now. - // The problem is with the DemangledObject making a revision of the - // success/failure of demangling by doing a comparison of the input string to - // the output string in the applyTo() method. In a previous encoding, I moved - // this logic into other demanglers and set a flag in DemangledObject, that way - // my MDMangGhidra could set a flag to succeed when we have a C-language variable - // (vs. C++) where the input == output is valid. We didn't like this pattern. - // The better way forward, which will require digging into the other demanglers - // further (keeping this for future work), is to throw an exception on failure - // instead of returning null as well as pushing this success/failure logic - // upstream (where I was attempting to put it) and removing the input == output - // test from DemangledObject; an object is only returned upon success and no - // rescinding of the success determination is made later. - return null; - // Following is the code that we had originally intended to use. - // DemangledVariable variable = new DemangledVariable(objectC.toString()); - // return variable; - } - - private DemangledObject processObjectCPP(MDObjectCPP objectCPP) { - MDTypeInfo typeinfo = objectCPP.getTypeInfo(); - DemangledObject resultObject = null; - if (typeinfo != null) { - if (typeinfo instanceof MDVariableInfo) { - DemangledVariable variable; - MDVariableInfo variableInfo = (MDVariableInfo) typeinfo; - MDType mdtype = variableInfo.getMDType(); - DemangledDataType dt = processDataType(null, (MDDataType) mdtype); - if ("std::nullptr_t".equals(dt.getName())) { - variable = new DemangledVariable(mangledSource, demangledSource, ""); - } - else { - variable = - new DemangledVariable(mangledSource, demangledSource, objectCPP.getName()); - variable.setNamespace(processNamespace(objectCPP.getQualification())); - } - variable.setDatatype(dt); - resultObject = variable; - variable.setConst(variableInfo.isConst()); - variable.setVolatile(variableInfo.isVolatile()); - variable.setPointer64(variableInfo.isPointer64()); - if (variableInfo.isRestrict()) { - variable.setRestrict(); - } - if (variableInfo.isUnaligned()) { - variable.setUnaligned(); - } - variable.setBasedName(variableInfo.getBasedName()); - if (variableInfo.isMember()) { - variable.setMemberScope(variableInfo.getMemberScope()); - } - } - else if (typeinfo instanceof MDFunctionInfo) { - if (typeinfo.getSpecialHandlingCode() == 'F') { - resultObject = new DemangledUnknown(mangledSource, demangledSource, null); - } - else { - DemangledFunction function = - new DemangledFunction(mangledSource, demangledSource, objectCPP.getName()); - function.setSignatureSourceType(SourceType.IMPORTED); - function.setNamespace(processNamespace(objectCPP.getQualification())); - resultObject = function; - objectResult = processFunction((MDFunctionInfo) typeinfo, function); - // Any other special values to be set? - if (typeinfo instanceof MDMemberFunctionInfo) { - if (typeinfo instanceof MDVCall) { - // Empty for now--placeholder for possible future logic. - } - else if (typeinfo instanceof MDVFAdjustor) { - // Empty for now--placeholder for possible future logic. - } - else if (typeinfo instanceof MDVtordisp) { - // Empty for now--placeholder for possible future logic. - } - else if (typeinfo instanceof MDVtordispex) { - // Empty for now--placeholder for possible future logic. - } - else { - // plain member function - } - } - else { - // global function - } - } - } - else if (typeinfo instanceof MDVxTable) { //Includes VFTable, VBTable, and RTTI4 - MDVxTable vxtable = (MDVxTable) typeinfo; - DemangledVariable variable = - new DemangledVariable(mangledSource, demangledSource, objectCPP.getName()); - variable.setNamespace(processNamespace(objectCPP.getQualification())); - variable.setConst(vxtable.isConst()); - variable.setVolatile(vxtable.isVolatile()); - variable.setPointer64(vxtable.isPointer64()); - resultObject = variable; - // The following code would be an alternative, depending on whether we get - // customer complaints or other fall-out from having created a variable here. - //resultObject = new DemangledUnknown(); - } - else if (typeinfo instanceof AbstractMDMetaClass) { //Includes all RTTI, except RTTI4 - DemangledVariable variable = - new DemangledVariable(mangledSource, demangledSource, objectCPP.getName()); - variable.setNamespace(processNamespace(objectCPP.getQualification())); - resultObject = variable; - // The following code would be an alternative, depending on whether we get - // customer complaints or other fall-out from having created a variable here. - //resultObject = new DemangledUnknown(); - } - else if (typeinfo instanceof MDGuard) { - DemangledVariable variable = - new DemangledVariable(mangledSource, demangledSource, objectCPP.getName()); - variable.setNamespace(processNamespace(objectCPP.getQualification())); - resultObject = variable; - // The following code would be an alternative, depending on whether we get - // customer complaints or other fall-out from having created a variable here. - //resultObject = new DemangledUnknown(); - } - else { - // Any others (e.g., case '9') - DemangledVariable variable = - new DemangledVariable(mangledSource, demangledSource, objectCPP.getName()); - variable.setNamespace(processNamespace(objectCPP.getQualification())); - resultObject = variable; - // The following code would be an alternative, depending on whether we get - // customer complaints or other fall-out from having created a variable here. - //resultObject = new DemangledUnknown(); - } - if (typeinfo.isPrivate()) { - resultObject.setVisibilty("private"); - } - else if (typeinfo.isProtected()) { - resultObject.setVisibilty("protected"); - } - else if (typeinfo.isPublic()) { - resultObject.setVisibilty("public"); - } - resultObject.setStatic(typeinfo.isStatic()); - resultObject.setVirtual(typeinfo.isVirtual()); - resultObject.setThunk(typeinfo.isThunk()); - if (typeinfo.isExternC()) { - resultObject.setSpecialPrefix("extern \"C\""); - } - } - else { - String baseName = objectCPP.getName(); - if (objectCPP.isString()) { - MDString mstring = objectCPP.getMDString(); - DemangledString demangledString = - new DemangledString(mangledSource, demangledSource, mstring.getName(), - mstring.toString(), mstring.getLength(), mstring.isUnicode()); - resultObject = demangledString; - } - else if (baseName.length() != 0) { - DemangledVariable variable; - variable = new DemangledVariable(mangledSource, demangledSource, baseName); - variable.setNamespace(processNamespace(objectCPP.getQualification())); - resultObject = variable; - } - } - return resultObject; - // //Various RTTI types (MDType '8' or '9') - // DemangledVariable variable = - // new - // DemangledVariable(objectCPP.getQualifiedName().getBasicName().toString()); - // variable.setNamespace(processNamespace(objectCPP.getQualifiedName())); - // return variable; - // TODO: fill in lots of object.____ items - // object.setVisibilty(typeinfo.g); - // object.setConst(isConst); - } - - // I think that this is a kludge. The mapping of MDTemplateNameAndArguments - // doesn't match - // well to the current DemangledObject hierarchy. - private DemangledVariable processTemplate(MDTemplateNameAndArguments template) { - DemangledVariable variable = - new DemangledVariable(mangledSource, demangledSource, template.toString()); - // NO NAMESPACE for high level template: variable.setNamespace(XXX); - // DemangledTemplate objectTemplate = new DemangledTemplate(); - // DemangledDataType dataType = new DemangledDataType((String) null); - // MDTemplateArgumentsList args = template.getArgumentsList(); - // if (args != null) { - // for (int index = 0; index < args.getNumArgs(); index++) { - // objectTemplate.addParameter(processDataType(null, (MDDataType) - // args.getArg(index))); - // } - // } - // dataType.setTemplate(objectTemplate); - // variable.setDatatype(dataType); - return variable; - } - - private DemangledFunction processFunction(MDFunctionInfo functionInfo, - DemangledFunction function) { - MDFunctionType functionType = (MDFunctionType) functionInfo.getMDType(); - String convention = functionType.getCallingConvention().toString(); - if ("__cdecl".equals(convention) && functionInfo.isMember() && !functionInfo.isStatic()) { - // TODO: ultimately the presence of a 'this' parareter will not be keyed - // to the calling convention, but for now we need to force it - convention = CompilerSpec.CALLING_CONVENTION_thiscall; - } - function.setCallingConvention(convention); - if (functionType.hasReturn() && functionType.getReturnType() != null) { - MDDataType retType = functionType.getReturnType(); - if (!retType.toString().isEmpty()) { - function.setReturnType(processDataType(null, retType)); - } - } - MDArgumentsList args = functionType.getArgumentsList(); - if (functionType.hasArgs() && args != null) { - for (int index = 0; index < args.getNumArgs(); index++) { - function.addParameter( - new DemangledParameter(processDataType(null, args.getArg(index)))); - } - } - if (functionType.isTypeCast()) { - function.setTypeCast(); - } - // function.setVirtual(functionType.isVirtual()); - // function.setStatic(functionType.isStatic()); - // if (functionType.isPrivate()) { - // function.setVisibilty("private"); - // } - // else if (functionType.isProtected()) { - // function.setVisibilty("protected"); - // } - // else if (functionType.isPublic()) { - // function.setVisibilty("public"); - // } - - // TODO: fix this kludge. Need to add appropriate suffixes to DemangledFunction (look - // at DemangledFunctionPointer?). Missing other possible suffixes from - // functionType.getCVMod(). - // String suffix = ""; - MDCVMod thisPointerCVMod = functionType.getThisPointerCVMod(); - if (thisPointerCVMod != null) { - if (thisPointerCVMod.isConst()) { - function.setTrailingConst(); - } - if (thisPointerCVMod.isVolatile()) { - function.setTrailingVolatile(); - } - if (thisPointerCVMod.isPointer64()) { - function.setTrailingPointer64(); - } - if (thisPointerCVMod.isRestricted()) { - function.setTrailingRestrict(); - } - if (thisPointerCVMod.isUnaligned()) { - function.setTrailingUnaligned(); - } - } - MDThrowAttribute ta = functionType.getThrowAttribute(); - if (ta != null) { - function.setThrowAttribute(ta.toString()); - } - - // TODO: fill in lots of function.____ items - return function; - } - - private DemangledFunctionPointer processDemangledFunctionPointer(MDPointerType pointerType) { - DemangledFunctionPointer functionPointer = - new DemangledFunctionPointer(mangledSource, demangledSource); - MDFunctionType functionType = (MDFunctionType) pointerType.getReferencedType(); - functionPointer.setCallingConvention(functionType.getCallingConvention().toString()); - functionPointer.setModifier(pointerType.getCVMod().toString()); - if (functionType.hasReturn() && functionType.getReturnType() != null) { - functionPointer.setReturnType(processDataType(null, functionType.getReturnType())); - } - MDArgumentsList args = functionType.getArgumentsList(); - if (functionType.hasArgs() && args != null) { - for (int index = 0; index < args.getNumArgs(); index++) { - functionPointer.addParameter(processDataType(null, args.getArg(index))); - } - } - MDCVMod thisPointerCVMod = functionType.getThisPointerCVMod(); - if (thisPointerCVMod != null) { - if (thisPointerCVMod.isConst()) { - functionPointer.setConst(); - } - if (thisPointerCVMod.isVolatile()) { - functionPointer.setVolatile(); - } - if (thisPointerCVMod.isPointer64()) { - functionPointer.setTrailingPointer64(); - } - if (thisPointerCVMod.isRestricted()) { - functionPointer.setTrailingRestrict(); - } - if (thisPointerCVMod.isUnaligned()) { - functionPointer.setTrailingUnaligned(); - } - } - // TODO: fill in lots of functionPointer.____ items - return functionPointer; - } - - private DemangledFunctionReference processDemangledFunctionReference(MDModifierType refType) { - if (!((refType instanceof MDReferenceType) || - (refType instanceof MDDataRightReferenceType))) { - return null; // Not planning on anything else yet. - } - DemangledFunctionReference functionReference = - new DemangledFunctionReference(mangledSource, demangledSource); - MDFunctionType functionType = (MDFunctionType) refType.getReferencedType(); - functionReference.setCallingConvention(functionType.getCallingConvention().toString()); - functionReference.setModifier(refType.getCVMod().toString()); - if (functionType.hasReturn() && functionType.getReturnType() != null) { - functionReference.setReturnType(processDataType(null, functionType.getReturnType())); - } - MDArgumentsList args = functionType.getArgumentsList(); - if (functionType.hasArgs() && args != null) { - for (int index = 0; index < args.getNumArgs(); index++) { - functionReference.addParameter(processDataType(null, args.getArg(index))); - } - } - // TODO: fill in lots of functionReference.____ items - return functionReference; - } - - private DemangledFunctionIndirect processDemangledFunctionIndirect( - MDFunctionIndirectType functionIndirectType) { - DemangledFunctionIndirect functionDefinition = - new DemangledFunctionIndirect(mangledSource, demangledSource); - MDFunctionType functionType = (MDFunctionType) functionIndirectType.getReferencedType(); - functionDefinition.setCallingConvention(functionType.getCallingConvention().toString()); - functionDefinition.setModifier(functionIndirectType.getCVMod().toString()); - functionDefinition.incrementPointerLevels(); - if (functionType.hasReturn() && functionType.getReturnType() != null) { - functionDefinition.setReturnType(processDataType(null, functionType.getReturnType())); - } - MDArgumentsList args = functionType.getArgumentsList(); - if (functionType.hasArgs() && args != null) { - for (int index = 0; index < args.getNumArgs(); index++) { - functionDefinition.addParameter(processDataType(null, args.getArg(index))); - } - } - // TODO: fill in lots of functionIndirect.____ items - return functionDefinition; - } - - // The following is/might be a kludge: using DemangledFunctionIndirect to see if it will - // hold the things that we need; regardless, the follow-on use of the DemangledFunction - // indirect might be clouded between the real, two underlying types. - private DemangledFunctionIndirect processDemangledFunctionQuestion( - MDModifierType modifierType) { - DemangledFunctionIndirect functionDefinition = - new DemangledFunctionIndirect(mangledSource, demangledSource); - MDFunctionType functionType = (MDFunctionType) modifierType.getReferencedType(); - functionDefinition.setCallingConvention(functionType.getCallingConvention().toString()); - functionDefinition.setModifier(modifierType.getCVMod().toString()); - functionDefinition.incrementPointerLevels(); - if (functionType.hasReturn() && functionType.getReturnType() != null) { - functionDefinition.setReturnType(processDataType(null, functionType.getReturnType())); - } - MDArgumentsList args = functionType.getArgumentsList(); - if (functionType.hasArgs() && args != null) { - for (int index = 0; index < args.getNumArgs(); index++) { - functionDefinition.addParameter(processDataType(null, args.getArg(index))); - } - } - // TODO: fill in lots of functionIndirect.____ items - return functionDefinition; - } - - // Passing "DemangledDataType resultDataType" in is a kludge, as this is done so - // incrementPointerLevels() can be used, but doing this recursion like this loses all - // storageClass information from the various nested pointers and such. TODO: need to add - // a "pointer type" with a contained "referenced data type" to DemangledObject (perhaps - // PointerObject?) - private DemangledDataType processDataType(DemangledDataType resultDataType, - MDDataType datatype) { - if (resultDataType == null) { - resultDataType = - new DemangledDataType(mangledSource, demangledSource, getDataTypeName(datatype)); - } - if (datatype.isSpecifiedSigned()) { - // Returns true if default signed or specified signed. TODO: There is no place to - // capture default signed versus specified signed (i.e., there are three types of - // char: default signed, specified signed, and unsigned) - resultDataType.setSigned(); - } - if (datatype.isUnsigned()) { - resultDataType.setUnsigned(); - } - - // Bunch of else-ifs for exclusive types - if (datatype instanceof MDModifierType) { - MDModifierType modifierType = (MDModifierType) datatype; - // if (modifierType.isBased()) { - // resultDataType.set___(); - // modifierType.getCVMod().getBasedName(); - // } - if (modifierType.isConst()) { - resultDataType.setConst(); - } - if (modifierType.isVolatile()) { - resultDataType.setVolatile(); - } - if (modifierType.isPointer64()) { - resultDataType.setPointer64(); - } - if (modifierType.isRestrict()) { - resultDataType.setRestrict(); - } - if (modifierType.isUnaligned()) { - resultDataType.setUnaligned(); - } - resultDataType.setBasedName(modifierType.getBasedName()); - // if (modifierType.isMember()) { - resultDataType.setMemberScope(modifierType.getMemberScope()); - // } - // TODO: fix. Following is a kludge because DemangledObject has no DemangledReference - // with corresponding referencedType. - if (modifierType instanceof MDArrayBasicType) { - resultDataType.setArray(1); - if ((modifierType.getReferencedType() instanceof MDFunctionType)) { - // MDType ref = modifierType.getReferencedType(); - // TODO: A demangled function reference is needed here. - // DemangledFunction function = new - // DemangledFunction(objectCPP.getQualifiedName().getBasicName().toString()); - // function.setNamespace(processNamespace(objectCPP.getQualifiedName())); - // //resultObject = function; - // return processFunction(ref, resultDataType); - } - else if (modifierType.getReferencedType() instanceof MDDataType) { - return processDataType(resultDataType, - (MDDataType) modifierType.getReferencedType()); - } - else { - // Empty for now--placeholder for possible future logic. - } - } - else if (modifierType instanceof MDPointerType) { - if ((modifierType.getReferencedType() instanceof MDFunctionType)) { - // TODO---------what are we returning... need to work on called routine. - DemangledFunctionPointer fp = - processDemangledFunctionPointer((MDPointerType) modifierType); - // TODO: fix. Following is a kludge because DemangledObject has no - // DemangledPointer with corresponding referencedType. - for (int i = 0; i < resultDataType.getPointerLevels(); i++) { - fp.incrementPointerLevels(); - } - if (resultDataType.isConst()) { - fp.setConst(); - } - if (resultDataType.isVolatile()) { - fp.setVolatile(); - } - if (resultDataType.isPointer64()) { - fp.setPointer64(); - } - return fp; - } - // modifierType.getArrayString(); - // resultDataType.setArray(); - //Processing the referenced type (for Ghidra, and then setting attributes on it) - DemangledDataType newResult = - processDataType(resultDataType, (MDDataType) modifierType.getReferencedType()); - newResult.incrementPointerLevels(); - if (modifierType.getCVMod().isConst()) { - newResult.setConst(); - } - if (modifierType.getCVMod().isVolatile()) { - newResult.setVolatile(); - } - if (modifierType.getCVMod().isPointer64()) { - newResult.setPointer64(); - } - return newResult; - } - // TODO: fix. Following is a kludge because DemangledObject has no - // DemangledReference - // with corresponding referencedType. - else if (modifierType instanceof MDReferenceType) { - // TODO---------what are we returning... need to work on called - // routine. - if ((modifierType.getReferencedType() instanceof MDFunctionType)) { - DemangledFunctionReference fr = processDemangledFunctionReference(modifierType); - // TODO: fix. Following is a kludge because DemangledObject has no - // DemangledPointer with corresponding referencedType. - for (int i = 0; i < resultDataType.getPointerLevels(); i++) { - fr.incrementPointerLevels(); - } - if (resultDataType.isConst()) { - fr.setConst(); - } - if (resultDataType.isVolatile()) { - fr.setVolatile(); - } - if (resultDataType.isPointer64()) { - fr.setPointer64(); - } - return fr; - } - //Processing the referenced type (for Ghidra, and then setting attributes on it) - DemangledDataType newResult = - processDataType(resultDataType, (MDDataType) modifierType.getReferencedType()); - newResult.setLValueReference(); - if (modifierType.getCVMod().isConst()) { - newResult.setConst(); - } - if (modifierType.getCVMod().isVolatile()) { - newResult.setVolatile(); - } - if (modifierType.getCVMod().isPointer64()) { - newResult.setPointer64(); - } - return newResult; - } - // TODO: fix. Following is a kludge because DemangledObject has no DemangledReference - // with corresponding referencedType. - else if (modifierType instanceof MDFunctionIndirectType) { - // TODO---------what are we returning... need to work on called routine. - DemangledFunctionIndirect fd = - processDemangledFunctionIndirect((MDFunctionIndirectType) modifierType); - for (int i = 0; i < resultDataType.getPointerLevels(); i++) { - fd.incrementPointerLevels(); - } - if (resultDataType.isConst()) { - fd.setConst(); - } - if (resultDataType.isVolatile()) { - fd.setVolatile(); - } - if (resultDataType.isPointer64()) { - fd.setPointer64(); - } - return fd; - } - else if (modifierType instanceof MDPointerRefDataType) { - resultDataType.setName(getDataTypeName(datatype)); - // Not sure if this is the correct thing to do for MDPointerRefDataType, but we - // are just going to assign the referred-to type: - //Processing the referenced type (for Ghidra, and then setting attributes on it) - return processDataType(resultDataType, - (MDDataType) modifierType.getReferencedType()); - } - else if (modifierType instanceof MDDataReferenceType) { - // Not sure if this is the correct thing to do for MDDataReferenceType, but we - // are just going to assign the referred-to type: - //Processing the referenced type (for Ghidra, and then setting attributes on it) - processDataType(resultDataType, (MDDataType) modifierType.getReferencedType()); - if (modifierType.getCVMod().isConst()) { - resultDataType.setConst(); - } - if (modifierType.getCVMod().isVolatile()) { - resultDataType.setVolatile(); - } - return resultDataType; - } - else if (modifierType instanceof MDDataRightReferenceType) { - if ((modifierType.getReferencedType() instanceof MDFunctionType)) { - resultDataType.setName(getDataTypeName(datatype)); - // TODO---------what are we returning... need to work on called routine. - DemangledFunctionReference fr = processDemangledFunctionReference(modifierType); - // TODO: fix. Following is a kludge because DemangledObject has no - // DemangledPointer with corresponding referencedType. - for (int i = 0; i < resultDataType.getPointerLevels(); i++) { - fr.incrementPointerLevels(); - } - if (resultDataType.isConst()) { - fr.setConst(); - } - if (resultDataType.isVolatile()) { - fr.setVolatile(); - } - if (resultDataType.isPointer64()) { - fr.setPointer64(); - } - return fr; - } - //Processing the referenced type (for Ghidra, and then setting attributes on it) - processDataType(resultDataType, (MDDataType) modifierType.getReferencedType()); - resultDataType.setRValueReference(); - if (modifierType.getCVMod().isConst()) { - resultDataType.setConst(); - } - if (modifierType.getCVMod().isVolatile()) { - resultDataType.setVolatile(); - } - if (modifierType.getCVMod().isPointer64()) { - resultDataType.setPointer64(); - } - return resultDataType; - } - else { - // not pointer, reference, or array type - if ((modifierType.getReferencedType() instanceof MDFunctionType)) { - // TODO---------what are we returning... need to work on called routine. - DemangledFunctionIndirect fx = processDemangledFunctionQuestion(modifierType); - // TODO: fix. Following is a kludge because DemangledObject has no - // DemangledPointer with corresponding referencedType. - if (resultDataType.isConst()) { - fx.setConst(); - } - if (resultDataType.isVolatile()) { - fx.setVolatile(); - } - if (resultDataType.isPointer64()) { - fx.setPointer64(); - } - return fx; - } - // resultDataType.incrementPointerLevels();//Not sure if we should do/use this. - DemangledDataType dataType = - processDataType(resultDataType, (MDDataType) modifierType.getReferencedType()); - if (modifierType.getCVMod().isConst()) { - resultDataType.setConst(); - } - if (modifierType.getCVMod().isVolatile()) { - resultDataType.setVolatile(); - } - if (modifierType.getCVMod().isPointer64()) { - resultDataType.setPointer64(); - } - return dataType; - } - } - else if (datatype instanceof MDComplexType) { - MDComplexType complexType = (MDComplexType) datatype; - // Hope this is correct... will return "class" or other - resultDataType.setName(complexType.getNamespace().getName()); - // TODO: setNamespace() wants a "DemangledType" for a namespace. - // Two problems: - // 1) we don't have an appropriate method to use - // 2) not sure DemangledType is appropriate; in MDComplexType we have an - // MDQualification--not an MDQualifiedName - resultDataType.setNamespace(processNamespace(complexType.getNamespace())); - - // Bunch of else-ifs for exclusive types - if (datatype instanceof MDEnumType) { - resultDataType.setEnum(); - // Put in underlying type (for sizing too). - MDEnumType enumType = (MDEnumType) datatype; - resultDataType.setEnumType(enumType.getUnderlyingFullTypeName()); - } - else if (datatype instanceof MDClassType) { - resultDataType.setClass(); - } - else if (datatype instanceof MDStructType) { - resultDataType.setStruct(); - } - else if (datatype instanceof MDUnionType) { - resultDataType.setUnion(); - } - else if (datatype instanceof MDCoclassType) { - resultDataType.setCoclass(); - } - else if (datatype instanceof MDCointerfaceType) { - resultDataType.setCointerface(); - } - } - else if (datatype instanceof MDReferenceType) { - resultDataType.setLValueReference(); - } - else if (datatype instanceof MDArrayBasicType) { - resultDataType.setArray(1); - } - else if (datatype instanceof MDVarArgsType) { - resultDataType.setVarArgs(); - } - else if (datatype instanceof MDArrayReferencedType arrRefType) { - return processDataType(resultDataType, arrRefType.getReferencedType()); - } - else if (datatype instanceof MDStdNullPtrType) { - resultDataType.setName(datatype.toString()); - } - else { - // MDDataType - // TODO MDW64Type needs repeated reference type parsing, just as modifier types need - // them. - resultDataType.setName(getDataTypeName(datatype)); - } - // TODO: No place to indicate a general pointer--we can indicate Pointer64 - // TODO: Not sure if anything fits this: resultDataType.setComplex(); - // TODO: resultDataType.setTemplate(); //TODO: Not sure templates are data types - // according to how MSFT demangles them. - // TODO: resultDataType.setTemplate(null); //TODO: Not sure templates are data types - // according to how MSFT demangles them. - - return resultDataType; - } - - /** - * Returns either a formal type name or a representative type name to fill into a - * MangledDataType if the formal name is blank - * @return the name - */ - private String getDataTypeName(MDDataType dataType) { - String name = dataType.getName(); - if (!name.isBlank()) { - return name; - } - name = dataType.getTypeName(); - if (!name.isBlank()) { - return name; - } - return dataType.toString(); - } } diff --git a/Ghidra/Features/MicrosoftDmang/src/main/java/mdemangler/MDMangVS2015.java b/Ghidra/Features/MicrosoftDmang/src/main/java/mdemangler/MDMangVS2015.java index 71f2e8cf81..a876b23d13 100644 --- a/Ghidra/Features/MicrosoftDmang/src/main/java/mdemangler/MDMangVS2015.java +++ b/Ghidra/Features/MicrosoftDmang/src/main/java/mdemangler/MDMangVS2015.java @@ -28,9 +28,8 @@ import mdemangler.template.MDTemplateArgumentsList; public class MDMangVS2015 extends MDMang { @Override - public MDParsableItem demangle(String mangledIn, boolean errorOnRemainingChars) - throws MDException { - MDParsableItem returnedItem = super.demangle(mangledIn, errorOnRemainingChars); + public MDParsableItem demangle() throws MDException { + MDParsableItem returnedItem = super.demangle(); //VS2015 does not understand all of the object types that we made up. These all fall // under MDObjectReserved; but it does understand MDObjectBracket objects. if (returnedItem instanceof MDObjectBracket) { diff --git a/Ghidra/Features/MicrosoftDmang/src/main/java/mdemangler/object/MDObjectC.java b/Ghidra/Features/MicrosoftDmang/src/main/java/mdemangler/object/MDObjectC.java index c1e81b1344..db2f43bfb7 100644 --- a/Ghidra/Features/MicrosoftDmang/src/main/java/mdemangler/object/MDObjectC.java +++ b/Ghidra/Features/MicrosoftDmang/src/main/java/mdemangler/object/MDObjectC.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -25,21 +25,131 @@ import mdemangler.naming.MDFragmentName; */ public class MDObjectC extends MDObject { protected MDFragmentName name; + int conventionIndex; + int numParameterBytes; + + private final String callingConvention[] = + { "__cdecl", "__stdcall", "__fastcall", "__vectorcall" }; public MDObjectC(MDMang dmang) { super(dmang); + conventionIndex = -1; + numParameterBytes = 0; name = new MDFragmentName(dmang); } + /** + * Returns the name + * @return the name + */ + public String getName() { + if (name == null) { + return null; + } + return name.getName(); + } + + /** + * Returns a calling convention string if the C object is determined to be a function with + * a specified convention + * @return the convention or {@code null} if not determined to be a function with convention + */ + public String getCallingConvention() { + if (conventionIndex == -1) { + return null; + } + return callingConvention[conventionIndex]; + } + + /** + * Returns the number of parameter bytes if the C object is determined to be a function with + * a specified convention + * @return the number of bytes; will always be zero for __cdecl + */ + public int getNumParameterBytes() { + return numParameterBytes; + } + @Override public void insert(StringBuilder builder) { + // We've come up with the demangling output for the function format ourselves. This + // format does not output anything for __cdecl (default) convention + if (conventionIndex >= 1 && conventionIndex <= 3) { + builder.append(callingConvention[conventionIndex]); + builder.append(' '); + } builder.append(name); + if (conventionIndex >= 1 && conventionIndex <= 3) { + builder.append(','); + builder.append(numParameterBytes); + } } + /* + * Follow are C-style mangling scheme under 32-bit model; __vectorcall also valid for 64-bit + * __cdecl: '_' prefix; no suffix; example "_name" + * __stdcall: '_' prefix; "@" suffix; example "_name@12" + * __fastcall: '@' prefix; "@" suffix; example "@name@12" + * __vectorcall: no prefix; "@@" suffix; example "name@@12" + */ @Override protected void parseInternal() throws MDException { - name.parse(); + if (!dmang.isFunction()) { + name.parse(); + return; + } + + int index = dmang.getIndex(); + char c = dmang.peek(); + if (c == '@') { + conventionIndex = 2; + dmang.next(); + } + else if (c == '_') { + conventionIndex = 0; // will be 0 or 1 + dmang.next(); + } + else { + conventionIndex = 3; + } + name.parse(); // This strips a trailing '@' if it exists + c = dmang.peek(); + if (c == '@') { + if (conventionIndex != 3) { + throw new MDException("Error parsing C Object calling convention"); + } + dmang.next(); // skip the '@' + } + else if (conventionIndex == 0 && + dmang.getMangledSymbol().charAt(dmang.getIndex() - 1) == '@') { + conventionIndex = 1; + } + + if (dmang.getArchitectureSize() != 32 && conventionIndex != 3) { + conventionIndex = -1; + dmang.setIndex(index); // reset iterator back to original location + name.parse(); + return; + } + + if (conventionIndex != 0) { + numParameterBytes = parseNumParameterBytes(); + } + } + + private int parseNumParameterBytes() throws MDException { + int loc = dmang.getIndex(); + String str = dmang.getMangledSymbol().substring(loc); + dmang.setIndex(loc + dmang.getNumCharsRemaining()); + try { + return Integer.parseInt(str); + } + catch (NumberFormatException e) { + throw new MDException("Error parsing C Object calling convention"); + } + } + } /******************************************************************************/ diff --git a/Ghidra/Features/MicrosoftDmang/src/main/java/mdemangler/object/MDObjectCPP.java b/Ghidra/Features/MicrosoftDmang/src/main/java/mdemangler/object/MDObjectCPP.java index c529b55ec8..b61c19e994 100644 --- a/Ghidra/Features/MicrosoftDmang/src/main/java/mdemangler/object/MDObjectCPP.java +++ b/Ghidra/Features/MicrosoftDmang/src/main/java/mdemangler/object/MDObjectCPP.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -56,6 +56,14 @@ public class MDObjectCPP extends MDObject { return this; } + /** + * Returns whether the object was a hashed object + * @return {@code true} if was a hashed object + */ + public boolean isHashObject() { + return hashedObjectFlag; + } + /** * Returns the name of the symbol, minus any namespace component. * @return the name. diff --git a/Ghidra/Features/MicrosoftDmang/src/test/java/mdemangler/MDBaseTestConfiguration.java b/Ghidra/Features/MicrosoftDmang/src/test/java/mdemangler/MDBaseTestConfiguration.java index d0ae855f55..c81d33aa2e 100644 --- a/Ghidra/Features/MicrosoftDmang/src/test/java/mdemangler/MDBaseTestConfiguration.java +++ b/Ghidra/Features/MicrosoftDmang/src/test/java/mdemangler/MDBaseTestConfiguration.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -44,6 +44,7 @@ public class MDBaseTestConfiguration { // Internal variables protected String mangled; protected MDParsableItem demangItem; + protected boolean isFunction = false; protected String demangled; protected String truth; @@ -61,6 +62,10 @@ public class MDBaseTestConfiguration { } } + public void setIsFunction(boolean isFunctionArg) { + isFunction = isFunctionArg; + } + /** * Runs through the process of creating a demangler, demangling a symbol string, * testing the output, and performing other ancillary outputs and tests. @@ -85,6 +90,7 @@ public class MDBaseTestConfiguration { outputInfo.append(getTestHeader()); } + mdm.setIsFunction(isFunction); // Meant to be overridden, as needed by extended classes demangItem = doDemangleSymbol(mdm, mangled); demangled = (demangItem == null) ? "" : demangItem.toString(); @@ -123,6 +129,7 @@ public class MDBaseTestConfiguration { } } + // Need to do a better job here private boolean isMangled(String s) { if (s.charAt(0) == '?') { return true; @@ -130,9 +137,9 @@ public class MDBaseTestConfiguration { else if (s.startsWith("__")) { return true; } - else if ((s.charAt(0) == '_') || Character.isUpperCase(s.charAt(1))) { - return true; - } +// else if ((s.charAt(0) == '_') || Character.isUpperCase(s.charAt(1))) { +// return true; +// } return false; } @@ -193,8 +200,10 @@ public class MDBaseTestConfiguration { // Meant to be overridden, as needed by extended classes protected MDParsableItem doDemangleSymbol(MDMang mdmIn, String mangledIn) throws Exception { + mdmIn.setMangledSymbol(mangledIn); + mdmIn.setErrorOnRemainingChars(true); try { - return mdmIn.demangle(mangledIn, true); + return mdmIn.demangle(); } catch (MDException e) { return null; diff --git a/Ghidra/Features/MicrosoftDmang/src/test/java/mdemangler/MDGhidraTestConfiguration.java b/Ghidra/Features/MicrosoftDmang/src/test/java/mdemangler/MDGhidraTestConfiguration.java index 09959ff954..0b647e3545 100644 --- a/Ghidra/Features/MicrosoftDmang/src/test/java/mdemangler/MDGhidraTestConfiguration.java +++ b/Ghidra/Features/MicrosoftDmang/src/test/java/mdemangler/MDGhidraTestConfiguration.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -15,8 +15,6 @@ */ package mdemangler; -import ghidra.app.util.demangler.DemangledObject; - /** * This class is a derivation of MDBaseTestConfiguration (see javadoc there). This * class must choose the appropriate truth from MDMangBaseTest (new truths might @@ -26,9 +24,9 @@ import ghidra.app.util.demangler.DemangledObject; */ public class MDGhidraTestConfiguration extends MDBaseTestConfiguration { - protected DemangledObject demangledObject; - protected String demangledGhidraObject; - protected DemangledObject demangledObjectCheck; +// protected DemangledObject demangledObject; +// protected String demangledGhidraObject; +// protected DemangledObject demangledObjectCheck; public MDGhidraTestConfiguration(boolean quiet) { super(quiet); @@ -48,10 +46,12 @@ public class MDGhidraTestConfiguration extends MDBaseTestConfiguration { @Override protected MDParsableItem doDemangleSymbol(MDMang mdmIn, String mangledIn) throws Exception { MDParsableItem returnItem; + mdmIn.setMangledSymbol(mangledIn); + mdmIn.setErrorOnRemainingChars(true); try { // For first boolean: set true in operational mode. - returnItem = ((MDMangGhidra) mdmIn).demangle(mangledIn, false, false); - demangledObject = ((MDMangGhidra) mdmIn).getObject(); + returnItem = mdmIn.demangle(); +// demangledObject = ((MDMangGhidra) mdmIn).getObject(); } catch (MDException e) { returnItem = null; @@ -62,14 +62,14 @@ public class MDGhidraTestConfiguration extends MDBaseTestConfiguration { @Override protected void doBasicTestsAndOutput() throws Exception { super.doBasicTestsAndOutput(); - if (demangledObject != null) { - demangledGhidraObject = demangledObject.toString(); - outputInfo.append("demangl: " + demangledGhidraObject + "\n"); - } - else { - demangledGhidraObject = ""; - outputInfo.append("demangled: NO RESULT\n"); - } +// if (demangledObject != null) { +// demangledGhidraObject = demangledObject.toString(); +// outputInfo.append("demangl: " + demangledGhidraObject + "\n"); +// } +// else { +// demangledGhidraObject = ""; +// outputInfo.append("demangled: NO RESULT\n"); +// } // For checking the original results, for comparison purposes, this code should probably // be calling the MicrosoftWineDemangler. // try { @@ -86,38 +86,38 @@ public class MDGhidraTestConfiguration extends MDBaseTestConfiguration { // } } - @Override - protected void doExtraProcCheck() throws Exception { - if ((demangledObjectCheck != null) && (demangledObject != null)) { - if (demangledObjectCheck.getClass() != demangledObject.getClass()) { - outputInfo.append("ObjComp: notequal NEW: " + demangledObject.getClass().getName() + - ", OLD: " + demangledObjectCheck.getClass().getName() + "\n"); - } - else { - outputInfo.append("ObjComp: equal NEW: " + demangledObject.getClass().getName() + - ", OLD: " + demangledObjectCheck.getClass().getName() + "\n"); - } - } - else { - if ((demangledObjectCheck == null) && (demangledObject == null)) { - outputInfo.append("ObjComp: Not possible -- both null\n"); - } - else if (demangledObjectCheck == null) { - outputInfo.append("ObjComp: Not possible -- OLD null; NEW: " + - demangledObject.getClass().getName() + "\n"); - } - else { - outputInfo.append("ObjComp: Not possible -- NEW null; OLD: " + - demangledObjectCheck.getClass().getName() + "\n"); - } - } - if (ghidraTestStringCompare(outputInfo, demangled, demangledGhidraObject)) { - outputInfo.append("RESULTS MATCH------******\n"); - } - else { - outputInfo.append("RESULTS MISMATCH------*********************************\n"); - } - } +// @Override +// protected void doExtraProcCheck() throws Exception { +// if ((demangledObjectCheck != null) && (demangledObject != null)) { +// if (demangledObjectCheck.getClass() != demangledObject.getClass()) { +// outputInfo.append("ObjComp: notequal NEW: " + demangledObject.getClass().getName() + +// ", OLD: " + demangledObjectCheck.getClass().getName() + "\n"); +// } +// else { +// outputInfo.append("ObjComp: equal NEW: " + demangledObject.getClass().getName() + +// ", OLD: " + demangledObjectCheck.getClass().getName() + "\n"); +// } +// } +// else { +// if ((demangledObjectCheck == null) && (demangledObject == null)) { +// outputInfo.append("ObjComp: Not possible -- both null\n"); +// } +// else if (demangledObjectCheck == null) { +// outputInfo.append("ObjComp: Not possible -- OLD null; NEW: " + +// demangledObject.getClass().getName() + "\n"); +// } +// else { +// outputInfo.append("ObjComp: Not possible -- NEW null; OLD: " + +// demangledObjectCheck.getClass().getName() + "\n"); +// } +// } +// if (ghidraTestStringCompare(outputInfo, demangled, demangledGhidraObject)) { +// outputInfo.append("RESULTS MATCH------******\n"); +// } +// else { +// outputInfo.append("RESULTS MISMATCH------*********************************\n"); +// } +// } private boolean ghidraTestStringCompare(StringBuilder outputInfoArg, String truthString, String ghidraString) { diff --git a/Ghidra/Features/MicrosoftDmang/src/test/java/mdemangler/MDMangBaseTest.java b/Ghidra/Features/MicrosoftDmang/src/test/java/mdemangler/MDMangBaseTest.java index b592b64cbf..11b71a3ffa 100644 --- a/Ghidra/Features/MicrosoftDmang/src/test/java/mdemangler/MDMangBaseTest.java +++ b/Ghidra/Features/MicrosoftDmang/src/test/java/mdemangler/MDMangBaseTest.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -194,6 +194,12 @@ public class MDMangBaseTest extends AbstractGenericTest { ms2013Truth); } + private void demangleAndTestFunction() throws Exception { + testConfiguration.setIsFunction(true); + testConfiguration.demangleAndTest(testName, mangled, mdTruth, msTruth, ghTruth, + ms2013Truth); + } + @Test public void testTripleQ0() throws Exception { mangled = "???__E??_7name0@name1@@6B@@@YMXXZ@?A0x647dec29@@$$FYMXXZ"; @@ -15312,6 +15318,81 @@ public class MDMangBaseTest extends AbstractGenericTest { demangleAndTest(); } + //===================== + /* + * Follow are C-style mangling scheme under 32-bit model; __vectorcall also valid for 64-bit + * __cdecl: '_' prefix; no suffix; example "_name" + * __stdcall: '_' prefix; "@" suffix; example "_name@12" + * __fastcall: '@' prefix; "@" suffix; example "@name@12" + * __vectorcall: no prefix; "@@" suffix; example "name@@12" + * + * We've come up with the string output formats for the C-style mangling scheme. + */ + + @Test + public void testCStyleCdeclFunction() throws Exception { + mangled = "_name"; + mdTruth = "name"; + msTruth = ""; + demangleAndTestFunction(); + } + + @Test + public void testCStyleCdeclNoFunction() throws Exception { + mangled = "_name"; + mdTruth = "_name"; + msTruth = ""; + demangleAndTest(); + } + + @Test + public void testCStyleStdcallFunction() throws Exception { + mangled = "_name@12"; + mdTruth = "__stdcall name,12"; + msTruth = ""; + demangleAndTestFunction(); + } + + @Test + public void testCStyleStdcallNoFunction() throws Exception { + mangled = "_name@12"; + mdTruth = ""; + msTruth = ""; + demangleAndTest(); + } + + @Test + public void testCStyleFastcallFunction() throws Exception { + mangled = "@name@12"; + mdTruth = "__fastcall name,12"; + msTruth = ""; + demangleAndTestFunction(); + } + + @Test + public void testCStyleFastcallNoFunction() throws Exception { + mangled = "@name@12"; + mdTruth = ""; + msTruth = ""; + demangleAndTest(); + } + + @Test + public void testCStyleVectorcallFunction() throws Exception { + mangled = "name@@12"; + mdTruth = "__vectorcall name,12"; + msTruth = ""; + demangleAndTestFunction(); + } + + @Test + public void testCStyleVectorcallNoFunction() throws Exception { + mangled = "name@@12"; + mdTruth = ""; + msTruth = ""; + demangleAndTest(); + } + //===================== //TODO: ignore for now. diff --git a/Ghidra/Features/MicrosoftDmang/src/test/java/mdemangler/MDMangExtraTest.java b/Ghidra/Features/MicrosoftDmang/src/test/java/mdemangler/MDMangExtraTest.java index c3c5fcf105..5cfb137c6c 100644 --- a/Ghidra/Features/MicrosoftDmang/src/test/java/mdemangler/MDMangExtraTest.java +++ b/Ghidra/Features/MicrosoftDmang/src/test/java/mdemangler/MDMangExtraTest.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -22,7 +22,6 @@ import java.util.List; import org.junit.Test; import generic.test.AbstractGenericTest; -import ghidra.app.util.demangler.DemangledObject; import mdemangler.naming.MDQualification; import mdemangler.object.MDObjectCPP; import mdemangler.typeinfo.MDVxTable; @@ -33,32 +32,6 @@ import mdemangler.typeinfo.MDVxTable; */ public class MDMangExtraTest extends AbstractGenericTest { - @Test - //This test checks that we can provide a mangled string for a function namespace. - // The return String from getOriginalMangled() is not null only for this special - // circumstance. So, in normal processing, we should check it for non-null to - // determine that we have a result of this form. - // The symbol here is from our cn3.cpp source target. - public void testFunctionNamespace() throws Exception { - String mangled = "?fn3@?2??Bar3@Foo2b@@SAHXZ@4HA"; - String wholeTruth = "int `public: static int __cdecl Foo2b::Bar3(void)'::`3'::fn3"; - String functionNamespaceMangledTruth = "?Bar3@Foo2b@@SAHXZ"; - String functionNamespaceTruth = "public: static int __cdecl Foo2b::Bar3(void)"; - - MDMangGhidra demangler = new MDMangGhidra(); - MDParsableItem item = demangler.demangle(mangled, true, true); - - String demangled = item.toString(); - assertEquals(wholeTruth, demangled); - DemangledObject obj = demangler.getObject(); - String mangledFunctionNamespace = obj.getNamespace().getNamespace().getMangledString(); - assertEquals(functionNamespaceMangledTruth, mangledFunctionNamespace); - - item = demangler.demangle(mangledFunctionNamespace, true, true); - demangled = item.toString(); - assertEquals(functionNamespaceTruth, demangled); - } - @Test public void testVxTableNestedQualifications() throws Exception { // Test string taken from MDMangBaseTest @@ -66,7 +39,10 @@ public class MDMangExtraTest extends AbstractGenericTest { String truth = "const b::a::`vftable'{for `e::d::c's `h::g::f's `k::j::i'}"; MDMangGhidra demangler = new MDMangGhidra(); - MDParsableItem item = demangler.demangle(mangled, true, true); + demangler.setMangledSymbol(mangled); + demangler.setErrorOnRemainingChars(true); + demangler.setDemangleOnlyKnownPatterns(true); + MDParsableItem item = demangler.demangle(); String demangled = item.toString(); assertEquals(truth, demangled); @@ -88,7 +64,9 @@ public class MDMangExtraTest extends AbstractGenericTest { String truth = "enum `void __cdecl name2::name1(bool)'::name0"; MDMangGhidra demangler = new MDMangGhidra(); - MDParsableItem item = demangler.demangleType(mangled, true); // note demangleType() + demangler.setMangledSymbol(mangled); + demangler.setErrorOnRemainingChars(true); + MDParsableItem item = demangler.demangleType(); // note demangleType() String demangled = item.toString(); assertEquals(truth, demangled); diff --git a/Ghidra/Features/MicrosoftDmang/src/test/java/mdemangler/MDMangUtilsTest.java b/Ghidra/Features/MicrosoftDmang/src/test/java/mdemangler/MDMangUtilsTest.java index 79f5f756ee..a41ec7a5b8 100644 --- a/Ghidra/Features/MicrosoftDmang/src/test/java/mdemangler/MDMangUtilsTest.java +++ b/Ghidra/Features/MicrosoftDmang/src/test/java/mdemangler/MDMangUtilsTest.java @@ -43,7 +43,9 @@ public class MDMangUtilsTest extends AbstractGenericTest { "class `struct name1::name2 __cdecl name1::name0(struct name1::name3,struct name1::name4)'::`1'::"; MDMangGhidra demangler = new MDMangGhidra(); - MDDataType item = demangler.demangleType(mangled, true); + demangler.setMangledSymbol(mangled); + demangler.setErrorOnRemainingChars(true); + MDDataType item = demangler.demangleType(); String demangled = item.toString(); SymbolPath symbolPath = MDMangUtils.getSymbolPath(item); @@ -69,7 +71,9 @@ public class MDMangUtilsTest extends AbstractGenericTest { "struct name8::name7::name0 && __ptr64>"; MDMangGhidra demangler = new MDMangGhidra(); - MDDataType item = demangler.demangleType(mangled, true); + demangler.setMangledSymbol(mangled); + demangler.setErrorOnRemainingChars(true); + MDDataType item = demangler.demangleType(); String demangled = item.toString(); SymbolPath symbolPath = MDMangUtils.getSymbolPath(item); @@ -94,7 +98,9 @@ public class MDMangUtilsTest extends AbstractGenericTest { "struct name4::name3::name0 && __ptr64>"; MDMangGhidra demangler = new MDMangGhidra(); - MDDataType item = demangler.demangleType(mangled, true); + demangler.setMangledSymbol(mangled); + demangler.setErrorOnRemainingChars(true); + MDDataType item = demangler.demangleType(); String demangled = item.toString(); SymbolPath symbolPath = MDMangUtils.getSymbolPath(item); diff --git a/Ghidra/Features/MicrosoftDmang/src/test/java/mdemangler/MDVS2013TestConfiguration.java b/Ghidra/Features/MicrosoftDmang/src/test/java/mdemangler/MDVS2013TestConfiguration.java index a8e0143158..7226ee7e3f 100644 --- a/Ghidra/Features/MicrosoftDmang/src/test/java/mdemangler/MDVS2013TestConfiguration.java +++ b/Ghidra/Features/MicrosoftDmang/src/test/java/mdemangler/MDVS2013TestConfiguration.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -41,8 +41,10 @@ public class MDVS2013TestConfiguration extends MDBaseTestConfiguration { @Override protected MDParsableItem doDemangleSymbol(MDMang mdmIn, String mangledIn) throws Exception { + mdmIn.setMangledSymbol(mangledIn); + // We are not setting errorOnRemainingChars to true (diff from base test) try { - return mdmIn.demangle(mangledIn, false); // "false" is different + return mdmIn.demangle(); } catch (MDException e) { return null; diff --git a/Ghidra/Features/MicrosoftDmang/src/test/java/mdemangler/MDVS2015TestConfiguration.java b/Ghidra/Features/MicrosoftDmang/src/test/java/mdemangler/MDVS2015TestConfiguration.java index 2ab8e3c80b..a113ca20d4 100644 --- a/Ghidra/Features/MicrosoftDmang/src/test/java/mdemangler/MDVS2015TestConfiguration.java +++ b/Ghidra/Features/MicrosoftDmang/src/test/java/mdemangler/MDVS2015TestConfiguration.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -36,8 +36,10 @@ public class MDVS2015TestConfiguration extends MDBaseTestConfiguration { @Override protected MDParsableItem doDemangleSymbol(MDMang mdmIn, String mangledIn) throws Exception { + mdmIn.setMangledSymbol(mangledIn); + // We are not setting errorOnRemainingChars to true (diff from base test) try { - return mdmIn.demangle(mangledIn, false); // "false" is different + return mdmIn.demangle(); } catch (MDException e) { return null; diff --git a/Ghidra/Features/PDB/developer_scripts/DumpAllSymbolsDemangledScript.java b/Ghidra/Features/PDB/developer_scripts/DumpAllSymbolsDemangledScript.java index 2645ab3a3d..9d3148a9c1 100644 --- a/Ghidra/Features/PDB/developer_scripts/DumpAllSymbolsDemangledScript.java +++ b/Ghidra/Features/PDB/developer_scripts/DumpAllSymbolsDemangledScript.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -75,8 +75,10 @@ public class DumpAllSymbolsDemangledScript extends GhidraScript { */ private static String getDemangledString(String mangledString) { MDMangGhidra demangler = new MDMangGhidra(); + demangler.setMangledSymbol(mangledString); + demangler.setErrorOnRemainingChars(true); try { - MDParsableItem parsableItem = demangler.demangle(mangledString, true); + MDParsableItem parsableItem = demangler.demangle(); if (parsableItem instanceof MDObjectCPP) { MDObjectCPP mdObject = (MDObjectCPP) parsableItem; return mdObject.getQualifiedName().toString(); diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/AbstractComplexTypeApplier.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/AbstractComplexTypeApplier.java index dfa9499cbe..afd8bec998 100644 --- a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/AbstractComplexTypeApplier.java +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/AbstractComplexTypeApplier.java @@ -118,8 +118,10 @@ public abstract class AbstractComplexTypeApplier extends MsDataTypeApplier { private static SymbolPath getSymbolPathFromMangledTypeName(String mangledString, String fullPathName) { MDMang demangler = new MDMangGhidra(); + demangler.setErrorOnRemainingChars(true); + demangler.setMangledSymbol(mangledString); try { - MDDataType mdDataType = demangler.demangleType(mangledString, true); + MDDataType mdDataType = demangler.demangleType(); // 20240626: Ultimately, it might be better to retrieve the Demangled-type to pass // to the DemangledObject.createNamespace() method to convert to a true Ghidra // Namespace that are flagged as functions (not capable at this time) or types or diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/PdbResearch.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/PdbResearch.java index d619b9b627..0446724034 100644 --- a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/PdbResearch.java +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/PdbResearch.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -798,8 +798,10 @@ public class PdbResearch { return null; } MDMangGhidra demangler = new MDMangGhidra(); + demangler.setMangledSymbol(mangledString); + demangler.setErrorOnRemainingChars(true); try { - MDParsableItem parsableItem = demangler.demangle(mangledString, true); + MDParsableItem parsableItem = demangler.demangle(); if (parsableItem instanceof MDObjectCPP) { MDObjectCPP mdObject = (MDObjectCPP) parsableItem; return mdObject.getQualifiedName().toString(); diff --git a/Ghidra/Features/SwiftDemangler/ghidra_scripts/SwiftDemanglerScript.java b/Ghidra/Features/SwiftDemangler/ghidra_scripts/SwiftDemanglerScript.java index 8b046a3a26..7dcdcb0ab4 100644 --- a/Ghidra/Features/SwiftDemangler/ghidra_scripts/SwiftDemanglerScript.java +++ b/Ghidra/Features/SwiftDemangler/ghidra_scripts/SwiftDemanglerScript.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -19,6 +19,7 @@ //@category Demangler import ghidra.app.script.GhidraScript; import ghidra.app.util.demangler.DemangledObject; +import ghidra.app.util.demangler.MangledContext; import ghidra.app.util.demangler.swift.*; import ghidra.app.util.demangler.swift.SwiftNativeDemangler.SwiftNativeDemangledOutput; import ghidra.program.model.symbol.*; @@ -53,12 +54,14 @@ public class SwiftDemanglerScript extends GhidraScript { println("No mangled Swift symbols found at " + currentAddress); return; } - + SwiftNativeDemangler nativeDemangler = new SwiftNativeDemangler(options.getSwiftDir()); SwiftNativeDemangledOutput demangledOutput = nativeDemangler.demangle(mangled); println(demangledOutput.toString()); - - DemangledObject demangledObject = demangler.demangle(mangled); + + MangledContext mangledContext = + demangler.createMangledContext(mangled, options, currentProgram, currentAddress); + DemangledObject demangledObject = demangler.demangle(mangledContext); if (demangledObject != null) { println(demangledObject.getClass().getSimpleName() + " " + mangled + " --> " + demangledObject); diff --git a/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/plugin/core/analysis/SwiftDemanglerAnalyzer.java b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/plugin/core/analysis/SwiftDemanglerAnalyzer.java index 5937c9b8b1..48c58cd410 100644 --- a/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/plugin/core/analysis/SwiftDemanglerAnalyzer.java +++ b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/plugin/core/analysis/SwiftDemanglerAnalyzer.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -57,13 +57,13 @@ public class SwiftDemanglerAnalyzer extends AbstractDemanglerAnalyzer { private File swiftDir; private boolean useIncompletePrefix = true; private boolean useUnsupportedPrefix = true; - private SwiftDemangler demangler = new SwiftDemangler(); /** * Creates a new {@link SwiftDemanglerAnalyzer} */ public SwiftDemanglerAnalyzer() { super(NAME, DESCRIPTION); + demangler = new SwiftDemangler(); setDefaultEnablement(true); } @@ -76,7 +76,7 @@ public class SwiftDemanglerAnalyzer extends AbstractDemanglerAnalyzer { public boolean added(Program program, AddressSetView set, TaskMonitor monitor, MessageLog log) throws CancelledException { try { - demangler.initialize(program); + ((SwiftDemangler) demangler).initialize(program); } catch (IOException e) { log.appendMsg(e.getMessage()); @@ -86,9 +86,9 @@ public class SwiftDemanglerAnalyzer extends AbstractDemanglerAnalyzer { } @Override - protected DemangledObject doDemangle(String mangled, DemanglerOptions options, MessageLog log) + protected DemangledObject doDemangle(MangledContext mangledContext, MessageLog log) throws DemangledException { - return demangler.demangle(mangled, options); + return demangler.demangle(mangledContext); } @Override diff --git a/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/SwiftDemangler.java b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/SwiftDemangler.java index 4fe64833ff..a36d05a8f7 100644 --- a/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/SwiftDemangler.java +++ b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/SwiftDemangler.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -54,7 +54,7 @@ public class SwiftDemangler implements Demangler { /** * Creates a new {@link SwiftDemangler} that is associated with the given {@link Program} - * + * * @param program The {@link Program} to demangle * @throws IOException if there was a problem parsing the Swift type metadata */ @@ -73,12 +73,6 @@ public class SwiftDemangler implements Demangler { return new SwiftDemanglerOptions(); } - @Override - public DemangledObject demangle(String mangled, boolean demangleOnlyKnownPatterns) - throws DemangledException { - return demangle(mangled); - } - public void initialize(Program program) throws IOException { cache = new HashMap<>(); nativeDemangler = null; @@ -95,6 +89,7 @@ public class SwiftDemangler implements Demangler { } @Override + @Deprecated(since = "11.3", forRemoval = true) public DemangledObject demangle(String mangled, DemanglerOptions op) throws DemangledException { SwiftDemanglerOptions options = getSwiftDemanglerOptions(op); Demangled demangled = getDemangled(mangled, options); @@ -111,9 +106,18 @@ public class SwiftDemangler implements Demangler { return null; } + @Override + public DemangledObject demangle(MangledContext context) throws DemangledException { + DemanglerOptions op = context.getOptions(); + String mangled = context.getMangled(); + DemangledObject demangledObject = demangle(mangled, op); + demangledObject.setMangledContext(context); + return demangledObject; + } + /** * Get a new {@link Demangled} by demangling the given mangled string - * + * * @param mangled The mangled string * @param op The options (could be null) * @return A new {@link Demangled} @@ -145,7 +149,7 @@ public class SwiftDemangler implements Demangler { /** * Gets the {@link SwiftTypeMetadata} - * + * * @return The {@link SwiftTypeMetadata}, or null if it is not available */ public SwiftTypeMetadata getTypeMetadata() { @@ -154,7 +158,7 @@ public class SwiftDemangler implements Demangler { /** * Checks to see whether the given symbol name is a mangled Swift symbol - * + * * @param symbolName The symbol name to check * @return True if the given symbol name is a mangled Swift symbol; otherwise, false */ @@ -165,7 +169,7 @@ public class SwiftDemangler implements Demangler { /** * Gets the {@link SwiftDemanglerOptions} from the given {@link DemanglerOptions} - * + * * @param opt The options * @return The @link SwiftDemanglerOptions} * @throws DemangledException If the given options are not {@link SwiftDemanglerOptions} @@ -180,7 +184,7 @@ public class SwiftDemangler implements Demangler { /** * Ensures that this demangler has access to a {@link SwiftNativeDemangler} - * + * * @param options The options * @throws DemangledException if there was a problem getting the {@link SwiftNativeDemangler} */ diff --git a/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/app/util/demangler/DemangledAddressTableTest.java b/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/app/util/demangler/DemangledAddressTableTest.java index 6889b77e14..1a2afffcc3 100644 --- a/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/app/util/demangler/DemangledAddressTableTest.java +++ b/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/app/util/demangler/DemangledAddressTableTest.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -19,6 +19,7 @@ import static org.junit.Assert.*; import org.junit.*; +import ghidra.app.util.demangler.gnu.GnuDemangler; import ghidra.program.database.ProgramDB; import ghidra.program.model.address.Address; import ghidra.program.model.data.*; @@ -74,9 +75,10 @@ public class DemangledAddressTableTest extends AbstractGhidraHeadlessIntegration } /** - * Test that the DemangledAddressTable will properly create a sequence of data + * Test that the DemangledAddressTable will properly create a sequence of data * pointers. This test deals with the simple case where no existing data * is present. End of block considered end of address table. + * @throws Exception upon error */ @Test public void testApply_NoNextSymbol_NoData() throws Exception { @@ -88,10 +90,13 @@ public class DemangledAddressTableTest extends AbstractGhidraHeadlessIntegration SymbolTable symbolTable = program.getSymbolTable(); symbolTable.createLabel(addr, mangled, SourceType.IMPORTED); - DemangledObject demangled = DemanglerUtil.demangle(mangled); + GnuDemangler demangler = new GnuDemangler(); + MangledContext mangledContext = + demangler.createMangledContext(mangled, null, program, addr); + DemangledObject demangled = demangler.demangle(mangledContext); assertTrue(demangled instanceof DemangledAddressTable); - - assertTrue(demangled.applyTo(program, addr, new DemanglerOptions(), TaskMonitor.DUMMY)); + assertTrue(demangled.getMangledContext() != null); + assertTrue(demangled.applyUsingContext(TaskMonitor.DUMMY)); // expected: UniqueSpace::vtable Symbol[] symbols = symbolTable.getSymbols(addr); @@ -107,9 +112,10 @@ public class DemangledAddressTableTest extends AbstractGhidraHeadlessIntegration } /** - * Test that the DemangledAddressTable will not create a sequence of data - * pointers due to a data collision. This test deals with the case where primitive types have been + * Test that the DemangledAddressTable will not create a sequence of data + * pointers due to a data collision. This test deals with the case where primitive types have been * previously created. End of block considered end of address table. + * @throws Exception upon error */ @Test public void testApply_NoNextSymbol_DataCollision() throws Exception { @@ -126,10 +132,13 @@ public class DemangledAddressTableTest extends AbstractGhidraHeadlessIntegration listing.createData(addr("0x118"), Undefined4DataType.dataType); listing.createData(addr("0x120"), DWordDataType.dataType); - DemangledObject demangled = DemanglerUtil.demangle(mangled); + GnuDemangler demangler = new GnuDemangler(); + MangledContext mangledContext = + demangler.createMangledContext(mangled, null, program, addr); + DemangledObject demangled = demangler.demangle(mangledContext); assertTrue(demangled instanceof DemangledAddressTable); - - assertTrue(demangled.applyTo(program, addr, new DemanglerOptions(), TaskMonitor.DUMMY)); + assertTrue(demangled.getMangledContext() != null); + assertTrue(demangled.applyUsingContext(TaskMonitor.DUMMY)); // expected: UniqueSpace::vtable Symbol[] symbols = symbolTable.getSymbols(addr); @@ -146,9 +155,10 @@ public class DemangledAddressTableTest extends AbstractGhidraHeadlessIntegration } /** - * Test that the DemangledAddressTable will properly create a sequence of data - * pointers. This test deals with the case where primitive types have been + * Test that the DemangledAddressTable will properly create a sequence of data + * pointers. This test deals with the case where primitive types have been * previously created. Next label considered end of address table. + * @throws Exception upon error */ @Test public void testApply_WithNextSymbol_UndefinedData() throws Exception { @@ -167,10 +177,13 @@ public class DemangledAddressTableTest extends AbstractGhidraHeadlessIntegration symbolTable.createLabel(addr("0x120"), "NextLabel", SourceType.IMPORTED); - DemangledObject demangled = DemanglerUtil.demangle(mangled); + GnuDemangler demangler = new GnuDemangler(); + MangledContext mangledContext = + demangler.createMangledContext(mangled, null, program, addr); + DemangledObject demangled = demangler.demangle(mangledContext); assertTrue(demangled instanceof DemangledAddressTable); - - assertTrue(demangled.applyTo(program, addr, new DemanglerOptions(), TaskMonitor.DUMMY)); + assertTrue(demangled.getMangledContext() != null); + assertTrue(demangled.applyUsingContext(TaskMonitor.DUMMY)); // expected: UniqueSpace::vtable Symbol[] symbols = symbolTable.getSymbols(addr); @@ -185,10 +198,11 @@ public class DemangledAddressTableTest extends AbstractGhidraHeadlessIntegration } /** - * Test that the DemangledAddressTable will properly create a sequence of data - * pointers. This test deals with the case where primitive types have been - * previously created where the first is an undefined array which dictates the + * Test that the DemangledAddressTable will properly create a sequence of data + * pointers. This test deals with the case where primitive types have been + * previously created where the first is an undefined array which dictates the * extent of the address table. Next label beyond end of address table. + * @throws Exception upon error */ @Test public void testApply_WithUndefinedArray() throws Exception { @@ -207,10 +221,13 @@ public class DemangledAddressTableTest extends AbstractGhidraHeadlessIntegration symbolTable.createLabel(addr("0x120"), "NextLabel", SourceType.IMPORTED); - DemangledObject demangled = DemanglerUtil.demangle(mangled); + GnuDemangler demangler = new GnuDemangler(); + MangledContext mangledContext = + demangler.createMangledContext(mangled, null, program, addr); + DemangledObject demangled = demangler.demangle(mangledContext); assertTrue(demangled instanceof DemangledAddressTable); - - assertTrue(demangled.applyTo(program, addr, new DemanglerOptions(), TaskMonitor.DUMMY)); + assertTrue(demangled.getMangledContext() != null); + assertTrue(demangled.applyUsingContext(TaskMonitor.DUMMY)); // expected: UniqueSpace::vtable Symbol[] symbols = symbolTable.getSymbols(addr); diff --git a/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/app/util/demangler/DemangledFunctionTest.java b/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/app/util/demangler/DemangledFunctionTest.java index 8d324c3011..cdca51a6f6 100644 --- a/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/app/util/demangler/DemangledFunctionTest.java +++ b/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/app/util/demangler/DemangledFunctionTest.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -21,6 +21,8 @@ import java.util.Arrays; import org.junit.*; +import ghidra.app.util.demangler.gnu.GnuDemangler; +import ghidra.app.util.demangler.microsoft.*; import ghidra.program.database.ProgramBuilder; import ghidra.program.database.ProgramDB; import ghidra.program.model.address.Address; @@ -62,11 +64,16 @@ public class DemangledFunctionTest extends AbstractGhidraHeadlessIntegrationTest // this is: public long __thiscall ATL::CRegKey::Close(void) String mangled = "?CloseM@CRegKeyM@ATL@@QAEJXZ"; - DemangledObject demangled = DemanglerUtil.demangle(mangled); + Address addr = addr("0x0101"); + MicrosoftDemangler demangler = new MicrosoftDemangler(); + MicrosoftDemanglerOptions options = demangler.createDefaultOptions(); + MicrosoftMangledContext mangledContext = + demangler.createMangledContext(mangled, options, program, addr); + options.setInterpretation(MsCInterpretation.FUNCTION); + DemangledObject demangled = demangler.demangle(mangledContext); assertTrue(demangled instanceof DemangledFunction); - Address addr = addr("0x0101"); - assertTrue(demangled.applyTo(program, addr, new DemanglerOptions(), TaskMonitor.DUMMY)); + assertTrue(demangled.applyTo(program, addr, options, TaskMonitor.DUMMY)); assertFunction("CloseM", addr); @@ -95,7 +102,14 @@ public class DemangledFunctionTest extends AbstractGhidraHeadlessIntegrationTest // this is: public long __thiscall ATL::CRegKey::Close(void) String mangled = "?Close@CRegKey@ATL@@QAEJXZ"; - DemangledObject demangled = DemanglerUtil.demangle(mangled); + Address addr = addr("0x0100"); + MicrosoftDemangler demangler = new MicrosoftDemangler(); + MangledContext mangledContext = + demangler.createMangledContext(mangled, null, program, addr); + DemanglerOptions options = mangledContext.getOptions(); + // TODO: need direct way to change "for function" vs. just address and program; which might mean MicrosoftDemanglerContext + //mangledContext.setIsFunction(true); + DemangledObject demangled = demangler.demangle(mangledContext); assertTrue(demangled instanceof DemangledFunction); FunctionManager functionMgr = program.getFunctionManager(); @@ -105,8 +119,7 @@ public class DemangledFunctionTest extends AbstractGhidraHeadlessIntegrationTest new AddressSet(addr("0x0100")), SourceType.IMPORTED); f2.setThunkedFunction(f1); - Address addr = addr("0x0100"); - demangled.applyTo(program, addr, new DemanglerOptions(), TaskMonitor.DUMMY); + demangled.applyTo(program, addr, options, TaskMonitor.DUMMY); assertFunction("Close", addr); assertNoBookmarkAt(addr); @@ -148,10 +161,16 @@ public class DemangledFunctionTest extends AbstractGhidraHeadlessIntegrationTest SymbolTable symbolTable = program.getSymbolTable(); symbolTable.createLabel(addr, mangled, SourceType.IMPORTED); - DemangledObject demangled = DemanglerUtil.demangle(mangled); + MicrosoftDemangler demangler = new MicrosoftDemangler(); + MangledContext mangledContext = + demangler.createMangledContext(mangled, null, program, addr); + DemanglerOptions options = mangledContext.getOptions(); + // TODO: need direct way to change "for function" vs. just address and program; which might mean MicrosoftDemanglerContext + //mangledContext.setIsFunction(true); + DemangledObject demangled = demangler.demangle(mangledContext); assertTrue(demangled instanceof DemangledFunction); - assertTrue(demangled.applyTo(program, addr, new DemanglerOptions(), TaskMonitor.DUMMY)); + assertTrue(demangled.applyTo(program, addr, options, TaskMonitor.DUMMY)); assertFunction("Close", addr); assertNoBookmarkAt(addr); @@ -185,10 +204,16 @@ public class DemangledFunctionTest extends AbstractGhidraHeadlessIntegrationTest String mangledWithAddr = SymbolUtilities.getAddressAppendedName(mangled, addr); symbolTable.createLabel(addr, mangledWithAddr, SourceType.IMPORTED); - DemangledObject demangled = DemanglerUtil.demangle(mangled); + MicrosoftDemangler demangler = new MicrosoftDemangler(); + MangledContext mangledContext = + demangler.createMangledContext(mangled, null, program, addr); + DemanglerOptions options = mangledContext.getOptions(); + // TODO: need direct way to change "for function" vs. just address and program; which might mean MicrosoftDemanglerContext + //mangledContext.setIsFunction(true); + DemangledObject demangled = demangler.demangle(mangledContext); assertTrue(demangled instanceof DemangledFunction); - assertTrue(demangled.applyTo(program, addr, new DemanglerOptions(), TaskMonitor.DUMMY)); + assertTrue(demangled.applyTo(program, addr, options, TaskMonitor.DUMMY)); assertFunction("Close", addr); assertNoBookmarkAt(addr); @@ -223,10 +248,16 @@ public class DemangledFunctionTest extends AbstractGhidraHeadlessIntegrationTest symbolTable.createLabel(addr, "Close", SourceType.IMPORTED); symbolTable.createLabel(addr, mangled, SourceType.IMPORTED); - DemangledObject demangled = DemanglerUtil.demangle(mangled); + MicrosoftDemangler demangler = new MicrosoftDemangler(); + MangledContext mangledContext = + demangler.createMangledContext(mangled, null, program, addr); + DemanglerOptions options = mangledContext.getOptions(); + // TODO: need direct way to change "for function" vs. just address and program; which might mean MicrosoftDemanglerContext + //mangledContext.setIsFunction(true); + DemangledObject demangled = demangler.demangle(mangledContext); assertTrue(demangled instanceof DemangledFunction); - assertTrue(demangled.applyTo(program, addr, new DemanglerOptions(), TaskMonitor.DUMMY)); + assertTrue(demangled.applyTo(program, addr, options, TaskMonitor.DUMMY)); assertFunction("Close", addr); assertNoBookmarkAt(addr); @@ -258,9 +289,15 @@ public class DemangledFunctionTest extends AbstractGhidraHeadlessIntegrationTest SymbolTable symbolTable = program.getSymbolTable(); symbolTable.createLabel(addr, mangled, SourceType.IMPORTED); - DemangledObject demangled = DemanglerUtil.demangle(mangled); + MicrosoftDemangler demangler = new MicrosoftDemangler(); + MangledContext mangledContext = + demangler.createMangledContext(mangled, null, program, addr); + DemanglerOptions options = mangledContext.getOptions(); + // TODO: need direct way to change "for function" vs. just address and program; which might mean MicrosoftDemanglerContext + //mangledContext.setIsFunction(true); + DemangledObject demangled = demangler.demangle(mangledContext); assertTrue(demangled instanceof DemangledFunction); - assertTrue(demangled.applyTo(program, addr, new DemanglerOptions(), TaskMonitor.DUMMY)); + assertTrue(demangled.applyTo(program, addr, options, TaskMonitor.DUMMY)); String className = "F,bool,enum_C::B_const&>_>"; @@ -306,10 +343,16 @@ public class DemangledFunctionTest extends AbstractGhidraHeadlessIntegrationTest Address addr = extLoc.getExternalSpaceAddress(); - DemangledObject demangled = DemanglerUtil.demangle(mangled); + MicrosoftDemangler demangler = new MicrosoftDemangler(); + MangledContext mangledContext = + demangler.createMangledContext(mangled, null, program, addr); + DemanglerOptions options = mangledContext.getOptions(); + // TODO: need direct way to change "for function" vs. just address and program; which might mean MicrosoftDemanglerContext + //mangledContext.setIsFunction(true); + DemangledObject demangled = demangler.demangle(mangledContext); assertTrue(demangled instanceof DemangledFunction); - assertTrue(demangled.applyTo(program, addr, new DemanglerOptions(), TaskMonitor.DUMMY)); + assertTrue(demangled.applyTo(program, addr, options, TaskMonitor.DUMMY)); assertFunction("Close", addr); assertNoBookmarkAt(addr); @@ -348,16 +391,20 @@ public class DemangledFunctionTest extends AbstractGhidraHeadlessIntegrationTest String functionName = "__gthread_active_p"; programBuilder.createEmptyFunction(functionName, "0x0101", 10, new VoidDataType()); + Address addr = addr("0x0103"); programBuilder.createLabel("0x0103", mangled); - DemangledObject demangled = DemanglerUtil.demangle(program, mangled); + GnuDemangler demangler = new GnuDemangler(); + MangledContext mangledContext = + demangler.createMangledContext(mangled, null, program, addr); + DemanglerOptions options = mangledContext.getOptions(); + DemangledObject demangled = demangler.demangle(mangledContext); assertNotNull(demangled); assertTrue(demangled instanceof DemangledVariable); assertEquals("__gthread_active_p()::__gthread_active_ptr", demangled.getSignature(false)); - Address addr = addr("0x0103"); - demangled.applyTo(program, addr, new DemanglerOptions(), TaskMonitor.DUMMY); + demangled.applyTo(program, addr, options, TaskMonitor.DUMMY); assertSimpleNamespaceExists("__gthread_active_p()"); assertNoBookmarkAt(addr); @@ -376,14 +423,19 @@ public class DemangledFunctionTest extends AbstractGhidraHeadlessIntegrationTest public void testApply_Function_DoNotApplyCallingConvention() throws Exception { String mangled = "?CloseM@CRegKeyM@ATL@@QAEJXZ"; - DemangledObject demangled = DemanglerUtil.demangle(mangled); + Address addr = addr("0x0101"); + MicrosoftDemangler demangler = new MicrosoftDemangler(); + MangledContext mangledContext = + demangler.createMangledContext(mangled, null, program, addr); + DemanglerOptions options = mangledContext.getOptions(); + // TODO: need direct way to change "for function" vs. just address and program; which might mean MicrosoftDemanglerContext + //mangledContext.setIsFunction(true); + DemangledObject demangled = demangler.demangle(mangledContext); assertTrue(demangled instanceof DemangledFunction); DemangledFunction demangledFunction = (DemangledFunction) demangled; demangledFunction.setCallingConvention(CompilerSpec.CALLING_CONVENTION_stdcall); - Address addr = addr("0x0101"); - DemanglerOptions options = new DemanglerOptions(); options.setApplyCallingConvention(false); assertTrue(demangled.applyTo(program, addr, options, TaskMonitor.DUMMY));

+ * {@link #setMangledContext(MangledContext)} + * + * Sets the mangled context in use since version 11.3. + *
+ * {@link #getMangledContext()} + * + * The mangled context in use since version 11.3. + *
* {@link #getName()} * @@ -38,7 +54,7 @@ package ghidra.app.util.demangler; * {@link #getDemangledName()} * - * The unmodified name that was set upon this object. + * The unmodified name that was set upon this object. *
- * The 'safe' name of this object when it is used as a namespace name. This usually has + * The 'safe' name of this object when it is used as a namespace name. This usually has * parameter and template information. Further, some characters within templates and * function signatures are replaced, such as spaces and namespace separators. *

@@ -59,9 +75,9 @@ package ghidra.app.util.demangler; * {@link #getNamespaceString()} *

- * This returns the unmodified name of this item, along with any unmodified parent - * namespace names, all separated by a namespace delimiter. Unlike - * {@link #getNamespaceName()}, the spaces and internal namespace tokens will not be + * This returns the unmodified name of this item, along with any unmodified parent + * namespace names, all separated by a namespace delimiter. Unlike + * {@link #getNamespaceName()}, the spaces and internal namespace tokens will not be * replaced. *

* Given this full demangled string: {@code Foo::Bar::Baz}, this method will return @@ -73,8 +89,8 @@ package ghidra.app.util.demangler; * {@link #getSignature()} *

- * Returns the complete string form of this object, with most known attributes. For - * functions, this will be a complete signature. + * Returns the complete string form of this object, with most known attributes. For + * functions, this will be a complete signature. *