/* ### * 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" #include "flow.hh" namespace ghidra { // Funcdata members pertaining directly to blocks /// A description of each block in the current structure hierarchy is /// printed to stream. This is suitable for a console mode or debug view /// of the state of control-flow structuring at any point during analysis. /// \param s is the output stream void Funcdata::printBlockTree(ostream &s) const { if (sblocks.getSize() != 0) sblocks.printTree(s,0); } void Funcdata::clearBlocks(void) { bblocks.clear(); sblocks.clear(); } /// Any override information is preserved. void Funcdata::clearJumpTables(void) { vector remain; vector::iterator iter; for(iter=jumpvec.begin();iter!=jumpvec.end();++iter) { JumpTable *jt = *iter; if (jt->isOverride()) { jt->clear(); // Clear out any derived data remain.push_back(jt); // Keep the override itself } else delete jt; } jumpvec = remain; } /// The JumpTable object is freed, and the associated BRANCHIND is no longer marked /// as a \e switch point. /// \param jt is the given JumpTable object void Funcdata::removeJumpTable(JumpTable *jt) { vector remain; vector::iterator iter; for(iter=jumpvec.begin();iter!=jumpvec.end();++iter) if ((*iter) != jt) remain.push_back(*iter); PcodeOp *op = jt->getIndirectOp(); delete jt; if (op != (PcodeOp *)0) op->getParent()->clearFlag(FlowBlock::f_switch_out); jumpvec = remain; } /// Assuming the given basic block is being removed, force any Varnode defined by /// a MULTIEQUAL in the block to be defined in the output block instead. This is used /// as part of the basic block removal process to patch up data-flow. /// \param bb is the given basic block void Funcdata::pushMultiequals(BlockBasic *bb) { BlockBasic *outblock; PcodeOp *origop,*replaceop; Varnode *origvn,*replacevn; list::iterator iter; list::const_iterator citer; if (bb->sizeOut()==0) return; if (bb->sizeOut()>1) warningHeader("push_multiequal on block with multiple outputs"); outblock = (BlockBasic *) bb->getOut(0); // Take first output block. If this is a // donothing block, it is the only output block int4 outblock_ind = bb->getOutRevIndex(0); for(iter=bb->beginOp();iter!=bb->endOp();++iter) { origop = *iter; if (origop->code() != CPUI_MULTIEQUAL) continue; origvn = origop->getOut(); if (origvn->hasNoDescend()) continue; bool needreplace = false; bool neednewunique = false; for(citer=origvn->beginDescend();citer!=origvn->endDescend();++citer) { PcodeOp *op = *citer; if ((op->code()==CPUI_MULTIEQUAL)&&(op->getParent()==outblock)) { bool deadEdge = true; // Check for reference to origvn NOT thru the dead edge for(int4 i=0;inumInput();++i) { if (i == outblock_ind) continue; // Not going thru dead edge if (op->getIn(i) == origvn) { // Reference to origvn deadEdge = false; break; } } if (deadEdge) { if ((origvn->getAddr() == op->getOut()->getAddr())&&origvn->isAddrTied()) // If origvn is addrtied and feeds into a MULTIEQUAL at same address in outblock // Then any use of origvn beyond outblock that did not go thru this MULTIEQUAL must have // propagated through some other register. So we make the new MULTIEQUAL write to a unique register neednewunique = true; continue; } } needreplace = true; break; } if (!needreplace) continue; // Construct artificial MULTIEQUAL vector branches; if (neednewunique) replacevn = newUnique(origvn->getSize()); else replacevn = newVarnode(origvn->getSize(),origvn->getAddr()); for(int4 i=0;isizeIn();++i) { if (outblock->getIn(i) == bb) branches.push_back(origvn); else branches.push_back( replacevn ); // In this situation there are other blocks "beyond" outblock which read // origvn defined in bb, but there are other blocks falling into outblock // Assuming the only out of bb is outblock, all heritages of origvn must // come through outblock. Thus any alternate ins to outblock must be // dominated by bb. So the artificial MULTIEQUAL we construct must have // all inputs be origvn } replaceop = newOp(branches.size(),outblock->getStart()); opSetOpcode(replaceop,CPUI_MULTIEQUAL); opSetOutput(replaceop,replacevn); opSetAllInput(replaceop,branches); opInsertBegin(replaceop,outblock); // Replace obsolete origvn with replacevn int4 i; list::iterator titer = origvn->descend.begin(); while(titer != origvn->descend.end()) { PcodeOp *op = *titer++; for(i=0;inumInput();++i) { if (op->getIn(i) != origvn) continue; if (i == outblock_ind && op->getParent() == outblock && op->code() == CPUI_MULTIEQUAL) { continue; } opSetInput(op,replacevn,i); break; } } } } /// If the MULTIEQUAL has no inputs, presumably the basic block is unreachable, so we treat /// the p-code op as a COPY from a new input Varnode. If there is 1 input, the MULTIEQUAL /// is transformed directly into a COPY. /// \param op is the given MULTIEQUAL void Funcdata::opZeroMulti(PcodeOp *op) { if (op->numInput()==0) { // If no branches left opInsertInput(op,newVarnode(op->getOut()->getSize(),op->getOut()->getAddr()),0); setInputVarnode(op->getIn(0)); // Then this is an input opSetOpcode(op,CPUI_COPY); } else if (op->numInput()==1) opSetOpcode(op,CPUI_COPY); } /// \brief Remove an outgoing branch of the given basic block /// /// MULTIEQUAL p-code ops (in other blocks) that take inputs from the outgoing branch /// are patched appropriately. /// \param bb is the given basic block /// \param num is the index of the outgoing edge to remove void Funcdata::branchRemoveInternal(BlockBasic *bb,int4 num) { BlockBasic *bbout; list::iterator iter; PcodeOp *op; int4 blocknum; if (bb->sizeOut() == 2) // If there is no decision left opDestroy(bb->lastOp()); // Remove the branch instruction bbout = (BlockBasic *) bb->getOut(num); blocknum = bbout->getInIndex(bb); bblocks.removeEdge(bb,bbout); // Sever (one) connection between bb and bbout for(iter=bbout->beginOp();iter!=bbout->endOp();++iter) { op = *iter; if (op->code() != CPUI_MULTIEQUAL) continue; opRemoveInput(op,blocknum); opZeroMulti(op); } } /// The edge is removed from control-flow and affected MULTIEQUAL ops are adjusted. /// \param bb is the basic block /// \param num is the index of the out edge to remove void Funcdata::removeBranch(BlockBasic *bb,int4 num) { branchRemoveInternal(bb,num); structureReset(); } /// \brief Check if given Varnode has any descendants in a dead block /// /// Assuming a basic block is marked \e dead, return \b true if any PcodeOp reading /// the Varnode is in the dead block. /// \param vn is the given Varnode /// \return \b true if the Varnode is read in the dead block bool Funcdata::descendantsOutside(Varnode *vn) { list::const_iterator iter; for(iter=vn->beginDescend();iter!=vn->endDescend();++iter) if (!(*iter)->getParent()->isDead()) return true; return false; } /// \brief Remove an active basic block from the function /// /// PcodeOps in the block are deleted. Data-flow and control-flow are otherwise /// patched up. Most of the work is patching up MULTIEQUALs and other remaining /// references to Varnodes flowing through the block to be removed. /// /// If descendant Varnodes are stranded by removing the block, either an exception is /// thrown, or optionally, the descendant Varnodes can be replaced with constants and /// a warning is printed. /// \param bb is the given basic block /// \param unreachable is \b true if the caller wants a warning for stranded Varnodes void Funcdata::blockRemoveInternal(BlockBasic *bb,bool unreachable) { BlockBasic *bbout; Varnode *deadvn; PcodeOp *op,*deadop; list::iterator iter; int4 i,j,blocknum; bool desc_warning; op = bb->lastOp(); if ((op != (PcodeOp *)0)&&(op->code() == CPUI_BRANCHIND)) { JumpTable *jt = findJumpTable(op); if (jt != (JumpTable *)0) removeJumpTable(jt); } if (!unreachable) { pushMultiequals(bb); // Make sure data flow is preserved for(i=0;isizeOut();++i) { bbout = (BlockBasic *) bb->getOut(i); if (bbout->isDead()) continue; blocknum = bbout->getInIndex(bb); // Get index of bb into bbout for(iter=bbout->beginOp();iter!=bbout->endOp();++iter) { op = *iter; if (op->code() != CPUI_MULTIEQUAL) continue; deadvn = op->getIn(blocknum); opRemoveInput(op,blocknum); // Remove the deleted blocks branch deadop = deadvn->getDef(); if ((deadvn->isWritten())&&(deadop->code()==CPUI_MULTIEQUAL)&&(deadop->getParent()==bb)) { // Append new branches for(j=0;jsizeIn();++j) opInsertInput(op,deadop->getIn(j),op->numInput()); } else { for(j=0;jsizeIn();++j) opInsertInput(op,deadvn,op->numInput()); // Otherwise make copies } opZeroMulti(op); } } } bblocks.removeFromFlow(bb); desc_warning = false; iter = bb->beginOp(); while(iter!=bb->endOp()) { // Finally remove all the ops op = *iter; if (op->isAssignment()) { // op still has some descendants deadvn = op->getOut(); if (unreachable) { bool undef = descend2Undef(deadvn); if (undef&&(!desc_warning)) { // Mark descendants as undefined warningHeader("Creating undefined varnodes in (possibly) reachable block"); desc_warning = true; // Print the warning only once } } if (descendantsOutside(deadvn)) // If any descendants outside of bb throw LowlevelError("Deleting op with descendants\n"); } if (op->isCall()) deleteCallSpecs(op); iter++; // Increment iterator before unlinking opDestroy(op); // No longer has descendants } bblocks.removeBlock(bb); // Remove the block altogether } /// The block must contain only \e marker operations (MULTIEQUAL) and possibly a single /// unconditional branch operation. The block and its PcodeOps are completely removed from /// the current control-flow and data-flow. This forces a reset of the control-flow structuring /// hierarchy. /// \param bb is the given basic block void Funcdata::removeDoNothingBlock(BlockBasic *bb) { if (bb->sizeOut()>1) throw LowlevelError("Cannot delete a reachable block unless it has 1 out or less"); bb->setDead(); blockRemoveInternal(bb,false); structureReset(); // Delete any structure we had before } /// \brief Remove any unreachable basic blocks /// /// A quick check for unreachable blocks can optionally be made, otherwise /// the cached state is checked via hasUnreachableBlocks(), which is turned on /// during analysis by calling the structureReset() method. /// \param issuewarning is \b true if warning comments are desired /// \param checkexistence is \b true to force an active search for unreachable blocks /// \return \b true if unreachable blocks were actually found and removed bool Funcdata::removeUnreachableBlocks(bool issuewarning,bool checkexistence) { vector list; uint4 i; if (checkexistence) { // Quick check for the existence of unreachable blocks for(i=0;iisEntryPoint()) continue; // Don't remove starting component if (blk->getImmedDom() == (FlowBlock *)0) break; } if (i==bblocks.getSize()) return false; } else if (!hasUnreachableBlocks()) // Use cached check return false; // There must be at least one unreachable block if we reach here for(i=0;iisEntryPoint()) break; bblocks.collectReachable(list,bblocks.getBlock(i),true); // Collect (un)reachable blocks for(i=0;isetDead(); if (issuewarning) { ostringstream s; BlockBasic *bb = (BlockBasic *)list[i]; s << "Removing unreachable block ("; s << bb->getStart().getSpace()->getName(); s << ','; bb->getStart().printRaw(s); s << ')'; warningHeader(s.str()); } } for(i=0;isizeOut() > 0) branchRemoveInternal(bb,0); } for(i=0;ilastOp(); if ((cbranch->code() != CPUI_CBRANCH)||(bb->sizeOut() != 2)) throw LowlevelError("Cannot push non-conditional edge"); PcodeOp *indop = bbnew->lastOp(); if (indop->code() != CPUI_BRANCHIND) throw LowlevelError("Can only push branch into indirect jump"); // Turn the conditional branch into a branch opRemoveInput(cbranch,1); // Remove the conditional variable opSetOpcode(cbranch,CPUI_BRANCH); bblocks.moveOutEdge(bb,slot,bbnew); // No change needs to be made to the indirect branch // we assume it handles its new branch implicitly structureReset(); } /// Look up the jump-table object with the matching PcodeOp address, then /// attach the given PcodeOp to it. /// \param op is the given BRANCHIND PcodeOp /// \return the matching jump-table object or NULL JumpTable *Funcdata::linkJumpTable(PcodeOp *op) { vector::iterator iter; JumpTable *jt; for(iter=jumpvec.begin();iter!=jumpvec.end();++iter) { jt = *iter; if (jt->getOpAddress() == op->getAddr()) { jt->setIndirectOp(op); return jt; } } return (JumpTable *)0; } /// Look up the jump-table object with the matching PcodeOp address /// \param op is the given BRANCHIND PcodeOp /// \return the matching jump-table object or NULL JumpTable *Funcdata::findJumpTable(const PcodeOp *op) const { vector::const_iterator iter; JumpTable *jt; for(iter=jumpvec.begin();iter!=jumpvec.end();++iter) { jt = *iter; if (jt->getOpAddress() == op->getAddr()) return jt; } return (JumpTable *)0; } /// The given address must have a BRANCHIND op attached to it. /// This is suitable for installing an override and must be called before /// flow has been traced. /// \param addr is the given Address /// \return the new jump-table object JumpTable *Funcdata::installJumpTable(const Address &addr) { if (isProcStarted()) throw LowlevelError("Cannot install jumptable if flow is already traced"); for(int4 i=0;igetOpAddress() == addr) throw LowlevelError("Trying to install over existing jumptable"); } JumpTable *newjt = new JumpTable(glb,addr); jumpvec.push_back(newjt); return newjt; } /// \brief Recover a jump-table for a given BRANCHIND using existing flow information /// /// A partial function (copy) is built using the flow info. Simplification is performed on the /// partial function (using the "jumptable" strategy), then destination addresses of the /// branch are recovered by examining the simplified data-flow. The jump-table object /// is populated with the recovered addresses. A code indicating success or the type of /// failure is returned. /// /// \param partial is a function object for caching analysis /// \param jt is the jump-table object to populate /// \param op is the BRANCHIND p-code op to analyze /// \param flow is the existing flow information /// \return the success/failure code JumpTable::RecoveryMode Funcdata::stageJumpTable(Funcdata &partial,JumpTable *jt,PcodeOp *op,FlowInfo *flow) { if (!partial.isJumptableRecoveryOn()) { // Do full analysis on the table if we haven't before partial.flags |= jumptablerecovery_on; // Mark that this Funcdata object is dedicated to jumptable recovery partial.truncatedFlow(this,flow); string oldactname = glb->allacts.getCurrentName(); // Save off old action try { glb->allacts.setCurrent("jumptable"); #ifdef OPACTION_DEBUG if (jtcallback != (void (*)(Funcdata &orig,Funcdata &fd))0) (*jtcallback)(*this,partial); // Alternative reset/perform else { #endif glb->allacts.getCurrent()->reset( partial ); glb->allacts.getCurrent()->perform( partial ); // Simplify the partial function #ifdef OPACTION_DEBUG } #endif glb->allacts.setCurrent(oldactname); // Restore old action } catch(LowlevelError &err) { glb->allacts.setCurrent(oldactname); warning(err.explain,op->getAddr()); return JumpTable::fail_normal; } } PcodeOp *partop = partial.findOp(op->getSeqNum()); if (partop==(PcodeOp *)0 || partop->code() != CPUI_BRANCHIND || partop->getAddr() != op->getAddr()) throw LowlevelError("Error recovering jumptable: Bad partial clone"); if (partop->isDead()) // Indirectop we were trying to recover was eliminated as dead code (unreachable) return JumpTable::success; // Return jumptable as // Test if the branch target is copied from the return address. if (testForReturnAddress(partop->getIn(0))) return JumpTable::fail_return; // Return special failure code. Switch would not recover anyway. try { jt->setLoadCollect(flow->doesJumpRecord()); jt->setIndirectOp(partop); if (jt->isPartial()) jt->recoverMultistage(&partial); else jt->recoverAddresses(&partial); // Analyze partial to recover jumptable addresses } catch(JumptableThunkError &err) { // Thrown by recoverAddresses return JumpTable::fail_thunk; } catch(LowlevelError &err) { warning(err.explain,op->getAddr()); return JumpTable::fail_normal; } return JumpTable::success; } /// Backtrack from the BRANCHIND, looking for ops that might affect the destination. /// If a CALLOTHER, which is not injected/inlined in some way, is in the flow path of /// the destination calculation, we know the jump-table analysis will fail and the failure mode is returned. /// \param op is the BRANCHIND op /// \return \b success if there is no early failure, or the failure mode otherwise JumpTable::RecoveryMode Funcdata::earlyJumpTableFail(PcodeOp *op) { Varnode *vn = op->getIn(0); list::const_iterator iter = op->insertiter; list::const_iterator startiter = beginOpDead(); int4 countMax = 8; while(iter != startiter) { if (vn->getSize() == 1) return JumpTable::success; countMax -= 1; if (countMax < 0) return JumpTable::success; // Don't iterate too many times --iter; op = *iter; Varnode *outvn = op->getOut(); bool outhit = false; if (outvn != (Varnode *)0) outhit = vn->intersects(*outvn); if (op->getEvalType() == PcodeOp::special) { if (op->isCall()) { OpCode opc = op->code(); if (opc == CPUI_CALLOTHER) { int4 id = (int4)op->getIn(0)->getOffset(); uint4 userOpType = glb->userops.getOp(id)->getType(); if (userOpType == UserPcodeOp::injected) return JumpTable::success; // Don't try to back track through injection if (userOpType == UserPcodeOp::jumpassist) return JumpTable::success; if (userOpType == UserPcodeOp::segment) return JumpTable::success; if (outhit) return JumpTable::fail_callother; // Address formed via uninjected CALLOTHER, analysis will fail // Assume CALLOTHER will not interfere with address and continue backtracking } else { // CALL or CALLIND - Output has not been established yet return JumpTable::success; // Don't try to back track through CALL } } else if (op->isBranch()) return JumpTable::success; // Don't try to back track further else { if (op->code() == CPUI_STORE) return JumpTable::success; // Don't try to back track through STORE if (outhit) return JumpTable::success; // Some special op (CPOOLREF, NEW, etc) generates address, don't assume failure // Assume special will not interfere with address and continue backtracking } } else if (op->getEvalType() == PcodeOp::unary) { if (outhit) { Varnode *invn = op->getIn(0); if (invn->getSize() != vn->getSize()) return JumpTable::success; vn = invn; // Treat input as address } // Continue backtracking } else if (op->getEvalType() == PcodeOp::binary) { if (outhit) { OpCode opc = op->code(); if (opc != CPUI_INT_ADD && opc != CPUI_INT_SUB && opc != CPUI_INT_XOR) return JumpTable::success; if (!op->getIn(1)->isConstant()) return JumpTable::success; // Don't back-track thru binary op, don't assume failure Varnode *invn = op->getIn(0); if (invn->getSize() != vn->getSize()) return JumpTable::success; vn = invn; // Treat input as address } // Continue backtracking } else { if (outhit) return JumpTable::success; } } return JumpTable::success; } /// \brief Recover control-flow destinations for a BRANCHIND /// /// If an existing and complete JumpTable exists for the BRANCHIND, it is returned immediately. /// Otherwise an attempt is made to analyze the current partial function and recover the set of destination /// addresses, which if successful will be returned as a new JumpTable object. /// \param partial is the Funcdata copy to perform analysis on if necessary /// \param op is the given BRANCHIND PcodeOp /// \param flow is current flow information for \b this function /// \param mode will hold the final success/failure code /// \return the recovered JumpTable or NULL if there was no success JumpTable *Funcdata::recoverJumpTable(Funcdata &partial,PcodeOp *op,FlowInfo *flow,JumpTable::RecoveryMode &mode) { JumpTable *jt; mode = JumpTable::success; jt = linkJumpTable(op); // Search for pre-existing jumptable if (jt != (JumpTable *)0) { if (!jt->isOverride()) { if (!jt->isPartial()) return jt; // Previously calculated jumptable (NOT an override and NOT incomplete) } mode = stageJumpTable(partial,jt,op,flow); // Recover based on override information if (mode != JumpTable::success) return (JumpTable *)0; jt->setIndirectOp(op); // Relink table back to original op return jt; } if ((flags & jumptablerecovery_dont)!=0) return (JumpTable *)0; // Explicitly told not to recover jumptables mode = earlyJumpTableFail(op); if (mode != JumpTable::success) return (JumpTable *)0; JumpTable trialjt(glb); mode = stageJumpTable(partial,&trialjt,op,flow); if (mode != JumpTable::success) return (JumpTable *)0; // if (trialjt.is_twostage()) // warning("Jumptable maybe incomplete. Second-stage recovery not implemented",trialjt.Opaddress()); jt = new JumpTable(&trialjt); // Make the jumptable permanent jumpvec.push_back(jt); jt->setIndirectOp(op); // Relink table back to original op return jt; } /// For each jump-table, for each address, the corresponding basic block index is computed. /// This also calculates the \e default branch for each jump-table. /// \param flow is the flow object (mapping addresses to p-code ops) void Funcdata::switchOverJumpTables(const FlowInfo &flow) { vector::iterator iter; for(iter=jumpvec.begin();iter!=jumpvec.end();++iter) (*iter)->switchOver(flow); } void Funcdata::installSwitchDefaults(void) { vector::iterator iter; for(iter=jumpvec.begin();iter!=jumpvec.end();++iter) { JumpTable *jt = *iter; PcodeOp *indop = jt->getIndirectOp(); BlockBasic *ind = indop->getParent(); // Mark any switch blocks default edge if (jt->getDefaultBlock() != -1) // If a default case is present ind->setDefaultSwitch(jt->getDefaultBlock()); } } /// For the current control-flow graph, (re)calculate the loop structure and dominance. /// This can be called multiple times as changes are made to control-flow. /// The structured hierarchy is also reset. void Funcdata::structureReset(void) { vector::iterator iter; vector rootlist; flags &= ~blocks_unreachable; // Clear any old blocks flag bblocks.structureLoops(rootlist); bblocks.calcForwardDominator(rootlist); if (rootlist.size() > 1) flags |= blocks_unreachable; // Check for dead jumptables vector alivejumps; for(iter=jumpvec.begin();iter!=jumpvec.end();++iter) { JumpTable *jt = *iter; PcodeOp *indop = jt->getIndirectOp(); if (indop->isDead()) { warningHeader("Recovered jumptable eliminated as dead code"); delete jt; continue; } alivejumps.push_back(jt); } jumpvec = alivejumps; sblocks.clear(); // Force structuring algorithm to start over // sblocks.build_copy(bblocks); // Make copy of the basic block control flow graph heritage.forceRestructure(); } /// \brief Force a specific control-flow edge to be marked as \e unstructured /// /// The edge is specified by a source and destination Address (of the branch). /// The resulting control-flow structure will have a \e goto statement modeling /// the edge. /// \param pcop is the source Address /// \param pcdest is the destination Address /// \return \b true if a control-flow edge was successfully labeled bool Funcdata::forceGoto(const Address &pcop,const Address &pcdest) { FlowBlock *bl,*bl2; PcodeOp *op,*op2; int4 i,j; for(i=0;ilastOp(); if (op == (PcodeOp *)0) continue; if (op->getAddr() != pcop) continue; // Find op to mark unstructured for(j=0;jsizeOut();++j) { bl2 = bl->getOut(j); op2 = bl2->lastOp(); if (op2 == (PcodeOp *)0) continue; if (op2->getAddr() != pcdest) continue; // Find particular branch bl->setGotoBranch(j); return true; } } return false; } /// \brief Create a new basic block for holding a merged CBRANCH /// /// This is used by ConditionalJoin to do the low-level control-flow manipulation /// to merge identical conditional branches. Given basic blocks containing the two /// CBRANCH ops to merge, the new block gets one of the two out edges from each block, /// and the remaining out edges are changed to point into the new block. /// \param block1 is the basic block containing the first CBRANCH to merge /// \param block2 is the basic block containing the second CBRANCH /// \param exita is the first common exit block for the CBRANCHs /// \param exitb is the second common exit block /// \param fora_block1ishigh designates which edge is moved for exita /// \param forb_block1ishigh designates which edge is moved for exitb /// \param addr is the Address associated with (1 of the) CBRANCH ops /// \return the new basic block BlockBasic *Funcdata::nodeJoinCreateBlock(BlockBasic *block1,BlockBasic *block2, BlockBasic *exita,BlockBasic *exitb, bool fora_block1ishigh,bool forb_block1ishigh,const Address &addr) { BlockBasic *newblock = bblocks.newBlockBasic(this); newblock->setFlag(FlowBlock::f_joined_block); newblock->setInitialRange(addr, addr); FlowBlock *swapa,*swapb; // Delete 2 of the original edges into exita and exitb if (fora_block1ishigh) { // Remove the edge from block1 bblocks.removeEdge(block1,exita); swapa = block2; } else { bblocks.removeEdge(block2,exita); swapa = block1; } if (forb_block1ishigh) { bblocks.removeEdge(block1,exitb); swapb = block2; } else { bblocks.removeEdge(block2,exitb); swapb = block1; } // Move the remaining two from block1,block2 to newblock bblocks.moveOutEdge(swapa,swapa->getOutIndex(exita),newblock); bblocks.moveOutEdge(swapb,swapb->getOutIndex(exitb),newblock); bblocks.addEdge(block1,newblock); bblocks.addEdge(block2,newblock); structureReset(); return newblock; } /// \brief Split given basic block b along an \e in edge /// /// A copy of the block is made, inheriting the same \e out edges but only the /// one indicated \e in edge, which is removed from the original block. /// Other data-flow is \b not affected. /// \param b is the given basic block /// \param inedge is the index of the indicated \e in edge BlockBasic *Funcdata::nodeSplitBlockEdge(BlockBasic *b,int4 inedge) { FlowBlock *a = b->getIn(inedge); BlockBasic *bprime; bprime = bblocks.newBlockBasic(this); bprime->setFlag(FlowBlock::f_duplicate_block); bprime->copyRange(b); bblocks.switchEdge(a,b,bprime); for(int4 i=0;isizeOut();++i) bblocks.addEdge(bprime,b->getOut(i)); return bprime; } /// \brief Split control-flow into a basic block, duplicating its p-code into a new block /// /// P-code is duplicated into another block, and control-flow is modified so that the new /// block takes over flow from one input edge to the original block. /// \param b is the basic block to be duplicated and split /// \param inedge is the index of the input edge to move to the duplicate block void Funcdata::nodeSplit(BlockBasic *b,int4 inedge) { // Split node b along inedge if (b->sizeOut() != 0) throw LowlevelError("Cannot (currently) nodesplit block with out flow"); if (b->sizeIn()<=1) throw LowlevelError("Cannot nodesplit block with only 1 in edge"); for(int4 i=0;isizeIn();++i) { if (b->getIn(i)->isMark()) throw LowlevelError("Cannot nodesplit block with redundant in edges"); b->setMark(); } for(int4 i=0;isizeIn();++i) b->clearMark(); // Create duplicate block BlockBasic *bprime = nodeSplitBlockEdge(b,inedge); CloneBlockOps cloner(*this); cloner.cloneBlock(b, bprime, inedge); // Copy b's ops into bprime // We would need to patch outputs here for the more general // case when b has out edges // any references not in b to varnodes defined in b // need to have MULTIEQUALs defined in b's out blocks // with edges coming from b and bprime structureReset(); } /// \brief Remove a basic block splitting its control-flow into two distinct paths /// /// This is used by ConditionalExecution to eliminate unnecessary control-flow joins. /// The given block must have 2 inputs and 2 outputs, (and no operations). The block /// is removed, and control-flow is adjusted so that /// In(0) flows to Out(0) and In(1) flows to Out(1), or vice versa. /// \param bl is the given basic block /// \param swap is \b true to force In(0)->Out(1) and In(1)->Out(0) void Funcdata::removeFromFlowSplit(BlockBasic *bl,bool swap) { if (!bl->emptyOp()) throw LowlevelError("Can only split the flow for an empty block"); bblocks.removeFromFlowSplit(bl,swap); bblocks.removeBlock(bl); structureReset(); } /// \brief Switch an outgoing edge from the given \e source block to flow into another block /// /// This does \e not adjust MULTIEQUAL data-flow. /// \param inblock is the given \e source block /// \param outbefore is the other side of the desired edge /// \param outafter is the new destination block desired void Funcdata::switchEdge(FlowBlock *inblock,BlockBasic *outbefore,FlowBlock *outafter) { bblocks.switchEdge(inblock,outbefore,outafter); structureReset(); } /// The given block must have a single output block, which will be removed. The given block /// has the p-code from the output block concatenated to its own, and it inherits the output /// block's out edges. /// \param bl is the given basic block void Funcdata::spliceBlockBasic(BlockBasic *bl) { BlockBasic *outbl = (BlockBasic *)0; if (bl->sizeOut() == 1) { outbl = (BlockBasic *)bl->getOut(0); if (outbl->sizeIn() != 1) outbl = (BlockBasic *)0; } if (outbl == (BlockBasic *)0) throw LowlevelError("Cannot splice basic blocks"); // Remove any jump op at the end of -bl- if (!bl->op.empty()) { PcodeOp *jumpop = bl->op.back(); if (jumpop->isBranch()) opDestroy(jumpop); } if (!outbl->op.empty()) { // Check for MULTIEQUALs PcodeOp *firstop = outbl->op.front(); if (firstop->code() == CPUI_MULTIEQUAL) throw LowlevelError("Splicing block with MULTIEQUAL"); firstop->clearFlag(PcodeOp::startbasic); list::iterator iter; // Move ops into -bl- for(iter=outbl->beginOp();iter!=outbl->endOp();++iter) { PcodeOp *op = *iter; op->setParent(bl); // Reset ops parent to -bl- } // Move all ops from -outbl- to end of -bl- bl->op.splice(bl->op.end(),outbl->op,outbl->op.begin(),outbl->op.end()); // insertiter should remain valid through splice bl->setOrder(); // Reset the seqnum ordering on all the ops } bl->mergeRange(outbl); // Update the address cover bblocks.spliceBlock(bl); structureReset(); } /// Make a basic clone of the p-code op copying its basic control-flow properties. /// In the case of a \e branch, the p-code op is not cloned and null is returned. /// \param op is the given PcodeOp /// \return the cloned op or null PcodeOp *CloneBlockOps::buildOpClone(PcodeOp *op) { PcodeOp *dup; if (op->isBranch()) { if (op->code() != CPUI_BRANCH) throw LowlevelError("Cannot duplicate 2-way or n-way branch in nodeplit"); return (PcodeOp *)0; } dup = data.newOp(op->numInput(),op->getAddr()); data.opSetOpcode(dup,op->code()); uint4 fl = op->flags & (PcodeOp::startbasic | PcodeOp::nocollapse | PcodeOp::startmark | PcodeOp::nonprinting | PcodeOp::halt | PcodeOp::badinstruction | PcodeOp::unimplemented | PcodeOp::noreturn | PcodeOp::missing | PcodeOp::indirect_creation | PcodeOp::indirect_store | PcodeOp::no_indirect_collapse | PcodeOp::calculated_bool | PcodeOp::ptrflow); dup->setFlag(fl); fl = op->addlflags & (PcodeOp::special_prop | PcodeOp::special_print | PcodeOp::incidental_copy | PcodeOp::is_cpool_transformed | PcodeOp::stop_type_propagation | PcodeOp::store_unmapped); dup->setAdditionalFlag(fl); cloneList.emplace_back(dup,op); // Map from clone to orig origToClone[op] = dup; // Map from orig to clone return dup; } /// Make a basic clone of a Varnode and its flags. The clone is created /// as an output Varnode of a previously cloned PcodeOp. /// \param origOp is the given op whose output should be cloned /// \param cloneOp is the cloned version void CloneBlockOps::buildVarnodeOutput(PcodeOp *origOp,PcodeOp *cloneOp) { Varnode *opvn = origOp->getOut(); Varnode *newvn; if (opvn == (Varnode *)0) return; newvn = data.newVarnodeOut(opvn->getSize(),opvn->getAddr(),cloneOp); uint4 vflags = opvn->getFlags(); vflags &= (Varnode::externref | Varnode::volatil | Varnode::incidental_copy | Varnode::readonly | Varnode::persist | Varnode::addrtied | Varnode::addrforce | Varnode::nolocalalias | Varnode::spacebase | Varnode::indirect_creation | Varnode::return_address | Varnode::precislo | Varnode::precishi | Varnode::incidental_copy); newvn->setFlags(vflags); uint2 aflags = opvn->addlflags; aflags &= (Varnode::writemask | Varnode::ptrflow | Varnode::stack_store); newvn->addlflags |= aflags; } /// P-code in a basic block is cloned into the split version of the block. /// \param b is the original basic block /// \param bprime is the cloned block /// \param inedge is the incoming edge index that was split on void CloneBlockOps::cloneBlock(BlockBasic *b,BlockBasic *bprime,int4 inedge) { PcodeOp *origOp,*cloneOp; list::iterator iter; for(iter=b->beginOp();iter!=b->endOp();++iter) { origOp = *iter; cloneOp = buildOpClone(origOp); if (cloneOp == (PcodeOp *)0) continue; buildVarnodeOutput(origOp,cloneOp); data.opInsertEnd(cloneOp,bprime); } patchInputs(inedge); } /// P-code in the list is cloned right before the given \b followOp. /// \param ops is the list of ops to clone /// \param followOp is the point where the cloned ops are inserted /// \return the output Varnode of the last cloned op Varnode *CloneBlockOps::cloneExpression(vector &ops,PcodeOp *followOp) { PcodeOp *origOp,*cloneOp; for(int4 i=0;igetOut(); } /// Map Varnodes that are inputs for PcodeOps in the original basic block to the input slots of /// the cloned ops. Constants and code ref Varnodes need to be duplicated, other Varnodes are shared /// between the ops. This routine also pulls an input Varnode out of original MULTIEQUAL ops and adds /// it back to the cloned MULTIEQUAL ops. /// \param inedge is the incoming edge index that was split on void CloneBlockOps::patchInputs(int4 inedge) { for(int4 pos=0;poscode() == CPUI_MULTIEQUAL) { cloneOp->setNumInputs(1); // One edge now goes into the new block data.opSetOpcode(cloneOp,CPUI_COPY); data.opSetInput(cloneOp,origOp->getIn(inedge),0); data.opRemoveInput(origOp,inedge); // One edge is removed from original block if (origOp->numInput() == 1) data.opSetOpcode(origOp,CPUI_COPY); } else if (origOp->code() == CPUI_INDIRECT) { throw LowlevelError("Can't clone INDIRECTs"); } else if (origOp->isCall()) { throw LowlevelError("Can't clone CALLs"); } else { for(int4 i=0;inumInput();++i) { Varnode *origVn = origOp->getIn(i); Varnode *cloneVn; if (origVn->isConstant()) cloneVn = origVn; else if (origVn->isAnnotation()) cloneVn = data.newCodeRef(origVn->getAddr()); else if (origVn->isFree()) throw LowlevelError("Can't clone free varnode"); else { if (origVn->isWritten()) { map::const_iterator iter = origToClone.find(origVn->getDef()); if (iter != origToClone.end()) { cloneVn = (*iter).second->getOut(); } else cloneVn = origVn; } else cloneVn = origVn; } data.opSetInput(cloneOp,cloneVn,i); } } } } } // End namespace ghidra