ghidra/Ghidra/Features/Decompiler/src/decompile/cpp/pcodecompile.cc
2022-03-04 14:56:46 -05:00

780 lines
25 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 "pcodecompile.hh"
string Location::format(void) const
{
ostringstream s;
s << filename << ":" << dec << lineno;
return s.str();
}
ExprTree::ExprTree(VarnodeTpl *vn)
{
outvn = vn;
ops = new vector<OpTpl *>;
}
ExprTree::ExprTree(OpTpl *op)
{
ops = new vector<OpTpl *>;
ops->push_back(op);
if (op->getOut() != (VarnodeTpl *)0)
outvn = new VarnodeTpl(*op->getOut());
else
outvn = (VarnodeTpl *)0;
}
ExprTree::~ExprTree(void)
{
if (outvn != (VarnodeTpl *)0)
delete outvn;
if (ops != (vector<OpTpl *> *)0) {
for(int4 i=0;i<ops->size();++i)
delete (*ops)[i];
delete ops;
}
}
vector<OpTpl *> *ExprTree::appendParams(OpTpl *op,vector<ExprTree *> *param)
{ // Create op expression with entire list of expression
// inputs
vector<OpTpl *> *res = new vector<OpTpl *>;
for(int4 i=0;i<param->size();++i) {
res->insert(res->end(),(*param)[i]->ops->begin(),(*param)[i]->ops->end());
(*param)[i]->ops->clear();
op->addInput((*param)[i]->outvn);
(*param)[i]->outvn = (VarnodeTpl *)0;
delete (*param)[i];
}
res->push_back(op);
delete param;
return res;
}
vector<OpTpl *> *ExprTree::toVector(ExprTree *expr)
{ // Grab the op vector and delete the output expression
vector<OpTpl *> *res = expr->ops;
expr->ops = (vector<OpTpl *> *)0;
delete expr;
return res;
}
void ExprTree::setOutput(VarnodeTpl *newout)
{ // Force the output of the expression to be new out
// If the original output is named, this requires
// an extra COPY op
OpTpl *op;
if (outvn == (VarnodeTpl *)0)
throw SleighError("Expression has no output");
if (outvn->isUnnamed()) {
delete outvn;
op = ops->back();
op->clearOutput();
op->setOutput(newout);
}
else {
op = new OpTpl(CPUI_COPY);
op->addInput(outvn);
op->setOutput(newout);
ops->push_back(op);
}
outvn = new VarnodeTpl(*newout);
}
void PcodeCompile::force_size(VarnodeTpl *vt,const ConstTpl &size,const vector<OpTpl *> &ops)
{
if ((vt->getSize().getType()!=ConstTpl::real)||(vt->getSize().getReal() != 0))
return; // Size already exists
vt->setSize(size);
if (!vt->isLocalTemp()) return;
// If the variable is a local temporary
// The size may need to be propagated to the various
// uses of the variable
OpTpl *op;
VarnodeTpl *vn;
for(int4 i=0;i<ops.size();++i) {
op = ops[i];
vn = op->getOut();
if ((vn!=(VarnodeTpl *)0)&&(vn->isLocalTemp())) {
if (vn->getOffset() == vt->getOffset()) {
if ((size.getType() == ConstTpl::real)&&(vn->getSize().getType() == ConstTpl::real)&&
(vn->getSize().getReal() != 0) && (vn->getSize().getReal() != size.getReal()))
throw SleighError("Localtemp size mismatch");
vn->setSize(size);
}
}
for(int4 j=0;j<op->numInput();++j) {
vn = op->getIn(j);
if (vn->isLocalTemp()&&(vn->getOffset()==vt->getOffset())) {
if ((size.getType() == ConstTpl::real)&&(vn->getSize().getType() == ConstTpl::real)&&
(vn->getSize().getReal() != 0) && (vn->getSize().getReal() != size.getReal()))
throw SleighError("Localtemp size mismatch");
vn->setSize(size);
}
}
}
}
void PcodeCompile::matchSize(int4 j,OpTpl *op,bool inputonly,const vector<OpTpl *> &ops)
{ // Find something to fill in zero size varnode
// j is the slot we are trying to fill (-1=output)
// Don't check output for non-zero if inputonly is true
VarnodeTpl *match = (VarnodeTpl *)0;
VarnodeTpl *vt;
int4 i,inputsize;
vt = (j==-1) ? op->getOut() : op->getIn(j);
if (!inputonly) {
if (op->getOut() != (VarnodeTpl *)0)
if (!op->getOut()->isZeroSize())
match = op->getOut();
}
inputsize = op->numInput();
for(i=0;i<inputsize;++i) {
if (match != (VarnodeTpl *)0) break;
if (op->getIn(i)->isZeroSize()) continue;
match = op->getIn(i);
}
if (match != (VarnodeTpl *)0)
force_size(vt,match->getSize(),ops);
}
void PcodeCompile::fillinZero(OpTpl *op,const vector<OpTpl *> &ops)
{ // Try to get rid of zero size varnodes in op
// Right now this is written assuming operands for the constructor are
// are built before any other pcode in the constructor is generated
int4 inputsize,i;
switch(op->getOpcode()) {
case CPUI_COPY: // Instructions where all inputs and output are same size
case CPUI_INT_ADD:
case CPUI_INT_SUB:
case CPUI_INT_2COMP:
case CPUI_INT_NEGATE:
case CPUI_INT_XOR:
case CPUI_INT_AND:
case CPUI_INT_OR:
case CPUI_INT_MULT:
case CPUI_INT_DIV:
case CPUI_INT_SDIV:
case CPUI_INT_REM:
case CPUI_INT_SREM:
case CPUI_FLOAT_ADD:
case CPUI_FLOAT_DIV:
case CPUI_FLOAT_MULT:
case CPUI_FLOAT_SUB:
case CPUI_FLOAT_NEG:
case CPUI_FLOAT_ABS:
case CPUI_FLOAT_SQRT:
case CPUI_FLOAT_CEIL:
case CPUI_FLOAT_FLOOR:
case CPUI_FLOAT_ROUND:
if ((op->getOut()!=(VarnodeTpl *)0)&&(op->getOut()->isZeroSize()))
matchSize(-1,op,false,ops);
inputsize = op->numInput();
for(i=0;i<inputsize;++i)
if (op->getIn(i)->isZeroSize())
matchSize(i,op,false,ops);
break;
case CPUI_INT_EQUAL: // Instructions with bool output
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_FLOAT_EQUAL:
case CPUI_FLOAT_NOTEQUAL:
case CPUI_FLOAT_LESS:
case CPUI_FLOAT_LESSEQUAL:
case CPUI_FLOAT_NAN:
case CPUI_BOOL_NEGATE:
case CPUI_BOOL_XOR:
case CPUI_BOOL_AND:
case CPUI_BOOL_OR:
if (op->getOut()->isZeroSize())
force_size(op->getOut(),ConstTpl(ConstTpl::real,1),ops);
inputsize = op->numInput();
for(i=0;i<inputsize;++i)
if (op->getIn(i)->isZeroSize())
matchSize(i,op,true,ops);
break;
// The shift amount does not necessarily have to be the same size
// But if no size is specified, assume it is the same size
case CPUI_INT_LEFT:
case CPUI_INT_RIGHT:
case CPUI_INT_SRIGHT:
if (op->getOut()->isZeroSize()) {
if (!op->getIn(0)->isZeroSize())
force_size(op->getOut(),op->getIn(0)->getSize(),ops);
}
else if (op->getIn(0)->isZeroSize())
force_size(op->getIn(0),op->getOut()->getSize(),ops);
// fallthru to subpiece constant check
case CPUI_SUBPIECE:
if (op->getIn(1)->isZeroSize())
force_size(op->getIn(1),ConstTpl(ConstTpl::real,4),ops);
break;
case CPUI_CPOOLREF:
if (op->getOut()->isZeroSize() && (!op->getIn(0)->isZeroSize()))
force_size(op->getOut(),op->getIn(0)->getSize(),ops);
if (op->getIn(0)->isZeroSize() && (!op->getOut()->isZeroSize()))
force_size(op->getIn(0),op->getOut()->getSize(),ops);
for(i=1;i<op->numInput();++i) {
if (op->getIn(i)->isZeroSize())
force_size(op->getIn(i),ConstTpl(ConstTpl::real,sizeof(uintb)),ops);
}
break;
default:
break;
}
}
bool PcodeCompile::propagateSize(ConstructTpl *ct)
{ // Fill in size for varnodes with size 0
// Return first OpTpl with a size 0 varnode
// that cannot be filled in or NULL otherwise
vector<OpTpl *> zerovec,zerovec2;
vector<OpTpl *>::const_iterator iter;
int4 lastsize;
for(iter=ct->getOpvec().begin();iter!=ct->getOpvec().end();++iter)
if ((*iter)->isZeroSize()) {
fillinZero(*iter,ct->getOpvec());
if ((*iter)->isZeroSize())
zerovec.push_back(*iter);
}
lastsize = zerovec.size() + 1;
while( zerovec.size() < lastsize ) {
lastsize = zerovec.size();
zerovec2.clear();
for(iter=zerovec.begin();iter!=zerovec.end();++iter) {
fillinZero(*iter,ct->getOpvec());
if ((*iter)->isZeroSize())
zerovec2.push_back( *iter );
}
zerovec = zerovec2;
}
if ( lastsize != 0 ) return false;
return true;
}
VarnodeTpl *PcodeCompile::buildTemporary(void)
{ // Build temporary variable (with zerosize)
VarnodeTpl *res = new VarnodeTpl(ConstTpl(uniqspace),
ConstTpl(ConstTpl::real,allocateTemp()),
ConstTpl(ConstTpl::real,0));
res->setUnnamed(true);
return res;
}
LabelSymbol *PcodeCompile::defineLabel(string *name)
{ // Create a label symbol
LabelSymbol *labsym = new LabelSymbol(*name,local_labelcount++);
delete name;
addSymbol(labsym); // Add symbol to local scope
return labsym;
}
vector<OpTpl *> *PcodeCompile::placeLabel(LabelSymbol *labsym)
{ // Create placeholder OpTpl for a label
if (labsym->isPlaced()) {
reportError(getLocation(labsym), "Label '" + labsym->getName() + "' is placed more than once");
}
labsym->setPlaced();
vector<OpTpl *> *res = new vector<OpTpl *>;
OpTpl *op = new OpTpl(LABELBUILD);
VarnodeTpl *idvn = new VarnodeTpl(ConstTpl(constantspace),
ConstTpl(ConstTpl::real,labsym->getIndex()),
ConstTpl(ConstTpl::real,4));
op->addInput(idvn);
res->push_back(op);
return res;
}
vector<OpTpl *> *PcodeCompile::newOutput(bool usesLocalKey,ExprTree *rhs,string *varname,uint4 size)
{
VarnodeSymbol *sym;
VarnodeTpl *tmpvn = buildTemporary();
if (size != 0)
tmpvn->setSize(ConstTpl(ConstTpl::real,size)); // Size was explicitly specified
else if ((rhs->getSize().getType()==ConstTpl::real)&&(rhs->getSize().getReal()!=0))
tmpvn->setSize(rhs->getSize()); // Inherit size from unnamed expression result
// Only inherit if the size is real, otherwise we
// cannot build the VarnodeSymbol with a placeholder constant
rhs->setOutput(tmpvn);
sym = new VarnodeSymbol(*varname,tmpvn->getSpace().getSpace(),tmpvn->getOffset().getReal(),tmpvn->getSize().getReal()); // Create new symbol regardless
addSymbol(sym);
if ((!usesLocalKey) && enforceLocalKey)
reportError(getLocation(sym), "Must use 'local' keyword to define symbol '"+*varname + "'");
delete varname;
return ExprTree::toVector(rhs);
}
void PcodeCompile::newLocalDefinition(string *varname,uint4 size)
{ // Create a new temporary symbol (without generating any pcode)
VarnodeSymbol *sym;
sym = new VarnodeSymbol(*varname,uniqspace,allocateTemp(),size);
addSymbol(sym);
delete varname;
}
ExprTree *PcodeCompile::createOp(OpCode opc,ExprTree *vn)
{ // Create new expression with output -outvn-
// built by performing -opc- on input vn.
// Free input expression
VarnodeTpl *outvn = buildTemporary();
OpTpl *op = new OpTpl(opc);
op->addInput(vn->outvn);
op->setOutput(outvn);
vn->ops->push_back(op);
vn->outvn = new VarnodeTpl(*outvn);
return vn;
}
ExprTree *PcodeCompile::createOp(OpCode opc,ExprTree *vn1,
ExprTree *vn2)
{ // Create new expression with output -outvn-
// built by performing -opc- on inputs vn1 and vn2.
// Free input expressions
VarnodeTpl *outvn = buildTemporary();
vn1->ops->insert(vn1->ops->end(),vn2->ops->begin(),vn2->ops->end());
vn2->ops->clear();
OpTpl *op = new OpTpl(opc);
op->addInput(vn1->outvn);
op->addInput(vn2->outvn);
vn2->outvn = (VarnodeTpl *)0;
op->setOutput(outvn);
vn1->ops->push_back(op);
vn1->outvn = new VarnodeTpl(*outvn);
delete vn2;
return vn1;
}
ExprTree *PcodeCompile::createOpOut(VarnodeTpl *outvn,OpCode opc,
ExprTree *vn1,ExprTree *vn2)
{ // Create an op with explicit output and two inputs
vn1->ops->insert(vn1->ops->end(),vn2->ops->begin(),vn2->ops->end());
vn2->ops->clear();
OpTpl *op = new OpTpl(opc);
op->addInput(vn1->outvn);
op->addInput(vn2->outvn);
vn2->outvn = (VarnodeTpl *)0;
op->setOutput(outvn);
vn1->ops->push_back(op);
vn1->outvn = new VarnodeTpl(*outvn);
delete vn2;
return vn1;
}
ExprTree *PcodeCompile::createOpOutUnary(VarnodeTpl *outvn,OpCode opc,ExprTree *vn)
{ // Create an op with explicit output and 1 input
OpTpl *op = new OpTpl(opc);
op->addInput(vn->outvn);
op->setOutput(outvn);
vn->ops->push_back(op);
vn->outvn = new VarnodeTpl(*outvn);
return vn;
}
vector<OpTpl *> *PcodeCompile::createOpNoOut(OpCode opc,ExprTree *vn)
{ // Create new expression by creating op with given -opc-
// and single input vn. Free the input expression
OpTpl *op = new OpTpl(opc);
op->addInput(vn->outvn);
vn->outvn = (VarnodeTpl *)0; // There is no longer an output to this expression
vector<OpTpl *> *res = vn->ops;
vn->ops = (vector<OpTpl *> *)0;
delete vn;
res->push_back(op);
return res;
}
vector<OpTpl *> *PcodeCompile::createOpNoOut(OpCode opc,ExprTree *vn1,ExprTree *vn2)
{ // Create new expression by creating op with given -opc-
// and inputs vn1 and vn2. Free the input expressions
vector<OpTpl *> *res = vn1->ops;
vn1->ops = (vector<OpTpl *> *)0;
res->insert(res->end(),vn2->ops->begin(),vn2->ops->end());
vn2->ops->clear();
OpTpl *op = new OpTpl(opc);
op->addInput(vn1->outvn);
vn1->outvn = (VarnodeTpl *)0;
op->addInput(vn2->outvn);
vn2->outvn = (VarnodeTpl *)0;
res->push_back(op);
delete vn1;
delete vn2;
return res;
}
vector<OpTpl *> *PcodeCompile::createOpConst(OpCode opc,uintb val)
{
VarnodeTpl *vn = new VarnodeTpl(ConstTpl(constantspace),
ConstTpl(ConstTpl::real,val),
ConstTpl(ConstTpl::real,4));
vector<OpTpl *> *res = new vector<OpTpl *>;
OpTpl *op = new OpTpl(opc);
op->addInput(vn);
res->push_back(op);
return res;
}
ExprTree *PcodeCompile::createLoad(StarQuality *qual,ExprTree *ptr)
{ // Create new load expression, free ptr expression
VarnodeTpl *outvn = buildTemporary();
OpTpl *op = new OpTpl(CPUI_LOAD);
// The first varnode input to the load is a constant reference to the AddrSpace being loaded
// from. Internally, we really store the pointer to the AddrSpace as the reference, but this
// isn't platform independent. So officially, we assume that the constant reference will be the
// AddrSpace index. We can safely assume this always has size 4.
VarnodeTpl *spcvn = new VarnodeTpl(ConstTpl(constantspace),
qual->id,
ConstTpl(ConstTpl::real,8));
op->addInput(spcvn);
op->addInput(ptr->outvn);
op->setOutput(outvn);
ptr->ops->push_back(op);
if (qual->size > 0)
force_size(outvn,ConstTpl(ConstTpl::real,qual->size),*ptr->ops);
ptr->outvn = new VarnodeTpl(*outvn);
delete qual;
return ptr;
}
vector<OpTpl *> *PcodeCompile::createStore(StarQuality *qual,
ExprTree *ptr,ExprTree *val)
{
vector<OpTpl *> *res = ptr->ops;
ptr->ops = (vector<OpTpl *> *)0;
res->insert(res->end(),val->ops->begin(),val->ops->end());
val->ops->clear();
OpTpl *op = new OpTpl(CPUI_STORE);
// The first varnode input to the store is a constant reference to the AddrSpace being loaded
// from. Internally, we really store the pointer to the AddrSpace as the reference, but this
// isn't platform independent. So officially, we assume that the constant reference will be the
// AddrSpace index. We can safely assume this always has size 4.
VarnodeTpl *spcvn = new VarnodeTpl(ConstTpl(constantspace),
qual->id,
ConstTpl(ConstTpl::real,8));
op->addInput(spcvn);
op->addInput(ptr->outvn);
op->addInput(val->outvn);
res->push_back(op);
force_size(val->outvn,ConstTpl(ConstTpl::real,qual->size),*res);
ptr->outvn = (VarnodeTpl *)0;
val->outvn = (VarnodeTpl *)0;
delete ptr;
delete val;
delete qual;
return res;
}
ExprTree *PcodeCompile::createUserOp(UserOpSymbol *sym,vector<ExprTree *> *param)
{ // Create userdefined pcode op, given symbol and parameters
VarnodeTpl *outvn = buildTemporary();
ExprTree *res = new ExprTree();
res->ops = createUserOpNoOut(sym,param);
res->ops->back()->setOutput(outvn);
res->outvn = new VarnodeTpl(*outvn);
return res;
}
vector<OpTpl *> *PcodeCompile::createUserOpNoOut(UserOpSymbol *sym,vector<ExprTree *> *param)
{
OpTpl *op = new OpTpl(CPUI_CALLOTHER);
VarnodeTpl *vn = new VarnodeTpl(ConstTpl(constantspace),
ConstTpl(ConstTpl::real,sym->getIndex()),
ConstTpl(ConstTpl::real,4));
op->addInput(vn);
return ExprTree::appendParams(op,param);
}
ExprTree *PcodeCompile::createVariadic(OpCode opc,vector<ExprTree *> *param)
{
VarnodeTpl *outvn = buildTemporary();
ExprTree *res = new ExprTree();
OpTpl *op = new OpTpl(opc);
res->ops = ExprTree::appendParams(op,param);
res->ops->back()->setOutput(outvn);
res->outvn = new VarnodeTpl(*outvn);
return res;
}
void PcodeCompile::appendOp(OpCode opc,ExprTree *res,uintb constval,int4 constsz)
{ // Take output of res expression, combine with constant,
// using opc operation, return the resulting expression
OpTpl *op = new OpTpl(opc);
VarnodeTpl *constvn = new VarnodeTpl(ConstTpl(constantspace),
ConstTpl(ConstTpl::real,constval),
ConstTpl(ConstTpl::real,constsz));
VarnodeTpl *outvn = buildTemporary();
op->addInput(res->outvn);
op->addInput(constvn);
op->setOutput(outvn);
res->ops->push_back(op);
res->outvn = new VarnodeTpl(*outvn);
}
VarnodeTpl *PcodeCompile::buildTruncatedVarnode(VarnodeTpl *basevn,uint4 bitoffset,uint4 numbits)
{ // Build a truncated form -basevn- that matches the bitrange [ -bitoffset-, -numbits- ] if possible
// using just ConstTpl mechanics, otherwise return null
uint4 byteoffset = bitoffset / 8; // Convert to byte units
uint4 numbytes = numbits / 8;
uintb fullsz = 0;
if (basevn->getSize().getType() == ConstTpl::real) {
// If we know the size of base, make sure the bit range is in bounds
fullsz = basevn->getSize().getReal();
if (fullsz == 0) return (VarnodeTpl *)0;
if (byteoffset + numbytes > fullsz)
throw SleighError("Requested bit range out of bounds");
}
if ((bitoffset % 8) != 0) return (VarnodeTpl *)0;
if ((numbits % 8) != 0) return (VarnodeTpl *)0;
if (basevn->getSpace().isUniqueSpace()) // Do we really want to prevent truncated uniques??
return (VarnodeTpl *)0;
ConstTpl::const_type offset_type = basevn->getOffset().getType();
if ((offset_type != ConstTpl::real)&&(offset_type != ConstTpl::handle))
return (VarnodeTpl *)0;
ConstTpl specialoff;
if (offset_type == ConstTpl::handle) {
// We put in the correct adjustment to offset assuming things are little endian
// We defer the correct big endian calculation until after the consistency check
// because we need to know the subtable export sizes
specialoff = ConstTpl(ConstTpl::handle,basevn->getOffset().getHandleIndex(),
ConstTpl::v_offset_plus,byteoffset);
}
else {
if (basevn->getSize().getType() != ConstTpl::real)
throw SleighError("Could not construct requested bit range");
uintb plus;
if (defaultspace->isBigEndian())
plus = fullsz - (byteoffset + numbytes);
else
plus = byteoffset;
specialoff = ConstTpl(ConstTpl::real,basevn->getOffset().getReal() + plus);
}
VarnodeTpl *res = new VarnodeTpl(basevn->getSpace(),specialoff,ConstTpl(ConstTpl::real,numbytes));
return res;
}
vector<OpTpl *> *PcodeCompile::assignBitRange(VarnodeTpl *vn,uint4 bitoffset,uint4 numbits,ExprTree *rhs)
{ // Create an expression assigning the rhs to a bitrange within sym
string errmsg;
if (numbits == 0)
errmsg = "Size of bitrange is zero";
uint4 smallsize = (numbits+7)/8; // Size of input (output of rhs)
bool shiftneeded = (bitoffset != 0);
bool zextneeded = true;
uintb mask = (uintb)2;
mask = ~(((mask<<(numbits-1))-1) << bitoffset);
if (vn->getSize().getType()==ConstTpl::real) {
// If we know the size of the bitranged varnode, we can
// do some immediate checks, and possibly simplify things
uint4 symsize = vn->getSize().getReal();
if (symsize > 0)
zextneeded = (symsize > smallsize);
symsize *= 8; // Convert to number of bits
if ((bitoffset>=symsize)||(bitoffset+numbits>symsize))
errmsg = "Assigned bitrange is bad";
else if ((bitoffset==0)&&(numbits==symsize))
errmsg = "Assigning to bitrange is superfluous";
}
if (errmsg.size()>0) { // Was there an error condition
reportError((const Location *)0, errmsg); // Report the error
delete vn; // Clean up
vector<OpTpl *> *resops = rhs->ops; // Passthru old expression
rhs->ops = (vector<OpTpl *> *)0;
delete rhs;
return resops;
}
// We know what the size of the input has to be
force_size(rhs->outvn,ConstTpl(ConstTpl::real,smallsize),*rhs->ops);
ExprTree *res;
VarnodeTpl *finalout = buildTruncatedVarnode(vn,bitoffset,numbits);
if (finalout != (VarnodeTpl *)0) {
delete vn; // Don't keep the original Varnode object
res = createOpOutUnary(finalout,CPUI_COPY,rhs);
}
else {
if (bitoffset + numbits > 64)
errmsg = "Assigned bitrange extends past first 64 bits";
res = new ExprTree(vn);
appendOp(CPUI_INT_AND,res,mask,0);
if (zextneeded)
createOp(CPUI_INT_ZEXT,rhs);
if (shiftneeded)
appendOp(CPUI_INT_LEFT,rhs,bitoffset,4);
finalout = new VarnodeTpl(*vn);
res = createOpOut(finalout,CPUI_INT_OR,res,rhs);
}
if (errmsg.size() > 0)
reportError((const Location *)0, errmsg);
vector<OpTpl *> *resops = res->ops;
res->ops = (vector<OpTpl *> *)0;
delete res;
return resops;
}
ExprTree *PcodeCompile::createBitRange(SpecificSymbol *sym,uint4 bitoffset,uint4 numbits)
{ // Create an expression computing the indicated bitrange of sym
// The result is truncated to the smallest byte size that can
// contain the indicated number of bits. The result has the
// desired bits shifted all the way to the right
string errmsg;
if (numbits == 0)
errmsg = "Size of bitrange is zero";
VarnodeTpl *vn = sym->getVarnode();
uint4 finalsize = (numbits+7)/8; // Round up to neareast byte size
uint4 truncshift = 0;
bool maskneeded = ((numbits%8)!=0);
bool truncneeded = true;
// Special case where we can set the size, without invoking
// a truncation operator
if ((errmsg.size()==0)&&(bitoffset==0)&&(!maskneeded)) {
if ((vn->getSpace().getType()==ConstTpl::handle)&&vn->isZeroSize()) {
vn->setSize(ConstTpl(ConstTpl::real,finalsize));
ExprTree *res = new ExprTree(vn);
// VarnodeTpl *cruft = buildTemporary();
// delete cruft;
return res;
}
}
if (errmsg.size()==0) {
VarnodeTpl *truncvn = buildTruncatedVarnode(vn,bitoffset,numbits);
if (truncvn != (VarnodeTpl *)0) { // If we are able to construct a simple truncated varnode
ExprTree *res = new ExprTree(truncvn); // Return just the varnode as an expression
delete vn;
return res;
}
}
if (vn->getSize().getType()==ConstTpl::real) {
// If we know the size of the input varnode, we can
// do some immediate checks, and possibly simplify things
uint4 insize = vn->getSize().getReal();
if (insize > 0) {
truncneeded = (finalsize < insize);
insize *= 8; // Convert to number of bits
if ((bitoffset >= insize)||(bitoffset+numbits > insize))
errmsg = "Bitrange is bad";
if (maskneeded && ((bitoffset+numbits)==insize))
maskneeded = false;
}
}
uintb mask = (uintb)2;
mask = ((mask<<(numbits-1))-1);
if (truncneeded && ((bitoffset % 8)==0)) {
truncshift = bitoffset/8;
bitoffset = 0;
}
if ((bitoffset==0)&&(!truncneeded)&&(!maskneeded))
errmsg = "Superfluous bitrange";
if (maskneeded && (finalsize > 8))
errmsg = "Illegal masked bitrange producing varnode larger than 64 bits: " + sym->getName();
ExprTree *res = new ExprTree(vn);
if (errmsg.size()>0) { // Check for error condition
reportError(getLocation(sym), errmsg);
return res;
}
if (bitoffset !=0)
appendOp(CPUI_INT_RIGHT,res,bitoffset,4);
if (truncneeded)
appendOp(CPUI_SUBPIECE,res,truncshift,4);
if (maskneeded)
appendOp(CPUI_INT_AND,res,mask,finalsize);
force_size(res->outvn,ConstTpl(ConstTpl::real,finalsize),*res->ops);
return res;
}
VarnodeTpl *PcodeCompile::addressOf(VarnodeTpl *var,uint4 size)
{ // Produce constant varnode that is the offset
// portion of varnode -var-
if (size==0) { // If no size specified
if (var->getSpace().getType() == ConstTpl::spaceid) {
AddrSpace *spc = var->getSpace().getSpace(); // Look to the particular space
size = spc->getAddrSize(); // to see if it has a standard address size
}
}
VarnodeTpl *res;
if ((var->getOffset().getType() == ConstTpl::real)&&(var->getSpace().getType() == ConstTpl::spaceid)) {
AddrSpace *spc = var->getSpace().getSpace();
uintb off = AddrSpace::byteToAddress(var->getOffset().getReal(),spc->getWordSize());
res = new VarnodeTpl(ConstTpl(constantspace),
ConstTpl(ConstTpl::real,off),
ConstTpl(ConstTpl::real,size));
}
else
res = new VarnodeTpl(ConstTpl(constantspace),var->getOffset(),ConstTpl(ConstTpl::real,size));
delete var;
return res;
}