mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-04 10:19:23 +02:00
134 lines
7.4 KiB
C++
134 lines
7.4 KiB
C++
/* ###
|
|
* IP: GHIDRA
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
/// \file cover.hh
|
|
/// \brief Classes describing the topological scope of variables within a function
|
|
#ifndef __COVER_HH__
|
|
#define __COVER_HH__
|
|
|
|
#include "type.hh"
|
|
|
|
namespace ghidra {
|
|
|
|
class PcodeOp;
|
|
class FlowBlock;
|
|
class Varnode;
|
|
|
|
/// \brief A set of PcodeOps that can be tested for Cover intersections
|
|
///
|
|
/// This is a set of PcodeOp objects, designed for quick intersection tests with a Cover. The set is
|
|
/// lazily constructed via its populate() method at the time the first intersection test is needed.
|
|
/// Once an intersection has been established between a PcodeOp in \b this set and a Varnode Cover,
|
|
/// 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
|
|
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
|
|
public:
|
|
PcodeOpSet(void) { is_pop = false; }
|
|
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
|
|
///
|
|
/// Call-back to the owner to lazily add PcodeOps to \b this set. The override method calls addOp() for
|
|
/// each PcodeOp it wants to add, then calls finalize() to make \b this set ready for intersection tests.
|
|
virtual void populate(void)=0;
|
|
|
|
/// \brief (Secondary) test that the given PcodeOp affects the Varnode
|
|
///
|
|
/// This method is called after an intersection of a PcodeOp in \b this set with a Varnode Cover has been
|
|
/// determined. This allows the owner to make a final determination if merging should be prevented.
|
|
/// \param op is the PcodeOp that intersects with the Varnode Cover
|
|
/// \param vn is the Varnode whose Cover is intersected
|
|
/// \return \b true if merging should be prevented
|
|
virtual bool affectsTest(PcodeOp *op,Varnode *vn) const=0;
|
|
|
|
void clear(void) { is_pop = false; opList.clear(); blockStart.clear(); } ///< Clear all PcodeOps in \b this
|
|
static bool compareByBlock(const PcodeOp *a,const PcodeOp *b); ///< Compare PcodeOps for \b this set
|
|
};
|
|
|
|
/// \brief The topological scope of a variable within a basic block
|
|
///
|
|
/// Within a basic block, the topological scope of a variable can be considered
|
|
/// a contiguous range of p-code operations. This range can be described with
|
|
/// a \e start and \e stop PcodeOp object, indicating all p-code operations between
|
|
/// the two inclusive. The \e start and \e stop may hold special encodings meaning:
|
|
/// - From the beginning of the block
|
|
/// - To the end of the block
|
|
class CoverBlock {
|
|
const PcodeOp *start; ///< Beginning of the range
|
|
const PcodeOp *stop; ///< End of the range
|
|
public:
|
|
CoverBlock(void) { start = (const PcodeOp *)0; stop = (const PcodeOp *)0; } ///< Construct empty/uncovered block
|
|
static uintm getUIndex(const PcodeOp *op); ///< Get the comparison index for a PcodeOp
|
|
const PcodeOp *getStart(void) const { return start; } ///< Get the start of the range
|
|
const PcodeOp *getStop(void) const { return stop; } ///< Get the end of the range
|
|
void clear(void) { start = (const PcodeOp *)0; stop = (const PcodeOp *)0; } ///< Clear \b this block to empty/uncovered
|
|
void setAll(void) {
|
|
start = (const PcodeOp *)0; stop = (const PcodeOp *)1; } ///< Mark whole block as covered
|
|
void setBegin(const PcodeOp *begin) {
|
|
start = begin; if (stop==(const PcodeOp *)0) stop = (const PcodeOp *)1; } ///< Reset start of range
|
|
void setEnd(const PcodeOp *end) { stop = end; } ///< Reset end of range
|
|
int4 intersect(const CoverBlock &op2) const; ///< Compute intersection with another CoverBlock
|
|
bool empty(void) const {
|
|
return ((start==(const PcodeOp *)0)&&(stop==(const PcodeOp *)0)); } ///< Return \b true if \b this is empty/uncovered
|
|
bool contain(const PcodeOp *point) const; ///< Check containment of given point
|
|
int4 boundary(const PcodeOp *point) const; ///< Characterize given point as boundary
|
|
void merge(const CoverBlock &op2); ///< Merge another CoverBlock into \b this
|
|
void print(ostream &s) const; ///< Dump a description to stream
|
|
};
|
|
|
|
/// \brief A description of the topological scope of a single variable object
|
|
///
|
|
/// The \b topological \b scope of a variable within a function is the set of
|
|
/// locations within the code of the function where that variable holds a variable.
|
|
/// For the decompiler, a high-level variable in this sense, HighVariable, is a collection
|
|
/// of Varnode objects. In order to merge Varnodes into a HighVariable, the topological
|
|
/// scope of each Varnode must not intersect because that would mean the high-level variable
|
|
/// holds different values at the same point in the function.
|
|
///
|
|
/// Internally this is implemented as a map from basic block to their non-empty CoverBlock
|
|
class Cover {
|
|
map<int4,CoverBlock> cover; ///< block index -> CoverBlock
|
|
static const CoverBlock emptyBlock; ///< Global empty CoverBlock for blocks not covered by \b this
|
|
void addRefRecurse(const FlowBlock *bl); ///< Fill-in \b this recursively from the given block
|
|
public:
|
|
void clear(void) { cover.clear(); } ///< Clear \b this to an empty Cover
|
|
int4 compareTo(const Cover &op2) const; ///< Give ordering of \b this and another Cover
|
|
const CoverBlock &getCoverBlock(int4 i) const; ///< Get the CoverBlock corresponding to the i-th block
|
|
int4 intersect(const Cover &op2) const; ///< Characterize the intersection between \b this and another Cover.
|
|
int4 intersectByBlock(int4 blk,const Cover &op2) const; ///< Characterize the intersection on a specific block
|
|
void intersectList(vector<int4> &listout,const Cover &op2,int4 level) const;
|
|
bool intersect(const PcodeOpSet &opSet,Varnode *rep) const; ///< Does \b this cover any PcodeOp in the given PcodeOpSet
|
|
bool contain(const PcodeOp *op,int4 max) const;
|
|
int4 containVarnodeDef(const Varnode *vn) const;
|
|
void merge(const Cover &op2); ///< Merge \b this with another Cover block by block
|
|
void rebuild(const Varnode *vn); ///< Reset \b this based on def-use of a single Varnode
|
|
void addDefPoint(const Varnode *vn); ///< Reset to the single point where the given Varnode is defined
|
|
void addRefPoint(const PcodeOp *ref,const Varnode *vn); ///< Add a variable read to \b this Cover
|
|
// void remove_refpoint(const PcodeOp *ref,const Varnode *vn) {
|
|
// rebuild(vn); } // Cheap but inefficient
|
|
void print(ostream &s) const; ///< Dump a description of \b this cover to stream
|
|
map<int4,CoverBlock>::const_iterator begin(void) const { return cover.begin(); } ///< Get beginning of CoverBlocks
|
|
map<int4,CoverBlock>::const_iterator end(void) const { return cover.end(); } ///< Get end of CoverBlocks
|
|
};
|
|
|
|
} // End namespace ghidra
|
|
#endif
|