GP-2358 Packed protocol for decompiler marshaling

This commit is contained in:
caheckman 2022-07-26 15:36:05 -04:00
parent 6a1a649213
commit 79c3508f54
119 changed files with 4238 additions and 2207 deletions

View file

@ -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 {
* <li>last pcode op has fall-through</li>
* <li>internal label used to branch beyond last pcode op</li>
* </ul>
* @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) {

View file

@ -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> 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);
}
}

View file

@ -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

View file

@ -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 \<addr> 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);
}
}

View file

@ -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);

View file

@ -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);

View file

@ -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

View file

@ -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

View file

@ -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 \<addr> 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]));
}

View file

@ -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<String, AttributeId> lookupAttributeId = new HashMap<>();
// private static HashMap<String, AttributeId> 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);

View file

@ -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 {

View file

@ -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);
}

View file

@ -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);

View file

@ -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<PcodeBlock> 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();

View file

@ -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);

View file

@ -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();

View file

@ -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();
}

View file

@ -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);
}

View file

@ -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);
}
}

View file

@ -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);

View file

@ -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<String, ElementId> lookupElementId = new HashMap<>();
// private static HashMap<String, ElementId> 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);
}

View file

@ -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();
}

View file

@ -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;

View file

@ -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 <prototype>} 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;

View file

@ -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;
}

View file

@ -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 (;;) {

View file

@ -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);
}
}

View file

@ -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());
}
}
}

View file

@ -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();

View file

@ -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;

View file

@ -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();

View file

@ -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<ParamMeasure> pmlist)
throws PcodeXMLException {
throws DecoderException {
int el = decoder.openElement();
ParamMeasure pm = new ParamMeasure();
pm.decode(decoder, this);

View file

@ -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();

View file

@ -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 <high>} 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;
}

View file

@ -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);

View file

@ -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;
}
}

View file

@ -265,9 +265,9 @@ public class LocalSymbolMap {
* Decode a &lt;mapsym&gt; 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 &lt;symbollist&gt; and &lt;mapsym&gt; 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<HighSymbol> parms = new ArrayList<>();
while (decoder.peekElement() != 0) {

View file

@ -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());
}

View file

@ -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);

View file

@ -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);

View file

@ -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<AddressSpace> 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<Integer> 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 <addr> 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;
}
}

View file

@ -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;
}
}

View file

@ -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);

View file

@ -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);
}

View file

@ -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<? extends PcodeBlock> 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<? extends PcodeBlock> 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);

View file

@ -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();

View file

@ -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 <type> element");
throw new DecoderException("Expecting <type> 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;

View file

@ -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);

View file

@ -420,21 +420,22 @@ public class PcodeOp {
}
/**
* Encode this PcodeOp to a stream as an \<op> element
* Encode just the opcode and input/output Varnode data for this PcodeOp to a stream
* as an \<op> 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;

View file

@ -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<Varnode> 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();

View file

@ -150,9 +150,9 @@ public class SequenceNumber implements Comparable<SequenceNumber> {
* @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 (;;) {

View file

@ -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);
}
}

View file

@ -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

View file

@ -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;
}

View file

@ -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<Map.Entry<String, String>> 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<String, String> 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);
}
}

View file

@ -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;
}
}

View file

@ -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();
}
}

View file

@ -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);
}
}