GP-3256 Added support for Instruction length-override

This commit is contained in:
ghidra1 2023-08-28 09:17:44 -04:00
parent a820a36e94
commit aefb7f2aed
67 changed files with 1895 additions and 574 deletions

View file

@ -131,7 +131,8 @@ public class DebuggerCopyPlan {
} }
long off = ins.getMinAddress().subtract(fromRange.getMinAddress()); long off = ins.getMinAddress().subtract(fromRange.getMinAddress());
Address dest = intoAddress.add(off); Address dest = intoAddress.add(off);
intoListing.createInstruction(dest, ins.getPrototype(), ins, ins); intoListing.createInstruction(dest, ins.getPrototype(), ins, ins,
ins.getLength());
} }
} }
}, },
@ -143,8 +144,7 @@ public class DebuggerCopyPlan {
@Override @Override
public void copy(TraceProgramView from, AddressRange fromRange, Program into, public void copy(TraceProgramView from, AddressRange fromRange, Program into,
Address intoAddress, TaskMonitor monitor) Address intoAddress, TaskMonitor monitor) throws Exception {
throws Exception {
Listing intoListing = into.getListing(); Listing intoListing = into.getListing();
for (Data data : from.getListing() for (Data data : from.getListing()
.getDefinedData(new AddressSet(fromRange), true)) { .getDefinedData(new AddressSet(fromRange), true)) {
@ -204,8 +204,8 @@ public class DebuggerCopyPlan {
} }
} }
private Namespace findOrCopyNamespace(Namespace ns, SymbolTable intoTable, private Namespace findOrCopyNamespace(Namespace ns, SymbolTable intoTable, Program into)
Program into) throws Exception { throws Exception {
if (ns.isGlobal()) { if (ns.isGlobal()) {
return into.getGlobalNamespace(); return into.getGlobalNamespace();
} }
@ -442,8 +442,8 @@ public class DebuggerCopyPlan {
public boolean isRequiresInitializedMemory(TraceProgramView from, Program dest) { public boolean isRequiresInitializedMemory(TraceProgramView from, Program dest) {
return checkBoxes.entrySet().stream().anyMatch(ent -> { return checkBoxes.entrySet().stream().anyMatch(ent -> {
Copier copier = ent.getKey(); Copier copier = ent.getKey();
return copier.isRequiresInitializedMemory() && return copier.isRequiresInitializedMemory() && copier.isAvailable(from, dest) &&
copier.isAvailable(from, dest) && ent.getValue().isSelected(); ent.getValue().isSelected();
}); });
} }
} }

View file

@ -15,7 +15,7 @@
*/ */
package ghidra.trace.database.listing; package ghidra.trace.database.listing;
import static ghidra.lifecycle.Unfinished.TODO; import static ghidra.lifecycle.Unfinished.*;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.*; import java.util.*;
@ -278,11 +278,6 @@ public interface DBTraceCodeUnitAdapter extends TraceCodeUnit, MemBufferMixin {
return DBTraceCommentAdapter.arrayFromComment(getComment(commentType)); return DBTraceCommentAdapter.arrayFromComment(getComment(commentType));
} }
@Override
default boolean isSuccessor(CodeUnit codeUnit) {
return getMaxAddress().isSuccessor(codeUnit.getMinAddress());
}
@Override @Override
default boolean contains(Address testAddr) { default boolean contains(Address testAddr) {
return getMinAddress().compareTo(testAddr) <= 0 && testAddr.compareTo(getMaxAddress()) <= 0; return getMinAddress().compareTo(testAddr) <= 0 && testAddr.compareTo(getMaxAddress()) <= 0;

View file

@ -21,12 +21,14 @@ import java.nio.ByteBuffer;
import java.util.*; import java.util.*;
import db.DBRecord; import db.DBRecord;
import ghidra.program.database.code.InstructionDB;
import ghidra.program.model.address.*; import ghidra.program.model.address.*;
import ghidra.program.model.lang.*; import ghidra.program.model.lang.*;
import ghidra.program.model.listing.ContextChangeException; import ghidra.program.model.listing.ContextChangeException;
import ghidra.program.model.listing.FlowOverride; import ghidra.program.model.listing.FlowOverride;
import ghidra.program.model.mem.*; import ghidra.program.model.mem.*;
import ghidra.program.model.symbol.*; import ghidra.program.model.symbol.*;
import ghidra.program.model.util.CodeUnitInsertionException;
import ghidra.trace.database.DBTrace; import ghidra.trace.database.DBTrace;
import ghidra.trace.database.DBTraceUtils; import ghidra.trace.database.DBTraceUtils;
import ghidra.trace.database.context.DBTraceRegisterContextManager; import ghidra.trace.database.context.DBTraceRegisterContextManager;
@ -36,7 +38,7 @@ import ghidra.trace.database.guest.InternalTracePlatform;
import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree; import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree;
import ghidra.trace.database.symbol.DBTraceReference; import ghidra.trace.database.symbol.DBTraceReference;
import ghidra.trace.database.symbol.DBTraceReferenceSpace; import ghidra.trace.database.symbol.DBTraceReferenceSpace;
import ghidra.trace.model.Lifespan; import ghidra.trace.model.*;
import ghidra.trace.model.Trace.TraceInstructionChangeType; import ghidra.trace.model.Trace.TraceInstructionChangeType;
import ghidra.trace.model.guest.TracePlatform; import ghidra.trace.model.guest.TracePlatform;
import ghidra.trace.model.listing.TraceInstruction; import ghidra.trace.model.listing.TraceInstruction;
@ -52,17 +54,21 @@ import ghidra.util.database.annot.*;
* The implementation of {@link TraceInstruction} for {@link DBTrace} * The implementation of {@link TraceInstruction} for {@link DBTrace}
*/ */
@DBAnnotatedObjectInfo(version = 0) @DBAnnotatedObjectInfo(version = 0)
public class DBTraceInstruction extends AbstractDBTraceCodeUnit<DBTraceInstruction> implements public class DBTraceInstruction extends AbstractDBTraceCodeUnit<DBTraceInstruction>
TraceInstruction, InstructionAdapterFromPrototype, InstructionContext { implements TraceInstruction, InstructionAdapterFromPrototype, InstructionContext {
private static final Address[] EMPTY_ADDRESS_ARRAY = new Address[] {}; private static final Address[] EMPTY_ADDRESS_ARRAY = new Address[] {};
private static final String TABLE_NAME = "Instructions"; private static final String TABLE_NAME = "Instructions";
private static final byte FALLTHROUGH_SET_MASK = 0x01; private static final byte FALLTHROUGH_SET_MASK = 0x01;
private static final byte FALLTHROUGH_CLEAR_MASK = ~FALLTHROUGH_SET_MASK; private static final byte FALLTHROUGH_CLEAR_MASK = ~FALLTHROUGH_SET_MASK;
private static final byte FLOWOVERRIDE_SET_MASK = 0x0e; private static final byte FLOW_OVERRIDE_SET_MASK = 0x0e;
private static final byte FLOWOVERRIDE_CLEAR_MASK = ~FLOWOVERRIDE_SET_MASK; private static final byte FLOW_OVERRIDE_CLEAR_MASK = ~FLOW_OVERRIDE_SET_MASK;
private static final int FLOWOVERRIDE_SHIFT = 1; private static final int FLOW_OVERRIDE_SHIFT = 1;
private static final byte LENGTH_OVERRIDE_SET_MASK = 0x70;
private static final byte LENGTH_OVERRIDE_CLEAR_MASK = ~LENGTH_OVERRIDE_SET_MASK;
private static final int LENGTH_OVERRIDE_SHIFT = 4;
static final String PLATFORM_COLUMN_NAME = "Platform"; static final String PLATFORM_COLUMN_NAME = "Platform";
static final String PROTOTYPE_COLUMN_NAME = "Prototype"; static final String PROTOTYPE_COLUMN_NAME = "Prototype";
@ -143,6 +149,7 @@ public class DBTraceInstruction extends AbstractDBTraceCodeUnit<DBTraceInstructi
protected InstructionPrototype prototype; protected InstructionPrototype prototype;
protected FlowOverride flowOverride; protected FlowOverride flowOverride;
protected int lengthOverride;
protected ParserContext parserContext; protected ParserContext parserContext;
protected InternalTracePlatform platform; protected InternalTracePlatform platform;
@ -186,18 +193,24 @@ public class DBTraceInstruction extends AbstractDBTraceCodeUnit<DBTraceInstructi
* @param platform the platform * @param platform the platform
* @param prototype the instruction prototype * @param prototype the instruction prototype
* @param context the context for locating or creating the prototype entry * @param context the context for locating or creating the prototype entry
* @param forcedLengthOverride reduced instruction byte-length (1..7) or 0 to use default length
*/ */
protected void set(InternalTracePlatform platform, InstructionPrototype prototype, protected void set(InternalTracePlatform platform, InstructionPrototype prototype,
ProcessorContextView context) { ProcessorContextView context, int forcedLengthOverride) {
this.platformKey = platform.getIntKey(); this.platformKey = platform.getIntKey();
// NOTE: Using "this" for the MemBuffer seems a bit precarious. // NOTE: Using "this" for the MemBuffer seems a bit precarious.
DBTraceGuestLanguage languageEntry = platform == null ? null : platform.getLanguageEntry(); DBTraceGuestLanguage languageEntry = platform.getLanguageEntry();
this.prototypeKey = (int) space.manager this.prototypeKey =
.findOrRecordPrototype(prototype, languageEntry, this, context) (int) space.manager.findOrRecordPrototype(prototype, languageEntry, this, context)
.getKey(); .getKey();
this.flowOverride = FlowOverride.NONE; // flags field is already consistent this.flowOverride = FlowOverride.NONE; // flags field is already consistent
this.lengthOverride = 0;
update(PLATFORM_COLUMN, PROTOTYPE_COLUMN, FLAGS_COLUMN); update(PLATFORM_COLUMN, PROTOTYPE_COLUMN, FLAGS_COLUMN);
if (forcedLengthOverride != 0) {
updateLengthOverride(forcedLengthOverride);
}
// TODO: Can there be more in this context than the context register??? // TODO: Can there be more in this context than the context register???
doSetPlatformMapping(platform); doSetPlatformMapping(platform);
this.prototype = prototype; this.prototype = prototype;
@ -216,12 +229,19 @@ public class DBTraceInstruction extends AbstractDBTraceCodeUnit<DBTraceInstructi
} }
prototype = space.manager.getPrototypeByKey(prototypeKey); prototype = space.manager.getPrototypeByKey(prototypeKey);
if (prototype == null) { if (prototype == null) {
Msg.error(this, Msg.error(this, "Instruction table is corrupt for address " + getMinAddress() +
"Instruction table is corrupt for address " + getMinAddress() +
". Missing prototype " + prototypeKey); ". Missing prototype " + prototypeKey);
prototype = new InvalidPrototype(getTrace().getBaseLanguage()); prototype = new InvalidPrototype(getTrace().getBaseLanguage());
} }
flowOverride = FlowOverride.values()[(flags & FLOWOVERRIDE_SET_MASK) >> FLOWOVERRIDE_SHIFT]; flowOverride =
FlowOverride.values()[(flags & FLOW_OVERRIDE_SET_MASK) >> FLOW_OVERRIDE_SHIFT];
lengthOverride = (flags & LENGTH_OVERRIDE_SET_MASK) >> LENGTH_OVERRIDE_SHIFT;
if (lengthOverride != 0 && lengthOverride < prototype.getLength()) {
Address minAddr = getMinAddress();
Address newEndAddr = minAddr.add(lengthOverride - 1);
doSetRange(new AddressRangeImpl(minAddr, newEndAddr));
}
doSetPlatformMapping(platform); doSetPlatformMapping(platform);
} }
@ -334,8 +354,7 @@ public class DBTraceInstruction extends AbstractDBTraceCodeUnit<DBTraceInstructi
if (flowType.hasFallthrough()) { if (flowType.hasFallthrough()) {
try { try {
return instructionContext.getAddress() return instructionContext.getAddress()
.addNoWrap( .addNoWrap(prototype.getFallThroughOffset(instructionContext));
prototype.getFallThroughOffset(instructionContext));
} }
catch (AddressOverflowException e) { catch (AddressOverflowException e) {
return null; return null;
@ -364,6 +383,9 @@ public class DBTraceInstruction extends AbstractDBTraceCodeUnit<DBTraceInstructi
} }
return null; return null;
} }
if (lengthOverride != 0 && getFlowType().hasFallthrough()) {
return getMinAddress().add(lengthOverride);
}
return getDefaultFallThrough(); return getDefaultFallThrough();
} }
} }
@ -433,6 +455,10 @@ public class DBTraceInstruction extends AbstractDBTraceCodeUnit<DBTraceInstructi
return EMPTY_ADDRESS_ARRAY; return EMPTY_ADDRESS_ARRAY;
} }
if (lengthOverride != 0 && getFlowType().hasFallthrough()) {
list.add(getMinAddress().add(lengthOverride));
}
return list.toArray(new Address[list.size()]); return list.toArray(new Address[list.size()]);
} }
} }
@ -526,8 +552,8 @@ public class DBTraceInstruction extends AbstractDBTraceCodeUnit<DBTraceInstructi
} }
FlowType origFlowType = getFlowType(); FlowType origFlowType = getFlowType();
flags &= FLOWOVERRIDE_CLEAR_MASK; flags &= FLOW_OVERRIDE_CLEAR_MASK;
flags |= (flowOverride.ordinal() << FLOWOVERRIDE_SHIFT) & FLOWOVERRIDE_SET_MASK; flags |= (flowOverride.ordinal() << FLOW_OVERRIDE_SHIFT) & FLOW_OVERRIDE_SET_MASK;
this.flowOverride = flowOverride; this.flowOverride = flowOverride;
update(FLAGS_COLUMN); update(FLAGS_COLUMN);
@ -545,8 +571,91 @@ public class DBTraceInstruction extends AbstractDBTraceCodeUnit<DBTraceInstructi
} }
} }
space.trace.setChanged( space.trace.setChanged(
new TraceChangeRecord<>(TraceInstructionChangeType.FLOW_OVERRIDE_CHANGED, new TraceChangeRecord<>(TraceInstructionChangeType.FLOW_OVERRIDE_CHANGED, space, this,
space, this, oldFlowOverride, flowOverride)); oldFlowOverride, flowOverride));
}
@Override
public void setLengthOverride(int length) throws CodeUnitInsertionException {
int oldLengthOverride = this.lengthOverride;
try (LockHold hold = space.trace.lockWrite()) {
checkDeleted();
InstructionPrototype proto = getPrototype();
length = InstructionDB.checkLengthOverride(length, proto);
if (length == lengthOverride) {
return; // no change
}
int newLength = length != 0 ? length : proto.getLength();
if (newLength > getLength()) {
Address minAddr = getMinAddress();
Address newEndAddr = minAddr.add(newLength - 1);
TraceAddressSnapRange tasr = new ImmutableTraceAddressSnapRange(
new AddressRangeImpl(minAddr.next(), newEndAddr), getLifespan());
for (AbstractDBTraceCodeUnit<?> cu : space.definedUnits.getIntersecting(tasr)) {
if (cu != this) {
throw new CodeUnitInsertionException(
"Length override of " + newLength + " conflicts with code unit at " +
cu.getMinAddress() + ", lifespan=" + cu.getLifespan());
}
}
}
updateLengthOverride(length);
}
space.trace.setChanged(
new TraceChangeRecord<>(TraceInstructionChangeType.LENGTH_OVERRIDE_CHANGED, space, this,
oldLengthOverride, length));
}
private void updateLengthOverride(int length) {
flags &= LENGTH_OVERRIDE_CLEAR_MASK;
flags |= (length << LENGTH_OVERRIDE_SHIFT);
lengthOverride = length;
update(FLAGS_COLUMN);
int newLength = length != 0 ? length : getPrototype().getLength();
Address minAddr = getMinAddress();
Address newEndAddr = minAddr.add(newLength - 1);
doSetRange(new AddressRangeImpl(minAddr, newEndAddr));
}
@Override
public boolean isLengthOverridden() {
return lengthOverride != 0;
}
@Override
public int getLength() {
if (lengthOverride != 0) {
return lengthOverride;
}
return super.getLength();
}
@Override
public int getParsedLength() {
if (lengthOverride == 0) {
return super.getLength();
}
return getPrototype().getLength();
}
@Override
public byte[] getParsedBytes() throws MemoryAccessException {
if (!isLengthOverridden()) {
return getBytes();
}
try (LockHold hold = LockHold.lock(space.lock.readLock())) {
checkIsValid();
int len = getPrototype().getLength();
byte[] b = new byte[len];
Address addr = getAddress();
if (len != getMemory().getBytes(addr, b)) {
throw new MemoryAccessException("Failed to read " + len + " bytes at " + addr);
}
return b;
}
} }
@Override @Override
@ -622,8 +731,8 @@ public class DBTraceInstruction extends AbstractDBTraceCodeUnit<DBTraceInstructi
} }
update(FLAGS_COLUMN); update(FLAGS_COLUMN);
space.trace.setChanged( space.trace.setChanged(
new TraceChangeRecord<>(TraceInstructionChangeType.FALL_THROUGH_OVERRIDE_CHANGED, new TraceChangeRecord<>(TraceInstructionChangeType.FALL_THROUGH_OVERRIDE_CHANGED, space,
space, this, !overridden, overridden)); this, !overridden, overridden));
} }
@Override @Override

View file

@ -59,9 +59,10 @@ public class DBTraceInstructionsMemoryView
@Override @Override
public DBTraceInstruction create(Lifespan lifespan, Address address, public DBTraceInstruction create(Lifespan lifespan, Address address,
TracePlatform platform, InstructionPrototype prototype, TracePlatform platform, InstructionPrototype prototype,
ProcessorContextView context) throws CodeUnitInsertionException { ProcessorContextView context, int forcedLengthOverride)
throws CodeUnitInsertionException {
return delegateWrite(address.getAddressSpace(), return delegateWrite(address.getAddressSpace(),
m -> m.create(lifespan, address, platform, prototype, context)); m -> m.create(lifespan, address, platform, prototype, context, forcedLengthOverride));
} }
@Override @Override

View file

