diff --git a/Ghidra/Features/Decompiler/certification.manifest b/Ghidra/Features/Decompiler/certification.manifest index e3d0538c50..3ff8179a61 100644 --- a/Ghidra/Features/Decompiler/certification.manifest +++ b/Ghidra/Features/Decompiler/certification.manifest @@ -17,6 +17,8 @@ src/decompile/datatests/ccmp.xml||GHIDRA||||END| src/decompile/datatests/concat.xml||GHIDRA||||END| src/decompile/datatests/concatsplit.xml||GHIDRA||||END| src/decompile/datatests/condconst.xml||GHIDRA||||END| +src/decompile/datatests/condconst2.xml||GHIDRA||||END| +src/decompile/datatests/condconstsub.xml||GHIDRA||||END| src/decompile/datatests/condexesub.xml||GHIDRA||||END| src/decompile/datatests/condmulti.xml||GHIDRA||||END| src/decompile/datatests/convert.xml||GHIDRA||||END| diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc index 2a4b1afa77..f21c0f823b 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc @@ -4248,6 +4248,40 @@ void ActionConditionalConst::placeMultipleConstants(vector &phiNode } } +/// \brief Try to push the constant at the front point through to the output of the given PcodeOp +/// +/// If successful, create a ConstPoint to search for reads of this new constant. +/// \param points is the set of points with the current point at the front +/// \param op is p-code op to push the constant through +void ActionConditionalConst::pushConstant(list &points,PcodeOp *op) + +{ + if ((op->getEvalType() & PcodeOp::special) != 0) return; + if (op->getOpcode()->isFloatingPointOp()) return; + Varnode *outvn = op->getOut(); + if (outvn->getSize() > sizeof(uintb)) return; + Varnode *vn = points.front().vn; + int4 slot = op->getSlot(vn); + uintb in[3]; + for(int4 i=0;inumInput();++i) { + if (i == slot) + in[i] = points.front().value; + else { + Varnode *inVn = op->getIn(i); + if (inVn->getSize() > sizeof(uintb)) return; + if (inVn->isConstant()) + in[i] = op->getIn(i)->getOffset(); + else + return; // Not all inputs are constant + } + } + bool evalError; + uintb outval = op->executeSimple(in,evalError); + if (evalError) + return; + points.emplace_back(outvn,outval,points.front().constBlock,points.front().inSlot,points.front().blockIsDom); +} + /// \brief Replace MULTIEQUAL edges with constant if there is no alternate flow /// /// A given Varnode is known to be constant along a set of MULTIEQUAL edges. If these edges are excised from the @@ -4297,57 +4331,107 @@ void ActionConditionalConst::handlePhiNodes(Varnode *varVn,Varnode *constVn,vect } } -/// \brief Replace reads of a given Varnode with a constant. +/// \brief Test if we can reach the given Varnode via a path other than through the immediate edge /// -/// For each read op, check that is in or dominated by a specific block we known -/// the Varnode is constant in. -/// \param varVn is the given Varnode -/// \param constVn is the constant Varnode to replace with (may be null) -/// \param constVal is the constant value being propagated -/// \param constBlock is the block which dominates ops reading the constant value +/// The given Varnode is an input to a MULTIEQUAL through a specific input slot. If we can reach the +/// same Varnode backtracking through one of the other slots, return \b true. We can backtrack +/// through MULTIEQUALs up to a given depth and possibly a final INT_ADD. +/// \param vn is the given Varnode +/// \param op is the MULTIEQUAL reading \b vn +/// \param slot is the input index of \b vn +/// \param depth is the maximum depth to backtrack +/// \return \b true if an alternate path to the Varnode is found +bool ActionConditionalConst::testAlternatePath(Varnode *vn,PcodeOp *op,int4 slot,int4 depth) + +{ + for(int4 i=0;inumInput();++i) { + if (i == slot) continue; + Varnode *inVn = op->getIn(i); + if (inVn == vn) return true; + if (inVn->isWritten()) { + PcodeOp *curOp = inVn->getDef(); + OpCode opc = curOp->code(); + if (opc == CPUI_INT_ADD || opc == CPUI_PTRSUB || opc == CPUI_PTRADD) { + if (curOp->getIn(0) == vn || curOp->getIn(1) == vn) + return true; + } + else if (opc == CPUI_MULTIEQUAL) { + if (depth == 0) continue; + if (testAlternatePath(vn,curOp,-1,depth-1)) + return true; + } + } + } + return false; +} + +/// \brief At each ConstPoint, replace reads of the Varnode down the constant path with a constant Varnode +/// +/// Process ConstPoints from the front of the list. +/// For each read op of current point's Varnode, check if it is in the constant path. +/// If it is, replace the read with a new constant Varnode. +/// If not in the constant path, attempt to make the output Varnode into a new ConstPoint that may have +/// have reads in the constant path. +/// \param points is the list of ConstPoints /// \param useMultiequal is \b true if conditional constants can be applied to MULTIEQUAL ops /// \param data is the function being analyzed -void ActionConditionalConst::propagateConstant(Varnode *varVn,Varnode *constVn,uintb constVal, - FlowBlock *constBlock,bool useMultiequal,Funcdata &data) +void ActionConditionalConst::propagateConstant(list &points,bool useMultiequal,Funcdata &data) { vector phiNodeEdges; - list::const_iterator iter,enditer; - iter = varVn->beginDescend(); - enditer = varVn->endDescend(); - while(iter != enditer) { - PcodeOp *op = *iter; - while(iter != enditer && *iter == op) - ++iter; // Advance iterator off of current op, as this descendant may be erased - OpCode opc = op->code(); - if (opc == CPUI_INDIRECT) // Don't propagate constant into these - continue; - else if (opc == CPUI_MULTIEQUAL) { - if (!useMultiequal) + while(!points.empty()) { + ConstPoint &point(points.front()); + Varnode *varVn = point.vn; + Varnode *constVn = point.constVn; + FlowBlock *constBlock = point.constBlock; + list::const_iterator iter = varVn->beginDescend(); + list::const_iterator enditer = varVn->endDescend(); + while(iter != enditer) { + PcodeOp *op = *iter; + while(iter != enditer && *iter == op) + ++iter; // Advance iterator off of current op, as this descendant may be erased + OpCode opc = op->code(); + if (opc == CPUI_INDIRECT) // Don't propagate constant into these continue; - if (varVn->isAddrTied() && varVn->getAddr() == op->getOut()->getAddr()) - continue; - FlowBlock *bl = op->getParent(); - for(int4 slot=0;slotnumInput();++slot) { - if (op->getIn(slot) == varVn) { - if (constBlock->dominates(bl->getIn(slot))) { - phiNodeEdges.emplace_back(op,slot); + else if (opc == CPUI_MULTIEQUAL) { + if (!useMultiequal) + continue; + if (varVn->isAddrTied() && varVn->getAddr() == op->getOut()->getAddr()) + continue; + FlowBlock *bl = op->getParent(); + if (bl == constBlock) { // The immediate edge from the conditional block, coming into a MULTIEQUAL + if (op->getIn(point.inSlot) == varVn) { + // Its possible the compiler still intends the constant value to be the same variable + // Test for conditions when this is likely so we don't unnecessarily create a new variable + if (point.value > 1) continue; + if (op->getOut()->isAddrTied()) continue; + if (testAlternatePath(varVn, op, point.inSlot, 2)) continue; + phiNodeEdges.emplace_back(op,point.inSlot); } } + else if (point.blockIsDom) { + for(int4 slot=0;slotnumInput();++slot) { + if (op->getIn(slot) == varVn) { + if (constBlock->dominates(bl->getIn(slot))) { + phiNodeEdges.emplace_back(op,slot); + } + } + } + } + continue; } - continue; - } - else if (opc == CPUI_COPY) { // Don't propagate into COPY unless... - PcodeOp *followOp = op->getOut()->loneDescend(); - if (followOp == (PcodeOp *)0) continue; - if (followOp->isMarker()) continue; - if (followOp->code() == CPUI_COPY) continue; + else if (opc == CPUI_COPY) { // Don't propagate into COPY unless... + PcodeOp *followOp = op->getOut()->loneDescend(); + if (followOp == (PcodeOp *)0) continue; + if (followOp->isMarker()) continue; + if (followOp->code() == CPUI_COPY) continue; // ...unless COPY is into something more interesting - } - if (constBlock->dominates(op->getParent())) { - if (constVn == (Varnode *)0) - constVn = data.newConstant(varVn->getSize(), constVal); - if (opc == CPUI_RETURN){ + } + if (!point.blockIsDom) continue; + if (constBlock->dominates(op->getParent())) { + if (constVn == (Varnode *)0) + constVn = data.newConstant(varVn->getSize(), point.value); + if (opc == CPUI_RETURN) { // CPUI_RETURN ops can't directly take constants // as inputs PcodeOp *copyBeforeRet = data.newOp(1, op->getAddr()); @@ -4356,19 +4440,70 @@ void ActionConditionalConst::propagateConstant(Varnode *varVn,Varnode *constVn,u data.newVarnodeOut(varVn->getSize(),varVn->getAddr(),copyBeforeRet); data.opSetInput(op,copyBeforeRet->getOut(),1); data.opInsertBefore(copyBeforeRet,op); + } + else { + int4 slot = op->getSlot(varVn); + data.opSetInput(op,constVn,slot); // Replace ref with constant! + } + count += 1; // We made a change } else { - int4 slot = op->getSlot(varVn); - data.opSetInput(op,constVn,slot); // Replace ref with constant! + pushConstant(points, op); } - count += 1; // We made a change } + if (!phiNodeEdges.empty()) { + if (constVn == (Varnode *)0) + constVn = data.newConstant(varVn->getSize(), point.value); + handlePhiNodes(varVn, constVn, phiNodeEdges, data); + phiNodeEdges.clear(); + } + points.pop_front(); } - if (!phiNodeEdges.empty()) { - if (constVn == (Varnode *)0) - constVn = data.newConstant(varVn->getSize(), constVal); - handlePhiNodes(varVn, constVn, phiNodeEdges, data); +} + +/// \brief Find a Varnode being compared to a constant creating the given CBRANCH boolean +/// +/// If the boolean is created by comparing a Varnode to a constant, create a ConstPoint record +/// indicating the path down which the Varnode can be considered constant. +/// \param points will hold any new ConstPoint +/// \param boolVn is the given CBRANCH boolean +/// \param bl is the block constaining the CBRANCH +/// \param blockDom is an array of booleans indicating along which out edges a constant could be pushed +/// \param flipEdge is \b true if the meaning of the CBRANCH has been flipped +void ActionConditionalConst::findConstCompare(list &points,Varnode *boolVn,FlowBlock *bl, + bool *blockDom,bool flipEdge) +{ + if (!boolVn->isWritten()) return; + PcodeOp *compOp = boolVn->getDef(); + OpCode opc = compOp->code(); + if (opc == CPUI_BOOL_NEGATE) { + flipEdge = !flipEdge; + boolVn = compOp->getIn(0); + if (!boolVn->isWritten()) return; + compOp = boolVn->getDef(); + opc = compOp->code(); } + int4 constEdge; // Out edge where value is constant + if (opc == CPUI_INT_EQUAL) + constEdge = 1; + else if (opc == CPUI_INT_NOTEQUAL) + constEdge = 0; + else + return; + // Find the variable and verify that it is compared to a constant + Varnode *varVn = compOp->getIn(0); + Varnode *constVn = compOp->getIn(1); + if (!constVn->isConstant()) { + if (!varVn->isConstant()) + return; + Varnode *tmp = constVn; + constVn = varVn; + varVn = tmp; + } + if (varVn->loneDescend() != (PcodeOp *)0) return; + if (flipEdge) + constEdge = 1 - constEdge; + points.emplace_back(varVn,constVn,bl->getOut(constEdge),bl->getOutRevIndex(constEdge),blockDom[constEdge]); } int4 ActionConditionalConst::apply(Funcdata &data) @@ -4384,55 +4519,23 @@ int4 ActionConditionalConst::apply(Funcdata &data) useMultiequal = false; // Don't propagate into MULTIEQUAL } const BlockGraph &blockGraph(data.getBasicBlocks()); - bool blockdom[2]; + bool blockDom[2]; + list points; for(int4 i=0;ilastOp(); if (cBranch == (PcodeOp *)0 || cBranch->code() != CPUI_CBRANCH) continue; Varnode *boolVn = cBranch->getIn(1); - blockdom[0] = bl->getOut(0)->restrictedByConditional(bl); // Make sure boolean constant holds down false branch - blockdom[1] = bl->getOut(1)->restrictedByConditional(bl); - if (!blockdom[0] && !blockdom[1]) continue; + blockDom[0] = bl->getOut(0)->restrictedByConditional(bl); // Make sure boolean constant holds down false branch + blockDom[1] = bl->getOut(1)->restrictedByConditional(bl); bool flipEdge = cBranch->isBooleanFlip(); if (boolVn->loneDescend() == (PcodeOp *)0) { // If the boolean is read more than once // Search for implied constants, bool=0 down false branch, bool=1 down true branch - if (blockdom[0]) - propagateConstant(boolVn, (Varnode *)0, flipEdge ? 1 : 0, bl->getFalseOut(), useMultiequal, data); - if (blockdom[1]) - propagateConstant(boolVn, (Varnode *)0, flipEdge ? 0 : 1, bl->getTrueOut(), useMultiequal, data); + points.emplace_back(boolVn, flipEdge ? 1 : 0, bl->getFalseOut(),bl->getOutRevIndex(0),blockDom[0]); + points.emplace_back(boolVn, flipEdge ? 0 : 1, bl->getTrueOut(),bl->getOutRevIndex(1),blockDom[1]); } - if (!boolVn->isWritten()) continue; - PcodeOp *compOp = boolVn->getDef(); - OpCode opc = compOp->code(); - if (opc == CPUI_BOOL_NEGATE) { - flipEdge = !flipEdge; - boolVn = compOp->getIn(0); - if (!boolVn->isWritten()) continue; - compOp = boolVn->getDef(); - opc = compOp->code(); - } - int4 constEdge; // Out edge where value is constant - if (opc == CPUI_INT_EQUAL) - constEdge = 1; - else if (opc == CPUI_INT_NOTEQUAL) - constEdge = 0; - else - continue; - // Find the variable and verify that it is compared to a constant - Varnode *varVn = compOp->getIn(0); - Varnode *constVn = compOp->getIn(1); - if (!constVn->isConstant()) { - if (!varVn->isConstant()) - continue; - Varnode *tmp = constVn; - constVn = varVn; - varVn = tmp; - } - if (varVn->loneDescend() != (PcodeOp *)0) continue; - if (flipEdge) - constEdge = 1 - constEdge; - if (!blockdom[constEdge]) continue; // Make sure condition holds - propagateConstant(varVn,constVn,0,bl->getOut(constEdge),useMultiequal,data); + findConstCompare(points, boolVn, bl, blockDom, flipEdge); + propagateConstant(points, useMultiequal, data); } return 0; } diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.hh index 3b1a41456e..d484facb7c 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.hh @@ -567,14 +567,30 @@ public: /// \brief Propagate conditional constants class ActionConditionalConst : public Action { + /// \brief Description of a point in control-flow where a Varnode can propagate as a constant down a conditional branch + struct ConstPoint { + Varnode *vn; ///< Varnode that is constant for some reads + Varnode *constVn; ///< Representative of the constant (may be null) + uintb value; ///< The constant value + FlowBlock *constBlock; ///< Block that dominates all reads where vn is constant + int4 inSlot; ///< Input edge from condition block + bool blockIsDom; ///< Is \b true if block is dominated by constant path + ConstPoint(Varnode *v,Varnode *c,FlowBlock *bl,int4 slot,bool isDom) { + vn = v; constVn = c; value = c->getOffset(); constBlock = bl; inSlot = slot; blockIsDom = isDom; } ///< Construct from constant Varnode + ConstPoint(Varnode *v,uintb val,FlowBlock *bl,int4 slot,bool isDom) { + vn = v; constVn = (Varnode *)0; value = val; constBlock = bl; inSlot = slot; blockIsDom = isDom; } ///< Construct from constant value + }; static void clearMarks(const vector &opList); static void collectReachable(Varnode *vn,vector &phiNodeEdges,vector &reachable); static bool flowToAlternatePath(PcodeOp *op); static bool flowTogether(const vector &edges,int4 i,vector &result); static Varnode *placeCopy(PcodeOp *op,BlockBasic *bl,Varnode *constVn,Funcdata &data); + static void findConstCompare(list &points,Varnode *boolVn,FlowBlock *bl,bool *blockDom,bool flipEdge); + static void pushConstant(list &points,PcodeOp *op); static void placeMultipleConstants(vector &phiNodeEdges,vector &marks,Varnode *constVn,Funcdata &data); void handlePhiNodes(Varnode *varVn,Varnode *constVn,vector &phiNodeEdges,Funcdata &data); - void propagateConstant(Varnode *varVn,Varnode *constVn,uintb constVal,FlowBlock *constBlock,bool useMultiequal,Funcdata &data); + bool testAlternatePath(Varnode *vn,PcodeOp *op,int4 slot,int4 depth); + void propagateConstant(list &points,bool useMultiequal,Funcdata &data); public: ActionConditionalConst(const string &g) : Action(0,"condconst",g) {} ///< Constructor virtual Action *clone(const ActionGroupList &grouplist) const { diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/double.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/double.cc index b56f08658c..858b64b499 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/double.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/double.cc @@ -2169,6 +2169,7 @@ bool LessThreeWay::normalizeHi(void) } hiconstform = false; if (vnhil2->isConstant()) { + if (in.getSize() > sizeof(uintb)) return false; // Must have enough precision for constant hiconstform = true; hival = vnhil2->getOffset(); SplitVarnode::getTrueFalse(hilessbool,hiflip,hilesstrue,hilessfalse); diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/op.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/op.cc index cc7e46033f..dcf7553fd3 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/op.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/op.cc @@ -471,6 +471,32 @@ uintb PcodeOp::collapse(bool &markedInput) const { throw LowlevelError("Invalid constant collapse"); } +/// The p-code op must be \e special, or an exception is thrown. The operation is performed +/// and if there is no evaluation error, the result is returned and \b evalError is set to \b false. +/// \param in is an array of input values +/// \return the result of applying \b this operation to the input values +uintb PcodeOp::executeSimple(uintb *in,bool &evalError) const + +{ + uint4 evalType = getEvalType(); + uintb res; + try { + if (evalType == PcodeOp::unary) + res = opcode->evaluateUnary(output->getSize(),inrefs[0]->getSize(),in[0]); + else if (evalType == PcodeOp::binary) + res = opcode->evaluateBinary(output->getSize(),inrefs[0]->getSize(),in[0],in[1]); + else if (evalType == PcodeOp::ternary) + res = opcode->evaluateTernary(output->getSize(),inrefs[0]->getSize(),in[0],in[1],in[2]); + else + throw LowlevelError("Cannot perform simple execution of "+(string)get_opname(code())); + } catch(EvaluationError &err) { + evalError = true; + return 0; + } + evalError = false; + return res; +} + /// Knowing that \b this PcodeOp has collapsed its constant inputs, one of which has /// symbol content, figure out if the symbol should propagate to the new given output constant. /// \param newConst is the given output constant diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/op.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/op.hh index 3111e8c8b4..7e516fdf57 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/op.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/op.hh @@ -233,6 +233,7 @@ public: OpCode code(void) const { return opcode->getOpcode(); } ///< Get the opcode id (enum) for this op bool isCommutative(void) const { return ((flags & PcodeOp::commutative)!=0); } ///< Return \b true if inputs commute uintb collapse(bool &markedInput) const; ///< Calculate the constant output produced by this op + uintb executeSimple(uintb *in,bool &evalError) const; ///< Execute \b this operation on the given input values void collapseConstantSymbol(Varnode *newConst) const; ///< Propagate constant symbol from inputs to given output PcodeOp *nextOp(void) const; ///< Return the next op in the control-flow from this or \e null PcodeOp *previousOp(void) const; ///< Return the previous op within this op's basic block or \e null diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/opbehavior.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/opbehavior.cc index 390dbea084..ab23e94b83 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/opbehavior.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/opbehavior.cc @@ -129,7 +129,20 @@ uintb OpBehavior::evaluateBinary(int4 sizeout,int4 sizein,uintb in1,uintb in2) c string name(get_opname(opcode)); throw LowlevelError("Binary emulation unimplemented for "+name); } - + +/// \param sizeout is the size of the output in bytes +/// \param sizein is the size of the inputs in bytes +/// \param in1 is the first input value +/// \param in2 is the second input value +/// \param in3 is the third input value +/// \return the output value +uintb OpBehavior::evaluateTernary(int4 sizeout,int4 sizein,uintb in1,uintb in2,uintb in3) const + +{ + string name(get_opname(opcode)); + throw LowlevelError("Ternary emulation unimplemented for "+name); +} + /// If the output value is known, recover the input value. /// \param sizeout is the size of the output in bytes /// \param out is the output value @@ -752,6 +765,20 @@ uintb OpBehaviorSubpiece::evaluateBinary(int4 sizeout,int4 sizein,uintb in1,uint return res; } +uintb OpBehaviorPtradd::evaluateTernary(int4 sizeout,int4 sizein,uintb in1,uintb in2,uintb in3) const + +{ + uintb res = (in1 + in2 * in3) & calc_mask(sizeout); + return res; +} + +uintb OpBehaviorPtrsub::evaluateBinary(int4 sizeout,int4 sizein,uintb in1,uintb in2) const + +{ + uintb res = (in1 + in2) & calc_mask(sizeout); + return res; +} + uintb OpBehaviorPopcount::evaluateUnary(int4 sizeout,int4 sizein,uintb in1) const { diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/opbehavior.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/opbehavior.hh index 2eb9752bac..d1733c7ac2 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/opbehavior.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/opbehavior.hh @@ -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. @@ -66,7 +66,10 @@ public: /// \brief Emulate the binary op-code on input values virtual uintb evaluateBinary(int4 sizeout,int4 sizein,uintb in1,uintb in2) const; - + + /// \brief Emulate the ternary op-code on input values + virtual uintb evaluateTernary(int4 sizeout,int4 sizein,uintb in1,uintb in2,uintb in3) const; + /// \brief Reverse the binary op-code operation, recovering an input value virtual uintb recoverInputBinary(int4 slot,int4 sizeout,uintb out,int4 sizein,uintb in) const; @@ -506,6 +509,20 @@ public: virtual uintb evaluateBinary(int4 sizeout,int4 sizein,uintb in1,uintb in2) const; }; +/// CPUI_PTRADD behavior +class OpBehaviorPtradd : public OpBehavior { +public: + OpBehaviorPtradd(void) : OpBehavior(CPUI_PTRADD,false) {} ///< Constructor + virtual uintb evaluateTernary(int4 sizeout,int4 sizein,uintb in1,uintb in2,uintb in3) const; +}; + +/// CPUI_PTRSUB behavior +class OpBehaviorPtrsub : public OpBehavior { +public: + OpBehaviorPtrsub(void) : OpBehavior(CPUI_PTRSUB,false) {} ///< Constructor + virtual uintb evaluateBinary(int4 sizeout,int4 sizein,uintb in1,uintb in2) const; +}; + /// CPUI_POPCOUNT behavior class OpBehaviorPopcount : public OpBehavior { public: diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/typeop.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/typeop.cc index bb12d46769..549b10e824 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/typeop.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/typeop.cc @@ -2226,7 +2226,7 @@ TypeOpPtradd::TypeOpPtradd(TypeFactory *t) : TypeOp(t,CPUI_PTRADD,"+") { opflags = PcodeOp::ternary | PcodeOp::nocollapse; addlflags = arithmetic_op; - behave = new OpBehavior(CPUI_PTRADD,false); // Dummy behavior + behave = new OpBehaviorPtradd(); } Datatype *TypeOpPtradd::getInputLocal(const PcodeOp *op,int4 slot) const @@ -2296,7 +2296,7 @@ TypeOpPtrsub::TypeOpPtrsub(TypeFactory *t) : TypeOp(t,CPUI_PTRSUB,"->") // allow this to be commutative. opflags = PcodeOp::binary|PcodeOp::nocollapse; addlflags = arithmetic_op; - behave = new OpBehavior(CPUI_PTRSUB,false); // Dummy behavior + behave = new OpBehaviorPtrsub(); } Datatype *TypeOpPtrsub::getOutputLocal(const PcodeOp *op) const diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/typeop.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/typeop.hh index 51b20cc7e8..90ac4ed351 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/typeop.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/typeop.hh @@ -91,6 +91,17 @@ public: uintb evaluateBinary(int4 sizeout,int4 sizein,uintb in1,uintb in2) const { return behave->evaluateBinary(sizeout,sizein,in1,in2); } + /// \brief Emulate the ternary op-code on an input value + /// + /// \param sizeout is the size of the output in bytes + /// \param sizein is the size of the inputs in bytes + /// \param in1 is the first input value + /// \param in2 is the second input value + /// \param in3 is the third input value + /// \return the output value + uintb evaluateTernary(int4 sizeout,int4 sizein,uintb in1,uintb in2,uintb in3) const { + return behave->evaluateTernary(sizeout,sizein,in1,in2,in3); } + /// \brief Reverse the binary op-code operation, recovering a constant input value /// /// If the output value and one of the input values is known, recover the value diff --git a/Ghidra/Features/Decompiler/src/decompile/datatests/condconst2.xml b/Ghidra/Features/Decompiler/src/decompile/datatests/condconst2.xml new file mode 100644 index 0000000000..b065d8b81b --- /dev/null +++ b/Ghidra/Features/Decompiler/src/decompile/datatests/condconst2.xml @@ -0,0 +1,45 @@ + + + + + 00b970478160 +6120fbe7 + + + 0b4601b107330360 +7047 + + + 087a613818bf01207047 + + + + + + + +\(char\)ptrint +return cVar1; +cVar1 = '\\0'; +cVar1 = 'a'; +\*tadd = tval; +tval = tval \+ 7; +return cptr\[8\] != 'a'; +SUB + diff --git a/Ghidra/Features/Decompiler/src/decompile/datatests/condconstsub.xml b/Ghidra/Features/Decompiler/src/decompile/datatests/condconstsub.xml new file mode 100644 index 0000000000..c188de8196 --- /dev/null +++ b/Ghidra/Features/Decompiler/src/decompile/datatests/condconstsub.xml @@ -0,0 +1,23 @@ + + + + +400000b40b000014c0035fd6 + + + +return 0; +iVar1 = otherfunc\(\); +return iVar1; +\(int4\)ptr +