diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/equate/CreateEnumEquateCommand.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/equate/CreateEnumEquateCommand.java index 4ea10b3bfd..d3d87b9dfc 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/equate/CreateEnumEquateCommand.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/equate/CreateEnumEquateCommand.java @@ -15,13 +15,20 @@ */ package ghidra.app.plugin.core.equate; +import java.util.*; + import ghidra.framework.cmd.BackgroundCommand; 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.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.util.exception.CancelledException; +import ghidra.util.Msg; +import ghidra.util.exception.*; import ghidra.util.task.TaskMonitor; public class CreateEnumEquateCommand extends BackgroundCommand { @@ -30,6 +37,7 @@ public class CreateEnumEquateCommand extends BackgroundCommand { private Enum enoom; private Program program; private boolean shouldDoOnSubOps; + private EquateTable equateTable; /** * Constructor @@ -41,18 +49,23 @@ public class CreateEnumEquateCommand extends BackgroundCommand { */ public CreateEnumEquateCommand(Program program, AddressSetView addresses, Enum enoom, boolean shouldDoOnSubOps) { - this.program = program; - this.addresses = addresses; - this.enoom = enoom; + this.program = Objects.requireNonNull(program); + this.addresses = Objects.requireNonNull(addresses); + this.enoom = Objects.requireNonNull(enoom); this.shouldDoOnSubOps = shouldDoOnSubOps; } + @Override + public String getName() { + return "Create Enum Equate Command"; + } + @Override public boolean applyTo(DomainObject obj, TaskMonitor monitor) { - EquateTable et = program.getEquateTable(); + equateTable = program.getEquateTable(); try { - et.applyEnum(addresses, enoom, monitor, shouldDoOnSubOps); + applyEnum(monitor); } catch (CancelledException e) { return false; @@ -60,8 +73,95 @@ public class CreateEnumEquateCommand extends BackgroundCommand { return true; } - @Override - public String getName() { - return "Create Enum Equate Command"; + public void applyEnum(TaskMonitor monitor) throws CancelledException { + + 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; + } + } diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/CreateEnumFromSelectionTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/CreateEnumFromSelectionTest.java index 7a1fa130bf..dd13229014 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/CreateEnumFromSelectionTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/CreateEnumFromSelectionTest.java @@ -38,16 +38,13 @@ import ghidra.program.model.data.*; import ghidra.program.model.data.Enum; import ghidra.test.AbstractGhidraHeadedIntegrationTest; 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 { private static final String PROGRAM_FILENAME = "notepad"; - private static final int TASK_TIMEOUT = 2000; - //private Program program; private PluginTool tool; private ProgramDB program; private TestEnv env; @@ -57,10 +54,6 @@ public class CreateEnumFromSelectionTest extends AbstractGhidraHeadedIntegration private ArchiveRootNode archiveRootNode; private ArchiveNode programNode; - public CreateEnumFromSelectionTest() { - super(); - } - @Before public void setUp() throws Exception { env = new TestEnv(); @@ -93,16 +86,6 @@ public class CreateEnumFromSelectionTest extends AbstractGhidraHeadedIntegration return builder.getProgram(); } -// @Override -// protected void tearDown() throws Exception { -// executeOnSwingWithoutBlocking(new Runnable() { -// public void run() { -// ProgramManager pm = tool.getService(ProgramManager.class); -// pm.closeProgram(); -// -// } -// }); -// } @After public void tearDown() throws Exception { @@ -116,8 +99,7 @@ public class CreateEnumFromSelectionTest extends AbstractGhidraHeadedIntegration }); // this handles the save changes dialog and potential analysis dialogs - closeAllWindowsAndFrames(); - env.release(program); + closeAllWindows(); 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); 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); final JTextField tf = findComponent(window, JTextField.class); @@ -283,7 +265,7 @@ public class CreateEnumFromSelectionTest extends AbstractGhidraHeadedIntegration tf.setText("myNewEnum"); pressButtonByText(window, "OK"); - Window window2 = waitForWindow(tool.getToolFrame(), "Duplicate ENUM Name", TASK_TIMEOUT); + Window window2 = waitForWindow("Duplicate ENUM Name"); assertNotNull(window2); 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 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) { tree.expandPath(node); waitForTree(); @@ -373,37 +334,4 @@ public class CreateEnumFromSelectionTest extends AbstractGhidraHeadedIntegration private void waitForTree() { 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 children = parent.getChildren(); - for (GTreeNode node : children) { - if (node instanceof CategoryNode && node.getName().equals(categoryName)) { - return (CategoryNode) node; - } - } - return null; - } } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/EquateManager.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/EquateManager.java index 5d6f26e75c..8dd3c86f42 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/EquateManager.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/EquateManager.java @@ -17,9 +17,6 @@ package ghidra.program.database.symbol; import java.io.IOException; import java.util.*; -import java.util.function.Consumer; -import java.util.stream.Stream; -import java.util.stream.StreamSupport; import db.*; import db.util.ErrorHandler; @@ -27,14 +24,12 @@ import ghidra.program.database.*; import ghidra.program.database.map.AddressKeyAddressIterator; import ghidra.program.database.map.AddressMap; 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.EquateTable; import ghidra.program.util.ChangeManager; import ghidra.program.util.EquateInfo; -import ghidra.util.*; +import ghidra.util.Lock; +import ghidra.util.UniversalID; import ghidra.util.exception.*; import ghidra.util.task.TaskMonitor; @@ -114,112 +109,6 @@ public class EquateManager implements EquateTable, ErrorHandler, ManagerDB { 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 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 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 public Equate createEquate(String name, long value) throws DuplicateNameException, InvalidInputException { diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/symbol/EquateTable.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/symbol/EquateTable.java index 46ffdf3e6c..4464782dac 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/symbol/EquateTable.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/symbol/EquateTable.java @@ -21,9 +21,9 @@ import java.util.Iterator; import java.util.List; import ghidra.program.model.address.*; -import ghidra.program.model.data.Enum; import ghidra.util.exception.*; import ghidra.util.task.TaskMonitor; + /** * 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 @@ -32,40 +32,25 @@ import ghidra.util.task.TaskMonitor; */ public interface EquateTable { + /** - * Creates equates and/or adds references for scalars - * in the given address set using the given data type. - * The data type given must be an enumeration data type. - * @param addrSet the address set to use. - * @param dataType the data type to use. - * @param monitor task monitor to cancel the remove operation - * @param shouldDoOnSubOps true if the enum should be applied inside sub-operands as well. - * - * @throws CancelledException if the operation is cancelled - * @throws IllegalArgumentException if the dataType is null or not an enum. + * Creates a new equate + * @param name the name to associate with the given value. + * @param value the value to associate with the given name. + * @return the equate + * @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 void applyEnum(AddressSetView addrSet, Enum dataType, TaskMonitor monitor, - boolean shouldDoOnSubOps) throws CancelledException; + public Equate createEquate(String name, long value) + throws DuplicateNameException, InvalidInputException; - /** - * Creates a new equate - * @param name the name to associate with the given value. - * @param value the value to associate with the given name. - * @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. - */ + /** + * 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); /** @@ -75,24 +60,25 @@ public interface EquateTable { * @param monitor task monitor to cancel the remove operation * @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 - * exists. - * @param name the of the equate to be retrieved. - */ - public Equate getEquate(String name); + /** + * Returns the equate with the given name, null if no such equate exists + * @param name the of the equate to be retrieved + * @return the equate + */ + public Equate getEquate(String name); - /** - * Returns the first equate found that is associated with the given - * value at the given reference address and operand position; - * @param reference address 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. + /** + * Returns the first equate found that is associated with the given + * value at the given reference address and operand position; + * @param reference address 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. * @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 @@ -102,7 +88,7 @@ public interface EquateTable { * @return the list of equates or empty list if there is no such equate. */ public List getEquates(Address reference, int opndPosition); - + /** * Returns the equates (one for each scalar and opIndex) at the given reference address. * For an instruction a given operand can have multiple scalars. @@ -111,39 +97,42 @@ public interface EquateTable { */ public List getEquates(Address reference); - /** - * Returns an address iterator over all the addresses where - * equates have been set. - */ - public AddressIterator getEquateAddresses(); - - /** - * Returns all equates defined for value. - * @param value the value to get all equates for. - */ - public List getEquates(long value); - - /** - * Returns an iterator over all equates. - */ + /** + * Returns an address iterator over all the addresses where + * equates have been set. + * @return the iterator + */ + public AddressIterator getEquateAddresses(); + + /** + * Returns all equates defined for value. + * @param value the value to get all equates for. + * @return the equates + */ + public List getEquates(long value); + + /** + * Returns an iterator over all equates. + * @return the iterator + */ public Iterator getEquates(); - /** - * Return an address iterator over each address with an - * equate reference starting at the start address. - * - * @param start start address - * @return an AddressIterator over addresses with defined equate references - */ - public AddressIterator getEquateAddresses(Address start); + /** + * Return an address iterator over each address with an + * equate reference starting at the start address. + * + * @param start start address + * @return an AddressIterator over addresses with defined equate references + */ + public AddressIterator getEquateAddresses(Address start); - /** - * Return an address iterator over each address with an - * equate reference that is in the specified address set. - * - * @param asv the address set - * @return AddressIterator over addresses with defined equate references - */ - public AddressIterator getEquateAddresses(AddressSetView asv); + /** + * Return an address iterator over each address with an + * equate reference that is in the specified address set. + * + * @param asv the address set + * @return AddressIterator over addresses with defined equate references + */ + public AddressIterator getEquateAddresses(AddressSetView asv); }