/* ### * 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 "slgh_compile.hh" #include "filemanage.hh" #include SleighCompile *slgh; // Global pointer to sleigh object for use with parser #ifdef YYDEBUG extern int yydebug; // Global debugging state for parser #endif extern FILE *yyin; // Global pointer to file for lexer extern int yyparse(void); extern int yylex_destroy(void); static VarnodeTpl *find_size(const ConstTpl &offset,const ConstructTpl *ct) { // Find a defining instance of the local variable // with given -offset- const vector &ops(ct->getOpvec()); VarnodeTpl *vn; OpTpl *op; for(int4 i=0;igetOut(); if ((vn!=(VarnodeTpl *)0)&&(vn->isLocalTemp())) { if (vn->getOffset() == offset) return vn; } for(int4 j=0;jnumInput();++j) { vn = op->getIn(j); if (vn->isLocalTemp()&&(vn->getOffset()==offset)) return vn; } } return (VarnodeTpl *)0; } static bool force_exportsize(ConstructTpl *ct) { // Look for zero size temps in export statement HandleTpl *result = ct->getResult(); if (result == (HandleTpl *)0) return true; VarnodeTpl *vt; if (result->getPtrSpace().isUniqueSpace()&&result->getPtrSize().isZero()) { vt = find_size(result->getPtrOffset(),ct); if (vt == (VarnodeTpl *)0) return false; result->setPtrSize(vt->getSize()); } else if (result->getSpace().isUniqueSpace()&&result->getSize().isZero()) { vt = find_size(result->getPtrOffset(),ct); if (vt == (VarnodeTpl *)0) return false; result->setSize(vt->getSize()); } return true; } SectionVector::SectionVector(ConstructTpl *rtl,SymbolScope *scope) { nextindex = -1; main.section = rtl; main.scope = scope; } void SectionVector::append(ConstructTpl *rtl,SymbolScope *scope) { while(named.size() <= nextindex) named.push_back(RtlPair()); named[ nextindex ] = RtlPair(rtl,scope); } SpaceQuality::SpaceQuality(const string &nm) { // Default space qualities name = nm; type = ramtype; size = 0; wordsize = 1; isdefault = false; } FieldQuality::FieldQuality(string *nm,uintb *l,uintb *h) { name = *nm; low = *l; high = *h; signext = false; flow = true; hex = true; delete nm; delete l; delete h; } void WithBlock::set(SubtableSymbol *s, PatternEquation *pq, vector *cvec) { ss = s; pateq = pq; if (pateq != (PatternEquation *)0) pateq->layClaim(); if (cvec != (vector *)0) { for(int4 i=0;isize();++i) contvec.push_back((*cvec)[i]); // Lay claim to -cvec-s pointers, we don't clone delete cvec; } } WithBlock::~WithBlock(void) { if (pateq != (PatternEquation *)0) PatternEquation::release(pateq); for(int4 i=0;i &stack, PatternEquation *pateq) { list::const_iterator iter; for(iter=stack.begin();iter!=stack.end();++iter) { PatternEquation *witheq = (*iter).pateq; if (witheq != (PatternEquation *)0) pateq = new EquationAnd(witheq, pateq); } return pateq; } vector *WithBlock::collectAndPrependContext(const list &stack, vector *contvec) { // Make new list of ContextChanges, prepending everything from stack to -contvec-, delete old contvec vector *res = (vector *)0; list::const_iterator iter; for(iter=stack.begin();iter!=stack.end();++iter) { const vector &changelist( (*iter).contvec ); if (changelist.size() == 0) continue; if (res == (vector *)0) res = new vector(); for(int4 i=0;ipush_back(changelist[i]->clone()); } } if (contvec != (vector *)0) { if (contvec->size() != 0) { if (res == (vector *)0) res = new vector(); for(int4 i=0;isize();++i) res->push_back((*contvec)[i]); // lay claim to contvecs pointer } delete contvec; } return res; } SubtableSymbol *WithBlock::getCurrentSubtable(const list &stack) { list::const_iterator iter; for(iter=stack.begin();iter!=stack.end();++iter) { if ((*iter).ss != (SubtableSymbol *)0) return (*iter).ss; } return (SubtableSymbol *)0; } ConsistencyChecker::ConsistencyChecker(SubtableSymbol *rt,bool un,bool warndead) { root_symbol = rt; unnecessarypcode = 0; readnowrite = 0; writenoread = 0; printextwarning = un; printdeadwarning = warndead; } int4 ConsistencyChecker::recoverSize(const ConstTpl &sizeconst,Constructor *ct) { int4 size,handindex; OperandSymbol *opsym; SubtableSymbol *tabsym; map::const_iterator iter; switch(sizeconst.getType()) { case ConstTpl::real: size = (int4) sizeconst.getReal(); break; case ConstTpl::handle: handindex = sizeconst.getHandleIndex(); opsym = ct->getOperand(handindex); size = opsym->getSize(); if (size == -1) { tabsym = dynamic_cast(opsym->getDefiningSymbol()); if (tabsym == (SubtableSymbol *)0) throw SleighError("Could not recover varnode template size"); iter = sizemap.find(tabsym); if (iter == sizemap.end()) throw SleighError("Subtable out of order"); size = (*iter).second; } break; default: throw SleighError("Bad constant type as varnode template size"); } return size; } void ConsistencyChecker::dealWithUnnecessaryExt(OpTpl *op,Constructor *ct) { // Deal with detected extension (SEXT or ZEXT) where the // input size is the same as the output size if (printextwarning) { cerr << "Unnecessary "; printOpName(cerr,op); cerr << " in constructor starting at line " << dec << ct->getLineno() << endl; } op->setOpcode(CPUI_COPY); // Equivalent to copy unnecessarypcode += 1; } void ConsistencyChecker::dealWithUnnecessaryTrunc(OpTpl *op,Constructor *ct) { if (printextwarning) { cerr << "Unnecessary "; printOpName(cerr,op); cerr << " in constructor starting at line " << dec << ct->getLineno() << endl; } op->setOpcode(CPUI_COPY); // Equivalent to copy op->removeInput(1); unnecessarypcode += 1; } bool ConsistencyChecker::checkOpMisuse(OpTpl *op,Constructor *ct) { switch(op->getOpcode()) { case CPUI_INT_LESS: { VarnodeTpl *vn = op->getIn(1); if (vn->getSpace().isConstSpace() && vn->getOffset().isZero()) { cerr << "Unsigned comparison with zero is always false in constructor starting at line " << dec << ct->getLineno() << endl; } } break; default: break; } return true; } bool ConsistencyChecker::sizeRestriction(OpTpl *op,Constructor *ct) { // Make sure op template meets size restrictions // Return false and any info about mismatched sizes int4 vnout,vn0,vn1; AddrSpace *spc; switch(op->getOpcode()) { case CPUI_COPY: // Instructions where all inputs and output are same size case CPUI_INT_2COMP: case CPUI_INT_NEGATE: case CPUI_FLOAT_NEG: case CPUI_FLOAT_ABS: case CPUI_FLOAT_SQRT: case CPUI_FLOAT_CEIL: case CPUI_FLOAT_FLOOR: case CPUI_FLOAT_ROUND: vnout = recoverSize(op->getOut()->getSize(),ct); if (vnout == -1) { printOpError(op,ct,-1,-1,"Using subtable with exports in expression"); return false; } vn0 = recoverSize(op->getIn(0)->getSize(),ct); if (vn0 == -1) { printOpError(op,ct,0,0,"Using subtable with exports in expression"); return false; } if (vnout == vn0) return true; if ((vnout==0)||(vn0==0)) return true; printOpError(op,ct,-1,0,"Input and output sizes must match"); return false; case CPUI_INT_ADD: case CPUI_INT_SUB: 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: vnout = recoverSize(op->getOut()->getSize(),ct); if (vnout == -1) { printOpError(op,ct,-1,-1,"Using subtable with exports in expression"); return false; } vn0 = recoverSize(op->getIn(0)->getSize(),ct); if (vn0 == -1) { printOpError(op,ct,0,0,"Using subtable with exports in expression"); return false; } vn1 = recoverSize(op->getIn(1)->getSize(),ct); if (vn1 == -1) { printOpError(op,ct,1,1,"Using subtable with exports in expression"); return false; } if ((vnout!=0)&&(vn0!=0)&&(vnout!=vn0)) { printOpError(op,ct,-1,0,"The output and all input sizes must match"); return false; } if ((vnout!=0)&&(vn1!=0)&&(vnout!=vn1)) { printOpError(op,ct,-1,1,"The output and all input sizes must match"); return false; } if ((vn0!=0)&&(vn1!=0)&&(vn0!=vn1)) { printOpError(op,ct,0,1,"The output and all input sizes must match"); return false; } return true; case CPUI_FLOAT_NAN: vnout = recoverSize(op->getOut()->getSize(),ct); if (vnout == -1) { printOpError(op,ct,-1,-1,"Using subtable with exports in expression"); return false; } if (vnout != 1) { printOpError(op,ct,-1,-1,"Output must be a boolean (size 1)"); return false; } break; case CPUI_INT_EQUAL: // Instructions with bool output, all inputs equal size 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: vnout = recoverSize(op->getOut()->getSize(),ct); if (vnout == -1) { printOpError(op,ct,-1,-1,"Using subtable with exports in expression"); return false; } if (vnout != 1) { printOpError(op,ct,-1,-1,"Output must be a boolean (size 1)"); return false; } vn0 = recoverSize(op->getIn(0)->getSize(),ct); if (vn0 == -1) { printOpError(op,ct,0,0,"Using subtable with exports in expression"); return false; } vn1 = recoverSize(op->getIn(1)->getSize(),ct); if (vn1 == -1) { printOpError(op,ct,1,1,"Using subtable with exports in expression"); return false; } if ((vn0==0)||(vn1==0)) return true; if (vn0 != vn1) { printOpError(op,ct,0,1,"Inputs must be the same size"); return false; } return true; case CPUI_BOOL_XOR: case CPUI_BOOL_AND: case CPUI_BOOL_OR: vnout = recoverSize(op->getOut()->getSize(),ct); if (vnout == -1) { printOpError(op,ct,-1,-1,"Using subtable with exports in expression"); return false; } if (vnout != 1) { printOpError(op,ct,-1,-1,"Output must be a boolean (size 1)"); return false; } vn0 = recoverSize(op->getIn(0)->getSize(),ct); if (vn0 == -1) { printOpError(op,ct,0,0,"Using subtable with exports in expression"); return false; } if (vn0 != 1) { printOpError(op,ct,0,0,"Input must be a boolean (size 1)"); return false; } return true; case CPUI_BOOL_NEGATE: vnout = recoverSize(op->getOut()->getSize(),ct); if (vnout == -1) { printOpError(op,ct,-1,-1,"Using subtable with exports in expression"); return false; } if (vnout != 1) { printOpError(op,ct,-1,-1,"Output must be a boolean (size 1)"); return false; } vn0 = recoverSize(op->getIn(0)->getSize(),ct); if (vn0 == -1) { printOpError(op,ct,0,0,"Using subtable with exports in expression"); return false; } if (vn0 != 1) { printOpError(op,ct,0,0,"Input must be a boolean (size 1)"); return false; } return true; // The shift amount does not necessarily have to be the same size // But the output and first parameter must be same size case CPUI_INT_LEFT: case CPUI_INT_RIGHT: case CPUI_INT_SRIGHT: vnout = recoverSize(op->getOut()->getSize(),ct); if (vnout == -1) { printOpError(op,ct,-1,-1,"Using subtable with exports in expression"); return false; } vn0 = recoverSize(op->getIn(0)->getSize(),ct); if (vn0 == -1) { printOpError(op,ct,0,0,"Using subtable with exports in expression"); return false; } if ((vnout==0)||(vn0==0)) return true; if (vnout != vn0) { printOpError(op,ct,-1,0,"Output and first input must be the same size"); return false; } return true; case CPUI_INT_ZEXT: case CPUI_INT_SEXT: vnout = recoverSize(op->getOut()->getSize(),ct); if (vnout == -1) { printOpError(op,ct,-1,-1,"Using subtable with exports in expression"); return false; } vn0 = recoverSize(op->getIn(0)->getSize(),ct); if (vn0 == -1) { printOpError(op,ct,0,0,"Using subtable with exports in expression"); return false; } if ((vnout==0)||(vn0==0)) return true; if (vnout == vn0) { dealWithUnnecessaryExt(op,ct); return true; } else if (vnout < vn0) { printOpError(op,ct,-1,0,"Output size must be strictly bigger than input size"); return false; } return true; case CPUI_CBRANCH: vn1 = recoverSize(op->getIn(1)->getSize(),ct); if (vn1 == -1) { printOpError(op,ct,1,1,"Using subtable with exports in expression"); return false; } if (vn1 != 1) { printOpError(op,ct,1,1,"Input must be a boolean (size 1)"); return false; } return true; case CPUI_LOAD: case CPUI_STORE: if (op->getIn(0)->getOffset().getType() != ConstTpl::spaceid) return true; spc = op->getIn(0)->getOffset().getSpace(); vn1 = recoverSize(op->getIn(1)->getSize(),ct); if (vn1 == -1) { printOpError(op,ct,1,1,"Using subtable with exports in expression"); return false; } if ((vn1!=0)&&(vn1 != spc->getAddrSize())) { printOpError(op,ct,1,1,"Pointer size must match size of space"); return false; } return true; case CPUI_SUBPIECE: vnout = recoverSize(op->getOut()->getSize(),ct); if (vnout == -1) { printOpError(op,ct,-1,-1,"Using subtable with exports in expression"); return false; } vn0 = recoverSize(op->getIn(0)->getSize(),ct); if (vn0 == -1) { printOpError(op,ct,0,0,"Using subtable with exports in expression"); return false; } vn1 = op->getIn(1)->getOffset().getReal(); if ((vnout==0)||(vn0==0)) return true; if ((vnout==vn0)&&(vn1==0)) { // No actual truncation is occuring dealWithUnnecessaryTrunc(op,ct); return true; } else if (vnout>=vn0) { printOpError(op,ct,-1,0,"Output must be strictly smaller than input"); return false; } if (vnout>vn0-vn1) { printOpError(op,ct,-1,0,"Too much truncation"); return false; } return true; default: break; } return true; } void ConsistencyChecker::printOpName(ostream &s,OpTpl *op) { switch(op->getOpcode()) { case CPUI_COPY: s << "Copy(=)"; break; case CPUI_LOAD: s << "Load(*)"; break; case CPUI_STORE: s << "Store(*)"; break; case CPUI_BRANCH: s << "Branch(goto)"; break; case CPUI_CBRANCH: s << "Conditional branch(if)"; break; case CPUI_BRANCHIND: s << "Indirect branch(goto[])"; break; case CPUI_CALL: s << "Call"; break; case CPUI_CALLIND: s << "Indirect Call"; break; case CPUI_CALLOTHER: s << "User defined"; break; case CPUI_RETURN: s << "Return"; break; case CPUI_INT_EQUAL: s << "Equality(==)"; break; case CPUI_INT_NOTEQUAL: s << "Notequal(!=)"; break; case CPUI_INT_SLESS: s << "Signed less than(s<)"; break; case CPUI_INT_SLESSEQUAL: s << "Signed less than or equal(s<=)"; break; case CPUI_INT_LESS: s << "Less than(<)"; break; case CPUI_INT_LESSEQUAL: s << "Less than or equal(<=)"; break; case CPUI_INT_ZEXT: s << "Zero extension(zext)"; break; case CPUI_INT_SEXT: s << "Signed extension(sext)"; break; case CPUI_INT_ADD: s << "Addition(+)"; break; case CPUI_INT_SUB: s << "Subtraction(-)"; break; case CPUI_INT_CARRY: s << "Carry"; break; case CPUI_INT_SCARRY: s << "Signed carry"; break; case CPUI_INT_SBORROW: s << "Signed borrow"; break; case CPUI_INT_2COMP: s << "Twos complement(-)"; break; case CPUI_INT_NEGATE: s << "Negate(~)"; break; case CPUI_INT_XOR: s << "Exclusive or(^)"; break; case CPUI_INT_AND: s << "And(&)"; break; case CPUI_INT_OR: s << "Or(|)"; break; case CPUI_INT_LEFT: s << "Left shift(<<)"; break; case CPUI_INT_RIGHT: s << "Right shift(>>)"; break; case CPUI_INT_SRIGHT: s << "Signed right shift(s>>)"; break; case CPUI_INT_MULT: s << "Multiplication(*)"; break; case CPUI_INT_DIV: s << "Division(/)"; break; case CPUI_INT_SDIV: s << "Signed division(s/)"; break; case CPUI_INT_REM: s << "Remainder(%)"; break; case CPUI_INT_SREM: s << "Signed remainder(s%)"; break; case CPUI_BOOL_NEGATE: s << "Boolean negate(!)"; break; case CPUI_BOOL_XOR: s << "Boolean xor(^^)"; break; case CPUI_BOOL_AND: s << "Boolean and(&&)"; break; case CPUI_BOOL_OR: s << "Boolean or(||)"; break; case CPUI_FLOAT_EQUAL: s << "Float equal(f==)"; break; case CPUI_FLOAT_NOTEQUAL: s << "Float notequal(f!=)"; break; case CPUI_FLOAT_LESS: s << "Float less than(f<)"; break; case CPUI_FLOAT_LESSEQUAL: s << "Float less than or equal(f<=)"; break; case CPUI_FLOAT_NAN: s << "Not a number(nan)"; break; case CPUI_FLOAT_ADD: s << "Float addition(f+)"; break; case CPUI_FLOAT_DIV: s << "Float division(f/)"; break; case CPUI_FLOAT_MULT: s << "Float multiplication(f*)"; break; case CPUI_FLOAT_SUB: s << "Float subtractions(f-)"; break; case CPUI_FLOAT_NEG: s << "Float minus(f-)"; break; case CPUI_FLOAT_ABS: s << "Absolute value(abs)"; break; case CPUI_FLOAT_SQRT: s << "Square root"; break; case CPUI_FLOAT_INT2FLOAT: s << "Integer to float conversion(int2float)"; break; case CPUI_FLOAT_FLOAT2FLOAT: s << "Float to float conversion(float2float)"; break; case CPUI_FLOAT_TRUNC: s << "Float truncation(trunc)"; break; case CPUI_FLOAT_CEIL: s << "Ceiling(ceil)"; break; case CPUI_FLOAT_FLOOR: s << "Floor"; break; case CPUI_FLOAT_ROUND: s << "Round"; break; case CPUI_MULTIEQUAL: s << "Build"; break; case CPUI_INDIRECT: s << "Delay"; break; case CPUI_SUBPIECE: s << "Truncation(:)"; break; case CPUI_SEGMENTOP: s << "Segment table(segment)"; break; case CPUI_CPOOLREF: s << "Constant Pool(cpool)"; break; case CPUI_NEW: s << "New object(newobject)"; break; default: break; } } OperandSymbol *ConsistencyChecker::getOperandSymbol(int4 slot,OpTpl *op,Constructor *ct) { VarnodeTpl *vn; OperandSymbol *opsym; int4 handindex; if (slot == -1) vn = op->getOut(); else vn = op->getIn(slot); switch(vn->getSize().getType()) { case ConstTpl::handle: handindex = vn->getSize().getHandleIndex(); opsym = ct->getOperand(handindex); break; default: opsym = (OperandSymbol *)0; break; } return opsym; } void ConsistencyChecker::printOpError(OpTpl *op,Constructor *ct,int4 err1,int4 err2,const string &msg) { SubtableSymbol *sym = ct->getParent(); OperandSymbol *op1,*op2; op1 = getOperandSymbol(err1,op,ct); if (err2 != err1) op2 = getOperandSymbol(err2,op,ct); else op2 = (OperandSymbol *)0; cerr << "Size restriction error in table \"" << sym->getName() << "\"" << endl; cerr << " in constructor starting at line " << dec << ct->getLineno() << endl; if ((op1 != (OperandSymbol *)0)&&(op2 != (OperandSymbol *)0)) { cerr << " Problem with \"" << op1->getName(); cerr << "\" and \"" << op2->getName() << "\""; } else if (op1 != (OperandSymbol *)0) cerr << " Problem with \"" << op1->getName() << "\""; else if (op2 != (OperandSymbol *)0) cerr << " Problem with \"" << op2->getName() << "\""; else cerr << " Problem"; cerr << " in "; printOpName(cerr,op); cerr << " operator" << endl << " " << msg << endl; } bool ConsistencyChecker::checkConstructorSection(Constructor *ct,ConstructTpl *cttpl) { // Check all the OpTpl s within the given section for consistency, return true if all tests pass if (cttpl == (ConstructTpl *)0) return true; // Nothing to check vector::const_iterator iter; const vector &ops(cttpl->getOpvec()); bool testresult = true; for(iter=ops.begin();iter!=ops.end();++iter) { if (!sizeRestriction(*iter,ct)) testresult = false; if (!checkOpMisuse(*iter,ct)) testresult = false; } return testresult; } bool ConsistencyChecker::checkVarnodeTruncation(Constructor *ct,int4 slot, OpTpl *op,VarnodeTpl *vn,bool isbigendian) { const ConstTpl &off( vn->getOffset() ); if (off.getType() != ConstTpl::handle) return true; if (off.getSelect() != ConstTpl::v_offset_plus) return true; ConstTpl::const_type sztype = vn->getSize().getType(); if ((sztype != ConstTpl::real)&&(sztype != ConstTpl::handle)) { printOpError(op,ct,slot,slot,"Bad truncation expression"); return false; } int4 sz = recoverSize(off,ct); // Recover the size of the original operand if (sz <= 0) { printOpError(op,ct,slot,slot,"Could not recover size"); return false; } bool res = vn->adjustTruncation(sz,isbigendian); if (!res) { printOpError(op,ct,slot,slot,"Truncation operator out of bounds"); return false; } return true; } bool ConsistencyChecker::checkSectionTruncations(Constructor *ct,ConstructTpl *cttpl,bool isbigendian) { // Check all the varnodes that have an offset_plus template // adjust the plus if we are bigendian // make sure the truncation is valid vector::const_iterator iter; const vector &ops(cttpl->getOpvec()); bool testresult = true; for(iter=ops.begin();iter!=ops.end();++iter) { OpTpl *op = *iter; VarnodeTpl *outvn = op->getOut(); if (outvn != (VarnodeTpl *)0) { if (!checkVarnodeTruncation(ct,-1,op,outvn,isbigendian)) testresult = false; } for(int4 i=0;inumInput();++i) { if (!checkVarnodeTruncation(ct,i,op,op->getIn(i),isbigendian)) testresult = false; } } return testresult; } bool ConsistencyChecker::checkSubtable(SubtableSymbol *sym) { int4 tablesize = 0; int4 numconstruct = sym->getNumConstructors(); Constructor *ct; bool testresult = true; bool seenemptyexport = false; bool seennonemptyexport = false; for(int4 i=0;igetConstructor(i); if (!checkConstructorSection(ct,ct->getTempl())) testresult = false; int4 numsection = ct->getNumSections(); for(int4 j=0;jgetNamedTempl(j))) testresult = false; } if (ct->getTempl() == (ConstructTpl *)0) continue; // Unimplemented HandleTpl *exportres = ct->getTempl()->getResult(); if (exportres != (HandleTpl *)0) { if (seenemptyexport && (!seennonemptyexport)) { cerr << "Table " << sym->getName() << " exports inconsistently" << endl; cerr << "Constructor starting at line " << dec << ct->getLineno() << " is first inconsistency" << endl; testresult = false; } seennonemptyexport = true; int4 exsize = recoverSize(exportres->getSize(),ct); if (tablesize == 0) tablesize = exsize; if ((exsize!=0)&&(exsize != tablesize)) { cerr << "Table " << sym->getName() << " has inconsistent export size." << endl; cerr << "Constructor starting at line " << dec << ct->getLineno() << " is first conflict" << endl; testresult = false; } } else { if (seennonemptyexport && (!seenemptyexport)) { cerr << "Table " << sym->getName() << " exports inconsistently" << endl; cerr << "Constructor starting at line " << dec << ct->getLineno() << " is first inconsistency" << endl; testresult = false; } seenemptyexport = true; } } if (seennonemptyexport) { if (tablesize == 0) cerr << "Warning: Table " << sym->getName() << " exports size 0" << endl; sizemap[sym] = tablesize; // Remember recovered size } else sizemap[sym] = -1; return testresult; } void ConsistencyChecker::setPostOrder(SubtableSymbol *root) { postorder.clear(); sizemap.clear(); // Establish post-order of SubtableSymbols so that we can // recursively fill in sizes of varnodes which are exported // from constructors vector path; vector state; vector ctstate; sizemap[root] = -1; // Mark root as traversed path.push_back(root); state.push_back(0); ctstate.push_back(0); while(!path.empty()) { SubtableSymbol *cur = path.back(); int4 ctind = state.back(); if (ctind >= cur->getNumConstructors()) { path.pop_back(); // Table is fully traversed state.pop_back(); ctstate.pop_back(); postorder.push_back(cur); // Post the traversed table } else { Constructor *ct = cur->getConstructor(ctind); int4 oper = ctstate.back(); if (oper >= ct->getNumOperands()) { state.back() = ctind + 1; // Constructor fully traversed ctstate.back() = 0; } else { ctstate.back() = oper + 1; OperandSymbol *opsym = ct->getOperand(oper); SubtableSymbol *subsym = dynamic_cast(opsym->getDefiningSymbol()); if (subsym != (SubtableSymbol *)0) { map::const_iterator iter; iter = sizemap.find(subsym); if (iter == sizemap.end()) { // Not traversed yet sizemap[subsym] = -1; // Mark table as traversed path.push_back(subsym); // Recurse state.push_back(0); ctstate.push_back(0); } } } } } } bool ConsistencyChecker::possibleIntersection(const VarnodeTpl *vn1,const VarnodeTpl *vn2) { // Conservatively test whether vn1 and vn2 can intersect if (vn1->getSpace().isConstSpace()) return false; if (vn2->getSpace().isConstSpace()) return false; bool u1 = vn1->getSpace().isUniqueSpace(); bool u2 = vn2->getSpace().isUniqueSpace(); if (u1 != u2) return false; if (vn1->getSpace().getType() != ConstTpl::spaceid) return true; if (vn2->getSpace().getType() != ConstTpl::spaceid) return true; AddrSpace *spc = vn1->getSpace().getSpace(); if (spc != vn2->getSpace().getSpace()) return false; if (vn2->getOffset().getType() != ConstTpl::real) return true; if (vn2->getSize().getType() != ConstTpl::real) return true; if (vn1->getOffset().getType() != ConstTpl::real) return true; if (vn1->getSize().getType() != ConstTpl::real) return true; uintb offset = vn1->getOffset().getReal(); uintb size = vn1->getSize().getReal(); uintb off = vn2->getOffset().getReal(); if (off+vn2->getSize().getReal()-1 < offset) return false; if (off > (offset+size-1)) return false; return true; } bool ConsistencyChecker::readWriteInterference(const VarnodeTpl *vn,const OpTpl *op,bool checkread) const { // Does op potentially read vn // This is extremely conservative. Basically any op where // we can't see exactly what might be written is considered // interference switch(op->getOpcode()) { case BUILD: case CROSSBUILD: case DELAY_SLOT: case MACROBUILD: case CPUI_LOAD: case CPUI_STORE: case CPUI_BRANCH: case CPUI_CBRANCH: case CPUI_BRANCHIND: case CPUI_CALL: case CPUI_CALLIND: case CPUI_CALLOTHER: case CPUI_RETURN: case LABELBUILD: // Another value might jump in here return true; default: break; } if (checkread) { int4 numinputs = op->numInput(); for(int4 i=0;igetIn(i))) return true; } // We always check for writes to -vn- const VarnodeTpl *vn2 = op->getOut(); if (vn2 != (const VarnodeTpl *)0) { if (possibleIntersection(vn,vn2)) return true; } return false; } void ConsistencyChecker::examineVn(map &recs, const VarnodeTpl *vn,uint4 i,int4 inslot,int4 secnum) { // If varnode is a temporary, count whether it is read or written if (vn == (const VarnodeTpl *)0) return; if (!vn->getSpace().isUniqueSpace()) return; if (vn->getOffset().getType() != ConstTpl::real) return; map::iterator iter; iter = recs.insert( pair(vn->getOffset().getReal(),OptimizeRecord())).first; if (inslot>=0) { (*iter).second.readop = i; (*iter).second.readcount += 1; (*iter).second.inslot = inslot; (*iter).second.readsection = secnum; } else { (*iter).second.writeop = i; (*iter).second.writecount += 1; (*iter).second.writesection = secnum; } } void ConsistencyChecker::optimizeGather1(Constructor *ct,map &recs,int4 secnum) const { // Look for reads and writes to temporaries, count how many times each temporary is read or written ConstructTpl *tpl; if (secnum < 0) tpl = ct->getTempl(); else tpl = ct->getNamedTempl(secnum); if (tpl == (ConstructTpl *)0) return; const vector &ops( tpl->getOpvec() ); for(uint4 i=0;inumInput();++j) { const VarnodeTpl *vnin = op->getIn(j); examineVn(recs,vnin,i,j,secnum); } const VarnodeTpl *vn = op->getOut(); examineVn(recs,vn,i,-1,secnum); } } void ConsistencyChecker::optimizeGather2(Constructor *ct,map &recs,int4 secnum) const { // Make sure any temp used by the export is not optimized away ConstructTpl *tpl; if (secnum < 0) tpl = ct->getTempl(); else tpl = ct->getNamedTempl(secnum); if (tpl == (ConstructTpl *)0) return; HandleTpl *hand = tpl->getResult(); if (hand == (HandleTpl *)0) return; if (hand->getPtrSpace().isUniqueSpace()) { if (hand->getPtrOffset().getType() == ConstTpl::real) { pair::iterator,bool> res; uintb offset = hand->getPtrOffset().getReal(); res = recs.insert( pair(offset,OptimizeRecord())); (*res.first).second.writeop = 0; (*res.first).second.readop = 0; (*res.first).second.writecount = 2; (*res.first).second.readcount = 2; (*res.first).second.readsection = -2; (*res.first).second.writesection = -2; } } if (hand->getSpace().isUniqueSpace()) { if ((hand->getPtrSpace().getType() == ConstTpl::real)&& (hand->getPtrOffset().getType() == ConstTpl::real)) { pair::iterator,bool> res; uintb offset = hand->getPtrOffset().getReal(); res = recs.insert( pair(offset,OptimizeRecord())); (*res.first).second.writeop = 0; (*res.first).second.readop = 0; (*res.first).second.writecount = 2; (*res.first).second.readcount = 2; (*res.first).second.readsection = -2; (*res.first).second.writesection = -2; } } } ConsistencyChecker::OptimizeRecord *ConsistencyChecker::findValidRule(Constructor *ct,map &recs) const { map::iterator iter; iter = recs.begin(); while(iter != recs.end()) { OptimizeRecord &currec( (*iter).second ); ++iter; if ((currec.writecount==1)&&(currec.readcount==1)&&(currec.readsection==currec.writesection)) { // Temporary must be read and written exactly once ConstructTpl *tpl; if (currec.readsection < 0) tpl = ct->getTempl(); else tpl = ct->getNamedTempl(currec.readsection); const vector &ops( tpl->getOpvec() ); const OpTpl *op = ops[ currec.readop ]; if (currec.writeop >= currec.readop) // Read must come after write throw SleighError("Read of temporary before write"); if (op->getOpcode() == CPUI_COPY) { bool saverecord = true; currec.opttype = 0; // Read op is a COPY const VarnodeTpl *vn = op->getOut(); for(int4 i=currec.writeop+1;igetOpcode() == CPUI_COPY) { bool saverecord = true; currec.opttype = 1; // Write op is a COPY const VarnodeTpl *vn = op->getIn(0); for(int4 i=currec.writeop+1;i deleteops; ConstructTpl *ctempl; if (rec.readsection < 0) ctempl = ct->getTempl(); else ctempl = ct->getNamedTempl(rec.readsection); if (rec.opttype == 0) { // If read op is COPY int4 readop = rec.readop; OpTpl *op = ctempl->getOpvec()[ readop ]; VarnodeTpl *vnout = new VarnodeTpl(*op->getOut()); // Make COPY output ctempl->setOutput(vnout,rec.writeop); // become write output deleteops.push_back(readop); // and then delete the read (COPY) } else if (rec.opttype == 1) { // If write op is COPY int4 writeop = rec.writeop; OpTpl *op = ctempl->getOpvec()[ writeop ]; VarnodeTpl *vnin = new VarnodeTpl(*op->getIn(0)); // Make COPY input ctempl->setInput(vnin,rec.readop,rec.inslot); // become read input deleteops.push_back(writeop); // and then delete the write (COPY) } ctempl->deleteOps(deleteops); } void ConsistencyChecker::checkUnusedTemps(Constructor *ct,const map &recs) { map::const_iterator iter; iter = recs.begin(); while(iter != recs.end()) { const OptimizeRecord &currec( (*iter).second ); if (currec.readcount == 0) { if (printdeadwarning) cerr << "Warning: temporary is written but not read in constructor starting at line " << dec << ct->getLineno() << endl; writenoread += 1; } else if (currec.writecount == 0) { cerr << "Error: temporary is read but not written in constructor starting at line " << dec << ct->getLineno() << endl; readnowrite += 1; } ++iter; } } void ConsistencyChecker::optimize(Constructor *ct) { OptimizeRecord *currec; map recs; int4 numsections = ct->getNumSections(); do { recs.clear(); for(int4 i=-1;igetNumConstructors(); Constructor *ct; for(int4 j=0;jgetConstructor(j); int4 numsections = ct->getNumSections(); for(int4 k=-1;kgetTempl(); else tpl = ct->getNamedTempl(k); if (tpl == (ConstructTpl *)0) continue; if (!checkSectionTruncations(ct,tpl,isbigendian)) testresult = false; } } } return testresult; } void ConsistencyChecker::optimizeAll(void) { for(int4 i=0;igetNumConstructors(); Constructor *ct; for(int4 i=0;igetConstructor(i); optimize(ct); } } } bool FieldContext::operator<(const FieldContext &op2) const { if (sym->getName() != op2.sym->getName()) return (sym->getName() < op2.sym->getName()); return (qual->low < op2.qual->low); } void MacroBuilder::free(void) { vector::iterator iter; for(iter=params.begin();iter!=params.end();++iter) delete *iter; params.clear(); } void MacroBuilder::reportError(const string &val) { slgh->reportError(val,false); haserror = true; } void MacroBuilder::setMacroOp(OpTpl *macroop) { // Set up parameters for a particular macro invocation VarnodeTpl *vn; HandleTpl *hand; free(); for(int4 i=1;inumInput();++i) { vn = macroop->getIn(i); hand = new HandleTpl(vn); params.push_back(hand); } } bool MacroBuilder::transferOp(OpTpl *op,vector ¶ms) { // Fix handle details of a macro generated OpTpl relative to its specific invocation // and transfer it into the output stream VarnodeTpl *outvn = op->getOut(); int4 handleIndex = 0; int4 plus; bool hasrealsize = false; uintb realsize = 0; if (outvn != (VarnodeTpl *)0) { plus = outvn->transfer(params); if (plus >= 0) { reportError("Cannot currently assign to bitrange of macro parameter that is a temporary"); return false; } } for(int4 i=0;inumInput();++i) { VarnodeTpl *vn = op->getIn(i); if (vn->getOffset().getType() == ConstTpl::handle) { handleIndex = vn->getOffset().getHandleIndex(); hasrealsize = (vn->getSize().getType() == ConstTpl::real); realsize = vn->getSize().getReal(); } plus = vn->transfer(params); if (plus >= 0) { if (!hasrealsize) { reportError("Problem with bit range operator in macro"); return false; } uintb newtemp = slgh->getUniqueAddr(); // Generate a new temporary location // Generate a SUBPIECE op that implements the offset_plus OpTpl *subpieceop = new OpTpl(CPUI_SUBPIECE); VarnodeTpl *newvn = new VarnodeTpl(ConstTpl(slgh->getUniqueSpace()),ConstTpl(ConstTpl::real,newtemp), ConstTpl(ConstTpl::real,realsize)); subpieceop->setOutput(newvn); HandleTpl *hand = params[handleIndex]; VarnodeTpl *origvn = new VarnodeTpl( hand->getSpace(), hand->getPtrOffset(), hand->getSize() ); subpieceop->addInput(origvn); VarnodeTpl *plusvn = new VarnodeTpl( ConstTpl(slgh->getConstantSpace()), ConstTpl(ConstTpl::real,plus), ConstTpl(ConstTpl::real, 4) ); subpieceop->addInput(plusvn); outvec.push_back(subpieceop); delete vn; // Replace original varnode op->setInput(new VarnodeTpl( *newvn ), i); // with output of subpiece } } outvec.push_back(op); return true; } void MacroBuilder::dump(OpTpl *op) { OpTpl *clone; VarnodeTpl *v_clone,*vn; clone = new OpTpl(op->getOpcode()); vn = op->getOut(); if (vn != (VarnodeTpl *)0) { v_clone = new VarnodeTpl(*vn); clone->setOutput(v_clone); } for(int4 i=0;inumInput();++i) { vn = op->getIn(i); v_clone = new VarnodeTpl(*vn); if (v_clone->isRelative()) { // Adjust relative index, depending on the labelbase uintb val = v_clone->getOffset().getReal() + getLabelBase(); v_clone->setRelative(val); } clone->addInput(v_clone); } if (!transferOp(clone,params)) delete clone; } void MacroBuilder::setLabel(OpTpl *op) { // A label within a macro is local to the macro, but when // we expand the macro, we have to adjust the index of // the label, which is local to the macro, so that it fits // in with other labels local to the parent OpTpl *clone; VarnodeTpl *v_clone; clone = new OpTpl(op->getOpcode()); v_clone = new VarnodeTpl( *op->getIn(0) ); // Clone the label index // Make adjustment to macro local value so that it is parent local uintb val = v_clone->getOffset().getReal() + getLabelBase(); v_clone->setOffset(val); clone->addInput(v_clone); outvec.push_back(clone); } uintb SleighPcode::allocateTemp(void) { return compiler->getUniqueAddr(); } void SleighPcode::reportError(const string &msg) { return compiler->reportError(msg,true); } void SleighPcode::addSymbol(SleighSymbol *sym) { return compiler->addSymbol(sym); } SleighCompile::SleighCompile(void) : SleighBase() { pcode.setCompiler(this); contextlock = false; // Context layout is not locked userop_count = 0; errors = 0; warnunnecessarypcode = false; warndeadtemps = false; lenientconflicterrors = true; warnalllocalcollisions = false; warnallnops = false; root = (SubtableSymbol *)0; } void SleighCompile::predefinedSymbols(void) { // Define the "pre" defined spaces and symbols // This must happen after endian has been defined symtab.addScope(); // Create global scope // Some predefined symbols root = new SubtableSymbol("instruction"); // Base constructors symtab.addSymbol(root); insertSpace(new ConstantSpace(this,this,"const",AddrSpace::constant_space_index)); SpaceSymbol *spacesym = new SpaceSymbol(getConstantSpace()); // Constant space symtab.addSymbol(spacesym); OtherSpace *otherSpace = new OtherSpace(this,this,"OTHER",AddrSpace::other_space_index); insertSpace(otherSpace); spacesym = new SpaceSymbol(otherSpace); symtab.addSymbol(spacesym); insertSpace(new UniqueSpace(this,this,"unique",numSpaces(),0)); spacesym = new SpaceSymbol(getUniqueSpace()); // Temporary register space symtab.addSymbol(spacesym); StartSymbol *startsym = new StartSymbol("inst_start",getConstantSpace()); symtab.addSymbol(startsym); EndSymbol *endsym = new EndSymbol("inst_next",getConstantSpace()); symtab.addSymbol(endsym); EpsilonSymbol *epsilon = new EpsilonSymbol("epsilon",getConstantSpace()); symtab.addSymbol(epsilon); pcode.setConstantSpace(getConstantSpace()); pcode.setUniqueSpace(getUniqueSpace()); } int4 SleighCompile::calcContextVarLayout(int4 start,int4 sz,int4 numbits) { VarnodeSymbol *sym = contexttable[start].sym; FieldQuality *qual; int4 i,j; int4 maxbits; if ((sym->getSize()) % 4 != 0) reportError("Invalid size of context register: "+sym->getName()+" : must be a multiple of 4 bytes",false); maxbits = sym->getSize() * 8 -1; i = 0; while(ilow; int4 max = qual->high; if ((max - min) > (8*sizeof(uintm))) reportError("Size of bitfield " + qual->name + " larger than 32-bits",false); if (max > maxbits) reportError("Scope of bitfield " + qual->name + " extends beyond the size of context register",false); j = i+1; // Find union of fields overlapping with first field while(jlow <= max) { // We have overlap of context variables if (qual->high > max) max = qual->high; // reportWarning("Local context variables overlap in "+sym->getName(),false); } else break; j = j+1; } int4 alloc = max-min+1; int4 startword = numbits / (8*sizeof(uintm)); int4 endword = (numbits+alloc-1) / (8*sizeof(uintm)); if (startword != endword) numbits = endword * (8*sizeof(uintm)); // Bump up to next word uint4 low = numbits; numbits += alloc; for(;ilow - min + low; uint4 h = numbits-1-(max-qual->high); ContextField *field = new ContextField(qual->signext,l,h); addSymbol(new ContextSymbol(qual->name,field,sym,qual->low,qual->high,qual->flow)); } } sym->markAsContext(); return numbits; } void SleighCompile::buildDecisionTrees(void) { DecisionProperties props; root->buildDecisionTree(props); for(int4 i=0;ibuildDecisionTree(props); const vector &ierrors( props.getIdentErrors() ); for(int4 i=0;i &cerrors( props.getConflictErrors() ); for(int4 i=0;ibuildPattern(cerr); // This should recursively hit everything if (root->isError()) errors += 1; for(int4 i=0;iisError()) errors += 1; if (tables[i]->getPattern() == (TokenPattern *)0) reportWarning("Unreferenced table: "+tables[i]->getName(),false); } } void SleighCompile::checkConsistency(void) { ConsistencyChecker checker(root,warnunnecessarypcode,warndeadtemps); if (!checker.test()) { errors += 1; return; } if (!checker.testTruncations(isBigEndian())) { errors += 1; return; } if ((!warnunnecessarypcode)&&(checker.getNumUnnecessaryPcode() > 0)) { cerr << dec << checker.getNumUnnecessaryPcode(); cerr << " unnecessary extensions/truncations were converted to copies" << endl; cerr << "Use -u switch to list each individually" << endl; } checker.optimizeAll(); if (checker.getNumReadNoWrite() > 0) { errors += 1; return; } if ((!warndeadtemps)&&(checker.getNumWriteNoRead() > 0)) { cerr << dec << checker.getNumWriteNoRead(); cerr << " operations wrote to temporaries that were not read" << endl; cerr << "Use -t switch to list each individually" << endl; } } int4 SleighCompile::findCollision(map &local2Operand,const vector &locals,int operand) { for(int4 i=0;i::iterator,bool> res; res = local2Operand.insert(pair(locals[i],operand)); if (!res.second) { int4 oldIndex = (*res.first).second; if (oldIndex != operand) return oldIndex; } } return -1; } bool SleighCompile::checkLocalExports(Constructor *ct) { if (ct->getTempl() == (ConstructTpl *)0) return true; // No template, collisions impossible if (ct->getTempl()->buildOnly()) return true; // Operand exports aren't manipulated, so no collision is possible if (ct->getNumOperands() < 2) return true; // Collision can only happen with multiple operands bool noCollisions = true; map collect; for(int4 i=0;igetNumOperands();++i) { vector newCollect; ct->getOperand(i)->collectLocalValues(newCollect); if (newCollect.empty()) continue; int4 collideOperand = findCollision(collect, newCollect, i); if (collideOperand >= 0) { noCollisions = false; if (warnalllocalcollisions) { cerr << "Possible collision with symbols "; cerr << ct->getOperand(collideOperand)->getName(); cerr << " and " << ct->getOperand(i)->getName(); cerr << " in constructor starting at line " << dec << ct->getLineno() << endl; } break; // Don't continue } } return noCollisions; } void SleighCompile::checkLocalCollisions(void) { int4 collisionCount = 0; SubtableSymbol *sym = root; // Start with the instruction table int4 i = -1; for(;;) { int4 numconst = sym->getNumConstructors(); for(int4 j=0;jgetConstructor(j))) collisionCount += 1; } i+=1; if (i>=tables.size()) break; sym = tables[i]; } if (collisionCount > 0) { cerr << "WARNING: " << dec << collisionCount << " constructors with local collisions between operands" << endl; if (!warnalllocalcollisions) cerr << "Use -c switch to list each individually" << endl; } } void SleighCompile::checkNops(void) { if (noplist.size() > 0) { if (warnallnops) { for(int4 i=0;ibegin();iter!=scope->end();++iter) { LabelSymbol *sym = (LabelSymbol *)*iter; if (sym->getType() != SleighSymbol::label_symbol) continue; if (sym->getRefCount() == 0) s << " Label <" << sym->getName() << "> was placed but not used\n"; else if (!sym->isPlaced()) s << " Label <" << sym->getName() << "> was referenced but never placed\n"; } return s.str(); } void SleighCompile::addSymbol(SleighSymbol *sym) { // Make sure symbol table errors are caught try { symtab.addSymbol(sym); } catch(SleighError &err) { reportError(err.explain,true); } } void SleighCompile::reportError(const string &msg,bool includeline) { cerr << "Error in " << filename.back() << ' '; if (includeline) cerr << "at line " << dec << lineno.back() << ": "; cerr << msg << endl; errors += 1; if (errors >50) { cerr << "Too many errors: Aborting" << endl; exit(2); } } void SleighCompile::reportWarning(const string &msg,bool includeline) { cerr << "Warning in " << filename.back() << ' '; if (includeline) cerr << "at line " << dec << lineno.back() << ": "; cerr << msg << endl; } uintb SleighCompile::getUniqueAddr(void) { uintb base = getUniqueBase(); setUniqueBase(base + 16); // Should be maximum size of a unique return base; } void SleighCompile::process(void) { // Do all post processing on the parsed data structures checkNops(); if (getDefaultSpace() == (AddrSpace *)0) reportError("No default space specified",false); if (errors>0) return; checkConsistency(); if (errors>0) return; checkLocalCollisions(); if (errors>0) return; buildPatterns(); if (errors>0) return; buildDecisionTrees(); if (errors>0) return; try { buildXrefs(); // Make sure we can build crossrefs properly } catch(SleighError &err) { cerr << err.explain << endl; errors += 1; return; } checkUniqueAllocation(); symtab.purge(); // Get rid of any symbols we don't plan to save } // Methods needed by the lexer void SleighCompile::calcContextLayout(void) { if (contextlock) return; // Already locked contextlock = true; int4 context_offset = 0; int4 begin,sz; stable_sort(contexttable.begin(),contexttable.end()); begin = 0; while(begin < contexttable.size()) { // Define the context variables sz = 1; while ((begin+sz < contexttable.size())&&(contexttable[begin+sz].sym==contexttable[begin].sym)) sz += 1; context_offset = calcContextVarLayout(begin,sz,context_offset); begin += sz; } // context_size = (context_offset+8*sizeof(uintm)-1)/(8*sizeof(uintm)); // Delete the quals for(int4 i=0;i::const_iterator iter = preproc_defines.find(nm); if (iter == preproc_defines.end()) return false; res = (*iter).second; return true; } void SleighCompile::setPreprocValue(const string &nm,const string &value) { preproc_defines[nm] = value; } bool SleighCompile::undefinePreprocValue(const string &nm) { map::iterator iter = preproc_defines.find(nm); if (iter==preproc_defines.end()) return false; preproc_defines.erase(iter); return true; } // Functions needed by the parser TokenSymbol *SleighCompile::defineToken(string *name,uintb *sz) { uint4 size = *sz; delete sz; if ((size&7)!=0) { reportError(*name+"token size must be multiple of 8",true); size = (size/8)+1; } else size = size/8; Token *newtoken = new Token(*name,size,isBigEndian(),tokentable.size()); tokentable.push_back(newtoken); delete name; TokenSymbol *res = new TokenSymbol(newtoken); addSymbol(res); return res; } void SleighCompile::addTokenField(TokenSymbol *sym,FieldQuality *qual) { TokenField *field = new TokenField(sym->getToken(),qual->signext,qual->low,qual->high); addSymbol(new ValueSymbol(qual->name,field)); delete qual; } bool SleighCompile::addContextField(VarnodeSymbol *sym,FieldQuality *qual) { if (contextlock) return false; // Context layout has already been satisfied contexttable.push_back(FieldContext(sym,qual)); return true; } void SleighCompile::newSpace(SpaceQuality *qual) { if (qual->size == 0) { reportError("Space definition missing size attribute",true); delete qual; return; } int4 delay = (qual->type == SpaceQuality::registertype) ? 0 : 1; AddrSpace *spc = new AddrSpace(this,this,IPTR_PROCESSOR,qual->name,qual->size,qual->wordsize,numSpaces(),AddrSpace::hasphysical,delay); insertSpace(spc); if (qual->isdefault) { if (getDefaultSpace() != (AddrSpace *)0) reportError("Multiple default spaces",true); else { setDefaultSpace(spc->getIndex()); // Make the flagged space the default pcode.setDefaultSpace(spc); } } delete qual; addSymbol( new SpaceSymbol(spc) ); } SectionSymbol *SleighCompile::newSectionSymbol(const string &nm) { SectionSymbol *sym = new SectionSymbol(nm,sections.size()); try { symtab.addGlobalSymbol(sym); } catch(SleighError &err) { reportError(err.explain,true); } sections.push_back(sym); numSections = sections.size(); return sym; } void SleighCompile::setEndian(int4 end) { // This MUST be called at the very beginning of the parse // The parser should enforce this setBigEndian( (end == 1) ); predefinedSymbols(); // Set up symbols now that we know endianess } void SleighCompile::defineVarnodes(SpaceSymbol *spacesym,uintb *off,uintb *size,vector *names) { AddrSpace *spc = spacesym->getSpace(); uintb myoff = *off; for(int4 i=0;isize();++i) { if ((*names)[i] != "_") addSymbol( new VarnodeSymbol((*names)[i],spc,myoff,*size) ); myoff += *size; } delete names; delete off; delete size; } void SleighCompile::defineBitrange(string *name,VarnodeSymbol *sym,uint4 bitoffset,uint4 numb) { // Define a new symbol as a subrange of bits within another symbol // If the ends of the range fall on byte boundaries, we // simply define a normal VarnodeSymbol, otherwise we create // a special symbol which is a place holder for the bitrange operator string namecopy = *name; delete name; uint4 size = 8*sym->getSize(); // Number of bits if (numb == 0) { reportError("Size of bitrange is zero for: "+namecopy,true); return; } if ((bitoffset >= size)||((bitoffset+numb)>size)) { reportError("Bad bitrange for: "+namecopy,true); return; } if ((bitoffset%8 == 0)&&(numb%8 == 0)) { // This can be reduced to an ordinary varnode definition AddrSpace *newspace = sym->getFixedVarnode().space; uintb newoffset = sym->getFixedVarnode().offset; int4 newsize = numb/8; if (isBigEndian()) newoffset += (size-bitoffset-numb)/8; else newoffset += bitoffset/8; addSymbol( new VarnodeSymbol(namecopy,newspace,newoffset,newsize) ); } else // Otherwise define the special symbol addSymbol( new BitrangeSymbol(namecopy,sym,bitoffset,numb) ); } void SleighCompile::addUserOp(vector *names) { for(int4 i=0;isize();++i) { UserOpSymbol *sym = new UserOpSymbol((*names)[i]); sym->setIndex(userop_count++); addSymbol( sym ); } delete names; } SleighSymbol *SleighCompile::dedupSymbolList(vector *symlist) { // Find duplicates in -symlist-, null out all but first SleighSymbol *res = (SleighSymbol *)0; for(int4 i=0;isize();++i) { SleighSymbol *sym = (*symlist)[i]; if (sym == (SleighSymbol *)0) continue; for(int4 j=i+1;jsize();++j) { if ((*symlist)[j] == sym) { // Found a duplicate res = sym; // Return example duplicate for error reporting (*symlist)[j] = (SleighSymbol *)0; // Null out the duplicate } } } return res; } void SleighCompile::attachValues(vector *symlist,vector *numlist) { SleighSymbol *dupsym = dedupSymbolList(symlist); if (dupsym != (SleighSymbol *)0) reportWarning("\"attach values\" list contains duplicate entries: "+dupsym->getName(),true); for(int4 i=0;isize();++i) { ValueSymbol *sym = (ValueSymbol *)(*symlist)[i]; if (sym == (ValueSymbol *)0) continue; PatternValue *patval = sym->getPatternValue(); if (patval->maxValue() + 1 != numlist->size()) { reportError("Attach value " + sym->getName() + " is wrong size for list", true); } symtab.replaceSymbol(sym, new ValueMapSymbol(sym->getName(),patval,*numlist)); } delete numlist; delete symlist; } void SleighCompile::attachNames(vector *symlist,vector *names) { SleighSymbol *dupsym = dedupSymbolList(symlist); if (dupsym != (SleighSymbol *)0) reportWarning("\"attach names\" list contains duplicate entries: "+dupsym->getName(),true); for(int4 i=0;isize();++i) { ValueSymbol *sym = (ValueSymbol *)(*symlist)[i]; if (sym == (ValueSymbol *)0) continue; PatternValue *patval = sym->getPatternValue(); if (patval->maxValue() + 1 != names->size()) { reportError("Attach name " + sym->getName() + " is wrong size for list", true); } symtab.replaceSymbol(sym,new NameSymbol(sym->getName(),patval,*names)); } delete names; delete symlist; } void SleighCompile::attachVarnodes(vector *symlist,vector *varlist) { SleighSymbol *dupsym = dedupSymbolList(symlist); if (dupsym != (SleighSymbol *)0) reportWarning("\"attach variables\" list contains duplicate entries: "+dupsym->getName(),true); for(int4 i=0;isize();++i) { ValueSymbol *sym = (ValueSymbol *)(*symlist)[i]; if (sym == (ValueSymbol *)0) continue; PatternValue *patval = sym->getPatternValue(); if (patval->maxValue() + 1 != varlist->size()) { reportError("Attach varnode " + sym->getName() + " is wrong size for list", true); } int4 sz = 0; for(int4 j=0;jsize();++j) { VarnodeSymbol *vsym = (VarnodeSymbol *)(*varlist)[j]; if (vsym != (VarnodeSymbol *)0) { if (sz == 0) sz = vsym->getFixedVarnode().size; else if (sz != vsym->getFixedVarnode().size) { reportError("Attach statement contains varnodes of different sizes",true); break; } } } symtab.replaceSymbol(sym,new VarnodeListSymbol(sym->getName(),patval,*varlist)); } delete varlist; delete symlist; } SubtableSymbol *SleighCompile::newTable(string *nm) { SubtableSymbol *sym = new SubtableSymbol(*nm); addSymbol(sym); tables.push_back(sym); delete nm; return sym; } void SleighCompile::newOperand(Constructor *ct,string *nm) { int4 index = ct->getNumOperands(); OperandSymbol *sym = new OperandSymbol(*nm,index,ct); addSymbol(sym); ct->addOperand(sym); delete nm; } PatternEquation *SleighCompile::constrainOperand(OperandSymbol *sym,PatternExpression *patexp) { // Create constraint on operand PatternEquation *res; FamilySymbol *famsym = dynamic_cast(sym->getDefiningSymbol()); if (famsym != (FamilySymbol *)0) { // Operand already defined as family symbol // This equation must be a constraint res = new EqualEquation(famsym->getPatternValue(),patexp); } else { // Operand is currently undefined, so we can't constrain PatternExpression::release(patexp); res = (PatternEquation *)0; } return res; } void SleighCompile::defineOperand(OperandSymbol *sym,PatternExpression *patexp) { // Define operand in terms of PatternExpression try { sym->defineOperand(patexp); sym->setOffsetIrrelevant(); // If not a self-definition, the operand has no // pattern directly associated with it, so // the operand's offset is irrelevant } catch(SleighError &err) { reportError(err.explain,true); PatternExpression::release(patexp); } } PatternEquation *SleighCompile::defineInvisibleOperand(TripleSymbol *sym) { int4 index = curct->getNumOperands(); OperandSymbol *opsym = new OperandSymbol(sym->getName(),index,curct); addSymbol(opsym); curct->addInvisibleOperand(opsym); PatternEquation *res = new OperandEquation(opsym->getIndex()); SleighSymbol::symbol_type tp = sym->getType(); try { if ((tp==SleighSymbol::value_symbol)||(tp==SleighSymbol::context_symbol)) { opsym->defineOperand(sym->getPatternExpression()); } else { opsym->defineOperand(sym); // reportWarning("Defining invisible operand "+sym->getName(),true); } } catch(SleighError &err) { reportError(err.explain,true); } return res; } void SleighCompile::selfDefine(OperandSymbol *sym) { // Define operand as global symbol of same name TripleSymbol *glob = dynamic_cast(symtab.findSymbol(sym->getName(),1)); if (glob == (TripleSymbol *)0) { reportError(sym->getName()+": No matching global symbol",true); return; } SleighSymbol::symbol_type tp = glob->getType(); try { if ((tp==SleighSymbol::value_symbol)||(tp==SleighSymbol::context_symbol)) { sym->defineOperand(glob->getPatternExpression()); } else sym->defineOperand(glob); } catch(SleighError &err) { reportError(err.explain,true); } } ConstructTpl *SleighCompile::setResultVarnode(ConstructTpl *ct,VarnodeTpl *vn) { // Set constructors handle to indicate given varnode HandleTpl *res = new HandleTpl(vn); delete vn; ct->setResult(res); return ct; } ConstructTpl *SleighCompile::setResultStarVarnode(ConstructTpl *ct,StarQuality *star,VarnodeTpl *vn) { // Set constructors handle to be the value pointed // at by -vn- HandleTpl *res = new HandleTpl(star->id,ConstTpl(ConstTpl::real,star->size),vn, getUniqueSpace(),getUniqueAddr()); delete star; delete vn; ct->setResult(res); return ct; } bool SleighCompile::contextMod(vector *vec,ContextSymbol *sym,PatternExpression *pe) { // A temporary change to a context variable (within the parsing of a single instruction) // Because we are in the middle of parsing, the "inst_next" value has not been computed yet // So we check to make sure the value expression doesn't use this symbol vector vallist; pe->listValues(vallist); for(uint4 i=0;i(vallist[i]) != (const EndInstructionValue *)0) return false; // Otherwise we generate a "temporary" change to context instruction (ContextOp) ContextField *field = (ContextField *)sym->getPatternValue(); ContextOp *op = new ContextOp(field->getStartBit(),field->getEndBit(),pe); vec->push_back(op); return true; } void SleighCompile::contextSet(vector *vec,TripleSymbol *sym, ContextSymbol *cvar) { // A permanent (global) change to context. During parsing of an instruction, this change // is put off until the full instruction has been parsed. The existing value in the context // field is set permanently to that value starting at the address given by the address expression ContextField *field = (ContextField *)cvar->getPatternValue(); ContextCommit *op = new ContextCommit(sym,field->getStartBit(),field->getEndBit(),cvar->getFlow()); vec->push_back(op); } MacroSymbol *SleighCompile::createMacro(string *name,vector *params) { // create a macro symbol (with parameter names) curct = (Constructor *)0; // Not currently defining a Constructor curmacro = new MacroSymbol(*name,macrotable.size()); delete name; addSymbol(curmacro); symtab.addScope(); // New scope for the body of the macro definition pcode.resetLabelCount(); // Macros have their own labels for(int4 i=0;isize();++i) { OperandSymbol *oper = new OperandSymbol((*params)[i],i,(Constructor *)0); addSymbol(oper); curmacro->addOperand(oper); } delete params; return curmacro; } void SleighCompile::compareMacroParams(MacroSymbol *sym,const vector ¶m) { // Match up any qualities of the macro's OperandSymbols with // any OperandSymbol passed into the macro for(uint4 i=0;igetOut(); if (outvn == (VarnodeTpl *)0) continue; // Check if an OperandSymbol was passed into this macro if (outvn->getOffset().getType() != ConstTpl::handle) continue; int4 hand = outvn->getOffset().getHandleIndex(); // The matching operands OperandSymbol *macroop = sym->getOperand(i); OperandSymbol *parentop; if (curct == (Constructor *)0) parentop = curmacro->getOperand(hand); else parentop = curct->getOperand(hand); // This is the only property we check right now if (macroop->isCodeAddress()) parentop->setCodeAddress(); } } vector *SleighCompile::createMacroUse(MacroSymbol *sym,vector *param) { // Create macro build directive, given symbol and parameters if (sym->getNumOperands() != param->size()) { string errmsg = "Invocation of macro \"" + sym->getName(); if (param->size() > sym->getNumOperands()) errmsg += "\" passes too many parameters"; else errmsg += "\" passes too few parameters"; reportError(errmsg,true); return new vector; } compareMacroParams(sym,*param); OpTpl *op = new OpTpl(MACROBUILD); VarnodeTpl *idvn = new VarnodeTpl(ConstTpl(getConstantSpace()), ConstTpl(ConstTpl::real,sym->getIndex()), ConstTpl(ConstTpl::real,4)); op->addInput(idvn); return ExprTree::appendParams(op,param); } SectionVector *SleighCompile::standaloneSection(ConstructTpl *main) { // Create SectionVector for just the main rtl section with no named sections SectionVector *res = new SectionVector(main,symtab.getCurrentScope()); return res; } SectionVector *SleighCompile::firstNamedSection(ConstructTpl *main,SectionSymbol *sym) { // Start the first named p-code section after the main p-code section sym->incrementDefineCount(); SymbolScope *curscope = symtab.getCurrentScope(); // This should be a Constructor scope SymbolScope *parscope = curscope->getParent(); if (parscope != symtab.getGlobalScope()) throw LowlevelError("firstNamedSection called when not in Constructor scope"); // Unrecoverable error symtab.addScope(); // Add new scope under the Constructor scope SectionVector *res = new SectionVector(main,curscope); res->setNextIndex(sym->getTemplateId()); return res; } SectionVector *SleighCompile::nextNamedSection(SectionVector *vec,ConstructTpl *section,SectionSymbol *sym) { // Add additional named p-code sections sym->incrementDefineCount(); symtab.popScope(); // Pop the scope of the last named section SymbolScope *curscope = symtab.getCurrentScope(); // This should now be the Constructor scope SymbolScope *parscope = curscope->getParent(); if (parscope != symtab.getGlobalScope()) throw LowlevelError("nextNamedSection called when not in section scope"); // Unrecoverable symtab.addScope(); // Add new scope under the Constructor scope (not the last section scope) vec->append(section,curscope); // Associate finished section vec->setNextIndex(sym->getTemplateId()); // Set index for the NEXT section (not been fully parsed yet) return vec; } SectionVector *SleighCompile::finalNamedSection(SectionVector *vec,ConstructTpl *section) { // Fill-in final named section to match the previous SectionSymbol vec->append(section,symtab.getCurrentScope()); symtab.popScope(); // Pop the section scope return vec; } vector *SleighCompile::createCrossBuild(VarnodeTpl *addr,SectionSymbol *sym) { // Create the crossbuild directive as a pcode template unique_allocatemask = 1; vector *res = new vector(); VarnodeTpl *sectionid = new VarnodeTpl(ConstTpl(getConstantSpace()), ConstTpl(ConstTpl::real,sym->getTemplateId()), ConstTpl(ConstTpl::real,4)); // This is simply a single pcodeop (template), where the opcode indicates the crossbuild directive OpTpl *op = new OpTpl( CROSSBUILD ); op->addInput(addr); // The first input is the VarnodeTpl representing the address op->addInput(sectionid); // The second input is the indexed representing the named pcode section to build res->push_back(op); sym->incrementRefCount(); // Keep track of the references to the section symbol return res; } Constructor *SleighCompile::createConstructor(SubtableSymbol *sym) { if (sym == (SubtableSymbol *)0) sym = WithBlock::getCurrentSubtable(withstack); if (sym == (SubtableSymbol *)0) sym = root; curmacro = (MacroSymbol *)0; // Not currently defining a macro curct = new Constructor(sym); curct->setLineno(lineno.back()); sym->addConstructor(curct); symtab.addScope(); // Make a new symbol scope for our constructor pcode.resetLabelCount(); return curct; } void SleighCompile::resetConstructors(void) { // Reset set state after a an error in previous constructor symtab.setCurrentScope(symtab.getGlobalScope()); // Purge any dangling local scopes } bool SleighCompile::expandMacros(ConstructTpl *ctpl,const vector ¯otable) { vector newvec; vector::const_iterator iter; OpTpl *op; for(iter=ctpl->getOpvec().begin();iter!=ctpl->getOpvec().end();++iter) { op = *iter; if (op->getOpcode() == MACROBUILD) { MacroBuilder builder(this,newvec,ctpl->numLabels()); int4 index = op->getIn(0)->getOffset().getReal(); if (index >= macrotable.size()) return false; builder.setMacroOp(op); ConstructTpl *macro_tpl = macrotable[index]; builder.build(macro_tpl,-1); ctpl->setNumLabels( ctpl->numLabels() + macro_tpl->numLabels() ); delete op; // Throw away the place holder op if (builder.hasError()) return false; } else newvec.push_back(op); } ctpl->setOpvec(newvec); return true; } bool SleighCompile::finalizeSections(Constructor *big,SectionVector *vec) { // Do all final checks, expansions, and linking for p-code sections vector errors; RtlPair cur = vec->getMainPair(); int4 i=-1; string sectionstring = " Main section: "; int4 max = vec->getMaxId(); for(;;) { string errstring; errstring = checkSymbols(cur.scope); // Check labels in the section's scope if (errstring.size()==0) { if (!expandMacros(cur.section,macrotable)) errors.push_back(sectionstring + "Could not expand macros"); vector check; big->markSubtableOperands(check); int4 res = cur.section->fillinBuild(check,getConstantSpace()); if (res == 1) errors.push_back(sectionstring + "Duplicate BUILD statements"); if (res == 2) errors.push_back(sectionstring + "Unnecessary BUILD statements"); if (!PcodeCompile::propagateSize(cur.section)) errors.push_back(sectionstring + "Could not resolve at least 1 variable size"); } if (i < 0) { // These potential errors only apply to main section if (cur.section->getResult() != (HandleTpl *)0) { // If there is an export statement if (big->getParent()==root) errors.push_back(" Cannot have export statement in root constructor"); else if (!force_exportsize(cur.section)) errors.push_back(" Size of export is unknown"); } } if (cur.section->delaySlot() != 0) { // Delay slot is present in this constructor if (root != big->getParent()) { // it is not in a root constructor reportWarning("Delay slot used in",false); cerr << " "; big->printInfo(cerr); cerr << endl; } if (cur.section->delaySlot() > maxdelayslotbytes) // Keep track of maximum delayslot parameter maxdelayslotbytes = cur.section->delaySlot(); } do { i += 1; if (i >= max) break; cur = vec->getNamedPair(i); } while(cur.section == (ConstructTpl *)0); if (i >= max) break; SectionSymbol *sym = sections[i]; sectionstring = " " + sym->getName() + " section: "; } if (!errors.empty()) { ostringstream s; s << "in "; big->printInfo(s); reportError(s.str(),false); for(int4 j=0;jgetSpace().isUniqueSpace() && (vn->getOffset().getType() == ConstTpl::real)) { uintb val = vn->getOffset().getReal(); val <<= sa; vn->setOffset(val); } } void SleighCompile::shiftUniqueOp(OpTpl *op,int4 sa) { // Shift the offset up by -sa- bits for any varnode used by this -op- in the unique space VarnodeTpl *outvn = op->getOut(); if (outvn != (VarnodeTpl *)0) shiftUniqueVn(outvn,sa); for(int4 i=0;inumInput();++i) shiftUniqueVn(op->getIn(i),sa); } void SleighCompile::shiftUniqueHandle(HandleTpl *hand,int4 sa) { // Shift the offset up by -sa- bits, for either the dynamic or static varnode aspects that are in the unique space if (hand->getSpace().isUniqueSpace() && (hand->getPtrSpace().getType() == ConstTpl::real) && (hand->getPtrOffset().getType() == ConstTpl::real)) { uintb val = hand->getPtrOffset().getReal(); val <<= sa; hand->setPtrOffset(val); } else if (hand->getPtrSpace().isUniqueSpace() && (hand->getPtrOffset().getType() == ConstTpl::real)) { uintb val = hand->getPtrOffset().getReal(); val <<= sa; hand->setPtrOffset(val); } if (hand->getTempSpace().isUniqueSpace() && (hand->getTempOffset().getType() == ConstTpl::real)) { uintb val = hand->getTempOffset().getReal(); val <<= sa; hand->setTempOffset(val); } } void SleighCompile::shiftUniqueConstruct(ConstructTpl *tpl,int4 sa) { // Shift the offset up by -sa- bits, for any varnode in the unique space associated with this template HandleTpl *result = tpl->getResult(); if (result != (HandleTpl *)0) shiftUniqueHandle(result,sa); const vector &vec( tpl->getOpvec() ); for(int4 i=0;igetNumConstructors(); for(int4 j=0;jgetConstructor(j); ConstructTpl *tpl = ct->getTempl(); if (tpl != (ConstructTpl *)0) shiftUniqueConstruct(tpl,sa); for(int4 k=0;kgetNamedTempl(k); if (namedtpl != (ConstructTpl *)0) shiftUniqueConstruct(namedtpl,sa); } } i+=1; if (i>=tables.size()) break; sym = tables[i]; } uintm ubase = getUniqueBase(); // We have to adjust the unique base ubase <<= sa; setUniqueBase(ubase); } void SleighCompile::pushWith(SubtableSymbol *ss,PatternEquation *pateq,vector *contvec) { withstack.push_back(WithBlock()); withstack.back().set(ss,pateq,contvec); } void SleighCompile::popWith(void) { withstack.pop_back(); } void SleighCompile::buildConstructor(Constructor *big,PatternEquation *pateq,vector *contvec,SectionVector *vec) { // Take all the different parse pieces for a Constructor and build the Constructor object bool noerrors = true; if (vec != (SectionVector *)0) { // If the sections were implemented noerrors = finalizeSections(big,vec); if (noerrors) { // Attach the sections to the Constructor big->setMainSection(vec->getMainSection()); int4 max = vec->getMaxId(); for(int4 i=0;igetNamedSection(i); if (section != (ConstructTpl *)0) big->setNamedSection(section,i); } } delete vec; } if (noerrors) { pateq = WithBlock::collectAndPrependPattern(withstack, pateq); contvec = WithBlock::collectAndPrependContext(withstack, contvec); big->addEquation(pateq); big->removeTrailingSpace(); if (contvec != (vector *)0) { big->addContext(*contvec); delete contvec; } } symtab.popScope(); // In all cases pop scope } void SleighCompile::buildMacro(MacroSymbol *sym,ConstructTpl *rtl) { string errstring = checkSymbols(symtab.getCurrentScope()); if (errstring.size() != 0) { reportError(" in definition of macro "+sym->getName(),false); cerr << errstring; return; } if (!expandMacros(rtl,macrotable)) { reportError("Could not expand submacro in definition of macro "+sym->getName(),true); return; } PcodeCompile::propagateSize(rtl); // Propagate size information (as much as possible) sym->setConstruct(rtl); symtab.popScope(); // Pop local variables used to define macro macrotable.push_back(rtl); } void SleighCompile::recordNop(void) { ostringstream s; s << "NOP detected at " << filename.back() << ':' << dec << lineno.back(); noplist.push_back(s.str()); } static int4 run_compilation(const char *filein,const char *fileout,SleighCompile &compiler) { compiler.parseFromNewFile(filein); slgh = &compiler; // Set global pointer up for parser yyin = fopen(filein,"r"); // Open the file for the lexer if (yyin == (FILE *)0) { cerr << "Unable to open specfile: " << filein << endl; return 2; } try { int4 parseres = yyparse(); // Try to parse fclose(yyin); if (parseres==0) compiler.process(); // Do all the post-processing if ((parseres==0)&&(compiler.numErrors()==0)) { // If no errors ofstream s(fileout); if (!s) { ostringstream errs; errs << "Unable to open output file: " << fileout; throw SleighError(errs.str()); } compiler.saveXml(s); // Dump output xml s.close(); } else { cerr << "No output produced" <getRoot(); for(;;) { const List &list(el->getChildren()); List::const_iterator iter; for(iter=list.begin();iter!=list.end();++iter) { el = *iter; if (el->getName() == "processorfile") { specfileout = el->getContent(); int4 num = el->getNumAttributes(); for(int4 i=0;igetAttributeName(i)=="slaspec") specfilein = el->getAttributeValue(i); else { compiler.setPreprocValue(el->getAttributeName(i),el->getAttributeValue(i)); } } } else if (el->getName() == "language_spec") break; else if (el->getName() == "language_description") break; } if (iter==list.end()) break; } delete doc; if (specfilein.size() == 0) { cerr << "Input slaspec file was not specified in " << filein << endl; exit(1); } if (specfileout.size() == 0) { cerr << "Output sla file was not specified in " << filein << endl; exit(1); } return run_compilation(specfilein.c_str(),specfileout.c_str(),compiler); } static void findSlaSpecs(vector &res, const string &dir, const string &suffix) { FileManage::matchListDir(res, suffix, true, dir, false); vector dirs; FileManage::directoryList(dirs, dir); vector::const_iterator iter; for(iter = dirs.begin();iter!=dirs.end();++iter) { const string &nextdir( *iter ); findSlaSpecs(res, nextdir,suffix); } } static void initCompiler(SleighCompile &compiler, map &defines, bool enableUnnecessaryPcodeWarning, bool disableLenientConflict, bool enableAllCollisionWarning, bool enableAllNopWarning,bool enableDeadTempWarning,bool enforceLocalKeyWord) { map::iterator iter = defines.begin(); for (iter = defines.begin(); iter != defines.end(); iter++) { compiler.setPreprocValue((*iter).first, (*iter).second); } if (enableUnnecessaryPcodeWarning) compiler.setUnnecessaryPcodeWarning(true); if (disableLenientConflict) compiler.setLenientConflict(false); if (enableAllCollisionWarning) compiler.setLocalCollisionWarning( true ); if (enableAllNopWarning) compiler.setAllNopWarning( true ); if (enableDeadTempWarning) compiler.setDeadTempWarning(true); if (enforceLocalKeyWord) compiler.setEnforceLocalKeyWord(true); } static void segvHandler(int sig) { exit(1); // Just die - prevents OS from popping-up a dialog } int main(int argc,char **argv) { int4 retval = 0; signal(SIGSEGV, &segvHandler); // Exit on SEGV errors #ifdef YYDEBUG yydebug = 0; #endif if (argc < 2) { cerr << "USAGE: sleigh [-x] [-dNAME=VALUE] inputfile [outputfile]" << endl; cerr << " -a scan for all slaspec files recursively where inputfile is a directory" << endl; cerr << " -x turns on parser debugging" << endl; cerr << " -u print warnings for unnecessary pcode instructions" << endl; cerr << " -l report pattern conflicts" << endl; cerr << " -n print warnings for all NOP constructors" << endl; cerr << " -t print warnings for dead temporaries" << endl; cerr << " -e enforce use of 'local' keyword for temporaries" << endl; cerr << " -c print warnings for all constructors with colliding operands" << endl; cerr << " -DNAME=VALUE defines a preprocessor macro NAME with value VALUE" << endl; exit(2); } const string SLAEXT(".sla"); // Default sla extension const string SLASPECEXT(".slaspec"); map defines; bool enableUnnecessaryPcodeWarning = false; bool disableLenientConflict = false; bool enableAllCollisionWarning = false; bool enableAllNopWarning = false; bool enableDeadTempWarning = false; bool enforceLocalKeyWord = false; bool compileAll = false; int4 i; for(i=1;i slaspecs; string dirStr = "."; if (i != argc) dirStr = argv[i]; findSlaSpecs(slaspecs, dirStr,SLASPECEXT); cout << "Compiling " << slaspecs.size() << " slaspec files in " << dirStr << "\n"; for(int4 j=0;j "sleigh file.slaspec file.sla" string fileoutSTR = fileinPreExt; fileoutSTR.append(SLAEXT); retval = run_compilation(fileinExamine.c_str(),fileoutSTR.c_str(),compiler); }else{ retval = run_xml(fileinExamine.c_str(),compiler); } } } return retval; }