mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-05-13 13:39:07 +08:00
GP-3256 Added support for Instruction length-override
This commit is contained in:
+7
-7
@@ -131,7 +131,8 @@ public class DebuggerCopyPlan {
|
||||
}
|
||||
long off = ins.getMinAddress().subtract(fromRange.getMinAddress());
|
||||
Address dest = intoAddress.add(off);
|
||||
intoListing.createInstruction(dest, ins.getPrototype(), ins, ins);
|
||||
intoListing.createInstruction(dest, ins.getPrototype(), ins, ins,
|
||||
ins.getLength());
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -143,8 +144,7 @@ public class DebuggerCopyPlan {
|
||||
|
||||
@Override
|
||||
public void copy(TraceProgramView from, AddressRange fromRange, Program into,
|
||||
Address intoAddress, TaskMonitor monitor)
|
||||
throws Exception {
|
||||
Address intoAddress, TaskMonitor monitor) throws Exception {
|
||||
Listing intoListing = into.getListing();
|
||||
for (Data data : from.getListing()
|
||||
.getDefinedData(new AddressSet(fromRange), true)) {
|
||||
@@ -204,8 +204,8 @@ public class DebuggerCopyPlan {
|
||||
}
|
||||
}
|
||||
|
||||
private Namespace findOrCopyNamespace(Namespace ns, SymbolTable intoTable,
|
||||
Program into) throws Exception {
|
||||
private Namespace findOrCopyNamespace(Namespace ns, SymbolTable intoTable, Program into)
|
||||
throws Exception {
|
||||
if (ns.isGlobal()) {
|
||||
return into.getGlobalNamespace();
|
||||
}
|
||||
@@ -442,8 +442,8 @@ public class DebuggerCopyPlan {
|
||||
public boolean isRequiresInitializedMemory(TraceProgramView from, Program dest) {
|
||||
return checkBoxes.entrySet().stream().anyMatch(ent -> {
|
||||
Copier copier = ent.getKey();
|
||||
return copier.isRequiresInitializedMemory() &&
|
||||
copier.isAvailable(from, dest) && ent.getValue().isSelected();
|
||||
return copier.isRequiresInitializedMemory() && copier.isAvailable(from, dest) &&
|
||||
ent.getValue().isSelected();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
+1
-6
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
package ghidra.trace.database.listing;
|
||||
|
||||
import static ghidra.lifecycle.Unfinished.TODO;
|
||||
import static ghidra.lifecycle.Unfinished.*;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.*;
|
||||
@@ -278,11 +278,6 @@ public interface DBTraceCodeUnitAdapter extends TraceCodeUnit, MemBufferMixin {
|
||||
return DBTraceCommentAdapter.arrayFromComment(getComment(commentType));
|
||||
}
|
||||
|
||||
@Override
|
||||
default boolean isSuccessor(CodeUnit codeUnit) {
|
||||
return getMaxAddress().isSuccessor(codeUnit.getMinAddress());
|
||||
}
|
||||
|
||||
@Override
|
||||
default boolean contains(Address testAddr) {
|
||||
return getMinAddress().compareTo(testAddr) <= 0 && testAddr.compareTo(getMaxAddress()) <= 0;
|
||||
|
||||
+132
-23
@@ -21,12 +21,14 @@ import java.nio.ByteBuffer;
|
||||
import java.util.*;
|
||||
|
||||
import db.DBRecord;
|
||||
import ghidra.program.database.code.InstructionDB;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.lang.*;
|
||||
import ghidra.program.model.listing.ContextChangeException;
|
||||
import ghidra.program.model.listing.FlowOverride;
|
||||
import ghidra.program.model.mem.*;
|
||||
import ghidra.program.model.symbol.*;
|
||||
import ghidra.program.model.util.CodeUnitInsertionException;
|
||||
import ghidra.trace.database.DBTrace;
|
||||
import ghidra.trace.database.DBTraceUtils;
|
||||
import ghidra.trace.database.context.DBTraceRegisterContextManager;
|
||||
@@ -36,7 +38,7 @@ import ghidra.trace.database.guest.InternalTracePlatform;
|
||||
import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree;
|
||||
import ghidra.trace.database.symbol.DBTraceReference;
|
||||
import ghidra.trace.database.symbol.DBTraceReferenceSpace;
|
||||
import ghidra.trace.model.Lifespan;
|
||||
import ghidra.trace.model.*;
|
||||
import ghidra.trace.model.Trace.TraceInstructionChangeType;
|
||||
import ghidra.trace.model.guest.TracePlatform;
|
||||
import ghidra.trace.model.listing.TraceInstruction;
|
||||
@@ -52,17 +54,21 @@ import ghidra.util.database.annot.*;
|
||||
* The implementation of {@link TraceInstruction} for {@link DBTrace}
|
||||
*/
|
||||
@DBAnnotatedObjectInfo(version = 0)
|
||||
public class DBTraceInstruction extends AbstractDBTraceCodeUnit<DBTraceInstruction> implements
|
||||
TraceInstruction, InstructionAdapterFromPrototype, InstructionContext {
|
||||
public class DBTraceInstruction extends AbstractDBTraceCodeUnit<DBTraceInstruction>
|
||||
implements TraceInstruction, InstructionAdapterFromPrototype, InstructionContext {
|
||||
private static final Address[] EMPTY_ADDRESS_ARRAY = new Address[] {};
|
||||
private static final String TABLE_NAME = "Instructions";
|
||||
|
||||
private static final byte FALLTHROUGH_SET_MASK = 0x01;
|
||||
private static final byte FALLTHROUGH_CLEAR_MASK = ~FALLTHROUGH_SET_MASK;
|
||||
|
||||
private static final byte FLOWOVERRIDE_SET_MASK = 0x0e;
|
||||
private static final byte FLOWOVERRIDE_CLEAR_MASK = ~FLOWOVERRIDE_SET_MASK;
|
||||
private static final int FLOWOVERRIDE_SHIFT = 1;
|
||||
private static final byte FLOW_OVERRIDE_SET_MASK = 0x0e;
|
||||
private static final byte FLOW_OVERRIDE_CLEAR_MASK = ~FLOW_OVERRIDE_SET_MASK;
|
||||
private static final int FLOW_OVERRIDE_SHIFT = 1;
|
||||
|
||||
private static final byte LENGTH_OVERRIDE_SET_MASK = 0x70;
|
||||
private static final byte LENGTH_OVERRIDE_CLEAR_MASK = ~LENGTH_OVERRIDE_SET_MASK;
|
||||
private static final int LENGTH_OVERRIDE_SHIFT = 4;
|
||||
|
||||
static final String PLATFORM_COLUMN_NAME = "Platform";
|
||||
static final String PROTOTYPE_COLUMN_NAME = "Prototype";
|
||||
@@ -143,6 +149,7 @@ public class DBTraceInstruction extends AbstractDBTraceCodeUnit<DBTraceInstructi
|
||||
|
||||
protected InstructionPrototype prototype;
|
||||
protected FlowOverride flowOverride;
|
||||
protected int lengthOverride;
|
||||
|
||||
protected ParserContext parserContext;
|
||||
protected InternalTracePlatform platform;
|
||||
@@ -186,18 +193,24 @@ public class DBTraceInstruction extends AbstractDBTraceCodeUnit<DBTraceInstructi
|
||||
* @param platform the platform
|
||||
* @param prototype the instruction prototype
|
||||
* @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,
|
||||
ProcessorContextView context) {
|
||||
ProcessorContextView context, int forcedLengthOverride) {
|
||||
this.platformKey = platform.getIntKey();
|
||||
// NOTE: Using "this" for the MemBuffer seems a bit precarious.
|
||||
DBTraceGuestLanguage languageEntry = platform == null ? null : platform.getLanguageEntry();
|
||||
this.prototypeKey = (int) space.manager
|
||||
.findOrRecordPrototype(prototype, languageEntry, this, context)
|
||||
.getKey();
|
||||
DBTraceGuestLanguage languageEntry = platform.getLanguageEntry();
|
||||
this.prototypeKey =
|
||||
(int) space.manager.findOrRecordPrototype(prototype, languageEntry, this, context)
|
||||
.getKey();
|
||||
this.flowOverride = FlowOverride.NONE; // flags field is already consistent
|
||||
this.lengthOverride = 0;
|
||||
update(PLATFORM_COLUMN, PROTOTYPE_COLUMN, FLAGS_COLUMN);
|
||||
|
||||
if (forcedLengthOverride != 0) {
|
||||
updateLengthOverride(forcedLengthOverride);
|
||||
}
|
||||
|
||||
// TODO: Can there be more in this context than the context register???
|
||||
doSetPlatformMapping(platform);
|
||||
this.prototype = prototype;
|
||||
@@ -216,12 +229,19 @@ public class DBTraceInstruction extends AbstractDBTraceCodeUnit<DBTraceInstructi
|
||||
}
|
||||
prototype = space.manager.getPrototypeByKey(prototypeKey);
|
||||
if (prototype == null) {
|
||||
Msg.error(this,
|
||||
"Instruction table is corrupt for address " + getMinAddress() +
|
||||
". Missing prototype " + prototypeKey);
|
||||
Msg.error(this, "Instruction table is corrupt for address " + getMinAddress() +
|
||||
". Missing prototype " + prototypeKey);
|
||||
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);
|
||||
}
|
||||
@@ -334,8 +354,7 @@ public class DBTraceInstruction extends AbstractDBTraceCodeUnit<DBTraceInstructi
|
||||
if (flowType.hasFallthrough()) {
|
||||
try {
|
||||
return instructionContext.getAddress()
|
||||
.addNoWrap(
|
||||
prototype.getFallThroughOffset(instructionContext));
|
||||
.addNoWrap(prototype.getFallThroughOffset(instructionContext));
|
||||
}
|
||||
catch (AddressOverflowException e) {
|
||||
return null;
|
||||
@@ -364,6 +383,9 @@ public class DBTraceInstruction extends AbstractDBTraceCodeUnit<DBTraceInstructi
|
||||
}
|
||||
return null;
|
||||
}
|
||||
if (lengthOverride != 0 && getFlowType().hasFallthrough()) {
|
||||
return getMinAddress().add(lengthOverride);
|
||||
}
|
||||
return getDefaultFallThrough();
|
||||
}
|
||||
}
|
||||
@@ -433,6 +455,10 @@ public class DBTraceInstruction extends AbstractDBTraceCodeUnit<DBTraceInstructi
|
||||
return EMPTY_ADDRESS_ARRAY;
|
||||
}
|
||||
|
||||
if (lengthOverride != 0 && getFlowType().hasFallthrough()) {
|
||||
list.add(getMinAddress().add(lengthOverride));
|
||||
}
|
||||
|
||||
return list.toArray(new Address[list.size()]);
|
||||
}
|
||||
}
|
||||
@@ -526,8 +552,8 @@ public class DBTraceInstruction extends AbstractDBTraceCodeUnit<DBTraceInstructi
|
||||
}
|
||||
FlowType origFlowType = getFlowType();
|
||||
|
||||
flags &= FLOWOVERRIDE_CLEAR_MASK;
|
||||
flags |= (flowOverride.ordinal() << FLOWOVERRIDE_SHIFT) & FLOWOVERRIDE_SET_MASK;
|
||||
flags &= FLOW_OVERRIDE_CLEAR_MASK;
|
||||
flags |= (flowOverride.ordinal() << FLOW_OVERRIDE_SHIFT) & FLOW_OVERRIDE_SET_MASK;
|
||||
this.flowOverride = flowOverride;
|
||||
update(FLAGS_COLUMN);
|
||||
|
||||
@@ -545,8 +571,91 @@ public class DBTraceInstruction extends AbstractDBTraceCodeUnit<DBTraceInstructi
|
||||
}
|
||||
}
|
||||
space.trace.setChanged(
|
||||
new TraceChangeRecord<>(TraceInstructionChangeType.FLOW_OVERRIDE_CHANGED,
|
||||
space, this, oldFlowOverride, flowOverride));
|
||||
new TraceChangeRecord<>(TraceInstructionChangeType.FLOW_OVERRIDE_CHANGED, space, this,
|
||||
oldFlowOverride, flowOverride));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLengthOverride(int length) throws CodeUnitInsertionException {
|
||||
int oldLengthOverride = this.lengthOverride;
|
||||
try (LockHold hold = space.trace.lockWrite()) {
|
||||
checkDeleted();
|
||||
InstructionPrototype proto = getPrototype();
|
||||
length = InstructionDB.checkLengthOverride(length, proto);
|
||||
if (length == lengthOverride) {
|
||||
return; // no change
|
||||
}
|
||||
|
||||
int newLength = length != 0 ? length : proto.getLength();
|
||||
if (newLength > getLength()) {
|
||||
Address minAddr = getMinAddress();
|
||||
Address newEndAddr = minAddr.add(newLength - 1);
|
||||
TraceAddressSnapRange tasr = new ImmutableTraceAddressSnapRange(
|
||||
new AddressRangeImpl(minAddr.next(), newEndAddr), getLifespan());
|
||||
for (AbstractDBTraceCodeUnit<?> cu : space.definedUnits.getIntersecting(tasr)) {
|
||||
if (cu != this) {
|
||||
throw new CodeUnitInsertionException(
|
||||
"Length override of " + newLength + " conflicts with code unit at " +
|
||||
cu.getMinAddress() + ", lifespan=" + cu.getLifespan());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updateLengthOverride(length);
|
||||
}
|
||||
space.trace.setChanged(
|
||||
new TraceChangeRecord<>(TraceInstructionChangeType.LENGTH_OVERRIDE_CHANGED, space, this,
|
||||
oldLengthOverride, length));
|
||||
}
|
||||
|
||||
private void updateLengthOverride(int length) {
|
||||
flags &= LENGTH_OVERRIDE_CLEAR_MASK;
|
||||
flags |= (length << LENGTH_OVERRIDE_SHIFT);
|
||||
lengthOverride = length;
|
||||
update(FLAGS_COLUMN);
|
||||
|
||||
int newLength = length != 0 ? length : getPrototype().getLength();
|
||||
Address minAddr = getMinAddress();
|
||||
Address newEndAddr = minAddr.add(newLength - 1);
|
||||
doSetRange(new AddressRangeImpl(minAddr, newEndAddr));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLengthOverridden() {
|
||||
return lengthOverride != 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLength() {
|
||||
if (lengthOverride != 0) {
|
||||
return lengthOverride;
|
||||
}
|
||||
return super.getLength();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getParsedLength() {
|
||||
if (lengthOverride == 0) {
|
||||
return super.getLength();
|
||||
}
|
||||
return getPrototype().getLength();
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getParsedBytes() throws MemoryAccessException {
|
||||
if (!isLengthOverridden()) {
|
||||
return getBytes();
|
||||
}
|
||||
try (LockHold hold = LockHold.lock(space.lock.readLock())) {
|
||||
checkIsValid();
|
||||
int len = getPrototype().getLength();
|
||||
byte[] b = new byte[len];
|
||||
Address addr = getAddress();
|
||||
if (len != getMemory().getBytes(addr, b)) {
|
||||
throw new MemoryAccessException("Failed to read " + len + " bytes at " + addr);
|
||||
}
|
||||
return b;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -622,8 +731,8 @@ public class DBTraceInstruction extends AbstractDBTraceCodeUnit<DBTraceInstructi
|
||||
}
|
||||
update(FLAGS_COLUMN);
|
||||
space.trace.setChanged(
|
||||
new TraceChangeRecord<>(TraceInstructionChangeType.FALL_THROUGH_OVERRIDE_CHANGED,
|
||||
space, this, !overridden, overridden));
|
||||
new TraceChangeRecord<>(TraceInstructionChangeType.FALL_THROUGH_OVERRIDE_CHANGED, space,
|
||||
this, !overridden, overridden));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+3
-2
@@ -59,9 +59,10 @@ public class DBTraceInstructionsMemoryView
|
||||
@Override
|
||||
public DBTraceInstruction create(Lifespan lifespan, Address address,
|
||||
TracePlatform platform, InstructionPrototype prototype,
|
||||
ProcessorContextView context) throws CodeUnitInsertionException {
|
||||
ProcessorContextView context, int forcedLengthOverride)
|
||||
throws CodeUnitInsertionException {
|
||||
return delegateWrite(address.getAddressSpace(),
|
||||
m -> m.create(lifespan, address, platform, prototype, context));
|
||||
m -> m.create(lifespan, address, platform, prototype, context, forcedLengthOverride));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+34
-17
@@ -19,6 +19,7 @@ import java.util.*;
|
||||
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import ghidra.program.database.code.InstructionDB;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.lang.*;
|
||||
import ghidra.program.model.lang.InstructionError.InstructionErrorType;
|
||||
@@ -103,16 +104,22 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView
|
||||
protected boolean isSuitable(Instruction candidate, Instruction protoInstr) {
|
||||
try {
|
||||
return candidate.getPrototype().equals(protoInstr.getPrototype()) &&
|
||||
Arrays.equals(candidate.getBytes(), protoInstr.getBytes()) &&
|
||||
candidate.isFallThroughOverridden() == protoInstr.isFallThroughOverridden() &&
|
||||
Objects.equals(candidate.getFallThrough(), protoInstr.getFallThrough()) &&
|
||||
candidate.getFlowOverride() == protoInstr.getFlowOverride();
|
||||
candidate.getFlowOverride() == protoInstr.getFlowOverride() &&
|
||||
candidate.getLength() == protoInstr.getLength() && // handles potential length override
|
||||
hasSameBytes(candidate, protoInstr);
|
||||
}
|
||||
catch (MemoryAccessException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean hasSameBytes(Instruction instr1, Instruction instr2)
|
||||
throws MemoryAccessException {
|
||||
return Arrays.equals(instr1.getParsedBytes(), instr2.getParsedBytes());
|
||||
}
|
||||
|
||||
protected Instruction doAdjustExisting(Address address, Instruction protoInstr)
|
||||
throws AddressOverflowException, CancelledException, CodeUnitInsertionException {
|
||||
DBTraceInstruction exists = getAt(lifespan.lmin(), address);
|
||||
@@ -193,8 +200,8 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView
|
||||
return prec;
|
||||
}
|
||||
|
||||
Instruction created =
|
||||
doCreate(lifespan, address, platform, protoInstr.getPrototype(), protoInstr);
|
||||
Instruction created = doCreate(lifespan, address, platform,
|
||||
protoInstr.getPrototype(), protoInstr, protoInstr.getLength());
|
||||
// copy override settings to replacement instruction
|
||||
if (protoInstr.isFallThroughOverridden()) {
|
||||
created.setFallThrough(protoInstr.getFallThrough());
|
||||
@@ -336,20 +343,30 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView
|
||||
* @param platform the platform (language, compiler) for the instruction
|
||||
* @param prototype the instruction's prototype
|
||||
* @param context the initial context for parsing the instruction
|
||||
* @return the new instructions
|
||||
* @throws CodeUnitInsertionException if the instruction cannot be created due to an existing
|
||||
* unit
|
||||
* @param length instruction byte-length (must be in the range 0..prototype.getLength()).
|
||||
* If smaller than the prototype length it must have a value no greater than 7, otherwise
|
||||
* an error will be thrown. A value of 0 or greater-than-or-equal the prototype length
|
||||
* will be ignored and not impose and override length. The length value must be a multiple
|
||||
* of the {@link Language#getInstructionAlignment() instruction alignment} .
|
||||
* @return the newly created instruction.
|
||||
* @throws CodeUnitInsertionException thrown if the new Instruction would overlap and
|
||||
* existing {@link CodeUnit} or the specified {@code length} is unsupported.
|
||||
* @throws IllegalArgumentException if a negative {@code length} is specified.
|
||||
* @throws AddressOverflowException if the instruction would fall off the address space
|
||||
*/
|
||||
protected DBTraceInstruction doCreate(Lifespan lifespan, Address address,
|
||||
InternalTracePlatform platform, InstructionPrototype prototype,
|
||||
ProcessorContextView context)
|
||||
ProcessorContextView context, int length)
|
||||
throws CodeUnitInsertionException, AddressOverflowException {
|
||||
if (platform.getLanguage() != prototype.getLanguage()) {
|
||||
throw new IllegalArgumentException("Platform and prototype disagree in language");
|
||||
}
|
||||
|
||||
Address endAddress = address.addNoWrap(prototype.getLength() - 1);
|
||||
int forcedLengthOverride = InstructionDB.checkLengthOverride(length, prototype);
|
||||
if (length == 0) {
|
||||
length = prototype.getLength();
|
||||
}
|
||||
Address endAddress = address.addNoWrap(length - 1);
|
||||
AddressRangeImpl createdRange = new AddressRangeImpl(address, endAddress);
|
||||
|
||||
// Truncate, then check that against existing code units.
|
||||
@@ -365,7 +382,7 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView
|
||||
doSetContext(tasr, prototype.getLanguage(), context);
|
||||
|
||||
DBTraceInstruction created = space.instructionMapSpace.put(tasr, null);
|
||||
created.set(platform, prototype, context);
|
||||
created.set(platform, prototype, context, forcedLengthOverride);
|
||||
|
||||
cacheForContaining.notifyNewEntry(tasr.getLifespan(), createdRange, created);
|
||||
cacheForSequence.notifyNewEntry(tasr.getLifespan(), createdRange, created);
|
||||
@@ -379,14 +396,14 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView
|
||||
|
||||
@Override
|
||||
public DBTraceInstruction create(Lifespan lifespan, Address address, TracePlatform platform,
|
||||
InstructionPrototype prototype, ProcessorContextView context)
|
||||
InstructionPrototype prototype, ProcessorContextView context, int forcedLengthOverride)
|
||||
throws CodeUnitInsertionException {
|
||||
InternalTracePlatform dbPlatform = space.manager.platformManager.assertMine(platform);
|
||||
try (LockHold hold = LockHold.lock(space.lock.writeLock())) {
|
||||
DBTraceInstruction created =
|
||||
doCreate(lifespan, address, dbPlatform, prototype, context);
|
||||
space.trace.setChanged(new TraceChangeRecord<>(TraceCodeChangeType.ADDED,
|
||||
space, created, created));
|
||||
doCreate(lifespan, address, dbPlatform, prototype, context, forcedLengthOverride);
|
||||
space.trace.setChanged(
|
||||
new TraceChangeRecord<>(TraceCodeChangeType.ADDED, space, created, created));
|
||||
return created;
|
||||
}
|
||||
catch (AddressOverflowException e) {
|
||||
@@ -567,9 +584,9 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView
|
||||
if (lastInstruction != null) {
|
||||
Address maxAddress = DBTraceCodeManager.instructionMax(lastInstruction, true);
|
||||
result.addRange(block.getStartAddress(), maxAddress);
|
||||
space.trace.setChanged(new TraceChangeRecord<>(TraceCodeChangeType.ADDED,
|
||||
space, new ImmutableTraceAddressSnapRange(
|
||||
block.getStartAddress(), maxAddress, lifespan)));
|
||||
space.trace.setChanged(new TraceChangeRecord<>(TraceCodeChangeType.ADDED, space,
|
||||
new ImmutableTraceAddressSnapRange(block.getStartAddress(), maxAddress,
|
||||
lifespan)));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
|
||||
+5
-4
@@ -708,18 +708,19 @@ public abstract class AbstractDBTraceProgramViewListing implements TraceProgramV
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("rawtypes")
|
||||
public PropertyMap getPropertyMap(String propertyName) {
|
||||
public PropertyMap<?> getPropertyMap(String propertyName) {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Instruction createInstruction(Address addr, InstructionPrototype prototype,
|
||||
MemBuffer memBuf, ProcessorContextView context) throws CodeUnitInsertionException {
|
||||
MemBuffer memBuf, ProcessorContextView context, int forcedLengthOverride)
|
||||
throws CodeUnitInsertionException {
|
||||
// TODO: Why memBuf? Can it vary from program memory?
|
||||
return codeOperations.instructions()
|
||||
.create(Lifespan.nowOn(program.snap), addr, platform, prototype, context);
|
||||
.create(Lifespan.nowOn(program.snap), addr, platform, prototype, context,
|
||||
forcedLengthOverride);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+13
-1
@@ -129,6 +129,8 @@ public class DBTraceProgramView implements TraceProgramView {
|
||||
listenFor(TraceDataTypeChangeType.RENAMED, this::dataTypeRenamed);
|
||||
listenFor(TraceDataTypeChangeType.DELETED, this::dataTypeDeleted);
|
||||
|
||||
listenFor(TraceInstructionChangeType.LENGTH_OVERRIDE_CHANGED,
|
||||
this::instructionLengthOverrideChanged);
|
||||
listenFor(TraceInstructionChangeType.FLOW_OVERRIDE_CHANGED,
|
||||
this::instructionFlowOverrideChanged);
|
||||
listenFor(TraceInstructionChangeType.FALL_THROUGH_OVERRIDE_CHANGED,
|
||||
@@ -466,9 +468,19 @@ public class DBTraceProgramView implements TraceProgramView {
|
||||
return;
|
||||
}
|
||||
queues.fireEvent(new ProgramChangeRecord(ChangeManager.DOCR_FALLTHROUGH_CHANGED,
|
||||
instruction.getMinAddress(), instruction.getMaxAddress(), null, null, null));
|
||||
instruction.getMinAddress(), instruction.getMinAddress(), null, null, null));
|
||||
}
|
||||
|
||||
private void instructionLengthOverrideChanged(TraceAddressSpace space,
|
||||
TraceInstruction instruction, int oldLengthOverride, int newLengthOverride) {
|
||||
DomainObjectEventQueues queues = isCodeVisible(space, instruction);
|
||||
if (queues == null) {
|
||||
return;
|
||||
}
|
||||
queues.fireEvent(new ProgramChangeRecord(ChangeManager.DOCR_LENGTH_OVERRIDE_CHANGED,
|
||||
instruction.getMinAddress(), instruction.getMinAddress(), null, null, null));
|
||||
}
|
||||
|
||||
private void memoryBytesChanged(TraceAddressSpace space, TraceAddressSnapRange range,
|
||||
byte[] oldIsNull, byte[] bytes) {
|
||||
DomainObjectEventQueues queues = isBytesVisible(space, range);
|
||||
|
||||
@@ -235,6 +235,8 @@ public interface Trace extends DataTypeManagerDomainObject {
|
||||
new TraceInstructionChangeType<>();
|
||||
public static final TraceInstructionChangeType<Boolean> FALL_THROUGH_OVERRIDE_CHANGED =
|
||||
new TraceInstructionChangeType<>();
|
||||
public static final TraceInstructionChangeType<Integer> LENGTH_OVERRIDE_CHANGED =
|
||||
new TraceInstructionChangeType<>();
|
||||
}
|
||||
|
||||
public static final class TraceMemoryBytesChangeType
|
||||
|
||||
+4
-3
@@ -37,11 +37,12 @@ public interface TraceInstructionsView extends TraceBaseDefinedUnitsView<TraceIn
|
||||
* @param platform the platform
|
||||
* @param prototype the instruction prototype
|
||||
* @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
|
||||
* @throws CodeUnitInsertionException if the instruction cannot be created
|
||||
*/
|
||||
TraceInstruction create(Lifespan lifespan, Address address, TracePlatform platform,
|
||||
InstructionPrototype prototype, ProcessorContextView context)
|
||||
InstructionPrototype prototype, ProcessorContextView context, int forcedLengthOverride)
|
||||
throws CodeUnitInsertionException;
|
||||
|
||||
/**
|
||||
@@ -50,10 +51,10 @@ public interface TraceInstructionsView extends TraceBaseDefinedUnitsView<TraceIn
|
||||
* @see #create(Lifespan, Address, TracePlatform, InstructionPrototype, ProcessorContextView)
|
||||
*/
|
||||
default TraceInstruction create(Lifespan lifespan, Address address,
|
||||
InstructionPrototype prototype, ProcessorContextView context)
|
||||
InstructionPrototype prototype, ProcessorContextView context, int forcedLengthOverride)
|
||||
throws CodeUnitInsertionException {
|
||||
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);
|
||||
Instruction pseudoIns = block.iterator().next();
|
||||
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));
|
||||
}
|
||||
|
||||
assertTrue(i4004.isSuccessor(i4006));
|
||||
assertFalse(i4004.isSuccessor(d4008));
|
||||
|
||||
assertFalse(i4004.contains(b.addr(0x4003)));
|
||||
assertTrue(i4004.contains(b.addr(0x4004)));
|
||||
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/images/DisassembledViewPluginMain.png||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/DockingWindows/Docking_Windows.htm||GHIDRA||||END|
|
||||
src/main/help/help/topics/DockingWindows/images/Tool.png||GHIDRA||||END|
|
||||
|
||||
@@ -24,7 +24,15 @@
|
||||
<LI><A href="#Disassemble_Static">Static 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>
|
||||
|
||||
<H2><A name="Disassemble"></A>Disassembly</H2>
|
||||
@@ -131,7 +139,7 @@
|
||||
undefined bytes.</I></P>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<H2><A name="Disassemble_Restricted"></A>Disassembly (Restricted)</H2>
|
||||
<H2><A name="Disassemble_Restricted"></A>Restricted Disassembly</H2>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<P><I>Restricted Disassembly</I> is similar to <I>Disassembly</I>, except that only bytes in
|
||||
@@ -157,13 +165,11 @@
|
||||
undefined bytes.</I></P>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<P class="providedbyplugin"> </P>
|
||||
|
||||
<H2><A name="Disassemble_Arm"></A>Disassemble ARM / Disassemble Thumb<A name=
|
||||
<H2><A name="Disassemble_Arm"></A>Disassemble ARM / Thumb<A name=
|
||||
"Disassemble_Thumb"></A></H2>
|
||||
|
||||
<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
|
||||
mode. The instructions available in ARM mode are 4 bytes long. In Thumb mode, the
|
||||
instructions are "generally" 2 bytes long. ARM and Thumb mode are mutually exclusive,
|
||||
@@ -199,38 +205,6 @@
|
||||
undefined bytes.</I></P>
|
||||
</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>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
@@ -244,11 +218,6 @@
|
||||
<DIV align="center">
|
||||
<IMG src="images/ProcessorOptions.png"><BR>
|
||||
<BR>
|
||||
|
||||
|
||||
<DIV align="left">
|
||||
<BR>
|
||||
</DIV>
|
||||
</DIV>
|
||||
|
||||
<P>The options are
|
||||
@@ -282,8 +251,90 @@
|
||||
|
||||
<LI><A href="help/topics/LanguageProviderPlugin/Languages.htm">Languages</A></LI>
|
||||
</UL>
|
||||
|
||||
<P> </P>
|
||||
<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>
|
||||
</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 =
|
||||
inst.isFallThroughOverridden() && (inst.getFallThrough() == null);
|
||||
boolean hasFlowOverride = inst.getFlowOverride() != FlowOverride.NONE;
|
||||
boolean hasLengthOverride = inst.isLengthOverridden();
|
||||
cuRep = cu.toString();
|
||||
if (removedFallThrough) {
|
||||
cuRep +=
|
||||
NEW_LINE + indent + getSpaces(addrRangeStr.length()) + " " +
|
||||
"Removed FallThrough";
|
||||
cuRep += NEW_LINE + indent + getSpaces(addrRangeStr.length()) + " " +
|
||||
"Removed FallThrough";
|
||||
}
|
||||
else if (inst.isFallThroughOverridden()) {
|
||||
Reference[] refs = cu.getReferencesFrom();
|
||||
// Show the fallthrough override.
|
||||
for (int i = 0; i < refs.length; i++) {
|
||||
if (refs[i].getReferenceType().isFallthrough()) {
|
||||
cuRep +=
|
||||
NEW_LINE + indent + getSpaces(addrRangeStr.length()) + " " +
|
||||
"FallThrough Override: " +
|
||||
DiffUtility.getUserToAddressString(inst.getProgram(), refs[i]);
|
||||
for (Reference ref : refs) {
|
||||
if (ref.getReferenceType().isFallthrough()) {
|
||||
cuRep += NEW_LINE + indent + getSpaces(addrRangeStr.length()) + " " +
|
||||
"FallThrough Override: " +
|
||||
DiffUtility.getUserToAddressString(inst.getProgram(), ref);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (hasFlowOverride) {
|
||||
cuRep +=
|
||||
NEW_LINE + indent + getSpaces(addrRangeStr.length()) + " " +
|
||||
"Flow Override: " + inst.getFlowOverride();
|
||||
cuRep += NEW_LINE + indent + getSpaces(addrRangeStr.length()) + " " +
|
||||
"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.
|
||||
// cuRep +=
|
||||
@@ -146,15 +149,15 @@ public abstract class CodeUnitDetails {
|
||||
return indent + "None";
|
||||
}
|
||||
StringBuffer buf = new StringBuffer();
|
||||
for (int i = 0; i < refs.length; i++) {
|
||||
if (refs[i].isExternalReference()) {
|
||||
buf.append(indent + "External Reference " + getRefInfo(pgm, refs[i]) + NEW_LINE);
|
||||
for (Reference ref : refs) {
|
||||
if (ref.isExternalReference()) {
|
||||
buf.append(indent + "External Reference " + getRefInfo(pgm, ref) + NEW_LINE);
|
||||
}
|
||||
else if (refs[i].isStackReference()) {
|
||||
buf.append(indent + "Stack Reference " + getRefInfo(pgm, refs[i]) + NEW_LINE);
|
||||
else if (ref.isStackReference()) {
|
||||
buf.append(indent + "Stack Reference " + getRefInfo(pgm, ref) + NEW_LINE);
|
||||
}
|
||||
else {
|
||||
buf.append(indent + "Reference " + getRefInfo(pgm, refs[i]) + NEW_LINE);
|
||||
buf.append(indent + "Reference " + getRefInfo(pgm, ref) + NEW_LINE);
|
||||
}
|
||||
}
|
||||
return buf.toString();
|
||||
|
||||
@@ -858,7 +858,13 @@ class CodeUnitMerger extends AbstractListingMerger {
|
||||
private void performMergeInstruction(Instruction instruction, boolean copyBytes)
|
||||
throws CodeUnitInsertionException, MemoryAccessException {
|
||||
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();
|
||||
// Code unit should already be cleared where this instruction needs to go.
|
||||
Listing resultListing = resultPgm.getListing();
|
||||
@@ -868,9 +874,12 @@ class CodeUnitMerger extends AbstractListingMerger {
|
||||
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(),
|
||||
new DumbMemBufferImpl(resultPgm.getMemory(), minAddress),
|
||||
new ProgramProcessorContext(resultPgm.getProgramContext(), minAddress));
|
||||
new ProgramProcessorContext(resultPgm.getProgramContext(), minAddress), lengthOverride);
|
||||
|
||||
// Set the fallthrough override if necessary.
|
||||
if (instruction.isFallThroughOverridden()) {
|
||||
|
||||
@@ -570,7 +570,6 @@ public class ListingMergeManager implements MergeResolver, ListingMergeConstants
|
||||
* determine the conflicts.
|
||||
* @param mergers the listing mergers whose conflicts are to be merged.
|
||||
* @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 CancelledException if the user cancels the merge.
|
||||
*/
|
||||
|
||||
+1
@@ -445,6 +445,7 @@ public class AutoAnalysisManager implements DomainObjectListener {
|
||||
break;
|
||||
case ChangeManager.DOCR_FALLTHROUGH_CHANGED:
|
||||
case ChangeManager.DOCR_FLOWOVERRIDE_CHANGED:
|
||||
case ChangeManager.DOCR_LENGTH_OVERRIDE_CHANGED:
|
||||
// TODO: not sure if this should be done this way or explicitly
|
||||
// via the application commands (this is inconsistent with other
|
||||
// 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_32DisassembleAction;
|
||||
private DockingAction setFlowOverrideAction;
|
||||
private DockingAction setLengthOverrideAction;
|
||||
|
||||
/** Dialog for obtaining the processor state to be used for disassembling. */
|
||||
// private ProcessorStateDialog processorStateDialog;
|
||||
@@ -177,6 +178,7 @@ public class DisassemblerPlugin extends Plugin {
|
||||
x86_64DisassembleAction = new X86_64DisassembleAction(this, GROUP_NAME, false);
|
||||
x86_32DisassembleAction = new X86_64DisassembleAction(this, GROUP_NAME, true);
|
||||
setFlowOverrideAction = new SetFlowOverrideAction(this, GROUP_NAME);
|
||||
setLengthOverrideAction = new SetLengthOverrideAction(this, GROUP_NAME);
|
||||
|
||||
tool.addAction(disassembleAction);
|
||||
tool.addAction(disassembleRestrictedAction);
|
||||
@@ -193,6 +195,7 @@ public class DisassemblerPlugin extends Plugin {
|
||||
tool.addAction(x86_32DisassembleAction);
|
||||
tool.addAction(contextAction);
|
||||
tool.addAction(setFlowOverrideAction);
|
||||
tool.addAction(setLengthOverrideAction);
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
};
|
||||
autoFallthroughAction.setPopupMenuData(new MenuData(new String[] { "Fallthrough",
|
||||
"Auto Override" }, null, "Fallthrough"));
|
||||
autoFallthroughAction.setPopupMenuData(
|
||||
new MenuData(new String[] { "Fallthrough", "Auto Override" }, null, "Fallthrough"));
|
||||
|
||||
tool.addAction(autoFallthroughAction);
|
||||
|
||||
@@ -111,8 +111,8 @@ public class FallThroughPlugin extends Plugin {
|
||||
return instruction != null && instruction.isFallThroughOverridden();
|
||||
}
|
||||
};
|
||||
clearFallthroughAction.setPopupMenuData(new MenuData(new String[] { "Fallthrough",
|
||||
"Clear Overrides" }, null, "Fallthrough"));
|
||||
clearFallthroughAction.setPopupMenuData(
|
||||
new MenuData(new String[] { "Fallthrough", "Clear Overrides" }, null, "Fallthrough"));
|
||||
|
||||
tool.addAction(clearFallthroughAction);
|
||||
|
||||
@@ -128,8 +128,8 @@ public class FallThroughPlugin extends Plugin {
|
||||
return instruction != null;
|
||||
}
|
||||
};
|
||||
setFallthroughAction.setPopupMenuData(new MenuData(
|
||||
new String[] { "Fallthrough", "Set..." }, null, "Fallthrough"));
|
||||
setFallthroughAction.setPopupMenuData(
|
||||
new MenuData(new String[] { "Fallthrough", "Set..." }, null, "Fallthrough"));
|
||||
|
||||
tool.addAction(setFallthroughAction);
|
||||
}
|
||||
|
||||
+9
-5
@@ -326,8 +326,9 @@ public class InsertBytesWidget extends ReusableDialogComponentProvider implement
|
||||
private List<OperandMetadata> createOperandMetadata(PseudoInstruction instruction)
|
||||
throws MemoryAccessException {
|
||||
|
||||
List<OperandMetadata> operands = new ArrayList<>();
|
||||
byte[] bytes = instruction.getParsedBytes();
|
||||
|
||||
List<OperandMetadata> operands = new ArrayList<>();
|
||||
for (int i = 0; i < instruction.getNumOperands(); i++) {
|
||||
OperandMetadata operandMD = new OperandMetadata();
|
||||
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
|
||||
// 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.
|
||||
byte[] mask = instruction.getPrototype().getOperandValueMask(i).getBytes();
|
||||
byte[] value = InstructionSearchUtils.byteArrayAnd(mask, instruction.getBytes());
|
||||
InstructionPrototype prototype = instruction.getPrototype();
|
||||
byte[] mask = prototype.getOperandValueMask(i).getBytes();
|
||||
byte[] value = InstructionSearchUtils.byteArrayAnd(mask, bytes);
|
||||
MaskContainer maskContainer = new MaskContainer(mask, value);
|
||||
|
||||
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
|
||||
// have to figure out which bits pertain to operands and just zero them out, so we're
|
||||
// just left with the instruction (mnemonic) bits.
|
||||
byte[] mask = instruction.getPrototype().getInstructionMask().getBytes();
|
||||
byte[] value = clearOperandBits(mask, instruction.getBytes());
|
||||
InstructionPrototype prototype = instruction.getPrototype();
|
||||
byte[] mask = prototype.getInstructionMask().getBytes();
|
||||
byte[] value = instruction.getParsedBytes();
|
||||
value = clearOperandBits(mask, value);
|
||||
MaskContainer mnemonicMask = new MaskContainer(mask, value);
|
||||
|
||||
InstructionMetadata instructionMD = new InstructionMetadata(mnemonicMask);
|
||||
|
||||
+21
-4
@@ -1,6 +1,5 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,8 +17,7 @@ package ghidra.app.plugin.core.reloc;
|
||||
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.lang.*;
|
||||
import ghidra.program.model.listing.Instruction;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.program.model.mem.DumbMemBufferImpl;
|
||||
import ghidra.program.model.mem.MemBuffer;
|
||||
import ghidra.program.model.symbol.Reference;
|
||||
@@ -31,6 +29,10 @@ public class InstructionStasher {
|
||||
private Address address;
|
||||
private InstructionPrototype prototype;
|
||||
private Reference[] referencesFrom;
|
||||
private FlowOverride flowOverride;
|
||||
private Address fallthroughOverride;
|
||||
private int lengthOverride;
|
||||
|
||||
private Address minAddress;
|
||||
|
||||
public InstructionStasher(Program program, Address address) {
|
||||
@@ -47,6 +49,12 @@ public class InstructionStasher {
|
||||
minAddress = instruction.getMinAddress();
|
||||
prototype = instruction.getPrototype();
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -57,7 +65,16 @@ public class InstructionStasher {
|
||||
MemBuffer buf = new DumbMemBufferImpl(program.getMemory(), minAddress);
|
||||
ProcessorContext context =
|
||||
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) {
|
||||
if (reference.getSource() != SourceType.DEFAULT) {
|
||||
|
||||
@@ -568,7 +568,13 @@ class ProgramTextWriter {
|
||||
}
|
||||
|
||||
try {
|
||||
byte[] bytes = cu.getBytes();
|
||||
byte[] bytes;
|
||||
if (cu instanceof Instruction instr) {
|
||||
bytes = instr.getParsedBytes();
|
||||
}
|
||||
else {
|
||||
bytes = cu.getBytes();
|
||||
}
|
||||
StringBuffer bytesbuf = new StringBuffer();
|
||||
for (int i = 0; i < bytes.length; ++i) {
|
||||
if (i > 0) {
|
||||
@@ -630,10 +636,7 @@ class ProgramTextWriter {
|
||||
|
||||
if (options.isHTML()) {
|
||||
Reference ref =
|
||||
cu.getProgram()
|
||||
.getReferenceManager()
|
||||
.getPrimaryReferenceFrom(cuAddress,
|
||||
i);
|
||||
cu.getProgram().getReferenceManager().getPrimaryReferenceFrom(cuAddress, i);
|
||||
addReferenceLinkedText(ref, opReps[i], true);
|
||||
}
|
||||
else {
|
||||
|
||||
+12
-1
@@ -174,7 +174,18 @@ public class BytesFieldFactory extends FieldFactory {
|
||||
}
|
||||
|
||||
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];
|
||||
Memory memory = cu.getProgram().getMemory();
|
||||
try {
|
||||
|
||||
+2
-1
@@ -120,7 +120,8 @@ public class MnemonicFieldFactory extends FieldFactory {
|
||||
}
|
||||
else if (cu instanceof Instruction) {
|
||||
Instruction instr = (Instruction) cu;
|
||||
if (instr.getFlowOverride() != FlowOverride.NONE || instr.isFallThroughOverridden()) {
|
||||
if (instr.getFlowOverride() != FlowOverride.NONE || instr.isFallThroughOverridden() ||
|
||||
instr.isLengthOverridden()) {
|
||||
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();
|
||||
String fallthroughComment = "-- Fallthrough Override: " +
|
||||
(fallThrough != null ? fallThrough.toString() : "NO-FALLTHROUGH");
|
||||
String fallthroughComment =
|
||||
"-- Fallthrough" + (instr.isFallThroughOverridden() ? " Override" : "") + ": " +
|
||||
(fallThrough != null ? fallThrough.toString() : "NO-FALLTHROUGH");
|
||||
comments.addFirst(fallthroughComment);
|
||||
}
|
||||
|
||||
if (instr.isLengthOverridden()) {
|
||||
String lengthOverrideComment = "-- Length Override: " + instr.getLength() +
|
||||
" (actual length is " + instr.getParsedLength() + ")";
|
||||
comments.addFirst(lengthOverrideComment);
|
||||
}
|
||||
|
||||
FlowOverride flowOverride = instr.getFlowOverride();
|
||||
if (flowOverride != FlowOverride.NONE) {
|
||||
String flowOverrideComment =
|
||||
|
||||
@@ -194,10 +194,10 @@ public class DiffUtility extends SimpleDiffUtility {
|
||||
}
|
||||
else if (namespace instanceof GhidraClass) {
|
||||
return otherProgram.getSymbolTable()
|
||||
.createClass(otherParentNamespace, namespace.getName(), source);
|
||||
.createClass(otherParentNamespace, namespace.getName(), source);
|
||||
}
|
||||
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 otherProgram.getReferenceManager()
|
||||
.getReference(fromAddr, toAddr, ref.getOperandIndex());
|
||||
.getReference(fromAddr, toAddr, ref.getOperandIndex());
|
||||
}
|
||||
Reference otherRef = otherProgram.getReferenceManager()
|
||||
.getPrimaryReferenceFrom(fromAddr, ref.getOperandIndex());
|
||||
.getPrimaryReferenceFrom(fromAddr, ref.getOperandIndex());
|
||||
if (otherRef != null && ref.getToAddress().hasSameAddressSpace(otherRef.getToAddress())) {
|
||||
return otherRef;
|
||||
}
|
||||
@@ -358,10 +358,10 @@ public class DiffUtility extends SimpleDiffUtility {
|
||||
return null;
|
||||
}
|
||||
return program.getReferenceManager()
|
||||
.getReference(fromAddr1, toAddr1, p2Ref.getOperandIndex());
|
||||
.getReference(fromAddr1, toAddr1, p2Ref.getOperandIndex());
|
||||
}
|
||||
Reference p1Ref = program.getReferenceManager()
|
||||
.getPrimaryReferenceFrom(fromAddr1, p2Ref.getOperandIndex());
|
||||
.getPrimaryReferenceFrom(fromAddr1, p2Ref.getOperandIndex());
|
||||
if (p1Ref != null && p1Ref.getToAddress().hasSameAddressSpace(p2Ref.getToAddress())) {
|
||||
return p1Ref;
|
||||
}
|
||||
@@ -386,8 +386,8 @@ public class DiffUtility extends SimpleDiffUtility {
|
||||
}
|
||||
// FIXME Should this be passing the Namespace?
|
||||
return otherProgram.getExternalManager()
|
||||
.addExtLocation(extLoc.getLibraryName(), extLoc.getLabel(), otherAddr,
|
||||
extLoc.getSource());
|
||||
.addExtLocation(extLoc.getLibraryName(), extLoc.getLabel(), otherAddr,
|
||||
extLoc.getSource());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -571,15 +571,18 @@ public class DiffUtility extends SimpleDiffUtility {
|
||||
Address rangeMin = range.getMinAddress();
|
||||
Address rangeMax = range.getMaxAddress();
|
||||
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) {
|
||||
Address minCuMinAddr = minCu.getMinAddress();
|
||||
if (minCuMinAddr.compareTo(rangeMin) != 0) {
|
||||
addrs.addRange(minCuMinAddr, minCu.getMaxAddress());
|
||||
addrs.addRange(minCuMinAddr, getMaxAddress(minCu));
|
||||
}
|
||||
}
|
||||
CodeUnit maxCu = listing.getCodeUnitContaining(rangeMax);
|
||||
if (maxCu != null) {
|
||||
Address maxCuMaxAddr = maxCu.getMaxAddress();
|
||||
Address maxCuMaxAddr = getMaxAddress(maxCu);
|
||||
if (maxCuMaxAddr.compareTo(rangeMax) != 0) {
|
||||
addrs.addRange(maxCu.getMinAddress(), maxCuMaxAddr);
|
||||
}
|
||||
@@ -588,6 +591,20 @@ public class DiffUtility extends SimpleDiffUtility {
|
||||
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.
|
||||
* 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) {
|
||||
return true;
|
||||
}
|
||||
// Checking length and prototype will handle use of length-override
|
||||
if (i1.getLength() != i2.getLength()) {
|
||||
return false;
|
||||
}
|
||||
@@ -2888,7 +2889,9 @@ public class ProgramDiff {
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -906,8 +906,7 @@ public class ProgramDiffDetails {
|
||||
ordinal + " " + fieldName + " " + actualDt.getMnemonic(actualDt.getDefaultSettings()) +
|
||||
" " + getCategoryName(actualDt) + " " + "DataTypeSize=" +
|
||||
(actualDt.isZeroLength() ? 0 : actualDt.getLength()) + " " + "ComponentSize=" +
|
||||
dtc.getLength() + " " + ((comment != null) ? comment : "") +
|
||||
" " + newLine);
|
||||
dtc.getLength() + " " + ((comment != null) ? comment : "") + " " + newLine);
|
||||
return actualDt;
|
||||
}
|
||||
|
||||
@@ -958,8 +957,7 @@ public class ProgramDiffDetails {
|
||||
fieldName = "field" + offset;
|
||||
}
|
||||
buf.append(newIndent + min.add(offset) + " " + dtc.getFieldName() + " " +
|
||||
dtc.getDataType().getName() + " " + "length=" +
|
||||
dtc.getLength() + " " +
|
||||
dtc.getDataType().getName() + " " + "length=" + dtc.getLength() + " " +
|
||||
((comment != null) ? comment : "") + " " + newLine);
|
||||
}
|
||||
}
|
||||
@@ -987,6 +985,7 @@ public class ProgramDiffDetails {
|
||||
boolean removedFallThrough =
|
||||
inst.isFallThroughOverridden() && (inst.getFallThrough() == null);
|
||||
boolean hasFlowOverride = inst.getFlowOverride() != FlowOverride.NONE;
|
||||
boolean hasLengthOverride = inst.isLengthOverridden();
|
||||
cuRep = cu.toString();
|
||||
if (removedFallThrough) {
|
||||
cuRep += newLine + indent + getSpaces(addrRangeStr.length()) + " " +
|
||||
@@ -1011,6 +1010,11 @@ public class ProgramDiffDetails {
|
||||
cuRep += newLine + indent + getSpaces(addrRangeStr.length()) + " " +
|
||||
"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()) + " " +
|
||||
"Instruction Prototype hash = " +
|
||||
Integer.toHexString(inst.getPrototype().hashCode());
|
||||
@@ -1463,8 +1467,8 @@ public class ProgramDiffDetails {
|
||||
private boolean addSpecificCommentDetails(int commentType, String commentName) {
|
||||
boolean hasCommentDiff = false;
|
||||
try {
|
||||
for (Address p1Address = minP1Address; p1Address.compareTo(
|
||||
maxP1Address) <= 0; p1Address = p1Address.add(1L)) {
|
||||
for (Address p1Address = minP1Address; p1Address
|
||||
.compareTo(maxP1Address) <= 0; p1Address = p1Address.add(1L)) {
|
||||
Address p2Address = SimpleDiffUtility.getCompatibleAddress(p1, p1Address, p2);
|
||||
String noComment = "No " + commentName + ".";
|
||||
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).
|
||||
if (cu.getProgram()
|
||||
.getListing()
|
||||
.getPropertyMap(
|
||||
propertyName) instanceof UnsupportedMapDB) {
|
||||
.getPropertyMap(propertyName) instanceof UnsupportedMapDB) {
|
||||
buf.append(
|
||||
indent2 + propertyName + " is an unsupported property." + newLine);
|
||||
continue;
|
||||
@@ -2241,8 +2244,8 @@ public class ProgramDiffDetails {
|
||||
BookmarkManager bmm1 = p1.getBookmarkManager();
|
||||
BookmarkManager bmm2 = p2.getBookmarkManager();
|
||||
try {
|
||||
for (Address p1Address = minP1Address; p1Address.compareTo(
|
||||
maxP1Address) <= 0; p1Address = p1Address.add(1)) {
|
||||
for (Address p1Address = minP1Address; p1Address
|
||||
.compareTo(maxP1Address) <= 0; p1Address = p1Address.add(1)) {
|
||||
Address p2Address = SimpleDiffUtility.getCompatibleAddress(p1, p1Address, p2);
|
||||
Bookmark[] marks1 = bmm1.getBookmarks(p1Address);
|
||||
Arrays.sort(marks1, BOOKMARK_COMPARATOR);
|
||||
@@ -2341,7 +2344,7 @@ public class ProgramDiffDetails {
|
||||
|
||||
private boolean isSameInstruction(Instruction i1, Instruction i2) {
|
||||
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 sameFlowOverride = i1.getFlowOverride() == i2.getFlowOverride();
|
||||
return samePrototypes && sameInstructionLength && sameFallthrough && sameFlowOverride;
|
||||
|
||||
@@ -29,7 +29,6 @@ import ghidra.util.datastruct.Accumulator;
|
||||
import ghidra.util.datastruct.ListAccumulator;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
import ghidra.util.task.TaskMonitorAdapter;
|
||||
import utility.function.TerminatingConsumer;
|
||||
|
||||
/**
|
||||
@@ -151,14 +150,26 @@ public class ProgramMemoryUtil {
|
||||
*/
|
||||
private static void copyByteRange(Memory toMem, Memory fromMem, AddressRange range)
|
||||
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
|
||||
int length = 0;
|
||||
Address writeAddress = range.getMinAddress();
|
||||
for (long len = range.getLength(); len > 0; len -= length) {
|
||||
length = (int) Math.min(len, Integer.MAX_VALUE);
|
||||
byte[] bytes = new byte[length];
|
||||
fromMem.getBytes(writeAddress, bytes);
|
||||
toMem.setBytes(writeAddress, bytes);
|
||||
byte[] srcBytes = new byte[length];
|
||||
|
||||
// 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) {
|
||||
writeAddress = writeAddress.add(length);
|
||||
}
|
||||
|
||||
@@ -628,7 +628,9 @@ public class ProgramMerge {
|
||||
return true;
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -682,8 +684,8 @@ public class ProgramMerge {
|
||||
bytesLength = (int) originMax.subtract(originMin);
|
||||
}
|
||||
else {
|
||||
originMax = originInstruction.getMaxAddress();
|
||||
bytesLength = originInstruction.getLength();
|
||||
bytesLength = originInstruction.getParsedLength();
|
||||
originMax = originMin.add(bytesLength - 1);
|
||||
}
|
||||
|
||||
Address resultMax = originToResultTranslator.getAddress(originMax);
|
||||
@@ -699,7 +701,7 @@ public class ProgramMerge {
|
||||
|
||||
// If there are byte differences for this instruction then the
|
||||
// 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.
|
||||
ProgramMemoryUtil.copyBytesInRanges(resultProgram, originProgram, resultMin, resultMax);
|
||||
}
|
||||
@@ -709,7 +711,8 @@ public class ProgramMerge {
|
||||
newInst = disassembleDelaySlottedInstruction(resultProgram, resultMin);
|
||||
}
|
||||
else {
|
||||
newInst = disassembleNonDelaySlotInstruction(resultProgram, resultMin);
|
||||
newInst = disassembleNonDelaySlotInstruction(resultProgram, resultMin,
|
||||
originInstruction.isLengthOverridden() ? originInstruction.getLength() : 0);
|
||||
}
|
||||
if (newInst == null) {
|
||||
return;
|
||||
@@ -740,24 +743,29 @@ public class ProgramMerge {
|
||||
}
|
||||
|
||||
private Instruction disassembleDelaySlottedInstruction(Program program, Address addr) {
|
||||
// WARNING: does not support instruction length override use
|
||||
// Use heavyweight disassembler for delay slotted instruction
|
||||
AddressSet restrictedSet = new AddressSet(addr);
|
||||
Disassembler disassembler =
|
||||
Disassembler.getDisassembler(program, TaskMonitor.DUMMY, null);
|
||||
Disassembler disassembler = Disassembler.getDisassembler(program, TaskMonitor.DUMMY, null);
|
||||
disassembler.disassemble(addr, restrictedSet, false);
|
||||
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
|
||||
DisassemblerContextImpl context = new DisassemblerContextImpl(program.getProgramContext());
|
||||
context.flowStart(addr);
|
||||
try {
|
||||
InstructionPrototype proto = program.getLanguage()
|
||||
.parse(new DumbMemBufferImpl(program.getMemory(), addr), context, false);
|
||||
if (lengthOverride > proto.getLength()) {
|
||||
lengthOverride = 0;
|
||||
}
|
||||
return resultListing.createInstruction(addr, proto,
|
||||
new DumbMemBufferImpl(program.getMemory(), addr),
|
||||
new ProgramProcessorContext(program.getProgramContext(), addr));
|
||||
new ProgramProcessorContext(program.getProgramContext(), addr),
|
||||
Math.min(lengthOverride, proto.getLength()));
|
||||
}
|
||||
catch (Exception e) {
|
||||
program.getBookmarkManager()
|
||||
@@ -767,17 +775,13 @@ public class ProgramMerge {
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean bytesAreDifferent(AddressSetView originByteDiffs, Address originMin,
|
||||
private boolean bytesMayDiffer(AddressSetView originByteDiffs, Address originMin,
|
||||
Address resultMin, int byteCnt) throws MemoryAccessException {
|
||||
if (originByteDiffs != null) {
|
||||
AddressSet resultByteDiffs = originToResultTranslator.getAddressSet(originByteDiffs);
|
||||
return resultByteDiffs.intersects(new AddressSet(resultMin, resultMin.add(byteCnt)));
|
||||
}
|
||||
byte[] originBytes = new byte[byteCnt];
|
||||
originProgram.getMemory().getBytes(originMin, originBytes);
|
||||
byte[] resultBytes = new byte[byteCnt];
|
||||
resultProgram.getMemory().getBytes(resultMin, resultBytes);
|
||||
return !Arrays.equals(originBytes, resultBytes);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -808,7 +812,7 @@ public class ProgramMerge {
|
||||
// If there are byte differences for this instruction then the
|
||||
// bytes need to get copied even though the user did not indicate to.
|
||||
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.
|
||||
ProgramMemoryUtil.copyBytesInRanges(resultProgram, originProgram, resultMin, resultMax);
|
||||
}
|
||||
@@ -1717,9 +1721,7 @@ public class ProgramMerge {
|
||||
// Now discard any tags we've been told to remove.
|
||||
if (discardTags != null) {
|
||||
Set<String> tagNames = getTagNames(discardTags);
|
||||
Iterator<FunctionTag> iter = resultTags.iterator();
|
||||
while (iter.hasNext()) {
|
||||
FunctionTag tag = iter.next();
|
||||
for (FunctionTag tag : resultTags) {
|
||||
if (tagNames.contains(tag.getName())) {
|
||||
resultFunction.removeTag(tag.getName());
|
||||
|
||||
@@ -3934,8 +3936,7 @@ public class ProgramMerge {
|
||||
if (originObject != null) {
|
||||
try {
|
||||
if (resultOpm == null) {
|
||||
resultOpm =
|
||||
createPropertyMap(userPropertyName, resultProgram, originOpm);
|
||||
resultOpm = createPropertyMap(userPropertyName, resultProgram, originOpm);
|
||||
}
|
||||
resultOpm.add(resultAddress, originObject);
|
||||
}
|
||||
|
||||
@@ -669,7 +669,8 @@ public class SymbolicPropogator {
|
||||
else {
|
||||
int instrByteHashCode = -1;
|
||||
try {
|
||||
instrByteHashCode = Arrays.hashCode(instr.getBytes());
|
||||
byte[] bytes = instr.getParsedBytes();
|
||||
instrByteHashCode = Arrays.hashCode(bytes);
|
||||
}
|
||||
catch (MemoryAccessException e) {
|
||||
// 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.program.model.address.*;
|
||||
import ghidra.program.model.data.EndianSettingsDefinition;
|
||||
import ghidra.program.model.listing.CodeUnit;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.program.model.mem.MemoryAccessException;
|
||||
import ghidra.program.util.BytesFieldLocation;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
@@ -50,100 +49,98 @@ public class BytesTableColumn extends ProgramLocationTableColumnExtensionPoint<A
|
||||
private static SettingsDefinition[] SETTINGS_DEFS =
|
||||
{ BYTE_COUNT, MEMORY_OFFSET, ENDIANESS, FORMAT };
|
||||
|
||||
private final GColumnRenderer<Byte[]> monospacedRenderer =
|
||||
new AbstractGColumnRenderer<>() {
|
||||
@Override
|
||||
protected void configureFont(JTable table, TableModel model, int column) {
|
||||
setFont(getFixedWidthFont());
|
||||
private final GColumnRenderer<Byte[]> monospacedRenderer = new AbstractGColumnRenderer<>() {
|
||||
@Override
|
||||
protected void configureFont(JTable table, TableModel model, int column) {
|
||||
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) {
|
||||
boolean bigEndian =
|
||||
(ENDIANESS.getChoice(settings) != EndianSettingsDefinition.LITTLE);
|
||||
int format = FORMAT.getChoice(settings);
|
||||
if (format == FormatSettingsDefinition.CHAR) {
|
||||
return bytesToString(bytes);
|
||||
}
|
||||
|
||||
int startIx = 0;
|
||||
int endIx = bytes.length;
|
||||
int inc = 1;
|
||||
if (!bigEndian) {
|
||||
startIx = bytes.length - 1;
|
||||
endIx = -1;
|
||||
inc = -1;
|
||||
StringBuilder buffer = new StringBuilder();
|
||||
for (int i = startIx; i != endIx; i += inc) {
|
||||
if (buffer.length() != 0) {
|
||||
buffer.append(' ');
|
||||
}
|
||||
buffer.append(getByteString(bytes[i], format));
|
||||
}
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
int format = FORMAT.getChoice(settings);
|
||||
if (format == FormatSettingsDefinition.CHAR) {
|
||||
return bytesToString(bytes);
|
||||
private String bytesToString(Byte[] bytes) {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
for (byte b : bytes) {
|
||||
char c = (char) (b & 0xff);
|
||||
if (c > 32 && c < 128) {
|
||||
buf.append((char) (b & 0xff));
|
||||
}
|
||||
|
||||
StringBuilder buffer = new StringBuilder();
|
||||
for (int i = startIx; i != endIx; i += inc) {
|
||||
if (buffer.length() != 0) {
|
||||
buffer.append(' ');
|
||||
}
|
||||
buffer.append(getByteString(bytes[i], format));
|
||||
else {
|
||||
buf.append('.');
|
||||
}
|
||||
return buffer.toString();
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
private String bytesToString(Byte[] bytes) {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
for (byte b : bytes) {
|
||||
char c = (char) (b & 0xff);
|
||||
if (c > 32 && c < 128) {
|
||||
buf.append((char) (b & 0xff));
|
||||
}
|
||||
else {
|
||||
buf.append('.');
|
||||
}
|
||||
}
|
||||
return buf.toString();
|
||||
private String getByteString(Byte b, int format) {
|
||||
|
||||
String val;
|
||||
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;
|
||||
}
|
||||
|
||||
private String getByteString(Byte b, int format) {
|
||||
@Override
|
||||
public Component getTableCellRendererComponent(GTableCellRenderingData data) {
|
||||
|
||||
String val;
|
||||
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;
|
||||
}
|
||||
JLabel label = (JLabel) super.getTableCellRendererComponent(data);
|
||||
|
||||
@Override
|
||||
public Component getTableCellRendererComponent(GTableCellRenderingData data) {
|
||||
Object value = data.getValue();
|
||||
Settings settings = data.getColumnSettings();
|
||||
|
||||
JLabel label = (JLabel) super.getTableCellRendererComponent(data);
|
||||
Byte[] bytes = (Byte[]) value;
|
||||
|
||||
Object value = data.getValue();
|
||||
Settings settings = data.getColumnSettings();
|
||||
setText(formatBytes(bytes, settings));
|
||||
|
||||
Byte[] bytes = (Byte[]) value;
|
||||
return label;
|
||||
}
|
||||
|
||||
setText(formatBytes(bytes, settings));
|
||||
|
||||
return label;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFilterString(Byte[] t, Settings settings) {
|
||||
String formatted = formatBytes(t, settings);
|
||||
return formatted;
|
||||
}
|
||||
};
|
||||
@Override
|
||||
public String getFilterString(Byte[] t, Settings settings) {
|
||||
String formatted = formatBytes(t, settings);
|
||||
return formatted;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Default Constructor
|
||||
@@ -194,7 +191,12 @@ public class BytesTableColumn extends ProgramLocationTableColumnExtensionPoint<A
|
||||
if (cu == null) { // can happen for 'SpecialAddress'es
|
||||
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);
|
||||
InstructionPrototype proto = program.getLanguage().parse(buf, context, false);
|
||||
Instruction createdInstruction =
|
||||
listing.createInstruction(atAddress, proto, buf, context);
|
||||
listing.createInstruction(atAddress, proto, buf, context, 0);
|
||||
commit = true;
|
||||
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 db.Transaction;
|
||||
import ghidra.app.plugin.core.bookmark.BookmarkPlugin;
|
||||
import ghidra.app.plugin.core.clear.ClearPlugin;
|
||||
import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
|
||||
@@ -1678,6 +1679,59 @@ public class CodeUnitIteratorTest extends AbstractGhidraHeadedIntegrationTest {
|
||||
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) {
|
||||
return space.getAddress(l);
|
||||
}
|
||||
@@ -1704,7 +1758,7 @@ public class CodeUnitIteratorTest extends AbstractGhidraHeadedIntegrationTest {
|
||||
ProcessorContext context = new ProgramProcessorContext(program.getProgramContext(), atAddr);
|
||||
InstructionPrototype proto = program.getLanguage().parse(buf, context, false);
|
||||
|
||||
listing.createInstruction(atAddr, proto, buf, context);
|
||||
listing.createInstruction(atAddr, proto, buf, context, 0);
|
||||
}
|
||||
|
||||
private void startTransaction() {
|
||||
|
||||
+1
-1
@@ -99,7 +99,7 @@ public class SymbolUtilities1Test extends AbstractGhidraHeadedIntegrationTest {
|
||||
new ProgramProcessorContext(program.getProgramContext(), addr(0x200));
|
||||
DumbMemBufferImpl membuf = new DumbMemBufferImpl(program.getMemory(), addr(0x200));
|
||||
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));
|
||||
assertEquals("LAB_00000200", symbol.getName());
|
||||
|
||||
|
||||
+1
-1
@@ -97,7 +97,7 @@ public class SymbolUtilities2Test extends AbstractGhidraHeadedIntegrationTest {
|
||||
new ProgramProcessorContext(program.getProgramContext(), addr(0x200));
|
||||
DumbMemBufferImpl membuf = new DumbMemBufferImpl(program.getMemory(), addr(0x200));
|
||||
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));
|
||||
assertEquals("LAB_CODE_0200", symbol.getName());
|
||||
|
||||
|
||||
+1
-1
@@ -1063,7 +1063,7 @@ public class CodeManagerTest extends AbstractGenericTest {
|
||||
MemBuffer buf = new DumbMemBufferImpl(mem, atAddr);
|
||||
ProcessorContext context = new ProgramProcessorContext(program.getProgramContext(), atAddr);
|
||||
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;
|
||||
|
||||
import static ghidra.feature.vt.api.main.VTMarkupItemApplyActionType.REPLACE;
|
||||
import static ghidra.feature.vt.db.VTTestUtils.addr;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static ghidra.feature.vt.api.main.VTMarkupItemApplyActionType.*;
|
||||
import static ghidra.feature.vt.db.VTTestUtils.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -379,7 +378,7 @@ public class DataTypeMarkupItemTest extends AbstractVTMarkupItemTest {
|
||||
new ProgramProcessorContext(program.getProgramContext(), atAddress);
|
||||
InstructionPrototype proto = program.getLanguage().parse(buf, context, false);
|
||||
Instruction createdInstruction =
|
||||
listing.createInstruction(atAddress, proto, buf, context);
|
||||
listing.createInstruction(atAddress, proto, buf, context, 0);
|
||||
commit = true;
|
||||
return createdInstruction;
|
||||
}
|
||||
|
||||
+2
-1
@@ -182,7 +182,8 @@ public abstract class PcodeEmit {
|
||||
*/
|
||||
void resolveFinalFallthrough() throws IOException {
|
||||
try {
|
||||
if (fallOverride == null || fallOverride.equals(getStartAddress().add(fallOffset))) {
|
||||
if (fallOverride == null) {
|
||||
// handles both length-override and fallthrough override cases
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -505,19 +505,6 @@ abstract class PseudoCodeUnit implements CodeUnit {
|
||||
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
|
||||
public String getComment(int commentType) {
|
||||
return comments.get(commentType);
|
||||
|
||||
+22
@@ -146,6 +146,18 @@ public class PseudoInstruction extends PseudoCodeUnit implements Instruction, In
|
||||
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
|
||||
public Register getBaseContextRegister() {
|
||||
return procContext.getBaseContextRegister();
|
||||
@@ -505,6 +517,16 @@ public class PseudoInstruction extends PseudoCodeUnit implements Instruction, In
|
||||
this.flowOverride = flowOverride != null ? flowOverride : FlowOverride.NONE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLengthOverride(int length) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLengthOverridden() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFallThroughOverridden() {
|
||||
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.symbol.FunctionSymbol;
|
||||
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.listing.*;
|
||||
import ghidra.program.model.mem.MemBuffer;
|
||||
@@ -107,7 +108,7 @@ class ListingDB implements Listing {
|
||||
|
||||
@Override
|
||||
public Instruction getInstructionContaining(Address addr) {
|
||||
return codeMgr.getInstructionContaining(addr);
|
||||
return codeMgr.getInstructionContaining(addr, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -261,14 +262,15 @@ class ListingDB implements Listing {
|
||||
}
|
||||
|
||||
@Override
|
||||
public PropertyMap getPropertyMap(String propertyName) {
|
||||
public PropertyMap<?> getPropertyMap(String propertyName) {
|
||||
return codeMgr.getPropertyMap(propertyName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Instruction createInstruction(Address addr, InstructionPrototype prototype,
|
||||
MemBuffer memBuf, ProcessorContextView context) throws CodeUnitInsertionException {
|
||||
return codeMgr.createCodeUnit(addr, prototype, memBuf, context);
|
||||
MemBuffer memBuf, ProcessorContextView context, int length)
|
||||
throws CodeUnitInsertionException {
|
||||
return codeMgr.createCodeUnit(addr, prototype, memBuf, context, length);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -278,8 +280,7 @@ class ListingDB implements Listing {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Data createData(Address addr, DataType dataType)
|
||||
throws CodeUnitInsertionException {
|
||||
public Data createData(Address addr, DataType dataType) throws CodeUnitInsertionException {
|
||||
return codeMgr.createCodeUnit(addr, dataType, dataType.getLength());
|
||||
}
|
||||
|
||||
@@ -292,8 +293,7 @@ class ListingDB implements Listing {
|
||||
@Override
|
||||
public void clearCodeUnits(Address startAddr, Address endAddr, boolean clearContext) {
|
||||
try {
|
||||
codeMgr.clearCodeUnits(startAddr, endAddr, clearContext,
|
||||
TaskMonitor.DUMMY);
|
||||
codeMgr.clearCodeUnits(startAddr, endAddr, clearContext, TaskMonitor.DUMMY);
|
||||
}
|
||||
catch (CancelledException e) {
|
||||
// can't happen with dummy monitor
|
||||
|
||||
+3
-1
@@ -109,8 +109,10 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM
|
||||
* property map (StringTranslations).
|
||||
* 19-Jan-2023 - version 26 Improved relocation data records to incorporate status and
|
||||
* 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
|
||||
|
||||
+85
-24
@@ -155,11 +155,16 @@ public class CodeManager implements ErrorHandler, ManagerDB {
|
||||
while (addrIter.hasNext()) {
|
||||
monitor.checkCancelled();
|
||||
Address addr = addrIter.next();
|
||||
|
||||
Instruction instr = getInstructionAt(addr);
|
||||
if (instr == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
long offset = oldFallThroughs.getLong(addr);
|
||||
Address toAddr = addr.getNewAddress(offset);
|
||||
refMgr.addMemoryReference(addr, toAddr, RefType.FALL_THROUGH,
|
||||
SourceType.USER_DEFINED, Reference.MNEMONIC);
|
||||
instr.setFallThrough(toAddr);
|
||||
}
|
||||
catch (NoValueException e) {
|
||||
// skip
|
||||
@@ -531,6 +536,16 @@ public class CodeManager implements ErrorHandler, ManagerDB {
|
||||
if (flowOverride != FlowOverride.NONE) {
|
||||
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 &&
|
||||
@@ -581,21 +596,34 @@ public class CodeManager implements ErrorHandler, ManagerDB {
|
||||
* @param prototype instruction definition object
|
||||
* @param memBuf the MemBuffer to use to get the bytes from memory
|
||||
* @param context object that has the state of all the registers.
|
||||
* @return the new instruction
|
||||
* @exception CodeUnitInsertionException thrown if code unit
|
||||
* overlaps with an existing code unit
|
||||
* @param length instruction byte-length (must be in the range 0..prototype.getLength()).
|
||||
* If smaller than the prototype length it must have a value no greater than 7, otherwise
|
||||
* an error will be thrown. A value of 0 or greater-than-or-equal the prototype length
|
||||
* will be ignored and not impose and override length. The length value must be a multiple
|
||||
* of the {@link Language#getInstructionAlignment() instruction alignment} .
|
||||
* @return the newly created instruction.
|
||||
* @throws CodeUnitInsertionException thrown if the new Instruction would overlap and
|
||||
* existing {@link CodeUnit} or the specified {@code length} is unsupported.
|
||||
* @throws IllegalArgumentException if a negative {@code length} is specified.
|
||||
*/
|
||||
public Instruction createCodeUnit(Address address, InstructionPrototype prototype,
|
||||
MemBuffer memBuf, ProcessorContextView context) throws CodeUnitInsertionException {
|
||||
MemBuffer memBuf, ProcessorContextView context, int length)
|
||||
throws CodeUnitInsertionException {
|
||||
|
||||
lock.acquire();
|
||||
creatingInstruction = true;
|
||||
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);
|
||||
|
||||
InstructionDB inst = addInstruction(address, endAddr, prototype, memBuf, context);
|
||||
if (forcedLengthOverride != 0) {
|
||||
inst.doSetLengthOverride(forcedLengthOverride);
|
||||
}
|
||||
|
||||
// fire event
|
||||
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.
|
||||
* If {@code usePrototypeLength==true}
|
||||
* <pre>{@literal
|
||||
* instruction.minAddress() <= addr <= instruction.maxAddress()
|
||||
* instruction.getMinAddress() <= addr <=
|
||||
* instruction.getMinAddress().add(instruction.getPrototype().getLength() - 1)
|
||||
* }</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 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
|
||||
* instruction does not exist
|
||||
*/
|
||||
public Instruction getInstructionContaining(Address address) {
|
||||
public Instruction getInstructionContaining(Address address, boolean usePrototypeLength) {
|
||||
lock.acquire();
|
||||
try {
|
||||
Instruction instr = getInstructionAt(address);
|
||||
|
||||
if (instr != null) {
|
||||
return instr;
|
||||
}
|
||||
|
||||
instr = this.getInstructionBefore(address);
|
||||
|
||||
if (instr != null && instr.contains(address)) {
|
||||
return instr;
|
||||
if (instr != null) {
|
||||
Address endAddr;
|
||||
if (usePrototypeLength && instr.isLengthOverridden()) {
|
||||
endAddr = instr.getMinAddress().add(instr.getParsedLength() - 1);
|
||||
}
|
||||
else {
|
||||
endAddr = instr.getMaxAddress();
|
||||
}
|
||||
if (address.compareTo(endAddr) <= 0) {
|
||||
return instr;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -1639,8 +1686,14 @@ public class CodeManager implements ErrorHandler, ManagerDB {
|
||||
nextInstEndAddr = nextInstAddr;
|
||||
int protoID = nextInstRec.getIntValue(InstDBAdapter.PROTO_ID_COL);
|
||||
InstructionPrototype proto = protoMgr.getPrototype(protoID);
|
||||
int len = proto != null ? proto.getLength()
|
||||
: nextInstAddr.getAddressSpace().getAddressableUnitSize();
|
||||
int len;
|
||||
if (proto != null) {
|
||||
len = InstructionDB.getLength(proto,
|
||||
nextInstRec.getByteValue(InstDBAdapter.FLAGS_COL));
|
||||
}
|
||||
else {
|
||||
len = nextInstAddr.getAddressSpace().getAddressableUnitSize();
|
||||
}
|
||||
if (len > 1) {
|
||||
try {
|
||||
nextInstEndAddr = nextInstAddr.addNoWrap(len - 1);
|
||||
@@ -1730,7 +1783,7 @@ public class CodeManager implements ErrorHandler, ManagerDB {
|
||||
if (addr != AddressMap.INVALID_ADDRESS_KEY) {
|
||||
lock.acquire();
|
||||
try {
|
||||
Instruction inst = getInstructionContaining(address);
|
||||
Instruction inst = getInstructionContaining(address, false);
|
||||
if (inst != null) {
|
||||
return null;
|
||||
}
|
||||
@@ -2538,7 +2591,7 @@ public class CodeManager implements ErrorHandler, ManagerDB {
|
||||
return;
|
||||
}
|
||||
boolean fail = false;
|
||||
if (getInstructionContaining(start) != null) {
|
||||
if (getInstructionContaining(start, false) != null) {
|
||||
fail = true;
|
||||
}
|
||||
else {
|
||||
@@ -2574,7 +2627,7 @@ public class CodeManager implements ErrorHandler, ManagerDB {
|
||||
if (!program.getMemory().contains(start, end)) {
|
||||
return false;
|
||||
}
|
||||
if (getInstructionContaining(start) != null) {
|
||||
if (getInstructionContaining(start, false) != null) {
|
||||
return false;
|
||||
}
|
||||
if (getDefinedDataContaining(start) != null) {
|
||||
@@ -3168,13 +3221,21 @@ public class CodeManager implements ErrorHandler, ManagerDB {
|
||||
* @param newFallThroughRef new fallthrough reference or null if removed
|
||||
*/
|
||||
public void fallThroughChanged(Address fromAddr, Reference newFallThroughRef) {
|
||||
if (newFallThroughRef != null &&
|
||||
newFallThroughRef.getReferenceType() != RefType.FALL_THROUGH) {
|
||||
throw new IllegalArgumentException("invalid reftype");
|
||||
}
|
||||
lock.acquire();
|
||||
try {
|
||||
InstructionDB instr = getInstructionAt(addrMap.getKey(fromAddr, false));
|
||||
// TODO: Should prevent this if instruction is null or isInDelaySlot
|
||||
if (instr != null) {
|
||||
instr.fallThroughChanged(newFallThroughRef);
|
||||
if (instr == null) {
|
||||
// Do not allow fallthrough ref without instruction
|
||||
if (newFallThroughRef != null) {
|
||||
refManager.delete(newFallThroughRef);
|
||||
}
|
||||
return;
|
||||
}
|
||||
instr.fallThroughChanged(newFallThroughRef);
|
||||
}
|
||||
finally {
|
||||
lock.release();
|
||||
|
||||
+2
-8
@@ -23,6 +23,7 @@ import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import db.DBRecord;
|
||||
import ghidra.program.database.*;
|
||||
import ghidra.program.database.references.ReferenceDBManager;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressOutOfBoundsException;
|
||||
import ghidra.program.model.lang.*;
|
||||
@@ -48,7 +49,7 @@ abstract class CodeUnitDB extends DatabaseObject implements CodeUnit, ProcessorC
|
||||
protected long addr;
|
||||
protected Address endAddr;
|
||||
protected int length;
|
||||
protected ReferenceManager refMgr;
|
||||
protected ReferenceDBManager refMgr;
|
||||
protected ProgramDB program;
|
||||
|
||||
private DBRecord commentRec;
|
||||
@@ -407,13 +408,6 @@ abstract class CodeUnitDB extends DatabaseObject implements CodeUnit, ProcessorC
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSuccessor(CodeUnit codeUnit) {
|
||||
Address min = codeUnit.getMinAddress();
|
||||
|
||||
return this.getMaxAddress().isSuccessor(min);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<String> propertyNames() {
|
||||
PropertyMapManager upm = codeMgr.getPropertyMapManager();
|
||||
|
||||
+213
-32
@@ -15,8 +15,7 @@
|
||||
*/
|
||||
package ghidra.program.database.code;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
|
||||
import db.DBRecord;
|
||||
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.scalar.Scalar;
|
||||
import ghidra.program.model.symbol.*;
|
||||
import ghidra.program.model.util.CodeUnitInsertionException;
|
||||
import ghidra.program.util.ChangeManager;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.exception.NoValueException;
|
||||
@@ -38,16 +38,21 @@ import ghidra.util.exception.NoValueException;
|
||||
*/
|
||||
public class InstructionDB extends CodeUnitDB implements Instruction, InstructionContext {
|
||||
|
||||
private static byte FALLTHROUGH_SET_MASK = (byte) 0x01;
|
||||
private static byte FALLTHROUGH_CLEAR_MASK = (byte) 0xfe;
|
||||
private static final byte FALLTHROUGH_SET_MASK = 0x01;
|
||||
private static final byte FALLTHROUGH_CLEAR_MASK = ~FALLTHROUGH_SET_MASK;
|
||||
|
||||
private static byte FLOWOVERRIDE_MASK = (byte) 0x0e;
|
||||
private static byte FLOWOVERRIDE_CLEAR_MASK = (byte) 0xf1;
|
||||
private static int FLOWOVERRIDE_SHIFT = 1;
|
||||
private static final byte FLOW_OVERRIDE_SET_MASK = 0x0e;
|
||||
private static final byte FLOW_OVERRIDE_CLEAR_MASK = ~FLOW_OVERRIDE_SET_MASK;
|
||||
private static final int FLOW_OVERRIDE_SHIFT = 1;
|
||||
|
||||
private static final byte LENGTH_OVERRIDE_SET_MASK = 0x70;
|
||||
private static final byte LENGTH_OVERRIDE_CLEAR_MASK = ~LENGTH_OVERRIDE_SET_MASK;
|
||||
private static final int LENGTH_OVERRIDE_SHIFT = 4;
|
||||
|
||||
private InstructionPrototype proto;
|
||||
private byte flags;
|
||||
private FlowOverride flowOverride;
|
||||
private int lengthOverride;
|
||||
private final static Address[] EMPTY_ADDR_ARRAY = new Address[0];
|
||||
private volatile boolean clearingFallThroughs = false;
|
||||
|
||||
@@ -68,7 +73,8 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio
|
||||
this.proto = proto;
|
||||
this.flags = flags;
|
||||
flowOverride =
|
||||
FlowOverride.getFlowOverride((flags & FLOWOVERRIDE_MASK) >> FLOWOVERRIDE_SHIFT);
|
||||
FlowOverride.getFlowOverride((flags & FLOW_OVERRIDE_SET_MASK) >> FLOW_OVERRIDE_SHIFT);
|
||||
refreshLength();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -83,6 +89,36 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio
|
||||
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
|
||||
protected boolean hasBeenDeleted(DBRecord rec) {
|
||||
if (rec == null) {
|
||||
@@ -106,10 +142,11 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio
|
||||
if (!newProto.equals(proto)) {
|
||||
return true;
|
||||
}
|
||||
length = proto.getLength();
|
||||
|
||||
flags = rec.getByteValue(InstDBAdapter.FLAGS_COL);
|
||||
flowOverride =
|
||||
FlowOverride.getFlowOverride((flags & FLOWOVERRIDE_MASK) >> FLOWOVERRIDE_SHIFT);
|
||||
FlowOverride.getFlowOverride((flags & FLOW_OVERRIDE_SET_MASK) >> FLOW_OVERRIDE_SHIFT);
|
||||
refreshLength();
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -142,6 +179,31 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio
|
||||
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
|
||||
public Address getFallFrom() {
|
||||
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
|
||||
public Address getFallThrough() {
|
||||
lock.acquire();
|
||||
try {
|
||||
checkIsValid();
|
||||
if (isFallThroughOverridden()) {
|
||||
for (Reference ref : refMgr.getReferencesFrom(address)) {
|
||||
if (ref.getReferenceType().isFallthrough() &&
|
||||
ref.getToAddress().isMemoryAddress()) {
|
||||
return ref.getToAddress();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
return getFallThroughReference();
|
||||
}
|
||||
return getDefaultFallThrough();
|
||||
}
|
||||
@@ -221,7 +286,7 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio
|
||||
return EMPTY_ADDR_ARRAY;
|
||||
}
|
||||
|
||||
ArrayList<Address> list = new ArrayList<>();
|
||||
Set<Address> list = new HashSet<>();
|
||||
for (Reference ref : refs) {
|
||||
if (!ref.getReferenceType().isIndirect()) {
|
||||
list.add(ref.getToAddress());
|
||||
@@ -232,8 +297,7 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio
|
||||
return EMPTY_ADDR_ARRAY;
|
||||
}
|
||||
|
||||
Address[] addrs = new Address[list.size()];
|
||||
return list.toArray(addrs);
|
||||
return list.toArray(new Address[list.size()]);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -537,8 +601,8 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio
|
||||
}
|
||||
FlowType origFlowType = getFlowType();
|
||||
|
||||
flags &= FLOWOVERRIDE_CLEAR_MASK;
|
||||
flags |= (flow.ordinal() << FLOWOVERRIDE_SHIFT);
|
||||
flags &= FLOW_OVERRIDE_CLEAR_MASK;
|
||||
flags |= (flow.ordinal() << FLOW_OVERRIDE_SHIFT);
|
||||
codeMgr.setFlags(addr, flags);
|
||||
flowOverride = flow;
|
||||
|
||||
@@ -623,19 +687,25 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
return;
|
||||
}
|
||||
refreshIfNeeded();
|
||||
clearingFallThroughs = true;
|
||||
try {
|
||||
boolean fallThroughPreserved = false;
|
||||
for (Reference ref : refMgr.getReferencesFrom(address)) {
|
||||
if (ref.getReferenceType() == RefType.FALL_THROUGH &&
|
||||
!ref.equals(keepFallThroughRef)) {
|
||||
refMgr.delete(ref);
|
||||
if (ref.getReferenceType() == RefType.FALL_THROUGH) {
|
||||
if (!fallThroughPreserved && ref.getToAddress().equals(keepFallThroughAddr)) {
|
||||
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) {
|
||||
if (!clearingFallThroughs) {
|
||||
clearFallThroughRefs(fallThroughRef);
|
||||
setFallthroughOverride(fallThroughRef != null &&
|
||||
fallThroughRef.getReferenceType() == RefType.FALL_THROUGH);
|
||||
Address fallThroughAddr = fallThroughRef != null ? fallThroughRef.getToAddress() : null;
|
||||
clearFallThroughRefs(fallThroughAddr); // ensure there is only one fallthrough ref
|
||||
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;
|
||||
}
|
||||
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
|
||||
@@ -673,8 +751,10 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio
|
||||
if (!isFallThroughOverridden()) {
|
||||
return;
|
||||
}
|
||||
// clear fall-through override
|
||||
clearFallThroughRefs(null);
|
||||
setFallthroughOverride(false);
|
||||
addLengthOverrideFallthroughRef(); // restore length-override fallthrough if needed
|
||||
}
|
||||
finally {
|
||||
lock.release();
|
||||
@@ -693,11 +773,12 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio
|
||||
return;
|
||||
}
|
||||
if (fallThroughAddr == null) {
|
||||
// Fall-through eliminated - no reference added
|
||||
// Fall-through eliminated (i.e., terminal flow) - no reference added
|
||||
clearFallThroughRefs(null);
|
||||
setFallthroughOverride(true);
|
||||
}
|
||||
else {
|
||||
// Adding fallthough ref will trigger override flag on callback
|
||||
refMgr.addMemoryReference(address, fallThroughAddr, RefType.FALL_THROUGH,
|
||||
SourceType.USER_DEFINED, Reference.MNEMONIC);
|
||||
}
|
||||
@@ -705,7 +786,107 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio
|
||||
finally {
|
||||
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) {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user