/* ### * 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" namespace ghidra { ElementId ELEM_CONTEXT_DATA = ElementId("context_data",120); ElementId ELEM_CONTEXT_POINTS = ElementId("context_points",121); ElementId ELEM_CONTEXT_POINTSET = ElementId("context_pointset",122); ElementId ELEM_CONTEXT_SET = ElementId("context_set",123); ElementId ELEM_SET = ElementId("set",124); ElementId ELEM_TRACKED_POINTSET = ElementId("tracked_pointset",125); ElementId ELEM_TRACKED_SET = ElementId("tracked_set",126); /// 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 encoded as a \ element. /// \param encoder is the stream encoder void TrackedContext::encode(Encoder &encoder) const { encoder.openElement(ELEM_SET); loc.space->encodeAttributes(encoder,loc.offset,loc.size); encoder.writeUnsignedInteger(ATTRIB_VAL, val); encoder.closeElement(ELEM_SET); } /// Parse a \ element to fill in the storage and value details. /// \param decoder is the stream decoder void TrackedContext::decode(Decoder &decoder) { uint4 elemId = decoder.openElement(ELEM_SET); loc.decodeFromAttributes(decoder); val = decoder.readUnsignedInteger(ATTRIB_VAL); decoder.closeElement(elemId); } /// \brief Encode all tracked register values for a specific address to a stream /// /// Encode all the tracked register values associated with a specific target address /// as a \ tag. /// \param encoder is the stream encoder /// \param addr is the specific address we have tracked values for /// \param vec is the list of tracked values void ContextDatabase::encodeTracked(Encoder &encoder,const Address &addr,const TrackedSet &vec) { if (vec.empty()) return; encoder.openElement(ELEM_TRACKED_POINTSET); addr.getSpace()->encodeAttributes(encoder,addr.getOffset() ); for(int4 i=0;i element, decoding each child in turn to populate a list of /// TrackedContext objects. /// \param decoder is the given stream decoder /// \param vec is the container that will hold the new TrackedContext objects void ContextDatabase::decodeTracked(Decoder &decoder,TrackedSet &vec) { vec.clear(); // Clear out any old stuff while(decoder.peekElement() != 0) { vec.emplace_back(); vec.back().decode(decoder); } } /// 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 elements within a parent \ element. /// \param encoder is the stream encoder /// \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::encodeContext(Encoder &encoder,const Address &addr,const uintm *vec) const { encoder.openElement(ELEM_CONTEXT_POINTSET); addr.getSpace()->encodeAttributes(encoder,addr.getOffset() ); map::const_iterator iter; for(iter=variables.begin();iter!=variables.end();++iter) { uintm val = (*iter).second.getValue(vec); encoder.openElement(ELEM_SET); encoder.writeString(ATTRIB_NAME, (*iter).first); encoder.writeUnsignedInteger(ATTRIB_VAL, val); encoder.closeElement(ELEM_SET); } encoder.closeElement(ELEM_CONTEXT_POINTSET); } /// \brief Restore a context blob for given address range from a stream decoder /// /// Parse either a \ or \ element. 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 decoder is the stream decoder /// \param addr1 is the starting address of the given range /// \param addr2 is the ending address of the given range void ContextInternal::decodeContext(Decoder &decoder,const Address &addr1,const Address &addr2) { for(;;) { uint4 subId = decoder.openElement(); if (subId != ELEM_SET) break; uintm val = decoder.readUnsignedInteger(ATTRIB_VAL); ContextBitRange &var(getVariable(decoder.readString(ATTRIB_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::encode(Encoder &encoder) const { if (database.empty() && trackbase.empty()) return; encoder.openElement(ELEM_CONTEXT_POINTS); partmap::const_iterator fiter,fenditer; fiter = database.begin(); fenditer = database.end(); for(;fiter!=fenditer;++fiter) // Save context at each changepoint encodeContext(encoder,(*fiter).first,(*fiter).second.array); partmap::const_iterator titer,tenditer; titer = trackbase.begin(); tenditer = trackbase.end(); for(;titer!=tenditer;++titer) encodeTracked(encoder,(*titer).first,(*titer).second); encoder.closeElement(ELEM_CONTEXT_POINTS); } void ContextInternal::decode(Decoder &decoder) { uint4 elemId = decoder.openElement(ELEM_CONTEXT_POINTS); for(;;) { uint4 subId = decoder.openElement(); if (subId == 0) break; if (subId == ELEM_CONTEXT_POINTSET) { uint4 attribId = decoder.getNextAttributeId(); decoder.rewindAttributes(); if (attribId==0) { decodeContext(decoder,Address(),Address()); // Restore the default value } else { VarnodeData vData; vData.decodeFromAttributes(decoder); decodeContext(decoder,vData.getAddr(),Address()); } } else if (subId == ELEM_TRACKED_POINTSET) { VarnodeData vData; vData.decodeFromAttributes(decoder); decodeTracked(decoder,trackbase.split(vData.getAddr()) ); } else throw LowlevelError("Bad tag"); decoder.closeElement(subId); } decoder.closeElement(elemId); } void ContextInternal::decodeFromSpec(Decoder &decoder) { uint4 elemId = decoder.openElement(ELEM_CONTEXT_DATA); for(;;) { uint4 subId = decoder.openElement(); if (subId == 0) break; Range range; range.decodeFromAttributes(decoder); // There MUST be a range Address addr1 = range.getFirstAddr(); Address addr2 = range.getLastAddrOpen(decoder.getAddrSpaceManager()); if (subId == ELEM_CONTEXT_SET) { decodeContext(decoder,addr1,addr2); } else if (subId == ELEM_TRACKED_SET) { decodeTracked(decoder,createSet(addr1,addr2)); } else throw LowlevelError("Bad tag"); decoder.closeElement(subId); } decoder.closeElement(elemId); } /// \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 } } // End namespace ghidra