diff --git a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/DecompileCallback.java b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/DecompileCallback.java
index 612f7224ca..a5ef331de2 100644
--- a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/DecompileCallback.java
+++ b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/DecompileCallback.java
@@ -671,8 +671,9 @@ public class DecompileCallback {
if (extRef != null) {
func = listing.getFunctionAt(extRef.getToAddress());
if (func == null) {
- String res = HighFunction.buildFunctionShellXML(extRef.getLabel(), addr);
- return buildResult(addr, null, res, null);
+ HighSymbol shellSymbol =
+ new HighFunctionShellSymbol(extRef.getLabel(), addr, dtmanage);
+ return buildResult(shellSymbol, null);
}
}
else {
@@ -690,13 +691,12 @@ public class DecompileCallback {
int extrapop = getExtraPopOverride(func, addr);
hfunc.grabFromFunction(extrapop, false, (extrapop != default_extrapop));
- String res = hfunc.buildFunctionXML(addr, 2);
+ HighSymbol funcSymbol = new HighFunctionSymbol(addr, 2, hfunc);
Namespace namespc = func.getParentNamespace();
if (debug != null) {
debug.getFNTypes(hfunc);
}
- res = buildResult(addr, null, res, namespc);
- return res;
+ return buildResult(funcSymbol, namespc);
}
catch (Exception e) {
Msg.error(this,
@@ -790,7 +790,7 @@ public class DecompileCallback {
return name;
}
- private String buildResult(Address addr, Address pc, String sym, Namespace namespc) {
+ private String buildResult(HighSymbol highSymbol, Namespace namespc) {
StringBuilder res = new StringBuilder();
res.append("\n");
res.append("\n");
@@ -801,16 +801,15 @@ public class DecompileCallback {
HighFunction.createNamespaceTag(res, namespc);
}
res.append("\n");
- String addrRes = Varnode.buildXMLAddress(addr);
if (debug != null) {
StringBuilder res2 = new StringBuilder();
- HighSymbol.buildMapSymXML(res2, addrRes, pc, sym);
+ HighSymbol.buildMapSymXML(res2, highSymbol);
String res2string = res2.toString();
debug.getMapped(namespc, res2string);
res.append(res2string);
}
else {
- HighSymbol.buildMapSymXML(res, addrRes, pc, sym);
+ HighSymbol.buildMapSymXML(res, highSymbol);
}
res.append("\n");
@@ -818,38 +817,25 @@ public class DecompileCallback {
}
private String buildData(Data data) { // Convert global variable to XML
- Address addr = data.getMinAddress();
Symbol sym = data.getPrimarySymbol();
- boolean readonly = data.isConstant();
- boolean isVolatile = data.isVolatile();
- if (!readonly) {
- readonly = isReadOnlyNoData(addr);
- }
- if (!isVolatile) {
- isVolatile = isVolatileNoData(addr);
- }
- if ((data.getDataType() == DataType.DEFAULT) && (sym == null) && !isVolatile && !readonly) {
- return null;
- }
- long uniqueId;
- String name;
+ HighCodeSymbol highSymbol;
if (sym != null) {
- name = sym.getName();
- uniqueId = sym.getID();
+ highSymbol = new HighCodeSymbol(sym.getID(), sym.getName(), data, dtmanage);
}
else {
- name = SymbolUtilities.getDynamicName(program, addr);
- uniqueId = 0;
+ highSymbol = new HighCodeSymbol(0,
+ SymbolUtilities.getDynamicName(program, data.getAddress()), data, dtmanage);
+ SymbolEntry entry = highSymbol.getFirstWholeMap();
+ if (data.getDataType() == DataType.DEFAULT && !entry.isReadOnly() &&
+ !entry.isVolatile()) {
+ return null;
+ }
}
-
- int sz = data.getLength();
- String symstring = MappedSymbol.buildSymbolXML(dtmanage, uniqueId, name, data.getDataType(),
- sz, true, true, readonly, isVolatile, -1, -1);
if (debug != null) {
- debug.getType(data.getDataType());
+ debug.getType(highSymbol.getDataType());
}
Namespace namespc = (sym != null) ? sym.getParentNamespace() : null;
- return buildResult(addr, null, symstring, namespc);
+ return buildResult(highSymbol, namespc);
}
private StringBuilder buildRegister(Register reg) {
@@ -869,28 +855,9 @@ public class DecompileCallback {
* @return the XML description
*/
private String buildLabel(Symbol sym, Address addr) {
- // TODO: Assume this is not data
- boolean isVolatile = isVolatileNoData(addr);
- if (!isVolatile) {
- isVolatile = program.getLanguage().isVolatile(addr);
- }
- boolean readonly = isReadOnlyNoData(addr);
-
- StringBuilder buf = new StringBuilder();
- buf.append("\n");
+ HighSymbol labelSymbol = new HighLabelSymbol(sym.getName(), addr, dtmanage);
Namespace namespc = sym.getParentNamespace();
- return buildResult(sym.getAddress(), null, buf.toString(), namespc);
+ return buildResult(labelSymbol, namespc);
}
/**
@@ -966,12 +933,12 @@ public class DecompileCallback {
hfunc.grabFromFunction(extrapop, includeDefaultNames,
(extrapop != default_extrapop));
- String funcsym = hfunc.buildFunctionXML(entry, (int) (diff + 1));
+ HighSymbol functionSymbol = new HighFunctionSymbol(entry, (int) (diff + 1), hfunc);
Namespace namespc = func.getParentNamespace();
if (debug != null) {
debug.getFNTypes(hfunc);
}
- return buildResult(entry, null, funcsym, namespc);
+ return buildResult(functionSymbol, namespc);
}
}
@@ -1105,15 +1072,6 @@ public class DecompileCallback {
}
private String buildExternalRef(Address addr, ExternalReference ref) {
- StringBuilder resBuf = new StringBuilder();
- resBuf.append(" 0)) { // Give the symbol a name if we can
- SpecXmlUtils.xmlEscapeAttribute(resBuf, "name", nm + "_exref");
- }
- resBuf.append(">\n");
- resBuf.append(Varnode.buildXMLAddress(addr));
-// res += Varnode.buildXMLAddress(ref.getToAddress());
// The decompiler model was to assume that the ExternalReference
// object could resolve the physical address where the dll
// function was getting loaded, just as a linker would do.
@@ -1127,8 +1085,8 @@ public class DecompileCallback {
// the address of the reference to hang the function on, and make
// no attempt to get a realistic linked address. This works because
// we never read bytes or look up code units at the address.
- resBuf.append("\n");
- return buildResult(addr, null, resBuf.toString(), null);
+ HighSymbol externSymbol = new HighExternalSymbol(ref.getLabel(), addr, addr, dtmanage);
+ return buildResult(externSymbol, null);
}
private void buildTrackSet(StringBuilder buf, Register reg, long val) {
diff --git a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/plugin/core/decompile/actions/RetypeVariableAction.java b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/plugin/core/decompile/actions/RetypeVariableAction.java
index eaba37170a..d4c1d76b9b 100644
--- a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/plugin/core/decompile/actions/RetypeVariableAction.java
+++ b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/plugin/core/decompile/actions/RetypeVariableAction.java
@@ -291,8 +291,8 @@ public class RetypeVariableAction extends AbstractDecompilerAction {
}
for (int i = 0; i < numParams; i++) {
- MappedSymbol param = localSymbolMap.getParamSymbol(i);
- if (param.getSlot() != i) {
+ HighSymbol param = localSymbolMap.getParamSymbol(i);
+ if (param.getCategoryIndex() != i) {
return true;
}
VariableStorage storage = param.getStorage();
diff --git a/Ghidra/Features/Decompiler/src/test.slow/java/ghidra/app/plugin/core/decompile/HighSymbolTest.java b/Ghidra/Features/Decompiler/src/test.slow/java/ghidra/app/plugin/core/decompile/HighSymbolTest.java
new file mode 100644
index 0000000000..204ae4ff85
--- /dev/null
+++ b/Ghidra/Features/Decompiler/src/test.slow/java/ghidra/app/plugin/core/decompile/HighSymbolTest.java
@@ -0,0 +1,106 @@
+/* ###
+ * 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.app.plugin.core.decompile;
+
+import static org.junit.Assert.*;
+
+import java.util.List;
+
+import org.junit.Test;
+
+import docking.widgets.fieldpanel.field.Field;
+import docking.widgets.fieldpanel.support.FieldLocation;
+import ghidra.app.decompiler.ClangToken;
+import ghidra.app.decompiler.ClangVariableToken;
+import ghidra.app.decompiler.component.ClangTextField;
+import ghidra.app.decompiler.component.DecompilerPanel;
+import ghidra.app.plugin.core.decompile.actions.RenameGlobalVariableTask;
+import ghidra.program.database.symbol.CodeSymbol;
+import ghidra.program.model.address.Address;
+import ghidra.program.model.listing.Data;
+import ghidra.program.model.pcode.*;
+
+public class HighSymbolTest extends AbstractDecompilerTest {
+ @Override
+ protected String getProgramName() {
+ return "Winmine__XP.exe.gzf";
+ }
+
+ protected ClangTextField getLineStarting(String val) {
+ DecompilerPanel panel = provider.getDecompilerPanel();
+ List fields = panel.getFields();
+ for (Field field : fields) {
+ ClangTextField textField = (ClangTextField) field;
+ String text = textField.getText().trim();
+ if (text.startsWith(val)) {
+ return textField;
+ }
+ }
+ return null;
+ }
+
+ private void renameGlobalVariable(HighSymbol highSymbol, HighVariable highVar, Varnode exact,
+ String newName) {
+ Address addr = highSymbol.getStorage().getMinAddress();
+ RenameGlobalVariableTask rename = new RenameGlobalVariableTask(provider.getTool(),
+ highSymbol.getName(), addr, highSymbol.getProgram());
+
+ assertTrue(rename.isValid("newGlobal"));
+ modifyProgram(p -> {
+ rename.commit();
+ });
+ }
+
+ @Test
+ public void testHighSymbol_globalRename() {
+
+ decompile("1001b49");
+
+ ClangTextField line = getLineStarting("DAT_010056a0");
+ FieldLocation loc = loc(line.getLineNumber(), 5);
+ ClangToken token = line.getToken(loc);
+ assertTrue(token instanceof ClangVariableToken);
+ HighVariable variable = token.getHighVariable();
+ assertTrue(variable instanceof HighGlobal);
+ HighSymbol highSymbol = variable.getSymbol();
+ assertTrue(highSymbol instanceof HighCodeSymbol);
+ HighCodeSymbol highCode = (HighCodeSymbol) highSymbol;
+ CodeSymbol codeSymbol = highCode.getCodeSymbol();
+ assertNull(codeSymbol); // A DAT_ should not have a permanent CodeSymbol
+ Data data = highCode.getData();
+ assertNotNull(data);
+ assertEquals(data.getAddress().getOffset(), 0x10056a0L);
+ assertEquals(data.getBaseDataType().getLength(), 2);
+
+ renameGlobalVariable(highSymbol, variable, null, "newGlobal");
+ waitForDecompiler();
+ line = getLineStarting("newGlobal");
+ loc = loc(line.getLineNumber(), 5);
+ token = line.getToken(loc);
+ assertTrue(token instanceof ClangVariableToken);
+ variable = token.getHighVariable();
+ assertTrue(variable instanceof HighGlobal);
+ highSymbol = variable.getSymbol();
+ assertTrue(highSymbol instanceof HighCodeSymbol);
+ highCode = (HighCodeSymbol) highSymbol;
+ assertTrue(highCode.isGlobal());
+ assertTrue(highCode.isNameLocked());
+ assertTrue(highCode.isTypeLocked());
+ codeSymbol = highCode.getCodeSymbol();
+ assertNotNull(codeSymbol);
+ assertEquals(codeSymbol.getID(), highCode.getId());
+ }
+}
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/DynamicEntry.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/DynamicEntry.java
new file mode 100644
index 0000000000..7e7f453f50
--- /dev/null
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/DynamicEntry.java
@@ -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("");
+ 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;
+ }
+
+}
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/DynamicSymbol.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/DynamicSymbol.java
deleted file mode 100644
index d60baf4b34..0000000000
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/DynamicSymbol.java
+++ /dev/null
@@ -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("");
- 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("\n");
- res.append(sym);
- buildHashXML(res);
- res.append("\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("\n");
- res.append(dtmanage.buildTypeRef(dt, length));
- res.append("\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);
- }
- }
-}
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/EquateSymbol.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/EquateSymbol.java
index 6c71a1a0d8..bf07755f4d 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/EquateSymbol.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/EquateSymbol.java
@@ -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 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("\n");
- res.append(sym);
- buildHashXML(res);
- res.append("\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("\n");
- res.append(" 0x");
- res.append(Long.toHexString(value));
- res.append("\n");
- res.append("\n");
- return res.toString();
+ buf.append(">\n");
+ buf.append(" 0x");
+ buf.append(Long.toHexString(value));
+ buf.append("\n");
+ buf.append("\n");
}
public static int convertName(String nm,long val) {
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/GlobalSymbolMap.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/GlobalSymbolMap.java
index a7414645ef..aca89b104c 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/GlobalSymbolMap.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/GlobalSymbolMap.java
@@ -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 addrMappedSymbols; // Hashed by addr
- private HashMap 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 addrMappedSymbols; // Look up symbols by address
+ private HashMap 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 getSymbols() {
return symbolMap.values().iterator();
}
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighCodeSymbol.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighCodeSymbol.java
index 9dbdd000f5..09e1f79df6 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighCodeSymbol.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighCodeSymbol.java
@@ -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);
- }
- }
}
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighConstant.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighConstant.java
index c7551c8c27..d17bcb5b66 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighConstant.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighConstant.java
@@ -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);
}
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighExternalSymbol.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighExternalSymbol.java
new file mode 100644
index 0000000000..99ff223616
--- /dev/null
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighExternalSymbol.java
@@ -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(" 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("\n");
+ }
+}
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighFunction.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighFunction.java
index e0e022fd3e..a6c4a104f6 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighFunction.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighFunction.java
@@ -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("\n");
- resBuf.append(Varnode.buildXMLAddress(addr));
- resBuf.append("\n");
- return resBuf.toString();
- }
-
public static ErrorHandler getErrorHandler(final Object errOriginator,
final String targetName) {
return new ErrorHandler() {
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighFunctionDBUtil.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighFunctionDBUtil.java
index 68c8907d9b..8cd3ad2b31 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighFunctionDBUtil.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighFunctionDBUtil.java
@@ -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 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 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 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);
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighFunctionShellSymbol.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighFunctionShellSymbol.java
new file mode 100644
index 0000000000..ecbe46823e
--- /dev/null
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighFunctionShellSymbol.java
@@ -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("\n");
+ buf.append(Varnode.buildXMLAddress(getStorage().getMinAddress()));
+ buf.append("\n");
+ }
+}
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighFunctionSymbol.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighFunctionSymbol.java
new file mode 100644
index 0000000000..db8dc97dc0
--- /dev/null
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighFunctionSymbol.java
@@ -0,0 +1,59 @@
+/* ###
+ * IP: GHIDRA
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package ghidra.program.model.pcode;
+
+import 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);
+ }
+}
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighLabelSymbol.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighLabelSymbol.java
new file mode 100644
index 0000000000..a6f0e9021e
--- /dev/null
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighLabelSymbol.java
@@ -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("\n");
+ }
+}
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighParam.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighParam.java
index 9b9b3b311c..a24d97700f 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighParam.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighParam.java
@@ -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();
}
}
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighSymbol.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighSymbol.java
index c7d644b43c..0b35dfeb6d 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighSymbol.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighSymbol.java
@@ -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("\n");
+ buf.append(dtmanage.buildTypeRef(type, getSize()));
+ buf.append("\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("\n");
- res.append(sym);
- res.append(addrHashRes);
- if (pc == null || pc.isExternalAddress()) {
- res.append("");
+ /**
+ * Restore a full HighSymbol from the next <mapsym> 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 <mapsym> 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("\n");
+ sym.saveXML(res);
+ for (SymbolEntry entry : sym.entryList) {
+ entry.saveXml(res);
}
res.append("\n");
}
-
- public static void buildRangelistXML(StringBuilder res, Address pc) {
- res.append("");
- if (pc != null) {
- AddressSpace space = pc.getAddressSpace();
- if (space.isOverlaySpace()) {
- space = space.getPhysicalSpace();
- pc = space.getAddress(pc.getOffset());
- }
- res.append("");
- }
- res.append("\n");
- }
}
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/LocalSymbolMap.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/LocalSymbolMap.java
index 070116effb..33caa385dd 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/LocalSymbolMap.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/LocalSymbolMap.java
@@ -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 addrMappedSymbols; // Hashed by addr and pcaddr
private HashMap 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();
symbolMap = new HashMap();
- 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 paramList = new ArrayList();
+ List paramList = new ArrayList();
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 PARAM_SYMBOL_SLOT_COMPARATOR =
- new Comparator() {
+ private static final Comparator PARAM_SYMBOL_SLOT_COMPARATOR =
+ new Comparator() {
@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 parms = new ArrayList();
+ ArrayList parms = new ArrayList();
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 iter = symbolMap.values().iterator();
while (iter.hasNext()) {
HighSymbol sym = iter.next();
- res.append(sym.buildXML());
+ HighSymbol.buildMapSymXML(res, sym);
}
res.append("\n");
res.append("\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 constantSymbolMap) {
- DynamicSymbol eqSymbol = constantSymbolMap.get(nm);
+ TreeMap 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 constantSymbolMap = null;
+ TreeMap 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();
+ constantSymbolMap = new TreeMap();
}
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);
}
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/MappedDataEntry.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/MappedDataEntry.java
new file mode 100644
index 0000000000..1dd91afcd2
--- /dev/null
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/MappedDataEntry.java
@@ -0,0 +1,75 @@
+/* ###
+ * IP: GHIDRA
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package ghidra.program.model.pcode;
+
+import 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();
+ }
+}
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/MappedEntry.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/MappedEntry.java
new file mode 100644
index 0000000000..fabb9d33f6
--- /dev/null
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/MappedEntry.java
@@ -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());
+ }
+}
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/MappedSymbol.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/MappedSymbol.java
deleted file mode 100644
index ea75a74707..0000000000
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/MappedSymbol.java
+++ /dev/null
@@ -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("> 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("\n");
- return res.toString();
- }
-}
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/SymbolEntry.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/SymbolEntry.java
new file mode 100644
index 0000000000..8459f4cf71
--- /dev/null
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/SymbolEntry.java
@@ -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("");
+ return;
+ }
+ res.append("");
+ AddressSpace space = pcaddr.getAddressSpace();
+ long off;
+ if (space.isOverlaySpace()) {
+ space = space.getPhysicalSpace();
+ off = space.getAddress(pcaddr.getOffset()).getUnsignedOffset();
+ }
+ else {
+ off = pcaddr.getUnsignedOffset();
+ }
+ res.append("");
+ res.append("\n");
+ }
+}