#include "action.hh"
#include "funcdata.hh"
#include "coreaction.hh"
Action::Action(uint4 f,const string &nm,const string &g)
{
flags = f;
status = status_start;
breakpoint = 0;
name = nm;
basegroup = g;
count_tests = 0;
count_apply = 0;
}
void Action::issueWarning(Architecture *glb)
{
if ((flags&(rule_warnings_on|rule_warnings_given)) == rule_warnings_on) {
flags |= rule_warnings_given;
glb->printMessage("WARNING: Applied action "+name);
}
}
bool Action::checkStartBreak(void)
{
if ((breakpoint&(break_start|tmpbreak_start))!=0) {
breakpoint &= ~(tmpbreak_start); return true; }
return false; }
#ifdef OPACTION_DEBUG
bool Action::turnOnDebug(const string &nm)
{
if (nm == name) {
flags |= rule_debug;
return true;
}
return false;
}
bool Action::turnOffDebug(const string &nm)
{
if (nm == name) {
flags &= ~rule_debug;
return true;
}
return false;
}
#endif
void Action::printStatistics(ostream &s) const
{
s << name << dec << " Tested=" << count_tests << " Applied=" << count_apply << endl;
}
void Action::reset(Funcdata &data)
{
status = status_start;
flags &= ~rule_warnings_given; }
void Action::resetStats(void)
{
count_tests = 0;
count_apply = 0;
}
bool Action::checkActionBreak(void)
{
if ((breakpoint&(break_action|tmpbreak_action))!=0) {
breakpoint &= ~(tmpbreak_action); return true; }
return false; }
int4 Action::print(ostream &s,int4 num,int4 depth) const
{
s << setw(4) << dec << num;
s << (char *) (((flags&rule_repeatapply)!=0) ? " repeat " : " ");
s << (char) (((flags&rule_onceperfunc)!=0) ? '!' : ' ');
s << (char) (((breakpoint&(break_start|tmpbreak_start))!=0) ? 'S' : ' ');
s << (char) (((breakpoint&(break_action|tmpbreak_action))!=0) ? 'A' : ' ');
for(int4 i=0;i<depth*5+2;++i)
s << ' ';
s << name;
return num+1;
}
void Action::printState(ostream &s) const
{
s << name;
switch(status) {
case status_repeat:
case status_breakstarthit:
case status_start:
s << " start";
break;
case status_mid:
s << ':';
break;
case status_end:
s << " end";
}
}
bool Action::setBreakPoint(uint4 tp,const string &specify)
{
Action *res = getSubAction(specify);
if (res != (Action *)0) {
res->breakpoint |= tp;
return true;
}
Rule *rule = getSubRule(specify);
if (rule != (Rule *)0) {
rule->setBreak(tp);
return true;
}
return false;
}
void Action::clearBreakPoints(void)
{
breakpoint = 0;
}
bool Action::setWarning(bool val,const string &specify)
{
Action *res = getSubAction(specify);
if (res != (Action *)0) {
if (val)
res->turnOnWarnings();
else
res->turnOffWarnings();
return true;
}
Rule *rule = getSubRule(specify);
if (rule != (Rule *)0) {
if (val)
rule->turnOnWarnings();
else
rule->turnOffWarnings();
return true;
}
return false;
}
bool Action::disableRule(const string &specify)
{
Rule *rule = getSubRule(specify);
if (rule != (Rule *)0) {
rule->setDisable();
return true;
}
return false;
}
bool Action::enableRule(const string &specify)
{
Rule *rule = getSubRule(specify);
if (rule != (Rule *)0) {
rule->clearDisable();
return true;
}
return false;
}
static void next_specifyterm(string &token,string &remain,const string &specify)
{
string::size_type res = specify.find(':');
if (res != string::npos) {
token = specify.substr(0,res);
remain = specify.substr(res+1);
}
else {
token = specify;
remain.clear();
}
}
Action *Action::getSubAction(const string &specify)
{
if (name == specify) return this;
return (Action *)0;
}
Rule *Action::getSubRule(const string &specify)
{
return (Rule *)0;
}
int4 Action::perform(Funcdata &data)
{
int4 res;
do {
switch(status) {
case status_start:
count = 0; if (checkStartBreak()) {
status = status_breakstarthit;
return -1; }
count_tests += 1;
case status_breakstarthit:
case status_repeat:
lcount = count;
case status_mid:
#ifdef OPACTION_DEBUG
data.debugActivate();
#endif
res = apply(data); #ifdef OPACTION_DEBUG
data.debugModPrint(getName());
#endif
if (res < 0) { status = status_mid;
return res;
}
else if (lcount < count) { issueWarning(data.getArch());
count_apply += 1;
if (checkActionBreak()) {
status = status_actionbreak;
return -1; }
#ifdef OPACTION_DEBUG
else if (data.debugBreak()) {
status = status_actionbreak;
data.debugHandleBreak();
return -1;
}
#endif
}
break;
case status_end:
return 0; break;
case status_actionbreak: break; }
status = status_repeat;
} while((lcount<count)&&((flags&rule_repeatapply)!=0));
if ((flags&(rule_onceperfunc|rule_oneactperfunc))!=0) {
if ((count>0)||((flags&rule_onceperfunc)!=0))
status = status_end;
else
status = status_start;
}
else
status = status_start;
return count;
}
ActionGroup::~ActionGroup(void)
{
vector<Action *>::iterator iter;
for(iter=list.begin();iter!=list.end();++iter)
delete *iter;
}
void ActionGroup::addAction(Action *ac)
{
list.push_back(ac);
}
void ActionGroup::clearBreakPoints(void)
{
vector<Action *>::const_iterator iter;
for(iter=list.begin();iter!= list.end();++iter)
(*iter)->clearBreakPoints();
Action::clearBreakPoints();
}
Action *ActionGroup::clone(const ActionGroupList &grouplist) const
{
ActionGroup *res = (ActionGroup *)0;
vector<Action *>::const_iterator iter;
Action *ac;
for(iter=list.begin();iter!=list.end();++iter) {
ac = (*iter)->clone(grouplist);
if (ac != (Action *)0) {
if (res == (ActionGroup *)0)
res = new ActionGroup(flags,getName());
res->addAction(ac);
}
}
return res;
}
void ActionGroup::reset(Funcdata &data)
{
vector<Action *>::iterator iter;
Action::reset(data);
for(iter=list.begin();iter!=list.end();++iter)
(*iter)->reset(data); }
void ActionGroup::resetStats(void)
{
vector<Action *>::iterator iter;
Action::resetStats();
for(iter=list.begin();iter!=list.end();++iter)
(*iter)->resetStats();
}
int4 ActionGroup::print(ostream &s,int4 num,int4 depth) const
{
vector<Action *>::const_iterator titer;
num = Action::print(s,num,depth);
s << endl;
for(titer=list.begin();titer!=list.end();++titer) {
num = (*titer)->print(s,num,depth+1);
if (state == titer)
s << " <-- ";
s << endl;
}
return num;
}
void ActionGroup::printState(ostream &s) const
{
Action *subact;
Action::printState(s);
if (status==status_mid) {
subact = *state;
subact->printState(s);
}
}
Action *ActionGroup::getSubAction(const string &specify)
{
string token,remain;
next_specifyterm(token,remain,specify);
if (name == token) {
if (remain.empty()) return this;
}
else
remain = specify;
vector<Action *>::iterator iter;
Action *lastaction = (Action *)0;
int4 matchcount = 0;
for(iter=list.begin();iter!=list.end();++iter) {
Action *testaction = (*iter)->getSubAction(remain);
if (testaction != (Action *)0) {
lastaction = testaction;
matchcount += 1;
if (matchcount > 1) return (Action *)0;
}
}
return lastaction;
}
Rule *ActionGroup::getSubRule(const string &specify)
{
string token,remain;
next_specifyterm(token,remain,specify);
if (name == token) {
if (remain.empty()) return (Rule *)0;
}
else
remain = specify;
vector<Action *>::iterator iter;
Rule *lastrule = (Rule *)0;
int4 matchcount = 0;
for(iter=list.begin();iter!=list.end();++iter) {
Rule *testrule = (*iter)->getSubRule(remain);
if (testrule != (Rule *)0) {
lastrule = testrule;
matchcount += 1;
if (matchcount > 1) return (Rule *)0;
}
}
return lastrule;
}
int4 ActionGroup::apply(Funcdata &data)
{
int4 res;
if (status != status_mid)
state = list.begin(); for(;state!=list.end();++state) {
res = (*state)->perform(data);
if (res>0) { count += res;
if (checkActionBreak()) { ++state;
return -1;
}
}
else if (res<0) return -1; }
return 0; }
Action *ActionRestartGroup::clone(const ActionGroupList &grouplist) const
{
ActionGroup *res = (ActionGroup *)0;
vector<Action *>::const_iterator iter;
Action *ac;
for(iter=list.begin();iter!=list.end();++iter) {
ac = (*iter)->clone(grouplist);
if (ac != (Action *)0) {
if (res == (ActionGroup *)0)
res = new ActionRestartGroup(flags,getName(),maxrestarts);
res->addAction(ac);
}
}
return res;
}
void ActionRestartGroup::reset(Funcdata &data)
{
curstart = 0;
ActionGroup::reset(data);
}
int4 ActionRestartGroup::apply(Funcdata &data)
{
int4 res;
if (curstart == -1) return 0; for(;;) {
res = ActionGroup::apply(data);
if (res != 0) return res;
if (!data.hasRestartPending()) {
curstart = -1;
return 0;
}
if (data.isJumptableRecoveryOn()) return 0;
curstart += 1;
if (curstart > maxrestarts) {
data.warningHeader("Exceeded maximum restarts with more pending");
curstart = -1;
return 0;
}
data.getArch()->clearAnalysis(&data);
vector<Action *>::iterator iter;
for(iter=list.begin();iter!=list.end();++iter)
(*iter)->reset(data); status = status_start;
}
}
#ifdef OPACTION_DEBUG
bool ActionGroup::turnOnDebug(const string &nm)
{
if (Action::turnOnDebug(nm))
return true;
vector<Action *>::iterator iter;
for(iter = list.begin();iter!=list.end();++iter)
if ((*iter)->turnOnDebug(nm))
return true;
return false;
}
bool ActionGroup::turnOffDebug(const string &nm)
{
if (Action::turnOffDebug(nm))
return true;
vector<Action *>::iterator iter;
for(iter = list.begin();iter!=list.end();++iter)
if ((*iter)->turnOffDebug(nm))
return true;
return false;
}
#endif
void ActionGroup::printStatistics(ostream &s) const
{
Action::printStatistics(s);
vector<Action *>::const_iterator iter;
for(iter = list.begin();iter!=list.end();++iter)
(*iter)->printStatistics(s);
}
Rule::Rule(const string &g,uint4 fl,const string &nm)
{
flags = fl;
name = nm;
breakpoint = 0;
basegroup = g;
count_tests = 0;
count_apply = 0;
}
void Rule::issueWarning(Architecture *glb)
{
if ((flags&(warnings_on|warnings_given)) == warnings_on) {
flags |= warnings_given;
glb->printMessage("WARNING: Applied rule "+name);
}
}
void Rule::reset(Funcdata &data)
{
flags &= ~warnings_given; }
void Rule::resetStats(void)
{
count_tests = 0;
count_apply = 0;
}
#ifdef OPACTION_DEBUG
bool Rule::turnOnDebug(const string &nm)
{
if (nm == name) {
flags |= rule_debug;
return true;
}
return false;
}
bool Rule::turnOffDebug(const string &nm)
{
if (nm == name) {
flags &= ~rule_debug;
return true;
}
return false;
}
#endif
void Rule::printStatistics(ostream &s) const
{
s << name << dec << " Tested=" << count_tests << " Applied=" << count_apply << endl;
}
void Rule::getOpList(vector<uint4> &oplist) const
{
uint4 i;
for(i=0;i<CPUI_MAX;++i)
oplist.push_back(i);
}
bool Rule::checkActionBreak(void)
{
if ((breakpoint&(Action::break_action|Action::tmpbreak_action))!=0) {
breakpoint &= ~(Action::tmpbreak_action); return true; }
return false; }
ActionPool::~ActionPool(void)
{
vector<Rule *>::iterator iter;
for(iter=allrules.begin();iter!=allrules.end();++iter)
delete *iter;
}
void ActionPool::addRule(Rule *rl)
{
vector<uint4> oplist;
vector<uint4>::iterator iter;
allrules.push_back(rl);
rl->getOpList(oplist);
for(iter=oplist.begin();iter!=oplist.end();++iter)
perop[*iter].push_back(rl); }
int4 ActionPool::print(ostream &s,int4 num,int4 depth) const
{
vector<Rule *>::const_iterator iter;
Rule *rl;
int4 i;
num = Action::print(s,num,depth);
s << endl;
depth += 1;
for(iter=allrules.begin();iter!=allrules.end();++iter) {
rl = *iter;
s << setw(4) << dec << num;
s << (char) ( rl->isDisabled() ? 'D' : ' ');
s << (char) ( ((rl->getBreakPoint()&(break_action|tmpbreak_action))!=0) ? 'A' : ' ');
for(i=0;i<depth*5+2;++i)
s << ' ';
s << rl->getName();
s << endl;
num += 1;
}
return num;
}
void ActionPool::printState(ostream &s) const
{
PcodeOp *op;
Action::printState(s);
if (status==status_mid) {
op = (*op_state).second;
s << ' ' << op->getSeqNum();
}
}
Rule *ActionPool::getSubRule(const string &specify)
{
string token,remain;
next_specifyterm(token,remain,specify);
if (name == token) {
if (remain.empty()) return (Rule *)0; }
else
remain = specify;
vector<Rule *>::iterator iter;
Rule *lastrule = (Rule *)0;
int4 matchcount = 0;
for(iter=allrules.begin();iter!=allrules.end();++iter) {
Rule *testrule = *iter;
if (testrule->getName() == remain) {
lastrule = testrule;
matchcount += 1;
if (matchcount > 1) return (Rule *)0;
}
}
return lastrule;
}
int4 ActionPool::processOp(PcodeOp *op,Funcdata &data)
{
Rule *rl;
int4 res;
uint4 opc;
if (op->isDead()) {
op_state++;
data.opDeadAndGone(op);
rule_index = 0;
return 0;
}
opc = op->code();
while(rule_index < perop[opc].size()) {
rl = perop[opc][rule_index++];
if (rl->isDisabled()) continue;
#ifdef OPACTION_DEBUG
data.debugActivate();
#endif
rl->count_tests += 1;
res = rl->applyOp(op,data);
#ifdef OPACTION_DEBUG
data.debugModPrint(rl->getName());
#endif
if (res>0) {
rl->count_apply += 1;
count += res;
rl->issueWarning(data.getArch()); if (rl->checkActionBreak())
return -1;
#ifdef OPACTION_DEBUG
if (data.debugBreak()) {
data.debugHandleBreak();
return -1;
}
#endif
if (op->isDead()) break;
if (opc != op->code()) { opc = op->code();
rule_index = 0;
}
}
else if (opc != op->code()) {
data.getArch()->printMessage("ERROR: Rule " + rl->getName() + " changed op without returning result of 1!");
opc = op->code();
rule_index = 0;
}
}
op_state++;
rule_index = 0;
return 0;
}
int4 ActionPool::apply(Funcdata &data)
{
if (status != status_mid) {
op_state = data.beginOpAll(); rule_index = 0;
}
for(;op_state!=data.endOpAll();)
if (0!=processOp((*op_state).second,data)) return -1;
return 0; }
void ActionPool::clearBreakPoints(void)
{
vector<Rule *>::const_iterator iter;
for(iter=allrules.begin();iter!=allrules.end();++iter)
(*iter)->clearBreakPoints();
Action::clearBreakPoints();
}
Action *ActionPool::clone(const ActionGroupList &grouplist) const
{
ActionPool *res = (ActionPool *)0;
vector<Rule *>::const_iterator iter;
Rule *rl;
for(iter=allrules.begin();iter!=allrules.end();++iter) {
rl = (*iter)->clone(grouplist);
if (rl != (Rule *)0) {
if (res == (ActionPool *)0)
res = new ActionPool(flags,getName());
res->addRule(rl);
}
}
return res;
}
void ActionPool::reset(Funcdata &data)
{
vector<Rule *>::iterator iter;
Action::reset(data);
for(iter=allrules.begin();iter!=allrules.end();++iter)
(*iter)->reset(data);
}
void ActionPool::resetStats(void)
{
vector<Rule *>::iterator iter;
Action::resetStats();
for(iter=allrules.begin();iter!=allrules.end();++iter)
(*iter)->resetStats();
}
#ifdef OPACTION_DEBUG
bool ActionPool::turnOnDebug(const string &nm)
{
vector<Rule *>::iterator iter;
if (Action::turnOnDebug(nm))
return true;
for(iter=allrules.begin();iter!=allrules.end();++iter)
if ((*iter)->turnOnDebug(nm))
return true;
return false;
}
bool ActionPool::turnOffDebug(const string &nm)
{
vector<Rule *>::iterator iter;
if (Action::turnOffDebug(nm))
return true;
for(iter=allrules.begin();iter!=allrules.end();++iter)
if ((*iter)->turnOffDebug(nm))
return true;
return false;
}
#endif
void ActionPool::printStatistics(ostream &s) const
{
vector<Rule *>::const_iterator iter;
Action::printStatistics(s);
for(iter=allrules.begin();iter!=allrules.end();++iter)
(*iter)->printStatistics(s);
}
const char ActionDatabase::universalname[] = "universal";
ActionDatabase::~ActionDatabase(void)
{
map<string,Action *>::iterator iter;
for(iter = actionmap.begin();iter!=actionmap.end();++iter)
delete (*iter).second;
}
void ActionDatabase::resetDefaults(void)
{
Action *universalAction = (Action *)0;
map<string,Action *>::iterator iter;
iter = actionmap.find(universalname);
if (iter != actionmap.end())
universalAction = (*iter).second;
for(iter = actionmap.begin();iter!=actionmap.end();++iter) {
Action *curAction = (*iter).second;
if (curAction != universalAction)
delete curAction; }
actionmap.clear();
registerAction(universalname, universalAction);
buildDefaultGroups();
setCurrent("decompile"); }
const ActionGroupList &ActionDatabase::getGroup(const string &grp) const
{
map<string,ActionGroupList>::const_iterator iter;
iter = groupmap.find(grp);
if (iter == groupmap.end())
throw LowlevelError("Action group does not exist: "+grp);
return (*iter).second;
}
Action *ActionDatabase::setCurrent(const string &actname)
{
currentactname = actname;
currentact = deriveAction(universalname,actname);
return currentact;
}
Action *ActionDatabase::toggleAction(const string &grp, const string &basegrp,bool val)
{
Action *act = getAction(universalname);
if (val)
addToGroup(grp,basegrp);
else
removeFromGroup(grp,basegrp);
const ActionGroupList &curgrp(getGroup(grp)); Action *newact = act->clone(curgrp);
registerAction(grp,newact);
if (grp == currentactname)
currentact = newact;
return newact;
}
void ActionDatabase::setGroup(const string &grp,const char **argv)
{
ActionGroupList &curgrp( groupmap[ grp ] );
curgrp.list.clear(); for(int4 i=0;;++i) {
if (argv[i] == (char *)0) break;
if (argv[i][0] == '\0') break;
curgrp.list.insert( argv[i] );
}
isDefaultGroups = false;
}
void ActionDatabase::cloneGroup(const string &oldname,const string &newname)
{
const ActionGroupList &curgrp(getGroup(oldname)); groupmap[ newname ] = curgrp; isDefaultGroups = false;
}
bool ActionDatabase::addToGroup(const string &grp, const string &basegroup)
{
isDefaultGroups = false;
ActionGroupList &curgrp( groupmap[ grp ] );
return curgrp.list.insert( basegroup ).second;
}
bool ActionDatabase::removeFromGroup(const string &grp, const string &basegrp)
{
isDefaultGroups = false;
ActionGroupList &curgrp( groupmap[ grp ] );
return (curgrp.list.erase(basegrp) > 0);
}
Action *ActionDatabase::getAction(const string &nm) const
{
map<string,Action *>::const_iterator iter;
iter = actionmap.find(nm);
if (iter == actionmap.end())
throw LowlevelError("No registered action: "+nm);
return (*iter).second;
}
void ActionDatabase::registerAction(const string &nm,Action *act)
{
map<string,Action *>::iterator iter;
iter = actionmap.find(nm);
if (iter != actionmap.end()) {
delete (*iter).second;
(*iter).second = act;
}
else {
actionmap[nm] = act;
}
}
Action *ActionDatabase::deriveAction(const string &baseaction, const string &grp)
{
map<string,Action *>::iterator iter;
iter = actionmap.find(grp);
if (iter != actionmap.end())
return (*iter).second;
const ActionGroupList &curgrp(getGroup(grp)); Action *act = getAction(baseaction);
Action *newact = act->clone( curgrp );
registerAction(grp,newact);
return newact;
}