From ab31812dd4920f6ea02512436fa6606111bf5bb4 Mon Sep 17 00:00:00 2001 From: caheckman <48068198+caheckman@users.noreply.github.com> Date: Tue, 28 Jun 2022 18:02:42 -0400 Subject: [PATCH] GP-2235 Eliminate threading conflict when using context for delayslot --- .../sleigh/SleighInstructionPrototype.java | 137 +++++++++++------- .../sleigh/SleighParserContext.java | 72 +++++---- 2 files changed, 129 insertions(+), 80 deletions(-) diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/plugin/processors/sleigh/SleighInstructionPrototype.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/plugin/processors/sleigh/SleighInstructionPrototype.java index 8069421e3a..de2d3bc94a 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/plugin/processors/sleigh/SleighInstructionPrototype.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/plugin/processors/sleigh/SleighInstructionPrototype.java @@ -13,10 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/* - * Created on Feb 9, 2005 - * - */ package ghidra.app.plugin.processors.sleigh; import java.util.*; @@ -161,19 +157,22 @@ public class SleighInstructionPrototype implements InstructionPrototype { private static void addExplicitFlow(ConstructState state, OpTpl op, int flags, FlowSummary summary) { - if (summary.flowState == null) + if (summary.flowState == null) { summary.flowState = new ArrayList<>(); + } FlowRecord res = new FlowRecord(); summary.flowState.add(res); res.flowFlags = flags; res.op = op; res.addressnode = null; VarnodeTpl dest = op.getInput()[0]; // First varnode input contains the destination address - if ((flags & (JUMPOUT | CALL | CROSSBUILD)) == 0) + if ((flags & (JUMPOUT | CALL | CROSSBUILD)) == 0) { return; + } // If the flow is out of the instruction, store the ConstructState so we can easily calculate address - if (state == null) + if (state == null) { return; + } if ((flags & CROSSBUILD) != 0) { res.addressnode = state; } @@ -191,6 +190,7 @@ public class SleighInstructionPrototype implements InstructionPrototype { * Walk the pcode templates in the order they would be emitted. * Collect flowFlags FlowRecords * @param walker the pcode template walker + * @return a summary of the flow information */ public static FlowSummary walkTemplates(OpTplWalker walker) { FlowSummary res = new FlowSummary(); @@ -218,24 +218,31 @@ public class SleighInstructionPrototype implements InstructionPrototype { break; case PcodeOp.BRANCH: destType = res.lastop.getInput()[0].getOffset().getType(); - if (destType == ConstTpl.J_NEXT) + if (destType == ConstTpl.J_NEXT) { flags = BRANCH_TO_END; - else if (destType == ConstTpl.J_START) + } + else if (destType == ConstTpl.J_START) { flags = NO_FALLTHRU; - else if (destType == ConstTpl.J_RELATIVE) + } + else if (destType == ConstTpl.J_RELATIVE) { flags = NO_FALLTHRU; - else + } + else { flags = JUMPOUT | NO_FALLTHRU; + } addExplicitFlow(walker.getState(), res.lastop, flags, res); break; case PcodeOp.CBRANCH: destType = res.lastop.getInput()[0].getOffset().getType(); - if (destType == ConstTpl.J_NEXT) + if (destType == ConstTpl.J_NEXT) { flags = BRANCH_TO_END; - else if ((destType != ConstTpl.J_START) && (destType != ConstTpl.J_RELATIVE)) + } + else if ((destType != ConstTpl.J_START) && (destType != ConstTpl.J_RELATIVE)) { flags = JUMPOUT; - else + } + else { flags = 0; + } addExplicitFlow(walker.getState(), res.lastop, flags, res); break; case PcodeOp.CALL: @@ -252,8 +259,9 @@ public class SleighInstructionPrototype implements InstructionPrototype { break; case PcodeOp.INDIRECT: // Encode delayslot destType = (int) res.lastop.getInput()[0].getOffset().getReal(); - if (destType > res.delay) + if (destType > res.delay) { res.delay = destType; + } default: break; @@ -263,8 +271,9 @@ public class SleighInstructionPrototype implements InstructionPrototype { } public static FlowType flowListToFlowType(List flowstate) { - if (flowstate == null) + if (flowstate == null) { return RefType.FALL_THROUGH; + } int flags = 0; for (FlowRecord rec : flowstate) { flags &= ~(NO_FALLTHRU | CROSSBUILD | LABEL); @@ -307,8 +316,9 @@ public class SleighInstructionPrototype implements InstructionPrototype { private static FlowType convertFlowFlags(int flowFlags) { - if ((flowFlags & LABEL) != 0) + if ((flowFlags & LABEL) != 0) { flowFlags |= BRANCH_TO_END; + } flowFlags &= ~(CROSSBUILD | LABEL); // NOTE: If prototype has cross-build, flow must be determined dynamically switch (flowFlags) { // Convert flags to a standard flowtype @@ -395,8 +405,9 @@ public class SleighInstructionPrototype implements InstructionPrototype { @Override public boolean equals(Object obj) { - if (obj == null) + if (obj == null) { return false; + } return (hashCode() == obj.hashCode()); // Trust entirely in hash } @@ -415,8 +426,9 @@ public class SleighInstructionPrototype implements InstructionPrototype { @Override public FlowType getFlowType(InstructionContext context) { - if (!hasCrossBuilds) + if (!hasCrossBuilds) { return flowType; + } int flags = 0; try { flags = gatherFlags(0, context, -1); @@ -457,6 +469,7 @@ public class SleighInstructionPrototype implements InstructionPrototype { } } catch (Exception e) { + // Treat bad instructions as not part of delay slot } return delayInstrCnt; } @@ -473,8 +486,9 @@ public class SleighInstructionPrototype implements InstructionPrototype { @Override public int getOpType(int opIndex, InstructionContext context) { - if (opIndex < 0 || opIndex >= opresolve.length) + if (opIndex < 0 || opIndex >= opresolve.length) { return OperandType.DYNAMIC; + } SleighParserContext protoContext; try { @@ -485,20 +499,25 @@ public class SleighInstructionPrototype implements InstructionPrototype { } ConstructState opState = mnemonicState.getSubState(opresolve[opIndex]); FixedHandle hand = protoContext.getFixedHandle(opState); - if (hand.isInvalid()) + if (hand.isInvalid()) { return OperandType.DYNAMIC; + } int indirect = isIndirect(opresolve[opIndex]) ? OperandType.INDIRECT : 0; if (hand.offset_space == null) { // Static handle int type = hand.space.getType(); - if (type == AddressSpace.TYPE_REGISTER) + if (type == AddressSpace.TYPE_REGISTER) { return OperandType.REGISTER | indirect; - if (type == AddressSpace.TYPE_CONSTANT) + } + if (type == AddressSpace.TYPE_CONSTANT) { return OperandType.SCALAR | indirect; + } OperandSymbol sym = mnemonicState.getConstructor().getOperand(opresolve[opIndex]); - if (sym.isCodeAddress()) + if (sym.isCodeAddress()) { return (OperandType.ADDRESS | OperandType.CODE | indirect); - if (type == AddressSpace.TYPE_RAM) + } + if (type == AddressSpace.TYPE_RAM) { return (OperandType.ADDRESS | OperandType.DATA | indirect); + } } return OperandType.DYNAMIC | indirect; } @@ -530,6 +549,7 @@ public class SleighInstructionPrototype implements InstructionPrototype { return context.getAddress().addNoWrap(getFallThroughOffset(context)); } catch (AddressOverflowException e) { + // Return null if fallthru goes beyond boundary of address space } } return null; @@ -565,13 +585,16 @@ public class SleighInstructionPrototype implements InstructionPrototype { private int gatherFlags(int curflags, InstructionContext context, int secnum) throws MemoryAccessException, UnknownContextException { List curlist = null; - if (secnum < 0) + if (secnum < 0) { curlist = flowStateList; - else if ((flowStateListNamed != null) && (secnum < flowStateListNamed.size())) + } + else if ((flowStateListNamed != null) && (secnum < flowStateListNamed.size())) { curlist = flowStateListNamed.get(secnum); + } - if (curlist == null) + if (curlist == null) { return curflags; + } for (FlowRecord rec : curlist) { if ((rec.flowFlags & CROSSBUILD) != 0) { @@ -611,20 +634,23 @@ public class SleighInstructionPrototype implements InstructionPrototype { * @param res is the resulting flow Addresses * @param parsecontext is the parsing context for the current instruction * @param context is the context for the particular address so crossbuilds can be resolved - * @throws MemoryAccessException - * @throws UnknownContextException + * @throws MemoryAccessException if a memory error occurred while resolving instruction details + * @throws UnknownContextException if the gather encounters an instruction not previously parsed */ private void gatherFlows(ArrayList
res, SleighParserContext parsecontext, InstructionContext context, int secnum) throws MemoryAccessException, UnknownContextException { List curlist = null; - if (secnum < 0) + if (secnum < 0) { curlist = flowStateList; - else if ((flowStateListNamed != null) && (secnum < flowStateListNamed.size())) + } + else if ((flowStateListNamed != null) && (secnum < flowStateListNamed.size())) { curlist = flowStateListNamed.get(secnum); + } - if (curlist == null) + if (curlist == null) { return; + } for (FlowRecord rec : curlist) { if ((rec.flowFlags & CROSSBUILD) != 0) { @@ -654,8 +680,9 @@ public class SleighInstructionPrototype implements InstructionPrototype { @Override public Address[] getFlows(InstructionContext context) { - if (flowStateList.size() == 0) + if (flowStateList.size() == 0) { return emptyFlow; + } ArrayList
addresses = new ArrayList<>(); try { @@ -668,8 +695,9 @@ public class SleighInstructionPrototype implements InstructionPrototype { return emptyFlow; } - if (addresses.size() == 0) + if (addresses.size() == 0) { return emptyFlow; + } return addresses.toArray(new Address[addresses.size()]); } @@ -706,6 +734,7 @@ public class SleighInstructionPrototype implements InstructionPrototype { sym.printList(walker, list); } catch (Exception e) { + // If the instruction produces memory errors, treat as if it has no representation list } AddressSpace curSpace = context.getAddress().getAddressSpace(); @@ -730,6 +759,7 @@ public class SleighInstructionPrototype implements InstructionPrototype { return ct.getOperand(opresolve[opIndex]); } catch (Exception e) { + // Return null for an out-of bounds opIndex } return null; } @@ -746,8 +776,9 @@ public class SleighInstructionPrototype implements InstructionPrototype { protoContext = (SleighParserContext) context.getParserContext(); ConstructState opState = mnemonicState.getSubState(opresolve[opIndex]); hand = protoContext.getFixedHandle(opState); - if (hand.isInvalid()) + if (hand.isInvalid()) { return null; + } } catch (Exception e) { return null; @@ -769,8 +800,9 @@ public class SleighInstructionPrototype implements InstructionPrototype { ConstructState opState = mnemonicState.getSubState(opresolve[opIndex]); FixedHandle hand = protoContext.getFixedHandle(opState); - if (hand.isInvalid()) + if (hand.isInvalid()) { return null; + } if (hand.space.getType() == AddressSpace.TYPE_CONSTANT) { int size = hand.size; if (size == 0) { @@ -784,6 +816,7 @@ public class SleighInstructionPrototype implements InstructionPrototype { } } catch (Exception e) { + // If opIndex is out of bounds (or if there are memory errors) return a null Scalar } return null; } @@ -798,13 +831,15 @@ public class SleighInstructionPrototype implements InstructionPrototype { SleighParserContext protoContext = (SleighParserContext) context.getParserContext(); ConstructState opState = mnemonicState.getSubState(opresolve[opIndex]); FixedHandle hand = protoContext.getFixedHandle(opState); - if (hand.isInvalid()) + if (hand.isInvalid()) { return null; + } if (hand.space.getType() == AddressSpace.TYPE_REGISTER) { return language.getRegister(hand.space, hand.offset_offset, hand.size); } } catch (Exception e) { + // If opIndex is out of bounds (or if there are memory errors) return null } return null; } @@ -960,7 +995,7 @@ public class SleighInstructionPrototype implements InstructionPrototype { bytecount += len; } while (bytecount < delaySlotByteCnt); - protoContext.setDelaySlotLength(bytecount); + protoContext = new SleighParserContext(protoContext, bytecount); } ParserWalker walker = new ParserWalker(protoContext); walker.baseState(); @@ -971,7 +1006,6 @@ public class SleighInstructionPrototype implements InstructionPrototype { if (!isindelayslot) { emit.resolveFinalFallthrough(); } - protoContext.setDelaySlotLength(0); return emit.getPcodeOp(); } catch (NotYetImplementedException e) { @@ -1002,7 +1036,7 @@ public class SleighInstructionPrototype implements InstructionPrototype { bytecount += len; } while (bytecount < delaySlotByteCnt); - protoContext.setDelaySlotLength(bytecount); + protoContext = new SleighParserContext(protoContext, bytecount); } ParserWalker walker = new ParserWalker(protoContext); walker.baseState(); @@ -1022,7 +1056,6 @@ public class SleighInstructionPrototype implements InstructionPrototype { if (!isindelayslot) { emit.resolveFinalFallthrough(); } - protoContext.setDelaySlotLength(0); emit.write(PcodeEmitPacked.end_tag); // Terminate the inst_tag return emit.getPackedBytes(); } @@ -1061,6 +1094,7 @@ public class SleighInstructionPrototype implements InstructionPrototype { } } catch (Exception e) { + // If opIndex is out of bounds (or if there are memory errors) return an empty p-code list } return emptyPCode; } @@ -1342,8 +1376,9 @@ public class SleighInstructionPrototype implements InstructionPrototype { if (subct != null) { walker.setConstructor(subct); subct.applyContext(walker, debug); - if (debug != null) + if (debug != null) { debug.indent(); + } break; } if (debug != null) { @@ -1355,8 +1390,9 @@ public class SleighInstructionPrototype implements InstructionPrototype { } } else { - if (debug != null) + if (debug != null) { debug.dumpPattern(sym, walker); + } } walker.setCurrentLength(sym.getMinimumLength()); walker.popOperand(); @@ -1416,10 +1452,12 @@ public class SleighInstructionPrototype implements InstructionPrototype { ConstructTpl templ = ct.getTempl(); if (templ != null) { HandleTpl res = templ.getResult(); - if (res != null) // Pop up handle to containing operand + if (res != null) { // Pop up handle to containing operand res.fix(walker.getParentHandle(), walker); - else + } + else { walker.getParentHandle().setInvalid(); + } } walker.popOperand(); } @@ -1430,8 +1468,8 @@ public class SleighInstructionPrototype implements InstructionPrototype { * 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 - * @param debug - * @throws MemoryAccessException + * @param debug (optional) logger for collecting information about the recovered context + * @throws MemoryAccessException if memory errors occur trying to recover context */ private void reconstructContext(SleighParserContext protoContext, SleighDebugLogger debug) throws MemoryAccessException { @@ -1442,8 +1480,9 @@ public class SleighInstructionPrototype implements InstructionPrototype { if (ct != null) { int oper = walker.getOperand(); int numoper = ct.getNumOperands(); - if (oper == 0) // Upon first entry to this Constructor + if (oper == 0) { // Upon first entry to this Constructor ct.applyContext(walker, debug); // Apply its context changes + } if (oper < numoper) { walker.pushOperand(oper); continue; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/plugin/processors/sleigh/SleighParserContext.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/plugin/processors/sleigh/SleighParserContext.java index d5185c61e7..d37931e5c8 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/plugin/processors/sleigh/SleighParserContext.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/plugin/processors/sleigh/SleighParserContext.java @@ -13,10 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/* - * Created on Jan 26, 2005 - * - */ package ghidra.app.plugin.processors.sleigh; import java.util.*; @@ -36,7 +32,6 @@ import ghidra.program.model.mem.MemoryAccessException; */ public class SleighParserContext implements ParserContext { -// private WeakReference memBufferRef; private MemBuffer memBuffer; private Address addr; // Address of start of instruction private Address nextInstrAddr; // Address of next instruction @@ -44,23 +39,22 @@ public class SleighParserContext implements ParserContext { private Address destAddr; // corresponds to inst_dest for call-fixup use private SleighInstructionPrototype prototype; private AddressSpace constantSpace; - private HashMap handleMap = - new HashMap(); + private HashMap handleMap; private ArrayList contextcommit; // Pending changes to context private int[] context; // packed context bits public SleighParserContext(MemBuffer memBuf, SleighInstructionPrototype prototype, ProcessorContextView processorContext) { + this.handleMap = new HashMap<>(); this.prototype = prototype; this.constantSpace = prototype.getLanguage().getAddressFactory().getConstantSpace(); -// this.memBufferRef = new WeakReference(memBuf); this.memBuffer = memBuf; this.addr = memBuf.getAddress(); int contextSize = prototype.getContextCache().getContextSize(); context = new int[contextSize]; - contextcommit = new ArrayList(); + contextcommit = new ArrayList<>(); try { nextInstrAddr = addr.add(prototype.getLength()); } @@ -69,7 +63,6 @@ public class SleighParserContext implements ParserContext { nextInstrAddr = null; } prototype.getContextCache().getContext(processorContext, context); - } @Override @@ -95,6 +88,33 @@ public class SleighParserContext implements ParserContext { destAddr = dAddr; } + /** + * 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 + */ + public SleighParserContext(SleighParserContext origContext, int delayByteCount) { + memBuffer = origContext.memBuffer; + prototype = origContext.prototype; + context = origContext.context; + contextcommit = origContext.contextcommit; + addr = origContext.addr; + refAddr = origContext.refAddr; + destAddr = origContext.destAddr; + constantSpace = origContext.constantSpace; + handleMap = origContext.handleMap; + try { + nextInstrAddr = addr.add(prototype.getLength() + delayByteCount); + } + catch (AddressOutOfBoundsException exc) { + // no next instruction, last instruction in memory. + nextInstrAddr = null; + } + } + /** * @return context commits for normal instruction parse. */ @@ -113,13 +133,13 @@ public class SleighParserContext implements ParserContext { } public void applyCommits(ProcessorContext ctx) throws MemoryAccessException { - if (contextcommit.size() == 0) + if (contextcommit.size() == 0) { return; + } ContextCache contextCache = prototype.getContextCache(); ParserWalker walker = new ParserWalker(this); walker.baseState(); - for (int i = 0; i < contextcommit.size(); ++i) { - ContextSet set = contextcommit.get(i); + for (ContextSet set : contextcommit) { FixedHandle hand; if (set.sym instanceof OperandSymbol) { // value of OperandSymbol is already calculated, find right node int ind = ((OperandSymbol) set.sym).getIndex(); @@ -159,16 +179,6 @@ public class SleighParserContext implements ParserContext { return nextInstrAddr; } - public void setDelaySlotLength(int delayByteLength) { - try { - nextInstrAddr = addr.add(prototype.getLength() + delayByteLength); - } - catch (AddressOutOfBoundsException exc) { - // no next instruction, last instruction in memory. - nextInstrAddr = null; - } - } - public AddressSpace getCurSpace() { return addr.getAddressSpace(); } @@ -187,7 +197,7 @@ public class SleighParserContext implements ParserContext { * 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 size + * @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). */ @@ -212,8 +222,8 @@ public class SleighParserContext implements ParserContext { * (packed in big endian format). Uninitialized or * undefined memory will return zero bit values. * @param offset offset relative start of this context - * @param startbit - * @param size + * @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). */ @@ -242,9 +252,9 @@ public class SleighParserContext implements ParserContext { /** * Get bytes from context into an int - * @param bytestart + * @param bytestart is the index of the first byte to fetch * @param bytesize number of bytes (range: 1 - 4) - * @return + * @return the packed bytes from context */ public int getContextBytes(int bytestart, int bytesize) { int intstart = bytestart / 4; @@ -266,7 +276,7 @@ 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) - * @return + * @return the array of context data */ public int[] getContextBytes() { return context; @@ -274,9 +284,9 @@ public class SleighParserContext implements ParserContext { /** * Get bits from context into an int - * @param startbit + * @param startbit is the index of the first bit to fetch * @param bitsize number of bits (range: 1 - 32) - * @return + * @return the packed bits */ public int getContextBits(int startbit, int bitsize) { int intstart = startbit / 32;