GT-3227 - review fixes

This commit is contained in:
dragonmacher 2019-10-15 15:45:48 -04:00
parent 665a83d6c0
commit c4b6058c31
4 changed files with 185 additions and 279 deletions

View file

@ -15,13 +15,20 @@
*/ */
package ghidra.app.plugin.core.equate; package ghidra.app.plugin.core.equate;
import java.util.*;
import ghidra.framework.cmd.BackgroundCommand; import ghidra.framework.cmd.BackgroundCommand;
import ghidra.framework.model.DomainObject; import ghidra.framework.model.DomainObject;
import ghidra.program.database.symbol.EquateManager;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSetView; import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.data.Enum; import ghidra.program.model.data.Enum;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.*;
import ghidra.program.model.scalar.Scalar;
import ghidra.program.model.symbol.Equate;
import ghidra.program.model.symbol.EquateTable; import ghidra.program.model.symbol.EquateTable;
import ghidra.util.exception.CancelledException; import ghidra.util.Msg;
import ghidra.util.exception.*;
import ghidra.util.task.TaskMonitor; import ghidra.util.task.TaskMonitor;
public class CreateEnumEquateCommand extends BackgroundCommand { public class CreateEnumEquateCommand extends BackgroundCommand {
@ -30,6 +37,7 @@ public class CreateEnumEquateCommand extends BackgroundCommand {
private Enum enoom; private Enum enoom;
private Program program; private Program program;
private boolean shouldDoOnSubOps; private boolean shouldDoOnSubOps;
private EquateTable equateTable;
/** /**
* Constructor * Constructor
@ -41,18 +49,23 @@ public class CreateEnumEquateCommand extends BackgroundCommand {
*/ */
public CreateEnumEquateCommand(Program program, AddressSetView addresses, Enum enoom, public CreateEnumEquateCommand(Program program, AddressSetView addresses, Enum enoom,
boolean shouldDoOnSubOps) { boolean shouldDoOnSubOps) {
this.program = program; this.program = Objects.requireNonNull(program);
this.addresses = addresses; this.addresses = Objects.requireNonNull(addresses);
this.enoom = enoom; this.enoom = Objects.requireNonNull(enoom);
this.shouldDoOnSubOps = shouldDoOnSubOps; this.shouldDoOnSubOps = shouldDoOnSubOps;
} }
@Override
public String getName() {
return "Create Enum Equate Command";
}
@Override @Override
public boolean applyTo(DomainObject obj, TaskMonitor monitor) { public boolean applyTo(DomainObject obj, TaskMonitor monitor) {
EquateTable et = program.getEquateTable(); equateTable = program.getEquateTable();
try { try {
et.applyEnum(addresses, enoom, monitor, shouldDoOnSubOps); applyEnum(monitor);
} }
catch (CancelledException e) { catch (CancelledException e) {
return false; return false;
@ -60,8 +73,95 @@ public class CreateEnumEquateCommand extends BackgroundCommand {
return true; return true;
} }
@Override public void applyEnum(TaskMonitor monitor) throws CancelledException {
public String getName() {
return "Create Enum Equate Command"; Listing listing = program.getListing();
InstructionIterator it = listing.getInstructions(addresses, true);
monitor.initialize(addresses.getNumAddresses());
while (it.hasNext()) {
monitor.checkCanceled();
Instruction instruction = it.next();
processsEquates(instruction);
monitor.incrementProgress(instruction.getLength());
}
} }
private void processsEquates(Instruction instruction) {
for (int opIndex = 0; opIndex < instruction.getNumOperands(); opIndex++) {
if (!shouldDoOnSubOps) {
// Only apply equates to scalars that are not contained in sub operands.
Scalar scalar = instruction.getScalar(opIndex);
maybeCreateEquateOnScalar(instruction, opIndex, scalar);
}
else {
// Apply equates to scalars in the sub operands as well.
List<?> subOperands = instruction.getDefaultOperandRepresentationList(opIndex);
for (Object subOp : subOperands) {
maybeCreateEquateOnScalar(instruction, opIndex, subOp);
}
}
}
}
private void maybeCreateEquateOnScalar(Instruction instruction, int opIndex,
Object operandRepresentation) {
if (!(operandRepresentation instanceof Scalar)) {
return;
}
Scalar scalar = (Scalar) operandRepresentation;
int enoomLength = enoom.getLength();
boolean anyValuesMatch = Arrays.stream(enoom.getValues()).anyMatch(enumValue -> {
return scalar.equals(new Scalar(enoomLength * 8, enumValue, scalar.isSigned()));
});
if (!anyValuesMatch) {
return;
}
if (program.getDataTypeManager().findDataTypeForID(enoom.getUniversalID()) == null) {
enoom = (Enum) program.getDataTypeManager().addDataType(enoom, null);
}
Address addr = instruction.getAddress();
removeUnusedEquates(opIndex, scalar, addr);
long value = scalar.getValue();
String equateName = EquateManager.formatNameForEquate(enoom.getUniversalID(), value);
Equate equate = getOrCreateEquate(equateName, value);
equate.addReference(addr, opIndex);
}
private void removeUnusedEquates(int opIndex, Scalar scalar, Address addr) {
Equate existingEquate = equateTable.getEquate(addr, opIndex, scalar.getValue());
if (existingEquate != null) {
if (existingEquate.getReferenceCount() <= 1) {
equateTable.removeEquate(existingEquate.getName());
}
}
}
private Equate getOrCreateEquate(String name, long value) {
Equate equate = equateTable.getEquate(name);
if (equate != null) {
return equate;
}
try {
equate = equateTable.createEquate(name, value);
}
catch (DuplicateNameException | InvalidInputException e) {
// These should not happen:
// Duplicate will not happen since we checked for the existence first; Invalid
// can't happen since we built the name ourselves (we are assuming)
Msg.error(this, "Unexpected error creating equate", e); // just in case
}
return equate;
}
} }

View file

@ -38,16 +38,13 @@ import ghidra.program.model.data.*;
import ghidra.program.model.data.Enum; import ghidra.program.model.data.Enum;
import ghidra.test.AbstractGhidraHeadedIntegrationTest; import ghidra.test.AbstractGhidraHeadedIntegrationTest;
import ghidra.test.TestEnv; import ghidra.test.TestEnv;
import ghidra.util.InvalidNameException;
/** /**
* Tests for the make enum from a selection of enums action * Tests for the 'make enum from a selection' action
*/ */
public class CreateEnumFromSelectionTest extends AbstractGhidraHeadedIntegrationTest { public class CreateEnumFromSelectionTest extends AbstractGhidraHeadedIntegrationTest {
private static final String PROGRAM_FILENAME = "notepad"; private static final String PROGRAM_FILENAME = "notepad";
private static final int TASK_TIMEOUT = 2000;
//private Program program;
private PluginTool tool; private PluginTool tool;
private ProgramDB program; private ProgramDB program;
private TestEnv env; private TestEnv env;
@ -57,10 +54,6 @@ public class CreateEnumFromSelectionTest extends AbstractGhidraHeadedIntegration
private ArchiveRootNode archiveRootNode; private ArchiveRootNode archiveRootNode;
private ArchiveNode programNode; private ArchiveNode programNode;
public CreateEnumFromSelectionTest() {
super();
}
@Before @Before
public void setUp() throws Exception { public void setUp() throws Exception {
env = new TestEnv(); env = new TestEnv();
@ -93,16 +86,6 @@ public class CreateEnumFromSelectionTest extends AbstractGhidraHeadedIntegration
return builder.getProgram(); return builder.getProgram();
} }
// @Override
// protected void tearDown() throws Exception {
// executeOnSwingWithoutBlocking(new Runnable() {
// public void run() {
// ProgramManager pm = tool.getService(ProgramManager.class);
// pm.closeProgram();
//
// }
// });
// }
@After @After
public void tearDown() throws Exception { public void tearDown() throws Exception {
@ -116,8 +99,7 @@ public class CreateEnumFromSelectionTest extends AbstractGhidraHeadedIntegration
}); });
// this handles the save changes dialog and potential analysis dialogs // this handles the save changes dialog and potential analysis dialogs
closeAllWindowsAndFrames(); closeAllWindows();
env.release(program);
env.dispose(); env.dispose();
} }
@ -174,7 +156,7 @@ public class CreateEnumFromSelectionTest extends AbstractGhidraHeadedIntegration
} }
}); });
Window window = waitForWindow(tool.getToolFrame(), "Name new ENUM", TASK_TIMEOUT); Window window = waitForWindow("Name new ENUM");
assertNotNull(window); assertNotNull(window);
final JTextField tf = findComponent(window, JTextField.class); final JTextField tf = findComponent(window, JTextField.class);
@ -274,7 +256,7 @@ public class CreateEnumFromSelectionTest extends AbstractGhidraHeadedIntegration
} }
}); });
Window window = waitForWindow(tool.getToolFrame(), "Name new ENUM", TASK_TIMEOUT); Window window = waitForWindow("Name new ENUM");
assertNotNull(window); assertNotNull(window);
final JTextField tf = findComponent(window, JTextField.class); final JTextField tf = findComponent(window, JTextField.class);
@ -283,7 +265,7 @@ public class CreateEnumFromSelectionTest extends AbstractGhidraHeadedIntegration
tf.setText("myNewEnum"); tf.setText("myNewEnum");
pressButtonByText(window, "OK"); pressButtonByText(window, "OK");
Window window2 = waitForWindow(tool.getToolFrame(), "Duplicate ENUM Name", TASK_TIMEOUT); Window window2 = waitForWindow("Duplicate ENUM Name");
assertNotNull(window2); assertNotNull(window2);
final JTextField tf2 = findComponent(window2, JTextField.class); final JTextField tf2 = findComponent(window2, JTextField.class);
@ -334,27 +316,6 @@ public class CreateEnumFromSelectionTest extends AbstractGhidraHeadedIntegration
} }
private DataTypeNode createEnum(CategoryNode categoryNode, String newEnumName, int size) {
Category category = categoryNode.getCategory();
DataTypeManager dataTypeManager = category.getDataTypeManager();
int id = dataTypeManager.startTransaction("new category");
Enum newEnum = new EnumDataType(newEnumName, size);
category.addDataType(newEnum, null);
dataTypeManager.endTransaction(id, true);
waitForTree();
return getDataTypeNode(categoryNode, newEnumName);
}
private DataTypeNode getDataTypeNode(GTreeNode parent, String dataTypeName) {
List<GTreeNode> children = parent.getChildren();
for (GTreeNode node : children) {
if (node instanceof DataTypeNode && node.getName().equals(dataTypeName)) {
return (DataTypeNode) node;
}
}
return null;
}
private void expandNode(GTreeNode node) { private void expandNode(GTreeNode node) {
tree.expandPath(node); tree.expandPath(node);
waitForTree(); waitForTree();
@ -373,37 +334,4 @@ public class CreateEnumFromSelectionTest extends AbstractGhidraHeadedIntegration
private void waitForTree() { private void waitForTree() {
waitForTree(tree); waitForTree(tree);
} }
private void waitForProgram() throws Exception {
program.flushEvents();
waitForTasks();
waitForPostedSwingRunnables();
}
private CategoryNode createCategory(CategoryNode categoryNode, String newCategoryName) {
Category category = categoryNode.getCategory();
DataTypeManager dataTypeManager = category.getDataTypeManager();
int id = dataTypeManager.startTransaction("new category");
try {
category.createCategory(newCategoryName);
}
catch (InvalidNameException e) {
// shouldn't happen
}
dataTypeManager.endTransaction(id, true);
waitForTree();
CategoryNode node = getCategoryNode(categoryNode, newCategoryName);
return node;
}
private CategoryNode getCategoryNode(GTreeNode parent, String categoryName) {
List<GTreeNode> children = parent.getChildren();
for (GTreeNode node : children) {
if (node instanceof CategoryNode && node.getName().equals(categoryName)) {
return (CategoryNode) node;
}
}
return null;
}
} }

