diff --git a/Ghidra/Features/Decompiler/buildNatives.gradle b/Ghidra/Features/Decompiler/buildNatives.gradle index 812a2d1f14..5b7edf8af2 100644 --- a/Ghidra/Features/Decompiler/buildNatives.gradle +++ b/Ghidra/Features/Decompiler/buildNatives.gradle @@ -86,6 +86,7 @@ model { include "emulateutil.cc" include "flow.cc" include "userop.cc" + include "expression.cc" include "multiprecision.cc" include "funcdata.cc" include "funcdata_block.cc" diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/Makefile b/Ghidra/Features/Decompiler/src/decompile/cpp/Makefile index c7096715d6..ca0719fa5f 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/Makefile +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/Makefile @@ -79,8 +79,8 @@ EXTERNAL_CONSOLEEXT_NAMES=$(subst .cc,,$(notdir $(EXTERNAL_CONSOLEEXT_SOURCE))) CORE= xml marshal space float address pcoderaw translate opcodes globalcontext # Additional core files for any projects that decompile DECCORE=capability architecture options graph cover block cast typeop database cpool \ - comment stringmanage modelrules fspec action loadimage grammar varnode op \ - type variable varmap jumptable emulate emulateutil flow userop multiprecision \ + comment stringmanage modelrules fspec action loadimage grammar varnode op type \ + variable varmap jumptable emulate emulateutil flow userop expression multiprecision \ funcdata funcdata_block funcdata_op funcdata_varnode unionresolve pcodeinject \ heritage prefersplit rangeutil ruleaction subflow blockaction merge double \ transform constseq coreaction condexe override dynamic crc32 prettyprint \ diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/condexe.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/condexe.cc index 0bc8d92c8e..53274ce4ac 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/condexe.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/condexe.cc @@ -17,22 +17,6 @@ namespace ghidra { -const int4 BooleanExpressionMatch::maxDepth = 1; - -bool BooleanExpressionMatch::verifyCondition(PcodeOp *op, PcodeOp *iop) - -{ - int4 res = BooleanMatch::evaluate(op->getIn(1), iop->getIn(1), maxDepth); - if (res == BooleanMatch::uncorrelated) - return false; - matchflip = (res == BooleanMatch::complementary); - if (op->isBooleanFlip()) - matchflip = !matchflip; - if (iop->isBooleanFlip()) - matchflip = !matchflip; - return true; -} - /// \brief Calculate boolean array of all address spaces that have had a heritage pass run. /// /// Used to test if all the links out of the iblock have been calculated. diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/condexe.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/condexe.hh index 033fb90af6..0c13e6f909 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/condexe.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/condexe.hh @@ -22,19 +22,6 @@ namespace ghidra { -/// \brief A helper class for describing the similarity of the boolean condition between 2 CBRANCH operations -/// -/// This class determines if two CBRANCHs share the same condition. It also determines if the conditions -/// are complements of each other, and/or they are shared along only one path. -class BooleanExpressionMatch { - static const int4 maxDepth; ///< Maximum depth to trace a boolean expression - bool matchflip; ///< True if the compared CBRANCH keys on the opposite boolean value of the root -public: - bool verifyCondition(PcodeOp *op, PcodeOp *iop); ///< Perform the correlation test on two CBRANCH operations - int4 getMultiSlot(void) const { return -1; } ///< Get the MULTIEQUAL slot in the critical path - bool getFlip(void) const { return matchflip; } ///< Return \b true if the expressions are anti-correlated -}; - /// \brief A class for simplifying a series of conditionally executed statements. /// /// This class tries to perform transformations like the following: diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc index 27b7366756..2a4b1afa77 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc @@ -5307,66 +5307,6 @@ int4 ActionInferTypes::apply(Funcdata &data) return 0; } -/// Assuming root->getOut() is the root of an expression formed with the -/// CPUI_INT_ADD op, collect all the Varnode \e terms of the expression. -void TermOrder::collect(void) - -{ - Varnode *curvn; - PcodeOp *curop; - PcodeOp *subop,*multop; - - vector opstack; // Depth first traversal path - vector multstack; - - opstack.push_back(root); - multstack.push_back((PcodeOp *)0); - - while(!opstack.empty()) { - curop = opstack.back(); - multop = multstack.back(); - opstack.pop_back(); - multstack.pop_back(); - for(int4 i=0;inumInput();++i) { - curvn = curop->getIn(i); // curvn is a node of the subtree IF - if (!curvn->isWritten()) { // curvn is not defined by another operation - terms.push_back(AdditiveEdge(curop,i,multop)); - continue; - } - if (curvn->loneDescend() == (PcodeOp *)0) { // curvn has more then one use - terms.push_back(AdditiveEdge(curop,i,multop)); - continue; - } - subop = curvn->getDef(); - if (subop->code() != CPUI_INT_ADD) { // or if curvn is defined with some other type of op - if ((subop->code()==CPUI_INT_MULT)&&(subop->getIn(1)->isConstant())) { - PcodeOp *addop = subop->getIn(0)->getDef(); - if ((addop!=(PcodeOp *)0)&&(addop->code()==CPUI_INT_ADD)) { - if (addop->getOut()->loneDescend()!=(PcodeOp *)0) { - opstack.push_back(addop); - multstack.push_back(subop); - continue; - } - } - } - terms.push_back(AdditiveEdge(curop,i,multop)); - continue; - } - opstack.push_back(subop); - multstack.push_back(multop); - } - } -} - -void TermOrder::sortTerms(void) - -{ - for(vector::iterator iter=terms.begin();iter!=terms.end();++iter) - sorter.push_back( &(*iter) ); - - sort(sorter.begin(),sorter.end(),additiveCompare); -} - /// (Re)build the default \e root Actions: decompile, jumptable, normalize, paramid, register, firstpass void ActionDatabase::buildDefaultGroups(void) @@ -5469,6 +5409,7 @@ void ActionDatabase::universalAction(Architecture *conf) actprop->addRule( new RulePullsubIndirect("analysis")); actprop->addRule( new RulePushMulti("nodejoin")); actprop->addRule( new RuleSborrow("analysis") ); + actprop->addRule( new RuleScarry("analysis") ); actprop->addRule( new RuleIntLessEqual("analysis") ); actprop->addRule( new RuleTrivialArith("analysis") ); actprop->addRule( new RuleTrivialBool("analysis") ); diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.hh index 10a8417800..3b1a41456e 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.hh @@ -1065,47 +1065,5 @@ public: bool valid(void) const { return (op != (PcodeOp *)0); } ///< Return \b true if there are edges left to iterate }; -/// Class representing a \e term in an additive expression -class AdditiveEdge { - PcodeOp *op; ///< Lone descendant reading the term - int4 slot; ///< The input slot of the term - Varnode *vn; ///< The term Varnode - PcodeOp *mult; ///< The (optional) multiplier being applied to the term -public: - AdditiveEdge(PcodeOp *o,int4 s,PcodeOp *m) { op = o; slot = s; vn = op->getIn(slot); mult=m; } ///< Constructor - PcodeOp *getMultiplier(void) const { return mult; } ///< Get the multiplier PcodeOp - PcodeOp *getOp(void) const { return op; } ///< Get the component PcodeOp adding in the term - int4 getSlot(void) const { return slot; } ///< Get the slot reading the term - Varnode *getVarnode(void) const { return vn; } ///< Get the Varnode term -}; - -/// \brief A class for ordering Varnode terms in an additive expression. -/// -/// Given the final PcodeOp in a data-flow expression that sums 2 or more -/// Varnode \e terms, this class collects all the terms then allows -/// sorting of the terms to facilitate constant collapse and factoring simplifications. -class TermOrder { - PcodeOp *root; ///< The final PcodeOp in the expression - vector terms; ///< Collected terms - vector sorter; ///< An array of references to terms for quick sorting - static bool additiveCompare(const AdditiveEdge *op1,const AdditiveEdge *op2); -public: - TermOrder(PcodeOp *rt) { root = rt; } ///< Construct given root PcodeOp - int4 getSize(void) const { return terms.size(); } ///< Get the number of terms in the expression - void collect(void); ///< Collect all the terms in the expression - void sortTerms(void); ///< Sort the terms using additiveCompare() - const vector &getSort(void) { return sorter; } ///< Get the sorted list of references -}; - -/// \brief A comparison operator for ordering terms in a sum -/// -/// This is based on Varnode::termOrder which groups constants terms and -/// ignores multiplicative coefficients. -/// \param op1 is the first term to compare -/// \param op2 is the second term -/// \return \b true if the first term is less than the second -inline bool TermOrder::additiveCompare(const AdditiveEdge *op1,const AdditiveEdge *op2) { - return (-1 == op1->getVarnode()->termOrder(op2->getVarnode())); } - } // End namespace ghidra #endif diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/expression.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/expression.cc new file mode 100644 index 0000000000..7f22105f31 --- /dev/null +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/expression.cc @@ -0,0 +1,559 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "expression.hh" + +namespace ghidra { + +/// \brief Return \b true if the alternate path looks more valid than the main path. +/// +/// Two different paths from a common Varnode each terminate at a CALL, CALLIND, or RETURN. +/// Evaluate which path most likely represents actual parameter/return value passing, +/// based on traversal information about each path. +/// \param vn is the Varnode terminating the \e alternate path +/// \param flags indicates traversals for both paths +/// \return \b true if the alternate path is preferred +bool TraverseNode::isAlternatePathValid(const Varnode *vn,uint4 flags) + +{ + if ((flags & (indirect | indirectalt)) == indirect) + // If main path traversed an INDIRECT but the alternate did not + return true; // Main path traversed INDIRECT, alternate did not + if ((flags & (indirect | indirectalt)) == indirectalt) + return false; // Alternate path traversed INDIRECT, main did not + if ((flags & actionalt) != 0) + return true; // Alternate path traversed a dedicated COPY + if (vn->loneDescend() == (PcodeOp*)0) return false; + const PcodeOp *op = vn->getDef(); + if (op == (PcodeOp*)0) return true; + while(op->isIncidentalCopy() && op->code() == CPUI_COPY) { // Skip any incidental COPY + vn = op->getIn(0); + if (vn->loneDescend() == (PcodeOp *)0) return false; + op = vn->getDef(); + if (op == (PcodeOp *)0) return true; + } + return !op->isMarker(); // MULTIEQUAL or INDIRECT indicates multiple values +} + +/// \brief Test if two operations with same opcode produce complementary boolean values +/// +/// This only tests for cases where the opcode is INT_LESS or INT_SLESS and one of the +/// inputs is constant. +/// \param bin1op is the first p-code op to compare +/// \param bin2op is the second p-code op to compare +/// \return \b true if the two operations always produce complementary values +bool BooleanMatch::sameOpComplement(PcodeOp *bin1op,PcodeOp *bin2op) + +{ + OpCode opcode = bin1op->code(); + if ((opcode == CPUI_INT_SLESS)||(opcode==CPUI_INT_LESS)) { + // Basically we test for the scenario like: x < 9 8 < x + int4 constslot = 0; + if (bin1op->getIn(1)->isConstant()) + constslot = 1; + if (!bin1op->getIn(constslot)->isConstant()) return false; + if (!bin2op->getIn(1-constslot)->isConstant()) return false; + if (!varnodeSame(bin1op->getIn(1-constslot),bin2op->getIn(constslot))) return false; + uintb val1 = bin1op->getIn(constslot)->getOffset(); + uintb val2 = bin2op->getIn(1-constslot)->getOffset(); + if (constslot!=0) { + uintb tmp = val2; + val2 = val1; + val1 = tmp; + } + if (val1 + 1 != val2) return false; + if ((val2 == 0)&&(opcode==CPUI_INT_LESS)) return false; // Corner case for unsigned + if (opcode==CPUI_INT_SLESS) { // Corner case for signed + int4 sz = bin1op->getIn(constslot)->getSize(); + if (signbit_negative(val2,sz) && (!signbit_negative(val1,sz))) + return false; + } + return true; + } + return false; +} + +/// \brief Do the given Varnodes hold the same value, possibly as constants +/// +/// \param a is the first Varnode to compare +/// \param b is the second Varnode +/// \return \b true if the Varnodes (always) hold the same value +bool BooleanMatch::varnodeSame(Varnode *a,Varnode *b) + +{ + if (a == b) return true; + if (a->isConstant() && b->isConstant()) + return (a->getOffset() == b->getOffset()); + return false; +} + +/// \brief Determine if two boolean Varnodes hold related values +/// +/// The values may be the \e same, or opposite of each other (\e complementary). +/// Otherwise the values are \e uncorrelated. The trees constructing each Varnode +/// are examined up to a maximum \b depth. If this is exceeded \e uncorrelated is returned. +/// \param vn1 is the first boolean Varnode +/// \param vn2 is the second boolean Varnode +/// \param depth is the maximum depth to traverse in the evaluation +/// \return the correlation class +int4 BooleanMatch::evaluate(Varnode *vn1,Varnode *vn2,int4 depth) + +{ + if (vn1 == vn2) return same; + PcodeOp *op1,*op2; + OpCode opc1,opc2; + if (vn1->isWritten()) { + op1 = vn1->getDef(); + opc1 = op1->code(); + if (opc1 == CPUI_BOOL_NEGATE) { + int res = evaluate(op1->getIn(0),vn2,depth); + if (res == same) // Flip same <-> complementary result + res = complementary; + else if (res == complementary) + res = same; + return res; + } + } + else { + op1 = (PcodeOp *)0; // Don't give up before checking if op2 is BOOL_NEGATE + opc1 = CPUI_MAX; + } + if (vn2->isWritten()) { + op2 = vn2->getDef(); + opc2 = op2->code(); + if (opc2 == CPUI_BOOL_NEGATE) { + int4 res = evaluate(vn1,op2->getIn(0),depth); + if (res == same) // Flip same <-> complementary result + res = complementary; + else if (res == complementary) + res = same; + return res; + } + } + else + return uncorrelated; + if (op1 == (PcodeOp *)0) + return uncorrelated; + if (!op1->isBoolOutput() || !op2->isBoolOutput()) + return uncorrelated; + if (depth != 0 && (opc1 == CPUI_BOOL_AND || opc1 == CPUI_BOOL_OR || opc1 == CPUI_BOOL_XOR)) { + if (opc2 == CPUI_BOOL_AND || opc2 == CPUI_BOOL_OR || opc2 == CPUI_BOOL_XOR) { + if (opc1 == opc2 || (opc1 == CPUI_BOOL_AND && opc2 == CPUI_BOOL_OR) || (opc1 == CPUI_BOOL_OR && opc2 == CPUI_BOOL_AND)) { + int4 pair1 = evaluate(op1->getIn(0),op2->getIn(0),depth-1); + int4 pair2; + if (pair1 == uncorrelated) { + pair1 = evaluate(op1->getIn(0),op2->getIn(1),depth-1); // Try other possible pairing (commutative op) + if (pair1 == uncorrelated) + return uncorrelated; + pair2 = evaluate(op1->getIn(1),op2->getIn(0),depth-1); + } + else { + pair2 = evaluate(op1->getIn(1),op2->getIn(1),depth-1); + } + if (pair2 == uncorrelated) + return uncorrelated; + if (opc1 == opc2) { + if (pair1 == same && pair2 == same) + return same; + else if (opc1 == CPUI_BOOL_XOR) { + if (pair1 == complementary && pair2 == complementary) + return same; + return complementary; + } + } + else { // Must be CPUI_BOOL_AND and CPUI_BOOL_OR + if (pair1 == complementary && pair2 == complementary) + return complementary; // De Morgan's Law + } + } + } + } + else { + // Two boolean output ops, compare them directly + if (opc1 == opc2) { + bool sameOp = true; + int numInputs = op1->numInput(); + for (int i = 0; i < numInputs; ++i){ + if (!varnodeSame(op1->getIn(i),op2->getIn(i))){ + sameOp = false; + break; + } + } + if (sameOp){ + return same; + } + if (sameOpComplement(op1,op2)) { + return complementary; + } + return uncorrelated; + } + // Check if the binary ops are complements of one another + int4 slot1 = 0; + int4 slot2 = 0; + bool reorder; + if (opc1 != get_booleanflip(opc2,reorder)) + return uncorrelated; + if (reorder) slot2 = 1; + if (!varnodeSame(op1->getIn(slot1),op2->getIn(slot2))) + return uncorrelated; + if (!varnodeSame(op1->getIn(1-slot1),op2->getIn(1-slot2))) + return uncorrelated; + return complementary; + } + return uncorrelated; +} + +const int4 BooleanExpressionMatch::maxDepth = 1; + +bool BooleanExpressionMatch::verifyCondition(PcodeOp *op, PcodeOp *iop) + +{ + int4 res = BooleanMatch::evaluate(op->getIn(1), iop->getIn(1), maxDepth); + if (res == BooleanMatch::uncorrelated) + return false; + matchflip = (res == BooleanMatch::complementary); + if (op->isBooleanFlip()) + matchflip = !matchflip; + if (iop->isBooleanFlip()) + matchflip = !matchflip; + return true; +} + +/// Assuming root->getOut() is the root of an expression formed with the +/// CPUI_INT_ADD op, collect all the Varnode \e terms of the expression. +void TermOrder::collect(void) + +{ + Varnode *curvn; + PcodeOp *curop; + PcodeOp *subop,*multop; + + vector opstack; // Depth first traversal path + vector multstack; + + opstack.push_back(root); + multstack.push_back((PcodeOp *)0); + + while(!opstack.empty()) { + curop = opstack.back(); + multop = multstack.back(); + opstack.pop_back(); + multstack.pop_back(); + for(int4 i=0;inumInput();++i) { + curvn = curop->getIn(i); // curvn is a node of the subtree IF + if (!curvn->isWritten()) { // curvn is not defined by another operation + terms.emplace_back(curop,i,multop); + continue; + } + if (curvn->loneDescend() == (PcodeOp *)0) { // curvn has more then one use + terms.emplace_back(curop,i,multop); + continue; + } + subop = curvn->getDef(); + if (subop->code() != CPUI_INT_ADD) { // or if curvn is defined with some other type of op + if ((subop->code()==CPUI_INT_MULT)&&(subop->getIn(1)->isConstant())) { + PcodeOp *addop = subop->getIn(0)->getDef(); + if ((addop!=(PcodeOp *)0)&&(addop->code()==CPUI_INT_ADD)) { + if (addop->getOut()->loneDescend()!=(PcodeOp *)0) { + opstack.push_back(addop); + multstack.push_back(subop); + continue; + } + } + } + terms.emplace_back(curop,i,multop); + continue; + } + opstack.push_back(subop); + multstack.push_back(multop); + } + } +} + +void TermOrder::sortTerms(void) + +{ + sorter.reserve(terms.size()); + for(vector::iterator iter=terms.begin();iter!=terms.end();++iter) + sorter.push_back( &(*iter) ); + + sort(sorter.begin(),sorter.end(),additiveCompare); +} + +/// The value \b true is returned if it can be proven that two terms add the same value to their +/// respective expressions. +/// \param op2 is the other term to compare with \b this +/// \return \b true if the terms are equivalent +bool AddExpression::Term::isEquivalent(const Term &op2) const + +{ + if (coeff != op2.coeff) return false; + return functionalEquality(vn,op2.vn); +} + +/// The value \b true is returned if it can be proven that the expressions always produce the same value. +/// \param op2 is the other expression to compare with \b this +/// \return \b true if the expressions are equivalent +bool AddExpression::isEquivalent(const AddExpression &op2) const + +{ + if (constval != op2.constval) + return false; + if (numTerms != op2.numTerms) return false; + if (numTerms == 1) { + if (terms[0].isEquivalent(op2.terms[0])) + return true; + } + else if (numTerms == 2) { + if (terms[0].isEquivalent(op2.terms[0]) && terms[1].isEquivalent(op2.terms[1])) + return true; + if (terms[0].isEquivalent(op2.terms[1]) && terms[1].isEquivalent(op2.terms[0])) + return true; + } + return false; +} + +/// Recursively collect terms, up to the given depth. INT_ADD either contributes to the constant sum, or +/// it is recursively walked. Term coefficients are collected from INT_MULT with a constant. +/// \param vn is the root of the (sub)expression +/// \param coeff is the current multiplicative coefficient of the subexpression +/// \param depth is the current depth +void AddExpression::gather(Varnode *vn,uintb coeff,int4 depth) + +{ + if (vn->isConstant()) { + constval = constval + coeff * vn->getOffset(); + constval &= calc_mask(vn->getSize()); + return; + } + if (vn->isWritten()) { + PcodeOp *op = vn->getDef(); + if (op->code() == CPUI_INT_ADD) { + if (!op->getIn(1)->isConstant()) + depth -= 1; + if (depth >= 0) { + gather(op->getIn(0),coeff,depth); + gather(op->getIn(1),coeff,depth); + return; + } + } + else if (op->code() == CPUI_INT_MULT) { + if (op->getIn(1)->isConstant()) { + coeff = coeff * op->getIn(1)->getOffset(); + coeff &= calc_mask(vn->getSize()); + gather(op->getIn(0),coeff,depth); + return; + } + } + } + add(vn,coeff); + return; +} + +/// Gather up to two non-constant additive terms, given two root Varnodes that are being subtracted. +/// \param a is the first root +/// \param b is the second root being subtracted from the first +void AddExpression::gatherTwoTermsSubtract(Varnode *a,Varnode *b) + +{ + int4 depth = (a->isConstant() || b->isConstant()) ? 1 : 0; + gather(a,(uintb)1,depth); + gather(b,calc_mask(b->getSize()),depth); +} + +/// Gather up to two non-constant additive terms, given two root Varnodes being added +/// \param a is the first root +/// \param b is the second root being added to the first +void AddExpression::gatherTwoTermsAdd(Varnode *a,Varnode *b) + +{ + int4 depth = (a->isConstant() || b->isConstant()) ? 1 : 0; + gather(a,(uintb)1,depth); + gather(b,(uintb)1,depth); +} + +/// Gather up to two non-constant additive terms in the expression at the given root. +/// \param root is the root Varnode +void AddExpression::gatherTwoTermsRoot(Varnode *root) + +{ + gather(root,(uintb)1,1); +} + +/// \brief Perform basic comparison of two given Varnodes +/// +/// Return +/// - 0 if \b vn1 and \b vn2 must hold same value +/// - -1 if they definitely don't hold same value +/// - 1 if the same value depends on ops writing to \b vn1 and \b vn2 +/// \param vn1 is the first Varnode to compare +/// \param vn2 is the second +/// \return a code -1, 0, or 1 +static int4 functionalEqualityLevel0(Varnode *vn1,Varnode *vn2) + +{ + if (vn1==vn2) return 0; + if (vn1->getSize() != vn2->getSize()) return -1; + if (vn1->isConstant()) { + if (vn2->isConstant()) { + return (vn1->getOffset() == vn2->getOffset()) ? 0 : -1; + } + return -1; + } + if (vn2->isConstant()) return -1; + if (vn1->isWritten() && vn2->isWritten()) return 1; + return -1; +} + +/// \brief Try to determine if \b vn1 and \b vn2 contain the same value +/// +/// Return: +/// - -1, if they do \b not, or if it can't be immediately verified +/// - 0, if they \b do hold the same value +/// - >0, if the result is contingent on additional varnode pairs having the same value +/// In the last case, the varnode pairs are returned as (res1[i],res2[i]), +/// where the return value is the number of pairs. +/// \param vn1 is the first Varnode to compare +/// \param vn2 is the second Varnode +/// \param res1 is a reference to the first returned Varnode +/// \param res2 is a reference to the second returned Varnode +/// \return the result of the comparison +int4 functionalEqualityLevel(Varnode *vn1,Varnode *vn2,Varnode **res1,Varnode **res2) + +{ + int4 testval = functionalEqualityLevel0(vn1,vn2); + if (testval != 1) return testval; + PcodeOp *op1 = vn1->getDef(); + PcodeOp *op2 = vn2->getDef(); + OpCode opc = op1->code(); + + if (opc != op2->code()) return -1; + + int4 num = op1->numInput(); + if (num != op2->numInput()) return -1; + if (op1->isMarker()) return -1; + if (op2->isCall()) return -1; + if (opc == CPUI_LOAD) { + // FIXME: We assume two loads produce the same + // result if the address is the same and the loads + // occur in the same instruction + if (op1->getAddr() != op2->getAddr()) return -1; + } + if (num >= 3) { + if (opc != CPUI_PTRADD) return -1; // If this is a PTRADD + if (op1->getIn(2)->getOffset() != op2->getIn(2)->getOffset()) return -1; // Make sure the elsize constant is equal + num = 2; // Otherwise treat as having 2 inputs + } + for(int4 i=0;igetIn(i); + res2[i] = op2->getIn(i); + } + + testval = functionalEqualityLevel0(res1[0],res2[0]); + if (testval == 0) { // A match locks in this comparison ordering + if (num==1) return 0; + testval = functionalEqualityLevel0(res1[1],res2[1]); + if (testval==0) return 0; + if (testval < 0) return -1; + res1[0] = res1[1]; // Match is contingent on second pair + res2[0] = res2[1]; + return 1; + } + if (num == 1) return testval; + int4 testval2 = functionalEqualityLevel0(res1[1],res2[1]); + if (testval2 == 0) { // A match locks in this comparison ordering + return testval; + } + int4 unmatchsize; + if ((testval==1)&&(testval2==1)) + unmatchsize = 2; + else + unmatchsize = -1; + + if (!op1->isCommutative()) return unmatchsize; + // unmatchsize must be 2 or -1 here on a commutative operator, + // try flipping + int4 comm1 = functionalEqualityLevel0(res1[0],res2[1]); + int4 comm2 = functionalEqualityLevel0(res1[1],res2[0]); + if ((comm1==0) && (comm2==0)) + return 0; + if ((comm1<0)||(comm2<0)) + return unmatchsize; + if (comm1==0) { // AND (comm2==1) + res1[0] = res1[1]; // Left over unmatch is res1[1] and res2[0] + return 1; + } + if (comm2==0) { // AND (comm1==1) + res2[0] = res2[1]; // Left over unmatch is res1[0] and res2[1] + return 1; + } + // If we reach here (comm1==1) AND (comm2==1) + if (unmatchsize == 2) // If the original ordering wasn't impossible + return 2; // Prefer the original ordering + Varnode *tmpvn = res2[0]; // Otherwise swap the ordering + res2[0] = res2[1]; + res2[1] = tmpvn; + return 2; +} + +/// \brief Determine if two Varnodes hold the same value +/// +/// Only return \b true if it can be immediately determined they are equivalent +/// \param vn1 is the first Varnode +/// \param vn2 is the second Varnode +/// \return true if they are provably equal +bool functionalEquality(Varnode *vn1,Varnode *vn2) + +{ + Varnode *buf1[2]; + Varnode *buf2[2]; + return (functionalEqualityLevel(vn1,vn2,buf1,buf2)==0); +} + +/// \brief Return true if vn1 and vn2 are verifiably different values +/// +/// This is actually a rather speculative test +/// \param vn1 is the first Varnode to compare +/// \param vn2 is the second Varnode +/// \param depth is the maximum level to recurse while testing +/// \return \b true if they are different +bool functionalDifference(Varnode *vn1,Varnode *vn2,int4 depth) + +{ + PcodeOp *op1,*op2; + int4 i,num; + + if (vn1 == vn2) return false; + if ((!vn1->isWritten())||(!vn2->isWritten())) { + if (vn1->isConstant() && vn2->isConstant()) + return !(vn1->getAddr()==vn2->getAddr()); + if (vn1->isInput()&&vn2->isInput()) return false; // Might be the same + if (vn1->isFree()||vn2->isFree()) return false; // Might be the same + return true; + } + op1 = vn1->getDef(); + op2 = vn2->getDef(); + if (op1->code() != op2->code()) return true; + num = op1->numInput(); + if (num != op2->numInput()) return true; + if (depth==0) return true; // Different as far as we can tell + depth -= 1; + for(i=0;igetIn(i),op2->getIn(i),depth)) + return true; + return false; +} + +} // End namespace ghidra diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/expression.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/expression.hh new file mode 100644 index 0000000000..fedf4d8b8a --- /dev/null +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/expression.hh @@ -0,0 +1,180 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/// \file expression.hh +/// \brief Classes to collect, analyze, and match expressions within p-code data-flow +#ifndef __EXPRESSION_HH__ +#define __EXPRESSION_HH__ + +#include "op.hh" + +namespace ghidra { + +/// \brief An edge in a data-flow path or graph +/// +/// A minimal node for traversing expressions in the data-flow +struct PcodeOpNode { + PcodeOp *op; ///< The p-code end-point of the edge + int4 slot; ///< Slot indicating the input Varnode end-point of the edge + PcodeOpNode(void) { op = (PcodeOp *)0; slot = 0; } ///< Unused constructor + PcodeOpNode(PcodeOp *o,int4 s) { op = o; slot = s; } ///< Constructor + bool operator<(const PcodeOpNode &op2) const; ///< Simple comparator for putting edges in a sorted container + static bool compareByHigh(const PcodeOpNode &a,const PcodeOpNode &b); ///< Compare Varnodes by their HighVariable +}; + +/// Compare PcodeOps (as pointers) first, then slot +/// \param op2 is the other edge to compare with \b this +/// \return true if \b this should come before the other PcodeOp +inline bool PcodeOpNode::operator<(const PcodeOpNode &op2) const + +{ + if (op != op2.op) + return (op->getSeqNum().getTime() < op2.op->getSeqNum().getTime()); + if (slot != op2.slot) + return (slot < op2.slot); + return false; +} + +/// Allow a sorting that groups together input Varnodes with the same HighVariable +/// \param a is the first Varnode to compare +/// \param b is the second Varnode to compare +/// \return true is \b a should come before \b b +inline bool PcodeOpNode::compareByHigh(const PcodeOpNode &a, const PcodeOpNode &b) + +{ + return a.op->getIn(a.slot)->getHigh() < b.op->getIn(b.slot)->getHigh(); +} + +/// \brief Node for a forward traversal of a Varnode expression +struct TraverseNode { + enum { + actionalt = 1, ///< Alternate path traverses a solid action or \e non-incidental COPY + indirect = 2, ///< Main path traverses an INDIRECT + indirectalt = 4, ///< Alternate path traverses an INDIRECT + lsb_truncated = 8, ///< Least significant byte(s) of original value have been truncated + concat_high = 0x10 ///< Original value has been concatented as \e most significant portion + }; + const Varnode *vn; ///< Varnode at the point of traversal + uint4 flags; ///< Flags associated with the node + TraverseNode(const Varnode *v,uint4 f) { vn = v; flags = f; } ///< Constructor + static bool isAlternatePathValid(const Varnode *vn,uint4 flags); +}; + +/// \brief Static methods for determining if two boolean expressions are the \b same or \b complementary +/// +/// Traverse (upto a specific depth) the two boolean expressions consisting of BOOL_AND, BOOL_OR, and +/// BOOL_XOR operations. Leaf operators in the expression can be other operators with boolean output (INT_LESS, +/// INT_SLESS, etc.). +class BooleanMatch { + static bool sameOpComplement(PcodeOp *bin1op, PcodeOp *bin2op); + static bool varnodeSame(Varnode *a,Varnode *b); +public: + enum { + same = 1, ///< Pair always hold the same value + complementary = 2, ///< Pair always hold complementary values + uncorrelated = 3 ///< Pair values are uncorrelated + }; + static int4 evaluate(Varnode *vn1,Varnode *vn2,int4 depth); +}; + +/// \brief A helper class for describing the similarity of the boolean condition between 2 CBRANCH operations +/// +/// This class determines if two CBRANCHs share the same condition. It also determines if the conditions +/// are complements of each other, and/or they are shared along only one path. +class BooleanExpressionMatch { + static const int4 maxDepth; ///< Maximum depth to trace a boolean expression + bool matchflip; ///< True if the compared CBRANCH keys on the opposite boolean value of the root +public: + bool verifyCondition(PcodeOp *op, PcodeOp *iop); ///< Perform the correlation test on two CBRANCH operations + int4 getMultiSlot(void) const { return -1; } ///< Get the MULTIEQUAL slot in the critical path + bool getFlip(void) const { return matchflip; } ///< Return \b true if the expressions are anti-correlated +}; + +/// Class representing a \e term in an additive expression +class AdditiveEdge { + PcodeOp *op; ///< Lone descendant reading the term + int4 slot; ///< The input slot of the term + Varnode *vn; ///< The term Varnode + PcodeOp *mult; ///< The (optional) multiplier being applied to the term +public: + AdditiveEdge(PcodeOp *o,int4 s,PcodeOp *m) { op = o; slot = s; vn = op->getIn(slot); mult=m; } ///< Constructor + PcodeOp *getMultiplier(void) const { return mult; } ///< Get the multiplier PcodeOp + PcodeOp *getOp(void) const { return op; } ///< Get the component PcodeOp adding in the term + int4 getSlot(void) const { return slot; } ///< Get the slot reading the term + Varnode *getVarnode(void) const { return vn; } ///< Get the Varnode term +}; + +/// \brief A class for ordering Varnode terms in an additive expression. +/// +/// Given the final PcodeOp in a data-flow expression that sums 2 or more +/// Varnode \e terms, this class collects all the terms then allows +/// sorting of the terms to facilitate constant collapse and factoring simplifications. +class TermOrder { + PcodeOp *root; ///< The final PcodeOp in the expression + vector terms; ///< Collected terms + vector sorter; ///< An array of references to terms for quick sorting + static bool additiveCompare(const AdditiveEdge *op1,const AdditiveEdge *op2); +public: + TermOrder(PcodeOp *rt) { root = rt; } ///< Construct given root PcodeOp + int4 getSize(void) const { return terms.size(); } ///< Get the number of terms in the expression + void collect(void); ///< Collect all the terms in the expression + void sortTerms(void); ///< Sort the terms using additiveCompare() + const vector &getSort(void) { return sorter; } ///< Get the sorted list of references +}; + +/// \brief Class for lightweight matching of two additive expressions +/// +/// Collect (up to 2) terms along with any constants and coefficients. +//// Determine if two expressions are equivalent. +class AddExpression { + /// \brief A term in the expression + class Term { + Varnode *vn; ///< The Varnode representing the term + uintb coeff; ///< Multiplicative coefficient + public: + Term(void) {} ///< Uninitialized constructor + Term(Varnode *v,uintb c) { vn = v; coeff = c; } ///< Constructor + bool isEquivalent(const Term &op2) const; ///< Compare two terms for functional equivalence + }; + uintb constval; ///< Collected constants in the expression + int4 numTerms; ///< Number of terms + Term terms[2]; ///< Terms making up the expression + void add(Varnode *vn,uintb coeff) { if (numTerms < 2) terms[numTerms++] = Term(vn,coeff); } ///< Add a term to the expression + void gather(Varnode *vn,uintb coeff,int4 depth); ///< Gather terms in the expression from a root point +public: + AddExpression(void) { constval = 0; numTerms = 0; } ///< Construct an empty expression + void gatherTwoTermsSubtract(Varnode *a,Varnode *b); ///< Walk expression given two roots being subtracted from one another + void gatherTwoTermsAdd(Varnode *a,Varnode *b); ///< Walk expression given two roots being added to each other + void gatherTwoTermsRoot(Varnode *root); ///< Gather up to 2 terms given root Varnode + bool isEquivalent(const AddExpression &op2) const; ///< Determine if 2 expressions are equivalent +}; + +/// \brief A comparison operator for ordering terms in a sum +/// +/// This is based on Varnode::termOrder which groups constants terms and +/// ignores multiplicative coefficients. +/// \param op1 is the first term to compare +/// \param op2 is the second term +/// \return \b true if the first term is less than the second +inline bool TermOrder::additiveCompare(const AdditiveEdge *op1,const AdditiveEdge *op2) { + return (-1 == op1->getVarnode()->termOrder(op2->getVarnode())); +} + +extern int4 functionalEqualityLevel(Varnode *vn1,Varnode *vn2,Varnode **res1,Varnode **res2); +extern bool functionalEquality(Varnode *vn1,Varnode *vn2); +extern bool functionalDifference(Varnode *vn1,Varnode *vn2,int4 depth); + +} // End namespace ghidra +#endif diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/jumptable.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/jumptable.hh index 2b1b121258..c6486103f7 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/jumptable.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/jumptable.hh @@ -21,6 +21,7 @@ #include "emulateutil.hh" #include "rangeutil.hh" +#include "expression.hh" namespace ghidra { diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/merge.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/merge.hh index 047a0a7845..4ff5c24802 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/merge.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/merge.hh @@ -20,6 +20,7 @@ /// \brief Utilities for merging low-level Varnodes into high-level variables #include "op.hh" +#include "expression.hh" namespace ghidra { diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/op.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/op.cc index ea8c1e9e04..cc7e46033f 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/op.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/op.cc @@ -1184,329 +1184,4 @@ void PcodeOpBank::clear(void) uniqid = 0; } -static int4 functionalEqualityLevel0(Varnode *vn1,Varnode *vn2) - -{ // Return 0 if -vn1- and -vn2- must hold same value - // Return -1 if they definitely don't hold same value - // Return 1 if the same value depends on ops writing to -vn1- and -vn2- - if (vn1==vn2) return 0; - if (vn1->getSize() != vn2->getSize()) return -1; - if (vn1->isConstant()) { - if (vn2->isConstant()) { - return (vn1->getOffset() == vn2->getOffset()) ? 0 : -1; - } - return -1; - } - if (vn2->isConstant()) return -1; - if (vn1->isWritten() && vn2->isWritten()) return 1; - return -1; -} - -/// \brief Try to determine if \b vn1 and \b vn2 contain the same value -/// -/// Return: -/// - -1, if they do \b not, or if it can't be immediately verified -/// - 0, if they \b do hold the same value -/// - >0, if the result is contingent on additional varnode pairs having the same value -/// In the last case, the varnode pairs are returned as (res1[i],res2[i]), -/// where the return value is the number of pairs. -/// \param vn1 is the first Varnode to compare -/// \param vn2 is the second Varnode -/// \param res1 is a reference to the first returned Varnode -/// \param res2 is a reference to the second returned Varnode -/// \return the result of the comparison -int4 functionalEqualityLevel(Varnode *vn1,Varnode *vn2,Varnode **res1,Varnode **res2) - -{ - int4 testval = functionalEqualityLevel0(vn1,vn2); - if (testval != 1) return testval; - PcodeOp *op1 = vn1->getDef(); - PcodeOp *op2 = vn2->getDef(); - OpCode opc = op1->code(); - - if (opc != op2->code()) return -1; - - int4 num = op1->numInput(); - if (num != op2->numInput()) return -1; - if (op1->isMarker()) return -1; - if (op2->isCall()) return -1; - if (opc == CPUI_LOAD) { - // FIXME: We assume two loads produce the same - // result if the address is the same and the loads - // occur in the same instruction - if (op1->getAddr() != op2->getAddr()) return -1; - } - if (num >= 3) { - if (opc != CPUI_PTRADD) return -1; // If this is a PTRADD - if (op1->getIn(2)->getOffset() != op2->getIn(2)->getOffset()) return -1; // Make sure the elsize constant is equal - num = 2; // Otherwise treat as having 2 inputs - } - for(int4 i=0;igetIn(i); - res2[i] = op2->getIn(i); - } - - testval = functionalEqualityLevel0(res1[0],res2[0]); - if (testval == 0) { // A match locks in this comparison ordering - if (num==1) return 0; - testval = functionalEqualityLevel0(res1[1],res2[1]); - if (testval==0) return 0; - if (testval < 0) return -1; - res1[0] = res1[1]; // Match is contingent on second pair - res2[0] = res2[1]; - return 1; - } - if (num == 1) return testval; - int4 testval2 = functionalEqualityLevel0(res1[1],res2[1]); - if (testval2 == 0) { // A match locks in this comparison ordering - return testval; - } - int4 unmatchsize; - if ((testval==1)&&(testval2==1)) - unmatchsize = 2; - else - unmatchsize = -1; - - if (!op1->isCommutative()) return unmatchsize; - // unmatchsize must be 2 or -1 here on a commutative operator, - // try flipping - int4 comm1 = functionalEqualityLevel0(res1[0],res2[1]); - int4 comm2 = functionalEqualityLevel0(res1[1],res2[0]); - if ((comm1==0) && (comm2==0)) - return 0; - if ((comm1<0)||(comm2<0)) - return unmatchsize; - if (comm1==0) { // AND (comm2==1) - res1[0] = res1[1]; // Left over unmatch is res1[1] and res2[0] - return 1; - } - if (comm2==0) { // AND (comm1==1) - res2[0] = res2[1]; // Left over unmatch is res1[0] and res2[1] - return 1; - } - // If we reach here (comm1==1) AND (comm2==1) - if (unmatchsize == 2) // If the original ordering wasn't impossible - return 2; // Prefer the original ordering - Varnode *tmpvn = res2[0]; // Otherwise swap the ordering - res2[0] = res2[1]; - res2[1] = tmpvn; - return 2; -} - -/// \brief Determine if two Varnodes hold the same value -/// -/// Only return \b true if it can be immediately determined they are equivalent -/// \param vn1 is the first Varnode -/// \param vn2 is the second Varnode -/// \return true if they are provably equal -bool functionalEquality(Varnode *vn1,Varnode *vn2) - -{ - Varnode *buf1[2]; - Varnode *buf2[2]; - return (functionalEqualityLevel(vn1,vn2,buf1,buf2)==0); -} - -/// \brief Return true if vn1 and vn2 are verifiably different values -/// -/// This is actually a rather speculative test -/// \param vn1 is the first Varnode to compare -/// \param vn2 is the second Varnode -/// \param depth is the maximum level to recurse while testing -/// \return \b true if they are different -bool functionalDifference(Varnode *vn1,Varnode *vn2,int4 depth) - -{ - PcodeOp *op1,*op2; - int4 i,num; - - if (vn1 == vn2) return false; - if ((!vn1->isWritten())||(!vn2->isWritten())) { - if (vn1->isConstant() && vn2->isConstant()) - return !(vn1->getAddr()==vn2->getAddr()); - if (vn1->isInput()&&vn2->isInput()) return false; // Might be the same - if (vn1->isFree()||vn2->isFree()) return false; // Might be the same - return true; - } - op1 = vn1->getDef(); - op2 = vn2->getDef(); - if (op1->code() != op2->code()) return true; - num = op1->numInput(); - if (num != op2->numInput()) return true; - if (depth==0) return true; // Different as far as we can tell - depth -= 1; - for(i=0;igetIn(i),op2->getIn(i),depth)) - return true; - return false; -} - -/// \brief Test if two operations with same opcode produce complementary boolean values -/// -/// This only tests for cases where the opcode is INT_LESS or INT_SLESS and one of the -/// inputs is constant. -/// \param bin1op is the first p-code op to compare -/// \param bin2op is the second p-code op to compare -/// \return \b true if the two operations always produce complementary values -bool BooleanMatch::sameOpComplement(PcodeOp *bin1op,PcodeOp *bin2op) - -{ - OpCode opcode = bin1op->code(); - if ((opcode == CPUI_INT_SLESS)||(opcode==CPUI_INT_LESS)) { - // Basically we test for the scenario like: x < 9 8 < x - int4 constslot = 0; - if (bin1op->getIn(1)->isConstant()) - constslot = 1; - if (!bin1op->getIn(constslot)->isConstant()) return false; - if (!bin2op->getIn(1-constslot)->isConstant()) return false; - if (!varnodeSame(bin1op->getIn(1-constslot),bin2op->getIn(constslot))) return false; - uintb val1 = bin1op->getIn(constslot)->getOffset(); - uintb val2 = bin2op->getIn(1-constslot)->getOffset(); - if (constslot!=0) { - uintb tmp = val2; - val2 = val1; - val1 = tmp; - } - if (val1 + 1 != val2) return false; - if ((val2 == 0)&&(opcode==CPUI_INT_LESS)) return false; // Corner case for unsigned - if (opcode==CPUI_INT_SLESS) { // Corner case for signed - int4 sz = bin1op->getIn(constslot)->getSize(); - if (signbit_negative(val2,sz) && (!signbit_negative(val1,sz))) - return false; - } - return true; - } - return false; -} - -/// \brief Do the given Varnodes hold the same value, possibly as constants -/// -/// \param a is the first Varnode to compare -/// \param b is the second Varnode -/// \return \b true if the Varnodes (always) hold the same value -bool BooleanMatch::varnodeSame(Varnode *a,Varnode *b) - -{ - if (a == b) return true; - if (a->isConstant() && b->isConstant()) - return (a->getOffset() == b->getOffset()); - return false; -} - -/// \brief Determine if two boolean Varnodes hold related values -/// -/// The values may be the \e same, or opposite of each other (\e complementary). -/// Otherwise the values are \e uncorrelated. The trees constructing each Varnode -/// are examined up to a maximum \b depth. If this is exceeded \e uncorrelated is returned. -/// \param vn1 is the first boolean Varnode -/// \param vn2 is the second boolean Varnode -/// \param depth is the maximum depth to traverse in the evaluation -/// \return the correlation class -int4 BooleanMatch::evaluate(Varnode *vn1,Varnode *vn2,int4 depth) - -{ - if (vn1 == vn2) return same; - PcodeOp *op1,*op2; - OpCode opc1,opc2; - if (vn1->isWritten()) { - op1 = vn1->getDef(); - opc1 = op1->code(); - if (opc1 == CPUI_BOOL_NEGATE) { - int res = evaluate(op1->getIn(0),vn2,depth); - if (res == same) // Flip same <-> complementary result - res = complementary; - else if (res == complementary) - res = same; - return res; - } - } - else { - op1 = (PcodeOp *)0; // Don't give up before checking if op2 is BOOL_NEGATE - opc1 = CPUI_MAX; - } - if (vn2->isWritten()) { - op2 = vn2->getDef(); - opc2 = op2->code(); - if (opc2 == CPUI_BOOL_NEGATE) { - int4 res = evaluate(vn1,op2->getIn(0),depth); - if (res == same) // Flip same <-> complementary result - res = complementary; - else if (res == complementary) - res = same; - return res; - } - } - else - return uncorrelated; - if (op1 == (PcodeOp *)0) - return uncorrelated; - if (!op1->isBoolOutput() || !op2->isBoolOutput()) - return uncorrelated; - if (depth != 0 && (opc1 == CPUI_BOOL_AND || opc1 == CPUI_BOOL_OR || opc1 == CPUI_BOOL_XOR)) { - if (opc2 == CPUI_BOOL_AND || opc2 == CPUI_BOOL_OR || opc2 == CPUI_BOOL_XOR) { - if (opc1 == opc2 || (opc1 == CPUI_BOOL_AND && opc2 == CPUI_BOOL_OR) || (opc1 == CPUI_BOOL_OR && opc2 == CPUI_BOOL_AND)) { - int4 pair1 = evaluate(op1->getIn(0),op2->getIn(0),depth-1); - int4 pair2; - if (pair1 == uncorrelated) { - pair1 = evaluate(op1->getIn(0),op2->getIn(1),depth-1); // Try other possible pairing (commutative op) - if (pair1 == uncorrelated) - return uncorrelated; - pair2 = evaluate(op1->getIn(1),op2->getIn(0),depth-1); - } - else { - pair2 = evaluate(op1->getIn(1),op2->getIn(1),depth-1); - } - if (pair2 == uncorrelated) - return uncorrelated; - if (opc1 == opc2) { - if (pair1 == same && pair2 == same) - return same; - else if (opc1 == CPUI_BOOL_XOR) { - if (pair1 == complementary && pair2 == complementary) - return same; - return complementary; - } - } - else { // Must be CPUI_BOOL_AND and CPUI_BOOL_OR - if (pair1 == complementary && pair2 == complementary) - return complementary; // De Morgan's Law - } - } - } - } - else { - // Two boolean output ops, compare them directly - if (opc1 == opc2) { - bool sameOp = true; - int numInputs = op1->numInput(); - for (int i = 0; i < numInputs; ++i){ - if (!varnodeSame(op1->getIn(i),op2->getIn(i))){ - sameOp = false; - break; - } - } - if (sameOp){ - return same; - } - if (sameOpComplement(op1,op2)) { - return complementary; - } - return uncorrelated; - } - // Check if the binary ops are complements of one another - int4 slot1 = 0; - int4 slot2 = 0; - bool reorder; - if (opc1 != get_booleanflip(opc2,reorder)) - return uncorrelated; - if (reorder) slot2 = 1; - if (!varnodeSame(op1->getIn(slot1),op2->getIn(slot2))) - return uncorrelated; - if (!varnodeSame(op1->getIn(1-slot1),op2->getIn(1-slot2))) - return uncorrelated; - return complementary; - } - return uncorrelated; -} - } // End namespace ghidra diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/op.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/op.hh index a2d5c984ee..3111e8c8b4 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/op.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/op.hh @@ -251,18 +251,6 @@ public: Datatype *inputTypeLocal(int4 slot) const { return opcode->getInputLocal(this,slot); } ///< Calculate the local input type }; -/// \brief An edge in a data-flow path or graph -/// -/// A minimal node for traversing expressions in the data-flow -struct PcodeOpNode { - PcodeOp *op; ///< The p-code end-point of the edge - int4 slot; ///< Slot indicating the input Varnode end-point of the edge - PcodeOpNode(void) { op = (PcodeOp *)0; slot = 0; } ///< Unused constructor - PcodeOpNode(PcodeOp *o,int4 s) { op = o; slot = s; } ///< Constructor - bool operator<(const PcodeOpNode &op2) const; ///< Simple comparator for putting edges in a sorted container - static bool compareByHigh(const PcodeOpNode &a,const PcodeOpNode &b); ///< Compare Varnodes by their HighVariable -}; - /// \brief A node in a tree structure of CPUI_PIECE operations /// /// If a group of Varnodes are concatenated into a larger structure, this object is used to explicitly gather @@ -362,49 +350,5 @@ public: list::const_iterator end(OpCode opc) const; }; -extern int4 functionalEqualityLevel(Varnode *vn1,Varnode *vn2,Varnode **res1,Varnode **res2); -extern bool functionalEquality(Varnode *vn1,Varnode *vn2); -extern bool functionalDifference(Varnode *vn1,Varnode *vn2,int4 depth); - -/// \brief Static methods for determining if two boolean expressions are the \b same or \b complementary -/// -/// Traverse (upto a specific depth) the two boolean expressions consisting of BOOL_AND, BOOL_OR, and -/// BOOL_XOR operations. Leaf operators in the expression can be other operators with boolean output (INT_LESS, -/// INT_SLESS, etc.). -class BooleanMatch { - static bool sameOpComplement(PcodeOp *bin1op, PcodeOp *bin2op); - static bool varnodeSame(Varnode *a,Varnode *b); -public: - enum { - same = 1, ///< Pair always hold the same value - complementary = 2, ///< Pair always hold complementary values - uncorrelated = 3 ///< Pair values are uncorrelated - }; - static int4 evaluate(Varnode *vn1,Varnode *vn2,int4 depth); -}; - -/// Compare PcodeOps (as pointers) first, then slot -/// \param op2 is the other edge to compare with \b this -/// \return true if \b this should come before the other PcodeOp -inline bool PcodeOpNode::operator<(const PcodeOpNode &op2) const - -{ - if (op != op2.op) - return (op->getSeqNum().getTime() < op2.op->getSeqNum().getTime()); - if (slot != op2.slot) - return (slot < op2.slot); - return false; -} - -/// Allow a sorting that groups together input Varnodes with the same HighVariable -/// \param a is the first Varnode to compare -/// \param b is the second Varnode to compare -/// \return true is \b a should come before \b b -inline bool PcodeOpNode::compareByHigh(const PcodeOpNode &a, const PcodeOpNode &b) - -{ - return a.op->getIn(a.slot)->getHigh() < b.op->getIn(b.slot)->getHigh(); -} - } // End namespace ghidra #endif diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.cc index 3801b017cd..0912e4f0e9 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.cc @@ -3361,27 +3361,26 @@ void RuleSborrow::getOpList(vector &oplist) const int4 RuleSborrow::applyOp(PcodeOp *op,Funcdata &data) { - Varnode *svn = op->getOut(); - Varnode *cvn,*avn,*bvn; - list::const_iterator iter; - PcodeOp *compop,*signop,*addop; int4 zside; + Varnode *svn = op->getOut(); + Varnode *avn = op->getIn(0); + Varnode *bvn = op->getIn(1); // Check for trivial case - if ((op->getIn(1)->isConstant()&&op->getIn(1)->getOffset()==0)|| - (op->getIn(0)->isConstant()&&op->getIn(0)->getOffset()==0)) { + if (bvn->isConstant()&&bvn->getOffset()==0) { data.opSetOpcode(op,CPUI_COPY); data.opSetInput(op,data.newConstant(1,0),0); data.opRemoveInput(op,1); return 1; } + list::const_iterator iter; for(iter=svn->beginDescend();iter!=svn->endDescend();++iter) { - compop = *iter; + PcodeOp *compop = *iter; if ((compop->code()!=CPUI_INT_EQUAL)&&(compop->code()!=CPUI_INT_NOTEQUAL)) continue; - cvn = (compop->getIn(0)==svn) ? compop->getIn(1) : compop->getIn(0); + Varnode *cvn = (compop->getIn(0)==svn) ? compop->getIn(1) : compop->getIn(0); if (!cvn->isWritten()) continue; - signop = cvn->getDef(); + PcodeOp *signop = cvn->getDef(); if (signop->code() != CPUI_INT_SLESS) continue; if (!signop->getIn(0)->constantMatch(0)) { if (!signop->getIn(1)->constantMatch(0)) continue; @@ -3389,36 +3388,13 @@ int4 RuleSborrow::applyOp(PcodeOp *op,Funcdata &data) } else zside = 0; - if (!signop->getIn(1-zside)->isWritten()) continue; - addop = signop->getIn(1-zside)->getDef(); - if (addop->code() == CPUI_INT_ADD) { - avn = op->getIn(0); - if (functionalEquality(avn,addop->getIn(0))) - bvn = addop->getIn(1); - else if (functionalEquality(avn,addop->getIn(1))) - bvn = addop->getIn(0); - else - continue; - } - else - continue; - if (bvn->isConstant()) { - Address flip(bvn->getSpace(),uintb_negate(bvn->getOffset()-1,bvn->getSize())); - bvn = op->getIn(1); - if (flip != bvn->getAddr()) continue; - } - else if (bvn->isWritten()) { - PcodeOp *otherop = bvn->getDef(); - if (otherop->code() == CPUI_INT_MULT) { - if (!otherop->getIn(1)->isConstant()) continue; - if (otherop->getIn(1)->getOffset() != calc_mask(otherop->getIn(1)->getSize())) continue; - bvn = otherop->getIn(0); - } - else if (otherop->code()==CPUI_INT_2COMP) - bvn = otherop->getIn(0); - if (!functionalEquality(bvn,op->getIn(1))) continue; - } - else + Varnode *xvn = signop->getIn(1-zside); + if (!xvn->isWritten()) continue; + AddExpression expr1; + expr1.gatherTwoTermsSubtract(avn, bvn); + AddExpression expr2; + expr2.gatherTwoTermsRoot(xvn); + if (!expr1.isEquivalent(expr2)) continue; if (compop->code() == CPUI_INT_NOTEQUAL) { data.opSetOpcode(compop,CPUI_INT_SLESS); // Replace all this with simple less than @@ -3435,6 +3411,88 @@ int4 RuleSborrow::applyOp(PcodeOp *op,Funcdata &data) return 0; } +/// \class RuleScarry +/// \brief Simplify signed comparisons using INT_SCARRY +/// +/// - `scarry(V,0) => false` +/// - `scarry(V,#W) != (V + #W s< 0) => V s< -#W` +/// - `scarry(V,#W) != (0 s< V + #W) => -#W s< V` +/// - `scarry(V,#W) == (0 s< V + #W) => V s<= -#W` +/// - `scarry(V,#W) == (V + #W s< 0) => -#W s<= V` +/// +/// Supports variations where W is constant. +void RuleScarry::getOpList(vector &oplist) const + +{ + oplist.push_back(CPUI_INT_SCARRY); +} + +int4 RuleScarry::applyOp(PcodeOp *op,Funcdata &data) + +{ + int4 zside; + + Varnode *svn = op->getOut(); + Varnode *avn = op->getIn(0); + Varnode *bvn = op->getIn(1); + + // Check for trivial case + if ((bvn->isConstant() && bvn->getOffset() == 0) || + (avn->isConstant() && avn->getOffset() == 0)) { + data.opSetOpcode(op,CPUI_COPY); + data.opSetInput(op,data.newConstant(1,0),0); + data.opRemoveInput(op,1); + return 1; + } + if (!bvn->isConstant()) { // One side must be constant + if (!avn->isConstant()) return 0; + avn = bvn; + bvn = op->getIn(0); + uintb val = calc_mask(bvn->getSize()); + val = val ^ (val >> 1); // Calculate integer minimum + if (val == bvn->getOffset()) return 0; // Rule does not work if bvn is the integer minimum + } + list::const_iterator iter; + for(iter=svn->beginDescend();iter!=svn->endDescend();++iter) { + PcodeOp *compop = *iter; + if ((compop->code()!=CPUI_INT_EQUAL)&&(compop->code()!=CPUI_INT_NOTEQUAL)) + continue; + Varnode *cvn = (compop->getIn(0)==svn) ? compop->getIn(1) : compop->getIn(0); + if (!cvn->isWritten()) continue; + PcodeOp *signop = cvn->getDef(); + if (signop->code() != CPUI_INT_SLESS) continue; + if (!signop->getIn(0)->constantMatch(0)) { + if (!signop->getIn(1)->constantMatch(0)) continue; + zside = 1; + } + else + zside = 0; + Varnode *xvn = signop->getIn(1-zside); + if (!xvn->isWritten()) continue; + AddExpression expr1; + expr1.gatherTwoTermsAdd(avn, bvn); + AddExpression expr2; + expr2.gatherTwoTermsRoot(xvn); + if (!expr1.isEquivalent(expr2)) + continue; + uintb newval = -bvn->getOffset() & calc_mask(bvn->getSize()); + Varnode *newConst = data.newConstant(bvn->getSize(), newval); + + if (compop->code() == CPUI_INT_NOTEQUAL) { + data.opSetOpcode(compop,CPUI_INT_SLESS); // Replace all this with simple less than + data.opSetInput(compop,avn,1-zside); + data.opSetInput(compop,newConst,zside); + } + else { + data.opSetOpcode(compop,CPUI_INT_SLESSEQUAL); + data.opSetInput(compop,avn,zside); + data.opSetInput(compop,newConst,1-zside); + } + return 1; + } + return 0; +} + /// \class RuleTrivialShift /// \brief Simplify trivial shifts: `V << 0 => V, V << #64 => 0` void RuleTrivialShift::getOpList(vector &oplist) const @@ -5609,8 +5667,6 @@ Varnode *RuleSLess2Zero::getHiBit(PcodeOp *op) /// \brief Simplify INT_SLESS applied to 0 or -1 /// /// Forms include: -/// - `0 s< V * -1 => V s< 0` -/// - `V * -1 s< 0 => 0 s< V` /// - `-1 s< SUB(V,hi) => -1 s< V` /// - `SUB(V,hi) s< 0 => V s< 0` /// - `-1 s< ~V => V s< 0` @@ -5621,6 +5677,7 @@ Varnode *RuleSLess2Zero::getHiBit(PcodeOp *op) /// - `-1 s< CONCAT(V,W) => -1 s> V` /// - `-1 s< (bool << #8*sz-1) => !bool` /// +/// Note that `0 s< -X => X s< 0` is not valid if X is maximally negative. /// There is a second set of forms where one side of the comparison is /// built out of a high and low piece, where the high piece determines the /// sign bit: @@ -5644,21 +5701,7 @@ int4 RuleSLess2Zero::applyOp(PcodeOp *op,Funcdata &data) if (lvn->isConstant()) { if (!rvn->isWritten()) return 0; - if (lvn->getOffset() == 0) { - feedOp = rvn->getDef(); - feedOpCode = feedOp->code(); - if (feedOpCode == CPUI_INT_MULT) { - coeff = feedOp->getIn(1); - if (!coeff->isConstant()) return 0; - if (coeff->getOffset() != calc_mask(coeff->getSize())) return 0; - avn = feedOp->getIn(0); - if (avn->isFree()) return 0; - data.opSetInput(op,avn,0); - data.opSetInput(op,lvn,1); - return 1; - } - } - else if (lvn->getOffset() == calc_mask(lvn->getSize())) { + if (lvn->getOffset() == calc_mask(lvn->getSize())) { feedOp = rvn->getDef(); feedOpCode = feedOp->code(); Varnode *hibit = getHiBit(feedOp); @@ -5736,69 +5779,57 @@ int4 RuleSLess2Zero::applyOp(PcodeOp *op,Funcdata &data) if (rvn->getOffset() == 0) { feedOp = lvn->getDef(); feedOpCode = feedOp->code(); - if (feedOpCode == CPUI_INT_MULT) { - coeff = feedOp->getIn(1); - if (!coeff->isConstant()) return 0; - if (coeff->getOffset() != calc_mask(coeff->getSize())) return 0; + Varnode *hibit = getHiBit(feedOp); + if (hibit != (Varnode *)0) { // Test for (hi ^ lo) s< 0 + if (hibit->isConstant()) + data.opSetInput(op,data.newConstant(hibit->getSize(),hibit->getOffset()),0); + else + data.opSetInput(op,hibit,0); + data.opSetOpcode(op,CPUI_INT_NOTEQUAL); + return 1; + } + else if (feedOpCode == CPUI_SUBPIECE) { + avn = feedOp->getIn(0); + if (avn->isFree() || avn->getSize() > 8) // Don't create comparison greater than 8 bytes + return 0; + if (lvn->getSize() + (int4)feedOp->getIn(1)->getOffset() == avn->getSize()) { + // We have SUB( avn, #hi ) s< 0 + data.opSetInput(op,avn,0); + data.opSetInput(op,data.newConstant(avn->getSize(),0),1); + return 1; + } + } + else if (feedOpCode == CPUI_INT_NEGATE) { + // We have ~avn s< 0 avn = feedOp->getIn(0); if (avn->isFree()) return 0; data.opSetInput(op,avn,1); - data.opSetInput(op,rvn,0); + data.opSetInput(op,data.newConstant(avn->getSize(),calc_mask(avn->getSize())),0); return 1; } - else { - Varnode *hibit = getHiBit(feedOp); - if (hibit != (Varnode *)0) { // Test for (hi ^ lo) s< 0 - if (hibit->isConstant()) - data.opSetInput(op,data.newConstant(hibit->getSize(),hibit->getOffset()),0); - else - data.opSetInput(op,hibit,0); - data.opSetOpcode(op,CPUI_INT_NOTEQUAL); - return 1; - } - else if (feedOpCode == CPUI_SUBPIECE) { - avn = feedOp->getIn(0); - if (avn->isFree() || avn->getSize() > 8) // Don't create comparison greater than 8 bytes - return 0; - if (lvn->getSize() + (int4)feedOp->getIn(1)->getOffset() == avn->getSize()) { - // We have SUB( avn, #hi ) s< 0 - data.opSetInput(op,avn,0); - data.opSetInput(op,data.newConstant(avn->getSize(),0),1); + else if (feedOpCode == CPUI_INT_AND) { + avn = feedOp->getIn(0); + if (avn->isFree() || lvn->loneDescend() == (PcodeOp *)0) + return 0; + Varnode *maskVn = feedOp->getIn(1); + if (maskVn->isConstant()) { + uintb mask = maskVn->getOffset(); + mask >>= (8 * avn->getSize() - 1); // Fetch sign-bit + if ((mask & 1) != 0) { + // We have avn & 0x8... s< 0 + data.opSetInput(op, avn, 0); return 1; } } - else if (feedOpCode == CPUI_INT_NEGATE) { - // We have ~avn s< 0 - avn = feedOp->getIn(0); - if (avn->isFree()) return 0; - data.opSetInput(op,avn,1); - data.opSetInput(op,data.newConstant(avn->getSize(),calc_mask(avn->getSize())),0); - return 1; - } - else if (feedOpCode == CPUI_INT_AND) { - avn = feedOp->getIn(0); - if (avn->isFree() || lvn->loneDescend() == (PcodeOp *)0) - return 0; - Varnode *maskVn = feedOp->getIn(1); - if (maskVn->isConstant()) { - uintb mask = maskVn->getOffset(); - mask >>= (8 * avn->getSize() - 1); // Fetch sign-bit - if ((mask & 1) != 0) { - // We have avn & 0x8... s< 0 - data.opSetInput(op, avn, 0); - return 1; - } - } - } - else if (feedOpCode == CPUI_PIECE) { - // We have CONCAT(V,W) s< 0 - avn = feedOp->getIn(0); // Most significant piece - if (avn->isFree()) - return 0; - data.opSetInput(op, avn, 0); - data.opSetInput(op, data.newConstant(avn->getSize(), 0), 1); - return 1; - } + } + else if (feedOpCode == CPUI_PIECE) { + // We have CONCAT(V,W) s< 0 + avn = feedOp->getIn(0); // Most significant piece + if (avn->isFree()) + return 0; + data.opSetInput(op, avn, 0); + data.opSetInput(op, data.newConstant(avn->getSize(), 0), 1); + return 1; } } } diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.hh index 7eab9fa087..5b8c80d2d0 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.hh @@ -629,6 +629,16 @@ public: virtual void getOpList(vector &oplist) const; virtual int4 applyOp(PcodeOp *op,Funcdata &data); }; +class RuleScarry : public Rule { +public: + RuleScarry(const string &g) : Rule(g, 0, "scarry") {} ///< Constructor + virtual Rule *clone(const ActionGroupList &grouplist) const { + if (!grouplist.contains(getGroup())) return (Rule *)0; + return new RuleScarry(getGroup()); + } + virtual void getOpList(vector &oplist) const; + virtual int4 applyOp(PcodeOp *op,Funcdata &data); +}; class RuleTrivialShift : public Rule { public: RuleTrivialShift(const string &g) : Rule(g, 0, "trivialshift") {} ///< Constructor diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/variable.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/variable.cc index f064ddd256..36ff25468c 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/variable.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/variable.cc @@ -15,6 +15,7 @@ */ #include "variable.hh" #include "op.hh" +#include "expression.hh" #include "database.hh" namespace ghidra { diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/varnode.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/varnode.cc index eb0814267d..a04614c582 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/varnode.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/varnode.cc @@ -2006,36 +2006,6 @@ void VarnodeBank::verifyIntegrity(void) const } #endif -/// \brief Return \b true if the alternate path looks more valid than the main path. -/// -/// Two different paths from a common Varnode each terminate at a CALL, CALLIND, or RETURN. -/// Evaluate which path most likely represents actual parameter/return value passing, -/// based on traversal information about each path. -/// \param vn is the Varnode terminating the \e alternate path -/// \param flags indicates traversals for both paths -/// \return \b true if the alternate path is preferred -bool TraverseNode::isAlternatePathValid(const Varnode *vn,uint4 flags) - -{ - if ((flags & (indirect | indirectalt)) == indirect) - // If main path traversed an INDIRECT but the alternate did not - return true; // Main path traversed INDIRECT, alternate did not - if ((flags & (indirect | indirectalt)) == indirectalt) - return false; // Alternate path traversed INDIRECT, main did not - if ((flags & actionalt) != 0) - return true; // Alternate path traversed a dedicated COPY - if (vn->loneDescend() == (PcodeOp*)0) return false; - const PcodeOp *op = vn->getDef(); - if (op == (PcodeOp*)0) return true; - while(op->isIncidentalCopy() && op->code() == CPUI_COPY) { // Skip any incidental COPY - vn = op->getIn(0); - if (vn->loneDescend() == (PcodeOp *)0) return false; - op = vn->getDef(); - if (op == (PcodeOp *)0) return true; - } - return !op->isMarker(); // MULTIEQUAL or INDIRECT indicates multiple values -} - /// Return true if \b vn1 contains the high part and \b vn2 the low part /// of what was(is) a single value. /// \param vn1 is the putative high Varnode diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/varnode.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/varnode.hh index 7360cf41ef..b78368fc8c 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/varnode.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/varnode.hh @@ -416,21 +416,6 @@ public: #endif }; -/// \brief Node for a forward traversal of a Varnode expression -struct TraverseNode { - enum { - actionalt = 1, ///< Alternate path traverses a solid action or \e non-incidental COPY - indirect = 2, ///< Main path traverses an INDIRECT - indirectalt = 4, ///< Alternate path traverses an INDIRECT - lsb_truncated = 8, ///< Least significant byte(s) of original value have been truncated - concat_high = 0x10 ///< Original value has been concatented as \e most significant portion - }; - const Varnode *vn; ///< Varnode at the point of traversal - uint4 flags; ///< Flags associated with the node - TraverseNode(const Varnode *v,uint4 f) { vn = v; flags = f; } ///< Constructor - static bool isAlternatePathValid(const Varnode *vn,uint4 flags); -}; - bool contiguous_test(Varnode *vn1,Varnode *vn2); ///< Test if Varnodes are pieces of a whole Varnode *findContiguousWhole(Funcdata &data,Varnode *vn1, Varnode *vn2); ///< Retrieve the whole Varnode given pieces