mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-04 02:09:44 +02:00
GP-5158 Conditional comparisons in AARCH64
This commit is contained in:
parent
f6dfa0964a
commit
2511498fcf
14 changed files with 777 additions and 471 deletions
|
@ -13,6 +13,7 @@ src/decompile/cpp/.gitignore||GHIDRA||||END|
|
||||||
src/decompile/cpp/Doxyfile||GHIDRA|||Most of this file is autogenerated by doxygen which falls under the GPL - output from GPL products are NOT GPL! - mjbell4|END|
|
src/decompile/cpp/Doxyfile||GHIDRA|||Most of this file is autogenerated by doxygen which falls under the GPL - output from GPL products are NOT GPL! - mjbell4|END|
|
||||||
src/decompile/cpp/Makefile||GHIDRA||||END|
|
src/decompile/cpp/Makefile||GHIDRA||||END|
|
||||||
src/decompile/datatests/boolless.xml||GHIDRA||||END|
|
src/decompile/datatests/boolless.xml||GHIDRA||||END|
|
||||||
|
src/decompile/datatests/ccmp.xml||GHIDRA||||END|
|
||||||
src/decompile/datatests/concat.xml||GHIDRA||||END|
|
src/decompile/datatests/concat.xml||GHIDRA||||END|
|
||||||
src/decompile/datatests/condconst.xml||GHIDRA||||END|
|
src/decompile/datatests/condconst.xml||GHIDRA||||END|
|
||||||
src/decompile/datatests/condmulti.xml||GHIDRA||||END|
|
src/decompile/datatests/condmulti.xml||GHIDRA||||END|
|
||||||
|
|
|
@ -19,172 +19,13 @@ namespace ghidra {
|
||||||
|
|
||||||
const int4 BooleanExpressionMatch::maxDepth = 1;
|
const int4 BooleanExpressionMatch::maxDepth = 1;
|
||||||
|
|
||||||
/// \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 BooleanExpressionMatch::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 BooleanExpressionMatch::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 BooleanExpressionMatch::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) {
|
|
||||||
if (varnodeSame(op1->getIn(0),op2->getIn(0)) && varnodeSame(op1->getIn(1),op2->getIn(1)))
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool BooleanExpressionMatch::verifyCondition(PcodeOp *op, PcodeOp *iop)
|
bool BooleanExpressionMatch::verifyCondition(PcodeOp *op, PcodeOp *iop)
|
||||||
|
|
||||||
{
|
{
|
||||||
int4 res = evaluate(op->getIn(1), iop->getIn(1), maxDepth);
|
int4 res = BooleanMatch::evaluate(op->getIn(1), iop->getIn(1), maxDepth);
|
||||||
if (res == uncorrelated)
|
if (res == BooleanMatch::uncorrelated)
|
||||||
return false;
|
return false;
|
||||||
matchflip = (res == complementary);
|
matchflip = (res == BooleanMatch::complementary);
|
||||||
if (op->isBooleanFlip())
|
if (op->isBooleanFlip())
|
||||||
matchflip = !matchflip;
|
matchflip = !matchflip;
|
||||||
if (iop->isBooleanFlip())
|
if (iop->isBooleanFlip())
|
||||||
|
|
|
@ -26,21 +26,9 @@ namespace ghidra {
|
||||||
///
|
///
|
||||||
/// This class determines if two CBRANCHs share the same condition. It also determines if the conditions
|
/// 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.
|
/// are complements of each other, and/or they are shared along only one path.
|
||||||
///
|
|
||||||
/// 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 BooleanExpressionMatch {
|
class BooleanExpressionMatch {
|
||||||
enum {
|
|
||||||
same = 1, ///< Pair always hold the same value
|
|
||||||
complementary = 2, ///< Pair always hold complementary values
|
|
||||||
uncorrelated = 3 ///< Pair values are uncorrelated
|
|
||||||
};
|
|
||||||
static const int4 maxDepth; ///< Maximum depth to trace a boolean expression
|
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
|
bool matchflip; ///< True if the compared CBRANCH keys on the opposite boolean value of the root
|
||||||
static bool sameOpComplement(PcodeOp *bin1op, PcodeOp *bin2op);
|
|
||||||
static bool varnodeSame(Varnode *a,Varnode *b);
|
|
||||||
static int4 evaluate(Varnode *vn1,Varnode *vn2,int4 depth);
|
|
||||||
public:
|
public:
|
||||||
bool verifyCondition(PcodeOp *op, PcodeOp *iop); ///< Perform the correlation test on two CBRANCH operations
|
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
|
int4 getMultiSlot(void) const { return -1; } ///< Get the MULTIEQUAL slot in the critical path
|
||||||
|
|
|
@ -5472,6 +5472,8 @@ void ActionDatabase::universalAction(Architecture *conf)
|
||||||
actprop->addRule( new RuleSlessToLess("analysis") );
|
actprop->addRule( new RuleSlessToLess("analysis") );
|
||||||
actprop->addRule( new RuleZextSless("analysis") );
|
actprop->addRule( new RuleZextSless("analysis") );
|
||||||
actprop->addRule( new RuleBitUndistribute("analysis") );
|
actprop->addRule( new RuleBitUndistribute("analysis") );
|
||||||
|
actprop->addRule( new RuleBooleanUndistribute("analysis") );
|
||||||
|
actprop->addRule( new RuleBooleanDedup("analysis") );
|
||||||
actprop->addRule( new RuleBoolZext("analysis") );
|
actprop->addRule( new RuleBoolZext("analysis") );
|
||||||
actprop->addRule( new RuleBooleanNegate("analysis") );
|
actprop->addRule( new RuleBooleanNegate("analysis") );
|
||||||
actprop->addRule( new RuleLogic2Bool("analysis") );
|
actprop->addRule( new RuleLogic2Bool("analysis") );
|
||||||
|
|
|
@ -123,10 +123,6 @@ class Funcdata {
|
||||||
void switchOverJumpTables(const FlowInfo &flow); ///< Convert jump-table addresses to basic block indices
|
void switchOverJumpTables(const FlowInfo &flow); ///< Convert jump-table addresses to basic block indices
|
||||||
void clearJumpTables(void); ///< Clear any jump-table information
|
void clearJumpTables(void); ///< Clear any jump-table information
|
||||||
BlockBasic *nodeSplitBlockEdge(BlockBasic *b,int4 inedge);
|
BlockBasic *nodeSplitBlockEdge(BlockBasic *b,int4 inedge);
|
||||||
PcodeOp *nodeSplitCloneOp(PcodeOp *op);
|
|
||||||
void nodeSplitCloneVarnode(PcodeOp *op,PcodeOp *newop);
|
|
||||||
void nodeSplitRawDuplicate(BlockBasic *b,BlockBasic *bprime);
|
|
||||||
void nodeSplitInputPatch(BlockBasic *b,BlockBasic *bprime,int4 inedge);
|
|
||||||
|
|
||||||
void sortCallSpecs(void); ///< Sort calls using a dominance based order
|
void sortCallSpecs(void); ///< Sort calls using a dominance based order
|
||||||
void deleteCallSpecs(PcodeOp *op); ///< Remove the specification for a particular call
|
void deleteCallSpecs(PcodeOp *op); ///< Remove the specification for a particular call
|
||||||
|
@ -487,6 +483,7 @@ public:
|
||||||
Varnode *createStackRef(AddrSpace *spc,uintb off,PcodeOp *op,Varnode *stackptr,bool insertafter);
|
Varnode *createStackRef(AddrSpace *spc,uintb off,PcodeOp *op,Varnode *stackptr,bool insertafter);
|
||||||
Varnode *opStackLoad(AddrSpace *spc,uintb off,uint4 sz,PcodeOp *op,Varnode *stackptr,bool insertafter);
|
Varnode *opStackLoad(AddrSpace *spc,uintb off,uint4 sz,PcodeOp *op,Varnode *stackptr,bool insertafter);
|
||||||
PcodeOp *opStackStore(AddrSpace *spc,uintb off,PcodeOp *op,bool insertafter);
|
PcodeOp *opStackStore(AddrSpace *spc,uintb off,PcodeOp *op,bool insertafter);
|
||||||
|
Varnode *opBoolNegate(Varnode *vn,PcodeOp *op,bool insertafter);
|
||||||
void opUndoPtradd(PcodeOp *op,bool finalize); ///< Convert a CPUI_PTRADD back into a CPUI_INT_ADD
|
void opUndoPtradd(PcodeOp *op,bool finalize); ///< Convert a CPUI_PTRADD back into a CPUI_INT_ADD
|
||||||
static int4 opFlipInPlaceTest(PcodeOp *op,vector<PcodeOp *> &fliplist);
|
static int4 opFlipInPlaceTest(PcodeOp *op,vector<PcodeOp *> &fliplist);
|
||||||
void opFlipInPlaceExecute(vector<PcodeOp *> &fliplist);
|
void opFlipInPlaceExecute(vector<PcodeOp *> &fliplist);
|
||||||
|
@ -618,6 +615,29 @@ public:
|
||||||
void setFuncdata(Funcdata *f) { fd = f; } ///< Establish the container for \b this emitter
|
void setFuncdata(Funcdata *f) { fd = f; } ///< Establish the container for \b this emitter
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// \brief Control the cloning of PcodeOps from within a basic block into another block
|
||||||
|
///
|
||||||
|
/// Used for splitting control-flow at a merge point. Can duplicate either a whole basic block, or an expression
|
||||||
|
/// subset within a basic block.
|
||||||
|
class CloneBlockOps {
|
||||||
|
/// \brief Helper class for pairing a p-code op with its clone
|
||||||
|
struct ClonePair {
|
||||||
|
PcodeOp *cloneOp; ///< New cloned op
|
||||||
|
PcodeOp *origOp; ///< Original op that was cloned
|
||||||
|
ClonePair(PcodeOp *c,PcodeOp *o) { cloneOp = c; origOp = o; } ///< Constructor
|
||||||
|
};
|
||||||
|
Funcdata &data;
|
||||||
|
vector<ClonePair> cloneList; ///< List of cloned ops
|
||||||
|
map<PcodeOp *,PcodeOp *> origToClone; ///< Map from original p-code op to its clone
|
||||||
|
PcodeOp *buildOpClone(PcodeOp *op); ///< Produce a skeleton copy of the given PcodeOp
|
||||||
|
void buildVarnodeOutput(PcodeOp *origOp,PcodeOp *cloneOp); ///< Clone the output Varnode of the given op onto its clone
|
||||||
|
void patchInputs(int4 inedge); ///< Set the input Varnodes of all cloned ops
|
||||||
|
public:
|
||||||
|
CloneBlockOps(Funcdata &fd) : data(fd) {} ///< Constructor
|
||||||
|
void cloneBlock(BlockBasic *b,BlockBasic *bprime,int4 inedge); ///< Clone all p-code ops from a block into its copy
|
||||||
|
Varnode *cloneExpression(vector<PcodeOp *> &ops,PcodeOp *followOp); ///< Clone p-code ops in an expression
|
||||||
|
};
|
||||||
|
|
||||||
/// \brief Helper class for determining if Varnodes can trace their value from a legitimate source
|
/// \brief Helper class for determining if Varnodes can trace their value from a legitimate source
|
||||||
///
|
///
|
||||||
/// Try to determine if a Varnode (expressed as a particular input to a CALL, CALLIND, or RETURN op)
|
/// Try to determine if a Varnode (expressed as a particular input to a CALL, CALLIND, or RETURN op)
|
||||||
|
|
|
@ -833,150 +833,6 @@ BlockBasic *Funcdata::nodeSplitBlockEdge(BlockBasic *b,int4 inedge)
|
||||||
return bprime;
|
return bprime;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// \brief Duplicate the given PcodeOp as part of splitting a block
|
|
||||||
///
|
|
||||||
/// Make a basic clone of the p-code op copying its basic control-flow properties
|
|
||||||
/// \param op is the given PcodeOp
|
|
||||||
/// \return the cloned op
|
|
||||||
PcodeOp *Funcdata::nodeSplitCloneOp(PcodeOp *op)
|
|
||||||
|
|
||||||
{
|
|
||||||
PcodeOp *dup;
|
|
||||||
|
|
||||||
if (op->isBranch()) {
|
|
||||||
if (op->code() != CPUI_BRANCH)
|
|
||||||
throw LowlevelError("Cannot duplicate 2-way or n-way branch in nodeplit");
|
|
||||||
return (PcodeOp *)0;
|
|
||||||
}
|
|
||||||
dup = newOp(op->numInput(),op->getAddr());
|
|
||||||
opSetOpcode(dup,op->code());
|
|
||||||
uint4 fl = op->flags & (PcodeOp::startbasic | PcodeOp::nocollapse |
|
|
||||||
PcodeOp::startmark);
|
|
||||||
dup->setFlag(fl);
|
|
||||||
return dup;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// \brief Duplicate output Varnode of the given p-code op, as part of splitting a block
|
|
||||||
///
|
|
||||||
/// Make a basic clone of the Varnode and its basic flags. The clone is created
|
|
||||||
/// as an output of a previously cloned PcodeOp.
|
|
||||||
/// \param op is the given op whose output should be cloned
|
|
||||||
/// \param newop is the cloned version
|
|
||||||
void Funcdata::nodeSplitCloneVarnode(PcodeOp *op,PcodeOp *newop)
|
|
||||||
|
|
||||||
{
|
|
||||||
Varnode *opvn = op->getOut();
|
|
||||||
Varnode *newvn;
|
|
||||||
|
|
||||||
if (opvn == (Varnode *)0) return;
|
|
||||||
newvn = newVarnodeOut(opvn->getSize(),opvn->getAddr(),newop);
|
|
||||||
uint4 vflags = opvn->getFlags();
|
|
||||||
vflags &= (Varnode::externref | Varnode::volatil | Varnode::incidental_copy |
|
|
||||||
Varnode::readonly | Varnode::persist |
|
|
||||||
Varnode::addrtied | Varnode::addrforce);
|
|
||||||
newvn->setFlags(vflags);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// \brief Clone all p-code ops from a block into its copy
|
|
||||||
///
|
|
||||||
/// P-code in a basic block is cloned into the split version of the block.
|
|
||||||
/// Only the output Varnodes are cloned, not the inputs.
|
|
||||||
/// \param b is the original basic block
|
|
||||||
/// \param bprime is the cloned block
|
|
||||||
void Funcdata::nodeSplitRawDuplicate(BlockBasic *b,BlockBasic *bprime)
|
|
||||||
|
|
||||||
{
|
|
||||||
PcodeOp *b_op,*prime_op;
|
|
||||||
list<PcodeOp *>::iterator iter;
|
|
||||||
|
|
||||||
for(iter=b->beginOp();iter!=b->endOp();++iter) {
|
|
||||||
b_op = *iter;
|
|
||||||
prime_op = nodeSplitCloneOp(b_op);
|
|
||||||
if (prime_op == (PcodeOp *)0) continue;
|
|
||||||
nodeSplitCloneVarnode(b_op,prime_op);
|
|
||||||
opInsertEnd(prime_op,bprime);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// \brief Patch Varnode inputs to p-code ops in split basic block
|
|
||||||
///
|
|
||||||
/// Map Varnodes that are inputs for PcodeOps in the original basic block to the
|
|
||||||
/// input slots of the cloned ops in the split block. Constants and code ref Varnodes
|
|
||||||
/// need to be duplicated, other Varnodes are shared between the ops. This routine
|
|
||||||
/// also pulls an input Varnode out of riginal MULTIEQUAL ops and adds it back
|
|
||||||
/// to the cloned MULTIEQUAL ops.
|
|
||||||
/// \param b is the original basic block
|
|
||||||
/// \param bprime is the split clone of the block
|
|
||||||
/// \param inedge is the incoming edge index that was split on
|
|
||||||
void Funcdata::nodeSplitInputPatch(BlockBasic *b,BlockBasic *bprime,int4 inedge)
|
|
||||||
|
|
||||||
{
|
|
||||||
list<PcodeOp *>::iterator biter,piter;
|
|
||||||
PcodeOp *bop,*pop;
|
|
||||||
Varnode *bvn,*pvn;
|
|
||||||
map<PcodeOp *,PcodeOp *> btop; // Map from b to bprime
|
|
||||||
vector<PcodeOp *> pind; // pop needing b input
|
|
||||||
vector<PcodeOp *> bind; // bop giving input
|
|
||||||
vector<int4> pslot; // slot within pop needing b input
|
|
||||||
|
|
||||||
biter = b->beginOp();
|
|
||||||
piter = bprime->beginOp();
|
|
||||||
|
|
||||||
while(piter != bprime->endOp()) {
|
|
||||||
bop = *biter;
|
|
||||||
pop = *piter;
|
|
||||||
btop[bop] = pop; // Establish mapping
|
|
||||||
if (bop->code() == CPUI_MULTIEQUAL) {
|
|
||||||
pop->setNumInputs(1); // One edge now goes into bprime
|
|
||||||
opSetOpcode(pop,CPUI_COPY);
|
|
||||||
opSetInput(pop,bop->getIn(inedge),0);
|
|
||||||
opRemoveInput(bop,inedge); // One edge is removed from b
|
|
||||||
if (bop->numInput() == 1)
|
|
||||||
opSetOpcode(bop,CPUI_COPY);
|
|
||||||
}
|
|
||||||
else if (bop->code() == CPUI_INDIRECT) {
|
|
||||||
throw LowlevelError("Can't handle INDIRECTs in nodesplit");
|
|
||||||
}
|
|
||||||
else if (bop->isCall()) {
|
|
||||||
throw LowlevelError("Can't handle CALLs in nodesplit");
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
for(int4 i=0;i<pop->numInput();++i) {
|
|
||||||
bvn = bop->getIn(i);
|
|
||||||
if (bvn->isConstant())
|
|
||||||
pvn = newConstant(bvn->getSize(),bvn->getOffset());
|
|
||||||
else if (bvn->isAnnotation())
|
|
||||||
pvn = newCodeRef(bvn->getAddr());
|
|
||||||
else if (bvn->isFree())
|
|
||||||
throw LowlevelError("Can't handle free varnode in nodesplit");
|
|
||||||
else {
|
|
||||||
if (bvn->isWritten()) {
|
|
||||||
if (bvn->getDef()->getParent() == b) {
|
|
||||||
pind.push_back(pop); // Need a cross reference
|
|
||||||
bind.push_back(bvn->getDef());
|
|
||||||
pslot.push_back(i);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
pvn = bvn;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
pvn = bvn;
|
|
||||||
}
|
|
||||||
opSetInput(pop,pvn,i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
++piter;
|
|
||||||
++biter;
|
|
||||||
}
|
|
||||||
|
|
||||||
for(int4 i=0;i<pind.size();++i) {
|
|
||||||
pop = pind[i];
|
|
||||||
PcodeOp *cross = btop[bind[i]];
|
|
||||||
opSetInput(pop,cross->getOut(),pslot[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// \brief Split control-flow into a basic block, duplicating its p-code into a new block
|
/// \brief Split control-flow into a basic block, duplicating its p-code into a new block
|
||||||
///
|
///
|
||||||
/// P-code is duplicated into another block, and control-flow is modified so that the new
|
/// P-code is duplicated into another block, and control-flow is modified so that the new
|
||||||
|
@ -1000,10 +856,8 @@ void Funcdata::nodeSplit(BlockBasic *b,int4 inedge)
|
||||||
|
|
||||||
// Create duplicate block
|
// Create duplicate block
|
||||||
BlockBasic *bprime = nodeSplitBlockEdge(b,inedge);
|
BlockBasic *bprime = nodeSplitBlockEdge(b,inedge);
|
||||||
// Make copy of b's ops
|
CloneBlockOps cloner(*this);
|
||||||
nodeSplitRawDuplicate(b,bprime);
|
cloner.cloneBlock(b, bprime, inedge); // Copy b's ops into bprime
|
||||||
// Patch up inputs based on split
|
|
||||||
nodeSplitInputPatch(b,bprime,inedge);
|
|
||||||
|
|
||||||
// We would need to patch outputs here for the more general
|
// We would need to patch outputs here for the more general
|
||||||
// case when b has out edges
|
// case when b has out edges
|
||||||
|
@ -1087,4 +941,152 @@ void Funcdata::spliceBlockBasic(BlockBasic *bl)
|
||||||
structureReset();
|
structureReset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Make a basic clone of the p-code op copying its basic control-flow properties.
|
||||||
|
/// In the case of a \e branch, the p-code op is not cloned and null is returned.
|
||||||
|
/// \param op is the given PcodeOp
|
||||||
|
/// \return the cloned op or null
|
||||||
|
PcodeOp *CloneBlockOps::buildOpClone(PcodeOp *op)
|
||||||
|
|
||||||
|
{
|
||||||
|
PcodeOp *dup;
|
||||||
|
|
||||||
|
if (op->isBranch()) {
|
||||||
|
if (op->code() != CPUI_BRANCH)
|
||||||
|
throw LowlevelError("Cannot duplicate 2-way or n-way branch in nodeplit");
|
||||||
|
return (PcodeOp *)0;
|
||||||
|
}
|
||||||
|
dup = data.newOp(op->numInput(),op->getAddr());
|
||||||
|
data.opSetOpcode(dup,op->code());
|
||||||
|
uint4 fl = op->flags & (PcodeOp::startbasic | PcodeOp::nocollapse | PcodeOp::startmark |
|
||||||
|
PcodeOp::nonprinting | PcodeOp::halt | PcodeOp::badinstruction | PcodeOp::unimplemented |
|
||||||
|
PcodeOp::noreturn | PcodeOp::missing | PcodeOp::indirect_creation | PcodeOp::indirect_store |
|
||||||
|
PcodeOp::no_indirect_collapse | PcodeOp::calculated_bool | PcodeOp::ptrflow);
|
||||||
|
dup->setFlag(fl);
|
||||||
|
fl = op->addlflags & (PcodeOp::special_prop | PcodeOp::special_print | PcodeOp::incidental_copy |
|
||||||
|
PcodeOp::is_cpool_transformed | PcodeOp::stop_type_propagation | PcodeOp::store_unmapped);
|
||||||
|
dup->setAdditionalFlag(fl);
|
||||||
|
|
||||||
|
cloneList.emplace_back(dup,op); // Map from clone to orig
|
||||||
|
origToClone[op] = dup; // Map from orig to clone
|
||||||
|
return dup;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Make a basic clone of the Varnode and its flags. The clone is created
|
||||||
|
/// as an output of a previously cloned PcodeOp.
|
||||||
|
/// \param op is the given op whose output should be cloned
|
||||||
|
/// \param newop is the cloned version
|
||||||
|
void CloneBlockOps::buildVarnodeOutput(PcodeOp *origOp,PcodeOp *cloneOp)
|
||||||
|
|
||||||
|
{
|
||||||
|
Varnode *opvn = origOp->getOut();
|
||||||
|
Varnode *newvn;
|
||||||
|
|
||||||
|
if (opvn == (Varnode *)0) return;
|
||||||
|
newvn = data.newVarnodeOut(opvn->getSize(),opvn->getAddr(),cloneOp);
|
||||||
|
uint4 vflags = opvn->getFlags();
|
||||||
|
vflags &= (Varnode::externref | Varnode::volatil | Varnode::incidental_copy | Varnode::readonly |
|
||||||
|
Varnode::persist | Varnode::addrtied | Varnode::addrforce | Varnode::nolocalalias | Varnode::spacebase |
|
||||||
|
Varnode::indirect_creation | Varnode::return_address | Varnode::precislo | Varnode::precishi |
|
||||||
|
Varnode::incidental_copy);
|
||||||
|
newvn->setFlags(vflags);
|
||||||
|
uint2 aflags = opvn->addlflags;
|
||||||
|
aflags &= (Varnode::writemask | Varnode::ptrflow | Varnode::stack_store);
|
||||||
|
newvn->addlflags |= aflags;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// P-code in a basic block is cloned into the split version of the block.
|
||||||
|
/// \param b is the original basic block
|
||||||
|
/// \param bprime is the cloned block
|
||||||
|
/// \param inedge is the incoming edge index that was split on
|
||||||
|
void CloneBlockOps::cloneBlock(BlockBasic *b,BlockBasic *bprime,int4 inedge)
|
||||||
|
|
||||||
|
{
|
||||||
|
PcodeOp *origOp,*cloneOp;
|
||||||
|
list<PcodeOp *>::iterator iter;
|
||||||
|
|
||||||
|
for(iter=b->beginOp();iter!=b->endOp();++iter) {
|
||||||
|
origOp = *iter;
|
||||||
|
cloneOp = buildOpClone(origOp);
|
||||||
|
if (cloneOp == (PcodeOp *)0) continue;
|
||||||
|
buildVarnodeOutput(origOp,cloneOp);
|
||||||
|
data.opInsertEnd(cloneOp,bprime);
|
||||||
|
}
|
||||||
|
patchInputs(inedge);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// P-code in the list is cloned right before the given \b followOp.
|
||||||
|
/// \param ops is the list of ops to clone
|
||||||
|
/// \param followOp is the point where the cloned ops are inserted
|
||||||
|
/// \return the output Varnode of the last cloned op
|
||||||
|
Varnode *CloneBlockOps::cloneExpression(vector<PcodeOp *> &ops,PcodeOp *followOp)
|
||||||
|
|
||||||
|
{
|
||||||
|
PcodeOp *origOp,*cloneOp;
|
||||||
|
for(int4 i=0;i<ops.size();++i) {
|
||||||
|
origOp = ops[i];
|
||||||
|
cloneOp = buildOpClone(origOp);
|
||||||
|
if (cloneOp == (PcodeOp *)0) continue;
|
||||||
|
buildVarnodeOutput(origOp,cloneOp);
|
||||||
|
data.opInsertBefore(cloneOp, followOp);
|
||||||
|
}
|
||||||
|
if (cloneList.empty())
|
||||||
|
throw LowlevelError("No expression to clone");
|
||||||
|
patchInputs(0);
|
||||||
|
cloneOp = cloneList.back().cloneOp;
|
||||||
|
return cloneOp->getOut();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Map Varnodes that are inputs for PcodeOps in the original basic block to the input slots of
|
||||||
|
/// the cloned ops. Constants and code ref Varnodes need to be duplicated, other Varnodes are shared
|
||||||
|
/// between the ops. This routine also pulls an input Varnode out of original MULTIEQUAL ops and adds
|
||||||
|
/// it back to the cloned MULTIEQUAL ops.
|
||||||
|
/// \param inedge is the incoming edge index that was split on
|
||||||
|
void CloneBlockOps::patchInputs(int4 inedge)
|
||||||
|
|
||||||
|
{
|
||||||
|
for(int4 pos=0;pos<cloneList.size();++pos) {
|
||||||
|
PcodeOp *origOp = cloneList[pos].origOp;
|
||||||
|
PcodeOp *cloneOp = cloneList[pos].cloneOp;
|
||||||
|
if (origOp->code() == CPUI_MULTIEQUAL) {
|
||||||
|
cloneOp->setNumInputs(1); // One edge now goes into the new block
|
||||||
|
data.opSetOpcode(cloneOp,CPUI_COPY);
|
||||||
|
data.opSetInput(cloneOp,origOp->getIn(inedge),0);
|
||||||
|
data.opRemoveInput(origOp,inedge); // One edge is removed from original block
|
||||||
|
if (origOp->numInput() == 1)
|
||||||
|
data.opSetOpcode(origOp,CPUI_COPY);
|
||||||
|
}
|
||||||
|
else if (origOp->code() == CPUI_INDIRECT) {
|
||||||
|
throw LowlevelError("Can't clone INDIRECTs");
|
||||||
|
}
|
||||||
|
else if (origOp->isCall()) {
|
||||||
|
throw LowlevelError("Can't clone CALLs");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for(int4 i=0;i<cloneOp->numInput();++i) {
|
||||||
|
Varnode *origVn = origOp->getIn(i);
|
||||||
|
Varnode *cloneVn;
|
||||||
|
if (origVn->isConstant())
|
||||||
|
cloneVn = origVn;
|
||||||
|
else if (origVn->isAnnotation())
|
||||||
|
cloneVn = data.newCodeRef(origVn->getAddr());
|
||||||
|
else if (origVn->isFree())
|
||||||
|
throw LowlevelError("Can't clone free varnode");
|
||||||
|
else {
|
||||||
|
if (origVn->isWritten()) {
|
||||||
|
map<PcodeOp *,PcodeOp *>::const_iterator iter = origToClone.find(origVn->getDef());
|
||||||
|
if (iter != origToClone.end()) {
|
||||||
|
cloneVn = (*iter).second->getOut();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
cloneVn = origVn;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
cloneVn = origVn;
|
||||||
|
}
|
||||||
|
data.opSetInput(cloneOp,cloneVn,i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // End namespace ghidra
|
} // End namespace ghidra
|
||||||
|
|
|
@ -526,6 +526,25 @@ Varnode *Funcdata::opStackLoad(AddrSpace *spc,uintb off,uint4 sz,PcodeOp *op,Var
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// \brief Construct the boolean negation of a given boolean Varnode into a temporary register
|
||||||
|
///
|
||||||
|
/// \param vn is the given Varnode
|
||||||
|
/// \param op is the point at which to insert the BOOL_NEGATE op
|
||||||
|
/// \return the result Varnode
|
||||||
|
Varnode *Funcdata::opBoolNegate(Varnode *vn,PcodeOp *op,bool insertafter)
|
||||||
|
|
||||||
|
{
|
||||||
|
PcodeOp *negateop = newOp(1,op->getAddr());
|
||||||
|
opSetOpcode(negateop,CPUI_BOOL_NEGATE);
|
||||||
|
Varnode *resvn = newUniqueOut(1,negateop);
|
||||||
|
opSetInput(negateop,vn,0);
|
||||||
|
if (insertafter)
|
||||||
|
opInsertAfter(negateop,op);
|
||||||
|
else
|
||||||
|
opInsertBefore(negateop,op);
|
||||||
|
return resvn;
|
||||||
|
}
|
||||||
|
|
||||||
/// Convert the given CPUI_PTRADD into the equivalent CPUI_INT_ADD. This may involve inserting a
|
/// Convert the given CPUI_PTRADD into the equivalent CPUI_INT_ADD. This may involve inserting a
|
||||||
/// CPUI_INT_MULT PcodeOp. If finalization is requested and a new PcodeOp is needed, the output
|
/// CPUI_INT_MULT PcodeOp. If finalization is requested and a new PcodeOp is needed, the output
|
||||||
/// Varnode is marked as \e implicit and has its data-type set
|
/// Varnode is marked as \e implicit and has its data-type set
|
||||||
|
|
|
@ -1340,4 +1340,163 @@ bool functionalDifference(Varnode *vn1,Varnode *vn2,int4 depth)
|
||||||
return false;
|
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) {
|
||||||
|
if (varnodeSame(op1->getIn(0),op2->getIn(0)) && varnodeSame(op1->getIn(1),op2->getIn(1)))
|
||||||
|
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
|
||||||
|
|
|
@ -63,6 +63,7 @@ public:
|
||||||
class PcodeOp {
|
class PcodeOp {
|
||||||
friend class BlockBasic; // Just insert_before, insert_after, setOrder
|
friend class BlockBasic; // Just insert_before, insert_after, setOrder
|
||||||
friend class Funcdata;
|
friend class Funcdata;
|
||||||
|
friend class CloneBlockOps;
|
||||||
friend class PcodeOpBank;
|
friend class PcodeOpBank;
|
||||||
friend class VarnodeBank; // Only uses setInput
|
friend class VarnodeBank; // Only uses setInput
|
||||||
public:
|
public:
|
||||||
|
@ -365,6 +366,23 @@ extern int4 functionalEqualityLevel(Varnode *vn1,Varnode *vn2,Varnode **res1,Var
|
||||||
extern bool functionalEquality(Varnode *vn1,Varnode *vn2);
|
extern bool functionalEquality(Varnode *vn1,Varnode *vn2);
|
||||||
extern bool functionalDifference(Varnode *vn1,Varnode *vn2,int4 depth);
|
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
|
/// Compare PcodeOps (as pointers) first, then slot
|
||||||
/// \param op2 is the other edge to compare with \b this
|
/// \param op2 is the other edge to compare with \b this
|
||||||
/// \return true if \b this should come before the other PcodeOp
|
/// \return true if \b this should come before the other PcodeOp
|
||||||
|
|
|
@ -2667,6 +2667,256 @@ int4 RuleBitUndistribute::applyOp(PcodeOp *op,Funcdata &data)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// \class RuleBooleanUndistribute
|
||||||
|
/// \brief Undo distributed BOOL_AND through INT_NOTEQUAL
|
||||||
|
///
|
||||||
|
/// - `A && B != A && C => A && (B != C)`
|
||||||
|
/// - `A || B == A || C => A || (B == C)`
|
||||||
|
/// - `A && B == A && C => !A || (B == C)`
|
||||||
|
void RuleBooleanUndistribute::getOpList(vector<uint4> &oplist) const
|
||||||
|
|
||||||
|
{
|
||||||
|
oplist.push_back(CPUI_INT_EQUAL);
|
||||||
|
oplist.push_back(CPUI_INT_NOTEQUAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// \brief Test if the two given Varnodes are matching boolean expressions
|
||||||
|
///
|
||||||
|
/// If the expressions are complementary, \b true is still returned, but the boolean parameter
|
||||||
|
/// is flipped.
|
||||||
|
/// \param leftVn is the first given expression to match
|
||||||
|
/// \param rightVn is the second given expression to match
|
||||||
|
/// \param rightFlip is flipped if the expressions are complementary
|
||||||
|
/// \return \b true if the expressions match
|
||||||
|
bool RuleBooleanUndistribute::isMatch(Varnode *leftVn,Varnode *rightVn,bool &rightFlip)
|
||||||
|
|
||||||
|
{
|
||||||
|
int4 val = BooleanMatch::evaluate(leftVn,rightVn,1);
|
||||||
|
if (val == BooleanMatch::same)
|
||||||
|
return true;
|
||||||
|
if (val == BooleanMatch::complementary) {
|
||||||
|
rightFlip = !rightFlip;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int4 RuleBooleanUndistribute::applyOp(PcodeOp *op,Funcdata &data)
|
||||||
|
|
||||||
|
{
|
||||||
|
Varnode *vn0 = op->getIn(0);
|
||||||
|
if (!vn0->isWritten()) return 0;
|
||||||
|
Varnode *vn1 = op->getIn(1);
|
||||||
|
if (!vn1->isWritten()) return 0;
|
||||||
|
PcodeOp *op0 = vn0->getDef();
|
||||||
|
OpCode opc0 = op0->code();
|
||||||
|
if (opc0 != CPUI_BOOL_AND && opc0 != CPUI_BOOL_OR) return 0;
|
||||||
|
PcodeOp *op1 = vn1->getDef();
|
||||||
|
OpCode opc1 = op1->code();
|
||||||
|
if (opc1 != CPUI_BOOL_AND && opc1 != CPUI_BOOL_OR) return 0;
|
||||||
|
Varnode *ins[4];
|
||||||
|
ins[0] = op0->getIn(0);
|
||||||
|
ins[1] = op0->getIn(1);
|
||||||
|
ins[2] = op1->getIn(0);
|
||||||
|
ins[3] = op1->getIn(1);
|
||||||
|
if (ins[0]->isFree() || ins[1]->isFree() || ins[2]->isFree() || ins[3]->isFree()) return 0;
|
||||||
|
bool isflipped[4];
|
||||||
|
isflipped[0] = isflipped[1] = isflipped[2] = isflipped[3] = false;
|
||||||
|
bool centralEqual = (op->code() == CPUI_INT_EQUAL);
|
||||||
|
if (opc0 == CPUI_BOOL_OR) {
|
||||||
|
isflipped[0] = !isflipped[0];
|
||||||
|
isflipped[1] = !isflipped[1];
|
||||||
|
centralEqual = !centralEqual;
|
||||||
|
}
|
||||||
|
if (opc1 == CPUI_BOOL_OR) {
|
||||||
|
isflipped[2] = !isflipped[2];
|
||||||
|
isflipped[3] = !isflipped[3];
|
||||||
|
centralEqual = !centralEqual;
|
||||||
|
}
|
||||||
|
int4 leftSlot,rightSlot;
|
||||||
|
if (isMatch(ins[0],ins[2],isflipped[2])) {
|
||||||
|
leftSlot = 0;
|
||||||
|
rightSlot = 2;
|
||||||
|
}
|
||||||
|
else if (isMatch(ins[0],ins[3],isflipped[3])) {
|
||||||
|
leftSlot = 0;
|
||||||
|
rightSlot = 3;
|
||||||
|
}
|
||||||
|
else if (isMatch(ins[1],ins[2],isflipped[2])) {
|
||||||
|
leftSlot = 1;
|
||||||
|
rightSlot = 2;
|
||||||
|
}
|
||||||
|
else if (isMatch(ins[1],ins[3],isflipped[3])) {
|
||||||
|
leftSlot = 1;
|
||||||
|
rightSlot = 3;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
if (isflipped[leftSlot] != isflipped[rightSlot]) return 0;
|
||||||
|
OpCode combineOpc;
|
||||||
|
if (centralEqual) {
|
||||||
|
combineOpc = CPUI_BOOL_OR;
|
||||||
|
isflipped[leftSlot] = !isflipped[leftSlot];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
combineOpc = CPUI_BOOL_AND;
|
||||||
|
}
|
||||||
|
Varnode *finalA = ins[leftSlot];
|
||||||
|
if (isflipped[leftSlot])
|
||||||
|
finalA = data.opBoolNegate(finalA, op, false);
|
||||||
|
if (isflipped[1-leftSlot])
|
||||||
|
centralEqual = !centralEqual;
|
||||||
|
if (isflipped[5-rightSlot])
|
||||||
|
centralEqual = !centralEqual;
|
||||||
|
Varnode *finalB = ins[1-leftSlot];
|
||||||
|
Varnode *finalC = ins[5-rightSlot];
|
||||||
|
PcodeOp *eqOp = data.newOp(2,op->getAddr());
|
||||||
|
data.opSetOpcode(eqOp, centralEqual ? CPUI_INT_EQUAL : CPUI_INT_NOTEQUAL);
|
||||||
|
Varnode *tmp1 = data.newUniqueOut(1, eqOp);
|
||||||
|
data.opSetInput(eqOp,finalB,0);
|
||||||
|
data.opSetInput(eqOp,finalC,1);
|
||||||
|
data.opInsertBefore(eqOp, op);
|
||||||
|
data.opSetOpcode(op, combineOpc);
|
||||||
|
data.opSetInput(op,tmp1,1);
|
||||||
|
data.opSetInput(op,finalA,0);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// \class RuleBooleanDedup
|
||||||
|
/// \brief Remove duplicate clauses in boolean expressions
|
||||||
|
///
|
||||||
|
/// - `(A && B) || (A && C) => A && (B || C)`
|
||||||
|
/// - `(A || B) && (A || C) => A || (B && C)`
|
||||||
|
/// - `(A || B) || (!A && C) => A || (B || C)`
|
||||||
|
/// - `(A && B) && (A && C) => A && (B && C)`
|
||||||
|
/// - `(A || B) || (A || C) => A || (B || C)`
|
||||||
|
void RuleBooleanDedup::getOpList(vector<uint4> &oplist) const
|
||||||
|
|
||||||
|
{
|
||||||
|
oplist.push_back(CPUI_BOOL_AND);
|
||||||
|
oplist.push_back(CPUI_BOOL_OR);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RuleBooleanDedup::isMatch(Varnode *leftVn,Varnode *rightVn,bool &isFlip)
|
||||||
|
|
||||||
|
{
|
||||||
|
int4 val = BooleanMatch::evaluate(leftVn,rightVn,1);
|
||||||
|
if (val == BooleanMatch::same) {
|
||||||
|
isFlip = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (val == BooleanMatch::complementary) {
|
||||||
|
isFlip = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int4 RuleBooleanDedup::applyOp(PcodeOp *op,Funcdata &data)
|
||||||
|
|
||||||
|
{
|
||||||
|
Varnode *vn0 = op->getIn(0);
|
||||||
|
if (!vn0->isWritten()) return 0;
|
||||||
|
Varnode *vn1 = op->getIn(1);
|
||||||
|
if (!vn1->isWritten()) return 0;
|
||||||
|
PcodeOp *op0 = vn0->getDef();
|
||||||
|
OpCode opc0 = op0->code();
|
||||||
|
if (opc0 != CPUI_BOOL_AND && opc0 != CPUI_BOOL_OR) return 0;
|
||||||
|
PcodeOp *op1 = vn1->getDef();
|
||||||
|
OpCode opc1 = op1->code();
|
||||||
|
if (opc1 != CPUI_BOOL_AND && opc1 != CPUI_BOOL_OR) return 0;
|
||||||
|
Varnode *ins[4];
|
||||||
|
ins[0] = op0->getIn(0);
|
||||||
|
ins[1] = op0->getIn(1);
|
||||||
|
ins[2] = op1->getIn(0);
|
||||||
|
ins[3] = op1->getIn(1);
|
||||||
|
if (ins[0]->isFree() || ins[1]->isFree() || ins[2]->isFree() || ins[3]->isFree()) return 0;
|
||||||
|
bool isflipped = false;
|
||||||
|
Varnode *leftA,*rightA;
|
||||||
|
Varnode *leftO,*rightO;
|
||||||
|
if (isMatch(ins[0],ins[2],isflipped)) {
|
||||||
|
leftA = ins[0];
|
||||||
|
rightA = ins[2];
|
||||||
|
leftO = ins[1];
|
||||||
|
rightO = ins[3];
|
||||||
|
}
|
||||||
|
else if (isMatch(ins[0],ins[3],isflipped)) {
|
||||||
|
leftA = ins[0];
|
||||||
|
rightA = ins[3];
|
||||||
|
leftO = ins[1];
|
||||||
|
rightO = ins[2];
|
||||||
|
}
|
||||||
|
else if (isMatch(ins[1],ins[2],isflipped)) {
|
||||||
|
leftA = ins[1];
|
||||||
|
rightA = ins[2];
|
||||||
|
leftO = ins[0];
|
||||||
|
rightO = ins[3];
|
||||||
|
}
|
||||||
|
else if (isMatch(ins[1],ins[3],isflipped)) {
|
||||||
|
leftA = ins[1];
|
||||||
|
rightA = ins[3];
|
||||||
|
leftO = ins[0];
|
||||||
|
rightO = ins[2];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
OpCode centralOpc = op->code();
|
||||||
|
OpCode bcOpc,finalOpc;
|
||||||
|
Varnode *finalA;
|
||||||
|
if (isflipped) {
|
||||||
|
if (centralOpc == CPUI_BOOL_AND && opc0 == CPUI_BOOL_AND && opc1 == CPUI_BOOL_AND) {
|
||||||
|
// (A && B) && (!A && C)
|
||||||
|
data.opSetOpcode(op, CPUI_COPY);
|
||||||
|
data.opRemoveInput(op, 1);
|
||||||
|
data.opSetInput(op,data.newConstant(1, 0),0); // Whole expression is false
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (centralOpc == CPUI_BOOL_OR && opc0 == CPUI_BOOL_OR && opc1 == CPUI_BOOL_OR) {
|
||||||
|
// (A || B) || (!A || C)
|
||||||
|
data.opSetOpcode(op, CPUI_COPY);
|
||||||
|
data.opRemoveInput(op, 1);
|
||||||
|
data.opSetInput(op,data.newConstant(1, 1),0); // Whole expression is true
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (centralOpc == CPUI_BOOL_OR && opc0 != opc1) {
|
||||||
|
// (A || B) || (!A && C)
|
||||||
|
finalA = (opc0 == CPUI_BOOL_OR) ? leftA : rightA;
|
||||||
|
finalOpc = CPUI_BOOL_OR;
|
||||||
|
bcOpc = CPUI_BOOL_OR;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (centralOpc == opc0 && centralOpc == opc1) {
|
||||||
|
// (A && B) && (A && C) or (A || B) || (A || C)
|
||||||
|
finalA = leftA;
|
||||||
|
finalOpc = centralOpc;
|
||||||
|
bcOpc = centralOpc;
|
||||||
|
}
|
||||||
|
else if (opc0 == opc1 && centralOpc != opc0) {
|
||||||
|
// (A && B) || (A && C) or (A || B) && (A || C)
|
||||||
|
finalA = leftA;
|
||||||
|
finalOpc = opc0;
|
||||||
|
bcOpc = centralOpc;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PcodeOp *bcOp = data.newOp(2,op->getAddr());
|
||||||
|
Varnode *tmp = data.newUniqueOut(1, bcOp);
|
||||||
|
data.opSetOpcode(bcOp, bcOpc);
|
||||||
|
data.opSetInput(bcOp, leftO, 0);
|
||||||
|
data.opSetInput(bcOp, rightO, 1);
|
||||||
|
data.opInsertBefore(bcOp, op);
|
||||||
|
data.opSetOpcode(op, finalOpc);
|
||||||
|
data.opSetInput(op, finalA, 0);
|
||||||
|
data.opSetInput(op, tmp, 1);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
/// \class RuleBooleanNegate
|
/// \class RuleBooleanNegate
|
||||||
/// \brief Simplify comparisons with boolean values: `V == false => !V, V == true => V`
|
/// \brief Simplify comparisons with boolean values: `V == false => !V, V == true => V`
|
||||||
///
|
///
|
||||||
|
@ -8925,128 +9175,90 @@ int4 RuleNegateNegate::applyOp(PcodeOp *op,Funcdata &data)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if given Varnode is a boolean value and break down its construction.
|
/// Check if the given Varnode is a boolean value and return the root of the expression.
|
||||||
/// Varnode is assumed to be an input to a MULTIEQUAL
|
/// The Varnode is assumed to be an input to a MULTIEQUAL.
|
||||||
/// \param vn is the given root Varnode
|
/// \param vn is the given Varnode
|
||||||
/// \return \b true if it is a boolean expression
|
/// \return null if the Varnode is not a boolean value, otherwise return the root Varnode of the expression
|
||||||
bool RuleConditionalMove::BoolExpress::initialize(Varnode *vn)
|
Varnode *RuleConditionalMove::checkBoolean(Varnode *vn)
|
||||||
|
|
||||||
{
|
{
|
||||||
if (!vn->isWritten()) return false;
|
if (!vn->isWritten()) return (Varnode *)0;
|
||||||
op = vn->getDef();
|
PcodeOp *op = vn->getDef();
|
||||||
opc = op->code();
|
if (op->isBoolOutput()) {
|
||||||
switch(opc) {
|
return vn;
|
||||||
case CPUI_COPY:
|
}
|
||||||
in0 = op->getIn(0);
|
if (op->code() == CPUI_COPY) {
|
||||||
if (in0->isConstant()) {
|
vn = op->getIn(0);
|
||||||
optype = 0;
|
if (vn->isConstant()) {
|
||||||
val = in0->getOffset();
|
uintb val = vn->getOffset();
|
||||||
return ((val & ~((uintb)1)) == 0);
|
if ((val & ~((uintb)1)) == 0)
|
||||||
|
return vn;
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
case CPUI_INT_EQUAL:
|
|
||||||
case CPUI_INT_NOTEQUAL:
|
|
||||||
case CPUI_INT_SLESS:
|
|
||||||
case CPUI_INT_SLESSEQUAL:
|
|
||||||
case CPUI_INT_LESS:
|
|
||||||
case CPUI_INT_LESSEQUAL:
|
|
||||||
case CPUI_INT_CARRY:
|
|
||||||
case CPUI_INT_SCARRY:
|
|
||||||
case CPUI_INT_SBORROW:
|
|
||||||
case CPUI_BOOL_XOR:
|
|
||||||
case CPUI_BOOL_AND:
|
|
||||||
case CPUI_BOOL_OR:
|
|
||||||
case CPUI_FLOAT_EQUAL:
|
|
||||||
case CPUI_FLOAT_NOTEQUAL:
|
|
||||||
case CPUI_FLOAT_LESS:
|
|
||||||
case CPUI_FLOAT_LESSEQUAL:
|
|
||||||
in0 = op->getIn(0);
|
|
||||||
in1 = op->getIn(1);
|
|
||||||
optype = 2;
|
|
||||||
break;
|
|
||||||
case CPUI_BOOL_NEGATE:
|
|
||||||
case CPUI_FLOAT_NAN:
|
|
||||||
in0 = op->getIn(0);
|
|
||||||
optype = 1;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
return true;
|
return (Varnode *)0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Evaluate if \b this expression can be easily propagated past a merge point.
|
/// \brief Determine if the given expression can be propagated out of the condition
|
||||||
/// Also can the Varnode be used past the merge, or does its value need to be reconstructed.
|
///
|
||||||
/// \param root is the split point
|
/// If p-code ops contributing to the expression are contained in a conditional branch, they are collected in
|
||||||
/// \param branch is the block on which the expression exists and after which is the merge
|
/// \b ops to later be pulled out of the branch (via duplication).
|
||||||
|
/// \param vn is the root of the given expression
|
||||||
|
/// \param ops will hold the set of ops that need to be duplicated
|
||||||
|
/// \param root is the block that performs the conditional branch
|
||||||
|
/// \param branch is the conditional branch
|
||||||
/// \return \b true if the expression can be propagated
|
/// \return \b true if the expression can be propagated
|
||||||
bool RuleConditionalMove::BoolExpress::evaluatePropagation(FlowBlock *root,FlowBlock *branch)
|
bool RuleConditionalMove::gatherExpression(Varnode *vn,vector<PcodeOp *> &ops,FlowBlock *root,FlowBlock *branch)
|
||||||
|
|
||||||
{
|
{
|
||||||
mustreconstruct = false;
|
if (vn->isConstant()) return true; // Constants can always be propagated
|
||||||
if (optype==0) return true; // Constants can always be propagated
|
if (vn->isFree()) return false;
|
||||||
|
if (vn->isAddrTied()) return false;
|
||||||
if (root == branch) return true; // Can always propagate if there is no branch
|
if (root == branch) return true; // Can always propagate if there is no branch
|
||||||
|
if (!vn->isWritten()) return true;
|
||||||
|
PcodeOp *op = vn->getDef();
|
||||||
if (op->getParent() != branch) return true; // Can propagate if value formed before branch
|
if (op->getParent() != branch) return true; // Can propagate if value formed before branch
|
||||||
mustreconstruct = true; // Final op is performed in branch, so it must be reconstructed
|
ops.push_back(op);
|
||||||
if (in0->isFree() && !in0->isConstant()) return false;
|
int4 pos = 0;
|
||||||
if (in0->isWritten() && (in0->getDef()->getParent()==branch)) return false;
|
while(pos < ops.size()) {
|
||||||
if (optype == 2) {
|
op = ops[pos];
|
||||||
if (in1->isFree() && !in1->isConstant()) return false;
|
pos += 1;
|
||||||
if (in1->isWritten() && (in1->getDef()->getParent()==branch)) return false;
|
if (op->getEvalType() == PcodeOp::special)
|
||||||
|
return false;
|
||||||
|
for(int4 i=0;i<op->numInput();++i) {
|
||||||
|
Varnode *in0 = op->getIn(i);
|
||||||
|
if (in0->isFree() && !in0->isConstant()) return false;
|
||||||
|
if (in0->isWritten() && (in0->getDef()->getParent()==branch)) {
|
||||||
|
if (in0->isAddrTied()) return false; // Don't pull out results that can be indirectly addressed
|
||||||
|
if (in0->loneDescend() != op) return false; // Don't pull out results with more than one use
|
||||||
|
if (ops.size() >= 4) return false;
|
||||||
|
ops.push_back(in0->getDef());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Produce the boolean Varnode to use after the merge.
|
/// Reproduce the bolean expression resulting in the given Varnode.
|
||||||
/// Either reuse the existing Varnode or reconstruct it,
|
/// Either reuse the existing Varnode or reconstruct it,
|
||||||
/// making sure the expression does not depend on data in the branch.
|
/// making sure the expression does not depend on data in the branch.
|
||||||
/// \param insertop is point at which any reconstruction should be inserted
|
/// \param insertop is point at which any reconstruction should be inserted
|
||||||
/// \param data is the function being analyzed
|
/// \param data is the function being analyzed
|
||||||
/// \return the Varnode representing the boolean expression
|
/// \return the Varnode representing the boolean expression
|
||||||
Varnode *RuleConditionalMove::BoolExpress::constructBool(PcodeOp *insertop,Funcdata &data)
|
Varnode *RuleConditionalMove::constructBool(Varnode *vn,PcodeOp *insertop,vector<PcodeOp *> &ops,Funcdata &data)
|
||||||
|
|
||||||
{
|
{
|
||||||
Varnode *resvn;
|
Varnode *resvn;
|
||||||
if (mustreconstruct) {
|
if (!ops.empty()) {
|
||||||
PcodeOp *newop = data.newOp(optype,op->getAddr()); // Keep the original address
|
sort(ops.begin(),ops.end(),compareOp);
|
||||||
data.opSetOpcode(newop, opc );
|
CloneBlockOps cloner(data);
|
||||||
resvn = data.newUniqueOut(1,newop);
|
resvn = cloner.cloneExpression(ops, insertop);
|
||||||
if (in0->isConstant())
|
|
||||||
in0 = data.newConstant(in0->getSize(),in0->getOffset());
|
|
||||||
data.opSetInput(newop,in0,0);
|
|
||||||
if (optype == 2) { // Binary op
|
|
||||||
if (in1->isConstant())
|
|
||||||
in1 = data.newConstant(in1->getSize(),in1->getOffset());
|
|
||||||
data.opSetInput(newop,in1,1);
|
|
||||||
}
|
|
||||||
data.opInsertBefore(newop,insertop);
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (optype == 0)
|
resvn = vn;
|
||||||
resvn = data.newConstant(1,val);
|
|
||||||
else
|
|
||||||
resvn = op->getOut();
|
|
||||||
}
|
}
|
||||||
return resvn;
|
return resvn;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// \brief Construct the boolean negation of a given boolean Varnode
|
|
||||||
///
|
|
||||||
/// \param vn is the given Varnode
|
|
||||||
/// \param op is the point at which to insert the BOOL_NEGATE op
|
|
||||||
/// \param data is the function being analyzed
|
|
||||||
/// \return the output of the new op
|
|
||||||
Varnode *RuleConditionalMove::constructNegate(Varnode *vn,PcodeOp *op,Funcdata &data)
|
|
||||||
|
|
||||||
{
|
|
||||||
PcodeOp *negateop = data.newOp(1,op->getAddr());
|
|
||||||
data.opSetOpcode(negateop,CPUI_BOOL_NEGATE);
|
|
||||||
Varnode *resvn = data.newUniqueOut(1,negateop);
|
|
||||||
data.opSetInput(negateop,vn,0);
|
|
||||||
data.opInsertBefore(negateop,op);
|
|
||||||
return resvn;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// \class RuleConditionalMove
|
/// \class RuleConditionalMove
|
||||||
/// \brief Simplify various conditional move situations
|
/// \brief Simplify various conditional move situations
|
||||||
///
|
///
|
||||||
|
@ -9079,16 +9291,16 @@ void RuleConditionalMove::getOpList(vector<uint4> &oplist) const
|
||||||
int4 RuleConditionalMove::applyOp(PcodeOp *op,Funcdata &data)
|
int4 RuleConditionalMove::applyOp(PcodeOp *op,Funcdata &data)
|
||||||
|
|
||||||
{
|
{
|
||||||
BoolExpress bool0;
|
|
||||||
BoolExpress bool1;
|
|
||||||
BlockBasic *bb;
|
BlockBasic *bb;
|
||||||
FlowBlock *inblock0,*inblock1;
|
FlowBlock *inblock0,*inblock1;
|
||||||
FlowBlock *rootblock0,*rootblock1;
|
FlowBlock *rootblock0,*rootblock1;
|
||||||
|
|
||||||
if (op->numInput() != 2) return 0; // MULTIEQUAL must have exactly 2 inputs
|
if (op->numInput() != 2) return 0; // MULTIEQUAL must have exactly 2 inputs
|
||||||
|
|
||||||
if (!bool0.initialize(op->getIn(0))) return 0;
|
Varnode *bool0 = checkBoolean(op->getIn(0));
|
||||||
if (!bool1.initialize(op->getIn(1))) return 0;
|
if (bool0 == (Varnode *)0) return 0;
|
||||||
|
Varnode *bool1 = checkBoolean(op->getIn(1));
|
||||||
|
if (bool1 == (Varnode *)0) return 0;
|
||||||
|
|
||||||
// Look for the situation
|
// Look for the situation
|
||||||
// inblock0
|
// inblock0
|
||||||
|
@ -9120,8 +9332,10 @@ int4 RuleConditionalMove::applyOp(PcodeOp *op,Funcdata &data)
|
||||||
if (cbranch == (PcodeOp *)0) return 0;
|
if (cbranch == (PcodeOp *)0) return 0;
|
||||||
if (cbranch->code() != CPUI_CBRANCH) return 0;
|
if (cbranch->code() != CPUI_CBRANCH) return 0;
|
||||||
|
|
||||||
if (!bool0.evaluatePropagation(rootblock0,inblock0)) return 0;
|
vector<PcodeOp *> opList0;
|
||||||
if (!bool1.evaluatePropagation(rootblock0,inblock1)) return 0;
|
if (!gatherExpression(bool0,opList0,rootblock0,inblock0)) return 0;
|
||||||
|
vector<PcodeOp *> opList1;
|
||||||
|
if (!gatherExpression(bool1,opList1,rootblock0,inblock1)) return 0;
|
||||||
|
|
||||||
bool path0istrue;
|
bool path0istrue;
|
||||||
if (rootblock0 != inblock0)
|
if (rootblock0 != inblock0)
|
||||||
|
@ -9131,7 +9345,7 @@ int4 RuleConditionalMove::applyOp(PcodeOp *op,Funcdata &data)
|
||||||
if (cbranch->isBooleanFlip())
|
if (cbranch->isBooleanFlip())
|
||||||
path0istrue = !path0istrue;
|
path0istrue = !path0istrue;
|
||||||
|
|
||||||
if (!bool0.isConstant() && !bool1.isConstant()) {
|
if (!bool0->isConstant() && !bool1->isConstant()) {
|
||||||
if (inblock0 == rootblock0) {
|
if (inblock0 == rootblock0) {
|
||||||
Varnode *boolvn = cbranch->getIn(1);
|
Varnode *boolvn = cbranch->getIn(1);
|
||||||
bool andorselect = path0istrue;
|
bool andorselect = path0istrue;
|
||||||
|
@ -9147,8 +9361,8 @@ int4 RuleConditionalMove::applyOp(PcodeOp *op,Funcdata &data)
|
||||||
data.opUninsert( op );
|
data.opUninsert( op );
|
||||||
data.opSetOpcode(op, opc);
|
data.opSetOpcode(op, opc);
|
||||||
data.opInsertBegin(op, bb);
|
data.opInsertBegin(op, bb);
|
||||||
Varnode *firstvn = bool0.constructBool(op,data);
|
Varnode *firstvn = constructBool(bool0,op,opList0,data);
|
||||||
Varnode *secondvn = bool1.constructBool(op,data);
|
Varnode *secondvn = constructBool(bool1,op,opList1,data);
|
||||||
data.opSetInput(op,firstvn,0);
|
data.opSetInput(op,firstvn,0);
|
||||||
data.opSetInput(op,secondvn,1);
|
data.opSetInput(op,secondvn,1);
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -9168,8 +9382,8 @@ int4 RuleConditionalMove::applyOp(PcodeOp *op,Funcdata &data)
|
||||||
OpCode opc = andorselect ? CPUI_BOOL_OR : CPUI_BOOL_AND;
|
OpCode opc = andorselect ? CPUI_BOOL_OR : CPUI_BOOL_AND;
|
||||||
data.opSetOpcode(op, opc);
|
data.opSetOpcode(op, opc);
|
||||||
data.opInsertBegin(op, bb);
|
data.opInsertBegin(op, bb);
|
||||||
Varnode *firstvn = bool1.constructBool(op,data);
|
Varnode *firstvn = constructBool(bool1,op,opList1,data);
|
||||||
Varnode *secondvn = bool0.constructBool(op,data);
|
Varnode *secondvn = constructBool(bool0,op,opList0,data);
|
||||||
data.opSetInput(op,firstvn,0);
|
data.opSetInput(op,firstvn,0);
|
||||||
data.opSetInput(op,secondvn,1);
|
data.opSetInput(op,secondvn,1);
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -9180,17 +9394,17 @@ int4 RuleConditionalMove::applyOp(PcodeOp *op,Funcdata &data)
|
||||||
// Below here some change is being made
|
// Below here some change is being made
|
||||||
data.opUninsert( op ); // Changing from MULTIEQUAL, this should be reinserted
|
data.opUninsert( op ); // Changing from MULTIEQUAL, this should be reinserted
|
||||||
int4 sz = op->getOut()->getSize();
|
int4 sz = op->getOut()->getSize();
|
||||||
if (bool0.isConstant() && bool1.isConstant()) {
|
if (bool0->isConstant() && bool1->isConstant()) {
|
||||||
if (bool0.getVal() == bool1.getVal()) {
|
if (bool0->getOffset() == bool1->getOffset()) {
|
||||||
data.opRemoveInput(op,1);
|
data.opRemoveInput(op,1);
|
||||||
data.opSetOpcode(op,CPUI_COPY);
|
data.opSetOpcode(op,CPUI_COPY);
|
||||||
data.opSetInput(op, data.newConstant( sz, bool0.getVal() ), 0 );
|
data.opSetInput(op, data.newConstant( sz, bool0->getOffset() ), 0 );
|
||||||
data.opInsertBegin(op,bb);
|
data.opInsertBegin(op,bb);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
data.opRemoveInput(op,1);
|
data.opRemoveInput(op,1);
|
||||||
Varnode *boolvn = cbranch->getIn(1);
|
Varnode *boolvn = cbranch->getIn(1);
|
||||||
bool needcomplement = ( (bool0.getVal()==0) == path0istrue );
|
bool needcomplement = ( (bool0->getOffset()==0) == path0istrue );
|
||||||
if (sz == 1) {
|
if (sz == 1) {
|
||||||
if (needcomplement)
|
if (needcomplement)
|
||||||
data.opSetOpcode(op,CPUI_BOOL_NEGATE);
|
data.opSetOpcode(op,CPUI_BOOL_NEGATE);
|
||||||
|
@ -9203,32 +9417,32 @@ int4 RuleConditionalMove::applyOp(PcodeOp *op,Funcdata &data)
|
||||||
data.opSetOpcode(op,CPUI_INT_ZEXT);
|
data.opSetOpcode(op,CPUI_INT_ZEXT);
|
||||||
data.opInsertBegin(op,bb);
|
data.opInsertBegin(op,bb);
|
||||||
if (needcomplement)
|
if (needcomplement)
|
||||||
boolvn = constructNegate(boolvn,op,data);
|
boolvn = data.opBoolNegate(boolvn,op,false);
|
||||||
data.opSetInput(op,boolvn,0);
|
data.opSetInput(op,boolvn,0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (bool0.isConstant()) {
|
else if (bool0->isConstant()) {
|
||||||
bool needcomplement = (path0istrue != (bool0.getVal()!=0));
|
bool needcomplement = (path0istrue != (bool0->getOffset()!=0));
|
||||||
OpCode opc = (bool0.getVal()!=0) ? CPUI_BOOL_OR : CPUI_BOOL_AND;
|
OpCode opc = (bool0->getOffset()!=0) ? CPUI_BOOL_OR : CPUI_BOOL_AND;
|
||||||
data.opSetOpcode(op,opc);
|
data.opSetOpcode(op,opc);
|
||||||
data.opInsertBegin(op,bb);
|
data.opInsertBegin(op,bb);
|
||||||
Varnode *boolvn = cbranch->getIn(1);
|
Varnode *boolvn = cbranch->getIn(1);
|
||||||
if (needcomplement)
|
if (needcomplement)
|
||||||
boolvn = constructNegate(boolvn,op,data);
|
boolvn = data.opBoolNegate(boolvn,op,false);
|
||||||
Varnode *body1 = bool1.constructBool(op,data);
|
Varnode *body1 = constructBool(bool1,op,opList1,data);
|
||||||
data.opSetInput(op,boolvn,0);
|
data.opSetInput(op,boolvn,0);
|
||||||
data.opSetInput(op,body1,1);
|
data.opSetInput(op,body1,1);
|
||||||
}
|
}
|
||||||
else { // bool1 must be constant
|
else { // bool1 must be constant
|
||||||
bool needcomplement = (path0istrue == (bool1.getVal()!=0));
|
bool needcomplement = (path0istrue == (bool1->getOffset()!=0));
|
||||||
OpCode opc = (bool1.getVal()!=0) ? CPUI_BOOL_OR : CPUI_BOOL_AND;
|
OpCode opc = (bool1->getOffset()!=0) ? CPUI_BOOL_OR : CPUI_BOOL_AND;
|
||||||
data.opSetOpcode(op,opc);
|
data.opSetOpcode(op,opc);
|
||||||
data.opInsertBegin(op,bb);
|
data.opInsertBegin(op,bb);
|
||||||
Varnode *boolvn = cbranch->getIn(1);
|
Varnode *boolvn = cbranch->getIn(1);
|
||||||
if (needcomplement)
|
if (needcomplement)
|
||||||
boolvn = constructNegate(boolvn,op,data);
|
boolvn = data.opBoolNegate(boolvn,op,false);
|
||||||
Varnode *body0 = bool0.constructBool(op,data);
|
Varnode *body0 = constructBool(bool0,op,opList0,data);
|
||||||
data.opSetInput(op,boolvn,0);
|
data.opSetInput(op,boolvn,0);
|
||||||
data.opSetInput(op,body0,1);
|
data.opSetInput(op,body0,1);
|
||||||
}
|
}
|
||||||
|
@ -9312,6 +9526,11 @@ bool RuleIgnoreNan::checkBackForCompare(Varnode *floatVar,Varnode *root)
|
||||||
if (!root->isWritten()) return false;
|
if (!root->isWritten()) return false;
|
||||||
PcodeOp *def1 = root->getDef();
|
PcodeOp *def1 = root->getDef();
|
||||||
if (!def1->isBoolOutput()) return false;
|
if (!def1->isBoolOutput()) return false;
|
||||||
|
if (def1->code() == CPUI_BOOL_NEGATE) {
|
||||||
|
Varnode *vn = def1->getIn(0);
|
||||||
|
if (!vn->isWritten()) return false;
|
||||||
|
def1 = vn->getDef();
|
||||||
|
}
|
||||||
if (def1->getOpcode()->isFloatingPointOp()) {
|
if (def1->getOpcode()->isFloatingPointOp()) {
|
||||||
if (def1->numInput() != 2) return false;
|
if (def1->numInput() != 2) return false;
|
||||||
if (functionalEquality(floatVar, def1->getIn(0)))
|
if (functionalEquality(floatVar, def1->getIn(0)))
|
||||||
|
@ -9321,7 +9540,7 @@ bool RuleIgnoreNan::checkBackForCompare(Varnode *floatVar,Varnode *root)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
OpCode opc = def1->code();
|
OpCode opc = def1->code();
|
||||||
if (opc != CPUI_BOOL_AND || opc != CPUI_BOOL_OR)
|
if (opc != CPUI_BOOL_AND && opc != CPUI_BOOL_OR)
|
||||||
return false;
|
return false;
|
||||||
for(int4 i=0;i<2;++i) {
|
for(int4 i=0;i<2;++i) {
|
||||||
Varnode *vn = def1->getIn(i);
|
Varnode *vn = def1->getIn(i);
|
||||||
|
|
|
@ -547,6 +547,28 @@ 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 RuleBooleanUndistribute : public Rule {
|
||||||
|
static bool isMatch(Varnode *leftVn,Varnode *rightVn,bool &rightFlip);
|
||||||
|
public:
|
||||||
|
RuleBooleanUndistribute(const string &g) : Rule(g, 0, "booleanundistribute") {} ///< Constructor
|
||||||
|
virtual Rule *clone(const ActionGroupList &grouplist) const {
|
||||||
|
if (!grouplist.contains(getGroup())) return (Rule *)0;
|
||||||
|
return new RuleBooleanUndistribute(getGroup());
|
||||||
|
}
|
||||||
|
virtual void getOpList(vector<uint4> &oplist) const;
|
||||||
|
virtual int4 applyOp(PcodeOp *op,Funcdata &data);
|
||||||
|
};
|
||||||
|
class RuleBooleanDedup : public Rule {
|
||||||
|
static bool isMatch(Varnode *leftVn,Varnode *rightVn,bool &isFlip);
|
||||||
|
public:
|
||||||
|
RuleBooleanDedup(const string &g) : Rule(g, 0, "booleandedup") {} ///< Constructor
|
||||||
|
virtual Rule *clone(const ActionGroupList &grouplist) const {
|
||||||
|
if (!grouplist.contains(getGroup())) return (Rule *)0;
|
||||||
|
return new RuleBooleanDedup(getGroup());
|
||||||
|
}
|
||||||
|
virtual void getOpList(vector<uint4> &oplist) const;
|
||||||
|
virtual int4 applyOp(PcodeOp *op,Funcdata &data);
|
||||||
|
};
|
||||||
class RuleBooleanNegate : public Rule {
|
class RuleBooleanNegate : public Rule {
|
||||||
public:
|
public:
|
||||||
RuleBooleanNegate(const string &g) : Rule(g, 0, "booleannegate") {} ///< Constructor
|
RuleBooleanNegate(const string &g) : Rule(g, 0, "booleannegate") {} ///< Constructor
|
||||||
|
@ -1394,23 +1416,10 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
class RuleConditionalMove : public Rule {
|
class RuleConditionalMove : public Rule {
|
||||||
/// \brief Class for categorizing and rebuilding a boolean expression
|
static Varnode *checkBoolean(Varnode *vn); ///< Check for boolean expression
|
||||||
class BoolExpress {
|
static bool gatherExpression(Varnode *vn,vector<PcodeOp *> &ops,FlowBlock *root,FlowBlock *branch);
|
||||||
int4 optype; ///< 0=constant 1=unary 2=binary
|
static Varnode *constructBool(Varnode *vn,PcodeOp *insertop,vector<PcodeOp *> &ops,Funcdata &data); ///< Construct the expression after the merge
|
||||||
OpCode opc; ///< OpCode constructing the boolean value
|
static bool compareOp(PcodeOp *op0,PcodeOp *op1) { return op0->getSeqNum().getOrder() < op1->getSeqNum().getOrder(); }
|
||||||
PcodeOp *op; ///< PcodeOp constructing the boolean value
|
|
||||||
uintb val; ///< Value (if boolean is constant)
|
|
||||||
Varnode *in0; ///< First input
|
|
||||||
Varnode *in1; ///< Second input
|
|
||||||
bool mustreconstruct; ///< Must make a copy of final boolean operation
|
|
||||||
public:
|
|
||||||
bool isConstant(void) const { return (optype==0); } ///< Return \b true if boolean is a constant
|
|
||||||
uintb getVal(void) const { return val; } ///< Get the constant boolean value
|
|
||||||
bool initialize(Varnode *vn); ///< Initialize based on output Varnode
|
|
||||||
bool evaluatePropagation(FlowBlock *root,FlowBlock *branch); ///< Can this expression be propagated
|
|
||||||
Varnode *constructBool(PcodeOp *insertop,Funcdata &data); ///< Construct the expression after the merge
|
|
||||||
};
|
|
||||||
static Varnode *constructNegate(Varnode *vn,PcodeOp *op,Funcdata &data);
|
|
||||||
public:
|
public:
|
||||||
RuleConditionalMove(const string &g) : Rule( g, 0, "conditionalmove") {} ///< Constructor
|
RuleConditionalMove(const string &g) : Rule( g, 0, "conditionalmove") {} ///< Constructor
|
||||||
virtual Rule *clone(const ActionGroupList &grouplist) const {
|
virtual Rule *clone(const ActionGroupList &grouplist) const {
|
||||||
|
|
|
@ -1315,7 +1315,7 @@ Datatype *TypeOpIntSub::getOutputToken(const PcodeOp *op,CastStrategy *castStrat
|
||||||
TypeOpIntCarry::TypeOpIntCarry(TypeFactory *t)
|
TypeOpIntCarry::TypeOpIntCarry(TypeFactory *t)
|
||||||
: TypeOpFunc(t,CPUI_INT_CARRY,"CARRY",TYPE_BOOL,TYPE_UINT)
|
: TypeOpFunc(t,CPUI_INT_CARRY,"CARRY",TYPE_BOOL,TYPE_UINT)
|
||||||
{
|
{
|
||||||
opflags = PcodeOp::binary | PcodeOp::commutative;
|
opflags = PcodeOp::binary | PcodeOp::commutative | PcodeOp::booloutput;
|
||||||
addlflags = arithmetic_op;
|
addlflags = arithmetic_op;
|
||||||
behave = new OpBehaviorIntCarry();
|
behave = new OpBehaviorIntCarry();
|
||||||
}
|
}
|
||||||
|
@ -1331,7 +1331,8 @@ string TypeOpIntCarry::getOperatorName(const PcodeOp *op) const
|
||||||
TypeOpIntScarry::TypeOpIntScarry(TypeFactory *t)
|
TypeOpIntScarry::TypeOpIntScarry(TypeFactory *t)
|
||||||
: TypeOpFunc(t,CPUI_INT_SCARRY,"SCARRY",TYPE_BOOL,TYPE_INT)
|
: TypeOpFunc(t,CPUI_INT_SCARRY,"SCARRY",TYPE_BOOL,TYPE_INT)
|
||||||
{
|
{
|
||||||
opflags = PcodeOp::binary | PcodeOp::commutative;
|
opflags = PcodeOp::binary | PcodeOp::commutative | PcodeOp::booloutput;
|
||||||
|
addlflags = arithmetic_op;
|
||||||
behave = new OpBehaviorIntScarry();
|
behave = new OpBehaviorIntScarry();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1346,7 +1347,7 @@ string TypeOpIntScarry::getOperatorName(const PcodeOp *op) const
|
||||||
TypeOpIntSborrow::TypeOpIntSborrow(TypeFactory *t)
|
TypeOpIntSborrow::TypeOpIntSborrow(TypeFactory *t)
|
||||||
: TypeOpFunc(t,CPUI_INT_SBORROW,"SBORROW",TYPE_BOOL,TYPE_INT)
|
: TypeOpFunc(t,CPUI_INT_SBORROW,"SBORROW",TYPE_BOOL,TYPE_INT)
|
||||||
{
|
{
|
||||||
opflags = PcodeOp::binary;
|
opflags = PcodeOp::binary | PcodeOp::booloutput;
|
||||||
addlflags = arithmetic_op;
|
addlflags = arithmetic_op;
|
||||||
behave = new OpBehaviorIntSborrow();
|
behave = new OpBehaviorIntSborrow();
|
||||||
}
|
}
|
||||||
|
|
|
@ -157,6 +157,7 @@ private:
|
||||||
friend class VarnodeBank;
|
friend class VarnodeBank;
|
||||||
friend class Merge;
|
friend class Merge;
|
||||||
friend class Funcdata;
|
friend class Funcdata;
|
||||||
|
friend class CloneBlockOps;
|
||||||
void updateCover(void) const; ///< Internal function for update coverage information
|
void updateCover(void) const; ///< Internal function for update coverage information
|
||||||
void calcCover(void) const; ///< Turn on the Cover object for this Varnode
|
void calcCover(void) const; ///< Turn on the Cover object for this Varnode
|
||||||
void clearCover(void) const; ///< Turn off any coverage information
|
void clearCover(void) const; ///< Turn off any coverage information
|
||||||
|
|
26
Ghidra/Features/Decompiler/src/decompile/datatests/ccmp.xml
Normal file
26
Ghidra/Features/Decompiler/src/decompile/datatests/ccmp.xml
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
<decompilertest>
|
||||||
|
<!--
|
||||||
|
Example of AARCH64 ccmp instruction. The boolean expression needs to be pulled
|
||||||
|
out of the conditional block.
|
||||||
|
-->
|
||||||
|
<binaryimage arch="AARCH64:LE:64:v8A:default">
|
||||||
|
<bytechunk space="ram" offset="0x42caec" readonly="true">
|
||||||
|
f403012a
|
||||||
|
f30300aa600640391ff00071800a497a
|
||||||
|
4c000054a1320394c0035fd6
|
||||||
|
</bytechunk>
|
||||||
|
<symbol space="ram" offset="0x42caec" name="ccmp"/>
|
||||||
|
<symbol space="ram" offset="0x4f9588" name="otherfunc"/>
|
||||||
|
</binaryimage>
|
||||||
|
<script>
|
||||||
|
<com>parse line extern void ccmp(uint1 *ptr,int4 val);</com>
|
||||||
|
<com>lo fu ccmp</com>
|
||||||
|
<com>decompile</com>
|
||||||
|
<com>print C</com>
|
||||||
|
<com>quit</com>
|
||||||
|
</script>
|
||||||
|
<stringmatch name="Ccmp test #1" min="1" max="1">if \(ptr\[1\] == 0x3c && val < 10\)</stringmatch>
|
||||||
|
<stringmatch name="Ccmp test #2" min="1" max="1">if</stringmatch>
|
||||||
|
<stringmatch name="Ccmp test #3" min="0" max="0">SBORROW</stringmatch>
|
||||||
|
<stringmatch name="Ccmp test #4" min="0" max="0">false</stringmatch>
|
||||||
|
</decompilertest>
|
Loading…
Add table
Add a link
Reference in a new issue