ghidra/Ghidra/Features/Decompiler/src/decompile/cpp/op.cc

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