diff --git a/Ghidra/Debug/ProposedUtils/src/main/java/ghidra/pcode/exec/SleighProgramCompiler.java b/Ghidra/Debug/ProposedUtils/src/main/java/ghidra/pcode/exec/SleighProgramCompiler.java index 33339ea6c5..f27a449d23 100644 --- a/Ghidra/Debug/ProposedUtils/src/main/java/ghidra/pcode/exec/SleighProgramCompiler.java +++ b/Ghidra/Debug/ProposedUtils/src/main/java/ghidra/pcode/exec/SleighProgramCompiler.java @@ -15,6 +15,7 @@ */ package ghidra.pcode.exec; +import java.io.IOException; import java.util.List; import java.util.Map; @@ -81,9 +82,10 @@ public class SleighProgramCompiler { * @return the list of p-code ops * @throws UnknownInstructionException in case of crossbuilds, the target instruction is unknown * @throws MemoryAccessException in case of crossbuilds, the target address cannot be accessed + * @throws IOException for errors in during emitting */ public static List buildOps(Language language, ConstructTpl template) - throws UnknownInstructionException, MemoryAccessException { + throws UnknownInstructionException, MemoryAccessException, IOException { Address zero = language.getDefaultSpace().getAddress(0); SleighParserContext c = new SleighParserContext(zero, zero, zero, zero); ParserWalker walk = new ParserWalker(c); @@ -116,6 +118,7 @@ public class SleighProgramCompiler { * This is basically a hack to avoid NPEs when no output varnode is given. * * @param parser the parser to add the symbol to + * @return the nil symbol */ protected static VarnodeSymbol addNilSymbol(PcodeParser parser) { SleighSymbol exists = parser.findSymbol(NIL_SYMBOL_NAME); @@ -124,9 +127,8 @@ public class SleighProgramCompiler { return (VarnodeSymbol) exists; } long offset = parser.allocateTemp(); - VarnodeSymbol nil = - new VarnodeSymbol(new Location("", 0), NIL_SYMBOL_NAME, parser.getUniqueSpace(), - offset, 1); + VarnodeSymbol nil = new VarnodeSymbol(new Location("", 0), NIL_SYMBOL_NAME, + parser.getUniqueSpace(), offset, 1); parser.addSymbol(nil); return nil; } @@ -156,7 +158,7 @@ public class SleighProgramCompiler { return ctor.construct(language, SleighProgramCompiler.buildOps(language, template), libSyms); } - catch (UnknownInstructionException | MemoryAccessException e) { + catch (UnknownInstructionException | MemoryAccessException | IOException e) { throw new AssertionError(e); } } @@ -278,9 +280,8 @@ public class SleighProgramCompiler { String p = params.get(i); Varnode a = args.get(i); if (a == null && i == 0) { // Only allow output to be omitted - parser.addSymbol( - new VarnodeSymbol(nil.getLocation(), p, nilVnData.space, nilVnData.offset, - nilVnData.size)); + parser.addSymbol(new VarnodeSymbol(nil.getLocation(), p, nilVnData.space, + nilVnData.offset, nilVnData.size)); } else { parser.addSymbol(paramSym(language, sleigh, opName, p, a)); diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/address.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/address.cc index 1cb02c5b2f..b3758ddd15 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/address.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/address.cc @@ -304,7 +304,7 @@ void Range::decode(Decoder &decoder) { uint4 elemId = decoder.openElement(); if (elemId != ELEM_RANGE && elemId != ELEM_REGISTER) - throw XmlError("Expecting or element"); + throw DecoderError("Expecting or element"); decodeFromAttributes(decoder); decoder.closeElement(elemId); } @@ -354,7 +354,7 @@ void RangeProperties::decode(Decoder &decoder) { uint4 elemId = decoder.openElement(); if (elemId != ELEM_RANGE && elemId != ELEM_REGISTER) - throw XmlError("Expecting or element"); + throw DecoderError("Expecting or element"); for(;;) { uint4 attribId = decoder.getNextAttributeId(); if (attribId == 0) break; diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/architecture.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/architecture.cc index 8245f76d64..9b36819f84 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/architecture.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/architecture.cc @@ -534,22 +534,25 @@ void Architecture::nameFunction(const Address &addr,string &name) const name = defname.str(); } -/// This process sets up a "register relative" space for this architecture -/// If the name is "stack", this space takes on the role of an "official" stack space -/// Should only be called once during initialization +/// \brief Create a new address space associated with a pointer register +/// +/// This process sets up a \e register \e relative"space for this architecture. +/// If indicated, this space takes on the role of the \e formal stack space. +/// Should only be called once during initialization. /// \param basespace is the address space underlying the stack /// \param nm is the name of the new space /// \param ptrdata is the register location acting as a pointer into the new space /// \param truncSize is the (possibly truncated) size of the register that fits the space /// \param isreversejustified is \b true if small variables are justified opposite of endianness /// \param stackGrowth is \b true if a stack implemented in this space grows in the negative direction +/// \param isFormal is the indicator for the \b formal stack space void Architecture::addSpacebase(AddrSpace *basespace,const string &nm,const VarnodeData &ptrdata, - int4 truncSize,bool isreversejustified,bool stackGrowth) + int4 truncSize,bool isreversejustified,bool stackGrowth,bool isFormal) { int4 ind = numSpaces(); - SpacebaseSpace *spc = new SpacebaseSpace(this,translate,nm,ind,truncSize,basespace,ptrdata.space->getDelay()+1); + SpacebaseSpace *spc = new SpacebaseSpace(this,translate,nm,ind,truncSize,basespace,ptrdata.space->getDelay()+1,isFormal); if (isreversejustified) setReverseJustified(spc); insertSpace(spc); @@ -1047,7 +1050,7 @@ void Architecture::decodeStackPointer(Decoder &decoder) truncSize = basespace->getAddrSize(); } - addSpacebase(basespace,"stack",point,truncSize,isreversejustify,stackGrowth); // Create the "official" stackpointer + addSpacebase(basespace,"stack",point,truncSize,isreversejustify,stackGrowth,true); // Create the "official" stackpointer } /// Manually alter the dead-code delay for a specific address space, @@ -1114,7 +1117,7 @@ void Architecture::decodeSpacebase(Decoder &decoder) AddrSpace *basespace = decoder.readSpace(ATTRIB_SPACE); decoder.closeElement(elemId); const VarnodeData &point(translate->getRegister(registerName)); - addSpacebase(basespace,nameString,point,point.size,false,false); + addSpacebase(basespace,nameString,point,point.size,false,false,false); } /// Configure memory based on a \ element. Mark specific address ranges diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/architecture.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/architecture.hh index 5951ad871f..e21c817bdf 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/architecture.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/architecture.hh @@ -247,7 +247,7 @@ public: #endif protected: void addSpacebase(AddrSpace *basespace,const string &nm,const VarnodeData &ptrdata, - int4 truncSize,bool isreversejustified,bool stackGrowth); ///< Create a new space and associated pointer + int4 truncSize,bool isreversejustified,bool stackGrowth,bool isFormal); void addNoHighPtr(const Range &rng); ///< Add a new region where pointers do not exist // Factory routines for building this architecture diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/comment_ghidra.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/comment_ghidra.cc index 3df7705f87..04a4c2cf0d 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/comment_ghidra.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/comment_ghidra.cc @@ -40,7 +40,7 @@ void CommentDatabaseGhidra::fillCache(const Address &fad) const iter = cache.beginComment(fad); iterend = cache.endComment(fad); - XmlDecode decoder(ghidra); + PackedDecode decoder(ghidra); if (ghidra->getComments(fad,commentfilter,decoder)) { cache.decode(decoder); } diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/consolemain.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/consolemain.cc index b4555a9940..fd746de500 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/consolemain.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/consolemain.cc @@ -71,7 +71,7 @@ void IfcLoadFile::execute(istream &s) else *status->optr << "Wrong tag type for experimental rules: "+root->getName() << endl; } - catch(XmlError &err) { + catch(DecoderError &err) { *status->optr << err.explain << endl; *status->optr << "Skipping experimental rules" << endl; } @@ -81,7 +81,7 @@ void IfcLoadFile::execute(istream &s) bool iserror = false; try { dcp->conf->init(store); - } catch(XmlError &err) { + } catch(DecoderError &err) { errmsg = err.explain; iserror = true; } catch(LowlevelError &err) { @@ -156,7 +156,7 @@ void IfcRestore::execute(istream &s) dcp->conf->restoreXml(store); } catch(LowlevelError &err) { throw IfaceExecutionError(err.explain); - } catch(XmlError &err) { + } catch(DecoderError &err) { throw IfaceExecutionError(err.explain); } diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/cpool_ghidra.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/cpool_ghidra.cc index f8a82d2f7e..e8dee82567 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/cpool_ghidra.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/cpool_ghidra.cc @@ -32,16 +32,16 @@ const CPoolRecord *ConstantPoolGhidra::getRecord(const vector &refs) cons { const CPoolRecord *rec = cache.getRecord(refs); if (rec == (const CPoolRecord *)0) { - XmlDecode decoder(ghidra); bool success; + PackedDecode decoder(ghidra); try { success = ghidra->getCPoolRef(refs,decoder); } catch(JavaError &err) { throw LowlevelError("Error fetching constant pool record: " + err.explain); } - catch(XmlError &err) { - throw LowlevelError("Error in constant pool record xml: "+err.explain); + catch(DecoderError &err) { + throw LowlevelError("Error in constant pool record encoding: "+err.explain); } if (!success) { ostringstream s; diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/database_ghidra.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/database_ghidra.cc index e55c936f29..35030ffe0c 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/database_ghidra.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/database_ghidra.cc @@ -52,7 +52,7 @@ Scope *ScopeGhidra::reresolveScope(uint8 id) const if (cacheScope != (Scope *)0) return cacheScope; // Scope was previously cached - XmlDecode decoder(ghidra); + PackedDecode decoder(ghidra); if (!ghidra->getNamespacePath(id,decoder)) throw LowlevelError("Could not get namespace info"); @@ -212,7 +212,7 @@ Symbol *ScopeGhidra::removeQuery(const Address &addr) const // Have we queried this address before if (holes.inRange(addr,1)) return (Symbol *)0; - XmlDecode decoder(ghidra); + PackedDecode decoder(ghidra); if (ghidra->getMappedSymbolsXML(addr,decoder)) { // Query GHIDRA about this address sym = dump2Cache(decoder); // Add it to the cache } @@ -349,9 +349,9 @@ Funcdata *ScopeGhidra::resolveExternalRefFunction(ExternRefSymbol *sym) const if (resFd == (Funcdata *)0) { // If the function isn't in cache, we use the special // getExternalRefXML interface to recover the external function + PackedDecode decoder(ghidra); SymbolEntry *entry = sym->getFirstWholeMap(); - XmlDecode decoder(ghidra); - if (ghidra->getExternalRefXML(entry->getAddr(),decoder)) { + if (ghidra->getExternalRef(entry->getAddr(),decoder)) { FunctionSymbol *funcSym; // Make sure referenced function is cached funcSym = dynamic_cast(dump2Cache(decoder)); diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/fspec.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/fspec.cc index 64c6379777..442ade3ed6 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/fspec.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/fspec.cc @@ -4395,13 +4395,10 @@ void FuncProto::decode(Decoder &decoder,Architecture *glb) } else if (attribId == ATTRIB_EXTRAPOP) { seenextrapop = true; - string expopval = decoder.readString(); - if (expopval == "unknown") + try { + readextrapop = decoder.readSignedInteger(); + } catch(DecoderError &err) { readextrapop = ProtoModel::extrapop_unknown; - else { - istringstream i1(expopval); - i1.unsetf(ios::dec | ios::hex | ios::oct); - i1 >> readextrapop; } } else if (attribId == ATTRIB_MODELLOCK) { diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/ghidra_arch.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/ghidra_arch.cc index 2356581ace..ce67e2cc88 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/ghidra_arch.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/ghidra_arch.cc @@ -23,6 +23,28 @@ #include "cpool_ghidra.hh" #include "inject_ghidra.hh" +//AttributeId ATTRIB_BADDATA = AttributeId("baddata",145); + +ElementId ELEM_COMMAND_ISNAMEUSED = ElementId("command_isnameused",239); +ElementId ELEM_COMMAND_GETBYTES = ElementId("command_getbytes",240); +ElementId ELEM_COMMAND_GETCALLFIXUP = ElementId("command_getcallfixup",241); +ElementId ELEM_COMMAND_GETCALLMECH = ElementId("command_getcallmech",242); +ElementId ELEM_COMMAND_GETCALLOTHERFIXUP = ElementId("command_getcallotherfixup",243); +ElementId ELEM_COMMAND_GETCODELABEL = ElementId("command_getcodelabel",244); +ElementId ELEM_COMMAND_GETCOMMENTS = ElementId("command_getcomments",245); +ElementId ELEM_COMMAND_GETCPOOLREF = ElementId("command_getcpoolref",246); +ElementId ELEM_COMMAND_GETDATATYPE = ElementId("command_getdatatype",247); +ElementId ELEM_COMMAND_GETEXTERNALREF = ElementId("command_getexternalref",248); +ElementId ELEM_COMMAND_GETMAPPEDSYMBOLS = ElementId("command_getmappedsymbols",249); +ElementId ELEM_COMMAND_GETNAMESPACEPATH = ElementId("command_getnamespacepath",250); +ElementId ELEM_COMMAND_GETPCODE = ElementId("command_getpcode",251); +ElementId ELEM_COMMAND_GETPCODEEXECUTABLE = ElementId("command_getpcodeexecutable",252); +ElementId ELEM_COMMAND_GETREGISTER = ElementId("command_getregister",253); +ElementId ELEM_COMMAND_GETREGISTERNAME = ElementId("command_getregistername",254); +ElementId ELEM_COMMAND_GETSTRINGDATA = ElementId("command_getstringdata",255); +ElementId ELEM_COMMAND_GETTRACKEDREGISTERS = ElementId("command_gettrackedregisters",256); +ElementId ELEM_COMMAND_GETUSEROPNAME = ElementId("command_getuseropname",257); + /// Catch the signal so the OS doesn't pop up a dialog /// \param sig is the OS signal (should always be SIGSEGV) void ArchitectureGhidra::segvHandler(int4 sig) @@ -143,7 +165,7 @@ void ArchitectureGhidra::readStringStream(istream &s,string &res) /// \param s is the input stream from the client. /// \param decoder is the given stream decoder that will hold the result /// \return \b true if a response was received -bool ArchitectureGhidra::readStream(istream &s,Decoder &decoder) +bool ArchitectureGhidra::readStringStream(istream &s,Decoder &decoder) { int4 type = readToAnyBurst(s); @@ -159,38 +181,6 @@ bool ArchitectureGhidra::readStream(istream &s,Decoder &decoder) throw JavaError("alignment","Expecting string or end of query response"); } -/// The method expects to see protocol markers indicating a string from the client, -/// otherwise it throws and exception. An array size is encoded in the first 4 characters -/// of the string. An array of this size is allocated and filled with the -/// rest of the string. -/// \param s is the input stream from the client -/// \return the array of packed p-code data -uint1 *ArchitectureGhidra::readPackedStream(istream &s) - -{ - int4 type = readToAnyBurst(s); - if (type == 14) { - uint4 size = 0; - int4 c = s.get(); - size ^= (c-0x20); - c = s.get(); - size ^= ((c-0x20)<<6); - c = s.get(); - size ^= ((c-0x20)<<12); - c = s.get(); - size ^= ((c-0x20)<<18); - uint1 *res = new uint1[ size ]; - s.read((char *)res,size); - type = readToAnyBurst(s); - if (type != 15) - throw JavaError("alignment","Expecting packed string end"); - return res; - } - if ((type&1)==1) - return (uint1 *)0; - throw JavaError("alignment","Expecting string or end of query response"); -} - /// Write out a string with correct protocol markers /// \param s is the output stream to the client /// \param msg is the string to send @@ -240,27 +230,13 @@ bool ArchitectureGhidra::readAll(istream &s,Decoder &decoder) { readToResponse(s); - if (readStream(s,decoder)) { + if (readStringStream(s,decoder)) { readResponseEnd(s); return true; } return false; } -/// Read up to the beginning of a query response, check for an -/// exception record, otherwise read in packed p-code op data. -/// \param s is the input stream from the client -/// \return the array of packed p-coded data -uint1 *ArchitectureGhidra::readPackedAll(istream &s) - -{ - readToResponse(s); - uint1 *doc = readPackedStream(s); - if (doc != (uint1 *)0) - readResponseEnd(s); - return doc; -} - /// \brief Send an exception message to the Ghidra client /// /// This generally called because of some sort of alignment issue in the @@ -412,8 +388,12 @@ bool ArchitectureGhidra::getRegister(const string ®name,Decoder &decoder) { sout.write("\000\000\001\004",4); - writeStringStream(sout,"getRegister"); - writeStringStream(sout,regname); + sout.write("\000\000\001\016",4); // Beginning of string header + PackedEncode encoder(sout); + encoder.openElement(ELEM_COMMAND_GETREGISTER); + encoder.writeString(ATTRIB_NAME, regname); + encoder.closeElement(ELEM_COMMAND_GETREGISTER); + sout.write("\000\000\001\017",4); sout.write("\000\000\001\005",4); sout.flush(); @@ -429,11 +409,12 @@ string ArchitectureGhidra::getRegisterName(const VarnodeData &vndata) { sout.write("\000\000\001\004",4); - writeStringStream(sout,"getRegisterName"); sout.write("\000\000\001\016",4); // Beginning of string header Address addr(vndata.space,vndata.offset); - encoder.clear(); + PackedEncode encoder(sout); + encoder.openElement(ELEM_COMMAND_GETREGISTERNAME); addr.encode(encoder,vndata.size); + encoder.closeElement(ELEM_COMMAND_GETREGISTERNAME); sout.write("\000\000\001\017",4); sout.write("\000\000\001\005",4); sout.flush(); @@ -456,10 +437,11 @@ bool ArchitectureGhidra::getTrackedRegisters(const Address &addr,Decoder &decode { sout.write("\000\000\001\004",4); - writeStringStream(sout,"getTrackedRegisters"); sout.write("\000\000\001\016",4); // Beginning of string header - encoder.clear(); + PackedEncode encoder(sout); + encoder.openElement(ELEM_COMMAND_GETTRACKEDREGISTERS); addr.encode(encoder); + encoder.closeElement(ELEM_COMMAND_GETTRACKEDREGISTERS); sout.write("\000\000\001\017",4); sout.write("\000\000\001\005",4); sout.flush(); @@ -475,9 +457,11 @@ string ArchitectureGhidra::getUserOpName(int4 index) { sout.write("\000\000\001\004",4); - writeStringStream(sout,"getUserOpName"); sout.write("\000\000\001\016",4); // Beginning of string header - sout << dec << index; + PackedEncode encoder(sout); + encoder.openElement(ELEM_COMMAND_GETUSEROPNAME); + encoder.writeSignedInteger(ATTRIB_INDEX, index); + encoder.closeElement(ELEM_COMMAND_GETUSEROPNAME); sout.write("\000\000\001\017",4); sout.write("\000\000\001\005",4); sout.flush(); @@ -489,24 +473,24 @@ string ArchitectureGhidra::getUserOpName(int4 index) return res; } -/// Get a description of all the p-code ops for the instruction -/// at the given address. The information is stored in a special -/// compressed format. (See PcodeEmit::restorePackedOp) +/// Get a description of all the p-code ops for the instruction at the given address. /// \param addr is the address of the instruction -/// \return an array of the packed data -uint1 *ArchitectureGhidra::getPcodePacked(const Address &addr) +/// \param decoder is the stream decoder for holding the result +/// \return true if the request is successful and ops are ready to be decoded +bool ArchitectureGhidra::getPcode(const Address &addr,Decoder &decoder) { sout.write("\000\000\001\004",4); - writeStringStream(sout,"getPacked"); sout.write("\000\000\001\016",4); // Beginning of string header - encoder.clear(); + PackedEncode encoder(sout); + encoder.openElement(ELEM_COMMAND_GETPCODE); addr.encode(encoder); + encoder.closeElement(ELEM_COMMAND_GETPCODE); sout.write("\000\000\001\017",4); sout.write("\000\000\001\005",4); sout.flush(); - return readPackedAll(sin); + return readAll(sin,decoder); } /// The Ghidra client will pass back a \ element, \ element, or some @@ -520,10 +504,11 @@ bool ArchitectureGhidra::getMappedSymbolsXML(const Address &addr,Decoder &decode { sout.write("\000\000\001\004",4); - writeStringStream(sout,"getMappedSymbolsXML"); sout.write("\000\000\001\016",4); // Beginning of string - encoder.clear(); + PackedEncode encoder(sout); + encoder.openElement(ELEM_COMMAND_GETMAPPEDSYMBOLS); addr.encode(encoder); + encoder.closeElement(ELEM_COMMAND_GETMAPPEDSYMBOLS); sout.write("\000\000\001\017",4); sout.write("\000\000\001\005",4); sout.flush(); @@ -540,14 +525,15 @@ bool ArchitectureGhidra::getMappedSymbolsXML(const Address &addr,Decoder &decode /// \param addr is the given address /// \param decoder is the stream decoder that will hold the result /// \return \b true if the query completes successfully -bool ArchitectureGhidra::getExternalRefXML(const Address &addr,Decoder &decoder) +bool ArchitectureGhidra::getExternalRef(const Address &addr,Decoder &decoder) { sout.write("\000\000\001\004",4); - writeStringStream(sout,"getExternalRefXML"); sout.write("\000\000\001\016",4); // Beginning of string header - encoder.clear(); + PackedEncode encoder(sout); + encoder.openElement(ELEM_COMMAND_GETEXTERNALREF); addr.encode(encoder); + encoder.closeElement(ELEM_COMMAND_GETEXTERNALREF); sout.write("\000\000\001\017",4); sout.write("\000\000\001\005",4); sout.flush(); @@ -565,9 +551,11 @@ bool ArchitectureGhidra::getNamespacePath(uint8 id,Decoder &decoder) { sout.write("\000\000\001\004",4); - writeStringStream(sout,"getNamespacePath"); sout.write("\000\000\001\016",4); // Beginning of string header - sout << hex << id; + PackedEncode encoder(sout); + encoder.openElement(ELEM_COMMAND_GETNAMESPACEPATH); + encoder.writeUnsignedInteger(ATTRIB_ID, id); + encoder.closeElement(ELEM_COMMAND_GETNAMESPACEPATH); sout.write("\000\000\001\017",4); sout.write("\000\000\001\005",4); sout.flush(); @@ -579,15 +567,13 @@ bool ArchitectureGhidra::isNameUsed(const string &nm,uint8 startId,uint8 stopId) { sout.write("\000\000\001\004",4); - writeStringStream(sout,"isNameUsed"); sout.write("\000\000\001\016",4); // Beginning of string header - sout << nm; - sout.write("\000\000\001\017",4); - sout.write("\000\000\001\016",4); // Beginning of string header - sout << hex << startId; - sout.write("\000\000\001\017",4); - sout.write("\000\000\001\016",4); // Beginning of string header - sout << hex << stopId; + PackedEncode encoder(sout); + encoder.openElement(ELEM_COMMAND_ISNAMEUSED); + encoder.writeString(ATTRIB_NAME, nm); + encoder.writeUnsignedInteger(ATTRIB_FIRST, startId); + encoder.writeUnsignedInteger(ATTRIB_LAST, stopId); + encoder.closeElement(ELEM_COMMAND_ISNAMEUSED); sout.write("\000\000\001\017",4); sout.write("\000\000\001\005",4); sout.flush(); @@ -606,10 +592,11 @@ string ArchitectureGhidra::getCodeLabel(const Address &addr) { sout.write("\000\000\001\004",4); - writeStringStream(sout,"getSymbol"); sout.write("\000\000\001\016",4); // Beginning of string header - encoder.clear(); + PackedEncode encoder(sout); + encoder.openElement(ELEM_COMMAND_GETCODELABEL); addr.encode(encoder); + encoder.closeElement(ELEM_COMMAND_GETCODELABEL); sout.write("\000\000\001\017",4); sout.write("\000\000\001\005",4); sout.flush(); @@ -627,14 +614,16 @@ string ArchitectureGhidra::getCodeLabel(const Address &addr) /// \param id is a unique id associated with the data-type, pass 0 if unknown /// \param decoder is the stream decoder that will hold the result /// \return \b true if the query completed successfully -bool ArchitectureGhidra::getType(const string &name,uint8 id,Decoder &decoder) +bool ArchitectureGhidra::getDataType(const string &name,uint8 id,Decoder &decoder) { sout.write("\000\000\001\004",4); - writeStringStream(sout,"getType"); - writeStringStream(sout,name); sout.write("\000\000\001\016",4); // Beginning of string header - sout << dec << (int8)id; // Pass as a signed integer + PackedEncode encoder(sout); + encoder.openElement(ELEM_COMMAND_GETDATATYPE); + encoder.writeString(ATTRIB_NAME, name); + encoder.writeSignedInteger(ATTRIB_ID, id); + encoder.closeElement(ELEM_COMMAND_GETDATATYPE); sout.write("\000\000\001\017",4); sout.write("\000\000\001\005",4); sout.flush(); @@ -654,13 +643,12 @@ bool ArchitectureGhidra::getComments(const Address &fad,uint4 flags,Decoder &dec { sout.write("\000\000\001\004",4); - writeStringStream(sout,"getComments"); sout.write("\000\000\001\016",4); // Beginning of string header - encoder.clear(); + PackedEncode encoder(sout); + encoder.openElement(ELEM_COMMAND_GETCOMMENTS); + encoder.writeUnsignedInteger(ATTRIB_TYPE, flags); fad.encode(encoder); - sout.write("\000\000\001\017",4); - sout.write("\000\000\001\016",4); // Beginning of string header - sout << dec << flags; + encoder.closeElement(ELEM_COMMAND_GETCOMMENTS); sout.write("\000\000\001\017",4); sout.write("\000\000\001\005",4); sout.flush(); @@ -678,10 +666,11 @@ void ArchitectureGhidra::getBytes(uint1 *buf,int4 size,const Address &inaddr) { sout.write("\000\000\001\004",4); - writeStringStream(sout,"getBytes"); sout.write("\000\000\001\016",4); // Beginning of string header - encoder.clear(); + PackedEncode encoder(sout); + encoder.openElement(ELEM_COMMAND_GETBYTES); inaddr.encode(encoder,size); + encoder.closeElement(ELEM_COMMAND_GETBYTES); sout.write("\000\000\001\017",4); sout.write("\000\000\001\005",4); sout.flush(); @@ -726,14 +715,14 @@ void ArchitectureGhidra::getStringData(vector &buffer,const Address &addr { sout.write("\000\000\001\004",4); - writeStringStream(sout,"getString"); sout.write("\000\000\001\016",4); // Beginning of string header - encoder.clear(); - addr.encode(encoder,maxBytes); - sout.write("\000\000\001\017",4); - writeStringStream(sout,ct->getName()); - sout.write("\000\000\001\016",4); // Beginning of string header - sout << dec << (int8)ct->getId(); // Pass as a signed integer + PackedEncode encoder(sout); + encoder.openElement(ELEM_COMMAND_GETSTRINGDATA); + encoder.writeSignedInteger(ATTRIB_MAXSIZE, maxBytes); + encoder.writeString(ATTRIB_TYPE,ct->getName()); + encoder.writeUnsignedInteger(ATTRIB_ID, ct->getId()); + addr.encode(encoder); + encoder.closeElement(ELEM_COMMAND_GETSTRINGDATA); sout.write("\000\000\001\017",4); sout.write("\000\000\001\005",4); @@ -785,18 +774,33 @@ bool ArchitectureGhidra::getPcodeInject(const string &name,int4 type,const Injec { sout.write("\000\000\001\004",4); - if (type == InjectPayload::CALLFIXUP_TYPE) - writeStringStream(sout,"getCallFixup"); - else if (type == InjectPayload::CALLOTHERFIXUP_TYPE) - writeStringStream(sout,"getCallotherFixup"); - else if (type == InjectPayload::CALLMECHANISM_TYPE) - writeStringStream(sout,"getCallMech"); - else - writeStringStream(sout,"getXPcode"); - writeStringStream(sout,name); sout.write("\000\000\001\016",4); - encoder.clear(); - con.encode(encoder); + PackedEncode encoder(sout); + if (type == InjectPayload::CALLFIXUP_TYPE) { + encoder.openElement(ELEM_COMMAND_GETCALLFIXUP); + encoder.writeString(ATTRIB_NAME, name); + con.encode(encoder); + encoder.closeElement(ELEM_COMMAND_GETCALLFIXUP); + } + else if (type == InjectPayload::CALLOTHERFIXUP_TYPE) { + encoder.openElement(ELEM_COMMAND_GETCALLOTHERFIXUP); + encoder.writeString(ATTRIB_NAME, name); + con.encode(encoder); + encoder.closeElement(ELEM_COMMAND_GETCALLOTHERFIXUP); + } + else if (type == InjectPayload::CALLMECHANISM_TYPE) { + encoder.openElement(ELEM_COMMAND_GETCALLMECH); + encoder.writeString(ATTRIB_NAME, name); + con.encode(encoder); + encoder.closeElement(ELEM_COMMAND_GETCALLMECH); + } + else { + encoder.openElement(ELEM_COMMAND_GETPCODEEXECUTABLE); + encoder.writeString(ATTRIB_NAME, name); + con.encode(encoder); + encoder.closeElement(ELEM_COMMAND_GETPCODEEXECUTABLE); + } + sout.write("\000\000\001\017",4); sout.write("\000\000\001\005",4); sout.flush(); @@ -814,12 +818,16 @@ bool ArchitectureGhidra::getCPoolRef(const vector &refs,Decoder &decoder) { sout.write("\000\000\001\004",4); - writeStringStream(sout,"getCPoolRef"); sout.write("\000\000\001\016",4); // Beginning of string header - sout << hex << refs[0]; - for(int4 i=1;i &refs,Decoder &decoder) return readAll(sin,decoder); } -// Document *ArchitectureGhidra::getScopeProperties(Scope *newscope) - -// { // Query ghidra about the properties of a namespace scope -// vector namepath; -// newscope->getNameSegments(namepath); -// sout.write("\000\000\001\004",4); -// writeStringStream(sout,"getScope"); -// sout.write("\000\000\001\016",4); // Beginning of string header -// sout << "\n"; -// for(int4 i=0;i" << namepath[i] << "\n"; -// sout << "\n"; -// sout.write("\000\000\001\017",4); -// sout.write("\000\000\001\005",4); -// sout.flush(); -// return readXMLAll(sin); -// } - void ArchitectureGhidra::printMessage(const string &message) const { @@ -861,7 +851,7 @@ void ArchitectureGhidra::printMessage(const string &message) const /// \param o is the output stream to the Ghidra client ArchitectureGhidra::ArchitectureGhidra(const string &pspec,const string &cspec,const string &tspec, const string &corespec,istream &i,ostream &o) - : Architecture(), sin(i), sout(o), encoder(sout) + : Architecture(), sin(i), sout(o) { print->setMarkup(true); diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/ghidra_arch.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/ghidra_arch.hh index d557707a1a..bd0fc3afef 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/ghidra_arch.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/ghidra_arch.hh @@ -21,6 +21,26 @@ #include "architecture.hh" +extern ElementId ELEM_COMMAND_ISNAMEUSED; ///< Marshaling element \ +extern ElementId ELEM_COMMAND_GETBYTES; ///< Marshaling element \ +extern ElementId ELEM_COMMAND_GETCALLFIXUP; ///< Marshaling element \ +extern ElementId ELEM_COMMAND_GETCALLMECH; ///< Marshaling element \ +extern ElementId ELEM_COMMAND_GETCALLOTHERFIXUP; ///< Marshaling element \ +extern ElementId ELEM_COMMAND_GETCODELABEL; ///< Marshaling element \ +extern ElementId ELEM_COMMAND_GETCOMMENTS; ///< Marshaling element \ +extern ElementId ELEM_COMMAND_GETCPOOLREF; ///< Marshaling element \ +extern ElementId ELEM_COMMAND_GETDATATYPE; ///< Marshaling element \ +extern ElementId ELEM_COMMAND_GETEXTERNALREF; ///< Marshaling element \ +extern ElementId ELEM_COMMAND_GETMAPPEDSYMBOLS; ///< Marshaling element \ +extern ElementId ELEM_COMMAND_GETNAMESPACEPATH; ///< Marshaling element \ +extern ElementId ELEM_COMMAND_GETPCODE; ///< Marshaling element \ +extern ElementId ELEM_COMMAND_GETPCODEEXECUTABLE; ///< Marshaling element \ +extern ElementId ELEM_COMMAND_GETREGISTER; ///< Marshaling element \ +extern ElementId ELEM_COMMAND_GETREGISTERNAME; ///< Marshaling element \ +extern ElementId ELEM_COMMAND_GETSTRINGDATA; ///< Marshaling element \ +extern ElementId ELEM_COMMAND_GETTRACKEDREGISTERS; ///< Marshaling element \ +extern ElementId ELEM_COMMAND_GETUSEROPNAME; ///< Marshaling element \ + /// \brief Exception that mirrors exceptions thrown by the Ghidra client /// /// If the Ghidra client throws an exception while trying to answer a query, @@ -60,7 +80,6 @@ struct JavaError : public LowlevelError { class ArchitectureGhidra : public Architecture { istream &sin; ///< Input stream for interfacing with Ghidra ostream &sout; ///< Output stream for interfacing with Ghidra - XmlEncode encoder; ///< Encoder used to write to Ghidra mutable string warnings; ///< Warnings accumulated by the decompiler string pspecxml; ///< XML pspec passed from Ghidra string cspecxml; ///< XML cspec passed from Ghidra @@ -91,13 +110,13 @@ public: string getRegisterName(const VarnodeData &vndata); ///< Retrieve a register name given its storage location bool getTrackedRegisters(const Address &addr,Decoder &decoder); ///< Retrieve \e tracked register values at the given address string getUserOpName(int4 index); ///< Get the name of a user-defined p-code op - uint1 *getPcodePacked(const Address &addr); ///< Get p-code for a single instruction + bool getPcode(const Address &addr,Decoder &decoder); ///< Get p-code for a single instruction bool getMappedSymbolsXML(const Address &addr,Decoder &decoder); ///< Get symbols associated with the given address - bool getExternalRefXML(const Address &addr,Decoder &decoder); ///< Retrieve a description of an external function + bool getExternalRef(const Address &addr,Decoder &decoder); ///< Retrieve a description of an external function bool getNamespacePath(uint8 id,Decoder &decoder); ///< Get a description of a namespace path bool isNameUsed(const string &nm,uint8 startId,uint8 stopId); ///< Is given name used along namespace path string getCodeLabel(const Address &addr); ///< Retrieve a label at the given address - bool getType(const string &name,uint8 id,Decoder &decoder); ///< Retrieve a data-type description for the given name and id + bool getDataType(const string &name,uint8 id,Decoder &decoder); ///< Retrieve a data-type description for the given name and id bool getComments(const Address &fad,uint4 flags,Decoder &decoder); ///< Retrieve comments for a particular function void getBytes(uint1 *buf,int4 size,const Address &inaddr); ///< Retrieve bytes in the LoadImage at the given address bool getPcodeInject(const string &name,int4 type,const InjectContext &con,Decoder &decoder); @@ -136,13 +155,11 @@ public: static int4 readToAnyBurst(istream &s); ///< Read the next message protocol marker static bool readBoolStream(istream &s); ///< Read a boolean value from the client static void readStringStream(istream &s,string &res); ///< Receive a string from the client + static bool readStringStream(istream &s,Decoder &decoder); ///< Receive an encoded string from the client static void writeStringStream(ostream &s,const string &msg); ///< Send a string to the client static void readToResponse(istream &s); ///< Read the query response protocol marker static void readResponseEnd(istream &s); ///< Read the ending query response protocol marker static bool readAll(istream &s,Decoder &decoder); ///< Read a whole response as an XML document - static bool readStream(istream &s,Decoder &decoder); ///< Receive an XML document from the client - static uint1 *readPackedStream(istream &s); ///< Read packed p-code op information - static uint1 *readPackedAll(istream &s); ///< Read a whole response as packed p-code op information static void passJavaException(ostream &s,const string &tp,const string &msg); static bool isDynamicSymbolName(const string &nm); ///< Check if name is of form FUN_.. or DAT_.. diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/ghidra_context.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/ghidra_context.cc index 4aba8fe4d8..226c3249c8 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/ghidra_context.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/ghidra_context.cc @@ -19,7 +19,7 @@ const TrackedSet &ContextGhidra::getTrackedSet(const Address &addr) const { cache.clear(); - XmlDecode decoder(glb); + PackedDecode decoder(glb); glb->getTrackedRegisters(addr,decoder); uint4 elemId = decoder.openElement(ELEM_TRACKED_POINTSET); diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/ghidra_process.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/ghidra_process.cc index 32a721b9b9..5a82e0d48f 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/ghidra_process.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/ghidra_process.cc @@ -126,9 +126,9 @@ int4 GhidraCommand::doit(void) throw JavaError("alignment","Missing end of command"); rawAction(); } - catch(XmlError &err) { + catch(DecoderError &err) { string errmsg; - errmsg = "XML processing error: " + err.explain; + errmsg = "Marshaling error: " + err.explain; ghidra->printMessage( errmsg ); } catch(JavaError &err) { @@ -277,8 +277,8 @@ void DecompileAt::loadParameters(void) { GhidraCommand::loadParameters(); - XmlDecode decoder(ghidra); - ArchitectureGhidra::readStream(sin,decoder); // Read encoded address directly from in stream + PackedDecode decoder(ghidra); + ArchitectureGhidra::readStringStream(sin,decoder); // Read encoded address directly from in stream addr = Address::decode(decoder); // Decode for functions address } @@ -305,7 +305,7 @@ void DecompileAt::rawAction(void) sout.write("\000\000\001\016",4); if (fd->isProcComplete()) { - XmlEncode encoder(sout); + PackedEncode encoder(sout); encoder.openElement(ELEM_DOC); if (ghidra->getSendParamMeasures() && (ghidra->allacts.getCurrentName() == "paramid")) { ParamIDAnalysis pidanalysis( fd, true ); // Only send back final prototype @@ -331,8 +331,8 @@ void StructureGraph::loadParameters(void) { GhidraCommand::loadParameters(); - XmlDecode decoder(ghidra); - ArchitectureGhidra::readStream(sin,decoder); + PackedDecode decoder(ghidra); + ArchitectureGhidra::readStringStream(sin,decoder); ingraph.decode(decoder); } @@ -351,7 +351,7 @@ void StructureGraph::rawAction(void) resultgraph.orderBlocks(); sout.write("\000\000\001\016",4); - XmlEncode encoder(sout); + PackedEncode encoder(sout); resultgraph.encode(encoder); sout.write("\000\000\001\017",4); ingraph.clear(); @@ -411,8 +411,17 @@ void SetOptions::loadParameters(void) { GhidraCommand::loadParameters(); - optionsListTag.clear(); - ArchitectureGhidra::readStringStream(sin, optionsListTag); + if (decoder != (Decoder *)0) + delete decoder; + decoder = new PackedDecode(ghidra); + ArchitectureGhidra::readStringStream(sin, *decoder); +} + +SetOptions::~SetOptions(void) + +{ + if (decoder != (Decoder *)0) + delete decoder; } void SetOptions::rawAction(void) @@ -421,12 +430,9 @@ void SetOptions::rawAction(void) res = false; ghidra->resetDefaults(); - DocumentStorage storage; - istringstream s(optionsListTag); - Document *doc = storage.parseDocument(s); - XmlDecode decoder(ghidra,doc->getRoot()); - ghidra->options->decode(decoder); - optionsListTag.clear(); + ghidra->options->decode(*decoder); + delete decoder; + decoder = (Decoder *)0; res = true; } diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/ghidra_process.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/ghidra_process.hh index 34de0d51ac..8c682d80dc 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/ghidra_process.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/ghidra_process.hh @@ -217,16 +217,18 @@ public: /// The decompiler supports configuration of a variety of named options that affect /// everything from how code is transformed to how it is displayed (See ArchOption). /// The command expects 2 string parameters: the encoded integer id of the program, -/// and an XML document containing an \ tag. The \ tag -/// contains one child tag for each option to be configured. +/// and an encoded document containing an \ element. The \ element +/// contains one child element for each option to be configured. /// The command returns a single character message, 't' or 'f', indicating whether the /// configuration succeeded. class SetOptions : public GhidraCommand { - string optionsListTag; ///< The XML tag + Decoder *decoder; ///< The \ decoder virtual void loadParameters(void); virtual void sendResult(void); public: bool res; ///< Set to \b true if the option change succeeded + SetOptions(void) { decoder = (Decoder *)0; res = false; } ///< Constructor + virtual ~SetOptions(void); virtual void rawAction(void); }; diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/ghidra_translate.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/ghidra_translate.cc index c516bd35d0..dd2c0274a2 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/ghidra_translate.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/ghidra_translate.cc @@ -46,14 +46,14 @@ const VarnodeData &GhidraTranslate::getRegister(const string &nm) const map::const_iterator iter = nm2addr.find(nm); if (iter != nm2addr.end()) return (*iter).second; - XmlDecode decoder(glb); + PackedDecode decoder(glb); try { if (!glb->getRegister(nm,decoder)) // Ask Ghidra client about the register throw LowlevelError("No register named "+nm); } - catch(XmlError &err) { + catch(DecoderError &err) { ostringstream errmsg; - errmsg << "Error parsing XML response for query of register: " << nm; + errmsg << "Error decoding response for query of register: " << nm; errmsg << " -- " << err.explain; throw LowlevelError(errmsg.str()); } @@ -100,9 +100,10 @@ int4 GhidraTranslate::oneInstruction(PcodeEmit &emit,const Address &baseaddr) co { int4 offset; - uint1 *doc; + PackedDecode decoder(glb); + bool success; try { - doc = glb->getPcodePacked(baseaddr); // Request p-code for one instruction + success = glb->getPcode(baseaddr,decoder); // Request p-code for one instruction } catch(JavaError &err) { ostringstream s; @@ -110,34 +111,26 @@ int4 GhidraTranslate::oneInstruction(PcodeEmit &emit,const Address &baseaddr) co baseaddr.printRaw(s); throw LowlevelError(s.str()); } - if (doc == (uint1 *)0) { + if (!success) { ostringstream s; s << "No pcode could be generated at address: " << baseaddr.getShortcut(); baseaddr.printRaw(s); throw BadDataError(s.str()); } - uintb val; - const uint1 *ptr = PcodeEmit::unpackOffset(doc+1,val); - offset = (int4)val; - - if (*doc == PcodeEmit::unimpl_tag) { + int4 el = decoder.openElement(); + offset = decoder.readSignedInteger(ATTRIB_OFFSET); + if (el == ELEM_UNIMPL) { ostringstream s; s << "Instruction not implemented in pcode:\n "; baseaddr.printRaw(s); - delete [] doc; throw UnimplError(s.str(),offset); } - int4 spcindex = (int4)(*ptr++ - 0x20); - AddrSpace *spc = getSpace(spcindex); - uintb instoffset; - ptr = PcodeEmit::unpackOffset(ptr,instoffset); - Address pc(spc,instoffset); + Address pc = Address::decode(decoder); - while(*ptr == PcodeEmit::op_tag) - ptr = emit.restorePackedOp(pc,ptr,this); - delete [] doc; + while(decoder.peekElement() != 0) + emit.decodeOp(pc,decoder); return offset; } diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/heritage.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/heritage.cc index 19610dd6d7..dd3b411738 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/heritage.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/heritage.cc @@ -1872,7 +1872,7 @@ void Heritage::splitJoinLevel(vector &lastcombo,vector &ne int4 sizeaccum = 0; int4 j; for(j=recnum;jgetPiece(recnum).size; + sizeaccum += joinrec->getPiece(j).size; if (sizeaccum == curvn->getSize()) { j += 1; break; diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/ifacedecomp.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/ifacedecomp.cc index 3f1c8ee531..9c73542903 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/ifacedecomp.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/ifacedecomp.cc @@ -3472,8 +3472,8 @@ void execute(IfaceStatus *status,IfaceDecompData *dcp) *status->optr << "Low-level ERROR: " << err.explain << endl; dcp->abortFunction(*status->optr); } - catch(XmlError &err) { - *status->optr << "XML ERROR: " << err.explain << endl; + catch(DecoderError &err) { + *status->optr << "Decoding ERROR: " << err.explain << endl; dcp->abortFunction(*status->optr); } status->evaluateError(); diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/inject_ghidra.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/inject_ghidra.cc index f9449b87b1..6ecde748c1 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/inject_ghidra.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/inject_ghidra.cc @@ -48,7 +48,7 @@ void InjectPayloadGhidra::inject(InjectContext &con,PcodeEmit &emit) const { ArchitectureGhidra *ghidra = (ArchitectureGhidra *)con.glb; - XmlDecode decoder(ghidra); + PackedDecode decoder(ghidra); try { if (!ghidra->getPcodeInject(name,type,con,decoder)) throw LowlevelError("Could not retrieve pcode snippet: "+name); @@ -56,12 +56,13 @@ void InjectPayloadGhidra::inject(InjectContext &con,PcodeEmit &emit) const catch(JavaError &err) { throw LowlevelError("Error getting pcode snippet: " + err.explain); } - catch(XmlError &err) { + catch(DecoderError &err) { throw LowlevelError("Error in pcode snippet xml: "+err.explain); } uint4 elemId = decoder.openElement(); + Address addr = Address::decode(decoder); while(decoder.peekElement() != 0) - emit.decodeOp(decoder); + emit.decodeOp(addr,decoder); decoder.closeElement(elemId); } @@ -121,7 +122,7 @@ void ExecutablePcodeGhidra::inject(InjectContext &con,PcodeEmit &emit) const { ArchitectureGhidra *ghidra = (ArchitectureGhidra *)con.glb; - XmlDecode decoder(ghidra); + PackedDecode decoder(ghidra); try { if (!ghidra->getPcodeInject(name,type,con,decoder)) throw LowlevelError("Could not retrieve pcode snippet: "+name); @@ -129,12 +130,13 @@ void ExecutablePcodeGhidra::inject(InjectContext &con,PcodeEmit &emit) const catch(JavaError &err) { throw LowlevelError("Error getting pcode snippet: " + err.explain); } - catch(XmlError &err) { + catch(DecoderError &err) { throw LowlevelError("Error in pcode snippet xml: "+err.explain); } uint4 elemId = decoder.openElement(); + Address addr = Address::decode(decoder); while(decoder.peekElement() != 0) - emit.decodeOp(decoder); + emit.decodeOp(addr,decoder); decoder.closeElement(elemId); } @@ -144,7 +146,7 @@ void ExecutablePcodeGhidra::decode(Decoder &decoder) uint4 elemId = decoder.openElement(); if (elemId != ELEM_PCODE && elemId != ELEM_CASE_PCODE && elemId != ELEM_ADDR_PCODE && elemId != ELEM_DEFAULT_PCODE && elemId != ELEM_SIZE_PCODE) - throw XmlError("Expecting , , , , or "); + throw DecoderError("Expecting , , , , or "); decodePayloadAttributes(decoder); decodePayloadParams(decoder); // Parse the parameters decoder.closeElementSkipping(elemId); // But skip rest of body diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/inject_sleigh.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/inject_sleigh.cc index 9d8951e9d1..0229f4ba9b 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/inject_sleigh.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/inject_sleigh.cc @@ -233,7 +233,7 @@ void ExecutablePcodeSleigh::decode(Decoder &decoder) uint4 elemId = decoder.openElement(); if (elemId != ELEM_PCODE && elemId != ELEM_CASE_PCODE && elemId != ELEM_ADDR_PCODE && elemId != ELEM_DEFAULT_PCODE && elemId != ELEM_SIZE_PCODE) - throw XmlError("Expecting , , , , or "); + throw DecoderError("Expecting , , , , or "); decodePayloadAttributes(decoder); decodePayloadParams(decoder); uint4 subId = decoder.openElement(ELEM_BODY); @@ -269,8 +269,8 @@ void InjectPayloadDynamic::decodeEntry(Decoder &decoder) delete (*iter).second; // Delete any preexisting document addrMap[addr] = doc; } - catch(XmlError &err) { - throw LowlevelError("Error in dynamic payload XML"); + catch(DecoderError &err) { + throw LowlevelError("Error decoding dynamic payload"); } decoder.closeElement(subId); } @@ -284,8 +284,9 @@ void InjectPayloadDynamic::inject(InjectContext &context,PcodeEmit &emit) const const Element *el = (*eiter).second->getRoot(); XmlDecode decoder(glb->translate,el); uint4 rootId = decoder.openElement(ELEM_INST); + Address addr = Address::decode(decoder); while(decoder.peekElement() != 0) - emit.decodeOp(decoder); + emit.decodeOp(addr,decoder); decoder.closeElement(rootId); } diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/marshal.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/marshal.cc index 17a9c11ffb..80090cdca5 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/marshal.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/marshal.cc @@ -16,8 +16,12 @@ #include "marshal.hh" #include "translate.hh" +using namespace PackedFormat; + unordered_map AttributeId::lookupAttributeId; +const int4 PackedDecode::BUFFER_SIZE = 1024; + /// Access static vector of AttributeId objects that are registered during static initialization /// The list itself is created once on the first call to this method. /// \return a reference to the vector @@ -48,7 +52,7 @@ void AttributeId::initialize(void) AttributeId *attrib = thelist[i]; #ifdef CPUI_DEBUG if (lookupAttributeId.find(attrib->name) != lookupAttributeId.end()) - throw XmlError(attrib->name + " attribute registered more than once"); + throw DecoderError(attrib->name + " attribute registered more than once"); #endif lookupAttributeId[attrib->name] = attrib->id; } @@ -88,7 +92,7 @@ void ElementId::initialize(void) ElementId *elem = thelist[i]; #ifdef CPUI_DEBUG if (lookupElementId.find(elem->name) != lookupElementId.end()) - throw XmlError(elem->name + " element registered more than once"); + throw DecoderError(elem->name + " element registered more than once"); #endif lookupElementId[elem->name] = elem->id; } @@ -103,16 +107,6 @@ XmlDecode::~XmlDecode(void) delete document; } -void XmlDecode::clear(void) - -{ - if (document != (Document *)0) - delete document; - document = (Document *)0; - rootElement = (const Element *)0; - attributeIndex = -1; -} - void XmlDecode::ingestStream(istream &s) { @@ -169,7 +163,7 @@ uint4 XmlDecode::openElement(const ElementId &elemId) const Element *el; if (elStack.empty()) { if (rootElement == (const Element *)0) - throw XmlError("Expecting <" + elemId.getName() + "> but reached end of document"); + throw DecoderError("Expecting <" + elemId.getName() + "> but reached end of document"); el = rootElement; rootElement = (const Element *)0; // Only open document once } @@ -181,10 +175,10 @@ uint4 XmlDecode::openElement(const ElementId &elemId) iterStack.back() = ++iter; } else - throw XmlError("Expecting <" + elemId.getName() + "> but no remaining children in current element"); + throw DecoderError("Expecting <" + elemId.getName() + "> but no remaining children in current element"); } if (el->getName() != elemId.getName()) - throw XmlError("Expecting <" + elemId.getName() + "> but got <" + el->getName() + ">"); + throw DecoderError("Expecting <" + elemId.getName() + "> but got <" + el->getName() + ">"); elStack.push_back(el); iterStack.push_back(el->getChildren().begin()); attributeIndex = -1; @@ -197,9 +191,9 @@ void XmlDecode::closeElement(uint4 id) #ifdef CPUI_DEBUG const Element *el = elStack.back(); if (iterStack.back() != el->getChildren().end()) - throw XmlError("Closing element <" + el->getName() + "> with additional children"); + throw DecoderError("Closing element <" + el->getName() + "> with additional children"); if (ElementId::find(el->getName()) != id) - throw XmlError("Trying to close <" + el->getName() + "> with mismatching id"); + throw DecoderError("Trying to close <" + el->getName() + "> with mismatching id"); #endif elStack.pop_back(); iterStack.pop_back(); @@ -212,7 +206,7 @@ void XmlDecode::closeElementSkipping(uint4 id) #ifdef CPUI_DEBUG const Element *el = elStack.back(); if (ElementId::find(el->getName()) != id) - throw XmlError("Trying to close <" + el->getName() + "> with mismatching id"); + throw DecoderError("Trying to close <" + el->getName() + "> with mismatching id"); #endif elStack.pop_back(); iterStack.pop_back(); @@ -251,7 +245,7 @@ int4 XmlDecode::findMatchingAttribute(const Element *el,const string &attribName if (el->getAttributeName(i) == attribName) return i; } - throw XmlError("Attribute missing: " + attribName); + throw DecoderError("Attribute missing: " + attribName); } bool XmlDecode::readBool(void) @@ -355,7 +349,7 @@ AddrSpace *XmlDecode::readSpace(void) string nm = el->getAttributeValue(attributeIndex); AddrSpace *res = spcManager->getSpaceByName(nm); if (res == (AddrSpace *)0) - throw XmlError("Unknown address space name: "+nm); + throw DecoderError("Unknown address space name: "+nm); return res; } @@ -373,7 +367,7 @@ AddrSpace *XmlDecode::readSpace(const AttributeId &attribId) } AddrSpace *res = spcManager->getSpaceByName(nm); if (res == (AddrSpace *)0) - throw XmlError("Unknown address space name: "+nm); + throw DecoderError("Unknown address space name: "+nm); return res; } @@ -472,6 +466,543 @@ void XmlEncode::writeSpace(const AttributeId &attribId,const AddrSpace *spc) a_v(outStream,attribId.getName(),spc->getName()); } +/// The integer is encoded, 7-bits per byte, starting with the most significant 7-bits. +/// The integer is decode from the \e current position, and the position is advanced. +/// \param len is the number of bytes to extract +uint8 PackedDecode::readInteger(int4 len) + +{ + uint8 res = 0; + while(len > 0) { + res <<= RAWDATA_BITSPERBYTE; + res |= (getNextByte(curPos) & RAWDATA_MASK); + len -= 1; + } + return res; +} + +/// The \e current position is reset to the start of the current open element. Attributes are scanned +/// and skipped until the attribute matching the given id is found. The \e current position is set to the +/// start of the matching attribute, in preparation for one of the read*() methods. +/// If the id is not found an exception is thrown. +/// \param attribId is the attribute id to scan for. +void PackedDecode::findMatchingAttribute(const AttributeId &attribId) + +{ + curPos = startPos; + for(;;) { + uint1 header1 = getByte(curPos); + if ((header1 & HEADER_MASK) != ATTRIBUTE) break; + uint4 id = header1 & ELEMENTID_MASK; + if ((header1 & HEADEREXTEND_MASK) != 0) { + id <<= RAWDATA_BITSPERBYTE; + id |= (getBytePlus1(curPos) & RAWDATA_MASK); + } + if (attribId.getId() == id) + return; // Found it + skipAttribute(); + } + throw DecoderError("Attribute " + attribId.getName() + " is not present"); +} + +/// The attribute at the \e current position is scanned enough to determine its length, and the position +/// is advanced to the following byte. +void PackedDecode::skipAttribute(void) + +{ + uint1 header1 = getNextByte(curPos); // Attribute header + if ((header1 & HEADEREXTEND_MASK) != 0) + getNextByte(curPos); // Extra byte for extended id + uint1 typeByte = getNextByte(curPos); // Type (and length) byte + uint1 attribType = typeByte >> TYPECODE_SHIFT; + if (attribType == TYPECODE_BOOLEAN || attribType == TYPECODE_SPECIALSPACE) + return; // has no additional data + uint4 length = readLengthCode(typeByte); // Length of data in bytes + if (attribType == TYPECODE_STRING) { + length = readInteger(length); // Read length field to get final length of string + } + advancePosition(curPos, length); // Skip -length- data +} + +/// This assumes the header and \b type \b byte have been read. Decode type and length info and finish +/// skipping over the attribute so that the next call to getNextAttributeId() is on cut. +/// \param typeByte is the previously scanned type byte +void PackedDecode::skipAttributeRemaining(uint1 typeByte) + +{ + uint1 attribType = typeByte >> TYPECODE_SHIFT; + if (attribType == TYPECODE_BOOLEAN || attribType == TYPECODE_SPECIALSPACE) + return; // has no additional data + uint4 length = readLengthCode(typeByte); // Length of data in bytes + if (attribType == TYPECODE_STRING) { + length = readInteger(length); // Read length field to get final length of string + } + advancePosition(curPos, length); // Skip -length- data +} + +PackedDecode::~PackedDecode(void) + +{ + list::const_iterator iter; + for(iter=inStream.begin();iter!=inStream.end();++iter) { + delete [] (*iter).start; + } +} + +void PackedDecode::ingestStream(istream &s) + +{ + int4 gcount = 0; + while(s.peek() > 0) { + uint1 *buf = new uint1[BUFFER_SIZE + 1]; + inStream.emplace_back(buf,buf+BUFFER_SIZE); + s.get((char *)buf,BUFFER_SIZE+1,'\0'); + gcount = s.gcount(); + } + endPos.seqIter = inStream.begin(); + if (endPos.seqIter != inStream.end()) { + endPos.current = (*endPos.seqIter).start; + endPos.end = (*endPos.seqIter).end; + // Make sure there is at least one character after ingested buffer + if (gcount == BUFFER_SIZE) { + // Last buffer was entirely filled + uint1 *endbuf = new uint1[1]; // Add one more buffer + inStream.emplace_back(endbuf,endbuf + 1); + gcount = 0; + } + uint1 *buf = inStream.back().start; + buf[gcount] = ELEMENT_END; + } +} + +uint4 PackedDecode::peekElement(void) + +{ + uint1 header1 = getByte(endPos); + if ((header1 & HEADER_MASK) != ELEMENT_START) + return 0; + uint4 id = header1 & ELEMENTID_MASK; + if ((header1 & HEADEREXTEND_MASK) != 0) { + id <<= RAWDATA_BITSPERBYTE; + id |= (getBytePlus1(endPos) & RAWDATA_MASK); + } + return id; +} + +uint4 PackedDecode::openElement(void) + +{ + uint1 header1 = getByte(endPos); + if ((header1 & HEADER_MASK) != ELEMENT_START) + return 0; + getNextByte(endPos); + uint4 id = header1 & ELEMENTID_MASK; + if ((header1 & HEADEREXTEND_MASK) != 0) { + id <<= RAWDATA_BITSPERBYTE; + id |= (getNextByte(endPos) & RAWDATA_MASK); + } + startPos = endPos; + curPos = endPos; + header1 = getByte(curPos); + while((header1 & HEADER_MASK) == ATTRIBUTE) { + skipAttribute(); + header1 = getByte(curPos); + } + endPos = curPos; + curPos = startPos; + attributeRead = true; // "Last attribute was read" is vacuously true + return id; +} + +uint4 PackedDecode::openElement(const ElementId &elemId) + +{ + uint4 id = openElement(); + if (id != elemId.getId()) { + if (id == 0) + throw DecoderError("Expecting <" + elemId.getName() + "> but did not scan an element"); + throw DecoderError("Expecting <" + elemId.getName() + "> but id did not match"); + } + return id; +} + +void PackedDecode::closeElement(uint4 id) + +{ + uint1 header1 = getNextByte(endPos); + if ((header1 & HEADER_MASK) != ELEMENT_END) + throw DecoderError("Expecting element close"); + uint4 closeId = header1 & ELEMENTID_MASK; + if ((header1 & HEADEREXTEND_MASK) != 0) { + closeId <<= RAWDATA_BITSPERBYTE; + closeId |= (getNextByte(endPos) & RAWDATA_MASK); + } + if (id != closeId) + throw DecoderError("Did not see expected closing element"); +} + +void PackedDecode::closeElementSkipping(uint4 id) + +{ + vector idstack; + idstack.push_back(id); + do { + uint1 header1 = getByte(endPos) & HEADER_MASK; + if (header1 == ELEMENT_END) { + closeElement(idstack.back()); + idstack.pop_back(); + } + else if (header1 == ELEMENT_START) { + idstack.push_back(openElement()); + } + else + throw DecoderError("Corrupt stream"); + } while(!idstack.empty()); +} + +void PackedDecode::rewindAttributes(void) + +{ + curPos = startPos; + attributeRead = true; +} + +uint4 PackedDecode::getNextAttributeId(void) + +{ + if (!attributeRead) + skipAttribute(); + uint1 header1 = getByte(curPos); + if ((header1 & HEADER_MASK) != ATTRIBUTE) + return 0; + uint4 id = header1 & ELEMENTID_MASK; + if ((header1 & HEADEREXTEND_MASK) != 0) { + id <<= RAWDATA_BITSPERBYTE; + id |= (getBytePlus1(curPos) & RAWDATA_MASK); + } + attributeRead = false; + return id; +} + +bool PackedDecode::readBool(void) + +{ + uint1 header1 = getNextByte(curPos); + if ((header1 & HEADEREXTEND_MASK)!=0) + getNextByte(curPos); + uint1 typeByte = getNextByte(curPos); + if ((typeByte >> TYPECODE_SHIFT) != TYPECODE_BOOLEAN) + throw DecoderError("Expecting boolean attribute"); + attributeRead = true; + return ((typeByte & LENGTHCODE_MASK) != 0); +} + +bool PackedDecode::readBool(const AttributeId &attribId) + +{ + findMatchingAttribute(attribId); + bool res = readBool(); + curPos = startPos; + return res; +} + +intb PackedDecode::readSignedInteger(void) + +{ + uint1 header1 = getNextByte(curPos); + if ((header1 & HEADEREXTEND_MASK)!=0) + getNextByte(curPos); + uint1 typeByte = getNextByte(curPos); + uint4 typeCode = typeByte >> TYPECODE_SHIFT; + intb res; + if (typeCode == TYPECODE_SIGNEDINT_POSITIVE) { + res = readInteger(readLengthCode(typeByte)); + } + else if (typeCode == TYPECODE_SIGNEDINT_NEGATIVE) { + res = readInteger(readLengthCode(typeByte)); + res = -res; + } + else { + skipAttributeRemaining(typeByte); + throw DecoderError("Expecting signed integer attribute"); + } + attributeRead = true; + return res; +} + +intb PackedDecode::readSignedInteger(const AttributeId &attribId) + +{ + findMatchingAttribute(attribId); + intb res = readSignedInteger(); + curPos = startPos; + return res; +} + +uintb PackedDecode::readUnsignedInteger(void) + +{ + uint1 header1 = getNextByte(curPos); + if ((header1 & HEADEREXTEND_MASK)!=0) + getNextByte(curPos); + uint1 typeByte = getNextByte(curPos); + uint4 typeCode = typeByte >> TYPECODE_SHIFT; + uintb res; + if (typeCode == TYPECODE_UNSIGNEDINT) { + res = readInteger(readLengthCode(typeByte)); + } + else { + skipAttributeRemaining(typeByte); + throw DecoderError("Expecting unsigned integer attribute"); + } + attributeRead = true; + return res; +} + +uintb PackedDecode::readUnsignedInteger(const AttributeId &attribId) + +{ + findMatchingAttribute(attribId); + uintb res = readUnsignedInteger(); + curPos = startPos; + return res; +} + +string PackedDecode::readString(void) + +{ + uint1 header1 = getNextByte(curPos); + if ((header1 & HEADEREXTEND_MASK)!=0) + getNextByte(curPos); + uint1 typeByte = getNextByte(curPos); + uint4 typeCode = typeByte >> TYPECODE_SHIFT; + if (typeCode != TYPECODE_STRING) { + skipAttributeRemaining(typeByte); + throw DecoderError("Expecting string attribute"); + } + int4 length = readLengthCode(typeByte); + length = readInteger(length); + + attributeRead = true; + int4 curLen = curPos.end - curPos.current; + if (curLen >= length) { + string res((const char *)curPos.current,length); + advancePosition(curPos, length); + return res; + } + string res((const char *)curPos.current,curLen); + length -= curLen; + advancePosition(curPos, curLen); + while(length > 0) { + curLen = curPos.end - curPos.current; + if (curLen > length) + curLen = length; + res.append((const char *)curPos.current,curLen); + length -= curLen; + advancePosition(curPos, curLen); + } + return res; +} + +string PackedDecode::readString(const AttributeId &attribId) + +{ + findMatchingAttribute(attribId); + string res = readString(); + curPos = startPos; + return res; +} + +AddrSpace *PackedDecode::readSpace(void) + +{ + uint1 header1 = getNextByte(curPos); + if ((header1 & HEADEREXTEND_MASK)!=0) + getNextByte(curPos); + uint1 typeByte = getNextByte(curPos); + uint4 typeCode = typeByte >> TYPECODE_SHIFT; + int4 res; + AddrSpace *spc; + if (typeCode == TYPECODE_ADDRESSSPACE) { + res = readInteger(readLengthCode(typeByte)); + spc = spcManager->getSpace(res); + if (spc == (AddrSpace *)0) + throw DecoderError("Unknown address space index"); + } + else if (typeCode == TYPECODE_SPECIALSPACE) { + uint4 specialCode = readLengthCode(typeByte); + if (specialCode == SPECIALSPACE_STACK) + spc = spcManager->getStackSpace(); + else if (specialCode == SPECIALSPACE_JOIN) { + spc = spcManager->getJoinSpace(); + } + else { + throw DecoderError("Cannot marshal special address space"); + } + } + else { + skipAttributeRemaining(typeByte); + throw DecoderError("Expecting space attribute"); + } + attributeRead = true; + return spc; +} + +AddrSpace *PackedDecode::readSpace(const AttributeId &attribId) + +{ + findMatchingAttribute(attribId); + AddrSpace *res = readSpace(); + curPos = startPos; + return res; +} + +void PackedEncode::writeInteger(uint1 typeByte,uint8 val) + +{ + uint1 lenCode; + int4 sa; + if (val == 0) { + lenCode = 0; + sa = -1; + } + if (val < 0x800000000) { + if (val < 0x200000) { + if (val < 0x80) { + lenCode = 1; // 7-bits + sa = 0; + } + else if (val < 0x4000) { + lenCode = 2; // 14-bits + sa = RAWDATA_BITSPERBYTE; + } + else { + lenCode = 3; // 21-bits + sa = 2*RAWDATA_BITSPERBYTE; + } + } + else if (val < 0x10000000) { + lenCode = 4; // 28-bits + sa = 3*RAWDATA_BITSPERBYTE; + } + else { + lenCode = 5; // 35-bits + sa = 4*RAWDATA_BITSPERBYTE; + } + } + else if (val < 0x2000000000000) { + if (val < 0x40000000000) { + lenCode = 6; + sa = 5*RAWDATA_BITSPERBYTE; + } + else { + lenCode = 7; + sa = 6*RAWDATA_BITSPERBYTE; + } + } + else { + if (val < 0x100000000000000) { + lenCode = 8; + sa = 7*RAWDATA_BITSPERBYTE; + } + else if (val < 0x8000000000000000) { + lenCode = 9; + sa = 8*RAWDATA_BITSPERBYTE; + } + else { + lenCode = 10; + sa = 9*RAWDATA_BITSPERBYTE; + } + } + typeByte |= lenCode; + outStream.put(typeByte); + for(;sa >= 0;sa -= RAWDATA_BITSPERBYTE) { + uint1 piece = (val >> sa) & RAWDATA_MASK; + piece |= RAWDATA_MARKER; + outStream.put(piece); + } +} + +void PackedEncode::openElement(const ElementId &elemId) + +{ + writeHeader(ELEMENT_START, elemId.getId()); +} + +void PackedEncode::closeElement(const ElementId &elemId) + +{ + writeHeader(ELEMENT_END, elemId.getId()); +} + +void PackedEncode::writeBool(const AttributeId &attribId,bool val) + +{ + writeHeader(ATTRIBUTE, attribId.getId()); + uint1 typeByte = val ? ((TYPECODE_BOOLEAN << TYPECODE_SHIFT) | 1) : (TYPECODE_BOOLEAN << TYPECODE_SHIFT); + outStream.put(typeByte); +} + +void PackedEncode::writeSignedInteger(const AttributeId &attribId,intb val) + +{ + writeHeader(ATTRIBUTE, attribId.getId()); + uint1 typeByte; + uint8 num; + if (val < 0) { + typeByte = (TYPECODE_SIGNEDINT_NEGATIVE << TYPECODE_SHIFT); + num = -val; + } + else { + typeByte = (TYPECODE_SIGNEDINT_POSITIVE << TYPECODE_SHIFT); + num = val; + } + writeInteger(typeByte, num); +} + +void PackedEncode::writeUnsignedInteger(const AttributeId &attribId,uintb val) + +{ + writeHeader(ATTRIBUTE, attribId.getId()); + writeInteger((TYPECODE_UNSIGNEDINT << TYPECODE_SHIFT),val); +} + +void PackedEncode::writeString(const AttributeId &attribId,const string &val) + +{ + uint8 length = val.length(); + writeHeader(ATTRIBUTE, attribId.getId()); + writeInteger((TYPECODE_STRING << TYPECODE_SHIFT), length); + outStream.write(val.c_str(), length); +} + +void PackedEncode::writeSpace(const AttributeId &attribId,const AddrSpace *spc) + +{ + writeHeader(ATTRIBUTE, attribId.getId()); + switch(spc->getType()) { + case IPTR_FSPEC: + outStream.put((TYPECODE_SPECIALSPACE << TYPECODE_SHIFT) | SPECIALSPACE_FSPEC); + break; + case IPTR_IOP: + outStream.put((TYPECODE_SPECIALSPACE << TYPECODE_SHIFT) | SPECIALSPACE_IOP); + break; + case IPTR_JOIN: + outStream.put((TYPECODE_SPECIALSPACE << TYPECODE_SHIFT) | SPECIALSPACE_JOIN); + break; + case IPTR_SPACEBASE: + if (spc->isFormalStackSpace()) + outStream.put((TYPECODE_SPECIALSPACE << TYPECODE_SHIFT) | SPECIALSPACE_STACK); + else + outStream.put((TYPECODE_SPECIALSPACE << TYPECODE_SHIFT) | SPECIALSPACE_SPACEBASE); // A secondary register offset space + break; + default: + uint8 spcId = spc->getIndex(); + writeInteger((TYPECODE_ADDRESSSPACE << TYPECODE_SHIFT), spcId); + break; + } +} + // Common attributes. Attributes with multiple uses AttributeId ATTRIB_CONTENT = AttributeId("XMLcontent",1); AttributeId ATTRIB_ALIGN = AttributeId("align",2); @@ -513,4 +1044,4 @@ ElementId ELEM_VAL = ElementId("val",8); ElementId ELEM_VALUE = ElementId("value",9); ElementId ELEM_VOID = ElementId("void",10); -ElementId ELEM_UNKNOWN = ElementId("XMLunknown",251); // Number serves as next open index +ElementId ELEM_UNKNOWN = ElementId("XMLunknown",270); // Number serves as next open index diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/marshal.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/marshal.hh index 7e4202079c..6c8edea813 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/marshal.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/marshal.hh @@ -17,6 +17,7 @@ #define __CPUI_MARSHAL__ #include "xml.hh" +#include #include using namespace std; @@ -100,11 +101,6 @@ public: const AddrSpaceManager *getAddrSpaceManager(void) const { return spcManager; } ///< Get the manager used for address space decoding virtual ~Decoder(void) {} ///< Destructor - /// \brief Clear any current decoding state - /// - /// Allows the same decoder to be reused. Object is ready for new call to ingestStream. - virtual void clear(void)=0; - /// \brief Prepare to decode a given stream /// /// Called once before any decoding. Currently this is assumed to make an internal copy of the stream data, @@ -262,11 +258,6 @@ class Encoder { public: virtual ~Encoder(void) {} ///< Destructor - /// \brief Clear any state associated with the encoder - /// - /// The encoder should be ready to write a new document after this call. - virtual void clear(void)=0; - /// \brief Begin a new element in the encoding /// /// The element will have the given ElementId annotation and becomes the \e current element. @@ -333,7 +324,6 @@ public: XmlDecode(const AddrSpaceManager *spc) : Decoder(spc) { document = (Document *)0; rootElement = (const Element *)0; attributeIndex = -1; } ///< Constructor for use with ingestStream virtual ~XmlDecode(void); - virtual void clear(void); virtual void ingestStream(istream &s); virtual uint4 peekElement(void); virtual uint4 openElement(void); @@ -364,7 +354,6 @@ class XmlEncode : public Encoder { bool elementTagIsOpen; ///< If \b true, new attributes can be written to the current element public: XmlEncode(ostream &s) : outStream(s) { elementTagIsOpen = false; } ///< Construct from a stream - virtual void clear(void) { elementTagIsOpen = false; } virtual void openElement(const ElementId &elemId); virtual void closeElement(const ElementId &elemId); virtual void writeBool(const AttributeId &attribId,bool val); @@ -374,6 +363,206 @@ public: virtual void writeSpace(const AttributeId &attribId,const AddrSpace *spc); }; +/// \brief Protocol format for PackedEncode and PackedDecode classes +/// +/// All bytes in the encoding are expected to be non-zero. Element encoding looks like +/// - 01xiiiii is an element start +/// - 10xiiiii is an element end +/// - 11xiiiii is an attribute start +/// +/// Where iiiii is the (first) 5 bits of the element/attribute id. +/// If x=0, the id is complete. If x=1, the next byte contains 7 more bits of the id: 1iiiiiii +/// +/// After an attribute start, there follows a \e type byte: ttttllll, where the first 4 bits indicate the +/// type of attribute and final 4 bits are a \b length \b code. The types are: +/// - 1 = boolean (lengthcode=0 for false, lengthcode=1 for true) +/// - 2 = positive signed integer +/// - 3 = negative signed integer (stored in negated form) +/// - 4 = unsigned integer +/// - 5 = basic address space (encoded as the integer index of the space) +/// - 6 = special address space (lengthcode 0=>stack 1=>join 2=>fspec 3=>iop) +/// - 7 = string +/// +/// All attribute types except \e boolean and \e special, have an encoded integer after the \e type byte. +/// The \b length \b code, indicates the number bytes used to encode the integer, 7-bits of info per byte, 1iiiiiii. +/// A \b length \b code of zero is used to encode an integer value of 0, with no following bytes. +/// +/// For strings, the integer encoded after the \e type byte, is the actual length of the string. The +/// string data itself is stored immediately after the length integer using UTF8 format. +namespace PackedFormat { + static const uint1 HEADER_MASK = 0xc0; + static const uint1 ELEMENT_START = 0x40; + static const uint1 ELEMENT_END = 0x80; + static const uint1 ATTRIBUTE = 0xc0; + static const uint1 HEADEREXTEND_MASK = 0x20; + static const uint1 ELEMENTID_MASK = 0x1f; + static const uint1 RAWDATA_MASK = 0x7f; + static const int4 RAWDATA_BITSPERBYTE = 7; + static const uint1 RAWDATA_MARKER = 0x80; + static const int4 TYPECODE_SHIFT = 4; + static const uint1 LENGTHCODE_MASK = 0xf; + static const uint1 TYPECODE_BOOLEAN = 1; + static const uint1 TYPECODE_SIGNEDINT_POSITIVE = 2; + static const uint1 TYPECODE_SIGNEDINT_NEGATIVE = 3; + static const uint1 TYPECODE_UNSIGNEDINT = 4; + static const uint1 TYPECODE_ADDRESSSPACE = 5; + static const uint1 TYPECODE_SPECIALSPACE = 6; + static const uint1 TYPECODE_STRING = 7; + static const uint4 SPECIALSPACE_STACK = 0; + static const uint4 SPECIALSPACE_JOIN = 1; + static const uint4 SPECIALSPACE_FSPEC = 2; + static const uint4 SPECIALSPACE_IOP = 3; + static const uint4 SPECIALSPACE_SPACEBASE = 4; +} + +/// \brief A byte-based decoder designed to marshal info to the decompiler efficiently +/// +/// The decoder expects an encoding as described in PackedFormat. When ingested, the stream bytes are +/// held in a sequence of arrays (ByteChunk). During decoding, \b this object maintains a Position in the +/// stream at the start and end of the current open element, and a Position of the next attribute to read to +/// facilitate getNextAttributeId() and associated read*() methods. +class PackedDecode : public Decoder { +public: + static const int4 BUFFER_SIZE; ///< The size, in bytes, of a single cached chunk of the input stream +private: + /// \brief A bounded array of bytes + class ByteChunk { + friend class PackedDecode; + uint1 *start; ///< Start of the byte array + uint1 *end; ///< End of the byte array + public: + ByteChunk(uint1 *s,uint1 *e) { start = s; end = e; } ///< Constructor + }; + /// \brief An iterator into input stream + class Position { + friend class PackedDecode; + list::const_iterator seqIter; ///< Current byte sequence + uint1 *current; ///< Current position in sequence + uint1 *end; ///< End of current sequence + }; + list inStream; ///< Incoming raw data as a sequence of byte arrays + Position startPos; ///< Position at the start of the current open element + Position curPos; ///< Position of the next attribute as returned by getNextAttributeId + Position endPos; ///< Ending position after all attributes in current open element + bool attributeRead; ///< Has the last attribute returned by getNextAttributeId been read + uint1 getByte(Position &pos) { return *pos.current; } ///< Get the byte at the current position, do not advance + uint1 getBytePlus1(Position &pos); ///< Get the byte following the current byte, do not advance position + uint1 getNextByte(Position &pos); ///< Get the byte at the current position and advance to the next byte + void advancePosition(Position &pos,int4 skip); ///< Advance the position by the given number of bytes + uint8 readInteger(int4 len); ///< Read an integer from the \e current position given its length in bytes + uint4 readLengthCode(uint1 typeByte) { return ((uint4)typeByte & PackedFormat::LENGTHCODE_MASK); } ///< Extract length code from type byte + void findMatchingAttribute(const AttributeId &attribId); ///< Find attribute matching the given id in open element + void skipAttribute(void); ///< Skip over the attribute at the current position + void skipAttributeRemaining(uint1 typeByte); ///< Skip over remaining attribute data, after a mismatch +public: + PackedDecode(const AddrSpaceManager *spcManager) : Decoder(spcManager) {} ///< Constructor + virtual ~PackedDecode(void); + virtual void ingestStream(istream &s); + virtual uint4 peekElement(void); + virtual uint4 openElement(void); + virtual uint4 openElement(const ElementId &elemId); + virtual void closeElement(uint4 id); + virtual void closeElementSkipping(uint4 id); + virtual void rewindAttributes(void); + virtual uint4 getNextAttributeId(void); + virtual bool readBool(void); + virtual bool readBool(const AttributeId &attribId); + virtual intb readSignedInteger(void); + virtual intb readSignedInteger(const AttributeId &attribId); + virtual uintb readUnsignedInteger(void); + virtual uintb readUnsignedInteger(const AttributeId &attribId); + virtual string readString(void); + virtual string readString(const AttributeId &attribId); + virtual AddrSpace *readSpace(void); + virtual AddrSpace *readSpace(const AttributeId &attribId); +}; + +/// \brief A byte-based encoder designed to marshal from the decompiler efficiently +/// +/// See PackedDecode for details of the encoding format. +class PackedEncode : public Encoder { + ostream &outStream; ///< The stream receiving the encoded data + void writeHeader(uint1 header,uint4 id); + void writeInteger(uint1 typeByte,uint8 val); +public: + PackedEncode(ostream &s) : outStream(s) {} ///< Construct from a stream + virtual void openElement(const ElementId &elemId); + virtual void closeElement(const ElementId &elemId); + virtual void writeBool(const AttributeId &attribId,bool val); + virtual void writeSignedInteger(const AttributeId &attribId,intb val); + virtual void writeUnsignedInteger(const AttributeId &attribId,uintb val); + virtual void writeString(const AttributeId &attribId,const string &val); + virtual void writeSpace(const AttributeId &attribId,const AddrSpace *spc); +}; + +/// An exception is thrown if the position currently points to the last byte in the stream +/// \param pos is the position in the stream to look ahead from +/// \return the next byte +inline uint1 PackedDecode::getBytePlus1(Position &pos) + +{ + uint1 *ptr = pos.current + 1; + if (ptr == pos.end) { + list::const_iterator iter = pos.seqIter; + ++iter; + if (iter == inStream.end()) + throw DecoderError("Unexpected end of stream"); + ptr = (*iter).start; + } + return *ptr; +} + +/// An exception is thrown if there are no additional bytes in the stream +/// \param pos is the position of the byte +/// \return the byte at the current position +inline uint1 PackedDecode::getNextByte(Position &pos) + +{ + uint1 res = *pos.current; + pos.current += 1; + if (pos.current != pos.end) + return res; + ++pos.seqIter; + if (pos.seqIter == inStream.end()) + throw DecoderError("Unexpected end of stream"); + pos.current = (*pos.seqIter).start; + pos.end = (*pos.seqIter).end; + return res; +} + +/// An exception is thrown of position is advanced past the end of the stream +/// \param pos is the position being advanced +/// \param skip is the number of bytes to advance +inline void PackedDecode::advancePosition(Position &pos,int4 skip) + +{ + while(pos.end - pos.current <= skip) { + skip -= (pos.end - pos.current); + ++pos.seqIter; + if (pos.seqIter == inStream.end()) + throw DecoderError("Unexpected end of stream"); + pos.current = (*pos.seqIter).start; + pos.end = (*pos.seqIter).end; + } + pos.current += skip; +} + +inline void PackedEncode::writeHeader(uint1 header,uint4 id) + +{ + if (id > 0x1f) { + header |= PackedFormat::HEADEREXTEND_MASK; + header |= (id >> PackedFormat::RAWDATA_BITSPERBYTE); + uint1 extendByte = (id & PackedFormat::RAWDATA_MASK) | PackedFormat::RAWDATA_MARKER; + outStream.put(header); + outStream.put(extendByte); + } + else { + header |= id; + outStream.put(header); + } +} + extern ElementId ELEM_UNKNOWN; ///< Special element to represent an element with an unrecognized name extern AttributeId ATTRIB_UNKNOWN; ///< Special attribute to represent an attribute with an unrecognized name extern AttributeId ATTRIB_CONTENT; ///< Special attribute for XML text content of an element diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/op.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/op.hh index 2d29977583..b8b960c55c 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/op.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/op.hh @@ -230,6 +230,7 @@ public: const string &getOpName(void) const { return opcode->getName(); } ///< Return the name of this op void printDebug(ostream &s) const; ///< Print debug description of this op to stream void encode(Encoder &encoder) const; ///< Encode a description of \b this op to stream + /// \brief Retrieve the PcodeOp encoded as the address \e addr static PcodeOp *getOpFromConst(const Address &addr) { return (PcodeOp *)(uintp)addr.getOffset(); } diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/pcoderaw.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/pcoderaw.cc index 5856d7b096..317d4dbf1c 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/pcoderaw.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/pcoderaw.cc @@ -63,3 +63,39 @@ bool VarnodeData::contains(const VarnodeData &op2) const if ((offset + (size-1)) < (op2.offset + (op2.size-1))) return false; return true; } + +/// This assumes the \ element is already open. +/// Decode info suitable for call to PcodeEmit::dump. The output pointer is changed to null if there +/// is no output for this op, otherwise the existing pointer is used to store the output. +/// \param decoder is the stream decoder +/// \param isize is the (preparsed) number of input parameters for the p-code op +/// \param invar is an array of storage for the input Varnodes +/// \param outvar is a (handle) to the storage for the output Varnode +/// \return the p-code op OpCode +OpCode PcodeOpRaw::decode(Decoder &decoder,int4 isize,VarnodeData *invar,VarnodeData **outvar) + +{ + OpCode opcode = (OpCode)decoder.readSignedInteger(ATTRIB_CODE); + uint4 subId = decoder.peekElement(); + if (subId == ELEM_VOID) { + decoder.openElement(); + decoder.closeElement(subId); + *outvar = (VarnodeData *)0; + } + else { + (*outvar)->decode(decoder); + } + for(int4 i=0;igetConstantSpace(); + invar[i].offset = (uintb)(uintp)decoder.readSpace(ATTRIB_NAME); + invar[i].size = sizeof(void *); + decoder.closeElement(subId); + } + else + invar[i].decode(decoder); + } + return opcode; +} diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/pcoderaw.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/pcoderaw.hh index 78db818b14..fd70a956e7 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/pcoderaw.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/pcoderaw.hh @@ -120,6 +120,9 @@ public: void clearInputs(void); ///< Remove all input varnodes to this op int4 numInput(void) const; ///< Get the number of input varnodes to this op VarnodeData *getInput(int4 i) const; ///< Get the i-th input varnode for this op + + /// \brief Decode the raw OpCode and input/output Varnode data for a PcodeOp + static OpCode decode(Decoder &decoder,int4 isize,VarnodeData *invar,VarnodeData **outvar); }; /// The core behavior for this operation is controlled by an OpBehavior object diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/prettyprint.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/prettyprint.cc index 00f891bfae..3e19d00d55 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/prettyprint.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/prettyprint.cc @@ -287,7 +287,7 @@ void EmitMarkup::setOutputStream(ostream *t) if (encoder != (Encoder *)0) delete encoder; s = t; - encoder = new XmlEncode(*s); + encoder = new PackedEncode(*s); } int4 TokenSplit::countbase = 0; diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/sleigh_arch.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/sleigh_arch.cc index b7dc79cf08..c0518ec0ef 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/sleigh_arch.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/sleigh_arch.cc @@ -126,7 +126,7 @@ void SleighArchitecture::loadLanguageDescription(const string &specfile,ostream try { decoder.ingestStream(s); } - catch(XmlError &err) { + catch(DecoderError &err) { errs << "WARNING: Unable to parse sleigh specfile: " << specfile; return; } @@ -246,7 +246,7 @@ void SleighArchitecture::buildSpecFile(DocumentStorage &store) Document *doc = store.openDocument(processorfile); store.registerTag(doc->getRoot()); } - catch(XmlError &err) { + catch(DecoderError &err) { ostringstream serr; serr << "XML error parsing processor specification: " << processorfile; serr << "\n " << err.explain; @@ -263,7 +263,7 @@ void SleighArchitecture::buildSpecFile(DocumentStorage &store) Document *doc = store.openDocument(compilerfile); store.registerTag(doc->getRoot()); } - catch(XmlError &err) { + catch(DecoderError &err) { ostringstream serr; serr << "XML error parsing compiler specification: " << compilerfile; serr << "\n " << err.explain; @@ -281,7 +281,7 @@ void SleighArchitecture::buildSpecFile(DocumentStorage &store) Document *doc = store.openDocument(slafile); store.registerTag(doc->getRoot()); } - catch(XmlError &err) { + catch(DecoderError &err) { ostringstream serr; serr << "XML error parsing SLEIGH file: " << slafile; serr << "\n " << err.explain; diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/slgh_compile.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/slgh_compile.cc index 3ec58b9d05..4a8b301c34 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/slgh_compile.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/slgh_compile.cc @@ -3577,7 +3577,7 @@ static int4 run_xml(const string &filein,SleighCompile &compiler) try { doc = xml_tree(s); } - catch(XmlError &err) { + catch(DecoderError &err) { cerr << "Unable to parse single input file as XML spec: " << filein << endl; exit(1); } diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/space.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/space.cc index c51e58560e..347137a0c6 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/space.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/space.cc @@ -170,7 +170,7 @@ uintb AddrSpace::decodeAttributes(Decoder &decoder,uint4 &size) const offset = decoder.readUnsignedInteger(); } else if (attribId == ATTRIB_SIZE) { - size = decoder.readUnsignedInteger(); + size = decoder.readSignedInteger(); } } if (!foundoffset) @@ -316,7 +316,7 @@ void AddrSpace::decodeBasicAttributes(Decoder &decoder) if (attribId == ATTRIB_INDEX) index = decoder.readSignedInteger(); else if (attribId == ATTRIB_SIZE) - addressSize = decoder.readUnsignedInteger(); + addressSize = decoder.readSignedInteger(); else if (attribId == ATTRIB_WORDSIZE) wordsize = decoder.readUnsignedInteger(); else if (attribId == ATTRIB_BIGENDIAN) { diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/space.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/space.hh index f5e61cf060..92209bd302 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/space.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/space.hh @@ -94,12 +94,13 @@ public: does_deadcode = 4, ///< Dead-code analysis is done on this space programspecific = 8, ///< Space is specific to a particular loadimage reverse_justification = 16, ///< Justification within aligned word is opposite of endianness - overlay = 32, ///< This space is an overlay of another space - overlaybase = 64, ///< This is the base space for overlay space(s) - truncated = 128, ///< Space is truncated from its original size, expect pointers larger than this size - hasphysical = 256, ///< Has physical memory associated with it - is_otherspace = 512, ///< Quick check for the OtherSpace derived class - has_nearpointers = 0x400 ///< Does there exist near pointers into this space + formal_stackspace = 0x20, ///< Space attached to the formal \b stack \b pointer + overlay = 0x40, ///< This space is an overlay of another space + overlaybase = 0x80, ///< This is the base space for overlay space(s) + truncated = 0x100, ///< Space is truncated from its original size, expect pointers larger than this size + hasphysical = 0x200, ///< Has physical memory associated with it + is_otherspace = 0x400, ///< Quick check for the OtherSpace derived class + has_nearpointers = 0x800 ///< Does there exist near pointers into this space }; private: spacetype type; ///< Type of space (PROCESSOR, CONSTANT, INTERNAL, ...) @@ -149,6 +150,7 @@ public: bool hasPhysical(void) const; ///< Return \b true if data is physically stored in this bool isBigEndian(void) const; ///< Return \b true if values in this space are big endian bool isReverseJustified(void) const; ///< Return \b true if alignment justification does not match endianness + bool isFormalStackSpace(void) const; ///< Return \b true if \b this is attached to the formal \b stack \b pointer bool isOverlay(void) const; ///< Return \b true if this is an overlay space bool isOverlayBase(void) const; ///< Return \b true if other spaces overlay this space bool isOtherSpace(void) const; ///< Return \b true if \b this is the \e other address space @@ -446,6 +448,11 @@ inline bool AddrSpace::isReverseJustified(void) const { return ((flags&reverse_justification)!=0); } +/// Currently an architecture can declare only one formal stack pointer. +inline bool AddrSpace::isFormalStackSpace(void) const { + return ((flags&formal_stackspace)!=0); +} + inline bool AddrSpace::isOverlay(void) const { return ((flags&overlay)!=0); } diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/testfunction.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/testfunction.cc index 66f8d3fa2e..6311dba06a 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/testfunction.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/testfunction.cc @@ -108,7 +108,7 @@ void FunctionTestCollection::buildProgram(DocumentStorage &docStorage) try { dcp->conf->init(docStorage); dcp->conf->readLoaderSymbols("::"); // Read in loader symbols - } catch(XmlError &err) { + } catch(DecoderError &err) { errmsg = err.explain; iserror = true; } catch(LowlevelError &err) { diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/translate.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/translate.cc index bf49c06f9e..d604790f5f 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/translate.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/translate.cc @@ -43,7 +43,7 @@ void TruncationTag::decode(Decoder &decoder) } /// Construct a virtual space. This is usually used for the stack -/// space, but multiple such spaces are allowed. +/// space, which is indicated by the \b isFormal parameters, but multiple such spaces are allowed. /// \param m is the manager for this \b program \b specific address space /// \param t is associated processor translator /// \param nm is the name of the space @@ -51,13 +51,16 @@ void TruncationTag::decode(Decoder &decoder) /// \param sz is the size of the space /// \param base is the containing space /// \param dl is the heritage delay +/// \param isFormal is the formal stack space indicator SpacebaseSpace::SpacebaseSpace(AddrSpaceManager *m,const Translate *t,const string &nm,int4 ind,int4 sz, - AddrSpace *base,int4 dl) + AddrSpace *base,int4 dl,bool isFormal) : AddrSpace(m,t,IPTR_SPACEBASE,nm,sz,base->getWordSize(),ind,0,dl) { contain = base; hasbaseregister = false; // No base register assigned yet isNegativeStack = true; // default stack growth + if (isFormal) + setFlags(formal_stackspace); } /// This is a partial constructor, which must be followed up @@ -907,124 +910,24 @@ const FloatFormat *Translate::getFloatFormat(int4 size) const /// A single pcode operation is parsed from an \ element and /// returned to the application via the PcodeEmit::dump method. /// \param decoder is the stream decoder -void PcodeEmit::decodeOp(Decoder &decoder) +void PcodeEmit::decodeOp(const Address &addr,Decoder &decoder) { int4 opcode; + int4 isize; VarnodeData outvar; - VarnodeData invar[30]; + VarnodeData invar[16]; VarnodeData *outptr; uint4 elemId = decoder.openElement(ELEM_OP); - opcode = decoder.readSignedInteger(ATTRIB_CODE); - Address pc = Address::decode(decoder); - uint4 subId = decoder.peekElement(); - if (subId == ELEM_VOID) { - decoder.openElement(); - decoder.closeElement(subId); - outptr = (VarnodeData *)0; - } + isize = decoder.readSignedInteger(ATTRIB_SIZE); + outptr = &outvar; + if (isize <= 16) + opcode = PcodeOpRaw::decode(decoder, isize, invar, &outptr); else { - outvar.decode(decoder); - outptr = &outvar; - } - int4 isize = 0; - while(isize < 30) { - subId = decoder.peekElement(); - if (subId == 0) break; - if (subId == ELEM_SPACEID) { - decoder.openElement(); - invar[isize].space = decoder.getAddrSpaceManager()->getConstantSpace(); - invar[isize].offset = (uintb)(uintp)decoder.readSpace(ATTRIB_NAME); - invar[isize].size = sizeof(void *); - decoder.closeElement(subId); - } - else - invar[isize].decode(decoder); - isize += 1; + vector varStorage(isize,VarnodeData()); + opcode = PcodeOpRaw::decode(decoder, isize, varStorage.data(), &outptr); } decoder.closeElement(elemId); - dump(pc,(OpCode)opcode,outptr,invar,isize); -} - -/// A Helper function for PcodeEmit::restorePackedOp that reads an unsigned offset from a packed stream -/// \param ptr is a pointer into a packed byte stream -/// \param off is where the offset read from the stream is stored -/// \return a pointer to the next unconsumed byte of the stream -const uint1 *PcodeEmit::unpackOffset(const uint1 *ptr,uintb &off) - -{ - uintb res = 0; - int4 shift; - for(shift=0;shift<67;shift+=6) { - uint1 val = *ptr++; - if (val == end_tag) { - off = res; - return ptr; - } - uintb bits = ((uintb)(val-0x20))<getSpace(spcindex); - ptr = unpackOffset(ptr,v.offset); - v.size = (uint4)(*ptr++ - 0x20); - } - else if (tag == spaceid_tag) { - v.space = manage->getConstantSpace(); - int4 spcindex = (int4)(*ptr++ - 0x20); - v.offset = (uintb)(uintp)manage->getSpace( spcindex ); - v.size = sizeof(void *); - } - else - throw LowlevelError("Bad packed VarnodeData"); - return ptr; -} - -/// A convenience method for passing around pcode operations via a special packed format. -/// A single pcode operation is parsed from a byte stream and returned to the application -/// via the PcodeEmit::dump method. -/// \param addr is the address of the instruction that generated this pcode -/// \param ptr is a pointer into a packed byte stream -/// \param manage is the AddrSpace manager object of the associated processor -/// \return a pointer to the next unconsumed byte of the stream -const uint1 *PcodeEmit::restorePackedOp(const Address &addr,const uint1 *ptr,const AddrSpaceManager *manage) - -{ - int4 opcode; - VarnodeData outvar; - VarnodeData invar[30]; - VarnodeData *outptr; - - ptr += 1; // Consume the -op- tag - opcode = (int4)(*ptr++ - 0x20); // Opcode - if (*ptr == void_tag) { - ptr += 1; - outptr = (VarnodeData *)0; - } - else { - ptr = unpackVarnodeData(ptr,outvar,manage); - outptr = &outvar; - } - int4 isize = 0; - while(*ptr != end_tag) { - ptr = unpackVarnodeData(ptr,invar[isize],manage); - isize += 1; - } - ptr += 1; // Consume the end tag dump(addr,(OpCode)opcode,outptr,invar,isize); - return ptr; } diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/translate.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/translate.hh index d41549f42d..4f296ee103 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/translate.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/translate.hh @@ -108,23 +108,7 @@ public: virtual void dump(const Address &addr,OpCode opc,VarnodeData *outvar,VarnodeData *vars,int4 isize)=0; /// Emit pcode directly from an \ element - void decodeOp(Decoder &decoder); - - enum { // Tags for packed pcode format - unimpl_tag = 0x20, - inst_tag = 0x21, - op_tag = 0x22, - void_tag = 0x23, - spaceid_tag = 0x24, - addrsz_tag = 0x25, - end_tag = 0x60 - }; - /// Helper function for unpacking an offset from a pcode byte stream - static const uint1 *unpackOffset(const uint1 *ptr,uintb &off); - /// Helper function for unpacking a varnode from a pcode byte stream - static const uint1 *unpackVarnodeData(const uint1 *ptr,VarnodeData &v,const AddrSpaceManager *trans); - /// Emit pcode directly from a packed byte stream - const uint1 *restorePackedOp(const Address &addr,const uint1 *ptr,const AddrSpaceManager *trans); + void decodeOp(const Address &addr,Decoder &decoder); }; /// \brief Abstract class for emitting disassembly to an application @@ -192,7 +176,7 @@ class SpacebaseSpace : public AddrSpace { VarnodeData baseOrig; ///< Original base register before any truncation void setBaseRegister(const VarnodeData &data,int4 origSize,bool stackGrowth); ///< Set the base register at time space is created public: - SpacebaseSpace(AddrSpaceManager *m,const Translate *t,const string &nm,int4 ind,int4 sz,AddrSpace *base,int4 dl); + SpacebaseSpace(AddrSpaceManager *m,const Translate *t,const string &nm,int4 ind,int4 sz,AddrSpace *base,int4 dl,bool isFormal); SpacebaseSpace(AddrSpaceManager *m,const Translate *t); ///< For use with decode virtual int4 numSpacebase(void) const; virtual const VarnodeData &getSpacebase(int4 i) const; diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/typegrp_ghidra.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/typegrp_ghidra.cc index 69566ab460..351e3df1a1 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/typegrp_ghidra.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/typegrp_ghidra.cc @@ -20,13 +20,14 @@ Datatype *TypeFactoryGhidra::findById(const string &n,uint8 id,int4 sz) { Datatype *ct = TypeFactory::findById(n,id,sz); // Try internal find if (ct != (Datatype *)0) return ct; - XmlDecode decoder(glb); + ArchitectureGhidra *ghidra = (ArchitectureGhidra *)glb; + PackedDecode decoder(ghidra); try { - if (!((ArchitectureGhidra *)glb)->getType(n,id,decoder)) // See if ghidra knows about type + if (!ghidra->getDataType(n,id,decoder)) // See if ghidra knows about type return (Datatype *)0; } - catch(XmlError &err) { - throw LowlevelError("XML error: "+err.explain); + catch(DecoderError &err) { + throw LowlevelError("Decoder error: "+err.explain); } ct = decodeType(decoder); // Parse ghidra's type return ct; diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/xml.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/xml.cc index 51fb5870ca..fc4d1bf616 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/xml.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/xml.cc @@ -2287,7 +2287,7 @@ const string &Element::getAttributeValue(const string &nm) const for(uint4 i=0;i ®list) const {} + virtual void getUserOpNames(vector &res) const {} + virtual int4 instructionLength(const Address &baseaddr) const { return -1; } + virtual int4 oneInstruction(PcodeEmit &emit,const Address &baseaddr) const { return -1; } + virtual int4 printAssembly(AssemblyEmit &emit,const Address &baseaddr) const { return -1; } +}; + +class MarshalTestEnvironment { + DummyTranslate translate; + TestAddrSpaceManager addrSpaceManage; +public: + MarshalTestEnvironment(void); + static void build(void); +}; + +static AddrSpaceManager *spcManager = (AddrSpaceManager *)0; +static MarshalTestEnvironment theEnviron; + +TestAddrSpaceManager::TestAddrSpaceManager(Translate *t) + : AddrSpaceManager() +{ + insertSpace(new AddrSpace(this,t,IPTR_PROCESSOR,"ram",8,1,3,AddrSpace::hasphysical,1)); +} + +MarshalTestEnvironment::MarshalTestEnvironment(void) + : translate(), addrSpaceManage(&translate) +{ + +} + +void MarshalTestEnvironment::build(void) + +{ + spcManager = &theEnviron.addrSpaceManage; +} + +void test_signed_attributes(ostringstream &outStream,Encoder &encoder,Decoder &decoder) + +{ + encoder.openElement(ELEM_ADDR); + encoder.writeSignedInteger(ATTRIB_ALIGN, 3); // 7-bits + encoder.writeSignedInteger(ATTRIB_BIGENDIAN, -0x100); // 14-bits + encoder.writeSignedInteger(ATTRIB_CONSTRUCTOR, 0x1fffff); // 21-bits + encoder.writeSignedInteger(ATTRIB_DESTRUCTOR, -0xabcdefa); // 28-bits + encoder.writeSignedInteger(ATTRIB_EXTRAPOP, 0x300000000); // 35-bits + encoder.writeSignedInteger(ATTRIB_FORMAT, -0x30101010101); // 42-bits + encoder.writeSignedInteger(ATTRIB_ID, 0x123456789011); // 49-bits + encoder.writeSignedInteger(ATTRIB_INDEX, -0xf0f0f0f0f0f0f0); // 56-bits + encoder.writeSignedInteger(ATTRIB_METATYPE, 0x7fffffffffffffff); // 63-bits + encoder.closeElement(ELEM_ADDR); + istringstream inStream(outStream.str()); + decoder.ingestStream(inStream); + uint4 el = decoder.openElement(ELEM_ADDR); + uint4 flags = 0; + for(;;) { + uint4 attribId = decoder.getNextAttributeId(); + if (attribId == 0) break; + if (attribId == ATTRIB_ALIGN) { + int8 val = decoder.readSignedInteger(); + flags |= 1; + ASSERT_EQUALS(val, 3); + } + else if (attribId == ATTRIB_BIGENDIAN) { + int8 val = decoder.readSignedInteger(); + flags |= 2; + ASSERT_EQUALS(val, -0x100); + } + else if (attribId == ATTRIB_CONSTRUCTOR) { + int8 val = decoder.readSignedInteger(); + flags |= 4; + ASSERT_EQUALS(val, 0x1fffff); + } + else if (attribId == ATTRIB_DESTRUCTOR) { + int8 val = decoder.readSignedInteger(); + flags |= 8; + ASSERT_EQUALS(val, -0xabcdefa); + } + else if (attribId == ATTRIB_EXTRAPOP) { + int8 val = decoder.readSignedInteger(); + flags |= 0x10; + ASSERT_EQUALS(val, 0x300000000); + } + else if (attribId == ATTRIB_FORMAT) { + int8 val = decoder.readSignedInteger(); + flags |= 0x20; + ASSERT_EQUALS(val, -0x30101010101); + } + else if (attribId == ATTRIB_ID) { + int8 val = decoder.readSignedInteger(); + flags |= 0x40; + ASSERT_EQUALS(val, 0x123456789011); + } + else if (attribId == ATTRIB_INDEX) { + int8 val = decoder.readSignedInteger(); + flags |= 0x80; + ASSERT_EQUALS(val, -0xf0f0f0f0f0f0f0); + } + else if (attribId == ATTRIB_METATYPE) { + int8 val = decoder.readSignedInteger(); + flags |= 0x100; + ASSERT_EQUALS(val, 0x7fffffffffffffff); + } + } + decoder.closeElement(el); + ASSERT_EQUALS(flags,0x1ff); +} + +void test_unsigned_attributes(ostringstream &outStream,Encoder &encoder,Decoder &decoder) + +{ + encoder.openElement(ELEM_ADDR); + encoder.writeUnsignedInteger(ATTRIB_ALIGN, 3); // 7-bits + encoder.writeUnsignedInteger(ATTRIB_BIGENDIAN, 0x100); // 14-bits + encoder.writeUnsignedInteger(ATTRIB_CONSTRUCTOR, 0x1fffff); // 21-bits + encoder.writeUnsignedInteger(ATTRIB_DESTRUCTOR, 0xabcdefa); // 28-bits + encoder.writeUnsignedInteger(ATTRIB_EXTRAPOP, 0x300000000); // 35-bits + encoder.writeUnsignedInteger(ATTRIB_FORMAT, 0x30101010101); // 42-bits + encoder.writeUnsignedInteger(ATTRIB_ID, 0x123456789011); // 49-bits + encoder.writeUnsignedInteger(ATTRIB_INDEX, 0xf0f0f0f0f0f0f0); // 56-bits + encoder.writeUnsignedInteger(ATTRIB_METATYPE, 0x7fffffffffffffff); // 63-bits + encoder.writeUnsignedInteger(ATTRIB_MODEL, 0x8000000000000000); // 64-bits + encoder.closeElement(ELEM_ADDR); + istringstream inStream(outStream.str()); + decoder.ingestStream(inStream); + uint4 el = decoder.openElement(ELEM_ADDR); + uint8 val = decoder.readUnsignedInteger(ATTRIB_ALIGN); + ASSERT_EQUALS(val, 3); + val = decoder.readUnsignedInteger(ATTRIB_BIGENDIAN); + ASSERT_EQUALS(val, 0x100); + val = decoder.readUnsignedInteger(ATTRIB_CONSTRUCTOR); + ASSERT_EQUALS(val, 0x1fffff); + val = decoder.readUnsignedInteger(ATTRIB_DESTRUCTOR); + ASSERT_EQUALS(val, 0xabcdefa); + val = decoder.readUnsignedInteger(ATTRIB_EXTRAPOP); + ASSERT_EQUALS(val, 0x300000000); + val = decoder.readUnsignedInteger(ATTRIB_FORMAT); + ASSERT_EQUALS(val, 0x30101010101); + val = decoder.readUnsignedInteger(ATTRIB_ID); + ASSERT_EQUALS(val, 0x123456789011); + val = decoder.readUnsignedInteger(ATTRIB_INDEX); + ASSERT_EQUALS(val, 0xf0f0f0f0f0f0f0); + val = decoder.readUnsignedInteger(ATTRIB_METATYPE); + ASSERT_EQUALS(val, 0x7fffffffffffffff); + val = decoder.readUnsignedInteger(ATTRIB_MODEL); + ASSERT_EQUALS(val, 0x8000000000000000); + decoder.closeElement(el); +} + +TEST(marshal_signed_packed) { + ostringstream outStream; + + theEnviron.build(); + PackedEncode encoder(outStream); + PackedDecode decoder(spcManager); + test_signed_attributes(outStream, encoder, decoder); +} + +TEST(marshal_signed_xml) { + ostringstream outStream; + + theEnviron.build(); + XmlEncode encoder(outStream); + XmlDecode decoder(spcManager); + test_signed_attributes(outStream, encoder, decoder); +} + +TEST(marshal_unsigned_packed) { + ostringstream outStream; + + theEnviron.build(); + PackedEncode encoder(outStream); + PackedDecode decoder(spcManager); + test_unsigned_attributes(outStream, encoder, decoder); +} + +TEST(marshal_unsigned_xml) { + ostringstream outStream; + + theEnviron.build(); + XmlEncode encoder(outStream); + XmlDecode decoder(spcManager); + test_unsigned_attributes(outStream, encoder, decoder); +} + +void test_attributes(ostringstream &outStream,Encoder &encoder,Decoder &decoder) + +{ + encoder.openElement(ELEM_DATA); + encoder.writeBool(ATTRIB_ALIGN, true); + encoder.writeBool(ATTRIB_BIGENDIAN, false); + AddrSpace *spc = spcManager->getSpace(3); + encoder.writeSpace(ATTRIB_SPACE, spc); + encoder.writeString(ATTRIB_VAL,""); // Empty string + encoder.writeString(ATTRIB_VALUE,"hello"); + encoder.writeString(ATTRIB_CONSTRUCTOR,"<<\xe2\x82\xac>>&\"bl a h\'\\bleh\n\t"); + string longString = "one to three four five six seven eight nine ten eleven twelve thirteen " + "fourteen fifteen sixteen seventeen eighteen nineteen twenty twenty one " + "blahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblah"; + encoder.writeString(ATTRIB_DESTRUCTOR,longString); + encoder.closeElement(ELEM_DATA); + istringstream inStream(outStream.str()); + decoder.ingestStream(inStream); + uint4 el = decoder.openElement(ELEM_DATA); + bool bval = decoder.readBool(ATTRIB_ALIGN); + ASSERT(bval); + bval = decoder.readBool(ATTRIB_BIGENDIAN); + ASSERT(!bval); + spc = decoder.readSpace(ATTRIB_SPACE); + ASSERT_EQUALS(spc,spcManager->getSpace(3)); + string val = decoder.readString(ATTRIB_VAL); + ASSERT_EQUALS(val,""); + val = decoder.readString(ATTRIB_VALUE); + ASSERT_EQUALS(val,"hello"); + val = decoder.readString(ATTRIB_CONSTRUCTOR); + ASSERT_EQUALS(val,"<<\xe2\x82\xac>>&\"bl a h\'\\bleh\n\t"); + val = decoder.readString(ATTRIB_DESTRUCTOR); + ASSERT_EQUALS(val,longString); + decoder.closeElement(el); +} + +TEST(marshal_attribs_packed) { + ostringstream outStream; + theEnviron.build(); + PackedEncode encoder(outStream); + PackedDecode decoder(spcManager); + test_attributes(outStream, encoder, decoder); +} + +TEST(marshal_attribs_xml) { + ostringstream outStream; + theEnviron.build(); + XmlEncode encoder(outStream); + XmlDecode decoder(spcManager); + test_attributes(outStream, encoder, decoder); +} + +void test_hierarchy(ostringstream &outStream,Encoder &encoder,Decoder &decoder) + +{ + encoder.openElement(ELEM_DATA); // el1 + encoder.writeBool(ATTRIB_CONTENT, true); + encoder.openElement(ELEM_INPUT); // el2 + encoder.openElement(ELEM_OUTPUT); // el3 + encoder.writeSignedInteger(ATTRIB_ID, 0x1000); + encoder.openElement(ELEM_DATA); // el4 + encoder.openElement(ELEM_DATA); // el5 + encoder.openElement(ELEM_OFF); // el6 + encoder.closeElement(ELEM_OFF); + encoder.openElement(ELEM_OFF); // el6 + encoder.writeString(ATTRIB_ID,"blahblah"); + encoder.closeElement(ELEM_OFF); + encoder.openElement(ELEM_OFF); // el6 + encoder.closeElement(ELEM_OFF); + encoder.closeElement(ELEM_DATA); // close el5 + encoder.closeElement(ELEM_DATA); // close el4 + encoder.openElement(ELEM_SYMBOL); // skip4 + encoder.writeUnsignedInteger(ATTRIB_ID, 17); + encoder.openElement(ELEM_TARGET); // skip5 + encoder.closeElement(ELEM_TARGET); // close skip5 + encoder.closeElement(ELEM_SYMBOL); // close skip4 + encoder.closeElement(ELEM_OUTPUT); // close el3 + encoder.closeElement(ELEM_INPUT); // close el2 + encoder.openElement(ELEM_INPUT); // el2 + encoder.closeElement(ELEM_INPUT); + encoder.openElement(ELEM_INPUT); // el2 + encoder.closeElement(ELEM_INPUT); + encoder.openElement(ELEM_INPUT); // el2 + encoder.closeElement(ELEM_INPUT); + encoder.openElement(ELEM_INPUT); // el2 + encoder.closeElement(ELEM_INPUT); + encoder.openElement(ELEM_INPUT); // el2 + encoder.closeElement(ELEM_INPUT); + encoder.openElement(ELEM_INPUT); // el2 + encoder.closeElement(ELEM_INPUT); + encoder.closeElement(ELEM_DATA); // close el1 + istringstream inStream(outStream.str()); + decoder.ingestStream(inStream); + uint4 el1 = decoder.openElement(ELEM_DATA); + // Skip over the bool + uint4 el2 = decoder.openElement(ELEM_INPUT); + uint4 el3 = decoder.openElement(ELEM_OUTPUT); + int4 val = decoder.readSignedInteger(ATTRIB_ID); + ASSERT_EQUALS(val, 0x1000); + uint4 el4 = decoder.peekElement(); + ASSERT_EQUALS(el4, ELEM_DATA.getId()); + decoder.openElement(); + uint4 el5 = decoder.openElement(); + ASSERT_EQUALS(el5, ELEM_DATA.getId()); + uint4 el6 = decoder.openElement(ELEM_OFF); + decoder.closeElement(el6); + el6 = decoder.openElement(ELEM_OFF); + decoder.closeElement(el6); + el6 = decoder.openElement(ELEM_OFF); + decoder.closeElement(el6); + decoder.closeElement(el5); + decoder.closeElement(el4); + decoder.closeElementSkipping(el3); + decoder.closeElement(el2); + el2 = decoder.openElement(ELEM_INPUT); + decoder.closeElement(el2); + el2 = decoder.openElement(ELEM_INPUT); + decoder.closeElement(el2); + decoder.closeElementSkipping(el1); +} + +TEST(marshal_hierarchy_packed) { + ostringstream outStream; + theEnviron.build(); + PackedEncode encoder(outStream); + PackedDecode decoder(spcManager); + test_hierarchy(outStream, encoder, decoder); +} + +TEST(marshal_hierarchy_xml) { + ostringstream outStream; + theEnviron.build(); + XmlEncode encoder(outStream); + XmlDecode decoder(spcManager); + test_hierarchy(outStream, encoder, decoder); +} + +void test_unexpected_eof(ostringstream &outStream,Encoder &encoder,Decoder &decoder) + +{ + encoder.openElement(ELEM_DATA); + encoder.openElement(ELEM_INPUT); + encoder.writeString(ATTRIB_NAME,"hello"); + encoder.closeElement(ELEM_INPUT); + bool sawUnexpectedError = false; + try { + istringstream inStream(outStream.str()); + decoder.ingestStream(inStream); + uint4 el1 = decoder.openElement(ELEM_DATA); + uint4 el2 = decoder.openElement(ELEM_INPUT); + decoder.closeElement(el2); + decoder.closeElement(el1); + } catch(DecoderError &err) { + sawUnexpectedError = true; + } + ASSERT(sawUnexpectedError); +} + +TEST(marshal_unexpected_packed) { + ostringstream outStream; + theEnviron.build(); + PackedEncode encoder(outStream); + PackedDecode decoder(spcManager); + test_unexpected_eof(outStream, encoder, decoder); +} + +TEST(marshal_unexpected_xml) { + ostringstream outStream; + theEnviron.build(); + XmlEncode encoder(outStream); + XmlDecode decoder(spcManager); + test_unexpected_eof(outStream, encoder, decoder); +} + +void test_noremaining(ostringstream &outStream,Encoder &encoder,Decoder &decoder) + +{ + encoder.openElement(ELEM_INPUT); + encoder.openElement(ELEM_OFF); + encoder.closeElement(ELEM_OFF); + encoder.closeElement(ELEM_INPUT); + istringstream inStream(outStream.str()); + decoder.ingestStream(inStream); + decoder.openElement(ELEM_INPUT); + uint4 el2 = decoder.openElement(ELEM_OFF); + decoder.closeElement(el2); + bool sawNoRemaining = false; + try { + el2 = decoder.openElement(ELEM_OFF); + } catch(DecoderError &err) { + sawNoRemaining = true; + } + ASSERT(sawNoRemaining); +} + +void test_openmismatch(ostringstream &outStream,Encoder &encoder,Decoder &decoder) + +{ + encoder.openElement(ELEM_INPUT); + encoder.openElement(ELEM_OFF); + encoder.closeElement(ELEM_OFF); + encoder.closeElement(ELEM_INPUT); + istringstream inStream(outStream.str()); + decoder.ingestStream(inStream); + decoder.openElement(ELEM_INPUT); + bool sawOpenMismatch = false; + try { + decoder.openElement(ELEM_OUTPUT); + } catch(DecoderError &err) { + sawOpenMismatch = true; + } + ASSERT(sawOpenMismatch); +} + +void test_closemismatch(ostringstream &outStream,Encoder &encoder,Decoder &decoder) + +{ + encoder.openElement(ELEM_INPUT); + encoder.openElement(ELEM_OFF); + encoder.closeElement(ELEM_OFF); + encoder.closeElement(ELEM_INPUT); + istringstream inStream(outStream.str()); + decoder.ingestStream(inStream); + uint4 el1 = decoder.openElement(ELEM_INPUT); + bool sawCloseMismatch = false; + try { + decoder.closeElement(el1); + } catch(DecoderError &err) { + sawCloseMismatch = true; + } + ASSERT(sawCloseMismatch); +} + +TEST(marshal_noremaining_packed) { + ostringstream outStream; + theEnviron.build(); + PackedEncode encoder(outStream); + PackedDecode decoder(spcManager); + test_noremaining(outStream, encoder, decoder); +} + +TEST(marshal_noremaining_xml) { + ostringstream outStream; + theEnviron.build(); + XmlEncode encoder(outStream); + XmlDecode decoder(spcManager); + test_noremaining(outStream, encoder, decoder); +} + +TEST(marshal_openmismatch_packed) { + ostringstream outStream; + theEnviron.build(); + PackedEncode encoder(outStream); + PackedDecode decoder(spcManager); + test_openmismatch(outStream, encoder, decoder); +} + +TEST(marshal_openmismatch_xml) { + ostringstream outStream; + theEnviron.build(); + XmlEncode encoder(outStream); + XmlDecode decoder(spcManager); + test_openmismatch(outStream, encoder, decoder); +} + +TEST(marshal_closemismatch_packed) { + ostringstream outStream; + theEnviron.build(); + PackedEncode encoder(outStream); + PackedDecode decoder(spcManager); + test_closemismatch(outStream, encoder, decoder); +} + +TEST(marshal_bufferpad) { + ASSERT_EQUALS(PackedDecode::BUFFER_SIZE,1024); + ostringstream s; + PackedEncode encoder(s); + encoder.openElement(ELEM_INPUT); // 1-byte + for(int4 i=0;i<511;++i) // 1022-bytes + encoder.writeBool(ATTRIB_CONTENT, (i&1) == 0); + encoder.closeElement(ELEM_INPUT); + ASSERT_EQUALS(s.str().length(),1024); // Encoding should exactly fill one buffer + istringstream s2(s.str()); + PackedDecode decoder(spcManager); + decoder.ingestStream(s2); + uint4 el = decoder.openElement(ELEM_INPUT); + for(int4 i=0;i<511;++i) { + uint4 attribId = decoder.getNextAttributeId(); + ASSERT_EQUALS(attribId,ATTRIB_CONTENT.getId()); + bool val = decoder.readBool(); + ASSERT_EQUALS(val, (i&1) == 0); + } + uint4 nextel = decoder.peekElement(); + ASSERT_EQUALS(nextel,0); + decoder.closeElement(el); +} diff --git a/Ghidra/Features/Decompiler/src/decompile/unittests/testtypes.cc b/Ghidra/Features/Decompiler/src/decompile/unittests/testtypes.cc index 81b1b7f669..4ce7cdde65 100644 --- a/Ghidra/Features/Decompiler/src/decompile/unittests/testtypes.cc +++ b/Ghidra/Features/Decompiler/src/decompile/unittests/testtypes.cc @@ -31,7 +31,7 @@ public: static void build(void); }; -TypeTestEnvironment theEnviron; +static TypeTestEnvironment theEnviron; TypeTestEnvironment::TypeTestEnvironment(void) diff --git a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/ClangBreak.java b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/ClangBreak.java index 4624a49fbe..60e1624db1 100644 --- a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/ClangBreak.java +++ b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/ClangBreak.java @@ -36,8 +36,8 @@ public class ClangBreak extends ClangToken { } @Override - public void decode(Decoder decoder, PcodeFactory pfactory) throws PcodeXMLException { + public void decode(Decoder decoder, PcodeFactory pfactory) throws DecoderException { indent = (int) decoder.readSignedInteger(AttributeId.ATTRIB_INDENT); - super.decode(decoder, pfactory); + setText(""); } } diff --git a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/ClangCommentToken.java b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/ClangCommentToken.java index 63e86b18da..a930c9b4fc 100644 --- a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/ClangCommentToken.java +++ b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/ClangCommentToken.java @@ -56,7 +56,7 @@ public class ClangCommentToken extends ClangToken { } @Override - public void decode(Decoder decoder, PcodeFactory pfactory) throws PcodeXMLException { + public void decode(Decoder decoder, PcodeFactory pfactory) throws DecoderException { AddressSpace spc = decoder.readSpace(AttributeId.ATTRIB_SPACE); long offset = decoder.readUnsignedInteger(AttributeId.ATTRIB_OFF); srcaddr = spc.getAddress(offset); diff --git a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/ClangFieldToken.java b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/ClangFieldToken.java index 2ef6deb630..665d0b5a77 100644 --- a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/ClangFieldToken.java +++ b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/ClangFieldToken.java @@ -54,7 +54,7 @@ public class ClangFieldToken extends ClangToken { } @Override - public void decode(Decoder decoder, PcodeFactory pfactory) throws PcodeXMLException { + public void decode(Decoder decoder, PcodeFactory pfactory) throws DecoderException { String datatypestring = null; long id = 0; for (;;) { diff --git a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/ClangFuncNameToken.java b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/ClangFuncNameToken.java index 5a0bdf65d0..bb6c1dd6cc 100644 --- a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/ClangFuncNameToken.java +++ b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/ClangFuncNameToken.java @@ -66,7 +66,7 @@ public class ClangFuncNameToken extends ClangToken { } @Override - public void decode(Decoder decoder, PcodeFactory pfactory) throws PcodeXMLException { + public void decode(Decoder decoder, PcodeFactory pfactory) throws DecoderException { for (;;) { int attribId = decoder.getNextAttributeId(); if (attribId == 0) { diff --git a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/ClangLabelToken.java b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/ClangLabelToken.java index 5fd9230097..bfddd2d921 100644 --- a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/ClangLabelToken.java +++ b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/ClangLabelToken.java @@ -43,7 +43,7 @@ public class ClangLabelToken extends ClangToken { } @Override - public void decode(Decoder decoder, PcodeFactory pfactory) throws PcodeXMLException { + public void decode(Decoder decoder, PcodeFactory pfactory) throws DecoderException { AddressSpace spc = decoder.readSpace(AttributeId.ATTRIB_SPACE); long offset = decoder.readUnsignedInteger(AttributeId.ATTRIB_OFF); blockaddr = spc.getAddress(offset); diff --git a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/ClangMarkup.java b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/ClangMarkup.java index d298fd52bf..e6714ba948 100644 --- a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/ClangMarkup.java +++ b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/ClangMarkup.java @@ -36,7 +36,7 @@ public abstract class ClangMarkup { // Placeholder for CLANG XML identifiers public static final String GLOBAL_COLOR = "global"; public static ClangTokenGroup buildClangTree(Decoder decoder, HighFunction hfunc) - throws PcodeXMLException { + throws DecoderException { ClangTokenGroup docroot; int el = decoder.openElement(); if (el == ElementId.ELEM_FUNCTION.id()) { diff --git a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/ClangOpToken.java b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/ClangOpToken.java index 3b78ec7bde..6c0f9d8e0d 100644 --- a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/ClangOpToken.java +++ b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/ClangOpToken.java @@ -61,7 +61,7 @@ public class ClangOpToken extends ClangToken { } @Override - public void decode(Decoder decoder, PcodeFactory pfactory) throws PcodeXMLException { + public void decode(Decoder decoder, PcodeFactory pfactory) throws DecoderException { for (;;) { int attribId = decoder.getNextAttributeId(); if (attribId == 0) { diff --git a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/ClangReturnType.java b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/ClangReturnType.java index 74f2f5282a..7c7546e573 100644 --- a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/ClangReturnType.java +++ b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/ClangReturnType.java @@ -50,7 +50,7 @@ public class ClangReturnType extends ClangTokenGroup { } @Override - public void decode(Decoder decoder, PcodeFactory pfactory) throws PcodeXMLException { + public void decode(Decoder decoder, PcodeFactory pfactory) throws DecoderException { for (;;) { int attribId = decoder.getNextAttributeId(); if (attribId == 0) { diff --git a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/ClangStatement.java b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/ClangStatement.java index 0b25fb38d2..555319dc10 100644 --- a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/ClangStatement.java +++ b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/ClangStatement.java @@ -48,7 +48,7 @@ public class ClangStatement extends ClangTokenGroup { } @Override - public void decode(Decoder decoder, PcodeFactory pfactory) throws PcodeXMLException { + public void decode(Decoder decoder, PcodeFactory pfactory) throws DecoderException { for (;;) { int attribId = decoder.getNextAttributeId(); if (attribId == 0) { diff --git a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/ClangSyntaxToken.java b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/ClangSyntaxToken.java index ae7177d84c..0dcf78d048 100644 --- a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/ClangSyntaxToken.java +++ b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/ClangSyntaxToken.java @@ -56,7 +56,7 @@ public class ClangSyntaxToken extends ClangToken { } @Override - public void decode(Decoder decoder, PcodeFactory pfactory) throws PcodeXMLException { + public void decode(Decoder decoder, PcodeFactory pfactory) throws DecoderException { for (;;) { int attribId = decoder.getNextAttributeId(); if (attribId == 0) { diff --git a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/ClangToken.java b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/ClangToken.java index 57deac4add..c26878a937 100644 --- a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/ClangToken.java +++ b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/ClangToken.java @@ -155,7 +155,7 @@ public class ClangToken implements ClangNode { this.text = text; } - public void decode(Decoder decoder, PcodeFactory pfactory) throws PcodeXMLException { + public void decode(Decoder decoder, PcodeFactory pfactory) throws DecoderException { String col = null; for (;;) { int attribId = decoder.getNextAttributeId(); @@ -177,7 +177,7 @@ public class ClangToken implements ClangNode { } static public ClangToken buildToken(int node, ClangNode par, Decoder decoder, - PcodeFactory pfactory) throws PcodeXMLException { + PcodeFactory pfactory) throws DecoderException { ClangToken token = null; if (node == ELEM_VARIABLE.id()) { token = new ClangVariableToken(par); @@ -207,7 +207,7 @@ public class ClangToken implements ClangNode { token = new ClangFieldToken(par); } else { - throw new PcodeXMLException("Expecting token element"); + throw new DecoderException("Expecting token element"); } token.decode(decoder, pfactory); return token; diff --git a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/ClangTokenGroup.java b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/ClangTokenGroup.java index 9d161c56ec..6309c1d448 100644 --- a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/ClangTokenGroup.java +++ b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/ClangTokenGroup.java @@ -106,7 +106,7 @@ public class ClangTokenGroup implements ClangNode, Iterable { } } - public void decode(Decoder decoder, PcodeFactory pfactory) throws PcodeXMLException { + public void decode(Decoder decoder, PcodeFactory pfactory) throws DecoderException { for (;;) { int elem = decoder.openElement(); if (elem == 0) { diff --git a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/ClangTypeToken.java b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/ClangTypeToken.java index 1e41ea71f9..515b03f444 100644 --- a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/ClangTypeToken.java +++ b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/ClangTypeToken.java @@ -51,7 +51,7 @@ public class ClangTypeToken extends ClangToken { } @Override - public void decode(Decoder decoder, PcodeFactory pfactory) throws PcodeXMLException { + public void decode(Decoder decoder, PcodeFactory pfactory) throws DecoderException { long id = 0; for (;;) { int attribId = decoder.getNextAttributeId(); diff --git a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/ClangVariableDecl.java b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/ClangVariableDecl.java index 6331aa9013..99f55c2f49 100644 --- a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/ClangVariableDecl.java +++ b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/ClangVariableDecl.java @@ -51,7 +51,7 @@ public class ClangVariableDecl extends ClangTokenGroup { } @Override - public void decode(Decoder decoder, PcodeFactory pfactory) throws PcodeXMLException { + public void decode(Decoder decoder, PcodeFactory pfactory) throws DecoderException { long symref = decoder.readUnsignedInteger(AttributeId.ATTRIB_SYMREF); super.decode(decoder, pfactory); HighSymbol sym = pfactory.getSymbol(symref); diff --git a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/ClangVariableToken.java b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/ClangVariableToken.java index 8afc3a8d5e..3f96d65d5c 100644 --- a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/ClangVariableToken.java +++ b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/ClangVariableToken.java @@ -86,7 +86,7 @@ public class ClangVariableToken extends ClangToken { } @Override - public void decode(Decoder decoder, PcodeFactory pfactory) throws PcodeXMLException { + public void decode(Decoder decoder, PcodeFactory pfactory) throws DecoderException { for (;;) { int attribId = decoder.getNextAttributeId(); if (attribId == 0) { diff --git a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/DecompInterface.java b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/DecompInterface.java index f65994aa5e..d8f01cdea2 100644 --- a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/DecompInterface.java +++ b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/DecompInterface.java @@ -27,7 +27,6 @@ import generic.jar.ResourceFile; import ghidra.app.plugin.processors.sleigh.SleighLanguage; import ghidra.app.plugin.processors.sleigh.UniqueLayout; import ghidra.program.model.address.Address; -import ghidra.program.model.address.AddressFactory; import ghidra.program.model.lang.*; import ghidra.program.model.listing.Function; import ghidra.program.model.listing.Program; @@ -49,7 +48,7 @@ import ghidra.util.task.TaskMonitor; * DecompInterface ifc = new DecompInterface(); * * // Setup any options or other initialization - * ifc.setOptions(xmlOptions); // Inform interface of global options + * ifc.setOptions(options); // Inform interface of global options * // ifc.toggleSyntaxTree(false); // Don't produce syntax trees * // ifc.toggleCCode(false); // Don't produce C code * // ifc.setSimplificationStyle("normalize"); // Alternate analysis style @@ -88,7 +87,9 @@ public class DecompInterface { protected CompilerSpec compilerSpec; protected DecompileProcess decompProcess; protected DecompileCallback decompCallback; - protected Encoder encoder; + protected PackedEncode paramEncode; // Encoder for decompiler command parameters + protected Decoder decoder; // Decoder for the Decompiler's main outputs + protected StringIngest stringResponse = new StringIngest(); // Ingester for simple responses private DecompileDebug debug; protected CancelledListener monitorListener = new CancelledListener() { @Override @@ -99,7 +100,7 @@ public class DecompInterface { // Initialization state private String actionname; // Name of simplification action - private DecompileOptions xmlOptions; // Current decompiler options + private DecompileOptions options; // Current decompiler options private boolean printSyntaxTree; // Whether syntax tree is returned private boolean printCCode; // Whether C code is returned private boolean sendParamMeasures; // Whether Parameter Measures are returned @@ -110,8 +111,9 @@ public class DecompInterface { pcodelanguage = null; dtmanage = null; decompCallback = null; - xmlOptions = null; - encoder = null; + options = null; + paramEncode = null; + decoder = null; debug = null; decompileMessage = ""; compilerSpec = null; @@ -215,31 +217,33 @@ public class DecompInterface { decompProcess = DecompileProcessFactory.get(); } long uniqueBase = UniqueLayout.SLEIGH_BASE.getOffset(pcodelanguage); - encoder.clear(); - pcodelanguage.encodeTranslator(encoder, program.getAddressFactory(), uniqueBase); - String tspec = encoder.toString(); - encoder.clear(); - dtmanage.encodeCoreTypes(encoder); - String coretypes = encoder.toString(); + XmlEncode xmlEncode = new XmlEncode(); + pcodelanguage.encodeTranslator(xmlEncode, program.getAddressFactory(), uniqueBase); + String tspec = xmlEncode.toString(); + xmlEncode.clear(); + dtmanage.encodeCoreTypes(xmlEncode); + String coretypes = xmlEncode.toString(); SleighLanguageDescription sleighdescription = (SleighLanguageDescription) pcodelanguage.getLanguageDescription(); ResourceFile pspecfile = sleighdescription.getSpecFile(); String pspecxml = fileToString(pspecfile); - XmlEncode xmlEncode = new XmlEncode(); + xmlEncode.clear(); compilerSpec.encode(xmlEncode); String cspecxml = xmlEncode.toString(); decompCallback.setNativeMessage(null); - decompProcess.registerProgram(decompCallback, pspecxml, cspecxml, tspec, coretypes); + decompProcess.registerProgram(decompCallback, pspecxml, cspecxml, tspec, coretypes, + program); String nativeMessage = decompCallback.getNativeMessage(); if ((nativeMessage != null) && (nativeMessage.length() != 0)) { throw new IOException("Could not register program: " + nativeMessage); } - if (xmlOptions != null) { - decompProcess.setMaxResultSize(xmlOptions.getMaxPayloadMBytes()); - if (!decompProcess.sendCommand1Param("setOptions", xmlOptions.getXML(this)) - .toString() - .equals("t")) { + if (options != null) { + paramEncode.clear(); + options.encode(paramEncode, this); + decompProcess.setMaxResultSize(options.getMaxPayloadMBytes()); + decompProcess.sendCommand1Param("setOptions", paramEncode, stringResponse); + if (!stringResponse.toString().equals("t")) { throw new IOException("Did not accept decompiler options"); } } @@ -247,35 +251,32 @@ public class DecompInterface { throw new IOException("Decompile action not specified"); } if (!actionname.equals("decompile")) { - if (!decompProcess.sendCommand2Params("setAction", actionname, "") - .toString() - .equals("t")) { + decompProcess.sendCommand2Params("setAction", actionname, "", stringResponse); + if (!stringResponse.toString().equals("t")) { throw new IOException("Could not set decompile action"); } } if (!printSyntaxTree) { - if (!decompProcess.sendCommand2Params("setAction", "", "notree") - .toString() - .equals("t")) { + decompProcess.sendCommand2Params("setAction", "", "notree", stringResponse); + if (!stringResponse.toString().equals("t")) { throw new IOException("Could not turn off syntax tree"); } } if (!printCCode) { - if (!decompProcess.sendCommand2Params("setAction", "", "noc").toString().equals("t")) { + decompProcess.sendCommand2Params("setAction", "", "noc", stringResponse); + if (!stringResponse.toString().equals("t")) { throw new IOException("Could not turn off C printing"); } } if (sendParamMeasures) { - if (!decompProcess.sendCommand2Params("setAction", "", "parammeasures") - .toString() - .equals("t")) { + decompProcess.sendCommand2Params("setAction", "", "parammeasures", stringResponse); + if (!stringResponse.toString().equals("t")) { throw new IOException("Could not turn on sending of parameter measures"); } } if (jumpLoad) { - if (!decompProcess.sendCommand2Params("setAction", "", "jumpload") - .toString() - .equals("t")) { + decompProcess.sendCommand2Params("setAction", "", "jumpload", stringResponse); + if (!stringResponse.toString().equals("t")) { throw new IOException("Could not turn on jumptable loads"); } } @@ -322,7 +323,8 @@ public class DecompInterface { compilerSpec = spec; dtmanage = new PcodeDataTypeManager(prog); - encoder = new XmlEncode(); + paramEncode = new PackedEncode(); + decoder = new PackedDecode(prog.getAddressFactory()); try { decompCallback = new DecompileCallback(prog, pcodelanguage, program.getCompilerSpec(), dtmanage); @@ -344,7 +346,8 @@ public class DecompInterface { } program = null; decompCallback = null; - encoder = null; + paramEncode = null; + decoder = null; return false; } @@ -360,7 +363,8 @@ public class DecompInterface { if (program != null) { program = null; decompCallback = null; - encoder = null; + paramEncode = null; + decoder = null; try { if ((decompProcess != null) && decompProcess.isReady()) { decompProcess.deregisterProgram(); @@ -422,9 +426,8 @@ public class DecompInterface { } try { verifyProcess(); - return decompProcess.sendCommand2Params("setAction", actionstring, "") - .toString() - .equals("t"); + decompProcess.sendCommand2Params("setAction", actionstring, "", stringResponse); + return stringResponse.toString().equals("t"); } catch (IOException e) { // don't care @@ -460,9 +463,8 @@ public class DecompInterface { String printstring = val ? "tree" : "notree"; try { verifyProcess(); - return decompProcess.sendCommand2Params("setAction", "", printstring) - .toString() - .equals("t"); + decompProcess.sendCommand2Params("setAction", "", printstring, stringResponse); + return stringResponse.toString().equals("t"); } catch (IOException e) { // don't care @@ -499,9 +501,8 @@ public class DecompInterface { String printstring = val ? "c" : "noc"; try { verifyProcess(); - return decompProcess.sendCommand2Params("setAction", "", printstring) - .toString() - .equals("t"); + decompProcess.sendCommand2Params("setAction", "", printstring, stringResponse); + return stringResponse.toString().equals("t"); } catch (IOException e) { // don't care @@ -537,9 +538,8 @@ public class DecompInterface { String printstring = val ? "parammeasures" : "noparammeasures"; try { verifyProcess(); - return decompProcess.sendCommand2Params("setAction", "", printstring) - .toString() - .equals("t"); + decompProcess.sendCommand2Params("setAction", "", printstring, stringResponse); + return stringResponse.toString().equals("t"); } catch (IOException e) { // don't care @@ -568,9 +568,8 @@ public class DecompInterface { String jumpstring = val ? "jumpload" : "nojumpload"; try { verifyProcess(); - return decompProcess.sendCommand2Params("setAction", "", jumpstring) - .toString() - .equals("t"); + decompProcess.sendCommand2Params("setAction", "", jumpstring, stringResponse); + return stringResponse.toString().equals("t"); } catch (IOException e) { // don't care @@ -593,11 +592,11 @@ public class DecompInterface { * recovering from decompiler process crash, the interface * keeps the options object around and automatically * sends it to the new decompiler process. - * @param xmloptions the new (or changed) option object + * @param options the new (or changed) option object * @return true if the decompiler process accepted the new options */ - public synchronized boolean setOptions(DecompileOptions xmloptions) { - this.xmlOptions = xmloptions; + public synchronized boolean setOptions(DecompileOptions options) { + this.options = options; decompileMessage = ""; // Property can be set before process exists if (decompProcess == null) { @@ -605,10 +604,11 @@ public class DecompInterface { } try { verifyProcess(); - decompProcess.setMaxResultSize(xmlOptions.getMaxPayloadMBytes()); - return decompProcess.sendCommand1Param("setOptions", xmloptions.getXML(this)) - .toString() - .equals("t"); + paramEncode.clear(); + options.encode(paramEncode, this); + decompProcess.setMaxResultSize(options.getMaxPayloadMBytes()); + decompProcess.sendCommand1Param("setOptions", paramEncode, stringResponse); + return stringResponse.toString().equals("t"); } catch (IOException e) { // don't care @@ -626,7 +626,7 @@ public class DecompInterface { * @return options that will be passed to the decompiler */ public synchronized DecompileOptions getOptions() { - return this.xmlOptions; + return this.options; } /** @@ -643,8 +643,8 @@ public class DecompInterface { int res = -1; try { if ((decompProcess != null) && decompProcess.isReady()) { - String retval = decompProcess.sendCommand("flushNative").toString(); - return Integer.parseInt(retval); + decompProcess.sendCommand("flushNative", stringResponse); + return Integer.parseInt(stringResponse.toString()); } } catch (IOException e) { @@ -657,8 +657,8 @@ public class DecompInterface { return res; } - public synchronized BlockGraph structureGraph(BlockGraph ingraph, AddressFactory factory, - int timeoutSecs, TaskMonitor monitor) { + public synchronized BlockGraph structureGraph(BlockGraph ingraph, int timeoutSecs, + TaskMonitor monitor) { decompileMessage = ""; if (monitor != null && monitor.isCancelled()) { return null; @@ -666,18 +666,15 @@ public class DecompInterface { if (monitor != null) { monitor.addCancelledListener(monitorListener); } - LimitedByteBuffer res = null; BlockGraph resgraph = null; try { - encoder.clear(); - ingraph.encode(encoder); verifyProcess(); - res = decompProcess.sendCommand1ParamTimeout("structureGraph", encoder.toString(), - timeoutSecs); + paramEncode.clear(); + ingraph.encode(paramEncode); + decompProcess.sendCommand1ParamTimeout("structureGraph", paramEncode, timeoutSecs, + decoder); decompileMessage = decompCallback.getNativeMessage(); - if (res != null) { - XmlDecode decoder = new XmlDecode(factory); - decoder.ingestStream(res.getInputStream(), "structureGraph results"); + if (!decoder.isEmpty()) { resgraph = new BlockGraph(); resgraph.decode(decoder); resgraph.transferObjectRef(ingraph); @@ -710,7 +707,6 @@ public class DecompInterface { return null; } - LimitedByteBuffer res = null; if (monitor != null) { monitor.addCancelledListener(monitorListener); } @@ -726,12 +722,18 @@ public class DecompInterface { debug.setFunction(func); } decompCallback.setFunction(func, funcEntry, debug); - encoder.clear(); - AddressXML.encode(encoder, funcEntry); verifyProcess(); - res = decompProcess.sendCommand1ParamTimeout("decompileAt", encoder.toString(), - timeoutSecs); + paramEncode.clear(); + AddressXML.encode(paramEncode, funcEntry); + decompProcess.sendCommand1ParamTimeout("decompileAt", paramEncode, timeoutSecs, + decoder); decompileMessage = decompCallback.getNativeMessage(); + if (debug != null) { + XmlEncode xmlEncode = new XmlEncode(); + options.encode(xmlEncode, this); + debug.shutdown(pcodelanguage, xmlEncode.toString()); + debug = null; + } } catch (Exception ex) { decompileMessage = "Exception while decompiling " + func.getEntryPoint() + ": " + @@ -742,10 +744,6 @@ public class DecompInterface { monitor.removeCancelledListener(monitorListener); } } - if (debug != null) { - debug.shutdown(pcodelanguage, xmlOptions.getXML(this)); - debug = null; - } DecompileProcess.DisposeState processState; if (decompProcess != null) { @@ -758,12 +756,8 @@ public class DecompInterface { processState = DecompileProcess.DisposeState.DISPOSED_ON_CANCEL; } - InputStream stream = null; - if (res != null) { - stream = res.getInputStream(); - } return new DecompileResults(func, pcodelanguage, compilerSpec, dtmanage, decompileMessage, - stream, processState); + decoder, processState); } /** diff --git a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/DecompileCallback.java b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/DecompileCallback.java index 2cc0122992..4d8e2edcc3 100644 --- a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/DecompileCallback.java +++ b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/DecompileCallback.java @@ -38,7 +38,6 @@ import ghidra.util.Msg; import ghidra.util.UndefinedFunction; import ghidra.util.exception.UsrException; import ghidra.util.task.TaskMonitor; -import ghidra.util.xml.SpecXmlUtils; /** * @@ -49,7 +48,6 @@ import ghidra.util.xml.SpecXmlUtils; public class DecompileCallback { public final static int MAX_SYMBOL_COUNT = 16; - public final static byte[] EMPTY_BYTE_ARRAY = new byte[0]; /** * Data returned for a query about strings @@ -75,8 +73,6 @@ public class DecompileCallback { private Charset utf8Charset; private String nativeMessage; - private XmlDecodeLight lightDecoder; - private XmlEncode resultEncode; private InstructionBlock lastPseudoInstructionBlock; private Disassembler pseudoDisassembler; @@ -93,8 +89,6 @@ public class DecompileCallback { nativeMessage = null; debug = null; utf8Charset = Charset.availableCharsets().get(CharsetInfo.UTF8); - lightDecoder = new XmlDecodeLight(addrfactory); - resultEncode = new XmlEncode(); } /** @@ -140,21 +134,24 @@ public class DecompileCallback { nativeMessage = msg; } - public byte[] getBytes(String addrxml) { + /** + * Get bytes from the program's memory image. + * @param addr is the starting address to fetch bytes from + * @param size is the number of bytes to fetch + * @return the bytes matching the query or null if the query can't be met + */ + public byte[] getBytes(Address addr, int size) { + if (overlaySpace != null) { + addr = overlaySpace.getOverlayAddress(addr); + } + if (addr == Address.NO_ADDRESS) { + Msg.error(this, "Address does not physically map"); + return null; + } + if (addr.isRegisterAddress()) { + return null; + } try { - lightDecoder.ingestString(addrxml); - lightDecoder.openElement(); - Address addr = AddressXML.decodeFromAttributes(lightDecoder); - int size = (int) lightDecoder.readSignedInteger(ATTRIB_SIZE); - if (overlaySpace != null) { - addr = overlaySpace.getOverlayAddress(addr); - } - if (addr == Address.NO_ADDRESS) { - throw new PcodeXMLException("Address does not physically map"); - } - if (addr.isRegisterAddress()) { - return null; - } byte[] resbytes = new byte[size]; int bytesRead = program.getMemory().getBytes(addr, resbytes, 0, size); if (debug != null) { @@ -172,9 +169,6 @@ public class DecompileCallback { catch (MemoryAccessException e) { Msg.warn(this, "Decompiling " + funcEntry + ": " + e.getMessage()); } - catch (PcodeXMLException e) { - Msg.error(this, "Decompiling " + funcEntry + ": " + e.getMessage()); - } catch (Exception e) { Msg.error(this, "Decompiling " + funcEntry + ", error while accessing bytes: " + e.getMessage(), e); @@ -184,59 +178,42 @@ public class DecompileCallback { /** * Collect any/all comments for the function starting at the indicated - * address + * address. Filter based on selected comment types. * - * @param addrstring is the XML rep of function address - * @param types is the string encoding of the comment type flags - * @return Encoded description of comments + * @param addr is the indicated address + * @param types is the set of flags + * @param resultEncoder will contain the collected comments * @throws IOException for errors in the underlying stream */ - public byte[] getComments(String addrstring, String types) throws IOException { - Address addr; - int flags; - try { - lightDecoder.ingestString(addrstring); - addr = AddressXML.decode(lightDecoder); - if (overlaySpace != null) { - addr = overlaySpace.getOverlayAddress(addr); - } + public void getComments(Address addr, int types, Encoder resultEncoder) throws IOException { + if (overlaySpace != null) { + addr = overlaySpace.getOverlayAddress(addr); } - catch (PcodeXMLException e) { - Msg.error(this, "Decompiling " + funcEntry + ": " + e.getMessage()); - return EMPTY_BYTE_ARRAY; - } - flags = SpecXmlUtils.decodeInt(types); Function func = getFunctionAt(addr); if (func == null) { - return EMPTY_BYTE_ARRAY; + return; } - resultEncode.clear(); - encodeComments(resultEncode, addr, func, flags); + encodeComments(resultEncoder, addr, func, types); if (debug != null) { XmlEncode xmlEncode = new XmlEncode(); - encodeComments(xmlEncode, addr, func, flags); + encodeComments(xmlEncode, addr, func, types); debug.getComments(xmlEncode.toString()); } - return resultEncode.getBytes(); } - public PackedBytes getPcodePacked(String addrstring) { - Address addr = null; - try { - lightDecoder.ingestString(addrstring); - addr = AddressXML.decode(lightDecoder); - if (overlaySpace != null) { - addr = overlaySpace.getOverlayAddress(addr); - } - } - catch (PcodeXMLException e) { - Msg.error(this, "Decompiling " + funcEntry + ": " + e.getMessage()); - return null; + /** + * Generate p-code ops for the instruction at the given address + * @param addr is the given address + * @param resultEncoder will contain the generated p-code ops + */ + public void getPcode(Address addr, PackedEncode resultEncoder) { + if (overlaySpace != null) { + addr = overlaySpace.getOverlayAddress(addr); } try { Instruction instr = getInstruction(addr); if (instr == null) { - return null; + return; } if (undefinedBody != null) { undefinedBody.addRange(instr.getMinAddress(), instr.getMaxAddress()); @@ -250,11 +227,10 @@ public class DecompileCallback { } } - PackedBytes pcode = instr.getPrototype() - .getPcodePacked(instr.getInstructionContext(), + instr.getPrototype() + .getPcodePacked(resultEncoder, instr.getInstructionContext(), new InstructionPcodeOverride(instr)); - - return pcode; + return; } catch (UsrException e) { Msg.warn(this, @@ -264,23 +240,23 @@ public class DecompileCallback { Msg.error(this, "Decompiling " + funcEntry + ", pcode error at " + addr + ": " + e.getMessage(), e); } - return null; - + resultEncoder.clear(); } /** * Encode a list of pcode, representing an entire Instruction, to the stream * * @param encoder is the stream encoder - * @param ops pcode ops + * @param addr is the Address to associate with the Instruction + * @param ops is the pcode ops * @param fallthruoffset number of bytes after instruction start that pcode * flow falls into * @param paramshift special instructions for injection use * @param addrFactory is the address factory for recovering address space names * @throws IOException for errors in the underlying stream */ - public static void encodeInstruction(Encoder encoder, PcodeOp[] ops, int fallthruoffset, - int paramshift, AddressFactory addrFactory) throws IOException { + public static void encodeInstruction(Encoder encoder, Address addr, PcodeOp[] ops, + int fallthruoffset, int paramshift, AddressFactory addrFactory) throws IOException { if ((ops.length == 1) && (ops[0].getOpcode() == PcodeOp.UNIMPLEMENTED)) { encoder.openElement(ELEM_UNIMPL); encoder.writeSignedInteger(ATTRIB_OFFSET, fallthruoffset); @@ -292,29 +268,36 @@ public class DecompileCallback { if (paramshift != 0) { encoder.writeSignedInteger(ATTRIB_PARAMSHIFT, paramshift); } + AddressXML.encode(encoder, addr); for (PcodeOp op : ops) { - op.encode(encoder, addrFactory); + op.encodeRaw(encoder, addrFactory); } encoder.closeElement(ELEM_INST); } - public byte[] getPcodeInject(String nm, String context, int type) { + /** + * Generate p-code ops for a named injection payload + * @param nm is the name of the payload + * @param paramDecoder contains the context + * @param type is the type of payload + * @param resultEncoder will contain the generated p-code ops + */ + public void getPcodeInject(String nm, Decoder paramDecoder, int type, Encoder resultEncoder) { PcodeInjectLibrary snippetLibrary = pcodecompilerspec.getPcodeInjectLibrary(); InjectPayload payload = snippetLibrary.getPayload(type, nm); if (payload == null) { Msg.warn(this, "Decompiling " + funcEntry + ", no pcode inject with name: " + nm); - return EMPTY_BYTE_ARRAY; // No fixup associated with this name + return; // No fixup associated with this name } InjectContext con = snippetLibrary.buildInjectContext(); PcodeOp[] pcode; try { - lightDecoder.ingestString(context); - con.decode(lightDecoder); + con.decode(paramDecoder); } - catch (PcodeXMLException e) { + catch (DecoderException e) { Msg.error(this, "Decompiling " + funcEntry + ": " + e.getMessage()); - return EMPTY_BYTE_ARRAY; + return; } try { int fallThruOffset; @@ -322,14 +305,14 @@ public class DecompileCallback { // Executable p-code has no underlying instruction address and // does (should) not use the inst_start, inst_next symbols that need // to know about it. - fallThruOffset = 4; // Provide a dummy length for the XML doc + fallThruOffset = 4; // Provide a dummy length } else { Instruction instr = getInstruction(con.baseAddr); if (instr == null) { Msg.warn(this, "Decompiling " + funcEntry + ", pcode inject error at " + con.baseAddr + ": instruction not found"); - return EMPTY_BYTE_ARRAY; + return; } // get next inst addr for inst_next pcode variable @@ -347,18 +330,16 @@ public class DecompileCallback { } pcode = payload.getPcode(program, con); if (pcode == null) { - return EMPTY_BYTE_ARRAY; // Return without result, which should let the decompiler exit gracefully + return; // Return without result, which should let the decompiler exit gracefully } - resultEncode.clear(); - encodeInstruction(resultEncode, pcode, fallThruOffset, payload.getParamShift(), - addrfactory); + encodeInstruction(resultEncoder, con.baseAddr, pcode, fallThruOffset, + payload.getParamShift(), addrfactory); if (debug != null) { XmlEncode xmlEncode = new XmlEncode(); - encodeInstruction(xmlEncode, pcode, fallThruOffset, payload.getParamShift(), - addrfactory); + encodeInstruction(xmlEncode, con.baseAddr, pcode, fallThruOffset, + payload.getParamShift(), addrfactory); debug.addInject(con.baseAddr, nm, type, xmlEncode.toString()); } - return resultEncode.getBytes(); } catch (UnknownInstructionException e) { Msg.warn(this, "Decompiling " + funcEntry + ", pcode inject error at " + con.baseAddr + @@ -368,22 +349,25 @@ public class DecompileCallback { Msg.error(this, "Decompiling " + funcEntry + ", pcode inject error at " + con.baseAddr + ": " + e.getMessage(), e); } - return EMPTY_BYTE_ARRAY; } - public byte[] getCPoolRef(long[] refs) throws IOException { + /** + * Look up details of a specific constant pool reference + * @param refs is the constant id (which may consist of multiple integers) + * @param resultEncoder will contain the reference details + * @throws IOException for errors in the underlying stream while encoding results + */ + public void getCPoolRef(long[] refs, Encoder resultEncoder) throws IOException { if (cpool == null) { cpool = pcodecompilerspec.getPcodeInjectLibrary().getConstantPool(program); } Record record = cpool.getRecord(refs); - resultEncode.clear(); - record.encode(resultEncode, refs[0], dtmanage); + record.encode(resultEncoder, refs[0], dtmanage); if (debug != null) { XmlEncode xmlEncode = new XmlEncode(); record.encode(xmlEncode, refs[0], dtmanage); debug.getCPoolRef(xmlEncode.toString(), refs); } - return resultEncode.getBytes(); } private Instruction getInstruction(Address addr) throws UnknownInstructionException { @@ -447,18 +431,14 @@ public class DecompileCallback { throw new UnknownInstructionException("Invalid instruction address (improperly aligned)"); } - public String getSymbol(String addrstring) { // Return first symbol name at this address - Address addr; - try { - lightDecoder.ingestString(addrstring); - addr = AddressXML.decode(lightDecoder); - if (overlaySpace != null) { - addr = overlaySpace.getOverlayAddress(addr); - } - } - catch (PcodeXMLException e) { - Msg.error(this, "Decompiling " + funcEntry + ": " + e.getMessage()); - return null; + /** + * Return the first symbol name at the given address + * @param addr is the given address + * @return the symbol or null if no symbol is found + */ + public String getCodeLabel(Address addr) { + if (overlaySpace != null) { + addr = overlaySpace.getOverlayAddress(addr); } try { Symbol sym = program.getSymbolTable().getPrimarySymbol(addr); @@ -575,17 +555,15 @@ public class DecompileCallback { /** * Write a description of the formal namespace path to the given namespace * @param id is the ID of the given namespace - * @return the encoded result + * @param resultEncoder is where to write the encoded result * @throws IOException for errors in the underlying stream */ - public byte[] getNamespacePath(long id) throws IOException { + public void getNamespacePath(long id, Encoder resultEncoder) throws IOException { Namespace namespace = getNameSpaceByID(id); - resultEncode.clear(); - HighFunction.encodeNamespace(resultEncode, namespace); + HighFunction.encodeNamespace(resultEncoder, namespace); if (debug != null) { debug.getNamespacePath(namespace); } - return resultEncode.getBytes(); } private void encodeHeaderComment(Encoder encoder, Function func) throws IOException { @@ -683,81 +661,61 @@ public class DecompileCallback { } /** - * Describe data or functions at addr. + * Describe data or functions at the given address; either function, reference, data, or hole. * Called by the native decompiler to query the GHIDRA database about any * symbols at the given address. * - * @param addrstring XML encoded address to query - * @return an encoded description, either function, reference, datatype, or hole + * @param addr is the given address + * @param resultEncoder is where to write encoded description */ - public byte[] getMappedSymbolsXML(String addrstring) { - Address addr; - try { - lightDecoder.ingestString(addrstring); - addr = AddressXML.decode(lightDecoder); - if (overlaySpace != null) { - addr = overlaySpace.getOverlayAddress(addr); - } - if (addr == Address.NO_ADDRESS) { - // Unknown spaces may result from "spacebase" registers defined in cspec - return EMPTY_BYTE_ARRAY; - } + public void getMappedSymbols(Address addr, Encoder resultEncoder) { + if (overlaySpace != null) { + addr = overlaySpace.getOverlayAddress(addr); } - catch (PcodeXMLException e) { - Msg.error(this, "Decompiling " + funcEntry + ": " + e.getMessage()); - return EMPTY_BYTE_ARRAY; + if (addr == Address.NO_ADDRESS) { + // Unknown spaces may result from "spacebase" registers defined in cspec + return; } try { Object obj = lookupSymbol(addr); - resultEncode.clear(); if (obj instanceof Function) { boolean includeDefaults = addr.equals(funcEntry); - encodeFunction(resultEncode, (Function) obj, addr, includeDefaults); + encodeFunction(resultEncoder, (Function) obj, addr, includeDefaults); } else if (obj instanceof Data) { - if (!encodeData(resultEncode, (Data) obj)) { - encodeHole(resultEncode, addr); + if (!encodeData(resultEncoder, (Data) obj)) { + encodeHole(resultEncoder, addr); } } else if (obj instanceof ExternalReference) { - encodeExternalRef(resultEncode, addr, (ExternalReference) obj); + encodeExternalRef(resultEncoder, addr, (ExternalReference) obj); } else if (obj instanceof Symbol) { - encodeLabel(resultEncode, (Symbol) obj, addr); + encodeLabel(resultEncoder, (Symbol) obj, addr); } else { - encodeHole(resultEncode, addr); // There is a hole, describe the extent of the hole + encodeHole(resultEncoder, addr); // There is a hole, describe the extent of the hole } - return resultEncode.getBytes(); + return; } catch (Exception e) { - Msg.error(this, "Decompiling " + funcEntry + ", mapped symbol error for " + addrstring + + Msg.error(this, "Decompiling " + funcEntry + ", mapped symbol error for " + addr + ": " + e.getMessage(), e); } - return EMPTY_BYTE_ARRAY; + return; } /** - * Describe an external reference at the given address - * @param addrstring is the description of the address - * @return the encoded description + * Get a description of an external reference at the given address + * @param addr is the given address + * @param resultEncoder will contain the resulting description */ - public byte[] getExternalRefXML(String addrstring) { - Address addr; - try { - lightDecoder.ingestString(addrstring); - addr = AddressXML.decode(lightDecoder); - if (overlaySpace != null) { - addr = overlaySpace.getOverlayAddress(addr); - } - } - catch (PcodeXMLException e) { - Msg.error(this, "Decompiling " + funcEntry + ": " + e.getMessage()); - return EMPTY_BYTE_ARRAY; + public void getExternalRef(Address addr, Encoder resultEncoder) { + if (overlaySpace != null) { + addr = overlaySpace.getOverlayAddress(addr); } try { - Function func = null; if (cachedFunction != null && cachedFunction.getEntryPoint().equals(addr)) { func = cachedFunction; @@ -778,9 +736,8 @@ public class DecompileCallback { } HighSymbol shellSymbol = new HighFunctionShellSymbol(extId, extRef.getLabel(), addr, dtmanage); - resultEncode.clear(); - encodeResult(resultEncode, shellSymbol, null); - return resultEncode.getBytes(); + encodeResult(resultEncoder, shellSymbol, null); + return; } } else { @@ -789,7 +746,7 @@ public class DecompileCallback { } if (func == null) { // Its conceivable we could have external data, but we aren't currently checking for it - return EMPTY_BYTE_ARRAY; + return; } HighFunction hfunc = new HighFunction(func, pcodelanguage, pcodecompilerspec, dtmanage); @@ -803,86 +760,89 @@ public class DecompileCallback { debug.getFNTypes(hfunc); debug.addPossiblePrototypeExtension(func); } - resultEncode.clear(); - encodeResult(resultEncode, funcSymbol, namespc); - return resultEncode.getBytes(); + encodeResult(resultEncoder, funcSymbol, namespc); + return; } catch (Exception e) { Msg.error(this, - "Decompiling " + funcEntry + ", error in getExternalRefXML: " + e.getMessage(), e); + "Decompiling " + funcEntry + ", error in getExternalRef: " + e.getMessage(), e); } - return EMPTY_BYTE_ARRAY; } - public byte[] getType(String name, long id) throws IOException { + /** + * Get a description of a data-type given its name and type id + * @param name is the name of the data-type + * @param id is the type id + * @param resultEncoder will contain the resulting description + * @throws IOException for errors in the underlying stream while encoding + */ + public void getDataType(String name, long id, Encoder resultEncoder) throws IOException { DataType type = dtmanage.findBaseType(name, id); if (type == null) { - return EMPTY_BYTE_ARRAY; + return; } - resultEncode.clear(); - dtmanage.encodeType(resultEncode, type, 0); + dtmanage.encodeType(resultEncoder, type, 0); if (debug != null) { debug.getType(type); } - return resultEncode.getBytes(); } - public byte[] getRegister(String name) throws IOException { + /** + * Return a description of the register with the given name + * @param name is the given name + * @param resultEncoder is where to write the description + * @throws IOException for errors writing to the underlying stream + */ + public void getRegister(String name, Encoder resultEncoder) throws IOException { Register reg = pcodelanguage.getRegister(name); if (reg == null) { throw new RuntimeException("No Register Defined: " + name); } - resultEncode.clear(); - encodeRegister(resultEncode, reg); - return resultEncode.getBytes(); + encodeRegister(resultEncoder, reg); } - public String getRegisterName(String addrstring) { - try { - lightDecoder.ingestString(addrstring); - lightDecoder.openElement(); - Address addr = AddressXML.decodeFromAttributes(lightDecoder); - int size = (int) lightDecoder.readSignedInteger(ATTRIB_SIZE); - Register reg = pcodelanguage.getRegister(addr, size); - if (reg == null) { - return ""; - } - return reg.getName(); + /** + * Given a storage location, return the register name for that location, or null if there + * is no register there. + * @param addr is the starting address of the storage location + * @param size is the size of storage in bytes + * @return the register name or null + */ + public String getRegisterName(Address addr, int size) { + Register reg = pcodelanguage.getRegister(addr, size); + if (reg == null) { + return ""; } - catch (PcodeXMLException e) { - Msg.error(this, "Decompiling " + funcEntry + - ", error while searching for register name: " + e.getMessage(), e); - } - return ""; + return reg.getName(); } - public byte[] getTrackedRegisters(String addrstring) throws IOException { - Address addr; - try { - lightDecoder.ingestString(addrstring); - addr = AddressXML.decode(lightDecoder); - if (overlaySpace != null) { - addr = overlaySpace.getOverlayAddress(addr); - } - } - catch (PcodeXMLException e) { - Msg.error(this, "Decompiling " + funcEntry + ": " + e.getMessage()); - return EMPTY_BYTE_ARRAY; + /** + * Get "tracked" register values, constant values associated with a specific register at + * a specific point in the code. + * @param addr is the "point" in the code to look for tracked values + * @param resultEncoder will hold the resulting description of registers and values + * @throws IOException for errors in the underlying stream writing the result + */ + public void getTrackedRegisters(Address addr, Encoder resultEncoder) throws IOException { + if (overlaySpace != null) { + addr = overlaySpace.getOverlayAddress(addr); } ProgramContext context = program.getProgramContext(); - resultEncode.clear(); - encodeTrackedPointSet(resultEncode, addr, context); + encodeTrackedPointSet(resultEncoder, addr, context); if (debug != null) { XmlEncode xmlEncode = new XmlEncode(); encodeTrackedPointSet(xmlEncode, addr, context); debug.getTrackedRegisters(xmlEncode.toString()); } - return resultEncode.getBytes(); } - public String getUserOpName(String indexStr) { - int index = Integer.parseInt(indexStr); + /** + * Get the name of a user op given its index + * @param index is the given index + * @return the userop name or null + */ + public String getUserOpName(int index) { String name = pcodelanguage.getUserDefinedOpName(index); return name; } @@ -1007,7 +967,7 @@ public class DecompileCallback { } /** - * This function deals with the vagaries of the getMappedSymbolsXML + * This function deals with the vagaries of the getMappedSymbols * interface when the queried address is in the body of a function. * Basically, if the address is the entry point of the function, all the * function data is sent. Otherwise a hole is sent back of the biggest @@ -1268,35 +1228,25 @@ public class DecompileCallback { } /** - * Check for a string at an address and return a UTF8 encoded byte array. + * Check for a string at the given address and return a UTF8 encoded byte array. * If there is already data present at the address, use this to determine the * string encoding. Otherwise use the data-type info passed in to determine the encoding. * Check that the bytes at the address represent a valid string encoding that doesn't * exceed the maximum character limit passed in. Return null if the string is invalid. * Return the string translated into a UTF8 byte array otherwise. A (valid) empty * string is returned as a zero length array. - * @param addrString is the XML encoded address and maximum byte limit + * @param addr is the given address + * @param maxChars is the maximum character limit * @param dtName is the name of a character data-type * @param dtId is the id associated with the character data-type * @return the UTF8 encoded byte array or null */ - public StringData getStringData(String addrString, String dtName, long dtId) { - Address addr; - int maxChars; - try { - lightDecoder.ingestString(addrString); - lightDecoder.openElement(); - addr = AddressXML.decodeFromAttributes(lightDecoder); - maxChars = (int) lightDecoder.readSignedInteger(ATTRIB_SIZE); - if (overlaySpace != null) { - addr = overlaySpace.getOverlayAddress(addr); - } - if (addr == Address.NO_ADDRESS) { - throw new PcodeXMLException("Address does not physically map"); - } + public StringData getStringData(Address addr, int maxChars, String dtName, long dtId) { + if (overlaySpace != null) { + addr = overlaySpace.getOverlayAddress(addr); } - catch (PcodeXMLException e) { - Msg.error(this, "Decompiling " + funcEntry + ": " + e.getMessage()); + if (addr == Address.NO_ADDRESS) { + Msg.error(this, "Address does not physically map"); return null; } Data data = program.getListing().getDataContaining(addr); diff --git a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/DecompileDebug.java b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/DecompileDebug.java index 7f2950f736..d1a10f0eda 100644 --- a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/DecompileDebug.java +++ b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/DecompileDebug.java @@ -328,7 +328,7 @@ public class DecompileDebug { encoder.closeElement(ELEM_STRING); } encoder.closeElement(ELEM_STRINGMANAGE); - debugStream.write(encoder.getBytes()); + encoder.writeTo(debugStream); } private void dumpDataTypes(OutputStream debugStream) throws IOException { @@ -356,7 +356,7 @@ public class DecompileDebug { } } encoder.closeElement(ELEM_TYPEGRP); - debugStream.write(encoder.getBytes()); + encoder.writeTo(debugStream); } private void dumpTrackedContext(OutputStream debugStream) throws IOException { @@ -463,7 +463,7 @@ public class DecompileDebug { encoder.closeElement(ELEM_SET); } encoder.closeElement(ELEM_CONTEXT_POINTSET); - debugStream.write(encoder.getBytes()); + encoder.writeTo(debugStream); } } @@ -616,13 +616,13 @@ public class DecompileDebug { } } encoder.closeElement(ELEM_SPECEXTENSIONS); - debugStream.write(encoder.getBytes()); + encoder.writeTo(debugStream); } private void dumpCoretypes(OutputStream debugStream) throws IOException { XmlEncode encoder = new XmlEncode(); dtmanage.encodeCoreTypes(encoder); - debugStream.write(encoder.getBytes()); + encoder.writeTo(debugStream); } public void getPcode(Address addr, Instruction instr) { diff --git a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/DecompileOptions.java b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/DecompileOptions.java index 1dddf53a86..68544ae7d4 100644 --- a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/DecompileOptions.java +++ b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/DecompileOptions.java @@ -16,10 +16,13 @@ package ghidra.app.decompiler; import static ghidra.GhidraOptions.*; +import static ghidra.program.model.pcode.AttributeId.*; +import static ghidra.program.model.pcode.ElementId.*; import java.awt.Color; import java.awt.Font; import java.awt.event.MouseEvent; +import java.io.IOException; import ghidra.GhidraOptions.CURSOR_MOUSE_BUTTON_NAMES; import ghidra.app.util.HelpTopics; @@ -31,6 +34,8 @@ import ghidra.program.database.ProgramCompilerSpec; import ghidra.program.model.lang.*; import ghidra.program.model.lang.CompilerSpec.EvaluationModelType; import ghidra.program.model.listing.Program; +import ghidra.program.model.pcode.ElementId; +import ghidra.program.model.pcode.Encoder; import ghidra.util.HelpLocation; import ghidra.util.SystemUtilities; @@ -664,128 +669,125 @@ public class DecompileOptions { grabFromToolAndProgram(ownerPlugin, opt, program); } - private static void appendOption(StringBuffer buf, String name, String p1, String p2, - String p3) { - buf.append(" <"); - buf.append(name); - buf.append('>'); + private static void appendOption(Encoder encoder, ElementId option, String p1, String p2, + String p3) throws IOException { + encoder.openElement(option); if ((p2.length() == 0) && (p3.length() == 0)) { - buf.append(p1); + encoder.writeString(ATTRIB_CONTENT, p1); } else { - buf.append('\n'); - buf.append(" "); - buf.append(p1); - buf.append("\n"); - buf.append(" "); - buf.append(p2); // Print even if empty, as p3 isn't - buf.append("\n"); + encoder.openElement(ELEM_PARAM1); + encoder.writeString(ATTRIB_CONTENT, p1); + encoder.closeElement(ELEM_PARAM1); + encoder.openElement(ELEM_PARAM2); + encoder.writeString(ATTRIB_CONTENT, p2); // Print even if empty, as p3 isn't + encoder.closeElement(ELEM_PARAM2); if (p3.length() != 0) { - buf.append(" "); - buf.append(p3); - buf.append("\n"); + encoder.openElement(ELEM_PARAM3); + encoder.writeString(ATTRIB_CONTENT, p3); + encoder.closeElement(ELEM_PARAM3); } } - buf.append("\n"); + encoder.closeElement(option); } /** - * Produce XML document of configuration options - * to be sent to decompiler process. This object - * is global to all decompile processes so we can - * tailor to the specific process by passing in the - * interface + * Encode all the configuration options to a stream for the decompiler process. + * This object is global to all decompile processes so we can tailor to the specific process + * by passing in the interface. + * @param encoder is the stream encoder * @param iface specific DecompInterface being sent options - * @return XML document as a string + * @throws IOException for errors writing to the underlying stream */ - public String getXML(DecompInterface iface) { - StringBuffer buf = new StringBuffer(); - buf.append("\n"); - appendOption(buf, "currentaction", "conditionalexe", predicate ? "on" : "off", ""); - appendOption(buf, "readonly", readOnly ? "on" : "off", "", ""); - appendOption(buf, "currentaction", iface.getSimplificationStyle(), "unreachable", + public void encode(Encoder encoder, DecompInterface iface) throws IOException { + encoder.openElement(ELEM_OPTIONSLIST); + appendOption(encoder, ELEM_CURRENTACTION, "conditionalexe", predicate ? "on" : "off", ""); + appendOption(encoder, ELEM_READONLY, readOnly ? "on" : "off", "", ""); + appendOption(encoder, ELEM_CURRENTACTION, iface.getSimplificationStyle(), "unreachable", eliminateUnreachable ? "on" : "off"); - appendOption(buf, "currentaction", iface.getSimplificationStyle(), "doubleprecis", + appendOption(encoder, ELEM_CURRENTACTION, iface.getSimplificationStyle(), "doubleprecis", simplifyDoublePrecision ? "on" : "off"); // Must set language early so that the object is in place before other option changes - appendOption(buf, "setlanguage", displayLanguage.toString(), "", ""); + appendOption(encoder, ELEM_SETLANGUAGE, displayLanguage.toString(), "", ""); if (ignoreunimpl != IGNOREUNIMPL_OPTIONDEFAULT) { - appendOption(buf, "ignoreunimplemented", ignoreunimpl ? "on" : "off", "", ""); + appendOption(encoder, ELEM_IGNOREUNIMPLEMENTED, ignoreunimpl ? "on" : "off", "", ""); } if (inferconstptr != INFERCONSTPTR_OPTIONDEFAULT) { - appendOption(buf, "inferconstptr", inferconstptr ? "on" : "off", "", ""); + appendOption(encoder, ELEM_INFERCONSTPTR, inferconstptr ? "on" : "off", "", ""); } if (analyzeForLoops != ANALYZEFORLOOPS_OPTIONDEFAULT) { - appendOption(buf, "analyzeforloops", analyzeForLoops ? "on" : "off", "", ""); + appendOption(encoder, ELEM_ANALYZEFORLOOPS, analyzeForLoops ? "on" : "off", "", ""); } if (nullToken != NULLTOKEN_OPTIONDEFAULT) { - appendOption(buf, "nullprinting", nullToken ? "on" : "off", "", ""); + appendOption(encoder, ELEM_NULLPRINTING, nullToken ? "on" : "off", "", ""); } if (inplaceTokens != INPLACEOP_OPTIONDEFAULT) { - appendOption(buf, "inplaceops", inplaceTokens ? "on" : "off", "", ""); + appendOption(encoder, ELEM_INPLACEOPS, inplaceTokens ? "on" : "off", "", ""); } if (aliasBlock != ALIASBLOCK_OPTIONDEFAULT) { - appendOption(buf, "aliasblock", aliasBlock.getOptionString(), "", ""); + appendOption(encoder, ELEM_ALIASBLOCK, aliasBlock.getOptionString(), "", ""); } if (conventionPrint != CONVENTION_OPTIONDEFAULT) { - appendOption(buf, "conventionprinting", conventionPrint ? "on" : "off", "", ""); + appendOption(encoder, ELEM_CONVENTIONPRINTING, conventionPrint ? "on" : "off", "", ""); } if (noCastPrint != NOCAST_OPTIONDEFAULT) { - appendOption(buf, "nocastprinting", noCastPrint ? "on" : "off", "", ""); + appendOption(encoder, ELEM_NOCASTPRINTING, noCastPrint ? "on" : "off", "", ""); } if (maxwidth != MAXWIDTH_OPTIONDEFAULT) { - appendOption(buf, "maxlinewidth", Integer.toString(maxwidth), "", ""); + appendOption(encoder, ELEM_MAXLINEWIDTH, Integer.toString(maxwidth), "", ""); } if (indentwidth != INDENTWIDTH_OPTIONDEFAULT) { - appendOption(buf, "indentincrement", Integer.toString(indentwidth), "", ""); + appendOption(encoder, ELEM_INDENTINCREMENT, Integer.toString(indentwidth), "", ""); } if (commentindent != COMMENTINDENT_OPTIONDEFAULT) { - appendOption(buf, "commentindent", Integer.toString(commentindent), "", ""); + appendOption(encoder, ELEM_COMMENTINDENT, Integer.toString(commentindent), "", ""); } if (commentStyle != COMMENTSTYLE_OPTIONDEFAULT) { String curstyle = CommentStyleEnum.CPPStyle.equals(commentStyle) ? "cplusplus" : "c"; - appendOption(buf, "commentstyle", curstyle, "", ""); + appendOption(encoder, ELEM_COMMENTSTYLE, curstyle, "", ""); } if (commentPLATEInclude != COMMENTPLATE_OPTIONDEFAULT) { - appendOption(buf, "commentinstruction", "header", commentPLATEInclude ? "on" : "off", - ""); + appendOption(encoder, ELEM_COMMENTINSTRUCTION, "header", + commentPLATEInclude ? "on" : "off", ""); } if (commentPREInclude != COMMENTPRE_OPTIONDEFAULT) { - appendOption(buf, "commentinstruction", "user2", commentPREInclude ? "on" : "off", ""); + appendOption(encoder, ELEM_COMMENTINSTRUCTION, "user2", + commentPREInclude ? "on" : "off", ""); } if (commentEOLInclude != COMMENTEOL_OPTIONDEFAULT) { - appendOption(buf, "commentinstruction", "user1", commentEOLInclude ? "on" : "off", ""); + appendOption(encoder, ELEM_COMMENTINSTRUCTION, "user1", + commentEOLInclude ? "on" : "off", ""); } if (commentPOSTInclude != COMMENTPOST_OPTIONDEFAULT) { - appendOption(buf, "commentinstruction", "user3", commentPOSTInclude ? "on" : "off", ""); + appendOption(encoder, ELEM_COMMENTINSTRUCTION, "user3", + commentPOSTInclude ? "on" : "off", ""); } if (commentWARNInclude != COMMENTWARN_OPTIONDEFAULT) { - appendOption(buf, "commentinstruction", "warning", commentWARNInclude ? "on" : "off", - ""); + appendOption(encoder, ELEM_COMMENTINSTRUCTION, "warning", + commentWARNInclude ? "on" : "off", ""); } if (commentHeadInclude != COMMENTHEAD_OPTIONDEFAULT) { - appendOption(buf, "commentheader", "header", commentHeadInclude ? "on" : "off", ""); - } - if (commentWARNInclude != COMMENTWARN_OPTIONDEFAULT) { - appendOption(buf, "commentheader", "warningheader", commentWARNInclude ? "on" : "off", + appendOption(encoder, ELEM_COMMENTHEADER, "header", commentHeadInclude ? "on" : "off", ""); } + if (commentWARNInclude != COMMENTWARN_OPTIONDEFAULT) { + appendOption(encoder, ELEM_COMMENTHEADER, "warningheader", + commentWARNInclude ? "on" : "off", ""); + } if (namespaceStrategy != NAMESPACE_OPTIONDEFAULT) { - appendOption(buf, "namespacestrategy", namespaceStrategy.getOptionString(), "", ""); + appendOption(encoder, ELEM_NAMESPACESTRATEGY, namespaceStrategy.getOptionString(), "", + ""); } if (integerFormat != INTEGERFORMAT_OPTIONDEFAULT) { - appendOption(buf, "integerformat", integerFormat.getOptionString(), "", ""); + appendOption(encoder, ELEM_INTEGERFORMAT, integerFormat.getOptionString(), "", ""); } if (maxIntructionsPer != SUGGESTED_MAX_INSTRUCTIONS) { - appendOption(buf, "maxinstruction", Integer.toString(maxIntructionsPer), "", ""); + appendOption(encoder, ELEM_MAXINSTRUCTION, Integer.toString(maxIntructionsPer), "", ""); } - appendOption(buf, "protoeval", protoEvalModel, "", ""); - buf.append("\n"); - return buf.toString(); + appendOption(encoder, ELEM_PROTOEVAL, protoEvalModel, "", ""); + encoder.closeElement(ELEM_OPTIONSLIST); } public int getMaxWidth() { diff --git a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/DecompileProcess.java b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/DecompileProcess.java index b2b72a924f..0c7aed7e19 100644 --- a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/DecompileProcess.java +++ b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/DecompileProcess.java @@ -15,14 +15,18 @@ */ package ghidra.app.decompiler; +import static ghidra.program.model.pcode.AttributeId.*; +import static ghidra.program.model.pcode.ElementId.*; + import java.io.*; +import ghidra.program.model.address.Address; import ghidra.program.model.lang.InjectPayload; -import ghidra.program.model.lang.PackedBytes; +import ghidra.program.model.listing.Program; +import ghidra.program.model.pcode.*; import ghidra.util.Msg; import ghidra.util.timer.GTimer; import ghidra.util.timer.GTimerMonitor; -import ghidra.util.xml.SpecXmlUtils; /** * @@ -66,8 +70,13 @@ public class DecompileProcess { private int archId = -1; // architecture id for decomp process private DecompileCallback callback; // Callback interface for decompiler + private String programSource; // String describing program for error reports private int maxResultSizeMBYtes = 50; // maximum result size in MBytes to allow from decompiler + private PackedDecode paramDecoder; // Ingest queries from the decompiler process + private StringIngest stringDecoder; // Ingest of exception and status messages + private PackedEncode resultEncoder; // Encode responses to decompile process queries + public enum DisposeState { NOT_DISPOSED, // Process was/is not disposed DISPOSED_ON_TIMEOUT, // A timeout occurred @@ -89,6 +98,7 @@ public class DecompileProcess { disposestate = DisposeState.DISPOSED_ON_TIMEOUT; } }; + stringDecoder = new StringIngest(); } public void dispose() { @@ -186,17 +196,10 @@ public class DecompileProcess { throw new IOException("Ghidra/decompiler alignment error"); } - private int readToBuffer(LimitedByteBuffer buf) throws IOException { + private int readToBuffer(ByteIngest buf) throws IOException { int cur; for (;;) { - cur = nativeIn.read(); - while (cur > 0) { - buf.append((byte) cur); - cur = nativeIn.read(); - } - if (cur == -1) { - break; - } + buf.ingestStream(nativeIn); do { cur = nativeIn.read(); } @@ -214,17 +217,17 @@ public class DecompileProcess { throw new IOException("Decompiler process died"); } - private String readQueryString() throws IOException { + private void readQueryParam(ByteIngest ingester) throws IOException { int type = readToBurst(); if (type != 14) { throw new IOException("GHIDRA/decompiler alignment error"); } - LimitedByteBuffer buf = new LimitedByteBuffer(16, 1 << 16); - type = readToBuffer(buf); + ingester.open(1 << 16, programSource); + type = readToBuffer(ingester); if (type != 15) { throw new IOException("GHIDRA/decompiler alignment error"); } - return buf.toString(); + ingester.endIngest(); } private void writeString(String msg) throws IOException { @@ -233,40 +236,20 @@ public class DecompileProcess { write(string_end); } - private void writeBytes(byte[] msg) throws IOException { - write(string_start); - write(msg); - write(string_end); - } - - /** - * Transfer bytes written to -out- to decompiler process - * @param out has the collected byte for this write - * @throws IOException for any problems with the output stream - */ - private void writeBytes(PackedBytes out) throws IOException { - write(string_start); - int sz = out.size(); - int sz1 = (sz & 0x3f) + 0x20; - sz >>>= 6; - int sz2 = (sz & 0x3f) + 0x20; - sz >>>= 6; - int sz3 = (sz & 0x3f) + 0x20; - sz >>>= 6; - int sz4 = (sz & 0x3f) + 0x20; - write(sz1); - write(sz2); - write(sz3); - write(sz4); - if (nativeOut != null) { // null if disposed - out.writeTo(nativeOut); + private void writeString(Encoder byteResult) throws IOException { + if (nativeOut == null) { + return; } + write(string_start); + byteResult.writeTo(nativeOut); write(string_end); } private void generateException() throws IOException, DecompileException { - String type = readQueryString(); - String message = readQueryString(); + readQueryParam(stringDecoder); + String type = stringDecoder.toString(); + readQueryParam(stringDecoder); + String message = stringDecoder.toString(); readToBurst(); // Read exception terminator if (type.equals("alignment")) { throw new IOException("Alignment error: " + message); @@ -274,89 +257,78 @@ public class DecompileProcess { throw new DecompileException(type, message); } - private LimitedByteBuffer readResponse() throws IOException, DecompileException { + private void readResponse(ByteIngest mainResponse) throws IOException, DecompileException { readToResponse(); int type = readToBurst(); - String name; - LimitedByteBuffer retbuf = null; - LimitedByteBuffer buf = null; + int commandId; + ByteIngest currentResponse = null; while (type != 7) { switch (type) { case 4: - name = readQueryString(); + readQueryParam(paramDecoder); try { - if (name.length() < 4) { - throw new Exception("Bad decompiler query: " + name); - } - switch (name.charAt(3)) { - case 'a': // isNameUsed + commandId = paramDecoder.openElement(); + switch (commandId) { + case COMMAND_ISNAMEUSED: isNameUsed(); break; - case 'B': + case COMMAND_GETBYTES: getBytes(); // getBytes break; - case 'C': - if (name.equals("getComments")) { - getComments(); - } - else if (name.equals("getCallFixup")) { - getPcodeInject(InjectPayload.CALLFIXUP_TYPE); - } - else if (name.equals("getCallotherFixup")) { - getPcodeInject(InjectPayload.CALLOTHERFIXUP_TYPE); - } - else if (name.equals("getCallMech")) { - getPcodeInject(InjectPayload.CALLMECHANISM_TYPE); - } - else { - getCPoolRef(); - } + case COMMAND_GETCOMMENTS: + getComments(); break; - case 'E': - getExternalRefXML(); // getExternalRefXML + case COMMAND_GETCALLFIXUP: + getPcodeInject(InjectPayload.CALLFIXUP_TYPE); break; - case 'M': - getMappedSymbolsXML(); // getMappedSymbolsXML + case COMMAND_GETCALLOTHERFIXUP: + getPcodeInject(InjectPayload.CALLOTHERFIXUP_TYPE); break; - case 'N': - getNamespacePath(); + case COMMAND_GETCALLMECH: + getPcodeInject(InjectPayload.CALLMECHANISM_TYPE); break; - case 'P': - getPcodePacked(); // getPacked - break; - case 'R': - if (name.equals("getRegister")) { - getRegister(); - } - else { - getRegisterName(); - } - break; - case 'S': - if (name.equals("getString")) { - getStringData(); - } - else { - getSymbol(); // getSymbol - } - break; - case 'T': - if (name.equals("getType")) { - getType(); - } - else { - getTrackedRegisters(); - } - break; - case 'U': - getUserOpName(); // getUserOpName - break; - case 'X': + case COMMAND_GETPCODEEXECUTABLE: getPcodeInject(InjectPayload.EXECUTABLEPCODE_TYPE); break; + case COMMAND_GETCPOOLREF: + getCPoolRef(); + break; + case COMMAND_GETEXTERNALREF: + getExternalRef(); + break; + case COMMAND_GETMAPPEDSYMBOLS: + getMappedSymbols(); + break; + case COMMAND_GETNAMESPACEPATH: + getNamespacePath(); + break; + case COMMAND_GETPCODE: + getPcode(); + break; + case COMMAND_GETREGISTER: + getRegister(); + break; + case COMMAND_GETREGISTERNAME: + getRegisterName(); + break; + case COMMAND_GETSTRINGDATA: + getStringData(); + break; + case COMMAND_GETCODELABEL: + getCodeLabel(); + break; + case COMMAND_GETDATATYPE: + getDataType(); + break; + case COMMAND_GETTRACKEDREGISTERS: + getTrackedRegisters(); + break; + case COMMAND_GETUSEROPNAME: + getUserOpName(); + break; default: - throw new Exception("Unsupported decompiler query '" + name + "'"); + throw new Exception("Unsupported decompiler query"); } } catch (Exception e) { // Catch ANY exception query generates @@ -384,47 +356,45 @@ public class DecompileProcess { generateException(); break; case 14: // Start of the main decompiler output - if (buf != null) { + if (currentResponse != null) { throw new IOException("Nested decompiler output"); } // Allocate storage buffer for the result, which is generally not tiny. So we // start with any initial allocation of 1024 bytes, also give an absolute upper bound // determined by maxResultSizeMBYtes - buf = new LimitedByteBuffer(1024, maxResultSizeMBYtes << 20); + currentResponse = mainResponse; + currentResponse.open(maxResultSizeMBYtes << 20, programSource); break; case 15: // This is the end of the main decompiler output - if (buf == null) { + if (currentResponse == null) { throw new IOException("Mismatched string header"); } - retbuf = buf; - buf = null; // Reset the main buffer as a native message may follow + currentResponse.endIngest(); + currentResponse = null; // Reset current buffer as a native message may follow break; case 16: // Beginning of any native message from the decompiler -// if (buf!=null) -// throw new IOException("Nested decompiler output"); - // if buf is non-null, then res was interrupted - // so we just throw out the partial result - buf = new LimitedByteBuffer(64, 1 << 20); + currentResponse = stringDecoder; + currentResponse.open(1 << 20, programSource); break; case 17: // End of the native message from the decompiler - if (buf == null) { + if (currentResponse == null) { throw new IOException("Mismatched message header"); } - callback.setNativeMessage(buf.toString()); - buf = null; + currentResponse.endIngest(); + callback.setNativeMessage(currentResponse.toString()); + currentResponse = null; break; default: throw new IOException("GHIDRA/decompiler alignment error"); } - if (buf == null) { + if (currentResponse == null) { type = readToBurst(); } else { - type = readToBuffer(buf); + type = readToBuffer(currentResponse); } } - return retbuf; } // Calls to the decompiler @@ -436,16 +406,20 @@ public class DecompileProcess { * @param cspecxml = string containing .cspec xml * @param tspecxml = XML string containing translator spec * @param coretypesxml = XML description of core data-types + * @param program is the program being registered * @throws IOException for problems with the pipe to the decompiler process * @throws DecompileException for problems executing the command */ public synchronized void registerProgram(DecompileCallback cback, String pspecxml, - String cspecxml, String tspecxml, String coretypesxml) + String cspecxml, String tspecxml, String coretypesxml, Program program) throws IOException, DecompileException { callback = cback; + programSource = program.getName(); + resultEncoder = new PackedEncode(); + paramDecoder = new PackedDecode(program.getAddressFactory()); + StringIngest response = new StringIngest(); // Don't use stringResponse setup(); - String restring = null; try { write(command_start); writeString("registerProgram"); @@ -454,13 +428,13 @@ public class DecompileProcess { writeString(tspecxml); writeString(coretypesxml); write(command_end); - restring = readResponse().toString(); + readResponse(response); } catch (IOException e) { statusGood = false; throw e; } - archId = Integer.parseInt(restring); + archId = Integer.parseInt(response.toString()); } /** @@ -476,42 +450,43 @@ public class DecompileProcess { // Once a program is deregistered, the process is never // used again statusGood = false; - String restring = null; write(command_start); writeString("deregisterProgram"); writeString(Integer.toString(archId)); write(command_end); - restring = readResponse().toString(); + StringIngest response = new StringIngest(); // Don't use stringResponse + readResponse(response); + int res = Integer.parseInt(response.toString()); callback = null; - int res = Integer.parseInt(restring); + programSource = null; + paramDecoder = null; + resultEncoder = null; return res; } /** * Send a single command to the decompiler with no parameters and return response * @param command is the name of the command to execute - * @return the response String + * @param response the response accumulator * @throws IOException for any problems with the pipe to the decompiler process * @throws DecompileException for any problems executing the command */ - public synchronized LimitedByteBuffer sendCommand(String command) + public synchronized void sendCommand(String command, ByteIngest response) throws IOException, DecompileException { if (!statusGood) { throw new IOException(command + " called on bad process"); } - LimitedByteBuffer resbuf = null; try { write(command_start); writeString(command); writeString(Integer.toString(archId)); write(command_end); - resbuf = readResponse(); + readResponse(response); } catch (IOException e) { statusGood = false; throw e; } - return resbuf; } public synchronized boolean isReady() { @@ -520,20 +495,19 @@ public class DecompileProcess { /** * @param command the decompiler should execute - * @param param an additional parameter for the command + * @param param an additional (encoded) parameter for the command * @param timeoutSecs the number of seconds to run before timing out - * @return the response string + * @param response the response accumulator * @throws IOException for any problems with the pipe to the decompiler process * @throws DecompileException for any problems while executing the command */ - public synchronized LimitedByteBuffer sendCommand1ParamTimeout(String command, String param, - int timeoutSecs) throws IOException, DecompileException { + public synchronized void sendCommand1ParamTimeout(String command, Encoder param, + int timeoutSecs, ByteIngest response) throws IOException, DecompileException { if (!statusGood) { throw new IOException(command + " called on bad process"); } - LimitedByteBuffer resbuf = null; int validatedTimeoutMs = getTimeoutMs(timeoutSecs); GTimerMonitor timerMonitor = GTimer.scheduleRunnable(validatedTimeoutMs, timeoutRunnable); @@ -543,7 +517,7 @@ public class DecompileProcess { writeString(Integer.toString(archId)); writeString(param); write(command_end); - resbuf = readResponse(); + readResponse(response); } catch (IOException e) { statusGood = false; @@ -556,7 +530,6 @@ public class DecompileProcess { finally { timerMonitor.cancel(); } - return resbuf; } private int getTimeoutMs(int timeoutSecs) { @@ -571,16 +544,15 @@ public class DecompileProcess { * @param command string to send * @param param1 is the first parameter string * @param param2 is the second parameter string - * @return the result string + * @param response the response accumulator * @throws IOException for any problems with the pipe to the decompiler process * @throws DecompileException for problems executing the command */ - public synchronized LimitedByteBuffer sendCommand2Params(String command, String param1, - String param2) throws IOException, DecompileException { + public synchronized void sendCommand2Params(String command, String param1, String param2, + ByteIngest response) throws IOException, DecompileException { if (!statusGood) { throw new IOException(command + " called on bad process"); } - LimitedByteBuffer resbuf = null; try { write(command_start); writeString(command); @@ -588,15 +560,19 @@ public class DecompileProcess { writeString(param1); writeString(param2); write(command_end); - resbuf = readResponse(); + readResponse(response); } catch (IOException e) { statusGood = false; throw e; } - return resbuf; } + /** + * Set an upper limit on the amount of data that can be sent back by the decompiler in response + * to a single command. + * @param maxResultSizeMBytes is the maximum size in megabytes + */ public void setMaxResultSize(int maxResultSizeMBytes) { this.maxResultSizeMBYtes = maxResultSizeMBytes; } @@ -604,64 +580,94 @@ public class DecompileProcess { /** * Send a command to the decompiler with one parameter and return the result * @param command is the command string - * @param param1 is the parameter as a string - * @return the result string + * @param param1 is the encoded parameter + * @param response is the result accumulator * @throws IOException for problems with the pipe to the decompiler process * @throws DecompileException for problems executing the command */ - public synchronized LimitedByteBuffer sendCommand1Param(String command, String param1) + public synchronized void sendCommand1Param(String command, Encoder param1, ByteIngest response) throws IOException, DecompileException { if (!statusGood) { throw new IOException(command + " called on bad process"); } - LimitedByteBuffer resbuf = null; try { write(command_start); writeString(command); writeString(Integer.toString(archId)); writeString(param1); write(command_end); - resbuf = readResponse(); + readResponse(response); + } + catch (IOException e) { + statusGood = false; + throw e; + } + } + + /** + * Send a command to the decompiler with one parameter and return the result + * @param command is the command string + * @param param1 is the parameter encoded as a string + * @param response is the result accumulator + * @throws IOException for problems with the pipe to the decompiler process + * @throws DecompileException for problems executing the command + */ + public synchronized void sendCommand1Param(String command, String param1, ByteIngest response) + throws IOException, DecompileException { + if (!statusGood) { + throw new IOException(command + " called on bad process"); + } + try { + write(command_start); + writeString(command); + writeString(Integer.toString(archId)); + writeString(param1); + write(command_end); + readResponse(response); } catch (IOException e) { statusGood = false; throw e; } - return resbuf; } // Calls from the decompiler - private void getRegister() throws IOException { - String name = readQueryString(); - byte[] res = callback.getRegister(name); + private void getRegister() throws IOException, DecoderException { + resultEncoder.clear(); + String name = paramDecoder.readString(ATTRIB_NAME); + callback.getRegister(name, resultEncoder); write(query_response_start); - if (res.length != 0) { - writeBytes(res); + if (!resultEncoder.isEmpty()) { + writeString(resultEncoder); } write(query_response_end); } - private void getRegisterName() throws IOException { - String addr = readQueryString(); + private void getRegisterName() throws IOException, DecoderException { + int el = paramDecoder.openElement(ELEM_ADDR); + Address addr = AddressXML.decodeFromAttributes(paramDecoder); + int size = (int) paramDecoder.readSignedInteger(ATTRIB_SIZE); + paramDecoder.closeElement(el); - String res = callback.getRegisterName(addr); + String res = callback.getRegisterName(addr, size); write(query_response_start); writeString(res); write(query_response_end); } - private void getTrackedRegisters() throws IOException { - String addr = readQueryString(); - byte[] res = callback.getTrackedRegisters(addr); + private void getTrackedRegisters() throws IOException, DecoderException { + resultEncoder.clear(); + Address addr = AddressXML.decode(paramDecoder); + callback.getTrackedRegisters(addr, resultEncoder); write(query_response_start); - writeBytes(res); + writeString(resultEncoder); write(query_response_end); } - private void getUserOpName() throws IOException { - String indexStr = readQueryString(); - String res = callback.getUserOpName(indexStr); + private void getUserOpName() throws IOException, DecoderException { + int index = (int) paramDecoder.readSignedInteger(ATTRIB_INDEX); + String res = callback.getUserOpName(index); if (res == null) { res = ""; } @@ -670,70 +676,72 @@ public class DecompileProcess { write(query_response_end); } - private void getPcodePacked() throws IOException { - String addr = readQueryString(); - PackedBytes out = callback.getPcodePacked(addr); + private void getPcode() throws IOException, DecoderException { + resultEncoder.clear(); + Address addr = AddressXML.decode(paramDecoder); + callback.getPcode(addr, resultEncoder); write(query_response_start); - if ((out != null) && (out.size() != 0)) { - writeBytes(out); + if (!resultEncoder.isEmpty()) { + writeString(resultEncoder); } write(query_response_end); } - private void getPcodeInject(int type) throws IOException { - String name = readQueryString(); - String context = readQueryString(); - byte[] res = callback.getPcodeInject(name, context, type); + private void getPcodeInject(int type) throws IOException, DecoderException { + resultEncoder.clear(); + String name = paramDecoder.readString(ATTRIB_NAME); + callback.getPcodeInject(name, paramDecoder, type, resultEncoder); write(query_response_start); - if (res.length != 0) { - writeBytes(res); + if (!resultEncoder.isEmpty()) { + writeString(resultEncoder); } write(query_response_end); } - private void getCPoolRef() throws IOException { - String liststring = readQueryString(); - String[] split = liststring.split(","); - long[] refs = new long[split.length]; - for (int i = 0; i < split.length; ++i) { - refs[i] = Long.parseUnsignedLong(split[i], 16); + private void getCPoolRef() throws IOException, DecoderException { + resultEncoder.clear(); + int size = (int) paramDecoder.readSignedInteger(ATTRIB_SIZE); + long refs[] = new long[size]; + for (int i = 0; i < size; ++i) { + int el = paramDecoder.openElement(ELEM_VALUE); + refs[i] = paramDecoder.readUnsignedInteger(ATTRIB_CONTENT); + paramDecoder.closeElement(el); } - byte[] res = callback.getCPoolRef(refs); + callback.getCPoolRef(refs, resultEncoder); write(query_response_start); - if (res.length != 0) { - writeBytes(res); + if (!resultEncoder.isEmpty()) { + writeString(resultEncoder); } write(query_response_end); } - private void getMappedSymbolsXML() throws IOException { - String addr = readQueryString(); + private void getMappedSymbols() throws IOException, DecoderException { + resultEncoder.clear(); + Address addr = AddressXML.decode(paramDecoder); + callback.getMappedSymbols(addr, resultEncoder); - byte[] res = callback.getMappedSymbolsXML(addr); write(query_response_start); - if (res.length != 0) { - writeBytes(res); + if (!resultEncoder.isEmpty()) { + writeString(resultEncoder); } write(query_response_end); } - private void getNamespacePath() throws IOException { - String idString = readQueryString(); - long id = Long.parseLong(idString, 16); - byte[] res = callback.getNamespacePath(id); + private void getNamespacePath() throws IOException, DecoderException { + resultEncoder.clear(); + long id = paramDecoder.readUnsignedInteger(ATTRIB_ID); + callback.getNamespacePath(id, resultEncoder); write(query_response_start); - if (res.length != 0) { - writeBytes(res); + if (!resultEncoder.isEmpty()) { + writeString(resultEncoder); } write(query_response_end); } - private void isNameUsed() throws IOException { - String name = readQueryString(); - String startString = readQueryString(); - String stopString = readQueryString(); - long startId = Long.parseLong(startString, 16); - long stopId = Long.parseLong(stopString, 16); + private void isNameUsed() throws IOException, DecoderException { + String name = paramDecoder.readString(ATTRIB_NAME); + long startId = paramDecoder.readUnsignedInteger(ATTRIB_FIRST); + long stopId = paramDecoder.readUnsignedInteger(ATTRIB_LAST); boolean res = callback.isNameUsed(name, startId, stopId); write(query_response_start); write(string_start); @@ -742,20 +750,20 @@ public class DecompileProcess { write(query_response_end); } - private void getExternalRefXML() throws IOException { - String refaddr = readQueryString(); - byte[] res = callback.getExternalRefXML(refaddr); + private void getExternalRef() throws IOException, DecoderException { + resultEncoder.clear(); + Address addr = AddressXML.decode(paramDecoder); + callback.getExternalRef(addr, resultEncoder); write(query_response_start); - if (res.length != 0) { - writeBytes(res); + if (!resultEncoder.isEmpty()) { + writeString(resultEncoder); } write(query_response_end); } - private void getSymbol() throws IOException { - String addr = readQueryString(); - - String res = callback.getSymbol(addr); + private void getCodeLabel() throws IOException, DecoderException { + Address addr = AddressXML.decode(paramDecoder); + String res = callback.getCodeLabel(addr); if (res == null) { res = ""; } @@ -764,29 +772,35 @@ public class DecompileProcess { write(query_response_end); } - private void getComments() throws IOException { - String addr = readQueryString(); - String flags = readQueryString(); - byte[] res = callback.getComments(addr, flags); + private void getComments() throws IOException, DecoderException { + resultEncoder.clear(); + int types = (int) paramDecoder.readUnsignedInteger(ATTRIB_TYPE); + Address addr = AddressXML.decode(paramDecoder); + + callback.getComments(addr, types, resultEncoder); write(query_response_start); - writeBytes(res); + writeString(resultEncoder); write(query_response_end); } - private void getType() throws IOException { - String name = readQueryString(); - long id = SpecXmlUtils.decodeLong(readQueryString()); - byte[] res = callback.getType(name, id); + private void getDataType() throws IOException, DecoderException { + resultEncoder.clear(); + String name = paramDecoder.readString(ATTRIB_NAME); + long id = paramDecoder.readSignedInteger(ATTRIB_ID); + callback.getDataType(name, id, resultEncoder); write(query_response_start); - if (res.length != 0) { - writeBytes(res); + if (!resultEncoder.isEmpty()) { + writeString(resultEncoder); } write(query_response_end); } - private void getBytes() throws IOException { - String size = readQueryString(); - byte[] res = callback.getBytes(size); + private void getBytes() throws IOException, DecoderException { + int el = paramDecoder.openElement(ELEM_ADDR); + Address addr = AddressXML.decodeFromAttributes(paramDecoder); + int size = (int) paramDecoder.readSignedInteger(ATTRIB_SIZE); + paramDecoder.closeElement(el); + byte[] res = callback.getBytes(addr, size); write(query_response_start); if ((res != null) && (res.length > 0)) { write(byte_start); @@ -801,11 +815,13 @@ public class DecompileProcess { write(query_response_end); } - private void getStringData() throws IOException { - String addr = readQueryString(); - String dtName = readQueryString(); - long dtId = SpecXmlUtils.decodeLong(readQueryString()); - DecompileCallback.StringData stringData = callback.getStringData(addr, dtName, dtId); + private void getStringData() throws IOException, DecoderException { + int maxChars = (int) paramDecoder.readSignedInteger(ATTRIB_MAXSIZE); + String dtName = paramDecoder.readString(ATTRIB_TYPE); + long dtId = paramDecoder.readUnsignedInteger(ATTRIB_ID); + Address addr = AddressXML.decode(paramDecoder); + DecompileCallback.StringData stringData = + callback.getStringData(addr, maxChars, dtName, dtId); write(query_response_start); if (stringData != null) { byte[] res = stringData.byteData; diff --git a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/DecompileResults.java b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/DecompileResults.java index ca7ec992c7..df900cd542 100644 --- a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/DecompileResults.java +++ b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/DecompileResults.java @@ -17,8 +17,6 @@ package ghidra.app.decompiler; import static ghidra.program.model.pcode.ElementId.*; -import java.io.InputStream; - import ghidra.program.model.lang.CompilerSpec; import ghidra.program.model.lang.Language; import ghidra.program.model.listing.Function; @@ -64,7 +62,7 @@ public class DecompileResults { private DecompileProcess.DisposeState processState; public DecompileResults(Function f, Language language, CompilerSpec compilerSpec, - PcodeDataTypeManager d, String e, InputStream raw, + PcodeDataTypeManager d, String e, Decoder decoder, DecompileProcess.DisposeState processState) { function = f; this.language = language; @@ -75,7 +73,7 @@ public class DecompileResults { hparamid = null; docroot = null; //dumpResults(raw); - decodeStream(raw); + decodeStream(decoder); } // private void dumpResults(String raw) { @@ -203,14 +201,11 @@ public class DecompileResults { return printer.print(true); } - private void decodeStream(InputStream rawstream) { - if (rawstream == null) { + private void decodeStream(Decoder decoder) { + if (decoder == null || decoder.isEmpty()) { return; } - XmlDecode decoder = new XmlDecode(function.getProgram().getAddressFactory()); try { - decoder.ingestStream(rawstream, - "Decompiler results for function at " + function.getEntryPoint()); hfunc = null; hparamid = null; docroot = null; @@ -243,13 +238,13 @@ public class DecompileResults { } decoder.closeElement(docel); } - catch (PcodeXMLException e) { // Error while walking the DOM + catch (DecoderException e) { // Error while walking the DOM errMsg = e.getMessage(); hfunc = null; hparamid = null; return; } - catch (RuntimeException e) { // Exception from the raw parser + catch (Exception e) { // Exception with the underlying stream errMsg = e.getMessage(); hfunc = null; hparamid = null; diff --git a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/LimitedByteBuffer.java b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/LimitedByteBuffer.java deleted file mode 100644 index 998af5644b..0000000000 --- a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/LimitedByteBuffer.java +++ /dev/null @@ -1,82 +0,0 @@ -/* ### - * IP: GHIDRA - * REVIEWED: YES - * - * 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. - */ -package ghidra.app.decompiler; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.util.Arrays; - -/** - * Class for accumulating bytes into an automatically expanding buffer with an explicit upper limit to the size - * - */ -public class LimitedByteBuffer { - byte value[]; - int count; // Current number of characters - int absmax; // Absolute maximum number of characters - - /** - * Create the buffer specifying its initial and limiting capacity - * @param initial is the number of bytes to be initially allocated for the buffer - * @param amax is the absolute maximum number of bytes the buffer is allowed to expand to before throwing exceptions - */ - public LimitedByteBuffer(int initial,int amax) { - value = new byte[initial]; - count = 0; - absmax = amax; - } - - /** - * Append a byte into the buffer. The buffer's internal storage is expanded as necessary, but only up to - * the specified maximum. If this append exceeds that maximum, then an exception is thrown - * @param b is the byte to append - * @throws IOException - */ - public void append(byte b) throws IOException { - int newCount = count + 1; - if (newCount > value.length) { - if (newCount > absmax) { - int maxResultSizeMBytes = absmax >> 20; - throw new IOException("Decompiler results exceeded payload limit of " + - maxResultSizeMBytes + " MBytes"); - } - int newcapacity = value.length * 2; - if (newcapacity < 0) - newcapacity = Integer.MAX_VALUE; - if (newcapacity > absmax) - newcapacity = absmax; - value = Arrays.copyOf(value, newcapacity); - } - value[count++] = b; - } - - /** - * Generate an InputStream from the bytes that have been appended to the buffer - * The buffer is NOT copied - * @return the new InputStream - */ - public ByteArrayInputStream getInputStream() { - return new ByteArrayInputStream(value,0,count); - } - - /* (non-Javadoc) - * @see java.lang.Object#toString() - */ - public String toString() { - return new String(value,0,count); - } -} diff --git a/Ghidra/Features/FunctionGraphDecompilerExtension/src/main/java/ghidra/app/plugin/core/functiongraph/graph/layout/DecompilerNestedLayout.java b/Ghidra/Features/FunctionGraphDecompilerExtension/src/main/java/ghidra/app/plugin/core/functiongraph/graph/layout/DecompilerNestedLayout.java index 7bb0ba38ea..13dead4195 100644 --- a/Ghidra/Features/FunctionGraphDecompilerExtension/src/main/java/ghidra/app/plugin/core/functiongraph/graph/layout/DecompilerNestedLayout.java +++ b/Ghidra/Features/FunctionGraphDecompilerExtension/src/main/java/ghidra/app/plugin/core/functiongraph/graph/layout/DecompilerNestedLayout.java @@ -142,7 +142,7 @@ public class DecompilerNestedLayout extends AbstractFGLayout { throw new RuntimeException("Unable to initialize: " + ifc.getLastMessage()); } - outgraph = ifc.structureGraph(ingraph, program.getAddressFactory(), 0, monitor); + outgraph = ifc.structureGraph(ingraph, 0, monitor); } finally { ifc.dispose(); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/plugin/processors/sleigh/PcodeEmit.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/plugin/processors/sleigh/PcodeEmit.java index 9c0a7f29c8..c4a2d26416 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/plugin/processors/sleigh/PcodeEmit.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/plugin/processors/sleigh/PcodeEmit.java @@ -19,6 +19,7 @@ */ package ghidra.app.plugin.processors.sleigh; +import java.io.IOException; import java.util.ArrayList; import ghidra.app.plugin.processors.sleigh.symbol.*; @@ -188,8 +189,9 @@ public abstract class PcodeEmit { *
  • last pcode op has fall-through
  • *
  • internal label used to branch beyond last pcode op
  • * + * @throws IOException for stream errors emitting ops */ - void resolveFinalFallthrough() { + void resolveFinalFallthrough() throws IOException { try { if (fallOverride == null || fallOverride.equals(getStartAddress().add(fallOffset))) { return; @@ -207,9 +209,10 @@ public abstract class PcodeEmit { dump(startAddress, PcodeOp.BRANCH, new VarnodeData[] { dest }, 1, null); } - abstract void dump(Address instrAddr, int opcode, VarnodeData[] in, int isize, VarnodeData out); + abstract void dump(Address instrAddr, int opcode, VarnodeData[] in, int isize, VarnodeData out) + throws IOException; - private boolean dumpBranchOverride(OpTpl opt) { + private boolean dumpBranchOverride(OpTpl opt) throws IOException { int opcode = opt.getOpcode(); VarnodeTpl[] inputs = opt.getInput(); if (opcode == PcodeOp.CALL) { @@ -227,7 +230,7 @@ public abstract class PcodeEmit { return false; } - private void dumpNullReturn() { + private void dumpNullReturn() throws IOException { VarnodeTpl nullAddr = new VarnodeTpl(new ConstTpl(const_space), new ConstTpl(ConstTpl.REAL, 0), @@ -237,7 +240,7 @@ public abstract class PcodeEmit { dump(retOpt); } - private boolean dumpCallOverride(OpTpl opt, boolean returnAfterCall) { + private boolean dumpCallOverride(OpTpl opt, boolean returnAfterCall) throws IOException { int opcode = opt.getOpcode(); VarnodeTpl[] inputs = opt.getInput(); if (opcode == PcodeOp.BRANCH) { @@ -316,7 +319,7 @@ public abstract class PcodeEmit { return false; } - private boolean dumpReturnOverride(OpTpl opt) { + private boolean dumpReturnOverride(OpTpl opt) throws IOException { int opcode = opt.getOpcode(); VarnodeTpl[] inputs = opt.getInput(); @@ -416,7 +419,7 @@ public abstract class PcodeEmit { return false; } - private boolean dumpFlowOverride(OpTpl opt) { + private boolean dumpFlowOverride(OpTpl opt) throws IOException { if (flowOverride == null || opt.getOutput() != null) { return false; // only call, branch and return instructions can be affected } @@ -483,8 +486,9 @@ public abstract class PcodeEmit { * We assume the location of the base pointer is in dyncache[1] * @param dyncache is the existing array * @param vn is the V_OFFSET_PLUS VarnodeTpl to adjust for + * @throws IOException for stream errors emitting ops */ - private void generatePointerAdd(VarnodeData[] dyncache, VarnodeTpl vn) { + private void generatePointerAdd(VarnodeData[] dyncache, VarnodeTpl vn) throws IOException { long offsetPlus = vn.getOffset().getReal() & 0xffff; if (offsetPlus == 0) { return; @@ -505,7 +509,7 @@ public abstract class PcodeEmit { dyncache[1] = tmpData; } - private void dump(OpTpl opt) { + private void dump(OpTpl opt) throws IOException { VarnodeData[] dyncache = null; VarnodeTpl vn, outvn; @@ -581,7 +585,7 @@ public abstract class PcodeEmit { } private void appendBuild(OpTpl bld, int secnum) - throws UnknownInstructionException, MemoryAccessException { + throws UnknownInstructionException, MemoryAccessException, IOException { // Recover operand index from build statement int index = (int) bld.getInput()[0].getOffset().getReal(); Symbol sym = walker.getConstructor().getOperand(index).getDefiningSymbol(); @@ -612,8 +616,10 @@ public abstract class PcodeEmit { * @param op is the DELAYSLOT directive * @throws UnknownInstructionException for problems finding the delay slot Instruction * @throws MemoryAccessException for problems resolving details of the delay slot Instruction + * @throws IOException for stream errors emitting ops */ - private void delaySlot(OpTpl op) throws UnknownInstructionException, MemoryAccessException { + private void delaySlot(OpTpl op) + throws UnknownInstructionException, MemoryAccessException, IOException { if (inDelaySlot) { throw new SleighException( @@ -656,9 +662,10 @@ public abstract class PcodeEmit { * @param secnum is the section number of the section containing the CROSSBUILD directive * @throws UnknownInstructionException for problems finding the referenced Instruction * @throws MemoryAccessException for problems resolving details of the referenced Instruction + * @throws IOException for stream errors emitting ops */ private void appendCrossBuild(OpTpl bld, int secnum) - throws UnknownInstructionException, MemoryAccessException { + throws UnknownInstructionException, MemoryAccessException, IOException { if (secnum >= 0) { throw new SleighException( "CROSSBUILD recursion problem for instruction at " + walker.getAddr()); @@ -698,7 +705,7 @@ public abstract class PcodeEmit { } public void build(ConstructTpl construct, int secnum) - throws UnknownInstructionException, MemoryAccessException { + throws UnknownInstructionException, MemoryAccessException, IOException { if (construct == null) { throw new NotYetImplementedException( "Semantics for this instruction are not implemented"); @@ -739,9 +746,10 @@ public abstract class PcodeEmit { * @param secnum index of the section to be built * @throws MemoryAccessException for problems resolving details of the underlying Instruction * @throws UnknownInstructionException for problems finding the underlying Instruction + * @throws IOException for stream errors emitting ops */ private void buildEmpty(Constructor ct, int secnum) - throws UnknownInstructionException, MemoryAccessException { + throws UnknownInstructionException, MemoryAccessException, IOException { int numops = ct.getNumOperands(); for (int i = 0; i < numops; ++i) { diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/plugin/processors/sleigh/PcodeEmitPacked.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/plugin/processors/sleigh/PcodeEmitPacked.java index 935d3fa434..69285b65dd 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/plugin/processors/sleigh/PcodeEmitPacked.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/plugin/processors/sleigh/PcodeEmitPacked.java @@ -15,22 +15,21 @@ */ package ghidra.app.plugin.processors.sleigh; +import static ghidra.program.model.pcode.AttributeId.*; +import static ghidra.program.model.pcode.ElementId.*; + +import java.io.IOException; import java.util.ArrayList; import ghidra.program.model.address.Address; -import ghidra.program.model.address.AddressSpace; import ghidra.program.model.lang.InstructionContext; -import ghidra.program.model.lang.PackedBytes; -import ghidra.program.model.pcode.PcodeOp; -import ghidra.program.model.pcode.PcodeOverride; +import ghidra.program.model.pcode.*; /** * * */ public class PcodeEmitPacked extends PcodeEmit { - public final static int unimpl_tag = 0x20, inst_tag = 0x21, op_tag = 0x22, void_tag = 0x23, - spaceid_tag = 0x24, addrsz_tag = 0x25, end_tag = 0x60; // End of a number public class LabelRef { public int opIndex; // Index of operation referencing the label @@ -46,33 +45,32 @@ public class PcodeEmitPacked extends PcodeEmit { } } - private PackedBytes buf; + private PatchEncoder encoder; private ArrayList labelref = null; - - /** - * Pcode emitter constructor for producing a packed binary representation - * for unimplemented or empty responses. - */ - public PcodeEmitPacked() { - super(); - buf = new PackedBytes(64); - } + private boolean hasRelativePatch = false; /** * Pcode emitter constructor for producing a packed binary representation. + * @param encoder is the stream encoder to emit to * @param walk parser walker * @param ictx instruction contexts * @param fallOffset default instruction fall offset (i.e., instruction length including delay slotted instructions) * @param override required if pcode overrides are to be utilized */ - public PcodeEmitPacked(ParserWalker walk, InstructionContext ictx, int fallOffset, - PcodeOverride override) { + public PcodeEmitPacked(PatchEncoder encoder, ParserWalker walk, InstructionContext ictx, + int fallOffset, PcodeOverride override) { super(walk, ictx, fallOffset, override); - buf = new PackedBytes(512); + this.encoder = encoder; } - public PackedBytes getPackedBytes() { - return buf; + public void emitHeader() throws IOException { + encoder.openElement(ELEM_INST); + encoder.writeSignedInteger(ATTRIB_OFFSET, getFallOffset()); + AddressXML.encode(encoder, getStartAddress()); + } + + public void emitTail() throws IOException { + encoder.closeElement(ELEM_INST); } @Override @@ -90,8 +88,9 @@ public class PcodeEmitPacked extends PcodeEmit { mask >>>= (8 - ref.labelSize) * 8; res &= mask; } - // We need to skip over op_tag, op_code, void_tag, addrsz_tag, and spc bytes - insertOffset(ref.streampos + 5, res); // Insert the final offset into the stream + if (!encoder.patchIntegerAttribute(ref.streampos, ATTRIB_OFFSET, res)) { + throw new SleighException("PcodeEmitPacked: Unable to patch relative offset"); + } } } @@ -100,92 +99,60 @@ public class PcodeEmitPacked extends PcodeEmit { */ @Override void addLabelRef() { + // We know we need to do patching on a particular input parameter if (labelref == null) { labelref = new ArrayList<>(); } + // Delay putting in the LabelRef until we are ready to emit the parameter + hasRelativePatch = true; + } + + /** + * Create the LabelRef now that the next element written will be the parameter needing a patch + */ + private void addLabelRefDelayed() { int labelIndex = (int) incache[0].offset; int labelSize = incache[0].size; - // Force the emitter to write out a maximum length encoding (12 bytes) of a long + // Force the encoder to write out a maximum length encoding of a long // so that we have space to insert whatever value we need to when this relative is resolved incache[0].offset = -1; - labelref.add(new LabelRef(numOps, labelIndex, labelSize, buf.size())); + labelref.add(new LabelRef(numOps, labelIndex, labelSize, encoder.size())); + hasRelativePatch = false; // Mark patch as handled } - /* (non-Javadoc) - * @see ghidra.app.plugin.processors.sleigh.PcodeEmit#dump(ghidra.program.model.address.Address, int, ghidra.app.plugin.processors.sleigh.VarnodeData[], int, ghidra.app.plugin.processors.sleigh.VarnodeData) - */ @Override - void dump(Address instrAddr, int opcode, VarnodeData[] in, int isize, VarnodeData out) { + void dump(Address instrAddr, int opcode, VarnodeData[] in, int isize, VarnodeData out) + throws IOException { opcode = checkOverrides(opcode, in); checkOverlays(opcode, in, isize, out); - buf.write(op_tag); - buf.write(opcode + 0x20); + encoder.openElement(ELEM_OP); + encoder.writeSignedInteger(ATTRIB_CODE, opcode); + encoder.writeSignedInteger(ATTRIB_SIZE, isize); if (out == null) { - buf.write(void_tag); + encoder.openElement(ELEM_VOID); + encoder.closeElement(ELEM_VOID); } else { - dumpVarnodeData(out); + out.encode(encoder); } int i = 0; if ((opcode == PcodeOp.LOAD) || (opcode == PcodeOp.STORE)) { dumpSpaceId(in[0]); i = 1; } + else if (hasRelativePatch) { + addLabelRefDelayed(); + } for (; i < isize; ++i) { - dumpVarnodeData(in[i]); + in[i].encode(encoder); } - buf.write(end_tag); + encoder.closeElement(ELEM_OP); } - private void dumpSpaceId(VarnodeData v) { - buf.write(spaceid_tag); - int spcindex = ((int) v.offset >> AddressSpace.ID_UNIQUE_SHIFT); - buf.write(spcindex + 0x20); - } - - private void dumpVarnodeData(VarnodeData v) { - buf.write(addrsz_tag); - int spcindex = v.space.getUnique(); - buf.write(spcindex + 0x20); - dumpOffset(v.offset); - buf.write(v.size + 0x20); - } - - public void write(int val) { - buf.write(val); - } - - /** - * Encode and dump an integer value to the packed byte stream - * @param val is the integer to write - */ - public void dumpOffset(long val) { - while (val != 0) { - int chunk = (int) (val & 0x3f); - val >>>= 6; - buf.write(chunk + 0x20); - } - buf.write(end_tag); - } - - private void insertOffset(int streampos, long val) { - while (val != 0) { - if (buf.getByte(streampos) == end_tag) { - throw new SleighException("Could not properly insert relative jump offset"); - } - int chunk = (int) (val & 0x3f); - val >>>= 6; - buf.insertByte(streampos, chunk + 0x20); - streampos += 1; - } - for (int i = 0; i < 11; ++i) { - if (buf.getByte(streampos) == end_tag) { - return; - } - buf.insertByte(streampos, 0x20); // Zero fill - streampos += 1; - } - throw new SleighException("Could not find terminator while inserting relative jump offset"); + private void dumpSpaceId(VarnodeData v) throws IOException { + encoder.openElement(ELEM_SPACEID); + encoder.writeSpaceId(ATTRIB_NAME, v.offset); + encoder.closeElement(ELEM_SPACEID); } } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/plugin/processors/sleigh/SleighInstructionPrototype.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/plugin/processors/sleigh/SleighInstructionPrototype.java index 041197414a..7ec62bf239 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/plugin/processors/sleigh/SleighInstructionPrototype.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/plugin/processors/sleigh/SleighInstructionPrototype.java @@ -15,6 +15,7 @@ */ package ghidra.app.plugin.processors.sleigh; +import java.io.IOException; import java.util.*; import ghidra.app.plugin.assembler.sleigh.sem.AssemblyResolvedPatterns; @@ -1018,7 +1019,8 @@ public class SleighInstructionPrototype implements InstructionPrototype { } @Override - public PackedBytes getPcodePacked(InstructionContext context, PcodeOverride override) { + public void getPcodePacked(PatchEncoder encoder, InstructionContext context, + PcodeOverride override) throws IOException { int fallOffset = getLength(); try { SleighParserContext protoContext = (SleighParserContext) context.getParserContext(); @@ -1037,23 +1039,17 @@ public class SleighInstructionPrototype implements InstructionPrototype { } ParserWalker walker = new ParserWalker(protoContext); walker.baseState(); - PcodeEmitPacked emit = new PcodeEmitPacked(walker, context, fallOffset, override); - emit.write(PcodeEmitPacked.inst_tag); - emit.dumpOffset(emit.getFallOffset()); - - // Write out the sequence number as a space and an offset - Address instrAddr = emit.getStartAddress(); - int spcindex = instrAddr.getAddressSpace().getUnique(); - emit.write(spcindex + 0x20); - emit.dumpOffset(instrAddr.getOffset()); + PcodeEmitPacked emit = + new PcodeEmitPacked(encoder, walker, context, fallOffset, override); + emit.emitHeader(); emit.build(walker.getConstructor().getTempl(), -1); emit.resolveRelatives(); if (!isindelayslot) { emit.resolveFinalFallthrough(); } - emit.write(PcodeEmitPacked.end_tag); // Terminate the inst_tag - return emit.getPackedBytes(); + emit.emitTail(); + return; } catch (NotYetImplementedException e) { // unimpl @@ -1061,10 +1057,10 @@ public class SleighInstructionPrototype implements InstructionPrototype { catch (Exception e) { Msg.error(this, "Pcode error at " + context.getAddress() + ": " + e.getMessage()); } - PcodeEmitPacked emit = new PcodeEmitPacked(); - emit.write(PcodeEmitPacked.unimpl_tag); - emit.dumpOffset(length); - return emit.getPackedBytes(); + encoder.clear(); + encoder.openElement(ElementId.ELEM_UNIMPL); + encoder.writeSignedInteger(AttributeId.ATTRIB_OFFSET, length); + encoder.closeElement(ElementId.ELEM_UNIMPL); } @Override diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/plugin/processors/sleigh/VarnodeData.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/plugin/processors/sleigh/VarnodeData.java index e75c48a2ea..5c60196e6b 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/plugin/processors/sleigh/VarnodeData.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/plugin/processors/sleigh/VarnodeData.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,15 +19,32 @@ */ package ghidra.app.plugin.processors.sleigh; +import static ghidra.program.model.pcode.AttributeId.*; +import static ghidra.program.model.pcode.ElementId.*; + +import java.io.IOException; + import ghidra.program.model.address.AddressSpace; +import ghidra.program.model.pcode.Encoder; /** - * - * * All the resolved pieces of data needed to build a Varnode */ public class VarnodeData { public AddressSpace space; public long offset; public int size; + + /** + * Encode the data to stream as an \ element + * @param encoder is the stream encoder + * @throws IOException for errors writing to the underlying stream + */ + public void encode(Encoder encoder) throws IOException { + encoder.openElement(ELEM_ADDR); + encoder.writeSpace(ATTRIB_SPACE, space); + encoder.writeUnsignedInteger(ATTRIB_OFFSET, offset); + encoder.writeSignedInteger(ATTRIB_SIZE, size); + encoder.closeElement(ELEM_ADDR); + } } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/InjectContext.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/InjectContext.java index 2e05c7f693..453c6f6b3e 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/InjectContext.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/InjectContext.java @@ -37,7 +37,7 @@ public class InjectContext { public InjectContext() { } - public void decode(Decoder decoder) throws PcodeXMLException { + public void decode(Decoder decoder) throws DecoderException { int el = decoder.openElement(ELEM_CONTEXT); baseAddr = AddressXML.decode(decoder); callAddr = AddressXML.decode(decoder); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/InjectPayloadSleigh.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/InjectPayloadSleigh.java index 81f72fb657..9b3cbeafdb 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/InjectPayloadSleigh.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/InjectPayloadSleigh.java @@ -27,7 +27,6 @@ import ghidra.app.plugin.processors.sleigh.template.*; import ghidra.program.model.address.Address; import ghidra.program.model.address.AddressFactory; import ghidra.program.model.listing.Program; -import ghidra.program.model.mem.MemoryAccessException; import ghidra.program.model.pcode.*; import ghidra.util.xml.SpecXmlUtils; import ghidra.xml.*; @@ -171,11 +170,7 @@ public class InjectPayloadSleigh implements InjectPayload { setupParameters(context, walker); emit.build(pcodeTemplate, -1); } - catch (UnknownInstructionException e) { // Should not be happening in a CallFixup - e.printStackTrace(); - return; - } - catch (MemoryAccessException e) { // Should not be happening in a CallFixup + catch (Exception e) { // Should not be happening in a CallFixup e.printStackTrace(); return; } @@ -236,8 +231,7 @@ public class InjectPayloadSleigh implements InjectPayload { public void encode(Encoder encoder) throws IOException { encoder.openElement(ELEM_PCODE); if (type == CALLMECHANISM_TYPE && subType >= 0) { - encoder.writeString(ATTRIB_INJECT, - (subType == 0) ? "uponentry" : "uponreturn"); + encoder.writeString(ATTRIB_INJECT, (subType == 0) ? "uponentry" : "uponreturn"); } if (paramShift != 0) { encoder.writeSignedInteger(ATTRIB_PARAMSHIFT, paramShift); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/InstructionPrototype.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/InstructionPrototype.java index 1823d08818..72c64fd101 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/InstructionPrototype.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/InstructionPrototype.java @@ -15,13 +15,13 @@ */ package ghidra.program.model.lang; +import java.io.IOException; import java.util.ArrayList; import ghidra.program.model.address.Address; import ghidra.program.model.mem.MemBuffer; import ghidra.program.model.mem.MemoryAccessException; -import ghidra.program.model.pcode.PcodeOp; -import ghidra.program.model.pcode.PcodeOverride; +import ghidra.program.model.pcode.*; import ghidra.program.model.scalar.Scalar; import ghidra.program.model.symbol.FlowType; import ghidra.program.model.symbol.RefType; @@ -293,12 +293,14 @@ public interface InstructionPrototype { public PcodeOp[] getPcode(InstructionContext context, PcodeOverride override); /** - * Same as getPcode but returns the operations in a packed format to optimize transfer to other processes + * Same as getPcode but emits the operations directly to an encoder to optimize transfer to other processes + * @param encoder is the encoder receiving the operations * @param context the instruction context * @param override if not null, may indicate that different elements of the pcode generation are overridden - * @return the set of packed bytes encoding the p-code + * @throws IOException for errors writing to any stream underlying the encoder */ - public PackedBytes getPcodePacked(InstructionContext context, PcodeOverride override); + public void getPcodePacked(PatchEncoder encoder, InstructionContext context, + PcodeOverride override) throws IOException; /** * Get an array of PCode operations (micro code) that a particular operand diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/InvalidPrototype.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/InvalidPrototype.java index a10c492083..0f0e86a98d 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/InvalidPrototype.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/InvalidPrototype.java @@ -15,13 +15,13 @@ */ package ghidra.program.model.lang; +import java.io.IOException; import java.util.ArrayList; import ghidra.program.model.address.Address; import ghidra.program.model.mem.MemBuffer; import ghidra.program.model.mem.MemoryAccessException; -import ghidra.program.model.pcode.PcodeOp; -import ghidra.program.model.pcode.PcodeOverride; +import ghidra.program.model.pcode.*; import ghidra.program.model.scalar.Scalar; import ghidra.program.model.symbol.FlowType; import ghidra.program.model.symbol.RefType; @@ -155,8 +155,9 @@ public class InvalidPrototype implements InstructionPrototype, ParserContext { } @Override - public PackedBytes getPcodePacked(InstructionContext context, PcodeOverride override) { - return null; + public void getPcodePacked(PatchEncoder encoder, InstructionContext context, + PcodeOverride override) throws IOException { + // Does not emit anything } @Override diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/AddressXML.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/AddressXML.java index 4ba100f2c0..5f809fc89a 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/AddressXML.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/AddressXML.java @@ -365,9 +365,9 @@ public class AddressXML { * Create an address from "space" and "offset" attributes of the current element * @param decoder is the stream decoder * @return the decoded Address - * @throws PcodeXMLException for any problems decoding the stream + * @throws DecoderException for any problems decoding the stream */ - public static Address decodeFromAttributes(Decoder decoder) throws PcodeXMLException { + public static Address decodeFromAttributes(Decoder decoder) throws DecoderException { AddressSpace spc = null; long offset = -1; for (;;) { @@ -397,10 +397,10 @@ public class AddressXML { * * An empty \ element, with no attributes, results in Address.NO_ADDRESS being returned. * @param decoder is the stream decoder - * @return Address created from XML info - * @throws PcodeXMLException for any problems decoding the stream + * @return Address created from decode info + * @throws DecoderException for any problems decoding the stream */ - public static Address decode(Decoder decoder) throws PcodeXMLException { + public static Address decode(Decoder decoder) throws DecoderException { int el = decoder.openElement(); if (el == ELEM_SPACEID.id()) { AddressSpace spc = decoder.readSpace(ATTRIB_NAME); @@ -431,6 +431,7 @@ public class AddressXML { } decoder.closeElement(el); if (spc == null) { + // EXTERNAL_SPACE is currently a placeholder for an unsupported decompiler address space return Address.NO_ADDRESS; } return spc.getAddress(offset); @@ -586,7 +587,9 @@ public class AddressXML { encoder.openElement(ELEM_ADDR); encoder.writeSpace(ATTRIB_SPACE, AddressSpace.VARIABLE_SPACE); encoder.writeString(ATTRIB_PIECE1, encodeVarnodePiece(varnodes[0])); - encoder.writeString(ATTRIB_PIECE2, encodeVarnodePiece(varnodes[1])); + if (varnodes.length > 1) { + encoder.writeString(ATTRIB_PIECE2, encodeVarnodePiece(varnodes[1])); + } if (varnodes.length > 2) { encoder.writeString(ATTRIB_PIECE3, encodeVarnodePiece(varnodes[2])); } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/AttributeId.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/AttributeId.java index adf1b7c256..9d414673d8 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/AttributeId.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/AttributeId.java @@ -15,8 +15,6 @@ */ package ghidra.program.model.pcode; -import java.util.HashMap; - /** * An annotation for a data element being transferred to/from a stream * @@ -36,24 +34,24 @@ import java.util.HashMap; */ public record AttributeId(String name, int id) { - private static HashMap lookupAttributeId = new HashMap<>(); +// private static HashMap lookupAttributeId = new HashMap<>(); - public AttributeId { - // add new attribute to lookup map - if (null != lookupAttributeId.put(name, this)) { - throw new RuntimeException("Duplicate AttributeId: " + name); - } - } +// public AttributeId { +// // add new attribute to lookup map +// if (null != lookupAttributeId.put(name, this)) { +// throw new RuntimeException("Duplicate AttributeId: " + name); +// } +// } - /** - * Find the id associated with a specific attribute name - * @param nm the attribute name - * @return the associated id - */ - public static int find(String nm) { - AttributeId res = lookupAttributeId.getOrDefault(nm, ATTRIB_UNKNOWN); - return res.id; - } +// /** +// * Find the id associated with a specific attribute name +// * @param nm the attribute name +// * @return the associated id +// */ +// public static int find(String nm) { +// AttributeId res = lookupAttributeId.getOrDefault(nm, ATTRIB_UNKNOWN); +// return res.id; +// } // Common attributes. Attributes with multiple uses public static final AttributeId ATTRIB_CONTENT = new AttributeId("XMLcontent", 1); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/BlockCondition.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/BlockCondition.java index d036288abc..5590b06034 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/BlockCondition.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/BlockCondition.java @@ -49,7 +49,7 @@ public class BlockCondition extends BlockGraph { } @Override - protected void decodeHeader(Decoder decoder) throws PcodeXMLException { + protected void decodeHeader(Decoder decoder) throws DecoderException { super.decodeHeader(decoder); String opcodename = decoder.readString(AttributeId.ATTRIB_OPCODE); try { diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/BlockCopy.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/BlockCopy.java index da9aa70e38..05cda477ca 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/BlockCopy.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/BlockCopy.java @@ -85,7 +85,7 @@ public class BlockCopy extends PcodeBlock { } @Override - protected void decodeHeader(Decoder decoder) throws PcodeXMLException { + protected void decodeHeader(Decoder decoder) throws DecoderException { super.decodeHeader(decoder); altindex = (int) decoder.readSignedInteger(AttributeId.ATTRIB_ALTINDEX); } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/BlockGoto.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/BlockGoto.java index 400518758f..64ce9450d9 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/BlockGoto.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/BlockGoto.java @@ -63,7 +63,7 @@ public class BlockGoto extends BlockGraph { } @Override - protected void decodeBody(Decoder decoder, BlockMap resolver) throws PcodeXMLException { + protected void decodeBody(Decoder decoder, BlockMap resolver) throws DecoderException { super.decodeBody(decoder, resolver); int el = decoder.openElement(ELEM_TARGET); int target = (int) decoder.readSignedInteger(ATTRIB_INDEX); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/BlockGraph.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/BlockGraph.java index 0d6c90ddb3..b100f8ed16 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/BlockGraph.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/BlockGraph.java @@ -156,7 +156,7 @@ public class BlockGraph extends PcodeBlock { } @Override - protected void decodeBody(Decoder decoder, BlockMap resolver) throws PcodeXMLException { + protected void decodeBody(Decoder decoder, BlockMap resolver) throws DecoderException { BlockMap newresolver = new BlockMap(resolver); super.decodeBody(decoder, newresolver); ArrayList tmplist = new ArrayList<>(); @@ -182,9 +182,9 @@ public class BlockGraph extends PcodeBlock { /** * Decode all blocks and edges in this container from a stream. * @param decoder is the stream decoder - * @throws PcodeXMLException if there are invalid encodings + * @throws DecoderException if there are invalid encodings */ - public void decode(Decoder decoder) throws PcodeXMLException { + public void decode(Decoder decoder) throws DecoderException { BlockMap resolver = new BlockMap(decoder.getAddressFactory()); decode(decoder, resolver); resolver.resolveGotoReferences(); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/BlockIfGoto.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/BlockIfGoto.java index a4902f2bd3..022de9caeb 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/BlockIfGoto.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/BlockIfGoto.java @@ -66,7 +66,7 @@ public class BlockIfGoto extends BlockGraph { } @Override - protected void decodeBody(Decoder decoder, BlockMap resolver) throws PcodeXMLException { + protected void decodeBody(Decoder decoder, BlockMap resolver) throws DecoderException { super.decodeBody(decoder, resolver); int el = decoder.openElement(ELEM_TARGET); int target = (int) decoder.readSignedInteger(ATTRIB_INDEX); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/BlockMultiGoto.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/BlockMultiGoto.java index f6c8f467d0..9b0e2f9420 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/BlockMultiGoto.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/BlockMultiGoto.java @@ -59,7 +59,7 @@ public class BlockMultiGoto extends BlockGraph { } @Override - protected void decodeBody(Decoder decoder, BlockMap resolver) throws PcodeXMLException { + protected void decodeBody(Decoder decoder, BlockMap resolver) throws DecoderException { super.decodeBody(decoder, resolver); for (;;) { int el = decoder.peekElement(); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/ByteIngest.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/ByteIngest.java new file mode 100644 index 0000000000..4570e92d0a --- /dev/null +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/ByteIngest.java @@ -0,0 +1,59 @@ +/* ### + * 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. + */ +package ghidra.program.model.pcode; + +import java.io.IOException; +import java.io.InputStream; + +/** + * An object that can ingest bytes from a stream in preparation for decoding + */ +public interface ByteIngest { + + /** + * Clear any previous cached bytes. + */ + public void clear(); + + /** + * Open the ingester for receiving bytes. This establishes the description of the source of + * the bytes and maximum number of bytes that can be read + * @param max is the maximum number of bytes that can be read + * @param source is the description of the byte source + */ + public void open(int max, String source); + + /** + * Ingest bytes from the stream up to (and including) the first 0 byte. This can be called + * multiple times to read in bytes in different chunks. + * An absolute limit is set on the number of bytes that can be ingested via the + * max parameter to a previous call to open(), otherwise an exception is thrown. + * @param inStream is the input stream to read from + * @throws IOException for errors reading from the stream + */ + public void ingestStream(InputStream inStream) throws IOException; + + /** + * Formal indicator that ingesting of bytes is complete and processing can begin + * @throws IOException for errors processing the underlying stream + */ + public void endIngest() throws IOException; + + /** + * @return true if no bytes have yet been ingested via ingestStream() + */ + public boolean isEmpty(); +} diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/Decoder.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/Decoder.java index d4df02d70a..8fd9922137 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/Decoder.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/Decoder.java @@ -15,8 +15,6 @@ */ package ghidra.program.model.pcode; -import java.io.InputStream; - import ghidra.program.model.address.AddressFactory; import ghidra.program.model.address.AddressSpace; @@ -35,41 +33,27 @@ import ghidra.program.model.address.AddressSpace; * whose data can be extracted using a read*(AttributeId) call that is passed the special ATTRIB_CONTENT id. * This attribute will not be traversed by getNextAttributeId(). */ -public interface Decoder { +public interface Decoder extends ByteIngest { public AddressFactory getAddressFactory(); - /** - * Clear any current decoding state. - * Allows the same decoder to be reused. Object is ready for new call to ingestStream. - */ - public void clear(); - - /** - * Prepare to decode a given stream. - * Called once before any decoding. Currently this is assumed to make an internal copy of - * the stream data, i.e. the input stream is cleared before any decoding takes place. - * @param stream is the given input stream to be decode - * @param source is a label describing the source of the input stream - * @throws PcodeXMLException for errors reading the stream - */ - public void ingestStream(InputStream stream, String source) throws PcodeXMLException; - /** * Peek at the next child element of the current parent, without traversing in (opening) it. * The element id is returned, which can be compared to ElementId labels. * If there are no remaining child elements to traverse, 0 is returned. * @return the element id or 0 + * @throws DecoderException for an unexpected end of stream */ - public int peekElement(); + public int peekElement() throws DecoderException; /** * Open (traverse into) the next child element of the current parent. * The child becomes the current parent. * The list of attributes is initialized for use with getNextAttributeId. * @return the id of the child element or 0 if there are no additional children + * @throws DecoderException for an unexpected end of stream */ - public int openElement(); + public int openElement() throws DecoderException; /** * Open (traverse into) the next child element, which must be of a specific type @@ -77,35 +61,36 @@ public interface Decoder { * getNextAttributeId. The child must match the given element id or an exception is thrown. * @param elemId is the given element id to match * @return the id of the child element - * @throws PcodeXMLException if the expected element is not the next element + * @throws DecoderException if the expected element is not the next element */ - public int openElement(ElementId elemId) throws PcodeXMLException; + public int openElement(ElementId elemId) throws DecoderException; /** * Close the current element * The data for the current element is considered fully processed. If the element has additional * children, an exception is thrown. The stream must indicate the end of the element in some way. * @param id is the id of the element to close (which must be the current element) - * @throws PcodeXMLException if not at end of expected element + * @throws DecoderException if not at end of expected element */ - public void closeElement(int id) throws PcodeXMLException; + public void closeElement(int id) throws DecoderException; /** * Close the current element, skipping any child elements that have not yet been parsed. * This closes the given element, which must be current. If there are child elements that have * not been parsed, this is not considered an error, and they are skipped over in the parse. * @param id is the id of the element to close (which must be the current element) - * @throws PcodeXMLException if the indicated element is not the current element + * @throws DecoderException if the indicated element is not the current element */ - public void closeElementSkipping(int id) throws PcodeXMLException; + public void closeElementSkipping(int id) throws DecoderException; /** * Get the next attribute id for the current element * Attributes are automatically set up for traversal using this method, when the element is * opened. If all attributes have been traversed (or there are no attributes), 0 is returned. * @return the id of the next attribute or 0 + * @throws DecoderException for unexpected end of stream */ - public int getNextAttributeId(); + public int getNextAttributeId() throws DecoderException; /** * Reset attribute traversal for the current element @@ -119,9 +104,9 @@ public interface Decoder { * The last attribute, as returned by getNextAttributeId, is treated as a boolean, and its * value is returned. * @return the boolean value associated with the current attribute. - * @throws PcodeXMLException if the expected value is not present + * @throws DecoderException if the expected value is not present */ - public boolean readBool() throws PcodeXMLException; + public boolean readBool() throws DecoderException; /** * Find and parse a specific attribute in the current element as a boolean value @@ -131,18 +116,18 @@ public interface Decoder { * Parsing via getNextAttributeId is reset. * @param attribId is the specific attribute id to match * @return the boolean value - * @throws PcodeXMLException if the expected value is not present + * @throws DecoderException if the expected value is not present */ - public boolean readBool(AttributeId attribId) throws PcodeXMLException; + public boolean readBool(AttributeId attribId) throws DecoderException; /** * Parse the current attribute as a signed integer value * The last attribute, as returned by getNextAttributeId, is treated as a signed integer, * and its value is returned. * @return the signed integer value associated with the current attribute. - * @throws PcodeXMLException if the expected value is not present + * @throws DecoderException if the expected value is not present */ - public long readSignedInteger() throws PcodeXMLException; + public long readSignedInteger() throws DecoderException; /** * Find and parse a specific attribute in the current element as a signed integer @@ -152,18 +137,18 @@ public interface Decoder { * Parsing via getNextAttributeId is reset. * @param attribId is the specific attribute id to match * @return the signed integer value - * @throws PcodeXMLException if the expected value is not present + * @throws DecoderException if the expected value is not present */ - public long readSignedInteger(AttributeId attribId) throws PcodeXMLException; + public long readSignedInteger(AttributeId attribId) throws DecoderException; /** * Parse the current attribute as an unsigned integer value * The last attribute, as returned by getNextAttributeId, is treated as an unsigned integer, * and its value is returned. * @return the unsigned integer value associated with the current attribute. - * @throws PcodeXMLException if the expected value is not present + * @throws DecoderException if the expected value is not present */ - public long readUnsignedInteger() throws PcodeXMLException; + public long readUnsignedInteger() throws DecoderException; /** * Find and parse a specific attribute in the current element as an unsigned integer @@ -173,17 +158,17 @@ public interface Decoder { * Parsing via getNextAttributeId is reset. * @param attribId is the specific attribute id to match * @return the unsigned integer value - * @throws PcodeXMLException if the expected value is not present + * @throws DecoderException if the expected value is not present */ - public long readUnsignedInteger(AttributeId attribId) throws PcodeXMLException; + public long readUnsignedInteger(AttributeId attribId) throws DecoderException; /** * Parse the current attribute as a string * The last attribute, as returned by getNextAttributeId, is returned as a string. * @return the string associated with the current attribute. - * @throws PcodeXMLException if the expected value is not present + * @throws DecoderException if the expected value is not present */ - public String readString() throws PcodeXMLException; + public String readString() throws DecoderException; /** * Find the specific attribute in the current element and return it as a string @@ -192,17 +177,17 @@ public interface Decoder { * and exception is thrown. Parse via getNextAttributeId is reset. * @param attribId is the specific attribute id to match * @return the string associated with the attribute - * @throws PcodeXMLException if the expected value is not present + * @throws DecoderException if the expected value is not present */ - public String readString(AttributeId attribId) throws PcodeXMLException; + public String readString(AttributeId attribId) throws DecoderException; /** * Parse the current attribute as an address space * The last attribute, as returned by getNextAttributeId, is returned as an address space. * @return the address space associated with the current attribute. - * @throws PcodeXMLException if the expected value is not present + * @throws DecoderException if the expected value is not present */ - public AddressSpace readSpace() throws PcodeXMLException; + public AddressSpace readSpace() throws DecoderException; /** * Find the specific attribute in the current element and return it as an address space @@ -211,16 +196,16 @@ public interface Decoder { * exception is thrown. Parse via getNextAttributeId is reset. * @param attribId is the specific attribute id to match * @return the address space associated with the attribute - * @throws PcodeXMLException if the expected value is not present + * @throws DecoderException if the expected value is not present */ - public AddressSpace readSpace(AttributeId attribId) throws PcodeXMLException; + public AddressSpace readSpace(AttributeId attribId) throws DecoderException; /** * Skip parsing of the next element * The element skipped is the one that would be opened by the next call to openElement. - * @throws PcodeXMLException if there is no new element + * @throws DecoderException if there is no new element */ - public default void skipElement() throws PcodeXMLException { + public default void skipElement() throws DecoderException { int elemId = openElement(); closeElementSkipping(elemId); } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/PcodeXMLException.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/DecoderException.java similarity index 68% rename from Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/PcodeXMLException.java rename to Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/DecoderException.java index 8d3eeeb8fd..c9eb56f9dc 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/PcodeXMLException.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/DecoderException.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,15 +16,14 @@ package ghidra.program.model.pcode; /** - * - * - * Exception thrown when pcode cannot be restored from XML. + * Exception thrown for errors decoding decompiler objects from stream */ -public class PcodeXMLException extends PcodeException { - public PcodeXMLException(String msg) { - super("XML comms: "+msg); +public class DecoderException extends PcodeException { + public DecoderException(String msg) { + super("Decoding error: " + msg); } - public PcodeXMLException(String msg, Throwable cause) { - super("XML comms: "+msg, cause); + + public DecoderException(String msg, Throwable cause) { + super("Decoding error: " + msg, cause); } } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/DynamicEntry.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/DynamicEntry.java index 69b011cb10..d8dbad18fc 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/DynamicEntry.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/DynamicEntry.java @@ -65,7 +65,7 @@ public class DynamicEntry extends SymbolEntry { } @Override - public void decode(Decoder decoder) throws PcodeXMLException { + public void decode(Decoder decoder) throws DecoderException { int addrel = decoder.openElement(ELEM_HASH); hash = decoder.readUnsignedInteger(ATTRIB_VAL); decoder.closeElement(addrel); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/ElementId.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/ElementId.java index ca2342818b..82a38f6b26 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/ElementId.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/ElementId.java @@ -15,8 +15,6 @@ */ package ghidra.program.model.pcode; -import java.util.HashMap; - /** * An annotation for a specific collection of hierarchical data * @@ -28,28 +26,28 @@ import java.util.HashMap; * as an attribute. * * @param name unique element name - * @param id unqiue element ID + * @param id unique element ID */ public record ElementId(String name, int id) { - private static HashMap lookupElementId = new HashMap<>(); +// private static HashMap lookupElementId = new HashMap<>(); - public ElementId { - // add new element to lookup map - if (null != lookupElementId.put(name, this)) { - throw new RuntimeException("Duplicate ElementId instance: " + name); - } - } +// public ElementId { +// // add new element to lookup map +// if (null != lookupElementId.put(name, this)) { +// throw new RuntimeException("Duplicate ElementId instance: " + name); +// } +// } - /** - * Find the id associated with a specific element name - * @param nm the element name - * @return the associated id - */ - public static int find(String nm) { - ElementId res = lookupElementId.getOrDefault(nm, ELEM_UNKNOWN); - return res.id; - } +// /** +// * Find the id associated with a specific element name +// * @param nm the element name +// * @return the associated id +// */ +// public static int find(String nm) { +// ElementId res = lookupElementId.getOrDefault(nm, ELEM_UNKNOWN); +// return res.id; +// } public static final ElementId ELEM_DATA = new ElementId("data", 1); public static final ElementId ELEM_INPUT = new ElementId("input", 2); @@ -364,5 +362,64 @@ public record ElementId(String name, int id) { // raw_arch // public static final ElementId ELEM_RAW_SAVEFILE = new ElementId("raw_savefile", 237); - public static final ElementId ELEM_UNKNOWN = new ElementId("XMLunknown", 251); + // ghidra_arch + public static final int COMMAND_ISNAMEUSED = 239; + public static final ElementId ELEM_COMMAND_ISNAMEUSED = + new ElementId("command_isnameused", COMMAND_ISNAMEUSED); + public static final int COMMAND_GETBYTES = 240; + public static final ElementId ELEM_COMMAND_GETBYTES = + new ElementId("command_getbytes", COMMAND_GETBYTES); + public static final int COMMAND_GETCALLFIXUP = 241; + public static final ElementId ELEM_COMMAND_GETCALLFIXUP = + new ElementId("command_getcallfixup", COMMAND_GETCALLFIXUP); + public static final int COMMAND_GETCALLMECH = 242; + public static final ElementId ELEM_COMMAND_GETCALLMECH = + new ElementId("command_getcallmech", COMMAND_GETCALLMECH); + public static final int COMMAND_GETCALLOTHERFIXUP = 243; + public static final ElementId ELEM_COMMAND_GETCALLOTHERFIXUP = + new ElementId("command_getcallotherfixup", COMMAND_GETCALLOTHERFIXUP); + public static final int COMMAND_GETCODELABEL = 244; + public static final ElementId ELEM_COMMAND_GETCODELABEL = + new ElementId("command_getcodelabel", COMMAND_GETCODELABEL); + public static final int COMMAND_GETCOMMENTS = 245; + public static final ElementId ELEM_COMMAND_GETCOMMENTS = + new ElementId("command_getcomments", COMMAND_GETCOMMENTS); + public static final int COMMAND_GETCPOOLREF = 246; + public static final ElementId ELEM_COMMAND_GETCPOOLREF = + new ElementId("command_getcpoolref", COMMAND_GETCPOOLREF); + public static final int COMMAND_GETDATATYPE = 247; + public static final ElementId ELEM_COMMAND_GETDATATYPE = + new ElementId("command_getdatatype", COMMAND_GETDATATYPE); + public static final int COMMAND_GETEXTERNALREF = 248; + public static final ElementId ELEM_COMMAND_GETEXTERNALREF = + new ElementId("command_getexternalref", COMMAND_GETEXTERNALREF); + public static final int COMMAND_GETMAPPEDSYMBOLS = 249; + public static final ElementId ELEM_COMMAND_GETMAPPEDSYMBOLS = + new ElementId("command_getmappedsymbols", COMMAND_GETMAPPEDSYMBOLS); + public static final int COMMAND_GETNAMESPACEPATH = 250; + public static final ElementId ELEM_COMMAND_GETNAMESPACEPATH = + new ElementId("command_getnamespacepath", COMMAND_GETNAMESPACEPATH); + public static final int COMMAND_GETPCODE = 251; + public static final ElementId ELEM_COMMAND_GETPCODE = + new ElementId("command_getpcode", COMMAND_GETPCODE); + public static final int COMMAND_GETPCODEEXECUTABLE = 252; + public static final ElementId ELEM_COMMAND_GETPCODEEXECUTABLE = + new ElementId("command_getpcodeexecutable", COMMAND_GETPCODEEXECUTABLE); + public static final int COMMAND_GETREGISTER = 253; + public static final ElementId ELEM_COMMAND_GETREGISTER = + new ElementId("command_getregister", COMMAND_GETREGISTER); + public static final int COMMAND_GETREGISTERNAME = 254; + public static final ElementId ELEM_COMMAND_GETREGISTERNAME = + new ElementId("command_getregistername", COMMAND_GETREGISTERNAME); + public static final int COMMAND_GETSTRINGDATA = 255; + public static final ElementId ELEM_COMMAND_GETSTRINGDATA = + new ElementId("command_getstring", COMMAND_GETSTRINGDATA); + public static final int COMMAND_GETTRACKEDREGISTERS = 256; + public static final ElementId ELEM_COMMAND_GETTRACKEDREGISTERS = + new ElementId("command_gettrackedregisters", COMMAND_GETTRACKEDREGISTERS); + public static final int COMMAND_GETUSEROPNAME = 257; + public static final ElementId ELEM_COMMAND_GETUSEROPNAME = + new ElementId("command_getuseropname", COMMAND_GETUSEROPNAME); + + public static final ElementId ELEM_UNKNOWN = new ElementId("XMLunknown", 270); } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/Encoder.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/Encoder.java index 9322043f77..10efd3a0fd 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/Encoder.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/Encoder.java @@ -16,6 +16,7 @@ package ghidra.program.model.pcode; import java.io.IOException; +import java.io.OutputStream; import ghidra.program.model.address.AddressSpace; @@ -102,8 +103,15 @@ public interface Encoder { void writeSpace(AttributeId attribId, AddressSpace spc) throws IOException; /** - * Return anything written to the encoder (since the last clear) as a byte array. - * @return the array of bytes + * Dump all the accumulated bytes in this encoder to the stream. + * @param stream is the output stream + * @throws IOException for errors during the write operation */ - byte[] getBytes(); + public void writeTo(OutputStream stream) throws IOException; + + /** + * The encoder is considered empty if the writeTo() method would output zero bytes + * @return true if there are no bytes in the encoder + */ + public boolean isEmpty(); } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/EquateSymbol.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/EquateSymbol.java index 941b2fe883..99a769aeee 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/EquateSymbol.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/EquateSymbol.java @@ -68,7 +68,7 @@ public class EquateSymbol extends HighSymbol { } @Override - public void decode(Decoder decoder) throws PcodeXMLException { + public void decode(Decoder decoder) throws DecoderException { int symel = decoder.openElement(ELEM_EQUATESYMBOL); decodeHeader(decoder); type = DataType.DEFAULT; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/FunctionPrototype.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/FunctionPrototype.java index 62dc9effc0..81f99cd413 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/FunctionPrototype.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/FunctionPrototype.java @@ -25,7 +25,6 @@ import ghidra.program.model.lang.*; import ghidra.program.model.listing.*; import ghidra.program.model.symbol.SourceType; import ghidra.util.Msg; -import ghidra.util.xml.SpecXmlUtils; /** * @@ -418,24 +417,23 @@ public class FunctionPrototype { * Decode the function prototype from a {@code } element in the stream. * @param decoder is the stream decoder * @param dtmanage is the DataTypeManager used to parse data-type tags - * @throws PcodeXMLException for invalid encodings + * @throws DecoderException for invalid encodings */ public void decodePrototype(Decoder decoder, PcodeDataTypeManager dtmanage) - throws PcodeXMLException { + throws DecoderException { int node = decoder.openElement(ELEM_PROTOTYPE); modelname = decoder.readString(ATTRIB_MODEL); PrototypeModel protoModel = dtmanage.getProgram().getCompilerSpec().getCallingConvention(modelname); if (protoModel == null) { - throw new PcodeXMLException("Bad prototype model name: " + modelname); + throw new DecoderException("Bad prototype model name: " + modelname); } hasThis = protoModel.hasThisPointer(); - String val = decoder.readString(ATTRIB_EXTRAPOP); - if (val.equals("unknown")) { - extrapop = PrototypeModel.UNKNOWN_EXTRAPOP; + try { + extrapop = (int) decoder.readSignedInteger(ATTRIB_EXTRAPOP); } - else { - extrapop = SpecXmlUtils.decodeInt(val); + catch (DecoderException e) { + extrapop = PrototypeModel.UNKNOWN_EXTRAPOP; } modellock = false; dotdotdot = false; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighCodeSymbol.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighCodeSymbol.java index db478e8da3..9b90e6a788 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighCodeSymbol.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighCodeSymbol.java @@ -148,7 +148,7 @@ public class HighCodeSymbol extends HighSymbol { } @Override - public void decode(Decoder decoder) throws PcodeXMLException { + public void decode(Decoder decoder) throws DecoderException { super.decode(decoder); symbol = null; } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighConstant.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighConstant.java index 38729851d3..94fa6c8090 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighConstant.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighConstant.java @@ -86,7 +86,7 @@ public class HighConstant extends HighVariable { } @Override - public void decode(Decoder decoder) throws PcodeXMLException { + public void decode(Decoder decoder) throws DecoderException { //int el = decoder.openElement(ElementId.ELEM_HIGH); long symref = 0; for (;;) { diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighFunction.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighFunction.java index deb5a53f65..9b907f45d9 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighFunction.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighFunction.java @@ -218,7 +218,7 @@ public class HighFunction extends PcodeSyntaxTree { return super.newVarnode(sz, addr, id); } - private void decodeHigh(Decoder decoder) throws PcodeXMLException { + private void decodeHigh(Decoder decoder) throws DecoderException { int el = decoder.openElement(ELEM_HIGH); String classstring = decoder.readString(ATTRIB_CLASS); HighVariable var; @@ -239,13 +239,13 @@ public class HighFunction extends PcodeSyntaxTree { var = new HighConstant(this); break; default: - throw new PcodeXMLException("Unknown HighVariable class string: " + classstring); + throw new DecoderException("Unknown HighVariable class string: " + classstring); } var.decode(decoder); decoder.closeElement(el); } - private void decodeHighlist(Decoder decoder) throws PcodeXMLException { + private void decodeHighlist(Decoder decoder) throws DecoderException { int el = decoder.openElement(ELEM_HIGHLIST); while (decoder.peekElement() != 0) { decodeHigh(decoder); @@ -254,11 +254,11 @@ public class HighFunction extends PcodeSyntaxTree { } @Override - public void decode(Decoder decoder) throws PcodeXMLException { + public void decode(Decoder decoder) throws DecoderException { int start = decoder.openElement(ELEM_FUNCTION); String name = decoder.readString(ATTRIB_NAME); if (!func.getName().equals(name)) { - throw new PcodeXMLException("Function name mismatch: " + func.getName() + " + " + name); + throw new DecoderException("Function name mismatch: " + func.getName() + " + " + name); } for (;;) { int subel = decoder.peekElement(); @@ -269,7 +269,7 @@ public class HighFunction extends PcodeSyntaxTree { Address addr = AddressXML.decode(decoder); addr = func.getEntryPoint().getAddressSpace().getOverlayAddress(addr); if (!func.getEntryPoint().equals(addr)) { - throw new PcodeXMLException("Mismatched address in function tag"); + throw new DecoderException("Mismatched address in function tag"); } } else if (subel == ELEM_PROTOTYPE.id()) { @@ -298,7 +298,7 @@ public class HighFunction extends PcodeSyntaxTree { decoder.skipElement(); } else { - throw new PcodeXMLException("Unknown element in function"); + throw new DecoderException("Unknown element in function"); } } decoder.closeElement(start); @@ -308,9 +308,9 @@ public class HighFunction extends PcodeSyntaxTree { * Decode the Jump Table list for this function from the stream * * @param decoder is the stream decoder - * @throws PcodeXMLException for invalid encodings + * @throws DecoderException for invalid encodings */ - private void decodeJumpTableList(Decoder decoder) throws PcodeXMLException { + private void decodeJumpTableList(Decoder decoder) throws DecoderException { int el = decoder.openElement(ELEM_JUMPTABLELIST); while (decoder.peekElement() != 0) { JumpTable table = new JumpTable(func.getEntryPoint().getAddressSpace()); @@ -422,7 +422,7 @@ public class HighFunction extends PcodeSyntaxTree { return reslocal; } catch (InvalidInputException e) { - throw new PcodeXMLException("Bad storage node", e); + throw new DecoderException("Bad storage node", e); } } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighGlobal.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighGlobal.java index 8fc4890ed8..208eb68858 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighGlobal.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighGlobal.java @@ -47,7 +47,7 @@ public class HighGlobal extends HighVariable { } @Override - public void decode(Decoder decoder) throws PcodeXMLException { + public void decode(Decoder decoder) throws DecoderException { // int el = decoder.openElement(ElementId.ELEM_HIGH); long symref = 0; offset = -1; @@ -90,7 +90,7 @@ public class HighGlobal extends HighVariable { } symbol = globalMap.newSymbol(symref, addr, symbolType, symbolSize); if (symbol == null) { - throw new PcodeXMLException("Bad global storage: " + addr.toString()); + throw new DecoderException("Bad global storage: " + addr.toString()); } } } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighLocal.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighLocal.java index 4e8455c6b2..83cdc25c34 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighLocal.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighLocal.java @@ -50,7 +50,7 @@ public class HighLocal extends HighVariable { } @Override - public void decode(Decoder decoder) throws PcodeXMLException { + public void decode(Decoder decoder) throws DecoderException { //int el = decoder.openElement(ElementId.ELEM_HIGH); long symref = decoder.readUnsignedInteger(AttributeId.ATTRIB_SYMREF); offset = -1; @@ -60,14 +60,14 @@ public class HighLocal extends HighVariable { break; } if (attribId == AttributeId.ATTRIB_OFFSET.id()) { - offset = (int) decoder.readUnsignedInteger(); + offset = (int) decoder.readSignedInteger(); break; } } decodeInstances(decoder); symbol = function.getLocalSymbolMap().getSymbol(symref); if (symbol == null) { - throw new PcodeXMLException("HighLocal is missing symbol"); + throw new DecoderException("HighLocal is missing symbol"); } if (offset < 0) { name = symbol.getName(); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighOther.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighOther.java index 3a75015e32..4830a28911 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighOther.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighOther.java @@ -63,7 +63,7 @@ public class HighOther extends HighVariable { } @Override - public void decode(Decoder decoder) throws PcodeXMLException { + public void decode(Decoder decoder) throws DecoderException { // int el = decoder.openElement(ElementId.ELEM_HIGH); long symref = 0; offset = -1; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighParam.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighParam.java index ff6d432a40..9ab6497963 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighParam.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighParam.java @@ -54,7 +54,7 @@ public class HighParam extends HighLocal { } @Override - public void decode(Decoder decoder) throws PcodeXMLException { + public void decode(Decoder decoder) throws DecoderException { super.decode(decoder); HighSymbol sym = getSymbol(); slot = sym.getCategoryIndex(); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighParamID.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighParamID.java index f5dec7e721..dd36547f98 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighParamID.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighParamID.java @@ -134,11 +134,11 @@ public class HighParamID extends PcodeSyntaxTree { * @see ghidra.program.model.pcode.PcodeSyntaxTree#readXML(org.jdom.Element) */ @Override - public void decode(Decoder decoder) throws PcodeXMLException { + public void decode(Decoder decoder) throws DecoderException { int start = decoder.openElement(ELEM_PARAMMEASURES); functionname = decoder.readString(ATTRIB_NAME); if (!func.getName().equals(functionname)) { - throw new PcodeXMLException( + throw new DecoderException( "Function name mismatch: " + func.getName() + " + " + functionname); } for (;;) { @@ -151,7 +151,7 @@ public class HighParamID extends PcodeSyntaxTree { functionaddress = func.getEntryPoint().getAddressSpace().getOverlayAddress(functionaddress); if (!func.getEntryPoint().equals(functionaddress)) { - throw new PcodeXMLException("Mismatched address in function tag"); + throw new DecoderException("Mismatched address in function tag"); } } else if (subel == ELEM_PROTO.id()) { @@ -173,7 +173,7 @@ public class HighParamID extends PcodeSyntaxTree { decodeParamMeasure(decoder, outputlist); } else { - throw new PcodeXMLException("Unknown tag in parammeasures"); + throw new DecoderException("Unknown tag in parammeasures"); } } decoder.closeElement(start); @@ -183,10 +183,10 @@ public class HighParamID extends PcodeSyntaxTree { * Decode the inputs or outputs list for this function from a stream. * @param decoder is the stream decoder * @param pmlist is populated with the resulting list - * @throws PcodeXMLException for invalid encodings + * @throws DecoderException for invalid encodings */ private void decodeParamMeasure(Decoder decoder, List pmlist) - throws PcodeXMLException { + throws DecoderException { int el = decoder.openElement(); ParamMeasure pm = new ParamMeasure(); pm.decode(decoder, this); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighSymbol.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighSymbol.java index 3a33d47959..e59d762362 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighSymbol.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighSymbol.java @@ -392,7 +392,7 @@ public class HighSymbol { } encoder.writeSignedInteger(ATTRIB_CAT, category); if (categoryIndex >= 0) { - encoder.writeSignedInteger(ATTRIB_INDEX, categoryIndex); + encoder.writeUnsignedInteger(ATTRIB_INDEX, categoryIndex); } } @@ -408,7 +408,7 @@ public class HighSymbol { encoder.closeElement(ELEM_SYMBOL); } - protected void decodeHeader(Decoder decoder) throws PcodeXMLException { + protected void decodeHeader(Decoder decoder) throws DecoderException { name = null; id = 0; typelock = false; @@ -449,16 +449,16 @@ public class HighSymbol { } } if (id == 0) { - throw new PcodeXMLException("missing unique symbol id"); + throw new DecoderException("missing unique symbol id"); } } /** * Decode this symbol object and its associated mappings from the stream. * @param decoder is the stream decoder - * @throws PcodeXMLException for invalid encodings + * @throws DecoderException for invalid encodings */ - public void decode(Decoder decoder) throws PcodeXMLException { + public void decode(Decoder decoder) throws DecoderException { int symel = decoder.openElement(ELEM_SYMBOL); decodeHeader(decoder); type = dtmanage.decodeDataType(decoder); @@ -498,7 +498,7 @@ public class HighSymbol { entryList[0] = new MappedEntry(this, newStorage, entry.getPCAdress()); } catch (InvalidInputException e) { - throw new PcodeXMLException("Unable to parse auto-parameter"); + throw new DecoderException("Unable to parse auto-parameter"); } } } @@ -511,10 +511,10 @@ public class HighSymbol { * @param isGlobal is true if this symbol is being read into a global scope * @param high is the function model that will own the new symbol * @return the new symbol - * @throws PcodeXMLException for invalid encodings + * @throws DecoderException for invalid encodings */ public static HighSymbol decodeMapSym(Decoder decoder, boolean isGlobal, HighFunction high) - throws PcodeXMLException { + throws DecoderException { HighSymbol res = null; int mapel = decoder.openElement(ELEM_MAPSYM); int symel = decoder.peekElement(); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighVariable.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighVariable.java index c0c66c66f0..901c1321d7 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighVariable.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighVariable.java @@ -141,13 +141,13 @@ public abstract class HighVariable { * Decode the data-type and the Varnode instances of this HighVariable. * The "representative" Varnode is also populated. * @param decoder is the stream decoder - * @throws PcodeXMLException for invalid encodings + * @throws DecoderException for invalid encodings */ - protected void decodeInstances(Decoder decoder) throws PcodeXMLException { + protected void decodeInstances(Decoder decoder) throws DecoderException { int repref = (int) decoder.readUnsignedInteger(AttributeId.ATTRIB_REPREF); Varnode rep = function.getRef(repref); if (rep == null) { - throw new PcodeXMLException("Undefined varnode reference"); + throw new DecoderException("Undefined varnode reference"); } type = null; @@ -188,7 +188,7 @@ public abstract class HighVariable { /** * Decode this HighVariable from a {@code } element in the stream * @param decoder is the stream decoder - * @throws PcodeXMLException for invalid encodings + * @throws DecoderException for invalid encodings */ - public abstract void decode(Decoder decoder) throws PcodeXMLException; + public abstract void decode(Decoder decoder) throws DecoderException; } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/JumpTable.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/JumpTable.java index ab7376ce86..61da926947 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/JumpTable.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/JumpTable.java @@ -79,7 +79,7 @@ public class JumpTable { return num; } - public void decode(Decoder decoder) throws PcodeXMLException { + public void decode(Decoder decoder) throws DecoderException { int el = decoder.openElement(ELEM_LOADTABLE); size = (int) decoder.readSignedInteger(ATTRIB_SIZE); num = (int) decoder.readSignedInteger(ATTRIB_NUM); @@ -160,9 +160,9 @@ public class JumpTable { /** * Decode a JumpTable object from the stream. * @param decoder is the stream decoder - * @throws PcodeXMLException for invalid encodings + * @throws DecoderException for invalid encodings */ - public void decode(Decoder decoder) throws PcodeXMLException { + public void decode(Decoder decoder) throws DecoderException { int el = decoder.openElement(ELEM_JUMPTABLE); if (decoder.peekElement() == 0) { // Empty jumptable decoder.closeElement(el); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/LinkedByteBuffer.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/LinkedByteBuffer.java new file mode 100644 index 0000000000..55c826a9c7 --- /dev/null +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/LinkedByteBuffer.java @@ -0,0 +1,154 @@ +/* ### + * 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. + */ +package ghidra.program.model.pcode; + +import java.io.IOException; +import java.io.InputStream; + +public class LinkedByteBuffer { + + public static class Position { + public ArrayIter seqIter; + public byte[] array; + public int current; + + public void copy(Position pos) { + seqIter = pos.seqIter; + array = pos.array; + current = pos.current; + } + + public final byte getByte() { + return array[current]; + } + + public final byte getBytePlus1() throws DecoderException { + int plus1 = current + 1; + if (plus1 == array.length) { + ArrayIter iter = seqIter.next; + if (iter == null) { + throw new DecoderException("Unexpected end of stream"); + } + return iter.array[0]; + } + return array[plus1]; + } + + public final byte getNextByte() throws DecoderException { + byte res = array[current]; + current += 1; + if (current != array.length) { + return res; + } + seqIter = seqIter.next; + if (seqIter == null) { + throw new DecoderException("Unexpected end of stream"); + } + array = seqIter.array; + current = 0; + return res; + } + + public final void advancePosition(int skip) throws DecoderException { + while (array.length - current <= skip) { + skip -= (array.length - current); + seqIter = seqIter.next; + if (seqIter == null) { + throw new DecoderException("Unexpected end of stream"); + } + array = seqIter.array; + current = 0; + } + current += skip; + } + } + + public static class ArrayIter { + public ArrayIter next; + public byte[] array; + } + + public final static int BUFFER_SIZE = 1024; + + private ArrayIter initialBuffer; + private int byteCount; + private int maxCount; + private ArrayIter currentBuffer; + private int currentPos; + private String source; + + public LinkedByteBuffer(int max, String src) { + initialBuffer = new ArrayIter(); + currentBuffer = initialBuffer; + byteCount = 0; + currentPos = 0; + maxCount = max; + initialBuffer.array = new byte[BUFFER_SIZE]; + initialBuffer.next = null; + source = src; + } + + public void ingestStream(InputStream stream) throws IOException { + int tok = stream.read(); + if (tok <= 0) { + return; + } + for (;;) { + if (byteCount > maxCount) { + throw new IOException("Response buffer size exceded for: " + source); + } + do { + if (currentPos == BUFFER_SIZE) { + break; + } + currentBuffer.array[currentPos++] = (byte) tok; + tok = stream.read(); + } + while (tok > 0); + byteCount += currentPos; + if (tok <= 0) { + return; // Reached terminator or end of stream, ingest is finished + } + // Set up next buffer + currentBuffer.next = new ArrayIter(); + currentBuffer = currentBuffer.next; + currentBuffer.array = new byte[BUFFER_SIZE]; + currentPos = 0; + } + } + + /** + * Add a particular byte to the buffer + * @param val is the byte value to add + */ + public void pad(int val) { + if (currentPos == BUFFER_SIZE) { + byteCount += currentPos; + currentBuffer.next = new ArrayIter(); + currentBuffer = currentBuffer.next; + currentBuffer.array = new byte[1]; + currentPos = 0; + } + + currentBuffer.array[currentPos++] = (byte) val; + } + + public void getStartPosition(Position position) { + position.array = initialBuffer.array; + position.current = 0; + position.seqIter = initialBuffer; + } +} diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/LocalSymbolMap.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/LocalSymbolMap.java index 00c1fe765f..35aed16f13 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/LocalSymbolMap.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/LocalSymbolMap.java @@ -265,9 +265,9 @@ public class LocalSymbolMap { * Decode a <mapsym> element from the stream. * @param decoder is the stream decoder * @return the reconstructed HighSymbol - * @throws PcodeXMLException for problems sub tags + * @throws DecoderException for problems sub tags */ - private HighSymbol decodeSymbol(Decoder decoder) throws PcodeXMLException { + private HighSymbol decodeSymbol(Decoder decoder) throws DecoderException { HighSymbol res = HighSymbol.decodeMapSym(decoder, false, func); insertSymbol(res); return res; @@ -277,9 +277,9 @@ public class LocalSymbolMap { * Decode a local symbol scope from the stream * * @param decoder is the stream decoder - * @throws PcodeXMLException for invalid encodings + * @throws DecoderException for invalid encodings */ - public void decodeScope(Decoder decoder) throws PcodeXMLException { + public void decodeScope(Decoder decoder) throws DecoderException { int el = decoder.openElement(ELEM_LOCALDB); localSpace = decoder.readSpace(ATTRIB_MAIN); int scopeel = decoder.openElement(ELEM_SCOPE); @@ -308,9 +308,9 @@ public class LocalSymbolMap { /** * Add mapped symbols to this LocalVariableMap, by decoding the <symbollist> and <mapsym> elements * @param decoder is the stream decoder - * @throws PcodeXMLException for invalid encodings + * @throws DecoderException for invalid encodings */ - public void decodeSymbolList(Decoder decoder) throws PcodeXMLException { + public void decodeSymbolList(Decoder decoder) throws DecoderException { int el = decoder.openElement(ELEM_SYMBOLLIST); ArrayList parms = new ArrayList<>(); while (decoder.peekElement() != 0) { diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/MappedDataEntry.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/MappedDataEntry.java index 1e887bb63a..0088576b37 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/MappedDataEntry.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/MappedDataEntry.java @@ -51,7 +51,7 @@ public class MappedDataEntry extends MappedEntry { } @Override - public void decode(Decoder decoder) throws PcodeXMLException { + public void decode(Decoder decoder) throws DecoderException { super.decode(decoder); data = symbol.getProgram().getListing().getDataAt(storage.getMinAddress()); } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/MappedEntry.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/MappedEntry.java index cfa9ead96b..c13bff6c45 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/MappedEntry.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/MappedEntry.java @@ -54,14 +54,14 @@ public class MappedEntry extends SymbolEntry { } @Override - public void decode(Decoder decoder) throws PcodeXMLException { + public void decode(Decoder decoder) throws DecoderException { HighFunction function = symbol.function; Program program = function.getFunction().getProgram(); int addrel = decoder.openElement(ElementId.ELEM_ADDR); int sz = symbol.type.getLength(); if (sz == 0) { - throw new PcodeXMLException( + throw new DecoderException( "Invalid symbol 0-sized data-type: " + symbol.type.getName()); } try { @@ -76,7 +76,7 @@ public class MappedEntry extends SymbolEntry { } } catch (InvalidInputException e) { - throw new PcodeXMLException("Invalid storage: " + e.getMessage()); + throw new DecoderException("Invalid storage: " + e.getMessage()); } decoder.closeElement(addrel); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/PackedBytes.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/PackedBytes.java similarity index 80% rename from Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/PackedBytes.java rename to Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/PackedBytes.java index 1eda564154..6dbde2b3c0 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/PackedBytes.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/PackedBytes.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.program.model.lang; +package ghidra.program.model.pcode; import java.io.IOException; import java.io.OutputStream; @@ -25,6 +24,9 @@ import java.util.Arrays; * It allows the bytes to be edited in the middle of collection * */ +/** + * + */ public class PackedBytes { private byte[] out; private int bytecnt; @@ -62,16 +64,31 @@ public class PackedBytes { */ public void write(int val) { int newcount = bytecnt + 1; - if (newcount > out.length) + if (newcount > out.length) { out = Arrays.copyOf(out, Math.max(out.length << 1, newcount)); + } out[bytecnt] = (byte) val; bytecnt = newcount; } + /** + * Dump an array of bytes to the packed byte stream + * @param byteArray is the byte array + */ + public void write(byte[] byteArray) { + int newcount = bytecnt + byteArray.length; + if (newcount > out.length) { + out = Arrays.copyOf(out, Math.max(out.length << 1, newcount)); + } + System.arraycopy(byteArray, 0, out, bytecnt, byteArray.length); + bytecnt = newcount; + } + public int find(int start, int val) { while (start < bytecnt) { - if (out[start] == val) + if (out[start] == val) { return start; + } start += 1; } return -1; @@ -80,7 +97,7 @@ public class PackedBytes { /** * Write the accumulated packed byte stream onto the output stream * @param s is the output stream receiving the bytes - * @throws IOException + * @throws IOException for stream errors */ public void writeTo(OutputStream s) throws IOException { s.write(out, 0, bytecnt); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/PackedDecode.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/PackedDecode.java new file mode 100644 index 0000000000..4041a458be --- /dev/null +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/PackedDecode.java @@ -0,0 +1,502 @@ +/* ### + * 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. + */ +package ghidra.program.model.pcode; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; + +import ghidra.program.model.address.AddressFactory; +import ghidra.program.model.address.AddressSpace; + +/* + * A byte-based decoder designed to marshal info to the decompiler efficiently + * All bytes in the encoding are expected to be non-zero. Element encoding looks like + * - 01xiiiii is an element start + * - 10xiiiii is an element end + * - 11xiiiii is an attribute start + * + * Where iiiii is the (first) 5 bits of the element/attribute id. + * If x=0, the id is complete. If x=1, the next byte contains 7 more bits of the id: 1iiiiiii + * + * After an attribute start, there follows a type byte: ttttllll, where the first 4 bits indicate + * the type of attribute and final 4 bits are a "length code". The types are: + * - 1 = boolean (lengthcode=0 for false, lengthcode=1 for true) + * - 2 = positive signed integer + * - 3 = negative signed integer (stored in negated form) + * - 4 = unsigned integer + * - 5 = basic address space (encoded as the integer index of the space) + * - 6 = special address space (lengthcode 0=>stack 1=>join 2=>fspec 3=>iop) + * - 7 = string + * + * All attribute types except "boolean" and "special", have an encoded integer after the \e type byte. + * The "length code", indicates the number bytes used to encode the integer, + * 7-bits of info per byte, 1iiiiiii. A "length code" of 0 is used to encode and integer value + * of 0, with no following bytes. + * + * For strings, the integer encoded after the \e type byte, is the actual length of the string. The + * string data itself is stored immediately after the length integer using UTF8 format. + * */ +public class PackedDecode implements Decoder { + + public static final int HEADER_MASK = 0xc0; + public static final int ELEMENT_START = 0x40; + public static final int ELEMENT_END = 0x80; + public static final int ATTRIBUTE = 0xc0; + public static final int HEADEREXTEND_MASK = 0x20; + public static final int ELEMENTID_MASK = 0x1f; + public static final int RAWDATA_MASK = 0x7f; + public static final int RAWDATA_BITSPERBYTE = 7; + public static final int RAWDATA_MARKER = 0x80; + public static final int TYPECODE_SHIFT = 4; + public static final int LENGTHCODE_MASK = 0xf; + public static final int TYPECODE_BOOLEAN = 1; + public static final int TYPECODE_SIGNEDINT_POSITIVE = 2; + public static final int TYPECODE_SIGNEDINT_NEGATIVE = 3; + public static final int TYPECODE_UNSIGNEDINT = 4; + public static final int TYPECODE_ADDRESSSPACE = 5; + public static final int TYPECODE_SPECIALSPACE = 6; + public static final int TYPECODE_STRING = 7; + public static final int SPECIALSPACE_STACK = 0; + public static final int SPECIALSPACE_JOIN = 1; + public static final int SPECIALSPACE_FSPEC = 2; + public static final int SPECIALSPACE_IOP = 3; + public static final int SPECIALSPACE_SPACEBASE = 4; + + private AddressFactory addrFactory; + private AddressSpace[] spaces; + private LinkedByteBuffer inStream; + private LinkedByteBuffer.Position startPos; + private LinkedByteBuffer.Position curPos; + private LinkedByteBuffer.Position endPos; + private boolean attributeRead; + + public PackedDecode(AddressFactory addrFactory) { + this.addrFactory = addrFactory; + inStream = null; + startPos = new LinkedByteBuffer.Position(); + curPos = new LinkedByteBuffer.Position(); + endPos = new LinkedByteBuffer.Position(); + buildAddrSpaceArray(); + } + + private void buildAddrSpaceArray() { + ArrayList spaceList = new ArrayList<>(); + AddressSpace[] allSpaces = addrFactory.getAllAddressSpaces(); + for (AddressSpace spc : allSpaces) { + int type = spc.getType(); + if (type != AddressSpace.TYPE_CONSTANT && type != AddressSpace.TYPE_RAM && + type != AddressSpace.TYPE_REGISTER && type != AddressSpace.TYPE_UNIQUE && + type != AddressSpace.TYPE_OTHER) { + continue; + } + int ind = spc.getUnique(); + while (spaceList.size() <= ind) { + spaceList.add(null); + } + spaceList.set(ind, spc); + } + spaces = new AddressSpace[spaceList.size()]; + spaceList.toArray(spaces); + } + + private long readInteger(int len) throws DecoderException { + long res = 0; + while (len > 0) { + res <<= RAWDATA_BITSPERBYTE; + res |= (curPos.getNextByte() & RAWDATA_MASK); + len -= 1; + } + return res; + } + + private void findMatchingAttribute(AttributeId attribId) throws DecoderException { + curPos.copy(startPos); + for (;;) { + byte header1 = curPos.getByte(); + if ((header1 & HEADER_MASK) != ATTRIBUTE) { + break; + } + int id = header1 & ELEMENTID_MASK; + if ((header1 & HEADEREXTEND_MASK) != 0) { + id <<= RAWDATA_BITSPERBYTE; + id |= (curPos.getBytePlus1() & RAWDATA_MASK); + } + if (attribId.id() == id) { + return; // Found it + } + skipAttribute(); + } + throw new DecoderException("Attribute " + attribId.name() + " is not present"); + } + + private void skipAttribute() throws DecoderException { + byte header1 = curPos.getNextByte(); // Attribute header + if ((header1 & HEADEREXTEND_MASK) != 0) { + curPos.getNextByte(); // Extra byte for extended id + } + byte typeByte = curPos.getNextByte(); // Type (and length) byte + int attribType = typeByte >> TYPECODE_SHIFT; + if (attribType == TYPECODE_BOOLEAN || attribType == TYPECODE_SPECIALSPACE) { + return; // has no additional data + } + int length = typeByte & LENGTHCODE_MASK; // Length of data in bytes + if (attribType == TYPECODE_STRING) { // For a string + length = (int) readInteger(length); // Read length field to get final length of string + } + curPos.advancePosition(length); // Skip -length- data + } + + private void skipAttributeRemaining(byte typeByte) throws DecoderException { + int attribType = typeByte >> TYPECODE_SHIFT; + if (attribType == TYPECODE_BOOLEAN || attribType == TYPECODE_SPECIALSPACE) { + return; // has no additional data + } + int length = typeByte & LENGTHCODE_MASK; // Length of data in bytes + if (attribType == TYPECODE_STRING) { // For a string + length = (int) readInteger(length); // Read length field to get final length of string + } + curPos.advancePosition(length); // Skip -length- data + } + + @Override + public AddressFactory getAddressFactory() { + return addrFactory; + } + + @Override + public void clear() { + inStream = null; + } + + @Override + public void open(int max, String source) { + inStream = new LinkedByteBuffer(max, source); + } + + @Override + public void ingestStream(InputStream stream) throws IOException { + inStream.ingestStream(stream); + } + + @Override + public void endIngest() { + inStream.pad(ELEMENT_END); + inStream.getStartPosition(endPos); + } + + @Override + public boolean isEmpty() { + return (inStream == null); + } + + @Override + public int peekElement() throws DecoderException { + byte header1 = endPos.getByte(); + if ((header1 & HEADER_MASK) != ELEMENT_START) { + return 0; + } + int id = header1 & ELEMENTID_MASK; + if ((header1 & HEADEREXTEND_MASK) != 0) { + id <<= RAWDATA_BITSPERBYTE; + id |= (endPos.getBytePlus1() & RAWDATA_MASK); + } + return id; + } + + @Override + public int openElement() throws DecoderException { + byte header1 = endPos.getByte(); + if ((header1 & HEADER_MASK) != ELEMENT_START) { + return 0; + } + endPos.getNextByte(); + int id = header1 & ELEMENTID_MASK; + if ((header1 & HEADEREXTEND_MASK) != 0) { + id <<= RAWDATA_BITSPERBYTE; + id |= (endPos.getNextByte() & RAWDATA_MASK); + } + startPos.copy(endPos); + curPos.copy(endPos); + header1 = curPos.getByte(); + while ((header1 & HEADER_MASK) == ATTRIBUTE) { + skipAttribute(); + header1 = curPos.getByte(); + } + endPos.copy(curPos); + curPos.copy(startPos); + attributeRead = true; // "Last attribute was read" is vacuously true + return id; + } + + @Override + public int openElement(ElementId elemId) throws DecoderException { + int id = openElement(); + if (id != elemId.id()) { + if (id == 0) { + throw new DecoderException( + "Expecting <" + elemId.name() + "> but did not scan an element"); + } + throw new DecoderException("Expecting <" + elemId.name() + "> but id did not match"); + } + return id; + } + + @Override + public void closeElement(int id) throws DecoderException { + byte header1 = endPos.getNextByte(); + if ((header1 & HEADER_MASK) != ELEMENT_END) { + throw new DecoderException("Expecting element close"); + } + int closeId = header1 & ELEMENTID_MASK; + if ((header1 & HEADEREXTEND_MASK) != 0) { + closeId <<= RAWDATA_BITSPERBYTE; + closeId |= (endPos.getNextByte() & RAWDATA_MASK); + } + if (id != closeId) { + throw new DecoderException("Did not see expected closing element"); + } + } + + @Override + public void closeElementSkipping(int id) throws DecoderException { + ArrayList idstack = new ArrayList<>(); + idstack.add(id); + do { + int header1 = endPos.getByte() & HEADER_MASK; + if (header1 == ELEMENT_END) { + int pos = idstack.size() - 1; + closeElement(idstack.get(pos)); + idstack.remove(pos); + } + else if (header1 == ELEMENT_START) { + idstack.add(openElement()); + } + else { + throw new DecoderException("Corrupt stream"); + } + } + while (!idstack.isEmpty()); + } + + @Override + public int getNextAttributeId() throws DecoderException { + if (!attributeRead) { + skipAttribute(); + } + byte header1 = curPos.getByte(); + if ((header1 & HEADER_MASK) != ATTRIBUTE) { + return 0; + } + int id = header1 & ELEMENTID_MASK; + if ((header1 & HEADEREXTEND_MASK) != 0) { + id <<= RAWDATA_BITSPERBYTE; + id |= (curPos.getBytePlus1() & RAWDATA_MASK); + } + attributeRead = false; + return id; + } + + @Override + public void rewindAttributes() { + curPos.copy(startPos); + attributeRead = true; + } + + @Override + public boolean readBool() throws DecoderException { + byte header1 = curPos.getNextByte(); + if ((header1 & HEADEREXTEND_MASK) != 0) { + curPos.getNextByte(); + } + byte typeByte = curPos.getNextByte(); + if ((typeByte >> TYPECODE_SHIFT) != TYPECODE_BOOLEAN) { + throw new DecoderException("Expecting boolean attribute"); + } + attributeRead = true; + return ((typeByte & LENGTHCODE_MASK) != 0); + } + + @Override + public boolean readBool(AttributeId attribId) throws DecoderException { + findMatchingAttribute(attribId); + boolean res = readBool(); + curPos.copy(startPos); + return res; + } + + @Override + public long readSignedInteger() throws DecoderException { + byte header1 = curPos.getNextByte(); + if ((header1 & HEADEREXTEND_MASK) != 0) { + curPos.getNextByte(); + } + byte typeByte = curPos.getNextByte(); + int typeCode = typeByte >> TYPECODE_SHIFT; + long res; + if (typeCode == TYPECODE_SIGNEDINT_POSITIVE) { + res = readInteger(typeByte & LENGTHCODE_MASK); + } + else if (typeCode == TYPECODE_SIGNEDINT_NEGATIVE) { + res = readInteger(typeByte & LENGTHCODE_MASK); + res = -res; + } + else { + skipAttributeRemaining(typeByte); + throw new DecoderException("Expecting signed integer attribute"); + } + attributeRead = true; + return res; + } + + @Override + public long readSignedInteger(AttributeId attribId) throws DecoderException { + findMatchingAttribute(attribId); + long res = readSignedInteger(); + curPos.copy(startPos); + return res; + } + + @Override + public long readUnsignedInteger() throws DecoderException { + byte header1 = curPos.getNextByte(); + if ((header1 & HEADEREXTEND_MASK) != 0) { + curPos.getNextByte(); + } + byte typeByte = curPos.getNextByte(); + int typeCode = typeByte >> TYPECODE_SHIFT; + long res; + if (typeCode == TYPECODE_UNSIGNEDINT) { + res = readInteger(typeByte & 0xf); + } + else { + skipAttributeRemaining(typeByte); + throw new DecoderException("Expecting unsigned integer attribute"); + } + attributeRead = true; + return res; + } + + @Override + public long readUnsignedInteger(AttributeId attribId) throws DecoderException { + findMatchingAttribute(attribId); + long res = readUnsignedInteger(); + curPos.copy(startPos); + return res; + } + + @Override + public String readString() throws DecoderException { + byte header1 = curPos.getNextByte(); + if ((header1 & HEADEREXTEND_MASK) != 0) { + curPos.getNextByte(); + } + byte typeByte = curPos.getNextByte(); + int typeCode = typeByte >> TYPECODE_SHIFT; + if (typeCode != TYPECODE_STRING) { + skipAttributeRemaining(typeByte); + throw new DecoderException("Expecting string attribute"); + } + int length = typeByte & LENGTHCODE_MASK; + length = (int) readInteger(length); + + attributeRead = true; + int curLen = curPos.array.length - curPos.current; + if (curLen >= length) { + String res = new String(curPos.array, curPos.current, length); + curPos.advancePosition(length); + return res; + } + StringBuilder buf = new StringBuilder(); + String res = new String(curPos.array, curPos.current, curLen); + buf.append(res); + length -= curLen; + curPos.advancePosition(curLen); + while (length > 0) { + curLen = curPos.array.length - curPos.current; + if (curLen > length) { + curLen = length; + } + res = new String(curPos.array, curPos.current, curLen); + buf.append(res); + length -= curLen; + curPos.advancePosition(curLen); + } + res = buf.toString(); + return res; + } + + @Override + public String readString(AttributeId attribId) throws DecoderException { + findMatchingAttribute(attribId); + String res = readString(); + curPos.copy(startPos); + return res; + } + + @Override + public AddressSpace readSpace() throws DecoderException { + byte header1 = curPos.getNextByte(); + if ((header1 & HEADEREXTEND_MASK) != 0) { + curPos.getNextByte(); + } + byte typeByte = curPos.getNextByte(); + int typeCode = typeByte >> TYPECODE_SHIFT; + AddressSpace spc = null; + if (typeCode == TYPECODE_ADDRESSSPACE) { + int res = (int) readInteger(typeByte & LENGTHCODE_MASK); + if (res >= 0 && res < spaces.length) { + spc = spaces[res]; + } + if (spc == null) { + throw new DecoderException("Unknown address space index"); + } + } + else if (typeCode == TYPECODE_SPECIALSPACE) { + int specialCode = typeByte & LENGTHCODE_MASK; + if (specialCode == SPECIALSPACE_STACK) { + spc = addrFactory.getStackSpace(); + } + else if (specialCode == SPECIALSPACE_JOIN) { + spc = AddressSpace.VARIABLE_SPACE; + } + else if (specialCode == SPECIALSPACE_SPACEBASE) { + // TODO: Add support for decompiler non-stack "register relative" spaces + // We let the null address space get returned here. Its as if, no space + // attribute is given in an element, resulting in NO_ADDRESS + // spc = null; + } + else { + throw new DecoderException("Cannot marshal special address space"); + } + } + else { + skipAttributeRemaining(typeByte); + throw new DecoderException("Expecting space attribute"); + } + attributeRead = true; + return spc; + } + + @Override + public AddressSpace readSpace(AttributeId attribId) throws DecoderException { + findMatchingAttribute(attribId); + AddressSpace res = readSpace(); + curPos.copy(startPos); + return res; + } + +} diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/PackedEncode.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/PackedEncode.java new file mode 100644 index 0000000000..d1ea3ee984 --- /dev/null +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/PackedEncode.java @@ -0,0 +1,293 @@ +/* ### + * 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. + */ +package ghidra.program.model.pcode; + +import static ghidra.program.model.pcode.PackedDecode.*; + +import java.io.IOException; +import java.io.OutputStream; + +import ghidra.program.model.address.AddressSpace; + +/** + * A byte-based encoder designed to marshal to the decompiler efficiently + * See {@code PackedDecode} for details of the encoding format + */ +public class PackedEncode implements PatchEncoder { + private PackedBytes outStream; + + private void writeHeader(int header, int id) { + if (id > 0x1f) { + header |= HEADEREXTEND_MASK; + header |= (id >> RAWDATA_BITSPERBYTE); + int extendByte = (id & RAWDATA_MASK) | RAWDATA_MARKER; + outStream.write(header); + outStream.write(extendByte); + } + else { + header |= id; + outStream.write(header); + } + } + + private void writeInteger(int typeByte, long val) { + byte lenCode; + int sa; + if (val <= 0) { + if (val == 0) { + lenCode = 0; + sa = -1; + } + else { + lenCode = 10; + sa = 9 * RAWDATA_BITSPERBYTE; + } + } + else if (val < 0x800000000L) { + if (val < 0x200000L) { + if (val < 0x80L) { + lenCode = 1; // 7-bits + sa = 0; + } + else if (val < 0x4000L) { + lenCode = 2; // 14-bits + sa = RAWDATA_BITSPERBYTE; + } + else { + lenCode = 3; // 21-bits + sa = 2 * RAWDATA_BITSPERBYTE; + } + } + else if (val < 0x10000000L) { + lenCode = 4; // 28-bits + sa = 3 * RAWDATA_BITSPERBYTE; + } + else { + lenCode = 5; // 35-bits + sa = 4 * RAWDATA_BITSPERBYTE; + } + } + else if (val < 0x2000000000000L) { + if (val < 0x40000000000L) { + lenCode = 6; + sa = 5 * RAWDATA_BITSPERBYTE; + } + else { + lenCode = 7; + sa = 6 * RAWDATA_BITSPERBYTE; + } + } + else { + if (val < 0x100000000000000L) { + lenCode = 8; + sa = 7 * RAWDATA_BITSPERBYTE; + } + else { + lenCode = 9; + sa = 8 * RAWDATA_BITSPERBYTE; + } + } + typeByte |= lenCode; + outStream.write(typeByte); + for (; sa >= 0; sa -= RAWDATA_BITSPERBYTE) { + long piece = (val >>> sa) & RAWDATA_MASK; + piece |= RAWDATA_MARKER; + outStream.write((int) piece); + } + } + + public PackedEncode() { + outStream = null; + } + + @Override + public void clear() { + outStream = new PackedBytes(512); + } + + @Override + public void openElement(ElementId elemId) throws IOException { + writeHeader(ELEMENT_START, elemId.id()); + } + + @Override + public void closeElement(ElementId elemId) throws IOException { + writeHeader(ELEMENT_END, elemId.id()); + } + + @Override + public void writeBool(AttributeId attribId, boolean val) throws IOException { + writeHeader(ATTRIBUTE, attribId.id()); + int typeByte = val ? 0x11 : 0x10; + outStream.write(typeByte); + } + + @Override + public void writeSignedInteger(AttributeId attribId, long val) throws IOException { + writeHeader(0xc0, attribId.id()); + int typeByte; + long num; + if (val < 0) { + typeByte = (TYPECODE_SIGNEDINT_NEGATIVE << TYPECODE_SHIFT); + num = -val; + } + else { + typeByte = (TYPECODE_SIGNEDINT_POSITIVE << TYPECODE_SHIFT); + num = val; + } + writeInteger(typeByte, num); + } + + @Override + public void writeUnsignedInteger(AttributeId attribId, long val) throws IOException { + writeHeader(ATTRIBUTE, attribId.id()); + writeInteger((TYPECODE_UNSIGNEDINT << TYPECODE_SHIFT), val); + } + + @Override + public void writeString(AttributeId attribId, String val) throws IOException { + byte[] bytes = val.getBytes(); + writeHeader(ATTRIBUTE, attribId.id()); + writeInteger((TYPECODE_STRING << TYPECODE_SHIFT), bytes.length); + outStream.write(bytes); + } + + @Override + public void writeSpace(AttributeId attribId, AddressSpace spc) throws IOException { + writeHeader(ATTRIBUTE, attribId.id()); + switch (spc.getType()) { + case AddressSpace.TYPE_CONSTANT: + case AddressSpace.TYPE_RAM: + case AddressSpace.TYPE_REGISTER: + case AddressSpace.TYPE_UNIQUE: + case AddressSpace.TYPE_OTHER: + writeInteger((TYPECODE_ADDRESSSPACE << TYPECODE_SHIFT), spc.getUnique()); + break; + case AddressSpace.TYPE_VARIABLE: + outStream.write((TYPECODE_SPECIALSPACE << TYPECODE_SHIFT) | SPECIALSPACE_JOIN); + break; + case AddressSpace.TYPE_STACK: + outStream.write((TYPECODE_SPECIALSPACE << TYPECODE_SHIFT) | SPECIALSPACE_STACK); + break; + default: + throw new IOException("Cannot marshal address space: " + spc.getName()); + } + } + + @Override + public void writeTo(OutputStream stream) throws IOException { + outStream.writeTo(stream); + } + + @Override + public boolean isEmpty() { + return (outStream.size() == 0); + } + + @Override + public int size() { + return outStream.size(); + } + + @Override + public void writeSpaceId(AttributeId attribId, long spaceId) { + writeHeader(ATTRIBUTE, attribId.id()); + int uniqueId = (int) spaceId >> AddressSpace.ID_UNIQUE_SHIFT; + writeInteger((TYPECODE_ADDRESSSPACE << TYPECODE_SHIFT), uniqueId); + } + + /** + * Return the position after the open element directive at the given position. + * @param pos is the given position + * @return the next position or -1 if the current byte is not an open directive + */ + private int skipOpen(int pos) { + int val = outStream.getByte(pos) & (HEADER_MASK | HEADEREXTEND_MASK); + if (val == ELEMENT_START) { + return pos + 1; + } + else if (val == (ELEMENT_START | HEADEREXTEND_MASK)) { + return pos + 2; + } + return -1; + } + + /** + * Read the integer at the given position. + * @param pos is the given position + * @param len is the length of the integer in 7-bit bytes + * @return the integer + */ + private long readInteger(int pos, int len) { + long res = 0; + while (len > 0) { + res <<= RAWDATA_BITSPERBYTE; + res |= (outStream.getByte(pos) & RAWDATA_MASK); + pos += 1; + len -= 1; + } + return res; + } + + @Override + public boolean patchIntegerAttribute(int pos, AttributeId attribId, long val) { + int typeByte; + int length; + + pos = skipOpen(pos); + if (pos < 0) { + return false; + } + for (;;) { + int header1 = outStream.getByte(pos); // Attribute header + if ((header1 & HEADER_MASK) != ATTRIBUTE) { + return false; + } + pos += 1; + int curid = header1 & ELEMENTID_MASK; + if ((header1 & HEADEREXTEND_MASK) != 0) { + curid <<= RAWDATA_BITSPERBYTE; + curid |= outStream.getByte(pos) & RAWDATA_MASK; + pos += 1; // Extra byte for extended id + } + typeByte = outStream.getByte(pos) & 0xff; // Type (and length) byte + pos += 1; + int attribType = typeByte >> TYPECODE_SHIFT; + if (attribType == TYPECODE_BOOLEAN || attribType == TYPECODE_SPECIALSPACE) { + continue; // has no additional data + } + length = typeByte & LENGTHCODE_MASK; // Length of data in bytes + if (attribType == TYPECODE_STRING) { // For a string + length = (int) readInteger(pos, length); // Read length field to get final length of string + } + if (attribId.id() == curid) { + break; + } + pos += length; // Skip -length- data + } + if (length != 10) { + return false; + } + + for (int sa = 9 * RAWDATA_BITSPERBYTE; sa >= 0; sa -= RAWDATA_BITSPERBYTE) { + long piece = (val >>> sa) & RAWDATA_MASK; + piece |= RAWDATA_MARKER; + outStream.insertByte(pos, (int) piece); + pos += 1; + } + return true; + } +} diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/ParamMeasure.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/ParamMeasure.java index 34ef0375e7..2ecd35b67d 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/ParamMeasure.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/ParamMeasure.java @@ -50,9 +50,9 @@ public class ParamMeasure { * Decode a ParamMeasure object from the stream. * @param decoder is the stream decoder * @param factory pcode factory - * @throws PcodeXMLException for an invalid encoding + * @throws DecoderException for an invalid encoding */ - public void decode(Decoder decoder, PcodeFactory factory) throws PcodeXMLException { + public void decode(Decoder decoder, PcodeFactory factory) throws DecoderException { vn = Varnode.decode(decoder, factory); dt = factory.getDataTypeManager().decodeDataType(decoder); int rankel = decoder.openElement(ElementId.ELEM_RANK); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/PatchEncoder.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/PatchEncoder.java new file mode 100644 index 0000000000..86f27755bc --- /dev/null +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/PatchEncoder.java @@ -0,0 +1,52 @@ +/* ### + * 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. + */ +package ghidra.program.model.pcode; + +/** + * This is an encoder that produces encodings that can be retroactively patched. + * The contained encoding is expected to be byte based. The user can record a position + * in the encoding by calling the size() method in the middle of encoding, and then later + * use the returned offset to call the patchIntegerAttribute() method and modify the + * encoding at the recorded position. + */ +public interface PatchEncoder extends Encoder { + + /** + * Write a given raw spaceid (as returned by AddressSpace.getSpaceID()) as an attribute. + * The effect is the same as if writeSpace() was called with the AddressSpace matching + * the spaceid, i.e. the decoder will read this as just space attribute. + * @param attribId is the attribute + * @param spaceId is the given spaceid + */ + public void writeSpaceId(AttributeId attribId, long spaceId); + + /** + * The returned value can be used as a position for later modification + * @return the number of bytes written to this stream so far + */ + public int size(); + + /** + * Replace an integer attribute for the element at the given position. + * The position is assumed to be at an open directive for the element containing the + * attribute to be patched. + * @param pos is the given position + * @param attribId is the attribute to be patched + * @param val is the new value to insert + * @return true if the attribute is successfully patched + */ + public boolean patchIntegerAttribute(int pos, AttributeId attribId, long val); +} diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/PcodeBlock.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/PcodeBlock.java index 132ecd463c..4cf04a3668 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/PcodeBlock.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/PcodeBlock.java @@ -166,28 +166,28 @@ public class PcodeBlock { * Decode a single edge * @param decoder is the stream decoder * @param resolver used to recover PcodeBlock reference - * @throws PcodeXMLException for invalid encodings + * @throws DecoderException for invalid encodings */ - public void decode(Decoder decoder, BlockMap resolver) throws PcodeXMLException { + public void decode(Decoder decoder, BlockMap resolver) throws DecoderException { int el = decoder.openElement(ELEM_EDGE); label = 0; // Tag does not currently contain info about label int endIndex = (int) decoder.readSignedInteger(ATTRIB_END); point = resolver.findLevelBlock(endIndex); if (point == null) { - throw new PcodeXMLException("Bad serialized edge in block graph"); + throw new DecoderException("Bad serialized edge in block graph"); } reverse_index = (int) decoder.readSignedInteger(ATTRIB_REV); decoder.closeElement(el); } public void decode(Decoder decoder, ArrayList blockList) - throws PcodeXMLException { + throws DecoderException { int el = decoder.openElement(ELEM_EDGE); label = 0; // Tag does not currently contain info about label int endIndex = (int) decoder.readSignedInteger(ATTRIB_END); point = blockList.get(endIndex); if (point == null) { - throw new PcodeXMLException("Bad serialized edge in block list"); + throw new DecoderException("Bad serialized edge in block list"); } reverse_index = (int) decoder.readSignedInteger(ATTRIB_REV); decoder.closeElement(el); @@ -248,9 +248,9 @@ public class PcodeBlock { * Decode the next input edge from the stream * @param decoder is the stream decoder * @param resolver is used to find PcodeBlocks - * @throws PcodeXMLException for any invalid encoding + * @throws DecoderException for any invalid encoding */ - protected void decodeNextInEdge(Decoder decoder, BlockMap resolver) throws PcodeXMLException { + protected void decodeNextInEdge(Decoder decoder, BlockMap resolver) throws DecoderException { BlockEdge inEdge = new BlockEdge(); intothis.add(inEdge); inEdge.decode(decoder, resolver); @@ -265,10 +265,10 @@ public class PcodeBlock { * Decode the next input edge from the stream. Resolve block indices via a blockList * @param decoder is the stream decoder * @param blockList allows lookup of PcodeBlock via index - * @throws PcodeXMLException for any invalid encoding + * @throws DecoderException for any invalid encoding */ protected void decodeNextInEdge(Decoder decoder, ArrayList blockList) - throws PcodeXMLException { + throws DecoderException { BlockEdge inEdge = new BlockEdge(); intothis.add(inEdge); inEdge.decode(decoder, blockList); @@ -358,7 +358,7 @@ public class PcodeBlock { encoder.writeSignedInteger(ATTRIB_INDEX, index); } - protected void decodeHeader(Decoder decoder) throws PcodeXMLException { + protected void decodeHeader(Decoder decoder) throws DecoderException { index = (int) decoder.readSignedInteger(ATTRIB_INDEX); } @@ -387,13 +387,13 @@ public class PcodeBlock { * Restore the any additional information beyond header and edges from stream * @param decoder is the stream decoder * @param resolver is for looking up edge references - * @throws PcodeXMLException for invalid encoding + * @throws DecoderException for invalid encoding */ - protected void decodeBody(Decoder decoder, BlockMap resolver) throws PcodeXMLException { + protected void decodeBody(Decoder decoder, BlockMap resolver) throws DecoderException { // No body to restore by default } - protected void decodeEdges(Decoder decoder, BlockMap resolver) throws PcodeXMLException { + protected void decodeEdges(Decoder decoder, BlockMap resolver) throws DecoderException { for (;;) { int el = decoder.peekElement(); if (el != ELEM_EDGE.id()) { @@ -420,9 +420,9 @@ public class PcodeBlock { * Decode this block from a stream * @param decoder is the stream decoder * @param resolver is the map from reference to block object - * @throws PcodeXMLException for errors in the encoding + * @throws DecoderException for errors in the encoding */ - public void decode(Decoder decoder, BlockMap resolver) throws PcodeXMLException { + public void decode(Decoder decoder, BlockMap resolver) throws DecoderException { int el = decoder.openElement(ELEM_BLOCK); decodeHeader(decoder); decodeBody(decoder, resolver); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/PcodeBlockBasic.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/PcodeBlockBasic.java index edcedfb0ab..5a25faba4b 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/PcodeBlockBasic.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/PcodeBlockBasic.java @@ -127,7 +127,7 @@ public class PcodeBlockBasic extends PcodeBlock { } @Override - protected void decodeBody(Decoder decoder, BlockMap resolver) throws PcodeXMLException { + protected void decodeBody(Decoder decoder, BlockMap resolver) throws DecoderException { int rangelistel = decoder.openElement(ELEM_RANGELIST); for (;;) { int rangeel = decoder.peekElement(); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/PcodeDataTypeManager.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/PcodeDataTypeManager.java index 028e381d4e..d3e59b6767 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/PcodeDataTypeManager.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/PcodeDataTypeManager.java @@ -165,9 +165,9 @@ public class PcodeDataTypeManager { * Decode a data-type from the stream * @param decoder is the stream decoder * @return the decoded data-type object - * @throws PcodeXMLException for invalid encodings + * @throws DecoderException for invalid encodings */ - public DataType decodeDataType(Decoder decoder) throws PcodeXMLException { + public DataType decodeDataType(Decoder decoder) throws DecoderException { int el = decoder.openElement(); if (el == ELEM_VOID.id()) { decoder.closeElement(el); @@ -196,7 +196,7 @@ public class PcodeDataTypeManager { return findBaseType(name, id); } if (el != ELEM_TYPE.id()) { - throw new PcodeXMLException("Expecting element"); + throw new DecoderException("Expecting element"); } if (name.length() != 0) { @@ -258,7 +258,7 @@ public class PcodeDataTypeManager { return Undefined.getUndefinedDataType(size).clone(progDataTypes); } if (restype == null) { - throw new PcodeXMLException("Unable to resolve DataType"); + throw new DecoderException("Unable to resolve DataType"); } decoder.closeElementSkipping(el); return restype; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/PcodeFactory.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/PcodeFactory.java index f619f1ed2d..5c9d48fe4f 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/PcodeFactory.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/PcodeFactory.java @@ -60,11 +60,11 @@ public interface PcodeFactory { * @param addr join address associated with pieces * * @return the decoded VariableStorage - * @throws PcodeXMLException for an improperly encoded stream + * @throws DecoderException for an improperly encoded stream * @throws InvalidInputException if the pieces are not valid storage locations */ public VariableStorage decodeVarnodePieces(Decoder decoder, Address addr) - throws PcodeXMLException, InvalidInputException; + throws DecoderException, InvalidInputException; public Varnode createFromStorage(Address addr, VariableStorage storage, int logicalSize); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/PcodeOp.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/PcodeOp.java index 942e47332a..d2e697a9ce 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/PcodeOp.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/PcodeOp.java @@ -420,21 +420,22 @@ public class PcodeOp { } /** - * Encode this PcodeOp to a stream as an \ element + * Encode just the opcode and input/output Varnode data for this PcodeOp to a stream + * as an \ element * @param encoder is the stream encoder * @param addrFactory is a factory for looking up encoded address spaces * @throws IOException for errors in the underlying stream */ - public void encode(Encoder encoder, AddressFactory addrFactory) throws IOException { + public void encodeRaw(Encoder encoder, AddressFactory addrFactory) throws IOException { encoder.openElement(ELEM_OP); encoder.writeSignedInteger(ATTRIB_CODE, opcode); - seqnum.encode(encoder); + encoder.writeSignedInteger(ATTRIB_SIZE, input.length); if (output == null) { encoder.openElement(ELEM_VOID); encoder.closeElement(ELEM_VOID); } else { - output.encode(encoder); + output.encodeRaw(encoder); } if ((opcode == PcodeOp.LOAD) || (opcode == PcodeOp.STORE)) { int spaceId = (int) input[0].getOffset(); @@ -444,10 +445,10 @@ public class PcodeOp { encoder.closeElement(ELEM_SPACEID); } else if (input.length > 0) { - input[0].encode(encoder); + input[0].encodeRaw(encoder); } for (int i = 1; i < input.length; ++i) { - input[i].encode(encoder); + input[i].encodeRaw(encoder); } encoder.closeElement(ELEM_OP); } @@ -459,9 +460,9 @@ public class PcodeOp { * @param pfact factory used to create p-code correctly * * @return new PcodeOp - * @throws PcodeXMLException if encodings are invalid + * @throws DecoderException if encodings are invalid */ - public static PcodeOp decode(Decoder decoder, PcodeFactory pfact) throws PcodeXMLException { + public static PcodeOp decode(Decoder decoder, PcodeFactory pfact) throws DecoderException { int el = decoder.openElement(ELEM_OP); int opc = (int) decoder.readSignedInteger(ATTRIB_CODE); SequenceNumber seqnum = SequenceNumber.decode(decoder); @@ -480,7 +481,7 @@ public class PcodeOp { res = pfact.newOp(seqnum, opc, inputlist, output); } catch (UnknownInstructionException e) { - throw new PcodeXMLException("Bad opcode: " + e.getMessage(), e); + throw new DecoderException("Bad opcode: " + e.getMessage(), e); } decoder.closeElement(el); return res; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/PcodeSyntaxTree.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/PcodeSyntaxTree.java index 98b5f30434..eb2d2710dd 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/PcodeSyntaxTree.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/PcodeSyntaxTree.java @@ -69,50 +69,49 @@ public class PcodeSyntaxTree implements PcodeFactory { } private static Varnode getVarnodePiece(String pieceStr, AddressFactory addrFactory) - throws PcodeXMLException { + throws DecoderException { // TODO: Can't handle register name since addrFactory can't handle this String[] varnodeTokens = pieceStr.split(":"); if (varnodeTokens.length != 3) { - throw new PcodeXMLException("Invalid XML addr piece: " + pieceStr); + throw new DecoderException("Invalid \"join\" address piece: " + pieceStr); } AddressSpace space = addrFactory.getAddressSpace(varnodeTokens[0]); if (space == null) { - throw new PcodeXMLException("Invalid XML addr, space not found: " + pieceStr); + throw new DecoderException("Invalid space for \"join\" address piece: " + pieceStr); } if (!varnodeTokens[1].startsWith("0x")) { - throw new PcodeXMLException("Invalid XML addr piece offset: " + pieceStr); + throw new DecoderException("Invalid offset for \"join\" address piece: " + pieceStr); } long offset; try { offset = Long.parseUnsignedLong(varnodeTokens[1].substring(2), 16); } catch (NumberFormatException e) { - throw new PcodeXMLException("Invalid XML addr piece offset: " + pieceStr); + throw new DecoderException("Invalid offset for \"join\" address piece: " + pieceStr); } int size; try { size = Integer.parseInt(varnodeTokens[2]); } catch (NumberFormatException e) { - throw new PcodeXMLException("Invalid XML addr piece size: " + pieceStr); + throw new DecoderException("Invalid size for \"join\" address piece: " + pieceStr); } return new Varnode(space.getAddress(offset), size); } @Override public VariableStorage decodeVarnodePieces(Decoder decoder, Address addr) - throws PcodeXMLException, InvalidInputException { + throws DecoderException, InvalidInputException { ArrayList list = new ArrayList<>(); for (;;) { int attribId = decoder.getNextAttributeId(); if (attribId == 0) { break; } - else if (attribId >= ATTRIB_PIECE1.id() && - attribId <= ATTRIB_PIECE9.id()) { + else if (attribId >= ATTRIB_PIECE1.id() && attribId <= ATTRIB_PIECE9.id()) { int index = attribId - ATTRIB_PIECE1.id(); if (index != list.size()) { - throw new PcodeXMLException("\"piece\" attributes must be in order"); + throw new DecoderException("\"piece\" attributes must be in order"); } list.add(getVarnodePiece(decoder.readString(), decoder.getAddressFactory())); } @@ -523,7 +522,7 @@ public class PcodeSyntaxTree implements PcodeFactory { return op; } - private void decodeVarnode(Decoder decoder) throws PcodeXMLException { + private void decodeVarnode(Decoder decoder) throws DecoderException { int el = decoder.openElement(ELEM_VARNODES); for (;;) { int subId = decoder.peekElement(); @@ -535,7 +534,7 @@ public class PcodeSyntaxTree implements PcodeFactory { decoder.closeElement(el); } - private void decodeBasicBlock(Decoder decoder, BlockMap resolver) throws PcodeXMLException { + private void decodeBasicBlock(Decoder decoder, BlockMap resolver) throws DecoderException { int el = decoder.openElement(ELEM_BLOCK); int order = 0; PcodeBlockBasic bl = new PcodeBlockBasic(); @@ -559,7 +558,7 @@ public class PcodeSyntaxTree implements PcodeFactory { decoder.closeElement(el); } - private void decodeBlockEdge(Decoder decoder) throws PcodeXMLException { + private void decodeBlockEdge(Decoder decoder) throws DecoderException { int el = decoder.openElement(ELEM_BLOCKEDGE); int blockInd = (int) decoder.readSignedInteger(ATTRIB_INDEX); PcodeBlockBasic curBlock = bblocks.get(blockInd); @@ -573,7 +572,7 @@ public class PcodeSyntaxTree implements PcodeFactory { decoder.closeElement(el); } - public void decode(Decoder decoder) throws PcodeXMLException { + public void decode(Decoder decoder) throws DecoderException { int el = decoder.openElement(ELEM_AST); if (!vbank.isEmpty()) { clear(); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/SequenceNumber.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/SequenceNumber.java index e13f8b6802..314c1c3171 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/SequenceNumber.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/SequenceNumber.java @@ -150,9 +150,9 @@ public class SequenceNumber implements Comparable { * @param decoder is the stream decoder * * @return new sequence number - * @throws PcodeXMLException for an invalid encoding + * @throws DecoderException for an invalid encoding */ - public static SequenceNumber decode(Decoder decoder) throws PcodeXMLException { + public static SequenceNumber decode(Decoder decoder) throws DecoderException { int el = decoder.openElement(ELEM_SEQNUM); int uniq = -1; for (;;) { diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/StringIngest.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/StringIngest.java new file mode 100644 index 0000000000..0ba3d8eb34 --- /dev/null +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/StringIngest.java @@ -0,0 +1,75 @@ +/* ### + * 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. + */ +package ghidra.program.model.pcode; + +import java.io.*; + +public class StringIngest implements ByteIngest { + + private ByteArrayOutputStream outStream; + private String source; + private int maxBytes; + + public StringIngest() { + outStream = null; + source = null; + maxBytes = 0; + } + + public StringIngest(int max, String src) { + open(max, src); + } + + @Override + public void open(int max, String src) { + maxBytes = max; + source = src; + outStream = new ByteArrayOutputStream(); + } + + @Override + public void ingestStream(InputStream inStream) throws IOException { + int tok = inStream.read(); + while (tok > 0) { + outStream.write(tok); + if (outStream.size() >= maxBytes) { + throw new IOException("Buffer size exceeded: " + source); + } + tok = inStream.read(); + } + } + + @Override + public void endIngest() { + // Nothing needs to be done + } + + @Override + public void clear() { + outStream = null; + source = null; + } + + @Override + public String toString() { + return outStream.toString(); + } + + @Override + public boolean isEmpty() { + return (outStream.size() == 0); + } +} diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/SymbolEntry.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/SymbolEntry.java index c4ca79d21e..bfd9a2b4a2 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/SymbolEntry.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/SymbolEntry.java @@ -43,9 +43,9 @@ public abstract class SymbolEntry { /** * Decode this entry from the stream. Typically more than one element is consumed. * @param decoder is the stream decoder - * @throws PcodeXMLException for invalid encodings + * @throws DecoderException for invalid encodings */ - public abstract void decode(Decoder decoder) throws PcodeXMLException; + public abstract void decode(Decoder decoder) throws DecoderException; /** * Encode this entry as (a set of) elements to the given stream @@ -85,7 +85,7 @@ public abstract class SymbolEntry { return pcaddr; } - protected void decodeRangeList(Decoder decoder) throws PcodeXMLException { + protected void decodeRangeList(Decoder decoder) throws DecoderException { int rangelistel = decoder.openElement(ELEM_RANGELIST); if (decoder.peekElement() != 0) { // we only use this to establish first-use diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/Varnode.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/Varnode.java index ac76822e65..493ab946db 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/Varnode.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/Varnode.java @@ -316,10 +316,11 @@ public class Varnode { } /** + * Encode just the raw storage info for this Varnode to stream * @param encoder is the stream encoder * @throws IOException for errors in the underlying stream */ - public void encode(Encoder encoder) throws IOException { + public void encodeRaw(Encoder encoder) throws IOException { AddressXML.encode(encoder, address, size); } @@ -329,9 +330,9 @@ public class Varnode { * @param decoder is the stream decoder * @param factory pcode factory used to create valid pcode * @return the new Varnode - * @throws PcodeXMLException if XML is improperly formed + * @throws DecoderException if the Varnode is improperly encoded */ - public static Varnode decode(Decoder decoder, PcodeFactory factory) throws PcodeXMLException { + public static Varnode decode(Decoder decoder, PcodeFactory factory) throws DecoderException { int el = decoder.peekElement(); if (el == ELEM_VOID.id()) { decoder.openElement(); @@ -379,7 +380,7 @@ public class Varnode { factory.decodeVarnodePieces(decoder, addr); } catch (InvalidInputException e) { - throw new PcodeXMLException("Invalid varnode pieces: " + e.getMessage()); + throw new DecoderException("Invalid varnode pieces: " + e.getMessage()); } } decoder.rewindAttributes(); @@ -413,7 +414,7 @@ public class Varnode { } } } - decoder.closeElement(sz); + decoder.closeElement(el); return vn; } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/XmlDecode.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/XmlDecode.java deleted file mode 100644 index 76f684e520..0000000000 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/XmlDecode.java +++ /dev/null @@ -1,293 +0,0 @@ -/* ### - * 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. - */ -package ghidra.program.model.pcode; - -import static ghidra.program.model.pcode.AttributeId.*; - -import java.io.InputStream; -import java.util.Iterator; -import java.util.Map; - -import org.xml.sax.*; - -import ghidra.program.model.address.AddressFactory; -import ghidra.program.model.address.AddressSpace; -import ghidra.util.Msg; -import ghidra.util.xml.SpecXmlUtils; -import ghidra.xml.*; - -public class XmlDecode implements Decoder { - - private XmlPullParser parser; - private XmlElement currentEl; - private Iterator> attribIterator; - private String attribValue; - private AddressFactory spcManager; - - public XmlDecode(AddressFactory factory) { - parser = null; - currentEl = null; - attribIterator = null; - attribValue = null; - spcManager = factory; - } - - @Override - public AddressFactory getAddressFactory() { - return spcManager; - } - - @Override - public void clear() { - parser = null; - currentEl = null; - attribIterator = null; - } - - @Override - public void ingestStream(InputStream stream, String source) throws PcodeXMLException { - ErrorHandler handler = new ErrorHandler() { - @Override - public void error(SAXParseException exception) throws SAXException { - Msg.error(this, "Error parsing " + source, exception); - } - - @Override - public void fatalError(SAXParseException exception) throws SAXException { - Msg.error(this, "Fatal error parsing " + source, exception); - } - - @Override - public void warning(SAXParseException exception) throws SAXException { - Msg.warn(this, "Warning parsing " + source, exception); - } - }; - try { - parser = XmlPullParserFactory.create(stream, source, handler, false); - } - catch (Exception e) { - throw new PcodeXMLException("XML parsing error: " + e.getMessage(), e); - } - } - - @Override - public int peekElement() { - XmlElement el = parser.peek(); - if (!el.isStart()) { - return 0; - } - return ElementId.find(el.getName()); - } - - @Override - public int openElement() { - XmlElement el = parser.softStart(); - if (el == null) { - return 0; - } - currentEl = el; - attribIterator = null; - return ElementId.find(currentEl.getName()); - } - - @Override - public int openElement(ElementId elemId) throws PcodeXMLException { - XmlElement el = parser.softStart(elemId.name()); - if (el == null) { - throw new PcodeXMLException("Expecting element <" + elemId.name() + '>'); - } - currentEl = el; - attribIterator = null; - return ElementId.find(currentEl.getName()); - } - - @Override - public void closeElement(int id) throws PcodeXMLException { - XmlElement el = parser.next(); - if (!el.isEnd()) { - throw new PcodeXMLException("Expecting end, but got start <" + el.getName() + '>'); - } - currentEl = null; - // Only one possible element can be closed as enforced by SAXParser - // so additional checks are somewhat redundant -// int elemId = ElementId.find(el.getName()); -// if (elemId != id) { -// throw new PcodeXMLException("Unexpected end, <" + el.getName() + '>'); -// } - } - - @Override - public void closeElementSkipping(int id) throws PcodeXMLException { - currentEl = null; - XmlElement el = parser.peek(); - if (el == null) { - throw new PcodeXMLException("No more elements"); - } - int level = el.getLevel(); - if (el.isStart()) { - level -= 1; - } - for (;;) { - el = parser.next(); - int curlevel = el.getLevel(); - if (curlevel > level) { - continue; - } - if (curlevel < level) { - throw new PcodeXMLException("Missing end element"); - } - if (el.isEnd()) { - break; - } - } - int elemId = ElementId.find(el.getName()); - if (elemId != id) { - throw new PcodeXMLException("Unexpected element end: " + el.getName()); - } - } - - @Override - public int getNextAttributeId() { - if (attribIterator == null) { - attribIterator = currentEl.getAttributeIterator(); - } - if (!attribIterator.hasNext()) { - return 0; - } - Map.Entry entry = attribIterator.next(); - attribValue = entry.getValue(); - return AttributeId.find(entry.getKey()); - } - - @Override - public void rewindAttributes() { - attribIterator = null; - } - - private String readContent() throws PcodeXMLException { - XmlElement el = parser.peek(); - if (el == null || !el.isEnd()) { - throw new PcodeXMLException("Cannot request ATTRIB_CONTENT here"); - } - return el.getText(); - } - - @Override - public boolean readBool() throws PcodeXMLException { - return SpecXmlUtils.decodeBoolean(attribValue); - } - - @Override - public boolean readBool(AttributeId attribId) throws PcodeXMLException { - String value; - if (attribId == ATTRIB_CONTENT) { - value = readContent(); - } - else { - value = currentEl.getAttribute(attribId.name()); - } - if (value == null) { - throw new PcodeXMLException("Missing attribute: " + attribId.name()); - } - attribIterator = null; - return SpecXmlUtils.decodeBoolean(value); - } - - @Override - public long readSignedInteger() throws PcodeXMLException { - return SpecXmlUtils.decodeLong(attribValue); - } - - @Override - public long readSignedInteger(AttributeId attribId) throws PcodeXMLException { - String value; - if (attribId == ATTRIB_CONTENT) { - value = readContent(); - } - else { - value = currentEl.getAttribute(attribId.name()); - } - if (value == null) { - throw new PcodeXMLException("Missing attribute: " + attribId.name()); - } - attribIterator = null; - return SpecXmlUtils.decodeLong(value); - } - - @Override - public long readUnsignedInteger() throws PcodeXMLException { - return SpecXmlUtils.decodeLong(attribValue); - } - - @Override - public long readUnsignedInteger(AttributeId attribId) throws PcodeXMLException { - String value; - if (attribId == ATTRIB_CONTENT) { - value = readContent(); - } - else { - value = currentEl.getAttribute(attribId.name()); - } - if (value == null) { - throw new PcodeXMLException("Missing attribute: " + attribId.name()); - } - attribIterator = null; - return SpecXmlUtils.decodeLong(value); - } - - @Override - public String readString() throws PcodeXMLException { - return attribValue; - } - - @Override - public String readString(AttributeId attribId) throws PcodeXMLException { - String value; - if (attribId == ATTRIB_CONTENT) { - value = readContent(); - } - else { - value = currentEl.getAttribute(attribId.name()); - } - if (value == null) { - throw new PcodeXMLException("Missing attribute: " + attribId.name()); - } - attribIterator = null; - return value; - } - - @Override - public AddressSpace readSpace() throws PcodeXMLException { - return spcManager.getAddressSpace(attribValue); - } - - @Override - public AddressSpace readSpace(AttributeId attribId) throws PcodeXMLException { - String value; - if (attribId == ATTRIB_CONTENT) { - value = readContent(); - } - else { - value = currentEl.getAttribute(attribId.name()); - } - if (value == null) { - throw new PcodeXMLException("Missing attribute: " + attribId.name()); - } - attribIterator = null; - return spcManager.getAddressSpace(value); - } - -} diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/XmlDecodeLight.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/XmlDecodeLight.java deleted file mode 100644 index 2d29470a5d..0000000000 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/XmlDecodeLight.java +++ /dev/null @@ -1,400 +0,0 @@ -/* ### - * 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. - */ -package ghidra.program.model.pcode; - -import static ghidra.program.model.pcode.AttributeId.*; - -import java.io.InputStream; - -import ghidra.program.model.address.AddressFactory; -import ghidra.program.model.address.AddressSpace; -import ghidra.util.xml.SpecXmlUtils; - -/** - * Lightweight XML decoder. - * - Element and attribute identifiers must contain only letters or digits - * - No XML comments - * - No escape codes - * - No content (except white space) - */ -public class XmlDecodeLight implements Decoder { - - private AddressFactory addressFactory; - private String raw; - private int currentPos; - private boolean startOpen; - private int attribStart; - private int currentElement; - - public XmlDecodeLight(AddressFactory addrFactory) { - addressFactory = addrFactory; - } - - @Override - public AddressFactory getAddressFactory() { - return addressFactory; - } - - @Override - public void clear() { - raw = null; - } - - public void ingestString(String data) { - raw = data; - currentPos = 0; - startOpen = false; - attribStart = -1; - } - - private int scanWhiteSpace(int start) throws PcodeXMLException { - while (start < raw.length()) { - char tok = raw.charAt(start); - if (!Character.isWhitespace(tok)) { - return start; - } - start += 1; - } - throw new PcodeXMLException("Premature end of stream"); - } - - private int scanIdentifier(int start) throws PcodeXMLException { - while (start < raw.length()) { - char tok = raw.charAt(start); - if (!Character.isLetterOrDigit(tok)) { - return start; - } - start += 1; - } - throw new PcodeXMLException("Premature end of stream"); - } - - private int scanToEndOfStart(int start) throws PcodeXMLException { - int state = 0; - while (start < raw.length()) { - char tok = raw.charAt(start); - if (state == 0) { - if (tok == '/' || tok == '>') { - return start; - } - if (tok == '\"') { - state = 1; - } - } - else if (state == 1) { - if (tok == '\"') { - state = 0; - } - } - start += 1; - } - throw new PcodeXMLException("Premature end of stream"); - } - - private String scanElement() throws PcodeXMLException { - int pos = currentPos; - if (startOpen) { - pos = scanToEndOfStart(pos); - if (raw.charAt(pos) == '>') { - pos += 1; - } - } - pos = scanWhiteSpace(pos); - if (raw.charAt(pos) != '<') { - throw new PcodeXMLException("Expecting start of element"); - } - pos += 1; - if (pos < raw.length() && raw.charAt(pos) == '/') { - return null; - } - pos = scanWhiteSpace(pos); - int endPos = scanIdentifier(pos); - if (pos == endPos) { - throw new PcodeXMLException("Parse error"); - } - currentPos = endPos; - startOpen = true; - return raw.substring(pos, endPos); - } - - private int scanQuote() throws PcodeXMLException { - int pos = currentPos + 1; - while (pos < raw.length()) { - if (raw.charAt(pos) == '\"') { - return pos + 1; - } - pos += 1; - } - throw new PcodeXMLException("Premature end of stream"); - } - - private String scanAttribute() throws PcodeXMLException { - int pos = currentPos; - currentPos = scanQuote(); - return raw.substring(pos + 1, currentPos - 1); - } - - @Override - public void ingestStream(InputStream stream, String source) throws PcodeXMLException { - throw new PcodeXMLException("Unimplemented method"); - } - - @Override - public int peekElement() { - int savePos = currentPos; - boolean saveStartOpen = startOpen; - String el; - try { - el = scanElement(); - currentPos = savePos; - startOpen = saveStartOpen; - if (el == null) { - return 0; - } - } - catch (PcodeXMLException e) { - currentPos = savePos; - startOpen = saveStartOpen; - return 0; - } - return ElementId.find(el); - } - - @Override - public int openElement() { - String el; - try { - el = scanElement(); - if (el == null) { - return 0; - } - } - catch (PcodeXMLException e) { - return 0; - } - attribStart = currentPos; - currentElement = ElementId.find(el); - return currentElement; - } - - @Override - public int openElement(ElementId elemId) throws PcodeXMLException { - String el = scanElement(); - if (el == null) { - throw new PcodeXMLException("Expecting start of " + elemId.name()); - } - attribStart = currentPos; - currentElement = ElementId.find(el); - if (currentElement != elemId.id()) { - throw new PcodeXMLException("Expecting element " + elemId.name()); - } - return currentElement; - } - - @Override - public void closeElement(int id) throws PcodeXMLException { - int pos = currentPos; - if (startOpen) { - pos = scanToEndOfStart(currentPos); - char tok = raw.charAt(pos); - if (tok == '/') { - pos += 1; - if (pos >= raw.length()) { - throw new PcodeXMLException("Premature end of stream"); - } - if (raw.charAt(pos) != '>') { - throw new PcodeXMLException("Parse error"); - } - currentPos = pos + 1; - if (id != currentElement) { - throw new PcodeXMLException("Parse error"); - } - startOpen = false; - return; - } - if (tok != '>') { - throw new PcodeXMLException("Parse error"); - } - startOpen = false; - } - pos = scanWhiteSpace(pos); - if (raw.charAt(pos) != '<') { - throw new PcodeXMLException("Parse error"); - } - pos += 1; - if (pos >= raw.length() || raw.charAt(pos) != '/') { - throw new PcodeXMLException("Parse error"); - } - pos = scanWhiteSpace(pos + 1); - int endpos = scanIdentifier(pos); - String ident = raw.substring(pos, endpos); - if (id != ElementId.find(ident)) { - throw new PcodeXMLException("Expecting end token"); - } - pos = scanWhiteSpace(endpos); - if (raw.charAt(pos) != '>') { - throw new PcodeXMLException("Parse error"); - } - currentPos = pos + 1; - } - - @Override - public void closeElementSkipping(int id) throws PcodeXMLException { - throw new PcodeXMLException("closeElementSkipping unimplemented"); - } - - @Override - public int getNextAttributeId() { - if (!startOpen) { - return 0; - } - try { - int pos = scanWhiteSpace(currentPos); - char tok = raw.charAt(pos); - if (tok == '\"') { - pos = scanQuote(); - pos = scanWhiteSpace(pos); - tok = raw.charAt(pos); - } - if (tok == '>' || tok == '/') { - currentPos = pos; - return 0; - } - int endPos = scanIdentifier(pos); - if (pos == endPos) { - throw new PcodeXMLException("Parse error"); - } - String ident = raw.substring(pos, endPos); - pos = scanWhiteSpace(endPos); - if (raw.charAt(pos) != '=') { - throw new PcodeXMLException("Parse error"); - } - pos = scanWhiteSpace(pos + 1); - if (raw.charAt(pos) != '\"') { - throw new PcodeXMLException("Parse error"); - } - currentPos = pos; - return AttributeId.find(ident); - } - catch (PcodeXMLException e) { - return 0; - } - } - - private void findAttribute(AttributeId attribId) throws PcodeXMLException { - currentPos = attribStart; - startOpen = true; - for (;;) { - int id = getNextAttributeId(); - if (id == 0) { - break; - } - if (id == attribId.id()) { - return; - } - } - throw new PcodeXMLException("Missing attribute: " + attribId.name()); - } - - @Override - public void rewindAttributes() { - currentPos = attribStart; - startOpen = true; - } - - @Override - public boolean readBool() throws PcodeXMLException { - String value = scanAttribute(); - return SpecXmlUtils.decodeBoolean(value); - } - - @Override - public boolean readBool(AttributeId attribId) throws PcodeXMLException { - findAttribute(attribId); - String value = scanAttribute(); - currentPos = attribStart; - startOpen = true; - return SpecXmlUtils.decodeBoolean(value); - } - - @Override - public long readSignedInteger() throws PcodeXMLException { - String value = scanAttribute(); - return SpecXmlUtils.decodeLong(value); - } - - @Override - public long readSignedInteger(AttributeId attribId) throws PcodeXMLException { - findAttribute(attribId); - String value = scanAttribute(); - currentPos = attribStart; - startOpen = true; - return SpecXmlUtils.decodeLong(value); - } - - @Override - public long readUnsignedInteger() throws PcodeXMLException { - String value = scanAttribute(); - return SpecXmlUtils.decodeLong(value); - } - - @Override - public long readUnsignedInteger(AttributeId attribId) throws PcodeXMLException { - findAttribute(attribId); - String value = scanAttribute(); - currentPos = attribStart; - startOpen = true; - return SpecXmlUtils.decodeLong(value); - } - - @Override - public String readString() throws PcodeXMLException { - return scanAttribute(); - } - - @Override - public String readString(AttributeId attribId) throws PcodeXMLException { - findAttribute(attribId); - String value = scanAttribute(); - currentPos = attribStart; - startOpen = true; - return value; - } - - @Override - public AddressSpace readSpace() throws PcodeXMLException { - String value = scanAttribute(); - AddressSpace spc = addressFactory.getAddressSpace(value); - if (spc == null) { - throw new PcodeXMLException("Unknown address space: " + value); - } - return spc; - } - - @Override - public AddressSpace readSpace(AttributeId attribId) throws PcodeXMLException { - findAttribute(attribId); - String value = scanAttribute(); - currentPos = attribStart; - startOpen = true; - AddressSpace spc = addressFactory.getAddressSpace(value); - if (spc == null) { - throw new PcodeXMLException("Unknown address space: " + value); - } - return spc; - } - -} diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/XmlEncode.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/XmlEncode.java index e866608f9b..2437676d84 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/XmlEncode.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/XmlEncode.java @@ -18,6 +18,7 @@ package ghidra.program.model.pcode; import static ghidra.program.model.pcode.AttributeId.*; import java.io.IOException; +import java.io.OutputStream; import ghidra.program.model.address.AddressSpace; import ghidra.util.xml.SpecXmlUtils; @@ -168,7 +169,13 @@ public class XmlEncode implements Encoder { } @Override - public byte[] getBytes() { - return buffer.toString().getBytes(); + public void writeTo(OutputStream stream) throws IOException { + byte[] res = buffer.toString().getBytes(); + stream.write(res); + } + + @Override + public boolean isEmpty() { + return buffer.isEmpty(); } } diff --git a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/pcode/EncodeDecodeTest.java b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/pcode/EncodeDecodeTest.java new file mode 100644 index 0000000000..d926a95926 --- /dev/null +++ b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/pcode/EncodeDecodeTest.java @@ -0,0 +1,483 @@ +/* ### + * 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. + */ +package ghidra.program.model.pcode; + +import static ghidra.program.model.pcode.AttributeId.*; +import static ghidra.program.model.pcode.ElementId.*; +import static org.junit.Assert.*; + +import java.io.*; + +import org.junit.Before; +import org.junit.Test; + +import generic.test.AbstractGenericTest; +import ghidra.program.model.address.*; + +public class EncodeDecodeTest extends AbstractGenericTest { + + private AddressFactory addrFactory; + + private void testSignedAttributes(Encoder encoder, Decoder decoder) + throws DecoderException, IOException + + { + encoder.openElement(ELEM_ADDR); + encoder.writeSignedInteger(ATTRIB_ALIGN, 3); // 7-bits + encoder.writeSignedInteger(ATTRIB_BIGENDIAN, -0x100); // 14-bits + encoder.writeSignedInteger(ATTRIB_CONSTRUCTOR, 0x1fffff); // 21-bits + encoder.writeSignedInteger(ATTRIB_DESTRUCTOR, -0xabcdefa); // 28-bits + encoder.writeSignedInteger(ATTRIB_EXTRAPOP, 0x300000000L); // 35-bits + encoder.writeSignedInteger(ATTRIB_FORMAT, -0x30101010101L); // 42-bits + encoder.writeSignedInteger(ATTRIB_ID, 0x123456789011L); // 49-bits + encoder.writeSignedInteger(ATTRIB_INDEX, -0xf0f0f0f0f0f0f0L); // 56-bits + encoder.writeSignedInteger(ATTRIB_METATYPE, 0x7fffffffffffffffL); // 63-bits + encoder.closeElement(ELEM_ADDR); + ByteArrayOutputStream outStream = new ByteArrayOutputStream(); + encoder.writeTo(outStream); + ByteArrayInputStream inStream = new ByteArrayInputStream(outStream.toByteArray()); + decoder.open(1 << 20, "testSignedAttributes"); + decoder.ingestStream(inStream); + decoder.endIngest(); + int el = decoder.openElement(ELEM_ADDR); + int flags = 0; + for (;;) { + int attribId = decoder.getNextAttributeId(); + if (attribId == 0) { + break; + } + if (attribId == ATTRIB_ALIGN.id()) { + long val = decoder.readSignedInteger(); + flags |= 1; + assertEquals(val, 3); + } + else if (attribId == ATTRIB_BIGENDIAN.id()) { + long val = decoder.readSignedInteger(); + flags |= 2; + assertEquals(val, -0x100); + } + else if (attribId == ATTRIB_CONSTRUCTOR.id()) { + long val = decoder.readSignedInteger(); + flags |= 4; + assertEquals(val, 0x1fffff); + } + else if (attribId == ATTRIB_DESTRUCTOR.id()) { + long val = decoder.readSignedInteger(); + flags |= 8; + assertEquals(val, -0xabcdefa); + } + else if (attribId == ATTRIB_EXTRAPOP.id()) { + long val = decoder.readSignedInteger(); + flags |= 0x10; + assertEquals(val, 0x300000000L); + } + else if (attribId == ATTRIB_FORMAT.id()) { + long val = decoder.readSignedInteger(); + flags |= 0x20; + assertEquals(val, -0x30101010101L); + } + else if (attribId == ATTRIB_ID.id()) { + long val = decoder.readSignedInteger(); + flags |= 0x40; + assertEquals(val, 0x123456789011L); + } + else if (attribId == ATTRIB_INDEX.id()) { + long val = decoder.readSignedInteger(); + flags |= 0x80; + assertEquals(val, -0xf0f0f0f0f0f0f0L); + } + else if (attribId == ATTRIB_METATYPE.id()) { + long val = decoder.readSignedInteger(); + flags |= 0x100; + assertEquals(val, 0x7fffffffffffffffL); + } + } + decoder.closeElement(el); + assertEquals(flags, 0x1ff); + } + + private void testUnsignedAttributes(Encoder encoder, Decoder decoder) + throws DecoderException, IOException + + { + encoder.openElement(ELEM_ADDR); + encoder.writeUnsignedInteger(ATTRIB_ALIGN, 3); // 7-bits + encoder.writeUnsignedInteger(ATTRIB_BIGENDIAN, 0x100); // 14-bits + encoder.writeUnsignedInteger(ATTRIB_CONSTRUCTOR, 0x1fffff); // 21-bits + encoder.writeUnsignedInteger(ATTRIB_DESTRUCTOR, 0xabcdefa); // 28-bits + encoder.writeUnsignedInteger(ATTRIB_EXTRAPOP, 0x300000000L); // 35-bits + encoder.writeUnsignedInteger(ATTRIB_FORMAT, 0x30101010101L); // 42-bits + encoder.writeUnsignedInteger(ATTRIB_ID, 0x123456789011L); // 49-bits + encoder.writeUnsignedInteger(ATTRIB_INDEX, 0xf0f0f0f0f0f0f0L); // 56-bits + encoder.writeUnsignedInteger(ATTRIB_METATYPE, 0x7fffffffffffffffL); // 63-bits + encoder.writeUnsignedInteger(ATTRIB_MODEL, 0x8000000000000000L); // 64-bits + encoder.closeElement(ELEM_ADDR); + ByteArrayOutputStream outStream = new ByteArrayOutputStream(); + encoder.writeTo(outStream); + ByteArrayInputStream inStream = new ByteArrayInputStream(outStream.toByteArray()); + decoder.open(1 << 20, "testUnsignedAttributes"); + decoder.ingestStream(inStream); + decoder.endIngest(); + int el = decoder.openElement(ELEM_ADDR); + long val = decoder.readUnsignedInteger(ATTRIB_ALIGN); + assertEquals(val, 3); + val = decoder.readUnsignedInteger(ATTRIB_BIGENDIAN); + assertEquals(val, 0x100); + val = decoder.readUnsignedInteger(ATTRIB_CONSTRUCTOR); + assertEquals(val, 0x1fffff); + val = decoder.readUnsignedInteger(ATTRIB_DESTRUCTOR); + assertEquals(val, 0xabcdefa); + val = decoder.readUnsignedInteger(ATTRIB_EXTRAPOP); + assertEquals(val, 0x300000000L); + val = decoder.readUnsignedInteger(ATTRIB_FORMAT); + assertEquals(val, 0x30101010101L); + val = decoder.readUnsignedInteger(ATTRIB_ID); + assertEquals(val, 0x123456789011L); + val = decoder.readUnsignedInteger(ATTRIB_INDEX); + assertEquals(val, 0xf0f0f0f0f0f0f0L); + val = decoder.readUnsignedInteger(ATTRIB_METATYPE); + assertEquals(val, 0x7fffffffffffffffL); + val = decoder.readUnsignedInteger(ATTRIB_MODEL); + assertEquals(val, 0x8000000000000000L); + decoder.closeElement(el); + } + + private void testAttributes(Encoder encoder, Decoder decoder) + throws DecoderException, IOException + + { + encoder.openElement(ELEM_DATA); + encoder.writeBool(ATTRIB_ALIGN, true); + encoder.writeBool(ATTRIB_BIGENDIAN, false); + AddressSpace spc = addrFactory.getDefaultAddressSpace(); + encoder.writeSpace(ATTRIB_SPACE, spc); + encoder.writeString(ATTRIB_VAL, ""); // Empty string + encoder.writeString(ATTRIB_VALUE, "hello"); + encoder.writeString(ATTRIB_CONSTRUCTOR, "<<\u20ac>>&\"bl a h\'\\bleh\n\t"); + String longString = + "one to three four five six seven eight nine ten eleven twelve thirteen " + + "fourteen fifteen sixteen seventeen eighteen nineteen twenty twenty one " + + "blahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblah"; + encoder.writeString(ATTRIB_DESTRUCTOR, longString); + encoder.closeElement(ELEM_DATA); + ByteArrayOutputStream outStream = new ByteArrayOutputStream(); + encoder.writeTo(outStream); + ByteArrayInputStream inStream = new ByteArrayInputStream(outStream.toByteArray()); + decoder.open(1 << 20, "testAttributes"); + decoder.ingestStream(inStream); + decoder.endIngest(); + int el = decoder.openElement(ELEM_DATA); + boolean bval = decoder.readBool(ATTRIB_ALIGN); + assertTrue(bval); + bval = decoder.readBool(ATTRIB_BIGENDIAN); + assertTrue(!bval); + spc = decoder.readSpace(ATTRIB_SPACE); + assertEquals(spc, addrFactory.getDefaultAddressSpace()); + String val = decoder.readString(ATTRIB_VAL); + assertEquals(val, ""); + val = decoder.readString(ATTRIB_VALUE); + assertEquals(val, "hello"); + val = decoder.readString(ATTRIB_CONSTRUCTOR); + assertEquals(val, "<<\u20ac>>&\"bl a h\'\\bleh\n\t"); + val = decoder.readString(ATTRIB_DESTRUCTOR); + assertEquals(val, longString); + decoder.closeElement(el); + } + + private void testHierarchy(Encoder encoder, Decoder decoder) + throws IOException, DecoderException + + { + encoder.openElement(ELEM_DATA); // el1 + encoder.writeBool(ATTRIB_CONTENT, true); + encoder.openElement(ELEM_INPUT); // el2 + encoder.openElement(ELEM_OUTPUT); // el3 + encoder.writeSignedInteger(ATTRIB_ID, 0x1000); + encoder.openElement(ELEM_DATA); // el4 + encoder.openElement(ELEM_DATA); // el5 + encoder.openElement(ELEM_OFF); // el6 + encoder.closeElement(ELEM_OFF); + encoder.openElement(ELEM_OFF); // el6 + encoder.writeString(ATTRIB_ID, "blahblah"); + encoder.closeElement(ELEM_OFF); + encoder.openElement(ELEM_OFF); // el6 + encoder.closeElement(ELEM_OFF); + encoder.closeElement(ELEM_DATA); // close el5 + encoder.closeElement(ELEM_DATA); // close el4 + encoder.openElement(ELEM_SYMBOL); // skip4 + encoder.writeUnsignedInteger(ATTRIB_ID, 17); + encoder.openElement(ELEM_TARGET); // skip5 + encoder.closeElement(ELEM_TARGET); // close skip5 + encoder.closeElement(ELEM_SYMBOL); // close skip4 + encoder.closeElement(ELEM_OUTPUT); // close el3 + encoder.closeElement(ELEM_INPUT); // close el2 + encoder.openElement(ELEM_INPUT); // el2 + encoder.closeElement(ELEM_INPUT); + encoder.openElement(ELEM_INPUT); // el2 + encoder.closeElement(ELEM_INPUT); + encoder.openElement(ELEM_INPUT); // el2 + encoder.closeElement(ELEM_INPUT); + encoder.openElement(ELEM_INPUT); // el2 + encoder.closeElement(ELEM_INPUT); + encoder.openElement(ELEM_INPUT); // el2 + encoder.closeElement(ELEM_INPUT); + encoder.openElement(ELEM_INPUT); // el2 + encoder.closeElement(ELEM_INPUT); + encoder.closeElement(ELEM_DATA); // close el1 + ByteArrayOutputStream outStream = new ByteArrayOutputStream(); + encoder.writeTo(outStream); + ByteArrayInputStream inStream = new ByteArrayInputStream(outStream.toByteArray()); + decoder.open(1 << 20, "testHierarchy"); + decoder.ingestStream(inStream); + decoder.endIngest(); + int el1 = decoder.openElement(ELEM_DATA); + // Skip over the bool + int el2 = decoder.openElement(ELEM_INPUT); + int el3 = decoder.openElement(ELEM_OUTPUT); + int val = (int) decoder.readSignedInteger(ATTRIB_ID); + assertEquals(val, 0x1000); + int el4 = decoder.peekElement(); + assertEquals(el4, ELEM_DATA.id()); + decoder.openElement(); + int el5 = decoder.openElement(); + assertEquals(el5, ELEM_DATA.id()); + int el6 = decoder.openElement(ELEM_OFF); + decoder.closeElement(el6); + el6 = decoder.openElement(ELEM_OFF); + decoder.closeElement(el6); + el6 = decoder.openElement(ELEM_OFF); + decoder.closeElement(el6); + decoder.closeElement(el5); + decoder.closeElement(el4); + decoder.closeElementSkipping(el3); + decoder.closeElement(el2); + el2 = decoder.openElement(ELEM_INPUT); + decoder.closeElement(el2); + el2 = decoder.openElement(ELEM_INPUT); + decoder.closeElement(el2); + decoder.closeElementSkipping(el1); + } + + private void testUnexpectedEof(Encoder encoder, Decoder decoder) throws IOException + + { + encoder.openElement(ELEM_DATA); + encoder.openElement(ELEM_INPUT); + encoder.writeString(ATTRIB_NAME, "hello"); + encoder.closeElement(ELEM_INPUT); + boolean sawUnexpectedError = false; + try { + ByteArrayOutputStream outStream = new ByteArrayOutputStream(); + encoder.writeTo(outStream); + ByteArrayInputStream inStream = new ByteArrayInputStream(outStream.toByteArray()); + decoder.open(1 << 20, "testAttributes"); + decoder.ingestStream(inStream); + decoder.endIngest(); + int el1 = decoder.openElement(ELEM_DATA); + int el2 = decoder.openElement(ELEM_INPUT); + decoder.closeElement(el2); + decoder.closeElement(el1); + } + catch (DecoderException err) { + sawUnexpectedError = true; + } + assertTrue(sawUnexpectedError); + } + + public void testNoremaining(Encoder encoder, Decoder decoder) + throws IOException, DecoderException + + { + encoder.openElement(ELEM_INPUT); + encoder.openElement(ELEM_OFF); + encoder.closeElement(ELEM_OFF); + encoder.closeElement(ELEM_INPUT); + ByteArrayOutputStream outStream = new ByteArrayOutputStream(); + encoder.writeTo(outStream); + ByteArrayInputStream inStream = new ByteArrayInputStream(outStream.toByteArray()); + decoder.open(1 << 20, "testNoremaining"); + decoder.ingestStream(inStream); + decoder.endIngest(); + decoder.openElement(ELEM_INPUT); + int el2 = decoder.openElement(ELEM_OFF); + decoder.closeElement(el2); + boolean sawNoRemaining = false; + try { + el2 = decoder.openElement(ELEM_OFF); + } + catch (DecoderException err) { + sawNoRemaining = true; + } + assertTrue(sawNoRemaining); + } + + private void testOpenmismatch(Encoder encoder, Decoder decoder) + throws IOException, DecoderException + + { + encoder.openElement(ELEM_INPUT); + encoder.openElement(ELEM_OFF); + encoder.closeElement(ELEM_OFF); + encoder.closeElement(ELEM_INPUT); + ByteArrayOutputStream outStream = new ByteArrayOutputStream(); + encoder.writeTo(outStream); + ByteArrayInputStream inStream = new ByteArrayInputStream(outStream.toByteArray()); + decoder.open(1 << 20, "testOpenmismatch"); + decoder.ingestStream(inStream); + decoder.endIngest(); + decoder.openElement(ELEM_INPUT); + boolean sawOpenMismatch = false; + try { + decoder.openElement(ELEM_OUTPUT); + } + catch (DecoderException err) { + sawOpenMismatch = true; + } + assertTrue(sawOpenMismatch); + } + + private void testClosemismatch(Encoder encoder, Decoder decoder) + throws IOException, DecoderException + + { + encoder.openElement(ELEM_INPUT); + encoder.openElement(ELEM_OFF); + encoder.closeElement(ELEM_OFF); + encoder.closeElement(ELEM_INPUT); + ByteArrayOutputStream outStream = new ByteArrayOutputStream(); + encoder.writeTo(outStream); + ByteArrayInputStream inStream = new ByteArrayInputStream(outStream.toByteArray()); + decoder.open(1 << 20, "testClosemismatch"); + decoder.ingestStream(inStream); + decoder.endIngest(); + int el1 = decoder.openElement(ELEM_INPUT); + boolean sawCloseMismatch = false; + try { + decoder.closeElement(el1); + } + catch (DecoderException err) { + sawCloseMismatch = true; + } + assertTrue(sawCloseMismatch); + } + + @Before + public void setUp() { + AddressSpace spaces[] = new AddressSpace[4]; + spaces[0] = new GenericAddressSpace("ram", 32, AddressSpace.TYPE_RAM, 2); + spaces[1] = new GenericAddressSpace("register", 32, AddressSpace.TYPE_REGISTER, 3); + spaces[2] = new GenericAddressSpace("const", 32, AddressSpace.TYPE_CONSTANT, 0); + spaces[3] = new GenericAddressSpace("unique", 32, AddressSpace.TYPE_UNIQUE, 1); + + addrFactory = new DefaultAddressFactory(spaces); + } + + @Test + public void testMarshalSignedPacked() throws DecoderException, IOException { + PackedEncode encoder = new PackedEncode(); + encoder.clear(); + PackedDecode decoder = new PackedDecode(addrFactory); + testSignedAttributes(encoder, decoder); + } + + @Test + public void marshalUnsignedPacked() throws DecoderException, IOException { + PackedEncode encoder = new PackedEncode(); + encoder.clear(); + PackedDecode decoder = new PackedDecode(addrFactory); + testUnsignedAttributes(encoder, decoder); + } + + @Test + public void marshalAttribsPacked() throws DecoderException, IOException { + PackedEncode encoder = new PackedEncode(); + encoder.clear(); + PackedDecode decoder = new PackedDecode(addrFactory); + testAttributes(encoder, decoder); + } + + @Test + public void marshalHierarchyPacked() throws DecoderException, IOException { + PackedEncode encoder = new PackedEncode(); + encoder.clear(); + PackedDecode decoder = new PackedDecode(addrFactory); + testHierarchy(encoder, decoder); + } + + @Test + public void marshalUnexpectedPacked() throws IOException { + PackedEncode encoder = new PackedEncode(); + encoder.clear(); + PackedDecode decoder = new PackedDecode(addrFactory); + testUnexpectedEof(encoder, decoder); + } + + @Test + public void marshalNoremainingPacked() throws IOException, DecoderException { + PackedEncode encoder = new PackedEncode(); + encoder.clear(); + PackedDecode decoder = new PackedDecode(addrFactory); + testNoremaining(encoder, decoder); + } + + @Test + public void marshalOpenmismatchPacked() throws IOException, DecoderException { + PackedEncode encoder = new PackedEncode(); + encoder.clear(); + PackedDecode decoder = new PackedDecode(addrFactory); + testOpenmismatch(encoder, decoder); + } + + @Test + public void marshalClosemismatchPacked() throws IOException, DecoderException { + PackedEncode encoder = new PackedEncode(); + encoder.clear(); + PackedDecode decoder = new PackedDecode(addrFactory); + testClosemismatch(encoder, decoder); + } + + @Test + public void marshalBufferpad() throws IOException, DecoderException { + assertEquals(LinkedByteBuffer.BUFFER_SIZE, 1024); + PackedEncode encoder = new PackedEncode(); + encoder.clear(); + encoder.openElement(ELEM_INPUT); // 1-byte + for (int i = 0; i < 511; ++i) { + encoder.writeBool(ATTRIB_CONTENT, (i & 1) == 0); + } + encoder.closeElement(ELEM_INPUT); + ByteArrayOutputStream outStream = new ByteArrayOutputStream(); + encoder.writeTo(outStream); + byte[] bytesOut = outStream.toByteArray(); + assertEquals(bytesOut.length, 1024); // Encoding should exactly fill one buffer + ByteArrayInputStream inStream = new ByteArrayInputStream(bytesOut); + PackedDecode decoder = new PackedDecode(addrFactory); + decoder.open(1 << 20, "marshalBufferpad"); + decoder.ingestStream(inStream); + decoder.endIngest(); + int el = decoder.openElement(ELEM_INPUT); + for (int i = 0; i < 511; ++i) { + int attribId = decoder.getNextAttributeId(); + assertEquals(attribId, ATTRIB_CONTENT.id()); + boolean val = decoder.readBool(); + assertEquals(val, (i & 1) == 0); + } + int nextel = decoder.peekElement(); + assertEquals(nextel, 0); + decoder.closeElement(el); + } + +}