Skip to content

Commit

Permalink
Add dataflowAPI/stackanalysis.C
Browse files Browse the repository at this point in the history
  • Loading branch information
hainest committed Apr 3, 2024
1 parent 2abc269 commit f7448ca
Show file tree
Hide file tree
Showing 4 changed files with 456 additions and 129 deletions.
40 changes: 4 additions & 36 deletions dataflowAPI/h/stackanalysis.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,6 @@
#endif
#endif

// These are _NOT_ in the Dyninst namespace...
namespace Dyninst {
namespace ParseAPI {
class Function;
Expand Down Expand Up @@ -96,10 +95,6 @@ class StackAnalysis {
Definition() : addr(0), block(NULL), type(TOP) {}

bool operator==(const Definition &other) const {
// FIXME: To pass checks in StackAnalysis::summarize(), we consider
// definitions equivalent as long as one is not BOTTOM and the other
// something else. This is not proper.
//return type == other.type && block == other.block;
if (type == BOTTOM && other.type != BOTTOM) return false;
if (type != BOTTOM && other.type == BOTTOM) return false;
return true;
Expand Down Expand Up @@ -129,7 +124,6 @@ class StackAnalysis {
}
};

// This class represents offsets on the stack, which we call heights.
class DATAFLOW_EXPORT Height {
public:
typedef signed long Height_t;
Expand All @@ -145,7 +139,6 @@ class StackAnalysis {

Height_t height() const { return height_; }

// FIXME if we stop using TOP == MAXINT and BOT == MININT...
bool operator<(const Height &rhs) const noexcept {
return (height_ < rhs.height_);
}
Expand Down Expand Up @@ -253,62 +246,47 @@ class StackAnalysis {
return defHeights == other.defHeights;
}

// Returns an iterator to the set of DefHeights
std::set<DefHeight>::iterator begin() {
return defHeights.begin();
}

// Returns a constant iterator to the set of DefHeights
std::set<DefHeight>::const_iterator begin() const {
return defHeights.begin();
}

// Returns an iterator to the end of the set of DefHeights
std::set<DefHeight>::iterator end() {
return defHeights.end();
}

// Returns a constant iterator to the end of the set of DefHeights
std::set<DefHeight>::const_iterator end() const {
return defHeights.end();
}

// Returns the size of this set
std::set<DefHeight>::size_type size() const {
return defHeights.size();
}

// Inserts a DefHeight into this set
void insert(const DefHeight &dh) {
defHeights.insert(dh);
}

// Returns true if this DefHeightSet is TOP
bool isTopSet() const;

// Returns true if this DefHeightSet is BOTTOM
bool isBottomSet() const;

// Sets this DefHeightSet to TOP
void makeTopSet();

// Sets this DefHeightSet to BOTTOM
void makeBottomSet();

// Populates this DefHeightSet with the corresponding information
void makeNewSet(ParseAPI::Block *b, Address addr,
const Absloc &origLoc, const Height &h);

// Adds to this DefHeightSet a new definition with height h
void addInitSet(const Height &h);

// Updates all Heights in this set by the delta amount
void addDeltaSet(long delta);

// Returns the result of computing a meet on all Heights in this set
Height getHeightSet() const;

// Returns the result of computing a meet on all Definitions in this set
Definition getDefSet() const;

private:
Expand Down Expand Up @@ -386,7 +364,6 @@ class StackAnalysis {
bool retop;
bool topBottom;

// Handle complex math from SIB functions
std::map<Absloc, std::pair<long, bool> > fromRegs;

private:
Expand Down Expand Up @@ -425,8 +402,6 @@ class StackAnalysis {
typedef std::map<ParseAPI::Block *, AbslocState> BlockState;
typedef std::map<ParseAPI::Block *, TransferSet> BlockSummaryState;

// To build intervals, we must replay the effect of each instruction.
// To avoid sucking enormous time, we keep those transfer functions around...
typedef std::map<ParseAPI::Block *, std::map<Offset, TransferFuncs> >
InstructionEffects;
typedef std::map<ParseAPI::Block *, std::map<Offset, TransferSet> >
Expand All @@ -449,7 +424,6 @@ class StackAnalysis {
DATAFLOW_EXPORT Height findFP(ParseAPI::Block *, Address addr);
DATAFLOW_EXPORT void findDefinedHeights(ParseAPI::Block* b, Address addr,
std::vector<std::pair<Absloc, Height> >& heights);
// TODO: Update DataflowAPI manual
DATAFLOW_EXPORT void findDefHeightPairs(ParseAPI::Block *b, Address addr,
std::vector<std::pair<Absloc, DefHeightSet> > &defHeights);

Expand Down Expand Up @@ -538,33 +512,27 @@ class StackAnalysis {

Height getStackCleanAmount(ParseAPI::Function *func);

// limit the number of definitions we track per register.
static const unsigned DEF_LIMIT = 2;

ParseAPI::Function *func;

// Map from call sites to PLT-resolved addresses
std::map<Address, Address> callResolutionMap;

// Function summaries to utilize during analysis
std::map<Address, TransferSet> functionSummaries;

std::set<Address> toppableFunctions;

// SP effect tracking
BlockEffects *blockEffects; // Pointer so we can make it an annotation
InstructionEffects *insnEffects; // Pointer so we can make it an annotation
CallEffects *callEffects; // Pointer so we can make it an annotation
BlockEffects *blockEffects;
InstructionEffects *insnEffects;
CallEffects *callEffects;

BlockState blockInputs;
BlockState blockOutputs;

// Like blockInputs and blockOutputs, but used for function summaries.
// Instead of tracking Heights, we track transfer functions.
BlockSummaryState blockSummaryInputs;
BlockSummaryState blockSummaryOutputs;

Intervals *intervals_; // Pointer so we can make it an annotation
Intervals *intervals_;

FuncCleanAmounts funcCleanAmounts;
int word_size;
Expand Down
56 changes: 2 additions & 54 deletions dataflowAPI/src/stackanalysis.C
Original file line number Diff line number Diff line change
Expand Up @@ -90,23 +90,6 @@ if(!(X)) { \
throw(stackanalysis_exception(#X));\
}

//
// Concepts:
//
// There are three terms we throw around:
//
// Stack height: the size of the stack; (cur_stack_ptr - func_start_stack_ptr)
// Stack delta: the difference in the size of the stack over the execution of
// a region of code (basic block here). (block_end_stack_ptr -
// block_start_stack_ptr)
// Stack clean: the amount the callee function shifts the stack. This is an x86
// idiom. On x86 the caller pushes arguments onto the stack (and thus shifts
// the stack). Normally the caller also cleans the stack (that is, removes
// arguments). However, some callee functions perform this cleaning
// themselves. We need to account for this in our analysis, which otherwise
// assumes that callee functions don't alter the stack.


bool StackAnalysis::analyze() {
genInsnEffects();

Expand Down Expand Up @@ -220,9 +203,6 @@ void add_target_exclude(std::stack<Block *> &workstack,
}
}

// We want to create a transfer function for the block as a whole. This will
// allow us to perform our fixpoint calculation over blocks (thus, O(B^2))
// rather than instructions (thus, O(I^2)).
void StackAnalysis::summarizeBlocks(bool verbose) {
intra_nosink_nocatch epred;
std::set<Block *> doneSet;
Expand Down Expand Up @@ -394,8 +374,7 @@ void getRetAndTailCallBlocks(Function *func, std::set<Block *> &retBlocks) {
} // namespace


// Looks for return edges in the function, following tail calls if necessary.
// Returns true if any return edges are found.

bool StackAnalysis::canGetFunctionSummary() {
std::set<Block *> retBlocks;
getRetAndTailCallBlocks(func, retBlocks);
Expand Down Expand Up @@ -1041,15 +1020,8 @@ std::ostream &operator<<(std::ostream &os,
return os;
}

///////////////////
// Insn effect fragments
///////////////////

// Visitor class to evaluate stack heights and PC-relative addresses
class StateEvalVisitor : public Visitor {
public:
// addr is the starting address of instruction insn.
// insn is the instruction containing the expression to evaluate.
StateEvalVisitor(Address addr, Instruction insn,
StackAnalysis::AbslocState *s) : defined(true), state(s) {
rip = addr + insn.size();
Expand Down Expand Up @@ -1129,8 +1101,6 @@ private:
StackAnalysis::AbslocState *state;
Address rip;

// Stack for calculations
// bool is true if the value in Address is a stack height
std::deque<std::pair<Address, bool> > results;

};
Expand Down Expand Up @@ -2317,8 +2287,6 @@ void StackAnalysis::handleSyscall(Instruction insn, Block *block,
}
}

// Handle instructions for which we have no special handling implemented. Be
// conservative for safety.
void StackAnalysis::handleDefault(Instruction insn, Block *block,
const Offset off, TransferFuncs &xferFuncs) {
// Form sets of read/written Abslocs
Expand Down Expand Up @@ -2629,7 +2597,6 @@ bool StackAnalysis::handleNormalCall(Instruction insn, Block *block,
return true;
}

// Create transfer functions for tail calls
bool StackAnalysis::handleJump(Instruction insn, Block *block, Offset off,
TransferFuncs &xferFuncs, TransferSet &funcSummary) {
Address calledAddr = 0;
Expand Down Expand Up @@ -2921,7 +2888,6 @@ void StackAnalysis::meetSummaryInputs(Block *block, TransferSet &blockInput,
}


// Keep track of up to DEF_LIMIT multiple definitions/heights, then bottom
StackAnalysis::DefHeightSet StackAnalysis::meetDefHeights(
const DefHeightSet &s1, const DefHeightSet &s2) {
DefHeightSet newSet;
Expand Down Expand Up @@ -3290,8 +3256,6 @@ bool StackAnalysis::DefHeightSet::isBottomSet() const {
}


// Destructive update of the input map. Assumes inputs are absolute,
// uninitialized, or bottom; no deltas.
StackAnalysis::DefHeightSet StackAnalysis::TransferFunc::apply(
const AbslocState &inputs) const {
STACKANALYSIS_ASSERT(target.isValid());
Expand Down Expand Up @@ -3390,7 +3354,6 @@ StackAnalysis::DefHeightSet StackAnalysis::TransferFunc::apply(
return inputSet;
}

// Returns accumulated transfer function without modifying inputs
StackAnalysis::TransferFunc StackAnalysis::TransferFunc::summaryAccumulate(
const TransferSet &inputs) const {
TransferFunc input;
Expand Down Expand Up @@ -3663,8 +3626,6 @@ StackAnalysis::TransferFunc StackAnalysis::TransferFunc::summaryAccumulate(
}


// Accumulation to the input map. This is intended to create a summary, so we
// create something that can take further input.
void StackAnalysis::TransferFunc::accumulate(TransferSet &inputs) {
inputs[target] = summaryAccumulate(inputs);
}
Expand Down Expand Up @@ -3774,7 +3735,6 @@ std::string StackAnalysis::format(const TransferSet &input) const {
}


// Converts a delta in a Result to a long
long StackAnalysis::extractDelta(Result deltaRes) {
long delta;
switch(deltaRes.size()) {
Expand Down Expand Up @@ -3818,10 +3778,6 @@ bool StackAnalysis::getSubReg(const MachRegister &reg, MachRegister &subreg) {
return true;
}

// If reg is an 8 byte register (rax, rbx, rcx, etc.) with a 4 byte subregister,
// retops the subregister. If reg is a 4 byte register (eax, ebx, ecx, etc.)
// with an 8 byte base register, retops the base register. The appropriate
// retop transfer function is added to xferFuncs.
void StackAnalysis::retopBaseSubReg(const MachRegister &reg,
TransferFuncs &xferFuncs) {
if (reg.size() == 4 && reg.getBaseRegister().size() == 8) {
Expand All @@ -3835,11 +3791,7 @@ void StackAnalysis::retopBaseSubReg(const MachRegister &reg,
}
}

// If reg is an 8 byte register (rax, rbx, rcx, etc.) with a 4 byte subregister,
// copies the subregister into the base register. If reg is a 4 byte register
// (eax, ebx, ecx, etc.) with an 8 byte base register, copies the base register
// into the subregister. The appropriate copy transfer function is added to
// xferFuncs.

void StackAnalysis::copyBaseSubReg(const MachRegister &reg,
TransferFuncs &xferFuncs) {
if (reg.size() == 4 && reg.getBaseRegister().size() == 8) {
Expand All @@ -3855,10 +3807,6 @@ void StackAnalysis::copyBaseSubReg(const MachRegister &reg,
}
}

// If reg is an 8 byte register (rax, rbx, rcx, etc.) with a 4 byte subregister,
// bottoms the subregister. If reg is a 4 byte register (eax, ebx, ecx, etc.)
// with an 8 byte base register, bottoms the base register. The appropriate
// bottom transfer function is added to xferFuncs.
void StackAnalysis::bottomBaseSubReg(const MachRegister &reg,
TransferFuncs &xferFuncs) {
if (reg.size() == 4 && reg.getBaseRegister().size() == 8) {
Expand Down

0 comments on commit f7448ca

Please sign in to comment.