/* ### * IP: GHIDRA * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "globalcontext.hh" /// Bits within the whole context blob are labeled starting with 0 as the most significant bit /// in the first word in the sequence. The new context value must be contained within a single /// word. /// \param sbit is the starting (most significant) bit of the new value /// \param ebit is the ending (least significant) bit of the new value 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); } /// The register storage and value are serialized as a \ tag. /// \param s is the output stream void TrackedContext::saveXml(ostream &s) const { s << "saveXmlAttributes(s,loc.offset,loc.size); a_v_u(s,"val",val); s << "/>\n"; } /// Read a \ tag to fill in the storage and value details /// \param el is the root \ tag /// \param manage is the manager used to decode address references 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; } /// \brief Save all tracked register values for a specific address to an XML stream /// /// Encode all the tracked register values associated with a specific target address /// as a \ tag. /// \param s is the output stream /// \param addr is the specific address we have tracked values for /// \param vec is the list of tracked values void ContextDatabase::saveTracked(ostream &s,const Address &addr, const TrackedSet &vec) { if (vec.empty()) return; s << "saveXmlAttributes(s,addr.getOffset() ); s << ">\n"; for(int4 i=0;i\n"; } /// \brief Restore a sequence of tracked register values from an XML stream /// /// Given a root \ tag, decode each child in turn populating a list of /// TrackedContext objects. /// \param el is the root tag /// \param manage is used to resolve address space references /// \param vec is the container that will hold the new TrackedContext objects void ContextDatabase::restoreTracked(const Element *el,const AddrSpaceManager *manage, TrackedSet &vec) { vec.clear(); // Clear out any old stuff 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; } } /// The default value is returned for addresses that have not been overlaid with other values. /// \param nm is the name of the context variable /// \param val is the default value to establish void ContextDatabase::setVariableDefault(const string &nm,uintm val) { ContextBitRange &var( getVariable(nm) ); var.setValue(getDefaultValue(),val); } /// This will return the default value used for addresses that have not been overlaid with other values. /// \param nm is the name of the context variable /// \return the variable's default value uintm ContextDatabase::getDefaultValue(const string &nm) const { const ContextBitRange &var( getVariable(nm) ); return var.getValue(getDefaultValue()); } /// The variable will be changed to the new value, starting at the given address up to the next /// point of change. /// \param nm is the name of the context variable /// \param addr is the given address /// \param value is the new value to set void ContextDatabase::setVariable(const string &nm,const Address &addr, uintm value) { const ContextBitRange &bitrange( getVariable(nm) ); int4 num = bitrange.getWord(); uintm mask = bitrange.getMask()< contvec; getRegionToChangePoint(contvec,addr,num,mask); for(uint4 i=0;i contvec; getRegionToChangePoint(contvec,addr,num,mask); for(uint4 i=0;i vec; getRegionForSet(vec,addr1,addr2,num,mask); for(uint4 i=0;i vec; getRegionForSet(vec,begad,endad,bitrange.getWord(),bitrange.getMask() << bitrange.getShift()); for(int4 i=0;i mem.offset) continue; tendoff = tcont.loc.offset + tcont.loc.size - 1; if (tendoff < endoff) continue; uintb res = tcont.val; // If we have proper containment, trim value based on endianness 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 ); // Final trim based on size return res; } return (uintb)0; } /// The "array of words" and mask array are resized to the given value. Old values are preserved, /// chopping off the last values, or appending zeroes, as needed. /// \param sz is the new number of words to resize array to 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 tags within a parent \ tag. /// \param s is the output stream /// \param addr is the address of the split point where the blob is valid /// \param vec is the array of words holding the blob values void ContextInternal::saveContext(ostream &s,const Address &addr, const uintm *vec) const { s << "saveXmlAttributes(s,addr.getOffset() ); s << ">\n"; map::const_iterator iter; for(iter=variables.begin();iter!=variables.end();++iter) { uintm val = (*iter).second.getValue(vec); s << " \n"; } s << "\n"; } /// \brief Restore a context blob for given address range from an XML tag /// /// The tag can be either \ or \. In either case, /// children are parsed to get context variable values. Then a context blob is /// reconstructed from the values. The new blob is added to the interval map based /// on the address range. If the start address is invalid, the default value of /// the context variables are painted. The second address can be invalid, if /// only a split point is known. /// \param el is the root XML tag /// \param addr1 is the starting address of the given range /// \param addr2 is the ending address of the given range 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 vec; if (addr1.isInvalid()) { // Invalid addr1, indicates we should set default value uintm *defaultBuffer = getDefaultValue(); for(int4 i=0;i size) { size = sz; database.defaultValue().reset(size); } variables[nm] = bitrange; } ContextBitRange &ContextInternal::getVariable(const string &nm) { map::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::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 &res,const Address &addr1,const Address &addr2, int4 num,uintm mask) { database.split(addr1); partmap::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; // Mark that this value is being definitely set ++aiter; } } void ContextInternal::getRegionToChangePoint(vector &res,const Address &addr,int4 num,uintm mask) { database.split(addr); partmap::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; // Reached point where this value was definitively set before 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 << "\n"; partmap::const_iterator fiter,fenditer; fiter = database.begin(); fenditer = database.end(); for(;fiter!=fenditer;++fiter) // Save context at each changepoint saveContext(s,(*fiter).first,(*fiter).second.array); partmap::const_iterator titer,tenditer; titer = trackbase.begin(); tenditer = trackbase.end(); for(;titer!=tenditer;++titer) saveTracked(s,(*titer).first,(*titer).second); s << "\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()); // Restore the default value } 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 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); // There MUST be a range 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); // There MUST be a range Address addr1,addr2; addr1 = range.getFirstAddr(); addr2 = range.getLastAddrOpen(manage); restoreTracked(subel,manage,createSet(addr1,addr2)); } else throw LowlevelError("Bad tag: "+subel->getName()); ++iter; } } /// \param db is the context database that will be encapsulated ContextCache::ContextCache(ContextDatabase *db) { database = db; curspace = (AddrSpace *)0; // Mark cache as invalid allowset = true; } /// Check if the address is in the current valid range. If it is, return the cached /// blob. Otherwise, make a call to the database and cache a new block and valid range. /// \param addr is the given address /// \param buf is where the blob should be stored void ContextCache::getContext(const Address &addr,uintm *buf) const { if ((addr.getSpace()!=curspace)||(first>addr.getOffset())||(lastgetContext(addr,first,last); } for(int4 i=0;igetContextSize();++i) buf[i] = context[i]; } /// \brief Change the value of a context variable at the given address with no bound /// /// The context value is set starting at the given address and \e paints memory up /// to the next explicit change point. /// \param addr is the given starting address /// \param num is the word index of the context variable /// \param mask is the mask delimiting the context variable /// \param value is the (already shifted) value to set 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; // Invalidate cache } /// \brief Change the value of a context variable across an explicit address range /// /// The context value is \e painted across the range. The context variable is marked as /// explicitly changing at the starting address of the range. /// \param addr1 is the starting address of the given range /// \param addr2 is the ending address of the given range /// \param num is the word index of the context variable /// \param mask is the mask delimiting the context variable /// \param value is the (already shifted) value to set 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; // Invalidate cache if ((first<=addr2.getOffset())&&(last>=addr2.getOffset())) curspace = (AddrSpace *)0; // Invalidate cache if ((first>=addr1.getOffset())&&(first<=addr2.getOffset())) curspace = (AddrSpace *)0; // Invalidate cache }