CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
213 lines
7.2 KiB
TeX
213 lines
7.2 KiB
TeX
\section{Examples}
|
|
\label{sec:examples}
|
|
We show several examples of how to use DataflowAPI. In these examples, we assume
|
|
that the mutatee has been parsed and we have function and block objects to analyze.
|
|
Users may refer to the ParseAPI manual for how to obtain these function and block objects.
|
|
|
|
\subsection{Slicing}
|
|
The following example uses DataflowAPI to perform a backward slice on an
|
|
indirect jump instruction to determine the instructions that affect the
|
|
calculation of the jump target. The goal of this example is to show (1) how to
|
|
convert an instruction to assignments; (2) how to perform slicing on a given
|
|
assignment; (3) how to extend the default \texttt{Slicer::Predicates} and write
|
|
call back functions to control the behavior of slicing.
|
|
|
|
\lstset{language=[GNU]C++,basicstyle=\fontfamily{fvm}\selectfont\small}
|
|
\lstset{numbers=left, numberstyle=\tiny, stepnumber=5, numbersep=5pt}
|
|
\lstset{showstringspaces=false}
|
|
\begin{lstlisting}
|
|
#include "Instruction.h"
|
|
#include "CFG.h"
|
|
#include "slicing.h"
|
|
|
|
using namespace Dyninst;
|
|
using namespace ParseAPI;
|
|
using namespace InstructionAPI;
|
|
using namespace DataflowAPI;
|
|
|
|
// We extend the default predicates to control when to stop slicing
|
|
class ConstantPred : public Slicer::Predicates {
|
|
public:
|
|
// We do not want to track through memory writes
|
|
virtual bool endAtPoint(Assignment::Ptr ap) {
|
|
return ap->insn().writesMemory();
|
|
}
|
|
|
|
// We can treat PC as a constant as its value is the address of the instruction
|
|
virtual bool addPredecessor(AbsRegion reg) {
|
|
if (reg.absloc().type() == Absloc::Register) {
|
|
MachRegister r = reg.absloc().reg();
|
|
return !r.isPC();
|
|
}
|
|
return true;
|
|
}
|
|
};
|
|
|
|
// Assume that block b in function f ends with an indirect jump.
|
|
void AnalyzeJumpTarget(Function *f, Block *b) {
|
|
// Get the last instruction in this block, which should be a jump
|
|
Instruction insn = b->getInsn(b->last());
|
|
|
|
// Convert the instruction to assignments
|
|
// The first parameter means to cache the conversion results.
|
|
// The second parameter means whether to use stack analysis to anlyze stack accesses.
|
|
AssignmentConverter ac(true, false);
|
|
vector<Assignment::Ptr> assignments;
|
|
ac.convert(insn, b->last(), f, b, assignments);
|
|
|
|
// An instruction can corresponds to multiple assignment.
|
|
// Here we look for the assignment that changes the PC.
|
|
Assignment::Ptr pcAssign;
|
|
for (auto ait = assignments.begin(); ait != assignments.end(); ++ait) {
|
|
const AbsRegion &out = (*ait)->out();
|
|
if (out.absloc().type() == Absloc::Register && out.absloc().reg().isPC()) {
|
|
pcAssign = *ait;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Create a Slicer that will start from the given assignment
|
|
Slicer s(pcAssign, b, f);
|
|
|
|
// We use the customized predicates to control slicing
|
|
ConstantPred mp;
|
|
GraphPtr slice = s.backwardSlice(mp);
|
|
}
|
|
\end{lstlisting}
|
|
|
|
|
|
\subsection{Symbolic Evaluation}
|
|
The following example shows how to expand a slice to ASTs and analyze an AST.
|
|
Suppose we have a slice representing the instructions that affect the jump
|
|
target of an indirect jump instruction.
|
|
We can get the expression of the jump targets and visit the expression to see if it
|
|
is a constant.
|
|
|
|
\lstset{language=[GNU]C++,basicstyle=\fontfamily{fvm}\selectfont\small}
|
|
\lstset{numbers=left, numberstyle=\tiny, stepnumber=5, numbersep=5pt}
|
|
\lstset{showstringspaces=false}
|
|
\begin{lstlisting}
|
|
#include "SymEval.h"
|
|
#include "slicing.h"
|
|
using namespace Dyninst;
|
|
using namespace DataflowAPI;
|
|
|
|
// We extend the default ASTVisitor to check whether the AST is a constant
|
|
class ConstVisitor: public ASTVisitor {
|
|
public:
|
|
bool resolved;
|
|
Address target;
|
|
ConstVisitor() : resolved(true), target(0){}
|
|
|
|
// We reach a constant node and record its value
|
|
virtual AST::Ptr visit(DataflowAPI::ConstantAST * ast) {
|
|
target = ast->val().val;
|
|
return AST::Ptr();
|
|
};
|
|
|
|
// If the AST contains a variable
|
|
// or an operation, then the control flow target cannot
|
|
// be resolved through constant propagation
|
|
virtual AST::Ptr visit(DataflowAPI::VariableAST *) {
|
|
resolved = false;
|
|
return AST::Ptr();
|
|
};
|
|
virtual AST::Ptr visit(DataflowAPI::RoseAST * ast) {
|
|
resolved = false;
|
|
|
|
// Recursively visit all children
|
|
unsigned totalChildren = ast->numChildren();
|
|
for (unsigned i = 0 ; i < totalChildren; ++i) {
|
|
ast->child(i)->accept(this);
|
|
}
|
|
return AST::Ptr();
|
|
};
|
|
};
|
|
|
|
Address ExpandSlice(GraphPtr slice, Assignment::Ptr pcAssign) {
|
|
Result_t symRet;
|
|
SymEval::expand(slice, symRet);
|
|
|
|
// We get AST representing the jump target
|
|
AST::Ptr pcExp = symRet[pcAssign];
|
|
|
|
// We analyze the AST to see if it can actually be resolved by constant propagation
|
|
ConstVisitor cv;
|
|
pcExp->accept(&cv);
|
|
if (cv.resolved) return cv.target;
|
|
return 0;
|
|
}
|
|
\end{lstlisting}
|
|
|
|
\subsection{Liveness Analysis}
|
|
The following example shows how to query for live registers.
|
|
|
|
\lstset{language=[GNU]C++,basicstyle=\fontfamily{fvm}\selectfont\small}
|
|
\lstset{numbers=left, numberstyle=\tiny, stepnumber=5, numbersep=5pt}
|
|
\lstset{showstringspaces=false}
|
|
\begin{lstlisting}
|
|
#include "Location.h"
|
|
#include "liveness.h"
|
|
#include "bitArray.h"
|
|
using namespace std;
|
|
using namespace Dyninst;
|
|
using namespace Dyninst::ParseAPI;
|
|
|
|
void LivenessAnalysis(Function *f, Block *b) {
|
|
// Construct a liveness analyzer based on the address width of the mutatee.
|
|
// 32-bit code and 64-bit code have different ABI.
|
|
LivenessAnalyzer la(f->obj()->cs()->getAddressWidth());
|
|
|
|
// Construct a liveness query location
|
|
Location loc(f, b);
|
|
|
|
// Query live registers at the block entry
|
|
bitArray liveEntry;
|
|
if (!la.query(loc, LivenessAnalyzer::Before, liveEntry)) {
|
|
printf("Cannot look up live registers at block entry\n");
|
|
}
|
|
|
|
printf("There are %d registers live at the block entry\n", liveEntry.count());
|
|
|
|
// Query live register at the block exit
|
|
bitArray liveExit;
|
|
if (!la.query(loc, LivenessAnalyzer::After, liveExit)) {
|
|
printf("Cannot look up live registers at block exit\n");
|
|
}
|
|
|
|
printf("rbx is live or not at the block exit: %d\n", liveExit.test(la.getIndex(x86_64::rbx)));
|
|
}
|
|
\end{lstlisting}
|
|
|
|
\subsection{Stack Analysis}
|
|
The following example shows how to use stack analysis to print out all defined stack heights at the first instruction in a block.
|
|
|
|
\lstset{language=[GNU]C++,basicstyle=\fontfamily{fvm}\selectfont\small}
|
|
\lstset{numbers=left, numberstyle=\tiny, stepnumber=5, numbersep=5pt}
|
|
\lstset{showstringspaces=false}
|
|
\begin{lstlisting}
|
|
#include "CFG.h"
|
|
#include "Absloc.h"
|
|
#include "stackanalysis.h"
|
|
using namespace Dyninst;
|
|
using namespace ParseAPI;
|
|
|
|
void StackHeight(Function *func, Block *block) {
|
|
// Get the address of the first instruction of the block
|
|
Address addr = block->start();
|
|
|
|
// Get the stack heights at that address
|
|
StackAnalysis sa(func);
|
|
std::vector<std::pair<Absloc, StackAnalysis::Height>> heights;
|
|
sa.findDefinedHeights(block, addr, heights);
|
|
|
|
// Print out the stack heights
|
|
for (auto iter = heights.begin(); iter != heights.end(); iter++) {
|
|
const Absloc &loc = iter->first;
|
|
const StackAnalysis::Height &height = iter->second;
|
|
printf("%s := %s\n", loc.format().c_str(), height.format().c_str());
|
|
}
|
|
}
|
|
\end{lstlisting}
|
|
|