Support for per function unaffected/killedbycall/likelytrash

This commit is contained in:
caheckman 2021-05-18 11:46:19 -04:00
parent 156ce7ef80
commit 75b7d5fe14
8 changed files with 703 additions and 245 deletions

View File

@ -2066,9 +2066,11 @@ int4 ActionLikelyTrash::apply(Funcdata &data)
{
vector<PcodeOp *> indlist;
int4 num = data.getFuncProto().numLikelyTrash();
for(int4 j=0;j<num;++j) {
const VarnodeData &vdata( data.getFuncProto().getLikelyTrash(j) );
vector<VarnodeData>::const_iterator iter,enditer;
iter = data.getFuncProto().trashBegin();
enditer = data.getFuncProto().trashEnd();
for(;iter!=enditer;++iter) {
const VarnodeData &vdata( *iter );
Varnode *vn = data.findCoveredInput(vdata.size,vdata.getAddr());
if (vn == (Varnode *)0) continue;
if (vn->isTypeLock()||vn->isNameLock()) continue;

View File

@ -16,44 +16,65 @@
#include "fspec.hh"
#include "funcdata.hh"
void ParamEntry::resolveJoin(void)
/// \brief Find a ParamEntry matching the given storage Varnode
///
/// Search through the list backward.
/// \param entryList is the list of ParamEntry to search through
/// \param vn is the storage to search for
/// \return the matching ParamEntry or null
const ParamEntry *ParamEntry::findEntryByStorage(const list<ParamEntry> &entryList,const VarnodeData &vn)
{
if (spaceid->getType() == IPTR_JOIN)
joinrec = spaceid->getManager()->findJoin(addressbase);
else
joinrec = (JoinRecord *)0;
list<ParamEntry>::const_reverse_iterator iter = entryList.rbegin();
for(;iter!=entryList.rend();++iter) {
const ParamEntry &entry(*iter);
if (entry.spaceid == vn.space && entry.addressbase == vn.offset && entry.size == vn.size) {
return &entry;
}
}
return (const ParamEntry *)0;
}
/// \brief Construct entry from components
///
/// \param t is the data-type class (TYPE_UNKNOWN or TYPE_FLOAT)
/// \param grp is the group id
/// \param grpsize is the number of consecutive groups occupied
/// \param loc is the starting address of the memory range
/// \param sz is the number of bytes in the range
/// \param mnsz is the smallest size of a logical value
/// \param align is the alignment (0 means the memory range will hold one parameter exclusively)
/// \param normalstack is \b true if parameters are allocated from the front of the range
ParamEntry::ParamEntry(type_metatype t,int4 grp,int4 grpsize,const Address &loc,int4 sz,int4 mnsz,int4 align,bool normalstack)
/// If the ParamEntry is initialized with a \e join address, cache the join record and
/// adjust the group and groupsize based on the ParamEntrys being overlapped
/// \param curList is the current list of ParamEntry
void ParamEntry::resolveJoin(list<ParamEntry> &curList)
{
flags = 0;
type = t;
group = grp;
groupsize = grpsize;
spaceid = loc.getSpace();
addressbase = loc.getOffset();
size = sz;
minsize = mnsz;
alignment = align;
if (alignment != 0)
numslots = size / alignment;
else
numslots = 1;
if (!normalstack)
flags |= reverse_stack;
resolveJoin();
if (spaceid->getType() != IPTR_JOIN) {
joinrec = (JoinRecord *)0;
return;
}
joinrec = spaceid->getManager()->findJoin(addressbase);
int4 mingrp = 1000;
int4 maxgrp = -1;
for(int4 i=0;i<joinrec->numPieces();++i) {
const ParamEntry *entry = findEntryByStorage(curList, joinrec->getPiece(i));
if (entry != (const ParamEntry *)0) {
if (entry->group < mingrp)
mingrp = entry->group;
int4 max = entry->group + entry->groupsize;
if (max > maxgrp)
maxgrp = max;
}
}
if (maxgrp < 0 || mingrp >= 1000)
throw LowlevelError("<pentry> join must overlap at least one previous entry");
group = mingrp;
groupsize = (maxgrp - mingrp);
if (groupsize > joinrec->numPieces())
throw LowlevelError("<pentry> join must overlap sequential entries");
}
/// A ParamEntry with \e join storage must either overlap a single other ParamEntry or
/// all pieces must overlap.
/// \return \b true if \b this is a join whose pieces do not all overlap
bool ParamEntry::isNonOverlappingJoin(void) const
{
if (joinrec == (JoinRecord *)0)
return false;
return (joinrec->numPieces() != groupsize);
}
/// This entry must properly contain the other memory range, and
@ -299,7 +320,9 @@ Address ParamEntry::getAddrBySlot(int4 &slotnum,int4 sz) const
/// \param el is the root \<pentry> element
/// \param manage is a manager to resolve address space references
/// \param normalstack is \b true if the parameters should be allocated from the front of the range
void ParamEntry::restoreXml(const Element *el,const AddrSpaceManager *manage,bool normalstack)
/// \param grouped is \b true if \b this will be grouped with other entries
/// \param curList is the list of ParamEntry defined up to this point
void ParamEntry::restoreXml(const Element *el,const AddrSpaceManager *manage,bool normalstack,bool grouped,list<ParamEntry> &curList)
{
flags = 0;
@ -334,16 +357,6 @@ void ParamEntry::restoreXml(const Element *el,const AddrSpaceManager *manage,boo
}
else if (attrname == "metatype")
type = string2metatype(el->getAttributeValue(i));
else if (attrname == "group") { // Override the group
istringstream i5(el->getAttributeValue(i));
i5.unsetf(ios::dec | ios::hex | ios::oct);
i5 >> group;
}
else if (attrname == "groupsize") {
istringstream i6(el->getAttributeValue(i));
i6.unsetf(ios::dec | ios::hex | ios::oct);
i6 >> groupsize;
}
else if (attrname == "extension") {
flags &= ~((uint4)(smallsize_zext | smallsize_sext | smallsize_inttype));
if (el->getAttributeValue(i) == "sign")
@ -386,7 +399,9 @@ void ParamEntry::restoreXml(const Element *el,const AddrSpaceManager *manage,boo
throw LowlevelError("For positive stack growth, <pentry> size must match alignment");
}
}
resolveJoin();
if (grouped)
flags |= is_grouped;
resolveJoin(curList);
}
/// \brief Check if \b this entry represents a \e joined parameter and requires extra scrutiny
@ -418,6 +433,43 @@ void ParamEntry::extraChecks(list<ParamEntry> &entry)
flags |= extracheck_high; // The default is to do extra checks on the high
}
/// If the storage is in the \e join space, we count the number of join pieces that overlap something
/// in the given list of entries.
/// \param curList is the list of entries to check
/// \return the number of overlapping pieces
int4 ParamEntry::countJoinOverlap(const list<ParamEntry> &curList) const
{
if (joinrec == (JoinRecord *)0)
return 0;
int count = 0;
for (int4 i=0;i<joinrec->numPieces();++i) {
const ParamEntry *match = findEntryByStorage(curList, joinrec->getPiece(i));
if (match != (const ParamEntry *)0)
count += 1;
}
return count;
}
/// Entries within a group must be distinguishable by size or by type.
/// Throw an exception if the entries aren't distinguishable
/// \param entry1 is the first ParamEntry to compare
/// \param entry2 is the second ParamEntry to compare
void ParamEntry::orderWithinGroup(const ParamEntry &entry1,const ParamEntry &entry2)
{
if (entry2.minsize > entry1.size || entry1.minsize > entry2.size)
return;
if (entry1.type != entry2.type) {
if (entry1.type == TYPE_UNKNOWN) {
throw LowlevelError("<pentry> tags with a specific type must come before the general type");
}
return;
}
throw LowlevelError("<pentry> tags within a group must be distinguished by size or type");
}
ParamListStandard::ParamListStandard(const ParamListStandard &op2)
{
@ -427,7 +479,7 @@ ParamListStandard::ParamListStandard(const ParamListStandard &op2)
maxdelay = op2.maxdelay;
pointermax = op2.pointermax;
thisbeforeret = op2.thisbeforeret;
nonfloatgroup = op2.nonfloatgroup;
resourceTwoStart = op2.resourceTwoStart;
populateResolver();
}
@ -677,30 +729,39 @@ void ParamListStandard::buildTrialMap(ParamActive *active) const
active->sortTrials();
}
/// \brief Calculate the range of floating-point entries within a given set of parameter \e trials
/// \brief Calculate the range of trials in each of the two resource sections
///
/// The trials must already be mapped, which should put floating-point entries first.
/// This method calculates the range of floating-point entries and the range of general purpose
/// entries and passes them back.
/// The trials must already be mapped, which should put them in group order. The sections
/// split at the group given by \b resourceTwoStart. We pass back the range of trial indices
/// for each section. If \b resourceTwoStart is 0, then there is really only one section, and
/// the empty range [0,0] is passed back for the second section.
/// \param active is the given set of parameter trials
/// \param floatstart will pass back the index of the first floating-point trial
/// \param floatstop will pass back the index (+1) of the last floating-point trial
/// \param start will pass back the index of the first general purpose trial
/// \param stop will pass back the index (+1) of the last general purpose trial
void ParamListStandard::separateFloat(ParamActive *active,int4 &floatstart,int4 &floatstop,int4 &start,int4 &stop) const
/// \param oneStart will pass back the index of the first trial in the first section
/// \param oneStop will pass back the index (+1) of the last trial in the first section
/// \param twoStart will pass back the index of the first trial in the second section
/// \param twoStop will pass back the index (+1) of the last trial in the second section
void ParamListStandard::separateSections(ParamActive *active,int4 &oneStart,int4 &oneStop,int4 &twoStart,int4 &twoStop) const
{
int4 numtrials = active->getNumTrials();
if (resourceTwoStart == 0) {
// Only one section
oneStart = 0;
oneStop = numtrials;
twoStart = 0;
twoStop = 0;
return;
}
int4 i=0;
for(;i<numtrials;++i) {
ParamTrial &curtrial(active->getTrial(i));
if (curtrial.getEntry()==(const ParamEntry *)0) continue;
if (curtrial.getEntry()->getType()!=TYPE_FLOAT) break;
if (curtrial.getEntry()->getGroup() >= resourceTwoStart) break;
}
floatstart = 0;
floatstop = i;
start = i;
stop = numtrials;
oneStart = 0;
oneStop = i;
twoStart = i;
twoStop = numtrials;
}
/// \brief Enforce exclusion rules for the given set of parameter trials
@ -768,14 +829,17 @@ void ParamListStandard::forceNoUse(ParamActive *active, int4 start, int4 stop) c
///
/// If there is a chain of slots whose length is greater than \b maxchain,
/// where all trials are \e inactive, mark trials in any later slot as \e inactive.
/// Mark any \e inactive trials before this (that aren't in a maximal chain)
/// as active. Inspection and marking is restricted to a given range of trials
/// to facilitate separate analysis of floating-point and general-purpose resources.
/// Mark any \e inactive trials before this (that aren't in a maximal chain) as active.
/// The parameter entries in the model may be split up into different resource sections,
/// as in floating-point vs general purpose. This method must be called on a single
/// section at a time. The \b start and \b stop indices describe the range of trials
/// in the particular section.
/// \param active is the set of trials, which must be sorted
/// \param maxchain is the maximum number of \e inactive trials to allow in a chain
/// \param start is the first index in the range of trials to consider
/// \param stop is the last index (+1) in the range of trials to consider
void ParamListStandard::forceInactiveChain(ParamActive *active,int4 maxchain,int4 start,int4 stop) const
/// \param groupstart is the smallest group id in the particular section
void ParamListStandard::forceInactiveChain(ParamActive *active,int4 maxchain,int4 start,int4 stop,int4 groupstart) const
{
bool seenchain = false;
@ -794,10 +858,7 @@ void ParamListStandard::forceInactiveChain(ParamActive *active,int4 maxchain,int
seenchain = true; // Mark that we have already seen an inactive chain
}
if (i==start) {
if (trial.getEntry()->getType() == TYPE_FLOAT)
chainlength += (active->getTrial(0).slotGroup()+1);
else
chainlength += (trial.slotGroup() - nonfloatgroup + 1);
chainlength += (trial.slotGroup() - groupstart + 1);
}
else
chainlength += trial.slotGroup() - active->getTrial(i-1).slotGroup();
@ -861,6 +922,76 @@ void ParamListStandard::populateResolver(void)
}
}
/// \brief Read a \<pentry> tag and add it to \b this list
///
/// \param el is the \<pentry element
/// \param manage is manager for parsing address spaces
/// \param effectlist holds any passed back effect records
/// \param groupid is the group to which the new ParamEntry is assigned
/// \param normalstack is \b true if the parameters should be allocated from the front of the range
/// \param autokill is \b true if parameters are automatically added to the killedbycall list
/// \param splitFloat is \b true if floating-point parameters are in their own resource section
/// \param grouped is \b true if the new ParamEntry is grouped with other entries
void ParamListStandard::parsePentry(const Element *el,const AddrSpaceManager *manage,vector<EffectRecord> &effectlist,
int4 groupid,bool normalstack,bool autokill,bool splitFloat,bool grouped)
{
entry.emplace_back(groupid);
entry.back().restoreXml(el,manage,normalstack,grouped,entry);
if (splitFloat) {
if (entry.back().getType() == TYPE_FLOAT) {
if (resourceTwoStart >= 0)
throw LowlevelError("parameter list floating-point entries must come first");
}
else if (resourceTwoStart < 0)
resourceTwoStart = groupid; // First time we have seen an integer slot
}
AddrSpace *spc = entry.back().getSpace();
if (spc->getType() == IPTR_SPACEBASE)
spacebase = spc;
else if (autokill) // If a register parameter AND we automatically generate killedbycall
effectlist.push_back(EffectRecord(entry.back(),EffectRecord::killedbycall));
int4 maxgroup = entry.back().getGroup() + entry.back().getGroupSize();
if (maxgroup > numgroup)
numgroup = maxgroup;
}
/// \brief Read a group of \<pentry> tags that are allocated as a group
///
/// All ParamEntry objects will share the same \b group id.
/// \param el is the \<pentry element
/// \param manage is manager for parsing address spaces
/// \param effectlist holds any passed back effect records
/// \param groupid is the group to which all ParamEntry elements are assigned
/// \param normalstack is \b true if the parameters should be allocated from the front of the range
/// \param autokill is \b true if parameters are automatically added to the killedbycall list
/// \param splitFloat is \b true if floating-point parameters are in their own resource section
void ParamListStandard::parseGroup(const Element *el,const AddrSpaceManager *manage,vector<EffectRecord> &effectlist,
int4 groupid,bool normalstack,bool autokill,bool splitFloat)
{
const List &flist(el->getChildren());
List::const_iterator iter = flist.begin();
int4 basegroup = numgroup;
ParamEntry *previous1 = (ParamEntry *)0;
ParamEntry *previous2 = (ParamEntry *)0;
for(;iter!=flist.end();++iter) {
const Element *subel = *iter;
if (subel->getName() != "pentry")
throw LowlevelError("Expected <pentry> child of <group>: " + subel->getName());
parsePentry(subel, manage, effectlist, basegroup, normalstack, autokill, splitFloat, true);
ParamEntry &pentry( entry.back() );
if (pentry.getSpace()->getType() == IPTR_JOIN)
throw LowlevelError("<pentry> in the join space not allowed in <group> tag");
if (previous1 != (ParamEntry *)0) {
ParamEntry::orderWithinGroup(*previous1,pentry);
if (previous2 != (ParamEntry *)0)
ParamEntry::orderWithinGroup(*previous2,pentry);
}
previous2 = previous1;
previous1 = &pentry;
}
}
void ParamListStandard::fillinMap(ParamActive *active) const
{
@ -869,12 +1000,12 @@ void ParamListStandard::fillinMap(ParamActive *active) const
buildTrialMap(active); // Associate varnodes with sorted list of parameter locations
forceExclusionGroup(active);
int4 floatstart,floatstop,start,stop;
separateFloat(active,floatstart,floatstop,start,stop);
forceNoUse(active,floatstart,floatstop);
forceNoUse(active,start,stop); // Definitely not used -- overrides active
forceInactiveChain(active,2,floatstart,floatstop); // Chains of inactivity override later actives
forceInactiveChain(active,2,start,stop);
int4 oneStart,oneStop,twoStart,twoStop;
separateSections(active,oneStart,oneStop,twoStart,twoStop);
forceNoUse(active,oneStart,oneStop);
forceNoUse(active,twoStart,twoStop); // Definitely not used -- overrides active
forceInactiveChain(active,2,oneStart,oneStop,0); // Chains of inactivity override later actives
forceInactiveChain(active,2,twoStart,twoStop,resourceTwoStart);
// Mark every active trial as used
for(int4 i=0;i<active->getNumTrials();++i) {
@ -1024,11 +1155,11 @@ void ParamListStandard::restoreXml(const Element *el,const AddrSpaceManager *man
vector<EffectRecord> &effectlist,bool normalstack)
{
int4 lastgroup = -1;
numgroup = 0;
spacebase = (AddrSpace *)0;
pointermax = 0;
thisbeforeret = false;
bool splitFloat = true; // True if we should split FLOAT entries into their own resource section
bool autokilledbycall = false;
for(int4 i=0;i<el->getNumAttributes();++i) {
const string &attrname( el->getAttributeName(i) );
@ -1043,33 +1174,29 @@ void ParamListStandard::restoreXml(const Element *el,const AddrSpaceManager *man
else if (attrname == "killedbycall") {
autokilledbycall = xml_readbool( el->getAttributeValue(i) );
}
else if (attrname == "separatefloat") {
splitFloat = xml_readbool( el->getAttributeValue(i) );
}
}
nonfloatgroup = -1; // We haven't seen any integer slots yet
resourceTwoStart = splitFloat ? -1 : 0;
const List &flist(el->getChildren());
List::const_iterator fiter;
for(fiter=flist.begin();fiter!=flist.end();++fiter) {
const Element *subel = *fiter;
if (subel->getName() == "pentry") {
entry.emplace_back(numgroup);
entry.back().restoreXml(subel,manage,normalstack);
if (entry.back().getType()==TYPE_FLOAT) {
if (nonfloatgroup >= 0)
throw LowlevelError("parameter list floating-point entries must come first");
parsePentry(subel, manage, effectlist, numgroup, normalstack, autokilledbycall, splitFloat, false);
}
else if (subel->getName() == "group") {
parseGroup(subel, manage, effectlist, numgroup, normalstack, autokilledbycall, splitFloat);
}
}
// Check that any pentry tags with join storage don't overlap following tags
for (list<ParamEntry>::const_iterator eiter=entry.begin();eiter!=entry.end();++eiter) {
const ParamEntry &curEntry( *eiter );
if (curEntry.isNonOverlappingJoin()) {
if (curEntry.countJoinOverlap(entry) != 1) {
throw LowlevelError("pentry tag must be listed after all its overlaps");
}
else if (nonfloatgroup < 0)
nonfloatgroup = numgroup; // First time we have seen an integer slot
AddrSpace *spc = entry.back().getSpace();
if (spc->getType() == IPTR_SPACEBASE)
spacebase = spc;
else if (autokilledbycall) // If a register parameter AND we automatically generate killedbycall
effectlist.push_back(EffectRecord(entry.back(),EffectRecord::killedbycall));
int4 maxgroup = entry.back().getGroup() + entry.back().getGroupSize();
if (maxgroup > numgroup)
numgroup = maxgroup;
if (entry.back().getGroup() < lastgroup)
throw LowlevelError("pentrys must come in group order");
lastgroup = entry.back().getGroup();
}
}
calcDelay();
@ -1214,8 +1341,19 @@ void ParamListStandardOut::restoreXml(const Element *el,const AddrSpaceManager *
ParamListStandard::restoreXml(el,manage,effectlist,normalstack);
// Check for double precision entries
list<ParamEntry>::iterator iter;
for(iter=entry.begin();iter!=entry.end();++iter)
(*iter).extraChecks(entry);
ParamEntry *previous1 = (ParamEntry *)0;
ParamEntry *previous2 = (ParamEntry *)0;
for(iter=entry.begin();iter!=entry.end();++iter) {
ParamEntry &curEntry(*iter);
curEntry.extraChecks(entry);
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
@ -1628,9 +1766,9 @@ void FspecSpace::restoreXml(const Element *el)
EffectRecord::EffectRecord(const Address &addr,int4 size)
{
address.space = addr.getSpace();
address.offset = addr.getOffset();
address.size = size;
range.space = addr.getSpace();
range.offset = addr.getOffset();
range.size = size;
type = unknown_effect;
}
@ -1639,9 +1777,9 @@ EffectRecord::EffectRecord(const Address &addr,int4 size)
EffectRecord::EffectRecord(const ParamEntry &entry,uint4 t)
{
address.space = entry.getSpace();
address.offset = entry.getBase();
address.size = entry.getSize();
range.space = entry.getSpace();
range.offset = entry.getBase();
range.size = entry.getSize();
type = t;
}
@ -1650,7 +1788,7 @@ EffectRecord::EffectRecord(const ParamEntry &entry,uint4 t)
EffectRecord::EffectRecord(const VarnodeData &data,uint4 t)
{
address = data;
range = data;
type = t;
}
@ -1659,9 +1797,9 @@ EffectRecord::EffectRecord(const VarnodeData &data,uint4 t)
void EffectRecord::saveXml(ostream &s) const
{
Address addr(address.space,address.offset);
Address addr(range.space,range.offset);
if ((type == unaffected)||(type == killedbycall)||(type == return_address))
addr.saveXml(s,address.size);
addr.saveXml(s,range.size);
else
throw LowlevelError("Bad EffectRecord type");
}
@ -1674,7 +1812,7 @@ void EffectRecord::restoreXml(uint4 grouptype,const Element *el,const AddrSpaceM
{
type = grouptype;
address.restoreXml(el,manage);
range.restoreXml(el,manage);
}
void ProtoModel::defaultLocalRange(void)
@ -1881,7 +2019,7 @@ uint4 ProtoModel::lookupEffect(const vector<EffectRecord> &efflist,const Address
vector<EffectRecord>::const_iterator iter;
iter = upper_bound(efflist.begin(),efflist.end(),cur);
iter = upper_bound(efflist.begin(),efflist.end(),cur,EffectRecord::compareByAddress);
// First element greater than cur (address must be greater)
// go back one more, and we get first el less or equal to cur
if (iter==efflist.begin()) return EffectRecord::unknown_effect; // Can't go back one
@ -1896,6 +2034,44 @@ uint4 ProtoModel::lookupEffect(const vector<EffectRecord> &efflist,const Address
return EffectRecord::unknown_effect;
}
/// \brief Look up a particular EffectRecord from a given list by its Address and size
///
/// The index of the matching EffectRecord from the given list is returned. Only the first
/// \e listSize elements are examined, which much be sorted by Address.
/// If no matching range exists, a negative number is returned.
/// - -1 if the Address and size don't overlap any other EffectRecord
/// - -2 if there is overlap with another EffectRecord
///
/// \param efflist is the given list
/// \param listSize is the number of records in the list to search through
/// \param addr is the starting Address of the record to find
/// \param size is the size of the record to find
/// \return the index of the matching record or a negative number
int4 ProtoModel::lookupRecord(const vector<EffectRecord> &efflist,int4 listSize,
const Address &addr,int4 size)
{
if (listSize == 0) return -1;
EffectRecord cur(addr,size);
vector<EffectRecord>::const_iterator begiter = efflist.begin();
vector<EffectRecord>::const_iterator enditer = begiter + listSize;
vector<EffectRecord>::const_iterator iter;
iter = upper_bound(begiter,enditer,cur,EffectRecord::compareByAddress);
// First element greater than cur (address must be greater)
// go back one more, and we get first el less or equal to cur
if (iter==efflist.begin()) {
Address closeAddr = (*iter).getAddress();
return (closeAddr.overlap(0,addr,size) < 0) ? -1 : -2;
}
--iter;
Address closeAddr =(*iter).getAddress();
int4 sz = (*iter).getSize();
if (addr == closeAddr && size == sz)
return iter - begiter;
return (addr.overlap(0,closeAddr,sz) < 0) ? -1 : -2;
}
/// The model is searched for an EffectRecord matching the given range
/// and the effect type is returned. If there is no EffectRecord or the
/// effect generally isn't known, EffectRecord::unknown_effect is returned.
@ -2054,7 +2230,7 @@ void ProtoModel::restoreXml(const Element *el)
// Provide the default return address, if there isn't a specific one for the model
effectlist.push_back(EffectRecord(glb->defaultReturnAddr,EffectRecord::return_address));
}
sort(effectlist.begin(),effectlist.end());
sort(effectlist.begin(),effectlist.end(),EffectRecord::compareByAddress);
sort(likelytrash.begin(),likelytrash.end());
if (!sawlocalrange)
defaultLocalRange();
@ -2151,17 +2327,18 @@ void ProtoModelMerged::intersectEffects(const vector<EffectRecord> &efflist)
const EffectRecord &eff1( effectlist[i] );
const EffectRecord &eff2( efflist[j] );
if (eff1 < eff2)
if (EffectRecord::compareByAddress(eff1, eff2))
i += 1;
else if (eff2 < eff1)
else if (EffectRecord::compareByAddress(eff2, eff1))
j += 1;
else {
newlist.push_back(eff1);
if (eff1 == eff2)
newlist.push_back(eff1);
i += 1;
j += 1;
}
}
effectlist = newlist;
effectlist.swap(newlist);
}
/// The \e likely-trash locations are intersected. Anything in \b this that is not also in the
@ -2918,6 +3095,126 @@ void FuncProto::updateThisPointer(void)
param->setThisPointer(true);
}
/// If the \e effectlist for \b this is non-empty, it contains the complete set of
/// EffectRecords. Save just those that override the underlying list from ProtoModel
/// \param s is the stream to write to
void FuncProto::saveEffectXml(ostream &s) const
{
if (effectlist.empty()) return;
vector<const EffectRecord *> unaffectedList;
vector<const EffectRecord *> killedByCallList;
const EffectRecord *retAddr = (const EffectRecord *)0;
for(vector<EffectRecord>::const_iterator iter=effectlist.begin();iter!=effectlist.end();++iter) {
const EffectRecord &curRecord( *iter );
uint4 type = model->hasEffect(curRecord.getAddress(), curRecord.getSize());
if (type == curRecord.getType()) continue;
if (curRecord.getType() == EffectRecord::unaffected)
unaffectedList.push_back(&curRecord);
else if (curRecord.getType() == EffectRecord::killedbycall)
killedByCallList.push_back(&curRecord);
else if (curRecord.getType() == EffectRecord::return_address)
retAddr = &curRecord;
}
if (!unaffectedList.empty()) {
s << " <unaffected>\n";
for(int4 i=0;i<unaffectedList.size();++i) {
s << " ";
unaffectedList[i]->saveXml(s);
s << '\n';
}
s << " </unaffected>\n";
}
if (!killedByCallList.empty()) {
s << " <killedbycall>\n";
for(int4 i=0;i<killedByCallList.size();++i) {
s << " ";
killedByCallList[i]->saveXml(s);
s << '\n';
}
s << " </killedbycall>\n";
}
if (retAddr != (const EffectRecord *)0) {
s << " <returnaddress>\n ";
retAddr->saveXml(s);
s << "\n </returnaddress>\n";
}
}
/// If the -likelytrash- list is not empty it overrides the underlying ProtoModel's list.
/// Write any VarnodeData that does not appear in the ProtoModel to the XML stream.
/// \param s is the stream to write to
void FuncProto::saveLikelyTrashXml(ostream &s) const
{
if (likelytrash.empty()) return;
vector<VarnodeData>::const_iterator iter1,iter2;
iter1 = model->trashBegin();
iter2 = model->trashEnd();
s << " <likelytrash>\n";
for(vector<VarnodeData>::const_iterator iter=likelytrash.begin();iter!=likelytrash.end();++iter) {
const VarnodeData &cur(*iter);
if (binary_search(iter1,iter2,cur)) continue; // Already exists in ProtoModel
s << " <addr";
cur.space->saveXmlAttributes(s,cur.offset,cur.size);
s << "/>\n";
}
s << " </likelytrash>\n";
}
/// EffectRecords read into \e effectlist by restoreXml() override the list from ProtoModel.
/// If this list is not empty, set up \e effectlist as a complete override containing
/// all EffectRecords from ProtoModel plus all the overrides.
void FuncProto::restoreEffectXml(void)
{
if (effectlist.empty()) return;
vector<EffectRecord> tmpList;
tmpList.swap(effectlist);
for(vector<EffectRecord>::const_iterator iter=model->effectBegin();iter!=model->effectEnd();++iter) {
effectlist.push_back(*iter);
}
bool hasNew = false;
int4 listSize = effectlist.size();
for(vector<EffectRecord>::const_iterator iter=tmpList.begin();iter!=tmpList.end();++iter) {
const EffectRecord &curRecord( *iter );
int4 off = ProtoModel::lookupRecord(effectlist, listSize, curRecord.getAddress(), curRecord.getSize());
if (off == -2)
throw LowlevelError("Partial overlap of prototype override with existing effects");
else if (off >= 0) {
// Found matching record, change its type
effectlist[off] = curRecord;
}
else {
effectlist.push_back(curRecord);
hasNew = true;
}
}
if (hasNew)
sort(effectlist.begin(),effectlist.end(),EffectRecord::compareByAddress);
}
/// VarnodeData read into \e likelytrash by restoreXml() are additional registers over
/// what is already in ProtoModel. Make \e likelytrash in \b this a complete list by
/// merging in everything from ProtoModel.
void FuncProto::restoreLikelyTrashXml(void)
{
if (likelytrash.empty()) return;
vector<VarnodeData> tmpList;
tmpList.swap(likelytrash);
vector<VarnodeData>::const_iterator iter1,iter2;
iter1 = model->trashBegin();
iter2 = model->trashEnd();
for(vector<VarnodeData>::const_iterator iter=iter1;iter!=iter2;++iter)
likelytrash.push_back(*iter);
for(vector<VarnodeData>::const_iterator iter=tmpList.begin();iter!=tmpList.end();++iter) {
if (!binary_search(iter1,iter2,*iter))
likelytrash.push_back(*iter); // Add in the new register
}
sort(likelytrash.begin(),likelytrash.end());
}
/// Prepend the indicated number of input parameters to \b this.
/// The new parameters have a data-type of xunknown4. If they were
/// originally locked, the existing parameters are preserved.
@ -3472,23 +3769,22 @@ vector<EffectRecord>::const_iterator FuncProto::effectEnd(void) const
return effectlist.end();
}
/// \return the number of individual storage locations
int4 FuncProto::numLikelyTrash(void) const
/// \return the iterator to the start of the list
vector<VarnodeData>::const_iterator FuncProto::trashBegin(void) const
{
if (likelytrash.empty())
return model->numLikelyTrash();
return likelytrash.size();
return model->trashBegin();
return likelytrash.begin();
}
/// \param i is the index of the storage location
/// \return the storage location which may hold a trash value
const VarnodeData &FuncProto::getLikelyTrash(int4 i) const
/// \return the iterator to the end of the list
vector<VarnodeData>::const_iterator FuncProto::trashEnd(void) const
{
if (likelytrash.empty())
return model->getLikelyTrash(i);
return likelytrash[i];
return model->trashEnd();
return likelytrash.end();
}
/// \brief Decide whether a given storage location could be, or could hold, an input parameter
@ -3772,57 +4068,8 @@ void FuncProto::saveXml(ostream &s) const
outparam->getAddress().saveXml(s,outparam->getSize());
outparam->getType()->saveXml(s);
s << " </returnsym>\n";
if (!effectlist.empty()) {
int4 othercount = 0;
s << " <unaffected>\n";
for(uint4 i=0;i<effectlist.size();++i) {
uint4 tp = effectlist[i].getType();
if (tp!=EffectRecord::unaffected) {
othercount += 1;
continue;
}
s << " ";
effectlist[i].saveXml(s);
s << '\n';
}
s << " </unaffected>\n";
if (othercount > 0) {
othercount = 0;
s << " <killedbycall>\n";
for(uint4 i=0;i<effectlist.size();++i) {
uint4 tp = effectlist[i].getType();
if (tp != EffectRecord::killedbycall) {
othercount += 1;
continue;
}
s << " ";
effectlist[i].saveXml(s);
s << '\n';
}
s << " </killedbycall>\n";
}
if (othercount > 0) {
s << " <returnaddress>\n";
for(uint4 i=0;i<effectlist.size();++i) {
uint4 tp = effectlist[i].getType();
if (tp != EffectRecord::return_address) continue;
s << " ";
effectlist[i].saveXml(s);
s << '\n';
}
s << " </returnaddress>\n";
}
}
if (!likelytrash.empty()) {
s << " <likelytrash>\n";
for(uint4 i=0;i<likelytrash.size();++i) {
s << " <addr";
const VarnodeData &vdata(likelytrash[i]);
vdata.space->saveXmlAttributes(s,vdata.offset,vdata.size);
s << "/>\n";
}
s << " </likelytrash>\n";
}
saveEffectXml(s);
saveLikelyTrashXml(s);
if (injectid >= 0) {
Architecture *glb = model->getArch();
s << " <inject>" << glb->pcodeinjectlib->getCallFixupName(injectid) << "</inject>\n";
@ -4006,8 +4253,8 @@ void FuncProto::restoreXml(const Element *el,Architecture *glb)
store->restoreXml(*iter,model);
}
}
sort(effectlist.begin(),effectlist.end());
sort(likelytrash.begin(),likelytrash.end());
restoreEffectXml();
restoreLikelyTrashXml();
if (!isModelLocked()) {
if (isInputLocked())
flags |= modellock;

View File

@ -56,7 +56,8 @@ public:
smallsize_inttype = 32, ///< Assume values that are below the max \b size are sign OR zero extended based on integer type
smallsize_floatext = 64, ///< Assume values smaller than max \b size are floating-point extended to full size
extracheck_high = 128, ///< Perform extra checks during parameter recovery on most sig portion of the double
extracheck_low = 256 ///< Perform extra checks during parameter recovery on least sig portion of the double
extracheck_low = 256, ///< Perform extra checks during parameter recovery on least sig portion of the double
is_grouped = 512 ///< This entry is grouped with other entries
};
private:
uint4 flags; ///< Boolean properties of the parameter
@ -70,7 +71,8 @@ private:
int4 alignment; ///< How much alignment (0 means only 1 logical value is allowed)
int4 numslots; ///< (Maximum) number of slots that can store separate parameters
JoinRecord *joinrec; ///< Non-null if this is logical variable from joined pieces
void resolveJoin(void); ///< If the ParamEntry is initialized with a \e join address, cache the join record
static const ParamEntry *findEntryByStorage(const list<ParamEntry> &entryList,const VarnodeData &vn);
void resolveJoin(list<ParamEntry> &curList); ///< Make adjustments for a \e join ParamEntry
/// \brief Is the logical value left-justified within its container
bool isLeftJustified(void) const { return (((flags&force_left_justify)!=0)||(!spaceid->isBigEndian())); }
@ -85,6 +87,8 @@ public:
type_metatype 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
bool isNonOverlappingJoin(void) const; ///< Return \b true if not all pieces overlap other ParamEntry tags
bool contains(const ParamEntry &op2) const; ///< Does \b this contain the indicated entry.
bool containedBy(const Address &addr,int4 sz) const; ///< Is this entry contained by the given range
int4 justifiedContain(const Address &addr,int4 sz) const; ///< Calculate endian aware containment
@ -94,10 +98,12 @@ public:
AddrSpace *getSpace(void) const { return spaceid; } ///< Get the address space containing \b this entry
uintb getBase(void) const { return addressbase; } ///< Get the starting offset of \b this entry
Address getAddrBySlot(int4 &slot,int4 sz) const;
void restoreXml(const Element *el,const AddrSpaceManager *manage,bool normalstack);
void restoreXml(const Element *el,const AddrSpaceManager *manage,bool normalstack,bool grouped,list<ParamEntry> &curList);
void extraChecks(list<ParamEntry> &entry);
bool isParamCheckHigh(void) const { return ((flags & extracheck_high)!=0); } ///< Return \b true if there is a high overlap
bool isParamCheckLow(void) const { return ((flags & extracheck_low)!=0); } ///< Return \b true if there is a low overlap
int4 countJoinOverlap(const list<ParamEntry> &curList) const; ///< Count the number of other entries \b this overlaps
static void orderWithinGroup(const ParamEntry &entry1,const ParamEntry &entry2); ///< Enforce ParamEntry group ordering rules
};
/// \brief Class for storing ParamEntry objects in an interval range (rangemap)
@ -318,22 +324,22 @@ public:
unknown_effect = 4 ///< An unknown effect (indicates the absence of an EffectRecord)
};
private:
VarnodeData address; ///< The memory range affected
VarnodeData range; ///< The memory range affected
uint4 type; ///< The type of effect
public:
EffectRecord(void) {} ///< Constructor for use with restoreXml()
EffectRecord(const EffectRecord &op2) { address = op2.address; type = op2.type; } ///< Copy constructor
EffectRecord(const EffectRecord &op2) { range = op2.range; type = op2.type; } ///< Copy constructor
EffectRecord(const Address &addr,int4 size); ///< Construct a memory range with an unknown effect
EffectRecord(const ParamEntry &entry,uint4 t); ///< Construct an effect on a parameter storage location
EffectRecord(const VarnodeData &addr,uint4 t); ///< Construct an effect on a memory range
uint4 getType(void) const { return type; } ///< Get the type of effect
Address getAddress(void) const { return Address(address.space,address.offset); } ///< Get the starting address of the affected range
int4 getSize(void) const { return address.size; } ///< Get the size of the affected range
bool operator<(const EffectRecord &op2) const; ///< Comparator for EffectRecords
Address getAddress(void) const { return Address(range.space,range.offset); } ///< Get the starting address of the affected range
int4 getSize(void) const { return range.size; } ///< Get the size of the affected range
bool operator==(const EffectRecord &op2) const; ///< Equality operator
bool operator!=(const EffectRecord &op2) const; ///< Inequality operator
void saveXml(ostream &s) const; ///< Save the record to an XML stream
void restoreXml(uint4 grouptype,const Element *el,const AddrSpaceManager *manage); ///< Restore the record from an XML stream
static bool compareByAddress(const EffectRecord &op1,const EffectRecord &op2);
};
/// A group of ParamEntry objects that form a complete set for passing
@ -501,19 +507,23 @@ protected:
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
int4 nonfloatgroup; ///< Group of first entry which is not marked float
int4 resourceTwoStart; ///< If there are two resource sections, the group of the first entry in the second section
list<ParamEntry> entry; ///< The ordered list of parameter entries
vector<ParamEntryResolver *> resolverMap; ///< Map from space id to resolver
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
void buildTrialMap(ParamActive *active) const; ///< Build map from parameter trials to model ParamEntrys
void separateFloat(ParamActive *active,int4 &floatstart,int4 &floatstop,int4 &start,int4 &stop) const;
void separateSections(ParamActive *active,int4 &oneStart,int4 &oneStop,int4 &twoStart,int4 &twoStop) const;
void forceExclusionGroup(ParamActive *active) const;
void forceNoUse(ParamActive *active,int4 start,int4 stop) const;
void forceInactiveChain(ParamActive *active,int4 maxchain,int4 start,int4 stop) const;
void forceInactiveChain(ParamActive *active,int4 maxchain,int4 start,int4 stop,int4 groupstart) const;
void calcDelay(void); ///< Calculate the maximum heritage delay for any potential parameter in this list
void populateResolver(void); ///< Build the ParamEntry resolver maps
void parsePentry(const Element *el,const AddrSpaceManager *manage,vector<EffectRecord> &effectlist,
int4 groupid,bool normalstack,bool autokill,bool splitFloat,bool grouped);
void parseGroup(const Element *el,const AddrSpaceManager *manage,vector<EffectRecord> &effectlist,
int4 groupid,bool normalstack,bool autokill,bool splitFloat);
public:
ParamListStandard(void) {} ///< Construct for use with restoreXml()
ParamListStandard(const ParamListStandard &op2); ///< Copy constructor
@ -710,9 +720,8 @@ public:
const RangeList &getParamRange(void) const { return paramrange; } ///< Get the range of (possible) stack parameters
vector<EffectRecord>::const_iterator effectBegin(void) const { return effectlist.begin(); } ///< Get an iterator to the first EffectRecord
vector<EffectRecord>::const_iterator effectEnd(void) const { return effectlist.end(); } ///< Get an iterator to the last EffectRecord
int4 numLikelyTrash(void) const { return likelytrash.size(); } ///< Get the number of \e likelytrash locations
const VarnodeData &getLikelyTrash(int4 i) const { return likelytrash[i]; } ///< Get the i-th \e likelytrashh location
vector<VarnodeData>::const_iterator trashBegin(void) const { return likelytrash.begin(); } ///< Get an iterator to the first \e likelytrash
vector<VarnodeData>::const_iterator trashEnd(void) const { return likelytrash.end(); } ///< Get an iterator to the last \e likelytrash
/// \brief Characterize whether the given range overlaps parameter storage
///
/// Does the range naturally fit inside a potential parameter entry from this model or does
@ -841,6 +850,7 @@ public:
virtual bool isMerged(void) const { return false; } ///< Is \b this a merged prototype model
virtual void restoreXml(const Element *el); ///< Restore \b this model from an XML stream
static uint4 lookupEffect(const vector<EffectRecord> &efflist,const Address &addr,int4 size);
static int4 lookupRecord(const vector<EffectRecord> &efflist,int4 listSize,const Address &addr,int4 size);
};
/// \brief Class for calculating "goodness of fit" of parameter trials against a prototype model
@ -1187,6 +1197,10 @@ class FuncProto {
int4 injectid; ///< (If non-negative) id of p-code snippet that should replace this function
int4 returnBytesConsumed; ///< Number of bytes of return value that are consumed by callers (0 = all bytes)
void updateThisPointer(void); ///< Make sure any "this" parameter is properly marked
void saveEffectXml(ostream &s) const; ///< Save any overriding EffectRecords to XML stream
void saveLikelyTrashXml(ostream &s) const; ///< Save any overriding likelytrash registers to XML stream
void restoreEffectXml(void); ///< Merge in any EffectRecord overrides
void restoreLikelyTrashXml(void); ///< Merge in any \e likelytrash overrides
protected:
void paramShift(int4 paramshift); ///< Add parameters to the front of the input parameter list
bool isParamshiftApplied(void) const { return ((flags&paramshift_applied)!=0); } ///< Has a parameter shift been applied
@ -1360,8 +1374,8 @@ public:
uint4 hasEffect(const Address &addr,int4 size) const;
vector<EffectRecord>::const_iterator effectBegin(void) const; ///< Get iterator to front of EffectRecord list
vector<EffectRecord>::const_iterator effectEnd(void) const; ///< Get iterator to end of EffectRecord list
int4 numLikelyTrash(void) const; ///< Get the number of \e likely-trash locations
const VarnodeData &getLikelyTrash(int4 i) const; ///< Get the i-th \e likely-trash location
vector<VarnodeData>::const_iterator trashBegin(void) const; ///< Get iterator to front of \e likelytrash list
vector<VarnodeData>::const_iterator trashEnd(void) const; ///< Get iterator to end of \e likelytrash list
int4 characterizeAsInputParam(const Address &addr,int4 size) const;
bool possibleInputParam(const Address &addr,int4 size) const;
bool possibleOutputParam(const Address &addr,int4 size) const;
@ -1553,28 +1567,30 @@ inline const ParamTrial &ParamActive::getTrialForInputVarnode(int4 slot) const
return trial[slot];
}
/// Sort on the memory range, then the effect type
/// \param op2 is the other record to compare with \b this
/// \brief Compare two EffectRecords by their start Address
///
/// \param op1 is the first record to compare
/// \param op2 is the other record to compare
/// \return \b true if \b this should be ordered before the other record
inline bool EffectRecord::operator<(const EffectRecord &op2) const
inline bool EffectRecord::compareByAddress(const EffectRecord &op1,const EffectRecord &op2)
{
if (address < op2.address) return true;
if (address != op2.address) return false;
return (type < op2.type);
if (op1.range.space != op2.range.space)
return (op1.range.space->getIndex() < op2.range.space->getIndex());
return (op1.range.offset < op2.range.offset);
}
inline bool EffectRecord::operator==(const EffectRecord &op2) const
{
if (address != op2.address) return false;
if (range != op2.range) return false;
return (type == op2.type);
}
inline bool EffectRecord::operator!=(const EffectRecord &op2) const
{
if (address != op2.address) return true;
if (range != op2.range) return true;
return (type != op2.type);
}

