diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/sleighbase.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/sleighbase.cc index 5893acc878..1eb18a07a8 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/sleighbase.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/sleighbase.cc @@ -15,7 +15,48 @@ */ #include "sleighbase.hh" -const int4 SleighBase::SLA_FORMAT_VERSION = 2; +const int4 SleighBase::SLA_FORMAT_VERSION = 3; + +int4 SourceFileIndexer::index(const string filename){ + auto it = fileToIndex.find(filename); + if (fileToIndex.end() != it){ + return it->second; + } + fileToIndex[filename] = leastUnusedIndex; + indexToFile[leastUnusedIndex] = filename; + return leastUnusedIndex++; +} + +int4 SourceFileIndexer::getIndex(string filename){ + return fileToIndex[filename]; +} + +string SourceFileIndexer::getFilename(int4 index){ + return indexToFile[index]; +} + +void SourceFileIndexer::restoreXml(const Element *el){ + const List &sourceFiles(el->getChildren()); + List::const_iterator iter = sourceFiles.begin(); + for (; iter != sourceFiles.end(); ++iter){ + string filename = (*iter)->getAttributeValue("name"); + int4 index = stoi((*iter)->getAttributeValue("index"),NULL,10); + fileToIndex[filename] = index; + indexToFile[index] = filename; + } +} + +void SourceFileIndexer::saveXml(ostream& s) const { + s << "\n"; + for (int4 i = 0; i < leastUnusedIndex; ++i){ + s << ("\n"; + } + s << "\n"; +} + SleighBase::SleighBase(void) @@ -155,7 +196,7 @@ void SleighBase::saveXml(ostream &s) const if (numSections != 0) a_v_u(s,"numsections",numSections); s << ">\n"; - + indexer.saveXml(s); s << "getName()); s << ">\n"; @@ -231,6 +272,8 @@ void SleighBase::restoreXml(const Element *el) floatformats.back().restoreXml(*iter); ++iter; } + indexer.restoreXml(*iter); + iter++; restoreXmlSpaces(*iter,this); iter++; symtab.restoreXml(*iter,this); diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/sleighbase.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/sleighbase.hh index f3c76e2837..97af4cbfbe 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/sleighbase.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/sleighbase.hh @@ -21,7 +21,35 @@ #include "translate.hh" #include "slghsymbol.hh" +/// \brief class for recording source file information for SLEIGH constructors. + +/// +/// A SLEIGH specification may contain many source files. This class is +/// used to associate each constructor in a SLEIGH language to the source +/// file where it is defined. This information is useful when debugging +/// SLEIGH specifications. Sourcefiles are assigned a numeric index and +/// the mapping from indices to filenames is written to the generated .sla +/// file. For each constructor, the data written to the .sla file includes +/// the source file index. +class SourceFileIndexer { +public: + SourceFileIndexer() {leastUnusedIndex = 0;} + ~SourceFileIndexer(void) { } + ///Returns the index of the file. If the file is not in the index it is added. + int4 index(const string filename); + int4 getIndex(const string); ///< get the index of a file. Error if the file is not in the index. + string getFilename(int4); ///< get the filename corresponding to an index + void restoreXml(const Element *el); ///< read a stored index mapping from an XML file + void saveXml(ostream&) const; ///< save the index mapping to an XML file + +private: + int4 leastUnusedIndex; ///< one-up count for assigning indices to files + map indexToFile; ///< map from indices to files + map fileToIndex; ///< map from files to indices +}; + /// \brief Common core of classes that read or write SLEIGH specification files natively. + /// /// This class represents what's in common across the SLEIGH infrastructure between: /// - Reading the various SLEIGH specification files @@ -36,6 +64,7 @@ protected: uint4 maxdelayslotbytes; ///< Maximum number of bytes in a delay-slot directive uint4 unique_allocatemask; ///< Bits that are guaranteed to be zero in the unique allocation scheme uint4 numSections; ///< Number of \e named sections + SourceFileIndexer indexer; ///< source file index used when generating SLEIGH constructor debug info void buildXrefs(vector &errorPairs); ///< Build register map. Collect user-ops and context-fields. void reregisterContext(void); ///< Reregister context fields for a new executable void restoreXml(const Element *el); ///< Read a SLEIGH specification from XML diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/slgh_compile.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/slgh_compile.cc index 474bd7fa1f..ce2bce63c8 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/slgh_compile.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/slgh_compile.cc @@ -2502,6 +2502,8 @@ Constructor *SleighCompile::createConstructor(SubtableSymbol *sym) sym->addConstructor(curct); symtab.addScope(); // Make a new symbol scope for our constructor pcode.resetLabelCount(); + int4 index = indexer.index(ctorLocationMap[curct].getFilename()); + curct->setSrcIndex(index); return curct; } diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/slghsymbol.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/slghsymbol.cc index a600bd8806..5f4a8fe323 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/slghsymbol.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/slghsymbol.cc @@ -1596,7 +1596,7 @@ void Constructor::saveXml(ostream &s) const s << " parent=\"0x" << hex << parent->getId() << "\""; s << " first=\"" << dec << firstwhitespace << "\""; s << " length=\"" << minimumlength << "\""; - s << " line=\"" << lineno << "\">\n"; + s << " line=\"" << src_index << ":" << lineno << "\">\n"; for(int4 i=0;igetId() << "\"/>\n"; for(int4 i=0;i> minimumlength; } { - istringstream s(el->getAttributeValue("line")); - s.unsetf(ios::dec | ios::hex | ios::oct); - s >> lineno; + string src_and_line = el->getAttributeValue("line"); + size_t pos = src_and_line.find(":"); + src_index = stoi(src_and_line.substr(0, pos),NULL,10); + lineno = stoi(src_and_line.substr(pos+1,src_and_line.length()),NULL,10); } const List &list(el->getChildren()); List::const_iterator iter; diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/slghsymbol.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/slghsymbol.hh index 5eed414a59..11aafca1be 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/slghsymbol.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/slghsymbol.hh @@ -477,6 +477,7 @@ class Constructor { // This is NOT a symbol int4 firstwhitespace; // Index of first whitespace piece in -printpiece- int4 flowthruindex; // if >=0 then print only a single operand no markup int4 lineno; + int4 src_index; //source file index mutable bool inerror; // An error is associated with this Constructor void orderOperands(void); public: @@ -491,6 +492,8 @@ public: uintm getId(void) const { return id; } void setLineno(int4 ln) { lineno = ln; } int4 getLineno(void) const { return lineno; } + void setSrcIndex(int4 index) {src_index = index;} + int4 getSrcIndex(void) {return src_index;} void addContext(const vector &vec) { context = vec; } void addOperand(OperandSymbol *sym); void addInvisibleOperand(OperandSymbol *sym); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/plugin/processors/sleigh/Constructor.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/plugin/processors/sleigh/Constructor.java index eb2eaa4a26..4f6c17f528 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/plugin/processors/sleigh/Constructor.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/plugin/processors/sleigh/Constructor.java @@ -26,6 +26,7 @@ import ghidra.app.plugin.processors.sleigh.template.ConstructTpl; import ghidra.app.plugin.processors.sleigh.template.HandleTpl; import ghidra.program.model.lang.UnknownInstructionException; import ghidra.program.model.mem.MemoryAccessException; +import ghidra.util.Msg; import ghidra.util.xml.SpecXmlUtils; import ghidra.xml.XmlElement; import ghidra.xml.XmlPullParser; @@ -55,6 +56,7 @@ public class Constructor implements Comparable { in original (uncompiled) specfile */ private int flowthruindex; + private String sourceFile; public Constructor() { parent = null; @@ -115,14 +117,14 @@ public class Constructor implements Comparable { public String print(ParserWalker walker) throws MemoryAccessException { String res = ""; - for (int i = 0; i < printpiece.length; ++i) { - if (printpiece[i].length() != 0) { - if (printpiece[i].charAt(0) == '\n') { - int index = printpiece[i].charAt(1) - 'A'; + for (String element : printpiece) { + if (element.length() != 0) { + if (element.charAt(0) == '\n') { + int index = element.charAt(1) - 'A'; res += operands[index].print(walker); } else { - res += printpiece[i]; + res += element; } } } @@ -130,7 +132,7 @@ public class Constructor implements Comparable { } public String printSeparator(int separatorIndex) { - + // Separator is all chars to the left of the corresponding operand // The mnemonic (first sequence of contiguous non-space print-pieces) // is ignored when identifying the first separator (index 0) and the @@ -140,7 +142,7 @@ public class Constructor implements Comparable { if (separatorIndex < 0 || separatorIndex > operands.length) { return null; } - + String cachedSeparator = separators[separatorIndex]; if (cachedSeparator != null) { if (cachedSeparator.length() == 0) { @@ -188,11 +190,11 @@ public class Constructor implements Comparable { FixedHandle lastHandle = null; int lastHandleIndex = -1; - for (int i = 0; i < printpiece.length; ++i) { + for (String element : printpiece) { int prevSize = list.size(); - if (printpiece[i].length() != 0) { - if (printpiece[i].charAt(0) == '\n') { - int index = printpiece[i].charAt(1) - 'A'; + if (element.length() != 0) { + if (element.charAt(0) == '\n') { + int index = element.charAt(1) - 'A'; operands[index].printList(walker, list); if (prevSize != list.size() && ++opSymbolCnt == 1) { // Identify sole handle which can be fixed @@ -212,8 +214,9 @@ public class Constructor implements Comparable { } } else { - for (int j = 0; j < printpiece[i].length(); ++j) - list.add(new Character(printpiece[i].charAt(j))); + for (int j = 0; j < element.length(); ++j) { + list.add(new Character(element.charAt(j))); + } } } } @@ -264,8 +267,9 @@ public class Constructor implements Comparable { return res; } } - if (firstwhitespace == -1) + if (firstwhitespace == -1) { return res; // Nothing to print + } for (int i = firstwhitespace + 1; i < printpiece.length; ++i) { if (printpiece[i].length() != 0) { if (printpiece[i].charAt(0) == '\n') { @@ -289,8 +293,8 @@ public class Constructor implements Comparable { */ public void applyContext(ParserWalker walker, SleighDebugLogger debug) throws MemoryAccessException { - for (int i = 0; i < context.length; ++i) { - context[i].apply(walker, debug); + for (ContextChange element : context) { + element.apply(walker, debug); } } @@ -300,10 +304,12 @@ public class Constructor implements Comparable { * @return the named section (or null) */ public ConstructTpl getNamedTempl(int secnum) { - if (namedtempl == null) + if (namedtempl == null) { return null; - if (secnum < namedtempl.size()) + } + if (secnum < namedtempl.size()) { return namedtempl.get(secnum); + } return null; } @@ -316,7 +322,17 @@ public class Constructor implements Comparable { parent = (SubtableSymbol) symtab.findSymbol(myId); firstwhitespace = SpecXmlUtils.decodeInt(el.getAttribute("first")); minimumlength = SpecXmlUtils.decodeInt(el.getAttribute("length")); - lineno = SpecXmlUtils.decodeInt(el.getAttribute("line")); + String sourceAndLine = el.getAttribute("line"); + String[] parts = sourceAndLine.split(":"); + if (parts.length != 2) { + Msg.error(this, "Bad line attribute in .sla file"); + lineno = -1; + sourceFile = "UNKNOWN"; + } + else { + lineno = Integer.parseInt(parts[1].trim()); + sourceFile = sleigh.getSourceFileIndexer().getFileName(Integer.parseInt(parts[0].trim())); + } ArrayList oplist = new ArrayList<>(); ArrayList piecelist = new ArrayList<>(); @@ -354,17 +370,21 @@ public class Constructor implements Comparable { ConstructTpl curtempl = new ConstructTpl(); int sectionid = curtempl.restoreXml(parser, sleigh.getAddressFactory()); if (sectionid < 0) { - if (templ != null) + if (templ != null) { throw new UnknownInstructionException("Duplicate main template section"); + } templ = curtempl; } else { - if (namedtempl == null) + if (namedtempl == null) { namedtempl = new ArrayList<>(); - while (namedtempl.size() <= sectionid) + } + while (namedtempl.size() <= sectionid) { namedtempl.add(null); - if (namedtempl.get(sectionid) != null) + } + if (namedtempl.get(sectionid) != null) { throw new UnknownInstructionException("Duplicate named template section"); + } namedtempl.set(sectionid, curtempl); } } @@ -378,10 +398,12 @@ public class Constructor implements Comparable { context = new ContextChange[coplist.size()]; coplist.toArray(context); if ((printpiece.length == 1) && (printpiece[0].length() >= 2) && - (printpiece[0].charAt(0) == '\n')) + (printpiece[0].charAt(0) == '\n')) { flowthruindex = printpiece[0].charAt(1) - 'A'; - else + } + else { flowthruindex = -1; + } parser.end(el); } @@ -391,18 +413,21 @@ public class Constructor implements Comparable { * @return array of operand indices */ public int[] getOpsPrintOrder() { - if (firstwhitespace == -1) + if (firstwhitespace == -1) { return new int[0]; + } int count = 0; for (int i = firstwhitespace + 1; i < printpiece.length; ++i) { - if (printpiece[i].length() != 0 && printpiece[i].charAt(0) == '\n') + if (printpiece[i].length() != 0 && printpiece[i].charAt(0) == '\n') { count += 1; + } } int[] res = new int[count]; count = 0; for (int i = firstwhitespace + 1; i < printpiece.length; ++i) { - if (printpiece[i].length() != 0 && printpiece[i].charAt(0) == '\n') + if (printpiece[i].length() != 0 && printpiece[i].charAt(0) == '\n') { res[count++] = printpiece[i].charAt(1) - 'A'; + } } return res; } @@ -444,4 +469,12 @@ public class Constructor implements Comparable { } return true; } + + /** + * Returns the source file + * @return source file + */ + public String getSourceFile() { + return sourceFile; + } } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/plugin/processors/sleigh/SleighDebugLogger.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/plugin/processors/sleigh/SleighDebugLogger.java index d397b6df28..7eeaec4132 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/plugin/processors/sleigh/SleighDebugLogger.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/plugin/processors/sleigh/SleighDebugLogger.java @@ -84,8 +84,8 @@ public class SleighDebugLogger { } if (!(language instanceof SleighLanguage)) { - throw new IllegalArgumentException("unsupport language provider: " + - language.getClass().getSimpleName()); + throw new IllegalArgumentException( + "unsupport language provider: " + language.getClass().getSimpleName()); } ContextCache contextCache = new ContextCache(); @@ -112,9 +112,8 @@ public class SleighDebugLogger { } try { - prototype = - new SleighInstructionPrototype((SleighLanguage) language, buf, context, - contextCache, false, this); + prototype = new SleighInstructionPrototype((SleighLanguage) language, buf, context, + contextCache, false, this); prototype.cacheInfo(buf, context, false); @@ -148,8 +147,9 @@ public class SleighDebugLogger { * @throws IllegalArgumentException if program language provider is not Sleigh */ public SleighDebugLogger(Program program, Address start, SleighDebugMode mode) { - this(new MemoryBufferImpl(program.getMemory(), start), new MyProcessorContextView( - program.getProgramContext(), start), program.getLanguage(), mode); + this(new MemoryBufferImpl(program.getMemory(), start), + new MyProcessorContextView(program.getProgramContext(), start), program.getLanguage(), + mode); } private class DebugInstructionContext implements InstructionContext { @@ -240,7 +240,8 @@ public class SleighDebugLogger { if (!"instruction".equals(tableName) || name.startsWith("\n")) { name = tableName; } - list.add(name + "(" + Integer.toString(ct.getLineno()) + ")"); + + list.add(name + "(" + ct.getSourceFile() + ":" + Integer.toString(ct.getLineno()) + ")"); int flowthruindex = ct.getFlowthruIndex(); if (flowthruindex != -1) { @@ -338,8 +339,7 @@ public class SleighDebugLogger { endbit = endbit % 8; for (int i = 0; i < value.length; i++) { - String byteStr = - StringUtilities.pad(Integer.toBinaryString(value[i] & 0xff), '0', 8); + String byteStr = StringUtilities.pad(Integer.toBinaryString(value[i] & 0xff), '0', 8); if (startbit >= 0) { if (endByte == i) { byteStr = @@ -621,8 +621,8 @@ public class SleighDebugLogger { Math.min(contextBaseRegister.getMinimumByteSize() - byteOffset, maskvec.length * 4); byte[] maskPatternValue = new byte[2 * contextBaseRegister.getMinimumByteSize()]; - System.arraycopy(getBytes(valvec), 0, maskPatternValue, (maskPatternValue.length / 2) + - byteOffset, vecByteCnt); + System.arraycopy(getBytes(valvec), 0, maskPatternValue, + (maskPatternValue.length / 2) + byteOffset, vecByteCnt); byte[] mask = getBytes(maskvec); System.arraycopy(mask, 0, maskActualValue, byteOffset, vecByteCnt); @@ -640,9 +640,8 @@ public class SleighDebugLogger { BigInteger actual = childActualValue.getUnsignedValueIgnoreMask(); BigInteger match = childMatchValue.getUnsignedValueIgnoreMask(); String partialMatch = childMatchValue.hasValue() ? "" : "*"; - String matchStr = - match.equals(actual) ? " Match" : (" Failed (=0x" + - Long.toHexString(actual.longValue()) + ")"); + String matchStr = match.equals(actual) ? " Match" + : (" Failed (=0x" + Long.toHexString(actual.longValue()) + ")"); int msb = baseRegSize - reg.getLeastSignificatBitInBaseRegister() - 1; int lsb = msb - reg.getBitLength() + 1; append(partialMatch + reg.getName() + "(" + lsb + "," + msb + ") == 0x" + @@ -719,7 +718,8 @@ public class SleighDebugLogger { dumpGlobalSet(state, num, mask, value, null); } - private void dumpGlobalSet(ConstructState state, int num, int mask, int value, Address setAddr) { + private void dumpGlobalSet(ConstructState state, int num, int mask, int value, + Address setAddr) { byte[] maskActualValue = new byte[contextBaseRegister.getMinimumByteSize() * 2]; int byteOffset = num * 4; @@ -951,8 +951,7 @@ public class SleighDebugLogger { public static String getFormattedBytes(byte[] value) { StringBuffer buf = new StringBuffer(); for (int i = 0; i < value.length; i++) { - String byteStr = - StringUtilities.pad(Integer.toBinaryString(value[i] & 0xff), '0', 8); + String byteStr = StringUtilities.pad(Integer.toBinaryString(value[i] & 0xff), '0', 8); buf.append(byteStr); if (i < (value.length - 1)) { buf.append(" "); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/plugin/processors/sleigh/SleighLanguage.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/plugin/processors/sleigh/SleighLanguage.java index f45f183e38..b5a3be9dfd 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/plugin/processors/sleigh/SleighLanguage.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/plugin/processors/sleigh/SleighLanguage.java @@ -42,6 +42,7 @@ import ghidra.program.model.mem.MemoryAccessException; import ghidra.program.model.symbol.SourceType; import ghidra.program.model.util.AddressLabelInfo; import ghidra.program.model.util.ProcessorSymbolType; +import ghidra.sleigh.grammar.SourceFileIndexer; import ghidra.sleigh.grammar.SleighPreprocessor; import ghidra.util.*; import ghidra.util.task.TaskMonitor; @@ -52,8 +53,18 @@ import utilities.util.FileUtilities; public class SleighLanguage implements Language { - public static final int SLA_FORMAT_VERSION = 2; // What format of the .sla file this expects - // This value should always match SleighBase.SLA_FORMAT_VERSION + /** + * NOTE: The value of {@link SleighLanguage#SLA_FORMAT_VERSION} must match that of + * {@link ghidra.pcodeCPort.sleighbase.SleighBase#SLA_FORMAT_VERSION}!. + *

