GP-3256 Added support for Instruction length-override

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

View file

@ -131,7 +131,8 @@ public class DebuggerCopyPlan {
}
long off = ins.getMinAddress().subtract(fromRange.getMinAddress());
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();
});
}
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -468,9 +468,6 @@ public class DBTraceCodeUnitTest extends AbstractGhidraHeadlessIntegrationTest
d4008 = b.addData(0, b.addr(0x4008), LongDataType.dataType, b.buf(1, 2, 3, 4));
}
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)));

View file

@ -220,6 +220,8 @@ src/main/help/help/topics/DbViewerPlugin/images/DatabaseViewer.png||GHIDRA||||EN
src/main/help/help/topics/DisassembledViewPlugin/DisassembledViewPlugin.htm||GHIDRA||||END|
src/main/help/help/topics/DisassembledViewPlugin/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|

View file

@ -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">&nbsp;</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.&nbsp; ARM processors have two states, ARM and Thumb
mode.&nbsp; The instructions available in ARM mode are 4 bytes long.&nbsp; In Thumb mode, the
instructions are "generally" 2 bytes long.&nbsp; 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>&nbsp;</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

View file

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

View file

@ -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()) {

View file

@ -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.
*/

View file

@ -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)

View file

@ -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) {

View file

@ -0,0 +1,137 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.plugin.core.disassembler;
import db.Transaction;
import docking.action.MenuData;
import docking.widgets.dialogs.NumberInputDialog;
import ghidra.app.context.ListingActionContext;
import ghidra.app.context.ListingContextAction;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.*;
import ghidra.program.model.util.CodeUnitInsertionException;
import ghidra.util.Msg;
class SetLengthOverrideAction extends ListingContextAction {
private DisassemblerPlugin plugin;
public SetLengthOverrideAction(DisassemblerPlugin plugin, String groupName) {
super("Modify Instruction Length", plugin.getName());
this.plugin = plugin;
setPopupMenuData(
new MenuData(new String[] { "Modify Instruction Length..." }, null, groupName));
}
@Override
public void actionPerformed(ListingActionContext context) {
PluginTool tool = plugin.getTool();
Address address = context.getAddress();
if (address == null) {
return;
}
Program program = context.getProgram();
Listing listing = program.getListing();
Instruction instr = listing.getInstructionAt(address);
if (instr == null) {
return;
}
int protoLen = instr.getPrototype().getLength();
if (protoLen == 1) {
Msg.showError(this, null, "Length Override Error",
"Length override for 1-byte instruction not allowed");
return;
}
String restoreTip = ", 0=restore";
String alignTip = "";
int align = program.getLanguage().getInstructionAlignment();
if (align != 1) {
alignTip = ", must be multiple of " + align;
}
int minLength = 0;
long maxLength = Math.min(Instruction.MAX_LENGTH_OVERRIDE, protoLen - 1);
Instruction nextInstr = listing.getInstructionAfter(address);
if (nextInstr != null &&
nextInstr.getAddress().getAddressSpace().equals(address.getAddressSpace())) {
long limit = nextInstr.getAddress().subtract(address);
maxLength = Math.min(limit, maxLength);
if (limit < instr.getParsedLength()) {
minLength = 1; // unable to restore to default length using 0 value
restoreTip = "";
}
}
if (maxLength == 1) {
// Assume we have an instruction whose length can't be changed
Msg.showError(this, null, "Length Override Error",
"Insufficient space to alter current length override of 1-byte");
return;
}
int currentLengthOverride = 0;
if (instr.isLengthOverridden()) {
currentLengthOverride = instr.getLength();
}
NumberInputDialog dialog = new NumberInputDialog("Override/Restore Instruction Length",
"Enter byte-length [" + minLength + ".." + maxLength + restoreTip + alignTip + "]",
currentLengthOverride, minLength, (int) maxLength, false);
tool.showDialog(dialog);
if (dialog.wasCancelled()) {
return;
}
String kind = "Set";
int lengthOverride = dialog.getIntValue();
if (lengthOverride == 0) {
if (!instr.isLengthOverridden()) {
return; // no change
}
kind = "Clear";
}
try (Transaction tx = instr.getProgram().openTransaction(kind + " Length Override")) {
instr.setLengthOverride(lengthOverride);
}
catch (CodeUnitInsertionException e) {
Msg.showError(this, null, "Length Override Error", e.getMessage());
}
}
@Override
public boolean isEnabledForContext(ListingActionContext context) {
Address address = context.getAddress();
if (address == null) {
return false;
}
Program program = context.getProgram();
Instruction instr = program.getListing().getInstructionAt(address);
if (instr == null) {
return false;
}
int alignment = program.getLanguage().getInstructionAlignment();
return instr.getParsedLength() > alignment;
}
}

View file

@ -91,8 +91,8 @@ public class FallThroughPlugin extends Plugin {
return instruction != null && !instruction.isFallThroughOverridden();
}
};
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);
}

