mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-03 09:49:23 +02:00
Merge remote-tracking branch
'origin/GP-5950_MultipathConditionalConstants' (Closes #8455)
This commit is contained in:
commit
48a7542e47
12 changed files with 366 additions and 94 deletions
|
@ -17,6 +17,8 @@ src/decompile/datatests/ccmp.xml||GHIDRA||||END|
|
||||||
src/decompile/datatests/concat.xml||GHIDRA||||END|
|
src/decompile/datatests/concat.xml||GHIDRA||||END|
|
||||||
src/decompile/datatests/concatsplit.xml||GHIDRA||||END|
|
src/decompile/datatests/concatsplit.xml||GHIDRA||||END|
|
||||||
src/decompile/datatests/condconst.xml||GHIDRA||||END|
|
src/decompile/datatests/condconst.xml||GHIDRA||||END|
|
||||||
|
src/decompile/datatests/condconst2.xml||GHIDRA||||END|
|
||||||
|
src/decompile/datatests/condconstsub.xml||GHIDRA||||END|
|
||||||
src/decompile/datatests/condexesub.xml||GHIDRA||||END|
|
src/decompile/datatests/condexesub.xml||GHIDRA||||END|
|
||||||
src/decompile/datatests/condmulti.xml||GHIDRA||||END|
|
src/decompile/datatests/condmulti.xml||GHIDRA||||END|
|
||||||
src/decompile/datatests/convert.xml||GHIDRA||||END|
|
src/decompile/datatests/convert.xml||GHIDRA||||END|
|
||||||
|
|
|
@ -4248,6 +4248,40 @@ void ActionConditionalConst::placeMultipleConstants(vector<PcodeOpNode> &phiNode
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// \brief Try to push the constant at the front point through to the output of the given PcodeOp
|
||||||
|
///
|
||||||
|
/// If successful, create a ConstPoint to search for reads of this new constant.
|
||||||
|
/// \param points is the set of points with the current point at the front
|
||||||
|
/// \param op is p-code op to push the constant through
|
||||||
|
void ActionConditionalConst::pushConstant(list<ConstPoint> &points,PcodeOp *op)
|
||||||
|
|
||||||
|
{
|
||||||
|
if ((op->getEvalType() & PcodeOp::special) != 0) return;
|
||||||
|
if (op->getOpcode()->isFloatingPointOp()) return;
|
||||||
|
Varnode *outvn = op->getOut();
|
||||||
|
if (outvn->getSize() > sizeof(uintb)) return;
|
||||||
|
Varnode *vn = points.front().vn;
|
||||||
|
int4 slot = op->getSlot(vn);
|
||||||
|
uintb in[3];
|
||||||
|
for(int4 i=0;i<op->numInput();++i) {
|
||||||
|
if (i == slot)
|
||||||
|
in[i] = points.front().value;
|
||||||
|
else {
|
||||||
|
Varnode *inVn = op->getIn(i);
|
||||||
|
if (inVn->getSize() > sizeof(uintb)) return;
|
||||||
|
if (inVn->isConstant())
|
||||||
|
in[i] = op->getIn(i)->getOffset();
|
||||||
|
else
|
||||||
|
return; // Not all inputs are constant
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool evalError;
|
||||||
|
uintb outval = op->executeSimple(in,evalError);
|
||||||
|
if (evalError)
|
||||||
|
return;
|
||||||
|
points.emplace_back(outvn,outval,points.front().constBlock,points.front().inSlot,points.front().blockIsDom);
|
||||||
|
}
|
||||||
|
|
||||||
/// \brief Replace MULTIEQUAL edges with constant if there is no alternate flow
|
/// \brief Replace MULTIEQUAL edges with constant if there is no alternate flow
|
||||||
///
|
///
|
||||||
/// A given Varnode is known to be constant along a set of MULTIEQUAL edges. If these edges are excised from the
|
/// A given Varnode is known to be constant along a set of MULTIEQUAL edges. If these edges are excised from the
|
||||||
|
@ -4297,57 +4331,107 @@ void ActionConditionalConst::handlePhiNodes(Varnode *varVn,Varnode *constVn,vect
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// \brief Replace reads of a given Varnode with a constant.
|
/// \brief Test if we can reach the given Varnode via a path other than through the immediate edge
|
||||||
///
|
///
|
||||||
/// For each read op, check that is in or dominated by a specific block we known
|
/// The given Varnode is an input to a MULTIEQUAL through a specific input slot. If we can reach the
|
||||||
/// the Varnode is constant in.
|
/// same Varnode backtracking through one of the other slots, return \b true. We can backtrack
|
||||||
/// \param varVn is the given Varnode
|
/// through MULTIEQUALs up to a given depth and possibly a final INT_ADD.
|
||||||
/// \param constVn is the constant Varnode to replace with (may be null)
|
/// \param vn is the given Varnode
|
||||||
/// \param constVal is the constant value being propagated
|
/// \param op is the MULTIEQUAL reading \b vn
|
||||||
/// \param constBlock is the block which dominates ops reading the constant value
|
/// \param slot is the input index of \b vn
|
||||||
|
/// \param depth is the maximum depth to backtrack
|
||||||
|
/// \return \b true if an alternate path to the Varnode is found
|
||||||
|
bool ActionConditionalConst::testAlternatePath(Varnode *vn,PcodeOp *op,int4 slot,int4 depth)
|
||||||
|
|
||||||
|
{
|
||||||
|
for(int4 i=0;i<op->numInput();++i) {
|
||||||
|
if (i == slot) continue;
|
||||||
|
Varnode *inVn = op->getIn(i);
|
||||||
|
if (inVn == vn) return true;
|
||||||
|
if (inVn->isWritten()) {
|
||||||
|
PcodeOp *curOp = inVn->getDef();
|
||||||
|
OpCode opc = curOp->code();
|
||||||
|
if (opc == CPUI_INT_ADD || opc == CPUI_PTRSUB || opc == CPUI_PTRADD) {
|
||||||
|
if (curOp->getIn(0) == vn || curOp->getIn(1) == vn)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (opc == CPUI_MULTIEQUAL) {
|
||||||
|
if (depth == 0) continue;
|
||||||
|
if (testAlternatePath(vn,curOp,-1,depth-1))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// \brief At each ConstPoint, replace reads of the Varnode down the constant path with a constant Varnode
|
||||||
|
///
|
||||||
|
/// Process ConstPoints from the front of the list.
|
||||||
|
/// For each read op of current point's Varnode, check if it is in the constant path.
|
||||||
|
/// If it is, replace the read with a new constant Varnode.
|
||||||
|
/// If not in the constant path, attempt to make the output Varnode into a new ConstPoint that may have
|
||||||
|
/// have reads in the constant path.
|
||||||
|
/// \param points is the list of ConstPoints
|
||||||
/// \param useMultiequal is \b true if conditional constants can be applied to MULTIEQUAL ops
|
/// \param useMultiequal is \b true if conditional constants can be applied to MULTIEQUAL ops
|
||||||
/// \param data is the function being analyzed
|
/// \param data is the function being analyzed
|
||||||
void ActionConditionalConst::propagateConstant(Varnode *varVn,Varnode *constVn,uintb constVal,
|
void ActionConditionalConst::propagateConstant(list<ConstPoint> &points,bool useMultiequal,Funcdata &data)
|
||||||
FlowBlock *constBlock,bool useMultiequal,Funcdata &data)
|
|
||||||
|
|
||||||
{
|
{
|
||||||
vector<PcodeOpNode> phiNodeEdges;
|
vector<PcodeOpNode> phiNodeEdges;
|
||||||
list<PcodeOp *>::const_iterator iter,enditer;
|
while(!points.empty()) {
|
||||||
iter = varVn->beginDescend();
|
ConstPoint &point(points.front());
|
||||||
enditer = varVn->endDescend();
|
Varnode *varVn = point.vn;
|
||||||
while(iter != enditer) {
|
Varnode *constVn = point.constVn;
|
||||||
PcodeOp *op = *iter;
|
FlowBlock *constBlock = point.constBlock;
|
||||||
while(iter != enditer && *iter == op)
|
list<PcodeOp *>::const_iterator iter = varVn->beginDescend();
|
||||||
++iter; // Advance iterator off of current op, as this descendant may be erased
|
list<PcodeOp *>::const_iterator enditer = varVn->endDescend();
|
||||||
OpCode opc = op->code();
|
while(iter != enditer) {
|
||||||
if (opc == CPUI_INDIRECT) // Don't propagate constant into these
|
PcodeOp *op = *iter;
|
||||||
continue;
|
while(iter != enditer && *iter == op)
|
||||||
else if (opc == CPUI_MULTIEQUAL) {
|
++iter; // Advance iterator off of current op, as this descendant may be erased
|
||||||
if (!useMultiequal)
|
OpCode opc = op->code();
|
||||||
|
if (opc == CPUI_INDIRECT) // Don't propagate constant into these
|
||||||
continue;
|
continue;
|
||||||
if (varVn->isAddrTied() && varVn->getAddr() == op->getOut()->getAddr())
|
else if (opc == CPUI_MULTIEQUAL) {
|
||||||
continue;
|
if (!useMultiequal)
|
||||||
FlowBlock *bl = op->getParent();
|
continue;
|
||||||
for(int4 slot=0;slot<op->numInput();++slot) {
|
if (varVn->isAddrTied() && varVn->getAddr() == op->getOut()->getAddr())
|
||||||
if (op->getIn(slot) == varVn) {
|
continue;
|
||||||
if (constBlock->dominates(bl->getIn(slot))) {
|
FlowBlock *bl = op->getParent();
|
||||||
phiNodeEdges.emplace_back(op,slot);
|
if (bl == constBlock) { // The immediate edge from the conditional block, coming into a MULTIEQUAL
|
||||||
|
if (op->getIn(point.inSlot) == varVn) {
|
||||||
|
// Its possible the compiler still intends the constant value to be the same variable
|
||||||
|
// Test for conditions when this is likely so we don't unnecessarily create a new variable
|
||||||
|
if (point.value > 1) continue;
|
||||||
|
if (op->getOut()->isAddrTied()) continue;
|
||||||
|
if (testAlternatePath(varVn, op, point.inSlot, 2)) continue;
|
||||||
|
phiNodeEdges.emplace_back(op,point.inSlot);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (point.blockIsDom) {
|
||||||
|
for(int4 slot=0;slot<op->numInput();++slot) {
|
||||||
|
if (op->getIn(slot) == varVn) {
|
||||||
|
if (constBlock->dominates(bl->getIn(slot))) {
|
||||||
|
phiNodeEdges.emplace_back(op,slot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
continue;
|
else if (opc == CPUI_COPY) { // Don't propagate into COPY unless...
|
||||||
}
|
PcodeOp *followOp = op->getOut()->loneDescend();
|
||||||
else if (opc == CPUI_COPY) { // Don't propagate into COPY unless...
|
if (followOp == (PcodeOp *)0) continue;
|
||||||
PcodeOp *followOp = op->getOut()->loneDescend();
|
if (followOp->isMarker()) continue;
|
||||||
if (followOp == (PcodeOp *)0) continue;
|
if (followOp->code() == CPUI_COPY) continue;
|
||||||
if (followOp->isMarker()) continue;
|
|
||||||
if (followOp->code() == CPUI_COPY) continue;
|
|
||||||
// ...unless COPY is into something more interesting
|
// ...unless COPY is into something more interesting
|
||||||
}
|
}
|
||||||
if (constBlock->dominates(op->getParent())) {
|
if (!point.blockIsDom) continue;
|
||||||
if (constVn == (Varnode *)0)
|
if (constBlock->dominates(op->getParent())) {
|
||||||
constVn = data.newConstant(varVn->getSize(), constVal);
|
if (constVn == (Varnode *)0)
|
||||||
if (opc == CPUI_RETURN){
|
constVn = data.newConstant(varVn->getSize(), point.value);
|
||||||
|
if (opc == CPUI_RETURN) {
|
||||||
// CPUI_RETURN ops can't directly take constants
|
// CPUI_RETURN ops can't directly take constants
|
||||||
// as inputs
|
// as inputs
|
||||||
PcodeOp *copyBeforeRet = data.newOp(1, op->getAddr());
|
PcodeOp *copyBeforeRet = data.newOp(1, op->getAddr());
|
||||||
|
@ -4356,19 +4440,70 @@ void ActionConditionalConst::propagateConstant(Varnode *varVn,Varnode *constVn,u
|
||||||
data.newVarnodeOut(varVn->getSize(),varVn->getAddr(),copyBeforeRet);
|
data.newVarnodeOut(varVn->getSize(),varVn->getAddr(),copyBeforeRet);
|
||||||
data.opSetInput(op,copyBeforeRet->getOut(),1);
|
data.opSetInput(op,copyBeforeRet->getOut(),1);
|
||||||
data.opInsertBefore(copyBeforeRet,op);
|
data.opInsertBefore(copyBeforeRet,op);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
int4 slot = op->getSlot(varVn);
|
||||||
|
data.opSetInput(op,constVn,slot); // Replace ref with constant!
|
||||||
|
}
|
||||||
|
count += 1; // We made a change
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
int4 slot = op->getSlot(varVn);
|
pushConstant(points, op);
|
||||||
data.opSetInput(op,constVn,slot); // Replace ref with constant!
|
|
||||||
}
|
}
|
||||||
count += 1; // We made a change
|
|
||||||
}
|
}
|
||||||
|
if (!phiNodeEdges.empty()) {
|
||||||
|
if (constVn == (Varnode *)0)
|
||||||
|
constVn = data.newConstant(varVn->getSize(), point.value);
|
||||||
|
handlePhiNodes(varVn, constVn, phiNodeEdges, data);
|
||||||
|
phiNodeEdges.clear();
|
||||||
|
}
|
||||||
|
points.pop_front();
|
||||||
}
|
}
|
||||||
if (!phiNodeEdges.empty()) {
|
}
|
||||||
if (constVn == (Varnode *)0)
|
|
||||||
constVn = data.newConstant(varVn->getSize(), constVal);
|
/// \brief Find a Varnode being compared to a constant creating the given CBRANCH boolean
|
||||||
handlePhiNodes(varVn, constVn, phiNodeEdges, data);
|
///
|
||||||
|
/// If the boolean is created by comparing a Varnode to a constant, create a ConstPoint record
|
||||||
|
/// indicating the path down which the Varnode can be considered constant.
|
||||||
|
/// \param points will hold any new ConstPoint
|
||||||
|
/// \param boolVn is the given CBRANCH boolean
|
||||||
|
/// \param bl is the block constaining the CBRANCH
|
||||||
|
/// \param blockDom is an array of booleans indicating along which out edges a constant could be pushed
|
||||||
|
/// \param flipEdge is \b true if the meaning of the CBRANCH has been flipped
|
||||||
|
void ActionConditionalConst::findConstCompare(list<ConstPoint> &points,Varnode *boolVn,FlowBlock *bl,
|
||||||
|
bool *blockDom,bool flipEdge)
|
||||||
|
{
|
||||||
|
if (!boolVn->isWritten()) return;
|
||||||
|
PcodeOp *compOp = boolVn->getDef();
|
||||||
|
OpCode opc = compOp->code();
|
||||||
|
if (opc == CPUI_BOOL_NEGATE) {
|
||||||
|
flipEdge = !flipEdge;
|
||||||
|
boolVn = compOp->getIn(0);
|
||||||
|
if (!boolVn->isWritten()) return;
|
||||||
|
compOp = boolVn->getDef();
|
||||||
|
opc = compOp->code();
|
||||||
}
|
}
|
||||||
|
int4 constEdge; // Out edge where value is constant
|
||||||
|
if (opc == CPUI_INT_EQUAL)
|
||||||
|
constEdge = 1;
|
||||||
|
else if (opc == CPUI_INT_NOTEQUAL)
|
||||||
|
constEdge = 0;
|
||||||
|
else
|
||||||
|
return;
|
||||||
|
// Find the variable and verify that it is compared to a constant
|
||||||
|
Varnode *varVn = compOp->getIn(0);
|
||||||
|
Varnode *constVn = compOp->getIn(1);
|
||||||
|
if (!constVn->isConstant()) {
|
||||||
|
if (!varVn->isConstant())
|
||||||
|
return;
|
||||||
|
Varnode *tmp = constVn;
|
||||||
|
constVn = varVn;
|
||||||
|
varVn = tmp;
|
||||||
|
}
|
||||||
|
if (varVn->loneDescend() != (PcodeOp *)0) return;
|
||||||
|
if (flipEdge)
|
||||||
|
constEdge = 1 - constEdge;
|
||||||
|
points.emplace_back(varVn,constVn,bl->getOut(constEdge),bl->getOutRevIndex(constEdge),blockDom[constEdge]);
|
||||||
}
|
}
|
||||||
|
|
||||||
int4 ActionConditionalConst::apply(Funcdata &data)
|
int4 ActionConditionalConst::apply(Funcdata &data)
|
||||||
|
@ -4384,55 +4519,23 @@ int4 ActionConditionalConst::apply(Funcdata &data)
|
||||||
useMultiequal = false; // Don't propagate into MULTIEQUAL
|
useMultiequal = false; // Don't propagate into MULTIEQUAL
|
||||||
}
|
}
|
||||||
const BlockGraph &blockGraph(data.getBasicBlocks());
|
const BlockGraph &blockGraph(data.getBasicBlocks());
|
||||||
bool blockdom[2];
|
bool blockDom[2];
|
||||||
|
list<ConstPoint> points;
|
||||||
for(int4 i=0;i<blockGraph.getSize();++i) {
|
for(int4 i=0;i<blockGraph.getSize();++i) {
|
||||||
FlowBlock *bl = blockGraph.getBlock(i);
|
FlowBlock *bl = blockGraph.getBlock(i);
|
||||||
PcodeOp *cBranch = bl->lastOp();
|
PcodeOp *cBranch = bl->lastOp();
|
||||||
if (cBranch == (PcodeOp *)0 || cBranch->code() != CPUI_CBRANCH) continue;
|
if (cBranch == (PcodeOp *)0 || cBranch->code() != CPUI_CBRANCH) continue;
|
||||||
Varnode *boolVn = cBranch->getIn(1);
|
Varnode *boolVn = cBranch->getIn(1);
|
||||||
blockdom[0] = bl->getOut(0)->restrictedByConditional(bl); // Make sure boolean constant holds down false branch
|
blockDom[0] = bl->getOut(0)->restrictedByConditional(bl); // Make sure boolean constant holds down false branch
|
||||||
blockdom[1] = bl->getOut(1)->restrictedByConditional(bl);
|
blockDom[1] = bl->getOut(1)->restrictedByConditional(bl);
|
||||||
if (!blockdom[0] && !blockdom[1]) continue;
|
|
||||||
bool flipEdge = cBranch->isBooleanFlip();
|
bool flipEdge = cBranch->isBooleanFlip();
|
||||||
if (boolVn->loneDescend() == (PcodeOp *)0) { // If the boolean is read more than once
|
if (boolVn->loneDescend() == (PcodeOp *)0) { // If the boolean is read more than once
|
||||||
// Search for implied constants, bool=0 down false branch, bool=1 down true branch
|
// Search for implied constants, bool=0 down false branch, bool=1 down true branch
|
||||||
if (blockdom[0])
|
points.emplace_back(boolVn, flipEdge ? 1 : 0, bl->getFalseOut(),bl->getOutRevIndex(0),blockDom[0]);
|
||||||
propagateConstant(boolVn, (Varnode *)0, flipEdge ? 1 : 0, bl->getFalseOut(), useMultiequal, data);
|
points.emplace_back(boolVn, flipEdge ? 0 : 1, bl->getTrueOut(),bl->getOutRevIndex(1),blockDom[1]);
|
||||||
if (blockdom[1])
|
|
||||||
propagateConstant(boolVn, (Varnode *)0, flipEdge ? 0 : 1, bl->getTrueOut(), useMultiequal, data);
|
|
||||||
}
|
}
|
||||||
if (!boolVn->isWritten()) continue;
|
findConstCompare(points, boolVn, bl, blockDom, flipEdge);
|
||||||
PcodeOp *compOp = boolVn->getDef();
|
propagateConstant(points, useMultiequal, data);
|
||||||
OpCode opc = compOp->code();
|
|
||||||
if (opc == CPUI_BOOL_NEGATE) {
|
|
||||||
flipEdge = !flipEdge;
|
|
||||||
boolVn = compOp->getIn(0);
|
|
||||||
if (!boolVn->isWritten()) continue;
|
|
||||||
compOp = boolVn->getDef();
|
|
||||||
opc = compOp->code();
|
|
||||||
}
|
|
||||||
int4 constEdge; // Out edge where value is constant
|
|
||||||
if (opc == CPUI_INT_EQUAL)
|
|
||||||
constEdge = 1;
|
|
||||||
else if (opc == CPUI_INT_NOTEQUAL)
|
|
||||||
constEdge = 0;
|
|
||||||
else
|
|
||||||
continue;
|
|
||||||
// Find the variable and verify that it is compared to a constant
|
|
||||||
Varnode *varVn = compOp->getIn(0);
|
|
||||||
Varnode *constVn = compOp->getIn(1);
|
|
||||||
if (!constVn->isConstant()) {
|
|
||||||
if (!varVn->isConstant())
|
|
||||||
continue;
|
|
||||||
Varnode *tmp = constVn;
|
|
||||||
constVn = varVn;
|
|
||||||
varVn = tmp;
|
|
||||||
}
|
|
||||||
if (varVn->loneDescend() != (PcodeOp *)0) continue;
|
|
||||||
if (flipEdge)
|
|
||||||
constEdge = 1 - constEdge;
|
|
||||||
if (!blockdom[constEdge]) continue; // Make sure condition holds
|
|
||||||
propagateConstant(varVn,constVn,0,bl->getOut(constEdge),useMultiequal,data);
|
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -567,14 +567,30 @@ public:
|
||||||
|
|
||||||
/// \brief Propagate conditional constants
|
/// \brief Propagate conditional constants
|
||||||
class ActionConditionalConst : public Action {
|
class ActionConditionalConst : public Action {
|
||||||
|
/// \brief Description of a point in control-flow where a Varnode can propagate as a constant down a conditional branch
|
||||||
|
struct ConstPoint {
|
||||||
|
Varnode *vn; ///< Varnode that is constant for some reads
|
||||||
|
Varnode *constVn; ///< Representative of the constant (may be null)
|
||||||
|
uintb value; ///< The constant value
|
||||||
|
FlowBlock *constBlock; ///< Block that dominates all reads where vn is constant
|
||||||
|
int4 inSlot; ///< Input edge from condition block
|
||||||
|
bool blockIsDom; ///< Is \b true if block is dominated by constant path
|
||||||
|
ConstPoint(Varnode *v,Varnode *c,FlowBlock *bl,int4 slot,bool isDom) {
|
||||||
|
vn = v; constVn = c; value = c->getOffset(); constBlock = bl; inSlot = slot; blockIsDom = isDom; } ///< Construct from constant Varnode
|
||||||
|
ConstPoint(Varnode *v,uintb val,FlowBlock *bl,int4 slot,bool isDom) {
|
||||||
|
vn = v; constVn = (Varnode *)0; value = val; constBlock = bl; inSlot = slot; blockIsDom = isDom; } ///< Construct from constant value
|
||||||
|
};
|
||||||
static void clearMarks(const vector<PcodeOp *> &opList);
|
static void clearMarks(const vector<PcodeOp *> &opList);
|
||||||
static void collectReachable(Varnode *vn,vector<PcodeOpNode> &phiNodeEdges,vector<PcodeOp *> &reachable);
|
static void collectReachable(Varnode *vn,vector<PcodeOpNode> &phiNodeEdges,vector<PcodeOp *> &reachable);
|
||||||
static bool flowToAlternatePath(PcodeOp *op);
|
static bool flowToAlternatePath(PcodeOp *op);
|
||||||
static bool flowTogether(const vector<PcodeOpNode> &edges,int4 i,vector<int4> &result);
|
static bool flowTogether(const vector<PcodeOpNode> &edges,int4 i,vector<int4> &result);
|
||||||
static Varnode *placeCopy(PcodeOp *op,BlockBasic *bl,Varnode *constVn,Funcdata &data);
|
static Varnode *placeCopy(PcodeOp *op,BlockBasic *bl,Varnode *constVn,Funcdata &data);
|
||||||
|
static void findConstCompare(list<ConstPoint> &points,Varnode *boolVn,FlowBlock *bl,bool *blockDom,bool flipEdge);
|
||||||
|
static void pushConstant(list<ConstPoint> &points,PcodeOp *op);
|
||||||
static void placeMultipleConstants(vector<PcodeOpNode> &phiNodeEdges,vector<int4> &marks,Varnode *constVn,Funcdata &data);
|
static void placeMultipleConstants(vector<PcodeOpNode> &phiNodeEdges,vector<int4> &marks,Varnode *constVn,Funcdata &data);
|
||||||
void handlePhiNodes(Varnode *varVn,Varnode *constVn,vector<PcodeOpNode> &phiNodeEdges,Funcdata &data);
|
void handlePhiNodes(Varnode *varVn,Varnode *constVn,vector<PcodeOpNode> &phiNodeEdges,Funcdata &data);
|
||||||
void propagateConstant(Varnode *varVn,Varnode *constVn,uintb constVal,FlowBlock *constBlock,bool useMultiequal,Funcdata &data);
|
bool testAlternatePath(Varnode *vn,PcodeOp *op,int4 slot,int4 depth);
|
||||||
|
void propagateConstant(list<ConstPoint> &points,bool useMultiequal,Funcdata &data);
|
||||||
public:
|
public:
|
||||||
ActionConditionalConst(const string &g) : Action(0,"condconst",g) {} ///< Constructor
|
ActionConditionalConst(const string &g) : Action(0,"condconst",g) {} ///< Constructor
|
||||||
virtual Action *clone(const ActionGroupList &grouplist) const {
|
virtual Action *clone(const ActionGroupList &grouplist) const {
|
||||||
|
|
|
@ -2169,6 +2169,7 @@ bool LessThreeWay::normalizeHi(void)
|
||||||
}
|
}
|
||||||
hiconstform = false;
|
hiconstform = false;
|
||||||
if (vnhil2->isConstant()) {
|
if (vnhil2->isConstant()) {
|
||||||
|
if (in.getSize() > sizeof(uintb)) return false; // Must have enough precision for constant
|
||||||
hiconstform = true;
|
hiconstform = true;
|
||||||
hival = vnhil2->getOffset();
|
hival = vnhil2->getOffset();
|
||||||
SplitVarnode::getTrueFalse(hilessbool,hiflip,hilesstrue,hilessfalse);
|
SplitVarnode::getTrueFalse(hilessbool,hiflip,hilesstrue,hilessfalse);
|
||||||
|
|
|
@ -471,6 +471,32 @@ uintb PcodeOp::collapse(bool &markedInput) const {
|
||||||
throw LowlevelError("Invalid constant collapse");
|
throw LowlevelError("Invalid constant collapse");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The p-code op must be \e special, or an exception is thrown. The operation is performed
|
||||||
|
/// and if there is no evaluation error, the result is returned and \b evalError is set to \b false.
|
||||||
|
/// \param in is an array of input values
|
||||||
|
/// \return the result of applying \b this operation to the input values
|
||||||
|
uintb PcodeOp::executeSimple(uintb *in,bool &evalError) const
|
||||||
|
|
||||||
|
{
|
||||||
|
uint4 evalType = getEvalType();
|
||||||
|
uintb res;
|
||||||
|
try {
|
||||||
|
if (evalType == PcodeOp::unary)
|
||||||
|
res = opcode->evaluateUnary(output->getSize(),inrefs[0]->getSize(),in[0]);
|
||||||
|
else if (evalType == PcodeOp::binary)
|
||||||
|
res = opcode->evaluateBinary(output->getSize(),inrefs[0]->getSize(),in[0],in[1]);
|
||||||
|
else if (evalType == PcodeOp::ternary)
|
||||||
|
res = opcode->evaluateTernary(output->getSize(),inrefs[0]->getSize(),in[0],in[1],in[2]);
|
||||||
|
else
|
||||||
|
throw LowlevelError("Cannot perform simple execution of "+(string)get_opname(code()));
|
||||||
|
} catch(EvaluationError &err) {
|
||||||
|
evalError = true;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
evalError = false;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
/// Knowing that \b this PcodeOp has collapsed its constant inputs, one of which has
|
/// Knowing that \b this PcodeOp has collapsed its constant inputs, one of which has
|
||||||
/// symbol content, figure out if the symbol should propagate to the new given output constant.
|
/// symbol content, figure out if the symbol should propagate to the new given output constant.
|
||||||
/// \param newConst is the given output constant
|
/// \param newConst is the given output constant
|
||||||
|
|
|
@ -233,6 +233,7 @@ public:
|
||||||
OpCode code(void) const { return opcode->getOpcode(); } ///< Get the opcode id (enum) for this op
|
OpCode code(void) const { return opcode->getOpcode(); } ///< Get the opcode id (enum) for this op
|
||||||
bool isCommutative(void) const { return ((flags & PcodeOp::commutative)!=0); } ///< Return \b true if inputs commute
|
bool isCommutative(void) const { return ((flags & PcodeOp::commutative)!=0); } ///< Return \b true if inputs commute
|
||||||
uintb collapse(bool &markedInput) const; ///< Calculate the constant output produced by this op
|
uintb collapse(bool &markedInput) const; ///< Calculate the constant output produced by this op
|
||||||
|
uintb executeSimple(uintb *in,bool &evalError) const; ///< Execute \b this operation on the given input values
|
||||||
void collapseConstantSymbol(Varnode *newConst) const; ///< Propagate constant symbol from inputs to given output
|
void collapseConstantSymbol(Varnode *newConst) const; ///< Propagate constant symbol from inputs to given output
|
||||||
PcodeOp *nextOp(void) const; ///< Return the next op in the control-flow from this or \e null
|
PcodeOp *nextOp(void) const; ///< Return the next op in the control-flow from this or \e null
|
||||||
PcodeOp *previousOp(void) const; ///< Return the previous op within this op's basic block or \e null
|
PcodeOp *previousOp(void) const; ///< Return the previous op within this op's basic block or \e null
|
||||||
|
|
|
@ -129,7 +129,20 @@ uintb OpBehavior::evaluateBinary(int4 sizeout,int4 sizein,uintb in1,uintb in2) c
|
||||||
string name(get_opname(opcode));
|
string name(get_opname(opcode));
|
||||||
throw LowlevelError("Binary emulation unimplemented for "+name);
|
throw LowlevelError("Binary emulation unimplemented for "+name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// \param sizeout is the size of the output in bytes
|
||||||
|
/// \param sizein is the size of the inputs in bytes
|
||||||
|
/// \param in1 is the first input value
|
||||||
|
/// \param in2 is the second input value
|
||||||
|
/// \param in3 is the third input value
|
||||||
|
/// \return the output value
|
||||||
|
uintb OpBehavior::evaluateTernary(int4 sizeout,int4 sizein,uintb in1,uintb in2,uintb in3) const
|
||||||
|
|
||||||
|
{
|
||||||
|
string name(get_opname(opcode));
|
||||||
|
throw LowlevelError("Ternary emulation unimplemented for "+name);
|
||||||
|
}
|
||||||
|
|
||||||
/// If the output value is known, recover the input value.
|
/// If the output value is known, recover the input value.
|
||||||
/// \param sizeout is the size of the output in bytes
|
/// \param sizeout is the size of the output in bytes
|
||||||
/// \param out is the output value
|
/// \param out is the output value
|
||||||
|
@ -752,6 +765,20 @@ uintb OpBehaviorSubpiece::evaluateBinary(int4 sizeout,int4 sizein,uintb in1,uint
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uintb OpBehaviorPtradd::evaluateTernary(int4 sizeout,int4 sizein,uintb in1,uintb in2,uintb in3) const
|
||||||
|
|
||||||
|
{
|
||||||
|
uintb res = (in1 + in2 * in3) & calc_mask(sizeout);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
uintb OpBehaviorPtrsub::evaluateBinary(int4 sizeout,int4 sizein,uintb in1,uintb in2) const
|
||||||
|
|
||||||
|
{
|
||||||
|
uintb res = (in1 + in2) & calc_mask(sizeout);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
uintb OpBehaviorPopcount::evaluateUnary(int4 sizeout,int4 sizein,uintb in1) const
|
uintb OpBehaviorPopcount::evaluateUnary(int4 sizeout,int4 sizein,uintb in1) const
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
|
@ -4,9 +4,9 @@
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
@ -66,7 +66,10 @@ public:
|
||||||
|
|
||||||
/// \brief Emulate the binary op-code on input values
|
/// \brief Emulate the binary op-code on input values
|
||||||
virtual uintb evaluateBinary(int4 sizeout,int4 sizein,uintb in1,uintb in2) const;
|
virtual uintb evaluateBinary(int4 sizeout,int4 sizein,uintb in1,uintb in2) const;
|
||||||
|
|
||||||
|
/// \brief Emulate the ternary op-code on input values
|
||||||
|
virtual uintb evaluateTernary(int4 sizeout,int4 sizein,uintb in1,uintb in2,uintb in3) const;
|
||||||
|
|
||||||
/// \brief Reverse the binary op-code operation, recovering an input value
|
/// \brief Reverse the binary op-code operation, recovering an input value
|
||||||
virtual uintb recoverInputBinary(int4 slot,int4 sizeout,uintb out,int4 sizein,uintb in) const;
|
virtual uintb recoverInputBinary(int4 slot,int4 sizeout,uintb out,int4 sizein,uintb in) const;
|
||||||
|
|
||||||
|
@ -506,6 +509,20 @@ public:
|
||||||
virtual uintb evaluateBinary(int4 sizeout,int4 sizein,uintb in1,uintb in2) const;
|
virtual uintb evaluateBinary(int4 sizeout,int4 sizein,uintb in1,uintb in2) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// CPUI_PTRADD behavior
|
||||||
|
class OpBehaviorPtradd : public OpBehavior {
|
||||||
|
public:
|
||||||
|
OpBehaviorPtradd(void) : OpBehavior(CPUI_PTRADD,false) {} ///< Constructor
|
||||||
|
virtual uintb evaluateTernary(int4 sizeout,int4 sizein,uintb in1,uintb in2,uintb in3) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// CPUI_PTRSUB behavior
|
||||||
|
class OpBehaviorPtrsub : public OpBehavior {
|
||||||
|
public:
|
||||||
|
OpBehaviorPtrsub(void) : OpBehavior(CPUI_PTRSUB,false) {} ///< Constructor
|
||||||
|
virtual uintb evaluateBinary(int4 sizeout,int4 sizein,uintb in1,uintb in2) const;
|
||||||
|
};
|
||||||
|
|
||||||
/// CPUI_POPCOUNT behavior
|
/// CPUI_POPCOUNT behavior
|
||||||
class OpBehaviorPopcount : public OpBehavior {
|
class OpBehaviorPopcount : public OpBehavior {
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -2226,7 +2226,7 @@ TypeOpPtradd::TypeOpPtradd(TypeFactory *t) : TypeOp(t,CPUI_PTRADD,"+")
|
||||||
{
|
{
|
||||||
opflags = PcodeOp::ternary | PcodeOp::nocollapse;
|
opflags = PcodeOp::ternary | PcodeOp::nocollapse;
|
||||||
addlflags = arithmetic_op;
|
addlflags = arithmetic_op;
|
||||||
behave = new OpBehavior(CPUI_PTRADD,false); // Dummy behavior
|
behave = new OpBehaviorPtradd();
|
||||||
}
|
}
|
||||||
|
|
||||||
Datatype *TypeOpPtradd::getInputLocal(const PcodeOp *op,int4 slot) const
|
Datatype *TypeOpPtradd::getInputLocal(const PcodeOp *op,int4 slot) const
|
||||||
|
@ -2296,7 +2296,7 @@ TypeOpPtrsub::TypeOpPtrsub(TypeFactory *t) : TypeOp(t,CPUI_PTRSUB,"->")
|
||||||
// allow this to be commutative.
|
// allow this to be commutative.
|
||||||
opflags = PcodeOp::binary|PcodeOp::nocollapse;
|
opflags = PcodeOp::binary|PcodeOp::nocollapse;
|
||||||
addlflags = arithmetic_op;
|
addlflags = arithmetic_op;
|
||||||
behave = new OpBehavior(CPUI_PTRSUB,false); // Dummy behavior
|
behave = new OpBehaviorPtrsub();
|
||||||
}
|
}
|
||||||
|
|
||||||
Datatype *TypeOpPtrsub::getOutputLocal(const PcodeOp *op) const
|
Datatype *TypeOpPtrsub::getOutputLocal(const PcodeOp *op) const
|
||||||
|
|
|
@ -91,6 +91,17 @@ public:
|
||||||
uintb evaluateBinary(int4 sizeout,int4 sizein,uintb in1,uintb in2) const {
|
uintb evaluateBinary(int4 sizeout,int4 sizein,uintb in1,uintb in2) const {
|
||||||
return behave->evaluateBinary(sizeout,sizein,in1,in2); }
|
return behave->evaluateBinary(sizeout,sizein,in1,in2); }
|
||||||
|
|
||||||
|
/// \brief Emulate the ternary op-code on an input value
|
||||||
|
///
|
||||||
|
/// \param sizeout is the size of the output in bytes
|
||||||
|
/// \param sizein is the size of the inputs in bytes
|
||||||
|
/// \param in1 is the first input value
|
||||||
|
/// \param in2 is the second input value
|
||||||
|
/// \param in3 is the third input value
|
||||||
|
/// \return the output value
|
||||||
|
uintb evaluateTernary(int4 sizeout,int4 sizein,uintb in1,uintb in2,uintb in3) const {
|
||||||
|
return behave->evaluateTernary(sizeout,sizein,in1,in2,in3); }
|
||||||
|
|
||||||
/// \brief Reverse the binary op-code operation, recovering a constant input value
|
/// \brief Reverse the binary op-code operation, recovering a constant input value
|
||||||
///
|
///
|
||||||
/// If the output value and one of the input values is known, recover the value
|
/// If the output value and one of the input values is known, recover the value
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
<decompilertest>
|
||||||
|
<binaryimage arch="ARM:LE:32:v8:default">
|
||||||
|
<!--
|
||||||
|
Examples of conditional constants being immediately passed to MULTIEQUAL after the branch
|
||||||
|
-->
|
||||||
|
<bytechunk space="ram" offset="0x4022871a">
|
||||||
|
00b970478160
|
||||||
|
6120fbe7
|
||||||
|
</bytechunk>
|
||||||
|
<bytechunk space="ram" offset="0x40228728">
|
||||||
|
0b4601b107330360
|
||||||
|
7047
|
||||||
|
</bytechunk>
|
||||||
|
<bytechunk space="ram" offset="0x40228736">
|
||||||
|
087a613818bf01207047
|
||||||
|
</bytechunk>
|
||||||
|
<symbol space="ram" offset="0x4022871a" name="zeroprop"/>
|
||||||
|
<symbol space="ram" offset="0x40228728" name="throughadd"/>
|
||||||
|
<symbol space="ram" offset="0x40228736" name="condmove"/>
|
||||||
|
<symbol space="ram" offset="0x40228758" name="myfunc"/>
|
||||||
|
</binaryimage>
|
||||||
|
<script>
|
||||||
|
<com>set context TMode 1 [ram,0x4022871a] [ram,0x40228800]</com>
|
||||||
|
<com>parse line extern char zeroprop(int4 *ptrint,int4 val);</com>
|
||||||
|
<com>parse line extern void throughadd(int4 *tadd,int4 tval);</com>
|
||||||
|
<com>parse line extern bool condmove(int4 cval,char *cptr);</com>
|
||||||
|
<com>lo fu zeroprop</com>
|
||||||
|
<com>decompile</com>
|
||||||
|
<com>print C</com>
|
||||||
|
<com>lo fu throughadd</com>
|
||||||
|
<com>decompile</com>
|
||||||
|
<com>print C</com>
|
||||||
|
<com>lo fu condmove</com>
|
||||||
|
<com>decompile</com>
|
||||||
|
<com>print C</com>
|
||||||
|
</script>
|
||||||
|
<stringmatch name="Immediate Conditional #1" min="0" max="0">\(char\)ptrint</stringmatch>
|
||||||
|
<stringmatch name="Immediate Conditional #2" min="1" max="1">return cVar1;</stringmatch>
|
||||||
|
<stringmatch name="Immediate Conditional #3" min="1" max="1">cVar1 = '\\0';</stringmatch>
|
||||||
|
<stringmatch name="Immediate Conditional #4" min="1" max="1">cVar1 = 'a';</stringmatch>
|
||||||
|
<stringmatch name="Immediate Conditional #5" min="1" max="1">\*tadd = tval;</stringmatch>
|
||||||
|
<stringmatch name="Immediate Conditional #6" min="1" max="1">tval = tval \+ 7;</stringmatch>
|
||||||
|
<stringmatch name="Immediate Conditional #7" min="1" max="1">return cptr\[8\] != 'a';</stringmatch>
|
||||||
|
<stringmatch name="Immediate Conditional #8" min="0" max="0">SUB</stringmatch>
|
||||||
|
</decompilertest>
|
|
@ -0,0 +1,23 @@
|
||||||
|
<decompilertest>
|
||||||
|
<binaryimage arch="AARCH64:LE:64:v8A:default">
|
||||||
|
<!--
|
||||||
|
Example of conditional constant operated on prior to branch
|
||||||
|
-->
|
||||||
|
<bytechunk space="ram" offset="0x110320" readonly="true">
|
||||||
|
400000b40b000014c0035fd6
|
||||||
|
</bytechunk>
|
||||||
|
</binaryimage>
|
||||||
|
<script>
|
||||||
|
<com>map fun r0x110320 foo</com>
|
||||||
|
<com>override flow r0x110324 callreturn</com>
|
||||||
|
<com>map fun r0x110350 otherfunc</com>
|
||||||
|
<com>parse line extern int4 foo(void *ptr);</com>
|
||||||
|
<com>lo fu foo</com>
|
||||||
|
<com>decompile</com>
|
||||||
|
<com>print C</com>
|
||||||
|
</script>
|
||||||
|
<stringmatch name="Modified conditional constant #1" min="1" max="1">return 0;</stringmatch>
|
||||||
|
<stringmatch name="Modified conditional constant #2" min="1" max="1">iVar1 = otherfunc\(\);</stringmatch>
|
||||||
|
<stringmatch name="Modified conditional constant #3" min="1" max="1">return iVar1;</stringmatch>
|
||||||
|
<stringmatch name="Modified conditional constant #4" min="0" max="0">\(int4\)ptr</stringmatch>
|
||||||
|
</decompilertest>
|
Loading…
Add table
Add a link
Reference in a new issue