#include "fspec.hh"
#include "funcdata.hh"
const ParamEntry *ParamEntry::findEntryByStorage(const list<ParamEntry> &entryList,const VarnodeData &vn)
{
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;
}
void ParamEntry::resolveJoin(list<ParamEntry> &curList)
{
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");
}
bool ParamEntry::isNonOverlappingJoin(void) const
{
if (joinrec == (JoinRecord *)0)
return false;
return (joinrec->numPieces() != groupsize);
}
bool ParamEntry::contains(const ParamEntry &op2) const
{
if ((type!=TYPE_UNKNOWN)&&(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;
if (alignment != op2.alignment) return false;
return true;
}
bool ParamEntry::containedBy(const Address &addr,int4 sz) const
{
if (spaceid != addr.getSpace()) return false;
if (addressbase < addr.getOffset()) return false;
uintb entryoff = addressbase + size-1;
uintb rangeoff = addr.getOffset() + sz-1;
return (entryoff <= rangeoff);
}
int4 ParamEntry::justifiedContain(const Address &addr,int4 sz) const
{
if (joinrec != (JoinRecord *)0) {
int4 res = 0;
for(int4 i=joinrec->numPieces()-1;i>=0;--i) { const VarnodeData &vdata(joinrec->getPiece(i));
int4 cur = vdata.getAddr().justifiedContain(vdata.size,addr,sz,false);
if (cur<0)
res += vdata.size; else {
return res + cur;
}
}
return -1; }
if (alignment==0) {
Address entry(spaceid,addressbase);
return entry.justifiedContain(size,addr,sz,((flags&force_left_justify)!=0));
}
if (spaceid != addr.getSpace()) return -1;
uintb startaddr = addr.getOffset();
if (startaddr < addressbase) return -1;
uintb endaddr = startaddr + sz - 1;
if (endaddr < startaddr) return -1; if (endaddr > (addressbase+size-1)) return -1;
startaddr -= addressbase;
endaddr -= addressbase;
if (!isLeftJustified()) { int4 res = (int4)((endaddr+1) % alignment);
if (res==0) return 0;
return (alignment-res);
}
return (int4)(startaddr % alignment);
}
bool ParamEntry::getContainer(const Address &addr,int4 sz,VarnodeData &res) const
{
Address endaddr = addr + (sz-1);
if (joinrec != (JoinRecord *)0) {
for(int4 i=joinrec->numPieces()-1;i>=0;--i) { const VarnodeData &vdata(joinrec->getPiece(i));
if ((addr.overlap(0,vdata.getAddr(),vdata.size) >=0)&&
(endaddr.overlap(0,vdata.getAddr(),vdata.size)>=0)) {
res = vdata;
return true;
}
}
return false; }
Address entry(spaceid,addressbase);
if (addr.overlap(0,entry,size)<0) return false;
if (endaddr.overlap(0,entry,size)<0) return false;
if (alignment==0) {
res.space = spaceid;
res.offset = addressbase;
res.size = size;
return true;
}
uintb al = (addr.getOffset() - addressbase) % alignment;
res.space = spaceid;
res.offset = addr.getOffset() - al;
res.size = (int4)(endaddr.getOffset()-res.offset) + 1;
int4 al2 = res.size % alignment;
if (al2 != 0)
res.size += (alignment - al2); return true;
}
OpCode ParamEntry::assumedExtension(const Address &addr,int4 sz,VarnodeData &res) const
{
if ((flags & (smallsize_zext|smallsize_sext|smallsize_inttype))==0) return CPUI_COPY;
if (alignment != 0) {
if (sz >= alignment)
return CPUI_COPY;
}
else if (sz >= size)
return CPUI_COPY;
if (joinrec != (JoinRecord *)0) return CPUI_COPY;
if (justifiedContain(addr,sz)!=0) return CPUI_COPY; if (alignment == 0) { res.space = spaceid;
res.offset = addressbase;
res.size = size;
}
else { res.space = spaceid;
int4 alignAdjust = (addr.getOffset() - addressbase) % alignment;
res.offset = addr.getOffset() - alignAdjust;
res.size = alignment;
}
if ((flags & smallsize_zext)!=0)
return CPUI_INT_ZEXT;
if ((flags & smallsize_inttype)!=0)
return CPUI_PIECE;
return CPUI_INT_SEXT;
}
int4 ParamEntry::getSlot(const Address &addr,int4 skip) const
{
int4 res = group;
if (alignment != 0) {
uintb diff = addr.getOffset() + skip - addressbase;
int4 baseslot = (int4)diff / alignment;
if (isReverseStack())
res += (numslots -1) - baseslot;
else
res += baseslot;
}
else if (skip != 0) {
res += (groupsize-1);
}
return res;
}
Address ParamEntry::getAddrBySlot(int4 &slotnum,int4 sz) const
{
Address res; int4 spaceused;
if (sz < minsize) return res;
if (alignment == 0) { if (slotnum != 0) return res; if (sz > size) return res; res = Address(spaceid,addressbase); spaceused = size;
if (((flags & smallsize_floatext)!=0)&&(sz != size)) { AddrSpaceManager *manager = spaceid->getManager();
res = manager->constructFloatExtensionAddress(res,size,sz);
return res;
}
}
else {
int4 slotsused = sz / alignment; if ( (sz % alignment) != 0)
slotsused += 1;
if (slotnum + slotsused > numslots) return res;
spaceused = slotsused * alignment;
int4 index;
if (isReverseStack()) {
index = numslots;
index -= slotnum;
index -= slotsused;
}
else
index = slotnum;
res = Address(spaceid, addressbase + index * alignment);
slotnum += slotsused; }
if (!isLeftJustified()) res = res + (spaceused - sz);
return res;
}
void ParamEntry::restoreXml(const Element *el,const AddrSpaceManager *manage,bool normalstack,bool grouped,list<ParamEntry> &curList)
{
flags = 0;
type = TYPE_UNKNOWN;
size = minsize = -1; alignment = 0; numslots = 1;
groupsize = 1; int4 num = el->getNumAttributes();
for(int4 i=0;i<num;++i) {
const string &attrname( el->getAttributeName(i) );
if (attrname=="minsize") {
istringstream i1(el->getAttributeValue(i));
i1.unsetf(ios::dec | ios::hex | ios::oct);
i1 >> minsize;
}
else if (attrname == "size") { istringstream i2(el->getAttributeValue(i));
i2.unsetf(ios::dec | ios::hex | ios::oct);
i2 >> alignment;
}
else if (attrname == "align") { istringstream i4(el->getAttributeValue(i));
i4.unsetf(ios::dec | ios::hex | ios::oct);
i4 >> alignment;
}
else if (attrname == "maxsize") {
istringstream i3(el->getAttributeValue(i));
i3.unsetf(ios::dec | ios::hex | ios::oct);
i3 >> size;
}
else if (attrname == "metatype")
type = string2metatype(el->getAttributeValue(i));
else if (attrname == "extension") {
flags &= ~((uint4)(smallsize_zext | smallsize_sext | smallsize_inttype));
if (el->getAttributeValue(i) == "sign")
flags |= smallsize_sext;
else if (el->getAttributeValue(i) == "zero")
flags |= smallsize_zext;
else if (el->getAttributeValue(i) == "inttype")
flags |= smallsize_inttype;
else if (el->getAttributeValue(i) == "float")
flags |= smallsize_floatext;
else if (el->getAttributeValue(i) != "none")
throw LowlevelError("Bad extension attribute");
}
else
throw LowlevelError("Unknown ParamEntry attribute: "+attrname);
}
if ((size==-1)||(minsize==-1))
throw LowlevelError("ParamEntry not fully specified");
if (alignment == size)
alignment = 0;
Address addr;
addr = Address::restoreXml( *el->getChildren().begin(),manage);
spaceid = addr.getSpace();
addressbase = addr.getOffset();
if (alignment != 0) {
numslots = size / alignment;
}
if (spaceid->isReverseJustified()) {
if (spaceid->isBigEndian())
flags |= force_left_justify;
else
throw LowlevelError("No support for right justification in little endian encoding");
}
if (!normalstack) {
flags |= reverse_stack;
if (alignment != 0) {
if ((size % alignment) != 0)
throw LowlevelError("For positive stack growth, <pentry> size must match alignment");
}
}
if (grouped)
flags |= is_grouped;
resolveJoin(curList);
}
void ParamEntry::extraChecks(list<ParamEntry> &entry)
{
if (joinrec == (JoinRecord *)0) return; if (joinrec->numPieces() != 2) return;
const VarnodeData &highPiece(joinrec->getPiece(0));
bool seenOnce = false;
list<ParamEntry>::const_iterator iter;
for(iter=entry.begin();iter!=entry.end();++iter) { AddrSpace *spc = (*iter).getSpace();
uintb off = (*iter).getBase();
int4 sz = (*iter).getSize();
if ((highPiece.offset == off)&&(highPiece.space == spc)&&(highPiece.size == sz)) {
if (seenOnce) throw LowlevelError("Extra check hits twice");
seenOnce = true;
flags |= extracheck_low; }
}
if (!seenOnce)
flags |= extracheck_high; }
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;
}
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)
{
numgroup = op2.numgroup;
entry = op2.entry;
spacebase = op2.spacebase;
maxdelay = op2.maxdelay;
pointermax = op2.pointermax;
thisbeforeret = op2.thisbeforeret;
resourceTwoStart = op2.resourceTwoStart;
populateResolver();
}
ParamListStandard::~ParamListStandard(void)
{
for(int4 i=0;i<resolverMap.size();++i) {
ParamEntryResolver *resolver = resolverMap[i];
if (resolver != (ParamEntryResolver *)0)
delete resolver;
}
}
const ParamEntry *ParamListStandard::findEntry(const Address &loc,int4 size) const
{
int4 index = loc.getSpace()->getIndex();
if (index >= resolverMap.size())
return (const ParamEntry *)0;
ParamEntryResolver *resolver = resolverMap[index];
if (resolver == (ParamEntryResolver *)0)
return (const ParamEntry *)0;
pair<ParamEntryResolver::const_iterator,ParamEntryResolver::const_iterator> res;
res = resolver->find(loc.getOffset());
while(res.first != res.second) {
const ParamEntry *testEntry = (*res.first).getParamEntry();
++res.first;
if (testEntry->getMinSize() > size) continue;
if (testEntry->justifiedContain(loc,size)==0) return testEntry;
}
return (const ParamEntry *)0;
}
int4 ParamListStandard::characterizeAsParam(const Address &loc,int4 size) const
{
int4 index = loc.getSpace()->getIndex();
if (index >= resolverMap.size())
return 0;
ParamEntryResolver *resolver = resolverMap[index];
if (resolver == (ParamEntryResolver *)0)
return 0;
pair<ParamEntryResolver::const_iterator,ParamEntryResolver::const_iterator> iterpair;
iterpair = resolver->find(loc.getOffset());
int4 res = 0;
while(iterpair.first != iterpair.second) {
const ParamEntry *testEntry = (*iterpair.first).getParamEntry();
if (testEntry->getMinSize() <= size && testEntry->justifiedContain(loc, size)==0)
return 1;
if (testEntry->isExclusion() && testEntry->containedBy(loc, size))
res = 2;
++iterpair.first;
}
if (res != 2 && iterpair.first != resolver->end()) {
iterpair.second = resolver->find_end(loc.getOffset() + (size-1));
while(iterpair.first != iterpair.second) {
const ParamEntry *testEntry = (*iterpair.first).getParamEntry();
if (testEntry->isExclusion() && testEntry->containedBy(loc, size)) {
res = 2;
break;
}
++iterpair.first;
}
}
return res;
}
Address ParamListStandard::assignAddress(const Datatype *tp,vector<int4> &status) 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;
Address res = curEntry.getAddrBySlot(status[grp],tp->getSize());
if (res.isInvalid()) continue; if (curEntry.isExclusion()) {
int4 maxgrp = grp + curEntry.getGroupSize();
for(int4 j=grp;j<maxgrp;++j) status[j] = -1; }
return res;
}
return Address(); }
void ParamListStandard::assignMap(const vector<Datatype *> &proto,TypeFactory &typefactory,vector<ParameterPieces> &res) const
{
vector<int4> status(numgroup,0);
if (res.size() == 2) { res.back().addr = assignAddress(res.back().type,status); res.back().flags |= ParameterPieces::hiddenretparm;
if (res.back().addr.isInvalid())
throw ParamUnassignedError("Cannot assign parameter address for " + res.back().type->getName());
}
for(int4 i=1;i<proto.size();++i) {
res.emplace_back();
if ((pointermax != 0) && (proto[i]->getSize() > pointermax)) { 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);
if (res.back().addr.isInvalid())
throw ParamUnassignedError("Cannot assign parameter address for " + proto[i]->getName());
res.back().type = proto[i];
res.back().flags = 0;
}
}
void ParamListStandard::buildTrialMap(ParamActive *active) const
{
vector<const ParamEntry *> hitlist; bool seenfloattrial = false;
bool seeninttrial = false;
for(int4 i=0;i<active->getNumTrials();++i) {
ParamTrial ¶mtrial(active->getTrial(i));
const ParamEntry *entrySlot = findEntry(paramtrial.getAddress(),paramtrial.getSize());
if (entrySlot == (const ParamEntry *)0)
paramtrial.markNoUse();
else {
paramtrial.setEntry( entrySlot, 0 );
if (entrySlot->getType() == TYPE_FLOAT)
seenfloattrial = true;
else
seeninttrial = true;
int4 grp = entrySlot->getGroup();
while(hitlist.size() <= grp)
hitlist.push_back((const ParamEntry *)0);
const ParamEntry *lastentry = hitlist[grp];
if (lastentry == (const ParamEntry *)0)
hitlist[grp] = entrySlot; }
}
for(int4 i=0;i<hitlist.size();++i) {
const ParamEntry *curentry = hitlist[i];
if (curentry == (const ParamEntry *)0) {
list<ParamEntry>::const_iterator iter;
for(iter=entry.begin();iter!=entry.end();++iter) {
curentry = &(*iter);
if (curentry->getGroup() == i) break; }
if ((!seenfloattrial)&&(curentry->getType()==TYPE_FLOAT))
continue; if ((!seeninttrial)&&(curentry->getType()!=TYPE_FLOAT))
continue; int4 sz = curentry->isExclusion() ? curentry->getSize() : curentry->getAlign();
int4 nextslot = 0;
Address addr = curentry->getAddrBySlot(nextslot,sz);
int4 trialpos = active->getNumTrials();
active->registerTrial(addr,sz);
ParamTrial ¶mtrial(active->getTrial(trialpos));
paramtrial.markUnref();
paramtrial.setEntry(curentry,0);
}
else if (!curentry->isExclusion()) {
vector<int4> slotlist;
for(int4 j=0;j<active->getNumTrials();++j) {
ParamTrial ¶mtrial(active->getTrial(j));
if (paramtrial.getEntry() != curentry) continue;
int4 slot = curentry->getSlot(paramtrial.getAddress(),0) - curentry->getGroup();
int4 endslot = curentry->getSlot(paramtrial.getAddress(),paramtrial.getSize()-1) - curentry->getGroup();
if (endslot < slot) { int4 tmp = slot;
slot = endslot;
endslot = tmp;
}
while(slotlist.size() <= endslot)
slotlist.push_back(0);
while(slot<=endslot) {
slotlist[slot] = 1;
slot += 1;
}
}
for(int4 j=0;j<slotlist.size();++j) {
if (slotlist[j] == 0) {
int4 nextslot = j; Address addr = curentry->getAddrBySlot(nextslot,curentry->getAlign());
int4 trialpos = active->getNumTrials();
active->registerTrial(addr,curentry->getAlign());
ParamTrial ¶mtrial(active->getTrial(trialpos));
paramtrial.markUnref();
paramtrial.setEntry(curentry,0);
}
}
}
}
active->sortTrials();
}
void ParamListStandard::separateSections(ParamActive *active,int4 &oneStart,int4 &oneStop,int4 &twoStart,int4 &twoStop) const
{
int4 numtrials = active->getNumTrials();
if (resourceTwoStart == 0) {
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()->getGroup() >= resourceTwoStart) break;
}
oneStart = 0;
oneStop = i;
twoStart = i;
twoStop = numtrials;
}
void ParamListStandard::forceExclusionGroup(ParamActive *active) const
{
int4 curupper = -1;
bool exclusion = false;
int4 numtrials = active->getNumTrials();
for(int4 i=0;i<numtrials;++i) {
ParamTrial &curtrial(active->getTrial(i));
if (curtrial.isActive()) {
int4 grp = curtrial.getEntry()->getGroup();
exclusion = curtrial.getEntry()->isExclusion();
if (grp <= curupper) { if (exclusion)
curtrial.markInactive(); }
else
curupper = grp + curtrial.getEntry()->getGroupSize() - 1; }
}
}
void ParamListStandard::forceNoUse(ParamActive *active, int4 start, int4 stop) const
{
bool seendefnouse = false;
int4 curgroup = -1;
bool exclusion = false;
bool alldefnouse = false;
for (int4 i = start; i < stop; ++i) {
ParamTrial &curtrial(active->getTrial(i));
if (curtrial.getEntry() == (const ParamEntry *) 0)
continue; int4 grp = curtrial.getEntry()->getGroup();
exclusion = curtrial.getEntry()->isExclusion();
if ((grp <= curgroup) && exclusion) { if (!curtrial.isDefinitelyNotUsed()) alldefnouse = false; }
else { if (alldefnouse) seendefnouse = true; alldefnouse = curtrial.isDefinitelyNotUsed();
curgroup = grp + curtrial.getEntry()->getGroupSize() - 1;
}
if (seendefnouse)
curtrial.markInactive();
}
}
void ParamListStandard::forceInactiveChain(ParamActive *active,int4 maxchain,int4 start,int4 stop,int4 groupstart) const
{
bool seenchain = false;
int4 chainlength = 0;
int4 max = -1;
for(int4 i=start;i<stop;++i) {
ParamTrial &trial(active->getTrial(i));
if (trial.getEntry() == (const ParamEntry *)0) continue; if (!trial.isActive()) {
if (trial.isUnref()&&active->isRecoverSubcall()) {
if (trial.getAddress().getSpace()->getType() == IPTR_SPACEBASE) seenchain = true; }
if (i==start) {
chainlength += (trial.slotGroup() - groupstart + 1);
}
else
chainlength += trial.slotGroup() - active->getTrial(i-1).slotGroup();
if (chainlength > maxchain)
seenchain = true;
}
else {
chainlength = 0;
if (!seenchain)
max = i;
}
if (seenchain)
trial.markInactive();
}
for(int4 i=start;i<=max;++i) { ParamTrial &trial(active->getTrial(i));
if (trial.isDefinitelyNotUsed()) continue;
if (!trial.isActive())
trial.markActive();
}
}
void ParamListStandard::calcDelay(void)
{
maxdelay = 0;
list<ParamEntry>::const_iterator iter;
for(iter=entry.begin();iter!=entry.end();++iter) {
int4 delay = (*iter).getSpace()->getDelay();
if (delay > maxdelay)
maxdelay = delay;
}
}
void ParamListStandard::populateResolver(void)
{
int4 maxid = -1;
list<ParamEntry>::iterator iter;
for(iter=entry.begin();iter!=entry.end();++iter) {
int4 id = (*iter).getSpace()->getIndex();
if (id > maxid)
maxid = id;
}
resolverMap.resize(maxid+1, (ParamEntryResolver *)0);
int4 position = 0;
for(iter=entry.begin();iter!=entry.end();++iter) {
ParamEntry *paramEntry = &(*iter);
int4 spaceId = paramEntry->getSpace()->getIndex();
ParamEntryResolver *resolver = resolverMap[spaceId];
if (resolver == (ParamEntryResolver *)0) {
resolver = new ParamEntryResolver();
resolverMap[spaceId] = resolver;
}
uintb first = paramEntry->getBase();
uintb last = first + (paramEntry->getSize() - 1);
ParamEntryResolver::inittype initData(position,paramEntry);
position += 1;
resolver->insert(initData,first,last);
}
}
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; }
AddrSpace *spc = entry.back().getSpace();
if (spc->getType() == IPTR_SPACEBASE)
spacebase = spc;
else if (autokill) effectlist.push_back(EffectRecord(entry.back(),EffectRecord::killedbycall));
int4 maxgroup = entry.back().getGroup() + entry.back().getGroupSize();
if (maxgroup > numgroup)
numgroup = maxgroup;
}
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
{
if (active->getNumTrials() == 0) return;
buildTrialMap(active);
forceExclusionGroup(active);
int4 oneStart,oneStop,twoStart,twoStop;
separateSections(active,oneStart,oneStop,twoStart,twoStop);
forceNoUse(active,oneStart,oneStop);
forceNoUse(active,twoStart,twoStop); forceInactiveChain(active,2,oneStart,oneStop,0); forceInactiveChain(active,2,twoStart,twoStop,resourceTwoStart);
for(int4 i=0;i<active->getNumTrials();++i) {
ParamTrial ¶mtrial(active->getTrial(i));
if (paramtrial.isActive())
paramtrial.markUsed();
}
}
bool ParamListStandard::checkJoin(const Address &hiaddr,int4 hisize,const Address &loaddr,int4 losize) const
{
const ParamEntry *entryHi = findEntry(hiaddr,hisize);
if (entryHi == (const ParamEntry *)0) return false;
const ParamEntry *entryLo = findEntry(loaddr,losize);
if (entryLo == (const ParamEntry *)0) return false;
if (entryHi->getGroup() == entryLo->getGroup()) {
if (entryHi->isExclusion()||entryLo->isExclusion()) return false;
if (!hiaddr.isContiguous(hisize,loaddr,losize)) return false;
if (((hiaddr.getOffset() - entryHi->getBase()) % entryHi->getAlign()) != 0) return false;
if (((loaddr.getOffset() - entryLo->getBase()) % entryLo->getAlign()) != 0) return false;
return true;
}
else {
int4 sizesum = hisize + losize;
list<ParamEntry>::const_iterator iter;
for(iter=entry.begin();iter!=entry.end();++iter) {
if ((*iter).getSize() < sizesum) continue;
if ((*iter).justifiedContain(loaddr,losize)!=0) continue;
if ((*iter).justifiedContain(hiaddr,hisize)!=losize) continue;
return true;
}
}
return false;
}
bool ParamListStandard::checkSplit(const Address &loc,int4 size,int4 splitpoint) const
{
Address loc2 = loc + splitpoint;
int4 size2 = size - splitpoint;
const ParamEntry *entryNum = findEntry(loc,splitpoint);
if (entryNum == (const ParamEntry *)0) return false;
entryNum = findEntry(loc2,size2);
if (entryNum == (const ParamEntry *)0) return false;
return true;
}
bool ParamListStandard::possibleParam(const Address &loc,int4 size) const
{
return ((const ParamEntry *)0 != findEntry(loc,size));
}
bool ParamListStandard::possibleParamWithSlot(const Address &loc,int4 size,int4 &slot,int4 &slotsize) const
{
const ParamEntry *entryNum = findEntry(loc,size);
if (entryNum == (const ParamEntry *)0) return false;
slot = entryNum->getSlot(loc,0);
if (entryNum->isExclusion()) {
slotsize = entryNum->getGroupSize();
}
else {
slotsize = ((size-1) / entryNum->getAlign()) + 1;
}
return true;
}
bool ParamListStandard::getBiggestContainedParam(const Address &loc,int4 size,VarnodeData &res) const
{
int4 index = loc.getSpace()->getIndex();
if (index >= resolverMap.size())
return false;
ParamEntryResolver *resolver = resolverMap[index];
if (resolver == (ParamEntryResolver *)0)
return false;
Address endLoc = loc + (size-1);
if (endLoc.getOffset() < loc.getOffset())
return false; const ParamEntry *maxEntry = (const ParamEntry *)0;
ParamEntryResolver::const_iterator iter = resolver->find_begin(loc.getOffset());
ParamEntryResolver::const_iterator enditer = resolver->find_end(endLoc.getOffset());
while(iter != enditer) {
const ParamEntry *testEntry = (*iter).getParamEntry();
++iter;
if (testEntry->containedBy(loc, size)) {
if (maxEntry == (const ParamEntry *)0)
maxEntry = testEntry;
else if (testEntry->getSize() > maxEntry->getSize())
maxEntry = testEntry;
}
}
if (maxEntry != (const ParamEntry *)0) {
if (!maxEntry->isExclusion())
return false;
res.space = maxEntry->getSpace();
res.offset = maxEntry->getBase();
res.size = maxEntry->getSize();
return true;
}
return false;
}
bool ParamListStandard::unjustifiedContainer(const Address &loc,int4 size,VarnodeData &res) const
{
list<ParamEntry>::const_iterator iter;
for(iter=entry.begin();iter!=entry.end();++iter) {
if ((*iter).getMinSize() > size) continue;
int4 just = (*iter).justifiedContain(loc,size);
if (just < 0) continue;
if (just == 0) return false;
(*iter).getContainer(loc,size,res);
return true;
}
return false;
}
OpCode ParamListStandard::assumedExtension(const Address &addr,int4 size,VarnodeData &res) const
{
list<ParamEntry>::const_iterator iter;
for(iter=entry.begin();iter!=entry.end();++iter) {
if ((*iter).getMinSize() > size) continue;
OpCode ext = (*iter).assumedExtension(addr,size,res);
if (ext != CPUI_COPY)
return ext;
}
return CPUI_COPY;
}
void ParamListStandard::getRangeList(AddrSpace *spc,RangeList &res) const
{
list<ParamEntry>::const_iterator iter;
for(iter=entry.begin();iter!=entry.end();++iter) {
if ((*iter).getSpace() != spc) continue;
uintb baseoff = (*iter).getBase();
uintb endoff = baseoff + (*iter).getSize() - 1;
res.insertRange(spc,baseoff,endoff);
}
}
void ParamListStandard::restoreXml(const Element *el,const AddrSpaceManager *manage,
vector<EffectRecord> &effectlist,bool normalstack)
{
numgroup = 0;
spacebase = (AddrSpace *)0;
pointermax = 0;
thisbeforeret = false;
bool splitFloat = true; bool autokilledbycall = false;
for(int4 i=0;i<el->getNumAttributes();++i) {
const string &attrname( el->getAttributeName(i) );
if (attrname == "pointermax") {
istringstream i1(el->getAttributeValue(i));
i1.unsetf(ios::dec | ios::hex | ios::oct);
i1 >> pointermax;
}
else if (attrname == "thisbeforeretpointer") {
thisbeforeret = xml_readbool( el->getAttributeValue(i) );
}
else if (attrname == "killedbycall") {
autokilledbycall = xml_readbool( el->getAttributeValue(i) );
}
else if (attrname == "separatefloat") {
splitFloat = xml_readbool( el->getAttributeValue(i) );
}
}
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") {
parsePentry(subel, manage, effectlist, numgroup, normalstack, autokilledbycall, splitFloat, false);
}
else if (subel->getName() == "group") {
parseGroup(subel, manage, effectlist, numgroup, normalstack, autokilledbycall, splitFloat);
}
}
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");
}
}
}
calcDelay();
populateResolver();
}
ParamList *ParamListStandard::clone(void) const
{
ParamList *res = new ParamListStandard(*this);
return res;
}
void ParamListRegisterOut::assignMap(const vector<Datatype *> &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 (res.back().addr.isInvalid())
throw ParamUnassignedError("Cannot assign parameter address for " + proto[0]->getName());
}
res.back().type = proto[0];
res.back().flags = 0;
}
void ParamListRegisterOut::fillinMap(ParamActive *active) const
{
if (active->getNumTrials() == 0) return; const ParamEntry *bestentry = (const ParamEntry *)0;
int4 bestcover = 0;
type_metatype bestmetatype = TYPE_PTR;
list<ParamEntry>::const_iterator iter;
for(iter=entry.begin();iter!=entry.end();++iter) {
const ParamEntry *curentry = &(*iter);
bool putativematch = false;
for(int4 j=0;j<active->getNumTrials();++j) { ParamTrial ¶mtrial(active->getTrial(j));
if (paramtrial.isActive()) {
int4 res = curentry->justifiedContain(paramtrial.getAddress(),paramtrial.getSize());
if (res >= 0) {
paramtrial.setEntry(curentry,res);
putativematch = true;
}
else
paramtrial.setEntry((const ParamEntry *)0,0);
}
else
paramtrial.setEntry((const ParamEntry *)0,0);
}
if (!putativematch) continue;
active->sortTrials();
int4 offmatch = 0;
int4 k;
for(k=0;k<active->getNumTrials();++k) {
ParamTrial ¶mtrial(active->getTrial(k));
if (paramtrial.getEntry() == (const ParamEntry *)0) continue;
if (offmatch != paramtrial.getOffset()) break;
if (((offmatch == 0)&&curentry->isParamCheckLow()) ||
((offmatch != 0)&&curentry->isParamCheckHigh())) { if (paramtrial.isRemFormed()) break; if (paramtrial.isIndCreateFormed()) break; }
offmatch += paramtrial.getSize();
}
if (offmatch < curentry->getMinSize()) k = 0; if ((k==active->getNumTrials())&&((curentry->getType() > bestmetatype)||(offmatch > bestcover))) {
bestentry = curentry;
bestcover = offmatch;
bestmetatype = curentry->getType();
}
}
if (bestentry==(const ParamEntry *)0) {
for(int4 i=0;i<active->getNumTrials();++i)
active->getTrial(i).markNoUse();
}
else {
for(int4 i=0;i<active->getNumTrials();++i) {
ParamTrial ¶mtrial(active->getTrial(i));
if (paramtrial.isActive()) {
int4 res = bestentry->justifiedContain(paramtrial.getAddress(),paramtrial.getSize());
if (res >= 0) {
paramtrial.markUsed(); paramtrial.setEntry(bestentry,res);
}
else {
paramtrial.markNoUse();
paramtrial.setEntry((const ParamEntry *)0,0);
}
}
else {
paramtrial.markNoUse();
paramtrial.setEntry((const ParamEntry *)0,0);
}
}
active->sortTrials();
}
}
bool ParamListRegisterOut::possibleParam(const Address &loc,int4 size) const
{
list<ParamEntry>::const_iterator iter;
for(iter=entry.begin();iter!=entry.end();++iter) {
if ((*iter).justifiedContain(loc,size)>=0)
return true;
}
return false;
}
void ParamListRegisterOut::restoreXml(const Element *el,const AddrSpaceManager *manage,
vector<EffectRecord> &effectlist,bool normalstack)
{
ParamListStandard::restoreXml(el,manage,effectlist,normalstack);
list<ParamEntry>::iterator iter;
for(iter=entry.begin();iter!=entry.end();++iter) {
ParamEntry &curEntry(*iter);
curEntry.extraChecks(entry);
}
}
ParamList *ParamListRegisterOut::clone(void) const
{
ParamList *res = new ParamListRegisterOut(*this);
return res;
}
void ParamListRegister::fillinMap(ParamActive *active) const
{
if (active->getNumTrials() == 0) return;
for(int4 i=0;i<active->getNumTrials();++i) {
ParamTrial ¶mtrial(active->getTrial(i));
const ParamEntry *entrySlot = findEntry(paramtrial.getAddress(),paramtrial.getSize());
if (entrySlot == (const ParamEntry *)0) paramtrial.markNoUse();
else {
paramtrial.setEntry( entrySlot,0 ); 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; }
res.back().addr = assignAddress(proto[0],status);
if (res.back().addr.isInvalid()) { 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(); res.back().type = pointertp; res.back().flags = ParameterPieces::hiddenretparm; }
}
void ParamListStandardOut::restoreXml(const Element *el,const AddrSpaceManager *manage,vector<EffectRecord> &effectlist,bool normalstack)
{
ParamListRegisterOut::restoreXml(el,manage,effectlist,normalstack);
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
{
ParamList *res = new ParamListStandardOut( *this );
return res;
}
void ParamListMerged::foldIn(const ParamListStandard &op2)
{
if (entry.empty()) {
spacebase = op2.getSpacebase();
entry = op2.getEntry();
return;
}
if ((spacebase != op2.getSpacebase())&&(op2.getSpacebase() != (AddrSpace *)0))
throw LowlevelError("Cannot merge prototype models with different stacks");
list<ParamEntry>::const_iterator iter2;
for(iter2=op2.getEntry().begin();iter2!=op2.getEntry().end();++iter2) {
const ParamEntry &opentry( *iter2 );
int4 typeint = 0;
list<ParamEntry>::iterator iter;
for(iter=entry.begin();iter!=entry.end();++iter) {
if ((*iter).contains(opentry)) {
typeint = 2;
break;
}
if (opentry.contains( *iter )) {
typeint = 1;
break;
}
}
if (typeint==2) {
if ((*iter).getMinSize() != opentry.getMinSize())
typeint = 0;
}
else if (typeint == 1) {
if ((*iter).getMinSize() != opentry.getMinSize())
typeint = 0;
else
*iter = opentry; }
if (typeint == 0)
entry.push_back(opentry);
}
}
ParamList *ParamListMerged::clone(void) const
{
ParamList *res = new ParamListMerged(*this);
return res;
}
ParamTrial ParamTrial::splitHi(int4 sz) const
{
ParamTrial res(addr,sz,slot);
res.flags = flags;
return res;
}
ParamTrial ParamTrial::splitLo(int4 sz) const
{
Address newaddr = addr + (size-sz);
ParamTrial res(newaddr,sz,slot+1);
res.flags = flags;
return res;
}
bool ParamTrial::testShrink(const Address &newaddr,int4 sz) const
{
Address testaddr;
if (addr.isBigEndian())
testaddr = addr + (size - sz);
else
testaddr = addr;
if (testaddr != newaddr)
return false;
if (entry != (const ParamEntry *)0) return false;
return true;
}
bool ParamTrial::operator<(const ParamTrial &b) const
{
if (entry == (const ParamEntry *)0) return false;
if (b.entry == (const ParamEntry *)0) return true;
int4 grpa = entry->getGroup();
int4 grpb = b.entry->getGroup();
if (grpa != grpb)
return (grpa < grpb);
if (entry != b.entry) return (entry < b.entry);
if (entry->isExclusion()) {
return (offset < b.offset);
}
if (addr != b.addr) {
if (entry->isReverseStack())
return (b.addr < addr);
else
return (addr < b.addr);
}
return (size < b.size);
}
ParamActive::ParamActive(bool recoversub)
{
slotbase = 1;
stackplaceholder = -1;
numpasses = 0;
maxpass = 0;
isfullychecked = false;
needsfinalcheck = false;
recoversubcall = recoversub;
}
void ParamActive::clear(void)
{
trial.clear();
slotbase = 1;
stackplaceholder = -1;
numpasses = 0;
isfullychecked = false;
}
void ParamActive::registerTrial(const Address &addr,int4 sz)
{
trial.push_back(ParamTrial(addr,sz,slotbase));
if (addr.getSpace()->getType() != IPTR_SPACEBASE)
trial.back().markKilledByCall();
slotbase += 1;
}
int4 ParamActive::whichTrial(const Address &addr,int4 sz) const
{
for(int4 i=0;i<trial.size();++i) {
if (addr.overlap(0,trial[i].getAddress(),trial[i].getSize())>=0) return i;
if (sz<=1) return -1;
Address endaddr = addr + (sz-1);
if (endaddr.overlap(0,trial[i].getAddress(),trial[i].getSize())>=0) return i;
}
return -1;
}
void ParamActive::freePlaceholderSlot(void)
{
for(int4 i=0;i<trial.size();++i) {
if (trial[i].getSlot() > stackplaceholder)
trial[i].setSlot(trial[i].getSlot() - 1);
}
stackplaceholder = -2;
slotbase -= 1;
maxpass = 0;
}
void ParamActive::deleteUnusedTrials(void)
{
vector<ParamTrial> newtrials;
int4 slot = 1;
for(int4 i=0;i<trial.size();++i) {
ParamTrial &curtrial(trial[i]);
if (curtrial.isUsed()) {
curtrial.setSlot(slot);
slot += 1;
newtrials.push_back(curtrial);
}
}
trial = newtrials;
}
void ParamActive::splitTrial(int4 i,int4 sz)
{
if (stackplaceholder >= 0)
throw LowlevelError("Cannot split parameter when the placeholder has not been recovered");
vector<ParamTrial> newtrials;
int4 slot = trial[i].getSlot();
for(int4 j=0;j<i;++j) {
newtrials.push_back(trial[j]);
int4 oldslot = newtrials.back().getSlot();
if (oldslot > slot)
newtrials.back().setSlot(oldslot+1);
}
newtrials.push_back(trial[i].splitHi(sz));
newtrials.push_back(trial[i].splitLo(trial[i].getSize()-sz));
for(int4 j=i+1;j<trial.size();++j) {
newtrials.push_back(trial[j]);
int4 oldslot = newtrials.back().getSlot();
if (oldslot > slot)
newtrials.back().setSlot(oldslot+1);
}
slotbase += 1;
trial = newtrials;
}
void ParamActive::joinTrial(int4 slot,const Address &addr,int4 sz)
{
if (stackplaceholder >= 0)
throw LowlevelError("Cannot join parameters when the placeholder has not been removed");
vector<ParamTrial> newtrials;
int4 sizecheck = 0;
for(int4 i=0;i<trial.size();++i) {
ParamTrial &curtrial( trial[i] );
int4 curslot = curtrial.getSlot();
if (curslot < slot)
newtrials.push_back(curtrial);
else if (curslot == slot) {
sizecheck += curtrial.getSize();
newtrials.push_back(ParamTrial(addr,sz,slot));
newtrials.back().markUsed();
newtrials.back().markActive();
}
else if (curslot == slot + 1) { sizecheck += curtrial.getSize();
}
else {
newtrials.push_back(curtrial);
newtrials.back().setSlot(curslot-1);
}
}
if (sizecheck != sz)
throw LowlevelError("Size mismatch when joining parameters");
slotbase -= 1;
trial = newtrials;
}
int4 ParamActive::getNumUsed(void) const
{
int4 count;
for(count=0;count<trial.size();++count) {
if (!trial[count].isUsed()) break;
}
return count;
}
const string FspecSpace::NAME = "fspec";
FspecSpace::FspecSpace(AddrSpaceManager *m,const Translate *t,int4 ind)
: AddrSpace(m,t,IPTR_FSPEC,NAME,sizeof(void *),1,ind,0,1)
{
clearFlags(heritaged|does_deadcode|big_endian);
if (HOST_ENDIAN==1) setFlags(big_endian);
}
void FspecSpace::saveXmlAttributes(ostream &s,uintb offset) const
{
FuncCallSpecs *fc = (FuncCallSpecs *)(uintp)offset;
if (fc->getEntryAddress().isInvalid())
s << " space=\"fspec\"";
else {
AddrSpace *id = fc->getEntryAddress().getSpace();
a_v(s,"space",id->getName()); s << ' ' << "offset=\"";
printOffset(s,fc->getEntryAddress().getOffset());
s << "\"";
}
}
void FspecSpace::saveXmlAttributes(ostream &s,uintb offset,int4 size) const
{
FuncCallSpecs *fc = (FuncCallSpecs *)(uintp)offset;
if (fc->getEntryAddress().isInvalid())
s << " space=\"fspec\"";
else {
AddrSpace *id = fc->getEntryAddress().getSpace();
a_v(s,"space",id->getName()); s << ' ' << "offset=\"";
printOffset(s,fc->getEntryAddress().getOffset());
s << "\"";
a_v_i(s,"size",size);
}
}
void FspecSpace::printRaw(ostream &s,uintb offset) const
{
FuncCallSpecs *fc = (FuncCallSpecs *)(uintp)offset;
if (fc->getName().size() != 0)
s << fc->getName();
else {
s << "func_";
fc->getEntryAddress().printRaw(s);
}
}
void FspecSpace::saveXml(ostream &s) const
{
throw LowlevelError("Should never save fspec space to XML");
}
void FspecSpace::restoreXml(const Element *el)
{
throw LowlevelError("Should never restore fspec space from XML");
}
EffectRecord::EffectRecord(const Address &addr,int4 size)
{
range.space = addr.getSpace();
range.offset = addr.getOffset();
range.size = size;
type = unknown_effect;
}
EffectRecord::EffectRecord(const ParamEntry &entry,uint4 t)
{
range.space = entry.getSpace();
range.offset = entry.getBase();
range.size = entry.getSize();
type = t;
}
EffectRecord::EffectRecord(const VarnodeData &data,uint4 t)
{
range = data;
type = t;
}
void EffectRecord::saveXml(ostream &s) const
{
Address addr(range.space,range.offset);
if ((type == unaffected)||(type == killedbycall)||(type == return_address))
addr.saveXml(s,range.size);
else
throw LowlevelError("Bad EffectRecord type");
}
void EffectRecord::restoreXml(uint4 grouptype,const Element *el,const AddrSpaceManager *manage)
{
type = grouptype;
range.restoreXml(el,manage);
}
void ProtoModel::defaultLocalRange(void)
{
AddrSpace *spc = glb->getStackSpace();
uintb first,last;
if (stackgrowsnegative) { last = spc->getHighest();
if (spc->getAddrSize()>=4)
first = last - 999999;
else if (spc->getAddrSize()>=2)
first = last - 9999;
else
first = last - 99;
localrange.insertRange(spc,first,last);
}
else { first = 0;
if (spc->getAddrSize()>=4)
last = 999999;
else if (spc->getAddrSize()>=2)
last = 9999;
else
last = 99;
localrange.insertRange(spc,first,last);
}
}
void ProtoModel::defaultParamRange(void)
{
AddrSpace *spc = glb->getStackSpace();
uintb first,last;
if (stackgrowsnegative) { first = 0;
if (spc->getAddrSize()>=4)
last = 511;
else if (spc->getAddrSize()>=2)
last = 255;
else
last = 15;
paramrange.insertRange(spc,first,last);
}
else { last = spc->getHighest();
if (spc->getAddrSize()>=4)
first = last - 511;
else if (spc->getAddrSize()>=2)
first = last - 255;
else
first = last - 15;
paramrange.insertRange(spc,first,last); }
}
void ProtoModel::buildParamList(const string &strategy)
{
if ((strategy == "")||(strategy == "standard")) {
input = new ParamListStandard();
output = new ParamListStandardOut();
}
else if (strategy == "register") {
input = new ParamListRegister();
output = new ParamListRegisterOut();
}
else
throw LowlevelError("Unknown strategy type: "+strategy);
}
ProtoModel::ProtoModel(Architecture *g)
{
glb = g;
input = (ParamList *)0;
output = (ParamList *)0;
compatModel = (const ProtoModel *)0;
extrapop=0;
injectUponEntry = -1;
injectUponReturn = -1;
stackgrowsnegative = true; hasThis = false;
isConstruct = false;
defaultLocalRange();
defaultParamRange();
}
ProtoModel::ProtoModel(const string &nm,const ProtoModel &op2)
{
glb = op2.glb;
name = nm;
extrapop = op2.extrapop;
if (op2.input != (ParamList *)0)
input = op2.input->clone();
else
input = (ParamList *)0;
if (op2.output != (ParamList *)0)
output = op2.output->clone();
else
output = (ParamList *)0;
effectlist = op2.effectlist;
likelytrash = op2.likelytrash;
injectUponEntry = op2.injectUponEntry;
injectUponReturn = op2.injectUponReturn;
localrange = op2.localrange;
paramrange = op2.paramrange;
stackgrowsnegative = op2.stackgrowsnegative;
hasThis = op2.hasThis;
isConstruct = op2.isConstruct;
if (name == "__thiscall")
hasThis = true;
compatModel = &op2;
}
ProtoModel::~ProtoModel(void)
{
if (input != (ParamList *)0)
delete input;
if (output != (ParamList *)0)
delete output;
}
bool ProtoModel::isCompatible(const ProtoModel *op2) const
{
if (this == op2 || compatModel == op2 || op2->compatModel == this)
return true;
return false;
}
void ProtoModel::assignParameterStorage(const vector<Datatype *> &typelist,vector<ParameterPieces> &res,bool ignoreOutputError)
{
if (ignoreOutputError) {
try {
output->assignMap(typelist,*glb->types,res);
}
catch(ParamUnassignedError &err) {
res.clear();
res.emplace_back();
res.back().flags = 0;
res.back().type = glb->types->getTypeVoid();
}
}
else {
output->assignMap(typelist,*glb->types,res);
}
input->assignMap(typelist,*glb->types,res);
}
uint4 ProtoModel::lookupEffect(const vector<EffectRecord> &efflist,const Address &addr,int4 size)
{
if (addr.getSpace()->getType()==IPTR_INTERNAL) return EffectRecord::unaffected;
EffectRecord cur(addr,size);
vector<EffectRecord>::const_iterator iter;
iter = upper_bound(efflist.begin(),efflist.end(),cur,EffectRecord::compareByAddress);
if (iter==efflist.begin()) return EffectRecord::unknown_effect; --iter;
Address hit = (*iter).getAddress();
int4 sz = (*iter).getSize();
if (sz == 0 && (hit.getSpace() == addr.getSpace())) return EffectRecord::unaffected;
int4 where = addr.overlap(0,hit,sz);
if ((where>=0)&&(where+size<=sz))
return (*iter).getType();
return EffectRecord::unknown_effect;
}
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);
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;
}
uint4 ProtoModel::hasEffect(const Address &addr,int4 size) const
{
return lookupEffect(effectlist,addr,size);
}
void ProtoModel::restoreXml(const Element *el)
{
int4 numattr = el->getNumAttributes();
bool sawlocalrange = false;
bool sawparamrange = false;
bool sawretaddr = false;
stackgrowsnegative = true; AddrSpace *stackspc = glb->getStackSpace();
if (stackspc != (AddrSpace *)0)
stackgrowsnegative = stackspc->stackGrowsNegative(); string strategystring;
localrange.clear();
paramrange.clear();
extrapop = -300;
hasThis = false;
isConstruct = false;
effectlist.clear();
injectUponEntry = -1;
injectUponReturn = -1;
likelytrash.clear();
for(int4 i=0;i<numattr;++i) {
if (el->getAttributeName(i) == "name")
name = el->getAttributeValue(i);
else if (el->getAttributeName(i) == "extrapop") {
if (el->getAttributeValue(i) == "unknown")
extrapop = extrapop_unknown;
else {
istringstream s(el->getAttributeValue(i));
s.unsetf(ios::dec | ios::hex | ios::oct);
s >> extrapop;
}
}
else if (el->getAttributeName(i) == "stackshift") {
}
else if (el->getAttributeName(i) == "strategy") {
strategystring = el->getAttributeValue(i);
}
else if (el->getAttributeName(i) == "hasthis") {
hasThis = xml_readbool(el->getAttributeValue(i));
}
else if (el->getAttributeName(i) == "constructor") {
isConstruct = xml_readbool(el->getAttributeValue(i));
}
else
throw LowlevelError("Unknown prototype attribute: "+el->getAttributeName(i));
}
if (name == "__thiscall")
hasThis = true;
if (extrapop == -300)
throw LowlevelError("Missing prototype attributes");
buildParamList(strategystring); const List &list(el->getChildren());
List::const_iterator iter;
for(iter=list.begin();iter!=list.end();++iter) {
const Element *subnode = *iter;
if (subnode->getName() == "input") {
input->restoreXml(subnode,glb,effectlist,stackgrowsnegative);
if (stackspc != (AddrSpace *)0) {
input->getRangeList(stackspc,paramrange);
if (!paramrange.empty())
sawparamrange = true;
}
}
else if (subnode->getName() == "output") {
output->restoreXml(subnode,glb,effectlist,stackgrowsnegative);
}
else if (subnode->getName() == "unaffected") {
const List &flist(subnode->getChildren());
List::const_iterator fiter;
for(fiter=flist.begin();fiter!=flist.end();++fiter) {
effectlist.emplace_back();
effectlist.back().restoreXml(EffectRecord::unaffected,*fiter,glb);
}
}
else if (subnode->getName() == "killedbycall") {
const List &flist(subnode->getChildren());
List::const_iterator fiter;
for(fiter=flist.begin();fiter!=flist.end();++fiter) {
effectlist.emplace_back();
effectlist.back().restoreXml(EffectRecord::killedbycall,*fiter,glb);
}
}
else if (subnode->getName() == "returnaddress") {
const List &flist(subnode->getChildren());
List::const_iterator fiter;
for(fiter=flist.begin();fiter!=flist.end();++fiter) {
effectlist.emplace_back();
effectlist.back().restoreXml(EffectRecord::return_address,*fiter,glb);
}
sawretaddr = true;
}
else if (subnode->getName() == "localrange") {
sawlocalrange = true;
const List &sublist(subnode->getChildren());
List::const_iterator subiter;
for(subiter=sublist.begin();subiter!=sublist.end();++subiter) {
Range range;
range.restoreXml(*subiter,glb);
localrange.insertRange(range.getSpace(),range.getFirst(),range.getLast());
}
}
else if (subnode->getName() == "paramrange") {
sawparamrange = true;
const List &sublist(subnode->getChildren());
List::const_iterator subiter;
for(subiter=sublist.begin();subiter!=sublist.end();++subiter) {
Range range;
range.restoreXml(*subiter,glb);
paramrange.insertRange(range.getSpace(),range.getFirst(),range.getLast());
}
}
else if (subnode->getName() == "likelytrash") {
const List &flist(subnode->getChildren());
List::const_iterator fiter;
for(fiter=flist.begin();fiter!=flist.end();++fiter) {
likelytrash.emplace_back();
likelytrash.back().restoreXml(*fiter,glb);
}
}
else if (subnode->getName() == "pcode") {
if (subnode->getAttributeValue("inject") == "uponentry") {
injectUponEntry = glb->pcodeinjectlib->restoreXmlInject("Protomodel : "+name,
name+"@@inject_uponentry",
InjectPayload::CALLMECHANISM_TYPE,subnode);
}
else {
injectUponReturn = glb->pcodeinjectlib->restoreXmlInject("Protomodel : "+name,
name+"@@inject_uponreturn",
InjectPayload::CALLMECHANISM_TYPE,subnode);
}
}
else if (subnode->getName() == "description") {
}
else
throw LowlevelError("Unknown element in prototype: "+subnode->getName());
}
if ((!sawretaddr)&&(glb->defaultReturnAddr.space != (AddrSpace *)0)) {
effectlist.push_back(EffectRecord(glb->defaultReturnAddr,EffectRecord::return_address));
}
sort(effectlist.begin(),effectlist.end(),EffectRecord::compareByAddress);
sort(likelytrash.begin(),likelytrash.end());
if (!sawlocalrange)
defaultLocalRange();
if (!sawparamrange)
defaultParamRange();
}
ScoreProtoModel::ScoreProtoModel(bool isinput,const ProtoModel *mod,int4 numparam)
{
isinputscore = isinput;
model = mod;
entry.reserve(numparam);
finalscore = -1;
mismatch = 0;
}
void ScoreProtoModel::addParameter(const Address &addr,int4 sz)
{
int4 orig = entry.size();
int4 slot,slotsize;
bool isparam;
if (isinputscore)
isparam = model->possibleInputParamWithSlot(addr,sz,slot,slotsize);
else
isparam = model->possibleOutputParamWithSlot(addr,sz,slot,slotsize);
if (isparam) {
entry.emplace_back();
entry.back().origIndex = orig;
entry.back().slot = slot;
entry.back().size = slotsize;
}
else {
mismatch += 1;
}
}
void ScoreProtoModel::doScore(void)
{
sort(entry.begin(),entry.end());
int4 nextfree = 0; int4 basescore = 0;
int4 penalty[4];
penalty[0] = 16;
penalty[1] = 10;
penalty[2] = 7;
penalty[3] = 5;
int4 penaltyfinal = 3;
int4 mismatchpenalty = 20;
for(int4 i=0;i<entry.size();++i) {
const PEntry &p( entry[i] );
if (p.slot > nextfree) { while(nextfree < p.slot) {
if (nextfree < 4)
basescore += penalty[nextfree];
else
basescore += penaltyfinal;
nextfree += 1;
}
nextfree += p.size;
}
else if (nextfree > p.slot) { basescore += mismatchpenalty;
if (p.slot + p.size > nextfree)
nextfree = p.slot + p.size;
}
else {
nextfree = p.slot + p.size;
}
}
finalscore = basescore + mismatchpenalty * mismatch;
}
void ProtoModelMerged::intersectEffects(const vector<EffectRecord> &efflist)
{
vector<EffectRecord> newlist;
int4 i = 0;
int4 j = 0;
while((i<effectlist.size())&&(j<efflist.size())) {
const EffectRecord &eff1( effectlist[i] );
const EffectRecord &eff2( efflist[j] );
if (EffectRecord::compareByAddress(eff1, eff2))
i += 1;
else if (EffectRecord::compareByAddress(eff2, eff1))
j += 1;
else {
if (eff1 == eff2)
newlist.push_back(eff1);
i += 1;
j += 1;
}
}
effectlist.swap(newlist);
}
void ProtoModelMerged::intersectLikelyTrash(const vector<VarnodeData> &trashlist)
{
vector<VarnodeData> newlist;
int4 i=0;
int4 j=0;
while((i<likelytrash.size())&&(j<trashlist.size())) {
const VarnodeData &trs1( likelytrash[i] );
const VarnodeData &trs2( trashlist[j] );
if (trs1 < trs2)
i += 1;
else if (trs2 < trs1)
j += 1;
else {
newlist.push_back(trs1);
i += 1;
j += 1;
}
}
likelytrash = newlist;
}
void ProtoModelMerged::foldIn(ProtoModel *model)
{
if (model->glb != glb) throw LowlevelError("Mismatched architecture");
if ((model->input->getType() != ParamList::p_standard)&&
(model->input->getType() != ParamList::p_register))
throw LowlevelError("Can only resolve between standard prototype models");
if (input == (ParamList *)0) { input = new ParamListMerged();
output = new ParamListStandardOut(*(ParamListStandardOut *)model->output);
((ParamListMerged *)input)->foldIn(*(ParamListStandard *)model->input); extrapop = model->extrapop;
effectlist = model->effectlist;
injectUponEntry = model->injectUponEntry;
injectUponReturn = model->injectUponReturn;
likelytrash = model->likelytrash;
localrange = model->localrange;
paramrange = model->paramrange;
}
else {
((ParamListMerged *)input)->foldIn(*(ParamListStandard *)model->input);
if (extrapop != model->extrapop)
extrapop = ProtoModel::extrapop_unknown;
if ((injectUponEntry != model->injectUponEntry)||(injectUponReturn != model->injectUponReturn))
throw LowlevelError("Cannot merge prototype models with different inject ids");
intersectEffects(model->effectlist);
intersectLikelyTrash(model->likelytrash);
set<Range>::const_iterator iter;
for(iter=model->localrange.begin();iter!=model->localrange.end();++iter)
localrange.insertRange((*iter).getSpace(),(*iter).getFirst(),(*iter).getLast());
for(iter=model->paramrange.begin();iter!=model->paramrange.end();++iter)
paramrange.insertRange((*iter).getSpace(),(*iter).getFirst(),(*iter).getLast());
}
}
ProtoModel *ProtoModelMerged::selectModel(ParamActive *active) const
{
int4 bestscore = 500;
int4 bestindex = -1;
for(int4 i=0;i<modellist.size();++i) {
int4 numtrials = active->getNumTrials();
ScoreProtoModel scoremodel(true,modellist[i],numtrials);
for(int4 j=0;j<numtrials;++j) {
ParamTrial &trial( active->getTrial(j) );
if (trial.isActive())
scoremodel.addParameter(trial.getAddress(),trial.getSize());
}
scoremodel.doScore();
int4 score = scoremodel.getScore();
if (score < bestscore) {
bestscore = score;
bestindex = i;
if (bestscore == 0)
break; }
}
if (bestindex >= 0)
return modellist[bestindex];
throw LowlevelError("No model matches : missing default");
}
void ProtoModelMerged::restoreXml(const Element *el)
{
name = el->getAttributeValue("name");
const List &list(el->getChildren());
List::const_iterator iter;
for(iter=list.begin();iter!=list.end();++iter) { const Element *subel = *iter;
ProtoModel *mymodel = glb->getModel( subel->getAttributeValue("name"));
if (mymodel == (ProtoModel *)0)
throw LowlevelError("Missing prototype model: "+subel->getAttributeValue("name"));
foldIn(mymodel);
modellist.push_back(mymodel);
}
((ParamListMerged *)input)->finalize();
((ParamListMerged *)output)->finalize();
}
void ParameterBasic::setTypeLock(bool val)
{
if (val) {
flags |= ParameterPieces::typelock;
if (type->getMetatype() == TYPE_UNKNOWN) flags |= ParameterPieces::sizelock;
}
else
flags &= ~((uint4)(ParameterPieces::typelock|ParameterPieces::sizelock));
}
void ParameterBasic::setNameLock(bool val)
{
if (val)
flags |= ParameterPieces::namelock;
else
flags &= ~((uint4)ParameterPieces::namelock);
}
void ParameterBasic::setThisPointer(bool val)
{
if (val)
flags |= ParameterPieces::isthis;
else
flags &= ~((uint4)ParameterPieces::isthis);
}
void ParameterBasic::overrideSizeLockType(Datatype *ct)
{
if (type->getSize() == ct->getSize()) {
if (!isSizeTypeLocked())
throw LowlevelError("Overriding parameter that is not size locked");
type = ct;
return;
}
throw LowlevelError("Overriding parameter with different type size");
}
void ParameterBasic::resetSizeLockType(TypeFactory *factory)
{
if (type->getMetatype() == TYPE_UNKNOWN) return; int4 size = type->getSize();
type = factory->getBase(size,TYPE_UNKNOWN);
}
ProtoParameter *ParameterBasic::clone(void) const
{
ParameterBasic *res = new ParameterBasic(name,addr,type,flags);
return res;
}
const string &ParameterSymbol::getName(void) const
{
return sym->getName();
}
Datatype *ParameterSymbol::getType(void) const
{
return sym->getType();
}
Address ParameterSymbol::getAddress(void) const
{
return sym->getFirstWholeMap()->getAddr();
}
int4 ParameterSymbol::getSize(void) const
{
return sym->getFirstWholeMap()->getSize();
}
bool ParameterSymbol::isTypeLocked(void) const
{
return sym->isTypeLocked();
}
bool ParameterSymbol::isNameLocked(void) const
{
return sym->isNameLocked();
}
bool ParameterSymbol::isSizeTypeLocked(void) const
{
return sym->isSizeTypeLocked();
}
bool ParameterSymbol::isThisPointer(void) const
{
return sym->isThisPointer();
}
bool ParameterSymbol::isIndirectStorage(void) const
{
return sym->isIndirectStorage();
}
bool ParameterSymbol::isHiddenReturn(void) const
{
return sym->isHiddenReturn();
}
bool ParameterSymbol::isNameUndefined(void) const
{
return sym->isNameUndefined();
}
void ParameterSymbol::setTypeLock(bool val)
{
Scope *scope = sym->getScope();
uint4 attrs = Varnode::typelock;
if (!sym->isNameUndefined())
attrs |= Varnode::namelock;
if (val)
scope->setAttribute(sym,attrs);
else
scope->clearAttribute(sym,attrs);
}
void ParameterSymbol::setNameLock(bool val)
{
Scope *scope = sym->getScope();
if (val)
scope->setAttribute(sym,Varnode::namelock);
else
scope->clearAttribute(sym,Varnode::namelock);
}
void ParameterSymbol::setThisPointer(bool val)
{
Scope *scope = sym->getScope();
scope->setThisPointer(sym, val);
}
void ParameterSymbol::overrideSizeLockType(Datatype *ct)
{
sym->getScope()->overrideSizeLockType(sym,ct);
}
void ParameterSymbol::resetSizeLockType(TypeFactory *factory)
{
sym->getScope()->resetSizeLockType(sym);
}
ProtoParameter *ParameterSymbol::clone(void) const
{
throw LowlevelError("Should not be cloning ParameterSymbol");
}
Symbol *ParameterSymbol::getSymbol(void) const
{
return sym;
}
ProtoStoreSymbol::ProtoStoreSymbol(Scope *sc,const Address &usepoint)
{
scope = sc;
restricted_usepoint = usepoint;
outparam = (ProtoParameter *)0;
ParameterPieces pieces;
pieces.type = scope->getArch()->types->getTypeVoid();
pieces.flags = 0;
ProtoStoreSymbol::setOutput(pieces);
}
ProtoStoreSymbol::~ProtoStoreSymbol(void)
{
for(int4 i=0;i<inparam.size();++i) {
ProtoParameter *param = inparam[i];
if (param != (ProtoParameter *)0)
delete param;
}
if (outparam != (ProtoParameter *)0)
delete outparam;
}
ParameterSymbol *ProtoStoreSymbol::getSymbolBacked(int4 i)
{
while(inparam.size() <= i)
inparam.push_back((ProtoParameter *)0);
ParameterSymbol *res = dynamic_cast<ParameterSymbol *>(inparam[i]);
if (res != (ParameterSymbol *)0)
return res;
if (inparam[i] != (ProtoParameter *)0)
delete inparam[i];
res = new ParameterSymbol();
inparam[i] = res;
return res;
}
ProtoParameter *ProtoStoreSymbol::setInput(int4 i, const string &nm,const ParameterPieces &pieces)
{
ParameterSymbol *res = getSymbolBacked(i);
res->sym = scope->getCategorySymbol(0,i);
SymbolEntry *entry;
Address usepoint;
bool isindirect = (pieces.flags & ParameterPieces::indirectstorage) != 0;
bool ishidden = (pieces.flags & ParameterPieces::hiddenretparm) != 0;
if (res->sym != (Symbol *)0) {
entry = res->sym->getFirstWholeMap();
if ((entry->getAddr() != pieces.addr)||(entry->getSize() != pieces.type->getSize())) {
scope->removeSymbol(res->sym);
res->sym = (Symbol *)0;
}
}
if (res->sym == (Symbol *)0) {
if (scope->discoverScope(pieces.addr,pieces.type->getSize(),usepoint) == (Scope *)0)
usepoint = restricted_usepoint;
res->sym = scope->addSymbol(nm,pieces.type,pieces.addr,usepoint)->getSymbol();
scope->setCategory(res->sym,0,i);
if (isindirect || ishidden) {
uint4 mirror = 0;
if (isindirect)
mirror |= Varnode::indirectstorage;
if (ishidden)
mirror |= Varnode::hiddenretparm;
scope->setAttribute(res->sym,mirror);
}
return res;
}
if (res->sym->isIndirectStorage() != isindirect) {
if (isindirect)
scope->setAttribute(res->sym,Varnode::indirectstorage);
else
scope->clearAttribute(res->sym,Varnode::indirectstorage);
}
if (res->sym->isHiddenReturn() != ishidden) {
if (ishidden)
scope->setAttribute(res->sym,Varnode::hiddenretparm);
else
scope->clearAttribute(res->sym,Varnode::hiddenretparm);
}
if ((nm.size()!=0)&&(nm!=res->sym->getName()))
scope->renameSymbol(res->sym,nm);
if (pieces.type != res->sym->getType())
scope->retypeSymbol(res->sym,pieces.type);
return res;
}
void ProtoStoreSymbol::clearInput(int4 i)
{
Symbol *sym = scope->getCategorySymbol(0,i);
if (sym != (Symbol *)0) {
scope->setCategory(sym,-1,0); scope->removeSymbol(sym); }
int4 sz = scope->getCategorySize(0);
for(int4 j=i+1;j<sz;++j) {
sym = scope->getCategorySymbol(0,j);
if (sym != (Symbol *)0)
scope->setCategory(sym,0,j-1);
}
}
void ProtoStoreSymbol::clearAllInputs(void)
{
scope->clearCategory(0);
}
int4 ProtoStoreSymbol::getNumInputs(void) const
{
return scope->getCategorySize(0);
}
ProtoParameter *ProtoStoreSymbol::getInput(int4 i)
{
Symbol *sym = scope->getCategorySymbol(0,i);
if (sym == (Symbol *)0)
return (ProtoParameter *)0;
ParameterSymbol *res = getSymbolBacked(i);
res->sym = sym;
return res;
}
ProtoParameter *ProtoStoreSymbol::setOutput(const ParameterPieces &piece)
{
if (outparam != (ProtoParameter *)0)
delete outparam;
outparam = new ParameterBasic("",piece.addr,piece.type,piece.flags);
return outparam;
}
void ProtoStoreSymbol::clearOutput(void)
{
ParameterPieces pieces;
pieces.type = scope->getArch()->types->getTypeVoid();
pieces.flags = 0;
setOutput(pieces);
}
ProtoParameter *ProtoStoreSymbol::getOutput(void)
{
return outparam;
}
ProtoStore *ProtoStoreSymbol::clone(void) const
{
ProtoStoreSymbol *res;
res = new ProtoStoreSymbol(scope,restricted_usepoint);
delete res->outparam;
if (outparam != (ProtoParameter *)0)
res->outparam = outparam->clone();
else
res->outparam = (ProtoParameter *)0;
return res;
}
void ProtoStoreSymbol::saveXml(ostream &s) const
{ }
void ProtoStoreSymbol::restoreXml(const Element *el,ProtoModel *model)
{
throw LowlevelError("Do not restore symbol-backed prototype through this interface");
}
ProtoStoreInternal::ProtoStoreInternal(Datatype *vt)
{
voidtype = vt;
outparam = (ProtoParameter *)0;
ParameterPieces pieces;
pieces.type = voidtype;
pieces.flags = 0;
ProtoStoreInternal::setOutput(pieces);
}
ProtoStoreInternal::~ProtoStoreInternal(void)
{
if (outparam != (ProtoParameter *)0)
delete outparam;
for(int4 i=0;i<inparam.size();++i) {
ProtoParameter *param = inparam[i];
if (param != (ProtoParameter *)0)
delete param;
}
}
ProtoParameter *ProtoStoreInternal::setInput(int4 i,const string &nm,const ParameterPieces &pieces)
{
while(inparam.size() <= i)
inparam.push_back((ProtoParameter *)0);
if (inparam[i] != (ProtoParameter *)0)
delete inparam[i];
inparam[i] = new ParameterBasic(nm,pieces.addr,pieces.type,pieces.flags);
return inparam[i];
}
void ProtoStoreInternal::clearInput(int4 i)
{
int4 sz = inparam.size();
if (i>=sz) return;
if (inparam[i] != (ProtoParameter *)0)
delete inparam[i];
inparam[i] = (ProtoParameter *)0;
for(int4 j=i+1;j<sz;++j) { inparam[j-1] = inparam[j];
inparam[j] = (ProtoParameter *)0;
}
while(inparam.back() == (ProtoParameter *)0)
inparam.pop_back();
}
void ProtoStoreInternal::clearAllInputs(void)
{
for(int4 i=0;i<inparam.size();++i) {
if (inparam[i] != (ProtoParameter *)0)
delete inparam[i];
}
inparam.clear();
}
int4 ProtoStoreInternal::getNumInputs(void) const
{
return inparam.size();
}
ProtoParameter *ProtoStoreInternal::getInput(int4 i)
{
if (i>=inparam.size())
return (ProtoParameter *)0;
return inparam[i];
}
ProtoParameter *ProtoStoreInternal::setOutput(const ParameterPieces &piece)
{
if (outparam != (ProtoParameter *)0)
delete outparam;
outparam = new ParameterBasic("",piece.addr,piece.type,piece.flags);
return outparam;
}
void ProtoStoreInternal::clearOutput(void)
{
if (outparam != (ProtoParameter *)0)
delete outparam;
outparam = new ParameterBasic(voidtype);
}
ProtoParameter *ProtoStoreInternal::getOutput(void)
{
return outparam;
}
ProtoStore *ProtoStoreInternal::clone(void) const
{
ProtoStoreInternal *res = new ProtoStoreInternal(voidtype);
delete res->outparam;
if (outparam != (ProtoParameter *)0)
res->outparam = outparam->clone();
else
res->outparam = (ProtoParameter *)0;
for(int4 i=0;i<inparam.size();++i) {
ProtoParameter *param = inparam[i];
if (param != (ProtoParameter *)0)
param = param->clone();
res->inparam.push_back(param);
}
return res;
}
void ProtoStoreInternal::saveXml(ostream &s) const
{
s << "<internallist>\n";
if (outparam != (ProtoParameter *)0) {
s << "<retparam";
if (outparam->isTypeLocked())
a_v_b(s,"typelock",true);
s << ">\n";
outparam->getAddress().saveXml(s);
outparam->getType()->saveXml(s);
s << "</retparam>\n";
}
else {
s << "<retparam>\n <addr/>\n <void/>\n</retparam>\n";
}
for(int4 i=0;i<inparam.size();++i) {
ProtoParameter *param = inparam[i];
s << "<param";
if (param->getName().size()!=0)
a_v(s,"name",param->getName());
if (param->isTypeLocked())
a_v_b(s,"typelock",true);
if (param->isNameLocked())
a_v_b(s,"namelock",true);
if (param->isThisPointer())
a_v_b(s,"thisptr",true);
if (param->isIndirectStorage())
a_v_b(s,"indirectstorage",true);
if (param->isHiddenReturn())
a_v_b(s,"hiddenretparm",true);
s << ">\n";
param->getAddress().saveXml(s);
param->getType()->saveXml(s);
s << "</param>\n";
}
s << "</internallist>\n";
}
void ProtoStoreInternal::restoreXml(const Element *el,ProtoModel *model)
{
if (el->getName() != "internallist")
throw LowlevelError("Mismatched ProtoStore tag: ProtoStoreInternal did not get <internallist>");
Architecture *glb = model->getArch();
const List &list(el->getChildren());
List::const_iterator iter;
vector<ParameterPieces> pieces;
vector<string> namelist;
bool addressesdetermined = true;
pieces.push_back( ParameterPieces() ); namelist.push_back("ret");
pieces.back().type = outparam->getType();
pieces.back().flags = 0;
if (outparam->isTypeLocked())
pieces.back().flags |= ParameterPieces::typelock;
if (outparam->isIndirectStorage())
pieces.back().flags |= ParameterPieces::indirectstorage;
if (outparam->getAddress().isInvalid())
addressesdetermined = false;
for(iter=list.begin();iter!=list.end();++iter) { const Element *subel = *iter;
string name;
uint4 flags = 0;
for(int4 i=0;i<subel->getNumAttributes();++i) {
const string &attr( subel->getAttributeName(i) );
if (attr == "name")
name = subel->getAttributeValue(i);
else if (attr == "typelock") {
if (xml_readbool(subel->getAttributeValue(i)))
flags |= ParameterPieces::typelock;
}
else if (attr == "namelock") {
if (xml_readbool(subel->getAttributeValue(i)))
flags |= ParameterPieces::namelock;
}
else if (attr == "thisptr") {
if (xml_readbool(subel->getAttributeValue(i)))
flags |= ParameterPieces::isthis;
}
else if (attr == "indirectstorage") {
if (xml_readbool(subel->getAttributeValue(i)))
flags |= ParameterPieces::indirectstorage;
}
else if (attr == "hiddenretparm") {
if (xml_readbool(subel->getAttributeValue(i)))
flags |= ParameterPieces::hiddenretparm;
}
}
if ((flags & ParameterPieces::hiddenretparm) == 0)
namelist.push_back(name);
pieces.emplace_back();
ParameterPieces &curparam( pieces.back() );
const List &sublist(subel->getChildren());
List::const_iterator subiter;
subiter = sublist.begin();
curparam.addr = Address::restoreXml(*subiter,glb);
++subiter;
curparam.type = glb->types->restoreXmlType(*subiter);
curparam.flags = flags;
if (curparam.addr.isInvalid())
addressesdetermined = false;
}
ProtoParameter *curparam;
if (!addressesdetermined) {
vector<Datatype *> typelist;
for(int4 i=0;i<pieces.size();++i) typelist.push_back( pieces[i].type );
vector<ParameterPieces> addrPieces;
model->assignParameterStorage(typelist,addrPieces,true);
addrPieces.swap(pieces);
uint4 k = 0;
for(uint4 i=0;i<pieces.size();++i) {
if ((pieces[i].flags & ParameterPieces::hiddenretparm)!=0)
continue; pieces[i].flags = addrPieces[k].flags; k = k + 1;
}
if (pieces[0].addr.isInvalid()) { pieces[0].flags &= ~((uint4)ParameterPieces::typelock); }
curparam = setOutput(pieces[0]);
curparam->setTypeLock((pieces[0].flags & ParameterPieces::typelock)!=0);
}
uint4 j=1;
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); continue; }
curparam = setInput(i-1,namelist[j],pieces[i]);
curparam->setTypeLock((pieces[i].flags & ParameterPieces::typelock)!=0);
curparam->setNameLock((pieces[i].flags & ParameterPieces::namelock)!=0);
j = j + 1;
}
}
void FuncProto::updateThisPointer(void)
{
if (!model->hasThisPointer()) return;
int4 numInputs = store->getNumInputs();
if (numInputs == 0) return;
ProtoParameter *param = store->getInput(0);
if (param->isHiddenReturn()) {
if (numInputs < 2) return;
param = store->getInput(1);
}
param->setThisPointer(true);
}
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";
}
}
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; s << " <addr";
cur.space->saveXmlAttributes(s,cur.offset,cur.size);
s << "/>\n";
}
s << " </likelytrash>\n";
}
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) {
effectlist[off] = curRecord;
}
else {
effectlist.push_back(curRecord);
hasNew = true;
}
}
if (hasNew)
sort(effectlist.begin(),effectlist.end(),EffectRecord::compareByAddress);
}
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); }
sort(likelytrash.begin(),likelytrash.end());
}
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;
TypeFactory *typefactory = model->getArch()->types;
if (isOutputLocked())
typelist.push_back( getOutputType() );
else
typelist.push_back( typefactory->getTypeVoid() );
nmlist.push_back("");
Datatype *extra = typefactory->getBase(4,TYPE_UNKNOWN); for(int4 i=0;i<paramshift;++i) {
nmlist.push_back("");
typelist.push_back(extra);
}
if (isInputLocked()) { int4 num = numParams();
for(int4 i=0;i<num;++i) {
ProtoParameter *param = getParam(i);
nmlist.push_back(param->getName());
typelist.push_back( param->getType() );
}
}
else
isdotdotdot = true;
vector<ParameterPieces> pieces;
model->assignParameterStorage(typelist,pieces,false);
delete store;
store = new ProtoStoreInternal(typefactory->getTypeVoid());
store->setOutput(pieces[0]);
uint4 j=1;
for(uint4 i=1;i<pieces.size();++i) {
if ((pieces[i].flags & ParameterPieces::hiddenretparm) != 0) {
store->setInput(i-1,"rethidden",pieces[i]);
continue; }
store->setInput(j,nmlist[j],pieces[i]);
j = j + 1;
}
setInputLock(true);
setDotdotdot(isdotdotdot);
}
void FuncProto::resolveModel(ParamActive *active)
{
if (model == (ProtoModel *)0) return;
if (!model->isMerged()) return; ProtoModelMerged *mergemodel = (ProtoModelMerged *)model;
ProtoModel *newmodel = mergemodel->selectModel(active);
setModel(newmodel);
}
FuncProto::FuncProto(void)
{
model = (ProtoModel *)0;
store = (ProtoStore *)0;
flags = 0;
injectid = -1;
returnBytesConsumed = 0;
}
void FuncProto::copy(const FuncProto &op2)
{
model = op2.model;
extrapop = op2.extrapop;
flags = op2.flags;
if (store != (ProtoStore *)0)
delete store;
if (op2.store != (ProtoStore *)0)
store = op2.store->clone();
else
store = (ProtoStore *)0;
effectlist = op2.effectlist;
likelytrash = op2.likelytrash;
injectid = op2.injectid;
}
void FuncProto::copyFlowEffects(const FuncProto &op2)
{
flags &= ~((uint4)(is_inline|no_return));
flags |= op2.flags & (is_inline|no_return);
injectid = op2.injectid;
}
void FuncProto::setModel(ProtoModel *m)
{
if (m != (ProtoModel *)0) {
int4 expop = m->getExtraPop();
if ((model == (ProtoModel *)0)||(expop != ProtoModel::extrapop_unknown))
extrapop = expop;
if (m->hasThisPointer())
flags |= has_thisptr;
if (m->isConstructor())
flags |= is_constructor;
model = m;
}
else {
model = m;
extrapop = ProtoModel::extrapop_unknown;
}
flags &= ~((uint4)unknown_model); }
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);
setInputLock(true);
setOutputLock(true);
setModelLock(true);
}
void FuncProto::getPieces(PrototypePieces &pieces) const
{
pieces.model = model;
if (store == (ProtoStore *)0) return;
pieces.outtype = store->getOutput()->getType();
int4 num = store->getNumInputs();
for(int4 i=0;i<num;++i) {
ProtoParameter *param = store->getInput(i);
pieces.intypes.push_back(param->getType());
pieces.innames.push_back(param->getName());
}
pieces.dotdotdot = isDotdotdot();
}
void FuncProto::setScope(Scope *s,const Address &startpoint)
{
store = new ProtoStoreSymbol(s,startpoint);
if (model == (ProtoModel *)0)
setModel(s->getArch()->defaultfp);
}
void FuncProto::setInternal(ProtoModel *m,Datatype *vt)
{
store = new ProtoStoreInternal(vt);
if (model == (ProtoModel *)0)
setModel(m);
}
FuncProto::~FuncProto(void)
{
if (store != (ProtoStore *)0)
delete store;
}
bool FuncProto::isInputLocked(void) const
{
if ((flags&voidinputlock)!=0) return true;
if (numParams()==0) return false;
ProtoParameter *param = getParam(0);
if (param->isTypeLocked()) return true;
return false;
}
void FuncProto::setInputLock(bool val)
{
if (val)
flags |= modellock; int4 num = numParams();
if (num == 0) {
flags = val ? (flags|voidinputlock) : (flags& ~((uint4)voidinputlock));
return;
}
for(int4 i=0;i<num;++i) {
ProtoParameter *param = getParam(i);
param->setTypeLock(val);
}
}
void FuncProto::setOutputLock(bool val)
{
if (val)
flags |= modellock; store->getOutput()->setTypeLock(val);
}
bool FuncProto::setReturnBytesConsumed(int4 val)
{
if (val == 0)
return false;
if (returnBytesConsumed == 0 || val < returnBytesConsumed) {
returnBytesConsumed = val;
return true;
}
return false;
}
void FuncProto::resolveExtraPop(void)
{
if (!isInputLocked()) return;
int4 numparams = numParams();
if (isDotdotdot()) {
if (numparams != 0) setExtraPop(4); return; }
int4 expop = 4; for(int4 i=0;i<numparams;++i) {
ProtoParameter *param = getParam(i);
const Address &addr( param->getAddress() );
if (addr.getSpace()->getType() != IPTR_SPACEBASE) continue;
int4 cur = (int4)addr.getOffset() + param->getSize();
cur = (cur+3)&0xffffffc; if (cur > expop)
expop = cur;
}
setExtraPop(expop);
}
void FuncProto::clearUnlockedInput(void)
{
if (isInputLocked()) return;
store->clearAllInputs();
}
void FuncProto::clearUnlockedOutput(void)
{
ProtoParameter *outparam = getOutput();
if (outparam->isTypeLocked()) {
if (outparam->isSizeTypeLocked()) {
if (model != (ProtoModel *)0)
outparam->resetSizeLockType(getArch()->types);
}
}
else
store->clearOutput();
returnBytesConsumed = 0;
}
void FuncProto::clearInput(void)
{
store->clearAllInputs();
flags &= ~((uint4)voidinputlock); }
void FuncProto::cancelInjectId(void)
{
injectid = -1;
flags &= ~((uint4)is_inline);
}
void FuncProto::updateInputTypes(Funcdata &data,const vector<Varnode *> &triallist,ParamActive *activeinput)
{
if (isInputLocked()) return; store->clearAllInputs();
int4 count = 0;
int4 numtrials = activeinput->getNumTrials();
for(int4 i=0;i<numtrials;++i) {
ParamTrial &trial(activeinput->getTrial(i));
if (trial.isUsed()) {
Varnode *vn = triallist[trial.getSlot()-1];
if (vn->isMark()) continue;
ParameterPieces pieces;
if (vn->isPersist()) {
int4 sz;
pieces.addr = data.findDisjointCover(vn, sz);
if (sz == vn->getSize())
pieces.type = vn->getHigh()->getType();
else
pieces.type = data.getArch()->types->getBase(sz, TYPE_UNKNOWN);
pieces.flags = 0;
}
else {
pieces.addr = trial.getAddress();
pieces.type = vn->getHigh()->getType();
pieces.flags = 0;
}
store->setInput(count,"",pieces);
count += 1;
vn->setMark();
}
}
for(int4 i=0;i<triallist.size();++i)
triallist[i]->clearMark();
updateThisPointer();
}
void FuncProto::updateInputNoTypes(Funcdata &data,const vector<Varnode *> &triallist,ParamActive *activeinput)
{
if (isInputLocked()) return; store->clearAllInputs();
int4 count = 0;
int4 numtrials = activeinput->getNumTrials();
TypeFactory *factory = data.getArch()->types;
for(int4 i=0;i<numtrials;++i) {
ParamTrial &trial(activeinput->getTrial(i));
if (trial.isUsed()) {
Varnode *vn = triallist[trial.getSlot()-1];
if (vn->isMark()) continue;
ParameterPieces pieces;
if (vn->isPersist()) {
int4 sz;
pieces.addr = data.findDisjointCover(vn, sz);
pieces.type = factory->getBase(sz, TYPE_UNKNOWN);
pieces.flags = 0;
}
else {
pieces.addr = trial.getAddress();
pieces.type = factory->getBase(vn->getSize(),TYPE_UNKNOWN);
pieces.flags = 0;
}
store->setInput(count,"",pieces);
count += 1;
vn->setMark(); }
}
for(int4 i=0;i<triallist.size();++i)
triallist[i]->clearMark();
}
void FuncProto::updateOutputTypes(const vector<Varnode *> &triallist)
{
ProtoParameter *outparm = getOutput();
if (!outparm->isTypeLocked()) {
if (triallist.empty()) {
store->clearOutput();
return;
}
}
else if (outparm->isSizeTypeLocked()) {
if (triallist.empty()) return;
if ((triallist[0]->getAddr() == outparm->getAddress())&&(triallist[0]->getSize() == outparm->getSize()))
outparm->overrideSizeLockType(triallist[0]->getHigh()->getType());
return;
}
else
return;
if (triallist.empty()) return;
ParameterPieces pieces;
pieces.addr = triallist[0]->getAddr();
pieces.type = triallist[0]->getHigh()->getType();
pieces.flags = 0;
store->setOutput(pieces);
}
void FuncProto::updateOutputNoTypes(const vector<Varnode *> &triallist,TypeFactory *factory)
{
if (isOutputLocked()) return;
if (triallist.empty()) {
store->clearOutput();
return;
}
ParameterPieces pieces;
pieces.type = factory->getBase(triallist[0]->getSize(),TYPE_UNKNOWN);
pieces.addr = triallist[0]->getAddr();
pieces.flags = 0;
store->setOutput(pieces);
}
void FuncProto::updateAllTypes(const vector<string> &namelist,const vector<Datatype *> &typelist,
bool dtdtdt)
{
setModel(model); store->clearAllInputs();
store->clearOutput();
flags &= ~((uint4)voidinputlock);
setDotdotdot(dtdtdt);
vector<ParameterPieces> pieces;
try {
model->assignParameterStorage(typelist,pieces,false);
store->setOutput(pieces[0]);
uint4 j=1;
for(uint4 i=1;i<pieces.size();++i) {
if ((pieces[i].flags & ParameterPieces::hiddenretparm) != 0) {
store->setInput(i-1,"rethidden",pieces[i]);
continue; }
store->setInput(i-1,namelist[j],pieces[i]);
j = j + 1;
}
}
catch(ParamUnassignedError &err) {
flags |= error_inputparam;
}
updateThisPointer();
}
uint4 FuncProto::hasEffect(const Address &addr,int4 size) const
{
if (effectlist.empty())
return model->hasEffect(addr,size);
return ProtoModel::lookupEffect(effectlist,addr,size);
}
vector<EffectRecord>::const_iterator FuncProto::effectBegin(void) const
{
if (effectlist.empty())
return model->effectBegin();
return effectlist.begin();
}
vector<EffectRecord>::const_iterator FuncProto::effectEnd(void) const
{
if (effectlist.empty())
return model->effectEnd();
return effectlist.end();
}
vector<VarnodeData>::const_iterator FuncProto::trashBegin(void) const
{
if (likelytrash.empty())
return model->trashBegin();
return likelytrash.begin();
}
vector<VarnodeData>::const_iterator FuncProto::trashEnd(void) const
{
if (likelytrash.empty())
return model->trashEnd();
return likelytrash.end();
}
int4 FuncProto::characterizeAsInputParam(const Address &addr,int4 size) const
{
if (!isDotdotdot()) { if ((flags&voidinputlock)!=0) return 0;
int4 num = numParams();
if (num > 0) {
bool locktest = false; int4 characterCode = 0;
for(int4 i=0;i<num;++i) {
ProtoParameter *param = getParam(i);
if (!param->isTypeLocked()) continue;
locktest = true;
Address iaddr = param->getAddress();
if (iaddr.justifiedContain(param->getSize(),addr,size,false)==0)
return 1;
if (iaddr.containedBy(param->getSize(), addr, size))
characterCode = 2;
}
if (locktest) return characterCode;
}
}
return model->characterizeAsInputParam(addr, size);
}
bool FuncProto::possibleInputParam(const Address &addr,int4 size) const
{
if (!isDotdotdot()) { if ((flags&voidinputlock)!=0) return false;
int4 num = numParams();
if (num > 0) {
bool locktest = false; for(int4 i=0;i<num;++i) {
ProtoParameter *param = getParam(i);
if (!param->isTypeLocked()) continue;
locktest = true;
Address iaddr = param->getAddress();
if (iaddr.justifiedContain(param->getSize(),addr,size,false)==0)
return true;
}
if (locktest) return false;
}
}
return model->possibleInputParam(addr,size);
}
bool FuncProto::possibleOutputParam(const Address &addr,int4 size) const
{
if (isOutputLocked()) {
ProtoParameter *outparam = getOutput();
if (outparam->getType()->getMetatype() == TYPE_VOID)
return false;
Address iaddr = outparam->getAddress();
if (iaddr.justifiedContain(outparam->getSize(),addr,size,false)==0)
return true;
return false;
}
return model->possibleOutputParam(addr,size);
}
bool FuncProto::unjustifiedInputParam(const Address &addr,int4 size,VarnodeData &res) const
{
if (!isDotdotdot()) { if ((flags&voidinputlock)!=0) return false;
int4 num = numParams();
if (num > 0) {
bool locktest = false; for(int4 i=0;i<num;++i) {
ProtoParameter *param = getParam(i);
if (!param->isTypeLocked()) continue;
locktest = true;
Address iaddr = param->getAddress();
int4 just = iaddr.justifiedContain(param->getSize(),addr,size,false);
if (just ==0) return false; if (just > 0) {
res.space = iaddr.getSpace();
res.offset = iaddr.getOffset();
res.size = param->getSize();
return true;
}
}
if (locktest) return false;
}
}
return model->unjustifiedInputParam(addr,size,res);
}
bool FuncProto::getBiggestContainedInputParam(const Address &loc,int4 size,VarnodeData &res) const
{
if (!isDotdotdot()) { if ((flags&voidinputlock)!=0) return false;
int4 num = numParams();
if (num > 0) {
bool locktest = false; res.size = 0;
for(int4 i=0;i<num;++i) {
ProtoParameter *param = getParam(i);
if (!param->isTypeLocked()) continue;
locktest = true;
Address iaddr = param->getAddress();
if (iaddr.containedBy(param->getSize(), loc, size)) {
if (param->getSize() > res.size) {
res.space = iaddr.getSpace();
res.offset = iaddr.getOffset();
res.size = param->getSize();
}
}
}
if (locktest)
return (res.size == 0);
}
}
return model->getBiggestContainedInputParam(loc,size,res);
}
bool FuncProto::isCompatible(const FuncProto &op2) const
{
if (!model->isCompatible(op2.model)) return false;
if (op2.isOutputLocked()) {
if (isOutputLocked()) {
ProtoParameter *out1 = store->getOutput();
ProtoParameter *out2 = op2.store->getOutput();
if (*out1 != *out2) return false;
}
}
if ((extrapop != ProtoModel::extrapop_unknown)&&
(extrapop != op2.extrapop)) return false;
if (isDotdotdot() != op2.isDotdotdot()) { if (op2.isDotdotdot()) {
if (isInputLocked()) return false;
}
else
return false;
}
if (injectid != op2.injectid) return false;
if ((flags&(is_inline|no_return)) != (op2.flags&(is_inline|no_return)))
return false;
if (effectlist.size() != op2.effectlist.size()) return false;
for(int4 i=0;i<effectlist.size();++i)
if (effectlist[i] != op2.effectlist[i]) return false;
if (likelytrash.size() != op2.likelytrash.size()) return false;
for(int4 i=0;i<likelytrash.size();++i)
if (likelytrash[i] != op2.likelytrash[i]) return false;
return true;
}
void FuncProto::printRaw(const string &funcname,ostream &s) const
{
if (model != (ProtoModel *)0)
s << model->getName() << ' ';
else
s << "(no model) ";
getOutputType()->printRaw(s);
s << ' ' << funcname << '(';
int4 num = numParams();
for(int4 i=0;i<num;++i) {
if (i != 0)
s << ',';
getParam(i)->getType()->printRaw(s);
}
if (isDotdotdot()) {
if (num!=0)
s << ',';
s << "...";
}
s << ") extrapop=" << dec << extrapop;
}
void FuncProto::saveXml(ostream &s) const
{
s << " <prototype";
a_v(s,"model",model->getName());
if (extrapop == ProtoModel::extrapop_unknown)
a_v(s,"extrapop","unknown");
else
a_v_i(s,"extrapop",extrapop);
if (isDotdotdot())
a_v_b(s,"dotdotdot",true);
if (isModelLocked())
a_v_b(s,"modellock",true);
if ((flags&voidinputlock)!=0)
a_v_b(s,"voidlock",true);
if (isInline())
a_v_b(s,"inline",true);
if (isNoReturn())
a_v_b(s,"noreturn",true);
if (hasCustomStorage())
a_v_b(s,"custom",true);
if (isConstructor())
a_v_b(s,"constructor",true);
if (isDestructor())
a_v_b(s,"destructor",true);
s << ">\n";
ProtoParameter *outparam = store->getOutput();
s << " <returnsym";
if (outparam->isTypeLocked())
a_v_b(s,"typelock",true);
s << ">\n ";
outparam->getAddress().saveXml(s,outparam->getSize());
outparam->getType()->saveXml(s);
s << " </returnsym>\n";
saveEffectXml(s);
saveLikelyTrashXml(s);
if (injectid >= 0) {
Architecture *glb = model->getArch();
s << " <inject>" << glb->pcodeinjectlib->getCallFixupName(injectid) << "</inject>\n";
}
store->saveXml(s); s << " </prototype>\n";
}
void FuncProto::restoreXml(const Element *el,Architecture *glb)
{
if (store == (ProtoStore *)0)
throw LowlevelError("Prototype storage must be set before restoring FuncProto");
ProtoModel *mod = (ProtoModel *)0;
bool seenextrapop = false;
bool seenunknownmod = false;
int4 readextrapop;
int4 num = el->getNumAttributes();
flags = 0;
injectid = -1;
for(int4 i=0;i<num;++i) {
const string &attrname( el->getAttributeName(i) );
if (attrname == "model") {
const string &modelname( el->getAttributeValue(i) );
if ((modelname == "default")||(modelname.size()==0))
mod = glb->defaultfp; else if (modelname == "unknown") {
mod = glb->defaultfp; seenunknownmod = true;
}
else
mod = glb->getModel(modelname);
}
else if (attrname == "extrapop") {
seenextrapop = true;
const string &expopval( el->getAttributeValue(i) );
if (expopval == "unknown")
readextrapop = ProtoModel::extrapop_unknown;
else {
istringstream i1(expopval);
i1.unsetf(ios::dec | ios::hex | ios::oct);
i1 >> readextrapop;
}
}
else if (attrname == "modellock") {
if (xml_readbool(el->getAttributeValue(i)))
flags |= modellock;
}
else if (attrname == "dotdotdot") {
if (xml_readbool(el->getAttributeValue(i)))
flags |= dotdotdot;
}
else if (attrname == "voidlock") {
if (xml_readbool(el->getAttributeValue(i)))
flags |= voidinputlock;
}
else if (attrname == "inline") {
if (xml_readbool(el->getAttributeValue(i)))
flags |= is_inline;
}
else if (attrname == "noreturn") {
if (xml_readbool(el->getAttributeValue(i)))
flags |= no_return;
}
else if (attrname == "custom") {
if (xml_readbool(el->getAttributeValue(i)))
flags |= custom_storage;
}
else if (attrname == "constructor") {
if (xml_readbool(el->getAttributeValue(i)))
flags |= is_constructor;
}
else if (attrname == "destructor") {
if (xml_readbool(el->getAttributeValue(i)))
flags |= is_destructor;
}
}
if (mod != (ProtoModel *)0) setModel(mod); if (seenextrapop) extrapop = readextrapop;
if (seenunknownmod)
flags |= unknown_model;
const List &list(el->getChildren());
List::const_iterator iter = list.begin();
const Element *subel = (const Element *)0;
if (iter != list.end()) {
subel = *iter;
++iter;
}
if (subel != (const Element *)0) {
ParameterPieces outpieces;
bool outputlock = false;
if (subel->getName() == "returnsym") {
num = subel->getNumAttributes();
for(int4 i=0;i<num;++i) {
const string &attrname( subel->getAttributeName(i) );
if (attrname == "typelock")
outputlock = xml_readbool(subel->getAttributeValue(i));
}
const List &list2(subel->getChildren());
List::const_iterator riter = list2.begin();
int4 tmpsize;
outpieces.addr = Address::restoreXml(*riter,glb,tmpsize);
++riter;
outpieces.type = glb->types->restoreXmlType(*riter);
outpieces.flags = 0;
}
else if (subel->getName() == "addr") { int4 tmpsize;
outpieces.addr = Address::restoreXml(subel,glb,tmpsize);
outpieces.type = glb->types->restoreXmlType(*iter);
outpieces.flags = 0;
++iter;
}
else
throw LowlevelError("Missing <returnsym> tag");
store->setOutput(outpieces); store->getOutput()->setTypeLock(outputlock);
}
else
throw LowlevelError("Missing <returnsym> tag");
if (((flags&voidinputlock)!=0)||(isOutputLocked()))
flags |= modellock;
for(;iter!=list.end();++iter) {
if ((*iter)->getName() == "unaffected") {
const List &list2((*iter)->getChildren());
List::const_iterator iter2 = list2.begin();
while(iter2 != list2.end()) {
effectlist.emplace_back();
effectlist.back().restoreXml(EffectRecord::unaffected,*iter2,glb);
++iter2;
}
}
else if ((*iter)->getName() == "killedbycall") {
const List &list2((*iter)->getChildren());
List::const_iterator iter2 = list2.begin();
while(iter2 != list2.end()) {
effectlist.emplace_back();
effectlist.back().restoreXml(EffectRecord::killedbycall,*iter2,glb);
++iter2;
}
}
else if ((*iter)->getName() == "returnaddress") {
const List &list2((*iter)->getChildren());
List::const_iterator iter2 = list2.begin();
while(iter2 != list2.end()) {
effectlist.emplace_back();
effectlist.back().restoreXml(EffectRecord::return_address,*iter2,glb);
++iter2;
}
}
else if ((*iter)->getName() == "likelytrash") {
const List &list2((*iter)->getChildren());
List::const_iterator iter2 = list2.begin();
while(iter2 != list2.end()) {
likelytrash.emplace_back();
likelytrash.back().restoreXml(*iter2,glb);
++iter2;
}
}
else if ((*iter)->getName() == "inject") {
injectid = glb->pcodeinjectlib->getPayloadId(InjectPayload::CALLFIXUP_TYPE,(*iter)->getContent());
flags |= is_inline;
}
else if ((*iter)->getName() == "internallist") {
store->restoreXml(*iter,model);
}
}
restoreEffectXml();
restoreLikelyTrashXml();
if (!isModelLocked()) {
if (isInputLocked())
flags |= modellock;
}
if (extrapop == ProtoModel::extrapop_unknown)
resolveExtraPop();
ProtoParameter *outparam = store->getOutput();
if ((outparam->getType()->getMetatype()!=TYPE_VOID)&&outparam->getAddress().isInvalid()) {
throw LowlevelError("<returnsym> tag must include a valid storage address");
}
updateThisPointer();
}
void FuncCallSpecs::resolveSpacebaseRelative(Funcdata &data,Varnode *phvn)
{
Varnode *refvn = phvn->getDef()->getIn(0);
AddrSpace *spacebase = refvn->getSpace();
if (spacebase->getType() != IPTR_SPACEBASE) {
data.warningHeader("This function may have set the stack pointer");
}
stackoffset = refvn->getOffset();
if (stackPlaceholderSlot >= 0) {
if (op->getIn(stackPlaceholderSlot) == phvn) {
data.opRemoveInput(op,stackPlaceholderSlot);
clearStackPlaceholderSlot();
return;
}
}
if (isInputLocked()) {
int4 slot = op->getSlot(phvn)-1;
if (slot >= numParams())
throw LowlevelError("Stack placeholder does not line up with locked parameter");
ProtoParameter *param = getParam(slot);
Address addr = param->getAddress();
if (addr.getSpace() != spacebase) {
if (spacebase->getType() == IPTR_SPACEBASE)
throw LowlevelError("Stack placeholder does not match locked space");
}
stackoffset -= addr.getOffset();
stackoffset = spacebase->wrapOffset(stackoffset);
return;
}
throw LowlevelError("Unresolved stack placeholder");
}
void FuncCallSpecs::abortSpacebaseRelative(Funcdata &data)
{
if (stackPlaceholderSlot >= 0) {
data.opRemoveInput(op,stackPlaceholderSlot);
clearStackPlaceholderSlot();
}
}
FuncCallSpecs::FuncCallSpecs(PcodeOp *call_op)
: FuncProto(), activeinput(true), activeoutput(true)
{
effective_extrapop = ProtoModel::extrapop_unknown;
stackoffset = offset_unknown;
stackPlaceholderSlot = -1;
paramshift = 0;
op = call_op;
fd = (Funcdata *)0;
if (call_op->code() == CPUI_CALL) {
entryaddress = call_op->getIn(0)->getAddr();
if (entryaddress.getSpace()->getType() == IPTR_FSPEC) {
FuncCallSpecs *otherfc = FuncCallSpecs::getFspecFromConst(entryaddress);
entryaddress = otherfc->entryaddress;
}
}
isinputactive = false;
isoutputactive = false;
isbadjumptable = false;
}
void FuncCallSpecs::setFuncdata(Funcdata *f)
{
if (fd != (Funcdata *)0)
throw LowlevelError("Setting call spec function multiple times");
fd = f;
if (fd != (Funcdata *)0) {
entryaddress = fd->getAddress();
if (fd->getName().size() != 0)
name = fd->getName();
}
}
FuncCallSpecs *FuncCallSpecs::clone(PcodeOp *newop) const
{
FuncCallSpecs *res = new FuncCallSpecs(newop);
res->setFuncdata(fd);
res->effective_extrapop = effective_extrapop;
res->stackoffset = stackoffset;
res->paramshift = paramshift;
res->isbadjumptable = isbadjumptable;
res->copy(*this); return res;
}
Varnode *FuncCallSpecs::getSpacebaseRelative(void) const
{
if (stackPlaceholderSlot<0) return (Varnode *)0;
Varnode *tmpvn = op->getIn(stackPlaceholderSlot);
if (!tmpvn->isSpacebasePlaceholder()) return (Varnode *)0;
if (!tmpvn->isWritten()) return (Varnode *)0;
PcodeOp *loadop = tmpvn->getDef();
if (loadop->code() != CPUI_LOAD) return (Varnode *)0;
return loadop->getIn(1); }
Varnode *FuncCallSpecs::buildParam(Funcdata &data,Varnode *vn,ProtoParameter *param,Varnode *stackref)
{
if (vn == (Varnode *)0) { AddrSpace *spc = param->getAddress().getSpace();
uintb off = param->getAddress().getOffset();
int4 sz = param->getSize();
vn = data.opStackLoad(spc,off,sz,op,stackref,false);
return vn;
}
if (vn->getSize() == param->getSize()) return vn;
PcodeOp *newop = data.newOp(2,op->getAddr());
data.opSetOpcode(newop,CPUI_SUBPIECE);
Varnode *newout = data.newUniqueOut(param->getSize(),newop);
if (vn->isFree() && !vn->isConstant() && !vn->hasNoDescend())
vn = data.newVarnode(vn->getSize(),vn->getAddr());
data.opSetInput(newop,vn,0);
data.opSetInput(newop,data.newConstant(4,0),1);
data.opInsertBefore(newop,op);
return newout;
}
int4 FuncCallSpecs::transferLockedInputParam(ProtoParameter *param)
{
int4 numtrials = activeinput.getNumTrials();
Address startaddr = param->getAddress();
int4 sz = param->getSize();
Address lastaddr = startaddr + (sz-1);
for(int4 i=0;i<numtrials;++i) {
ParamTrial &curtrial( activeinput.getTrial(i) );
if (startaddr < curtrial.getAddress()) continue;
Address trialend = curtrial.getAddress() + (curtrial.getSize() - 1);
if (trialend < lastaddr) continue;
if (curtrial.isDefinitelyNotUsed()) return 0; return curtrial.getSlot();
}
if (startaddr.getSpace()->getType() == IPTR_SPACEBASE)
return -1;
return 0;
}
PcodeOp *FuncCallSpecs::transferLockedOutputParam(ProtoParameter *param)
{
Varnode *vn = op->getOut();
if (vn != (Varnode *)0) {
if (param->getAddress().justifiedContain(param->getSize(),vn->getAddr(),vn->getSize(),false)==0)
return op;
if (vn->getAddr().justifiedContain(vn->getSize(),param->getAddress(),param->getSize(),false)==0)
return op;
return (PcodeOp *)0;
}
PcodeOp *indop = op->previousOp();
while((indop!=(PcodeOp *)0)&&(indop->code()==CPUI_INDIRECT)) {
if (indop->isIndirectCreation()) {
vn = indop->getOut();
if (param->getAddress().justifiedContain(param->getSize(),vn->getAddr(),vn->getSize(),false)==0)
return indop;
if (vn->getAddr().justifiedContain(vn->getSize(),param->getAddress(),param->getSize(),false)==0)
return indop;
}
indop = indop->previousOp();
}
return (PcodeOp *)0;
}
bool FuncCallSpecs::transferLockedInput(vector<Varnode *> &newinput)
{
newinput.push_back(op->getIn(0)); int4 numparams = numParams();
Varnode *stackref = (Varnode *)0;
for(int4 i=0;i<numparams;++i) {
int4 reuse = transferLockedInputParam(getParam(i));
if (reuse == 0) return false;
if (reuse > 0)
newinput.push_back(op->getIn(reuse));
else {
if (stackref == (Varnode *)0)
stackref = getSpacebaseRelative();
if (stackref == (Varnode *)0)
return false;
newinput.push_back((Varnode *)0);
}
}
return true;
}
bool FuncCallSpecs::transferLockedOutput(Varnode *&newoutput)
{
ProtoParameter *param = getOutput();
if (param->getType()->getMetatype() == TYPE_VOID) {
newoutput = (Varnode *)0;
return true;
}
PcodeOp *outop = transferLockedOutputParam(param);
if (outop == (PcodeOp *)0)
newoutput = (Varnode *)0;
else
newoutput = outop->getOut();
return true;
}
void FuncCallSpecs::commitNewInputs(Funcdata &data,vector<Varnode *> &newinput)
{
if (!isInputLocked()) return;
Varnode *stackref = getSpacebaseRelative();
Varnode *placeholder = (Varnode *)0;
if (stackPlaceholderSlot>=0)
placeholder = op->getIn(stackPlaceholderSlot);
bool noplacehold = true;
stackPlaceholderSlot = -1;
int4 numPasses = activeinput.getNumPasses();
activeinput.clear();
int4 numparams = numParams();
for(int4 i=0;i<numparams;++i) {
ProtoParameter *param = getParam(i);
Varnode *vn = buildParam(data,newinput[1+i],param,stackref);
newinput[1+i] = vn;
activeinput.registerTrial(param->getAddress(),param->getSize());
activeinput.getTrial(i).markActive(); if (noplacehold&&(param->getAddress().getSpace()->getType() == IPTR_SPACEBASE)) {
vn->setSpacebasePlaceholder();
noplacehold = false; placeholder = (Varnode *)0; }
}
if (placeholder != (Varnode *)0) { newinput.push_back(placeholder); setStackPlaceholderSlot(newinput.size()-1);
}
data.opSetAllInput(op,newinput);
if (!isDotdotdot()) clearActiveInput(); else {
if (numPasses > 0)
activeinput.finishPass(); }
}
void FuncCallSpecs::commitNewOutputs(Funcdata &data,Varnode *newout)
{
if (!isOutputLocked()) return;
activeoutput.clear();
if (newout != (Varnode *)0) {
ProtoParameter *param = getOutput();
activeoutput.registerTrial(param->getAddress(),param->getSize());
PcodeOp *indop = newout->getDef();
if (newout->getSize() == param->getSize()) {
if (indop != op) {
data.opUnsetOutput(indop);
data.opUnlink(indop); data.opSetOutput(op,newout);
}
}
else if (newout->getSize() < param->getSize()) {
if (indop != op) {
data.opUninsert(indop);
data.opSetOpcode(indop,CPUI_SUBPIECE);
}
else {
indop = data.newOp(2,op->getAddr());
data.opSetOpcode(indop,CPUI_SUBPIECE);
data.opSetOutput(indop,newout); }
Varnode *realout = data.newVarnodeOut(param->getSize(),param->getAddress(),op);
data.opSetInput(indop,realout,0);
data.opSetInput(indop,data.newConstant(4,0),1);
data.opInsertAfter(indop,op);
}
else { VarnodeData vardata;
OpCode opc = assumedOutputExtension(param->getAddress(),param->getSize(),vardata);
Address hiaddr = newout->getAddr();
if (opc != CPUI_COPY) {
if (opc == CPUI_PIECE) { if (param->getType()->getMetatype() == TYPE_INT)
opc = CPUI_INT_SEXT;
else
opc = CPUI_INT_ZEXT;
}
if (indop != op) {
data.opUninsert(indop);
data.opRemoveInput(indop,1);
data.opSetOpcode(indop,opc);
Varnode *outvn = data.newVarnodeOut(param->getSize(),param->getAddress(),op);
data.opSetInput(indop,outvn,0);
data.opInsertAfter(indop,op);
}
else {
PcodeOp *extop = data.newOp(1,op->getAddr());
data.opSetOpcode(extop,opc);
data.opSetOutput(extop,newout); Varnode *outvn = data.newVarnodeOut(param->getSize(),param->getAddress(),op);
data.opSetInput(extop,outvn,0);
data.opInsertAfter(extop,op);
}
}
else { int4 hisz = newout->getSize() - param->getSize();
if (!newout->getAddr().getSpace()->isBigEndian())
hiaddr = hiaddr + param->getSize();
PcodeOp *newindop = data.newIndirectCreation(op,hiaddr,hisz,true);
if (indop != op) {
data.opUninsert(indop);
data.opSetOpcode(indop,CPUI_PIECE);
Varnode *outvn = data.newVarnodeOut(param->getSize(),param->getAddress(),op);
data.opSetInput(indop,newindop->getOut(),0);
data.opSetInput(indop,outvn,1);
data.opInsertAfter(indop,op);
}
else {
PcodeOp *concatop = data.newOp(2,op->getAddr());
data.opSetOpcode(concatop,CPUI_PIECE);
data.opSetOutput(concatop,newout); Varnode *outvn = data.newVarnodeOut(param->getSize(),param->getAddress(),op);
data.opSetInput(concatop,newindop->getOut(),0);
data.opSetInput(concatop,outvn,1);
data.opInsertAfter(concatop,op);
}
}
}
}
clearActiveOutput();
}
void FuncCallSpecs::initActiveInput(void)
{
isinputactive = true;
int4 maxdelay = getMaxInputDelay();
if (maxdelay > 0)
maxdelay = 3;
activeinput.setMaxPass(maxdelay);
}
bool FuncCallSpecs::checkInputJoin(int4 slot1,bool ishislot,Varnode *vn1,Varnode *vn2) const
{
if (isInputActive()) return false;
if (slot1 >= activeinput.getNumTrials()) return false; const ParamTrial *hislot,*loslot;
if (ishislot) { hislot = &activeinput.getTrialForInputVarnode(slot1);
loslot = &activeinput.getTrialForInputVarnode(slot1+1);
if (hislot->getSize() != vn1->getSize()) return false;
if (loslot->getSize() != vn2->getSize()) return false;
}
else {
loslot = &activeinput.getTrialForInputVarnode(slot1);
hislot = &activeinput.getTrialForInputVarnode(slot1+1);
if (loslot->getSize() != vn1->getSize()) return false;
if (hislot->getSize() != vn2->getSize()) return false;
}
return FuncProto::checkInputJoin(hislot->getAddress(),hislot->getSize(),loslot->getAddress(),loslot->getSize());
}
void FuncCallSpecs::doInputJoin(int4 slot1,bool ishislot)
{
if (isInputLocked())
throw LowlevelError("Trying to join parameters on locked function prototype");
const ParamTrial &trial1( activeinput.getTrialForInputVarnode(slot1) );
const ParamTrial &trial2( activeinput.getTrialForInputVarnode(slot1+1) );
const Address &addr1( trial1.getAddress() );
const Address &addr2( trial2.getAddress() );
Architecture *glb = getArch();
Address joinaddr;
if (ishislot)
joinaddr = glb->constructJoinAddress(glb->translate,addr1,trial1.getSize(),addr2,trial2.getSize());
else
joinaddr = glb->constructJoinAddress(glb->translate,addr2,trial2.getSize(),addr1,trial1.getSize());
activeinput.joinTrial(slot1,joinaddr,trial1.getSize()+trial2.getSize());
}
bool FuncCallSpecs::lateRestriction(const FuncProto &restrictedProto,vector<Varnode *> &newinput,Varnode *&newoutput)
{
if (!hasModel()) {
copy(restrictedProto);
return true;
}
if (!isCompatible(restrictedProto)) return false;
copy(restrictedProto); if (isDotdotdot() && (!isinputactive)) return false;
if (isInputLocked())
if (!transferLockedInput(newinput)) return false;
if (isOutputLocked())
if (!transferLockedOutput(newoutput)) return false;
return true;
}
void FuncCallSpecs::deindirect(Funcdata &data,Funcdata *newfd)
{
entryaddress = newfd->getAddress();
name = newfd->getName();
fd = newfd;
Varnode *vn = data.newVarnodeCallSpecs(this);
data.opSetInput(op,vn,0);
data.opSetOpcode(op,CPUI_CALL);
if (isOverride()) return;
data.getOverride().insertIndirectOverride(op->getAddr(),entryaddress);
vector<Varnode *> newinput;
Varnode *newoutput;
FuncProto &newproto( newfd->getFuncProto() );
if ((!newproto.isNoReturn())&&(!newproto.isInline())&&
lateRestriction(newproto,newinput,newoutput)) {
commitNewInputs(data,newinput);
commitNewOutputs(data,newoutput);
}
else {
data.setRestartPending(true);
}
}
void FuncCallSpecs::forceSet(Funcdata &data,const FuncProto &fp)
{
vector<Varnode *> newinput;
Varnode *newoutput;
FuncProto *newproto = new FuncProto();
newproto->copy(fp);
data.getOverride().insertProtoOverride(op->getAddr(),newproto);
if (lateRestriction(fp,newinput,newoutput)) {
commitNewInputs(data,newinput);
commitNewOutputs(data,newoutput);
}
else {
data.setRestartPending(true);
}
setInputLock(true);
setInputErrors(fp.hasInputErrors());
setOutputErrors(fp.hasOutputErrors());
}
void FuncCallSpecs::insertPcode(Funcdata &data)
{
int4 id = getInjectUponReturn();
if (id < 0) return; InjectPayload *payload = data.getArch()->pcodeinjectlib->getPayload(id);
list<PcodeOp *>::iterator iter = op->getBasicIter();
++iter;
data.doLiveInject(payload,op->getAddr(),op->getParent(),iter);
}
void FuncCallSpecs::collectOutputTrialVarnodes(vector<Varnode *> &trialvn)
{
if (op->getOut() != (Varnode *)0)
throw LowlevelError("Output of call was determined prematurely");
while(trialvn.size() < activeoutput.getNumTrials()) trialvn.push_back((Varnode *)0);
PcodeOp *indop = op->previousOp();
while(indop != (PcodeOp *)0) {
if (indop->code() != CPUI_INDIRECT) break;
if (indop->isIndirectCreation()) {
Varnode *vn = indop->getOut();
int4 index = activeoutput.whichTrial(vn->getAddr(),vn->getSize());
if (index >= 0) {
trialvn[index] = vn;
activeoutput.getTrial(index).setAddress(vn->getAddr(),vn->getSize());
}
}
indop = indop->previousOp();
}
}
void FuncCallSpecs::finalInputCheck(void)
{
AncestorRealistic ancestorReal;
for(int4 i=0;i<activeinput.getNumTrials();++i) {
ParamTrial &trial(activeinput.getTrial(i));
if (!trial.isActive()) continue;
if (!trial.hasCondExeEffect()) continue;
int4 slot = trial.getSlot();
if (!ancestorReal.execute(op,slot,&trial,false))
trial.markNoUse();
}
}
void FuncCallSpecs::checkInputTrialUse(Funcdata &data,AliasChecker &aliascheck)
{
if (op->isDead())
throw LowlevelError("Function call in dead code");
int4 maxancestor = data.getArch()->trim_recurse_max;
bool callee_pop = false;
int4 expop = 0;
if (hasModel()) {
callee_pop = (getModelExtraPop() == ProtoModel::extrapop_unknown);
if (callee_pop) {
expop = getExtraPop();
if ((expop==ProtoModel::extrapop_unknown)||(expop <=4))
callee_pop = false;
}
}
AncestorRealistic ancestorReal;
for(int4 i=0;i<activeinput.getNumTrials();++i) {
ParamTrial &trial(activeinput.getTrial(i));
if (trial.isChecked()) continue;
int4 slot = trial.getSlot();
Varnode *vn = op->getIn(slot);
if (vn->getSpace()->getType() == IPTR_SPACEBASE) {
if (aliascheck.hasLocalAlias(vn))
trial.markNoUse();
else if (!data.getFuncProto().getLocalRange().inRange(vn->getAddr(),1))
trial.markNoUse();
else if (callee_pop) {
if ((int4)(trial.getAddress().getOffset() + (trial.getSize()-1)) < expop)
trial.markActive();
else
trial.markNoUse();
}
else if (ancestorReal.execute(op,slot,&trial,false)) {
if (data.ancestorOpUse(maxancestor,vn,op,trial,0))
trial.markActive();
else
trial.markInactive();
}
else
trial.markNoUse(); }
else {
if (ancestorReal.execute(op,slot,&trial,true)) {
if (data.ancestorOpUse(maxancestor,vn,op,trial,0)) {
trial.markActive();
if (trial.hasCondExeEffect())
activeinput.markNeedsFinalCheck();
}
else
trial.markInactive();
}
else if (vn->isInput()) trial.markInactive();
else
trial.markNoUse(); }
if (trial.isDefinitelyNotUsed()) data.opSetInput(op,data.newConstant(vn->getSize(),0),slot);
}
}
void FuncCallSpecs::checkOutputTrialUse(Funcdata &data,vector<Varnode *> &trialvn)
{
collectOutputTrialVarnodes(trialvn);
for(int4 i=0;i<trialvn.size();++i) {
ParamTrial &curtrial(activeoutput.getTrial(i));
if (curtrial.isChecked())
throw LowlevelError("Output trial has been checked prematurely");
if (trialvn[i] != (Varnode *)0)
curtrial.markActive();
else
curtrial.markInactive(); }
}
void FuncCallSpecs::buildInputFromTrials(Funcdata &data)
{
AddrSpace *spc;
uintb off;
int4 sz;
bool isspacebase;
Varnode *vn;
vector<Varnode *> newparam;
newparam.push_back(op->getIn(0));
for(int4 i=0;i<activeinput.getNumTrials();++i) {
const ParamTrial ¶mtrial( activeinput.getTrial(i) );
if (!paramtrial.isUsed()) continue; sz = paramtrial.getSize();
isspacebase = false;
const Address &addr(paramtrial.getAddress());
spc = addr.getSpace();
off = addr.getOffset();
if (spc->getType() == IPTR_SPACEBASE) {
isspacebase = true;
off = spc->wrapOffset(stackoffset + off); }
if (paramtrial.isUnref()) { vn = data.newVarnode(sz,Address(spc,off)); }
else {
vn = op->getIn(paramtrial.getSlot()); if (vn->getSize() > sz) { Varnode *outvn; PcodeOp *newop = data.newOp(2,op->getAddr());
if (data.getArch()->translate->isBigEndian())
outvn = data.newVarnodeOut(sz,vn->getAddr()+(vn->getSize()-sz),newop);
else
outvn = data.newVarnodeOut(sz,vn->getAddr(),newop);
data.opSetOpcode(newop,CPUI_SUBPIECE);
data.opSetInput(newop,vn,0);
data.opSetInput(newop,data.newConstant(1,0),1);
data.opInsertBefore(newop,op);
vn = outvn;
}
}
newparam.push_back(vn);
if (isspacebase)
data.getScopeLocal()->markNotMapped(spc,off,sz,true);
}
data.opSetAllInput(op,newparam); activeinput.deleteUnusedTrials();
}
Varnode *FuncCallSpecs::findPreexistingWhole(Varnode *vn1,Varnode *vn2)
{
PcodeOp *op1 = vn1->loneDescend();
if (op1 == (PcodeOp *)0) return (Varnode *)0;
PcodeOp *op2 = vn2->loneDescend();
if (op2 == (PcodeOp *)0) return (Varnode *)0;
if (op1 != op2) return (Varnode *)0;
if (op1->code() != CPUI_PIECE) return (Varnode *)0;
return op1->getOut();
}
void FuncCallSpecs::buildOutputFromTrials(Funcdata &data,vector<Varnode *> &trialvn)
{
Varnode *finaloutvn;
vector<Varnode *> finalvn;
for(int4 i=0;i<activeoutput.getNumTrials();++i) { ParamTrial &curtrial(activeoutput.getTrial(i));
if (!curtrial.isUsed()) break;
Varnode *vn = trialvn[ curtrial.getSlot() - 1];
finalvn.push_back(vn);
}
activeoutput.deleteUnusedTrials(); if (activeoutput.getNumTrials()==0) return;
vector<PcodeOp *> deletedops;
if (activeoutput.getNumTrials()==1) { finaloutvn = finalvn[0];
PcodeOp *indop = finaloutvn->getDef();
deletedops.push_back(indop);
data.opSetOutput(op,finaloutvn); }
else if (activeoutput.getNumTrials()==2) {
Varnode *hivn = finalvn[1]; Varnode *lovn = finalvn[0];
if (data.isDoublePrecisOn()) {
lovn->setPrecisLo(); hivn->setPrecisHi();
}
deletedops.push_back(hivn->getDef());
deletedops.push_back(lovn->getDef());
finaloutvn = findPreexistingWhole(hivn,lovn);
if (finaloutvn == (Varnode *)0) {
Address joinaddr = data.getArch()->constructJoinAddress(data.getArch()->translate,
hivn->getAddr(),hivn->getSize(),
lovn->getAddr(),lovn->getSize());
finaloutvn = data.newVarnode(hivn->getSize()+lovn->getSize(),joinaddr);
data.opSetOutput(op,finaloutvn);
PcodeOp *sublo = data.newOp(2,op->getAddr());
data.opSetOpcode(sublo,CPUI_SUBPIECE);
data.opSetInput(sublo,finaloutvn,0);
data.opSetInput(sublo,data.newConstant(4,0),1);
data.opSetOutput(sublo,lovn);
data.opInsertAfter(sublo,op);
PcodeOp *subhi = data.newOp(2,op->getAddr());
data.opSetOpcode(subhi,CPUI_SUBPIECE);
data.opSetInput(subhi,finaloutvn,0);
data.opSetInput(subhi,data.newConstant(4,lovn->getSize()),1);
data.opSetOutput(subhi,hivn);
data.opInsertAfter(subhi,op);
}
else { deletedops.push_back(finaloutvn->getDef()); data.opSetOutput(op,finaloutvn);
}
}
else
return;
for(int4 i=0;i<deletedops.size();++i) { PcodeOp *dop = deletedops[i];
Varnode *in0 = dop->getIn(0);
Varnode *in1 = dop->getIn(1);
data.opDestroy(dop);
if (in0 != (Varnode *)0)
data.deleteVarnode(in0);
if (in1 != (Varnode *)0)
data.deleteVarnode(in1);
}
}
int4 FuncCallSpecs::getInputBytesConsumed(int4 slot) const
{
if (slot >= inputConsume.size())
return 0;
return inputConsume[slot];
}
bool FuncCallSpecs::setInputBytesConsumed(int4 slot,int4 val) const
{
while(inputConsume.size() <= slot)
inputConsume.push_back(0);
int4 oldVal = inputConsume[slot];
if (oldVal == 0 || val < oldVal)
inputConsume[slot] = val;
return (oldVal != val);
}
void FuncCallSpecs::paramshiftModifyStart(void)
{
if (paramshift==0) return;
paramShift(paramshift);
}
bool FuncCallSpecs::paramshiftModifyStop(Funcdata &data)
{
if (paramshift == 0) return false;
if (isParamshiftApplied()) return false;
setParamshiftApplied(true);
if (op->numInput() < paramshift + 1)
throw LowlevelError("Paramshift mechanism is confused");
for(int4 i=0;i<paramshift;++i) {
data.opRemoveInput(op,1);
removeParam(0);
}
return true;
}
uint4 FuncCallSpecs::hasEffectTranslate(const Address &addr,int4 size) const
{
AddrSpace *spc = addr.getSpace();
if (spc->getType() != IPTR_SPACEBASE)
return hasEffect(addr,size);
if (stackoffset == offset_unknown) return EffectRecord::unknown_effect;
uintb newoff = spc->wrapOffset(addr.getOffset()-stackoffset); return hasEffect(Address(spc,newoff),size);
}
void FuncCallSpecs::countMatchingCalls(const vector<FuncCallSpecs *> &qlst)
{
vector<FuncCallSpecs *> copyList(qlst);
sort(copyList.begin(),copyList.end(),compareByEntryAddress);
int4 i;
for(i=0;i<copyList.size();++i) {
if (!copyList[i]->entryaddress.isInvalid()) break;
copyList[i]->matchCallCount = 1; }
if (i == copyList.size()) return;
Address lastAddr = copyList[i]->entryaddress;
int4 lastChange = i++;
int4 num;
for(;i<copyList.size();++i) {
if (copyList[i]->entryaddress == lastAddr) continue;
num = i - lastChange;
for(;lastChange<i;++lastChange)
copyList[lastChange]->matchCallCount = num;
lastAddr = copyList[i]->entryaddress;
}
num = i - lastChange;
for(;lastChange<i;++lastChange)
copyList[lastChange]->matchCallCount = num;
}