mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-04 10:19:23 +02:00
Subvariable flow into switch
This commit is contained in:
parent
e6f09b141b
commit
936f541e64
7 changed files with 164 additions and 67 deletions
|
@ -484,6 +484,24 @@ inline uintb pcode_left(uintb val,int4 sa) {
|
||||||
return val << sa;
|
return val << sa;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// \brief Calculate smallest mask that covers the given value
|
||||||
|
///
|
||||||
|
/// Calculcate a mask that covers either the least significant byte, uint2, uint4, or uint8,
|
||||||
|
/// whatever is smallest.
|
||||||
|
/// \param val is the given value
|
||||||
|
/// \return the minimal mask
|
||||||
|
inline uintb minimalmask(uintb val)
|
||||||
|
|
||||||
|
{
|
||||||
|
if (val > 0xffffffff)
|
||||||
|
return ~((uintb)0);
|
||||||
|
if (val > 0xffff)
|
||||||
|
return 0xffffffff;
|
||||||
|
if (val > 0xff)
|
||||||
|
return 0xffff;
|
||||||
|
return 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
extern bool signbit_negative(uintb val,int4 size); ///< Return true if the sign-bit is set
|
extern bool signbit_negative(uintb val,int4 size); ///< Return true if the sign-bit is set
|
||||||
extern uintb calc_mask(int4 size); ///< Calculate a mask for a given byte size
|
extern uintb calc_mask(int4 size); ///< Calculate a mask for a given byte size
|
||||||
extern uintb uintb_negate(uintb in,int4 size); ///< Negate the \e sized value
|
extern uintb uintb_negate(uintb in,int4 size); ///< Negate the \e sized value
|
||||||
|
|
|
@ -3373,7 +3373,7 @@ void ActionDeadCode::markConsumedParameters(FuncCallSpecs *fc,vector<Varnode *>
|
||||||
if (vn->isAutoLive())
|
if (vn->isAutoLive())
|
||||||
consumeVal = ~((uintb)0);
|
consumeVal = ~((uintb)0);
|
||||||
else
|
else
|
||||||
consumeVal = minimalMask(vn->getNZMask());
|
consumeVal = minimalmask(vn->getNZMask());
|
||||||
pushConsumed(consumeVal,vn,worklist);
|
pushConsumed(consumeVal,vn,worklist);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3398,7 +3398,7 @@ uintb ActionDeadCode::gatherConsumedReturn(Funcdata &data)
|
||||||
if (returnOp->isDead()) continue;
|
if (returnOp->isDead()) continue;
|
||||||
if (returnOp->numInput() > 1) {
|
if (returnOp->numInput() > 1) {
|
||||||
Varnode *vn = returnOp->getIn(1);
|
Varnode *vn = returnOp->getIn(1);
|
||||||
consumeVal |= minimalMask(vn->getNZMask());
|
consumeVal |= minimalmask(vn->getNZMask());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return consumeVal;
|
return consumeVal;
|
||||||
|
@ -3455,11 +3455,21 @@ int4 ActionDeadCode::apply(Funcdata &data)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
else if (!op->isAssignment()) {
|
else if (!op->isAssignment()) {
|
||||||
if (op->code() == CPUI_RETURN) {
|
OpCode opc = op->code();
|
||||||
|
if (opc == CPUI_RETURN) {
|
||||||
pushConsumed(~((uintb)0),op->getIn(0),worklist);
|
pushConsumed(~((uintb)0),op->getIn(0),worklist);
|
||||||
for(i=1;i<op->numInput();++i)
|
for(i=1;i<op->numInput();++i)
|
||||||
pushConsumed(returnConsume,op->getIn(i),worklist);
|
pushConsumed(returnConsume,op->getIn(i),worklist);
|
||||||
}
|
}
|
||||||
|
else if (opc == CPUI_BRANCHIND) {
|
||||||
|
JumpTable *jt = data.findJumpTable(op);
|
||||||
|
uintb mask;
|
||||||
|
if (jt != (JumpTable *)0)
|
||||||
|
mask = jt->getSwitchVarConsume();
|
||||||
|
else
|
||||||
|
mask = ~((uintb)0);
|
||||||
|
pushConsumed(mask,op->getIn(0),worklist);
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
for(i=0;i<op->numInput();++i)
|
for(i=0;i<op->numInput();++i)
|
||||||
pushConsumed(~((uintb)0),op->getIn(i),worklist);
|
pushConsumed(~((uintb)0),op->getIn(i),worklist);
|
||||||
|
|
|
@ -543,7 +543,6 @@ class ActionDeadCode : public Action {
|
||||||
static bool neverConsumed(Varnode *vn,Funcdata &data);
|
static bool neverConsumed(Varnode *vn,Funcdata &data);
|
||||||
static void markConsumedParameters(FuncCallSpecs *fc,vector<Varnode *> &worklist);
|
static void markConsumedParameters(FuncCallSpecs *fc,vector<Varnode *> &worklist);
|
||||||
static uintb gatherConsumedReturn(Funcdata &data);
|
static uintb gatherConsumedReturn(Funcdata &data);
|
||||||
static uintb minimalMask(uintb val); ///< Calculate smallest mask that covers the given value
|
|
||||||
public:
|
public:
|
||||||
ActionDeadCode(const string &g) : Action(0,"deadcode",g) {} ///< Constructor
|
ActionDeadCode(const string &g) : Action(0,"deadcode",g) {} ///< Constructor
|
||||||
virtual Action *clone(const ActionGroupList &grouplist) const {
|
virtual Action *clone(const ActionGroupList &grouplist) const {
|
||||||
|
@ -1077,20 +1076,4 @@ public:
|
||||||
inline bool TermOrder::additiveCompare(const PcodeOpEdge *op1,const PcodeOpEdge *op2) {
|
inline bool TermOrder::additiveCompare(const PcodeOpEdge *op1,const PcodeOpEdge *op2) {
|
||||||
return (-1 == op1->getVarnode()->termOrder(op2->getVarnode())); }
|
return (-1 == op1->getVarnode()->termOrder(op2->getVarnode())); }
|
||||||
|
|
||||||
/// Calculcate a mask that covers either the least significant byte, uint2, uint4, or uint8,
|
|
||||||
/// whatever is smallest.
|
|
||||||
/// \param val is the given value
|
|
||||||
/// \return the minimal mask
|
|
||||||
inline uintb ActionDeadCode::minimalMask(uintb val)
|
|
||||||
|
|
||||||
{
|
|
||||||
if (val > 0xffffffff)
|
|
||||||
return ~((uintb)0);
|
|
||||||
if (val > 0xffff)
|
|
||||||
return 0xffffffff;
|
|
||||||
if (val > 0xff)
|
|
||||||
return 0xffff;
|
|
||||||
return 0xff;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1219,23 +1219,18 @@ void JumpBasic::buildLabels(Funcdata *fd,vector<Address> &addresstable,vector<ui
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void JumpBasic::foldInNormalization(Funcdata *fd,PcodeOp *indop)
|
Varnode *JumpBasic::foldInNormalization(Funcdata *fd,PcodeOp *indop)
|
||||||
|
|
||||||
{ // Assume normalized and unnormalized values are found
|
{
|
||||||
// Fold normalization pcode into indirect branch
|
// Set the BRANCHIND input to be the unnormalized switch variable, so
|
||||||
// Treat unnormalized value as input CPUI_BRANCHIND
|
// all the intervening code to calculate the final address is eliminated as dead.
|
||||||
// so it becomes literally the C switch statement
|
|
||||||
fd->opSetInput(indop,switchvn,0);
|
fd->opSetInput(indop,switchvn,0);
|
||||||
|
return switchvn;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool JumpBasic::foldInGuards(Funcdata *fd,JumpTable *jump)
|
bool JumpBasic::foldInGuards(Funcdata *fd,JumpTable *jump)
|
||||||
|
|
||||||
{ // We now think of the BRANCHIND as encompassing
|
{
|
||||||
// the guard function, so we "disarm" the guard
|
|
||||||
// instructions by making the guard condition
|
|
||||||
// always false. If the simplification removes
|
|
||||||
// the unusable branches, we are left with only
|
|
||||||
// one path through the switch
|
|
||||||
bool change = false;
|
bool change = false;
|
||||||
for(int4 i=0;i<selectguards.size();++i) {
|
for(int4 i=0;i<selectguards.size();++i) {
|
||||||
PcodeOp *cbranch = selectguards[i].getBranch();
|
PcodeOp *cbranch = selectguards[i].getBranch();
|
||||||
|
@ -1825,7 +1820,7 @@ void JumpAssisted::buildLabels(Funcdata *fd,vector<Address> &addresstable,vector
|
||||||
label.push_back(0xBAD1ABE1); // Add fake label to match the defaultAddress
|
label.push_back(0xBAD1ABE1); // Add fake label to match the defaultAddress
|
||||||
}
|
}
|
||||||
|
|
||||||
void JumpAssisted::foldInNormalization(Funcdata *fd,PcodeOp *indop)
|
Varnode *JumpAssisted::foldInNormalization(Funcdata *fd,PcodeOp *indop)
|
||||||
|
|
||||||
{
|
{
|
||||||
// Replace all outputs of jumpassist op with switchvn (including BRANCHIND)
|
// Replace all outputs of jumpassist op with switchvn (including BRANCHIND)
|
||||||
|
@ -1837,6 +1832,7 @@ void JumpAssisted::foldInNormalization(Funcdata *fd,PcodeOp *indop)
|
||||||
fd->opSetInput(op,switchvn,0);
|
fd->opSetInput(op,switchvn,0);
|
||||||
}
|
}
|
||||||
fd->opDestroy(assistOp); // Get rid of the assist op (it has served its purpose)
|
fd->opDestroy(assistOp); // Get rid of the assist op (it has served its purpose)
|
||||||
|
return switchvn;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool JumpAssisted::foldInGuards(Funcdata *fd,JumpTable *jump)
|
bool JumpAssisted::foldInGuards(Funcdata *fd,JumpTable *jump)
|
||||||
|
@ -1972,6 +1968,7 @@ JumpTable::JumpTable(Architecture *g,Address ad)
|
||||||
jmodel = (JumpModel *)0;
|
jmodel = (JumpModel *)0;
|
||||||
origmodel = (JumpModel *)0;
|
origmodel = (JumpModel *)0;
|
||||||
indirect = (PcodeOp *)0;
|
indirect = (PcodeOp *)0;
|
||||||
|
switchVarConsume = ~((uintb)0);
|
||||||
mostcommon = ~((uint4)0);
|
mostcommon = ~((uint4)0);
|
||||||
maxtablesize = 1024;
|
maxtablesize = 1024;
|
||||||
maxaddsub = 1;
|
maxaddsub = 1;
|
||||||
|
@ -1988,6 +1985,7 @@ JumpTable::JumpTable(const JumpTable *op2)
|
||||||
jmodel = (JumpModel *)0;
|
jmodel = (JumpModel *)0;
|
||||||
origmodel = (JumpModel *)0;
|
origmodel = (JumpModel *)0;
|
||||||
indirect = (PcodeOp *)0;
|
indirect = (PcodeOp *)0;
|
||||||
|
switchVarConsume = ~((uintb)0);
|
||||||
mostcommon = ~((uint4)0);
|
mostcommon = ~((uint4)0);
|
||||||
maxtablesize = op2->maxtablesize;
|
maxtablesize = op2->maxtablesize;
|
||||||
maxaddsub = op2->maxaddsub;
|
maxaddsub = op2->maxaddsub;
|
||||||
|
@ -2081,7 +2079,7 @@ void JumpTable::addBlockToSwitch(BlockBasic *bl,uintb lab)
|
||||||
|
|
||||||
void JumpTable::switchOver(const FlowInfo &flow)
|
void JumpTable::switchOver(const FlowInfo &flow)
|
||||||
|
|
||||||
{ // Convert absolute addresses to block indices
|
{
|
||||||
FlowBlock *parent,*tmpbl;
|
FlowBlock *parent,*tmpbl;
|
||||||
uint4 pos;
|
uint4 pos;
|
||||||
int4 i,j,count,maxcount;
|
int4 i,j,count,maxcount;
|
||||||
|
@ -2116,6 +2114,28 @@ void JumpTable::switchOver(const FlowInfo &flow)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Eliminate any code involved in actually computing the destination address so
|
||||||
|
/// it looks like the CPUI_BRANCHIND operation does it all internally.
|
||||||
|
/// \param fd is the function containing \b this switch
|
||||||
|
void JumpTable::foldInNormalization(Funcdata *fd)
|
||||||
|
|
||||||
|
{
|
||||||
|
Varnode *switchvn = jmodel->foldInNormalization(fd,indirect);
|
||||||
|
if (switchvn != (Varnode *)0) {
|
||||||
|
// If possible, mark up the switch variable as not fully consumed so that
|
||||||
|
// subvariable flow can truncate it.
|
||||||
|
switchVarConsume = minimalmask(switchvn->getNZMask());
|
||||||
|
if (switchVarConsume >= calc_mask(switchvn->getSize())) { // If mask covers everything
|
||||||
|
if (switchvn->isWritten()) {
|
||||||
|
PcodeOp *op = switchvn->getDef();
|
||||||
|
if (op->code() == CPUI_INT_SEXT) { // Check for a signed extension
|
||||||
|
switchVarConsume = calc_mask(op->getIn(0)->getSize()); // Assume the extension is not consumed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void JumpTable::trivialSwitchOver(void)
|
void JumpTable::trivialSwitchOver(void)
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -2259,6 +2279,7 @@ void JumpTable::clear(void)
|
||||||
label.clear();
|
label.clear();
|
||||||
loadpoints.clear();
|
loadpoints.clear();
|
||||||
indirect = (PcodeOp *)0;
|
indirect = (PcodeOp *)0;
|
||||||
|
switchVarConsume = ~((uintb)0);
|
||||||
recoverystage = 0;
|
recoverystage = 0;
|
||||||
// -opaddress- -maxtablesize- -maxaddsub- -maxleftright- -maxext- -collectloads- are permanent
|
// -opaddress- -maxtablesize- -maxaddsub- -maxleftright- -maxext- -collectloads- are permanent
|
||||||
}
|
}
|
||||||
|
|
|
@ -192,7 +192,22 @@ public:
|
||||||
virtual void buildAddresses(Funcdata *fd,PcodeOp *indop,vector<Address> &addresstable,vector<LoadTable> *loadpoints) const=0;
|
virtual void buildAddresses(Funcdata *fd,PcodeOp *indop,vector<Address> &addresstable,vector<LoadTable> *loadpoints) const=0;
|
||||||
virtual void findUnnormalized(uint4 maxaddsub,uint4 maxleftright,uint4 maxext)=0;
|
virtual void findUnnormalized(uint4 maxaddsub,uint4 maxleftright,uint4 maxext)=0;
|
||||||
virtual void buildLabels(Funcdata *fd,vector<Address> &addresstable,vector<uintb> &label,const JumpModel *orig) const=0;
|
virtual void buildLabels(Funcdata *fd,vector<Address> &addresstable,vector<uintb> &label,const JumpModel *orig) const=0;
|
||||||
virtual void foldInNormalization(Funcdata *fd,PcodeOp *indop)=0;
|
|
||||||
|
/// \brief Do normalization of the given switch specific to \b this model.
|
||||||
|
///
|
||||||
|
/// The PcodeOp machinery is removed so it looks like the CPUI_BRANCHIND simply takes the
|
||||||
|
/// switch variable as an input Varnode and automatically interprets its values to reach
|
||||||
|
/// the correct destination.
|
||||||
|
/// \param fd is the function containing the switch
|
||||||
|
/// \param indop is the given switch as a CPUI_BRANCHIND
|
||||||
|
/// \return the Varnode holding the final unnormalized switch variable
|
||||||
|
virtual Varnode *foldInNormalization(Funcdata *fd,PcodeOp *indop)=0;
|
||||||
|
|
||||||
|
/// \brief Eliminate any \e guard code involved in computing the switch destination
|
||||||
|
///
|
||||||
|
/// We now think of the BRANCHIND as encompassing any guard function.
|
||||||
|
/// \param fd is the function containing the switch
|
||||||
|
/// \param jump is the JumpTable owning \b this model.
|
||||||
virtual bool foldInGuards(Funcdata *fd,JumpTable *jump)=0;
|
virtual bool foldInGuards(Funcdata *fd,JumpTable *jump)=0;
|
||||||
virtual bool sanityCheck(Funcdata *fd,PcodeOp *indop,vector<Address> &addresstable)=0;
|
virtual bool sanityCheck(Funcdata *fd,PcodeOp *indop,vector<Address> &addresstable)=0;
|
||||||
virtual JumpModel *clone(JumpTable *jt) const=0;
|
virtual JumpModel *clone(JumpTable *jt) const=0;
|
||||||
|
@ -213,7 +228,7 @@ public:
|
||||||
virtual void buildAddresses(Funcdata *fd,PcodeOp *indop,vector<Address> &addresstable,vector<LoadTable> *loadpoints) const;
|
virtual void buildAddresses(Funcdata *fd,PcodeOp *indop,vector<Address> &addresstable,vector<LoadTable> *loadpoints) const;
|
||||||
virtual void findUnnormalized(uint4 maxaddsub,uint4 maxleftright,uint4 maxext) {}
|
virtual void findUnnormalized(uint4 maxaddsub,uint4 maxleftright,uint4 maxext) {}
|
||||||
virtual void buildLabels(Funcdata *fd,vector<Address> &addresstable,vector<uintb> &label,const JumpModel *orig) const;
|
virtual void buildLabels(Funcdata *fd,vector<Address> &addresstable,vector<uintb> &label,const JumpModel *orig) const;
|
||||||
virtual void foldInNormalization(Funcdata *fd,PcodeOp *indop) {}
|
virtual Varnode *foldInNormalization(Funcdata *fd,PcodeOp *indop) { return (Varnode *)0; }
|
||||||
virtual bool foldInGuards(Funcdata *fd,JumpTable *jump) { return false; }
|
virtual bool foldInGuards(Funcdata *fd,JumpTable *jump) { return false; }
|
||||||
virtual bool sanityCheck(Funcdata *fd,PcodeOp *indop,vector<Address> &addresstable) { return true; }
|
virtual bool sanityCheck(Funcdata *fd,PcodeOp *indop,vector<Address> &addresstable) { return true; }
|
||||||
virtual JumpModel *clone(JumpTable *jt) const;
|
virtual JumpModel *clone(JumpTable *jt) const;
|
||||||
|
@ -241,6 +256,16 @@ protected:
|
||||||
void findSmallestNormal(uint4 matchsize);
|
void findSmallestNormal(uint4 matchsize);
|
||||||
void findNormalized(Funcdata *fd,BlockBasic *rootbl,int4 pathout,uint4 matchsize,uint4 maxtablesize);
|
void findNormalized(Funcdata *fd,BlockBasic *rootbl,int4 pathout,uint4 matchsize,uint4 maxtablesize);
|
||||||
void markFoldableGuards();
|
void markFoldableGuards();
|
||||||
|
|
||||||
|
/// \brief Eliminate the given guard to \b this switch
|
||||||
|
///
|
||||||
|
/// We \e disarm the guard instructions by making the guard condition
|
||||||
|
/// always \b false. If the simplification removes the unusable branches,
|
||||||
|
/// we are left with only one path through the switch.
|
||||||
|
/// \param fd is the function containing the switch
|
||||||
|
/// \param guard is a description of the particular guard mechanism
|
||||||
|
/// \param jump is the JumpTable owning \b this model
|
||||||
|
/// \return \b true if a change was made to data-flow
|
||||||
virtual bool foldInOneGuard(Funcdata *fd,GuardRecord &guard,JumpTable *jump);
|
virtual bool foldInOneGuard(Funcdata *fd,GuardRecord &guard,JumpTable *jump);
|
||||||
public:
|
public:
|
||||||
JumpBasic(JumpTable *jt) : JumpModel(jt) { jrange = (JumpValuesRange *)0; }
|
JumpBasic(JumpTable *jt) : JumpModel(jt) { jrange = (JumpValuesRange *)0; }
|
||||||
|
@ -253,7 +278,7 @@ public:
|
||||||
virtual void buildAddresses(Funcdata *fd,PcodeOp *indop,vector<Address> &addresstable,vector<LoadTable> *loadpoints) const;
|
virtual void buildAddresses(Funcdata *fd,PcodeOp *indop,vector<Address> &addresstable,vector<LoadTable> *loadpoints) const;
|
||||||
virtual void findUnnormalized(uint4 maxaddsub,uint4 maxleftright,uint4 maxext);
|
virtual void findUnnormalized(uint4 maxaddsub,uint4 maxleftright,uint4 maxext);
|
||||||
virtual void buildLabels(Funcdata *fd,vector<Address> &addresstable,vector<uintb> &label,const JumpModel *orig) const;
|
virtual void buildLabels(Funcdata *fd,vector<Address> &addresstable,vector<uintb> &label,const JumpModel *orig) const;
|
||||||
virtual void foldInNormalization(Funcdata *fd,PcodeOp *indop);
|
virtual Varnode *foldInNormalization(Funcdata *fd,PcodeOp *indop);
|
||||||
virtual bool foldInGuards(Funcdata *fd,JumpTable *jump);
|
virtual bool foldInGuards(Funcdata *fd,JumpTable *jump);
|
||||||
virtual bool sanityCheck(Funcdata *fd,PcodeOp *indop,vector<Address> &addresstable);
|
virtual bool sanityCheck(Funcdata *fd,PcodeOp *indop,vector<Address> &addresstable);
|
||||||
virtual JumpModel *clone(JumpTable *jt) const;
|
virtual JumpModel *clone(JumpTable *jt) const;
|
||||||
|
@ -341,45 +366,54 @@ public:
|
||||||
virtual void buildAddresses(Funcdata *fd,PcodeOp *indop,vector<Address> &addresstable,vector<LoadTable> *loadpoints) const;
|
virtual void buildAddresses(Funcdata *fd,PcodeOp *indop,vector<Address> &addresstable,vector<LoadTable> *loadpoints) const;
|
||||||
virtual void findUnnormalized(uint4 maxaddsub,uint4 maxleftright,uint4 maxext) {}
|
virtual void findUnnormalized(uint4 maxaddsub,uint4 maxleftright,uint4 maxext) {}
|
||||||
virtual void buildLabels(Funcdata *fd,vector<Address> &addresstable,vector<uintb> &label,const JumpModel *orig) const;
|
virtual void buildLabels(Funcdata *fd,vector<Address> &addresstable,vector<uintb> &label,const JumpModel *orig) const;
|
||||||
virtual void foldInNormalization(Funcdata *fd,PcodeOp *indop);
|
virtual Varnode *foldInNormalization(Funcdata *fd,PcodeOp *indop);
|
||||||
virtual bool foldInGuards(Funcdata *fd,JumpTable *jump);
|
virtual bool foldInGuards(Funcdata *fd,JumpTable *jump);
|
||||||
virtual bool sanityCheck(Funcdata *fd,PcodeOp *indop,vector<Address> &addresstable) { return true; }
|
virtual bool sanityCheck(Funcdata *fd,PcodeOp *indop,vector<Address> &addresstable) { return true; }
|
||||||
virtual JumpModel *clone(JumpTable *jt) const;
|
virtual JumpModel *clone(JumpTable *jt) const;
|
||||||
virtual void clear(void) { assistOp = (PcodeOp *)0; switchvn = (Varnode *)0; }
|
virtual void clear(void) { assistOp = (PcodeOp *)0; switchvn = (Varnode *)0; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// \brief A map from values to control-flow targets within a function
|
||||||
|
///
|
||||||
|
/// A JumpTable is attached to a specific CPUI_BRANCHIND and encapsulates all
|
||||||
|
/// the information necessary to model the indirect jump as a \e switch statement.
|
||||||
|
/// It knows how to map from specific switch variable values to the destination
|
||||||
|
/// \e case block and how to label the value.
|
||||||
class JumpTable {
|
class JumpTable {
|
||||||
Architecture *glb; // Architecture under which this jumptable operates
|
Architecture *glb; ///< Architecture under which this jump-table operates
|
||||||
JumpModel *jmodel,*origmodel;
|
JumpModel *jmodel; ///< Current model of how the jump table is implemented in code
|
||||||
vector<Address> addresstable; // Raw addresses in the jumptable
|
JumpModel *origmodel; ///< Initial jump table model, which may be incomplete
|
||||||
vector<uint4> blocktable; // Addresses converted to basic blocks
|
vector<Address> addresstable; ///< Raw addresses in the jump-table
|
||||||
vector<uintb> label;
|
vector<uint4> blocktable; ///< Addresses converted to basic blocks
|
||||||
vector<LoadTable> loadpoints;
|
vector<uintb> label; ///< The case label for each explicit target
|
||||||
Address opaddress; // Absolute address of op
|
vector<LoadTable> loadpoints; ///< Any recovered in-memory data for the jump-table
|
||||||
PcodeOp *indirect; // INDIRECT op referring to this jump table
|
Address opaddress; ///< Absolute address of the INDIRECT jump
|
||||||
uint4 mostcommon; // Most common position in table
|
PcodeOp *indirect; ///< CPUI_INDIRECT op referring linked to \b this jump-table
|
||||||
uint4 maxtablesize; // Maximum table size we allow to be built (sanity check)
|
uintb switchVarConsume; ///< Bits of the switch variable being consumed
|
||||||
uint4 maxaddsub; // Maximum ADDs or SUBs to normalize
|
uint4 mostcommon; ///< Index of the most common position in table, prior to deduping
|
||||||
uint4 maxleftright; // Maximum shifts to normalize
|
uint4 maxtablesize; ///< Maximum table size we allow to be built (sanity check)
|
||||||
uint4 maxext; // Maximum extensions to normalize
|
uint4 maxaddsub; ///< Maximum ADDs or SUBs to normalize
|
||||||
int4 recoverystage; // 0=no stages, 1=needs additional stage, 2=complete
|
uint4 maxleftright; ///< Maximum shifts to normalize
|
||||||
bool collectloads;
|
uint4 maxext; ///< Maximum extensions to normalize
|
||||||
void recoverModel(Funcdata *fd);
|
int4 recoverystage; ///< 0=no stages, 1=needs additional stage, 2=complete
|
||||||
|
bool collectloads; ///< Set to \b true if information about in-memory model data is/should be collected
|
||||||
|
void recoverModel(Funcdata *fd); ///< Attempt recovery of the jump-table model
|
||||||
void trivialSwitchOver(void);
|
void trivialSwitchOver(void);
|
||||||
void sanityCheck(Funcdata *fd);
|
void sanityCheck(Funcdata *fd); ///< Perform sanity check on recovered address targets
|
||||||
uint4 block2Position(const FlowBlock *bl) const;
|
uint4 block2Position(const FlowBlock *bl) const;
|
||||||
static bool isReachable(PcodeOp *op);
|
static bool isReachable(PcodeOp *op);
|
||||||
public:
|
public:
|
||||||
JumpTable(Architecture *g,Address ad=Address());
|
JumpTable(Architecture *g,Address ad=Address()); ///< Constructor
|
||||||
JumpTable(const JumpTable *op2);
|
JumpTable(const JumpTable *op2); ///< Copy constructor
|
||||||
~JumpTable(void);
|
~JumpTable(void); ///< Destructor
|
||||||
bool isSwitchedOver(void) const { return !blocktable.empty(); }
|
bool isSwitchedOver(void) const { return !blocktable.empty(); } ///< Return \b true if addresses converted to basic-blocks
|
||||||
bool isRecovered(void) const { return !addresstable.empty(); }
|
bool isRecovered(void) const { return !addresstable.empty(); } ///< Return \b true if a model has been recovered
|
||||||
bool isLabelled(void) const { return !label.empty(); }
|
bool isLabelled(void) const { return !label.empty(); } ///< Return \b true if \e case labels are computed
|
||||||
bool isOverride(void) const;
|
bool isOverride(void) const; ///< Return \b true if \b this table was manually overridden
|
||||||
bool isPossibleMultistage(void) const { return (addresstable.size()==1); }
|
bool isPossibleMultistage(void) const { return (addresstable.size()==1); }
|
||||||
int4 getStage(void) const { return recoverystage; }
|
int4 getStage(void) const { return recoverystage; }
|
||||||
int4 numEntries(void) const { return addresstable.size(); }
|
int4 numEntries(void) const { return addresstable.size(); }
|
||||||
|
uintb getSwitchVarConsume(void) const { return switchVarConsume; } ///< Get bits of switch variable consumed by \b this table
|
||||||
int4 getMostCommon(void) const { return mostcommon; }
|
int4 getMostCommon(void) const { return mostcommon; }
|
||||||
const Address &getOpAddress(void) const { return opaddress; }
|
const Address &getOpAddress(void) const { return opaddress; }
|
||||||
PcodeOp *getIndirectOp(void) const { return indirect; }
|
PcodeOp *getIndirectOp(void) const { return indirect; }
|
||||||
|
@ -395,10 +429,10 @@ public:
|
||||||
void setMostCommonBlock(uint4 bl) { mostcommon = bl; }
|
void setMostCommonBlock(uint4 bl) { mostcommon = bl; }
|
||||||
void setLoadCollect(bool val) { collectloads = val; }
|
void setLoadCollect(bool val) { collectloads = val; }
|
||||||
void addBlockToSwitch(BlockBasic *bl,uintb lab);
|
void addBlockToSwitch(BlockBasic *bl,uintb lab);
|
||||||
void switchOver(const FlowInfo &flow);
|
void switchOver(const FlowInfo &flow); ///< Convert absolute addresses to block indices
|
||||||
uintb getLabelByIndex(int4 index) const { return label[index]; }
|
uintb getLabelByIndex(int4 index) const { return label[index]; } ///< Given a \e case index, get its label
|
||||||
void foldInNormalization(Funcdata *fd) { jmodel->foldInNormalization(fd,indirect); }
|
void foldInNormalization(Funcdata *fd); ///< Hide the normalization code for the switch
|
||||||
bool foldInGuards(Funcdata *fd) { return jmodel->foldInGuards(fd,this); }
|
bool foldInGuards(Funcdata *fd) { return jmodel->foldInGuards(fd,this); } ///< Hide any guard code for \b this switch
|
||||||
void recoverAddresses(Funcdata *fd);
|
void recoverAddresses(Funcdata *fd);
|
||||||
void recoverMultistage(Funcdata *fd);
|
void recoverMultistage(Funcdata *fd);
|
||||||
bool recoverLabels(Funcdata *fd);
|
bool recoverLabels(Funcdata *fd);
|
||||||
|
|
|
@ -340,6 +340,28 @@ bool SubvariableFlow::tryCallReturnPull(PcodeOp *op,ReplaceVarnode *rvn)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// \brief Determine if the subgraph variable can act as a switch variable for the given BRANCHIND
|
||||||
|
///
|
||||||
|
/// We query the JumpTable associated with the BRANCHIND to see if its switch variable
|
||||||
|
/// can be trimmed as indicated by the logical flow.
|
||||||
|
/// \param op is the given BRANCHIND op
|
||||||
|
/// \param rvn is the subgraph variable flowing to the BRANCHIND
|
||||||
|
/// \return \b true if the switch variable can be successfully trimmed to its logical size
|
||||||
|
bool SubvariableFlow::trySwitchPull(PcodeOp *op,ReplaceVarnode *rvn)
|
||||||
|
|
||||||
|
{
|
||||||
|
if ((rvn->mask & 1) == 0) return false; // Logical value must be justified
|
||||||
|
if ((rvn->vn->getConsume()&~rvn->mask)!=0) // If there's something outside the mask being consumed
|
||||||
|
return false; // we can't trim
|
||||||
|
patchlist.push_back(PatchRecord());
|
||||||
|
patchlist.back().type = 2;
|
||||||
|
patchlist.back().pullop = op;
|
||||||
|
patchlist.back().in1 = rvn;
|
||||||
|
patchlist.back().slot = 0;
|
||||||
|
pullcount += 1; // A true terminal modification
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/// Try to trace the logical variable through descendant Varnodes
|
/// Try to trace the logical variable through descendant Varnodes
|
||||||
/// creating new nodes in the logical subgraph and updating the worklist.
|
/// creating new nodes in the logical subgraph and updating the worklist.
|
||||||
/// \param rvn is the given subgraph variable to trace
|
/// \param rvn is the given subgraph variable to trace
|
||||||
|
@ -573,6 +595,10 @@ bool SubvariableFlow::traceForward(ReplaceVarnode *rvn)
|
||||||
if (!tryReturnPull(op,rvn,slot)) return false;
|
if (!tryReturnPull(op,rvn,slot)) return false;
|
||||||
hcount += 1;
|
hcount += 1;
|
||||||
break;
|
break;
|
||||||
|
case CPUI_BRANCHIND:
|
||||||
|
if (!trySwitchPull(op, rvn)) return false;
|
||||||
|
hcount += 1;
|
||||||
|
break;
|
||||||
case CPUI_BOOL_NEGATE:
|
case CPUI_BOOL_NEGATE:
|
||||||
case CPUI_BOOL_AND:
|
case CPUI_BOOL_AND:
|
||||||
case CPUI_BOOL_OR:
|
case CPUI_BOOL_OR:
|
||||||
|
@ -850,6 +876,10 @@ bool SubvariableFlow::traceForwardSext(ReplaceVarnode *rvn)
|
||||||
if (!tryReturnPull(op,rvn,slot)) return false;
|
if (!tryReturnPull(op,rvn,slot)) return false;
|
||||||
hcount += 1;
|
hcount += 1;
|
||||||
break;
|
break;
|
||||||
|
case CPUI_BRANCHIND:
|
||||||
|
if (!trySwitchPull(op,rvn)) return false;
|
||||||
|
hcount += 1;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -1331,7 +1361,7 @@ void SubvariableFlow::doReplacement(void)
|
||||||
fd->opSetInput(pullop,getReplaceVarnode((*piter).in1),0);
|
fd->opSetInput(pullop,getReplaceVarnode((*piter).in1),0);
|
||||||
fd->opSetInput(pullop,getReplaceVarnode((*piter).in2),1);
|
fd->opSetInput(pullop,getReplaceVarnode((*piter).in2),1);
|
||||||
}
|
}
|
||||||
else if (type == 2) { // A call parameter or return value
|
else if (type == 2) { // A call parameter, return value, or switch variable
|
||||||
fd->opSetInput(pullop,getReplaceVarnode((*piter).in1),(*piter).slot);
|
fd->opSetInput(pullop,getReplaceVarnode((*piter).in1),(*piter).slot);
|
||||||
}
|
}
|
||||||
else if (type == 3) {
|
else if (type == 3) {
|
||||||
|
|
|
@ -62,7 +62,7 @@ class SubvariableFlow {
|
||||||
/// \brief Operation with a new logical value as (part of) input, but output Varnode is unchanged
|
/// \brief Operation with a new logical value as (part of) input, but output Varnode is unchanged
|
||||||
class PatchRecord {
|
class PatchRecord {
|
||||||
friend class SubvariableFlow;
|
friend class SubvariableFlow;
|
||||||
int4 type; ///< 0=COPY 1=compare 2=call 3=AND/SHIFT
|
int4 type; ///< 0=COPY 1=compare 2=call/return/branchind 3=AND/SHIFT
|
||||||
PcodeOp *pullop; ///< Op being affected
|
PcodeOp *pullop; ///< Op being affected
|
||||||
ReplaceVarnode *in1; ///< The logical variable input
|
ReplaceVarnode *in1; ///< The logical variable input
|
||||||
ReplaceVarnode *in2; ///< (optional second parameter)
|
ReplaceVarnode *in2; ///< (optional second parameter)
|
||||||
|
@ -91,6 +91,7 @@ class SubvariableFlow {
|
||||||
bool tryCallPull(PcodeOp *op,ReplaceVarnode *rvn,int4 slot);
|
bool tryCallPull(PcodeOp *op,ReplaceVarnode *rvn,int4 slot);
|
||||||
bool tryReturnPull(PcodeOp *op,ReplaceVarnode *rvn,int4 slot);
|
bool tryReturnPull(PcodeOp *op,ReplaceVarnode *rvn,int4 slot);
|
||||||
bool tryCallReturnPull(PcodeOp *op,ReplaceVarnode *rvn);
|
bool tryCallReturnPull(PcodeOp *op,ReplaceVarnode *rvn);
|
||||||
|
bool trySwitchPull(PcodeOp *op,ReplaceVarnode *rvn);
|
||||||
bool traceForward(ReplaceVarnode *rvn); ///< Trace the logical data-flow forward for the given subgraph variable
|
bool traceForward(ReplaceVarnode *rvn); ///< Trace the logical data-flow forward for the given subgraph variable
|
||||||
bool traceBackward(ReplaceVarnode *rvn); ///< Trace the logical data-flow backward for the given subgraph variable
|
bool traceBackward(ReplaceVarnode *rvn); ///< Trace the logical data-flow backward for the given subgraph variable
|
||||||
bool traceForwardSext(ReplaceVarnode *rvn); ///< Trace logical data-flow forward assuming sign-extensions
|
bool traceForwardSext(ReplaceVarnode *rvn); ///< Trace logical data-flow forward assuming sign-extensions
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue