mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-05 10:49:34 +02:00
GP-1556 - Added support for searching for structure fields by offset
This commit is contained in:
parent
883f5a687a
commit
812ea4fe1e
45 changed files with 1461 additions and 840 deletions
|
@ -58,7 +58,7 @@
|
|||
references to the address 0040767d.</P>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<P><IMG alt="" src="../../shared/tip.png">You can also <A href="#Data_Types">show
|
||||
<P><IMG alt="" src="images/tip.png">You can also <A href="#Data_Types">show
|
||||
references to data types</A> from the Data Type Manager. In this case, all locations where
|
||||
the selected data type is applied will be highlighted.</P>
|
||||
</BLOCKQUOTE>
|
||||
|
@ -73,11 +73,11 @@
|
|||
|
||||
<LI>Select whichever of the following is available from the popup menu:
|
||||
<UL>
|
||||
<LI>Select <B>References <IMG alt="" src="../../shared/arrow.gif"> Show References to</B>
|
||||
<LI>Select <B>References <IMG alt="" src="images/arrow.gif"> Show References to</B>
|
||||
from the popup menu.</LI>
|
||||
<LI>Select <B>References <IMG alt="" src="../../shared/arrow.gif"> Find References to</B>
|
||||
<LI>Select <B>References <IMG alt="" src="images/arrow.gif"> Find References to</B>
|
||||
from the popup menu.</LI>
|
||||
<LI>Select <B>References <IMG alt="" src="../../shared/arrow.gif"> Find Uses of</B>
|
||||
<LI>Select <B>References <IMG alt="" src="images/arrow.gif"> Find Uses of</B>
|
||||
from the popup menu.</LI>
|
||||
</UL>
|
||||
</OL>
|
||||
|
@ -134,12 +134,12 @@
|
|||
</P>
|
||||
<BLOCKQUOTE>
|
||||
<P>
|
||||
<IMG alt="" src="../../shared/tip.png">
|
||||
<IMG alt="" src="images/tip.png">
|
||||
To instead make a Program Selection Highlight, use the select button mentioned above.
|
||||
Then, click from the menu bar <B>Select
|
||||
<img src="../../shared/arrow.gif" alt="->" border="0">
|
||||
<img src="images/arrow.gif" alt="->" border="0">
|
||||
Program Highlight
|
||||
<img src="../../shared/arrow.gif" alt="->" border="0">
|
||||
<img src="images/arrow.gif" alt="->" border="0">
|
||||
Entire Selection
|
||||
</B>
|
||||
</P>
|
||||
|
@ -167,7 +167,7 @@
|
|||
the decompiled function will be shown.
|
||||
</P>
|
||||
|
||||
<P><A name="Make_Selection"></A><IMG alt="" src="../../shared/tip.png"> You can make a
|
||||
<P><A name="Make_Selection"></A><IMG alt="" src="images/tip.png"> You can make a
|
||||
selection in the Code Browser from the entries in the table:</P>
|
||||
|
||||
<OL>
|
||||
|
@ -198,16 +198,26 @@
|
|||
the action will display all places that function is applied.</P>
|
||||
|
||||
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<P>
|
||||
<IMG border="0" src="images/tip.png" alt=""> 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.
|
||||
</P>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<P>
|
||||
<IMG border="0" src="../../shared/note.png" alt="">
|
||||
<IMG border="0" src="images/note.png" alt="">
|
||||
<A name="Data_Type_Discovery"></A>
|
||||
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 <CODE>Data Type Reference Finder</CODE>
|
||||
service. This causes the search to be slower, but also reports many more type
|
||||
uses. To disable the dynamic searching, use the
|
||||
<B>Search</B><img src="../../shared/arrow.gif" alt="->" border="0">
|
||||
<B>Search</B><img src="images/arrow.gif" alt="->" border="0">
|
||||
<B>Dynamic Data Type Discovery</B>
|
||||
<A href="help/topics/Tool/ToolOptions_Dialog.htm">tool option</A>.
|
||||
</P>
|
||||
|
@ -221,7 +231,7 @@
|
|||
<OL start="1" type="1">
|
||||
<LI>Right-mouse anywhere on the code unit*</LI>
|
||||
|
||||
<LI>Select <B>References <IMG alt="" src="../../shared/arrow.gif"> Show References to
|
||||
<LI>Select <B>References <IMG alt="" src="images/arrow.gif"> Show References to
|
||||
Address </B> from the popup menu.</LI>
|
||||
</OL>
|
||||
|
||||
|
@ -240,7 +250,7 @@
|
|||
|
||||
<BLOCKQUOTE>
|
||||
<P>
|
||||
<IMG border="0" src="../../shared/note.png" alt=""> This action will show only direct
|
||||
<IMG border="0" src="images/note.png" alt=""> This action will show only direct
|
||||
references to the current code unit. No other special reference finding will take place.
|
||||
</P>
|
||||
</BLOCKQUOTE>
|
||||
|
@ -250,7 +260,7 @@
|
|||
|
||||
<BLOCKQUOTE>
|
||||
<P>
|
||||
<IMG border="0" src="../../shared/tip.png" alt=""> <I>see <A href=
|
||||
<IMG border="0" src="images/tip.png" alt=""> <I>see <A href=
|
||||
"help/topics/DockingWindows/Docking_Windows.htm#Renaming_Windows">Docking Windows - Renaming
|
||||
Windows</A></I>
|
||||
</P>
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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 <b>and</b> 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 <b>and</b> 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 <b>and</b> will
|
||||
* display the results of the search.
|
||||
* <p>
|
||||
* 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);
|
||||
}
|
||||
|
|
|
@ -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<Variable> 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
|
||||
|
|
|
@ -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<LocationReference> 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) };
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -260,10 +260,10 @@ public abstract class LocationDescriptor {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns a generic {@link ProgramLocation} based upon the <tt>program</tt> and
|
||||
* <tt>homeAddress</tt> of this <tt>LocationDescriptor</tt>. Subclasses should override this
|
||||
* Returns a generic {@link ProgramLocation} based upon the <tt>program</tt> and
|
||||
* <tt>homeAddress</tt> of this <tt>LocationDescriptor</tt>. 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;
|
||||
|
|
|
@ -99,7 +99,7 @@ public class LocationReference implements Comparable<LocationReference> {
|
|||
* 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
|
||||
* <tt>from</tt> 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<LocationReference> {
|
|||
* 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<LocationReference> {
|
|||
* 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<LocationReference> {
|
|||
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<LocationReference> {
|
|||
"\taddress: " + locationOfUseAddress + ",\n" +
|
||||
((refType.equals("")) ? "" : "\trefType: " + refType + ",\n") +
|
||||
"\tisOffcut: " + isOffcutReference + ",\n" +
|
||||
((context == EMPTY_CONTEXT) ? "" : "\tcontext: " + context + ",") +
|
||||
((context == EMPTY_CONTEXT) ? "" : "\tcontext: " + context + "\n") +
|
||||
"}";
|
||||
//@formatter:off
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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<LocationReferencesProvider> iterator =
|
||||
providerList.iterator(); iterator.hasNext();) {
|
||||
for (Iterator<LocationReferencesProvider> 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);
|
||||
|
|
|
@ -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.
|
||||
* <br>
|
||||
* <b>Note: </b> 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.
|
||||
* <br>
|
||||
* @param accumulator the results storage
|
||||
* <p>
|
||||
* <b>Note: </b> 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 <tt>dataType</tt> must be
|
||||
* a {@link Composite} to search for a field
|
||||
* @param fieldName optional field name for which to search; the <tt>dataType</tt> 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<LocationReference> 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.
|
||||
* <br>
|
||||
* <b>Note: </b> 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.
|
||||
* <br>
|
||||
* @param accumulator the results storage
|
||||
* <p>
|
||||
* <b>Note: </b> 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 <tt>dataType</tt> must be
|
||||
* a {@link Composite} to search for a field
|
||||
* @param fieldName optional field name for which to search; the <tt>dataType</tt> 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<LocationReference> 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.
|
||||
* <p>
|
||||
* <b>Note: </b> 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<LocationReference> 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.
|
||||
* <p>
|
||||
* <b>Note: </b> 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.
|
||||
* <p>
|
||||
* 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<LocationReference> 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<LocationReference> 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<LocationReference> 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<LocationReference> accumulator, Program program, DataType dataType,
|
||||
String fieldName, TaskMonitor monitor) throws CancelledException {
|
||||
FieldMatcher fieldMatcher, TaskMonitor monitor) throws CancelledException {
|
||||
|
||||
List<DataTypeReferenceFinder> 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<LocationReference> accumulator,
|
||||
Program program, Predicate<Data> dataMatcher, String fieldName, TaskMonitor monitor)
|
||||
throws CancelledException {
|
||||
Program program, Predicate<Data> 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<String> 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<String> getEnumNames(Data data, Enum enumm) {
|
||||
String enumEntryName = data.getDefaultValueRepresentation();
|
||||
List<String> 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<LocationReference> accumulator, Data data, String fieldName,
|
||||
Accumulator<LocationReference> accumulator, Data data, FieldMatcher fieldMatcher,
|
||||
Predicate<Data> 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<LocationReference> accumulator, Data data, String fieldName,
|
||||
Accumulator<LocationReference> 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<Reference> 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<? extends DataType> 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)));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -40,12 +40,10 @@ class UnionLocationDescriptor extends DataTypeLocationDescriptor {
|
|||
protected void doGetReferences(Accumulator<LocationReference> 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
|
||||
|
|
|
@ -38,7 +38,7 @@ public interface DataTypeReferenceFinder extends ExtensionPoint {
|
|||
* <p>
|
||||
* Note that this operation is multi-threaded and that results will be delivered as they
|
||||
* are found via the <code>callback</code>.
|
||||
*
|
||||
*
|
||||
* @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<DataTypeReference> callback,
|
||||
TaskMonitor monitor) throws CancelledException;
|
||||
Consumer<DataTypeReference> 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 {
|
|||
* <p>
|
||||
* Note that this operation is multi-threaded and that results will be delivered as they
|
||||
* are found via the <code>callback</code>.
|
||||
*
|
||||
*
|
||||
* @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<DataTypeReference> 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.
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* Note that this operation is multi-threaded and that results will be delivered as they
|
||||
* are found via the <code>callback</code>.
|
||||
*
|
||||
* @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<DataTypeReference> callback, TaskMonitor monitor) throws CancelledException;
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
* <p>
|
||||
* 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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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++) {
|
||||
|
|
|
@ -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<OperandFieldElement> 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;
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ import ghidra.util.classfinder.ClassSearcher;
|
|||
|
||||
/**
|
||||
* Generates data value Fields for data subcomponents.
|
||||
* <P>
|
||||
* <P>
|
||||
* 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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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 <T> the return type
|
||||
* @param <E> 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, E extends Exception> T tx(Program p, ExceptionalSupplier<T, E> 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.
|
||||
*
|
||||
*
|
||||
* <P>Note: this is a blocking operation. Your test will not proceed while this method is
|
||||
* sleeping.
|
||||
*
|
||||
*
|
||||
* <P><B>Do not leave this call in your test when committing changes.</B>
|
||||
* @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
|
||||
|
|
|
@ -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<Address> 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) {
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue