mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-04 10:19:23 +02:00
GP-2560 Don't reprocess Funcdata for multiple jumptables
This commit is contained in:
parent
ae6322ba36
commit
11abb716c4
4 changed files with 80 additions and 55 deletions
|
@ -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 *> ¬reached)
|
||||
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 *> ¬reached);
|
||||
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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -481,47 +481,48 @@ 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;
|
||||
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);
|
||||
|
||||
ostringstream s1;
|
||||
s1 << name << "@@jump@";
|
||||
op->getAddr().printRaw(s1);
|
||||
|
||||
Funcdata partial(s1.str(),localmap->getParent(),baseaddr,(FunctionSymbol *)0);
|
||||
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");
|
||||
try {
|
||||
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
|
||||
else {
|
||||
if (jtcallback != (void (*)(Funcdata &orig,Funcdata &fd))0)
|
||||
(*jtcallback)(*this,partial); // Alternative reset/perform
|
||||
else {
|
||||
#endif
|
||||
glb->allacts.getCurrent()->reset( partial );
|
||||
glb->allacts.getCurrent()->perform( partial ); // Simplify the partial function
|
||||
glb->allacts.getCurrent()->reset( partial );
|
||||
glb->allacts.getCurrent()->perform( partial ); // Simplify the partial function
|
||||
#ifdef OPACTION_DEBUG
|
||||
}
|
||||
#endif
|
||||
glb->allacts.setCurrent(oldactname); // Restore old action
|
||||
}
|
||||
#endif
|
||||
glb->allacts.setCurrent(oldactname); // Restore old action
|
||||
if (partop->isDead()) // Indirectop we were trying to recover was eliminated as dead code (unreachable)
|
||||
return 0; // Return jumptable as
|
||||
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())
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue