#include "funcdata.hh"
Funcdata::Funcdata(const string &nm,Scope *scope,const Address &addr,FunctionSymbol *sym,int4 sz)
: baseaddr(addr),
funcp(),
vbank(scope->getArch()),
heritage(this),
covermerge(*this)
{ functionSymbol = sym;
flags = 0;
clean_up_index = 0;
high_level_index = 0;
cast_phase_index = 0;
glb = scope->getArch();
minLanedSize = glb->getMinimumLanedRegisterSize();
name = nm;
size = sz;
AddrSpace *stackid = glb->getStackSpace();
if (nm.size()==0)
localmap = (ScopeLocal *)0; else {
uint8 id;
if (sym != (FunctionSymbol *)0)
id = sym->getId();
else {
id = 0x57AB12CD;
id = (id << 32) | (addr.getOffset() & 0xffffffff);
}
ScopeLocal *newMap = new ScopeLocal(id,stackid,this,glb);
glb->symboltab->attachScope(newMap,scope); localmap = newMap;
funcp.setScope(localmap,baseaddr+ -1);
localmap->resetLocalWindow();
}
activeoutput = (ParamActive *)0;
#ifdef OPACTION_DEBUG
jtcallback = (void (*)(Funcdata &orig,Funcdata &fd))0;
opactdbg_count = 0;
opactdbg_breakcount = -1;
opactdbg_on = false;
opactdbg_breakon = false;
opactdbg_active = false;
#endif
}
void Funcdata::clear(void)
{
flags &= ~(highlevel_on|blocks_generated|processing_started|typerecovery_on|restart_pending);
clean_up_index = 0;
high_level_index = 0;
cast_phase_index = 0;
minLanedSize = glb->getMinimumLanedRegisterSize();
localmap->clearUnlocked(); localmap->resetLocalWindow();
clearActiveOutput();
funcp.clearUnlockedOutput(); clearBlocks();
obank.clear();
vbank.clear();
clearCallSpecs();
clearJumpTables();
heritage.clear();
#ifdef OPACTION_DEBUG
opactdbg_count = 0;
#endif
}
void Funcdata::warning(const string &txt,const Address &ad) const
{
string msg;
if ((flags & jumptablerecovery_on)!=0)
msg = "WARNING (jumptable): ";
else
msg = "WARNING: ";
msg += txt;
glb->commentdb->addCommentNoDuplicate(Comment::warning,baseaddr,ad,msg);
}
void Funcdata::warningHeader(const string &txt) const
{
string msg;
if ((flags & jumptablerecovery_on)!=0)
msg = "WARNING (jumptable): ";
else
msg = "WARNING: ";
msg += txt;
glb->commentdb->addCommentNoDuplicate(Comment::warningheader,baseaddr,baseaddr,msg);
}
void Funcdata::startProcessing(void)
{
if ((flags & processing_started)!=0)
throw LowlevelError("Function processing already started");
flags |= processing_started;
if (funcp.isInline())
warningHeader("This is an inlined function");
localmap->clearUnlocked();
funcp.clearUnlockedOutput();
Address baddr(baseaddr.getSpace(),0);
Address eaddr(baseaddr.getSpace(),~((uintb)0));
followFlow(baddr,eaddr);
structureReset();
sortCallSpecs(); heritage.buildInfoList();
localoverride.applyDeadCodeDelay(*this);
}
void Funcdata::stopProcessing(void)
{
flags |= processing_complete;
obank.destroyDead(); #ifdef CPUI_STATISTICS
glb->stats->process(*this);
#endif
}
bool Funcdata::startTypeRecovery(void)
{
if ((flags & typerecovery_on)!=0) return false; flags |= typerecovery_on;
return true;
}
Funcdata::~Funcdata(void)
{
if (localmap != (ScopeLocal *)0)
glb->symboltab->deleteScope(localmap);
clearCallSpecs();
for(int4 i=0;i<jumpvec.size();++i) delete jumpvec[i];
glb = (Architecture *)0;
}
void Funcdata::printRaw(ostream &s) const
{
if (bblocks.getSize()==0) {
if (obank.empty())
throw RecovError("No operations to print");
PcodeOpTree::const_iterator iter;
s << "Raw operations: \n";
for(iter=obank.beginAll();iter!=obank.endAll();++iter) {
s << (*iter).second->getSeqNum() << ":\t";
(*iter).second->printRaw(s);
s << endl;
}
}
else
bblocks.printRaw(s);
}
void Funcdata::spacebase(void)
{
VarnodeLocSet::const_iterator iter,enditer;
int4 i,j,numspace;
Varnode *vn;
AddrSpace *spc;
for(j=0;j<glb->numSpaces();++j) {
spc = glb->getSpace(j);
if (spc == (AddrSpace *)0) continue;
numspace = spc->numSpacebase();
for(i=0;i<numspace;++i) {
const VarnodeData &point(spc->getSpacebase(i));
Datatype *ct = glb->types->getTypeSpacebase(spc,getAddress());
Datatype *ptr = glb->types->getTypePointer(point.size,ct,spc->getWordSize());
iter = vbank.beginLoc(point.size,Address(point.space,point.offset));
enditer = vbank.endLoc(point.size,Address(point.space,point.offset));
while(iter != enditer) {
vn = *iter++;
if (vn->isFree()) continue;
if (vn->isSpacebase()) { PcodeOp *op = vn->getDef();
if ((op != (PcodeOp *)0)&&(op->code() == CPUI_INT_ADD))
splitUses(vn);
}
else {
vn->setFlags(Varnode::spacebase); if (vn->isInput()) vn->updateType(ptr,true,true);
}
}
}
}
}
Varnode *Funcdata::newSpacebasePtr(AddrSpace *id)
{
Varnode *vn;
const VarnodeData &point(id->getSpacebase(0));
vn = newVarnode(point.size, Address(point.space,point.offset));
return vn;
}
Varnode *Funcdata::findSpacebaseInput(AddrSpace *id) const
{
Varnode *vn;
const VarnodeData &point(id->getSpacebase(0));
vn = vbank.findInput(point.size, Address(point.space,point.offset));
return vn;
}
void Funcdata::spacebaseConstant(PcodeOp *op,int4 slot,SymbolEntry *entry,const Address &rampoint,uintb origval,int4 origsize)
{
int4 sz = rampoint.getAddrSize();
AddrSpace *spaceid = rampoint.getSpace();
Datatype *sb_type = glb->types->getTypeSpacebase(spaceid,Address());
sb_type = glb->types->getTypePointer(sz,sb_type,spaceid->getWordSize());
Varnode *spacebase_vn,*outvn,*newconst;
uintb extra = rampoint.getOffset() - entry->getAddr().getOffset(); extra = AddrSpace::byteToAddress(extra,rampoint.getSpace()->getWordSize());
PcodeOp *addOp = (PcodeOp *)0;
PcodeOp *extraOp = (PcodeOp *)0;
PcodeOp *zextOp = (PcodeOp *)0;
PcodeOp *subOp = (PcodeOp *)0;
bool isCopy = false;
if (op->code() == CPUI_COPY) { isCopy = true;
if (sz < origsize)
zextOp = op;
else {
op->insertInput(1); if (origsize < sz)
subOp = op;
else if (extra != 0)
extraOp = op;
else
addOp = op;
}
}
spacebase_vn = newConstant(sz,0);
spacebase_vn->updateType(sb_type,true,true);
spacebase_vn->setFlags(Varnode::spacebase);
if (addOp == (PcodeOp *)0) {
addOp = newOp(2,op->getAddr());
opSetOpcode(addOp,CPUI_PTRSUB);
newUniqueOut(sz,addOp);
opInsertBefore(addOp,op);
}
else {
opSetOpcode(addOp,CPUI_PTRSUB);
}
outvn = addOp->getOut();
uintb newconstoff = origval - extra; newconst = newConstant(sz,newconstoff);
newconst->setPtrCheck(); if (spaceid->isTruncated())
addOp->setPtrFlow();
opSetInput(addOp,spacebase_vn,0);
opSetInput(addOp,newconst,1);
Symbol *sym = entry->getSymbol();
Datatype *entrytype = sym->getType();
Datatype *ptrentrytype = glb->types->getTypePointerStripArray(sz,entrytype,spaceid->getWordSize());
bool typelock = sym->isTypeLocked();
if (typelock && (entrytype->getMetatype() == TYPE_UNKNOWN))
typelock = false;
outvn->updateType(ptrentrytype,typelock,false);
if (extra != 0) {
if (extraOp == (PcodeOp *)0) {
extraOp = newOp(2,op->getAddr());
opSetOpcode(extraOp,CPUI_INT_ADD);
newUniqueOut(sz,extraOp);
opInsertBefore(extraOp,op);
}
else
opSetOpcode(extraOp,CPUI_INT_ADD);
Varnode *extconst = newConstant(sz,extra);
extconst->setPtrCheck();
opSetInput(extraOp,outvn,0);
opSetInput(extraOp,extconst,1);
outvn = extraOp->getOut();
}
if (sz < origsize) { if (zextOp == (PcodeOp *)0) {
zextOp = newOp(1,op->getAddr());
opSetOpcode(zextOp,CPUI_INT_ZEXT); newUniqueOut(origsize,zextOp);
opInsertBefore(zextOp,op);
}
else
opSetOpcode(zextOp,CPUI_INT_ZEXT);
opSetInput(zextOp,outvn,0);
outvn = zextOp->getOut();
}
else if (origsize < sz) { if (subOp == (PcodeOp *)0) {
subOp = newOp(2,op->getAddr());
opSetOpcode(subOp,CPUI_SUBPIECE);
newUniqueOut(origsize,subOp);
opInsertBefore(subOp,op);
}
else
opSetOpcode(subOp,CPUI_SUBPIECE);
opSetInput(subOp,outvn,0);
opSetInput(subOp,newConstant(4, 0), 1); outvn = subOp->getOut();
}
if (!isCopy)
opSetInput(op,outvn,slot);
}
void Funcdata::clearCallSpecs(void)
{
int4 i;
for(i=0;i<qlst.size();++i)
delete qlst[i];
qlst.clear(); }
FuncCallSpecs *Funcdata::getCallSpecs(const PcodeOp *op) const
{
int4 i;
const Varnode *vn;
vn = op->getIn(0);
if (vn->getSpace()->getType()==IPTR_FSPEC)
return FuncCallSpecs::getFspecFromConst(vn->getAddr());
for(i=0;i<qlst.size();++i)
if (qlst[i]->getOp() == op) return qlst[i];
return (FuncCallSpecs *)0;
}
bool Funcdata::compareCallspecs(const FuncCallSpecs *a,const FuncCallSpecs *b)
{
int4 ind1,ind2;
ind1 = a->getOp()->getParent()->getIndex();
ind2 = b->getOp()->getParent()->getIndex();
if (ind1 != ind2) return (ind1 < ind2);
return (a->getOp()->getSeqNum().getOrder() < b->getOp()->getSeqNum().getOrder());
}
void Funcdata::sortCallSpecs(void)
{
sort(qlst.begin(),qlst.end(),compareCallspecs);
}
void Funcdata::deleteCallSpecs(PcodeOp *op)
{
vector<FuncCallSpecs *>::iterator iter;
for(iter=qlst.begin();iter!=qlst.end();++iter) {
FuncCallSpecs *fc = *iter;
if (fc->getOp() == op) {
delete fc;
qlst.erase(iter);
return;
}
}
}
int4 Funcdata::fillinExtrapop(void)
{
if (hasNoCode()) return funcp.getExtraPop();
if (funcp.getExtraPop() != ProtoModel::extrapop_unknown)
return funcp.getExtraPop();
list<PcodeOp *>::const_iterator iter = beginOp(CPUI_RETURN);
if (iter == endOp(CPUI_RETURN)) return 0;
PcodeOp *retop = *iter;
uint1 buffer[4];
glb->loader->loadFill(buffer,4,retop->getAddr());
int4 extrapop = 4; if (buffer[0] == 0xc2) {
extrapop = buffer[2]; extrapop <<= 8;
extrapop += buffer[1];
extrapop += 4; }
funcp.setExtraPop( extrapop ); return extrapop;
}
void Funcdata::printVarnodeTree(ostream &s) const
{
VarnodeDefSet::const_iterator iter,enditer;
Varnode *vn;
iter = vbank.beginDef();
enditer = vbank.endDef();
while(iter != enditer) {
vn = *iter++;
vn->printInfo(s);
}
}
void Funcdata::printLocalRange(ostream &s) const
{
localmap->printBounds(s);
ScopeMap::const_iterator iter,enditer;
iter = localmap->childrenBegin();
enditer = localmap->childrenEnd();
for(;iter!=enditer;++iter) {
Scope *l1 = (*iter).second;
l1->printBounds(s);
}
}
void Funcdata::restoreXmlJumpTable(const Element *el)
{
const List &list( el->getChildren() );
List::const_iterator iter;
for(iter=list.begin();iter!=list.end();++iter) {
JumpTable *jt = new JumpTable(glb);
jt->restoreXml(*iter);
jumpvec.push_back(jt);
}
}
void Funcdata::saveXmlJumpTable(ostream &s) const
{
if (jumpvec.empty()) return;
vector<JumpTable *>::const_iterator iter;
s << "<jumptablelist>\n";
for(iter=jumpvec.begin();iter!=jumpvec.end();++iter)
(*iter)->saveXml(s);
s << "</jumptablelist>\n";
}
void Funcdata::saveVarnodeXml(ostream &s,VarnodeLocSet::const_iterator iter,VarnodeLocSet::const_iterator enditer)
{
Varnode *vn;
while(iter!=enditer) {
vn = *iter++;
vn->saveXml(s);
s << '\n';
}
}
void Funcdata::saveXmlHigh(ostream &s) const
{
Varnode *vn;
HighVariable *high;
if (!isHighOn()) return;
s << "<highlist>";
VarnodeLocSet::const_iterator iter;
for(iter=beginLoc();iter!=endLoc();++iter) {
vn = *iter;
if (vn->isAnnotation()) continue;
high = vn->getHigh();
if (high->isMark()) continue;
high->setMark();
high->saveXml(s);
}
for(iter=beginLoc();iter!=endLoc();++iter) {
vn = *iter;
if (!vn->isAnnotation())
vn->getHigh()->clearMark();
}
s << "</highlist>\n";
}
void Funcdata::saveXmlTree(ostream &s) const
{
s << "<ast>\n";
s << "<varnodes>\n";
for(int4 i=0;i<glb->numSpaces();++i) {
AddrSpace *base = glb->getSpace(i);
if (base == (AddrSpace *)0 || base->getType()==IPTR_IOP) continue;
VarnodeLocSet::const_iterator iter = vbank.beginLoc(base);
VarnodeLocSet::const_iterator enditer = vbank.endLoc(base);
saveVarnodeXml(s,iter,enditer);
}
s << "</varnodes>\n";
list<PcodeOp *>::iterator oiter,endoiter;
PcodeOp *op;
BlockBasic *bs;
for(int4 i=0;i<bblocks.getSize();++i) {
bs = (BlockBasic *)bblocks.getBlock(i);
s << "<block";
a_v_i(s,"index",bs->getIndex());
s << ">\n";
bs->saveXmlBody(s);
oiter = bs->beginOp();
endoiter = bs->endOp();
while(oiter != endoiter) {
op = *oiter++;
op->saveXml(s);
s << '\n';
}
s << "</block>\n";
}
for(int4 i=0;i<bblocks.getSize();++i) {
bs = (BlockBasic *)bblocks.getBlock(i);
if (bs->sizeIn() == 0) continue;
s << "<blockedge";
a_v_i(s,"index",bs->getIndex());
s << ">\n";
bs->saveXmlEdges(s);
s << "</blockedge>\n";
}
s << "</ast>\n";
}
void Funcdata::saveXml(ostream &s,uint8 id,bool savetree) const
{
s << "<function";
if (id != 0)
a_v_u(s, "id", id);
a_v(s,"name",name);
a_v_i(s,"size",size);
if (hasNoCode())
a_v_b(s,"nocode",true);
s << ">\n";
baseaddr.saveXml(s);
s << '\n';
if (!hasNoCode()) {
localmap->saveXmlRecursive(s,false); }
if (savetree) {
saveXmlTree(s);
saveXmlHigh(s);
}
saveXmlJumpTable(s);
funcp.saveXml(s); localoverride.saveXml(s,glb);
s << "</function>\n";
}
uint8 Funcdata::restoreXml(const Element *el)
{
name.clear();
size = -1;
uint8 id = 0;
AddrSpace *stackid = glb->getStackSpace();
for(int4 i=0;i<el->getNumAttributes();++i) {
const string &attrName(el->getAttributeName(i));
if (attrName == "name")
name = el->getAttributeValue(i);
else if (attrName == "size") {
istringstream s( el->getAttributeValue(i));
s.unsetf(ios::dec | ios::hex | ios::oct);
s >> size;
}
else if (attrName == "id") {
istringstream s( el->getAttributeValue(i));
s.unsetf(ios::dec | ios::hex | ios::oct);
s >> id;
}
else if (attrName == "nocode") {
if (xml_readbool(el->getAttributeValue(i)))
flags |= no_code;
}
}
if (name.size() == 0)
throw LowlevelError("Missing function name");
if (size == -1)
throw LowlevelError("Missing function size");
const List &list( el->getChildren() );
List::const_iterator iter = list.begin();
baseaddr = Address::restoreXml( *iter, glb );
++iter;
for(;iter!=list.end();++iter) {
if ((*iter)->getName() == "localdb") {
if (localmap != (ScopeLocal *)0)
throw LowlevelError("Pre-existing local scope when restoring: "+name);
ScopeLocal *newMap = new ScopeLocal(id,stackid,this,glb);
glb->symboltab->restoreXmlScope(*iter,newMap); localmap = newMap;
}
else if ((*iter)->getName() == "override")
localoverride.restoreXml(*iter,glb);
else if ((*iter)->getName() == "prototype") {
if (localmap == (ScopeLocal *)0) {
ScopeLocal *newMap = new ScopeLocal(id,stackid,this,glb);
Scope *scope = glb->symboltab->getGlobalScope();
glb->symboltab->attachScope(newMap,scope); localmap = newMap;
}
funcp.setScope(localmap,baseaddr+ -1); funcp.restoreXml(*iter,glb);
}
else if ((*iter)->getName() == "jumptablelist")
restoreXmlJumpTable(*iter);
}
if (localmap == (ScopeLocal *)0) { ScopeLocal *newMap = new ScopeLocal(id,stackid,this,glb);
Scope *scope = glb->symboltab->getGlobalScope();
glb->symboltab->attachScope(newMap,scope); localmap = newMap;
funcp.setScope(localmap,baseaddr+ -1);
}
localmap->resetLocalWindow();
return id;
}
void Funcdata::doLiveInject(InjectPayload *payload,const Address &addr,BlockBasic *bl,list<PcodeOp *>::iterator iter)
{
PcodeEmitFd emitter;
InjectContext &context(glb->pcodeinjectlib->getCachedContext());
emitter.setFuncdata(this);
context.clear();
context.baseaddr = addr; context.nextaddr = addr;
list<PcodeOp *>::const_iterator deaditer = obank.endDead();
bool deadempty = (obank.beginDead() == deaditer);
if (!deadempty)
--deaditer;
payload->inject(context,emitter);
if (deadempty)
deaditer = obank.beginDead();
else
++deaditer;
while(deaditer != obank.endDead()) {
PcodeOp *op = *deaditer;
++deaditer;
if (op->isCallOrBranch())
throw LowlevelError("Illegal branching injection");
opInsert(op,bl,iter);
}
}
void PcodeEmitFd::dump(const Address &addr,OpCode opc,VarnodeData *outvar,VarnodeData *vars,int4 isize)
{ PcodeOp *op;
Varnode *vn;
if (outvar != (VarnodeData *)0) {
Address oaddr(outvar->space,outvar->offset);
op = fd->newOp(isize,addr);
fd->newVarnodeOut(outvar->size,oaddr,op);
}
else
op = fd->newOp(isize,addr);
fd->opSetOpcode(op,opc);
int4 i=0;
if (op->isCodeRef()) { Address addrcode(vars[0].space,vars[0].offset);
fd->opSetInput(op,fd->newCodeRef(addrcode),0);
i += 1;
}
for(;i<isize;++i) {
vn = fd->newVarnode(vars[i].size,vars[i].space,vars[i].offset);
fd->opSetInput(op,vn,i);
}
}
#ifdef OPACTION_DEBUG
void Funcdata::debugModCheck(PcodeOp *op)
{
if (op->isModified()) return;
if (!debugCheckRange(op)) return;
op->setAdditionalFlag(PcodeOp::modified);
ostringstream before;
op->printDebug(before);
modify_list.push_back(op);
modify_before.push_back( before.str() );
}
void Funcdata::debugModClear(void)
{
for(int4 i=0;i<modify_list.size();++i)
modify_list[i]->clearAdditionalFlag(PcodeOp::modified);
modify_list.clear();
modify_before.clear();
opactdbg_active = false;
}
void Funcdata::debugModPrint(const string &actionname)
{
if (!opactdbg_active) return;
opactdbg_active = false;
if (modify_list.empty()) return;
PcodeOp *op;
ostringstream s;
opactdbg_breakon |= (opactdbg_count == opactdbg_breakcount);
s << "DEBUG " << dec << opactdbg_count++ << ": " << actionname << endl;
for(int4 i=0;i<modify_list.size();++i) {
op = modify_list[i];
s << modify_before[i] << endl;
s << " ";
op->printDebug(s);
s << endl;
op->clearAdditionalFlag(PcodeOp::modified);
}
modify_list.clear();
modify_before.clear();
glb->printDebug(s.str());
}
void Funcdata::debugSetRange(const Address &pclow,const Address &pchigh,
uintm uqlow,uintm uqhigh)
{
opactdbg_on = true;
opactdbg_pclow.push_back(pclow);
opactdbg_pchigh.push_back(pchigh);
opactdbg_uqlow.push_back(uqlow);
opactdbg_uqhigh.push_back(uqhigh);
}
bool Funcdata::debugCheckRange(PcodeOp *op)
{
int4 i,size;
size = opactdbg_pclow.size();
for(i=0;i<size;++i) {
if (!opactdbg_pclow[i].isInvalid()) {
if (op->getAddr() < opactdbg_pclow[i])
continue;
if (opactdbg_pchigh[i] < op->getAddr())
continue;
}
if (opactdbg_uqlow[i] != ~((uintm)0)) {
if (opactdbg_uqlow[i] > op->getTime())
continue;
if (opactdbg_uqhigh[i] < op->getTime())
continue;
}
return true;
}
return false;
}
void Funcdata::debugPrintRange(int4 i) const
{
ostringstream s;
if (!opactdbg_pclow[i].isInvalid()) {
s << "PC = (";
opactdbg_pclow[i].printRaw(s);
s << ',';
opactdbg_pchigh[i].printRaw(s);
s << ") ";
}
else
s << "entire function ";
if (opactdbg_uqlow[i] != ~((uintm)0)) {
s << "unique = (" << hex << opactdbg_uqlow[i] << ',';
s << opactdbg_uqhigh[i] << ')';
}
glb->printDebug(s.str());
}
#endif