mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-06-02 07:41:33 +08:00
Merge remote-tracking branch 'origin/GP-3256_ghidra1_InstrLengthOverride--SQUASHED'
This commit is contained in:
+7
-7
@@ -131,7 +131,8 @@ public class DebuggerCopyPlan {
|
|||||||
}
|
}
|
||||||
long off = ins.getMinAddress().subtract(fromRange.getMinAddress());
|
long off = ins.getMinAddress().subtract(fromRange.getMinAddress());
|
||||||
Address dest = intoAddress.add(off);
|
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
|
@Override
|
||||||
public void copy(TraceProgramView from, AddressRange fromRange, Program into,
|
public void copy(TraceProgramView from, AddressRange fromRange, Program into,
|
||||||
Address intoAddress, TaskMonitor monitor)
|
Address intoAddress, TaskMonitor monitor) throws Exception {
|
||||||
throws Exception {
|
|
||||||
Listing intoListing = into.getListing();
|
Listing intoListing = into.getListing();
|
||||||
for (Data data : from.getListing()
|
for (Data data : from.getListing()
|
||||||
.getDefinedData(new AddressSet(fromRange), true)) {
|
.getDefinedData(new AddressSet(fromRange), true)) {
|
||||||
@@ -204,8 +204,8 @@ public class DebuggerCopyPlan {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Namespace findOrCopyNamespace(Namespace ns, SymbolTable intoTable,
|
private Namespace findOrCopyNamespace(Namespace ns, SymbolTable intoTable, Program into)
|
||||||
Program into) throws Exception {
|
throws Exception {
|
||||||
if (ns.isGlobal()) {
|
if (ns.isGlobal()) {
|
||||||
return into.getGlobalNamespace();
|
return into.getGlobalNamespace();
|
||||||
}
|
}
|
||||||
@@ -442,8 +442,8 @@ public class DebuggerCopyPlan {
|
|||||||
public boolean isRequiresInitializedMemory(TraceProgramView from, Program dest) {
|
public boolean isRequiresInitializedMemory(TraceProgramView from, Program dest) {
|
||||||
return checkBoxes.entrySet().stream().anyMatch(ent -> {
|
return checkBoxes.entrySet().stream().anyMatch(ent -> {
|
||||||
Copier copier = ent.getKey();
|
Copier copier = ent.getKey();
|
||||||
return copier.isRequiresInitializedMemory() &&
|
return copier.isRequiresInitializedMemory() && copier.isAvailable(from, dest) &&
|
||||||
copier.isAvailable(from, dest) && ent.getValue().isSelected();
|
ent.getValue().isSelected();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-6
@@ -15,7 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package ghidra.trace.database.listing;
|
package ghidra.trace.database.listing;
|
||||||
|
|
||||||
import static ghidra.lifecycle.Unfinished.TODO;
|
import static ghidra.lifecycle.Unfinished.*;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
@@ -278,11 +278,6 @@ public interface DBTraceCodeUnitAdapter extends TraceCodeUnit, MemBufferMixin {
|
|||||||
return DBTraceCommentAdapter.arrayFromComment(getComment(commentType));
|
return DBTraceCommentAdapter.arrayFromComment(getComment(commentType));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
default boolean isSuccessor(CodeUnit codeUnit) {
|
|
||||||
return getMaxAddress().isSuccessor(codeUnit.getMinAddress());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
default boolean contains(Address testAddr) {
|
default boolean contains(Address testAddr) {
|
||||||
return getMinAddress().compareTo(testAddr) <= 0 && testAddr.compareTo(getMaxAddress()) <= 0;
|
return getMinAddress().compareTo(testAddr) <= 0 && testAddr.compareTo(getMaxAddress()) <= 0;
|
||||||
|
|||||||
+132
-23
@@ -21,12 +21,14 @@ import java.nio.ByteBuffer;
|
|||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
import db.DBRecord;
|
import db.DBRecord;
|
||||||
|
import ghidra.program.database.code.InstructionDB;
|
||||||
import ghidra.program.model.address.*;
|
import ghidra.program.model.address.*;
|
||||||
import ghidra.program.model.lang.*;
|
import ghidra.program.model.lang.*;
|
||||||
import ghidra.program.model.listing.ContextChangeException;
|
import ghidra.program.model.listing.ContextChangeException;
|
||||||
import ghidra.program.model.listing.FlowOverride;
|
import ghidra.program.model.listing.FlowOverride;
|
||||||
import ghidra.program.model.mem.*;
|
import ghidra.program.model.mem.*;
|
||||||
import ghidra.program.model.symbol.*;
|
import ghidra.program.model.symbol.*;
|
||||||
|
import ghidra.program.model.util.CodeUnitInsertionException;
|
||||||
import ghidra.trace.database.DBTrace;
|
import ghidra.trace.database.DBTrace;
|
||||||
import ghidra.trace.database.DBTraceUtils;
|
import ghidra.trace.database.DBTraceUtils;
|
||||||
import ghidra.trace.database.context.DBTraceRegisterContextManager;
|
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.map.DBTraceAddressSnapRangePropertyMapTree;
|
||||||
import ghidra.trace.database.symbol.DBTraceReference;
|
import ghidra.trace.database.symbol.DBTraceReference;
|
||||||
import ghidra.trace.database.symbol.DBTraceReferenceSpace;
|
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.Trace.TraceInstructionChangeType;
|
||||||
import ghidra.trace.model.guest.TracePlatform;
|
import ghidra.trace.model.guest.TracePlatform;
|
||||||
import ghidra.trace.model.listing.TraceInstruction;
|
import ghidra.trace.model.listing.TraceInstruction;
|
||||||
@@ -52,17 +54,21 @@ import ghidra.util.database.annot.*;
|
|||||||
* The implementation of {@link TraceInstruction} for {@link DBTrace}
|
* The implementation of {@link TraceInstruction} for {@link DBTrace}
|
||||||
*/
|
*/
|
||||||
@DBAnnotatedObjectInfo(version = 0)
|
@DBAnnotatedObjectInfo(version = 0)
|
||||||
public class DBTraceInstruction extends AbstractDBTraceCodeUnit<DBTraceInstruction> implements
|
public class DBTraceInstruction extends AbstractDBTraceCodeUnit<DBTraceInstruction>
|
||||||
TraceInstruction, InstructionAdapterFromPrototype, InstructionContext {
|
implements TraceInstruction, InstructionAdapterFromPrototype, InstructionContext {
|
||||||
private static final Address[] EMPTY_ADDRESS_ARRAY = new Address[] {};
|
private static final Address[] EMPTY_ADDRESS_ARRAY = new Address[] {};
|
||||||
private static final String TABLE_NAME = "Instructions";
|
private static final String TABLE_NAME = "Instructions";
|
||||||
|
|
||||||
private static final byte FALLTHROUGH_SET_MASK = 0x01;
|
private static final byte FALLTHROUGH_SET_MASK = 0x01;
|
||||||
private static final byte FALLTHROUGH_CLEAR_MASK = ~FALLTHROUGH_SET_MASK;
|
private static final byte FALLTHROUGH_CLEAR_MASK = ~FALLTHROUGH_SET_MASK;
|
||||||
|
|
||||||
private static final byte FLOWOVERRIDE_SET_MASK = 0x0e;
|
private static final byte FLOW_OVERRIDE_SET_MASK = 0x0e;
|
||||||
private static final byte FLOWOVERRIDE_CLEAR_MASK = ~FLOWOVERRIDE_SET_MASK;
|
private static final byte FLOW_OVERRIDE_CLEAR_MASK = ~FLOW_OVERRIDE_SET_MASK;
|
||||||
private static final int FLOWOVERRIDE_SHIFT = 1;
|
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 PLATFORM_COLUMN_NAME = "Platform";
|
||||||
static final String PROTOTYPE_COLUMN_NAME = "Prototype";
|
static final String PROTOTYPE_COLUMN_NAME = "Prototype";
|
||||||
@@ -143,6 +149,7 @@ public class DBTraceInstruction extends AbstractDBTraceCodeUnit<DBTraceInstructi
|
|||||||
|
|
||||||
protected InstructionPrototype prototype;
|
protected InstructionPrototype prototype;
|
||||||
protected FlowOverride flowOverride;
|
protected FlowOverride flowOverride;
|
||||||
|
protected int lengthOverride;
|
||||||
|
|
||||||
protected ParserContext parserContext;
|
protected ParserContext parserContext;
|
||||||
protected InternalTracePlatform platform;
|
protected InternalTracePlatform platform;
|
||||||
@@ -186,18 +193,24 @@ public class DBTraceInstruction extends AbstractDBTraceCodeUnit<DBTraceInstructi
|
|||||||
* @param platform the platform
|
* @param platform the platform
|
||||||
* @param prototype the instruction prototype
|
* @param prototype the instruction prototype
|
||||||
* @param context the context for locating or creating the prototype entry
|
* @param context the context for locating or creating the prototype entry
|
||||||
|
* @param forcedLengthOverride reduced instruction byte-length (1..7) or 0 to use default length
|
||||||
*/
|
*/
|
||||||
protected void set(InternalTracePlatform platform, InstructionPrototype prototype,
|
protected void set(InternalTracePlatform platform, InstructionPrototype prototype,
|
||||||
ProcessorContextView context) {
|
ProcessorContextView context, int forcedLengthOverride) {
|
||||||
this.platformKey = platform.getIntKey();
|
this.platformKey = platform.getIntKey();
|
||||||
// NOTE: Using "this" for the MemBuffer seems a bit precarious.
|
// NOTE: Using "this" for the MemBuffer seems a bit precarious.
|
||||||
DBTraceGuestLanguage languageEntry = platform == null ? null : platform.getLanguageEntry();
|
DBTraceGuestLanguage languageEntry = platform.getLanguageEntry();
|
||||||
this.prototypeKey = (int) space.manager
|
this.prototypeKey =
|
||||||
.findOrRecordPrototype(prototype, languageEntry, this, context)
|
(int) space.manager.findOrRecordPrototype(prototype, languageEntry, this, context)
|
||||||
.getKey();
|
.getKey();
|
||||||
this.flowOverride = FlowOverride.NONE; // flags field is already consistent
|
this.flowOverride = FlowOverride.NONE; // flags field is already consistent
|
||||||
|
this.lengthOverride = 0;
|
||||||
update(PLATFORM_COLUMN, PROTOTYPE_COLUMN, FLAGS_COLUMN);
|
update(PLATFORM_COLUMN, PROTOTYPE_COLUMN, FLAGS_COLUMN);
|
||||||
|
|
||||||
|
if (forcedLengthOverride != 0) {
|
||||||
|
updateLengthOverride(forcedLengthOverride);
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Can there be more in this context than the context register???
|
// TODO: Can there be more in this context than the context register???
|
||||||
doSetPlatformMapping(platform);
|
doSetPlatformMapping(platform);
|
||||||
this.prototype = prototype;
|
this.prototype = prototype;
|
||||||
@@ -216,12 +229,19 @@ public class DBTraceInstruction extends AbstractDBTraceCodeUnit<DBTraceInstructi
|
|||||||
}
|
}
|
||||||
prototype = space.manager.getPrototypeByKey(prototypeKey);
|
prototype = space.manager.getPrototypeByKey(prototypeKey);
|
||||||
if (prototype == null) {
|
if (prototype == null) {
|
||||||
Msg.error(this,
|
Msg.error(this, "Instruction table is corrupt for address " + getMinAddress() +
|
||||||
"Instruction table is corrupt for address " + getMinAddress() +
|
". Missing prototype " + prototypeKey);
|
||||||
". Missing prototype " + prototypeKey);
|
|
||||||
prototype = new InvalidPrototype(getTrace().getBaseLanguage());
|
prototype = new InvalidPrototype(getTrace().getBaseLanguage());
|
||||||
}
|
}
|
||||||
flowOverride = FlowOverride.values()[(flags & FLOWOVERRIDE_SET_MASK) >> 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);
|
doSetPlatformMapping(platform);
|
||||||
}
|
}
|
||||||
@@ -334,8 +354,7 @@ public class DBTraceInstruction extends AbstractDBTraceCodeUnit<DBTraceInstructi
|
|||||||
if (flowType.hasFallthrough()) {
|
if (flowType.hasFallthrough()) {
|
||||||
try {
|
try {
|
||||||
return instructionContext.getAddress()
|
return instructionContext.getAddress()
|
||||||
.addNoWrap(
|
.addNoWrap(prototype.getFallThroughOffset(instructionContext));
|
||||||
prototype.getFallThroughOffset(instructionContext));
|
|
||||||
}
|
}
|
||||||
catch (AddressOverflowException e) {
|
catch (AddressOverflowException e) {
|
||||||
return null;
|
return null;
|
||||||
@@ -364,6 +383,9 @@ public class DBTraceInstruction extends AbstractDBTraceCodeUnit<DBTraceInstructi
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
if (lengthOverride != 0 && getFlowType().hasFallthrough()) {
|
||||||
|
return getMinAddress().add(lengthOverride);
|
||||||
|
}
|
||||||
return getDefaultFallThrough();
|
return getDefaultFallThrough();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -433,6 +455,10 @@ public class DBTraceInstruction extends AbstractDBTraceCodeUnit<DBTraceInstructi
|
|||||||
return EMPTY_ADDRESS_ARRAY;
|
return EMPTY_ADDRESS_ARRAY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (lengthOverride != 0 && getFlowType().hasFallthrough()) {
|
||||||
|
list.add(getMinAddress().add(lengthOverride));
|
||||||
|
}
|
||||||
|
|
||||||
return list.toArray(new Address[list.size()]);
|
return list.toArray(new Address[list.size()]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -526,8 +552,8 @@ public class DBTraceInstruction extends AbstractDBTraceCodeUnit<DBTraceInstructi
|
|||||||
}
|
}
|
||||||
FlowType origFlowType = getFlowType();
|
FlowType origFlowType = getFlowType();
|
||||||
|
|
||||||
flags &= FLOWOVERRIDE_CLEAR_MASK;
|
flags &= FLOW_OVERRIDE_CLEAR_MASK;
|
||||||
flags |= (flowOverride.ordinal() << FLOWOVERRIDE_SHIFT) & FLOWOVERRIDE_SET_MASK;
|
flags |= (flowOverride.ordinal() << FLOW_OVERRIDE_SHIFT) & FLOW_OVERRIDE_SET_MASK;
|
||||||
this.flowOverride = flowOverride;
|
this.flowOverride = flowOverride;
|
||||||
update(FLAGS_COLUMN);
|
update(FLAGS_COLUMN);
|
||||||
|
|
||||||
@@ -545,8 +571,91 @@ public class DBTraceInstruction extends AbstractDBTraceCodeUnit<DBTraceInstructi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
space.trace.setChanged(
|
space.trace.setChanged(
|
||||||
new TraceChangeRecord<>(TraceInstructionChangeType.FLOW_OVERRIDE_CHANGED,
|
new TraceChangeRecord<>(TraceInstructionChangeType.FLOW_OVERRIDE_CHANGED, space, this,
|
||||||
space, this, oldFlowOverride, flowOverride));
|
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
|
@Override
|
||||||
@@ -622,8 +731,8 @@ public class DBTraceInstruction extends AbstractDBTraceCodeUnit<DBTraceInstructi
|
|||||||
}
|
}
|
||||||
update(FLAGS_COLUMN);
|
update(FLAGS_COLUMN);
|
||||||
space.trace.setChanged(
|
space.trace.setChanged(
|
||||||
new TraceChangeRecord<>(TraceInstructionChangeType.FALL_THROUGH_OVERRIDE_CHANGED,
|
new TraceChangeRecord<>(TraceInstructionChangeType.FALL_THROUGH_OVERRIDE_CHANGED, space,
|
||||||
space, this, !overridden, overridden));
|
this, !overridden, overridden));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
+3
-2
@@ -59,9 +59,10 @@ public class DBTraceInstructionsMemoryView
|
|||||||
@Override
|
@Override
|
||||||
public DBTraceInstruction create(Lifespan lifespan, Address address,
|
public DBTraceInstruction create(Lifespan lifespan, Address address,
|
||||||
TracePlatform platform, InstructionPrototype prototype,
|
TracePlatform platform, InstructionPrototype prototype,
|
||||||
ProcessorContextView context) throws CodeUnitInsertionException {
|
ProcessorContextView context, int forcedLengthOverride)
|
||||||
|
throws CodeUnitInsertionException {
|
||||||
return delegateWrite(address.getAddressSpace(),
|
return delegateWrite(address.getAddressSpace(),
|
||||||
m -> m.create(lifespan, address, platform, prototype, context));
|
m -> m.create(lifespan, address, platform, prototype, context, forcedLengthOverride));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
+34
-17
@@ -19,6 +19,7 @@ import java.util.*;
|
|||||||
|
|
||||||
import org.apache.commons.lang3.tuple.Pair;
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
|
|
||||||
|
import ghidra.program.database.code.InstructionDB;
|
||||||
import ghidra.program.model.address.*;
|
import ghidra.program.model.address.*;
|
||||||
import ghidra.program.model.lang.*;
|
import ghidra.program.model.lang.*;
|
||||||
import ghidra.program.model.lang.InstructionError.InstructionErrorType;
|
import ghidra.program.model.lang.InstructionError.InstructionErrorType;
|
||||||
@@ -103,16 +104,22 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView
|
|||||||
protected boolean isSuitable(Instruction candidate, Instruction protoInstr) {
|
protected boolean isSuitable(Instruction candidate, Instruction protoInstr) {
|
||||||
try {
|
try {
|
||||||
return candidate.getPrototype().equals(protoInstr.getPrototype()) &&
|
return candidate.getPrototype().equals(protoInstr.getPrototype()) &&
|
||||||
Arrays.equals(candidate.getBytes(), protoInstr.getBytes()) &&
|
|
||||||
candidate.isFallThroughOverridden() == protoInstr.isFallThroughOverridden() &&
|
candidate.isFallThroughOverridden() == protoInstr.isFallThroughOverridden() &&
|
||||||
Objects.equals(candidate.getFallThrough(), protoInstr.getFallThrough()) &&
|
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) {
|
catch (MemoryAccessException e) {
|
||||||
throw new AssertionError(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)
|
protected Instruction doAdjustExisting(Address address, Instruction protoInstr)
|
||||||
throws AddressOverflowException, CancelledException, CodeUnitInsertionException {
|
throws AddressOverflowException, CancelledException, CodeUnitInsertionException {
|
||||||
DBTraceInstruction exists = getAt(lifespan.lmin(), address);
|
DBTraceInstruction exists = getAt(lifespan.lmin(), address);
|
||||||
@@ -193,8 +200,8 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView
|
|||||||
return prec;
|
return prec;
|
||||||
}
|
}
|
||||||
|
|
||||||
Instruction created =
|
Instruction created = doCreate(lifespan, address, platform,
|
||||||
doCreate(lifespan, address, platform, protoInstr.getPrototype(), protoInstr);
|
protoInstr.getPrototype(), protoInstr, protoInstr.getLength());
|
||||||
// copy override settings to replacement instruction
|
// copy override settings to replacement instruction
|
||||||
if (protoInstr.isFallThroughOverridden()) {
|
if (protoInstr.isFallThroughOverridden()) {
|
||||||
created.setFallThrough(protoInstr.getFallThrough());
|
created.setFallThrough(protoInstr.getFallThrough());
|
||||||
@@ -336,20 +343,30 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView
|
|||||||
* @param platform the platform (language, compiler) for the instruction
|
* @param platform the platform (language, compiler) for the instruction
|
||||||
* @param prototype the instruction's prototype
|
* @param prototype the instruction's prototype
|
||||||
* @param context the initial context for parsing the instruction
|
* @param context the initial context for parsing the instruction
|
||||||
* @return the new instructions
|
* @param length instruction byte-length (must be in the range 0..prototype.getLength()).
|
||||||
* @throws CodeUnitInsertionException if the instruction cannot be created due to an existing
|
* If smaller than the prototype length it must have a value no greater than 7, otherwise
|
||||||
* unit
|
* 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
|
* @throws AddressOverflowException if the instruction would fall off the address space
|
||||||
*/
|
*/
|
||||||
protected DBTraceInstruction doCreate(Lifespan lifespan, Address address,
|
protected DBTraceInstruction doCreate(Lifespan lifespan, Address address,
|
||||||
InternalTracePlatform platform, InstructionPrototype prototype,
|
InternalTracePlatform platform, InstructionPrototype prototype,
|
||||||
ProcessorContextView context)
|
ProcessorContextView context, int length)
|
||||||
throws CodeUnitInsertionException, AddressOverflowException {
|
throws CodeUnitInsertionException, AddressOverflowException {
|
||||||
if (platform.getLanguage() != prototype.getLanguage()) {
|
if (platform.getLanguage() != prototype.getLanguage()) {
|
||||||
throw new IllegalArgumentException("Platform and prototype disagree in language");
|
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);
|
AddressRangeImpl createdRange = new AddressRangeImpl(address, endAddress);
|
||||||
|
|
||||||
// Truncate, then check that against existing code units.
|
// Truncate, then check that against existing code units.
|
||||||
@@ -365,7 +382,7 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView
|
|||||||
doSetContext(tasr, prototype.getLanguage(), context);
|
doSetContext(tasr, prototype.getLanguage(), context);
|
||||||
|
|
||||||
DBTraceInstruction created = space.instructionMapSpace.put(tasr, null);
|
DBTraceInstruction created = space.instructionMapSpace.put(tasr, null);
|
||||||
created.set(platform, prototype, context);
|
created.set(platform, prototype, context, forcedLengthOverride);
|
||||||
|
|
||||||
cacheForContaining.notifyNewEntry(tasr.getLifespan(), createdRange, created);
|
cacheForContaining.notifyNewEntry(tasr.getLifespan(), createdRange, created);
|
||||||
cacheForSequence.notifyNewEntry(tasr.getLifespan(), createdRange, created);
|
cacheForSequence.notifyNewEntry(tasr.getLifespan(), createdRange, created);
|
||||||
@@ -379,14 +396,14 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DBTraceInstruction create(Lifespan lifespan, Address address, TracePlatform platform,
|
public DBTraceInstruction create(Lifespan lifespan, Address address, TracePlatform platform,
|
||||||
InstructionPrototype prototype, ProcessorContextView context)
|
InstructionPrototype prototype, ProcessorContextView context, int forcedLengthOverride)
|
||||||
throws CodeUnitInsertionException {
|
throws CodeUnitInsertionException {
|
||||||
InternalTracePlatform dbPlatform = space.manager.platformManager.assertMine(platform);
|
InternalTracePlatform dbPlatform = space.manager.platformManager.assertMine(platform);
|
||||||
try (LockHold hold = LockHold.lock(space.lock.writeLock())) {
|
try (LockHold hold = LockHold.lock(space.lock.writeLock())) {
|
||||||
DBTraceInstruction created =
|
DBTraceInstruction created =
|
||||||
doCreate(lifespan, address, dbPlatform, prototype, context);
|
doCreate(lifespan, address, dbPlatform, prototype, context, forcedLengthOverride);
|
||||||
space.trace.setChanged(new TraceChangeRecord<>(TraceCodeChangeType.ADDED,
|
space.trace.setChanged(
|
||||||
space, created, created));
|
new TraceChangeRecord<>(TraceCodeChangeType.ADDED, space, created, created));
|
||||||
return created;
|
return created;
|
||||||
}
|
}
|
||||||
catch (AddressOverflowException e) {
|
catch (AddressOverflowException e) {
|
||||||
@@ -567,9 +584,9 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView
|
|||||||
if (lastInstruction != null) {
|
if (lastInstruction != null) {
|
||||||
Address maxAddress = DBTraceCodeManager.instructionMax(lastInstruction, true);
|
Address maxAddress = DBTraceCodeManager.instructionMax(lastInstruction, true);
|
||||||
result.addRange(block.getStartAddress(), maxAddress);
|
result.addRange(block.getStartAddress(), maxAddress);
|
||||||
space.trace.setChanged(new TraceChangeRecord<>(TraceCodeChangeType.ADDED,
|
space.trace.setChanged(new TraceChangeRecord<>(TraceCodeChangeType.ADDED, space,
|
||||||
space, new ImmutableTraceAddressSnapRange(
|
new ImmutableTraceAddressSnapRange(block.getStartAddress(), maxAddress,
|
||||||
block.getStartAddress(), maxAddress, lifespan)));
|
lifespan)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
|||||||
+5
-4
@@ -708,18 +708,19 @@ public abstract class AbstractDBTraceProgramViewListing implements TraceProgramV
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SuppressWarnings("rawtypes")
|
public PropertyMap<?> getPropertyMap(String propertyName) {
|
||||||
public PropertyMap getPropertyMap(String propertyName) {
|
|
||||||
// TODO Auto-generated method stub
|
// TODO Auto-generated method stub
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Instruction createInstruction(Address addr, InstructionPrototype prototype,
|
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?
|
// TODO: Why memBuf? Can it vary from program memory?
|
||||||
return codeOperations.instructions()
|
return codeOperations.instructions()
|
||||||
.create(Lifespan.nowOn(program.snap), addr, platform, prototype, context);
|
.create(Lifespan.nowOn(program.snap), addr, platform, prototype, context,
|
||||||
|
forcedLengthOverride);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
+13
-1
@@ -129,6 +129,8 @@ public class DBTraceProgramView implements TraceProgramView {
|
|||||||
listenFor(TraceDataTypeChangeType.RENAMED, this::dataTypeRenamed);
|
listenFor(TraceDataTypeChangeType.RENAMED, this::dataTypeRenamed);
|
||||||
listenFor(TraceDataTypeChangeType.DELETED, this::dataTypeDeleted);
|
listenFor(TraceDataTypeChangeType.DELETED, this::dataTypeDeleted);
|
||||||
|
|
||||||
|
listenFor(TraceInstructionChangeType.LENGTH_OVERRIDE_CHANGED,
|
||||||
|
this::instructionLengthOverrideChanged);
|
||||||
listenFor(TraceInstructionChangeType.FLOW_OVERRIDE_CHANGED,
|
listenFor(TraceInstructionChangeType.FLOW_OVERRIDE_CHANGED,
|
||||||
this::instructionFlowOverrideChanged);
|
this::instructionFlowOverrideChanged);
|
||||||
listenFor(TraceInstructionChangeType.FALL_THROUGH_OVERRIDE_CHANGED,
|
listenFor(TraceInstructionChangeType.FALL_THROUGH_OVERRIDE_CHANGED,
|
||||||
@@ -466,7 +468,17 @@ public class DBTraceProgramView implements TraceProgramView {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
queues.fireEvent(new ProgramChangeRecord(ChangeManager.DOCR_FALLTHROUGH_CHANGED,
|
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,
|
private void memoryBytesChanged(TraceAddressSpace space, TraceAddressSnapRange range,
|
||||||
|
|||||||
@@ -235,6 +235,8 @@ public interface Trace extends DataTypeManagerDomainObject {
|
|||||||
new TraceInstructionChangeType<>();
|
new TraceInstructionChangeType<>();
|
||||||
public static final TraceInstructionChangeType<Boolean> FALL_THROUGH_OVERRIDE_CHANGED =
|
public static final TraceInstructionChangeType<Boolean> FALL_THROUGH_OVERRIDE_CHANGED =
|
||||||
new TraceInstructionChangeType<>();
|
new TraceInstructionChangeType<>();
|
||||||
|
public static final TraceInstructionChangeType<Integer> LENGTH_OVERRIDE_CHANGED =
|
||||||
|
new TraceInstructionChangeType<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final class TraceMemoryBytesChangeType
|
public static final class TraceMemoryBytesChangeType
|
||||||
|
|||||||
+4
-3
@@ -37,11 +37,12 @@ public interface TraceInstructionsView extends TraceBaseDefinedUnitsView<TraceIn
|
|||||||
* @param platform the platform
|
* @param platform the platform
|
||||||
* @param prototype the instruction prototype
|
* @param prototype the instruction prototype
|
||||||
* @param context the input disassembly context for the instruction
|
* @param context the input disassembly context for the instruction
|
||||||
|
* @param forcedLengthOverride reduced instruction byte-length (1..7) or 0 to use default length
|
||||||
* @return the new instruction
|
* @return the new instruction
|
||||||
* @throws CodeUnitInsertionException if the instruction cannot be created
|
* @throws CodeUnitInsertionException if the instruction cannot be created
|
||||||
*/
|
*/
|
||||||
TraceInstruction create(Lifespan lifespan, Address address, TracePlatform platform,
|
TraceInstruction create(Lifespan lifespan, Address address, TracePlatform platform,
|
||||||
InstructionPrototype prototype, ProcessorContextView context)
|
InstructionPrototype prototype, ProcessorContextView context, int forcedLengthOverride)
|
||||||
throws CodeUnitInsertionException;
|
throws CodeUnitInsertionException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -50,10 +51,10 @@ public interface TraceInstructionsView extends TraceBaseDefinedUnitsView<TraceIn
|
|||||||
* @see #create(Lifespan, Address, TracePlatform, InstructionPrototype, ProcessorContextView)
|
* @see #create(Lifespan, Address, TracePlatform, InstructionPrototype, ProcessorContextView)
|
||||||
*/
|
*/
|
||||||
default TraceInstruction create(Lifespan lifespan, Address address,
|
default TraceInstruction create(Lifespan lifespan, Address address,
|
||||||
InstructionPrototype prototype, ProcessorContextView context)
|
InstructionPrototype prototype, ProcessorContextView context, int forcedLengthOverride)
|
||||||
throws CodeUnitInsertionException {
|
throws CodeUnitInsertionException {
|
||||||
return create(lifespan, address, getTrace().getPlatformManager().getHostPlatform(),
|
return create(lifespan, address, getTrace().getPlatformManager().getHostPlatform(),
|
||||||
prototype, context);
|
prototype, context, forcedLengthOverride);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+2
-1
@@ -564,7 +564,8 @@ public class ToyDBTraceBuilder implements AutoCloseable {
|
|||||||
InstructionBlock block = dis.pseudoDisassembleBlock(memBuf, defaultContextValue, 1);
|
InstructionBlock block = dis.pseudoDisassembleBlock(memBuf, defaultContextValue, 1);
|
||||||
Instruction pseudoIns = block.iterator().next();
|
Instruction pseudoIns = block.iterator().next();
|
||||||
return code.instructions()
|
return code.instructions()
|
||||||
.create(Lifespan.nowOn(snap), start, platform, pseudoIns.getPrototype(), pseudoIns);
|
.create(Lifespan.nowOn(snap), start, platform, pseudoIns.getPrototype(), pseudoIns,
|
||||||
|
0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
-3
@@ -468,9 +468,6 @@ public class DBTraceCodeUnitTest extends AbstractGhidraHeadlessIntegrationTest
|
|||||||
d4008 = b.addData(0, b.addr(0x4008), LongDataType.dataType, b.buf(1, 2, 3, 4));
|
d4008 = b.addData(0, b.addr(0x4008), LongDataType.dataType, b.buf(1, 2, 3, 4));
|
||||||
}
|
}
|
||||||
|
|
||||||
assertTrue(i4004.isSuccessor(i4006));
|
|
||||||
assertFalse(i4004.isSuccessor(d4008));
|
|
||||||
|
|
||||||
assertFalse(i4004.contains(b.addr(0x4003)));
|
assertFalse(i4004.contains(b.addr(0x4003)));
|
||||||
assertTrue(i4004.contains(b.addr(0x4004)));
|
assertTrue(i4004.contains(b.addr(0x4004)));
|
||||||
assertTrue(i4004.contains(b.addr(0x4005)));
|
assertTrue(i4004.contains(b.addr(0x4005)));
|
||||||
|
|||||||
@@ -220,6 +220,8 @@ src/main/help/help/topics/DbViewerPlugin/images/DatabaseViewer.png||GHIDRA||||EN
|
|||||||
src/main/help/help/topics/DisassembledViewPlugin/DisassembledViewPlugin.htm||GHIDRA||||END|
|
src/main/help/help/topics/DisassembledViewPlugin/DisassembledViewPlugin.htm||GHIDRA||||END|
|
||||||
src/main/help/help/topics/DisassembledViewPlugin/images/DisassembledViewPluginMain.png||GHIDRA||||END|
|
src/main/help/help/topics/DisassembledViewPlugin/images/DisassembledViewPluginMain.png||GHIDRA||||END|
|
||||||
src/main/help/help/topics/DisassemblerPlugin/Disassembly.htm||GHIDRA||||END|
|
src/main/help/help/topics/DisassemblerPlugin/Disassembly.htm||GHIDRA||||END|
|
||||||
|
src/main/help/help/topics/DisassemblerPlugin/images/LengthOverrideLockPrefixExample.png||GHIDRA||||END|
|
||||||
|
src/main/help/help/topics/DisassemblerPlugin/images/LengthOverrideLockPrefixExample2.png||GHIDRA||||END|
|
||||||
src/main/help/help/topics/DisassemblerPlugin/images/ProcessorOptions.png||GHIDRA||||END|
|
src/main/help/help/topics/DisassemblerPlugin/images/ProcessorOptions.png||GHIDRA||||END|
|
||||||
src/main/help/help/topics/DockingWindows/Docking_Windows.htm||GHIDRA||||END|
|
src/main/help/help/topics/DockingWindows/Docking_Windows.htm||GHIDRA||||END|
|
||||||
src/main/help/help/topics/DockingWindows/images/Tool.png||GHIDRA||||END|
|
src/main/help/help/topics/DockingWindows/images/Tool.png||GHIDRA||||END|
|
||||||
|
|||||||
@@ -25,6 +25,14 @@
|
|||||||
|
|
||||||
<LI><A href="#Disassemble_Restricted">Restricted Disassembly</A></LI>
|
<LI><A href="#Disassemble_Restricted">Restricted Disassembly</A></LI>
|
||||||
|
|
||||||
|
<LI><A href="#Disassemble_Arm">Disassemble ARM / Thumb</A></LI>
|
||||||
|
|
||||||
|
<LI><A href="#ProcessorOptions">Processor Options</A></LI>
|
||||||
|
|
||||||
|
<LI><A href="#Modify_Instruction_Flow">Modify Instruction Flow</A></LI>
|
||||||
|
|
||||||
|
<LI><A href="#Modify_Instruction_Length">Modify Instruction Length</A></LI>
|
||||||
|
|
||||||
</UL>
|
</UL>
|
||||||
|
|
||||||
<H2><A name="Disassemble"></A>Disassembly</H2>
|
<H2><A name="Disassemble"></A>Disassembly</H2>
|
||||||
@@ -131,7 +139,7 @@
|
|||||||
undefined bytes.</I></P>
|
undefined bytes.</I></P>
|
||||||
</BLOCKQUOTE>
|
</BLOCKQUOTE>
|
||||||
|
|
||||||
<H2><A name="Disassemble_Restricted"></A>Disassembly (Restricted)</H2>
|
<H2><A name="Disassemble_Restricted"></A>Restricted Disassembly</H2>
|
||||||
|
|
||||||
<BLOCKQUOTE>
|
<BLOCKQUOTE>
|
||||||
<P><I>Restricted Disassembly</I> is similar to <I>Disassembly</I>, except that only bytes in
|
<P><I>Restricted Disassembly</I> is similar to <I>Disassembly</I>, except that only bytes in
|
||||||
@@ -157,13 +165,11 @@
|
|||||||
undefined bytes.</I></P>
|
undefined bytes.</I></P>
|
||||||
</BLOCKQUOTE>
|
</BLOCKQUOTE>
|
||||||
|
|
||||||
<P class="providedbyplugin"> </P>
|
<H2><A name="Disassemble_Arm"></A>Disassemble ARM / Thumb<A name=
|
||||||
|
|
||||||
<H2><A name="Disassemble_Arm"></A>Disassemble ARM / Disassemble Thumb<A name=
|
|
||||||
"Disassemble_Thumb"></A></H2>
|
"Disassemble_Thumb"></A></H2>
|
||||||
|
|
||||||
<BLOCKQUOTE>
|
<BLOCKQUOTE>
|
||||||
<P><I>Disassemble ARM and Disassemble Thumb</I> will only be available if the program you are
|
<P><I>Disassemble ARM and Disassemble Thumb actions</I> will only be available if the program you are
|
||||||
working with is an ARM based processor. ARM processors have two states, ARM and Thumb
|
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
|
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,
|
instructions are "generally" 2 bytes long. ARM and Thumb mode are mutually exclusive,
|
||||||
@@ -199,38 +205,6 @@
|
|||||||
undefined bytes.</I></P>
|
undefined bytes.</I></P>
|
||||||
</BLOCKQUOTE>
|
</BLOCKQUOTE>
|
||||||
|
|
||||||
<H2><A name="Modify_Instruction_Flow"></A>Modify Instruction Flow</H2>
|
|
||||||
|
|
||||||
<BLOCKQUOTE>
|
|
||||||
<P>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.</P>
|
|
||||||
<P>The following basic flow types may be imposed upon the default flow of an instruction:</P>
|
|
||||||
<UL>
|
|
||||||
<LI>BRANCH</LI>
|
|
||||||
<LI>CALL</LI>
|
|
||||||
<LI>CALL_RETURN</LI>
|
|
||||||
<LI>RETURN</LI>
|
|
||||||
</UL>
|
|
||||||
<P>In all situations the conditional nature of the original flow is perserved.</P>
|
|
||||||
<P>To Modify Instruction Flow:</P>
|
|
||||||
<OL>
|
|
||||||
<LI>Place the cursor on an instruction within the Code Browser. Note that
|
|
||||||
instructions which are purely fall-through can not be modified.</LI>
|
|
||||||
<LI>Right-mouse-click</LI>
|
|
||||||
<LI>Select <I>Modify Instruction Flow...</I> menu item.
|
|
||||||
<LI>Within the <I>Modify Instruction Flow</I> dialog select the desired basic flow
|
|
||||||
behavior.</LI>
|
|
||||||
<LI>Click OK in the dialog</LI>
|
|
||||||
</OL>
|
|
||||||
<P align="left"><I><IMG src="help/shared/tip.png">An instruction whose flow has been
|
|
||||||
modified will have its' mnemonic color modified.</I></P>
|
|
||||||
|
|
||||||
</BLOCKQUOTE>
|
|
||||||
|
|
||||||
|
|
||||||
<H2><A name="ProcessorOptions"></A>Processor Options</H2>
|
<H2><A name="ProcessorOptions"></A>Processor Options</H2>
|
||||||
|
|
||||||
<BLOCKQUOTE>
|
<BLOCKQUOTE>
|
||||||
@@ -244,11 +218,6 @@
|
|||||||
<DIV align="center">
|
<DIV align="center">
|
||||||
<IMG src="images/ProcessorOptions.png"><BR>
|
<IMG src="images/ProcessorOptions.png"><BR>
|
||||||
<BR>
|
<BR>
|
||||||
|
|
||||||
|
|
||||||
<DIV align="left">
|
|
||||||
<BR>
|
|
||||||
</DIV>
|
|
||||||
</DIV>
|
</DIV>
|
||||||
|
|
||||||
<P>The options are
|
<P>The options are
|
||||||
@@ -282,8 +251,90 @@
|
|||||||
|
|
||||||
<LI><A href="help/topics/LanguageProviderPlugin/Languages.htm">Languages</A></LI>
|
<LI><A href="help/topics/LanguageProviderPlugin/Languages.htm">Languages</A></LI>
|
||||||
</UL>
|
</UL>
|
||||||
|
|
||||||
<P> </P>
|
|
||||||
<BR>
|
<BR>
|
||||||
|
|
||||||
|
<H2><A name="Modify_Instruction_Flow"></A>Modify Instruction Flow</H2>
|
||||||
|
|
||||||
|
<BLOCKQUOTE>
|
||||||
|
<P>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.</P>
|
||||||
|
<P>The following basic flow types may be imposed upon the default flow of an instruction:</P>
|
||||||
|
<UL>
|
||||||
|
<LI>BRANCH</LI>
|
||||||
|
<LI>CALL</LI>
|
||||||
|
<LI>CALL_RETURN</LI>
|
||||||
|
<LI>RETURN</LI>
|
||||||
|
</UL>
|
||||||
|
<P>In all situations the conditional nature of the original flow is perserved.</P>
|
||||||
|
<P>To Modify Instruction Flow:</P>
|
||||||
|
<OL>
|
||||||
|
<LI>Place the cursor on an instruction within the Code Browser. Note that
|
||||||
|
instructions which are purely fall-through can not be modified.</LI>
|
||||||
|
<LI>Right-mouse-click</LI>
|
||||||
|
<LI>Select <I>Modify Instruction Flow...</I> menu item.
|
||||||
|
<LI>Within the <I>Modify Instruction Flow</I> dialog select the desired basic flow
|
||||||
|
behavior.</LI>
|
||||||
|
<LI>Click OK in the dialog</LI>
|
||||||
|
</OL>
|
||||||
|
<P align="left"><I><IMG src="help/shared/tip.png">An instruction whose flow has been
|
||||||
|
modified will have its' mnemonic color modified.</I></P>
|
||||||
|
|
||||||
|
</BLOCKQUOTE>
|
||||||
|
|
||||||
|
<H2><A name="Modify_Instruction_Length"></A>Modify Instruction Length</H2>
|
||||||
|
|
||||||
|
<BLOCKQUOTE>
|
||||||
|
<P>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
|
||||||
|
<code>JZ</code> 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.</P>
|
||||||
|
<BR>
|
||||||
|
|
||||||
|
<DIV align="center">
|
||||||
|
<IMG src="images/LengthOverrideLockPrefixExample.png"><BR>
|
||||||
|
<BR>
|
||||||
|
</DIV>
|
||||||
|
|
||||||
|
<P>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.</P>
|
||||||
|
|
||||||
|
<OL>
|
||||||
|
<LI>Place the cursor on an instruction within the Code Browser (e.g.,
|
||||||
|
<code>CMPXCHG</code> instruction).</LI>
|
||||||
|
<LI>Right-mouse-click</LI>
|
||||||
|
<LI>Select <I>Modify Instruction Length...</I> menu item.
|
||||||
|
<LI>Within the <I>Modify Instruction Length</I> 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.</LI>
|
||||||
|
<LI>Click OK in the dialog</LI>
|
||||||
|
<LI>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., <B>D</B> key binding).</LI>
|
||||||
|
<LI>You may also delete the error bookmark which should no longer be relavent.
|
||||||
|
</OL>
|
||||||
|
|
||||||
|
<P>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 <code>JNZ</code>
|
||||||
|
instruction.
|
||||||
|
</P>
|
||||||
|
<BR>
|
||||||
|
|
||||||
|
<DIV align="center">
|
||||||
|
<IMG src="images/LengthOverrideLockPrefixExample2.png"><BR>
|
||||||
|
<BR>
|
||||||
|
</DIV>
|
||||||
|
|
||||||
|
</BLOCKQUOTE>
|
||||||
|
|
||||||
</BODY>
|
</BODY>
|
||||||
</HTML>
|
</HTML>
|
||||||
|
|||||||
BIN
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
BIN
Binary file not shown.
|
After Width: | Height: | Size: 23 KiB |
@@ -68,28 +68,31 @@ public abstract class CodeUnitDetails {
|
|||||||
boolean removedFallThrough =
|
boolean removedFallThrough =
|
||||||
inst.isFallThroughOverridden() && (inst.getFallThrough() == null);
|
inst.isFallThroughOverridden() && (inst.getFallThrough() == null);
|
||||||
boolean hasFlowOverride = inst.getFlowOverride() != FlowOverride.NONE;
|
boolean hasFlowOverride = inst.getFlowOverride() != FlowOverride.NONE;
|
||||||
|
boolean hasLengthOverride = inst.isLengthOverridden();
|
||||||
cuRep = cu.toString();
|
cuRep = cu.toString();
|
||||||
if (removedFallThrough) {
|
if (removedFallThrough) {
|
||||||
cuRep +=
|
cuRep += NEW_LINE + indent + getSpaces(addrRangeStr.length()) + " " +
|
||||||
NEW_LINE + indent + getSpaces(addrRangeStr.length()) + " " +
|
"Removed FallThrough";
|
||||||
"Removed FallThrough";
|
|
||||||
}
|
}
|
||||||
else if (inst.isFallThroughOverridden()) {
|
else if (inst.isFallThroughOverridden()) {
|
||||||
Reference[] refs = cu.getReferencesFrom();
|
Reference[] refs = cu.getReferencesFrom();
|
||||||
// Show the fallthrough override.
|
// Show the fallthrough override.
|
||||||
for (int i = 0; i < refs.length; i++) {
|
for (Reference ref : refs) {
|
||||||
if (refs[i].getReferenceType().isFallthrough()) {
|
if (ref.getReferenceType().isFallthrough()) {
|
||||||
cuRep +=
|
cuRep += NEW_LINE + indent + getSpaces(addrRangeStr.length()) + " " +
|
||||||
NEW_LINE + indent + getSpaces(addrRangeStr.length()) + " " +
|
"FallThrough Override: " +
|
||||||
"FallThrough Override: " +
|
DiffUtility.getUserToAddressString(inst.getProgram(), ref);
|
||||||
DiffUtility.getUserToAddressString(inst.getProgram(), refs[i]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (hasFlowOverride) {
|
if (hasFlowOverride) {
|
||||||
cuRep +=
|
cuRep += NEW_LINE + indent + getSpaces(addrRangeStr.length()) + " " +
|
||||||
NEW_LINE + indent + getSpaces(addrRangeStr.length()) + " " +
|
"Flow Override: " + inst.getFlowOverride();
|
||||||
"Flow Override: " + inst.getFlowOverride();
|
}
|
||||||
|
if (hasLengthOverride) {
|
||||||
|
cuRep += NEW_LINE + indent + getSpaces(addrRangeStr.length()) + " " +
|
||||||
|
"Length Override: " + inst.getLength() + " (actual length is " +
|
||||||
|
inst.getParsedLength() + ")";
|
||||||
}
|
}
|
||||||
// Commented the following out, since we may want the hash code in the future.
|
// Commented the following out, since we may want the hash code in the future.
|
||||||
// cuRep +=
|
// cuRep +=
|
||||||
@@ -146,15 +149,15 @@ public abstract class CodeUnitDetails {
|
|||||||
return indent + "None";
|
return indent + "None";
|
||||||
}
|
}
|
||||||
StringBuffer buf = new StringBuffer();
|
StringBuffer buf = new StringBuffer();
|
||||||
for (int i = 0; i < refs.length; i++) {
|
for (Reference ref : refs) {
|
||||||
if (refs[i].isExternalReference()) {
|
if (ref.isExternalReference()) {
|
||||||
buf.append(indent + "External Reference " + getRefInfo(pgm, refs[i]) + NEW_LINE);
|
buf.append(indent + "External Reference " + getRefInfo(pgm, ref) + NEW_LINE);
|
||||||
}
|
}
|
||||||
else if (refs[i].isStackReference()) {
|
else if (ref.isStackReference()) {
|
||||||
buf.append(indent + "Stack Reference " + getRefInfo(pgm, refs[i]) + NEW_LINE);
|
buf.append(indent + "Stack Reference " + getRefInfo(pgm, ref) + NEW_LINE);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
buf.append(indent + "Reference " + getRefInfo(pgm, refs[i]) + NEW_LINE);
|
buf.append(indent + "Reference " + getRefInfo(pgm, ref) + NEW_LINE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return buf.toString();
|
return buf.toString();
|
||||||
|
|||||||
@@ -858,7 +858,13 @@ class CodeUnitMerger extends AbstractListingMerger {
|
|||||||
private void performMergeInstruction(Instruction instruction, boolean copyBytes)
|
private void performMergeInstruction(Instruction instruction, boolean copyBytes)
|
||||||
throws CodeUnitInsertionException, MemoryAccessException {
|
throws CodeUnitInsertionException, MemoryAccessException {
|
||||||
Address minAddress = instruction.getMinAddress();
|
Address minAddress = instruction.getMinAddress();
|
||||||
Address maxAddress = instruction.getMaxAddress();
|
Address maxAddress;
|
||||||
|
if (instruction.isLengthOverridden()) {
|
||||||
|
maxAddress = minAddress.add(instruction.getParsedLength() - 1);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
maxAddress = instruction.getMaxAddress();
|
||||||
|
}
|
||||||
Program fromPgm = instruction.getProgram();
|
Program fromPgm = instruction.getProgram();
|
||||||
// Code unit should already be cleared where this instruction needs to go.
|
// Code unit should already be cleared where this instruction needs to go.
|
||||||
Listing resultListing = resultPgm.getListing();
|
Listing resultListing = resultPgm.getListing();
|
||||||
@@ -868,9 +874,12 @@ class CodeUnitMerger extends AbstractListingMerger {
|
|||||||
ProgramMemoryUtil.copyBytesInRanges(resultPgm, fromPgm, minAddress, maxAddress);
|
ProgramMemoryUtil.copyBytesInRanges(resultPgm, fromPgm, minAddress, maxAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// avoid forcing length of new instruction if old instruction length was not overriden
|
||||||
|
int lengthOverride = instruction.isLengthOverridden() ? instruction.getLength() : 0;
|
||||||
|
|
||||||
Instruction inst = resultListing.createInstruction(minAddress, instruction.getPrototype(),
|
Instruction inst = resultListing.createInstruction(minAddress, instruction.getPrototype(),
|
||||||
new DumbMemBufferImpl(resultPgm.getMemory(), minAddress),
|
new DumbMemBufferImpl(resultPgm.getMemory(), minAddress),
|
||||||
new ProgramProcessorContext(resultPgm.getProgramContext(), minAddress));
|
new ProgramProcessorContext(resultPgm.getProgramContext(), minAddress), lengthOverride);
|
||||||
|
|
||||||
// Set the fallthrough override if necessary.
|
// Set the fallthrough override if necessary.
|
||||||
if (instruction.isFallThroughOverridden()) {
|
if (instruction.isFallThroughOverridden()) {
|
||||||
|
|||||||
@@ -570,7 +570,6 @@ public class ListingMergeManager implements MergeResolver, ListingMergeConstants
|
|||||||
* determine the conflicts.
|
* determine the conflicts.
|
||||||
* @param mergers the listing mergers whose conflicts are to be merged.
|
* @param mergers the listing mergers whose conflicts are to be merged.
|
||||||
* @param monitor indicates progress to user and allows cancel.
|
* @param monitor indicates progress to user and allows cancel.
|
||||||
* @throws ProgramConflictException if programs can't be compared using Diff.
|
|
||||||
* @throws MemoryAccessException if bytes can't be merged.
|
* @throws MemoryAccessException if bytes can't be merged.
|
||||||
* @throws CancelledException if the user cancels the merge.
|
* @throws CancelledException if the user cancels the merge.
|
||||||
*/
|
*/
|
||||||
|
|||||||
+1
@@ -445,6 +445,7 @@ public class AutoAnalysisManager implements DomainObjectListener {
|
|||||||
break;
|
break;
|
||||||
case ChangeManager.DOCR_FALLTHROUGH_CHANGED:
|
case ChangeManager.DOCR_FALLTHROUGH_CHANGED:
|
||||||
case ChangeManager.DOCR_FLOWOVERRIDE_CHANGED:
|
case ChangeManager.DOCR_FLOWOVERRIDE_CHANGED:
|
||||||
|
case ChangeManager.DOCR_LENGTH_OVERRIDE_CHANGED:
|
||||||
// TODO: not sure if this should be done this way or explicitly
|
// TODO: not sure if this should be done this way or explicitly
|
||||||
// via the application commands (this is inconsistent with other
|
// via the application commands (this is inconsistent with other
|
||||||
// codeDefined cases which do not rely on change events (e.g., disassembly)
|
// codeDefined cases which do not rely on change events (e.g., disassembly)
|
||||||
|
|||||||
+3
@@ -88,6 +88,7 @@ public class DisassemblerPlugin extends Plugin {
|
|||||||
private DockingAction x86_64DisassembleAction;
|
private DockingAction x86_64DisassembleAction;
|
||||||
private DockingAction x86_32DisassembleAction;
|
private DockingAction x86_32DisassembleAction;
|
||||||
private DockingAction setFlowOverrideAction;
|
private DockingAction setFlowOverrideAction;
|
||||||
|
private DockingAction setLengthOverrideAction;
|
||||||
|
|
||||||
/** Dialog for obtaining the processor state to be used for disassembling. */
|
/** Dialog for obtaining the processor state to be used for disassembling. */
|
||||||
// private ProcessorStateDialog processorStateDialog;
|
// private ProcessorStateDialog processorStateDialog;
|
||||||
@@ -177,6 +178,7 @@ public class DisassemblerPlugin extends Plugin {
|
|||||||
x86_64DisassembleAction = new X86_64DisassembleAction(this, GROUP_NAME, false);
|
x86_64DisassembleAction = new X86_64DisassembleAction(this, GROUP_NAME, false);
|
||||||
x86_32DisassembleAction = new X86_64DisassembleAction(this, GROUP_NAME, true);
|
x86_32DisassembleAction = new X86_64DisassembleAction(this, GROUP_NAME, true);
|
||||||
setFlowOverrideAction = new SetFlowOverrideAction(this, GROUP_NAME);
|
setFlowOverrideAction = new SetFlowOverrideAction(this, GROUP_NAME);
|
||||||
|
setLengthOverrideAction = new SetLengthOverrideAction(this, GROUP_NAME);
|
||||||
|
|
||||||
tool.addAction(disassembleAction);
|
tool.addAction(disassembleAction);
|
||||||
tool.addAction(disassembleRestrictedAction);
|
tool.addAction(disassembleRestrictedAction);
|
||||||
@@ -193,6 +195,7 @@ public class DisassemblerPlugin extends Plugin {
|
|||||||
tool.addAction(x86_32DisassembleAction);
|
tool.addAction(x86_32DisassembleAction);
|
||||||
tool.addAction(contextAction);
|
tool.addAction(contextAction);
|
||||||
tool.addAction(setFlowOverrideAction);
|
tool.addAction(setFlowOverrideAction);
|
||||||
|
tool.addAction(setLengthOverrideAction);
|
||||||
}
|
}
|
||||||
|
|
||||||
void disassembleRestrictedCallback(ListingActionContext context) {
|
void disassembleRestrictedCallback(ListingActionContext context) {
|
||||||
|
|||||||
+137
@@ -0,0 +1,137 @@
|
|||||||
|
/* ###
|
||||||
|
* IP: GHIDRA
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ghidra.app.plugin.core.disassembler;
|
||||||
|
|
||||||
|
import db.Transaction;
|
||||||
|
import docking.action.MenuData;
|
||||||
|
import docking.widgets.dialogs.NumberInputDialog;
|
||||||
|
import ghidra.app.context.ListingActionContext;
|
||||||
|
import ghidra.app.context.ListingContextAction;
|
||||||
|
import ghidra.framework.plugintool.PluginTool;
|
||||||
|
import ghidra.program.model.address.Address;
|
||||||
|
import ghidra.program.model.listing.*;
|
||||||
|
import ghidra.program.model.util.CodeUnitInsertionException;
|
||||||
|
import ghidra.util.Msg;
|
||||||
|
|
||||||
|
class SetLengthOverrideAction extends ListingContextAction {
|
||||||
|
|
||||||
|
private DisassemblerPlugin plugin;
|
||||||
|
|
||||||
|
public SetLengthOverrideAction(DisassemblerPlugin plugin, String groupName) {
|
||||||
|
super("Modify Instruction Length", plugin.getName());
|
||||||
|
this.plugin = plugin;
|
||||||
|
setPopupMenuData(
|
||||||
|
new MenuData(new String[] { "Modify Instruction Length..." }, null, groupName));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ListingActionContext context) {
|
||||||
|
|
||||||
|
PluginTool tool = plugin.getTool();
|
||||||
|
|
||||||
|
Address address = context.getAddress();
|
||||||
|
if (address == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Program program = context.getProgram();
|
||||||
|
Listing listing = program.getListing();
|
||||||
|
Instruction instr = listing.getInstructionAt(address);
|
||||||
|
if (instr == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int protoLen = instr.getPrototype().getLength();
|
||||||
|
if (protoLen == 1) {
|
||||||
|
Msg.showError(this, null, "Length Override Error",
|
||||||
|
"Length override for 1-byte instruction not allowed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String restoreTip = ", 0=restore";
|
||||||
|
|
||||||
|
String alignTip = "";
|
||||||
|
int align = program.getLanguage().getInstructionAlignment();
|
||||||
|
if (align != 1) {
|
||||||
|
alignTip = ", must be multiple of " + align;
|
||||||
|
}
|
||||||
|
|
||||||
|
int minLength = 0;
|
||||||
|
long maxLength = Math.min(Instruction.MAX_LENGTH_OVERRIDE, protoLen - 1);
|
||||||
|
Instruction nextInstr = listing.getInstructionAfter(address);
|
||||||
|
if (nextInstr != null &&
|
||||||
|
nextInstr.getAddress().getAddressSpace().equals(address.getAddressSpace())) {
|
||||||
|
long limit = nextInstr.getAddress().subtract(address);
|
||||||
|
maxLength = Math.min(limit, maxLength);
|
||||||
|
if (limit < instr.getParsedLength()) {
|
||||||
|
minLength = 1; // unable to restore to default length using 0 value
|
||||||
|
restoreTip = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (maxLength == 1) {
|
||||||
|
// Assume we have an instruction whose length can't be changed
|
||||||
|
Msg.showError(this, null, "Length Override Error",
|
||||||
|
"Insufficient space to alter current length override of 1-byte");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int currentLengthOverride = 0;
|
||||||
|
if (instr.isLengthOverridden()) {
|
||||||
|
currentLengthOverride = instr.getLength();
|
||||||
|
}
|
||||||
|
|
||||||
|
NumberInputDialog dialog = new NumberInputDialog("Override/Restore Instruction Length",
|
||||||
|
"Enter byte-length [" + minLength + ".." + maxLength + restoreTip + alignTip + "]",
|
||||||
|
currentLengthOverride, minLength, (int) maxLength, false);
|
||||||
|
tool.showDialog(dialog);
|
||||||
|
|
||||||
|
if (dialog.wasCancelled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String kind = "Set";
|
||||||
|
int lengthOverride = dialog.getIntValue();
|
||||||
|
if (lengthOverride == 0) {
|
||||||
|
if (!instr.isLengthOverridden()) {
|
||||||
|
return; // no change
|
||||||
|
}
|
||||||
|
kind = "Clear";
|
||||||
|
}
|
||||||
|
|
||||||
|
try (Transaction tx = instr.getProgram().openTransaction(kind + " Length Override")) {
|
||||||
|
instr.setLengthOverride(lengthOverride);
|
||||||
|
}
|
||||||
|
catch (CodeUnitInsertionException e) {
|
||||||
|
Msg.showError(this, null, "Length Override Error", e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEnabledForContext(ListingActionContext context) {
|
||||||
|
Address address = context.getAddress();
|
||||||
|
if (address == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Program program = context.getProgram();
|
||||||
|
Instruction instr = program.getListing().getInstructionAt(address);
|
||||||
|
if (instr == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
int alignment = program.getLanguage().getInstructionAlignment();
|
||||||
|
return instr.getParsedLength() > alignment;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+6
-6
@@ -91,8 +91,8 @@ public class FallThroughPlugin extends Plugin {
|
|||||||
return instruction != null && !instruction.isFallThroughOverridden();
|
return instruction != null && !instruction.isFallThroughOverridden();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
autoFallthroughAction.setPopupMenuData(new MenuData(new String[] { "Fallthrough",
|
autoFallthroughAction.setPopupMenuData(
|
||||||
"Auto Override" }, null, "Fallthrough"));
|
new MenuData(new String[] { "Fallthrough", "Auto Override" }, null, "Fallthrough"));
|
||||||
|
|
||||||
tool.addAction(autoFallthroughAction);
|
tool.addAction(autoFallthroughAction);
|
||||||
|
|
||||||
@@ -111,8 +111,8 @@ public class FallThroughPlugin extends Plugin {
|
|||||||
return instruction != null && instruction.isFallThroughOverridden();
|
return instruction != null && instruction.isFallThroughOverridden();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
clearFallthroughAction.setPopupMenuData(new MenuData(new String[] { "Fallthrough",
|
clearFallthroughAction.setPopupMenuData(
|
||||||
"Clear Overrides" }, null, "Fallthrough"));
|
new MenuData(new String[] { "Fallthrough", "Clear Overrides" }, null, "Fallthrough"));
|
||||||
|
|
||||||
tool.addAction(clearFallthroughAction);
|
tool.addAction(clearFallthroughAction);
|
||||||
|
|
||||||
@@ -128,8 +128,8 @@ public class FallThroughPlugin extends Plugin {
|
|||||||
return instruction != null;
|
return instruction != null;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
setFallthroughAction.setPopupMenuData(new MenuData(
|
setFallthroughAction.setPopupMenuData(
|
||||||
new String[] { "Fallthrough", "Set..." }, null, "Fallthrough"));
|
new MenuData(new String[] { "Fallthrough", "Set..." }, null, "Fallthrough"));
|
||||||
|
|
||||||
tool.addAction(setFallthroughAction);
|
tool.addAction(setFallthroughAction);
|
||||||
}
|
}
|
||||||
|
|||||||
+9
-5
@@ -326,8 +326,9 @@ public class InsertBytesWidget extends ReusableDialogComponentProvider implement
|
|||||||
private List<OperandMetadata> createOperandMetadata(PseudoInstruction instruction)
|
private List<OperandMetadata> createOperandMetadata(PseudoInstruction instruction)
|
||||||
throws MemoryAccessException {
|
throws MemoryAccessException {
|
||||||
|
|
||||||
List<OperandMetadata> operands = new ArrayList<>();
|
byte[] bytes = instruction.getParsedBytes();
|
||||||
|
|
||||||
|
List<OperandMetadata> operands = new ArrayList<>();
|
||||||
for (int i = 0; i < instruction.getNumOperands(); i++) {
|
for (int i = 0; i < instruction.getNumOperands(); i++) {
|
||||||
OperandMetadata operandMD = new OperandMetadata();
|
OperandMetadata operandMD = new OperandMetadata();
|
||||||
operandMD.setOpType(instruction.getOperandType(i));
|
operandMD.setOpType(instruction.getOperandType(i));
|
||||||
@@ -337,8 +338,9 @@ public class InsertBytesWidget extends ReusableDialogComponentProvider implement
|
|||||||
// prototype object in the pseudo instruction. For the value string we have to do
|
// prototype object in the pseudo instruction. For the value string we have to do
|
||||||
// a bit of calculating: we know the entire instruction byte string and we know
|
// a bit of calculating: we know the entire instruction byte string and we know
|
||||||
// this operand mask, so AND them together and we get the operand bytes.
|
// this operand mask, so AND them together and we get the operand bytes.
|
||||||
byte[] mask = instruction.getPrototype().getOperandValueMask(i).getBytes();
|
InstructionPrototype prototype = instruction.getPrototype();
|
||||||
byte[] value = InstructionSearchUtils.byteArrayAnd(mask, instruction.getBytes());
|
byte[] mask = prototype.getOperandValueMask(i).getBytes();
|
||||||
|
byte[] value = InstructionSearchUtils.byteArrayAnd(mask, bytes);
|
||||||
MaskContainer maskContainer = new MaskContainer(mask, value);
|
MaskContainer maskContainer = new MaskContainer(mask, value);
|
||||||
|
|
||||||
operandMD.setMaskContainer(maskContainer);
|
operandMD.setMaskContainer(maskContainer);
|
||||||
@@ -362,8 +364,10 @@ public class InsertBytesWidget extends ReusableDialogComponentProvider implement
|
|||||||
// The mask array we can get directly from the prototype. For the value array we
|
// The mask array we can get directly from the prototype. For the value array we
|
||||||
// have to figure out which bits pertain to operands and just zero them out, so we're
|
// have to figure out which bits pertain to operands and just zero them out, so we're
|
||||||
// just left with the instruction (mnemonic) bits.
|
// just left with the instruction (mnemonic) bits.
|
||||||
byte[] mask = instruction.getPrototype().getInstructionMask().getBytes();
|
InstructionPrototype prototype = instruction.getPrototype();
|
||||||
byte[] value = clearOperandBits(mask, instruction.getBytes());
|
byte[] mask = prototype.getInstructionMask().getBytes();
|
||||||
|
byte[] value = instruction.getParsedBytes();
|
||||||
|
value = clearOperandBits(mask, value);
|
||||||
MaskContainer mnemonicMask = new MaskContainer(mask, value);
|
MaskContainer mnemonicMask = new MaskContainer(mask, value);
|
||||||
|
|
||||||
InstructionMetadata instructionMD = new InstructionMetadata(mnemonicMask);
|
InstructionMetadata instructionMD = new InstructionMetadata(mnemonicMask);
|
||||||
|
|||||||
+21
-4
@@ -1,6 +1,5 @@
|
|||||||
/* ###
|
/* ###
|
||||||
* IP: GHIDRA
|
* IP: GHIDRA
|
||||||
* REVIEWED: YES
|
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -18,8 +17,7 @@ package ghidra.app.plugin.core.reloc;
|
|||||||
|
|
||||||
import ghidra.program.model.address.Address;
|
import ghidra.program.model.address.Address;
|
||||||
import ghidra.program.model.lang.*;
|
import ghidra.program.model.lang.*;
|
||||||
import ghidra.program.model.listing.Instruction;
|
import ghidra.program.model.listing.*;
|
||||||
import ghidra.program.model.listing.Program;
|
|
||||||
import ghidra.program.model.mem.DumbMemBufferImpl;
|
import ghidra.program.model.mem.DumbMemBufferImpl;
|
||||||
import ghidra.program.model.mem.MemBuffer;
|
import ghidra.program.model.mem.MemBuffer;
|
||||||
import ghidra.program.model.symbol.Reference;
|
import ghidra.program.model.symbol.Reference;
|
||||||
@@ -31,6 +29,10 @@ public class InstructionStasher {
|
|||||||
private Address address;
|
private Address address;
|
||||||
private InstructionPrototype prototype;
|
private InstructionPrototype prototype;
|
||||||
private Reference[] referencesFrom;
|
private Reference[] referencesFrom;
|
||||||
|
private FlowOverride flowOverride;
|
||||||
|
private Address fallthroughOverride;
|
||||||
|
private int lengthOverride;
|
||||||
|
|
||||||
private Address minAddress;
|
private Address minAddress;
|
||||||
|
|
||||||
public InstructionStasher(Program program, Address address) {
|
public InstructionStasher(Program program, Address address) {
|
||||||
@@ -47,6 +49,12 @@ public class InstructionStasher {
|
|||||||
minAddress = instruction.getMinAddress();
|
minAddress = instruction.getMinAddress();
|
||||||
prototype = instruction.getPrototype();
|
prototype = instruction.getPrototype();
|
||||||
referencesFrom = instruction.getReferencesFrom();
|
referencesFrom = instruction.getReferencesFrom();
|
||||||
|
flowOverride = instruction.getFlowOverride();
|
||||||
|
fallthroughOverride =
|
||||||
|
instruction.isFallThroughOverridden() ? instruction.getFallThrough() : null;
|
||||||
|
// Relocation data change may mutate instruction. Do not force length of instruction
|
||||||
|
// unless it was previously overriden. A value of 0 allows length to match prototoype.
|
||||||
|
lengthOverride = instruction.isLengthOverridden() ? instruction.getLength() : 0;
|
||||||
program.getListing().clearCodeUnits(minAddress, instruction.getMaxAddress(), false);
|
program.getListing().clearCodeUnits(minAddress, instruction.getMaxAddress(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,7 +65,16 @@ public class InstructionStasher {
|
|||||||
MemBuffer buf = new DumbMemBufferImpl(program.getMemory(), minAddress);
|
MemBuffer buf = new DumbMemBufferImpl(program.getMemory(), minAddress);
|
||||||
ProcessorContext context =
|
ProcessorContext context =
|
||||||
new ProgramProcessorContext(program.getProgramContext(), minAddress);
|
new ProgramProcessorContext(program.getProgramContext(), minAddress);
|
||||||
program.getListing().createInstruction(minAddress, prototype, buf, context);
|
Instruction instr = program.getListing()
|
||||||
|
.createInstruction(minAddress, prototype, buf, context, lengthOverride);
|
||||||
|
|
||||||
|
if (flowOverride != FlowOverride.NONE) {
|
||||||
|
instr.setFlowOverride(flowOverride);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fallthroughOverride != null) {
|
||||||
|
instr.setFallThrough(fallthroughOverride);
|
||||||
|
}
|
||||||
|
|
||||||
for (Reference reference : referencesFrom) {
|
for (Reference reference : referencesFrom) {
|
||||||
if (reference.getSource() != SourceType.DEFAULT) {
|
if (reference.getSource() != SourceType.DEFAULT) {
|
||||||
|
|||||||
@@ -568,7 +568,13 @@ class ProgramTextWriter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
byte[] bytes = cu.getBytes();
|
byte[] bytes;
|
||||||
|
if (cu instanceof Instruction instr) {
|
||||||
|
bytes = instr.getParsedBytes();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
bytes = cu.getBytes();
|
||||||
|
}
|
||||||
StringBuffer bytesbuf = new StringBuffer();
|
StringBuffer bytesbuf = new StringBuffer();
|
||||||
for (int i = 0; i < bytes.length; ++i) {
|
for (int i = 0; i < bytes.length; ++i) {
|
||||||
if (i > 0) {
|
if (i > 0) {
|
||||||
@@ -630,10 +636,7 @@ class ProgramTextWriter {
|
|||||||
|
|
||||||
if (options.isHTML()) {
|
if (options.isHTML()) {
|
||||||
Reference ref =
|
Reference ref =
|
||||||
cu.getProgram()
|
cu.getProgram().getReferenceManager().getPrimaryReferenceFrom(cuAddress, i);
|
||||||
.getReferenceManager()
|
|
||||||
.getPrimaryReferenceFrom(cuAddress,
|
|
||||||
i);
|
|
||||||
addReferenceLinkedText(ref, opReps[i], true);
|
addReferenceLinkedText(ref, opReps[i], true);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|||||||
+12
-1
@@ -174,7 +174,18 @@ public class BytesFieldFactory extends FieldFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
CodeUnit cu = (CodeUnit) obj;
|
CodeUnit cu = (CodeUnit) obj;
|
||||||
int length = Math.min(cu.getLength(), 100);
|
int length;
|
||||||
|
|
||||||
|
// Instructions: use prototype length so we display all bytes for length-override case
|
||||||
|
if (cu instanceof Instruction) {
|
||||||
|
// Consider all parsed bytes even if the overlap the next instruction due to the
|
||||||
|
// use of an instruction length-override.
|
||||||
|
length = ((Instruction) cu).getParsedLength();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
length = Math.min(cu.getLength(), 100);
|
||||||
|
}
|
||||||
|
|
||||||
byte[] bytes = new byte[length];
|
byte[] bytes = new byte[length];
|
||||||
Memory memory = cu.getProgram().getMemory();
|
Memory memory = cu.getProgram().getMemory();
|
||||||
try {
|
try {
|
||||||
|
|||||||
+2
-1
@@ -120,7 +120,8 @@ public class MnemonicFieldFactory extends FieldFactory {
|
|||||||
}
|
}
|
||||||
else if (cu instanceof Instruction) {
|
else if (cu instanceof Instruction) {
|
||||||
Instruction instr = (Instruction) cu;
|
Instruction instr = (Instruction) cu;
|
||||||
if (instr.getFlowOverride() != FlowOverride.NONE || instr.isFallThroughOverridden()) {
|
if (instr.getFlowOverride() != FlowOverride.NONE || instr.isFallThroughOverridden() ||
|
||||||
|
instr.isLengthOverridden()) {
|
||||||
c = MnemonicColors.OVERRIDE;
|
c = MnemonicColors.OVERRIDE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+11
-3
@@ -177,12 +177,20 @@ public class PostCommentFieldFactory extends FieldFactory {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (instr.isFallThroughOverridden()) {
|
if (instr.isLengthOverridden() || instr.isFallThroughOverridden()) {
|
||||||
Address fallThrough = instr.getFallThrough();
|
Address fallThrough = instr.getFallThrough();
|
||||||
String fallthroughComment = "-- Fallthrough Override: " +
|
String fallthroughComment =
|
||||||
(fallThrough != null ? fallThrough.toString() : "NO-FALLTHROUGH");
|
"-- Fallthrough" + (instr.isFallThroughOverridden() ? " Override" : "") + ": " +
|
||||||
|
(fallThrough != null ? fallThrough.toString() : "NO-FALLTHROUGH");
|
||||||
comments.addFirst(fallthroughComment);
|
comments.addFirst(fallthroughComment);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (instr.isLengthOverridden()) {
|
||||||
|
String lengthOverrideComment = "-- Length Override: " + instr.getLength() +
|
||||||
|
" (actual length is " + instr.getParsedLength() + ")";
|
||||||
|
comments.addFirst(lengthOverrideComment);
|
||||||
|
}
|
||||||
|
|
||||||
FlowOverride flowOverride = instr.getFlowOverride();
|
FlowOverride flowOverride = instr.getFlowOverride();
|
||||||
if (flowOverride != FlowOverride.NONE) {
|
if (flowOverride != FlowOverride.NONE) {
|
||||||
String flowOverrideComment =
|
String flowOverrideComment =
|
||||||
|
|||||||
@@ -194,10 +194,10 @@ public class DiffUtility extends SimpleDiffUtility {
|
|||||||
}
|
}
|
||||||
else if (namespace instanceof GhidraClass) {
|
else if (namespace instanceof GhidraClass) {
|
||||||
return otherProgram.getSymbolTable()
|
return otherProgram.getSymbolTable()
|
||||||
.createClass(otherParentNamespace, namespace.getName(), source);
|
.createClass(otherParentNamespace, namespace.getName(), source);
|
||||||
}
|
}
|
||||||
return otherProgram.getSymbolTable()
|
return otherProgram.getSymbolTable()
|
||||||
.createNameSpace(otherParentNamespace, namespace.getName(), source);
|
.createNameSpace(otherParentNamespace, namespace.getName(), source);
|
||||||
}
|
}
|
||||||
|
|
||||||
// /**
|
// /**
|
||||||
@@ -330,10 +330,10 @@ public class DiffUtility extends SimpleDiffUtility {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return otherProgram.getReferenceManager()
|
return otherProgram.getReferenceManager()
|
||||||
.getReference(fromAddr, toAddr, ref.getOperandIndex());
|
.getReference(fromAddr, toAddr, ref.getOperandIndex());
|
||||||
}
|
}
|
||||||
Reference otherRef = otherProgram.getReferenceManager()
|
Reference otherRef = otherProgram.getReferenceManager()
|
||||||
.getPrimaryReferenceFrom(fromAddr, ref.getOperandIndex());
|
.getPrimaryReferenceFrom(fromAddr, ref.getOperandIndex());
|
||||||
if (otherRef != null && ref.getToAddress().hasSameAddressSpace(otherRef.getToAddress())) {
|
if (otherRef != null && ref.getToAddress().hasSameAddressSpace(otherRef.getToAddress())) {
|
||||||
return otherRef;
|
return otherRef;
|
||||||
}
|
}
|
||||||
@@ -358,10 +358,10 @@ public class DiffUtility extends SimpleDiffUtility {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return program.getReferenceManager()
|
return program.getReferenceManager()
|
||||||
.getReference(fromAddr1, toAddr1, p2Ref.getOperandIndex());
|
.getReference(fromAddr1, toAddr1, p2Ref.getOperandIndex());
|
||||||
}
|
}
|
||||||
Reference p1Ref = program.getReferenceManager()
|
Reference p1Ref = program.getReferenceManager()
|
||||||
.getPrimaryReferenceFrom(fromAddr1, p2Ref.getOperandIndex());
|
.getPrimaryReferenceFrom(fromAddr1, p2Ref.getOperandIndex());
|
||||||
if (p1Ref != null && p1Ref.getToAddress().hasSameAddressSpace(p2Ref.getToAddress())) {
|
if (p1Ref != null && p1Ref.getToAddress().hasSameAddressSpace(p2Ref.getToAddress())) {
|
||||||
return p1Ref;
|
return p1Ref;
|
||||||
}
|
}
|
||||||
@@ -386,8 +386,8 @@ public class DiffUtility extends SimpleDiffUtility {
|
|||||||
}
|
}
|
||||||
// FIXME Should this be passing the Namespace?
|
// FIXME Should this be passing the Namespace?
|
||||||
return otherProgram.getExternalManager()
|
return otherProgram.getExternalManager()
|
||||||
.addExtLocation(extLoc.getLibraryName(), extLoc.getLabel(), otherAddr,
|
.addExtLocation(extLoc.getLibraryName(), extLoc.getLabel(), otherAddr,
|
||||||
extLoc.getSource());
|
extLoc.getSource());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -571,15 +571,18 @@ public class DiffUtility extends SimpleDiffUtility {
|
|||||||
Address rangeMin = range.getMinAddress();
|
Address rangeMin = range.getMinAddress();
|
||||||
Address rangeMax = range.getMaxAddress();
|
Address rangeMax = range.getMaxAddress();
|
||||||
CodeUnit minCu = listing.getCodeUnitContaining(rangeMin);
|
CodeUnit minCu = listing.getCodeUnitContaining(rangeMin);
|
||||||
|
// NOTE: minCu does not consider prior code unit which may share bytes due to
|
||||||
|
// possible instruction length-override. Max address of code unit will factor-in
|
||||||
|
// full length of instruction prototype.
|
||||||
if (minCu != null) {
|
if (minCu != null) {
|
||||||
Address minCuMinAddr = minCu.getMinAddress();
|
Address minCuMinAddr = minCu.getMinAddress();
|
||||||
if (minCuMinAddr.compareTo(rangeMin) != 0) {
|
if (minCuMinAddr.compareTo(rangeMin) != 0) {
|
||||||
addrs.addRange(minCuMinAddr, minCu.getMaxAddress());
|
addrs.addRange(minCuMinAddr, getMaxAddress(minCu));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
CodeUnit maxCu = listing.getCodeUnitContaining(rangeMax);
|
CodeUnit maxCu = listing.getCodeUnitContaining(rangeMax);
|
||||||
if (maxCu != null) {
|
if (maxCu != null) {
|
||||||
Address maxCuMaxAddr = maxCu.getMaxAddress();
|
Address maxCuMaxAddr = getMaxAddress(maxCu);
|
||||||
if (maxCuMaxAddr.compareTo(rangeMax) != 0) {
|
if (maxCuMaxAddr.compareTo(rangeMax) != 0) {
|
||||||
addrs.addRange(maxCu.getMinAddress(), maxCuMaxAddr);
|
addrs.addRange(maxCu.getMinAddress(), maxCuMaxAddr);
|
||||||
}
|
}
|
||||||
@@ -588,6 +591,20 @@ public class DiffUtility extends SimpleDiffUtility {
|
|||||||
return addrs;
|
return addrs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the code unit max address. Instruction max address will be based upon prototype
|
||||||
|
* length which may be longer than code unit length if length-override is in effect.
|
||||||
|
* @param cu code unit
|
||||||
|
* @return effective code unit length
|
||||||
|
*/
|
||||||
|
private static Address getMaxAddress(CodeUnit cu) {
|
||||||
|
if (cu instanceof Instruction inst && inst.isLengthOverridden()) {
|
||||||
|
// factor in prototype length when length-override applies
|
||||||
|
return cu.getMinAddress().add(inst.getParsedLength() - 1);
|
||||||
|
}
|
||||||
|
return cu.getMaxAddress();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the signed hex string representing the int value.
|
* Returns the signed hex string representing the int value.
|
||||||
* Positive values are represented beginning with 0x. (i.e. value of 12 would be 0xc)
|
* Positive values are represented beginning with 0x. (i.e. value of 12 would be 0xc)
|
||||||
|
|||||||
@@ -2871,6 +2871,7 @@ public class ProgramDiff {
|
|||||||
if (i1 == i2) {
|
if (i1 == i2) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
// Checking length and prototype will handle use of length-override
|
||||||
if (i1.getLength() != i2.getLength()) {
|
if (i1.getLength() != i2.getLength()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -2888,7 +2889,9 @@ public class ProgramDiff {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (!Arrays.equals(i1.getBytes(), i2.getBytes())) {
|
byte[] bytes1 = i1.getParsedBytes();
|
||||||
|
byte[] bytes2 = i2.getParsedBytes();
|
||||||
|
if (!Arrays.equals(bytes1, bytes2)) {
|
||||||
return false; // bytes differ
|
return false; // bytes differ
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -906,8 +906,7 @@ public class ProgramDiffDetails {
|
|||||||
ordinal + " " + fieldName + " " + actualDt.getMnemonic(actualDt.getDefaultSettings()) +
|
ordinal + " " + fieldName + " " + actualDt.getMnemonic(actualDt.getDefaultSettings()) +
|
||||||
" " + getCategoryName(actualDt) + " " + "DataTypeSize=" +
|
" " + getCategoryName(actualDt) + " " + "DataTypeSize=" +
|
||||||
(actualDt.isZeroLength() ? 0 : actualDt.getLength()) + " " + "ComponentSize=" +
|
(actualDt.isZeroLength() ? 0 : actualDt.getLength()) + " " + "ComponentSize=" +
|
||||||
dtc.getLength() + " " + ((comment != null) ? comment : "") +
|
dtc.getLength() + " " + ((comment != null) ? comment : "") + " " + newLine);
|
||||||
" " + newLine);
|
|
||||||
return actualDt;
|
return actualDt;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -958,8 +957,7 @@ public class ProgramDiffDetails {
|
|||||||
fieldName = "field" + offset;
|
fieldName = "field" + offset;
|
||||||
}
|
}
|
||||||
buf.append(newIndent + min.add(offset) + " " + dtc.getFieldName() + " " +
|
buf.append(newIndent + min.add(offset) + " " + dtc.getFieldName() + " " +
|
||||||
dtc.getDataType().getName() + " " + "length=" +
|
dtc.getDataType().getName() + " " + "length=" + dtc.getLength() + " " +
|
||||||
dtc.getLength() + " " +
|
|
||||||
((comment != null) ? comment : "") + " " + newLine);
|
((comment != null) ? comment : "") + " " + newLine);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -987,6 +985,7 @@ public class ProgramDiffDetails {
|
|||||||
boolean removedFallThrough =
|
boolean removedFallThrough =
|
||||||
inst.isFallThroughOverridden() && (inst.getFallThrough() == null);
|
inst.isFallThroughOverridden() && (inst.getFallThrough() == null);
|
||||||
boolean hasFlowOverride = inst.getFlowOverride() != FlowOverride.NONE;
|
boolean hasFlowOverride = inst.getFlowOverride() != FlowOverride.NONE;
|
||||||
|
boolean hasLengthOverride = inst.isLengthOverridden();
|
||||||
cuRep = cu.toString();
|
cuRep = cu.toString();
|
||||||
if (removedFallThrough) {
|
if (removedFallThrough) {
|
||||||
cuRep += newLine + indent + getSpaces(addrRangeStr.length()) + " " +
|
cuRep += newLine + indent + getSpaces(addrRangeStr.length()) + " " +
|
||||||
@@ -1011,6 +1010,11 @@ public class ProgramDiffDetails {
|
|||||||
cuRep += newLine + indent + getSpaces(addrRangeStr.length()) + " " +
|
cuRep += newLine + indent + getSpaces(addrRangeStr.length()) + " " +
|
||||||
"Flow Override: " + inst.getFlowOverride();
|
"Flow Override: " + inst.getFlowOverride();
|
||||||
}
|
}
|
||||||
|
if (hasLengthOverride) {
|
||||||
|
cuRep += newLine + indent + getSpaces(addrRangeStr.length()) + " " +
|
||||||
|
"Length Override: " + inst.getLength() + " (actual length is " +
|
||||||
|
inst.getParsedLength() + ")";
|
||||||
|
}
|
||||||
cuRep += newLine + indent + getSpaces(addrRangeStr.length()) + " " +
|
cuRep += newLine + indent + getSpaces(addrRangeStr.length()) + " " +
|
||||||
"Instruction Prototype hash = " +
|
"Instruction Prototype hash = " +
|
||||||
Integer.toHexString(inst.getPrototype().hashCode());
|
Integer.toHexString(inst.getPrototype().hashCode());
|
||||||
@@ -1463,8 +1467,8 @@ public class ProgramDiffDetails {
|
|||||||
private boolean addSpecificCommentDetails(int commentType, String commentName) {
|
private boolean addSpecificCommentDetails(int commentType, String commentName) {
|
||||||
boolean hasCommentDiff = false;
|
boolean hasCommentDiff = false;
|
||||||
try {
|
try {
|
||||||
for (Address p1Address = minP1Address; p1Address.compareTo(
|
for (Address p1Address = minP1Address; p1Address
|
||||||
maxP1Address) <= 0; p1Address = p1Address.add(1L)) {
|
.compareTo(maxP1Address) <= 0; p1Address = p1Address.add(1L)) {
|
||||||
Address p2Address = SimpleDiffUtility.getCompatibleAddress(p1, p1Address, p2);
|
Address p2Address = SimpleDiffUtility.getCompatibleAddress(p1, p1Address, p2);
|
||||||
String noComment = "No " + commentName + ".";
|
String noComment = "No " + commentName + ".";
|
||||||
String cmt1 = l1.getComment(commentType, p1Address);
|
String cmt1 = l1.getComment(commentType, p1Address);
|
||||||
@@ -2169,8 +2173,7 @@ public class ProgramDiffDetails {
|
|||||||
// Handle case where the class for a Saveable property is missing (unsupported).
|
// Handle case where the class for a Saveable property is missing (unsupported).
|
||||||
if (cu.getProgram()
|
if (cu.getProgram()
|
||||||
.getListing()
|
.getListing()
|
||||||
.getPropertyMap(
|
.getPropertyMap(propertyName) instanceof UnsupportedMapDB) {
|
||||||
propertyName) instanceof UnsupportedMapDB) {
|
|
||||||
buf.append(
|
buf.append(
|
||||||
indent2 + propertyName + " is an unsupported property." + newLine);
|
indent2 + propertyName + " is an unsupported property." + newLine);
|
||||||
continue;
|
continue;
|
||||||
@@ -2241,8 +2244,8 @@ public class ProgramDiffDetails {
|
|||||||
BookmarkManager bmm1 = p1.getBookmarkManager();
|
BookmarkManager bmm1 = p1.getBookmarkManager();
|
||||||
BookmarkManager bmm2 = p2.getBookmarkManager();
|
BookmarkManager bmm2 = p2.getBookmarkManager();
|
||||||
try {
|
try {
|
||||||
for (Address p1Address = minP1Address; p1Address.compareTo(
|
for (Address p1Address = minP1Address; p1Address
|
||||||
maxP1Address) <= 0; p1Address = p1Address.add(1)) {
|
.compareTo(maxP1Address) <= 0; p1Address = p1Address.add(1)) {
|
||||||
Address p2Address = SimpleDiffUtility.getCompatibleAddress(p1, p1Address, p2);
|
Address p2Address = SimpleDiffUtility.getCompatibleAddress(p1, p1Address, p2);
|
||||||
Bookmark[] marks1 = bmm1.getBookmarks(p1Address);
|
Bookmark[] marks1 = bmm1.getBookmarks(p1Address);
|
||||||
Arrays.sort(marks1, BOOKMARK_COMPARATOR);
|
Arrays.sort(marks1, BOOKMARK_COMPARATOR);
|
||||||
@@ -2341,7 +2344,7 @@ public class ProgramDiffDetails {
|
|||||||
|
|
||||||
private boolean isSameInstruction(Instruction i1, Instruction i2) {
|
private boolean isSameInstruction(Instruction i1, Instruction i2) {
|
||||||
boolean samePrototypes = i1.getPrototype().equals(i2.getPrototype());
|
boolean samePrototypes = i1.getPrototype().equals(i2.getPrototype());
|
||||||
boolean sameInstructionLength = i1.getLength() == i2.getLength();
|
boolean sameInstructionLength = i1.getLength() == i2.getLength(); // factors length override
|
||||||
boolean sameFallthrough = ProgramDiff.isSameFallthrough(p1, i1, p2, i2);
|
boolean sameFallthrough = ProgramDiff.isSameFallthrough(p1, i1, p2, i2);
|
||||||
boolean sameFlowOverride = i1.getFlowOverride() == i2.getFlowOverride();
|
boolean sameFlowOverride = i1.getFlowOverride() == i2.getFlowOverride();
|
||||||
return samePrototypes && sameInstructionLength && sameFallthrough && sameFlowOverride;
|
return samePrototypes && sameInstructionLength && sameFallthrough && sameFlowOverride;
|
||||||
|
|||||||
@@ -29,7 +29,6 @@ import ghidra.util.datastruct.Accumulator;
|
|||||||
import ghidra.util.datastruct.ListAccumulator;
|
import ghidra.util.datastruct.ListAccumulator;
|
||||||
import ghidra.util.exception.CancelledException;
|
import ghidra.util.exception.CancelledException;
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.TaskMonitor;
|
||||||
import ghidra.util.task.TaskMonitorAdapter;
|
|
||||||
import utility.function.TerminatingConsumer;
|
import utility.function.TerminatingConsumer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -151,14 +150,26 @@ public class ProgramMemoryUtil {
|
|||||||
*/
|
*/
|
||||||
private static void copyByteRange(Memory toMem, Memory fromMem, AddressRange range)
|
private static void copyByteRange(Memory toMem, Memory fromMem, AddressRange range)
|
||||||
throws MemoryAccessException {
|
throws MemoryAccessException {
|
||||||
|
|
||||||
|
// TODO: Addresses from one program cannot be used with another program (e.g., overlays)
|
||||||
|
// TODO: Should be using AddressTranslator
|
||||||
|
// TODO: Method relies too heavily on caller limiting range to valid initialized memory in both programs
|
||||||
|
|
||||||
// Copy the bytes for this range
|
// Copy the bytes for this range
|
||||||
int length = 0;
|
int length = 0;
|
||||||
Address writeAddress = range.getMinAddress();
|
Address writeAddress = range.getMinAddress();
|
||||||
for (long len = range.getLength(); len > 0; len -= length) {
|
for (long len = range.getLength(); len > 0; len -= length) {
|
||||||
length = (int) Math.min(len, Integer.MAX_VALUE);
|
length = (int) Math.min(len, Integer.MAX_VALUE);
|
||||||
byte[] bytes = new byte[length];
|
byte[] srcBytes = new byte[length];
|
||||||
fromMem.getBytes(writeAddress, bytes);
|
|
||||||
toMem.setBytes(writeAddress, bytes);
|
// NOTE: problems will arise if srcLen != length
|
||||||
|
int srcLen = fromMem.getBytes(writeAddress, srcBytes);
|
||||||
|
byte[] destBytes = new byte[srcLen];
|
||||||
|
if (toMem.getBytes(writeAddress, destBytes) != srcLen ||
|
||||||
|
!Arrays.equals(destBytes, 0, srcLen, srcBytes, 0, srcLen)) {
|
||||||
|
// only copy bytes if they differ
|
||||||
|
toMem.setBytes(writeAddress, srcBytes, 0, srcLen);
|
||||||
|
}
|
||||||
if (len > length) {
|
if (len > length) {
|
||||||
writeAddress = writeAddress.add(length);
|
writeAddress = writeAddress.add(length);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -628,7 +628,9 @@ public class ProgramMerge {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
if (!Arrays.equals(instruction.getBytes(), resultInstruction.getBytes())) {
|
byte[] bytes = instruction.getParsedBytes();
|
||||||
|
byte[] resultBytes = resultInstruction.getParsedBytes();
|
||||||
|
if (!Arrays.equals(bytes, resultBytes)) {
|
||||||
return true; // bytes differ
|
return true; // bytes differ
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -682,8 +684,8 @@ public class ProgramMerge {
|
|||||||
bytesLength = (int) originMax.subtract(originMin);
|
bytesLength = (int) originMax.subtract(originMin);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
originMax = originInstruction.getMaxAddress();
|
bytesLength = originInstruction.getParsedLength();
|
||||||
bytesLength = originInstruction.getLength();
|
originMax = originMin.add(bytesLength - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
Address resultMax = originToResultTranslator.getAddress(originMax);
|
Address resultMax = originToResultTranslator.getAddress(originMax);
|
||||||
@@ -699,7 +701,7 @@ public class ProgramMerge {
|
|||||||
|
|
||||||
// If there are byte differences for this instruction then the
|
// If there are byte differences for this instruction then the
|
||||||
// bytes need to get copied even though the user did not indicate to.
|
// bytes need to get copied even though the user did not indicate to.
|
||||||
if (bytesAreDifferent(originByteDiffs, originMin, resultMin, bytesLength)) { // FIXME
|
if (bytesMayDiffer(originByteDiffs, originMin, resultMin, bytesLength)) {
|
||||||
// Copy all the bytes for the instruction if any bytes differ.
|
// Copy all the bytes for the instruction if any bytes differ.
|
||||||
ProgramMemoryUtil.copyBytesInRanges(resultProgram, originProgram, resultMin, resultMax);
|
ProgramMemoryUtil.copyBytesInRanges(resultProgram, originProgram, resultMin, resultMax);
|
||||||
}
|
}
|
||||||
@@ -709,7 +711,8 @@ public class ProgramMerge {
|
|||||||
newInst = disassembleDelaySlottedInstruction(resultProgram, resultMin);
|
newInst = disassembleDelaySlottedInstruction(resultProgram, resultMin);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
newInst = disassembleNonDelaySlotInstruction(resultProgram, resultMin);
|
newInst = disassembleNonDelaySlotInstruction(resultProgram, resultMin,
|
||||||
|
originInstruction.isLengthOverridden() ? originInstruction.getLength() : 0);
|
||||||
}
|
}
|
||||||
if (newInst == null) {
|
if (newInst == null) {
|
||||||
return;
|
return;
|
||||||
@@ -740,24 +743,29 @@ public class ProgramMerge {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Instruction disassembleDelaySlottedInstruction(Program program, Address addr) {
|
private Instruction disassembleDelaySlottedInstruction(Program program, Address addr) {
|
||||||
|
// WARNING: does not support instruction length override use
|
||||||
// Use heavyweight disassembler for delay slotted instruction
|
// Use heavyweight disassembler for delay slotted instruction
|
||||||
AddressSet restrictedSet = new AddressSet(addr);
|
AddressSet restrictedSet = new AddressSet(addr);
|
||||||
Disassembler disassembler =
|
Disassembler disassembler = Disassembler.getDisassembler(program, TaskMonitor.DUMMY, null);
|
||||||
Disassembler.getDisassembler(program, TaskMonitor.DUMMY, null);
|
|
||||||
disassembler.disassemble(addr, restrictedSet, false);
|
disassembler.disassemble(addr, restrictedSet, false);
|
||||||
return program.getListing().getInstructionAt(addr);
|
return program.getListing().getInstructionAt(addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Instruction disassembleNonDelaySlotInstruction(Program program, Address addr) {
|
private Instruction disassembleNonDelaySlotInstruction(Program program, Address addr,
|
||||||
|
int lengthOverride) {
|
||||||
// Use lightweight disassembler for simple case
|
// Use lightweight disassembler for simple case
|
||||||
DisassemblerContextImpl context = new DisassemblerContextImpl(program.getProgramContext());
|
DisassemblerContextImpl context = new DisassemblerContextImpl(program.getProgramContext());
|
||||||
context.flowStart(addr);
|
context.flowStart(addr);
|
||||||
try {
|
try {
|
||||||
InstructionPrototype proto = program.getLanguage()
|
InstructionPrototype proto = program.getLanguage()
|
||||||
.parse(new DumbMemBufferImpl(program.getMemory(), addr), context, false);
|
.parse(new DumbMemBufferImpl(program.getMemory(), addr), context, false);
|
||||||
|
if (lengthOverride > proto.getLength()) {
|
||||||
|
lengthOverride = 0;
|
||||||
|
}
|
||||||
return resultListing.createInstruction(addr, proto,
|
return resultListing.createInstruction(addr, proto,
|
||||||
new DumbMemBufferImpl(program.getMemory(), addr),
|
new DumbMemBufferImpl(program.getMemory(), addr),
|
||||||
new ProgramProcessorContext(program.getProgramContext(), addr));
|
new ProgramProcessorContext(program.getProgramContext(), addr),
|
||||||
|
Math.min(lengthOverride, proto.getLength()));
|
||||||
}
|
}
|
||||||
catch (Exception e) {
|
catch (Exception e) {
|
||||||
program.getBookmarkManager()
|
program.getBookmarkManager()
|
||||||
@@ -767,17 +775,13 @@ public class ProgramMerge {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean bytesAreDifferent(AddressSetView originByteDiffs, Address originMin,
|
private boolean bytesMayDiffer(AddressSetView originByteDiffs, Address originMin,
|
||||||
Address resultMin, int byteCnt) throws MemoryAccessException {
|
Address resultMin, int byteCnt) throws MemoryAccessException {
|
||||||
if (originByteDiffs != null) {
|
if (originByteDiffs != null) {
|
||||||
AddressSet resultByteDiffs = originToResultTranslator.getAddressSet(originByteDiffs);
|
AddressSet resultByteDiffs = originToResultTranslator.getAddressSet(originByteDiffs);
|
||||||
return resultByteDiffs.intersects(new AddressSet(resultMin, resultMin.add(byteCnt)));
|
return resultByteDiffs.intersects(new AddressSet(resultMin, resultMin.add(byteCnt)));
|
||||||
}
|
}
|
||||||
byte[] originBytes = new byte[byteCnt];
|
return true;
|
||||||
originProgram.getMemory().getBytes(originMin, originBytes);
|
|
||||||
byte[] resultBytes = new byte[byteCnt];
|
|
||||||
resultProgram.getMemory().getBytes(resultMin, resultBytes);
|
|
||||||
return !Arrays.equals(originBytes, resultBytes);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -808,7 +812,7 @@ public class ProgramMerge {
|
|||||||
// If there are byte differences for this instruction then the
|
// If there are byte differences for this instruction then the
|
||||||
// bytes need to get copied even though the user did not indicate to.
|
// bytes need to get copied even though the user did not indicate to.
|
||||||
if (copyBytes &&
|
if (copyBytes &&
|
||||||
bytesAreDifferent(originByteDiffs, originMin, resultMin, originData.getLength())) {
|
bytesMayDiffer(originByteDiffs, originMin, resultMin, originData.getLength())) {
|
||||||
// Copy all the bytes for the instruction if any bytes differ.
|
// Copy all the bytes for the instruction if any bytes differ.
|
||||||
ProgramMemoryUtil.copyBytesInRanges(resultProgram, originProgram, resultMin, resultMax);
|
ProgramMemoryUtil.copyBytesInRanges(resultProgram, originProgram, resultMin, resultMax);
|
||||||
}
|
}
|
||||||
@@ -1717,9 +1721,7 @@ public class ProgramMerge {
|
|||||||
// Now discard any tags we've been told to remove.
|
// Now discard any tags we've been told to remove.
|
||||||
if (discardTags != null) {
|
if (discardTags != null) {
|
||||||
Set<String> tagNames = getTagNames(discardTags);
|
Set<String> tagNames = getTagNames(discardTags);
|
||||||
Iterator<FunctionTag> iter = resultTags.iterator();
|
for (FunctionTag tag : resultTags) {
|
||||||
while (iter.hasNext()) {
|
|
||||||
FunctionTag tag = iter.next();
|
|
||||||
if (tagNames.contains(tag.getName())) {
|
if (tagNames.contains(tag.getName())) {
|
||||||
resultFunction.removeTag(tag.getName());
|
resultFunction.removeTag(tag.getName());
|
||||||
|
|
||||||
@@ -3934,8 +3936,7 @@ public class ProgramMerge {
|
|||||||
if (originObject != null) {
|
if (originObject != null) {
|
||||||
try {
|
try {
|
||||||
if (resultOpm == null) {
|
if (resultOpm == null) {
|
||||||
resultOpm =
|
resultOpm = createPropertyMap(userPropertyName, resultProgram, originOpm);
|
||||||
createPropertyMap(userPropertyName, resultProgram, originOpm);
|
|
||||||
}
|
}
|
||||||
resultOpm.add(resultAddress, originObject);
|
resultOpm.add(resultAddress, originObject);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -669,7 +669,8 @@ public class SymbolicPropogator {
|
|||||||
else {
|
else {
|
||||||
int instrByteHashCode = -1;
|
int instrByteHashCode = -1;
|
||||||
try {
|
try {
|
||||||
instrByteHashCode = Arrays.hashCode(instr.getBytes());
|
byte[] bytes = instr.getParsedBytes();
|
||||||
|
instrByteHashCode = Arrays.hashCode(bytes);
|
||||||
}
|
}
|
||||||
catch (MemoryAccessException e) {
|
catch (MemoryAccessException e) {
|
||||||
// this should NEVER happen, should always be able to get the bytes...
|
// this should NEVER happen, should always be able to get the bytes...
|
||||||
|
|||||||
@@ -26,8 +26,7 @@ import ghidra.docking.settings.*;
|
|||||||
import ghidra.framework.plugintool.ServiceProvider;
|
import ghidra.framework.plugintool.ServiceProvider;
|
||||||
import ghidra.program.model.address.*;
|
import ghidra.program.model.address.*;
|
||||||
import ghidra.program.model.data.EndianSettingsDefinition;
|
import ghidra.program.model.data.EndianSettingsDefinition;
|
||||||
import ghidra.program.model.listing.CodeUnit;
|
import ghidra.program.model.listing.*;
|
||||||
import ghidra.program.model.listing.Program;
|
|
||||||
import ghidra.program.model.mem.MemoryAccessException;
|
import ghidra.program.model.mem.MemoryAccessException;
|
||||||
import ghidra.program.util.BytesFieldLocation;
|
import ghidra.program.util.BytesFieldLocation;
|
||||||
import ghidra.program.util.ProgramLocation;
|
import ghidra.program.util.ProgramLocation;
|
||||||
@@ -50,100 +49,98 @@ public class BytesTableColumn extends ProgramLocationTableColumnExtensionPoint<A
|
|||||||
private static SettingsDefinition[] SETTINGS_DEFS =
|
private static SettingsDefinition[] SETTINGS_DEFS =
|
||||||
{ BYTE_COUNT, MEMORY_OFFSET, ENDIANESS, FORMAT };
|
{ BYTE_COUNT, MEMORY_OFFSET, ENDIANESS, FORMAT };
|
||||||
|
|
||||||
private final GColumnRenderer<Byte[]> monospacedRenderer =
|
private final GColumnRenderer<Byte[]> monospacedRenderer = new AbstractGColumnRenderer<>() {
|
||||||
new AbstractGColumnRenderer<>() {
|
@Override
|
||||||
@Override
|
protected void configureFont(JTable table, TableModel model, int column) {
|
||||||
protected void configureFont(JTable table, TableModel model, int column) {
|
setFont(getFixedWidthFont());
|
||||||
setFont(getFixedWidthFont());
|
}
|
||||||
|
|
||||||
|
private String formatBytes(Byte[] bytes, Settings settings) {
|
||||||
|
boolean bigEndian = (ENDIANESS.getChoice(settings) != EndianSettingsDefinition.LITTLE);
|
||||||
|
|
||||||
|
int startIx = 0;
|
||||||
|
int endIx = bytes.length;
|
||||||
|
int inc = 1;
|
||||||
|
if (!bigEndian) {
|
||||||
|
startIx = bytes.length - 1;
|
||||||
|
endIx = -1;
|
||||||
|
inc = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String formatBytes(Byte[] bytes, Settings settings) {
|
int format = FORMAT.getChoice(settings);
|
||||||
boolean bigEndian =
|
if (format == FormatSettingsDefinition.CHAR) {
|
||||||
(ENDIANESS.getChoice(settings) != EndianSettingsDefinition.LITTLE);
|
return bytesToString(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
int startIx = 0;
|
StringBuilder buffer = new StringBuilder();
|
||||||
int endIx = bytes.length;
|
for (int i = startIx; i != endIx; i += inc) {
|
||||||
int inc = 1;
|
if (buffer.length() != 0) {
|
||||||
if (!bigEndian) {
|
buffer.append(' ');
|
||||||
startIx = bytes.length - 1;
|
|
||||||
endIx = -1;
|
|
||||||
inc = -1;
|
|
||||||
}
|
}
|
||||||
|
buffer.append(getByteString(bytes[i], format));
|
||||||
|
}
|
||||||
|
return buffer.toString();
|
||||||
|
}
|
||||||
|
|
||||||
int format = FORMAT.getChoice(settings);
|
private String bytesToString(Byte[] bytes) {
|
||||||
if (format == FormatSettingsDefinition.CHAR) {
|
StringBuilder buf = new StringBuilder();
|
||||||
return bytesToString(bytes);
|
for (byte b : bytes) {
|
||||||
|
char c = (char) (b & 0xff);
|
||||||
|
if (c > 32 && c < 128) {
|
||||||
|
buf.append((char) (b & 0xff));
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
StringBuilder buffer = new StringBuilder();
|
buf.append('.');
|
||||||
for (int i = startIx; i != endIx; i += inc) {
|
|
||||||
if (buffer.length() != 0) {
|
|
||||||
buffer.append(' ');
|
|
||||||
}
|
|
||||||
buffer.append(getByteString(bytes[i], format));
|
|
||||||
}
|
}
|
||||||
return buffer.toString();
|
|
||||||
}
|
}
|
||||||
|
return buf.toString();
|
||||||
|
}
|
||||||
|
|
||||||
private String bytesToString(Byte[] bytes) {
|
private String getByteString(Byte b, int format) {
|
||||||
StringBuilder buf = new StringBuilder();
|
|
||||||
for (byte b : bytes) {
|
String val;
|
||||||
char c = (char) (b & 0xff);
|
switch (format) {
|
||||||
if (c > 32 && c < 128) {
|
case FormatSettingsDefinition.DECIMAL:
|
||||||
buf.append((char) (b & 0xff));
|
val = Integer.toString(b);
|
||||||
}
|
break;
|
||||||
else {
|
case FormatSettingsDefinition.BINARY:
|
||||||
buf.append('.');
|
val = Integer.toBinaryString(b & 0x0ff);
|
||||||
}
|
val = StringFormat.padIt(val, 8, (char) 0, true);
|
||||||
}
|
break;
|
||||||
return buf.toString();
|
case FormatSettingsDefinition.OCTAL:
|
||||||
|
val = Integer.toOctalString(b & 0x0ff);
|
||||||
|
val = StringFormat.padIt(val, 3, (char) 0, true);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
case FormatSettingsDefinition.HEX:
|
||||||
|
val = Integer.toHexString(b & 0x0ff).toUpperCase();
|
||||||
|
val = StringFormat.padIt(val, 2, (char) 0, true);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
private String getByteString(Byte b, int format) {
|
@Override
|
||||||
|
public Component getTableCellRendererComponent(GTableCellRenderingData data) {
|
||||||
|
|
||||||
String val;
|
JLabel label = (JLabel) super.getTableCellRendererComponent(data);
|
||||||
switch (format) {
|
|
||||||
case FormatSettingsDefinition.DECIMAL:
|
|
||||||
val = Integer.toString(b);
|
|
||||||
break;
|
|
||||||
case FormatSettingsDefinition.BINARY:
|
|
||||||
val = Integer.toBinaryString(b & 0x0ff);
|
|
||||||
val = StringFormat.padIt(val, 8, (char) 0, true);
|
|
||||||
break;
|
|
||||||
case FormatSettingsDefinition.OCTAL:
|
|
||||||
val = Integer.toOctalString(b & 0x0ff);
|
|
||||||
val = StringFormat.padIt(val, 3, (char) 0, true);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
case FormatSettingsDefinition.HEX:
|
|
||||||
val = Integer.toHexString(b & 0x0ff).toUpperCase();
|
|
||||||
val = StringFormat.padIt(val, 2, (char) 0, true);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return val;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
Object value = data.getValue();
|
||||||
public Component getTableCellRendererComponent(GTableCellRenderingData data) {
|
Settings settings = data.getColumnSettings();
|
||||||
|
|
||||||
JLabel label = (JLabel) super.getTableCellRendererComponent(data);
|
Byte[] bytes = (Byte[]) value;
|
||||||
|
|
||||||
Object value = data.getValue();
|
setText(formatBytes(bytes, settings));
|
||||||
Settings settings = data.getColumnSettings();
|
|
||||||
|
|
||||||
Byte[] bytes = (Byte[]) value;
|
return label;
|
||||||
|
}
|
||||||
|
|
||||||
setText(formatBytes(bytes, settings));
|
@Override
|
||||||
|
public String getFilterString(Byte[] t, Settings settings) {
|
||||||
return label;
|
String formatted = formatBytes(t, settings);
|
||||||
}
|
return formatted;
|
||||||
|
}
|
||||||
@Override
|
};
|
||||||
public String getFilterString(Byte[] t, Settings settings) {
|
|
||||||
String formatted = formatBytes(t, settings);
|
|
||||||
return formatted;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default Constructor
|
* Default Constructor
|
||||||
@@ -194,7 +191,12 @@ public class BytesTableColumn extends ProgramLocationTableColumnExtensionPoint<A
|
|||||||
if (cu == null) { // can happen for 'SpecialAddress'es
|
if (cu == null) { // can happen for 'SpecialAddress'es
|
||||||
return new Byte[0];
|
return new Byte[0];
|
||||||
}
|
}
|
||||||
bytes = cu.getBytes();
|
if (cu instanceof Instruction instr) {
|
||||||
|
bytes = instr.getParsedBytes();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
bytes = cu.getBytes();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -94,7 +94,7 @@ public abstract class AbstractListingMergeManagerTest extends AbstractMergeTest
|
|||||||
new ProgramProcessorContext(program.getProgramContext(), atAddress);
|
new ProgramProcessorContext(program.getProgramContext(), atAddress);
|
||||||
InstructionPrototype proto = program.getLanguage().parse(buf, context, false);
|
InstructionPrototype proto = program.getLanguage().parse(buf, context, false);
|
||||||
Instruction createdInstruction =
|
Instruction createdInstruction =
|
||||||
listing.createInstruction(atAddress, proto, buf, context);
|
listing.createInstruction(atAddress, proto, buf, context, 0);
|
||||||
commit = true;
|
commit = true;
|
||||||
return createdInstruction;
|
return createdInstruction;
|
||||||
}
|
}
|
||||||
|
|||||||
+261
@@ -0,0 +1,261 @@
|
|||||||
|
/* ###
|
||||||
|
* IP: GHIDRA
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ghidra.app.merge.listing;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import ghidra.program.database.OriginalProgramModifierListener;
|
||||||
|
import ghidra.program.database.ProgramDB;
|
||||||
|
import ghidra.program.model.address.AddressSet;
|
||||||
|
import ghidra.program.model.listing.Instruction;
|
||||||
|
import ghidra.program.model.listing.Listing;
|
||||||
|
import ghidra.program.model.symbol.*;
|
||||||
|
import ghidra.program.model.util.CodeUnitInsertionException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test the merge of the versioned program's listing.
|
||||||
|
* Provides tests that create instructions with overrides in various combinations.
|
||||||
|
* For tests with conflicts, checks selection of latest, my, or original code unit(s).
|
||||||
|
*/
|
||||||
|
public class CodeUnitMergeManager6Test extends AbstractListingMergeManagerTest {
|
||||||
|
|
||||||
|
public CodeUnitMergeManager6Test() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAddLengthOverrideMyInstrPickMyInstr() throws Exception {
|
||||||
|
mtf.initialize("DiffTestPgm1", new OriginalProgramModifierListener() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void modifyOriginal(ProgramDB program) throws Exception {
|
||||||
|
int txId = program.startTransaction("Modify Original Program");
|
||||||
|
boolean commit = false;
|
||||||
|
try {
|
||||||
|
// nop #0x3 11011001 00110001 00000000
|
||||||
|
// imm r1,#0x300 00110001 00000000
|
||||||
|
// imm r0,#0x0 00000000 00000000
|
||||||
|
setBytes(program, "0x10013d9", new byte[] { (byte) 0xd9, 0x31, 0, 0, 0 });
|
||||||
|
commit = true;
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
program.endTransaction(txId, commit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void modifyLatest(ProgramDB program) {
|
||||||
|
int txId = program.startTransaction("Modify Latest Program");
|
||||||
|
boolean commit = false;
|
||||||
|
try {
|
||||||
|
disassemble(program, "0x10013d9", "0x10013dc");
|
||||||
|
commit = true;
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
program.endTransaction(txId, commit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void modifyPrivate(ProgramDB program) {
|
||||||
|
int txId = program.startTransaction("Modify My Program");
|
||||||
|
boolean commit = false;
|
||||||
|
try {
|
||||||
|
disassemble(program, "0x10013d9", "0x10013dc");
|
||||||
|
Listing listing = program.getListing();
|
||||||
|
Instruction instr = listing.getInstructionAt(addr(program, "0x10013d9"));
|
||||||
|
try {
|
||||||
|
instr.setLengthOverride(1);
|
||||||
|
}
|
||||||
|
catch (CodeUnitInsertionException e) {
|
||||||
|
failWithException("Unexpected exception", e);
|
||||||
|
}
|
||||||
|
disassemble(program, "0x10013da", "0x10013db");
|
||||||
|
instr = listing.getInstructionAt(addr(program, "0x10013da"));
|
||||||
|
assertNotNull("Failed to create overlapped instruction", instr);
|
||||||
|
commit = true;
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
program.endTransaction(txId, commit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
executeMerge(ASK_USER);
|
||||||
|
chooseCodeUnit("0x10013d9", "0x10013db", KEEP_MY);
|
||||||
|
waitForMergeCompletion();
|
||||||
|
|
||||||
|
assertSameCodeUnits(resultProgram, myProgram, new AddressSet(addr("0x10013d9"),
|
||||||
|
addr("0x10013db")));
|
||||||
|
|
||||||
|
ReferenceManager refMgr = resultProgram.getReferenceManager();
|
||||||
|
Reference[] refs = refMgr.getReferencesFrom(addr("0x10013d9"));
|
||||||
|
assertEquals(1, refs.length);
|
||||||
|
assertEquals(RefType.FALL_THROUGH, refs[0].getReferenceType());
|
||||||
|
assertEquals(addr("0x10013dc"), refs[0].getToAddress());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testModifyLengthOverrideMyInstrPickOriginalInstr() throws Exception {
|
||||||
|
mtf.initialize("DiffTestPgm1", new OriginalProgramModifierListener() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void modifyOriginal(ProgramDB program) throws Exception {
|
||||||
|
int txId = program.startTransaction("Modify Original Program");
|
||||||
|
boolean commit = false;
|
||||||
|
try {
|
||||||
|
// nop #0x3 11011001 00110001 00000000
|
||||||
|
// imm r1,#0x300 00110001 00000000
|
||||||
|
// imm r0,#0x0 00000000 00000000
|
||||||
|
setBytes(program, "0x10013d9", new byte[] { (byte) 0xd9, 0x31, 0, 0, 0 });
|
||||||
|
disassemble(program, "0x10013d9", "0x10013dc");
|
||||||
|
Listing listing = program.getListing();
|
||||||
|
Instruction instr = listing.getInstructionAt(addr(program, "0x10013d9"));
|
||||||
|
try {
|
||||||
|
instr.setLengthOverride(1);
|
||||||
|
}
|
||||||
|
catch (CodeUnitInsertionException e) {
|
||||||
|
failWithException("Unexpected exception", e);
|
||||||
|
}
|
||||||
|
commit = true;
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
program.endTransaction(txId, commit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void modifyLatest(ProgramDB program) {
|
||||||
|
int txId = program.startTransaction("Modify Latest Program");
|
||||||
|
boolean commit = false;
|
||||||
|
try {
|
||||||
|
disassemble(program, "0x10013da", "0x10013db");
|
||||||
|
Listing listing = program.getListing();
|
||||||
|
Instruction instr = listing.getInstructionAt(addr(program, "0x10013da"));
|
||||||
|
assertNotNull("Failed to create overlapped instruction", instr);
|
||||||
|
commit = true;
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
program.endTransaction(txId, commit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void modifyPrivate(ProgramDB program) {
|
||||||
|
int txId = program.startTransaction("Modify My Program");
|
||||||
|
boolean commit = false;
|
||||||
|
try {
|
||||||
|
clear(program, "0x10013d9", "0x10013d9");
|
||||||
|
disassemble(program, "0x10013d9", "0x10013dc");
|
||||||
|
commit = true;
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
program.endTransaction(txId, commit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
executeMerge(ASK_USER);
|
||||||
|
chooseCodeUnit("0x10013d9", "0x10013db", KEEP_ORIGINAL);
|
||||||
|
waitForMergeCompletion();
|
||||||
|
|
||||||
|
assertSameCodeUnits(resultProgram, originalProgram, new AddressSet(addr("0x10013d9"),
|
||||||
|
addr("0x10013db")));
|
||||||
|
|
||||||
|
ReferenceManager refMgr = resultProgram.getReferenceManager();
|
||||||
|
Reference[] refs = refMgr.getReferencesFrom(addr("0x10013d9"));
|
||||||
|
assertEquals(1, refs.length);
|
||||||
|
assertEquals(RefType.FALL_THROUGH, refs[0].getReferenceType());
|
||||||
|
assertEquals(addr("0x10013dc"), refs[0].getToAddress());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAddLengthAndFallthroughOverrideMyInstrPickMyInstr() throws Exception {
|
||||||
|
mtf.initialize("DiffTestPgm1", new OriginalProgramModifierListener() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void modifyOriginal(ProgramDB program) throws Exception {
|
||||||
|
int txId = program.startTransaction("Modify Original Program");
|
||||||
|
boolean commit = false;
|
||||||
|
try {
|
||||||
|
// nop #0x3 11011001 00110001 00000000
|
||||||
|
// imm r1,#0x300 00110001 00000000
|
||||||
|
// imm r0,#0x0 00000000 00000000
|
||||||
|
setBytes(program, "0x10013d9", new byte[] { (byte) 0xd9, 0x31, 0, 0, 0 });
|
||||||
|
commit = true;
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
program.endTransaction(txId, commit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void modifyLatest(ProgramDB program) {
|
||||||
|
int txId = program.startTransaction("Modify Latest Program");
|
||||||
|
boolean commit = false;
|
||||||
|
try {
|
||||||
|
disassemble(program, "0x10013d9", "0x10013dc");
|
||||||
|
commit = true;
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
program.endTransaction(txId, commit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void modifyPrivate(ProgramDB program) {
|
||||||
|
int txId = program.startTransaction("Modify My Program");
|
||||||
|
boolean commit = false;
|
||||||
|
try {
|
||||||
|
disassemble(program, "0x10013d9", "0x10013dc");
|
||||||
|
Listing listing = program.getListing();
|
||||||
|
Instruction instr = listing.getInstructionAt(addr(program, "0x10013d9"));
|
||||||
|
try {
|
||||||
|
instr.setLengthOverride(1);
|
||||||
|
}
|
||||||
|
catch (CodeUnitInsertionException e) {
|
||||||
|
failWithException("Unexpected exception", e);
|
||||||
|
}
|
||||||
|
instr.setFallThrough(addr(program, "0x10013de"));
|
||||||
|
|
||||||
|
disassemble(program, "0x10013da", "0x10013db");
|
||||||
|
instr = listing.getInstructionAt(addr(program, "0x10013da"));
|
||||||
|
assertNotNull("Failed to create overlapped instruction", instr);
|
||||||
|
commit = true;
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
program.endTransaction(txId, commit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
executeMerge(ASK_USER);
|
||||||
|
chooseCodeUnit("0x10013d9", "0x10013db", KEEP_MY);
|
||||||
|
waitForMergeCompletion();
|
||||||
|
|
||||||
|
assertSameCodeUnits(resultProgram, myProgram, new AddressSet(addr("0x10013d9"),
|
||||||
|
addr("0x10013db")));
|
||||||
|
|
||||||
|
ReferenceManager refMgr = resultProgram.getReferenceManager();
|
||||||
|
Reference[] refs = refMgr.getReferencesFrom(addr("0x10013d9"));
|
||||||
|
assertEquals(1, refs.length);
|
||||||
|
assertEquals(RefType.FALL_THROUGH, refs[0].getReferenceType());
|
||||||
|
assertEquals(addr("0x10013de"), refs[0].getToAddress());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+55
-1
@@ -19,6 +19,7 @@ import static org.junit.Assert.*;
|
|||||||
|
|
||||||
import org.junit.*;
|
import org.junit.*;
|
||||||
|
|
||||||
|
import db.Transaction;
|
||||||
import ghidra.app.plugin.core.bookmark.BookmarkPlugin;
|
import ghidra.app.plugin.core.bookmark.BookmarkPlugin;
|
||||||
import ghidra.app.plugin.core.clear.ClearPlugin;
|
import ghidra.app.plugin.core.clear.ClearPlugin;
|
||||||
import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
|
import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
|
||||||
@@ -1678,6 +1679,59 @@ public class CodeUnitIteratorTest extends AbstractGhidraHeadedIntegrationTest {
|
|||||||
assertEquals(10, cnt);
|
assertEquals(10, cnt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetCodeUnitsSetWithOverride() throws Exception {
|
||||||
|
|
||||||
|
// set length-override on 0x3f4
|
||||||
|
try (Transaction tx = program.openTransaction("Set Length Override")) {
|
||||||
|
program.getListing().getInstructionAt(addr(0x3f4)).setLengthOverride(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
AddressSet set = new AddressSet();
|
||||||
|
set.addRange(addr(0x3f0), addr(0x3f9));
|
||||||
|
|
||||||
|
CodeUnitIterator it = listing.getCodeUnits(set, true);
|
||||||
|
|
||||||
|
CodeUnit cu = it.next();
|
||||||
|
assertEquals(addr(0x3f1), cu.getMinAddress());
|
||||||
|
assertTrue(cu instanceof Data);
|
||||||
|
assertFalse(((Data) cu).isDefined());
|
||||||
|
|
||||||
|
cu = it.next();
|
||||||
|
assertEquals(addr(0x3f2), cu.getMinAddress());
|
||||||
|
assertTrue(cu instanceof Instruction);
|
||||||
|
assertEquals(2, cu.getLength());
|
||||||
|
|
||||||
|
cu = it.next();
|
||||||
|
assertEquals(addr(0x3f4), cu.getMinAddress());
|
||||||
|
assertTrue(cu instanceof Instruction);
|
||||||
|
assertEquals(1, cu.getLength()); // length overriden
|
||||||
|
assertEquals("imm r0,#0x0", cu.toString());
|
||||||
|
|
||||||
|
cu = it.next();
|
||||||
|
assertEquals(addr(0x3f5), cu.getMinAddress());
|
||||||
|
assertTrue(cu instanceof Data);
|
||||||
|
assertFalse(((Data) cu).isDefined());
|
||||||
|
|
||||||
|
cu = it.next();
|
||||||
|
assertEquals(addr(0x3f6), cu.getMinAddress());
|
||||||
|
assertTrue(cu instanceof Instruction);
|
||||||
|
assertEquals(2, cu.getLength());
|
||||||
|
|
||||||
|
cu = it.next();
|
||||||
|
assertEquals(addr(0x3f8), cu.getMinAddress());
|
||||||
|
assertTrue(cu instanceof Data);
|
||||||
|
assertFalse(((Data) cu).isDefined());
|
||||||
|
|
||||||
|
cu = it.next();
|
||||||
|
assertEquals(addr(0x3f9), cu.getMinAddress());
|
||||||
|
assertTrue(cu instanceof Data);
|
||||||
|
assertFalse(((Data) cu).isDefined());
|
||||||
|
|
||||||
|
assertFalse(it.hasNext());
|
||||||
|
assertNull(it.next());
|
||||||
|
}
|
||||||
|
|
||||||
private Address addr(long l) {
|
private Address addr(long l) {
|
||||||
return space.getAddress(l);
|
return space.getAddress(l);
|
||||||
}
|
}
|
||||||
@@ -1704,7 +1758,7 @@ public class CodeUnitIteratorTest extends AbstractGhidraHeadedIntegrationTest {
|
|||||||
ProcessorContext context = new ProgramProcessorContext(program.getProgramContext(), atAddr);
|
ProcessorContext context = new ProgramProcessorContext(program.getProgramContext(), atAddr);
|
||||||
InstructionPrototype proto = program.getLanguage().parse(buf, context, false);
|
InstructionPrototype proto = program.getLanguage().parse(buf, context, false);
|
||||||
|
|
||||||
listing.createInstruction(atAddr, proto, buf, context);
|
listing.createInstruction(atAddr, proto, buf, context, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void startTransaction() {
|
private void startTransaction() {
|
||||||
|
|||||||
+1
-1
@@ -99,7 +99,7 @@ public class SymbolUtilities1Test extends AbstractGhidraHeadedIntegrationTest {
|
|||||||
new ProgramProcessorContext(program.getProgramContext(), addr(0x200));
|
new ProgramProcessorContext(program.getProgramContext(), addr(0x200));
|
||||||
DumbMemBufferImpl membuf = new DumbMemBufferImpl(program.getMemory(), addr(0x200));
|
DumbMemBufferImpl membuf = new DumbMemBufferImpl(program.getMemory(), addr(0x200));
|
||||||
InstructionPrototype proto = program.getLanguage().parse(membuf, context, false);
|
InstructionPrototype proto = program.getLanguage().parse(membuf, context, false);
|
||||||
listing.createInstruction(addr(0x200), proto, membuf, context);
|
listing.createInstruction(addr(0x200), proto, membuf, context, 0);
|
||||||
Symbol symbol = symbolTable.getPrimarySymbol(addr(0x200));
|
Symbol symbol = symbolTable.getPrimarySymbol(addr(0x200));
|
||||||
assertEquals("LAB_00000200", symbol.getName());
|
assertEquals("LAB_00000200", symbol.getName());
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -97,7 +97,7 @@ public class SymbolUtilities2Test extends AbstractGhidraHeadedIntegrationTest {
|
|||||||
new ProgramProcessorContext(program.getProgramContext(), addr(0x200));
|
new ProgramProcessorContext(program.getProgramContext(), addr(0x200));
|
||||||
DumbMemBufferImpl membuf = new DumbMemBufferImpl(program.getMemory(), addr(0x200));
|
DumbMemBufferImpl membuf = new DumbMemBufferImpl(program.getMemory(), addr(0x200));
|
||||||
InstructionPrototype proto = program.getLanguage().parse(membuf, context, false);
|
InstructionPrototype proto = program.getLanguage().parse(membuf, context, false);
|
||||||
listing.createInstruction(addr(0x200), proto, membuf, context);
|
listing.createInstruction(addr(0x200), proto, membuf, context, 0);
|
||||||
Symbol symbol = symbolTable.getPrimarySymbol(addr(0x200));
|
Symbol symbol = symbolTable.getPrimarySymbol(addr(0x200));
|
||||||
assertEquals("LAB_CODE_0200", symbol.getName());
|
assertEquals("LAB_CODE_0200", symbol.getName());
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1063,7 +1063,7 @@ public class CodeManagerTest extends AbstractGenericTest {
|
|||||||
MemBuffer buf = new DumbMemBufferImpl(mem, atAddr);
|
MemBuffer buf = new DumbMemBufferImpl(mem, atAddr);
|
||||||
ProcessorContext context = new ProgramProcessorContext(program.getProgramContext(), atAddr);
|
ProcessorContext context = new ProgramProcessorContext(program.getProgramContext(), atAddr);
|
||||||
InstructionPrototype proto = program.getLanguage().parse(buf, context, false);
|
InstructionPrototype proto = program.getLanguage().parse(buf, context, false);
|
||||||
listing.createInstruction(atAddr, proto, buf, context);
|
listing.createInstruction(atAddr, proto, buf, context, 0);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-5
@@ -15,10 +15,9 @@
|
|||||||
*/
|
*/
|
||||||
package ghidra.feature.vt.api.markupitem;
|
package ghidra.feature.vt.api.markupitem;
|
||||||
|
|
||||||
import static ghidra.feature.vt.api.main.VTMarkupItemApplyActionType.REPLACE;
|
import static ghidra.feature.vt.api.main.VTMarkupItemApplyActionType.*;
|
||||||
import static ghidra.feature.vt.db.VTTestUtils.addr;
|
import static ghidra.feature.vt.db.VTTestUtils.*;
|
||||||
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.*;
|
||||||
import static org.junit.Assert.assertTrue;
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -379,7 +378,7 @@ public class DataTypeMarkupItemTest extends AbstractVTMarkupItemTest {
|
|||||||
new ProgramProcessorContext(program.getProgramContext(), atAddress);
|
new ProgramProcessorContext(program.getProgramContext(), atAddress);
|
||||||
InstructionPrototype proto = program.getLanguage().parse(buf, context, false);
|
InstructionPrototype proto = program.getLanguage().parse(buf, context, false);
|
||||||
Instruction createdInstruction =
|
Instruction createdInstruction =
|
||||||
listing.createInstruction(atAddress, proto, buf, context);
|
listing.createInstruction(atAddress, proto, buf, context, 0);
|
||||||
commit = true;
|
commit = true;
|
||||||
return createdInstruction;
|
return createdInstruction;
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-1
@@ -182,7 +182,8 @@ public abstract class PcodeEmit {
|
|||||||
*/
|
*/
|
||||||
void resolveFinalFallthrough() throws IOException {
|
void resolveFinalFallthrough() throws IOException {
|
||||||
try {
|
try {
|
||||||
if (fallOverride == null || fallOverride.equals(getStartAddress().add(fallOffset))) {
|
if (fallOverride == null) {
|
||||||
|
// handles both length-override and fallthrough override cases
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -505,19 +505,6 @@ abstract class PseudoCodeUnit implements CodeUnit {
|
|||||||
return program.getListing().getCodeUnitBefore(address);
|
return program.getListing().getCodeUnitBefore(address);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Return true if the given CodeUnit follows directly after this code unit.
|
|
||||||
*
|
|
||||||
* @throws ConcurrentModificationException
|
|
||||||
* if this object is no longer valid.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public boolean isSuccessor(CodeUnit codeUnit) {
|
|
||||||
Address min = codeUnit.getMinAddress();
|
|
||||||
|
|
||||||
return this.getMaxAddress().isSuccessor(min);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getComment(int commentType) {
|
public String getComment(int commentType) {
|
||||||
return comments.get(commentType);
|
return comments.get(commentType);
|
||||||
|
|||||||
+22
@@ -146,6 +146,18 @@ public class PseudoInstruction extends PseudoCodeUnit implements Instruction, In
|
|||||||
return b0;
|
return b0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getParsedLength() {
|
||||||
|
// length-override is not supported
|
||||||
|
return getLength();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] getParsedBytes() throws MemoryAccessException {
|
||||||
|
// length-override is not supported
|
||||||
|
return getBytes();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Register getBaseContextRegister() {
|
public Register getBaseContextRegister() {
|
||||||
return procContext.getBaseContextRegister();
|
return procContext.getBaseContextRegister();
|
||||||
@@ -505,6 +517,16 @@ public class PseudoInstruction extends PseudoCodeUnit implements Instruction, In
|
|||||||
this.flowOverride = flowOverride != null ? flowOverride : FlowOverride.NONE;
|
this.flowOverride = flowOverride != null ? flowOverride : FlowOverride.NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setLengthOverride(int length) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isLengthOverridden() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isFallThroughOverridden() {
|
public boolean isFallThroughOverridden() {
|
||||||
return fallThroughOverride != null;
|
return fallThroughOverride != null;
|
||||||
|
|||||||
+9
-9
@@ -22,7 +22,8 @@ import ghidra.program.database.function.OverlappingFunctionException;
|
|||||||
import ghidra.program.database.module.TreeManager;
|
import ghidra.program.database.module.TreeManager;
|
||||||
import ghidra.program.database.symbol.FunctionSymbol;
|
import ghidra.program.database.symbol.FunctionSymbol;
|
||||||
import ghidra.program.model.address.*;
|
import ghidra.program.model.address.*;
|
||||||
import ghidra.program.model.data.*;
|
import ghidra.program.model.data.DataType;
|
||||||
|
import ghidra.program.model.data.DataTypeManager;
|
||||||
import ghidra.program.model.lang.*;
|
import ghidra.program.model.lang.*;
|
||||||
import ghidra.program.model.listing.*;
|
import ghidra.program.model.listing.*;
|
||||||
import ghidra.program.model.mem.MemBuffer;
|
import ghidra.program.model.mem.MemBuffer;
|
||||||
@@ -107,7 +108,7 @@ class ListingDB implements Listing {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Instruction getInstructionContaining(Address addr) {
|
public Instruction getInstructionContaining(Address addr) {
|
||||||
return codeMgr.getInstructionContaining(addr);
|
return codeMgr.getInstructionContaining(addr, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -261,14 +262,15 @@ class ListingDB implements Listing {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PropertyMap getPropertyMap(String propertyName) {
|
public PropertyMap<?> getPropertyMap(String propertyName) {
|
||||||
return codeMgr.getPropertyMap(propertyName);
|
return codeMgr.getPropertyMap(propertyName);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Instruction createInstruction(Address addr, InstructionPrototype prototype,
|
public Instruction createInstruction(Address addr, InstructionPrototype prototype,
|
||||||
MemBuffer memBuf, ProcessorContextView context) throws CodeUnitInsertionException {
|
MemBuffer memBuf, ProcessorContextView context, int length)
|
||||||
return codeMgr.createCodeUnit(addr, prototype, memBuf, context);
|
throws CodeUnitInsertionException {
|
||||||
|
return codeMgr.createCodeUnit(addr, prototype, memBuf, context, length);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -278,8 +280,7 @@ class ListingDB implements Listing {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Data createData(Address addr, DataType dataType)
|
public Data createData(Address addr, DataType dataType) throws CodeUnitInsertionException {
|
||||||
throws CodeUnitInsertionException {
|
|
||||||
return codeMgr.createCodeUnit(addr, dataType, dataType.getLength());
|
return codeMgr.createCodeUnit(addr, dataType, dataType.getLength());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -292,8 +293,7 @@ class ListingDB implements Listing {
|
|||||||
@Override
|
@Override
|
||||||
public void clearCodeUnits(Address startAddr, Address endAddr, boolean clearContext) {
|
public void clearCodeUnits(Address startAddr, Address endAddr, boolean clearContext) {
|
||||||
try {
|
try {
|
||||||
codeMgr.clearCodeUnits(startAddr, endAddr, clearContext,
|
codeMgr.clearCodeUnits(startAddr, endAddr, clearContext, TaskMonitor.DUMMY);
|
||||||
TaskMonitor.DUMMY);
|
|
||||||
}
|
}
|
||||||
catch (CancelledException e) {
|
catch (CancelledException e) {
|
||||||
// can't happen with dummy monitor
|
// can't happen with dummy monitor
|
||||||
|
|||||||
+3
-1
@@ -109,8 +109,10 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM
|
|||||||
* property map (StringTranslations).
|
* property map (StringTranslations).
|
||||||
* 19-Jan-2023 - version 26 Improved relocation data records to incorporate status and
|
* 19-Jan-2023 - version 26 Improved relocation data records to incorporate status and
|
||||||
* byte-length when original FileBytes should be used.
|
* byte-length when original FileBytes should be used.
|
||||||
|
* 10-Jul-2023 - VERSION 27 Add support for Instruction length override which utilizes
|
||||||
|
* unused flag bits.
|
||||||
*/
|
*/
|
||||||
static final int DB_VERSION = 26;
|
static final int DB_VERSION = 27;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* UPGRADE_REQUIRED_BFORE_VERSION should be changed to DB_VERSION anytime the
|
* UPGRADE_REQUIRED_BFORE_VERSION should be changed to DB_VERSION anytime the
|
||||||
|
|||||||
+85
-24
@@ -155,11 +155,16 @@ public class CodeManager implements ErrorHandler, ManagerDB {
|
|||||||
while (addrIter.hasNext()) {
|
while (addrIter.hasNext()) {
|
||||||
monitor.checkCancelled();
|
monitor.checkCancelled();
|
||||||
Address addr = addrIter.next();
|
Address addr = addrIter.next();
|
||||||
|
|
||||||
|
Instruction instr = getInstructionAt(addr);
|
||||||
|
if (instr == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
long offset = oldFallThroughs.getLong(addr);
|
long offset = oldFallThroughs.getLong(addr);
|
||||||
Address toAddr = addr.getNewAddress(offset);
|
Address toAddr = addr.getNewAddress(offset);
|
||||||
refMgr.addMemoryReference(addr, toAddr, RefType.FALL_THROUGH,
|
instr.setFallThrough(toAddr);
|
||||||
SourceType.USER_DEFINED, Reference.MNEMONIC);
|
|
||||||
}
|
}
|
||||||
catch (NoValueException e) {
|
catch (NoValueException e) {
|
||||||
// skip
|
// skip
|
||||||
@@ -531,6 +536,16 @@ public class CodeManager implements ErrorHandler, ManagerDB {
|
|||||||
if (flowOverride != FlowOverride.NONE) {
|
if (flowOverride != FlowOverride.NONE) {
|
||||||
lastInstruction.setFlowOverride(flowOverride);
|
lastInstruction.setFlowOverride(flowOverride);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (protoInstr.isLengthOverridden()) {
|
||||||
|
lastInstruction.setLengthOverride(protoInstr.getLength());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (CodeUnitInsertionException e) {
|
||||||
|
// unexpected - should have been caught previously
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (errorAddr != null && conflictCodeUnit == null &&
|
if (errorAddr != null && conflictCodeUnit == null &&
|
||||||
@@ -581,21 +596,34 @@ public class CodeManager implements ErrorHandler, ManagerDB {
|
|||||||
* @param prototype instruction definition object
|
* @param prototype instruction definition object
|
||||||
* @param memBuf the MemBuffer to use to get the bytes from memory
|
* @param memBuf the MemBuffer to use to get the bytes from memory
|
||||||
* @param context object that has the state of all the registers.
|
* @param context object that has the state of all the registers.
|
||||||
* @return the new instruction
|
* @param length instruction byte-length (must be in the range 0..prototype.getLength()).
|
||||||
* @exception CodeUnitInsertionException thrown if code unit
|
* If smaller than the prototype length it must have a value no greater than 7, otherwise
|
||||||
* overlaps with an existing code unit
|
* 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.
|
||||||
*/
|
*/
|
||||||
public Instruction createCodeUnit(Address address, InstructionPrototype prototype,
|
public Instruction createCodeUnit(Address address, InstructionPrototype prototype,
|
||||||
MemBuffer memBuf, ProcessorContextView context) throws CodeUnitInsertionException {
|
MemBuffer memBuf, ProcessorContextView context, int length)
|
||||||
|
throws CodeUnitInsertionException {
|
||||||
|
|
||||||
lock.acquire();
|
lock.acquire();
|
||||||
creatingInstruction = true;
|
creatingInstruction = true;
|
||||||
try {
|
try {
|
||||||
Address endAddr = address.addNoWrap(prototype.getLength() - 1);
|
int forcedLengthOverride = InstructionDB.checkLengthOverride(length, prototype);
|
||||||
|
if (length == 0) {
|
||||||
|
length = prototype.getLength();
|
||||||
|
}
|
||||||
|
Address endAddr = address.addNoWrap(length - 1);
|
||||||
checkValidAddressRange(address, endAddr);
|
checkValidAddressRange(address, endAddr);
|
||||||
|
|
||||||
InstructionDB inst = addInstruction(address, endAddr, prototype, memBuf, context);
|
InstructionDB inst = addInstruction(address, endAddr, prototype, memBuf, context);
|
||||||
|
if (forcedLengthOverride != 0) {
|
||||||
|
inst.doSetLengthOverride(forcedLengthOverride);
|
||||||
|
}
|
||||||
|
|
||||||
// fire event
|
// fire event
|
||||||
program.setChanged(ChangeManager.DOCR_CODE_ADDED, address, endAddr, null, inst);
|
program.setChanged(ChangeManager.DOCR_CODE_ADDED, address, endAddr, null, inst);
|
||||||
@@ -1370,29 +1398,48 @@ public class CodeManager implements ErrorHandler, ManagerDB {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the instruction whose min address is less than or equal to the specified address and
|
* Returns an instruction whose min address is less than or equal to the specified address and
|
||||||
* whose max address is greater than or equal to the specified address.
|
* whose max address is greater than or equal to the specified address.
|
||||||
|
* If {@code usePrototypeLength==true}
|
||||||
* <pre>{@literal
|
* <pre>{@literal
|
||||||
* instruction.minAddress() <= addr <= instruction.maxAddress()
|
* instruction.getMinAddress() <= addr <=
|
||||||
|
* instruction.getMinAddress().add(instruction.getPrototype().getLength() - 1)
|
||||||
* }</pre>
|
* }</pre>
|
||||||
*
|
* If {@code usePrototypeLength==false}
|
||||||
|
* <pre>{@literal
|
||||||
|
* instruction.getMinAddress() <= addr <= instruction.getMaxAddress()
|
||||||
|
* }</pre>
|
||||||
|
* The use of the prototype length is required when guarding against memory modifications. If
|
||||||
|
* a length-override is present only one of the entangled instructions will be returned and is
|
||||||
|
* intended to simply indicate the presence of a conflict.
|
||||||
* @param address the address to be contained
|
* @param address the address to be contained
|
||||||
|
* @param usePrototypeLength if actual prototype length should be considered when identifying a
|
||||||
|
* conflict (required when checking for memory modification conflicts), otherwise code unit
|
||||||
|
* length is used. These lengths can vary when a
|
||||||
|
* {@link Instruction#setLengthOverride(int) length-override} is in affect for an instruction.
|
||||||
* @return the instruction containing the specified address, or null if a
|
* @return the instruction containing the specified address, or null if a
|
||||||
* instruction does not exist
|
* instruction does not exist
|
||||||
*/
|
*/
|
||||||
public Instruction getInstructionContaining(Address address) {
|
public Instruction getInstructionContaining(Address address, boolean usePrototypeLength) {
|
||||||
lock.acquire();
|
lock.acquire();
|
||||||
try {
|
try {
|
||||||
Instruction instr = getInstructionAt(address);
|
Instruction instr = getInstructionAt(address);
|
||||||
|
|
||||||
if (instr != null) {
|
if (instr != null) {
|
||||||
return instr;
|
return instr;
|
||||||
}
|
}
|
||||||
|
|
||||||
instr = this.getInstructionBefore(address);
|
instr = this.getInstructionBefore(address);
|
||||||
|
if (instr != null) {
|
||||||
if (instr != null && instr.contains(address)) {
|
Address endAddr;
|
||||||
return instr;
|
if (usePrototypeLength && instr.isLengthOverridden()) {
|
||||||
|
endAddr = instr.getMinAddress().add(instr.getParsedLength() - 1);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
endAddr = instr.getMaxAddress();
|
||||||
|
}
|
||||||
|
if (address.compareTo(endAddr) <= 0) {
|
||||||
|
return instr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -1639,8 +1686,14 @@ public class CodeManager implements ErrorHandler, ManagerDB {
|
|||||||
nextInstEndAddr = nextInstAddr;
|
nextInstEndAddr = nextInstAddr;
|
||||||
int protoID = nextInstRec.getIntValue(InstDBAdapter.PROTO_ID_COL);
|
int protoID = nextInstRec.getIntValue(InstDBAdapter.PROTO_ID_COL);
|
||||||
InstructionPrototype proto = protoMgr.getPrototype(protoID);
|
InstructionPrototype proto = protoMgr.getPrototype(protoID);
|
||||||
int len = proto != null ? proto.getLength()
|
int len;
|
||||||
: nextInstAddr.getAddressSpace().getAddressableUnitSize();
|
if (proto != null) {
|
||||||
|
len = InstructionDB.getLength(proto,
|
||||||
|
nextInstRec.getByteValue(InstDBAdapter.FLAGS_COL));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
len = nextInstAddr.getAddressSpace().getAddressableUnitSize();
|
||||||
|
}
|
||||||
if (len > 1) {
|
if (len > 1) {
|
||||||
try {
|
try {
|
||||||
nextInstEndAddr = nextInstAddr.addNoWrap(len - 1);
|
nextInstEndAddr = nextInstAddr.addNoWrap(len - 1);
|
||||||
@@ -1730,7 +1783,7 @@ public class CodeManager implements ErrorHandler, ManagerDB {
|
|||||||
if (addr != AddressMap.INVALID_ADDRESS_KEY) {
|
if (addr != AddressMap.INVALID_ADDRESS_KEY) {
|
||||||
lock.acquire();
|
lock.acquire();
|
||||||
try {
|
try {
|
||||||
Instruction inst = getInstructionContaining(address);
|
Instruction inst = getInstructionContaining(address, false);
|
||||||
if (inst != null) {
|
if (inst != null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -2538,7 +2591,7 @@ public class CodeManager implements ErrorHandler, ManagerDB {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
boolean fail = false;
|
boolean fail = false;
|
||||||
if (getInstructionContaining(start) != null) {
|
if (getInstructionContaining(start, false) != null) {
|
||||||
fail = true;
|
fail = true;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -2574,7 +2627,7 @@ public class CodeManager implements ErrorHandler, ManagerDB {
|
|||||||
if (!program.getMemory().contains(start, end)) {
|
if (!program.getMemory().contains(start, end)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (getInstructionContaining(start) != null) {
|
if (getInstructionContaining(start, false) != null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (getDefinedDataContaining(start) != null) {
|
if (getDefinedDataContaining(start) != null) {
|
||||||
@@ -3168,13 +3221,21 @@ public class CodeManager implements ErrorHandler, ManagerDB {
|
|||||||
* @param newFallThroughRef new fallthrough reference or null if removed
|
* @param newFallThroughRef new fallthrough reference or null if removed
|
||||||
*/
|
*/
|
||||||
public void fallThroughChanged(Address fromAddr, Reference newFallThroughRef) {
|
public void fallThroughChanged(Address fromAddr, Reference newFallThroughRef) {
|
||||||
|
if (newFallThroughRef != null &&
|
||||||
|
newFallThroughRef.getReferenceType() != RefType.FALL_THROUGH) {
|
||||||
|
throw new IllegalArgumentException("invalid reftype");
|
||||||
|
}
|
||||||
lock.acquire();
|
lock.acquire();
|
||||||
try {
|
try {
|
||||||
InstructionDB instr = getInstructionAt(addrMap.getKey(fromAddr, false));
|
InstructionDB instr = getInstructionAt(addrMap.getKey(fromAddr, false));
|
||||||
// TODO: Should prevent this if instruction is null or isInDelaySlot
|
if (instr == null) {
|
||||||
if (instr != null) {
|
// Do not allow fallthrough ref without instruction
|
||||||
instr.fallThroughChanged(newFallThroughRef);
|
if (newFallThroughRef != null) {
|
||||||
|
refManager.delete(newFallThroughRef);
|
||||||
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
instr.fallThroughChanged(newFallThroughRef);
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
lock.release();
|
lock.release();
|
||||||
|
|||||||
+2
-8
@@ -23,6 +23,7 @@ import org.apache.commons.lang3.StringUtils;
|
|||||||
|
|
||||||
import db.DBRecord;
|
import db.DBRecord;
|
||||||
import ghidra.program.database.*;
|
import ghidra.program.database.*;
|
||||||
|
import ghidra.program.database.references.ReferenceDBManager;
|
||||||
import ghidra.program.model.address.Address;
|
import ghidra.program.model.address.Address;
|
||||||
import ghidra.program.model.address.AddressOutOfBoundsException;
|
import ghidra.program.model.address.AddressOutOfBoundsException;
|
||||||
import ghidra.program.model.lang.*;
|
import ghidra.program.model.lang.*;
|
||||||
@@ -48,7 +49,7 @@ abstract class CodeUnitDB extends DatabaseObject implements CodeUnit, ProcessorC
|
|||||||
protected long addr;
|
protected long addr;
|
||||||
protected Address endAddr;
|
protected Address endAddr;
|
||||||
protected int length;
|
protected int length;
|
||||||
protected ReferenceManager refMgr;
|
protected ReferenceDBManager refMgr;
|
||||||
protected ProgramDB program;
|
protected ProgramDB program;
|
||||||
|
|
||||||
private DBRecord commentRec;
|
private DBRecord commentRec;
|
||||||
@@ -407,13 +408,6 @@ abstract class CodeUnitDB extends DatabaseObject implements CodeUnit, ProcessorC
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isSuccessor(CodeUnit codeUnit) {
|
|
||||||
Address min = codeUnit.getMinAddress();
|
|
||||||
|
|
||||||
return this.getMaxAddress().isSuccessor(min);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Iterator<String> propertyNames() {
|
public Iterator<String> propertyNames() {
|
||||||
PropertyMapManager upm = codeMgr.getPropertyMapManager();
|
PropertyMapManager upm = codeMgr.getPropertyMapManager();
|
||||||
|
|||||||
+213
-32
@@ -15,8 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package ghidra.program.database.code;
|
package ghidra.program.database.code;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import db.DBRecord;
|
import db.DBRecord;
|
||||||
import ghidra.program.database.DBObjectCache;
|
import ghidra.program.database.DBObjectCache;
|
||||||
@@ -29,6 +28,7 @@ import ghidra.program.model.mem.MemoryAccessException;
|
|||||||
import ghidra.program.model.pcode.PcodeOp;
|
import ghidra.program.model.pcode.PcodeOp;
|
||||||
import ghidra.program.model.scalar.Scalar;
|
import ghidra.program.model.scalar.Scalar;
|
||||||
import ghidra.program.model.symbol.*;
|
import ghidra.program.model.symbol.*;
|
||||||
|
import ghidra.program.model.util.CodeUnitInsertionException;
|
||||||
import ghidra.program.util.ChangeManager;
|
import ghidra.program.util.ChangeManager;
|
||||||
import ghidra.util.Msg;
|
import ghidra.util.Msg;
|
||||||
import ghidra.util.exception.NoValueException;
|
import ghidra.util.exception.NoValueException;
|
||||||
@@ -38,16 +38,21 @@ import ghidra.util.exception.NoValueException;
|
|||||||
*/
|
*/
|
||||||
public class InstructionDB extends CodeUnitDB implements Instruction, InstructionContext {
|
public class InstructionDB extends CodeUnitDB implements Instruction, InstructionContext {
|
||||||
|
|
||||||
private static byte FALLTHROUGH_SET_MASK = (byte) 0x01;
|
private static final byte FALLTHROUGH_SET_MASK = 0x01;
|
||||||
private static byte FALLTHROUGH_CLEAR_MASK = (byte) 0xfe;
|
private static final byte FALLTHROUGH_CLEAR_MASK = ~FALLTHROUGH_SET_MASK;
|
||||||
|
|
||||||
private static byte FLOWOVERRIDE_MASK = (byte) 0x0e;
|
private static final byte FLOW_OVERRIDE_SET_MASK = 0x0e;
|
||||||
private static byte FLOWOVERRIDE_CLEAR_MASK = (byte) 0xf1;
|
private static final byte FLOW_OVERRIDE_CLEAR_MASK = ~FLOW_OVERRIDE_SET_MASK;
|
||||||
private static int FLOWOVERRIDE_SHIFT = 1;
|
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;
|
||||||
|
|
||||||
private InstructionPrototype proto;
|
private InstructionPrototype proto;
|
||||||
private byte flags;
|
private byte flags;
|
||||||
private FlowOverride flowOverride;
|
private FlowOverride flowOverride;
|
||||||
|
private int lengthOverride;
|
||||||
private final static Address[] EMPTY_ADDR_ARRAY = new Address[0];
|
private final static Address[] EMPTY_ADDR_ARRAY = new Address[0];
|
||||||
private volatile boolean clearingFallThroughs = false;
|
private volatile boolean clearingFallThroughs = false;
|
||||||
|
|
||||||
@@ -68,7 +73,8 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio
|
|||||||
this.proto = proto;
|
this.proto = proto;
|
||||||
this.flags = flags;
|
this.flags = flags;
|
||||||
flowOverride =
|
flowOverride =
|
||||||
FlowOverride.getFlowOverride((flags & FLOWOVERRIDE_MASK) >> FLOWOVERRIDE_SHIFT);
|
FlowOverride.getFlowOverride((flags & FLOW_OVERRIDE_SET_MASK) >> FLOW_OVERRIDE_SHIFT);
|
||||||
|
refreshLength();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -83,6 +89,36 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio
|
|||||||
return proto.hasDelaySlots() ? (length * 2) : length;
|
return proto.hasDelaySlots() ? (length * 2) : length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void refreshLength() {
|
||||||
|
length = proto.getLength();
|
||||||
|
lengthOverride = (flags & LENGTH_OVERRIDE_SET_MASK) >> LENGTH_OVERRIDE_SHIFT;
|
||||||
|
if (lengthOverride != 0 && lengthOverride < length) {
|
||||||
|
length = lengthOverride;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
lengthOverride = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the instruction code unit length based upon its prototype and flags
|
||||||
|
* which will be used to check for a length-override condition.
|
||||||
|
* @param proto instruction prototype
|
||||||
|
* @param flags instruction flags
|
||||||
|
* @return instruction code unit length
|
||||||
|
*/
|
||||||
|
static int getLength(InstructionPrototype proto, byte flags) {
|
||||||
|
int length = proto.getLength();
|
||||||
|
int lengthOverride = (flags & LENGTH_OVERRIDE_SET_MASK) >> LENGTH_OVERRIDE_SHIFT;
|
||||||
|
if (lengthOverride != 0 && lengthOverride < length) {
|
||||||
|
length = lengthOverride;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
lengthOverride = 0;
|
||||||
|
}
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean hasBeenDeleted(DBRecord rec) {
|
protected boolean hasBeenDeleted(DBRecord rec) {
|
||||||
if (rec == null) {
|
if (rec == null) {
|
||||||
@@ -106,10 +142,11 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio
|
|||||||
if (!newProto.equals(proto)) {
|
if (!newProto.equals(proto)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
length = proto.getLength();
|
|
||||||
flags = rec.getByteValue(InstDBAdapter.FLAGS_COL);
|
flags = rec.getByteValue(InstDBAdapter.FLAGS_COL);
|
||||||
flowOverride =
|
flowOverride =
|
||||||
FlowOverride.getFlowOverride((flags & FLOWOVERRIDE_MASK) >> FLOWOVERRIDE_SHIFT);
|
FlowOverride.getFlowOverride((flags & FLOW_OVERRIDE_SET_MASK) >> FLOW_OVERRIDE_SHIFT);
|
||||||
|
refreshLength();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -142,6 +179,31 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getParsedLength() {
|
||||||
|
return isLengthOverridden() ? proto.getLength() : getLength();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] getParsedBytes() throws MemoryAccessException {
|
||||||
|
if (!isLengthOverridden()) {
|
||||||
|
return getBytes();
|
||||||
|
}
|
||||||
|
lock.acquire();
|
||||||
|
try {
|
||||||
|
checkIsValid();
|
||||||
|
int len = proto.getLength();
|
||||||
|
byte[] b = new byte[len];
|
||||||
|
if (len != getMemory().getBytes(address, b)) {
|
||||||
|
throw new MemoryAccessException("Failed to read " + len + " bytes at " + address);
|
||||||
|
}
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
lock.release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Address getFallFrom() {
|
public Address getFallFrom() {
|
||||||
lock.acquire();
|
lock.acquire();
|
||||||
@@ -192,19 +254,22 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Address getFallThroughReference() {
|
||||||
|
for (Reference ref : refMgr.getReferencesFrom(address)) {
|
||||||
|
if (ref.getReferenceType().isFallthrough() && ref.getToAddress().isMemoryAddress()) {
|
||||||
|
return ref.getToAddress();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Address getFallThrough() {
|
public Address getFallThrough() {
|
||||||
lock.acquire();
|
lock.acquire();
|
||||||
try {
|
try {
|
||||||
checkIsValid();
|
checkIsValid();
|
||||||
if (isFallThroughOverridden()) {
|
if (isFallThroughOverridden()) {
|
||||||
for (Reference ref : refMgr.getReferencesFrom(address)) {
|
return getFallThroughReference();
|
||||||
if (ref.getReferenceType().isFallthrough() &&
|
|
||||||
ref.getToAddress().isMemoryAddress()) {
|
|
||||||
return ref.getToAddress();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
return getDefaultFallThrough();
|
return getDefaultFallThrough();
|
||||||
}
|
}
|
||||||
@@ -221,7 +286,7 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio
|
|||||||
return EMPTY_ADDR_ARRAY;
|
return EMPTY_ADDR_ARRAY;
|
||||||
}
|
}
|
||||||
|
|
||||||
ArrayList<Address> list = new ArrayList<>();
|
Set<Address> list = new HashSet<>();
|
||||||
for (Reference ref : refs) {
|
for (Reference ref : refs) {
|
||||||
if (!ref.getReferenceType().isIndirect()) {
|
if (!ref.getReferenceType().isIndirect()) {
|
||||||
list.add(ref.getToAddress());
|
list.add(ref.getToAddress());
|
||||||
@@ -232,8 +297,7 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio
|
|||||||
return EMPTY_ADDR_ARRAY;
|
return EMPTY_ADDR_ARRAY;
|
||||||
}
|
}
|
||||||
|
|
||||||
Address[] addrs = new Address[list.size()];
|
return list.toArray(new Address[list.size()]);
|
||||||
return list.toArray(addrs);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -537,8 +601,8 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio
|
|||||||
}
|
}
|
||||||
FlowType origFlowType = getFlowType();
|
FlowType origFlowType = getFlowType();
|
||||||
|
|
||||||
flags &= FLOWOVERRIDE_CLEAR_MASK;
|
flags &= FLOW_OVERRIDE_CLEAR_MASK;
|
||||||
flags |= (flow.ordinal() << FLOWOVERRIDE_SHIFT);
|
flags |= (flow.ordinal() << FLOW_OVERRIDE_SHIFT);
|
||||||
codeMgr.setFlags(addr, flags);
|
codeMgr.setFlags(addr, flags);
|
||||||
flowOverride = flow;
|
flowOverride = flow;
|
||||||
|
|
||||||
@@ -623,19 +687,25 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Clear all existing fall-through references from address.
|
* Clear all existing fall-through references from address.
|
||||||
* @param keepFallThroughRef if not null, corresponding fall-through reference will be preserved
|
* @param keepFallThroughAddr if not null, corresponding fall-through reference will be
|
||||||
|
* preserved.
|
||||||
*/
|
*/
|
||||||
private void clearFallThroughRefs(Reference keepFallThroughRef) {
|
private void clearFallThroughRefs(Address keepFallThroughAddr) {
|
||||||
if (clearingFallThroughs) {
|
if (clearingFallThroughs) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
refreshIfNeeded();
|
refreshIfNeeded();
|
||||||
clearingFallThroughs = true;
|
clearingFallThroughs = true;
|
||||||
try {
|
try {
|
||||||
|
boolean fallThroughPreserved = false;
|
||||||
for (Reference ref : refMgr.getReferencesFrom(address)) {
|
for (Reference ref : refMgr.getReferencesFrom(address)) {
|
||||||
if (ref.getReferenceType() == RefType.FALL_THROUGH &&
|
if (ref.getReferenceType() == RefType.FALL_THROUGH) {
|
||||||
!ref.equals(keepFallThroughRef)) {
|
if (!fallThroughPreserved && ref.getToAddress().equals(keepFallThroughAddr)) {
|
||||||
refMgr.delete(ref);
|
fallThroughPreserved = true; // only preserve one
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
refMgr.delete(ref);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -646,9 +716,16 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio
|
|||||||
|
|
||||||
void fallThroughChanged(Reference fallThroughRef) {
|
void fallThroughChanged(Reference fallThroughRef) {
|
||||||
if (!clearingFallThroughs) {
|
if (!clearingFallThroughs) {
|
||||||
clearFallThroughRefs(fallThroughRef);
|
Address fallThroughAddr = fallThroughRef != null ? fallThroughRef.getToAddress() : null;
|
||||||
setFallthroughOverride(fallThroughRef != null &&
|
clearFallThroughRefs(fallThroughAddr); // ensure there is only one fallthrough ref
|
||||||
fallThroughRef.getReferenceType() == RefType.FALL_THROUGH);
|
if (fallThroughAddr == null) { // fallthrough ref removed
|
||||||
|
setFallthroughOverride(false);
|
||||||
|
addLengthOverrideFallthroughRef(); // restore length-override fallthrough if needed
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// enable fallthrough-override if fallThroughRef does not match length-override fallthrough
|
||||||
|
setFallthroughOverride(!fallThroughAddr.equals(getLengthOverrideFallThrough()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -661,8 +738,9 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio
|
|||||||
flags &= FALLTHROUGH_CLEAR_MASK;
|
flags &= FALLTHROUGH_CLEAR_MASK;
|
||||||
}
|
}
|
||||||
codeMgr.setFlags(addr, flags);
|
codeMgr.setFlags(addr, flags);
|
||||||
|
program.setChanged(ChangeManager.DOCR_FALLTHROUGH_CHANGED, address, address, null,
|
||||||
|
null);
|
||||||
}
|
}
|
||||||
program.setChanged(ChangeManager.DOCR_FALLTHROUGH_CHANGED, address, address, null, null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -673,8 +751,10 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio
|
|||||||
if (!isFallThroughOverridden()) {
|
if (!isFallThroughOverridden()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// clear fall-through override
|
||||||
clearFallThroughRefs(null);
|
clearFallThroughRefs(null);
|
||||||
setFallthroughOverride(false);
|
setFallthroughOverride(false);
|
||||||
|
addLengthOverrideFallthroughRef(); // restore length-override fallthrough if needed
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
lock.release();
|
lock.release();
|
||||||
@@ -693,11 +773,12 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (fallThroughAddr == null) {
|
if (fallThroughAddr == null) {
|
||||||
// Fall-through eliminated - no reference added
|
// Fall-through eliminated (i.e., terminal flow) - no reference added
|
||||||
clearFallThroughRefs(null);
|
clearFallThroughRefs(null);
|
||||||
setFallthroughOverride(true);
|
setFallthroughOverride(true);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
// Adding fallthough ref will trigger override flag on callback
|
||||||
refMgr.addMemoryReference(address, fallThroughAddr, RefType.FALL_THROUGH,
|
refMgr.addMemoryReference(address, fallThroughAddr, RefType.FALL_THROUGH,
|
||||||
SourceType.USER_DEFINED, Reference.MNEMONIC);
|
SourceType.USER_DEFINED, Reference.MNEMONIC);
|
||||||
}
|
}
|
||||||
@@ -705,7 +786,107 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio
|
|||||||
finally {
|
finally {
|
||||||
lock.release();
|
lock.release();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setLengthOverride(int len) throws CodeUnitInsertionException {
|
||||||
|
lock.acquire();
|
||||||
|
try {
|
||||||
|
checkDeleted();
|
||||||
|
if (doSetLengthOverride(len)) {
|
||||||
|
program.setChanged(ChangeManager.DOCR_LENGTH_OVERRIDE_CHANGED, address, address,
|
||||||
|
null, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
lock.release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check and revise a specified {@code length} to arrive at a suitable length-override value.
|
||||||
|
* @param length instruction byte-length (must be in the range 0..{@code prototype-length}).
|
||||||
|
* 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} .
|
||||||
|
* @param prototype instruction prototype
|
||||||
|
* @return length-override value (0 = disable length-override)
|
||||||
|
* @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.
|
||||||
|
*/
|
||||||
|
public static int checkLengthOverride(int length, InstructionPrototype prototype)
|
||||||
|
throws IllegalArgumentException, CodeUnitInsertionException {
|
||||||
|
if (length < 0) {
|
||||||
|
throw new IllegalArgumentException("Negative length not permitted");
|
||||||
|
}
|
||||||
|
int instrProtoLength = prototype.getLength();
|
||||||
|
if (length == 0 || length == instrProtoLength) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (length > instrProtoLength) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int align = prototype.getLanguage().getInstructionAlignment();
|
||||||
|
if (length % align != 0) {
|
||||||
|
throw new CodeUnitInsertionException(
|
||||||
|
"Length(" + length + ") override must be a multiple of " + align + " bytes");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (length > MAX_LENGTH_OVERRIDE) {
|
||||||
|
throw new CodeUnitInsertionException("Unsupported length override: " + length);
|
||||||
|
}
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean doSetLengthOverride(int len) throws CodeUnitInsertionException {
|
||||||
|
|
||||||
|
int protoLength = proto.getLength();
|
||||||
|
len = checkLengthOverride(len, proto);
|
||||||
|
if (len == lengthOverride) {
|
||||||
|
return false; // no change
|
||||||
|
}
|
||||||
|
|
||||||
|
int instrLength = len != 0 ? len : protoLength;
|
||||||
|
if (instrLength > getLength()) {
|
||||||
|
Address newEndAddr = address.add(instrLength - 1);
|
||||||
|
Address nextCodeUnitAddr = codeMgr.getDefinedAddressAfter(address);
|
||||||
|
if (nextCodeUnitAddr != null && nextCodeUnitAddr.compareTo(newEndAddr) <= 0) {
|
||||||
|
throw new CodeUnitInsertionException("Length override of " + instrLength +
|
||||||
|
" conflicts with code unit at " + nextCodeUnitAddr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
flags &= LENGTH_OVERRIDE_CLEAR_MASK;
|
||||||
|
flags |= (len << LENGTH_OVERRIDE_SHIFT);
|
||||||
|
codeMgr.setFlags(addr, flags);
|
||||||
|
|
||||||
|
endAddr = null;
|
||||||
|
refreshLength();
|
||||||
|
|
||||||
|
addLengthOverrideFallthroughRef();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addLengthOverrideFallthroughRef() {
|
||||||
|
if (isLengthOverridden() && !isFallThroughOverridden()) {
|
||||||
|
// length-override always uses default fall-through address
|
||||||
|
refMgr.addMemoryReference(address, getDefaultFallThrough(), RefType.FALL_THROUGH,
|
||||||
|
SourceType.USER_DEFINED, Reference.MNEMONIC);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isLengthOverridden() {
|
||||||
|
refreshIfNeeded();
|
||||||
|
return lengthOverride != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Address getLengthOverrideFallThrough() {
|
||||||
|
return isLengthOverridden() ? getDefaultFallThrough() : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean addrsEqual(Address addr1, Address addr2) {
|
private boolean addrsEqual(Address addr1, Address addr2) {
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user