View File

@ -98,7 +98,7 @@ void StringManager::saveXml(ostream &s) const
s << " <bytes";
a_v_b(s, "trunc", stringData.isTruncated);
s << ">\n" << setfill('0');
for(int4 i=0;stringData.byteData.size();++i) {
for(int4 i=0;i<stringData.byteData.size();++i) {
s << hex << setw(2) << (int4)stringData.byteData[i];
if (i%20 == 19)
s << "\n ";

View File

@ -296,6 +296,14 @@
<ref name="addr_tags_type"/>
</define>
<define name="pentry_group_type">
<oneOrMore>
<element name="pentry">
<ref name="pentry_type"/>
</element>
</oneOrMore>
</define>
<define name="rangelist_type">
<oneOrMore>
<ref name="range_type"/>
@ -359,11 +367,19 @@
<optional> <attribute name="pointermax"/> </optional>
<optional> <attribute name="thisbeforeretpointer"/> </optional>
<optional> <attribute name="killedbycall"/> </optional>
<zeroOrMore>
<element name="pentry">
<ref name="pentry_type"/>
</element>
</zeroOrMore>
<optional> <attribute name="separatefloat"/> </optional>
<interleave>
<zeroOrMore>
<element name="pentry">
<ref name="pentry_type"/>
</element>
</zeroOrMore>
<zeroOrMore>
<element name="group">
<ref name="pentry_group_type"/>
</element>
</zeroOrMore>
</interleave>
</element>
<element name="output">

View File

@ -15,7 +15,7 @@
*/
package ghidra.program.model.lang;
import java.util.Iterator;
import java.util.*;
import java.util.Map.Entry;
import ghidra.app.plugin.processors.sleigh.VarnodeData;
@ -36,6 +36,9 @@ public class ParamEntry {
private static final int IS_BIG_ENDIAN = 16; // Interpret values in this container as big endian
private static final int SMALLSIZE_INTTYPE = 32; // Assume values that are below max size are extended based on integer type
private static final int SMALLSIZE_FLOAT = 64; // Assume values smaller than max -size- are floating-point extended to full size
//private static final int EXTRACHECK_HIGH = 128;
//private static final int EXTRACHECK_LOW = 256;
private static final int IS_GROUPED = 512; // The entry is grouped with other entries
public static final int TYPE_UNKNOWN = 8; // Default type restriction
public static final int TYPE_PTR = 2; // pointer types
@ -93,6 +96,10 @@ public class ParamEntry {
return ((flags & REVERSE_STACK) != 0);
}
public boolean isGrouped() {
return ((flags & IS_GROUPED) != 0);
}
public boolean isBigEndian() {
return ((flags & IS_BIG_ENDIAN) != 0);
}
@ -105,6 +112,16 @@ public class ParamEntry {
return (((flags & IS_BIG_ENDIAN) == 0) || ((flags & FORCE_LEFT_JUSTIFY) != 0));
}
/**
* @return true if at least one piece of a join doesn't overlap with another ParamEntry
*/
public boolean isNonOverlappingJoin() {
if (joinrec == null) {
return false;
}
return (joinrec.length != groupsize);
}
public AddressSpace getSpace() {
return spaceid;
}
@ -264,6 +281,71 @@ public class ParamEntry {
return slotnum;
}
/**
* Find the ParamEntry in the list whose storage matches the given Varnode
* @param curList is the list of ParamEntry
* @param varnode is the given Varnode
* @return the matching entry or null
*/
private static ParamEntry findEntryByStorage(List<ParamEntry> curList, Varnode varnode) {
ListIterator<ParamEntry> iter = curList.listIterator(curList.size());
while (iter.hasPrevious()) {
ParamEntry entry = iter.previous();
if (entry.spaceid.getSpaceID() == varnode.getSpace() &&
entry.addressbase == varnode.getOffset() && entry.size == varnode.getSize()) {
return entry;
}
}
return null;
}
public int countJoinOverlap(List<ParamEntry> curList) {
if (joinrec == null) {
return 0;
}
int count = 0;
for (Varnode vn : joinrec) {
ParamEntry match = findEntryByStorage(curList, vn);
if (match != null) {
count += 1;
}
}
return count;
}
/**
* Adjust the group and groupsize based on the ParamEntrys being overlapped
* @param curList is the current list of ParamEntry
* @throws XmlParseException if no overlap is found
*/
private void resolveJoin(List<ParamEntry> curList) throws XmlParseException {
if (joinrec == null) {
return;
}
int mingrp = 1000;
int maxgrp = -1;
for (Varnode piece : joinrec) {
ParamEntry entry = findEntryByStorage(curList, piece);
if (entry != null) {
if (entry.group < mingrp) {
mingrp = entry.group;
}
int max = entry.group + entry.groupsize;
if (max > maxgrp) {
maxgrp = max;
}
}
}
if (maxgrp < 0 || mingrp >= 1000) {
throw new XmlParseException("<pentry> join must overlap at least one previous entry");
}
group = mingrp;
groupsize = (maxgrp - mingrp);
if (groupsize > joinrec.length) {
throw new XmlParseException("<pentry> join must overlap sequential entries");
}
}
public void saveXml(StringBuilder buffer) {
buffer.append("<pentry");
SpecXmlUtils.encodeSignedIntegerAttribute(buffer, "minsize", minsize);
@ -275,9 +357,6 @@ public class ParamEntry {
String tok = (type == TYPE_FLOAT) ? "float" : "ptr";
SpecXmlUtils.encodeStringAttribute(buffer, "metatype", tok);
}
if (groupsize != 1) {
SpecXmlUtils.encodeSignedIntegerAttribute(buffer, "groupsize", groupsize);
}
String extString = null;
if ((flags & SMALLSIZE_SEXT) != 0) {
extString = "sign";
@ -307,7 +386,8 @@ public class ParamEntry {
buffer.append("</pentry>");
}
public void restoreXml(XmlPullParser parser, CompilerSpec cspec) throws XmlParseException {
public void restoreXml(XmlPullParser parser, CompilerSpec cspec, List<ParamEntry> curList,
boolean grouped) throws XmlParseException {
flags = 0;
type = TYPE_UNKNOWN;
size = minsize = -1; // Must be filled in
@ -344,12 +424,6 @@ public class ParamEntry {
}
}
}
else if (name.equals("group")) {
group = SpecXmlUtils.decodeInt(entry.getValue());
}
else if (name.equals("groupsize")) {
groupsize = SpecXmlUtils.decodeInt(entry.getValue());
}
else if (name.equals("extension")) {
flags &= ~(SMALLSIZE_ZEXT | SMALLSIZE_SEXT | SMALLSIZE_INTTYPE | SMALLSIZE_FLOAT);
String value = entry.getValue();
@ -413,7 +487,10 @@ public class ParamEntry {
}
}
}
// resolveJoin
if (grouped) {
flags |= IS_GROUPED;
}
resolveJoin(curList);
parser.end(el);
}
@ -518,4 +595,26 @@ public class ParamEntry {
}
return TYPE_UNKNOWN;
}
/**
* ParamEntry within a group must be distinguishable by size or by type
* @param entry1 is the first being compared
* @param entry2 is the second being compared
* @throws XmlParseException if the pair is not distinguishable
*/
public static void orderWithinGroup(ParamEntry entry1, ParamEntry entry2)
throws XmlParseException {
if (entry2.minsize > entry1.size || entry1.minsize > entry2.size) {
return;
}
if (entry1.type != entry2.type) {
if (entry1.type == TYPE_UNKNOWN) {
throw new XmlParseException(
"<pentry> tags with a specific type must come before the general type");
}
return;
}
throw new XmlParseException(
"<pentry> tags within a group must be distinguished by size or type");
}
}

View File

@ -38,6 +38,7 @@ public class ParamListStandard implements ParamList {
// protected int maxdelay;
protected int pointermax; // If non-zero, maximum size of a datatype before converting to a pointer
protected boolean thisbeforeret; // Do hidden return pointers usurp the storage of the this pointer
protected int resourceTwoStart; // Group id starting the section resource section (or 0 if only one section)
protected ParamEntry[] entry;
protected AddressSpace spacebase; // Space containing relative offset parameters
@ -213,22 +214,89 @@ public class ParamListStandard implements ParamList {
if (thisbeforeret) {
SpecXmlUtils.encodeStringAttribute(buffer, "thisbeforeretpointer", "yes");
}
if (isInput && resourceTwoStart == 0) {
SpecXmlUtils.encodeBooleanAttribute(buffer, "separatefloat", false);
}
buffer.append(">\n");
int curgroup = -1;
for (ParamEntry el : entry) {
if (curgroup >= 0) {
if (!el.isGrouped() || el.getGroup() != curgroup) {
buffer.append("</group>\n");
curgroup = -1;
}
}
if (el.isGrouped()) {
if (curgroup < 0) {
buffer.append("<group>\n");
curgroup = el.getGroup();
}
}
el.saveXml(buffer);
buffer.append('\n');
}
if (curgroup >= 0) {
buffer.append("</group>\n");
}
buffer.append(isInput ? "</input>" : "</output>");
}
private void parsePentry(XmlPullParser parser, CompilerSpec cspec, ArrayList<ParamEntry> pe,
int groupid, boolean splitFloat, boolean grouped) throws XmlParseException {
ParamEntry pentry = new ParamEntry(groupid);
pe.add(pentry);
pentry.restoreXml(parser, cspec, pe, grouped);
if (splitFloat) {
if (pentry.getType() == ParamEntry.TYPE_FLOAT) {
if (resourceTwoStart >= 0) {
throw new XmlParseException(
"parameter list floating-point entries must come first");
}
}
else if (resourceTwoStart < 0) {
resourceTwoStart = groupid; // First time we have seen an integer slot
}
}
if (pentry.getSpace().isStackSpace()) {
spacebase = pentry.getSpace();
}
int maxgroup = pentry.getGroup() + pentry.getGroupSize();
if (maxgroup > numgroup) {
numgroup = maxgroup;
}
}
private void parseGroup(XmlPullParser parser, CompilerSpec cspec, ArrayList<ParamEntry> pe,
int groupid, boolean splitFloat) throws XmlParseException {
XmlElement el = parser.start("group");
int basegroup = numgroup;
int count = 0;
while (parser.peek().isStart()) {
parsePentry(parser, cspec, pe, basegroup, splitFloat, true);
count += 1;
ParamEntry lastEntry = pe.get(pe.size() - 1);
if (lastEntry.getSpace().getType() == AddressSpace.TYPE_JOIN) {
throw new XmlParseException(
"<pentry> in the join space not allowed in <group> tag");
}
if (count > 1) {
ParamEntry.orderWithinGroup(pe.get(pe.size() - 2), lastEntry);
if (count > 2) {
ParamEntry.orderWithinGroup(pe.get(pe.size() - 3), lastEntry);
}
}
}
parser.end(el);
}
@Override
public void restoreXml(XmlPullParser parser, CompilerSpec cspec) throws XmlParseException {
ArrayList<ParamEntry> pe = new ArrayList<>();
int lastgroup = -1;
numgroup = 0;
spacebase = null;
pointermax = 0;
thisbeforeret = false;
boolean splitFloat = true;
XmlElement mainel = parser.start();
String attribute = mainel.getAttribute("pointermax");
if (attribute != null) {
@ -238,35 +306,30 @@ public class ParamListStandard implements ParamList {
if (attribute != null) {
thisbeforeret = SpecXmlUtils.decodeBoolean(attribute);
}
boolean seennonfloat = false; // Have we seen any integer slots yet
attribute = mainel.getAttribute("separatefloat");
if (attribute != null) {
splitFloat = SpecXmlUtils.decodeBoolean(attribute);
}
resourceTwoStart = splitFloat ? -1 : 0;
for (;;) {
XmlElement el = parser.peek();
if (!el.isStart()) {
break;
}
ParamEntry pentry = new ParamEntry(numgroup);
pentry.restoreXml(parser, cspec);
pe.add(pentry);
if (pentry.getType() == ParamEntry.TYPE_FLOAT) {
if (seennonfloat) {
throw new XmlParseException(
"parameter list floating-point entries must come first");
if (el.getName().equals("pentry")) {
parsePentry(parser, cspec, pe, numgroup, splitFloat, false);
}
else if (el.getName().equals("group")) {
parseGroup(parser, cspec, pe, numgroup, splitFloat);
}
}
// Check that any pentry tags with join storage don't overlap following tags
for (ParamEntry curEntry : pe) {
if (curEntry.isNonOverlappingJoin()) {
if (curEntry.countJoinOverlap(pe) != 1) {
throw new XmlParseException("pentry tag must be listed after all its overlaps");
}
}
else {
seennonfloat = true;
}
if (pentry.getSpace().isStackSpace()) {
spacebase = pentry.getSpace();
}
int maxgroup = pentry.getGroup() + pentry.getGroupSize();
if (maxgroup > numgroup) {
numgroup = maxgroup;
}
if (pentry.getGroup() < lastgroup) {
throw new XmlParseException("pentrys must come in group order");
}
lastgroup = pentry.getGroup();
}
parser.end(mainel);
entry = new ParamEntry[pe.size()];

View File

@ -15,12 +15,14 @@
*/
package ghidra.program.model.lang;
import java.util.ArrayList;
import ghidra.program.model.data.DataType;
import ghidra.program.model.listing.Program;
import ghidra.program.model.listing.VariableStorage;
import ghidra.util.exception.InvalidInputException;
import java.util.ArrayList;
import ghidra.xml.XmlParseException;
import ghidra.xml.XmlPullParser;
public class ParamListStandardOut extends ParamListStandard {
@ -29,8 +31,9 @@ public class ParamListStandardOut extends ParamListStandard {
ArrayList<VariableStorage> res, boolean addAutoParams) {
int[] status = new int[numgroup];
for (int i = 0; i < numgroup; ++i)
for (int i = 0; i < numgroup; ++i) {
status[i] = 0;
}
VariableStorage store = assignAddress(prog, proto[0], status, false, false);
if (!store.isUnassignedStorage()) {
@ -57,4 +60,16 @@ public class ParamListStandardOut extends ParamListStandard {
}
@Override
public void restoreXml(XmlPullParser parser, CompilerSpec cspec) throws XmlParseException {
super.restoreXml(parser, cspec);
// ParamEntry tags in the output list are considered a group
for (int i = 1; i < entry.length; ++i) {
ParamEntry.orderWithinGroup(entry[i - 1], entry[i]);
if (i > 1) {
ParamEntry.orderWithinGroup(entry[i - 2], entry[i]);
}
}
}
}