@ -19,6 +19,7 @@ import java.util.*;
import org.apache.commons.lang3.tuple.Pair; import org.apache.commons.lang3.tuple.Pair;
import ghidra.program.database.code.InstructionDB;
import ghidra.program.model.address.*; import ghidra.program.model.address.*;
import ghidra.program.model.lang.*; import ghidra.program.model.lang.*;
import ghidra.program.model.lang.InstructionError.InstructionErrorType; import ghidra.program.model.lang.InstructionError.InstructionErrorType;
@ -103,16 +104,22 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView
protected boolean isSuitable(Instruction candidate, Instruction protoInstr) { protected boolean isSuitable(Instruction candidate, Instruction protoInstr) {
try { try {
return candidate.getPrototype().equals(protoInstr.getPrototype()) && return candidate.getPrototype().equals(protoInstr.getPrototype()) &&
Arrays.equals(candidate.getBytes(), protoInstr.getBytes()) &&
candidate.isFallThroughOverridden() == protoInstr.isFallThroughOverridden() && candidate.isFallThroughOverridden() == protoInstr.isFallThroughOverridden() &&
Objects.equals(candidate.getFallThrough(), protoInstr.getFallThrough()) && Objects.equals(candidate.getFallThrough(), protoInstr.getFallThrough()) &&
candidate.getFlowOverride() == protoInstr.getFlowOverride(); candidate.getFlowOverride() == protoInstr.getFlowOverride() &&
candidate.getLength() == protoInstr.getLength() && // handles potential length override
hasSameBytes(candidate, protoInstr);
} }
catch (MemoryAccessException e) { catch (MemoryAccessException e) {
throw new AssertionError(e); throw new AssertionError(e);
} }
} }
private boolean hasSameBytes(Instruction instr1, Instruction instr2)
throws MemoryAccessException {
return Arrays.equals(instr1.getParsedBytes(), instr2.getParsedBytes());
}
protected Instruction doAdjustExisting(Address address, Instruction protoInstr) protected Instruction doAdjustExisting(Address address, Instruction protoInstr)
throws AddressOverflowException, CancelledException, CodeUnitInsertionException { throws AddressOverflowException, CancelledException, CodeUnitInsertionException {
DBTraceInstruction exists = getAt(lifespan.lmin(), address); DBTraceInstruction exists = getAt(lifespan.lmin(), address);
@ -193,8 +200,8 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView
return prec; return prec;
} }
Instruction created = Instruction created = doCreate(lifespan, address, platform,
doCreate(lifespan, address, platform, protoInstr.getPrototype(), protoInstr); protoInstr.getPrototype(), protoInstr, protoInstr.getLength());
// copy override settings to replacement instruction // copy override settings to replacement instruction
if (protoInstr.isFallThroughOverridden()) { if (protoInstr.isFallThroughOverridden()) {
created.setFallThrough(protoInstr.getFallThrough()); created.setFallThrough(protoInstr.getFallThrough());
@ -336,20 +343,30 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView
* @param platform the platform (language, compiler) for the instruction * @param platform the platform (language, compiler) for the instruction
* @param prototype the instruction's prototype * @param prototype the instruction's prototype
* @param context the initial context for parsing the instruction * @param context the initial context for parsing the instruction
* @return the new instructions * @param length instruction byte-length (must be in the range 0..prototype.getLength()).
* @throws CodeUnitInsertionException if the instruction cannot be created due to an existing * If smaller than the prototype length it must have a value no greater than 7, otherwise
* unit * an error will be thrown. A value of 0 or greater-than-or-equal the prototype length
* will be ignored and not impose and override length. The length value must be a multiple
* of the {@link Language#getInstructionAlignment() instruction alignment} .
* @return the newly created instruction.
* @throws CodeUnitInsertionException thrown if the new Instruction would overlap and
* existing {@link CodeUnit} or the specified {@code length} is unsupported.
* @throws IllegalArgumentException if a negative {@code length} is specified.
* @throws AddressOverflowException if the instruction would fall off the address space * @throws AddressOverflowException if the instruction would fall off the address space
*/ */
protected DBTraceInstruction doCreate(Lifespan lifespan, Address address, protected DBTraceInstruction doCreate(Lifespan lifespan, Address address,
InternalTracePlatform platform, InstructionPrototype prototype, InternalTracePlatform platform, InstructionPrototype prototype,
ProcessorContextView context) ProcessorContextView context, int length)
throws CodeUnitInsertionException, AddressOverflowException { throws CodeUnitInsertionException, AddressOverflowException {
if (platform.getLanguage() != prototype.getLanguage()) { if (platform.getLanguage() != prototype.getLanguage()) {
throw new IllegalArgumentException("Platform and prototype disagree in language"); throw new IllegalArgumentException("Platform and prototype disagree in language");
} }
Address endAddress = address.addNoWrap(prototype.getLength() - 1); int forcedLengthOverride = InstructionDB.checkLengthOverride(length, prototype);
if (length == 0) {
length = prototype.getLength();
}
Address endAddress = address.addNoWrap(length - 1);
AddressRangeImpl createdRange = new AddressRangeImpl(address, endAddress); AddressRangeImpl createdRange = new AddressRangeImpl(address, endAddress);
// Truncate, then check that against existing code units. // Truncate, then check that against existing code units.
@ -365,7 +382,7 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView
doSetContext(tasr, prototype.getLanguage(), context); doSetContext(tasr, prototype.getLanguage(), context);
DBTraceInstruction created = space.instructionMapSpace.put(tasr, null); DBTraceInstruction created = space.instructionMapSpace.put(tasr, null);
created.set(platform, prototype, context); created.set(platform, prototype, context, forcedLengthOverride);
cacheForContaining.notifyNewEntry(tasr.getLifespan(), createdRange, created); cacheForContaining.notifyNewEntry(tasr.getLifespan(), createdRange, created);
cacheForSequence.notifyNewEntry(tasr.getLifespan(), createdRange, created); cacheForSequence.notifyNewEntry(tasr.getLifespan(), createdRange, created);
@ -379,14 +396,14 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView
@Override @Override
public DBTraceInstruction create(Lifespan lifespan, Address address, TracePlatform platform, public DBTraceInstruction create(Lifespan lifespan, Address address, TracePlatform platform,
InstructionPrototype prototype, ProcessorContextView context) InstructionPrototype prototype, ProcessorContextView context, int forcedLengthOverride)
throws CodeUnitInsertionException { throws CodeUnitInsertionException {
InternalTracePlatform dbPlatform = space.manager.platformManager.assertMine(platform); InternalTracePlatform dbPlatform = space.manager.platformManager.assertMine(platform);
try (LockHold hold = LockHold.lock(space.lock.writeLock())) { try (LockHold hold = LockHold.lock(space.lock.writeLock())) {
DBTraceInstruction created = DBTraceInstruction created =
doCreate(lifespan, address, dbPlatform, prototype, context); doCreate(lifespan, address, dbPlatform, prototype, context, forcedLengthOverride);
space.trace.setChanged(new TraceChangeRecord<>(TraceCodeChangeType.ADDED, space.trace.setChanged(
space, created, created)); new TraceChangeRecord<>(TraceCodeChangeType.ADDED, space, created, created));
return created; return created;
} }
catch (AddressOverflowException e) { catch (AddressOverflowException e) {
@ -567,9 +584,9 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView
if (lastInstruction != null) { if (lastInstruction != null) {
Address maxAddress = DBTraceCodeManager.instructionMax(lastInstruction, true); Address maxAddress = DBTraceCodeManager.instructionMax(lastInstruction, true);
result.addRange(block.getStartAddress(), maxAddress); result.addRange(block.getStartAddress(), maxAddress);
space.trace.setChanged(new TraceChangeRecord<>(TraceCodeChangeType.ADDED, space.trace.setChanged(new TraceChangeRecord<>(TraceCodeChangeType.ADDED, space,
space, new ImmutableTraceAddressSnapRange( new ImmutableTraceAddressSnapRange(block.getStartAddress(), maxAddress,
block.getStartAddress(), maxAddress, lifespan))); lifespan)));
} }
} }
return result; return result;

View file

@ -708,18 +708,19 @@ public abstract class AbstractDBTraceProgramViewListing implements TraceProgramV
} }
@Override @Override
@SuppressWarnings("rawtypes") public PropertyMap<?> getPropertyMap(String propertyName) {
public PropertyMap getPropertyMap(String propertyName) {
// TODO Auto-generated method stub // TODO Auto-generated method stub
return null; return null;
} }
@Override @Override
public Instruction createInstruction(Address addr, InstructionPrototype prototype, public Instruction createInstruction(Address addr, InstructionPrototype prototype,
MemBuffer memBuf, ProcessorContextView context) throws CodeUnitInsertionException { MemBuffer memBuf, ProcessorContextView context, int forcedLengthOverride)
throws CodeUnitInsertionException {
// TODO: Why memBuf? Can it vary from program memory? // TODO: Why memBuf? Can it vary from program memory?
return codeOperations.instructions() return codeOperations.instructions()
.create(Lifespan.nowOn(program.snap), addr, platform, prototype, context); .create(Lifespan.nowOn(program.snap), addr, platform, prototype, context,
forcedLengthOverride);
} }
@Override @Override

View file

@ -129,6 +129,8 @@ public class DBTraceProgramView implements TraceProgramView {
listenFor(TraceDataTypeChangeType.RENAMED, this::dataTypeRenamed); listenFor(TraceDataTypeChangeType.RENAMED, this::dataTypeRenamed);
listenFor(TraceDataTypeChangeType.DELETED, this::dataTypeDeleted); listenFor(TraceDataTypeChangeType.DELETED, this::dataTypeDeleted);
listenFor(TraceInstructionChangeType.LENGTH_OVERRIDE_CHANGED,
this::instructionLengthOverrideChanged);
listenFor(TraceInstructionChangeType.FLOW_OVERRIDE_CHANGED, listenFor(TraceInstructionChangeType.FLOW_OVERRIDE_CHANGED,
this::instructionFlowOverrideChanged); this::instructionFlowOverrideChanged);
listenFor(TraceInstructionChangeType.FALL_THROUGH_OVERRIDE_CHANGED, listenFor(TraceInstructionChangeType.FALL_THROUGH_OVERRIDE_CHANGED,
@ -466,7 +468,17 @@ public class DBTraceProgramView implements TraceProgramView {
return; return;
} }
queues.fireEvent(new ProgramChangeRecord(ChangeManager.DOCR_FALLTHROUGH_CHANGED, queues.fireEvent(new ProgramChangeRecord(ChangeManager.DOCR_FALLTHROUGH_CHANGED,
instruction.getMinAddress(), instruction.getMaxAddress(), null, null, null)); instruction.getMinAddress(), instruction.getMinAddress(), null, null, null));
}
private void instructionLengthOverrideChanged(TraceAddressSpace space,
TraceInstruction instruction, int oldLengthOverride, int newLengthOverride) {
DomainObjectEventQueues queues = isCodeVisible(space, instruction);
if (queues == null) {
return;
}
queues.fireEvent(new ProgramChangeRecord(ChangeManager.DOCR_LENGTH_OVERRIDE_CHANGED,
instruction.getMinAddress(), instruction.getMinAddress(), null, null, null));
} }
private void memoryBytesChanged(TraceAddressSpace space, TraceAddressSnapRange range, private void memoryBytesChanged(TraceAddressSpace space, TraceAddressSnapRange range,

View file

@ -235,6 +235,8 @@ public interface Trace extends DataTypeManagerDomainObject {
new TraceInstructionChangeType<>(); new TraceInstructionChangeType<>();
public static final TraceInstructionChangeType<Boolean> FALL_THROUGH_OVERRIDE_CHANGED = public static final TraceInstructionChangeType<Boolean> FALL_THROUGH_OVERRIDE_CHANGED =
new TraceInstructionChangeType<>(); new TraceInstructionChangeType<>();
public static final TraceInstructionChangeType<Integer> LENGTH_OVERRIDE_CHANGED =
new TraceInstructionChangeType<>();
} }
public static final class TraceMemoryBytesChangeType public static final class TraceMemoryBytesChangeType

View file

@ -37,11 +37,12 @@ public interface TraceInstructionsView extends TraceBaseDefinedUnitsView<TraceIn
* @param platform the platform * @param platform the platform
* @param prototype the instruction prototype * @param prototype the instruction prototype
* @param context the input disassembly context for the instruction * @param context the input disassembly context for the instruction
* @param forcedLengthOverride reduced instruction byte-length (1..7) or 0 to use default length
* @return the new instruction * @return the new instruction
* @throws CodeUnitInsertionException if the instruction cannot be created * @throws CodeUnitInsertionException if the instruction cannot be created
*/ */
TraceInstruction create(Lifespan lifespan, Address address, TracePlatform platform, TraceInstruction create(Lifespan lifespan, Address address, TracePlatform platform,
InstructionPrototype prototype, ProcessorContextView context) InstructionPrototype prototype, ProcessorContextView context, int forcedLengthOverride)
throws CodeUnitInsertionException; throws CodeUnitInsertionException;
/** /**
@ -50,10 +51,10 @@ public interface TraceInstructionsView extends TraceBaseDefinedUnitsView<TraceIn
* @see #create(Lifespan, Address, TracePlatform, InstructionPrototype, ProcessorContextView) * @see #create(Lifespan, Address, TracePlatform, InstructionPrototype, ProcessorContextView)
*/ */
default TraceInstruction create(Lifespan lifespan, Address address, default TraceInstruction create(Lifespan lifespan, Address address,
InstructionPrototype prototype, ProcessorContextView context) InstructionPrototype prototype, ProcessorContextView context, int forcedLengthOverride)
throws CodeUnitInsertionException { throws CodeUnitInsertionException {
return create(lifespan, address, getTrace().getPlatformManager().getHostPlatform(), return create(lifespan, address, getTrace().getPlatformManager().getHostPlatform(),
prototype, context); prototype, context, forcedLengthOverride);
} }
/** /**

View file

@ -564,7 +564,8 @@ public class ToyDBTraceBuilder implements AutoCloseable {
InstructionBlock block = dis.pseudoDisassembleBlock(memBuf, defaultContextValue, 1); InstructionBlock block = dis.pseudoDisassembleBlock(memBuf, defaultContextValue, 1);
Instruction pseudoIns = block.iterator().next(); Instruction pseudoIns = block.iterator().next();
return code.instructions() return code.instructions()
.create(Lifespan.nowOn(snap), start, platform, pseudoIns.getPrototype(), pseudoIns); .create(Lifespan.nowOn(snap), start, platform, pseudoIns.getPrototype(), pseudoIns,
0);
} }
/** /**

View file

@ -468,9 +468,6 @@ public class DBTraceCodeUnitTest extends AbstractGhidraHeadlessIntegrationTest
d4008 = b.addData(0, b.addr(0x4008), LongDataType.dataType, b.buf(1, 2, 3, 4)); d4008 = b.addData(0, b.addr(0x4008), LongDataType.dataType, b.buf(1, 2, 3, 4));
} }
assertTrue(i4004.isSuccessor(i4006));
assertFalse(i4004.isSuccessor(d4008));
assertFalse(i4004.contains(b.addr(0x4003))); assertFalse(i4004.contains(b.addr(0x4003)));
assertTrue(i4004.contains(b.addr(0x4004))); assertTrue(i4004.contains(b.addr(0x4004)));
assertTrue(i4004.contains(b.addr(0x4005))); assertTrue(i4004.contains(b.addr(0x4005)));

View file

@ -220,6 +220,8 @@ src/main/help/help/topics/DbViewerPlugin/images/DatabaseViewer.png||GHIDRA||||EN
src/main/help/help/topics/DisassembledViewPlugin/DisassembledViewPlugin.htm||GHIDRA||||END| src/main/help/help/topics/DisassembledViewPlugin/DisassembledViewPlugin.htm||GHIDRA||||END|
src/main/help/help/topics/DisassembledViewPlugin/images/DisassembledViewPluginMain.png||GHIDRA||||END| src/main/help/help/topics/DisassembledViewPlugin/images/DisassembledViewPluginMain.png||GHIDRA||||END|
src/main/help/help/topics/DisassemblerPlugin/Disassembly.htm||GHIDRA||||END| src/main/help/help/topics/DisassemblerPlugin/Disassembly.htm||GHIDRA||||END|
src/main/help/help/topics/DisassemblerPlugin/images/LengthOverrideLockPrefixExample.png||GHIDRA||||END|
src/main/help/help/topics/DisassemblerPlugin/images/LengthOverrideLockPrefixExample2.png||GHIDRA||||END|
src/main/help/help/topics/DisassemblerPlugin/images/ProcessorOptions.png||GHIDRA||||END| src/main/help/help/topics/DisassemblerPlugin/images/ProcessorOptions.png||GHIDRA||||END|
src/main/help/help/topics/DockingWindows/Docking_Windows.htm||GHIDRA||||END| src/main/help/help/topics/DockingWindows/Docking_Windows.htm||GHIDRA||||END|
src/main/help/help/topics/DockingWindows/images/Tool.png||GHIDRA||||END| src/main/help/help/topics/DockingWindows/images/Tool.png||GHIDRA||||END|

View file

@ -25,6 +25,14 @@
<LI><A href="#Disassemble_Restricted">Restricted Disassembly</A></LI> <LI><A href="#Disassemble_Restricted">Restricted Disassembly</A></LI>
<LI><A href="#Disassemble_Arm">Disassemble ARM / Thumb</A></LI>
<LI><A href="#ProcessorOptions">Processor Options</A></LI>
<LI><A href="#Modify_Instruction_Flow">Modify Instruction Flow</A></LI>
<LI><A href="#Modify_Instruction_Length">Modify Instruction Length</A></LI>
</UL> </UL>
<H2><A name="Disassemble"></A>Disassembly</H2> <H2><A name="Disassemble"></A>Disassembly</H2>
@ -131,7 +139,7 @@
undefined bytes.</I></P> undefined bytes.</I></P>
</BLOCKQUOTE> </BLOCKQUOTE>
<H2><A name="Disassemble_Restricted"></A>Disassembly (Restricted)</H2> <H2><A name="Disassemble_Restricted"></A>Restricted Disassembly</H2>
<BLOCKQUOTE> <BLOCKQUOTE>
<P><I>Restricted Disassembly</I> is similar to <I>Disassembly</I>, except that only bytes in <P><I>Restricted Disassembly</I> is similar to <I>Disassembly</I>, except that only bytes in
@ -157,13 +165,11 @@
undefined bytes.</I></P> undefined bytes.</I></P>
</BLOCKQUOTE> </BLOCKQUOTE>
<P class="providedbyplugin">&nbsp;</P> <H2><A name="Disassemble_Arm"></A>Disassemble ARM / Thumb<A name=
<H2><A name="Disassemble_Arm"></A>Disassemble ARM / Disassemble Thumb<A name=
"Disassemble_Thumb"></A></H2> "Disassemble_Thumb"></A></H2>
<BLOCKQUOTE> <BLOCKQUOTE>
<P><I>Disassemble ARM and Disassemble Thumb</I> will only be available if the program you are <P><I>Disassemble ARM and Disassemble Thumb actions</I> will only be available if the program you are
working with is an ARM based processor.&nbsp; ARM processors have two states, ARM and Thumb working with is an ARM based processor.&nbsp; ARM processors have two states, ARM and Thumb
mode.&nbsp; The instructions available in ARM mode are 4 bytes long.&nbsp; In Thumb mode, the mode.&nbsp; The instructions available in ARM mode are 4 bytes long.&nbsp; In Thumb mode, the
instructions are "generally" 2 bytes long.&nbsp; ARM and Thumb mode are mutually exclusive, instructions are "generally" 2 bytes long.&nbsp; ARM and Thumb mode are mutually exclusive,
@ -199,38 +205,6 @@
undefined bytes.</I></P> undefined bytes.</I></P>
</BLOCKQUOTE> </BLOCKQUOTE>
<H2><A name="Modify_Instruction_Flow"></A>Modify Instruction Flow</H2>
<BLOCKQUOTE>
<P>With certain processors and situations it may be desirable to modify the default
flow of an instruction to better reflect the nature of its flow. For example a jump may
actually be performing a call type operation, a call may be performing a long-jump.
This distinction primarily affects the subroutine models and flow analysis performed
within Ghidra.</P>
<P>The following basic flow types may be imposed upon the default flow of an instruction:</P>
<UL>
<LI>BRANCH</LI>
<LI>CALL</LI>
<LI>CALL_RETURN</LI>
<LI>RETURN</LI>
</UL>
<P>In all situations the conditional nature of the original flow is perserved.</P>
<P>To Modify Instruction Flow:</P>
<OL>
<LI>Place the cursor on an instruction within the Code Browser. Note that
instructions which are purely fall-through can not be modified.</LI>
<LI>Right-mouse-click</LI>
<LI>Select <I>Modify Instruction Flow...</I> menu item.
<LI>Within the <I>Modify Instruction Flow</I> dialog select the desired basic flow
behavior.</LI>
<LI>Click OK in the dialog</LI>
</OL>
<P align="left"><I><IMG src="help/shared/tip.png">An instruction whose flow has been
modified will have its' mnemonic color modified.</I></P>
</BLOCKQUOTE>
<H2><A name="ProcessorOptions"></A>Processor Options</H2> <H2><A name="ProcessorOptions"></A>Processor Options</H2>
<BLOCKQUOTE> <BLOCKQUOTE>
@ -244,11 +218,6 @@
<DIV align="center"> <DIV align="center">
<IMG src="images/ProcessorOptions.png"><BR> <IMG src="images/ProcessorOptions.png"><BR>
<BR> <BR>
<DIV align="left">
<BR>
</DIV>
</DIV> </DIV>
<P>The options are <P>The options are
@ -282,8 +251,90 @@
<LI><A href="help/topics/LanguageProviderPlugin/Languages.htm">Languages</A></LI> <LI><A href="help/topics/LanguageProviderPlugin/Languages.htm">Languages</A></LI>
</UL> </UL>
<P>&nbsp;</P>
<BR> <BR>
<H2><A name="Modify_Instruction_Flow"></A>Modify Instruction Flow</H2>
<BLOCKQUOTE>
<P>With certain processors and situations it may be desirable to modify the default
flow of an instruction to better reflect the nature of its flow. For example a jump may
actually be performing a call type operation, a call may be performing a long-jump.
This distinction primarily affects the subroutine models and flow analysis performed
within Ghidra.</P>
<P>The following basic flow types may be imposed upon the default flow of an instruction:</P>
<UL>
<LI>BRANCH</LI>
<LI>CALL</LI>
<LI>CALL_RETURN</LI>
<LI>RETURN</LI>
</UL>
<P>In all situations the conditional nature of the original flow is perserved.</P>
<P>To Modify Instruction Flow:</P>
<OL>
<LI>Place the cursor on an instruction within the Code Browser. Note that
instructions which are purely fall-through can not be modified.</LI>
<LI>Right-mouse-click</LI>
<LI>Select <I>Modify Instruction Flow...</I> menu item.
<LI>Within the <I>Modify Instruction Flow</I> dialog select the desired basic flow
behavior.</LI>
<LI>Click OK in the dialog</LI>
</OL>
<P align="left"><I><IMG src="help/shared/tip.png">An instruction whose flow has been
modified will have its' mnemonic color modified.</I></P>
</BLOCKQUOTE>
<H2><A name="Modify_Instruction_Length"></A>Modify Instruction Length</H2>
<BLOCKQUOTE>
<P>There are certain situations where code may flow into an offcut location within the
middle of another instruction where the bytes happen to form a different instruction.
While this generally indicates a bad flow, this can also be a legitimate situation
of an overlapping instruction. For example, with x86 instructions where a flow
may bypass a LOCK prefix byte. Depending on which flow disassembles first, the situation
may manifest differently. Below is an example of the x86 LOCK prefix case. This situation can be
quickly identified by the error bookmark along with the offcut reference. In this case the
<code>JZ</code> instruction has two flows: 1) one falls-through and 2) conditionally jumps
around the LOCK prefix byte resulting in an offcut flow and disassembly conflict.</P>
<BR>
<DIV align="center">
<IMG src="images/LengthOverrideLockPrefixExample.png"><BR>
<BR>
</DIV>
<P>The above case can be resolved by overriding the code-unit length of the first
instruction to 1-byte allowing the incomplete disassembly of the offcut instruction
to be repaired.</P>
<OL>
<LI>Place the cursor on an instruction within the Code Browser (e.g.,
<code>CMPXCHG</code> instruction).</LI>
<LI>Right-mouse-click</LI>
<LI>Select <I>Modify Instruction Length...</I> menu item.
<LI>Within the <I>Modify Instruction Length</I> dialog enter the reduced instruction
length (e.g., 1 in this case). Note that this does not impact the number of bytes
actually parsed, but only the affective code unit length.</LI>
<LI>Click OK in the dialog</LI>
<LI>This should result in the subsequent locations becoming undefined code units
which can know be disassembled. Click on the first undefined location and disassemble
(i.e., <B>D</B> key binding).</LI>
<LI>You may also delete the error bookmark which should no longer be relavent.
</OL>
<P>The image below shows this same code after these length override steps have been
performed. The fallthrough of the first instruction, whose length was overriden from four
to one, is preserved and both instructions fallthrough to the same <code>JNZ</code>
instruction.
</P>
<BR>
<DIV align="center">
<IMG src="images/LengthOverrideLockPrefixExample2.png"><BR>
<BR>
</DIV>
</BLOCKQUOTE>
</BODY> </BODY>
</HTML> </HTML>

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

View file

@ -68,29 +68,32 @@ public abstract class CodeUnitDetails {
boolean removedFallThrough = boolean removedFallThrough =
inst.isFallThroughOverridden() && (inst.getFallThrough() == null); inst.isFallThroughOverridden() && (inst.getFallThrough() == null);
boolean hasFlowOverride = inst.getFlowOverride() != FlowOverride.NONE; boolean hasFlowOverride = inst.getFlowOverride() != FlowOverride.NONE;
boolean hasLengthOverride = inst.isLengthOverridden();
cuRep = cu.toString(); cuRep = cu.toString();
if (removedFallThrough) { if (removedFallThrough) {
cuRep += cuRep += NEW_LINE + indent + getSpaces(addrRangeStr.length()) + " " +
NEW_LINE + indent + getSpaces(addrRangeStr.length()) + " " +
"Removed FallThrough"; "Removed FallThrough";
} }
else if (inst.isFallThroughOverridden()) { else if (inst.isFallThroughOverridden()) {
Reference[] refs = cu.getReferencesFrom(); Reference[] refs = cu.getReferencesFrom();
// Show the fallthrough override. // Show the fallthrough override.
for (int i = 0; i < refs.length; i++) { for (Reference ref : refs) {
if (refs[i].getReferenceType().isFallthrough()) { if (ref.getReferenceType().isFallthrough()) {
cuRep += cuRep += NEW_LINE + indent + getSpaces(addrRangeStr.length()) + " " +
NEW_LINE + indent + getSpaces(addrRangeStr.length()) + " " +
"FallThrough Override: " + "FallThrough Override: " +
DiffUtility.getUserToAddressString(inst.getProgram(), refs[i]); DiffUtility.getUserToAddressString(inst.getProgram(), ref);
} }
} }
} }
if (hasFlowOverride) { if (hasFlowOverride) {
cuRep += cuRep += NEW_LINE + indent + getSpaces(addrRangeStr.length()) + " " +
NEW_LINE + indent + getSpaces(addrRangeStr.length()) + " " +
"Flow Override: " + inst.getFlowOverride(); "Flow Override: " + inst.getFlowOverride();
} }
if (hasLengthOverride) {
cuRep += NEW_LINE + indent + getSpaces(addrRangeStr.length()) + " " +
"Length Override: " + inst.getLength() + " (actual length is " +
inst.getParsedLength() + ")";
}
// Commented the following out, since we may want the hash code in the future. // Commented the following out, since we may want the hash code in the future.
// cuRep += // cuRep +=
// STANDARD_NEW_LINE + indent + getSpaces(addrRangeStr.length()) + " " + // STANDARD_NEW_LINE + indent + getSpaces(addrRangeStr.length()) + " " +
@ -146,15 +149,15 @@ public abstract class CodeUnitDetails {
return indent + "None"; return indent + "None";
} }
StringBuffer buf = new StringBuffer(); StringBuffer buf = new StringBuffer();
for (int i = 0; i < refs.length; i++) { for (Reference ref : refs) {
if (refs[i].isExternalReference()) { if (ref.isExternalReference()) {
buf.append(indent + "External Reference " + getRefInfo(pgm, refs[i]) + NEW_LINE); buf.append(indent + "External Reference " + getRefInfo(pgm, ref) + NEW_LINE);
} }
else if (refs[i].isStackReference()) { else if (ref.isStackReference()) {
buf.append(indent + "Stack Reference " + getRefInfo(pgm, refs[i]) + NEW_LINE); buf.append(indent + "Stack Reference " + getRefInfo(pgm, ref) + NEW_LINE);
} }
else { else {
buf.append(indent + "Reference " + getRefInfo(pgm, refs[i]) + NEW_LINE); buf.append(indent + "Reference " + getRefInfo(pgm, ref) + NEW_LINE);
} }
} }
return buf.toString(); return buf.toString();

View file

@ -858,7 +858,13 @@ class CodeUnitMerger extends AbstractListingMerger {
private void performMergeInstruction(Instruction instruction, boolean copyBytes) private void performMergeInstruction(Instruction instruction, boolean copyBytes)
throws CodeUnitInsertionException, MemoryAccessException { throws CodeUnitInsertionException, MemoryAccessException {
Address minAddress = instruction.getMinAddress(); Address minAddress = instruction.getMinAddress();
Address maxAddress = instruction.getMaxAddress(); Address maxAddress;
if (instruction.isLengthOverridden()) {
maxAddress = minAddress.add(instruction.getParsedLength() - 1);
}
else {
maxAddress = instruction.getMaxAddress();
}
Program fromPgm = instruction.getProgram(); Program fromPgm = instruction.getProgram();
// Code unit should already be cleared where this instruction needs to go. // Code unit should already be cleared where this instruction needs to go.
Listing resultListing = resultPgm.getListing(); Listing resultListing = resultPgm.getListing();
@ -868,9 +874,12 @@ class CodeUnitMerger extends AbstractListingMerger {
ProgramMemoryUtil.copyBytesInRanges(resultPgm, fromPgm, minAddress, maxAddress); ProgramMemoryUtil.copyBytesInRanges(resultPgm, fromPgm, minAddress, maxAddress);
} }
// avoid forcing length of new instruction if old instruction length was not overriden
int lengthOverride = instruction.isLengthOverridden() ? instruction.getLength() : 0;
Instruction inst = resultListing.createInstruction(minAddress, instruction.getPrototype(), Instruction inst = resultListing.createInstruction(minAddress, instruction.getPrototype(),
new DumbMemBufferImpl(resultPgm.getMemory(), minAddress), new DumbMemBufferImpl(resultPgm.getMemory(), minAddress),
new ProgramProcessorContext(resultPgm.getProgramContext(), minAddress)); new ProgramProcessorContext(resultPgm.getProgramContext(), minAddress), lengthOverride);
// Set the fallthrough override if necessary. // Set the fallthrough override if necessary.
if (instruction.isFallThroughOverridden()) { if (instruction.isFallThroughOverridden()) {

View file

@ -570,7 +570,6 @@ public class ListingMergeManager implements MergeResolver, ListingMergeConstants
* determine the conflicts. * determine the conflicts.
* @param mergers the listing mergers whose conflicts are to be merged. * @param mergers the listing mergers whose conflicts are to be merged.
* @param monitor indicates progress to user and allows cancel. * @param monitor indicates progress to user and allows cancel.
* @throws ProgramConflictException if programs can't be compared using Diff.
* @throws MemoryAccessException if bytes can't be merged. * @throws MemoryAccessException if bytes can't be merged.
* @throws CancelledException if the user cancels the merge. * @throws CancelledException if the user cancels the merge.
*/ */

View file

@ -445,6 +445,7 @@ public class AutoAnalysisManager implements DomainObjectListener {
break; break;
case ChangeManager.DOCR_FALLTHROUGH_CHANGED: case ChangeManager.DOCR_FALLTHROUGH_CHANGED:
case ChangeManager.DOCR_FLOWOVERRIDE_CHANGED: case ChangeManager.DOCR_FLOWOVERRIDE_CHANGED:
case ChangeManager.DOCR_LENGTH_OVERRIDE_CHANGED:
// TODO: not sure if this should be done this way or explicitly // TODO: not sure if this should be done this way or explicitly
// via the application commands (this is inconsistent with other // via the application commands (this is inconsistent with other
// codeDefined cases which do not rely on change events (e.g., disassembly) // codeDefined cases which do not rely on change events (e.g., disassembly)

View file

@ -88,6 +88,7 @@ public class DisassemblerPlugin extends Plugin {
private DockingAction x86_64DisassembleAction; private DockingAction x86_64DisassembleAction;
private DockingAction x86_32DisassembleAction; private DockingAction x86_32DisassembleAction;
private DockingAction setFlowOverrideAction; private DockingAction setFlowOverrideAction;
private DockingAction setLengthOverrideAction;
/** Dialog for obtaining the processor state to be used for disassembling. */ /** Dialog for obtaining the processor state to be used for disassembling. */
// private ProcessorStateDialog processorStateDialog; // private ProcessorStateDialog processorStateDialog;
@ -177,6 +178,7 @@ public class DisassemblerPlugin extends Plugin {
x86_64DisassembleAction = new X86_64DisassembleAction(this, GROUP_NAME, false); x86_64DisassembleAction = new X86_64DisassembleAction(this, GROUP_NAME, false);
x86_32DisassembleAction = new X86_64DisassembleAction(this, GROUP_NAME, true); x86_32DisassembleAction = new X86_64DisassembleAction(this, GROUP_NAME, true);
setFlowOverrideAction = new SetFlowOverrideAction(this, GROUP_NAME); setFlowOverrideAction = new SetFlowOverrideAction(this, GROUP_NAME);
setLengthOverrideAction = new SetLengthOverrideAction(this, GROUP_NAME);
tool.addAction(disassembleAction); tool.addAction(disassembleAction);
tool.addAction(disassembleRestrictedAction); tool.addAction(disassembleRestrictedAction);
@ -193,6 +195,7 @@ public class DisassemblerPlugin extends Plugin {
tool.addAction(x86_32DisassembleAction); tool.addAction(x86_32DisassembleAction);
tool.addAction(contextAction); tool.addAction(contextAction);
tool.addAction(setFlowOverrideAction); tool.addAction(setFlowOverrideAction);
tool.addAction(setLengthOverrideAction);
} }
void disassembleRestrictedCallback(ListingActionContext context) { void disassembleRestrictedCallback(ListingActionContext context) {

View file

@ -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;
}
}

View file

@ -91,8 +91,8 @@ public class FallThroughPlugin extends Plugin {
return instruction != null && !instruction.isFallThroughOverridden(); return instruction != null && !instruction.isFallThroughOverridden();
} }
}; };
autoFallthroughAction.setPopupMenuData(new MenuData(new String[] { "Fallthrough", autoFallthroughAction.setPopupMenuData(
"Auto Override" }, null, "Fallthrough")); new MenuData(new String[] { "Fallthrough", "Auto Override" }, null, "Fallthrough"));
tool.addAction(autoFallthroughAction); tool.addAction(autoFallthroughAction);
@ -111,8 +111,8 @@ public class FallThroughPlugin extends Plugin {
return instruction != null && instruction.isFallThroughOverridden(); return instruction != null && instruction.isFallThroughOverridden();
} }
}; };
clearFallthroughAction.setPopupMenuData(new MenuData(new String[] { "Fallthrough", clearFallthroughAction.setPopupMenuData(
"Clear Overrides" }, null, "Fallthrough")); new MenuData(new String[] { "Fallthrough", "Clear Overrides" }, null, "Fallthrough"));
tool.addAction(clearFallthroughAction); tool.addAction(clearFallthroughAction);
@ -128,8 +128,8 @@ public class FallThroughPlugin extends Plugin {
return instruction != null; return instruction != null;
} }
}; };
setFallthroughAction.setPopupMenuData(new MenuData( setFallthroughAction.setPopupMenuData(
new String[] { "Fallthrough", "Set..." }, null, "Fallthrough")); new MenuData(new String[] { "Fallthrough", "Set..." }, null, "Fallthrough"));
tool.addAction(setFallthroughAction); tool.addAction(setFallthroughAction);
} }

