Merge remote-tracking branch

'origin/GP-6134_Dan_fixEmuInstNext2-take2--SQUASHED' into patch
(Closes #8646)
This commit is contained in:
Ryan Kurtz
2026-01-22 11:05:05 -05:00
7 changed files with 440 additions and 322 deletions

View File

@@ -313,6 +313,39 @@ public class BytesTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest
}
}
/**
* This tests the inst_next2 symbol
*/
@Test
public void testSK() throws Throwable {
try (ToyDBTraceBuilder tb = new ToyDBTraceBuilder("Test", "Toy:BE:64:default")) {
TraceThread thread = initTrace(tb, """
pc = 0x00400000;
sp = 0x00110000;
""",
List.of(
"imm r0, #123", // decimal
"sk",
"imm r0, #911" // decimal
));
Writer writer = createWriter(tb.host, 0);
PcodeEmulator emu = createEmulator(tb.host, writer);
PcodeThread<byte[]> emuThread = emu.newThread(thread.getPath());
emuThread.stepInstruction(); // imm 123
emuThread.stepInstruction(); // sk
try (Transaction tx = tb.startTransaction()) {
writer.writeDown(1);
}
assertEquals(BigInteger.valueOf(0x00400006),
TraceSleighUtils.evaluate("pc", tb.trace, 1, thread, 0));
assertEquals(BigInteger.valueOf(123),
TraceSleighUtils.evaluate("r0", tb.trace, 1, thread, 0));
}
}
/**
* Test the instruction decoder considers the cached state
*

View File

@@ -62,12 +62,14 @@ public class SleighInstructionPrototype implements InstructionPrototype {
static public class FlowSummary {
public int delay;
public boolean hasCrossBuilds;
public boolean hasNext2;
public ArrayList<FlowRecord> flowState;
public OpTpl lastop;
public FlowSummary() {
delay = 0;
hasCrossBuilds = false;
hasNext2 = false;
flowState = null;
lastop = null;
}
@@ -82,6 +84,7 @@ public class SleighInstructionPrototype implements InstructionPrototype {
private int delaySlotByteCnt;
private boolean hasCrossBuilds;
private boolean hasNext2;
private ArrayList<ArrayList<FlowRecord>> flowStateListNamed;
private final static PcodeOp[] emptyPCode = new PcodeOp[0];
@@ -153,6 +156,11 @@ public class SleighInstructionPrototype implements InstructionPrototype {
return hasCrossBuilds;
}
@Override
public boolean hasNext2Dependency() {
return hasNext2;
}
private static void addExplicitFlow(ConstructState state, OpTpl op, int flags,
FlowSummary summary) {
if (summary.flowState == null) {
@@ -207,14 +215,14 @@ public class SleighInstructionPrototype implements InstructionPrototype {
}
res.lastop = (OpTpl) state;
switch (res.lastop.getOpcode()) {
case PcodeOp.PTRSUB: // encoded crossbuild directive
case PcodeOp.PTRSUB -> { // encoded crossbuild directive
res.hasCrossBuilds = true;
addExplicitFlow(walker.getState(), res.lastop, CROSSBUILD, res);
break;
case PcodeOp.BRANCHIND:
}
case PcodeOp.BRANCHIND -> {
addExplicitFlow(null, res.lastop, BRANCH_INDIRECT | NO_FALLTHRU, res);
break;
case PcodeOp.BRANCH:
}
case PcodeOp.BRANCH -> {
destType = res.lastop.getInput()[0].getOffset().getType();
if (destType == ConstTpl.J_NEXT) {
flags = BRANCH_TO_END;
@@ -229,8 +237,8 @@ public class SleighInstructionPrototype implements InstructionPrototype {
flags = JUMPOUT | NO_FALLTHRU;
}
addExplicitFlow(walker.getState(), res.lastop, flags, res);
break;
case PcodeOp.CBRANCH:
}
case PcodeOp.CBRANCH -> {
destType = res.lastop.getInput()[0].getOffset().getType();
if (destType == ConstTpl.J_NEXT) {
flags = BRANCH_TO_END;
@@ -245,27 +253,32 @@ public class SleighInstructionPrototype implements InstructionPrototype {
flags = 0;
}
addExplicitFlow(walker.getState(), res.lastop, flags, res);
break;
case PcodeOp.CALL:
}
case PcodeOp.CALL -> {
addExplicitFlow(walker.getState(), res.lastop, CALL, res);
break;
case PcodeOp.CALLIND:
}
case PcodeOp.CALLIND -> {
addExplicitFlow(null, res.lastop, CALL_INDIRECT, res);
break;
case PcodeOp.RETURN:
}
case PcodeOp.RETURN -> {
addExplicitFlow(null, res.lastop, RETURN | NO_FALLTHRU, res);
break;
case PcodeOp.PTRADD: // Encoded label build directive
}
case PcodeOp.PTRADD -> { // Encoded label build directive
addExplicitFlow(null, res.lastop, LABEL, res);
break;
case PcodeOp.INDIRECT: // Encode delayslot
}
case PcodeOp.INDIRECT -> { // Encode delayslot
destType = (int) res.lastop.getInput()[0].getOffset().getReal();
if (destType > res.delay) {
res.delay = destType;
}
default:
break;
}
default -> {
}
}
for (VarnodeTpl input : res.lastop.getInput()) {
if (input.getOffset().getType() == ConstTpl.J_NEXT2) {
res.hasNext2 = true;
}
}
}
return res;
@@ -293,6 +306,7 @@ public class SleighInstructionPrototype implements InstructionPrototype {
delaySlotByteCnt = summary.delay;
hasCrossBuilds = summary.hasCrossBuilds;
hasNext2 = summary.hasNext2;
if (summary.flowState != null) {
flowStateList = summary.flowState;
flowType = flowListToFlowType(summary.flowState);
@@ -1461,11 +1475,11 @@ public class SleighInstructionPrototype implements InstructionPrototype {
}
/**
* Reconstruct the ParserContext's internal packed context array and its list of global
* Reconstruct the ParserContext's internal packed context array and its list of global
* ContextSet directives by walking a previously resolved ConstructState tree
*
* @param protoContext is the SleighParserContext containing the tree and holding the context
* results
* results
* @param debug (optional) logger for collecting information about the recovered context
* @throws MemoryAccessException if memory errors occur trying to recover context
*/

