mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-04 02:09:44 +02:00
GP-4907 Multistage jumptable adjustment
This commit is contained in:
parent
86e77bd9ef
commit
691137abc1
8 changed files with 198 additions and 103 deletions
|
@ -4410,12 +4410,8 @@ int4 ActionSwitchNorm::apply(Funcdata &data)
|
|||
for(int4 i=0;i<data.numJumpTables();++i) {
|
||||
JumpTable *jt = data.getJumpTable(i);
|
||||
if (!jt->isLabelled()) {
|
||||
if (jt->recoverLabels(&data)) { // Recover case statement labels
|
||||
// If this returns true, the jumptable was not fully recovered during flow analysis
|
||||
// So we need to issue a restart
|
||||
data.getOverride().insertMultistageJump(jt->getOpAddress());
|
||||
data.setRestartPending(true);
|
||||
}
|
||||
jt->matchModel(&data);
|
||||
jt->recoverLabels(&data); // Recover case statement labels
|
||||
jt->foldInNormalization(&data);
|
||||
count += 1;
|
||||
}
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
@ -781,7 +781,6 @@ void FlowInfo::generateOps(void)
|
|||
if (hasInject())
|
||||
injectPcode();
|
||||
do {
|
||||
bool collapsed_jumptable = false;
|
||||
while(!tablelist.empty()) { // For each jumptable found
|
||||
vector<JumpTable *> newTables;
|
||||
recoverJumpTables(newTables, notreached);
|
||||
|
@ -793,16 +792,13 @@ void FlowInfo::generateOps(void)
|
|||
int4 num = jt->numEntries();
|
||||
for(int4 i=0;i<num;++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
|
||||
fallthru();
|
||||
}
|
||||
}
|
||||
|
||||
checkContainedCall(); // Check for PIC constructions
|
||||
if (collapsed_jumptable)
|
||||
checkMultistageJumptables();
|
||||
checkMultistageJumptables();
|
||||
while(notreachcnt < notreached.size()) {
|
||||
tablelist.push_back(notreached[notreachcnt]);
|
||||
notreachcnt += 1;
|
||||
|
@ -1435,14 +1431,18 @@ void FlowInfo::recoverJumpTables(vector<JumpTable *> &newTables,vector<PcodeOp *
|
|||
JumpTable::RecoveryMode mode;
|
||||
JumpTable *jt = data.recoverJumpTable(partial,op,this,mode); // Recover it
|
||||
if (jt == (JumpTable *)0) { // Could not recover jumptable
|
||||
if ((mode == JumpTable::fail_noflow) && (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
|
||||
if (!isFlowForInline()) // Unless this flow is being inlined for something else
|
||||
truncateIndirectJump(op,mode); // Treat the indirect jump as a call
|
||||
}
|
||||
else if (jt->isPartial()) {
|
||||
if (tablelist.size() > 1 && !isInArray(notreached,op)) {
|
||||
// If the recovery is incomplete 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 recover the table again later
|
||||
}
|
||||
else
|
||||
jt->markComplete(); // If we aren't revisiting, mark the table as complete
|
||||
}
|
||||
newTables.push_back(jt);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
@ -528,14 +528,11 @@ JumpTable::RecoveryMode Funcdata::stageJumpTable(Funcdata &partial,JumpTable *jt
|
|||
try {
|
||||
jt->setLoadCollect(flow->doesJumpRecord());
|
||||
jt->setIndirectOp(partop);
|
||||
if (jt->getStage()>0)
|
||||
if (jt->isPartial())
|
||||
jt->recoverMultistage(&partial);
|
||||
else
|
||||
jt->recoverAddresses(&partial); // Analyze partial to recover jumptable addresses
|
||||
}
|
||||
catch(JumptableNotReachableError &err) { // Thrown by recoverAddresses
|
||||
return JumpTable::fail_noflow;
|
||||
}
|
||||
catch(JumptableThunkError &err) { // Thrown by recoverAddresses
|
||||
return JumpTable::fail_thunk;
|
||||
}
|
||||
|
@ -645,7 +642,7 @@ JumpTable *Funcdata::recoverJumpTable(Funcdata &partial,PcodeOp *op,FlowInfo *fl
|
|||
jt = linkJumpTable(op); // Search for pre-existing jumptable
|
||||
if (jt != (JumpTable *)0) {
|
||||
if (!jt->isOverride()) {
|
||||
if (jt->getStage() != 1)
|
||||
if (!jt->isPartial())
|
||||
return jt; // Previously calculated jumptable (NOT an override and NOT incomplete)
|
||||
}
|
||||
mode = stageJumpTable(partial,jt,op,flow); // Recover based on override information
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
@ -1049,7 +1049,7 @@ void JumpBasic::analyzeGuards(BlockBasic *bl,int4 pathout)
|
|||
int4 i,j,indpath;
|
||||
int4 maxbranch = 2; // Maximum number of CBRANCHs to consider
|
||||
int4 maxpullback = 2;
|
||||
bool usenzmask = (jumptable->getStage() == 0);
|
||||
bool usenzmask = !jumptable->isPartial();
|
||||
|
||||
selectguards.clear();
|
||||
BlockBasic *prevbl;
|
||||
|
@ -2205,6 +2205,33 @@ JumpModel *JumpAssisted::clone(JumpTable *jt) const
|
|||
return clone;
|
||||
}
|
||||
|
||||
void JumpTable::saveModel(void)
|
||||
|
||||
{
|
||||
if (origmodel != (JumpModel *)0)
|
||||
delete origmodel;
|
||||
origmodel = jmodel;
|
||||
jmodel = (JumpModel *)0;
|
||||
}
|
||||
|
||||
void JumpTable::restoreSavedModel(void)
|
||||
|
||||
{
|
||||
if (jmodel != (JumpModel *)0)
|
||||
delete jmodel;
|
||||
jmodel = origmodel;
|
||||
origmodel = (JumpModel *)0;
|
||||
}
|
||||
|
||||
void JumpTable::clearSavedModel(void)
|
||||
|
||||
{
|
||||
if (origmodel != (JumpModel *)0) {
|
||||
delete origmodel;
|
||||
origmodel = (JumpModel *)0;
|
||||
}
|
||||
}
|
||||
|
||||
/// Try to recover each model in turn, until we find one that matches the specific BRANCHIND.
|
||||
/// \param fd is the function containing the switch
|
||||
void JumpTable::recoverModel(Funcdata *fd)
|
||||
|
@ -2252,12 +2279,12 @@ void JumpTable::sanityCheck(Funcdata *fd,vector<int4> *loadcounts)
|
|||
|
||||
{
|
||||
if (jmodel->isOverride())
|
||||
return; // Don't perform sanity check on an override
|
||||
return; // Don't perform sanity check on an override
|
||||
uint4 sz = addresstable.size();
|
||||
|
||||
if (!isReachable(indirect))
|
||||
throw JumptableNotReachableError("No legal flow");
|
||||
if (addresstable.size() == 1) { // One entry is likely some kind of thunk
|
||||
partialTable = true; // If the jumptable is not reachable, mark as incomplete
|
||||
if (addresstable.size() == 1) { // One entry is likely some kind of thunk
|
||||
bool isthunk = false;
|
||||
uintb diff;
|
||||
Address addr = addresstable[0];
|
||||
|
@ -2346,7 +2373,7 @@ JumpTable::JumpTable(Architecture *g,Address ad)
|
|||
maxaddsub = 1;
|
||||
maxleftright = 1;
|
||||
maxext = 1;
|
||||
recoverystage = 0;
|
||||
partialTable = false;
|
||||
collectloads = false;
|
||||
defaultIsFolded = false;
|
||||
}
|
||||
|
@ -2367,7 +2394,7 @@ JumpTable::JumpTable(const JumpTable *op2)
|
|||
maxaddsub = op2->maxaddsub;
|
||||
maxleftright = op2->maxleftright;
|
||||
maxext = op2->maxext;
|
||||
recoverystage = op2->recoverystage;
|
||||
partialTable = op2->partialTable;
|
||||
collectloads = op2->collectloads;
|
||||
defaultIsFolded = false;
|
||||
// We just clone the addresses themselves
|
||||
|
@ -2587,8 +2614,8 @@ void JumpTable::recoverAddresses(Funcdata *fd)
|
|||
}
|
||||
if (jmodel->getTableSize() == 0) {
|
||||
ostringstream err;
|
||||
err << "Impossible to reach jumptable at " << opaddress;
|
||||
throw JumptableNotReachableError(err.str());
|
||||
err << "Jumptable with 0 entries at " << opaddress;
|
||||
throw LowlevelError(err.str());
|
||||
}
|
||||
// if (sz < 2)
|
||||
// fd->warning("Jumptable has only one branch",opaddress);
|
||||
|
@ -2609,10 +2636,7 @@ void JumpTable::recoverAddresses(Funcdata *fd)
|
|||
void JumpTable::recoverMultistage(Funcdata *fd)
|
||||
|
||||
{
|
||||
if (origmodel != (JumpModel *)0)
|
||||
delete origmodel;
|
||||
origmodel = jmodel;
|
||||
jmodel = (JumpModel *)0;
|
||||
saveModel();
|
||||
|
||||
vector<Address> oldaddresstable = addresstable;
|
||||
addresstable.clear();
|
||||
|
@ -2621,36 +2645,25 @@ void JumpTable::recoverMultistage(Funcdata *fd)
|
|||
recoverAddresses(fd);
|
||||
}
|
||||
catch(JumptableThunkError &err) {
|
||||
if (jmodel != (JumpModel *)0)
|
||||
delete jmodel;
|
||||
jmodel = origmodel;
|
||||
origmodel = (JumpModel *)0;
|
||||
restoreSavedModel();
|
||||
addresstable = oldaddresstable;
|
||||
fd->warning("Second-stage recovery error",indirect->getAddr());
|
||||
}
|
||||
catch(LowlevelError &err) {
|
||||
if (jmodel != (JumpModel *)0)
|
||||
delete jmodel;
|
||||
jmodel = origmodel;
|
||||
origmodel = (JumpModel *)0;
|
||||
restoreSavedModel();
|
||||
addresstable = oldaddresstable;
|
||||
fd->warning("Second-stage recovery error",indirect->getAddr());
|
||||
}
|
||||
recoverystage = 2;
|
||||
if (origmodel != (JumpModel *)0) { // Keep the new model if it was created successfully
|
||||
delete origmodel;
|
||||
origmodel = (JumpModel *)0;
|
||||
}
|
||||
partialTable = false;
|
||||
clearSavedModel(); // Keep the new model if it was created successfully
|
||||
}
|
||||
|
||||
/// This is run assuming the address table has already been recovered, via recoverAddresses() in another
|
||||
/// Funcdata instance. So recoverModel() needs to be rerun on the instance passed in here.
|
||||
///
|
||||
/// The unnormalized switch variable is recovered, and for each possible address table entry, the variable
|
||||
/// value that produces it is calculated and stored as the formal \e case label for the associated code block.
|
||||
/// \param fd is the (final instance of the) function containing the switch
|
||||
/// \return \b true if it looks like a multi-stage restart is needed.
|
||||
bool JumpTable::recoverLabels(Funcdata *fd)
|
||||
/// This assumes the address table has already been recovered, via recoverAddresses() in another
|
||||
/// Funcdata instance. We rerun recoverModel() to match with the current Funcdata. If the recovered model
|
||||
/// does not match the original address table size, we may be missing control-flow. In this case,
|
||||
/// if it looks like we have a \e multistage jumptable, we generate a multistage restart, otherwise
|
||||
/// we generate a warning of the mismatch.
|
||||
void JumpTable::matchModel(Funcdata *fd)
|
||||
|
||||
{
|
||||
if (!isRecovered())
|
||||
|
@ -2658,24 +2671,33 @@ bool JumpTable::recoverLabels(Funcdata *fd)
|
|||
|
||||
// Unless the model is an override, move model (created on a flow copy) so we can create a current instance
|
||||
if (jmodel != (JumpModel *)0) {
|
||||
if (origmodel != (JumpModel *)0)
|
||||
delete origmodel;
|
||||
if (!jmodel->isOverride()) {
|
||||
origmodel = jmodel;
|
||||
jmodel = (JumpModel *)0;
|
||||
}
|
||||
else
|
||||
if (!jmodel->isOverride())
|
||||
saveModel();
|
||||
else {
|
||||
clearSavedModel();
|
||||
fd->warning("Switch is manually overridden",opaddress);
|
||||
}
|
||||
|
||||
bool multistagerestart = false;
|
||||
recoverModel(fd); // Create a current instance of the model
|
||||
if (jmodel != (JumpModel *)0) {
|
||||
if (jmodel->getTableSize() != addresstable.size()) {
|
||||
fd->warning("Could not find normalized switch variable to match jumptable",opaddress);
|
||||
if ((addresstable.size()==1)&&(jmodel->getTableSize() > 1))
|
||||
multistagerestart = true;
|
||||
}
|
||||
}
|
||||
recoverModel(fd); // Create a current instance of the model
|
||||
if (jmodel != (JumpModel *)0 && jmodel->getTableSize() != addresstable.size()) {
|
||||
if ((addresstable.size()==1)&&(jmodel->getTableSize() > 1)) {
|
||||
// The jumptable was not fully recovered during flow analysis, try to issue a restart
|
||||
fd->getOverride().insertMultistageJump(opaddress);
|
||||
fd->setRestartPending(true);
|
||||
return;
|
||||
}
|
||||
fd->warning("Could not find normalized switch variable to match jumptable",opaddress);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// The unnormalized switch variable is recovered, and for each possible address table entry, the variable
|
||||
/// value that produces it is calculated and stored as the formal \e case label for the associated code block.
|
||||
/// \param fd is the (final instance of the) function containing the switch
|
||||
void JumpTable::recoverLabels(Funcdata *fd)
|
||||
|
||||
{
|
||||
if (jmodel != (JumpModel *)0) {
|
||||
if ((origmodel == (JumpModel *)0)||(origmodel->getTableSize()==0)) {
|
||||
jmodel->findUnnormalized(maxaddsub,maxleftright,maxext);
|
||||
jmodel->buildLabels(fd,addresstable,label,jmodel);
|
||||
|
@ -2692,11 +2714,7 @@ bool JumpTable::recoverLabels(Funcdata *fd)
|
|||
trivialSwitchOver();
|
||||
jmodel->buildLabels(fd,addresstable,label,origmodel);
|
||||
}
|
||||
if (origmodel != (JumpModel *)0) {
|
||||
delete origmodel;
|
||||
origmodel = (JumpModel *)0;
|
||||
}
|
||||
return multistagerestart;
|
||||
clearSavedModel();
|
||||
}
|
||||
|
||||
/// Clear out any data that is specific to a Funcdata instance.
|
||||
|
@ -2704,10 +2722,7 @@ bool JumpTable::recoverLabels(Funcdata *fd)
|
|||
void JumpTable::clear(void)
|
||||
|
||||
{
|
||||
if (origmodel != (JumpModel *)0) {
|
||||
delete origmodel;
|
||||
origmodel = (JumpModel *)0;
|
||||
}
|
||||
clearSavedModel();
|
||||
if (jmodel->isOverride())
|
||||
jmodel->clear();
|
||||
else {
|
||||
|
@ -2722,7 +2737,7 @@ void JumpTable::clear(void)
|
|||
indirect = (PcodeOp *)0;
|
||||
switchVarConsume = ~((uintb)0);
|
||||
defaultBlock = -1;
|
||||
recoverystage = 0;
|
||||
partialTable = false;
|
||||
// -opaddress- -maxtablesize- -maxaddsub- -maxleftright- -maxext- -collectloads- are permanent
|
||||
}
|
||||
|
||||
|
@ -2816,11 +2831,11 @@ bool JumpTable::checkForMultistage(Funcdata *fd)
|
|||
|
||||
{
|
||||
if (addresstable.size()!=1) return false;
|
||||
if (recoverystage != 0) return false;
|
||||
if (partialTable) return false;
|
||||
if (indirect == (PcodeOp *)0) return false;
|
||||
|
||||
if (fd->getOverride().queryMultistageJumptable(indirect->getAddr())) {
|
||||
recoverystage = 1; // Mark that we need additional recovery
|
||||
partialTable = true; // Mark that we need additional recovery
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
@ -42,11 +42,6 @@ struct JumptableThunkError : public LowlevelError {
|
|||
JumptableThunkError(const string &s) : LowlevelError(s) {} ///< Construct with an explanatory string
|
||||
};
|
||||
|
||||
/// \brief Exception thrown is there are no legal flows to a switch
|
||||
struct JumptableNotReachableError : public LowlevelError {
|
||||
JumptableNotReachableError(const string &s) : LowlevelError(s) {} ///< Constructor
|
||||
};
|
||||
|
||||
/// \brief A description where and how data was loaded from memory
|
||||
///
|
||||
/// This is a generic table description, giving the starting address
|
||||
|
@ -547,9 +542,8 @@ public:
|
|||
success = 0, ///< JumpTable is fully recovered
|
||||
fail_normal = 1, ///< Normal failure to recover
|
||||
fail_thunk = 2, ///< Likely \b thunk
|
||||
fail_noflow = 3, ///< No legal flow to BRANCHIND
|
||||
fail_return = 4, ///< Likely \b return operation
|
||||
fail_callother = 5 ///< Address formed by CALLOTHER
|
||||
fail_return = 3, ///< Likely \b return operation
|
||||
fail_callother = 4 ///< Address formed by CALLOTHER
|
||||
};
|
||||
private:
|
||||
/// \brief An address table index and its corresponding out-edge
|
||||
|
@ -575,9 +569,12 @@ private:
|
|||
uint4 maxaddsub; ///< Maximum ADDs or SUBs to normalize
|
||||
uint4 maxleftright; ///< Maximum shifts to normalize
|
||||
uint4 maxext; ///< Maximum extensions to normalize
|
||||
int4 recoverystage; ///< 0=no stages recovered, 1=additional stage needed, 2=complete
|
||||
bool partialTable; ///< Set to \b true if \b this table is incomplete and needs additional recovery steps
|
||||
bool collectloads; ///< Set to \b true if information about in-memory model data is/should be collected
|
||||
bool defaultIsFolded; ///< The \e default block is the target of a folded CBRANCH (and cannot have a label)
|
||||
void saveModel(void); ///< Save off current model (if any) and prepare for instantiating a new model
|
||||
void restoreSavedModel(void); ///< Restore any saved model as the current model
|
||||
void clearSavedModel(void); ///< Clear any saved model
|
||||
void recoverModel(Funcdata *fd); ///< Attempt recovery of the jump-table model
|
||||
void trivialSwitchOver(void); ///< Switch \b this table over to a trivial model
|
||||
void sanityCheck(Funcdata *fd,vector<int4> *loadpoints); ///< Perform sanity check on recovered address targets
|
||||
|
@ -590,8 +587,8 @@ public:
|
|||
bool isRecovered(void) const { return !addresstable.empty(); } ///< Return \b true if a model has been recovered
|
||||
bool isLabelled(void) const { return !label.empty(); } ///< Return \b true if \e case labels are computed
|
||||
bool isOverride(void) const; ///< Return \b true if \b this table was manually overridden
|
||||
bool isPossibleMultistage(void) const { return (addresstable.size()==1); } ///< Return \b true if this could be multi-staged
|
||||
int4 getStage(void) const { return recoverystage; } ///< Return what stage of recovery this jump-table is in.
|
||||
bool isPartial(void) const { return partialTable; } ///< Return \b true if \b this is a partial table needing more recovery
|
||||
void markComplete(void) { partialTable = false; } ///< Mark whatever is recovered so far as the complete table
|
||||
int4 numEntries(void) const { return addresstable.size(); } ///< Return the size of the address table for \b this jump-table
|
||||
uintb getSwitchVarConsume(void) const { return switchVarConsume; } ///< Get bits of switch variable consumed by \b this table
|
||||
int4 getDefaultBlock(void) const { return defaultBlock; } ///< Get the out-edge corresponding to the \e default switch destination
|
||||
|
@ -616,7 +613,8 @@ public:
|
|||
bool foldInGuards(Funcdata *fd) { return jmodel->foldInGuards(fd,this); } ///< Hide any guard code for \b this switch
|
||||
void recoverAddresses(Funcdata *fd); ///< Recover the raw jump-table addresses (the address table)
|
||||
void recoverMultistage(Funcdata *fd); ///< Recover jump-table addresses keeping track of a possible previous stage
|
||||
bool recoverLabels(Funcdata *fd); ///< Recover the case labels for \b this jump-table
|
||||
void matchModel(Funcdata *fd); ///< Try to match JumpTable model to the existing function
|
||||
void recoverLabels(Funcdata *fd); ///< Recover the case labels for \b this jump-table
|
||||
bool checkForMultistage(Funcdata *fd); ///< Check if this jump-table requires an additional recovery stage
|
||||
void clear(void); ///< Clear instance specific data for \b this jump-table
|
||||
void encode(Encoder &encoder) const; ///< Encode \b this jump-table as a \<jumptable> element
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue