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"); + } +}