mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-03 17:59:46 +02:00
Merge remote-tracking branch 'origin/GP-5934_SborrowScarry'
(Closes #8396, Closes #8400)
This commit is contained in:
commit
94212ab653
17 changed files with 898 additions and 670 deletions
|
@ -86,6 +86,7 @@ model {
|
||||||
include "emulateutil.cc"
|
include "emulateutil.cc"
|
||||||
include "flow.cc"
|
include "flow.cc"
|
||||||
include "userop.cc"
|
include "userop.cc"
|
||||||
|
include "expression.cc"
|
||||||
include "multiprecision.cc"
|
include "multiprecision.cc"
|
||||||
include "funcdata.cc"
|
include "funcdata.cc"
|
||||||
include "funcdata_block.cc"
|
include "funcdata_block.cc"
|
||||||
|
|
|
@ -79,8 +79,8 @@ EXTERNAL_CONSOLEEXT_NAMES=$(subst .cc,,$(notdir $(EXTERNAL_CONSOLEEXT_SOURCE)))
|
||||||
CORE= xml marshal space float address pcoderaw translate opcodes globalcontext
|
CORE= xml marshal space float address pcoderaw translate opcodes globalcontext
|
||||||
# Additional core files for any projects that decompile
|
# Additional core files for any projects that decompile
|
||||||
DECCORE=capability architecture options graph cover block cast typeop database cpool \
|
DECCORE=capability architecture options graph cover block cast typeop database cpool \
|
||||||
comment stringmanage modelrules fspec action loadimage grammar varnode op \
|
comment stringmanage modelrules fspec action loadimage grammar varnode op type \
|
||||||
type variable varmap jumptable emulate emulateutil flow userop multiprecision \
|
variable varmap jumptable emulate emulateutil flow userop expression multiprecision \
|
||||||
funcdata funcdata_block funcdata_op funcdata_varnode unionresolve pcodeinject \
|
funcdata funcdata_block funcdata_op funcdata_varnode unionresolve pcodeinject \
|
||||||
heritage prefersplit rangeutil ruleaction subflow blockaction merge double \
|
heritage prefersplit rangeutil ruleaction subflow blockaction merge double \
|
||||||
transform constseq coreaction condexe override dynamic crc32 prettyprint \
|
transform constseq coreaction condexe override dynamic crc32 prettyprint \
|
||||||
|
|
|
@ -17,22 +17,6 @@
|
||||||
|
|
||||||
namespace ghidra {
|
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.
|
/// \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.
|
/// Used to test if all the links out of the iblock have been calculated.
|
||||||
|
|
|
@ -22,19 +22,6 @@
|
||||||
|
|
||||||
namespace ghidra {
|
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.
|
/// \brief A class for simplifying a series of conditionally executed statements.
|
||||||
///
|
///
|
||||||
/// This class tries to perform transformations like the following:
|
/// This class tries to perform transformations like the following:
|
||||||
|
|
|
@ -5307,66 +5307,6 @@ int4 ActionInferTypes::apply(Funcdata &data)
|
||||||
return 0;
|
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<PcodeOp *> opstack; // Depth first traversal path
|
|
||||||
vector<PcodeOp *> 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;i<curop->numInput();++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<AdditiveEdge>::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
|
/// (Re)build the default \e root Actions: decompile, jumptable, normalize, paramid, register, firstpass
|
||||||
void ActionDatabase::buildDefaultGroups(void)
|
void ActionDatabase::buildDefaultGroups(void)
|
||||||
|
|
||||||
|
@ -5469,6 +5409,7 @@ void ActionDatabase::universalAction(Architecture *conf)
|
||||||
actprop->addRule( new RulePullsubIndirect("analysis"));
|
actprop->addRule( new RulePullsubIndirect("analysis"));
|
||||||
actprop->addRule( new RulePushMulti("nodejoin"));
|
actprop->addRule( new RulePushMulti("nodejoin"));
|
||||||
actprop->addRule( new RuleSborrow("analysis") );
|
actprop->addRule( new RuleSborrow("analysis") );
|
||||||
|
actprop->addRule( new RuleScarry("analysis") );
|
||||||
actprop->addRule( new RuleIntLessEqual("analysis") );
|
actprop->addRule( new RuleIntLessEqual("analysis") );
|
||||||
actprop->addRule( new RuleTrivialArith("analysis") );
|
actprop->addRule( new RuleTrivialArith("analysis") );
|
||||||
actprop->addRule( new RuleTrivialBool("analysis") );
|
actprop->addRule( new RuleTrivialBool("analysis") );
|
||||||
|
|
|
@ -1065,47 +1065,5 @@ public:
|
||||||
bool valid(void) const { return (op != (PcodeOp *)0); } ///< Return \b true if there are edges left to iterate
|
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<AdditiveEdge> terms; ///< Collected terms
|
|
||||||
vector<AdditiveEdge *> 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<AdditiveEdge *> &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
|
} // End namespace ghidra
|
||||||
#endif
|
#endif
|
||||||
|
|
559
Ghidra/Features/Decompiler/src/decompile/cpp/expression.cc
Normal file
559
Ghidra/Features/Decompiler/src/decompile/cpp/expression.cc
Normal file
|
@ -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<PcodeOp *> opstack; // Depth first traversal path
|
||||||
|
vector<PcodeOp *> 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;i<curop->numInput();++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<AdditiveEdge>::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;i<num;++i) {
|
||||||
|
res1[i] = op1->getIn(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;i<num;++i)
|
||||||
|
if (functionalDifference(op1->getIn(i),op2->getIn(i),depth))
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // End namespace ghidra
|
180
Ghidra/Features/Decompiler/src/decompile/cpp/expression.hh
Normal file
180
Ghidra/Features/Decompiler/src/decompile/cpp/expression.hh
Normal file
|
@ -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<AdditiveEdge> terms; ///< Collected terms
|
||||||
|
vector<AdditiveEdge *> 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<AdditiveEdge *> &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
|
|
@ -21,6 +21,7 @@
|
||||||
|
|
||||||
#include "emulateutil.hh"
|
#include "emulateutil.hh"
|
||||||
#include "rangeutil.hh"
|
#include "rangeutil.hh"
|
||||||
|
#include "expression.hh"
|
||||||
|
|
||||||
namespace ghidra {
|
namespace ghidra {
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
/// \brief Utilities for merging low-level Varnodes into high-level variables
|
/// \brief Utilities for merging low-level Varnodes into high-level variables
|
||||||
|
|
||||||
#include "op.hh"
|
#include "op.hh"
|
||||||
|
#include "expression.hh"
|
||||||
|
|
||||||
namespace ghidra {
|
namespace ghidra {
|
||||||
|
|
||||||
|
|
|
@ -1184,329 +1184,4 @@ void PcodeOpBank::clear(void)
|
||||||
uniqid = 0;
|
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;i<num;++i) {
|
|
||||||
res1[i] = op1->getIn(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;i<num;++i)
|
|
||||||
if (functionalDifference(op1->getIn(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
|
} // End namespace ghidra
|
||||||
|
|
|
@ -251,18 +251,6 @@ public:
|
||||||
Datatype *inputTypeLocal(int4 slot) const { return opcode->getInputLocal(this,slot); } ///< Calculate the local input type
|
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
|
/// \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
|
/// If a group of Varnodes are concatenated into a larger structure, this object is used to explicitly gather
|
||||||
|
@ -362,49 +350,5 @@ public:
|
||||||
list<PcodeOp *>::const_iterator end(OpCode opc) const;
|
list<PcodeOp *>::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
|
} // End namespace ghidra
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -3361,27 +3361,26 @@ void RuleSborrow::getOpList(vector<uint4> &oplist) const
|
||||||
int4 RuleSborrow::applyOp(PcodeOp *op,Funcdata &data)
|
int4 RuleSborrow::applyOp(PcodeOp *op,Funcdata &data)
|
||||||
|
|
||||||
{
|
{
|
||||||
Varnode *svn = op->getOut();
|
|
||||||
Varnode *cvn,*avn,*bvn;
|
|
||||||
list<PcodeOp *>::const_iterator iter;
|
|
||||||
PcodeOp *compop,*signop,*addop;
|
|
||||||
int4 zside;
|
int4 zside;
|
||||||
|
|
||||||
|
Varnode *svn = op->getOut();
|
||||||
|
Varnode *avn = op->getIn(0);
|
||||||
|
Varnode *bvn = op->getIn(1);
|
||||||
// Check for trivial case
|
// Check for trivial case
|
||||||
if ((op->getIn(1)->isConstant()&&op->getIn(1)->getOffset()==0)||
|
if (bvn->isConstant()&&bvn->getOffset()==0) {
|
||||||
(op->getIn(0)->isConstant()&&op->getIn(0)->getOffset()==0)) {
|
|
||||||
data.opSetOpcode(op,CPUI_COPY);
|
data.opSetOpcode(op,CPUI_COPY);
|
||||||
data.opSetInput(op,data.newConstant(1,0),0);
|
data.opSetInput(op,data.newConstant(1,0),0);
|
||||||
data.opRemoveInput(op,1);
|
data.opRemoveInput(op,1);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
list<PcodeOp *>::const_iterator iter;
|
||||||
for(iter=svn->beginDescend();iter!=svn->endDescend();++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))
|
if ((compop->code()!=CPUI_INT_EQUAL)&&(compop->code()!=CPUI_INT_NOTEQUAL))
|
||||||
continue;
|
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;
|
if (!cvn->isWritten()) continue;
|
||||||
signop = cvn->getDef();
|
PcodeOp *signop = cvn->getDef();
|
||||||
if (signop->code() != CPUI_INT_SLESS) continue;
|
if (signop->code() != CPUI_INT_SLESS) continue;
|
||||||
if (!signop->getIn(0)->constantMatch(0)) {
|
if (!signop->getIn(0)->constantMatch(0)) {
|
||||||
if (!signop->getIn(1)->constantMatch(0)) continue;
|
if (!signop->getIn(1)->constantMatch(0)) continue;
|
||||||
|
@ -3389,36 +3388,13 @@ int4 RuleSborrow::applyOp(PcodeOp *op,Funcdata &data)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
zside = 0;
|
zside = 0;
|
||||||
if (!signop->getIn(1-zside)->isWritten()) continue;
|
Varnode *xvn = signop->getIn(1-zside);
|
||||||
addop = signop->getIn(1-zside)->getDef();
|
if (!xvn->isWritten()) continue;
|
||||||
if (addop->code() == CPUI_INT_ADD) {
|
AddExpression expr1;
|
||||||
avn = op->getIn(0);
|
expr1.gatherTwoTermsSubtract(avn, bvn);
|
||||||
if (functionalEquality(avn,addop->getIn(0)))
|
AddExpression expr2;
|
||||||
bvn = addop->getIn(1);
|
expr2.gatherTwoTermsRoot(xvn);
|
||||||
else if (functionalEquality(avn,addop->getIn(1)))
|
if (!expr1.isEquivalent(expr2))
|
||||||
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
|
|
||||||
continue;
|
continue;
|
||||||
if (compop->code() == CPUI_INT_NOTEQUAL) {
|
if (compop->code() == CPUI_INT_NOTEQUAL) {
|
||||||
data.opSetOpcode(compop,CPUI_INT_SLESS); // Replace all this with simple less than
|
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;
|
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<uint4> &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<PcodeOp *>::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
|
/// \class RuleTrivialShift
|
||||||
/// \brief Simplify trivial shifts: `V << 0 => V, V << #64 => 0`
|
/// \brief Simplify trivial shifts: `V << 0 => V, V << #64 => 0`
|
||||||
void RuleTrivialShift::getOpList(vector<uint4> &oplist) const
|
void RuleTrivialShift::getOpList(vector<uint4> &oplist) const
|
||||||
|
@ -5609,8 +5667,6 @@ Varnode *RuleSLess2Zero::getHiBit(PcodeOp *op)
|
||||||
/// \brief Simplify INT_SLESS applied to 0 or -1
|
/// \brief Simplify INT_SLESS applied to 0 or -1
|
||||||
///
|
///
|
||||||
/// Forms include:
|
/// Forms include:
|
||||||
/// - `0 s< V * -1 => V s< 0`
|
|
||||||
/// - `V * -1 s< 0 => 0 s< V`
|
|
||||||
/// - `-1 s< SUB(V,hi) => -1 s< V`
|
/// - `-1 s< SUB(V,hi) => -1 s< V`
|
||||||
/// - `SUB(V,hi) s< 0 => V s< 0`
|
/// - `SUB(V,hi) s< 0 => V s< 0`
|
||||||
/// - `-1 s< ~V => 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< CONCAT(V,W) => -1 s> V`
|
||||||
/// - `-1 s< (bool << #8*sz-1) => !bool`
|
/// - `-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
|
/// 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
|
/// built out of a high and low piece, where the high piece determines the
|
||||||
/// sign bit:
|
/// sign bit:
|
||||||
|
@ -5644,21 +5701,7 @@ int4 RuleSLess2Zero::applyOp(PcodeOp *op,Funcdata &data)
|
||||||
|
|
||||||
if (lvn->isConstant()) {
|
if (lvn->isConstant()) {
|
||||||
if (!rvn->isWritten()) return 0;
|
if (!rvn->isWritten()) return 0;
|
||||||
if (lvn->getOffset() == 0) {
|
if (lvn->getOffset() == calc_mask(lvn->getSize())) {
|
||||||
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())) {
|
|
||||||
feedOp = rvn->getDef();
|
feedOp = rvn->getDef();
|
||||||
feedOpCode = feedOp->code();
|
feedOpCode = feedOp->code();
|
||||||
Varnode *hibit = getHiBit(feedOp);
|
Varnode *hibit = getHiBit(feedOp);
|
||||||
|
@ -5736,17 +5779,6 @@ int4 RuleSLess2Zero::applyOp(PcodeOp *op,Funcdata &data)
|
||||||
if (rvn->getOffset() == 0) {
|
if (rvn->getOffset() == 0) {
|
||||||
feedOp = lvn->getDef();
|
feedOp = lvn->getDef();
|
||||||
feedOpCode = feedOp->code();
|
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,1);
|
|
||||||
data.opSetInput(op,rvn,0);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Varnode *hibit = getHiBit(feedOp);
|
Varnode *hibit = getHiBit(feedOp);
|
||||||
if (hibit != (Varnode *)0) { // Test for (hi ^ lo) s< 0
|
if (hibit != (Varnode *)0) { // Test for (hi ^ lo) s< 0
|
||||||
if (hibit->isConstant())
|
if (hibit->isConstant())
|
||||||
|
@ -5801,7 +5833,6 @@ int4 RuleSLess2Zero::applyOp(PcodeOp *op,Funcdata &data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -629,6 +629,16 @@ public:
|
||||||
virtual void getOpList(vector<uint4> &oplist) const;
|
virtual void getOpList(vector<uint4> &oplist) const;
|
||||||
virtual int4 applyOp(PcodeOp *op,Funcdata &data);
|
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<uint4> &oplist) const;
|
||||||
|
virtual int4 applyOp(PcodeOp *op,Funcdata &data);
|
||||||
|
};
|
||||||
class RuleTrivialShift : public Rule {
|
class RuleTrivialShift : public Rule {
|
||||||
public:
|
public:
|
||||||
RuleTrivialShift(const string &g) : Rule(g, 0, "trivialshift") {} ///< Constructor
|
RuleTrivialShift(const string &g) : Rule(g, 0, "trivialshift") {} ///< Constructor
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
*/
|
*/
|
||||||
#include "variable.hh"
|
#include "variable.hh"
|
||||||
#include "op.hh"
|
#include "op.hh"
|
||||||
|
#include "expression.hh"
|
||||||
#include "database.hh"
|
#include "database.hh"
|
||||||
|
|
||||||
namespace ghidra {
|
namespace ghidra {
|
||||||
|
|
|
@ -2006,36 +2006,6 @@ void VarnodeBank::verifyIntegrity(void) const
|
||||||
}
|
}
|
||||||
#endif
|
#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
|
/// Return true if \b vn1 contains the high part and \b vn2 the low part
|
||||||
/// of what was(is) a single value.
|
/// of what was(is) a single value.
|
||||||
/// \param vn1 is the putative high Varnode
|
/// \param vn1 is the putative high Varnode
|
||||||
|
|
|
@ -416,21 +416,6 @@ public:
|
||||||
#endif
|
#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
|
bool contiguous_test(Varnode *vn1,Varnode *vn2); ///< Test if Varnodes are pieces of a whole
|
||||||
Varnode *findContiguousWhole(Funcdata &data,Varnode *vn1,
|
Varnode *findContiguousWhole(Funcdata &data,Varnode *vn1,
|
||||||
Varnode *vn2); ///< Retrieve the whole Varnode given pieces
|
Varnode *vn2); ///< Retrieve the whole Varnode given pieces
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue