GP-3840 Adjustment to FlowBlock::restrictedByConditional

This commit is contained in:
caheckman 2023-09-13 20:38:46 +00:00
parent c072972153
commit 0a23876e01
21 changed files with 147 additions and 34 deletions

View File

@ -12,6 +12,7 @@ src/decompile/.project||GHIDRA||||END|
src/decompile/cpp/.gitignore||GHIDRA||||END|
src/decompile/cpp/Doxyfile||GHIDRA|||Most of this file is autogenerated by doxygen which falls under the GPL - output from GPL products are NOT GPL! - mjbell4|END|
src/decompile/cpp/Makefile||GHIDRA||||END|
src/decompile/datatests/boolless.xml||GHIDRA||||END|
src/decompile/datatests/concat.xml||GHIDRA||||END|
src/decompile/datatests/condconst.xml||GHIDRA||||END|
src/decompile/datatests/condmulti.xml||GHIDRA||||END|

View File

@ -1026,11 +1026,6 @@ ALLEXTERNALS = NO
EXTERNAL_GROUPS = YES
# The PERL_PATH should be the absolute path and name of the perl script
# interpreter (i.e. the result of `which perl').
PERL_PATH = /usr/bin/perl
#---------------------------------------------------------------------------
# Configuration options related to the dot tool
#---------------------------------------------------------------------------

View File

@ -239,7 +239,9 @@ public:
void decodeFlowOverride(Decoder &decoder); ///< Set flow overrides from XML
virtual ~Architecture(void); ///< Destructor
virtual string getDescription(void) const { return archid; } ///< Get a string describing \b this architecture
/// \brief Get a string describing \b this architecture
/// \return the description
virtual string getDescription(void) const { return archid; }
/// \brief Print an error message to console
///

View File

@ -407,9 +407,15 @@ bool FlowBlock::restrictedByConditional(const FlowBlock *cond) const
{
if (sizeIn() == 1) return true; // Its impossible for any path to come through sibling to this
if (getImmedDom() != cond) return false; // This is not dominated by conditional block at all
bool seenCond = false;
for(int4 i=0;i<sizeIn();++i) {
const FlowBlock *inBlock = getIn(i);
if (inBlock == cond) continue; // The unique edge from cond to this
if (inBlock == cond) {
if (seenCond)
return false; // Coming in from cond block on multiple direct edges
seenCond = true;
continue;
}
while(inBlock != this) {
if (inBlock == cond) return false; // Must have come through sibling
inBlock = inBlock->getImmedDom();

View File

@ -34,15 +34,15 @@ class Varnode;
/// affectsTest() can do secondary testing to determine if the intersection should prevent merging.
class PcodeOpSet {
friend class Cover;
vector<PcodeOp *> opList; // Ops in this set, sorted on block index, then SeqNum::order
vector<int4> blockStart; // Index of first op in each non-empty block
bool is_pop; // Has the populate() method been called
vector<PcodeOp *> opList; ///< Ops in this set, sorted on block index, then SeqNum::order
vector<int4> blockStart; ///< Index of first op in each non-empty block
bool is_pop; ///< Has the populate() method been called
protected:
void addOp(PcodeOp *op) { opList.push_back(op); } ///< Add a PcodeOp into the set
void finalize(void); // Sort ops in the set into blocks
void finalize(void); ///< Sort ops in the set into blocks
public:
PcodeOpSet(void) { is_pop = false; }
bool isPopulated(void) const { return is_pop; } /// Return \b true if \b this set is populated
bool isPopulated(void) const { return is_pop; } ///< Return \b true if \b this set is populated
virtual ~PcodeOpSet(void) {}
/// \brief Populate the PcodeOp object in \b this set

View File

@ -709,7 +709,13 @@ public:
virtual void encode(Encoder &encoder) const=0; ///< Encode \b this as a \<scope> element
virtual void decode(Decoder &decoder)=0; ///< Decode \b this Scope from a \<scope> element
virtual void decodeWrappingAttributes(Decoder &decoder) {} ///< Restore attributes for \b this Scope from wrapping element
/// \brief Restore attributes for \b this from a parent element that is not a Scope
///
/// Attributes are read from the (already opened) element, prior to reading reading the
/// \<scope> element specific to \b this Scope
/// \param decoder is the stream decoder
virtual void decodeWrappingAttributes(Decoder &decoder) {}
virtual void printEntries(ostream &s) const=0; ///< Dump a description of all SymbolEntry objects to a stream
/// \brief Get the number of Symbols in the given category

View File

@ -949,8 +949,15 @@ public:
/// \return the maximum number of passes across all output parameters in \b this model
int4 getMaxOutputDelay(void) const { return output->getMaxDelay(); }
virtual bool isMerged(void) const { return false; } ///< Is \b this a merged prototype model
virtual bool isUnknown(void) const { return false; } ///< Is \b this an unrecognized prototype model
/// \brief Is \b this a merged prototype model
///
/// \return \b true if \b this is a merged form of multiple independent prototype models
virtual bool isMerged(void) const { return false; }
/// \brief If \b this an unrecognized prototype model
///
/// \return \b true if \b this is a placeholder for an unrecognized prototype model name
virtual bool isUnknown(void) const { return false; }
virtual void decode(Decoder &decoder); ///< Restore \b this model from a stream
static uint4 lookupEffect(const vector<EffectRecord> &efflist,const Address &addr,int4 size);
static int4 lookupRecord(const vector<EffectRecord> &efflist,int4 listSize,const Address &addr,int4 size);

View File

@ -25,7 +25,8 @@ ElementId ELEM_HIGHLIST = ElementId("highlist",117);
ElementId ELEM_JUMPTABLELIST = ElementId("jumptablelist",118);
ElementId ELEM_VARNODES = ElementId("varnodes",119);
/// \param nm is the (base) name of the function
/// \param nm is the (base) name of the function, as a formal symbol
/// \param disp is the name used when displaying the function name in output
/// \param scope is Symbol scope associated with the function
/// \param addr is the entry address for the function
/// \param sym is the symbol representing the function

View File

@ -1238,7 +1238,7 @@ void Heritage::guardOutputOverlap(PcodeOp *callOp,const Address &addr,int4 size,
///
/// \param fc is the call site potentially returning a value
/// \param addr is the starting address of the range
/// \param addr is the starting address of the range relative to the callee
/// \param transAddr is the starting address of the range relative to the callee
/// \param size is the size of the range in bytes
/// \param write is the set of new written Varnodes
/// \return \b true if the INDIRECTs were created

View File

@ -323,9 +323,19 @@ public:
virtual bool sanityCheck(Funcdata *fd,PcodeOp *indop,vector<Address> &addresstable)=0;
virtual JumpModel *clone(JumpTable *jt) const=0; ///< Clone \b this model
virtual void clear(void) {} ///< Clear any non-permanent aspects of the model
virtual void encode(Encoder &encoder) const {} ///< Encode this model to a stream
virtual void decode(Decoder &decoder) {} ///< Decode \b this model from a stream
/// \brief Clear any non-permanent aspects of the model
virtual void clear(void) {}
/// \brief Encode \b this model to a stream
///
/// \param encoder is the stream encoder
virtual void encode(Encoder &encoder) const {}
/// \brief Decode \b this model from a stream
///
/// \param decoder is the stream decoder
virtual void decode(Decoder &decoder) {}
};
/// \brief A trivial jump-table model, where the BRANCHIND input Varnode is the switch variable

View File

@ -49,9 +49,9 @@ class Funcdata;
/// of the Cover of an address tied Varnode and a PcodeOp in this set, affectsTest() can do
/// secondary testing of whether the Varnode is actually modified by the PcodeOp.
class StackAffectingOps : public PcodeOpSet {
Funcdata &data;
Funcdata &data; ///< The function containing these ops
public:
StackAffectingOps(Funcdata &fd) : data(fd) {}
StackAffectingOps(Funcdata &fd) : data(fd) {} ///< Constructor
virtual void populate(void);
virtual bool affectsTest(PcodeOp *op,Varnode *vn) const;
};

View File

@ -964,6 +964,13 @@ uint4 OptionSplitDatatypes::getOptionBit(const string &val)
throw LowlevelError("Unknown data-type split option: "+val);
}
/// \class OptionSplitDatatypes
/// \brief Control which data-type assignments are split into multiple COPY/LOAD/STORE operations
///
/// Any combination of the three options can be given:
/// - "struct" = Divide structure data-types into separate field assignments
/// - "array" = Divide array data-types into separate element assignments
/// - "pointer" = Divide assignments, via LOAD/STORE, through pointers
string OptionSplitDatatypes::apply(Architecture *glb,const string &p1,const string &p2,const string &p3) const
{
@ -987,6 +994,14 @@ string OptionSplitDatatypes::apply(Architecture *glb,const string &p1,const stri
return "Split data-type configuration set";
}
/// \class OptionNanIgnore
/// \brief Which Not a Number (NaN) operations should be ignored
///
/// The option controls which p-code NaN operations are replaced with a \b false constant, assuming
/// the input is a valid floating-point value.
/// - "none" = No operations are replaced
/// - "compare" = Replace NaN operations associated with floating-poing comparisons
/// - "all" = Replace all NaN operations
string OptionNanIgnore::apply(Architecture *glb,const string &p1,const string &p2,const string &p3) const
{

View File

@ -328,7 +328,9 @@ public:
/// Inform the emitter that a printing group is ending.
/// \param id is the id associated with the group (as returned by openGroup)
virtual void closeGroup(int4 id) {}
virtual void clear(void) { parenlevel = 0; indentlevel=0; pendPrint=(PendPrint *)0; } ///< Reset the emitter to its initial state
/// \brief Reset the emitter to its initial state
virtual void clear(void) { parenlevel = 0; indentlevel=0; pendPrint=(PendPrint *)0; }
virtual void setOutputStream(ostream *t)=0; ///< Set the output stream for the emitter
virtual ostream *getOutputStream(void) const=0; ///< Get the current output stream
virtual void spaces(int4 num,int4 bump=0);

View File

@ -5313,6 +5313,7 @@ Varnode *RuleSLess2Zero::getHiBit(PcodeOp *op)
/// - `-1 s< (V & 0xf000) => -1 s< V
/// - `CONCAT(V,W) s< 0 => V s< 0`
/// - `-1 s< CONCAT(V,W) => -1 s> V`
/// - `-1 s< (bool << #8*sz-1) => !bool`
///
/// There is a second set of forms where one side of the comparison is
/// built out of a high and low piece, where the high piece determines the
@ -5409,6 +5410,19 @@ int4 RuleSLess2Zero::applyOp(PcodeOp *op,Funcdata &data)
data.opSetInput(op, data.newConstant(avn->getSize(),calc_mask(avn->getSize())), 0);
return 1;
}
else if (feedOpCode == CPUI_INT_LEFT) {
coeff = feedOp->getIn(1);
if (!coeff->isConstant() || coeff->getOffset() != lvn->getSize() * 8 - 1)
return 0;
avn = feedOp->getIn(0);
if (!avn->isWritten() || !avn->getDef()->isBoolOutput())
return 0;
// We have -1 s< (bool << #8*sz-1)
data.opSetOpcode(op, CPUI_BOOL_NEGATE);
data.opRemoveInput(op, 1);
data.opSetInput(op, avn, 0);
return 1;
}
}
}
else if (rvn->isConstant()) {

View File

@ -188,6 +188,12 @@ AddrSpace *SleighBuilder::generatePointer(const VarnodeTpl *vntpl,VarnodeData &v
return hand.space;
}
/// \brief Add in an additional offset to the address of a dynamic Varnode
///
/// The Varnode is ultimately read/written via LOAD/STORE operation AND has undergone a truncation
/// operation, so an additional offset needs to get added to the pointer referencing the Varnode.
/// \param op is the LOAD/STORE operation being generated
/// \param vntpl is the dynamic Varnode
void SleighBuilder::generatePointerAdd(PcodeData *op,const VarnodeTpl *vntpl)
{

View File

@ -2017,7 +2017,7 @@ bool SplitDatatype::testCopyConstraints(PcodeOp *copyOp)
/// \brief If the given Varnode is an extended precision constant, create split constants
///
/// Look for ZEXT(#c) and CONCAT(#c1,#c2) forms. Try to split into single precision Varnodes.
/// Look for ZEXT(c) and CONCAT(c1,c2) forms. Try to split into single precision Varnodes.
/// \param vn is the given Varnode
/// \param inVarnodes will contain the split constant Varnodes
/// \return \b true if the Varnode is an extended precision constant and the split is successful
@ -2138,7 +2138,7 @@ void SplitDatatype::buildInSubpieces(Varnode *rootVn,PcodeOp *followOp,vector<Va
/// Extract different pieces from the given root based on the offsets and
/// output data-types in \b dataTypePieces.
/// \param rootVn is the given root Varnode
/// \param inVarnodes is the container for the new Varnodes
/// \param outVarnodes is the container for the new Varnodes
void SplitDatatype::buildOutVarnodes(Varnode *rootVn,vector<Varnode *> &outVarnodes)
{

View File

@ -213,10 +213,30 @@ public:
virtual Datatype *getSubType(int8 off,int8 *newoff) const; ///< Recover component data-type one-level down
virtual Datatype *nearestArrayedComponentForward(int8 off,int8 *newoff,int8 *elSize) const;
virtual Datatype *nearestArrayedComponentBackward(int8 off,int8 *newoff,int8 *elSize) const;
virtual int4 getHoleSize(int4 off) const { return 0; } ///< Get number of bytes at the given offset that are padding
virtual int4 numDepend(void) const { return 0; } ///< Return number of component sub-types
virtual Datatype *getDepend(int4 index) const { return (Datatype *)0; } ///< Return the i-th component sub-type
virtual void printNameBase(ostream &s) const { if (!name.empty()) s<<name[0]; } ///< Print name as short prefix
/// \brief Get number of bytes at the given offset that are padding
///
/// For the given offset into \b this data-type, determine if the byte at that offset is considered
/// padding, and if so, return the number of bytes in the padding. Otherwise, return 0.
/// \return the number of bytes of padding or 0
virtual int4 getHoleSize(int4 off) const { return 0; }
/// \brief Get the number of component sub-types making up \b this data-type
///
/// \return the number of components
virtual int4 numDepend(void) const { return 0; }
/// \brief Get a specific component sub-type by index
///
/// \param index is the index specifying which sub-type to return
/// \return the i-th component sub-type
virtual Datatype *getDepend(int4 index) const { return (Datatype *)0; }
/// \brief Print (part of) the name of \b this data-type as short prefix for a label
///
/// This is used for building variable names to give some indication of the variable's underlying data-type
/// \param s is the stream write the name prefix to
virtual void printNameBase(ostream &s) const { if (!name.empty()) s<<name[0]; }
virtual int4 compare(const Datatype &op,int4 level) const; ///< Order types for propagation
virtual int4 compareDependency(const Datatype &op) const; ///< Compare for storage in tree structure
virtual void encode(Encoder &encoder) const; ///< Encode the data-type to a stream

View File

@ -53,8 +53,16 @@ protected:
uint4 addlflags; ///< Additional properties
string name; ///< Symbol denoting this operation
OpBehavior *behave; ///< Object for emulating the behavior of the op-code
virtual void setMetatypeIn(type_metatype val) {} ///< Set the data-type associated with inputs to this opcode
virtual void setMetatypeOut(type_metatype val) {} ///< Set the data-type associated with outputs of this opcode
/// \brief Set the data-type (as a meta-type) associated with inputs to this opcode
///
/// \param val is the data-type of inputs
virtual void setMetatypeIn(type_metatype val) {}
/// \brief Set the data-type (as a meta-type) associated with outputs of this opcode
///
/// \param val is the data-type of outputs
virtual void setMetatypeOut(type_metatype val) {}
virtual void setSymbol(const string &nm) { name = nm; } ///< Set the display symbol associated with the op-code
public:
TypeOp(TypeFactory *t,OpCode opc,const string &n); ///< Constructor

View File

@ -262,11 +262,11 @@ class HighIntersectTest {
void purgeHigh(HighVariable *high); ///< Remove cached intersection tests for a given HighVariable
bool testUntiedCallIntersection(HighVariable *tied,HighVariable *untied);
public:
HighIntersectTest(PcodeOpSet &cCover) : affectingOps(cCover) {}
HighIntersectTest(PcodeOpSet &cCover) : affectingOps(cCover) {} ///< Constructor
void moveIntersectTests(HighVariable *high1,HighVariable *high2);
bool updateHigh(HighVariable *a); ///< Make sure given HighVariable's Cover is up-to-date
bool intersection(HighVariable *a,HighVariable *b);
void clear(void) { highedgemap.clear(); }
void clear(void) { highedgemap.clear(); } ///< Clear any cached tests
};
/// The internal cover is marked as dirty. If \b this is a piece of a VariableGroup, it and all the other

View File

@ -347,7 +347,7 @@ void ScopeLocal::annotateRawStackPtr(void)
/// If the return value is passed back in a location whose address space holds \b this scope's variables,
/// assume the return value is unmapped, unless there is a specific alias into the location.
/// Mark the range as unmapped.
/// \param is the sorted list of alias offsets into the space
/// \param alias is the sorted list of alias offsets into the space
void ScopeLocal::checkUnaliasedReturn(const vector<uintb> &alias)
{

View File

@ -0,0 +1,20 @@
<decompilertest>
<!--
Function accessing a boolean test through the high-bit of a status register via INT_SRIGHT.
-->
<binaryimage arch="8051:BE:16:default:default">
<bytechunk space="CODE" offset="0xa000">
e5
52b40b005002740122
</bytechunk>
<symbol space="CODE" offset="0xa000" name="boolless"/>
</binaryimage>
<script>
<com>lo fu boolless</com>
<com>dec</com>
<com>print C</com>
<com>quit</com>
</script>
<stringmatch name="Boolean thru Less-than #1" min="1" max="1">if \(uINTMEM52 &lt; 0xb\)</stringmatch>
<stringmatch name="Boolean thru Less-than #2" min="0" max="0">&lt;&lt; 7</stringmatch>
</decompilertest>