GP-3938 PrototypeModel rules

This commit is contained in:
caheckman 2023-05-30 11:07:08 -04:00
parent dae07c1900
commit 191371675a
55 changed files with 6000 additions and 1016 deletions

View file

@ -79,7 +79,7 @@ EXTERNAL_CONSOLEEXT_NAMES=$(subst .cc,,$(notdir $(EXTERNAL_CONSOLEEXT_SOURCE)))
CORE= xml marshal space float address pcoderaw translate opcodes globalcontext
# Additional core files for any projects that decompile
DECCORE=capability architecture options graph cover block cast typeop database cpool \
comment stringmanage fspec action loadimage grammar varnode op \
comment stringmanage modelrules fspec action loadimage grammar varnode op \
type variable varmap jumptable emulate emulateutil flow userop \
funcdata funcdata_block funcdata_op funcdata_varnode unionresolve pcodeinject \
heritage prefersplit rangeutil ruleaction subflow blockaction merge double \

View file

@ -165,7 +165,7 @@ bool ParamEntry::groupOverlap(const ParamEntry &op2) const
bool ParamEntry::subsumesDefinition(const ParamEntry &op2) const
{
if ((type!=TYPE_UNKNOWN)&&(op2.type != type)) return false;
if ((type!=TYPECLASS_GENERAL)&&(op2.type != type)) return false;
if (spaceid != op2.spaceid) return false;
if (op2.addressbase < addressbase) return false;
if ((op2.addressbase+op2.size-1) > (addressbase+size-1)) return false;
@ -467,7 +467,7 @@ void ParamEntry::decode(Decoder &decoder,bool normalstack,bool grouped,list<Para
{
flags = 0;
type = TYPE_UNKNOWN;
type = TYPECLASS_GENERAL;
size = minsize = -1; // Must be filled in
alignment = 0; // default
numslots = 1;
@ -488,8 +488,8 @@ void ParamEntry::decode(Decoder &decoder,bool normalstack,bool grouped,list<Para
else if (attribId == ATTRIB_MAXSIZE) {
size = decoder.readSignedInteger();
}
else if (attribId == ATTRIB_METATYPE)
type = string2metatype(decoder.readString());
else if (attribId == ATTRIB_STORAGE || attribId == ATTRIB_METATYPE)
type = string2typeclass(decoder.readString());
else if (attribId == ATTRIB_EXTENSION) {
flags &= ~((uint4)(smallsize_zext | smallsize_sext | smallsize_inttype));
string ext = decoder.readString();
@ -550,7 +550,7 @@ void ParamEntry::orderWithinGroup(const ParamEntry &entry1,const ParamEntry &ent
if (entry2.minsize > entry1.size || entry1.minsize > entry2.size)
return;
if (entry1.type != entry2.type) {
if (entry1.type == TYPE_UNKNOWN) {
if (entry1.type == TYPECLASS_GENERAL) {
throw LowlevelError("<pentry> tags with a specific type must come before the general type");
}
return;
@ -565,9 +565,11 @@ ParamListStandard::ParamListStandard(const ParamListStandard &op2)
entry = op2.entry;
spacebase = op2.spacebase;
maxdelay = op2.maxdelay;
pointermax = op2.pointermax;
thisbeforeret = op2.thisbeforeret;
resourceStart = op2.resourceStart;
for(list<ModelRule>::const_iterator iter=op2.modelRules.begin();iter!=op2.modelRules.end();++iter) {
modelRules.emplace_back(*iter,&op2);
}
populateResolver();
}
@ -645,75 +647,100 @@ int4 ParamListStandard::characterizeAsParam(const Address &loc,int4 size) const
return ParamEntry::no_containment;
}
/// Given the next data-type and the status of previously allocated slots,
/// \brief Assign storage for given parameter class, using the fallback assignment algorithm
///
/// Given a resource list, a data-type, and the status of previously allocated slots,
/// select the storage location for the parameter. The status array is
/// indexed by \e group: a positive value indicates how many \e slots have been allocated
/// from that group, and a -1 indicates the group/resource is fully consumed.
/// \param tp is the data-type of the next parameter
/// If an Address can be assigned to the parameter, it and other details are passed back in the
/// ParameterPieces object and the \e success code is returned. Otherwise, the \e fail code is returned.
/// \param resource is the resource list to allocate from
/// \param tp is the data-type of the parameter
/// \param matchExact is \b false if TYPECLASS_GENERAL is considered a match for any storage class
/// \param status is an array marking how many \e slots have already been consumed in a group
/// \return the newly assigned address for the parameter
Address ParamListStandard::assignAddress(const Datatype *tp,vector<int4> &status) const
/// \param param will hold the address of the newly assigned parameter
/// \return either \e success or \e fail
uint4 ParamListStandard::assignAddressFallback(type_class resource,Datatype *tp,bool matchExact,
vector<int4> &status,ParameterPieces &param) const
{
list<ParamEntry>::const_iterator iter;
for(iter=entry.begin();iter!=entry.end();++iter) {
const ParamEntry &curEntry( *iter );
int4 grp = curEntry.getGroup();
if (status[grp]<0) continue;
if ((curEntry.getType() != TYPE_UNKNOWN) && tp->getMetatype() != curEntry.getType())
continue; // Wrong type
if (resource != curEntry.getType()) {
if (matchExact || curEntry.getType() != TYPECLASS_GENERAL)
continue; // Wrong type
}
Address res = curEntry.getAddrBySlot(status[grp],tp->getAlignSize(),tp->getAlignment());
if (res.isInvalid()) continue; // If -tp- doesn't fit an invalid address is returned
param.addr = curEntry.getAddrBySlot(status[grp],tp->getAlignSize(),tp->getAlignment());
if (param.addr.isInvalid()) continue; // If -tp- doesn't fit an invalid address is returned
if (curEntry.isExclusion()) {
const vector<int4> &groupSet(curEntry.getAllGroups());
for(int4 j=0;j<groupSet.size();++j) // For an exclusion entry
status[groupSet[j]] = -1; // some number of groups are taken up
}
return res;
param.type = tp;
param.flags = 0;
return AssignAction::success;
}
return Address(); // Return invalid address to indicated we could not assign anything
return AssignAction::fail; // Unable to make an assignment
}
void ParamListStandard::assignMap(const vector<Datatype *> &proto,TypeFactory &typefactory,vector<ParameterPieces> &res) const
/// \brief Fill in the Address and other details for the given parameter
///
/// Attempt to apply a ModelRule first. If these do not succeed, use the fallback assignment algorithm.
/// \param dt is the data-type assigned to the parameter
/// \param proto is the description of the function prototype
/// \param pos is the position of the parameter to assign (pos=-1 for output, pos >=0 for input)
/// \param tlist is the data-type factory for (possibly) transforming the parameter's data-type
/// \param status is the consumed resource status array
/// \param res is parameter description to be filled in
/// \return the response code
uint4 ParamListStandard::assignAddress(Datatype *dt,const PrototypePieces &proto,int4 pos,TypeFactory &tlist,
vector<int4> &status,ParameterPieces &res) const
{
for(list<ModelRule>::const_iterator iter=modelRules.begin();iter!=modelRules.end();++iter) {
uint4 responseCode = (*iter).assignAddress(dt, proto, pos, tlist, status, res);
if (responseCode != AssignAction::fail)
return responseCode;
}
type_class store = metatype2typeclass(dt->getMetatype());
return assignAddressFallback(store,dt,false,status,res);
}
void ParamListStandard::assignMap(const PrototypePieces &proto,TypeFactory &typefactory,vector<ParameterPieces> &res) const
{
vector<int4> status(numgroup,0);
if (res.size() == 2) { // Check for hidden parameters defined by the output list
res.back().addr = assignAddress(res.back().type,status); // Reserve first param for hidden ret value
res.back().flags |= ParameterPieces::hiddenretparm;
if (res.back().addr.isInvalid())
Datatype *dt = res.back().type;
type_class store;
if ((res.back().flags & ParameterPieces::hiddenretparm) != 0)
store = TYPECLASS_HIDDENRET;
else
store = metatype2typeclass(dt->getMetatype());
// Reserve first param for hidden return pointer
if (assignAddressFallback(store,dt,false,status,res.back()) == AssignAction::fail)
throw ParamUnassignedError("Cannot assign parameter address for " + res.back().type->getName());
res.back().flags |= ParameterPieces::hiddenretparm;
}
for(int4 i=1;i<proto.size();++i) {
for(int4 i=0;i<proto.intypes.size();++i) {
res.emplace_back();
if ((pointermax != 0) && (proto[i]->getSize() > pointermax)) { // Datatype is too big
// Assume datatype is stored elsewhere and only the pointer is passed
AddrSpace *spc = spacebase;
if (spc == (AddrSpace*)0) spc = typefactory.getArch()->getDefaultDataSpace();
int4 pointersize = spc->getAddrSize();
int4 wordsize = spc->getWordSize();
Datatype *pointertp = typefactory.getTypePointer(pointersize,proto[i],wordsize);
res.back().addr = assignAddress(pointertp,status);
res.back().type = pointertp;
res.back().flags = ParameterPieces::indirectstorage;
}
else {
res.back().addr = assignAddress(proto[i],status);
res.back().type = proto[i];
res.back().flags = 0;
}
if (res.back().addr.isInvalid())
throw ParamUnassignedError("Cannot assign parameter address for " + proto[i]->getName());
Datatype *dt = proto.intypes[i];
if (assignAddress(dt,proto,i,typefactory,status,res.back()) == AssignAction::fail)
throw ParamUnassignedError("Cannot assign parameter address for " + dt->getName());
}
}
/// From among the ParamEntrys matching the given \e group, return the one that best matches
/// the given \e metatype attribute. If there are no ParamEntrys in the group, null is returned.
/// \param grp is the given \e group number
/// \param prefType is the preferred \e metatype attribute to match
const ParamEntry *ParamListStandard::selectUnreferenceEntry(int4 grp,type_metatype prefType) const
/// \param prefType is the preferred \e storage \e class attribute to match
const ParamEntry *ParamListStandard::selectUnreferenceEntry(int4 grp,type_class prefType) const
{
int4 bestScore = -1;
@ -725,7 +752,7 @@ const ParamEntry *ParamListStandard::selectUnreferenceEntry(int4 grp,type_metaty
int4 curScore;
if (curEntry->getType() == prefType)
curScore = 2;
else if (prefType == TYPE_UNKNOWN)
else if (prefType == TYPECLASS_GENERAL)
curScore = 1;
else
curScore = 0;
@ -760,7 +787,7 @@ void ParamListStandard::buildTrialMap(ParamActive *active) const
paramtrial.setEntry( entrySlot, 0 ); // Keep track of entry recovered for this trial
if (paramtrial.isActive()) {
if (entrySlot->getType() == TYPE_FLOAT)
if (entrySlot->getType() == TYPECLASS_FLOAT)
floatCount += 1;
else
intCount += 1;
@ -783,7 +810,7 @@ void ParamListStandard::buildTrialMap(ParamActive *active) const
const ParamEntry *curentry = hitlist[i];
if (curentry == (const ParamEntry *)0) {
curentry = selectUnreferenceEntry(i, (floatCount > intCount) ? TYPE_FLOAT : TYPE_UNKNOWN);
curentry = selectUnreferenceEntry(i, (floatCount > intCount) ? TYPECLASS_FLOAT : TYPECLASS_GENERAL);
if (curentry == (const ParamEntry *)0)
continue;
int4 sz = curentry->isExclusion() ? curentry->getSize() : curentry->getAlign();
@ -890,7 +917,7 @@ void ParamListStandard::markGroupNoUse(ParamActive *active,int4 activeTrial,int4
/// \param group is the group number
/// \param groupStart is the index of the first trial in the group
/// \param prefType is a preferred entry to type to use in scoring
void ParamListStandard::markBestInactive(ParamActive *active,int4 group,int4 groupStart,type_metatype prefType)
void ParamListStandard::markBestInactive(ParamActive *active,int4 group,int4 groupStart,type_class prefType)
{
int4 numTrials = active->getNumTrials();
@ -939,7 +966,7 @@ void ParamListStandard::forceExclusionGroup(ParamActive *active)
int4 grp = curtrial.getEntry()->getGroup();
if (grp != curGroup) {
if (inactiveCount > 1)
markBestInactive(active, curGroup, groupStart, TYPE_UNKNOWN);
markBestInactive(active, curGroup, groupStart, TYPECLASS_GENERAL);
curGroup = grp;
groupStart = i;
inactiveCount = 0;
@ -952,7 +979,7 @@ void ParamListStandard::forceExclusionGroup(ParamActive *active)
}
}
if (inactiveCount > 1)
markBestInactive(active, curGroup, groupStart, TYPE_UNKNOWN);
markBestInactive(active, curGroup, groupStart, TYPECLASS_GENERAL);
}
/// \brief Mark every trial above the first "definitely not used" as \e inactive.
@ -1123,17 +1150,17 @@ void ParamListStandard::populateResolver(void)
void ParamListStandard::parsePentry(Decoder &decoder,vector<EffectRecord> &effectlist,
int4 groupid,bool normalstack,bool autokill,bool splitFloat,bool grouped)
{
type_metatype lastMeta = TYPE_UNION;
type_class lastClass = TYPECLASS_CLASS4;
if (!entry.empty()) {
lastMeta = entry.back().isGrouped() ? TYPE_UNKNOWN : entry.back().getType();
lastClass = entry.back().isGrouped() ? TYPECLASS_GENERAL : entry.back().getType();
}
entry.emplace_back(groupid);
entry.back().decode(decoder,normalstack,grouped,entry);
if (splitFloat) {
type_metatype currentMeta = grouped ? TYPE_UNKNOWN : entry.back().getType();
if (lastMeta != currentMeta) {
if (lastMeta > currentMeta)
throw LowlevelError("parameter list entries must be ordered by metatype");
type_class currentClass = grouped ? TYPECLASS_GENERAL : entry.back().getType();
if (lastClass != currentClass) {
if (lastClass < currentClass)
throw LowlevelError("parameter list entries must be ordered by storage class");
resourceStart.push_back(groupid);
}
}
@ -1351,7 +1378,7 @@ void ParamListStandard::decode(Decoder &decoder,vector<EffectRecord> &effectlist
{
numgroup = 0;
spacebase = (AddrSpace *)0;
pointermax = 0;
int4 pointermax = 0;
thisbeforeret = false;
bool splitFloat = true; // True if we should split FLOAT entries into their own resource section
bool autokilledbycall = false;
@ -1381,11 +1408,30 @@ void ParamListStandard::decode(Decoder &decoder,vector<EffectRecord> &effectlist
else if (subId == ELEM_GROUP) {
parseGroup(decoder, effectlist, numgroup, normalstack, autokilledbycall, splitFloat);
}
else if (subId == ELEM_RULE) {
break;
}
}
for(;;) {
uint4 subId = decoder.peekElement();
if (subId == 0) break;
if (subId == ELEM_RULE) {
modelRules.emplace_back();
modelRules.back().decode(decoder, this);
}
else {
throw LowlevelError("<pentry> and <group> elements must come before any <modelrule>");
}
}
decoder.closeElement(elemId);
resourceStart.push_back(numgroup);
calcDelay();
populateResolver();
if (pointermax > 0) { // Add a ModelRule at the end that converts too big data-types to pointers
SizeRestrictedFilter typeFilter(pointermax+1,0);
ConvertToPointer action(this);
modelRules.emplace_back(typeFilter,action,this);
}
}
ParamList *ParamListStandard::clone(void) const
@ -1395,27 +1441,102 @@ ParamList *ParamListStandard::clone(void) const
return res;
}
void ParamListRegisterOut::assignMap(const vector<Datatype *> &proto,TypeFactory &typefactory,vector<ParameterPieces> &res) const
void ParamListRegisterOut::assignMap(const PrototypePieces &proto,TypeFactory &typefactory,vector<ParameterPieces> &res) const
{
vector<int4> status(numgroup,0);
res.emplace_back();
if (proto[0]->getMetatype() != TYPE_VOID) {
res.back().addr = assignAddress(proto[0],status);
if (proto.outtype->getMetatype() != TYPE_VOID) {
assignAddress(proto.outtype,proto,-1,typefactory,status,res.back());
if (res.back().addr.isInvalid())
throw ParamUnassignedError("Cannot assign parameter address for " + proto[0]->getName());
throw ParamUnassignedError("Cannot assign parameter address for " + proto.outtype->getName());
}
else {
res.back().type = proto.outtype;
res.back().flags = 0;
}
res.back().type = proto[0];
res.back().flags = 0;
}
void ParamListRegisterOut::fillinMap(ParamActive *active) const
ParamList *ParamListRegisterOut::clone(void) const
{
ParamList *res = new ParamListRegisterOut(*this);
return res;
}
void ParamListRegister::fillinMap(ParamActive *active) const
{
if (active->getNumTrials() == 0) return; // No trials to check
// Mark anything active as used
for(int4 i=0;i<active->getNumTrials();++i) {
ParamTrial &paramtrial(active->getTrial(i));
const ParamEntry *entrySlot = findEntry(paramtrial.getAddress(),paramtrial.getSize());
if (entrySlot == (const ParamEntry *)0) // There may be no matching entry (if the model was recovered late)
paramtrial.markNoUse();
else {
paramtrial.setEntry( entrySlot,0 ); // Keep track of entry recovered for this trial
if (paramtrial.isActive())
paramtrial.markUsed();
}
}
active->sortTrials();
}
ParamList *ParamListRegister::clone(void) const
{
ParamList *res = new ParamListRegister( *this );
return res;
}
void ParamListStandardOut::assignMap(const PrototypePieces &proto,TypeFactory &typefactory,vector<ParameterPieces> &res) const
{
vector<int4> status(numgroup,0);
res.emplace_back();
if (proto.outtype->getMetatype() == TYPE_VOID) {
res.back().type = proto.outtype;
res.back().flags = 0;
return; // Leave the address as invalid
}
uint4 responseCode = assignAddress(proto.outtype,proto,-1,typefactory,status,res.back());
if (responseCode != AssignAction::success) { // Could not assign an address (too big)
AddrSpace *spc = spacebase;
if (spc == (AddrSpace *)0)
spc = typefactory.getArch()->getDefaultDataSpace();
int4 pointersize = spc->getAddrSize();
int4 wordsize = spc->getWordSize();
Datatype *pointertp = typefactory.getTypePointer(pointersize, proto.outtype, wordsize);
if (responseCode == AssignAction::hiddenret_specialreg_void) {
res.back().type = typefactory.getTypeVoid();
res.back().flags = 0;
}
else {
if (assignAddressFallback(TYPECLASS_PTR,pointertp,false,status,res.back()) == AssignAction::fail)
throw ParamUnassignedError("Cannot assign return value as a pointer");
res.back().flags = ParameterPieces::indirectstorage;
}
res.emplace_back(); // Add extra storage location in the input params
res.back().type = pointertp; // that holds a pointer to where the return value should be stored
// leave its address invalid, to be filled in by the input list assignMap
// Encode whether or not hidden return should be drawn from TYPECLASS_HIDDENRET
bool isSpecial = (responseCode == AssignAction::hiddenret_specialreg ||
responseCode == AssignAction::hiddenret_specialreg_void);
res.back().flags = isSpecial ? ParameterPieces::hiddenretparm : 0;
}
}
void ParamListStandardOut::fillinMap(ParamActive *active) const
{
if (active->getNumTrials() == 0) return; // No trials to check
const ParamEntry *bestentry = (const ParamEntry *)0;
int4 bestcover = 0;
type_metatype bestmetatype = TYPE_PTR;
type_class bestclass = TYPECLASS_PTR;
// Find entry which is best covered by the active trials
list<ParamEntry>::const_iterator iter;
@ -1457,10 +1578,10 @@ void ParamListRegisterOut::fillinMap(ParamActive *active) const
k = 0; // Don't use this entry
// Prefer a more generic type restriction if we have it
// prefer the larger coverage
if ((k==active->getNumTrials())&&((curentry->getType() > bestmetatype)||(offmatch > bestcover))) {
if ((k==active->getNumTrials())&&((curentry->getType() < bestclass)||(offmatch > bestcover))) {
bestentry = curentry;
bestcover = offmatch;
bestmetatype = curentry->getType();
bestclass = curentry->getType();
}
}
if (bestentry==(const ParamEntry *)0) {
@ -1490,7 +1611,7 @@ void ParamListRegisterOut::fillinMap(ParamActive *active) const
}
}
bool ParamListRegisterOut::possibleParam(const Address &loc,int4 size) const
bool ParamListStandardOut::possibleParam(const Address &loc,int4 size) const
{
list<ParamEntry>::const_iterator iter;
@ -1501,92 +1622,6 @@ bool ParamListRegisterOut::possibleParam(const Address &loc,int4 size) const
return false;
}
ParamList *ParamListRegisterOut::clone(void) const
{
ParamList *res = new ParamListRegisterOut(*this);
return res;
}
void ParamListRegister::fillinMap(ParamActive *active) const
{
if (active->getNumTrials() == 0) return; // No trials to check
// Mark anything active as used
for(int4 i=0;i<active->getNumTrials();++i) {
ParamTrial &paramtrial(active->getTrial(i));
const ParamEntry *entrySlot = findEntry(paramtrial.getAddress(),paramtrial.getSize());
if (entrySlot == (const ParamEntry *)0) // There may be no matching entry (if the model was recovered late)
paramtrial.markNoUse();
else {
paramtrial.setEntry( entrySlot,0 ); // Keep track of entry recovered for this trial
if (paramtrial.isActive())
paramtrial.markUsed();
}
}
active->sortTrials();
}
ParamList *ParamListRegister::clone(void) const
{
ParamList *res = new ParamListRegister( *this );
return res;
}
void ParamListStandardOut::assignMap(const vector<Datatype *> &proto,TypeFactory &typefactory,vector<ParameterPieces> &res) const
{
vector<int4> status(numgroup,0);
res.emplace_back();
res.back().type = proto[0];
res.back().flags = 0;
if (proto[0]->getMetatype() == TYPE_VOID) {
return; // Leave the address as invalid
}
res.back().addr = assignAddress(proto[0],status);
if (res.back().addr.isInvalid()) { // Could not assign an address (too big)
AddrSpace *spc = spacebase;
if (spc == (AddrSpace *)0)
spc = typefactory.getArch()->getDefaultDataSpace();
int4 pointersize = spc->getAddrSize();
int4 wordsize = spc->getWordSize();
Datatype *pointertp = typefactory.getTypePointer(pointersize, proto[0], wordsize);
res.back().addr = assignAddress(pointertp,status);
if (res.back().addr.isInvalid())
throw ParamUnassignedError("Cannot assign return value as a pointer");
res.back().type = pointertp;
res.back().flags = ParameterPieces::indirectstorage;
res.emplace_back(); // Add extra storage location in the input params
res.back().type = pointertp; // that holds a pointer to where the return value should be stored
// leave its address invalid, to be filled in by the input list assignMap
res.back().flags = ParameterPieces::hiddenretparm; // Mark it as special
}
}
void ParamListStandardOut::decode(Decoder &decoder,vector<EffectRecord> &effectlist,bool normalstack)
{
ParamListRegisterOut::decode(decoder,effectlist,normalstack);
// Check for double precision entries
list<ParamEntry>::iterator iter;
ParamEntry *previous1 = (ParamEntry *)0;
ParamEntry *previous2 = (ParamEntry *)0;
for(iter=entry.begin();iter!=entry.end();++iter) {
ParamEntry &curEntry(*iter);
if (previous1 != (ParamEntry *)0) {
ParamEntry::orderWithinGroup(*previous1, curEntry);
if (previous2 != (ParamEntry *)0)
ParamEntry::orderWithinGroup(*previous2, curEntry);
}
previous2 = previous1;
previous1 = &curEntry;
}
}
ParamList *ParamListStandardOut::clone(void) const
{
@ -1980,6 +2015,20 @@ void FspecSpace::decode(Decoder &decoder)
throw LowlevelError("Should never decode fspec space from stream");
}
/// Swap any data-type and flags, but leave the storage address intact.
/// This assumes the two parameters are the same size.
/// \param op is the other parameter to swap with \b this.
void ParameterPieces::swapMarkup(ParameterPieces &op)
{
uint4 tmpFlags = flags;
Datatype *tmpType = type;
flags = op.flags;
type = op.type;
op.flags = tmpFlags;
op.type = tmpType;
}
/// The type is set to \e unknown_effect
/// \param addr is the start of the memory range
/// \param size is the number of bytes in the memory range
@ -2186,27 +2235,25 @@ bool ProtoModel::isCompatible(const ProtoModel *op2) const
/// \brief Calculate input and output storage locations given a function prototype
///
/// The data-types of the function prototype are passed in as an ordered list, with the
/// first data-type corresponding to the \e return \e value and all remaining
/// data-types corresponding to the input parameters. Based on \b this model, a storage location
/// is selected for each (input and output) parameter and passed back to the caller.
/// The passed back storage locations are ordered similarly, with the output storage
/// as the first entry. The model has the option of inserting a \e hidden return value
/// pointer in the input storage locations.
/// The data-types of the function prototype are passed in. Based on \b this model, a
/// location is selected for each (input and output) parameter and passed back to the
/// caller. The passed back storage locations are ordered with the output storage
/// as the first entry, followed by the input storage locations. The model has the option
/// of inserting a \e hidden return value pointer in the input storage locations.
///
/// A \b void return type is indicated by the formal TYPE_VOID in the (either) list.
/// A \b void return type is indicated by the formal TYPE_VOID.
/// If the model can't map the specific output prototype, the caller has the option of whether
/// an exception (ParamUnassignedError) is thrown. If they choose not to throw,
/// the unmapped return value is assumed to be \e void.
/// \param typelist is the list of data-types from the function prototype
/// \param proto is the data-types associated with the function prototype
/// \param res will hold the storage locations for each parameter
/// \param ignoreOutputError is \b true if problems assigning the output parameter are ignored
void ProtoModel::assignParameterStorage(const vector<Datatype *> &typelist,vector<ParameterPieces> &res,bool ignoreOutputError)
void ProtoModel::assignParameterStorage(const PrototypePieces &proto,vector<ParameterPieces> &res,bool ignoreOutputError)
{
if (ignoreOutputError) {
try {
output->assignMap(typelist,*glb->types,res);
output->assignMap(proto,*glb->types,res);
}
catch(ParamUnassignedError &err) {
res.clear();
@ -2217,9 +2264,23 @@ void ProtoModel::assignParameterStorage(const vector<Datatype *> &typelist,vecto
}
}
else {
output->assignMap(typelist,*glb->types,res);
output->assignMap(proto,*glb->types,res);
}
input->assignMap(proto,*glb->types,res);
if (hasThis && res.size() > 1) {
int4 thisIndex = 1;
if ((res[1].flags & ParameterPieces::hiddenretparm) != 0 && res.size() > 2) {
if (input->isThisBeforeRetPointer()) {
// pointer has been bumped by auto-return-storage
res[1].swapMarkup(res[2]); // must swap markup for slots 1 and 2
}
else {
thisIndex = 2;
}
}
res[thisIndex].flags |= ParameterPieces::isthis;
}
input->assignMap(typelist,*glb->types,res);
}
/// \brief Look up an effect from the given EffectRecord list
@ -3198,11 +3259,12 @@ void ProtoStoreInternal::decode(Decoder &decoder,ProtoModel *model)
{
Architecture *glb = model->getArch();
vector<ParameterPieces> pieces;
vector<string> namelist;
PrototypePieces proto;
proto.model = model;
proto.firstVarArgSlot = -1;
bool addressesdetermined = true;
pieces.push_back( ParameterPieces() ); // Push on placeholder for output pieces
namelist.push_back("ret");
pieces.back().type = outparam->getType();
pieces.back().flags = 0;
if (outparam->isTypeLocked())
@ -3245,7 +3307,7 @@ void ProtoStoreInternal::decode(Decoder &decoder,ProtoModel *model)
}
}
if ((flags & ParameterPieces::hiddenretparm) == 0)
namelist.push_back(name);
proto.innames.push_back(name);
pieces.emplace_back();
ParameterPieces &curparam( pieces.back() );
curparam.addr = Address::decode(decoder);
@ -3260,11 +3322,11 @@ void ProtoStoreInternal::decode(Decoder &decoder,ProtoModel *model)
if (!addressesdetermined) {
// If addresses for parameters are not provided, use
// the model to derive them from type info
vector<Datatype *> typelist;
for(int4 i=0;i<pieces.size();++i) // Save off the decoded types
typelist.push_back( pieces[i].type );
proto.outtype = pieces[0].type;
for(int4 i=1;i<pieces.size();++i) // Save off the decoded types
proto.intypes.push_back( pieces[i].type );
vector<ParameterPieces> addrPieces;
model->assignParameterStorage(typelist,addrPieces,true);
model->assignParameterStorage(proto,addrPieces,true);
addrPieces.swap(pieces);
uint4 k = 0;
for(uint4 i=0;i<pieces.size();++i) {
@ -3279,14 +3341,14 @@ void ProtoStoreInternal::decode(Decoder &decoder,ProtoModel *model)
curparam = setOutput(pieces[0]);
curparam->setTypeLock((pieces[0].flags & ParameterPieces::typelock)!=0);
}
uint4 j=1;
uint4 j=0;
for(uint4 i=1;i<pieces.size();++i) {
if ((pieces[i].flags&ParameterPieces::hiddenretparm)!=0) {
curparam = setInput(i-1,"rethidden",pieces[i]);
curparam->setTypeLock((pieces[0].flags & ParameterPieces::typelock)!=0); // Has output's typelock
continue; // increment i but not j
}
curparam = setInput(i-1,namelist[j],pieces[i]);
curparam = setInput(i-1,proto.innames[j],pieces[i]);
curparam->setTypeLock((pieces[i].flags & ParameterPieces::typelock)!=0);
curparam->setNameLock((pieces[i].flags & ParameterPieces::namelock)!=0);
j = j + 1;
@ -3436,37 +3498,36 @@ void FuncProto::paramShift(int4 paramshift)
if ((model == (ProtoModel *)0)||(store == (ProtoStore *)0))
throw LowlevelError("Cannot parameter shift without a model");
vector<string> nmlist;
vector<Datatype *> typelist;
bool isdotdotdot = false;
PrototypePieces proto;
proto.model = model;
proto.firstVarArgSlot = -1;
TypeFactory *typefactory = model->getArch()->types;
if (isOutputLocked())
typelist.push_back( getOutputType() );
proto.outtype = getOutputType();
else
typelist.push_back( typefactory->getTypeVoid() );
nmlist.push_back("");
proto.outtype = typefactory->getTypeVoid();
Datatype *extra = typefactory->getBase(4,TYPE_UNKNOWN); // The extra parameters have this type
for(int4 i=0;i<paramshift;++i) {
nmlist.push_back("");
typelist.push_back(extra);
proto.innames.push_back("");
proto.intypes.push_back(extra);
}
if (isInputLocked()) { // Copy in the original parameter types
int4 num = numParams();
for(int4 i=0;i<num;++i) {
ProtoParameter *param = getParam(i);
nmlist.push_back(param->getName());
typelist.push_back( param->getType() );
proto.innames.push_back(param->getName());
proto.intypes.push_back( param->getType() );
}
}
else
isdotdotdot = true;
proto.firstVarArgSlot = paramshift;
// Reassign the storage locations for this new parameter list
vector<ParameterPieces> pieces;
model->assignParameterStorage(typelist,pieces,false);
model->assignParameterStorage(proto,pieces,false);
delete store;
@ -3474,17 +3535,17 @@ void FuncProto::paramShift(int4 paramshift)
store = new ProtoStoreInternal(typefactory->getTypeVoid());
store->setOutput(pieces[0]);
uint4 j=1;
uint4 j=0;
for(uint4 i=1;i<pieces.size();++i) {
if ((pieces[i].flags & ParameterPieces::hiddenretparm) != 0) {
store->setInput(i-1,"rethidden",pieces[i]);
continue; // increment i but not j
}
store->setInput(j,nmlist[j],pieces[i]);
store->setInput(j,proto.innames[j],pieces[i]);
j = j + 1;
}
setInputLock(true);
setDotdotdot(isdotdotdot);
setDotdotdot(proto.firstVarArgSlot >= 0);
}
/// \brief If \b this has a \e merged model, pick the most likely model (from the merged set)
@ -3571,15 +3632,7 @@ void FuncProto::setPieces(const PrototypePieces &pieces)
{
if (pieces.model != (ProtoModel *)0)
setModel(pieces.model);
vector<Datatype *> typelist;
vector<string> nmlist;
typelist.push_back(pieces.outtype);
nmlist.push_back("");
for(int4 i=0;i<pieces.intypes.size();++i) {
typelist.push_back(pieces.intypes[i]);
nmlist.push_back(pieces.innames[i]);
}
updateAllTypes(nmlist,typelist,pieces.dotdotdot);
updateAllTypes(pieces);
setInputLock(true);
setOutputLock(true);
setModelLock(true);
@ -3600,7 +3653,7 @@ void FuncProto::getPieces(PrototypePieces &pieces) const
pieces.intypes.push_back(param->getType());
pieces.innames.push_back(param->getName());
}
pieces.dotdotdot = isDotdotdot();
pieces.firstVarArgSlot = isDotdotdot() ? num : -1;
}
/// Input parameters are set based on an existing function Scope
@ -3924,32 +3977,30 @@ void FuncProto::updateOutputNoTypes(const vector<Varnode *> &triallist,TypeFacto
/// the first entry corresponds to the output parameter (return value) and the remaining
/// entries correspond to input parameters. Storage locations and hidden return parameters are
/// calculated, creating a complete function protototype. Existing locks are overridden.
/// \param namelist is the list of parameter names
/// \param typelist is the list of data-types
/// \param dtdtdt is \b true if the new prototype accepts variable argument lists
void FuncProto::updateAllTypes(const vector<string> &namelist,const vector<Datatype *> &typelist,
bool dtdtdt)
/// \param proto is the list of names, data-types, and other attributes
void FuncProto::updateAllTypes(const PrototypePieces &proto)
{
setModel(model); // This resets extrapop
store->clearAllInputs();
store->clearOutput();
flags &= ~((uint4)voidinputlock);
setDotdotdot(dtdtdt);
setDotdotdot(proto.firstVarArgSlot >= 0);
vector<ParameterPieces> pieces;
// Calculate what memory locations hold each type
try {
model->assignParameterStorage(typelist,pieces,false);
model->assignParameterStorage(proto,pieces,false);
store->setOutput(pieces[0]);
uint4 j=1;
uint4 j=0;
for(uint4 i=1;i<pieces.size();++i) {
if ((pieces[i].flags & ParameterPieces::hiddenretparm) != 0) {
store->setInput(i-1,"rethidden",pieces[i]);
continue; // increment i but not j
}
store->setInput(i-1,namelist[j],pieces[i]);
string nm = (j >= proto.innames.size()) ? "" : proto.innames[j];
store->setInput(i-1,nm,pieces[i]);
j = j + 1;
}
}
@ -4254,11 +4305,13 @@ Address FuncProto::getThisPointerStorage(Datatype *dt)
{
if (!model->hasThisPointer())
return Address();
vector<Datatype *> typelist;
typelist.push_back(getOutputType());
typelist.push_back(dt);
PrototypePieces proto;
proto.model = model;
proto.firstVarArgSlot = -1;
proto.outtype = getOutputType();
proto.intypes.push_back(dt);
vector<ParameterPieces> res;
model->assignParameterStorage(typelist, res, true);
model->assignParameterStorage(proto, res, true);
for(int4 i=1;i<res.size();++i) {
if ((res[i].flags & ParameterPieces::hiddenretparm) != 0) continue;
return res[i].addr;

View file

@ -19,11 +19,12 @@
#ifndef __FSPEC_HH__
#define __FSPEC_HH__
#include "op.hh"
#include "modelrules.hh"
#include "rangemap.hh"
namespace ghidra {
class ProtoModel;
class JoinRecord;
extern AttributeId ATTRIB_CUSTOM; ///< Marshaling attribute "custom"
@ -102,7 +103,7 @@ public:
};
private:
uint4 flags; ///< Boolean properties of the parameter
type_metatype type; ///< Data-type class that this entry must match
type_class type; ///< Data-type storage class that this entry must match
vector<int4> groupSet; ///< Group(s) \b this entry belongs to
AddrSpace *spaceid; ///< Address space containing the range
uintb addressbase; ///< Starting offset of the range
@ -126,7 +127,7 @@ public:
int4 getMinSize(void) const { return minsize; } ///< Get the minimum size of a logical value contained in \b this
int4 getAlign(void) const { return alignment; } ///< Get the alignment of \b this entry
JoinRecord *getJoinRecord(void) const { return joinrec; } ///< Get record describing joined pieces (or null if only 1 piece)
type_metatype getType(void) const { return type; } ///< Get the data-type class associated with \b this
type_class getType(void) const { return type; } ///< Get the data-type class associated with \b this
bool isExclusion(void) const { return (alignment==0); } ///< Return \b true if this holds a single parameter exclusively
bool isReverseStack(void) const { return ((flags & reverse_stack)!=0); } ///< Return \b true if parameters are allocated in reverse order
bool isGrouped(void) const { return ((flags & is_grouped)!=0); } ///< Return \b true if \b this is grouped with other entries
@ -362,6 +363,17 @@ struct ParameterPieces {
Address addr; ///< Storage address of the parameter
Datatype *type; ///< The datatype of the parameter
uint4 flags; ///< additional attributes of the parameter
void swapMarkup(ParameterPieces &op); ///< Swap data-type markup between \b this and another parameter
};
/// \brief Raw components of a function prototype (obtained from parsing source code)
struct PrototypePieces {
ProtoModel *model; ///< (Optional) model on which prototype is based
string name; ///< Identifier (function name) associated with prototype
Datatype *outtype; ///< Return data-type
vector<Datatype *> intypes; ///< Input data-types
vector<string> innames; ///< Identifiers for input types
int4 firstVarArgSlot; ///< First position of a variable argument, or -1 if not varargs
};
/// \brief Description of the indirect effect a sub-function has on a memory range
@ -421,7 +433,7 @@ public:
/// \param proto is the ordered list of data-types
/// \param typefactory is the TypeFactory (for constructing pointers)
/// \param res will contain the storage locations corresponding to the datatypes
virtual void assignMap(const vector<Datatype *> &proto,TypeFactory &typefactory,vector<ParameterPieces> &res) const=0;
virtual void assignMap(const PrototypePieces &proto,TypeFactory &typefactory,vector<ParameterPieces> &res) const=0;
/// \brief Given an unordered list of storage locations, calculate a function prototype
///
@ -520,6 +532,14 @@ public:
/// \return the stack address space, if \b this models parameters passed on the stack, NULL otherwise
virtual AddrSpace *getSpacebase(void) const=0;
/// \brief Return \b true if \b this pointer occurs before an indirect return pointer
///
/// The \e automatic parameters: \b this parameter and the \e hidden return value pointer both
/// tend to be allocated from the initial general purpose registers reserved for parameter passing.
/// This method returns \b true if the \b this parameter is allocated first.
/// \return \b false if the \e hidden return value pointer is allocated first
virtual bool isThisBeforeRetPointer(void) const=0;
/// \brief For a given address space, collect all the parameter locations within that space
///
/// Pass back the memory ranges for any parameter that is stored in the given address space.
@ -558,19 +578,18 @@ class ParamListStandard : public ParamList {
protected:
int4 numgroup; ///< Number of \e groups in this parameter convention
int4 maxdelay; ///< Maximum heritage delay across all parameters
int4 pointermax; ///< If non-zero, maximum size of a data-type before converting to a pointer
bool thisbeforeret; ///< Does a \b this parameter come before a hidden return parameter
vector<int4> resourceStart; ///< The starting group for each resource section
list<ParamEntry> entry; ///< The ordered list of parameter entries
vector<ParamEntryResolver *> resolverMap; ///< Map from space id to resolver
list<ModelRule> modelRules; ///< Rules to apply when assigning addresses
AddrSpace *spacebase; ///< Address space containing relative offset parameters
const ParamEntry *findEntry(const Address &loc,int4 size) const; ///< Given storage location find matching ParamEntry
Address assignAddress(const Datatype *tp,vector<int4> &status) const; ///< Assign storage for given parameter data-type
const ParamEntry *selectUnreferenceEntry(int4 grp,type_metatype prefType) const; ///< Select entry to fill an unreferenced param
const ParamEntry *selectUnreferenceEntry(int4 grp,type_class prefType) const; ///< Select entry to fill an unreferenced param
void buildTrialMap(ParamActive *active) const; ///< Build map from parameter trials to model ParamEntrys
void separateSections(ParamActive *active,vector<int4> &trialStart) const;
static void markGroupNoUse(ParamActive *active,int4 activeTrial,int4 trialStart);
static void markBestInactive(ParamActive *active,int4 group,int4 groupStart,type_metatype prefType);
static void markBestInactive(ParamActive *active,int4 group,int4 groupStart,type_class prefType);
static void forceExclusionGroup(ParamActive *active);
static void forceNoUse(ParamActive *active,int4 start,int4 stop);
static void forceInactiveChain(ParamActive *active,int4 maxchain,int4 start,int4 stop,int4 groupstart);
@ -586,8 +605,12 @@ public:
ParamListStandard(const ParamListStandard &op2); ///< Copy constructor
virtual ~ParamListStandard(void);
const list<ParamEntry> &getEntry(void) const { return entry; } ///< Get the list of parameter entries
uint4 assignAddressFallback(type_class resource,Datatype *tp,bool matchExact,vector<int4> &status,
ParameterPieces &param) const;
uint4 assignAddress(Datatype *dt,const PrototypePieces &proto,int4 pos,TypeFactory &tlst,
vector<int4> &status,ParameterPieces &res) const;
virtual uint4 getType(void) const { return p_standard; }
virtual void assignMap(const vector<Datatype *> &proto,TypeFactory &typefactory,vector<ParameterPieces> &res) const;
virtual void assignMap(const PrototypePieces &proto,TypeFactory &typefactory,vector<ParameterPieces> &res) const;
virtual void fillinMap(ParamActive *active) const;
virtual bool checkJoin(const Address &hiaddr,int4 hisize,const Address &loaddr,int4 losize) const;
virtual bool checkSplit(const Address &loc,int4 size,int4 splitpoint) const;
@ -598,12 +621,32 @@ public:
virtual bool unjustifiedContainer(const Address &loc,int4 size,VarnodeData &res) const;
virtual OpCode assumedExtension(const Address &addr,int4 size,VarnodeData &res) const;
virtual AddrSpace *getSpacebase(void) const { return spacebase; }
virtual bool isThisBeforeRetPointer(void) const { return thisbeforeret; }
virtual void getRangeList(AddrSpace *spc,RangeList &res) const;
virtual int4 getMaxDelay(void) const { return maxdelay; }
virtual void decode(Decoder &decoder,vector<EffectRecord> &effectlist,bool normalstack);
virtual ParamList *clone(void) const;
};
/// \brief A standard model for returning output parameters from a function
///
/// This has a more involved assignment strategy than its parent class.
/// Entries in the resource list are treated as a \e group, meaning that only one can
/// fit the desired storage size and type attributes of the return value. If no entry
/// fits, the return value is converted to a pointer data-type, storage allocation is
/// attempted again, and the return value is marked as a \e hidden return parameter
/// to inform the input model.
class ParamListStandardOut : public ParamListStandard {
public:
ParamListStandardOut(void) : ParamListStandard() {} ///< Constructor for use with decode()
ParamListStandardOut(const ParamListStandardOut &op2) : ParamListStandard(op2) {} ///< Copy constructor
virtual uint4 getType(void) const { return p_standard_out; }
virtual void assignMap(const PrototypePieces &proto,TypeFactory &typefactory,vector<ParameterPieces> &res) const;
virtual void fillinMap(ParamActive *active) const;
virtual bool possibleParam(const Address &loc,int4 size) const;
virtual ParamList *clone(void) const;
};
/// \brief A model for passing back return values from a function
///
/// This is a resource list of potential storage locations for a return value,
@ -611,14 +654,12 @@ public:
/// for selecting a storage location. When assigning based on data-type (assignMap), the first list
/// entry that fits is chosen. When assigning from a set of actively used locations (fillinMap),
/// this class chooses the location that is the closest fitting match to an entry in the resource list.
class ParamListRegisterOut : public ParamListStandard {
class ParamListRegisterOut : public ParamListStandardOut {
public:
ParamListRegisterOut(void) : ParamListStandard() {} ///< Constructor
ParamListRegisterOut(const ParamListRegisterOut &op2) : ParamListStandard(op2) {} ///< Copy constructor
ParamListRegisterOut(void) : ParamListStandardOut() {} ///< Constructor
ParamListRegisterOut(const ParamListRegisterOut &op2) : ParamListStandardOut(op2) {} ///< Copy constructor
virtual uint4 getType(void) const { return p_register_out; }
virtual void assignMap(const vector<Datatype *> &proto,TypeFactory &typefactory,vector<ParameterPieces> &res) const;
virtual void fillinMap(ParamActive *active) const;
virtual bool possibleParam(const Address &loc,int4 size) const;
virtual void assignMap(const PrototypePieces &proto,TypeFactory &typefactory,vector<ParameterPieces> &res) const;
virtual ParamList *clone(void) const;
};
@ -638,24 +679,6 @@ public:
virtual ParamList *clone(void) const;
};
/// \brief A standard model for returning output parameters from a function
///
/// This has a more involved assignment strategy than its parent class.
/// Entries in the resource list are treated as a \e group, meaning that only one can
/// fit the desired storage size and type attributes of the return value. If no entry
/// fits, the return value is converted to a pointer data-type, storage allocation is
/// attempted again, and the return value is marked as a \e hidden return parameter
/// to inform the input model.
class ParamListStandardOut : public ParamListRegisterOut {
public:
ParamListStandardOut(void) : ParamListRegisterOut() {} ///< Constructor for use with decode()
ParamListStandardOut(const ParamListStandardOut &op2) : ParamListRegisterOut(op2) {} ///< Copy constructor
virtual uint4 getType(void) const { return p_standard_out; }
virtual void assignMap(const vector<Datatype *> &proto,TypeFactory &typefactory,vector<ParameterPieces> &res) const;
virtual void decode(Decoder &decoder,vector<EffectRecord> &effectlist,bool normalstack);
virtual ParamList *clone(void) const;
};
/// \brief A union of other input parameter passing models
///
/// This model is viewed as a union of a constituent set of resource lists.
@ -671,7 +694,7 @@ public:
void foldIn(const ParamListStandard &op2); ///< Add another model to the union
void finalize(void) { populateResolver(); } ///< Fold-ins are finished, finalize \b this
virtual uint4 getType(void) const { return p_merged; }
virtual void assignMap(const vector<Datatype *> &proto,TypeFactory &typefactory,vector<ParameterPieces> &res) const {
virtual void assignMap(const PrototypePieces &proto,TypeFactory &typefactory,vector<ParameterPieces> &res) const {
throw LowlevelError("Cannot assign prototype before model has been resolved"); }
virtual void fillinMap(ParamActive *active) const {
throw LowlevelError("Cannot determine prototype before model has been resolved"); }
@ -752,7 +775,7 @@ public:
void deriveOutputMap(ParamActive *active) const {
output->fillinMap(active); }
void assignParameterStorage(const vector<Datatype *> &typelist,vector<ParameterPieces> &res,bool ignoreOutputError);
void assignParameterStorage(const PrototypePieces &proto,vector<ParameterPieces> &res,bool ignoreOutputError);
/// \brief Check if the given two input storage locations can represent a single logical parameter
///
@ -1276,16 +1299,6 @@ public:
virtual void decode(Decoder &decoder,ProtoModel *model);
};
/// \brief Raw components of a function prototype (obtained from parsing source code)
struct PrototypePieces {
ProtoModel *model; ///< (Optional) model on which prototype is based
string name; ///< Identifier (function name) associated with prototype
Datatype *outtype; ///< Return data-type
vector<Datatype *> intypes; ///< Input data-types
vector<string> innames; ///< Identifiers for input types
bool dotdotdot; ///< True if prototype takes variable arguments
};
/// \brief A \b function \b prototype
///
/// A description of the parameters and return value for a specific function.
@ -1484,7 +1497,7 @@ public:
void updateInputNoTypes(Funcdata &data,const vector<Varnode *> &triallist,ParamActive *activeinput);
void updateOutputTypes(const vector<Varnode *> &triallist);
void updateOutputNoTypes(const vector<Varnode *> &triallist,TypeFactory *factory);
void updateAllTypes(const vector<string> &namelist,const vector<Datatype *> &typelist,bool dtdtdt);
void updateAllTypes(const PrototypePieces &proto);
ProtoParameter *getParam(int4 i) const { return store->getInput(i); } ///< Get the i-th input parameter
void setParam(int4 i,const string &name,const ParameterPieces &piece) { store->setInput(i, name, piece); } ///< Set parameter storage directly
void removeParam(int4 i) { store->clearInput(i); } ///< Remove the i-th input parameter

File diff suppressed because it is too large Load diff

View file

@ -719,18 +719,22 @@ bool FunctionModifier::isValid(void) const
Datatype *FunctionModifier::modType(Datatype *base,const TypeDeclarator *decl,Architecture *glb) const
{
vector<Datatype *> intypes;
PrototypePieces proto;
if (base == (Datatype *)0)
proto.outtype = glb->types->getTypeVoid();
else
proto.outtype = base;
// Varargs is encoded as extra null pointer in paramlist
bool dotdotdot = false;
proto.firstVarArgSlot = -1;
if ((!paramlist.empty())&&(paramlist.back() == (TypeDeclarator *)0)) {
dotdotdot = true;
proto.firstVarArgSlot = paramlist.size() - 1;
}
getInTypes(intypes,glb);
getInTypes(proto.intypes,glb);
ProtoModel *protomodel = decl->getModel(glb);
return glb->types->getTypeCode(protomodel,base,intypes,dotdotdot);
proto.model = decl->getModel(glb);
return glb->types->getTypeCode(proto);
}
TypeDeclarator::~TypeDeclarator(void)
@ -781,7 +785,7 @@ bool TypeDeclarator::getPrototype(PrototypePieces &pieces,Architecture *glb) con
fmod->getInTypes(pieces.intypes,glb);
pieces.innames.clear();
fmod->getInNames(pieces.innames);
pieces.dotdotdot = fmod->isDotdotdot();
pieces.firstVarArgSlot = fmod->isDotdotdot() ? pieces.intypes.size() : -1;
// Construct the output type
pieces.outtype = basetype;

View file

@ -1146,8 +1146,9 @@ AttributeId ATTRIB_TYPELOCK = AttributeId("typelock",23);
AttributeId ATTRIB_VAL = AttributeId("val",24);
AttributeId ATTRIB_VALUE = AttributeId("value",25);
AttributeId ATTRIB_WORDSIZE = AttributeId("wordsize",26);
AttributeId ATTRIB_STORAGE = AttributeId("storage",149);
AttributeId ATTRIB_UNKNOWN = AttributeId("XMLunknown",149); // Number serves as next open index
AttributeId ATTRIB_UNKNOWN = AttributeId("XMLunknown",150); // Number serves as next open index
ElementId ELEM_DATA = ElementId("data",1);
ElementId ELEM_INPUT = ElementId("input",2);
@ -1160,6 +1161,6 @@ ElementId ELEM_VAL = ElementId("val",8);
ElementId ELEM_VALUE = ElementId("value",9);
ElementId ELEM_VOID = ElementId("void",10);
ElementId ELEM_UNKNOWN = ElementId("XMLunknown",273); // Number serves as next open index
ElementId ELEM_UNKNOWN = ElementId("XMLunknown",284); // Number serves as next open index
} // End namespace ghidra

View file

@ -675,6 +675,7 @@ extern AttributeId ATTRIB_TYPELOCK; ///< Marshaling attribute "typelock"
extern AttributeId ATTRIB_VAL; ///< Marshaling attribute "val"
extern AttributeId ATTRIB_VALUE; ///< Marshaling attribute "value"
extern AttributeId ATTRIB_WORDSIZE; ///< Marshaling attribute "wordsize"
extern AttributeId ATTRIB_STORAGE; ///< Marshaling attribute "storage"
extern ElementId ELEM_DATA; ///< Marshaling element \<data>
extern ElementId ELEM_INPUT; ///< Marshaling element \<input>

View file

@ -0,0 +1,918 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "modelrules.hh"
#include "funcdata.hh"
namespace ghidra {
ElementId ELEM_DATATYPE = ElementId("datatype",273);
ElementId ELEM_CONSUME = ElementId("consume",274);
ElementId ELEM_CONSUME_EXTRA = ElementId("consume_extra",275);
ElementId ELEM_CONVERT_TO_PTR = ElementId("convert_to_ptr",276);
ElementId ELEM_GOTO_STACK = ElementId("goto_stack",277);
ElementId ELEM_JOIN = ElementId("join",278);
ElementId ELEM_DATATYPE_AT = ElementId("datatype_at",279);
ElementId ELEM_POSITION = ElementId("position",280);
ElementId ELEM_VARARGS = ElementId("varargs",281);
ElementId ELEM_HIDDEN_RETURN = ElementId("hidden_return",282);
ElementId ELEM_JOIN_PER_PRIMITIVE = ElementId("join_per_primitive",283);
/// \brief Extract an ordered list of primitive data-types making up the given data-type
///
/// The primitive data-types are passed back in an array. If the given data-type is already
/// primitive, it is passed back as is. Otherwise if it is composite, its components are recursively
/// listed. If a filler data-type is provided, it is used to fill \e holes in structures. If
/// a maximum number of extracted primitives is exceeded, or if no filler is provided and a hole
/// is encountered, or if a non-primitive non-composite data-type is encountered, \b false is returned.
/// \param dt is the given data-type to extract primitives from
/// \param max is the maximum number of primitives to extract before giving up
/// \param filler is the data-type to use as filler (or null)
/// \param res will hold the list of primitives
/// \return \b true if all primitives were extracted
bool DatatypeFilter::extractPrimitives(Datatype *dt,int4 max,Datatype *filler,vector<Datatype *> &res)
{
switch(dt->getMetatype()) {
case TYPE_UNKNOWN:
case TYPE_INT:
case TYPE_UINT:
case TYPE_BOOL:
case TYPE_CODE:
case TYPE_FLOAT:
case TYPE_PTR:
case TYPE_PTRREL:
if (res.size() >= max)
return false;
res.push_back(dt);
return true;
case TYPE_ARRAY:
{
int4 numEls = ((TypeArray *)dt)->numElements();
Datatype *base = ((TypeArray *)dt)->getBase();
for(int4 i=0;i<numEls;++i) {
if (!extractPrimitives(base,max,filler,res))
return false;
}
return true;
}
case TYPE_STRUCT:
break;
default:
return false;
}
TypeStruct *structPtr = (TypeStruct *)dt;
int4 curOff = 0;
vector<TypeField>::const_iterator enditer = structPtr->endField();
for(vector<TypeField>::const_iterator iter=structPtr->beginField();iter!=enditer;++iter) {
int4 nextOff = (*iter).offset;
if (nextOff > curOff) {
if (filler == (Datatype *)0)
return false;
while(curOff < nextOff) {
if (res.size() >= max)
return false;
res.push_back(filler);
curOff += filler->getSize();
}
}
if (!extractPrimitives((*iter).type,max,filler,res))
return false;
curOff += (*iter).type->getSize();
}
return true;
}
/// \param decoder is the given stream decoder
/// \return the new data-type filter instance
DatatypeFilter *DatatypeFilter::decodeFilter(Decoder &decoder)
{
DatatypeFilter *filter;
uint4 elemId = decoder.openElement(ELEM_DATATYPE);
string nm = decoder.readString(ATTRIB_NAME);
if (nm == "any") {
filter = new SizeRestrictedFilter();
}
else if (nm == "homogeneous-float-aggregate") {
filter = new HomogeneousAggregate(TYPE_FLOAT,4,0,0);
}
else {
// If no other name matches, assume this is a metatype
type_metatype meta = string2metatype(nm);
filter = new MetaTypeFilter(meta);
}
filter->decode(decoder);
decoder.closeElement(elemId);
return filter;
}
SizeRestrictedFilter::SizeRestrictedFilter(int4 min,int4 max)
{
minSize = min;
maxSize = max;
if (maxSize == 0 && minSize >= 0) {
// If no ATTRIB_MAXSIZE is given, assume there is no upper bound on size
maxSize = 0x7fffffff;
}
}
/// If \b maxSize is not zero, the data-type is checked to see if its size in bytes
/// falls between \b minSize and \b maxSize inclusive.
/// \param dt is the data-type to test
/// \return \b true if the data-type meets the size restrictions
bool SizeRestrictedFilter::filterOnSize(Datatype *dt) const
{
if (maxSize == 0) return true; // maxSize of 0 means no size filtering is performed
return (dt->getSize() >= minSize && dt->getSize() <= maxSize);
}
void SizeRestrictedFilter::decode(Decoder &decoder)
{
for(;;) {
uint4 attribId = decoder.getNextAttributeId();
if (attribId == 0) break;
if (attribId == ATTRIB_MINSIZE)
minSize = decoder.readUnsignedInteger();
else if (attribId == ATTRIB_MAXSIZE)
maxSize = decoder.readUnsignedInteger();
}
if (maxSize == 0 && minSize >= 0) {
// If no ATTRIB_MAXSIZE is given, assume there is no upper bound on size
maxSize = 0x7fffffff;
}
}
MetaTypeFilter::MetaTypeFilter(type_metatype meta)
{
metaType = meta;
}
MetaTypeFilter::MetaTypeFilter(type_metatype meta,int4 min,int4 max)
: SizeRestrictedFilter(min,max)
{
metaType = meta;
}
bool MetaTypeFilter::filter(Datatype *dt) const
{
if (dt->getMetatype() != metaType) return false;
return filterOnSize(dt);
}
HomogeneousAggregate::HomogeneousAggregate(type_metatype meta)
{
metaType = meta;
maxPrimitives = 2;
}
HomogeneousAggregate::HomogeneousAggregate(type_metatype meta,int4 maxPrim,int4 min,int4 max)
: SizeRestrictedFilter(min,max)
{
metaType = meta;
maxPrimitives = maxPrim;
}
bool HomogeneousAggregate::filter(Datatype *dt) const
{
type_metatype meta = dt->getMetatype();
if (meta != TYPE_ARRAY && meta != TYPE_STRUCT)
return false;
vector<Datatype *> res;
if (!extractPrimitives(dt, 4, (Datatype *)0, res))
return false;
Datatype *base = res[0];
if (base->getMetatype() != metaType)
return false;
for(int4 i=1;i<res.size();++i) {
if (res[i] != base)
return false;
}
return true;
}
/// If the next element is a qualifier filter, decode it from the stream and return it.
/// Otherwise return null
/// \param decoder is the given stream decoder
/// \return the new qualifier instance or null
QualifierFilter *QualifierFilter::decodeFilter(Decoder &decoder)
{
QualifierFilter *filter;
uint4 elemId = decoder.peekElement();
if (elemId == ELEM_VARARGS)
filter = new VarargsFilter();
else if (elemId == ELEM_POSITION)
filter = new PositionMatchFilter(-1);
else if (elemId == ELEM_DATATYPE_AT)
filter = new DatatypeMatchFilter();
else
return (QualifierFilter *)0;
filter->decode(decoder);
return filter;
}
/// The AndFilter assumes ownership of all the filters in the array and the original vector is cleared
/// \param filters is the list of filters pulled into \b this filter
AndFilter::AndFilter(vector<QualifierFilter *> filters)
{
subQualifiers.swap(filters);
}
AndFilter::~AndFilter(void)
{
for(int4 i=0;i<subQualifiers.size();++i)
delete subQualifiers[i];
}
QualifierFilter *AndFilter::clone(void) const
{
vector<QualifierFilter *> newFilters;
for(int4 i=0;i<subQualifiers.size();++i)
newFilters.push_back(subQualifiers[i]->clone());
return new AndFilter(newFilters);
}
bool AndFilter::filter(const PrototypePieces &proto,int4 pos) const
{
for(int4 i=0;i<subQualifiers.size();++i) {
if (!subQualifiers[i]->filter(proto,pos))
return false;
}
return true;
}
bool VarargsFilter::filter(const PrototypePieces &proto,int4 pos) const
{
if (proto.firstVarArgSlot < 0) return false;
return (pos >= proto.firstVarArgSlot);
}
void VarargsFilter::decode(Decoder &decoder)
{
uint4 elemId = decoder.openElement(ELEM_VARARGS);
decoder.closeElement(elemId);
}
bool PositionMatchFilter::filter(const PrototypePieces &proto,int4 pos) const
{
return (pos == position);
}
void PositionMatchFilter::decode(Decoder &decoder)
{
uint4 elemId = decoder.openElement(ELEM_POSITION);
position = decoder.readSignedInteger(ATTRIB_INDEX);
decoder.closeElement(elemId);
}
DatatypeMatchFilter::~DatatypeMatchFilter(void)
{
if (typeFilter != (DatatypeFilter *)0)
delete typeFilter;
}
QualifierFilter *DatatypeMatchFilter::clone(void) const
{
DatatypeMatchFilter *res = new DatatypeMatchFilter();
res->position = position;
res->typeFilter = typeFilter->clone();
return res;
}
bool DatatypeMatchFilter::filter(const PrototypePieces &proto,int4 pos) const
{
// The position of the current parameter being assigned, pos, is NOT used
Datatype *dt;
if (position < 0)
dt = proto.outtype;
else {
if (position >= proto.intypes.size())
return false;
dt = proto.intypes[position];
}
return typeFilter->filter(dt);
}
void DatatypeMatchFilter::decode(Decoder &decoder)
{
uint4 elemId = decoder.openElement(ELEM_DATATYPE_AT);
position = decoder.readSignedInteger(ATTRIB_INDEX);
typeFilter = DatatypeFilter::decodeFilter(decoder);
decoder.closeElement(elemId);
}
/// \brief Read the next model rule action element from the stream
///
/// Allocate the action object corresponding to the element and configure it.
/// If the next element is not an action, throw an exception.
/// \param parser is the stream decoder
/// \param res is the resource set for the new action
/// \return the new action
AssignAction *AssignAction::decodeAction(Decoder &decoder,const ParamListStandard *res)
{
AssignAction *action;
uint4 elemId = decoder.peekElement();
if (elemId == ELEM_GOTO_STACK)
action = new GotoStack(res,0);
else if (elemId == ELEM_JOIN) {
action = new MultiSlotAssign(res);
}
else if (elemId == ELEM_CONSUME) {
action = new ConsumeAs(TYPECLASS_GENERAL,res);
}
else if (elemId == ELEM_CONVERT_TO_PTR) {
action = new ConvertToPointer(res);
}
else if (elemId == ELEM_HIDDEN_RETURN) {
action = new HiddenReturnAssign(res,false);
}
else if (elemId == ELEM_JOIN_PER_PRIMITIVE) {
bool consumeMostSig = false;
AddrSpace *spc = res->getSpacebase();
if (spc != (AddrSpace *)0 && spc->isBigEndian()) {
consumeMostSig = true;
}
action = new MultiMemberAssign(TYPECLASS_GENERAL,false,consumeMostSig,res);
}
else
throw DecoderError("Expecting model rule action");
action->decode(decoder);
return action;
}
/// \brief Read the next model rule sideeffect element from the stream
///
/// Allocate the sideeffect object corresponding to the element and configure it.
/// If the next element is not a sideeffect, throw an exception.
/// \param parser is the stream decoder
/// \param res is the resource set for the new sideeffect
/// \return the new sideeffect
AssignAction *AssignAction::decodeSideeffect(Decoder &decoder,const ParamListStandard *res)
{
AssignAction *action;
uint4 elemId = decoder.peekElement();
if (elemId == ELEM_CONSUME_EXTRA) {
action = new ConsumeExtra(res);
}
else
throw DecoderError("Expecting model rule sideeffect");
action->decode(decoder);
return action;
}
void GotoStack::initializeEntry(void)
{
const list<ParamEntry> &entries(resource->getEntry());
list<ParamEntry>::const_iterator iter;
for(iter=entries.begin();iter!=entries.end();++iter) {
const ParamEntry &entry( *iter );
if (!entry.isExclusion() && entry.getSpace()->getType() == IPTR_SPACEBASE) {
stackEntry = &entry;
break;
}
}
if (stackEntry == (const ParamEntry *)0)
throw LowlevelError("Cannot find matching <pentry> for action: gotostack");
}
/// \param res is the new resource set to associate with \b this action
/// \param val is a dummy value
GotoStack::GotoStack(const ParamListStandard *res,int4 val)
: AssignAction(res)
{
stackEntry = (const ParamEntry *)0;
}
GotoStack::GotoStack(const ParamListStandard *res)
: AssignAction(res)
{
stackEntry = (const ParamEntry *)0;
initializeEntry();
}
uint4 GotoStack::assignAddress(Datatype *dt,const PrototypePieces &proto,int4 pos,TypeFactory &tlst,
vector<int4> &status,ParameterPieces &res) const
{
int4 grp = stackEntry->getGroup();
res.type = dt;
res.addr = stackEntry->getAddrBySlot(status[grp],dt->getSize(),dt->getAlignment());
res.flags = 0;
return success;
}
void GotoStack::decode(Decoder &decoder)
{
uint4 elemId = decoder.openElement(ELEM_GOTO_STACK);
decoder.closeElement(elemId);
initializeEntry();
}
ConvertToPointer::ConvertToPointer(const ParamListStandard *res)
: AssignAction(res)
{
space = res->getSpacebase();
}
uint4 ConvertToPointer::assignAddress(Datatype *dt,const PrototypePieces &proto,int4 pos,TypeFactory &tlist,
vector<int4> &status,ParameterPieces &res) const
{
AddrSpace *spc = space;
if (spc == (AddrSpace*)0)
spc = tlist.getArch()->getDefaultDataSpace();
int4 pointersize = spc->getAddrSize();
int4 wordsize = spc->getWordSize();
// Convert the data-type to a pointer
Datatype *pointertp = tlist.getTypePointer(pointersize,dt,wordsize);
// (Recursively) assign storage
uint4 responseCode = resource->assignAddress(pointertp, proto, pos, tlist, status, res);
res.flags = ParameterPieces::indirectstorage;
return responseCode;
}
void ConvertToPointer::decode(Decoder &decoder)
{
uint4 elemId = decoder.openElement(ELEM_CONVERT_TO_PTR);
decoder.closeElement(elemId);
}
/// Find the first ParamEntry matching the \b resourceType, and the ParamEntry
/// corresponding to the \e stack if \b consumeFromStack is set.
void MultiSlotAssign::initializeEntries(void)
{
const list<ParamEntry> &entries(resource->getEntry());
firstIter = entries.end();
list<ParamEntry>::const_iterator iter;
for(iter=entries.begin();iter!=entries.end();++iter) {
const ParamEntry &entry( *iter );
if (firstIter == entries.end() && entry.isExclusion() && entry.getType() == resourceType &&
entry.getAllGroups().size() == 1)
firstIter = iter; // First matching resource size
if (!entry.isExclusion() && entry.getSpace()->getType() == IPTR_SPACEBASE) {
stackEntry = &entry;
break;
}
}
if (firstIter == entries.end())
throw LowlevelError("Could not find matching resources for action: join");
if (consumeFromStack && stackEntry == (const ParamEntry *)0)
throw LowlevelError("Cannot find matching <pentry> for action: join");
}
/// Set default configuration
/// \param res is the new resource set to associate with \b this action
MultiSlotAssign::MultiSlotAssign(const ParamListStandard *res)
: AssignAction(res)
{
resourceType = TYPECLASS_GENERAL; // Join general purpose registers
uint4 listType = res->getType();
// Consume from stack on input parameters by default
consumeFromStack = (listType != ParamList::p_register_out && listType != ParamList::p_standard_out);
consumeMostSig = false;
enforceAlignment = false;
justifyRight = false;
AddrSpace *spc = res->getSpacebase();
if (spc != (AddrSpace *)0 && spc->isBigEndian()) {
consumeMostSig = true;
justifyRight = true;
}
stackEntry = (const ParamEntry *)0;
}
MultiSlotAssign::MultiSlotAssign(type_class store,bool stack,bool mostSig,bool align,bool justRight,const ParamListStandard *res)
: AssignAction(res)
{
resourceType = store;
consumeFromStack = stack;
consumeMostSig = mostSig;
enforceAlignment = align;
justifyRight = justRight;
stackEntry = (const ParamEntry *)0;
initializeEntries();
}
uint4 MultiSlotAssign::assignAddress(Datatype *dt,const PrototypePieces &proto,int4 pos,TypeFactory &tlist,
vector<int4> &status,ParameterPieces &res) const
{
vector<int4> tmpStatus = status;
vector<VarnodeData> pieces;
int4 sizeLeft = dt->getSize();
list<ParamEntry>::const_iterator iter = firstIter;
list<ParamEntry>::const_iterator endIter = resource->getEntry().end();
if (enforceAlignment) {
int4 resourcesConsumed = 0;
while(iter != endIter) {
const ParamEntry &entry( *iter );
if (!entry.isExclusion())
break; // Reached end of resource list
if (entry.getType() == resourceType && entry.getAllGroups().size() == 1) { // Single register
if (tmpStatus[entry.getGroup()] == 0) { // Not consumed
int4 align = dt->getAlignment();
int4 regSize = entry.getSize();
if (align <= regSize || (resourcesConsumed % align) == 0)
break;
tmpStatus[entry.getGroup()] = -1; // Consume unaligned register
}
resourcesConsumed += entry.getSize();
}
++iter;
}
}
while(sizeLeft > 0 && iter != endIter) {
const ParamEntry &entry( *iter );
++iter;
if (!entry.isExclusion())
break; // Reached end of resource list
if (entry.getType() != resourceType || entry.getAllGroups().size() != 1)
continue; // Not a single register from desired resource list
if (tmpStatus[entry.getGroup()] != 0)
continue; // Already consumed
int4 trialSize = entry.getSize();
Address addr = entry.getAddrBySlot(tmpStatus[entry.getGroup()], trialSize,1);
tmpStatus[entry.getGroup()] = -1; // Consume the register
pieces.push_back(VarnodeData());
pieces.back().space = addr.getSpace();
pieces.back().offset = addr.getOffset();
pieces.back().size = trialSize;
sizeLeft -= trialSize;
}
if (sizeLeft > 0) { // Have to use stack to get enough bytes
if (!consumeFromStack)
return fail;
int4 grp = stackEntry->getGroup();
Address addr = stackEntry->getAddrBySlot(tmpStatus[grp],sizeLeft,1); // Consume all the space we need
pieces.push_back(VarnodeData());
pieces.back().space = addr.getSpace();
pieces.back().offset = addr.getOffset();
pieces.back().size = sizeLeft;
}
else if (sizeLeft < 0) { // Have odd data-type size
if (resourceType == TYPECLASS_FLOAT && pieces.size() == 1) {
AddrSpaceManager *manager = tlist.getArch();
VarnodeData &tmp( pieces.front() );
Address addr = manager->constructFloatExtensionAddress(tmp.getAddr(),tmp.size,dt->getSize());
tmp.space = addr.getSpace();
tmp.offset = addr.getOffset();
tmp.size = dt->getSize();
}
else if (justifyRight) {
pieces.front().offset += -sizeLeft; // Initial bytes of first entry are padding
pieces.front().size += sizeLeft;
}
else {
pieces.back().size += sizeLeft;
}
}
status = tmpStatus; // Commit resource usage for all the pieces
res.flags = 0;
res.type = dt;
if (pieces.size() == 1) {
res.addr = pieces[0].getAddr();
return success;
}
if (!consumeMostSig) {
vector<VarnodeData> reverse;
for(int4 i=pieces.size()-1;i>=0;--i)
reverse.push_back(pieces[i]);
pieces.swap(reverse);
}
JoinRecord *joinRecord = tlist.getArch()->findAddJoin(pieces, 0);
res.addr = joinRecord->getUnified().getAddr();
return success;
}
void MultiSlotAssign::decode(Decoder &decoder)
{
uint4 elemId = decoder.openElement(ELEM_JOIN);
for(;;) {
uint4 attribId = decoder.getNextAttributeId();
if (attribId == 0) break;
if (attribId == ATTRIB_REVERSEJUSTIFY) {
if (decoder.readBool())
justifyRight = !justifyRight;
}
else if (attribId == ATTRIB_STORAGE) {
resourceType = string2typeclass(decoder.readString());
}
else if (attribId == ATTRIB_ALIGN) {
enforceAlignment = decoder.readBool();
}
}
decoder.closeElement(elemId);
initializeEntries(); // Need new firstIter
}
MultiMemberAssign::MultiMemberAssign(type_class store,bool stack,bool mostSig,const ParamListStandard *res)
: AssignAction(res)
{
resourceType = store;
consumeFromStack = stack;
consumeMostSig = mostSig;
}
uint4 MultiMemberAssign::assignAddress(Datatype *dt,const PrototypePieces &proto,int4 pos,TypeFactory &tlist,
vector<int4> &status,ParameterPieces &res) const
{
vector<int4> tmpStatus = status;
vector<VarnodeData> pieces;
vector<Datatype *> primitives;
if (!DatatypeFilter::extractPrimitives(dt,16,(Datatype *)0,primitives))
return fail;
ParameterPieces param;
for(int4 i=0;i<primitives.size();++i) {
Datatype *curType = primitives[i];
if (resource->assignAddressFallback(resourceType, curType, !consumeFromStack, tmpStatus,param) == fail)
return fail;
pieces.push_back(VarnodeData());
pieces.back().space = param.addr.getSpace();
pieces.back().offset = param.addr.getOffset();
pieces.back().size = curType->getSize();
}
status = tmpStatus; // Commit resource usage for all the pieces
res.flags = 0;
res.type = dt;
if (pieces.size() == 1) {
res.addr = pieces[0].getAddr();
return success;
}
if (!consumeMostSig) {
vector<VarnodeData> reverse;
for(int4 i=pieces.size()-1;i>=0;--i)
reverse.push_back(pieces[i]);
pieces.swap(reverse);
}
JoinRecord *joinRecord = tlist.getArch()->findAddJoin(pieces, 0);
res.addr = joinRecord->getUnified().getAddr();
return success;
}
void MultiMemberAssign::decode(Decoder &decoder)
{
uint4 elemId = decoder.openElement(ELEM_JOIN_PER_PRIMITIVE);
for(;;) {
uint4 attribId = decoder.getNextAttributeId();
if (attribId == 0) break;
if (attribId == ATTRIB_STORAGE) {
resourceType = string2typeclass(decoder.readString());
}
}
decoder.closeElement(elemId);
}
ConsumeAs::ConsumeAs(type_class store,const ParamListStandard *res)
: AssignAction(res)
{
resourceType = store;
}
uint4 ConsumeAs::assignAddress(Datatype *dt,const PrototypePieces &proto,int4 pos,TypeFactory &tlist,
vector<int4> &status,ParameterPieces &res) const
{
return resource->assignAddressFallback(resourceType, dt, true, status, res);
}
void ConsumeAs::decode(Decoder &decoder)
{
uint4 elemId = decoder.openElement(ELEM_CONSUME);
resourceType = string2typeclass(decoder.readString(ATTRIB_STORAGE));
decoder.closeElement(elemId);
}
HiddenReturnAssign::HiddenReturnAssign(const ParamListStandard *res,bool voidLock)
: AssignAction(res)
{
retCode = voidLock ? hiddenret_specialreg_void : hiddenret_specialreg;
}
uint4 HiddenReturnAssign::assignAddress(Datatype *dt,const PrototypePieces &proto,int4 pos,TypeFactory &tlist,
vector<int4> &status,ParameterPieces &res) const
{
return retCode; // Signal to assignMap to use TYPECLASS_HIDDENRET
}
void HiddenReturnAssign::decode(Decoder &decoder)
{
uint4 elemId = decoder.openElement(ELEM_HIDDEN_RETURN);
uint4 attribId = decoder.getNextAttributeId();
if (attribId == ATTRIB_VOIDLOCK)
retCode = hiddenret_specialreg_void;
else
retCode = hiddenret_specialreg;
decoder.closeElement(elemId);
}
/// Find the first ParamEntry matching the \b resourceType.
void ConsumeExtra::initializeEntries(void)
{
const list<ParamEntry> &entries(resource->getEntry());
firstIter = entries.end();
list<ParamEntry>::const_iterator iter;
for(iter=entries.begin();iter!=entries.end();++iter) {
const ParamEntry &entry( *iter );
if (entry.isExclusion() && entry.getType() == resourceType && entry.getAllGroups().size() == 1) {
firstIter = iter; // First matching resource size
break;
}
}
if (firstIter == entries.end())
throw LowlevelError("Could not find matching resources for action: consumeextra");
}
ConsumeExtra::ConsumeExtra(const ParamListStandard *res)
: AssignAction(res)
{
resourceType = TYPECLASS_GENERAL;
matchSize = true;
}
ConsumeExtra::ConsumeExtra(type_class store,bool match,const ParamListStandard *res)
: AssignAction(res)
{
resourceType = store;
matchSize = match;
initializeEntries();
}
uint4 ConsumeExtra::assignAddress(Datatype *dt,const PrototypePieces &proto,int4 pos,TypeFactory &tlist,
vector<int4> &status,ParameterPieces &res) const
{
list<ParamEntry>::const_iterator iter = firstIter;
list<ParamEntry>::const_iterator endIter = resource->getEntry().end();
int4 sizeLeft = dt->getSize();
while(sizeLeft > 0 && iter != endIter) {
const ParamEntry &entry(*iter);
++iter;
if (!entry.isExclusion())
break; // Reached end of resource list
if (entry.getType() != resourceType || entry.getAllGroups().size() != 1)
continue; // Not a single register in desired list
if (status[entry.getGroup()] != 0)
continue; // Already consumed
status[entry.getGroup()] = -1; // Consume the slot/register
sizeLeft -= entry.getSize();
if (!matchSize)
break; // Only consume a single register
}
return success;
}
void ConsumeExtra::decode(Decoder &decoder)
{
uint4 elemId = decoder.openElement(ELEM_CONSUME_EXTRA);
resourceType = string2typeclass(decoder.readString(ATTRIB_STORAGE));
decoder.closeElement(elemId);
initializeEntries();
}
ModelRule::ModelRule(const ModelRule &op2,const ParamListStandard *res)
{
if (op2.filter != (DatatypeFilter *)0)
filter = op2.filter->clone();
else
filter = (DatatypeFilter *)0;
if (op2.qualifier != (QualifierFilter *)0)
qualifier = op2.qualifier->clone();
else
qualifier = (QualifierFilter *)0;
if (op2.assign != (AssignAction *)0)
assign = op2.assign->clone(res);
else
assign = (AssignAction *)0;
for(int4 i=0;i<op2.sideeffects.size();++i)
sideeffects.push_back(op2.sideeffects[i]->clone(res));
}
/// The provided components are cloned into the new object.
/// \param typeFilter is the data-type filter the rule applies before performing the action
/// \param action is the action that will be applied
/// \param res is the resource list to which \b this rule will be applied
ModelRule::ModelRule(const DatatypeFilter &typeFilter,const AssignAction &action,const ParamListStandard *res)
{
filter = typeFilter.clone();
qualifier = (QualifierFilter *)0;
assign = action.clone(res);
}
ModelRule::~ModelRule(void)
{
if (filter != (DatatypeFilter *)0)
delete filter;
if (qualifier != (QualifierFilter *)0)
delete qualifier;
if (assign != (AssignAction *)0)
delete assign;
for(int4 i=0;i<sideeffects.size();++i)
delete sideeffects[i];
}
/// \brief Assign an address and other details for a specific parameter or for return storage in context
///
/// The Address is only assigned if the data-type filter and the optional qualifier filter
/// pass, otherwise a \b fail response is returned.
/// If the filters pass, the Address is assigned based on the AssignAction specific to
/// \b this rule, and the action's response code is returned.
/// \param dt is the data-type of the parameter or return value
/// \param proto is the high-level description of the function prototype
/// \param pos is the position of the parameter (pos>=0) or return storage (pos=-1)
/// \param tlist is a data-type factory for (possibly) transforming the data-type
/// \param status is the resource consumption array
/// \param res will hold the resulting description of the parameter
/// \return the response code
uint4 ModelRule::assignAddress(Datatype *dt,const PrototypePieces &proto,int4 pos,TypeFactory &tlist,
vector<int4> &status,ParameterPieces &res) const
{
if (!filter->filter(dt)) {
return AssignAction::fail;
}
if (qualifier != (QualifierFilter *)0 && !qualifier->filter(proto,pos)) {
return AssignAction::fail;
}
uint4 response = assign->assignAddress(dt,proto,pos,tlist,status,res);
if (response != AssignAction::fail) {
for(int4 i=0;i<sideeffects.size();++i) {
sideeffects[i]->assignAddress(dt,proto,pos,tlist,status,res);
}
}
return response;
}
/// \param decoder is the stream decoder
/// \param res is the parameter resource list owning \b this rule
void ModelRule::decode(Decoder &decoder,const ParamListStandard *res)
{
vector<QualifierFilter *> qualifiers;
uint4 elemId = decoder.openElement(ELEM_RULE);
filter = DatatypeFilter::decodeFilter(decoder);
for(;;) {
QualifierFilter *qual = QualifierFilter::decodeFilter(decoder);
if (qual == (QualifierFilter *)0)
break;
qualifiers.push_back(qual);
}
if (qualifiers.size() == 0)
qualifier = (QualifierFilter *)0;
else if (qualifiers.size() == 1) {
qualifier = qualifiers[0];
qualifiers.clear();
}
else {
qualifier = new AndFilter(qualifiers);
}
assign = AssignAction::decodeAction(decoder, res);
while(decoder.peekElement() != 0) {
sideeffects.push_back(AssignAction::decodeSideeffect(decoder,res));
}
decoder.closeElement(elemId);
}
} // End namespace ghidra

View file

@ -0,0 +1,393 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/// \file modelrules.hh
/// \brief Definitions for rules governing mapping of data-type to address for prototype models
#ifndef __MODELRULES_HH__
#define __MODELRULES_HH__
#include "op.hh"
namespace ghidra {
class ParameterPieces;
class ParamListStandard;
class ParamEntry;
extern ElementId ELEM_DATATYPE; ///< Marshaling element \<datatype>
extern ElementId ELEM_CONSUME; ///< Marshaling element \<consume>
extern ElementId ELEM_CONSUME_EXTRA; ///< Marshaling element \<consume_extra>
extern ElementId ELEM_CONVERT_TO_PTR; ///< Marshaling element \<convert_to_ptr>
extern ElementId ELEM_GOTO_STACK; ///< Marshaling element \<goto_stack>
extern ElementId ELEM_JOIN; ///< Marshaling element \<join>
extern ElementId ELEM_DATATYPE_AT; ///< Marshaling element \<datatype_at>
extern ElementId ELEM_POSITION; ///< Marshaling element \<position>
extern ElementId ELEM_VARARGS; ///< Marshaling element \<varargs>
extern ElementId ELEM_HIDDEN_RETURN; ///< Marshaling element \<hidden_return>
extern ElementId ELEM_JOIN_PER_PRIMITIVE; ///< Marshaling element \<join_per_primitive>
/// \brief A filter selecting a specific class of data-type
///
/// An instance is configured via the decode() method, then a test of whether
/// a data-type belongs to its class can be performed by calling the filter() method.
class DatatypeFilter {
public:
virtual ~DatatypeFilter(void) {} ///< Destructor
/// \brief Make a copy of \b this filter
///
/// \return the newly allocated copy
virtual DatatypeFilter *clone(void) const=0;
/// \brief Test whether the given data-type belongs to \b this filter's data-type class
///
/// \param dt is the given data-type to test
/// \return \b true if the data-type is in the class, \b false otherwise
virtual bool filter(Datatype *dt) const=0;
/// \brief Configure details of the data-type class being filtered from the given stream
///
/// \param decoder is the given stream decoder
virtual void decode(Decoder &decoder)=0;
static bool extractPrimitives(Datatype *dt,int4 max,Datatype *filler,vector<Datatype *> &res);
static DatatypeFilter *decodeFilter(Decoder &decoder); ///< Instantiate a filter from the given stream
};
/// \brief A common base class for data-type filters that tests for a size range
///
/// Any filter that inherits from \b this, can use ATTRIB_MINSIZE and ATTRIB_MAXSIZE
/// to place bounds on the possible sizes of data-types. The bounds are enforced
/// by calling filterOnSize() within the inheriting classes filter() method.
class SizeRestrictedFilter : public DatatypeFilter {
protected:
int4 minSize; ///< Minimum size of the data-type in bytes
int4 maxSize; ///< Maximum size of the data-type in bytes
public:
SizeRestrictedFilter(void) { minSize=0; maxSize=0; } ///< Constructor for use with decode()
SizeRestrictedFilter(int4 min,int4 max); ///< Constructor
bool filterOnSize(Datatype *dt) const; ///< Enforce any size bounds on a given data-type
virtual DatatypeFilter *clone(void) const { return new SizeRestrictedFilter(minSize,maxSize); }
virtual bool filter(Datatype *dt) const { return filterOnSize(dt); }
virtual void decode(Decoder &decoder);
};
/// \brief Filter on a single meta data-type
///
/// Filters on TYPE_STRUCT or TYPE_FLOAT etc. Additional filtering on size of the data-type can be configured.
class MetaTypeFilter : public SizeRestrictedFilter {
protected:
type_metatype metaType; ///< The meta-type this filter lets through
public:
MetaTypeFilter(type_metatype meta); ///< Constructor for use with decode()
MetaTypeFilter(type_metatype meta,int4 min,int4 max); ///< Constructor
virtual DatatypeFilter *clone(void) const { return new MetaTypeFilter(metaType,minSize,maxSize); }
virtual bool filter(Datatype *dt) const;
};
/// \brief Filter on a homogeneous aggregate data-type
///
/// All primitive data-types must be the same.
class HomogeneousAggregate : public SizeRestrictedFilter {
type_metatype metaType; ///< The expected meta-type
int4 maxPrimitives; ///< Maximum number of primitives in the aggregate
public:
HomogeneousAggregate(type_metatype meta); ///< Constructor for use with decode()
HomogeneousAggregate(type_metatype meta,int4 maxPrim,int4 min,int4 max); ///< Constructor
virtual DatatypeFilter *clone(void) const { return new HomogeneousAggregate(metaType,maxPrimitives, minSize,maxSize); }
virtual bool filter(Datatype *dt) const;
};
/// \brief A filter on some aspect of a specific function prototype
///
/// An instance is configured via the decode() method, then a test of whether
/// a function prototype meets its criteria can be performed by calling its filter() method.
class QualifierFilter {
public:
virtual ~QualifierFilter(void) {} ///< Destructor
/// \brief Make a copy of \b this qualifier
///
/// \return the newly allocated copy
virtual QualifierFilter *clone(void) const=0;
/// \brief Test whether the given function prototype meets \b this filter's criteria
///
/// \param proto is the high-level description of the function prototype to test
/// \param pos is the position of a specific output (pos=-1) or input (pos >=0) in context
/// \return \b true if the prototype meets the criteria, \b false otherwise
virtual bool filter(const PrototypePieces &proto,int4 pos) const=0;
/// \brief Configure details of the criteria being filtered from the given stream
///
/// \param decoder is the given stream decoder
virtual void decode(Decoder &decoder) {}
static QualifierFilter *decodeFilter(Decoder &decoder); ///< Try to instantiate a qualifier filter
};
/// \brief Logically AND multiple QualifierFilters together into a single filter
///
/// An instances contains some number of other arbitrary filters. In order for \b this filter to
/// pass, all these contained filters must pass.
class AndFilter : public QualifierFilter {
vector<QualifierFilter *> subQualifiers; ///< Filters being logically ANDed together
public:
AndFilter(vector<QualifierFilter *> filters); ///< Construct from array of filters
virtual ~AndFilter(void);
virtual QualifierFilter *clone(void) const;
virtual bool filter(const PrototypePieces &proto,int4 pos) const;
virtual void decode(Decoder &decoder) {}
};
/// \brief A filter that selects function parameters that are considered optional
///
/// If the underlying function prototype is considered to take variable arguments, the first
/// \e n parameters (as determined by PrototypePieces.firstVarArgSlot) are considered non-optional.
/// If additional data-types are provided beyond the initial \e n, these are considered optional.
/// This filter returns \b true for these optional parameters
class VarargsFilter : public QualifierFilter {
public:
virtual QualifierFilter *clone(void) const { return new VarargsFilter(); }
virtual bool filter(const PrototypePieces &proto,int4 pos) const;
virtual void decode(Decoder &decoder);
};
/// \brief Filter that selects for a particular parameter position
///
/// This matches if the position of the current parameter being assigned, within the data-type
/// list, matches the \b position attribute of \b this filter.
class PositionMatchFilter : public QualifierFilter {
int4 position; ///< Parameter position being filtered for
public:
PositionMatchFilter(int4 pos) { position = pos; } ///< Constructor
virtual QualifierFilter *clone(void) const { return new PositionMatchFilter(position); }
virtual bool filter(const PrototypePieces &proto,int4 pos) const;
virtual void decode(Decoder &decoder);
};
/// \brief Check if the function signature has a specific data-type in a specific position
/// This filter does not match against the data-type in the current position
/// being assigned, but against a parameter at a fixed position.
class DatatypeMatchFilter : public QualifierFilter {
int4 position; ///< The position of the data-type to check
DatatypeFilter *typeFilter; ///< The data-type that must be at \b position
public:
DatatypeMatchFilter(void) { position = -1; typeFilter = (DatatypeFilter *)0; } ///< Constructor for use with decode
virtual ~DatatypeMatchFilter(void);
virtual QualifierFilter *clone(void) const;
virtual bool filter(const PrototypePieces &proto,int4 pos) const;
virtual void decode(Decoder &decoder);
};
/// \brief An action that assigns an Address to a function prototype parameter
///
/// A request for the address of either \e return storage or an input parameter is made
/// through the assignAddress() method, which is given full information about the function prototype.
/// Details about how the action performs is configured through the decode() method.
class AssignAction {
public:
enum {
success, ///< Data-type is fully assigned
fail, ///< Action could not be applied (not enough resources)
hiddenret_ptrparam, ///< Hidden return pointer as first input parameter
hiddenret_specialreg, ///< Hidden return pointer in dedicated input register
hiddenret_specialreg_void ///< Hidden return pointer, but no normal return
};
protected:
const ParamListStandard *resource; ///< Resources to which this action applies
public:
AssignAction(const ParamListStandard *res) {resource = res; } ///< Constructor
virtual ~AssignAction(void) {}
/// \brief Make a copy of \b this action
///
/// \param newResource is the new resource object that will own the clone
/// \return the newly allocated copy
virtual AssignAction *clone(const ParamListStandard *newResource) const=0;
/// \brief Assign an address and other meta-data for a specific parameter or for return storage in context
///
/// The Address is assigned based on the data-type of the parameter, available register
/// resources, and other details of the function prototype. Consumed resources are marked.
/// This method returns a response code:
/// - success - indicating the Address was successfully assigned
/// - fail - if the Address could not be assigned
/// - hiddenret_ptrparam - if an additional \e hidden \e return \e parameter is required
///
/// \param dt is the data-type of the parameter or return value
/// \param proto is the high-level description of the function prototype
/// \param pos is the position of the parameter (pos>=0) or return storage (pos=-1)
/// \param tlist is a data-type factory for (possibly) transforming the data-type
/// \param status is the resource consumption array
/// \param res will hold the resulting description of the parameter
/// \return the response code
virtual uint4 assignAddress(Datatype *dt,const PrototypePieces &proto,int4 pos,TypeFactory &tlist,
vector<int4> &status,ParameterPieces &res) const=0;
/// \brief Configure any details of how \b this action should behave from the stream
///
/// \param decoder is the given stream decoder
virtual void decode(Decoder &decoder)=0;
static AssignAction *decodeAction(Decoder &decoder,const ParamListStandard *res);
static AssignAction *decodeSideeffect(Decoder &decoder,const ParamListStandard *res);
};
/// \brief Action assigning a parameter Address from the next available stack location
class GotoStack : public AssignAction {
const ParamEntry *stackEntry; ///< Parameter Entry corresponding to the stack
void initializeEntry(void); ///< Find stack entry in resource list
public:
GotoStack(const ParamListStandard *res,int4 val); ///< Constructor for use with decode
GotoStack(const ParamListStandard *res); ///< Constructor for use with decode()
virtual AssignAction *clone(const ParamListStandard *newResource) const { return new GotoStack(newResource); }
virtual uint4 assignAddress(Datatype *dt,const PrototypePieces &proto,int4 pos,TypeFactory &tlist,
vector<int4> &status,ParameterPieces &res) const;
virtual void decode(Decoder &decoder);
};
/// \brief Action converting the parameter's data-type to a pointer, and assigning storage for the pointer
///
/// This assumes the data-type is stored elsewhere and only the pointer is passed as a parameter
class ConvertToPointer : public AssignAction {
AddrSpace *space; ///< Address space used for pointer size
public:
ConvertToPointer(const ParamListStandard *res); ///< Constructor for use with decode()
virtual AssignAction *clone(const ParamListStandard *newResource) const { return new ConvertToPointer(newResource); }
virtual uint4 assignAddress(Datatype *dt,const PrototypePieces &proto,int4 pos,TypeFactory &tlist,
vector<int4> &status,ParameterPieces &res) const;
virtual void decode(Decoder &decoder);
};
/// \brief Consume multiple registers to pass a data-type
///
/// Available registers are consumed until the data-type is covered, and an appropriate
/// \e join space address is assigned. Registers can be consumed from a specific resource list.
/// Consumption can spill over onto the stack if desired.
class MultiSlotAssign : public AssignAction {
type_class resourceType; ///< Resource list from which to consume
bool consumeFromStack; ///< True if resources should be consumed from the stack
bool consumeMostSig; ///< True if resources are consumed starting with most significant bytes
bool enforceAlignment; ///< True if register resources are discarded to match alignment
bool justifyRight; ///< True if initial bytes are padding for odd data-type sizes
const ParamEntry *stackEntry; ///< The stack resource
list<ParamEntry>::const_iterator firstIter; ///< Iterator to first element in the resource list
void initializeEntries(void); ///< Cache specific ParamEntry needed by the action
public:
MultiSlotAssign(const ParamListStandard *res); ///< Constructor for use with decode
MultiSlotAssign(type_class store,bool stack,bool mostSig,bool align,bool justRight,const ParamListStandard *res);
virtual AssignAction *clone(const ParamListStandard *newResource) const {
return new MultiSlotAssign(resourceType,consumeFromStack,consumeMostSig,enforceAlignment,justifyRight,newResource); }
virtual uint4 assignAddress(Datatype *dt,const PrototypePieces &proto,int4 pos,TypeFactory &tlist,
vector<int4> &status,ParameterPieces &res) const;
virtual void decode(Decoder &decoder);
};
/// \brief Consume a register per primitive member of an aggregate data-type
///
/// The data-type is split up into its underlying primitive elements, and each one
/// is assigned a register from the specific resource list. There must be no padding between
/// elements. No packing of elements into a single register occurs.
class MultiMemberAssign : public AssignAction {
type_class resourceType; ///< Resource list from which to consume
bool consumeFromStack; ///< True if resources should be consumed from the stack
bool consumeMostSig; ///< True if resources are consumed starting with most significant bytes
public:
MultiMemberAssign(type_class store,bool stack,bool mostSig,const ParamListStandard *res);
virtual AssignAction *clone(const ParamListStandard *newResource) const {
return new MultiMemberAssign(resourceType,consumeFromStack,consumeMostSig,newResource); }
virtual uint4 assignAddress(Datatype *dt,const PrototypePieces &proto,int4 pos,TypeFactory &tlist,
vector<int4> &status,ParameterPieces &res) const;
virtual void decode(Decoder &decoder);
};
/// \brief Consume a parameter from a specific resource list
///
/// Normally the resource list is determined by the parameter data-type, but this
/// action specifies an overriding resource list. Assignment will \e not fall through to the stack.
class ConsumeAs : public AssignAction {
type_class resourceType; ///< The resource list the parameter is consumed from
public:
ConsumeAs(type_class store,const ParamListStandard *res); ///< Constructor
virtual AssignAction *clone(const ParamListStandard *newResource) const {
return new ConsumeAs(resourceType,newResource); }
virtual uint4 assignAddress(Datatype *dt,const PrototypePieces &proto,int4 pos,TypeFactory &tlist,
vector<int4> &status,ParameterPieces &res) const;
virtual void decode(Decoder &decoder);
};
/// \brief Allocate the return value as special input register
///
/// The assignAddress() method signals with \b hiddenret_specialreg, indicating that the
/// input register assignMap() method should use storage class TYPECLASS_HIDDENRET to assign
/// an additional input register to hold a pointer to the return value. This is different than
/// the default \e hiddenret action that assigns a location based TYPECLASS_PTR and generally
/// consumes a general purpose input register.
class HiddenReturnAssign : public AssignAction {
uint4 retCode; ///< The specific signal to pass back
public:
HiddenReturnAssign(const ParamListStandard *res,bool voidLock); ///< Constructor
virtual AssignAction *clone(const ParamListStandard *newResource) const {
return new HiddenReturnAssign(newResource, retCode == hiddenret_specialreg_void); }
virtual uint4 assignAddress(Datatype *dt,const PrototypePieces &proto,int4 pos,TypeFactory &tlist,
vector<int4> &status,ParameterPieces &res) const;
virtual void decode(Decoder &decoder);
};
/// \brief Consume additional registers from an alternate resource list
///
/// This action is a side-effect and doesn't assign an address for the current parameter.
/// The resource list, \b resourceType, is specified. If the side-effect is triggered,
/// register resources from this list are consumed. If \b matchSize is true (the default),
/// registers are consumed, until the number of bytes in the data-type is reached. Otherwise,
/// only a single register is consumed. If all registers are already consumed, no action is taken.
class ConsumeExtra : public AssignAction {
type_class resourceType; ///< The other resource list to consume from
list<ParamEntry>::const_iterator firstIter; ///< Iterator to first element in the resource list
bool matchSize; ///< \b false, if side-effect only consumes a single register
void initializeEntries(void); ///< Cache specific ParamEntry needed by the action
public:
ConsumeExtra(const ParamListStandard *res); ///< Constructor for use with decode
ConsumeExtra(type_class store,bool match,const ParamListStandard *res); ///< Constructor
virtual AssignAction *clone(const ParamListStandard *newResource) const {
return new ConsumeExtra(resourceType,matchSize,newResource); }
virtual uint4 assignAddress(Datatype *dt,const PrototypePieces &proto,int4 pos,TypeFactory &tlist,
vector<int4> &status,ParameterPieces &res) const;
virtual void decode(Decoder &decoder);
};
/// \brief A rule controlling how parameters are assigned addresses
///
/// Rules are applied to a parameter in the context of a full function prototype.
/// A rule applies only for a specific class of data-type associated with the parameter, as
/// determined by its DatatypeFilter, and may have other criteria limiting when it applies
/// (via QualifierFilter).
class ModelRule {
DatatypeFilter *filter; ///< Which data-types \b this rule applies to
QualifierFilter *qualifier; ///< Additional qualifiers for when the rule should apply (if non-null)
AssignAction *assign; ///< How the Address should be assigned
vector<AssignAction *> sideeffects; ///< Extra actions that happen on success
public:
ModelRule(void) {
filter = (DatatypeFilter *)0; qualifier = (QualifierFilter *)0; assign = (AssignAction *)0; } ///< Constructor for use with decode
ModelRule(const ModelRule &op2,const ParamListStandard *res); ///< Copy constructor
ModelRule(const DatatypeFilter &typeFilter,const AssignAction &action,const ParamListStandard *res); ///< Construct from components
~ModelRule(void); ///< Destructor
uint4 assignAddress(Datatype *dt,const PrototypePieces &proto,int4 pos,TypeFactory &tlist,
vector<int4> &status,ParameterPieces &res) const;
void decode(Decoder &decoder,const ParamListStandard *res); ///< Decode \b this rule from stream
};
} // End namespace ghidra
#endif

View file

@ -578,9 +578,9 @@ void JoinSpace::encodeAttributes(Encoder &encoder,uintb offset,int4 size) const
encodeAttributes(encoder,offset); // Ignore size
}
/// Parse a join address the current element. Pieces of the join are encoded as a sequence
/// of attributes. The Translate::findAddJoin method is used to construct a logical
/// address within the join space.
/// Parse the current element as a join address. Pieces of the join are encoded as a sequence
/// of ATTRIB_PIECE attributes. "piece1" corresponds to the most significant piece. The
/// Translate::findAddJoin method is used to construct a logical address within the join space.
/// \param decoder is the stream decoder
/// \param size is a reference to be filled in as the size encoded by the tag
/// \return the offset of the final address encoded by the tag

View file

@ -629,7 +629,8 @@ AddrSpace *AddrSpaceManager::getNextSpaceInOrder(AddrSpace *spc) const
}
/// Given a list of memory locations, the \e pieces, either find a pre-existing JoinRecord or
/// create a JoinRecord that represents the logical joining of the pieces.
/// create a JoinRecord that represents the logical joining of the pieces. The pieces must
/// be in order from most significant to least significant.
/// \param pieces if the list memory locations to be joined
/// \param logicalsize of a \e single \e piece join, or zero
/// \return a pointer to the JoinRecord

View file

@ -196,7 +196,7 @@ public:
/// from \e most \e significant to \e least \e significant.
class JoinRecord {
friend class AddrSpaceManager;
vector<VarnodeData> pieces; ///< All the physical pieces of the symbol
vector<VarnodeData> pieces; ///< All the physical pieces of the symbol, most significant to least
VarnodeData unified; ///< Special entry representing entire symbol in one chunk
public:
int4 numPieces(void) const { return pieces.size(); } ///< Get number of pieces in this record

View file

@ -349,6 +349,72 @@ type_metatype string2metatype(const string &metastring)
throw LowlevelError("Unknown metatype: "+metastring);
}
/// Given a description of a data-type \e class, return the \b type_class.
/// \param classstring is the description of the class
/// \return the encoded type_class
type_class string2typeclass(const string &classstring)
{
switch(classstring[0]) {
case 'c':
if (classstring == "class1")
return TYPECLASS_CLASS1;
else if (classstring == "class2")
return TYPECLASS_CLASS2;
else if (classstring == "class3")
return TYPECLASS_CLASS3;
else if (classstring == "class4")
return TYPECLASS_CLASS4;
break;
case 'g':
if (classstring == "general")
return TYPECLASS_GENERAL;
break;
case 'h':
if (classstring == "hiddenret")
return TYPECLASS_HIDDENRET;
break;
case 'f':
if (classstring == "float")
return TYPECLASS_FLOAT;
break;
case 'p':
if (classstring == "ptr" || classstring == "pointer")
return TYPECLASS_PTR;
break;
case 'v':
if (classstring == "vector")
return TYPECLASS_VECTOR;
break;
case 'u':
if (classstring == "unknown")
return TYPECLASS_GENERAL;
break;
}
throw LowlevelError("Unknown data-type class: " + classstring);
}
/// Assign the basic storage class based on a metatype:
/// - TYPE_FLOAT -> TYPECLASS_FLOAT
/// - TYPE_PTR -> TYPECLASS_PTR
///
/// Everything else returns the general purpose TYPECLASS_GENERAL
/// \param meta is the metatype
/// \return the storage class
type_class metatype2typeclass(type_metatype meta)
{
switch(meta) {
case TYPE_FLOAT:
return TYPECLASS_FLOAT;
case TYPE_PTR:
return TYPECLASS_PTR;
default:
break;
}
return TYPECLASS_GENERAL;
}
/// Encode a formal description of the data-type as a \<type> element.
/// For composite data-types, the description goes down one level, describing
/// the component types only by reference.
@ -2383,31 +2449,19 @@ Datatype *TypePointerRel::getPtrToFromParent(Datatype *base,int4 off,TypeFactory
/// Turn on the data-type's function prototype
/// \param tfact is the factory that owns \b this
/// \param model is the prototype model
/// \param outtype is the return type of the prototype
/// \param intypes is the list of input parameters
/// \param dotdotdot is true if the prototype takes variable arguments
/// \param sig is the list of names and data-types making up the prototype
/// \param voidtype is the reference "void" data-type
void TypeCode::setPrototype(TypeFactory *tfact,ProtoModel *model,
Datatype *outtype,const vector<Datatype *> &intypes,
bool dotdotdot,Datatype *voidtype)
void TypeCode::setPrototype(TypeFactory *tfact,const PrototypePieces &sig,Datatype *voidtype)
{
factory = tfact;
flags |= variable_length;
if (proto != (FuncProto *)0)
delete proto;
proto = new FuncProto();
proto->setInternal(model,voidtype);
vector<Datatype *> typelist;
vector<string> blanknames(intypes.size()+1);
if (outtype == (Datatype *)0)
typelist.push_back(voidtype);
else
typelist.push_back(outtype);
for(int4 i=0;i<intypes.size();++i)
typelist.push_back(intypes[i]);
proto->setInternal(sig.model,voidtype);
proto->updateAllTypes(blanknames,typelist,dotdotdot);
proto->updateAllTypes(sig);
proto->setInputLock(true);
proto->setOutputLock(true);
}
@ -3670,17 +3724,12 @@ TypeSpacebase *TypeFactory::getTypeSpacebase(AddrSpace *id,const Address &addr)
}
/// Creates a TypeCode object and associates a specific function prototype with it.
/// \param model is the prototype model associated with the function
/// \param outtype is the return type of the function
/// \param intypes is the array of input parameters of the function
/// \param dotdotdot is true if the function takes variable arguments
/// \param proto is the list of names, data-types, and other attributes of the prototype
/// \return the TypeCode object
TypeCode *TypeFactory::getTypeCode(ProtoModel *model,Datatype *outtype,
const vector<Datatype *> &intypes,
bool dotdotdot)
TypeCode *TypeFactory::getTypeCode(const PrototypePieces &proto)
{
TypeCode tc; // getFuncdata type with no name
tc.setPrototype(this,model,outtype,intypes,dotdotdot,getTypeVoid());
tc.setPrototype(this,proto,getTypeVoid());
tc.markComplete();
return (TypeCode *) findAdd(tc);
}
@ -4311,11 +4360,15 @@ void TypeFactory::decodeAlignmentMap(Decoder &decoder)
void TypeFactory::setDefaultAlignmentMap(void)
{
alignMap.resize(5,0);
alignMap.resize(9,0);
alignMap[1] = 1;
alignMap[2] = 2;
alignMap[3] = 2;
alignMap[4] = 4;
alignMap[5] = 4;
alignMap[6] = 4;
alignMap[7] = 4;
alignMap[8] = 8;
}
/// Recover default enumeration properties (size and meta-type) from

View file

@ -122,12 +122,32 @@ enum sub_metatype {
SUB_UNION = 1, ///< Compare as TYPE_UNION
SUB_PARTIALUNION = 0 ///< Compare as a TYPE_PARTIALUNION
};
/// Data-type classes for the purpose of assigning storage
enum type_class {
TYPECLASS_GENERAL = 0, ///< General purpose
TYPECLASS_FLOAT = 1, ///< Floating-point data-types
TYPECLASS_PTR = 2, ///< Pointer data-types
TYPECLASS_HIDDENRET = 3, ///< Class for hidden return values
TYPECLASS_VECTOR = 4, ///< Vector data-types
TYPECLASS_CLASS1 = 100, ///< Architecture specific class 1
TYPECLASS_CLASS2 = 101, ///< Architecture specific class 2
TYPECLASS_CLASS3 = 102, ///< Architecture specific class 3
TYPECLASS_CLASS4 = 103 ///< Architecture specific class 4
};
/// Convert type \b meta-type to name
extern void metatype2string(type_metatype metatype,string &res);
/// Convert string to type \b meta-type
extern type_metatype string2metatype(const string &metastring);
/// Convert a string to a data-type class
extern type_class string2typeclass(const string &classstring);
/// Convert a data-type metatype to a data-type class
extern type_class metatype2typeclass(type_metatype meta);
class Architecture; // Forward declarations
class PcodeOp;
class Scope;
@ -616,7 +636,7 @@ public:
};
class FuncProto; // Forward declaration
class ProtoModel;
class PrototypePieces;
/// \brief Datatype object representing executable code.
///
@ -626,9 +646,7 @@ protected:
friend class TypeFactory;
FuncProto *proto; ///< If non-null, this describes the prototype of the underlying function
TypeFactory *factory; ///< Factory owning \b this
void setPrototype(TypeFactory *tfact,ProtoModel *model,
Datatype *outtype,const vector<Datatype *> &intypes,
bool dotdotdot,Datatype *voidtype); ///< Establish a function pointer
void setPrototype(TypeFactory *tfact,const PrototypePieces &sig,Datatype *voidtype); ///< Establish a function pointer
void setPrototype(TypeFactory *typegrp,const FuncProto *fp); ///< Set a particular function prototype on \b this
void decodeStub(Decoder &decoder); ///< Restore stub of data-type without the full prototype
void decodePrototype(Decoder &decoder,bool isConstructor,bool isDestructor,TypeFactory &typegrp); ///< Restore any prototype description
@ -752,9 +770,7 @@ public:
TypePartialUnion *getTypePartialUnion(TypeUnion *contain,int4 off,int4 sz); ///< Create a partial union
TypeEnum *getTypeEnum(const string &n); ///< Create an (empty) enumeration
TypeSpacebase *getTypeSpacebase(AddrSpace *id,const Address &addr); ///< Create a "spacebase" type
TypeCode *getTypeCode(ProtoModel *model,Datatype *outtype,
const vector<Datatype *> &intypes,
bool dotdotdot); ///< Create a "function" datatype
TypeCode *getTypeCode(const PrototypePieces &proto); ///< Create a "function" datatype
Datatype *getTypedef(Datatype *ct,const string &name,uint8 id,uint4 format); ///< Create a new \e typedef data-type
TypePointerRel *getTypePointerRel(TypePointer *parentPtr,Datatype *ptrTo,int4 off); ///< Get pointer offset relative to a container
TypePointerRel *getTypePointerRel(int4 sz,Datatype *parent,Datatype *ptrTo,int4 ws,int4 off,const string &nm);