Merge branch 'master' of gitlab.evoforge.org:ghidra/ghidra into debugger

This commit is contained in:
Dan 2021-04-05 09:20:02 -04:00
commit 102f4e8a7a
34 changed files with 701 additions and 328 deletions

View file

@ -17,6 +17,7 @@
<listEntry value="org.eclipse.debug.ui.launchGroup.debug"/>
<listEntry value="org.eclipse.debug.ui.launchGroup.run"/>
</listAttribute>
<booleanAttribute key="org.eclipse.jdt.launching.ATTR_ATTR_USE_ARGFILE" value="false"/>
<booleanAttribute key="org.eclipse.jdt.launching.ATTR_USE_CLASSPATH_ONLY_JAR" value="false"/>
<booleanAttribute key="org.eclipse.jdt.launching.ATTR_USE_START_ON_FIRST_THREAD" value="true"/>
<listAttribute key="org.eclipse.jdt.launching.CLASSPATH">
@ -29,5 +30,5 @@
<stringAttribute key="org.eclipse.jdt.launching.MODULE_NAME" value="Framework Utility"/>
<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="ghidra.GhidraRun"/>
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="Framework Utility"/>
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-XX:+IgnoreUnrecognizedVMOptions -Djava.system.class.loader=ghidra.GhidraClassLoader -Xshare:off -Dfile.encoding=UTF8 -Duser.country=US -Duser.language=en -Dsun.java2d.pmoffscreen=false -Dsun.java2d.xrender=true -Dsun.java2d.d3d=false -Xdock:name=&quot;Ghidra&quot; -Dvisualvm.display.name=Ghidra -Dpython.console.encoding=UTF-8 -DContinuesInterceptor.disabled=true"/>
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-XX:+IgnoreUnrecognizedVMOptions -Djava.system.class.loader=ghidra.GhidraClassLoader -Xshare:off -Dfile.encoding=UTF8 -Duser.country=US -Duser.language=en -Dsun.java2d.pmoffscreen=false -Dsun.java2d.xrender=true -Dsun.java2d.d3d=false -Xdock:name=&quot;Ghidra&quot; -Dvisualvm.display.name=Ghidra -Dpython.console.encoding=UTF-8 -DContinuesInterceptor.disabled=true --illegal-access=permit"/>
</launchConfiguration>

View file

