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

797 lines
26 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 "sleigh.hh"
#include "loadimage.hh"
PcodeCacher::PcodeCacher(void)
{
// We aim to allocate this array only once
uint4 maxsize = 600;
poolstart = new VarnodeData[ maxsize ];
endpool = poolstart + maxsize;
curpool = poolstart;
}
PcodeCacher::~PcodeCacher(void)
{
delete [] poolstart;
}
/// Expand the VarnodeData pool so that \e size more elements fit, and return
/// a pointer to first available element.
/// \param size is the number of elements to expand the pool by
/// \return the first available VarnodeData
VarnodeData *PcodeCacher::expandPool(uint4 size)
{
uint4 curmax = endpool - poolstart;
uint4 cursize = curpool - poolstart;
if (cursize + size <= curmax)
return curpool; // No expansion necessary
uint4 increase = (cursize + size) - curmax;
if (increase < 100) // Increase by at least 100
increase = 100;
uint4 newsize = curmax + increase;
VarnodeData *newpool = new VarnodeData[newsize];
for(uint4 i=0;i<cursize;++i)
newpool[i] = poolstart[i]; // Copy old data
// Update references to the old pool
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; // Free up old pool
poolstart = newpool;
curpool = newpool + (cursize + size);
endpool = newpool + newsize;
return newpool + cursize;
}
/// Store off a reference to the Varnode and the absolute index of the next
/// instruction. The Varnode must be an operand of the current instruction.
/// \param ptr is the Varnode reference
void PcodeCacher::addLabelRef(VarnodeData *ptr)
{
label_refs.emplace_back();
label_refs.back().dataptr = ptr;
label_refs.back().calling_index = issued.size();
}
/// The label has an id that is referred to by Varnodes holding
/// intra-instruction branch targets, prior to converting
/// them to a \e relative \e branch offset. The label is associated with
/// the absolute index of the next PcodeData object to be issued,
/// facilitating this conversion.
/// \param id is the given id of the label
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();
}
/// Assuming all the PcodeData has been generated for an
/// instruction, go resolve any relative offsets and back
/// patch their value(s) into the PcodeData
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");
// Calculate the relative index given the two absolute indices
uintb res = labels[id] - (*iter).calling_index;
res &= calc_mask( ptr->size );
ptr->offset = res;
}
}
/// Each p-code operation is presented to the emitter via its dump() method.
/// \param addr is the Address associated with the p-code operation
/// \param emt is the emitter
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);
}
/// \brief Generate a concrete VarnodeData object from the given template (VarnodeTpl)
///
/// \param vntpl is the template to reference
/// \param vn is the object to fill in with concrete values
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));
}
/// \brief Generate a pointer VarnodeData from a dynamic template (VarnodeTpl)
///
/// The symbol represents a value referenced through a dynamic pointer.
/// This method generates the varnode representing the pointer itself and also
/// returns the address space in anticipation of generating the LOAD or STORE
/// that actually manipulates the value.
/// \param vntpl is the dynamic template to reference
/// \param vn is the object to fill with concrete values
/// \return the address space being pointed to
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; // Add in V_OFFSET_PLUS
newparams[1].offset = offsetPlus;
newparams[1].size = newparams[0].size;
op->outvar = nextop->invar + 1; // Output of ADD is input to original op
op->outvar->space = uniq_space; // Result of INT_ADD in special runtime temp
op->outvar->offset = uniq_space->getTrans()->getUniqueStart(Translate::RUNTIME_BITRANGE_EA);
}
void SleighBuilder::dump(OpTpl *op)
{ // Dump on op through low-level dump interface
// filling in dynamic loads and stores if necessary
PcodeData *thisop;
VarnodeData *invars;
VarnodeData *loadvars;
VarnodeData *storevars;
VarnodeTpl *vn,*outvn;
int4 isize = op->numInput();
// First build all the inputs
invars = cache->allocateVarnodes(isize);
for(int4 i=0;i<isize;++i) {
vn = op->getIn(i);
if (vn->isDynamic(*walker)) {
generateLocation(vn,invars[i]); // Input of -op- is really temporary storage
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]); // Output of -op- is really temporary storage
thisop->outvar = storevars+2;
PcodeData *store_op = cache->allocateInstruction();
store_op->opc = CPUI_STORE;
store_op->isize = 3;
// store_op->outvar = (VarnodeData *)0;
store_op->invar = storevars;
AddrSpace *spc = generatePointer(outvn,storevars[1]); // pointer
storevars[0].space = const_space;
storevars[0].offset = (uintb)(uintp)spc; // space in which to store
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);
}
}
}
/// \brief Build a named p-code section of a constructor that contains only implied BUILD directives
///
/// If a named section of a constructor is empty, we still need to walk
/// through any subtables that might contain p-code in their named sections.
/// This method treats each subtable operand as an implied \e build directive,
/// in the otherwise empty section.
/// \param ct is the matching currently Constructor being built
/// \param secnum is the particular \e named section number to build
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();
}
}
/// Bits used to make temporary registers unique across multiple instructions
/// are generated based on the given address.
/// \param addr is the given Address
void SleighBuilder::setUniqueOffset(const Address &addr)
{
uniqueoffset = (addr.getOffset() & uniquemask)<<4;
}
/// \brief Constructor
///
/// \param w is the parsed instruction
/// \param dcache is a cache of nearby instruction parses
/// \param pc will hold the PcodeData and VarnodeData objects produced by \b this builder
/// \param cspc is the constant address space
/// \param uspc is the unique address space
/// \param umask is the mask to use to find unique bits within an Address
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)
{
// Append p-code for a particular build statement
int4 index = bld->getIn(0)->getOffset().getReal(); // Recover operand index from build statement
// Check if operand is a subtable
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)
{
// Append pcode for an entire instruction (delay slot)
// in the middle of the current instruction
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); // Build the whole delay slot
fallOffset += len;
bytecount += len;
} while(bytecount < delaySlotByteCnt);
walker = tmp; // Restore original context
uniqueoffset = olduniqueoffset;
}
void SleighBuilder::setLabel(OpTpl *op)
{
cache->addLabel( op->getIn(0)->getOffset().getReal()+getLabelBase() );
}
void SleighBuilder::appendCrossBuild(OpTpl *bld,int4 secnum)
{
// Weave in the p-code section from an instruction at another address
// bld-param(0) contains the address of the instruction
// bld-param(1) contains the section number
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;
}
/// \param min is the minimum number of allocations before a reuse is expected
/// \param hashsize is the number of elements in the hash-table
void DisassemblyCache::initialize(int4 min,int4 hashsize)
{
minimumreuse = min;
mask = hashsize-1;
uintb masktest = coveringmask((uintb)mask);
if (masktest != (uintb)mask) // -hashsize- must be a power of 2
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,translate);
pos->initialize(75,20,constspace);
list[i] = pos;
}
ParserContext *pos = list[0];
for(int4 i=0;i<hashsize;++i)
hashtable[i] = pos; // Make sure all hashtable positions point to a real ParserContext
}
void DisassemblyCache::free(void)
{
for(int4 i=0;i<minimumreuse;++i)
delete list[i];
delete [] list;
delete [] hashtable;
}
/// \param trans is the Translate object instantiating this cache (for inst_next2 callbacks)
/// \param ccache is the ContextCache front-end shared across all the parser contexts
/// \param cspace is the constant address space used for minting constant Varnodes
/// \param cachesize is the number of distinct ParserContext objects in this cache
/// \param windowsize is the size of the ParserContext hash-table
DisassemblyCache::DisassemblyCache(Translate *trans,ContextCache *ccache,AddrSpace *cspace,int4 cachesize,int4 windowsize)
{
translate = trans;
contextcache = ccache;
constspace = cspace;
initialize(cachesize,windowsize); // Set default settings for the cache
}
/// Return a (possibly cached) ParserContext that is associated with \e addr
/// If n different calls to this interface are made with n different Addresses, if
/// - n <= minimumreuse AND
/// - all the addresses are within the windowsize (=mask+1)
///
/// then the cacher guarantees that you get all different ParserContext objects
/// \param addr is the Address to disassemble at
/// \return the ParserContext associated with the address
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; // Advance the circular index
if (nextfree >= minimumreuse)
nextfree = 0;
res->setAddr(addr);
res->setParserState(ParserContext::uninitialized); // Need to start over with parsing
hashtable[ hashindex ] = res; // Stick it into the hashtable
return res;
}
/// \param ld is the LoadImage to draw program bytes from
/// \param c_db is the context database
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();
}
/// Completely clear everything except the base and reconstruct
/// with a new LoadImage and ContextDatabase
/// \param ld is the new LoadImage
/// \param c_db is the new ContextDatabase
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;
}
/// The .sla file from the document store is loaded and cache objects are prepared
/// \param store is the document store containing the main \<sleigh> tag.
void Sleigh::initialize(DocumentStorage &store)
{
if (!isInitialized()) { // Initialize the base if not already
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(this,cache,getConstantSpace(),parser_cachesize,parser_windowsize);
}
/// \brief Obtain a parse tree for the instruction at the given address
///
/// The tree may be cached from a previous access. If the address
/// has not been parsed, disassembly is performed, and a new parse tree
/// is prepared. Depending on the desired \e state, the parse tree
/// can be prepared either for disassembly or for p-code generation.
/// \param addr is the given address of the instruction
/// \param state is the desired parse state.
/// \return the parse tree object (ParseContext)
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;
}
// If we reach here, state must be ParserContext::pcode
resolveHandles(*pos);
return pos;
}
/// Resolve \e all the constructors involved in the instruction at the indicated address
/// \param pos is the parse object that will hold the resulting tree
void Sleigh::resolve(ParserContext &pos) const
{
loader->loadFill(pos.getBuffer(),16,pos.getAddr());
ParserWalkerChange walker(&pos);
pos.deallocateState(walker); // Clear the previous resolve and initialize the walker
Constructor *ct,*subct;
uint4 off;
int4 oper,numoper;
pos.setDelaySlot(0);
walker.setOffset(0); // Initial offset
pos.clearCommits(); // Clear any old context commits
pos.loadContext(); // Get context for current address
ct = root->resolve(walker); // Base constructor
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); // Descend into new operand and reserve space
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) { // Finished processing constructor
walker.calcCurrentLength(ct->getMinimumLength(),numoper);
walker.popOperand();
// Check for use of delayslot
ConstructTpl *templ = ct->getTempl();
if ((templ != (ConstructTpl *)0)&&(templ->delaySlot() > 0))
pos.setDelaySlot(templ->delaySlot());
}
}
pos.setNaddr(pos.getAddr()+pos.getLength()); // Update Naddr to pointer after instruction
pos.setParserState(ParserContext::disassembly);
}
/// Resolve handle templates for the given parse tree, assuming Constructors
/// are already resolved.
/// \param pos is the given parse tree
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); // Descend into node
triple = sym->getDefiningSymbol();
if (triple != (TripleSymbol *)0) {
if (triple->getType() == SleighSymbol::subtable_symbol)
break;
else // Some other kind of symbol as an operand
triple->getFixedHandle(walker.getParentHandle(),walker);
}
else { // Must be an expression
PatternExpression *patexp = sym->getDefiningExpression();
intb res = patexp->getValue(walker);
FixedHandle &hand(walker.getParentHandle());
hand.space = pos.getConstSpace(); // Result of expression is a constant
hand.offset_space = (AddrSpace *)0;
hand.offset_offset = (uintb)res;
hand.size = 0; // This size should not get used
}
walker.popOperand();
oper += 1;
}
if (oper >= numoper) { // Finished processing constructor
ConstructTpl *templ = ct->getTempl();
if (templ != (ConstructTpl *)0) {
HandleTpl *res = templ->getResult();
if (res != (HandleTpl *)0) // Pop up handle to containing operand
res->fix(walker.getParentHandle(),walker);
// If we need an indicator that the constructor exports nothing try
// else
// walker.getParentHandle().setInvalid();
}
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 {
// Do not pass pos->getNaddr() to obtainContext, as pos may have been previously cached and had naddr adjusted
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);
}