Refactor of HighSymbol to use SymbolEntry

This commit is contained in:
caheckman 2019-12-16 18:08:09 -05:00
parent 7fa8245f90
commit 97b43b9470
22 changed files with 1422 additions and 679 deletions

View file

@ -0,0 +1,127 @@
/* ###
* 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 ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.listing.Program;
import ghidra.program.model.listing.VariableStorage;
import ghidra.util.exception.AssertException;
import ghidra.util.exception.InvalidInputException;
import ghidra.util.xml.SpecXmlUtils;
import ghidra.xml.XmlElement;
import ghidra.xml.XmlPullParser;
/**
* A HighSymbol mapping based on local hashing of the symbol's Varnode within a
* function's syntax tree. The storage address of a temporary Varnode (a Varnode in
* the "unique" address space) is too ephemeral to use as a permanent way to identify it.
* This symbol stores a hash (generated by DynamicHash) that is better suited to
* identifying the Varnode.
*/
public class DynamicEntry extends SymbolEntry {
private long hash; // Hash encoding the specific Varnode
/**
* Constructor for use with restoreXML
* @param sym is the owning HighSymbol
*/
public DynamicEntry(HighSymbol sym) {
super(sym);
}
/**
* Construct given the underlying symbol, defining Address of the Varnode, and the hash value
* @param sym is the given symbol
* @param addr is the defining Address
* @param h is the hash value
*/
public DynamicEntry(HighSymbol sym, Address addr, long h) {
super(sym);
pcaddr = addr;
hash = h;
}
/**
* @return the hash value
*/
public long getHash() {
return hash;
}
@Override
public void restoreXML(XmlPullParser parser) throws PcodeXMLException {
XmlElement addrel = parser.start("hash");
hash = SpecXmlUtils.decodeLong(addrel.getAttribute("val"));
parser.end(addrel);
parseRangeList(parser);
}
@Override
public void saveXml(StringBuilder buf) {
buf.append("<hash val=\"0x").append(Long.toHexString(hash)).append("\"/>");
buildRangelistXML(buf);
}
@Override
public VariableStorage getStorage() {
Program program = symbol.getProgram();
try {
return new VariableStorage(program, AddressSpace.HASH_SPACE.getAddress(getHash()),
getSize());
}
catch (InvalidInputException e) {
throw new AssertException("Unexpected exception", e);
}
}
/**
* Build the dynamic storage object for a new DynamicEntry, given the underlying temporary
* Varnode and its function model. The hash is created from local information in the
* syntax tree near the Varnode.
* @param vn is the underlying Varnode
* @param high is the function model
* @return the dynamic storage object
*/
public static VariableStorage buildDynamicStorage(Varnode vn, HighFunction high) {
DynamicHash dynamicHash = new DynamicHash(vn, high);
Program program = high.getFunction().getProgram();
long ourHash = dynamicHash.getHash();
try {
return new VariableStorage(program, AddressSpace.HASH_SPACE.getAddress(ourHash),
vn.getSize());
}
catch (InvalidInputException e) {
throw new AssertException("Unexpected exception", e);
}
}
@Override
public int getSize() {
return symbol.type.getLength();
}
@Override
public boolean isReadOnly() {
return false;
}
@Override
public boolean isVolatile() {
return false;
}
}

View file

@ -1,142 +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 ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.data.DataType;
import ghidra.program.model.listing.Program;
import ghidra.program.model.listing.VariableStorage;
import ghidra.util.exception.AssertException;
import ghidra.util.exception.InvalidInputException;
import ghidra.util.xml.SpecXmlUtils;
import ghidra.xml.XmlElement;
import ghidra.xml.XmlPullParser;
/**
* Decompiler symbol whose references are encoded as dynamic hashes into the PcodeSyntaxTree
*
*/
public class DynamicSymbol extends HighSymbol {
protected long hash; // Hash encoding the specific Varnode
public DynamicSymbol(HighFunction func) { // For use with restoreXML
super(func);
}
public DynamicSymbol(long uniqueId, String nm, DataType tp, int size, HighFunction func,
Address addr, long hash) {
super(uniqueId, nm, tp, size, addr, func);
this.hash = hash;
}
public long getHash() {
return hash;
}
protected void buildHashXML(StringBuilder buf) {
buf.append("<hash val=\"0x").append(Long.toHexString(hash)).append("\"/>");
buildRangelistXML(buf, pcaddr);
}
@Override
public String buildXML() {
String sym = buildSymbolXML(function.getDataTypeManager(), name, type, size,
isTypeLocked(), isNameLocked(), isReadOnly(), false, 0);
StringBuilder res = new StringBuilder();
res.append("<mapsym type=\"dynamic\">\n");
res.append(sym);
buildHashXML(res);
res.append("</mapsym>\n");
return res.toString();
}
@Override
public void restoreXML(XmlPullParser parser) throws PcodeXMLException {
XmlElement symel = parser.start("symbol");
restoreSymbolXML(symel);
type = function.getDataTypeManager().readXMLDataType(parser);
size = type.getLength();
parser.end(symel);
if (size == 0) {
throw new PcodeXMLException("Invalid symbol 0-sized data-type: " + type.getName());
}
restoreEntryXML(parser);
while(parser.peek().isStart()) {
parser.discardSubTree();
}
}
@Override
protected void restoreEntryXML(XmlPullParser parser) throws PcodeXMLException {
XmlElement addrel = parser.start("hash");
hash = SpecXmlUtils.decodeLong(addrel.getAttribute("val"));
parser.end(addrel);
pcaddr = parseRangeList(parser);
}
@Override
public VariableStorage getStorage() {
Program program = function.getFunction().getProgram();
try {
return new VariableStorage(program, AddressSpace.HASH_SPACE.getAddress(getHash()),
getSize());
}
catch (InvalidInputException e) {
throw new AssertException("Unexpected exception", e);
}
}
public static String buildSymbolXML(PcodeDataTypeManager dtmanage, String nm,
DataType dt, int length, boolean tl, boolean nl, boolean ro, boolean isVolatile,
int format) {
StringBuilder res = new StringBuilder();
res.append("<symbol");
if (nm != null) {
SpecXmlUtils.xmlEscapeAttribute(res, "name", nm);
}
SpecXmlUtils.encodeBooleanAttribute(res, "typelock", tl);
SpecXmlUtils.encodeBooleanAttribute(res, "namelock", nl);
SpecXmlUtils.encodeBooleanAttribute(res, "readonly", ro);
if (isVolatile) {
SpecXmlUtils.encodeBooleanAttribute(res, "volatile", true);
}
res.append(">\n");
res.append(dtmanage.buildTypeRef(dt, length));
res.append("</symbol>\n");
return res.toString();
}
/**
* Build dynamic VariableStorage for a unique variable
* @param vn is the variable in the unique space
* @param high is the HighFunction containing the variable
* @return the dynamic VariableStorage
*/
public static VariableStorage buildDynamicStorage(Varnode vn, HighFunction high) {
DynamicHash dynamicHash = new DynamicHash(vn, high);
Program program = high.getFunction().getProgram();
long ourHash = dynamicHash.getHash();
try {
return new VariableStorage(program, AddressSpace.HASH_SPACE.getAddress(ourHash),
vn.getSize());
}
catch (InvalidInputException e) {
throw new AssertException("Unexpected exception", e);
}
}
}

View file