@ -36,7 +36,7 @@ public class PropagateConstantReferences extends GhidraScript {
public void run() throws Exception {
long numInstructions = currentProgram.getListing().getNumInstructions();
monitor.initialize((int) (numInstructions));
monitor.setMessage("Constant Propogation Markup");
monitor.setMessage("Constant Propagation Markup");
// set up the address set to restrict processing
AddressSet restrictedSet =

View file

@ -57,7 +57,7 @@ public class PropagateX86ConstantReferences extends GhidraScript {
public void run() throws Exception {
long numInstructions = currentProgram.getListing().getNumInstructions();
monitor.initialize((int) (numInstructions));
monitor.setMessage("Constant Propogation Markup");
monitor.setMessage("Constant Propagation Markup");
// set up the address set to restrict processing
AddressSet restrictedSet = new AddressSet(currentSelection);

View file

@ -37,7 +37,6 @@ import ghidra.app.plugin.assembler.Assembler;
import ghidra.app.plugin.assembler.Assemblers;
import ghidra.app.plugin.core.assembler.AssemblyDualTextField.*;
import ghidra.app.plugin.core.codebrowser.CodeViewerProvider;
import ghidra.app.util.PluginConstants;
import ghidra.app.util.viewer.field.ListingField;
import ghidra.app.util.viewer.listingpanel.ListingModelAdapter;
import ghidra.app.util.viewer.listingpanel.ListingPanel;
@ -78,7 +77,7 @@ public class AssembleDockingAction extends DockingAction {
private Language lang;
private Assembler assembler;
private final MyListener listener = new MyListener();
private PluginTool tool;
//private PluginTool tool;
// Callback to keep the autocompleter positioned under the fields
private FieldPanelOverLayoutListener autoCompleteMover = (FieldPanelOverLayoutEvent ev) -> {
@ -159,23 +158,7 @@ public class AssembleDockingAction extends DockingAction {
*/
public AssembleDockingAction(PluginTool tool, String name, String owner) {
this(name, owner);
this.tool = tool;
}
@Override
public void dispose() {
super.dispose();
input.dispose();
}
protected void onFirstInvocation() {
ComponentProvider prov = tool.getComponentProvider(PluginConstants.CODE_BROWSER);
cv = (CodeViewerProvider) prov;
listpane = cv.getListingPanel();
codepane = listpane.getFieldPanel();
fieldLayoutManager = new FieldPanelOverLayoutManager(codepane);
codepane.setLayout(fieldLayoutManager);
//this.tool = tool;
// If I lose focus, cancel the assembly
input.addFocusListener(new FocusListener() {
@ -189,14 +172,37 @@ public class AssembleDockingAction extends DockingAction {
cancel();
}
});
input.getMnemonicField().setBorder(BorderFactory.createLineBorder(Color.RED, 2));
input.getOperandsField().setBorder(BorderFactory.createLineBorder(Color.RED, 2));
input.getAssemblyField().setBorder(BorderFactory.createLineBorder(Color.RED, 2));
input.getAutocompleter().addAutocompletionListener(listener);
input.addKeyListener(listener);
}
fieldLayoutManager.addLayoutListener(autoCompleteMover);
@Override
public void dispose() {
super.dispose();
input.dispose();
}
protected void prepareLayout(ActionContext context) {
ComponentProvider prov = context.getComponentProvider();
if (cv != prov) {
if (cv != null) {
codepane.setLayout(null);
fieldLayoutManager.removeLayoutListener(autoCompleteMover);
}
cv = (CodeViewerProvider) prov;
listpane = cv.getListingPanel();
codepane = listpane.getFieldPanel();
fieldLayoutManager = new FieldPanelOverLayoutManager(codepane);
codepane.setLayout(fieldLayoutManager);
fieldLayoutManager.addLayoutListener(autoCompleteMover);
}
}
/**
@ -265,11 +271,13 @@ public class AssembleDockingAction extends DockingAction {
@Override
public void actionPerformed(ActionContext context) {
if (cv == null) {
onFirstInvocation();
if (!(context instanceof ListingActionContext)) {
return;
}
prepareLayout(context);
ListingActionContext lac = (ListingActionContext) context;
ProgramLocation cur = cv.getLocation();
ProgramLocation cur = lac.getLocation();
prog = cur.getProgram();
addr = cur.getAddress();
@ -356,6 +364,7 @@ public class AssembleDockingAction extends DockingAction {
@Override
public boolean isAddToPopup(ActionContext context) {
// currently only works on a listing
if (!(context instanceof ListingActionContext)) {
return false;

View file

@ -111,7 +111,6 @@ public class ElfSymbol implements ByteArrayConverter {
* @param reader to read symbol from
* @param symbolIndex index of the symbol to read
* @param symbolTable symbol table to associate the symbol to
* @param stringTable string table to read symbols from
* @param header else header
* @return newly created ElfSymbol
*

View file

@ -17,6 +17,7 @@ src/decompile/datatests/forloop_thruspecial.xml||GHIDRA||||END|
src/decompile/datatests/forloop_varused.xml||GHIDRA||||END|
src/decompile/datatests/forloop_withskip.xml||GHIDRA||||END|
src/decompile/datatests/loopcomment.xml||GHIDRA||||END|
src/decompile/datatests/multiret.xml||GHIDRA||||END|
src/decompile/datatests/namespace.xml||GHIDRA||||END|
src/decompile/datatests/nestedoffset.xml||GHIDRA||||END|
src/decompile/datatests/noforloop_alias.xml||GHIDRA||||END|

View file

@ -27,7 +27,7 @@
vector<ArchitectureCapability *> ArchitectureCapability::thelist;
const uint4 ArchitectureCapability::majorversion = 4;
const uint4 ArchitectureCapability::minorversion = 0;
const uint4 ArchitectureCapability::minorversion = 1;
/// This builds a list of just the ArchitectureCapability extensions
void ArchitectureCapability::initialize(void)

View file

@ -1852,7 +1852,7 @@ int4 ActionReturnRecovery::apply(Funcdata &data)
int4 slot = trial.getSlot();
vn = op->getIn(slot);
if (ancestorReal.execute(op,slot,&trial,false))
if (data.ancestorOpUse(maxancestor,vn,op,trial))
if (data.ancestorOpUse(maxancestor,vn,op,trial,0))
trial.markActive(); // This varnode sees active use as a parameter
count += 1;
}

View file

@ -27,7 +27,7 @@
implement these models provides the quickest inroad into
obtaining an overall understanding of the code.
We list all these fundemental classes here, loosely grouped
We list all these fundamental classes here, loosely grouped
as follows. There is one set of classes that describe the
\e Syntax \e Trees, which are built up from the original p-code,
and transformed during the decompiler's simplification process.
@ -43,18 +43,18 @@
- AddrSpace
- A place within the reverse engineering model where data
can be stored. The typical address spaces are \b ram,
modeling the main databus of a processor, and \b register,
modeling a processors on board registers. Data is stored a
byte at a time at \b offsets within the AddrSpace.
modeling the main databus of a processor, and \b register,
modeling a processor's on board registers. Data is stored a
byte at a time at \b offsets within the AddrSpace.
.
- Address
- An AddrSpace and an offset within the space forms the
Address of the byte at that offset.
Address of the byte at that offset.
.
- Varnode
- A contiguous set of bytes, given by an Address and a size,
encoding a single value in the model. In terms of SSA
syntax tree, a Varnode is also a node in the tree.
encoding a single value in the model. In terms of SSA
syntax tree, a Varnode is also a node in the tree.
.
- SeqNum
- A \e sequence \e number that extends Address for distinguishing PcodeOps
@ -161,14 +161,14 @@
and local scope.
\code
string & getName(); // get name of function
Address & getAddress(); // get Address of function's entry point
int4 numCalls(); // number of subfunctions called by this function
string & getName(); // get name of function
Address & getAddress(); // get Address of function's entry point
int4 numCalls(); // number of subfunctions called by this function
FuncCallSpecs *getCallSpecs(int4 i); // get specs for one of the subfunctions
BlockGraph & getBasicBlocks(); // get the collection of basic blocks
BlockGraph & getBasicBlocks(); // get the collection of basic blocks
iterator beginLoc(Address &); // Search for Varnodes in tree
iterator beginLoc(int4,Address &); // based on the Varnode's address
iterator beginLoc(int4,Address &); // based on the Varnode's address
iterator beginLoc(int4,Address &,Address &,uintm);
iterator beginDef(uint4,Address &); // Search for Varnode based on the
// address of its defining operation
@ -221,14 +221,14 @@
array, and structure qualifiers.
\code
class TypePointer : public Datatype { // pointer to (some other type)
Datatype *getBase(); // get Datatype being pointed to
class TypePointer : public Datatype { // pointer to (some other type)
Datatype *getBase(); // get Datatype being pointed to
};
class TypeArray : public Datatype { // array of (some other type)
Datatype *getBase(); // get Datatype of array element
class TypeArray : public Datatype { // array of (some other type)
Datatype *getBase(); // get Datatype of array element
};
class TypeStruct : public Datatype { // structure with fields of (some other types)
TypeField *getField(int4,int4,int4 *); // get Datatype of a field
class TypeStruct : public Datatype { // structure with fields of (some other types)
TypeField *getField(int4,int4,int4 *); // get Datatype of a field
};
\endcode
@ -237,12 +237,12 @@
This is a container for Datatypes.
\code
Datatype *findByName(string &); // find a Datatype by name
Datatype *getTypeVoid(); // retrieve common types
Datatype *findByName(string &); // find a Datatype by name
Datatype *getTypeVoid(); // retrieve common types
Datatype *getTypeChar();
Datatype *getBase(int4 size,type_metatype);
Datatype *getTypePointer(int4,Datatype *,uint4); // get a pointer to another type
Datatype *getTypeArray(int4,Datatype *); // get an array of another type
Datatype *getTypePointer(int4,Datatype *,uint4); // get a pointer to another type
Datatype *getTypeArray(int4,Datatype *); // get an array of another type
\endcode
\section classhighvariable HighVariable
@ -257,7 +257,7 @@
\code
int4 numInstances(); // get number of different Varnodes associated
// with this variable.
Varnode * getInstance(int4); // get (one of the) Varnodes associated with
Varnode * getInstance(int4); // get (one of the) Varnodes associated with
// this variable.
Datatype * getType(); // get Datatype of this variable
Symbol * getSymbol(); // get Symbol associated with this variable
@ -274,11 +274,11 @@
lives in a scope, has a name, and has a Datatype.
\code
string & getName(); // get the name of the symbol
Datatype * getType(); // get the Datatype of the symbol
Scope * getScope(); // get the scope containing the symbol
string & getName(); // get the name of the symbol
Datatype * getType(); // get the Datatype of the symbol
Scope * getScope(); // get the scope containing the symbol
SymbolEntry * getFirstWholeMap(); // get the (first) SymbolEntry associated
// with this symbol
// with this symbol
\endcode
\section classsymbolentry SymbolEntry
@ -300,16 +300,16 @@
This is a container for symbols.
\code
SymbolEntry *findAddr(Address &,Address &); // find a Symbol by address
SymbolEntry *findAddr(Address &,Address &); // find a Symbol by address
SymbolEntry *findContainer(Address &,int4,Address &); // find containing symbol
Funcdata * findFunction(Address &); // find a function by entry address
Symbol * findByName(string &); // find a Symbol by name
SymbolEntry *queryByAddr(Address &,Address &); // search for symbols across multiple scopes
Funcdata * findFunction(Address &); // find a function by entry address
Symbol * findByName(string &); // find a Symbol by name
SymbolEntry *queryByAddr(Address &,Address &); // search for symbols across multiple scopes
SymbolEntry *queryContainer(Address &,int4,Address &);
Funcdata * queryFunction(Address &);
Scope * discoverScope(Address &,int4,Address &); // discover scope of an address
string & getName(); // get name of scope
Scope * getParent(); // get parent scope
string & getName(); // get name of scope
Scope * getParent(); // get parent scope
\endcode
\section classdatabase Database

View file

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -38,7 +37,7 @@
the main code workflow.
The library provides its own Register
Transfer Languate (RTL), referred to internally as \b p-code,
Transfer Language (RTL), referred to internally as \b p-code,
which is designed specifically for reverse engineering
applications. The disassembly of processor specific machine-code
languages, and subsequent translation into \b p-code, forms
@ -275,7 +274,7 @@
about the variables it analyzes, as this kind of
information is generally not present in the input
binary. Some information can be gathered about a
variable, based on the instructions it is used in (.i.e
variable, based on the instructions it is used in (i.e.
if it is used in a floating point instruction). Other
information about type might be available from header
files or from the user. Once this is gathered, the
@ -301,7 +300,7 @@
compiler would, but to simplify and normalize for
easier understanding and recognition by human analysts
(and follow on machine processing). Typical examples
of transforms include, copy propagation, constant
of transforms include: copy propagation, constant
propagation, collecting terms, cancellation of
operators and other algebraic simplifications, undoing
multiplication and division optimizations, commuting
@ -373,7 +372,7 @@
Even after the initial merging of variables in phase 1,
there are generally still too many for normal C code. So
the decompiler, does additional, more speculative merging.
the decompiler does additional, more speculative merging.
It first tries to merge the inputs and outputs of copy
operations, and then the inputs and outputs of more
general operations. And finally, merging is attempted on

View file

@ -3174,17 +3174,20 @@ void FuncProto::setOutputLock(bool val)
store->getOutput()->setTypeLock(val);
}
/// This value can be used as a hint as to how much of the return value is important and
/// is used to inform the dead code \e consume algorithm.
/// \param val is the estimated number of bytes or 0
/// \return \b true if the value was changed
/// Provide a hint as to how many bytes of the return value are important.
/// The smallest hint is used to inform the dead-code removal algorithm.
/// \param val is the hint (number of bytes or 0 for all bytes)
/// \return \b true if the smallest hint has changed
bool FuncProto::setReturnBytesConsumed(int4 val)
{
int4 oldVal = returnBytesConsumed;
if (oldVal == 0 || val < oldVal)
if (val == 0)
return false;
if (returnBytesConsumed == 0 || val < returnBytesConsumed) {
returnBytesConsumed = val;
return (oldVal != val);
return true;
}
return false;
}
/// \brief Assuming \b this prototype is locked, calculate the \e extrapop
@ -4744,7 +4747,7 @@ void FuncCallSpecs::checkInputTrialUse(Funcdata &data,AliasChecker &aliascheck)
trial.markNoUse();
}
else if (ancestorReal.execute(op,slot,&trial,false)) {
if (data.ancestorOpUse(maxancestor,vn,op,trial))
if (data.ancestorOpUse(maxancestor,vn,op,trial,0))
trial.markActive();
else
trial.markInactive();
@ -4754,7 +4757,7 @@ void FuncCallSpecs::checkInputTrialUse(Funcdata &data,AliasChecker &aliascheck)
}
else {
if (ancestorReal.execute(op,slot,&trial,true)) {
if (data.ancestorOpUse(maxancestor,vn,op,trial)) {
if (data.ancestorOpUse(maxancestor,vn,op,trial,0)) {
trial.markActive();
if (trial.hasCondExeEffect())
activeinput.markNeedsFinalCheck();

View file

@ -58,6 +58,11 @@ class Funcdata {
baddata_present = 0x800, ///< Set if function flowed into bad data
double_precis_on = 0x1000 ///< Set if we are performing double precision recovery
};
enum {
traverse_actionalt = 1, ///< Alternate path traverses a solid action or \e non-incidental COPY
traverse_indirect = 2, ///< Main path traverses an INDIRECT
traverse_indirectalt = 4 ///< Alternate path traverses an INDIRECT
};
uint4 flags; ///< Boolean properties associated with \b this function
uint4 clean_up_index; ///< Creation index of first Varnode created after start of cleanup
uint4 high_level_index; ///< Creation index of first Varnode created after HighVariables are created
@ -116,6 +121,7 @@ class Funcdata {
void nodeSplitCloneVarnode(PcodeOp *op,PcodeOp *newop);
void nodeSplitRawDuplicate(BlockBasic *b,BlockBasic *bprime);
void nodeSplitInputPatch(BlockBasic *b,BlockBasic *bprime,int4 inedge);
static bool isAlternatePathValid(const Varnode *vn,uint4 flags);
static bool descendantsOutside(Varnode *vn);
static void saveVarnodeXml(ostream &s,VarnodeLocSet::const_iterator iter,VarnodeLocSet::const_iterator enditer);
static bool checkIndirectUse(Varnode *vn);
@ -363,9 +369,9 @@ public:
HighVariable *findHigh(const string &name) const; ///< Find a high-level variable by name
void mapGlobals(void); ///< Make sure there is a Symbol entry for all global Varnodes
bool checkCallDoubleUse(const PcodeOp *opmatch,const PcodeOp *op,const Varnode *vn,const ParamTrial &trial) const;
bool onlyOpUse(const Varnode *invn,const PcodeOp *opmatch,const ParamTrial &trial) const;
bool ancestorOpUse(int4 maxlevel,const Varnode *invn,const PcodeOp *op,ParamTrial &trial) const;
bool checkCallDoubleUse(const PcodeOp *opmatch,const PcodeOp *op,const Varnode *vn,uint4 flags,const ParamTrial &trial) const;
bool onlyOpUse(const Varnode *invn,const PcodeOp *opmatch,const ParamTrial &trial,uint4 mainFlags) const;
bool ancestorOpUse(int4 maxlevel,const Varnode *invn,const PcodeOp *op,ParamTrial &trial,uint4 mainFlags) const;
bool syncVarnodesWithSymbols(const ScopeLocal *lm,bool typesyes);
void transferVarnodeProperties(Varnode *vn,Varnode *newVn,int4 lsbOffset);
bool fillinReadOnly(Varnode *vn); ///< Replace the given Varnode with its (constant) value in the load image

View file

@ -1472,6 +1472,30 @@ void Funcdata::mapGlobals(void)
warningHeader("Globals starting with '_' overlap smaller symbols at the same address");
}
/// \brief Return \b true if the alternate path looks more valid than the main path.
///
/// Two different paths from a common Varnode each terminate at a CALL, CALLIND, or RETURN.
/// Evaluate which path most likely represents actual parameter/return value passing,
/// based on traversal information about each path.
/// \param vn is the Varnode terminating the \e alternate path
/// \param flags indicates traversals for both paths
/// \return \b true if the alternate path is preferred
bool Funcdata::isAlternatePathValid(const Varnode *vn,uint4 flags)
{
if ((flags & (traverse_indirect | traverse_indirectalt)) == traverse_indirect)
// If main path traversed an INDIRECT but the alternate did not
return true; // Main path traversed INDIRECT, alternate did not
if ((flags & (traverse_indirect | traverse_indirectalt)) == traverse_indirectalt)
return false; // Alternate path traversed INDIRECT, main did not
if ((flags & traverse_actionalt) != 0)
return true; // Alternate path traversed a dedicated COPY
if (vn->loneDescend() == (PcodeOp*)0) return false;
const PcodeOp *op = vn->getDef();
if (op == (PcodeOp*)0) return true;
return !op->isMarker(); // MULTIEQUAL or INDIRECT indicates multiple values
}
/// \brief Test for legitimate double use of a parameter trial
///
/// The given trial is a \e putative input to first CALL, but can also trace its data-flow
@ -1480,9 +1504,10 @@ void Funcdata::mapGlobals(void)
/// \param opmatch is the first CALL linked to the trial
/// \param op is the second CALL
/// \param vn is the Varnode parameter for the second CALL
/// \param flags indicates what p-code ops were crossed to reach \e vn
/// \param trial is the given parameter trial
/// \return \b true for a legitimate double use
bool Funcdata::checkCallDoubleUse(const PcodeOp *opmatch,const PcodeOp *op,const Varnode *vn,const ParamTrial &trial) const
bool Funcdata::checkCallDoubleUse(const PcodeOp *opmatch,const PcodeOp *op,const Varnode *vn,uint4 flags,const ParamTrial &trial) const
{
int4 j = op->getSlot(vn);
@ -1511,7 +1536,13 @@ bool Funcdata::checkCallDoubleUse(const PcodeOp *opmatch,const PcodeOp *op,const
if (fc->isInputActive()) {
const ParamTrial &curtrial( fc->getActiveInput()->getTrialForInputVarnode(j) );
if ((!curtrial.isChecked())||(!curtrial.isActive())) return true;
if (curtrial.isChecked()) {
if (curtrial.isActive())
return false;
}
else if (isAlternatePathValid(vn,flags))
return false;
return true;
}
return false;
}
@ -1523,28 +1554,31 @@ bool Funcdata::checkCallDoubleUse(const PcodeOp *opmatch,const PcodeOp *op,const
/// \param invn is the given Varnode
/// \param opmatch is the putative CALL op using the Varnode for parameter passing
/// \param trial is the parameter trial object associated with the Varnode
/// \param mainFlags are flags describing traversals along the \e main path, from \e invn to \e opmatch
/// \return \b true if the Varnode seems only to be used as parameter to \b opmatch
bool Funcdata::onlyOpUse(const Varnode *invn,const PcodeOp *opmatch,const ParamTrial &trial) const
bool Funcdata::onlyOpUse(const Varnode *invn,const PcodeOp *opmatch,const ParamTrial &trial,uint4 mainFlags) const
{
vector<const Varnode *> varlist;
vector<TraverseNode> varlist;
list<PcodeOp *>::const_iterator iter;
const Varnode *vn,*subvn;
const PcodeOp *op;
int4 i;
bool res = true;
varlist.reserve(64);
invn->setMark(); // Marks prevent infinite loops
varlist.push_back(invn);
varlist.emplace_back(invn,mainFlags);
i = 0;
while(i < varlist.size()) {
vn = varlist[i++];
for(i=0;i < varlist.size();++i) {
vn = varlist[i].vn;
uint4 baseFlags = varlist[i].flags;
for(iter=vn->descend.begin();iter!=vn->descend.end();++iter) {
op = *iter;
if (op == opmatch) {
if (op->getIn(trial.getSlot())==vn) continue;
}
uint4 curFlags = baseFlags;
switch(op->code()) {
case CPUI_BRANCH: // These ops define a USE of a variable
case CPUI_CBRANCH:
@ -1555,17 +1589,39 @@ bool Funcdata::onlyOpUse(const Varnode *invn,const PcodeOp *opmatch,const ParamT
break;
case CPUI_CALL:
case CPUI_CALLIND:
if (checkCallDoubleUse(opmatch,op,vn,trial)) continue;
if (checkCallDoubleUse(opmatch,op,vn,curFlags,trial)) continue;
res = false;
break;
case CPUI_INDIRECT:
curFlags |= Funcdata::traverse_indirectalt;
break;
case CPUI_COPY:
if ((op->getOut()->getSpace()->getType()!=IPTR_INTERNAL)&&!op->isIncidentalCopy()&&!vn->isIncidentalCopy()) {
curFlags |= Funcdata::traverse_actionalt;
}
break;
case CPUI_RETURN:
if (opmatch->code()==CPUI_RETURN) { // Are we in a different return
if (op->getIn(trial.getSlot())==vn) // But at the same slot
continue;
}
else if (activeoutput != (ParamActive *)0) { // Are we in the middle of analyzing returns
if (op->getIn(0) != vn) { // Unless we hold actual return value
if (!isAlternatePathValid(vn,curFlags))
continue; // Don't consider this a "use"
}
}
res = false;
break;
case CPUI_MULTIEQUAL:
case CPUI_PIECE:
case CPUI_SUBPIECE:
case CPUI_INT_SEXT:
case CPUI_INT_ZEXT:
case CPUI_CAST:
break;
default:
curFlags |= Funcdata::traverse_actionalt;
break;
}
if (!res) break;
@ -1576,7 +1632,7 @@ bool Funcdata::onlyOpUse(const Varnode *invn,const PcodeOp *opmatch,const ParamT
break;
}
if (!subvn->isMark()) {
varlist.push_back(subvn);
varlist.emplace_back(subvn,curFlags);
subvn->setMark();
}
}
@ -1584,7 +1640,7 @@ bool Funcdata::onlyOpUse(const Varnode *invn,const PcodeOp *opmatch,const ParamT
if (!res) break;
}
for(i=0;i<varlist.size();++i)
varlist[i]->clearMark();
varlist[i].vn->clearMark();
return res;
}
@ -1596,9 +1652,10 @@ bool Funcdata::onlyOpUse(const Varnode *invn,const PcodeOp *opmatch,const ParamT
/// \param invn is the given trial Varnode to test
/// \param op is the given CALL or RETURN
/// \param trial is the associated parameter trial object
/// \param mainFlags describes traversals along the path from \e invn to \e op
/// \return \b true if the Varnode is only used for the CALL/RETURN
bool Funcdata::ancestorOpUse(int4 maxlevel,const Varnode *invn,
const PcodeOp *op,ParamTrial &trial) const
const PcodeOp *op,ParamTrial &trial,uint4 mainFlags) const
{
int4 i;
@ -1610,7 +1667,7 @@ bool Funcdata::ancestorOpUse(int4 maxlevel,const Varnode *invn,
if (!invn->isTypeLock()) return false;
// If the input is typelocked
// this is as good as being written
return onlyOpUse(invn,op,trial); // Test if varnode is only used in op
return onlyOpUse(invn,op,trial,mainFlags); // Test if varnode is only used in op
}
const PcodeOp *def = invn->getDef();
@ -1620,7 +1677,7 @@ bool Funcdata::ancestorOpUse(int4 maxlevel,const Varnode *invn,
// as an "only use"
if (def->isIndirectCreation())
return false;
return ancestorOpUse(maxlevel-1,def->getIn(0),op,trial);
return ancestorOpUse(maxlevel-1,def->getIn(0),op,trial,mainFlags | Funcdata::traverse_indirect);
case CPUI_MULTIEQUAL:
// Check if there is any ancestor whose only
// use is in this op
@ -1628,7 +1685,7 @@ bool Funcdata::ancestorOpUse(int4 maxlevel,const Varnode *invn,
def->setMark(); // Mark that this MULTIEQUAL is on the path
// Note: onlyOpUse is using Varnode::setMark
for(i=0;i<def->numInput();++i) {
if (ancestorOpUse(maxlevel-1,def->getIn(i),op,trial)) {
if (ancestorOpUse(maxlevel-1,def->getIn(i),op,trial, mainFlags)) {
def->clearMark();
return true;
}
@ -1637,13 +1694,12 @@ bool Funcdata::ancestorOpUse(int4 maxlevel,const Varnode *invn,
return false;
case CPUI_COPY:
if ((invn->getSpace()->getType()==IPTR_INTERNAL)||def->isIncidentalCopy()||def->getIn(0)->isIncidentalCopy()) {
if (!ancestorOpUse(maxlevel-1,def->getIn(0),op,trial)) return false;
return true;
return ancestorOpUse(maxlevel-1,def->getIn(0),op,trial,mainFlags);
}
break;
case CPUI_PIECE:
// Concatenation tends to be artificial, so recurse through the least significant part
return ancestorOpUse(maxlevel-1,def->getIn(1),op,trial);
return ancestorOpUse(maxlevel-1,def->getIn(1),op,trial,mainFlags);
case CPUI_SUBPIECE:
// This is a rather kludgy way to get around where a DIV (or other similar) instruction
// causes a register that looks like the high precision piece of the function return
@ -1664,7 +1720,7 @@ bool Funcdata::ancestorOpUse(int4 maxlevel,const Varnode *invn,
break;
}
// This varnode must be top ancestor at this point
return onlyOpUse(invn,op,trial); // Test if varnode is only used in op
return onlyOpUse(invn,op,trial,mainFlags); // Test if varnode is only used in op
}
/// \return \b true if there are two input flows, one of which is a normal \e solid flow

View file

@ -130,6 +130,45 @@ FlowBlock *PriorityQueue::extract(void)
return res;
}
/// Initialize heritage state information for a particular address space
/// \param spc is the address space
HeritageInfo::HeritageInfo(AddrSpace *spc)
{
if (spc == (AddrSpace *)0) {
space = (AddrSpace *)0;
delay = 0;
deadcodedelay = 0;
hasCallPlaceholders = false;
}
else if (!spc->isHeritaged()) {
space = (AddrSpace *)0;
delay = spc->getDelay();
deadcodedelay = spc->getDeadcodeDelay();
hasCallPlaceholders = false;
}
else {
space = spc;
delay = spc->getDelay();
deadcodedelay = spc->getDeadcodeDelay();
hasCallPlaceholders = (spc->getType() == IPTR_SPACEBASE);
}
deadremoved = 0;
warningissued = false;
loadGuardSearch = false;
}
void HeritageInfo::reset(void)
{
// Leave any override intact: deadcodedelay = delay;
deadremoved = 0;
if (space != (AddrSpace *)0)
hasCallPlaceholders = (space->getType() == IPTR_SPACEBASE);
warningissued = false;
loadGuardSearch = false;
}
/// Instantiate the heritage manager for a particular function.
/// \param data is the function
Heritage::Heritage(Funcdata *data)
@ -1180,16 +1219,10 @@ void Heritage::guardCalls(uint4 flags,const Address &addr,int4 size,vector<Varno
uintb off = addr.getOffset();
bool tryregister = true;
if (spc->getType() == IPTR_SPACEBASE) {
if (fc->getStackPlaceholderSlot() < 0) { // Any stack resolution is complete (or never started)
if (fc->getSpacebaseOffset() != FuncCallSpecs::offset_unknown)
off = spc->wrapOffset(off - fc->getSpacebaseOffset());
else
tryregister = false; // Do not attempt to register this stack loc as a trial
}
else { // Stack has not been resolved, so we need to abort
fc->abortSpacebaseRelative(*fd);
tryregister = false;
}
if (fc->getSpacebaseOffset() != FuncCallSpecs::offset_unknown)
off = spc->wrapOffset(off - fc->getSpacebaseOffset());
else
tryregister = false; // Do not attempt to register this stack loc as a trial
}
Address transAddr(spc,off); // Address relative to callee's stack
if (tryregister) {
@ -1695,6 +1728,19 @@ static void verify_dfs(const vector<FlowBlock *> &list,vector<vector<FlowBlock *
}
#endif
/// Assuming we are just about to do heritage on an address space,
/// clear any placeholder LOADs associated with it on CALLs.
/// \param info is state for the specific address space
void Heritage::clearStackPlaceholders(HeritageInfo *info)
{
int4 numCalls = fd->numCalls();
for(int4 i=0;i<numCalls;++i) {
fd->getCallSpecs(i)->abortSpacebaseRelative(*fd);
}
info->hasCallPlaceholders = false; // Mark that clear has taken place
}
/// \brief Perform one level of Varnode splitting to match a JoinRecord
///
/// Split all the pieces in \b lastcombo, putting them into \b nextlev in order,
@ -2288,16 +2334,9 @@ void Heritage::buildInfoList(void)
{
if (!infolist.empty()) return;
const AddrSpaceManager *manage = fd->getArch();
infolist.resize(manage->numSpaces());
for(int4 i=0;i<manage->numSpaces();++i) {
AddrSpace *spc = manage->getSpace(i);
if (spc == (AddrSpace *)0)
infolist[i].set((AddrSpace *)0,0,0);
else if (!spc->isHeritaged())
infolist[i].set((AddrSpace *)0,spc->getDelay(),spc->getDeadcodeDelay());
else
infolist[i].set(spc,spc->getDelay(),spc->getDeadcodeDelay());
}
infolist.reserve(manage->numSpaces());
for(int4 i=0;i<manage->numSpaces();++i)
infolist.emplace_back(manage->getSpace(i));
}
/// From any address space that is active for this pass, free Varnodes are collected
@ -2328,6 +2367,9 @@ void Heritage::heritage(void)
info = &infolist[i];
if (!info->isHeritaged()) continue;
if (pass < info->delay) continue; // It is too soon to heritage this space
if (info->hasCallPlaceholders)
clearStackPlaceholders(info);
if (!info->loadGuardSearch) {
info->loadGuardSearch = true;
if (discoverIndexedStackPointers(info->space,freeStores,true)) {

View file

@ -90,11 +90,11 @@ class HeritageInfo {
int4 deadremoved; ///< >0 if Varnodes in this space have been eliminated
bool loadGuardSearch; ///< \b true if the search for LOAD ops to guard has been performed
bool warningissued; ///< \b true if warning issued previously
void set(AddrSpace *spc,int4 dl,int4 dcdl) {
space=spc; delay=dl; deadcodedelay=dcdl; deadremoved=0; warningissued=false; loadGuardSearch = false; } ///< Set all fields
bool hasCallPlaceholders; ///< \b true for the \e stack space, if stack placeholders have not been removed
bool isHeritaged(void) const { return (space != (AddrSpace *)0); } ///< Return \b true if heritage is performed on this space
void reset(void) {
deadremoved = 0; deadcodedelay = delay; warningissued = false; loadGuardSearch = false; } ///< Reset
void reset(void); ///< Reset the state
public:
HeritageInfo(AddrSpace *spc); ///< Constructor
};
/// \brief Description of a LOAD operation that needs to be guarded
@ -222,6 +222,7 @@ class Heritage {
/// \brief Get the heritage status for the given address space
const HeritageInfo *getInfo(AddrSpace *spc) const { return &(infolist[spc->getIndex()]); }
void clearStackPlaceholders(HeritageInfo *info); ///< Clear remaining stack placeholder LOADs on any call
void splitJoinLevel(vector<Varnode *> &lastcombo,vector<Varnode *> &nextlev,JoinRecord *joinrec);
void splitJoinRead(Varnode *vn,JoinRecord *joinrec);
void splitJoinWrite(Varnode *vn,JoinRecord *joinrec);

View file

@ -376,6 +376,13 @@ public:
#endif
};
/// \brief Node for a forward traversal of a Varnode expression
struct TraverseNode {
const Varnode *vn; ///< Varnode at the point of traversal
uint4 flags; ///< Flags associated with the node
TraverseNode(const Varnode *v,uint4 f) { vn = v; flags = f; }
};
bool contiguous_test(Varnode *vn1,Varnode *vn2); ///< Test if Varnodes are pieces of a whole
Varnode *findContiguousWhole(Funcdata &data,Varnode *vn1,
Varnode *vn2); ///< Retrieve the whole Varnode given pieces

View file

@ -0,0 +1,30 @@
<decompilertest>
<binaryimage arch="x86:LE:64:default:gcc">
<!--
Contrived example that seems to set up multiple return value sizes.
This used to make the decompiler enter an infinite loop.
-->
<bytechunk space="ram" offset="0x100000" readonly="true">
4883ec1864488b042528000000488944
240831c083fe01744f83fe02742a897c
2404488d7c2404e8dc0f00008b442404
488b542408644833142528000000753d
4883c418c3000000488d7c2404b8e903
00006689442404e8ac0f00008b442404
ebce000000000000488d7c2404c64424
0461e8910f00008b442404ebb3e88e0f
0000
</bytechunk>
</binaryimage>
<script>
<com>map fun r0x100000 getval</com>
<com>override flow r0x10007d callreturn</com>
<com>lo fu getval</com>
<com>decompile</com>
<com>print C</com>
<com>quit</com>
</script>
<stringmatch name="Multi-size return #1" min="1" max="1">Stack20 = CONCAT31.*0x61</stringmatch>
<stringmatch name="Multi-size return #2" min="1" max="1">Stack20 = CONCAT22.*0x3e9</stringmatch>
<stringmatch name="Multi-size return #3" min="1" max="1">Stack20 = param_1</stringmatch>
</decompilertest>

View file

@ -2508,6 +2508,17 @@
</para>
</listitem>
</varlistentry>
<varlistentry id="GeneralMaxInstruction">
<term><emphasis role="bold">Max Instructions per Function</emphasis></term>
<listitem>
<para>
This option sets a maximum number of machine instructions that the decompiler will attempt
to analyze for a single function, as a safeguard against analyzing a long sequence
of zeroes or other constant data. The decompiler will quickly throw an exception if it
traces control-flow into more than the indicated number of instructions.
</para>
</listitem>
</varlistentry>
</variablelist>
</informalexample>
</para>

View file

@ -75,21 +75,21 @@ public class DecompileOptions {
private final static String IGNOREUNIMPL_OPTIONDESCRIPTION =
"If set, instructions which do not have a p-code translation implemented are " +
"treated as if they do nothing (like a NOP)";
private final static boolean IGNOREUNIMPL_OPTIONDEFAULT = false;
private final static boolean IGNOREUNIMPL_OPTIONDEFAULT = false; // Must match Architecture::resetDefaultsInternal
private boolean ignoreunimpl;
private final static String INFERCONSTPTR_OPTIONSTRING = "Analysis.Infer constant pointers";
private final static String INFERCONSTPTR_OPTIONDESCRIPTION =
"If set, constants which are not being explicitly used as pointers, but which can be interpreted " +
"as a legitimate address, will still be treated as having a pointer datatype";
private final static boolean INFERCONSTPTR_OPTIONDEFAULT = true;
private final static boolean INFERCONSTPTR_OPTIONDEFAULT = true; // Must match Architecture::resetDefaultsInternal
private boolean inferconstptr;
private final static String ANALYZEFORLOOPS_OPTIONSTRING = "Analysis.Recover -for- loops";
private final static String ANALYZEFORLOOPS_OPTIONDESCRIPTION =
"If set, the decompiler attempts to recover for-loop variables, including their initializer, condition, " +
"and incrementer statements. Loop variable bounds are displayed as a formal -for- loop header";
private final static boolean ANALYZEFORLOOPS_OPTIONDEFAULT = true;
private final static boolean ANALYZEFORLOOPS_OPTIONDEFAULT = true; // Must match Architecture::resetDefaultsInternal
private boolean analyzeForLoops;
private final static String NULLTOKEN_OPTIONSTRING = "Display.Print 'NULL' for null pointers";
@ -97,7 +97,7 @@ public class DecompileOptions {
"If set, any zero valued pointer (null pointer) will " +
"be printed using the token 'NULL'. Otherwise, a cast " +
"of the number '0' is printed.";
private final static boolean NULLTOKEN_OPTIONDEFAULT = false;
private final static boolean NULLTOKEN_OPTIONDEFAULT = false; // Must match PrintC::resetDefaultsPrintC
private boolean nullToken;
private final static String INPLACEOP_OPTIONSTRING =
@ -105,7 +105,7 @@ public class DecompileOptions {
private final static String INPLACEOP_OPTIONDESCRIPTION =
"If set the inplace assignment operators will be used " +
"for appropriate expressions. '+=' '*=' '&=' '<<=' etc.";
private final static boolean INPLACEOP_OPTIONDEFAULT = false;
private final static boolean INPLACEOP_OPTIONDEFAULT = false; // Must match PrintC::resetDefaultsPrintC
private boolean inplaceTokens;
private final static String ALIASBLOCK_OPTIONSTRING = "Analysis.Alias Blocking";
@ -137,40 +137,40 @@ public class DecompileOptions {
}
}
private final static AliasBlockEnum ALIASBLOCK_OPTIONDEFAULT = AliasBlockEnum.Array;
private final static AliasBlockEnum ALIASBLOCK_OPTIONDEFAULT = AliasBlockEnum.Array; // Must match Architecture::resetDefaultsInternal
private AliasBlockEnum aliasBlock;
private final static String CONVENTION_OPTIONSTRING = "Display.Print calling convention name";
private final static String CONVENTION_OPTIONDESCRIPTION =
"If set, the names of callling conventions (which differ " +
"from the default) will be printed as part of the function prototype.";
private final static boolean CONVENTION_OPTIONDEFAULT = true;
private final static boolean CONVENTION_OPTIONDEFAULT = true; // Must match PrintC::resetDefaultsPrintC
private boolean conventionPrint;
private final static String NOCAST_OPTIONSTRING = "Display.Disable printing of type casts";
private final static String NOCAST_OPTIONDESCRIPTION =
"If set, any C style type cast recovered by the decompiler will not be displayed. " +
"The resulting C syntax may not parse correctly.";
private final static boolean NOCAST_OPTIONDEFAULT = false;
private final static boolean NOCAST_OPTIONDEFAULT = false; // Must match PrintC::resetDefaultsPrintC
private boolean noCastPrint;
private final static String MAXWIDTH_OPTIONSTRING = "Display.Maximum characters in a code line";
private final static String MAXWIDTH_OPTIONDESCRIPTION =
"Maximum number of characters allowed per line before " + "before line breaks are forced.";
private final static int MAXWIDTH_OPTIONDEFAULT = 100;
private final static int MAXWIDTH_OPTIONDEFAULT = 100; // Must match EmitPrettyPrint::resetDefaultsPrettyPrint
private int maxwidth;
private final static String INDENTWIDTH_OPTIONSTRING =
"Display.Number of characters per indent level";
private final static String INDENTWIDTH_OPTIONDESCRIPTION =
"Number of characters indented for each level of control-flow " + "or scope nesting";
private final static int INDENTWIDTH_OPTIONDEFAULT = 2;
private final static int INDENTWIDTH_OPTIONDEFAULT = 2; // Must match EmitXml::resetDefaultsInternal
private int indentwidth;
private final static String COMMENTINDENT_OPTIONSTRING = "Display.Comment line indent level";
private final static String COMMENTINDENT_OPTIONDESCRIPTION =
"Number of characters each line of comments is indented";
private final static int COMMENTINDENT_OPTIONDEFAULT = 20;
private final static int COMMENTINDENT_OPTIONDEFAULT = 20; // Must match PrintLanguage::resetDefaultsInternal
private int commentindent;
private final static String COMMENTSTYLE_OPTIONSTRING = "Display.Comment style";
@ -178,6 +178,7 @@ public class DecompileOptions {
"Choice between either the C style comments /* */ or C++ style // ";
public static final int SUGGESTED_DECOMPILE_TIMEOUT_SECS = 30;
public static final int SUGGESTED_MAX_PAYLOAD_BYTES = 50;
public static final int SUGGESTED_MAX_INSTRUCTIONS = 100000; // Must match Architecture::resetDefaultsInternal
public enum CommentStyleEnum {
@ -195,53 +196,51 @@ public class DecompileOptions {
}
}
private final static CommentStyleEnum COMMENTSTYLE_OPTIONDEFAULT = CommentStyleEnum.CStyle;
private final static CommentStyleEnum COMMENTSTYLE_OPTIONDEFAULT = CommentStyleEnum.CStyle; // Must match PrintC::resetDefaultsPrintC
private CommentStyleEnum commentStyle;
private final static String COMMENTPRE_OPTIONSTRING = "Display.Display PRE comments";
private final static String COMMENTPRE_OPTIONDESCRIPTION =
"If set, disassembly pre-instruction (PRE) comments are displayed " +
"in the decompiler C output";
private final static boolean COMMENTPRE_OPTIONDEFAULT = true;
private final static boolean COMMENTPRE_OPTIONDEFAULT = true; // Must match PrintLanguage::resetDefaultsInternal
private boolean commentPREInclude;
private final static String COMMENTPLATE_OPTIONSTRING = "Display.Display PLATE comments";
private final static String COMMENTPLATE_OPTIONDESCRIPTION =
"If set, disassembly plate comments are displayed " + "in the decompiler C output";
private final static boolean COMMENTPLATE_OPTIONDEFAULT = false;
private final static boolean COMMENTPLATE_OPTIONDEFAULT = false; // Must match PrintLanguage::resetDefaultsInternal
private boolean commentPLATEInclude;
private final static String COMMENTPOST_OPTIONSTRING = "Display.Display POST comments";
private final static String COMMENTPOST_OPTIONDESCRIPTION =
"If set, disassembly post-instruction (POST) comments are displayed " +
"in the decompiler C output";
private final static boolean COMMENTPOST_OPTIONDEFAULT = false;
private final static boolean COMMENTPOST_OPTIONDEFAULT = false; // Must match PrintLanguage::resetDefaultsInternal
private boolean commentPOSTInclude;
private final static String COMMENTEOL_OPTIONSTRING = "Display.Display EOL comments";
private final static String COMMENTEOL_OPTIONDESCRIPTION =
"If set, disassembly end-of-line (EOL) comments are displayed " +
"in the decompiler C output";
private final static boolean COMMENTEOL_OPTIONDEFAULT = false;
private final static boolean COMMENTEOL_OPTIONDEFAULT = false; // Must match PrintLanguage::resetDefaultsInternal
private boolean commentEOLInclude;
private final static String COMMENTWARN_OPTIONSTRING = "Display.Display Warning comments";
private final static String COMMENTWARN_OPTIONDESCRIPTION =
"If set, warnings generated by the decompiler embedded in the displayed " +
"code as comments";
private final static boolean COMMENTWARN_OPTIONDEFAULT = true;
private final static boolean COMMENTWARN_OPTIONDEFAULT = true; // Must match PrintLanguage::resetDefaultsInternal
private boolean commentWARNInclude;
private final static String COMMENTHEAD_OPTIONSTRING = "Display.Display Header comment";
private final static String COMMENTHEAD_OPTIONDESCRIPTION =
"If set, the entry point plate comment is displayed as " + "a function header comment.";
private final static boolean COMMENTHEAD_OPTIONDEFAULT = true;
private final static boolean COMMENTHEAD_OPTIONDEFAULT = true; // Must match PrintLanguage::resetDefaultsInternal
private boolean commentHeadInclude;
public enum NamespaceStrategy {
Minimal("minimal", "Minimally"),
All("all", "Always"),
Never("none", "Never");
Minimal("minimal", "Minimally"), All("all", "Always"), Never("none", "Never");
private String label;
private String optionString;
@ -264,7 +263,7 @@ public class DecompileOptions {
private final static String NAMESPACE_OPTIONSTRING = "Display.Display Namespaces";
private final static String NAMESPACE_OPTIONDESCRIPTION =
"Choose how/if namespace tokens should be displayed along with symbol names";
private final static NamespaceStrategy NAMESPACE_OPTIONDEFAULT = NamespaceStrategy.Minimal;
private final static NamespaceStrategy NAMESPACE_OPTIONDEFAULT = NamespaceStrategy.Minimal; // Must match PrintLanguage::resetDefaultsInternal
private NamespaceStrategy namespaceStrategy;
private final static String INTEGERFORMAT_OPTIONSTRING = "Display.Integer format";
@ -295,7 +294,7 @@ public class DecompileOptions {
}
}
private final static IntegerFormatEnum INTEGERFORMAT_OPTIONDEFAULT = IntegerFormatEnum.BestFit;
private final static IntegerFormatEnum INTEGERFORMAT_OPTIONDEFAULT = IntegerFormatEnum.BestFit; // Must match PrintLanguage::resetDefaultsInternal
private IntegerFormatEnum integerFormat;
private final static Color HIGHLIGHT_MIDDLE_MOUSE_DEF = new Color(255, 255, 0, 128);
@ -356,10 +355,12 @@ public class DecompileOptions {
private final static String LINE_NUMBER_MSG = "Display.Display Line Numbers";
private final static String DECOMPILE_TIMEOUT = "Decompiler Timeout (seconds)";
private final static String PAYLOAD_LIMIT = "Decompiler Max-Payload (MBytes)";
private final static String MAX_INSTRUCTIONS = "Max Instructions per Function";
private final static Boolean LINE_NUMBER_DEF = Boolean.TRUE;
private boolean displayLineNumbers;
private int decompileTimeoutSeconds;
private int payloadLimitMBytes;
private int maxIntructionsPer;
private int cachedResultsSize;
private DecompilerLanguage displayLanguage; // Output language displayed by the decompiler
@ -407,6 +408,7 @@ public class DecompileOptions {
protoEvalModel = "default";
decompileTimeoutSeconds = SUGGESTED_DECOMPILE_TIMEOUT_SECS;
payloadLimitMBytes = SUGGESTED_MAX_PAYLOAD_BYTES;
maxIntructionsPer = SUGGESTED_MAX_INSTRUCTIONS;
cachedResultsSize = SUGGESTED_CACHED_RESULTS_SIZE;
}
@ -472,6 +474,7 @@ public class DecompileOptions {
displayLineNumbers = opt.getBoolean(LINE_NUMBER_MSG, LINE_NUMBER_DEF);
decompileTimeoutSeconds = opt.getInt(DECOMPILE_TIMEOUT, SUGGESTED_DECOMPILE_TIMEOUT_SECS);
payloadLimitMBytes = opt.getInt(PAYLOAD_LIMIT, SUGGESTED_MAX_PAYLOAD_BYTES);
maxIntructionsPer = opt.getInt(MAX_INSTRUCTIONS, SUGGESTED_MAX_INSTRUCTIONS);
cachedResultsSize = opt.getInt(CACHED_RESULTS_SIZE_MSG, SUGGESTED_CACHED_RESULTS_SIZE);
grabFromToolOptions(ownerPlugin);
@ -533,172 +536,132 @@ public class DecompileOptions {
* @param program the program
*/
public void registerOptions(Plugin ownerPlugin, ToolOptions opt, Program program) {
opt.registerOption(PREDICATE_OPTIONSTRING,
PREDICATE_OPTIONDEFAULT,
opt.registerOption(PREDICATE_OPTIONSTRING, PREDICATE_OPTIONDEFAULT,
new HelpLocation(HelpTopics.DECOMPILER, "AnalysisPredicate"),
PREDICATE_OPTIONDESCRIPTION);
opt.registerOption(READONLY_OPTIONSTRING,
READONLY_OPTIONDEFAULT,
opt.registerOption(READONLY_OPTIONSTRING, READONLY_OPTIONDEFAULT,
new HelpLocation(HelpTopics.DECOMPILER, "AnalysisReadOnly"),
READONLY_OPTIONDESCRIPTION);
opt.registerOption(ELIMINATE_UNREACHABLE_OPTIONSTRING,
ELIMINATE_UNREACHABLE_OPTIONDEFAULT,
opt.registerOption(ELIMINATE_UNREACHABLE_OPTIONSTRING, ELIMINATE_UNREACHABLE_OPTIONDEFAULT,
new HelpLocation(HelpTopics.DECOMPILER, "AnalysisUnreachable"),
ELIMINATE_UNREACHABLE_OPTIONDESCRIPTION);
opt.registerOption(SIMPLIFY_DOUBLEPRECISION_OPTIONSTRING,
SIMPLIFY_DOUBLEPRECISION_OPTIONDEFAULT,
new HelpLocation(HelpTopics.DECOMPILER, "AnalysisExtendedPrecision"),
SIMPLIFY_DOUBLEPRECISION_OPTIONDESCRIPTION);
opt.registerOption(IGNOREUNIMPL_OPTIONSTRING,
IGNOREUNIMPL_OPTIONDEFAULT,
opt.registerOption(IGNOREUNIMPL_OPTIONSTRING, IGNOREUNIMPL_OPTIONDEFAULT,
new HelpLocation(HelpTopics.DECOMPILER, "AnalysisIgnoreUnimplemented"),
IGNOREUNIMPL_OPTIONDESCRIPTION);
opt.registerOption(INFERCONSTPTR_OPTIONSTRING,
INFERCONSTPTR_OPTIONDEFAULT,
opt.registerOption(INFERCONSTPTR_OPTIONSTRING, INFERCONSTPTR_OPTIONDEFAULT,
new HelpLocation(HelpTopics.DECOMPILER, "AnalysisInferConstants"),
INFERCONSTPTR_OPTIONDESCRIPTION);
opt.registerOption(ANALYZEFORLOOPS_OPTIONSTRING,
ANALYZEFORLOOPS_OPTIONDEFAULT,
opt.registerOption(ANALYZEFORLOOPS_OPTIONSTRING, ANALYZEFORLOOPS_OPTIONDEFAULT,
new HelpLocation(HelpTopics.DECOMPILER, "AnalysisForLoops"),
ANALYZEFORLOOPS_OPTIONDESCRIPTION);
opt.registerOption(NULLTOKEN_OPTIONSTRING,
NULLTOKEN_OPTIONDEFAULT,
new HelpLocation(HelpTopics.DECOMPILER, "DisplayNull"),
NULLTOKEN_OPTIONDESCRIPTION);
opt.registerOption(INPLACEOP_OPTIONSTRING,
INPLACEOP_OPTIONDEFAULT,
opt.registerOption(NULLTOKEN_OPTIONSTRING, NULLTOKEN_OPTIONDEFAULT,
new HelpLocation(HelpTopics.DECOMPILER, "DisplayNull"), NULLTOKEN_OPTIONDESCRIPTION);
opt.registerOption(INPLACEOP_OPTIONSTRING, INPLACEOP_OPTIONDEFAULT,
new HelpLocation(HelpTopics.DECOMPILER, "AnalysisInPlace"),
INPLACEOP_OPTIONDESCRIPTION);
opt.registerOption(ALIASBLOCK_OPTIONSTRING,
ALIASBLOCK_OPTIONDEFAULT,
opt.registerOption(ALIASBLOCK_OPTIONSTRING, ALIASBLOCK_OPTIONDEFAULT,
new HelpLocation(HelpTopics.DECOMPILER, "AnalysisAliasBlocking"),
ALIASBLOCK_OPTIONDESCRIPTION);
opt.registerOption(CONVENTION_OPTIONSTRING,
CONVENTION_OPTIONDEFAULT,
opt.registerOption(CONVENTION_OPTIONSTRING, CONVENTION_OPTIONDEFAULT,
new HelpLocation(HelpTopics.DECOMPILER, "DisplayConvention"),
CONVENTION_OPTIONDESCRIPTION);
opt.registerOption(NOCAST_OPTIONSTRING,
NOCAST_OPTIONDEFAULT,
opt.registerOption(NOCAST_OPTIONSTRING, NOCAST_OPTIONDEFAULT,
new HelpLocation(HelpTopics.DECOMPILER, "DisplayDisableCasts"),
NOCAST_OPTIONDESCRIPTION);
opt.registerOption(MAXWIDTH_OPTIONSTRING,
MAXWIDTH_OPTIONDEFAULT,
new HelpLocation(HelpTopics.DECOMPILER, "DisplayMaxChar"),
MAXWIDTH_OPTIONDESCRIPTION);
opt.registerOption(INDENTWIDTH_OPTIONSTRING,
INDENTWIDTH_OPTIONDEFAULT,
opt.registerOption(MAXWIDTH_OPTIONSTRING, MAXWIDTH_OPTIONDEFAULT,
new HelpLocation(HelpTopics.DECOMPILER, "DisplayMaxChar"), MAXWIDTH_OPTIONDESCRIPTION);
opt.registerOption(INDENTWIDTH_OPTIONSTRING, INDENTWIDTH_OPTIONDEFAULT,
new HelpLocation(HelpTopics.DECOMPILER, "DisplayIndentLevel"),
INDENTWIDTH_OPTIONDESCRIPTION);
opt.registerOption(COMMENTINDENT_OPTIONSTRING,
COMMENTINDENT_OPTIONDEFAULT,
opt.registerOption(COMMENTINDENT_OPTIONSTRING, COMMENTINDENT_OPTIONDEFAULT,
new HelpLocation(HelpTopics.DECOMPILER, "DisplayCommentIndent"),
COMMENTINDENT_OPTIONDESCRIPTION);
opt.registerOption(COMMENTSTYLE_OPTIONSTRING,
COMMENTSTYLE_OPTIONDEFAULT,
opt.registerOption(COMMENTSTYLE_OPTIONSTRING, COMMENTSTYLE_OPTIONDEFAULT,
new HelpLocation(HelpTopics.DECOMPILER, "DisplayCommentStyle"),
COMMENTSTYLE_OPTIONDESCRIPTION);
opt.registerOption(COMMENTEOL_OPTIONSTRING,
COMMENTEOL_OPTIONDEFAULT,
opt.registerOption(COMMENTEOL_OPTIONSTRING, COMMENTEOL_OPTIONDEFAULT,
new HelpLocation(HelpTopics.DECOMPILER, "CommentOptions"),
COMMENTEOL_OPTIONDESCRIPTION);
opt.registerOption(COMMENTPRE_OPTIONSTRING,
COMMENTPRE_OPTIONDEFAULT,
opt.registerOption(COMMENTPRE_OPTIONSTRING, COMMENTPRE_OPTIONDEFAULT,
new HelpLocation(HelpTopics.DECOMPILER, "CommentOptions"),
COMMENTPRE_OPTIONDESCRIPTION);
opt.registerOption(COMMENTPOST_OPTIONSTRING,
COMMENTPOST_OPTIONDEFAULT,
opt.registerOption(COMMENTPOST_OPTIONSTRING, COMMENTPOST_OPTIONDEFAULT,
new HelpLocation(HelpTopics.DECOMPILER, "CommentOptions"),
COMMENTPOST_OPTIONDESCRIPTION);
opt.registerOption(COMMENTPLATE_OPTIONSTRING,
COMMENTPLATE_OPTIONDEFAULT,
opt.registerOption(COMMENTPLATE_OPTIONSTRING, COMMENTPLATE_OPTIONDEFAULT,
new HelpLocation(HelpTopics.DECOMPILER, "CommentOptions"),
COMMENTPLATE_OPTIONDESCRIPTION);
opt.registerOption(COMMENTWARN_OPTIONSTRING,
COMMENTWARN_OPTIONDEFAULT,
opt.registerOption(COMMENTWARN_OPTIONSTRING, COMMENTWARN_OPTIONDEFAULT,
new HelpLocation(HelpTopics.DECOMPILER, "DisplayWarningComments"),
COMMENTWARN_OPTIONDESCRIPTION);
opt.registerOption(COMMENTHEAD_OPTIONSTRING,
COMMENTHEAD_OPTIONDEFAULT,
opt.registerOption(COMMENTHEAD_OPTIONSTRING, COMMENTHEAD_OPTIONDEFAULT,
new HelpLocation(HelpTopics.DECOMPILER, "DisplayHeaderComment"),
COMMENTHEAD_OPTIONDESCRIPTION);
opt.registerOption(NAMESPACE_OPTIONSTRING,
NAMESPACE_OPTIONDEFAULT,
opt.registerOption(NAMESPACE_OPTIONSTRING, NAMESPACE_OPTIONDEFAULT,
new HelpLocation(HelpTopics.DECOMPILER, "DisplayNamespaces"),
NAMESPACE_OPTIONDESCRIPTION);
opt.registerOption(INTEGERFORMAT_OPTIONSTRING,
INTEGERFORMAT_OPTIONDEFAULT,
opt.registerOption(INTEGERFORMAT_OPTIONSTRING, INTEGERFORMAT_OPTIONDEFAULT,
new HelpLocation(HelpTopics.DECOMPILER, "DisplayIntegerFormat"),
INTEGERFORMAT_OPTIONDESCRIPTION);
opt.registerOption(HIGHLIGHT_KEYWORD_MSG,
HIGHLIGHT_KEYWORD_DEF,
opt.registerOption(HIGHLIGHT_KEYWORD_MSG, HIGHLIGHT_KEYWORD_DEF,
new HelpLocation(HelpTopics.DECOMPILER, "DisplayTokenColor"),
"Color used for highlighting keywords.");
opt.registerOption(HIGHLIGHT_TYPE_MSG,
HIGHLIGHT_TYPE_DEF,
opt.registerOption(HIGHLIGHT_TYPE_MSG, HIGHLIGHT_TYPE_DEF,
new HelpLocation(HelpTopics.DECOMPILER, "DisplayTokenColor"),
"Color used for highlighting types.");
opt.registerOption(HIGHLIGHT_FUNCTION_MSG,
HIGHLIGHT_FUNCTION_DEF,
opt.registerOption(HIGHLIGHT_FUNCTION_MSG, HIGHLIGHT_FUNCTION_DEF,
new HelpLocation(HelpTopics.DECOMPILER, "DisplayTokenColor"),
"Color used for highlighting function names.");
opt.registerOption(HIGHLIGHT_COMMENT_MSG,
HIGHLIGHT_COMMENT_DEF,
opt.registerOption(HIGHLIGHT_COMMENT_MSG, HIGHLIGHT_COMMENT_DEF,
new HelpLocation(HelpTopics.DECOMPILER, "DisplayTokenColor"),
"Color used for highlighting comments.");
opt.registerOption(HIGHLIGHT_VARIABLE_MSG,
HIGHLIGHT_VARIABLE_DEF,
opt.registerOption(HIGHLIGHT_VARIABLE_MSG, HIGHLIGHT_VARIABLE_DEF,
new HelpLocation(HelpTopics.DECOMPILER, "DisplayTokenColor"),
"Color used for highlighting variables.");
opt.registerOption(HIGHLIGHT_CONST_MSG,
HIGHLIGHT_CONST_DEF,
opt.registerOption(HIGHLIGHT_CONST_MSG, HIGHLIGHT_CONST_DEF,
new HelpLocation(HelpTopics.DECOMPILER, "DisplayTokenColor"),
"Color used for highlighting constants.");
opt.registerOption(HIGHLIGHT_PARAMETER_MSG,
HIGHLIGHT_PARAMETER_DEF,
opt.registerOption(HIGHLIGHT_PARAMETER_MSG, HIGHLIGHT_PARAMETER_DEF,
new HelpLocation(HelpTopics.DECOMPILER, "DisplayTokenColor"),
"Color used for highlighting parameters.");
opt.registerOption(HIGHLIGHT_GLOBAL_MSG,
HIGHLIGHT_GLOBAL_DEF,
opt.registerOption(HIGHLIGHT_GLOBAL_MSG, HIGHLIGHT_GLOBAL_DEF,
new HelpLocation(HelpTopics.DECOMPILER, "DisplayTokenColor"),
"Color used for highlighting global variables.");
opt.registerOption(HIGHLIGHT_DEFAULT_MSG,
HIGHLIGHT_DEFAULT_DEF,
opt.registerOption(HIGHLIGHT_DEFAULT_MSG, HIGHLIGHT_DEFAULT_DEF,
new HelpLocation(HelpTopics.DECOMPILER, "DisplayColorDefault"),
"The color used when a specific color is not specified.");
opt.registerOption(CODE_VIEWER_BACKGROUND_COLOR_MSG,
CODE_VIEWER_BACKGROUND_COLOR,
opt.registerOption(CODE_VIEWER_BACKGROUND_COLOR_MSG, CODE_VIEWER_BACKGROUND_COLOR,
new HelpLocation(HelpTopics.DECOMPILER, "DisplayBackgroundColor"),
"The background color of the decompiler window.");
opt.registerOption(FONT_MSG,
DEFAULT_FONT,
opt.registerOption(FONT_MSG, DEFAULT_FONT,
new HelpLocation(HelpTopics.DECOMPILER, "DisplayFont"),
"The font used to render text in the decompiler.");
opt.registerOption(SEARCH_HIGHLIGHT_MSG,
SEARCH_HIGHLIGHT_DEF,
opt.registerOption(SEARCH_HIGHLIGHT_MSG, SEARCH_HIGHLIGHT_DEF,
new HelpLocation(HelpTopics.DECOMPILER, "DisplayFindHighlight"),
"The color used to highlight matches using the Find Dialog.");
opt.registerOption(LINE_NUMBER_MSG,
LINE_NUMBER_DEF,
opt.registerOption(LINE_NUMBER_MSG, LINE_NUMBER_DEF,
new HelpLocation(HelpTopics.DECOMPILER, "DisplayLineNumbers"),
"Toggle for displaying line numbers in the decompiler.");
opt.registerOption(DECOMPILE_TIMEOUT,
SUGGESTED_DECOMPILE_TIMEOUT_SECS,
opt.registerOption(DECOMPILE_TIMEOUT, SUGGESTED_DECOMPILE_TIMEOUT_SECS,
new HelpLocation(HelpTopics.DECOMPILER, "GeneralTimeout"),
"The number of seconds to allow the decompiler to run before terminating the " +
"decompiler.\nCurrently this does not affect the UI, which will run indefinitely. " +
"This setting currently only affects background analysis that uses the decompiler.");
opt.registerOption(PAYLOAD_LIMIT,
SUGGESTED_MAX_PAYLOAD_BYTES,
opt.registerOption(PAYLOAD_LIMIT, SUGGESTED_MAX_PAYLOAD_BYTES,
new HelpLocation(HelpTopics.DECOMPILER, "GeneralMaxPayload"),
"The maximum size of the decompiler result payload in MBYtes (Suggested value: 50).");
opt.registerOption(HIGHLIGHT_CURRENT_VARIABLE_MSG,
HIGHLIGHT_CURRENT_VARIABLE_DEF,
opt.registerOption(MAX_INSTRUCTIONS, SUGGESTED_MAX_INSTRUCTIONS,
new HelpLocation(HelpTopics.DECOMPILER, "GeneralMaxInstruction"),
"The maximum number of instructions decompiled in a single function");
opt.registerOption(HIGHLIGHT_CURRENT_VARIABLE_MSG, HIGHLIGHT_CURRENT_VARIABLE_DEF,
new HelpLocation(HelpTopics.DECOMPILER, "DisplayCurrentHighlight"),
"Current variable highlight");
opt.registerOption(CACHED_RESULTS_SIZE_MSG,
SUGGESTED_CACHED_RESULTS_SIZE,
new HelpLocation(HelpTopics.DECOMPILER, "GeneralCacheSize"),
CACHE_RESULTS_DESCRIPTION);
opt.registerOption(CACHED_RESULTS_SIZE_MSG, SUGGESTED_CACHED_RESULTS_SIZE,
new HelpLocation(HelpTopics.DECOMPILER, "GeneralCacheSize"), CACHE_RESULTS_DESCRIPTION);
grabFromToolAndProgram(ownerPlugin, opt, program);
}
@ -751,32 +714,76 @@ public class DecompileOptions {
// Must set language early so that the object is in place before other option changes
appendOption(buf, "setlanguage", displayLanguage.toString(), "", "");
appendOption(buf, "ignoreunimplemented", ignoreunimpl ? "on" : "off", "", "");
appendOption(buf, "inferconstptr", inferconstptr ? "on" : "off", "", "");
appendOption(buf, "analyzeforloops", analyzeForLoops ? "on" : "off", "", "");
appendOption(buf, "nullprinting", nullToken ? "on" : "off", "", "");
appendOption(buf, "inplaceops", inplaceTokens ? "on" : "off", "", "");
appendOption(buf, "aliasblock", aliasBlock.getOptionString(), "", "");
appendOption(buf, "conventionprinting", conventionPrint ? "on" : "off", "", "");
appendOption(buf, "nocastprinting", noCastPrint ? "on" : "off", "", "");
appendOption(buf, "maxlinewidth", Integer.toString(maxwidth), "", "");
appendOption(buf, "indentincrement", Integer.toString(indentwidth), "", "");
appendOption(buf, "commentindent", Integer.toString(commentindent), "", "");
String curstyle = CommentStyleEnum.CPPStyle.equals(commentStyle) ? "cplusplus" : "c";
appendOption(buf, "commentstyle", curstyle, "", "");
appendOption(buf, "commentinstruction", "header", commentPLATEInclude ? "on" : "off", "");
appendOption(buf, "commentinstruction", "user2", commentPREInclude ? "on" : "off", "");
appendOption(buf, "commentinstruction", "user1", commentEOLInclude ? "on" : "off", "");
appendOption(buf, "commentinstruction", "user3", commentPOSTInclude ? "on" : "off", "");
appendOption(buf, "commentinstruction", "warning", commentWARNInclude ? "on" : "off", "");
appendOption(buf, "commentheader", "header", commentHeadInclude ? "on" : "off", "");
appendOption(buf, "commentheader", "warningheader", commentWARNInclude ? "on" : "off", "");
appendOption(buf, "namespacestrategy", namespaceStrategy.getOptionString(), "", "");
appendOption(buf, "integerformat", integerFormat.getOptionString(), "", "");
if (ignoreunimpl != IGNOREUNIMPL_OPTIONDEFAULT) {
appendOption(buf, "ignoreunimplemented", ignoreunimpl ? "on" : "off", "", "");
}
if (inferconstptr != INFERCONSTPTR_OPTIONDEFAULT) {
appendOption(buf, "inferconstptr", inferconstptr ? "on" : "off", "", "");
}
if (analyzeForLoops != ANALYZEFORLOOPS_OPTIONDEFAULT) {
appendOption(buf, "analyzeforloops", analyzeForLoops ? "on" : "off", "", "");
}
if (nullToken != NULLTOKEN_OPTIONDEFAULT) {
appendOption(buf, "nullprinting", nullToken ? "on" : "off", "", "");
}
if (inplaceTokens != INPLACEOP_OPTIONDEFAULT) {
appendOption(buf, "inplaceops", inplaceTokens ? "on" : "off", "", "");
}
if (aliasBlock != ALIASBLOCK_OPTIONDEFAULT) {
appendOption(buf, "aliasblock", aliasBlock.getOptionString(), "", "");
}
if (conventionPrint != CONVENTION_OPTIONDEFAULT) {
appendOption(buf, "conventionprinting", conventionPrint ? "on" : "off", "", "");
}
if (noCastPrint != NOCAST_OPTIONDEFAULT) {
appendOption(buf, "nocastprinting", noCastPrint ? "on" : "off", "", "");
}
if (maxwidth != MAXWIDTH_OPTIONDEFAULT) {
appendOption(buf, "maxlinewidth", Integer.toString(maxwidth), "", "");
}
if (indentwidth != INDENTWIDTH_OPTIONDEFAULT) {
appendOption(buf, "indentincrement", Integer.toString(indentwidth), "", "");
}
if (commentindent != COMMENTINDENT_OPTIONDEFAULT) {
appendOption(buf, "commentindent", Integer.toString(commentindent), "", "");
}
if (commentStyle != COMMENTSTYLE_OPTIONDEFAULT) {
String curstyle = CommentStyleEnum.CPPStyle.equals(commentStyle) ? "cplusplus" : "c";
appendOption(buf, "commentstyle", curstyle, "", "");
}
if (commentPLATEInclude != COMMENTPLATE_OPTIONDEFAULT) {
appendOption(buf, "commentinstruction", "header", commentPLATEInclude ? "on" : "off",
"");
}
if (commentPREInclude != COMMENTPRE_OPTIONDEFAULT) {
appendOption(buf, "commentinstruction", "user2", commentPREInclude ? "on" : "off", "");
}
if (commentEOLInclude != COMMENTEOL_OPTIONDEFAULT) {
appendOption(buf, "commentinstruction", "user1", commentEOLInclude ? "on" : "off", "");
}
if (commentPOSTInclude != COMMENTPOST_OPTIONDEFAULT) {
appendOption(buf, "commentinstruction", "user3", commentPOSTInclude ? "on" : "off", "");
}
if (commentWARNInclude != COMMENTWARN_OPTIONDEFAULT) {
appendOption(buf, "commentinstruction", "warning", commentWARNInclude ? "on" : "off",
"");
}
if (commentHeadInclude != COMMENTHEAD_OPTIONDEFAULT) {
appendOption(buf, "commentheader", "header", commentHeadInclude ? "on" : "off", "");
}
if (commentWARNInclude != COMMENTWARN_OPTIONDEFAULT) {
appendOption(buf, "commentheader", "warningheader", commentWARNInclude ? "on" : "off",
"");
}
if (namespaceStrategy != NAMESPACE_OPTIONDEFAULT) {
appendOption(buf, "namespacestrategy", namespaceStrategy.getOptionString(), "", "");
}
if (integerFormat != INTEGERFORMAT_OPTIONDEFAULT) {
appendOption(buf, "integerformat", integerFormat.getOptionString(), "", "");
}
if (maxIntructionsPer != SUGGESTED_MAX_INSTRUCTIONS) {
appendOption(buf, "maxinstruction", Integer.toString(maxIntructionsPer), "", "");
}
appendOption(buf, "protoeval", protoEvalModel, "", "");
buf.append("</optionslist>\n");
return buf.toString();
@ -958,6 +965,14 @@ public class DecompileOptions {
payloadLimitMBytes = mbytes;
}
public int getMaxInstructions() {
return maxIntructionsPer;
}
public void setMaxInstructions(int num) {
maxIntructionsPer = num;
}
public CommentStyleEnum getCommentStyle() {
return commentStyle;
}

View file

@ -21,13 +21,14 @@ import ghidra.app.plugin.assembler.sleigh.parse.AssemblyParseResult;
import ghidra.app.plugin.assembler.sleigh.sem.*;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressOverflowException;
import ghidra.program.model.lang.InstructionBlock;
import ghidra.program.model.listing.Instruction;
import ghidra.program.model.listing.InstructionIterator;
import ghidra.program.model.mem.MemoryAccessException;
/**
* The primary interface for performing assembly in Ghidra.
*
* <p>
* Use the {@link Assemblers} class to obtain a suitable implementation for a given program or
* language.
*/
@ -35,26 +36,30 @@ public interface Assembler {
/**
* Assemble a sequence of instructions and place them at the given address.
*
* <p>
* This method is only valid if the assembler is bound to a program. An instance may optionally
* implement this method without a program binding. In that case, the returned instruction
* block will refer to pseudo instructions.
* implement this method without a program binding. In that case, the returned iterator will
* refer to pseudo instructions.
*
* <p>
* NOTE: There must be an active transaction on the bound program for this method to succeed.
*
* @param at the location where the resulting instructions should be placed
* @param listing a new-line separated or array sequence of instructions
* @return the block of resulting instructions
* @return an iterator over the resulting instructions
* @throws AssemblySyntaxException a textual instruction is non well-formed
* @throws AssemblySemanticException a well-formed instruction cannot be assembled
* @throws MemoryAccessException there is an issue writing the result to program memory
* @throws AddressOverflowException the resulting block is beyond the valid address range
*/
public InstructionBlock assemble(Address at, String... listing) throws AssemblySyntaxException,
public InstructionIterator assemble(Address at, String... listing)
throws AssemblySyntaxException,
AssemblySemanticException, MemoryAccessException, AddressOverflowException;
/**
* Assemble a line instruction at the given address.
*
* <p>
* This method is valid with or without a bound program. Even if bound, the program is not
* modified; however, the appropriate context information is taken from the bound program.
* Without a program, the language's default context is taken at the given location.
@ -71,6 +76,7 @@ public interface Assembler {
/**
* Assemble a line instruction at the given address, assuming the given context.
*
* <p>
* This method works like {@link #assembleLine(Address, String)} except that it allows you to
* override the assumed context at that location.
*
@ -87,15 +93,18 @@ public interface Assembler {
/**
* Parse a line instruction.
*
* <p>
* Generally, you should just use {@link #assembleLine(Address, String)}, but if you'd like
* access to the parse trees outside of an {@link AssemblySelector}, then this may be an
* acceptable option. Most notably, this is an excellent way to obtain suggestions for
* auto-completion.
*
* <p>
* Each item in the returned collection is either a complete parse tree, or a syntax error
* Because all parse paths are attempted, it's possible to get many mixed results. For example,
* The input line may be a valid instruction; however, there may be suggestions to continue the
* line toward another valid instruction.
*
* @param line the line (or partial line) to parse
* @return the results of parsing
*/
@ -104,12 +113,15 @@ public interface Assembler {
/**
* Resolve a given parse tree at the given address, assuming the given context
*
* <p>
* Each item in the returned collection is either a completely resolved instruction, or a
* semantic error. Because all resolutions are attempted, it's possible to get many mixed
* results.
*
* NOTE: The resolved instructions are given as masks and values. Where the mask does not
* cover, you can choose any value.
* <p>
* NOTE: The resolved instructions are given as masks and values. Where the mask does not cover,
* you can choose any value.
*
* @param parse a parse result giving a valid tree
* @param at the location of the start of the instruction
* @param ctx the context register value at the start of the instruction
@ -121,12 +133,15 @@ public interface Assembler {
/**
* Resolve a given parse tree at the given address.
*
* <p>
* Each item in the returned collection is either a completely resolved instruction, or a
* semantic error. Because all resolutions are attempted, it's possible to get many mixed
* results.
*
* NOTE: The resolved instructions are given as masks and values. Where the mask does not
* cover, you can choose any value.
* <p>
* NOTE: The resolved instructions are given as masks and values. Where the mask does not cover,
* you can choose any value.
*
* @param parse a parse result giving a valid tree
* @param at the location of the start of the instruction
* @return the results of semantic resolution
@ -136,8 +151,10 @@ public interface Assembler {
/**
* Assemble a line instruction at the given address.
*
* <p>
* This method works like {@link #resolveLine(Address, String, AssemblyPatternBlock)}, except
* that it derives the context using {@link #getContextAt(Address)}.
*
* @param at the location of the start of the instruction
* @param line the textual assembly code
* @return the collection of semantic resolution results
@ -149,9 +166,11 @@ public interface Assembler {
/**
* Assemble a line instruction at the given address, assuming the given context.
*
* <p>
* This method works like {@link #assembleLine(Address, String, AssemblyPatternBlock)}, except
* that it returns all possible resolutions for the parse trees that pass the
* {@link AssemblySelector}.
*
* @param at the location of the start of the instruction
* @param line the textual assembly code
* @param ctx the context register value at the start of the instruction
@ -164,8 +183,10 @@ public interface Assembler {
/**
* Place a resolved (and fully-masked) instruction into the bound program.
*
* This method is not valid without a program binding. Also, this method must be called during
* a program database transaction.
* <p>
* This method is not valid without a program binding. Also, this method must be called during a
* program database transaction.
*
* @param res the resolved and fully-masked instruction
* @param at the location of the start of the instruction
* @return the new {@link Instruction} code unit
@ -175,22 +196,27 @@ public interface Assembler {
throws MemoryAccessException;
/**
* Place an instruction into the bound program.
* Place instruction bytes into the bound program.
*
* <p>
* This method is not valid without a program binding. Also, this method must be called during a
* program database transaction.
*
* This method is not valid without a program binding. Also, this method must be called during
* a program database transaction.
* @param insbytes the instruction data
* @param at the location of the start of the instruction
* @return the new {@link Instruction} code unit
* @return an iterator over the disassembled instructions
* @throws MemoryAccessException there is an issue writing the result to program memory
*/
public Instruction patchProgram(byte[] insbytes, Address at) throws MemoryAccessException;
public InstructionIterator patchProgram(byte[] insbytes, Address at)
throws MemoryAccessException;
/**
* Get the context at a given address
*
* <p>
* If there is a program binding, this will extract the actual context at the given address.
* Otherwise, it will obtain the default context at the given address for the language.
*
* @param addr the address
* @return the context
*/

View file

@ -28,16 +28,15 @@ import ghidra.program.model.listing.Program;
/**
* The primary class for obtaining an {@link Assembler} for a Ghidra-supported language.
*
* <p>
* The general flow is: First, obtain an assembler for a language or program. Second, call its
* {@link Assembler#assemble(Address, String...)} and related methods to perform assembly. More
* advanced uses pass a {@link AssemblySelector} to control certain aspects of assembly instruction
* selection, and to obtain advanced diagnostics, like detailed errors and code completion.
*
* <pre>
* {@code
* Assembler asm = Assemblers.getAssembler(currentProgram);
* asm.assemble(currentAddress, "ADD ...");
* }
* </pre>
*/
public final class Assemblers {
@ -45,6 +44,7 @@ public final class Assemblers {
/**
* Get a builder for the given language, possibly using a cached one.
*
* @param lang the language
* @return the builder for that language, if successful
*/
@ -64,10 +64,11 @@ public final class Assemblers {
/**
* Get an assembler for the given program.
*
* Provides an assembler suitable for the program's language, and bound to the program. Calls
* to its Assembler#assemble() function will cause modifications to the bound program. If this
* is the first time an assembler for the program's language has been requested, this function
* may take some time to build the assembler.
* <p>
* Provides an assembler suitable for the program's language, and bound to the program. Calls to
* its Assembler#assemble() function will cause modifications to the bound program. If this is
* the first time an assembler for the program's language has been requested, this function may
* take some time to build the assembler.
*
* @param selector a method to select a single result from many
* @param program the program for which an assembler is requested
@ -81,6 +82,7 @@ public final class Assemblers {
/**
* Get an assembler for the given language.
*
* <p>
* Provides a suitable assembler for the given language. Only calls to its
* Assembler#assembleLine() method are valid. If this is the first time a language has been
* requested, this function may take some time to build the assembler. Otherwise, it returns a

View file

@ -15,6 +15,8 @@
*/
package ghidra.app.plugin.assembler.sleigh;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.*;
import ghidra.app.plugin.assembler.*;
@ -25,7 +27,8 @@ import ghidra.app.plugin.processors.sleigh.SleighLanguage;
import ghidra.program.disassemble.Disassembler;
import ghidra.program.disassemble.DisassemblerMessageListener;
import ghidra.program.model.address.*;
import ghidra.program.model.lang.*;
import ghidra.program.model.lang.Register;
import ghidra.program.model.lang.RegisterValue;
import ghidra.program.model.listing.*;
import ghidra.program.model.mem.Memory;
import ghidra.program.model.mem.MemoryAccessException;
@ -98,22 +101,25 @@ public class SleighAssembler implements Assembler {
if (!res.getInstruction().isFullMask()) {
throw new AssemblySelectionError("Selected instruction must have a full mask.");
}
return patchProgram(res.getInstruction().getVals(), at);
return patchProgram(res.getInstruction().getVals(), at).next();
}
@Override
public Instruction patchProgram(byte[] insbytes, Address at) throws MemoryAccessException {
listing.clearCodeUnits(at, at.add(insbytes.length - 1), false);
public InstructionIterator patchProgram(byte[] insbytes, Address at)
throws MemoryAccessException {
Address end = at.add(insbytes.length - 1);
listing.clearCodeUnits(at, end, false);
memory.setBytes(at, insbytes);
dis.disassemble(at, new AddressSet(at));
return listing.getInstructionAt(at);
return listing.getInstructions(new AddressSet(at, end), true);
}
@Override
public InstructionBlock assemble(Address at, String... assembly) throws AssemblySyntaxException,
AssemblySemanticException, MemoryAccessException, AddressOverflowException {
InstructionBlock block = new InstructionBlock(at);
public InstructionIterator assemble(Address at, String... assembly)
throws AssemblySyntaxException, AssemblySemanticException, MemoryAccessException,
AddressOverflowException {
Address start = at;
ByteArrayOutputStream buf = new ByteArrayOutputStream();
for (String part : assembly) {
for (String line : part.split("\n")) {
RegisterValue rv = program.getProgramContext().getDisassemblyContext(at);
@ -124,13 +130,16 @@ public class SleighAssembler implements Assembler {
if (insbytes == null) {
return null;
}
Instruction ins = patchProgram(insbytes, at);
block.addInstruction(ins);
try {
buf.write(insbytes);
}
catch (IOException e) {
throw new AssertionError(e);
}
at = at.addNoWrap(insbytes.length);
}
}
return block;
return patchProgram(buf.toByteArray(), start);
}
@Override
@ -221,10 +230,11 @@ public class SleighAssembler implements Assembler {
/**
* A convenience to obtain a map of program labels strings to long values
*
* @return the map
*
* {@literal TODO Use a Map<String, Address> instead so that, if possible, symbol values can be checked}
* lest they be an invalid substitution for a given operand.
* {@literal TODO Use a Map<String, Address> instead so that, if possible, symbol values can be checked}
* lest they be an invalid substitution for a given operand.
*/
protected Map<String, Long> getProgramLabels() {
Map<String, Long> labels = new HashMap<>();

View file

@ -24,12 +24,10 @@ import ghidra.program.model.listing.Program;
* This is meant to be used as an idiom in a try-with-resources block:
*
* <pre>
* {@code
* try (ProgramTransaction t = ProgramTransaction.open(program, "Demo")) {
* program.getMemory().....
* t.commit();
* }
* }
* </pre>
*
* <p>

View file

@ -588,8 +588,10 @@ public class PcodeDataTypeManager {
}
else {
int sz = type.getLength();
boolean isVarLength = false;
if (sz <= 0) {
sz = size;
isVarLength = true;
}
appendNameIdAttributes(resBuf, origType);
if (sz < 16) {
@ -601,6 +603,9 @@ public class PcodeDataTypeManager {
// Build an "opaque" structure with no fields
SpecXmlUtils.encodeStringAttribute(resBuf, "metatype", "struct");
SpecXmlUtils.encodeSignedIntegerAttribute(resBuf, "size", sz);
if (isVarLength) {
SpecXmlUtils.encodeBooleanAttribute(resBuf, "varlength", isVarLength);
}
resBuf.append('>');
}
}

View file

@ -15,36 +15,86 @@
*/
package ghidra.app.plugin.assembler.sleigh;
import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import java.util.ArrayList;
import java.util.List;
import org.junit.*;
import generic.test.AbstractGenericTest;
import ghidra.app.plugin.assembler.*;
import ghidra.app.plugin.processors.sleigh.SleighLanguageProvider;
import ghidra.program.database.ProgramDB;
import ghidra.program.database.util.ProgramTransaction;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressOverflowException;
import ghidra.program.model.lang.Language;
import ghidra.program.model.lang.LanguageID;
import ghidra.program.model.listing.*;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
public class PublicAPITest extends AbstractGenericTest {
private Language x86;
Language x86;
Language toy;
Program program;
@Before
public void setUp() throws Exception {
SleighLanguageProvider provider = new SleighLanguageProvider();
x86 = provider.getLanguage(new LanguageID("x86:LE:64:default"));
toy = provider.getLanguage(new LanguageID("Toy:BE:64:default"));
}
@After
public void tearDown() throws Exception {
if (program != null) {
program.release(this);
}
}
@Test
public void testADD0() throws AssemblySyntaxException, AssemblySemanticException {
// Mostly just test that it doesn't crash
Assembler asm = Assemblers.getAssembler(x86);
byte[] b =
asm.assembleLine(x86.getDefaultSpace().getAddress(0x40000000), "ADD byte ptr [RBX],BL");
printArray(b);
assertNotEquals(0, b.length);
}
public static void printArray(byte[] arr) {
for (int i = 0; i < arr.length; i++) {
System.out.printf("%02x", arr[i]);
protected Address addr(long offset) {
return program.getAddressFactory().getDefaultAddressSpace().getAddress(offset);
}
@Test
public void testAssembleWithDelaySlot() throws Exception,
AddressOverflowException, CancelledException {
program = new ProgramDB("test", toy, toy.getDefaultCompilerSpec(), this);
InstructionIterator it;
try (ProgramTransaction tid = ProgramTransaction.open(program, "Test")) {
program.getMemory()
.createInitializedBlock(".text", addr(0x00400000), 0x1000, (byte) 0,
TaskMonitor.DUMMY, false);
Assembler asm = Assemblers.getAssembler(program);
it = asm.assemble(addr(0x00400000),
"brds 0x00400004",
"add r0, #6");
tid.commit();
}
System.out.println();
List<Instruction> result = new ArrayList<>();
while (it.hasNext()) {
result.add(it.next());
}
assertEquals(2, result.size());
assertEquals("brds", result.get(0).getMnemonicString());
assertEquals("_add", result.get(1).getMnemonicString());
}
}

View file

@ -349,6 +349,14 @@ cc: "M" is bits3_3=0x7 { export S_flag;
setResultFlags(A);
}
:XRA (HL) is op0_8=0xae & HL {
AC_flag = 0;
CY_flag = 0;
P_flag = 0;
A = A ^ *:1 HL;
setResultFlags(A);
}
:XRI imm8 is op0_8=0xee; imm8 {
AC_flag = 0;
CY_flag = 0;

View file

@ -5,14 +5,15 @@ data/languages/mips.dwarf||GHIDRA||||END|
data/languages/mips.ldefs||GHIDRA||||END|
data/languages/mips.sinc||GHIDRA||||END|
data/languages/mips16.sinc||GHIDRA||||END|
data/languages/mips32.cspec||GHIDRA||||END|
data/languages/mips32.pspec||GHIDRA||||END|
data/languages/mips32Instructions.sinc||GHIDRA||||END|
data/languages/mips32R6.pspec||GHIDRA||||END|
data/languages/mips32R6be.slaspec||GHIDRA||||END|
data/languages/mips32R6le.slaspec||GHIDRA||||END|
data/languages/mips32_fp64.cspec||GHIDRA||||END|
data/languages/mips32be.cspec||GHIDRA||||END|
data/languages/mips32be.slaspec||GHIDRA||||END|
data/languages/mips32le.cspec||GHIDRA||||END|
data/languages/mips32le.slaspec||GHIDRA||||END|
data/languages/mips32micro.pspec||GHIDRA||||END|
data/languages/mips64.cspec||GHIDRA||||END|

View file

@ -10,8 +10,8 @@
manualindexfile="../manuals/mipsM16.idx"
id="MIPS:BE:32:default">
<description>MIPS32 32-bit addresses, big endian, with mips16e</description>
<compiler name="default" spec="mips32.cspec" id="default"/>
<compiler name="Visual Studio" spec="mips32.cspec" id="windows"/>
<compiler name="default" spec="mips32be.cspec" id="default"/>
<compiler name="Visual Studio" spec="mips32be.cspec" id="windows"/>
<external_name tool="gnu" name="mips:4000"/>
<external_name tool="IDA-PRO" name="mipsb"/>
<external_name tool="DWARF.register.mapping.file" name="mips.dwarf"/>
@ -26,8 +26,8 @@
manualindexfile="../manuals/mipsM16.idx"
id="MIPS:LE:32:default">
<description>MIPS32 32-bit addresses, little endian, with mips16e</description>
<compiler name="default" spec="mips32.cspec" id="default"/>
<compiler name="Visual Studio" spec="mips32.cspec" id="windows"/>
<compiler name="default" spec="mips32le.cspec" id="default"/>
<compiler name="Visual Studio" spec="mips32le.cspec" id="windows"/>
<external_name tool="gnu" name="mips:4000"/>
<external_name tool="IDA-PRO" name="mipsl"/>
<external_name tool="DWARF.register.mapping.file" name="mips.dwarf"/>
@ -284,7 +284,7 @@
manualindexfile="../manuals/mipsMic.idx"
id="MIPS:BE:32:micro">
<description>MIPS32 32-bit addresses, big endian, with microMIPS</description>
<compiler name="default" spec="mips32.cspec" id="default"/>
<compiler name="default" spec="mips32be.cspec" id="default"/>
<external_name tool="gnu" name="mips:micromips"/>
<external_name tool="IDA-PRO" name="mipsb"/>
<external_name tool="DWARF.register.mapping.file" name="mips.dwarf"/>
@ -299,8 +299,8 @@
manualindexfile="../manuals/mipsMic.idx"
id="MIPS:LE:32:micro">
<description>MIPS32 32-bit addresses, little endian, with microMIPS</description>
<compiler name="default" spec="mips32.cspec" id="default"/>
<compiler name="Visual Studio" spec="mips32.cspec" id="windows"/>
<compiler name="default" spec="mips32le.cspec" id="default"/>
<compiler name="Visual Studio" spec="mips32le.cspec" id="windows"/>
<external_name tool="IDA-PRO" name="mipsl"/>
<external_name tool="DWARF.register.mapping.file" name="mips.dwarf"/>
</language>

View file

@ -0,0 +1,90 @@
<?xml version="1.0" encoding="UTF-8"?>
<compiler_spec>
<data_organization>
<pointer_size value="4"/>
<float_size value="4" />
<double_size value="8" />
<long_double_size value="8" />
<size_alignment_map>
<entry size="1" alignment="1" />
<entry size="2" alignment="2" />
<entry size="4" alignment="4" />
<entry size="8" alignment="8" />
</size_alignment_map>
</data_organization>
<stackpointer register="sp" space="ram"/>
<funcptr align="2"/>
<spacebase name="gp" register="gp" space="ram"/>
<global>
<range space="gp"/>
<range space="ram"/>
<range space="register" first="0x2000" last="0x2fff"/>
</global>
<returnaddress>
<register name="ra"/>
</returnaddress>
<default_proto>
<prototype name="__stdcall" extrapop="0" stackshift="0">
<input>
<pentry minsize="1" maxsize="8" metatype="float">
<register name="f12_13"/>
</pentry>
<pentry minsize="1" maxsize="8" metatype="float">
<register name="f14_15"/>
</pentry>
<pentry minsize="1" maxsize="4">
<register name="a0"/>
</pentry>
<pentry minsize="1" maxsize="4">
<register name="a1"/>
</pentry>
<pentry minsize="1" maxsize="4">
<register name="a2"/>
</pentry>
<pentry minsize="1" maxsize="4">
<register name="a3"/>
</pentry>
<pentry minsize="1" maxsize="500" align="4">
<addr offset="16" space="stack"/>
</pentry>
</input>
<output>
<pentry minsize="1" maxsize="8" metatype="float">
<register name="f0_1"/>
</pentry>
<pentry minsize="1" maxsize="4">
<register name="v0"/>
</pentry>
<pentry minsize="5" maxsize="8">
<addr space="join" piece1="v1" piece2="v0"/>
</pentry>
</output>
<unaffected>
<register name="s0"/>
<register name="s1"/>
<register name="s2"/>
<register name="s3"/>
<register name="s4"/>
<register name="s5"/>
<register name="s6"/>
<register name="s7"/>
<register name="s8"/>
<register name="sp"/>
<register name="gp"/>
<register name="f20"/>
<register name="f22"/>
<register name="f24"/>
<register name="f26"/>
<register name="f28"/>
<register name="f30"/>
</unaffected>
<localrange>
<range space="stack" first="0xfff0bdc0" last="0xffffffff"/>
<range space="stack" first="0" last="15"/> <!-- This is backup storage space for register params, but we treat as locals -->
</localrange>
</prototype>
</default_proto>
</compiler_spec>

View file

@ -86,8 +86,8 @@ define ram offset=0x36 size=2 [ RCOUNT ]; # Repeat counter
# define ram offset=0x38 size=2 [ DCOUNT ]; # 13 bits long DO Loop counter
define ram offset=0x54 size=1 [ TBLPAG ]; # 7bit Data Table Page Address
@else
define ram offset=0x31 size=1 [ TBLPAG ]; # 8bit Data Table Page Address
define ram offset=0x33 size=1 [ PSVPAG ]; # Program Memory Visibility Page Address Pointer
define ram offset=0x32 size=1 [ TBLPAG ]; # 8bit Data Table Page Address
define ram offset=0x34 size=1 [ PSVPAG ]; # Program Memory Visibility Page Address Pointer
define ram offset=0x36 size=2 [ RCOUNT ]; # Repeat counter
define ram offset=0x38 size=2 [ DCOUNT ]; # 13 bits long DO Loop counter
define ram offset=0x3A size=3 [ DOSTART ];

View file

@ -81,6 +81,9 @@ VMARGS_WINDOWS=-Dlog4j.skipJansi=true
# Ghidra does not use class data sharing, so explicitly turn it off to avoid the warning.
VMARGS=-Xshare:off
# Permit "illegal reflective accesses" to enable compatibility with some 3rd party jars
VMARGS=--illegal-access=permit
# Persistent cache directory used by the application. This directory will be used to store
# persistent application caches for all users. The default location for Mac/Linux is the same as
# specified by java.io.tmpdir property. The default location for Windows corresponds to the

View file

@ -261,7 +261,7 @@ class XmlExporter:
elif ch == '\'' : return "&apos;"
elif ch == '"' : return "&quot;"
elif ch == '\x7F': return ''
elif ord(ch) > 0x7F: return '&#x' + format((ord(ch),"x")) + ";"
elif ord(ch) > 0x7F: return '&#x' + format(ord(ch),"x") + ";"
return ch

View file

@ -647,7 +647,7 @@ class XmlExporter(IdaXml):
# tag_remove seems to be losing last character
# work around is to add a space
cmt_text = ida_lines.tag_remove(cmt + ' ')
self.write_text(cmt_text)
self.write_text(cmt_text.decode('utf-8'))
self.end_element(COMMENT, False)
@ -2395,7 +2395,7 @@ class XmlImporter(IdaXml):
# overlayed addresses not currently handled
return BADADDR
elif ':' in addrstr:
[segstr, offset_str] = string.split(addrstr,':')
[segstr, offset_str] = str.split(addrstr,':')
offset = int(offset_str,16)
if self.is_int(segstr) == True:
sgmt = int(segstr,16)
@ -3111,11 +3111,11 @@ class XmlImporter(IdaXml):
"""
regcmt = member.find(REGULAR_CMT)
if regcmt != None:
idc.set_member_cmt(mbr, regcmt.text, False)
ida_struct.set_member_cmt(mbr, regcmt.text, False)
self.update_counter(MEMBER + ':' + REGULAR_CMT)
rptcmt = member.find(REPEATABLE_CMT)
if rptcmt != None:
idc.set_member_cmt(mbr, rptcmt.text, True)
ida_struct.set_member_cmt(mbr, rptcmt.text, True)
self.update_counter(MEMBER + ':' + REPEATABLE_CMT)
@ -3239,7 +3239,7 @@ class XmlImporter(IdaXml):
idc.warning(msg)
return
elif ':' in addrstr:
[seg_str, offset_str] = string.split(addrstr,':')
[seg_str, offset_str] = str.split(addrstr,':')
offset = int(offset_str, 16)
if self.is_int(seg_str):
base = int(seg_str, 16)