/* ### * 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. */ #ifndef __GLOBALCONTEXT__ #define __GLOBALCONTEXT__ /// \file globalcontext.hh /// \brief Utilities for getting address-based context to the disassembler and decompiler #include "pcoderaw.hh" #include "partmap.hh" /// \brief Description of a context variable within the disassembly context \e blob /// /// Disassembly context is stored as individual (integer) values packed into a sequence of words. This class /// represents the info for encoding or decoding a single value within this sequence. A value is /// a contiguous range of bits within one context word. Size can range from 1 bit up to the size of a word. class ContextBitRange { int4 word; ///< Index of word containing this context value int4 startbit; ///< Starting bit of the value within its word (0=most significant bit 1=least significant) int4 endbit; ///< Ending bit of the value within its word int4 shift; ///< Right-shift amount to apply when unpacking this value from its word uintm mask; ///< Mask to apply (after shifting) when unpacking this value from its word public: ContextBitRange(void) { } ///< Constructor for use with restoreXml() ContextBitRange(int4 sbit,int4 ebit); ///< Construct a context value given an absolute bit range int4 getShift(void) const { return shift; } ///< Return the shift-amount for \b this value uintm getMask(void) const { return mask; } ///< Return the mask for \b this value int4 getWord(void) const { return word; } ///< Return the word index for \b this value /// \brief Set \b this value within a given context blob /// /// \param vec is the given context blob to alter (as an array of uintm words) /// \param val is the integer value to set void setValue(uintm *vec,uintm val) const { uintm newval = vec[word]; newval &= ~(mask<>shift)&mask); } }; /// \brief A tracked register (Varnode) and the value it contains /// /// This is the object returned when querying for tracked registers, /// via ContextDatabase::getTrackedSet(). It holds the storage details of the register and /// the actual value it holds at the point of the query. struct TrackedContext { VarnodeData loc; ///< Storage details of the register being tracked uintb val; ///< The value of the register void restoreXml(const Element *el,const AddrSpaceManager *manage); ///< Restore \b this from an XML stream void saveXml(ostream &s) const; ///< Save \b this to an XML stream }; typedef vector TrackedSet; ///< A set of tracked registers and their values (at one code point) /// \brief An interface to a database of disassembly/decompiler \b context information /// /// \b Context \b information is a set of named variables that hold concrete values at specific /// addresses in the target executable being analyzed. A variable can hold different values at /// different addresses, but a specific value at a specific address never changes. Analysis recovers /// these values over time, populating this database, and querying this database lets analysis /// provides concrete values for memory locations in context. /// /// Context variables come in two flavors: /// - \b Low-level \b context \b variables: /// These can affect instruction decoding. These can be as small as a single bit and need to /// be defined in the Sleigh specification (so that Sleigh knows how they effect disassembly). /// These variables are not mapped to normal memory locations with an address space and offset /// (although they often have a corresponding embedding into a normal memory location). /// The model to keep in mind is a control register with specialized bit-fields within it. /// - \b High-level \b tracked \b variables: /// These are normal memory locations that are to be treated as constants across some range of /// code. These are normally registers that are being tracked by the compiler outside the /// domain of normal local and global variables. They have a specific value established by /// the compiler coming into a function but are not supposed to be interpreted as a high-level /// variable. Typical examples are the direction flag (for \e string instructions) and segment /// registers. All tracked variables are interpreted as a constant value at the start of a /// function, although the memory location can be recycled for other calculations later in the /// function. /// /// Low-level context variables can be queried and set by name -- getVariable(), setVariable(), /// setVariableRegion() -- but the disassembler accesses all the variables at an address as a group /// via getContext(), setContextChangePoint(), setContextRegion(). In this setting, all the values /// are packed together in an array of words, a context \e blob (See ContextBitRange). /// /// Tracked variables are also queried as a group via getTrackedSet() and createSet(). These return /// a list of TrackedContext objects. class ContextDatabase { protected: static void saveTracked(ostream &s,const Address &addr,const TrackedSet &vec); static void restoreTracked(const Element *el,const AddrSpaceManager *manage,TrackedSet &vec); /// \brief Retrieve the context variable description object by name /// /// If the variable doesn't exist an exception is thrown. /// \param nm is the name of the context value /// \return the ContextBitRange object matching the name virtual ContextBitRange &getVariable(const string &nm)=0; /// \brief Retrieve the context variable description object by name /// /// If the variable doesn't exist an exception is thrown. /// \param nm is the name of the context value /// \return the ContextBitRange object matching the name virtual const ContextBitRange &getVariable(const string &nm) const=0; /// \brief Grab the context blob(s) for the given address range, marking bits that will be set /// /// This is an internal routine for obtaining the actual memory regions holding context values /// for the address range. This also informs the system which bits are getting set. A split is forced /// at the first address, and at least one memory region is passed back. The second address can be /// invalid in which case the memory region passed back is valid from the first address to whatever /// the next split point is. /// \param res will hold pointers to memory regions for the given range /// \param addr1 is the starting address of the range /// \param addr2 is (1 past) the last address of the range or is invalid /// \param num is the word index for the context value that will be set /// \param mask is a mask of the value being set (within its word) virtual void getRegionForSet(vector &res,const Address &addr1, const Address &addr2,int4 num,uintm mask)=0; /// \brief Grab the context blob(s) starting at the given address up to the first point of change /// /// This is an internal routine for obtaining the actual memory regions holding context values /// starting at the given address. A specific context value is specified, and all memory regions /// are returned up to the first address where that particular context value changes. /// \param res will hold pointers to memory regions being passed back /// \param addr is the starting address of the regions to fetch /// \param num is the word index for the specific context value being set /// \param mask is a mask of the context value being set (within its word) virtual void getRegionToChangePoint(vector &res,const Address &addr,int4 num,uintm mask)=0; /// \brief Retrieve the memory region holding all default context values /// /// This fetches the active memory holding the default context values on top of which all other context /// values are overlaid. /// \return the memory region holding all the default context values virtual uintm *getDefaultValue(void)=0; /// \brief Retrieve the memory region holding all default context values /// /// This fetches the active memory holding the default context values on top of which all other context /// values are overlaid. /// \return the memory region holding all the default context values virtual const uintm *getDefaultValue(void) const=0; public: virtual ~ContextDatabase() {} ///< Destructor /// \brief Retrieve the number of words (uintm) in a context \e blob /// /// \return the number of words virtual int4 getContextSize(void) const=0; /// \brief Register a new named context variable (as a bit range) with the database /// /// A new variable is registered by providing a name and the range of bits the value will occupy /// within the context blob. The full blob size is automatically increased if necessary. The variable /// must be contained within a single word, and all variables must be registered before any values can /// be set. /// \param nm is the name of the new variable /// \param sbit is the position of the variable's most significant bit within the blob /// \param ebit is the position of the variable's least significant bit within the blob virtual void registerVariable(const string &nm,int4 sbit,int4 ebit)=0; /// \brief Get the context blob of values associated with a given address /// /// \param addr is the given address /// \return the memory region holding the context values for the address virtual const uintm *getContext(const Address &addr) const=0; /// \brief Get the context blob of values associated with a given address and its bounding offsets /// /// In addition to the memory region, the range of addresses for which the region is valid /// is passed back as offsets into the address space. /// \param addr is the given address /// \param first will hold the starting offset of the valid range /// \param last will hold the ending offset of the valid range /// \return the memory region holding the context values for the address virtual const uintm *getContext(const Address &addr,uintb &first,uintb &last) const=0; /// \brief Get the set of default values for all tracked registers /// /// \return the list of TrackedContext objects virtual TrackedSet &getTrackedDefault(void)=0; /// \brief Get the set of tracked register values associated with the given address /// /// \param addr is the given address /// \return the list of TrackedContext objects virtual const TrackedSet &getTrackedSet(const Address &addr) const=0; /// \brief Create a tracked register set that is valid over the given range /// /// This really should be an internal routine. The created set is empty, old values are blown /// away. If old/default values are to be preserved, they must be copied back in. /// \param addr1 is the starting address of the given range /// \param addr2 is (1 past) the ending address of the given range /// \return the empty set of tracked register values virtual TrackedSet &createSet(const Address &addr1,const Address &addr2)=0; /// \brief Serialize the entire database to an XML stream /// /// \param s is the output stream virtual void saveXml(ostream &s) const=0; /// \brief Restore the state of \b this database object from a serialized XML stream /// /// \param el is the root element of the XML describing the database state /// \param manage is used to resolve address space references virtual void restoreXml(const Element *el,const AddrSpaceManager *manage)=0; /// \brief Add initial context state from XML tags in compiler/processor specifications /// /// The database can be configured with a consistent initial state by providing /// \ tags in either the compiler or processor specification file for the architecture /// \param el is a \ tag /// \param manage is used to resolve address space references virtual void restoreFromSpec(const Element *el,const AddrSpaceManager *manage)=0; void setVariableDefault(const string &nm,uintm val); ///< Provide a default value for a context variable uintm getDefaultValue(const string &nm) const; ///< Retrieve the default value for a context variable void setVariable(const string &nm,const Address &addr,uintm value); ///< Set a context value at the given address uintm getVariable(const string &nm,const Address &addr) const; ///< Retrieve a context value at the given address void setContextChangePoint(const Address &addr,int4 num,uintm mask,uintm value); void setContextRegion(const Address &addr1,const Address &addr2,int4 num,uintm mask,uintm value); void setVariableRegion(const string &nm,const Address &begad, const Address &endad,uintm value); uintb getTrackedValue(const VarnodeData &mem,const Address &point) const; }; /// \brief An in-memory implementation of the ContextDatabase interface /// /// Context blobs are held in a partition map on addresses. Any address within the map /// indicates a \e split point, where the value of a context variable was explicitly changed. /// Sets of tracked registers are held in a separate partition map. class ContextInternal : public ContextDatabase { /// \brief A context blob, holding context values across some range of code addresses /// /// This is an internal object that allocates the actual "array of words" for a context blob. /// An associated mask array holds 1-bits for context variables that were explicitly set for the /// specific split point. struct FreeArray { uintm *array; ///< The "array of words" holding context variable values uintm *mask; ///< The mask array indicating which variables are explicitly set int4 size; ///< The number of words in the array FreeArray(void) { size=0; array = (uintm *)0; mask = (uintm *)0; } ///< Construct an empty context blob ~FreeArray(void) { if (size!=0) { delete [] array; delete [] mask; } } ///< Destructor void reset(int4 sz); ///< Resize the context blob, preserving old values FreeArray &operator=(const FreeArray &op2); ///< Assignment operator }; int4 size; ///< Number of words in a context blob (for this architecture) map variables; ///< Map from context variable name to description object partmap database; ///< Partition map of context blobs (FreeArray) partmap trackbase; ///< Partition map of tracked register sets void saveContext(ostream &s,const Address &addr,const uintm *vec) const; void restoreContext(const Element *el,const Address &addr1,const Address &addr2); virtual ContextBitRange &getVariable(const string &nm); virtual const ContextBitRange &getVariable(const string &nm) const; virtual void getRegionForSet(vector &res,const Address &addr1, const Address &addr2,int4 num,uintm mask); virtual void getRegionToChangePoint(vector &res,const Address &addr,int4 num,uintm mask); virtual uintm *getDefaultValue(void) { return database.defaultValue().array; } virtual const uintm *getDefaultValue(void) const { return database.defaultValue().array; } public: ContextInternal(void) { size = 0; } virtual ~ContextInternal(void) {} virtual int4 getContextSize(void) const { return size; } virtual void registerVariable(const string &nm,int4 sbit,int4 ebit); virtual const uintm *getContext(const Address &addr) const { return database.getValue(addr).array; } virtual const uintm *getContext(const Address &addr,uintb &first,uintb &last) const; virtual TrackedSet &getTrackedDefault(void) { return trackbase.defaultValue(); } virtual const TrackedSet &getTrackedSet(const Address &addr) const { return trackbase.getValue(addr); } virtual TrackedSet &createSet(const Address &addr1,const Address &addr2); virtual void saveXml(ostream &s) const; virtual void restoreXml(const Element *el,const AddrSpaceManager *manage); virtual void restoreFromSpec(const Element *el,const AddrSpaceManager *manage); }; /// \brief A helper class for caching the active context blob to minimize database lookups /// /// This merely caches the last retrieved context blob ("array of words") and the range of /// addresses over which the blob is valid. It encapsulates the ContextDatabase itself and /// exposes a minimal interface (getContext() and setContext()). class ContextCache { ContextDatabase *database; ///< The encapsulated context database bool allowset; ///< If set to \b false, and setContext() call is dropped mutable AddrSpace *curspace; ///< Address space of the current valid range mutable uintb first; ///< Starting offset of the current valid range mutable uintb last; ///< Ending offset of the current valid range mutable const uintm *context; ///< The current cached context blob public: ContextCache(ContextDatabase *db); ///< Construct given a context database ContextDatabase *getDatabase(void) const { return database; } ///< Retrieve the encapsulated database object void allowSet(bool val) { allowset = val; } ///< Toggle whether setContext() calls are ignored void getContext(const Address &addr,uintm *buf) const; ///< Retrieve the context blob for the given address void setContext(const Address &addr,int4 num,uintm mask,uintm value); void setContext(const Address &addr1,const Address &addr2,int4 num,uintm mask,uintm value); }; #endif