mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-05 10:49:34 +02:00
GP-1653 Allow PrototypeModel aliases
This commit is contained in:
parent
22a5950d08
commit
cb913e6a91
6 changed files with 234 additions and 45 deletions
|
@ -747,6 +747,7 @@ void Architecture::parseDynamicRule(const Element *el)
|
||||||
/// This handles the \<prototype> and \<resolveprototype> tags. It builds the
|
/// This handles the \<prototype> and \<resolveprototype> tags. It builds the
|
||||||
/// ProtoModel object based on the tag and makes it available generally to the decompiler.
|
/// ProtoModel object based on the tag and makes it available generally to the decompiler.
|
||||||
/// \param el is the XML tag element
|
/// \param el is the XML tag element
|
||||||
|
/// \return the new ProtoModel object
|
||||||
ProtoModel *Architecture::parseProto(const Element *el)
|
ProtoModel *Architecture::parseProto(const Element *el)
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -1100,6 +1101,26 @@ void Architecture::parseAggressiveTrim(const Element *el)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Clone the named ProtoModel, attaching it to another name.
|
||||||
|
/// \param aliasName is the new name to assign
|
||||||
|
/// \param parentName is the name of the parent model
|
||||||
|
void Architecture::createModelAlias(const string &aliasName,const string &parentName)
|
||||||
|
|
||||||
|
{
|
||||||
|
map<string,ProtoModel *>::const_iterator iter = protoModels.find(parentName);
|
||||||
|
if (iter == protoModels.end())
|
||||||
|
throw LowlevelError("Requesting non-existent prototype model: "+parentName);
|
||||||
|
ProtoModel *model = (*iter).second;
|
||||||
|
if (model->isMerged())
|
||||||
|
throw LowlevelError("Cannot make alias of merged model: "+parentName);
|
||||||
|
if (model->getAliasParent() != (const ProtoModel *)0)
|
||||||
|
throw LowlevelError("Cannot make alias of an alias: "+parentName);
|
||||||
|
iter = protoModels.find(aliasName);
|
||||||
|
if (iter != protoModels.end())
|
||||||
|
throw LowlevelError("Duplicate ProtoModel name: "+aliasName);
|
||||||
|
protoModels[aliasName] = new ProtoModel(aliasName,*model);
|
||||||
|
}
|
||||||
|
|
||||||
/// This looks for the \<processor_spec> tag and and sets configuration
|
/// This looks for the \<processor_spec> tag and and sets configuration
|
||||||
/// parameters based on it.
|
/// parameters based on it.
|
||||||
/// \param store is the document store holding the tag
|
/// \param store is the document store holding the tag
|
||||||
|
@ -1215,6 +1236,12 @@ void Architecture::parseCompilerConfig(DocumentStorage &store)
|
||||||
parseDeadcodeDelay(*iter);
|
parseDeadcodeDelay(*iter);
|
||||||
else if (elname == "inferptrbounds")
|
else if (elname == "inferptrbounds")
|
||||||
parseInferPtrBounds(*iter);
|
parseInferPtrBounds(*iter);
|
||||||
|
else if (elname == "modelalias") {
|
||||||
|
const Element *el = *iter;
|
||||||
|
string aliasName = el->getAttributeValue("name");
|
||||||
|
string parentName = el->getAttributeValue("parent");
|
||||||
|
createModelAlias(aliasName, parentName);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
el = store.getTag("specextensions"); // Look for any user-defined configuration document
|
el = store.getTag("specextensions"); // Look for any user-defined configuration document
|
||||||
|
@ -1224,7 +1251,7 @@ void Architecture::parseCompilerConfig(DocumentStorage &store)
|
||||||
const string &elname( (*iter)->getName() );
|
const string &elname( (*iter)->getName() );
|
||||||
if (elname == "prototype")
|
if (elname == "prototype")
|
||||||
parseProto(*iter);
|
parseProto(*iter);
|
||||||
else if (elname == "callfixup") {
|
else if (elname == "callfixup") {
|
||||||
pcodeinjectlib->restoreXmlInject(archid+" : compiler spec", (*iter)->getAttributeValue("name"),
|
pcodeinjectlib->restoreXmlInject(archid+" : compiler spec", (*iter)->getAttributeValue("name"),
|
||||||
InjectPayload::CALLFIXUP_TYPE, *iter);
|
InjectPayload::CALLFIXUP_TYPE, *iter);
|
||||||
}
|
}
|
||||||
|
@ -1253,8 +1280,7 @@ void Architecture::parseCompilerConfig(DocumentStorage &store)
|
||||||
// We must have a __thiscall calling convention
|
// We must have a __thiscall calling convention
|
||||||
map<string,ProtoModel *>::iterator miter = protoModels.find("__thiscall");
|
map<string,ProtoModel *>::iterator miter = protoModels.find("__thiscall");
|
||||||
if (miter == protoModels.end()) { // If __thiscall doesn't exist we clone it off of the default
|
if (miter == protoModels.end()) { // If __thiscall doesn't exist we clone it off of the default
|
||||||
ProtoModel *thismodel = new ProtoModel("__thiscall",*defaultfp);
|
createModelAlias("__thiscall",defaultfp->getName());
|
||||||
protoModels["__thiscall"] = thismodel;
|
|
||||||
}
|
}
|
||||||
userops.setDefaults(this);
|
userops.setDefaults(this);
|
||||||
initializeSegments();
|
initializeSegments();
|
||||||
|
|
|
@ -258,6 +258,7 @@ protected:
|
||||||
void fillinReadOnlyFromLoader(void); ///< Load info about read-only sections
|
void fillinReadOnlyFromLoader(void); ///< Load info about read-only sections
|
||||||
void initializeSegments(); ///< Set up segment resolvers
|
void initializeSegments(); ///< Set up segment resolvers
|
||||||
void cacheAddrSpaceProperties(void); ///< Calculate some frequently used space properties and cache them
|
void cacheAddrSpaceProperties(void); ///< Calculate some frequently used space properties and cache them
|
||||||
|
void createModelAlias(const string &aliasName,const string &parentName); ///< Create name alias for a ProtoModel
|
||||||
|
|
||||||
void parseProcessorConfig(DocumentStorage &store); ///< Apply processor specific configuration
|
void parseProcessorConfig(DocumentStorage &store); ///< Apply processor specific configuration
|
||||||
void parseCompilerConfig(DocumentStorage &store); ///< Apply compiler specific configuration
|
void parseCompilerConfig(DocumentStorage &store); ///< Apply compiler specific configuration
|
||||||
|
|
|
@ -672,6 +672,7 @@ public:
|
||||||
virtual ~ProtoModel(void); ///< Destructor
|
virtual ~ProtoModel(void); ///< Destructor
|
||||||
const string &getName(void) const { return name; } ///< Get the name of the prototype model
|
const string &getName(void) const { return name; } ///< Get the name of the prototype model
|
||||||
Architecture *getArch(void) const { return glb; } ///< Get the owning Architecture
|
Architecture *getArch(void) const { return glb; } ///< Get the owning Architecture
|
||||||
|
const ProtoModel *getAliasParent(void) const { return compatModel; } ///< Return \e model \b this is an alias of (or null)
|
||||||
uint4 hasEffect(const Address &addr,int4 size) const; ///< Determine side-effect of \b this on the given memory range
|
uint4 hasEffect(const Address &addr,int4 size) const; ///< Determine side-effect of \b this on the given memory range
|
||||||
int4 getExtraPop(void) const { return extrapop; } ///< Get the stack-pointer \e extrapop for \b this model
|
int4 getExtraPop(void) const { return extrapop; } ///< Get the stack-pointer \e extrapop for \b this model
|
||||||
void setExtraPop(int4 ep) { extrapop = ep; } ///< Set the stack-pointer \e extrapop
|
void setExtraPop(int4 ep) { extrapop = ep; } ///< Set the stack-pointer \e extrapop
|
||||||
|
|
|
@ -263,6 +263,13 @@
|
||||||
</element>
|
</element>
|
||||||
</zeroOrMore>
|
</zeroOrMore>
|
||||||
|
|
||||||
|
<zeroOrMore>
|
||||||
|
<element name="modelalias">
|
||||||
|
<attribute name="name"/>
|
||||||
|
<attribute name="parent"/>
|
||||||
|
</element>
|
||||||
|
</zeroOrMore>
|
||||||
|
|
||||||
<optional>
|
<optional>
|
||||||
<element name="eval_current_prototype">
|
<element name="eval_current_prototype">
|
||||||
<attribute name="name"/>
|
<attribute name="name"/>
|
||||||
|
|
|
@ -36,6 +36,7 @@ import ghidra.program.model.pcode.AddressXML;
|
||||||
import ghidra.program.model.pcode.Varnode;
|
import ghidra.program.model.pcode.Varnode;
|
||||||
import ghidra.util.Msg;
|
import ghidra.util.Msg;
|
||||||
import ghidra.util.SystemUtilities;
|
import ghidra.util.SystemUtilities;
|
||||||
|
import ghidra.util.exception.DuplicateNameException;
|
||||||
import ghidra.util.xml.SpecXmlUtils;
|
import ghidra.util.xml.SpecXmlUtils;
|
||||||
import ghidra.xml.*;
|
import ghidra.xml.*;
|
||||||
|
|
||||||
|
@ -58,7 +59,6 @@ public class BasicCompilerSpec implements CompilerSpec {
|
||||||
protected PrototypeModel evalCalledModel; // Default model used to evaluate a called function
|
protected PrototypeModel evalCalledModel; // Default model used to evaluate a called function
|
||||||
protected PrototypeModel[] allmodels; // All models
|
protected PrototypeModel[] allmodels; // All models
|
||||||
protected PrototypeModel[] models; // All models excluding merge models
|
protected PrototypeModel[] models; // All models excluding merge models
|
||||||
private boolean copiedThisModel; // true if __thiscall is copied from default model
|
|
||||||
private Register stackPointer; // Register holding the stack pointer
|
private Register stackPointer; // Register holding the stack pointer
|
||||||
private AddressSpace stackSpace;
|
private AddressSpace stackSpace;
|
||||||
private AddressSpace stackBaseSpace;
|
private AddressSpace stackBaseSpace;
|
||||||
|
@ -88,9 +88,11 @@ public class BasicCompilerSpec implements CompilerSpec {
|
||||||
* @throws XmlParseException for badly formed XML
|
* @throws XmlParseException for badly formed XML
|
||||||
* @throws SAXException for syntax errors in the XML
|
* @throws SAXException for syntax errors in the XML
|
||||||
* @throws IOException for errors accessing the stream
|
* @throws IOException for errors accessing the stream
|
||||||
|
* @throws DuplicateNameException if there exists more than one PrototypeModel with the same name
|
||||||
*/
|
*/
|
||||||
public BasicCompilerSpec(CompilerSpecDescription description, SleighLanguage language,
|
public BasicCompilerSpec(CompilerSpecDescription description, SleighLanguage language,
|
||||||
InputStream stream) throws XmlParseException, SAXException, IOException {
|
InputStream stream)
|
||||||
|
throws XmlParseException, SAXException, IOException, DuplicateNameException {
|
||||||
this.description = description;
|
this.description = description;
|
||||||
this.language = language;
|
this.language = language;
|
||||||
buildInjectLibrary();
|
buildInjectLibrary();
|
||||||
|
@ -140,7 +142,7 @@ public class BasicCompilerSpec implements CompilerSpec {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (IOException | SAXException | XmlParseException e) {
|
catch (IOException | SAXException | XmlParseException | DuplicateNameException e) {
|
||||||
parseException = e;
|
parseException = e;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -167,7 +169,6 @@ public class BasicCompilerSpec implements CompilerSpec {
|
||||||
evalCalledModel = op2.evalCalledModel;
|
evalCalledModel = op2.evalCalledModel;
|
||||||
defaultModel = op2.defaultModel;
|
defaultModel = op2.defaultModel;
|
||||||
allmodels = op2.allmodels;
|
allmodels = op2.allmodels;
|
||||||
copiedThisModel = op2.copiedThisModel;
|
|
||||||
globalSet = op2.globalSet; // May need to clone if \<global> tag becomes user extendable
|
globalSet = op2.globalSet; // May need to clone if \<global> tag becomes user extendable
|
||||||
joinSpace = op2.joinSpace; // AddressSpace is immutable
|
joinSpace = op2.joinSpace; // AddressSpace is immutable
|
||||||
models = op2.models;
|
models = op2.models;
|
||||||
|
@ -219,7 +220,8 @@ public class BasicCompilerSpec implements CompilerSpec {
|
||||||
return errHandler;
|
return errHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initialize(String srcName, XmlPullParser parser) throws XmlParseException {
|
private void initialize(String srcName, XmlPullParser parser)
|
||||||
|
throws XmlParseException, DuplicateNameException {
|
||||||
this.sourceName = srcName;
|
this.sourceName = srcName;
|
||||||
spaceBases = null;
|
spaceBases = null;
|
||||||
extraRanges = null;
|
extraRanges = null;
|
||||||
|
@ -236,7 +238,6 @@ public class BasicCompilerSpec implements CompilerSpec {
|
||||||
funcPtrAlign = 0;
|
funcPtrAlign = 0;
|
||||||
deadCodeDelay = null;
|
deadCodeDelay = null;
|
||||||
inferPtrBounds = null;
|
inferPtrBounds = null;
|
||||||
copiedThisModel = false;
|
|
||||||
|
|
||||||
restoreXml(parser);
|
restoreXml(parser);
|
||||||
}
|
}
|
||||||
|
@ -280,28 +281,6 @@ public class BasicCompilerSpec implements CompilerSpec {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addThisCallConventionIfMissing(List<PrototypeModel> modelList,
|
|
||||||
String defaultName) {
|
|
||||||
if (defaultName == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
boolean foundThisCall = false;
|
|
||||||
PrototypeModel defModel = null;
|
|
||||||
for (PrototypeModel model : modelList) {
|
|
||||||
if (CALLING_CONVENTION_thiscall.equals(model.name)) {
|
|
||||||
foundThisCall = true;
|
|
||||||
}
|
|
||||||
if (defaultName.equals(model.name)) {
|
|
||||||
defModel = model;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (defModel != null && !foundThisCall) {
|
|
||||||
PrototypeModel thisModel = new PrototypeModel(CALLING_CONVENTION_thiscall, defModel);
|
|
||||||
modelList.add(thisModel);
|
|
||||||
copiedThisModel = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void applyContextSettings(DefaultProgramContext programContext) {
|
public void applyContextSettings(DefaultProgramContext programContext) {
|
||||||
for (ContextSetting cs : ctxsetting) {
|
for (ContextSetting cs : ctxsetting) {
|
||||||
|
@ -467,21 +446,28 @@ public class BasicCompilerSpec implements CompilerSpec {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Establish cross referencing to prototype models.
|
* Establish cross referencing to prototype models.
|
||||||
* All xrefs are regenerated from a single complete list of PrototypeModels
|
* All xrefs are regenerated from a single complete list of PrototypeModels.
|
||||||
|
* If there are PrototypeModels with duplicate names, return an example name.
|
||||||
|
* Return null otherwise
|
||||||
*
|
*
|
||||||
* @param modelList is the complete list of models
|
* @param modelList is the complete list of models
|
||||||
* @param defaultName is the name to use for the default model (or null)
|
* @param defaultName is the name to use for the default model (or null)
|
||||||
* @param evalCurrent is the name to use for evaluating the current function (or null)
|
* @param evalCurrent is the name to use for evaluating the current function (or null)
|
||||||
* @param evalCalled is the name to use for evaluating called functions (or null)
|
* @param evalCalled is the name to use for evaluating called functions (or null)
|
||||||
|
* @return a PrototypeModel name that was duplicated or null
|
||||||
*/
|
*/
|
||||||
protected void modelXrefs(List<PrototypeModel> modelList, String defaultName,
|
protected String modelXrefs(List<PrototypeModel> modelList, String defaultName,
|
||||||
String evalCurrent, String evalCalled) {
|
String evalCurrent, String evalCalled) {
|
||||||
|
String foundDuplicate = null;
|
||||||
buildModelArrays(modelList);
|
buildModelArrays(modelList);
|
||||||
callingConventionMap = new HashMap<>();
|
callingConventionMap = new HashMap<>();
|
||||||
for (PrototypeModel model : models) {
|
for (PrototypeModel model : models) {
|
||||||
String name = model.getName();
|
String name = model.getName();
|
||||||
if (name != null) {
|
if (name != null) {
|
||||||
callingConventionMap.put(name, model);
|
PrototypeModel previous = callingConventionMap.put(name, model);
|
||||||
|
if (previous != null) {
|
||||||
|
foundDuplicate = name;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -500,6 +486,7 @@ public class BasicCompilerSpec implements CompilerSpec {
|
||||||
evalCalledModel = evalmodel;
|
evalCalledModel = evalmodel;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return foundDuplicate;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -538,10 +525,6 @@ public class BasicCompilerSpec implements CompilerSpec {
|
||||||
if (model == defaultModel) {
|
if (model == defaultModel) {
|
||||||
continue; // Already emitted
|
continue; // Already emitted
|
||||||
}
|
}
|
||||||
if (copiedThisModel && model.hasThisPointer() &&
|
|
||||||
model.name.equals(CALLING_CONVENTION_thiscall)) {
|
|
||||||
continue; // Don't need to emit the copy
|
|
||||||
}
|
|
||||||
model.saveXml(buffer, pcodeInject);
|
model.saveXml(buffer, pcodeInject);
|
||||||
}
|
}
|
||||||
if (evalCurrentModel != null && evalCurrentModel != defaultModel) {
|
if (evalCurrentModel != null && evalCurrentModel != defaultModel) {
|
||||||
|
@ -571,10 +554,12 @@ public class BasicCompilerSpec implements CompilerSpec {
|
||||||
* Initialize this object from an XML stream. A single \<compiler_spec> tag is expected.
|
* Initialize this object from an XML stream. A single \<compiler_spec> tag is expected.
|
||||||
* @param parser is the XML stream
|
* @param parser is the XML stream
|
||||||
* @throws XmlParseException for badly formed XML
|
* @throws XmlParseException for badly formed XML
|
||||||
|
* @throws DuplicateNameException if we parse more than one PrototypeModel with the same name
|
||||||
*/
|
*/
|
||||||
private void restoreXml(XmlPullParser parser) throws XmlParseException {
|
private void restoreXml(XmlPullParser parser) throws XmlParseException, DuplicateNameException {
|
||||||
List<PrototypeModel> modelList = new ArrayList<>();
|
List<PrototypeModel> modelList = new ArrayList<>();
|
||||||
boolean seenDefault = false;
|
boolean seenDefault = false;
|
||||||
|
boolean seenThisCall = false;
|
||||||
String defaultName = null;
|
String defaultName = null;
|
||||||
String evalCurrentPrototype = null;
|
String evalCurrentPrototype = null;
|
||||||
String evalCalledPrototype = null;
|
String evalCalledPrototype = null;
|
||||||
|
@ -617,12 +602,28 @@ public class BasicCompilerSpec implements CompilerSpec {
|
||||||
defaultName = model.name;
|
defaultName = model.name;
|
||||||
seenDefault = true;
|
seenDefault = true;
|
||||||
}
|
}
|
||||||
|
if (model.getName().equals(CALLING_CONVENTION_thiscall)) {
|
||||||
|
seenThisCall = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (name.equals("prototype")) {
|
else if (name.equals("prototype")) {
|
||||||
PrototypeModel model = addPrototypeModel(modelList, parser);
|
PrototypeModel model = addPrototypeModel(modelList, parser);
|
||||||
if (defaultName == null) {
|
if (defaultName == null) {
|
||||||
defaultName = model.name;
|
defaultName = model.name;
|
||||||
}
|
}
|
||||||
|
if (model.getName().equals(CALLING_CONVENTION_thiscall)) {
|
||||||
|
seenThisCall = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (name.equals("modelalias")) {
|
||||||
|
XmlElement el = parser.start();
|
||||||
|
String aliasName = el.getAttribute("name");
|
||||||
|
String parentName = el.getAttribute("parent");
|
||||||
|
parser.end(el);
|
||||||
|
createModelAlias(aliasName, parentName, modelList);
|
||||||
|
if (aliasName.equals(CALLING_CONVENTION_thiscall)) {
|
||||||
|
seenThisCall = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (name.equals("resolveprototype")) {
|
else if (name.equals("resolveprototype")) {
|
||||||
addPrototypeModel(modelList, parser);
|
addPrototypeModel(modelList, parser);
|
||||||
|
@ -683,8 +684,14 @@ public class BasicCompilerSpec implements CompilerSpec {
|
||||||
language.getDefaultSpace().getSize(),
|
language.getDefaultSpace().getSize(),
|
||||||
language.getDefaultSpace().getAddressableUnitSize(), AddressSpace.TYPE_STACK, 0);
|
language.getDefaultSpace().getAddressableUnitSize(), AddressSpace.TYPE_STACK, 0);
|
||||||
}
|
}
|
||||||
addThisCallConventionIfMissing(modelList, defaultName);
|
if (!seenThisCall) {
|
||||||
modelXrefs(modelList, defaultName, evalCurrentPrototype, evalCalledPrototype);
|
createModelAlias(CALLING_CONVENTION_thiscall, defaultName, modelList);
|
||||||
|
}
|
||||||
|
String dupName =
|
||||||
|
modelXrefs(modelList, defaultName, evalCurrentPrototype, evalCalledPrototype);
|
||||||
|
if (dupName != null) {
|
||||||
|
throw new DuplicateNameException("Multiple prototype models with the name: " + dupName);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void saveProperties(StringBuilder buffer) {
|
private void saveProperties(StringBuilder buffer) {
|
||||||
|
@ -995,6 +1002,35 @@ public class BasicCompilerSpec implements CompilerSpec {
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clone the named PrototypeModel, attaching it to another name.
|
||||||
|
* @param aliasName is the new name
|
||||||
|
* @param parentName is the name of the PrototypeModel to clone
|
||||||
|
* @param modelList is the container
|
||||||
|
* @throws XmlParseException if the parent model cannot be established
|
||||||
|
*/
|
||||||
|
private void createModelAlias(String aliasName, String parentName,
|
||||||
|
List<PrototypeModel> modelList) throws XmlParseException {
|
||||||
|
PrototypeModel parentModel = null;
|
||||||
|
for (PrototypeModel model : modelList) {
|
||||||
|
if (parentName.equals(model.getName())) {
|
||||||
|
parentModel = model;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (parentModel == null) {
|
||||||
|
throw new XmlParseException("Parent for model alias does not exist: " + parentName);
|
||||||
|
}
|
||||||
|
if (parentModel.isMerged()) {
|
||||||
|
throw new XmlParseException("Cannot make alias of merged model: " + parentName);
|
||||||
|
}
|
||||||
|
if (parentModel.getAliasParent() != null) {
|
||||||
|
throw new XmlParseException("Cannot make alias of an alias: " + parentName);
|
||||||
|
}
|
||||||
|
PrototypeModel newModel = new PrototypeModel(aliasName, parentModel);
|
||||||
|
modelList.add(newModel);
|
||||||
|
}
|
||||||
|
|
||||||
private PrototypeModel addPrototypeModel(List<PrototypeModel> modelList, XmlPullParser parser)
|
private PrototypeModel addPrototypeModel(List<PrototypeModel> modelList, XmlPullParser parser)
|
||||||
throws XmlParseException {
|
throws XmlParseException {
|
||||||
PrototypeModel model;
|
PrototypeModel model;
|
||||||
|
@ -1116,7 +1152,7 @@ public class BasicCompilerSpec implements CompilerSpec {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
BasicCompilerSpec other = (BasicCompilerSpec) obj;
|
BasicCompilerSpec other = (BasicCompilerSpec) obj;
|
||||||
if (aggressiveTrim != other.aggressiveTrim || copiedThisModel != other.copiedThisModel) {
|
if (aggressiveTrim != other.aggressiveTrim) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!dataOrganization.isEquivalent(other.dataOrganization)) {
|
if (!dataOrganization.isEquivalent(other.dataOrganization)) {
|
||||||
|
|
|
@ -17,6 +17,7 @@ package ghidra.program.model.lang;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import ghidra.program.database.SpecExtension;
|
||||||
import ghidra.program.model.address.*;
|
import ghidra.program.model.address.*;
|
||||||
import ghidra.program.model.data.*;
|
import ghidra.program.model.data.*;
|
||||||
import ghidra.program.model.listing.*;
|
import ghidra.program.model.listing.*;
|
||||||
|
@ -31,9 +32,6 @@ import ghidra.xml.*;
|
||||||
* A function calling convention model.
|
* A function calling convention model.
|
||||||
* Formal specification of how a compiler passes
|
* Formal specification of how a compiler passes
|
||||||
* arguments between functions.
|
* arguments between functions.
|
||||||
*
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
public class PrototypeModel {
|
public class PrototypeModel {
|
||||||
public static final int UNKNOWN_EXTRAPOP = 0x8000;
|
public static final int UNKNOWN_EXTRAPOP = 0x8000;
|
||||||
|
@ -50,6 +48,7 @@ public class PrototypeModel {
|
||||||
private Varnode[] killedbycall; // Memory ranges definitely affected by calls
|
private Varnode[] killedbycall; // Memory ranges definitely affected by calls
|
||||||
private Varnode[] returnaddress; // Memory used to store the return address
|
private Varnode[] returnaddress; // Memory used to store the return address
|
||||||
private Varnode[] likelytrash; // Memory likely to be meaningless on input
|
private Varnode[] likelytrash; // Memory likely to be meaningless on input
|
||||||
|
private PrototypeModel compatModel; // The model this is an alias of
|
||||||
private AddressSet localRange; // Range on the stack considered for local storage
|
private AddressSet localRange; // Range on the stack considered for local storage
|
||||||
private AddressSet paramRange; // Range on the stack considered for parameter storage
|
private AddressSet paramRange; // Range on the stack considered for parameter storage
|
||||||
private InputListType inputListType = InputListType.STANDARD;
|
private InputListType inputListType = InputListType.STANDARD;
|
||||||
|
@ -59,6 +58,16 @@ public class PrototypeModel {
|
||||||
private boolean hasUponEntry; // Does this have an uponentry injection
|
private boolean hasUponEntry; // Does this have an uponentry injection
|
||||||
private boolean hasUponReturn; // Does this have an uponreturn injection
|
private boolean hasUponReturn; // Does this have an uponreturn injection
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a named alias of another PrototypeModel.
|
||||||
|
* All elements of the original model are copied except:
|
||||||
|
* 1) The name
|
||||||
|
* 2) The generic calling convention (which is based on name)
|
||||||
|
* 3) The hasThis property (which allows __thiscall to alias something else)
|
||||||
|
* 4) The "fact of" the model being an alias
|
||||||
|
* @param name is the name of the alias
|
||||||
|
* @param model is the other PrototypeModel
|
||||||
|
*/
|
||||||
public PrototypeModel(String name, PrototypeModel model) {
|
public PrototypeModel(String name, PrototypeModel model) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
isExtension = false;
|
isExtension = false;
|
||||||
|
@ -71,6 +80,7 @@ public class PrototypeModel {
|
||||||
killedbycall = model.killedbycall;
|
killedbycall = model.killedbycall;
|
||||||
returnaddress = model.returnaddress;
|
returnaddress = model.returnaddress;
|
||||||
likelytrash = model.likelytrash;
|
likelytrash = model.likelytrash;
|
||||||
|
compatModel = model;
|
||||||
localRange = new AddressSet(model.localRange);
|
localRange = new AddressSet(model.localRange);
|
||||||
paramRange = new AddressSet(model.paramRange);
|
paramRange = new AddressSet(model.paramRange);
|
||||||
hasThis = model.hasThis || name.equals(CompilerSpec.CALLING_CONVENTION_thiscall);
|
hasThis = model.hasThis || name.equals(CompilerSpec.CALLING_CONVENTION_thiscall);
|
||||||
|
@ -91,6 +101,7 @@ public class PrototypeModel {
|
||||||
killedbycall = null;
|
killedbycall = null;
|
||||||
returnaddress = null;
|
returnaddress = null;
|
||||||
likelytrash = null;
|
likelytrash = null;
|
||||||
|
compatModel = null;
|
||||||
localRange = null;
|
localRange = null;
|
||||||
paramRange = null;
|
paramRange = null;
|
||||||
genericCallingConvention = GenericCallingConvention.unknown;
|
genericCallingConvention = GenericCallingConvention.unknown;
|
||||||
|
@ -100,6 +111,10 @@ public class PrototypeModel {
|
||||||
hasUponReturn = false;
|
hasUponReturn = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the generic calling convention enum associated with this
|
||||||
|
* @return the enum
|
||||||
|
*/
|
||||||
public GenericCallingConvention getGenericCallingConvention() {
|
public GenericCallingConvention getGenericCallingConvention() {
|
||||||
return genericCallingConvention;
|
return genericCallingConvention;
|
||||||
}
|
}
|
||||||
|
@ -144,6 +159,12 @@ public class PrototypeModel {
|
||||||
return returnaddress;
|
return returnaddress;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If this returns true, it indicates this model is an artificial merge of other models.
|
||||||
|
* A merged model can be used as part of the analysis process when attempting to distinguish
|
||||||
|
* between different possible models for an unknown function.
|
||||||
|
* @return true if this model is an artificial merge of other models
|
||||||
|
*/
|
||||||
public boolean isMerged() {
|
public boolean isMerged() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -155,30 +176,58 @@ public class PrototypeModel {
|
||||||
return isExtension;
|
return isExtension;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the formal name of the model
|
||||||
|
*/
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of extra bytes popped from the stack when a function that uses
|
||||||
|
* this model returns to its caller. This is usually just the number of bytes used to
|
||||||
|
* store the return value, but some conventions may do additional clean up of stack parameters.
|
||||||
|
* A special value of UNKNOWN_EXTRAPOP indicates that the number of bytes is unknown.
|
||||||
|
* @return the number of extra bytes popped
|
||||||
|
*/
|
||||||
public int getExtrapop() {
|
public int getExtrapop() {
|
||||||
return extrapop;
|
return extrapop;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the number of bytes on the stack used, by this model, to store the return value
|
||||||
|
*/
|
||||||
public int getStackshift() {
|
public int getStackshift() {
|
||||||
return stackshift;
|
return stackshift;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return true if this model has an implied "this" parameter for referencing class data
|
||||||
|
*/
|
||||||
public boolean hasThisPointer() {
|
public boolean hasThisPointer() {
|
||||||
return hasThis;
|
return hasThis;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return true if this model is used specifically for class constructors
|
||||||
|
*/
|
||||||
public boolean isConstructor() {
|
public boolean isConstructor() {
|
||||||
return isConstruct;
|
return isConstruct;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the allocation strategy for this model
|
||||||
|
*/
|
||||||
public InputListType getInputListType() {
|
public InputListType getInputListType() {
|
||||||
return inputListType;
|
return inputListType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return true if this model has specific p-code injections associated with it
|
||||||
|
* (either an "uponentry" or "uponreturn" payload),
|
||||||
|
* which are used to decompile functions with this model.
|
||||||
|
* @return true if this model uses p-code injections
|
||||||
|
*/
|
||||||
public boolean hasInjection() {
|
public boolean hasInjection() {
|
||||||
return hasUponEntry || hasUponReturn;
|
return hasUponEntry || hasUponReturn;
|
||||||
}
|
}
|
||||||
|
@ -334,6 +383,14 @@ public class PrototypeModel {
|
||||||
return finalres;
|
return finalres;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If this is an alias of another model, return that model. Otherwise null is returned.
|
||||||
|
* @return the parent model or null
|
||||||
|
*/
|
||||||
|
public PrototypeModel getAliasParent() {
|
||||||
|
return compatModel;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If a PrototypeModel fails to parse (from XML) a substitute model may be provided, in which
|
* If a PrototypeModel fails to parse (from XML) a substitute model may be provided, in which
|
||||||
* case this method returns true. In all other cases this method returns false;
|
* case this method returns true. In all other cases this method returns false;
|
||||||
|
@ -359,7 +416,19 @@ public class PrototypeModel {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Marshal this object as XML to an output buffer
|
||||||
|
* @param buffer is the output buffer
|
||||||
|
* @param injectLibrary is a library containing any inject payloads associated with the model
|
||||||
|
*/
|
||||||
public void saveXml(StringBuilder buffer, PcodeInjectLibrary injectLibrary) {
|
public void saveXml(StringBuilder buffer, PcodeInjectLibrary injectLibrary) {
|
||||||
|
if (compatModel != null) {
|
||||||
|
buffer.append("<modelalias");
|
||||||
|
SpecXmlUtils.encodeStringAttribute(buffer, "name", name);
|
||||||
|
SpecXmlUtils.encodeStringAttribute(buffer, "parent", compatModel.name);
|
||||||
|
buffer.append("/>\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
buffer.append("<prototype");
|
buffer.append("<prototype");
|
||||||
SpecXmlUtils.encodeStringAttribute(buffer, "name", name);
|
SpecXmlUtils.encodeStringAttribute(buffer, "name", name);
|
||||||
if (extrapop != PrototypeModel.UNKNOWN_EXTRAPOP) {
|
if (extrapop != PrototypeModel.UNKNOWN_EXTRAPOP) {
|
||||||
|
@ -516,11 +585,20 @@ public class PrototypeModel {
|
||||||
return name + "@@inject_uponreturn";
|
return name + "@@inject_uponreturn";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restore the model from an XML stream.
|
||||||
|
* @param parser is the XML parser (initialized to the start of the stream)
|
||||||
|
* @param cspec is the parent compiler specification owning the model
|
||||||
|
* @throws XmlParseException is there are problems parsing the XML
|
||||||
|
*/
|
||||||
public void restoreXml(XmlPullParser parser, CompilerSpec cspec) throws XmlParseException {
|
public void restoreXml(XmlPullParser parser, CompilerSpec cspec) throws XmlParseException {
|
||||||
inputParams = null;
|
inputParams = null;
|
||||||
outputParams = null;
|
outputParams = null;
|
||||||
XmlElement protoElement = parser.start();
|
XmlElement protoElement = parser.start();
|
||||||
name = protoElement.getAttribute("name");
|
name = protoElement.getAttribute("name");
|
||||||
|
if (!SpecExtension.isValidFormalName(name)) {
|
||||||
|
throw new XmlParseException("Prototype name uses illegal characters");
|
||||||
|
}
|
||||||
extrapop = PrototypeModel.UNKNOWN_EXTRAPOP;
|
extrapop = PrototypeModel.UNKNOWN_EXTRAPOP;
|
||||||
String extpopStr = protoElement.getAttribute("extrapop");
|
String extpopStr = protoElement.getAttribute("extrapop");
|
||||||
if (!extpopStr.equals("unknown")) {
|
if (!extpopStr.equals("unknown")) {
|
||||||
|
@ -597,22 +675,57 @@ public class PrototypeModel {
|
||||||
parser.end(protoElement);
|
parser.end(protoElement);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if the given address range is possible input parameter storage for this model.
|
||||||
|
* If it is, "true" is returned, and additional information about the parameter's
|
||||||
|
* position is passed back in the provided record.
|
||||||
|
* @param loc is the starting address of the range
|
||||||
|
* @param size is the size of the range in bytes
|
||||||
|
* @param res is the pass-back record
|
||||||
|
* @return true if the range is a possible parameter
|
||||||
|
*/
|
||||||
public boolean possibleInputParamWithSlot(Address loc, int size, ParamList.WithSlotRec res) {
|
public boolean possibleInputParamWithSlot(Address loc, int size, ParamList.WithSlotRec res) {
|
||||||
return inputParams.possibleParamWithSlot(loc, size, res);
|
return inputParams.possibleParamWithSlot(loc, size, res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if the given address range is possible return value storage for this model.
|
||||||
|
* If it is, "true" is returned, and additional information about the storage
|
||||||
|
* position is passed back in the provided record.
|
||||||
|
* @param loc is the starting address of the range
|
||||||
|
* @param size is the size of the range in bytes
|
||||||
|
* @param res is the pass-back record
|
||||||
|
* @return true if the range is possible return value storage
|
||||||
|
*/
|
||||||
public boolean possibleOutputParamWithSlot(Address loc, int size, ParamList.WithSlotRec res) {
|
public boolean possibleOutputParamWithSlot(Address loc, int size, ParamList.WithSlotRec res) {
|
||||||
return outputParams.possibleParamWithSlot(loc, size, res);
|
return outputParams.possibleParamWithSlot(loc, size, res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assuming the model allows open ended storage of parameters on the stack,
|
||||||
|
* return the byte alignment required for individual stack parameters.
|
||||||
|
* @return the stack alignment in bytes
|
||||||
|
*/
|
||||||
public int getStackParameterAlignment() {
|
public int getStackParameterAlignment() {
|
||||||
return inputParams.getStackParameterAlignment();
|
return inputParams.getStackParameterAlignment();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the byte offset where the first input parameter on the stack is allocated.
|
||||||
|
* The value is relative to the incoming stack pointer of the called function.
|
||||||
|
* For normal stacks, this is the offset of the first byte in the first parameter.
|
||||||
|
* For reverse stacks, this is the offset immediately after the last byte of the parameter.
|
||||||
|
* @return the byte offset of the first param
|
||||||
|
*/
|
||||||
public Long getStackParameterOffset() {
|
public Long getStackParameterOffset() {
|
||||||
return inputParams.getStackParameterOffset();
|
return inputParams.getStackParameterOffset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a list of all input storage locations consisting of a single register
|
||||||
|
* @param prog is the current Program
|
||||||
|
* @return a VariableStorage ojbect for each register
|
||||||
|
*/
|
||||||
public VariableStorage[] getPotentialInputRegisterStorage(Program prog) {
|
public VariableStorage[] getPotentialInputRegisterStorage(Program prog) {
|
||||||
return inputParams.getPotentialRegisterStorage(prog);
|
return inputParams.getPotentialRegisterStorage(prog);
|
||||||
}
|
}
|
||||||
|
@ -659,6 +772,11 @@ public class PrototypeModel {
|
||||||
if (!SystemUtilities.isArrayEqual(likelytrash, obj.likelytrash)) {
|
if (!SystemUtilities.isArrayEqual(likelytrash, obj.likelytrash)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
String compatName = (compatModel != null) ? compatModel.getName() : "";
|
||||||
|
String compatNameOp2 = (obj.compatModel != null) ? obj.compatModel.getName() : "";
|
||||||
|
if (!compatName.equals(compatNameOp2)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if (!SystemUtilities.isEqual(localRange, obj.localRange)) {
|
if (!SystemUtilities.isEqual(localRange, obj.localRange)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue