GP-1572 - Set Equates - continuation of pull request to allow users to

apply equates from enums where multiple names are mapped to a single
value.

Closes #3618
This commit is contained in:
dragonmacher 2021-12-10 17:29:20 -05:00
parent 1da1c9fef3
commit 6746fdf60a
13 changed files with 1172 additions and 1004 deletions

View file

@ -32,7 +32,7 @@ import ghidra.program.util.ProgramLocation;
import ghidra.util.task.TaskMonitor;
/**
*Class to handle creating new equates for a selection or the whole program
* Class to handle creating new equates for a selection or the whole program
*/
public class CreateEquateCmd extends BackgroundCommand {
@ -46,9 +46,10 @@ public class CreateEquateCmd extends BackgroundCommand {
/**
*
* @param scalar user defined scalar to search for in program
* @param iter the range of code units for which to maybe create equates
* @param iter the range of code units for which to maybe create equates
* @param equateName user defined name for the new equate to be set
* @param overwriteExisting
* @param overwriteExisting true to rename existing equates
* @param context the action context
*/
public CreateEquateCmd(Scalar scalar, CodeUnitIterator iter, String equateName,
boolean overwriteExisting, ListingActionContext context) {
@ -64,9 +65,10 @@ public class CreateEquateCmd extends BackgroundCommand {
/**
*
* @param scalar user defined scalar to search for in program
* @param iter the range of code units for which to maybe create equates
* @param iter the range of code units for which to maybe create equates
* @param enoom the enum to use for formatting the equate name
* @param overwriteExisting
* @param overwriteExisting true to rename existing equates
* @param context the action context
*/
public CreateEquateCmd(Scalar scalar, CodeUnitIterator iter, Enum enoom,
boolean overwriteExisting, ListingActionContext context) {
@ -138,7 +140,6 @@ public class CreateEquateCmd extends BackgroundCommand {
private void createEquate(DomainObject domain, CodeUnit codeUnit, int opIndex,
Scalar scalar) {
EquateTable equateTable = codeUnit.getProgram().getEquateTable();
Address address = codeUnit.getAddress();
Equate curEquate = equateTable.getEquate(address, opIndex, targetScalarValue);
@ -156,7 +157,7 @@ public class CreateEquateCmd extends BackgroundCommand {
cmd.applyTo(domain);
}
}
private String generateFormattedEquateName() {
Program program = context.getProgram();
Enum enumWithId = (Enum) program.getDataTypeManager().addDataType(enoom, null);

View file

@ -68,29 +68,20 @@ public class EquatePlugin extends Plugin {
private DockingAction renameAction;
private DockingAction removeAction;
private DockingAction applyEnumAction;
private SetEquateDialog setEquateDialog;
private ApplyEnumDialog applyEnumDialog;
public EquatePlugin(PluginTool tool) {
super(tool);
createActions();
}
/*
* Returns the GUI for the {@link SetEquateDialog}. Note that this function will close
* any instance of the dialog that is currently open and construct a new one.
*/
private SetEquateDialog createEquateDialog(ListingActionContext context, Scalar scalar) {
if (setEquateDialog != null) {
setEquateDialog.close();
}
InitializeDialogTask task = new InitializeDialogTask(context.getProgram(), scalar);
TaskLauncher.launch(task);
setEquateDialog = task.getDialog();
setEquateDialog.setHelpLocation(new HelpLocation(getName(), "Set_Equate"));
return setEquateDialog;
SetEquateDialog dialog = task.getDialog();
dialog.setHelpLocation(new HelpLocation(getName(), "Set_Equate"));
return dialog;
}
/*
@ -99,22 +90,12 @@ public class EquatePlugin extends Plugin {
*/
private ApplyEnumDialog applyEnumDialog(ListingActionContext context) {
DataTypeManager dtm = context.getProgram().getDataTypeManager();
if (applyEnumDialog != null) {
applyEnumDialog.close();
}
applyEnumDialog = new ApplyEnumDialog(tool, dtm);
ApplyEnumDialog applyEnumDialog = new ApplyEnumDialog(tool, dtm);
applyEnumDialog.setHelpLocation(new HelpLocation(getName(), "Apply_Enum"));
tool.showDialog(applyEnumDialog);
return applyEnumDialog;
}
private void dispose(SetEquateDialog dialog) {
if (setEquateDialog == dialog) {
setEquateDialog.dispose();
setEquateDialog = null;
}
}
/**
* Determine if a Set Equate operation is permitted for the specified action context.
* The current context must satisfy the following constraints:
@ -167,39 +148,27 @@ public class EquatePlugin extends Plugin {
*/
private void setEquate(ListingActionContext context) {
// Get the scalar item that was selected. If this returns null, then something
// invalid was selected, so exit.
Scalar curScalar = getScalar(context);
if (curScalar == null) {
Scalar scalar = getScalar(context);
if (scalar == null) {
return;
}
// Create the dialog that will allow the user to select options.
createEquateDialog(context, curScalar);
SetEquateDialog dialog = createEquateDialog(context, scalar);
// Set the state of the some buttons on the dialog. ie: if the user has selected
// a range of addresses we should automatically set the "selection" radio button
// to the selected state.
setEquateDialog.setHasSelection(context);
// If the user has selected the cancel button, exit.
if (setEquateDialog.showSetDialog() == SetEquateDialog.CANCELED) {
// Set the state of the some buttons on the dialog. ie: if the user has selected a range
// of addresses we should automatically set the "selection" radio button to the selected
// state.
dialog.setHasSelection(context);
if (dialog.showSetDialog() == SetEquateDialog.CANCELED) {
return;
}
// Define an iterator for the task. We're using a CodeUnitIterator to make sure we inspect
// all Data and Instruction instances.
CodeUnitIterator iter = null;
// Get the program listing. This is what allows us to get an iterator for the
// addresses we're interested in.
Listing listing = context.getProgram().getListing();
// Now we have to 'populate' the iterator with the proper addresses. Once we have the
// iterator, we'll create a background task to process them.
//
SelectionType selectionType = setEquateDialog.getSelectionType();
SelectionType selectionType = dialog.getSelectionType();
if (selectionType == SelectionType.CURRENT_ADDRESS) {
AddressSet addrSet = new AddressSet(context.getAddress());
iter = listing.getCodeUnits(addrSet, false);
@ -213,21 +182,12 @@ public class EquatePlugin extends Plugin {
iter = listing.getCodeUnits(context.getProgram().getMemory(), true);
}
BackgroundCommand cmd;
if (setEquateDialog.getEnumDataType() != null) {
// Now set up a command to run in the background, and execute the task.
cmd = new CreateEquateCmd(curScalar, iter, setEquateDialog.getEnumDataType(),
setEquateDialog.getOverwriteExisting(), context);
}
else {
// Now set up a command to run in the background, and execute the task.
cmd = new CreateEquateCmd(curScalar, iter, setEquateDialog.getEquateName(),
setEquateDialog.getOverwriteExisting(), context);
}
BackgroundCommand cmd =
new CreateEquateCmd(scalar, iter, dialog.getEquateName(),
dialog.getOverwriteExisting(), context);
tool.executeBackgroundCommand(cmd, context.getProgram());
// Finally, blow away the dialog.
dispose(setEquateDialog);
dialog.dispose();
}
/**
@ -236,40 +196,34 @@ public class EquatePlugin extends Plugin {
* @param context the action context
*/
private void applyEnum(ListingActionContext context) {
applyEnumDialog = applyEnumDialog(context);
DataType dataType = applyEnumDialog.getUserChosenDataType();
ApplyEnumDialog dialog = applyEnumDialog(context);
DataType dataType = dialog.getUserChosenDataType();
if (dataType == null) {
return;
}
if (!(dataType instanceof Enum)) {
Msg.showError(this, null, "Input Error", "Data Type must be an enum");
return;
}
boolean shouldDoOnSubOps = applyEnumDialog.shouldApplyOnSubOps();
AddressSetView addresses = context.getSelection();
if (addresses.isEmpty()) {
addresses = new AddressSet(context.getAddress());
}
boolean shouldDoOnSubOps = dialog.shouldApplyOnSubOps();
Program program = context.getProgram();
CreateEnumEquateCommand cmd =
new CreateEnumEquateCommand(program, addresses, (Enum) dataType, shouldDoOnSubOps);
tool.executeBackgroundCommand(cmd, program);
dialog.dispose();
}
/**
* Called in response to the user activating rename action from the context menu.
*
* @param context the action context
*/
private void renameEquate(ListingActionContext context) {
// Get the scalar item that was selected. If this returns null, then something
// invalid was selected, so exit.
Scalar curScalar = getScalar(context);
if (curScalar == null) {
Scalar scalar = getScalar(context);
if (scalar == null) {
return;
}
@ -279,54 +233,48 @@ public class EquatePlugin extends Plugin {
Listing listing = context.getProgram().getListing();
// Create the dialog that will allow the user to select options.
createEquateDialog(context, curScalar);
SetEquateDialog dialog = createEquateDialog(context, scalar);
// Set the state of the some buttons on the dialog. ie: if the user has selected
// a range of addresses we should automatically set the "selection" radio button
// to the selected state.
setEquateDialog.setHasSelection(context);
// Check for user-cancel action.
int result = setEquateDialog.showRenameDialog();
// Set the state of the some buttons on the dialog. ie: if the user has selected a range
// of addresses we should automatically set the "selection" radio button to the selected
// state.
dialog.setHasSelection(context);
int result = dialog.showRenameDialog();
if (result == SetEquateDialog.CANCELED) {
return;
}
// Retrieve the name we want to change the equate(s) to. If the name is null,
// it means it's not a name that's already in the table, so just remove the
// equate.
String equateName = setEquateDialog.getEquateName();
// Retrieve the name we want to change the equate(s) to. If the name is null, it means
// it's not a name that's already in the table, so just remove the equate.
String equateName = dialog.getEquateName();
// Define an iterator for the task. We're using a CodeUnitIterator to make sure we inspect
// all Data and Instruction instances.
CodeUnitIterator iter = null;
SelectionType selectionType = setEquateDialog.getSelectionType();
SelectionType selectionType = dialog.getSelectionType();
if (selectionType == SelectionType.CURRENT_ADDRESS) {
AddressSet addrSet = new AddressSet(context.getAddress());
iter = listing.getCodeUnits(addrSet, false);
}
else if (selectionType == SelectionType.SELECTION) {
iter = listing.getCodeUnits(context.getSelection(), true);
}
else if (selectionType == SelectionType.ENTIRE_PROGRAM) {
iter = listing.getCodeUnits(context.getProgram().getMemory(), true);
}
// Determine if this is a remove or rename. If the user has left the "new equate name" field
// blank in the dialog, then remove the equate entirely.
// Determine if this is a remove or rename. If the user has left the "new equate name"
// field blank in the dialog, then remove the equate entirely.
Enum enoom = dialog.getEnumDataType();
if (equateName == null) {
removeEquateOverRange(context, equate, iter);
}
else {
renameEquate(context, equate, equateName, iter);
renameEquate(context, enoom, equate, equateName, iter);
}
// Destroy the dialog.
dispose(setEquateDialog);
dialog.dispose();
}
private Equate getEquate(ListingActionContext context) {
@ -338,7 +286,8 @@ public class EquatePlugin extends Plugin {
return equateTable.getEquate(context.getAddress(), getOperandIndex(context), s.getValue());
}
private void renameEquate(ListingActionContext context, Equate oldEquate, String newEquateName,
private void renameEquate(ListingActionContext context, Enum enoom, Equate oldEquate,
String newEquateName,
CodeUnitIterator iter) {
// First do a sanity check to make sure we're not trying to change to a duplicate
@ -356,14 +305,15 @@ public class EquatePlugin extends Plugin {
// Now loop over all the code units and search for matching scalars...
while (iter.hasNext()) {
CodeUnit cu = iter.next();
renameEquateForCodeUnit(context, oldEquate, newEquateName, oldEquateName, bckCmd, cu);
renameEquateForCodeUnit(context, enoom, oldEquate, newEquateName, oldEquateName, bckCmd,
cu);
}
// Finally, execute all the rename tasks.
tool.executeBackgroundCommand(bckCmd, context.getProgram());
}
private void renameEquateForCodeUnit(ListingActionContext context, Equate equate,
private void renameEquateForCodeUnit(ListingActionContext context, Enum enoom, Equate equate,
String newName, String oldName, CompoundBackgroundCommand bgCmd, CodeUnit cu) {
if (cu instanceof Instruction) {
@ -373,7 +323,7 @@ public class EquatePlugin extends Plugin {
List<Integer> opIndices = getInstructionMatches(program, inst, equate);
Address addr = inst.getAddress();
for (Integer opIndice : opIndices) {
bgCmd.add(createRenameCmd(oldName, newName, addr, opIndice));
bgCmd.add(createRenameCmd(enoom, oldName, newName, addr, opIndice));
}
}
else if (cu instanceof Data) {
@ -381,15 +331,15 @@ public class EquatePlugin extends Plugin {
Data data = (Data) cu;
if (isDataMatch(data, context, equate)) {
Address addr = data.getAddress();
bgCmd.add(createRenameCmd(oldName, newName, addr, getOperandIndex(context)));
bgCmd.add(createRenameCmd(enoom, oldName, newName, addr, getOperandIndex(context)));
}
}
}
private RenameEquateCmd createRenameCmd(String oldName, String newName, Address addr,
private RenameEquateCmd createRenameCmd(Enum enoom, String oldName, String newName,
Address addr,
int opIndex) {
Enum enoom = getEnumDataType();
if (enoom != null) {
return new RenameEquateCmd(oldName, enoom, addr, opIndex);
}
@ -397,10 +347,6 @@ public class EquatePlugin extends Plugin {
return new RenameEquateCmd(oldName, newName, addr, opIndex);
}
public Enum getEnumDataType() {
return setEquateDialog.getEnumDataType();
}
private void removeEquateOverRange(ListingActionContext context, Equate equate,
CodeUnitIterator iter) {

View file

@ -20,6 +20,7 @@ import java.awt.event.*;
import java.util.*;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.swing.*;
import javax.swing.table.TableColumnModel;
@ -210,13 +211,18 @@ public class SetEquateDialog extends DialogComponentProvider {
.stream()
.filter(dt -> dt instanceof Enum)
.map(Enum.class::cast)
.flatMap(enoom -> Arrays.stream(enoom.getNames(scalar.getValue())).map(name -> new EquateRowObject(name, enoom)))
.flatMap(this::enumToRowObjects)
.forEach(entries::add);
//@formatter:on
return entries;
}
private Stream<EquateRowObject> enumToRowObjects(Enum enoom) {
String[] names = enoom.getNames(scalar.getValue());
return Arrays.stream(names).map(name -> new EquateRowObject(name, enoom));
}
/*
* Builds the main panel of the dialog and returns it.
*/
@ -467,7 +473,7 @@ public class SetEquateDialog extends DialogComponentProvider {
/**
* Returns true if the user has chosen to overwrite any existing equate rules.
*
* @return true if the user has chosen to overwrite any existing equate rules.
* @return true if the user has chosen to overwrite any existing equate rules.
*/
public boolean getOverwriteExisting() {
return overwriteExistingEquates.isSelected();
@ -507,9 +513,6 @@ public class SetEquateDialog extends DialogComponentProvider {
this.setStatusText(text);
}
/**
* Called when user selects OK button
*/
@Override
protected void okCallback() {
if (isValid(this.getEquateName(), scalar)) {
@ -529,7 +532,6 @@ public class SetEquateDialog extends DialogComponentProvider {
// look up the new equate string
Equate newEquate = equateTable.getEquate(equateStr);
if (newEquate != null && getEnumDataType() == null) {
// make sure any existing equate with that name has the same value.
if (newEquate.getValue() != testScalar.getValue()) {
@ -545,14 +547,12 @@ public class SetEquateDialog extends DialogComponentProvider {
return (Enum) dataTypeManager.findDataTypeForID(id);
}
/**
* Called when user selects Cancel Button.
*/
@Override
protected void cancelCallback() {
close();
}
@Override
public void dispose() {
suggestedEquatesTable.dispose();
filterPanel.dispose();
@ -577,7 +577,7 @@ public class SetEquateDialog extends DialogComponentProvider {
}
this.enoom = enoom;
this.entryName = enoom.getName(value);
this.entryName = name;
this.dataTypeUUID = enoom.getUniversalID();
this.path = getFullPath(enoom);
String formattedEquateName = EquateManager.formatNameForEquate(dataTypeUUID, value);
@ -673,6 +673,9 @@ public class SetEquateDialog extends DialogComponentProvider {
if (enoom == null || !enoom.isEquivalent(other.getEnumDataType())) {
return false;
}
if (!Objects.equals(entryName, other.entryName)) {
return false;
}
return true;
}

View file

@ -100,25 +100,28 @@ public class EnumDataTypeHTMLRepresentation extends HTMLDataTypeRepresentation {
List<ValidatableLine> list = new ArrayList<>(values.length);
for (long value : values) {
String name = enumDataType.getName(value);
if (trim) {
name = StringUtilities.trimMiddle(name, ToolTipUtils.LINE_LENGTH);
}
String[] names = enumDataType.getNames(value);
for (String name : names) {
if (trim) {
name = StringUtilities.trimMiddle(name, ToolTipUtils.LINE_LENGTH);
}
String hexString = Long.toHexString(value);
if (value < 0) {
// Long will print leading FF for 8 bytes, regardless of enum size. Keep only
// n bytes worth of text. For example, when n is 2, turn FFFFFFFFFFFFFF12 into FF12
int length = hexString.length();
hexString = hexString.substring(length - (n * 2));
}
String hexString = Long.toHexString(value);
if (value < 0) {
// Long will print leading FF for 8 bytes, regardless of enum size. Keep only
// n bytes worth of text. For example, when n is 2, turn FFFFFFFFFFFFFF12 into
// FF12
int length = hexString.length();
hexString = hexString.substring(length - (n * 2));
}
String comment = enumDataType.getComment(name);
if (trim && comment != null) {
comment = StringUtilities.trim(comment, ToolTipUtils.LINE_LENGTH);
}
String comment = enumDataType.getComment(name);
if (trim && comment != null) {
comment = StringUtilities.trim(comment, ToolTipUtils.LINE_LENGTH);
}
list.add(new TextLine(name + " = 0x" + hexString + " " + comment));
list.add(new TextLine(name + " = 0x" + hexString + " " + comment));
}
}
return list;

View file

@ -122,7 +122,7 @@ public abstract class AbstractGhidraHeadlessIntegrationTest extends AbstractDock
}
// TODO add methods:
// createDefaultToyProgram() with no params
// createDefaultToyProgram() with no params
// createDefaultX86Program() with no params
// createDefaultX86ProgramBuilder() with no params
// createClassicNotepadProgram()
@ -198,10 +198,12 @@ public abstract class AbstractGhidraHeadlessIntegrationTest extends AbstractDock
/**
* Provides a convenient method for modifying the current program, handling the transaction
* logic.
* logic.
*
* @param p the program
* @param c the code to execute
* @see #modifyProgram(Program, ExceptionalCallback)
* @see #modifyProgram(Program, ExceptionalFunction)
*/
public static <E extends Exception> void tx(Program p, ExceptionalCallback<E> c) {
int txId = p.startTransaction("Test - Function in Transaction");
@ -227,6 +229,7 @@ public abstract class AbstractGhidraHeadlessIntegrationTest extends AbstractDock
*
* @param p the program
* @param c the code to execute
* @see #modifyProgram(Program, ExceptionalFunction)
*/
public static <E extends Exception> void modifyProgram(Program p, ExceptionalCallback<E> c) {
tx(p, c);
@ -239,6 +242,7 @@ public abstract class AbstractGhidraHeadlessIntegrationTest extends AbstractDock
* @param program the program
* @param f the function for modifying the program and creating the desired result
* @return the result
* @see #modifyProgram(Program, ExceptionalCallback)
*/
public <R, E extends Exception> R modifyProgram(Program program,
ExceptionalFunction<Program, R, E> f) {
@ -462,11 +466,11 @@ 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.
* 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.
* sleeping.
*
* <P><B>Do not leave this call in your test when committing changes.</B>
* @param p the program
@ -507,7 +511,7 @@ public abstract class AbstractGhidraHeadlessIntegrationTest extends AbstractDock
}
/**
* Waits for a launched script to complete by using the given listener.
* 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

View file

@ -1,455 +0,0 @@
/* ###
* 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.plugin.core.equate;
import static org.junit.Assert.*;
import java.util.Set;
import javax.swing.JTextField;
import org.junit.After;
import org.junit.Before;
import docking.ActionContext;
import docking.ComponentProvider;
import docking.action.DockingActionIf;
import ghidra.GhidraOptions;
import ghidra.app.context.ListingActionContext;
import ghidra.app.events.ProgramLocationPluginEvent;
import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
import ghidra.app.util.PluginConstants;
import ghidra.app.util.bean.SetEquateDialog;
import ghidra.app.util.datatype.ApplyEnumDialog;
import ghidra.app.util.viewer.field.ListingTextField;
import ghidra.app.util.viewer.field.OperandFieldFactory;
import ghidra.framework.options.Options;
import ghidra.program.database.ProgramBuilder;
import ghidra.program.database.symbol.EquateManager;
import ghidra.program.model.address.*;
import ghidra.program.model.data.*;
import ghidra.program.model.data.Enum;
import ghidra.program.model.listing.*;
import ghidra.program.model.symbol.Equate;
import ghidra.program.model.symbol.EquateTable;
import ghidra.program.util.OperandFieldLocation;
import ghidra.test.AbstractProgramBasedTest;
import ghidra.util.UniversalID;
import ghidra.util.exception.DuplicateNameException;
import ghidra.util.exception.InvalidInputException;
/**
* Tests the Equate Plugin functionality.
*/
public abstract class AbstractEquatePluginTest extends AbstractProgramBasedTest {
protected Listing listing;
protected EquatePlugin equatePlugin;
protected CodeBrowserPlugin cb;
protected DockingActionIf setAction;
protected DockingActionIf renameAction;
protected DockingActionIf removeAction;
protected DockingActionIf applyEnumAction;
protected final static String SHOW_BLOCK_NAME_OPTION = GhidraOptions.OPERAND_GROUP_TITLE +
Options.DELIMITER + GhidraOptions.OPTION_SHOW_BLOCK_NAME;
/**
* Sets up the plugins and actions for the tests.
*
* @throws Exception
*/
@Before
public void setUp() throws Exception {
initialize();
equatePlugin = getPlugin(tool, EquatePlugin.class);
cb = codeBrowser;
listing = program.getListing();
Options options = tool.getOptions(GhidraOptions.CATEGORY_BROWSER_DISPLAY);
options.getBoolean(SHOW_BLOCK_NAME_OPTION, false);
setAction = getAction(equatePlugin, "Set Equate");
renameAction = getAction(equatePlugin, "Rename Equate");
removeAction = getAction(equatePlugin, "Remove Equate");
applyEnumAction = getAction(equatePlugin, "Apply Enum");
}
@Override
protected String getProgramName() {
return "notepad";
}
@Override
protected Program getProgram() throws Exception {
return buildProgram();
}
/**
* Builds a program and sets equates.
*
* @return
* @throws Exception
*/
protected Program buildProgram() throws Exception {
ProgramBuilder builder = new ProgramBuilder("notepad", ProgramBuilder._X86);
builder.createMemory("test", "0x01001000", 0x100);
builder.createMemory("test", "0x01002000", 0x100);
builder.createMemory("test", "0x01003000", 0x4000);
// for selection containing a function stack address
builder.setBytes("0x01004bbd", "c2 08 00"); // return of previous function
builder.setBytes("0x01004bc0", "53 8b 5c 24 08 56 8b 35 b8 10 00 01 57 ff 35 78 80 00 01 " +
"53 ff d6 8b 3d e0 10 00 01 53 ff d7 8d 5c 43 02 68 9c 13 00 01 53 ff d6 53 ff d7 " +
"ff 35 7c 80 00 01 8d 5c 43 02 53 ff d6 53 ff d7 8d 5c 43 02 68 e0 17 00 01 53 ff " +
"d6 53 ff d7 66 83 64 43 02 00 8d 44 43 02 5f 5e 5b c2 04 00");
builder.disassemble(new AddressSet(builder.getProgram(), builder.addr("0x01004bc0"),
builder.addr("0x01004c1a")), true);
builder.createFunction("0x01004bc0");
//
// Add bytes that result in instructions containing scalar values for X86
//
builder.setBytes("0x010018d6", "6a 02", true);
builder.setBytes("0x010062d6", "83 c0 05", true);
builder.setBytes("0x01002a83", "6a 01", true);
builder.setBytes("0x01002a8e", "c2 10 00", true);
// not an equate - for testing action enablement
builder.setBytes("0x01003359", "74 4c", true);
builder.setBytes("0x01003a94", "6a fd", true);
builder.setBytes("0x010035be", "6a 02", true);
builder.setBytes("0x0100364c", "6a 02", true);
builder.setBytes("0x01004ad6", "6a 02", true);
builder.setBytes("0x01004bbd", "c2 08 00", true);
builder.setBytes("0x01004e4a", "6a 5a", true);
builder.setBytes("0x01005128", "68 b7 00 00 00", true);
builder.setBytes("0x0100519f", "6a 02", true);
builder.setBytes("0x01005a01", "3d b7 00 00 00", true);
builder.setBytes("0x01005e9d", "68 b7 00 00 00", true);
builder.setBytes("0x010059ef", "83 f8 ff", true);
builder.setBytes("0x01004c20", "6a 02", true);
builder.setBytes("0x01003384", "ff ff", true);
builder.setBytes("0x010060f0", "6a 01", true);
builder.createEquate("0x010060f0", "ANOTHER_ONE", 1, 0);
builder.setBytes("0x01006133", "68 b7 00 00 00", true);
builder.setBytes("0x01006245", "68 b7 00 00 00", true);
builder.setBytes("0x010063da", "f6 45 fc 02", true);
builder.setBytes("0x0100649a", "a1 c0 85 00 01", true);
builder.setBytes("0x010064c5", "83 c4 08", true);
builder.setBytes("0x010064a3", "68 10 66 00 01", true);
builder.setBytes("0x010064ae", "83 c4 04", true);
builder.setBytes("0x01006500", "83 c4 08", true);
builder.setBytes("0x010065a7", "c7 45 fc ff ff ff ff", true);
builder.setBytes("0x010065b0", "c7 45 fc ff ff ff ff", true);
builder.createEquate("0x010064c5", "EIGHT", 8, 1);
builder.createEquate("0x01006500", "EIGHT", 8, 1);
builder.setBytes("0x01006455", "83 c4 04", true);
builder.createEquate("0x01006455", "FOUR", 4, 1);
builder.createEquate("0x010064ae", "FOUR", 4, 1);
builder.setBytes("0x01006140", "6a 01", true);
builder.setBytes("0x01006147", "6a 01", true);
builder.setBytes("0x0100621d", "6a 01", true);
builder.setBytes("0x010063f4", "6a 01", true);
builder.createEquate("0x01006140", "ONE", 1, 0);
builder.createEquate("0x01006147", "ONE", 1, 0);
builder.createEquate("0x0100621d", "ONE", 1, 1);
builder.createEquate("0x010063f4", "ONE", 1, 1);
builder.setBytes("0x010061a2", "6a 30", true);
builder.createEquate("0x010061a2", "THIRTY", 0x30, 0);
builder.setBytes("0x01006252", "6a 02", true);
builder.setBytes("0x0100644d", "6a 02", true);
builder.createEquate("0x01006252", "TWO", 2, 0);
builder.createEquate("0x010063da", "TWO", 2, 1);
builder.createEquate("0x0100644d", "TWO", 2, 0);
builder.setBytes("0x010060b2", "6a 02", true);
builder.setBytes("0x01006254", "6a 02", true);
builder.createEquate("0x010060b2", "TWO_alt", 2, 1);
builder.createEquate("0x01006254", "TWO_alt", 2, 0);
return builder.getProgram();
}
@Override
@After
public void tearDown() throws Exception {
env.dispose();
}
//=================================================================================================
// Private Methods
//=================================================================================================
protected ListingActionContext getListingContext() {
ActionContext context = cb.getProvider().getActionContext(null);
return (ListingActionContext) context;
}
protected ApplyEnumDialog performApplyEnum() {
ComponentProvider provider = tool.getComponentProvider(PluginConstants.CODE_BROWSER);
DockingActionIf action = getAction(equatePlugin, "Apply Enum");
performAction(action, provider, false);
ApplyEnumDialog d = waitForDialogComponent(ApplyEnumDialog.class);
return d;
}
protected void performAction(String name) {
ComponentProvider provider = cb.getProvider();
DockingActionIf action = getAction(equatePlugin, name);
performAction(action, provider, true);
waitForTasks();
}
protected void assertConvertActionsInPopup(boolean inPopup) {
Set<DockingActionIf> actions = getActionsByOwner(tool, "EquatePlugin");
for (DockingActionIf action : actions) {
String actionName = action.getName();
if (actionName.startsWith("Convert")) {
assertEquals("Unexpected popup menu item state: " + actionName, inPopup,
action.isAddToPopup(getListingContext()));
}
}
}
protected void assertNonFloatConvertActionsInPopup() {
Set<DockingActionIf> actions = getActionsByOwner(tool, "EquatePlugin");
for (DockingActionIf action : actions) {
String actionName = action.getName();
if (actionName.startsWith("Convert")) {
boolean inPopup =
!(actionName.indexOf("Double") > 0 || actionName.indexOf("Float") > 0);
assertEquals("Unexpected popup menu item state: " + actionName, inPopup,
action.isAddToPopup(getListingContext()));
}
}
}
protected void assertConvertNonCharNonSignedActionsInPopup() {
Set<DockingActionIf> actions = getActionsByOwner(tool, "EquatePlugin");
for (DockingActionIf element : actions) {
String name = element.getName();
if (name.startsWith("Convert") &&
(name.indexOf("Char") < 0 && name.indexOf("Signed") < 0)) {
assertTrue(element.isAddToPopup(getListingContext()));
}
}
}
protected void assertConvertNonSignedActionsInPopup() {
Set<DockingActionIf> actions = getActionsByOwner(tool, "EquatePlugin");
for (DockingActionIf action : actions) {
String name = action.getName();
if (name.startsWith("Convert") && name.indexOf("Signed") < 0) {
assertTrue(action.isAddToPopup(getListingContext()));
}
}
}
protected void createEquate(long address, int opIndex, String equateName, long equateValue)
throws Exception {
EquateTable equateTable = program.getEquateTable();
int transactionID = program.startTransaction("Test - Create Equate at " + address);
Equate equate = equateTable.createEquate(equateName, equateValue);
equate.addReference(addr(address), opIndex);
program.endTransaction(transactionID, true);
flushProgramEvents();
}
protected void assertEquatesAppliedFromEnum(UniversalID id, int... values) {
EquateTable et = program.getEquateTable();
for (int value : values) {
String fullName = EquateManager.formatNameForEquate(id, value);
assertNotNull("Equate not applied from Enum . No equate for " + fullName,
et.getEquate(fullName));
}
}
protected void assertEquatesNotApplied(UniversalID id, int... values) {
EquateTable et = program.getEquateTable();
for (int value : values) {
String fullName = EquateManager.formatNameForEquate(id, value);
assertNull("Equate should not have been applied " + fullName, et.getEquate(fullName));
}
}
protected void cancel(SetEquateDialog d) {
pressButtonByText(d.getComponent(), "Cancel");
}
protected SetEquateDialog showSetEquateDialog() {
performAction(setAction, cb.getProvider(), false);
SetEquateDialog d =
waitForDialogComponent(tool.getToolFrame(), SetEquateDialog.class, 2000);
assertNotNull(d);
assertEquals("Set Equate", d.getTitle());
return d;
}
protected void createSameValueDifferentNameEquates()
throws DuplicateNameException, InvalidInputException {
EquateTable eqTable = program.getEquateTable();
int transactionID =
program.startTransaction("Test - Create Equate - Same Value/Different Name");
for (int i = 0; i < 5; i++) {
eqTable.createEquate("MY_EQUATE_" + i, 5);
}
program.endTransaction(transactionID, true);
flushProgramEvents();
}
protected void assertNoEquateAt(String addressString, String scalarText) {
Address address = program.getAddressFactory().getAddress(addressString);
cb.goToField(address, OperandFieldFactory.FIELD_NAME, 0, 0);
ListingTextField f = (ListingTextField) cb.getCurrentField();
assertEquals(scalarText, f.getText());
}
protected void assertEquateAt(String equateText, AddressSet scalarAddresses) {
for (AddressRange addressRange : scalarAddresses) {
Address address = addressRange.getMinAddress();
cb.goToField(address, OperandFieldFactory.FIELD_NAME, 0, 0);
ListingTextField f = (ListingTextField) cb.getCurrentField();
assertEquals("Equate \"" + equateText + "\" not applied @ " + address, equateText,
f.getText());
}
}
protected Enum createEnum_myEnum() {
DataTypeManager dataTypeManager = program.getDataTypeManager();
int id = dataTypeManager.startTransaction("EquatePluginTest enum");
Enum enumm = new EnumDataType("myEnum", 1);
enumm.add("zeroScalar", 0);
enumm.add("oneScalar", 1);
enumm.add("twoScalar", 2);
enumm.add("threeScalar", 3);
enumm.add("fourScalar", 4);
enumm.add("fiveScalar", 5);
enumm = (Enum) dataTypeManager.addDataType(enumm, null);
dataTypeManager.endTransaction(id, true);
return enumm;
}
protected void setEquateString(String equateText, SetEquateDialog d) {
JTextField tf = findComponent(d.getComponent(), JTextField.class);
assertNotNull(tf);
assertEquals("", tf.getText());
setText(tf, equateText);
}
protected SetEquateDialog getSetEquatesDialog() {
SetEquateDialog d = showSetEquateDialog();
return d;
}
protected AddressSet selectScalars_0xb7() {
AddressSet addrs = toAddressSet(0x1006245, 0x1006133, 0x1005e9d);
makeSelection(tool, program, addrs);
return addrs;
}
protected AddressSet selectRange(Address start, Address end) {
AddressSet addrs = toAddressSet(start, end);
makeSelection(tool, program, addrs);
return addrs;
}
protected AddressSet selectSetAndInvalidAddress(AddressSet addAddrSet, Address addr) {
AddressSet addrs = new AddressSet();
addrs.add(addAddrSet);
addrs.add(addr);
makeSelection(tool, program, addrs);
return addrs;
}
protected void fireOperandFieldLocationEvent(Address addr, int opIndex) {
Instruction inst = listing.getInstructionAt(addr);
if (inst == null) {
fail("Must have a code unit to use this method");
}
String str = CodeUnitFormat.DEFAULT.getOperandRepresentationString(inst, opIndex);
OperandFieldLocation loc =
new OperandFieldLocation(program, addr, null, null, str, opIndex, 0);
tool.firePluginEvent(new ProgramLocationPluginEvent("test", loc, program));
waitForSwing();
}
protected void putCursorOnOperand(long offset, int opIndex) {
Address addr = addr(offset);
fireOperandFieldLocationEvent(addr, opIndex);
waitForBusyTool(tool);
}
protected SetEquateDialog showRenameEquatesDialog() {
performAction(renameAction, cb.getProvider(), false);
SetEquateDialog d =
waitForDialogComponent(tool.getToolFrame(), SetEquateDialog.class, 2000);
assertNotNull(d);
assertEquals("Rename Equate", d.getTitle());
return d;
}
protected void flushProgramEvents() {
program.flushEvents();
waitForBusyTool(tool);
}
protected void createSignedData(Address addr) throws Exception {
createData(addr, SignedWordDataType.dataType);
}
protected void createUnsignedData(Address addr) throws Exception {
createData(addr, WordDataType.dataType);
}
protected void createData(Address addr, DataType dt) throws Exception {
int id = program.startTransaction("Test - Create Data");
dt = dt.clone(program.getDataTypeManager());
int length = dt.getLength();
assertTrue("", length > 0);
listing.clearCodeUnits(addr, addr.add(length - 1), true);
program.getListing().createData(addr, dt);
program.endTransaction(id, true);
}
}

View file

@ -0,0 +1,526 @@
/* ###
* 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.plugin.core.equate;
import static org.junit.Assert.*;
import javax.swing.JCheckBox;
import javax.swing.JRadioButton;
import org.junit.Before;
import org.junit.Test;
import docking.ComponentProvider;
import docking.action.DockingActionIf;
import docking.widgets.DropDownSelectionTextField;
import ghidra.GhidraOptions;
import ghidra.app.events.ProgramLocationPluginEvent;
import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
import ghidra.app.util.PluginConstants;
import ghidra.app.util.bean.SetEquateDialog;
import ghidra.app.util.datatype.ApplyEnumDialog;
import ghidra.framework.options.Options;
import ghidra.program.database.ProgramBuilder;
import ghidra.program.database.symbol.EquateManager;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSet;
import ghidra.program.model.data.*;
import ghidra.program.model.data.Enum;
import ghidra.program.model.listing.*;
import ghidra.program.model.symbol.Equate;
import ghidra.program.model.symbol.EquateTable;
import ghidra.program.util.OperandFieldLocation;
import ghidra.test.AbstractProgramBasedTest;
import ghidra.util.UniversalID;
/**
* Tests the Equate Plugin functionality.
*/
public class EquatePlugin2Test extends AbstractProgramBasedTest {
private final static String SHOW_BLOCK_NAME_OPTION = GhidraOptions.OPERAND_GROUP_TITLE +
Options.DELIMITER + GhidraOptions.OPTION_SHOW_BLOCK_NAME;
private DockingActionIf setAction;
private DockingActionIf renameAction;
private EquatePlugin equatePlugin;
private CodeBrowserPlugin cb;
private Listing listing;
@Before
public void setUp() throws Exception {
initialize();
equatePlugin = getPlugin(tool, EquatePlugin.class);
cb = codeBrowser;
listing = program.getListing();
Options options = tool.getOptions(GhidraOptions.CATEGORY_BROWSER_DISPLAY);
options.getBoolean(SHOW_BLOCK_NAME_OPTION, false);
setAction = getAction(equatePlugin, "Set Equate");
renameAction = getAction(equatePlugin, "Rename Equate");
}
@Override
public String getProgramName() {
return "sample";
}
@Override
public Program getProgram() throws Exception {
ProgramBuilder builder = new ProgramBuilder("sample", ProgramBuilder._X86);
builder.createMemory("test", "0x01001000", 0x100);
builder.createMemory("test", "0x01002000", 0x100);
builder.createMemory("test", "0x01003000", 0x4000);
// for selection containing a function stack address
builder.setBytes("0x01004bbd", "c2 08 00"); // return of previous function
builder.setBytes("0x01004bc0", "53 8b 5c 24 08 56 8b 35 b8 10 00 01 57 ff 35 78 80 00 01 " +
"53 ff d6 8b 3d e0 10 00 01 53 ff d7 8d 5c 43 02 68 9c 13 00 01 53 ff d6 53 ff d7 " +
"ff 35 7c 80 00 01 8d 5c 43 02 53 ff d6 53 ff d7 8d 5c 43 02 68 e0 17 00 01 53 ff " +
"d6 53 ff d7 66 83 64 43 02 00 8d 44 43 02 5f 5e 5b c2 04 00");
builder.disassemble(new AddressSet(builder.getProgram(), builder.addr("0x01004bc0"),
builder.addr("0x01004c1a")), true);
builder.createFunction("0x01004bc0");
//
// Add bytes that result in instructions containing scalar values for X86
//
builder.setBytes("0x010018d6", "6a 02", true);
builder.setBytes("0x010062d6", "83 c0 05", true);
builder.setBytes("0x01002a83", "6a 01", true);
builder.setBytes("0x01002a8e", "c2 10 00", true);
// not an equate - for testing action enablement
builder.setBytes("0x01003359", "74 4c", true);
builder.setBytes("0x01003a94", "6a fd", true);
builder.setBytes("0x010035be", "6a 02", true);
builder.setBytes("0x0100364c", "6a 02", true);
builder.setBytes("0x01004ad6", "6a 02", true);
builder.setBytes("0x01004bbd", "c2 08 00", true);
builder.setBytes("0x01004e4a", "6a 5a", true);
builder.setBytes("0x01005128", "68 b7 00 00 00", true);
builder.setBytes("0x0100519f", "6a 02", true);
builder.setBytes("0x01005a01", "3d b7 00 00 00", true);
builder.setBytes("0x01005e9d", "68 b7 00 00 00", true);
builder.setBytes("0x010059ef", "83 f8 ff", true);
builder.setBytes("0x01004c20", "6a 02", true);
builder.setBytes("0x01003384", "ff ff", true);
builder.setBytes("0x010060f0", "6a 01", true);
builder.createEquate("0x010060f0", "ANOTHER_ONE", 1, 0);
builder.setBytes("0x01006133", "68 b7 00 00 00", true);
builder.setBytes("0x01006245", "68 b7 00 00 00", true);
builder.setBytes("0x010063da", "f6 45 fc 02", true);
builder.setBytes("0x0100649a", "a1 c0 85 00 01", true);
builder.setBytes("0x010064c5", "83 c4 08", true);
builder.setBytes("0x010064a3", "68 10 66 00 01", true);
builder.setBytes("0x010064ae", "83 c4 04", true);
builder.setBytes("0x01006500", "83 c4 08", true);
builder.setBytes("0x010065a7", "c7 45 fc ff ff ff ff", true);
builder.setBytes("0x010065b0", "c7 45 fc ff ff ff ff", true);
builder.createEquate("0x010064c5", "EIGHT", 8, 1);
builder.createEquate("0x01006500", "EIGHT", 8, 1);
builder.setBytes("0x01006455", "83 c4 04", true);
builder.createEquate("0x01006455", "FOUR", 4, 1);
builder.createEquate("0x010064ae", "FOUR", 4, 1);
builder.setBytes("0x01006140", "6a 01", true);
builder.setBytes("0x01006147", "6a 01", true);
builder.setBytes("0x0100621d", "6a 01", true);
builder.setBytes("0x010063f4", "6a 01", true);
builder.createEquate("0x01006140", "ONE", 1, 0);
builder.createEquate("0x01006147", "ONE", 1, 0);
builder.createEquate("0x0100621d", "ONE", 1, 1);
builder.createEquate("0x010063f4", "ONE", 1, 1);
builder.setBytes("0x010061a2", "6a 30", true);
builder.createEquate("0x010061a2", "THIRTY", 0x30, 0);
builder.setBytes("0x01006252", "6a 02", true);
builder.setBytes("0x0100644d", "6a 02", true);
builder.createEquate("0x01006252", "TWO", 2, 0);
builder.createEquate("0x010063da", "TWO", 2, 1);
builder.createEquate("0x0100644d", "TWO", 2, 0);
builder.setBytes("0x010060b2", "6a 02", true);
builder.setBytes("0x01006254", "6a 02", true);
builder.createEquate("0x010060b2", "TWO_alt", 2, 1);
builder.createEquate("0x01006254", "TWO_alt", 2, 0);
return builder.getProgram();
}
@Test
public void testApplyToOptions_SetEquate() {
putCursorOnOperand(0x01002a8e, 0);
SetEquateDialog d = showSetEquateDialog();
JRadioButton applyToCurrentButton =
(JRadioButton) findComponentByName(d.getComponent(), "applyToCurrent");
JRadioButton applyToSelectionButton =
(JRadioButton) findComponentByName(d.getComponent(), "applyToSelection");
final JRadioButton applyToAllButton =
(JRadioButton) findComponentByName(d.getComponent(), "applyToAll");
JCheckBox overwriteExistingEquatesButton =
(JCheckBox) findComponentByName(d.getComponent(), "Overwrite");
//dialog should come up with "current location" enabled and selected in all cases (even if a selection)
assertNotNull(applyToCurrentButton);
assertTrue(applyToCurrentButton.isEnabled());
assertTrue(applyToCurrentButton.isSelected());
//no selection in this case so should be disabled and not selected
assertNotNull(applyToSelectionButton);
assertFalse(applyToSelectionButton.isEnabled());
assertFalse(applyToSelectionButton.isSelected());
//entire program should be enabled but not selected
assertNotNull(applyToAllButton);
assertTrue(applyToAllButton.isEnabled());
assertFalse(applyToAllButton.isSelected());
//overwrite existing should be visible but not be enabled or selected
assertTrue(overwriteExistingEquatesButton.isVisible());
assertFalse(overwriteExistingEquatesButton.isEnabled());
assertFalse(overwriteExistingEquatesButton.isSelected());
//select applyToAll and verify that the other buttons respond accordingly
applyToAllButton.setSelected(true);
runSwing(
() -> applyToAllButton.getActionListeners()[0].actionPerformed(null));
program.flushEvents();
waitForSwing();
assertTrue(applyToAllButton.isSelected());
assertFalse(applyToCurrentButton.isSelected());
assertFalse(applyToSelectionButton.isSelected());
assertTrue(overwriteExistingEquatesButton.isEnabled());
assertFalse(overwriteExistingEquatesButton.isSelected());
close(d);
}
/*
* Tests that the current selection button is the default option on the
* Set Equate dialog if there is a range of addresses selected.
*
* @throws InterruptedException
* @throws InvocationTargetException
*/
@Test
public void testApplyToOptions_SetEquate_Selection() {
// put the cursor on a scalar
putCursorOnOperand(0x1004bbd, 0);
// make a selection containing the scalar above.
makeSelection(tool, program, addr("1004bbd"), addr("1004bc5"));
SetEquateDialog d = showSetEquateDialog();
JRadioButton applyToCurrentButton =
(JRadioButton) findComponentByName(d.getComponent(), "applyToCurrent");
final JRadioButton applyToSelectionButton =
(JRadioButton) findComponentByName(d.getComponent(), "applyToSelection");
final JRadioButton applyToAllButton =
(JRadioButton) findComponentByName(d.getComponent(), "applyToAll");
JCheckBox overwriteExistingEquatesButton =
(JCheckBox) findComponentByName(d.getComponent(), "Overwrite");
//change as of 7.0 - dialog should come up with "current location" enabled.
assertNotNull(applyToCurrentButton);
assertTrue(applyToCurrentButton.isEnabled());
assertFalse(applyToCurrentButton.isSelected());
//have selection in this case so should be enabled but not selected
assertNotNull(applyToSelectionButton);
assertTrue(applyToSelectionButton.isEnabled());
assertTrue(applyToSelectionButton.isSelected());
//entire program should be enabled but not selected
assertNotNull(applyToAllButton);
assertTrue(applyToAllButton.isEnabled());
assertFalse(applyToAllButton.isSelected());
//overwrite existing should be visible and enabled, but not selected
assertTrue(overwriteExistingEquatesButton.isVisible());
assertTrue(overwriteExistingEquatesButton.isEnabled());
assertFalse(overwriteExistingEquatesButton.isSelected());
//select applyToSelection and verify that the other buttons respond accordingly
applyToSelectionButton.setSelected(true);
runSwing(
() -> applyToSelectionButton.getActionListeners()[0].actionPerformed(null));
program.flushEvents();
waitForSwing();
assertFalse(applyToAllButton.isSelected());
assertFalse(applyToCurrentButton.isSelected());
assertTrue(applyToSelectionButton.isSelected());
assertTrue(overwriteExistingEquatesButton.isEnabled());
assertFalse(overwriteExistingEquatesButton.isSelected());
close(d);
}
@Test
public void testApplyToOptions_RenameEquate() {
putCursorOnOperand(0x010064c5, 1);
performAction(renameAction, cb.getProvider(), false);
SetEquateDialog d =
waitForDialogComponent(SetEquateDialog.class);
assertNotNull(d);
JRadioButton applyToCurrentButton =
(JRadioButton) findComponentByName(d.getComponent(), "applyToCurrent");
JRadioButton applyToSelectionButton =
(JRadioButton) findComponentByName(d.getComponent(), "applyToSelection");
JRadioButton applyToAllButton =
(JRadioButton) findComponentByName(d.getComponent(), "applyToAll");
JCheckBox overwriteExistingEquatesButton =
(JCheckBox) findComponentByName(d.getComponent(), "Overwrite");
//no selection so should come up selected
assertNotNull(applyToCurrentButton);
assertTrue(applyToCurrentButton.isEnabled());
assertTrue(applyToCurrentButton.isSelected());
//no selection in this case so should be disabled and not selected
assertNotNull(applyToSelectionButton);
assertFalse(applyToSelectionButton.isEnabled());
assertFalse(applyToSelectionButton.isSelected());
//entire program should be enabled but not selected
assertNotNull(applyToAllButton);
assertTrue(applyToAllButton.isEnabled());
assertFalse(applyToAllButton.isSelected());
//overwrite existing should not be visible, enabled, or selected
assertFalse(overwriteExistingEquatesButton.isVisible());
assertFalse(overwriteExistingEquatesButton.isEnabled());
assertFalse(overwriteExistingEquatesButton.isSelected());
close(d);
}
@Test
public void testApplyToOptions_RenameEquate_Selection() {
putCursorOnOperand(0x010064c5, 1);
selectRange(addr("10064c5"), addr("10064ce"));
performAction(renameAction, cb.getProvider(), false);
SetEquateDialog d =
waitForDialogComponent(SetEquateDialog.class);
assertNotNull(d);
JRadioButton applyToCurrentButton =
(JRadioButton) findComponentByName(d.getComponent(), "applyToCurrent");
JRadioButton applyToSelectionButton =
(JRadioButton) findComponentByName(d.getComponent(), "applyToSelection");
JRadioButton applyToAllButton =
(JRadioButton) findComponentByName(d.getComponent(), "applyToAll");
JCheckBox overwriteExistingEquatesButton =
(JCheckBox) findComponentByName(d.getComponent(), "Overwrite");
//change as of 7.0 - dialog should come up with "current location" enabled.
assertNotNull(applyToCurrentButton);
assertTrue(applyToCurrentButton.isEnabled());
assertFalse(applyToCurrentButton.isSelected());
//have selection in this case so should be enabled but not selected
assertNotNull(applyToSelectionButton);
assertTrue(applyToSelectionButton.isEnabled());
assertTrue(applyToSelectionButton.isSelected());
//entire program should be enabled but not selected
assertNotNull(applyToAllButton);
assertTrue(applyToAllButton.isEnabled());
assertFalse(applyToAllButton.isSelected());
//overwrite existing should not be visible, enabled, or selected
assertFalse(overwriteExistingEquatesButton.isVisible());
assertFalse(overwriteExistingEquatesButton.isEnabled());
assertFalse(overwriteExistingEquatesButton.isSelected());
close(d);
}
@Test
public void testApplyEnumInSelection_DoOnSubOperands() throws Exception {
Address addr1 = addr(0x01004bdd);
Address addr2 = addr(0x01004c1f);
selectRange(addr1, addr2);
EquateTable et = program.getEquateTable();
UniversalID id = createEnum_myEnum().getUniversalID();
createEquate(0x01004c0d, 1, "bob", 0x0);
assertNotNull(et.getEquate(addr(0x01004c0d), 1, 0x0));
ApplyEnumDialog d = performApplyEnum();
setToggleButtonSelected(d.getComponent(), "subOpCB", true);
DropDownSelectionTextField<DataType> textField = d.getEditor().getDropDownTextField();
triggerText(textField, "myEnum");
waitForSwing();
triggerEnter(textField);
waitForTasks();
assertFalse(et.getEquate(addr(0x01004c0d), 1, 0x0).getName().equals("bob"));
assertEquatesAppliedFromEnum(id, 0, 2, 4);
assertEquatesNotApplied(id, 3, 5);
// Special cases right below
String errorMsg = "Equate not applied from Enum . No equate for ";
Address twoSameScalarAddress = addr(0x01004bdf);
assertNotNull(errorMsg, et.getEquate(twoSameScalarAddress, 1, 0x2));
Address twoDifferentScalarsAddress = addr(0x01004c0d);
assertNotNull(errorMsg, et.getEquate(twoDifferentScalarsAddress, 0, 0x2));
assertNotNull(errorMsg, et.getEquate(twoDifferentScalarsAddress, 1, 0x0));
}
@Test
public void testApplyEnumInSelection_DontDoOnSubOperands() throws Exception {
Address addr1 = addr(0x01004c0b);
Address addr2 = addr(0x01004c17);
selectRange(addr1, addr2);
UniversalID id = createEnum_myEnum().getUniversalID();
ApplyEnumDialog d = performApplyEnum();
setToggleButtonSelected(d.getComponent(), "subOpCB", false);
DropDownSelectionTextField<DataType> textField = d.getEditor().getDropDownTextField();
triggerText(textField, "myEnum");
waitForSwing();
triggerEnter(textField);
assertEquatesAppliedFromEnum(id, 0);
assertEquatesNotApplied(id, 2);
}
private SetEquateDialog showSetEquateDialog() {
performAction(setAction, cb.getProvider(), false);
SetEquateDialog d = waitForDialogComponent(SetEquateDialog.class);
assertEquals("Set Equate", d.getTitle());
return d;
}
private void fireOperandFieldLocationEvent(Address addr, int opIndex) {
Instruction inst = listing.getInstructionAt(addr);
if (inst == null) {
fail("Must have a code unit to use this method");
}
String str = CodeUnitFormat.DEFAULT.getOperandRepresentationString(inst, opIndex);
OperandFieldLocation loc =
new OperandFieldLocation(program, addr, null, null, str, opIndex, 0);
tool.firePluginEvent(new ProgramLocationPluginEvent("test", loc, program));
waitForSwing();
}
private void putCursorOnOperand(long offset, int opIndex) {
Address addr = addr(offset);
fireOperandFieldLocationEvent(addr, opIndex);
waitForBusyTool(tool);
}
private void assertEquatesAppliedFromEnum(UniversalID id, int... values) {
EquateTable et = program.getEquateTable();
for (int value : values) {
String fullName = EquateManager.formatNameForEquate(id, value);
assertNotNull("Equate not applied from Enum . No equate for " + fullName,
et.getEquate(fullName));
}
}
private void assertEquatesNotApplied(UniversalID id, int... values) {
EquateTable et = program.getEquateTable();
for (int value : values) {
String fullName = EquateManager.formatNameForEquate(id, value);
assertNull("Equate should not have been applied " + fullName, et.getEquate(fullName));
}
}
private void createEquate(long address, int opIndex, String equateName, long equateValue)
throws Exception {
EquateTable equateTable = program.getEquateTable();
int transactionID = program.startTransaction("Test - Create Equate at " + address);
Equate equate = equateTable.createEquate(equateName, equateValue);
equate.addReference(addr(address), opIndex);
program.endTransaction(transactionID, true);
flushProgramEvents();
}
private AddressSet selectRange(Address start, Address end) {
AddressSet addrs = toAddressSet(start, end);
makeSelection(tool, program, addrs);
return addrs;
}
private void flushProgramEvents() {
program.flushEvents();
waitForBusyTool(tool);
}
private Enum createEnum_myEnum() {
DataTypeManager dataTypeManager = program.getDataTypeManager();
int id = dataTypeManager.startTransaction("EquatePluginTest enum");
Enum enumm = new EnumDataType("myEnum", 1);
enumm.add("zeroScalar", 0);
enumm.add("oneScalar", 1);
enumm.add("twoScalar", 2);
enumm.add("threeScalar", 3);
enumm.add("fourScalar", 4);
enumm.add("fiveScalar", 5);
enumm = (Enum) dataTypeManager.addDataType(enumm, null);
dataTypeManager.endTransaction(id, true);
return enumm;
}
private ApplyEnumDialog performApplyEnum() {
ComponentProvider provider = tool.getComponentProvider(PluginConstants.CODE_BROWSER);
DockingActionIf action = getAction(equatePlugin, "Apply Enum");
performAction(action, provider, false);
ApplyEnumDialog d = waitForDialogComponent(ApplyEnumDialog.class);
return d;
}
}

View file

@ -81,7 +81,7 @@ public class EquateTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
}
private Program buildProgram() throws Exception {
ToyProgramBuilder builder = new ToyProgramBuilder("notepad", true);
ToyProgramBuilder builder = new ToyProgramBuilder("sample", true);
builder.createMemory("test", "0x01006000", 0x1000);
builder.createEquate("0x010060f0", "ANOTHER_ONE", 1, 0);
@ -116,7 +116,7 @@ public class EquateTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
@Test
public void testEquateTableView() throws Exception {
// verify that the equate table shows the equates and the references
// verify that the equate table shows the equates and the references
assertNotNull(refsTable);
assertNotNull(refsModel);
assertEquals(1, refsModel.getRowCount());
@ -137,7 +137,7 @@ public class EquateTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
@Test
public void testRefsNavigation() {
// select a row in the refs table; the browser should go there
// select a row in the refs table; the browser should go there
setRowSelection(equatesTable, 1, 1);
@ -271,7 +271,7 @@ public class EquateTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
assertTrue(pluginAction.isEnabled());
performAction(pluginAction, false);
OptionDialog d = waitForDialogComponent(tool.getToolFrame(), OptionDialog.class, 2000);
OptionDialog d = waitForDialogComponent(OptionDialog.class);
assertNotNull(d);
assertEquals("Delete Equate?", d.getTitle());
@ -307,7 +307,7 @@ public class EquateTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
assertTrue(pluginAction.isEnabled());
performAction(pluginAction, false);
OptionDialog d = waitForDialogComponent(tool.getToolFrame(), OptionDialog.class, 2000);
OptionDialog d = waitForDialogComponent(OptionDialog.class);
assertNotNull(d);
assertEquals("Delete Equate?", d.getTitle());
@ -328,7 +328,7 @@ public class EquateTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
//==================================================================================================
// Private methods
//==================================================================================================
//==================================================================================================
private void setRowSelection(JTable table, int rowStart, int rowEnd) {
waitForSwing();

View file

@ -169,7 +169,7 @@ class EnumDB extends DataTypeDB implements Enum {
checkIsValid();
initializeIfNeeded();
List<String> list = valueMap.get(value);
if (list == null) {
if (list == null || list.isEmpty()) {
return new String[0];
}
return list.toArray(new String[0]);

View file

@ -66,9 +66,6 @@ public class EquateDB extends DatabaseObject implements Equate {
return true;
}
/**
* @see ghidra.program.model.symbol.Equate#addReference(ghidra.program.model.address.Address, int)
*/
@Override
public void addReference(Address refAddr, int opIndex) {
checkDeleted();
@ -95,9 +92,6 @@ public class EquateDB extends DatabaseObject implements Equate {
}
}
/**
* @see ghidra.program.model.symbol.Equate#addReference(long, ghidra.program.model.address.Address)
*/
@Override
public void addReference(long dynamicHash, Address refAddr) {
checkDeleted();
@ -145,9 +139,6 @@ public class EquateDB extends DatabaseObject implements Equate {
return opIndex;
}
/**
* @see ghidra.program.model.symbol.Equate#getName()
*/
@Override
public String getName() {
checkIsValid();
@ -178,9 +169,6 @@ public class EquateDB extends DatabaseObject implements Equate {
return null;
}
/**
* @see ghidra.program.model.symbol.Equate#getReferenceCount()
*/
@Override
public int getReferenceCount() {
checkIsValid();
@ -193,9 +181,6 @@ public class EquateDB extends DatabaseObject implements Equate {
return 0;
}
/**
* @see ghidra.program.model.symbol.Equate#getReferences()
*/
@Override
public EquateReference[] getReferences() {
checkIsValid();
@ -208,9 +193,6 @@ public class EquateDB extends DatabaseObject implements Equate {
return new EquateReference[0];
}
/**
* @see ghidra.program.model.symbol.Equate#getReferences(Address)
*/
@Override
public List<EquateReference> getReferences(Address refAddr) {
Lock lock = equateMgr.getLock();
@ -229,27 +211,18 @@ public class EquateDB extends DatabaseObject implements Equate {
return new ArrayList<>();
}
/**
* @see ghidra.program.model.symbol.Equate#getValue()
*/
@Override
public long getValue() {
checkIsValid();
return record.getLongValue(EquateDBAdapter.VALUE_COL);
}
/**
* @see ghidra.program.model.symbol.Equate#getDisplayValue()
*/
@Override
public String getDisplayValue() {
long val = getValue();
return ((val < 0) ? "-" : "") + "0x" + Long.toHexString(Math.abs(val));
}
/**
* @see ghidra.program.model.symbol.Equate#removeReference(ghidra.program.model.address.Address, int)
*/
@Override
public void removeReference(Address refAddr, int opIndex) {
checkDeleted();
@ -261,9 +234,6 @@ public class EquateDB extends DatabaseObject implements Equate {
}
}
/**
* @see ghidra.program.model.symbol.Equate#removeReference(long, ghidra.program.model.address.Address)
*/
@Override
public void removeReference(long dynamicHash, Address refAddr) {
checkDeleted();
@ -275,9 +245,6 @@ public class EquateDB extends DatabaseObject implements Equate {
}
}
/**
* @see ghidra.program.model.symbol.Equate#renameEquate(java.lang.String)
*/
@Override
public void renameEquate(String newName) throws DuplicateNameException, InvalidInputException {
Lock lock = equateMgr.getLock();
@ -294,6 +261,7 @@ public class EquateDB extends DatabaseObject implements Equate {
throw new DuplicateNameException("Equate named " + newName + " already exists");
}
catch (NotFoundException e) {
// this is expected, since an existing name will be an unwanted duplicate
}
catch (IOException e) {
equateMgr.dbError(e);
@ -333,10 +301,6 @@ public class EquateDB extends DatabaseObject implements Equate {
return getName().startsWith(EquateManager.DATATYPE_TAG);
}
/**
*
* @see java.lang.Object#equals(Object)
*/
@Override
public boolean equals(Object obj) {
@ -350,26 +314,19 @@ public class EquateDB extends DatabaseObject implements Equate {
if (getClass() != obj.getClass()) {
return false;
}
Equate eq = (Equate) obj;
Equate eq = (Equate) obj;
if (getValue() != eq.getValue()) {
return false;
}
return getName().equals(eq.getName());
}
/**
*
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
return getName().hashCode();
}
/**
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return getDisplayName();

View file

@ -37,6 +37,13 @@ public interface Enum extends DataType {
*/
public String getName(long value);
/**
* Returns all names that map to the given value.
* @param value value for the enum entries.
* @return all names; null if there is not name for the given value.
*/
public String[] getNames(long value);
/**
* Get the comment for the given name.
* @param name name of the entry.

View file

@ -96,6 +96,15 @@ public class EnumDataType extends GenericDataType implements Enum {
return list.get(0);
}
@Override
public String[] getNames(long value) {
List<String> list = valueMap.get(value);
if (list == null || list.isEmpty()) {
return null;
}
return list.toArray(new String[0]);
}
@Override
public String getComment(String valueName) {
String comment = commentMap.get(valueName);