/* * See the dyninst/COPYRIGHT file for copyright information. * * We provide the Paradyn Tools (below described as "Paradyn") * on an AS IS basis, and do not warrant its validity or performance. * We reserve the right to update, modify, or discontinue this * software at any time. We shall have no obligation to supply such * updates or modifications or any other form of support to you. * * By your use of Paradyn, you understand and agree that we (or any * other person or entity with proprietary rights in Paradyn) are * under no obligation to provide either maintenance services, * update services, notices of latent defects, or correction of * defects for Paradyn. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ // A simple forward slice using a search of the control flow graph. // Templated on a function that says when to stop slicing. #if !defined(_SLICING_H_) #define _SLICING_H_ #include #include "dyntypes.h" #include #include #include #include #include #include #include #include #include #include #include #include "util.h" #include "Node.h" #include "Edge.h" #include "AbslocInterface.h" #include namespace Dyninst { namespace ParseAPI { class Block; class Edge; class Function; } class Assignment; class AbsRegion; typedef dyncompat::shared_ptr AssignmentPtr; class Graph; typedef dyncompat::shared_ptr GraphPtr; namespace InstructionAPI { class Instruction; } typedef dyncompat::shared_ptr InstructionPtr; class Slicer; // Used in temp slicer; should probably // replace OperationNodes when we fix up // the DDG code. class DATAFLOW_EXPORT SliceNode : public Node { public: typedef dyncompat::shared_ptr Ptr; static SliceNode::Ptr create(AssignmentPtr ptr, ParseAPI::Block *block, ParseAPI::Function *func) { return Ptr(new SliceNode(ptr, block, func)); } ParseAPI::Block *block() const { return b_; } ParseAPI::Function *func() const { return f_; } Address addr() const; AssignmentPtr assign() const { return a_; } Node::Ptr copy() { return Node::Ptr(); } bool isVirtual() const { return false; } std::string format() const; virtual ~SliceNode() {} private: SliceNode(AssignmentPtr ptr, ParseAPI::Block *block, ParseAPI::Function *func) : a_(ptr), b_(block), f_(func) {} AssignmentPtr a_; ParseAPI::Block *b_; ParseAPI::Function *f_; friend class Slicer; }; class SliceEdge : public Edge { public: typedef dyncompat::shared_ptr Ptr; DATAFLOW_EXPORT static SliceEdge::Ptr create(SliceNode::Ptr source, SliceNode::Ptr target, AbsRegion const&data) { return Ptr(new SliceEdge(source, target, data)); } const AbsRegion &data() const { return data_; } private: SliceEdge(const SliceNode::Ptr source, const SliceNode::Ptr target, AbsRegion const& data) : Edge(source, target), data_(data) {} AbsRegion data_; }; class Slicer { public: typedef std::pair InsnInstance; typedef std::vector InsnVec; // An instruction cache to avoid redundant instruction decoding. // A user can optionaly provide a cache shared by multiple slicers. // The cache is keyed with basic block starting address. typedef dyn_hash_map InsnCache; DATAFLOW_EXPORT Slicer(AssignmentPtr a, ParseAPI::Block *block, ParseAPI::Function *func, bool cache = true, bool stackAnalysis = true); DATAFLOW_EXPORT Slicer(AssignmentPtr a, ParseAPI::Block *block, ParseAPI::Function *func, AssignmentConverter *ac); DATAFLOW_EXPORT Slicer(AssignmentPtr a, ParseAPI::Block *block, ParseAPI::Function *func, AssignmentConverter *ac, InsnCache *c); DATAFLOW_EXPORT ~Slicer(); DATAFLOW_EXPORT static bool isWidenNode(Node::Ptr n); struct DATAFLOW_EXPORT ContextElement { // We can implicitly find the callsite given a block, // since calls end blocks. It's easier to look up // the successor this way than with an address. ParseAPI::Function *func; // If non-NULL this must be an internal context // element, since we have an active call site. ParseAPI::Block *block; // To enter or leave a function we must be able to // map corresponding abstract regions. // In particular, we need to know the depth of the // stack in the caller. int stackDepth; ContextElement(ParseAPI::Function *f) : func(f), block(NULL), stackDepth(-1) {} ContextElement(ParseAPI::Function *f, long depth) : func(f), block(NULL), stackDepth(depth) {} }; // This should be sufficient... typedef std::deque Context; // Where we are in a particular search... struct DATAFLOW_EXPORT Location { // The block we're looking through ParseAPI::Function *func; ParseAPI::Block *block; // current block // Where we are in the block InsnVec::iterator current; InsnVec::iterator end; bool fwd; InsnVec::reverse_iterator rcurrent; InsnVec::reverse_iterator rend; Address addr() const { if(fwd) return (*current).second; else return (*rcurrent).second;} Location(ParseAPI::Function *f, ParseAPI::Block *b) : func(f), block(b), fwd(true){} Location() : func(NULL), block(NULL), fwd(true) {} }; // Describes an abstract region, a minimal context // (block and function), and the assignment that // relates to that region (uses or defines it, // depending on slice direction) // // A slice is composed of Elements; SliceFrames // keep a list of the currently active elements // that are at the `leading edge' of the // under-construction slice struct DATAFLOW_EXPORT Element { Element(ParseAPI::Block * b, ParseAPI::Function * f, AbsRegion const& r, Assignment::Ptr p) : block(b), func(f), reg(r), ptr(p) { } // basic comparator for ordering bool operator<(const Element& el) const { if (ptr->addr() < el.ptr->addr()) { return true; } if (el.ptr->addr() < ptr->addr()) { return false; } if (ptr->out() < el.ptr->out()) { return true; } return false; } ParseAPI::Block * block; ParseAPI::Function * func; AbsRegion reg; Assignment::Ptr ptr; }; // State for recursive slicing is a context, location pair // and a list of AbsRegions that are being searched for. struct DATAFLOW_EXPORT SliceFrame { SliceFrame( Location const& l, Context const& c) : loc(l), con(c), valid(true) { } SliceFrame() : valid(true) { } SliceFrame(bool v) : valid(v) { } // Active slice nodes -- describe regions // that are currently under scrutiny std::map > active; typedef std::map > ActiveMap; Location loc; Context con; bool valid; Address addr() const { return loc.addr(); } }; class Predicates { bool clearCache, controlFlowDep; public: typedef std::pair StackDepth_t; typedef std::stack CallStack_t; DATAFLOW_EXPORT bool performCacheClear() { if (clearCache) {clearCache = false; return true;} else return false; } DATAFLOW_EXPORT void setClearCache(bool b) { clearCache = b; } DATAFLOW_EXPORT bool searchForControlFlowDep() { return controlFlowDep; } DATAFLOW_EXPORT void setSearchForControlFlowDep(bool cfd) { controlFlowDep = cfd; } // A negative number means that we do not bound slicing size. DATAFLOW_EXPORT virtual int slicingSizeLimitFactor() { return -1; } DATAFLOW_EXPORT virtual bool allowImprecision() { return false; } DATAFLOW_EXPORT virtual bool widenAtPoint(AssignmentPtr) { return false; } DATAFLOW_EXPORT virtual bool endAtPoint(AssignmentPtr) { return false; } DATAFLOW_EXPORT virtual bool followCall(ParseAPI::Function * /*callee*/, CallStack_t & /*cs*/, AbsRegion /*argument*/) { return false; } DATAFLOW_EXPORT virtual std::vector followCallBackward(ParseAPI::Block * /*callerB*/, CallStack_t & /*cs*/, AbsRegion /*argument*/) { std::vector vec; return vec; } DATAFLOW_EXPORT virtual bool addPredecessor(AbsRegion /*reg*/) { return true; } DATAFLOW_EXPORT virtual bool widenAtAssignment(const AbsRegion & /*in*/, const AbsRegion & /*out*/) { return false; } DATAFLOW_EXPORT virtual ~Predicates() {} // Callback function when adding a new node to the slice. // Return true if we want to continue slicing DATAFLOW_EXPORT virtual bool addNodeCallback(AssignmentPtr, std::set &) { return true;} // Callback function after we have added new a node and corresponding new edges to the slice. // This function allows users to inspect the current slice graph and determine which abslocs // need further slicing and which abslocs are no longer interesting, by modifying the current // SliceFrame. DATAFLOW_EXPORT virtual bool modifyCurrentFrame(SliceFrame &, GraphPtr, Slicer*) {return true;} DATAFLOW_EXPORT virtual bool ignoreEdge(ParseAPI::Edge*) { return false;} DATAFLOW_EXPORT Predicates() : clearCache(false), controlFlowDep(false) {} }; DATAFLOW_EXPORT GraphPtr forwardSlice(Predicates &predicates); DATAFLOW_EXPORT GraphPtr backwardSlice(Predicates &predicates); private: typedef enum { forward, backward } Direction; // Our slicing is context-sensitive; that is, if we enter // a function foo from a caller bar, all return edges // from foo must enter bar. This makes an assumption that // the return address is not modified, but hey. // We represent this as a list of call sites. This is redundant // with the image_instPoint data structure, but hopefully that // one will be going away. bool getStackDepth(ParseAPI::Function *func, ParseAPI::Block *block, Address callAddr, long &height); // Add the newly called function to the given Context. void pushContext(Context &context, ParseAPI::Function *callee, ParseAPI::Block *callBlock, long stackDepth); // And remove it as appropriate void popContext(Context &context); typedef std::queue LocList; bool ReachableFromBothBranches(ParseAPI::Edge *e, std::vector &newE); // Used for keeping track of visited edges in the // slicing search struct CacheEdge { CacheEdge(Address src, Address trg) : s(src), t(trg) { } Address s; Address t; bool operator<(CacheEdge const& o) const { if(s < o.s) return true; else if(o.s < s) return false; else if(t < o.t) return true; else return false; } }; /* * An element that is a slicing `def' (where * def means `definition' in the backward case * and `use' in the forward case, along with * the associated AbsRegion that labels the slicing * edge. * * These two pieces of information, along with an * element describing the other end of the dependency, * are what you need to create a slice edge. */ struct Def { Def(Element const& e, AbsRegion const& r) : ele(e), data(r) { } Element ele; AbsRegion data; struct DefHasher { size_t operator() (const Def &o) const { return Assignment::AssignmentPtrHasher()(o.ele.ptr); } }; // only the Assignment::Ptr of an Element matters // for comparison bool operator<(Def const& o) const { if(ele.ptr < o.ele.ptr) return true; else if(o.ele.ptr < ele.ptr) return false; else if(data < o.data) return true; else return false; } bool operator==(Def const &o) const { if (ele.ptr != o.ele.ptr) return false; return data == o.data; } }; /* * A cache from AbsRegions -> Defs. * * Each node that has been visited in the search * has a DefCache that reflects the resolution of * any AbsRegions down-slice. If the node is visited * again through a different search path (if the graph * has fork-join structure), this caching prevents * expensive recursion */ class DefCache { public: DefCache() { } ~DefCache() { } // add the values from another defcache void merge(DefCache const& o); // replace mappings in this cache with those // from another void replace(DefCache const& o); std::set & get(AbsRegion const& r) { return defmap[r]; } bool defines(AbsRegion const& r) const { return defmap.find(r) != defmap.end(); } void print() const; private: std::map< AbsRegion, std::set > defmap; }; // For preventing insertion of duplicate edges // into the slice graph struct EdgeTuple { EdgeTuple(SliceNode::Ptr src, SliceNode::Ptr dst, AbsRegion const& reg) : s(src), d(dst), r(reg) { } bool operator<(EdgeTuple const& o) const { if(s < o.s) return true; else if(o.s < s) return false; else if(d < o.d) return true; else if(o.d < d) return false; else if(r < o.r) return true; else return false; } bool operator == (EdgeTuple const &o) const { if (s != o.s) return false; if (d != o.d) return false; return r == o.r; } SliceNode::Ptr s; SliceNode::Ptr d; AbsRegion r; }; // Shift an abs region by a given stack offset void shiftAbsRegion(AbsRegion const&callerReg, AbsRegion &calleeReg, long stack_depth, ParseAPI::Function *callee); // Shift all of the abstract regions active in the current frame void shiftAllAbsRegions( SliceFrame & cur, long stack_depth, ParseAPI::Function *callee); /* * Internal slicing support routines follow. See the * implementation file for descriptions of what these * routines actually do to implement slicing. */ GraphPtr sliceInternal(Direction dir, Predicates &predicates); void sliceInternalAux( GraphPtr g, Direction dir, Predicates &p, SliceFrame &cand, bool skip, std::map > & visited, std::unordered_map & single, std::unordered_map& cache); bool updateAndLink( GraphPtr g, Direction dir, SliceFrame & cand, DefCache & cache, Predicates &p); void updateAndLinkFromCache( GraphPtr g, Direction dir, SliceFrame & f, DefCache & cache); void removeBlocked( SliceFrame & f, std::set const& block); bool stopSlicing(SliceFrame::ActiveMap& active, GraphPtr g, Address addr, Direction dir); void markVisited( std::map > & visited, CacheEdge const& e, SliceFrame::ActiveMap const& active); void cachePotential( Direction dir, Assignment::Ptr assn, DefCache & cache); bool findMatch( GraphPtr g, Direction dir, SliceFrame const& cand, AbsRegion const& cur, Assignment::Ptr assn, std::vector & matches, DefCache & cache); bool getNextCandidates( Direction dir, Predicates & p, SliceFrame const& cand, std::vector & newCands); /* forward slicing */ bool getSuccessors( Predicates &p, SliceFrame const& cand, std::vector & newCands); bool handleCall( Predicates & p, SliceFrame & cur, bool & err); bool followCall( Predicates & p, ParseAPI::Block * target, SliceFrame & cur); bool handleCallDetails( SliceFrame & cur, ParseAPI::Block * caller_block); bool handleReturn( Predicates & p, SliceFrame & cur, bool & err); void handleReturnDetails( SliceFrame & cur); bool handleDefault( Direction dir, Predicates & p, ParseAPI::Edge * e, SliceFrame & cur, bool & err); /* backwards slicing */ bool getPredecessors( Predicates &p, SliceFrame const& cand, std::vector & newCands); bool handleCallBackward( Predicates & p, SliceFrame const& cand, std::vector & newCands, ParseAPI::Edge * e, bool & err); std::vector followCallBackward( Predicates & p, SliceFrame const& cand, AbsRegion const& reg, ParseAPI::Block * caller_block); bool handleCallDetailsBackward( SliceFrame & cur); bool handleReturnBackward( Predicates & p, SliceFrame const& cand, SliceFrame & newCand, ParseAPI::Edge * e, bool & err); bool handleReturnDetailsBackward( SliceFrame & cur, ParseAPI::Block * caller_block); bool followReturn( Predicates & p, SliceFrame const& cand, ParseAPI::Block * source); void handlePredecessorEdge(ParseAPI::Edge* e, Predicates& p, SliceFrame const& cand, std::vector & newCands, bool& err, SliceFrame& nf); /* general slicing support */ void constructInitialFrame( Direction dir, SliceFrame & initFrame); void widenAll(GraphPtr graph, Direction dir, SliceFrame const& frame); bool kills(AbsRegion const& reg, Assignment::Ptr &assign); void widen(GraphPtr graph, Direction dir, Element const&source); void insertPair(GraphPtr graph, Direction dir, Element const&source, Element const&target, AbsRegion const& data); void insertPair(GraphPtr graph, Direction dir, SliceNode::Ptr& source, SliceNode::Ptr& target, AbsRegion const& data); void convertInstruction(const InstructionAPI::Instruction &, Address, ParseAPI::Function *, ParseAPI::Block *, std::vector &); void fastForward(Location &loc, Address addr); void fastBackward(Location &loc, Address addr); SliceNode::Ptr widenNode(); void markAsEndNode(GraphPtr ret, Direction dir, Element ¤t); void markAsExitNode(GraphPtr ret, Element ¤t); void markAsEntryNode(GraphPtr ret, Element ¤t); void getInsns(Location &loc); public: void getInsnsBackward(Location &loc); private: void setAliases(Assignment::Ptr, Element &); SliceNode::Ptr createNode(Element const&); void cleanGraph(GraphPtr g); void promotePlausibleNodes(GraphPtr g, Direction d); ParseAPI::Block *getBlock(ParseAPI::Edge *e, Direction dir); void insertInitialNode(GraphPtr ret, Direction dir, SliceNode::Ptr aP); void mergeRecursiveCaches(std::unordered_map& sc, std::unordered_map& c, Address a); InsnCache* insnCache_; bool own_insnCache; AssignmentPtr a_; ParseAPI::Block *b_; ParseAPI::Function *f_; // Assignments map to unique slice nodes std::unordered_map created_; // cache to prevent edge duplication struct EdgeTupleHasher { size_t operator() (const EdgeTuple& et) const { size_t seed = (size_t)(et.s.get()); dyncompat::hash_combine( seed , (size_t)(et.d.get())); return seed; } }; std::unordered_map unique_edges_; // map of previous active maps. these are used to end recursion. typedef std::map > PrevMap; std::map prev_maps; // set of plausible entry/exit nodes. std::set plausibleNodes; // a stack and set of addresses that mirror our recursion. // these are used to detect loops and properly merge cache. std::deque
addrStack; std::set
addrSet; AssignmentConverter* converter; bool own_converter; SliceNode::Ptr widen_; public: // A set of edges that have been visited during slicing, // which can be used for external users to figure out // which part of code has been analyzed std::set visitedEdges; }; } #endif