mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-04 10:19:23 +02:00
1162 lines
30 KiB
C++
1162 lines
30 KiB
C++
/* ###
|
|
* IP: GHIDRA
|
|
*
|
|
* 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.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
#include "action.hh"
|
|
#include "funcdata.hh"
|
|
|
|
#include "coreaction.hh"
|
|
|
|
namespace ghidra {
|
|
|
|
/// Specify the name, group, and properties of the Action
|
|
/// \param f is the collection of property flags
|
|
/// \param nm is the Action name
|
|
/// \param g is the Action group
|
|
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;
|
|
}
|
|
|
|
/// If enabled, issue a warning that this Action has been applied
|
|
/// \param glb is the controlling Architecture
|
|
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);
|
|
}
|
|
}
|
|
|
|
/// Check if there was an active \e start break point on this action
|
|
/// \return true if there was a start breakpoint
|
|
bool Action::checkStartBreak(void)
|
|
|
|
{
|
|
if ((breakpoint&(break_start|tmpbreak_start))!=0) {
|
|
breakpoint &= ~(tmpbreak_start); // Clear breakpoint if temporary
|
|
return true; // Breakpoint was active
|
|
}
|
|
return false; // Breakpoint was not active
|
|
}
|
|
|
|
#ifdef OPACTION_DEBUG
|
|
|
|
/// If this Action matches the given name, enable debugging.
|
|
/// \param nm is the Action name to match
|
|
/// \return true if debugging was enabled
|
|
bool Action::turnOnDebug(const string &nm)
|
|
|
|
{
|
|
if (nm == name) {
|
|
flags |= rule_debug;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/// If this Action matches the given name, disable debugging.
|
|
/// \param nm is the Action name to match
|
|
/// \return true if debugging was disabled
|
|
bool Action::turnOffDebug(const string &nm)
|
|
|
|
{
|
|
if (nm == name) {
|
|
flags &= ~rule_debug;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
/// Print out the collected statistics for the Action to stream
|
|
/// \param s is the output stream
|
|
void Action::printStatistics(ostream &s) const
|
|
|
|
{
|
|
s << name << dec << " Tested=" << count_tests << " Applied=" << count_apply << endl;
|
|
}
|
|
|
|
/// \param data is the new function \b this Action may affect
|
|
void Action::reset(Funcdata &data)
|
|
|
|
{
|
|
status = status_start;
|
|
flags &= ~rule_warnings_given; // Indicate a warning has not been given yet
|
|
}
|
|
|
|
/// Reset all the counts to zero
|
|
void Action::resetStats(void)
|
|
|
|
{
|
|
count_tests = 0;
|
|
count_apply = 0;
|
|
}
|
|
|
|
/// Check if there was an active \e action breakpoint on this Action
|
|
/// \return true if there was an action breakpoint
|
|
bool Action::checkActionBreak(void)
|
|
|
|
{
|
|
if ((breakpoint&(break_action|tmpbreak_action))!=0) {
|
|
breakpoint &= ~(tmpbreak_action); // Clear temporary breakpoint
|
|
return true; // Breakpoint was active
|
|
}
|
|
return false; // Breakpoint was not active
|
|
}
|
|
|
|
/// The description is suitable for a console mode listing of actions
|
|
/// \param s is the output stream
|
|
/// \param num is a starting index to associate with the action (and its sub-actions)
|
|
/// \param depth is amount of indent necessary before printing
|
|
/// \return the next available index
|
|
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;
|
|
}
|
|
|
|
/// This will the Action name and the next step to execute
|
|
/// \param s is the output stream
|
|
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";
|
|
}
|
|
}
|
|
|
|
/// A breakpoint can be placed on \b this Action or some sub-action by properly
|
|
/// specifying the (sub)action name.
|
|
/// \param tp is the type of breakpoint (\e break_start, break_action, etc.)
|
|
/// \param specify is the (possibly sub)action to apply the break point to
|
|
/// \return true if a breakpoint was successfully set
|
|
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;
|
|
}
|
|
|
|
/// If enabled, a warning will be printed whenever this action applies.
|
|
/// The warning can be toggled for \b this Action or some sub-action by
|
|
/// specifying its name.
|
|
/// \param val is the toggle value for the warning
|
|
/// \param specify is the name of the action or sub-action to toggle
|
|
/// \return true if the warning was successfully toggled
|
|
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;
|
|
}
|
|
|
|
/// An individual Rule can be disabled by name, within \b this Action. It must
|
|
/// be specified by a ':' separated name \e path, from the root Action down
|
|
/// to the specific Rule.
|
|
/// \param specify is the name path
|
|
/// \return \b true if the Rule is successfully disabled
|
|
bool Action::disableRule(const string &specify)
|
|
|
|
{
|
|
Rule *rule = getSubRule(specify);
|
|
if (rule != (Rule *)0) {
|
|
rule->setDisable();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/// An individual Rule can be enabled by name, within \b this Action. It must
|
|
/// be specified by a ':' separated name \e path, from the root Action down
|
|
/// to the specific Rule.
|
|
/// \param specify is the name path
|
|
/// \return \b true if the Rule is successfully enabled
|
|
bool Action::enableRule(const string &specify)
|
|
|
|
{
|
|
Rule *rule = getSubRule(specify);
|
|
if (rule != (Rule *)0) {
|
|
rule->clearDisable();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/// Pull the next token from a ':' separated list of Action and Rule names
|
|
/// \param token will be filled with string up to the next ':'
|
|
/// \param remain will be whats left of the list of removing the token and ':'
|
|
/// \param is the list to pull the token from
|
|
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();
|
|
}
|
|
}
|
|
|
|
/// If this Action matches the given name, it is returned. If the
|
|
/// name matches a sub-action, this is returned.
|
|
/// \param specify is the action name to match
|
|
/// \return the matching Action or sub-action
|
|
Action *Action::getSubAction(const string &specify)
|
|
|
|
{
|
|
if (name == specify) return this;
|
|
return (Action *)0;
|
|
}
|
|
|
|
/// Find a Rule, as a component of \b this Action, with the given name.
|
|
/// \param specify is the name of the rule
|
|
/// \return the matching sub-rule
|
|
Rule *Action::getSubRule(const string &specify)
|
|
|
|
{
|
|
return (Rule *)0;
|
|
}
|
|
|
|
/// Run \b this Action until completion or a breakpoint occurs. Depending
|
|
/// on the behavior properties of this instance, the apply() method may get
|
|
/// called many times or none. Generally the number of changes made by
|
|
/// the action is returned, but if a breakpoint occurs -1 is returned.
|
|
/// A successive call to perform() will "continue" from the break point.
|
|
/// \param data is the function being acted on
|
|
/// \return the number of changes or -1
|
|
int4 Action::perform(Funcdata &data)
|
|
|
|
{
|
|
int4 res;
|
|
|
|
do {
|
|
switch(status) {
|
|
case status_start:
|
|
count = 0; // No changes made yet by this action
|
|
if (checkStartBreak()) {
|
|
status = status_breakstarthit;
|
|
return -1; // Indicate partial completion
|
|
}
|
|
count_tests += 1;
|
|
case status_breakstarthit:
|
|
case status_repeat:
|
|
lcount = count;
|
|
case status_mid:
|
|
#ifdef OPACTION_DEBUG
|
|
data.debugActivate();
|
|
#endif
|
|
res = apply(data); // Start or continue action
|
|
#ifdef OPACTION_DEBUG
|
|
data.debugModPrint(getName());
|
|
#endif
|
|
if (res < 0) { // negative indicates partial completion
|
|
status = status_mid;
|
|
return res;
|
|
}
|
|
else if (lcount < count) { // Action has been applied
|
|
issueWarning(data.getArch());
|
|
count_apply += 1;
|
|
if (checkActionBreak()) {
|
|
status = status_actionbreak;
|
|
return -1; // Indicate action breakpoint
|
|
}
|
|
#ifdef OPACTION_DEBUG
|
|
else if (data.debugBreak()) {
|
|
status = status_actionbreak;
|
|
data.debugHandleBreak();
|
|
return -1;
|
|
}
|
|
#endif
|
|
}
|
|
break;
|
|
case status_end:
|
|
return 0; // Rule applied, do not repeat until reset
|
|
break;
|
|
case status_actionbreak: // Returned -1 last time, but we do not reapply
|
|
break; // we either repeat, or return our count
|
|
}
|
|
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;
|
|
}
|
|
|
|
/// To be used only during the construction of \b this ActionGroup. This routine
|
|
/// adds an Action to the end of this group's list.
|
|
/// \param ac is the Action to add
|
|
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); // Reset each subrule
|
|
}
|
|
|
|
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; // Still have to match entire 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; // Still have to match entire 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(); // Initialize the derived action
|
|
for(;state!=list.end();++state) {
|
|
res = (*state)->perform(data);
|
|
if (res>0) { // A change was made
|
|
count += res;
|
|
if (checkActionBreak()) { // Check if this is an action breakpoint
|
|
++state;
|
|
return -1;
|
|
}
|
|
}
|
|
else if (res<0) // Partial completion of member
|
|
return -1; // equates to partial completion of group action
|
|
}
|
|
|
|
return 0; // Indicate successful completion
|
|
}
|
|
|
|
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; // Already completed
|
|
for(;;) {
|
|
res = ActionGroup::apply(data);
|
|
if (res != 0) return res;
|
|
if (!data.hasRestartPending()) {
|
|
curstart = -1;
|
|
return 0;
|
|
}
|
|
if (data.isJumptableRecoveryOn()) // Don't restart within jumptable recovery
|
|
return 0;
|
|
curstart += 1;
|
|
if (curstart > maxrestarts) {
|
|
data.warningHeader("Exceeded maximum restarts with more pending");
|
|
curstart = -1;
|
|
return 0;
|
|
}
|
|
data.getArch()->clearAnalysis(&data);
|
|
|
|
// Reset everything but ourselves
|
|
vector<Action *>::iterator iter;
|
|
for(iter=list.begin();iter!=list.end();++iter)
|
|
(*iter)->reset(data); // Reset each subrule
|
|
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);
|
|
}
|
|
|
|
/// \param g is the groupname to which \b this Rule belongs
|
|
/// \param fl is the set of properties
|
|
/// \param nm is the name of the Rule
|
|
Rule::Rule(const string &g,uint4 fl,const string &nm)
|
|
|
|
{
|
|
flags = fl;
|
|
name = nm;
|
|
breakpoint = 0;
|
|
basegroup = g;
|
|
count_tests = 0;
|
|
count_apply = 0;
|
|
}
|
|
|
|
/// This method is called whenever \b this Rule applies. If warnings have been
|
|
/// enabled for the Rule via turnOnWarnings(), this method will print a message
|
|
/// indicating the Rule has been applied. Even with repeat calls, the message
|
|
/// will only be printed once (until reset() is called)
|
|
/// \param glb is the Architecture holding the console to print to
|
|
void Rule::issueWarning(Architecture *glb)
|
|
|
|
{
|
|
if ((flags&(warnings_on|warnings_given)) == warnings_on) {
|
|
flags |= warnings_given;
|
|
glb->printMessage("WARNING: Applied rule "+name);
|
|
}
|
|
}
|
|
|
|
/// Any state that is specific to a particular function is cleared by this method.
|
|
/// This method can be used to initialize a Rule based on a new function it will apply to
|
|
/// \param data is the \e new function about to be transformed
|
|
void Rule::reset(Funcdata &data)
|
|
|
|
{
|
|
flags &= ~warnings_given; // Indicate that warning has not yet been given
|
|
}
|
|
|
|
/// Counts of when this Rule has been attempted/applied are reset to zero.
|
|
/// Derived Rules may reset their own statistics.
|
|
void Rule::resetStats(void)
|
|
|
|
{
|
|
count_tests = 0;
|
|
count_apply = 0;
|
|
}
|
|
|
|
#ifdef OPACTION_DEBUG
|
|
/// If \b this Rule has the given name, then enable debugging.
|
|
/// \param nm is the given name to match
|
|
/// \return true if debugging was enabled
|
|
bool Rule::turnOnDebug(const string &nm)
|
|
|
|
{
|
|
if (nm == name) {
|
|
flags |= rule_debug;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/// If \b this Rule has the given name, then disable debugging.
|
|
/// \param nm is the given name to match
|
|
/// \return true if debugging was disabled
|
|
bool Rule::turnOffDebug(const string &nm)
|
|
|
|
{
|
|
if (nm == name) {
|
|
flags &= ~rule_debug;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
/// Print the accumulated counts associated with applying this Rule to stream.
|
|
/// This method is intended for console mode debugging. Derived Rules may
|
|
/// override this to display their own statistics.
|
|
/// \param s is the output stream
|
|
void Rule::printStatistics(ostream &s) const
|
|
|
|
{
|
|
s << name << dec << " Tested=" << count_tests << " Applied=" << count_apply << endl;
|
|
}
|
|
|
|
/// Populate the given array with all possible OpCodes this Rule might apply to.
|
|
/// By default, this method returns all possible OpCodes
|
|
/// \param oplist is the array to populate
|
|
void Rule::getOpList(vector<uint4> &oplist) const
|
|
|
|
{
|
|
uint4 i;
|
|
|
|
for(i=0;i<CPUI_MAX;++i)
|
|
oplist.push_back(i);
|
|
}
|
|
|
|
/// This method is called every time the Rule successfully applies. If it returns
|
|
/// \b true, this indicates to the system that an action breakpoint has occurred.
|
|
/// \return true if an action breakpoint should occur because of this Rule
|
|
bool Rule::checkActionBreak(void)
|
|
|
|
{
|
|
if ((breakpoint&(Action::break_action|Action::tmpbreak_action))!=0) {
|
|
breakpoint &= ~(Action::tmpbreak_action); // Clear temporary breakpoint
|
|
return true; // Breakpoint was active
|
|
}
|
|
return false; // Breakpoint was not active
|
|
}
|
|
|
|
ActionPool::~ActionPool(void)
|
|
|
|
{
|
|
vector<Rule *>::iterator iter;
|
|
|
|
for(iter=allrules.begin();iter!=allrules.end();++iter)
|
|
delete *iter;
|
|
}
|
|
|
|
/// This method should only be invoked during construction of this ActionPool
|
|
/// A single Rule is added to the pool. The Rule's OpCode is inspected by this method.
|
|
/// \param rl is the Rule to add
|
|
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); // Add rule to list for each op it registers for
|
|
}
|
|
|
|
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; // Match, but not a rule
|
|
}
|
|
else
|
|
remain = specify; // Still have to match entire 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;
|
|
}
|
|
|
|
/// This method attempts to apply each Rule to the current PcodeOp
|
|
/// Action breakpoints are checked if the Rule successfully applies.
|
|
/// 0 is returned for no breakpoint, -1 if a breakpoint occurs.
|
|
/// If a breakpoint did occur, an additional call to processOp() will
|
|
/// pick up where it left off before the breakpoint. The PcodeOp iterator is advanced.
|
|
/// \param op is the current PcodeOp
|
|
/// \param data is the function being transformed
|
|
/// \return 0 if no breakpoint, -1 otherwise
|
|
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()); // Check if we need to issue a warning
|
|
if (rl->checkActionBreak())
|
|
return -1;
|
|
#ifdef OPACTION_DEBUG
|
|
if (data.debugBreak()) {
|
|
data.debugHandleBreak();
|
|
return -1;
|
|
}
|
|
#endif
|
|
if (op->isDead()) break;
|
|
if (opc != op->code()) { // Set of rules to apply to this op has changed
|
|
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(); // Initialize the derived action
|
|
rule_index = 0;
|
|
}
|
|
for(;op_state!=data.endOpAll();)
|
|
if (0!=processOp((*op_state).second,data)) return -1;
|
|
|
|
return 0; // Indicate successful completion
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
/// Clear out (possibly altered) root Actions. Reset the default groups.
|
|
/// Set the default root action "decompile"
|
|
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; // Clear out any old (modified) root actions
|
|
}
|
|
actionmap.clear();
|
|
registerAction(universalname, universalAction);
|
|
|
|
buildDefaultGroups();
|
|
setCurrent("decompile"); // The default root action
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
/// The Action is specified by name. A grouplist must already exist for this name.
|
|
/// If the Action doesn't already exist, it will be derived from the \e universal
|
|
/// action via this grouplist.
|
|
/// \param actname is the name of the \e root Action
|
|
Action *ActionDatabase::setCurrent(const string &actname)
|
|
|
|
{
|
|
currentactname = actname;
|
|
currentact = deriveAction(universalname,actname);
|
|
return currentact;
|
|
}
|
|
|
|
|
|
/// A particular group is either added or removed from the grouplist defining
|
|
/// a particular \e root Action. The \e root Action is then (re)derived from the universal
|
|
/// \param grp is the name of the \e root Action
|
|
/// \param basegrp is name of group (within the grouplist) to toggle
|
|
/// \param val is \b true if the group should be added or \b false if it should be removed
|
|
/// \return the modified \e root Action
|
|
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)); // Group should already exist
|
|
Action *newact = act->clone(curgrp);
|
|
|
|
registerAction(grp,newact);
|
|
|
|
if (grp == currentactname)
|
|
currentact = newact;
|
|
|
|
return newact;
|
|
}
|
|
|
|
/// (Re)set the grouplist for a particular \e root Action. Do not use this routine
|
|
/// to redefine an existing \e root Action.
|
|
/// \param grp is the name of the \e root Action
|
|
/// \param argv is a list of static char pointers, which must end with a NULL pointer, or a zero length string.
|
|
void ActionDatabase::setGroup(const string &grp,const char **argv)
|
|
|
|
{
|
|
ActionGroupList &curgrp( groupmap[ grp ] );
|
|
curgrp.list.clear(); // Clear out any old members
|
|
for(int4 i=0;;++i) {
|
|
if (argv[i] == (char *)0) break;
|
|
if (argv[i][0] == '\0') break;
|
|
curgrp.list.insert( argv[i] );
|
|
}
|
|
isDefaultGroups = false;
|
|
}
|
|
|
|
/// Copy an existing \e root Action by copying its grouplist, giving it a new name.
|
|
/// This is suitable for a copy then modify strategy to create a new \e root Action.
|
|
/// Do not use to redefine a \e root Action that has already been instantiated
|
|
/// \param oldname is the name of an existing \e root Action
|
|
/// \param newname is the name of the copy
|
|
void ActionDatabase::cloneGroup(const string &oldname,const string &newname)
|
|
|
|
{
|
|
const ActionGroupList &curgrp(getGroup(oldname)); // Should already exist
|
|
groupmap[ newname ] = curgrp; // Copy the group
|
|
isDefaultGroups = false;
|
|
}
|
|
|
|
/// Add a group to the grouplist for a particular \e root Action.
|
|
/// Do not use to redefine a \e root Action that has already been instantiated.
|
|
/// \param grp is the name of the \e root Action
|
|
/// \param basegroup is the group to add
|
|
/// \return \b true for a new addition, \b false is the group was already present
|
|
bool ActionDatabase::addToGroup(const string &grp, const string &basegroup)
|
|
|
|
{
|
|
isDefaultGroups = false;
|
|
ActionGroupList &curgrp( groupmap[ grp ] );
|
|
return curgrp.list.insert( basegroup ).second;
|
|
}
|
|
|
|
/// The group is removed from the grouplist of a \e root Action.
|
|
/// Do not use to redefine a \e root Action that has already been instantiated.
|
|
/// \param grp is the name of the \e root Action
|
|
/// \param basegrp is the group to remove
|
|
/// \return \b true if the group existed and was removed
|
|
bool ActionDatabase::removeFromGroup(const string &grp, const string &basegrp)
|
|
|
|
{
|
|
isDefaultGroups = false;
|
|
ActionGroupList &curgrp( groupmap[ grp ] );
|
|
return (curgrp.list.erase(basegrp) > 0);
|
|
}
|
|
|
|
/// \param nm is the name of the \e root Action
|
|
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;
|
|
}
|
|
|
|
/// Internal method for associated a \e root Action name with its Action object.
|
|
/// The database takes over memory management of the object.
|
|
/// \param nm is the name to register as
|
|
/// \param act is the Action object
|
|
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;
|
|
}
|
|
}
|
|
|
|
/// Internal method to build the Action object corresponding to a \e root Action
|
|
/// The new Action object is created by selectively cloning components
|
|
/// from an existing object based on a grouplist.
|
|
/// \param baseaction is the name of the model Action object to derive \e from
|
|
/// \param grp is the name of the grouplist steering the clone
|
|
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; // Already derived this action
|
|
|
|
const ActionGroupList &curgrp(getGroup(grp)); // Group should already exist
|
|
Action *act = getAction(baseaction);
|
|
Action *newact = act->clone( curgrp );
|
|
|
|
// Register the action with the name of the group it was derived from
|
|
registerAction(grp,newact);
|
|
return newact;
|
|
}
|
|
|
|
} // End namespace ghidra
|