#include "sleigh.hh"
#include "loadimage.hh"
PcodeCacher::PcodeCacher(void)
{
uint4 maxsize = 600;
poolstart = new VarnodeData[ maxsize ];
endpool = poolstart + maxsize;
curpool = poolstart;
}
PcodeCacher::~PcodeCacher(void)
{
delete [] poolstart;
}
VarnodeData *PcodeCacher::expandPool(uint4 size)
{
uint4 curmax = endpool - poolstart;
uint4 cursize = curpool - poolstart;
if (cursize + size <= curmax)
return curpool; uint4 increase = (cursize + size) - curmax;
if (increase < 100) increase = 100;
uint4 newsize = curmax + increase;
VarnodeData *newpool = new VarnodeData[newsize];
for(uint4 i=0;i<cursize;++i)
newpool[i] = poolstart[i]; for(uint4 i=0;i<issued.size();++i) {
VarnodeData *outvar = issued[i].outvar;
if (outvar != (VarnodeData *)0) {
outvar = newpool + (outvar - poolstart);
issued[i].outvar = outvar;
}
VarnodeData *invar = issued[i].invar;
if (invar != (VarnodeData *)0) {
invar = newpool + (invar - poolstart);
issued[i].invar = invar;
}
}
list<RelativeRecord>::iterator iter;
for(iter=label_refs.begin();iter!=label_refs.end();++iter) {
VarnodeData *ref = (*iter).dataptr;
(*iter).dataptr = newpool + (ref - poolstart);
}
delete [] poolstart; poolstart = newpool;
curpool = newpool + (cursize + size);
endpool = newpool + newsize;
return newpool + cursize;
}
void PcodeCacher::addLabelRef(VarnodeData *ptr)
{
label_refs.emplace_back();
label_refs.back().dataptr = ptr;
label_refs.back().calling_index = issued.size();
}
void PcodeCacher::addLabel(uint4 id)
{
while(labels.size() <= id)
labels.push_back(0xbadbeef);
labels[ id ] = issued.size();
}
void PcodeCacher::clear(void)
{
curpool = poolstart;
issued.clear();
label_refs.clear();
labels.clear();
}
void PcodeCacher::resolveRelatives(void)
{
list<RelativeRecord>::const_iterator iter;
for(iter=label_refs.begin();iter!=label_refs.end();++iter) {
VarnodeData *ptr = (*iter).dataptr;
uint4 id = ptr->offset;
if ((id >= labels.size())||(labels[id] == 0xbadbeef))
throw LowlevelError("Reference to non-existant sleigh label");
uintb res = labels[id] - (*iter).calling_index;
res &= calc_mask( ptr->size );
ptr->offset = res;
}
}
void PcodeCacher::emit(const Address &addr,PcodeEmit *emt) const
{
vector<PcodeData>::const_iterator iter;
for(iter=issued.begin();iter!=issued.end();++iter)
emt->dump(addr,(*iter).opc,(*iter).outvar,(*iter).invar,(*iter).isize);
}
void SleighBuilder::generateLocation(const VarnodeTpl *vntpl,VarnodeData &vn)
{
vn.space = vntpl->getSpace().fixSpace(*walker);
vn.size = vntpl->getSize().fix(*walker);
if (vn.space == const_space)
vn.offset = vntpl->getOffset().fix(*walker) & calc_mask(vn.size);
else if (vn.space == uniq_space) {
vn.offset = vntpl->getOffset().fix(*walker);
vn.offset |= uniqueoffset;
}
else
vn.offset = vn.space->wrapOffset(vntpl->getOffset().fix(*walker));
}
AddrSpace *SleighBuilder::generatePointer(const VarnodeTpl *vntpl,VarnodeData &vn)
{
const FixedHandle &hand(walker->getFixedHandle(vntpl->getOffset().getHandleIndex()));
vn.space = hand.offset_space;
vn.size = hand.offset_size;
if (vn.space == const_space)
vn.offset = hand.offset_offset & calc_mask(vn.size);
else if (vn.space == uniq_space)
vn.offset = hand.offset_offset | uniqueoffset;
else
vn.offset = vn.space->wrapOffset(hand.offset_offset);
return hand.space;
}
void SleighBuilder::generatePointerAdd(PcodeData *op,const VarnodeTpl *vntpl)
{
uintb offsetPlus = vntpl->getOffset().getReal() & 0xffff;
if (offsetPlus == 0) {
return;
}
PcodeData *nextop = cache->allocateInstruction();
nextop->opc = op->opc;
nextop->invar = op->invar;
nextop->isize = op->isize;
nextop->outvar = op->outvar;
op->isize = 2;
op->opc = CPUI_INT_ADD;
VarnodeData *newparams = op->invar = cache->allocateVarnodes(2);
newparams[0] = nextop->invar[1];
newparams[1].space = const_space; newparams[1].offset = offsetPlus;
newparams[1].size = newparams[0].size;
op->outvar = nextop->invar + 1; op->outvar->space = uniq_space; op->outvar->offset = uniq_space->getTrans()->getUniqueStart(Translate::RUNTIME_BITRANGE_EA);
}
void SleighBuilder::dump(OpTpl *op)
{ PcodeData *thisop;
VarnodeData *invars;
VarnodeData *loadvars;
VarnodeData *storevars;
VarnodeTpl *vn,*outvn;
int4 isize = op->numInput();
invars = cache->allocateVarnodes(isize);
for(int4 i=0;i<isize;++i) {
vn = op->getIn(i);
if (vn->isDynamic(*walker)) {
generateLocation(vn,invars[i]); PcodeData *load_op = cache->allocateInstruction();
load_op->opc = CPUI_LOAD;
load_op->outvar = invars + i;
load_op->isize = 2;
loadvars = load_op->invar = cache->allocateVarnodes(2);
AddrSpace *spc = generatePointer(vn,loadvars[1]);
loadvars[0].space = const_space;
loadvars[0].offset = (uintb)(uintp)spc;
loadvars[0].size = sizeof(spc);
if (vn->getOffset().getSelect() == ConstTpl::v_offset_plus)
generatePointerAdd(load_op, vn);
}
else
generateLocation(vn,invars[i]);
}
if ((isize>0)&&(op->getIn(0)->isRelative())) {
invars->offset += getLabelBase();
cache->addLabelRef(invars);
}
thisop = cache->allocateInstruction();
thisop->opc = op->getOpcode();
thisop->invar = invars;
thisop->isize = isize;
outvn = op->getOut();
if (outvn != (VarnodeTpl *)0) {
if (outvn->isDynamic(*walker)) {
storevars = cache->allocateVarnodes(3);
generateLocation(outvn,storevars[2]); thisop->outvar = storevars+2;
PcodeData *store_op = cache->allocateInstruction();
store_op->opc = CPUI_STORE;
store_op->isize = 3;
store_op->invar = storevars;
AddrSpace *spc = generatePointer(outvn,storevars[1]); storevars[0].space = const_space;
storevars[0].offset = (uintb)(uintp)spc; storevars[0].size = sizeof(spc);
if (outvn->getOffset().getSelect() == ConstTpl::v_offset_plus)
generatePointerAdd(store_op,outvn);
}
else {
thisop->outvar = cache->allocateVarnodes(1);
generateLocation(outvn,*thisop->outvar);
}
}
}
void SleighBuilder::buildEmpty(Constructor *ct,int4 secnum)
{
int4 numops = ct->getNumOperands();
for(int4 i=0;i<numops;++i) {
SubtableSymbol *sym = (SubtableSymbol *)ct->getOperand(i)->getDefiningSymbol();
if (sym == (SubtableSymbol *)0) continue;
if (sym->getType() != SleighSymbol::subtable_symbol) continue;
walker->pushOperand(i);
ConstructTpl *construct = walker->getConstructor()->getNamedTempl(secnum);
if (construct == (ConstructTpl *)0)
buildEmpty(walker->getConstructor(),secnum);
else
build(construct,secnum);
walker->popOperand();
}
}
void SleighBuilder::setUniqueOffset(const Address &addr)
{
uniqueoffset = (addr.getOffset() & uniquemask)<<4;
}
SleighBuilder::SleighBuilder(ParserWalker *w,DisassemblyCache *dcache,PcodeCacher *pc,AddrSpace *cspc,
AddrSpace *uspc,uint4 umask)
: PcodeBuilder(0)
{
walker = w;
discache = dcache;
cache = pc;
const_space = cspc;
uniq_space = uspc;
uniquemask = umask;
uniqueoffset = (walker->getAddr().getOffset() & uniquemask)<<4;
}
void SleighBuilder::appendBuild(OpTpl *bld,int4 secnum)
{
int4 index = bld->getIn(0)->getOffset().getReal(); SubtableSymbol *sym = (SubtableSymbol *)walker->getConstructor()->getOperand(index)->getDefiningSymbol();
if ((sym==(SubtableSymbol *)0)||(sym->getType() != SleighSymbol::subtable_symbol)) return;
walker->pushOperand(index);
Constructor *ct = walker->getConstructor();
if (secnum >=0) {
ConstructTpl *construct = ct->getNamedTempl(secnum);
if (construct == (ConstructTpl *)0)
buildEmpty(ct,secnum);
else
build(construct,secnum);
}
else {
ConstructTpl *construct = ct->getTempl();
build(construct,-1);
}
walker->popOperand();
}
void SleighBuilder::delaySlot(OpTpl *op)
{
ParserWalker *tmp = walker;
uintb olduniqueoffset = uniqueoffset;
Address baseaddr = tmp->getAddr();
int4 fallOffset = tmp->getLength();
int4 delaySlotByteCnt = tmp->getParserContext()->getDelaySlot();
int4 bytecount = 0;
do {
Address newaddr = baseaddr + fallOffset;
setUniqueOffset(newaddr);
const ParserContext *pos = discache->getParserContext(newaddr);
if (pos->getParserState() != ParserContext::pcode)
throw LowlevelError("Could not obtain cached delay slot instruction");
int4 len = pos->getLength();
ParserWalker newwalker( pos );
walker = &newwalker;
walker->baseState();
build(walker->getConstructor()->getTempl(),-1); fallOffset += len;
bytecount += len;
} while(bytecount < delaySlotByteCnt);
walker = tmp; uniqueoffset = olduniqueoffset;
}
void SleighBuilder::setLabel(OpTpl *op)
{
cache->addLabel( op->getIn(0)->getOffset().getReal()+getLabelBase() );
}
void SleighBuilder::appendCrossBuild(OpTpl *bld,int4 secnum)
{
if (secnum>=0)
throw LowlevelError("CROSSBUILD directive within a named section");
secnum = bld->getIn(1)->getOffset().getReal();
VarnodeTpl *vn = bld->getIn(0);
AddrSpace *spc = vn->getSpace().fixSpace(*walker);
uintb addr = spc->wrapOffset( vn->getOffset().fix(*walker) );
ParserWalker *tmp = walker;
uintb olduniqueoffset = uniqueoffset;
Address newaddr(spc,addr);
setUniqueOffset(newaddr);
const ParserContext *pos = discache->getParserContext( newaddr );
if (pos->getParserState() != ParserContext::pcode)
throw LowlevelError("Could not obtain cached crossbuild instruction");
ParserWalker newwalker( pos, tmp->getParserContext() );
walker = &newwalker;
walker->baseState();
Constructor *ct = walker->getConstructor();
ConstructTpl *construct = ct->getNamedTempl(secnum);
if (construct == (ConstructTpl *)0)
buildEmpty(ct,secnum);
else
build(construct,secnum);
walker = tmp;
uniqueoffset = olduniqueoffset;
}
void DisassemblyCache::initialize(int4 min,int4 hashsize)
{
minimumreuse = min;
mask = hashsize-1;
uintb masktest = coveringmask((uintb)mask);
if (masktest != (uintb)mask) throw LowlevelError("Bad windowsize for disassembly cache");
list = new ParserContext *[minimumreuse];
nextfree = 0;
hashtable = new ParserContext *[hashsize];
for(int4 i=0;i<minimumreuse;++i) {
ParserContext *pos = new ParserContext(contextcache);
pos->initialize(75,20,constspace);
list[i] = pos;
}
ParserContext *pos = list[0];
for(int4 i=0;i<hashsize;++i)
hashtable[i] = pos; }
void DisassemblyCache::free(void)
{
for(int4 i=0;i<minimumreuse;++i)
delete list[i];
delete [] list;
delete [] hashtable;
}
DisassemblyCache::DisassemblyCache(ContextCache *ccache,AddrSpace *cspace,int4 cachesize,int4 windowsize)
{
contextcache = ccache;
constspace = cspace;
initialize(cachesize,windowsize); }
ParserContext *DisassemblyCache::getParserContext(const Address &addr)
{
int4 hashindex = ((int4) addr.getOffset()) & mask;
ParserContext *res = hashtable[ hashindex ];
if (res->getAddr() == addr)
return res;
res = list[ nextfree ];
nextfree += 1; if (nextfree >= minimumreuse)
nextfree = 0;
res->setAddr(addr);
res->setParserState(ParserContext::uninitialized); hashtable[ hashindex ] = res; return res;
}
Sleigh::Sleigh(LoadImage *ld,ContextDatabase *c_db)
: SleighBase()
{
loader = ld;
context_db = c_db;
cache = new ContextCache(c_db);
discache = (DisassemblyCache *)0;
}
void Sleigh::clearForDelete(void)
{
delete cache;
if (discache != (DisassemblyCache *)0)
delete discache;
}
Sleigh::~Sleigh(void)
{
clearForDelete();
}
void Sleigh::reset(LoadImage *ld,ContextDatabase *c_db)
{
clearForDelete();
pcode_cache.clear();
loader = ld;
context_db = c_db;
cache = new ContextCache(c_db);
discache = (DisassemblyCache *)0;
}
void Sleigh::initialize(DocumentStorage &store)
{
if (!isInitialized()) { const Element *el = store.getTag("sleigh");
if (el == (const Element *)0)
throw LowlevelError("Could not find sleigh tag");
restoreXml(el);
}
else
reregisterContext();
uint4 parser_cachesize = 2;
uint4 parser_windowsize = 32;
if ((maxdelayslotbytes > 1)||(unique_allocatemask != 0)) {
parser_cachesize = 8;
parser_windowsize = 256;
}
discache = new DisassemblyCache(cache,getConstantSpace(),parser_cachesize,parser_windowsize);
}
ParserContext *Sleigh::obtainContext(const Address &addr,int4 state) const
{
ParserContext *pos = discache->getParserContext(addr);
int4 curstate = pos->getParserState();
if (curstate >= state)
return pos;
if (curstate == ParserContext::uninitialized) {
resolve(*pos);
if (state == ParserContext::disassembly)
return pos;
}
resolveHandles(*pos);
return pos;
}
void Sleigh::resolve(ParserContext &pos) const
{
loader->loadFill(pos.getBuffer(),16,pos.getAddr());
ParserWalkerChange walker(&pos);
pos.deallocateState(walker); Constructor *ct,*subct;
uint4 off;
int4 oper,numoper;
pos.setDelaySlot(0);
walker.setOffset(0); pos.clearCommits(); pos.loadContext(); ct = root->resolve(walker); walker.setConstructor(ct);
ct->applyContext(walker);
while(walker.isState()) {
ct = walker.getConstructor();
oper = walker.getOperand();
numoper = ct->getNumOperands();
while(oper < numoper) {
OperandSymbol *sym = ct->getOperand(oper);
off = walker.getOffset(sym->getOffsetBase()) + sym->getRelativeOffset();
pos.allocateOperand(oper,walker); walker.setOffset(off);
TripleSymbol *tsym = sym->getDefiningSymbol();
if (tsym != (TripleSymbol *)0) {
subct = tsym->resolve(walker);
if (subct != (Constructor *)0) {
walker.setConstructor(subct);
subct->applyContext(walker);
break;
}
}
walker.setCurrentLength(sym->getMinimumLength());
walker.popOperand();
oper += 1;
}
if (oper >= numoper) { walker.calcCurrentLength(ct->getMinimumLength(),numoper);
walker.popOperand();
ConstructTpl *templ = ct->getTempl();
if ((templ != (ConstructTpl *)0)&&(templ->delaySlot() > 0))
pos.setDelaySlot(templ->delaySlot());
}
}
pos.setNaddr(pos.getAddr()+pos.getLength()); pos.setParserState(ParserContext::disassembly);
}
void Sleigh::resolveHandles(ParserContext &pos) const
{
TripleSymbol *triple;
Constructor *ct;
int4 oper,numoper;
ParserWalker walker(&pos);
walker.baseState();
while(walker.isState()) {
ct = walker.getConstructor();
oper = walker.getOperand();
numoper = ct->getNumOperands();
while(oper < numoper) {
OperandSymbol *sym = ct->getOperand(oper);
walker.pushOperand(oper); triple = sym->getDefiningSymbol();
if (triple != (TripleSymbol *)0) {
if (triple->getType() == SleighSymbol::subtable_symbol)
break;
else triple->getFixedHandle(walker.getParentHandle(),walker);
}
else { PatternExpression *patexp = sym->getDefiningExpression();
intb res = patexp->getValue(walker);
FixedHandle &hand(walker.getParentHandle());
hand.space = pos.getConstSpace(); hand.offset_space = (AddrSpace *)0;
hand.offset_offset = (uintb)res;
hand.size = 0; }
walker.popOperand();
oper += 1;
}
if (oper >= numoper) { ConstructTpl *templ = ct->getTempl();
if (templ != (ConstructTpl *)0) {
HandleTpl *res = templ->getResult();
if (res != (HandleTpl *)0) res->fix(walker.getParentHandle(),walker);
}
walker.popOperand();
}
}
pos.setParserState(ParserContext::pcode);
}
int4 Sleigh::instructionLength(const Address &baseaddr) const
{
ParserContext *pos = obtainContext(baseaddr,ParserContext::disassembly);
return pos->getLength();
}
int4 Sleigh::printAssembly(AssemblyEmit &emit,const Address &baseaddr) const
{
int4 sz;
ParserContext *pos = obtainContext(baseaddr,ParserContext::disassembly);
ParserWalker walker(pos);
walker.baseState();
Constructor *ct = walker.getConstructor();
ostringstream mons;
ct->printMnemonic(mons,walker);
ostringstream body;
ct->printBody(body,walker);
emit.dump(baseaddr,mons.str(),body.str());
sz = pos->getLength();
return sz;
}
int4 Sleigh::oneInstruction(PcodeEmit &emit,const Address &baseaddr) const
{
int4 fallOffset;
if (alignment != 1) {
if ((baseaddr.getOffset() % alignment)!=0) {
ostringstream s;
s << "Instruction address not aligned: " << baseaddr;
throw UnimplError(s.str(),0);
}
}
ParserContext *pos = obtainContext(baseaddr,ParserContext::pcode);
pos->applyCommits();
fallOffset = pos->getLength();
if (pos->getDelaySlot()>0) {
int4 bytecount = 0;
do {
ParserContext *delaypos = obtainContext(pos->getAddr() + fallOffset,ParserContext::pcode);
delaypos->applyCommits();
int4 len = delaypos->getLength();
fallOffset += len;
bytecount += len;
} while(bytecount < pos->getDelaySlot());
pos->setNaddr(pos->getAddr()+fallOffset);
}
ParserWalker walker(pos);
walker.baseState();
pcode_cache.clear();
SleighBuilder builder(&walker,discache,&pcode_cache,getConstantSpace(),getUniqueSpace(),unique_allocatemask);
try {
builder.build(walker.getConstructor()->getTempl(),-1);
pcode_cache.resolveRelatives();
pcode_cache.emit(baseaddr,&emit);
} catch(UnimplError &err) {
ostringstream s;
s << "Instruction not implemented in pcode:\n ";
ParserWalker *cur = builder.getCurrentWalker();
cur->baseState();
Constructor *ct = cur->getConstructor();
cur->getAddr().printRaw(s);
s << ": ";
ct->printMnemonic(s,*cur);
s << " ";
ct->printBody(s,*cur);
err.explain = s.str();
err.instruction_length = fallOffset;
throw err;
}
return fallOffset;
}
void Sleigh::registerContext(const string &name,int4 sbit,int4 ebit)
{
context_db->registerVariable(name,sbit,ebit);
}
void Sleigh::setContextDefault(const string &name,uintm val)
{
context_db->setVariableDefault(name,val);
}
void Sleigh::allowContextSet(bool val) const
{
cache->allowSet(val);
}