mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-03 09:49:23 +02:00
1451 lines
49 KiB
C++
1451 lines
49 KiB
C++
/* ###
|
|
* 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 ops
|
|
|
|
/// \param op is the given PcodeOp
|
|
/// \param opc is the op-code to set
|
|
void Funcdata::opSetOpcode(PcodeOp *op,OpCode opc)
|
|
|
|
{
|
|
#ifdef OPACTION_DEBUG
|
|
if (opactdbg_active)
|
|
debugModCheck(op);
|
|
#endif
|
|
obank.changeOpcode(op, glb->inst[opc] );
|
|
}
|
|
|
|
/// \param op is the given CPUI_RETURN op
|
|
/// \param flag is one of \e halt, \e badinstruction, \e unimplemented, \e noreturn, or \e missing.
|
|
void Funcdata::opMarkHalt(PcodeOp *op,uint4 flag)
|
|
|
|
{
|
|
if (op->code() != CPUI_RETURN)
|
|
throw LowlevelError("Only RETURN pcode ops can be marked as halt");
|
|
flag &= (PcodeOp::halt|PcodeOp::badinstruction|
|
|
PcodeOp::unimplemented|PcodeOp::noreturn|
|
|
PcodeOp::missing);
|
|
if (flag == 0)
|
|
throw LowlevelError("Bad halt flag");
|
|
op->setFlag(flag);
|
|
}
|
|
|
|
/// The output Varnode becomes \e free but is not immediately deleted.
|
|
/// \param op is the given PcodeOp
|
|
void Funcdata::opUnsetOutput(PcodeOp *op)
|
|
|
|
{
|
|
Varnode *vn;
|
|
|
|
vn = op->getOut();
|
|
if (vn == (Varnode *)0) return; // Nothing to do
|
|
#ifdef OPACTION_DEBUG
|
|
if (opactdbg_active)
|
|
debugModCheck(op);
|
|
#endif
|
|
op->setOutput((Varnode *)0); // This must come before make_free
|
|
vbank.makeFree(vn);
|
|
vn->clearCover();
|
|
}
|
|
|
|
/// \param op is the specific PcodeOp
|
|
/// \param vn is the output Varnode to set
|
|
void Funcdata::opSetOutput(PcodeOp *op,Varnode *vn)
|
|
|
|
{
|
|
if (vn == op->getOut()) return; // Already set to this vn
|
|
#ifdef OPACTION_DEBUG
|
|
if (opactdbg_active)
|
|
debugModCheck(op);
|
|
#endif
|
|
if (op->getOut() != (Varnode *)0) {
|
|
opUnsetOutput(op);
|
|
}
|
|
|
|
if (vn->getDef() != (PcodeOp *)0) // If this varnode is already an output
|
|
opUnsetOutput(vn->getDef());
|
|
vn = vbank.setDef(vn,op);
|
|
setVarnodeProperties(vn);
|
|
op->setOutput(vn);
|
|
}
|
|
|
|
/// The input Varnode is unlinked from the op.
|
|
/// \param op is the given PcodeOp
|
|
/// \param slot is the input slot to clear
|
|
void Funcdata::opUnsetInput(PcodeOp *op,int4 slot)
|
|
|
|
{
|
|
Varnode *vn = op->getIn(slot);
|
|
|
|
vn->eraseDescend(op);
|
|
op->clearInput(slot); // Must be called AFTER descend_erase
|
|
}
|
|
|
|
/// \param op is the given PcodeOp
|
|
/// \param vn is the operand Varnode to set
|
|
/// \param slot is the input slot where the Varnode is placed
|
|
void Funcdata::opSetInput(PcodeOp *op,Varnode *vn,int4 slot)
|
|
|
|
{
|
|
if (vn == op->getIn(slot)) return; // Already set to this vn
|
|
if (vn->isConstant()) { // Constants should have only one descendant
|
|
if (!vn->hasNoDescend())
|
|
if (!vn->isSpacebase()) { // Unless they are a spacebase
|
|
Varnode *cvn = newConstant(vn->getSize(),vn->getOffset());
|
|
cvn->copySymbol(vn);
|
|
vn = cvn;
|
|
}
|
|
}
|
|
#ifdef OPACTION_DEBUG
|
|
if (opactdbg_active)
|
|
debugModCheck(op);
|
|
#endif
|
|
if (op->getIn(slot) != (Varnode *)0)
|
|
opUnsetInput(op,slot);
|
|
|
|
vn->addDescend(op); // Add this op to list of vn's descendants
|
|
op->setInput(vn,slot); // op must be up to date AFTER calling descend_add
|
|
}
|
|
|
|
/// This is convenience method that is more efficient than call opSetInput() twice.
|
|
/// \param op is the given PcodeOp
|
|
/// \param slot1 is the first input slot being switched
|
|
/// \param slot2 is the second input slot
|
|
void Funcdata::opSwapInput(PcodeOp *op,int4 slot1,int4 slot2)
|
|
|
|
{
|
|
#ifdef OPACTION_DEBUG
|
|
if (opactdbg_active)
|
|
debugModCheck(op);
|
|
#endif
|
|
Varnode *tmp = op->getIn(slot1);
|
|
op->setInput(op->getIn(slot2),slot1);
|
|
op->setInput(tmp,slot2);
|
|
}
|
|
|
|
/// \brief Insert the given PcodeOp at specific point in a basic block
|
|
///
|
|
/// The PcodeOp is removed from the \e dead list and is inserted \e immediately before
|
|
/// the specified iterator.
|
|
/// \param op is the given PcodeOp
|
|
/// \param bl is the basic block being inserted into
|
|
/// \param iter indicates exactly where the op is inserted
|
|
void Funcdata::opInsert(PcodeOp *op,BlockBasic *bl,list<PcodeOp *>::iterator iter)
|
|
|
|
{
|
|
#ifdef OPACTION_DEBUG
|
|
if (opactdbg_active)
|
|
debugModCheck(op);
|
|
#endif
|
|
obank.markAlive(op);
|
|
bl->insert(iter,op);
|
|
}
|
|
|
|
/// The op is taken out of its basic block and put into the dead list. If the removal
|
|
/// is permanent the input and output Varnodes should be unset.
|
|
/// \param op is the given PcodeOp
|
|
void Funcdata::opUninsert(PcodeOp *op)
|
|
|
|
{
|
|
#ifdef OPACTION_DEBUG
|
|
if (opactdbg_active)
|
|
debugModCheck(op);
|
|
#endif
|
|
obank.markDead(op);
|
|
op->getParent()->removeOp(op);
|
|
}
|
|
|
|
/// The op is extricated from all its Varnode connections to the functions data-flow and
|
|
/// removed from its basic block. This will \e not change block connections. The PcodeOp
|
|
/// objects remains in the \e dead list.
|
|
/// \param op is the given PcodeOp
|
|
void Funcdata::opUnlink(PcodeOp *op)
|
|
|
|
{
|
|
int4 i;
|
|
#ifdef OPACTION_DEBUG
|
|
if (opactdbg_active)
|
|
debugModCheck(op);
|
|
#endif
|
|
// Unlink input and output varnodes
|
|
opUnsetOutput(op);
|
|
for(i=0;i<op->numInput();++i)
|
|
opUnsetInput(op,i);
|
|
if (op->getParent() != (BlockBasic *)0) // Remove us from basic block
|
|
opUninsert(op);
|
|
}
|
|
|
|
/// All input and output Varnodes to the op are destroyed (their object resources freed),
|
|
/// and the op is permanently moved to the \e dead list.
|
|
/// To call this routine, make sure that either:
|
|
/// - The op has no output
|
|
/// - The op's output has no descendants
|
|
/// - or all descendants of output are also going to be destroyed
|
|
///
|
|
/// \param op is the given PcodeOp
|
|
void Funcdata::opDestroy(PcodeOp *op)
|
|
|
|
{
|
|
#ifdef OPACTION_DEBUG
|
|
if (opactdbg_active)
|
|
debugModCheck(op);
|
|
#endif
|
|
|
|
if (op->getOut() != (Varnode *)0)
|
|
destroyVarnode(op->getOut());
|
|
for(int4 i=0;i<op->numInput();++i) {
|
|
Varnode *vn = op->getIn(i);
|
|
if (vn != (Varnode *)0)
|
|
opUnsetInput(op,i);
|
|
}
|
|
if (op->getParent() != (BlockBasic *)0) {
|
|
obank.markDead(op);
|
|
op->getParent()->removeOp(op);
|
|
}
|
|
}
|
|
|
|
/// This is a specialized routine for deleting an op during flow generation that has
|
|
/// been replaced by something else. The op is expected to be \e dead with none of its inputs
|
|
/// or outputs linked to anything else. Both the PcodeOp and all the input/output Varnodes are destroyed.
|
|
/// \param op is the given PcodeOp
|
|
void Funcdata::opDestroyRaw(PcodeOp *op)
|
|
|
|
{
|
|
for(int4 i=0;i<op->numInput();++i)
|
|
destroyVarnode(op->getIn(i));
|
|
if (op->getOut() != (Varnode *)0)
|
|
destroyVarnode(op->getOut());
|
|
obank.destroy(op);
|
|
}
|
|
|
|
/// All previously existing input Varnodes are unset. The input slots for the
|
|
/// op are resized and then filled in from the specified array.
|
|
/// \param op is the given PcodeOp to set
|
|
/// \param vvec is the specified array of new input Varnodes
|
|
void Funcdata::opSetAllInput(PcodeOp *op,const vector<Varnode *> &vvec)
|
|
|
|
{
|
|
int4 i;
|
|
|
|
#ifdef OPACTION_DEBUG
|
|
if (opactdbg_active)
|
|
debugModCheck(op);
|
|
#endif
|
|
for(i=0;i<op->numInput();++i)
|
|
if (op->getIn(i) != (Varnode *)0)
|
|
opUnsetInput(op,i);
|
|
|
|
op->setNumInputs( vvec.size() );
|
|
|
|
for(i=0;i<op->numInput();++i)
|
|
opSetInput(op,vvec[i],i);
|
|
}
|
|
|
|
/// The Varnode in the specified slot is unlinked from the op and the slot itself
|
|
/// is removed. The slot index for any remaining input Varnodes coming after the
|
|
/// specified slot is decreased by one.
|
|
/// \param op is the given PcodeOp
|
|
/// \param slot is the index of the specified slot to remove
|
|
void Funcdata::opRemoveInput(PcodeOp *op,int4 slot)
|
|
|
|
{
|
|
#ifdef OPACTION_DEBUG
|
|
if (opactdbg_active)
|
|
debugModCheck(op);
|
|
#endif
|
|
opUnsetInput(op,slot);
|
|
op->removeInput(slot);
|
|
}
|
|
|
|
/// The given Varnode is set into the given operand slot. Any existing input Varnodes
|
|
/// with slot indices equal to or greater than the specified slot are pushed into the
|
|
/// next slot.
|
|
/// \param op is the given PcodeOp
|
|
/// \param vn is the given Varnode to insert
|
|
/// \param slot is the input index to insert at
|
|
void Funcdata::opInsertInput(PcodeOp *op,Varnode *vn,int4 slot)
|
|
|
|
{
|
|
#ifdef OPACTION_DEBUG
|
|
if (opactdbg_active)
|
|
debugModCheck(op);
|
|
#endif
|
|
op->insertInput(slot);
|
|
opSetInput(op,vn,slot);
|
|
}
|
|
|
|
/// \param inputs is the number of operands the new op will have
|
|
/// \param pc is the Address associated with the new op
|
|
/// \return the new PcodeOp
|
|
PcodeOp *Funcdata::newOp(int4 inputs,const Address &pc)
|
|
|
|
{
|
|
return obank.create(inputs,pc);
|
|
}
|
|
|
|
/// This method is typically used for cloning.
|
|
/// \param inputs is the number of operands the new op will have
|
|
/// \param sq is the sequence number (Address and sub-index) of the new op
|
|
/// \return the new PcodeOp
|
|
PcodeOp *Funcdata::newOp(int4 inputs,const SeqNum &sq)
|
|
|
|
{
|
|
return obank.create(inputs,sq);
|
|
}
|
|
|
|
/// The given PcodeOp is inserted \e immediately before the \e follow op except:
|
|
/// - MULTIEQUALS in a basic block all occur first
|
|
/// - INDIRECTs occur immediately before their op
|
|
/// - a branch op must be the very last op in a basic block
|
|
///
|
|
/// \param op is the given PcodeOp to insert
|
|
/// \param follow is the op to insert before
|
|
void Funcdata::opInsertBefore(PcodeOp *op,PcodeOp *follow)
|
|
|
|
{
|
|
list<PcodeOp *>::iterator iter = follow->getBasicIter();
|
|
BlockBasic *parent = follow->getParent();
|
|
|
|
if (op->code() != CPUI_INDIRECT) {
|
|
// There should not be an INDIRECT immediately preceding op
|
|
PcodeOp *previousop;
|
|
while(iter != parent->beginOp()) {
|
|
--iter;
|
|
previousop = *iter;
|
|
if (previousop->code() != CPUI_INDIRECT) {
|
|
++iter;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
opInsert(op,parent,iter);
|
|
}
|
|
|
|
/// The given PcodeOp is inserted \e immediately after the \e prev op except:
|
|
/// - MULTIEQUALS in a basic block all occur first
|
|
/// - INDIRECTs occur immediately before their op
|
|
/// - a branch op must be the very last op in a basic block
|
|
///
|
|
/// \param op is the given PcodeOp to insert
|
|
/// \param prev is the op to insert after
|
|
void Funcdata::opInsertAfter(PcodeOp *op,PcodeOp *prev)
|
|
|
|
{
|
|
if (prev->isMarker()) {
|
|
if (prev->code() == CPUI_INDIRECT) {
|
|
Varnode *invn = prev->getIn(1);
|
|
if (invn->getSpace()->getType()==IPTR_IOP) {
|
|
PcodeOp *targOp = PcodeOp::getOpFromConst(invn->getAddr()); // Store or call
|
|
if (!targOp->isDead())
|
|
prev = targOp;
|
|
}
|
|
}
|
|
}
|
|
list<PcodeOp *>::iterator iter = prev->getBasicIter();
|
|
BlockBasic *parent = prev->getParent();
|
|
|
|
iter++;
|
|
|
|
if (op->code() != CPUI_MULTIEQUAL) {
|
|
// There should not be a MULTIEQUAL immediately after op
|
|
PcodeOp *nextop;
|
|
while(iter != parent->endOp()) {
|
|
nextop = *iter;
|
|
++iter;
|
|
if (nextop->code() != CPUI_MULTIEQUAL) {
|
|
--iter;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
opInsert(op,prev->getParent(),iter);
|
|
}
|
|
|
|
/// The given PcodeOp is inserted as the \e first op in the basic block except:
|
|
/// - MULTIEQUALS in a basic block all occur first
|
|
/// - INDIRECTs occur immediately before their op
|
|
/// - a branch op must be the very last op in a basic block
|
|
///
|
|
/// \param op is the given PcodeOp to insert
|
|
/// \param bl is the basic block to insert into
|
|
void Funcdata::opInsertBegin(PcodeOp *op,BlockBasic *bl)
|
|
|
|
{
|
|
list<PcodeOp *>::iterator iter = bl->beginOp();
|
|
|
|
if (op->code()!=CPUI_MULTIEQUAL) {
|
|
while(iter != bl->endOp()) {
|
|
if ((*iter)->code() != CPUI_MULTIEQUAL)
|
|
break;
|
|
++iter;
|
|
}
|
|
}
|
|
opInsert(op,bl,iter);
|
|
}
|
|
|
|
/// The given PcodeOp is inserted as the \e last op in the basic block except:
|
|
/// - MULTIEQUALS in a basic block all occur first
|
|
/// - INDIRECTs occur immediately before their op
|
|
/// - a branch op must be the very last op in a basic block
|
|
///
|
|
/// \param op is the given PcodeOp to insert
|
|
/// \param bl is the basic block to insert into
|
|
void Funcdata::opInsertEnd(PcodeOp *op,BlockBasic *bl)
|
|
|
|
{
|
|
list<PcodeOp *>::iterator iter = bl->endOp();
|
|
|
|
if (iter != bl->beginOp()) {
|
|
--iter;
|
|
if (!(*iter)->isFlowBreak())
|
|
++iter;
|
|
}
|
|
opInsert(op,bl,iter);
|
|
}
|
|
|
|
/// \brief Create an INT_ADD PcodeOp calculating an offset to the \e spacebase register.
|
|
///
|
|
/// The \e spacebase register is looked up for the given address space, or an optional previously
|
|
/// existing register Varnode can be provided. An insertion point op must be provided,
|
|
/// and newly generated ops can come either before or after this insertion point.
|
|
/// \param spc is the given address space
|
|
/// \param off is the offset to calculate relative to the \e spacebase register
|
|
/// \param op is the insertion point PcodeOp
|
|
/// \param stackptr is the \e spacebase register Varnode (if available)
|
|
/// \param insertafter is \b true if new ops are inserted \e after the insertion point
|
|
/// \return the \e unique space Varnode holding the calculated offset
|
|
Varnode *Funcdata::createStackRef(AddrSpace *spc,uintb off,PcodeOp *op,Varnode *stackptr,bool insertafter)
|
|
|
|
{
|
|
PcodeOp *addop;
|
|
Varnode *addout;
|
|
int4 addrsize;
|
|
|
|
// Calculate CURRENT stackpointer as base for relative offset
|
|
if (stackptr == (Varnode *)0) // If we are not reusing an old reference to the stack pointer
|
|
stackptr = newSpacebasePtr(spc); // create a new reference
|
|
addrsize = stackptr->getSize();
|
|
addop = newOp(2,op->getAddr());
|
|
opSetOpcode(addop,CPUI_INT_ADD);
|
|
addout = newUniqueOut(addrsize,addop);
|
|
opSetInput(addop,stackptr,0);
|
|
off = AddrSpace::byteToAddress(off,spc->getWordSize());
|
|
opSetInput(addop,newConstant(addrsize,off),1);
|
|
if (insertafter)
|
|
opInsertAfter(addop,op);
|
|
else
|
|
opInsertBefore(addop,op);
|
|
|
|
AddrSpace *containerid = spc->getContain();
|
|
SegmentOp *segdef = glb->userops.getSegmentOp(containerid->getIndex());
|
|
|
|
if (segdef != (SegmentOp *)0) {
|
|
PcodeOp *segop = newOp(3,op->getAddr());
|
|
opSetOpcode(segop,CPUI_SEGMENTOP);
|
|
Varnode *segout = newUniqueOut(containerid->getAddrSize(),segop);
|
|
opSetInput(segop,newVarnodeSpace(containerid),0);
|
|
opSetInput(segop,newConstant(segdef->getBaseSize(),0),1);
|
|
opSetInput(segop,addout,2);
|
|
opInsertAfter(segop,addop); // Make sure -segop- comes after -addop- regardless if before/after -op-
|
|
addout = segout;
|
|
}
|
|
|
|
return addout;
|
|
}
|
|
|
|
/// \brief Create a STORE expression at an offset relative to a \e spacebase register for a given address space
|
|
///
|
|
/// The \e spacebase register is looked up for the given address space. An insertion point
|
|
/// op must be provided, and newly generated ops can come either before or after this insertion point.
|
|
/// The Varnode value being stored must still be set on the returned PcodeOp.
|
|
/// \param spc is the given address space
|
|
/// \param off is the offset to calculate relative to the \e spacebase register
|
|
/// \param op is the insertion point PcodeOp
|
|
/// \param insertafter is \b true if new ops are inserted \e after the insertion point
|
|
/// \return the STORE PcodeOp
|
|
PcodeOp *Funcdata::opStackStore(AddrSpace *spc,uintb off,PcodeOp *op,bool insertafter)
|
|
|
|
{ // Create pcode sequence that stores a value at an offset relative to a spacebase
|
|
// -off- is the offset, -size- is the size of the value
|
|
// The sequence is inserted before/after -op- based on whether -insertafter- is false/true
|
|
// Return the store op
|
|
Varnode *addout;
|
|
PcodeOp *storeop;
|
|
|
|
// Calculate CURRENT stackpointer as base for relative offset
|
|
addout = createStackRef(spc,off,op,(Varnode *)0,insertafter);
|
|
|
|
storeop = newOp(3,op->getAddr());
|
|
opSetOpcode(storeop,CPUI_STORE);
|
|
|
|
opSetInput(storeop,newVarnodeSpace(spc->getContain()),0);
|
|
opSetInput(storeop,addout,1);
|
|
opInsertAfter(storeop,addout->getDef()); // STORE comes after stack building op, regardless of -insertafter-
|
|
return storeop;
|
|
}
|
|
|
|
/// \brief Create a LOAD expression at an offset relative to a \e spacebase register for a given address space
|
|
///
|
|
/// The \e spacebase register is looked up for the given address space, or an optional previously
|
|
/// existing register Varnode can be provided. An insertion point op must be provided,
|
|
/// and newly generated ops can come either before or after this insertion point.
|
|
/// \param spc is the given address space
|
|
/// \param off is the offset to calculate relative to the \e spacebase register
|
|
/// \param sz is the size of the desire LOAD in bytes
|
|
/// \param op is the insertion point PcodeOp
|
|
/// \param stackref is the \e spacebase register Varnode (if available)
|
|
/// \param insertafter is \b true if new ops are inserted \e after the insertion point
|
|
/// \return the \e unique space Varnode holding the result of the LOAD
|
|
Varnode *Funcdata::opStackLoad(AddrSpace *spc,uintb off,uint4 sz,PcodeOp *op,Varnode *stackref,bool insertafter)
|
|
|
|
{
|
|
Varnode *addout = createStackRef(spc,off,op,stackref,insertafter);
|
|
PcodeOp *loadop = newOp(2,op->getAddr());
|
|
opSetOpcode(loadop,CPUI_LOAD);
|
|
opSetInput(loadop,newVarnodeSpace(spc->getContain()),0);
|
|
opSetInput(loadop,addout,1);
|
|
Varnode *res = newUniqueOut(sz,loadop);
|
|
opInsertAfter(loadop,addout->getDef()); // LOAD comes after stack building op, regardless of -insertafter-
|
|
return res;
|
|
}
|
|
|
|
/// Convert the given CPUI_PTRADD into the equivalent CPUI_INT_ADD. This may involve inserting a
|
|
/// CPUI_INT_MULT PcodeOp. If finalization is requested and a new PcodeOp is needed, the output
|
|
/// Varnode is marked as \e implicit and has its data-type set
|
|
/// \param op is the given PTRADD
|
|
/// \param finalize is \b true if finalization is needed for any new PcodeOp
|
|
void Funcdata::opUndoPtradd(PcodeOp *op,bool finalize)
|
|
|
|
{
|
|
Varnode *multVn = op->getIn(2);
|
|
int4 multSize = multVn->getOffset(); // Size the PTRADD thinks we are pointing
|
|
|
|
opRemoveInput(op,2);
|
|
opSetOpcode(op,CPUI_INT_ADD);
|
|
if (multSize == 1) return; // If no multiplier, we are done
|
|
Varnode *offVn = op->getIn(1);
|
|
if (offVn->isConstant()) {
|
|
uintb newVal = multSize * offVn->getOffset();
|
|
newVal &= calc_mask(offVn->getSize());
|
|
Varnode *newOffVn = newConstant(offVn->getSize(), newVal);
|
|
if (finalize)
|
|
newOffVn->updateType(offVn->getTypeReadFacing(op), false, false);
|
|
opSetInput(op,newOffVn,1);
|
|
return;
|
|
}
|
|
PcodeOp *multOp = newOp(2,op->getAddr());
|
|
opSetOpcode(multOp,CPUI_INT_MULT);
|
|
Varnode *addVn = newUniqueOut(offVn->getSize(),multOp);
|
|
if (finalize) {
|
|
addVn->updateType(multVn->getType(), false, false);
|
|
addVn->setImplied();
|
|
}
|
|
opSetInput(multOp,offVn,0);
|
|
opSetInput(multOp,multVn,1);
|
|
opSetInput(op,addVn,1);
|
|
opInsertBefore(multOp,op);
|
|
}
|
|
|
|
/// Make a clone of the given PcodeOp, copying control-flow properties as well. The data-type
|
|
/// is \e not cloned.
|
|
/// \param op is the PcodeOp to clone
|
|
/// \param seq is the (possibly custom) sequence number to associate with the clone
|
|
/// \return the cloned PcodeOp
|
|
PcodeOp *Funcdata::cloneOp(const PcodeOp *op,const SeqNum &seq)
|
|
|
|
{
|
|
PcodeOp *newop = newOp(op->numInput(),seq);
|
|
opSetOpcode(newop,op->code());
|
|
uint4 fl = op->flags & (PcodeOp::startmark | PcodeOp::startbasic);
|
|
newop->setFlag(fl);
|
|
if (op->getOut() != (Varnode *)0)
|
|
opSetOutput(newop,cloneVarnode(op->getOut()));
|
|
for(int4 i=0;i<op->numInput();++i)
|
|
opSetInput(newop,cloneVarnode(op->getIn(i)),i);
|
|
return newop;
|
|
}
|
|
|
|
/// Return the first CPUI_RETURN operation that is not dead or an artificial halt
|
|
/// \return a representative CPUI_RETURN op or NULL if there are none
|
|
PcodeOp *Funcdata::getFirstReturnOp(void) const
|
|
|
|
{
|
|
list<PcodeOp *>::const_iterator iter,iterend;
|
|
iterend = endOp(CPUI_RETURN);
|
|
for(iter=beginOp(CPUI_RETURN);iter!=iterend;++iter) {
|
|
PcodeOp *retop = *iter;
|
|
if (retop->isDead()) continue;
|
|
if (retop->getHaltType()!=0) continue;
|
|
return retop;
|
|
}
|
|
return (PcodeOp *)0;
|
|
}
|
|
|
|
/// \brief Create new PcodeOp with 2 or 3 given operands
|
|
///
|
|
/// The new op will have a \e unique space output Varnode and will be inserted before
|
|
/// the given \e follow op.
|
|
/// \param follow is the \e follow up to insert the new PcodeOp before
|
|
/// \param opc is the op-code of the new PcodeOp
|
|
/// \param in1 is the first operand
|
|
/// \param in2 is the second operand
|
|
/// \param in3 is the optional third param
|
|
/// \return the new PcodeOp
|
|
PcodeOp *Funcdata::newOpBefore(PcodeOp *follow,OpCode opc,Varnode *in1,Varnode *in2,Varnode *in3)
|
|
|
|
{
|
|
PcodeOp *newop;
|
|
int4 sz;
|
|
|
|
sz = (in3 == (Varnode *)0) ? 2 : 3;
|
|
newop = newOp(sz,follow->getAddr());
|
|
opSetOpcode(newop,opc);
|
|
newUniqueOut(in1->getSize(),newop);
|
|
opSetInput(newop,in1,0);
|
|
opSetInput(newop,in2,1);
|
|
if (sz==3)
|
|
opSetInput(newop,in3,2);
|
|
opInsertBefore(newop,follow);
|
|
return newop;
|
|
}
|
|
|
|
/// \brief Create a new CPUI_INDIRECT around a PcodeOp with an indirect effect
|
|
///
|
|
/// Typically this is used to annotate data-flow, for the given storage range, passing
|
|
/// through a CALL or STORE. An output Varnode is automatically created.
|
|
/// \param indeffect is the PcodeOp with the indirect effect
|
|
/// \param addr is the starting address of the storage range to protect
|
|
/// \param sz is the number of bytes in the storage range
|
|
/// \param extraFlags are extra boolean properties to put on the INDIRECT
|
|
/// \return the new CPUI_INDIRECT op
|
|
PcodeOp *Funcdata::newIndirectOp(PcodeOp *indeffect,const Address &addr,int4 sz,uint4 extraFlags)
|
|
|
|
{
|
|
Varnode *newin;
|
|
PcodeOp *newop;
|
|
|
|
newin = newVarnode(sz,addr);
|
|
newop = newOp(2,indeffect->getAddr());
|
|
newop->flags |= extraFlags;
|
|
newVarnodeOut(sz,addr,newop);
|
|
opSetOpcode(newop,CPUI_INDIRECT);
|
|
opSetInput(newop,newin,0);
|
|
opSetInput(newop,newVarnodeIop(indeffect),1);
|
|
opInsertBefore(newop,indeffect);
|
|
return newop;
|
|
}
|
|
|
|
/// \brief Build a CPUI_INDIRECT op that \e indirectly \e creates a Varnode
|
|
///
|
|
/// An \e indirectly \e created Varnode effectively has no data-flow before the INDIRECT op
|
|
/// that defines it, and the value contained by the Varnode is not explicitly calculable.
|
|
/// The new Varnode is allocated with a given storage range.
|
|
/// \param indeffect is the p-code causing the indirect effect
|
|
/// \param addr is the starting address of the given storage range
|
|
/// \param sz is the number of bytes in the storage range
|
|
/// \param possibleout is \b true if the output should be treated as a \e directwrite.
|
|
/// \return the new CPUI_INDIRECT op
|
|
PcodeOp *Funcdata::newIndirectCreation(PcodeOp *indeffect,const Address &addr,int4 sz,bool possibleout)
|
|
|
|
{
|
|
Varnode *newout,*newin;
|
|
PcodeOp *newop;
|
|
|
|
newin = newConstant(sz,0);
|
|
newop = newOp(2,indeffect->getAddr());
|
|
newop->flags |= PcodeOp::indirect_creation;
|
|
newout = newVarnodeOut(sz,addr,newop);
|
|
if (!possibleout)
|
|
newin->flags |= Varnode::indirect_creation;
|
|
newout->flags |= Varnode::indirect_creation;
|
|
opSetOpcode(newop,CPUI_INDIRECT);
|
|
opSetInput(newop,newin,0);
|
|
opSetInput(newop,newVarnodeIop(indeffect),1);
|
|
opInsertBefore(newop,indeffect);
|
|
return newop;
|
|
}
|
|
|
|
/// Data-flow through the given CPUI_INDIRECT op is marked so that the output Varnode
|
|
/// is considered \e indirectly \e created.
|
|
/// An \e indirectly \e created Varnode effectively has no data-flow before the INDIRECT op
|
|
/// that defines it, and the value contained by the Varnode is not explicitly calculable.
|
|
/// \param indop is the given CPUI_INDIRECT op
|
|
/// \param possibleOutput is \b true if INDIRECT should be marked as a possible call output
|
|
void Funcdata::markIndirectCreation(PcodeOp *indop,bool possibleOutput)
|
|
|
|
{
|
|
Varnode *outvn = indop->getOut();
|
|
Varnode *in0 = indop->getIn(0);
|
|
|
|
indop->flags |= PcodeOp::indirect_creation;
|
|
if (!in0->isConstant())
|
|
throw LowlevelError("Indirect creation not properly formed");
|
|
if (!possibleOutput)
|
|
in0->flags |= Varnode::indirect_creation;
|
|
outvn->flags |= Varnode::indirect_creation;
|
|
}
|
|
|
|
/// \brief Generate raw p-code for the function
|
|
///
|
|
/// Follow flow from the entry point generating PcodeOps for each instruction encountered.
|
|
/// The caller can provide a bounding range that constrains where control can flow to.
|
|
/// \param baddr is the beginning of the constraining range
|
|
/// \param eaddr is the end of the constraining range
|
|
void Funcdata::followFlow(const Address &baddr,const Address &eaddr)
|
|
|
|
{
|
|
if (!obank.empty()) {
|
|
if ((flags & blocks_generated)==0)
|
|
throw LowlevelError("Function loaded for inlining");
|
|
return; // Already translated
|
|
}
|
|
|
|
uint4 fl = 0;
|
|
fl |= glb->flowoptions; // Global flow options
|
|
FlowInfo flow(*this,obank,bblocks,qlst);
|
|
flow.setRange(baddr,eaddr);
|
|
flow.setFlags(fl);
|
|
flow.setMaximumInstructions(glb->max_instructions);
|
|
flow.generateOps();
|
|
size = flow.getSize();
|
|
// Cannot keep track of function sizes in general because of non-contiguous functions
|
|
// glb->symboltab->update_size(name,size);
|
|
|
|
flow.generateBlocks();
|
|
flags |= blocks_generated;
|
|
switchOverJumpTables(flow);
|
|
if (flow.hasUnimplemented())
|
|
flags |= unimplemented_present;
|
|
if (flow.hasBadData())
|
|
flags |= baddata_present;
|
|
}
|
|
|
|
/// \brief Generate a clone with truncated control-flow given a partial function
|
|
///
|
|
/// Existing p-code is cloned from another function whose flow has not been completely
|
|
/// followed. Artificial halt operators are inserted wherever flow is incomplete and
|
|
/// basic blocks are generated.
|
|
/// \param fd is the partial function to clone
|
|
/// \param flow is partial function's flow information
|
|
void Funcdata::truncatedFlow(const Funcdata *fd,const FlowInfo *flow)
|
|
|
|
{
|
|
if (!obank.empty())
|
|
throw LowlevelError("Trying to do truncated flow on pre-existing pcode");
|
|
|
|
list<PcodeOp *>::const_iterator oiter; // Clone the raw pcode
|
|
for(oiter=fd->obank.beginDead();oiter!=fd->obank.endDead();++oiter)
|
|
cloneOp(*oiter,(*oiter)->getSeqNum());
|
|
obank.setUniqId(fd->obank.getUniqId());
|
|
|
|
// Clone callspecs
|
|
for(int4 i=0;i<fd->qlst.size();++i) {
|
|
FuncCallSpecs *oldspec = fd->qlst[i];
|
|
PcodeOp *newop = findOp(oldspec->getOp()->getSeqNum());
|
|
FuncCallSpecs *newspec = oldspec->clone(newop);
|
|
Varnode *invn0 = newop->getIn(0);
|
|
if (invn0->getSpace()->getType() == IPTR_FSPEC) { // Replace embedded pointer to callspec
|
|
Varnode *newvn0 = newVarnodeCallSpecs(newspec);
|
|
opSetInput(newop,newvn0,0);
|
|
deleteVarnode(invn0);
|
|
}
|
|
qlst.push_back(newspec);
|
|
}
|
|
|
|
vector<JumpTable *>::const_iterator jiter; // Clone the jumptables
|
|
for(jiter=fd->jumpvec.begin();jiter!=fd->jumpvec.end();++jiter) {
|
|
PcodeOp *indop = (*jiter)->getIndirectOp();
|
|
if (indop == (PcodeOp *)0) // If indirect op has not been linked, this is probably a jumptable override
|
|
continue; // that has not been reached by the flow yet, so we ignore/truncate it
|
|
PcodeOp *newop = findOp(indop->getSeqNum());
|
|
if (newop == (PcodeOp *)0)
|
|
throw LowlevelError("Could not trace jumptable across partial clone");
|
|
JumpTable *jtclone = new JumpTable(*jiter);
|
|
jtclone->setIndirectOp(newop);
|
|
jumpvec.push_back(jtclone);
|
|
}
|
|
|
|
FlowInfo partialflow(*this,obank,bblocks,qlst,flow); // Clone the flow
|
|
if (partialflow.hasInject())
|
|
partialflow.injectPcode();
|
|
// Clear error reporting flags
|
|
// Keep possible unreachable flag
|
|
partialflow.clearFlags(~((uint4)FlowInfo::possible_unreachable));
|
|
|
|
partialflow.generateBlocks(); // Generate basic blocks for partial flow
|
|
flags |= blocks_generated;
|
|
}
|
|
|
|
/// \brief In-line the p-code from another function into \b this function
|
|
///
|
|
/// Raw PcodeOps for the in-line function are generated and then cloned into
|
|
/// \b this function. Depending on the control-flow complexity of the in-line
|
|
/// function, the PcodeOps are injected as if they are all part of the call site
|
|
/// address (EZModel), or the PcodeOps preserve their address and extra branch
|
|
/// instructions are inserted to integrate control-flow of the in-line into
|
|
/// the calling function.
|
|
/// \param inlinefd is the function to in-line
|
|
/// \param flow is the flow object being injected
|
|
/// \param callop is the site of the injection
|
|
/// \return \b true if the injection was successful
|
|
bool Funcdata::inlineFlow(Funcdata *inlinefd,FlowInfo &flow,PcodeOp *callop)
|
|
|
|
{
|
|
inlinefd->getArch()->clearAnalysis(inlinefd);
|
|
FlowInfo inlineflow(*inlinefd,inlinefd->obank,inlinefd->bblocks,inlinefd->qlst);
|
|
inlinefd->obank.setUniqId( obank.getUniqId() );
|
|
|
|
// Generate the pcode ops to be inlined
|
|
Address baddr(baseaddr.getSpace(),0);
|
|
Address eaddr(baseaddr.getSpace(),~((uintb)0));
|
|
inlineflow.setRange(baddr,eaddr);
|
|
inlineflow.setFlags(FlowInfo::error_outofbounds|FlowInfo::error_unimplemented|
|
|
FlowInfo::error_reinterpreted|FlowInfo::flow_forinline);
|
|
inlineflow.forwardRecursion(flow);
|
|
inlineflow.generateOps();
|
|
|
|
if (inlineflow.checkEZModel()) {
|
|
// With an EZ clone there are no jumptables to clone
|
|
list<PcodeOp *>::const_iterator oiter = obank.endDead();
|
|
--oiter; // There is at least one op
|
|
flow.inlineEZClone(inlineflow,callop->getAddr());
|
|
++oiter;
|
|
if (oiter != obank.endDead()) { // If there was at least one PcodeOp cloned
|
|
PcodeOp *firstop = *oiter;
|
|
oiter = obank.endDead();
|
|
--oiter;
|
|
PcodeOp *lastop = *oiter;
|
|
obank.moveSequenceDead(firstop,lastop,callop); // Move cloned sequence to right after callop
|
|
if (callop->isBlockStart())
|
|
firstop->setFlag(PcodeOp::startbasic); // First op of inline inherits callop's startbasic flag
|
|
else
|
|
firstop->clearFlag(PcodeOp::startbasic);
|
|
}
|
|
opDestroyRaw(callop);
|
|
}
|
|
else {
|
|
Address retaddr;
|
|
if (!flow.testHardInlineRestrictions(inlinefd,callop,retaddr))
|
|
return false;
|
|
vector<JumpTable *>::const_iterator jiter; // Clone any jumptables from inline piece
|
|
for(jiter=inlinefd->jumpvec.begin();jiter!=inlinefd->jumpvec.end();++jiter) {
|
|
JumpTable *jtclone = new JumpTable(*jiter);
|
|
jumpvec.push_back(jtclone);
|
|
}
|
|
flow.inlineClone(inlineflow,retaddr);
|
|
|
|
// Convert CALL op to a jump
|
|
while(callop->numInput()>1)
|
|
opRemoveInput(callop,callop->numInput()-1);
|
|
|
|
opSetOpcode(callop,CPUI_BRANCH);
|
|
Varnode *inlineaddr = newCodeRef( inlinefd->getAddress() );
|
|
opSetInput(callop,inlineaddr,0);
|
|
}
|
|
|
|
obank.setUniqId( inlinefd->obank.getUniqId() );
|
|
|
|
return true;
|
|
}
|
|
|
|
/// \brief Find the primary branch operation for an instruction
|
|
///
|
|
/// For machine instructions that branch, this finds the \e primary PcodeOp that performs
|
|
/// the branch. The instruction is provided as a list of p-code ops, and the caller can
|
|
/// specify whether they expect to see a \e branch, \e call, or \e return operation.
|
|
/// \param iter is the start of the operations for the instruction
|
|
/// \param enditer is the end of the operations for the instruction
|
|
/// \param findbranch is \b true if the caller expects to see a BRANCH, CBRANCH, or BRANCHIND
|
|
/// \param findcall is \b true if the caller expects to see CALL or CALLIND
|
|
/// \param findreturn is \b true if the caller expects to see RETURN
|
|
/// \return the first branching PcodeOp that matches the criteria or NULL
|
|
PcodeOp *Funcdata::findPrimaryBranch(PcodeOpTree::const_iterator iter,PcodeOpTree::const_iterator enditer,
|
|
bool findbranch,bool findcall,bool findreturn)
|
|
{
|
|
while(iter != enditer) {
|
|
PcodeOp *op = (*iter).second;
|
|
switch(op->code()) {
|
|
case CPUI_BRANCH:
|
|
case CPUI_CBRANCH:
|
|
if (findbranch) {
|
|
if (!op->getIn(0)->isConstant()) // Make sure this is not an internal branch
|
|
return op;
|
|
}
|
|
break;
|
|
case CPUI_BRANCHIND:
|
|
if (findbranch)
|
|
return op;
|
|
break;
|
|
case CPUI_CALL:
|
|
case CPUI_CALLIND:
|
|
if (findcall)
|
|
return op;
|
|
break;
|
|
case CPUI_RETURN:
|
|
if (findreturn)
|
|
return op;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
++iter;
|
|
}
|
|
return (PcodeOp *)0;
|
|
}
|
|
|
|
/// \brief Override the control-flow p-code for a particular instruction
|
|
///
|
|
/// P-code in \b this function is modified to change the control-flow of
|
|
/// the instruction at the given address, based on the Override type.
|
|
/// \param addr is the given address of the instruction to modify
|
|
/// \param type is the Override type
|
|
void Funcdata::overrideFlow(const Address &addr,uint4 type)
|
|
|
|
{
|
|
PcodeOpTree::const_iterator iter = beginOp(addr);
|
|
PcodeOpTree::const_iterator enditer = endOp(addr);
|
|
|
|
PcodeOp *op = (PcodeOp *)0;
|
|
if (type == Override::BRANCH)
|
|
op = findPrimaryBranch(iter,enditer,false,true,true);
|
|
else if (type == Override::CALL)
|
|
op = findPrimaryBranch(iter,enditer,true,false,true);
|
|
else if (type == Override::CALL_RETURN)
|
|
op = findPrimaryBranch(iter,enditer,true,true,true);
|
|
else if (type == Override::RETURN)
|
|
op = findPrimaryBranch(iter,enditer,true,true,false);
|
|
|
|
if ((op == (PcodeOp *)0)||(!op->isDead()))
|
|
throw LowlevelError("Could not apply flowoverride");
|
|
|
|
OpCode opc = op->code();
|
|
if (type == Override::BRANCH) {
|
|
if (opc == CPUI_CALL)
|
|
opSetOpcode(op,CPUI_BRANCH);
|
|
else if (opc == CPUI_CALLIND)
|
|
opSetOpcode(op,CPUI_BRANCHIND);
|
|
else if (opc == CPUI_RETURN)
|
|
opSetOpcode(op,CPUI_BRANCHIND);
|
|
}
|
|
else if ((type == Override::CALL)||(type == Override::CALL_RETURN)) {
|
|
if (opc == CPUI_BRANCH)
|
|
opSetOpcode(op,CPUI_CALL);
|
|
else if (opc == CPUI_BRANCHIND)
|
|
opSetOpcode(op,CPUI_CALLIND);
|
|
else if (opc == CPUI_CBRANCH)
|
|
throw LowlevelError("Do not currently support CBRANCH overrides");
|
|
else if (opc == CPUI_RETURN)
|
|
opSetOpcode(op,CPUI_CALLIND);
|
|
if (type == Override::CALL_RETURN) { // Insert a new return op after call
|
|
PcodeOp *newReturn = newOp(1,addr);
|
|
opSetOpcode(newReturn,CPUI_RETURN);
|
|
opSetInput(newReturn,newConstant(1,0),0);
|
|
opDeadInsertAfter(newReturn,op);
|
|
}
|
|
}
|
|
else if (type == Override::RETURN) {
|
|
if ((opc == CPUI_BRANCH)||(opc == CPUI_CBRANCH)||(opc == CPUI_CALL))
|
|
throw LowlevelError("Do not currently support complex overrides");
|
|
else if (opc == CPUI_BRANCHIND)
|
|
opSetOpcode(op,CPUI_RETURN);
|
|
else if (opc == CPUI_CALLIND)
|
|
opSetOpcode(op,CPUI_RETURN);
|
|
}
|
|
}
|
|
|
|
/// Do in-place replacement of
|
|
/// - `c <= x` with `c-1 < x` OR
|
|
/// - `x <= c` with `x < c+1`
|
|
///
|
|
/// \param op is comparison PcodeOp
|
|
/// \return true if a valid replacement was performed
|
|
bool Funcdata::replaceLessequal(PcodeOp *op)
|
|
|
|
{
|
|
Varnode *vn;
|
|
int4 i;
|
|
intb val,diff;
|
|
|
|
if ((vn=op->getIn(0))->isConstant()) {
|
|
diff = -1;
|
|
i = 0;
|
|
}
|
|
else if ((vn=op->getIn(1))->isConstant()) {
|
|
diff = 1;
|
|
i = 1;
|
|
}
|
|
else
|
|
return false;
|
|
|
|
val = sign_extend(vn->getOffset(),8*vn->getSize()-1);
|
|
if (op->code() == CPUI_INT_SLESSEQUAL) {
|
|
if ((val<0)&&(val+diff>0)) return false; // Check for sign overflow
|
|
if ((val>0)&&(val+diff<0)) return false;
|
|
opSetOpcode(op,CPUI_INT_SLESS);
|
|
}
|
|
else { // Check for unsigned overflow
|
|
if ((diff==-1)&&(val==0)) return false;
|
|
if ((diff==1)&&(val==-1)) return false;
|
|
opSetOpcode(op,CPUI_INT_LESS);
|
|
}
|
|
uintb res = (val+diff) & calc_mask(vn->getSize());
|
|
Varnode *newvn = newConstant(vn->getSize(),res);
|
|
newvn->copySymbol(vn); // Preserve data-type (and any Symbol info)
|
|
opSetInput(op,newvn,i);
|
|
return true;
|
|
}
|
|
|
|
/// If a term has a multiplicative coefficient, but the underlying term is still additive,
|
|
/// in some situations we may need to distribute the coefficient before simplifying further.
|
|
/// The given PcodeOp is a INT_MULT where the second input is a constant. We also
|
|
/// know the first input is formed with INT_ADD. Distribute the coefficient to the INT_ADD inputs.
|
|
/// \param op is the given PcodeOp
|
|
/// \return \b true if the action was performed
|
|
bool Funcdata::distributeIntMultAdd(PcodeOp *op)
|
|
|
|
{
|
|
Varnode *newvn0,*newvn1;
|
|
PcodeOp *addop = op->getIn(0)->getDef();
|
|
Varnode *vn0 = addop->getIn(0);
|
|
Varnode *vn1 = addop->getIn(1);
|
|
if ((vn0->isFree())&&(!vn0->isConstant())) return false;
|
|
if ((vn1->isFree())&&(!vn1->isConstant())) return false;
|
|
uintb coeff = op->getIn(1)->getOffset();
|
|
int4 sz = op->getOut()->getSize();
|
|
// Do distribution
|
|
if (vn0->isConstant()) {
|
|
uintb val = coeff * vn0->getOffset();
|
|
val &= calc_mask(sz);
|
|
newvn0 = newConstant(sz,val);
|
|
}
|
|
else {
|
|
PcodeOp *newop0 = newOp(2,op->getAddr());
|
|
opSetOpcode(newop0,CPUI_INT_MULT);
|
|
newvn0 = newUniqueOut(sz,newop0);
|
|
opSetInput(newop0, vn0, 0); // To first input of original add
|
|
Varnode *newcvn = newConstant(sz,coeff);
|
|
opSetInput(newop0, newcvn, 1);
|
|
opInsertBefore(newop0, op);
|
|
}
|
|
|
|
if (vn1->isConstant()) {
|
|
uintb val = coeff * vn1->getOffset();
|
|
val &= calc_mask(sz);
|
|
newvn1 = newConstant(sz,val);
|
|
}
|
|
else {
|
|
PcodeOp *newop1 = newOp(2,op->getAddr());
|
|
opSetOpcode(newop1,CPUI_INT_MULT);
|
|
newvn1 = newUniqueOut(sz,newop1);
|
|
opSetInput(newop1, vn1, 0); // To second input of original add
|
|
Varnode *newcvn = newConstant(sz,coeff);
|
|
opSetInput(newop1, newcvn, 1);
|
|
opInsertBefore(newop1, op);
|
|
}
|
|
|
|
opSetInput( op, newvn0, 0); // new ADD's inputs are outputs of new MULTs
|
|
opSetInput( op, newvn1, 1);
|
|
opSetOpcode(op, CPUI_INT_ADD);
|
|
|
|
return true;
|
|
}
|
|
|
|
/// If:
|
|
/// - The given Varnode is defined by a CPUI_INT_MULT.
|
|
/// - The second input to the INT_MULT is a constant.
|
|
/// - The first input is defined by another CPUI_INT_MULT,
|
|
/// - This multiply is also by a constant.
|
|
///
|
|
/// The constants are combined and \b true is returned.
|
|
/// Otherwise no change is made and \b false is returned.
|
|
/// \param vn is the given Varnode
|
|
/// \return \b true if a change was made
|
|
bool Funcdata::collapseIntMultMult(Varnode *vn)
|
|
|
|
{
|
|
if (!vn->isWritten()) return false;
|
|
PcodeOp *op = vn->getDef();
|
|
if (op->code() != CPUI_INT_MULT) return false;
|
|
Varnode *constVnFirst = op->getIn(1);
|
|
if (!constVnFirst->isConstant()) return false;
|
|
if (!op->getIn(0)->isWritten()) return false;
|
|
PcodeOp *otherMultOp = op->getIn(0)->getDef();
|
|
if (otherMultOp->code() != CPUI_INT_MULT) return false;
|
|
Varnode *constVnSecond = otherMultOp->getIn(1);
|
|
if (!constVnSecond->isConstant()) return false;
|
|
Varnode *invn = otherMultOp->getIn(0);
|
|
if (invn->isFree()) return false;
|
|
int4 sz = invn->getSize();
|
|
uintb val = (constVnFirst->getOffset() * constVnSecond->getOffset()) & calc_mask(sz);
|
|
Varnode *newvn = newConstant(sz,val);
|
|
opSetInput(op,newvn,1);
|
|
opSetInput(op,invn,0);
|
|
return true;
|
|
}
|
|
|
|
/// Return a Varnode in the \e unique space that is defined by a COPY op taking the given Varnode as input.
|
|
/// If a COPY op to a \e unique already exists, it may be returned. If the preexisting COPY is not usable
|
|
/// at the specified \b point, it is redefined at an earlier point in the control-flow so that it can be used.
|
|
/// \param vn is the given Varnode to COPY
|
|
/// \param point is the PcodeOp where the copy needs to be available
|
|
/// \return the \e unique Varnode COPY
|
|
Varnode *Funcdata::buildCopyTemp(Varnode *vn,PcodeOp *point)
|
|
|
|
{
|
|
PcodeOp *otherOp = (PcodeOp *)0;
|
|
PcodeOp *usedCopy = (PcodeOp *)0;
|
|
list<PcodeOp *>::const_iterator iter;
|
|
for(iter=vn->beginDescend();iter!=vn->endDescend();++iter) {
|
|
PcodeOp *op = *iter;
|
|
if (op->code() != CPUI_COPY) continue;
|
|
Varnode *outvn = op->getOut();
|
|
if (outvn->getSpace()->getType() == IPTR_INTERNAL) {
|
|
if (outvn->isTypeLock())
|
|
continue;
|
|
otherOp = op;
|
|
break;
|
|
}
|
|
}
|
|
if (otherOp != (PcodeOp *)0) {
|
|
if (point->getParent() == otherOp->getParent()) {
|
|
if (point->getSeqNum().getOrder() < otherOp->getSeqNum().getOrder())
|
|
usedCopy = (PcodeOp *)0;
|
|
else
|
|
usedCopy = otherOp;
|
|
}
|
|
else {
|
|
BlockBasic *common;
|
|
common = (BlockBasic *)FlowBlock::findCommonBlock(point->getParent(),otherOp->getParent());
|
|
if (common == point->getParent())
|
|
usedCopy = (PcodeOp *)0;
|
|
else if (common == otherOp->getParent())
|
|
usedCopy = otherOp;
|
|
else { // Neither op is ancestor of the other
|
|
usedCopy = newOp(1,common->getStop());
|
|
opSetOpcode(usedCopy,CPUI_COPY);
|
|
newUniqueOut(vn->getSize(),usedCopy);
|
|
opSetInput(usedCopy,vn,0);
|
|
opInsertEnd(usedCopy,common);
|
|
}
|
|
}
|
|
}
|
|
if (usedCopy == (PcodeOp *)0) {
|
|
usedCopy = newOp(1,point->getAddr());
|
|
opSetOpcode(usedCopy, CPUI_COPY);
|
|
newUniqueOut(vn->getSize(), usedCopy);
|
|
opSetInput(usedCopy, vn, 0);
|
|
opInsertBefore(usedCopy, point);
|
|
}
|
|
if (otherOp != (PcodeOp *)0 && otherOp != usedCopy) {
|
|
totalReplace(otherOp->getOut(),usedCopy->getOut());
|
|
opDestroy(otherOp);
|
|
}
|
|
return usedCopy->getOut();
|
|
}
|
|
|
|
/// \brief Trace a boolean value to a set of PcodeOps that can be changed to flip the boolean value
|
|
///
|
|
/// The boolean Varnode is either the output of the given PcodeOp or the
|
|
/// first input if the PcodeOp is a CBRANCH. The list of ops that need flipping is
|
|
/// returned in an array
|
|
/// \param op is the given PcodeOp
|
|
/// \param fliplist is the array that will hold the ops to flip
|
|
/// \return 0 if the change normalizes, 1 if the change is ambivalent, 2 if the change does not normalize
|
|
int4 Funcdata::opFlipInPlaceTest(PcodeOp *op,vector<PcodeOp *> &fliplist)
|
|
|
|
{
|
|
Varnode *vn;
|
|
int4 subtest1,subtest2;
|
|
switch(op->code()) {
|
|
case CPUI_CBRANCH:
|
|
vn = op->getIn(1);
|
|
if (vn->loneDescend() != op) return 2;
|
|
if (!vn->isWritten()) return 2;
|
|
return opFlipInPlaceTest(vn->getDef(),fliplist);
|
|
case CPUI_INT_EQUAL:
|
|
case CPUI_FLOAT_EQUAL:
|
|
fliplist.push_back(op);
|
|
return 1;
|
|
case CPUI_BOOL_NEGATE:
|
|
case CPUI_INT_NOTEQUAL:
|
|
case CPUI_FLOAT_NOTEQUAL:
|
|
fliplist.push_back(op);
|
|
return 0;
|
|
case CPUI_INT_SLESS:
|
|
case CPUI_INT_LESS:
|
|
vn = op->getIn(0);
|
|
fliplist.push_back(op);
|
|
if (!vn->isConstant()) return 1;
|
|
return 0;
|
|
case CPUI_INT_SLESSEQUAL:
|
|
case CPUI_INT_LESSEQUAL:
|
|
vn = op->getIn(1);
|
|
fliplist.push_back(op);
|
|
if (vn->isConstant()) return 1;
|
|
return 0;
|
|
case CPUI_BOOL_OR:
|
|
case CPUI_BOOL_AND:
|
|
vn = op->getIn(0);
|
|
if (vn->loneDescend() != op) return 2;
|
|
if (!vn->isWritten()) return 2;
|
|
subtest1 = opFlipInPlaceTest(vn->getDef(),fliplist);
|
|
if (subtest1 == 2)
|
|
return 2;
|
|
vn = op->getIn(1);
|
|
if (vn->loneDescend() != op) return 2;
|
|
if (!vn->isWritten()) return 2;
|
|
subtest2 = opFlipInPlaceTest(vn->getDef(),fliplist);
|
|
if (subtest2 == 2)
|
|
return 2;
|
|
fliplist.push_back(op);
|
|
return subtest1; // Front of AND/OR must be normalizing
|
|
default:
|
|
break;
|
|
}
|
|
return 2;
|
|
}
|
|
|
|
/// \brief Perform op-code flips (in-place) to change a boolean value
|
|
///
|
|
/// The precomputed list of PcodeOps have their op-codes modified to
|
|
/// facilitate the flip.
|
|
/// \param data is the function being modified
|
|
/// \param fliplist is the list of PcodeOps to modify
|
|
void Funcdata::opFlipInPlaceExecute(vector<PcodeOp *> &fliplist)
|
|
|
|
{
|
|
Varnode *vn;
|
|
for(int4 i=0;i<fliplist.size();++i) {
|
|
PcodeOp *op = fliplist[i];
|
|
bool flipyes;
|
|
OpCode opc = get_booleanflip(op->code(),flipyes);
|
|
if (opc == CPUI_COPY) { // We remove this (CPUI_BOOL_NEGATE) entirely
|
|
vn = op->getIn(0);
|
|
PcodeOp *otherop = op->getOut()->loneDescend(); // Must be a lone descendant
|
|
int4 slot = otherop->getSlot(op->getOut());
|
|
opSetInput(otherop,vn,slot); // Propagate -vn- into otherop
|
|
opDestroy(op);
|
|
}
|
|
else if (opc == CPUI_MAX) {
|
|
if (op->code() == CPUI_BOOL_AND)
|
|
opSetOpcode(op,CPUI_BOOL_OR);
|
|
else if (op->code() == CPUI_BOOL_OR)
|
|
opSetOpcode(op,CPUI_BOOL_AND);
|
|
else
|
|
throw LowlevelError("Bad flipInPlace op");
|
|
}
|
|
else {
|
|
opSetOpcode(op,opc);
|
|
if (flipyes) {
|
|
opSwapInput(op,0,1);
|
|
|
|
if ((opc == CPUI_INT_LESSEQUAL)||(opc == CPUI_INT_SLESSEQUAL))
|
|
replaceLessequal(op);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// \brief Find a duplicate calculation of a given PcodeOp reading a specific Varnode
|
|
///
|
|
/// We only match 1 level of calculation. Additionally the duplicate must occur in the
|
|
/// indicated basic block, earlier than a specified op.
|
|
/// \param op is the given PcodeOp
|
|
/// \param vn is the specific Varnode that must be involved in the calculation
|
|
/// \param bl is the indicated basic block
|
|
/// \param earliest is the specified op to be earlier than
|
|
/// \return the discovered duplicate PcodeOp or NULL
|
|
PcodeOp *Funcdata::cseFindInBlock(PcodeOp *op,Varnode *vn,BlockBasic *bl,PcodeOp *earliest)
|
|
|
|
{
|
|
list<PcodeOp *>::const_iterator iter;
|
|
|
|
for(iter=vn->beginDescend();iter!=vn->endDescend();++iter) {
|
|
PcodeOp *res = *iter;
|
|
if (res == op) continue; // Must not be -op-
|
|
if (res->getParent() != bl) continue; // Must be in -bl-
|
|
if (earliest != (PcodeOp *)0) {
|
|
if (earliest->getSeqNum().getOrder() < res->getSeqNum().getOrder()) continue; // Must occur earlier than earliest
|
|
}
|
|
Varnode *outvn1 = op->getOut();
|
|
Varnode *outvn2 = res->getOut();
|
|
if (outvn2 == (Varnode *)0) continue;
|
|
Varnode *buf1[2];
|
|
Varnode *buf2[2];
|
|
if (functionalEqualityLevel(outvn1,outvn2,buf1,buf2) == 0)
|
|
return res;
|
|
}
|
|
return (PcodeOp *)0;
|
|
}
|
|
|
|
/// \brief Perform a Common Subexpression Elimination step
|
|
///
|
|
/// Assuming the two given PcodeOps perform the identical operation on identical operands
|
|
/// (depth 1 functional equivalence) eliminate the redundancy. Return the remaining (dominating)
|
|
/// PcodeOp. If neither op dominates the other, both are eliminated, and a new PcodeOp
|
|
/// is built at a commonly accessible point.
|
|
/// \param op1 is the first of the given PcodeOps
|
|
/// \param op2 is the second given PcodeOp
|
|
/// \return the dominating PcodeOp
|
|
PcodeOp *Funcdata::cseElimination(PcodeOp *op1,PcodeOp *op2)
|
|
|
|
{
|
|
PcodeOp *replace;
|
|
|
|
if (op1->getParent() == op2->getParent()) {
|
|
if (op1->getSeqNum().getOrder() < op2->getSeqNum().getOrder())
|
|
replace = op1;
|
|
else
|
|
replace = op2;
|
|
}
|
|
else {
|
|
BlockBasic *common;
|
|
common = (BlockBasic *)FlowBlock::findCommonBlock(op1->getParent(),op2->getParent());
|
|
if (common == op1->getParent())
|
|
replace = op1;
|
|
else if (common == op2->getParent())
|
|
replace = op2;
|
|
else { // Neither op is ancestor of the other
|
|
replace = newOp(op1->numInput(),common->getStop());
|
|
opSetOpcode(replace,op1->code());
|
|
newVarnodeOut(op1->getOut()->getSize(),op1->getOut()->getAddr(),replace);
|
|
for(int4 i=0;i<op1->numInput();++i) {
|
|
if (op1->getIn(i)->isConstant())
|
|
opSetInput(replace,newConstant(op1->getIn(i)->getSize(),op1->getIn(i)->getOffset()),i);
|
|
else
|
|
opSetInput(replace,op1->getIn(i),i);
|
|
}
|
|
opInsertEnd(replace,common);
|
|
}
|
|
}
|
|
if (replace != op1) {
|
|
totalReplace(op1->getOut(),replace->getOut());
|
|
opDestroy(op1);
|
|
}
|
|
if (replace != op2) {
|
|
totalReplace(op2->getOut(),replace->getOut());
|
|
opDestroy(op2);
|
|
}
|
|
return replace;
|
|
}
|
|
|
|
/// \brief Comparator for (hash,PcodeOp) pairs
|
|
///
|
|
/// Compare by hash.
|
|
/// \param a is the first pair
|
|
/// \param b is the second pair
|
|
/// \return \b true if the first comes before the second
|
|
static bool compareCseHash(const pair<uintm,PcodeOp *> &a,const pair<uintm,PcodeOp *> &b)
|
|
|
|
{
|
|
return (a.first < b.first);
|
|
}
|
|
|
|
/// \brief Perform Common Subexpression Elimination on a list of Varnode descendants
|
|
///
|
|
/// The list consists of PcodeOp descendants of a single Varnode paired with a hash value.
|
|
/// The hash serves as a primary test for duplicate calculations; if it doesn't match
|
|
/// the PcodeOps aren't common subexpressions. This method searches for hash matches
|
|
/// then does secondary testing and eliminates any redundancy it finds.
|
|
/// \param list is the list of (hash, PcodeOp) pairs
|
|
/// \param outlist will hold Varnodes produced by duplicate calculations
|
|
void Funcdata::cseEliminateList(vector< pair<uintm,PcodeOp *> > &list,vector<Varnode *> &outlist)
|
|
|
|
{
|
|
PcodeOp *op1,*op2,*resop;
|
|
vector< pair<uintm,PcodeOp *> >::iterator liter1,liter2;
|
|
|
|
if (list.empty()) return;
|
|
stable_sort(list.begin(),list.end(),compareCseHash);
|
|
liter1 = list.begin();
|
|
liter2 = list.begin();
|
|
liter2++;
|
|
while(liter2 != list.end()) {
|
|
if ((*liter1).first == (*liter2).first) {
|
|
op1 = (*liter1).second;
|
|
op2 = (*liter2).second;
|
|
if ((!op1->isDead())&&(!op2->isDead())&&op1->isCseMatch(op2)) {
|
|
Varnode *outvn1 = op1->getOut();
|
|
Varnode *outvn2 = op2->getOut();
|
|
if ((outvn1 == (Varnode *)0)||isHeritaged(outvn1)) {
|
|
if ((outvn2 == (Varnode *)0)||isHeritaged(outvn2)) {
|
|
resop = cseElimination(op1,op2);
|
|
outlist.push_back(resop->getOut());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
liter1++;
|
|
liter2++;
|
|
}
|
|
}
|
|
|
|
/// This routine should be called only after Varnode merging and explicit/implicit attributes have
|
|
/// been calculated. Determine if the given op can be moved (only within its basic block) to
|
|
/// after \e lastOp. The output of any PcodeOp moved across must not be involved, directly or
|
|
/// indirectly, with any variable in the expression rooted at the given op.
|
|
/// If the move is possible, perform the move.
|
|
/// \param op is the given PcodeOp
|
|
/// \param lastOp is the PcodeOp to move past
|
|
/// \return \b true if the move is possible
|
|
bool Funcdata::moveRespectingCover(PcodeOp *op,PcodeOp *lastOp)
|
|
|
|
{
|
|
if (op == lastOp) return true; // Nothing to move past
|
|
if (op->isCall()) return false;
|
|
PcodeOp *prevOp = (PcodeOp *)0;
|
|
if (op->code() == CPUI_CAST) {
|
|
Varnode *vn = op->getIn(0);
|
|
if (!vn->isExplicit()) { // If CAST is part of expression, we need to move the previous op as well
|
|
if (!vn->isWritten()) return false;
|
|
prevOp = vn->getDef();
|
|
if (prevOp->isCall()) return false;
|
|
if (op->previousOp() != prevOp) return false; // Previous op must exist and feed into the CAST
|
|
}
|
|
}
|
|
Varnode *rootvn = op->getOut();
|
|
vector<HighVariable *> highList;
|
|
int4 typeVal = HighVariable::markExpression(rootvn, highList);
|
|
PcodeOp *curOp = op;
|
|
do {
|
|
PcodeOp *nextOp = curOp->nextOp();
|
|
OpCode opc = nextOp->code();
|
|
if (opc != CPUI_COPY && opc != CPUI_CAST) break; // Limit ourselves to only crossing COPY and CAST ops
|
|
if (rootvn == nextOp->getIn(0)) break; // Data-flow order dependence
|
|
Varnode *copyVn = nextOp->getOut();
|
|
if (copyVn->getHigh()->isMark()) break; // Direct interference: COPY writes what original op reads
|
|
if (typeVal != 0 && copyVn->isAddrTied()) break; // Possible indirect interference
|
|
curOp = nextOp;
|
|
} while(curOp != lastOp);
|
|
for(int4 i=0;i<highList.size();++i) // Clear marks on expression
|
|
highList[i]->clearMark();
|
|
if (curOp == lastOp) { // If we are able to cross everything
|
|
opUninsert(op); // Move -op-
|
|
opInsertAfter(op, lastOp);
|
|
if (prevOp != (PcodeOp *)0) { // If there was a CAST, move both ops
|
|
opUninsert(prevOp);
|
|
opInsertAfter(prevOp, lastOp);
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
} // End namespace ghidra
|