/* ### * 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 "funcdata.hh" namespace ghidra { AttributeId ATTRIB_NOCODE = AttributeId("nocode",84); ElementId ELEM_AST = ElementId("ast",115); ElementId ELEM_FUNCTION = ElementId("function",116); ElementId ELEM_HIGHLIST = ElementId("highlist",117); ElementId ELEM_JUMPTABLELIST = ElementId("jumptablelist",118); ElementId ELEM_VARNODES = ElementId("varnodes",119); /// \param nm is the (base) name of the function, as a formal symbol /// \param disp is the name used when displaying the function name in output /// \param scope is Symbol scope associated with the function /// \param addr is the entry address for the function /// \param sym is the symbol representing the function /// \param sz is the number of bytes (of code) in the function body Funcdata::Funcdata(const string &nm,const string &disp,Scope *scope,const Address &addr,FunctionSymbol *sym,int4 sz) : baseaddr(addr), funcp(), vbank(scope->getArch()), heritage(this), covermerge(*this) { // Initialize high-level properties of // function by giving address and size functionSymbol = sym; flags = 0; clean_up_index = 0; high_level_index = 0; cast_phase_index = 0; glb = scope->getArch(); minLanedSize = glb->getMinimumLanedRegisterSize(); name = nm; displayName = disp; size = sz; AddrSpace *stackid = glb->getStackSpace(); if (nm.size()==0) localmap = (ScopeLocal *)0; // Filled in by decode else { uint8 id; if (sym != (FunctionSymbol *)0) id = sym->getId(); else { // Missing a symbol, build unique id based on address id = 0x57AB12CD; id = (id << 32) | (addr.getOffset() & 0xffffffff); } ScopeLocal *newMap = new ScopeLocal(id,stackid,this,glb); glb->symboltab->attachScope(newMap,scope); // This may throw and delete newMap localmap = newMap; funcp.setScope(localmap,baseaddr+ -1); localmap->resetLocalWindow(); } activeoutput = (ParamActive *)0; #ifdef OPACTION_DEBUG jtcallback = (void (*)(Funcdata &orig,Funcdata &fd))0; opactdbg_count = 0; opactdbg_breakcount = -1; opactdbg_on = false; opactdbg_breakon = false; opactdbg_active = false; #endif } void Funcdata::clear(void) { // Clear everything associated with decompilation (analysis) flags &= ~(highlevel_on|blocks_generated|processing_started|typerecovery_start|typerecovery_on| double_precis_on|restart_pending); clean_up_index = 0; high_level_index = 0; cast_phase_index = 0; minLanedSize = glb->getMinimumLanedRegisterSize(); localmap->clearUnlocked(); // Clear non-permanent stuff localmap->resetLocalWindow(); clearActiveOutput(); funcp.clearUnlockedOutput(); // Inputs are cleared by localmap unionMap.clear(); clearBlocks(); obank.clear(); vbank.clear(); clearCallSpecs(); clearJumpTables(); // Do not clear overrides heritage.clear(); covermerge.clear(); #ifdef OPACTION_DEBUG opactdbg_count = 0; #endif } /// The comment is added to the global database, indexed via its placement address and /// the entry address of the function. The emitter will attempt to place the comment /// before the source expression that maps most closely to the address. /// \param txt is the string body of the comment /// \param ad is the placement address void Funcdata::warning(const string &txt,const Address &ad) const { string msg; if ((flags & jumptablerecovery_on)!=0) msg = "WARNING (jumptable): "; else msg = "WARNING: "; msg += txt; glb->commentdb->addCommentNoDuplicate(Comment::warning,baseaddr,ad,msg); } /// The warning will be emitted as part of the block comment printed right before the /// prototype. The comment is stored in the global comment database, indexed via the function's /// entry address. /// \param txt is the string body of the comment void Funcdata::warningHeader(const string &txt) const { string msg; if ((flags & jumptablerecovery_on)!=0) msg = "WARNING (jumptable): "; else msg = "WARNING: "; msg += txt; glb->commentdb->addCommentNoDuplicate(Comment::warningheader,baseaddr,baseaddr,msg); } /// This routine does basic set-up for analyzing the function. In particular, it /// generates the raw p-code, builds basic blocks, and generates the call specification /// objects. void Funcdata::startProcessing(void) { if ((flags & processing_started)!=0) throw LowlevelError("Function processing already started"); flags |= processing_started; if (funcp.isInline()) warningHeader("This is an inlined function"); localmap->clearUnlocked(); funcp.clearUnlockedOutput(); Address baddr(baseaddr.getSpace(),0); Address eaddr(baseaddr.getSpace(),~((uintb)0)); followFlow(baddr,eaddr); structureReset(); sortCallSpecs(); // Must come after structure reset heritage.buildInfoList(); localoverride.applyDeadCodeDelay(*this); } void Funcdata::stopProcessing(void) { flags |= processing_complete; obank.destroyDead(); // Free up anything in the dead list #ifdef CPUI_STATISTICS glb->stats->process(*this); #endif } bool Funcdata::startTypeRecovery(void) { if ((flags & typerecovery_start)!=0) return false; // Already started flags |= typerecovery_start; return true; } Funcdata::~Funcdata(void) { // clear(); if (localmap != (ScopeLocal *)0) glb->symboltab->deleteScope(localmap); clearCallSpecs(); for(int4 i=0;igetSeqNum() << ":\t"; (*iter).second->printRaw(s); s << endl; } } else bblocks.printRaw(s); } /// This routine searches for an marks Varnode objects, like stack-pointer registers, /// that are used as a base address for a virtual address space. Each Varnode gets a /// special data-type and is marked so that Varnode::isSpacebase() returns \b true. void Funcdata::spacebase(void) { VarnodeLocSet::const_iterator iter,enditer; int4 i,j,numspace; Varnode *vn; AddrSpace *spc; for(j=0;jnumSpaces();++j) { spc = glb->getSpace(j); if (spc == (AddrSpace *)0) continue; numspace = spc->numSpacebase(); for(i=0;igetSpacebase(i)); // Find input varnode at this size and location Datatype *ct = glb->types->getTypeSpacebase(spc,getAddress()); Datatype *ptr = glb->types->getTypePointer(point.size,ct,spc->getWordSize()); iter = vbank.beginLoc(point.size,Address(point.space,point.offset)); enditer = vbank.endLoc(point.size,Address(point.space,point.offset)); while(iter != enditer) { vn = *iter++; if (vn->isFree()) continue; if (vn->isSpacebase()) { // This has already been marked spacebase // We have given it a chance for descendants to // be eliminated naturally, now force a split if // it still has multiple descendants PcodeOp *op = vn->getDef(); if ((op != (PcodeOp *)0)&&(op->code() == CPUI_INT_ADD)) splitUses(vn); } else { vn->setFlags(Varnode::spacebase); // Mark all base registers (not just input) if (vn->isInput()) // Only set type on the input spacebase register vn->updateType(ptr,true,true); } } } } } /// Given an address space, like \e stack, that is known to have a base register /// pointing to it, construct a Varnode representing that register. /// \param id is the \e stack like address space /// \return a newly allocated stack-pointer Varnode Varnode *Funcdata::newSpacebasePtr(AddrSpace *id) { Varnode *vn; // Assume that id has a base register (otherwise an exception is thrown) const VarnodeData &point(id->getSpacebase(0)); vn = newVarnode(point.size, Address(point.space,point.offset)); return vn; } /// Given an address space, like \e stack, that is known to have a base register /// pointing to it, try to locate the unique Varnode that holds the input value /// of this register. /// \param id is the \e stack like address space /// \return the input stack-pointer Varnode (or NULL if it doesn't exist) Varnode *Funcdata::findSpacebaseInput(AddrSpace *id) const { Varnode *vn; // Assume that id has a base register (otherwise an exception is thrown) const VarnodeData &point(id->getSpacebase(0)); vn = vbank.findInput(point.size, Address(point.space,point.offset)); return vn; } /// \brief If it doesn't exist, create an input Varnode of the base register corresponding to the given address space /// /// The address space must have a base register associated with it or an exception is thrown. /// If a Varnode representing the incoming base register already exists, it is returned. Otherwise /// a new Varnode is created and returned. In either case, the Varnode will have the TypeSpacebase data-type set. /// \param id is the given address space /// \return the input Varnode corresponding to the base register Varnode *Funcdata::constructSpacebaseInput(AddrSpace *id) { Varnode *spacePtr = findSpacebaseInput(id); if (spacePtr != (Varnode *)0) return spacePtr; if (id->numSpacebase() == 0) throw LowlevelError("Unable to construct pointer into space: "+id->getName()); const VarnodeData &point(id->getSpacebase(0)); Datatype *ct = glb->types->getTypeSpacebase(id,getAddress()); Datatype *ptr = glb->types->getTypePointer(point.size,ct,id->getWordSize()); spacePtr = newVarnode(point.size, point.getAddr(), ptr); spacePtr = setInputVarnode(spacePtr); spacePtr->setFlags(Varnode::spacebase); spacePtr->updateType(ptr, true, true); return spacePtr; } /// \brief Create a constant representing the \e base of the given global address space /// /// The constant will have the TypeSpacebase data-type set. /// \param id is the given address space /// \return the constant base Varnode Varnode *Funcdata::constructConstSpacebase(AddrSpace *id) { Datatype *ct = glb->types->getTypeSpacebase(id,Address()); Datatype *ptr = glb->types->getTypePointer(id->getAddrSize(),ct,id->getWordSize()); Varnode *spacePtr = newConstant(id->getAddrSize(),0); spacePtr->updateType(ptr,true,true); spacePtr->setFlags(Varnode::spacebase); return spacePtr; } /// \brief Convert a constant pointer into a \e ram CPUI_PTRSUB /// /// A constant known to be a pointer into an address space like \b ram is converted /// into a Varnode defined by CPUI_PTRSUB, which triggers a Symbol lookup at points /// during analysis. The constant must point to a known Symbol. /// /// The PTRSUB takes the constant 0 as its first input, which is marked /// as a \e spacebase to indicate this situation. The second input to PTRSUB becomes /// the offset to the Symbol within the address space. An additional INT_SUB may be inserted /// to get from the start of the Symbol to the address indicated by the original /// constant pointer. /// \param op is the PcodeOp referencing the constant pointer /// \param slot is the input slot of the constant pointer /// \param entry is the Symbol being pointed (in)to /// \param rampoint is the constant pointer interpreted as an Address /// \param origval is the constant /// \param origsize is the size of the constant void Funcdata::spacebaseConstant(PcodeOp *op,int4 slot,SymbolEntry *entry,const Address &rampoint,uintb origval,int4 origsize) { int4 sz = rampoint.getAddrSize(); AddrSpace *spaceid = rampoint.getSpace(); Datatype *sb_type = glb->types->getTypeSpacebase(spaceid,Address()); sb_type = glb->types->getTypePointer(sz,sb_type,spaceid->getWordSize()); Varnode *spacebase_vn,*outvn,*newconst; uintb extra = rampoint.getOffset() - entry->getAddr().getOffset(); // Offset from beginning of entry extra = AddrSpace::byteToAddress(extra,rampoint.getSpace()->getWordSize()); // Convert to address units PcodeOp *addOp = (PcodeOp *)0; PcodeOp *extraOp = (PcodeOp *)0; PcodeOp *zextOp = (PcodeOp *)0; PcodeOp *subOp = (PcodeOp *)0; bool isCopy = false; if (op->code() == CPUI_COPY) { // We replace COPY with final op of this calculation isCopy = true; if (sz < origsize) zextOp = op; else { op->insertInput(1); // PTRSUB, ADD, SUBPIECE all take 2 parameters if (origsize < sz) subOp = op; else if (extra != 0) extraOp = op; else addOp = op; } } spacebase_vn = newConstant(sz,0); spacebase_vn->updateType(sb_type,true,true); spacebase_vn->setFlags(Varnode::spacebase); if (addOp == (PcodeOp *)0) { addOp = newOp(2,op->getAddr()); opSetOpcode(addOp,CPUI_PTRSUB); newUniqueOut(sz,addOp); opInsertBefore(addOp,op); } else { opSetOpcode(addOp,CPUI_PTRSUB); } outvn = addOp->getOut(); // Make sure newconstant and extra preserve origval in address units uintb newconstoff = origval - extra; // everything is already in address units newconst = newConstant(sz,newconstoff); newconst->setPtrCheck(); // No longer need to check this constant as a pointer if (spaceid->isTruncated()) addOp->setPtrFlow(); opSetInput(addOp,spacebase_vn,0); opSetInput(addOp,newconst,1); Symbol *sym = entry->getSymbol(); Datatype *entrytype = sym->getType(); Datatype *ptrentrytype = glb->types->getTypePointerStripArray(sz,entrytype,spaceid->getWordSize()); bool typelock = sym->isTypeLocked(); if (typelock && (entrytype->getMetatype() == TYPE_UNKNOWN)) typelock = false; outvn->updateType(ptrentrytype,typelock,false); if (extra != 0) { if (extraOp == (PcodeOp *)0) { extraOp = newOp(2,op->getAddr()); opSetOpcode(extraOp,CPUI_INT_ADD); newUniqueOut(sz,extraOp); opInsertBefore(extraOp,op); } else opSetOpcode(extraOp,CPUI_INT_ADD); Varnode *extconst = newConstant(sz,extra); extconst->setPtrCheck(); opSetInput(extraOp,outvn,0); opSetInput(extraOp,extconst,1); outvn = extraOp->getOut(); } if (sz < origsize) { // The new constant is smaller than the original varnode, so we extend it if (zextOp == (PcodeOp *)0) { zextOp = newOp(1,op->getAddr()); opSetOpcode(zextOp,CPUI_INT_ZEXT); // Create an extension to get back to original varnode size newUniqueOut(origsize,zextOp); opInsertBefore(zextOp,op); } else opSetOpcode(zextOp,CPUI_INT_ZEXT); opSetInput(zextOp,outvn,0); outvn = zextOp->getOut(); } else if (origsize < sz) { // The new constant is bigger than the original varnode, truncate it if (subOp == (PcodeOp *)0) { subOp = newOp(2,op->getAddr()); opSetOpcode(subOp,CPUI_SUBPIECE); newUniqueOut(origsize,subOp); opInsertBefore(subOp,op); } else opSetOpcode(subOp,CPUI_SUBPIECE); opSetInput(subOp,outvn,0); opSetInput(subOp,newConstant(4, 0), 1); // Take least significant piece outvn = subOp->getOut(); } if (!isCopy) opSetInput(op,outvn,slot); } void Funcdata::clearCallSpecs(void) { int4 i; for(i=0;igetIn(0); if (vn->getSpace()->getType()==IPTR_FSPEC) return FuncCallSpecs::getFspecFromConst(vn->getAddr()); for(i=0;igetOp() == op) return qlst[i]; return (FuncCallSpecs *)0; } /// \brief Compare call specification objects by call site address /// /// \param a is the first call specification to compare /// \param b is the second call specification /// \return \b true if the first call specification should come before the second bool Funcdata::compareCallspecs(const FuncCallSpecs *a,const FuncCallSpecs *b) { int4 ind1,ind2; ind1 = a->getOp()->getParent()->getIndex(); ind2 = b->getOp()->getParent()->getIndex(); if (ind1 != ind2) return (ind1 < ind2); return (a->getOp()->getSeqNum().getOrder() < b->getOp()->getSeqNum().getOrder()); } /// Calls are put in dominance order so that earlier calls get evaluated first. /// Order affects parameter analysis. void Funcdata::sortCallSpecs(void) { sort(qlst.begin(),qlst.end(),compareCallspecs); } /// This is used internally if a CALL is removed (because it is unreachable) /// \param op is the particular specification to remove void Funcdata::deleteCallSpecs(PcodeOp *op) { vector::iterator iter; for(iter=qlst.begin();iter!=qlst.end();++iter) { FuncCallSpecs *fc = *iter; if (fc->getOp() == op) { delete fc; qlst.erase(iter); return; } } } /// If \e extrapop is unknown, recover it from what we know about this function /// and set the value permanently for \b this Funcdata object. /// If there is no function body it may be impossible to know the value, in which case /// this returns the reserved value indicating \e extrapop is unknown. /// /// \return the recovered value int4 Funcdata::fillinExtrapop(void) { if (hasNoCode()) // If no code to make a decision on return funcp.getExtraPop(); // either we already know it or we don't if (funcp.getExtraPop() != ProtoModel::extrapop_unknown) return funcp.getExtraPop(); // If we already know it, just return it list::const_iterator iter = beginOp(CPUI_RETURN); if (iter == endOp(CPUI_RETURN)) return 0; // If no return statements, answer is irrelevant PcodeOp *retop = *iter; uint1 buffer[4]; glb->loader->loadFill(buffer,4,retop->getAddr()); // We are assuming x86 code here int4 extrapop = 4; // The default case if (buffer[0] == 0xc2) { extrapop = buffer[2]; // Pull out immediate 16-bits extrapop <<= 8; extrapop += buffer[1]; extrapop += 4; // extra 4 for the return address } funcp.setExtraPop( extrapop ); // Save what we have learned on the prototype return extrapop; } /// A description of each Varnode currently involved in the data-flow of \b this /// function is printed to the output stream. This is suitable as part of a console mode /// or debug view of the function at any point during its analysis /// \param s is the output stream void Funcdata::printVarnodeTree(ostream &s) const { VarnodeDefSet::const_iterator iter,enditer; Varnode *vn; iter = vbank.beginDef(); enditer = vbank.endDef(); while(iter != enditer) { vn = *iter++; vn->printInfo(s); } } /// Each scope has a set of memory ranges associated with it, encompassing /// storage locations of variables that are \e assumed to be in the scope. /// Each range for each local scope is printed. /// \param s is the output stream void Funcdata::printLocalRange(ostream &s) const { localmap->printBounds(s); ScopeMap::const_iterator iter,enditer; iter = localmap->childrenBegin(); enditer = localmap->childrenEnd(); for(;iter!=enditer;++iter) { Scope *l1 = (*iter).second; l1->printBounds(s); } } /// Parse a \ element and build a JumpTable object for /// each \ child element. /// \param decoder is the stream decoder void Funcdata::decodeJumpTable(Decoder &decoder) { uint4 elemId = decoder.openElement(ELEM_JUMPTABLELIST); while(decoder.peekElement() != 0) { JumpTable *jt = new JumpTable(glb); jt->decode(decoder); jumpvec.push_back(jt); } decoder.closeElement(elemId); } /// A \ element is written with \ children describing /// each jump-table associated with the control-flow of \b this function. /// \param encoder is the stream encoder void Funcdata::encodeJumpTable(Encoder &encoder) const { if (jumpvec.empty()) return; vector::const_iterator iter; encoder.openElement(ELEM_JUMPTABLELIST); for(iter=jumpvec.begin();iter!=jumpvec.end();++iter) (*iter)->encode(encoder); encoder.closeElement(ELEM_JUMPTABLELIST); } /// \brief Encode descriptions for a set of Varnodes to a stream /// /// This is an internal function for the function's marshaling system. /// Individual elements are written in sequence for Varnodes in a given set. /// The set is bounded by iterators using the 'loc' ordering. /// \param encoder is the stream encoder /// \param iter is the beginning of the set /// \param enditer is the end of the set void Funcdata::encodeVarnode(Encoder &encoder,VarnodeLocSet::const_iterator iter,VarnodeLocSet::const_iterator enditer) { Varnode *vn; while(iter!=enditer) { vn = *iter++; vn->encode(encoder); } } /// This produces a single \ element, with a \ child for each /// high-level variable (HighVariable) currently associated with \b this function. /// \param encoder is the stream encoder void Funcdata::encodeHigh(Encoder &encoder) const { Varnode *vn; HighVariable *high; if (!isHighOn()) return; encoder.openElement(ELEM_HIGHLIST); VarnodeLocSet::const_iterator iter; for(iter=beginLoc();iter!=endLoc();++iter) { vn = *iter; if (vn->isAnnotation()) continue; high = vn->getHigh(); if (high->isMark()) continue; high->setMark(); high->encode(encoder); } for(iter=beginLoc();iter!=endLoc();++iter) { vn = *iter; if (!vn->isAnnotation()) vn->getHigh()->clearMark(); } encoder.closeElement(ELEM_HIGHLIST); } /// A single \ element is produced with children describing Varnodes, PcodeOps, and /// basic blocks making up \b this function's current syntax tree. /// \param encoder is the stream encoder void Funcdata::encodeTree(Encoder &encoder) const { encoder.openElement(ELEM_AST); encoder.openElement(ELEM_VARNODES); for(int4 i=0;inumSpaces();++i) { AddrSpace *base = glb->getSpace(i); if (base == (AddrSpace *)0 || base->getType()==IPTR_IOP) continue; VarnodeLocSet::const_iterator iter = vbank.beginLoc(base); VarnodeLocSet::const_iterator enditer = vbank.endLoc(base); encodeVarnode(encoder,iter,enditer); } encoder.closeElement(ELEM_VARNODES); list::iterator oiter,endoiter; PcodeOp *op; BlockBasic *bs; for(int4 i=0;igetIndex()); bs->encodeBody(encoder); oiter = bs->beginOp(); endoiter = bs->endOp(); while(oiter != endoiter) { op = *oiter++; op->encode(encoder); } encoder.closeElement(ELEM_BLOCK); } for(int4 i=0;isizeIn() == 0) continue; encoder.openElement(ELEM_BLOCKEDGE); encoder.writeSignedInteger(ATTRIB_INDEX, bs->getIndex()); bs->encodeEdges(encoder); encoder.closeElement(ELEM_BLOCKEDGE); } encoder.closeElement(ELEM_AST); } /// A description of \b this function is written to the stream, /// including name, address, prototype, symbol, jump-table, and override information. /// If indicated by the caller, a description of the entire PcodeOp and Varnode /// tree is also emitted. /// \param encoder is the stream encoder /// \param id is the unique id associated with the function symbol /// \param savetree is \b true if the p-code tree should be emitted void Funcdata::encode(Encoder &encoder,uint8 id,bool savetree) const { encoder.openElement(ELEM_FUNCTION); if (id != 0) encoder.writeUnsignedInteger(ATTRIB_ID, id); encoder.writeString(ATTRIB_NAME, name); encoder.writeSignedInteger(ATTRIB_SIZE, size); if (hasNoCode()) encoder.writeBool(ATTRIB_NOCODE, true); baseaddr.encode(encoder); if (!hasNoCode()) { localmap->encodeRecursive(encoder,false); // Save scope and all subscopes } if (savetree) { encodeTree(encoder); encodeHigh(encoder); } encodeJumpTable(encoder); funcp.encode(encoder); // Must be saved after database localoverride.encode(encoder,glb); encoder.closeElement(ELEM_FUNCTION); } /// Parse a \ element, recovering the name, address, prototype, symbol, /// jump-table, and override information for \b this function. /// \param decoder is the stream decoder /// \return the symbol id associated with the function uint8 Funcdata::decode(Decoder &decoder) { // clear(); // Shouldn't be needed name.clear(); size = -1; uint8 id = 0; AddrSpace *stackid = glb->getStackSpace(); uint4 elemId = decoder.openElement(ELEM_FUNCTION); for(;;) { uint4 attribId = decoder.getNextAttributeId(); if (attribId == 0) break; if (attribId == ATTRIB_NAME) name = decoder.readString(); else if (attribId == ATTRIB_SIZE) { size = decoder.readSignedInteger(); } else if (attribId == ATTRIB_ID) { id = decoder.readUnsignedInteger(); } else if (attribId == ATTRIB_NOCODE) { if (decoder.readBool()) flags |= no_code; } else if (attribId == ATTRIB_LABEL) displayName = decoder.readString(); } if (name.size() == 0) throw LowlevelError("Missing function name"); if (displayName.size() == 0) displayName = name; if (size == -1) throw LowlevelError("Missing function size"); baseaddr = Address::decode( decoder ); for(;;) { uint4 subId = decoder.peekElement(); if (subId == 0) break; if (subId == ELEM_LOCALDB) { if (localmap != (ScopeLocal *)0) throw LowlevelError("Pre-existing local scope when restoring: "+name); ScopeLocal *newMap = new ScopeLocal(id,stackid,this,glb); glb->symboltab->decodeScope(decoder,newMap); // May delete newMap and throw localmap = newMap; } else if (subId == ELEM_OVERRIDE) localoverride.decode(decoder,glb); else if (subId == ELEM_PROTOTYPE) { if (localmap == (ScopeLocal *)0) { // If we haven't seen a tag yet, assume we have a default local scope ScopeLocal *newMap = new ScopeLocal(id,stackid,this,glb); Scope *scope = glb->symboltab->getGlobalScope(); glb->symboltab->attachScope(newMap,scope); // May delete newMap and throw localmap = newMap; } funcp.setScope(localmap,baseaddr+ -1); // localmap built earlier funcp.decode(decoder,glb); } else if (subId == ELEM_JUMPTABLELIST) decodeJumpTable(decoder); } decoder.closeElement(elemId); if (localmap == (ScopeLocal *)0) { // Seen neither or // This is a function shell, so we provide default locals ScopeLocal *newMap = new ScopeLocal(id,stackid,this,glb); Scope *scope = glb->symboltab->getGlobalScope(); glb->symboltab->attachScope(newMap,scope); // May delete newMap and throw localmap = newMap; funcp.setScope(localmap,baseaddr+ -1); } localmap->resetLocalWindow(); return id; } /// \brief Inject p-code from a \e payload into \b this live function /// /// Raw PcodeOps are generated from the payload within a given basic block at a specific /// position in \b this function. /// \param payload is the injection payload /// \param addr is the address at the point of injection /// \param bl is the given basic block holding the new ops /// \param iter indicates the point of insertion void Funcdata::doLiveInject(InjectPayload *payload,const Address &addr,BlockBasic *bl,list::iterator iter) { PcodeEmitFd emitter; InjectContext &context(glb->pcodeinjectlib->getCachedContext()); emitter.setFuncdata(this); context.clear(); context.baseaddr = addr; // Shouldn't be using inst_next, inst_next2 or inst_start here context.nextaddr = addr; list::const_iterator deaditer = obank.endDead(); bool deadempty = (obank.beginDead() == deaditer); if (!deadempty) --deaditer; payload->inject(context,emitter); // Calculate iterator to first injected op if (deadempty) deaditer = obank.beginDead(); else ++deaditer; while(deaditer != obank.endDead()) { PcodeOp *op = *deaditer; ++deaditer; if (op->isCallOrBranch()) throw LowlevelError("Illegal branching injection"); opInsert(op,bl,iter); } } void PcodeEmitFd::dump(const Address &addr,OpCode opc,VarnodeData *outvar,VarnodeData *vars,int4 isize) { // Convert template data into a real PcodeOp PcodeOp *op; Varnode *vn; if (outvar != (VarnodeData *)0) { Address oaddr(outvar->space,outvar->offset); op = fd->newOp(isize,addr); fd->newVarnodeOut(outvar->size,oaddr,op); } else op = fd->newOp(isize,addr); fd->opSetOpcode(op,opc); int4 i=0; if (op->isCodeRef()) { // Is the first input parameter a code reference Address addrcode(vars[0].space,vars[0].offset); // addrcode.toPhysical() // For backward compatibility with SLED fd->opSetInput(op,fd->newCodeRef(addrcode),0); i += 1; // This is handled by FlowInfo // if ((opc==CPUI_CALL)&&(addrcode==pos->getNaddr())) { // This is probably PIC code and the call is really a jump // fd->op_setopcode(op,CPUI_BRANCH); // } } for(;inewVarnode(vars[i].size,vars[i].space,vars[i].offset); fd->opSetInput(op,vn,i); } } /// \brief Get the resolved union field associated with the given edge /// /// If there is no field associated with the edge, null is returned /// \param parent is the data-type being resolved /// \param op is the PcodeOp component of the given edge /// \param slot is the slot component of the given edge /// \return the associated field as a ResolvedUnion or null const ResolvedUnion *Funcdata::getUnionField(const Datatype *parent,const PcodeOp *op,int4 slot) const { map::const_iterator iter; ResolveEdge edge(parent,op,slot); iter = unionMap.find(edge); if (iter != unionMap.end()) return &(*iter).second; return (const ResolvedUnion *)0; } /// \brief Associate a union field with the given edge /// /// If there was a previous association, it is overwritten unless it was \e locked. /// The method returns \b true except in this case where a previous locked association exists. /// \param parent is the parent union data-type /// \param op is the PcodeOp component of the given edge /// \param slot is the slot component of the given edge /// \param resolve is the resolved union /// \return \b true unless there was a locked association bool Funcdata::setUnionField(const Datatype *parent,const PcodeOp *op,int4 slot,const ResolvedUnion &resolve) { ResolveEdge edge(parent,op,slot); pair::iterator,bool> res; res = unionMap.emplace(edge,resolve); if (!res.second) { if ((*res.first).second.isLocked()) { return false; } (*res.first).second = resolve; } if (op->code() == CPUI_MULTIEQUAL && slot >= 0) { // Data-type propagation doesn't happen between MULTIEQUAL input slots holding the same Varnode // So if this is a MULTIEQUAL, copy resolution to any other input slots holding the same Varnode const Varnode *vn = op->getIn(slot); // The Varnode being directly set for(int4 i=0;inumInput();++i) { if (i == slot) continue; if (op->getIn(i) != vn) continue; // Check that different input slot holds same Varnode ResolveEdge dupedge(parent,op,i); res = unionMap.emplace(dupedge,resolve); if (!res.second) { if (!(*res.first).second.isLocked()) (*res.first).second = resolve; } } } return true; } /// \brief Force a specific union field resolution for the given edge /// /// The \b parent data-type is taken directly from the given Varnode. /// \param parent is the parent data-type /// \param fieldNum is the index of the field to force /// \param op is PcodeOp of the edge /// \param slot is -1 for the write edge or >=0 indicating the particular read edge void Funcdata::forceFacingType(Datatype *parent,int4 fieldNum,PcodeOp *op,int4 slot) { Datatype *baseType = parent; if (baseType->getMetatype() == TYPE_PTR) baseType = ((TypePointer *)baseType)->getPtrTo(); if (parent->isPointerRel()) { // Don't associate a relative pointer with the resolution, but convert to a standard pointer parent = glb->types->getTypePointer(parent->getSize(), baseType, ((TypePointer *)parent)->getWordSize()); } ResolvedUnion resolve(parent,fieldNum,*glb->types); setUnionField(parent, op, slot, resolve); } /// \brief Copy a read/write facing resolution for a specific data-type from one PcodeOp to another /// /// \param parent is the data-type that needs resolution /// \param op is the new reading PcodeOp /// \param slot is the new slot (-1 for write, >=0 for read) /// \param oldOp is the PcodeOp to inherit the resolution from /// \param oldSlot is the old slot (-1 for write, >=0 for read) int4 Funcdata::inheritResolution(Datatype *parent,const PcodeOp *op,int4 slot,PcodeOp *oldOp,int4 oldSlot) { map::const_iterator iter; ResolveEdge edge(parent,oldOp,oldSlot); iter = unionMap.find(edge); if (iter == unionMap.end()) return -1; setUnionField(parent,op,slot,(*iter).second); return (*iter).second.getFieldNum(); } #ifdef OPACTION_DEBUG /// The current state of the op is recorded for later comparison after /// its been modified. /// \param op is the given PcodeOp being recorded void Funcdata::debugModCheck(PcodeOp *op) { if (op->isModified()) return; if (!debugCheckRange(op)) return; op->setAdditionalFlag(PcodeOp::modified); ostringstream before; op->printDebug(before); modify_list.push_back(op); modify_before.push_back( before.str() ); } void Funcdata::debugModClear(void) { for(int4 i=0;iclearAdditionalFlag(PcodeOp::modified); modify_list.clear(); modify_before.clear(); opactdbg_active = false; } /// \param actionname is the name of the Action being debugged void Funcdata::debugModPrint(const string &actionname) { if (!opactdbg_active) return; opactdbg_active = false; if (modify_list.empty()) return; PcodeOp *op; ostringstream s; opactdbg_breakon |= (opactdbg_count == opactdbg_breakcount); s << "DEBUG " << dec << opactdbg_count++ << ": " << actionname << endl; for(int4 i=0;iprintDebug(s); s << endl; op->clearAdditionalFlag(PcodeOp::modified); } modify_list.clear(); modify_before.clear(); glb->printDebug(s.str()); } /// \param pclow is the beginning of the memory range to trace /// \param pchigh is the end of the range /// \param uqlow is an (optional) sequence number to associate with the beginning of the range /// \param uqhigh is an (optional) sequence number to associate with the end of the range void Funcdata::debugSetRange(const Address &pclow,const Address &pchigh, uintm uqlow,uintm uqhigh) { opactdbg_on = true; opactdbg_pclow.push_back(pclow); opactdbg_pchigh.push_back(pchigh); opactdbg_uqlow.push_back(uqlow); opactdbg_uqhigh.push_back(uqhigh); } /// \param op is the given PcodeOp to check /// \return \b true if the op is being traced bool Funcdata::debugCheckRange(PcodeOp *op) { int4 i,size; size = opactdbg_pclow.size(); for(i=0;igetAddr() < opactdbg_pclow[i]) continue; if (opactdbg_pchigh[i] < op->getAddr()) continue; } if (opactdbg_uqlow[i] != ~((uintm)0)) { if (opactdbg_uqlow[i] > op->getTime()) continue; if (opactdbg_uqhigh[i] < op->getTime()) continue; } return true; } return false; } void Funcdata::debugPrintRange(int4 i) const { ostringstream s; if (!opactdbg_pclow[i].isInvalid()) { s << "PC = ("; opactdbg_pclow[i].printRaw(s); s << ','; opactdbg_pchigh[i].printRaw(s); s << ") "; } else s << "entire function "; if (opactdbg_uqlow[i] != ~((uintm)0)) { s << "unique = (" << hex << opactdbg_uqlow[i] << ','; s << opactdbg_uqhigh[i] << ')'; } glb->printDebug(s.str()); } #endif } // End namespace ghidra