#include "globalcontext.hh"
ContextBitRange::ContextBitRange(int4 sbit,int4 ebit)
{
word = sbit/(8*sizeof(uintm));
startbit = sbit - word*8*sizeof(uintm);
endbit = ebit - word*8*sizeof(uintm);
shift = 8*sizeof(uintm)-endbit-1;
mask = (~((uintm)0))>>(startbit+shift);
}
void TrackedContext::saveXml(ostream &s) const
{
s << "<set";
loc.space->saveXmlAttributes(s,loc.offset,loc.size);
a_v_u(s,"val",val);
s << "/>\n";
}
void TrackedContext::restoreXml(const Element *el,const AddrSpaceManager *manage)
{
int4 size;
Address addr = Address::restoreXml(el,manage,size);
istringstream s(el->getAttributeValue("val"));
s.unsetf(ios::dec | ios::hex | ios::oct);
s >> val;
loc.space = addr.getSpace();
loc.offset = addr.getOffset();
loc.size = size;
}
void ContextDatabase::saveTracked(ostream &s,const Address &addr,
const TrackedSet &vec)
{
if (vec.empty()) return;
s << "<tracked_pointset";
addr.getSpace()->saveXmlAttributes(s,addr.getOffset() );
s << ">\n";
for(int4 i=0;i<vec.size();++i) {
s << " ";
vec[i].saveXml(s);
}
s << "</tracked_pointset>\n";
}
void ContextDatabase::restoreTracked(const Element *el,const AddrSpaceManager *manage,
TrackedSet &vec)
{
vec.clear(); const List &list(el->getChildren());
List::const_iterator iter = list.begin();
while(iter != list.end()) {
const Element *subel = *iter;
vec.emplace_back();
vec.back().restoreXml(subel,manage);
++iter;
}
}
void ContextDatabase::setVariableDefault(const string &nm,uintm val)
{
ContextBitRange &var( getVariable(nm) );
var.setValue(getDefaultValue(),val);
}
uintm ContextDatabase::getDefaultValue(const string &nm) const
{
const ContextBitRange &var( getVariable(nm) );
return var.getValue(getDefaultValue());
}
void ContextDatabase::setVariable(const string &nm,const Address &addr,
uintm value)
{
const ContextBitRange &bitrange( getVariable(nm) );
int4 num = bitrange.getWord();
uintm mask = bitrange.getMask()<<bitrange.getShift();
vector<uintm *> contvec;
getRegionToChangePoint(contvec,addr,num,mask);
for(uint4 i=0;i<contvec.size();++i)
bitrange.setValue(contvec[i],value);
}
uintm ContextDatabase::getVariable(const string &nm,const Address &addr) const
{
const ContextBitRange &bitrange( getVariable(nm) );
const uintm *context = getContext(addr);
return bitrange.getValue(context);
}
void ContextDatabase::setContextChangePoint(const Address &addr,int4 num,uintm mask,uintm value)
{
vector<uintm *> contvec;
getRegionToChangePoint(contvec,addr,num,mask);
for(uint4 i=0;i<contvec.size();++i) {
uintm *newcontext = contvec[i];
uintm val = newcontext[ num ];
val &= ~mask; val |= value;
newcontext[ num ] = val;
}
}
void ContextDatabase::setContextRegion(const Address &addr1,const Address &addr2,
int4 num,uintm mask,uintm value)
{
vector<uintm *> vec;
getRegionForSet(vec,addr1,addr2,num,mask);
for(uint4 i=0;i<vec.size();++i)
vec[i][num] = (vec[i][num] & ~mask) | value;
}
void ContextDatabase::setVariableRegion(const string &nm,
const Address &begad,
const Address &endad,
uintm value)
{
const ContextBitRange &bitrange( getVariable(nm) );
vector<uintm *> vec;
getRegionForSet(vec,begad,endad,bitrange.getWord(),bitrange.getMask() << bitrange.getShift());
for(int4 i=0;i<vec.size();++i)
bitrange.setValue(vec[i],value);
}
uintb ContextDatabase::getTrackedValue(const VarnodeData &mem,const Address &point) const
{
const TrackedSet &tset(getTrackedSet(point));
uintb endoff = mem.offset + mem.size - 1;
uintb tendoff;
for(int4 i=0;i<tset.size();++i) {
const TrackedContext &tcont( tset[i] );
if (tcont.loc.space != mem.space) continue;
if (tcont.loc.offset > mem.offset) continue;
tendoff = tcont.loc.offset + tcont.loc.size - 1;
if (tendoff < endoff) continue;
uintb res = tcont.val;
if (tcont.loc.space->isBigEndian()) {
if (endoff != tendoff)
res >>= (8* (tendoff - mem.offset));
}
else {
if (mem.offset != tcont.loc.offset)
res >>= (8* (mem.offset-tcont.loc.offset));
}
res &= calc_mask( mem.size ); return res;
}
return (uintb)0;
}
void ContextInternal::FreeArray::reset(int4 sz)
{
uintm *newarray = (uintm *)0;
uintm *newmask = (uintm *)0;
if (sz != 0) {
newarray = new uintm[sz];
newmask = new uintm[sz];
int4 min;
if (sz > size) {
min = size;
for(int4 i=min;i<sz;++i) {
newarray[i] = 0; newmask[i] = 0;
}
}
else
min = sz;
for(int4 i=0;i<min;++i) { newarray[i] = array[i];
newmask[i] = mask[i];
}
}
if (size!=0) {
delete [] array;
delete [] mask;
}
array = newarray;
mask = newmask;
size = sz;
}
ContextInternal::FreeArray &ContextInternal::FreeArray::operator=(const FreeArray &op2)
{
if (size!=0) {
delete [] array;
delete [] mask;
}
array = (uintm *)0;
mask = (uintm *)0;
size = op2.size;
if (size != 0) {
array = new uintm[size];
mask = new uintm[size];
for(int4 i=0;i<size;++i) {
array[i] = op2.array[i]; mask[i] = 0; }
}
return *this;
}
void ContextInternal::saveContext(ostream &s,const Address &addr,
const uintm *vec) const
{
s << "<context_pointset";
addr.getSpace()->saveXmlAttributes(s,addr.getOffset() );
s << ">\n";
map<string,ContextBitRange>::const_iterator iter;
for(iter=variables.begin();iter!=variables.end();++iter) {
uintm val = (*iter).second.getValue(vec);
s << " <set";
a_v(s,"name",(*iter).first);
a_v_u(s,"val",val);
s << "/>\n";
}
s << "</context_pointset>\n";
}
void ContextInternal::restoreContext(const Element *el,const Address &addr1,const Address &addr2)
{
const List &list(el->getChildren());
List::const_iterator iter = list.begin();
while(iter != list.end()) {
const Element *subel = *iter;
istringstream s(subel->getAttributeValue("val"));
s.unsetf(ios::dec | ios::hex | ios::oct);
uintm val;
s >> val;
ContextBitRange &var(getVariable(subel->getAttributeValue("name")));
vector<uintm *> vec;
if (addr1.isInvalid()) { uintm *defaultBuffer = getDefaultValue();
for(int4 i=0;i<size;++i)
defaultBuffer[i] = 0;
vec.push_back(defaultBuffer);
}
else
getRegionForSet(vec,addr1,addr2,var.getWord(),var.getMask()<<var.getShift());
for(int4 i=0;i<vec.size();++i)
var.setValue(vec[i],val);
++iter;
}
}
void ContextInternal::registerVariable(const string &nm,int4 sbit,int4 ebit)
{
if (!database.empty())
throw LowlevelError("Cannot register new context variables after database is initialized");
ContextBitRange bitrange(sbit,ebit);
int4 sz = sbit/(8*sizeof(uintm)) + 1;
if ((ebit/(8*sizeof(uintm)) + 1) != sz)
throw LowlevelError("Context variable does not fit in one word");
if (sz > size) {
size = sz;
database.defaultValue().reset(size);
}
variables[nm] = bitrange;
}
ContextBitRange &ContextInternal::getVariable(const string &nm)
{
map<string,ContextBitRange>::iterator iter;
iter = variables.find(nm);
if (iter == variables.end())
throw LowlevelError("Non-existent context variable: "+nm);
return (*iter).second;
}
const ContextBitRange &ContextInternal::getVariable(const string &nm) const
{
map<string,ContextBitRange>::const_iterator iter;
iter = variables.find(nm);
if (iter == variables.end())
throw LowlevelError("Non-existent context variable: "+nm);
return (*iter).second;
}
const uintm *ContextInternal::getContext(const Address &addr,
uintb &first,uintb &last) const
{
int4 valid;
Address before,after;
const uintm *res = database.bounds(addr,before,after,valid).array;
if (((valid&1)!=0)||(before.getSpace() != addr.getSpace()))
first = 0;
else
first = before.getOffset();
if (((valid&2)!=0)||(after.getSpace() != addr.getSpace()))
last = addr.getSpace()->getHighest();
else
last = after.getOffset()-1;
return res;
}
void ContextInternal::getRegionForSet(vector<uintm *> &res,const Address &addr1,const Address &addr2,
int4 num,uintm mask)
{
database.split(addr1);
partmap<Address,FreeArray>::iterator aiter,biter;
aiter = database.begin(addr1);
if (!addr2.isInvalid()) {
database.split(addr2);
biter = database.begin(addr2);
}
else
biter = database.end();
while(aiter != biter) {
uintm *context = (*aiter).second.array;
uintm *maskPtr = (*aiter).second.mask;
res.push_back(context);
maskPtr[num] |= mask; ++aiter;
}
}
void ContextInternal::getRegionToChangePoint(vector<uintm *> &res,const Address &addr,int4 num,uintm mask)
{
database.split(addr);
partmap<Address,FreeArray>::iterator aiter,biter;
uintm *maskArray,*vecArray;
aiter = database.begin(addr);
biter = database.end();
if (aiter == biter) return;
vecArray = (*aiter).second.array;
res.push_back(vecArray);
maskArray = (*aiter).second.mask;
maskArray[num] |= mask;
++aiter;
while(aiter != biter) {
vecArray = (*aiter).second.array;
maskArray = (*aiter).second.mask;
if ((maskArray[num] & mask) != 0) break; res.push_back(vecArray);
++aiter;
}
}
TrackedSet &ContextInternal::createSet(const Address &addr1,const Address &addr2)
{
TrackedSet &res(trackbase.clearRange(addr1,addr2));
res.clear();
return res;
}
void ContextInternal::saveXml(ostream &s) const
{
if (database.empty() && trackbase.empty()) return;
s << "<context_points>\n";
partmap<Address,FreeArray>::const_iterator fiter,fenditer;
fiter = database.begin();
fenditer = database.end();
for(;fiter!=fenditer;++fiter) saveContext(s,(*fiter).first,(*fiter).second.array);
partmap<Address,TrackedSet>::const_iterator titer,tenditer;
titer = trackbase.begin();
tenditer = trackbase.end();
for(;titer!=tenditer;++titer)
saveTracked(s,(*titer).first,(*titer).second);
s << "</context_points>\n";
}
void ContextInternal::restoreXml(const Element *el,const AddrSpaceManager *manage)
{
const List &list(el->getChildren());
List::const_iterator iter = list.begin();
while(iter != list.end()) {
const Element *subel = *iter;
if (subel->getName() == "context_pointset") {
if (subel->getNumAttributes()==0) {
restoreContext(subel,Address(),Address()); }
else {
Address addr = Address::restoreXml(subel,manage);
restoreContext(subel,addr,Address());
}
}
else if (subel->getName() == "tracked_pointset") {
Address addr = Address::restoreXml(subel,manage);
restoreTracked(subel,manage,trackbase.split(addr) );
}
else
throw LowlevelError("Bad <context_points> tag: "+subel->getName());
++iter;
}
}
void ContextInternal::restoreFromSpec(const Element *el,const AddrSpaceManager *manage)
{
const List &list(el->getChildren());
List::const_iterator iter = list.begin();
while(iter != list.end()) {
const Element *subel = *iter;
if (subel->getName() == "context_set") {
Range range;
range.restoreXml(subel,manage); Address addr1,addr2;
addr1 = range.getFirstAddr();
addr2 = range.getLastAddrOpen(manage);
restoreContext(subel,addr1,addr2);
}
else if (subel->getName() == "tracked_set") {
Range range;
range.restoreXml(subel,manage); Address addr1,addr2;
addr1 = range.getFirstAddr();
addr2 = range.getLastAddrOpen(manage);
restoreTracked(subel,manage,createSet(addr1,addr2));
}
else
throw LowlevelError("Bad <context_data> tag: "+subel->getName());
++iter;
}
}
ContextCache::ContextCache(ContextDatabase *db)
{
database = db;
curspace = (AddrSpace *)0; allowset = true;
}
void ContextCache::getContext(const Address &addr,uintm *buf) const
{
if ((addr.getSpace()!=curspace)||(first>addr.getOffset())||(last<addr.getOffset())) {
curspace = addr.getSpace();
context = database->getContext(addr,first,last);
}
for(int4 i=0;i<database->getContextSize();++i)
buf[i] = context[i];
}
void ContextCache::setContext(const Address &addr,int4 num,uintm mask,uintm value)
{
if (!allowset) return;
database->setContextChangePoint(addr,num,mask,value);
if ((addr.getSpace()==curspace)&&(first<=addr.getOffset())&&(last>=addr.getOffset()))
curspace = (AddrSpace *)0; }
void ContextCache::setContext(const Address &addr1,const Address &addr2,int4 num,uintm mask,uintm value)
{
if (!allowset) return;
database->setContextRegion(addr1,addr2,num,mask,value);
if ((addr1.getSpace()==curspace)&&(first<=addr1.getOffset())&&(last>=addr1.getOffset()))
curspace = (AddrSpace *)0; if ((first<=addr2.getOffset())&&(last>=addr2.getOffset()))
curspace = (AddrSpace *)0; if ((first>=addr1.getOffset())&&(first<=addr2.getOffset()))
curspace = (AddrSpace *)0; }