@ -21,7 +21,7 @@ import ghidra.util.xml.SpecXmlUtils;
import ghidra.xml.XmlElement;
import ghidra.xml.XmlPullParser;
public class EquateSymbol extends DynamicSymbol {
public class EquateSymbol extends HighSymbol {
public static final int FORMAT_DEFAULT = 0;
public static final int FORMAT_HEX = 1;
@ -39,16 +39,22 @@ public class EquateSymbol extends DynamicSymbol {
public EquateSymbol(long uniqueId, String nm, long val, HighFunction func, Address addr,
long hash) {
super(uniqueId, nm, DataType.DEFAULT, 1, func, addr, hash);
super(uniqueId, nm, DataType.DEFAULT, func);
category = 1;
value = val;
convert = FORMAT_DEFAULT;
DynamicEntry entry = new DynamicEntry(this, addr, hash);
addMapEntry(entry);
}
public EquateSymbol(long uniqueId, int conv, long val, HighFunction func, Address addr,
long hash) {
super(uniqueId, "", DataType.DEFAULT, 1, func, addr, hash);
super(uniqueId, "", DataType.DEFAULT, func);
category = 1;
value = val;
convert = conv;
DynamicEntry entry = new DynamicEntry(this, addr, hash);
addMapEntry(entry);
}
public long getValue() { return value; }
@ -56,9 +62,8 @@ public class EquateSymbol extends DynamicSymbol {
@Override
public void restoreXML(XmlPullParser parser) throws PcodeXMLException {
XmlElement symel = parser.start("equatesymbol");
restoreSymbolXML(symel);
restoreXMLHeader(symel);
type = DataType.DEFAULT;
size = 1;
convert = FORMAT_DEFAULT;
String formString = symel.getAttribute("format");
if (formString != null) {
@ -81,40 +86,12 @@ public class EquateSymbol extends DynamicSymbol {
parser.start("value");
value = SpecXmlUtils.decodeLong(parser.end().getText()); // End <value> tag
parser.end(symel);
if (size == 0) {
throw new PcodeXMLException("Invalid symbol 0-sized data-type: " + type.getName());
}
restoreEntryXML(parser);
while(parser.peek().isStart()) {
parser.discardSubTree();
}
}
@Override
public String buildXML() {
String sym = buildSymbolXML(function.getDataTypeManager(), name, value, isNameLocked(), false, convert);
StringBuilder res = new StringBuilder();
res.append("<mapsym type=\"equate\">\n");
res.append(sym);
buildHashXML(res);
res.append("</mapsym>\n");
return res.toString();
}
public static String buildSymbolXML(PcodeDataTypeManager dtmanage, String nm,long value,
boolean nl, boolean isVolatile,int convert) {
StringBuilder res = new StringBuilder();
res.append("<equatesymbol");
if (nm != null) {
SpecXmlUtils.xmlEscapeAttribute(res, "name", nm);
}
SpecXmlUtils.encodeBooleanAttribute(res, "typelock", true);
SpecXmlUtils.encodeBooleanAttribute(res, "namelock", nl);
SpecXmlUtils.encodeSignedIntegerAttribute(res, "cat", 1); // Specify category 1 for the equate
if (isVolatile) {
SpecXmlUtils.encodeBooleanAttribute(res, "volatile", true);
}
public void saveXML(StringBuilder buf) {
buf.append("<equatesymbol");
saveXMLHeader(buf);
if (convert != 0) {
String formString = "hex";
if (convert == FORMAT_HEX) {
@ -132,14 +109,13 @@ public class EquateSymbol extends DynamicSymbol {
else if (convert == FORMAT_CHAR) {
formString = "char";
}
SpecXmlUtils.encodeStringAttribute(res, "format", formString);
SpecXmlUtils.encodeStringAttribute(buf, "format", formString);
}
res.append(">\n");
res.append(" <value>0x");
res.append(Long.toHexString(value));
res.append("</value>\n");
res.append("</equatesymbol>\n");
return res.toString();
buf.append(">\n");
buf.append(" <value>0x");
buf.append(Long.toHexString(value));
buf.append("</value>\n");
buf.append("</equatesymbol>\n");
}
public static int convertName(String nm,long val) {

View file

@ -26,14 +26,26 @@ import ghidra.program.model.listing.Program;
import ghidra.program.model.symbol.Symbol;
import ghidra.program.model.symbol.SymbolTable;
/**
* A container for global symbols in the decompiler's model of a function. It contains
* HighSymbol objects for any symbol accessed by the particular function that is in either
* the global scope or some other global namespace. Currently the container is populated
* indirectly from the HighGlobal objects marshaled back from the decompiler, using either
* the populateSymbol() or newSymbol() methods. HighSymbols are stored by Address and by id,
* which matches the formal SymbolDB id when it exists.
*/
public class GlobalSymbolMap {
private Program program;
private HighFunction func;
private SymbolTable symbolTable;
private HashMap<Address, HighCodeSymbol> addrMappedSymbols; // Hashed by addr
private HashMap<Long, HighCodeSymbol> symbolMap; // Hashed by unique key
private HighFunction func; // Is the full decompiler model of the function to which this belongs
private SymbolTable symbolTable; // Used for cross-referencing with Ghidra's database backed Symbols
private HashMap<Address, HighCodeSymbol> addrMappedSymbols; // Look up symbols by address
private HashMap<Long, HighCodeSymbol> symbolMap; // Look up symbol by id
private long uniqueSymbolId; // Next available symbol id
/**
* Construct a global symbol map attached to a particular function model.
* @param f is the decompiler function model
*/
public GlobalSymbolMap(HighFunction f) {
program = f.getFunction().getProgram();
func = f;
@ -108,14 +120,28 @@ public class GlobalSymbolMap {
return symbol;
}
/**
* Retrieve a HighSymbol based on an id
* @param id is the id
* @return the matching HighSymbol or null
*/
public HighCodeSymbol getSymbol(long id) {
return symbolMap.get(id);
}
/**
* Retrieve a HighSymbol based on an Address
* @param addr is the given Address
* @return the matching HighSymbol or null
*/
public HighCodeSymbol getSymbol(Address addr) {
return addrMappedSymbols.get(addr);
}
/**
* Get an iterator over all HighSymbols in this container
* @return the iterator
*/
public Iterator<HighCodeSymbol> getSymbols() {
return symbolMap.values().iterator();
}

View file

@ -17,35 +17,110 @@ package ghidra.program.model.pcode;
import ghidra.program.database.symbol.CodeSymbol;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressFactory;
import ghidra.program.model.data.DataType;
import ghidra.program.model.listing.*;
import ghidra.program.model.symbol.Symbol;
import ghidra.program.model.symbol.SymbolUtilities;
import ghidra.util.exception.InvalidInputException;
import ghidra.xml.XmlElement;
import ghidra.xml.XmlPullParser;
/**
* Symbol returned by the decompiler that wraps a CodeSymbol
* A global symbol as part of the decompiler's model of a function. This symbol can
* be backed by a formal CodeSymbol, obtained using getCodeSymbol(). This symbol can be backed
* by a formal Data object, obtained using getData(). If there is a backing CodeSymbol, this takes its name,
* otherwise the name is dynamically generated using SymbolUtilities. The data-type attached to this does
* not necessarily match the backing CodeSymbol or Data object.
*/
public class HighCodeSymbol extends HighSymbol {
private CodeSymbol symbol;
private Data data;
private VariableStorage storage;
/**
* Construct with a backing CodeSymbol. An attempt is made to also find a backing Data object.
* @param sym is the backing CodeSymbol
* @param dataType is the (possibly distinct) data-type associated with the new HighSymbol
* @param sz is the storage size, in bytes, of the symbol
* @param func is the decompiler function model owning the new HighSymbol
*/
public HighCodeSymbol(CodeSymbol sym, DataType dataType, int sz, HighFunction func) {
super(sym.getID(), sym.getName(), dataType, sz, null, func);
super(sym.getID(), sym.getName(), dataType, func);
symbol = sym;
data = null;
setNameLock(true);
setTypeLock(true);
Data data = null;
Object dataObj = symbol.getObject();
if (dataObj instanceof Data) {
data = (Data) dataObj;
}
VariableStorage store;
try {
store = new VariableStorage(symbol.getProgram(), symbol.getAddress(), sz);
}
catch (InvalidInputException e) {
store = VariableStorage.UNASSIGNED_STORAGE;
}
SymbolEntry entry;
if (data != null) {
entry = new MappedDataEntry(this, store, data);
}
else {
entry = new MappedEntry(this, store, null);
}
addMapEntry(entry);
}
/**
* Construct with just a (global) storage address and size. There will be no backing CodeSymbol.
* An attempt is made to find a backing Data object.
* @param id is the id to associate with the new HighSymbol
* @param addr is the starting Address of the symbol storage
* @param dataType is the data-type associated with the new symbol
* @param sz is the size of the symbol storage in bytes
* @param func is the decompiler function model owning the new symbol
*/
public HighCodeSymbol(long id, Address addr, DataType dataType, int sz, HighFunction func) {
super(id, SymbolUtilities.getDynamicName(func.getFunction().getProgram(), addr), dataType,
sz, null, func);
func);
symbol = null;
data = func.getFunction().getProgram().getListing().getDataAt(addr);
setNameLock(true);
setTypeLock(true);
Program program = func.getFunction().getProgram();
Data data = program.getListing().getDataAt(addr);
VariableStorage store;
try {
store = new VariableStorage(program, addr, sz);
}
catch (InvalidInputException e) {
store = VariableStorage.UNASSIGNED_STORAGE;
}
SymbolEntry entry;
if (data != null) {
entry = new MappedDataEntry(this, store, data);
}
else {
entry = new MappedEntry(this, store, null);
}
addMapEntry(entry);
}
/**
* Constructor for HighSymbol which is unattached to a HighFunction
* @param id is the unique id to assign
* @param nm is the name of the symbol
* @param data is an underlying Data object defining the storage and data-type
* @param dtmanage is the data-type manager for XML reference
*/
public HighCodeSymbol(long id, String nm, Data data, PcodeDataTypeManager dtmanage) {
super(id, nm, data.getDataType(), true, true, dtmanage);
Program program = dtmanage.getProgram();
VariableStorage store;
try {
store = new VariableStorage(program, data.getMinAddress(), data.getLength());
}
catch (InvalidInputException e) {
store = VariableStorage.UNASSIGNED_STORAGE;
}
SymbolEntry entry = new MappedDataEntry(this, store, data);
addMapEntry(entry);
}
@Override
@ -53,78 +128,30 @@ public class HighCodeSymbol extends HighSymbol {
return true;
}
/**
* Get the CodeSymbol backing this, if it exists
* @return the CodeSymbol or null
*/
public CodeSymbol getCodeSymbol() {
return symbol;
}
/**
* Get the Data object backing this, if it exists
* @return the Data object or null
*/
public Data getData() {
if (data == null) {
Object dataObj = symbol.getObject();
if (dataObj instanceof Data) {
data = (Data) dataObj;
}
SymbolEntry entry = entryList[0];
if (entry instanceof MappedDataEntry) {
return ((MappedDataEntry) entry).getData();
}
return data;
}
@Override
public VariableStorage getStorage() {
if (storage == null) {
Data dataObj = getData();
if (dataObj != null) {
try {
storage = new VariableStorage(function.getFunction().getProgram(),
dataObj.getAddress(), dataObj.getLength());
}
catch (InvalidInputException e) {
storage = VariableStorage.UNASSIGNED_STORAGE;
}
}
else {
storage = VariableStorage.UNASSIGNED_STORAGE;
}
}
return storage;
}
@Override
public String buildXML() {
// TODO Auto-generated method stub
return null;
}
@Override
public void restoreXML(XmlPullParser parser) throws PcodeXMLException {
XmlElement symel = parser.start("symbol");
restoreSymbolXML(symel);
Symbol tmpSymbol = function.getFunction().getProgram().getSymbolTable().getSymbol(getId());
if (tmpSymbol instanceof CodeSymbol) {
symbol = (CodeSymbol) tmpSymbol;
}
type = function.getDataTypeManager().readXMLDataType(parser);
size = type.getLength();
parser.end(symel);
restoreEntryXML(parser);
while (parser.peek().isStart()) {
parser.discardSubTree();
}
super.restoreXML(parser);
symbol = null;
}
@Override
protected void restoreEntryXML(XmlPullParser parser) throws PcodeXMLException {
AddressFactory addrFactory = function.getAddressFactory();
XmlElement addrel = parser.start("addr");
int sz = type.getLength();
if (sz == 0) {
throw new PcodeXMLException("Invalid symbol 0-sized data-type: " + type.getName());
}
Address varAddr = Varnode.readXMLAddress(addrel, addrFactory);
parser.end(addrel);
pcaddr = parseRangeList(parser);
if (symbol == null) {
Program program = function.getFunction().getProgram();
data = program.getListing().getDataAt(varAddr);
}
}
}

View file

@ -98,11 +98,7 @@ public class HighConstant extends HighVariable {
if (symbol == null) {
symbol = function.getGlobalSymbolMap().getSymbol(symref);
}
if (symbol instanceof DynamicSymbol) {
name = symbol.getName();
symbol.setHighVariable(this);
}
else if (symbol == null) {
if (symbol == null) {
GlobalSymbolMap globalMap = function.getGlobalSymbolMap();
Program program = function.getFunction().getProgram();
symbol = globalMap.populateSymbol(symref, null, -1);
@ -114,6 +110,10 @@ public class HighConstant extends HighVariable {
}
}
}
else if (symbol.getFirstWholeMap() instanceof DynamicEntry) {
name = symbol.getName();
symbol.setHighVariable(this);
}
}
parser.end(el);
}

View file

@ -0,0 +1,67 @@
/* ###
* 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 ghidra.program.model.address.Address;
import ghidra.program.model.data.DataType;
import ghidra.program.model.listing.VariableStorage;
import ghidra.util.exception.InvalidInputException;
import ghidra.util.xml.SpecXmlUtils;
/**
* A symbol, within a decompiler model, for a function without a body in the current Program.
* The Address of this symbol corresponds to the code location that CALL instructions refer to.
* In anticipation of a (not fully resolved) thunking mechanism, this symbol also has a separate
* resolve Address, which is where the decompiler expects to retrieve the detailed Function object.
*/
public class HighExternalSymbol extends HighSymbol {
private Address resolveAddress; // The location of the Function object
/**
* Construct the external reference symbol given a name, the symbol Address, and a
* resolving Address.
* @param nm is the given name
* @param addr is the symbol Address
* @param resolveAddr is the resolve Address
* @param dtmanage is a PcodeDataTypeManager for facilitating XML marshaling
*/
public HighExternalSymbol(String nm, Address addr, Address resolveAddr,
PcodeDataTypeManager dtmanage) {
super(0, nm, DataType.DEFAULT, true, true, dtmanage);
resolveAddress = resolveAddr;
VariableStorage store;
try {
store = new VariableStorage(getProgram(), addr, 1);
}
catch (InvalidInputException e) {
store = VariableStorage.UNASSIGNED_STORAGE;
}
MappedEntry entry = new MappedEntry(this, store, null);
addMapEntry(entry);
}
@Override
public void saveXML(StringBuilder buf) {
buf.append("<externrefsymbol");
if ((name != null) && (name.length() > 0)) { // Give the symbol a name if we can
SpecXmlUtils.xmlEscapeAttribute(buf, "name", name + "_exref");
}
buf.append(">\n");
buf.append(Varnode.buildXMLAddress(resolveAddress));
buf.append("</externrefsymbol>\n");
}
}

View file

@ -482,26 +482,6 @@ public class HighFunction extends PcodeSyntaxTree {
return resBuf.toString();
}
/**
* Build the XML representation of only the shell function info not including everything known
* about the function.
*
* @param name name of the function
* @param addr address the function is located at
*
* @return the XML string
*/
static public String buildFunctionShellXML(String name, Address addr) {
StringBuilder resBuf = new StringBuilder();
resBuf.append("<function");
SpecXmlUtils.xmlEscapeAttribute(resBuf, "name", name);
SpecXmlUtils.encodeSignedIntegerAttribute(resBuf, "size", 1);
resBuf.append(">\n");
resBuf.append(Varnode.buildXMLAddress(addr));
resBuf.append("</function>\n");
return resBuf.toString();
}
public static ErrorHandler getErrorHandler(final Object errOriginator,
final String targetName) {
return new ErrorHandler() {

View file

@ -40,8 +40,10 @@ public class HighFunctionDBUtil {
public static final String AUTO_CAT = "/auto_proto"; // Category for auto generated prototypes
/**
* Commit function return to the underlying database.
* @param highFunction
* Commit the decompiler's version of the function return data-type to the database.
* The decompiler's version of the prototype model is committed as well
* @param highFunction is the decompiler's model of the function
* @param source is the desired SourceType for the commit
*/
public static void commitReturnToDatabase(HighFunction highFunction, SourceType source) {
try {
@ -84,7 +86,9 @@ public class HighFunctionDBUtil {
List<Parameter> params = getParameters(highFunction, useDataTypes);
commitParamsToDatabase(function, highFunction.getFunctionPrototype(), params,
FunctionPrototype functionPrototype = highFunction.getFunctionPrototype();
String modelName = (functionPrototype != null) ? functionPrototype.getModelName() : null;
commitParamsToDatabase(function, modelName, params,
highFunction.getFunctionPrototype().isVarArg(), true, source);
}
@ -113,22 +117,26 @@ public class HighFunctionDBUtil {
}
/**
* Commit the specified parameter list to the specified function.
* @param function
* @param params
* Commit a specified set of parameters for the given function to the database.
* The name, data-type, and storage is committed for each parameter. The parameters are
* provided along with a formal PrototypeModel. If the parameters fit the model, they are
* committed using "dynamic" storage. Otherwise, they are committed using "custom" storage.
* @param function is the Function being modified
* @param modelName is the name of the underlying PrototypeModel
* @param params is the formal list of parameter objects
* @param hasVarArgs is true if the prototype can take variable arguments
* @param renameConflicts if true any name conflicts will be resolved
* by renaming the conflicting local variable/label
* @param source source type
* @throws DuplicateNameException if commit of parameters caused conflict with other
* local variable/label. Should not occur if renameConflicts is true.
* @throws InvalidInputException
* @throws InvalidInputException for invalid variable names or for parameter data-types that aren't fixed length
* @throws DuplicateNameException is there are collisions between variable names in the function's scope
*/
public static void commitParamsToDatabase(Function function, FunctionPrototype prototype,
public static void commitParamsToDatabase(Function function, String modelName,
List<Parameter> params, boolean hasVarArgs, boolean renameConflicts, SourceType source)
throws DuplicateNameException, InvalidInputException {
String modelName = prototype != null ? prototype.getModelName() : null;
try {
function.updateFunction(modelName, null, params,
FunctionUpdateType.DYNAMIC_STORAGE_ALL_PARAMS, true, source);
@ -180,6 +188,7 @@ public class HighFunctionDBUtil {
break;
}
catch (DuplicateNameException e) {
// Continue looping until we get a unique symbol name
}
catch (InvalidInputException e) {
break;
@ -188,9 +197,10 @@ public class HighFunctionDBUtil {
}
/**
* Commit all local variables to the underlying database.
* @param highFunction
* @param source source type
* Commit local variables from the decompiler's model of the function to the database.
* This does NOT include formal function parameters.
* @param highFunction is the decompiler's model of the function
* @param source is the desired SourceType for the commit
*/
public static void commitLocalsToDatabase(HighFunction highFunction, SourceType source) {
@ -281,11 +291,12 @@ public class HighFunctionDBUtil {
Iterator<HighSymbol> symbols = highFunction.getLocalSymbolMap().getSymbols();
while (symbols.hasNext()) {
HighSymbol symbol = symbols.next();
if (!(symbol instanceof DynamicSymbol)) {
SymbolEntry entry = symbol.getFirstWholeMap();
if (!(entry instanceof DynamicEntry)) {
continue;
}
// Note: assumes there is only one hash method used for unique locals
if (((DynamicSymbol) symbol).getHash() == hash) {
if (((DynamicEntry) entry).getHash() == hash) {
if (symbol.getHighVariable() != null) {
return true; // Hash successfully attached to a variable
}
@ -303,8 +314,9 @@ public class HighFunctionDBUtil {
*/
private static Variable clearConflictingLocalVariables(HighSymbol local) {
if (local instanceof MappedSymbol) {
if (((MappedSymbol) local).getSlot() >= 0) { // Don't clear parameters
SymbolEntry entry = local.getFirstWholeMap();
if (entry instanceof MappedEntry) {
if (local.isParameter()) { // Don't clear parameters
throw new IllegalArgumentException();
}
}
@ -314,12 +326,12 @@ public class HighFunctionDBUtil {
VariableStorage storage = local.getStorage();
int firstUseOffset = local.getFirstUseOffset();
if (local instanceof DynamicSymbol) {
if (entry instanceof DynamicEntry) {
DynamicSymbol dynamicSym = (DynamicSymbol) local;
DynamicEntry dynamicEntry = (DynamicEntry) entry;
for (Variable ul : func.getLocalVariables(VariableFilter.UNIQUE_VARIABLE_FILTER)) {
// Note: assumes there is only one hash method used for unique locals
if (ul.getFirstStorageVarnode().getOffset() == dynamicSym.getHash()) {
if (ul.getFirstStorageVarnode().getOffset() == dynamicEntry.getHash()) {
return ul;
}
}
@ -357,12 +369,12 @@ public class HighFunctionDBUtil {
* @return the matching parameter that can be modified
* @throws InvalidInputException if the desired parameter cannot be modified
*/
private static Parameter getDatabaseParameter(MappedSymbol param) throws InvalidInputException {
private static Parameter getDatabaseParameter(HighSymbol param) throws InvalidInputException {
HighFunction highFunction = param.getHighFunction();
Function function = highFunction.getFunction();
int slot = param.getSlot();
int slot = param.getCategoryIndex();
Parameter[] parameters = function.getParameters();
if (slot < parameters.length) {
if (parameters[slot].isAutoParameter()) {
@ -424,10 +436,8 @@ public class HighFunctionDBUtil {
boolean isRename = name != null;
if (variable.isParameter()) {
MappedSymbol mappedSymbol = (MappedSymbol) variable;
Parameter dbParam = getDatabaseParameter(mappedSymbol);
VariableStorage storage = mappedSymbol.getStorage();
Parameter dbParam = getDatabaseParameter(variable);
VariableStorage storage = variable.getStorage();
if (dataType != null) {
if (resized && function.hasCustomVariableStorage()) {
VariableStorage newStorage =
@ -448,7 +458,7 @@ public class HighFunctionDBUtil {
HighVariable tmpHigh = variable.getHighVariable();
if (tmpHigh != null && tmpHigh.getRepresentative().isUnique()) {
storage =
DynamicSymbol.buildDynamicStorage(tmpHigh.getRepresentative(), highFunction);
DynamicEntry.buildDynamicStorage(tmpHigh.getRepresentative(), highFunction);
var = null;
}
else {
@ -591,26 +601,26 @@ public class HighFunctionDBUtil {
}
/**
* Commit an override of a calls prototype to the database
* @param function is the Function whose call is being overriden
* @param callsite is the address of the call
* @param sig signature override
* @throws InvalidInputException
* @throws DuplicateNameException
* Commit an overriding prototype for a particular call site to the database. The override
* only applies to the function(s) containing the actual call site. Calls to the same function from
* other sites are unaffected. This is used typically either for indirect calls are for calls to
* a function with a variable number of parameters.
* @param function is the Function whose call site is being overridden
* @param callsite is the address of the calling instruction (the call site)
* @param sig is the overriding function signature
* @throws InvalidInputException if there are problems committing the override symbol
*/
public static void writeOverride(Function function, Address callsite, FunctionSignature sig)
throws InvalidInputException, DuplicateNameException {
throws InvalidInputException {
ParameterDefinition[] params = sig.getArguments();
FunctionSignatureImpl fsig = new FunctionSignatureImpl("tmpname"); // Empty datatype, will get renamed later
FunctionDefinitionDataType fsig = new FunctionDefinitionDataType("tmpname"); // Empty datatype, will get renamed later
fsig.setGenericCallingConvention(sig.getGenericCallingConvention());
fsig.setArguments(params);
fsig.setReturnType(sig.getReturnType());
fsig.setVarArgs(sig.hasVarArgs());
FunctionDefinitionDataType dt = new FunctionDefinitionDataType(fsig);
DataTypeSymbol datsym = new DataTypeSymbol(dt, "prt", AUTO_CAT);
DataTypeSymbol datsym = new DataTypeSymbol(fsig, "prt", AUTO_CAT);
Program program = function.getProgram();
SymbolTable symtab = program.getSymbolTable();
DataTypeManager dtmanage = program.getDataTypeManager();
@ -623,9 +633,9 @@ public class HighFunctionDBUtil {
/**
* Read a call prototype override which corresponds to the specified override code symbol
* @param sym special call override code symbol whose address corresponds to a callsite
* @param sym special call override code symbol whose address corresponds to a call site
* @return call prototype override DataTypeSymbol or null if associated function signature
* datatype could not be found
* data-type could not be found
*/
public static DataTypeSymbol readOverride(Symbol sym) {
DataTypeSymbol datsym = DataTypeSymbol.readSymbol(AUTO_CAT, sym);

View file

@ -0,0 +1,58 @@
/* ###
* 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 ghidra.program.model.address.Address;
import ghidra.program.model.data.DataType;
import ghidra.program.model.listing.VariableStorage;
import ghidra.util.exception.InvalidInputException;
import ghidra.util.xml.SpecXmlUtils;
/**
* A function symbol that represents only a shell of (the name and address) the function,
* when no other information is available.
*/
public class HighFunctionShellSymbol extends HighSymbol {
/**
* Construct the function shell given a name and address
* @param nm is the given name
* @param addr is the given address
* @param manage is PcodeDataTypeManager to facilitate XML marshaling
*/
public HighFunctionShellSymbol(String nm, Address addr, PcodeDataTypeManager manage) {
super(0, nm, DataType.DEFAULT, true, true, manage);
VariableStorage store;
try {
store = new VariableStorage(getProgram(), addr, 1);
}
catch (InvalidInputException e) {
store = VariableStorage.UNASSIGNED_STORAGE;
}
MappedEntry entry = new MappedEntry(this, store, null);
addMapEntry(entry);
}
@Override
public void saveXML(StringBuilder buf) {
buf.append("<function");
SpecXmlUtils.xmlEscapeAttribute(buf, "name", name);
SpecXmlUtils.encodeSignedIntegerAttribute(buf, "size", 1);
buf.append(">\n");
buf.append(Varnode.buildXMLAddress(getStorage().getMinAddress()));
buf.append("</function>\n");
}
}

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 ghidra.program.model.address.Address;
import ghidra.program.model.data.DataType;
import ghidra.program.model.listing.VariableStorage;
import ghidra.util.exception.InvalidInputException;
/**
* A function symbol that encapsulates detailed information about a particular function
* for the purposes of decompilation. The detailed model is provided by a backing HighFunction object.
*/
public class HighFunctionSymbol extends HighSymbol {
/**
* Construct given an Address, size, and decompiler function model for the symbol.
* The Address is typically the entry point of the function but may be different
* if the function is getting mapped from elsewhere (i.e. the EXTERNAL space). The size
* is given in bytes but generally isn't the true size of the function. The size needs to
* make the symbol just big enough to absorb any off-cut Address queries.
* @param addr is the starting Address of the symbol
* @param size is the pseudo-size of the function
* @param function is the decompiler model of the function
*/
public HighFunctionSymbol(Address addr, int size, HighFunction function) {
super(0, "", DataType.DEFAULT, function);
VariableStorage store;
try {
store = new VariableStorage(getProgram(), addr, size);
}
catch (InvalidInputException e) {
store = VariableStorage.UNASSIGNED_STORAGE;
}
MappedEntry entry = new MappedEntry(this, store, null);
addMapEntry(entry);
}
@Override
public void saveXML(StringBuilder buf) {
MappedEntry entry = (MappedEntry) getFirstWholeMap();
String funcString =
function.buildFunctionXML(entry.getStorage().getMinAddress(), entry.getSize());
buf.append(funcString);
}
}

View file

@ -0,0 +1,54 @@
/* ###
* 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 ghidra.program.model.address.Address;
import ghidra.program.model.data.DataType;
import ghidra.program.model.listing.VariableStorage;
import ghidra.util.exception.InvalidInputException;
/**
* A symbol with no underlying data-type. A label within code. This is used to
* model named jump targets within a function to the decompiler.
*/
public class HighLabelSymbol extends HighSymbol {
/**
* Construct the label given a name and address
* @param nm is the given name
* @param addr is the given Address
* @param dtmanage is a PcodeDataManager to facilitate XML marshaling
*/
public HighLabelSymbol(String nm, Address addr, PcodeDataTypeManager dtmanage) {
super(0, nm, DataType.DEFAULT, true, true, dtmanage);
VariableStorage store;
try {
store = new VariableStorage(getProgram(), addr, 1);
}
catch (InvalidInputException e) {
store = VariableStorage.UNASSIGNED_STORAGE;
}
MappedEntry entry = new MappedEntry(this, store, null);
addMapEntry(entry);
}
@Override
public void saveXML(StringBuilder buf) {
buf.append("<labelsym");
saveXMLHeader(buf);
buf.append("/>\n");
}
}

View file

@ -57,11 +57,8 @@ public class HighParam extends HighLocal {
@Override
public void restoreXml(XmlPullParser parser) throws PcodeXMLException {
super.restoreXml(parser);
slot = 0;
HighSymbol sym = getSymbol();
if (sym instanceof MappedSymbol) {
slot = ((MappedSymbol) sym).getSlot();
}
slot = sym.getCategoryIndex();
}
}

View file

@ -16,105 +16,245 @@
package ghidra.program.model.pcode;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.data.DataType;
import ghidra.program.model.listing.Program;
import ghidra.program.model.listing.VariableStorage;
import ghidra.util.xml.SpecXmlUtils;
import ghidra.xml.XmlElement;
import ghidra.xml.XmlPullParser;
public abstract class HighSymbol {
/**
* A symbol within the decompiler's model of a particular function. The symbol has a name and a data-type
* along with other properties. The symbol is mapped to one or more storage locations by attaching a
* SymbolEntry for each mapping.
*/
public class HighSymbol {
public static final long ID_BASE = 0x4000000000000000L; // Put keys in the dynamic symbol portion of the key space
protected String name;
protected DataType type;
protected int size; // Size of this variable
protected Address pcaddr; // first-use address
protected HighFunction function; // associated function
protected int category; // Sub-class of symbol -1=none 0=parameter 1=equate
protected int categoryIndex; // Numbering within the sub-class
private boolean namelock; // Is this variable's name locked
private boolean typelock; // Is this variable's datatype locked
private boolean readonly;
private long id; // Unique id of this symbol
protected SymbolEntry[] entryList; // List of mappings for this symbol
private HighVariable highVariable;
private PcodeDataTypeManager dtmanage; // Datatype manager for XML generation
public HighSymbol(HighFunction func) { // For use with restoreXML
/**
* Constructor for use with restoreXML
* @param func is the HighFunction using the symbol
*/
protected HighSymbol(HighFunction func) {
function = func;
dtmanage = function.getDataTypeManager();
}
public HighSymbol(long uniqueId, String nm, DataType tp, int sz, Address pc,
HighFunction func) {
/**
* Construct a base symbol, given a name and data-type. Mappings must be attached separately.
* @param uniqueId is the id to associate with the new symbol
* @param nm is the given name
* @param tp is the given data-type
* @param func is the function model owning the new symbol
*/
protected HighSymbol(long uniqueId, String nm, DataType tp, HighFunction func) {
function = func;
dtmanage = function.getDataTypeManager();
name = nm;
type = tp;
size = sz;
pcaddr = pc;
namelock = false;
typelock = false;
function = func;
id = uniqueId;
category = -1;
categoryIndex = -1;
}
/**
* Construct a symbol that is not attached to a function model. The symbol is given
* a name, data-type, and other basic attributes. Mappings must be attached separately.
* @param uniqueId is the id to associate with the new symbol
* @param nm is the given name
* @param tp is the given data-type
* @param tlock is true if the symbol is type locked
* @param nlock is true if the symbol is name locked
* @param manage is a PcodeDataTypeManager to facilitate XML marshaling
*/
protected HighSymbol(long uniqueId, String nm, DataType tp, boolean tlock, boolean nlock,
PcodeDataTypeManager manage) {
function = null;
dtmanage = manage;
name = nm;
type = tp;
namelock = nlock;
typelock = tlock;
id = uniqueId;
category = -1;
categoryIndex = -1;
}
protected void addMapEntry(SymbolEntry entry) {
if (entryList == null) {
entryList = new SymbolEntry[1];
entryList[0] = entry;
}
else {
SymbolEntry[] newList = new SymbolEntry[entryList.length + 1];
for (int i = 0; i < entryList.length; ++i) {
newList[i] = entryList[i];
}
newList[entryList.length] = entry;
entryList = newList;
}
}
/**
* Get id associated with this symbol.
* @return the id
*/
public long getId() {
return id;
}
/**
* Associate a particular HighVariable with this symbol. This is used to link the symbol
* into the decompiler's description of how a function manipulates a particular symbol.
* @param high is the associated HighVariable
*/
public void setHighVariable(HighVariable high) {
this.highVariable = high;
}
/**
* Get the HighVariable associate with this symbol if any. This allows the user to go straight
* into the decompiler's function to see how the symbol gets manipulated.
* @return the associated HighVariable or null
*/
public HighVariable getHighVariable() {
return highVariable;
}
/**
* Get the base name of this symbol
* @return the name
*/
public String getName() {
return name;
}
/**
* Get the Program object containing the function being modeled.
* @return the Program
*/
public Program getProgram() {
return dtmanage.getProgram();
}
/**
* @return the data-type associate with this symbol
*/
public DataType getDataType() {
return type;
}
/**
* @return the number of bytes consumed by the storage for this symbol
*/
public int getSize() {
return size;
return entryList[0].getSize();
}
/**
* Get the first code Address, within the function, where this symbol's storage actually
* holds the value of the symbol. If there is more than one mapping for the symbol, this
* returns the code Address for the first mapping. A null value indicates that the storage
* is valid over the whole function (at least). If the value is non-null, the symbol storage
* may be used for other purposes at prior locations.
* @return the first use code Address or null
*/
public Address getPCAddress() {
return pcaddr;
return entryList[0].pcaddr;
}
/**
* Get the first code Address (expressed as a different in bytes from the starting address of the
* function) where this symbol's storage actually holds the value of the symbol. A value of 0 indicates
* that the storage is valid across the entire function. A negative value indicates the storage is
* an input to the function.
* @return the first-use offset of this symbol's storage
*/
protected int getFirstUseOffset() {
Address pcaddr = entryList[0].pcaddr;
if (pcaddr == null) {
return 0;
}
return (int) pcaddr.subtract(getHighFunction().getFunction().getEntryPoint());
}
/**
* Get the function model of which this symbol is a part.
* @return the HighFunction owning this symbol
*/
public HighFunction getHighFunction() {
return function;
}
/**
* Set the category and associated index for this symbol. The category indicates a specific sub-class
* of symbols. Currently -1=none, 0=parameter, 1=equate
* @param cat is the category
* @param index is the category index ("slot" for parameters)
*/
protected void setCategory(int cat, int index) {
category = cat;
categoryIndex = index;
}
/**
* Set whether this symbol's data-type is considered "locked". If it is "locked",
* this symbol's data-type is considered unchangeable during decompilation. The data-type
* will be forced into the decompiler's model of the function to the extent possible.
* @param typelock is true if the data-type should be considered "locked".
*/
public void setTypeLock(boolean typelock) {
this.typelock = typelock;
}
/**
* Set whether this symbol's name is considered "locked". If it is "locked", the decompiler
* will use the name when labeling the storage described by this symbol.
* @param namelock is true if the name should be considered "locked".
*/
public void setNameLock(boolean namelock) {
this.namelock = namelock;
}
public void setReadOnly(boolean readOnly) {
this.readonly = readOnly;
}
/**
* If this returns true, this symbol's data-type is "locked", meaning
* it is considered unchangeable during decompilation. The data-type
* will be forced into the decompiler's model of the function to the extent possible.
* @return true if the data-type is considered "locked".
*/
public boolean isTypeLocked() {
return typelock;
}
/**
* If this returns true, this symbol's name is "locked". meaning the decompiler
* is forced to use the name when labeling the storage described by this symbol.
* @return true if the name is considered "locked".
*/
public boolean isNameLocked() {
return namelock;
}
/**
* @return true if the symbol's value is considered read-only (by the decompiler)
*/
public boolean isReadOnly() {
return readonly;
return entryList[0].isReadOnly();
}
/**
@ -122,7 +262,15 @@ public abstract class HighSymbol {
* @return true if this is a parameter
*/
public boolean isParameter() {
return false;
return (category == 0);
}
/**
* For parameters (category=0), this method returns the position of the parameter within the function prototype.
* @return the category index for this symbol
*/
public int getCategoryIndex() {
return categoryIndex;
}
/**
@ -133,16 +281,55 @@ public abstract class HighSymbol {
return false;
}
public abstract VariableStorage getStorage();
/**
* @return the first mapping object attached to this symbol
*/
public SymbolEntry getFirstWholeMap() {
return entryList[0];
}
public abstract String buildXML();
public abstract void restoreXML(XmlPullParser parser)
throws PcodeXMLException;
/**
* @return the storage associated with this symbol (associated with the first mapping)
*/
public VariableStorage getStorage() {
return entryList[0].getStorage();
}
protected abstract void restoreEntryXML(XmlPullParser parser) throws PcodeXMLException;
/**
* Write out attributes for the base XML tag
* @param buf is the XML output stream
*/
protected void saveXMLHeader(StringBuilder buf) {
if ((id >> 56) != (ID_BASE >> 56)) { // Don't send down internal ids
SpecXmlUtils.encodeUnsignedIntegerAttribute(buf, "id", id);
}
SpecXmlUtils.xmlEscapeAttribute(buf, "name", name);
SpecXmlUtils.encodeBooleanAttribute(buf, "typelock", typelock);
SpecXmlUtils.encodeBooleanAttribute(buf, "namelock", namelock);
SpecXmlUtils.encodeBooleanAttribute(buf, "readonly", isReadOnly());
boolean isVolatile = entryList[0].isVolatile();
if (isVolatile) {
SpecXmlUtils.encodeBooleanAttribute(buf, "volatile", true);
}
SpecXmlUtils.encodeSignedIntegerAttribute(buf, "cat", category);
if (categoryIndex >= 0) {
SpecXmlUtils.encodeSignedIntegerAttribute(buf, "index", categoryIndex);
}
}
protected void restoreSymbolXML(XmlElement symel) throws PcodeXMLException {
/**
* Save the symbol description as a tag to the XML stream. This does NOT save the mappings.
* @param buf is the XML stream
*/
public void saveXML(StringBuilder buf) {
buf.append("<symbol");
saveXMLHeader(buf);
buf.append(">\n");
buf.append(dtmanage.buildTypeRef(type, getSize()));
buf.append("</symbol>\n");
}
protected void restoreXMLHeader(XmlElement symel) throws PcodeXMLException {
id = SpecXmlUtils.decodeLong(symel.getAttribute("id"));
if (id == 0) {
throw new PcodeXMLException("missing unique symbol id");
@ -158,53 +345,102 @@ public abstract class HighSymbol {
namelock = true;
}
name = symel.getAttribute("name");
categoryIndex = -1;
category = -1;
if (symel.hasAttribute("cat")) {
category = SpecXmlUtils.decodeInt(symel.getAttribute("cat"));
if (category == 0) {
categoryIndex = SpecXmlUtils.decodeInt(symel.getAttribute("index"));
}
}
}
protected Address parseRangeList(XmlPullParser parser) {
Address addr = null;
XmlElement rangelistel = parser.start("rangelist");
if (parser.peek().isStart()) {
// we only use this to establish first-use
XmlElement rangeel = parser.start("range");
String spc = rangeel.getAttribute("space");
long offset = SpecXmlUtils.decodeLong(rangeel.getAttribute("first"));
addr = function.getAddressFactory().getAddressSpace(spc).getAddress(offset);
addr = function.getFunction().getEntryPoint().getAddressSpace().getOverlayAddress(addr);
parser.end(rangeel);
/**
* Restore this symbol object and its associated mappings from an XML description
* in the given stream.
* @param parser is the given XML stream
* @throws PcodeXMLException if the XML description is invalid
*/
public void restoreXML(XmlPullParser parser) throws PcodeXMLException {
XmlElement symel = parser.start("symbol");
restoreXMLHeader(symel);
type = dtmanage.readXMLDataType(parser);
parser.end(symel);
if (categoryIndex >= 0 && name.startsWith("$$undef")) {
// use default parameter name
name = "param_" + Integer.toString(categoryIndex + 1);
}
parser.end(rangelistel);
return addr;
while (parser.peek().isStart()) {
XmlElement el = parser.peek();
SymbolEntry entry;
if (el.getName().equals("hash")) {
entry = new DynamicEntry(this);
}
else if (this instanceof HighCodeSymbol) {
entry = new MappedDataEntry(this);
}
else {
entry = new MappedEntry(this);
}
entry.restoreXML(parser);
addMapEntry(entry);
}
}
public static void buildMapSymXML(StringBuilder res, String addrHashRes, Address pc, String sym) {
res.append("<mapsym>\n");
res.append(sym);
res.append(addrHashRes);
if (pc == null || pc.isExternalAddress()) {
res.append("<rangelist/>");
/**
* Restore a full HighSymbol from the next &lt;mapsym&gt; tag in the given XML stream.
* This method acts as an XML based HighSymbol factory, instantiating the correct class
* based on the particular tags.
* @param parser is the given XML stream
* @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 if the XML description is invalid
*/
public static HighSymbol restoreMapSymXML(XmlPullParser parser, boolean isGlobal,
HighFunction high) throws PcodeXMLException {
HighSymbol res = null;
parser.start("mapsym");
XmlElement symel = parser.peek();
if (symel.getName().equals("equatesymbol")) {
res = new EquateSymbol(high);
}
else if (isGlobal) {
// res = new HighCodeSymbol(high);
// Currently the decompiler does not send back global symbols. They are inferred from the HighVariables
}
else {
buildRangelistXML(res, pc);
res = new HighSymbol(high);
}
res.restoreXML(parser);
while (parser.peek().isStart()) {
SymbolEntry entry;
if (parser.peek().getName().equals("hash")) {
entry = new DynamicEntry(res);
}
else {
entry = new MappedEntry(res);
}
entry.restoreXML(parser);
res.addMapEntry(entry);
}
parser.end();
return res;
}
/**
* Write out the given symbol with all its mapping as a &lt;mapsym&gt; tag to the given XML stream.
* @param res is the given XML stream
* @param sym is the given symbol
*/
public static void buildMapSymXML(StringBuilder res, HighSymbol sym) {
res.append("<mapsym>\n");
sym.saveXML(res);
for (SymbolEntry entry : sym.entryList) {
entry.saveXml(res);
}
res.append("</mapsym>\n");
}
public static void buildRangelistXML(StringBuilder res, Address pc) {
res.append("<rangelist>");
if (pc != null) {
AddressSpace space = pc.getAddressSpace();
if (space.isOverlaySpace()) {
space = space.getPhysicalSpace();
pc = space.getAddress(pc.getOffset());
}
res.append("<range");
SpecXmlUtils.encodeStringAttribute(res, "space", space.getName());
long off = pc.getUnsignedOffset();
SpecXmlUtils.encodeUnsignedIntegerAttribute(res, "first", off);
SpecXmlUtils.encodeUnsignedIntegerAttribute(res, "last", off);
res.append("/>");
}
res.append("</rangelist>\n");
}
}

View file

@ -29,18 +29,19 @@ import ghidra.xml.XmlElement;
import ghidra.xml.XmlPullParser;
/**
*
*
* Local variables visible to a function. This includes mapped (on the stack) and
* unmapped (only stored in a register).
*
* A container for local symbols within the decompiler's model of a function. It contains HighSymbol
* objects for any symbol within the scope of the function, including parameters. The container is populated
* either from the underlying Function object (when sending information to the decompiler) or read in from
* an XML description (when receiving a function model from the decompiler). HighSymbols can be obtained
* via Address using findLocal() or by id using getSymbol(). Parameters can be accessed specifically
* using getParamSymbol().
*/
public class LocalSymbolMap {
private HighFunction func; // Function to which these variables are local
private String spacename;
private HashMap<MappedVarKey, HighSymbol> addrMappedSymbols; // Hashed by addr and pcaddr
private HashMap<Long, HighSymbol> symbolMap; // Hashed by unique key
private MappedSymbol[] paramSymbols;
private HighSymbol[] paramSymbols;
private long uniqueSymbolId; // Next available symbol id
/**
@ -52,10 +53,14 @@ public class LocalSymbolMap {
spacename = spcname;
addrMappedSymbols = new HashMap<MappedVarKey, HighSymbol>();
symbolMap = new HashMap<Long, HighSymbol>();
paramSymbols = new MappedSymbol[0];
paramSymbols = new HighSymbol[0];
uniqueSymbolId = 0;
}
/**
* Get the decompiler's function model owning this container
* @return the owning HighFunction
*/
public HighFunction getHighFunction() {
return func;
}
@ -89,7 +94,6 @@ public class LocalSymbolMap {
if (Undefined.isUndefined(dt)) {
istypelock = false;
}
int sz = var.getLength();
String name = var.getName();
VariableStorage storage = var.getVariableStorage();
@ -100,8 +104,8 @@ public class LocalSymbolMap {
}
HighSymbol sym;
if (storage.isHashStorage()) {
sym = newDynamicSymbol(id, name, dt, sz, storage.getFirstVarnode().getOffset(),
defAddr);
sym =
newDynamicSymbol(id, name, dt, storage.getFirstVarnode().getOffset(), defAddr);
}
else {
sym = newMappedSymbol(id, name, dt, storage, defAddr, -1);
@ -116,7 +120,7 @@ public class LocalSymbolMap {
Address pcaddr = dbFunction.getEntryPoint();
pcaddr = pcaddr.subtractWrap(1);
List<MappedSymbol> paramList = new ArrayList<MappedSymbol>();
List<HighSymbol> paramList = new ArrayList<HighSymbol>();
for (int i = 0; i < p.length; ++i) {
Parameter var = p[i];
if (!var.isValid()) {
@ -128,8 +132,7 @@ public class LocalSymbolMap {
VariableStorage storage = var.getVariableStorage();
Address resAddr = storage.isStackStorage() ? null : pcaddr;
long id = getNextId();
MappedSymbol paramSymbol =
newMappedSymbol(id, name, dt, storage, resAddr, i);
HighSymbol paramSymbol = newMappedSymbol(id, name, dt, storage, resAddr, i);
paramList.add(paramSymbol);
boolean namelock = true;
if (!includeDefaultNames) {
@ -139,7 +142,7 @@ public class LocalSymbolMap {
paramSymbol.setTypeLock(lock);
}
paramSymbols = new MappedSymbol[paramList.size()];
paramSymbols = new HighSymbol[paramList.size()];
paramList.toArray(paramSymbols);
Arrays.sort(paramSymbols, PARAM_SYMBOL_SLOT_COMPARATOR);
@ -163,21 +166,7 @@ public class LocalSymbolMap {
* @throws PcodeXMLException for problems sub tags
*/
private HighSymbol parseSymbolXML(XmlPullParser parser) throws PcodeXMLException {
XmlElement node = parser.start("mapsym");
String typename = node.getAttribute("type");
HighSymbol res = null;
if (typename == null) {
res = new MappedSymbol(func);
}
else if (typename.equals("dynamic")) {
res = new DynamicSymbol(func);
}
else if (typename.equals("equate")) {
res = new EquateSymbol(func);
}
res.restoreXML(parser);
parser.end(node);
HighSymbol res = HighSymbol.restoreMapSymXML(parser, false, func);
insertSymbol(res);
return res;
}
@ -207,11 +196,11 @@ public class LocalSymbolMap {
parser.end(el);
}
private static final Comparator<MappedSymbol> PARAM_SYMBOL_SLOT_COMPARATOR =
new Comparator<MappedSymbol>() {
private static final Comparator<HighSymbol> PARAM_SYMBOL_SLOT_COMPARATOR =
new Comparator<HighSymbol>() {
@Override
public int compare(MappedSymbol sym1, MappedSymbol sym2) {
return sym1.getSlot() - sym2.getSlot();
public int compare(HighSymbol sym1, HighSymbol sym2) {
return sym1.getCategoryIndex() - sym2.getCategoryIndex();
}
};
@ -222,14 +211,14 @@ public class LocalSymbolMap {
*/
public void parseSymbolList(XmlPullParser parser) throws PcodeXMLException {
XmlElement el = parser.start("symbollist");
ArrayList<MappedSymbol> parms = new ArrayList<MappedSymbol>();
ArrayList<HighSymbol> parms = new ArrayList<HighSymbol>();
while (parser.peek().isStart()) {
HighSymbol sym = parseSymbolXML(parser);
if (sym.isParameter()) {
parms.add((MappedSymbol) sym);
parms.add(sym);
}
}
paramSymbols = new MappedSymbol[parms.size()];
paramSymbols = new HighSymbol[parms.size()];
parms.toArray(paramSymbols);
Arrays.sort(paramSymbols, PARAM_SYMBOL_SLOT_COMPARATOR);
parser.end(el);
@ -255,7 +244,7 @@ public class LocalSymbolMap {
Iterator<HighSymbol> iter = symbolMap.values().iterator();
while (iter.hasNext()) {
HighSymbol sym = iter.next();
res.append(sym.buildXML());
HighSymbol.buildMapSymXML(res, sym);
}
res.append("</symbollist>\n");
res.append("</scope>\n");
@ -306,14 +295,26 @@ public class LocalSymbolMap {
return symbolMap.get(id);
}
/**
* Get the number of parameter symbols in this scope
* @return the number of parameters
*/
public int getNumParams() {
return paramSymbols.length;
}
public MappedSymbol getParamSymbol(int i) {
/**
* @param i is the desired parameter position
* @return the i-th parameter HighSymbol
*/
public HighSymbol getParamSymbol(int i) {
return paramSymbols[i];
}
/**
* @param i is the desired parameter position
* @return the i-th parameter variable
*/
public HighParam getParam(int i) {
return (HighParam) paramSymbols[i].getHighVariable();
}
@ -328,22 +329,29 @@ public class LocalSymbolMap {
return false;
}
public MappedSymbol newMappedSymbol(long id, String nm, DataType dt, VariableStorage store,
protected HighSymbol newMappedSymbol(long id, String nm, DataType dt, VariableStorage store,
Address pcaddr, int slot) {
if (id == 0) {
id = getNextId();
}
MappedSymbol sym = new MappedSymbol(id, nm, dt, store, pcaddr, func, slot);
HighSymbol sym = new HighSymbol(id, nm, dt, func);
if (slot >= 0) {
sym.setCategory(0, slot);
}
MappedEntry entry = new MappedEntry(sym, store, pcaddr);
sym.addMapEntry(entry);
insertSymbol(sym);
return sym;
}
public DynamicSymbol newDynamicSymbol(long id, String nm, DataType dt, int sz, long hash,
protected HighSymbol newDynamicSymbol(long id, String nm, DataType dt, long hash,
Address pcaddr) {
if (id == 0) {
id = getNextId();
}
DynamicSymbol sym = new DynamicSymbol(id, nm, dt, sz, func, pcaddr, hash);
HighSymbol sym = new HighSymbol(id, nm, dt, func);
DynamicEntry entry = new DynamicEntry(sym, pcaddr, hash);
sym.addMapEntry(entry);
insertSymbol(sym);
return sym;
}
@ -356,17 +364,16 @@ public class LocalSymbolMap {
uniqueSymbolId = val;
}
}
if (sym instanceof MappedSymbol) {
MappedSymbol mapSym = (MappedSymbol)sym;
MappedVarKey key = new MappedVarKey(mapSym.getStorage(),mapSym.getPCAddress());
if (sym.entryList[0] instanceof MappedEntry) {
MappedVarKey key = new MappedVarKey(sym.getStorage(), sym.getPCAddress());
addrMappedSymbols.put(key, sym);
}
symbolMap.put(uniqueId, sym);
}
private void newEquateSymbol(long uniqueId, String nm, long val, long hash, Address addr,
TreeMap<String, DynamicSymbol> constantSymbolMap) {
DynamicSymbol eqSymbol = constantSymbolMap.get(nm);
TreeMap<String, HighSymbol> constantSymbolMap) {
HighSymbol eqSymbol = constantSymbolMap.get(nm);
if (eqSymbol != null) {
return; // New reference to same symbol
}
@ -390,7 +397,7 @@ public class LocalSymbolMap {
* @param dbFunction is the function to pull equates for
*/
private void grabEquates(Function dbFunction) {
TreeMap<String, DynamicSymbol> constantSymbolMap = null;
TreeMap<String, HighSymbol> constantSymbolMap = null;
// Find named constants via Equates
Program program = dbFunction.getProgram();
EquateTable equateTable = program.getEquateTable();
@ -406,7 +413,7 @@ public class LocalSymbolMap {
long hash[] = DynamicHash.calcConstantHash(instr, eq.getValue());
for (long element : hash) {
if (constantSymbolMap == null) {
constantSymbolMap = new TreeMap<String, DynamicSymbol>();
constantSymbolMap = new TreeMap<String, HighSymbol>();
}
newEquateSymbol(0, eq.getDisplayName(), eq.getValue(), element, defAddr,
constantSymbolMap);
@ -429,7 +436,7 @@ public class LocalSymbolMap {
// Add constant dynamic symbols to map
if (constantSymbolMap != null) {
for (DynamicSymbol sym : constantSymbolMap.values()) {
for (HighSymbol sym : constantSymbolMap.values()) {
long id = getNextId();
symbolMap.put(id, sym);
}

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 ghidra.program.model.listing.Data;
import ghidra.program.model.listing.VariableStorage;
import ghidra.xml.XmlPullParser;
/**
* A normal address based HighSymbol mapping with an associated Data object
*/
public class MappedDataEntry extends MappedEntry {
private Data data; // Backing data object
/**
* Constructor for use with restoreXML
* @param sym is the owning HighSymbol
*/
public MappedDataEntry(HighSymbol sym) {
super(sym);
}
/**
* Construct given a symbol, storage, and a backing Data object
* @param sym the given symbol
* @param store the given storage
* @param d the backing Data object
*/
public MappedDataEntry(HighSymbol sym, VariableStorage store, Data d) {
super(sym, store, null);
data = d;
}
/**
* @return the backing Data object
*/
public Data getData() {
return data;
}
@Override
public void restoreXML(XmlPullParser parser) throws PcodeXMLException {
super.restoreXML(parser);
data = symbol.getProgram().getListing().getDataAt(storage.getMinAddress());
}
@Override
public boolean isReadOnly() {
if (data.isConstant()) {
return true;
}
return super.isReadOnly();
}
@Override
public boolean isVolatile() {
if (data.isVolatile()) {
return true;
}
return super.isVolatile();
}
}

View file

@ -0,0 +1,162 @@
/* ###
* 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 ghidra.program.model.address.*;
import ghidra.program.model.listing.Program;
import ghidra.program.model.listing.VariableStorage;
import ghidra.program.model.mem.MemoryBlock;
import ghidra.program.model.symbol.Reference;
import ghidra.program.model.symbol.ReferenceIterator;
import ghidra.util.exception.InvalidInputException;
import ghidra.xml.XmlElement;
import ghidra.xml.XmlPullParser;
/**
* A normal mapping of a HighSymbol to a particular Address, consuming a set number of bytes
*/
public class MappedEntry extends SymbolEntry {
protected VariableStorage storage;
/**
* For use with restoreXML
* @param sym is the owning symbol
*/
public MappedEntry(HighSymbol sym) {
super(sym);
}
/**
* Construct given a symbol, storage, and first-use Address
* @param sym is the given symbol
* @param store is the given storage
* @param addr is the first-use Address (or null)
*/
public MappedEntry(HighSymbol sym, VariableStorage store, Address addr) {
super(sym);
storage = store;
pcaddr = addr;
}
@Override
public void restoreXML(XmlPullParser parser) throws PcodeXMLException {
HighFunction function = symbol.function;
Program program = function.getFunction().getProgram();
AddressFactory addrFactory = function.getAddressFactory();
XmlElement addrel = parser.start("addr");
int sz = symbol.type.getLength();
if (sz == 0) {
throw new PcodeXMLException(
"Invalid symbol 0-sized data-type: " + symbol.type.getName());
}
try {
Address varAddr = Varnode.readXMLAddress(addrel, addrFactory);
AddressSpace spc = varAddr.getAddressSpace();
if ((spc == null) || (spc.getType() != AddressSpace.TYPE_VARIABLE)) {
storage = new VariableStorage(program, varAddr, sz);
}
else {
storage = function.readXMLVarnodePieces(addrel, varAddr);
}
}
catch (InvalidInputException e) {
throw new PcodeXMLException("Invalid storage: " + e.getMessage());
}
parser.end(addrel);
parseRangeList(parser);
}
@Override
public void saveXml(StringBuilder buf) {
int logicalsize = 0; // Assume datatype size and storage size are the same
int typeLength = symbol.type.getLength();
if (typeLength != storage.size() && typeLength > 0) {
logicalsize = typeLength; // Force a logicalsize
}
String addrRes = Varnode.buildXMLAddress(storage.getVarnodes(), logicalsize);
buf.append(addrRes);
buildRangelistXML(buf);
}
@Override
public VariableStorage getStorage() {
return storage;
}
@Override
public int getSize() {
return storage.size();
}
@Override
public boolean isReadOnly() {
Address addr = storage.getMinAddress();
if (addr == null) {
return false;
}
boolean readonly = false;
Program program = symbol.getProgram();
MemoryBlock block = program.getMemory().getBlock(addr);
if (block != null) {
readonly = !block.isWrite();
// if the block says read-only, check the refs to the variable
// if the block says read-only, check the refs to the variable
if (readonly) {
ReferenceIterator refIter = program.getReferenceManager().getReferencesTo(addr);
int count = 0;
// boolean foundRead = false;
while (refIter.hasNext() && count < 100) {
Reference ref = refIter.next();
if (ref.getReferenceType().isWrite()) {
readonly = false;
break;
}
if (ref.getReferenceType().isRead()) {
// foundRead = true;
}
count++;
}
// TODO: Don't do override if no read reference found
//
// if we only have indirect refs to it, don't assume readonly!
//if (!foundRead && readonly && count > 1) {
// readonly = false;
//}
// they must be reading it multiple times for some reason
// if (readonly && count > 1) {
// readonly = false;
// }
}
}
return readonly;
}
@Override
public boolean isVolatile() {
Address addr = storage.getMinAddress();
if (addr == null) {
return false;
}
Program program = symbol.getProgram();
if (program.getLanguage().isVolatile(addr)) {
return true;
}
MemoryBlock block = program.getMemory().getBlock(addr);
return (block != null && block.isVolatile());
}
}

View file

@ -1,165 +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 ghidra.program.model.address.*;
import ghidra.program.model.data.DataType;
import ghidra.program.model.lang.ParamEntry;
import ghidra.program.model.listing.Program;
import ghidra.program.model.listing.VariableStorage;
import ghidra.util.exception.InvalidInputException;
import ghidra.util.xml.SpecXmlUtils;
import ghidra.xml.XmlElement;
import ghidra.xml.XmlPullParser;
public class MappedSymbol extends HighSymbol {
private VariableStorage storage;
private int slot; // parameter slot, -1 for non-parameter
public MappedSymbol(HighFunction func) { // For use with restoreXML
super(func);
}
public MappedSymbol(long uniqueId, String name, DataType dt, VariableStorage store,
Address pcaddr,
HighFunction func, int slot) {
super(uniqueId, name, dt, store.size(), pcaddr, func);
if (store.size() != dt.getLength()) {
if (ParamEntry.getMetatype(dt) != ParamEntry.TYPE_FLOAT) {
throw new IllegalArgumentException("Specified size does not match storage size");
}
}
this.storage = store;
this.slot = slot;
}
@Override
public VariableStorage getStorage() {
return storage;
}
@Override
public boolean isParameter() {
return slot >= 0;
}
public int getSlot() {
return slot;
}
@Override
public String buildXML() {
if (storage.getMinAddress() == null) {
return ""; // skip unassigned/bad variable
}
StringBuilder res = new StringBuilder();
int cat = isParameter() ? 0 : -1;
String sym = buildSymbolXML(function.getDataTypeManager(), getId(), name, type, size,
isTypeLocked(), isNameLocked(), false, false, cat, slot);
int logicalsize = 0; // Assume datatype size and storage size are the same
if ((type != null) && (type.getLength() != storage.size()))
{
logicalsize = type.getLength(); // Force a logicalsize
}
String addrRes = Varnode.buildXMLAddress(storage.getVarnodes(), logicalsize);
buildMapSymXML(res, addrRes, getPCAddress(), sym);
return res.toString();
}
@Override
public void restoreXML(XmlPullParser parser) throws PcodeXMLException {
XmlElement symel = parser.start("symbol");
restoreSymbolXML(symel);
slot = -1;
int cat = -1;
if (symel.hasAttribute("cat")) {
cat = SpecXmlUtils.decodeInt(symel.getAttribute("cat"));
if (cat == 0) {
slot = SpecXmlUtils.decodeInt(symel.getAttribute("index"));
}
}
type = function.getDataTypeManager().readXMLDataType(parser);
parser.end(symel);
if (slot >= 0 && name.startsWith("$$undef")) {
// use default parameter name
name = "param_" + Integer.toString(slot + 1);
}
restoreEntryXML(parser);
while (parser.peek().isStart()) {
parser.discardSubTree();
}
}
@Override
protected void restoreEntryXML(XmlPullParser parser) throws PcodeXMLException {
Program program = function.getFunction().getProgram();
AddressFactory addrFactory = function.getAddressFactory();
XmlElement addrel = parser.start("addr");
int sz = type.getLength();
if (sz == 0) {
throw new PcodeXMLException("Invalid symbol 0-sized data-type: " + type.getName());
}
try {
Address varAddr = Varnode.readXMLAddress(addrel, addrFactory);
AddressSpace spc = varAddr.getAddressSpace();
if ((spc == null) || (spc.getType() != AddressSpace.TYPE_VARIABLE)) {
storage = new VariableStorage(program, varAddr, sz);
}
else {
storage = function.readXMLVarnodePieces(addrel, varAddr);
}
}
catch (InvalidInputException e) {
throw new PcodeXMLException("Invalid storage: " + e.getMessage());
}
size = storage.size();
parser.end(addrel);
pcaddr = parseRangeList(parser);
}
public static String buildSymbolXML(PcodeDataTypeManager dtmanage, long uniqueId, String nm,
DataType dt, int length, boolean tl, boolean nl, boolean ro,
boolean isVolatile, int cat, int slot) {
StringBuilder res = new StringBuilder();
res.append("<symbol");
if ((uniqueId >> 56) == (ID_BASE >> 56)) {
uniqueId = 0; // Don't send down internal ids
}
if (uniqueId != 0) {
SpecXmlUtils.encodeUnsignedIntegerAttribute(res, "id", uniqueId);
}
SpecXmlUtils.xmlEscapeAttribute(res, "name", nm);
SpecXmlUtils.encodeBooleanAttribute(res, "typelock", tl);
SpecXmlUtils.encodeBooleanAttribute(res, "namelock", nl);
SpecXmlUtils.encodeBooleanAttribute(res, "readonly", ro);
if (isVolatile) {
SpecXmlUtils.encodeBooleanAttribute(res, "volatile", true);
}
SpecXmlUtils.encodeSignedIntegerAttribute(res, "cat", cat);
if (slot >= 0) {
SpecXmlUtils.encodeSignedIntegerAttribute(res, "index", slot);
}
res.append(">\n");
res.append(dtmanage.buildTypeRef(dt, length));
res.append("</symbol>\n");
return res.toString();
}
}

View file

@ -0,0 +1,125 @@
/* ###
* 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 ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.listing.VariableStorage;
import ghidra.util.xml.SpecXmlUtils;
import ghidra.xml.XmlElement;
import ghidra.xml.XmlPullParser;
/**
* A mapping from a HighSymbol object to the storage that holds the symbol's value.
*
*/
public abstract class SymbolEntry {
protected HighSymbol symbol; // The Symbol owning this entry
protected Address pcaddr; // Start of code range where this SymbolEntry applies
/**
* Constructor for use with restoreXML
* @param sym is the symbol owning this entry
*/
public SymbolEntry(HighSymbol sym) {
symbol = sym;
}
/**
* Restore this entry from the given XML stream. Typically more than one tag is consumed
* @param parser is the given XML stream
* @throws PcodeXMLException if the XML is invalid
*/
public abstract void restoreXML(XmlPullParser parser)
throws PcodeXMLException;
/**
* Save this entry as (a set of) XML tags to the given stream
* @param buf is the given stream
*/
public abstract void saveXml(StringBuilder buf);
/**
* Get the storage associated with this particular mapping of the Symbol
* @return the storage object
*/
public abstract VariableStorage getStorage();
/**
* Get the number of bytes consumed by the symbol when using this storage
* @return the size of this entry
*/
public abstract int getSize();
/**
* @return true if the mapped storage is read-only
*/
public abstract boolean isReadOnly();
/**
* @return true if the mapped storage is volatile
*/
public abstract boolean isVolatile();
/**
* The storage used to hold this Symbol may be used for other purposes at different points in
* the code. This returns the earliest address in the code where this storage is used for this symbol
* @return the starting address where the Symbol uses this storage
*/
public Address getPCAdress() {
return pcaddr;
}
protected void parseRangeList(XmlPullParser parser) {
XmlElement rangelistel = parser.start("rangelist");
if (parser.peek().isStart()) {
// we only use this to establish first-use
XmlElement rangeel = parser.start("range");
String spc = rangeel.getAttribute("space");
long offset = SpecXmlUtils.decodeLong(rangeel.getAttribute("first"));
pcaddr = symbol.function.getAddressFactory().getAddressSpace(spc).getAddress(offset);
pcaddr =
symbol.function.getFunction().getEntryPoint().getAddressSpace().getOverlayAddress(
pcaddr);
parser.end(rangeel);
}
parser.end(rangelistel);
}
protected void buildRangelistXML(StringBuilder res) {
if (pcaddr == null || pcaddr.isExternalAddress()) {
res.append("<rangelist/>");
return;
}
res.append("<rangelist>");
AddressSpace space = pcaddr.getAddressSpace();
long off;
if (space.isOverlaySpace()) {
space = space.getPhysicalSpace();
off = space.getAddress(pcaddr.getOffset()).getUnsignedOffset();
}
else {
off = pcaddr.getUnsignedOffset();
}
res.append("<range");
SpecXmlUtils.encodeStringAttribute(res, "space", space.getName());
SpecXmlUtils.encodeUnsignedIntegerAttribute(res, "first", off);
SpecXmlUtils.encodeUnsignedIntegerAttribute(res, "last", off);
res.append("/>");
res.append("</rangelist>\n");
}
}