+ * SLA_FORMAT_VERSION should be incremented whenever the format of the .sla + * files change. + *

+ * Version 3: October 2020: added source file information for each constructor.
+ * Version 2: April 2019: Changed numbering of Overlay spaces.
+ * Version 1: Initial version.
+ */ + public static final int SLA_FORMAT_VERSION = 3; private Map compilerSpecDescriptions; private HashMap compilerSpecs; private List additionalInject = null; @@ -70,6 +81,7 @@ public class SleighLanguage implements Language { private int defaultPointerWordSize = 1; // Default wordsize to send down with pointer data-types private SleighLanguageDescription description; private ParallelInstructionLanguageHelper parallelHelper; + private SourceFileIndexer indexer; //used to provide source file info for constructors /** * Symbols used by sleigh @@ -427,6 +439,14 @@ public class SleighLanguage implements Language { return symtab; } + /** + * Returns the source file indexer + * @return indexer + */ + public SourceFileIndexer getSourceFileIndexer() { + return indexer; + } + @Override public void reloadLanguage(TaskMonitor monitor) throws IOException { reloadLanguage(monitor, false); @@ -663,8 +683,7 @@ public class SleighLanguage implements Language { while (!parser.peek().isEnd()) { element = parser.start("properties", "segmented_address", "segmentop", "programcounter", "data_space", "inferptrbounds", "context_data", "volatile", "jumpassist", - "incidentalcopy", - "register_data", "default_symbols", "default_memory_blocks"); + "incidentalcopy", "register_data", "default_symbols", "default_memory_blocks"); if (element.getName().equals("properties")) { while (!parser.peek().isEnd()) { XmlElement next = parser.start("property"); @@ -907,6 +926,8 @@ public class SleighLanguage implements Language { if (numsecstr != null) { numSections = SpecXmlUtils.decodeInt(numsecstr); } + indexer = new SourceFileIndexer(); + indexer.restoreXml(parser); parseSpaces(parser); symtab = new SymbolTable(); symtab.restoreXml(parser, this); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/pcodeCPort/sleighbase/SleighBase.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/pcodeCPort/sleighbase/SleighBase.java index 7a7cf2e32a..8b0f02b056 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/pcodeCPort/sleighbase/SleighBase.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/pcodeCPort/sleighbase/SleighBase.java @@ -27,14 +27,18 @@ import ghidra.pcodeCPort.space.AddrSpace; import ghidra.pcodeCPort.space.spacetype; import ghidra.pcodeCPort.translate.Translate; import ghidra.pcodeCPort.utils.XmlUtils; +import ghidra.sleigh.grammar.SourceFileIndexer; public abstract class SleighBase extends Translate implements NamedSymbolProvider { // NOTE: restoreXml method removed as it is only used by the decompiler's // implementation - public static final int SLA_FORMAT_VERSION = 2; // What format of the .sla file this produces - // This value should always match SleighLanguage.SLA_FORMAT_VERSION + /** + * Note: The value of {@link SleighBase#SLA_FORMAT_VERSION} must match + * {@link ghidra.app.plugin.processors.sleigh.SleighLanguage#SLA_FORMAT_VERSION}. + */ + public static final int SLA_FORMAT_VERSION = 3; private VectorSTL userop = new VectorSTL<>(); private address_set varnode_xref = new address_set(); // Cross-reference registers by address protected SubtableSymbol root; @@ -42,6 +46,8 @@ public abstract class SleighBase extends Translate implements NamedSymbolProvide protected int maxdelayslotbytes; // Maximum number of bytes in a delayslot directive protected int unique_allocatemask; // Bits that are guaranteed to be zero in the unique allocation scheme protected int numSections; // Number of named sections + protected SourceFileIndexer indexer; //indexer for source files + //used to provide source file info for constructors @Override public SleighSymbol findSymbol(String nm) { @@ -61,6 +67,7 @@ public abstract class SleighBase extends Translate implements NamedSymbolProvide maxdelayslotbytes = 0; unique_allocatemask = 0; numSections = 0; + indexer = new SourceFileIndexer(); } public boolean isInitialized() { @@ -74,18 +81,21 @@ public abstract class SleighBase extends Translate implements NamedSymbolProvide for (iter = glb.begin(); !iter.isEnd(); iter.increment()) { SleighSymbol sym = iter.get(); if (sym.getType() == symbol_type.varnode_symbol) { - Pair, Boolean> res = varnode_xref.insert((VarnodeSymbol) sym); + Pair, Boolean> res = + varnode_xref.insert((VarnodeSymbol) sym); if (!res.second) { errorPairs.add(sym); errorPairs.add(res.first.get()); } - } else if (sym.getType() == symbol_type.userop_symbol) { + } + else if (sym.getType() == symbol_type.userop_symbol) { int index = ((UserOpSymbol) sym).getIndex(); while (userop.size() <= index) { userop.push_back(""); } userop.set(index, sym.getName()); - } else if (sym.getType() == symbol_type.context_symbol) { + } + else if (sym.getType() == symbol_type.context_symbol) { ContextSymbol csym = (ContextSymbol) sym; ContextField field = (ContextField) csym.getPatternValue(); int startbit = field.getStartBit(); @@ -189,13 +199,14 @@ public abstract class SleighBase extends Translate implements NamedSymbolProvide XmlUtils.a_v_u(s, "numsections", numSections); } s.append(">\n"); + indexer.saveXml(s); s.append("\n"); for (int i = 0; i < numSpaces(); ++i) { AddrSpace spc = getSpace(i); - if ((spc.getType() == spacetype.IPTR_CONSTANT) || (spc.getType() == spacetype.IPTR_FSPEC) - || (spc.getType() == spacetype.IPTR_IOP)) { + if ((spc.getType() == spacetype.IPTR_CONSTANT) || + (spc.getType() == spacetype.IPTR_FSPEC) || (spc.getType() == spacetype.IPTR_IOP)) { continue; } spc.saveXml(s); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/pcodeCPort/slgh_compile/SleighCompile.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/pcodeCPort/slgh_compile/SleighCompile.java index ad779e50bc..4e5bb2d993 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/pcodeCPort/slgh_compile/SleighCompile.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/pcodeCPort/slgh_compile/SleighCompile.java @@ -381,8 +381,7 @@ public class SleighCompile extends SleighBase { final int symSize = sym.getSize(); if (symSize % 4 != 0) { reportError(sym.location, - String.format( - "Invalid size of context register '%s' (%d); must be a multiple of 4", + String.format("Invalid size of context register '%s' (%d); must be a multiple of 4", sym.getName(), symSize)); } final int maxBits = symSize * 8 - 1; @@ -563,8 +562,7 @@ public class SleighCompile extends SleighBase { noCollisions = false; if (warnalllocalcollisions) { reportWarning(ct.location, - String.format( - "Possible operand collision between symbols '%s' and '%s'", + String.format("Possible operand collision between symbols '%s' and '%s'", ct.getOperand(collideOperand).getName(), ct.getOperand(i).getName())); } @@ -649,7 +647,6 @@ public class SleighCompile extends SleighBase { pcode.resetLabelCount(); } - public void reportError(Location location, String msg) { entry("reportError", location, msg); Msg.error(this, MessageFormattingUtils.format(location, msg)); @@ -922,9 +919,8 @@ public class SleighCompile extends SleighBase { insertSpace(spc); if (qual.isdefault) { if (getDefaultSpace() != null) { - reportError(location, - "Multiple default spaces -- '" + getDefaultSpace().getName() + "', '" + - qual.name + "'"); + reportError(location, "Multiple default spaces -- '" + getDefaultSpace().getName() + + "', '" + qual.name + "'"); } else { setDefaultSpace(spc.getIndex()); // Make the flagged space @@ -1331,6 +1327,10 @@ public class SleighCompile extends SleighBase { sym.addConstructor(curct); symtab.addScope(); // Make a new symbol scope for our constructor pcode.resetLabelCount(); + Integer index = indexer.index(location); + if (index != null) { + curct.setSourceFileIndex(index); + } return curct; } @@ -1452,8 +1452,7 @@ public class SleighCompile extends SleighBase { big.markSubtableOperands(check); Pair res = cur.section.fillinBuild(check, getConstantSpace()); if (res.first == 1) { - myErrors.push_back( - scopeString + "Duplicate BUILD statements at " + res.second); + myErrors.push_back(scopeString + "Duplicate BUILD statements at " + res.second); } if (res.first == 2) { myErrors.push_back( @@ -1461,8 +1460,7 @@ public class SleighCompile extends SleighBase { } if (!pcode.propagateSize(cur.section)) { - myErrors.push_back( - scopeString + "Could not resolve at least 1 variable size"); + myErrors.push_back(scopeString + "Could not resolve at least 1 variable size"); } } if (i < 0) { // These potential errors only apply to main section diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/pcodeCPort/slghsymbol/Constructor.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/pcodeCPort/slghsymbol/Constructor.java index 69b52932a9..f61bce837c 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/pcodeCPort/slghsymbol/Constructor.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/pcodeCPort/slghsymbol/Constructor.java @@ -48,6 +48,7 @@ public class Constructor { private int firstwhitespace; // Index of first whitespace piece in -printpiece- private int flowthruindex; // if >=0 then print only a single operand no markup private boolean inerror; + private int sourceFileIndex = -1; //source file index public TokenPattern getPattern() { return pattern; @@ -77,6 +78,22 @@ public class Constructor { return location == null ? 0 : location.lineno; } + /** + * Set the source file index + * @param index index + */ + public void setSourceFileIndex(int index) { + sourceFileIndex = index; + } + + /** + * Return the source file index + * @return index + */ + public int getIndex() { + return sourceFileIndex; + } + public void addContext(VectorSTL vec) { context = vec; } @@ -359,6 +376,8 @@ public class Constructor { s.print(minimumlength); s.append("\""); s.append(" line=\""); + s.print(sourceFileIndex); + s.append(":"); s.print(getLineno()); s.append("\">\n"); for (int i = 0; i < operands.size(); ++i) { diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/sleigh/grammar/SourceFileIndexer.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/sleigh/grammar/SourceFileIndexer.java new file mode 100644 index 0000000000..00fea56d76 --- /dev/null +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/sleigh/grammar/SourceFileIndexer.java @@ -0,0 +1,118 @@ +/* ### + * 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.sleigh.grammar; + +import java.io.PrintStream; + +import com.google.common.collect.BiMap; +import com.google.common.collect.HashBiMap; + +import ghidra.pcodeCPort.utils.XmlUtils; +import ghidra.util.Msg; +import ghidra.xml.XmlElement; +import ghidra.xml.XmlPullParser; + +/** + * This class is used to index source files in a SLEIGH language module. + * The SLEIGH compiler records the index of the source file for a constructor rather + * than the file name. This is an optimization to avoid repeating the file name in + * the .sla files. + */ +public class SourceFileIndexer { + + private BiMap filenameToIndex; + private int leastUnusedIndex; + + /** + * Creates a {code SourceFileIndexer} object with an empty index. + */ + public SourceFileIndexer() { + filenameToIndex = HashBiMap.create(); + leastUnusedIndex = 0; + } + + /** + * Adds the filename of a location to the index if it is not already present. + * @param loc location containing filename to add + * @return index associated with filename, or {@code null} if a {@code null} {@link Location} + * or a {@link Location} with a {@code null} filename was provided as input. + */ + public Integer index(Location loc) { + if (loc == null) { + Msg.info(this, "null Location"); + return null; + } + String filename = loc.filename; + if (filename == null) { + Msg.info(this, "null filename"); + return null; + } + Integer res = filenameToIndex.putIfAbsent(filename, leastUnusedIndex); + if (res == null) { + return leastUnusedIndex++; + } + return res; + } + + /** + * Returns the index for a filename + * @param filename file + * @return index or {@code null} if {@code filename} is not in the index. + */ + public Integer getIndex(String filename) { + return filenameToIndex.get(filename); + } + + /** + * Returns the file name at a given index + * @param index index + * @return file name or {@code null} if there is no file with that index + */ + public String getFileName(Integer index) { + return filenameToIndex.inverse().get(index); + } + + /** + * Save the index as XML + * @param s stream to write to + */ + public void saveXml(PrintStream s) { + s.append("\n"); + for (int i = 0; i < leastUnusedIndex; ++i) { + s.append("\n"); + } + s.append("\n"); + } + + /** + * Restore an index saved as to XML + * @param parser xml parser + */ + public void restoreXml(XmlPullParser parser) { + XmlElement elem = parser.start("sourcefiles"); + XmlElement subElem = null; + while ((subElem = parser.softStart("sourcefile")) != null) { + String filename = subElem.getAttribute("name"); + Integer index = Integer.parseInt(subElem.getAttribute("index")); + filenameToIndex.put(filename, index); + parser.end(subElem); + } + parser.end(elem); + } + +} diff --git a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/sleigh/grammar/SourceFileIndexerTest.java b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/sleigh/grammar/SourceFileIndexerTest.java new file mode 100644 index 0000000000..f4191422ab --- /dev/null +++ b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/sleigh/grammar/SourceFileIndexerTest.java @@ -0,0 +1,63 @@ +/* ### + * 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.sleigh.grammar; + +import static org.junit.Assert.*; + +import org.junit.Test; + +import generic.test.AbstractGenericTest; + +public class SourceFileIndexerTest extends AbstractGenericTest { + + @Test + public void basicTest() { + Location loc1_1 = new Location("file1", 1); + Location loc1_2 = new Location("file1", 2); + Location loc2_1 = new Location("file2", 11); + Location loc2_2 = new Location("file2", 22); + Location loc3_1 = new Location("file3", 111); + Location loc3_2 = new Location("file3", 222); + Location nullLocation = null; + Location nullFilename = new Location(null, 1000); + + SourceFileIndexer indexer = new SourceFileIndexer(); + indexer.index(loc1_1); + indexer.index(loc1_2); + indexer.index(loc3_2); + int ret2_1 = indexer.index(loc2_1); + indexer.index(nullLocation); + indexer.index(loc3_1); + indexer.index(nullFilename); + int ret2_2 = indexer.index(loc2_2); + + assertEquals(ret2_1, ret2_2); + + int file1_index = indexer.getIndex(loc1_1.filename); + assertEquals(file1_index, indexer.getIndex(loc1_2.filename).intValue()); + + int file2_index = indexer.getIndex(loc2_1.filename); + assertEquals(file2_index, indexer.getIndex(loc2_2.filename).intValue()); + + int file3_index = indexer.getIndex(loc3_1.filename); + assertEquals(file3_index, indexer.getIndex(loc3_2.filename).intValue()); + + assertNotEquals(file1_index, file2_index); + assertNotEquals(file1_index, file3_index); + assertNotEquals(file2_index, file3_index); + } + +}