diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/fspec.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/fspec.cc index 19ed41c636..c52d92c800 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/fspec.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/fspec.cc @@ -2317,14 +2317,7 @@ void ProtoModel::decode(Decoder &decoder) if (attribId == ATTRIB_NAME) name = decoder.readString(); else if (attribId == ATTRIB_EXTRAPOP) { - string extrapopString = decoder.readString(); - if (extrapopString == "unknown") - extrapop = extrapop_unknown; - else { - istringstream s(extrapopString); - s.unsetf(ios::dec | ios::hex | ios::oct); - s >> extrapop; - } + extrapop = decoder.readSignedIntegerExpectString("unknown", extrapop_unknown); } else if (attribId == ATTRIB_STACKSHIFT) { // Allow this attribute for backward compatibility @@ -4409,11 +4402,7 @@ void FuncProto::decode(Decoder &decoder,Architecture *glb) } else if (attribId == ATTRIB_EXTRAPOP) { seenextrapop = true; - try { - readextrapop = decoder.readSignedInteger(); - } catch(DecoderError &err) { - readextrapop = ProtoModel::extrapop_unknown; - } + readextrapop = decoder.readSignedIntegerExpectString("unknown", ProtoModel::extrapop_unknown); } else if (attribId == ATTRIB_MODELLOCK) { if (decoder.readBool()) diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/marshal.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/marshal.cc index cd1e7acdec..d9a63a0470 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/marshal.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/marshal.cc @@ -295,6 +295,33 @@ intb XmlDecode::readSignedInteger(const AttributeId &attribId) return res; } +intb XmlDecode::readSignedIntegerExpectString(const string &expect,intb expectval) + +{ + const Element *el = elStack.back(); + const string &value( el->getAttributeValue(attributeIndex) ); + if (value == expect) + return expectval; + istringstream s2(value); + s2.unsetf(ios::dec | ios::hex | ios::oct); + intb res = 0; + s2 >> res; + return res; +} + +intb XmlDecode::readSignedIntegerExpectString(const AttributeId &attribId,const string &expect,intb expectval) + +{ + string value = readString(attribId); + if (value == expect) + return expectval; + istringstream s2(value); + s2.unsetf(ios::dec | ios::hex | ios::oct); + intb res = 0; + s2 >> res; + return res; +} + uintb XmlDecode::readUnsignedInteger(void) { @@ -691,9 +718,9 @@ bool PackedDecode::readBool(void) if ((header1 & HEADEREXTEND_MASK)!=0) getNextByte(curPos); uint1 typeByte = getNextByte(curPos); + attributeRead = true; if ((typeByte >> TYPECODE_SHIFT) != TYPECODE_BOOLEAN) throw DecoderError("Expecting boolean attribute"); - attributeRead = true; return ((typeByte & LENGTHCODE_MASK) != 0); } @@ -724,6 +751,7 @@ intb PackedDecode::readSignedInteger(void) } else { skipAttributeRemaining(typeByte); + attributeRead = true; throw DecoderError("Expecting signed integer attribute"); } attributeRead = true; @@ -739,6 +767,40 @@ intb PackedDecode::readSignedInteger(const AttributeId &attribId) return res; } +intb PackedDecode::readSignedIntegerExpectString(const string &expect,intb expectval) + +{ + intb res; + Position tmpPos = curPos; + uint1 header1 = getNextByte(tmpPos); + if ((header1 & HEADEREXTEND_MASK)!=0) + getNextByte(tmpPos); + uint1 typeByte = getNextByte(tmpPos); + uint4 typeCode = typeByte >> TYPECODE_SHIFT; + if (typeCode == TYPECODE_STRING) { + string val = readString(); + if (val != expect) { + ostringstream s; + s << "Expecting string \"" << expect << "\" but read \"" << val << "\""; + throw DecoderError(s.str()); + } + res = expectval; + } + else { + res = readSignedInteger(); + } + return res; +} + +intb PackedDecode::readSignedIntegerExpectString(const AttributeId &attribId,const string &expect,intb expectval) + +{ + findMatchingAttribute(attribId); + intb res = readSignedIntegerExpectString(expect,expectval); + curPos = startPos; + return res; +} + uintb PackedDecode::readUnsignedInteger(void) { @@ -753,6 +815,7 @@ uintb PackedDecode::readUnsignedInteger(void) } else { skipAttributeRemaining(typeByte); + attributeRead = true; throw DecoderError("Expecting unsigned integer attribute"); } attributeRead = true; @@ -778,6 +841,7 @@ string PackedDecode::readString(void) uint4 typeCode = typeByte >> TYPECODE_SHIFT; if (typeCode != TYPECODE_STRING) { skipAttributeRemaining(typeByte); + attributeRead = true; throw DecoderError("Expecting string attribute"); } int4 length = readLengthCode(typeByte); @@ -842,6 +906,7 @@ AddrSpace *PackedDecode::readSpace(void) } else { skipAttributeRemaining(typeByte); + attributeRead = true; throw DecoderError("Expecting space attribute"); } attributeRead = true; diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/marshal.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/marshal.hh index a6cf0fb260..98337529d5 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/marshal.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/marshal.hh @@ -188,6 +188,29 @@ public: /// \return the signed integer value virtual intb readSignedInteger(const AttributeId &attribId)=0; + /// \brief Parse the current attribute as either a signed integer value or a string. + /// + /// If the attribute is an integer, its value is returned. If the attribute is a string, it must match an + /// expected string passed to the method, and a predetermined integer value associated with the string is returned. + /// If the attribute neither matches the expected string nor is an integer, the return value is undefined. + /// \param expect is the string value to expect if the attribute is encoded as a string + /// \param expectval is the integer value to return if the attribute matches the expected string + /// \return the encoded integer or the integer value associated with the expected string + virtual intb readSignedIntegerExpectString(const string &expect,intb expectval)=0; + + /// \brief Find and parse a specific attribute in the current element as either a signed integer or a string. + /// + /// If the attribute is an integer, its value is parsed and returned. + /// If the attribute is encoded as a string, it must match an expected string passed to this method. + /// In this case, a predetermined integer value is passed back, indicating a matching string was parsed. + /// If the attribute neither matches the expected string nor is an integer, the return value is undefined. + /// If there is no attribute matching the id, an exception is thrown. + /// \param attribId is the specific attribute id to match + /// \param expect is the string to expect, if the attribute is not encoded as an integer + /// \param expectval is the integer value to return if the attribute matches the expected string + /// \return the encoded integer or the integer value associated with the expected string + virtual intb readSignedIntegerExpectString(const AttributeId &attribId,const string &expect,intb expectval)=0; + /// \brief 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. @@ -337,6 +360,8 @@ public: virtual bool readBool(const AttributeId &attribId); virtual intb readSignedInteger(void); virtual intb readSignedInteger(const AttributeId &attribId); + virtual intb readSignedIntegerExpectString(const string &expect,intb expectval); + virtual intb readSignedIntegerExpectString(const AttributeId &attribId,const string &expect,intb expectval); virtual uintb readUnsignedInteger(void); virtual uintb readUnsignedInteger(const AttributeId &attribId); virtual string readString(void); @@ -470,6 +495,8 @@ public: virtual bool readBool(const AttributeId &attribId); virtual intb readSignedInteger(void); virtual intb readSignedInteger(const AttributeId &attribId); + virtual intb readSignedIntegerExpectString(const string &expect,intb expectval); + virtual intb readSignedIntegerExpectString(const AttributeId &attribId,const string &expect,intb expectval); virtual uintb readUnsignedInteger(void); virtual uintb readUnsignedInteger(const AttributeId &attribId); virtual string readString(void); diff --git a/Ghidra/Features/Decompiler/src/decompile/unittests/testmarshal.cc b/Ghidra/Features/Decompiler/src/decompile/unittests/testmarshal.cc index b941fede36..26c7d62d76 100644 --- a/Ghidra/Features/Decompiler/src/decompile/unittests/testmarshal.cc +++ b/Ghidra/Features/Decompiler/src/decompile/unittests/testmarshal.cc @@ -210,6 +210,50 @@ TEST(marshal_unsigned_xml) { test_unsigned_attributes(outStream, encoder, decoder); } +void test_mixed_attributes(ostringstream &outStream,Encoder &encoder,Decoder &decoder) + +{ + encoder.openElement(ELEM_ADDR); + encoder.writeSignedInteger(ATTRIB_ALIGN, 456); + encoder.writeString(ATTRIB_EXTRAPOP, "unknown"); + encoder.closeElement(ELEM_ADDR); + istringstream inStream(outStream.str()); + decoder.ingestStream(inStream); + int4 alignVal = -1; + int4 extrapopVal = -1; + uint4 el = decoder.openElement(ELEM_ADDR); + for(;;) { + uint4 attribId = decoder.getNextAttributeId(); + if (attribId == 0) break; + if (attribId == ATTRIB_ALIGN) + alignVal = decoder.readSignedIntegerExpectString("00blah", 700); + else if (attribId == ATTRIB_EXTRAPOP) + extrapopVal = decoder.readSignedIntegerExpectString("unknown", 800); + } + decoder.closeElement(el); + ASSERT_EQUALS(alignVal, 456); + ASSERT_EQUALS(extrapopVal, 800); +} + +TEST(marshal_mixed_packed) { + ostringstream outStream; + + theEnviron.build(); + PackedEncode encoder(outStream); + PackedDecode decoder(spcManager); + test_mixed_attributes(outStream, encoder, decoder); +} + +TEST(marshal_mixed_xml) { + ostringstream outStream; + + theEnviron.build(); + XmlEncode encoder(outStream); + XmlDecode decoder(spcManager); + test_mixed_attributes(outStream, encoder, decoder); +} + + void test_attributes(ostringstream &outStream,Encoder &encoder,Decoder &decoder) { 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 8fd9922137..dffd2e2cc1 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 @@ -141,6 +141,38 @@ public interface Decoder extends ByteIngest { */ public long readSignedInteger(AttributeId attribId) throws DecoderException; + /** + * Parse the current attribute as either a signed integer value or a string. + * If the attribute is an integer, its value is returned. + * If the attribute is a string, it must match an expected string passed to the method, + * and a predetermined integer value associated with the string is returned. + * If the attribute string does not match, or the attribute is encoded as anything other than + * a string or signed integer, an exception is thrown. + * @param expect is the string value to expect if the attribute is encoded as a string + * @param expectval is the integer value to return if the attribute matches the expected string + * @return the encoded integer or the integer value associated with the expected string + * @throws DecoderException is an integer value or expected string cannot be parsed + */ + public long readSignedIntegerExpectString(String expect, long expectval) + throws DecoderException; + + /** + * Find and parse a specific attribute in the current element as either a signed integer + * or a string. If the attribute is an integer, its value is returned. + * If the attribute is encoded as a string, it must match an expected string + * passed to this method. In this case, a predetermined integer value is passed back, + * indicating a matching string was parsed. If the attribute string does not match, or + * the attribute is encoded as anything other than a string or signed integer, an exception + * is thrown. + * @param attribId is the specific attribute id to match + * @param expect is the string to expect, if the attribute is not encoded as an integer + * @param expectval is the integer value to return if the attribute matches the expected string + * @return the encoded integer or the integer value associated with the expected string + * @throws DecoderException if an integer value or expected string cannot be parsed + */ + public long readSignedIntegerExpectString(AttributeId attribId, String expect, long expectval) + throws DecoderException; + /** * Parse the current attribute as an unsigned integer value * The last attribute, as returned by getNextAttributeId, is treated as an unsigned integer, 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 9f3fbbcc10..64393e2dd4 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 @@ -427,12 +427,8 @@ public class FunctionPrototype { PrototypeModel protoModel = dtmanage.getProgram().getCompilerSpec().getCallingConvention(modelname); hasThis = (protoModel == null) ? false : protoModel.hasThisPointer(); - try { - extrapop = (int) decoder.readSignedInteger(ATTRIB_EXTRAPOP); - } - catch (DecoderException e) { - extrapop = PrototypeModel.UNKNOWN_EXTRAPOP; - } + extrapop = (int) decoder.readSignedIntegerExpectString(ATTRIB_EXTRAPOP, "unknown", + PrototypeModel.UNKNOWN_EXTRAPOP); modellock = false; dotdotdot = false; voidinputlock = false; 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 31e70b70fc..ff5c8ca9c0 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 @@ -32,7 +32,6 @@ import ghidra.program.model.symbol.SourceType; import ghidra.util.Msg; import ghidra.util.exception.DuplicateNameException; import ghidra.util.exception.InvalidInputException; -import ghidra.util.xml.SpecXmlUtils; /** * @@ -155,13 +154,8 @@ public class HighParamID extends PcodeSyntaxTree { else if (subel == ELEM_PROTO.id()) { decoder.openElement(); modelname = decoder.readString(ATTRIB_MODEL); - String val = decoder.readString(ATTRIB_EXTRAPOP); - if (val.equals("unknown")) { - protoextrapop = PrototypeModel.UNKNOWN_EXTRAPOP; - } - else { - protoextrapop = SpecXmlUtils.decodeInt(val); - } + protoextrapop = (int) decoder.readSignedIntegerExpectString(ATTRIB_EXTRAPOP, + "unknown", PrototypeModel.UNKNOWN_EXTRAPOP); decoder.closeElement(subel); } else if (subel == ELEM_INPUT.id()) { 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 index 4937330643..d2c110e636 100644 --- 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 @@ -370,6 +370,41 @@ public class PackedDecode implements Decoder { return res; } + @Override + public long readSignedIntegerExpectString(String expect, long expectval) + throws DecoderException { + long res; + LinkedByteBuffer.Position tmpPos = new LinkedByteBuffer.Position(); + tmpPos.copy(curPos); + byte header1 = tmpPos.getNextByte(); + if ((header1 & HEADEREXTEND_MASK) != 0) { + tmpPos.getNextByte(); + } + byte typeByte = tmpPos.getNextByte(); + int typeCode = typeByte >> TYPECODE_SHIFT; + if (typeCode == TYPECODE_STRING) { + String val = readString(); + if (!val.equals(expect)) { + throw new DecoderException( + "Expecting string \"" + expect + "\" but read \"" + val + "\""); + } + res = expectval; + } + else { + res = readSignedInteger(); + } + return res; + } + + @Override + public long readSignedIntegerExpectString(AttributeId attribId, String expect, long expectval) + throws DecoderException { + findMatchingAttribute(attribId); + long res = readSignedIntegerExpectString(expect, expectval); + curPos.copy(startPos); + return res; + } + @Override public long readUnsignedInteger() throws DecoderException { byte header1 = curPos.getNextByte(); 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 index d926a95926..955e81f4b8 100644 --- 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 @@ -155,6 +155,38 @@ public class EncodeDecodeTest extends AbstractGenericTest { decoder.closeElement(el); } + private void testMixedAttributes(Encoder encoder, Decoder decoder) + throws DecoderException, IOException { + encoder.openElement(ELEM_ADDR); + encoder.writeSignedInteger(ATTRIB_ALIGN, 456); + encoder.writeString(ATTRIB_EXTRAPOP, "unknown"); + encoder.closeElement(ELEM_ADDR); + ByteArrayOutputStream outStream = new ByteArrayOutputStream(); + encoder.writeTo(outStream); + ByteArrayInputStream inStream = new ByteArrayInputStream(outStream.toByteArray()); + decoder.open(1 << 20, "testMixedAttributes"); + decoder.ingestStream(inStream); + decoder.endIngest(); + int alignVal = -1; + int extrapopVal = -1; + int el = decoder.openElement(ELEM_ADDR); + for (;;) { + int attribId = decoder.getNextAttributeId(); + if (attribId == 0) { + break; + } + if (attribId == ATTRIB_ALIGN.id()) { + alignVal = (int) decoder.readSignedIntegerExpectString("00blah", 700); + } + else if (attribId == ATTRIB_EXTRAPOP.id()) { + extrapopVal = (int) decoder.readSignedIntegerExpectString("unknown", 800); + } + } + decoder.closeElement(el); + assertEquals(alignVal, 456); + assertEquals(extrapopVal, 800); + } + private void testAttributes(Encoder encoder, Decoder decoder) throws DecoderException, IOException @@ -401,6 +433,14 @@ public class EncodeDecodeTest extends AbstractGenericTest { testUnsignedAttributes(encoder, decoder); } + @Test + public void marshalMixedPacked() throws DecoderException, IOException { + PackedEncode encoder = new PackedEncode(); + encoder.clear(); + PackedDecode decoder = new PackedDecode(addrFactory); + testMixedAttributes(encoder, decoder); + } + @Test public void marshalAttribsPacked() throws DecoderException, IOException { PackedEncode encoder = new PackedEncode();