View file

@ -326,8 +326,9 @@ public class InsertBytesWidget extends ReusableDialogComponentProvider implement
private List<OperandMetadata> createOperandMetadata(PseudoInstruction instruction) private List<OperandMetadata> createOperandMetadata(PseudoInstruction instruction)
throws MemoryAccessException { throws MemoryAccessException {
List<OperandMetadata> operands = new ArrayList<>(); byte[] bytes = instruction.getParsedBytes();
List<OperandMetadata> operands = new ArrayList<>();
for (int i = 0; i < instruction.getNumOperands(); i++) { for (int i = 0; i < instruction.getNumOperands(); i++) {
OperandMetadata operandMD = new OperandMetadata(); OperandMetadata operandMD = new OperandMetadata();
operandMD.setOpType(instruction.getOperandType(i)); operandMD.setOpType(instruction.getOperandType(i));
@ -337,8 +338,9 @@ public class InsertBytesWidget extends ReusableDialogComponentProvider implement
// prototype object in the pseudo instruction. For the value string we have to do // prototype object in the pseudo instruction. For the value string we have to do
// a bit of calculating: we know the entire instruction byte string and we know // a bit of calculating: we know the entire instruction byte string and we know
// this operand mask, so AND them together and we get the operand bytes. // this operand mask, so AND them together and we get the operand bytes.
byte[] mask = instruction.getPrototype().getOperandValueMask(i).getBytes(); InstructionPrototype prototype = instruction.getPrototype();
byte[] value = InstructionSearchUtils.byteArrayAnd(mask, instruction.getBytes()); byte[] mask = prototype.getOperandValueMask(i).getBytes();
byte[] value = InstructionSearchUtils.byteArrayAnd(mask, bytes);
MaskContainer maskContainer = new MaskContainer(mask, value); MaskContainer maskContainer = new MaskContainer(mask, value);
operandMD.setMaskContainer(maskContainer); operandMD.setMaskContainer(maskContainer);
@ -362,8 +364,10 @@ public class InsertBytesWidget extends ReusableDialogComponentProvider implement
// The mask array we can get directly from the prototype. For the value array we // The mask array we can get directly from the prototype. For the value array we
// have to figure out which bits pertain to operands and just zero them out, so we're // have to figure out which bits pertain to operands and just zero them out, so we're
// just left with the instruction (mnemonic) bits. // just left with the instruction (mnemonic) bits.
byte[] mask = instruction.getPrototype().getInstructionMask().getBytes(); InstructionPrototype prototype = instruction.getPrototype();
byte[] value = clearOperandBits(mask, instruction.getBytes()); byte[] mask = prototype.getInstructionMask().getBytes();
byte[] value = instruction.getParsedBytes();
value = clearOperandBits(mask, value);
MaskContainer mnemonicMask = new MaskContainer(mask, value); MaskContainer mnemonicMask = new MaskContainer(mask, value);
InstructionMetadata instructionMD = new InstructionMetadata(mnemonicMask); InstructionMetadata instructionMD = new InstructionMetadata(mnemonicMask);

View file

@ -1,6 +1,5 @@
/* ### /* ###
* IP: GHIDRA * IP: GHIDRA
* REVIEWED: YES
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -18,8 +17,7 @@ package ghidra.app.plugin.core.reloc;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.lang.*; import ghidra.program.model.lang.*;
import ghidra.program.model.listing.Instruction; import ghidra.program.model.listing.*;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.DumbMemBufferImpl; import ghidra.program.model.mem.DumbMemBufferImpl;
import ghidra.program.model.mem.MemBuffer; import ghidra.program.model.mem.MemBuffer;
import ghidra.program.model.symbol.Reference; import ghidra.program.model.symbol.Reference;
@ -31,6 +29,10 @@ public class InstructionStasher {
private Address address; private Address address;
private InstructionPrototype prototype; private InstructionPrototype prototype;
private Reference[] referencesFrom; private Reference[] referencesFrom;
private FlowOverride flowOverride;
private Address fallthroughOverride;
private int lengthOverride;
private Address minAddress; private Address minAddress;
public InstructionStasher(Program program, Address address) { public InstructionStasher(Program program, Address address) {
@ -47,6 +49,12 @@ public class InstructionStasher {
minAddress = instruction.getMinAddress(); minAddress = instruction.getMinAddress();
prototype = instruction.getPrototype(); prototype = instruction.getPrototype();
referencesFrom = instruction.getReferencesFrom(); referencesFrom = instruction.getReferencesFrom();
flowOverride = instruction.getFlowOverride();
fallthroughOverride =
instruction.isFallThroughOverridden() ? instruction.getFallThrough() : null;
// Relocation data change may mutate instruction. Do not force length of instruction
// unless it was previously overriden. A value of 0 allows length to match prototoype.
lengthOverride = instruction.isLengthOverridden() ? instruction.getLength() : 0;
program.getListing().clearCodeUnits(minAddress, instruction.getMaxAddress(), false); program.getListing().clearCodeUnits(minAddress, instruction.getMaxAddress(), false);
} }
@ -57,7 +65,16 @@ public class InstructionStasher {
MemBuffer buf = new DumbMemBufferImpl(program.getMemory(), minAddress); MemBuffer buf = new DumbMemBufferImpl(program.getMemory(), minAddress);
ProcessorContext context = ProcessorContext context =
new ProgramProcessorContext(program.getProgramContext(), minAddress); new ProgramProcessorContext(program.getProgramContext(), minAddress);
program.getListing().createInstruction(minAddress, prototype, buf, context); Instruction instr = program.getListing()
.createInstruction(minAddress, prototype, buf, context, lengthOverride);
if (flowOverride != FlowOverride.NONE) {
instr.setFlowOverride(flowOverride);
}
if (fallthroughOverride != null) {
instr.setFallThrough(fallthroughOverride);
}
for (Reference reference : referencesFrom) { for (Reference reference : referencesFrom) {
if (reference.getSource() != SourceType.DEFAULT) { if (reference.getSource() != SourceType.DEFAULT) {

View file

@ -568,7 +568,13 @@ class ProgramTextWriter {
} }
try { try {
byte[] bytes = cu.getBytes(); byte[] bytes;
if (cu instanceof Instruction instr) {
bytes = instr.getParsedBytes();
}
else {
bytes = cu.getBytes();
}
StringBuffer bytesbuf = new StringBuffer(); StringBuffer bytesbuf = new StringBuffer();
for (int i = 0; i < bytes.length; ++i) { for (int i = 0; i < bytes.length; ++i) {
if (i > 0) { if (i > 0) {
@ -630,10 +636,7 @@ class ProgramTextWriter {
if (options.isHTML()) { if (options.isHTML()) {
Reference ref = Reference ref =
cu.getProgram() cu.getProgram().getReferenceManager().getPrimaryReferenceFrom(cuAddress, i);
.getReferenceManager()
.getPrimaryReferenceFrom(cuAddress,
i);
addReferenceLinkedText(ref, opReps[i], true); addReferenceLinkedText(ref, opReps[i], true);
} }
else { else {

View file

@ -174,7 +174,18 @@ public class BytesFieldFactory extends FieldFactory {
} }
CodeUnit cu = (CodeUnit) obj; CodeUnit cu = (CodeUnit) obj;
int length = Math.min(cu.getLength(), 100); int length;
// Instructions: use prototype length so we display all bytes for length-override case
if (cu instanceof Instruction) {
// Consider all parsed bytes even if the overlap the next instruction due to the
// use of an instruction length-override.
length = ((Instruction) cu).getParsedLength();
}
else {
length = Math.min(cu.getLength(), 100);
}
byte[] bytes = new byte[length]; byte[] bytes = new byte[length];
Memory memory = cu.getProgram().getMemory(); Memory memory = cu.getProgram().getMemory();
try { try {

View file

@ -120,7 +120,8 @@ public class MnemonicFieldFactory extends FieldFactory {
} }
else if (cu instanceof Instruction) { else if (cu instanceof Instruction) {
Instruction instr = (Instruction) cu; Instruction instr = (Instruction) cu;
if (instr.getFlowOverride() != FlowOverride.NONE || instr.isFallThroughOverridden()) { if (instr.getFlowOverride() != FlowOverride.NONE || instr.isFallThroughOverridden() ||
instr.isLengthOverridden()) {
c = MnemonicColors.OVERRIDE; c = MnemonicColors.OVERRIDE;
} }
} }

View file

@ -177,12 +177,20 @@ public class PostCommentFieldFactory extends FieldFactory {
} }
} }
if (instr.isFallThroughOverridden()) { if (instr.isLengthOverridden() || instr.isFallThroughOverridden()) {
Address fallThrough = instr.getFallThrough(); Address fallThrough = instr.getFallThrough();
String fallthroughComment = "-- Fallthrough Override: " + String fallthroughComment =
"-- Fallthrough" + (instr.isFallThroughOverridden() ? " Override" : "") + ": " +
(fallThrough != null ? fallThrough.toString() : "NO-FALLTHROUGH"); (fallThrough != null ? fallThrough.toString() : "NO-FALLTHROUGH");
comments.addFirst(fallthroughComment); comments.addFirst(fallthroughComment);
} }
if (instr.isLengthOverridden()) {
String lengthOverrideComment = "-- Length Override: " + instr.getLength() +
" (actual length is " + instr.getParsedLength() + ")";
comments.addFirst(lengthOverrideComment);
}
FlowOverride flowOverride = instr.getFlowOverride(); FlowOverride flowOverride = instr.getFlowOverride();
if (flowOverride != FlowOverride.NONE) { if (flowOverride != FlowOverride.NONE) {
String flowOverrideComment = String flowOverrideComment =

View file

@ -571,15 +571,18 @@ public class DiffUtility extends SimpleDiffUtility {
Address rangeMin = range.getMinAddress(); Address rangeMin = range.getMinAddress();
Address rangeMax = range.getMaxAddress(); Address rangeMax = range.getMaxAddress();
CodeUnit minCu = listing.getCodeUnitContaining(rangeMin); CodeUnit minCu = listing.getCodeUnitContaining(rangeMin);
// NOTE: minCu does not consider prior code unit which may share bytes due to
// possible instruction length-override. Max address of code unit will factor-in
// full length of instruction prototype.
if (minCu != null) { if (minCu != null) {
Address minCuMinAddr = minCu.getMinAddress(); Address minCuMinAddr = minCu.getMinAddress();
if (minCuMinAddr.compareTo(rangeMin) != 0) { if (minCuMinAddr.compareTo(rangeMin) != 0) {
addrs.addRange(minCuMinAddr, minCu.getMaxAddress()); addrs.addRange(minCuMinAddr, getMaxAddress(minCu));
} }
} }
CodeUnit maxCu = listing.getCodeUnitContaining(rangeMax); CodeUnit maxCu = listing.getCodeUnitContaining(rangeMax);
if (maxCu != null) { if (maxCu != null) {
Address maxCuMaxAddr = maxCu.getMaxAddress(); Address maxCuMaxAddr = getMaxAddress(maxCu);
if (maxCuMaxAddr.compareTo(rangeMax) != 0) { if (maxCuMaxAddr.compareTo(rangeMax) != 0) {
addrs.addRange(maxCu.getMinAddress(), maxCuMaxAddr); addrs.addRange(maxCu.getMinAddress(), maxCuMaxAddr);
} }
@ -588,6 +591,20 @@ public class DiffUtility extends SimpleDiffUtility {
return addrs; return addrs;
} }
/**
* Get the code unit max address. Instruction max address will be based upon prototype
* length which may be longer than code unit length if length-override is in effect.
* @param cu code unit
* @return effective code unit length
*/
private static Address getMaxAddress(CodeUnit cu) {
if (cu instanceof Instruction inst && inst.isLengthOverridden()) {
// factor in prototype length when length-override applies
return cu.getMinAddress().add(inst.getParsedLength() - 1);
}
return cu.getMaxAddress();
}
/** /**
* Returns the signed hex string representing the int value. * Returns the signed hex string representing the int value.
* Positive values are represented beginning with 0x. (i.e. value of 12 would be 0xc) * Positive values are represented beginning with 0x. (i.e. value of 12 would be 0xc)

View file

@ -2871,6 +2871,7 @@ public class ProgramDiff {
if (i1 == i2) { if (i1 == i2) {
return true; return true;
} }
// Checking length and prototype will handle use of length-override
if (i1.getLength() != i2.getLength()) { if (i1.getLength() != i2.getLength()) {
return false; return false;
} }
@ -2888,7 +2889,9 @@ public class ProgramDiff {
} }
try { try {
if (!Arrays.equals(i1.getBytes(), i2.getBytes())) { byte[] bytes1 = i1.getParsedBytes();
byte[] bytes2 = i2.getParsedBytes();
if (!Arrays.equals(bytes1, bytes2)) {
return false; // bytes differ return false; // bytes differ
} }
} }

View file

@ -906,8 +906,7 @@ public class ProgramDiffDetails {
ordinal + " " + fieldName + " " + actualDt.getMnemonic(actualDt.getDefaultSettings()) + ordinal + " " + fieldName + " " + actualDt.getMnemonic(actualDt.getDefaultSettings()) +
" " + getCategoryName(actualDt) + " " + "DataTypeSize=" + " " + getCategoryName(actualDt) + " " + "DataTypeSize=" +
(actualDt.isZeroLength() ? 0 : actualDt.getLength()) + " " + "ComponentSize=" + (actualDt.isZeroLength() ? 0 : actualDt.getLength()) + " " + "ComponentSize=" +
dtc.getLength() + " " + ((comment != null) ? comment : "") + dtc.getLength() + " " + ((comment != null) ? comment : "") + " " + newLine);
" " + newLine);
return actualDt; return actualDt;
} }
@ -958,8 +957,7 @@ public class ProgramDiffDetails {
fieldName = "field" + offset; fieldName = "field" + offset;
} }
buf.append(newIndent + min.add(offset) + " " + dtc.getFieldName() + " " + buf.append(newIndent + min.add(offset) + " " + dtc.getFieldName() + " " +
dtc.getDataType().getName() + " " + "length=" + dtc.getDataType().getName() + " " + "length=" + dtc.getLength() + " " +
dtc.getLength() + " " +
((comment != null) ? comment : "") + " " + newLine); ((comment != null) ? comment : "") + " " + newLine);
} }
} }
@ -987,6 +985,7 @@ public class ProgramDiffDetails {
boolean removedFallThrough = boolean removedFallThrough =
inst.isFallThroughOverridden() && (inst.getFallThrough() == null); inst.isFallThroughOverridden() && (inst.getFallThrough() == null);
boolean hasFlowOverride = inst.getFlowOverride() != FlowOverride.NONE; boolean hasFlowOverride = inst.getFlowOverride() != FlowOverride.NONE;
boolean hasLengthOverride = inst.isLengthOverridden();
cuRep = cu.toString(); cuRep = cu.toString();
if (removedFallThrough) { if (removedFallThrough) {
cuRep += newLine + indent + getSpaces(addrRangeStr.length()) + " " + cuRep += newLine + indent + getSpaces(addrRangeStr.length()) + " " +
@ -1011,6 +1010,11 @@ public class ProgramDiffDetails {
cuRep += newLine + indent + getSpaces(addrRangeStr.length()) + " " + cuRep += newLine + indent + getSpaces(addrRangeStr.length()) + " " +
"Flow Override: " + inst.getFlowOverride(); "Flow Override: " + inst.getFlowOverride();
} }
if (hasLengthOverride) {
cuRep += newLine + indent + getSpaces(addrRangeStr.length()) + " " +
"Length Override: " + inst.getLength() + " (actual length is " +
inst.getParsedLength() + ")";
}
cuRep += newLine + indent + getSpaces(addrRangeStr.length()) + " " + cuRep += newLine + indent + getSpaces(addrRangeStr.length()) + " " +
"Instruction Prototype hash = " + "Instruction Prototype hash = " +
Integer.toHexString(inst.getPrototype().hashCode()); Integer.toHexString(inst.getPrototype().hashCode());
@ -1463,8 +1467,8 @@ public class ProgramDiffDetails {
private boolean addSpecificCommentDetails(int commentType, String commentName) { private boolean addSpecificCommentDetails(int commentType, String commentName) {
boolean hasCommentDiff = false; boolean hasCommentDiff = false;
try { try {
for (Address p1Address = minP1Address; p1Address.compareTo( for (Address p1Address = minP1Address; p1Address
maxP1Address) <= 0; p1Address = p1Address.add(1L)) { .compareTo(maxP1Address) <= 0; p1Address = p1Address.add(1L)) {
Address p2Address = SimpleDiffUtility.getCompatibleAddress(p1, p1Address, p2); Address p2Address = SimpleDiffUtility.getCompatibleAddress(p1, p1Address, p2);
String noComment = "No " + commentName + "."; String noComment = "No " + commentName + ".";
String cmt1 = l1.getComment(commentType, p1Address); String cmt1 = l1.getComment(commentType, p1Address);
@ -2169,8 +2173,7 @@ public class ProgramDiffDetails {
// Handle case where the class for a Saveable property is missing (unsupported). // Handle case where the class for a Saveable property is missing (unsupported).
if (cu.getProgram() if (cu.getProgram()
.getListing() .getListing()
.getPropertyMap( .getPropertyMap(propertyName) instanceof UnsupportedMapDB) {
propertyName) instanceof UnsupportedMapDB) {
buf.append( buf.append(
indent2 + propertyName + " is an unsupported property." + newLine); indent2 + propertyName + " is an unsupported property." + newLine);
continue; continue;
@ -2241,8 +2244,8 @@ public class ProgramDiffDetails {
BookmarkManager bmm1 = p1.getBookmarkManager(); BookmarkManager bmm1 = p1.getBookmarkManager();
BookmarkManager bmm2 = p2.getBookmarkManager(); BookmarkManager bmm2 = p2.getBookmarkManager();
try { try {
for (Address p1Address = minP1Address; p1Address.compareTo( for (Address p1Address = minP1Address; p1Address
maxP1Address) <= 0; p1Address = p1Address.add(1)) { .compareTo(maxP1Address) <= 0; p1Address = p1Address.add(1)) {
Address p2Address = SimpleDiffUtility.getCompatibleAddress(p1, p1Address, p2); Address p2Address = SimpleDiffUtility.getCompatibleAddress(p1, p1Address, p2);
Bookmark[] marks1 = bmm1.getBookmarks(p1Address); Bookmark[] marks1 = bmm1.getBookmarks(p1Address);
Arrays.sort(marks1, BOOKMARK_COMPARATOR); Arrays.sort(marks1, BOOKMARK_COMPARATOR);
@ -2341,7 +2344,7 @@ public class ProgramDiffDetails {
private boolean isSameInstruction(Instruction i1, Instruction i2) { private boolean isSameInstruction(Instruction i1, Instruction i2) {
boolean samePrototypes = i1.getPrototype().equals(i2.getPrototype()); boolean samePrototypes = i1.getPrototype().equals(i2.getPrototype());
boolean sameInstructionLength = i1.getLength() == i2.getLength(); boolean sameInstructionLength = i1.getLength() == i2.getLength(); // factors length override
boolean sameFallthrough = ProgramDiff.isSameFallthrough(p1, i1, p2, i2); boolean sameFallthrough = ProgramDiff.isSameFallthrough(p1, i1, p2, i2);
boolean sameFlowOverride = i1.getFlowOverride() == i2.getFlowOverride(); boolean sameFlowOverride = i1.getFlowOverride() == i2.getFlowOverride();
return samePrototypes && sameInstructionLength && sameFallthrough && sameFlowOverride; return samePrototypes && sameInstructionLength && sameFallthrough && sameFlowOverride;

View file

@ -29,7 +29,6 @@ import ghidra.util.datastruct.Accumulator;
import ghidra.util.datastruct.ListAccumulator; import ghidra.util.datastruct.ListAccumulator;
import ghidra.util.exception.CancelledException; import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor; import ghidra.util.task.TaskMonitor;
import ghidra.util.task.TaskMonitorAdapter;
import utility.function.TerminatingConsumer; import utility.function.TerminatingConsumer;
/** /**
@ -151,14 +150,26 @@ public class ProgramMemoryUtil {
*/ */
private static void copyByteRange(Memory toMem, Memory fromMem, AddressRange range) private static void copyByteRange(Memory toMem, Memory fromMem, AddressRange range)
throws MemoryAccessException { throws MemoryAccessException {
// TODO: Addresses from one program cannot be used with another program (e.g., overlays)
// TODO: Should be using AddressTranslator
// TODO: Method relies too heavily on caller limiting range to valid initialized memory in both programs
// Copy the bytes for this range // Copy the bytes for this range
int length = 0; int length = 0;
Address writeAddress = range.getMinAddress(); Address writeAddress = range.getMinAddress();
for (long len = range.getLength(); len > 0; len -= length) { for (long len = range.getLength(); len > 0; len -= length) {
length = (int) Math.min(len, Integer.MAX_VALUE); length = (int) Math.min(len, Integer.MAX_VALUE);
byte[] bytes = new byte[length]; byte[] srcBytes = new byte[length];
fromMem.getBytes(writeAddress, bytes);
toMem.setBytes(writeAddress, bytes); // NOTE: problems will arise if srcLen != length
int srcLen = fromMem.getBytes(writeAddress, srcBytes);
byte[] destBytes = new byte[srcLen];
if (toMem.getBytes(writeAddress, destBytes) != srcLen ||
!Arrays.equals(destBytes, 0, srcLen, srcBytes, 0, srcLen)) {
// only copy bytes if they differ
toMem.setBytes(writeAddress, srcBytes, 0, srcLen);
}
if (len > length) { if (len > length) {
writeAddress = writeAddress.add(length); writeAddress = writeAddress.add(length);
} }

View file

@ -628,7 +628,9 @@ public class ProgramMerge {
return true; return true;
} }
try { try {
if (!Arrays.equals(instruction.getBytes(), resultInstruction.getBytes())) { byte[] bytes = instruction.getParsedBytes();
byte[] resultBytes = resultInstruction.getParsedBytes();
if (!Arrays.equals(bytes, resultBytes)) {
return true; // bytes differ return true; // bytes differ
} }
} }
@ -682,8 +684,8 @@ public class ProgramMerge {
bytesLength = (int) originMax.subtract(originMin); bytesLength = (int) originMax.subtract(originMin);
} }
else { else {
originMax = originInstruction.getMaxAddress(); bytesLength = originInstruction.getParsedLength();
bytesLength = originInstruction.getLength(); originMax = originMin.add(bytesLength - 1);
} }
Address resultMax = originToResultTranslator.getAddress(originMax); Address resultMax = originToResultTranslator.getAddress(originMax);
@ -699,7 +701,7 @@ public class ProgramMerge {
// If there are byte differences for this instruction then the // If there are byte differences for this instruction then the
// bytes need to get copied even though the user did not indicate to. // bytes need to get copied even though the user did not indicate to.
if (bytesAreDifferent(originByteDiffs, originMin, resultMin, bytesLength)) { // FIXME if (bytesMayDiffer(originByteDiffs, originMin, resultMin, bytesLength)) {
// Copy all the bytes for the instruction if any bytes differ. // Copy all the bytes for the instruction if any bytes differ.
ProgramMemoryUtil.copyBytesInRanges(resultProgram, originProgram, resultMin, resultMax); ProgramMemoryUtil.copyBytesInRanges(resultProgram, originProgram, resultMin, resultMax);
} }
@ -709,7 +711,8 @@ public class ProgramMerge {
newInst = disassembleDelaySlottedInstruction(resultProgram, resultMin); newInst = disassembleDelaySlottedInstruction(resultProgram, resultMin);
} }
else { else {
newInst = disassembleNonDelaySlotInstruction(resultProgram, resultMin); newInst = disassembleNonDelaySlotInstruction(resultProgram, resultMin,
originInstruction.isLengthOverridden() ? originInstruction.getLength() : 0);
} }
if (newInst == null) { if (newInst == null) {
return; return;
@ -740,24 +743,29 @@ public class ProgramMerge {
} }
private Instruction disassembleDelaySlottedInstruction(Program program, Address addr) { private Instruction disassembleDelaySlottedInstruction(Program program, Address addr) {
// WARNING: does not support instruction length override use
// Use heavyweight disassembler for delay slotted instruction // Use heavyweight disassembler for delay slotted instruction
AddressSet restrictedSet = new AddressSet(addr); AddressSet restrictedSet = new AddressSet(addr);
Disassembler disassembler = Disassembler disassembler = Disassembler.getDisassembler(program, TaskMonitor.DUMMY, null);
Disassembler.getDisassembler(program, TaskMonitor.DUMMY, null);
disassembler.disassemble(addr, restrictedSet, false); disassembler.disassemble(addr, restrictedSet, false);
return program.getListing().getInstructionAt(addr); return program.getListing().getInstructionAt(addr);
} }
private Instruction disassembleNonDelaySlotInstruction(Program program, Address addr) { private Instruction disassembleNonDelaySlotInstruction(Program program, Address addr,
int lengthOverride) {
// Use lightweight disassembler for simple case // Use lightweight disassembler for simple case
DisassemblerContextImpl context = new DisassemblerContextImpl(program.getProgramContext()); DisassemblerContextImpl context = new DisassemblerContextImpl(program.getProgramContext());
context.flowStart(addr); context.flowStart(addr);
try { try {
InstructionPrototype proto = program.getLanguage() InstructionPrototype proto = program.getLanguage()
.parse(new DumbMemBufferImpl(program.getMemory(), addr), context, false); .parse(new DumbMemBufferImpl(program.getMemory(), addr), context, false);
if (lengthOverride > proto.getLength()) {
lengthOverride = 0;
}
return resultListing.createInstruction(addr, proto, return resultListing.createInstruction(addr, proto,
new DumbMemBufferImpl(program.getMemory(), addr), new DumbMemBufferImpl(program.getMemory(), addr),
new ProgramProcessorContext(program.getProgramContext(), addr)); new ProgramProcessorContext(program.getProgramContext(), addr),
Math.min(lengthOverride, proto.getLength()));
} }
catch (Exception e) { catch (Exception e) {
program.getBookmarkManager() program.getBookmarkManager()
@ -767,17 +775,13 @@ public class ProgramMerge {
return null; return null;
} }
private boolean bytesAreDifferent(AddressSetView originByteDiffs, Address originMin, private boolean bytesMayDiffer(AddressSetView originByteDiffs, Address originMin,
Address resultMin, int byteCnt) throws MemoryAccessException { Address resultMin, int byteCnt) throws MemoryAccessException {
if (originByteDiffs != null) { if (originByteDiffs != null) {
AddressSet resultByteDiffs = originToResultTranslator.getAddressSet(originByteDiffs); AddressSet resultByteDiffs = originToResultTranslator.getAddressSet(originByteDiffs);
return resultByteDiffs.intersects(new AddressSet(resultMin, resultMin.add(byteCnt))); return resultByteDiffs.intersects(new AddressSet(resultMin, resultMin.add(byteCnt)));
} }
byte[] originBytes = new byte[byteCnt]; return true;
originProgram.getMemory().getBytes(originMin, originBytes);
byte[] resultBytes = new byte[byteCnt];
resultProgram.getMemory().getBytes(resultMin, resultBytes);
return !Arrays.equals(originBytes, resultBytes);
} }
/** /**
@ -808,7 +812,7 @@ public class ProgramMerge {
// If there are byte differences for this instruction then the // If there are byte differences for this instruction then the
// bytes need to get copied even though the user did not indicate to. // bytes need to get copied even though the user did not indicate to.
if (copyBytes && if (copyBytes &&
bytesAreDifferent(originByteDiffs, originMin, resultMin, originData.getLength())) { bytesMayDiffer(originByteDiffs, originMin, resultMin, originData.getLength())) {
// Copy all the bytes for the instruction if any bytes differ. // Copy all the bytes for the instruction if any bytes differ.
ProgramMemoryUtil.copyBytesInRanges(resultProgram, originProgram, resultMin, resultMax); ProgramMemoryUtil.copyBytesInRanges(resultProgram, originProgram, resultMin, resultMax);
} }
@ -1717,9 +1721,7 @@ public class ProgramMerge {
// Now discard any tags we've been told to remove. // Now discard any tags we've been told to remove.
if (discardTags != null) { if (discardTags != null) {
Set<String> tagNames = getTagNames(discardTags); Set<String> tagNames = getTagNames(discardTags);
Iterator<FunctionTag> iter = resultTags.iterator(); for (FunctionTag tag : resultTags) {
while (iter.hasNext()) {
FunctionTag tag = iter.next();
if (tagNames.contains(tag.getName())) { if (tagNames.contains(tag.getName())) {
resultFunction.removeTag(tag.getName()); resultFunction.removeTag(tag.getName());
@ -3934,8 +3936,7 @@ public class ProgramMerge {
if (originObject != null) { if (originObject != null) {
try { try {
if (resultOpm == null) { if (resultOpm == null) {
resultOpm = resultOpm = createPropertyMap(userPropertyName, resultProgram, originOpm);
createPropertyMap(userPropertyName, resultProgram, originOpm);
} }
resultOpm.add(resultAddress, originObject); resultOpm.add(resultAddress, originObject);
} }

View file

@ -669,7 +669,8 @@ public class SymbolicPropogator {
else { else {
int instrByteHashCode = -1; int instrByteHashCode = -1;
try { try {
instrByteHashCode = Arrays.hashCode(instr.getBytes()); byte[] bytes = instr.getParsedBytes();
instrByteHashCode = Arrays.hashCode(bytes);
} }
catch (MemoryAccessException e) { catch (MemoryAccessException e) {
// this should NEVER happen, should always be able to get the bytes... // this should NEVER happen, should always be able to get the bytes...

View file

@ -26,8 +26,7 @@ import ghidra.docking.settings.*;
import ghidra.framework.plugintool.ServiceProvider; import ghidra.framework.plugintool.ServiceProvider;
import ghidra.program.model.address.*; import ghidra.program.model.address.*;
import ghidra.program.model.data.EndianSettingsDefinition; import ghidra.program.model.data.EndianSettingsDefinition;
import ghidra.program.model.listing.CodeUnit; import ghidra.program.model.listing.*;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.MemoryAccessException; import ghidra.program.model.mem.MemoryAccessException;
import ghidra.program.util.BytesFieldLocation; import ghidra.program.util.BytesFieldLocation;
import ghidra.program.util.ProgramLocation; import ghidra.program.util.ProgramLocation;
@ -50,16 +49,14 @@ public class BytesTableColumn extends ProgramLocationTableColumnExtensionPoint<A
private static SettingsDefinition[] SETTINGS_DEFS = private static SettingsDefinition[] SETTINGS_DEFS =
{ BYTE_COUNT, MEMORY_OFFSET, ENDIANESS, FORMAT }; { BYTE_COUNT, MEMORY_OFFSET, ENDIANESS, FORMAT };
private final GColumnRenderer<Byte[]> monospacedRenderer = private final GColumnRenderer<Byte[]> monospacedRenderer = new AbstractGColumnRenderer<>() {
new AbstractGColumnRenderer<>() {
@Override @Override
protected void configureFont(JTable table, TableModel model, int column) { protected void configureFont(JTable table, TableModel model, int column) {
setFont(getFixedWidthFont()); setFont(getFixedWidthFont());
} }
private String formatBytes(Byte[] bytes, Settings settings) { private String formatBytes(Byte[] bytes, Settings settings) {
boolean bigEndian = boolean bigEndian = (ENDIANESS.getChoice(settings) != EndianSettingsDefinition.LITTLE);
(ENDIANESS.getChoice(settings) != EndianSettingsDefinition.LITTLE);
int startIx = 0; int startIx = 0;
int endIx = bytes.length; int endIx = bytes.length;
@ -194,9 +191,14 @@ public class BytesTableColumn extends ProgramLocationTableColumnExtensionPoint<A
if (cu == null) { // can happen for 'SpecialAddress'es if (cu == null) { // can happen for 'SpecialAddress'es
return new Byte[0]; return new Byte[0];
} }
if (cu instanceof Instruction instr) {
bytes = instr.getParsedBytes();
}
else {
bytes = cu.getBytes(); bytes = cu.getBytes();
} }
} }
}
if (bytes == null) { if (bytes == null) {
bytes = new byte[byteCnt]; bytes = new byte[byteCnt];

View file

@ -94,7 +94,7 @@ public abstract class AbstractListingMergeManagerTest extends AbstractMergeTest
new ProgramProcessorContext(program.getProgramContext(), atAddress); new ProgramProcessorContext(program.getProgramContext(), atAddress);
InstructionPrototype proto = program.getLanguage().parse(buf, context, false); InstructionPrototype proto = program.getLanguage().parse(buf, context, false);
Instruction createdInstruction = Instruction createdInstruction =
listing.createInstruction(atAddress, proto, buf, context); listing.createInstruction(atAddress, proto, buf, context, 0);
commit = true; commit = true;
return createdInstruction; return createdInstruction;
} }

View file

@ -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());
}
}

View file

@ -19,6 +19,7 @@ import static org.junit.Assert.*;
import org.junit.*; import org.junit.*;
import db.Transaction;
import ghidra.app.plugin.core.bookmark.BookmarkPlugin; import ghidra.app.plugin.core.bookmark.BookmarkPlugin;
import ghidra.app.plugin.core.clear.ClearPlugin; import ghidra.app.plugin.core.clear.ClearPlugin;
import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin; import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
@ -1678,6 +1679,59 @@ public class CodeUnitIteratorTest extends AbstractGhidraHeadedIntegrationTest {
assertEquals(10, cnt); assertEquals(10, cnt);
} }
@Test
public void testGetCodeUnitsSetWithOverride() throws Exception {
// set length-override on 0x3f4
try (Transaction tx = program.openTransaction("Set Length Override")) {
program.getListing().getInstructionAt(addr(0x3f4)).setLengthOverride(1);
}
AddressSet set = new AddressSet();
set.addRange(addr(0x3f0), addr(0x3f9));
CodeUnitIterator it = listing.getCodeUnits(set, true);
CodeUnit cu = it.next();
assertEquals(addr(0x3f1), cu.getMinAddress());
assertTrue(cu instanceof Data);
assertFalse(((Data) cu).isDefined());
cu = it.next();
assertEquals(addr(0x3f2), cu.getMinAddress());
assertTrue(cu instanceof Instruction);
assertEquals(2, cu.getLength());
cu = it.next();
assertEquals(addr(0x3f4), cu.getMinAddress());
assertTrue(cu instanceof Instruction);
assertEquals(1, cu.getLength()); // length overriden
assertEquals("imm r0,#0x0", cu.toString());
cu = it.next();
assertEquals(addr(0x3f5), cu.getMinAddress());
assertTrue(cu instanceof Data);
assertFalse(((Data) cu).isDefined());
cu = it.next();
assertEquals(addr(0x3f6), cu.getMinAddress());
assertTrue(cu instanceof Instruction);
assertEquals(2, cu.getLength());
cu = it.next();
assertEquals(addr(0x3f8), cu.getMinAddress());
assertTrue(cu instanceof Data);
assertFalse(((Data) cu).isDefined());
cu = it.next();
assertEquals(addr(0x3f9), cu.getMinAddress());
assertTrue(cu instanceof Data);
assertFalse(((Data) cu).isDefined());
assertFalse(it.hasNext());
assertNull(it.next());
}
private Address addr(long l) { private Address addr(long l) {
return space.getAddress(l); return space.getAddress(l);
} }
@ -1704,7 +1758,7 @@ public class CodeUnitIteratorTest extends AbstractGhidraHeadedIntegrationTest {
ProcessorContext context = new ProgramProcessorContext(program.getProgramContext(), atAddr); ProcessorContext context = new ProgramProcessorContext(program.getProgramContext(), atAddr);
InstructionPrototype proto = program.getLanguage().parse(buf, context, false); InstructionPrototype proto = program.getLanguage().parse(buf, context, false);
listing.createInstruction(atAddr, proto, buf, context); listing.createInstruction(atAddr, proto, buf, context, 0);
} }
private void startTransaction() { private void startTransaction() {

View file

@ -99,7 +99,7 @@ public class SymbolUtilities1Test extends AbstractGhidraHeadedIntegrationTest {
new ProgramProcessorContext(program.getProgramContext(), addr(0x200)); new ProgramProcessorContext(program.getProgramContext(), addr(0x200));
DumbMemBufferImpl membuf = new DumbMemBufferImpl(program.getMemory(), addr(0x200)); DumbMemBufferImpl membuf = new DumbMemBufferImpl(program.getMemory(), addr(0x200));
InstructionPrototype proto = program.getLanguage().parse(membuf, context, false); InstructionPrototype proto = program.getLanguage().parse(membuf, context, false);
listing.createInstruction(addr(0x200), proto, membuf, context); listing.createInstruction(addr(0x200), proto, membuf, context, 0);
Symbol symbol = symbolTable.getPrimarySymbol(addr(0x200)); Symbol symbol = symbolTable.getPrimarySymbol(addr(0x200));
assertEquals("LAB_00000200", symbol.getName()); assertEquals("LAB_00000200", symbol.getName());

View file

@ -97,7 +97,7 @@ public class SymbolUtilities2Test extends AbstractGhidraHeadedIntegrationTest {
new ProgramProcessorContext(program.getProgramContext(), addr(0x200)); new ProgramProcessorContext(program.getProgramContext(), addr(0x200));
DumbMemBufferImpl membuf = new DumbMemBufferImpl(program.getMemory(), addr(0x200)); DumbMemBufferImpl membuf = new DumbMemBufferImpl(program.getMemory(), addr(0x200));
InstructionPrototype proto = program.getLanguage().parse(membuf, context, false); InstructionPrototype proto = program.getLanguage().parse(membuf, context, false);
listing.createInstruction(addr(0x200), proto, membuf, context); listing.createInstruction(addr(0x200), proto, membuf, context, 0);
Symbol symbol = symbolTable.getPrimarySymbol(addr(0x200)); Symbol symbol = symbolTable.getPrimarySymbol(addr(0x200));
assertEquals("LAB_CODE_0200", symbol.getName()); assertEquals("LAB_CODE_0200", symbol.getName());

View file

@ -1063,7 +1063,7 @@ public class CodeManagerTest extends AbstractGenericTest {
MemBuffer buf = new DumbMemBufferImpl(mem, atAddr); MemBuffer buf = new DumbMemBufferImpl(mem, atAddr);
ProcessorContext context = new ProgramProcessorContext(program.getProgramContext(), atAddr); ProcessorContext context = new ProgramProcessorContext(program.getProgramContext(), atAddr);
InstructionPrototype proto = program.getLanguage().parse(buf, context, false); InstructionPrototype proto = program.getLanguage().parse(buf, context, false);
listing.createInstruction(atAddr, proto, buf, context); listing.createInstruction(atAddr, proto, buf, context, 0);
} }
} }

View file

@ -15,10 +15,9 @@
*/ */
package ghidra.feature.vt.api.markupitem; package ghidra.feature.vt.api.markupitem;
import static ghidra.feature.vt.api.main.VTMarkupItemApplyActionType.REPLACE; import static ghidra.feature.vt.api.main.VTMarkupItemApplyActionType.*;
import static ghidra.feature.vt.db.VTTestUtils.addr; import static ghidra.feature.vt.db.VTTestUtils.*;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.*;
import static org.junit.Assert.assertTrue;
import java.util.List; import java.util.List;
@ -379,7 +378,7 @@ public class DataTypeMarkupItemTest extends AbstractVTMarkupItemTest {
new ProgramProcessorContext(program.getProgramContext(), atAddress); new ProgramProcessorContext(program.getProgramContext(), atAddress);
InstructionPrototype proto = program.getLanguage().parse(buf, context, false); InstructionPrototype proto = program.getLanguage().parse(buf, context, false);
Instruction createdInstruction = Instruction createdInstruction =
listing.createInstruction(atAddress, proto, buf, context); listing.createInstruction(atAddress, proto, buf, context, 0);
commit = true; commit = true;
return createdInstruction; return createdInstruction;
} }

View file

@ -182,7 +182,8 @@ public abstract class PcodeEmit {
*/ */
void resolveFinalFallthrough() throws IOException { void resolveFinalFallthrough() throws IOException {
try { try {
if (fallOverride == null || fallOverride.equals(getStartAddress().add(fallOffset))) { if (fallOverride == null) {
// handles both length-override and fallthrough override cases
return; return;
} }
} }

View file

@ -505,19 +505,6 @@ abstract class PseudoCodeUnit implements CodeUnit {
return program.getListing().getCodeUnitBefore(address); return program.getListing().getCodeUnitBefore(address);
} }
/**
* Return true if the given CodeUnit follows directly after this code unit.
*
* @throws ConcurrentModificationException
* if this object is no longer valid.
*/
@Override
public boolean isSuccessor(CodeUnit codeUnit) {
Address min = codeUnit.getMinAddress();
return this.getMaxAddress().isSuccessor(min);
}
@Override @Override
public String getComment(int commentType) { public String getComment(int commentType) {
return comments.get(commentType); return comments.get(commentType);

View file

@ -146,6 +146,18 @@ public class PseudoInstruction extends PseudoCodeUnit implements Instruction, In
return b0; return b0;
} }
@Override
public int getParsedLength() {
// length-override is not supported
return getLength();
}
@Override
public byte[] getParsedBytes() throws MemoryAccessException {
// length-override is not supported
return getBytes();
}
@Override @Override
public Register getBaseContextRegister() { public Register getBaseContextRegister() {
return procContext.getBaseContextRegister(); return procContext.getBaseContextRegister();
@ -505,6 +517,16 @@ public class PseudoInstruction extends PseudoCodeUnit implements Instruction, In
this.flowOverride = flowOverride != null ? flowOverride : FlowOverride.NONE; this.flowOverride = flowOverride != null ? flowOverride : FlowOverride.NONE;
} }
@Override
public void setLengthOverride(int length) {
throw new UnsupportedOperationException();
}
@Override
public boolean isLengthOverridden() {
return false;
}
@Override @Override
public boolean isFallThroughOverridden() { public boolean isFallThroughOverridden() {
return fallThroughOverride != null; return fallThroughOverride != null;

View file

@ -22,7 +22,8 @@ import ghidra.program.database.function.OverlappingFunctionException;
import ghidra.program.database.module.TreeManager; import ghidra.program.database.module.TreeManager;
import ghidra.program.database.symbol.FunctionSymbol; import ghidra.program.database.symbol.FunctionSymbol;
import ghidra.program.model.address.*; import ghidra.program.model.address.*;
import ghidra.program.model.data.*; import ghidra.program.model.data.DataType;
import ghidra.program.model.data.DataTypeManager;
import ghidra.program.model.lang.*; import ghidra.program.model.lang.*;
import ghidra.program.model.listing.*; import ghidra.program.model.listing.*;
import ghidra.program.model.mem.MemBuffer; import ghidra.program.model.mem.MemBuffer;
@ -107,7 +108,7 @@ class ListingDB implements Listing {
@Override @Override
public Instruction getInstructionContaining(Address addr) { public Instruction getInstructionContaining(Address addr) {
return codeMgr.getInstructionContaining(addr); return codeMgr.getInstructionContaining(addr, false);
} }
@Override @Override
@ -261,14 +262,15 @@ class ListingDB implements Listing {
} }
@Override @Override
public PropertyMap getPropertyMap(String propertyName) { public PropertyMap<?> getPropertyMap(String propertyName) {
return codeMgr.getPropertyMap(propertyName); return codeMgr.getPropertyMap(propertyName);
} }
@Override @Override
public Instruction createInstruction(Address addr, InstructionPrototype prototype, public Instruction createInstruction(Address addr, InstructionPrototype prototype,
MemBuffer memBuf, ProcessorContextView context) throws CodeUnitInsertionException { MemBuffer memBuf, ProcessorContextView context, int length)
return codeMgr.createCodeUnit(addr, prototype, memBuf, context); throws CodeUnitInsertionException {
return codeMgr.createCodeUnit(addr, prototype, memBuf, context, length);
} }
@Override @Override
@ -278,8 +280,7 @@ class ListingDB implements Listing {
} }
@Override @Override
public Data createData(Address addr, DataType dataType) public Data createData(Address addr, DataType dataType) throws CodeUnitInsertionException {
throws CodeUnitInsertionException {
return codeMgr.createCodeUnit(addr, dataType, dataType.getLength()); return codeMgr.createCodeUnit(addr, dataType, dataType.getLength());
} }
@ -292,8 +293,7 @@ class ListingDB implements Listing {
@Override @Override
public void clearCodeUnits(Address startAddr, Address endAddr, boolean clearContext) { public void clearCodeUnits(Address startAddr, Address endAddr, boolean clearContext) {
try { try {
codeMgr.clearCodeUnits(startAddr, endAddr, clearContext, codeMgr.clearCodeUnits(startAddr, endAddr, clearContext, TaskMonitor.DUMMY);
TaskMonitor.DUMMY);
} }
catch (CancelledException e) { catch (CancelledException e) {
// can't happen with dummy monitor // can't happen with dummy monitor

View file

@ -109,8 +109,10 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM
* property map (StringTranslations). * property map (StringTranslations).
* 19-Jan-2023 - version 26 Improved relocation data records to incorporate status and * 19-Jan-2023 - version 26 Improved relocation data records to incorporate status and
* byte-length when original FileBytes should be used. * byte-length when original FileBytes should be used.
* 10-Jul-2023 - VERSION 27 Add support for Instruction length override which utilizes
* unused flag bits.
*/ */
static final int DB_VERSION = 26; static final int DB_VERSION = 27;
/** /**
* UPGRADE_REQUIRED_BFORE_VERSION should be changed to DB_VERSION anytime the * UPGRADE_REQUIRED_BFORE_VERSION should be changed to DB_VERSION anytime the

View file

@ -155,11 +155,16 @@ public class CodeManager implements ErrorHandler, ManagerDB {
while (addrIter.hasNext()) { while (addrIter.hasNext()) {
monitor.checkCancelled(); monitor.checkCancelled();
Address addr = addrIter.next(); Address addr = addrIter.next();
Instruction instr = getInstructionAt(addr);
if (instr == null) {
continue;
}
try { try {
long offset = oldFallThroughs.getLong(addr); long offset = oldFallThroughs.getLong(addr);
Address toAddr = addr.getNewAddress(offset); Address toAddr = addr.getNewAddress(offset);
refMgr.addMemoryReference(addr, toAddr, RefType.FALL_THROUGH, instr.setFallThrough(toAddr);
SourceType.USER_DEFINED, Reference.MNEMONIC);
} }
catch (NoValueException e) { catch (NoValueException e) {
// skip // skip
@ -531,6 +536,16 @@ public class CodeManager implements ErrorHandler, ManagerDB {
if (flowOverride != FlowOverride.NONE) { if (flowOverride != FlowOverride.NONE) {
lastInstruction.setFlowOverride(flowOverride); lastInstruction.setFlowOverride(flowOverride);
} }
try {
if (protoInstr.isLengthOverridden()) {
lastInstruction.setLengthOverride(protoInstr.getLength());
}
}
catch (CodeUnitInsertionException e) {
// unexpected - should have been caught previously
throw new RuntimeException(e);
}
} }
if (errorAddr != null && conflictCodeUnit == null && if (errorAddr != null && conflictCodeUnit == null &&
@ -581,21 +596,34 @@ public class CodeManager implements ErrorHandler, ManagerDB {
* @param prototype instruction definition object * @param prototype instruction definition object
* @param memBuf the MemBuffer to use to get the bytes from memory * @param memBuf the MemBuffer to use to get the bytes from memory
* @param context object that has the state of all the registers. * @param context object that has the state of all the registers.
* @return the new instruction * @param length instruction byte-length (must be in the range 0..prototype.getLength()).
* @exception CodeUnitInsertionException thrown if code unit * If smaller than the prototype length it must have a value no greater than 7, otherwise
* overlaps with an existing code unit * an error will be thrown. A value of 0 or greater-than-or-equal the prototype length
* will be ignored and not impose and override length. The length value must be a multiple
* of the {@link Language#getInstructionAlignment() instruction alignment} .
* @return the newly created instruction.
* @throws CodeUnitInsertionException thrown if the new Instruction would overlap and
* existing {@link CodeUnit} or the specified {@code length} is unsupported.
* @throws IllegalArgumentException if a negative {@code length} is specified.
*/ */
public Instruction createCodeUnit(Address address, InstructionPrototype prototype, public Instruction createCodeUnit(Address address, InstructionPrototype prototype,
MemBuffer memBuf, ProcessorContextView context) throws CodeUnitInsertionException { MemBuffer memBuf, ProcessorContextView context, int length)
throws CodeUnitInsertionException {
lock.acquire(); lock.acquire();
creatingInstruction = true; creatingInstruction = true;
try { try {
Address endAddr = address.addNoWrap(prototype.getLength() - 1); int forcedLengthOverride = InstructionDB.checkLengthOverride(length, prototype);
if (length == 0) {
length = prototype.getLength();
}
Address endAddr = address.addNoWrap(length - 1);
checkValidAddressRange(address, endAddr); checkValidAddressRange(address, endAddr);
InstructionDB inst = addInstruction(address, endAddr, prototype, memBuf, context); InstructionDB inst = addInstruction(address, endAddr, prototype, memBuf, context);
if (forcedLengthOverride != 0) {
inst.doSetLengthOverride(forcedLengthOverride);
}
// fire event // fire event
program.setChanged(ChangeManager.DOCR_CODE_ADDED, address, endAddr, null, inst); program.setChanged(ChangeManager.DOCR_CODE_ADDED, address, endAddr, null, inst);
@ -1370,30 +1398,49 @@ public class CodeManager implements ErrorHandler, ManagerDB {
} }
/** /**
* Returns the instruction whose min address is less than or equal to the specified address and * Returns an instruction whose min address is less than or equal to the specified address and
* whose max address is greater than or equal to the specified address. * whose max address is greater than or equal to the specified address.
* If {@code usePrototypeLength==true}
* <pre>{@literal * <pre>{@literal
* instruction.minAddress() <= addr <= instruction.maxAddress() * instruction.getMinAddress() <= addr <=
* instruction.getMinAddress().add(instruction.getPrototype().getLength() - 1)
* }</pre> * }</pre>
* * If {@code usePrototypeLength==false}
* <pre>{@literal
* instruction.getMinAddress() <= addr <= instruction.getMaxAddress()
* }</pre>
* The use of the prototype length is required when guarding against memory modifications. If
* a length-override is present only one of the entangled instructions will be returned and is
* intended to simply indicate the presence of a conflict.
* @param address the address to be contained * @param address the address to be contained
* @param usePrototypeLength if actual prototype length should be considered when identifying a
* conflict (required when checking for memory modification conflicts), otherwise code unit
* length is used. These lengths can vary when a
* {@link Instruction#setLengthOverride(int) length-override} is in affect for an instruction.
* @return the instruction containing the specified address, or null if a * @return the instruction containing the specified address, or null if a
* instruction does not exist * instruction does not exist
*/ */
public Instruction getInstructionContaining(Address address) { public Instruction getInstructionContaining(Address address, boolean usePrototypeLength) {
lock.acquire(); lock.acquire();
try { try {
Instruction instr = getInstructionAt(address); Instruction instr = getInstructionAt(address);
if (instr != null) { if (instr != null) {
return instr; return instr;
} }
instr = this.getInstructionBefore(address); instr = this.getInstructionBefore(address);
if (instr != null) {
if (instr != null && instr.contains(address)) { Address endAddr;
if (usePrototypeLength && instr.isLengthOverridden()) {
endAddr = instr.getMinAddress().add(instr.getParsedLength() - 1);
}
else {
endAddr = instr.getMaxAddress();
}
if (address.compareTo(endAddr) <= 0) {
return instr; return instr;
} }
}
return null; return null;
} }
finally { finally {
@ -1639,8 +1686,14 @@ public class CodeManager implements ErrorHandler, ManagerDB {
nextInstEndAddr = nextInstAddr; nextInstEndAddr = nextInstAddr;
int protoID = nextInstRec.getIntValue(InstDBAdapter.PROTO_ID_COL); int protoID = nextInstRec.getIntValue(InstDBAdapter.PROTO_ID_COL);
InstructionPrototype proto = protoMgr.getPrototype(protoID); InstructionPrototype proto = protoMgr.getPrototype(protoID);
int len = proto != null ? proto.getLength() int len;
: nextInstAddr.getAddressSpace().getAddressableUnitSize(); if (proto != null) {
len = InstructionDB.getLength(proto,
nextInstRec.getByteValue(InstDBAdapter.FLAGS_COL));
}
else {
len = nextInstAddr.getAddressSpace().getAddressableUnitSize();
}
if (len > 1) { if (len > 1) {
try { try {
nextInstEndAddr = nextInstAddr.addNoWrap(len - 1); nextInstEndAddr = nextInstAddr.addNoWrap(len - 1);
@ -1730,7 +1783,7 @@ public class CodeManager implements ErrorHandler, ManagerDB {
if (addr != AddressMap.INVALID_ADDRESS_KEY) { if (addr != AddressMap.INVALID_ADDRESS_KEY) {
lock.acquire(); lock.acquire();
try { try {
Instruction inst = getInstructionContaining(address); Instruction inst = getInstructionContaining(address, false);
if (inst != null) { if (inst != null) {
return null; return null;
} }
@ -2538,7 +2591,7 @@ public class CodeManager implements ErrorHandler, ManagerDB {
return; return;
} }
boolean fail = false; boolean fail = false;
if (getInstructionContaining(start) != null) { if (getInstructionContaining(start, false) != null) {
fail = true; fail = true;
} }
else { else {
@ -2574,7 +2627,7 @@ public class CodeManager implements ErrorHandler, ManagerDB {
if (!program.getMemory().contains(start, end)) { if (!program.getMemory().contains(start, end)) {
return false; return false;
} }
if (getInstructionContaining(start) != null) { if (getInstructionContaining(start, false) != null) {
return false; return false;
} }
if (getDefinedDataContaining(start) != null) { if (getDefinedDataContaining(start) != null) {
@ -3168,13 +3221,21 @@ public class CodeManager implements ErrorHandler, ManagerDB {
* @param newFallThroughRef new fallthrough reference or null if removed * @param newFallThroughRef new fallthrough reference or null if removed
*/ */
public void fallThroughChanged(Address fromAddr, Reference newFallThroughRef) { public void fallThroughChanged(Address fromAddr, Reference newFallThroughRef) {
if (newFallThroughRef != null &&
newFallThroughRef.getReferenceType() != RefType.FALL_THROUGH) {
throw new IllegalArgumentException("invalid reftype");
}
lock.acquire(); lock.acquire();
try { try {
InstructionDB instr = getInstructionAt(addrMap.getKey(fromAddr, false)); InstructionDB instr = getInstructionAt(addrMap.getKey(fromAddr, false));
// TODO: Should prevent this if instruction is null or isInDelaySlot if (instr == null) {
if (instr != null) { // Do not allow fallthrough ref without instruction
instr.fallThroughChanged(newFallThroughRef); if (newFallThroughRef != null) {
refManager.delete(newFallThroughRef);
} }
return;
}
instr.fallThroughChanged(newFallThroughRef);
} }
finally { finally {
lock.release(); lock.release();

View file

@ -23,6 +23,7 @@ import org.apache.commons.lang3.StringUtils;
import db.DBRecord; import db.DBRecord;
import ghidra.program.database.*; import ghidra.program.database.*;
import ghidra.program.database.references.ReferenceDBManager;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressOutOfBoundsException; import ghidra.program.model.address.AddressOutOfBoundsException;
import ghidra.program.model.lang.*; import ghidra.program.model.lang.*;
@ -48,7 +49,7 @@ abstract class CodeUnitDB extends DatabaseObject implements CodeUnit, ProcessorC
protected long addr; protected long addr;
protected Address endAddr; protected Address endAddr;
protected int length; protected int length;
protected ReferenceManager refMgr; protected ReferenceDBManager refMgr;
protected ProgramDB program; protected ProgramDB program;
private DBRecord commentRec; private DBRecord commentRec;
@ -407,13 +408,6 @@ abstract class CodeUnitDB extends DatabaseObject implements CodeUnit, ProcessorC
return false; return false;
} }
@Override
public boolean isSuccessor(CodeUnit codeUnit) {
Address min = codeUnit.getMinAddress();
return this.getMaxAddress().isSuccessor(min);
}
@Override @Override
public Iterator<String> propertyNames() { public Iterator<String> propertyNames() {
PropertyMapManager upm = codeMgr.getPropertyMapManager(); PropertyMapManager upm = codeMgr.getPropertyMapManager();

View file

@ -15,8 +15,7 @@
*/ */
package ghidra.program.database.code; package ghidra.program.database.code;
import java.util.ArrayList; import java.util.*;
import java.util.List;
import db.DBRecord; import db.DBRecord;
import ghidra.program.database.DBObjectCache; import ghidra.program.database.DBObjectCache;
@ -29,6 +28,7 @@ import ghidra.program.model.mem.MemoryAccessException;
import ghidra.program.model.pcode.PcodeOp; import ghidra.program.model.pcode.PcodeOp;
import ghidra.program.model.scalar.Scalar; import ghidra.program.model.scalar.Scalar;
import ghidra.program.model.symbol.*; import ghidra.program.model.symbol.*;
import ghidra.program.model.util.CodeUnitInsertionException;
import ghidra.program.util.ChangeManager; import ghidra.program.util.ChangeManager;
import ghidra.util.Msg; import ghidra.util.Msg;
import ghidra.util.exception.NoValueException; import ghidra.util.exception.NoValueException;
@ -38,16 +38,21 @@ import ghidra.util.exception.NoValueException;
*/ */
public class InstructionDB extends CodeUnitDB implements Instruction, InstructionContext { public class InstructionDB extends CodeUnitDB implements Instruction, InstructionContext {
private static byte FALLTHROUGH_SET_MASK = (byte) 0x01; private static final byte FALLTHROUGH_SET_MASK = 0x01;
private static byte FALLTHROUGH_CLEAR_MASK = (byte) 0xfe; private static final byte FALLTHROUGH_CLEAR_MASK = ~FALLTHROUGH_SET_MASK;
private static byte FLOWOVERRIDE_MASK = (byte) 0x0e; private static final byte FLOW_OVERRIDE_SET_MASK = 0x0e;
private static byte FLOWOVERRIDE_CLEAR_MASK = (byte) 0xf1; private static final byte FLOW_OVERRIDE_CLEAR_MASK = ~FLOW_OVERRIDE_SET_MASK;
private static int FLOWOVERRIDE_SHIFT = 1; private static final int FLOW_OVERRIDE_SHIFT = 1;
private static final byte LENGTH_OVERRIDE_SET_MASK = 0x70;
private static final byte LENGTH_OVERRIDE_CLEAR_MASK = ~LENGTH_OVERRIDE_SET_MASK;
private static final int LENGTH_OVERRIDE_SHIFT = 4;
private InstructionPrototype proto; private InstructionPrototype proto;
private byte flags; private byte flags;
private FlowOverride flowOverride; private FlowOverride flowOverride;
private int lengthOverride;
private final static Address[] EMPTY_ADDR_ARRAY = new Address[0]; private final static Address[] EMPTY_ADDR_ARRAY = new Address[0];
private volatile boolean clearingFallThroughs = false; private volatile boolean clearingFallThroughs = false;
@ -68,7 +73,8 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio
this.proto = proto; this.proto = proto;
this.flags = flags; this.flags = flags;
flowOverride = flowOverride =
FlowOverride.getFlowOverride((flags & FLOWOVERRIDE_MASK) >> FLOWOVERRIDE_SHIFT); FlowOverride.getFlowOverride((flags & FLOW_OVERRIDE_SET_MASK) >> FLOW_OVERRIDE_SHIFT);
refreshLength();
} }
@Override @Override
@ -83,6 +89,36 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio
return proto.hasDelaySlots() ? (length * 2) : length; return proto.hasDelaySlots() ? (length * 2) : length;
} }
private void refreshLength() {
length = proto.getLength();
lengthOverride = (flags & LENGTH_OVERRIDE_SET_MASK) >> LENGTH_OVERRIDE_SHIFT;
if (lengthOverride != 0 && lengthOverride < length) {
length = lengthOverride;
}
else {
lengthOverride = 0;
}
}
/**
* Get the instruction code unit length based upon its prototype and flags
* which will be used to check for a length-override condition.
* @param proto instruction prototype
* @param flags instruction flags
* @return instruction code unit length
*/
static int getLength(InstructionPrototype proto, byte flags) {
int length = proto.getLength();
int lengthOverride = (flags & LENGTH_OVERRIDE_SET_MASK) >> LENGTH_OVERRIDE_SHIFT;
if (lengthOverride != 0 && lengthOverride < length) {
length = lengthOverride;
}
else {
lengthOverride = 0;
}
return length;
}
@Override @Override
protected boolean hasBeenDeleted(DBRecord rec) { protected boolean hasBeenDeleted(DBRecord rec) {
if (rec == null) { if (rec == null) {
@ -106,10 +142,11 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio
if (!newProto.equals(proto)) { if (!newProto.equals(proto)) {
return true; return true;
} }
length = proto.getLength();
flags = rec.getByteValue(InstDBAdapter.FLAGS_COL); flags = rec.getByteValue(InstDBAdapter.FLAGS_COL);
flowOverride = flowOverride =
FlowOverride.getFlowOverride((flags & FLOWOVERRIDE_MASK) >> FLOWOVERRIDE_SHIFT); FlowOverride.getFlowOverride((flags & FLOW_OVERRIDE_SET_MASK) >> FLOW_OVERRIDE_SHIFT);
refreshLength();
return false; return false;
} }
@ -142,6 +179,31 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio
return null; return null;
} }
@Override
public int getParsedLength() {
return isLengthOverridden() ? proto.getLength() : getLength();
}
@Override
public byte[] getParsedBytes() throws MemoryAccessException {
if (!isLengthOverridden()) {
return getBytes();
}
lock.acquire();
try {
checkIsValid();
int len = proto.getLength();
byte[] b = new byte[len];
if (len != getMemory().getBytes(address, b)) {
throw new MemoryAccessException("Failed to read " + len + " bytes at " + address);
}
return b;
}
finally {
lock.release();
}
}
@Override @Override
public Address getFallFrom() { public Address getFallFrom() {
lock.acquire(); lock.acquire();
@ -192,19 +254,22 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio
} }
} }
private Address getFallThroughReference() {
for (Reference ref : refMgr.getReferencesFrom(address)) {
if (ref.getReferenceType().isFallthrough() && ref.getToAddress().isMemoryAddress()) {
return ref.getToAddress();
}
}
return null;
}
@Override @Override
public Address getFallThrough() { public Address getFallThrough() {
lock.acquire(); lock.acquire();
try { try {
checkIsValid(); checkIsValid();
if (isFallThroughOverridden()) { if (isFallThroughOverridden()) {
for (Reference ref : refMgr.getReferencesFrom(address)) { return getFallThroughReference();
if (ref.getReferenceType().isFallthrough() &&
ref.getToAddress().isMemoryAddress()) {
return ref.getToAddress();
}
}
return null;
} }
return getDefaultFallThrough(); return getDefaultFallThrough();
} }
@ -221,7 +286,7 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio
return EMPTY_ADDR_ARRAY; return EMPTY_ADDR_ARRAY;
} }
ArrayList<Address> list = new ArrayList<>(); Set<Address> list = new HashSet<>();
for (Reference ref : refs) { for (Reference ref : refs) {
if (!ref.getReferenceType().isIndirect()) { if (!ref.getReferenceType().isIndirect()) {
list.add(ref.getToAddress()); list.add(ref.getToAddress());
@ -232,8 +297,7 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio
return EMPTY_ADDR_ARRAY; return EMPTY_ADDR_ARRAY;
} }
Address[] addrs = new Address[list.size()]; return list.toArray(new Address[list.size()]);
return list.toArray(addrs);
} }
@Override @Override
@ -537,8 +601,8 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio
} }
FlowType origFlowType = getFlowType(); FlowType origFlowType = getFlowType();
flags &= FLOWOVERRIDE_CLEAR_MASK; flags &= FLOW_OVERRIDE_CLEAR_MASK;
flags |= (flow.ordinal() << FLOWOVERRIDE_SHIFT); flags |= (flow.ordinal() << FLOW_OVERRIDE_SHIFT);
codeMgr.setFlags(addr, flags); codeMgr.setFlags(addr, flags);
flowOverride = flow; flowOverride = flow;
@ -623,22 +687,28 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio
/** /**
* Clear all existing fall-through references from address. * Clear all existing fall-through references from address.
* @param keepFallThroughRef if not null, corresponding fall-through reference will be preserved * @param keepFallThroughAddr if not null, corresponding fall-through reference will be
* preserved.
*/ */
private void clearFallThroughRefs(Reference keepFallThroughRef) { private void clearFallThroughRefs(Address keepFallThroughAddr) {
if (clearingFallThroughs) { if (clearingFallThroughs) {
return; return;
} }
refreshIfNeeded(); refreshIfNeeded();
clearingFallThroughs = true; clearingFallThroughs = true;
try { try {
boolean fallThroughPreserved = false;
for (Reference ref : refMgr.getReferencesFrom(address)) { for (Reference ref : refMgr.getReferencesFrom(address)) {
if (ref.getReferenceType() == RefType.FALL_THROUGH && if (ref.getReferenceType() == RefType.FALL_THROUGH) {
!ref.equals(keepFallThroughRef)) { if (!fallThroughPreserved && ref.getToAddress().equals(keepFallThroughAddr)) {
fallThroughPreserved = true; // only preserve one
}
else {
refMgr.delete(ref); refMgr.delete(ref);
} }
} }
} }
}
finally { finally {
clearingFallThroughs = false; clearingFallThroughs = false;
} }
@ -646,9 +716,16 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio
void fallThroughChanged(Reference fallThroughRef) { void fallThroughChanged(Reference fallThroughRef) {
if (!clearingFallThroughs) { if (!clearingFallThroughs) {
clearFallThroughRefs(fallThroughRef); Address fallThroughAddr = fallThroughRef != null ? fallThroughRef.getToAddress() : null;
setFallthroughOverride(fallThroughRef != null && clearFallThroughRefs(fallThroughAddr); // ensure there is only one fallthrough ref
fallThroughRef.getReferenceType() == RefType.FALL_THROUGH); if (fallThroughAddr == null) { // fallthrough ref removed
setFallthroughOverride(false);
addLengthOverrideFallthroughRef(); // restore length-override fallthrough if needed
}
else {
// enable fallthrough-override if fallThroughRef does not match length-override fallthrough
setFallthroughOverride(!fallThroughAddr.equals(getLengthOverrideFallThrough()));
}
} }
} }
@ -661,8 +738,9 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio
flags &= FALLTHROUGH_CLEAR_MASK; flags &= FALLTHROUGH_CLEAR_MASK;
} }
codeMgr.setFlags(addr, flags); codeMgr.setFlags(addr, flags);
program.setChanged(ChangeManager.DOCR_FALLTHROUGH_CHANGED, address, address, null,
null);
} }
program.setChanged(ChangeManager.DOCR_FALLTHROUGH_CHANGED, address, address, null, null);
} }
@Override @Override
@ -673,8 +751,10 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio
if (!isFallThroughOverridden()) { if (!isFallThroughOverridden()) {
return; return;
} }
// clear fall-through override
clearFallThroughRefs(null); clearFallThroughRefs(null);
setFallthroughOverride(false); setFallthroughOverride(false);
addLengthOverrideFallthroughRef(); // restore length-override fallthrough if needed
} }
finally { finally {
lock.release(); lock.release();
@ -693,11 +773,12 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio
return; return;
} }
if (fallThroughAddr == null) { if (fallThroughAddr == null) {
// Fall-through eliminated - no reference added // Fall-through eliminated (i.e., terminal flow) - no reference added
clearFallThroughRefs(null); clearFallThroughRefs(null);
setFallthroughOverride(true); setFallthroughOverride(true);
} }
else { else {
// Adding fallthough ref will trigger override flag on callback
refMgr.addMemoryReference(address, fallThroughAddr, RefType.FALL_THROUGH, refMgr.addMemoryReference(address, fallThroughAddr, RefType.FALL_THROUGH,
SourceType.USER_DEFINED, Reference.MNEMONIC); SourceType.USER_DEFINED, Reference.MNEMONIC);
} }
@ -705,7 +786,107 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio
finally { finally {
lock.release(); lock.release();
} }
}
@Override
public void setLengthOverride(int len) throws CodeUnitInsertionException {
lock.acquire();
try {
checkDeleted();
if (doSetLengthOverride(len)) {
program.setChanged(ChangeManager.DOCR_LENGTH_OVERRIDE_CHANGED, address, address,
null, null);
}
}
finally {
lock.release();
}
}
/**
* Check and revise a specified {@code length} to arrive at a suitable length-override value.
* @param length instruction byte-length (must be in the range 0..{@code prototype-length}).
* If smaller than the prototype length it must have a value no greater than 7, otherwise
* an error will be thrown. A value of 0 or greater-than-or-equal the prototype length
* will be ignored and not impose and override length. The length value must be a multiple
* of the {@link Language#getInstructionAlignment() instruction alignment} .
* @param prototype instruction prototype
* @return length-override value (0 = disable length-override)
* @throws CodeUnitInsertionException thrown if the new Instruction would overlap and
* existing {@link CodeUnit} or the specified {@code length} is unsupported.
* @throws IllegalArgumentException if a negative {@code length} is specified.
*/
public static int checkLengthOverride(int length, InstructionPrototype prototype)
throws IllegalArgumentException, CodeUnitInsertionException {
if (length < 0) {
throw new IllegalArgumentException("Negative length not permitted");
}
int instrProtoLength = prototype.getLength();
if (length == 0 || length == instrProtoLength) {
return 0;
}
if (length > instrProtoLength) {
return 0;
}
int align = prototype.getLanguage().getInstructionAlignment();
if (length % align != 0) {
throw new CodeUnitInsertionException(
"Length(" + length + ") override must be a multiple of " + align + " bytes");
}
if (length > MAX_LENGTH_OVERRIDE) {
throw new CodeUnitInsertionException("Unsupported length override: " + length);
}
return length;
}
boolean doSetLengthOverride(int len) throws CodeUnitInsertionException {
int protoLength = proto.getLength();
len = checkLengthOverride(len, proto);
if (len == lengthOverride) {
return false; // no change
}
int instrLength = len != 0 ? len : protoLength;
if (instrLength > getLength()) {
Address newEndAddr = address.add(instrLength - 1);
Address nextCodeUnitAddr = codeMgr.getDefinedAddressAfter(address);
if (nextCodeUnitAddr != null && nextCodeUnitAddr.compareTo(newEndAddr) <= 0) {
throw new CodeUnitInsertionException("Length override of " + instrLength +
" conflicts with code unit at " + nextCodeUnitAddr);
}
}
flags &= LENGTH_OVERRIDE_CLEAR_MASK;
flags |= (len << LENGTH_OVERRIDE_SHIFT);
codeMgr.setFlags(addr, flags);
endAddr = null;
refreshLength();
addLengthOverrideFallthroughRef();
return true;
}
private void addLengthOverrideFallthroughRef() {
if (isLengthOverridden() && !isFallThroughOverridden()) {
// length-override always uses default fall-through address
refMgr.addMemoryReference(address, getDefaultFallThrough(), RefType.FALL_THROUGH,
SourceType.USER_DEFINED, Reference.MNEMONIC);
}
}
@Override
public boolean isLengthOverridden() {
refreshIfNeeded();
return lengthOverride != 0;
}
private Address getLengthOverrideFallThrough() {
return isLengthOverridden() ? getDefaultFallThrough() : null;
} }
private boolean addrsEqual(Address addr1, Address addr2) { private boolean addrsEqual(Address addr1, Address addr2) {

View file

@ -18,6 +18,8 @@ package ghidra.program.database.external;
import java.io.IOException; import java.io.IOException;
import java.util.*; import java.util.*;
import org.apache.commons.lang3.StringUtils;
import db.*; import db.*;
import ghidra.framework.store.FileSystem; import ghidra.framework.store.FileSystem;
import ghidra.program.database.ManagerDB; import ghidra.program.database.ManagerDB;
@ -90,8 +92,8 @@ public class ExternalManagerDB implements ManagerDB, ExternalManager {
@Override @Override
public void setProgram(ProgramDB program) { public void setProgram(ProgramDB program) {
this.program = program; this.program = program;
symbolMgr = (SymbolManager) program.getSymbolTable(); symbolMgr = program.getSymbolTable();
functionMgr = (FunctionManagerDB) program.getFunctionManager(); functionMgr = program.getFunctionManager();
scopeMgr = program.getNamespaceManager(); scopeMgr = program.getNamespaceManager();
} }
@ -153,8 +155,8 @@ public class ExternalManagerDB implements ManagerDB, ExternalManager {
oldAddrMap.decodeAddress(rec.getLongValue(OldExtRefAdapter.FROM_ADDR_COL)); oldAddrMap.decodeAddress(rec.getLongValue(OldExtRefAdapter.FROM_ADDR_COL));
int opIndex = rec.getShortValue(OldExtRefAdapter.OP_INDEX_COL); int opIndex = rec.getShortValue(OldExtRefAdapter.OP_INDEX_COL);
boolean userDefined = rec.getBooleanValue(OldExtRefAdapter.USER_DEFINED_COL); boolean userDefined = rec.getBooleanValue(OldExtRefAdapter.USER_DEFINED_COL);
String name = nameMap.get(rec.getLongValue(OldExtRefAdapter.EXT_NAME_ID_COL)); String extLibraryName = nameMap.get(rec.getLongValue(OldExtRefAdapter.EXT_NAME_ID_COL));
if (name == null) { if (extLibraryName == null) {
continue; // should not happen continue; // should not happen
} }
String label = rec.getString(OldExtRefAdapter.LABEL_COL); String label = rec.getString(OldExtRefAdapter.LABEL_COL);
@ -163,14 +165,11 @@ public class ExternalManagerDB implements ManagerDB, ExternalManager {
: null; : null;
try { try {
refMgr.addExternalReference(fromAddr, name, label, addr, refMgr.addExternalReference(fromAddr, extLibraryName, label, addr,
userDefined ? SourceType.USER_DEFINED : SourceType.IMPORTED, opIndex, userDefined ? SourceType.USER_DEFINED : SourceType.IMPORTED, opIndex,
RefType.DATA); RefType.DATA);
} }
catch (DuplicateNameException e) { catch (InvalidInputException | DuplicateNameException e) {
Msg.error(this, "Unexpected Exception: " + e.getMessage(), e);
}
catch (InvalidInputException e) {
Msg.error(this, "Unexpected Exception: " + e.getMessage(), e); Msg.error(this, "Unexpected Exception: " + e.getMessage(), e);
} }
monitor.setProgress(++cnt); monitor.setProgress(++cnt);
@ -283,7 +282,8 @@ public class ExternalManagerDB implements ManagerDB, ExternalManager {
private SourceType checkExternalLabel(String extLabel, Address extAddr, SourceType source) private SourceType checkExternalLabel(String extLabel, Address extAddr, SourceType source)
throws InvalidInputException { throws InvalidInputException {
if (extLabel == null || extLabel.length() == 0) { if (extLabel != null && (StringUtils.isBlank(extLabel) ||
SymbolUtilities.isReservedExternalDefaultName(extLabel, addrMap.getAddressFactory()))) {
extLabel = null; extLabel = null;
} }
if (extLabel == null && extAddr == null) { if (extLabel == null && extAddr == null) {
@ -312,8 +312,15 @@ public class ExternalManagerDB implements ManagerDB, ExternalManager {
throw new InvalidInputException("The namespace must be an external namespace."); throw new InvalidInputException("The namespace must be an external namespace.");
} }
sourceType = checkExternalLabel(extLabel, extAddr, sourceType); sourceType = checkExternalLabel(extLabel, extAddr, sourceType);
if (extAddr != null && !extAddr.isLoadedMemoryAddress()) { if (extAddr != null) {
throw new InvalidInputException("Invalid memory address"); if (!extAddr.isLoadedMemoryAddress()) {
throw new InvalidInputException("Invalid memory address: " + extAddr);
}
AddressSpace space = extAddr.getAddressSpace();
if (!space.equals(program.getAddressFactory().getAddressSpace(space.getName()))) {
throw new InvalidInputException(
"Memory address not defined for program: " + extAddr);
}
} }
lock.acquire(); lock.acquire();
try { try {
@ -359,7 +366,7 @@ public class ExternalManagerDB implements ManagerDB, ExternalManager {
/** /**
* Get the external location which best matches the specified parameters. * Get the external location which best matches the specified parameters.
* Preference is given to extLabel over extAddr * Preference is given to extLabel over extAddr
* @param libScope, the library namespace containing this external location. * @param library the library namespace containing this external location.
* @param extLabel the name of the external location. Can be null. * @param extLabel the name of the external location. Can be null.
* @param extAddr the address of function in the external program. Can be null * @param extAddr the address of function in the external program. Can be null
* @return the best matching ExternalLocation or null. * @return the best matching ExternalLocation or null.

View file

@ -2297,7 +2297,7 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
void checkRangeForInstructions(Address start, Address end) throws MemoryAccessException { void checkRangeForInstructions(Address start, Address end) throws MemoryAccessException {
CodeManager codeManager = program.getCodeManager(); CodeManager codeManager = program.getCodeManager();
Instruction instr = codeManager.getInstructionContaining(start); Instruction instr = codeManager.getInstructionContaining(start, true);
if (instr != null) { if (instr != null) {
throw new MemoryAccessException( throw new MemoryAccessException(
"Memory change conflicts with instruction at " + instr.getMinAddress()); "Memory change conflicts with instruction at " + instr.getMinAddress());

View file

@ -449,6 +449,9 @@ public class ReferenceDBManager implements ReferenceManager, ManagerDB, ErrorHan
if (!fromAddr.isMemoryAddress()) { if (!fromAddr.isMemoryAddress()) {
throw new IllegalArgumentException("From address must be memory address"); throw new IllegalArgumentException("From address must be memory address");
} }
if (!type.isData()) {
throw new IllegalArgumentException("Invalid stack reference type: " + type);
}
Function function = program.getFunctionManager().getFunctionContaining(fromAddr); Function function = program.getFunctionManager().getFunctionContaining(fromAddr);
if (function == null) { if (function == null) {
throw new IllegalArgumentException( throw new IllegalArgumentException(
@ -488,6 +491,9 @@ public class ReferenceDBManager implements ReferenceManager, ManagerDB, ErrorHan
if (!fromAddr.isMemoryAddress()) { if (!fromAddr.isMemoryAddress()) {
throw new IllegalArgumentException("From address must be memory address"); throw new IllegalArgumentException("From address must be memory address");
} }
if (!type.isData()) {
throw new IllegalArgumentException("Invalid register reference type: " + type);
}
removeAllFrom(fromAddr, opIndex); removeAllFrom(fromAddr, opIndex);
try { try {
return addRef(fromAddr, register.getAddress(), type, sourceType, opIndex, false, false, return addRef(fromAddr, register.getAddress(), type, sourceType, opIndex, false, false,
@ -573,9 +579,9 @@ public class ReferenceDBManager implements ReferenceManager, ManagerDB, ErrorHan
if (!fromAddr.isMemoryAddress()) { if (!fromAddr.isMemoryAddress()) {
throw new IllegalArgumentException("From address must be memory addresses"); throw new IllegalArgumentException("From address must be memory addresses");
} }
removeAllFrom(fromAddr, opIndex);
try { try {
if (symbolMgr.getPrimarySymbol(location.getExternalSpaceAddress()) != null) { if (symbolMgr.getPrimarySymbol(location.getExternalSpaceAddress()) != null) {
removeAllFrom(fromAddr, opIndex);
return addRef(fromAddr, location.getExternalSpaceAddress(), type, sourceType, return addRef(fromAddr, location.getExternalSpaceAddress(), type, sourceType,
opIndex, false, false, 0); opIndex, false, false, 0);
} }
@ -598,24 +604,17 @@ public class ReferenceDBManager implements ReferenceManager, ManagerDB, ErrorHan
public Reference addExternalReference(Address fromAddr, String libraryName, String extLabel, public Reference addExternalReference(Address fromAddr, String libraryName, String extLabel,
Address extAddr, SourceType sourceType, int opIndex, RefType type) Address extAddr, SourceType sourceType, int opIndex, RefType type)
throws InvalidInputException, DuplicateNameException { throws InvalidInputException, DuplicateNameException {
if (libraryName == null || libraryName.length() == 0) {
throw new InvalidInputException("A valid library name must be specified."); if (!fromAddr.isMemoryAddress()) {
throw new IllegalArgumentException("From addresses must be memory addresses");
} }
if (extLabel != null && extLabel.length() == 0) {
extLabel = null;
}
if (extLabel == null && extAddr == null) {
throw new InvalidInputException("Either an external label or address is required");
}
if (!fromAddr.isMemoryAddress() || (extAddr != null && !extAddr.isMemoryAddress())) {
throw new IllegalArgumentException(
"From and extAddr addresses must be memory addresses");
}
removeAllFrom(fromAddr, opIndex);
try { try {
ExternalManagerDB extMgr = (ExternalManagerDB) program.getExternalManager(); ExternalManagerDB extMgr = program.getExternalManager();
ExternalLocation extLoc = ExternalLocation extLoc =
extMgr.addExtLocation(libraryName, extLabel, extAddr, sourceType); extMgr.addExtLocation(libraryName, extLabel, extAddr, sourceType);
removeAllFrom(fromAddr, opIndex);
Address toAddr = extLoc.getExternalSpaceAddress(); Address toAddr = extLoc.getExternalSpaceAddress();
return addRef(fromAddr, toAddr, type, sourceType, opIndex, false, false, 0); return addRef(fromAddr, toAddr, type, sourceType, opIndex, false, false, 0);
@ -630,24 +629,16 @@ public class ReferenceDBManager implements ReferenceManager, ManagerDB, ErrorHan
public Reference addExternalReference(Address fromAddr, Namespace extNamespace, String extLabel, public Reference addExternalReference(Address fromAddr, Namespace extNamespace, String extLabel,
Address extAddr, SourceType sourceType, int opIndex, RefType type) Address extAddr, SourceType sourceType, int opIndex, RefType type)
throws InvalidInputException, DuplicateNameException { throws InvalidInputException, DuplicateNameException {
if (extNamespace == null || !extNamespace.isExternal()) { if (!fromAddr.isMemoryAddress()) {
throw new InvalidInputException("The namespace must be an external namespace."); throw new IllegalArgumentException("From addresses must be memory addresses");
} }
if (extLabel != null && extLabel.length() == 0) {
extLabel = null;
}
if (extLabel == null && extAddr == null) {
throw new InvalidInputException("Either an external label or address is required");
}
if (!fromAddr.isMemoryAddress() || (extAddr != null && !extAddr.isMemoryAddress())) {
throw new IllegalArgumentException(
"From and extAddr addresses must be memory addresses");
}
removeAllFrom(fromAddr, opIndex);
try { try {
ExternalManagerDB extMgr = (ExternalManagerDB) program.getExternalManager(); ExternalManagerDB extMgr = program.getExternalManager();
ExternalLocation extLoc = ExternalLocation extLoc =
extMgr.addExtLocation(extNamespace, extLabel, extAddr, sourceType); extMgr.addExtLocation(extNamespace, extLabel, extAddr, sourceType);
removeAllFrom(fromAddr, opIndex);
Address toAddr = extLoc.getExternalSpaceAddress(); Address toAddr = extLoc.getExternalSpaceAddress();
return addRef(fromAddr, toAddr, type, sourceType, opIndex, false, false, 0); return addRef(fromAddr, toAddr, type, sourceType, opIndex, false, false, 0);
} }

View file

@ -26,7 +26,7 @@ import ghidra.program.model.mem.MemoryAccessException;
public class AllBytesHashCalculator implements HashCalculator { public class AllBytesHashCalculator implements HashCalculator {
@Override @Override
public int calcHash(int startHash, Instruction inst) throws MemoryAccessException { public int calcHash(int startHash, Instruction inst) throws MemoryAccessException {
byte[] bytes = inst.getBytes(); byte[] bytes = inst.getParsedBytes();
for(int i=0;i<bytes.length;++i) { for(int i=0;i<bytes.length;++i) {
startHash = SimpleCRC32.hashOneByte(startHash, bytes[i]); startHash = SimpleCRC32.hashOneByte(startHash, bytes[i]);
} }

View file

@ -20,6 +20,7 @@ package ghidra.program.model.listing;
import java.util.ConcurrentModificationException; import java.util.ConcurrentModificationException;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.lang.InstructionPrototype;
import ghidra.program.model.lang.Register; import ghidra.program.model.lang.Register;
import ghidra.program.model.mem.MemBuffer; import ghidra.program.model.mem.MemBuffer;
import ghidra.program.model.mem.MemoryAccessException; import ghidra.program.model.mem.MemoryAccessException;
@ -174,20 +175,19 @@ public interface CodeUnit extends MemBuffer, PropertySet {
*/ */
public void setCommentAsArray(int commentType, String[] comment); public void setCommentAsArray(int commentType, String[] comment);
/**
* Return true if the given CodeUnit follows
* directly after this code unit.
* @param codeUnit the codeUnit being tested to see if it follows this codeUnit.
*/
public boolean isSuccessor(CodeUnit codeUnit);
/** /**
* Get length of this code unit. * Get length of this code unit.
* NOTE: If an {@link Instruction#isLengthOverridden() instruction length-override} is
* set this method will return the reduced length.
* @return code unit length
*/ */
public int getLength(); public int getLength();
/** /**
* Get the bytes that make up this code unit. * Get the bytes that make up this code unit.
* NOTE: If an {@link Instruction#isLengthOverridden() instruction length-override} is
* set this method will not return all bytes associated with the
* {@link InstructionPrototype instruction prototype}.
* @return an array of bytes that are in memory at the codeunits address. The * @return an array of bytes that are in memory at the codeunits address. The
* array length is the same as the codeUnits length * array length is the same as the codeUnits length
* @throws MemoryAccessException if the full number of bytes could not be read. * @throws MemoryAccessException if the full number of bytes could not be read.

View file

@ -149,11 +149,6 @@ public class DataStub implements Data {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
@Override
public boolean isSuccessor(CodeUnit codeUnit) {
throw new UnsupportedOperationException();
}
@Override @Override
public int getLength() { public int getLength() {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();

View file

@ -19,10 +19,12 @@ import java.util.List;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.lang.*; import ghidra.program.model.lang.*;
import ghidra.program.model.mem.Memory;
import ghidra.program.model.mem.MemoryAccessException;
import ghidra.program.model.pcode.PcodeOp; import ghidra.program.model.pcode.PcodeOp;
import ghidra.program.model.pcode.PcodeOverride;
import ghidra.program.model.symbol.FlowType; import ghidra.program.model.symbol.FlowType;
import ghidra.program.model.symbol.RefType; import ghidra.program.model.symbol.RefType;
import ghidra.program.model.util.CodeUnitInsertionException;
/** /**
* Interface to define an instruction for a processor. * Interface to define an instruction for a processor.
@ -30,9 +32,10 @@ import ghidra.program.model.symbol.RefType;
public interface Instruction extends CodeUnit, ProcessorContext { public interface Instruction extends CodeUnit, ProcessorContext {
public static final int INVALID_DEPTH_CHANGE = InstructionPrototype.INVALID_DEPTH_CHANGE; // 2^24 public static final int INVALID_DEPTH_CHANGE = InstructionPrototype.INVALID_DEPTH_CHANGE; // 2^24
public static final int MAX_LENGTH_OVERRIDE = 7;
/** /**
* Get the prototype for this instruction. * @return the prototype for this instruction.
*/ */
public InstructionPrototype getPrototype(); public InstructionPrototype getPrototype();
@ -46,6 +49,7 @@ public interface Instruction extends CodeUnit, ProcessorContext {
/** /**
* Get objects used by this operand (Address, Scalar, Register ...) * Get objects used by this operand (Address, Scalar, Register ...)
* @param opIndex index of the operand. * @param opIndex index of the operand.
* @return objects used by this operand (Address, Scalar, Register ...)
*/ */
public Object[] getOpObjects(int opIndex); public Object[] getOpObjects(int opIndex);
@ -108,6 +112,7 @@ public interface Instruction extends CodeUnit, ProcessorContext {
/** /**
* Get the operand reference type for the given operand index. * Get the operand reference type for the given operand index.
* @param index operand index * @param index operand index
* @return the operand reference type for the given operand index.
*/ */
public RefType getOperandRefType(int index); public RefType getOperandRefType(int index);
@ -134,7 +139,7 @@ public interface Instruction extends CodeUnit, ProcessorContext {
public Address getFallThrough(); public Address getFallThrough();
/** /**
* Get the Address for the instruction that fell through to * @return the Address for the instruction that fell through to
* this instruction. * this instruction.
* This is useful for handling instructions that are found * This is useful for handling instructions that are found
* in a delay slot. * in a delay slot.
@ -159,32 +164,86 @@ public interface Instruction extends CodeUnit, ProcessorContext {
public Address[] getDefaultFlows(); public Address[] getDefaultFlows();
/** /**
* Get the flow type of this instruction (how this * @return the flow type of this instruction (how this
* instruction flows to the next instruction). * instruction flows to the next instruction).
*/ */
public FlowType getFlowType(); public FlowType getFlowType();
/** /**
* Returns true if this instruction has no execution flow other than fall-through. * @return true if this instruction has no execution flow other than fall-through.
*/ */
public boolean isFallthrough(); public boolean isFallthrough();
/** /**
* Returns true if this instruction has a fall-through flow. * @return true if this instruction has a fall-through flow.
*/ */
public boolean hasFallthrough(); public boolean hasFallthrough();
/** /**
* Returns the flow override which may have been set on this instruction. * @return the flow override which may have been set on this instruction.
*/ */
public FlowOverride getFlowOverride(); public FlowOverride getFlowOverride();
/** /**
* Set the flow override for this instruction. * Set the flow override for this instruction.
* @param flowOverride * @param flowOverride flow override setting or {@link FlowOverride#NONE} to clear.
*/ */
public void setFlowOverride(FlowOverride flowOverride); public void setFlowOverride(FlowOverride flowOverride);
/**
* Set instruction length override. Specified length must be in the range 0..7 where 0 clears
* the setting and adopts the default length. The specified length must be less than the actual
* number of bytes consumed by the prototype and be a multiple of the language specified
* instruction alignment.
* <p>
* NOTE: Use of the feature with a delay slot instruction is discouraged.
* @param length effective instruction code unit length.
* @throws CodeUnitInsertionException if expanding instruction length conflicts with another
* instruction or length is not a multiple of the language specified instruction alignment.
*/
public void setLengthOverride(int length) throws CodeUnitInsertionException;
/**
* Determine if an instruction length override has been set.
* @return true if length override has been set else false.
*/
public boolean isLengthOverridden();
/**
* Get the actual number of bytes parsed when forming this instruction. While this method
* will generally return the same value as {@link #getLength()}, its value will differ when
* {@link #setLengthOverride(int)} has been used. In addition, it is important to note that
* {@link #getMaxAddress()} will always reflect a non-overlapping address which reflects
* {@link #getLength()}.
* This method is equivalent to the following code for a given instruction:
* <br>
* <pre>
* {@link InstructionPrototype} proto = instruction.{@link #getPrototype()};
* int length = proto.{@link InstructionPrototype#getLength() getLength()};
* </pre>
* @return the actual number of bytes parsed when forming this instruction
*/
public int getParsedLength();
/**
* Get the actual bytes parsed when forming this instruction. While this method
* will generally return the same value as {@link #getBytes()}, it will return more bytes when
* {@link #setLengthOverride(int)} has been used. In this override situation, the bytes
* returned will generally duplicate some of the parsed bytes associated with the next
* instruction that this instruction overlaps.
* This method is equivalent to the following code for a given instruction:
* <br>
* <pre>
* {@link InstructionPrototype} proto = instruction.{@link #getPrototype()};
* {@link Memory} mem = instruction.{@link #getMemory()};
* byte[] bytes = mem.getBytes(instruction.{@link #getAddress()}, proto.getLength());
* int length = proto.{@link InstructionPrototype#getLength() getLength()};
* </pre>
* @return the actual number of bytes parsed when forming this instruction
* @throws MemoryAccessException if the full number of bytes could not be read
*/
public byte[] getParsedBytes() throws MemoryAccessException;
/** /**
* Get an array of PCode operations (micro code) that this instruction * Get an array of PCode operations (micro code) that this instruction
* performs. Flow overrides are not factored into pcode. * performs. Flow overrides are not factored into pcode.
@ -223,21 +282,22 @@ public interface Instruction extends CodeUnit, ProcessorContext {
* some RISC processors such as SPARC and the PA-RISC. This * some RISC processors such as SPARC and the PA-RISC. This
* returns an integer instead of a boolean in case some other * returns an integer instead of a boolean in case some other
* processor executes more than one instruction from a delay slot. * processor executes more than one instruction from a delay slot.
* @return delay slot depth (number of instructions)
*/ */
public int getDelaySlotDepth(); public int getDelaySlotDepth();
/** /**
* Return true if this instruction was disassembled in a delay slot * @return true if this instruction was disassembled in a delay slot
*/ */
public boolean isInDelaySlot(); public boolean isInDelaySlot();
/** /**
* Get the instruction following this one in address order. * @return the instruction following this one in address order or null if none found.
*/ */
public Instruction getNext(); public Instruction getNext();
/** /**
* Get the instruction before this one in address order. * @return the instruction before this one in address order or null if none found.
*/ */
public Instruction getPrevious(); public Instruction getPrevious();
@ -256,7 +316,7 @@ public interface Instruction extends CodeUnit, ProcessorContext {
public void clearFallThroughOverride(); public void clearFallThroughOverride();
/** /**
* Returns true if this instructions fallthrough has been overriden. * @return true if this instructions fallthrough has been overriden.
*/ */
public boolean isFallThroughOverridden(); public boolean isFallThroughOverridden();

View file

@ -28,7 +28,8 @@ import ghidra.util.Msg;
public class InstructionPcodeOverride implements PcodeOverride { public class InstructionPcodeOverride implements PcodeOverride {
protected Instruction instr; protected final Instruction instr;
private boolean callOverrideApplied = false; private boolean callOverrideApplied = false;
private boolean jumpOverrideApplied = false; private boolean jumpOverrideApplied = false;
private boolean callOtherCallOverrideApplied = false; private boolean callOtherCallOverrideApplied = false;
@ -64,7 +65,7 @@ public class InstructionPcodeOverride implements PcodeOverride {
public Address getFallThroughOverride() { public Address getFallThroughOverride() {
Address defaultFallAddr = instr.getDefaultFallThrough(); Address defaultFallAddr = instr.getDefaultFallThrough();
Address fallAddr = instr.getFallThrough(); Address fallAddr = instr.getFallThrough();
if (fallAddr != null && !fallAddr.equals(defaultFallAddr)) { if (fallAddr != null && (instr.isLengthOverridden() || !fallAddr.equals(defaultFallAddr))) {
return fallAddr; return fallAddr;
} }
return null; return null;

View file

@ -146,21 +146,26 @@ public class InstructionStub implements Instruction {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
@Override
public boolean isSuccessor(CodeUnit codeUnit) {
throw new UnsupportedOperationException();
}
@Override @Override
public int getLength() { public int getLength() {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
@Override
public int getParsedLength() {
throw new UnsupportedOperationException();
}
@Override @Override
public byte[] getBytes() throws MemoryAccessException { public byte[] getBytes() throws MemoryAccessException {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
@Override
public byte[] getParsedBytes() throws MemoryAccessException {
throw new UnsupportedOperationException();
}
@Override @Override
public void getBytesInCodeUnit(byte[] buffer, int bufferOffset) throws MemoryAccessException { public void getBytesInCodeUnit(byte[] buffer, int bufferOffset) throws MemoryAccessException {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
@ -458,6 +463,16 @@ public class InstructionStub implements Instruction {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
@Override
public void setLengthOverride(int length) {
throw new UnsupportedOperationException();
}
@Override
public boolean isLengthOverridden() {
throw new UnsupportedOperationException();
}
@Override @Override
public void setFlowOverride(FlowOverride flowOverride) { public void setFlowOverride(FlowOverride flowOverride) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();

View file

@ -602,17 +602,22 @@ public interface Listing {
* changes will result in a <code>ContextChangeException</code> * changes will result in a <code>ContextChangeException</code>
* *
* @param addr the address at which to create an instruction * @param addr the address at which to create an instruction
* @param prototype the InstructionPrototype the describes the type of * @param prototype the InstructionPrototype that describes the type of instruction to create.
* instruction to create. * @param memBuf buffer that provides the bytes that make up the instruction.
* @param memBuf buffer that provides the bytes that make up the
* instruction.
* @param context the processor context at this location. * @param context the processor context at this location.
* @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. * @return the newly created instruction.
* @exception CodeUnitInsertionException thrown if the new Instruction would * @throws CodeUnitInsertionException thrown if the new Instruction would overlap and
* overlap and existing Instruction or defined data. * existing {@link CodeUnit} or the specified {@code length} is unsupported.
* @throws IllegalArgumentException if a negative {@code length} is specified.
*/ */
public Instruction createInstruction(Address addr, InstructionPrototype prototype, public Instruction createInstruction(Address addr, InstructionPrototype prototype,
MemBuffer memBuf, ProcessorContextView context) throws CodeUnitInsertionException; MemBuffer memBuf, ProcessorContextView context, int length)
throws CodeUnitInsertionException;
/** /**
* Creates a complete set of instructions. A preliminary pass will be made * Creates a complete set of instructions. A preliminary pass will be made
@ -659,8 +664,7 @@ public interface Listing {
* @exception CodeUnitInsertionException thrown if the new Instruction would * @exception CodeUnitInsertionException thrown if the new Instruction would
* overlap and existing Instruction or defined data. * overlap and existing Instruction or defined data.
*/ */
public Data createData(Address addr, DataType dataType) public Data createData(Address addr, DataType dataType) throws CodeUnitInsertionException;
throws CodeUnitInsertionException;
/** /**
* Clears any code units in the given range returning everything to "db"s, * Clears any code units in the given range returning everything to "db"s,

View file

@ -282,13 +282,14 @@ public class StubListing implements Listing {
} }
@Override @Override
public PropertyMap getPropertyMap(String propertyName) { public PropertyMap<?> getPropertyMap(String propertyName) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
@Override @Override
public Instruction createInstruction(Address addr, InstructionPrototype prototype, public Instruction createInstruction(Address addr, InstructionPrototype prototype,
MemBuffer memBuf, ProcessorContextView context) throws CodeUnitInsertionException { MemBuffer memBuf, ProcessorContextView context, int forcedLengthOverride)
throws CodeUnitInsertionException {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }

View file

@ -18,6 +18,7 @@ package ghidra.program.model.symbol;
import java.util.List; import java.util.List;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.Library; import ghidra.program.model.listing.Library;
import ghidra.util.exception.DuplicateNameException; import ghidra.util.exception.DuplicateNameException;
import ghidra.util.exception.InvalidInputException; import ghidra.util.exception.InvalidInputException;
@ -167,92 +168,121 @@ public interface ExternalManager {
* Adds a new external library name * Adds a new external library name
* @param libraryName the new external library name to add. * @param libraryName the new external library name to add.
* @param source the source of this external library * @param source the source of this external library
* @return library * @return library external {@link Library namespace}
* @throws InvalidInputException * @throws InvalidInputException if {@code libraryName} is invalid or null. A library name
* with spaces or the empty string are not permitted.
* Neither {@code extLabel} nor {@code extAddr} was specified properly.
* @throws DuplicateNameException if another non-Library namespace has the same name * @throws DuplicateNameException if another non-Library namespace has the same name
*/ */
public Library addExternalLibraryName(String libraryName, SourceType source) public Library addExternalLibraryName(String libraryName, SourceType source)
throws InvalidInputException, DuplicateNameException; throws InvalidInputException, DuplicateNameException;
/** /**
* Get or create an external location associated with an library/file named extName * Get or create an external location associated with a library/file named {@code libraryName}
* and the label within that file specified by extLabel * and the location within that file identified by {@code extLabel} and/or its memory address
* {@code extAddr}. Either or both {@code extLabel} or {@code extAddr} must be specified.
* @param libraryName the external library name * @param libraryName the external library name
* @param extLabel the external label * @param extLabel the external label or null
* @param extAddr the external address * @param extAddr the external memory address or null
* @param sourceType the source type of this external library's symbol * @param sourceType the source type of this external library's symbol
* @return external location * @return external location
* @throws InvalidInputException * @throws InvalidInputException if {@code libraryName} is invalid or null, or an invalid
* {@code extlabel} is specified. Names with spaces or the empty string are not permitted.
* Neither {@code extLabel} nor {@code extAddr} was specified properly.
* @throws DuplicateNameException if another non-Library namespace has the same name * @throws DuplicateNameException if another non-Library namespace has the same name
* @throws IllegalArgumentException if an invalid {@code extAddr} was specified.
*/ */
public ExternalLocation addExtLocation(String libraryName, String extLabel, Address extAddr, public ExternalLocation addExtLocation(String libraryName, String extLabel, Address extAddr,
SourceType sourceType) throws InvalidInputException, DuplicateNameException; SourceType sourceType) throws InvalidInputException, DuplicateNameException;
/** /**
* Get or create an external location in the indicated parent namespace with the specified name. * Create an external location in the indicated external parent namespace
* and identified by {@code extLabel} and/or its memory address
* {@code extAddr}. Either or both {@code extLabel} or {@code extAddr} must be specified.
* @param extNamespace the external namespace * @param extNamespace the external namespace
* @param extLabel the external label * @param extLabel the external label or null
* @param extAddr the external address * @param extAddr the external memory address or null
* @param sourceType the source type of this external library's symbol * @param sourceType the source type of this external library's symbol
* @return external location * @return external location
* @throws InvalidInputException * @throws InvalidInputException if an invalid {@code extlabel} is specified.
* Names with spaces or the empty string are not permitted.
* Neither {@code extLabel} nor {@code extAddr} was specified properly.
* @throws IllegalArgumentException if an invalid {@code extAddr} was specified.
*/ */
public ExternalLocation addExtLocation(Namespace extNamespace, String extLabel, Address extAddr, public ExternalLocation addExtLocation(Namespace extNamespace, String extLabel, Address extAddr,
SourceType sourceType) throws InvalidInputException; SourceType sourceType) throws InvalidInputException;
/** /**
* Get or create an external location in the indicated parent namespace with the specified name. * Get or create an external location in the indicated external parent namespace
* and identified by {@code extLabel} and/or its memory address
* {@code extAddr}. Either or both {@code extLabel} or {@code extAddr} must be specified.
* @param extNamespace the external namespace * @param extNamespace the external namespace
* @param extLabel the external label * @param extLabel the external label or null
* @param extAddr the external address * @param extAddr the external memory address or null
* @param sourceType the source type of this external library's symbol * @param sourceType the source type of this external library's symbol
* @param reuseExisting if true, this will return an existing matching external * @param reuseExisting if true, this will return an existing matching external
* location instead of creating a new one. * location instead of creating a new one.
* @return external location * @return external location
* @throws InvalidInputException * @throws InvalidInputException if an invalid {@code extlabel} is specified.
* Names with spaces or the empty string are not permitted.
* Neither {@code extLabel} nor {@code extAddr} was specified properly.
* @throws IllegalArgumentException if an invalid {@code extAddr} was specified.
*/ */
public ExternalLocation addExtLocation(Namespace extNamespace, String extLabel, Address extAddr, public ExternalLocation addExtLocation(Namespace extNamespace, String extLabel, Address extAddr,
SourceType sourceType, boolean reuseExisting) SourceType sourceType, boolean reuseExisting)
throws InvalidInputException; throws InvalidInputException;
/** /**
* Get or create an external location associated with an library/file named extName * Create an external {@link Function} in the external {@link Library} namespace
* and the label within that file specified by extLabel * {@code libararyName} and identified by {@code extLabel} and/or its memory address
* {@code extAddr}. Either or both {@code extLabel} or {@code extAddr} must be specified.
* @param libraryName the external library name * @param libraryName the external library name
* @param extLabel the external label * @param extLabel label within the external program, may be null if extAddr is not null
* @param extAddr the external address * @param extAddr memory address within the external program, may be null
* @param sourceType the source type of this external library's symbol * @param sourceType the source type of this external library's symbol
* @return external location * @return external location
* @throws InvalidInputException * @throws InvalidInputException if {@code libraryName} is invalid or null, or an invalid
* {@code extlabel} is specified. Names with spaces or the empty string are not permitted.
* Neither {@code extLabel} nor {@code extAddr} was specified properly.
* @throws DuplicateNameException if another non-Library namespace has the same name * @throws DuplicateNameException if another non-Library namespace has the same name
* @throws IllegalArgumentException if an invalid {@code extAddr} was specified.
*/ */
public ExternalLocation addExtFunction(String libraryName, String extLabel, Address extAddr, public ExternalLocation addExtFunction(String libraryName, String extLabel, Address extAddr,
SourceType sourceType) throws InvalidInputException, DuplicateNameException; SourceType sourceType) throws InvalidInputException, DuplicateNameException;
/** /**
* Get or create an external function location associated with an library/file named extName * Create an external {@link Function} in the indicated external parent namespace
* and the label within that file specified by extLabel * and identified by {@code extLabel} and/or its memory address
* {@code extAddr}. Either or both {@code extLabel} or {@code extAddr} must be specified.
* @param extNamespace the external namespace * @param extNamespace the external namespace
* @param extLabel the external label * @param extLabel the external label or null
* @param extAddr the external address * @param extAddr the external memory address or null
* @param sourceType the source type of this external library's symbol * @param sourceType the source type of this external library's symbol
* @return external location * @return external location
* @throws InvalidInputException * @throws InvalidInputException if an invalid {@code extlabel} is specified.
* Names with spaces or the empty string are not permitted.
* Neither {@code extLabel} nor {@code extAddr} was specified properly.
* @throws IllegalArgumentException if an invalid {@code extAddr} was specified.
*/ */
public ExternalLocation addExtFunction(Namespace extNamespace, String extLabel, Address extAddr, public ExternalLocation addExtFunction(Namespace extNamespace, String extLabel, Address extAddr,
SourceType sourceType) throws InvalidInputException; SourceType sourceType) throws InvalidInputException;
/** /**
* Get or create an external function location associated with an library/file named extName * Get or create an external {@link Function} in the indicated external parent namespace
* and the label within that file specified by extLabel * and identified by {@code extLabel} and/or its memory address
* {@code extAddr}. Either or both {@code extLabel} or {@code extAddr} must be specified.
* @param extNamespace the external namespace * @param extNamespace the external namespace
* @param extLabel the external label * @param extLabel the external label or null
* @param extAddr the external memory address or null
* @param sourceType the source type of this external library's symbol * @param sourceType the source type of this external library's symbol
* @param reuseExisting if true, will return any existing matching location instead of * @param reuseExisting if true, will return any existing matching location instead of
* creating a new one. If false, will prefer to create a new one as long as the specified * creating a new one. If false, will prefer to create a new one as long as the specified
* address is not null and not used in an existing location. * address is not null and not used in an existing location.
* @return external location * @return external location
* @throws InvalidInputException * @throws InvalidInputException if an invalid {@code extlabel} is specified.
* Names with spaces or the empty string are not permitted.
* Neither {@code extLabel} nor {@code extAddr} was specified properly.
* @throws IllegalArgumentException if an invalid {@code extAddr} was specified.
*/ */
public ExternalLocation addExtFunction(Namespace extNamespace, String extLabel, Address extAddr, public ExternalLocation addExtFunction(Namespace extNamespace, String extLabel, Address extAddr,
SourceType sourceType, boolean reuseExisting) SourceType sourceType, boolean reuseExisting)

View file

@ -17,13 +17,24 @@
package ghidra.program.model.symbol; package ghidra.program.model.symbol;
import ghidra.program.model.address.Address;
import ghidra.program.model.block.CodeBlock;
import ghidra.program.model.listing.CodeUnit;
import ghidra.program.model.listing.Instruction;
import ghidra.program.model.pcode.PcodeOp;
/** /**
* Class to define reference types. * {@link RefType} defines reference types used to specify the nature of a directional
* relationship between a source-location and a destination-location where a "location"
* may correspond to a {@link Address}, {@link CodeUnit}, {@link CodeBlock} or other
* code related objects. Reference types are generally identified as either
* {@link #isData() data} (see {@link DataRefType}) or {@link #isFlow() flow}
* (see {@link FlowType}).
*/ */
public abstract class RefType { public abstract class RefType {
// //
// NOTE: // IMPORTANT:
// - When creating a new flow type, be sure to add code to the RefTypeFactory // - When creating a new flow type, be sure to add code to the RefTypeFactory
// - Once a RefType value is defined it must be maintained for upgrade use // - Once a RefType value is defined it must be maintained for upgrade use
// //
@ -70,86 +81,181 @@ public abstract class RefType {
static final byte __UNKNOWNPARAM = 107; static final byte __UNKNOWNPARAM = 107;
@Deprecated @Deprecated
static final byte __STACK_READ = 110; // Use __READ instead - required for upgrade use static final byte __STACK_READ = 110; // Use __READ instead - retained for upgrade use
@Deprecated @Deprecated
static final byte __STACK_WRITE = 111; // Use __WRITE instead - required for upgrade use static final byte __STACK_WRITE = 111; // Use __WRITE instead - retained for upgrade use
static final byte __EXTERNAL_REF = 113; static final byte __EXTERNAL_REF = 113;
static final byte __UNKNOWNDATA_IND = 114; static final byte __UNKNOWNDATA_IND = 114;
static final byte __DYNAMICDATA = 127; static final byte __DYNAMICDATA = 127;
/**
* {@link #INVALID} corresponds to an unknown {@link FlowType} which encountered an error
* when determining the flow-type of the instruction at the from address.
*/
public static final FlowType INVALID = public static final FlowType INVALID =
new FlowType.Builder(__INVALID, "INVALID") new FlowType.Builder(__INVALID, "INVALID")
.setHasFall() .setHasFall()
.build(); .build();
/**
* {@link #FLOW} corresponds to a complex or generic {@link FlowType}. This may be used
* to describe the flow-type of an instruction or code-block which contains multiple outbound
* flows of differing types. This should not be used for a specific flow {@link Reference}.
*/
public static final FlowType FLOW = public static final FlowType FLOW =
new FlowType.Builder(__UNKNOWNFLOW, "FLOW") new FlowType.Builder(__UNKNOWNFLOW, "FLOW")
.setHasFall() .setHasFall()
.build(); .build();
/**
* {@link #FALL_THROUGH} corresponds to an instruction fall-through override where modeling
* requires a fall-through instruction to convey a branch around other {@link CodeUnit}s.
* While this may be freely used to describe the flow-type of a code-block or its relationship
* to another code-block, its use with a {@link Reference} is <b>reserved for internal use</b>
* to reflect an {@link Instruction} fall-through-override or length-override condition.
*/
public static final FlowType FALL_THROUGH = public static final FlowType FALL_THROUGH =
new FlowType.Builder(__FALL_THROUGH, "FALL_THROUGH") new FlowType.Builder(__FALL_THROUGH, "FALL_THROUGH")
.setHasFall() .setHasFall()
.build(); .build();
/**
* {@link #UNCONDITIONAL_JUMP} corresponds to an unconditional jump/branch {@link FlowType}.
* This may be used to describe the flow-type of an instruction or code-block, or
* {@link Reference} to another instruction or code-block.
*/
public static final FlowType UNCONDITIONAL_JUMP = public static final FlowType UNCONDITIONAL_JUMP =
new FlowType.Builder(__UNCONDITIONAL_JUMP, "UNCONDITIONAL_JUMP") new FlowType.Builder(__UNCONDITIONAL_JUMP, "UNCONDITIONAL_JUMP")
.setIsJump() .setIsJump()
.build(); .build();
/**
* {@link #CONDITIONAL_JUMP} corresponds to a conditional jump/branch {@link FlowType}.
* This may be used to describe the flow-type of an instruction or code-block, or
* {@link Reference} to another instruction or code-block.
*/
public static final FlowType CONDITIONAL_JUMP = public static final FlowType CONDITIONAL_JUMP =
new FlowType.Builder(__CONDITIONAL_JUMP, "CONDITIONAL_JUMP") new FlowType.Builder(__CONDITIONAL_JUMP, "CONDITIONAL_JUMP")
.setHasFall() .setHasFall()
.setIsJump() .setIsJump()
.setIsConditional() .setIsConditional()
.build(); .build();
/**
* {@link #UNCONDITIONAL_CALL} corresponds to an unconditional call {@link FlowType} with fall-through.
* This may be used to describe the flow-type of an instruction or code-block, or
* call {@link Reference} to another instruction or code-block.
*/
public static final FlowType UNCONDITIONAL_CALL = public static final FlowType UNCONDITIONAL_CALL =
new FlowType.Builder(__UNCONDITIONAL_CALL, "UNCONDITIONAL_CALL") new FlowType.Builder(__UNCONDITIONAL_CALL, "UNCONDITIONAL_CALL")
.setHasFall() .setHasFall()
.setIsCall() .setIsCall()
.build(); .build();
/**
* {@link #CONDITIONAL_CALL} corresponds to a conditional call {@link FlowType} with fall-through.
* This may be used to describe the flow-type of an instruction or code-block, or
* call {@link Reference} to another instruction or code-block.
*/
public static final FlowType CONDITIONAL_CALL = public static final FlowType CONDITIONAL_CALL =
new FlowType.Builder(__CONDITIONAL_CALL, "CONDITIONAL_CALL") new FlowType.Builder(__CONDITIONAL_CALL, "CONDITIONAL_CALL")
.setHasFall() .setHasFall()
.setIsCall() .setIsCall()
.setIsConditional() .setIsConditional()
.build(); .build();
/**
* {@link #TERMINATOR} corresponds to a terminal {@link FlowType} (e.g., return from a
* function). This may be used to describe the flow-type of an instruction or code-block
* but should generally not be used with a {@link Reference}.
*/
public static final FlowType TERMINATOR = public static final FlowType TERMINATOR =
new FlowType.Builder(__TERMINATOR, "TERMINATOR") new FlowType.Builder(__TERMINATOR, "TERMINATOR")
.setIsTerminal() .setIsTerminal()
.build(); .build();
/**
* {@link #COMPUTED_JUMP} corresponds to a computed jump/branch {@link FlowType}.
* This may be used to describe the flow-type of an instruction or code-block, or
* {@link Reference} to another instruction or code-block.
*/
public static final FlowType COMPUTED_JUMP = public static final FlowType COMPUTED_JUMP =
new FlowType.Builder(__COMPUTED_JUMP, "COMPUTED_JUMP") new FlowType.Builder(__COMPUTED_JUMP, "COMPUTED_JUMP")
.setIsJump() .setIsJump()
.setIsComputed() .setIsComputed()
.build(); .build();
/**
* {@link #TERMINATOR} corresponds to a terminal {@link FlowType} (e.g., conditional return
* from a function). This may be used to describe the flow-type of an instruction or code-block
* but should generally not be used with a {@link Reference}.
*/
public static final FlowType CONDITIONAL_TERMINATOR = public static final FlowType CONDITIONAL_TERMINATOR =
new FlowType.Builder(__CONDITIONAL_TERMINATOR, "CONDITIONAL_TERMINATOR") new FlowType.Builder(__CONDITIONAL_TERMINATOR, "CONDITIONAL_TERMINATOR")
.setHasFall() .setHasFall()
.setIsTerminal() .setIsTerminal()
.setIsConditional() .setIsConditional()
.build(); .build();
/**
* {@link #COMPUTED_CALL} corresponds to a computed call {@link FlowType} with fall-through.
* This may be used to describe the flow-type of an instruction or code-block, or
* call {@link Reference} to another instruction or code-block.
*/
public static final FlowType COMPUTED_CALL = public static final FlowType COMPUTED_CALL =
new FlowType.Builder(__COMPUTED_CALL, "COMPUTED_CALL") new FlowType.Builder(__COMPUTED_CALL, "COMPUTED_CALL")
.setHasFall() .setHasFall()
.setIsCall() .setIsCall()
.setIsComputed() .setIsComputed()
.build(); .build();
/**
* {@link #CALL_TERMINATOR} corresponds to an unconditional call {@link FlowType}
* followed by a terminal without fall-through (e.g., unconditional return from a function).
* This may be used to describe the flow-type of an instruction or code-block but
* should generally not be used with a {@link Reference}. A corresponding {@link Reference}
* should generally specify {@link #__UNCONDITIONAL_CALL}.
*/
public static final FlowType CALL_TERMINATOR = public static final FlowType CALL_TERMINATOR =
new FlowType.Builder(__CALL_TERMINATOR, "CALL_TERMINATOR") new FlowType.Builder(__CALL_TERMINATOR, "CALL_TERMINATOR")
.setIsCall() .setIsCall()
.setIsTerminal() .setIsTerminal()
.build(); .build();
/**
* {@link #COMPUTED_CALL_TERMINATOR} corresponds to an unconditional call {@link FlowType}
* followed by a terminal without fall-through (e.g., unconditional return from a function).
* This may be used to describe the flow-type of an instruction or code-block but
* should generally not be used with a {@link Reference}. A corresponding {@link Reference}
* should generally specify {@link #COMPUTED_CALL}.
*/
public static final FlowType COMPUTED_CALL_TERMINATOR = public static final FlowType COMPUTED_CALL_TERMINATOR =
new FlowType.Builder(__COMPUTED_CALL_TERMINATOR, "COMPUTED_CALL_TERMINATOR") new FlowType.Builder(__COMPUTED_CALL_TERMINATOR, "COMPUTED_CALL_TERMINATOR")
.setIsCall() .setIsCall()
.setIsTerminal() .setIsTerminal()
.setIsComputed() .setIsComputed()
.build(); .build();
/**
* {@link #CONDITIONAL_CALL_TERMINATOR} corresponds to a conditional call {@link FlowType}
* followed by a terminal without fall-through (e.g., unconditional return from a function).
* This may be used to describe the flow-type of an instruction or code-block but
* should generally not be used with a {@link Reference}. A corresponding {@link Reference}
* should generally specify {@link #CONDITIONAL_CALL}.
*/
public static final FlowType CONDITIONAL_CALL_TERMINATOR = public static final FlowType CONDITIONAL_CALL_TERMINATOR =
new FlowType.Builder(__CONDITIONAL_CALL_TERMINATOR, "CONDITIONAL_CALL_TERMINATOR") new FlowType.Builder(__CONDITIONAL_CALL_TERMINATOR, "CONDITIONAL_CALL_TERMINATOR")
.setIsCall() .setIsCall()
.setIsTerminal() .setIsTerminal()
.setIsConditional() .setIsConditional()
.build(); .build();
/**
* {@link #CONDITIONAL_COMPUTED_CALL} corresponds to a conditional computed call {@link FlowType}
* with fall-through. This may be used to describe the flow-type of an instruction or
* code-block, or call {@link Reference} to another instruction or code-block.
*/
public static final FlowType CONDITIONAL_COMPUTED_CALL = new FlowType.Builder( public static final FlowType CONDITIONAL_COMPUTED_CALL = new FlowType.Builder(
__CONDITIONAL_COMPUTED_CALL, "CONDITIONAL_COMPUTED_CALL") __CONDITIONAL_COMPUTED_CALL, "CONDITIONAL_COMPUTED_CALL")
.setHasFall() .setHasFall()
@ -157,6 +263,12 @@ public abstract class RefType {
.setIsComputed() .setIsComputed()
.setIsConditional() .setIsConditional()
.build(); .build();
/**
* {@link #CONDITIONAL_COMPUTED_JUMP} corresponds to a conditional computed jump/branch
* {@link FlowType}. This may be used to describe the flow-type of an instruction or
* code-block, or {@link Reference} to another instruction or code-block.
*/
public static final FlowType CONDITIONAL_COMPUTED_JUMP = public static final FlowType CONDITIONAL_COMPUTED_JUMP =
new FlowType.Builder(__CONDITIONAL_COMPUTED_JUMP, "CONDITIONAL_COMPUTED_JUMP") new FlowType.Builder(__CONDITIONAL_COMPUTED_JUMP, "CONDITIONAL_COMPUTED_JUMP")
.setHasFall() .setHasFall()
@ -164,31 +276,89 @@ public abstract class RefType {
.setIsComputed() .setIsComputed()
.setIsConditional() .setIsConditional()
.build(); .build();
/**
* {@link #JUMP_TERMINATOR} corresponds to a conditional jump/branch {@link FlowType}
* followed by a terminal without fall-through (e.g., unconditional return from a function).
* This may be used to describe the flow-type of an instruction or code-block but
* should generally not be used with a {@link Reference}. A corresponding {@link Reference}
* should generally specify {@link #CONDITIONAL_JUMP}.
*/
public static final FlowType JUMP_TERMINATOR = public static final FlowType JUMP_TERMINATOR =
new FlowType.Builder(__JUMP_TERMINATOR, "JUMP_TERMINATOR") new FlowType.Builder(__JUMP_TERMINATOR, "JUMP_TERMINATOR")
.setIsJump() .setIsJump()
.setIsTerminal() .setIsTerminal()
.build(); .build();
/**
* {@link #INDIRECTION} corresponds to a flow {@link Reference} placed on a pointer data location
* that is utilized indirectly by a computed jump/branch or call instruction.
*/
public static final FlowType INDIRECTION = public static final FlowType INDIRECTION =
new FlowType.Builder(__INDIRECTION, "INDIRECTION") new FlowType.Builder(__INDIRECTION, "INDIRECTION")
.build(); .build();
/**
* {@link #__CALL_OVERRIDE_UNCONDITIONAL} is used with a memory {@link Reference} to
* override the destination of an instruction {@link PcodeOp#CALL} or {@link PcodeOp#CALLIND}
* pcode operation. {@link PcodeOp#CALLIND} operations are changed to {@link PcodeOp#CALL}
* operations. The new call target is the "to" address of the {@link Reference}. The override
* only takes effect when the {@link Reference} is primary, and only when there is exactly
* one {@link #__CALL_OVERRIDE_UNCONDITIONAL} {@link Reference} at the "from" address of
* the reference.
*/
public static final FlowType CALL_OVERRIDE_UNCONDITIONAL = public static final FlowType CALL_OVERRIDE_UNCONDITIONAL =
new FlowType.Builder(__CALL_OVERRIDE_UNCONDITIONAL, "CALL_OVERRIDE_UNCONDITIONAL") new FlowType.Builder(__CALL_OVERRIDE_UNCONDITIONAL, "CALL_OVERRIDE_UNCONDITIONAL")
.setHasFall() .setHasFall()
.setIsCall() .setIsCall()
.setIsOverride() .setIsOverride()
.build(); .build();
/**
* {@link #JUMP_OVERRIDE_UNCONDITIONAL} is used with a memory {@link Reference} to
* override the destination of an instruction {@link PcodeOp#BRANCH} or {@link PcodeOp#CBRANCH}
* pcode operation. {@link PcodeOp#CBRANCH} operations are changed to {@link PcodeOp#BRANCH}
* operations. The new jump target is the "to" address of the {@link Reference}. The override
* only takes effect when the {@link Reference} is primary, and only when there is exactly
* one {@link #JUMP_OVERRIDE_UNCONDITIONAL} reference at the "from" address of
* the reference.
*/
public static final FlowType JUMP_OVERRIDE_UNCONDITIONAL = public static final FlowType JUMP_OVERRIDE_UNCONDITIONAL =
new FlowType.Builder(__JUMP_OVERRIDE_UNCONDITIONAL, "JUMP_OVERRIDE_UNCONDITIONAL") new FlowType.Builder(__JUMP_OVERRIDE_UNCONDITIONAL, "JUMP_OVERRIDE_UNCONDITIONAL")
.setIsJump() .setIsJump()
.setIsOverride() .setIsOverride()
.build(); .build();
/**
* {@link #CALLOTHER_OVERRIDE_CALL} is used to change a {@link PcodeOp#CALLOTHER} pcode operation
* to a {@link PcodeOp#CALL} operation. The new call target is the "to" address of the
* {@link Reference}. Any inputs to the original {@link PcodeOp#CALLOTHER} are discarded;
* the new {@link PcodeOp#CALL} may have inputs assigned to it during decompilation. The
* override only takes effect when the {@link Reference} is primary, and only when there is
* exactly one {@link #CALLOTHER_OVERRIDE_CALL} reference at the "from" address of the
* reference. Only the first {@link PcodeOp#CALLOTHER} operation at the "from" address of the
* reference is changed. Applying this override to instances of a {@link PcodeOp#CALLOTHER}
* that have an output is not recommended and can adversely affect decompilation
* (e.g., the decompiler may throw an exception). Note that this reference override takes
* precedence over {@link #CALLOTHER_OVERRIDE_JUMP} references.
*/
public static final FlowType CALLOTHER_OVERRIDE_CALL = public static final FlowType CALLOTHER_OVERRIDE_CALL =
new FlowType.Builder(__CALLOTHER_OVERRIDE_CALL, "CALLOTHER_OVERRIDE_CALL") new FlowType.Builder(__CALLOTHER_OVERRIDE_CALL, "CALLOTHER_OVERRIDE_CALL")
.setHasFall() .setHasFall()
.setIsCall() .setIsCall()
.setIsOverride() .setIsOverride()
.build(); .build();
/**
* {@link #CALLOTHER_OVERRIDE_CALL} is used to change a {@link PcodeOp#CALLOTHER} pcode
* operation to a {@link PcodeOp#BRANCH} operation. The new jump target is the "to" address
* of the {@link Reference}. The override only takes effect when the {@link Reference} is
* primary, and only when there is exactly one {@link #CALLOTHER_OVERRIDE_CALL} reference at
* the "from" address of the reference. Only the first {@link PcodeOp#CALLOTHER} operation
* at the "from" address of the reference is changed. Applying this override to an instance
* of a {@link PcodeOp#CALLOTHER} with output is not recommended
* (see {@link #CALLOTHER_OVERRIDE_CALL}).
*/
public static final FlowType CALLOTHER_OVERRIDE_JUMP = public static final FlowType CALLOTHER_OVERRIDE_JUMP =
new FlowType.Builder(__CALLOTHER_OVERRIDE_JUMP, "CALLOTHER_OVERRIDE_JUMP") new FlowType.Builder(__CALLOTHER_OVERRIDE_JUMP, "CALLOTHER_OVERRIDE_JUMP")
.setIsJump() .setIsJump()
@ -196,61 +366,80 @@ public abstract class RefType {
.build(); .build();
/** /**
* Reference type is unknown. * {@link #THUNK} type identifies the relationship between a thunk-function and its
* corresponding thunked-function which do not rely on a stored {@link Reference}.
*/ */
public static final RefType THUNK = new DataRefType(__DYNAMICDATA, "THUNK", 0); public static final RefType THUNK = new DataRefType(__DYNAMICDATA, "THUNK", 0);
/** /**
* Reference type assigned when data access is unknown. * {@link #DATA} type identifies a generic reference from either an instruction,
* when the read/write data access is unknown, or from pointer data when it refers to
* data or it's unknown if it refers to code. A pointer that is known to refer to code
* should generally have a {@link #INDIRECTION} type if used for by a computed
* jump/branch or call.
*/ */
public static final RefType DATA = new DataRefType(__UNKNOWNDATA, "DATA", 0); public static final RefType DATA = new DataRefType(__UNKNOWNDATA, "DATA", 0);
/** /**
* Reference type assigned when data (constant or pointer) is passed to a function * {@link #PARAM} type is used to identify data (constant or pointer) that is passed
* to a function.
*/ */
public static final RefType PARAM = new DataRefType(__UNKNOWNPARAM, "PARAM", 0); public static final RefType PARAM = new DataRefType(__UNKNOWNPARAM, "PARAM", 0);
/**
* {@link #DATA_IND} corresponds to a data {@link Reference} placed on a pointer data location
* that is utilized indirectly to access a data location.
* @deprecated use of this type is discouraged and may be eliminated in a future release.
* The type {@link #DATA} should generally be used in place of this type.
*/
public static final RefType DATA_IND = public static final RefType DATA_IND =
new DataRefType(__UNKNOWNDATA_IND, "DATA_IND", DataRefType.INDX); new DataRefType(__UNKNOWNDATA_IND, "DATA_IND", DataRefType.INDX);
/** /**
* Reference type assigned when data is being read. * {@link #READ} type identifies an instruction reference to a data location that is directly
* read.
*/ */
public static final RefType READ = new DataRefType(__READ, "READ", DataRefType.READX); public static final RefType READ = new DataRefType(__READ, "READ", DataRefType.READX);
/** /**
* Reference type assigned when data is being written. * {@link #WRITE} type identifies an instruction reference to a data location that is directly
* written.
*/ */
public static final RefType WRITE = new DataRefType(__WRITE, "WRITE", DataRefType.WRITEX); public static final RefType WRITE = new DataRefType(__WRITE, "WRITE", DataRefType.WRITEX);
/** /**
* Reference type assigned when data is read and written. * {@link #READ_WRITE} type identifies an instruction reference to a data location that is
* both directly read and written.
*/ */
public static final RefType READ_WRITE = public static final RefType READ_WRITE =
new DataRefType(__READ_WRITE, "READ_WRITE", DataRefType.READX | DataRefType.WRITEX); new DataRefType(__READ_WRITE, "READ_WRITE", DataRefType.READX | DataRefType.WRITEX);
/** /**
* Reference type assigned when data is being read. * {@link #READ_IND} type identifies an instruction reference to a data location that is
* indirectly read using a stored pointer or computed value.
*/ */
public static final RefType READ_IND = public static final RefType READ_IND =
new DataRefType(__READ_IND, "READ_IND", DataRefType.READX | DataRefType.INDX); new DataRefType(__READ_IND, "READ_IND", DataRefType.READX | DataRefType.INDX);
/** /**
* Reference type assigned when data is being written. * {@link #WRITE_IND} type identifies an instruction reference to a data location that is
* indirectly written using a stored pointer or computed value.
*/ */
public static final RefType WRITE_IND = public static final RefType WRITE_IND =
new DataRefType(__WRITE_IND, "WRITE_IND", DataRefType.WRITEX | DataRefType.INDX); new DataRefType(__WRITE_IND, "WRITE_IND", DataRefType.WRITEX | DataRefType.INDX);
/** /**
* Reference type assigned when data is read and written. * {@link #READ_WRITE_IND} type identifies an instruction reference to a data location that is
* both indirectly read and written using a stored pointer or computed value.
*/ */
public static final RefType READ_WRITE_IND = new DataRefType(__READ_WRITE_IND, "READ_WRITE_IND", public static final RefType READ_WRITE_IND = new DataRefType(__READ_WRITE_IND, "READ_WRITE_IND",
DataRefType.READX | DataRefType.WRITEX | DataRefType.INDX); DataRefType.READX | DataRefType.WRITEX | DataRefType.INDX);
/** /**
* Reference type used internally to identify external entry points. * {@link #EXTERNAL_REF} type is used internally to identify external entry point locations
* The use of this RefType for references to external library data or functions * using a from address of {@link Address#NO_ADDRESS}.
* <p>
* NOTE: The use of this type for references to external library data or functions
* is deprecated and should not be used for that purpose. * is deprecated and should not be used for that purpose.
*/ */
public static final RefType EXTERNAL_REF = new DataRefType(__EXTERNAL_REF, "EXTERNAL", 0); public static final RefType EXTERNAL_REF = new DataRefType(__EXTERNAL_REF, "EXTERNAL", 0);
@ -266,6 +455,9 @@ public abstract class RefType {
if (this == RefType.THUNK) { if (this == RefType.THUNK) {
return "Thunk"; return "Thunk";
} }
if (this == RefType.FALL_THROUGH) {
return "FallThrough";
}
if (isRead() && isWrite()) { if (isRead() && isWrite()) {
return "RW"; return "RW";
@ -301,6 +493,37 @@ public abstract class RefType {
return type; return type;
} }
/**
* Returns name of ref-type
* @return the name
*/
public String getName() {
return name;
}
@Override
public boolean equals(Object obj) {
if (obj == null || !getClass().equals(obj.getClass())) {
return false;
}
RefType other = (RefType) obj;
return type == other.type;
}
@Override
public int hashCode() {
return type;
}
@Override
public String toString() {
return name;
}
//
// Data related methods
//
/** /**
* Returns true if the reference is to data * Returns true if the reference is to data
* @return true if the reference is to data * @return true if the reference is to data
@ -325,6 +548,18 @@ public abstract class RefType {
return false; return false;
} }
//
// Flow related methods
//
/**
* Returns true if the reference is an instruction flow reference
* @return true if the reference is an instruction flow reference
*/
public boolean isFlow() {
return false;
}
/** /**
* Returns true if the reference is indirect * Returns true if the reference is indirect
* @return true if the reference is indirect * @return true if the reference is indirect
@ -336,14 +571,6 @@ public abstract class RefType {
return false; return false;
} }
/**
* Returns true if the reference is an instruction flow reference
* @return true if the reference is an instruction flow reference
*/
public boolean isFlow() {
return false;
}
/** /**
* Return true if this flow type is one that does not cause a break in control flow * Return true if this flow type is one that does not cause a break in control flow
* @return if this flow type is one that does not cause a break in control flow * @return if this flow type is one that does not cause a break in control flow
@ -416,30 +643,4 @@ public abstract class RefType {
return false; return false;
} }
/**
* Returns name of ref-type
* @return the name
*/
public String getName() {
return name;
}
@Override
public boolean equals(Object obj) {
if (obj == null || !getClass().equals(obj.getClass())) {
return false;
}
RefType other = (RefType) obj;
return type == other.type;
}
@Override
public int hashCode() {
return type;
}
@Override
public String toString() {
return name;
}
} }

View file

@ -17,6 +17,7 @@ package ghidra.program.model.symbol;
import ghidra.program.model.address.*; import ghidra.program.model.address.*;
import ghidra.program.model.lang.Register; import ghidra.program.model.lang.Register;
import ghidra.program.model.listing.Library;
import ghidra.program.model.listing.Variable; import ghidra.program.model.listing.Variable;
import ghidra.program.model.mem.MemoryBlock; import ghidra.program.model.mem.MemoryBlock;
import ghidra.util.exception.DuplicateNameException; import ghidra.util.exception.DuplicateNameException;
@ -69,7 +70,8 @@ public interface ReferenceManager {
/** /**
* Adds a memory reference. The first memory reference placed on * Adds a memory reference. The first memory reference placed on
* an operand will be made primary by default. All non-memory references * an operand will be made primary by default. All non-memory references
* will be removed from the specified operand. * will be removed from the specified operand. Certain reference {@link RefType types}
* may not be specified (e.g., {@link RefType#FALL_THROUGH}).
* @param fromAddr address of the codeunit where the reference occurs * @param fromAddr address of the codeunit where the reference occurs
* @param toAddr address of the location being referenced. * @param toAddr address of the location being referenced.
* Memory, stack, and register addresses are all permitted. * Memory, stack, and register addresses are all permitted.
@ -78,6 +80,7 @@ public interface ReferenceManager {
* @param opIndex the operand index * @param opIndex the operand index
* display of the operand making this reference * display of the operand making this reference
* @return new memory reference * @return new memory reference
* @throws IllegalArgumentException if unsupported {@link RefType type} is specified
*/ */
public Reference addMemoryReference(Address fromAddr, Address toAddr, RefType type, public Reference addMemoryReference(Address fromAddr, Address toAddr, RefType type,
SourceType source, int opIndex); SourceType source, int opIndex);
@ -109,10 +112,14 @@ public interface ReferenceManager {
* shiftValue parameter. The first memory reference placed on * shiftValue parameter. The first memory reference placed on
* an operand will be made primary by default. All non-memory references * an operand will be made primary by default. All non-memory references
* will be removed from the specified operand. * will be removed from the specified operand.
* @param fromAddr address for the "from" *
* @param toAddr computed as the value of the operand at opIndex shifted * @param fromAddr source/from memory address
* by the number of bits specified by shiftValue * @param toAddr destination/to memory address computed as some
* @param shiftValue shifted value * {@link ShiftedReference#getValue() base offset value} shifted left
* by the number of bits specified by shiftValue. The least-significant bits of toAddr
* offset should be 0's based upon the specified shiftValue since this value is shifted
* right to calculate the base offset value.
* @param shiftValue number of bits to shift
* @param type reference type - how the location is being referenced * @param type reference type - how the location is being referenced
* @param source the source of this reference * @param source the source of this reference
* @param opIndex the operand index * @param opIndex the operand index
@ -122,19 +129,27 @@ public interface ReferenceManager {
RefType type, SourceType source, int opIndex); RefType type, SourceType source, int opIndex);
/** /**
* Adds an external reference. If a reference already * Adds an external reference to an external symbol. If a reference already
* exists for the fromAddr and opIndex, the existing reference is replaced * exists at {@code fromAddr} and {@code opIndex} the existing reference is replaced
* with the new reference. * with a new reference. If the external symbol cannot be found, a new {@link Library}
* @param fromAddr from address (source of the reference) * and/or {@link ExternalLocation} symbol will be created which corresponds to the specified
* library/file named {@code libraryName}
* and the location within that file identified by {@code extLabel} and/or its memory address
* {@code extAddr}. Either or both {@code extLabel} or {@code extAddr} must be specified.
*
* @param fromAddr from memory address (source of the reference)
* @param libraryName name of external program * @param libraryName name of external program
* @param extLabel label within the external program, may be null if extAddr is not null * @param extLabel label within the external program, may be null if extAddr is not null
* @param extAddr address within the external program, may be null * @param extAddr memory address within the external program, may be null
* @param source the source of this reference * @param source the source of this reference
* @param opIndex operand index * @param opIndex operand index
* @param type reference type - how the location is being referenced * @param type reference type - how the location is being referenced
* @return new external space reference * @return new external space reference
* @throws InvalidInputException * @throws InvalidInputException if {@code libraryName} is invalid or null, or an invalid
* @throws DuplicateNameException * {@code extlabel} is specified. Names with spaces or the empty string are not permitted.
* Neither {@code extLabel} nor {@code extAddr} was specified properly.
* @throws DuplicateNameException if another non-Library namespace has the same name
* @throws IllegalArgumentException if an invalid {@code extAddr} was specified.
*/ */
public Reference addExternalReference(Address fromAddr, String libraryName, String extLabel, public Reference addExternalReference(Address fromAddr, String libraryName, String extLabel,
Address extAddr, SourceType source, int opIndex, RefType type) Address extAddr, SourceType source, int opIndex, RefType type)
@ -144,7 +159,8 @@ public interface ReferenceManager {
* Adds an external reference. If a reference already * Adds an external reference. If a reference already
* exists for the fromAddr and opIndex, the existing reference is replaced * exists for the fromAddr and opIndex, the existing reference is replaced
* with the new reference. * with the new reference.
* @param fromAddr from address (source of the reference) *
* @param fromAddr from memory address (source of the reference)
* @param extNamespace external namespace containing the named external label. * @param extNamespace external namespace containing the named external label.
* @param extLabel label within the external program, may be null if extAddr is not null * @param extLabel label within the external program, may be null if extAddr is not null
* @param extAddr address within the external program, may be null * @param extAddr address within the external program, may be null
@ -152,8 +168,11 @@ public interface ReferenceManager {
* @param opIndex operand index * @param opIndex operand index
* @param type reference type - how the location is being referenced * @param type reference type - how the location is being referenced
* @return new external space reference * @return new external space reference
* @throws InvalidInputException * @throws InvalidInputException if an invalid {@code extlabel} is specified.
* @throws DuplicateNameException * Names with spaces or the empty string are not permitted.
* Neither {@code extLabel} nor {@code extAddr} was specified properly.
* @throws DuplicateNameException if another non-Library namespace has the same name
* @throws IllegalArgumentException if an invalid {@code extAddr} was specified.
*/ */
public Reference addExternalReference(Address fromAddr, Namespace extNamespace, String extLabel, public Reference addExternalReference(Address fromAddr, Namespace extNamespace, String extLabel,
Address extAddr, SourceType source, int opIndex, RefType type) Address extAddr, SourceType source, int opIndex, RefType type)
@ -163,7 +182,8 @@ public interface ReferenceManager {
* Adds an external reference. If a reference already * Adds an external reference. If a reference already
* exists for the fromAddr and opIndex, the existing reference is replaced * exists for the fromAddr and opIndex, the existing reference is replaced
* with the new reference. * with the new reference.
* @param fromAddr from address (source of the reference) *
* @param fromAddr from memory address (source of the reference)
* @param opIndex operand index * @param opIndex operand index
* @param location external location * @param location external location
* @param source the source of this reference * @param source the source of this reference

View file

@ -661,17 +661,22 @@ public interface ChangeManager {
*/ */
public final static int DOCR_FLOWOVERRIDE_CHANGED = 163; public final static int DOCR_FLOWOVERRIDE_CHANGED = 163;
/**
* An instruction length override was changed for an instruction.
*/
public final static int DOCR_LENGTH_OVERRIDE_CHANGED = 164;
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
/** /**
* A custom format for a data type was added. * A custom format for a data type was added.
*/ */
public final static int DOCR_CUSTOM_FORMAT_ADDED = 164; public final static int DOCR_CUSTOM_FORMAT_ADDED = 165;
/** /**
* A custom format for a data type was removed. * A custom format for a data type was removed.
*/ */
public final static int DOCR_CUSTOM_FORMAT_REMOVED = 165; public final static int DOCR_CUSTOM_FORMAT_REMOVED = 166;
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
// //
@ -682,17 +687,17 @@ public interface ChangeManager {
/** /**
* An AddressSetPropertyMap was added. * An AddressSetPropertyMap was added.
*/ */
public final static int DOCR_ADDRESS_SET_PROPERTY_MAP_ADDED = 166; public final static int DOCR_ADDRESS_SET_PROPERTY_MAP_ADDED = 167;
/** /**
* An AddressSetPropertyMap was removed. * An AddressSetPropertyMap was removed.
*/ */
public final static int DOCR_ADDRESS_SET_PROPERTY_MAP_REMOVED = 167; public final static int DOCR_ADDRESS_SET_PROPERTY_MAP_REMOVED = 168;
/** /**
* An AddressSetPropertyMap was changed. * An AddressSetPropertyMap was changed.
*/ */
public final static int DOCR_ADDRESS_SET_PROPERTY_MAP_CHANGED = 168; public final static int DOCR_ADDRESS_SET_PROPERTY_MAP_CHANGED = 169;
/** /**
* An IntAddressSetPropertyMap was added. * An IntAddressSetPropertyMap was added.

View file

@ -72,10 +72,16 @@ public class InstructionUtils {
textBuf.append( textBuf.append(
"\nConstructor Line #'s:\n" + getString(debug.getConstructorLineNumbers(), true)) "\nConstructor Line #'s:\n" + getString(debug.getConstructorLineNumbers(), true))
.append('\n'); .append('\n');
textBuf.append("\nByte Length : " + instruction.getLength()); int len = instruction.getLength();
textBuf.append("\nByte Length : " + len);
if (instruction.isLengthOverridden()) {
textBuf.append("\n >>> reflects length override, actual length is " +
instruction.getParsedLength());
}
try { try {
byte[] bytes = instruction.getParsedBytes();
textBuf.append( textBuf.append(
"\nInstr Bytes : " + SleighDebugLogger.getFormattedBytes(instruction.getBytes())); "\nInstr Bytes : " + SleighDebugLogger.getFormattedBytes(bytes));
textBuf.append("\nMask : " + debug.getFormattedInstructionMask(-1)); textBuf.append("\nMask : " + debug.getFormattedInstructionMask(-1));
textBuf.append("\nMasked Bytes: " + debug.getFormattedMaskedValue(-1)).append('\n'); textBuf.append("\nMasked Bytes: " + debug.getFormattedMaskedValue(-1)).append('\n');
} }

View file

@ -162,7 +162,7 @@ public class ClipboardPluginTest extends AbstractGhidraHeadedIntegrationTest {
new ClassicSampleX86ProgramBuilder("notepad", false, this); new ClassicSampleX86ProgramBuilder("notepad", false, this);
// need a default label at 01002cf0, so make up a reference // need a default label at 01002cf0, so make up a reference
builder.createMemoryReference("01002ce5", "01002cf0", RefType.FALL_THROUGH, builder.createMemoryReference("01002ce5", "01002cf0", RefType.DATA,
SourceType.ANALYSIS); SourceType.ANALYSIS);
return builder.getProgram(); return builder.getProgram();