diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/copying/DebuggerCopyPlan.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/copying/DebuggerCopyPlan.java
index 818067afff..c1ba706387 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/copying/DebuggerCopyPlan.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/copying/DebuggerCopyPlan.java
@@ -131,7 +131,8 @@ public class DebuggerCopyPlan {
}
long off = ins.getMinAddress().subtract(fromRange.getMinAddress());
Address dest = intoAddress.add(off);
- intoListing.createInstruction(dest, ins.getPrototype(), ins, ins);
+ intoListing.createInstruction(dest, ins.getPrototype(), ins, ins,
+ ins.getLength());
}
}
},
@@ -143,8 +144,7 @@ public class DebuggerCopyPlan {
@Override
public void copy(TraceProgramView from, AddressRange fromRange, Program into,
- Address intoAddress, TaskMonitor monitor)
- throws Exception {
+ Address intoAddress, TaskMonitor monitor) throws Exception {
Listing intoListing = into.getListing();
for (Data data : from.getListing()
.getDefinedData(new AddressSet(fromRange), true)) {
@@ -204,8 +204,8 @@ public class DebuggerCopyPlan {
}
}
- private Namespace findOrCopyNamespace(Namespace ns, SymbolTable intoTable,
- Program into) throws Exception {
+ private Namespace findOrCopyNamespace(Namespace ns, SymbolTable intoTable, Program into)
+ throws Exception {
if (ns.isGlobal()) {
return into.getGlobalNamespace();
}
@@ -442,8 +442,8 @@ public class DebuggerCopyPlan {
public boolean isRequiresInitializedMemory(TraceProgramView from, Program dest) {
return checkBoxes.entrySet().stream().anyMatch(ent -> {
Copier copier = ent.getKey();
- return copier.isRequiresInitializedMemory() &&
- copier.isAvailable(from, dest) && ent.getValue().isSelected();
+ return copier.isRequiresInitializedMemory() && copier.isAvailable(from, dest) &&
+ ent.getValue().isSelected();
});
}
}
diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceCodeUnitAdapter.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceCodeUnitAdapter.java
index 08f680b1e3..d96aae386c 100644
--- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceCodeUnitAdapter.java
+++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceCodeUnitAdapter.java
@@ -15,7 +15,7 @@
*/
package ghidra.trace.database.listing;
-import static ghidra.lifecycle.Unfinished.TODO;
+import static ghidra.lifecycle.Unfinished.*;
import java.nio.ByteBuffer;
import java.util.*;
@@ -278,11 +278,6 @@ public interface DBTraceCodeUnitAdapter extends TraceCodeUnit, MemBufferMixin {
return DBTraceCommentAdapter.arrayFromComment(getComment(commentType));
}
- @Override
- default boolean isSuccessor(CodeUnit codeUnit) {
- return getMaxAddress().isSuccessor(codeUnit.getMinAddress());
- }
-
@Override
default boolean contains(Address testAddr) {
return getMinAddress().compareTo(testAddr) <= 0 && testAddr.compareTo(getMaxAddress()) <= 0;
diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceInstruction.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceInstruction.java
index 9ba646ac6f..347bec2e49 100644
--- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceInstruction.java
+++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceInstruction.java
@@ -21,12 +21,14 @@ import java.nio.ByteBuffer;
import java.util.*;
import db.DBRecord;
+import ghidra.program.database.code.InstructionDB;
import ghidra.program.model.address.*;
import ghidra.program.model.lang.*;
import ghidra.program.model.listing.ContextChangeException;
import ghidra.program.model.listing.FlowOverride;
import ghidra.program.model.mem.*;
import ghidra.program.model.symbol.*;
+import ghidra.program.model.util.CodeUnitInsertionException;
import ghidra.trace.database.DBTrace;
import ghidra.trace.database.DBTraceUtils;
import ghidra.trace.database.context.DBTraceRegisterContextManager;
@@ -36,7 +38,7 @@ import ghidra.trace.database.guest.InternalTracePlatform;
import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree;
import ghidra.trace.database.symbol.DBTraceReference;
import ghidra.trace.database.symbol.DBTraceReferenceSpace;
-import ghidra.trace.model.Lifespan;
+import ghidra.trace.model.*;
import ghidra.trace.model.Trace.TraceInstructionChangeType;
import ghidra.trace.model.guest.TracePlatform;
import ghidra.trace.model.listing.TraceInstruction;
@@ -52,17 +54,21 @@ import ghidra.util.database.annot.*;
* The implementation of {@link TraceInstruction} for {@link DBTrace}
*/
@DBAnnotatedObjectInfo(version = 0)
-public class DBTraceInstruction extends AbstractDBTraceCodeUnit implements
- TraceInstruction, InstructionAdapterFromPrototype, InstructionContext {
+public class DBTraceInstruction extends AbstractDBTraceCodeUnit
+ implements TraceInstruction, InstructionAdapterFromPrototype, InstructionContext {
private static final Address[] EMPTY_ADDRESS_ARRAY = new Address[] {};
private static final String TABLE_NAME = "Instructions";
private static final byte FALLTHROUGH_SET_MASK = 0x01;
private static final byte FALLTHROUGH_CLEAR_MASK = ~FALLTHROUGH_SET_MASK;
- private static final byte FLOWOVERRIDE_SET_MASK = 0x0e;
- private static final byte FLOWOVERRIDE_CLEAR_MASK = ~FLOWOVERRIDE_SET_MASK;
- private static final int FLOWOVERRIDE_SHIFT = 1;
+ private static final byte FLOW_OVERRIDE_SET_MASK = 0x0e;
+ private static final byte FLOW_OVERRIDE_CLEAR_MASK = ~FLOW_OVERRIDE_SET_MASK;
+ private static final int FLOW_OVERRIDE_SHIFT = 1;
+
+ private static final byte LENGTH_OVERRIDE_SET_MASK = 0x70;
+ private static final byte LENGTH_OVERRIDE_CLEAR_MASK = ~LENGTH_OVERRIDE_SET_MASK;
+ private static final int LENGTH_OVERRIDE_SHIFT = 4;
static final String PLATFORM_COLUMN_NAME = "Platform";
static final String PROTOTYPE_COLUMN_NAME = "Prototype";
@@ -143,6 +149,7 @@ public class DBTraceInstruction extends AbstractDBTraceCodeUnit> FLOWOVERRIDE_SHIFT];
+ flowOverride =
+ FlowOverride.values()[(flags & FLOW_OVERRIDE_SET_MASK) >> FLOW_OVERRIDE_SHIFT];
+
+ lengthOverride = (flags & LENGTH_OVERRIDE_SET_MASK) >> LENGTH_OVERRIDE_SHIFT;
+ if (lengthOverride != 0 && lengthOverride < prototype.getLength()) {
+ Address minAddr = getMinAddress();
+ Address newEndAddr = minAddr.add(lengthOverride - 1);
+ doSetRange(new AddressRangeImpl(minAddr, newEndAddr));
+ }
doSetPlatformMapping(platform);
}
@@ -334,8 +354,7 @@ public class DBTraceInstruction extends AbstractDBTraceCodeUnit(TraceInstructionChangeType.FLOW_OVERRIDE_CHANGED,
- space, this, oldFlowOverride, flowOverride));
+ new TraceChangeRecord<>(TraceInstructionChangeType.FLOW_OVERRIDE_CHANGED, space, this,
+ oldFlowOverride, flowOverride));
+ }
+
+ @Override
+ public void setLengthOverride(int length) throws CodeUnitInsertionException {
+ int oldLengthOverride = this.lengthOverride;
+ try (LockHold hold = space.trace.lockWrite()) {
+ checkDeleted();
+ InstructionPrototype proto = getPrototype();
+ length = InstructionDB.checkLengthOverride(length, proto);
+ if (length == lengthOverride) {
+ return; // no change
+ }
+
+ int newLength = length != 0 ? length : proto.getLength();
+ if (newLength > getLength()) {
+ Address minAddr = getMinAddress();
+ Address newEndAddr = minAddr.add(newLength - 1);
+ TraceAddressSnapRange tasr = new ImmutableTraceAddressSnapRange(
+ new AddressRangeImpl(minAddr.next(), newEndAddr), getLifespan());
+ for (AbstractDBTraceCodeUnit> cu : space.definedUnits.getIntersecting(tasr)) {
+ if (cu != this) {
+ throw new CodeUnitInsertionException(
+ "Length override of " + newLength + " conflicts with code unit at " +
+ cu.getMinAddress() + ", lifespan=" + cu.getLifespan());
+ }
+ }
+ }
+
+ updateLengthOverride(length);
+ }
+ space.trace.setChanged(
+ new TraceChangeRecord<>(TraceInstructionChangeType.LENGTH_OVERRIDE_CHANGED, space, this,
+ oldLengthOverride, length));
+ }
+
+ private void updateLengthOverride(int length) {
+ flags &= LENGTH_OVERRIDE_CLEAR_MASK;
+ flags |= (length << LENGTH_OVERRIDE_SHIFT);
+ lengthOverride = length;
+ update(FLAGS_COLUMN);
+
+ int newLength = length != 0 ? length : getPrototype().getLength();
+ Address minAddr = getMinAddress();
+ Address newEndAddr = minAddr.add(newLength - 1);
+ doSetRange(new AddressRangeImpl(minAddr, newEndAddr));
+ }
+
+ @Override
+ public boolean isLengthOverridden() {
+ return lengthOverride != 0;
+ }
+
+ @Override
+ public int getLength() {
+ if (lengthOverride != 0) {
+ return lengthOverride;
+ }
+ return super.getLength();
+ }
+
+ @Override
+ public int getParsedLength() {
+ if (lengthOverride == 0) {
+ return super.getLength();
+ }
+ return getPrototype().getLength();
+ }
+
+ @Override
+ public byte[] getParsedBytes() throws MemoryAccessException {
+ if (!isLengthOverridden()) {
+ return getBytes();
+ }
+ try (LockHold hold = LockHold.lock(space.lock.readLock())) {
+ checkIsValid();
+ int len = getPrototype().getLength();
+ byte[] b = new byte[len];
+ Address addr = getAddress();
+ if (len != getMemory().getBytes(addr, b)) {
+ throw new MemoryAccessException("Failed to read " + len + " bytes at " + addr);
+ }
+ return b;
+ }
}
@Override
@@ -622,8 +731,8 @@ public class DBTraceInstruction extends AbstractDBTraceCodeUnit(TraceInstructionChangeType.FALL_THROUGH_OVERRIDE_CHANGED,
- space, this, !overridden, overridden));
+ new TraceChangeRecord<>(TraceInstructionChangeType.FALL_THROUGH_OVERRIDE_CHANGED, space,
+ this, !overridden, overridden));
}
@Override
diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceInstructionsMemoryView.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceInstructionsMemoryView.java
index 3f5d315004..a55df5600b 100644
--- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceInstructionsMemoryView.java
+++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceInstructionsMemoryView.java
@@ -59,9 +59,10 @@ public class DBTraceInstructionsMemoryView
@Override
public DBTraceInstruction create(Lifespan lifespan, Address address,
TracePlatform platform, InstructionPrototype prototype,
- ProcessorContextView context) throws CodeUnitInsertionException {
+ ProcessorContextView context, int forcedLengthOverride)
+ throws CodeUnitInsertionException {
return delegateWrite(address.getAddressSpace(),
- m -> m.create(lifespan, address, platform, prototype, context));
+ m -> m.create(lifespan, address, platform, prototype, context, forcedLengthOverride));
}
@Override
diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceInstructionsView.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceInstructionsView.java
index 676dccd17b..95fd050ab0 100644
--- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceInstructionsView.java
+++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceInstructionsView.java
@@ -19,6 +19,7 @@ import java.util.*;
import org.apache.commons.lang3.tuple.Pair;
+import ghidra.program.database.code.InstructionDB;
import ghidra.program.model.address.*;
import ghidra.program.model.lang.*;
import ghidra.program.model.lang.InstructionError.InstructionErrorType;
@@ -103,16 +104,22 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView
protected boolean isSuitable(Instruction candidate, Instruction protoInstr) {
try {
return candidate.getPrototype().equals(protoInstr.getPrototype()) &&
- Arrays.equals(candidate.getBytes(), protoInstr.getBytes()) &&
candidate.isFallThroughOverridden() == protoInstr.isFallThroughOverridden() &&
Objects.equals(candidate.getFallThrough(), protoInstr.getFallThrough()) &&
- candidate.getFlowOverride() == protoInstr.getFlowOverride();
+ candidate.getFlowOverride() == protoInstr.getFlowOverride() &&
+ candidate.getLength() == protoInstr.getLength() && // handles potential length override
+ hasSameBytes(candidate, protoInstr);
}
catch (MemoryAccessException e) {
throw new AssertionError(e);
}
}
+ private boolean hasSameBytes(Instruction instr1, Instruction instr2)
+ throws MemoryAccessException {
+ return Arrays.equals(instr1.getParsedBytes(), instr2.getParsedBytes());
+ }
+
protected Instruction doAdjustExisting(Address address, Instruction protoInstr)
throws AddressOverflowException, CancelledException, CodeUnitInsertionException {
DBTraceInstruction exists = getAt(lifespan.lmin(), address);
@@ -193,8 +200,8 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView
return prec;
}
- Instruction created =
- doCreate(lifespan, address, platform, protoInstr.getPrototype(), protoInstr);
+ Instruction created = doCreate(lifespan, address, platform,
+ protoInstr.getPrototype(), protoInstr, protoInstr.getLength());
// copy override settings to replacement instruction
if (protoInstr.isFallThroughOverridden()) {
created.setFallThrough(protoInstr.getFallThrough());
@@ -336,20 +343,30 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView
* @param platform the platform (language, compiler) for the instruction
* @param prototype the instruction's prototype
* @param context the initial context for parsing the instruction
- * @return the new instructions
- * @throws CodeUnitInsertionException if the instruction cannot be created due to an existing
- * unit
+ * @param length instruction byte-length (must be in the range 0..prototype.getLength()).
+ * If smaller than the prototype length it must have a value no greater than 7, otherwise
+ * an error will be thrown. A value of 0 or greater-than-or-equal the prototype length
+ * will be ignored and not impose and override length. The length value must be a multiple
+ * of the {@link Language#getInstructionAlignment() instruction alignment} .
+ * @return the newly created instruction.
+ * @throws CodeUnitInsertionException thrown if the new Instruction would overlap and
+ * existing {@link CodeUnit} or the specified {@code length} is unsupported.
+ * @throws IllegalArgumentException if a negative {@code length} is specified.
* @throws AddressOverflowException if the instruction would fall off the address space
*/
protected DBTraceInstruction doCreate(Lifespan lifespan, Address address,
InternalTracePlatform platform, InstructionPrototype prototype,
- ProcessorContextView context)
+ ProcessorContextView context, int length)
throws CodeUnitInsertionException, AddressOverflowException {
if (platform.getLanguage() != prototype.getLanguage()) {
throw new IllegalArgumentException("Platform and prototype disagree in language");
}
- Address endAddress = address.addNoWrap(prototype.getLength() - 1);
+ int forcedLengthOverride = InstructionDB.checkLengthOverride(length, prototype);
+ if (length == 0) {
+ length = prototype.getLength();
+ }
+ Address endAddress = address.addNoWrap(length - 1);
AddressRangeImpl createdRange = new AddressRangeImpl(address, endAddress);
// Truncate, then check that against existing code units.
@@ -365,7 +382,7 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView
doSetContext(tasr, prototype.getLanguage(), context);
DBTraceInstruction created = space.instructionMapSpace.put(tasr, null);
- created.set(platform, prototype, context);
+ created.set(platform, prototype, context, forcedLengthOverride);
cacheForContaining.notifyNewEntry(tasr.getLifespan(), createdRange, created);
cacheForSequence.notifyNewEntry(tasr.getLifespan(), createdRange, created);
@@ -379,14 +396,14 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView
@Override
public DBTraceInstruction create(Lifespan lifespan, Address address, TracePlatform platform,
- InstructionPrototype prototype, ProcessorContextView context)
+ InstructionPrototype prototype, ProcessorContextView context, int forcedLengthOverride)
throws CodeUnitInsertionException {
InternalTracePlatform dbPlatform = space.manager.platformManager.assertMine(platform);
try (LockHold hold = LockHold.lock(space.lock.writeLock())) {
DBTraceInstruction created =
- doCreate(lifespan, address, dbPlatform, prototype, context);
- space.trace.setChanged(new TraceChangeRecord<>(TraceCodeChangeType.ADDED,
- space, created, created));
+ doCreate(lifespan, address, dbPlatform, prototype, context, forcedLengthOverride);
+ space.trace.setChanged(
+ new TraceChangeRecord<>(TraceCodeChangeType.ADDED, space, created, created));
return created;
}
catch (AddressOverflowException e) {
@@ -567,9 +584,9 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView
if (lastInstruction != null) {
Address maxAddress = DBTraceCodeManager.instructionMax(lastInstruction, true);
result.addRange(block.getStartAddress(), maxAddress);
- space.trace.setChanged(new TraceChangeRecord<>(TraceCodeChangeType.ADDED,
- space, new ImmutableTraceAddressSnapRange(
- block.getStartAddress(), maxAddress, lifespan)));
+ space.trace.setChanged(new TraceChangeRecord<>(TraceCodeChangeType.ADDED, space,
+ new ImmutableTraceAddressSnapRange(block.getStartAddress(), maxAddress,
+ lifespan)));
}
}
return result;
diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/program/AbstractDBTraceProgramViewListing.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/program/AbstractDBTraceProgramViewListing.java
index 22b316fd77..a3263acf40 100644
--- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/program/AbstractDBTraceProgramViewListing.java
+++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/program/AbstractDBTraceProgramViewListing.java
@@ -708,18 +708,19 @@ public abstract class AbstractDBTraceProgramViewListing implements TraceProgramV
}
@Override
- @SuppressWarnings("rawtypes")
- public PropertyMap getPropertyMap(String propertyName) {
+ public PropertyMap> getPropertyMap(String propertyName) {
// TODO Auto-generated method stub
return null;
}
@Override
public Instruction createInstruction(Address addr, InstructionPrototype prototype,
- MemBuffer memBuf, ProcessorContextView context) throws CodeUnitInsertionException {
+ MemBuffer memBuf, ProcessorContextView context, int forcedLengthOverride)
+ throws CodeUnitInsertionException {
// TODO: Why memBuf? Can it vary from program memory?
return codeOperations.instructions()
- .create(Lifespan.nowOn(program.snap), addr, platform, prototype, context);
+ .create(Lifespan.nowOn(program.snap), addr, platform, prototype, context,
+ forcedLengthOverride);
}
@Override
diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/program/DBTraceProgramView.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/program/DBTraceProgramView.java
index 1bc046f38c..a6e1ae917b 100644
--- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/program/DBTraceProgramView.java
+++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/program/DBTraceProgramView.java
@@ -129,6 +129,8 @@ public class DBTraceProgramView implements TraceProgramView {
listenFor(TraceDataTypeChangeType.RENAMED, this::dataTypeRenamed);
listenFor(TraceDataTypeChangeType.DELETED, this::dataTypeDeleted);
+ listenFor(TraceInstructionChangeType.LENGTH_OVERRIDE_CHANGED,
+ this::instructionLengthOverrideChanged);
listenFor(TraceInstructionChangeType.FLOW_OVERRIDE_CHANGED,
this::instructionFlowOverrideChanged);
listenFor(TraceInstructionChangeType.FALL_THROUGH_OVERRIDE_CHANGED,
@@ -466,9 +468,19 @@ public class DBTraceProgramView implements TraceProgramView {
return;
}
queues.fireEvent(new ProgramChangeRecord(ChangeManager.DOCR_FALLTHROUGH_CHANGED,
- instruction.getMinAddress(), instruction.getMaxAddress(), null, null, null));
+ instruction.getMinAddress(), instruction.getMinAddress(), null, null, null));
}
+ private void instructionLengthOverrideChanged(TraceAddressSpace space,
+ TraceInstruction instruction, int oldLengthOverride, int newLengthOverride) {
+ DomainObjectEventQueues queues = isCodeVisible(space, instruction);
+ if (queues == null) {
+ return;
+ }
+ queues.fireEvent(new ProgramChangeRecord(ChangeManager.DOCR_LENGTH_OVERRIDE_CHANGED,
+ instruction.getMinAddress(), instruction.getMinAddress(), null, null, null));
+ }
+
private void memoryBytesChanged(TraceAddressSpace space, TraceAddressSnapRange range,
byte[] oldIsNull, byte[] bytes) {
DomainObjectEventQueues queues = isBytesVisible(space, range);
diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/Trace.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/Trace.java
index f3ec5693bf..9300b72886 100644
--- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/Trace.java
+++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/Trace.java
@@ -235,6 +235,8 @@ public interface Trace extends DataTypeManagerDomainObject {
new TraceInstructionChangeType<>();
public static final TraceInstructionChangeType FALL_THROUGH_OVERRIDE_CHANGED =
new TraceInstructionChangeType<>();
+ public static final TraceInstructionChangeType LENGTH_OVERRIDE_CHANGED =
+ new TraceInstructionChangeType<>();
}
public static final class TraceMemoryBytesChangeType
diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/listing/TraceInstructionsView.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/listing/TraceInstructionsView.java
index 34e2c03259..c8028156e6 100644
--- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/listing/TraceInstructionsView.java
+++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/listing/TraceInstructionsView.java
@@ -37,11 +37,12 @@ public interface TraceInstructionsView extends TraceBaseDefinedUnitsViewStatic Disassembly
Restricted Disassembly
+
+ Disassemble ARM / Thumb
+ Processor Options
+
+ Modify Instruction Flow
+
+ Modify Instruction Length
+
Disassembly
@@ -131,7 +139,7 @@
undefined bytes.
- Disassembly (Restricted)
+ Restricted Disassembly
Restricted Disassembly is similar to Disassembly, except that only bytes in
@@ -157,13 +165,11 @@
undefined bytes.
-
-
- Disassemble ARM / Disassemble ThumbDisassemble ARM / Thumb
- Disassemble ARM and Disassemble Thumb will only be available if the program you are
+
Disassemble ARM and Disassemble Thumb actions will only be available if the program you are
working with is an ARM based processor. ARM processors have two states, ARM and Thumb
mode. The instructions available in ARM mode are 4 bytes long. In Thumb mode, the
instructions are "generally" 2 bytes long. ARM and Thumb mode are mutually exclusive,
@@ -199,38 +205,6 @@
undefined bytes.
- Modify Instruction Flow
-
-
- With certain processors and situations it may be desirable to modify the default
- flow of an instruction to better reflect the nature of its flow. For example a jump may
- actually be performing a call type operation, a call may be performing a long-jump.
- This distinction primarily affects the subroutine models and flow analysis performed
- within Ghidra.
- The following basic flow types may be imposed upon the default flow of an instruction:
-
- - BRANCH
- - CALL
- - CALL_RETURN
- - RETURN
-
- In all situations the conditional nature of the original flow is perserved.
- To Modify Instruction Flow:
-
- - Place the cursor on an instruction within the Code Browser. Note that
- instructions which are purely fall-through can not be modified.
- - Right-mouse-click
- - Select Modify Instruction Flow... menu item.
-
- Within the Modify Instruction Flow dialog select the desired basic flow
- behavior.
- - Click OK in the dialog
-
-
An instruction whose flow has been
- modified will have its' mnemonic color modified.
-
-
-
-
Processor Options
@@ -244,11 +218,6 @@
-
-
-
-
-
The options are
@@ -282,8 +251,90 @@
Languages
-
-
+
+ Modify Instruction Flow
+
+
+ With certain processors and situations it may be desirable to modify the default
+ flow of an instruction to better reflect the nature of its flow. For example a jump may
+ actually be performing a call type operation, a call may be performing a long-jump.
+ This distinction primarily affects the subroutine models and flow analysis performed
+ within Ghidra.
+ The following basic flow types may be imposed upon the default flow of an instruction:
+
+ - BRANCH
+ - CALL
+ - CALL_RETURN
+ - RETURN
+
+ In all situations the conditional nature of the original flow is perserved.
+ To Modify Instruction Flow:
+
+ - Place the cursor on an instruction within the Code Browser. Note that
+ instructions which are purely fall-through can not be modified.
+ - Right-mouse-click
+ - Select Modify Instruction Flow... menu item.
+
- Within the Modify Instruction Flow dialog select the desired basic flow
+ behavior.
+ - Click OK in the dialog
+
+
An instruction whose flow has been
+ modified will have its' mnemonic color modified.
+
+
+
+ Modify Instruction Length
+
+
+ There are certain situations where code may flow into an offcut location within the
+ middle of another instruction where the bytes happen to form a different instruction.
+ While this generally indicates a bad flow, this can also be a legitimate situation
+ of an overlapping instruction. For example, with x86 instructions where a flow
+ may bypass a LOCK prefix byte. Depending on which flow disassembles first, the situation
+ may manifest differently. Below is an example of the x86 LOCK prefix case. This situation can be
+ quickly identified by the error bookmark along with the offcut reference. In this case the
+ JZ instruction has two flows: 1) one falls-through and 2) conditionally jumps
+ around the LOCK prefix byte resulting in an offcut flow and disassembly conflict.
+
+
+
+

+
+
+
+ The above case can be resolved by overriding the code-unit length of the first
+ instruction to 1-byte allowing the incomplete disassembly of the offcut instruction
+ to be repaired.
+
+
+ - Place the cursor on an instruction within the Code Browser (e.g.,
+
CMPXCHG instruction).
+ - Right-mouse-click
+ - Select Modify Instruction Length... menu item.
+
- Within the Modify Instruction Length dialog enter the reduced instruction
+ length (e.g., 1 in this case). Note that this does not impact the number of bytes
+ actually parsed, but only the affective code unit length.
+ - Click OK in the dialog
+ - This should result in the subsequent locations becoming undefined code units
+ which can know be disassembled. Click on the first undefined location and disassemble
+ (i.e., D key binding).
+ - You may also delete the error bookmark which should no longer be relavent.
+
+
+ The image below shows this same code after these length override steps have been
+ performed. The fallthrough of the first instruction, whose length was overriden from four
+ to one, is preserved and both instructions fallthrough to the same JNZ
+ instruction.
+
+
+
+
+

+
+
+
+
+