GP-2424 Decompiler allows unknown prototype model names

This commit is contained in:
caheckman 2022-08-11 16:57:37 -04:00
parent a5fdeba51a
commit 60604b5672
15 changed files with 166 additions and 122 deletions

View file

@ -221,9 +221,9 @@ Architecture::~Architecture(void)
/// The Architecture maintains the set of prototype models that can
/// be applied for this particular executable. Retrieve one by name.
/// The model must exist or an exception is thrown.
/// If the model doesn't exist, null is returned.
/// \param nm is the name
/// \return the matching model
/// \return the matching model or null
ProtoModel *Architecture::getModel(const string &nm) const
{
@ -231,7 +231,7 @@ ProtoModel *Architecture::getModel(const string &nm) const
iter = protoModels.find(nm);
if (iter==protoModels.end())
throw LowlevelError("Prototype model does not exist: "+nm);
return (ProtoModel *)0;
return (*iter).second;
}
@ -313,10 +313,13 @@ int4 Architecture::getMinimumLanedRegisterSize(void) const
/// The default model is used whenever an explicit model is not known
/// or can't be determined.
/// \param nm is the name of the model to set
void Architecture::setDefaultModel(const string &nm)
void Architecture::setDefaultModel(ProtoModel *model)
{
defaultfp = getModel(nm);
if (defaultfp != (ProtoModel *)0)
defaultfp->setPrintInDecl(true);
model->setPrintInDecl(false);
defaultfp = model;
}
/// Throw out the syntax tree, (unlocked) symbols, comments, and other derived information
@ -817,7 +820,7 @@ ProtoModel *Architecture::decodeProto(Decoder &decoder)
res->decode(decoder);
ProtoModel *other = protoModels[res->getName()];
ProtoModel *other = getModel(res->getName());
if (other != (ProtoModel *)0) {
string errMsg = "Duplicate ProtoModel name: " + res->getName();
delete res;
@ -836,7 +839,7 @@ void Architecture::decodeProtoEval(Decoder &decoder)
{
uint4 elemId = decoder.openElement();
string modelName = decoder.readString(ATTRIB_NAME);
ProtoModel *res = protoModels[ modelName ];
ProtoModel *res = getModel(modelName);
if (res == (ProtoModel *)0)
throw LowlevelError("Unknown prototype model name: "+modelName);
@ -864,7 +867,8 @@ void Architecture::decodeDefaultProto(Decoder &decoder)
while(decoder.peekElement() != 0) {
if (defaultfp != (ProtoModel *)0)
throw LowlevelError("More than one default prototype model");
defaultfp = decodeProto(decoder);
ProtoModel *model = decodeProto(decoder);
setDefaultModel(model);
}
decoder.closeElement(elemId);
}
@ -1192,6 +1196,20 @@ void Architecture::createModelAlias(const string &aliasName,const string &parent
protoModels[aliasName] = new ProtoModel(aliasName,*model);
}
/// A new UnknownProtoModel, which clones its behavior from the default model, is created and associated with the
/// unrecognized name. Subsequent queries of the name return this new model.
/// \param modelName is the unrecognized name
/// \return the new \e unknown prototype model associated with the name
ProtoModel *Architecture::createUnknownModel(const string &modelName)
{
UnknownProtoModel *model = new UnknownProtoModel(modelName,defaultfp);
protoModels[modelName] = model;
if (modelName == "unknown") // "unknown" is a reserved/internal name
model->setPrintInDecl(false); // don't print it in declarations
return model;
}
/// This looks for the \<processor_spec> tag and and sets configuration
/// parameters based on it.
/// \param store is the document store holding the tag
@ -1360,8 +1378,8 @@ void Architecture::parseCompilerConfig(DocumentStorage &store)
addOtherSpace();
if (defaultfp == (ProtoModel *)0) {
if (protoModels.size() == 1)
defaultfp = (*protoModels.begin()).second;
if (protoModels.size() > 0)
setDefaultModel((*protoModels.begin()).second);
else
throw LowlevelError("No default prototype specified");
}

View file

@ -216,11 +216,12 @@ public:
void resetDefaults(void); ///< Reset defaults values for options owned by \b this
ProtoModel *getModel(const string &nm) const; ///< Get a specific PrototypeModel
bool hasModel(const string &nm) const; ///< Does this Architecture have a specific PrototypeModel
ProtoModel *createUnknownModel(const string &modelName); ///< Create a model for an unrecognized name
bool highPtrPossible(const Address &loc,int4 size) const; ///< Are pointers possible to the given location?
AddrSpace *getSpaceBySpacebase(const Address &loc,int4 size) const; ///< Get space associated with a \e spacebase register
const LanedRegister *getLanedRegister(const Address &loc,int4 size) const; ///< Get LanedRegister associated with storage
int4 getMinimumLanedRegisterSize(void) const; ///< Get the minimum size of a laned register in bytes
void setDefaultModel(const string &nm); ///< Set the default PrototypeModel
void setDefaultModel(ProtoModel *model); ///< Set the default PrototypeModel
void clearAnalysis(Funcdata *fd); ///< Clear analysis specific to a function
void readLoaderSymbols(const string &delim); ///< Read any symbols from loader into database
void collectBehaviors(vector<OpBehavior *> &behave) const; ///< Provide a list of OpBehavior objects

View file

@ -2174,7 +2174,7 @@ int4 ActionDefaultParams::apply(Funcdata &data)
if (otherfunc != (Funcdata *)0) {
fc->copy(otherfunc->getFuncProto());
if ((!fc->isModelLocked())&&(!fc->hasMatchingModel(evalfp)))
if ((!fc->isModelLocked())&& !fc->hasMatchingModel(evalfp))
fc->setModel(evalfp);
}
else
@ -4060,7 +4060,7 @@ int4 ActionPrototypeTypes::apply(Funcdata &data)
ProtoModel *evalfp = data.getArch()->evalfp_current;
if (evalfp == (ProtoModel *)0)
evalfp = data.getArch()->defaultfp;
if ((!data.getFuncProto().isModelLocked())&&(!data.getFuncProto().hasMatchingModel(evalfp)))
if ((!data.getFuncProto().isModelLocked()) && !data.getFuncProto().hasMatchingModel(evalfp))
data.getFuncProto().setModel(evalfp);
if (data.getFuncProto().hasThisPointer())
data.prepareThisPointer();
@ -4340,10 +4340,15 @@ int4 ActionPrototypeWarnings::apply(Funcdata &data)
if (ourproto.hasOutputErrors()) {
data.warningHeader("Cannot assign location of return value for this function: Return value may be inaccurate");
}
if (ourproto.isUnknownModel() && (!ourproto.hasCustomStorage()) &&
(ourproto.isInputLocked() || ourproto.isOutputLocked())) {
data.warningHeader("Unknown calling convention yet parameter storage is locked");
}
if (ourproto.isModelUnknown()) {
ostringstream s;
s << "Unknown calling convention";
if (ourproto.printModelInDecl())
s << ": " << ourproto.getModelName();
if (!ourproto.hasCustomStorage() && (ourproto.isInputLocked() || ourproto.isOutputLocked()))
s << " -- yet parameter storage is locked";
data.warningHeader(s.str());
}
int4 numcalls = data.numCalls();
for(int4 i=0;i<numcalls;++i) {
FuncCallSpecs *fc = data.getCallSpecs(i);

View file

@ -2104,6 +2104,7 @@ ProtoModel::ProtoModel(Architecture *g)
stackgrowsnegative = true; // Normal stack parameter ordering
hasThis = false;
isConstruct = false;
isPrinted = true;
defaultLocalRange();
defaultParamRange();
}
@ -2116,6 +2117,7 @@ ProtoModel::ProtoModel(const string &nm,const ProtoModel &op2)
{
glb = op2.glb;
name = nm;
isPrinted = true; // Don't inherit. Always print unless setPrintInDecl called explicitly
extrapop = op2.extrapop;
if (op2.input != (ParamList *)0)
input = op2.input->clone();
@ -2302,6 +2304,7 @@ void ProtoModel::decode(Decoder &decoder)
extrapop = -300;
hasThis = false;
isConstruct = false;
isPrinted = true;
effectlist.clear();
injectUponEntry = -1;
injectUponReturn = -1;
@ -3546,7 +3549,6 @@ void FuncProto::setModel(ProtoModel *m)
model = m;
extrapop = ProtoModel::extrapop_unknown;
}
flags &= ~((uint4)unknown_model); // Model is not "unknown" (even if null pointer is passed in)
}
/// The full function prototype is (re)set from a model, names, and data-types
@ -4374,7 +4376,6 @@ void FuncProto::decode(Decoder &decoder,Architecture *glb)
throw LowlevelError("Prototype storage must be set before restoring FuncProto");
ProtoModel *mod = (ProtoModel *)0;
bool seenextrapop = false;
bool seenunknownmod = false;
int4 readextrapop;
flags = 0;
injectid = -1;
@ -4384,14 +4385,13 @@ void FuncProto::decode(Decoder &decoder,Architecture *glb)
if (attribId == 0) break;
if (attribId == ATTRIB_MODEL) {
string modelname = decoder.readString();
if ((modelname == "default")||(modelname.size()==0))
mod = glb->defaultfp; // Get default model
else if (modelname == "unknown") {
mod = glb->defaultfp; // Use the default
seenunknownmod = true;
}
else
if (modelname.size()==0 || modelname == "default")
mod = glb->defaultfp; // Use the default model
else {
mod = glb->getModel(modelname);
if (mod == (ProtoModel *)0) // Model name is unrecognized
mod = glb->createUnknownModel(modelname); // Create model with placeholder behavior
}
}
else if (attribId == ATTRIB_EXTRAPOP) {
seenextrapop = true;
@ -4438,8 +4438,6 @@ void FuncProto::decode(Decoder &decoder,Architecture *glb)
setModel(mod); // This sets extrapop to model default
if (seenextrapop) // If explicitly set
extrapop = readextrapop;
if (seenunknownmod)
flags |= unknown_model;
uint4 subId = decoder.peekElement();
if (subId != 0) {

View file

@ -715,6 +715,7 @@ class ProtoModel {
bool stackgrowsnegative; ///< True if stack parameters have (normal) low address to high address ordering
bool hasThis; ///< True if this model has a \b this parameter (auto-parameter)
bool isConstruct; ///< True if this model is a constructor for a particular object
bool isPrinted; ///< True if this model should be printed as part of function declarations
void defaultLocalRange(void); ///< Set the default stack range used for local variables
void defaultParamRange(void); ///< Set the default stack range used for input parameters
void buildParamList(const string &strategy); ///< Establish the main resource lists for input and output parameters.
@ -927,6 +928,8 @@ public:
bool isStackGrowsNegative(void) const { return stackgrowsnegative; } ///< Return \b true if the stack \e grows toward smaller addresses
bool hasThisPointer(void) const { return hasThis; } ///< Is \b this a model for (non-static) class methods
bool isConstructor(void) const { return isConstruct; } ///< Is \b this model for class constructors
bool printInDecl(void) const { return isPrinted; } ///< Return \b true if name should be printed in function declarations
void setPrintInDecl(bool val) { isPrinted = val; } ///< Set whether \b this name should be printed in function declarations
/// \brief Return the maximum heritage delay across all possible input parameters
///
@ -945,11 +948,26 @@ public:
int4 getMaxOutputDelay(void) const { return output->getMaxDelay(); }
virtual bool isMerged(void) const { return false; } ///< Is \b this a merged prototype model
virtual bool isUnknown(void) const { return false; } ///< Is \b this an unrecognized prototype model
virtual void decode(Decoder &decoder); ///< Restore \b this model from a stream
static uint4 lookupEffect(const vector<EffectRecord> &efflist,const Address &addr,int4 size);
static int4 lookupRecord(const vector<EffectRecord> &efflist,int4 listSize,const Address &addr,int4 size);
};
/// \brief An unrecognized prototype model
///
/// This kind of model is created for function prototypes that specify a model name for which
/// there is no matching object. A model is created for the name by cloning behavior from a
/// placeholder model, usually the \e default model. This object mostly behaves like its placeholder
/// model but can identify itself as an \e unknown model and adopts the unrecognized model name.
class UnknownProtoModel : public ProtoModel {
ProtoModel *placeholderModel; ///< The model whose behavior \b this adopts as a behavior placeholder
public:
UnknownProtoModel(const string &nm,ProtoModel *placeHold) : ProtoModel(nm,*placeHold) { placeholderModel = placeHold; }
ProtoModel *getPlaceholderModel(void) const { return placeholderModel; } ///< Retrieve the placeholder model
virtual bool isUnknown(void) const { return true; }
};
/// \brief Class for calculating "goodness of fit" of parameter trials against a prototype model
///
/// The class is instantiated with a prototype model (ProtoModel). A set of Varnode parameter trials
@ -1280,11 +1298,10 @@ class FuncProto {
error_inputparam = 64, ///< Set if the input parameters are not properly represented
error_outputparam = 128, ///< Set if the return value(s) are not properly represented
custom_storage = 256, ///< Parameter storage is custom (not derived from ProtoModel)
unknown_model = 512, ///< Set if the PrototypeModel isn't known
is_constructor = 0x400, ///< Function is an (object-oriented) constructor
is_destructor = 0x800, ///< Function is an (object-oriented) destructor
has_thisptr= 0x1000, ///< Function is a method with a 'this' pointer as an argument
is_override = 0x2000 ///< Set if \b this prototype is created to override a single call site
is_constructor = 0x200, ///< Function is an (object-oriented) constructor
is_destructor = 0x400, ///< Function is an (object-oriented) destructor
has_thisptr= 0x800, ///< Function is a method with a 'this' pointer as an argument
is_override = 0x1000 ///< Set if \b this prototype is created to override a single call site
};
ProtoModel *model; ///< Model of for \b this prototype
ProtoStore *store; ///< Storage interface for parameters
@ -1317,15 +1334,15 @@ public:
void setModel(ProtoModel *m); ///< Set the prototype model for \b this
bool hasModel(void) const { return (model != (ProtoModel *)0); } ///< Does \b this prototype have a model
bool hasMatchingModel(const FuncProto *op2) const { return (model == op2->model); } ///< Does \b this have a matching model
bool hasMatchingModel(const ProtoModel *op2) const { return (model == op2); } ///< Does \b this use the given model
const string &getModelName(void) const { return model->getName(); } ///< Get the prototype model name
int4 getModelExtraPop(void) const { return model->getExtraPop(); } ///< Get the \e extrapop of the prototype model
bool isModelUnknown(void) const { return model->isUnknown(); } ///< Return \b true if the prototype model is \e unknown
bool printModelInDecl(void) const { return model->printInDecl(); } ///< Return \b true if the name should be printed in declarations
bool isInputLocked(void) const; ///< Are input data-types locked
bool isOutputLocked(void) const { return store->getOutput()->isTypeLocked(); } ///< Is the output data-type locked
bool isModelLocked(void) const { return ((flags&modellock)!=0); } ///< Is the prototype model for \b this locked
bool isUnknownModel(void) const { return ((flags&unknown_model)!=0); } ///< Is prototype model officially "unknown"
bool hasCustomStorage(void) const { return ((flags&custom_storage)!=0); } ///< Is \b this a "custom" function prototype
void setInputLock(bool val); ///< Toggle the data-type lock on input parameters
void setOutputLock(bool val); ///< Toggle the data-type lock on the return value

View file

@ -257,7 +257,10 @@ string OptionReadOnly::apply(Architecture *glb,const string &p1,const string &p2
string OptionDefaultPrototype::apply(Architecture *glb,const string &p1,const string &p2,const string &p3) const
{
glb->setDefaultModel(p1);
ProtoModel *model = glb->getModel(p1);
if (model == (ProtoModel *)0)
throw LowlevelError("Unknown prototype model :" + p1);
glb->setDefaultModel(model);
return "Set default prototype to "+p1;
}
@ -764,7 +767,7 @@ string OptionProtoEval::apply(Architecture *glb,const string &p1,const string &p
if (p1 == "default")
model = glb->defaultfp;
else {
model = glb->protoModels[p1];
model = glb->getModel(p1);
if (model == (ProtoModel *)0)
throw ParseError("Unknown prototype model: "+p1);
}

View file

@ -37,16 +37,6 @@ ElementId ELEM_VARIABLE = ElementId("variable",26);
const string Emit::EMPTY_STRING = "";
const string EmitMarkup::highlight[] = { "keyword",
"comment",
"type",
"funcname",
"var",
"const",
"param",
"global",
"" };
/// \brief Emit a sequence of space characters as part of source code
///
/// \param num is the number of space characters to emit
@ -161,7 +151,7 @@ void EmitMarkup::tagVariable(const string &name,syntax_highlight hl,const Varnod
{
encoder->openElement(ELEM_VARIABLE);
if (hl != no_color)
encoder->writeString(ATTRIB_COLOR, highlight[(int4)hl]);
encoder->writeUnsignedInteger(ATTRIB_COLOR, hl);
if (vn != (const Varnode *)0)
encoder->writeUnsignedInteger(ATTRIB_VARREF, vn->getCreateIndex());
if (op != (const PcodeOp *)0)
@ -175,7 +165,7 @@ void EmitMarkup::tagOp(const string &name,syntax_highlight hl,const PcodeOp *op)
{
encoder->openElement(ELEM_OP);
if (hl != no_color)
encoder->writeString(ATTRIB_COLOR,highlight[(int4)hl]);
encoder->writeUnsignedInteger(ATTRIB_COLOR,hl);
if (op != (const PcodeOp *)0)
encoder->writeUnsignedInteger(ATTRIB_OPREF, op->getTime());
encoder->writeString(ATTRIB_CONTENT,name);
@ -187,7 +177,7 @@ void EmitMarkup::tagFuncName(const string &name,syntax_highlight hl,const Funcda
{
encoder->openElement(ELEM_FUNCNAME);
if (hl != no_color)
encoder->writeString(ATTRIB_COLOR,highlight[(int4)hl]);
encoder->writeUnsignedInteger(ATTRIB_COLOR,hl);
if (op != (const PcodeOp *)0)
encoder->writeUnsignedInteger(ATTRIB_OPREF, op->getTime());
encoder->writeString(ATTRIB_CONTENT,name);
@ -199,7 +189,7 @@ void EmitMarkup::tagType(const string &name,syntax_highlight hl,const Datatype *
{
encoder->openElement(ELEM_TYPE);
if (hl != no_color)
encoder->writeString(ATTRIB_COLOR,highlight[(int4)hl]);
encoder->writeUnsignedInteger(ATTRIB_COLOR,hl);
if (ct->getId() != 0) {
encoder->writeUnsignedInteger(ATTRIB_ID, ct->getId());
}
@ -212,7 +202,7 @@ void EmitMarkup::tagField(const string &name,syntax_highlight hl,const Datatype
{
encoder->openElement(ELEM_FIELD);
if (hl != no_color)
encoder->writeString(ATTRIB_COLOR,highlight[(int4)hl]);
encoder->writeUnsignedInteger(ATTRIB_COLOR,hl);
if (ct != (const Datatype *)0) {
encoder->writeString(ATTRIB_NAME,ct->getName());
if (ct->getId() != 0) {
@ -231,7 +221,7 @@ void EmitMarkup::tagComment(const string &name,syntax_highlight hl,const AddrSpa
{
encoder->openElement(ELEM_COMMENT);
if (hl != no_color)
encoder->writeString(ATTRIB_COLOR,highlight[(int4)hl]);
encoder->writeUnsignedInteger(ATTRIB_COLOR,hl);
encoder->writeSpace(ATTRIB_SPACE, spc);
encoder->writeUnsignedInteger(ATTRIB_OFF, off);
encoder->writeString(ATTRIB_CONTENT,name);
@ -243,7 +233,7 @@ void EmitMarkup::tagLabel(const string &name,syntax_highlight hl,const AddrSpace
{
encoder->openElement(ELEM_LABEL);
if (hl != no_color)
encoder->writeString(ATTRIB_COLOR,highlight[(int4)hl]);
encoder->writeUnsignedInteger(ATTRIB_COLOR,hl);
encoder->writeSpace(ATTRIB_SPACE,spc);
encoder->writeUnsignedInteger(ATTRIB_OFF, off);
encoder->writeString(ATTRIB_CONTENT,name);
@ -255,7 +245,7 @@ void EmitMarkup::print(const string &data,syntax_highlight hl)
{
encoder->openElement(ELEM_SYNTAX);
if (hl != no_color)
encoder->writeString(ATTRIB_COLOR,highlight[(int4)hl]);
encoder->writeUnsignedInteger(ATTRIB_COLOR,hl);
encoder->writeString(ATTRIB_CONTENT,data);
encoder->closeElement(ELEM_SYNTAX);
}

View file

@ -103,6 +103,8 @@ protected:
public:
Emit(void) { indentlevel=0; parenlevel=0; pendPrint=(PendPrint *)0; resetDefaultsInternal(); } ///< Constructor
/// \brief Possible types of syntax highlighting
///
/// Values must match constants in ClangToken
enum syntax_highlight {
keyword_color = 0, ///< Keyword in the high-level language
comment_color = 1, ///< Comments
@ -112,7 +114,8 @@ public:
const_color = 5, ///< Constant values
param_color = 6, ///< Function parameters
global_color = 7, ///< Global variable identifiers
no_color = 8 ///< Un-highlighted
no_color = 8, ///< Un-highlighted
error_color = 9 ///< Indicates a warning or error state
};
virtual ~Emit(void) {} ///< Destructor
@ -437,7 +440,6 @@ public:
/// This class can be used as the low-level back-end to EmitPrettyPrint to provide a solution
/// that does both pretty printing and markup.
class EmitMarkup : public Emit {
static const string highlight[]; ///< Map from syntax_highlight enumeration to color attribute string
protected:
ostream *s; ///< Stream being emitted to
Encoder *encoder; ///< How markup is encoded to the output stream

View file

@ -2479,11 +2479,10 @@ void PrintC::emitFunctionDeclaration(const Funcdata *fd)
emitPrototypeOutput(proto,fd);
emit->spaces(1);
if (option_convention) {
if (fd->getFuncProto().hasModel()) {
if (!fd->getFuncProto().hasMatchingModel(fd->getArch()->defaultfp)) { // If not the default
emit->print(fd->getFuncProto().getModelName(),EmitMarkup::keyword_color);
emit->spaces(1);
}
if (fd->getFuncProto().printModelInDecl()) {
Emit::syntax_highlight highlight = fd->getFuncProto().isModelUnknown() ? Emit::error_color : Emit::keyword_color;
emit->print(fd->getFuncProto().getModelName(),highlight);
emit->spaces(1);
}
}
int4 id1 = emit->openGroup();