mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-04 18:29:37 +02:00
GP-3256 Added support for Instruction length-override
This commit is contained in:
parent
a820a36e94
commit
aefb7f2aed
67 changed files with 1895 additions and 574 deletions
|
@ -131,7 +131,8 @@ public class DebuggerCopyPlan {
|
|||
}
|
||||
long off = ins.getMinAddress().subtract(fromRange.getMinAddress());
|
||||
Address dest = intoAddress.add(off);
|
||||
intoListing.createInstruction(dest, ins.getPrototype(), ins, ins);
|
||||
intoListing.createInstruction(dest, ins.getPrototype(), ins, ins,
|
||||
ins.getLength());
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -143,8 +144,7 @@ public class DebuggerCopyPlan {
|
|||
|
||||
@Override
|
||||
public void copy(TraceProgramView from, AddressRange fromRange, Program into,
|
||||
Address intoAddress, TaskMonitor monitor)
|
||||
throws Exception {
|
||||
Address intoAddress, TaskMonitor monitor) throws Exception {
|
||||
Listing intoListing = into.getListing();
|
||||
for (Data data : from.getListing()
|
||||
.getDefinedData(new AddressSet(fromRange), true)) {
|
||||
|
@ -204,8 +204,8 @@ public class DebuggerCopyPlan {
|
|||
}
|
||||
}
|
||||
|
||||
private Namespace findOrCopyNamespace(Namespace ns, SymbolTable intoTable,
|
||||
Program into) throws Exception {
|
||||
private Namespace findOrCopyNamespace(Namespace ns, SymbolTable intoTable, Program into)
|
||||
throws Exception {
|
||||
if (ns.isGlobal()) {
|
||||
return into.getGlobalNamespace();
|
||||
}
|
||||
|
@ -442,8 +442,8 @@ public class DebuggerCopyPlan {
|
|||
public boolean isRequiresInitializedMemory(TraceProgramView from, Program dest) {
|
||||
return checkBoxes.entrySet().stream().anyMatch(ent -> {
|
||||
Copier copier = ent.getKey();
|
||||
return copier.isRequiresInitializedMemory() &&
|
||||
copier.isAvailable(from, dest) && ent.getValue().isSelected();
|
||||
return copier.isRequiresInitializedMemory() && copier.isAvailable(from, dest) &&
|
||||
ent.getValue().isSelected();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
*/
|
||||
package ghidra.trace.database.listing;
|
||||
|
||||
import static ghidra.lifecycle.Unfinished.TODO;
|
||||
import static ghidra.lifecycle.Unfinished.*;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.*;
|
||||
|
@ -278,11 +278,6 @@ public interface DBTraceCodeUnitAdapter extends TraceCodeUnit, MemBufferMixin {
|
|||
return DBTraceCommentAdapter.arrayFromComment(getComment(commentType));
|
||||
}
|
||||
|
||||
@Override
|
||||
default boolean isSuccessor(CodeUnit codeUnit) {
|
||||
return getMaxAddress().isSuccessor(codeUnit.getMinAddress());
|
||||
}
|
||||
|
||||
@Override
|
||||
default boolean contains(Address testAddr) {
|
||||
return getMinAddress().compareTo(testAddr) <= 0 && testAddr.compareTo(getMaxAddress()) <= 0;
|
||||
|
|
|
@ -21,12 +21,14 @@ import java.nio.ByteBuffer;
|
|||
import java.util.*;
|
||||
|
||||
import db.DBRecord;
|
||||
import ghidra.program.database.code.InstructionDB;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.lang.*;
|
||||
import ghidra.program.model.listing.ContextChangeException;
|
||||
import ghidra.program.model.listing.FlowOverride;
|
||||
import ghidra.program.model.mem.*;
|
||||
import ghidra.program.model.symbol.*;
|
||||
import ghidra.program.model.util.CodeUnitInsertionException;
|
||||
import ghidra.trace.database.DBTrace;
|
||||
import ghidra.trace.database.DBTraceUtils;
|
||||
import ghidra.trace.database.context.DBTraceRegisterContextManager;
|
||||
|
@ -36,7 +38,7 @@ import ghidra.trace.database.guest.InternalTracePlatform;
|
|||
import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree;
|
||||
import ghidra.trace.database.symbol.DBTraceReference;
|
||||
import ghidra.trace.database.symbol.DBTraceReferenceSpace;
|
||||
import ghidra.trace.model.Lifespan;
|
||||
import ghidra.trace.model.*;
|
||||
import ghidra.trace.model.Trace.TraceInstructionChangeType;
|
||||
import ghidra.trace.model.guest.TracePlatform;
|
||||
import ghidra.trace.model.listing.TraceInstruction;
|
||||
|
@ -52,17 +54,21 @@ import ghidra.util.database.annot.*;
|
|||
* The implementation of {@link TraceInstruction} for {@link DBTrace}
|
||||
*/
|
||||
@DBAnnotatedObjectInfo(version = 0)
|
||||
public class DBTraceInstruction extends AbstractDBTraceCodeUnit<DBTraceInstruction> implements
|
||||
TraceInstruction, InstructionAdapterFromPrototype, InstructionContext {
|
||||
public class DBTraceInstruction extends AbstractDBTraceCodeUnit<DBTraceInstruction>
|
||||
implements TraceInstruction, InstructionAdapterFromPrototype, InstructionContext {
|
||||
private static final Address[] EMPTY_ADDRESS_ARRAY = new Address[] {};
|
||||
private static final String TABLE_NAME = "Instructions";
|
||||
|
||||
private static final byte FALLTHROUGH_SET_MASK = 0x01;
|
||||
private static final byte FALLTHROUGH_CLEAR_MASK = ~FALLTHROUGH_SET_MASK;
|
||||
|
||||
private static final byte FLOWOVERRIDE_SET_MASK = 0x0e;
|
||||
private static final byte FLOWOVERRIDE_CLEAR_MASK = ~FLOWOVERRIDE_SET_MASK;
|
||||
private static final int FLOWOVERRIDE_SHIFT = 1;
|
||||
private static final byte FLOW_OVERRIDE_SET_MASK = 0x0e;
|
||||
private static final byte FLOW_OVERRIDE_CLEAR_MASK = ~FLOW_OVERRIDE_SET_MASK;
|
||||
private static final int FLOW_OVERRIDE_SHIFT = 1;
|
||||
|
||||
private static final byte LENGTH_OVERRIDE_SET_MASK = 0x70;
|
||||
private static final byte LENGTH_OVERRIDE_CLEAR_MASK = ~LENGTH_OVERRIDE_SET_MASK;
|
||||
private static final int LENGTH_OVERRIDE_SHIFT = 4;
|
||||
|
||||
static final String PLATFORM_COLUMN_NAME = "Platform";
|
||||
static final String PROTOTYPE_COLUMN_NAME = "Prototype";
|
||||
|
@ -143,6 +149,7 @@ public class DBTraceInstruction extends AbstractDBTraceCodeUnit<DBTraceInstructi
|
|||
|
||||
protected InstructionPrototype prototype;
|
||||
protected FlowOverride flowOverride;
|
||||
protected int lengthOverride;
|
||||
|
||||
protected ParserContext parserContext;
|
||||
protected InternalTracePlatform platform;
|
||||
|
@ -186,18 +193,24 @@ public class DBTraceInstruction extends AbstractDBTraceCodeUnit<DBTraceInstructi
|
|||
* @param platform the platform
|
||||
* @param prototype the instruction prototype
|
||||
* @param context the context for locating or creating the prototype entry
|
||||
* @param forcedLengthOverride reduced instruction byte-length (1..7) or 0 to use default length
|
||||
*/
|
||||
protected void set(InternalTracePlatform platform, InstructionPrototype prototype,
|
||||
ProcessorContextView context) {
|
||||
ProcessorContextView context, int forcedLengthOverride) {
|
||||
this.platformKey = platform.getIntKey();
|
||||
// NOTE: Using "this" for the MemBuffer seems a bit precarious.
|
||||
DBTraceGuestLanguage languageEntry = platform == null ? null : platform.getLanguageEntry();
|
||||
this.prototypeKey = (int) space.manager
|
||||
.findOrRecordPrototype(prototype, languageEntry, this, context)
|
||||
.getKey();
|
||||
DBTraceGuestLanguage languageEntry = platform.getLanguageEntry();
|
||||
this.prototypeKey =
|
||||
(int) space.manager.findOrRecordPrototype(prototype, languageEntry, this, context)
|
||||
.getKey();
|
||||
this.flowOverride = FlowOverride.NONE; // flags field is already consistent
|
||||
this.lengthOverride = 0;
|
||||
update(PLATFORM_COLUMN, PROTOTYPE_COLUMN, FLAGS_COLUMN);
|
||||
|
||||
if (forcedLengthOverride != 0) {
|
||||
updateLengthOverride(forcedLengthOverride);
|
||||
}
|
||||
|
||||
// TODO: Can there be more in this context than the context register???
|
||||
doSetPlatformMapping(platform);
|
||||
this.prototype = prototype;
|
||||
|
@ -216,12 +229,19 @@ public class DBTraceInstruction extends AbstractDBTraceCodeUnit<DBTraceInstructi
|
|||
}
|
||||
prototype = space.manager.getPrototypeByKey(prototypeKey);
|
||||
if (prototype == null) {
|
||||
Msg.error(this,
|
||||
"Instruction table is corrupt for address " + getMinAddress() +
|
||||
". Missing prototype " + prototypeKey);
|
||||
Msg.error(this, "Instruction table is corrupt for address " + getMinAddress() +
|
||||
". Missing prototype " + prototypeKey);
|
||||
prototype = new InvalidPrototype(getTrace().getBaseLanguage());
|
||||
}
|
||||
flowOverride = FlowOverride.values()[(flags & FLOWOVERRIDE_SET_MASK) >> FLOWOVERRIDE_SHIFT];
|
||||
flowOverride =
|
||||
FlowOverride.values()[(flags & FLOW_OVERRIDE_SET_MASK) >> FLOW_OVERRIDE_SHIFT];
|
||||
|
||||
lengthOverride = (flags & LENGTH_OVERRIDE_SET_MASK) >> LENGTH_OVERRIDE_SHIFT;
|
||||
if (lengthOverride != 0 && lengthOverride < prototype.getLength()) {
|
||||
Address minAddr = getMinAddress();
|
||||
Address newEndAddr = minAddr.add(lengthOverride - 1);
|
||||
doSetRange(new AddressRangeImpl(minAddr, newEndAddr));
|
||||
}
|
||||
|
||||
doSetPlatformMapping(platform);
|
||||
}
|
||||
|
@ -334,8 +354,7 @@ public class DBTraceInstruction extends AbstractDBTraceCodeUnit<DBTraceInstructi
|
|||
if (flowType.hasFallthrough()) {
|
||||
try {
|
||||
return instructionContext.getAddress()
|
||||
.addNoWrap(
|
||||
prototype.getFallThroughOffset(instructionContext));
|
||||
.addNoWrap(prototype.getFallThroughOffset(instructionContext));
|
||||
}
|
||||
catch (AddressOverflowException e) {
|
||||
return null;
|
||||
|
@ -364,6 +383,9 @@ public class DBTraceInstruction extends AbstractDBTraceCodeUnit<DBTraceInstructi
|
|||
}
|
||||
return null;
|
||||
}
|
||||
if (lengthOverride != 0 && getFlowType().hasFallthrough()) {
|
||||
return getMinAddress().add(lengthOverride);
|
||||
}
|
||||
return getDefaultFallThrough();
|
||||
}
|
||||
}
|
||||
|
@ -433,6 +455,10 @@ public class DBTraceInstruction extends AbstractDBTraceCodeUnit<DBTraceInstructi
|
|||
return EMPTY_ADDRESS_ARRAY;
|
||||
}
|
||||
|
||||
if (lengthOverride != 0 && getFlowType().hasFallthrough()) {
|
||||
list.add(getMinAddress().add(lengthOverride));
|
||||
}
|
||||
|
||||
return list.toArray(new Address[list.size()]);
|
||||
}
|
||||
}
|
||||
|
@ -526,8 +552,8 @@ public class DBTraceInstruction extends AbstractDBTraceCodeUnit<DBTraceInstructi
|
|||
}
|
||||
FlowType origFlowType = getFlowType();
|
||||
|
||||
flags &= FLOWOVERRIDE_CLEAR_MASK;
|
||||
flags |= (flowOverride.ordinal() << FLOWOVERRIDE_SHIFT) & FLOWOVERRIDE_SET_MASK;
|
||||
flags &= FLOW_OVERRIDE_CLEAR_MASK;
|
||||
flags |= (flowOverride.ordinal() << FLOW_OVERRIDE_SHIFT) & FLOW_OVERRIDE_SET_MASK;
|
||||
this.flowOverride = flowOverride;
|
||||
update(FLAGS_COLUMN);
|
||||
|
||||
|
@ -545,8 +571,91 @@ public class DBTraceInstruction extends AbstractDBTraceCodeUnit<DBTraceInstructi
|
|||
}
|
||||
}
|
||||
space.trace.setChanged(
|
||||
new TraceChangeRecord<>(TraceInstructionChangeType.FLOW_OVERRIDE_CHANGED,
|
||||
space, this, oldFlowOverride, flowOverride));
|
||||
new TraceChangeRecord<>(TraceInstructionChangeType.FLOW_OVERRIDE_CHANGED, space, this,
|
||||
oldFlowOverride, flowOverride));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLengthOverride(int length) throws CodeUnitInsertionException {
|
||||
int oldLengthOverride = this.lengthOverride;
|
||||
try (LockHold hold = space.trace.lockWrite()) {
|
||||
checkDeleted();
|
||||
InstructionPrototype proto = getPrototype();
|
||||
length = InstructionDB.checkLengthOverride(length, proto);
|
||||
if (length == lengthOverride) {
|
||||
return; // no change
|
||||
}
|
||||
|
||||
int newLength = length != 0 ? length : proto.getLength();
|
||||
if (newLength > getLength()) {
|
||||
Address minAddr = getMinAddress();
|
||||
Address newEndAddr = minAddr.add(newLength - 1);
|
||||
TraceAddressSnapRange tasr = new ImmutableTraceAddressSnapRange(
|
||||
new AddressRangeImpl(minAddr.next(), newEndAddr), getLifespan());
|
||||
for (AbstractDBTraceCodeUnit<?> cu : space.definedUnits.getIntersecting(tasr)) {
|
||||
if (cu != this) {
|
||||
throw new CodeUnitInsertionException(
|
||||
"Length override of " + newLength + " conflicts with code unit at " +
|
||||
cu.getMinAddress() + ", lifespan=" + cu.getLifespan());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updateLengthOverride(length);
|
||||
}
|
||||
space.trace.setChanged(
|
||||
new TraceChangeRecord<>(TraceInstructionChangeType.LENGTH_OVERRIDE_CHANGED, space, this,
|
||||
oldLengthOverride, length));
|
||||
}
|
||||
|
||||
private void updateLengthOverride(int length) {
|
||||
flags &= LENGTH_OVERRIDE_CLEAR_MASK;
|
||||
flags |= (length << LENGTH_OVERRIDE_SHIFT);
|
||||
lengthOverride = length;
|
||||
update(FLAGS_COLUMN);
|
||||
|
||||
int newLength = length != 0 ? length : getPrototype().getLength();
|
||||
Address minAddr = getMinAddress();
|
||||
Address newEndAddr = minAddr.add(newLength - 1);
|
||||
doSetRange(new AddressRangeImpl(minAddr, newEndAddr));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLengthOverridden() {
|
||||
return lengthOverride != 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLength() {
|
||||
if (lengthOverride != 0) {
|
||||
return lengthOverride;
|
||||
}
|
||||
return super.getLength();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getParsedLength() {
|
||||
if (lengthOverride == 0) {
|
||||
return super.getLength();
|
||||
}
|
||||
return getPrototype().getLength();
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getParsedBytes() throws MemoryAccessException {
|
||||
if (!isLengthOverridden()) {
|
||||
return getBytes();
|
||||
}
|
||||
try (LockHold hold = LockHold.lock(space.lock.readLock())) {
|
||||
checkIsValid();
|
||||
int len = getPrototype().getLength();
|
||||
byte[] b = new byte[len];
|
||||
Address addr = getAddress();
|
||||
if (len != getMemory().getBytes(addr, b)) {
|
||||
throw new MemoryAccessException("Failed to read " + len + " bytes at " + addr);
|
||||
}
|
||||
return b;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -622,8 +731,8 @@ public class DBTraceInstruction extends AbstractDBTraceCodeUnit<DBTraceInstructi
|
|||
}
|
||||
update(FLAGS_COLUMN);
|
||||
space.trace.setChanged(
|
||||
new TraceChangeRecord<>(TraceInstructionChangeType.FALL_THROUGH_OVERRIDE_CHANGED,
|
||||
space, this, !overridden, overridden));
|
||||
new TraceChangeRecord<>(TraceInstructionChangeType.FALL_THROUGH_OVERRIDE_CHANGED, space,
|
||||
this, !overridden, overridden));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -59,9 +59,10 @@ public class DBTraceInstructionsMemoryView
|
|||
@Override
|
||||
public DBTraceInstruction create(Lifespan lifespan, Address address,
|
||||
TracePlatform platform, InstructionPrototype prototype,
|
||||
ProcessorContextView context) throws CodeUnitInsertionException {
|
||||
ProcessorContextView context, int forcedLengthOverride)
|
||||
throws CodeUnitInsertionException {
|
||||
return delegateWrite(address.getAddressSpace(),
|
||||
m -> m.create(lifespan, address, platform, prototype, context));
|
||||
m -> m.create(lifespan, address, platform, prototype, context, forcedLengthOverride));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -19,6 +19,7 @@ import java.util.*;
|
|||
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import ghidra.program.database.code.InstructionDB;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.lang.*;
|
||||
import ghidra.program.model.lang.InstructionError.InstructionErrorType;
|
||||
|
@ -103,16 +104,22 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView
|
|||
protected boolean isSuitable(Instruction candidate, Instruction protoInstr) {
|
||||
try {
|
||||
return candidate.getPrototype().equals(protoInstr.getPrototype()) &&
|
||||
Arrays.equals(candidate.getBytes(), protoInstr.getBytes()) &&
|
||||
candidate.isFallThroughOverridden() == protoInstr.isFallThroughOverridden() &&
|
||||
Objects.equals(candidate.getFallThrough(), protoInstr.getFallThrough()) &&
|
||||
candidate.getFlowOverride() == protoInstr.getFlowOverride();
|
||||
candidate.getFlowOverride() == protoInstr.getFlowOverride() &&
|
||||
candidate.getLength() == protoInstr.getLength() && // handles potential length override
|
||||
hasSameBytes(candidate, protoInstr);
|
||||
}
|
||||
catch (MemoryAccessException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean hasSameBytes(Instruction instr1, Instruction instr2)
|
||||
throws MemoryAccessException {
|
||||
return Arrays.equals(instr1.getParsedBytes(), instr2.getParsedBytes());
|
||||
}
|
||||
|
||||
protected Instruction doAdjustExisting(Address address, Instruction protoInstr)
|
||||
throws AddressOverflowException, CancelledException, CodeUnitInsertionException {
|
||||
DBTraceInstruction exists = getAt(lifespan.lmin(), address);
|
||||
|
@ -193,8 +200,8 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView
|
|||
return prec;
|
||||
}
|
||||
|
||||
Instruction created =
|
||||
doCreate(lifespan, address, platform, protoInstr.getPrototype(), protoInstr);
|
||||
Instruction created = doCreate(lifespan, address, platform,
|
||||
protoInstr.getPrototype(), protoInstr, protoInstr.getLength());
|
||||
// copy override settings to replacement instruction
|
||||
if (protoInstr.isFallThroughOverridden()) {
|
||||
created.setFallThrough(protoInstr.getFallThrough());
|
||||
|
@ -336,20 +343,30 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView
|
|||
* @param platform the platform (language, compiler) for the instruction
|
||||
* @param prototype the instruction's prototype
|
||||
* @param context the initial context for parsing the instruction
|
||||
* @return the new instructions
|
||||
* @throws CodeUnitInsertionException if the instruction cannot be created due to an existing
|
||||
* unit
|
||||
* @param length instruction byte-length (must be in the range 0..prototype.getLength()).
|
||||
* If smaller than the prototype length it must have a value no greater than 7, otherwise
|
||||
* an error will be thrown. A value of 0 or greater-than-or-equal the prototype length
|
||||
* will be ignored and not impose and override length. The length value must be a multiple
|
||||
* of the {@link Language#getInstructionAlignment() instruction alignment} .
|
||||
* @return the newly created instruction.
|
||||
* @throws CodeUnitInsertionException thrown if the new Instruction would overlap and
|
||||
* existing {@link CodeUnit} or the specified {@code length} is unsupported.
|
||||
* @throws IllegalArgumentException if a negative {@code length} is specified.
|
||||
* @throws AddressOverflowException if the instruction would fall off the address space
|
||||
*/
|
||||
protected DBTraceInstruction doCreate(Lifespan lifespan, Address address,
|
||||
InternalTracePlatform platform, InstructionPrototype prototype,
|
||||
ProcessorContextView context)
|
||||
ProcessorContextView context, int length)
|
||||
throws CodeUnitInsertionException, AddressOverflowException {
|
||||
if (platform.getLanguage() != prototype.getLanguage()) {
|
||||
throw new IllegalArgumentException("Platform and prototype disagree in language");
|
||||
}
|
||||
|
||||
Address endAddress = address.addNoWrap(prototype.getLength() - 1);
|
||||
int forcedLengthOverride = InstructionDB.checkLengthOverride(length, prototype);
|
||||
if (length == 0) {
|
||||
length = prototype.getLength();
|
||||
}
|
||||
Address endAddress = address.addNoWrap(length - 1);
|
||||
AddressRangeImpl createdRange = new AddressRangeImpl(address, endAddress);
|
||||
|
||||
// Truncate, then check that against existing code units.
|
||||
|
@ -365,7 +382,7 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView
|
|||
doSetContext(tasr, prototype.getLanguage(), context);
|
||||
|
||||
DBTraceInstruction created = space.instructionMapSpace.put(tasr, null);
|
||||
created.set(platform, prototype, context);
|
||||
created.set(platform, prototype, context, forcedLengthOverride);
|
||||
|
||||
cacheForContaining.notifyNewEntry(tasr.getLifespan(), createdRange, created);
|
||||
cacheForSequence.notifyNewEntry(tasr.getLifespan(), createdRange, created);
|
||||
|
@ -379,14 +396,14 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView
|
|||
|
||||
@Override
|
||||
public DBTraceInstruction create(Lifespan lifespan, Address address, TracePlatform platform,
|
||||
InstructionPrototype prototype, ProcessorContextView context)
|
||||
InstructionPrototype prototype, ProcessorContextView context, int forcedLengthOverride)
|
||||
throws CodeUnitInsertionException {
|
||||
InternalTracePlatform dbPlatform = space.manager.platformManager.assertMine(platform);
|
||||
try (LockHold hold = LockHold.lock(space.lock.writeLock())) {
|
||||
DBTraceInstruction created =
|
||||
doCreate(lifespan, address, dbPlatform, prototype, context);
|
||||
space.trace.setChanged(new TraceChangeRecord<>(TraceCodeChangeType.ADDED,
|
||||
space, created, created));
|
||||
doCreate(lifespan, address, dbPlatform, prototype, context, forcedLengthOverride);
|
||||
space.trace.setChanged(
|
||||
new TraceChangeRecord<>(TraceCodeChangeType.ADDED, space, created, created));
|
||||
return created;
|
||||
}
|
||||
catch (AddressOverflowException e) {
|
||||
|
@ -567,9 +584,9 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView
|
|||
if (lastInstruction != null) {
|
||||
Address maxAddress = DBTraceCodeManager.instructionMax(lastInstruction, true);
|
||||
result.addRange(block.getStartAddress(), maxAddress);
|
||||
space.trace.setChanged(new TraceChangeRecord<>(TraceCodeChangeType.ADDED,
|
||||
space, new ImmutableTraceAddressSnapRange(
|
||||
block.getStartAddress(), maxAddress, lifespan)));
|
||||
space.trace.setChanged(new TraceChangeRecord<>(TraceCodeChangeType.ADDED, space,
|
||||
new ImmutableTraceAddressSnapRange(block.getStartAddress(), maxAddress,
|
||||
lifespan)));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
|
|
|
@ -708,18 +708,19 @@ public abstract class AbstractDBTraceProgramViewListing implements TraceProgramV
|
|||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("rawtypes")
|
||||
public PropertyMap getPropertyMap(String propertyName) {
|
||||
public PropertyMap<?> getPropertyMap(String propertyName) {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Instruction createInstruction(Address addr, InstructionPrototype prototype,
|
||||
MemBuffer memBuf, ProcessorContextView context) throws CodeUnitInsertionException {
|
||||
MemBuffer memBuf, ProcessorContextView context, int forcedLengthOverride)
|
||||
throws CodeUnitInsertionException {
|
||||
// TODO: Why memBuf? Can it vary from program memory?
|
||||
return codeOperations.instructions()
|
||||
.create(Lifespan.nowOn(program.snap), addr, platform, prototype, context);
|
||||
.create(Lifespan.nowOn(program.snap), addr, platform, prototype, context,
|
||||
forcedLengthOverride);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -129,6 +129,8 @@ public class DBTraceProgramView implements TraceProgramView {
|
|||
listenFor(TraceDataTypeChangeType.RENAMED, this::dataTypeRenamed);
|
||||
listenFor(TraceDataTypeChangeType.DELETED, this::dataTypeDeleted);
|
||||
|
||||
listenFor(TraceInstructionChangeType.LENGTH_OVERRIDE_CHANGED,
|
||||
this::instructionLengthOverrideChanged);
|
||||
listenFor(TraceInstructionChangeType.FLOW_OVERRIDE_CHANGED,
|
||||
this::instructionFlowOverrideChanged);
|
||||
listenFor(TraceInstructionChangeType.FALL_THROUGH_OVERRIDE_CHANGED,
|
||||
|
@ -466,9 +468,19 @@ public class DBTraceProgramView implements TraceProgramView {
|
|||
return;
|
||||
}
|
||||
queues.fireEvent(new ProgramChangeRecord(ChangeManager.DOCR_FALLTHROUGH_CHANGED,
|
||||
instruction.getMinAddress(), instruction.getMaxAddress(), null, null, null));
|
||||
instruction.getMinAddress(), instruction.getMinAddress(), null, null, null));
|
||||
}
|
||||
|
||||
private void instructionLengthOverrideChanged(TraceAddressSpace space,
|
||||
TraceInstruction instruction, int oldLengthOverride, int newLengthOverride) {
|
||||
DomainObjectEventQueues queues = isCodeVisible(space, instruction);
|
||||
if (queues == null) {
|
||||
return;
|
||||
}
|
||||
queues.fireEvent(new ProgramChangeRecord(ChangeManager.DOCR_LENGTH_OVERRIDE_CHANGED,
|
||||
instruction.getMinAddress(), instruction.getMinAddress(), null, null, null));
|
||||
}
|
||||
|
||||
private void memoryBytesChanged(TraceAddressSpace space, TraceAddressSnapRange range,
|
||||
byte[] oldIsNull, byte[] bytes) {
|
||||
DomainObjectEventQueues queues = isBytesVisible(space, range);
|
||||
|
|
|
@ -235,6 +235,8 @@ public interface Trace extends DataTypeManagerDomainObject {
|
|||
new TraceInstructionChangeType<>();
|
||||
public static final TraceInstructionChangeType<Boolean> FALL_THROUGH_OVERRIDE_CHANGED =
|
||||
new TraceInstructionChangeType<>();
|
||||
public static final TraceInstructionChangeType<Integer> LENGTH_OVERRIDE_CHANGED =
|
||||
new TraceInstructionChangeType<>();
|
||||
}
|
||||
|
||||
public static final class TraceMemoryBytesChangeType
|
||||
|
|
|
@ -37,11 +37,12 @@ public interface TraceInstructionsView extends TraceBaseDefinedUnitsView<TraceIn
|
|||
* @param platform the platform
|
||||
* @param prototype the instruction prototype
|
||||
* @param context the input disassembly context for the instruction
|
||||
* @param forcedLengthOverride reduced instruction byte-length (1..7) or 0 to use default length
|
||||
* @return the new instruction
|
||||
* @throws CodeUnitInsertionException if the instruction cannot be created
|
||||
*/
|
||||
TraceInstruction create(Lifespan lifespan, Address address, TracePlatform platform,
|
||||
InstructionPrototype prototype, ProcessorContextView context)
|
||||
InstructionPrototype prototype, ProcessorContextView context, int forcedLengthOverride)
|
||||
throws CodeUnitInsertionException;
|
||||
|
||||
/**
|
||||
|
@ -50,10 +51,10 @@ public interface TraceInstructionsView extends TraceBaseDefinedUnitsView<TraceIn
|
|||
* @see #create(Lifespan, Address, TracePlatform, InstructionPrototype, ProcessorContextView)
|
||||
*/
|
||||
default TraceInstruction create(Lifespan lifespan, Address address,
|
||||
InstructionPrototype prototype, ProcessorContextView context)
|
||||
InstructionPrototype prototype, ProcessorContextView context, int forcedLengthOverride)
|
||||
throws CodeUnitInsertionException {
|
||||
return create(lifespan, address, getTrace().getPlatformManager().getHostPlatform(),
|
||||
prototype, context);
|
||||
prototype, context, forcedLengthOverride);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -564,7 +564,8 @@ public class ToyDBTraceBuilder implements AutoCloseable {
|
|||
InstructionBlock block = dis.pseudoDisassembleBlock(memBuf, defaultContextValue, 1);
|
||||
Instruction pseudoIns = block.iterator().next();
|
||||
return code.instructions()
|
||||
.create(Lifespan.nowOn(snap), start, platform, pseudoIns.getPrototype(), pseudoIns);
|
||||
.create(Lifespan.nowOn(snap), start, platform, pseudoIns.getPrototype(), pseudoIns,
|
||||
0);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -468,9 +468,6 @@ public class DBTraceCodeUnitTest extends AbstractGhidraHeadlessIntegrationTest
|
|||
d4008 = b.addData(0, b.addr(0x4008), LongDataType.dataType, b.buf(1, 2, 3, 4));
|
||||
}
|
||||
|
||||
assertTrue(i4004.isSuccessor(i4006));
|
||||
assertFalse(i4004.isSuccessor(d4008));
|
||||
|
||||
assertFalse(i4004.contains(b.addr(0x4003)));
|
||||
assertTrue(i4004.contains(b.addr(0x4004)));
|
||||
assertTrue(i4004.contains(b.addr(0x4005)));
|
||||
|
|
|
@ -220,6 +220,8 @@ src/main/help/help/topics/DbViewerPlugin/images/DatabaseViewer.png||GHIDRA||||EN
|
|||
src/main/help/help/topics/DisassembledViewPlugin/DisassembledViewPlugin.htm||GHIDRA||||END|
|
||||
src/main/help/help/topics/DisassembledViewPlugin/images/DisassembledViewPluginMain.png||GHIDRA||||END|
|
||||
src/main/help/help/topics/DisassemblerPlugin/Disassembly.htm||GHIDRA||||END|
|
||||
src/main/help/help/topics/DisassemblerPlugin/images/LengthOverrideLockPrefixExample.png||GHIDRA||||END|
|
||||
src/main/help/help/topics/DisassemblerPlugin/images/LengthOverrideLockPrefixExample2.png||GHIDRA||||END|
|
||||
src/main/help/help/topics/DisassemblerPlugin/images/ProcessorOptions.png||GHIDRA||||END|
|
||||
src/main/help/help/topics/DockingWindows/Docking_Windows.htm||GHIDRA||||END|
|
||||
src/main/help/help/topics/DockingWindows/images/Tool.png||GHIDRA||||END|
|
||||
|
|
|
@ -24,7 +24,15 @@
|
|||
<LI><A href="#Disassemble_Static">Static Disassembly</A></LI>
|
||||
|
||||
<LI><A href="#Disassemble_Restricted">Restricted Disassembly</A></LI>
|
||||
|
||||
<LI><A href="#Disassemble_Arm">Disassemble ARM / Thumb</A></LI>
|
||||
|
||||
<LI><A href="#ProcessorOptions">Processor Options</A></LI>
|
||||
|
||||
<LI><A href="#Modify_Instruction_Flow">Modify Instruction Flow</A></LI>
|
||||
|
||||
<LI><A href="#Modify_Instruction_Length">Modify Instruction Length</A></LI>
|
||||
|
||||
</UL>
|
||||
|
||||
<H2><A name="Disassemble"></A>Disassembly</H2>
|
||||
|
@ -131,7 +139,7 @@
|
|||
undefined bytes.</I></P>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<H2><A name="Disassemble_Restricted"></A>Disassembly (Restricted)</H2>
|
||||
<H2><A name="Disassemble_Restricted"></A>Restricted Disassembly</H2>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<P><I>Restricted Disassembly</I> is similar to <I>Disassembly</I>, except that only bytes in
|
||||
|
@ -157,13 +165,11 @@
|
|||
undefined bytes.</I></P>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<P class="providedbyplugin"> </P>
|
||||
|
||||
<H2><A name="Disassemble_Arm"></A>Disassemble ARM / Disassemble Thumb<A name=
|
||||
<H2><A name="Disassemble_Arm"></A>Disassemble ARM / Thumb<A name=
|
||||
"Disassemble_Thumb"></A></H2>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<P><I>Disassemble ARM and Disassemble Thumb</I> will only be available if the program you are
|
||||
<P><I>Disassemble ARM and Disassemble Thumb actions</I> will only be available if the program you are
|
||||
working with is an ARM based processor. ARM processors have two states, ARM and Thumb
|
||||
mode. The instructions available in ARM mode are 4 bytes long. In Thumb mode, the
|
||||
instructions are "generally" 2 bytes long. ARM and Thumb mode are mutually exclusive,
|
||||
|
@ -199,38 +205,6 @@
|
|||
undefined bytes.</I></P>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<H2><A name="Modify_Instruction_Flow"></A>Modify Instruction Flow</H2>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<P>With certain processors and situations it may be desirable to modify the default
|
||||
flow of an instruction to better reflect the nature of its flow. For example a jump may
|
||||
actually be performing a call type operation, a call may be performing a long-jump.
|
||||
This distinction primarily affects the subroutine models and flow analysis performed
|
||||
within Ghidra.</P>
|
||||
<P>The following basic flow types may be imposed upon the default flow of an instruction:</P>
|
||||
<UL>
|
||||
<LI>BRANCH</LI>
|
||||
<LI>CALL</LI>
|
||||
<LI>CALL_RETURN</LI>
|
||||
<LI>RETURN</LI>
|
||||
</UL>
|
||||
<P>In all situations the conditional nature of the original flow is perserved.</P>
|
||||
<P>To Modify Instruction Flow:</P>
|
||||
<OL>
|
||||
<LI>Place the cursor on an instruction within the Code Browser. Note that
|
||||
instructions which are purely fall-through can not be modified.</LI>
|
||||
<LI>Right-mouse-click</LI>
|
||||
<LI>Select <I>Modify Instruction Flow...</I> menu item.
|
||||
<LI>Within the <I>Modify Instruction Flow</I> dialog select the desired basic flow
|
||||
behavior.</LI>
|
||||
<LI>Click OK in the dialog</LI>
|
||||
</OL>
|
||||
<P align="left"><I><IMG src="help/shared/tip.png">An instruction whose flow has been
|
||||
modified will have its' mnemonic color modified.</I></P>
|
||||
|
||||
</BLOCKQUOTE>
|
||||
|
||||
|
||||
<H2><A name="ProcessorOptions"></A>Processor Options</H2>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
|
@ -244,11 +218,6 @@
|
|||
<DIV align="center">
|
||||
<IMG src="images/ProcessorOptions.png"><BR>
|
||||
<BR>
|
||||
|
||||
|
||||
<DIV align="left">
|
||||
<BR>
|
||||
</DIV>
|
||||
</DIV>
|
||||
|
||||
<P>The options are
|
||||
|
@ -282,8 +251,90 @@
|
|||
|
||||
<LI><A href="help/topics/LanguageProviderPlugin/Languages.htm">Languages</A></LI>
|
||||
</UL>
|
||||
|
||||
<P> </P>
|
||||
<BR>
|
||||
|
||||
<H2><A name="Modify_Instruction_Flow"></A>Modify Instruction Flow</H2>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<P>With certain processors and situations it may be desirable to modify the default
|
||||
flow of an instruction to better reflect the nature of its flow. For example a jump may
|
||||
actually be performing a call type operation, a call may be performing a long-jump.
|
||||
This distinction primarily affects the subroutine models and flow analysis performed
|
||||
within Ghidra.</P>
|
||||
<P>The following basic flow types may be imposed upon the default flow of an instruction:</P>
|
||||
<UL>
|
||||
<LI>BRANCH</LI>
|
||||
<LI>CALL</LI>
|
||||
<LI>CALL_RETURN</LI>
|
||||
<LI>RETURN</LI>
|
||||
</UL>
|
||||
<P>In all situations the conditional nature of the original flow is perserved.</P>
|
||||
<P>To Modify Instruction Flow:</P>
|
||||
<OL>
|
||||
<LI>Place the cursor on an instruction within the Code Browser. Note that
|
||||
instructions which are purely fall-through can not be modified.</LI>
|
||||
<LI>Right-mouse-click</LI>
|
||||
<LI>Select <I>Modify Instruction Flow...</I> menu item.
|
||||
<LI>Within the <I>Modify Instruction Flow</I> dialog select the desired basic flow
|
||||
behavior.</LI>
|
||||
<LI>Click OK in the dialog</LI>
|
||||
</OL>
|
||||
<P align="left"><I><IMG src="help/shared/tip.png">An instruction whose flow has been
|
||||
modified will have its' mnemonic color modified.</I></P>
|
||||
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<H2><A name="Modify_Instruction_Length"></A>Modify Instruction Length</H2>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<P>There are certain situations where code may flow into an offcut location within the
|
||||
middle of another instruction where the bytes happen to form a different instruction.
|
||||
While this generally indicates a bad flow, this can also be a legitimate situation
|
||||
of an overlapping instruction. For example, with x86 instructions where a flow
|
||||
may bypass a LOCK prefix byte. Depending on which flow disassembles first, the situation
|
||||
may manifest differently. Below is an example of the x86 LOCK prefix case. This situation can be
|
||||
quickly identified by the error bookmark along with the offcut reference. In this case the
|
||||
<code>JZ</code> instruction has two flows: 1) one falls-through and 2) conditionally jumps
|
||||
around the LOCK prefix byte resulting in an offcut flow and disassembly conflict.</P>
|
||||
<BR>
|
||||
|
||||
<DIV align="center">
|
||||
<IMG src="images/LengthOverrideLockPrefixExample.png"><BR>
|
||||
<BR>
|
||||
</DIV>
|
||||
|
||||
<P>The above case can be resolved by overriding the code-unit length of the first
|
||||
instruction to 1-byte allowing the incomplete disassembly of the offcut instruction
|
||||
to be repaired.</P>
|
||||
|
||||
<OL>
|
||||
<LI>Place the cursor on an instruction within the Code Browser (e.g.,
|
||||
<code>CMPXCHG</code> instruction).</LI>
|
||||
<LI>Right-mouse-click</LI>
|
||||
<LI>Select <I>Modify Instruction Length...</I> menu item.
|
||||
<LI>Within the <I>Modify Instruction Length</I> dialog enter the reduced instruction
|
||||
length (e.g., 1 in this case). Note that this does not impact the number of bytes
|
||||
actually parsed, but only the affective code unit length.</LI>
|
||||
<LI>Click OK in the dialog</LI>
|
||||
<LI>This should result in the subsequent locations becoming undefined code units
|
||||
which can know be disassembled. Click on the first undefined location and disassemble
|
||||
(i.e., <B>D</B> key binding).</LI>
|
||||
<LI>You may also delete the error bookmark which should no longer be relavent.
|
||||
</OL>
|
||||
|
||||
<P>The image below shows this same code after these length override steps have been
|
||||
performed. The fallthrough of the first instruction, whose length was overriden from four
|
||||
to one, is preserved and both instructions fallthrough to the same <code>JNZ</code>
|
||||
instruction.
|
||||
</P>
|
||||
<BR>
|
||||
|
||||
<DIV align="center">
|
||||
<IMG src="images/LengthOverrideLockPrefixExample2.png"><BR>
|
||||
<BR>
|
||||
</DIV>
|
||||
|
||||
</BLOCKQUOTE>
|
||||
|
||||
</BODY>
|
||||
</HTML>
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 13 KiB |
Binary file not shown.
After Width: | Height: | Size: 23 KiB |
|
@ -68,28 +68,31 @@ public abstract class CodeUnitDetails {
|
|||
boolean removedFallThrough =
|
||||
inst.isFallThroughOverridden() && (inst.getFallThrough() == null);
|
||||
boolean hasFlowOverride = inst.getFlowOverride() != FlowOverride.NONE;
|
||||
boolean hasLengthOverride = inst.isLengthOverridden();
|
||||
cuRep = cu.toString();
|
||||
if (removedFallThrough) {
|
||||
cuRep +=
|
||||
NEW_LINE + indent + getSpaces(addrRangeStr.length()) + " " +
|
||||
"Removed FallThrough";
|
||||
cuRep += NEW_LINE + indent + getSpaces(addrRangeStr.length()) + " " +
|
||||
"Removed FallThrough";
|
||||
}
|
||||
else if (inst.isFallThroughOverridden()) {
|
||||
Reference[] refs = cu.getReferencesFrom();
|
||||
// Show the fallthrough override.
|
||||
for (int i = 0; i < refs.length; i++) {
|
||||
if (refs[i].getReferenceType().isFallthrough()) {
|
||||
cuRep +=
|
||||
NEW_LINE + indent + getSpaces(addrRangeStr.length()) + " " +
|
||||
"FallThrough Override: " +
|
||||
DiffUtility.getUserToAddressString(inst.getProgram(), refs[i]);
|
||||
for (Reference ref : refs) {
|
||||
if (ref.getReferenceType().isFallthrough()) {
|
||||
cuRep += NEW_LINE + indent + getSpaces(addrRangeStr.length()) + " " +
|
||||
"FallThrough Override: " +
|
||||
DiffUtility.getUserToAddressString(inst.getProgram(), ref);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (hasFlowOverride) {
|
||||
cuRep +=
|
||||
NEW_LINE + indent + getSpaces(addrRangeStr.length()) + " " +
|
||||
"Flow Override: " + inst.getFlowOverride();
|
||||
cuRep += NEW_LINE + indent + getSpaces(addrRangeStr.length()) + " " +
|
||||
"Flow Override: " + inst.getFlowOverride();
|
||||
}
|
||||
if (hasLengthOverride) {
|
||||
cuRep += NEW_LINE + indent + getSpaces(addrRangeStr.length()) + " " +
|
||||
"Length Override: " + inst.getLength() + " (actual length is " +
|
||||
inst.getParsedLength() + ")";
|
||||
}
|
||||
// Commented the following out, since we may want the hash code in the future.
|
||||
// cuRep +=
|
||||
|
@ -146,15 +149,15 @@ public abstract class CodeUnitDetails {
|
|||
return indent + "None";
|
||||
}
|
||||
StringBuffer buf = new StringBuffer();
|
||||
for (int i = 0; i < refs.length; i++) {
|
||||
if (refs[i].isExternalReference()) {
|
||||
buf.append(indent + "External Reference " + getRefInfo(pgm, refs[i]) + NEW_LINE);
|
||||
for (Reference ref : refs) {
|
||||
if (ref.isExternalReference()) {
|
||||
buf.append(indent + "External Reference " + getRefInfo(pgm, ref) + NEW_LINE);
|
||||
}
|
||||
else if (refs[i].isStackReference()) {
|
||||
buf.append(indent + "Stack Reference " + getRefInfo(pgm, refs[i]) + NEW_LINE);
|
||||
else if (ref.isStackReference()) {
|
||||
buf.append(indent + "Stack Reference " + getRefInfo(pgm, ref) + NEW_LINE);
|
||||
}
|
||||
else {
|
||||
buf.append(indent + "Reference " + getRefInfo(pgm, refs[i]) + NEW_LINE);
|
||||
buf.append(indent + "Reference " + getRefInfo(pgm, ref) + NEW_LINE);
|
||||
}
|
||||
}
|
||||
return buf.toString();
|
||||
|
|
|
@ -858,7 +858,13 @@ class CodeUnitMerger extends AbstractListingMerger {
|
|||
private void performMergeInstruction(Instruction instruction, boolean copyBytes)
|
||||
throws CodeUnitInsertionException, MemoryAccessException {
|
||||
Address minAddress = instruction.getMinAddress();
|
||||
Address maxAddress = instruction.getMaxAddress();
|
||||
Address maxAddress;
|
||||
if (instruction.isLengthOverridden()) {
|
||||
maxAddress = minAddress.add(instruction.getParsedLength() - 1);
|
||||
}
|
||||
else {
|
||||
maxAddress = instruction.getMaxAddress();
|
||||
}
|
||||
Program fromPgm = instruction.getProgram();
|
||||
// Code unit should already be cleared where this instruction needs to go.
|
||||
Listing resultListing = resultPgm.getListing();
|
||||
|
@ -868,9 +874,12 @@ class CodeUnitMerger extends AbstractListingMerger {
|
|||
ProgramMemoryUtil.copyBytesInRanges(resultPgm, fromPgm, minAddress, maxAddress);
|
||||
}
|
||||
|
||||
// avoid forcing length of new instruction if old instruction length was not overriden
|
||||
int lengthOverride = instruction.isLengthOverridden() ? instruction.getLength() : 0;
|
||||
|
||||
Instruction inst = resultListing.createInstruction(minAddress, instruction.getPrototype(),
|
||||
new DumbMemBufferImpl(resultPgm.getMemory(), minAddress),
|
||||
new ProgramProcessorContext(resultPgm.getProgramContext(), minAddress));
|
||||
new ProgramProcessorContext(resultPgm.getProgramContext(), minAddress), lengthOverride);
|
||||
|
||||
// Set the fallthrough override if necessary.
|
||||
if (instruction.isFallThroughOverridden()) {
|
||||
|
|
|
@ -570,7 +570,6 @@ public class ListingMergeManager implements MergeResolver, ListingMergeConstants
|
|||
* determine the conflicts.
|
||||
* @param mergers the listing mergers whose conflicts are to be merged.
|
||||
* @param monitor indicates progress to user and allows cancel.
|
||||
* @throws ProgramConflictException if programs can't be compared using Diff.
|
||||
* @throws MemoryAccessException if bytes can't be merged.
|
||||
* @throws CancelledException if the user cancels the merge.
|
||||
*/
|
||||
|
|
|
@ -445,6 +445,7 @@ public class AutoAnalysisManager implements DomainObjectListener {
|
|||
break;
|
||||
case ChangeManager.DOCR_FALLTHROUGH_CHANGED:
|
||||
case ChangeManager.DOCR_FLOWOVERRIDE_CHANGED:
|
||||
case ChangeManager.DOCR_LENGTH_OVERRIDE_CHANGED:
|
||||
// TODO: not sure if this should be done this way or explicitly
|
||||
// via the application commands (this is inconsistent with other
|
||||
// codeDefined cases which do not rely on change events (e.g., disassembly)
|
||||
|
|
|
@ -88,6 +88,7 @@ public class DisassemblerPlugin extends Plugin {
|
|||
private DockingAction x86_64DisassembleAction;
|
||||
private DockingAction x86_32DisassembleAction;
|
||||
private DockingAction setFlowOverrideAction;
|
||||
private DockingAction setLengthOverrideAction;
|
||||
|
||||
/** Dialog for obtaining the processor state to be used for disassembling. */
|
||||
// private ProcessorStateDialog processorStateDialog;
|
||||
|
@ -177,6 +178,7 @@ public class DisassemblerPlugin extends Plugin {
|
|||
x86_64DisassembleAction = new X86_64DisassembleAction(this, GROUP_NAME, false);
|
||||
x86_32DisassembleAction = new X86_64DisassembleAction(this, GROUP_NAME, true);
|
||||
setFlowOverrideAction = new SetFlowOverrideAction(this, GROUP_NAME);
|
||||
setLengthOverrideAction = new SetLengthOverrideAction(this, GROUP_NAME);
|
||||
|
||||
tool.addAction(disassembleAction);
|
||||
tool.addAction(disassembleRestrictedAction);
|
||||
|
@ -193,6 +195,7 @@ public class DisassemblerPlugin extends Plugin {
|
|||
tool.addAction(x86_32DisassembleAction);
|
||||
tool.addAction(contextAction);
|
||||
tool.addAction(setFlowOverrideAction);
|
||||
tool.addAction(setLengthOverrideAction);
|
||||
}
|
||||
|
||||
void disassembleRestrictedCallback(ListingActionContext context) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -91,8 +91,8 @@ public class FallThroughPlugin extends Plugin {
|
|||
return instruction != null && !instruction.isFallThroughOverridden();
|
||||
}
|
||||
};
|
||||
autoFallthroughAction.setPopupMenuData(new MenuData(new String[] { "Fallthrough",
|
||||
"Auto Override" }, null, "Fallthrough"));
|
||||
autoFallthroughAction.setPopupMenuData(
|
||||
new MenuData(new String[] { "Fallthrough", "Auto Override" }, null, "Fallthrough"));
|
||||
|
||||
tool.addAction(autoFallthroughAction);
|
||||
|
||||
|
@ -111,8 +111,8 @@ public class FallThroughPlugin extends Plugin {
|
|||
return instruction != null && instruction.isFallThroughOverridden();
|
||||
}
|
||||
};
|
||||
clearFallthroughAction.setPopupMenuData(new MenuData(new String[] { "Fallthrough",
|
||||
"Clear Overrides" }, null, "Fallthrough"));
|
||||
clearFallthroughAction.setPopupMenuData(
|
||||
new MenuData(new String[] { "Fallthrough", "Clear Overrides" }, null, "Fallthrough"));
|
||||
|
||||
tool.addAction(clearFallthroughAction);
|
||||
|
||||
|
@ -128,8 +128,8 @@ public class FallThroughPlugin extends Plugin {
|
|||
return instruction != null;
|
||||
}
|
||||
};
|
||||
setFallthroughAction.setPopupMenuData(new MenuData(
|
||||
new String[] { "Fallthrough", "Set..." }, null, "Fallthrough"));
|
||||
setFallthroughAction.setPopupMenuData(
|
||||
new MenuData(new String[] { "Fallthrough", "Set..." }, null, "Fallthrough"));
|
||||
|
||||
tool.addAction(setFallthroughAction);
|
||||
}
|
||||
|
|
|
@ -326,8 +326,9 @@ public class InsertBytesWidget extends ReusableDialogComponentProvider implement
|
|||
private List<OperandMetadata> createOperandMetadata(PseudoInstruction instruction)
|
||||
throws MemoryAccessException {
|
||||
|
||||
List<OperandMetadata> operands = new ArrayList<>();
|
||||
byte[] bytes = instruction.getParsedBytes();
|
||||
|
||||
List<OperandMetadata> operands = new ArrayList<>();
|
||||
for (int i = 0; i < instruction.getNumOperands(); i++) {
|
||||
OperandMetadata operandMD = new OperandMetadata();
|
||||
operandMD.setOpType(instruction.getOperandType(i));
|
||||
|
@ -337,8 +338,9 @@ public class InsertBytesWidget extends ReusableDialogComponentProvider implement
|
|||
// prototype object in the pseudo instruction. For the value string we have to do
|
||||
// a bit of calculating: we know the entire instruction byte string and we know
|
||||
// this operand mask, so AND them together and we get the operand bytes.
|
||||
byte[] mask = instruction.getPrototype().getOperandValueMask(i).getBytes();
|
||||
byte[] value = InstructionSearchUtils.byteArrayAnd(mask, instruction.getBytes());
|
||||
InstructionPrototype prototype = instruction.getPrototype();
|
||||
byte[] mask = prototype.getOperandValueMask(i).getBytes();
|
||||
byte[] value = InstructionSearchUtils.byteArrayAnd(mask, bytes);
|
||||
MaskContainer maskContainer = new MaskContainer(mask, value);
|
||||
|
||||
operandMD.setMaskContainer(maskContainer);
|
||||
|
@ -362,8 +364,10 @@ public class InsertBytesWidget extends ReusableDialogComponentProvider implement
|
|||
// The mask array we can get directly from the prototype. For the value array we
|
||||
// have to figure out which bits pertain to operands and just zero them out, so we're
|
||||
// just left with the instruction (mnemonic) bits.
|
||||
byte[] mask = instruction.getPrototype().getInstructionMask().getBytes();
|
||||
byte[] value = clearOperandBits(mask, instruction.getBytes());
|
||||
InstructionPrototype prototype = instruction.getPrototype();
|
||||
byte[] mask = prototype.getInstructionMask().getBytes();
|
||||
byte[] value = instruction.getParsedBytes();
|
||||
value = clearOperandBits(mask, value);
|
||||
MaskContainer mnemonicMask = new MaskContainer(mask, value);
|
||||
|
||||
InstructionMetadata instructionMD = new InstructionMetadata(mnemonicMask);
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -18,8 +17,7 @@ package ghidra.app.plugin.core.reloc;
|
|||
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.lang.*;
|
||||
import ghidra.program.model.listing.Instruction;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.program.model.mem.DumbMemBufferImpl;
|
||||
import ghidra.program.model.mem.MemBuffer;
|
||||
import ghidra.program.model.symbol.Reference;
|
||||
|
@ -31,6 +29,10 @@ public class InstructionStasher {
|
|||
private Address address;
|
||||
private InstructionPrototype prototype;
|
||||
private Reference[] referencesFrom;
|
||||
private FlowOverride flowOverride;
|
||||
private Address fallthroughOverride;
|
||||
private int lengthOverride;
|
||||
|
||||
private Address minAddress;
|
||||
|
||||
public InstructionStasher(Program program, Address address) {
|
||||
|
@ -47,6 +49,12 @@ public class InstructionStasher {
|
|||
minAddress = instruction.getMinAddress();
|
||||
prototype = instruction.getPrototype();
|
||||
referencesFrom = instruction.getReferencesFrom();
|
||||
flowOverride = instruction.getFlowOverride();
|
||||
fallthroughOverride =
|
||||
instruction.isFallThroughOverridden() ? instruction.getFallThrough() : null;
|
||||
// Relocation data change may mutate instruction. Do not force length of instruction
|
||||
// unless it was previously overriden. A value of 0 allows length to match prototoype.
|
||||
lengthOverride = instruction.isLengthOverridden() ? instruction.getLength() : 0;
|
||||
program.getListing().clearCodeUnits(minAddress, instruction.getMaxAddress(), false);
|
||||
}
|
||||
|
||||
|
@ -57,7 +65,16 @@ public class InstructionStasher {
|
|||
MemBuffer buf = new DumbMemBufferImpl(program.getMemory(), minAddress);
|
||||
ProcessorContext context =
|
||||
new ProgramProcessorContext(program.getProgramContext(), minAddress);
|
||||
program.getListing().createInstruction(minAddress, prototype, buf, context);
|
||||
Instruction instr = program.getListing()
|
||||
.createInstruction(minAddress, prototype, buf, context, lengthOverride);
|
||||
|
||||
if (flowOverride != FlowOverride.NONE) {
|
||||
instr.setFlowOverride(flowOverride);
|
||||
}
|
||||
|
||||
if (fallthroughOverride != null) {
|
||||
instr.setFallThrough(fallthroughOverride);
|
||||
}
|
||||
|
||||
for (Reference reference : referencesFrom) {
|
||||
if (reference.getSource() != SourceType.DEFAULT) {
|
||||
|
|
|
@ -568,7 +568,13 @@ class ProgramTextWriter {
|
|||
}
|
||||
|
||||
try {
|
||||
byte[] bytes = cu.getBytes();
|
||||
byte[] bytes;
|
||||
if (cu instanceof Instruction instr) {
|
||||
bytes = instr.getParsedBytes();
|
||||
}
|
||||
else {
|
||||
bytes = cu.getBytes();
|
||||
}
|
||||
StringBuffer bytesbuf = new StringBuffer();
|
||||
for (int i = 0; i < bytes.length; ++i) {
|
||||
if (i > 0) {
|
||||
|
@ -630,10 +636,7 @@ class ProgramTextWriter {
|
|||
|
||||
if (options.isHTML()) {
|
||||
Reference ref =
|
||||
cu.getProgram()
|
||||
.getReferenceManager()
|
||||
.getPrimaryReferenceFrom(cuAddress,
|
||||
i);
|
||||
cu.getProgram().getReferenceManager().getPrimaryReferenceFrom(cuAddress, i);
|
||||
addReferenceLinkedText(ref, opReps[i], true);
|
||||
}
|
||||
else {
|
||||
|
|
|
@ -174,7 +174,18 @@ public class BytesFieldFactory extends FieldFactory {
|
|||
}
|
||||
|
||||
CodeUnit cu = (CodeUnit) obj;
|
||||
int length = Math.min(cu.getLength(), 100);
|
||||
int length;
|
||||
|
||||
// Instructions: use prototype length so we display all bytes for length-override case
|
||||
if (cu instanceof Instruction) {
|
||||
// Consider all parsed bytes even if the overlap the next instruction due to the
|
||||
// use of an instruction length-override.
|
||||
length = ((Instruction) cu).getParsedLength();
|
||||
}
|
||||
else {
|
||||
length = Math.min(cu.getLength(), 100);
|
||||
}
|
||||
|
||||
byte[] bytes = new byte[length];
|
||||
Memory memory = cu.getProgram().getMemory();
|
||||
try {
|
||||
|
|
|
@ -120,7 +120,8 @@ public class MnemonicFieldFactory extends FieldFactory {
|
|||
}
|
||||
else if (cu instanceof Instruction) {
|
||||
Instruction instr = (Instruction) cu;
|
||||
if (instr.getFlowOverride() != FlowOverride.NONE || instr.isFallThroughOverridden()) {
|
||||
if (instr.getFlowOverride() != FlowOverride.NONE || instr.isFallThroughOverridden() ||
|
||||
instr.isLengthOverridden()) {
|
||||
c = MnemonicColors.OVERRIDE;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -177,12 +177,20 @@ public class PostCommentFieldFactory extends FieldFactory {
|
|||
}
|
||||
}
|
||||
|
||||
if (instr.isFallThroughOverridden()) {
|
||||
if (instr.isLengthOverridden() || instr.isFallThroughOverridden()) {
|
||||
Address fallThrough = instr.getFallThrough();
|
||||
String fallthroughComment = "-- Fallthrough Override: " +
|
||||
(fallThrough != null ? fallThrough.toString() : "NO-FALLTHROUGH");
|
||||
String fallthroughComment =
|
||||
"-- Fallthrough" + (instr.isFallThroughOverridden() ? " Override" : "") + ": " +
|
||||
(fallThrough != null ? fallThrough.toString() : "NO-FALLTHROUGH");
|
||||
comments.addFirst(fallthroughComment);
|
||||
}
|
||||
|
||||
if (instr.isLengthOverridden()) {
|
||||
String lengthOverrideComment = "-- Length Override: " + instr.getLength() +
|
||||
" (actual length is " + instr.getParsedLength() + ")";
|
||||
comments.addFirst(lengthOverrideComment);
|
||||
}
|
||||
|
||||
FlowOverride flowOverride = instr.getFlowOverride();
|
||||
if (flowOverride != FlowOverride.NONE) {
|
||||
String flowOverrideComment =
|
||||
|
|
|
@ -194,10 +194,10 @@ public class DiffUtility extends SimpleDiffUtility {
|
|||
}
|
||||
else if (namespace instanceof GhidraClass) {
|
||||
return otherProgram.getSymbolTable()
|
||||
.createClass(otherParentNamespace, namespace.getName(), source);
|
||||
.createClass(otherParentNamespace, namespace.getName(), source);
|
||||
}
|
||||
return otherProgram.getSymbolTable()
|
||||
.createNameSpace(otherParentNamespace, namespace.getName(), source);
|
||||
.createNameSpace(otherParentNamespace, namespace.getName(), source);
|
||||
}
|
||||
|
||||
// /**
|
||||
|
@ -330,10 +330,10 @@ public class DiffUtility extends SimpleDiffUtility {
|
|||
return null;
|
||||
}
|
||||
return otherProgram.getReferenceManager()
|
||||
.getReference(fromAddr, toAddr, ref.getOperandIndex());
|
||||
.getReference(fromAddr, toAddr, ref.getOperandIndex());
|
||||
}
|
||||
Reference otherRef = otherProgram.getReferenceManager()
|
||||
.getPrimaryReferenceFrom(fromAddr, ref.getOperandIndex());
|
||||
.getPrimaryReferenceFrom(fromAddr, ref.getOperandIndex());
|
||||
if (otherRef != null && ref.getToAddress().hasSameAddressSpace(otherRef.getToAddress())) {
|
||||
return otherRef;
|
||||
}
|
||||
|
@ -358,10 +358,10 @@ public class DiffUtility extends SimpleDiffUtility {
|
|||
return null;
|
||||
}
|
||||
return program.getReferenceManager()
|
||||
.getReference(fromAddr1, toAddr1, p2Ref.getOperandIndex());
|
||||
.getReference(fromAddr1, toAddr1, p2Ref.getOperandIndex());
|
||||
}
|
||||
Reference p1Ref = program.getReferenceManager()
|
||||
.getPrimaryReferenceFrom(fromAddr1, p2Ref.getOperandIndex());
|
||||
.getPrimaryReferenceFrom(fromAddr1, p2Ref.getOperandIndex());
|
||||
if (p1Ref != null && p1Ref.getToAddress().hasSameAddressSpace(p2Ref.getToAddress())) {
|
||||
return p1Ref;
|
||||
}
|
||||
|
@ -386,8 +386,8 @@ public class DiffUtility extends SimpleDiffUtility {
|
|||
}
|
||||
// FIXME Should this be passing the Namespace?
|
||||
return otherProgram.getExternalManager()
|
||||
.addExtLocation(extLoc.getLibraryName(), extLoc.getLabel(), otherAddr,
|
||||
extLoc.getSource());
|
||||
.addExtLocation(extLoc.getLibraryName(), extLoc.getLabel(), otherAddr,
|
||||
extLoc.getSource());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -571,15 +571,18 @@ public class DiffUtility extends SimpleDiffUtility {
|
|||
Address rangeMin = range.getMinAddress();
|
||||
Address rangeMax = range.getMaxAddress();
|
||||
CodeUnit minCu = listing.getCodeUnitContaining(rangeMin);
|
||||
// NOTE: minCu does not consider prior code unit which may share bytes due to
|
||||
// possible instruction length-override. Max address of code unit will factor-in
|
||||
// full length of instruction prototype.
|
||||
if (minCu != null) {
|
||||
Address minCuMinAddr = minCu.getMinAddress();
|
||||
if (minCuMinAddr.compareTo(rangeMin) != 0) {
|
||||
addrs.addRange(minCuMinAddr, minCu.getMaxAddress());
|
||||
addrs.addRange(minCuMinAddr, getMaxAddress(minCu));
|
||||
}
|
||||
}
|
||||
CodeUnit maxCu = listing.getCodeUnitContaining(rangeMax);
|
||||
if (maxCu != null) {
|
||||
Address maxCuMaxAddr = maxCu.getMaxAddress();
|
||||
Address maxCuMaxAddr = getMaxAddress(maxCu);
|
||||
if (maxCuMaxAddr.compareTo(rangeMax) != 0) {
|
||||
addrs.addRange(maxCu.getMinAddress(), maxCuMaxAddr);
|
||||
}
|
||||
|
@ -588,6 +591,20 @@ public class DiffUtility extends SimpleDiffUtility {
|
|||
return addrs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the code unit max address. Instruction max address will be based upon prototype
|
||||
* length which may be longer than code unit length if length-override is in effect.
|
||||
* @param cu code unit
|
||||
* @return effective code unit length
|
||||
*/
|
||||
private static Address getMaxAddress(CodeUnit cu) {
|
||||
if (cu instanceof Instruction inst && inst.isLengthOverridden()) {
|
||||
// factor in prototype length when length-override applies
|
||||
return cu.getMinAddress().add(inst.getParsedLength() - 1);
|
||||
}
|
||||
return cu.getMaxAddress();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the signed hex string representing the int value.
|
||||
* Positive values are represented beginning with 0x. (i.e. value of 12 would be 0xc)
|
||||
|
|
|
@ -2871,6 +2871,7 @@ public class ProgramDiff {
|
|||
if (i1 == i2) {
|
||||
return true;
|
||||
}
|
||||
// Checking length and prototype will handle use of length-override
|
||||
if (i1.getLength() != i2.getLength()) {
|
||||
return false;
|
||||
}
|
||||
|
@ -2888,7 +2889,9 @@ public class ProgramDiff {
|
|||
}
|
||||
|
||||
try {
|
||||
if (!Arrays.equals(i1.getBytes(), i2.getBytes())) {
|
||||
byte[] bytes1 = i1.getParsedBytes();
|
||||
byte[] bytes2 = i2.getParsedBytes();
|
||||
if (!Arrays.equals(bytes1, bytes2)) {
|
||||
return false; // bytes differ
|
||||
}
|
||||
}
|
||||
|
|
|
@ -906,8 +906,7 @@ public class ProgramDiffDetails {
|
|||
ordinal + " " + fieldName + " " + actualDt.getMnemonic(actualDt.getDefaultSettings()) +
|
||||
" " + getCategoryName(actualDt) + " " + "DataTypeSize=" +
|
||||
(actualDt.isZeroLength() ? 0 : actualDt.getLength()) + " " + "ComponentSize=" +
|
||||
dtc.getLength() + " " + ((comment != null) ? comment : "") +
|
||||
" " + newLine);
|
||||
dtc.getLength() + " " + ((comment != null) ? comment : "") + " " + newLine);
|
||||
return actualDt;
|
||||
}
|
||||
|
||||
|
@ -958,8 +957,7 @@ public class ProgramDiffDetails {
|
|||
fieldName = "field" + offset;
|
||||
}
|
||||
buf.append(newIndent + min.add(offset) + " " + dtc.getFieldName() + " " +
|
||||
dtc.getDataType().getName() + " " + "length=" +
|
||||
dtc.getLength() + " " +
|
||||
dtc.getDataType().getName() + " " + "length=" + dtc.getLength() + " " +
|
||||
((comment != null) ? comment : "") + " " + newLine);
|
||||
}
|
||||
}
|
||||
|
@ -987,6 +985,7 @@ public class ProgramDiffDetails {
|
|||
boolean removedFallThrough =
|
||||
inst.isFallThroughOverridden() && (inst.getFallThrough() == null);
|
||||
boolean hasFlowOverride = inst.getFlowOverride() != FlowOverride.NONE;
|
||||
boolean hasLengthOverride = inst.isLengthOverridden();
|
||||
cuRep = cu.toString();
|
||||
if (removedFallThrough) {
|
||||
cuRep += newLine + indent + getSpaces(addrRangeStr.length()) + " " +
|
||||
|
@ -1011,6 +1010,11 @@ public class ProgramDiffDetails {
|
|||
cuRep += newLine + indent + getSpaces(addrRangeStr.length()) + " " +
|
||||
"Flow Override: " + inst.getFlowOverride();
|
||||
}
|
||||
if (hasLengthOverride) {
|
||||
cuRep += newLine + indent + getSpaces(addrRangeStr.length()) + " " +
|
||||
"Length Override: " + inst.getLength() + " (actual length is " +
|
||||
inst.getParsedLength() + ")";
|
||||
}
|
||||
cuRep += newLine + indent + getSpaces(addrRangeStr.length()) + " " +
|
||||
"Instruction Prototype hash = " +
|
||||
Integer.toHexString(inst.getPrototype().hashCode());
|
||||
|
@ -1463,8 +1467,8 @@ public class ProgramDiffDetails {
|
|||
private boolean addSpecificCommentDetails(int commentType, String commentName) {
|
||||
boolean hasCommentDiff = false;
|
||||
try {
|
||||
for (Address p1Address = minP1Address; p1Address.compareTo(
|
||||
maxP1Address) <= 0; p1Address = p1Address.add(1L)) {
|
||||
for (Address p1Address = minP1Address; p1Address
|
||||
.compareTo(maxP1Address) <= 0; p1Address = p1Address.add(1L)) {
|
||||
Address p2Address = SimpleDiffUtility.getCompatibleAddress(p1, p1Address, p2);
|
||||
String noComment = "No " + commentName + ".";
|
||||
String cmt1 = l1.getComment(commentType, p1Address);
|
||||
|
@ -2169,8 +2173,7 @@ public class ProgramDiffDetails {
|
|||
// Handle case where the class for a Saveable property is missing (unsupported).
|
||||
if (cu.getProgram()
|
||||
.getListing()
|
||||
.getPropertyMap(
|
||||
propertyName) instanceof UnsupportedMapDB) {
|
||||
.getPropertyMap(propertyName) instanceof UnsupportedMapDB) {
|
||||
buf.append(
|
||||
indent2 + propertyName + " is an unsupported property." + newLine);
|
||||
continue;
|
||||
|
@ -2241,8 +2244,8 @@ public class ProgramDiffDetails {
|
|||
BookmarkManager bmm1 = p1.getBookmarkManager();
|
||||
BookmarkManager bmm2 = p2.getBookmarkManager();
|
||||
try {
|
||||
for (Address p1Address = minP1Address; p1Address.compareTo(
|
||||
maxP1Address) <= 0; p1Address = p1Address.add(1)) {
|
||||
for (Address p1Address = minP1Address; p1Address
|
||||
.compareTo(maxP1Address) <= 0; p1Address = p1Address.add(1)) {
|
||||
Address p2Address = SimpleDiffUtility.getCompatibleAddress(p1, p1Address, p2);
|
||||
Bookmark[] marks1 = bmm1.getBookmarks(p1Address);
|
||||
Arrays.sort(marks1, BOOKMARK_COMPARATOR);
|
||||
|
@ -2341,7 +2344,7 @@ public class ProgramDiffDetails {
|
|||
|
||||
private boolean isSameInstruction(Instruction i1, Instruction i2) {
|
||||
boolean samePrototypes = i1.getPrototype().equals(i2.getPrototype());
|
||||
boolean sameInstructionLength = i1.getLength() == i2.getLength();
|
||||
boolean sameInstructionLength = i1.getLength() == i2.getLength(); // factors length override
|
||||
boolean sameFallthrough = ProgramDiff.isSameFallthrough(p1, i1, p2, i2);
|
||||
boolean sameFlowOverride = i1.getFlowOverride() == i2.getFlowOverride();
|
||||
return samePrototypes && sameInstructionLength && sameFallthrough && sameFlowOverride;
|
||||
|
|
|
@ -29,7 +29,6 @@ import ghidra.util.datastruct.Accumulator;
|
|||
import ghidra.util.datastruct.ListAccumulator;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
import ghidra.util.task.TaskMonitorAdapter;
|
||||
import utility.function.TerminatingConsumer;
|
||||
|
||||
/**
|
||||
|
@ -151,14 +150,26 @@ public class ProgramMemoryUtil {
|
|||
*/
|
||||
private static void copyByteRange(Memory toMem, Memory fromMem, AddressRange range)
|
||||
throws MemoryAccessException {
|
||||
|
||||
// TODO: Addresses from one program cannot be used with another program (e.g., overlays)
|
||||
// TODO: Should be using AddressTranslator
|
||||
// TODO: Method relies too heavily on caller limiting range to valid initialized memory in both programs
|
||||
|
||||
// Copy the bytes for this range
|
||||
int length = 0;
|
||||
Address writeAddress = range.getMinAddress();
|
||||
for (long len = range.getLength(); len > 0; len -= length) {
|
||||
length = (int) Math.min(len, Integer.MAX_VALUE);
|
||||
byte[] bytes = new byte[length];
|
||||
fromMem.getBytes(writeAddress, bytes);
|
||||
toMem.setBytes(writeAddress, bytes);
|
||||
byte[] srcBytes = new byte[length];
|
||||
|
||||
// NOTE: problems will arise if srcLen != length
|
||||
int srcLen = fromMem.getBytes(writeAddress, srcBytes);
|
||||
byte[] destBytes = new byte[srcLen];
|
||||
if (toMem.getBytes(writeAddress, destBytes) != srcLen ||
|
||||
!Arrays.equals(destBytes, 0, srcLen, srcBytes, 0, srcLen)) {
|
||||
// only copy bytes if they differ
|
||||
toMem.setBytes(writeAddress, srcBytes, 0, srcLen);
|
||||
}
|
||||
if (len > length) {
|
||||
writeAddress = writeAddress.add(length);
|
||||
}
|
||||
|
|
|
@ -628,7 +628,9 @@ public class ProgramMerge {
|
|||
return true;
|
||||
}
|
||||
try {
|
||||
if (!Arrays.equals(instruction.getBytes(), resultInstruction.getBytes())) {
|
||||
byte[] bytes = instruction.getParsedBytes();
|
||||
byte[] resultBytes = resultInstruction.getParsedBytes();
|
||||
if (!Arrays.equals(bytes, resultBytes)) {
|
||||
return true; // bytes differ
|
||||
}
|
||||
}
|
||||
|
@ -682,8 +684,8 @@ public class ProgramMerge {
|
|||
bytesLength = (int) originMax.subtract(originMin);
|
||||
}
|
||||
else {
|
||||
originMax = originInstruction.getMaxAddress();
|
||||
bytesLength = originInstruction.getLength();
|
||||
bytesLength = originInstruction.getParsedLength();
|
||||
originMax = originMin.add(bytesLength - 1);
|
||||
}
|
||||
|
||||
Address resultMax = originToResultTranslator.getAddress(originMax);
|
||||
|
@ -699,7 +701,7 @@ public class ProgramMerge {
|
|||
|
||||
// If there are byte differences for this instruction then the
|
||||
// bytes need to get copied even though the user did not indicate to.
|
||||
if (bytesAreDifferent(originByteDiffs, originMin, resultMin, bytesLength)) { // FIXME
|
||||
if (bytesMayDiffer(originByteDiffs, originMin, resultMin, bytesLength)) {
|
||||
// Copy all the bytes for the instruction if any bytes differ.
|
||||
ProgramMemoryUtil.copyBytesInRanges(resultProgram, originProgram, resultMin, resultMax);
|
||||
}
|
||||
|
@ -709,7 +711,8 @@ public class ProgramMerge {
|
|||
newInst = disassembleDelaySlottedInstruction(resultProgram, resultMin);
|
||||
}
|
||||
else {
|
||||
newInst = disassembleNonDelaySlotInstruction(resultProgram, resultMin);
|
||||
newInst = disassembleNonDelaySlotInstruction(resultProgram, resultMin,
|
||||
originInstruction.isLengthOverridden() ? originInstruction.getLength() : 0);
|
||||
}
|
||||
if (newInst == null) {
|
||||
return;
|
||||
|
@ -740,24 +743,29 @@ public class ProgramMerge {
|
|||
}
|
||||
|
||||
private Instruction disassembleDelaySlottedInstruction(Program program, Address addr) {
|
||||
// WARNING: does not support instruction length override use
|
||||
// Use heavyweight disassembler for delay slotted instruction
|
||||
AddressSet restrictedSet = new AddressSet(addr);
|
||||
Disassembler disassembler =
|
||||
Disassembler.getDisassembler(program, TaskMonitor.DUMMY, null);
|
||||
Disassembler disassembler = Disassembler.getDisassembler(program, TaskMonitor.DUMMY, null);
|
||||
disassembler.disassemble(addr, restrictedSet, false);
|
||||
return program.getListing().getInstructionAt(addr);
|
||||
}
|
||||
|
||||
private Instruction disassembleNonDelaySlotInstruction(Program program, Address addr) {
|
||||
private Instruction disassembleNonDelaySlotInstruction(Program program, Address addr,
|
||||
int lengthOverride) {
|
||||
// Use lightweight disassembler for simple case
|
||||
DisassemblerContextImpl context = new DisassemblerContextImpl(program.getProgramContext());
|
||||
context.flowStart(addr);
|
||||
try {
|
||||
InstructionPrototype proto = program.getLanguage()
|
||||
.parse(new DumbMemBufferImpl(program.getMemory(), addr), context, false);
|
||||
if (lengthOverride > proto.getLength()) {
|
||||
lengthOverride = 0;
|
||||
}
|
||||
return resultListing.createInstruction(addr, proto,
|
||||
new DumbMemBufferImpl(program.getMemory(), addr),
|
||||
new ProgramProcessorContext(program.getProgramContext(), addr));
|
||||
new ProgramProcessorContext(program.getProgramContext(), addr),
|
||||
Math.min(lengthOverride, proto.getLength()));
|
||||
}
|
||||
catch (Exception e) {
|
||||
program.getBookmarkManager()
|
||||
|
@ -767,17 +775,13 @@ public class ProgramMerge {
|
|||
return null;
|
||||
}
|
||||
|
||||
private boolean bytesAreDifferent(AddressSetView originByteDiffs, Address originMin,
|
||||
private boolean bytesMayDiffer(AddressSetView originByteDiffs, Address originMin,
|
||||
Address resultMin, int byteCnt) throws MemoryAccessException {
|
||||
if (originByteDiffs != null) {
|
||||
AddressSet resultByteDiffs = originToResultTranslator.getAddressSet(originByteDiffs);
|
||||
return resultByteDiffs.intersects(new AddressSet(resultMin, resultMin.add(byteCnt)));
|
||||
}
|
||||
byte[] originBytes = new byte[byteCnt];
|
||||
originProgram.getMemory().getBytes(originMin, originBytes);
|
||||
byte[] resultBytes = new byte[byteCnt];
|
||||
resultProgram.getMemory().getBytes(resultMin, resultBytes);
|
||||
return !Arrays.equals(originBytes, resultBytes);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -808,7 +812,7 @@ public class ProgramMerge {
|
|||
// If there are byte differences for this instruction then the
|
||||
// bytes need to get copied even though the user did not indicate to.
|
||||
if (copyBytes &&
|
||||
bytesAreDifferent(originByteDiffs, originMin, resultMin, originData.getLength())) {
|
||||
bytesMayDiffer(originByteDiffs, originMin, resultMin, originData.getLength())) {
|
||||
// Copy all the bytes for the instruction if any bytes differ.
|
||||
ProgramMemoryUtil.copyBytesInRanges(resultProgram, originProgram, resultMin, resultMax);
|
||||
}
|
||||
|
@ -1717,9 +1721,7 @@ public class ProgramMerge {
|
|||
// Now discard any tags we've been told to remove.
|
||||
if (discardTags != null) {
|
||||
Set<String> tagNames = getTagNames(discardTags);
|
||||
Iterator<FunctionTag> iter = resultTags.iterator();
|
||||
while (iter.hasNext()) {
|
||||
FunctionTag tag = iter.next();
|
||||
for (FunctionTag tag : resultTags) {
|
||||
if (tagNames.contains(tag.getName())) {
|
||||
resultFunction.removeTag(tag.getName());
|
||||
|
||||
|
@ -3934,8 +3936,7 @@ public class ProgramMerge {
|
|||
if (originObject != null) {
|
||||
try {
|
||||
if (resultOpm == null) {
|
||||
resultOpm =
|
||||
createPropertyMap(userPropertyName, resultProgram, originOpm);
|
||||
resultOpm = createPropertyMap(userPropertyName, resultProgram, originOpm);
|
||||
}
|
||||
resultOpm.add(resultAddress, originObject);
|
||||
}
|
||||
|
|
|
@ -669,7 +669,8 @@ public class SymbolicPropogator {
|
|||
else {
|
||||
int instrByteHashCode = -1;
|
||||
try {
|
||||
instrByteHashCode = Arrays.hashCode(instr.getBytes());
|
||||
byte[] bytes = instr.getParsedBytes();
|
||||
instrByteHashCode = Arrays.hashCode(bytes);
|
||||
}
|
||||
catch (MemoryAccessException e) {
|
||||
// this should NEVER happen, should always be able to get the bytes...
|
||||
|
|
|
@ -26,8 +26,7 @@ import ghidra.docking.settings.*;
|
|||
import ghidra.framework.plugintool.ServiceProvider;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.data.EndianSettingsDefinition;
|
||||
import ghidra.program.model.listing.CodeUnit;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.program.model.mem.MemoryAccessException;
|
||||
import ghidra.program.util.BytesFieldLocation;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
|
@ -50,100 +49,98 @@ public class BytesTableColumn extends ProgramLocationTableColumnExtensionPoint<A
|
|||
private static SettingsDefinition[] SETTINGS_DEFS =
|
||||
{ BYTE_COUNT, MEMORY_OFFSET, ENDIANESS, FORMAT };
|
||||
|
||||
private final GColumnRenderer<Byte[]> monospacedRenderer =
|
||||
new AbstractGColumnRenderer<>() {
|
||||
@Override
|
||||
protected void configureFont(JTable table, TableModel model, int column) {
|
||||
setFont(getFixedWidthFont());
|
||||
private final GColumnRenderer<Byte[]> monospacedRenderer = new AbstractGColumnRenderer<>() {
|
||||
@Override
|
||||
protected void configureFont(JTable table, TableModel model, int column) {
|
||||
setFont(getFixedWidthFont());
|
||||
}
|
||||
|
||||
private String formatBytes(Byte[] bytes, Settings settings) {
|
||||
boolean bigEndian = (ENDIANESS.getChoice(settings) != EndianSettingsDefinition.LITTLE);
|
||||
|
||||
int startIx = 0;
|
||||
int endIx = bytes.length;
|
||||
int inc = 1;
|
||||
if (!bigEndian) {
|
||||
startIx = bytes.length - 1;
|
||||
endIx = -1;
|
||||
inc = -1;
|
||||
}
|
||||
|
||||
private String formatBytes(Byte[] bytes, Settings settings) {
|
||||
boolean bigEndian =
|
||||
(ENDIANESS.getChoice(settings) != EndianSettingsDefinition.LITTLE);
|
||||
int format = FORMAT.getChoice(settings);
|
||||
if (format == FormatSettingsDefinition.CHAR) {
|
||||
return bytesToString(bytes);
|
||||
}
|
||||
|
||||
int startIx = 0;
|
||||
int endIx = bytes.length;
|
||||
int inc = 1;
|
||||
if (!bigEndian) {
|
||||
startIx = bytes.length - 1;
|
||||
endIx = -1;
|
||||
inc = -1;
|
||||
StringBuilder buffer = new StringBuilder();
|
||||
for (int i = startIx; i != endIx; i += inc) {
|
||||
if (buffer.length() != 0) {
|
||||
buffer.append(' ');
|
||||
}
|
||||
buffer.append(getByteString(bytes[i], format));
|
||||
}
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
int format = FORMAT.getChoice(settings);
|
||||
if (format == FormatSettingsDefinition.CHAR) {
|
||||
return bytesToString(bytes);
|
||||
private String bytesToString(Byte[] bytes) {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
for (byte b : bytes) {
|
||||
char c = (char) (b & 0xff);
|
||||
if (c > 32 && c < 128) {
|
||||
buf.append((char) (b & 0xff));
|
||||
}
|
||||
|
||||
StringBuilder buffer = new StringBuilder();
|
||||
for (int i = startIx; i != endIx; i += inc) {
|
||||
if (buffer.length() != 0) {
|
||||
buffer.append(' ');
|
||||
}
|
||||
buffer.append(getByteString(bytes[i], format));
|
||||
else {
|
||||
buf.append('.');
|
||||
}
|
||||
return buffer.toString();
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
private String bytesToString(Byte[] bytes) {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
for (byte b : bytes) {
|
||||
char c = (char) (b & 0xff);
|
||||
if (c > 32 && c < 128) {
|
||||
buf.append((char) (b & 0xff));
|
||||
}
|
||||
else {
|
||||
buf.append('.');
|
||||
}
|
||||
}
|
||||
return buf.toString();
|
||||
private String getByteString(Byte b, int format) {
|
||||
|
||||
String val;
|
||||
switch (format) {
|
||||
case FormatSettingsDefinition.DECIMAL:
|
||||
val = Integer.toString(b);
|
||||
break;
|
||||
case FormatSettingsDefinition.BINARY:
|
||||
val = Integer.toBinaryString(b & 0x0ff);
|
||||
val = StringFormat.padIt(val, 8, (char) 0, true);
|
||||
break;
|
||||
case FormatSettingsDefinition.OCTAL:
|
||||
val = Integer.toOctalString(b & 0x0ff);
|
||||
val = StringFormat.padIt(val, 3, (char) 0, true);
|
||||
break;
|
||||
default:
|
||||
case FormatSettingsDefinition.HEX:
|
||||
val = Integer.toHexString(b & 0x0ff).toUpperCase();
|
||||
val = StringFormat.padIt(val, 2, (char) 0, true);
|
||||
break;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
private String getByteString(Byte b, int format) {
|
||||
@Override
|
||||
public Component getTableCellRendererComponent(GTableCellRenderingData data) {
|
||||
|
||||
String val;
|
||||
switch (format) {
|
||||
case FormatSettingsDefinition.DECIMAL:
|
||||
val = Integer.toString(b);
|
||||
break;
|
||||
case FormatSettingsDefinition.BINARY:
|
||||
val = Integer.toBinaryString(b & 0x0ff);
|
||||
val = StringFormat.padIt(val, 8, (char) 0, true);
|
||||
break;
|
||||
case FormatSettingsDefinition.OCTAL:
|
||||
val = Integer.toOctalString(b & 0x0ff);
|
||||
val = StringFormat.padIt(val, 3, (char) 0, true);
|
||||
break;
|
||||
default:
|
||||
case FormatSettingsDefinition.HEX:
|
||||
val = Integer.toHexString(b & 0x0ff).toUpperCase();
|
||||
val = StringFormat.padIt(val, 2, (char) 0, true);
|
||||
break;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
JLabel label = (JLabel) super.getTableCellRendererComponent(data);
|
||||
|
||||
@Override
|
||||
public Component getTableCellRendererComponent(GTableCellRenderingData data) {
|
||||
Object value = data.getValue();
|
||||
Settings settings = data.getColumnSettings();
|
||||
|
||||
JLabel label = (JLabel) super.getTableCellRendererComponent(data);
|
||||
Byte[] bytes = (Byte[]) value;
|
||||
|
||||
Object value = data.getValue();
|
||||
Settings settings = data.getColumnSettings();
|
||||
setText(formatBytes(bytes, settings));
|
||||
|
||||
Byte[] bytes = (Byte[]) value;
|
||||
return label;
|
||||
}
|
||||
|
||||
setText(formatBytes(bytes, settings));
|
||||
|
||||
return label;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFilterString(Byte[] t, Settings settings) {
|
||||
String formatted = formatBytes(t, settings);
|
||||
return formatted;
|
||||
}
|
||||
};
|
||||
@Override
|
||||
public String getFilterString(Byte[] t, Settings settings) {
|
||||
String formatted = formatBytes(t, settings);
|
||||
return formatted;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Default Constructor
|
||||
|
@ -194,7 +191,12 @@ public class BytesTableColumn extends ProgramLocationTableColumnExtensionPoint<A
|
|||
if (cu == null) { // can happen for 'SpecialAddress'es
|
||||
return new Byte[0];
|
||||
}
|
||||
bytes = cu.getBytes();
|
||||
if (cu instanceof Instruction instr) {
|
||||
bytes = instr.getParsedBytes();
|
||||
}
|
||||
else {
|
||||
bytes = cu.getBytes();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -94,7 +94,7 @@ public abstract class AbstractListingMergeManagerTest extends AbstractMergeTest
|
|||
new ProgramProcessorContext(program.getProgramContext(), atAddress);
|
||||
InstructionPrototype proto = program.getLanguage().parse(buf, context, false);
|
||||
Instruction createdInstruction =
|
||||
listing.createInstruction(atAddress, proto, buf, context);
|
||||
listing.createInstruction(atAddress, proto, buf, context, 0);
|
||||
commit = true;
|
||||
return createdInstruction;
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
|
@ -19,6 +19,7 @@ import static org.junit.Assert.*;
|
|||
|
||||
import org.junit.*;
|
||||
|
||||
import db.Transaction;
|
||||
import ghidra.app.plugin.core.bookmark.BookmarkPlugin;
|
||||
import ghidra.app.plugin.core.clear.ClearPlugin;
|
||||
import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
|
||||
|
@ -1678,6 +1679,59 @@ public class CodeUnitIteratorTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
assertEquals(10, cnt);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetCodeUnitsSetWithOverride() throws Exception {
|
||||
|
||||
// set length-override on 0x3f4
|
||||
try (Transaction tx = program.openTransaction("Set Length Override")) {
|
||||
program.getListing().getInstructionAt(addr(0x3f4)).setLengthOverride(1);
|
||||
}
|
||||
|
||||
AddressSet set = new AddressSet();
|
||||
set.addRange(addr(0x3f0), addr(0x3f9));
|
||||
|
||||
CodeUnitIterator it = listing.getCodeUnits(set, true);
|
||||
|
||||
CodeUnit cu = it.next();
|
||||
assertEquals(addr(0x3f1), cu.getMinAddress());
|
||||
assertTrue(cu instanceof Data);
|
||||
assertFalse(((Data) cu).isDefined());
|
||||
|
||||
cu = it.next();
|
||||
assertEquals(addr(0x3f2), cu.getMinAddress());
|
||||
assertTrue(cu instanceof Instruction);
|
||||
assertEquals(2, cu.getLength());
|
||||
|
||||
cu = it.next();
|
||||
assertEquals(addr(0x3f4), cu.getMinAddress());
|
||||
assertTrue(cu instanceof Instruction);
|
||||
assertEquals(1, cu.getLength()); // length overriden
|
||||
assertEquals("imm r0,#0x0", cu.toString());
|
||||
|
||||
cu = it.next();
|
||||
assertEquals(addr(0x3f5), cu.getMinAddress());
|
||||
assertTrue(cu instanceof Data);
|
||||
assertFalse(((Data) cu).isDefined());
|
||||
|
||||
cu = it.next();
|
||||
assertEquals(addr(0x3f6), cu.getMinAddress());
|
||||
assertTrue(cu instanceof Instruction);
|
||||
assertEquals(2, cu.getLength());
|
||||
|
||||
cu = it.next();
|
||||
assertEquals(addr(0x3f8), cu.getMinAddress());
|
||||
assertTrue(cu instanceof Data);
|
||||
assertFalse(((Data) cu).isDefined());
|
||||
|
||||
cu = it.next();
|
||||
assertEquals(addr(0x3f9), cu.getMinAddress());
|
||||
assertTrue(cu instanceof Data);
|
||||
assertFalse(((Data) cu).isDefined());
|
||||
|
||||
assertFalse(it.hasNext());
|
||||
assertNull(it.next());
|
||||
}
|
||||
|
||||
private Address addr(long l) {
|
||||
return space.getAddress(l);
|
||||
}
|
||||
|
@ -1704,7 +1758,7 @@ public class CodeUnitIteratorTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
ProcessorContext context = new ProgramProcessorContext(program.getProgramContext(), atAddr);
|
||||
InstructionPrototype proto = program.getLanguage().parse(buf, context, false);
|
||||
|
||||
listing.createInstruction(atAddr, proto, buf, context);
|
||||
listing.createInstruction(atAddr, proto, buf, context, 0);
|
||||
}
|
||||
|
||||
private void startTransaction() {
|
||||
|
|
|
@ -99,7 +99,7 @@ public class SymbolUtilities1Test extends AbstractGhidraHeadedIntegrationTest {
|
|||
new ProgramProcessorContext(program.getProgramContext(), addr(0x200));
|
||||
DumbMemBufferImpl membuf = new DumbMemBufferImpl(program.getMemory(), addr(0x200));
|
||||
InstructionPrototype proto = program.getLanguage().parse(membuf, context, false);
|
||||
listing.createInstruction(addr(0x200), proto, membuf, context);
|
||||
listing.createInstruction(addr(0x200), proto, membuf, context, 0);
|
||||
Symbol symbol = symbolTable.getPrimarySymbol(addr(0x200));
|
||||
assertEquals("LAB_00000200", symbol.getName());
|
||||
|
||||
|
|
|
@ -97,7 +97,7 @@ public class SymbolUtilities2Test extends AbstractGhidraHeadedIntegrationTest {
|
|||
new ProgramProcessorContext(program.getProgramContext(), addr(0x200));
|
||||
DumbMemBufferImpl membuf = new DumbMemBufferImpl(program.getMemory(), addr(0x200));
|
||||
InstructionPrototype proto = program.getLanguage().parse(membuf, context, false);
|
||||
listing.createInstruction(addr(0x200), proto, membuf, context);
|
||||
listing.createInstruction(addr(0x200), proto, membuf, context, 0);
|
||||
Symbol symbol = symbolTable.getPrimarySymbol(addr(0x200));
|
||||
assertEquals("LAB_CODE_0200", symbol.getName());
|
||||
|
||||
|
|
|
@ -1063,7 +1063,7 @@ public class CodeManagerTest extends AbstractGenericTest {
|
|||
MemBuffer buf = new DumbMemBufferImpl(mem, atAddr);
|
||||
ProcessorContext context = new ProgramProcessorContext(program.getProgramContext(), atAddr);
|
||||
InstructionPrototype proto = program.getLanguage().parse(buf, context, false);
|
||||
listing.createInstruction(atAddr, proto, buf, context);
|
||||
listing.createInstruction(atAddr, proto, buf, context, 0);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,10 +15,9 @@
|
|||
*/
|
||||
package ghidra.feature.vt.api.markupitem;
|
||||
|
||||
import static ghidra.feature.vt.api.main.VTMarkupItemApplyActionType.REPLACE;
|
||||
import static ghidra.feature.vt.db.VTTestUtils.addr;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static ghidra.feature.vt.api.main.VTMarkupItemApplyActionType.*;
|
||||
import static ghidra.feature.vt.db.VTTestUtils.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
@ -379,7 +378,7 @@ public class DataTypeMarkupItemTest extends AbstractVTMarkupItemTest {
|
|||
new ProgramProcessorContext(program.getProgramContext(), atAddress);
|
||||
InstructionPrototype proto = program.getLanguage().parse(buf, context, false);
|
||||
Instruction createdInstruction =
|
||||
listing.createInstruction(atAddress, proto, buf, context);
|
||||
listing.createInstruction(atAddress, proto, buf, context, 0);
|
||||
commit = true;
|
||||
return createdInstruction;
|
||||
}
|
||||
|
|
|
@ -182,7 +182,8 @@ public abstract class PcodeEmit {
|
|||
*/
|
||||
void resolveFinalFallthrough() throws IOException {
|
||||
try {
|
||||
if (fallOverride == null || fallOverride.equals(getStartAddress().add(fallOffset))) {
|
||||
if (fallOverride == null) {
|
||||
// handles both length-override and fallthrough override cases
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -505,19 +505,6 @@ abstract class PseudoCodeUnit implements CodeUnit {
|
|||
return program.getListing().getCodeUnitBefore(address);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the given CodeUnit follows directly after this code unit.
|
||||
*
|
||||
* @throws ConcurrentModificationException
|
||||
* if this object is no longer valid.
|
||||
*/
|
||||
@Override
|
||||
public boolean isSuccessor(CodeUnit codeUnit) {
|
||||
Address min = codeUnit.getMinAddress();
|
||||
|
||||
return this.getMaxAddress().isSuccessor(min);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getComment(int commentType) {
|
||||
return comments.get(commentType);
|
||||
|
|
|
@ -146,6 +146,18 @@ public class PseudoInstruction extends PseudoCodeUnit implements Instruction, In
|
|||
return b0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getParsedLength() {
|
||||
// length-override is not supported
|
||||
return getLength();
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getParsedBytes() throws MemoryAccessException {
|
||||
// length-override is not supported
|
||||
return getBytes();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Register getBaseContextRegister() {
|
||||
return procContext.getBaseContextRegister();
|
||||
|
@ -505,6 +517,16 @@ public class PseudoInstruction extends PseudoCodeUnit implements Instruction, In
|
|||
this.flowOverride = flowOverride != null ? flowOverride : FlowOverride.NONE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLengthOverride(int length) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLengthOverridden() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFallThroughOverridden() {
|
||||
return fallThroughOverride != null;
|
||||
|
|
|
@ -22,7 +22,8 @@ import ghidra.program.database.function.OverlappingFunctionException;
|
|||
import ghidra.program.database.module.TreeManager;
|
||||
import ghidra.program.database.symbol.FunctionSymbol;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.data.DataType;
|
||||
import ghidra.program.model.data.DataTypeManager;
|
||||
import ghidra.program.model.lang.*;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.program.model.mem.MemBuffer;
|
||||
|
@ -107,7 +108,7 @@ class ListingDB implements Listing {
|
|||
|
||||
@Override
|
||||
public Instruction getInstructionContaining(Address addr) {
|
||||
return codeMgr.getInstructionContaining(addr);
|
||||
return codeMgr.getInstructionContaining(addr, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -261,14 +262,15 @@ class ListingDB implements Listing {
|
|||
}
|
||||
|
||||
@Override
|
||||
public PropertyMap getPropertyMap(String propertyName) {
|
||||
public PropertyMap<?> getPropertyMap(String propertyName) {
|
||||
return codeMgr.getPropertyMap(propertyName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Instruction createInstruction(Address addr, InstructionPrototype prototype,
|
||||
MemBuffer memBuf, ProcessorContextView context) throws CodeUnitInsertionException {
|
||||
return codeMgr.createCodeUnit(addr, prototype, memBuf, context);
|
||||
MemBuffer memBuf, ProcessorContextView context, int length)
|
||||
throws CodeUnitInsertionException {
|
||||
return codeMgr.createCodeUnit(addr, prototype, memBuf, context, length);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -278,8 +280,7 @@ class ListingDB implements Listing {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Data createData(Address addr, DataType dataType)
|
||||
throws CodeUnitInsertionException {
|
||||
public Data createData(Address addr, DataType dataType) throws CodeUnitInsertionException {
|
||||
return codeMgr.createCodeUnit(addr, dataType, dataType.getLength());
|
||||
}
|
||||
|
||||
|
@ -292,8 +293,7 @@ class ListingDB implements Listing {
|
|||
@Override
|
||||
public void clearCodeUnits(Address startAddr, Address endAddr, boolean clearContext) {
|
||||
try {
|
||||
codeMgr.clearCodeUnits(startAddr, endAddr, clearContext,
|
||||
TaskMonitor.DUMMY);
|
||||
codeMgr.clearCodeUnits(startAddr, endAddr, clearContext, TaskMonitor.DUMMY);
|
||||
}
|
||||
catch (CancelledException e) {
|
||||
// can't happen with dummy monitor
|
||||
|
|
|
@ -109,8 +109,10 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM
|
|||
* property map (StringTranslations).
|
||||
* 19-Jan-2023 - version 26 Improved relocation data records to incorporate status and
|
||||
* byte-length when original FileBytes should be used.
|
||||
* 10-Jul-2023 - VERSION 27 Add support for Instruction length override which utilizes
|
||||
* unused flag bits.
|
||||
*/
|
||||
static final int DB_VERSION = 26;
|
||||
static final int DB_VERSION = 27;
|
||||
|
||||
/**
|
||||
* UPGRADE_REQUIRED_BFORE_VERSION should be changed to DB_VERSION anytime the
|
||||
|
|
|
@ -155,11 +155,16 @@ public class CodeManager implements ErrorHandler, ManagerDB {
|
|||
while (addrIter.hasNext()) {
|
||||
monitor.checkCancelled();
|
||||
Address addr = addrIter.next();
|
||||
|
||||
Instruction instr = getInstructionAt(addr);
|
||||
if (instr == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
long offset = oldFallThroughs.getLong(addr);
|
||||
Address toAddr = addr.getNewAddress(offset);
|
||||
refMgr.addMemoryReference(addr, toAddr, RefType.FALL_THROUGH,
|
||||
SourceType.USER_DEFINED, Reference.MNEMONIC);
|
||||
instr.setFallThrough(toAddr);
|
||||
}
|
||||
catch (NoValueException e) {
|
||||
// skip
|
||||
|
@ -531,6 +536,16 @@ public class CodeManager implements ErrorHandler, ManagerDB {
|
|||
if (flowOverride != FlowOverride.NONE) {
|
||||
lastInstruction.setFlowOverride(flowOverride);
|
||||
}
|
||||
|
||||
try {
|
||||
if (protoInstr.isLengthOverridden()) {
|
||||
lastInstruction.setLengthOverride(protoInstr.getLength());
|
||||
}
|
||||
}
|
||||
catch (CodeUnitInsertionException e) {
|
||||
// unexpected - should have been caught previously
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
if (errorAddr != null && conflictCodeUnit == null &&
|
||||
|
@ -581,21 +596,34 @@ public class CodeManager implements ErrorHandler, ManagerDB {
|
|||
* @param prototype instruction definition object
|
||||
* @param memBuf the MemBuffer to use to get the bytes from memory
|
||||
* @param context object that has the state of all the registers.
|
||||
* @return the new instruction
|
||||
* @exception CodeUnitInsertionException thrown if code unit
|
||||
* overlaps with an existing code unit
|
||||
* @param length instruction byte-length (must be in the range 0..prototype.getLength()).
|
||||
* If smaller than the prototype length it must have a value no greater than 7, otherwise
|
||||
* an error will be thrown. A value of 0 or greater-than-or-equal the prototype length
|
||||
* will be ignored and not impose and override length. The length value must be a multiple
|
||||
* of the {@link Language#getInstructionAlignment() instruction alignment} .
|
||||
* @return the newly created instruction.
|
||||
* @throws CodeUnitInsertionException thrown if the new Instruction would overlap and
|
||||
* existing {@link CodeUnit} or the specified {@code length} is unsupported.
|
||||
* @throws IllegalArgumentException if a negative {@code length} is specified.
|
||||
*/
|
||||
public Instruction createCodeUnit(Address address, InstructionPrototype prototype,
|
||||
MemBuffer memBuf, ProcessorContextView context) throws CodeUnitInsertionException {
|
||||
MemBuffer memBuf, ProcessorContextView context, int length)
|
||||
throws CodeUnitInsertionException {
|
||||
|
||||
lock.acquire();
|
||||
creatingInstruction = true;
|
||||
try {
|
||||
Address endAddr = address.addNoWrap(prototype.getLength() - 1);
|
||||
|
||||
int forcedLengthOverride = InstructionDB.checkLengthOverride(length, prototype);
|
||||
if (length == 0) {
|
||||
length = prototype.getLength();
|
||||
}
|
||||
Address endAddr = address.addNoWrap(length - 1);
|
||||
checkValidAddressRange(address, endAddr);
|
||||
|
||||
InstructionDB inst = addInstruction(address, endAddr, prototype, memBuf, context);
|
||||
if (forcedLengthOverride != 0) {
|
||||
inst.doSetLengthOverride(forcedLengthOverride);
|
||||
}
|
||||
|
||||
// fire event
|
||||
program.setChanged(ChangeManager.DOCR_CODE_ADDED, address, endAddr, null, inst);
|
||||
|
@ -1370,29 +1398,48 @@ public class CodeManager implements ErrorHandler, ManagerDB {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the instruction whose min address is less than or equal to the specified address and
|
||||
* Returns an instruction whose min address is less than or equal to the specified address and
|
||||
* whose max address is greater than or equal to the specified address.
|
||||
* If {@code usePrototypeLength==true}
|
||||
* <pre>{@literal
|
||||
* instruction.minAddress() <= addr <= instruction.maxAddress()
|
||||
* instruction.getMinAddress() <= addr <=
|
||||
* instruction.getMinAddress().add(instruction.getPrototype().getLength() - 1)
|
||||
* }</pre>
|
||||
*
|
||||
* If {@code usePrototypeLength==false}
|
||||
* <pre>{@literal
|
||||
* instruction.getMinAddress() <= addr <= instruction.getMaxAddress()
|
||||
* }</pre>
|
||||
* The use of the prototype length is required when guarding against memory modifications. If
|
||||
* a length-override is present only one of the entangled instructions will be returned and is
|
||||
* intended to simply indicate the presence of a conflict.
|
||||
* @param address the address to be contained
|
||||
* @param usePrototypeLength if actual prototype length should be considered when identifying a
|
||||
* conflict (required when checking for memory modification conflicts), otherwise code unit
|
||||
* length is used. These lengths can vary when a
|
||||
* {@link Instruction#setLengthOverride(int) length-override} is in affect for an instruction.
|
||||
* @return the instruction containing the specified address, or null if a
|
||||
* instruction does not exist
|
||||
*/
|
||||
public Instruction getInstructionContaining(Address address) {
|
||||
public Instruction getInstructionContaining(Address address, boolean usePrototypeLength) {
|
||||
lock.acquire();
|
||||
try {
|
||||
Instruction instr = getInstructionAt(address);
|
||||
|
||||
if (instr != null) {
|
||||
return instr;
|
||||
}
|
||||
|
||||
instr = this.getInstructionBefore(address);
|
||||
|
||||
if (instr != null && instr.contains(address)) {
|
||||
return instr;
|
||||
if (instr != null) {
|
||||
Address endAddr;
|
||||
if (usePrototypeLength && instr.isLengthOverridden()) {
|
||||
endAddr = instr.getMinAddress().add(instr.getParsedLength() - 1);
|
||||
}
|
||||
else {
|
||||
endAddr = instr.getMaxAddress();
|
||||
}
|
||||
if (address.compareTo(endAddr) <= 0) {
|
||||
return instr;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
@ -1639,8 +1686,14 @@ public class CodeManager implements ErrorHandler, ManagerDB {
|
|||
nextInstEndAddr = nextInstAddr;
|
||||
int protoID = nextInstRec.getIntValue(InstDBAdapter.PROTO_ID_COL);
|
||||
InstructionPrototype proto = protoMgr.getPrototype(protoID);
|
||||
int len = proto != null ? proto.getLength()
|
||||
: nextInstAddr.getAddressSpace().getAddressableUnitSize();
|
||||
int len;
|
||||
if (proto != null) {
|
||||
len = InstructionDB.getLength(proto,
|
||||
nextInstRec.getByteValue(InstDBAdapter.FLAGS_COL));
|
||||
}
|
||||
else {
|
||||
len = nextInstAddr.getAddressSpace().getAddressableUnitSize();
|
||||
}
|
||||
if (len > 1) {
|
||||
try {
|
||||
nextInstEndAddr = nextInstAddr.addNoWrap(len - 1);
|
||||
|
@ -1730,7 +1783,7 @@ public class CodeManager implements ErrorHandler, ManagerDB {
|
|||
if (addr != AddressMap.INVALID_ADDRESS_KEY) {
|
||||
lock.acquire();
|
||||
try {
|
||||
Instruction inst = getInstructionContaining(address);
|
||||
Instruction inst = getInstructionContaining(address, false);
|
||||
if (inst != null) {
|
||||
return null;
|
||||
}
|
||||
|
@ -2538,7 +2591,7 @@ public class CodeManager implements ErrorHandler, ManagerDB {
|
|||
return;
|
||||
}
|
||||
boolean fail = false;
|
||||
if (getInstructionContaining(start) != null) {
|
||||
if (getInstructionContaining(start, false) != null) {
|
||||
fail = true;
|
||||
}
|
||||
else {
|
||||
|
@ -2574,7 +2627,7 @@ public class CodeManager implements ErrorHandler, ManagerDB {
|
|||
if (!program.getMemory().contains(start, end)) {
|
||||
return false;
|
||||
}
|
||||
if (getInstructionContaining(start) != null) {
|
||||
if (getInstructionContaining(start, false) != null) {
|
||||
return false;
|
||||
}
|
||||
if (getDefinedDataContaining(start) != null) {
|
||||
|
@ -3168,13 +3221,21 @@ public class CodeManager implements ErrorHandler, ManagerDB {
|
|||
* @param newFallThroughRef new fallthrough reference or null if removed
|
||||
*/
|
||||
public void fallThroughChanged(Address fromAddr, Reference newFallThroughRef) {
|
||||
if (newFallThroughRef != null &&
|
||||
newFallThroughRef.getReferenceType() != RefType.FALL_THROUGH) {
|
||||
throw new IllegalArgumentException("invalid reftype");
|
||||
}
|
||||
lock.acquire();
|
||||
try {
|
||||
InstructionDB instr = getInstructionAt(addrMap.getKey(fromAddr, false));
|
||||
// TODO: Should prevent this if instruction is null or isInDelaySlot
|
||||
if (instr != null) {
|
||||
instr.fallThroughChanged(newFallThroughRef);
|
||||
if (instr == null) {
|
||||
// Do not allow fallthrough ref without instruction
|
||||
if (newFallThroughRef != null) {
|
||||
refManager.delete(newFallThroughRef);
|
||||
}
|
||||
return;
|
||||
}
|
||||
instr.fallThroughChanged(newFallThroughRef);
|
||||
}
|
||||
finally {
|
||||
lock.release();
|
||||
|
|
|
@ -23,6 +23,7 @@ import org.apache.commons.lang3.StringUtils;
|
|||
|
||||
import db.DBRecord;
|
||||
import ghidra.program.database.*;
|
||||
import ghidra.program.database.references.ReferenceDBManager;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressOutOfBoundsException;
|
||||
import ghidra.program.model.lang.*;
|
||||
|
@ -48,7 +49,7 @@ abstract class CodeUnitDB extends DatabaseObject implements CodeUnit, ProcessorC
|
|||
protected long addr;
|
||||
protected Address endAddr;
|
||||
protected int length;
|
||||
protected ReferenceManager refMgr;
|
||||
protected ReferenceDBManager refMgr;
|
||||
protected ProgramDB program;
|
||||
|
||||
private DBRecord commentRec;
|
||||
|
@ -407,13 +408,6 @@ abstract class CodeUnitDB extends DatabaseObject implements CodeUnit, ProcessorC
|
|||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSuccessor(CodeUnit codeUnit) {
|
||||
Address min = codeUnit.getMinAddress();
|
||||
|
||||
return this.getMaxAddress().isSuccessor(min);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<String> propertyNames() {
|
||||
PropertyMapManager upm = codeMgr.getPropertyMapManager();
|
||||
|
|
|
@ -15,8 +15,7 @@
|
|||
*/
|
||||
package ghidra.program.database.code;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
|
||||
import db.DBRecord;
|
||||
import ghidra.program.database.DBObjectCache;
|
||||
|
@ -29,6 +28,7 @@ import ghidra.program.model.mem.MemoryAccessException;
|
|||
import ghidra.program.model.pcode.PcodeOp;
|
||||
import ghidra.program.model.scalar.Scalar;
|
||||
import ghidra.program.model.symbol.*;
|
||||
import ghidra.program.model.util.CodeUnitInsertionException;
|
||||
import ghidra.program.util.ChangeManager;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.exception.NoValueException;
|
||||
|
@ -38,16 +38,21 @@ import ghidra.util.exception.NoValueException;
|
|||
*/
|
||||
public class InstructionDB extends CodeUnitDB implements Instruction, InstructionContext {
|
||||
|
||||
private static byte FALLTHROUGH_SET_MASK = (byte) 0x01;
|
||||
private static byte FALLTHROUGH_CLEAR_MASK = (byte) 0xfe;
|
||||
private static final byte FALLTHROUGH_SET_MASK = 0x01;
|
||||
private static final byte FALLTHROUGH_CLEAR_MASK = ~FALLTHROUGH_SET_MASK;
|
||||
|
||||
private static byte FLOWOVERRIDE_MASK = (byte) 0x0e;
|
||||
private static byte FLOWOVERRIDE_CLEAR_MASK = (byte) 0xf1;
|
||||
private static int FLOWOVERRIDE_SHIFT = 1;
|
||||
private static final byte FLOW_OVERRIDE_SET_MASK = 0x0e;
|
||||
private static final byte FLOW_OVERRIDE_CLEAR_MASK = ~FLOW_OVERRIDE_SET_MASK;
|
||||
private static final int FLOW_OVERRIDE_SHIFT = 1;
|
||||
|
||||
private static final byte LENGTH_OVERRIDE_SET_MASK = 0x70;
|
||||
private static final byte LENGTH_OVERRIDE_CLEAR_MASK = ~LENGTH_OVERRIDE_SET_MASK;
|
||||
private static final int LENGTH_OVERRIDE_SHIFT = 4;
|
||||
|
||||
private InstructionPrototype proto;
|
||||
private byte flags;
|
||||
private FlowOverride flowOverride;
|
||||
private int lengthOverride;
|
||||
private final static Address[] EMPTY_ADDR_ARRAY = new Address[0];
|
||||
private volatile boolean clearingFallThroughs = false;
|
||||
|
||||
|
@ -68,7 +73,8 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio
|
|||
this.proto = proto;
|
||||
this.flags = flags;
|
||||
flowOverride =
|
||||
FlowOverride.getFlowOverride((flags & FLOWOVERRIDE_MASK) >> FLOWOVERRIDE_SHIFT);
|
||||
FlowOverride.getFlowOverride((flags & FLOW_OVERRIDE_SET_MASK) >> FLOW_OVERRIDE_SHIFT);
|
||||
refreshLength();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -83,6 +89,36 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio
|
|||
return proto.hasDelaySlots() ? (length * 2) : length;
|
||||
}
|
||||
|
||||
private void refreshLength() {
|
||||
length = proto.getLength();
|
||||
lengthOverride = (flags & LENGTH_OVERRIDE_SET_MASK) >> LENGTH_OVERRIDE_SHIFT;
|
||||
if (lengthOverride != 0 && lengthOverride < length) {
|
||||
length = lengthOverride;
|
||||
}
|
||||
else {
|
||||
lengthOverride = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the instruction code unit length based upon its prototype and flags
|
||||
* which will be used to check for a length-override condition.
|
||||
* @param proto instruction prototype
|
||||
* @param flags instruction flags
|
||||
* @return instruction code unit length
|
||||
*/
|
||||
static int getLength(InstructionPrototype proto, byte flags) {
|
||||
int length = proto.getLength();
|
||||
int lengthOverride = (flags & LENGTH_OVERRIDE_SET_MASK) >> LENGTH_OVERRIDE_SHIFT;
|
||||
if (lengthOverride != 0 && lengthOverride < length) {
|
||||
length = lengthOverride;
|
||||
}
|
||||
else {
|
||||
lengthOverride = 0;
|
||||
}
|
||||
return length;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean hasBeenDeleted(DBRecord rec) {
|
||||
if (rec == null) {
|
||||
|
@ -106,10 +142,11 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio
|
|||
if (!newProto.equals(proto)) {
|
||||
return true;
|
||||
}
|
||||
length = proto.getLength();
|
||||
|
||||
flags = rec.getByteValue(InstDBAdapter.FLAGS_COL);
|
||||
flowOverride =
|
||||
FlowOverride.getFlowOverride((flags & FLOWOVERRIDE_MASK) >> FLOWOVERRIDE_SHIFT);
|
||||
FlowOverride.getFlowOverride((flags & FLOW_OVERRIDE_SET_MASK) >> FLOW_OVERRIDE_SHIFT);
|
||||
refreshLength();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -142,6 +179,31 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio
|
|||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getParsedLength() {
|
||||
return isLengthOverridden() ? proto.getLength() : getLength();
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getParsedBytes() throws MemoryAccessException {
|
||||
if (!isLengthOverridden()) {
|
||||
return getBytes();
|
||||
}
|
||||
lock.acquire();
|
||||
try {
|
||||
checkIsValid();
|
||||
int len = proto.getLength();
|
||||
byte[] b = new byte[len];
|
||||
if (len != getMemory().getBytes(address, b)) {
|
||||
throw new MemoryAccessException("Failed to read " + len + " bytes at " + address);
|
||||
}
|
||||
return b;
|
||||
}
|
||||
finally {
|
||||
lock.release();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Address getFallFrom() {
|
||||
lock.acquire();
|
||||
|
@ -192,19 +254,22 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio
|
|||
}
|
||||
}
|
||||
|
||||
private Address getFallThroughReference() {
|
||||
for (Reference ref : refMgr.getReferencesFrom(address)) {
|
||||
if (ref.getReferenceType().isFallthrough() && ref.getToAddress().isMemoryAddress()) {
|
||||
return ref.getToAddress();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Address getFallThrough() {
|
||||
lock.acquire();
|
||||
try {
|
||||
checkIsValid();
|
||||
if (isFallThroughOverridden()) {
|
||||
for (Reference ref : refMgr.getReferencesFrom(address)) {
|
||||
if (ref.getReferenceType().isFallthrough() &&
|
||||
ref.getToAddress().isMemoryAddress()) {
|
||||
return ref.getToAddress();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
return getFallThroughReference();
|
||||
}
|
||||
return getDefaultFallThrough();
|
||||
}
|
||||
|
@ -221,7 +286,7 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio
|
|||
return EMPTY_ADDR_ARRAY;
|
||||
}
|
||||
|
||||
ArrayList<Address> list = new ArrayList<>();
|
||||
Set<Address> list = new HashSet<>();
|
||||
for (Reference ref : refs) {
|
||||
if (!ref.getReferenceType().isIndirect()) {
|
||||
list.add(ref.getToAddress());
|
||||
|
@ -232,8 +297,7 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio
|
|||
return EMPTY_ADDR_ARRAY;
|
||||
}
|
||||
|
||||
Address[] addrs = new Address[list.size()];
|
||||
return list.toArray(addrs);
|
||||
return list.toArray(new Address[list.size()]);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -537,8 +601,8 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio
|
|||
}
|
||||
FlowType origFlowType = getFlowType();
|
||||
|
||||
flags &= FLOWOVERRIDE_CLEAR_MASK;
|
||||
flags |= (flow.ordinal() << FLOWOVERRIDE_SHIFT);
|
||||
flags &= FLOW_OVERRIDE_CLEAR_MASK;
|
||||
flags |= (flow.ordinal() << FLOW_OVERRIDE_SHIFT);
|
||||
codeMgr.setFlags(addr, flags);
|
||||
flowOverride = flow;
|
||||
|
||||
|
@ -623,19 +687,25 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio
|
|||
|
||||
/**
|
||||
* Clear all existing fall-through references from address.
|
||||
* @param keepFallThroughRef if not null, corresponding fall-through reference will be preserved
|
||||
* @param keepFallThroughAddr if not null, corresponding fall-through reference will be
|
||||
* preserved.
|
||||
*/
|
||||
private void clearFallThroughRefs(Reference keepFallThroughRef) {
|
||||
private void clearFallThroughRefs(Address keepFallThroughAddr) {
|
||||
if (clearingFallThroughs) {
|
||||
return;
|
||||
}
|
||||
refreshIfNeeded();
|
||||
clearingFallThroughs = true;
|
||||
try {
|
||||
boolean fallThroughPreserved = false;
|
||||
for (Reference ref : refMgr.getReferencesFrom(address)) {
|
||||
if (ref.getReferenceType() == RefType.FALL_THROUGH &&
|
||||
!ref.equals(keepFallThroughRef)) {
|
||||
refMgr.delete(ref);
|
||||
if (ref.getReferenceType() == RefType.FALL_THROUGH) {
|
||||
if (!fallThroughPreserved && ref.getToAddress().equals(keepFallThroughAddr)) {
|
||||
fallThroughPreserved = true; // only preserve one
|
||||
}
|
||||
else {
|
||||
refMgr.delete(ref);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -646,9 +716,16 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio
|
|||
|
||||
void fallThroughChanged(Reference fallThroughRef) {
|
||||
if (!clearingFallThroughs) {
|
||||
clearFallThroughRefs(fallThroughRef);
|
||||
setFallthroughOverride(fallThroughRef != null &&
|
||||
fallThroughRef.getReferenceType() == RefType.FALL_THROUGH);
|
||||
Address fallThroughAddr = fallThroughRef != null ? fallThroughRef.getToAddress() : null;
|
||||
clearFallThroughRefs(fallThroughAddr); // ensure there is only one fallthrough ref
|
||||
if (fallThroughAddr == null) { // fallthrough ref removed
|
||||
setFallthroughOverride(false);
|
||||
addLengthOverrideFallthroughRef(); // restore length-override fallthrough if needed
|
||||
}
|
||||
else {
|
||||
// enable fallthrough-override if fallThroughRef does not match length-override fallthrough
|
||||
setFallthroughOverride(!fallThroughAddr.equals(getLengthOverrideFallThrough()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -661,8 +738,9 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio
|
|||
flags &= FALLTHROUGH_CLEAR_MASK;
|
||||
}
|
||||
codeMgr.setFlags(addr, flags);
|
||||
program.setChanged(ChangeManager.DOCR_FALLTHROUGH_CHANGED, address, address, null,
|
||||
null);
|
||||
}
|
||||
program.setChanged(ChangeManager.DOCR_FALLTHROUGH_CHANGED, address, address, null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -673,8 +751,10 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio
|
|||
if (!isFallThroughOverridden()) {
|
||||
return;
|
||||
}
|
||||
// clear fall-through override
|
||||
clearFallThroughRefs(null);
|
||||
setFallthroughOverride(false);
|
||||
addLengthOverrideFallthroughRef(); // restore length-override fallthrough if needed
|
||||
}
|
||||
finally {
|
||||
lock.release();
|
||||
|
@ -693,11 +773,12 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio
|
|||
return;
|
||||
}
|
||||
if (fallThroughAddr == null) {
|
||||
// Fall-through eliminated - no reference added
|
||||
// Fall-through eliminated (i.e., terminal flow) - no reference added
|
||||
clearFallThroughRefs(null);
|
||||
setFallthroughOverride(true);
|
||||
}
|
||||
else {
|
||||
// Adding fallthough ref will trigger override flag on callback
|
||||
refMgr.addMemoryReference(address, fallThroughAddr, RefType.FALL_THROUGH,
|
||||
SourceType.USER_DEFINED, Reference.MNEMONIC);
|
||||
}
|
||||
|
@ -705,7 +786,107 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio
|
|||
finally {
|
||||
lock.release();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLengthOverride(int len) throws CodeUnitInsertionException {
|
||||
lock.acquire();
|
||||
try {
|
||||
checkDeleted();
|
||||
if (doSetLengthOverride(len)) {
|
||||
program.setChanged(ChangeManager.DOCR_LENGTH_OVERRIDE_CHANGED, address, address,
|
||||
null, null);
|
||||
}
|
||||
}
|
||||
finally {
|
||||
lock.release();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check and revise a specified {@code length} to arrive at a suitable length-override value.
|
||||
* @param length instruction byte-length (must be in the range 0..{@code prototype-length}).
|
||||
* If smaller than the prototype length it must have a value no greater than 7, otherwise
|
||||
* an error will be thrown. A value of 0 or greater-than-or-equal the prototype length
|
||||
* will be ignored and not impose and override length. The length value must be a multiple
|
||||
* of the {@link Language#getInstructionAlignment() instruction alignment} .
|
||||
* @param prototype instruction prototype
|
||||
* @return length-override value (0 = disable length-override)
|
||||
* @throws CodeUnitInsertionException thrown if the new Instruction would overlap and
|
||||
* existing {@link CodeUnit} or the specified {@code length} is unsupported.
|
||||
* @throws IllegalArgumentException if a negative {@code length} is specified.
|
||||
*/
|
||||
public static int checkLengthOverride(int length, InstructionPrototype prototype)
|
||||
throws IllegalArgumentException, CodeUnitInsertionException {
|
||||
if (length < 0) {
|
||||
throw new IllegalArgumentException("Negative length not permitted");
|
||||
}
|
||||
int instrProtoLength = prototype.getLength();
|
||||
if (length == 0 || length == instrProtoLength) {
|
||||
return 0;
|
||||
}
|
||||
if (length > instrProtoLength) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int align = prototype.getLanguage().getInstructionAlignment();
|
||||
if (length % align != 0) {
|
||||
throw new CodeUnitInsertionException(
|
||||
"Length(" + length + ") override must be a multiple of " + align + " bytes");
|
||||
}
|
||||
|
||||
if (length > MAX_LENGTH_OVERRIDE) {
|
||||
throw new CodeUnitInsertionException("Unsupported length override: " + length);
|
||||
}
|
||||
return length;
|
||||
}
|
||||
|
||||
boolean doSetLengthOverride(int len) throws CodeUnitInsertionException {
|
||||
|
||||
int protoLength = proto.getLength();
|
||||
len = checkLengthOverride(len, proto);
|
||||
if (len == lengthOverride) {
|
||||
return false; // no change
|
||||
}
|
||||
|
||||
int instrLength = len != 0 ? len : protoLength;
|
||||
if (instrLength > getLength()) {
|
||||
Address newEndAddr = address.add(instrLength - 1);
|
||||
Address nextCodeUnitAddr = codeMgr.getDefinedAddressAfter(address);
|
||||
if (nextCodeUnitAddr != null && nextCodeUnitAddr.compareTo(newEndAddr) <= 0) {
|
||||
throw new CodeUnitInsertionException("Length override of " + instrLength +
|
||||
" conflicts with code unit at " + nextCodeUnitAddr);
|
||||
}
|
||||
}
|
||||
|
||||
flags &= LENGTH_OVERRIDE_CLEAR_MASK;
|
||||
flags |= (len << LENGTH_OVERRIDE_SHIFT);
|
||||
codeMgr.setFlags(addr, flags);
|
||||
|
||||
endAddr = null;
|
||||
refreshLength();
|
||||
|
||||
addLengthOverrideFallthroughRef();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void addLengthOverrideFallthroughRef() {
|
||||
if (isLengthOverridden() && !isFallThroughOverridden()) {
|
||||
// length-override always uses default fall-through address
|
||||
refMgr.addMemoryReference(address, getDefaultFallThrough(), RefType.FALL_THROUGH,
|
||||
SourceType.USER_DEFINED, Reference.MNEMONIC);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLengthOverridden() {
|
||||
refreshIfNeeded();
|
||||
return lengthOverride != 0;
|
||||
}
|
||||
|
||||
private Address getLengthOverrideFallThrough() {
|
||||
return isLengthOverridden() ? getDefaultFallThrough() : null;
|
||||
}
|
||||
|
||||
private boolean addrsEqual(Address addr1, Address addr2) {
|
||||
|
|
|
@ -18,6 +18,8 @@ package ghidra.program.database.external;
|
|||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import db.*;
|
||||
import ghidra.framework.store.FileSystem;
|
||||
import ghidra.program.database.ManagerDB;
|
||||
|
@ -90,8 +92,8 @@ public class ExternalManagerDB implements ManagerDB, ExternalManager {
|
|||
@Override
|
||||
public void setProgram(ProgramDB program) {
|
||||
this.program = program;
|
||||
symbolMgr = (SymbolManager) program.getSymbolTable();
|
||||
functionMgr = (FunctionManagerDB) program.getFunctionManager();
|
||||
symbolMgr = program.getSymbolTable();
|
||||
functionMgr = program.getFunctionManager();
|
||||
scopeMgr = program.getNamespaceManager();
|
||||
}
|
||||
|
||||
|
@ -153,8 +155,8 @@ public class ExternalManagerDB implements ManagerDB, ExternalManager {
|
|||
oldAddrMap.decodeAddress(rec.getLongValue(OldExtRefAdapter.FROM_ADDR_COL));
|
||||
int opIndex = rec.getShortValue(OldExtRefAdapter.OP_INDEX_COL);
|
||||
boolean userDefined = rec.getBooleanValue(OldExtRefAdapter.USER_DEFINED_COL);
|
||||
String name = nameMap.get(rec.getLongValue(OldExtRefAdapter.EXT_NAME_ID_COL));
|
||||
if (name == null) {
|
||||
String extLibraryName = nameMap.get(rec.getLongValue(OldExtRefAdapter.EXT_NAME_ID_COL));
|
||||
if (extLibraryName == null) {
|
||||
continue; // should not happen
|
||||
}
|
||||
String label = rec.getString(OldExtRefAdapter.LABEL_COL);
|
||||
|
@ -163,14 +165,11 @@ public class ExternalManagerDB implements ManagerDB, ExternalManager {
|
|||
: null;
|
||||
|
||||
try {
|
||||
refMgr.addExternalReference(fromAddr, name, label, addr,
|
||||
refMgr.addExternalReference(fromAddr, extLibraryName, label, addr,
|
||||
userDefined ? SourceType.USER_DEFINED : SourceType.IMPORTED, opIndex,
|
||||
RefType.DATA);
|
||||
}
|
||||
catch (DuplicateNameException e) {
|
||||
Msg.error(this, "Unexpected Exception: " + e.getMessage(), e);
|
||||
}
|
||||
catch (InvalidInputException e) {
|
||||
catch (InvalidInputException | DuplicateNameException e) {
|
||||
Msg.error(this, "Unexpected Exception: " + e.getMessage(), e);
|
||||
}
|
||||
monitor.setProgress(++cnt);
|
||||
|
@ -283,7 +282,8 @@ public class ExternalManagerDB implements ManagerDB, ExternalManager {
|
|||
|
||||
private SourceType checkExternalLabel(String extLabel, Address extAddr, SourceType source)
|
||||
throws InvalidInputException {
|
||||
if (extLabel == null || extLabel.length() == 0) {
|
||||
if (extLabel != null && (StringUtils.isBlank(extLabel) ||
|
||||
SymbolUtilities.isReservedExternalDefaultName(extLabel, addrMap.getAddressFactory()))) {
|
||||
extLabel = 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.");
|
||||
}
|
||||
sourceType = checkExternalLabel(extLabel, extAddr, sourceType);
|
||||
if (extAddr != null && !extAddr.isLoadedMemoryAddress()) {
|
||||
throw new InvalidInputException("Invalid memory address");
|
||||
if (extAddr != null) {
|
||||
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();
|
||||
try {
|
||||
|
@ -359,7 +366,7 @@ public class ExternalManagerDB implements ManagerDB, ExternalManager {
|
|||
/**
|
||||
* Get the external location which best matches the specified parameters.
|
||||
* 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 extAddr the address of function in the external program. Can be null
|
||||
* @return the best matching ExternalLocation or null.
|
||||
|
|
|
@ -2297,7 +2297,7 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
|
|||
|
||||
void checkRangeForInstructions(Address start, Address end) throws MemoryAccessException {
|
||||
CodeManager codeManager = program.getCodeManager();
|
||||
Instruction instr = codeManager.getInstructionContaining(start);
|
||||
Instruction instr = codeManager.getInstructionContaining(start, true);
|
||||
if (instr != null) {
|
||||
throw new MemoryAccessException(
|
||||
"Memory change conflicts with instruction at " + instr.getMinAddress());
|
||||
|
|
|
@ -449,6 +449,9 @@ public class ReferenceDBManager implements ReferenceManager, ManagerDB, ErrorHan
|
|||
if (!fromAddr.isMemoryAddress()) {
|
||||
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);
|
||||
if (function == null) {
|
||||
throw new IllegalArgumentException(
|
||||
|
@ -488,6 +491,9 @@ public class ReferenceDBManager implements ReferenceManager, ManagerDB, ErrorHan
|
|||
if (!fromAddr.isMemoryAddress()) {
|
||||
throw new IllegalArgumentException("From address must be memory address");
|
||||
}
|
||||
if (!type.isData()) {
|
||||
throw new IllegalArgumentException("Invalid register reference type: " + type);
|
||||
}
|
||||
removeAllFrom(fromAddr, opIndex);
|
||||
try {
|
||||
return addRef(fromAddr, register.getAddress(), type, sourceType, opIndex, false, false,
|
||||
|
@ -573,9 +579,9 @@ public class ReferenceDBManager implements ReferenceManager, ManagerDB, ErrorHan
|
|||
if (!fromAddr.isMemoryAddress()) {
|
||||
throw new IllegalArgumentException("From address must be memory addresses");
|
||||
}
|
||||
removeAllFrom(fromAddr, opIndex);
|
||||
try {
|
||||
if (symbolMgr.getPrimarySymbol(location.getExternalSpaceAddress()) != null) {
|
||||
removeAllFrom(fromAddr, opIndex);
|
||||
return addRef(fromAddr, location.getExternalSpaceAddress(), type, sourceType,
|
||||
opIndex, false, false, 0);
|
||||
}
|
||||
|
@ -598,24 +604,17 @@ public class ReferenceDBManager implements ReferenceManager, ManagerDB, ErrorHan
|
|||
public Reference addExternalReference(Address fromAddr, String libraryName, String extLabel,
|
||||
Address extAddr, SourceType sourceType, int opIndex, RefType type)
|
||||
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 {
|
||||
ExternalManagerDB extMgr = (ExternalManagerDB) program.getExternalManager();
|
||||
ExternalManagerDB extMgr = program.getExternalManager();
|
||||
ExternalLocation extLoc =
|
||||
extMgr.addExtLocation(libraryName, extLabel, extAddr, sourceType);
|
||||
|
||||
removeAllFrom(fromAddr, opIndex);
|
||||
|
||||
Address toAddr = extLoc.getExternalSpaceAddress();
|
||||
|
||||
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,
|
||||
Address extAddr, SourceType sourceType, int opIndex, RefType type)
|
||||
throws InvalidInputException, DuplicateNameException {
|
||||
if (extNamespace == null || !extNamespace.isExternal()) {
|
||||
throw new InvalidInputException("The namespace must be an external namespace.");
|
||||
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 {
|
||||
ExternalManagerDB extMgr = (ExternalManagerDB) program.getExternalManager();
|
||||
ExternalManagerDB extMgr = program.getExternalManager();
|
||||
ExternalLocation extLoc =
|
||||
extMgr.addExtLocation(extNamespace, extLabel, extAddr, sourceType);
|
||||
|
||||
removeAllFrom(fromAddr, opIndex);
|
||||
|
||||
Address toAddr = extLoc.getExternalSpaceAddress();
|
||||
return addRef(fromAddr, toAddr, type, sourceType, opIndex, false, false, 0);
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ import ghidra.program.model.mem.MemoryAccessException;
|
|||
public class AllBytesHashCalculator implements HashCalculator {
|
||||
@Override
|
||||
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) {
|
||||
startHash = SimpleCRC32.hashOneByte(startHash, bytes[i]);
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ package ghidra.program.model.listing;
|
|||
import java.util.ConcurrentModificationException;
|
||||
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.lang.InstructionPrototype;
|
||||
import ghidra.program.model.lang.Register;
|
||||
import ghidra.program.model.mem.MemBuffer;
|
||||
import ghidra.program.model.mem.MemoryAccessException;
|
||||
|
@ -175,19 +176,18 @@ public interface CodeUnit extends MemBuffer, PropertySet {
|
|||
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();
|
||||
|
||||
/**
|
||||
* 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
|
||||
* array length is the same as the codeUnits length
|
||||
* @throws MemoryAccessException if the full number of bytes could not be read.
|
||||
|
|
|
@ -149,11 +149,6 @@ public class DataStub implements Data {
|
|||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSuccessor(CodeUnit codeUnit) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLength() {
|
||||
throw new UnsupportedOperationException();
|
||||
|
|
|
@ -19,10 +19,12 @@ import java.util.List;
|
|||
|
||||
import ghidra.program.model.address.Address;
|
||||
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.PcodeOverride;
|
||||
import ghidra.program.model.symbol.FlowType;
|
||||
import ghidra.program.model.symbol.RefType;
|
||||
import ghidra.program.model.util.CodeUnitInsertionException;
|
||||
|
||||
/**
|
||||
* 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 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();
|
||||
|
||||
|
@ -46,6 +49,7 @@ public interface Instruction extends CodeUnit, ProcessorContext {
|
|||
/**
|
||||
* Get objects used by this operand (Address, Scalar, Register ...)
|
||||
* @param opIndex index of the operand.
|
||||
* @return objects used by this operand (Address, Scalar, Register ...)
|
||||
*/
|
||||
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.
|
||||
* @param index operand index
|
||||
* @return the operand reference type for the given operand index.
|
||||
*/
|
||||
public RefType getOperandRefType(int index);
|
||||
|
||||
|
@ -134,7 +139,7 @@ public interface Instruction extends CodeUnit, ProcessorContext {
|
|||
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 is useful for handling instructions that are found
|
||||
* in a delay slot.
|
||||
|
@ -159,32 +164,86 @@ public interface Instruction extends CodeUnit, ProcessorContext {
|
|||
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).
|
||||
*/
|
||||
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();
|
||||
|
||||
/**
|
||||
* Returns true if this instruction has a fall-through flow.
|
||||
* @return true if this instruction has a fall-through flow.
|
||||
*/
|
||||
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();
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* 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
|
||||
* 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
|
||||
* returns an integer instead of a boolean in case some other
|
||||
* processor executes more than one instruction from a delay slot.
|
||||
* @return delay slot depth (number of instructions)
|
||||
*/
|
||||
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();
|
||||
|
||||
/**
|
||||
* 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();
|
||||
|
||||
/**
|
||||
* 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();
|
||||
|
||||
|
@ -256,7 +316,7 @@ public interface Instruction extends CodeUnit, ProcessorContext {
|
|||
public void clearFallThroughOverride();
|
||||
|
||||
/**
|
||||
* Returns true if this instructions fallthrough has been overriden.
|
||||
* @return true if this instructions fallthrough has been overriden.
|
||||
*/
|
||||
public boolean isFallThroughOverridden();
|
||||
|
||||
|
|
|
@ -28,7 +28,8 @@ import ghidra.util.Msg;
|
|||
|
||||
public class InstructionPcodeOverride implements PcodeOverride {
|
||||
|
||||
protected Instruction instr;
|
||||
protected final Instruction instr;
|
||||
|
||||
private boolean callOverrideApplied = false;
|
||||
private boolean jumpOverrideApplied = false;
|
||||
private boolean callOtherCallOverrideApplied = false;
|
||||
|
@ -64,7 +65,7 @@ public class InstructionPcodeOverride implements PcodeOverride {
|
|||
public Address getFallThroughOverride() {
|
||||
Address defaultFallAddr = instr.getDefaultFallThrough();
|
||||
Address fallAddr = instr.getFallThrough();
|
||||
if (fallAddr != null && !fallAddr.equals(defaultFallAddr)) {
|
||||
if (fallAddr != null && (instr.isLengthOverridden() || !fallAddr.equals(defaultFallAddr))) {
|
||||
return fallAddr;
|
||||
}
|
||||
return null;
|
||||
|
|
|
@ -146,21 +146,26 @@ public class InstructionStub implements Instruction {
|
|||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSuccessor(CodeUnit codeUnit) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLength() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getParsedLength() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getBytes() throws MemoryAccessException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getParsedBytes() throws MemoryAccessException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getBytesInCodeUnit(byte[] buffer, int bufferOffset) throws MemoryAccessException {
|
||||
throw new UnsupportedOperationException();
|
||||
|
@ -458,6 +463,16 @@ public class InstructionStub implements Instruction {
|
|||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLengthOverride(int length) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLengthOverridden() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFlowOverride(FlowOverride flowOverride) {
|
||||
throw new UnsupportedOperationException();
|
||||
|
|
|
@ -602,17 +602,22 @@ public interface Listing {
|
|||
* changes will result in a <code>ContextChangeException</code>
|
||||
*
|
||||
* @param addr the address at which to create an instruction
|
||||
* @param prototype the InstructionPrototype the describes the type of
|
||||
* instruction to create.
|
||||
* @param memBuf buffer that provides the bytes that make up the
|
||||
* instruction.
|
||||
* @param prototype the InstructionPrototype that describes the type of instruction to create.
|
||||
* @param memBuf buffer that provides the bytes that make up the instruction.
|
||||
* @param context the processor context at this location.
|
||||
* @return the newly created instruction.
|
||||
* @exception CodeUnitInsertionException thrown if the new Instruction would
|
||||
* overlap and existing Instruction or defined data.
|
||||
* @param length instruction byte-length (must be in the range 0..prototype.getLength()).
|
||||
* If smaller than the prototype length it must have a value no greater than 7, otherwise
|
||||
* an error will be thrown. A value of 0 or greater-than-or-equal the prototype length
|
||||
* will be ignored and not impose and override length. The length value must be a multiple
|
||||
* of the {@link Language#getInstructionAlignment() instruction alignment} .
|
||||
* @return the newly created instruction.
|
||||
* @throws CodeUnitInsertionException thrown if the new Instruction would overlap and
|
||||
* existing {@link CodeUnit} or the specified {@code length} is unsupported.
|
||||
* @throws IllegalArgumentException if a negative {@code length} is specified.
|
||||
*/
|
||||
public Instruction 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
|
||||
|
@ -659,8 +664,7 @@ public interface Listing {
|
|||
* @exception CodeUnitInsertionException thrown if the new Instruction would
|
||||
* overlap and existing Instruction or defined data.
|
||||
*/
|
||||
public Data createData(Address addr, DataType dataType)
|
||||
throws CodeUnitInsertionException;
|
||||
public Data createData(Address addr, DataType dataType) throws CodeUnitInsertionException;
|
||||
|
||||
/**
|
||||
* Clears any code units in the given range returning everything to "db"s,
|
||||
|
|
|
@ -282,13 +282,14 @@ public class StubListing implements Listing {
|
|||
}
|
||||
|
||||
@Override
|
||||
public PropertyMap getPropertyMap(String propertyName) {
|
||||
public PropertyMap<?> getPropertyMap(String propertyName) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Instruction createInstruction(Address addr, InstructionPrototype prototype,
|
||||
MemBuffer memBuf, ProcessorContextView context) throws CodeUnitInsertionException {
|
||||
MemBuffer memBuf, ProcessorContextView context, int forcedLengthOverride)
|
||||
throws CodeUnitInsertionException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@ package ghidra.program.model.symbol;
|
|||
import java.util.List;
|
||||
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.listing.Function;
|
||||
import ghidra.program.model.listing.Library;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
import ghidra.util.exception.InvalidInputException;
|
||||
|
@ -167,92 +168,121 @@ public interface ExternalManager {
|
|||
* Adds a new external library name
|
||||
* @param libraryName the new external library name to add.
|
||||
* @param source the source of this external library
|
||||
* @return library
|
||||
* @throws InvalidInputException
|
||||
* @return library external {@link Library namespace}
|
||||
* @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
|
||||
*/
|
||||
public Library addExternalLibraryName(String libraryName, SourceType source)
|
||||
throws InvalidInputException, DuplicateNameException;
|
||||
|
||||
/**
|
||||
* Get or create an external location associated with an library/file named extName
|
||||
* and the label within that file specified by extLabel
|
||||
* Get or create an external location associated with a 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 libraryName the external library name
|
||||
* @param extLabel the external label
|
||||
* @param extAddr the external address
|
||||
* @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
|
||||
* @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 IllegalArgumentException if an invalid {@code extAddr} was specified.
|
||||
*/
|
||||
public ExternalLocation addExtLocation(String libraryName, String extLabel, Address extAddr,
|
||||
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 extLabel the external label
|
||||
* @param extAddr the external address
|
||||
* @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
|
||||
* @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,
|
||||
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 extLabel the external label
|
||||
* @param extAddr the external address
|
||||
* @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 reuseExisting if true, this will return an existing matching external
|
||||
* location instead of creating a new one.
|
||||
* @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,
|
||||
SourceType sourceType, boolean reuseExisting)
|
||||
throws InvalidInputException;
|
||||
|
||||
/**
|
||||
* Get or create an external location associated with an library/file named extName
|
||||
* and the label within that file specified by extLabel
|
||||
* Create an external {@link Function} in the external {@link Library} namespace
|
||||
* {@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 extLabel the external label
|
||||
* @param extAddr the external address
|
||||
* @param extLabel label within the external program, may be null if extAddr is not null
|
||||
* @param extAddr memory address within the external program, may be null
|
||||
* @param sourceType the source type of this external library's symbol
|
||||
* @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 IllegalArgumentException if an invalid {@code extAddr} was specified.
|
||||
*/
|
||||
public ExternalLocation addExtFunction(String libraryName, String extLabel, Address extAddr,
|
||||
SourceType sourceType) throws InvalidInputException, DuplicateNameException;
|
||||
|
||||
/**
|
||||
* Get or create an external function location associated with an library/file named extName
|
||||
* and the label within that file specified by extLabel
|
||||
* Create an external {@link Function} 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 extLabel the external label
|
||||
* @param extAddr the external address
|
||||
* @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
|
||||
* @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,
|
||||
SourceType sourceType) throws InvalidInputException;
|
||||
|
||||
/**
|
||||
* Get or create an external function location associated with an library/file named extName
|
||||
* and the label within that file specified by extLabel
|
||||
* Get or create an external {@link Function} 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 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 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
|
||||
* address is not null and not used in an existing 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,
|
||||
SourceType sourceType, boolean reuseExisting)
|
||||
|
|
|
@ -17,13 +17,24 @@
|
|||
|
||||
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 {
|
||||
|
||||
//
|
||||
// NOTE:
|
||||
// IMPORTANT:
|
||||
// - 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
|
||||
//
|
||||
|
@ -70,86 +81,181 @@ public abstract class RefType {
|
|||
static final byte __UNKNOWNPARAM = 107;
|
||||
|
||||
@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
|
||||
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 __UNKNOWNDATA_IND = 114;
|
||||
|
||||
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 =
|
||||
new FlowType.Builder(__INVALID, "INVALID")
|
||||
.setHasFall()
|
||||
.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 =
|
||||
new FlowType.Builder(__UNKNOWNFLOW, "FLOW")
|
||||
.setHasFall()
|
||||
.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 =
|
||||
new FlowType.Builder(__FALL_THROUGH, "FALL_THROUGH")
|
||||
.setHasFall()
|
||||
.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 =
|
||||
new FlowType.Builder(__UNCONDITIONAL_JUMP, "UNCONDITIONAL_JUMP")
|
||||
.setIsJump()
|
||||
.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 =
|
||||
new FlowType.Builder(__CONDITIONAL_JUMP, "CONDITIONAL_JUMP")
|
||||
.setHasFall()
|
||||
.setIsJump()
|
||||
.setIsConditional()
|
||||
.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 =
|
||||
new FlowType.Builder(__UNCONDITIONAL_CALL, "UNCONDITIONAL_CALL")
|
||||
.setHasFall()
|
||||
.setIsCall()
|
||||
.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 =
|
||||
new FlowType.Builder(__CONDITIONAL_CALL, "CONDITIONAL_CALL")
|
||||
.setHasFall()
|
||||
.setIsCall()
|
||||
.setIsConditional()
|
||||
.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 =
|
||||
new FlowType.Builder(__TERMINATOR, "TERMINATOR")
|
||||
.setIsTerminal()
|
||||
.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 =
|
||||
new FlowType.Builder(__COMPUTED_JUMP, "COMPUTED_JUMP")
|
||||
.setIsJump()
|
||||
.setIsComputed()
|
||||
.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 =
|
||||
new FlowType.Builder(__CONDITIONAL_TERMINATOR, "CONDITIONAL_TERMINATOR")
|
||||
.setHasFall()
|
||||
.setIsTerminal()
|
||||
.setIsConditional()
|
||||
.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 =
|
||||
new FlowType.Builder(__COMPUTED_CALL, "COMPUTED_CALL")
|
||||
.setHasFall()
|
||||
.setIsCall()
|
||||
.setIsComputed()
|
||||
.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 =
|
||||
new FlowType.Builder(__CALL_TERMINATOR, "CALL_TERMINATOR")
|
||||
.setIsCall()
|
||||
.setIsTerminal()
|
||||
.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 =
|
||||
new FlowType.Builder(__COMPUTED_CALL_TERMINATOR, "COMPUTED_CALL_TERMINATOR")
|
||||
.setIsCall()
|
||||
.setIsTerminal()
|
||||
.setIsComputed()
|
||||
.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 =
|
||||
new FlowType.Builder(__CONDITIONAL_CALL_TERMINATOR, "CONDITIONAL_CALL_TERMINATOR")
|
||||
.setIsCall()
|
||||
.setIsTerminal()
|
||||
.setIsConditional()
|
||||
.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(
|
||||
__CONDITIONAL_COMPUTED_CALL, "CONDITIONAL_COMPUTED_CALL")
|
||||
.setHasFall()
|
||||
|
@ -157,6 +263,12 @@ public abstract class RefType {
|
|||
.setIsComputed()
|
||||
.setIsConditional()
|
||||
.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 =
|
||||
new FlowType.Builder(__CONDITIONAL_COMPUTED_JUMP, "CONDITIONAL_COMPUTED_JUMP")
|
||||
.setHasFall()
|
||||
|
@ -164,31 +276,89 @@ public abstract class RefType {
|
|||
.setIsComputed()
|
||||
.setIsConditional()
|
||||
.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 =
|
||||
new FlowType.Builder(__JUMP_TERMINATOR, "JUMP_TERMINATOR")
|
||||
.setIsJump()
|
||||
.setIsTerminal()
|
||||
.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 =
|
||||
new FlowType.Builder(__INDIRECTION, "INDIRECTION")
|
||||
.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 =
|
||||
new FlowType.Builder(__CALL_OVERRIDE_UNCONDITIONAL, "CALL_OVERRIDE_UNCONDITIONAL")
|
||||
.setHasFall()
|
||||
.setIsCall()
|
||||
.setIsOverride()
|
||||
.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 =
|
||||
new FlowType.Builder(__JUMP_OVERRIDE_UNCONDITIONAL, "JUMP_OVERRIDE_UNCONDITIONAL")
|
||||
.setIsJump()
|
||||
.setIsOverride()
|
||||
.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 =
|
||||
new FlowType.Builder(__CALLOTHER_OVERRIDE_CALL, "CALLOTHER_OVERRIDE_CALL")
|
||||
.setHasFall()
|
||||
.setIsCall()
|
||||
.setIsOverride()
|
||||
.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 =
|
||||
new FlowType.Builder(__CALLOTHER_OVERRIDE_JUMP, "CALLOTHER_OVERRIDE_JUMP")
|
||||
.setIsJump()
|
||||
|
@ -196,61 +366,80 @@ public abstract class RefType {
|
|||
.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);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* {@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 =
|
||||
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);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* 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 =
|
||||
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 =
|
||||
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 =
|
||||
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",
|
||||
DataRefType.READX | DataRefType.WRITEX | DataRefType.INDX);
|
||||
|
||||
/**
|
||||
* Reference type used internally to identify external entry points.
|
||||
* The use of this RefType for references to external library data or functions
|
||||
* {@link #EXTERNAL_REF} type is used internally to identify external entry point locations
|
||||
* 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.
|
||||
*/
|
||||
public static final RefType EXTERNAL_REF = new DataRefType(__EXTERNAL_REF, "EXTERNAL", 0);
|
||||
|
@ -266,6 +455,9 @@ public abstract class RefType {
|
|||
if (this == RefType.THUNK) {
|
||||
return "Thunk";
|
||||
}
|
||||
if (this == RefType.FALL_THROUGH) {
|
||||
return "FallThrough";
|
||||
}
|
||||
|
||||
if (isRead() && isWrite()) {
|
||||
return "RW";
|
||||
|
@ -301,6 +493,37 @@ public abstract class RefType {
|
|||
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
|
||||
* @return true if the reference is to data
|
||||
|
@ -325,6 +548,18 @@ public abstract class RefType {
|
|||
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
|
||||
* @return true if the reference is indirect
|
||||
|
@ -336,14 +571,6 @@ public abstract class RefType {
|
|||
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 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ package ghidra.program.model.symbol;
|
|||
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.lang.Register;
|
||||
import ghidra.program.model.listing.Library;
|
||||
import ghidra.program.model.listing.Variable;
|
||||
import ghidra.program.model.mem.MemoryBlock;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
|
@ -69,7 +70,8 @@ public interface ReferenceManager {
|
|||
/**
|
||||
* Adds a memory reference. The first memory reference placed on
|
||||
* 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 toAddr address of the location being referenced.
|
||||
* Memory, stack, and register addresses are all permitted.
|
||||
|
@ -78,6 +80,7 @@ public interface ReferenceManager {
|
|||
* @param opIndex the operand index
|
||||
* display of the operand making this reference
|
||||
* @return new memory reference
|
||||
* @throws IllegalArgumentException if unsupported {@link RefType type} is specified
|
||||
*/
|
||||
public Reference addMemoryReference(Address fromAddr, Address toAddr, RefType type,
|
||||
SourceType source, int opIndex);
|
||||
|
@ -109,10 +112,14 @@ public interface ReferenceManager {
|
|||
* shiftValue parameter. The first memory reference placed on
|
||||
* an operand will be made primary by default. All non-memory references
|
||||
* 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
|
||||
* by the number of bits specified by shiftValue
|
||||
* @param shiftValue shifted value
|
||||
*
|
||||
* @param fromAddr source/from memory address
|
||||
* @param toAddr destination/to memory address computed as some
|
||||
* {@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 source the source of this reference
|
||||
* @param opIndex the operand index
|
||||
|
@ -122,19 +129,27 @@ public interface ReferenceManager {
|
|||
RefType type, SourceType source, int opIndex);
|
||||
|
||||
/**
|
||||
* Adds an external reference. If a reference already
|
||||
* exists for the fromAddr and opIndex, the existing reference is replaced
|
||||
* with the new reference.
|
||||
* @param fromAddr from address (source of the reference)
|
||||
* Adds an external reference to an external symbol. If a reference already
|
||||
* exists at {@code fromAddr} and {@code opIndex} the existing reference is replaced
|
||||
* with a new reference. If the external symbol cannot be found, a new {@link Library}
|
||||
* 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 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 opIndex operand index
|
||||
* @param type reference type - how the location is being referenced
|
||||
* @return new external space reference
|
||||
* @throws InvalidInputException
|
||||
* @throws DuplicateNameException
|
||||
* @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 IllegalArgumentException if an invalid {@code extAddr} was specified.
|
||||
*/
|
||||
public Reference addExternalReference(Address fromAddr, String libraryName, String extLabel,
|
||||
Address extAddr, SourceType source, int opIndex, RefType type)
|
||||
|
@ -144,7 +159,8 @@ public interface ReferenceManager {
|
|||
* Adds an external reference. If a reference already
|
||||
* exists for the fromAddr and opIndex, the existing reference is replaced
|
||||
* 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 extLabel label within the external program, may be null if extAddr is not null
|
||||
* @param extAddr address within the external program, may be null
|
||||
|
@ -152,8 +168,11 @@ public interface ReferenceManager {
|
|||
* @param opIndex operand index
|
||||
* @param type reference type - how the location is being referenced
|
||||
* @return new external space reference
|
||||
* @throws InvalidInputException
|
||||
* @throws DuplicateNameException
|
||||
* @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 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,
|
||||
Address extAddr, SourceType source, int opIndex, RefType type)
|
||||
|
@ -163,7 +182,8 @@ public interface ReferenceManager {
|
|||
* Adds an external reference. If a reference already
|
||||
* exists for the fromAddr and opIndex, the existing reference is replaced
|
||||
* 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 location external location
|
||||
* @param source the source of this reference
|
||||
|
|
|
@ -661,17 +661,22 @@ public interface ChangeManager {
|
|||
*/
|
||||
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.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
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.
|
||||
|
|
|
@ -72,10 +72,16 @@ public class InstructionUtils {
|
|||
textBuf.append(
|
||||
"\nConstructor Line #'s:\n" + getString(debug.getConstructorLineNumbers(), true))
|
||||
.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 {
|
||||
byte[] bytes = instruction.getParsedBytes();
|
||||
textBuf.append(
|
||||
"\nInstr Bytes : " + SleighDebugLogger.getFormattedBytes(instruction.getBytes()));
|
||||
"\nInstr Bytes : " + SleighDebugLogger.getFormattedBytes(bytes));
|
||||
textBuf.append("\nMask : " + debug.getFormattedInstructionMask(-1));
|
||||
textBuf.append("\nMasked Bytes: " + debug.getFormattedMaskedValue(-1)).append('\n');
|
||||
}
|
||||
|
|
|
@ -162,7 +162,7 @@ public class ClipboardPluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
new ClassicSampleX86ProgramBuilder("notepad", false, this);
|
||||
|
||||
// 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);
|
||||
|
||||
return builder.getProgram();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue