diff --git a/Ghidra/Features/Base/src/main/help/help/topics/LocationReferencesPlugin/Location_References.html b/Ghidra/Features/Base/src/main/help/help/topics/LocationReferencesPlugin/Location_References.html index b744b46ac1..ac6b9e7006 100644 --- a/Ghidra/Features/Base/src/main/help/help/topics/LocationReferencesPlugin/Location_References.html +++ b/Ghidra/Features/Base/src/main/help/help/topics/LocationReferencesPlugin/Location_References.html @@ -58,7 +58,7 @@ references to the address 0040767d.

-

You can also show +

You can also show references to data types from the Data Type Manager. In this case, all locations where the selected data type is applied will be highlighted.

@@ -73,11 +73,11 @@
  • Select whichever of the following is available from the popup menu: @@ -134,12 +134,12 @@

    - + To instead make a Program Selection Highlight, use the select button mentioned above. Then, click from the menu bar Select - -> + -> Program Highlight - -> + -> Entire Selection

    @@ -167,7 +167,7 @@ the decompiled function will be shown.

    -

    You can make a +

    You can make a selection in the Code Browser from the entries in the table:

      @@ -198,16 +198,26 @@ the action will display all places that function is applied.

      + +
      +

      + When searching for references to a field of + a composite data type via the popup menu in the Data Type Manager, + you can search by name or by offset within the parent data type. This is useful when + the field you seek does not have a name. +

      +
      +

      - + By default, finding uses of data types will search not only for applied data types, but also will perform dynamic discovery of data types using the Data Type Reference Finder service. This causes the search to be slower, but also reports many more type uses. To disable the dynamic searching, use the - Search-> + Search-> Dynamic Data Type Discovery tool option.

      @@ -221,7 +231,7 @@
      1. Right-mouse anywhere on the code unit*
      2. -
      3. Select References Show References to +
      4. Select References Show References to Address from the popup menu.
      @@ -240,7 +250,7 @@

      - This action will show only direct + This action will show only direct references to the current code unit. No other special reference finding will take place.

      @@ -250,7 +260,7 @@

      - see see Docking Windows - Renaming Windows

      diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/actions/AbstractFindReferencesDataTypeAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/actions/AbstractFindReferencesDataTypeAction.java index fb6c9f9930..55517e73ff 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/actions/AbstractFindReferencesDataTypeAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/actions/AbstractFindReferencesDataTypeAction.java @@ -25,9 +25,10 @@ import docking.DockingUtils; import docking.action.*; import ghidra.app.plugin.core.navigation.FindAppliedDataTypesService; import ghidra.app.plugin.core.navigation.locationreferences.ReferenceUtils; +import ghidra.app.services.FieldMatcher; import ghidra.app.util.HelpTopics; import ghidra.framework.plugintool.PluginTool; -import ghidra.program.model.data.DataType; +import ghidra.program.model.data.*; import ghidra.util.*; public abstract class AbstractFindReferencesDataTypeAction extends DockingAction { @@ -89,7 +90,40 @@ public abstract class AbstractFindReferencesDataTypeAction extends DockingAction DataType dataType = getDataType(context); DataType baseDataType = ReferenceUtils.getBaseDataType(dataType); String field = getDataTypeField(baseDataType); - Swing.runLater(() -> service.findAndDisplayAppliedDataTypeAddresses(baseDataType, field)); + Swing.runLater(() -> doFindDataTypeUsage(service, baseDataType, field)); } + private void doFindDataTypeUsage(FindAppliedDataTypesService service, DataType dt, + String field) { + + if (field == null) { + // no field specified; search for all uses of the given type + service.findAndDisplayAppliedDataTypeAddresses(dt); + return; + } + + if (dt instanceof Structure) { + Integer offset = getOffsetForDeafaultFieldName((Structure) dt, field); + if (offset != null) { + // The user has picked a field by it's default name. In this case we need to + // search by offset to ensure we find code that does not use the default name. + FieldMatcher fieldMatcher = new FieldMatcher(dt, offset); + service.findAndDisplayAppliedDataTypeAddresses(dt, fieldMatcher); + return; + } + } + + service.findAndDisplayAppliedDataTypeAddresses(dt, field); + } + + private Integer getOffsetForDeafaultFieldName(Structure structure, String fieldName) { + DataTypeComponent[] components = structure.getComponents(); + for (DataTypeComponent dtc : components) { + String defaultName = dtc.getDefaultFieldName(); + if (fieldName.equals(defaultName)) { + return dtc.getOffset(); + } + } + return null; + } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/FindDataTypesBySizeAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/FindDataTypesBySizeAction.java index 7291896355..699b2c99aa 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/FindDataTypesBySizeAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/FindDataTypesBySizeAction.java @@ -53,8 +53,8 @@ public class FindDataTypesBySizeAction extends DockingAction { @Override public void actionPerformed(ActionContext context) { - NumberRangeInputDialog inputDialog = - new NumberRangeInputDialog(getName(), "Size(s)"); + NumberRangeInputDialog inputDialog = new NumberRangeInputDialog(getName(), "Size(s)"); + inputDialog.setHelpLocation(getHelpLocation()); if (!inputDialog.show()) { return; } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/FindReferencesToFieldAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/FindReferencesToFieldAction.java index f8a6c0e097..e7e1e28e21 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/FindReferencesToFieldAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/FindReferencesToFieldAction.java @@ -32,6 +32,7 @@ import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin; import ghidra.app.plugin.core.datamgr.DataTypesActionContext; import ghidra.app.plugin.core.datamgr.tree.DataTypeNode; import ghidra.app.plugin.core.navigation.FindAppliedDataTypesService; +import ghidra.app.services.FieldMatcher; import ghidra.framework.plugintool.PluginTool; import ghidra.program.model.data.*; import ghidra.program.model.data.Enum; @@ -98,15 +99,27 @@ public class FindReferencesToFieldAction extends DockingAction { choices = ((Enum) dt).getNames(); } - String userChoice = OptionDialog.showInputChoiceDialog(null, "Choose Field", - "Find uses of '" + dt.getName() + "' field", choices, null, - OptionDialog.QUESTION_MESSAGE); + String message = "Find uses of '" + dt.getName() + "' field by name or offset"; + String userChoice = OptionDialog.showEditableInputChoiceDialog(null, "Choose Field", + message, choices, null, OptionDialog.QUESTION_MESSAGE); if (userChoice == null) { return; } - Swing.runLater( - () -> service.findAndDisplayAppliedDataTypeAddresses(dt, userChoice)); + FieldMatcher fieldMatcher; + Long longChoice = parseInt(userChoice); + if (longChoice != null) { + fieldMatcher = new FieldMatcher(dt, longChoice.intValue()); + } + else { + fieldMatcher = new FieldMatcher(dt, userChoice); + } + + Swing.runLater(() -> service.findAndDisplayAppliedDataTypeAddresses(dt, fieldMatcher)); + } + + private Long parseInt(String s) { + return NumericUtilities.parseNumber(s, null); } private String[] getCompisiteFieldNames(Composite composite) { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/navigation/FindAppliedDataTypesService.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/navigation/FindAppliedDataTypesService.java index 262dada51b..f307d35523 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/navigation/FindAppliedDataTypesService.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/navigation/FindAppliedDataTypesService.java @@ -15,6 +15,7 @@ */ package ghidra.app.plugin.core.navigation; +import ghidra.app.services.FieldMatcher; import ghidra.program.model.data.DataType; /** @@ -25,7 +26,7 @@ public interface FindAppliedDataTypesService { /** * Tells this service to find all places where the given datatype is applied and will * display the results of the search. - * + * * @param dataType The datatype which to base the search upon. */ public void findAndDisplayAppliedDataTypeAddresses(DataType dataType); @@ -33,9 +34,23 @@ public interface FindAppliedDataTypesService { /** * Tells this service to find all places where the given datatype is applied and will * display the results of the search. - * + * * @param dataType The datatype which to base the search upon. * @param fieldName the sub-field for which to search */ public void findAndDisplayAppliedDataTypeAddresses(DataType dataType, String fieldName); + + /** + * Tells this service to find all places where the given datatype is applied and will + * display the results of the search. + *

      + * The supplied field matcher will be used to restrict matches to the given field. The matcher + * may be 'empty', supplying only the data type for which to search. In this case, all uses + * of the type will be matched, regardless of field. + * + * @param dataType The datatype which to base the search upon. + * @param fieldMatcher the field matcher. + */ + public void findAndDisplayAppliedDataTypeAddresses(DataType dataType, + FieldMatcher fieldMatcher); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/navigation/locationreferences/DataTypeLocationDescriptor.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/navigation/locationreferences/DataTypeLocationDescriptor.java index e6afb4f894..c65319de7b 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/navigation/locationreferences/DataTypeLocationDescriptor.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/navigation/locationreferences/DataTypeLocationDescriptor.java @@ -35,8 +35,8 @@ import ghidra.util.exception.CancelledException; import ghidra.util.task.TaskMonitor; /** - * A location descriptor that should be extended by location descriptor implementations that - * are based upon data types. + * A location descriptor that should be extended by location descriptor implementations that are + * based upon data types. */ abstract class DataTypeLocationDescriptor extends LocationDescriptor { @@ -70,18 +70,28 @@ abstract class DataTypeLocationDescriptor extends LocationDescriptor { findDataTypeReferences(accumulator, monitor); } - /** The original data type that this location descriptor describes */ + /** + * The original data type that this location descriptor describes + * @return the type + */ protected abstract DataType getSourceDataType(); - /** Generates the label for the results window */ + /** + * Generates the label for the results window + * @return the label + */ protected abstract String generateLabel(); - /** Returns the name of the data type, for example, 'Foo' or 'Foo.bar.baz' */ + /** + * Returns the name of the data type, for example, 'Foo' or 'Foo.bar.baz' + * @return the name + */ protected abstract String getDataTypeName(); - /** + /** * The base data type that this location descriptor describes (this may be the same as the * original data type. + * @return the type. */ protected DataType getBaseDataType() { return getSourceDataType(); // by default these two values are the same @@ -91,7 +101,7 @@ abstract class DataTypeLocationDescriptor extends LocationDescriptor { TaskMonitor monitor) throws CancelledException { DataType currentDataType = getDataType(); - ReferenceUtils.findDataTypeReferences(accumulator, currentDataType, null, program, + ReferenceUtils.findDataTypeReferences(accumulator, currentDataType, program, useDynamicSearching, monitor); } @@ -177,8 +187,8 @@ abstract class DataTypeLocationDescriptor extends LocationDescriptor { List allVariables = ReferenceUtils.getVariables(function, true); for (Variable variable : allVariables) { DataType variableDataType = variable.getDataType(); - if (ReferenceUtils.getBaseDataType(variableDataType).isEquivalent( - currentDataType)) { + if (ReferenceUtils.getBaseDataType(variableDataType) + .isEquivalent(currentDataType)) { return true; } } @@ -266,8 +276,8 @@ abstract class DataTypeLocationDescriptor extends LocationDescriptor { } // check for pointer names else if (label.endsWith("*") && label.startsWith(paramName)) { - // see if we need to chop off some '*'s, as we may have searched for a pointer to a - // pointer and have found a match against a simple pointer and thus the display may + // see if we need to chop off some '*'s, as we may have searched for a pointer to a + // pointer and have found a match against a simple pointer and thus the display may // not match our label if (paramParts.length == 1) { return paramName; // not a full declaration, just the name diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/navigation/locationreferences/GenericCompositeDataTypeLocationDescriptor.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/navigation/locationreferences/GenericCompositeDataTypeLocationDescriptor.java index fd4943d99e..0d583d58b9 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/navigation/locationreferences/GenericCompositeDataTypeLocationDescriptor.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/navigation/locationreferences/GenericCompositeDataTypeLocationDescriptor.java @@ -20,6 +20,7 @@ import java.awt.Color; import org.apache.commons.lang3.StringUtils; import docking.widgets.fieldpanel.support.Highlight; +import ghidra.app.services.FieldMatcher; import ghidra.app.util.viewer.field.*; import ghidra.program.model.address.Address; import ghidra.program.model.listing.Data; @@ -34,34 +35,39 @@ import ghidra.util.task.TaskMonitor; */ public class GenericCompositeDataTypeLocationDescriptor extends GenericDataTypeLocationDescriptor { - private String typeAndFieldName; - private String fieldName; + // this is either "Foo.bar" or "Foo offset 1" + private String typeDisplayString; + private FieldMatcher fieldMatcher; public GenericCompositeDataTypeLocationDescriptor( GenericCompositeDataTypeProgramLocation location, Program program) { super(location, program, location.getDataType()); - this.fieldName = location.getFieldName(); - this.typeAndFieldName = getDataTypeName() + '.' + fieldName; + this.fieldMatcher = location.getFieldMatcher(); + this.typeDisplayString = fieldMatcher.getDisplayText(); label = generateLabel(); } @Override protected void doGetReferences(Accumulator accumulator, TaskMonitor monitor) throws CancelledException { - ReferenceUtils.findDataTypeReferences(accumulator, getDataType(), fieldName, program, + ReferenceUtils.findDataTypeFieldReferences(accumulator, fieldMatcher, program, useDynamicSearching, monitor); } @Override public String getTypeName() { - return super.getTypeName() + "." + fieldName; + // not sure why this code was doing this and not using our variable; oversight? + // return super.getTypeName() + "." + fieldName; + return typeDisplayString; } // implemented to ignore the location being provided, since this is a 'dummy' type class @Override protected String generateLabel() { - return "\"" + originalDataType.getName() + "." + fieldName + "\" (DataType)"; + // changed this; keep old code around in case an edge case appears + // return "\"" + originalDataType.getName() + "." + fieldName + "\" (DataType)"; + return "\"" + typeDisplayString + "\" (DataType)"; } // Overridden to perform a simple check against data types, since the program locations are @@ -79,7 +85,7 @@ public class GenericCompositeDataTypeLocationDescriptor extends GenericDataTypeL GenericCompositeDataTypeLocationDescriptor otherDescriptor = (GenericCompositeDataTypeLocationDescriptor) obj; return getDataType().equals(otherDescriptor.getDataType()) && - fieldName.equals(otherDescriptor.fieldName); + fieldMatcher.equals(otherDescriptor.fieldMatcher); } @Override @@ -113,14 +119,15 @@ public class GenericCompositeDataTypeLocationDescriptor extends GenericDataTypeL else if (OperandFieldFactory.class.isAssignableFrom(fieldFactoryClass)) { // Not sure how to get the correct part of the text. This is a hack for now. - int offset = StringUtils.indexOfIgnoreCase(text, typeAndFieldName, 0); + int offset = StringUtils.indexOfIgnoreCase(text, typeDisplayString, 0); if (offset != -1) { - return new Highlight[] { - new Highlight(offset, offset + typeAndFieldName.length() - 1, highlightColor) }; + return new Highlight[] { new Highlight(offset, + offset + typeDisplayString.length() - 1, highlightColor) }; } } else if (FieldNameFieldFactory.class.isAssignableFrom(fieldFactoryClass)) { + String fieldName = fieldMatcher.getFieldName(); if (text.equalsIgnoreCase(fieldName)) { return new Highlight[] { new Highlight(0, text.length(), highlightColor) }; } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/navigation/locationreferences/GenericCompositeDataTypeProgramLocation.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/navigation/locationreferences/GenericCompositeDataTypeProgramLocation.java index 28420886a6..fe5cda5bec 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/navigation/locationreferences/GenericCompositeDataTypeProgramLocation.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/navigation/locationreferences/GenericCompositeDataTypeProgramLocation.java @@ -17,28 +17,39 @@ package ghidra.app.plugin.core.navigation.locationreferences; import java.util.Objects; +import ghidra.app.services.FieldMatcher; import ghidra.program.model.data.Composite; import ghidra.program.model.data.DataType; import ghidra.program.model.listing.Program; +import ghidra.util.exception.AssertException; /** * A class to signal that the ProgramLocation is used for data types and is not really * connected to the listing. This is a subclass is designed for data types that have fields, such * as {@link Composite} types and {@link Enum} types. - * + * * @see GenericCompositeDataTypeLocationDescriptor */ public class GenericCompositeDataTypeProgramLocation extends GenericDataTypeProgramLocation { - private String fieldName; + private FieldMatcher fieldMatcher; GenericCompositeDataTypeProgramLocation(Program program, DataType dataType, String fieldName) { + this(program, dataType, new FieldMatcher(dataType, fieldName)); + } + + GenericCompositeDataTypeProgramLocation(Program program, DataType dataType, + FieldMatcher fieldMatcher) { super(program, dataType); - this.fieldName = Objects.requireNonNull(fieldName); + this.fieldMatcher = Objects.requireNonNull(fieldMatcher); + + // sanity check + if (!Objects.equals(dataType, fieldMatcher.getDataType())) { + throw new AssertException("Data type does not match the FieldMatcher type"); + } } - public String getFieldName() { - return fieldName; + public FieldMatcher getFieldMatcher() { + return fieldMatcher; } - } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/navigation/locationreferences/LocationDescriptor.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/navigation/locationreferences/LocationDescriptor.java index 7974ce2d29..3b804d632d 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/navigation/locationreferences/LocationDescriptor.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/navigation/locationreferences/LocationDescriptor.java @@ -260,10 +260,10 @@ public abstract class LocationDescriptor { } /** - * Returns a generic {@link ProgramLocation} based upon the program and - * homeAddress of this LocationDescriptor. Subclasses should override this + * Returns a generic {@link ProgramLocation} based upon the program and + * homeAddress of this LocationDescriptor. Subclasses should override this * method to return more specific addresses. - * + * * @return a generic ProgramLocation. */ ProgramLocation getHomeLocation() { @@ -329,9 +329,9 @@ public abstract class LocationDescriptor { TaskMonitor monitor) throws CancelledException; /** - * Returns a descriptive category name for this location descriptor. This is used for - * display in a popup menu. - * + * Returns a descriptive category name for this location descriptor. This is used for + * display in a popup menu. + * * @return a descriptive category name for this location descriptor */ public String getTypeName() { @@ -355,8 +355,8 @@ public abstract class LocationDescriptor { /** * Gets all location references for the given descriptor, loading them if not already loaded. - * - * @param accumulator the datastructure into which will be placed a collection of + * + * @param accumulator the datastructure into which will be placed a collection of * location references that reference the location this descriptor is representing. * @param monitor A monitor to report progress or cancel the gathering of addresses. * @param reload True signals to perform a new search for reference addresses; false will @@ -370,10 +370,10 @@ public abstract class LocationDescriptor { } /** - * When true, the search algorithm will use dynamic searching when possible, which is to - * not only find references that are already created, but to also use external tools to - * locate potential references. - * + * When true, the search algorithm will use dynamic searching when possible, which is to + * not only find references that are already created, but to also use external tools to + * locate potential references. + * * @param useDynamicSearching true to perform dynamic searching */ void setUseDynamicSearching(boolean useDynamicSearching) { @@ -384,7 +384,7 @@ public abstract class LocationDescriptor { * Sets a listener on this descriptor that will be notified when the references contained * in this descriptor may no longer be accurate. For example, the listener will be called * when an undo or redo is performed in Ghidra. - * @param modelChangeListener The listener to add. + * @param modelFreshnessListener The listener to add. */ void setModelFreshnessListener(ChangeListener modelFreshnessListener) { this.modelFreshnessListener = modelFreshnessListener; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/navigation/locationreferences/LocationReference.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/navigation/locationreferences/LocationReference.java index d714294b27..e9cef3a394 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/navigation/locationreferences/LocationReference.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/navigation/locationreferences/LocationReference.java @@ -99,7 +99,7 @@ public class LocationReference implements Comparable { * Returns the address where the item described by this object is used. For example, for * data types, the address is where a data type is applied; for references, this value is the * from address. - * + * * @return the address where the item described by this object is used. */ public Address getLocationOfUse() { @@ -110,7 +110,7 @@ public class LocationReference implements Comparable { * Returns the context associated with this location. This could be a String that highlights * what part of a function signature the location matches or a line from the Decompiler * that matches. - * + * * @return the context */ @@ -118,7 +118,7 @@ public class LocationReference implements Comparable { * Returns the context associated with this location. The context may be a simple plain string * or may be String that highlights part of a function signature the location matches or * a line from the Decompiler that matches. - * + * * @return the context */ public LocationReferenceContext getContext() { @@ -169,12 +169,7 @@ public class LocationReference implements Comparable { if (!context.equals(other.context)) { return false; } - if (locationOfUseAddress == null) { - if (other.locationOfUseAddress != null) { - return false; - } - } - else if (!locationOfUseAddress.equals(other.locationOfUseAddress)) { + if (!Objects.equals(locationOfUseAddress, other.locationOfUseAddress)) { return false; } return refType.equals(other.refType); @@ -194,7 +189,7 @@ public class LocationReference implements Comparable { "\taddress: " + locationOfUseAddress + ",\n" + ((refType.equals("")) ? "" : "\trefType: " + refType + ",\n") + "\tisOffcut: " + isOffcutReference + ",\n" + - ((context == EMPTY_CONTEXT) ? "" : "\tcontext: " + context + ",") + + ((context == EMPTY_CONTEXT) ? "" : "\tcontext: " + context + "\n") + "}"; //@formatter:off } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/navigation/locationreferences/LocationReferenceContext.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/navigation/locationreferences/LocationReferenceContext.java index d85dc2ee37..efc85f3b15 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/navigation/locationreferences/LocationReferenceContext.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/navigation/locationreferences/LocationReferenceContext.java @@ -22,7 +22,7 @@ import ghidra.util.HTMLUtilities; /** * A class to hold context representation for {@link LocationReference}s. - * + * * @see LocationReferenceContextBuilder */ public class LocationReferenceContext { @@ -107,7 +107,7 @@ public class LocationReferenceContext { /** * Returns any sub-strings of this context's overall text that match client-defined input - * + * * See the {@link LocationReferenceContextBuilder} for how to define matching text pieces * @return the matching strings */ @@ -123,7 +123,7 @@ public class LocationReferenceContext { @Override public String toString() { - return Json.toString(this); + return getPlainText(); } /** diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/navigation/locationreferences/LocationReferencesPlugin.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/navigation/locationreferences/LocationReferencesPlugin.java index 3bf6d5ed38..142d5d371f 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/navigation/locationreferences/LocationReferencesPlugin.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/navigation/locationreferences/LocationReferencesPlugin.java @@ -26,8 +26,7 @@ import ghidra.app.events.ProgramClosedPluginEvent; import ghidra.app.nav.Navigatable; import ghidra.app.plugin.PluginCategoryNames; import ghidra.app.plugin.core.navigation.FindAppliedDataTypesService; -import ghidra.app.services.GoToService; -import ghidra.app.services.ProgramManager; +import ghidra.app.services.*; import ghidra.app.util.XReferenceUtils; import ghidra.app.util.query.TableService; import ghidra.framework.options.ToolOptions; @@ -211,8 +210,7 @@ public class LocationReferencesPlugin extends Plugin } private void disposeProviderList() { - for (int i = 0; i < providerList.size(); i++) { - LocationReferencesProvider provider = providerList.get(i); + for (LocationReferencesProvider provider : providerList) { provider.dispose(); } providerList.clear(); @@ -261,8 +259,8 @@ public class LocationReferencesPlugin extends Plugin } protected void programClosed(Program program) { - for (Iterator iterator = - providerList.iterator(); iterator.hasNext();) { + for (Iterator iterator = providerList.iterator(); iterator + .hasNext();) { LocationReferencesProvider provider = iterator.next(); if (provider.getProgram() == program) { provider.dispose(); @@ -293,11 +291,19 @@ public class LocationReferencesPlugin extends Plugin @Override public void findAndDisplayAppliedDataTypeAddresses(DataType dataType) { - findAndDisplayAppliedDataTypeAddresses(dataType, null); + findAndDisplayAppliedDataTypeAddresses(dataType, (FieldMatcher) null); } @Override public void findAndDisplayAppliedDataTypeAddresses(DataType dataType, String fieldName) { + FieldMatcher matcher = new FieldMatcher(dataType, fieldName); + findAndDisplayAppliedDataTypeAddresses(dataType, matcher); + } + + @Override + public void findAndDisplayAppliedDataTypeAddresses(DataType dataType, + FieldMatcher fieldMatcher) { + ProgramManager programManagerService = tool.getService(ProgramManager.class); GoToService goToService = tool.getService(GoToService.class); Program program = programManagerService.getCurrentProgram(); @@ -308,9 +314,9 @@ public class LocationReferencesPlugin extends Plugin } ProgramLocation genericLocation; - if (fieldName != null) { + if (fieldMatcher != null) { genericLocation = - new GenericCompositeDataTypeProgramLocation(program, dataType, fieldName); + new GenericCompositeDataTypeProgramLocation(program, dataType, fieldMatcher); } else { genericLocation = new GenericDataTypeProgramLocation(program, dataType); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/navigation/locationreferences/ReferenceUtils.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/navigation/locationreferences/ReferenceUtils.java index 08e657958c..51dbb0a1c5 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/navigation/locationreferences/ReferenceUtils.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/navigation/locationreferences/ReferenceUtils.java @@ -20,11 +20,11 @@ import java.util.Stack; import java.util.function.Consumer; import java.util.function.Predicate; -import ghidra.app.services.DataTypeReference; -import ghidra.app.services.DataTypeReferenceFinder; +import ghidra.app.services.*; import ghidra.program.model.address.*; import ghidra.program.model.data.*; import ghidra.program.model.data.Array; +import ghidra.program.model.data.Enum; import ghidra.program.model.listing.*; import ghidra.program.model.symbol.*; import ghidra.program.util.*; @@ -162,20 +162,24 @@ public final class ReferenceUtils { /** * Returns all references (locations) that use the given datatype. - *
      - * Note: This method call may take a long time, as it must search all of the - * data within the program and may also perform long running tasks, like decompiling every - * function in the program. - *
      - * @param accumulator the results storage + *

      + * Note: This method call may take a long time, as it must search all of the data + * within the program and may also perform long running tasks, like decompiling every function + * in the program. + * + * @param accumulator the results storage. * @param dataType The datatype for which to find references. - * @param fieldName optional field name for which to search; the dataType must be - * a {@link Composite} to search for a field + * @param fieldName optional field name for which to search; the dataType must be a + * {@link Composite} to search for a field. * @param program The program from within which to find references. * @param monitor A task monitor to be updated as data is searched; if this is null, then a - * dummy monitor will be used. - * @throws CancelledException if the monitor is cancelled + * dummy monitor will be used. + * @throws CancelledException if the monitor is cancelled. + * @deprecated use {@link #findDataTypeFieldReferences(Accumulator, FieldMatcher, Program, + * boolean, TaskMonitor)}. + * @Deprecated(since = "10.2") */ + @Deprecated public static void findDataTypeReferences(Accumulator accumulator, DataType dataType, String fieldName, Program program, TaskMonitor monitor) throws CancelledException { @@ -184,24 +188,26 @@ public final class ReferenceUtils { /** * Returns all references (locations) that use the given datatype. - *
      - * Note: This method call may take a long time, as it must search all of the - * data within the program and may also perform long running tasks, like decompiling every - * function in the program. - *
      - * @param accumulator the results storage + *

      + * Note: This method call may take a long time, as it must search all of the data + * within the program and may also perform long running tasks, like decompiling every function + * in the program. + * + * @param accumulator the results storage. * @param dataType The datatype for which to find references. - * @param fieldName optional field name for which to search; the dataType must be - * a {@link Composite} to search for a field + * @param fieldName optional field name for which to search; the dataType must be a + * {@link Composite} to search for a field. * @param program The program from within which to find references. - * @param discoverTypes if true, the {@link DataTypeReferenceFinder} service - * will be used to search for data types that are not applied in memory. - * Using the service will be slower, but will recover type usage that could - * not be found by examining the Listing. + * @param discoverTypes if true, the {@link DataTypeReferenceFinder} service will be used to + * search for data types that are not applied in memory. Using the service will be slower, but + * will recover type usage that could not be found by examining the Listing. * @param monitor A task monitor to be updated as data is searched; if this is null, then a - * dummy monitor will be used. - * @throws CancelledException if the monitor is cancelled + * dummy monitor will be used. + * @throws CancelledException if the monitor is cancelled. + * @deprecated use {@link #findDataTypeFieldReferences(Accumulator, FieldMatcher, Program, + * boolean, TaskMonitor)}. */ + @Deprecated(since = "10.2") public static void findDataTypeReferences(Accumulator accumulator, DataType dataType, String fieldName, Program program, boolean discoverTypes, TaskMonitor monitor) throws CancelledException { @@ -209,12 +215,72 @@ public final class ReferenceUtils { // Note: none of the params can be null, but this one gets used much later, so check now Objects.requireNonNull(dataType, () -> "Data Type cannot be null"); - if (monitor == null) { - monitor = TaskMonitor.DUMMY; - } + FieldMatcher fieldMatcher = new FieldMatcher(dataType, fieldName); + doFindDataTypeReferences(accumulator, fieldMatcher, program, discoverTypes, monitor); + } + /** + * Returns all references (locations) that use the given datatype. + *

      + * Note: This method call may take a long time, as it must search all of the data + * within the program and may also perform long running tasks, like decompiling every function + * in the program. + * + * @param accumulator the results storage. + * @param dataType The datatype for which to find references. + * @param program The program from within which to find references. + * @param discoverTypes if true, the {@link DataTypeReferenceFinder} service will be used to + * search for data types that are not applied in memory. Using the service will be slower, but + * will recover type usage that could not be found by examining the Listing. + * @param monitor A task monitor to be updated as data is searched; if this is null, then a + * dummy monitor will be used. + * @throws CancelledException if the monitor is cancelled. + */ + public static void findDataTypeReferences(Accumulator accumulator, + DataType dataType, Program program, boolean discoverTypes, TaskMonitor monitor) + throws CancelledException { + + doFindDataTypeReferences(accumulator, new FieldMatcher(dataType), program, discoverTypes, + monitor); + } + + /** + * Returns all references (locations) that use the given datatype. + *

      + * Note: This method call may take a long time, as it must search all of the data + * within the program and may also perform long running tasks, like decompiling every function + * in the program. + *

      + * The supplied field matcher will be used to restrict matches to the given field. The matcher + * may be 'empty', supplying only the data type for which to search. In this case, all uses + * of the type will be matched, regardless of field. + * + * @param accumulator the results storage. + * @param fieldMatcher the field matcher. + * @param program The program from within which to find references. + * @param discoverTypes if true, the {@link DataTypeReferenceFinder} service will be used to + * search for data types that are not applied in memory. Using the service will be slower, but + * will recover type usage that could not be found by examining the Listing. + * @param monitor A task monitor to be updated as data is searched; if this is null, then a + * dummy monitor will be used. + * @throws CancelledException if the monitor is cancelled. + */ + public static void findDataTypeFieldReferences(Accumulator accumulator, + FieldMatcher fieldMatcher, Program program, boolean discoverTypes, TaskMonitor monitor) + throws CancelledException { + + Objects.requireNonNull(fieldMatcher, "FieldMatcher cannot be null"); + doFindDataTypeReferences(accumulator, fieldMatcher, program, discoverTypes, monitor); + } + + private static void doFindDataTypeReferences(Accumulator accumulator, + FieldMatcher fieldMatcher, Program program, boolean discoverTypes, TaskMonitor monitor) + throws CancelledException { + + monitor = TaskMonitor.dummyIfNull(monitor); + + DataType dataType = fieldMatcher.getDataType(); Listing listing = program.getListing(); - long dataCount = listing.getNumDefinedData(); int functionCount = program.getFunctionManager().getFunctionCount(); int totalCount = (int) dataCount + functionCount; @@ -226,9 +292,10 @@ public final class ReferenceUtils { // the code. Accumulator asSet = asSet(accumulator); - if (fieldName == null) { - // It only makes sense to search here when we do not have a field - + if (fieldMatcher.isIgnored()) { + // + // It only makes sense to search here when we do not have a field to match + // boolean localsOnly = discoverTypes; FunctionIterator iterator = listing.getFunctions(false); findDataTypeMatchesInFunctionHeaders(asSet, iterator, dataType, localsOnly, monitor); @@ -244,10 +311,11 @@ public final class ReferenceUtils { boolean matches = dataTypesMatch(dataType, baseType); return matches; }; - findDataTypeMatchesInDefinedData(asSet, program, dataMatcher, fieldName, monitor); + + findDataTypeMatchesInDefinedData(asSet, program, dataMatcher, fieldMatcher, monitor); if (discoverTypes) { - findDataTypeMatchesOutsideOfListing(asSet, program, dataType, fieldName, monitor); + findDataTypeMatchesOutsideOfListing(asSet, program, dataType, fieldMatcher, monitor); } monitor.checkCanceled(); @@ -265,7 +333,7 @@ public final class ReferenceUtils { private static void findDataTypeMatchesOutsideOfListing( Accumulator accumulator, Program program, DataType dataType, - String fieldName, TaskMonitor monitor) throws CancelledException { + FieldMatcher fieldMatcher, TaskMonitor monitor) throws CancelledException { List finders = ClassSearcher.getInstances(DataTypeReferenceFinder.class); @@ -284,12 +352,7 @@ public final class ReferenceUtils { } for (DataTypeReferenceFinder finder : finders) { - if (fieldName == null) { - finder.findReferences(program, dataType, callback, monitor); - } - else { - finder.findReferences(program, dataType, fieldName, callback, monitor); - } + finder.findReferences(program, fieldMatcher, callback, monitor); } } @@ -617,15 +680,15 @@ public final class ReferenceUtils { * some digging to see what is buried at the given address. */ private static LocationDescriptor createDataMemberLocationDescriptor( - OperandFieldLocation location, Address refAddress) { + OperandFieldLocation location, Address address) { - // TODO we don't support data types on external addresses; this could change in the future - if (refAddress.isExternalAddress()) { + // Note: we don't support data types on external addresses; this could change in the future + if (address.isExternalAddress()) { return null; } Program program = location.getProgram(); - Data outermostData = getDataContainingAddress(program, refAddress); + Data outermostData = getDataContainingAddress(program, address); if (outermostData == null) { // no data return null; @@ -634,12 +697,12 @@ public final class ReferenceUtils { String fieldPath = getFieldPath(location); if (!fieldPath.contains(".")) { - // no field reference, so don't create a structure member reference, but just - // a generic data type reference + // no field reference, so don't create a structure member reference, but just a generic + // data type reference DataType type = outermostData.getDataType(); if (type == DataType.DEFAULT || Undefined.isUndefined(type)) { - // nobody wants to search for undefined usage; too many (this is the case - // where the user is not on an actual data type) + // nobody wants to search for undefined usage; too many (this is the case where the + // user is not on an actual data type) return null; } @@ -650,15 +713,15 @@ public final class ReferenceUtils { String fieldName = getFieldName(location); Address parentAddress = outermostData.getMinAddress(); - int componentAddress = (int) refAddress.subtract(parentAddress); + int componentAddress = (int) address.subtract(parentAddress); Data subData = outermostData.getComponentContaining(componentAddress); if (subData != null) { int[] componentPath = subData.getComponentPath(); FieldNameFieldLocation fieldLocation = - new FieldNameFieldLocation(program, refAddress, componentPath, fieldName, 0); + new FieldNameFieldLocation(program, address, componentPath, fieldName, 0); LocationDescriptor descriptor = - createSubDataMemberLocationDescriptor(program, refAddress, fieldLocation, subData); + createSubDataMemberLocationDescriptor(program, address, fieldLocation, subData); return descriptor; } @@ -667,18 +730,41 @@ public final class ReferenceUtils { // DataType dt = outermostData.getDataType(); if (dt instanceof Union) { - AddressFieldLocation addressLocation = new AddressFieldLocation(program, refAddress, - new int[] { 0 }, refAddress.toString(), 0); + AddressFieldLocation addressLocation = + new AddressFieldLocation(program, address, new int[] { 0 }, address.toString(), 0); return new UnionLocationDescriptor(addressLocation, program); } return null; } + private static GenericDataTypeLocationDescriptor createGenericDataTypeLocationDescriptor( + OperandFieldLocation location) { + + Data data = getDataAt(location); + if (data == null) { + return null; + } + + DataType dt = data.getDataType(); + if (dt instanceof Enum) { + + String enumText = location.getOperandRepresentation(); + GenericCompositeDataTypeProgramLocation genericLocation = + new GenericCompositeDataTypeProgramLocation(location.getProgram(), dt, enumText); + return new GenericCompositeDataTypeLocationDescriptor(genericLocation, + location.getProgram()); + } + + GenericDataTypeProgramLocation genericLocation = + new GenericDataTypeProgramLocation(location.getProgram(), dt); + return new GenericDataTypeLocationDescriptor(genericLocation, location.getProgram(), dt); + } + /* * Creates a location descriptor using the String display markup and type information * found inside of the VariableOffset object. - * + * * This method differs from createDataMemberLocationDescriptor() in that this method * will create locations that represent DataTypes that are not applied in memory. */ @@ -813,9 +899,15 @@ public final class ReferenceUtils { private static LocationDescriptor createOperandLocationDescriptor( OperandFieldLocation location) { + // this is the 'to' address Address refAddress = getReferenceAddress(location); if (refAddress == null) { - return null; // no reference and no variable-offset + // + // No reference address for this location. Try to create a generic data descriptor. + // + GenericDataTypeLocationDescriptor descriptor = + createGenericDataTypeLocationDescriptor(location); + return descriptor; } Address operandAddress = location.getAddress(); @@ -824,20 +916,18 @@ public final class ReferenceUtils { ReferenceManager referenceManager = program.getReferenceManager(); Reference reference = referenceManager.getReference(operandAddress, refAddress, operandIndex); - if (reference == null) { - // Prefer using the reference, for consistency. Without that, the - // VariableOffset object contains markup and type information we can use. - // Having a VariableOffset without a reference occurs when a - // register variable reference is inferred during instruction operand formatting. + // Prefer using the reference, for consistency. Without that, the VariableOffset + // object contains markup and type information we can use. Having a VariableOffset + // without a reference occurs when a register variable reference is inferred during + // instruction operand formatting. VariableOffset variableOffset = location.getVariableOffset(); return createGenericDataTypeLocationDescriptor(program, variableOffset); } - // note: not sure why we are ignoring external references. It seems like that is - // a thing you may want to find refs to. If you figure it out, update this - // comment. + // note: not sure why we are ignoring external references. It seems like that is a thing + // you may want to find refs to. If you figure it out, update this comment. // if (reference.isExternalReference()) { // return null; // } @@ -930,17 +1020,17 @@ public final class ReferenceUtils { /** * Searches defined data for types that match, according to the given predicate. - * + * * @param accumulator the results accumulator * @param program the program * @param dataMatcher the predicate that determines a successful match - * @param fieldName the optional field name for which to search + * @param fieldMatcher the field matcher; will be ignored if it contains null values * @param monitor the task monitor used to track progress and cancel the work * @throws CancelledException if the operation was cancelled */ public static void findDataTypeMatchesInDefinedData(Accumulator accumulator, - Program program, Predicate dataMatcher, String fieldName, TaskMonitor monitor) - throws CancelledException { + Program program, Predicate dataMatcher, FieldMatcher fieldMatcher, + TaskMonitor monitor) throws CancelledException { Listing listing = program.getListing(); DataIterator dataIter = listing.getDefinedData(true); @@ -948,39 +1038,43 @@ public final class ReferenceUtils { monitor.checkCanceled(); Data data = dataIter.next(); - getMatchingDataTypesReferencesFromDataAndSubData(accumulator, data, fieldName, + getMatchingDataTypesReferencesFromDataAndSubData(accumulator, data, fieldMatcher, dataMatcher, monitor); monitor.incrementProgress(1); } } - private static LocationReference createReferenceFromDefinedData(Data data, String fieldName) { + private static LocationReference createReferenceFromDefinedData(Data data, + FieldMatcher fieldMatcher) { Address dataAddress = data.getMinAddress(); - if (fieldName == null) { - // no field--include the hit + if (fieldMatcher.isIgnored()) { + // no field to match; include the hit return new LocationReference(dataAddress, data.getPathName()); } - DataTypeComponent component = getDataTypeComponent(data, fieldName); - if (component == null) { - // this implies the given data does not contain our field--do not include the hit - return null; - } - - // Note: just check the current type; we may have to unroll it, looking for pointers - // along the way if this is not sufficient DataType dt = data.getDataType(); if (dt instanceof Pointer) { // For defined data, do not include pointer types when we have a field name. A - // pointer to the base composite type is not a direct usage of the given field. + // pointer to the base composite type is not a direct usage of the given field.find return null; } + DataType baseDt = getBaseDataType(data.getDataType()); + if (matchesEnumField(data, baseDt, fieldMatcher)) { + return new LocationReference(dataAddress, fieldMatcher.getDisplayText()); + } + + DataTypeComponent component = getDataTypeComponent(baseDt, fieldMatcher); + if (component == null) { + return null; // not in composite + } + Address componentAddress; try { componentAddress = dataAddress.addNoWrap(component.getOffset()); - return new LocationReference(componentAddress, data.getPathName() + "." + fieldName); + return new LocationReference(componentAddress, + data.getPathName() + "." + fieldMatcher.getFieldName()); } catch (AddressOverflowException e) { // shouldn't happen @@ -990,33 +1084,58 @@ public final class ReferenceUtils { return null; } - private static DataTypeComponent getDataTypeComponent(Data data, String fieldName) { - DataType dt = getBaseDataType(data.getDataType()); - if (!(dt instanceof Composite)) { - Msg.debug(ReferenceUtils.class, - "Somehow searched for a field name on a Data Type that is not a Composite"); - return null; + private static boolean matchesEnumField(Data data, DataType dt, FieldMatcher matcher) { + if (!(dt instanceof Enum)) { + return false; } - Composite c = (Composite) dt; - DataTypeComponent[] components = c.getDefinedComponents(); - for (DataTypeComponent component : components) { - if (SystemUtilities.isEqual(component.getFieldName(), fieldName)) { - return component; + Enum enumm = (Enum) dt; + List names = getEnumNames(data, enumm); + for (String name : names) { + long value = enumm.getValue(name); + if (matcher.matches(name, (int) value)) { + return true; } } + return false; + } - // Note: sometimes this happens if the user searches on an array element field, which - // exists only in the Listing markup + private static List getEnumNames(Data data, Enum enumm) { + String enumEntryName = data.getDefaultValueRepresentation(); + List enumNames = new ArrayList<>(List.of(enumm.getNames())); + if (enumNames.contains(enumEntryName)) { + return List.of(enumEntryName); + } + + if (!enumEntryName.contains(" | ")) { + return Collections.emptyList(); + } + + // not a big fan of using this delimiter check, but will do so for now until there is + // a better way, such as asking the enum to do this work for us + String[] names = enumEntryName.split(" \\| "); + return List.of(names); + } + + private static DataTypeComponent getDataTypeComponent(DataType dt, FieldMatcher matcher) { + if (dt instanceof Composite) { + Composite c = (Composite) dt; + DataTypeComponent[] components = c.getDefinedComponents(); + for (DataTypeComponent component : components) { + if (matcher.matches(component.getFieldName(), component.getOffset())) { + return component; + } + } + } return null; } private static void getMatchingDataTypesReferencesFromDataAndSubData( - Accumulator accumulator, Data data, String fieldName, + Accumulator accumulator, Data data, FieldMatcher fieldMatcher, Predicate dataMatcher, TaskMonitor monitor) throws CancelledException { if (dataMatcher.test(data)) { - getMatchingDataTypesReferencesFromData(accumulator, data, fieldName, monitor); + getMatchingDataTypesReferencesFromData(accumulator, data, fieldMatcher, monitor); } // We know that arrays are all the same element; we decided to just mark the beginning. @@ -1031,16 +1150,16 @@ public final class ReferenceUtils { monitor.checkCanceled(); Data subData = data.getComponent(i); - getMatchingDataTypesReferencesFromDataAndSubData(accumulator, subData, fieldName, + getMatchingDataTypesReferencesFromDataAndSubData(accumulator, subData, fieldMatcher, dataMatcher, monitor); } } private static void getMatchingDataTypesReferencesFromData( - Accumulator accumulator, Data data, String fieldName, + Accumulator accumulator, Data data, FieldMatcher fieldMatcher, TaskMonitor monitor) throws CancelledException { - LocationReference ref = createReferenceFromDefinedData(data, fieldName); + LocationReference ref = createReferenceFromDefinedData(data, fieldMatcher); if (ref == null) { return; } @@ -1058,12 +1177,13 @@ public final class ReferenceUtils { Consumer referenceConsumer = reference -> { Address toAddress = reference.getToAddress(); - if (fieldName == null) { + if (fieldMatcher.isIgnored()) { + // no field to match; use the data address accumulator.add(new LocationReference(reference, isOffcut(program, toAddress))); return; } - // only add the reference if it is directly to the field + // have a field match; only add the reference if it is directly to the field if (toAddress.equals(dataAddress)) { accumulator.add(new LocationReference(reference, false)); } @@ -1113,7 +1233,6 @@ public final class ReferenceUtils { } private static boolean dataTypesMatch(DataType searchType, DataType possibleType) { - if (isBuiltIn(searchType)) { Class clazz = searchType.getClass(); return clazz.equals(possibleType.getClass()); @@ -1132,8 +1251,8 @@ public final class ReferenceUtils { return false; } - if (DataTypeManager.BUILT_IN_ARCHIVE_UNIVERSAL_ID.equals( - sourceArchive.getSourceArchiveID())) { + if (DataTypeManager.BUILT_IN_ARCHIVE_UNIVERSAL_ID + .equals(sourceArchive.getSourceArchiveID())) { return true; } return false; @@ -1141,7 +1260,7 @@ public final class ReferenceUtils { /** * Returns all references to the given variable - * + * * @param accumulator the results accumulator * @param program the program * @param variable the variable @@ -1153,8 +1272,8 @@ public final class ReferenceUtils { ReferenceManager referenceManager = program.getReferenceManager(); Reference[] variableRefsTo = referenceManager.getReferencesTo(variable); for (Reference ref : variableRefsTo) { - accumulator.add( - new LocationReference(ref, !ref.getToAddress().equals(variableAddress))); + accumulator + .add(new LocationReference(ref, !ref.getToAddress().equals(variableAddress))); } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/navigation/locationreferences/UnionLocationDescriptor.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/navigation/locationreferences/UnionLocationDescriptor.java index 21edb482de..a975fa9798 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/navigation/locationreferences/UnionLocationDescriptor.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/navigation/locationreferences/UnionLocationDescriptor.java @@ -40,12 +40,10 @@ class UnionLocationDescriptor extends DataTypeLocationDescriptor { protected void doGetReferences(Accumulator accumulator, TaskMonitor monitor) throws CancelledException { - // Note: we pass null for the 'fieldName', as we have no way of disambiguating which + // Note: we don't use 'fieldName' of the union, as we have no way of disambiguating which // field a reference will point to. So, grab all references. - - String fieldName = null; - ReferenceUtils.findDataTypeReferences(accumulator, union, fieldName, program, - useDynamicSearching, monitor); + ReferenceUtils.findDataTypeReferences(accumulator, union, program, useDynamicSearching, + monitor); } @Override diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/services/DataTypeReferenceFinder.java b/Ghidra/Features/Base/src/main/java/ghidra/app/services/DataTypeReferenceFinder.java index d03b54ffaf..1b02584827 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/services/DataTypeReferenceFinder.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/services/DataTypeReferenceFinder.java @@ -38,7 +38,7 @@ public interface DataTypeReferenceFinder extends ExtensionPoint { *

      * Note that this operation is multi-threaded and that results will be delivered as they * are found via the callback. - * + * * @param program the program to search * @param dataType the type for which to search * @param callback the callback to be called when a reference is found @@ -46,8 +46,7 @@ public interface DataTypeReferenceFinder extends ExtensionPoint { * @throws CancelledException if the operation was cancelled */ public void findReferences(Program program, DataType dataType, - Consumer callback, - TaskMonitor monitor) throws CancelledException; + Consumer callback, TaskMonitor monitor) throws CancelledException; /** * Finds references in the current program to specific field of the given {@link Composite} type @@ -55,14 +54,34 @@ public interface DataTypeReferenceFinder extends ExtensionPoint { *

      * Note that this operation is multi-threaded and that results will be delivered as they * are found via the callback. - * + * * @param program the program to search * @param dataType the type containing the field for which to search - * @param fieldName the name of the composite's field for which to search + * @param fieldName the name of the composite's field for which to search; may be null * @param callback the callback to be called when a reference is found * @param monitor the monitor that allows for progress and cancellation * @throws CancelledException if the operation was cancelled */ public void findReferences(Program program, DataType dataType, String fieldName, Consumer callback, TaskMonitor monitor) throws CancelledException; + + /** + * Finds references in the current program to specific field of the given {@link Composite} type + * in a manner appropriate with the given implementation. + *

      + * The supplied field matcher will be used to restrict matches to the given field. The matcher + * may be 'empty', supplying only the data type for which to search. In this case, all uses + * of the type will be matched, regardless of field. + *

      + * Note that this operation is multi-threaded and that results will be delivered as they + * are found via the callback. + * + * @param program the program to search + * @param fieldMatcher the field matcher to use for matching types + * @param callback the callback to be called when a reference is found + * @param monitor the monitor that allows for progress and cancellation + * @throws CancelledException if the operation was cancelled + */ + public void findReferences(Program program, FieldMatcher fieldMatcher, + Consumer callback, TaskMonitor monitor) throws CancelledException; } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/services/FieldMatcher.java b/Ghidra/Features/Base/src/main/java/ghidra/app/services/FieldMatcher.java new file mode 100644 index 0000000000..6e041c0157 --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/services/FieldMatcher.java @@ -0,0 +1,185 @@ +/* ### + * 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.services; + +import java.util.Objects; + +import ghidra.program.model.data.*; +import ghidra.util.datastruct.SortedRangeList; + +/** + * This class allows clients to match on multiple field attributes, such as name and offset + * within a parent data type. + *

      + * Use {@link #FieldMatcher(DataType)} as an 'empty' or 'ignored' field matcher to signal that any + * field match is considered value. + */ +public class FieldMatcher { + + private String fieldName; + private SortedRangeList fieldOffsets = new SortedRangeList(); + private DataType dataType; + + /** + * Creates an 'empty' matcher that can be used to signal no specific field or offset match + * is required. + * @param dataType the non-null data type. + */ + public FieldMatcher(DataType dataType) { + this.dataType = Objects.requireNonNull(dataType); + } + + public FieldMatcher(DataType dataType, String fieldName) { + this.dataType = Objects.requireNonNull(dataType); + this.fieldName = fieldName; + } + + public FieldMatcher(DataType dataType, int offset) { + this.dataType = Objects.requireNonNull(dataType); + fieldOffsets.addRange(offset, offset); + } + + /** + * Signals that no specific field match is required. + * @return true if no field or offset has been specified. + */ + public boolean isIgnored() { + return fieldName == null && fieldOffsets.isEmpty(); + } + + public boolean matches(String dtFieldName, int dtOffset) { + + if (isIgnored()) { + return true; // an empty matcher signals to match all fields + } + + if (fieldName != null) { + if (Objects.equals(fieldName, dtFieldName)) { + return true; + } + } + + if (fieldOffsets.contains(dtOffset)) { + return true; + } + + return false; + } + + /** + * Returns a display text for this field matcher, for example, {@code Foo.bar}. + * @return the display text + */ + public String getDisplayText() { + if (fieldName != null) { + return dataType.getName() + '.' + fieldName; + } + if (!fieldOffsets.isEmpty()) { + String compositeFieldName = generateCompositeFieldNameByOffset(); + if (compositeFieldName != null) { + return compositeFieldName; + } + return dataType.getName() + " at " + fieldOffsets.toString(); + } + return dataType.getName(); + } + + private String generateCompositeFieldNameByOffset() { + + long n = fieldOffsets.getNumValues(); + if (n != 1) { + return null; + } + + int offset = fieldOffsets.getMin(); + if (dataType instanceof Structure) { + Structure structure = (Structure) dataType; + DataTypeComponent dtc = structure.getComponentContaining(offset); + if (dtc != null) { + String name = dtc.getFieldName(); + if (name != null) { + return name; + } + return dtc.getDefaultFieldName(); + } + } + else if (dataType instanceof Composite) { + Composite composite = (Composite) dataType; + + DataTypeComponent[] components = composite.getComponents(); + for (DataTypeComponent dtc : components) { + int dtcOffset = dtc.getOffset(); + if (dtcOffset == offset) { + return dtc.getFieldName(); + } + } + } + + return null; + } + + public DataType getDataType() { + return dataType; + } + + /** + * Returns the field name given to this matcher or will attempt to generate a default field + * name using the given data type and offset. + * @return the field name or null + */ + public String getFieldName() { + if (fieldName != null) { + return fieldName; + } + return generateCompositeFieldNameByOffset(); + } + + @Override + public String toString() { + return getDisplayText(); + } + + @Override + public int hashCode() { + return Objects.hash(dataType, fieldName, fieldOffsets); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + + FieldMatcher other = (FieldMatcher) obj; + if (!Objects.equals(dataType, other.dataType)) { + return false; + } + if (!Objects.equals(fieldName, other.fieldName)) { + return false; + } + if (!Objects.equals(fieldOffsets, other.fieldOffsets)) { + return false; + } + return true; + } + +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/FieldFactory.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/FieldFactory.java index c8aa5a0be4..e43eb264db 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/FieldFactory.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/FieldFactory.java @@ -79,8 +79,8 @@ public abstract class FieldFactory implements ExtensionPoint { this.displayOptions = displayOptions; initDisplayOptions(); - fieldOptions.getOptions(name).setOptionsHelpLocation( - new HelpLocation("CodeBrowserPlugin", name)); + fieldOptions.getOptions(name) + .setOptionsHelpLocation(new HelpLocation("CodeBrowserPlugin", name)); } protected void initDisplayOptions() { @@ -141,6 +141,7 @@ public abstract class FieldFactory implements ExtensionPoint { * @param highlightProvider the HightLightProvider. * @param options the Options for display properties. * @param fieldOptions the Options for field specific properties. + * @return the factory */ public abstract FieldFactory newInstance(FieldFormatModel formatModel, HighlightProvider highlightProvider, ToolOptions options, ToolOptions fieldOptions); @@ -185,6 +186,7 @@ public abstract class FieldFactory implements ExtensionPoint { /** * Returns the Field name. + * @return the name. */ public String getFieldName() { return name; @@ -192,14 +194,15 @@ public abstract class FieldFactory implements ExtensionPoint { /** * Returns the default field color. + * @return the color. */ public Color getDefaultColor() { return Color.BLACK; } /** - * Returns the starting x position for the fields generated by this - * factory. + * Returns the starting x position for the fields generated by this factory. + * @return the start x. */ public int getStartX() { return startX; @@ -207,6 +210,7 @@ public abstract class FieldFactory implements ExtensionPoint { /** * Sets the starting x position for the fields generated by this factory. + * @param x the x position. */ public void setStartX(int x) { startX = x; @@ -214,6 +218,7 @@ public abstract class FieldFactory implements ExtensionPoint { /** * Returns the width of the fields generated by this factory. + * @return the width. */ public int getWidth() { return width; @@ -221,6 +226,7 @@ public abstract class FieldFactory implements ExtensionPoint { /** * Sets the width of the fields generated by this factory. + * @param w the width. */ public void setWidth(int w) { width = w; @@ -228,6 +234,7 @@ public abstract class FieldFactory implements ExtensionPoint { /** * Returns the FieldModel that this factory belongs to. + * @return the model. */ public FieldFormatModel getFieldModel() { return model; @@ -235,6 +242,7 @@ public abstract class FieldFactory implements ExtensionPoint { /** * Returns true if this FieldFactory is currently enabled to generate Fields. + * @return true if enabled. */ public boolean isEnabled() { return enabled; @@ -261,12 +269,14 @@ public abstract class FieldFactory implements ExtensionPoint { /** * Return a FieldLocation that corresponds to the given index, fieldNum, and ProgramLocation * IF and ONLY IF the given programLocation is the type generated by this class's - * {@link #getFieldLocation(ListingField, BigInteger, int, ProgramLocation)}. Each FieldFactory - * should generate and process a unique ProgramLocation class. + * {@link #getFieldLocation(ListingField, BigInteger, int, ProgramLocation)}. Each + * FieldFactory should generate and process a unique ProgramLocation class. + * * @param bf the ListingField at the current cursor. * @param index the line index (corresponds to an address) * @param fieldNum the index of field within the layout to try and get a FieldLocation. * @param loc the ProgramLocation to be converted into a FieldLocation. + * @return the location. */ public abstract FieldLocation getFieldLocation(ListingField bf, BigInteger index, int fieldNum, ProgramLocation loc); @@ -276,6 +286,7 @@ public abstract class FieldFactory implements ExtensionPoint { * @param row the row within this field * @param col the col on the given row within this field. * @param bf the ListingField containing the cursor. + * @return the location. */ public abstract ProgramLocation getProgramLocation(int row, int col, ListingField bf); @@ -313,6 +324,7 @@ public abstract class FieldFactory implements ExtensionPoint { /** * Returns a description of the fields generated by this factory. + * @return the text. */ public String getFieldText() { return name; @@ -320,14 +332,12 @@ public abstract class FieldFactory implements ExtensionPoint { /** * Returns the font metrics used by this field factory + * @return the metrics. */ public FontMetrics getMetrics() { return getMetrics(style); } - /** - * @return Returns the metrics. - */ protected FontMetrics getMetrics(int fontStyle) { if (fontStyle == -1) { return defaultMetrics; @@ -335,8 +345,7 @@ public abstract class FieldFactory implements ExtensionPoint { return fontMetrics[fontStyle]; } - @SuppressWarnings("deprecation") - // we know + @SuppressWarnings("deprecation") // we know private void setMetrics(Font newFont) { defaultMetrics = Toolkit.getDefaultToolkit().getFontMetrics(newFont); for (int i = 0; i < fontMetrics.length; i++) { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/OperandFieldHelper.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/OperandFieldHelper.java index 5451ac54ce..1c1c215955 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/OperandFieldHelper.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/OperandFieldHelper.java @@ -57,7 +57,7 @@ abstract class OperandFieldHelper extends FieldFactory { private final static String SPACE_AFTER_SEPARATOR_OPTION = GhidraOptions.OPERAND_GROUP_TITLE + Options.DELIMITER + "Add Space After Separator"; - public static enum UNDERLINE_CHOICE { + public enum UNDERLINE_CHOICE { Hidden, All, None } @@ -183,12 +183,6 @@ abstract class OperandFieldHelper extends FieldFactory { return null; } - /** - * Returns the FactoryField for the given object at index index. - * - * @param obj the object whose properties should be displayed. - * @param varWidth the amount of variable width spacing for any fields before this one. - */ ListingField getField(Object obj, ProxyObj proxy, int varWidth) { if (!enabled) { return null; @@ -222,10 +216,10 @@ abstract class OperandFieldHelper extends FieldFactory { ListingTextField btf = (ListingTextField) lf; FieldElement fieldElement = btf.getFieldElement(row, col); - if (!(fieldElement instanceof OperandFieldElement)) { return null; } + OperandFieldElement element = (OperandFieldElement) fieldElement; int opIndex = element.getOperandIndex(); int subOpIndex = element.getOperandSubIndex(); @@ -276,11 +270,10 @@ abstract class OperandFieldHelper extends FieldFactory { else if (obj instanceof Data) { Data data = (Data) obj; Address refAddr = null; - Reference primaryReference = - data.getProgram() - .getReferenceManager() - .getPrimaryReferenceFrom( - data.getMinAddress(), 0); + Program program = data.getProgram(); + ReferenceManager referenceManager = program.getReferenceManager(); + Address minAddress = data.getMinAddress(); + Reference primaryReference = referenceManager.getPrimaryReferenceFrom(minAddress, 0); Object value = data.getValue(); if (primaryReference != null) { refAddr = primaryReference.getToAddress(); @@ -291,20 +284,18 @@ abstract class OperandFieldHelper extends FieldFactory { } } - Program program = data.getProgram(); if (value instanceof Scalar) { Scalar scalar = (Scalar) value; - Equate equate = program.getEquateTable() - .getEquate(data.getMinAddress(), opIndex, - scalar.getValue()); + EquateTable equateTable = program.getEquateTable(); + Equate equate = equateTable.getEquate(minAddress, opIndex, scalar.getValue()); if (equate != null) { - return new EquateOperandFieldLocation(program, data.getMinAddress(), refAddr, + return new EquateOperandFieldLocation(program, minAddress, refAddr, equate.getDisplayName(), equate, opIndex, subOpIndex, translatedLocation.col()); } } - return new OperandFieldLocation(program, data.getMinAddress(), data.getComponentPath(), - refAddr, codeUnitFormat.getDataValueRepresentationString(data), 0, col); + return new OperandFieldLocation(program, minAddress, data.getComponentPath(), refAddr, + codeUnitFormat.getDataValueRepresentationString(data), 0, col); } return null; } @@ -477,9 +468,9 @@ abstract class OperandFieldHelper extends FieldFactory { private int addElements(Instruction inst, List elements, List objList, int opIndex, int subOpIndex, boolean underline, int characterOffset) { - for (int i = 0; i < objList.size(); i++) { - characterOffset = addElement(inst, elements, objList.get(i), underline, opIndex, - subOpIndex, characterOffset); + for (Object element : objList) { + characterOffset = addElement(inst, elements, element, underline, opIndex, subOpIndex, + characterOffset); } return characterOffset; } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/SubDataFieldFactory.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/SubDataFieldFactory.java index 1186e53f04..5fd5703ddb 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/SubDataFieldFactory.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/SubDataFieldFactory.java @@ -30,7 +30,7 @@ import ghidra.util.classfinder.ClassSearcher; /** * Generates data value Fields for data subcomponents. - *

      + *

      * This field is not meant to be loaded by the {@link ClassSearcher}, hence the X in the name. */ public class SubDataFieldFactory extends OperandFieldFactory { @@ -43,16 +43,10 @@ public class SubDataFieldFactory extends OperandFieldFactory { * @param path the component path for the data */ public SubDataFieldFactory(String name, int[] path) { - super(); this.componentPath = path; this.name = name; } - /** - * Constructor - * @param provider The FieldProvider object that serves as the SubDataFieldFactory factory. - * @param model The Field model that will use this Address factory. - */ private SubDataFieldFactory(String name, int[] componentPath, FieldFormatModel model, HighlightProvider hlProvider, ToolOptions displayOptions, ToolOptions fieldOptions) { super(model, hlProvider, displayOptions, fieldOptions); @@ -60,12 +54,6 @@ public class SubDataFieldFactory extends OperandFieldFactory { this.componentPath = componentPath; } - /** - * Returns the FactoryField for the given object at index index. - * @param varWidth the amount of variable width spacing for any fields - * before this one. - * @param proxy the object whose properties should be displayed. - */ @Override public ListingField getField(ProxyObj proxy, int varWidth) { Object obj = proxy.getObject(); @@ -138,8 +126,8 @@ public class SubDataFieldFactory extends OperandFieldFactory { @Override public FieldFactory newInstance(FieldFormatModel formatModel, HighlightProvider provider, - ToolOptions displayOptions, ToolOptions fieldOptions) { - return new SubDataFieldFactory(name, componentPath, formatModel, provider, displayOptions, + ToolOptions options, ToolOptions fieldOptions) { + return new SubDataFieldFactory(name, componentPath, formatModel, provider, options, fieldOptions); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/test/AbstractGhidraHeadlessIntegrationTest.java b/Ghidra/Features/Base/src/main/java/ghidra/test/AbstractGhidraHeadlessIntegrationTest.java index a16f46f1a0..b84187394c 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/test/AbstractGhidraHeadlessIntegrationTest.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/test/AbstractGhidraHeadlessIntegrationTest.java @@ -48,8 +48,7 @@ import ghidra.util.exception.AssertException; import ghidra.util.exception.RollbackException; import junit.framework.AssertionFailedError; import utility.application.ApplicationLayout; -import utility.function.ExceptionalCallback; -import utility.function.ExceptionalFunction; +import utility.function.*; public abstract class AbstractGhidraHeadlessIntegrationTest extends AbstractDockingTest { @@ -68,8 +67,6 @@ public abstract class AbstractGhidraHeadlessIntegrationTest extends AbstractDock private static Language Z80_LANGUAGE; public AbstractGhidraHeadlessIntegrationTest() { - super(); - // Ensure that all error messages do NOT use a gui popup, and instead are routed to the // console. setErrorGUIEnabled(false); @@ -167,7 +164,7 @@ public abstract class AbstractGhidraHeadlessIntegrationTest extends AbstractDock /** * Run a command against the specified program within a transaction. * The transaction will be committed unless the command throws a RollbackException. - * + * * @param program the program * @param cmd the command to apply * @return result of command applyTo method @@ -199,7 +196,7 @@ public abstract class AbstractGhidraHeadlessIntegrationTest extends AbstractDock /** * Provides a convenient method for modifying the current program, handling the transaction * logic. - * + * * @param p the program * @param c the code to execute * @see #modifyProgram(Program, ExceptionalCallback) @@ -222,11 +219,41 @@ public abstract class AbstractGhidraHeadlessIntegrationTest extends AbstractDock } } + /** + * Provides a convenient method for modifying the current program, handling the transaction + * logic and returning a result. + * @param the return type + * @param the exception type + * @param p the program + * @param s the code to execute + * @return the supplier's return value + * @see #modifyProgram(Program, ExceptionalCallback) + * @see #modifyProgram(Program, ExceptionalFunction) + */ + public static T tx(Program p, ExceptionalSupplier s) { + int txId = p.startTransaction("Test - Function in Transaction"); + boolean commit = true; + try { + T t = s.get(); + p.flushEvents(); + waitForSwing(); + return t; + } + catch (Exception e) { + commit = false; + failWithException("Exception modifying program '" + p.getName() + "'", e); + } + finally { + p.endTransaction(txId, commit); + } + return null; + } + /** * Provides a convenient method for modifying the current program, handling the transaction * logic. This method is calls {@link #tx(Program, ExceptionalCallback)}, but helps with * semantics. - * + * * @param p the program * @param c the code to execute * @see #modifyProgram(Program, ExceptionalFunction) @@ -238,7 +265,7 @@ public abstract class AbstractGhidraHeadlessIntegrationTest extends AbstractDock /** * Provides a convenient method for modifying the current program, handling the transaction * logic and returning a new item as a result - * + * * @param program the program * @param f the function for modifying the program and creating the desired result * @return the result @@ -439,7 +466,7 @@ public abstract class AbstractGhidraHeadlessIntegrationTest extends AbstractDock /** * Returns the global symbol with the given name if and only if it is the only * global symbol with that name. - * + * * @param program the program to search. * @param name the name of the global symbol to find. * @return the global symbol with the given name if and only if it is the only one. @@ -451,7 +478,7 @@ public abstract class AbstractGhidraHeadlessIntegrationTest extends AbstractDock /** * Returns the symbol in the given namespace with the given name if and only if it is the only * symbol in that namespace with that name. - * + * * @param program the program to search. * @param name the name of the symbol to find. * @param namespace the parent namespace; may be null @@ -468,14 +495,14 @@ public abstract class AbstractGhidraHeadlessIntegrationTest extends AbstractDock /** * A convenience method that allows you to open the given program in a default tool, * navigating to the given address. - * + * *

      Note: this is a blocking operation. Your test will not proceed while this method is * sleeping. - * + * *

      Do not leave this call in your test when committing changes. * @param p the program * @param address the address - * + * * @throws Exception if there is an issue create a {@link TestEnv} */ public void debugProgramInTool(Program p, String address) throws Exception { @@ -512,7 +539,7 @@ public abstract class AbstractGhidraHeadlessIntegrationTest extends AbstractDock /** * Waits for a launched script to complete by using the given listener. - * + * * @param listener the listener used to track script progress * @param timeoutMS the max time to wait; failing if exceeded */ @@ -534,7 +561,7 @@ public abstract class AbstractGhidraHeadlessIntegrationTest extends AbstractDock /** * Replaces the given implementations of the provided service class with the given class. - * + * * @param tool the tool whose services to update (optional) * @param service the service to override * @param replacement the new version of the service diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/navigation/locationreferences/AbstractLocationReferencesTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/navigation/locationreferences/AbstractLocationReferencesTest.java index 2edcc906db..e10896da19 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/navigation/locationreferences/AbstractLocationReferencesTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/navigation/locationreferences/AbstractLocationReferencesTest.java @@ -44,6 +44,7 @@ import ghidra.program.util.AddressFieldLocation; import ghidra.program.util.FieldNameFieldLocation; import ghidra.test.AbstractProgramBasedTest; import ghidra.test.ClassicSampleX86ProgramBuilder; +import ghidra.util.Msg; import ghidra.util.datastruct.Accumulator; import ghidra.util.datastruct.ListAccumulator; import ghidra.util.exception.CancelledException; @@ -51,7 +52,7 @@ import ghidra.util.table.GhidraTable; import ghidra.util.task.TaskMonitor; /** - * A base class for use by tests that exercise various types of + * A base class for use by tests that exercise various types of * {@link LocationDescriptor}. */ public abstract class AbstractLocationReferencesTest extends AbstractProgramBasedTest { @@ -98,7 +99,7 @@ public abstract class AbstractLocationReferencesTest extends AbstractProgramBase // // Arrays/Structures - // + // DataType type = new IntegerDataType(); DataType pointer = new PointerDataType(type); ArrayDataType array = new ArrayDataType(pointer, 4, pointer.getLength()); @@ -150,7 +151,7 @@ public abstract class AbstractLocationReferencesTest extends AbstractProgramBase private void doGoToDataNameFieldAt(Address a, int[] path) { openData(a); - // note: the path here is + // note: the path here is FieldNameFieldLocation location = new FieldNameFieldLocation(program, a, path, "name", 0); ProgramLocationPluginEvent event = new ProgramLocationPluginEvent("Test", location, program); @@ -318,7 +319,10 @@ public abstract class AbstractLocationReferencesTest extends AbstractProgramBase protected void assertResultCount(int expected) { List

      referenceAddresses = getResultAddresses(); - assertEquals(expected, referenceAddresses.size()); + if (referenceAddresses.size() != expected) { + Msg.debug(this, "Result addresses found: " + referenceAddresses); + fail("Incorrect number of results; see console"); + } } protected void assertResultCount(String msg, int expected) { diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/navigation/locationreferences/LocationReferencesPlugin1Test.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/navigation/locationreferences/LocationReferencesPlugin1Test.java index b6ec49b7de..8ca842aa2f 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/navigation/locationreferences/LocationReferencesPlugin1Test.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/navigation/locationreferences/LocationReferencesPlugin1Test.java @@ -15,13 +15,16 @@ */ package ghidra.app.plugin.core.navigation.locationreferences; -import static org.hamcrest.CoreMatchers.instanceOf; -import static org.hamcrest.CoreMatchers.is; -import static org.junit.Assert.assertThat; +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; import org.junit.Test; +import ghidra.app.cmd.data.CreateDataCmd; import ghidra.app.util.viewer.field.FieldNameFieldFactory; +import ghidra.program.model.address.Address; +import ghidra.program.model.data.*; +import ghidra.program.model.data.Enum; import ghidra.program.util.ProgramLocation; public class LocationReferencesPlugin1Test extends AbstractLocationReferencesTest { @@ -38,4 +41,61 @@ public class LocationReferencesPlugin1Test extends AbstractLocationReferencesTes LocationDescriptor descriptor = ReferenceUtils.getLocationDescriptor(location); assertThat(descriptor, is(instanceOf(StructureMemberLocationDescriptor.class))); } + + @Test + public void testFindStructureField_UnnamedDefaultField() { + + // apply a structure with unnamed fields + Structure struct = (Structure) getDt("/MyStruct"); + Address address = addr(0x01005560); + assertTrue(applyCmd(program, new CreateDataCmd(address, struct))); + + openData(0x01005560); + + goTo(addr(0x01005560), FieldNameFieldFactory.FIELD_NAME, 1); + + ProgramLocation location = codeBrowser.getCurrentLocation(); + LocationDescriptor descriptor = ReferenceUtils.getLocationDescriptor(location); + assertThat(descriptor, is(instanceOf(StructureMemberLocationDescriptor.class))); + } + + @Test + public void testFindEnumByMember() { + + // + // This test searches for usage of an enum field. We will add two different enum field + // uses to make sure we only find the one for which we are searching. + // + + Enum enoom = createEnum(); + Address otherAddress = addr(0x01008014); // 0x1 ONE; this also has references + assertTrue(applyCmd(program, new CreateDataCmd(otherAddress, enoom))); + + // this is the address will will use to search + Address address = addr(0x01008019); // 0x0 ZERO + assertTrue(applyCmd(program, new CreateDataCmd(address, enoom))); + + goTo(address, "Operands", 1); + + search(); + + assertResultCount(1); + } + + private DataType getDt(String path) { + DataTypeManager dtm = program.getDataTypeManager(); + DataType dataType = dtm.getDataType(path); + return dataType; + } + + private Enum createEnum() { + return tx(program, () -> { + ProgramBasedDataTypeManager dtm = program.getDataTypeManager(); + Enum dt = new EnumDataType("TestEnum", 1); + dt.add("ZERO", 0); + dt.add("ONE", 1); + dt.add("TWO", 2); + return (Enum) dtm.addDataType(dt, null); + }); + } } diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/navigation/locationreferences/LocationReferencesPlugin3Test.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/navigation/locationreferences/LocationReferencesPlugin3Test.java index 77930158fb..6060fd1887 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/navigation/locationreferences/LocationReferencesPlugin3Test.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/navigation/locationreferences/LocationReferencesPlugin3Test.java @@ -15,8 +15,7 @@ */ package ghidra.app.plugin.core.navigation.locationreferences; -import static org.hamcrest.CoreMatchers.instanceOf; -import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.*; import static org.junit.Assert.*; import java.util.*; @@ -44,7 +43,7 @@ public class LocationReferencesPlugin3Test extends AbstractLocationReferencesTes int parameterColumn = 1; goTo(address, "Function Signature", parameterColumn); - // change the return type + // change the return type DataType dataType = setReturnTypeToByte(address); search(); @@ -176,7 +175,7 @@ public class LocationReferencesPlugin3Test extends AbstractLocationReferencesTes @Test public void testLabelLocationDescriptor() throws Exception { - // 010039fe - LAB_010039fe + // 010039fe - LAB_010039fe Address address = addr(0x010039fe); int column = 3; goTo(address, "Label", column); @@ -231,19 +230,6 @@ public class LocationReferencesPlugin3Test extends AbstractLocationReferencesTes assertThat(descriptor, is(instanceOf(AddressLocationDescriptor.class))); } - @Test - public void testFieldNameLocationDescriptor_StructureFieldName_ArrayInStructure() - throws Exception { - - openData(0x01005540); - - goTo(addr(0x01005541), FieldNameFieldFactory.FIELD_NAME, 1); - - ProgramLocation location = codeBrowser.getCurrentLocation(); - LocationDescriptor descriptor = ReferenceUtils.getLocationDescriptor(location); - assertThat(descriptor, is(instanceOf(StructureMemberLocationDescriptor.class))); - } - @Test public void testFieldNameLocationDescriptor_StructureInArray() throws Exception { @@ -261,7 +247,7 @@ public class LocationReferencesPlugin3Test extends AbstractLocationReferencesTes @Test public void testFindReferencesToFunctionDefinitionDataTypeFromService() throws Exception { - // + // // For this test we will have to create a FunctionDefinitionData type that matches // that of an existing function // @@ -312,7 +298,7 @@ public class LocationReferencesPlugin3Test extends AbstractLocationReferencesTes // // Dynamic data types should show all references to the the outermost data, including - // offcut. + // offcut. // // go to an unused address @@ -379,11 +365,9 @@ public class LocationReferencesPlugin3Test extends AbstractLocationReferencesTes assertContains(results, from1, from2, from3, stringAddr); } - - //================================================================================================== // Private Methods -//================================================================================================== +//================================================================================================== private void createString_CallStructure(String addressString) throws Exception { // String diff --git a/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RecoveredClassHelper.java b/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RecoveredClassHelper.java index c68ec7eb4b..5d4aade630 100644 --- a/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RecoveredClassHelper.java +++ b/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RecoveredClassHelper.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -//DO NOT RUN. THIS IS NOT A SCRIPT! THIS IS A CLASS THAT IS USED BY SCRIPTS. +//DO NOT RUN. THIS IS NOT A SCRIPT! THIS IS A CLASS THAT IS USED BY SCRIPTS. package classrecovery; import java.util.*; @@ -162,8 +162,8 @@ public class RecoveredClassHelper { extendedFlatAPI = new ExtendedFlatProgramAPI(program, monitor); - this.classDataTypesCategoryPath = - extendedFlatAPI.createDataTypeCategoryPath(CategoryPath.ROOT, DTM_CLASS_DATA_FOLDER_NAME); + this.classDataTypesCategoryPath = extendedFlatAPI + .createDataTypeCategoryPath(CategoryPath.ROOT, DTM_CLASS_DATA_FOLDER_NAME); this.createBookmarks = createBookmarks; this.useShortTemplates = useShortTemplates; @@ -246,8 +246,8 @@ public class RecoveredClassHelper { } /** - * Method to add function to map of class it is contained in some functions are in - * multiple classes becuase they have references to multiple class vtables either + * Method to add function to map of class it is contained in some functions are in + * multiple classes becuase they have references to multiple class vtables either * because they have an inlined parent * @param functions the given list of functions * @param recoveredClass the given class @@ -286,7 +286,7 @@ public class RecoveredClassHelper { } /** - * Method to find all the vftables in the program + * Method to find all the vftables in the program * @return list of all vftable symbols * @throws CancelledException when cancelled */ @@ -368,9 +368,9 @@ public class RecoveredClassHelper { } /** - * Method to create a map with keyset containing all constructor/destructor functions in the - * given list of classes and associated mapping to list of refAddrPairs for called - * constructor/destructor functions + * Method to create a map with keyset containing all constructor/destructor functions in the + * given list of classes and associated mapping to list of refAddrPairs for called + * constructor/destructor functions * @param recoveredClasses the list of classes * @throws CancelledException if cancelled */ @@ -414,13 +414,12 @@ public class RecoveredClassHelper { calledFunction = thunkFunction; } // if thunk, need to use the thunked function to see if it is on list of cds - // but always need to use the actual called function to get reference address + // but always need to use the actual called function to get reference address // need to used the thunked function on the hashmap if (allConstructorsAndDestructors.contains(calledFunction)) { // get list of refs to this function from the calling function - List
      referencesToFunctionBFromFunctionA = - extendedFlatAPI.getReferencesToFunctionBFromFunctionA(function, - referencedFunction); + List
      referencesToFunctionBFromFunctionA = extendedFlatAPI + .getReferencesToFunctionBFromFunctionA(function, referencedFunction); // add them to list of ref address pairs Iterator
      iterator = referencesToFunctionBFromFunctionA.iterator(); while (iterator.hasNext()) { @@ -560,8 +559,8 @@ public class RecoveredClassHelper { * @return the reference to the class vftable in the given function or null if there isn't one * @throws CancelledException if cancelled */ - public Address getFirstClassVftableReference(RecoveredClass recoveredClass, - Function function) throws CancelledException { + public Address getFirstClassVftableReference(RecoveredClass recoveredClass, Function function) + throws CancelledException { List
      vftableReferenceList = functionToVftableRefsMap.get(function); @@ -595,11 +594,11 @@ public class RecoveredClassHelper { } /** - * Method to get a sorted list of both vftable and call refs to ancestor classes of the given + * Method to get a sorted list of both vftable and call refs to ancestor classes of the given * class in the given function * @param function the given function * @param recoveredClass the given class - * @return a sorted list of both vftable and call refs to ancestor classes of the given + * @return a sorted list of both vftable and call refs to ancestor classes of the given * class in the given function * @throws CancelledException if cancelled */ @@ -610,7 +609,7 @@ public class RecoveredClassHelper { Map referenceToClassMapForFunction = getReferenceToClassMap(recoveredClass, function); - // get a list of all ancestor classes referenced in the map + // get a list of all ancestor classes referenced in the map List classHierarchy = recoveredClass.getClassHierarchy(); // make a list of all related class references @@ -632,7 +631,7 @@ public class RecoveredClassHelper { /** * Method to create a map of all references to classes in the given function. Classes are, for this purpose, referenced if they - * a vftable belonging to a class is referenced or if a constructor/destructor function from a class is called + * a vftable belonging to a class is referenced or if a constructor/destructor function from a class is called * @param recoveredClass the given class * @param function the given function * @return Map of Address references to Class object for the given function @@ -719,7 +718,7 @@ public class RecoveredClassHelper { } /** - * Get the list of resolved functions that had multiple FID possibilities before but could + * Get the list of resolved functions that had multiple FID possibilities before but could * be resolved to the correct name. * @return the list of resolved FID functions */ @@ -853,7 +852,7 @@ public class RecoveredClassHelper { /** * Method to get a list of addresses that are references from the given address * @param address the given address - * @return a list of addresses that are references from the given address + * @return a list of addresses that are references from the given address */ private List
      getReferenceFromAddresses(Address address) { @@ -871,9 +870,9 @@ public class RecoveredClassHelper { } /** - * Retrieve the first stored vftable from the pcodeOps in the list + * Retrieve the first stored vftable from the pcodeOps in the list * @param storedPcodeOps list of offset/PcodeOp pairs - * @return first referenced vftable address + * @return first referenced vftable address * @throws CancelledException if cancelled */ public Address getStoredVftableAddress(List storedPcodeOps) @@ -921,7 +920,7 @@ public class RecoveredClassHelper { } /** - * Method to retrieve a list of vftable symbols, from the given list of vftables, in the given + * Method to retrieve a list of vftable symbols, from the given list of vftables, in the given * namespace * @param vftableSymbols the list of all vftable symbols * @param namespace the given namespace @@ -985,7 +984,7 @@ public class RecoveredClassHelper { /** * Method to determine if the given function is an inlined destructor or indeterminate in any class * @param function the given function - * @return true if the given function is an inlined function in any class, false if it is not an + * @return true if the given function is an inlined function in any class, false if it is not an * inlined function in any class * @throws CancelledException if cancelled */ @@ -1043,7 +1042,7 @@ public class RecoveredClassHelper { Namespace originalNamespace = function.getParentNamespace(); // temporarily remove class data type or other empty structures from return and params - // so they do not skew the structure contents from FillOutStructureCmd + // so they do not skew the structure contents from FillOutStructureCmd temporarilyReplaceEmptyStructures(function, recoveredClass.getClassNamespace()); // create maps and updates class member data information using structure and pcode info returned @@ -1100,9 +1099,10 @@ public class RecoveredClassHelper { if (!vftableClass.equals(recoveredClass)) { if (DEBUG) { - Msg.debug(this, "updating struct for " + recoveredClass.getName() + - " but first vftable in function " + function.getEntryPoint().toString() + - " is in class " + vftableClass.getName()); + Msg.debug(this, + "updating struct for " + recoveredClass.getName() + + " but first vftable in function " + function.getEntryPoint().toString() + + " is in class " + vftableClass.getName()); } return; @@ -1123,7 +1123,7 @@ public class RecoveredClassHelper { public void getStructureFromDecompilerPcode(RecoveredClass recoveredClass, Function function, Address firstVftableReference) throws CancelledException { - // get the decompiler highFunction + // get the decompiler highFunction HighFunction highFunction = decompilerUtils.getHighFunction(function); if (highFunction == null) { @@ -1133,7 +1133,7 @@ public class RecoveredClassHelper { List highVariables = new ArrayList(); - // if there are params add the first or the "this" param to the list to be checked first + // if there are params add the first or the "this" param to the list to be checked first // It is the most likely to store the vftablePtr if (highFunction.getFunctionPrototype().getNumParams() > 0) { @@ -1145,8 +1145,8 @@ public class RecoveredClassHelper { } // add the other high variables that store vftable pointer - highVariables.addAll( - getVariableThatStoresVftablePointer(highFunction, firstVftableReference)); + highVariables + .addAll(getVariableThatStoresVftablePointer(highFunction, firstVftableReference)); Iterator highVariableIterator = highVariables.iterator(); Address vftableAddress = null; @@ -1194,7 +1194,7 @@ public class RecoveredClassHelper { /** * Method to determine which variable in the decompiler stores the vftable address - * @param highFunction the decompiler high function + * @param highFunction the decompiler high function * @param vftableReference the address that points to a vftable * @return the list of variables in the given function that store the vftable address * @throws CancelledException if cancelled @@ -1262,7 +1262,8 @@ public class RecoveredClassHelper { continue; } - PointerDataType ptrUndefined = extendedFlatAPI.createPointerToUndefinedDataType(dataType); + PointerDataType ptrUndefined = + extendedFlatAPI.createPointerToUndefinedDataType(dataType); if (ptrUndefined != null) { function.getParameter(i).setDataType(ptrUndefined, SourceType.ANALYSIS); } @@ -1282,7 +1283,7 @@ public class RecoveredClassHelper { } /** - * Method to determine if the given possible ancestor is an ancestor of any of the listed classes + * Method to determine if the given possible ancestor is an ancestor of any of the listed classes * @param recoveredClasses List of classes * @param possibleAncestor possible ancestor of one of the listed classes * @return true if ancestor of one of the listed classes, false otherwise @@ -1356,7 +1357,7 @@ public class RecoveredClassHelper { /** * Method to get the listing address that the given PcodeOp is associated with * @param pcodeOp the given PcodeOp - * @return the address the given PcodeOp is associated with + * @return the address the given PcodeOp is associated with */ public Address getTargetAddressFromPcodeOp(PcodeOp pcodeOp) { return pcodeOp.getSeqnum().getTarget(); @@ -1364,12 +1365,7 @@ public class RecoveredClassHelper { public Function getCalledFunctionFromCallPcodeOp(PcodeOp calledPcodeOp) { - Function calledFunction = - api.getFunctionAt(calledPcodeOp.getInput(0).getAddress()); - if (calledFunction == null) { - return null; - } - + Function calledFunction = api.getFunctionAt(calledPcodeOp.getInput(0).getAddress()); return calledFunction; } @@ -1709,7 +1705,7 @@ public class RecoveredClassHelper { * Method to retrieve a single common function on both lists * @param list1 first list of functions * @param list2 second list of functions - * @return single function if there is one function on both lists, null if there are none or + * @return single function if there is one function on both lists, null if there are none or * more than one */ public Function getFunctionOnBothLists(List list1, List list2) { @@ -1770,7 +1766,8 @@ public class RecoveredClassHelper { ReferenceAddressPair refAddrPair = iterator.next(); Address sourceAddr = refAddrPair.getSource(); if (sourceAddr.compareTo(maxVftableReference) > 0) { - Function calledFunction = extendedFlatAPI.getFunctionAt(refAddrPair.getDestination()); + Function calledFunction = + extendedFlatAPI.getFunctionAt(refAddrPair.getDestination()); if (calledFunction != null) { possibleParentDestructors.add(calledFunction); } @@ -1821,7 +1818,7 @@ public class RecoveredClassHelper { List possibleParentConstructors = getPossibleParentConstructors(constDestFunction); - // remove any known destructors since they can't also be constructors - rarely these + // remove any known destructors since they can't also be constructors - rarely these // show up on possible const list possibleParentConstructors.removeAll(parentDestructors); @@ -1834,18 +1831,18 @@ public class RecoveredClassHelper { continue; } - // based on call order get possible parent destructors for the given function + // based on call order get possible parent destructors for the given function List possibleParentDestructors = getPossibleParentDestructors(constDestFunction); - // remove any known constructors since they can't also be destructors - rarely these + // remove any known constructors since they can't also be destructors - rarely these // show up on possible dest list possibleParentDestructors.removeAll(parentConstructors); Function parentDestructor = getFunctionOnBothLists(possibleParentDestructors, parentConstDestFunctions); - // another sanity check - make sure child function isn't a known constructor + // another sanity check - make sure child function isn't a known constructor if (parentDestructor != null && !childConstructors.contains(constDestFunction)) { childParentDestructorMap.put(constDestFunction, parentDestructor); continue; @@ -1866,7 +1863,7 @@ public class RecoveredClassHelper { } } - // once all checks pass, add both the child and parent constructors to their class + // once all checks pass, add both the child and parent constructors to their class // constructor list and remove from the indeterminate lists // the addConstructor method processes the offsets and types for the initialized class data Iterator childConstructorIterator = constructorKeySet.iterator(); @@ -1896,8 +1893,8 @@ public class RecoveredClassHelper { } /** - * Method to retrieve all types of class destructors including normal destructors, non-this - * destructors and inline destructors(does not include deleting destructors since they are + * Method to retrieve all types of class destructors including normal destructors, non-this + * destructors and inline destructors(does not include deleting destructors since they are * really a vfunction) * @param recoveredClass the given class * @return the list of all destructors for the given class @@ -1913,7 +1910,7 @@ public class RecoveredClassHelper { } /** - * Method to retrieve all types of class constructors including normal constructors and inline + * Method to retrieve all types of class constructors including normal constructors and inline * constructors * @param recoveredClass the given class * @return the list of all constructors for the given class @@ -1952,7 +1949,7 @@ public class RecoveredClassHelper { if (recoveredClass.getOrderToVftableMap().size() == 0) { createVftableOrderMapping(recoveredClass); } - + recoveredClass.addConstructor(constructorFunction); addToAllConstructors(constructorFunction); } @@ -1962,7 +1959,7 @@ public class RecoveredClassHelper { * @param recoveredClass given class * @param inlinedConstructorFunction given function * @throws InvalidInputException if issues setting return type - * @throws DuplicateNameException if try to create same symbol name already in namespace + * @throws DuplicateNameException if try to create same symbol name already in namespace */ public void addInlinedConstructorToClass(RecoveredClass recoveredClass, Function inlinedConstructorFunction) @@ -1977,7 +1974,7 @@ public class RecoveredClassHelper { * @param recoveredClass given class * @param destructorFunction given destructor function * @throws InvalidInputException if issues setting return type - * @throws DuplicateNameException if try to create same symbol name already in namespace + * @throws DuplicateNameException if try to create same symbol name already in namespace */ public void addDestructorToClass(RecoveredClass recoveredClass, Function destructorFunction) throws InvalidInputException, DuplicateNameException { @@ -2061,7 +2058,7 @@ public class RecoveredClassHelper { } /** - * + * * @param referenceToClassMap map of references to the class that contains the referenced function * @param referencesToConstructors list of addresses referring to constructors * @throws CancelledException if cancelled @@ -2080,7 +2077,8 @@ public class RecoveredClassHelper { Address constructorReference = constructorIterator.next(); RecoveredClass recoveredClass = referenceToClassMap.get(constructorReference); - Function constructor = extendedFlatAPI.getReferencedFunction(constructorReference, true); + Function constructor = + extendedFlatAPI.getReferencedFunction(constructorReference, true); if (recoveredClass.getIndeterminateList().contains(constructor)) { addConstructorToClass(recoveredClass, constructor); @@ -2223,8 +2221,8 @@ public class RecoveredClassHelper { /** * Method to get the address that references the first vftable in the given function * @param function the given function - * @return the address in the given function that references the first referenced vftable or - * null if no vftable is referenced in the given function + * @return the address in the given function that references the first referenced vftable or + * null if no vftable is referenced in the given function */ public Address getFirstVftableReferenceInFunction(Function function) { @@ -2388,7 +2386,7 @@ public class RecoveredClassHelper { } /** - * Method to get the total number of deleting destructors in the given list of classes + * Method to get the total number of deleting destructors in the given list of classes * @param recoveredClasses the list of classes * @return the total number of deleting destructors in the given list of classes * @throws CancelledException if cancelled @@ -2409,7 +2407,7 @@ public class RecoveredClassHelper { } /** - * Method to retrieve the total number of clone functions assigned to all the classes + * Method to retrieve the total number of clone functions assigned to all the classes * @param recoveredClasses List of classes * @return total number of clone functions assigned to classes * @throws CancelledException if cancelled @@ -2479,10 +2477,10 @@ public class RecoveredClassHelper { /** * Method to get a list of functions from the list of classes that could not be determined whether they - * were constructors or destructors + * were constructors or destructors * @param recoveredClasses the list of classes * @return list of functions from the list of classes that could not be determined whether they - * were constructors or destructors + * were constructors or destructors * @throws CancelledException if cancelled */ public List getRemainingIndeterminates(List recoveredClasses) @@ -2506,7 +2504,7 @@ public class RecoveredClassHelper { } /** - * + * * @param referenceToClassMap map from reference to class the referenced function is in * @param referencesToDestructors list of addresses referring to destructors * @throws CancelledException if cancelled @@ -2584,10 +2582,10 @@ public class RecoveredClassHelper { /** * Method to determine if a class's identified vbase_destructor is valid or not * If the class has both a vbase destructor and a regular destructor and the class has - * a non-virtual ancestor, and either the class is the lowest child or has a child with a + * a non-virtual ancestor, and either the class is the lowest child or has a child with a * vbase_destructor then it is a valid vbase_destructor. Otherwise, it isn't. * @param recoveredClass the given class object - * @return true if class has a vbase destructor, false if not + * @return true if class has a vbase destructor, false if not */ private boolean hasVbaseDestructor(RecoveredClass recoveredClass) throws CancelledException { Function vBaseDestructor = recoveredClass.getVBaseDestructor(); @@ -2613,10 +2611,10 @@ public class RecoveredClassHelper { } /** - * Method to determine if the given class has a child class with both a vbase destructor and a + * Method to determine if the given class has a child class with both a vbase destructor and a * regular destructor * @param recoveredClass the given class - * @return true if the given class has a child class with both a vbase destructor and a regular + * @return true if the given class has a child class with both a vbase destructor and a regular * destructor, false otherwise * @throws CancelledException if cancelled */ @@ -2650,8 +2648,8 @@ public class RecoveredClassHelper { String className = namespace.getName(); String classNameWithNamespace = namespace.getName(true); - CategoryPath classPath = extendedFlatAPI.createDataTypeCategoryPath(classDataTypesCategoryPath, - classNameWithNamespace); + CategoryPath classPath = extendedFlatAPI + .createDataTypeCategoryPath(classDataTypesCategoryPath, classNameWithNamespace); RecoveredClass newClass = new RecoveredClass(className, classPath, namespace, dataTypeManager); @@ -2672,7 +2670,7 @@ public class RecoveredClassHelper { * 5. add mapping from vftableAddress to class * 6. add list of const/dest functions to RecoveredClass object * 7. update list of all const/dest functions in currenetProgram - * 8. set RecoveredClass indeterminate list to const/dest list + * 8. set RecoveredClass indeterminate list to const/dest list * 9. update list of all indeterminate const/dest * @param vftableSymbolList List of vftable symbols * @param allowNullFunctionPtrs if true, allow existance of null pointers in vftable @@ -2714,7 +2712,7 @@ public class RecoveredClassHelper { } // Check to see if already have an existing RecoveredClass object for the - // class associated with the current vftable. + // class associated with the current vftable. RecoveredClass recoveredClass = getClass(vftableNamespace); if (recoveredClass == null) { @@ -2758,7 +2756,7 @@ public class RecoveredClassHelper { recoveredClass.addIndeterminateConstructorOrDestructorList( possibleConstructorDestructorsForThisClass); - // Add them to the list of all constructors and destructors in program + // Add them to the list of all constructors and destructors in program updateAllConstructorsAndDestructorsList(possibleConstructorDestructorsForThisClass); } // end of looping over vfTables @@ -2806,7 +2804,7 @@ public class RecoveredClassHelper { } /** - * Method to promote the namespace is a class namespace. + * Method to promote the namespace is a class namespace. * @return true if namespace is (now) a class namespace or false if it could not be promoted. */ private Namespace promoteToClassNamespace(Namespace namespace) { @@ -3014,8 +3012,8 @@ public class RecoveredClassHelper { } /** - * Method to find references to vftables that are not in functions, either in undefined areas or - * instructions that are not in functions. + * Method to find references to vftables that are not in functions, either in undefined areas or + * instructions that are not in functions. * @param vftableSymbols List of vftable symbols * @return List of addresses where vftables are referenced but are not in a function * @throws CancelledException when cancelled @@ -3096,10 +3094,10 @@ public class RecoveredClassHelper { } /** - * Method to add a search pattern, to the searcher, for the set of bytes representing a vftable + * Method to add a search pattern, to the searcher, for the set of bytes representing a vftable * address * @param searcher the MemoryBytePatternSearcher - * @param notInFunctionVftableRefs a list addresses of vftable references that are not contained + * @param notInFunctionVftableRefs a list addresses of vftable references that are not contained * in a function * @param newFunctions a list of newly created functions that reference the given vftable address * @param vftableAddress the given vftable address @@ -3135,10 +3133,11 @@ public class RecoveredClassHelper { notInFunctionVftableRefs.add(addr); } else { - boolean functionCreated = extendedFlatAPI.createFunction(program, addr); - if (!functionCreated) { - notInFunctionVftableRefs.add(addr); - } + boolean functionCreated = extendedFlatAPI.createFunction(prog, addr); + if (!functionCreated) { + notInFunctionVftableRefs.add(addr); + } + } } } @@ -3157,7 +3156,7 @@ public class RecoveredClassHelper { * Method to create a string buffer containing class parents in the correct order. The format * of the parent string is of the format "class : : ... * where parentN_spec = "virtual (only if inherited virtually) " - * Examples: + * Examples: * The class Pet with no parents would be "class Pet" * The class Cat with non-virtual parent Pet would be "class Cat : Pet" * The class A with virtual parent B and non-virtual parent C would be "class A : virtual B : C" @@ -3215,9 +3214,8 @@ public class RecoveredClassHelper { List parentClasses = recoveredClass.getClassHierarchy(); if (parentClasses.isEmpty()) { - throw new Exception( - recoveredClass.getClassNamespace().getName(true) + - " should not have an empty class hierarchy"); + throw new Exception(recoveredClass.getClassNamespace().getName(true) + + " should not have an empty class hierarchy"); } // if size one it only includes self @@ -3294,7 +3292,7 @@ public class RecoveredClassHelper { // listing has void too, otherwise, leave it as is, probably a void String returnType = getReturnTypeFromDecompiler(constructorFunction); - // Set error bookmark, add error message, and get the listing return type if the + // Set error bookmark, add error message, and get the listing return type if the // decompiler return type is null if (returnType == null) { @@ -3303,8 +3301,9 @@ public class RecoveredClassHelper { Msg.debug(this, msg1 + " at " + constructorFunction.getEntryPoint() + msg2); - program.getBookmarkManager().setBookmark(constructorFunction.getEntryPoint(), - BookmarkType.ERROR, "Decompiler Error", msg1 + msg2); + program.getBookmarkManager() + .setBookmark(constructorFunction.getEntryPoint(), BookmarkType.ERROR, + "Decompiler Error", msg1 + msg2); // get the return type from the listing and in some cases it will // indicate the correct type to help determine the below type to add @@ -3404,8 +3403,7 @@ public class RecoveredClassHelper { * @throws Exception when cancelled */ public void addVbaseDestructorsToClassNamespace(RecoveredClass recoveredClass, - Structure classStruct) - throws Exception { + Structure classStruct) throws Exception { Namespace classNamespace = recoveredClass.getClassNamespace(); @@ -3561,7 +3559,7 @@ public class RecoveredClassHelper { if (!isReplaceableType(function.getEntryPoint(), baseDataType)) { return; } - // create copy of existing one + // create copy of existing one DataType baseDataTypeCopy = baseDataType.copy(dataTypeManager); renameDataType(baseDataTypeCopy, baseDataType.getName() + "_REPLACED"); @@ -3619,23 +3617,23 @@ public class RecoveredClassHelper { return true; } - // test to see if the data type is an empty structure with "PlaceHolder Class Structure" in - // the description + // test to see if the data type is an empty structure with "PlaceHolder Class Structure" in + // the description Structure structure = (Structure) dataType; if (structure.isNotYetDefined() && structure.getDescription().equals("PlaceHolder Class Structure")) { return true; } - if (program.getBookmarkManager().getBookmark(address, BookmarkType.ANALYSIS, - "Function ID Analyzer") != null) { + if (program.getBookmarkManager() + .getBookmark(address, BookmarkType.ANALYSIS, "Function ID Analyzer") != null) { return true; } return false; } /** - * Method to remove existing class structures from the data type manager that were replaced by + * Method to remove existing class structures from the data type manager that were replaced by * newly created class structures and that have the "_REPLACED" suffix on them * @param recoveredClasses list of given recovered classes * @param removeNonEmpty if true, remove not only the empty replaced class structures but @@ -3652,10 +3650,10 @@ public class RecoveredClassHelper { for (RecoveredClass recoveredClass : recoveredClasses) { monitor.checkCanceled(); - // first get the new class structure and verify it exists - don't remove others if + // first get the new class structure and verify it exists - don't remove others if // new one doesn't exist - DataType classStructureDataType = dataTypeManager.getDataType( - recoveredClass.getClassPath(), recoveredClass.getName()); + DataType classStructureDataType = dataTypeManager + .getDataType(recoveredClass.getClassPath(), recoveredClass.getName()); if (classStructureDataType == null) { continue; } @@ -3798,7 +3796,8 @@ public class RecoveredClassHelper { if (symbol != null && symbol.getSource() == SourceType.ANALYSIS && !symbol.getName().equals(name) && !symbol.getParentNamespace().equals(namespace)) { - // add to list of bad namespaces to be cleaned up later + + // add to list of bad namespaces to be cleaned up later Namespace parentNamespace = symbol.getParentNamespace(); if (!parentNamespace.isGlobal()) { badFIDNamespaces.add(parentNamespace); @@ -3816,7 +3815,7 @@ public class RecoveredClassHelper { } return; } - // FID with multiple matches - either all FID_conflicts or one common name + // FID with multiple matches - either all FID_conflicts or one common name // since no good namespace all need to be removed but if there is a good base name // add FID if (bookmarkComment.contains("Multiple Matches")) { @@ -3863,7 +3862,7 @@ public class RecoveredClassHelper { throws CancelledException, InvalidInputException, DuplicateNameException, CircularDependencyException { - // find bad structure parameter data types + // find bad structure parameter data types List badStructureDataTypes = findBadParameterDataTypes(function, namespace); // find bad structure return data types @@ -3877,7 +3876,7 @@ public class RecoveredClassHelper { if (badStructureDataTypes.size() > 0) { // find all functions that call this function and do the same fixBadSignatures(function, badStructureDataTypes); - // add all the new bad dts to the list of bad ones + // add all the new bad dts to the list of bad ones Iterator badStructuresIterator = badStructureDataTypes.iterator(); while (badStructuresIterator.hasNext()) { monitor.checkCanceled(); @@ -3940,7 +3939,7 @@ public class RecoveredClassHelper { } /** - * Method to find and add to permanent removal list any incorrect empty structure params + * Method to find and add to permanent removal list any incorrect empty structure params * @param function the function to check for bad params * @param namespace the correct parent namespace of function * @throws CancelledException when cancelled @@ -3968,7 +3967,7 @@ public class RecoveredClassHelper { } /** - * Method to replace the given bad structure data types with undefined data types of same size + * Method to replace the given bad structure data types with undefined data types of same size * for the given functions parameters * @param function the function to fix * @param badStructureDataTypes the list of bad structure data types to replace if found @@ -4031,8 +4030,8 @@ public class RecoveredClassHelper { } /** - * Method to fix a bad return type if it is one of the bad structure data types on the given - * list. The list was previously generated from functions that had incorrect FID signatures + * Method to fix a bad return type if it is one of the bad structure data types on the given + * list. The list was previously generated from functions that had incorrect FID signatures * placed on them that this script recognized and corrected. * @param function the given function * @param badStructureDataTypes a list of bad structure data types @@ -4148,12 +4147,12 @@ public class RecoveredClassHelper { } /** - * Method to determine if the given constructor function calls any non-parent constructors + * Method to determine if the given constructor function calls any non-parent constructors * before the vftable refererence * @param recoveredClass the given class * @param constructor the given constructor function - * @param vftableReference the address of the reference to the class vftable - * @return true if the given constructor function calls any non-parent constructors before the + * @param vftableReference the address of the reference to the class vftable + * @return true if the given constructor function calls any non-parent constructors before the * vftable refererence, false otherwise * @throws CancelledException if cancelled */ @@ -4180,7 +4179,7 @@ public class RecoveredClassHelper { return false; } - // if call is before vtable and is not an inherited constructor and not the operator_new + // if call is before vtable and is not an inherited constructor and not the operator_new // then return true Address calledAddress = refPair.getDestination(); Function calledFunction = api.getFunctionAt(calledAddress); @@ -4206,7 +4205,7 @@ public class RecoveredClassHelper { } /** - * Method to find class clone functions + * Method to find class clone functions * @param recoveredClasses List of RecoveredClass objects * @throws CancelledException when script is cancelled * @throws Exception if issues making label @@ -4222,8 +4221,7 @@ public class RecoveredClassHelper { RecoveredClass recoveredClass = recoveredClassIterator.next(); - List allOtherConstructors = - new ArrayList(getAllConstructors()); + List allOtherConstructors = new ArrayList(getAllConstructors()); allOtherConstructors.removeAll(recoveredClass.getConstructorList()); // iterate through the vtable functions @@ -4406,7 +4404,7 @@ public class RecoveredClassHelper { } /** - * Method to remove the empty namespaces and unreferenced empty class structures that + * Method to remove the empty namespaces and unreferenced empty class structures that * that were incorrectly applied by FID * @throws CancelledException when script is cancelled */ @@ -4454,7 +4452,7 @@ public class RecoveredClassHelper { } /** - * Method to remove the incorrectly applied and unreferenced empty structures that are not used + * Method to remove the incorrectly applied and unreferenced empty structures that are not used * @throws CancelledException when script is cancelled */ private void removeEmptyStructures() throws CancelledException { @@ -4464,10 +4462,15 @@ public class RecoveredClassHelper { monitor.checkCanceled(); - Structure badStructure = badStructureIterator.next(); // if not used by anything remove it + Structure badStructure = badStructureIterator.next(); ListAccumulator accumulator = new ListAccumulator<>(); - ReferenceUtils.findDataTypeReferences(accumulator, badStructure, null, program, true, + + // TODO Discovering types can be very slow for large binaries. Discovering types + // should not be needed. Passing false here will still find all applied uses of the + // given structure. + boolean discoverTypes = true; + ReferenceUtils.findDataTypeReferences(accumulator, badStructure, program, discoverTypes, monitor); List referenceList = accumulator.asList(); @@ -4508,7 +4511,7 @@ public class RecoveredClassHelper { } /** - * Method to create empty vftable structures before class struct is created so that + * Method to create empty vftable structures before class struct is created so that * they can be added to the class structure. Afterwords, they are filled in with pointers * to vftable functions * @param recoveredClass the given class @@ -4631,9 +4634,10 @@ public class RecoveredClassHelper { * @throws Exception if other exception */ public void fillInAndApplyVftableStructAndNameVfunctions(RecoveredClass recoveredClass, - Map vftableToStructureMap, Structure classStruct) throws CancelledException, Exception { + Map vftableToStructureMap, Structure classStruct) + throws CancelledException, Exception { - //create function definition for each virtual function and put in vftable structure and + //create function definition for each virtual function and put in vftable structure and // data subfolder CategoryPath classPath = recoveredClass.getClassPath(); @@ -4660,7 +4664,6 @@ public class RecoveredClassHelper { nameVfunctions(recoveredClass, vftableAddress, vftableStructureName); } - List vFunctions = recoveredClass.getVirtualFunctions(vftableAddress); int vfunctionNumber = 1; Iterator vfIterator = vFunctions.iterator(); @@ -4683,7 +4686,6 @@ public class RecoveredClassHelper { replaceClassStructure(vfunction, recoveredClass.getName(), classStruct); } - String forClassSuffix = getForClassSuffix(vftableStructureName); String functionDefName = vfunction.getName(); int indexOfSuffix = functionDefName.indexOf(forClassSuffix); @@ -4695,9 +4697,8 @@ public class RecoveredClassHelper { } // get the classPath of highest level parent with vfAddress in their vftable - classPath = - getCategoryPathForFunctionSignature(vfunction, functionDefName, recoveredClass, - vftableAddress); + classPath = getCategoryPathForFunctionSignature(vfunction, functionDefName, + recoveredClass, vftableAddress); Symbol vfunctionSymbol = symbolTable.getPrimarySymbol(vfunction.getEntryPoint()); Namespace parentNamespace = vfunctionSymbol.getParentNamespace(); @@ -4790,7 +4791,7 @@ public class RecoveredClassHelper { vftableStruct = (Structure) dataTypeManager.addDataType(vftableStruct, DataTypeConflictHandler.DEFAULT_HANDLER); - // clear the array or unprocessed structure at the current vftable location and + // clear the array or unprocessed structure at the current vftable location and // apply the structure. It has to be one or the other and the correct length // because of the check at the beginning of the script that checked for either // array or structure of pointers and got size from them initially @@ -5030,7 +5031,7 @@ public class RecoveredClassHelper { } // else class has normal single inheritance so just search for the matching function definition - // in one of the class's ancestors and return the ancestor class it is found in or if not found, + // in one of the class's ancestors and return the ancestor class it is found in or if not found, // return the current class List classHierarchy = recoveredClass.getClassHierarchy(); @@ -5038,7 +5039,7 @@ public class RecoveredClassHelper { // to search only the ancestors Iterator classHierarchyIterator = classHierarchy.listIterator(1); - // TODO: eventually use the function definition to do an equivalence check amongst + // TODO: eventually use the function definition to do an equivalence check amongst // possible same-name vfunctions to make sure getting the correct one // in this case need to update the below check in dtMan to look through the .conflicts // FunctionDefinition functionDataType = new FunctionDefinitionDataType(vfunction, true); @@ -5070,7 +5071,7 @@ public class RecoveredClassHelper { } /** - * + * * @param functionDefDataType the function definition * @param classPath the given data type manager classPath * @return pointer to function signature data type @@ -5085,7 +5086,7 @@ public class RecoveredClassHelper { PointerDataType functionPointerDataType; - // If the given function definition doesn't exist in this folder make a new one and + // If the given function definition doesn't exist in this folder make a new one and // make a pointer to it if (existingDataType == null) { functionDefDataType.setCategoryPath(classPath); @@ -5098,6 +5099,7 @@ public class RecoveredClassHelper { return functionPointerDataType; } + /** * Method to add precomment inside functions containing inlined constructors at approximate * address of start of inlined function @@ -5154,7 +5156,8 @@ public class RecoveredClassHelper { while (inlinedDestructorIterator.hasNext()) { monitor.checkCanceled(); Function destructorFunction = inlinedDestructorIterator.next(); - Address classVftableRef = getFirstClassVftableReference(recoveredClass, destructorFunction); + Address classVftableRef = + getFirstClassVftableReference(recoveredClass, destructorFunction); if (classVftableRef == null) { continue; @@ -5320,8 +5323,7 @@ public class RecoveredClassHelper { extendedFlatAPI.getCalledFunctionByCallOrder(deletingDestructor, 1); if (firstCalledFunction == null || !recoveredClass.getConstructorOrDestructorFunctions() - .contains( - firstCalledFunction)) { + .contains(firstCalledFunction)) { return null; } @@ -5361,7 +5363,7 @@ public class RecoveredClassHelper { } /** - * + * * @param recoveredClass the given class * @param virtualFunction the given virtual function * @param operatorDeleteFunction the operator delete function @@ -5425,7 +5427,8 @@ public class RecoveredClassHelper { continue; } - Function referencedFunction = extendedFlatAPI.getReferencedFunction(codeUnitAddress, true); + Function referencedFunction = + extendedFlatAPI.getReferencedFunction(codeUnitAddress, true); if (referencedFunction == null) { continue; } @@ -5456,12 +5459,10 @@ public class RecoveredClassHelper { if (parentDestructorClasses.size() == 1) { if (!parentDestructorClasses.get(0) .getDestructorList() - .contains( - parentDestructor)) { + .contains(parentDestructor)) { addDestructorToClass(parentDestructorClasses.get(0), parentDestructor); parentDestructorClasses.get(0) - .removeIndeterminateConstructorOrDestructor( - parentDestructor); + .removeIndeterminateConstructorOrDestructor(parentDestructor); } } // if more than one parent class for this function then let either inline or multi-class @@ -5484,10 +5485,10 @@ public class RecoveredClassHelper { } /** - * Method to remove functions from the class constructor/destructor lists (and the overall list) - * that are not self-contained constructor/destructor functions. Add them to the list of + * Method to remove functions from the class constructor/destructor lists (and the overall list) + * that are not self-contained constructor/destructor functions. Add them to the list of * functions that contain inlined constructors or destructors. - * NOTE: this must be called after the global const/dest list is created but before + * NOTE: this must be called after the global const/dest list is created but before * functions get added to the other class lists * @param recoveredClasses list of classes to process * @throws CancelledException if cancelled @@ -5526,8 +5527,8 @@ public class RecoveredClassHelper { } /** - * Method to add the structure components from the given structureToAdd from the given starting - * offset to the given ending offset of the to the given structure at the given offset + * Method to add the structure components from the given structureToAdd from the given starting + * offset to the given ending offset of the to the given structure at the given offset * @param structure the structure to add to * @param structureToAdd the structure to add a subset of components from to the given structure * @param startOffset the starting offset of where to start adding in the container structure @@ -5538,7 +5539,7 @@ public class RecoveredClassHelper { public boolean addIndividualComponentsToStructure(Structure structure, Structure structureToAdd, int startOffset, int dataLength) throws CancelledException { - // this check does not allow growing of structure. It only allows adding to the structure if + // this check does not allow growing of structure. It only allows adding to the structure if // the block of components to add will fit at the given offset if (!EditStructureUtils.canAdd(structure, startOffset, dataLength, false, monitor)) { return false; @@ -5548,7 +5549,7 @@ public class RecoveredClassHelper { monitor.checkCanceled(); - // only copy the components up to the given total dataLength to copy + // only copy the components up to the given total dataLength to copy if ((dataTypeComponent.getOffset() + dataTypeComponent.getLength()) > dataLength) { break; } @@ -5560,7 +5561,6 @@ public class RecoveredClassHelper { return true; } - /** * Method to add alignment to the given length based on the default program address size * @param len the given length @@ -5613,13 +5613,11 @@ public class RecoveredClassHelper { List virtualParentClassStructures = getVirtualParentClassStructures(recoveredClass); - // if there are no virtual parents there will be no internal data + // if there are no virtual parents there will be no internal data if (virtualParentClassStructures.size() == 0) { return NONE; } - - DataTypeComponent[] definedComponents = structure.getDefinedComponents(); if (definedComponents.length == 0) { @@ -5654,13 +5652,13 @@ public class RecoveredClassHelper { } // if the current offset is differnet than the next offset then it is after the gap - // of undefineds and we have found the offset we need to return + // of undefineds and we have found the offset we need to return if (currentOffset != nextOffset) { return currentOffset; } - // if the currentOffset equals what we calculated as the next offset then the - // current data is contiguous to the last data so no undefineds between them and + // if the currentOffset equals what we calculated as the next offset then the + // current data is contiguous to the last data so no undefineds between them and // can continue looking for the gap of undefines nextOffset = currentOffset + dataTypeComponent.getLength(); @@ -5781,8 +5779,7 @@ public class RecoveredClassHelper { if (recoveredClass.getClassOffsetToVftableMap() .values() - .containsAll( - recoveredClass.getVftableAddresses())) { + .containsAll(recoveredClass.getVftableAddresses())) { return true; } return false; @@ -5818,7 +5815,7 @@ public class RecoveredClassHelper { continue; } - // get first called function and verify is not a c/d function in current class or + // get first called function and verify is not a c/d function in current class or // any class get second called function and verify it is operator delete Function firstCalledFunction = extendedFlatAPI.getCalledFunctionByCallOrder(vFunction, 1); @@ -5826,8 +5823,7 @@ public class RecoveredClassHelper { extendedFlatAPI.getCalledFunctionByCallOrder(vFunction, 2); if (firstCalledFunction != null && secondCalledFunction != null && !recoveredClass.getConstructorOrDestructorFunctions() - .contains( - firstCalledFunction) && + .contains(firstCalledFunction) && secondCalledFunction.equals(operator_delete) && !getAllConstructorsAndDestructors().contains(vFunction)) { recoveredClass.addDeletingDestructor(vFunction); @@ -5910,7 +5906,7 @@ public class RecoveredClassHelper { } /** - * Method to determine if the vftable reference(s) in a constructor are not in the first code + * Method to determine if the vftable reference(s) in a constructor are not in the first code * block for constructors that have more than one block * @param recoveredClasses List of RecoveredClass objects * @throws CancelledException when cancelled @@ -5950,9 +5946,9 @@ public class RecoveredClassHelper { continue; } - // if the first vftable reference is not in the first code block and the - // constructor calls any non-inherited constructors before the vtable reference, - // the constructor function is really another function with the constructor + // if the first vftable reference is not in the first code block and the + // constructor calls any non-inherited constructors before the vtable reference, + // the constructor function is really another function with the constructor // function inlined in it if (firstVftableReferenceAddress.compareTo(firstEndOfBlock) > 0) { @@ -6002,8 +5998,8 @@ public class RecoveredClassHelper { /** * Method to run the FillOutStructureCmd and return a FillOutStructureCmd object when - * a high variable used to run the cmd is found that stores the given firstVftableReference - * address. + * a high variable used to run the cmd is found that stores the given firstVftableReference + * address. * @param function the given function * @param firstVftableReference the first vftableReference in the given function * @return FillOutStructureCmd for the highVariable that stores the firstVftableReference address @@ -6019,7 +6015,7 @@ public class RecoveredClassHelper { return null; } - // get the decompiler highFunction + // get the decompiler highFunction HighFunction highFunction = decompilerUtils.getHighFunction(function); if (highFunction == null) { @@ -6028,7 +6024,7 @@ public class RecoveredClassHelper { List highVariables = new ArrayList(); - // if there are params add the first or the "this" param to the list to be checked first + // if there are params add the first or the "this" param to the list to be checked first // It is the most likely to store the vftablePtr if (highFunction.getFunctionPrototype().getNumParams() > 0) { @@ -6040,8 +6036,8 @@ public class RecoveredClassHelper { } // add the other high variables that store vftable pointer - highVariables.addAll( - getVariableThatStoresVftablePointer(highFunction, firstVftableReference)); + highVariables + .addAll(getVariableThatStoresVftablePointer(highFunction, firstVftableReference)); Iterator highVariableIterator = highVariables.iterator(); @@ -6070,10 +6066,10 @@ public class RecoveredClassHelper { } /** - * Method to figure out the indetermined inlined functions from each class as either combination - * constructor/inlined constructor or destrucor/inlined destructor. The method first uses any - * known called constructors or destructors to help determine which type then calls a method to - * determine, using vftable order, which class contains the constructor/destructor and which + * Method to figure out the indetermined inlined functions from each class as either combination + * constructor/inlined constructor or destrucor/inlined destructor. The method first uses any + * known called constructors or destructors to help determine which type then calls a method to + * determine, using vftable order, which class contains the constructor/destructor and which * contains the inlined constructor/destructor. * @param recoveredClasses List of classes * @throws CircularDependencyException if parent namespace is descendent of given namespace @@ -6100,7 +6096,7 @@ public class RecoveredClassHelper { Function inlineFunction = inlineIterator.next(); - // get the addresses in the function that refer to classes either by + // get the addresses in the function that refer to classes either by // referencing a vftable in a class or by calling a function in a class // TODO: add the atexit refs and then check them - make a map of atexit call to class map if not already Map referenceToClassMap = @@ -6108,7 +6104,7 @@ public class RecoveredClassHelper { List
      referencesToFunctions = extendedFlatAPI.getReferencesToFunctions(referenceToClassMap); - // if some of the references are to functions figure out if they are + // if some of the references are to functions figure out if they are // constructors destructors or add them to list of indetermined boolean isConstructor = false; boolean isDestructor = false; @@ -6159,7 +6155,7 @@ public class RecoveredClassHelper { else { // otherwise, use pcode info to figure out if inlined constructor or destructor - //If not already, make function a this call + //If not already, make function a this call makeFunctionThiscall(inlineFunction); List loads = getLoadPcodeOpPairs(inlineFunction); @@ -6214,7 +6210,7 @@ public class RecoveredClassHelper { referenceToIndeterminates); continue; } - // make the other referenced indeterminate c/d functions destructors + // make the other referenced indeterminate c/d functions destructors if (isConstructor == false && isDestructor == true) { createListedDestructorFunctions(referenceToClassMap, referenceToIndeterminates); @@ -6228,7 +6224,6 @@ public class RecoveredClassHelper { } } - /** * Method to retrieve the offset of the class data in the given structure * @param recoveredClass the given class @@ -6422,7 +6417,7 @@ public class RecoveredClassHelper { } } - // get the decompiler highFunction + // get the decompiler highFunction HighFunction highFunction = decompilerUtils.getHighFunction(function); if (highFunction == null) { @@ -6504,7 +6499,8 @@ public class RecoveredClassHelper { Address vftableAddress = vftableIterator.next(); // this gets the first function pointer in the vftable - Function firstVirtualFunction = extendedFlatAPI.getReferencedFunction(vftableAddress); + Function firstVirtualFunction = + extendedFlatAPI.getReferencedFunction(vftableAddress); processDeletingDestructor(recoveredClass, firstVirtualFunction); } } @@ -6514,7 +6510,7 @@ public class RecoveredClassHelper { * Find more deleting destructors by looking at other vfunctions to see if they call * their own cd and also call operator_delete * @param recoveredClasses List of RecoveredClass objects - * @throws CancelledException when script cancelled + * @throws CancelledException when script cancelled * @throws InvalidInputException if issues setting return type * @throws DuplicateNameException if try to create same symbol name already in namespace * @throws Exception if issues making label @@ -6550,7 +6546,8 @@ public class RecoveredClassHelper { monitor.checkCanceled(); Address vftableAddress = vftableAddressIterator.next(); - Function firstVirtualFunction = extendedFlatAPI.getReferencedFunction(vftableAddress); + Function firstVirtualFunction = + extendedFlatAPI.getReferencedFunction(vftableAddress); List virtualFunctions = recoveredClass.getVirtualFunctions(vftableAddress); @@ -6582,9 +6579,9 @@ public class RecoveredClassHelper { * 3. do not reference a vftable but call own destructor (call func on own c/d list) which * means it is just a deleting destructor for class but has no inlined destructor * @param recoveredClass the given class - * @param firstVftableFunction the first vftableFunction a class vftable + * @param firstVftableFunction the first vftableFunction a class vftable * @throws CancelledException if cancelled - * @throws DuplicateNameException if try to create same symbol name already in namespace + * @throws DuplicateNameException if try to create same symbol name already in namespace * @throws InvalidInputException if issues setting return type */ private void processDeletingDestructor(RecoveredClass recoveredClass, @@ -6592,7 +6589,7 @@ public class RecoveredClassHelper { throws CancelledException, DuplicateNameException, InvalidInputException { // if the first function on the vftable IS ALSO on the class constructor/destructor list - // then it is a deleting destructor with and inline destructor and we need to + // then it is a deleting destructor with and inline destructor and we need to // determine if the inline is the class or parent/grandparent class destructor if (getAllConstructorsAndDestructors().contains(firstVftableFunction)) { @@ -6709,9 +6706,9 @@ public class RecoveredClassHelper { /** * Use known ancestor class constructors and destructors to help classify indeterminate ones - * by who they call, ie constructors call parent (or grandparent) constructors and destructors - * call parent (or grandparent) destructors so use this to help figure out if the given class's - * indeterminate functions are constructors or destructors. + * by who they call, ie constructors call parent (or grandparent) constructors and destructors + * call parent (or grandparent) destructors so use this to help figure out if the given class's + * indeterminate functions are constructors or destructors. * @param recoveredClasses List of class objects * @throws CancelledException if cancelled * @throws CircularDependencyException if parent namespace is descendent of given namespace @@ -6779,10 +6776,10 @@ public class RecoveredClassHelper { /** * Method to classify indeterminate inline functions as either constructors or destructors - * using called ancestor information (may call parent or higher ancestor) or might be the same - * as a descendant constructor/destructor (ie a parent or ancestor is inlined into an - * indeterminate function so the same function is on both the parent inline list and the - * descendant regular list. + * using called ancestor information (may call parent or higher ancestor) or might be the same + * as a descendant constructor/destructor (ie a parent or ancestor is inlined into an + * indeterminate function so the same function is on both the parent inline list and the + * descendant regular list. * @param recoveredClasses list of classes * @throws CancelledException if cancelled * @throws CircularDependencyException if parent namespace is descendent of given namespace @@ -6814,7 +6811,7 @@ public class RecoveredClassHelper { monitor.checkCanceled(); Function indeterminateFunction = indeterminateIterator.next(); - // get the addresses in the function that refer to classes either by + // get the addresses in the function that refer to classes either by // referencing a vftable in a class or by calling a function in a class Map referenceToClassMap = getReferenceToClassMap(recoveredClass, indeterminateFunction); @@ -6874,8 +6871,8 @@ public class RecoveredClassHelper { } /** - * Method to find destructors using functions called by atexit. If they are on the list of - * indeterminate constructors or destructors and are called by atexit, then they are a + * Method to find destructors using functions called by atexit. If they are on the list of + * indeterminate constructors or destructors and are called by atexit, then they are a * destructor. * @param recoveredClasses list of classes * @throws CancelledException if cancelled @@ -6937,7 +6934,7 @@ public class RecoveredClassHelper { /** * Figure out which of the destructors that do not reference vftable are vbase destructors and - * which are destructors. + * which are destructors. * @param recoveredClasses List of RecoveredClass objects * @throws CancelledException if cancelled * @throws InvalidInputException if error setting return type @@ -7054,7 +7051,7 @@ public class RecoveredClassHelper { String fieldName = new String(); String comment = null; - // if the computed class struct has field name (ie from pdb) use it otherwise create one + // if the computed class struct has field name (ie from pdb) use it otherwise create one if (definedComponent.getFieldName() == null) { fieldName = "offset_" + extendedFlatAPI.toHexString(offset, false, true); } @@ -7077,9 +7074,8 @@ public class RecoveredClassHelper { return classDataStructure; } - /** - * Method to find the purecall function. + * Method to find the purecall function. * @param recoveredClasses List of RecoveredClass objects * @throws CancelledException when cancelled * @throws Exception when issue making label @@ -7128,7 +7124,7 @@ public class RecoveredClassHelper { if (possiblePureCall == null) { possiblePureCall = sameFunction; } - // if they ever don't match return + // if they ever don't match return else if (!possiblePureCall.equals(sameFunction)) { if (DEBUG) { Msg.debug(this, "Could not identify pure call. "); @@ -7192,12 +7188,11 @@ public class RecoveredClassHelper { return listOfUniqueAddresses; } - /** * Method to apply the function signature of the given function, if different, to the corresponding * function definition and call the method to update related structure fields and functions * @param vfunction the given function - * @param vftableAddress the vftable address of a vftable containing the function + * @param vftableAddress the vftable address of a vftable containing the function * @return a list of changed items * @throws CancelledException if cancelled * @throws DuplicateNameException if duplicate name exception while making changes @@ -7272,7 +7267,7 @@ public class RecoveredClassHelper { private DataTypeComponent getVfunctionComponent(Data vftableData, Function vfunction) throws CancelledException { - // get the Structure data type from the given Data + // get the Structure data type from the given Data Structure vfunctionStructure = getStructureFromData(vftableData); if (vfunctionStructure == null) { throw new IllegalArgumentException( @@ -7298,10 +7293,10 @@ public class RecoveredClassHelper { } /** - * Return a list of data items that have labels containing "vftable" and refer to the given + * Return a list of data items that have labels containing "vftable" and refer to the given * function * @param function the given function - * @return a list of vftable data addresses that contain references to the given function and + * @return a list of vftable data addresses that contain references to the given function and * have label containing text "vftable" * @throws CancelledException if cancelled */ @@ -7368,20 +7363,18 @@ public class RecoveredClassHelper { * @throws CancelledException if cancelled */ private List updateCorrespondingFunctionDefinition(Function vfunction, - DataTypeComponent structureComponent) - throws DuplicateNameException, DataTypeDependencyException, InvalidInputException, - CancelledException { + DataTypeComponent structureComponent) throws DuplicateNameException, + DataTypeDependencyException, InvalidInputException, CancelledException { List changedItems = new ArrayList(); - // skip purecalls - do not want to update function defs or other related sigs + // skip purecalls - do not want to update function defs or other related sigs if (vfunction.getName().contains("purecall")) { return null; } FunctionSignature listingFunctionSignature = vfunction.getSignature(true); - FunctionDefinition componentFunctionDefinition = getComponentFunctionDefinition(structureComponent); if (componentFunctionDefinition == null) { @@ -7429,7 +7422,6 @@ public class RecoveredClassHelper { return componentFunctionDefinition; } - /** * Method to edit the function definition pointed to by the given structure component with any * differences in the given function definition @@ -7440,7 +7432,6 @@ public class RecoveredClassHelper { private FunctionDefinition editFunctionDefinition(DataTypeComponent structureComponent, FunctionDefinition newFunctionDefinition) { - DataType componentDataType = structureComponent.getDataType(); if (!(componentDataType instanceof Pointer)) { throw new IllegalArgumentException("Structure component must be a pointer " + @@ -7474,7 +7465,6 @@ public class RecoveredClassHelper { return null; } - /** * Method to update the given function definition with the new function definition * @param functionDefinition the given function definition @@ -7502,7 +7492,7 @@ public class RecoveredClassHelper { // only update if there are differences and the func sigs have same length // if they don't have same length then something was possibly overridden in a child and - // it needs to stay the same as it was except the name + // it needs to stay the same as it was except the name if (!currentArgs.equals(changedArgs) && (currentArgs.length == changedArgs.length)) { ParameterDefinition[] newArgs = new ParameterDefinition[currentArgs.length]; for (int i = 0; i < currentArgs.length; i++) { @@ -7532,7 +7522,6 @@ public class RecoveredClassHelper { } - public List getClassStructures() throws CancelledException { Category category = program.getDataTypeManager().getCategory(classDataTypesCategoryPath); @@ -7629,22 +7618,20 @@ public class RecoveredClassHelper { return functions; } - - /** * Method to apply the given function definition to the corresponding function signatures that - * do not match and also update the vftable structure fields to have the correct name if + * do not match and also update the vftable structure fields to have the correct name if * different from the changed one. * @param functionDefinition the new function definition to apply - * @return a list of items changed + * @return a list of items changed * @throws CancelledException if cancelled * @throws DuplicateNameException if any changes cause duplicate name exceptions * @throws DataTypeDependencyException if any changes cause data type dependency issues * @throws InvalidInputException if there are invalid inputs when performing changes */ public List applyNewFunctionDefinition(FunctionDefinition functionDefinition) - throws CancelledException, DuplicateNameException, - DataTypeDependencyException, InvalidInputException { + throws CancelledException, DuplicateNameException, DataTypeDependencyException, + InvalidInputException { List changedItems = new ArrayList(); @@ -7739,12 +7726,12 @@ public class RecoveredClassHelper { if (component.getDataType().equals(pointerToVfunction)) { return i; } - } + } return vfunctionIndex; } /** - * Method to get the data type in the same folder as the given data type that is the pointer to + * Method to get the data type in the same folder as the given data type that is the pointer to * the given data type. This is getting an existing pointer not trying to create a new one. * @param dataType the given data type * @return the existing pointer data type to the given data type in the same class dt folder @@ -7845,7 +7832,7 @@ public class RecoveredClassHelper { String classNameWithNamespace = classNamespace.getName(true); - // Create Data Type Manager Category for given class + // Create Data Type Manager Category for given class CategoryPath classPath = extendedFlatAPI .createDataTypeCategoryPath(classDataTypesCategoryPath, classNameWithNamespace); @@ -7853,8 +7840,8 @@ public class RecoveredClassHelper { } /** - * Method to get the class Namespace corresponding to the given data type. NOTE: The data type - * must be in the DTM_CLASS_DATA_FOLDER_NAME folder in the data type manager. + * Method to get the class Namespace corresponding to the given data type. NOTE: The data type + * must be in the DTM_CLASS_DATA_FOLDER_NAME folder in the data type manager. * @param dataType the given data type * @return the class Namespace corresponding to the given data type * @throws CancelledException if cancelled @@ -7905,9 +7892,9 @@ public class RecoveredClassHelper { * @throws CancelledException if cancelled */ private Object updateListingVfunctionSignature(Data listingVftable, - DataTypeComponent structureComponent, - Address vftableAddress) throws DuplicateNameException, DataTypeDependencyException, - InvalidInputException, CancelledException { + DataTypeComponent structureComponent, Address vftableAddress) + throws DuplicateNameException, DataTypeDependencyException, InvalidInputException, + CancelledException { int numVfunctions = listingVftable.getNumComponents(); @@ -7966,7 +7953,7 @@ public class RecoveredClassHelper { } /** - * Method to update the given function's signature with the given function definition + * Method to update the given function's signature with the given function definition * @param function the given function * @param newFunctionDefinition the new function definition * @return true if the update worked, false otherwise @@ -7999,7 +7986,7 @@ public class RecoveredClassHelper { /** * Method to compare the given FunctionDefinition data type with the given FunctionSignature to - * see if they are equivalent (ie same name, same return type, same param types, same param names, + * see if they are equivalent (ie same name, same return type, same param types, same param names, * same calling convention, same hasVarArgs flag ... * @param definition the given FunctionDefinition data type * @param signature the given FunctionSignature @@ -8031,8 +8018,7 @@ public class RecoveredClassHelper { } private List updateComponentFieldName(DataTypeComponent structureComponent, - FunctionDefinition newFunctionSignature) - throws DuplicateNameException { + FunctionDefinition newFunctionSignature) throws DuplicateNameException { List changedItems = new ArrayList(); if (!structureComponent.getFieldName().equals(newFunctionSignature.getName())) { @@ -8044,8 +8030,7 @@ public class RecoveredClassHelper { return changedItems; } - public List getClassVftableSymbols(Namespace classNamespace) - throws CancelledException { + public List getClassVftableSymbols(Namespace classNamespace) throws CancelledException { List vftableSymbols = new ArrayList(); @@ -8123,15 +8108,15 @@ public class RecoveredClassHelper { return classStructureDataType; } - // If the map is not complete, the class structure will contain incomplete information so + // If the map is not complete, the class structure will contain incomplete information so // put out a debug message to indicate this issue if (!isClassOffsetToVftableMapComplete(recoveredClass)) { Msg.debug(this, "class vftable offset map for " + recoveredClass.getName() + " is not complete"); } - // iterate over the set of offsets to vftables and either add to undefined area or overwrite - // the parent class structures with the class vftable pointer then replace the rest of the + // iterate over the set of offsets to vftables and either add to undefined area or overwrite + // the parent class structures with the class vftable pointer then replace the rest of the // parent structure with its internal components for (Integer offset : classVftableOffsets) { monitor.checkCanceled(); @@ -8146,7 +8131,6 @@ public class RecoveredClassHelper { continue; } - // loop until the vftable pointer is added // if there is room and the component offset is not a structure, replace with vftablePtr // otherwise, within the range from top of containing component to the end of where the @@ -8165,7 +8149,7 @@ public class RecoveredClassHelper { } // if the component at the offset is the start of the component, and the component - // isn't a structure and if the component is big enough to be replaced, or there + // isn't a structure and if the component is big enough to be replaced, or there // are enough undefined that can be replaced then replace DataTypeComponent componentAt = classStructureDataType.getComponentAt(vftableOffset); @@ -8183,7 +8167,7 @@ public class RecoveredClassHelper { break; } - // otherwise if in middle of a containing dt then loop until all structs in + // otherwise if in middle of a containing dt then loop until all structs in // range are expanded and other items are cleared DataTypeComponent currentComponent = classStructureDataType.getComponentContaining(vftableOffset); @@ -8193,7 +8177,7 @@ public class RecoveredClassHelper { while (currentComponent != null && currentOffset < endOffset) { int nextOffset = currentComponent.getEndOffset() + 1; - // if there is a structure at the offset, replace it with its pieces + // if there is a structure at the offset, replace it with its pieces if (currentComponent.getDataType() instanceof Structure) { DataType currentDT = currentComponent.getDataType(); Structure internalStruct = (Structure) currentDT; @@ -8260,7 +8244,7 @@ public class RecoveredClassHelper { DataType vbaseStructPointer = dataTypeManager.getPointer(vbtableStructure); - // if it fits at offset or is at the end and class structure can be grown, + // if it fits at offset or is at the end and class structure can be grown, // copy the whole baseClass structure to the class Structure at the given offset boolean addedToStructure = EditStructureUtils.addDataTypeToStructure( classStructureDataType, vbtableOffset, vbaseStructPointer, VBTABLE_PTR, monitor); diff --git a/Ghidra/Features/Decompiler/src/test.slow/java/ghidra/app/plugin/core/decompile/AbstractDecompilerFindReferencesActionTest.java b/Ghidra/Features/Decompiler/src/test.slow/java/ghidra/app/plugin/core/decompile/AbstractDecompilerFindReferencesActionTest.java index d2fc442876..c403d5f9d7 100644 --- a/Ghidra/Features/Decompiler/src/test.slow/java/ghidra/app/plugin/core/decompile/AbstractDecompilerFindReferencesActionTest.java +++ b/Ghidra/Features/Decompiler/src/test.slow/java/ghidra/app/plugin/core/decompile/AbstractDecompilerFindReferencesActionTest.java @@ -31,8 +31,7 @@ import ghidra.app.nav.Navigatable; import ghidra.app.plugin.core.decompile.actions.FindReferencesToSymbolAction; import ghidra.app.plugin.core.navigation.locationreferences.LocationReferencesProvider; import ghidra.app.plugin.core.navigation.locationreferences.LocationReferencesService; -import ghidra.app.services.DataTypeReference; -import ghidra.app.services.DataTypeReferenceFinder; +import ghidra.app.services.*; import ghidra.program.model.data.DataType; import ghidra.program.model.listing.Program; import ghidra.program.util.ProgramLocation; @@ -172,6 +171,19 @@ public abstract class AbstractDecompilerFindReferencesActionTest extends Abstrac compositeFieldReferencesCallCount.incrementAndGet(); } + @Mock + public void findReferences(Program p, FieldMatcher fieldMatcher, + Consumer callback, TaskMonitor monitor) { + + if (fieldMatcher.isIgnored()) { + // an empty field matcher signals a data type search + dataTypeReferencesCallCount.incrementAndGet(); + } + else { + compositeFieldReferencesCallCount.incrementAndGet(); + } + } + public int getFindDataTypeReferencesCallCount() { return dataTypeReferencesCallCount.get(); } diff --git a/Ghidra/Features/Decompiler/src/test.slow/java/ghidra/app/plugin/core/decompile/DecompilerFindReferencesToActionTest.java b/Ghidra/Features/Decompiler/src/test.slow/java/ghidra/app/plugin/core/decompile/DecompilerFindReferencesToActionTest.java index 9c9e905522..0aae50fff7 100644 --- a/Ghidra/Features/Decompiler/src/test.slow/java/ghidra/app/plugin/core/decompile/DecompilerFindReferencesToActionTest.java +++ b/Ghidra/Features/Decompiler/src/test.slow/java/ghidra/app/plugin/core/decompile/DecompilerFindReferencesToActionTest.java @@ -31,9 +31,9 @@ public class DecompilerFindReferencesToActionTest public void testActionEnablement() throws Exception { /* - + Decomp of 'init_string': - + 1| 2| void init_string(mystring *ptr) 3| @@ -42,17 +42,17 @@ public class DecompilerFindReferencesToActionTest 6| return; 7| } 8| - + Note: there are two places in this function we can search for data type references: 1) the parameter (line 2, cols 17-25, 26-30) 2) the usage of the parameter: (line 5 at cols 2-5 for the type and cols 7-12 for the field) - + */ decompile(INIT_STRING_ADDR); // // Action should not enabled unless on the data type - // + // // Empty line int line = 1; int charPosition = 0; @@ -99,9 +99,9 @@ public class DecompilerFindReferencesToActionTest @Test public void testFindDataTypeReferences_ToEntireDataType_FromParameter() throws Exception { /* - + Decomp of 'init_string': - + 1| 2| void init_string(mystring *ptr) 3| @@ -110,11 +110,11 @@ public class DecompilerFindReferencesToActionTest 6| return; 7| } 8| - + Note: there are two places in this function we can search for data type references: 1) the parameter (line 2, cols 17-25, 26-30) 2) the usage of the parameter: (line 5 at cols 2-5 for the type and cols 7-12 for the field) - + */ decompile(INIT_STRING_ADDR); @@ -130,9 +130,9 @@ public class DecompilerFindReferencesToActionTest @Test public void testFindDataTypeReferences_ToEntireDataType_FromVariable() throws Exception { /* - + Decomp of 'init_string': - + 1| 2| void init_string(mystring *ptr) 3| @@ -141,11 +141,11 @@ public class DecompilerFindReferencesToActionTest 6| return; 7| } 8| - + Note: there are two places in this function we can search for data type references: 1) the parameter (line 2, cols 17-25, 26-30) 2) the usage of the parameter: (line 5 at cols 2-5 for the type and cols 7-12 for the field) - + */ decompile(INIT_STRING_ADDR); @@ -161,9 +161,9 @@ public class DecompilerFindReferencesToActionTest @Test public void testFindDataTypeReferences_ToFieldOfDataType() throws Exception { /* - + Decomp of 'init_string': - + 1| 2| void init_string(mystring *ptr) 3| @@ -172,11 +172,11 @@ public class DecompilerFindReferencesToActionTest 6| return; 7| } 8| - + Note: there are two places in this function we can search for data type references: 1) the parameter (line 2, cols 17-25, 26-30) 2) the usage of the parameter: (line 5 at cols 2-5 for the type and cols 7-12 for the field) - + */ decompile(INIT_STRING_ADDR); @@ -209,9 +209,9 @@ public class DecompilerFindReferencesToActionTest public void testFindDataTypeReferences_ToCurrentAddress() throws Exception { /* - + Decomp of 'init_string': - + 1| 2| void init_string(mystring *ptr) 3| @@ -247,6 +247,6 @@ public class DecompilerFindReferencesToActionTest //================================================================================================== // Private Methods -//================================================================================================== +//================================================================================================== } diff --git a/Ghidra/Features/Decompiler/src/test.slow/java/ghidra/app/plugin/core/decompile/StubDataTypeReferenceFinder.java b/Ghidra/Features/Decompiler/src/test.slow/java/ghidra/app/plugin/core/decompile/StubDataTypeReferenceFinder.java index b12f2f4fed..5567847ba5 100644 --- a/Ghidra/Features/Decompiler/src/test.slow/java/ghidra/app/plugin/core/decompile/StubDataTypeReferenceFinder.java +++ b/Ghidra/Features/Decompiler/src/test.slow/java/ghidra/app/plugin/core/decompile/StubDataTypeReferenceFinder.java @@ -17,8 +17,7 @@ package ghidra.app.plugin.core.decompile; import java.util.function.Consumer; -import ghidra.app.services.DataTypeReference; -import ghidra.app.services.DataTypeReferenceFinder; +import ghidra.app.services.*; import ghidra.program.model.data.DataType; import ghidra.program.model.listing.Program; import ghidra.util.exception.CancelledException; @@ -40,4 +39,10 @@ public class StubDataTypeReferenceFinder implements DataTypeReferenceFinder { Consumer callback, TaskMonitor monitor) throws CancelledException { // stub } + + @Override + public void findReferences(Program program, FieldMatcher fieldMatcher, + Consumer callback, TaskMonitor monitor) throws CancelledException { + // stub + } } diff --git a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/extension/datatype/finder/AnonymousVariableAccessDR.java b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/extension/datatype/finder/AnonymousVariableAccessDR.java index c7da04ea26..4584a292c9 100644 --- a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/extension/datatype/finder/AnonymousVariableAccessDR.java +++ b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/extension/datatype/finder/AnonymousVariableAccessDR.java @@ -20,30 +20,30 @@ import java.util.List; import ghidra.app.decompiler.ClangFieldToken; import ghidra.app.decompiler.ClangLine; import ghidra.app.services.DataTypeReference; +import ghidra.app.services.FieldMatcher; import ghidra.program.model.data.Composite; import ghidra.program.model.data.DataType; /** - * This class represents the use of a field of a {@link Composite} data type where there is - * no variable in the Decompiler for that data type. A normal variable access in the - * Decompiler may look like so: + * This class represents the use of a field of a {@link Composite} data type where there is no + * variable in the Decompiler for that data type. A normal variable access in the Decompiler + * may look like so: *
        * 	Foo f;
        * 	...
        * 	return f.some_field;
        * 
      - * + * * Alternatively, an anonymous variable access would look like this: *
        *	Bar b;
        * 	...
        *	return b->foo_array[1].some_field;
        * 
      - * - * In this case, foo_array[1] is a Foo, whose - * some_field is - * being accessed anonymously, since there is no variable of Foo declared - * in the current function. + * + * In this case, foo_array[1] is a Foo, whose + * some_field is being accessed anonymously, since there is no variable of + * Foo declared in the current function. */ public class AnonymousVariableAccessDR extends VariableAccessDR { @@ -52,42 +52,57 @@ public class AnonymousVariableAccessDR extends VariableAccessDR { } @Override - public void accumulateMatches(DataType dt, String fieldName, List results) { + public void accumulateMatches(DataType dt, FieldMatcher fieldMatcher, + List results) { // - // This class is backed by a ClangFieldToken. That class's data type is the composite - // that contains the field being accessed. A variable being accessed has 2 types being + // This class is backed by a ClangFieldToken. That class's data type is the composite that + // contains the field being accessed. A variable being accessed has 2 types being // touched: the aforementioned composite and the type of the field itself. // // This can match in one of two cases: - // 1) the client seeks to match a given field inside of the containing composite, or - // 2) the client seeks to match only the type, which means that the field type itself must match + // 1) the passed in type must match the field type and not the parent type, or + // 2) the passed in type must match the parent type, along with supplied field name/offset. // ClangFieldToken field = (ClangFieldToken) sourceToken; DataType compositeType = field.getDataType(); DataType fieldDt = DecompilerReference.getFieldDataType(field); - boolean matchesComposite = isEqual(dt, compositeType); - boolean matchesField = isEqual(dt, fieldDt); - boolean noMatch = !(matchesComposite || matchesField); + boolean matchesCompositeType = isEqual(dt, compositeType); + boolean matchesFieldType = isEqual(dt, fieldDt); + boolean noMatch = !(matchesCompositeType || matchesFieldType); if (noMatch) { return; } - if (fieldName == null) { - // case 2; no field name to check - if (matchesField) { + // + // Case 1 + // + // If the client did not specify a field to match, then we only want to match on the type + // of this reference's field type and NOT the composite type, since this reference is + // referring to the field and not the composite. + // + if (fieldMatcher.isIgnored()) { + if (matchesFieldType) { + // no field name and the search type matches this reference's field type results.add(createReference(variable)); } + // else there is no field and the search type does not match the reference's type return; } - // case 1; check the field name and the composite type - if (matchesComposite && field.getText().equals(fieldName)) { - results.add( - new DataTypeReference(compositeType, fieldName, getFunction(), getAddress(), - getContext())); + // + // Case 2 + // + // The client has requested a particular field of the parent composite. We only have a + // match if the parent type matches and the field name/offset matches. + // + String text = field.getText(); + int offset = field.getOffset(); + if (matchesCompositeType && fieldMatcher.matches(text, offset)) { + results.add(new DataTypeReference(compositeType, fieldMatcher.getFieldName(), + getFunction(), getAddress(), getContext())); } } } diff --git a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/extension/datatype/finder/DecompilerDataTypeReferenceFinder.java b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/extension/datatype/finder/DecompilerDataTypeReferenceFinder.java index 6ab7e20900..9a51588018 100644 --- a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/extension/datatype/finder/DecompilerDataTypeReferenceFinder.java +++ b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/extension/datatype/finder/DecompilerDataTypeReferenceFinder.java @@ -28,8 +28,7 @@ import ghidra.app.decompiler.parallel.*; import ghidra.app.plugin.core.datamgr.util.DataTypeUtils; import ghidra.app.plugin.core.navigation.locationreferences.LocationReference; import ghidra.app.plugin.core.navigation.locationreferences.ReferenceUtils; -import ghidra.app.services.DataTypeReference; -import ghidra.app.services.DataTypeReferenceFinder; +import ghidra.app.services.*; import ghidra.program.model.address.Address; import ghidra.program.model.data.BuiltInDataType; import ghidra.program.model.data.DataType; @@ -58,10 +57,28 @@ public class DecompilerDataTypeReferenceFinder implements DataTypeReferenceFinde @Override public void findReferences(Program program, DataType dataType, String fieldName, - Consumer callback, TaskMonitor monitor) throws CancelledException { + Consumer consumer, TaskMonitor monitor) throws CancelledException { + FieldMatcher fieldMatcher = new FieldMatcher(dataType, fieldName); DecompilerDataTypeFinderQCallback qCallback = - new DecompilerDataTypeFinderQCallback(program, dataType, fieldName, callback); + new DecompilerDataTypeFinderQCallback(program, dataType, fieldMatcher, consumer); + doFindReferences(program, dataType, qCallback, consumer, monitor); + } + + @Override + public void findReferences(Program program, FieldMatcher fieldMatcher, + Consumer consumer, TaskMonitor monitor) throws CancelledException { + + DataType dataType = fieldMatcher.getDataType(); + DecompilerDataTypeFinderQCallback qCallback = + new DecompilerDataTypeFinderQCallback(program, dataType, fieldMatcher, consumer); + + doFindReferences(program, dataType, qCallback, consumer, monitor); + } + + private void doFindReferences(Program program, DataType dataType, + DecompilerDataTypeFinderQCallback qCallback, Consumer consumer, + TaskMonitor monitor) throws CancelledException { Set functions = filterFunctions(program, dataType, monitor); @@ -87,7 +104,7 @@ public class DecompilerDataTypeReferenceFinder implements DataTypeReferenceFinde buildTypeLineage(dt, types); Set results = new HashSet<>(); - accumulateFunctionCallsToDefinedData(program, types, results, monitor); + accumulateFunctionCallsToDefinedData(program, dt, types, results, monitor); Listing listing = program.getListing(); FunctionIterator it = listing.getFunctions(true); @@ -115,21 +132,23 @@ public class DecompilerDataTypeReferenceFinder implements DataTypeReferenceFinde return results; } - private void accumulateFunctionCallsToDefinedData(Program program, Set potentialTypes, - Set results, TaskMonitor monitor) throws CancelledException { + private void accumulateFunctionCallsToDefinedData(Program program, DataType dataType, + Set potentialTypes, Set results, TaskMonitor monitor) + throws CancelledException { Listing listing = program.getListing(); AtomicInteger counter = new AtomicInteger(); SetAccumulator accumulator = new SetAccumulator<>(); Predicate dataMatcher = data -> { counter.incrementAndGet(); - DataType dataType = data.getDataType(); - boolean matches = potentialTypes.contains(dataType); + DataType dt = data.getDataType(); + boolean matches = potentialTypes.contains(dt); return matches; }; - ReferenceUtils.findDataTypeMatchesInDefinedData(accumulator, program, dataMatcher, null, - monitor); + FieldMatcher emptyMatcher = new FieldMatcher(dataType); + ReferenceUtils.findDataTypeMatchesInDefinedData(accumulator, program, dataMatcher, + emptyMatcher, monitor); for (LocationReference ref : accumulator) { Address address = ref.getLocationOfUse(); @@ -158,12 +177,10 @@ public class DecompilerDataTypeReferenceFinder implements DataTypeReferenceFinde // We have a different type, should we search for it? if (baseType instanceof BuiltInDataType) { - // When given a wrapper type (e.g., typedef) , ignore - // built-ins (e.g., int, byte, etc), as - // they will be of little value due to their volume in the program and the - // user *probably* did not intend to search for them. (Below we do not do - // this check, which allows the user to search directly for a - // built-in type, if they wish.) + // When given a wrapper type (e.g., typedef) , ignore built-ins (e.g., int, byte, etc), + // as they will be of little value due to their volume in the program and the user + // *probably* did not intend to search for them. (Below we do not do this check, which + // allows the user to search directly for a built-in type, if they wish.) return; } @@ -207,16 +224,16 @@ public class DecompilerDataTypeReferenceFinder implements DataTypeReferenceFinde private Consumer callback; private DataType dataType; - private String fieldName; + private FieldMatcher fieldMatcher; /* Search for composite field access */ - DecompilerDataTypeFinderQCallback(Program program, DataType dataType, String fieldName, - Consumer callback) { + DecompilerDataTypeFinderQCallback(Program program, DataType dataType, + FieldMatcher fieldMatcher, Consumer callback) { super(program, new DecompilerConfigurer()); this.dataType = dataType; - this.fieldName = fieldName; + this.fieldMatcher = fieldMatcher; this.callback = callback; } @@ -230,7 +247,7 @@ public class DecompilerDataTypeReferenceFinder implements DataTypeReferenceFinde } DecompilerDataTypeFinder finder = - new DecompilerDataTypeFinder(results, function, dataType, fieldName); + new DecompilerDataTypeFinder(results, function, dataType, fieldMatcher); List refs = finder.findUsage(); refs.forEach(r -> callback.accept(r)); @@ -263,14 +280,14 @@ public class DecompilerDataTypeReferenceFinder implements DataTypeReferenceFinde private DecompileResults decompilation; private Function function; private DataType dataType; - private String fieldName; + private FieldMatcher fieldMatcher; DecompilerDataTypeFinder(DecompileResults results, Function function, DataType dataType, - String fieldName) { + FieldMatcher fieldMatcher) { this.decompilation = results; this.function = function; this.dataType = dataType; - this.fieldName = fieldName; + this.fieldMatcher = fieldMatcher; } List findUsage() { @@ -300,7 +317,7 @@ public class DecompilerDataTypeReferenceFinder implements DataTypeReferenceFinde /** Finds any search input match in the given reference */ private void matchUsage(DecompilerReference reference, List results) { - reference.accumulateMatches(dataType, fieldName, results); + reference.accumulateMatches(dataType, fieldMatcher, results); } private List findVariableReferences(ClangTokenGroup tokens) { @@ -320,20 +337,20 @@ public class DecompilerDataTypeReferenceFinder implements DataTypeReferenceFinde * the case with Composite types, may have one of its fields accessed. Each result * found by this method will be at least a variable access and may also itself have * field accesses. - * + * *

      Sometimes a line is structured such that there are anonymous variable accesses. This * is the case where a Composite is being accessed, but the Composite itself is * not a variable in the current function. See {@link AnonymousVariableAccessDR} for * more details. - * + * * @param line the current line being processed from the Decompiler * @param results the accumulator into which matches will be placed */ private void findVariablesInLine(ClangLine line, List results) { List allTokens = line.getAllTokens(); - Iterable filteredTokens = IterableUtils.filteredIterable(allTokens, - token -> { + Iterable filteredTokens = + IterableUtils.filteredIterable(allTokens, token -> { // Only include desirable tokens (this is really just for easier debugging). // Update this filter if the loop below ever needs other types of tokens. return (token instanceof ClangTypeToken) || diff --git a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/extension/datatype/finder/DecompilerFieldAccess.java b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/extension/datatype/finder/DecompilerFieldAccess.java index cb52ff9711..754ebbbe8b 100644 --- a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/extension/datatype/finder/DecompilerFieldAccess.java +++ b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/extension/datatype/finder/DecompilerFieldAccess.java @@ -23,7 +23,7 @@ import ghidra.program.model.data.*; import ghidra.util.Msg; /** - * A class that represents access to a Decompiler {@link ClangFieldToken} object. This is the field + * A class that represents access to a Decompiler {@link ClangFieldToken} object. This is the field * of a variable, denoted by {@link ClangVariableToken}. */ public class DecompilerFieldAccess extends DecompilerVariable { @@ -73,6 +73,12 @@ public class DecompilerFieldAccess extends DecompilerVariable { return dt; } + @Override + public int getOffset() { + ClangFieldToken field = (ClangFieldToken) variable; + return field.getOffset(); + } + protected DataType getBaseType(DataType dt) { if (dt instanceof Array) { return getBaseType(((Array) dt).getDataType()); diff --git a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/extension/datatype/finder/DecompilerReference.java b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/extension/datatype/finder/DecompilerReference.java index 6af5431569..eb30d9d0d2 100644 --- a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/extension/datatype/finder/DecompilerReference.java +++ b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/extension/datatype/finder/DecompilerReference.java @@ -21,6 +21,7 @@ import ghidra.app.decompiler.*; import ghidra.app.plugin.core.navigation.locationreferences.LocationReferenceContext; import ghidra.app.plugin.core.navigation.locationreferences.LocationReferenceContextBuilder; import ghidra.app.services.DataTypeReference; +import ghidra.app.services.FieldMatcher; import ghidra.program.model.address.Address; import ghidra.program.model.data.*; import ghidra.program.model.listing.Function; @@ -51,12 +52,12 @@ public abstract class DecompilerReference { * The fieldName is optional. If not included, then only data type matches will * be sought. If it is included, then a match is only included when it is a reference * to the given data type where that type is being accessed by the given field name. - * + * * @param dt the data type to find - * @param fieldName the optional field name used to restrict matches. + * @param fieldMatcher the optional field matcher used to restrict matches. * @param results the accumulator object into which will be placed any matches */ - public abstract void accumulateMatches(DataType dt, String fieldName, + public abstract void accumulateMatches(DataType dt, FieldMatcher fieldMatcher, List results); public DecompilerVariable getVariable() { @@ -160,7 +161,7 @@ public abstract class DecompilerReference { int offset = field.getOffset(); int n = parent.getLength(); if (offset >= 0 && offset < n) { - DataTypeComponent dtc = parent.getComponentAt(field.getOffset()); + DataTypeComponent dtc = parent.getComponentContaining(field.getOffset()); fieldDt = dtc.getDataType(); } } diff --git a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/extension/datatype/finder/DecompilerVariable.java b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/extension/datatype/finder/DecompilerVariable.java index c5787b490b..90331b4ca3 100644 --- a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/extension/datatype/finder/DecompilerVariable.java +++ b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/extension/datatype/finder/DecompilerVariable.java @@ -188,6 +188,10 @@ public abstract class DecompilerVariable { return text; } + public int getOffset() { + return Integer.MIN_VALUE; // subclasses can override + } + @Override public String toString() { String castString = casts.isEmpty() ? "" : "\tcasts: " + casts + ",\n"; diff --git a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/extension/datatype/finder/ReturnTypeDR.java b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/extension/datatype/finder/ReturnTypeDR.java index b69b2e9db0..76d10a95c9 100644 --- a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/extension/datatype/finder/ReturnTypeDR.java +++ b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/extension/datatype/finder/ReturnTypeDR.java @@ -20,6 +20,7 @@ import java.util.List; import ghidra.app.decompiler.ClangLine; import ghidra.app.decompiler.ClangTypeToken; import ghidra.app.services.DataTypeReference; +import ghidra.app.services.FieldMatcher; import ghidra.program.model.data.DataType; public class ReturnTypeDR extends DecompilerReference { @@ -29,11 +30,11 @@ public class ReturnTypeDR extends DecompilerReference { } @Override - public void accumulateMatches(DataType dt, String fieldName, List results) { + public void accumulateMatches(DataType dt, FieldMatcher fieldMatcher, + List results) { - if (fieldName != null) { - // Return Types do not have any field usage - return; + if (!fieldMatcher.isIgnored()) { + return; // Return Types do not have any field usage } DataType myDt = getDataType(); diff --git a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/extension/datatype/finder/VariableAccessDR.java b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/extension/datatype/finder/VariableAccessDR.java index 0d64c2cac5..995aad974e 100644 --- a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/extension/datatype/finder/VariableAccessDR.java +++ b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/extension/datatype/finder/VariableAccessDR.java @@ -21,6 +21,7 @@ import java.util.List; import ghidra.app.decompiler.*; import ghidra.app.plugin.core.navigation.locationreferences.LocationReferenceContext; import ghidra.app.services.DataTypeReference; +import ghidra.app.services.FieldMatcher; import ghidra.program.model.address.Address; import ghidra.program.model.data.DataType; import ghidra.program.model.listing.Function; @@ -58,10 +59,11 @@ public class VariableAccessDR extends DecompilerReference { } @Override - public void accumulateMatches(DataType dt, String fieldName, List results) { + public void accumulateMatches(DataType dt, FieldMatcher fieldMatcher, + List results) { if (fields.isEmpty()) { - DecompilerVariable var = getMatch(dt, fieldName, variable, null); + DecompilerVariable var = getMatch(dt, fieldMatcher, variable, null); if (var != null) { DataTypeReference ref = createReference(var); results.add(ref); @@ -77,7 +79,7 @@ public class VariableAccessDR extends DecompilerReference { for (DecompilerVariable field : fields) { DecompilerVariable next = field; - DecompilerVariable var = getMatch(dt, fieldName, start, next); + DecompilerVariable var = getMatch(dt, fieldMatcher, start, next); if (var != null) { DataTypeReference ref = createReference(var, next); results.add(ref); @@ -87,46 +89,46 @@ public class VariableAccessDR extends DecompilerReference { } // - // Handle the last variable by itself (for the case where we are matching just on the - // type, with no field name) + // Handle the last variable by itself (for the case where we are matching just on the type, + // with no field name) // - if (fieldName != null) { + if (fieldMatcher.isIgnored()) { return; } - DecompilerVariable var = getMatch(dt, null, start, null); + DecompilerVariable var = getMatch(dt, fieldMatcher, start, null); if (var != null) { DataTypeReference ref = createReference(var); results.add(ref); } } - private DecompilerVariable getMatch(DataType dt, String fieldName, DecompilerVariable var, - DecompilerVariable potentialField) { + private DecompilerVariable getMatch(DataType dt, FieldMatcher fieldMatcher, + DecompilerVariable var, DecompilerVariable potentialField) { // Note: for now, I ignore the precedence of casting; if any cast type is a match, then // signal hooray - boolean searchForField = fieldName != null; + boolean searchForField = !fieldMatcher.isIgnored(); DecompilerVariable fieldVar = searchForField ? potentialField : null; DecompilerVariable match = getMatchingVarialbe(dt, var, fieldVar); if (match == null) { - // wrong type, nothing to do - return null; + return null; // wrong type, nothing to do } // Matches on the type, does the field match? - if (fieldName == null) { + if (fieldMatcher.isIgnored()) { return match; // no field to match } if (potentialField == null) { // check for the case where we have not been passed a 'potential field', but the given - // 'var' is itself the field we seek, such as in an if statement like this: + // 'var' is itself may be the field we seek, such as in an if statement like this: // if (color == RED) // where 'RED' is the variable we are checking String name = var.getName(); - if (fieldName.equals(name)) { + int offset = var.getOffset(); + if (fieldMatcher.matches(name, offset)) { return var; } @@ -134,7 +136,8 @@ public class VariableAccessDR extends DecompilerReference { } String name = potentialField.getName(); - if (fieldName.equals(name)) { + int offset = potentialField.getOffset(); + if (fieldMatcher.matches(name, offset)) { return match; } return null; @@ -156,13 +159,13 @@ public class VariableAccessDR extends DecompilerReference { // // Unusual Code Alert! - // It is a bit odd to check the field when you are looking for the type that contains - // the field. BUT, in the Decompiler, SOMETIMES the 'field' happens to have the - // data type of the thing that contains it. So, if you have: + // It is a bit odd to check the field when you are looking for the type that contains the + // field. BUT, in the Decompiler, SOMETIMES the 'field' happens to have the data type of + // the thing that contains it. So, if you have: // foo.bar - // then the 'bar' field will have a data type of Foo. Unfortunately, this is not - // always the case. For now, when the variable is global, we need to check the field - // Sad face emoji. + // then the 'bar' field will have a data type of Foo. Unfortunately, this is not always + // the case. For now, when the variable is global, we need to check the field. Sad face + // emoji. // HighVariable highVariable = var.variable.getHighVariable(); if (highVariable instanceof HighGlobal) { diff --git a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/extension/datatype/finder/VariableDR.java b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/extension/datatype/finder/VariableDR.java index 181fdd4c0e..ff6825dedd 100644 --- a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/extension/datatype/finder/VariableDR.java +++ b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/extension/datatype/finder/VariableDR.java @@ -20,6 +20,7 @@ import java.util.List; import ghidra.app.decompiler.*; import ghidra.app.plugin.core.navigation.locationreferences.LocationReferenceContext; import ghidra.app.services.DataTypeReference; +import ghidra.app.services.FieldMatcher; import ghidra.program.model.address.Address; import ghidra.program.model.data.DataType; import ghidra.program.model.listing.Function; @@ -52,7 +53,8 @@ public abstract class VariableDR extends DecompilerReference { } @Override - public void accumulateMatches(DataType dt, String fieldName, List results) { + public void accumulateMatches(DataType dt, FieldMatcher fieldMatcher, + List results) { if (variable == null) { // This implies our API was misused in that a variable was never set after creation @@ -61,21 +63,18 @@ public abstract class VariableDR extends DecompilerReference { DataType dataType = getDataType(); if (!isEqual(dataType, dt)) { - // wrong type, nothing to do - return; - } - - LocationReferenceContext context = getContext(); - Function function = getFunction(); - Address address = getAddress(); - if (fieldName == null) { - // no field to check, a match on the the type is good enough - results.add(new DataTypeReference(dataType, null, getFunction(), address, context)); - return; + return; // wrong type, nothing to do } String name = variable.getName(); - if (name.equals(fieldName)) { + int offset = variable.getOffset(); + if (fieldMatcher.matches(name, offset)) { + + // this will be null if the field matcher is empty + String fieldName = fieldMatcher.getFieldName(); + Function function = getFunction(); + Address address = getAddress(); + LocationReferenceContext context = getContext(); results.add(new DataTypeReference(dataType, fieldName, function, address, context)); } } diff --git a/Ghidra/Framework/Docking/src/main/java/docking/action/DockingAction.java b/Ghidra/Framework/Docking/src/main/java/docking/action/DockingAction.java index cc40f5a6c5..97c657dcd7 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/action/DockingAction.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/action/DockingAction.java @@ -41,10 +41,10 @@ import utilities.util.reflection.ReflectionUtilities; * the entire application. *

      * DockingActions can be invoked from the global menu, a popup menu, a toolbar, and/or a keybinding, - * depending on whether or not menuBarData, popupMenuData, toolBarData, and/or keyBindingData have + * depending on whether or not menuBarData, popupMenuData, toolBarData, and/or keyBindingData have * been set. *

      - * + * * Implementors of this class should override {@link #actionPerformed(ActionContext)}. * *

      @@ -199,12 +199,12 @@ public abstract class DockingAction implements DockingActionIf { *

      * If the client wants the action on all windows, then they can call {@link #shouldAddToAllWindows} *

      - * If the client wants the action to be on a window only when the window can produce - * a certain context type, the the client should call + * If the client wants the action to be on a window only when the window can produce + * a certain context type, the the client should call * {@link #addToWindowWhen(Class)} *

      * Otherwise, by default, the action will only be on the main window. - * + * */ @Override public final boolean shouldAddToWindow(boolean isMainWindow, Set> contextTypes) { @@ -212,7 +212,7 @@ public abstract class DockingAction implements DockingActionIf { if (menuBarData == null && toolBarData == null) { return false; } - + // clients can specify that the action should be on all windows. if (shouldAddToAllWindows) { return true; @@ -242,6 +242,14 @@ public abstract class DockingAction implements DockingActionIf { DockingWindowManager.getHelpService().registerHelp(this, location); } + /** + * Returns the help location for this action + * @return the help location for this action + */ + public HelpLocation getHelpLocation() { + return DockingWindowManager.getHelpService().getHelpLocation(this); + } + /** * Signals the the help system that this action does not need a help entry. Some actions * are so obvious that they do not require help, such as an action that renames a file. @@ -579,10 +587,10 @@ public abstract class DockingAction implements DockingActionIf { /** * Sets a predicate for dynamically determining the action's enabled state. If this * predicate is not set, the action's enable state must be controlled directly using the - * {@link DockingAction#setEnabled(boolean)} method. See + * {@link DockingAction#setEnabled(boolean)} method. See * {@link DockingActionIf#isEnabledForContext(ActionContext)} - * - * @param predicate the predicate that will be used to dynamically determine an action's + * + * @param predicate the predicate that will be used to dynamically determine an action's * enabled state. */ public void enabledWhen(Predicate predicate) { @@ -592,10 +600,10 @@ public abstract class DockingAction implements DockingActionIf { /** * Sets a predicate for dynamically determining if this action should be included in * an impending pop-up menu. If this predicate is not set, the action's will be included - * in an impending pop-up, if it is enabled. See + * in an impending pop-up, if it is enabled. See * {@link DockingActionIf#isAddToPopup(ActionContext)} - * - * @param predicate the predicate that will be used to dynamically determine an action's + * + * @param predicate the predicate that will be used to dynamically determine an action's * enabled state. */ public void popupWhen(Predicate predicate) { @@ -603,10 +611,10 @@ public abstract class DockingAction implements DockingActionIf { } /** - * Sets a predicate for dynamically determining if this action is valid for the current + * Sets a predicate for dynamically determining if this action is valid for the current * {@link ActionContext}. See {@link DockingActionIf#isValidContext(ActionContext)} - * - * @param predicate the predicate that will be used to dynamically determine an action's + * + * @param predicate the predicate that will be used to dynamically determine an action's * validity for a given {@link ActionContext} */ public void validContextWhen(Predicate predicate) { @@ -620,14 +628,14 @@ public abstract class DockingAction implements DockingActionIf { * that can produce an ActionContext that is appropriate for this action. *

      * @param contextClass the ActionContext class required to be producible by a - * provider that is hosted in that window before this action is added to that - * window. - * + * provider that is hosted in that window before this action is added to that + * window. + * */ public void addToWindowWhen(Class contextClass) { addToWindowWhenContextClass = contextClass; } - + /** * Tells this action to add itself to all windows *

      @@ -664,5 +672,5 @@ public abstract class DockingAction implements DockingActionIf { String classInfo = trace[0].toString(); return classInfo; } - + } diff --git a/Ghidra/Framework/Docking/src/main/java/docking/widgets/dialogs/InputWithChoicesDialog.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/dialogs/InputWithChoicesDialog.java index 73c967e021..384065103c 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/widgets/dialogs/InputWithChoicesDialog.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/dialogs/InputWithChoicesDialog.java @@ -27,8 +27,8 @@ import docking.widgets.label.GDLabel; import docking.widgets.label.GHtmlLabel; /** - * A dialog that has text fields to get user input. - * + * A dialog that has text fields to get user input. + * */ public class InputWithChoicesDialog extends DialogComponentProvider { @@ -39,12 +39,12 @@ public class InputWithChoicesDialog extends DialogComponentProvider { /** * Creates a provider for a generic input dialog with the specified title, * a label and a editable comboBox pre-populated with selectable values. The user - * can check the value of {@link #isCanceled()} to know whether or not + * can check the value of {@link #isCanceled()} to know whether or not * the user canceled the operation. To get the user selected value use the * {@link #getValue()} value(s) entered by the user. If the user cancelled the operation, then * null will be returned from getValue(). *

      - * + * * @param dialogTitle used as the name of the dialog's title bar * @param label value to use for the label of the text field * @param optionValues values to populate the combo box @@ -69,12 +69,12 @@ public class InputWithChoicesDialog extends DialogComponentProvider { /** * Creates a provider for a generic input dialog with the specified title, * a label and a editable comboBox pre-populated with selectable values. The user - * can check the value of {@link #isCanceled()} to know whether or not + * can check the value of {@link #isCanceled()} to know whether or not * the user canceled the operation. To get the user selected value use the * {@link #getValue()} value(s) entered by the user. If the user cancelled the operation, then * null will be returned from getValue(). *

      - * + * * @param dialogTitle used as the name of the dialog's title bar * @param label value to use for the label of the text field * @param optionValues values to populate the combo box @@ -180,6 +180,11 @@ public class InputWithChoicesDialog extends DialogComponentProvider { if (isCanceled) { return null; } + + if (allowEdits) { + return combo.getText(); + } + Object selectedItem = combo.getSelectedItem(); return selectedItem == null ? null : selectedItem.toString(); } @@ -188,7 +193,7 @@ public class InputWithChoicesDialog extends DialogComponentProvider { * Set the current choice to value. * @param value updated choice * @throws NoSuchElementException if choice does not permit edits and value is - * not a valid choice. + * not a valid choice. */ public void setValue(String value) { combo.setSelectedItem(value); diff --git a/Ghidra/Framework/Generic/src/main/java/ghidra/util/NumericUtilities.java b/Ghidra/Framework/Generic/src/main/java/ghidra/util/NumericUtilities.java index cbb6095eef..e216245985 100644 --- a/Ghidra/Framework/Generic/src/main/java/ghidra/util/NumericUtilities.java +++ b/Ghidra/Framework/Generic/src/main/java/ghidra/util/NumericUtilities.java @@ -57,17 +57,24 @@ public final class NumericUtilities { } /** - * parses the given string as a numeric value, detecting whether or not it begins with a Hex + * Parses the given string as a numeric value, detecting whether or not it begins with a Hex * prefix, and if not, parses as a long int value. + * @param numStr the number string + * @return the long value or 0 + * */ public static long parseNumber(String numStr) { - long value = 0; + return parseNumber(numStr, Long.valueOf(0)); + } + + public static Long parseNumber(String numStr, Long defaultValue) { numStr = (numStr == null ? "" : numStr.trim()); if (numStr.length() == 0) { - return value; + return defaultValue; } + long value = 0; try { if (numStr.startsWith(HEX_PREFIX_x) || numStr.startsWith(HEX_PREFIX_X)) { value = Integer.parseInt(numStr.substring(2), 16); @@ -78,6 +85,7 @@ public final class NumericUtilities { } catch (NumberFormatException exc) { // do nothing special; use default value + return defaultValue; } return value; @@ -192,7 +200,7 @@ public final class NumericUtilities { /** * returns the value of the specified long as hexadecimal, prefixing with the HEX_PREFIX_x * string. - * + * * @param value the long value to convert */ public final static String toHexString(long value) { @@ -202,7 +210,7 @@ public final class NumericUtilities { /** * returns the value of the specified long as hexadecimal, prefixing with the HEX_PREFIX_x * string. - * + * * @param value the long value to convert * @param size number of bytes to be represented */ @@ -216,7 +224,7 @@ public final class NumericUtilities { /** * returns the value of the specified long as signed hexadecimal, prefixing with the * HEX_PREFIX_x string. - * + * * @param value the long value to convert */ public final static String toSignedHexString(long value) { @@ -230,14 +238,14 @@ public final class NumericUtilities { } /** - * Converts a unsigned long value, which is currently stored in a + * Converts a unsigned long value, which is currently stored in a * java signed long, into a {@link BigInteger}. *

      - * In other words, the full 64 bits of the primitive java signed - * long is being used to store an unsigned value. This + * In other words, the full 64 bits of the primitive java signed + * long is being used to store an unsigned value. This * method converts this into a positive BigInteger value. - * - * @param value java unsigned long value stuffed into a + * + * @param value java unsigned long value stuffed into a * java signed long * @return new {@link BigInteger} with the positive value of the unsigned long value */ @@ -258,7 +266,7 @@ public final class NumericUtilities { /** * Get an unsigned aligned value corresponding to the specified unsigned value which will be * greater than or equal the specified value. - * + * * @param unsignedValue value to be aligned * @param alignment alignment * @return aligned value @@ -384,7 +392,7 @@ public final class NumericUtilities { * Philosophically, it is hexadecimal, but the only valid digits are 0 and F. Any * partially-included nibble will be broken down into bracketed bits. Displaying masks in this * way is convenient when shown proximal to related masked values. - * + * * @param msk the mask * @param n the number of nibbles, starting at the right * @param truncate true if leading Xs may be truncated @@ -441,7 +449,7 @@ public final class NumericUtilities { /** * The reverse of {@link #convertMaskedValueToHexString(long, long, int, boolean, int, String)} - * + * * @param msk an object to receive the resulting mask * @param val an object to receive the resulting value * @param hex the input string to parse @@ -669,7 +677,7 @@ public final class NumericUtilities { * -64h * * - * + * * @param number The number to represent * @param radix The base in which number is represented * @param mode Specifies how the number is formatted with respect to its signed-ness @@ -849,7 +857,7 @@ public final class NumericUtilities { /** * Determine if the provided Number is an integer type -- Byte, Short, Integer, or Long. - * + * * @param number the object to check for for integer-type * @return true if the provided number is an integer-type, false otherwise */ @@ -860,7 +868,7 @@ public final class NumericUtilities { /** * Determine if the provided Number class is an integer type. - * + * * @param numClass Class of an object * @return true if the class parameter is a integer type, false otherwise */ @@ -870,7 +878,7 @@ public final class NumericUtilities { /** * Determine if the provided Number is a floating-point type -- Float or Double. - * + * * @param number the object to check for for floating-point-type * @return true if the provided number is a floating-point-type, false otherwise */ @@ -881,7 +889,7 @@ public final class NumericUtilities { /** * Determine if the provided Number class is a floating-point type. - * + * * @param numClass Class of an object * @return true if the class parameter is a floating-point type, false otherwise */ @@ -895,7 +903,7 @@ public final class NumericUtilities { private static interface IntegerRadixRenderer { /** * Format the given number in the provided radix base. - * + * * @param number the number to render * @param radix the base in which to render * @return a string representing the provided number in the given base diff --git a/Ghidra/Framework/Generic/src/main/java/ghidra/util/datastruct/SortedRangeList.java b/Ghidra/Framework/Generic/src/main/java/ghidra/util/datastruct/SortedRangeList.java index 55d487f7e2..e132e159c3 100644 --- a/Ghidra/Framework/Generic/src/main/java/ghidra/util/datastruct/SortedRangeList.java +++ b/Ghidra/Framework/Generic/src/main/java/ghidra/util/datastruct/SortedRangeList.java @@ -18,9 +18,8 @@ package ghidra.util.datastruct; import java.util.*; /** - * Provides a list of integer ranges that are maintained in sorted order. - * When a range is added any ranges that overlap or are adjacent to one another - * will coalesce into a single range. + * Provides a list of integer ranges that are maintained in sorted order. When a range is added + * any ranges that overlap or are adjacent to one another will coalesce into a single range. */ public class SortedRangeList implements Iterable { TreeSet set; @@ -33,8 +32,7 @@ public class SortedRangeList implements Iterable { } /** - * Creates a new sorted range list with ranges equivalent to those in the - * specified list. + * Creates a new sorted range list with ranges equivalent to those in the specified list. * @param list the sorted range list to make an equivalent copy of. */ public SortedRangeList(SortedRangeList list) { @@ -47,9 +45,8 @@ public class SortedRangeList implements Iterable { } /** - * Adds the range from min to max to this sorted range list. - * If the range is adjacent to or overlaps any other existing ranges, - * then those ranges will coalesce. + * Adds the range from min to max to this sorted range list. If the range is adjacent to or + * overlaps any other existing ranges, then those ranges will coalesce. * @param min the range minimum * @param max the range maximum (inclusive) */ @@ -82,15 +79,18 @@ public class SortedRangeList implements Iterable { /** * Returns an iterator over all the ranges in this list. + * @return the iterator */ public Iterator getRanges() { return set.iterator(); } /** - * Returns an iterator over all the ranges in this list that iterates in the direction specified. - * @param forward true indicates to iterate forward from minimum to maximum range. - * false indicates backward iteration form maximum to minimum. + * Returns an iterator over all the ranges in this list that iterates in the direction + * specified. + * @param forward true indicates to iterate forward from minimum to maximum range; false + * indicates backward iteration form maximum to minimum. + * @return the iterator */ public Iterator getRanges(boolean forward) { if (forward) { @@ -106,6 +106,7 @@ public class SortedRangeList implements Iterable { /** * Returns the minimum int value in this sorted range list. + * @return the min value * @throws NoSuchElementException if the list is empty. */ public int getMin() throws NoSuchElementException { @@ -115,6 +116,7 @@ public class SortedRangeList implements Iterable { /** * Returns the maximum int value in this sorted range list. + * @return the max value * @throws NoSuchElementException if the list is empty. */ public int getMax() throws NoSuchElementException { @@ -124,13 +126,14 @@ public class SortedRangeList implements Iterable { /** * Returns the number of ranges in the list. + * @return the number of ranges */ public int getNumRanges() { return set.size(); } /** - * Removes the indicated range of values from the list. This will remove + * Removes the indicated range of values from the list. This will remove * any ranges or portion of ranges that overlap the indicated range. * @param min the minimum value for the range to remove. * @param max the maximum value for the range to remove. @@ -182,6 +185,7 @@ public class SortedRangeList implements Iterable { /** * Returns true if the value is contained in any ranges within this list. * @param value the value to check for. + * @return true if the value is contained in any ranges within this list. */ public boolean contains(int value) { Range key = new Range(value, value); @@ -217,6 +221,7 @@ public class SortedRangeList implements Iterable { * Returns true if a single range contains all the values from min to max. * @param min the minimum value * @param max the maximum value + * @return true if a single range contains all the values from min to max. */ public boolean contains(int min, int max) { Range range = getRangeContaining(min); @@ -284,9 +289,12 @@ public class SortedRangeList implements Iterable { } /** - * Returns true if the range from min to max intersects (overlaps) any ranges in this sorted range list. + * Returns true if the range from min to max intersects (overlaps) any ranges in this sorted + * range list. * @param min the range minimum value. - * @param max the range maximum value + * @param max the range maximum value. + * @return true if the range from min to max intersects (overlaps) any ranges in this sorted + * range list. */ public boolean intersects(int min, int max) { Range key = new Range(min, min); @@ -309,6 +317,7 @@ public class SortedRangeList implements Iterable { /** * Returns true if the range list is empty. + * @return true if the range list is empty. */ public boolean isEmpty() { return set.isEmpty(); @@ -327,7 +336,8 @@ public class SortedRangeList implements Iterable { } /** - * Creates a new SortedRangeList that is the intersection of this range list and the other range list specified. + * Creates a new SortedRangeList that is the intersection of this range list and the other + * range list specified. * @param other the other range list * @return the new SortedRangeList representing the intersection. */ @@ -339,12 +349,9 @@ public class SortedRangeList implements Iterable { return srl2; } - /* (non-Javadoc) - * @see java.lang.Object#toString() - */ @Override public String toString() { - StringBuffer buf = new StringBuffer(); + StringBuilder buf = new StringBuilder(); Iterator it = getRanges(); if (it.hasNext()) { Range r = it.next(); @@ -365,4 +372,28 @@ public class SortedRangeList implements Iterable { public void clear() { set.clear(); } + + @Override + public int hashCode() { + return Objects.hash(set); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + SortedRangeList other = (SortedRangeList) obj; + if (!Objects.equals(set, other.set)) { + return false; + } + return true; + } + } diff --git a/Ghidra/Framework/Help/src/main/java/help/validator/ReferenceTagProcessor.java b/Ghidra/Framework/Help/src/main/java/help/validator/ReferenceTagProcessor.java index 8303b77545..36ff433e87 100644 --- a/Ghidra/Framework/Help/src/main/java/help/validator/ReferenceTagProcessor.java +++ b/Ghidra/Framework/Help/src/main/java/help/validator/ReferenceTagProcessor.java @@ -48,10 +48,10 @@ public class ReferenceTagProcessor extends TagProcessor { this.anchorManager = anchorManager; // - // Note: currently all help being built has the required stylesheet living under + // Note: currently all help being built has the required stylesheet living under // /shared/ - // - // If we ever need a more robust styling mechanism, then this code would need to be + // + // If we ever need a more robust styling mechanism, then this code would need to be // updated to know how to search for the referenced stylesheet Path helpPath = help.getHelpLocation(); FileSystem fs = helpPath.getFileSystem(); @@ -81,8 +81,8 @@ public class ReferenceTagProcessor extends TagProcessor { if ("a".equals(tagType)) { if (tagAttributes.containsKey("href")) { try { - anchorManager.addAnchorRef( - new HREF(help, file, tagAttributes.get("href"), lineNum)); + anchorManager + .addAnchorRef(new HREF(help, file, tagAttributes.get("href"), lineNum)); } catch (URISyntaxException e) { errorCount++; @@ -102,8 +102,8 @@ public class ReferenceTagProcessor extends TagProcessor { else if ("img".equals(tagType)) { if (tagAttributes.containsKey("src")) { try { - anchorManager.addImageRef( - new IMG(help, file, tagAttributes.get("src"), lineNum)); + anchorManager + .addImageRef(new IMG(help, file, tagAttributes.get("src"), lineNum)); } catch (URISyntaxException e) { errorCount++; @@ -119,7 +119,7 @@ public class ReferenceTagProcessor extends TagProcessor { else if ("link".equals(tagType)) { String rel = tagAttributes.get("rel"); if (rel != null && "stylesheet".equals(rel.toLowerCase())) { -// TODO there is at least one help module that has multiple style sheets. I see no reason to +// TODO there is at least one help module that has multiple style sheets. I see no reason to // enforce this constraint: // if (hasStyleSheet) { // errorCount++; @@ -191,7 +191,8 @@ public class ReferenceTagProcessor extends TagProcessor { if (!hasDefaultStyleSheet) { errorCount++; errors.append("Incorrect stylesheet defined - none match " + defaultStyleSheet + - " in file " + htmlFile + EOL); + " in file " + htmlFile + EOL + "\tDiscovered stylesheets: " + styleSheets + EOL); + } } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/Data.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/Data.java index dd4af0dc06..9ca8ffef6e 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/Data.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/Data.java @@ -38,10 +38,10 @@ public interface Data extends CodeUnit, Settings { /** * Get the class used to express the value of this data. - * + * *

      NOTE: This determination is made based upon data type and settings only and does not * examine memory bytes which are used to construct the data value object. - * + * * @return value class or null if a consistent class is not utilized. */ public Class getValueClass(); @@ -216,7 +216,7 @@ public interface Data extends CodeUnit, Settings { * Return the first immediate child component that contains the byte at the given offset. It * is important to note that with certain datatypes there may be more than one component * containing the specified offset (see {@link #getComponentsContaining(int)}). - * + * * @param offset the amount to add to this data items address to get the address of the * requested data item. * @return first data component containing offset or null @@ -227,10 +227,10 @@ public interface Data extends CodeUnit, Settings { public Data getComponentAt(int offset); /** - * RReturn the first immediate child component that contains the byte at the given offset. It + * Return the first immediate child component that contains the byte at the given offset. It * is important to note that with certain datatypes there may be more than one component * containing the specified offset (see {@link #getComponentsContaining(int)}). - * + * * @param offset the amount to add to this data items address to get the * @return first data component containing offset or null address of the requested data item. */ diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/util/FieldNameFieldLocation.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/util/FieldNameFieldLocation.java index c336995a9e..e1ef650ad7 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/util/FieldNameFieldLocation.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/util/FieldNameFieldLocation.java @@ -17,13 +17,15 @@ package ghidra.program.util; +import java.util.Objects; + import ghidra.framework.options.SaveState; import ghidra.program.model.address.Address; import ghidra.program.model.listing.Program; /** - * The FieldNameFieldLocation class provides specific information - * about the Function Name field within a program location. + * The FieldNameFieldLocation class provides specific information about the Function + * Name field within a program location. */ public class FieldNameFieldLocation extends CodeUnitLocation { @@ -31,11 +33,11 @@ public class FieldNameFieldLocation extends CodeUnitLocation { /** * Construct a new FieldNameFieldLocation. - * + * * @param program the program of the location - * @param addr the address of the codeunit. - * @param componentPath if not null, it is the array of indexes that point - * to a specific data type inside of another data type + * @param addr the address of the code unit + * @param componentPath if not null, it is the array of indexes that point to a specific data + * type inside of another data type * @param fieldName the field name * @param charOffset the character position within the field name for this location. */ @@ -48,14 +50,14 @@ public class FieldNameFieldLocation extends CodeUnitLocation { } /** - * Default constructor needed for restoring - * a field name location from XML + * Default constructor needed for restoring a field name location from XML */ public FieldNameFieldLocation() { } /** * Returns the field name of this location. + * @return the name. */ public String getFieldName() { return fieldName; @@ -71,19 +73,19 @@ public class FieldNameFieldLocation extends CodeUnitLocation { @Override public boolean equals(Object obj) { - if (this == obj) + if (this == obj) { return true; - if (!super.equals(obj)) - return false; - if (getClass() != obj.getClass()) - return false; - FieldNameFieldLocation other = (FieldNameFieldLocation) obj; - if (fieldName == null) { - if (other.fieldName != null) - return false; } - else if (!fieldName.equals(other.fieldName)) + if (!super.equals(obj)) { return false; + } + if (getClass() != obj.getClass()) { + return false; + } + FieldNameFieldLocation other = (FieldNameFieldLocation) obj; + if (!Objects.equals(fieldName, other.fieldName)) { + return false; + } return true; } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/util/OperandFieldLocation.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/util/OperandFieldLocation.java index 6f7a0a3c5d..88ce0227e0 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/util/OperandFieldLocation.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/util/OperandFieldLocation.java @@ -15,14 +15,16 @@ */ package ghidra.program.util; +import java.util.Objects; + import ghidra.framework.options.SaveState; import ghidra.program.model.address.Address; import ghidra.program.model.listing.Program; import ghidra.program.model.listing.VariableOffset; /** - * The OperandFieldLocation class contains specific location information - * within the OPERAND field of a CodeUnitLocation object. + * The OperandFieldLocation class contains specific location information within the + * OPERAND field of a CodeUnitLocation object. */ public class OperandFieldLocation extends CodeUnitLocation { @@ -32,17 +34,18 @@ public class OperandFieldLocation extends CodeUnitLocation { /** * Construct a new OperandFieldLocation object. - * - * @param program the program of the location - * @param addr address of the location; should not be null - * @param componentPath array of indexes for each nested data component; the - * index is the data component's index within its parent; may be null + * + * @param program the program of the location. + * @param addr address of the location; should not be null. + * @param componentPath array of indexes for each nested data component; the index is the data + * component's index within its parent; may be null. + * @param refAddr the reference 'to' address. * @param rep the String representation of the operand. * @param opIndex the index of the operand at this location. * @param characterOffset the character position from the beginning of the operand. */ - public OperandFieldLocation(Program program, Address addr, int[] componentPath, - Address refAddr, String rep, int opIndex, int characterOffset) { + public OperandFieldLocation(Program program, Address addr, int[] componentPath, Address refAddr, + String rep, int opIndex, int characterOffset) { super(program, addr, componentPath, refAddr, 0, opIndex, characterOffset); @@ -52,68 +55,63 @@ public class OperandFieldLocation extends CodeUnitLocation { /** * Construct a new OperandFieldLocation object. - * - * @param program the program of the location - * @param addr address of the location; should not be null - * @param componentPath array of indexes for each nested data component; the - * index is the data component's index within its parent; may be null - * @param refAddr the "referred to" address if the location is - * over a reference; may be null + * + * @param program the program of the location. + * @param addr address of the location; should not be null. + * @param componentPath array of indexes for each nested data component; the index is the data + * component's index within its parent; may be null . + * @param refAddr the "referred to" address if the location is over a reference; may be null. * @param rep the String representation of the operand. * @param opIndex the index indicating the operand the location is on. - * @param subOpIndex the index of the Object within the operand, this can - * be used to call an instructions getOpObjects() method - * @param characterOffset the character position from the beginning of the operand field + * @param subOpIndex the index of the Object within the operand, this can be used to call an + * instructions getOpObjects() method. + * @param characterOffset the character position from the beginning of the operand field. */ - public OperandFieldLocation(Program program, Address addr, int[] componentPath, - Address refAddr, String rep, int opIndex, int subOpIndex, int characterOffset) { - + public OperandFieldLocation(Program program, Address addr, int[] componentPath, Address refAddr, + String rep, int opIndex, int subOpIndex, int characterOffset) { super(program, addr, componentPath, refAddr, 0, opIndex, characterOffset); - this.rep = rep; this.subOpIndex = subOpIndex; } /** * Construct a new OperandFieldLocation object for an instruction operand. - * - * @param program the program of the location - * @param addr address of the location; should not be null - * @param variableOffset associated variable offset or null - * @param refAddr the "referred to" address if the location is - * over a reference; may be null + * + * @param program the program of the location. + * @param addr address of the location; should not be null. + * @param variableOffset associated variable offset or null. + * @param refAddr the "referred to" address if the location is over a reference; may be null. * @param rep the String representation of the operand. * @param opIndex the index indicating the operand the location is on. - * @param subOpIndex the index of the Object within the operand, this can - * be used to call an instructions getOpObjects() method - * @param characterOffset the character position from the beginning of the operand field + * @param subOpIndex the index of the Object within the operand, this can be used to call an + * instructions getOpObjects() method. + * @param characterOffset the character position from the beginning of the operand field. */ public OperandFieldLocation(Program program, Address addr, VariableOffset variableOffset, Address refAddr, String rep, int opIndex, int subOpIndex, int characterOffset) { - super(program, addr, null, refAddr, 0, opIndex, characterOffset); - this.rep = rep; this.subOpIndex = subOpIndex; this.variableOffset = variableOffset; } /** - * Default constructor needed for restoring - * an operand field location from XML. - */ + * Default constructor needed for restoring an operand field location from XML. + */ public OperandFieldLocation() { } /** - * Returns VariableOffset object if applicable or null + * Returns VariableOffset object if applicable or null. + * @return the variable offset. */ public VariableOffset getVariableOffset() { return variableOffset; } /** - * Returns a string representation of the opernand at this location. + * Returns a string representation of the operand at this location. + * @return the representation. */ public String getOperandRepresentation() { return rep; @@ -121,6 +119,7 @@ public class OperandFieldLocation extends CodeUnitLocation { /** * Returns the index of the operand at this location. + * @return the index */ public int getOperandIndex() { return getColumn(); @@ -128,18 +127,15 @@ public class OperandFieldLocation extends CodeUnitLocation { /** * Returns the sub operand index at this location. - * This index can be used on the instruction.getOpObjects() - * to find the actual object (Address, Register, Scalar) the - * cursor is over. + *

      + * This index can be used on the instruction.getOpObjects() to find the actual object (Address, + * Register, Scalar) the cursor is over. * @return 0-n if over a valid OpObject, -1 otherwise */ public int getSubOperandIndex() { return subOpIndex; } - /** - * Returns a String representation of this location. - */ @Override public String toString() { return super.toString() + ", OpRep = " + rep + ", subOpIndex = " + subOpIndex + @@ -158,27 +154,25 @@ public class OperandFieldLocation extends CodeUnitLocation { @Override public boolean equals(Object obj) { - if (this == obj) + if (this == obj) { return true; - if (!super.equals(obj)) + } + if (!super.equals(obj)) { return false; - if (getClass() != obj.getClass()) + } + if (getClass() != obj.getClass()) { return false; + } OperandFieldLocation other = (OperandFieldLocation) obj; - if (rep == null) { - if (other.rep != null) - return false; + if (!Objects.equals(rep, other.rep)) { + return false; } - else if (!rep.equals(other.rep)) + if (subOpIndex != other.subOpIndex) { return false; - if (subOpIndex != other.subOpIndex) - return false; - if (variableOffset == null) { - if (other.variableOffset != null) - return false; } - else if (!variableOffset.equals(other.variableOffset)) + if (!Objects.equals(variableOffset, other.variableOffset)) { return false; + } return true; } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/util/ProgramLocation.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/util/ProgramLocation.java index 27c1db53cb..3ac1acd566 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/util/ProgramLocation.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/util/ProgramLocation.java @@ -95,7 +95,7 @@ public class ProgramLocation implements Comparable { * Construct a new ProgramLocation for the given address. The address will be adjusted to the * beginning of the {@link CodeUnit code unit} containing that address (if it exists). The * original address can be retrieved using the {@link #getByteAddress()}" method. - * + * * @param program the program associated with this program location (also used to obtain a * code-unit-aligned address) * @param addr address of the location; cannot be null @@ -118,7 +118,7 @@ public class ProgramLocation implements Comparable { * Construct a new ProgramLocation for the given address. The address will be adjusted to the * beginning of the {@link CodeUnit code unit} containing that address (if it exists). The * original address can be retrieved using the {@link #getByteAddress()} method. - * + * * @param program the program associated with this program location (also used to obtain a * code-unit-aligned address) * @param addr address for the location @@ -132,7 +132,7 @@ public class ProgramLocation implements Comparable { * Construct a new ProgramLocation for the given address. The address will be adjusted to the * beginning of the {@link CodeUnit code unit} containing that address (if it exists). The * original address can be retrieved using the {@link #getByteAddress()} method. - * + * * @param program the program associated with this program location (also used to obtain a * code-unit-aligned address) * @param addr address for the location @@ -150,7 +150,7 @@ public class ProgramLocation implements Comparable { * Construct a new ProgramLocation for the given address. The address will be adjusted to the * beginning of the {@link CodeUnit code unit} containing that address (if it exists). The * original address can be retrieved using the {@link #getByteAddress()} method. - * + * * @param program the program associated with this program location (also used to obtain a * code-unit-aligned address) * @param addr address for the location @@ -170,6 +170,7 @@ public class ProgramLocation implements Comparable { /** * Returns the componentPath for the {@link CodeUnit code unit}. Null will be returned if the * object is an {@link Instruction} or a top-level {@link Data} object. + * @return the path. */ public int[] getComponentPath() { return componentPath; @@ -177,6 +178,7 @@ public class ProgramLocation implements Comparable { /** * Returns the program associated with this location. + * @return the program. */ public Program getProgram() { return program; @@ -184,11 +186,12 @@ public class ProgramLocation implements Comparable { /** * Returns the address associated with this location. - * + * *

      * Note: this may not be the same as the byte address. For example, in a {@link CodeUnit code * unit} location this may be the minimum address of the code unit that contains the byte * address. + * @return the address. */ public Address getAddress() { return addr; @@ -196,6 +199,7 @@ public class ProgramLocation implements Comparable { /** * Returns the byte level address associated with this location. + * @return the byte address. */ public Address getByteAddress() { return byteAddr; @@ -203,6 +207,7 @@ public class ProgramLocation implements Comparable { /** * Returns the "referred to" address if the location is over an address in some field. + * @return the address. */ public Address getRefAddress() { return refAddr; @@ -210,7 +215,7 @@ public class ProgramLocation implements Comparable { /** * Save this program location to the given save state object. - * + * * @param obj the save state object for saving the location */ public void saveState(SaveState obj) { @@ -231,7 +236,7 @@ public class ProgramLocation implements Comparable { /** * Restore this program location using the given program and save state object. - * + * * @param program1 program to restore from * @param obj the save state to restore from */ @@ -254,7 +259,7 @@ public class ProgramLocation implements Comparable { /** * Get the program location for the given program and save state object. - * + * * @param program the program for the location * @param saveState the state to restore * @return the restored program location @@ -274,7 +279,7 @@ public class ProgramLocation implements Comparable { } // no address, it must be in a removed block; we can't use it } - catch (RuntimeException e) { // restoreState may not parse the address if it is no longer valid. + catch (RuntimeException e) { // state may not parse the address if it is no longer valid } catch (ClassNotFoundException e) { // not sure why we are ignoring this--if you know, then please let everyone else know @@ -419,7 +424,7 @@ public class ProgramLocation implements Comparable { } CodeUnit cu = p.getListing().getCodeUnitContaining(addr); - // if the codeunit is a data, try and dig down to the lowest subdata containing the address + // if the code unit is data, get the lowest sub-data containing the address if (cu instanceof Data) { Data data = (Data) cu; cu = data.getPrimitiveAt((int) addr.subtract(data.getAddress())); @@ -437,10 +442,10 @@ public class ProgramLocation implements Comparable { } /** - * Returns true if this location represents a valid location in the given program - * + * Returns true if this location represents a valid location in the given program. + * * @param testProgram the program to test if this location is valid. - * @return true if this location represents a valid location in the given program + * @return true if this location represents a valid location in the given program. */ public boolean isValid(Program testProgram) { return addr == null || testProgram.getAddressFactory().isValidAddress(addr); @@ -448,7 +453,7 @@ public class ProgramLocation implements Comparable { /** * Returns the row within the program location. - * + * * @return the row within the program location. */ public int getRow() { @@ -456,9 +461,9 @@ public class ProgramLocation implements Comparable { } /** - * Returns the character offset in the display item at the (row,col) - * - * @return the character offset in the display item at the (row,col) + * Returns the character offset in the display item at the (row,col). + * + * @return the character offset in the display item at the (row,col). */ public int getCharOffset() { return charOffset; @@ -467,6 +472,7 @@ public class ProgramLocation implements Comparable { /** * Returns the column index of the display piece represented by this location. For most * locations, there is only one display item per row, in which case this value will be 0. + * @return the column. */ public int getColumn() { return col; diff --git a/Ghidra/Framework/Utility/src/main/java/utility/function/ExceptionalSupplier.java b/Ghidra/Framework/Utility/src/main/java/utility/function/ExceptionalSupplier.java new file mode 100644 index 0000000000..2225d45cda --- /dev/null +++ b/Ghidra/Framework/Utility/src/main/java/utility/function/ExceptionalSupplier.java @@ -0,0 +1,34 @@ +/* ### + * 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 utility.function; + +/** + * A generic functional interface that is more semantically sound than {@link Runnable}. Use + * anywhere you wish to have a generic callback function and you need to throw an exception. + * + * @param the return type of your choice + * @param the exception of your choice + */ +@FunctionalInterface +public interface ExceptionalSupplier { + + /** + * The supplier method + * @return the item to return + * @throws E the declared exception + */ + public T get() throws E; +}