View file

@ -17,9 +17,6 @@ package ghidra.program.database.symbol;
import java.io.IOException; import java.io.IOException;
import java.util.*; import java.util.*;
import java.util.function.Consumer;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import db.*; import db.*;
import db.util.ErrorHandler; import db.util.ErrorHandler;
@ -27,14 +24,12 @@ import ghidra.program.database.*;
import ghidra.program.database.map.AddressKeyAddressIterator; import ghidra.program.database.map.AddressKeyAddressIterator;
import ghidra.program.database.map.AddressMap; import ghidra.program.database.map.AddressMap;
import ghidra.program.model.address.*; import ghidra.program.model.address.*;
import ghidra.program.model.data.Enum;
import ghidra.program.model.listing.*;
import ghidra.program.model.scalar.Scalar;
import ghidra.program.model.symbol.Equate; import ghidra.program.model.symbol.Equate;
import ghidra.program.model.symbol.EquateTable; import ghidra.program.model.symbol.EquateTable;
import ghidra.program.util.ChangeManager; import ghidra.program.util.ChangeManager;
import ghidra.program.util.EquateInfo; import ghidra.program.util.EquateInfo;
import ghidra.util.*; import ghidra.util.Lock;
import ghidra.util.UniversalID;
import ghidra.util.exception.*; import ghidra.util.exception.*;
import ghidra.util.task.TaskMonitor; import ghidra.util.task.TaskMonitor;
@ -114,112 +109,6 @@ public class EquateManager implements EquateTable, ErrorHandler, ManagerDB {
program.dbError(e); program.dbError(e);
} }
@Override
public void applyEnum(AddressSetView addresses, Enum enoom, TaskMonitor monitor,
boolean shouldDoOnSubOps) throws CancelledException {
if (addresses == null) {
throw new IllegalArgumentException("Can't apply Enum over null addresses");
}
if (enoom == null) {
throw new IllegalArgumentException("Data Type is null");
}
Consumer<Instruction> applyEquates = instruction -> {
if (monitor.isCancelled()) {
return;
}
for (int opIndex = 0; opIndex < instruction.getNumOperands(); opIndex++) {
if (!shouldDoOnSubOps) {
// Only apply equates to scalars that are not contained in sub operands.
Scalar scalar = instruction.getScalar(opIndex);
maybeCreateEquateOnScalar(enoom, instruction, opIndex, scalar);
}
else {
// Apply equates to scalars in the sub operands as well.
List<?> subOperands = instruction.getDefaultOperandRepresentationList(opIndex);
for (Object subOp : subOperands) {
maybeCreateEquateOnScalar(enoom, instruction, opIndex, subOp);
}
}
}
};
Listing listing = program.getListing();
InstructionIterator it = listing.getInstructions(addresses, true);
Stream<Instruction> instructions = StreamSupport.stream(it.spliterator(), false);
try {
lock.acquire();
instructions.forEach(applyEquates);
}
finally {
lock.release();
}
}
private void maybeCreateEquateOnScalar(Enum enoom, Instruction instruction, int opIndex,
Object operandRepresentation) {
if (!(operandRepresentation instanceof Scalar)) {
return;
}
Scalar scalar = (Scalar) operandRepresentation;
int enoomLength = enoom.getLength();
boolean anyValuesMatch = Arrays.stream(enoom.getValues()).anyMatch(enumValue -> {
return scalar.equals(new Scalar(enoomLength * 8, enumValue, scalar.isSigned()));
});
if (!anyValuesMatch) {
return;
}
if (program.getDataTypeManager().findDataTypeForID(enoom.getUniversalID()) == null) {
enoom = (Enum) program.getDataTypeManager().addDataType(enoom, null);
}
Address addr = instruction.getAddress();
removeUnusedEquates(opIndex, scalar, addr);
long value = scalar.getValue();
String equateName = EquateManager.formatNameForEquate(enoom.getUniversalID(), value);
Equate equate = getOrCreateEquate(equateName, value);
equate.addReference(addr, opIndex);
}
private void removeUnusedEquates(int opIndex, Scalar scalar, Address addr) {
Equate existingEquate = getEquate(addr, opIndex, scalar.getValue());
if (existingEquate != null) {
if (existingEquate.getReferenceCount() <= 1) {
removeEquate(existingEquate.getName());
}
}
}
private Equate getOrCreateEquate(String name, long value) {
Equate equate = getEquate(name);
if (equate != null) {
return equate;
}
try {
equate = createEquate(name, value);
}
catch (DuplicateNameException | InvalidInputException e) {
// These should not happen:
// Duplicate will not happen since we checked for the existence first; Invalid
// can't happen since we built the name ourselves (we are assuming)
Msg.error(this, "Unexpected error creating equate", e); // just in case
}
return equate;
}
@Override @Override
public Equate createEquate(String name, long value) public Equate createEquate(String name, long value)
throws DuplicateNameException, InvalidInputException { throws DuplicateNameException, InvalidInputException {

View file

@ -21,9 +21,9 @@ import java.util.Iterator;
import java.util.List; import java.util.List;
import ghidra.program.model.address.*; import ghidra.program.model.address.*;
import ghidra.program.model.data.Enum;
import ghidra.util.exception.*; import ghidra.util.exception.*;
import ghidra.util.task.TaskMonitor; import ghidra.util.task.TaskMonitor;
/** /**
* EquateTable manages all equates for program. An equate defines a relationship * EquateTable manages all equates for program. An equate defines a relationship
* between a scalar value and a string whereby the scalar may be represented by * between a scalar value and a string whereby the scalar may be represented by
@ -32,40 +32,25 @@ import ghidra.util.task.TaskMonitor;
*/ */
public interface EquateTable { public interface EquateTable {
/** /**
* Creates equates and/or adds references for scalars * Creates a new equate
* in the given address set using the given data type. * @param name the name to associate with the given value.
* The data type given must be an enumeration data type. * @param value the value to associate with the given name.
* @param addrSet the address set to use. * @return the equate
* @param dataType the data type to use. * @exception DuplicateNameException thrown if name is already in use
* @param monitor task monitor to cancel the remove operation * as an equate.
* @param shouldDoOnSubOps true if the enum should be applied inside sub-operands as well. * @throws InvalidInputException if name contains blank characters,
* * is zero length, or is null
* @throws CancelledException if the operation is cancelled
* @throws IllegalArgumentException if the dataType is null or not an enum.
*/ */
public void applyEnum(AddressSetView addrSet, Enum dataType, TaskMonitor monitor, public Equate createEquate(String name, long value)
boolean shouldDoOnSubOps) throws CancelledException; throws DuplicateNameException, InvalidInputException;
/** /**
* Creates a new equate * Removes the equate from the program.
* @param name the name to associate with the given value. * @param name the name of the equate to remove.
* @param value the value to associate with the given name. * @return true if the equate existed, false otherwise.
* @exception DuplicateNameException thrown if name is already in use */
* as an equate.
* @throws InvalidInputException if name contains blank characters,
* is zero length, or is null
*/
public Equate createEquate(String name,long value)
throws DuplicateNameException, InvalidInputException;
/**
* Removes the equate from the program.
* @param name the name of the equate to remove.
* @return true if the equate existed, false otherwise.
*/
public boolean removeEquate(String name); public boolean removeEquate(String name);
/** /**
@ -75,24 +60,25 @@ public interface EquateTable {
* @param monitor task monitor to cancel the remove operation * @param monitor task monitor to cancel the remove operation
* @throws CancelledException if the operation was cancelled. * @throws CancelledException if the operation was cancelled.
*/ */
public void deleteAddressRange(Address start, Address end, TaskMonitor monitor) throws CancelledException; public void deleteAddressRange(Address start, Address end, TaskMonitor monitor)
throws CancelledException;
/** /**
* Returns the equate with the given name, null if no such equate * Returns the equate with the given name, null if no such equate exists
* exists. * @param name the of the equate to be retrieved
* @param name the of the equate to be retrieved. * @return the equate
*/ */
public Equate getEquate(String name); public Equate getEquate(String name);
/** /**
* Returns the first equate found that is associated with the given * Returns the first equate found that is associated with the given
* value at the given reference address and operand position; * value at the given reference address and operand position;
* @param reference address where the equate is used. * @param reference address where the equate is used.
* @param opndPosition the operand index of the operand where the equate is used. * @param opndPosition the operand index of the operand where the equate is used.
* @param value the value where the equate is used. * @param value the value where the equate is used.
* @return the equate or null if there is no such equate. * @return the equate or null if there is no such equate.
*/ */
public Equate getEquate(Address reference, int opndPosition, long value); public Equate getEquate(Address reference, int opndPosition, long value);
/** /**
* Returns the equates (one for each scalar) at the given reference address * Returns the equates (one for each scalar) at the given reference address
@ -102,7 +88,7 @@ public interface EquateTable {
* @return the list of equates or empty list if there is no such equate. * @return the list of equates or empty list if there is no such equate.
*/ */
public List<Equate> getEquates(Address reference, int opndPosition); public List<Equate> getEquates(Address reference, int opndPosition);
/** /**
* Returns the equates (one for each scalar and opIndex) at the given reference address. * Returns the equates (one for each scalar and opIndex) at the given reference address.
* For an instruction a given operand can have multiple scalars. * For an instruction a given operand can have multiple scalars.
@ -111,39 +97,42 @@ public interface EquateTable {
*/ */
public List<Equate> getEquates(Address reference); public List<Equate> getEquates(Address reference);
/** /**
* Returns an address iterator over all the addresses where * Returns an address iterator over all the addresses where
* equates have been set. * equates have been set.
*/ * @return the iterator
public AddressIterator getEquateAddresses(); */
public AddressIterator getEquateAddresses();
/**
* Returns all equates defined for value. /**
* @param value the value to get all equates for. * Returns all equates defined for value.
*/ * @param value the value to get all equates for.
public List<Equate> getEquates(long value); * @return the equates
*/
/** public List<Equate> getEquates(long value);
* Returns an iterator over all equates.
*/ /**
* Returns an iterator over all equates.
* @return the iterator
*/
public Iterator<Equate> getEquates(); public Iterator<Equate> getEquates();
/** /**
* Return an address iterator over each address with an * Return an address iterator over each address with an
* equate reference starting at the start address. * equate reference starting at the start address.
* *
* @param start start address * @param start start address
* @return an AddressIterator over addresses with defined equate references * @return an AddressIterator over addresses with defined equate references
*/ */
public AddressIterator getEquateAddresses(Address start); public AddressIterator getEquateAddresses(Address start);
/** /**
* Return an address iterator over each address with an * Return an address iterator over each address with an
* equate reference that is in the specified address set. * equate reference that is in the specified address set.
* *
* @param asv the address set * @param asv the address set
* @return AddressIterator over addresses with defined equate references * @return AddressIterator over addresses with defined equate references
*/ */
public AddressIterator getEquateAddresses(AddressSetView asv); public AddressIterator getEquateAddresses(AddressSetView asv);
} }