diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/PostCommentFieldFactory.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/PostCommentFieldFactory.java index cf052e7f9b..a07091f242 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/PostCommentFieldFactory.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/PostCommentFieldFactory.java @@ -17,8 +17,9 @@ package ghidra.app.util.viewer.field; import java.awt.Color; import java.math.BigInteger; -import java.util.ArrayList; -import java.util.List; +import java.util.*; + +import org.apache.commons.lang3.StringUtils; import docking.widgets.fieldpanel.field.*; import docking.widgets.fieldpanel.support.FieldLocation; @@ -32,6 +33,8 @@ import ghidra.framework.options.ToolOptions; import ghidra.program.model.address.Address; import ghidra.program.model.address.AddressOverflowException; import ghidra.program.model.listing.*; +import ghidra.program.model.pcode.PcodeOp; +import ghidra.program.model.pcode.PcodeOverride; import ghidra.program.model.symbol.FlowType; import ghidra.program.model.symbol.RefType; import ghidra.program.util.*; @@ -154,49 +157,110 @@ public class PostCommentFieldFactory extends FieldFactory { } private String[] getAutoPostComment(CodeUnit cu) { - if (cu instanceof Instruction) { - String fallthroughComment = null; - String flowOverrideComment = null; - int count = 0; - Instruction instr = (Instruction) cu; - if (instr.isInDelaySlot()) { - // ensure that auto-comment come from parent and are only placed after last - // delay slot. Switch out inst with the parent instruction - int delaySlotPosition = 0; - while (instr.isInDelaySlot()) { - ++delaySlotPosition; - instr = instr.getPrevious(); - } - if (instr.getDelaySlotDepth() != delaySlotPosition) { - return null; // not the last delay slot + if (!(cu instanceof Instruction)) { + return null; + } + Instruction instr = (Instruction) cu; + LinkedList comments = new LinkedList<>(); + + if (instr.isInDelaySlot()) { + // ensure that auto-comment come from parent and are only placed after last + // delay slot. Switch out inst with the parent instruction + int delaySlotPosition = 0; + while (instr.isInDelaySlot()) { + ++delaySlotPosition; + instr = instr.getPrevious(); + } + if (instr.getDelaySlotDepth() != delaySlotPosition) { + return null; // not the last delay slot + } + } + + if (instr.isFallThroughOverridden()) { + Address fallThrough = instr.getFallThrough(); + String fallthroughComment = "-- Fallthrough Override: " + + (fallThrough != null ? fallThrough.toString() : "NO-FALLTHROUGH"); + comments.addFirst(fallthroughComment); + } + FlowOverride flowOverride = instr.getFlowOverride(); + if (flowOverride != FlowOverride.NONE) { + String flowOverrideComment = + "-- Flow Override: " + flowOverride + " (" + instr.getFlowType().getName() + ")"; + comments.addFirst(flowOverrideComment); + } + + InstructionPcodeOverride pCodeOverride = new InstructionPcodeOverride(instr); + + if (pCodeOverride.hasPotentialOverride()) { + PcodeOp[] pcodeOps = instr.getPcode(); + OverrideCommentData overrideData = null; + if (pCodeOverride.getPrimaryCallReference() == null) { + overrideData = getOverrideCommentData(instr, RefType.CALL_OVERRIDE_UNCONDITIONAL, + pcodeOps, pCodeOverride); + if (overrideData != null) { + String callOverrideComment = + "-- Call Destination Override: " + getOverridingCommentDestString( + overrideData.getOverridingRef(), instr.getProgram()); + comments.addFirst(callOverrideComment); } } - - if (instr.isFallThroughOverridden()) { - Address fallThrough = instr.getFallThrough(); - fallthroughComment = "-- Fallthrough Override: " + - (fallThrough != null ? fallThrough.toString() : "NO-FALLTHROUGH"); - ++count; + overrideData = getOverrideCommentData(instr, RefType.JUMP_OVERRIDE_UNCONDITIONAL, + pcodeOps, pCodeOverride); + if (overrideData != null) { + String jumpOverrideComment = + "-- Jump Destination Override: " + getOverridingCommentDestString( + overrideData.getOverridingRef(), instr.getProgram()); + comments.addFirst(jumpOverrideComment); } - FlowOverride flowOverride = instr.getFlowOverride(); - if (flowOverride != FlowOverride.NONE) { - flowOverrideComment = "-- Flow Override: " + flowOverride + " (" + - instr.getFlowType().getName() + ")"; - ++count; + overrideData = getOverrideCommentData(instr, RefType.CALLOTHER_OVERRIDE_CALL, pcodeOps, + pCodeOverride); + if (overrideData != null) { + String callOtherCallOverrideComment = + "-- CALLOTHER(" + overrideData.getOverriddenCallOther() + ") Call Override: " + + getOverridingCommentDestString(overrideData.getOverridingRef(), + instr.getProgram()); + if (overrideData.hasMultipleCallOthers()) { + callOtherCallOverrideComment += "\nWARNING: additional CALLOTHER ops present"; + } + comments.addFirst(callOtherCallOverrideComment); } - String[] autoComment = new String[count]; - if (fallthroughComment != null) { - autoComment[--count] = fallthroughComment; + else { + overrideData = + getOverrideCommentData(instr, RefType.CALLOTHER_OVERRIDE_JUMP, pcodeOps, + pCodeOverride); + if (overrideData != null) { + String callOtherJumpOverrideComment = + "-- CALLOTHER(" + overrideData.getOverriddenCallOther() + + ") Jump Override: " + getOverridingCommentDestString( + overrideData.getOverridingRef(), instr.getProgram()); + if (overrideData.hasMultipleCallOthers()) { + callOtherJumpOverrideComment += + "\nWARNING: additional CALLOTHER ops present"; + } + comments.addFirst(callOtherJumpOverrideComment); + } } - if (flowOverrideComment != null) { - autoComment[--count] = flowOverrideComment; - } - return autoComment; + } + if (comments.size() > 0) { + return comments.toArray(new String[0]); } return null; } + private String getOverridingCommentDestString(Address address, Program program) { + StringBuilder sb = new StringBuilder(); + String symbol = program.getSymbolTable().getPrimarySymbol(address).getName(true); + if (!StringUtils.isEmpty(symbol)) { + sb.append(symbol); + sb.append(" "); + } + sb.append("("); + sb.append(address.toString()); + sb.append(")"); + return sb.toString(); + } + /** * @see ghidra.app.util.viewer.field.FieldFactory#getProgramLocation(int, int, ghidra.app.util.viewer.field.ListingField) */ @@ -412,7 +476,8 @@ public class PostCommentFieldFactory extends FieldFactory { // TODO: convoluted logic since str will not be user comment if useLinesAfterBlock is true int nLinesAutoComment = ((comments.length == 0 && !useLinesAfterBlock) || alwaysShowAutomatic) - ? autoComment.length : 0; + ? autoComment.length + : 0; if (!useLinesAfterBlock && comments.length == 0 && nLinesAutoComment == 0) { return null; } @@ -497,4 +562,118 @@ public class PostCommentFieldFactory extends FieldFactory { return null; } + /** + * See {@link InstructionPcodeOverride#getOverridingReference(RefType)} + * See {@link ghidra.app.plugin.processors.sleigh.PcodeEmit#checkOverrides} + * @param inst instruction + * @param type reference type + * @return {@link OverrideCommentData} object corresponding to override comment + * ({@code null} if no override comment) + */ + private OverrideCommentData getOverrideCommentData(Instruction inst, RefType type, + PcodeOp[] pcodeOps, PcodeOverride pcodeOverride) { + //first, check whether the pcode corresponding to inst has an appropriate op + Set ops = new HashSet<>(); + if (type.equals(RefType.CALL_OVERRIDE_UNCONDITIONAL)) { + ops.add(PcodeOp.CALL); + ops.add(PcodeOp.CALLIND); + } + else if (type.equals(RefType.JUMP_OVERRIDE_UNCONDITIONAL)) { + ops.add(PcodeOp.BRANCH); + ops.add(PcodeOp.CBRANCH); + } + else if (type.equals(RefType.CALLOTHER_OVERRIDE_CALL) || + type.equals(RefType.CALLOTHER_OVERRIDE_JUMP)) { + ops.add(PcodeOp.CALLOTHER); + } + else { + return null; + } + + boolean hasAppropriatePcodeOp = false; + + //used to warn user that there are CALLOTHER ops at this instruction that are + //not overridden + boolean hasMultipleCallOthers = false; + //used to report the name of the CALLOTHER op that is overridden + String callOtherName = null; + for (PcodeOp op : pcodeOps) { + if (ops.contains(op.getOpcode())) { + hasAppropriatePcodeOp = true; + if (op.getOpcode() == PcodeOp.CALLOTHER) { + if (callOtherName == null) { + callOtherName = inst.getProgram().getLanguage().getUserDefinedOpName( + (int) op.getInput(0).getOffset()); + } + else { + hasMultipleCallOthers = true; + } + } + } + } + if (!hasAppropriatePcodeOp) { + return null; + } + + //now check whether there is an active overriding reference of the appropriate type + if (type.equals(RefType.CALL_OVERRIDE_UNCONDITIONAL)) { + Address ref = pcodeOverride.getOverridingReference(type); + if (ref != null) { + return new OverrideCommentData(ref, null, false); + } + return null; + } + if (type.equals(RefType.JUMP_OVERRIDE_UNCONDITIONAL)) { + Address ref = pcodeOverride.getOverridingReference(type); + if (ref != null) { + return new OverrideCommentData(ref, null, false); + } + return null; + } + if (type.equals(RefType.CALLOTHER_OVERRIDE_CALL)) { + Address ref = pcodeOverride.getOverridingReference(type); + if (ref != null) { + return new OverrideCommentData(ref, callOtherName, hasMultipleCallOthers); + } + return null; + } + //must be in the RefType.CALLOTHER_OVERRIDE_JUMP case + Address ref = pcodeOverride.getOverridingReference(RefType.CALLOTHER_OVERRIDE_CALL); + if (ref != null) { + return null; //CALLOTHER_OVERRIDE_CALL overrides have precedence + } + ref = pcodeOverride.getOverridingReference(RefType.CALLOTHER_OVERRIDE_JUMP); + if (ref == null) { + return null; + } + return new OverrideCommentData(ref, callOtherName, hasMultipleCallOthers); + + } + + private class OverrideCommentData { + private Address overridingRef; + private String overriddenCallOther; + private boolean hasMultipleCallOthers; + + OverrideCommentData(Address overridingRef, String overriddenCallOther, + boolean multipleCallOthers) { + this.overridingRef = overridingRef; + this.overriddenCallOther = overriddenCallOther; + this.hasMultipleCallOthers = multipleCallOthers; + } + + Address getOverridingRef() { + return overridingRef; + } + + String getOverriddenCallOther() { + return overriddenCallOther; + } + + boolean hasMultipleCallOthers() { + return hasMultipleCallOthers; + } + + } + } diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/viewer/field/PostCommentFieldFactoryTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/viewer/field/PostCommentFieldFactoryTest.java index 95cfdb90bd..6b2c9c34ba 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/viewer/field/PostCommentFieldFactoryTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/viewer/field/PostCommentFieldFactoryTest.java @@ -31,6 +31,7 @@ import ghidra.program.database.ProgramDB; import ghidra.program.model.address.Address; import ghidra.program.model.address.AddressFactory; import ghidra.program.model.listing.*; +import ghidra.program.model.symbol.*; import ghidra.test.*; public class PostCommentFieldFactoryTest extends AbstractGhidraHeadedIntegrationTest { @@ -72,6 +73,41 @@ public class PostCommentFieldFactoryTest extends AbstractGhidraHeadedIntegration builder.createReturnInstruction("1001042"); builder.disassemble("1001040", 1); + //create a function for testing jump override comments + builder.createEmptyFunction("jump_override", "1003000", 100, null); + builder.createConditionalJmpInstruction("1003000", "1003006"); + builder.createNOPInstruction("1003002", 4); + builder.createReturnInstruction("1003006"); + builder.createReturnInstruction("1003008"); + + //create a function for testing indirect call override comments + builder.createEmptyFunction("indirect_call_override", "1004000", 100, null); + //call [r1] + builder.setBytes("1004000", "f6 10"); + builder.disassemble("1004000", 2); + builder.createReturnInstruction("1004002"); + + //create function for testing direct call override comments + builder.createEmptyFunction("direct_call_override_backward_compatibility", "1005000", 10, + null); + builder.createEmptyFunction("call_dest_1", "1005020", 10, null); + builder.createCallInstruction("1005000", "1005020"); + builder.createReturnInstruction("1005002"); + + //create function for testing that overrides only happen when there is exactly one + //primary overriding reference (e.g., if there's a primary overriding ref on + //both the mnemonic and an operand then no override + builder.createEmptyFunction("only_one_primary_override_ref", "1006000", 10, null); + builder.createEmptyFunction("call_dest_2", "1006020", 10, null); + builder.createCallInstruction("1006000", "1006020"); + builder.createReturnInstruction("1006002"); + + //create function for testing overrides that don't actually change the destination + builder.createEmptyFunction("override_without_dest_change", "1007000", 10, null); + builder.createEmptyFunction("call_dest_3", "1007020", 10, null); + builder.createCallInstruction("1007000", "1007020"); + builder.createReturnInstruction("1007002"); + return builder.getProgram(); } @@ -230,6 +266,430 @@ public class PostCommentFieldFactoryTest extends AbstractGhidraHeadedIntegration assertEquals(4, tf.getNumRows()); } + @Test + public void testOverridingJumpComment() { + //test overriding a conditional jump to an unconditional jump + //using a RefType.JUMP_OVERRIDE_UNCONDITIONAL reference + ReferenceManager refManager = program.getReferenceManager(); + Reference ref1 = null; + int transactionID = program.startTransaction("add_primary_jump_ref"); + try { + ref1 = refManager.addMemoryReference(addr("1003000"), addr("1003006"), + RefType.JUMP_OVERRIDE_UNCONDITIONAL, SourceType.ANALYSIS, Reference.MNEMONIC); + refManager.setPrimary(ref1, true); + } + finally { + program.endTransaction(transactionID, true); + } + + assertTrue(cb.goToField(addr("1003000"), PostCommentFieldFactory.FIELD_NAME, 0, 1)); + ListingTextField tf = (ListingTextField) cb.getCurrentField(); + assertEquals("-- Jump Destination Override: LAB_01003006 (01003006)", tf.getText()); + + //test that making the reference non-primary removes the post comment + ref1 = refManager.getPrimaryReferenceFrom(addr("1003000"), Reference.MNEMONIC); + assertTrue(ref1.isPrimary()); + transactionID = program.startTransaction("set_ref_non_primary"); + try { + refManager.setPrimary(ref1, false); + } + finally { + program.endTransaction(transactionID, true); + } + assertFalse(cb.goToField(addr("1003000"), PostCommentFieldFactory.FIELD_NAME, 0, 1)); + + //test that adding a second ref of the same type and setting it to primary + //yields a new post comment + transactionID = program.startTransaction("add_second_jump_ref"); + Reference ref2 = null; + try { + ref2 = refManager.addMemoryReference(addr("1003000"), addr("1003008"), + RefType.JUMP_OVERRIDE_UNCONDITIONAL, SourceType.ANALYSIS, Reference.MNEMONIC); + refManager.setPrimary(ref2, true); + } + finally { + program.endTransaction(transactionID, true); + } + assertTrue(cb.goToField(addr("1003000"), PostCommentFieldFactory.FIELD_NAME, 0, 1)); + tf = (ListingTextField) cb.getCurrentField(); + assertEquals("-- Jump Destination Override: LAB_01003008 (01003008)", tf.getText()); + + //test the swapping which reference is primary changes the post comment + transactionID = program.startTransaction("swap_primary"); + try { + refManager.setPrimary(ref2, false); + refManager.setPrimary(ref1, true); + } + finally { + program.endTransaction(transactionID, true); + } + assertTrue(cb.goToField(addr("1003000"), PostCommentFieldFactory.FIELD_NAME, 0, 1)); + tf = (ListingTextField) cb.getCurrentField(); + assertEquals("-- Jump Destination Override: LAB_01003006 (01003006)", tf.getText()); + + //test that making all references non-primary removes the post comment + transactionID = program.startTransaction("no_primary_refs"); + try { + refManager.setPrimary(ref2, false); + refManager.setPrimary(ref1, false); + } + finally { + program.endTransaction(transactionID, true); + } + assertFalse(cb.goToField(addr("1003000"), PostCommentFieldFactory.FIELD_NAME, 0, 1)); + + //test that the other overriding reference types don't add any post comments + transactionID = program.startTransaction("add_overriding_call_ref"); + try { + ref2 = refManager.addMemoryReference(addr("1003000"), addr("1004000"), + RefType.CALL_OVERRIDE_UNCONDITIONAL, SourceType.ANALYSIS, Reference.MNEMONIC); + refManager.setPrimary(ref2, true); + } + finally { + program.endTransaction(transactionID, true); + } + assertFalse(cb.goToField(addr("1003000"), PostCommentFieldFactory.FIELD_NAME, 0, 1)); + + transactionID = program.startTransaction("add_overriding_callother_call_ref"); + try { + ref2 = refManager.addMemoryReference(addr("1003000"), addr("1004000"), + RefType.CALLOTHER_OVERRIDE_CALL, SourceType.ANALYSIS, Reference.MNEMONIC); + refManager.setPrimary(ref2, true); + } + finally { + program.endTransaction(transactionID, true); + } + assertFalse(cb.goToField(addr("1003000"), PostCommentFieldFactory.FIELD_NAME, 0, 1)); + + transactionID = program.startTransaction("add_overriding_callother_jump_ref"); + try { + ref2 = refManager.addMemoryReference(addr("1003000"), addr("1003006"), + RefType.CALLOTHER_OVERRIDE_JUMP, SourceType.ANALYSIS, Reference.MNEMONIC); + refManager.setPrimary(ref2, true); + } + finally { + program.endTransaction(transactionID, true); + } + assertFalse(cb.goToField(addr("1003000"), PostCommentFieldFactory.FIELD_NAME, 0, 1)); + + //last test: test primary refs on mnemonic and operand 1 + //shouldn't work + } + + @Test + public void testOverridingIndirectCallComment() { + //test that a primary RefType.CALL_OVERRIDE_UNCONDITIONAL reference on an indirect call + //causes a post comment indicating that the call destination has been overridden + ReferenceManager refManager = program.getReferenceManager(); + Reference ref1 = null; + int transactionID = program.startTransaction("override indirect call"); + try { + ref1 = refManager.addMemoryReference(addr("1004000"), addr("1003000"), + RefType.CALL_OVERRIDE_UNCONDITIONAL, SourceType.ANALYSIS, Reference.MNEMONIC); + refManager.setPrimary(ref1, true); + } + finally { + program.endTransaction(transactionID, true); + } + assertTrue(cb.goToField(addr("1004000"), PostCommentFieldFactory.FIELD_NAME, 0, 1)); + ListingTextField tf = (ListingTextField) cb.getCurrentField(); + assertEquals("-- Call Destination Override: jump_override (01003000)", tf.getText()); + + //test that making the reference non-primary remove the post comment + ref1 = refManager.getPrimaryReferenceFrom(addr("1004000"), Reference.MNEMONIC); + assertTrue(ref1.isPrimary()); + transactionID = program.startTransaction("set_ref_non_primary"); + try { + refManager.setPrimary(ref1, false); + } + finally { + program.endTransaction(transactionID, true); + } + assertFalse(cb.goToField(addr("1004000"), PostCommentFieldFactory.FIELD_NAME, 0, 1)); + + //test that adding a second ref of the same type and setting it to primary + //yields a new post comment + transactionID = program.startTransaction("add_second_ref"); + Reference ref2 = null; + try { + ref2 = refManager.addMemoryReference(addr("1004000"), addr("1001000"), + RefType.CALL_OVERRIDE_UNCONDITIONAL, SourceType.ANALYSIS, Reference.MNEMONIC); + refManager.setPrimary(ref2, true); + } + finally { + program.endTransaction(transactionID, true); + } + assertTrue(cb.goToField(addr("1004000"), PostCommentFieldFactory.FIELD_NAME, 0, 1)); + tf = (ListingTextField) cb.getCurrentField(); + assertEquals("-- Call Destination Override: FUN_01001000 (01001000)", tf.getText()); + + //test the swapping which reference is primary changes the post comment + transactionID = program.startTransaction("swap_primary"); + try { + refManager.setPrimary(ref2, false); + refManager.setPrimary(ref1, true); + } + finally { + program.endTransaction(transactionID, true); + } + assertTrue(cb.goToField(addr("1004000"), PostCommentFieldFactory.FIELD_NAME, 0, 1)); + tf = (ListingTextField) cb.getCurrentField(); + assertEquals("-- Call Destination Override: jump_override (01003000)", tf.getText()); + + //test that making all references non-primary removes the post comment + transactionID = program.startTransaction("no_primary_refs"); + try { + refManager.setPrimary(ref2, false); + refManager.setPrimary(ref1, false); + } + finally { + program.endTransaction(transactionID, true); + } + assertFalse(cb.goToField(addr("1004000"), PostCommentFieldFactory.FIELD_NAME, 0, 1)); + + transactionID = program.startTransaction("add_overriding_jump_ref"); + try { + ref2 = refManager.addMemoryReference(addr("1004000"), addr("1003008"), + RefType.JUMP_OVERRIDE_UNCONDITIONAL, SourceType.ANALYSIS, Reference.MNEMONIC); + refManager.setPrimary(ref2, true); + } + finally { + program.endTransaction(transactionID, true); + } + assertFalse(cb.goToField(addr("1004000"), PostCommentFieldFactory.FIELD_NAME, 0, 1)); + + transactionID = program.startTransaction("add_overriding_callother_call_ref"); + try { + ref2 = refManager.addMemoryReference(addr("1004000"), addr("1003000"), + RefType.CALLOTHER_OVERRIDE_CALL, SourceType.ANALYSIS, Reference.MNEMONIC); + refManager.setPrimary(ref2, true); + } + finally { + program.endTransaction(transactionID, true); + } + assertFalse(cb.goToField(addr("1004000"), PostCommentFieldFactory.FIELD_NAME, 0, 1)); + + transactionID = program.startTransaction("add_overriding_callother_jump_ref"); + try { + ref2 = refManager.addMemoryReference(addr("1004000"), addr("1003006"), + RefType.CALLOTHER_OVERRIDE_JUMP, SourceType.ANALYSIS, Reference.MNEMONIC); + refManager.setPrimary(ref2, true); + } + finally { + program.endTransaction(transactionID, true); + } + assertFalse(cb.goToField(addr("1004000"), PostCommentFieldFactory.FIELD_NAME, 0, 1)); + + } + + @Test + public void testOverridingDirectCallAndBackwardCompatibility() { + assertFalse(cb.goToField(addr("1005000"), PostCommentFieldFactory.FIELD_NAME, 0, 1)); + ReferenceManager refManager = program.getReferenceManager(); + Reference defaultRef = refManager.getPrimaryReferenceFrom(addr("1005000"), 0); + assertNotNull(defaultRef); + Reference callOverride = null; + int transactionID = program.startTransaction("add_overriding_callother_jump_ref"); + try { + callOverride = refManager.addMemoryReference(addr("1005000"), addr("1003000"), + RefType.CALL_OVERRIDE_UNCONDITIONAL, SourceType.ANALYSIS, Reference.MNEMONIC); + refManager.setPrimary(callOverride, true); + } + finally { + program.endTransaction(transactionID, true); + } + //there's a primary call-type reference on operand one, so the CALL_OVERRIDE_UNCONDITIONAL + //override should *not* be active (this is testing backward compatibility) + assertFalse(cb.goToField(addr("1005000"), PostCommentFieldFactory.FIELD_NAME, 0, 1)); + + //now make defaultRef non-primary and verify that the postcomment from callOverride + //shows up + transactionID = program.startTransaction("de-primary default ref"); + try { + refManager.setPrimary(defaultRef, false); + } + finally { + program.endTransaction(transactionID, true); + } + assertTrue(cb.goToField(addr("1005000"), PostCommentFieldFactory.FIELD_NAME, 0, 1)); + ListingTextField tf = (ListingTextField) cb.getCurrentField(); + assertEquals("-- Call Destination Override: jump_override (01003000)", tf.getText()); + + transactionID = program.startTransaction("set_ref_non_primary"); + try { + refManager.setPrimary(callOverride, false); + } + finally { + program.endTransaction(transactionID, true); + } + assertFalse(cb.goToField(addr("1005000"), PostCommentFieldFactory.FIELD_NAME, 0, 1)); + + //test that adding a second ref of the same type and setting it to primary + //yields a new post comment + transactionID = program.startTransaction("add_second_ref"); + Reference ref2 = null; + try { + ref2 = refManager.addMemoryReference(addr("1005000"), addr("1001000"), + RefType.CALL_OVERRIDE_UNCONDITIONAL, SourceType.ANALYSIS, Reference.MNEMONIC); + refManager.setPrimary(ref2, true); + } + finally { + program.endTransaction(transactionID, true); + } + assertTrue(cb.goToField(addr("1005000"), PostCommentFieldFactory.FIELD_NAME, 0, 1)); + tf = (ListingTextField) cb.getCurrentField(); + assertEquals("-- Call Destination Override: FUN_01001000 (01001000)", tf.getText()); + + //test the swapping which reference is primary changes the post comment + transactionID = program.startTransaction("swap_primary"); + try { + refManager.setPrimary(ref2, false); + refManager.setPrimary(callOverride, true); + } + finally { + program.endTransaction(transactionID, true); + } + assertTrue(cb.goToField(addr("1005000"), PostCommentFieldFactory.FIELD_NAME, 0, 1)); + tf = (ListingTextField) cb.getCurrentField(); + assertEquals("-- Call Destination Override: jump_override (01003000)", tf.getText()); + + //test that making all references non-primary removes the post comment + transactionID = program.startTransaction("no_primary_refs"); + try { + refManager.setPrimary(ref2, false); + refManager.setPrimary(callOverride, false); + } + finally { + program.endTransaction(transactionID, true); + } + assertFalse(cb.goToField(addr("1005000"), PostCommentFieldFactory.FIELD_NAME, 0, 1)); + + //verify that JUMP_OVERRIDE_UNCONDITIONAL references and + //CALLOTHER overriding references don't do anything + transactionID = program.startTransaction("add_overriding_jump_ref"); + try { + ref2 = refManager.addMemoryReference(addr("1005000"), addr("1003008"), + RefType.JUMP_OVERRIDE_UNCONDITIONAL, SourceType.ANALYSIS, Reference.MNEMONIC); + refManager.setPrimary(ref2, true); + } + finally { + program.endTransaction(transactionID, true); + } + assertFalse(cb.goToField(addr("1005000"), PostCommentFieldFactory.FIELD_NAME, 0, 1)); + + transactionID = program.startTransaction("add_overriding_callother_call_ref"); + try { + ref2 = refManager.addMemoryReference(addr("1005000"), addr("1003000"), + RefType.CALLOTHER_OVERRIDE_CALL, SourceType.ANALYSIS, Reference.MNEMONIC); + refManager.setPrimary(ref2, true); + } + finally { + program.endTransaction(transactionID, true); + } + assertFalse(cb.goToField(addr("1005000"), PostCommentFieldFactory.FIELD_NAME, 0, 1)); + + transactionID = program.startTransaction("add_overriding_callother_jump_ref"); + try { + ref2 = refManager.addMemoryReference(addr("1005000"), addr("1003006"), + RefType.CALLOTHER_OVERRIDE_JUMP, SourceType.ANALYSIS, Reference.MNEMONIC); + refManager.setPrimary(ref2, true); + } + finally { + program.endTransaction(transactionID, true); + } + assertFalse(cb.goToField(addr("1005000"), PostCommentFieldFactory.FIELD_NAME, 0, 1)); + } + + @Test + public void testExactlyOnePrimaryOverridingRef() { + assertFalse(cb.goToField(addr("1006000"), PostCommentFieldFactory.FIELD_NAME, 0, 1)); + ReferenceManager refManager = program.getReferenceManager(); + Reference defaultRef = refManager.getPrimaryReferenceFrom(addr("1006000"), 0); + assertNotNull(defaultRef); + Reference callOverrideMnemonic = null; + int transactionID = + program.startTransaction("turn_off_existing_primary_ref_and_create_override_ref"); + try { + refManager.setPrimary(defaultRef, false); + callOverrideMnemonic = refManager.addMemoryReference(addr("1006000"), addr("1003000"), + RefType.CALL_OVERRIDE_UNCONDITIONAL, SourceType.ANALYSIS, Reference.MNEMONIC); + refManager.setPrimary(callOverrideMnemonic, true); + + } + finally { + program.endTransaction(transactionID, true); + } + assertTrue(cb.goToField(addr("1006000"), PostCommentFieldFactory.FIELD_NAME, 0, 1)); + ListingTextField tf = (ListingTextField) cb.getCurrentField(); + assertEquals("-- Call Destination Override: jump_override (01003000)", tf.getText()); + Reference callOverrideOperand0 = null; + transactionID = program.startTransaction("set_operand_ref_primary"); + try { + callOverrideOperand0 = refManager.addMemoryReference(addr("1006000"), addr("1005020"), + RefType.CALL_OVERRIDE_UNCONDITIONAL, SourceType.ANALYSIS, 0); + refManager.setPrimary(callOverrideOperand0, true); + + } + finally { + program.endTransaction(transactionID, true); + } + //two primary override refs of same type, override should not take effect + assertFalse(cb.goToField(addr("1006000"), PostCommentFieldFactory.FIELD_NAME, 0, 1)); + transactionID = program.startTransaction("set_mnemonic_ref_non_primary"); + try { + refManager.setPrimary(callOverrideMnemonic, false); + } + finally { + program.endTransaction(transactionID, true); + } + //now there's only one primary overriding ref, so the override comment should be there + assertTrue(cb.goToField(addr("1006000"), PostCommentFieldFactory.FIELD_NAME, 0, 1)); + tf = (ListingTextField) cb.getCurrentField(); + assertEquals("-- Call Destination Override: call_dest_1 (01005020)", tf.getText()); + transactionID = program.startTransaction("set_operand_ref_non_primary"); + try { + refManager.setPrimary(callOverrideOperand0, false); + } + finally { + program.endTransaction(transactionID, true); + } + //no primary overriding refs, no override post comment + assertFalse(cb.goToField(addr("1006000"), PostCommentFieldFactory.FIELD_NAME, 0, 1)); + + } + + @Test + public void testOverridingWithChangingDestination() { + assertFalse(cb.goToField(addr("1007000"), PostCommentFieldFactory.FIELD_NAME, 0, 1)); + ReferenceManager refManager = program.getReferenceManager(); + Reference defaultRef = refManager.getPrimaryReferenceFrom(addr("1007000"), 0); + assertNotNull(defaultRef); + Reference callOverrideMnemonic = null; + int transactionID = program.startTransaction("override_without_changing_dest"); + try { + refManager.setPrimary(defaultRef, false); + callOverrideMnemonic = refManager.addMemoryReference(addr("1007000"), addr("1007020"), + RefType.CALL_OVERRIDE_UNCONDITIONAL, SourceType.ANALYSIS, Reference.MNEMONIC); + refManager.setPrimary(callOverrideMnemonic, true); + } + finally { + program.endTransaction(transactionID, true); + } + assertTrue(cb.goToField(addr("1007000"), PostCommentFieldFactory.FIELD_NAME, 0, 1)); + ListingTextField tf = (ListingTextField) cb.getCurrentField(); + assertEquals("-- Call Destination Override: call_dest_3 (01007020)", tf.getText()); + transactionID = program.startTransaction("turn_off_override"); + try { + refManager.setPrimary(defaultRef, true); + } + finally { + program.endTransaction(transactionID, true); + } + assertFalse(cb.goToField(addr("1007000"), PostCommentFieldFactory.FIELD_NAME, 0, 1)); + } + + //TODO: test overriding CALLOTHER ops + private void setCommentInFunction(Function function, String comment) { CodeUnit cu = program.getListing().getCodeUnitAt(function.getEntryPoint()); int transactionID = program.startTransaction("test"); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/plugin/processors/sleigh/PcodeEmit.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/plugin/processors/sleigh/PcodeEmit.java index 8a688c329b..50cf6a5d0d 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/plugin/processors/sleigh/PcodeEmit.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/plugin/processors/sleigh/PcodeEmit.java @@ -29,6 +29,7 @@ import ghidra.program.model.listing.FlowOverride; import ghidra.program.model.mem.MemoryAccessException; import ghidra.program.model.pcode.PcodeOp; import ghidra.program.model.pcode.PcodeOverride; +import ghidra.program.model.symbol.RefType; import ghidra.util.exception.NotYetImplementedException; /** @@ -738,42 +739,119 @@ public abstract class PcodeEmit { return opcode; } - //If there is an overriding reference on an indirect call, change the indirect - //call to a direct call - if (opcode == PcodeOp.CALLIND) { - Address callRef = override.getOverridingCallReference(); + //If there is an overriding call reference on an indirect call, change it to + //to a direct call, unless a call override has already been applied at this instruction + if (opcode == PcodeOp.CALLIND && !override.isCallOverrideRefApplied()) { + Address callRef = override.getOverridingReference(RefType.CALL_OVERRIDE_UNCONDITIONAL); if (callRef != null) { VarnodeData dest = in[0]; dest.space = callRef.getAddressSpace(); dest.offset = callRef.getOffset(); dest.size = dest.space.getPointerSize(); + override.setCallOverrideRefApplied(); return PcodeOp.CALL; } } - // Simple call reference override - use primary call reference as destination + //CALLOTHER ops can be overridden with RefType.CALLOTHER_OVERRIDE_CALL + //or RefType.CALLOTHER_OVERRIDE_JUMP + //Call overrides take precedence over jump overrides + //override at most one callother pcode op per native instruction + boolean callOtherOverrideApplied = override.isCallOtherCallOverrideRefApplied() || + override.isCallOtherJumpOverrideApplied(); + if (opcode == PcodeOp.CALLOTHER && !callOtherOverrideApplied) { + Address overrideRef = override.getOverridingReference(RefType.CALLOTHER_OVERRIDE_CALL); + VarnodeData dest = in[0]; + if (overrideRef != null) { + dest.space = overrideRef.getAddressSpace(); + dest.offset = overrideRef.getOffset(); + dest.size = dest.space.getPointerSize(); + override.setCallOtherCallOverrideRefApplied(); + return PcodeOp.CALL; + } + overrideRef = override.getOverridingReference(RefType.CALLOTHER_OVERRIDE_JUMP); + if (overrideRef != null) { + dest.space = overrideRef.getAddressSpace(); + dest.offset = overrideRef.getOffset(); + dest.size = dest.space.getPointerSize(); + override.setCallOtherJumpOverrideRefApplied(); + return PcodeOp.BRANCH; + } + } + + // Simple call reference override - grab destination from appropriate reference // Only perform reference override if destination function does not have a call-fixup - if (opcode == PcodeOp.CALL && + if (opcode == PcodeOp.CALL && !override.isCallOverrideRefApplied() && !override.hasCallFixup(in[0].space.getAddress(in[0].offset))) { - // Check for call reference (not supported if call-fixup exists for the instruction) - Address callRef = override.getOverridingCallReference(); - if (callRef != null) { - VarnodeData dest = in[0]; + VarnodeData dest = in[0]; + //call to override.getPrimaryCallReference kept for backward compatibility with + //old call override mechanism + //old mechanism has precedence over new + Address callRef = override.getPrimaryCallReference(); + boolean overridingRef = false; + if (callRef == null) { + callRef = override.getOverridingReference(RefType.CALL_OVERRIDE_UNCONDITIONAL); + overridingRef = true; + } + //every call instruction automatically has a call-type reference to the call target + //we don't want these references to count as overrides - only count as an override + //via explicitly changing the destination or using a CALL_OVERRIDE_UNCONDITIONAL reference + if (callRef != null && (overridingRef || actualOverride(dest, callRef))) { dest.space = callRef.getAddressSpace(); dest.offset = callRef.getOffset(); dest.size = dest.space.getPointerSize(); + override.setCallOverrideRefApplied(); + return PcodeOp.CALL; } } // Fall-through override - alter branch to next instruction if (fallOverride != null && (opcode == PcodeOp.CBRANCH || opcode == PcodeOp.BRANCH)) { + //don't apply fallthrough overrides into the constant space + if (in[0].space.getType() == AddressSpace.TYPE_CONSTANT) { + return opcode; + } VarnodeData dest = in[0]; if (defaultFallAddress.getOffset() == dest.offset) { dest.space = fallOverride.getAddressSpace(); dest.offset = fallOverride.getOffset(); dest.size = dest.space.getPointerSize(); } + return opcode; + } + + //if there is an overriding jump reference, change a conditional jump to an + //unconditional jump with the target given by the reference + if ((opcode == PcodeOp.BRANCH || opcode == PcodeOp.CBRANCH) && + !override.isJumpOverrideRefApplied()) { + //if the destination varnode is in the const space, it's a pcode-relative branch. + //these should not be overridden + if (in[0].space.getType() == AddressSpace.TYPE_CONSTANT) { + return opcode; + } + Address overrideRef = + override.getOverridingReference(RefType.JUMP_OVERRIDE_UNCONDITIONAL); + if (overrideRef != null) { + VarnodeData dest = in[0]; + dest.space = overrideRef.getAddressSpace(); + dest.offset = overrideRef.getOffset(); + dest.size = dest.space.getPointerSize(); + override.setJumpOverrideRefApplied(); + return PcodeOp.BRANCH; + } } return opcode; } + + // Used to check whether the address from a potentially overriding reference + // actually changes the call destination + private boolean actualOverride(VarnodeData data, Address addr) { + if (!data.space.equals(addr.getAddressSpace())) { + return true; + } + if (data.offset != addr.getOffset()) { + return true; + } + return false; + } } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/Instruction.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/Instruction.java index 1f6c20f4df..2301430e11 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/Instruction.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/Instruction.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,6 +15,8 @@ */ package ghidra.program.model.listing; +import java.util.List; + import ghidra.program.model.address.Address; import ghidra.program.model.address.UniqueAddressFactory; import ghidra.program.model.lang.*; @@ -23,8 +24,6 @@ import ghidra.program.model.pcode.PcodeOp; import ghidra.program.model.symbol.FlowType; import ghidra.program.model.symbol.RefType; -import java.util.List; - /** * Interface to define an instruction for a processor. */ diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/InstructionPcodeOverride.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/InstructionPcodeOverride.java index 5535bc2fcc..2d639cfcd8 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/InstructionPcodeOverride.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/InstructionPcodeOverride.java @@ -15,19 +15,49 @@ */ package ghidra.program.model.listing; +import java.util.ArrayList; +import java.util.List; + +import ghidra.app.plugin.processors.sleigh.PcodeEmit; import ghidra.program.model.address.Address; import ghidra.program.model.lang.InjectPayload; import ghidra.program.model.pcode.PcodeOverride; +import ghidra.program.model.symbol.RefType; import ghidra.program.model.symbol.Reference; -import ghidra.program.model.symbol.SourceType; import ghidra.util.Msg; public class InstructionPcodeOverride implements PcodeOverride { protected Instruction instr; + private boolean callOverrideApplied = false; + private boolean jumpOverrideApplied = false; + private boolean callOtherCallOverrideApplied = false; + private boolean callOtherJumpOverrideApplied = false; + private Address primaryCallAddress = null; + private List primaryOverridingReferences; + /** + * This constructor caches the primary and overriding "from" references of {@code instr}. + * This cache is never updated; the assumption is that this object is short-lived + * (duration of {@link PcodeEmit}) + * @param instr the instruction + */ public InstructionPcodeOverride(Instruction instr) { this.instr = instr; + + primaryOverridingReferences = new ArrayList<>(); + for (Reference ref : instr.getReferencesFrom()) { + if (!ref.isPrimary()) { + continue; + } + RefType type = ref.getReferenceType(); + if (type.isOverride()) { + primaryOverridingReferences.add(ref); + } + else if (type.isCall() && primaryCallAddress == null) { + primaryCallAddress = ref.getToAddress(); + } + } } @Override @@ -51,14 +81,27 @@ public class InstructionPcodeOverride implements PcodeOverride { } @Override - public Address getOverridingCallReference() { - for (Reference ref : instr.getReferencesFrom()) { - if (ref.getSource().equals(SourceType.USER_DEFINED) && ref.isPrimary() && - ref.getReferenceType().isCall()) { - return ref.getToAddress(); + public Address getOverridingReference(RefType type) { + if (!type.isOverride()) { + return null; + } + Address overrideAddress = null; + for (Reference ref : primaryOverridingReferences) { + if (ref.getReferenceType().equals(type)) { + if (overrideAddress == null) { + overrideAddress = ref.getToAddress(); + } + else { + return null; //only allow one primary reference of each type + } } } - return null; + return overrideAddress; + } + + @Override + public Address getPrimaryCallReference() { + return primaryCallAddress; } @Override @@ -89,4 +132,52 @@ public class InstructionPcodeOverride implements PcodeOverride { } return fixup; } + + @Override + public void setCallOverrideRefApplied() { + callOverrideApplied = true; + + } + + @Override + public boolean isCallOverrideRefApplied() { + return callOverrideApplied; + } + + @Override + public void setJumpOverrideRefApplied() { + jumpOverrideApplied = true; + + } + + @Override + public boolean isJumpOverrideRefApplied() { + return jumpOverrideApplied; + } + + @Override + public void setCallOtherCallOverrideRefApplied() { + callOtherCallOverrideApplied = true; + } + + @Override + public boolean isCallOtherCallOverrideRefApplied() { + return callOtherCallOverrideApplied; + } + + @Override + public void setCallOtherJumpOverrideRefApplied() { + callOtherJumpOverrideApplied = true; + + } + + @Override + public boolean isCallOtherJumpOverrideApplied() { + return callOtherJumpOverrideApplied; + } + + @Override + public boolean hasPotentialOverride() { + return !primaryOverridingReferences.isEmpty(); + } } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/PcodeOverride.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/PcodeOverride.java index 53c6c8a4a3..329a4023b0 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/PcodeOverride.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/PcodeOverride.java @@ -18,7 +18,7 @@ package ghidra.program.model.pcode; import ghidra.program.model.address.Address; import ghidra.program.model.lang.InjectPayload; import ghidra.program.model.listing.FlowOverride; -import ghidra.program.model.symbol.SourceType; +import ghidra.program.model.symbol.RefType; public interface PcodeOverride { @@ -35,11 +35,12 @@ public interface PcodeOverride { FlowOverride getFlowOverride(); /** - * Get the primary call reference address (whose {@link SourceType} must be {@link SourceType#USER_DEFINED}) - * from the current instruction + * Get the primary overriding reference address of {@link RefType} {@code type} from + * the current instruction + * @param type type of reference * @return call reference address or null */ - Address getOverridingCallReference(); + Address getOverridingReference(RefType type); /** * Get the fall-through override address which may have been @@ -66,4 +67,65 @@ public interface PcodeOverride { */ InjectPayload getCallFixup(Address callDestAddr); + /** + * Register that a call override has been applied at the current instruction. + */ + void setCallOverrideRefApplied(); + + /** + * Returns a boolean indicating whether a call override has been applied at the current instruction + * @return has call override been applied + */ + boolean isCallOverrideRefApplied(); + + /** + * Register that a jump override has been applied at the current instruction + */ + void setJumpOverrideRefApplied(); + + /** + * Returns a boolean indicating whether a jump override has been applied at the current instruction + * @return has jump override been applied + */ + boolean isJumpOverrideRefApplied(); + + /** + * Register that a callother call override has been applied at the current instruction + */ + void setCallOtherCallOverrideRefApplied(); + + /** + * Returns a boolean indicating whether a callother call override has been applied at the current + * instruction + * @return has callother call override been applied + */ + boolean isCallOtherCallOverrideRefApplied(); + + /** + * Register that a callother jump override has been applied at the current instruction + */ + void setCallOtherJumpOverrideRefApplied(); + + /** + * Returns a boolean indicating whether a callother jump override has been applied at the current + * instruction + * @return has callother jump override been applied + */ + boolean isCallOtherJumpOverrideApplied(); + + /** + * Returns a boolean indicating whether there are any primary overriding references at the current + * instruction + * @return are there primary overriding references + */ + boolean hasPotentialOverride(); + + /** + * + * Get the primary call reference address from the current instruction + * @return call reference address or null + */ + @Deprecated + Address getPrimaryCallReference(); + } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/symbol/FlowType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/symbol/FlowType.java index 7b34edc096..36c77ffede 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/symbol/FlowType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/symbol/FlowType.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,23 +20,82 @@ package ghidra.program.model.symbol; * flows from one instruction to the next) */ public final class FlowType extends RefType { - - private final boolean hasFall; + + private final boolean hasFall; private final boolean isCall; private final boolean isJump; - private final boolean isTeminal; + private final boolean isTerminal; private final boolean isConditional; private final boolean isComputed; + private final boolean isOverride; - protected FlowType(byte type, String name, boolean hasFall, boolean isCall, boolean isJump, boolean isTeminal, boolean isComputed, boolean isConditional) { - super(type, name); - this.hasFall = hasFall; - this.isCall = isCall; - this.isJump = isJump; - this.isTeminal = isTeminal; - this.isComputed = isComputed; - this.isConditional = isConditional; - } + protected static class Builder { + private byte type; + private String name; + + private boolean hasFall = false; + private boolean isCall = false; + private boolean isJump = false; + private boolean isTerminal = false; + private boolean isComputed = false; + private boolean isConditional = false; + private boolean isOverride = false; + + protected Builder(byte type, String name) { + this.type = type; + this.name = name; + } + + protected Builder setHasFall() { + this.hasFall = true; + return this; + } + + protected Builder setIsCall() { + this.isCall = true; + return this; + } + + protected Builder setIsJump() { + this.isJump = true; + return this; + } + + protected Builder setIsTerminal() { + this.isTerminal = true; + return this; + } + + protected Builder setIsComputed() { + this.isComputed = true; + return this; + } + + protected Builder setIsConditional() { + this.isConditional = true; + return this; + } + + protected Builder setIsOverride() { + this.isOverride = true; + return this; + } + + protected FlowType build() { + return new FlowType(this); + } + } + + private FlowType(Builder builder) { + super(builder.type, builder.name); + this.hasFall = builder.hasFall; + this.isCall = builder.isCall; + this.isJump = builder.isJump; + this.isTerminal = builder.isTerminal; + this.isComputed = builder.isComputed; + this.isConditional = builder.isConditional; + this.isOverride = builder.isOverride; + } @Override public boolean hasFallthrough() { @@ -71,7 +129,7 @@ public final class FlowType extends RefType { @Override public boolean isTerminal() { - return isTeminal; + return isTerminal; } @Override @@ -79,4 +137,9 @@ public final class FlowType extends RefType { return !isConditional; } + @Override + public boolean isOverride() { + return isOverride; + } + } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/symbol/RefType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/symbol/RefType.java index f4c60d18b0..99e290654a 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/symbol/RefType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/symbol/RefType.java @@ -53,6 +53,12 @@ public abstract class RefType { static final byte __CONDITIONAL_CALL_TERMINATOR = 14; static final byte __COMPUTED_CALL_TERMINATOR = 15; + static final byte __CALL_OVERRIDE_UNCONDITIONAL = 16; + static final byte __JUMP_OVERRIDE_UNCONDITIONAL = 17; + + static final byte __CALLOTHER_OVERRIDE_CALL = 18; + static final byte __CALLOTHER_OVERRIDE_JUMP = 19; + // DATA REFERENCE TYPES static final byte __UNKNOWNDATA = 100; static final byte __READ = 101; @@ -72,44 +78,58 @@ public abstract class RefType { static final byte __DYNAMICDATA = 127; public static final FlowType INVALID = - new FlowType(__INVALID, "INVALID", true, false, false, false, false, false); + new FlowType.Builder(__INVALID, "INVALID").setHasFall().build(); public static final FlowType FLOW = - new FlowType(__UNKNOWNFLOW, "FLOW", true, false, false, false, false, false); + new FlowType.Builder(__UNKNOWNFLOW, "FLOW").setHasFall().build(); public static final FlowType FALL_THROUGH = - new FlowType(__FALL_THROUGH, "FALL_THROUGH", true, false, false, false, false, false); - public static final FlowType UNCONDITIONAL_JUMP = new FlowType(__UNCONDITIONAL_JUMP, - "UNCONDITIONAL_JUMP", false, false, true, false, false, false); - public static final FlowType CONDITIONAL_JUMP = - new FlowType(__CONDITIONAL_JUMP, "CONDITIONAL_JUMP", true, false, true, false, false, true); - public static final FlowType UNCONDITIONAL_CALL = new FlowType(__UNCONDITIONAL_CALL, - "UNCONDITIONAL_CALL", true, true, false, false, false, false); - public static final FlowType CONDITIONAL_CALL = - new FlowType(__CONDITIONAL_CALL, "CONDITIONAL_CALL", true, true, false, false, false, true); + new FlowType.Builder(__FALL_THROUGH, "FALL_THROUGH").setHasFall().build(); + public static final FlowType UNCONDITIONAL_JUMP = + new FlowType.Builder(__UNCONDITIONAL_JUMP, "UNCONDITIONAL_JUMP").setIsJump().build(); + public static final FlowType CONDITIONAL_JUMP = new FlowType.Builder(__CONDITIONAL_JUMP, + "CONDITIONAL_JUMP").setHasFall().setIsJump().setIsConditional().build(); + public static final FlowType UNCONDITIONAL_CALL = new FlowType.Builder(__UNCONDITIONAL_CALL, + "UNCONDITIONAL_CALL").setHasFall().setIsCall().build(); + public static final FlowType CONDITIONAL_CALL = new FlowType.Builder(__CONDITIONAL_CALL, + "CONDITIONAL CALL").setHasFall().setIsCall().setIsConditional().build(); public static final FlowType TERMINATOR = - new FlowType(__TERMINATOR, "TERMINATOR", false, false, false, true, false, false); + new FlowType.Builder(__TERMINATOR, "TERMINATOR").setIsTerminal().build(); public static final FlowType COMPUTED_JUMP = - new FlowType(__COMPUTED_JUMP, "COMPUTED_JUMP", false, false, true, false, true, false); - public static final FlowType CONDITIONAL_TERMINATOR = new FlowType(__CONDITIONAL_TERMINATOR, - "CONDITIONAL_TERMINATOR", true, false, false, true, false, true); - public static final FlowType COMPUTED_CALL = - new FlowType(__COMPUTED_CALL, "COMPUTED_CALL", true, true, false, false, true, false); - public static final FlowType CALL_TERMINATOR = - new FlowType(__CALL_TERMINATOR, "CALL_TERMINATOR", false, true, false, true, false, false); - public static final FlowType COMPUTED_CALL_TERMINATOR = new FlowType(__COMPUTED_CALL_TERMINATOR, - "COMPUTED_CALL_TERMINATOR", false, true, false, true, true, false); + new FlowType.Builder(__COMPUTED_JUMP, "COMPUTED_JUMP").setIsJump().setIsComputed().build(); + public static final FlowType CONDITIONAL_TERMINATOR = + new FlowType.Builder(__CONDITIONAL_TERMINATOR, + "CONDITIONAL_TERMINATOR").setHasFall().setIsTerminal().setIsConditional().build(); + public static final FlowType COMPUTED_CALL = new FlowType.Builder(__COMPUTED_CALL, + "COMPUTED_CALL").setHasFall().setIsCall().setIsComputed().build(); + public static final FlowType CALL_TERMINATOR = new FlowType.Builder(__CALL_TERMINATOR, + "CALL_TERMINATOR").setIsCall().setIsTerminal().build(); + public static final FlowType COMPUTED_CALL_TERMINATOR = + new FlowType.Builder(__COMPUTED_CALL_TERMINATOR, + "COMPUTED_CALL_TERMINATOR").setIsCall().setIsTerminal().setIsComputed().build(); public static final FlowType CONDITIONAL_CALL_TERMINATOR = - new FlowType(__CONDITIONAL_CALL_TERMINATOR, "CONDITIONAL_CALL_TERMINATOR", false, true, - false, true, false, true); - public static final FlowType CONDITIONAL_COMPUTED_CALL = - new FlowType(__CONDITIONAL_COMPUTED_CALL, "CONDITIONAL_COMPUTED_CALL", true, true, false, - false, true, true); - public static final FlowType CONDITIONAL_COMPUTED_JUMP = - new FlowType(__CONDITIONAL_COMPUTED_JUMP, "CONDITIONAL_COMPUTED_JUMP", true, false, true, - false, true, true); - public static final FlowType JUMP_TERMINATOR = - new FlowType(__JUMP_TERMINATOR, "JUMP TERMINATOR", false, false, true, true, false, false); + new FlowType.Builder(__CONDITIONAL_CALL_TERMINATOR, + "CONDITIONAL_CALL_TERMINATOR").setIsCall().setIsTerminal().setIsConditional().build(); + public static final FlowType CONDITIONAL_COMPUTED_CALL = new FlowType.Builder( + __CONDITIONAL_COMPUTED_CALL, + "CONDITIONAL_COMPUTED_CALL").setHasFall().setIsCall().setIsComputed().setIsConditional().build(); + public static final FlowType CONDITIONAL_COMPUTED_JUMP = new FlowType.Builder( + __CONDITIONAL_COMPUTED_JUMP, + "CONDITIONAL_COMPUTED_JUMP").setHasFall().setIsJump().setIsComputed().setIsConditional().build(); + public static final FlowType JUMP_TERMINATOR = new FlowType.Builder(__JUMP_TERMINATOR, + "JUMP_TERMINATOR").setIsJump().setIsTerminal().build(); public static final FlowType INDIRECTION = - new FlowType(__INDIRECTION, "INDIRECTION", false, false, false, false, false, false); + new FlowType.Builder(__INDIRECTION, "INDIRECTION").build(); + public static final FlowType CALL_OVERRIDE_UNCONDITIONAL = + new FlowType.Builder(__CALL_OVERRIDE_UNCONDITIONAL, + "CALL_OVERRIDE_UNCONDITIONAL").setHasFall().setIsCall().setIsOverride().build(); + public static final FlowType JUMP_OVERRIDE_UNCONDITIONAL = + new FlowType.Builder(__JUMP_OVERRIDE_UNCONDITIONAL, + "JUMP_OVERRIDE_UNCONDITIONAL").setIsJump().setIsOverride().build(); + public static final FlowType CALLOTHER_OVERRIDE_CALL = + new FlowType.Builder(__CALLOTHER_OVERRIDE_CALL, + "CALLOTHER_OVERRIDE_CALL").setHasFall().setIsCall().setIsOverride().build(); + public static final FlowType CALLOTHER_OVERRIDE_JUMP = + new FlowType.Builder(__CALLOTHER_OVERRIDE_JUMP, + "CALLOTHER_OVERRIDE_JUMP").setIsJump().setIsOverride().build(); /** * Reference type is unknown. @@ -272,7 +292,7 @@ public abstract class RefType { } /** - * Returns true if the flow is a conditiona call or jump. + * Returns true if the flow is a conditional call or jump. */ public boolean isConditional() { return false; @@ -292,6 +312,14 @@ public abstract class RefType { return false; } + /** + * + * @return true precisely when the reference is an overriding reference + */ + public boolean isOverride() { + return false; + } + /** * @see java.lang.Object#equals(java.lang.Object) */ diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/symbol/RefTypeFactory.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/symbol/RefTypeFactory.java index c1957a034f..fe687481c4 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/symbol/RefTypeFactory.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/symbol/RefTypeFactory.java @@ -77,6 +77,14 @@ public class RefTypeFactory { REFTYPE_LOOKUP_BY_TYPE_MAP.put(RefType.STACK_READ.getValue(), RefType.STACK_READ); REFTYPE_LOOKUP_BY_TYPE_MAP.put(RefType.STACK_WRITE.getValue(), RefType.STACK_WRITE); REFTYPE_LOOKUP_BY_TYPE_MAP.put(RefType.EXTERNAL_REF.getValue(), RefType.EXTERNAL_REF); + REFTYPE_LOOKUP_BY_TYPE_MAP.put(RefType.__CALL_OVERRIDE_UNCONDITIONAL, + RefType.CALL_OVERRIDE_UNCONDITIONAL); + REFTYPE_LOOKUP_BY_TYPE_MAP.put(RefType.__JUMP_OVERRIDE_UNCONDITIONAL, + RefType.JUMP_OVERRIDE_UNCONDITIONAL); + REFTYPE_LOOKUP_BY_TYPE_MAP.put(RefType.__CALLOTHER_OVERRIDE_CALL, + RefType.CALLOTHER_OVERRIDE_CALL); + REFTYPE_LOOKUP_BY_TYPE_MAP.put(RefType.__CALLOTHER_OVERRIDE_JUMP, + RefType.CALLOTHER_OVERRIDE_JUMP); } private static RefType[] memoryRefTypes = new RefType[] { RefType.INDIRECTION, @@ -84,7 +92,9 @@ public class RefTypeFactory { RefType.CONDITIONAL_JUMP, RefType.UNCONDITIONAL_CALL, RefType.UNCONDITIONAL_JUMP, RefType.CONDITIONAL_COMPUTED_CALL, RefType.CONDITIONAL_COMPUTED_JUMP, RefType.PARAM, RefType.DATA, RefType.DATA_IND, RefType.READ, RefType.READ_IND, RefType.WRITE, - RefType.WRITE_IND, RefType.READ_WRITE, RefType.READ_WRITE_IND }; + RefType.WRITE_IND, RefType.READ_WRITE, RefType.READ_WRITE_IND, + RefType.CALL_OVERRIDE_UNCONDITIONAL, RefType.JUMP_OVERRIDE_UNCONDITIONAL, + RefType.CALLOTHER_OVERRIDE_CALL, RefType.CALLOTHER_OVERRIDE_JUMP }; private static HashSet validMemRefTypes = new HashSet<>(); static { @@ -99,13 +109,13 @@ public class RefTypeFactory { private static RefType[] dataRefTypes = new RefType[] { RefType.DATA, RefType.PARAM, RefType.READ, RefType.WRITE, RefType.READ_WRITE, }; - private static RefType[] extRefTypes = new RefType[] { - // TODO: RefType.EXTERNAL_REF should be deprecated and RefType.DATA taking its place - RefType.COMPUTED_CALL, RefType.COMPUTED_JUMP, RefType.CONDITIONAL_CALL, - RefType.CONDITIONAL_JUMP, RefType.UNCONDITIONAL_CALL, RefType.UNCONDITIONAL_JUMP, - RefType.CONDITIONAL_COMPUTED_CALL, RefType.CONDITIONAL_COMPUTED_JUMP, RefType.DATA, - RefType.DATA_IND, RefType.READ, RefType.READ_IND, RefType.WRITE, RefType.WRITE_IND, - RefType.READ_WRITE, RefType.READ_WRITE_IND }; + private static RefType[] extRefTypes = + new RefType[] { RefType.COMPUTED_CALL, RefType.COMPUTED_JUMP, RefType.CONDITIONAL_CALL, + RefType.CONDITIONAL_JUMP, RefType.UNCONDITIONAL_CALL, RefType.UNCONDITIONAL_JUMP, + RefType.CONDITIONAL_COMPUTED_CALL, RefType.CONDITIONAL_COMPUTED_JUMP, RefType.DATA, + RefType.DATA_IND, RefType.READ, RefType.READ_IND, RefType.WRITE, RefType.WRITE_IND, + RefType.READ_WRITE, RefType.READ_WRITE_IND, RefType.CALL_OVERRIDE_UNCONDITIONAL, + RefType.CALLOTHER_OVERRIDE_CALL, RefType.CALLOTHER_OVERRIDE_JUMP }; public static RefType[] getMemoryRefTypes() { return memoryRefTypes;