View file

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

View file

@ -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) {

View file

@ -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 {

View file

@ -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 {

View file

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

View file

@ -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 =

View file

@ -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)

View file

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

View file

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

View file

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

View file

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

View file

@ -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...

View file

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

View file

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

View file

@ -0,0 +1,261 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.merge.listing;
import static org.junit.Assert.*;
import org.junit.Test;
import ghidra.program.database.OriginalProgramModifierListener;
import ghidra.program.database.ProgramDB;
import ghidra.program.model.address.AddressSet;
import ghidra.program.model.listing.Instruction;
import ghidra.program.model.listing.Listing;
import ghidra.program.model.symbol.*;
import ghidra.program.model.util.CodeUnitInsertionException;
/**
* Test the merge of the versioned program's listing.
* Provides tests that create instructions with overrides in various combinations.
* For tests with conflicts, checks selection of latest, my, or original code unit(s).
*/
public class CodeUnitMergeManager6Test extends AbstractListingMergeManagerTest {
public CodeUnitMergeManager6Test() {
super();
}
@Test
public void testAddLengthOverrideMyInstrPickMyInstr() throws Exception {
mtf.initialize("DiffTestPgm1", new OriginalProgramModifierListener() {
@Override
public void modifyOriginal(ProgramDB program) throws Exception {
int txId = program.startTransaction("Modify Original Program");
boolean commit = false;
try {
// nop #0x3 11011001 00110001 00000000
// imm r1,#0x300 00110001 00000000
// imm r0,#0x0 00000000 00000000
setBytes(program, "0x10013d9", new byte[] { (byte) 0xd9, 0x31, 0, 0, 0 });
commit = true;
}
finally {
program.endTransaction(txId, commit);
}
}
@Override
public void modifyLatest(ProgramDB program) {
int txId = program.startTransaction("Modify Latest Program");
boolean commit = false;
try {
disassemble(program, "0x10013d9", "0x10013dc");
commit = true;
}
finally {
program.endTransaction(txId, commit);
}
}
@Override
public void modifyPrivate(ProgramDB program) {
int txId = program.startTransaction("Modify My Program");
boolean commit = false;
try {
disassemble(program, "0x10013d9", "0x10013dc");
Listing listing = program.getListing();
Instruction instr = listing.getInstructionAt(addr(program, "0x10013d9"));
try {
instr.setLengthOverride(1);
}
catch (CodeUnitInsertionException e) {
failWithException("Unexpected exception", e);
}
disassemble(program, "0x10013da", "0x10013db");
instr = listing.getInstructionAt(addr(program, "0x10013da"));
assertNotNull("Failed to create overlapped instruction", instr);
commit = true;
}
finally {
program.endTransaction(txId, commit);
}
}
});
executeMerge(ASK_USER);
chooseCodeUnit("0x10013d9", "0x10013db", KEEP_MY);
waitForMergeCompletion();
assertSameCodeUnits(resultProgram, myProgram, new AddressSet(addr("0x10013d9"),
addr("0x10013db")));
ReferenceManager refMgr = resultProgram.getReferenceManager();
Reference[] refs = refMgr.getReferencesFrom(addr("0x10013d9"));
assertEquals(1, refs.length);
assertEquals(RefType.FALL_THROUGH, refs[0].getReferenceType());
assertEquals(addr("0x10013dc"), refs[0].getToAddress());
}
@Test
public void testModifyLengthOverrideMyInstrPickOriginalInstr() throws Exception {
mtf.initialize("DiffTestPgm1", new OriginalProgramModifierListener() {
@Override
public void modifyOriginal(ProgramDB program) throws Exception {
int txId = program.startTransaction("Modify Original Program");
boolean commit = false;
try {
// nop #0x3 11011001 00110001 00000000
// imm r1,#0x300 00110001 00000000
// imm r0,#0x0 00000000 00000000
setBytes(program, "0x10013d9", new byte[] { (byte) 0xd9, 0x31, 0, 0, 0 });
disassemble(program, "0x10013d9", "0x10013dc");
Listing listing = program.getListing();
Instruction instr = listing.getInstructionAt(addr(program, "0x10013d9"));
try {
instr.setLengthOverride(1);
}
catch (CodeUnitInsertionException e) {
failWithException("Unexpected exception", e);
}
commit = true;
}
finally {
program.endTransaction(txId, commit);
}
}
@Override
public void modifyLatest(ProgramDB program) {
int txId = program.startTransaction("Modify Latest Program");
boolean commit = false;
try {
disassemble(program, "0x10013da", "0x10013db");
Listing listing = program.getListing();
Instruction instr = listing.getInstructionAt(addr(program, "0x10013da"));
assertNotNull("Failed to create overlapped instruction", instr);
commit = true;
}
finally {
program.endTransaction(txId, commit);
}
}
@Override
public void modifyPrivate(ProgramDB program) {
int txId = program.startTransaction("Modify My Program");
boolean commit = false;
try {
clear(program, "0x10013d9", "0x10013d9");
disassemble(program, "0x10013d9", "0x10013dc");
commit = true;
}
finally {
program.endTransaction(txId, commit);
}
}
});
executeMerge(ASK_USER);
chooseCodeUnit("0x10013d9", "0x10013db", KEEP_ORIGINAL);
waitForMergeCompletion();
assertSameCodeUnits(resultProgram, originalProgram, new AddressSet(addr("0x10013d9"),
addr("0x10013db")));
ReferenceManager refMgr = resultProgram.getReferenceManager();
Reference[] refs = refMgr.getReferencesFrom(addr("0x10013d9"));
assertEquals(1, refs.length);
assertEquals(RefType.FALL_THROUGH, refs[0].getReferenceType());
assertEquals(addr("0x10013dc"), refs[0].getToAddress());
}
@Test
public void testAddLengthAndFallthroughOverrideMyInstrPickMyInstr() throws Exception {
mtf.initialize("DiffTestPgm1", new OriginalProgramModifierListener() {
@Override
public void modifyOriginal(ProgramDB program) throws Exception {
int txId = program.startTransaction("Modify Original Program");
boolean commit = false;
try {
// nop #0x3 11011001 00110001 00000000
// imm r1,#0x300 00110001 00000000
// imm r0,#0x0 00000000 00000000
setBytes(program, "0x10013d9", new byte[] { (byte) 0xd9, 0x31, 0, 0, 0 });
commit = true;
}
finally {
program.endTransaction(txId, commit);
}
}
@Override
public void modifyLatest(ProgramDB program) {
int txId = program.startTransaction("Modify Latest Program");
boolean commit = false;
try {
disassemble(program, "0x10013d9", "0x10013dc");
commit = true;
}
finally {
program.endTransaction(txId, commit);
}
}
@Override
public void modifyPrivate(ProgramDB program) {
int txId = program.startTransaction("Modify My Program");
boolean commit = false;
try {
disassemble(program, "0x10013d9", "0x10013dc");
Listing listing = program.getListing();
Instruction instr = listing.getInstructionAt(addr(program, "0x10013d9"));
try {
instr.setLengthOverride(1);
}
catch (CodeUnitInsertionException e) {
failWithException("Unexpected exception", e);
}
instr.setFallThrough(addr(program, "0x10013de"));
disassemble(program, "0x10013da", "0x10013db");
instr = listing.getInstructionAt(addr(program, "0x10013da"));
assertNotNull("Failed to create overlapped instruction", instr);
commit = true;
}
finally {
program.endTransaction(txId, commit);
}
}
});
executeMerge(ASK_USER);
chooseCodeUnit("0x10013d9", "0x10013db", KEEP_MY);
waitForMergeCompletion();
assertSameCodeUnits(resultProgram, myProgram, new AddressSet(addr("0x10013d9"),
addr("0x10013db")));
ReferenceManager refMgr = resultProgram.getReferenceManager();
Reference[] refs = refMgr.getReferencesFrom(addr("0x10013d9"));
assertEquals(1, refs.length);
assertEquals(RefType.FALL_THROUGH, refs[0].getReferenceType());
assertEquals(addr("0x10013de"), refs[0].getToAddress());
}
}

View file

@ -19,6 +19,7 @@ import static org.junit.Assert.*;
import org.junit.*;
import 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() {

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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) {

View file

@ -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.

View file

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

View file

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

View file

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

View file

@ -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.

View file

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

View file

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

View file

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

View file

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

View file

@ -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,

View file

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

View file

@ -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)

View file

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

View file

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

View file

@ -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.

View file

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

View file

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