Merge remote-tracking branch 'origin/GP-5934_SborrowScarry'

(Closes #8396, Closes #8400)
This commit is contained in:
Ryan Kurtz 2025-08-25 13:16:35 -04:00
commit 94212ab653
17 changed files with 898 additions and 670 deletions

View file

@ -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"

View file

@ -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 \

View file

@ -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.

View file

@ -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:

View file

@ -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") );

View file

@ -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

View 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

View 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

View file

@ -21,6 +21,7 @@
#include "emulateutil.hh" #include "emulateutil.hh"
#include "rangeutil.hh" #include "rangeutil.hh"
#include "expression.hh"
namespace ghidra { namespace ghidra {

View file

@ -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 {

View file

@ -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

View file

@ -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

View file

@ -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,69 +5779,57 @@ 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) { Varnode *hibit = getHiBit(feedOp);
coeff = feedOp->getIn(1); if (hibit != (Varnode *)0) { // Test for (hi ^ lo) s< 0
if (!coeff->isConstant()) return 0; if (hibit->isConstant())
if (coeff->getOffset() != calc_mask(coeff->getSize())) return 0; 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); avn = feedOp->getIn(0);
if (avn->isFree()) return 0; if (avn->isFree()) return 0;
data.opSetInput(op,avn,1); data.opSetInput(op,avn,1);
data.opSetInput(op,rvn,0); data.opSetInput(op,data.newConstant(avn->getSize(),calc_mask(avn->getSize())),0);
return 1; return 1;
} }
else { else if (feedOpCode == CPUI_INT_AND) {
Varnode *hibit = getHiBit(feedOp); avn = feedOp->getIn(0);
if (hibit != (Varnode *)0) { // Test for (hi ^ lo) s< 0 if (avn->isFree() || lvn->loneDescend() == (PcodeOp *)0)
if (hibit->isConstant()) return 0;
data.opSetInput(op,data.newConstant(hibit->getSize(),hibit->getOffset()),0); Varnode *maskVn = feedOp->getIn(1);
else if (maskVn->isConstant()) {
data.opSetInput(op,hibit,0); uintb mask = maskVn->getOffset();
data.opSetOpcode(op,CPUI_INT_NOTEQUAL); mask >>= (8 * avn->getSize() - 1); // Fetch sign-bit
return 1; if ((mask & 1) != 0) {
} // We have avn & 0x8... s< 0
else if (feedOpCode == CPUI_SUBPIECE) { data.opSetInput(op, avn, 0);
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; return 1;
} }
} }
else if (feedOpCode == CPUI_INT_NEGATE) { }
// We have ~avn s< 0 else if (feedOpCode == CPUI_PIECE) {
avn = feedOp->getIn(0); // We have CONCAT(V,W) s< 0
if (avn->isFree()) return 0; avn = feedOp->getIn(0); // Most significant piece
data.opSetInput(op,avn,1); if (avn->isFree())
data.opSetInput(op,data.newConstant(avn->getSize(),calc_mask(avn->getSize())),0); return 0;
return 1; data.opSetInput(op, avn, 0);
} data.opSetInput(op, data.newConstant(avn->getSize(), 0), 1);
else if (feedOpCode == CPUI_INT_AND) { return 1;
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;
}
} }
} }
} }

View file

@ -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

View file

@ -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 {

View file

@ -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

View file

@ -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