View File

@@ -19,14 +19,15 @@ import java.util.*;
import ghidra.app.plugin.processors.sleigh.symbol.OperandSymbol;
import ghidra.app.plugin.processors.sleigh.symbol.TripleSymbol;
import ghidra.app.util.PseudoInstruction;
import ghidra.program.model.address.*;
import ghidra.program.model.lang.*;
import ghidra.program.model.mem.*;
import ghidra.util.Msg;
/**
*
*
* All the recovered context for a single instruction
* <p>
* The main data structure is the tree of constructors and operands
*/
@@ -71,12 +72,13 @@ public class SleighParserContext implements ParserContext {
}
/**
* Constructor for building precompiled templates.
* NOTE: This form does not support use of {@code inst_next2}.
* @param aAddr = address to which 'inst_start' resolves
* @param nAddr = address to which 'inst_next' resolves
* @param rAddr = special address associated with original call
* @param dAddr = destination address of original call being replaced
* Constructor for building precompiled templates. NOTE: This form does not support use of
* {@code inst_next2}.
*
* @param aAddr = address to which 'inst_start' resolves
* @param nAddr = address to which 'inst_next' resolves
* @param rAddr = special address associated with original call
* @param dAddr = destination address of original call being replaced
*/
public SleighParserContext(Address aAddr, Address nAddr, Address rAddr, Address dAddr) {
memBuffer = null;
@@ -91,10 +93,11 @@ public class SleighParserContext implements ParserContext {
}
/**
* Generate context specifically for an instruction that has a delayslot.
* When generating p-code SLEIGH has an alternate interpretation of the "inst_next"
* symbol that takes into account the instruction in the delay slot. This context is
* generated at the point when specific instruction(s) in the delay slot are known.
* Generate context specifically for an instruction that has a delayslot. When generating p-code
* SLEIGH has an alternate interpretation of the "inst_next" symbol that takes into account the
* instruction in the delay slot. This context is generated at the point when specific
* instruction(s) in the delay slot are known.
*
* @param origContext is the original context (for the instruction in isolation)
* @param delayByteCount is the number of bytes in instruction stream occupied by the delay slot
*/
@@ -175,6 +178,7 @@ public class SleighParserContext implements ParserContext {
/**
* get address of current instruction
*
* @return address of current instruction
*/
public Address getAddr() {
@@ -182,9 +186,10 @@ public class SleighParserContext implements ParserContext {
}
/**
* Get address of instruction after current instruction. This may return null if this context
* Get address of instruction after current instruction. This may return null if this context
* instance does not support use of {@code inst_next} or next address falls beyond end of
* address space.
*
* @return address of next instruction or null
*/
public Address getNaddr() {
@@ -192,33 +197,31 @@ public class SleighParserContext implements ParserContext {
}
/**
* Get address of instruction after the next instruction. This may return {@link #getNaddr()}
* if this context instance does not support use of {@code inst_next2} or parse of next
* instruction fails.
* @return address of instruction after the next instruction or null
* {@return the address of the instruction after the next instruction or
* {@link Address#NO_ADDRESS}, if the next instruction cannot be parsed or if {@code inst_next2}
* is not supported in this context.}
*
* @implNote If this is returning {@link Address#NO_ADDRESS} unexpectedly in emulation, refer to
* {@link PseudoInstruction} and its logic for choosing how many bytes to cache.
*/
public Address getN2addr() {
if (next2InstAddr != null) {
return next2InstAddr;
}
next2InstAddr = computeNext2Address();
if (next2InstAddr == null) {
// unsupported use of inst_next2 or parse failure on next instruction
// returns same as inst_next
next2InstAddr = nextInstrAddr;
}
return next2InstAddr;
}
/**
* Return the address after the next instruction (inst_next2). The length of next instruction
* based on attempted parse of next instruction and does not consider any delayslot use.
* The current instructions context is used during the parse.
* Return the address after the next instruction (inst_next2). The length of next instruction
* based on attempted parse of next instruction and does not consider any delayslot use. The
* current instructions context is used during the parse.
*
* @return address after the next instruction or null if unable/failed to determine
*/
private Address computeNext2Address() {
if (memBuffer == null || nextInstrAddr == null) {
return null; // not supported without memBuffer for parse
return Address.NO_ADDRESS; // not supported without memBuffer for parse
}
try {
Address nextAddr = nextInstrAddr;
@@ -240,13 +243,27 @@ public class SleighParserContext implements ParserContext {
return nextAddr.addNoWrap(proto.getLength());
}
catch (Exception e) {
// ignore
Msg.error(this, "Could not compute inst_next2: " + e);
/**
* Unsupported use of inst_next2 or parse failure on next instruction. IMPORTANT!: This
* CANNOT fall back to inst_next. While it may be tempting, it's likely that a jump to
* inst_next2 intends to *skip* that instruction. Falling back to inst_next will
* definitely *execute* that instruction leading to VERY unexpected behavior, especially
* in the emulator.
*
* We'll return Address.NO_ADDRESS, even though that doesn't actually make it into the
* p-code. PcodeEmit#generateLocation and ConstTpl#fix work on offsets, so they re-write
* it to "ram:00000000". Still, at least that is more likely to cause a crash (the
* desired behavior) vs. stumbling along and executing an instruction that was likely
* meant to be skipped.
*/
return Address.NO_ADDRESS;
}
return null;
}
/**
* Get address space containing current instruction
*
* @return address space containing current instruction
*/
public AddressSpace getCurSpace() {
@@ -255,6 +272,7 @@ public class SleighParserContext implements ParserContext {
/**
* Get constant address space
*
* @return constant address space
*/
public AddressSpace getConstSpace() {
@@ -262,8 +280,9 @@ public class SleighParserContext implements ParserContext {
}
/**
* Get memory buffer for current instruction which may also be used to parse next instruction
* or delay slot instructions.
* Get memory buffer for current instruction which may also be used to parse next instruction or
* delay slot instructions.
*
* @return memory buffer for current instruction
*/
public MemBuffer getMemBuffer() {
@@ -271,14 +290,15 @@ public class SleighParserContext implements ParserContext {
}
/**
* Get bytes from the instruction stream into an int
* (packed in big endian format). Uninitialized or
* undefined memory will return zero byte values.
* Get bytes from the instruction stream into an int (packed in big endian format).
* Uninitialized or undefined memory will return zero byte values.
*
* @param offset offset relative start of this context
* @param bytestart pattern byte offset relative to specified context offset
* @param bytestart pattern byte offset relative to specified context offset
* @param size is the number of bytes to fetch
* @return requested byte-range value
* @throws MemoryAccessException if no bytes are available at first byte when (offset+bytestart==0).
* @throws MemoryAccessException if no bytes are available at first byte when
* (offset+bytestart==0).
*/
public int getInstructionBytes(int offset, int bytestart, int size)
throws MemoryAccessException {
@@ -297,14 +317,15 @@ public class SleighParserContext implements ParserContext {
}
/**
* Get bits from the instruction stream into an int
* (packed in big endian format). Uninitialized or
* undefined memory will return zero bit values.
* Get bits from the instruction stream into an int (packed in big endian format). Uninitialized
* or undefined memory will return zero bit values.
*
* @param offset offset relative start of this context
* @param startbit is the index of the first bit to fetch
* @param size is the number of bits to fetch
* @return requested bit-range value
* @throws MemoryAccessException if no bytes are available at first byte when (offset+bytestart/8==0).
* @throws MemoryAccessException if no bytes are available at first byte when
* (offset+bytestart/8==0).
*/
public int getInstructionBits(int offset, int startbit, int size) throws MemoryAccessException {
@@ -331,6 +352,7 @@ public class SleighParserContext implements ParserContext {
/**
* Get the processor context value as a RegisterValue
*
* @return processor context value
*/
public RegisterValue getContextRegisterValue() {
@@ -369,6 +391,7 @@ public class SleighParserContext implements ParserContext {
/**
* Get bytes from context into an int
*
* @param bytestart is the index of the first byte to fetch
* @param bytesize number of bytes (range: 1 - 4)
* @return the packed bytes from context
@@ -391,8 +414,9 @@ public class SleighParserContext implements ParserContext {
}
/**
* Get full set of context bytes. Sleigh only supports context
* which is a multiple of 4-bytes (i.e., size of int)
* Get full set of context bytes. Sleigh only supports context which is a multiple of 4-bytes
* (i.e., size of int)
*
* @return the array of context data
*/
public int[] getContextBytes() {
@@ -401,6 +425,7 @@ public class SleighParserContext implements ParserContext {
/**
* Get bits from context into an int
*
* @param startbit is the index of the first bit to fetch
* @param bitsize number of bits (range: 1 - 32)
* @return the packed bits

View File

@@ -35,11 +35,11 @@ import ghidra.util.SystemUtilities;
import ghidra.util.exception.AssertException;
/**
* Pseudo (i.e., fake) instruction that is generated by the Disassembler. This form of
* has some limitation over an instruction which is obtained from a program listing.
* The instruction will completely cache all bytes corresponding to the prototype length
* at the specified address. Additional bytes will be cached for delay-slotted instructions
* to facilitate pcode generation and obtaining general pcode related attributes.
* Pseudo (i.e., fake) instruction that is generated by the Disassembler. This form of has some
* limitation over an instruction which is obtained from a program listing. The instruction will
* completely cache all bytes corresponding to the prototype length at the specified address.
* Additional bytes will be cached for delay-slotted instructions to facilitate pcode generation and
* obtaining general pcode related attributes.
*/
public class PseudoInstruction extends PseudoCodeUnit implements Instruction, InstructionContext {
@@ -54,8 +54,9 @@ public class PseudoInstruction extends PseudoCodeUnit implements Instruction, In
private Address fallThroughOverride = null; // NO_ADDRESS indicate fall-through removed
private FlowOverride flowOverride = FlowOverride.NONE;
/**
/**
* Construct a new PseudoInstruction within a program.
*
* @param program is the given Program
* @param addr address of the instruction
* @param prototype prototype of the instruction
@@ -73,8 +74,9 @@ public class PseudoInstruction extends PseudoCodeUnit implements Instruction, In
}
}
/**
/**
* Construct a new PseudoInstruction within a program.
*
* @param addrFactory program/language address factory
* @param addr address of the instruction
* @param prototype prototype of the instruction
@@ -91,8 +93,9 @@ public class PseudoInstruction extends PseudoCodeUnit implements Instruction, In
this.addrFactory = addrFactory;
}
/**
/**
* Construct a new PseudoInstruction without a program (flow override not supported).
*
* @param addr address of the instruction
* @param prototype prototype of the instruction
* @param memBuffer buffer containing the bytes for the instruction
@@ -110,8 +113,18 @@ public class PseudoInstruction extends PseudoCodeUnit implements Instruction, In
// NOTE: in certain cases this may not cache enough if slot size was
// specified as a minimum and not actual
int length = prototype.getLength();
int delaySlotByteCount = prototype.getDelaySlotByteCount();
if (delaySlotByteCount == 1) {
int extraByteCount = prototype.getDelaySlotByteCount();
if (prototype.hasNext2Dependency()) {
/**
* NOTE: The notes about problems below apply here, too. We're assuming that the next
* instruction is at most 3 bytes longer than this instruction. Maybe that suffices, but
* be prepared to 1) Add more slack bytes, or 2) Actually parse the next instruction(s).
* The difficulty with option 2 (and why I don't just do it now) is that I need the
* initial context register value for that next instruction.
*/
extraByteCount = Math.max(length, extraByteCount);
}
if (extraByteCount == 1) {
// Assume this is a minimum size and cache enough for one
// more instruction of the same size.
length += length;
@@ -119,7 +132,7 @@ public class PseudoInstruction extends PseudoCodeUnit implements Instruction, In
else {
// NOTE: This may have a problem if delaySlotByteCount is a
// minimum byte count and more bytes are needed for delay slots.
length += delaySlotByteCount;
length += extraByteCount;
}
// Sleigh utilizes 4-byte (int) chunks when evaluating patterns
// make sure we have enough bytes to give out for any valid offset
@@ -128,8 +141,9 @@ public class PseudoInstruction extends PseudoCodeUnit implements Instruction, In
}
/**
* Return the byte value repeated for all bytes within this instruction or null
* if byte values vary.
* Return the byte value repeated for all bytes within this instruction or null if byte values
* vary.
*
* @return repeated byte value or null if bytes vary
*/
public synchronized Byte getRepeatedByte() {

View File

@@ -19,6 +19,7 @@ import java.io.IOException;
import java.util.ArrayList;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.VariableOffset;
import ghidra.program.model.mem.MemBuffer;
import ghidra.program.model.mem.MemoryAccessException;
import ghidra.program.model.pcode.*;
@@ -27,295 +28,291 @@ import ghidra.program.model.symbol.FlowType;
import ghidra.program.model.symbol.RefType;
/**
* InstructionPrototype is designed to describe one machine level instruction.
* A language parser can return the same InstructionProtoype object for the
* same type node. Prototypes for instructions will normally be fixed for a node.
* InstructionPrototype is designed to describe one machine level instruction. A language parser can
* return the same InstructionProtoype object for the same type node. Prototypes for instructions
* will normally be fixed for a node.
*/
public interface InstructionPrototype {
public static final int INVALID_DEPTH_CHANGE = 16777216; // 2^24
/** Sentinel value to indicate an invalid depth change */
public static final int INVALID_DEPTH_CHANGE = 1 << 24;
/**
* Get a new instance of a ParserContext.
* @param buf
* @param processorContext
* @return instruction ParserContext
* @throws MemoryAccessException
* {@return a new instance of an instruction {@link ParserContext}}
*
* @param buf the memory from which this prototype was parsed, or an equivalent cache
* @param processorContext the (incoming) processor context during parse
* @throws MemoryAccessException if the memory buffer cannot be accessed
*/
public ParserContext getParserContext(MemBuffer buf, ProcessorContextView processorContext)
throws MemoryAccessException;
/**
* Get a ParserContext by parsing bytes outside of the normal disassembly process
* @param addr where the ParserContext is needed
* {@return a ParserContext by parsing bytes outside of the normal disassembly process}
*
* @param address where the ParserContext is needed, i.e., the first address of an instruction
* to be parsed
* @param buffer of actual bytes
* @param processorContext
* @return
* @throws InsufficientBytesException
* @throws UnknownInstructionException
* @throws UnknownContextException
* @throws MemoryAccessException
* @param processorContext the (incoming) processor context
* @throws InsufficientBytesException if not enough bytes are in the buffer
* @throws UnknownInstructionException if the bytes do not constitute a valid instruction
* @throws UnknownContextException if contextual dependencies, e.g. {@code crossbuild}
* instructions, are not available
* @throws MemoryAccessException if the memory buffer cannot be accessed
*/
public ParserContext getPseudoParserContext(Address addr, MemBuffer buffer,
public ParserContext getPseudoParserContext(Address address, MemBuffer buffer,
ProcessorContextView processorContext) throws InsufficientBytesException,
UnknownInstructionException, UnknownContextException, MemoryAccessException;
/**
* @return true if instruction prototype expects one or more delay slotted
* instructions to exist.
* {@return true if instruction prototype expects one or more delay slotted instructions to
* exist}
*/
public boolean hasDelaySlots();
/**
* @return true if instruction semantics have a CrossBuild instruction
* dependency which may require a robust InstructionContext with access
* to preceding instructions
* {@return true if instruction semantics have a {@code crossbuild} instruction dependency which
* may require a robust {@link InstructionContext} with access to preceding instructions}
*/
public boolean hasCrossBuildDependency();
/**
* Get the mnemonic for this CodeProtype. Examples: "MOV" and
* "CALL" for instructions and "DB" and "DA" for data.
* {@return true if instruction semantics contain a reference to {@code inst_next2}}.
*/
public boolean hasNext2Dependency();
/**
* {@return the mnemonic for this prototype}
* <p>
* Examples: "{@code MOV}" and "{@code CALL}"
*
* @param context the instruction context
* @return the mnemonic for this CodePrototype.
*/
public String getMnemonic(InstructionContext context);
/**
* Get the length of this CodeProtoype.
*
* @return the length of this CodeProtoype.
* {@return the length in bytes of this prototype}
*/
public int getLength();
/**
* Get a Mask that describe which bits of this instruction determine
* the opcode.
*
* @return a Mask for the opcode bits or null if unknown.
* {@return the {@link Mask} that describe which bits of this instruction determine the opcode,
* or null if unknown}
*/
public Mask getInstructionMask();
/**
* Get a Mask that describe which bits of this instruction determine
* the operand value.
*
* @return a Mask for the operand bits or null if unknown.
* {@return the {@link Mask} that describe which bits of this instruction determine a specific
* operand's value, or null if unknown}
*
* @param operandIndex the 0-up index of the operand
*/
public Mask getOperandValueMask(int operandIndex);
/**
* Get the flow type of this instruction. Used
* for analysis purposes. i.e., how this
* instruction flows to the next instruction.
* {@return the flow type of this instruction}
* <p>
* This is used for analysis purposes. i.e., how this instruction flows to the next instruction.
*
* @param context the instruction context
* @return flow type.
*/
public FlowType getFlowType(InstructionContext context);
/**
* Get the number of delay slot instructions for this
* argument. This should be 0 for instructions which don't have a
* delay slot. This is used to support the delay slots found on
* 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 the number of delay slot instructions following this instruction}
* <p>
* This should be 0 for instructions which don't have a delay slot. This is used to support the
* delay slots found on 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.
*
* @param context the instruction context
*
* @return the number of delay slot instructions for this instruction.
*/
public int getDelaySlotDepth(InstructionContext context);
/**
* @return the number of delay-slot instruction bytes which correspond
* to this prototype.
* {@return the number of delay-slot instruction bytes which correspond to this prototype}
*/
public int getDelaySlotByteCount();
/**
* Return true if this prototype was disassembled in a delay slot.
* {@return true if this prototype was disassembled in a delay slot}
*/
boolean isInDelaySlot();
/**
* Return the number of operands in this instruction.
*
* {@return the number of operands in this instruction}
*/
public int getNumOperands();
/**
* Get the type of a specific operand.
* {@return the type of a specific operand}
*
* @param opIndex the index of the operand. (zero based)
* @param operandIndex the 0-up index of the operand
* @param context the instruction context.
* @return the type of the operand.
*/
public int getOpType(int opIndex, InstructionContext context);
public int getOpType(int operandIndex, InstructionContext context);
/**
* Get the Address for default flow after instruction.
* {@return the {@link Address} for fall-through flow after this instruction, or null if flow
* cannot fall through this instruction}
*
* @param context the instruction context
*
* @return Address of fall through flow or null if flow
* does not fall through this instruction.
*/
public Address getFallThrough(InstructionContext context);
/**
* Get the byte offset to the default flow after instruction.
* If this instruction does not have a fall-through due to flow
* behavior, this method will still return an offset which accounts for
* the instruction length including delay slotted instructions if
* applicable.
* {@return the byte offset to the fall-through flow after this instruction}
* <p>
* Ordinarily, this is just the length (in bytes) of this instruction. However, if this
* instruction has delay-slotted instruction(s), their lengths are included. Even if flow cannot
* fall through this instruction, this method will still return a the fall-through offset.
*
* @param context the instruction context
*
* @return int how much to add to the current address to get
* the fall through address.
*/
public int getFallThroughOffset(InstructionContext context);
/**
* Get an array of Address objects for all flows other than
* a fall-through, null if no flows.
* {@return the {@link Address}es for all flows other than a fall-through, or null if no flows}
* <p>
* A null return is equivalent to an empty array. Note the result may include
* {@link Address#NO_ADDRESS} to indicate flow to an address that could not be evaluated, e.g.,
* to {@code inst_next2} when the skipped instruction could not be parsed.
*
* @param context the instruction context.
* @return an array of Address objects for all flows other than
* a fall-through, null if no flows.
*/
public Address[] getFlows(InstructionContext context);
/**
* Get the separator strings between an operand.
* {@return the separator string before a specific operand, or null}
* <p>
* In particular, the separator string for operand 0 are the characters <em>before</em> the
* first operand. The separator string for {@code numOperands} are the characters <em>after</em>
* the last operand. A null return value is equivalent to an empty string.
*
* The separator string for 0 are the characters before the first operand.
* The separator string for numOperands+1 are the characters after the last operand.
*
* @param opIndex valid values are 0 thru numOperands+1
* @return separator string, or null if there is no string
* @param operandIndex valid values are 0 thru {@code numOperands}, inclusive
*/
public String getSeparator(int opIndex);
public String getSeparator(int operandIndex);
/**
* Get a List of Objects that can be used to render an operands representation.
* {@return a the objects for rendering an operand's representation}
* <p>
* Each element is one of {@link Address}, {@link Register}, {@link Scalar},
* {@link VariableOffset}, {@link Character}, or null. This method may also return null (as in
* no list at all) if the operation is not supported. Nulls should be rendered as empty strings.
*
* @param opIndex operand to get the Representation List
* @param operandIndex the 0-up index of the operand
* @param context the instruction context
*
* @return ArrayList of Register, Address, Scalar, VariableOffset and Character objects
* of null if the operation isn't supported
*/
public ArrayList<Object> getOpRepresentationList(int opIndex, InstructionContext context);
public ArrayList<Object> getOpRepresentationList(int operandIndex, InstructionContext context);
/**
* If the indicated operand is an address, this gets the address value for
* that operand
* @param opIndex index of the operand.
* {@return the {@link Address} value of a specific operand, or null if its value is not an
* {@link Address}}
*
* @param operandIndex the 0-up index of the operand
* @param context the instruction context.
* @return the address indicated by the operand
*/
public Address getAddress(int opIndex, InstructionContext context);
public Address getAddress(int operandIndex, InstructionContext context);
/**
* If the indicated operand is a scalar, this gets the scalar value for
* that operand
* @param opIndex index of the operand.
* {@return the {@link Register} value of a specific operand, or null if its value is not an
* {@link Register}}
*
* @param operandIndex the 0-up index of the operand
* @param context the instruction context
* @return the scalar for the indicated operand
*/
public Scalar getScalar(int opIndex, InstructionContext context);
public Register getRegister(int operandIndex, InstructionContext context);
/**
* If the indicated operand is a register, this gets the register value
* for that operand
* @param opIndex index of the operand.
* {@return the {@link Scalar} value of a specific operand, or null if its value is not an
* {@link Scalar}}
*
* @param operandIndex the 0-up index of the operand
* @param context the instruction context
* @return a register description for the indicated operand
*/
public Register getRegister(int opIndex, InstructionContext context);
public Scalar getScalar(int operandIndex, InstructionContext context);
/**
* Get objects used by this operand (Address, Scalar, Register ...)
* @param opIndex the index of the operand. (zero based)
* {@return the objects used by a specific operand}
* <p>
* Each element is one of {@link Address}, {@link Register}, {@link Scalar}, or
* {@link VariableOffset}.
*
* @param operandIndex the 0-up index of the operand
* @param context the instruction context
* @return an array of objects found at this operand.
*/
public Object[] getOpObjects(int opIndex, InstructionContext context);
public Object[] getOpObjects(int operandIndex, InstructionContext context);
/**
* Get the suggested operand reference type.
* @param opIndex the index of the operand. (zero based)
* {@return the suggested reference type for a specific operand}
*
* @param operandIndex the 0-up index of the operand
* @param context the instruction context
* @param override if not null, steers local overrides of pcode generation
* @return reference type.
* @param override if not null, steers local overrides of p-code generation
*/
public RefType getOperandRefType(int opIndex, InstructionContext context,
public RefType getOperandRefType(int operandIndex, InstructionContext context,
PcodeOverride override);
/**
* Return true if the operand at opIndex should have a delimiter following it.
* @param opIndex the index of the operand to test for having a delimiter.
* {@return true if a specific operand ought to have a delimiter following it}
*
* @param operandIndex the 0-up index of the operand
*/
public boolean hasDelimeter(int opIndex);
public boolean hasDelimeter(int operandIndex);
/**
* Get the Result objects produced/affected by this instruction
* These would probably only be Register or Address
* {@return the objects used as input by this instruction}
* <p>
* Each element should probably only be one of {@link Address} or {@link Register}.
*
* @param context the instruction context
*
* @return an array of objects that are used by this instruction
*/
public Object[] getInputObjects(InstructionContext context);
/**
* Get the Result objects produced/affected by this instruction
* These would probably only be Register or Address
* {@return the objects affected by this instruction}
* <p>
* Each element should probably only be one of {@link Address} or {@link Register}.
*
* @param context the instruction context
*
* @return an array of objects that are affected by this instruction
*/
public Object[] getResultObjects(InstructionContext context);
/**
* Get an array of PCode operations (micro code) that this instruction
* performs.
* {@return the p-code operations (micro code) that this instruction performs}
* <p>
* This will return an empty array if the language does not support p-code for this instruction.
*
* @param context the instruction context
* @param override if not null, may indicate that different elements of the pcode generation are overridden
* @return array of PCODE,
* zero length array if language doesn't support PCODE for this instruction
* @param override if not null, may indicate that different elements of the pcode generation are
* overridden
*/
public PcodeOp[] getPcode(InstructionContext context, PcodeOverride override);
/**
* Same as getPcode but emits the operations directly to an encoder to optimize transfer to other processes
* Does the same as {@link #getPcode(InstructionContext, PcodeOverride)} but emits the
* operations directly to an encoder to optimize transfer to other processes}
*
* @param encoder is the encoder receiving the operations
* @param context the instruction context
* @param override if not null, may indicate that different elements of the pcode generation are overridden
* @param override if not null, may indicate that different elements of the pcode generation are
* overridden
* @throws IOException for errors writing to any stream underlying the encoder
*/
public void getPcodePacked(PatchEncoder encoder, InstructionContext context,
PcodeOverride override) throws IOException;
/**
* Get an array of PCode operations (micro code) that a particular operand
* performs to compute its value.
* {@return the p-code operations (micro code) that perform the computation of a particular
* operand's value}
*
* @param context the instruction context
* @param opIndex the index of the operand for which to get PCode.
*
* @return array of PCODE,
* zero length array if language doesn't support PCODE for this instruction
* @param operandIndex the 0-up index of the operand
*/
public PcodeOp[] getPcode(InstructionContext context, int opIndex);
public PcodeOp[] getPcode(InstructionContext context, int operandIndex);
/**
* Get processor language module associated with this prototype.
* @return language module
* {@return the processor language module associated with this prototype}
*/
public Language getLanguage();
}

View File

@@ -37,6 +37,7 @@ public class InvalidPrototype implements InstructionPrototype, ParserContext {
/**
* Construct a new invalid instruction prototype.
*
* @param lang is the Language for which the invalid instruction is discovered
*/
public InvalidPrototype(Language lang) {
@@ -54,6 +55,11 @@ public class InvalidPrototype implements InstructionPrototype, ParserContext {
return false;
}
@Override
public boolean hasNext2Dependency() {
return false;
}
@Override
public Mask getInstructionMask() {
return null;

View File

@@ -4,9 +4,9 @@
* 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.
@@ -35,26 +35,29 @@ public interface Instruction extends CodeUnit, ProcessorContext {
public static final int MAX_LENGTH_OVERRIDE = 7;
/**
* @return the prototype for this instruction.
* {@return the prototype for this instruction}
*/
public InstructionPrototype getPrototype();
/**
* If operand is a pure Register, return the register.
* @param opIndex index of the operand.
* @return A register if the operand represents a register.
* If a specific operand is a pure {@link Register}, return it
*
* @param operandIndex the 0-up index of the operand.
* @return the register or null
*/
public Register getRegister(int opIndex);
public Register getRegister(int operandIndex);
/**
* Get objects used by this operand (Address, Scalar, Register ...)
* @param opIndex index of the operand.
*
* @param operandIndex index of the operand.
* @return objects used by this operand (Address, Scalar, Register ...)
*/
public Object[] getOpObjects(int opIndex);
public Object[] getOpObjects(int operandIndex);
/**
* Get the Input objects used by this instruction.
* <p>
* These could be Scalars, Registers, Addresses
*
* @return an array of objects that are used by this instruction
@@ -63,6 +66,7 @@ public interface Instruction extends CodeUnit, ProcessorContext {
/**
* Get the Result objects produced/affected by this instruction
* <p>
* These would probably only be Register or Address
*
* @return an array of objects that are affected by this instruction
@@ -72,81 +76,86 @@ public interface Instruction extends CodeUnit, ProcessorContext {
/**
* Get the operand representation for the given operand index without markup.
*
* @param opIndex operand index
*
* @param operandIndex operand index
* @return operand represented as a string.
*/
public String getDefaultOperandRepresentation(int opIndex);
public String getDefaultOperandRepresentation(int operandIndex);
/**
* Get the operand representation for the given operand index.
* A list of Register, Address, Scalar, Character and String objects is returned - without markup!
* <p>
* A list of Register, Address, Scalar, Character and String objects is returned - without
* markup!
*
* @param opIndex operand index
*
* @return ArrayList of pieces of the operand representation. Unsupported languages may return null.
* @param operandIndex operand index
* @return ArrayList of pieces of the operand representation. Unsupported languages may return
* null.
*/
public List<Object> getDefaultOperandRepresentationList(int opIndex);
public List<Object> getDefaultOperandRepresentationList(int operandIndex);
/**
* Get the separator strings between an operand.
* <p>
* The separator string for 0 are the characters before the first operand. The separator string
* for numOperands+1 are the characters after the last operand.
*
* The separator string for 0 are the characters before the first operand.
* The separator string for numOperands+1 are the characters after the last operand.
*
* @param opIndex valid values are 0 thru numOperands+1
* @param operandIndex valid values are 0 thru numOperands+1
* @return separator string, or null if there is no string
*/
public String getSeparator(int opIndex);
public String getSeparator(int operandIndex);
/**
* Get the type of a specific operand.
*
* @param opIndex the index of the operand. (zero based)
* @param operandIndex the index of the operand. (zero based)
* @return the type of the operand.
*
* @see OperandType
*/
public int getOperandType(int opIndex);
public int getOperandType(int operandIndex);
/**
* Get the operand reference type for the given operand index.
* @param index operand index
*
* @param operandIndex operand index
* @return the operand reference type for the given operand index.
*/
public RefType getOperandRefType(int index);
public RefType getOperandRefType(int operandIndex);
/**
* Get default fall-through offset in bytes from start of instruction to the
* fallthrough instruction. This accounts for any
* instructions contained with delay slots.
* @return default fall-through offset or zero (0) if instruction has no fallthrough
* Get default fall-through offset in bytes from start of instruction to the fall-through
* instruction.
* <p>
* This accounts for any instructions contained with delay slots.
*
* @return default fall-through offset or zero (0) if instruction has no fall through
*/
public int getDefaultFallThroughOffset();
/**
* Get the default fallthrough for this instruction.
* Get the default fall through for this instruction.
* <p>
* This accounts for any instructions contained with delay slots.
* @return fall-through address or null if instruction has no default fallthrough
*
* @return fall-through address or null if instruction has no default fall through
*/
public Address getDefaultFallThrough();
/**
* Get the fallthrough for this instruction, factoring in
* any fallthrough override and delay slotted instructions.
* @return fall-through address or null if instruction has no fallthrough
* Get the fall through for this instruction, factoring in any fall-through override and delay
* slotted instructions.
*
* @return fall-through address or null if instruction has no fall through
*/
public Address getFallThrough();
/**
* @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.
* {@return the {@link Address} for the instruction that fell through to this instruction}
* <p>
* This is useful for handling instructions that are found in a delay slot.
* <p>
* Note: if an instruction is in a delay slot, then it may have a branch into the delay slot,
* which is handled as follows
*
* Note: if an instruction is in a delayslot, then it may have
* a branch into the delayslot, which is handled as follows
*
* <pre>
* JMPIF Y, X
* lab:
@@ -165,187 +174,207 @@ public interface Instruction extends CodeUnit, ProcessorContext {
* JMP Y, X
* _ADD getFallFrom() = JMP
* MOV getFallFrom() = null
*</pre>
* </pre>
*/
public Address getFallFrom();
/**
* Get an array of Address objects for all flows other than
* a fall-through. This will include any flow references which
* have been added to the instruction.
* @return flow addresses or null if there are no flows
* Get an array of {@link Address}es for all flows other than a fall-through.
* <p>
* This will include any flow references which have been added to the instruction. Note the
* result may include {@link Address#NO_ADDRESS} to indicate flow to an address that could not
* be evaluated, e.g., to {@code inst_next2} when the skipped instruction could not be parsed.
*
* @return flow addresses or null if there are no flows
*/
public Address[] getFlows();
/**
* Get an array of Address objects for all default flows established
* by the underlying instruction prototype. References are ignored.
* @return flow addresses or null if there are no flows
* Get an array of {@link Address}es for all default flows established by the underlying
* instruction prototype.
* <p>
* References are ignored. Note the result may include {@link Address#NO_ADDRESS} to indicate
* flow to an address that could not be evaluated, e.g., to {@code inst_next2} when the skipped
* instruction could not be parsed.
*
* @return flow addresses or null if there are no flows
*/
public Address[] getDefaultFlows();
/**
* @return the flow type of this instruction (how this
* instruction flows to the next instruction).
* {@return the flow type of this instruction (how this instruction flows to the next
* instruction)}
*/
public FlowType getFlowType();
/**
* @return 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();
/**
* @return true if this instruction has a fall-through flow.
* {@return true if this instruction has a fall-through flow}
*/
public boolean hasFallthrough();
/**
* @return 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 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.
* Set instruction length override.
* <p>
* 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.
* @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()}.
* Get the actual number of bytes parsed when forming this instruction.
* <p>
* 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()}.
* <p>
* 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
* Get the actual bytes parsed when forming this instruction.
* <p>
* 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.
* <p>
* 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.
* Get an array of p-code operations (micro code) that this instruction performs.
* <p>
* Flow overrides are not factored into p-code.
*
* @return an array of Pcode operations,
* a zero length array if the language does not support PCode
* @return an array of p-code operations, a zero length array if the language does not support
* p-code
*/
public PcodeOp[] getPcode();
/**
* Get an array of PCode operations (micro code) that this instruction
* performs. NOTE: If includeOverrides is true, unique temporary varnodes
* may be produced which vary in size to those produced for other instructions.
* @param includeOverrides if true any flow overrides will be factored
* into generated pcode.
* @return an array of Pcode operations,
* a zero length array if the language does not support PCode
* Get an array of p-code operations (micro code) that this instruction performs.
* <p>
* NOTE: If includeOverrides is true, unique temporary varnodes may be produced which vary in
* size to those produced for other instructions.
*
* @param includeOverrides if true any flow overrides will be factored into generated p-code.
* @return an array of p-code operations, a zero length array if the language does not support
* p-code
*/
public PcodeOp[] getPcode(boolean includeOverrides);
/**
* Get an array of PCode operations (micro code) that a particular operand
* performs to compute its value.
* Get an array of p-code operations (micro code) that a particular operand performs to compute
* its value.
*
* @param opIndex index of the operand to retrieve PCode
* @param operandIndex index of the operand to retrieve p-code
*
* @return an array of PCode operations,
* a zero length array if the language does not support PCode
* @return an array of p-code operations, a zero length array if the language does not support
* p-code
*/
public PcodeOp[] getPcode(int opIndex);
public PcodeOp[] getPcode(int operandIndex);
/**
* Get the number of delay slot instructions for this
* argument. This should be 0 for instructions which don't have a
* delay slot. This is used to support the delay slots found on
* 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.
* Get the number of delay slot instructions for this argument.
* <p>
* This should be 0 for instructions which don't have a delay slot. This is used to support the
* delay slots found on 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();
/**
* @return the instruction following this one in address order or null if none found.
* {@return the instruction following this one in address order or null if none found}
*/
public Instruction getNext();
/**
* @return the instruction before this one in address order or null if none found.
* {@return the instruction before this one in address order or null if none found}
*/
public Instruction getPrevious();
/**
* Overrides the instruction's default fallthrough address to the given address.
* The given address may be null to indicate that the instruction has no fallthrough.
* @param addr the address to be used as this instructions fallthrough address. May be null.
* Override the instruction's default fall-through address to the given address.
* <p>
* The given address may be null to indicate that the instruction has no fall through.
*
* @param addr the address to be used as this instructions fall-through address. May be null.
*/
public void setFallThrough(Address addr);
/**
* Restores this instruction's fallthrough address back to the default fallthrough
* for this instruction.
*
* Restores this instruction's fall-through address back to the default fall through for this
* instruction.
*/
public void clearFallThroughOverride();
/**
* @return true if this instructions fallthrough has been overriden.
* {@return true if this instruction's fall through has been overridden}
*/
public boolean isFallThroughOverridden();
/**
* @return the instruction context for this instruction
* {@return the instruction context for this instruction}
*/
public InstructionContext getInstructionContext();
}