mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-03 09:49:23 +02:00
1213 lines
37 KiB
C++
1213 lines
37 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 "op.hh"
|
|
#include "funcdata.hh"
|
|
|
|
namespace ghidra {
|
|
|
|
ElementId ELEM_IOP = ElementId("iop",113);
|
|
ElementId ELEM_UNIMPL = ElementId("unimpl",114);
|
|
|
|
const string IopSpace::NAME = "iop";
|
|
|
|
/// Constructor for the \b iop space.
|
|
/// There is only one such space, and it is considered internal
|
|
/// to the model, i.e. the Translate engine should never generate
|
|
/// addresses in this space.
|
|
/// \param m is the associated address space manager
|
|
/// \param t is the associated processor translator
|
|
/// \param ind is the associated index
|
|
IopSpace::IopSpace(AddrSpaceManager *m,const Translate *t,int4 ind)
|
|
: AddrSpace(m,t,IPTR_IOP,NAME,false,sizeof(void *),1,ind,0,1,1)
|
|
{
|
|
clearFlags(heritaged|does_deadcode|big_endian);
|
|
if (HOST_ENDIAN==1) // Endianness always set to host
|
|
setFlags(big_endian);
|
|
}
|
|
|
|
void IopSpace::printRaw(ostream &s,uintb offset) const
|
|
|
|
{ // Print info about op this address refers to
|
|
BlockBasic *bs;
|
|
BlockBasic *bl;
|
|
PcodeOp *op = (PcodeOp *)(uintp)offset; // Treat offset as op
|
|
|
|
if (!op->isBranch()) { // op parameter for CPUI_INDIRECT
|
|
s << op->getSeqNum();
|
|
return;
|
|
}
|
|
bs = op->getParent();
|
|
if (bs->sizeOut()==2) // We print the non-fallthru condition
|
|
bl = (BlockBasic *)(op->isFallthruTrue() ? bs->getOut(0) : bs->getOut(1));
|
|
else
|
|
bl = (BlockBasic *)bs->getOut(0);
|
|
s << "code_" << bl->getStart().getShortcut();
|
|
bl->getStart().printRaw(s);
|
|
}
|
|
|
|
void IopSpace::decode(Decoder &decoder)
|
|
|
|
{
|
|
throw LowlevelError("Should never decode iop space from stream");
|
|
}
|
|
|
|
/// Construct a completely unattached PcodeOp. Space is reserved for input and output Varnodes
|
|
/// but all are set initially to null.
|
|
/// \param s indicates the number of input slots reserved
|
|
/// \param sq is the sequence number to associate with the new PcodeOp
|
|
PcodeOp::PcodeOp(int4 s,const SeqNum &sq) : start(sq),inrefs(s)
|
|
|
|
{
|
|
flags = 0; // Start out life as dead
|
|
addlflags = 0;
|
|
parent = (BlockBasic *)0; // No parent yet
|
|
|
|
output = (Varnode *) 0;
|
|
opcode = (TypeOp *)0;
|
|
for(int4 i=0;i<inrefs.size();++i)
|
|
inrefs[i] = (Varnode *)0;
|
|
}
|
|
|
|
/// \brief Find the slot for a given Varnode, which may be take up multiple input slots
|
|
///
|
|
/// In the rare case that \b this PcodeOp takes the same Varnode as input multiple times,
|
|
/// use the specific descendant iterator producing \b this PcodeOp to work out the corresponding slot.
|
|
/// Every slot containing the given Varnode will be produced exactly once over the course of iteration.
|
|
/// \param vn is the given Varnode
|
|
/// \param firstSlot is the first instance of the Varnode in \b this input list
|
|
/// \param iter is the specific descendant iterator producing \b this
|
|
/// \return the slot corresponding to the iterator
|
|
int4 PcodeOp::getRepeatSlot(const Varnode *vn,int4 firstSlot,list<PcodeOp *>::const_iterator iter) const
|
|
|
|
{
|
|
int4 count = 1;
|
|
for(list<PcodeOp *>::const_iterator oiter=vn->beginDescend();oiter != iter;++oiter) {
|
|
if ((*oiter) == this)
|
|
count += 1;
|
|
}
|
|
if (count == 1) return firstSlot;
|
|
int4 recount = 1;
|
|
for(int4 i=firstSlot+1;i<inrefs.size();++i) {
|
|
if (inrefs[i] == vn) {
|
|
recount += 1;
|
|
if (recount == count)
|
|
return i;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/// Can this be collapsed to a copy op, i.e. are all inputs constants
|
|
/// \return \b true if this op can be callapsed
|
|
bool PcodeOp::isCollapsible(void) const
|
|
|
|
{
|
|
if ((flags & PcodeOp::nocollapse)!=0) return false;
|
|
if (!isAssignment()) return false;
|
|
if (inrefs.size()==0) return false;
|
|
for(int4 i=0;i<inrefs.size();++i)
|
|
if (!getIn(i)->isConstant()) return false;
|
|
if (getOut()->getSize() > sizeof(uintb)) return false;
|
|
return true;
|
|
}
|
|
|
|
/// Produce a hash of the following attributes: output size, the opcode, and the identity
|
|
/// of each input varnode. This is suitable for determining if two PcodeOps calculate identical values
|
|
/// \return the calculated hash or 0 if the op is not cse hashable
|
|
uintm PcodeOp::getCseHash(void) const
|
|
|
|
{
|
|
uintm hash;
|
|
if ((getEvalType()&(PcodeOp::unary|PcodeOp::binary))==0) return ((uintm)0);
|
|
if (code()==CPUI_COPY) return ((uintm)0); // Let copy propagation deal with this
|
|
|
|
hash = (output->getSize()<<8) | (uintm)code();
|
|
for(int4 i=0;i<inrefs.size();++i) {
|
|
const Varnode *vn = getIn(i);
|
|
hash = (hash<<8) | (hash>>(sizeof(uintm)*8-8));
|
|
if (vn->isConstant())
|
|
hash ^= (uintm)vn->getOffset();
|
|
else
|
|
hash ^= (uintm)vn->getCreateIndex(); // Hash in pointer itself as unique id
|
|
}
|
|
return hash;
|
|
}
|
|
|
|
/// Do these two ops represent a common subexpression?
|
|
/// This is the full test of matching indicated by getCseHash
|
|
/// \param op is the PcodeOp to compare with this
|
|
/// \return \b true if the two ops are a common subexpression match
|
|
bool PcodeOp::isCseMatch(const PcodeOp *op) const
|
|
|
|
{
|
|
if ((getEvalType()&(PcodeOp::unary|PcodeOp::binary))==0) return false;
|
|
if ((op->getEvalType()&(PcodeOp::unary|PcodeOp::binary))==0) return false;
|
|
if (output->getSize() != op->output->getSize()) return false;
|
|
if (code() != op->code()) return false;
|
|
if (code() == CPUI_COPY) return false; // Let copy propagation deal with this
|
|
if (inrefs.size() != op->inrefs.size()) return false;
|
|
for(int4 i=0;i<inrefs.size();++i) {
|
|
const Varnode *vn1 = getIn(i);
|
|
const Varnode *vn2 = op->getIn(i);
|
|
if (vn1 == vn2) continue;
|
|
if (vn1->isConstant()&&vn2->isConstant()&&(vn1->getOffset()==vn2->getOffset()))
|
|
continue;
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/// Its possible for the order of operations to be rearranged in some instances but still keep
|
|
/// equivalent data-flow. Test if \b this operation can be moved to occur immediately after
|
|
/// a specified \e point operation. This currently only tests for movement within a basic block.
|
|
/// \param point is the specified point to move \b this after
|
|
/// \return \b true if the move is possible
|
|
bool PcodeOp::isMoveable(const PcodeOp *point) const
|
|
|
|
{
|
|
if (this == point) return true; // No movement necessary
|
|
bool movingLoad = false;
|
|
if (getEvalType() == PcodeOp::special) {
|
|
if (code() == CPUI_LOAD)
|
|
movingLoad = true; // Allow LOAD to be moved with additional restrictions
|
|
else
|
|
return false; // Don't move special ops
|
|
}
|
|
if (parent != point->parent) return false; // Not in the same block
|
|
if (output != (Varnode *)0) {
|
|
// Output cannot be moved past an op that reads it
|
|
list<PcodeOp *>::const_iterator iter = output->beginDescend();
|
|
list<PcodeOp *>::const_iterator enditer = output->endDescend();
|
|
while(iter != enditer) {
|
|
PcodeOp *readOp = *iter;
|
|
++iter;
|
|
if (readOp->parent != parent) continue;
|
|
if (readOp->start.getOrder() <= point->start.getOrder())
|
|
return false; // Is in the block and is read before (or at) -point-
|
|
}
|
|
}
|
|
// Only allow this op to be moved across a CALL in very restrictive circumstances
|
|
bool crossCalls = false;
|
|
if (getEvalType() != PcodeOp::special) {
|
|
// Check for a normal op where all inputs and output are not address tied
|
|
if (output != (Varnode *)0 && !output->isAddrTied() && !output->isPersist()) {
|
|
int4 i;
|
|
for(i=0;i<numInput();++i) {
|
|
const Varnode *vn = getIn(i);
|
|
if (vn->isAddrTied() || vn->isPersist())
|
|
break;
|
|
}
|
|
if (i == numInput())
|
|
crossCalls = true;
|
|
}
|
|
}
|
|
vector<const Varnode *> tiedList;
|
|
for(int4 i=0;i<numInput();++i) {
|
|
const Varnode *vn = getIn(i);
|
|
if (vn->isAddrTied())
|
|
tiedList.push_back(vn);
|
|
}
|
|
list<PcodeOp *>::iterator biter = basiciter;
|
|
do {
|
|
++biter;
|
|
PcodeOp *op = *biter;
|
|
if (op->getEvalType() == PcodeOp::special) {
|
|
switch (op->code()) {
|
|
case CPUI_LOAD:
|
|
if (output != (Varnode *)0) {
|
|
if (output->isAddrTied()) return false;
|
|
}
|
|
break;
|
|
case CPUI_STORE:
|
|
if (movingLoad)
|
|
return false;
|
|
else {
|
|
if (!tiedList.empty()) return false;
|
|
if (output != (Varnode *)0) {
|
|
if (output->isAddrTied()) return false;
|
|
}
|
|
}
|
|
break;
|
|
case CPUI_INDIRECT: // Let thru, deal with what's INDIRECTed around separately
|
|
case CPUI_SEGMENTOP:
|
|
case CPUI_CPOOLREF:
|
|
break;
|
|
case CPUI_CALL:
|
|
case CPUI_CALLIND:
|
|
case CPUI_NEW:
|
|
if (!crossCalls) return false;
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
if (op->output != (Varnode *)0) {
|
|
if (movingLoad) {
|
|
if (op->output->isAddrTied()) return false;
|
|
}
|
|
for(int4 i=0;i<tiedList.size();++i) {
|
|
const Varnode *vn = tiedList[i];
|
|
if (vn->overlap(*op->output)>=0)
|
|
return false;
|
|
if (op->output->overlap(*vn)>=0)
|
|
return false;
|
|
}
|
|
}
|
|
} while(biter != point->basiciter);
|
|
return true;
|
|
}
|
|
|
|
/// Set the behavioral class (opcode) of this operation. For most applications this should only be called
|
|
/// by the PcodeOpBank. This is fairly low-level but does cache various boolean flags associated with the opcode
|
|
/// \param t_op is the behavioural class to set
|
|
void PcodeOp::setOpcode(TypeOp *t_op)
|
|
|
|
{
|
|
flags &= ~(PcodeOp::branch | PcodeOp::call | PcodeOp::coderef | PcodeOp::commutative |
|
|
PcodeOp::returns | PcodeOp::nocollapse | PcodeOp::marker | PcodeOp::booloutput |
|
|
PcodeOp::unary | PcodeOp::binary | PcodeOp::ternary | PcodeOp::special |
|
|
PcodeOp::has_callspec | PcodeOp::return_copy);
|
|
opcode = t_op;
|
|
flags |= t_op->getFlags();
|
|
}
|
|
|
|
/// Make sure there are exactly \e num input slots for this op.
|
|
/// All slots, regardless of the total being increased or decreased, are set to \e null.
|
|
/// \param num is the number of inputs to set
|
|
void PcodeOp::setNumInputs(int4 num)
|
|
|
|
{
|
|
inrefs.resize(num);
|
|
for(int4 i=0;i<num;++i)
|
|
inrefs[i] = (Varnode *)0;
|
|
}
|
|
|
|
/// Remove the input Varnode in a specific slot. The slot is eliminated and all Varnodes beyond this
|
|
/// slot are renumbered. All the other Varnodes are otherwise undisturbed.
|
|
/// \param slot is the index of the Varnode to remove
|
|
void PcodeOp::removeInput(int4 slot)
|
|
|
|
{
|
|
for(int4 i=slot+1;i<inrefs.size();++i)
|
|
inrefs[i-1] = inrefs[i];
|
|
inrefs.pop_back();
|
|
}
|
|
|
|
/// Insert space for a new Varnode before \e slot. The new space is filled with \e null.
|
|
/// \param slot is index of the slot where the new space is inserted
|
|
void PcodeOp::insertInput(int4 slot)
|
|
|
|
{
|
|
inrefs.push_back((Varnode *)0);
|
|
for(int4 i=inrefs.size()-1;i>slot;--i)
|
|
inrefs[i] = inrefs[i-1];
|
|
inrefs[slot] = (Varnode *)0;
|
|
}
|
|
|
|
// Find the next op in sequence from this op. This is usually in the same basic block, but this
|
|
// routine will follow flow into successive blocks during its search, so long as there is only one path
|
|
// \return the next PcodeOp or \e null
|
|
PcodeOp *PcodeOp::nextOp(void) const
|
|
|
|
{
|
|
list<PcodeOp *>::iterator iter;
|
|
BlockBasic *p;
|
|
|
|
p = parent; // Current parent
|
|
iter = basiciter; // Current iterator
|
|
|
|
iter ++;
|
|
while(iter == p->endOp()) {
|
|
if ((p->sizeOut() != 1)&&(p->sizeOut()!=2)) return (PcodeOp *)0;
|
|
p = (BlockBasic *) p->getOut(0);
|
|
iter = p->beginOp();
|
|
}
|
|
return *iter;
|
|
}
|
|
|
|
/// Find the previous op that flowed uniquely into this op, if it exists. This routine will not search
|
|
/// farther than the basic block containing this.
|
|
/// \return the previous PcodeOp or \e null
|
|
PcodeOp *PcodeOp::previousOp(void) const
|
|
|
|
{
|
|
list<PcodeOp *>::iterator iter;
|
|
|
|
if (basiciter == parent->beginOp()) return (PcodeOp *) 0;
|
|
iter = basiciter;
|
|
iter--;
|
|
return *iter;
|
|
}
|
|
|
|
/// Scan backward within the basic block containing this op and find the first op marked as the
|
|
/// start of an instruction. This also works if basic blocks haven't been calculated yet, and all
|
|
/// the ops are still in the dead list. The starting op may be from a different instruction if
|
|
/// this op was from an instruction in a delay slot
|
|
/// \return the starting PcodeOp
|
|
PcodeOp *PcodeOp::target(void) const
|
|
|
|
{
|
|
PcodeOp *retop;
|
|
list<PcodeOp *>::iterator iter;
|
|
iter = isDead() ? insertiter : basiciter;
|
|
retop = *iter;
|
|
while((retop->flags&PcodeOp::startmark)==0) {
|
|
--iter;
|
|
retop = *iter;
|
|
}
|
|
return retop;
|
|
}
|
|
|
|
/// Print an address and a raw representation of this op to the stream, suitable for console debugging apps
|
|
/// \param s is the stream to print to
|
|
void PcodeOp::printDebug(ostream &s) const
|
|
|
|
{
|
|
s << start << ": ";
|
|
if (isDead()||(parent==(BlockBasic *)0))
|
|
s << "**";
|
|
else
|
|
printRaw(s);
|
|
}
|
|
|
|
/// Encode a description including: the opcode name, the sequence number, and separate elements
|
|
/// providing a reference number for each input and output Varnode
|
|
/// \param encoder is the stream encoder
|
|
void PcodeOp::encode(Encoder &encoder) const
|
|
|
|
{
|
|
encoder.openElement(ELEM_OP);
|
|
encoder.writeSignedInteger(ATTRIB_CODE, (int4)code());
|
|
start.encode(encoder);
|
|
if (output==(Varnode *)0) {
|
|
encoder.openElement(ELEM_VOID);
|
|
encoder.closeElement(ELEM_VOID);
|
|
}
|
|
else {
|
|
encoder.openElement(ELEM_ADDR);
|
|
encoder.writeUnsignedInteger(ATTRIB_REF, output->getCreateIndex());
|
|
encoder.closeElement(ELEM_ADDR);
|
|
}
|
|
for(int4 i=0;i<inrefs.size();++i) {
|
|
const Varnode *vn = getIn(i);
|
|
if (vn == (const Varnode *)0) {
|
|
encoder.openElement(ELEM_VOID);
|
|
encoder.closeElement(ELEM_VOID);
|
|
}
|
|
else if (vn->getSpace()->getType()==IPTR_IOP) {
|
|
if ((i==1)&&(code()==CPUI_INDIRECT)) {
|
|
PcodeOp *indop = PcodeOp::getOpFromConst(vn->getAddr());
|
|
encoder.openElement(ELEM_IOP);
|
|
encoder.writeUnsignedInteger(ATTRIB_VALUE, indop->getSeqNum().getTime());
|
|
encoder.closeElement(ELEM_IOP);
|
|
}
|
|
else {
|
|
encoder.openElement(ELEM_VOID);
|
|
encoder.closeElement(ELEM_VOID);
|
|
}
|
|
}
|
|
else if (vn->getSpace()->getType()==IPTR_CONSTANT) {
|
|
if ((i==0)&&((code()==CPUI_STORE)||(code()==CPUI_LOAD))) {
|
|
AddrSpace *spc = vn->getSpaceFromConst();
|
|
encoder.openElement(ELEM_SPACEID);
|
|
encoder.writeSpace(ATTRIB_NAME, spc);
|
|
encoder.closeElement(ELEM_SPACEID);
|
|
}
|
|
else {
|
|
encoder.openElement(ELEM_ADDR);
|
|
encoder.writeUnsignedInteger(ATTRIB_REF, vn->getCreateIndex());
|
|
encoder.closeElement(ELEM_ADDR);
|
|
}
|
|
}
|
|
else {
|
|
encoder.openElement(ELEM_ADDR);
|
|
encoder.writeUnsignedInteger(ATTRIB_REF, vn->getCreateIndex());
|
|
encoder.closeElement(ELEM_ADDR);
|
|
}
|
|
}
|
|
encoder.closeElement(ELEM_OP);
|
|
}
|
|
|
|
/// Assuming all the inputs to this op are constants, compute the constant result of evaluating
|
|
/// this op on this inputs. If one if the inputs has attached symbol information,
|
|
/// pass-back "the fact of" as we may want to propagate the info to the new constant.
|
|
/// Throw an exception if a constant result cannot be produced.
|
|
/// \param markedInput will pass-back whether or not one of the inputs is a marked constant
|
|
/// \return the constant result
|
|
uintb PcodeOp::collapse(bool &markedInput) const {
|
|
const Varnode *vn0;
|
|
const Varnode *vn1;
|
|
|
|
vn0 = getIn(0);
|
|
if (vn0->getSymbolEntry() != (SymbolEntry *)0) {
|
|
markedInput = true;
|
|
}
|
|
switch(getEvalType()) {
|
|
case PcodeOp::unary:
|
|
return opcode->evaluateUnary(output->getSize(),vn0->getSize(),vn0->getOffset());
|
|
case PcodeOp::binary:
|
|
vn1 = getIn(1);
|
|
if (vn1->getSymbolEntry() != (SymbolEntry *)0) {
|
|
markedInput = true;
|
|
}
|
|
return opcode->evaluateBinary(output->getSize(),vn0->getSize(),
|
|
vn0->getOffset(),vn1->getOffset());
|
|
default:
|
|
break;
|
|
}
|
|
throw LowlevelError("Invalid constant collapse");
|
|
}
|
|
|
|
/// The p-code op must be \e special, or an exception is thrown. The operation is performed
|
|
/// and if there is no evaluation error, the result is returned and \b evalError is set to \b false.
|
|
/// \param in is an array of input values
|
|
/// \return the result of applying \b this operation to the input values
|
|
uintb PcodeOp::executeSimple(uintb *in,bool &evalError) const
|
|
|
|
{
|
|
uint4 evalType = getEvalType();
|
|
uintb res;
|
|
try {
|
|
if (evalType == PcodeOp::unary)
|
|
res = opcode->evaluateUnary(output->getSize(),inrefs[0]->getSize(),in[0]);
|
|
else if (evalType == PcodeOp::binary)
|
|
res = opcode->evaluateBinary(output->getSize(),inrefs[0]->getSize(),in[0],in[1]);
|
|
else if (evalType == PcodeOp::ternary)
|
|
res = opcode->evaluateTernary(output->getSize(),inrefs[0]->getSize(),in[0],in[1],in[2]);
|
|
else
|
|
throw LowlevelError("Cannot perform simple execution of "+(string)get_opname(code()));
|
|
} catch(EvaluationError &err) {
|
|
evalError = true;
|
|
return 0;
|
|
}
|
|
evalError = false;
|
|
return res;
|
|
}
|
|
|
|
/// Knowing that \b this PcodeOp has collapsed its constant inputs, one of which has
|
|
/// symbol content, figure out if the symbol should propagate to the new given output constant.
|
|
/// \param newConst is the given output constant
|
|
void PcodeOp::collapseConstantSymbol(Varnode *newConst) const
|
|
|
|
{
|
|
const Varnode *copyVn = (const Varnode *)0;
|
|
switch(code()) {
|
|
case CPUI_SUBPIECE:
|
|
if (getIn(1)->getOffset() != 0)
|
|
return; // Must be truncating high bytes
|
|
copyVn = getIn(0);
|
|
break;
|
|
case CPUI_COPY:
|
|
case CPUI_INT_ZEXT:
|
|
case CPUI_INT_NEGATE:
|
|
case CPUI_INT_2COMP:
|
|
copyVn = getIn(0);
|
|
break;
|
|
case CPUI_INT_LEFT:
|
|
case CPUI_INT_RIGHT:
|
|
case CPUI_INT_SRIGHT:
|
|
copyVn = getIn(0); // Marked varnode must be first input
|
|
break;
|
|
case CPUI_INT_ADD:
|
|
case CPUI_INT_MULT:
|
|
case CPUI_INT_AND:
|
|
case CPUI_INT_OR:
|
|
case CPUI_INT_XOR:
|
|
copyVn = getIn(0);
|
|
if (copyVn->getSymbolEntry() == (SymbolEntry *)0) {
|
|
copyVn = getIn(1);
|
|
}
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
if (copyVn->getSymbolEntry() == (SymbolEntry *)0)
|
|
return; // The first input must be marked
|
|
newConst->copySymbolIfValid(copyVn);
|
|
}
|
|
|
|
/// Compute nonzeromask assuming inputs to op have their masks properly defined. Assume the op has an output.
|
|
/// For any inputs to this op, that have zero bits where their nzmasks have zero bits, then the output
|
|
/// produced by this op is guaranteed to have zero bits at every location in the nzmask calculated by this function.
|
|
/// \param cliploop indicates the calculation shouldn't include inputs from known looping edges
|
|
/// \return the calculated non-zero mask
|
|
uintb PcodeOp::getNZMaskLocal(bool cliploop) const
|
|
|
|
{
|
|
int4 sa,sz1,sz2,size;
|
|
uintb resmask,val;
|
|
|
|
size = output->getSize();
|
|
uintb fullmask = calc_mask( size );
|
|
|
|
switch(opcode->getOpcode()) {
|
|
case CPUI_INT_EQUAL:
|
|
case CPUI_INT_NOTEQUAL:
|
|
case CPUI_INT_SLESS:
|
|
case CPUI_INT_SLESSEQUAL:
|
|
case CPUI_INT_LESS:
|
|
case CPUI_INT_LESSEQUAL:
|
|
case CPUI_INT_CARRY:
|
|
case CPUI_INT_SCARRY:
|
|
case CPUI_INT_SBORROW:
|
|
case CPUI_BOOL_NEGATE:
|
|
case CPUI_BOOL_XOR:
|
|
case CPUI_BOOL_AND:
|
|
case CPUI_BOOL_OR:
|
|
case CPUI_FLOAT_EQUAL:
|
|
case CPUI_FLOAT_NOTEQUAL:
|
|
case CPUI_FLOAT_LESS:
|
|
case CPUI_FLOAT_LESSEQUAL:
|
|
case CPUI_FLOAT_NAN:
|
|
resmask=1; // Only 1 bit not guaranteed to be 0
|
|
break;
|
|
case CPUI_COPY:
|
|
case CPUI_INT_ZEXT:
|
|
resmask = getIn(0)->getNZMask();
|
|
break;
|
|
case CPUI_INT_SEXT:
|
|
resmask = sign_extend( getIn(0)->getNZMask(), getIn(0)->getSize(), size);
|
|
break;
|
|
case CPUI_INT_XOR:
|
|
case CPUI_INT_OR:
|
|
resmask = getIn(0)->getNZMask();
|
|
if (resmask != fullmask)
|
|
resmask |= getIn(1)->getNZMask();
|
|
break;
|
|
case CPUI_INT_AND:
|
|
resmask = getIn(0)->getNZMask();
|
|
if (resmask != 0)
|
|
resmask &= getIn(1)->getNZMask();
|
|
break;
|
|
case CPUI_INT_LEFT:
|
|
if (!getIn(1)->isConstant())
|
|
resmask = fullmask;
|
|
else {
|
|
sa = getIn(1)->getOffset(); // Get shift amount
|
|
resmask = getIn(0)->getNZMask();
|
|
resmask = pcode_left(resmask,sa) & fullmask;
|
|
}
|
|
break;
|
|
case CPUI_INT_RIGHT:
|
|
if (!getIn(1)->isConstant())
|
|
resmask = fullmask;
|
|
else {
|
|
sz1 = getIn(0)->getSize();
|
|
sa = getIn(1)->getOffset(); // Get shift amount
|
|
resmask = getIn(0)->getNZMask();
|
|
resmask = pcode_right(resmask,sa);
|
|
if (sz1 > sizeof(uintb)) {
|
|
// resmask did not hold most sig bits of mask
|
|
if (sa >= 8*sz1)
|
|
resmask = 0;
|
|
else if (sa >= 8*sizeof(uintb)) {
|
|
// Full mask shifted over 8*sizeof(uintb)
|
|
resmask = calc_mask( sz1-sizeof(uintb) );
|
|
// Shift over remaining portion of sa
|
|
resmask >>= (sa-8*sizeof(uintb));
|
|
}
|
|
else {
|
|
// Fill in one bits from part of mask not originally
|
|
// calculated
|
|
uintb tmp = 0;
|
|
tmp -= 1;
|
|
tmp <<= (8*sizeof(uintb)-sa);
|
|
resmask |= tmp;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case CPUI_INT_SRIGHT:
|
|
if ((!getIn(1)->isConstant())||(size > sizeof(uintb)))
|
|
resmask = fullmask;
|
|
else {
|
|
sa = getIn(1)->getOffset(); // Get shift amount
|
|
resmask = getIn(0)->getNZMask();
|
|
if ((resmask & (fullmask ^ (fullmask>>1))) == 0) { // If we know sign bit is zero
|
|
resmask = pcode_right(resmask,sa); // Same as CPUI_INT_RIGHT
|
|
}
|
|
else {
|
|
resmask = pcode_right(resmask,sa);
|
|
resmask |= (fullmask >> sa) ^ fullmask; // Don't know what the new high bits are
|
|
}
|
|
}
|
|
break;
|
|
case CPUI_INT_DIV:
|
|
val = getIn(0)->getNZMask();
|
|
resmask = coveringmask(val);
|
|
if (getIn(1)->isConstant()) {
|
|
// Dividing by power of 2 is equiv to right shift
|
|
// if the denom is bigger than a power of 2, then
|
|
// the result still has at least that many highsig zerobits
|
|
sa = mostsigbit_set(getIn(1)->getNZMask());
|
|
if (sa != -1)
|
|
resmask >>= sa; // Add sa additional zerobits
|
|
}
|
|
break;
|
|
case CPUI_INT_REM:
|
|
val = (getIn(1)->getNZMask()-1); // Result is less than modulus
|
|
resmask = coveringmask(val);
|
|
break;
|
|
case CPUI_POPCOUNT:
|
|
sz1 = popcount(getIn(0)->getNZMask());
|
|
resmask = coveringmask((uintb)sz1);
|
|
resmask &= fullmask;
|
|
break;
|
|
case CPUI_LZCOUNT:
|
|
resmask = coveringmask(getIn(0)->getSize() * 8);
|
|
resmask &= fullmask;
|
|
break;
|
|
case CPUI_SUBPIECE:
|
|
resmask = getIn(0)->getNZMask();
|
|
sz1 = (int4)getIn(1)->getOffset();
|
|
if ((int4)getIn(0)->getSize() <= sizeof(uintb)) {
|
|
if (sz1 < sizeof(uintb))
|
|
resmask >>= 8*sz1;
|
|
else
|
|
resmask = 0;
|
|
}
|
|
else { // Extended precision
|
|
if (sz1 < sizeof(uintb)) {
|
|
resmask >>= 8*sz1;
|
|
if (sz1 > 0)
|
|
resmask |= fullmask << (8*(sizeof(uintb)-sz1));
|
|
}
|
|
else
|
|
resmask = fullmask;
|
|
}
|
|
resmask &= fullmask;
|
|
break;
|
|
case CPUI_PIECE:
|
|
sa = getIn(1)->getSize();
|
|
resmask = getIn(0)->getNZMask();
|
|
resmask = (sa < sizeof(uintb)) ? resmask << 8*sa : 0;
|
|
resmask |= getIn(1)->getNZMask();
|
|
break;
|
|
case CPUI_INT_MULT:
|
|
val = getIn(0)->getNZMask();
|
|
resmask = getIn(1)->getNZMask();
|
|
if (size > sizeof(uintb)) {
|
|
resmask = fullmask;
|
|
}
|
|
else {
|
|
sz1 = mostsigbit_set(val);
|
|
sz2 = mostsigbit_set(resmask);
|
|
if (sz1 == -1 || sz2 == -1) {
|
|
resmask = 0;
|
|
}
|
|
else {
|
|
int4 l1 = leastsigbit_set(val);
|
|
int4 l2 = leastsigbit_set(resmask);
|
|
sa = l1 + l2;
|
|
if (sa >= 8*size) {
|
|
resmask = 0;
|
|
}
|
|
else {
|
|
sz1 = sz1 - l1 + 1;
|
|
sz2 = sz2 - l2 + 1;
|
|
int4 total = sz1 + sz2;
|
|
if (sz1 == 1 || sz2 == 1)
|
|
total -= 1;
|
|
resmask = fullmask;
|
|
if (total < 8 * size)
|
|
resmask >>= (8*size - total);
|
|
resmask = (resmask << sa) & fullmask;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case CPUI_INT_ADD:
|
|
resmask = getIn(0)->getNZMask();
|
|
if (resmask!=fullmask) {
|
|
resmask |= getIn(1)->getNZMask();
|
|
resmask |= (resmask<<1); // Account for possible carries
|
|
resmask &= fullmask;
|
|
}
|
|
break;
|
|
case CPUI_MULTIEQUAL:
|
|
if (inrefs.size()==0)
|
|
resmask = fullmask;
|
|
else {
|
|
int4 i=0;
|
|
resmask = 0;
|
|
if (cliploop) {
|
|
for(;i<inrefs.size();++i) {
|
|
if (parent->isLoopIn(i)) continue;
|
|
resmask |= getIn(i)->getNZMask();
|
|
}
|
|
}
|
|
else {
|
|
for(;i<inrefs.size();++i)
|
|
resmask |= getIn(i)->getNZMask();
|
|
}
|
|
}
|
|
break;
|
|
case CPUI_CALL:
|
|
case CPUI_CALLIND:
|
|
case CPUI_CPOOLREF:
|
|
if (isCalculatedBool())
|
|
resmask = 1; // In certain cases we know the output is strictly boolean
|
|
else
|
|
resmask = fullmask;
|
|
break;
|
|
default:
|
|
resmask = fullmask;
|
|
break;
|
|
}
|
|
return resmask;
|
|
}
|
|
|
|
/// Compare the execution order of -this- and -bop-, if -this- executes earlier (dominates) return -1;
|
|
/// if -bop- executes earlier return 1, otherwise return 0. Note that 0 is returned if there is no absolute
|
|
/// execution order.
|
|
/// \param bop is the PcodeOp to compare this to
|
|
/// \return -1, 0, or 1, depending on the comparison
|
|
int4 PcodeOp::compareOrder(const PcodeOp *bop) const
|
|
|
|
{
|
|
if (parent == bop->parent)
|
|
return (start.getOrder() < bop->start.getOrder()) ? -1 : 1;
|
|
|
|
FlowBlock *common = FlowBlock::findCommonBlock(parent,bop->parent);
|
|
if (common == parent)
|
|
return -1;
|
|
if (common == bop->parent)
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
/// \brief Determine if a Varnode is a leaf within the CONCAT tree rooted at the given Varnode
|
|
///
|
|
/// The CONCAT tree is the maximal set of Varnodes that are all inputs to CPUI_PIECE operations,
|
|
/// with no other uses, and that all ultimately flow to the root Varnode. This method tests
|
|
/// whether a Varnode is a leaf of this tree.
|
|
/// \param rootVn is the given root of the CONCAT tree
|
|
/// \param vn is the Varnode to test as a leaf
|
|
/// \param relOffset is byte offset of the test Varnode within fully concatenated value (rooted at \b rootVn)
|
|
/// \return \b true is the test Varnode is a leaf of the tree
|
|
bool PieceNode::isLeaf(Varnode *rootVn,Varnode *vn,int4 relOffset)
|
|
|
|
{
|
|
if (vn->isMapped() && rootVn->getSymbolEntry() != vn->getSymbolEntry()) {
|
|
return true;
|
|
}
|
|
if (!vn->isWritten()) return true;
|
|
PcodeOp *def = vn->getDef();
|
|
if (def->code() != CPUI_PIECE) return true;
|
|
PcodeOp *op = vn->loneDescend();
|
|
if (op == (PcodeOp *)0) return true;
|
|
if (vn->isAddrTied()) {
|
|
Address addr = rootVn->getAddr() + relOffset;
|
|
if (vn->getAddr() != addr) return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/// Find the root of the CONCAT tree of Varnodes marked either isProtoPartial() or isAddrTied().
|
|
/// This will be the maximal Varnode that containing the given Varnode (as storage), with a
|
|
/// backward path to it through PIECE operations. All Varnodes along the path, except the root, will be
|
|
/// marked as isProtoPartial() or isAddrTied().
|
|
/// \return the root of the CONCAT tree
|
|
Varnode *PieceNode::findRoot(Varnode *vn)
|
|
|
|
{
|
|
while(vn->isProtoPartial() || vn->isAddrTied()) {
|
|
list<PcodeOp *>::const_iterator iter = vn->beginDescend();
|
|
PcodeOp *pieceOp = (PcodeOp *)0;
|
|
while(iter != vn->endDescend()) {
|
|
PcodeOp *op = *iter;
|
|
++iter;
|
|
if (op->code() != CPUI_PIECE) continue;
|
|
int4 slot = op->getSlot(vn);
|
|
Address addr = op->getOut()->getAddr();
|
|
if (addr.getSpace()->isBigEndian() == (slot == 1))
|
|
addr = addr + op->getIn(1-slot)->getSize();
|
|
addr.renormalize(vn->getSize()); // Allow for possible join address
|
|
if (addr == vn->getAddr()) {
|
|
if (pieceOp != (PcodeOp *)0) { // If there is more than one valid PIECE
|
|
if (op->compareOrder(pieceOp)) // Attach this to earliest one
|
|
pieceOp = op;
|
|
}
|
|
else
|
|
pieceOp = op;
|
|
}
|
|
}
|
|
if (pieceOp == (PcodeOp *)0)
|
|
break;
|
|
vn = pieceOp->getOut();
|
|
}
|
|
return vn;
|
|
}
|
|
|
|
/// \brief Build the CONCAT tree rooted at the given Varnode
|
|
///
|
|
/// Recursively walk backwards from the root through CPUI_PIECE operations, stopping if a Varnode
|
|
/// is deemed a leaf. Collect all Varnodes involved in the tree in a list. For each Varnode in the tree,
|
|
/// record whether it is leaf and also calculate its offset within the data-type attached to the root.
|
|
/// \param stack holds the markup for each node of the tree
|
|
/// \param rootVn is the given root of the tree
|
|
/// \param op is the current PIECE op to explore as part of the tree
|
|
/// \param baseOffset is the offset associated with the output of the current PIECE op wihtin the data-type
|
|
/// \param rootOffset is the offset of the \b rootVn within the data-type
|
|
void PieceNode::gatherPieces(vector<PieceNode> &stack,Varnode *rootVn,PcodeOp *op,int4 baseOffset,int4 rootOffset)
|
|
|
|
{
|
|
for(int4 i=0;i<2;++i) {
|
|
Varnode *vn = op->getIn(i);
|
|
int4 offset = (rootVn->getSpace()->isBigEndian() == (i==1)) ? baseOffset + op->getIn(1-i)->getSize() : baseOffset;
|
|
bool res = isLeaf(rootVn,vn,offset-rootOffset);
|
|
stack.emplace_back(op,i,offset,res);
|
|
if (!res)
|
|
gatherPieces(stack,rootVn,vn->getDef(),offset,rootOffset);
|
|
}
|
|
}
|
|
|
|
/// Add the PcodeOp to the list of ops with the same op-code. Currently only certain
|
|
/// op-codes have a dedicated list.
|
|
/// \param op is the given PcodeOp
|
|
void PcodeOpBank::addToCodeList(PcodeOp *op)
|
|
|
|
{
|
|
switch(op->code()) {
|
|
case CPUI_STORE:
|
|
op->codeiter = storelist.insert(storelist.end(),op);
|
|
break;
|
|
case CPUI_LOAD:
|
|
op->codeiter = loadlist.insert(loadlist.end(), op);
|
|
break;
|
|
case CPUI_RETURN:
|
|
op->codeiter = returnlist.insert(returnlist.end(),op);
|
|
break;
|
|
case CPUI_CALLOTHER:
|
|
op->codeiter = useroplist.insert(useroplist.end(),op);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/// Remove the PcodeOp from its list of ops with the same op-code. Currently only certain
|
|
/// op-codes have a dedicated list.
|
|
/// \param op is the given PcodeOp
|
|
void PcodeOpBank::removeFromCodeList(PcodeOp *op)
|
|
|
|
{
|
|
switch(op->code()) {
|
|
case CPUI_STORE:
|
|
storelist.erase(op->codeiter);
|
|
break;
|
|
case CPUI_LOAD:
|
|
loadlist.erase(op->codeiter);
|
|
break;
|
|
case CPUI_RETURN:
|
|
returnlist.erase(op->codeiter);
|
|
break;
|
|
case CPUI_CALLOTHER:
|
|
useroplist.erase(op->codeiter);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void PcodeOpBank::clearCodeLists(void)
|
|
|
|
{
|
|
storelist.clear();
|
|
loadlist.clear();
|
|
returnlist.clear();
|
|
useroplist.clear();
|
|
}
|
|
|
|
/// A new PcodeOp is allocated with the indicated number of input slots, which
|
|
/// start out empty. A sequence number is assigned, and the op is added to the
|
|
/// end of the \e dead list.
|
|
/// \param inputs is the number of input slots
|
|
/// \param pc is the Address to associate with the PcodeOp
|
|
/// \return the newly allocated PcodeOp
|
|
PcodeOp *PcodeOpBank::create(int4 inputs,const Address &pc)
|
|
|
|
{
|
|
PcodeOp *op = new PcodeOp(inputs,SeqNum(pc,uniqid++));
|
|
optree[op->getSeqNum()] = op;
|
|
op->setFlag(PcodeOp::dead); // Start out life as dead
|
|
op->insertiter = deadlist.insert(deadlist.end(),op);
|
|
return op;
|
|
}
|
|
|
|
/// A new PcodeOp is allocated with the indicated number of input slots and the
|
|
/// specific sequence number, suitable for cloning and restoring from XML.
|
|
/// The op is added to the end of the \e dead list.
|
|
/// \param inputs is the number of input slots
|
|
/// \param sq is the specified sequence number
|
|
/// \return the newly allocated PcodeOp
|
|
PcodeOp *PcodeOpBank::create(int4 inputs,const SeqNum &sq)
|
|
|
|
{
|
|
PcodeOp *op;
|
|
op = new PcodeOp(inputs,sq);
|
|
if (sq.getTime() >= uniqid)
|
|
uniqid = sq.getTime() + 1;
|
|
|
|
optree[op->getSeqNum()] = op;
|
|
op->setFlag(PcodeOp::dead); // Start out life as dead
|
|
op->insertiter = deadlist.insert(deadlist.end(),op);
|
|
return op;
|
|
}
|
|
|
|
void PcodeOpBank::destroyDead(void)
|
|
|
|
{
|
|
list<PcodeOp *>::iterator iter;
|
|
PcodeOp *op;
|
|
|
|
iter = deadlist.begin();
|
|
while(iter!=deadlist.end()) {
|
|
op = *iter++;
|
|
destroy(op);
|
|
}
|
|
}
|
|
|
|
/// The given PcodeOp is removed from all internal lists and added to a final
|
|
/// \e deadandgone list. The memory is not reclaimed until the whole container is
|
|
/// destroyed, in case pointer references still exist. These will all still
|
|
/// be marked as \e dead.
|
|
/// \param op is the given PcodeOp to destroy
|
|
void PcodeOpBank::destroy(PcodeOp *op)
|
|
|
|
{
|
|
if (!op->isDead())
|
|
throw LowlevelError("Deleting integrated op");
|
|
|
|
optree.erase(op->getSeqNum());
|
|
deadlist.erase(op->insertiter);
|
|
removeFromCodeList(op);
|
|
deadandgone.push_back(op);
|
|
}
|
|
|
|
/// The PcodeOp is assigned the new op-code, which may involve moving it
|
|
/// between the internal op-code specific lists.
|
|
/// \param op is the given PcodeOp to change
|
|
/// \param newopc is the new op-code object
|
|
void PcodeOpBank::changeOpcode(PcodeOp *op,TypeOp *newopc)
|
|
|
|
{
|
|
if (op->opcode != (TypeOp *)0)
|
|
removeFromCodeList(op);
|
|
op->setOpcode( newopc );
|
|
addToCodeList(op);
|
|
}
|
|
|
|
/// The PcodeOp is moved out of the \e dead list into the \e alive list. The
|
|
/// PcodeOp::isDead() method will now return \b false.
|
|
/// \param op is the given PcodeOp to mark
|
|
void PcodeOpBank::markAlive(PcodeOp *op)
|
|
|
|
{
|
|
deadlist.erase(op->insertiter);
|
|
op->clearFlag(PcodeOp::dead);
|
|
op->insertiter = alivelist.insert(alivelist.end(),op);
|
|
}
|
|
|
|
/// The PcodeOp is moved out of the \e alive list into the \e dead list. The
|
|
/// PcodeOp::isDead() method will now return \b true.
|
|
/// \param op is the given PcodeOp to mark
|
|
void PcodeOpBank::markDead(PcodeOp *op)
|
|
|
|
{
|
|
alivelist.erase(op->insertiter);
|
|
op->setFlag(PcodeOp::dead);
|
|
op->insertiter = deadlist.insert(deadlist.end(),op);
|
|
}
|
|
|
|
/// The op is moved to right after a specified op in the \e dead list.
|
|
/// \param op is the given PcodeOp to move
|
|
/// \param prev is the specified op in the \e dead list
|
|
void PcodeOpBank::insertAfterDead(PcodeOp *op,PcodeOp *prev)
|
|
|
|
{
|
|
if ((!op->isDead())||(!prev->isDead()))
|
|
throw LowlevelError("Dead move called on ops which aren't dead");
|
|
deadlist.erase(op->insertiter);
|
|
list<PcodeOp *>::iterator iter = prev->insertiter;
|
|
++iter;
|
|
op->insertiter = deadlist.insert(iter,op);
|
|
}
|
|
|
|
/// \brief Move a sequence of PcodeOps to a point in the \e dead list.
|
|
///
|
|
/// The point is right after a provided op. All ops must be in the \e dead list.
|
|
/// \param firstop is the first PcodeOp in the sequence to be moved
|
|
/// \param lastop is the last PcodeOp in the sequence to be moved
|
|
/// \param prev is the provided point to move to
|
|
void PcodeOpBank::moveSequenceDead(PcodeOp *firstop,PcodeOp *lastop,PcodeOp *prev)
|
|
|
|
{
|
|
list<PcodeOp *>::iterator enditer = lastop->insertiter;
|
|
++enditer;
|
|
list<PcodeOp *>::iterator previter = prev->insertiter;
|
|
++previter;
|
|
if (previter != firstop->insertiter) // Check for degenerate move
|
|
deadlist.splice(previter,deadlist,firstop->insertiter,enditer);
|
|
}
|
|
|
|
/// Incidental COPYs are not considered active use of parameter passing Varnodes by
|
|
/// parameter analysis algorithms.
|
|
/// \param firstop is the start of the range of incidental COPY ops
|
|
/// \param lastop is the end of the range of incidental COPY ops
|
|
void PcodeOpBank::markIncidentalCopy(PcodeOp *firstop,PcodeOp *lastop)
|
|
|
|
{
|
|
list<PcodeOp *>::iterator iter = firstop->insertiter;
|
|
list<PcodeOp *>::iterator enditer = lastop->insertiter;
|
|
++enditer;
|
|
while(iter != enditer) {
|
|
PcodeOp *op = *iter;
|
|
++iter;
|
|
if (op->code() == CPUI_COPY)
|
|
op->setAdditionalFlag(PcodeOp::incidental_copy);
|
|
}
|
|
}
|
|
|
|
/// Find the first PcodeOp at or after the given Address assuming they have not
|
|
/// yet been broken up into basic blocks. Take into account delay slots.
|
|
/// \param addr is the given Address
|
|
/// \return the targeted PcodeOp (or NULL)
|
|
PcodeOp *PcodeOpBank::target(const Address &addr) const
|
|
|
|
{
|
|
PcodeOpTree::const_iterator iter = optree.lower_bound(SeqNum(addr,0));
|
|
if (iter == optree.end()) return (PcodeOp *)0;
|
|
return (*iter).second->target();
|
|
}
|
|
|
|
/// \param num is the given sequence number
|
|
/// \return the matching PcodeOp (or NULL)
|
|
PcodeOp *PcodeOpBank::findOp(const SeqNum &num) const
|
|
|
|
{
|
|
PcodeOpTree::const_iterator iter = optree.find(num);
|
|
if (iter == optree.end()) return (PcodeOp *)0;
|
|
return (*iter).second;
|
|
}
|
|
|
|
/// The term \e fallthru in this context refers to p-code \e not assembly instructions.
|
|
/// \param op is the given PcodeOp
|
|
/// \return the fallthru PcodeOp
|
|
PcodeOp *PcodeOpBank::fallthru(const PcodeOp *op) const
|
|
|
|
{
|
|
PcodeOp *retop;
|
|
if (op->isDead()) {
|
|
// In this case we know an instruction is contiguous
|
|
// in the dead list
|
|
list<PcodeOp *>::const_iterator iter = op->insertiter;
|
|
++iter;
|
|
if (iter != deadlist.end()) {
|
|
retop = *iter;
|
|
if (!retop->isInstructionStart()) // If the next in dead list is not marked
|
|
return retop; // It is in the same instruction, and is the fallthru
|
|
}
|
|
--iter;
|
|
SeqNum max = op->getSeqNum();
|
|
while(!(*iter)->isInstructionStart()) // Find start of instruction
|
|
--iter;
|
|
// Find biggest sequence number in this instruction
|
|
// This is probably -op- itself because it is the
|
|
// last op in the instruction, but it might not be
|
|
// because of delay slot reordering
|
|
while((iter!=deadlist.end())&&(*iter != op)) {
|
|
if (max < (*iter)->getSeqNum())
|
|
max = (*iter)->getSeqNum();
|
|
++iter;
|
|
}
|
|
PcodeOpTree::const_iterator nextiter = optree.upper_bound(max);
|
|
if (nextiter == optree.end()) return (PcodeOp *)0;
|
|
retop = (*nextiter).second;
|
|
return retop;
|
|
}
|
|
else
|
|
return op->nextOp();
|
|
}
|
|
|
|
PcodeOpTree::const_iterator PcodeOpBank::begin(const Address &addr) const
|
|
|
|
{
|
|
return optree.lower_bound(SeqNum(addr,0));
|
|
}
|
|
|
|
PcodeOpTree::const_iterator PcodeOpBank::end(const Address &addr) const
|
|
|
|
{
|
|
return optree.upper_bound(SeqNum(addr,~((uintm)0)));
|
|
}
|
|
|
|
list<PcodeOp *>::const_iterator PcodeOpBank::begin(OpCode opc) const
|
|
|
|
{
|
|
switch(opc) {
|
|
case CPUI_STORE:
|
|
return storelist.begin();
|
|
case CPUI_LOAD:
|
|
return loadlist.begin();
|
|
case CPUI_RETURN:
|
|
return returnlist.begin();
|
|
case CPUI_CALLOTHER:
|
|
return useroplist.begin();
|
|
default:
|
|
break;
|
|
}
|
|
return alivelist.end();
|
|
}
|
|
|
|
list<PcodeOp *>::const_iterator PcodeOpBank::end(OpCode opc) const
|
|
|
|
{
|
|
switch(opc) {
|
|
case CPUI_STORE:
|
|
return storelist.end();
|
|
case CPUI_LOAD:
|
|
return loadlist.end();
|
|
case CPUI_RETURN:
|
|
return returnlist.end();
|
|
case CPUI_CALLOTHER:
|
|
return useroplist.end();
|
|
default:
|
|
break;
|
|
}
|
|
return alivelist.end();
|
|
}
|
|
|
|
void PcodeOpBank::clear(void)
|
|
|
|
{
|
|
list<PcodeOp *>::iterator iter;
|
|
|
|
for(iter=alivelist.begin();iter!=alivelist.end();++iter)
|
|
delete *iter;
|
|
for(iter=deadlist.begin();iter!=deadlist.end();++iter)
|
|
delete *iter;
|
|
for(iter=deadandgone.begin();iter!=deadandgone.end();++iter)
|
|
delete *iter;
|
|
optree.clear();
|
|
alivelist.clear();
|
|
deadlist.clear();
|
|
clearCodeLists();
|
|
deadandgone.clear();
|
|
uniqid = 0;
|
|
}
|
|
|
|
} // End namespace ghidra
|