mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-05 10:49:34 +02:00
784 lines
25 KiB
C++
784 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"
|
|
|
|
namespace ghidra {
|
|
|
|
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;
|
|
}
|
|
|
|
} // End namespace ghidra
|