mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-04 02:09:44 +02:00
1779 lines
110 KiB
C++
1779 lines
110 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 fspec.hh
|
|
/// \brief Definitions for specifying functions prototypes
|
|
|
|
#ifndef __FSPEC_HH__
|
|
#define __FSPEC_HH__
|
|
|
|
#include "modelrules.hh"
|
|
#include "rangemap.hh"
|
|
|
|
namespace ghidra {
|
|
|
|
class ProtoModel;
|
|
class JoinRecord;
|
|
|
|
extern AttributeId ATTRIB_CUSTOM; ///< Marshaling attribute "custom"
|
|
extern AttributeId ATTRIB_DOTDOTDOT; ///< Marshaling attribute "dotdotdot"
|
|
extern AttributeId ATTRIB_EXTENSION; ///< Marshaling attribute "extension"
|
|
extern AttributeId ATTRIB_HASTHIS; ///< Marshaling attribute "hasthis"
|
|
extern AttributeId ATTRIB_INLINE; ///< Marshaling attribute "inline"
|
|
extern AttributeId ATTRIB_KILLEDBYCALL; ///< Marshaling attribute "killedbycall"
|
|
extern AttributeId ATTRIB_MAXSIZE; ///< Marshaling attribute "maxsize"
|
|
extern AttributeId ATTRIB_MINSIZE; ///< Marshaling attribute "minsize"
|
|
extern AttributeId ATTRIB_MODELLOCK; ///< Marshaling attribute "modellock"
|
|
extern AttributeId ATTRIB_NORETURN; ///< Marshaling attribute "noreturn"
|
|
extern AttributeId ATTRIB_POINTERMAX; ///< Marshaling attribute "pointermax"
|
|
extern AttributeId ATTRIB_SEPARATEFLOAT; ///< Marshaling attribute "separatefloat"
|
|
extern AttributeId ATTRIB_STACKSHIFT; ///< Marshaling attribute "stackshift"
|
|
extern AttributeId ATTRIB_STRATEGY; ///< Marshaling attribute "strategy"
|
|
extern AttributeId ATTRIB_THISBEFORERETPOINTER; ///< Marshaling attribute "thisbeforeretpointer"
|
|
extern AttributeId ATTRIB_VOIDLOCK; ///< Marshaling attribute "voidlock"
|
|
|
|
extern ElementId ELEM_GROUP; ///< Marshaling element \<group>
|
|
extern ElementId ELEM_INTERNALLIST; ///< Marshaling element \<internallist>
|
|
extern ElementId ELEM_KILLEDBYCALL; ///< Marshaling element \<killedbycall>
|
|
extern ElementId ELEM_LIKELYTRASH; ///< Marshaling element \<likelytrash>
|
|
extern ElementId ELEM_LOCALRANGE; ///< Marshaling element \<localrange>
|
|
extern ElementId ELEM_MODEL; ///< Marshaling element \<model>
|
|
extern ElementId ELEM_PARAM; ///< Marshaling element \<param>
|
|
extern ElementId ELEM_PARAMRANGE; ///< Marshaling element \<paramrange>
|
|
extern ElementId ELEM_PENTRY; ///< Marshaling element \<pentry>
|
|
extern ElementId ELEM_PROTOTYPE; ///< Marshaling element \<prototype>
|
|
extern ElementId ELEM_RESOLVEPROTOTYPE; ///< Marshaling element \<resolveprototype>
|
|
extern ElementId ELEM_RETPARAM; ///< Marshaling element \<retparam>
|
|
extern ElementId ELEM_RETURNSYM; ///< Marshaling element \<returnsym>
|
|
extern ElementId ELEM_UNAFFECTED; ///< Marshaling element \<unaffected>
|
|
extern ElementId ELEM_INTERNAL_STORAGE; ///< Marshaling element \<internal_storage>
|
|
|
|
/// \brief Exception thrown when a prototype can't be modeled properly
|
|
struct ParamUnassignedError : public LowlevelError {
|
|
ParamUnassignedError(const string &s) : LowlevelError(s) {} ///< Constructor
|
|
};
|
|
|
|
/// \brief A contiguous range of memory that can be used to pass parameters
|
|
///
|
|
/// This range can be used to pass a single parameter (isExclusion() == \b true). This
|
|
/// is intended to model a parameter passed in a register. The logical value does not
|
|
/// have to fill the entire range. The size in bytes can range from a minimum, getMinSize(),
|
|
/// to the whole range, getSize(). Justification and extension of the logical value within
|
|
/// the range can be specified.
|
|
///
|
|
/// Alternately the range can be used as a resource for multiple parameters
|
|
/// (isExclusion() == \b false). In this case, the parameters are allocated sequentially
|
|
/// (usually) starting from the front of the range. The amount of space consumed by each
|
|
/// parameter is dictated by an \e alignment setting in bytes.
|
|
///
|
|
/// A ParamEntry can be associated with a particular class of data-types. Usually:
|
|
/// - TYPE_UNKNOWN for general purpose parameters
|
|
/// - TYPE_FLOAT for dedicated floating-point registers
|
|
class ParamEntry {
|
|
public:
|
|
enum {
|
|
force_left_justify = 1, ///< Big endian values are left justified within their slot
|
|
reverse_stack = 2, ///< Slots (for \e non-exlusion entries) are allocated in reverse order
|
|
smallsize_zext = 4, ///< Assume values that are below the max \b size are zero extended into this container
|
|
smallsize_sext = 8, ///< Assume values that are below the max \b size are sign extended into this container
|
|
// is_big_endian = 16, ///< Set if this value should be treated as big endian
|
|
smallsize_inttype = 0x20, ///< Assume values that are below the max \b size are sign OR zero extended based on integer type
|
|
smallsize_floatext = 0x40, ///< Assume values smaller than max \b size are floating-point extended to full size
|
|
extracheck_high = 0x80, ///< Perform extra checks during parameter recovery on most sig portion of the double
|
|
extracheck_low = 0x100, ///< Perform extra checks during parameter recovery on least sig portion of the double
|
|
is_grouped = 0x200, ///< This entry is grouped with other entries
|
|
overlapping = 0x400, ///< Overlaps an earlier entry (and doesn't consume additional resource slots)
|
|
first_storage = 0x800 ///< Entry is first in its storage class
|
|
};
|
|
enum {
|
|
no_containment, ///< Range neither contains nor is contained by a ParamEntry
|
|
contains_unjustified, ///< ParamEntry contains range, but the range does not cover the least significant bytes
|
|
contains_justified, ///< ParamEntry contains range, which covers the least significant bytes
|
|
contained_by ///< ParamEntry is contained by the range
|
|
};
|
|
private:
|
|
uint4 flags; ///< Boolean properties of the parameter
|
|
type_class type; ///< Data-type storage class that this entry must match
|
|
vector<int4> groupSet; ///< Group(s) \b this entry belongs to
|
|
AddrSpace *spaceid; ///< Address space containing the range
|
|
uintb addressbase; ///< Starting offset of the range
|
|
int4 size; ///< Size of the range in bytes
|
|
int4 minsize; ///< Minimum bytes allowed for the logical value
|
|
int4 alignment; ///< How much alignment (0 means only 1 logical value is allowed)
|
|
int4 numslots; ///< (Maximum) number of slots that can store separate parameters
|
|
JoinRecord *joinrec; ///< Non-null if this is logical variable from joined pieces
|
|
static const ParamEntry *findEntryByStorage(const list<ParamEntry> &entryList,const VarnodeData &vn);
|
|
void resolveFirst(list<ParamEntry> &curList); ///< Mark if \b this is the first ParamEntry in its storage class
|
|
void resolveJoin(list<ParamEntry> &curList); ///< Make adjustments for a \e join ParamEntry
|
|
void resolveOverlap(list<ParamEntry> &curList); ///< Make adjustments for ParamEntry that overlaps others
|
|
|
|
/// \brief Is the logical value left-justified within its container
|
|
bool isLeftJustified(void) const { return (((flags&force_left_justify)!=0)||(!spaceid->isBigEndian())); }
|
|
public:
|
|
ParamEntry(int4 grp) { groupSet.push_back(grp); } ///< Constructor for use with decode
|
|
int4 getGroup(void) const { return groupSet[0]; } ///< Get the group id \b this belongs to
|
|
const vector<int4> &getAllGroups(void) const { return groupSet; } ///< Get all group numbers \b this overlaps
|
|
bool groupOverlap(const ParamEntry &op2) const; ///< Check if \b this and op2 occupy any of the same groups
|
|
int4 getSize(void) const { return size; } ///< Get the size of the memory range in bytes.
|
|
int4 getMinSize(void) const { return minsize; } ///< Get the minimum size of a logical value contained in \b this
|
|
int4 getAlign(void) const { return alignment; } ///< Get the alignment of \b this entry
|
|
JoinRecord *getJoinRecord(void) const { return joinrec; } ///< Get record describing joined pieces (or null if only 1 piece)
|
|
type_class getType(void) const { return type; } ///< Get the data-type class associated with \b this
|
|
bool isExclusion(void) const { return (alignment==0); } ///< Return \b true if this holds a single parameter exclusively
|
|
bool isReverseStack(void) const { return ((flags & reverse_stack)!=0); } ///< Return \b true if parameters are allocated in reverse order
|
|
bool isGrouped(void) const { return ((flags & is_grouped)!=0); } ///< Return \b true if \b this is grouped with other entries
|
|
bool isOverlap(void) const { return ((flags & overlapping)!=0); } ///< Return \b true if \b this overlaps another entry
|
|
bool isFirstInClass(void) const { return ((flags & first_storage)!=0); } ///< Return \b true if \b this is the first entry in the storage class
|
|
bool subsumesDefinition(const ParamEntry &op2) const; ///< Does \b this subsume the definition of the given ParamEntry
|
|
bool containedBy(const Address &addr,int4 sz) const; ///< Is this entry contained by the given range
|
|
bool intersects(const Address &addr,int4 sz) const; ///< Does \b this intersect the given range in some way
|
|
int4 justifiedContain(const Address &addr,int4 sz) const; ///< Calculate endian aware containment
|
|
bool getContainer(const Address &addr,int4 sz,VarnodeData &res) const;
|
|
bool contains(const ParamEntry &op2) const; ///< Does \b this contain the given entry (as a subpiece)
|
|
OpCode assumedExtension(const Address &addr,int4 sz,VarnodeData &res) const;
|
|
int4 getSlot(const Address &addr,int4 skip) const;
|
|
AddrSpace *getSpace(void) const { return spaceid; } ///< Get the address space containing \b this entry
|
|
uintb getBase(void) const { return addressbase; } ///< Get the starting offset of \b this entry
|
|
Address getAddrBySlot(int4 &slot,int4 sz,int4 typeAlign) const;
|
|
void decode(Decoder &decoder,bool normalstack,bool grouped,list<ParamEntry> &curList);
|
|
bool isParamCheckHigh(void) const { return ((flags & extracheck_high)!=0); } ///< Return \b true if there is a high overlap
|
|
bool isParamCheckLow(void) const { return ((flags & extracheck_low)!=0); } ///< Return \b true if there is a low overlap
|
|
static void orderWithinGroup(const ParamEntry &entry1,const ParamEntry &entry2); ///< Enforce ParamEntry group ordering rules
|
|
};
|
|
|
|
/// \brief Class for storing ParamEntry objects in an interval range (rangemap)
|
|
class ParamEntryRange {
|
|
uintb first; ///< Starting offset of the ParamEntry's range
|
|
uintb last; ///< Ending offset of the ParamEntry's range
|
|
int4 position; ///< Position of the ParamEntry within the entire prototype list
|
|
ParamEntry *entry; ///< Pointer to the actual ParamEntry
|
|
|
|
/// \brief Helper class for initializing ParamEntryRange in a range map
|
|
class InitData {
|
|
friend class ParamEntryRange;
|
|
int4 position; ///< Position (within the full list) being assigned to the ParamEntryRange
|
|
ParamEntry *entry; ///< Underlying ParamEntry being assigned to the ParamEntryRange
|
|
public:
|
|
InitData(int4 pos,ParamEntry *e) { position = pos; entry = e; } ///< Constructor
|
|
};
|
|
|
|
/// \brief Helper class for subsorting on position
|
|
class SubsortPosition {
|
|
int4 position; ///< The position value
|
|
public:
|
|
SubsortPosition(void) {} ///< Constructor for use with rangemap
|
|
SubsortPosition(int4 pos) { position = pos; } ///< Construct given position
|
|
SubsortPosition(bool val) { position = val ? 1000000 : 0; } ///< Constructor minimal/maximal subsort
|
|
bool operator<(const SubsortPosition &op2) { return position < op2.position; } ///< Compare operation
|
|
};
|
|
public:
|
|
typedef uintb linetype; ///< The linear element for a rangemap
|
|
typedef SubsortPosition subsorttype; ///< The sub-sort object for a rangemap
|
|
typedef InitData inittype; ///< Initialization data for a ScopeMapper
|
|
|
|
ParamEntryRange(const inittype &data,uintb f,uintb l) {
|
|
first = f; last = l; position = data.position; entry = data.entry; } ///< Initialize the range
|
|
uintb getFirst(void) const { return first; } ///< Get the first address in the range
|
|
uintb getLast(void) const { return last; } ///< Get the last address in the range
|
|
subsorttype getSubsort(void) const { return SubsortPosition(position); } ///< Get the sub-subsort object
|
|
ParamEntry *getParamEntry(void) const { return entry; } ///< Get pointer to actual ParamEntry
|
|
};
|
|
typedef rangemap<ParamEntryRange> ParamEntryResolver; ///< A map from offset to ParamEntry
|
|
|
|
/// \brief A register or memory register that may be used to pass a parameter or return value
|
|
///
|
|
/// The parameter recovery utilities (see ParamActive) use this to denote a putative
|
|
/// parameter passing storage location. It is made up of the address and size of the memory range,
|
|
/// a set of properties about the use of the range (as a parameter) in context, and a link to
|
|
/// the matching part of the PrototypeModel.
|
|
///
|
|
/// Data-flow for the putative parameter is held directly by a Varnode. To quickly map to the
|
|
/// Varnode (which may or may not exist at points during the ParamTrial lifetime), the concept
|
|
/// of \b slot is used. ParamTrials are assigned a \e slot, starting at 1. For sub-function parameters,
|
|
/// this represents the actual input index of the Varnode in the corresponding CALL or CALLIND op.
|
|
/// For parameters, this gives the position within the list of possible input Varnodes in address order.
|
|
/// The \e slot ordering varies over the course of analysis and is unlikely to match
|
|
/// the final parameter ordering. The ParamTrial comparator sorts the trials in final parameter ordering.
|
|
class ParamTrial {
|
|
public:
|
|
enum {
|
|
checked = 1, ///< Trial has been checked
|
|
used = 2, ///< Trial is definitely used (final verdict)
|
|
defnouse = 4, ///< Trial is definitely not used
|
|
active = 8, ///< Trial looks active (hint that it is used)
|
|
unref = 0x10, ///< There is no direct reference to this parameter trial
|
|
killedbycall = 0x20, ///< Data in this location is unlikely to flow thru a func and still be a param
|
|
rem_formed = 0x40, ///< The trial is built out of a remainder operation
|
|
indcreate_formed = 0x80, ///< The trial is built out of an indirect creation
|
|
condexe_effect = 0x100, ///< This trial may be affected by conditional execution
|
|
ancestor_realistic = 0x200, ///< Trial has a realistic ancestor
|
|
ancestor_solid = 0x400 ///< Solid movement into the Varnode
|
|
};
|
|
private:
|
|
uint4 flags; ///< Boolean properties of the trial
|
|
Address addr; ///< Starting address of the memory range
|
|
int4 size; ///< Number of bytes in the memory range
|
|
int4 slot; ///< Slot assigned to this trial
|
|
const ParamEntry *entry; ///< PrototypeModel entry matching this trial
|
|
int4 offset; ///< "justified" offset into entry
|
|
int4 fixedPosition; ///< argument position if a fixed arg of a varargs function, else -1
|
|
public:
|
|
/// \brief Construct from components
|
|
ParamTrial(const Address &ad,int4 sz,int4 sl) { addr = ad; size = sz; slot = sl; flags=0; entry=(ParamEntry *)0; offset=-1; fixedPosition = -1; }
|
|
const Address &getAddress(void) const { return addr; } ///< Get the starting address of \b this trial
|
|
int4 getSize(void) const { return size; } ///< Get the number of bytes in \b this trial
|
|
int4 getSlot(void) const { return slot; } ///< Get the \e slot associated with \b this trial
|
|
void setSlot(int4 val) { slot = val; } ///< Set the \e slot associated with \b this trial
|
|
const ParamEntry *getEntry(void) const { return entry; } ///< Get the model entry associated with \b this trial
|
|
int4 getOffset(void) const { return offset; } ///< Get the offset associated with \b this trial
|
|
void setEntry(const ParamEntry *ent,int4 off) { entry=ent; offset=off; } ///< Set the model entry for this trial
|
|
void markUsed(void) { flags |= used; } ///< Mark the trial as a formal parameter
|
|
void markActive(void) { flags |= (active|checked); } ///< Mark that trial is actively used (in data-flow)
|
|
void markInactive(void) { flags &= ~((uint4)active); flags |= checked; } ///< Mark that trial is not actively used
|
|
void markNoUse(void) { flags &= ~((uint4)(active|used)); flags |= (checked|defnouse); } ///< Mark trial as definitely \e not a parameter
|
|
void markUnref(void) { flags |= (unref|checked); slot = -1; } ///< Mark that \b this trial has no Varnode representative
|
|
void markKilledByCall(void) { flags |= killedbycall; } ///< Mark that \b this storage is \e killed-by-call
|
|
bool isChecked(void) const { return ((flags & checked)!=0); } ///< Has \b this trial been checked
|
|
bool isActive(void) const { return ((flags & active)!=0); } ///< Is \b this trial actively used in data-flow
|
|
bool isDefinitelyNotUsed(void) const { return ((flags & defnouse)!=0); } ///< Is \b this trial as definitely not a parameter
|
|
bool isUsed(void) const { return ((flags & used)!=0); } ///< Is \b this trial as a formal parameter
|
|
bool isUnref(void) const { return ((flags & unref)!=0); } ///< Does \b this trial not have a Varnode representative
|
|
bool isKilledByCall(void) const { return ((flags & killedbycall)!=0); } ///< Is \b this storage \e killed-by-call
|
|
void setRemFormed(void) { flags |= rem_formed; } ///< Mark that \b this is formed by a INT_REM operation
|
|
bool isRemFormed(void) const { return ((flags & rem_formed)!=0); } ///< Is \b this formed by a INT_REM operation
|
|
void setIndCreateFormed(void) { flags |= indcreate_formed; } ///< Mark \b this trial as formed by \e indirect \e creation
|
|
bool isIndCreateFormed(void) const { return ((flags & indcreate_formed)!=0); } ///< Is \b this trial formed by \e indirect \e creation
|
|
void setCondExeEffect(void) { flags |= condexe_effect; } ///< Mark \b this trial as possibly affected by conditional execution
|
|
bool hasCondExeEffect(void) const { return ((flags & condexe_effect)!=0); } ///< Is \b this trial possibly affected by conditional execution
|
|
void setAncestorRealistic(void) { flags |= ancestor_realistic; } ///< Mark \b this as having a realistic ancestor
|
|
bool hasAncestorRealistic(void) const { return ((flags & ancestor_realistic)!=0); } ///< Does \b this have a realistic ancestor
|
|
void setAncestorSolid(void) { flags |= ancestor_solid; } ///< Mark \b this as showing solid movement into Varnode
|
|
bool hasAncestorSolid(void) const { return ((flags & ancestor_solid)!=0); } ///< Does \b this show solid movement into Varnode
|
|
int4 slotGroup(void) const { return entry->getSlot(addr,size-1); } ///< Get position of \b this within its parameter \e group
|
|
void setAddress(const Address &ad,int4 sz) { addr=ad; size=sz; } ///< Reset the memory range of \b this trial
|
|
ParamTrial splitHi(int4 sz) const; ///< Create a trial representing the first part of \b this
|
|
ParamTrial splitLo(int4 sz) const; ///< Create a trial representing the last part of \b this
|
|
bool testShrink(const Address &newaddr,int4 sz) const; ///< Test if \b this trial can be made smaller
|
|
bool operator<(const ParamTrial &b) const; ///< Sort trials in formal parameter order
|
|
void setFixedPosition(int4 pos) { fixedPosition = pos; } ///< Set fixed position
|
|
static bool fixedPositionCompare(const ParamTrial &a, const ParamTrial &b); ///< Sort by fixed position; stable for fixedPosition = -1
|
|
};
|
|
|
|
/// \brief Container class for ParamTrial objects
|
|
///
|
|
/// The parameter analysis algorithms use this class to maintain the collection
|
|
/// of parameter trials being actively considered for a given function. It holds the
|
|
/// ParamTrial objects and other information about the current state of analysis.
|
|
///
|
|
/// Trials are maintained in two stages, \e before parameter decisions have been made and \e after.
|
|
/// Before, trials are in input index order relative to the CALL or CALLIND op for a sub-function, or
|
|
/// they are in address order for input Varnodes to the active function.
|
|
/// After, the trials are put into formal parameter order, as dictated by the PrototypeModel.
|
|
class ParamActive {
|
|
vector<ParamTrial> trial; ///< The list of parameter trials
|
|
int4 slotbase; ///< Slot where next parameter will go
|
|
int4 stackplaceholder; ///< Which call input slot holds the stack placeholder
|
|
int4 numpasses; ///< Number of attempts at evaluating parameters
|
|
int4 maxpass; ///< Number of passes before we assume we have seen all params
|
|
bool isfullychecked; ///< True if all trials are fully examined (and no new trials are expected)
|
|
bool needsfinalcheck; ///< Should a final pass be made on trials (to take into account control-flow changes)
|
|
bool recoversubcall; ///< True if \b this is being used to recover prototypes of a sub-function call
|
|
public:
|
|
ParamActive(bool recoversub); ///< Construct an empty container
|
|
void clear(void); ///< Reset to an empty container
|
|
void registerTrial(const Address &addr,int4 sz); ///< Add a new trial to the container
|
|
int4 getNumTrials(void) const { return trial.size(); } ///< Get the number of trials in \b this container
|
|
ParamTrial &getTrial(int4 i) { return trial[i]; } ///< Get the i-th trial
|
|
const ParamTrial &getTrialForInputVarnode(int4 slot) const; ///< Get trial corresponding to the given input Varnode
|
|
int4 whichTrial(const Address &addr,int4 sz) const; ///< Get the trial overlapping with the given memory range
|
|
bool needsFinalCheck(void) const { return needsfinalcheck; } ///< Is a final check required
|
|
void markNeedsFinalCheck(void) { needsfinalcheck = true; } ///< Mark that a final check is required
|
|
bool isRecoverSubcall(void) const { return recoversubcall; } ///< Are these trials for a call to a sub-function
|
|
bool isFullyChecked(void) const { return isfullychecked; } ///< Are all trials checked with no new trials expected
|
|
void markFullyChecked(void) { isfullychecked = true; } ///< Mark that all trials are checked
|
|
void setPlaceholderSlot(void) { stackplaceholder = slotbase; slotbase += 1; } ///< Establish a stack placedholder slot
|
|
void freePlaceholderSlot(void); ///< Free the stack placeholder slot
|
|
int4 getNumPasses(void) const { return numpasses; } ///< How many trial analysis passes were performed
|
|
int4 getMaxPass(void) const { return maxpass; } ///< What is the maximum number of passes
|
|
void setMaxPass(int4 val) { maxpass = val; } ///< Set the maximum number of passes
|
|
void finishPass(void) { numpasses += 1; } ///< Mark that an analysis pass has completed
|
|
void sortTrials(void) { sort(trial.begin(),trial.end()); } ///< Sort the trials in formal parameter order
|
|
void deleteUnusedTrials(void); ///< Remove trials that were found not to be parameters
|
|
void splitTrial(int4 i,int4 sz); ///< Split the given trial in two
|
|
void joinTrial(int4 slot,const Address &addr,int4 sz); ///< Join adjacent parameter trials
|
|
int4 getNumUsed(void) const; ///< Get number of trials marked as formal parameters
|
|
|
|
/// \brief Test if the given trial can be shrunk to the given range
|
|
///
|
|
/// \param i is the index of the given trial
|
|
/// \param addr is the new address
|
|
/// \param sz is the new size
|
|
/// \return true if the trial can be shrunk to the new range
|
|
bool testShrink(int4 i,const Address &addr,int4 sz) const { return trial[i].testShrink(addr,sz); }
|
|
|
|
/// \brief Shrink the given trial to a new given range
|
|
///
|
|
/// \param i is the index of the given trial
|
|
/// \param addr is the new range's starting address
|
|
/// \param sz is the new range's size in bytes
|
|
void shrink(int4 i,const Address &addr,int4 sz) { trial[i].setAddress(addr,sz); }
|
|
|
|
void sortFixedPosition(void) {sort(trial.begin(),trial.end(),ParamTrial::fixedPositionCompare);} ///< sort the trials by fixed position then <
|
|
};
|
|
|
|
/// \brief A special space for encoding FuncCallSpecs
|
|
///
|
|
/// It is efficient and convenient to store the main subfunction
|
|
/// object (FuncCallSpecs) in the pcode operation which is actually making the
|
|
/// call. This address space allows a FuncCallSpecs to be encoded
|
|
/// as an address which replaces the formally encoded address of
|
|
/// the function being called, when manipulating the operation
|
|
/// internally. The space stored in the encoded address is
|
|
/// this special \b fspec space, and the offset is the actual
|
|
/// value of the pointer
|
|
class FspecSpace : public AddrSpace {
|
|
public:
|
|
FspecSpace(AddrSpaceManager *m,const Translate *t,int4 ind); ///< Constructor
|
|
virtual void encodeAttributes(Encoder &encoder,uintb offset) const;
|
|
virtual void encodeAttributes(Encoder &encoder,uintb offset,int4 size) const;
|
|
virtual void printRaw(ostream &s,uintb offset) const;
|
|
virtual void decode(Decoder &decoder);
|
|
static const string NAME; ///< Reserved name for the fspec space
|
|
};
|
|
|
|
/// \brief Basic elements of a parameter: address, data-type, properties
|
|
struct ParameterPieces {
|
|
enum {
|
|
isthis = 1, ///< Parameter is "this" pointer
|
|
hiddenretparm = 2, ///< Parameter is hidden pointer to return value, mirrors Varnode::hiddenretparm
|
|
indirectstorage = 4, ///< Parameter is indirect pointer to true parameter, mirrors Varnode::indirectstorage
|
|
namelock = 8, ///< Parameter's name is locked, mirrors Varnode::namelock
|
|
typelock = 16, ///< Parameter's data-type is locked, mirrors Varnode::typelock
|
|
sizelock = 32 ///< Size of the parameter is locked (but not the data-type)
|
|
};
|
|
Address addr; ///< Storage address of the parameter
|
|
Datatype *type; ///< The datatype of the parameter
|
|
uint4 flags; ///< additional attributes of the parameter
|
|
void swapMarkup(ParameterPieces &op); ///< Swap data-type markup between \b this and another parameter
|
|
};
|
|
|
|
/// \brief Raw components of a function prototype (obtained from parsing source code)
|
|
struct PrototypePieces {
|
|
ProtoModel *model; ///< (Optional) model on which prototype is based
|
|
string name; ///< Identifier (function name) associated with prototype
|
|
Datatype *outtype; ///< Return data-type
|
|
vector<Datatype *> intypes; ///< Input data-types
|
|
vector<string> innames; ///< Identifiers for input types
|
|
int4 firstVarArgSlot; ///< First position of a variable argument, or -1 if not varargs
|
|
};
|
|
|
|
/// \brief Description of the indirect effect a sub-function has on a memory range
|
|
///
|
|
/// This object applies only to the specific memory range, which is seen from the
|
|
/// point of view of the calling function as a particular
|
|
/// sub-function gets called. The main enumeration below lists the possible effects.
|
|
class EffectRecord {
|
|
public:
|
|
enum {
|
|
unaffected = 1, ///< The sub-function does not change the value at all
|
|
killedbycall = 2, ///< The memory is changed and is completely unrelated to its original value
|
|
return_address = 3, ///< The memory is being used to store the return address
|
|
unknown_effect = 4 ///< An unknown effect (indicates the absence of an EffectRecord)
|
|
};
|
|
private:
|
|
VarnodeData range; ///< The memory range affected
|
|
uint4 type; ///< The type of effect
|
|
public:
|
|
EffectRecord(void) {} ///< Constructor for use with decode()
|
|
EffectRecord(const EffectRecord &op2) { range = op2.range; type = op2.type; } ///< Copy constructor
|
|
EffectRecord(const Address &addr,int4 size); ///< Construct a memory range with an unknown effect
|
|
EffectRecord(const ParamEntry &entry,uint4 t); ///< Construct an effect on a parameter storage location
|
|
EffectRecord(const VarnodeData &addr,uint4 t); ///< Construct an effect on a memory range
|
|
uint4 getType(void) const { return type; } ///< Get the type of effect
|
|
Address getAddress(void) const { return Address(range.space,range.offset); } ///< Get the starting address of the affected range
|
|
int4 getSize(void) const { return range.size; } ///< Get the size of the affected range
|
|
bool operator==(const EffectRecord &op2) const; ///< Equality operator
|
|
bool operator!=(const EffectRecord &op2) const; ///< Inequality operator
|
|
void encode(Encoder &encoder) const; ///< Encode the record to a stream
|
|
void decode(uint4 grouptype,Decoder &decoder); ///< Decode the record from a stream
|
|
static bool compareByAddress(const EffectRecord &op1,const EffectRecord &op2);
|
|
};
|
|
|
|
/// A group of ParamEntry objects that form a complete set for passing
|
|
/// parameters in one direction (either input or output). The main tasks this class must
|
|
/// perform are:
|
|
/// - possibleParam() Quick test if a Varnode could ever be a parameter with this prototype
|
|
/// - fillinMap() Select trials completing prototype, given analysis info
|
|
/// - assignMap() Derive slot->address map, given a list of types
|
|
/// - checkJoin() Can two parameters be considered/converted into a single logical parameter
|
|
class ParamList {
|
|
public:
|
|
enum {
|
|
p_standard, ///< Standard input parameter model
|
|
p_standard_out, ///< Standard output (return value) model
|
|
p_register, ///< Unordered parameter passing locations model
|
|
p_register_out, ///< Multiple possible return value locations model
|
|
p_merged ///< A merged model (multiple models merged together)
|
|
};
|
|
virtual ~ParamList(void) {} ///< Destructor
|
|
virtual uint4 getType(void) const=0; ///< Get the type of parameter list
|
|
|
|
/// \brief Given list of data-types, map the list positions to storage locations
|
|
///
|
|
/// If we know the function prototype, recover how parameters are actually stored using the model.
|
|
/// \param proto is the ordered list of data-types
|
|
/// \param typefactory is the TypeFactory (for constructing pointers)
|
|
/// \param res will contain the storage locations corresponding to the datatypes
|
|
virtual void assignMap(const PrototypePieces &proto,TypeFactory &typefactory,vector<ParameterPieces> &res) const=0;
|
|
|
|
/// \brief Given an unordered list of storage locations, calculate a function prototype
|
|
///
|
|
/// A list of input (or output) trials is given, which may have holes, invalid inputs etc. Decide
|
|
/// on the formal ordered parameter list. Trials within the ParamActive are added, removed, or
|
|
/// reordered as needed.
|
|
/// \param active is the given list of trials
|
|
virtual void fillinMap(ParamActive *active) const=0;
|
|
|
|
/// \brief Check if the given two storage locations can represent a single logical parameter
|
|
///
|
|
/// Within the conventions of this model, do the two (hi/lo) locations represent
|
|
/// consecutive parameter locations that can be replaced by a single logical parameter.
|
|
/// \param hiaddr is the address of the most significant part of the value
|
|
/// \param hisize is the size of the most significant part in bytes
|
|
/// \param loaddr is the address of the least significant part of the value
|
|
/// \param losize is the size of the least significant part in bytes
|
|
/// \return \b true if the two pieces can be joined
|
|
virtual bool checkJoin(const Address &hiaddr,int4 hisize,const Address &loaddr,int4 losize) const=0;
|
|
|
|
/// \brief Check if it makes sense to split a single storage location into two parameters
|
|
///
|
|
/// A storage location and split point is provided, implying two new storage locations. Does
|
|
/// \b this model allow these locations to be considered parameters.
|
|
/// \param loc is the starting address of provided storage location
|
|
/// \param size is the size of the location in bytes
|
|
/// \param splitpoint is the number of bytes to consider in the first (in address order) piece
|
|
/// \return \b true if the storage location can be split
|
|
virtual bool checkSplit(const Address &loc,int4 size,int4 splitpoint) const=0;
|
|
|
|
/// \brief Characterize whether the given range overlaps parameter storage
|
|
///
|
|
/// Does the range naturally fit inside a potential parameter entry from this list or does
|
|
/// it contain a parameter entry. Return one of four enumerations indicating this characterization:
|
|
/// - no_containment - there is no containment between the range and any parameter in this list
|
|
/// - contains_unjustified - at least one parameter contains the range
|
|
/// - contains_justified - at least one parameter contains this range as its least significant bytes
|
|
/// - contained_by - no parameter contains this range, but the range contains at least one parameter
|
|
/// \param loc is the starting address of the given range
|
|
/// \param size is the number of bytes in the given range
|
|
/// \return the characterization code
|
|
virtual int4 characterizeAsParam(const Address &loc,int4 size) const=0;
|
|
|
|
/// \brief Does the given storage location make sense as a parameter
|
|
///
|
|
/// Within \b this model, decide if the storage location can be considered a parameter.
|
|
/// \param loc is the starting address of the storage location
|
|
/// \param size is the number of bytes in the storage location
|
|
/// \return \b true if the location can be a parameter
|
|
virtual bool possibleParam(const Address &loc,int4 size) const=0;
|
|
|
|
/// \brief Pass-back the slot and slot size for the given storage location as a parameter
|
|
///
|
|
/// This checks if the given storage location acts as a parameter in \b this model and
|
|
/// passes back the number of slots that it occupies.
|
|
/// \param loc is the starting address of the storage location
|
|
/// \param size is the number of bytes in the storage location
|
|
/// \param slot if the \e slot number to pass back
|
|
/// \param slotsize is the number of consumed slots to pass back
|
|
/// \return \b true if the location can be a parameter
|
|
virtual bool possibleParamWithSlot(const Address &loc,int4 size,int4 &slot,int4 &slotsize) const=0;
|
|
|
|
/// \brief Pass-back the biggest parameter contained within the given range
|
|
///
|
|
/// \param loc is the starting address of the given range
|
|
/// \param size is the number of bytes in the range
|
|
/// \param res will hold the parameter storage description being passed back
|
|
/// \return \b true if there is at least one parameter contained in the range
|
|
virtual bool getBiggestContainedParam(const Address &loc,int4 size,VarnodeData &res) const=0;
|
|
|
|
/// \brief Check if the given storage location looks like an \e unjustified parameter
|
|
///
|
|
/// The storage for a value may be contained in a normal parameter location but be
|
|
/// unjustified within that container, i.e. the least significant bytes are not being used.
|
|
/// If this is the case, pass back the full parameter location and return \b true.
|
|
/// \param loc is the starting address of the given storage
|
|
/// \param size is the number of bytes in the given storage
|
|
/// \param res is the full parameter storage to pass back
|
|
/// \return \b true if the given storage is unjustified within its parameter container
|
|
virtual bool unjustifiedContainer(const Address &loc,int4 size,VarnodeData &res) const=0;
|
|
|
|
/// \brief Get the type of extension and containing parameter for the given storage
|
|
///
|
|
/// If the given storage is properly contained within a normal parameter and the model
|
|
/// typically extends a small value into the full container, pass back the full container
|
|
/// and the type of extension.
|
|
/// \param addr is the starting address of the given storage
|
|
/// \param size is the number of bytes in the given storage
|
|
/// \param res is the parameter storage to pass back
|
|
/// \return the extension operator (INT_ZEXT INT_SEXT) or INT_COPY if there is no extension.
|
|
/// INT_PIECE indicates the extension is determined by the specific prototype.
|
|
virtual OpCode assumedExtension(const Address &addr,int4 size,VarnodeData &res) const=0;
|
|
|
|
/// \brief Get the address space associated with any stack based parameters in \b this list.
|
|
///
|
|
/// \return the stack address space, if \b this models parameters passed on the stack, NULL otherwise
|
|
virtual AddrSpace *getSpacebase(void) const=0;
|
|
|
|
/// \brief Return \b true if \b this pointer occurs before an indirect return pointer
|
|
///
|
|
/// The \e automatic parameters: \b this parameter and the \e hidden return value pointer both
|
|
/// tend to be allocated from the initial general purpose registers reserved for parameter passing.
|
|
/// This method returns \b true if the \b this parameter is allocated first.
|
|
/// \return \b false if the \e hidden return value pointer is allocated first
|
|
virtual bool isThisBeforeRetPointer(void) const=0;
|
|
|
|
/// \brief For a given address space, collect all the parameter locations within that space
|
|
///
|
|
/// Pass back the memory ranges for any parameter that is stored in the given address space.
|
|
/// \param spc is the given address space
|
|
/// \param res will hold the set of matching memory ranges
|
|
virtual void getRangeList(AddrSpace *spc,RangeList &res) const=0;
|
|
|
|
/// \brief Return the maximum heritage delay across all possible parameters
|
|
///
|
|
/// Depending on the address space, data-flow for a parameter may not be available until
|
|
/// extra transform passes have completed. This method returns the number of passes
|
|
/// that must occur before we can guarantee that all parameters have data-flow info.
|
|
/// \return the maximum number of passes across all parameters in \b this model
|
|
virtual int4 getMaxDelay(void) const=0;
|
|
|
|
/// \brief Return \b true if ParamEntry locations should automatically be considered killed by call
|
|
///
|
|
/// \return \b true if automatically assume killbycall
|
|
virtual bool isAutoKillByCall(void) const=0;
|
|
|
|
/// \brief Restore the model from an \<input> or \<output> element in the stream
|
|
///
|
|
/// \param decoder is the stream decoder
|
|
/// \param effectlist is a container collecting EffectRecords across all parameters
|
|
/// \param normalstack is \b true if parameters are pushed on the stack in the normal order
|
|
virtual void decode(Decoder &decoder,vector<EffectRecord> &effectlist,bool normalstack)=0;
|
|
|
|
virtual ParamList *clone(void) const=0; ///< Clone this parameter list model
|
|
};
|
|
|
|
/// \brief A standard model for parameters as an ordered list of storage resources
|
|
///
|
|
/// This is a configurable model for passing (input) parameters as a list to a function.
|
|
/// The model allows 1 or more resource lists based on data-type, either TYPE_UNKNOWN for
|
|
/// general purpose or TYPE_FLOAT for floating-point registers. Within a resource list,
|
|
/// any number of parameters can be used but they must come starting at the beginning of
|
|
/// the list with no \e holes (skipped resources). A resource list can include (at the end)
|
|
/// \e stack parameters that are allocated based on an alignment. Optionally, the model supports
|
|
/// converting data-types larger than a specified size to pointers within the parameter list.
|
|
class ParamListStandard : public ParamList {
|
|
protected:
|
|
int4 numgroup; ///< Number of \e groups in this parameter convention
|
|
int4 maxdelay; ///< Maximum heritage delay across all parameters
|
|
bool thisbeforeret; ///< Does a \b this parameter come before a hidden return parameter
|
|
vector<int4> resourceStart; ///< The starting group for each resource section
|
|
list<ParamEntry> entry; ///< The ordered list of parameter entries
|
|
vector<ParamEntryResolver *> resolverMap; ///< Map from space id to resolver
|
|
list<ModelRule> modelRules; ///< Rules to apply when assigning addresses
|
|
AddrSpace *spacebase; ///< Address space containing relative offset parameters
|
|
const ParamEntry *findEntry(const Address &loc,int4 size,bool just) const; ///< Given storage location find matching ParamEntry
|
|
const ParamEntry *selectUnreferenceEntry(int4 grp,type_class prefType) const; ///< Select entry to fill an unreferenced param
|
|
void buildTrialMap(ParamActive *active) const; ///< Build map from parameter trials to model ParamEntrys
|
|
void separateSections(ParamActive *active,vector<int4> &trialStart) const;
|
|
static void markGroupNoUse(ParamActive *active,int4 activeTrial,int4 trialStart);
|
|
static void markBestInactive(ParamActive *active,int4 group,int4 groupStart,type_class prefType);
|
|
static void forceExclusionGroup(ParamActive *active);
|
|
static void forceNoUse(ParamActive *active,int4 start,int4 stop);
|
|
static void forceInactiveChain(ParamActive *active,int4 maxchain,int4 start,int4 stop,int4 groupstart);
|
|
void calcDelay(void); ///< Calculate the maximum heritage delay for any potential parameter in this list
|
|
void addResolverRange(AddrSpace *spc,uintb first,uintb last,ParamEntry *paramEntry,int4 position);
|
|
void populateResolver(void); ///< Build the ParamEntry resolver maps
|
|
void parsePentry(Decoder &decoder,vector<EffectRecord> &effectlist,
|
|
int4 groupid,bool normalstack,bool autokill,bool splitFloat,bool grouped);
|
|
void parseGroup(Decoder &decoder,vector<EffectRecord> &effectlist,
|
|
int4 groupid,bool normalstack,bool autokill,bool splitFloat);
|
|
public:
|
|
ParamListStandard(void) {} ///< Construct for use with decode()
|
|
ParamListStandard(const ParamListStandard &op2); ///< Copy constructor
|
|
virtual ~ParamListStandard(void);
|
|
const list<ParamEntry> &getEntry(void) const { return entry; } ///< Get the list of parameter entries
|
|
list<ParamEntry>::const_iterator getFirstIter(type_class type) const; ///< Get iterator to first entry in a storage class
|
|
const ParamEntry *getStackEntry(void) const; ///< Get the stack entry
|
|
uint4 assignAddressFallback(type_class resource,Datatype *tp,bool matchExact,vector<int4> &status,
|
|
ParameterPieces ¶m) const;
|
|
uint4 assignAddress(Datatype *dt,const PrototypePieces &proto,int4 pos,TypeFactory &tlst,
|
|
vector<int4> &status,ParameterPieces &res) const;
|
|
virtual uint4 getType(void) const { return p_standard; }
|
|
virtual void assignMap(const PrototypePieces &proto,TypeFactory &typefactory,vector<ParameterPieces> &res) const;
|
|
virtual void fillinMap(ParamActive *active) const;
|
|
virtual bool checkJoin(const Address &hiaddr,int4 hisize,const Address &loaddr,int4 losize) const;
|
|
virtual bool checkSplit(const Address &loc,int4 size,int4 splitpoint) const;
|
|
virtual int4 characterizeAsParam(const Address &loc,int4 size) const;
|
|
virtual bool possibleParam(const Address &loc,int4 size) const;
|
|
virtual bool possibleParamWithSlot(const Address &loc,int4 size,int4 &slot,int4 &slotsize) const;
|
|
virtual bool getBiggestContainedParam(const Address &loc,int4 size,VarnodeData &res) const;
|
|
virtual bool unjustifiedContainer(const Address &loc,int4 size,VarnodeData &res) const;
|
|
virtual OpCode assumedExtension(const Address &addr,int4 size,VarnodeData &res) const;
|
|
virtual AddrSpace *getSpacebase(void) const { return spacebase; }
|
|
virtual bool isThisBeforeRetPointer(void) const { return thisbeforeret; }
|
|
virtual void getRangeList(AddrSpace *spc,RangeList &res) const;
|
|
virtual int4 getMaxDelay(void) const { return maxdelay; }
|
|
virtual bool isAutoKillByCall(void) const { return false; }
|
|
virtual void decode(Decoder &decoder,vector<EffectRecord> &effectlist,bool normalstack);
|
|
virtual ParamList *clone(void) const;
|
|
};
|
|
|
|
/// \brief A standard model for returning output parameters from a function
|
|
///
|
|
/// This has a more involved assignment strategy than its parent class.
|
|
/// Entries in the resource list are treated as a \e group, meaning that only one can
|
|
/// fit the desired storage size and type attributes of the return value. If no entry
|
|
/// fits, the return value is converted to a pointer data-type, storage allocation is
|
|
/// attempted again, and the return value is marked as a \e hidden return parameter
|
|
/// to inform the input model.
|
|
class ParamListStandardOut : public ParamListStandard {
|
|
bool useFillinFallback; ///< If \b true, use fillinMapFallback
|
|
void initialize(void); ///< Cache ModelRule information
|
|
public:
|
|
ParamListStandardOut(void) : ParamListStandard() {} ///< Constructor for use with decode()
|
|
ParamListStandardOut(const ParamListStandardOut &op2) : ParamListStandard(op2) {
|
|
useFillinFallback = op2.useFillinFallback; } ///< Copy constructor
|
|
void fillinMapFallback(ParamActive *active,bool firstOnly) const;
|
|
virtual uint4 getType(void) const { return p_standard_out; }
|
|
virtual void assignMap(const PrototypePieces &proto,TypeFactory &typefactory,vector<ParameterPieces> &res) const;
|
|
virtual void fillinMap(ParamActive *active) const;
|
|
virtual bool possibleParam(const Address &loc,int4 size) const;
|
|
virtual bool isAutoKillByCall(void) const { return useFillinFallback; }
|
|
virtual void decode(Decoder &decoder,vector<EffectRecord> &effectlist,bool normalstack);
|
|
virtual ParamList *clone(void) const;
|
|
};
|
|
|
|
/// \brief A model for passing back return values from a function
|
|
///
|
|
/// This is a resource list of potential storage locations for a return value,
|
|
/// at most 1 of which will be chosen for a given function. This models a simple strategy
|
|
/// for selecting a storage location. When assigning based on data-type (assignMap), the first list
|
|
/// entry that fits is chosen. When assigning from a set of actively used locations (fillinMap),
|
|
/// this class chooses the location that is the closest fitting match to an entry in the resource list.
|
|
class ParamListRegisterOut : public ParamListStandardOut {
|
|
public:
|
|
ParamListRegisterOut(void) : ParamListStandardOut() {} ///< Constructor
|
|
ParamListRegisterOut(const ParamListRegisterOut &op2) : ParamListStandardOut(op2) {} ///< Copy constructor
|
|
virtual uint4 getType(void) const { return p_register_out; }
|
|
virtual void assignMap(const PrototypePieces &proto,TypeFactory &typefactory,vector<ParameterPieces> &res) const;
|
|
virtual ParamList *clone(void) const;
|
|
};
|
|
|
|
/// \brief An unstructured model for passing input parameters to a function.
|
|
///
|
|
/// This is the \b register model, meaning a collection of registers, any of which
|
|
/// can be used to pass a parameter. This is nearly identical to ParamListStandard, but
|
|
/// rules banning \e holes are not enforced, any subset of the resource list can be used.
|
|
/// This makes sense for executables where parameters follow no conventions or only loose
|
|
/// conventions. The assignMap() method may make less sense in this scenario.
|
|
class ParamListRegister : public ParamListStandard {
|
|
public:
|
|
ParamListRegister(void) : ParamListStandard() {} ///< Constructor for use with decode()
|
|
ParamListRegister(const ParamListRegister &op2) : ParamListStandard(op2) {} ///< Copy constructor
|
|
virtual uint4 getType(void) const { return p_register; }
|
|
virtual void fillinMap(ParamActive *active) const;
|
|
virtual ParamList *clone(void) const;
|
|
};
|
|
|
|
/// \brief A union of other input parameter passing models
|
|
///
|
|
/// This model is viewed as a union of a constituent set of resource lists.
|
|
/// This allows initial data-flow analysis to proceed when the exact model
|
|
/// isn't known. The assignMap() and fillinMap() methods are disabled for
|
|
/// instances of this class. The controlling prototype model (ProtoModelMerged)
|
|
/// decides from among the constituent ParamList models before these routines
|
|
/// need to be invoked.
|
|
class ParamListMerged : public ParamListStandard {
|
|
public:
|
|
ParamListMerged(void) : ParamListStandard() {} ///< Constructor for use with decode
|
|
ParamListMerged(const ParamListMerged &op2) : ParamListStandard(op2) {} ///< Copy constructor
|
|
void foldIn(const ParamListStandard &op2); ///< Add another model to the union
|
|
void finalize(void) { populateResolver(); } ///< Fold-ins are finished, finalize \b this
|
|
virtual uint4 getType(void) const { return p_merged; }
|
|
virtual void assignMap(const PrototypePieces &proto,TypeFactory &typefactory,vector<ParameterPieces> &res) const {
|
|
throw LowlevelError("Cannot assign prototype before model has been resolved"); }
|
|
virtual void fillinMap(ParamActive *active) const {
|
|
throw LowlevelError("Cannot determine prototype before model has been resolved"); }
|
|
virtual ParamList *clone(void) const;
|
|
};
|
|
|
|
/// \brief A \b prototype \b model: a model for passing parameters between functions
|
|
///
|
|
/// This encompasses both input parameters and return values. It attempts to
|
|
/// describe the ABI, Application Binary Interface, of the processor or compiler.
|
|
/// Any number of function prototypes (FuncProto) can be implemented under a
|
|
/// \b prototype \b model, which represents a static rule set the compiler uses
|
|
/// to decide:
|
|
/// - Storage locations for input parameters
|
|
/// - Storage locations for return values
|
|
/// - Expected side-effects of a function on other (non-parameter) registers and storage locations
|
|
/// - Behavior of the stack and the stack pointer across function calls
|
|
///
|
|
/// Major analysis concerns are:
|
|
/// - Recovering function prototypes from data-flow information: deriveInputMap() and deriveOutputMap()
|
|
/// - Calculating parameter storage locations given a function prototype: assignParameterStorage()
|
|
/// - Behavior of data-flow around call sites
|
|
///
|
|
/// A prototype model supports the concept of \b extrapop, which is defined as the change in
|
|
/// value of the stack pointer (or the number of bytes popped from the stack) across a call.
|
|
/// This value is calculated starting from the point of the p-code CALL or CALLIND op, when the
|
|
/// stack parameters have already been pushed by the calling function. So \e extrapop only reflects
|
|
/// changes made by the callee.
|
|
class ProtoModel {
|
|
friend class ProtoModelMerged;
|
|
Architecture *glb; ///< The Architecture owning this prototype model
|
|
string name; ///< Name of the model
|
|
int4 extrapop; ///< Extra bytes popped from stack
|
|
ParamList *input; ///< Resource model for input parameters
|
|
ParamList *output; ///< Resource model for output parameters
|
|
const ProtoModel *compatModel; ///< The model \b this is a copy of
|
|
vector<EffectRecord> effectlist; ///< List of side-effects
|
|
vector<VarnodeData> likelytrash; ///< Storage locations potentially carrying \e trash values
|
|
vector<VarnodeData> internalstorage; ///< Registers that hold internal compiler constants
|
|
int4 injectUponEntry; ///< Id of injection to perform at beginning of function (-1 means not used)
|
|
int4 injectUponReturn; ///< Id of injection to perform after a call to this function (-1 means not used)
|
|
RangeList localrange; ///< Memory range(s) of space-based locals
|
|
RangeList paramrange; ///< Memory range(s) of space-based parameters
|
|
bool stackgrowsnegative; ///< True if stack parameters have (normal) low address to high address ordering
|
|
bool hasThis; ///< True if this model has a \b this parameter (auto-parameter)
|
|
bool isConstruct; ///< True if this model is a constructor for a particular object
|
|
bool isPrinted; ///< True if this model should be printed as part of function declarations
|
|
void defaultLocalRange(void); ///< Set the default stack range used for local variables
|
|
void defaultParamRange(void); ///< Set the default stack range used for input parameters
|
|
void buildParamList(const string &strategy); ///< Establish the main resource lists for input and output parameters.
|
|
public:
|
|
enum {
|
|
extrapop_unknown = 0x8000 ///< Reserved extrapop value meaning the function's \e extrapop is unknown
|
|
};
|
|
ProtoModel(Architecture *g); ///< Constructor for use with decode()
|
|
ProtoModel(const string &nm,const ProtoModel &op2); ///< Copy constructor changing the name
|
|
virtual ~ProtoModel(void); ///< Destructor
|
|
const string &getName(void) const { return name; } ///< Get the name of the prototype model
|
|
Architecture *getArch(void) const { return glb; } ///< Get the owning Architecture
|
|
const ProtoModel *getAliasParent(void) const { return compatModel; } ///< Return \e model \b this is an alias of (or null)
|
|
uint4 hasEffect(const Address &addr,int4 size) const; ///< Determine side-effect of \b this on the given memory range
|
|
int4 getExtraPop(void) const { return extrapop; } ///< Get the stack-pointer \e extrapop for \b this model
|
|
void setExtraPop(int4 ep) { extrapop = ep; } ///< Set the stack-pointer \e extrapop
|
|
int4 getInjectUponEntry(void) const { return injectUponEntry; } ///< Get the inject \e uponentry id
|
|
int4 getInjectUponReturn(void) const { return injectUponReturn; } ///< Get the inject \e uponreturn id
|
|
bool isCompatible(const ProtoModel *op2) const; ///< Return \b true if other given model can be substituted for \b this
|
|
|
|
/// \brief Given a list of input \e trials, derive the most likely input prototype
|
|
///
|
|
/// Trials are sorted and marked as \e used or not.
|
|
/// \param active is the collection of Varnode input trials
|
|
void deriveInputMap(ParamActive *active) const {
|
|
input->fillinMap(active); }
|
|
|
|
/// \brief Given a list of output \e trials, derive the most likely output prototype
|
|
///
|
|
/// One trial (at most) is marked \e used and moved to the front of the list
|
|
/// \param active is the collection of output trials
|
|
void deriveOutputMap(ParamActive *active) const {
|
|
output->fillinMap(active); }
|
|
|
|
void assignParameterStorage(const PrototypePieces &proto,vector<ParameterPieces> &res,bool ignoreOutputError);
|
|
|
|
/// \brief Check if the given two input storage locations can represent a single logical parameter
|
|
///
|
|
/// Within the conventions of this model, do the two (hi/lo) locations represent
|
|
/// consecutive input parameter locations that can be replaced by a single logical parameter.
|
|
/// \param hiaddr is the address of the most significant part of the value
|
|
/// \param hisize is the size of the most significant part in bytes
|
|
/// \param loaddr is the address of the least significant part of the value
|
|
/// \param losize is the size of the least significant part in bytes
|
|
/// \return \b true if the two pieces can be joined
|
|
bool checkInputJoin(const Address &hiaddr,int4 hisize,const Address &loaddr,int4 losize) const {
|
|
return input->checkJoin(hiaddr,hisize,loaddr,losize); }
|
|
|
|
/// \brief Check if the given two output storage locations can represent a single logical return value
|
|
///
|
|
/// Within the conventions of this model, do the two (hi/lo) locations represent
|
|
/// consecutive locations that can be replaced by a single logical return value.
|
|
/// \param hiaddr is the address of the most significant part of the value
|
|
/// \param hisize is the size of the most significant part in bytes
|
|
/// \param loaddr is the address of the least significant part of the value
|
|
/// \param losize is the size of the least significant part in bytes
|
|
/// \return \b true if the two pieces can be joined
|
|
bool checkOutputJoin(const Address &hiaddr,int4 hisize,const Address &loaddr,int4 losize) const {
|
|
return output->checkJoin(hiaddr,hisize,loaddr,losize); }
|
|
|
|
/// \brief Check if it makes sense to split a single storage location into two input parameters
|
|
///
|
|
/// A storage location and split point is provided, implying two new storage locations. Does
|
|
/// \b this model allow these locations to be considered separate parameters.
|
|
/// \param loc is the starting address of provided storage location
|
|
/// \param size is the size of the location in bytes
|
|
/// \param splitpoint is the number of bytes to consider in the first (in address order) piece
|
|
/// \return \b true if the storage location can be split
|
|
bool checkInputSplit(const Address &loc,int4 size,int4 splitpoint) const {
|
|
return input->checkSplit(loc,size,splitpoint); }
|
|
|
|
const RangeList &getLocalRange(void) const { return localrange; } ///< Get the range of (possible) local stack variables
|
|
const RangeList &getParamRange(void) const { return paramrange; } ///< Get the range of (possible) stack parameters
|
|
vector<EffectRecord>::const_iterator effectBegin(void) const { return effectlist.begin(); } ///< Get an iterator to the first EffectRecord
|
|
vector<EffectRecord>::const_iterator effectEnd(void) const { return effectlist.end(); } ///< Get an iterator to the last EffectRecord
|
|
vector<VarnodeData>::const_iterator trashBegin(void) const { return likelytrash.begin(); } ///< Get an iterator to the first \e likelytrash
|
|
vector<VarnodeData>::const_iterator trashEnd(void) const { return likelytrash.end(); } ///< Get an iterator to the last \e likelytrash
|
|
vector<VarnodeData>::const_iterator internalBegin(void) const { return internalstorage.begin(); } ///< Get an iterator to the first \e internalstorage
|
|
vector<VarnodeData>::const_iterator internalEnd(void) const { return internalstorage.end(); } ///< Get an iterator to the last \e internalstorage
|
|
|
|
/// \brief Characterize whether the given range overlaps parameter storage
|
|
///
|
|
/// Does the range naturally fit inside a potential parameter entry from this model or does
|
|
/// it contain a parameter entry. Return one of four values indicating this characterization:
|
|
/// - no_containment - there is no containment between the range and any parameter in this list
|
|
/// - contains_unjustified - at least one parameter contains the range
|
|
/// - contains_justified - at least one parameter contains this range as its least significant bytes
|
|
/// - contained_by - no parameter contains this range, but the range contains at least one parameter
|
|
/// \param loc is the starting address of the given range
|
|
/// \param size is the number of bytes in the given range
|
|
/// \return the characterization code
|
|
int4 characterizeAsInputParam(const Address &loc,int4 size) const {
|
|
return input->characterizeAsParam(loc, size);
|
|
}
|
|
|
|
/// \brief Characterize whether the given range overlaps output storage
|
|
///
|
|
/// Does the range naturally fit inside a potential output entry from this model or does
|
|
/// it contain an output entry. Return one of four values indicating this characterization:
|
|
/// - no_containment - there is no containment between the range and any parameter in this list
|
|
/// - contains_unjustified - at least one parameter contains the range
|
|
/// - contains_justified - at least one parameter contains this range as its least significant bytes
|
|
/// - contained_by - no parameter contains this range, but the range contains at least one parameter
|
|
/// \param loc is the starting address of the given range
|
|
/// \param size is the number of bytes in the given range
|
|
/// \return the characterization code
|
|
int4 characterizeAsOutput(const Address &loc,int4 size) const {
|
|
return output->characterizeAsParam(loc, size);
|
|
}
|
|
|
|
/// \brief Does the given storage location make sense as an input parameter
|
|
///
|
|
/// Within \b this model, decide if the storage location can be considered an input parameter.
|
|
/// \param loc is the starting address of the storage location
|
|
/// \param size is the number of bytes in the storage location
|
|
/// \return \b true if the location can be a parameter
|
|
bool possibleInputParam(const Address &loc,int4 size) const {
|
|
return input->possibleParam(loc,size); }
|
|
|
|
/// \brief Does the given storage location make sense as a return value
|
|
///
|
|
/// Within \b this model, decide if the storage location can be considered an output parameter.
|
|
/// \param loc is the starting address of the storage location
|
|
/// \param size is the number of bytes in the storage location
|
|
/// \return \b true if the location can be a parameter
|
|
bool possibleOutputParam(const Address &loc,int4 size) const {
|
|
return output->possibleParam(loc,size); }
|
|
|
|
/// \brief Pass-back the slot and slot size for the given storage location as an input parameter
|
|
///
|
|
/// This checks if the given storage location acts as an input parameter in \b this model and
|
|
/// passes back the number of slots that it occupies.
|
|
/// \param loc is the starting address of the storage location
|
|
/// \param size is the number of bytes in the storage location
|
|
/// \param slot if the \e slot number to pass back
|
|
/// \param slotsize is the number of consumed slots to pass back
|
|
/// \return \b true if the location can be a parameter
|
|
bool possibleInputParamWithSlot(const Address &loc,int4 size,int4 &slot,int4 &slotsize) const {
|
|
return input->possibleParamWithSlot(loc,size,slot,slotsize); }
|
|
|
|
/// \brief Pass-back the slot and slot size for the given storage location as a return value
|
|
///
|
|
/// This checks if the given storage location acts as an output parameter in \b this model and
|
|
/// passes back the number of slots that it occupies.
|
|
/// \param loc is the starting address of the storage location
|
|
/// \param size is the number of bytes in the storage location
|
|
/// \param slot if the \e slot number to pass back
|
|
/// \param slotsize is the number of consumed slots to pass back
|
|
/// \return \b true if the location can be a parameter
|
|
bool possibleOutputParamWithSlot(const Address &loc,int4 size,int4 &slot,int4 &slotsize) const {
|
|
return output->possibleParamWithSlot(loc,size,slot,slotsize); }
|
|
|
|
/// \brief Check if the given storage location looks like an \e unjustified input parameter
|
|
///
|
|
/// The storage for a value may be contained in a normal parameter location but be
|
|
/// unjustified within that container, i.e. the least significant bytes are not being used.
|
|
/// If this is the case, pass back the full parameter location and return \b true.
|
|
/// \param loc is the starting address of the given storage
|
|
/// \param size is the number of bytes in the given storage
|
|
/// \param res is the full parameter storage to pass back
|
|
/// \return \b true if the given storage is unjustified within its parameter container
|
|
bool unjustifiedInputParam(const Address &loc,int4 size,VarnodeData &res) const {
|
|
return input->unjustifiedContainer(loc,size,res); }
|
|
|
|
/// \brief Get the type of extension and containing input parameter for the given storage
|
|
///
|
|
/// If the given storage is properly contained within a normal parameter and the model
|
|
/// typically extends a small value into the full container, pass back the full container
|
|
/// and the type of extension.
|
|
/// \param addr is the starting address of the given storage
|
|
/// \param size is the number of bytes in the given storage
|
|
/// \param res is the parameter storage to pass back
|
|
/// \return the extension operator (INT_ZEXT INT_SEXT) or INT_COPY if there is no extension.
|
|
/// INT_PIECE indicates the extension is determined by the specific prototype.
|
|
OpCode assumedInputExtension(const Address &addr,int4 size,VarnodeData &res) const {
|
|
return input->assumedExtension(addr,size,res); }
|
|
|
|
/// \brief Get the type of extension and containing return value location for the given storage
|
|
///
|
|
/// If the given storage is properly contained within a normal return value location and the model
|
|
/// typically extends a small value into the full container, pass back the full container
|
|
/// and the type of extension.
|
|
/// \param addr is the starting address of the given storage
|
|
/// \param size is the number of bytes in the given storage
|
|
/// \param res is the parameter storage to pass back
|
|
/// \return the extension operator (INT_ZEXT INT_SEXT) or INT_COPY if there is no extension.
|
|
/// INT_PIECE indicates the extension is determined by the specific prototype.
|
|
OpCode assumedOutputExtension(const Address &addr,int4 size,VarnodeData &res) const {
|
|
return output->assumedExtension(addr,size,res); }
|
|
|
|
/// \brief Pass-back the biggest input parameter contained within the given range
|
|
///
|
|
/// \param loc is the starting address of the given range
|
|
/// \param size is the number of bytes in the range
|
|
/// \param res will hold the parameter storage description being passed back
|
|
/// \return \b true if there is at least one parameter contained in the range
|
|
bool getBiggestContainedInputParam(const Address &loc,int4 size,VarnodeData &res) const {
|
|
return input->getBiggestContainedParam(loc, size, res);
|
|
}
|
|
|
|
/// \brief Pass-back the biggest possible output parameter contained within the given range
|
|
///
|
|
/// \param loc is the starting address of the given range
|
|
/// \param size is the number of bytes in the range
|
|
/// \param res will hold the storage description being passed back
|
|
/// \return \b true if there is at least one possible output parameter contained in the range
|
|
bool getBiggestContainedOutput(const Address &loc,int4 size,VarnodeData &res) const {
|
|
return output->getBiggestContainedParam(loc, size, res);
|
|
}
|
|
|
|
AddrSpace *getSpacebase(void) const { return input->getSpacebase(); } ///< Get the stack space associated with \b this model
|
|
bool isStackGrowsNegative(void) const { return stackgrowsnegative; } ///< Return \b true if the stack \e grows toward smaller addresses
|
|
bool hasThisPointer(void) const { return hasThis; } ///< Is \b this a model for (non-static) class methods
|
|
bool isConstructor(void) const { return isConstruct; } ///< Is \b this model for class constructors
|
|
bool printInDecl(void) const { return isPrinted; } ///< Return \b true if name should be printed in function declarations
|
|
void setPrintInDecl(bool val) { isPrinted = val; } ///< Set whether \b this name should be printed in function declarations
|
|
|
|
/// \brief Return the maximum heritage delay across all possible input parameters
|
|
///
|
|
/// Depending on the address space, data-flow for a parameter may not be available until
|
|
/// extra transform passes have completed. This method returns the number of passes
|
|
/// that must occur before we can guarantee that all parameters have data-flow info.
|
|
/// \return the maximum number of passes across all input parameters in \b this model
|
|
int4 getMaxInputDelay(void) const { return input->getMaxDelay(); }
|
|
|
|
/// \brief Return the maximum heritage delay across all possible return values
|
|
///
|
|
/// Depending on the address space, data-flow for a parameter may not be available until
|
|
/// extra transform passes have completed. This method returns the number of passes
|
|
/// that must occur before we can guarantee that any return value has data-flow info.
|
|
/// \return the maximum number of passes across all output parameters in \b this model
|
|
int4 getMaxOutputDelay(void) const { return output->getMaxDelay(); }
|
|
|
|
/// \brief Does \b this model automatically consider potential output locations as killed by call
|
|
///
|
|
/// \return \b true if output locations should be considered killed by call
|
|
bool isAutoKillByCall(void) const { return output->isAutoKillByCall(); }
|
|
|
|
/// \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);
|
|
};
|
|
|
|
/// \brief An unrecognized prototype model
|
|
///
|
|
/// This kind of model is created for function prototypes that specify a model name for which
|
|
/// there is no matching object. A model is created for the name by cloning behavior from a
|
|
/// placeholder model, usually the \e default model. This object mostly behaves like its placeholder
|
|
/// model but can identify itself as an \e unknown model and adopts the unrecognized model name.
|
|
class UnknownProtoModel : public ProtoModel {
|
|
ProtoModel *placeholderModel; ///< The model whose behavior \b this adopts as a behavior placeholder
|
|
public:
|
|
UnknownProtoModel(const string &nm,ProtoModel *placeHold) : ProtoModel(nm,*placeHold) {
|
|
placeholderModel = placeHold; } ///< Constructor
|
|
ProtoModel *getPlaceholderModel(void) const { return placeholderModel; } ///< Retrieve the placeholder model
|
|
virtual bool isUnknown(void) const { return true; }
|
|
};
|
|
|
|
/// \brief Class for calculating "goodness of fit" of parameter trials against a prototype model
|
|
///
|
|
/// The class is instantiated with a prototype model (ProtoModel). A set of Varnode parameter trials
|
|
/// are registered by calling addParameter() for each trial. Then calling doScore() computes a score
|
|
/// that evaluates how well the set of registered trials fit the prototype model. A lower score
|
|
/// indicates a better fit.
|
|
class ScoreProtoModel {
|
|
/// \brief A record mapping trials to parameter entries in the prototype model
|
|
class PEntry {
|
|
public:
|
|
int4 origIndex; ///< Original index of trial
|
|
int4 slot; ///< Matching slot within the resource list
|
|
int4 size; ///< Number of slots occupied
|
|
/// \brief Compare PEntry objects by slot
|
|
///
|
|
/// \param op2 is the PEntry to compare \b this to
|
|
/// \return \b true if \b this should be ordered before the other PEntry
|
|
bool operator<(const PEntry &op2) const { return (slot < op2.slot); }
|
|
};
|
|
bool isinputscore; ///< True if scoring against input parameters, \b false for outputs
|
|
vector<PEntry> entry; ///< Map of parameter entries corresponding to trials
|
|
const ProtoModel *model; ///< Prototype model to score against
|
|
int4 finalscore; ///< The final fitness score
|
|
int4 mismatch; ///< Number of trials that don't fit the prototype model at all
|
|
public:
|
|
ScoreProtoModel(bool isinput,const ProtoModel *mod,int4 numparam); ///< Constructor
|
|
void addParameter(const Address &addr,int4 sz); ///< Register a trial to be scored
|
|
void doScore(void); ///< Compute the fitness score
|
|
int4 getScore(void) const { return finalscore; } ///< Get the fitness score
|
|
int4 getNumMismatch(void) const { return mismatch; } ///< Get the number of mismatched trials
|
|
};
|
|
|
|
/// \brief A prototype model made by merging together other models
|
|
///
|
|
/// This model serves as a placeholder for multiple models, when the exact model
|
|
/// hasn't been immediately determined. At the time of active parameter recovery
|
|
/// the correct model is selected for the given set of trials
|
|
/// from among the constituent prototype models used to build \b this,
|
|
/// by calling the method selectModel().
|
|
/// Up to this time, \b this serves as a merged form of the models
|
|
/// so that all potential parameter trials will be included in the analysis. The parameter recovery
|
|
/// for the output part of the model is currently limited, so the constituent models must all share
|
|
/// the same output model, and this part is \e not currently merged.
|
|
class ProtoModelMerged : public ProtoModel {
|
|
vector<ProtoModel *> modellist; ///< Constituent models being merged
|
|
void intersectEffects(const vector<EffectRecord> &efflist); ///< Fold EffectRecords into \b this model
|
|
static void intersectRegisters(vector<VarnodeData> ®List1,const vector<VarnodeData> ®List2);
|
|
public:
|
|
ProtoModelMerged(Architecture *g) : ProtoModel(g) {} ///< Constructor
|
|
virtual ~ProtoModelMerged(void) {} ///< Destructor
|
|
int4 numModels(void) const { return modellist.size(); } ///< Get the number of constituent models
|
|
ProtoModel *getModel(int4 i) const { return modellist[i]; } ///< Get the i-th model
|
|
void foldIn(ProtoModel *model); ///< Fold-in an additional prototype model
|
|
ProtoModel *selectModel(ParamActive *active) const; ///< Select the best model given a set of trials
|
|
virtual bool isMerged(void) const { return true; }
|
|
virtual void decode(Decoder &decoder);
|
|
};
|
|
|
|
class Symbol;
|
|
class AliasChecker;
|
|
|
|
/// \brief A function parameter viewed as a name, data-type, and storage address
|
|
///
|
|
/// This is the base class, with derived classes determining what is backing up
|
|
/// the information, whether is it a formal Symbol or just internal storage.
|
|
/// Both input parameters and return values can be represented with this object.
|
|
class ProtoParameter {
|
|
public:
|
|
ProtoParameter(void) {} ///< Constructor
|
|
virtual ~ProtoParameter(void) {} ///< Destructor
|
|
virtual const string &getName(void) const=0; ///< Get the name of the parameter ("" for return value)
|
|
virtual Datatype *getType(void) const=0; ///< Get the data-type associate with \b this
|
|
virtual Address getAddress(void) const=0; ///< Get the storage address for \b this parameter
|
|
virtual int4 getSize(void) const=0; ///< Get the number of bytes occupied by \b this parameter
|
|
virtual bool isTypeLocked(void) const=0; ///< Is the parameter data-type locked
|
|
virtual bool isNameLocked(void) const=0; ///< Is the parameter name locked
|
|
virtual bool isSizeTypeLocked(void) const=0; ///< Is the size of the parameter locked
|
|
virtual bool isThisPointer(void) const=0; ///< Is \b this the "this" pointer for a class method
|
|
virtual bool isIndirectStorage(void) const=0; ///< Is \b this really a pointer to the true parameter
|
|
virtual bool isHiddenReturn(void) const=0; ///< Is \b this a pointer to storage for a return value
|
|
virtual bool isNameUndefined(void) const=0; ///< Is the name of \b this parameter undefined
|
|
virtual void setTypeLock(bool val)=0; ///< Toggle the lock on the data-type
|
|
virtual void setNameLock(bool val)=0; ///< Toggle the lock on the name
|
|
virtual void setThisPointer(bool val)=0; ///< Toggle whether \b this is the "this" pointer for a class method
|
|
|
|
/// \brief Change (override) the data-type of a \e size-locked parameter.
|
|
///
|
|
/// The original parameter must have a \e type-lock and TYPE_UNKNOWN data-type.
|
|
/// The \e size-lock is preserved and \b this can be cleared back to its TYPE_UNKNOWN state.
|
|
/// \param ct is the overriding data-type
|
|
virtual void overrideSizeLockType(Datatype *ct)=0;
|
|
|
|
/// \brief Clear \b this parameter's data-type preserving any \e size-lock
|
|
///
|
|
/// The data-type is converted to a TYPE_UNKNOWN of the same size
|
|
/// \param factory is the TypeFactory that will construct the unknown data-type
|
|
virtual void resetSizeLockType(TypeFactory *factory)=0;
|
|
|
|
virtual ProtoParameter *clone(void) const=0; ///< Clone the parameter
|
|
|
|
/// \brief Retrieve the formal Symbol associated with \b this parameter
|
|
///
|
|
/// If there is no backing symbol an exception is thrown
|
|
/// \return the backing Symbol object
|
|
virtual Symbol *getSymbol(void) const=0;
|
|
|
|
/// \brief Compare storage location and data-type for equality
|
|
///
|
|
/// \param op2 is the parameter to compare with \b this
|
|
/// \return \b true if the parameters share a data-type and storage location
|
|
bool operator==(const ProtoParameter &op2) const {
|
|
if (getAddress() != op2.getAddress()) return false;
|
|
if (getType() != op2.getType()) return false;
|
|
return true;
|
|
}
|
|
|
|
/// \brief Compare storage location and data-type for inequality
|
|
///
|
|
/// \param op2 is the parameter to compare with \b this
|
|
/// \return \b true if the parameters do not share a data-type and storage location
|
|
bool operator!=(const ProtoParameter &op2) const {
|
|
return !(*this==op2); }
|
|
};
|
|
|
|
/// \brief A stand-alone parameter with no backing symbol
|
|
///
|
|
/// Name, data-type, and storage location is stored internally to the object.
|
|
/// This is suitable for return values, function pointer prototypes, or functions
|
|
/// that haven't been fully analyzed.
|
|
class ParameterBasic : public ProtoParameter {
|
|
string name; ///< The name of the parameter, "" for undefined or return value parameters
|
|
Address addr; ///< Storage address of the parameter
|
|
Datatype *type; ///< Data-type of the parameter
|
|
uint4 flags; ///< Lock and other properties from ParameterPieces flags
|
|
public:
|
|
ParameterBasic(const string &nm,const Address &ad,Datatype *tp,uint4 fl) {
|
|
name = nm; addr = ad; type = tp; flags=fl; } ///< Construct from components
|
|
ParameterBasic(Datatype *tp) {
|
|
type = tp; flags = 0; } ///< Construct a \e void parameter
|
|
virtual const string &getName(void) const { return name; }
|
|
virtual Datatype *getType(void) const { return type; }
|
|
virtual Address getAddress(void) const { return addr; }
|
|
virtual int4 getSize(void) const { return type->getSize(); }
|
|
virtual bool isTypeLocked(void) const { return ((flags&ParameterPieces::typelock)!=0); }
|
|
virtual bool isNameLocked(void) const { return ((flags&ParameterPieces::namelock)!=0); }
|
|
virtual bool isSizeTypeLocked(void) const { return ((flags&ParameterPieces::sizelock)!=0); }
|
|
virtual bool isThisPointer(void) const { return ((flags&ParameterPieces::isthis)!=0); }
|
|
virtual bool isIndirectStorage(void) const { return ((flags&ParameterPieces::indirectstorage)!=0); }
|
|
virtual bool isHiddenReturn(void) const { return ((flags&ParameterPieces::hiddenretparm)!=0); }
|
|
virtual bool isNameUndefined(void) const { return (name.size()==0); }
|
|
virtual void setTypeLock(bool val);
|
|
virtual void setNameLock(bool val);
|
|
virtual void setThisPointer(bool val);
|
|
virtual void overrideSizeLockType(Datatype *ct);
|
|
virtual void resetSizeLockType(TypeFactory *factory);
|
|
virtual ProtoParameter *clone(void) const;
|
|
virtual Symbol *getSymbol(void) const { throw LowlevelError("Parameter is not a real symbol"); }
|
|
};
|
|
|
|
/// \brief A collection parameter descriptions making up a function prototype
|
|
///
|
|
/// A unified interface for accessing descriptions of individual
|
|
/// parameters in a function prototype. Both input parameters and return values
|
|
/// are described.
|
|
class ProtoStore {
|
|
public:
|
|
virtual ~ProtoStore(void) {} ///< Constructor
|
|
|
|
/// \brief Establish name, data-type, storage of a specific input parameter
|
|
///
|
|
/// This either allocates a new parameter or replaces the existing one at the
|
|
/// specified input slot. If there is a backing symbol table, a Symbol is
|
|
/// created or modified.
|
|
/// \param i is the specified input slot
|
|
/// \param nm is the (optional) name of the parameter
|
|
/// \param pieces holds the raw storage address and data-type to set
|
|
/// \return the new/modified ProtoParameter
|
|
virtual ProtoParameter *setInput(int4 i,const string &nm,const ParameterPieces &pieces)=0;
|
|
|
|
/// \brief Clear the input parameter at the specified slot
|
|
///
|
|
/// The parameter is excised, any following parameters are shifted to fill its spot.
|
|
/// If there is a backing Symbol, it is removed from the SymbolTable
|
|
/// \param i is the specified parameter slot to remove
|
|
virtual void clearInput(int4 i)=0;
|
|
|
|
virtual void clearAllInputs(void)=0; ///< Clear all input parameters (and any backing symbols)
|
|
virtual int4 getNumInputs(void) const=0; ///< Get the number of input parameters for \b this prototype
|
|
virtual ProtoParameter *getInput(int4 i)=0; ///< Get the i-th input parameter (or NULL if it doesn't exist)
|
|
|
|
/// \brief Establish the data-type and storage of the return value
|
|
///
|
|
/// This either allocates a new parameter or replaces the existing one.
|
|
/// A \e void return value can be specified with an \e invalid address and TYPE_VOID data-type.
|
|
/// \param piece holds the raw storage address and data-type to set
|
|
/// \return the new/modified ProtoParameter
|
|
virtual ProtoParameter *setOutput(const ParameterPieces &piece)=0;
|
|
|
|
virtual void clearOutput(void)=0; ///< Clear the return value to TYPE_VOID
|
|
virtual ProtoParameter *getOutput(void)=0; ///< Get the return-value description
|
|
virtual ProtoStore *clone(void) const=0; ///< Clone the entire collection of parameter descriptions
|
|
|
|
/// \brief Encode any parameters that are not backed by symbols to a stream
|
|
///
|
|
/// Symbols are stored elsewhere, so symbol backed parameters are not serialized.
|
|
/// If there are any internal parameters an \<internallist> element is emitted.
|
|
/// \param encoder is the stream encoder
|
|
virtual void encode(Encoder &encoder) const=0;
|
|
|
|
/// \brief Restore any internal parameter descriptions from a stream
|
|
///
|
|
/// Parse an \<internallist> element containing \<param> and \<retparam> child elements.
|
|
/// \param decoder is the stream decoder
|
|
/// \param model is prototype model for determining storage for unassigned parameters
|
|
virtual void decode(Decoder &decoder,ProtoModel *model)=0;
|
|
};
|
|
|
|
/// \brief A parameter with a formal backing Symbol
|
|
///
|
|
/// Input parameters generally have a symbol associated with them.
|
|
/// This class holds a reference to the Symbol object and pulls the relevant
|
|
/// parameter information off of it.
|
|
class ParameterSymbol : public ProtoParameter {
|
|
friend class ProtoStoreSymbol;
|
|
Symbol *sym; ///< Backing Symbol for \b this parameter
|
|
public:
|
|
ParameterSymbol(void) { sym = (Symbol *)0; } ///< Constructor
|
|
virtual const string &getName(void) const;
|
|
virtual Datatype *getType(void) const;
|
|
virtual Address getAddress(void) const;
|
|
virtual int4 getSize(void) const;
|
|
virtual bool isTypeLocked(void) const;
|
|
virtual bool isNameLocked(void) const;
|
|
virtual bool isSizeTypeLocked(void) const;
|
|
virtual bool isThisPointer(void) const;
|
|
virtual bool isIndirectStorage(void) const;
|
|
virtual bool isHiddenReturn(void) const;
|
|
virtual bool isNameUndefined(void) const;
|
|
virtual void setTypeLock(bool val);
|
|
virtual void setNameLock(bool val);
|
|
virtual void setThisPointer(bool val);
|
|
virtual void overrideSizeLockType(Datatype *ct);
|
|
virtual void resetSizeLockType(TypeFactory *factory);
|
|
virtual ProtoParameter *clone(void) const;
|
|
virtual Symbol *getSymbol(void) const;
|
|
};
|
|
|
|
/// \brief A collection of parameter descriptions backed by Symbol information
|
|
///
|
|
/// Input parameters are determined by symbols a function Scope
|
|
/// (category 0). Information about the return-value is stored internally.
|
|
/// ProtoParameter objects are constructed on the fly as requested and cached.
|
|
class ProtoStoreSymbol : public ProtoStore {
|
|
Scope *scope; ///< Backing Scope for input parameters
|
|
Address restricted_usepoint; ///< A usepoint reference for storage locations (usually function entry -1)
|
|
vector<ProtoParameter *> inparam; ///< Cache of allocated input parameters
|
|
ProtoParameter *outparam; ///< The return-value parameter
|
|
ParameterSymbol *getSymbolBacked(int4 i); ///< Fetch or allocate the parameter for the indicated slot
|
|
public:
|
|
ProtoStoreSymbol(Scope *sc,const Address &usepoint); ///< Constructor
|
|
virtual ~ProtoStoreSymbol(void);
|
|
virtual ProtoParameter *setInput(int4 i,const string &nm,const ParameterPieces &pieces);
|
|
virtual void clearInput(int4 i);
|
|
virtual void clearAllInputs(void);
|
|
virtual int4 getNumInputs(void) const;
|
|
virtual ProtoParameter *getInput(int4 i);
|
|
virtual ProtoParameter *setOutput(const ParameterPieces &piece);
|
|
virtual void clearOutput(void);
|
|
virtual ProtoParameter *getOutput(void);
|
|
virtual ProtoStore *clone(void) const;
|
|
virtual void encode(Encoder &encoder) const;
|
|
virtual void decode(Decoder &decoder,ProtoModel *model);
|
|
};
|
|
|
|
/// \brief A collection of parameter descriptions without backing symbols
|
|
///
|
|
/// Parameter descriptions are stored internally to the object and are not
|
|
/// mirrored by a symbol table.
|
|
class ProtoStoreInternal : public ProtoStore {
|
|
Datatype *voidtype; ///< Cached reference to the \b void data-type
|
|
vector<ProtoParameter *> inparam; ///< Descriptions of input parameters
|
|
ProtoParameter *outparam; ///< Description of the return value
|
|
public:
|
|
ProtoStoreInternal(Datatype *vt); ///< Constructor
|
|
virtual ~ProtoStoreInternal(void);
|
|
virtual ProtoParameter *setInput(int4 i,const string &nm,const ParameterPieces &pieces);
|
|
virtual void clearInput(int4 i);
|
|
virtual void clearAllInputs(void);
|
|
virtual int4 getNumInputs(void) const;
|
|
virtual ProtoParameter *getInput(int4 i);
|
|
virtual ProtoParameter *setOutput(const ParameterPieces &piece);
|
|
virtual void clearOutput(void);
|
|
virtual ProtoParameter *getOutput(void);
|
|
virtual ProtoStore *clone(void) const;
|
|
virtual void encode(Encoder &encoder) const;
|
|
virtual void decode(Decoder &decoder,ProtoModel *model);
|
|
};
|
|
|
|
/// \brief A \b function \b prototype
|
|
///
|
|
/// A description of the parameters and return value for a specific function.
|
|
/// Parameter descriptions include both source code features like \e name and \e data-type
|
|
/// but also give the storage location. Storage follows a specific parameter passing convention
|
|
/// (ProtoModel), although individual parameters may be customized. The prototype describes
|
|
/// numerous properties related to calling the specific function:
|
|
/// - Side-effects on non-parameter storage locations (like save registers)
|
|
/// - P-code injection associated with the function (uponentry, uponreturn, callfixup)
|
|
/// - Additional bytes (\b extrapop) popped from the stack by the function
|
|
/// - Method flags (thiscall, is_constructor, is_destructor)
|
|
class FuncProto {
|
|
enum {
|
|
dotdotdot = 1, ///< Set if \b this prototype takes variable arguments (varargs)
|
|
voidinputlock = 2, ///< Set if \b this prototype takes no inputs and is locked
|
|
modellock = 4, ///< Set if the PrototypeModel is locked for \b this prototype
|
|
is_inline = 8, ///< Should \b this be inlined (within calling function) by decompiler
|
|
no_return = 16, ///< Function does not return
|
|
paramshift_applied = 32, ///< paramshift parameters have been added and removed
|
|
error_inputparam = 64, ///< Set if the input parameters are not properly represented
|
|
error_outputparam = 128, ///< Set if the return value(s) are not properly represented
|
|
custom_storage = 256, ///< Parameter storage is custom (not derived from ProtoModel)
|
|
is_constructor = 0x200, ///< Function is an (object-oriented) constructor
|
|
is_destructor = 0x400, ///< Function is an (object-oriented) destructor
|
|
has_thisptr= 0x800, ///< Function is a method with a 'this' pointer as an argument
|
|
is_override = 0x1000, ///< Set if \b this prototype is created to override a single call site
|
|
auto_killbycall = 0x2000 ///< Potential output storage should always be considered \e killed \e by \e call
|
|
};
|
|
ProtoModel *model; ///< Model of for \b this prototype
|
|
ProtoStore *store; ///< Storage interface for parameters
|
|
int4 extrapop; ///< Extra bytes popped from stack
|
|
uint4 flags; ///< Boolean properties of the function prototype
|
|
vector<EffectRecord> effectlist; ///< Side-effects associated with non-parameter storage locations
|
|
vector<VarnodeData> likelytrash; ///< Locations that may contain \e trash values
|
|
int4 injectid; ///< (If non-negative) id of p-code snippet that should replace this function
|
|
int4 returnBytesConsumed; ///< Number of bytes of return value that are consumed by callers (0 = all bytes)
|
|
void updateThisPointer(void); ///< Make sure any "this" parameter is properly marked
|
|
void encodeEffect(Encoder &encoder) const; ///< Encode any overriding EffectRecords to stream
|
|
void encodeLikelyTrash(Encoder &encoder) const; ///< Encode any overriding likelytrash registers to stream
|
|
void decodeEffect(void); ///< Merge in any EffectRecord overrides
|
|
void decodeLikelyTrash(void); ///< Merge in any \e likelytrash overrides
|
|
protected:
|
|
void paramShift(int4 paramshift); ///< Add parameters to the front of the input parameter list
|
|
bool isParamshiftApplied(void) const { return ((flags¶mshift_applied)!=0); } ///< Has a parameter shift been applied
|
|
/// \brief Toggle whether a parameter shift has been applied
|
|
void setParamshiftApplied(bool val) { flags = val ? (flags|paramshift_applied) : (flags & ~((uint4)paramshift_applied)); }
|
|
public:
|
|
FuncProto(void); ///< Constructor
|
|
~FuncProto(void); ///< Destructor
|
|
Architecture *getArch(void) const { return model->getArch(); } ///< Get the Architecture owning \b this
|
|
void copy(const FuncProto &op2); ///< Copy another function prototype
|
|
void copyFlowEffects(const FuncProto &op2); ///< Copy properties that affect data-flow
|
|
void getPieces(PrototypePieces &pieces) const; ///< Get the raw pieces of the prototype
|
|
void setPieces(const PrototypePieces &pieces); ///< Set \b this prototype based on raw pieces
|
|
void setScope(Scope *s,const Address &startpoint); ///< Set a backing symbol Scope for \b this
|
|
void setInternal(ProtoModel *m,Datatype *vt); ///< Set internal backing storage for \b this
|
|
void setModel(ProtoModel *m); ///< Set the prototype model for \b this
|
|
bool hasModel(void) const { return (model != (ProtoModel *)0); } ///< Does \b this prototype have a model
|
|
|
|
bool hasMatchingModel(const ProtoModel *op2) const { return (model == op2); } ///< Does \b this use the given model
|
|
const string &getModelName(void) const { return model->getName(); } ///< Get the prototype model name
|
|
int4 getModelExtraPop(void) const { return model->getExtraPop(); } ///< Get the \e extrapop of the prototype model
|
|
bool isModelUnknown(void) const { return model->isUnknown(); } ///< Return \b true if the prototype model is \e unknown
|
|
bool printModelInDecl(void) const { return model->printInDecl(); } ///< Return \b true if the name should be printed in declarations
|
|
|
|
bool isInputLocked(void) const; ///< Are input data-types locked
|
|
bool isOutputLocked(void) const { return store->getOutput()->isTypeLocked(); } ///< Is the output data-type locked
|
|
bool isModelLocked(void) const { return ((flags&modellock)!=0); } ///< Is the prototype model for \b this locked
|
|
bool hasCustomStorage(void) const { return ((flags&custom_storage)!=0); } ///< Is \b this a "custom" function prototype
|
|
void setInputLock(bool val); ///< Toggle the data-type lock on input parameters
|
|
void setOutputLock(bool val); ///< Toggle the data-type lock on the return value
|
|
|
|
/// \brief Toggle the lock on the prototype model for \b this.
|
|
///
|
|
/// The prototype model can be locked while still leaving parameters unlocked. Parameter
|
|
/// recovery will follow the rules of the locked model.
|
|
/// \param val is \b true to indicate a lock, \b false for unlocked
|
|
void setModelLock(bool val) { flags = val ? (flags|modellock) : (flags & ~((uint4)modellock)); }
|
|
|
|
bool isInline(void) const { return ((flags & is_inline)!=0); } ///< Does this function get \e in-lined during decompilation.
|
|
|
|
/// \brief Toggle the \e in-line setting for functions with \b this prototype
|
|
///
|
|
/// In-lining can be based on a \e call-fixup, or the full body of the function can be in-lined.
|
|
/// \param val is \b true if in-lining should be performed.
|
|
void setInline(bool val) { flags = val ? (flags|is_inline) : (flags & ~((uint4)is_inline)); }
|
|
|
|
/// \brief Get the injection id associated with \b this.
|
|
///
|
|
/// A non-negative id indicates a \e call-fixup is used to in-line function's with \b this prototype.
|
|
/// \return the id value corresponding to the specific call-fixup or -1 if there is no call-fixup
|
|
int4 getInjectId(void) const { return injectid; }
|
|
|
|
/// \brief Get an estimate of the number of bytes consumed by callers of \b this prototype.
|
|
///
|
|
/// A value of 0 means \e all possible bytes of the storage location are consumed.
|
|
/// \return the number of bytes or 0
|
|
int4 getReturnBytesConsumed(void) const { return returnBytesConsumed; }
|
|
|
|
bool setReturnBytesConsumed(int4 val); ///< Set the number of bytes consumed by callers of \b this
|
|
|
|
/// \brief Does a function with \b this prototype never return
|
|
bool isNoReturn(void) const { return ((flags & no_return)!=0); }
|
|
|
|
/// \brief Toggle the \e no-return setting for functions with \b this prototype
|
|
///
|
|
/// \param val is \b true to treat the function as never returning
|
|
void setNoReturn(bool val) { flags = val ? (flags|no_return) : (flags & ~((uint4)no_return)); }
|
|
|
|
/// \brief Is \b this a prototype for a class method, taking a \e this pointer.
|
|
bool hasThisPointer(void) const { return ((flags & has_thisptr)!=0); }
|
|
|
|
/// \brief Is \b this prototype for a class constructor method
|
|
bool isConstructor(void) const { return ((flags & is_constructor)!=0); }
|
|
|
|
/// \brief Toggle whether \b this prototype is a \e constructor method
|
|
///
|
|
/// \param val is \b true if \b this is a constructor, \b false otherwise
|
|
void setConstructor(bool val) { flags = val ? (flags|is_constructor) : (flags & ~((uint4)is_constructor)); }
|
|
|
|
/// \brief Is \b this prototype for a class destructor method
|
|
bool isDestructor(void) const { return ((flags & is_destructor)!=0); }
|
|
|
|
/// \brief Toggle whether \b this prototype is a \e destructor method
|
|
///
|
|
/// \param val is \b true if \b this is a destructor
|
|
void setDestructor(bool val) { flags = val ? (flags|is_destructor) : (flags & ~((uint4)is_destructor)); }
|
|
|
|
/// \brief Has \b this prototype been marked as having an incorrect input parameter descriptions
|
|
bool hasInputErrors(void) const { return ((flags&error_inputparam)!=0); }
|
|
|
|
/// \brief Has \b this prototype been marked as having an incorrect return value description
|
|
bool hasOutputErrors(void) const { return ((flags&error_outputparam)!=0); }
|
|
|
|
/// \brief Toggle the input error setting for \b this prototype
|
|
///
|
|
/// \param val is \b true if input parameters should be marked as in error
|
|
void setInputErrors(bool val) { flags = val ? (flags|error_inputparam) : (flags & ~((uint4)error_inputparam)); }
|
|
|
|
/// \brief Toggle the output error setting for \b this prototype
|
|
///
|
|
/// \param val is \b true if return value should be marked as in error
|
|
void setOutputErrors(bool val) { flags = val ? (flags|error_outputparam) : (flags & ~((uint4)error_outputparam)); }
|
|
|
|
int4 getExtraPop(void) const { return extrapop; } ///< Get the general \e extrapop setting for \b this prototype
|
|
void setExtraPop(int4 ep) { extrapop = ep; } ///< Set the general \e extrapop for \b this prototype
|
|
int4 getInjectUponEntry(void) const { return model->getInjectUponEntry(); } ///< Get any \e upon-entry injection id (or -1)
|
|
int4 getInjectUponReturn(void) const { return model->getInjectUponReturn(); } ///< Get any \e upon-return injection id (or -1)
|
|
void resolveExtraPop(void);
|
|
|
|
void clearUnlockedInput(void); ///< Clear input parameters that have not been locked
|
|
void clearUnlockedOutput(void); ///< Clear the return value if it has not been locked
|
|
void clearInput(void); ///< Clear all input parameters regardless of lock
|
|
void setInjectId(int4 id); ///< Associate a given injection with \b this prototype
|
|
void cancelInjectId(void); ///< Turn-off any in-lining for this function
|
|
|
|
void resolveModel(ParamActive *active);
|
|
|
|
/// \brief Given a list of input \e trials, derive the most likely inputs for \b this prototype
|
|
///
|
|
/// Trials are sorted and marked as \e used or not.
|
|
/// \param active is the collection of Varnode input trials
|
|
void deriveInputMap(ParamActive *active) const {
|
|
model->deriveInputMap(active); }
|
|
|
|
/// \brief Given a list of output \e trials, derive the most likely return value for \b this prototype
|
|
///
|
|
/// One trial (at most) is marked \e used and moved to the front of the list
|
|
/// \param active is the collection of output trials
|
|
void deriveOutputMap(ParamActive *active) const {
|
|
model->deriveOutputMap(active); }
|
|
|
|
/// \brief Check if the given two input storage locations can represent a single logical parameter
|
|
///
|
|
/// For \b this prototype, do the two (hi/lo) locations represent
|
|
/// consecutive input parameter locations that can be replaced by a single logical parameter.
|
|
/// \param hiaddr is the address of the most significant part of the value
|
|
/// \param hisz is the size of the most significant part in bytes
|
|
/// \param loaddr is the address of the least significant part of the value
|
|
/// \param losz is the size of the least significant part in bytes
|
|
/// \return \b true if the two pieces can be joined
|
|
bool checkInputJoin(const Address &hiaddr,int4 hisz,const Address &loaddr,int4 losz) const {
|
|
return model->checkInputJoin(hiaddr,hisz,loaddr,losz); }
|
|
|
|
/// \brief Check if it makes sense to split a single storage location into two input parameters
|
|
///
|
|
/// A storage location and split point is provided, implying two new storage locations. Does
|
|
/// \b this prototype allow these locations to be considered separate parameters.
|
|
/// \param loc is the starting address of provided storage location
|
|
/// \param size is the size of the location in bytes
|
|
/// \param splitpoint is the number of bytes to consider in the first (in address order) piece
|
|
/// \return \b true if the storage location can be split
|
|
bool checkInputSplit(const Address &loc,int4 size,int4 splitpoint) const {
|
|
return model->checkInputSplit(loc,size,splitpoint); }
|
|
|
|
void updateInputTypes(Funcdata &data,const vector<Varnode *> &triallist,ParamActive *activeinput);
|
|
void updateInputNoTypes(Funcdata &data,const vector<Varnode *> &triallist,ParamActive *activeinput);
|
|
void updateOutputTypes(const vector<Varnode *> &triallist);
|
|
void updateOutputNoTypes(const vector<Varnode *> &triallist,TypeFactory *factory);
|
|
void updateAllTypes(const PrototypePieces &proto);
|
|
ProtoParameter *getParam(int4 i) const { return store->getInput(i); } ///< Get the i-th input parameter
|
|
void setParam(int4 i,const string &name,const ParameterPieces &piece) { store->setInput(i, name, piece); } ///< Set parameter storage directly
|
|
void removeParam(int4 i) { store->clearInput(i); } ///< Remove the i-th input parameter
|
|
int4 numParams(void) const { return store->getNumInputs(); } ///< Get the number of input parameters
|
|
ProtoParameter *getOutput(void) const { return store->getOutput(); } ///< Get the return value
|
|
void setOutput(const ParameterPieces &piece) { store->setOutput(piece); } ///< Set return value storage directly
|
|
Datatype *getOutputType(void) const { return store->getOutput()->getType(); } ///< Get the return value data-type
|
|
const RangeList &getLocalRange(void) const { return model->getLocalRange(); } ///< Get the range of potential local stack variables
|
|
const RangeList &getParamRange(void) const { return model->getParamRange(); } ///< Get the range of potential stack parameters
|
|
bool isStackGrowsNegative(void) const { return model->isStackGrowsNegative(); } ///< Return \b true if the stack grows toward smaller addresses
|
|
bool isDotdotdot(void) const { return ((flags&dotdotdot)!=0); } ///< Return \b true if \b this takes a variable number of arguments
|
|
void setDotdotdot(bool val) { flags = val ? (flags|dotdotdot) : (flags & ~((uint4)dotdotdot)); } ///< Toggle whether \b this takes variable arguments
|
|
bool isOverride(void) const { return ((flags&is_override)!=0); } ///< Return \b true if \b this is a call site override
|
|
void setOverride(bool val) { flags = val ? (flags|is_override) : (flags & ~((uint4)is_override)); } ///< Toggle whether \b this is a call site override
|
|
uint4 hasEffect(const Address &addr,int4 size) const;
|
|
vector<EffectRecord>::const_iterator effectBegin(void) const; ///< Get iterator to front of EffectRecord list
|
|
vector<EffectRecord>::const_iterator effectEnd(void) const; ///< Get iterator to end of EffectRecord list
|
|
vector<VarnodeData>::const_iterator trashBegin(void) const; ///< Get iterator to front of \e likelytrash list
|
|
vector<VarnodeData>::const_iterator trashEnd(void) const; ///< Get iterator to end of \e likelytrash list
|
|
vector<VarnodeData>::const_iterator internalBegin(void) const { return model->internalBegin(); } ///< Get iterator to front of \e internalstorage list
|
|
vector<VarnodeData>::const_iterator internalEnd(void) const { return model->internalEnd(); } ///< Get iterator to end of \e internalstorage list
|
|
int4 characterizeAsInputParam(const Address &addr,int4 size) const;
|
|
int4 characterizeAsOutput(const Address &addr,int4 size) const;
|
|
bool possibleInputParam(const Address &addr,int4 size) const;
|
|
bool possibleOutputParam(const Address &addr,int4 size) const;
|
|
|
|
/// \brief Return the maximum heritage delay across all possible input parameters
|
|
///
|
|
/// Depending on the address space, data-flow for a parameter may not be available until
|
|
/// extra transform passes have completed. This method returns the number of passes
|
|
/// that must occur before we can guarantee that all parameters have data-flow info.
|
|
/// \return the maximum number of passes across all input parameters in \b this prototype
|
|
int4 getMaxInputDelay(void) const { return model->getMaxInputDelay(); }
|
|
|
|
/// \brief Return the maximum heritage delay across all possible return values
|
|
///
|
|
/// Depending on the address space, data-flow for a parameter may not be available until
|
|
/// extra transform passes have completed. This method returns the number of passes
|
|
/// that must occur before we can guarantee that any return value has data-flow info.
|
|
/// \return the maximum number of passes across all output parameters in \b this prototype
|
|
int4 getMaxOutputDelay(void) const { return model->getMaxOutputDelay(); }
|
|
|
|
bool unjustifiedInputParam(const Address &addr,int4 size,VarnodeData &res) const;
|
|
|
|
/// \brief Get the type of extension and containing input parameter for the given storage
|
|
///
|
|
/// If the given storage is properly contained within a normal parameter and the model
|
|
/// typically extends a small value into the full container, pass back the full container
|
|
/// and the type of extension.
|
|
/// \param addr is the starting address of the given storage
|
|
/// \param size is the number of bytes in the given storage
|
|
/// \param res is the parameter storage to pass back
|
|
/// \return the extension operator (INT_ZEXT INT_SEXT) or INT_COPY if there is no extension.
|
|
/// INT_PIECE indicates the extension is determined by the specific prototype.
|
|
OpCode assumedInputExtension(const Address &addr,int4 size,VarnodeData &res) const {
|
|
return model->assumedInputExtension(addr,size,res); }
|
|
|
|
/// \brief Get the type of extension and containing return value location for the given storage
|
|
///
|
|
/// If the given storage is properly contained within a normal return value location and the model
|
|
/// typically extends a small value into the full container, pass back the full container
|
|
/// and the type of extension.
|
|
/// \param addr is the starting address of the given storage
|
|
/// \param size is the number of bytes in the given storage
|
|
/// \param res is the parameter storage to pass back
|
|
/// \return the extension operator (INT_ZEXT INT_SEXT) or INT_COPY if there is no extension.
|
|
/// INT_PIECE indicates the extension is determined by the specific prototype.
|
|
OpCode assumedOutputExtension(const Address &addr,int4 size,VarnodeData &res) const {
|
|
return model->assumedOutputExtension(addr,size,res); }
|
|
|
|
/// \brief Pass-back the biggest potential input parameter contained within the given range
|
|
bool getBiggestContainedInputParam(const Address &loc,int4 size,VarnodeData &res) const;
|
|
|
|
/// \brief Pass-back the biggest potential output storage location contained within the given range
|
|
bool getBiggestContainedOutput(const Address &loc,int4 size,VarnodeData &res) const;
|
|
|
|
Address getThisPointerStorage(Datatype *dt); ///< Get the storage location associated with the "this" pointer
|
|
|
|
bool isCompatible(const FuncProto &op2) const;
|
|
AddrSpace *getSpacebase(void) const { return model->getSpacebase(); } ///< Get the \e stack address space
|
|
void printRaw(const string &funcname,ostream &s) const;
|
|
|
|
/// \brief Get the comparable properties of \b this prototype
|
|
///
|
|
/// Get properties not including locking, error, and inlining flags.
|
|
/// \return the active set of flags for \b this prototype
|
|
uint4 getComparableFlags(void) const { return (flags & (dotdotdot | is_constructor | is_destructor | has_thisptr )); }
|
|
|
|
bool isAutoKillByCall(void) const; ///< Is a potential output automatically considered \e killed \e by \e call
|
|
|
|
void encode(Encoder &encoder) const;
|
|
void decode(Decoder &decoder,Architecture *glb);
|
|
};
|
|
|
|
class Funcdata;
|
|
|
|
/// \brief A class for analyzing parameters to a sub-function call
|
|
///
|
|
/// This can be viewed as a function prototype that evolves over the course of
|
|
/// analysis. It derives off of FuncProto and includes facilities for analyzing
|
|
/// data-flow for parameter information. This is the high-level object managing
|
|
/// the examination of data-flow to recover a working prototype (ParamActive),
|
|
/// holding a stack-pointer placeholder to facilitate stack analysis, and deciding
|
|
/// on the working \e extrapop for the CALL.
|
|
///
|
|
/// A \b stack-pointer \b placeholder is a temporary Varnode in the input operands
|
|
/// of the CALL or CALLIND that is defined by a LOAD from the stack-pointer. By examining
|
|
/// the pointer, the exact value of the stack-pointer (relative to its incoming value) can
|
|
/// be computed at the point of the CALL. The temporary can arise naturally if stack
|
|
/// parameters are a possibility, otherwise a placeholder temporary is artificially
|
|
/// inserted into the CALL input. At the time heritage of the stack space is computed,
|
|
/// the placeholder is examined to read off the active stack-pointer offset for the CALL
|
|
/// and the placeholder is removed.
|
|
class FuncCallSpecs : public FuncProto {
|
|
PcodeOp *op; ///< Pointer to CALL or CALLIND instruction
|
|
string name; ///< Name of function if present
|
|
Address entryaddress; ///< First executing address of function
|
|
Funcdata *fd; ///< The Funcdata object for the called functon (if known)
|
|
int4 effective_extrapop; ///< Working extrapop for the CALL
|
|
uintb stackoffset; ///< Relative offset of stack-pointer at time of this call
|
|
int4 stackPlaceholderSlot; ///< Slot containing temporary stack tracing placeholder (-1 means unused)
|
|
int4 paramshift; ///< Number of input parameters to ignore before prototype
|
|
int4 matchCallCount; ///< Number of calls to this sub-function within the calling function
|
|
ParamActive activeinput; ///< Info for recovering input parameters
|
|
ParamActive activeoutput; ///< Info for recovering output parameters
|
|
mutable vector<int4> inputConsume; ///< Number of bytes consumed by sub-function, for each input parameter
|
|
bool isinputactive; ///< Are we actively trying to recover input parameters
|
|
bool isoutputactive; ///< Are we actively trying to recover output parameters
|
|
bool isbadjumptable; ///< Was the call originally a jump-table we couldn't recover
|
|
bool isstackoutputlock; ///< Do we have a locked output on the stack
|
|
Varnode *getSpacebaseRelative(void) const; ///< Get the active stack-pointer Varnode at \b this call site
|
|
Varnode *buildParam(Funcdata &data,Varnode *vn,ProtoParameter *param,Varnode *stackref);
|
|
int4 transferLockedInputParam(ProtoParameter *param);
|
|
void transferLockedOutputParam(ProtoParameter *param,vector<Varnode *> &newoutput);
|
|
bool transferLockedInput(vector<Varnode *> &newinput,const FuncProto &source);
|
|
bool transferLockedOutput(vector<Varnode *> &newoutput,const FuncProto &source);
|
|
void commitNewInputs(Funcdata &data,vector<Varnode *> &newinput);
|
|
void commitNewOutputs(Funcdata &data,vector<Varnode *> &newoutput);
|
|
void collectOutputTrialVarnodes(vector<Varnode *> &trialvn);
|
|
void setStackPlaceholderSlot(int4 slot) { stackPlaceholderSlot = slot;
|
|
if (isinputactive) activeinput.setPlaceholderSlot(); } ///< Set the slot of the stack-pointer placeholder
|
|
void clearStackPlaceholderSlot(void) {
|
|
stackPlaceholderSlot = -1; if (isinputactive) activeinput.freePlaceholderSlot(); } ///< Release the stack-pointer placeholder
|
|
public:
|
|
enum {
|
|
offset_unknown = 0xBADBEEF ///< "Magic" stack offset indicating the offset is unknown
|
|
};
|
|
FuncCallSpecs(PcodeOp *call_op); ///< Construct based on CALL or CALLIND
|
|
void setAddress(const Address &addr) { entryaddress = addr; } ///< Set (override) the callee's entry address
|
|
PcodeOp *getOp(void) const { return op; } ///< Get the CALL or CALLIND corresponding to \b this
|
|
Funcdata *getFuncdata(void) const { return fd; } ///< Get the Funcdata object associated with the called function
|
|
void setFuncdata(Funcdata *f); ///< Set the Funcdata object associated with the called function
|
|
FuncCallSpecs *clone(PcodeOp *newop) const; ///< Clone \b this given the mirrored p-code CALL
|
|
const string &getName(void) const { return name; } ///< Get the function name associated with the callee
|
|
const Address &getEntryAddress(void) const { return entryaddress; } ///< Get the entry address of the callee
|
|
void setEffectiveExtraPop(int4 epop) { effective_extrapop = epop; } ///< Set the specific \e extrapop associate with \b this call site
|
|
int4 getEffectiveExtraPop(void) const { return effective_extrapop; } ///< Get the specific \e extrapop associate with \b this call site
|
|
uintb getSpacebaseOffset(void) const { return stackoffset; } ///< Get the stack-pointer relative offset at the point of \b this call site
|
|
void setParamshift(int4 val) { paramshift = val; } ///< Set a parameter shift for this call site
|
|
int4 getParamshift(void) const { return paramshift; } ///< Get the parameter shift for this call site
|
|
int4 getMatchCallCount(void) const { return matchCallCount; } ///< Get the number of calls the caller makes to \b this sub-function
|
|
int4 getStackPlaceholderSlot(void) const { return stackPlaceholderSlot; } ///< Get the slot of the stack-pointer placeholder
|
|
|
|
void initActiveInput(void); ///< Turn on analysis recovering input parameters
|
|
void clearActiveInput(void) { isinputactive = false; } ///< Turn off analysis recovering input parameters
|
|
void initActiveOutput(void) { isoutputactive = true; } ///< Turn on analysis recovering the return value
|
|
void clearActiveOutput(void) { isoutputactive = false; } ///< Turn off analysis recovering the return value
|
|
bool isInputActive(void) const { return isinputactive; } ///< Return \b true if input parameter recovery analysis is active
|
|
bool isOutputActive(void) const { return isoutputactive; } ///< Return \b true if return value recovery analysis is active
|
|
void setBadJumpTable(bool val) { isbadjumptable = val; } ///< Toggle whether \b call site looked like an indirect jump
|
|
bool isBadJumpTable(void) const { return isbadjumptable; } ///< Return \b true if \b this call site looked like an indirect jump
|
|
void setStackOutputLock(bool val) { isstackoutputlock = val; } ///< Toggle whether output is locked and on the stack
|
|
bool isStackOutputLock(void) const { return isstackoutputlock; } ///< Return \b true if return value is locked and on the stack
|
|
ParamActive *getActiveInput(void) { return &activeinput; } ///< Get the analysis object for input parameter recovery
|
|
ParamActive *getActiveOutput(void) { return &activeoutput; } ///< Get the analysis object for return value recovery
|
|
|
|
bool checkInputJoin(int4 slot1,bool ishislot,Varnode *vn1,Varnode *vn2) const;
|
|
void doInputJoin(int4 slot1,bool ishislot);
|
|
bool lateRestriction(const FuncProto &restrictedProto,vector<Varnode *> &newinput,vector<Varnode *> &newoutput);
|
|
void deindirect(Funcdata &data,Funcdata *newfd);
|
|
void forceSet(Funcdata &data,const FuncProto &fp);
|
|
void insertPcode(Funcdata &data);
|
|
void createPlaceholder(Funcdata &data,AddrSpace *spacebase);
|
|
void resolveSpacebaseRelative(Funcdata &data,Varnode *phvn);
|
|
void abortSpacebaseRelative(Funcdata &data);
|
|
void finalInputCheck(void);
|
|
void checkInputTrialUse(Funcdata &data,AliasChecker &aliascheck);
|
|
void checkOutputTrialUse(Funcdata &data,vector<Varnode *> &trialvn);
|
|
void buildInputFromTrials(Funcdata &data);
|
|
void buildOutputFromTrials(Funcdata &data,vector<Varnode *> &trialvn);
|
|
int4 getInputBytesConsumed(int4 slot) const;
|
|
bool setInputBytesConsumed(int4 slot,int4 val) const;
|
|
void paramshiftModifyStart(void);
|
|
bool paramshiftModifyStop(Funcdata &data);
|
|
uint4 hasEffectTranslate(const Address &addr,int4 size) const;
|
|
static Varnode *findPreexistingWhole(Varnode *vn1,Varnode *vn2);
|
|
|
|
/// \brief Convert FspecSpace addresses to the underlying FuncCallSpecs object
|
|
///
|
|
/// \param addr is the given \e fspec address
|
|
/// \return the FuncCallSpecs object
|
|
static FuncCallSpecs *getFspecFromConst(const Address &addr) { return (FuncCallSpecs *)(uintp)addr.getOffset(); }
|
|
|
|
/// \brief Compare FuncCallSpecs by function entry address
|
|
///
|
|
/// \param a is the first FuncCallSpecs to compare
|
|
/// \param b is the second to compare
|
|
/// \return \b true if the first should be ordered before the second
|
|
static bool compareByEntryAddress(const FuncCallSpecs *a,const FuncCallSpecs *b) { return a->entryaddress < b->entryaddress; }
|
|
static void countMatchingCalls(const vector<FuncCallSpecs *> &qlst);
|
|
};
|
|
|
|
/// Return the trial associated with the input Varnode to the associated p-code CALL or CALLIND.
|
|
/// We take into account the call address parameter (subtract 1) and if the index occurs \e after the
|
|
/// index holding the stackpointer placeholder, we subtract an additional 1.
|
|
/// \param slot is the input index of the input Varnode
|
|
/// \return the corresponding parameter trial
|
|
inline const ParamTrial &ParamActive::getTrialForInputVarnode(int4 slot) const
|
|
|
|
{
|
|
slot -= ((stackplaceholder<0)||(slot<stackplaceholder)) ? 1 : 2;
|
|
return trial[slot];
|
|
}
|
|
|
|
/// \brief Compare two EffectRecords by their start Address
|
|
///
|
|
/// \param op1 is the first record to compare
|
|
/// \param op2 is the other record to compare
|
|
/// \return \b true if \b this should be ordered before the other record
|
|
inline bool EffectRecord::compareByAddress(const EffectRecord &op1,const EffectRecord &op2)
|
|
|
|
{
|
|
if (op1.range.space != op2.range.space)
|
|
return (op1.range.space->getIndex() < op2.range.space->getIndex());
|
|
return (op1.range.offset < op2.range.offset);
|
|
}
|
|
|
|
inline bool EffectRecord::operator==(const EffectRecord &op2) const
|
|
|
|
{
|
|
if (range != op2.range) return false;
|
|
return (type == op2.type);
|
|
}
|
|
|
|
inline bool EffectRecord::operator!=(const EffectRecord &op2) const
|
|
|
|
{
|
|
if (range != op2.range) return true;
|
|
return (type != op2.type);
|
|
}
|
|
|
|
} // End namespace ghidra
|
|
#endif
|