mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-03 09:49:23 +02:00
172 lines
12 KiB
C++
172 lines
12 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 flow.hh
|
|
/// \brief Utilities for following control-flow in p-code generated from machine instructions
|
|
|
|
#ifndef __FLOW_HH__
|
|
#define __FLOW_HH__
|
|
|
|
#include "funcdata.hh"
|
|
|
|
namespace ghidra {
|
|
|
|
/// \brief A class for generating the control-flow structure for a single function
|
|
///
|
|
/// Control-flow for the function is generated in two phases: the method generateOps() produces
|
|
/// all the raw p-code ops for the function, and the method generateBlocks() organizes the
|
|
/// p-code ops into basic blocks (PcodeBlockBasic).
|
|
/// In generateOps(), p-code is generated for every machine instruction that is reachable starting
|
|
/// with the entry point address of the function. All possible flow is followed, trimming flow
|
|
/// at instructions that end with the formal RETURN p-code operation. CALL and CALLIND are treated
|
|
/// as fall-through operations, and flow is not followed into the sub-function.
|
|
///
|
|
/// The class supports various options for handling corner cases during the flow following process,
|
|
/// including how to handle:
|
|
/// - Flow out of range (specified by setRange())
|
|
/// - Flow into unimplemened instructions
|
|
/// - Flow into unaccessible data
|
|
/// - Flow into previously traversed data at an \e off cut (\b reinterpreted data)
|
|
/// - Flow that (seemingly) doesn't end, exceeding a threshold on the number of instructions
|
|
///
|
|
/// In generateBlocks(), all previously generated PcodeOp instructions are assigned to a
|
|
/// PcodeBlockBasic. These objects define the formal basic block structure of the function.
|
|
/// Directed control-flow edges between the blocks are created at this time based on the
|
|
/// flow of p-code.
|
|
///
|
|
/// A Funcdata object provided to the constructor holds:
|
|
/// - The generated PcodeOp objects (within its PcodeOpBank).
|
|
/// - The control-flow graph (within its BlockGraph)
|
|
/// - The list of discovered sub-function calls (as FuncCallSpec objects)
|
|
///
|
|
/// The Translate object (provided by the Architecture owning the function) generates
|
|
/// the raw p-code ops for a single instruction. This FlowInfo class also handles
|
|
/// p-code \e injection triggers encountered during flow following, primarily using
|
|
/// the architecture's PcodeInjectLibrary to resolve them.
|
|
class FlowInfo {
|
|
public:
|
|
enum { ignore_outofbounds = 1, ///< Ignore/truncate flow into addresses out of the specified range
|
|
ignore_unimplemented = 2, ///< Treat unimplemented instructions as a NOP (no operation)
|
|
error_outofbounds = 4, ///< Throw an exception for flow into addresses out of the specified range
|
|
error_unimplemented = 8, ///< Throw an exception for flow into unimplemented instructions
|
|
error_reinterpreted = 0x10, ///< Throw an exception for flow into previously encountered data at a difference \e cut
|
|
error_toomanyinstructions = 0x20, ///< Throw an exception if too many instructions are encountered
|
|
unimplemented_present = 0x40, ///< Indicate we have encountered unimplemented instructions
|
|
baddata_present = 0x80, ///< Indicate we have encountered flow into unaccessible data
|
|
outofbounds_present = 0x100, ///< Indicate we have encountered flow out of the specified range
|
|
reinterpreted_present = 0x200, ///< Indicate we have encountered reinterpreted data
|
|
toomanyinstructions_present = 0x400, ///< Indicate the maximum instruction threshold was reached
|
|
possible_unreachable = 0x1000, ///< Indicate a CALL was converted to a BRANCH and some code may be unreachable
|
|
flow_forinline = 0x2000, ///< Indicate flow is being generated to in-line (a function)
|
|
record_jumploads = 0x4000 ///< Indicate that any jump table recovery should record the table structure
|
|
};
|
|
private:
|
|
/// \brief A helper function describing the number of bytes in a machine instruction and the starting p-code op
|
|
struct VisitStat {
|
|
SeqNum seqnum; ///< Sequence number of first PcodeOp in the instruction (or INVALID if no p-code)
|
|
int4 size; ///< Number of bytes in the instruction
|
|
};
|
|
Architecture *glb; ///< Owner of the function
|
|
Funcdata &data; ///< The function being flow-followed
|
|
PcodeOpBank &obank; ///< Container for generated p-code
|
|
BlockGraph &bblocks; ///< Container for the control-flow graph
|
|
vector<FuncCallSpecs *> &qlst; ///< The list of discovered sub-function call sites
|
|
PcodeEmitFd emitter; ///< PCodeOp factory (configured to allocate into \b data and \b obank)
|
|
vector<Address> unprocessed; ///< Addresses which are permanently unprocessed
|
|
vector<Address> addrlist; ///< Addresses to which there is flow
|
|
vector<PcodeOp *> tablelist; ///< List of BRANCHIND ops (preparing for jump table recovery)
|
|
vector<PcodeOp *> injectlist; ///< List of p-code ops that need injection
|
|
map<Address,VisitStat> visited; ///< Map of machine instructions that have been visited so far
|
|
list<PcodeOp *> block_edge1; ///< Source p-code op (Edges between basic blocks)
|
|
list<PcodeOp *> block_edge2; ///< Destination p-code op (Edges between basic blocks)
|
|
uint4 insn_count; ///< Number of instructions flowed through
|
|
uint4 insn_max; ///< Maximum number of instructions
|
|
Address baddr; ///< Start of range in which we are allowed to flow
|
|
Address eaddr; ///< End of range in which we are allowed to flow
|
|
Address minaddr; ///< Start of actual function range
|
|
Address maxaddr; ///< End of actual function range
|
|
bool flowoverride_present; ///< Does the function have registered flow override instructions
|
|
uint4 flags; ///< Boolean options for flow following
|
|
Funcdata *inline_head; ///< First function in the in-lining chain
|
|
set<Address> *inline_recursion; ///< Active list of addresses for function that are in-lined
|
|
set<Address> inline_base; ///< Storage for addresses of functions that are in-lined
|
|
bool hasPossibleUnreachable(void) const { return ((flags & possible_unreachable)!=0); } ///< Are there possible unreachable ops
|
|
void setPossibleUnreachable(void) { flags |= possible_unreachable; } ///< Mark that there may be unreachable ops
|
|
void clearProperties(void); ///< Clear any discovered flow properties
|
|
bool seenInstruction(const Address &addr) const {
|
|
return (visited.find(addr) != visited.end()); } ///< Has the given instruction (address) been seen in flow
|
|
PcodeOp *fallthruOp(PcodeOp *op) const; ///< Find fallthru pcode-op for given op
|
|
void newAddress(PcodeOp *from,const Address &to); ///< Register a new (non fall-thru) flow target
|
|
void deleteRemainingOps(list<PcodeOp *>::const_iterator oiter);
|
|
PcodeOp *xrefControlFlow(list<PcodeOp *>::const_iterator oiter,bool &startbasic,bool &isfallthru,FuncCallSpecs *fc);
|
|
bool processInstruction(const Address &curaddr,bool &startbasic);
|
|
void fallthru(void); ///< Process (the next) sequence of instructions in fall-thru order
|
|
PcodeOp *findRelTarget(PcodeOp *op,Address &res) const;
|
|
void findUnprocessed(void); ///< Add any remaining un-followed addresses to the \b unprocessed list
|
|
void dedupUnprocessed(void); ///< Get rid of duplicates in the \b unprocessed list
|
|
void fillinBranchStubs(void); ///< Fill-in artificial HALT p-code for \b unprocessed addresses
|
|
void collectEdges(void); ///< Collect edges between basic blocks as PcodeOp to PcodeOp pairs
|
|
void splitBasic(void); ///< Split raw p-code ops up into basic blocks
|
|
void connectBasic(void); ///< Generate edges between basic blocks
|
|
bool setFallthruBound(Address &bound); ///< Find end of the next unprocessed region
|
|
void handleOutOfBounds(const Address &fromaddr,const Address &toaddr);
|
|
PcodeOp *artificialHalt(const Address &addr,uint4 flag); ///< Create an artificial halt p-code op
|
|
void reinterpreted(const Address &addr); ///< Generate warning message or exception for a \e reinterpreted address
|
|
bool checkForFlowModification(FuncCallSpecs &fspecs);
|
|
void queryCall(FuncCallSpecs &fspecs); ///< Try to recover the Funcdata object corresponding to a given call
|
|
bool setupCallSpecs(PcodeOp *op,FuncCallSpecs *fc); ///< Set up the FuncCallSpecs object for a new call site
|
|
bool setupCallindSpecs(PcodeOp *op,FuncCallSpecs *fc);
|
|
void xrefInlinedBranch(PcodeOp *op); ///< Check for control-flow in a new injected p-code op
|
|
void doInjection(InjectPayload *payload,InjectContext &icontext,PcodeOp *op,FuncCallSpecs *fc);
|
|
void injectUserOp(PcodeOp *op); ///< Perform \e injection for a given user-defined p-code op
|
|
bool inlineSubFunction(FuncCallSpecs *fc); ///< In-line the sub-function at the given call site
|
|
bool injectSubFunction(FuncCallSpecs *fc); ///< Perform \e injection replacing the CALL at the given call site
|
|
void checkContainedCall(void);
|
|
void checkMultistageJumptables(void);
|
|
void recoverJumpTables(vector<JumpTable *> &newTables,vector<PcodeOp *> ¬reached);
|
|
void deleteCallSpec(FuncCallSpecs *fc); ///< Remove the given call site from the list for \b this function
|
|
void truncateIndirectJump(PcodeOp *op,JumpTable::RecoveryMode mode); ///< Treat indirect jump as CALLIND/RETURN
|
|
static bool isInArray(vector<PcodeOp *> &array,PcodeOp *op);
|
|
public:
|
|
FlowInfo(Funcdata &d,PcodeOpBank &o,BlockGraph &b,vector<FuncCallSpecs *> &q); ///< Constructor
|
|
FlowInfo(Funcdata &d,PcodeOpBank &o,BlockGraph &b,vector<FuncCallSpecs *> &q,const FlowInfo *op2); ///< Cloning constructor
|
|
void setRange(const Address &b,const Address &e) { baddr = b; eaddr = e; } ///< Establish the flow bounds
|
|
void setMaximumInstructions(uint4 max) { insn_max = max; } ///< Set the maximum number of instructions
|
|
void setFlags(uint4 val) { flags |= val; } ///< Enable a specific option
|
|
void clearFlags(uint4 val) { flags &= ~val; } ///< Disable a specific option
|
|
PcodeOp *target(const Address &addr) const; ///< Return first p-code op for instruction at given address
|
|
PcodeOp *branchTarget(PcodeOp *op) const; ///< Find the target referred to by a given BRANCH or CBRANCH
|
|
void updateTarget(PcodeOp *oldOp,PcodeOp *newOp); ///< Update the branch target for an inlined p-code op
|
|
void generateOps(void); ///< Generate raw control-flow from the function's base address
|
|
void generateBlocks(void); ///< Generate basic blocks from the raw control-flow
|
|
bool testHardInlineRestrictions(Funcdata *inlinefd,PcodeOp *op,Address &retaddr);
|
|
bool checkEZModel(void) const; ///< Check if \b this flow matches the EX in-lining model
|
|
void injectPcode(void); ///< Perform substitution on any op that requires \e injection
|
|
void forwardRecursion(const FlowInfo &op2); ///< Pull in-lining recursion information from another flow
|
|
void inlineClone(const FlowInfo &inlineflow,const Address &retaddr);
|
|
void inlineEZClone(const FlowInfo &inlineflow,const Address &calladdr);
|
|
int4 getSize(void) const { return (int4)(maxaddr.getOffset() - minaddr.getOffset()); } ///< Get the number of bytes covered by the flow
|
|
bool hasInject(void) const { return !injectlist.empty(); } ///< Does \b this flow have injections
|
|
bool hasUnimplemented(void) const { return ((flags & unimplemented_present)!=0); } ///< Does \b this flow have unimiplemented instructions
|
|
bool hasBadData(void) const { return ((flags & baddata_present)!=0); } ///< Does \b this flow reach inaccessible data
|
|
bool hasOutOfBounds(void) const { return ((flags & outofbounds_present)!=0); } ///< Does \b this flow out of bound
|
|
bool hasReinterpreted(void) const { return ((flags & reinterpreted_present)!=0); } ///< Does \b this flow reinterpret bytes
|
|
bool hasTooManyInstructions(void) const { return ((flags & toomanyinstructions_present)!=0); } ///< Does \b this flow have too many instructions
|
|
bool isFlowForInline(void) const { return ((flags & flow_forinline)!=0); } ///< Is \b this flow to be in-lined
|
|
bool doesJumpRecord(void) const { return ((flags & record_jumploads)!=0); } ///< Should jump table structure be recorded
|
|
};
|
|
|
|
} // End namespace ghidra
|
|
#endif
|