GP-2560 Don't reprocess Funcdata for multiple jumptables

This commit is contained in:
caheckman 2022-12-21 15:32:10 -05:00
parent ae6322ba36
commit 11abb716c4
4 changed files with 80 additions and 55 deletions

View file

@ -753,23 +753,16 @@ void FlowInfo::generateOps(void)
do {
bool collapsed_jumptable = false;
while(!tablelist.empty()) { // For each jumptable found
PcodeOp *op = tablelist.back();
tablelist.pop_back();
int4 failuremode;
JumpTable *jt = data.recoverJumpTable(op,this,failuremode); // Recover it
if (jt == (JumpTable *)0) { // Could not recover jumptable
if ((failuremode == 3) && (!tablelist.empty()) && (!isInArray(notreached,op))) {
// If the indirect op was not reachable with current flow AND there is more flow to generate,
// AND we haven't tried to recover this table before
notreached.push_back(op); // Save this op so we can try to recovery table again later
}
else if (!isFlowForInline()) // Unless this flow is being inlined for something else
truncateIndirectJump(op,failuremode); // Treat the indirect jump as a call
}
else {
vector<JumpTable *> newTables;
recoverJumpTables(newTables, notreached);
tablelist.clear();
for(int4 i=0;i<newTables.size();++i) {
JumpTable *jt = newTables[i];
if (jt == (JumpTable *)0) continue;
int4 num = jt->numEntries();
for(int4 i=0;i<num;++i)
newAddress(op,jt->getAddressByIndex(i));
newAddress(jt->getIndirectOp(),jt->getAddressByIndex(i));
if (jt->isPossibleMultistage())
collapsed_jumptable = true;
while(!addrlist.empty()) // Try to fill in as much more as possible
@ -1384,3 +1377,35 @@ void FlowInfo::checkMultistageJumptables(void)
tablelist.push_back(jt->getIndirectOp());
}
}
/// \brief Recover jumptables for current set of BRANCHIND ops using existing flow
///
/// \param newTables will hold one JumpTable pointer for each BRANCHIND in \b tablelist
void FlowInfo::recoverJumpTables(vector<JumpTable *> &newTables,vector<PcodeOp *> &notreached)
{
PcodeOp *op = tablelist[0];
ostringstream s1;
s1 << data.getName() << "@@jump@";
op->getAddr().printRaw(s1);
// Prepare partial Funcdata object for analysis if necessary
Funcdata partial(s1.str(),data.getScopeLocal()->getParent(),data.getAddress(),(FunctionSymbol *)0);
for(int4 i=0;i<tablelist.size();++i) {
op = tablelist[i];
int4 failuremode;
JumpTable *jt = data.recoverJumpTable(partial,op,this,failuremode); // Recover it
if (jt == (JumpTable *)0) { // Could not recover jumptable
if ((failuremode == 3) && (tablelist.size() > 1) && (!isInArray(notreached,op))) {
// If the indirect op was not reachable with current flow AND there is more flow to generate,
// AND we haven't tried to recover this table before
notreached.push_back(op); // Save this op so we can try to recovery table again later
}
else if (!isFlowForInline()) // Unless this flow is being inlined for something else
truncateIndirectJump(op,failuremode); // Treat the indirect jump as a call
}
newTables.push_back(jt);
}
}

View file

@ -133,6 +133,7 @@ private:
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 *> &notreached);
void deleteCallSpec(FuncCallSpecs *fc); ///< Remove the given call site from the list for \b this function
void truncateIndirectJump(PcodeOp *op,int4 failuremode); ///< Treat indirect jump as indirect call that never returns
static bool isInArray(vector<PcodeOp *> &array,PcodeOp *op);

View file

@ -115,7 +115,7 @@ class Funcdata {
void pushMultiequals(BlockBasic *bb); ///< Push MULTIEQUAL Varnodes of the given block into the output block
void clearBlocks(void); ///< Clear all basic blocks
void structureReset(void); ///< Calculate initial basic block structures (after a control-flow change)
int4 stageJumpTable(JumpTable *jt,PcodeOp *op,FlowInfo *flow);
int4 stageJumpTable(Funcdata &partial,JumpTable *jt,PcodeOp *op,FlowInfo *flow);
void switchOverJumpTables(const FlowInfo &flow); ///< Convert jump-table addresses to basic block indices
void clearJumpTables(void); ///< Clear any jump-table information
@ -516,7 +516,7 @@ public:
JumpTable *linkJumpTable(PcodeOp *op); ///< Link jump-table with a given BRANCHIND
JumpTable *findJumpTable(const PcodeOp *op) const; ///< Find a jump-table associated with a given BRANCHIND
JumpTable *installJumpTable(const Address &addr); ///< Install a new jump-table for the given Address
JumpTable *recoverJumpTable(PcodeOp *op,FlowInfo *flow,int4 &failuremode);
JumpTable *recoverJumpTable(Funcdata &partial,PcodeOp *op,FlowInfo *flow,int4 &failuremode);
int4 numJumpTables(void) const { return jumpvec.size(); } ///< Get the number of jump-tables for \b this function
JumpTable *getJumpTable(int4 i) { return jumpvec[i]; } ///< Get the i-th jump-table
void removeJumpTable(JumpTable *jt); ///< Remove/delete the given jump-table

View file

@ -481,34 +481,22 @@ JumpTable *Funcdata::installJumpTable(const Address &addr)
/// - 2 = \b likely \b thunk failure
/// - 3 = no legal flows to the BRANCHIND failure
///
/// \param partial is a function object for caching analysis
/// \param jt is the jump-table object to populate
/// \param op is the BRANCHIND p-code op to analyze
/// \param flow is the existing flow information
/// \return the success/failure code
int4 Funcdata::stageJumpTable(JumpTable *jt,PcodeOp *op,FlowInfo *flow)
int4 Funcdata::stageJumpTable(Funcdata &partial,JumpTable *jt,PcodeOp *op,FlowInfo *flow)
{
PcodeOp *partop = (PcodeOp *)0;
string oldactname;
ostringstream s1;
s1 << name << "@@jump@";
op->getAddr().printRaw(s1);
Funcdata partial(s1.str(),localmap->getParent(),baseaddr,(FunctionSymbol *)0);
if (!partial.isJumptableRecoveryOn()) {
// Do full analysis on the table if we haven't before
partial.flags |= jumptablerecovery_on; // Mark that this Funcdata object is dedicated to jumptable recovery
partial.truncatedFlow(this,flow);
partop = partial.findOp(op->getSeqNum());
if ((partop==(PcodeOp *)0) ||
(partop->code() != CPUI_BRANCHIND)||
(partop->getAddr() != op->getAddr()))
throw LowlevelError("Error recovering jumptable: Bad partial clone");
oldactname = glb->allacts.getCurrentName(); // Save off old action
glb->allacts.setCurrent("jumptable");
string oldactname = glb->allacts.getCurrentName(); // Save off old action
try {
glb->allacts.setCurrent("jumptable");
#ifdef OPACTION_DEBUG
if (jtcallback != (void (*)(Funcdata &orig,Funcdata &fd))0)
(*jtcallback)(*this,partial); // Alternative reset/perform
@ -520,8 +508,21 @@ int4 Funcdata::stageJumpTable(JumpTable *jt,PcodeOp *op,FlowInfo *flow)
}
#endif
glb->allacts.setCurrent(oldactname); // Restore old action
}
catch(LowlevelError &err) {
glb->allacts.setCurrent(oldactname);
warning(err.explain,op->getAddr());
return 1;
}
}
PcodeOp *partop = partial.findOp(op->getSeqNum());
if (partop==(PcodeOp *)0 || partop->code() != CPUI_BRANCHIND || partop->getAddr() != op->getAddr())
throw LowlevelError("Error recovering jumptable: Bad partial clone");
if (partop->isDead()) // Indirectop we were trying to recover was eliminated as dead code (unreachable)
return 0; // Return jumptable as
try {
jt->setLoadCollect(flow->doesJumpRecord());
jt->setIndirectOp(partop);
if (jt->getStage()>0)
@ -529,16 +530,13 @@ int4 Funcdata::stageJumpTable(JumpTable *jt,PcodeOp *op,FlowInfo *flow)
else
jt->recoverAddresses(&partial); // Analyze partial to recover jumptable addresses
}
catch(JumptableNotReachableError &err) {
glb->allacts.setCurrent(oldactname);
catch(JumptableNotReachableError &err) { // Thrown by recoverAddresses
return 3;
}
catch(JumptableThunkError &err) {
glb->allacts.setCurrent(oldactname);
catch(JumptableThunkError &err) { // Thrown by recoverAddresses
return 2;
}
catch(LowlevelError &err) {
glb->allacts.setCurrent(oldactname);
warning(err.explain,op->getAddr());
return 1;
}
@ -552,11 +550,12 @@ int4 Funcdata::stageJumpTable(JumpTable *jt,PcodeOp *op,FlowInfo *flow)
/// to the copy, and the resulting data-flow tree is examined to enumerate possible values
/// of the input Varnode to the given BRANCHIND PcodeOp. This information is stored in a
/// JumpTable object.
/// \param partial is the Funcdata copy to perform analysis on if necessary
/// \param op is the given BRANCHIND PcodeOp
/// \param flow is current flow information for \b this function
/// \param failuremode will hold the final success/failure code (0=success)
/// \return the recovered JumpTable or NULL if there was no success
JumpTable *Funcdata::recoverJumpTable(PcodeOp *op,FlowInfo *flow,int4 &failuremode)
JumpTable *Funcdata::recoverJumpTable(Funcdata &partial,PcodeOp *op,FlowInfo *flow,int4 &failuremode)
{
JumpTable *jt;
@ -568,7 +567,7 @@ JumpTable *Funcdata::recoverJumpTable(PcodeOp *op,FlowInfo *flow,int4 &failuremo
if (jt->getStage() != 1)
return jt; // Previously calculated jumptable (NOT an override and NOT incomplete)
}
failuremode = stageJumpTable(jt,op,flow); // Recover based on override information
failuremode = stageJumpTable(partial,jt,op,flow); // Recover based on override information
if (failuremode != 0)
return (JumpTable *)0;
jt->setIndirectOp(op); // Relink table back to original op
@ -578,7 +577,7 @@ JumpTable *Funcdata::recoverJumpTable(PcodeOp *op,FlowInfo *flow,int4 &failuremo
if ((flags & jumptablerecovery_dont)!=0)
return (JumpTable *)0; // Explicitly told not to recover jumptables
JumpTable trialjt(glb);
failuremode = stageJumpTable(&trialjt,op,flow);
failuremode = stageJumpTable(partial,&trialjt,op,flow);
if (failuremode != 0)
return (JumpTable *)0;
// if (trialjt.is_twostage())