mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-03 17:59:46 +02:00
Decompiler for-loops
This commit is contained in:
parent
1539318b59
commit
b2bc1eb019
25 changed files with 726 additions and 68 deletions
|
@ -1301,6 +1301,7 @@ void Architecture::resetDefaultsInternal(void)
|
||||||
flowoptions = FlowInfo::error_toomanyinstructions;
|
flowoptions = FlowInfo::error_toomanyinstructions;
|
||||||
max_instructions = 100000;
|
max_instructions = 100000;
|
||||||
infer_pointers = true;
|
infer_pointers = true;
|
||||||
|
analyze_for_loops = true;
|
||||||
readonlypropagate = false;
|
readonlypropagate = false;
|
||||||
alias_block_level = 2; // Block structs and arrays by default
|
alias_block_level = 2; // Block structs and arrays by default
|
||||||
}
|
}
|
||||||
|
|
|
@ -128,6 +128,7 @@ public:
|
||||||
bool aggressive_ext_trim; ///< Aggressively trim inputs that look like they are sign extended
|
bool aggressive_ext_trim; ///< Aggressively trim inputs that look like they are sign extended
|
||||||
bool readonlypropagate; ///< true if readonly values should be treated as constants
|
bool readonlypropagate; ///< true if readonly values should be treated as constants
|
||||||
bool infer_pointers; ///< True if we should infer pointers from constants that are likely addresses
|
bool infer_pointers; ///< True if we should infer pointers from constants that are likely addresses
|
||||||
|
bool analyze_for_loops; ///< True if we should attempt conversion of \e whiledo loops to \e for loops
|
||||||
vector<AddrSpace *> inferPtrSpaces; ///< Set of address spaces in which a pointer constant is inferable
|
vector<AddrSpace *> inferPtrSpaces; ///< Set of address spaces in which a pointer constant is inferable
|
||||||
int4 funcptr_align; ///< How many bits of alignment a function ptr has
|
int4 funcptr_align; ///< How many bits of alignment a function ptr has
|
||||||
uint4 flowoptions; ///< options passed to flow following engine
|
uint4 flowoptions; ///< options passed to flow following engine
|
||||||
|
|
|
@ -1254,7 +1254,16 @@ FlowBlock *BlockGraph::nextFlowAfter(const FlowBlock *bl) const
|
||||||
return nextbl;
|
return nextbl;
|
||||||
}
|
}
|
||||||
|
|
||||||
void BlockGraph::finalizePrinting(const Funcdata &data) const
|
void BlockGraph::finalTransform(Funcdata &data)
|
||||||
|
|
||||||
|
{
|
||||||
|
// Recurse into all the substructures
|
||||||
|
vector<FlowBlock *>::const_iterator iter;
|
||||||
|
for(iter=list.begin();iter!=list.end();++iter)
|
||||||
|
(*iter)->finalTransform(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BlockGraph::finalizePrinting(Funcdata &data) const
|
||||||
|
|
||||||
{
|
{
|
||||||
// Recurse into all the substructures
|
// Recurse into all the substructures
|
||||||
|
@ -2916,6 +2925,158 @@ void BlockIf::saveXmlBody(ostream &s) const
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Try to find a Varnode that represents the controlling \e loop \e variable for \b this loop.
|
||||||
|
/// The Varnode must be:
|
||||||
|
/// - tested by the exit condition
|
||||||
|
/// - have a MULTIEQUAL in the head block
|
||||||
|
/// - have a modification coming in from the tail block
|
||||||
|
/// - the modification must be the last op or moveable to the last op
|
||||||
|
///
|
||||||
|
/// If the loop variable is found, this routine sets the \e iterateOp and the \e loopDef.
|
||||||
|
/// \param cbranch is the CBRANCH implementing the loop exit
|
||||||
|
/// \param head is the head basic-block of the loop
|
||||||
|
/// \param tail is the tail basic-block of the loop
|
||||||
|
/// \param lastOp is the precomputed last PcodeOp of tail that isn't a BRANCH
|
||||||
|
void BlockWhileDo::findLoopVariable(PcodeOp *cbranch,BlockBasic *head,BlockBasic *tail,PcodeOp *lastOp)
|
||||||
|
|
||||||
|
{
|
||||||
|
Varnode *vn = cbranch->getIn(1);
|
||||||
|
if (!vn->isWritten()) return; // No loop variable found
|
||||||
|
PcodeOp *op = vn->getDef();
|
||||||
|
int4 slot = tail->getOutRevIndex(0);
|
||||||
|
|
||||||
|
PcodeOpNode path[4];
|
||||||
|
int4 count = 0;
|
||||||
|
if (op->isCall() || op->isMarker()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
path[0].op = op;
|
||||||
|
path[0].slot = 0;
|
||||||
|
while(count>=0) {
|
||||||
|
PcodeOp *curOp = path[count].op;
|
||||||
|
int4 ind = path[count].slot++;
|
||||||
|
if (ind >= curOp->numInput()) {
|
||||||
|
count -= 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Varnode *nextVn = curOp->getIn(ind);
|
||||||
|
if (!nextVn->isWritten()) continue;
|
||||||
|
PcodeOp *defOp = nextVn->getDef();
|
||||||
|
if (defOp->code() == CPUI_MULTIEQUAL) {
|
||||||
|
if (defOp->getParent() != head) continue;
|
||||||
|
Varnode *itvn = defOp->getIn(slot);
|
||||||
|
if (!itvn->isWritten()) continue;
|
||||||
|
PcodeOp *possibleIterate = itvn->getDef();
|
||||||
|
if (possibleIterate->getParent() == tail) { // Found proper head/tail configuration
|
||||||
|
if (possibleIterate->isMarker())
|
||||||
|
continue; // No iteration in tail
|
||||||
|
if (!possibleIterate->isMoveable(lastOp))
|
||||||
|
continue; // Not the final statement
|
||||||
|
loopDef = defOp;
|
||||||
|
iterateOp = possibleIterate;
|
||||||
|
return; // Found the loop variable
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (count == 3) continue;
|
||||||
|
if (defOp->isCall() || defOp->isMarker()) continue;
|
||||||
|
count += 1;
|
||||||
|
path[count].op = defOp;
|
||||||
|
path[count].slot = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return; // No loop variable found
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Given a control flow loop, try to find a putative initializer PcodeOp for the loop variable.
|
||||||
|
/// The initializer must be read by read by \e loopDef and by in a block that
|
||||||
|
/// flows only into the loop. If an initializer is found, then
|
||||||
|
/// \e initializeOp is set and the lastOp (not including a branch) in the initializer
|
||||||
|
/// block is returned. Otherwise null is returned.
|
||||||
|
/// \param head is the head block of the loop
|
||||||
|
/// \param slot is the block input coming from the loop tail
|
||||||
|
/// \return the last PcodeOp in the initializer's block
|
||||||
|
PcodeOp *BlockWhileDo::findInitializer(BlockBasic *head,int4 slot) const
|
||||||
|
|
||||||
|
{
|
||||||
|
if (head->sizeIn() != 2) return (PcodeOp *)0;
|
||||||
|
slot = 1 - slot;
|
||||||
|
Varnode *initVn = loopDef->getIn(slot);
|
||||||
|
if (!initVn->isWritten()) return (PcodeOp *)0;
|
||||||
|
PcodeOp *res = initVn->getDef();
|
||||||
|
if (res->isMarker()) return (PcodeOp *)0;
|
||||||
|
FlowBlock *initialBlock = res->getParent();
|
||||||
|
if (initialBlock != head->getIn(slot))
|
||||||
|
return (PcodeOp *)0; // Statement must terminate in block flowing to head
|
||||||
|
PcodeOp *lastOp = initialBlock->lastOp();
|
||||||
|
if (lastOp == (PcodeOp *)0) return (PcodeOp *)0;
|
||||||
|
if (initialBlock->sizeOut() != 1) return (PcodeOp *)0; // Initializer block must flow only to for loop
|
||||||
|
if (lastOp->isBranch()) {
|
||||||
|
lastOp = lastOp->previousOp();
|
||||||
|
if (lastOp == (PcodeOp *)0) return (PcodeOp *)0;
|
||||||
|
}
|
||||||
|
initializeOp = res;
|
||||||
|
return lastOp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// For-loop initializer or iterator statements must be the final statement in
|
||||||
|
/// their respective basic block. This method tests that iterateOp/initializeOp (specified
|
||||||
|
/// by \e slot) is the root of or can be turned into the root of a terminal statement.
|
||||||
|
/// The root output must be an explicit variable being read by the
|
||||||
|
/// \e loopDef MULTIEQUAL at the top of the loop. If the root is not the last
|
||||||
|
/// PcodeOp in the block, an attempt is made to move it.
|
||||||
|
/// Return the root PcodeOp if all these conditions are met, otherwise return null.
|
||||||
|
/// \param data is the function containing the while loop
|
||||||
|
/// \param slot is the slot read by \e loopDef from the output of the statement
|
||||||
|
/// \return an explicit statement or null
|
||||||
|
PcodeOp *BlockWhileDo::testTerminal(Funcdata &data,int4 slot) const
|
||||||
|
|
||||||
|
{
|
||||||
|
Varnode *vn = loopDef->getIn(slot);
|
||||||
|
if (!vn->isWritten()) return (PcodeOp *)0;
|
||||||
|
PcodeOp *finalOp = vn->getDef();
|
||||||
|
BlockBasic *parentBlock = (BlockBasic *)loopDef->getParent()->getIn(slot);
|
||||||
|
PcodeOp *resOp = finalOp;
|
||||||
|
if (finalOp->code() == CPUI_COPY && finalOp->notPrinted()) {
|
||||||
|
vn = finalOp->getIn(0);
|
||||||
|
if (!vn->isWritten()) return (PcodeOp *)0;
|
||||||
|
resOp = vn->getDef();
|
||||||
|
if (resOp->getParent() != parentBlock) return (PcodeOp *)0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!vn->isExplicit()) return (PcodeOp *)0;
|
||||||
|
if (resOp->notPrinted())
|
||||||
|
return (PcodeOp *)0; // Statement MUST be printed
|
||||||
|
|
||||||
|
// finalOp MUST be the last op in the basic block (except for the branch)
|
||||||
|
PcodeOp *lastOp = finalOp->getParent()->lastOp();
|
||||||
|
if (lastOp->isBranch())
|
||||||
|
lastOp = lastOp->previousOp();
|
||||||
|
if (!data.moveRespectingCover(finalOp, lastOp))
|
||||||
|
return (PcodeOp *)0;
|
||||||
|
|
||||||
|
return resOp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This is a final sanity check on the \e iterate statement. If statement is just a
|
||||||
|
/// CAST or COPY, we revert to displaying the whole loop using \e while
|
||||||
|
/// \return \b true is the statement looks like a suitable for-loop iterator.
|
||||||
|
bool BlockWhileDo::testIterateForm(void) const
|
||||||
|
|
||||||
|
{
|
||||||
|
PcodeOp *curOp = iterateOp;
|
||||||
|
OpCode opc = curOp->code();
|
||||||
|
while(opc == CPUI_COPY || opc == CPUI_CAST) {
|
||||||
|
Varnode *vn = curOp->getIn(0);
|
||||||
|
if (!curOp->notPrinted())
|
||||||
|
if (vn->isExplicit()) return false; // End of statement, no substantive op seen
|
||||||
|
if (!vn->isWritten()) return false;
|
||||||
|
curOp = vn->getDef();
|
||||||
|
opc = curOp->code();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void BlockWhileDo::markLabelBumpUp(bool bump)
|
void BlockWhileDo::markLabelBumpUp(bool bump)
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -2953,6 +3114,79 @@ FlowBlock *BlockWhileDo::nextFlowAfter(const FlowBlock *bl) const
|
||||||
return nextbl;
|
return nextbl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Determine if \b this block can be printed as a \e for loop, with an \e initializer statement
|
||||||
|
/// extracted from the previous block, and an \e iterator statement extracted from the body.
|
||||||
|
/// \param data is the function containing \b this loop
|
||||||
|
void BlockWhileDo::finalTransform(Funcdata &data)
|
||||||
|
|
||||||
|
{
|
||||||
|
BlockGraph::finalTransform(data);
|
||||||
|
if (!data.getArch()->analyze_for_loops) return;
|
||||||
|
if (hasOverflowSyntax()) return;
|
||||||
|
FlowBlock *copyBl = getFrontLeaf();
|
||||||
|
if (copyBl == (FlowBlock *)0) return;
|
||||||
|
BlockBasic *head = (BlockBasic *)copyBl->subBlock(0);
|
||||||
|
if (head->getType() != t_basic) return;
|
||||||
|
PcodeOp *lastOp = getBlock(1)->lastOp(); // There must be a last op in body, for there to be an iterator statement
|
||||||
|
if (lastOp == (PcodeOp *)0) return;
|
||||||
|
BlockBasic *tail = lastOp->getParent();
|
||||||
|
if (tail->sizeOut() != 1) return;
|
||||||
|
if (tail->getOut(0) != head) return;
|
||||||
|
PcodeOp *cbranch = getBlock(0)->lastOp();
|
||||||
|
if (cbranch == (PcodeOp *)0 || cbranch->code() != CPUI_CBRANCH) return;
|
||||||
|
if (lastOp->isBranch()) { // Convert lastOp to -point- iterateOp must appear after
|
||||||
|
lastOp = lastOp->previousOp();
|
||||||
|
if (lastOp == (PcodeOp *)0) return;
|
||||||
|
}
|
||||||
|
|
||||||
|
findLoopVariable(cbranch, head, tail, lastOp);
|
||||||
|
if (iterateOp == (PcodeOp *)0) return;
|
||||||
|
|
||||||
|
if (iterateOp != lastOp) {
|
||||||
|
data.opUninsert(iterateOp);
|
||||||
|
data.opInsertAfter(iterateOp, lastOp);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to set up initializer statement
|
||||||
|
lastOp = findInitializer(head, tail->getOutRevIndex(0));
|
||||||
|
if (lastOp == (PcodeOp *)0) return;
|
||||||
|
if (!initializeOp->isMoveable(lastOp)) {
|
||||||
|
initializeOp = (PcodeOp *)0; // Turn it off
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (initializeOp != lastOp) {
|
||||||
|
data.opUninsert(initializeOp);
|
||||||
|
data.opInsertAfter(initializeOp, lastOp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Assume that finalTransform() has run and that all HighVariable merging has occurred.
|
||||||
|
/// Do any final tests checking that the initialization and iteration statements are good.
|
||||||
|
/// Extract initialization and iteration statements from their basic blocks.
|
||||||
|
/// \param data is the function containing the loop
|
||||||
|
void BlockWhileDo::finalizePrinting(Funcdata &data) const
|
||||||
|
|
||||||
|
{
|
||||||
|
BlockGraph::finalizePrinting(data); // Continue recursing
|
||||||
|
if (iterateOp == (PcodeOp *)0) return; // For-loop printing not enabled
|
||||||
|
// TODO: We can check that iterate statement is not too complex
|
||||||
|
int4 slot = iterateOp->getParent()->getOutRevIndex(0);
|
||||||
|
iterateOp = testTerminal(data,slot); // Make sure iterator statement is explicit
|
||||||
|
if (iterateOp == (PcodeOp *)0) return;
|
||||||
|
if (!testIterateForm()) {
|
||||||
|
iterateOp = (PcodeOp *)0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (initializeOp == (PcodeOp *)0)
|
||||||
|
findInitializer(loopDef->getParent(), slot); // Last chance initializer
|
||||||
|
if (initializeOp != (PcodeOp *)0)
|
||||||
|
initializeOp = testTerminal(data,1-slot); // Make sure initializer statement is explicit
|
||||||
|
|
||||||
|
data.opMarkNonPrinting(iterateOp);
|
||||||
|
if (initializeOp != (PcodeOp *)0)
|
||||||
|
data.opMarkNonPrinting(initializeOp);
|
||||||
|
}
|
||||||
|
|
||||||
void BlockDoWhile::markLabelBumpUp(bool bump)
|
void BlockDoWhile::markLabelBumpUp(bool bump)
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -3083,7 +3317,7 @@ void BlockSwitch::grabCaseBasic(FlowBlock *switchbl,const vector<FlowBlock *> &c
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void BlockSwitch::finalizePrinting(const Funcdata &data) const
|
void BlockSwitch::finalizePrinting(Funcdata &data) const
|
||||||
|
|
||||||
{
|
{
|
||||||
BlockGraph::finalizePrinting(data); // Make sure to still recurse
|
BlockGraph::finalizePrinting(data); // Make sure to still recurse
|
||||||
|
|
|
@ -170,7 +170,8 @@ public:
|
||||||
virtual void flipInPlaceExecute(void);
|
virtual void flipInPlaceExecute(void);
|
||||||
virtual bool isComplex(void) const { return true; } ///< Is \b this too complex to be a condition (BlockCondition)
|
virtual bool isComplex(void) const { return true; } ///< Is \b this too complex to be a condition (BlockCondition)
|
||||||
virtual FlowBlock *nextFlowAfter(const FlowBlock *bl) const;
|
virtual FlowBlock *nextFlowAfter(const FlowBlock *bl) const;
|
||||||
virtual void finalizePrinting(const Funcdata &data) const {} ///< Make any final configurations necessary to print the block
|
virtual void finalTransform(Funcdata &data) {} ///< Do any structure driven final transforms
|
||||||
|
virtual void finalizePrinting(Funcdata &data) const {} ///< Make any final configurations necessary to print the block
|
||||||
virtual void saveXmlHeader(ostream &s) const; ///< Save basic information as XML attributes
|
virtual void saveXmlHeader(ostream &s) const; ///< Save basic information as XML attributes
|
||||||
virtual void restoreXmlHeader(const Element *el); ///< Restore basic information for XML attributes
|
virtual void restoreXmlHeader(const Element *el); ///< Restore basic information for XML attributes
|
||||||
virtual void saveXmlBody(ostream &s) const {} ///< Save detail about components to an XML stream
|
virtual void saveXmlBody(ostream &s) const {} ///< Save detail about components to an XML stream
|
||||||
|
@ -296,7 +297,8 @@ public:
|
||||||
virtual void printRaw(ostream &s) const;
|
virtual void printRaw(ostream &s) const;
|
||||||
virtual void emit(PrintLanguage *lng) const { lng->emitBlockGraph(this); }
|
virtual void emit(PrintLanguage *lng) const { lng->emitBlockGraph(this); }
|
||||||
virtual FlowBlock *nextFlowAfter(const FlowBlock *bl) const;
|
virtual FlowBlock *nextFlowAfter(const FlowBlock *bl) const;
|
||||||
virtual void finalizePrinting(const Funcdata &data) const;
|
virtual void finalTransform(Funcdata &data);
|
||||||
|
virtual void finalizePrinting(Funcdata &data) const;
|
||||||
virtual void saveXmlBody(ostream &s) const;
|
virtual void saveXmlBody(ostream &s) const;
|
||||||
virtual void restoreXmlBody(List::const_iterator &iter,List::const_iterator enditer,BlockMap &resolver);
|
virtual void restoreXmlBody(List::const_iterator &iter,List::const_iterator enditer,BlockMap &resolver);
|
||||||
void restoreXml(const Element *el,const AddrSpaceManager *m); ///< Restore \b this BlockGraph from an XML stream
|
void restoreXml(const Element *el,const AddrSpaceManager *m); ///< Restore \b this BlockGraph from an XML stream
|
||||||
|
@ -580,8 +582,23 @@ public:
|
||||||
/// Overflow syntax refers to the situation where there is a proper BlockWhileDo structure but
|
/// Overflow syntax refers to the situation where there is a proper BlockWhileDo structure but
|
||||||
/// the conditional block is too long or complicated to emit as a single conditional expression.
|
/// the conditional block is too long or complicated to emit as a single conditional expression.
|
||||||
/// An alternate `while(true) { }` form is used instead.
|
/// An alternate `while(true) { }` form is used instead.
|
||||||
|
///
|
||||||
|
/// If an iterator op is provided, the block will be printed using \e for loop syntax,
|
||||||
|
/// `for(i=0;i<10;++i)` where an \e initializer statement and \e iterator statement are
|
||||||
|
/// printed alongside the \e condition statement. Otherwise, \e while loop syntax is used
|
||||||
|
/// `while(i<10)`
|
||||||
class BlockWhileDo : public BlockGraph {
|
class BlockWhileDo : public BlockGraph {
|
||||||
|
mutable PcodeOp *initializeOp; ///< Statement used as \e for loop initializer
|
||||||
|
mutable PcodeOp *iterateOp; ///< Statement used as \e for loop iterator
|
||||||
|
mutable PcodeOp *loopDef; ///< MULTIEQUAL merging loop variable
|
||||||
|
void findLoopVariable(PcodeOp *cbranch,BlockBasic *head,BlockBasic *tail,PcodeOp *lastOp); ///< Find a \e loop \e variable
|
||||||
|
PcodeOp *findInitializer(BlockBasic *head,int4 slot) const; ///< Find the for-loop initializer op
|
||||||
|
PcodeOp *testTerminal(Funcdata &data,int4 slot) const; ///< Test that given statement is terminal and explicit
|
||||||
|
bool testIterateForm(void) const; ///< Return \b false if the iterate statement is of an unacceptable form
|
||||||
public:
|
public:
|
||||||
|
BlockWhileDo(void) { initializeOp = (PcodeOp *)0; iterateOp = (PcodeOp *)0; loopDef = (PcodeOp *)0; } ///< Constructor
|
||||||
|
PcodeOp *getInitializeOp(void) const { return initializeOp; }
|
||||||
|
PcodeOp *getIterateOp(void) const { return iterateOp; }
|
||||||
bool hasOverflowSyntax(void) const { return ((getFlags() & f_whiledo_overflow)!=0); } ///< Does \b this require overflow syntax
|
bool hasOverflowSyntax(void) const { return ((getFlags() & f_whiledo_overflow)!=0); } ///< Does \b this require overflow syntax
|
||||||
void setOverflowSyntax(void) { setFlag(f_whiledo_overflow); } ///< Set that \b this requires overflow syntax
|
void setOverflowSyntax(void) { setFlag(f_whiledo_overflow); } ///< Set that \b this requires overflow syntax
|
||||||
virtual block_type getType(void) const { return t_whiledo; }
|
virtual block_type getType(void) const { return t_whiledo; }
|
||||||
|
@ -590,6 +607,8 @@ public:
|
||||||
virtual void printHeader(ostream &s) const;
|
virtual void printHeader(ostream &s) const;
|
||||||
virtual void emit(PrintLanguage *lng) const { lng->emitBlockWhileDo(this); }
|
virtual void emit(PrintLanguage *lng) const { lng->emitBlockWhileDo(this); }
|
||||||
virtual FlowBlock *nextFlowAfter(const FlowBlock *bl) const;
|
virtual FlowBlock *nextFlowAfter(const FlowBlock *bl) const;
|
||||||
|
virtual void finalTransform(Funcdata &data);
|
||||||
|
virtual void finalizePrinting(Funcdata &data) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// \brief A loop structure where the condition is checked at the bottom.
|
/// \brief A loop structure where the condition is checked at the bottom.
|
||||||
|
@ -674,7 +693,7 @@ public:
|
||||||
virtual void printHeader(ostream &s) const;
|
virtual void printHeader(ostream &s) const;
|
||||||
virtual void emit(PrintLanguage *lng) const { lng->emitBlockSwitch(this); }
|
virtual void emit(PrintLanguage *lng) const { lng->emitBlockSwitch(this); }
|
||||||
virtual FlowBlock *nextFlowAfter(const FlowBlock *bl) const;
|
virtual FlowBlock *nextFlowAfter(const FlowBlock *bl) const;
|
||||||
virtual void finalizePrinting(const Funcdata &data) const;
|
virtual void finalizePrinting(Funcdata &data) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// \brief Helper class for resolving cross-references while deserializing BlockGraph objects
|
/// \brief Helper class for resolving cross-references while deserializing BlockGraph objects
|
||||||
|
|
|
@ -2095,6 +2095,13 @@ void ConditionalJoin::clear(void)
|
||||||
mergeneed.clear();
|
mergeneed.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int4 ActionStructureTransform::apply(Funcdata &data)
|
||||||
|
|
||||||
|
{
|
||||||
|
data.getStructure().finalTransform(data);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int4 ActionNormalizeBranches::apply(Funcdata &data)
|
int4 ActionNormalizeBranches::apply(Funcdata &data)
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
|
@ -262,6 +262,19 @@ public:
|
||||||
void clear(void); ///< Clear for a new test
|
void clear(void); ///< Clear for a new test
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// \brief Give each control-flow structure an opportunity to make a final transform
|
||||||
|
///
|
||||||
|
/// This is currently used to set up \e for loops via BlockWhileDo
|
||||||
|
class ActionStructureTransform : public Action {
|
||||||
|
public:
|
||||||
|
ActionStructureTransform(const string &g) : Action(0,"structuretransform",g) {} ///< Constructor
|
||||||
|
virtual Action *clone(const ActionGroupList &grouplist) const {
|
||||||
|
if (!grouplist.contains(getGroup())) return (Action *)0;
|
||||||
|
return new ActionStructureTransform(getGroup());
|
||||||
|
}
|
||||||
|
virtual int4 apply(Funcdata &data);
|
||||||
|
};
|
||||||
|
|
||||||
/// \brief Flip conditional control-flow so that \e preferred comparison operators are used
|
/// \brief Flip conditional control-flow so that \e preferred comparison operators are used
|
||||||
///
|
///
|
||||||
/// This is used as an alternative to the standard algorithm that structures control-flow, when
|
/// This is used as an alternative to the standard algorithm that structures control-flow, when
|
||||||
|
|
|
@ -5100,6 +5100,7 @@ void ActionDatabase::universalAction(Architecture *conf)
|
||||||
act->addAction( actcleanup );
|
act->addAction( actcleanup );
|
||||||
|
|
||||||
act->addAction( new ActionPreferComplement("blockrecovery") );
|
act->addAction( new ActionPreferComplement("blockrecovery") );
|
||||||
|
act->addAction( new ActionStructureTransform("blockrecovery") );
|
||||||
act->addAction( new ActionNormalizeBranches("normalizebranches") );
|
act->addAction( new ActionNormalizeBranches("normalizebranches") );
|
||||||
act->addAction( new ActionAssignHigh("merge") );
|
act->addAction( new ActionAssignHigh("merge") );
|
||||||
act->addAction( new ActionMergeRequired("merge") );
|
act->addAction( new ActionMergeRequired("merge") );
|
||||||
|
|
|
@ -482,6 +482,8 @@ public:
|
||||||
/// \brief End of all (alive) PcodeOp objects attached to a specific Address
|
/// \brief End of all (alive) PcodeOp objects attached to a specific Address
|
||||||
PcodeOpTree::const_iterator endOp(const Address &addr) const { return obank.end(addr); }
|
PcodeOpTree::const_iterator endOp(const Address &addr) const { return obank.end(addr); }
|
||||||
|
|
||||||
|
bool moveRespectingCover(PcodeOp *op,PcodeOp *lastOp); ///< Move given op past \e lastOp respecting covers if possible
|
||||||
|
|
||||||
// Jumptable routines
|
// Jumptable routines
|
||||||
JumpTable *linkJumpTable(PcodeOp *op); ///< Link jump-table with a given BRANCHIND
|
JumpTable *linkJumpTable(PcodeOp *op); ///< Link jump-table with a given BRANCHIND
|
||||||
JumpTable *findJumpTable(const PcodeOp *op) const; ///< Find a jump-table associated with a given BRANCHIND
|
JumpTable *findJumpTable(const PcodeOp *op) const; ///< Find a jump-table associated with a given BRANCHIND
|
||||||
|
|
|
@ -1361,3 +1361,54 @@ void cseEliminateList(Funcdata &data,vector< pair<uintm,PcodeOp *> > &list,vecto
|
||||||
liter2++;
|
liter2++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// This routine should be called only after Varnode merging and explicit/implicit attributes have
|
||||||
|
/// been calculated. Determine if the given op can be moved (only within its basic block) to
|
||||||
|
/// after \e lastOp. The output of any PcodeOp moved across must not be involved, directly or
|
||||||
|
/// indirectly, with any variable in the expression rooted at the given op.
|
||||||
|
/// If the move is possible, perform the move.
|
||||||
|
/// \param op is the given PcodeOp
|
||||||
|
/// \param lastOp is the PcodeOp to move past
|
||||||
|
/// \return \b true if the move is possible
|
||||||
|
bool Funcdata::moveRespectingCover(PcodeOp *op,PcodeOp *lastOp)
|
||||||
|
|
||||||
|
{
|
||||||
|
if (op == lastOp) return true; // Nothing to move past
|
||||||
|
if (op->isCall()) return false;
|
||||||
|
PcodeOp *prevOp = (PcodeOp *)0;
|
||||||
|
if (op->code() == CPUI_CAST) {
|
||||||
|
Varnode *vn = op->getIn(0);
|
||||||
|
if (!vn->isExplicit()) { // If CAST is part of expression, we need to move the previous op as well
|
||||||
|
if (!vn->isWritten()) return false;
|
||||||
|
prevOp = vn->getDef();
|
||||||
|
if (prevOp->isCall()) return false;
|
||||||
|
if (op->previousOp() != prevOp) return false; // Previous op must exist and feed into the CAST
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Varnode *rootvn = op->getOut();
|
||||||
|
vector<HighVariable *> highList;
|
||||||
|
int4 typeVal = HighVariable::markExpression(rootvn, highList);
|
||||||
|
PcodeOp *curOp = op;
|
||||||
|
do {
|
||||||
|
PcodeOp *nextOp = curOp->nextOp();
|
||||||
|
OpCode opc = nextOp->code();
|
||||||
|
if (opc != CPUI_COPY && opc != CPUI_CAST) break; // Limit ourselves to only crossing COPY and CAST ops
|
||||||
|
if (rootvn == nextOp->getIn(0)) break; // Data-flow order dependence
|
||||||
|
Varnode *copyVn = nextOp->getOut();
|
||||||
|
if (copyVn->getHigh()->isMark()) break; // Direct interference: COPY writes what original op reads
|
||||||
|
if (typeVal != 0 && copyVn->isAddrTied()) break; // Possible indirect interference
|
||||||
|
curOp = nextOp;
|
||||||
|
} while(curOp != lastOp);
|
||||||
|
for(int4 i=0;i<highList.size();++i) // Clear marks on expression
|
||||||
|
highList[i]->clearMark();
|
||||||
|
if (curOp == lastOp) { // If we are able to cross everything
|
||||||
|
opUninsert(op); // Move -op-
|
||||||
|
opInsertAfter(op, lastOp);
|
||||||
|
if (prevOp != (PcodeOp *)0) { // If there was a CAST, move both ops
|
||||||
|
opUninsert(prevOp);
|
||||||
|
opInsertAfter(prevOp, lastOp);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
|
@ -731,38 +731,35 @@ void Funcdata::clearDeadVarnodes(void)
|
||||||
void Funcdata::calcNZMask(void)
|
void Funcdata::calcNZMask(void)
|
||||||
|
|
||||||
{
|
{
|
||||||
vector<PcodeOp *> opstack;
|
vector<PcodeOpNode> opstack;
|
||||||
vector<int4> slotstack;
|
|
||||||
list<PcodeOp *>::const_iterator oiter;
|
list<PcodeOp *>::const_iterator oiter;
|
||||||
|
|
||||||
for(oiter=beginOpAlive();oiter!=endOpAlive();++oiter) {
|
for(oiter=beginOpAlive();oiter!=endOpAlive();++oiter) {
|
||||||
PcodeOp *op = *oiter;
|
PcodeOp *op = *oiter;
|
||||||
if (op->isMark()) continue;
|
if (op->isMark()) continue;
|
||||||
opstack.push_back(op);
|
opstack.push_back(PcodeOpNode(op,0));
|
||||||
slotstack.push_back(0);
|
|
||||||
op->setMark();
|
op->setMark();
|
||||||
|
|
||||||
do {
|
do {
|
||||||
// Get next edge
|
// Get next edge
|
||||||
op = opstack.back();
|
PcodeOpNode &node( opstack.back() );
|
||||||
int4 slot = slotstack.back();
|
if (node.slot >= node.op->numInput()) { // If no edge left
|
||||||
if (slot >= op->numInput()) { // If no edge left
|
Varnode *outvn = node.op->getOut();
|
||||||
Varnode *outvn = op->getOut();
|
|
||||||
if (outvn != (Varnode *)0) {
|
if (outvn != (Varnode *)0) {
|
||||||
outvn->nzm = op->getNZMaskLocal(true);
|
outvn->nzm = node.op->getNZMaskLocal(true);
|
||||||
}
|
}
|
||||||
opstack.pop_back(); // Pop a level
|
opstack.pop_back(); // Pop a level
|
||||||
slotstack.pop_back();
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
slotstack.back() = slot + 1; // Advance to next input
|
int4 oldslot = node.slot;
|
||||||
|
node.slot += 1; // Advance to next input
|
||||||
// Determine if we want to traverse this edge
|
// Determine if we want to traverse this edge
|
||||||
if (op->code() == CPUI_MULTIEQUAL) {
|
if (node.op->code() == CPUI_MULTIEQUAL) {
|
||||||
if (op->getParent()->isLoopIn(slot)) // Clip looping edges
|
if (node.op->getParent()->isLoopIn(oldslot)) // Clip looping edges
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// Traverse edge indicated by slot
|
// Traverse edge indicated by slot
|
||||||
Varnode *vn = op->getIn(slot);
|
Varnode *vn = node.op->getIn(oldslot);
|
||||||
if (!vn->isWritten()) {
|
if (!vn->isWritten()) {
|
||||||
if (vn->isConstant())
|
if (vn->isConstant())
|
||||||
vn->nzm = vn->getOffset();
|
vn->nzm = vn->getOffset();
|
||||||
|
@ -773,32 +770,32 @@ void Funcdata::calcNZMask(void)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (!vn->getDef()->isMark()) { // If haven't traversed before
|
else if (!vn->getDef()->isMark()) { // If haven't traversed before
|
||||||
opstack.push_back(vn->getDef());
|
opstack.push_back(PcodeOpNode(vn->getDef(),0));
|
||||||
slotstack.push_back(0);
|
|
||||||
vn->getDef()->setMark();
|
vn->getDef()->setMark();
|
||||||
}
|
}
|
||||||
} while(!opstack.empty());
|
} while(!opstack.empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vector<PcodeOp *> worklist;
|
||||||
// Clear marks and push ops with looping edges onto worklist
|
// Clear marks and push ops with looping edges onto worklist
|
||||||
for(oiter=beginOpAlive();oiter!=endOpAlive();++oiter) {
|
for(oiter=beginOpAlive();oiter!=endOpAlive();++oiter) {
|
||||||
PcodeOp *op = *oiter;
|
PcodeOp *op = *oiter;
|
||||||
op->clearMark();
|
op->clearMark();
|
||||||
if (op->code() == CPUI_MULTIEQUAL)
|
if (op->code() == CPUI_MULTIEQUAL)
|
||||||
opstack.push_back(op);
|
worklist.push_back(op);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Continue to propagate changes along all edges
|
// Continue to propagate changes along all edges
|
||||||
while(!opstack.empty()) {
|
while(!worklist.empty()) {
|
||||||
PcodeOp *op = opstack.back();
|
PcodeOp *op = worklist.back();
|
||||||
opstack.pop_back();
|
worklist.pop_back();
|
||||||
Varnode *vn = op->getOut();
|
Varnode *vn = op->getOut();
|
||||||
if (vn == (Varnode *)0) continue;
|
if (vn == (Varnode *)0) continue;
|
||||||
uintb nzmask = op->getNZMaskLocal(false);
|
uintb nzmask = op->getNZMaskLocal(false);
|
||||||
if (nzmask != vn->nzm) {
|
if (nzmask != vn->nzm) {
|
||||||
vn->nzm = nzmask;
|
vn->nzm = nzmask;
|
||||||
for(oiter=vn->beginDescend();oiter!=vn->endDescend();++oiter)
|
for(oiter=vn->beginDescend();oiter!=vn->endDescend();++oiter)
|
||||||
opstack.push_back(*oiter);
|
worklist.push_back(*oiter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -506,39 +506,33 @@ uintb JumpBasic::backup2Switch(Funcdata *fd,uintb output,Varnode *outvn,Varnode
|
||||||
void JumpBasic::findDeterminingVarnodes(PcodeOp *op,int4 slot)
|
void JumpBasic::findDeterminingVarnodes(PcodeOp *op,int4 slot)
|
||||||
|
|
||||||
{
|
{
|
||||||
vector<PcodeOp *> path;
|
vector<PcodeOpNode> path;
|
||||||
vector<int4> slotpath;
|
|
||||||
PcodeOp *curop;
|
|
||||||
Varnode *curvn;
|
|
||||||
bool firstpoint = false; // Have not seen likely switch variable yet
|
bool firstpoint = false; // Have not seen likely switch variable yet
|
||||||
|
|
||||||
path.push_back(op);
|
path.push_back(PcodeOpNode(op,slot));
|
||||||
slotpath.push_back(slot);
|
|
||||||
|
|
||||||
do { // Traverse through tree of inputs to final address
|
do { // Traverse through tree of inputs to final address
|
||||||
curop = path.back();
|
PcodeOpNode &node(path.back());
|
||||||
curvn = curop->getIn(slotpath.back());
|
Varnode *curvn = node.op->getIn(node.slot);
|
||||||
if (isprune(curvn)) { // Here is a node of the tree
|
if (isprune(curvn)) { // Here is a node of the tree
|
||||||
if (ispoint(curvn)) { // Is it a possible switch variable
|
if (ispoint(curvn)) { // Is it a possible switch variable
|
||||||
if (!firstpoint) { // If it is the first possible
|
if (!firstpoint) { // If it is the first possible
|
||||||
pathMeld.set(path,slotpath); // Take the current path as the result
|
pathMeld.set(path); // Take the current path as the result
|
||||||
firstpoint = true;
|
firstpoint = true;
|
||||||
}
|
}
|
||||||
else // If we have already seen at least one possible
|
else // If we have already seen at least one possible
|
||||||
pathMeld.meld(path,slotpath);
|
pathMeld.meld(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
slotpath.back() += 1;
|
path.back().slot += 1;
|
||||||
while(slotpath.back() >= path.back()->numInput()) {
|
while(path.back().slot >= path.back().op->numInput()) {
|
||||||
path.pop_back();
|
path.pop_back();
|
||||||
slotpath.pop_back();
|
|
||||||
if (path.empty()) break;
|
if (path.empty()) break;
|
||||||
slotpath.back() += 1;
|
path.back().slot += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else { // This varnode is not pruned
|
else { // This varnode is not pruned
|
||||||
path.push_back(curvn->getDef());
|
path.push_back(PcodeOpNode(curvn->getDef(),0));
|
||||||
slotpath.push_back(0);
|
|
||||||
}
|
}
|
||||||
} while(path.size() > 1);
|
} while(path.size() > 1);
|
||||||
if (pathMeld.empty()) { // Never found a likely point, which means that
|
if (pathMeld.empty()) { // Never found a likely point, which means that
|
||||||
|
@ -785,7 +779,7 @@ void PathMeld::internalIntersect(vector<int4> &parentMap)
|
||||||
/// \param cutOff is the number of PcodeOps with an input in the common path
|
/// \param cutOff is the number of PcodeOps with an input in the common path
|
||||||
/// \param parentMap is the map from old common Varnodes to the new common Varnodes
|
/// \param parentMap is the map from old common Varnodes to the new common Varnodes
|
||||||
/// \return the index of the last (earliest) Varnode in the common path or -1
|
/// \return the index of the last (earliest) Varnode in the common path or -1
|
||||||
int4 PathMeld::meldOps(const vector<PcodeOp *> &path,int4 cutOff,const vector<int4> &parentMap)
|
int4 PathMeld::meldOps(const vector<PcodeOpNode> &path,int4 cutOff,const vector<int4> &parentMap)
|
||||||
|
|
||||||
{
|
{
|
||||||
// First update opMeld.rootVn with new intersection information
|
// First update opMeld.rootVn with new intersection information
|
||||||
|
@ -804,7 +798,7 @@ int4 PathMeld::meldOps(const vector<PcodeOp *> &path,int4 cutOff,const vector<in
|
||||||
int4 meldPos = 0; // Ops moved from old opMeld into newMeld
|
int4 meldPos = 0; // Ops moved from old opMeld into newMeld
|
||||||
const BlockBasic *lastBlock = (const BlockBasic *)0;
|
const BlockBasic *lastBlock = (const BlockBasic *)0;
|
||||||
for(int4 i=0;i<cutOff;++i) {
|
for(int4 i=0;i<cutOff;++i) {
|
||||||
PcodeOp *op = path[i]; // Current op in the new path
|
PcodeOp *op = path[i].op; // Current op in the new path
|
||||||
PcodeOp *curOp = (PcodeOp *)0;
|
PcodeOp *curOp = (PcodeOp *)0;
|
||||||
while(meldPos < opMeld.size()) {
|
while(meldPos < opMeld.size()) {
|
||||||
PcodeOp *trialOp = opMeld[meldPos].op; // Current op in the old opMeld
|
PcodeOp *trialOp = opMeld[meldPos].op; // Current op in the old opMeld
|
||||||
|
@ -874,15 +868,14 @@ void PathMeld::set(const PathMeld &op2)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This container is initialized to hold a single data-flow path.
|
/// This container is initialized to hold a single data-flow path.
|
||||||
/// \param path is the list of PcodeOps in the path (in reverse execution order)
|
/// \param path is the list of PcodeOpNode edges in the path (in reverse execution order)
|
||||||
/// \param slot is the list of each Varnode presented as an input slot in the corresponding PcodeOp
|
void PathMeld::set(const vector<PcodeOpNode> &path)
|
||||||
void PathMeld::set(const vector<PcodeOp *> &path,const vector<int4> &slot)
|
|
||||||
|
|
||||||
{
|
{
|
||||||
for(int4 i=0;i<path.size();++i) {
|
for(int4 i=0;i<path.size();++i) {
|
||||||
PcodeOp *op = path[i];
|
const PcodeOpNode &node(path[i]);
|
||||||
Varnode *vn = op->getIn(slot[i]);
|
Varnode *vn = node.op->getIn(node.slot);
|
||||||
opMeld.push_back(RootedOp(op,i));
|
opMeld.push_back(RootedOp(node.op,i));
|
||||||
commonVn.push_back(vn);
|
commonVn.push_back(vn);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -921,23 +914,23 @@ void PathMeld::clear(void)
|
||||||
/// Add the new path, recalculating the set of Varnodes common to all paths.
|
/// Add the new path, recalculating the set of Varnodes common to all paths.
|
||||||
/// Paths are trimmed to ensure that any path that splits from the common intersection
|
/// Paths are trimmed to ensure that any path that splits from the common intersection
|
||||||
/// must eventually rejoin.
|
/// must eventually rejoin.
|
||||||
/// \param path is the new path of PcodeOps to meld, in reverse execution order
|
/// \param path is the new path of PcodeOpNode edges to meld, in reverse execution order
|
||||||
/// \param slot is the set of Varnodes in the new path presented as input slots to the corresponding PcodeOp
|
void PathMeld::meld(vector<PcodeOpNode> &path)
|
||||||
void PathMeld::meld(vector<PcodeOp *> &path,vector<int4> &slot)
|
|
||||||
|
|
||||||
{
|
{
|
||||||
vector<int4> parentMap;
|
vector<int4> parentMap;
|
||||||
|
|
||||||
for(int4 i=0;i<path.size();++i) {
|
for(int4 i=0;i<path.size();++i) {
|
||||||
Varnode *vn = path[i]->getIn(slot[i]);
|
PcodeOpNode &node(path[i]);
|
||||||
vn->setMark(); // Mark varnodes in the new path, so its easy to see intersection
|
node.op->getIn(node.slot)->setMark(); // Mark varnodes in the new path, so its easy to see intersection
|
||||||
}
|
}
|
||||||
internalIntersect(parentMap); // Calculate varnode intersection, and map from old intersection -> new
|
internalIntersect(parentMap); // Calculate varnode intersection, and map from old intersection -> new
|
||||||
int4 cutOff = -1;
|
int4 cutOff = -1;
|
||||||
|
|
||||||
// Calculate where the cutoff point is in the new path
|
// Calculate where the cutoff point is in the new path
|
||||||
for(int4 i=0;i<path.size();++i) {
|
for(int4 i=0;i<path.size();++i) {
|
||||||
Varnode *vn = path[i]->getIn(slot[i]);
|
PcodeOpNode &node(path[i]);
|
||||||
|
Varnode *vn = node.op->getIn(node.slot);
|
||||||
if (!vn->isMark()) { // If mark already cleared, we know it is in intersection
|
if (!vn->isMark()) { // If mark already cleared, we know it is in intersection
|
||||||
cutOff = i + 1; // Cut-off must at least be past this -vn-
|
cutOff = i + 1; // Cut-off must at least be past this -vn-
|
||||||
}
|
}
|
||||||
|
@ -948,7 +941,6 @@ void PathMeld::meld(vector<PcodeOp *> &path,vector<int4> &slot)
|
||||||
if (newCutoff >= 0) // If not all ops could be ordered
|
if (newCutoff >= 0) // If not all ops could be ordered
|
||||||
truncatePaths(newCutoff); // Cut off at the point where we couldn't order
|
truncatePaths(newCutoff); // Cut off at the point where we couldn't order
|
||||||
path.resize(cutOff);
|
path.resize(cutOff);
|
||||||
slot.resize(cutOff);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The starting Varnode, common to all paths, is provided as an index.
|
/// The starting Varnode, common to all paths, is provided as an index.
|
||||||
|
|
|
@ -73,15 +73,15 @@ class PathMeld {
|
||||||
vector<Varnode *> commonVn; ///< Varnodes in common with all paths
|
vector<Varnode *> commonVn; ///< Varnodes in common with all paths
|
||||||
vector<RootedOp> opMeld; ///< All the ops for the melded paths
|
vector<RootedOp> opMeld; ///< All the ops for the melded paths
|
||||||
void internalIntersect(vector<int4> &parentMap);
|
void internalIntersect(vector<int4> &parentMap);
|
||||||
int4 meldOps(const vector<PcodeOp *> &path,int4 cutOff,const vector<int4> &parentMap);
|
int4 meldOps(const vector<PcodeOpNode> &path,int4 cutOff,const vector<int4> &parentMap);
|
||||||
void truncatePaths(int4 cutPoint);
|
void truncatePaths(int4 cutPoint);
|
||||||
public:
|
public:
|
||||||
void set(const PathMeld &op2); ///< Copy paths from another container
|
void set(const PathMeld &op2); ///< Copy paths from another container
|
||||||
void set(const vector<PcodeOp *> &path,const vector<int4> &slot); ///< Initialize \b this to be a single path
|
void set(const vector<PcodeOpNode> &path); ///< Initialize \b this to be a single path
|
||||||
void set(PcodeOp *op,Varnode *vn); ///< Initialize \b this container to a single node "path"
|
void set(PcodeOp *op,Varnode *vn); ///< Initialize \b this container to a single node "path"
|
||||||
void append(const PathMeld &op2); ///< Append a new set of paths to \b this set of paths
|
void append(const PathMeld &op2); ///< Append a new set of paths to \b this set of paths
|
||||||
void clear(void); ///< Clear \b this to be an empty container
|
void clear(void); ///< Clear \b this to be an empty container
|
||||||
void meld(vector<PcodeOp *> &path,vector<int4> &slot); ///< Meld a new path into \b this container
|
void meld(vector<PcodeOpNode> &path); ///< Meld a new path into \b this container
|
||||||
void markPaths(bool val,int4 startVarnode); ///< Mark PcodeOps paths from the given start
|
void markPaths(bool val,int4 startVarnode); ///< Mark PcodeOps paths from the given start
|
||||||
int4 numCommonVarnode(void) const { return commonVn.size(); } ///< Return the number of Varnodes common to all paths
|
int4 numCommonVarnode(void) const { return commonVn.size(); } ///< Return the number of Varnodes common to all paths
|
||||||
int4 numOps(void) const { return opMeld.size(); } ///< Return the number of PcodeOps across all paths
|
int4 numOps(void) const { return opMeld.size(); } ///< Return the number of PcodeOps across all paths
|
||||||
|
|
|
@ -171,6 +171,106 @@ bool PcodeOp::isCseMatch(const PcodeOp *op) const
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Its possible for the order of operations to be rearranged in some instances but still keep
|
||||||
|
/// equivalent data-flow. Test if \b this operation can be moved to occur immediately after
|
||||||
|
/// a specified \e point operation. This currently only tests for movement within a basic block.
|
||||||
|
/// \param point is the specified point to move \b this after
|
||||||
|
/// \return \b true if the move is possible
|
||||||
|
bool PcodeOp::isMoveable(const PcodeOp *point) const
|
||||||
|
|
||||||
|
{
|
||||||
|
if (this == point) return true; // No movement necessary
|
||||||
|
bool movingLoad = false;
|
||||||
|
if (getEvalType() == PcodeOp::special) {
|
||||||
|
if (code() == CPUI_LOAD)
|
||||||
|
movingLoad = true; // Allow LOAD to be moved with additional restrictions
|
||||||
|
else
|
||||||
|
return false; // Don't move special ops
|
||||||
|
}
|
||||||
|
if (parent != point->parent) return false; // Not in the same block
|
||||||
|
if (output != (Varnode *)0) {
|
||||||
|
// Output cannot be moved past an op that reads it
|
||||||
|
list<PcodeOp *>::const_iterator iter = output->beginDescend();
|
||||||
|
list<PcodeOp *>::const_iterator enditer = output->endDescend();
|
||||||
|
while(iter != enditer) {
|
||||||
|
PcodeOp *readOp = *iter;
|
||||||
|
++iter;
|
||||||
|
if (readOp->parent != parent) continue;
|
||||||
|
if (readOp->start.getOrder() <= point->start.getOrder())
|
||||||
|
return false; // Is in the block and is read before (or at) -point-
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Only allow this op to be moved across a CALL in very restrictive circumstances
|
||||||
|
bool crossCalls = false;
|
||||||
|
if (getEvalType() != PcodeOp::special) {
|
||||||
|
// Check for a normal op where all inputs and output are not address tied
|
||||||
|
if (output != (Varnode *)0 && !output->isAddrTied() && !output->isPersist()) {
|
||||||
|
int4 i;
|
||||||
|
for(i=0;i<numInput();++i) {
|
||||||
|
const Varnode *vn = getIn(i);
|
||||||
|
if (vn->isAddrTied() || vn->isPersist())
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (i == numInput())
|
||||||
|
crossCalls = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
vector<const Varnode *> tiedList;
|
||||||
|
for(int4 i=0;i<numInput();++i) {
|
||||||
|
const Varnode *vn = getIn(i);
|
||||||
|
if (vn->isAddrTied())
|
||||||
|
tiedList.push_back(vn);
|
||||||
|
}
|
||||||
|
list<PcodeOp *>::iterator biter = basiciter;
|
||||||
|
do {
|
||||||
|
++biter;
|
||||||
|
PcodeOp *op = *biter;
|
||||||
|
if (op->getEvalType() == PcodeOp::special) {
|
||||||
|
switch (op->code()) {
|
||||||
|
case CPUI_LOAD:
|
||||||
|
if (output != (Varnode *)0) {
|
||||||
|
if (output->isAddrTied()) return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case CPUI_STORE:
|
||||||
|
if (movingLoad)
|
||||||
|
return false;
|
||||||
|
else {
|
||||||
|
if (!tiedList.empty()) return false;
|
||||||
|
if (output != (Varnode *)0) {
|
||||||
|
if (output->isAddrTied()) return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case CPUI_INDIRECT: // Let thru, deal with what's INDIRECTed around separately
|
||||||
|
case CPUI_SEGMENTOP:
|
||||||
|
case CPUI_CPOOLREF:
|
||||||
|
break;
|
||||||
|
case CPUI_CALL:
|
||||||
|
case CPUI_CALLIND:
|
||||||
|
case CPUI_NEW:
|
||||||
|
if (!crossCalls) return false;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (op->output != (Varnode *)0) {
|
||||||
|
if (movingLoad) {
|
||||||
|
if (op->output->isAddrTied()) return false;
|
||||||
|
}
|
||||||
|
for(int4 i=0;i<tiedList.size();++i) {
|
||||||
|
const Varnode *vn = tiedList[i];
|
||||||
|
if (vn->overlap(*op->output)>=0)
|
||||||
|
return false;
|
||||||
|
if (op->output->overlap(*vn)>=0)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while(biter != point->basiciter);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/// Set the behavioral class (opcode) of this operation. For most applications this should only be called
|
/// Set the behavioral class (opcode) of this operation. For most applications this should only be called
|
||||||
/// by the PcodeOpBank. This is fairly low-level but does cache various boolean flags associated with the opcode
|
/// by the PcodeOpBank. This is fairly low-level but does cache various boolean flags associated with the opcode
|
||||||
/// \param t_op is the behavioural class to set
|
/// \param t_op is the behavioural class to set
|
||||||
|
|
|
@ -206,6 +206,7 @@ public:
|
||||||
bool usesSpacebasePtr(void) const { return ((flags&PcodeOp::spacebase_ptr)!=0); }
|
bool usesSpacebasePtr(void) const { return ((flags&PcodeOp::spacebase_ptr)!=0); }
|
||||||
uintm getCseHash(void) const; ///< Return hash indicating possibility of common subexpression elimination
|
uintm getCseHash(void) const; ///< Return hash indicating possibility of common subexpression elimination
|
||||||
bool isCseMatch(const PcodeOp *op) const; ///< Return \b true if this and \e op represent common subexpressions
|
bool isCseMatch(const PcodeOp *op) const; ///< Return \b true if this and \e op represent common subexpressions
|
||||||
|
bool isMoveable(const PcodeOp *point) const; ///< Can \b this be moved to after \e point, without disturbing data-flow
|
||||||
TypeOp *getOpcode(void) const { return opcode; } ///< Get the opcode for this op
|
TypeOp *getOpcode(void) const { return opcode; } ///< Get the opcode for this op
|
||||||
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
|
||||||
|
@ -229,6 +230,16 @@ public:
|
||||||
bool inheritsSign(void) const { return opcode->inheritsSign(); } ///< Does this token inherit its sign from operands
|
bool inheritsSign(void) const { return opcode->inheritsSign(); } ///< Does this token inherit its sign from operands
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// \brief An edge in a data-flow path or graph
|
||||||
|
///
|
||||||
|
/// A minimal node for traversing expressions in the data-flow
|
||||||
|
struct PcodeOpNode {
|
||||||
|
PcodeOp *op; ///< The p-code end-point of the edge
|
||||||
|
int4 slot; ///< Slot indicating the input Varnode end-point of the edge
|
||||||
|
PcodeOpNode(void) { op = (PcodeOp *)0; slot = 0; } ///< Unused constructor
|
||||||
|
PcodeOpNode(PcodeOp *o,int4 s) { op = o; slot = s; } ///< Constructor
|
||||||
|
};
|
||||||
|
|
||||||
/// A map from sequence number (SeqNum) to PcodeOp
|
/// A map from sequence number (SeqNum) to PcodeOp
|
||||||
typedef map<SeqNum,PcodeOp *> PcodeOpTree;
|
typedef map<SeqNum,PcodeOp *> PcodeOpTree;
|
||||||
|
|
||||||
|
|
|
@ -57,6 +57,7 @@ OptionDatabase::OptionDatabase(Architecture *g)
|
||||||
registerOption(new OptionErrorTooManyInstructions());
|
registerOption(new OptionErrorTooManyInstructions());
|
||||||
registerOption(new OptionDefaultPrototype());
|
registerOption(new OptionDefaultPrototype());
|
||||||
registerOption(new OptionInferConstPtr());
|
registerOption(new OptionInferConstPtr());
|
||||||
|
registerOption(new OptionForLoops());
|
||||||
registerOption(new OptionInline());
|
registerOption(new OptionInline());
|
||||||
registerOption(new OptionNoReturn());
|
registerOption(new OptionNoReturn());
|
||||||
registerOption(new OptionStructAlign());
|
registerOption(new OptionStructAlign());
|
||||||
|
@ -245,6 +246,24 @@ string OptionInferConstPtr::apply(Architecture *glb,const string &p1,const strin
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// \class OptionForLoops
|
||||||
|
/// \brief Toggle whether the decompiler attempts to recover \e for-loop variables
|
||||||
|
///
|
||||||
|
/// Setting the first parameter to "on" causes the decompiler to search for a suitable loop variable
|
||||||
|
/// controlling iteration of a \e while-do block. The \e for-loop displays the following on a single line:
|
||||||
|
/// - loop variable initializer (optional)
|
||||||
|
/// - loop condition
|
||||||
|
/// - loop variable incrementer
|
||||||
|
///
|
||||||
|
string OptionForLoops::apply(Architecture *glb,const string &p1,const string &p2,const string &p3) const
|
||||||
|
|
||||||
|
{
|
||||||
|
glb->analyze_for_loops = onOrOff(p1);
|
||||||
|
|
||||||
|
string res = "Recovery of for-loops is " + p1;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
/// \class OptionInline
|
/// \class OptionInline
|
||||||
/// \brief Mark/unmark a specific function as \e inline
|
/// \brief Mark/unmark a specific function as \e inline
|
||||||
///
|
///
|
||||||
|
|
|
@ -96,6 +96,12 @@ public:
|
||||||
virtual string apply(Architecture *glb,const string &p1,const string &p2,const string &p3) const;
|
virtual string apply(Architecture *glb,const string &p1,const string &p2,const string &p3) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class OptionForLoops : public ArchOption {
|
||||||
|
public:
|
||||||
|
OptionForLoops(void) { name = "analyzeforloops"; } ///< Constructor
|
||||||
|
virtual string apply(Architecture *glb,const string &p1,const string &p2,const string &p3) const;
|
||||||
|
};
|
||||||
|
|
||||||
class OptionInline : public ArchOption {
|
class OptionInline : public ArchOption {
|
||||||
public:
|
public:
|
||||||
OptionInline(void) { name = "inline"; } ///< Constructor
|
OptionInline(void) { name = "inline"; } ///< Constructor
|
||||||
|
|
|
@ -2629,11 +2629,68 @@ void PrintC::emitBlockIf(const BlockIf *bl)
|
||||||
popMod();
|
popMod();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Print the loop using the keyword \e for, followed by a semicolon separated
|
||||||
|
/// - Initializer statement
|
||||||
|
/// - Condition statment
|
||||||
|
/// - Iterate statement
|
||||||
|
///
|
||||||
|
/// Then print the body of the loop
|
||||||
|
void PrintC::emitForLoop(const BlockWhileDo *bl)
|
||||||
|
|
||||||
|
{
|
||||||
|
const PcodeOp *op;
|
||||||
|
int4 indent;
|
||||||
|
|
||||||
|
pushMod();
|
||||||
|
unsetMod(no_branch|only_branch);
|
||||||
|
emitAnyLabelStatement(bl);
|
||||||
|
emit->tagLine();
|
||||||
|
op = bl->getBlock(0)->lastOp();
|
||||||
|
emit->tagOp("for",EmitXml::keyword_color,op);
|
||||||
|
emit->spaces(1);
|
||||||
|
int4 id1 = emit->openParen('(');
|
||||||
|
pushMod();
|
||||||
|
setMod(comma_separate);
|
||||||
|
op = bl->getInitializeOp(); // Emit the (optional) initializer statement
|
||||||
|
if (op != (PcodeOp *)0) {
|
||||||
|
int4 id3 = emit->beginStatement(op);
|
||||||
|
emitExpression(op);
|
||||||
|
emit->endStatement(id3);
|
||||||
|
}
|
||||||
|
emit->print(";");
|
||||||
|
emit->spaces(1);
|
||||||
|
bl->getBlock(0)->emit(this); // Emit the conditional statement
|
||||||
|
emit->print(";");
|
||||||
|
emit->spaces(1);
|
||||||
|
op = bl->getIterateOp(); // Emit the iterator statement
|
||||||
|
int4 id4 = emit->beginStatement(op);
|
||||||
|
emitExpression(op);
|
||||||
|
emit->endStatement(id4);
|
||||||
|
popMod();
|
||||||
|
emit->closeParen(')',id1);
|
||||||
|
emit->spaces(1);
|
||||||
|
indent = emit->startIndent();
|
||||||
|
emit->print("{");
|
||||||
|
setMod(no_branch); // Dont print goto at bottom of clause
|
||||||
|
int4 id2 = emit->beginBlock(bl->getBlock(1));
|
||||||
|
bl->getBlock(1)->emit(this);
|
||||||
|
emit->endBlock(id2);
|
||||||
|
emit->stopIndent(indent);
|
||||||
|
emit->tagLine();
|
||||||
|
emit->print("}");
|
||||||
|
popMod();
|
||||||
|
}
|
||||||
|
|
||||||
void PrintC::emitBlockWhileDo(const BlockWhileDo *bl)
|
void PrintC::emitBlockWhileDo(const BlockWhileDo *bl)
|
||||||
|
|
||||||
{
|
{
|
||||||
const PcodeOp *op;
|
const PcodeOp *op;
|
||||||
int4 indent;
|
int4 indent;
|
||||||
|
|
||||||
|
if (bl->getIterateOp() != (PcodeOp *)0) {
|
||||||
|
emitForLoop(bl);
|
||||||
|
return;
|
||||||
|
}
|
||||||
// whiledo block NEVER prints final branch
|
// whiledo block NEVER prints final branch
|
||||||
pushMod();
|
pushMod();
|
||||||
unsetMod(no_branch|only_branch);
|
unsetMod(no_branch|only_branch);
|
||||||
|
|
|
@ -157,6 +157,7 @@ protected:
|
||||||
void emitAnyLabelStatement(const FlowBlock *bl); ///< Emit any required label statement for a given control-flow block
|
void emitAnyLabelStatement(const FlowBlock *bl); ///< Emit any required label statement for a given control-flow block
|
||||||
void emitCommentGroup(const PcodeOp *inst); ///< Emit comments associated with a given statement
|
void emitCommentGroup(const PcodeOp *inst); ///< Emit comments associated with a given statement
|
||||||
void emitCommentFuncHeader(const Funcdata *fd); ///< Emit comments in the given function's header
|
void emitCommentFuncHeader(const Funcdata *fd); ///< Emit comments in the given function's header
|
||||||
|
void emitForLoop(const BlockWhileDo *bl); ///< Emit block as a \e for loop
|
||||||
void opFunc(const PcodeOp *op); ///< Push a \e functional expression based on the given p-code op to the RPN stack
|
void opFunc(const PcodeOp *op); ///< Push a \e functional expression based on the given p-code op to the RPN stack
|
||||||
void opTypeCast(const PcodeOp *op); ///< Push the given p-code op using type-cast syntax to the RPN stack
|
void opTypeCast(const PcodeOp *op); ///< Push the given p-code op using type-cast syntax to the RPN stack
|
||||||
void opHiddenFunc(const PcodeOp *op); ///< Push the given p-code op as a hidden token
|
void opHiddenFunc(const PcodeOp *op); ///< Push the given p-code op as a hidden token
|
||||||
|
|
|
@ -488,6 +488,62 @@ void HighVariable::saveXml(ostream &s) const
|
||||||
s << "</high>";
|
s << "</high>";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Given a Varnode at the root of an expression, we collect all the \e explicit HighVariables
|
||||||
|
/// involved in the expression. This should only be run after \e explicit and \e implicit
|
||||||
|
/// properties have been computed on Varnodes. The expression is traced back from the root
|
||||||
|
/// until explicit Varnodes are encountered; then their HighVariable is marked and added to the list.
|
||||||
|
/// The routine returns a value based on PcodeOps encountered in the expression:
|
||||||
|
/// - 1 for call instructions
|
||||||
|
/// - 2 for LOAD instructions
|
||||||
|
/// - 3 for both call and LOAD
|
||||||
|
/// - 0 for no calls or LOADS
|
||||||
|
///
|
||||||
|
/// \param vn is the given root Varnode of the expression
|
||||||
|
/// \param highList will hold the collected HighVariables
|
||||||
|
/// \return a value based on call and LOAD instructions in the expression
|
||||||
|
int4 HighVariable::markExpression(Varnode *vn,vector<HighVariable *> &highList)
|
||||||
|
|
||||||
|
{
|
||||||
|
HighVariable *high = vn->getHigh();
|
||||||
|
high->setMark();
|
||||||
|
highList.push_back(high);
|
||||||
|
int4 retVal = 0;
|
||||||
|
if (!vn->isWritten()) return retVal;
|
||||||
|
|
||||||
|
vector<PcodeOpNode> path;
|
||||||
|
PcodeOp *op = vn->getDef();
|
||||||
|
if (op->isCall())
|
||||||
|
retVal |= 1;
|
||||||
|
if (op->code() == CPUI_LOAD)
|
||||||
|
retVal |= 2;
|
||||||
|
path.push_back(PcodeOpNode(op,0));
|
||||||
|
while(!path.empty()) {
|
||||||
|
PcodeOpNode &node(path.back());
|
||||||
|
if (node.op->numInput() <= node.slot) {
|
||||||
|
path.pop_back();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Varnode *vn = node.op->getIn(node.slot);
|
||||||
|
node.slot += 1;
|
||||||
|
if (vn->isAnnotation()) continue;
|
||||||
|
if (vn->isExplicit()) {
|
||||||
|
high = vn->getHigh();
|
||||||
|
if (high->isMark()) continue; // Already in the list
|
||||||
|
high->setMark();
|
||||||
|
highList.push_back(high);
|
||||||
|
continue; // Truncate at explicit
|
||||||
|
}
|
||||||
|
if (!vn->isWritten()) continue;
|
||||||
|
op = vn->getDef();
|
||||||
|
if (op->isCall())
|
||||||
|
retVal |= 1;
|
||||||
|
if (op->code() == CPUI_LOAD)
|
||||||
|
retVal |= 2;
|
||||||
|
path.push_back(PcodeOpNode(vn->getDef(),0));
|
||||||
|
}
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef MERGEMULTI_DEBUG
|
#ifdef MERGEMULTI_DEBUG
|
||||||
/// \brief Check that there are no internal Cover intersections within \b this
|
/// \brief Check that there are no internal Cover intersections within \b this
|
||||||
///
|
///
|
||||||
|
|
|
@ -137,6 +137,7 @@ public:
|
||||||
// Varnode *findGlobalRep(void) const;
|
// Varnode *findGlobalRep(void) const;
|
||||||
static bool compareName(Varnode *vn1,Varnode *vn2); ///< Determine which given Varnode is most nameable
|
static bool compareName(Varnode *vn1,Varnode *vn2); ///< Determine which given Varnode is most nameable
|
||||||
static bool compareJustLoc(const Varnode *a,const Varnode *b); ///< Compare based on storage location
|
static bool compareJustLoc(const Varnode *a,const Varnode *b); ///< Compare based on storage location
|
||||||
|
static int4 markExpression(Varnode *vn,vector<HighVariable *> &highList); ///< Mark and collect variables in expression
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -2556,6 +2556,26 @@
|
||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
<varlistentry id="AnalysisForLoops">
|
||||||
|
<term><emphasis role="bold">Recover -for- loops</emphasis></term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
When this is toggle <emphasis>on</emphasis>, the decompiler attempts to pinpoint
|
||||||
|
variables that control the iteration over specific loops in the function body.
|
||||||
|
When these <emphasis>loop</emphasis> variables are discovered, the loop is
|
||||||
|
rendered using a standard <emphasis role="bold">for</emphasis> loop header
|
||||||
|
that contains an initializer statement, condition, and iterating statement.
|
||||||
|
<informalexample>
|
||||||
|
<code>for (iVar2 = 10; iVar2 < len; iVar2 = iVar2 + 1) { ...</code>
|
||||||
|
</informalexample>
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
If the toggle is <emphasis>off</emphasis>, the loop is displayed using
|
||||||
|
<emphasis role="bold">while</emphasis> syntax, with any initializer and
|
||||||
|
iterating statements mixed in with the loop body or preceding basic blocks.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
<varlistentry id="AnalysisUnreachable">
|
<varlistentry id="AnalysisUnreachable">
|
||||||
<term><emphasis role="bold">Eliminate unreachable code</emphasis></term>
|
<term><emphasis role="bold">Eliminate unreachable code</emphasis></term>
|
||||||
<listitem>
|
<listitem>
|
||||||
|
|
|
@ -158,6 +158,28 @@
|
||||||
</p>
|
</p>
|
||||||
</dd>
|
</dd>
|
||||||
<dt>
|
<dt>
|
||||||
|
<a name="AnalysisForLoops"></a><span class="term"><span class="bold"><strong>Recover -for- loops</strong></span></span>
|
||||||
|
</dt>
|
||||||
|
<dd>
|
||||||
|
<p>
|
||||||
|
When this is toggle <span class="emphasis"><em>on</em></span>, the decompiler attempts to pinpoint
|
||||||
|
variables that control the iteration over specific loops in the function body.
|
||||||
|
When these <span class="emphasis"><em>loop</em></span> variables are discovered, the loop is
|
||||||
|
rendered using a standard <span class="bold"><strong>for</strong></span> loop header
|
||||||
|
that contains an initializer statement, condition, and iterating statement.
|
||||||
|
</p>
|
||||||
|
<div class="informalexample">
|
||||||
|
<code class="code">for (iVar2 = 10; iVar2 < len; iVar2 = iVar2 + 1) { ...</code>
|
||||||
|
</div>
|
||||||
|
<p>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
If the toggle is <span class="emphasis"><em>off</em></span>, the loop is displayed using
|
||||||
|
<span class="bold"><strong>while</strong></span> syntax, with any initializer and
|
||||||
|
iterating statements mixed in with the loop body or preceding basic blocks.
|
||||||
|
</p>
|
||||||
|
</dd>
|
||||||
|
<dt>
|
||||||
<a name="AnalysisUnreachable"></a><span class="term"><span class="bold"><strong>Eliminate unreachable code</strong></span></span>
|
<a name="AnalysisUnreachable"></a><span class="term"><span class="bold"><strong>Eliminate unreachable code</strong></span></span>
|
||||||
</dt>
|
</dt>
|
||||||
<dd>
|
<dd>
|
||||||
|
|
|
@ -442,8 +442,8 @@ public class DecompileDebug {
|
||||||
Varnode.appendSpaceOffset(stringBuf, addr);
|
Varnode.appendSpaceOffset(stringBuf, addr);
|
||||||
stringBuf.append(">\n");
|
stringBuf.append(">\n");
|
||||||
for (ContextSymbol sym : ctxsymbols) {
|
for (ContextSymbol sym : ctxsymbols) {
|
||||||
int sbit = sym.getLow();
|
int sbit = sym.getInternalLow();
|
||||||
int ebit = sym.getHigh();
|
int ebit = sym.getInternalHigh();
|
||||||
int word = sbit / (8 * 4);
|
int word = sbit / (8 * 4);
|
||||||
int startbit = sbit - word * (8 * 4);
|
int startbit = sbit - word * (8 * 4);
|
||||||
int endbit = ebit - word * (8 * 4);
|
int endbit = ebit - word * (8 * 4);
|
||||||
|
|
|
@ -85,6 +85,13 @@ public class DecompileOptions {
|
||||||
private final static boolean INFERCONSTPTR_OPTIONDEFAULT = true;
|
private final static boolean INFERCONSTPTR_OPTIONDEFAULT = true;
|
||||||
private boolean inferconstptr;
|
private boolean inferconstptr;
|
||||||
|
|
||||||
|
private final static String ANALYZEFORLOOPS_OPTIONSTRING = "Analysis.Recover -for- loops";
|
||||||
|
private final static String ANALYZEFORLOOPS_OPTIONDESCRIPTION =
|
||||||
|
"If set, the decompiler attempts to recover for-loop variables, including their initializer, condition, " +
|
||||||
|
"and incrementer statements. Loop variable bounds are displayed as a formal -for- loop header";
|
||||||
|
private final static boolean ANALYZEFORLOOPS_OPTIONDEFAULT = true;
|
||||||
|
private boolean analyzeForLoops;
|
||||||
|
|
||||||
private final static String NULLTOKEN_OPTIONSTRING = "Display.Print 'NULL' for null pointers";
|
private final static String NULLTOKEN_OPTIONSTRING = "Display.Print 'NULL' for null pointers";
|
||||||
private final static String NULLTOKEN_OPTIONDESCRIPTION =
|
private final static String NULLTOKEN_OPTIONDESCRIPTION =
|
||||||
"If set, any zero valued pointer (null pointer) will " +
|
"If set, any zero valued pointer (null pointer) will " +
|
||||||
|
@ -366,6 +373,7 @@ public class DecompileOptions {
|
||||||
simplifyDoublePrecision = SIMPLIFY_DOUBLEPRECISION_OPTIONDEFAULT;
|
simplifyDoublePrecision = SIMPLIFY_DOUBLEPRECISION_OPTIONDEFAULT;
|
||||||
ignoreunimpl = IGNOREUNIMPL_OPTIONDEFAULT;
|
ignoreunimpl = IGNOREUNIMPL_OPTIONDEFAULT;
|
||||||
inferconstptr = INFERCONSTPTR_OPTIONDEFAULT;
|
inferconstptr = INFERCONSTPTR_OPTIONDEFAULT;
|
||||||
|
analyzeForLoops = ANALYZEFORLOOPS_OPTIONDEFAULT;
|
||||||
nullToken = NULLTOKEN_OPTIONDEFAULT;
|
nullToken = NULLTOKEN_OPTIONDEFAULT;
|
||||||
inplaceTokens = INPLACEOP_OPTIONDEFAULT;
|
inplaceTokens = INPLACEOP_OPTIONDEFAULT;
|
||||||
aliasBlock = ALIASBLOCK_OPTIONDEFAULT;
|
aliasBlock = ALIASBLOCK_OPTIONDEFAULT;
|
||||||
|
@ -426,6 +434,8 @@ public class DecompileOptions {
|
||||||
SIMPLIFY_DOUBLEPRECISION_OPTIONDEFAULT);
|
SIMPLIFY_DOUBLEPRECISION_OPTIONDEFAULT);
|
||||||
ignoreunimpl = opt.getBoolean(IGNOREUNIMPL_OPTIONSTRING, IGNOREUNIMPL_OPTIONDEFAULT);
|
ignoreunimpl = opt.getBoolean(IGNOREUNIMPL_OPTIONSTRING, IGNOREUNIMPL_OPTIONDEFAULT);
|
||||||
inferconstptr = opt.getBoolean(INFERCONSTPTR_OPTIONSTRING, INFERCONSTPTR_OPTIONDEFAULT);
|
inferconstptr = opt.getBoolean(INFERCONSTPTR_OPTIONSTRING, INFERCONSTPTR_OPTIONDEFAULT);
|
||||||
|
analyzeForLoops =
|
||||||
|
opt.getBoolean(ANALYZEFORLOOPS_OPTIONSTRING, ANALYZEFORLOOPS_OPTIONDEFAULT);
|
||||||
nullToken = opt.getBoolean(NULLTOKEN_OPTIONSTRING, NULLTOKEN_OPTIONDEFAULT);
|
nullToken = opt.getBoolean(NULLTOKEN_OPTIONSTRING, NULLTOKEN_OPTIONDEFAULT);
|
||||||
inplaceTokens = opt.getBoolean(INPLACEOP_OPTIONSTRING, INPLACEOP_OPTIONDEFAULT);
|
inplaceTokens = opt.getBoolean(INPLACEOP_OPTIONSTRING, INPLACEOP_OPTIONDEFAULT);
|
||||||
aliasBlock = opt.getEnum(ALIASBLOCK_OPTIONSTRING, ALIASBLOCK_OPTIONDEFAULT);
|
aliasBlock = opt.getEnum(ALIASBLOCK_OPTIONSTRING, ALIASBLOCK_OPTIONDEFAULT);
|
||||||
|
@ -547,6 +557,10 @@ public class DecompileOptions {
|
||||||
INFERCONSTPTR_OPTIONDEFAULT,
|
INFERCONSTPTR_OPTIONDEFAULT,
|
||||||
new HelpLocation(HelpTopics.DECOMPILER, "AnalysisInferConstants"),
|
new HelpLocation(HelpTopics.DECOMPILER, "AnalysisInferConstants"),
|
||||||
INFERCONSTPTR_OPTIONDESCRIPTION);
|
INFERCONSTPTR_OPTIONDESCRIPTION);
|
||||||
|
opt.registerOption(ANALYZEFORLOOPS_OPTIONSTRING,
|
||||||
|
ANALYZEFORLOOPS_OPTIONDEFAULT,
|
||||||
|
new HelpLocation(HelpTopics.DECOMPILER, "AnalysisForLoops"),
|
||||||
|
ANALYZEFORLOOPS_OPTIONDESCRIPTION);
|
||||||
opt.registerOption(NULLTOKEN_OPTIONSTRING,
|
opt.registerOption(NULLTOKEN_OPTIONSTRING,
|
||||||
NULLTOKEN_OPTIONDEFAULT,
|
NULLTOKEN_OPTIONDEFAULT,
|
||||||
new HelpLocation(HelpTopics.DECOMPILER, "DisplayNull"),
|
new HelpLocation(HelpTopics.DECOMPILER, "DisplayNull"),
|
||||||
|
@ -739,6 +753,7 @@ public class DecompileOptions {
|
||||||
|
|
||||||
appendOption(buf, "ignoreunimplemented", ignoreunimpl ? "on" : "off", "", "");
|
appendOption(buf, "ignoreunimplemented", ignoreunimpl ? "on" : "off", "", "");
|
||||||
appendOption(buf, "inferconstptr", inferconstptr ? "on" : "off", "", "");
|
appendOption(buf, "inferconstptr", inferconstptr ? "on" : "off", "", "");
|
||||||
|
appendOption(buf, "analyzeforloops", analyzeForLoops ? "on" : "off", "", "");
|
||||||
appendOption(buf, "nullprinting", nullToken ? "on" : "off", "", "");
|
appendOption(buf, "nullprinting", nullToken ? "on" : "off", "", "");
|
||||||
appendOption(buf, "inplaceops", inplaceTokens ? "on" : "off", "", "");
|
appendOption(buf, "inplaceops", inplaceTokens ? "on" : "off", "", "");
|
||||||
appendOption(buf, "aliasblock", aliasBlock.getOptionString(), "", "");
|
appendOption(buf, "aliasblock", aliasBlock.getOptionString(), "", "");
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
/* ###
|
/* ###
|
||||||
* IP: GHIDRA
|
* IP: GHIDRA
|
||||||
* REVIEWED: YES
|
|
||||||
*
|
*
|
||||||
* 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.
|
||||||
|
@ -20,10 +19,11 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.app.plugin.processors.sleigh.symbol;
|
package ghidra.app.plugin.processors.sleigh.symbol;
|
||||||
|
|
||||||
import ghidra.app.plugin.processors.sleigh.*;
|
import ghidra.app.plugin.processors.sleigh.SleighLanguage;
|
||||||
import ghidra.app.plugin.processors.sleigh.expression.*;
|
import ghidra.app.plugin.processors.sleigh.expression.*;
|
||||||
import ghidra.util.xml.*;
|
import ghidra.util.xml.SpecXmlUtils;
|
||||||
import ghidra.xml.*;
|
import ghidra.xml.XmlElement;
|
||||||
|
import ghidra.xml.XmlPullParser;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
@ -40,8 +40,40 @@ public class ContextSymbol extends ValueSymbol {
|
||||||
|
|
||||||
public VarnodeSymbol getVarnode() { return vn; }
|
public VarnodeSymbol getVarnode() { return vn; }
|
||||||
|
|
||||||
public int getLow() { return low; }
|
/**
|
||||||
public int getHigh() { return high; }
|
* Get starting bit of context value within its context register.
|
||||||
|
* @return the starting bit
|
||||||
|
*/
|
||||||
|
public int getLow() {
|
||||||
|
return low;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get ending bit of context value within its context register.
|
||||||
|
* @return the ending bit
|
||||||
|
*/
|
||||||
|
public int getHigh() {
|
||||||
|
return high;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the starting bit of the context value within the "global" buffer, after
|
||||||
|
* the values have been packed.
|
||||||
|
* @return the starting bit
|
||||||
|
*/
|
||||||
|
public int getInternalLow() {
|
||||||
|
return ((ContextField) patval).getStartBit();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the ending bit of the context value within the "global" buffer, after
|
||||||
|
* the values have been packed.
|
||||||
|
* @return the ending bit
|
||||||
|
*/
|
||||||
|
|
||||||
|
public int getInternalHigh() {
|
||||||
|
return ((ContextField) patval).getEndBit();
|
||||||
|
}
|
||||||
public boolean followsFlow() { return flow; }
|
public boolean followsFlow() { return flow; }
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue