diff --git a/Ghidra/Features/Base/ghidra_scripts/GraphClassesScript.java b/Ghidra/Features/Base/ghidra_scripts/GraphClassesScript.java index f0bec607e1..cf88d951f9 100644 --- a/Ghidra/Features/Base/ghidra_scripts/GraphClassesScript.java +++ b/Ghidra/Features/Base/ghidra_scripts/GraphClassesScript.java @@ -16,7 +16,8 @@ //Script to graph class hierarchies given metadata found in class structure description that // was applied using the RecoverClassesFromRTTIScript. //@category C++ -import java.util.*; +import java.util.ArrayList; +import java.util.List; import ghidra.app.script.GhidraScript; import ghidra.app.services.GraphDisplayBroker; @@ -46,7 +47,7 @@ public class GraphClassesScript extends GhidraScript { Category category = dataTypeManager.getCategory(dataTypePath); if (category == null) { println( - "/ClassDataTypes folder does not exist so there is no class data to process. Please run the ExtractClassInfoFromRTTIScript to generate the necessary information needed to run this script."); + "/ClassDataTypes folder does not exist so there is no class data to process. Please run the RecoverClassesFromRTTIScript to generate the necessary information needed to run this script."); return; } @@ -54,6 +55,11 @@ public class GraphClassesScript extends GhidraScript { getClassStructures(subCategories); + if (classStructures.isEmpty()) { + println("There were no class structures to process."); + return; + } + AttributedGraph graph = createGraph(); if (graph.getVertexCount() == 0) { println( @@ -93,43 +99,47 @@ public class GraphClassesScript extends GhidraScript { } } - private AttributedGraph createGraph() throws CancelledException { + /** + * Method to create a graph using preconfigured information found in class structure descriptions. + * The structure descriptions are created using + * {@link RecoveredClassUtils#createParentStringBuffer(RecoveredClass)} + * @return the newly created graph + */ + private AttributedGraph createGraph() throws Exception { AttributedGraph g = new AttributedGraph(); - Iterator classStructuresIterator = classStructures.iterator(); - while (classStructuresIterator.hasNext()) { + for (Structure classStructure : classStructures) { monitor.checkCanceled(); - Structure classStructure = classStructuresIterator.next(); - String description = classStructure.getDescription(); - String mainClassName = getClassName(description); - if (mainClassName == null) { + // parse description for class hierarchy + if (!description.startsWith("class")) { continue; } - AttributedVertex classVertex = g.addVertex(mainClassName); + // skip "class " to get overall class + description = description.substring(6); + String mainClassName = getClassName(description); + + if (mainClassName == null || mainClassName.isBlank()) { + continue; + } + + AttributedVertex classVertex = + g.addVertex(classStructure.getCategoryPath().getPath(), mainClassName); + classVertex.setDescription(classStructure.getCategoryPath().getPath()); int numParents = 0; - while (description.contains(":")) { + description = removeClassSubstring(description, mainClassName); + + while (description != null) { numParents++; - int indexOfColon = description.indexOf(":", 0); - - description = description.substring(indexOfColon + 1); - - int endOfBlock = description.indexOf(":", 0); - if (endOfBlock == -1) { - endOfBlock = description.length(); - } - - String parentName = description.substring(0, endOfBlock); - - description = description.substring(endOfBlock); + String parentName = getClassName(description); boolean isVirtualParent = false; if (parentName.contains("virtual")) { @@ -139,14 +149,38 @@ public class GraphClassesScript extends GhidraScript { parentName = parentName.replace("virtual", ""); parentName = parentName.replace(" ", ""); + // first try to get parent structure from inside child structure + Structure parentStructure = + getParentStructureFromChildStructure(classStructure, parentName); - AttributedVertex parentVertex = g.addVertex(parentName); + // if parent structure isn't in child structure then try to get it by name + // from the list of class structures - only returns one if unique + if (parentStructure == null) { + parentStructure = getParentStructureFromClassStructures(parentName); + } + + AttributedVertex parentVertex; + if (parentStructure == null) { + parentVertex = g.addVertex(parentName); + parentVertex.setDescription("Couldn't get parent structure " + parentName + + " from structure " + classStructure.getName() + + " or uniquely from all class structures"); + println("Couldn't get parent structure " + parentName + " from structure " + + classStructure.getName() + " or uniquely from all class structures"); + } + else { + parentVertex = + g.addVertex(parentStructure.getCategoryPath().getPath(), parentName); + parentVertex.setDescription(parentStructure.getCategoryPath().getPath()); + } AttributedEdge edge = g.addEdge(parentVertex, classVertex); if (isVirtualParent) { edge.setAttribute("Color", "Orange"); } // else leave it default lime green + + description = removeClassSubstring(description, parentName); } // no parent = blue vertex @@ -166,6 +200,94 @@ public class GraphClassesScript extends GhidraScript { return g; } + private String removeClassSubstring(String string, String substring) { + + int indexofSubstring = string.indexOf(substring); + if (indexofSubstring == -1) { + return null; + } + + if (indexofSubstring + substring.length() >= string.length()) { + return null; + } + + String newString = string.substring(indexofSubstring + substring.length()); + if (newString.isBlank() || newString.isEmpty()) { + return null; + } + + // should be a space : space and another class name next if gets to here + if (newString.length() < 4) { + return null; + } + + if (newString.indexOf(" : ") == 0) { + return newString.substring(3); + } + + return null; + + } + + private int getIndexOfFirstSingleColon(String string) { + + // replace all :: with something else so can isolate :'s + String testString = new String(string); + testString = testString.replace("::", "xx"); + + return testString.indexOf(":", 0); + + } + + /** + * Attempts to get the parent structure from within the child structure given the parent name + * @param childStructure the child structure + * @param parentName the name of the parent structure + * @return the parent structure or null if the parent structure is not contained in the child structure + * @throws CancelledException if cancelled + */ + private Structure getParentStructureFromChildStructure(Structure childStructure, + String parentName) + throws CancelledException { + + DataTypeComponent[] components = childStructure.getComponents(); + for (DataTypeComponent component : components) { + + monitor.checkCanceled(); + DataType componentDataType = component.getDataType(); + if (componentDataType instanceof Structure && + componentDataType.getName().equals(parentName)) { + return (Structure) componentDataType; + } + } + return null; + } + + /** + * Attempts to get the parent structure from the list of class structures + * @param parentName the name of the parent + * @return the parent structure if there is only one with the given name, else returns null + * @throws CancelledException if cancelled + */ + private Structure getParentStructureFromClassStructures(String parentName) + throws CancelledException { + + List parentStructures = new ArrayList(); + for (Structure classStructure : classStructures) { + monitor.checkCanceled(); + + if (classStructure.getName().equals(parentName)) { + parentStructures.add(classStructure); + } + + } + if (parentStructures.size() == 1) { + return parentStructures.get(0); + } + return null; + + } + private void showGraph(AttributedGraph graph) throws Exception { GraphDisplay display; @@ -176,26 +298,20 @@ public class GraphClassesScript extends GhidraScript { display.setGraph(graph, "test graph", false, TaskMonitor.DUMMY); } + private String getClassName(String description) { - // parse description for class hierarchy - if (!description.startsWith("class")) { - return null; - } - - // skip "class " to get overall class - description = description.substring(6); - int indexOfColon = description.indexOf(":", 0); - String mainClassName; + int indexOfColon = getIndexOfFirstSingleColon(description); + String firstClassName; if (indexOfColon == -1) { - mainClassName = description; + firstClassName = description; } else { - mainClassName = description.substring(0, indexOfColon - 1); + firstClassName = description.substring(0, indexOfColon - 1); } - mainClassName = mainClassName.replace(" ", ""); + firstClassName = firstClassName.replace(" ", ""); - return mainClassName; + return firstClassName; } } diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/map/AddressKeyIteratorTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/map/AddressKeyIteratorTest.java index e6af93cbb0..aa0403e16f 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/map/AddressKeyIteratorTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/map/AddressKeyIteratorTest.java @@ -115,7 +115,7 @@ public class AddressKeyIteratorTest extends AbstractGhidraHeadedIntegrationTest @Test public void testIterator0() throws Exception { - AddressKeyIterator it = new AddressKeyIterator(); + AddressKeyIterator it = AddressKeyIterator.EMPTY_ITERATOR; assertTrue(!it.hasNext()); assertTrue(!it.hasPrevious()); try { @@ -123,12 +123,14 @@ public class AddressKeyIteratorTest extends AbstractGhidraHeadedIntegrationTest Assert.fail(); } catch (NoSuchElementException e) { + // expected } try { it.previous(); Assert.fail(); } catch (NoSuchElementException e) { + // expected } } diff --git a/Ghidra/Features/Decompiler/ghidra_scripts/RecoverClassesFromRTTIScript.java b/Ghidra/Features/Decompiler/ghidra_scripts/RecoverClassesFromRTTIScript.java index d36d009e69..f3f04cb1fc 100644 --- a/Ghidra/Features/Decompiler/ghidra_scripts/RecoverClassesFromRTTIScript.java +++ b/Ghidra/Features/Decompiler/ghidra_scripts/RecoverClassesFromRTTIScript.java @@ -114,7 +114,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript { // multiple parents = red vertex // edge between child and parent is orange if child inherits the parent virtually // edge between child and parent is lime green if child inherits the parent non-virtually - private static final boolean GRAPH_CLASS_HIERARCHIES = false; + private static final boolean GRAPH_CLASS_HIERARCHIES = true; // show shortened class template names in class structure field names private static final boolean USE_SHORT_TEMPLATE_NAMES_IN_STRUCTURE_FIELDS = true; @@ -342,7 +342,8 @@ public class RecoverClassesFromRTTIScript extends GhidraScript { RecoveredClass recoveredClass = recoveredClassIterator.next(); - AttributedVertex classVertex = g.addVertex(recoveredClass.getName()); + AttributedVertex classVertex = + g.addVertex(recoveredClass.getClassPath().getPath(), recoveredClass.getName()); Map> classHierarchyMap = recoveredClass.getClassHierarchyMap(); @@ -350,6 +351,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript { // no parent = blue vertex if (classHierarchyMap.isEmpty()) { classVertex.setAttribute("Color", "Blue"); + classVertex.setDescription(recoveredClass.getClassPath().getPath()); continue; } @@ -364,6 +366,8 @@ public class RecoverClassesFromRTTIScript extends GhidraScript { classVertex.setAttribute("Color", "Red"); } + classVertex.setDescription(recoveredClass.getClassPath().getPath()); + Map parentToBaseTypeMap = recoveredClass.getParentToBaseTypeMap(); @@ -372,7 +376,10 @@ public class RecoverClassesFromRTTIScript extends GhidraScript { monitor.checkCanceled(); RecoveredClass parent = parentIterator.next(); - AttributedVertex parentVertex = g.addVertex(parent.getName()); + AttributedVertex parentVertex = + g.addVertex(parent.getClassPath().getPath(), parent.getName()); + + parentVertex.setDescription(parent.getClassPath().getPath()); AttributedEdge edge = g.addEdge(parentVertex, classVertex); diff --git a/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RecoveredClassUtils.java b/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RecoveredClassUtils.java index bef192897a..7331621216 100644 --- a/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RecoveredClassUtils.java +++ b/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RecoveredClassUtils.java @@ -3196,9 +3196,15 @@ public class RecoveredClassUtils { } /** - * Method to create a string buffer containing class parents in the corrector order + * Method to create a string buffer containing class parents in the correct order. The format + * of the parent string is of the format "class : : ... + * where parentN_spec = "virtual (only if inherited virtually) " + * Examples: + * The class Pet with no parents would be "class Pet" + * The class Cat with non-virtual parent Pet would be "class Cat : Pet" + * The class A with virtual parent B and non-virtual parent C would be "class A : virtual B : C" * @param recoveredClass the given class - * @return StringBuffer containing class parents + * @return StringBuffer containing class parent description * @throws CancelledException if cancelled */ public StringBuffer createParentStringBuffer(RecoveredClass recoveredClass) @@ -4442,6 +4448,7 @@ public class RecoveredClassUtils { recoveredClass.getName(), defaultPointerSize, dataTypeManager); } + // create a description indicating class parentage classStruct.setDescription(createParentStringBuffer(recoveredClass).toString()); classStruct = (Structure) dataTypeManager.addDataType(classStruct, diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/op.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/op.cc index d80da176cd..af89c583ac 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/op.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/op.cc @@ -415,8 +415,9 @@ void PcodeOp::saveXml(ostream &s) const else if (vn->getSpace()->getType()==IPTR_CONSTANT) { if ((i==0)&&((code()==CPUI_STORE)||(code()==CPUI_LOAD))) { AddrSpace *spc = Address::getSpaceFromConst(vn->getAddr()); - s << "getName() << "\"/>\n"; + s << "getName()); + s << "/>\n"; } else s << "getCreateIndex() << "\"/>\n"; diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/prettyprint.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/prettyprint.cc index 4b687b53f1..fac4c0ca13 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/prettyprint.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/prettyprint.cc @@ -259,8 +259,9 @@ void EmitXml::tagField(const char *ptr,syntax_highlight hl,const Datatype *ct,in void EmitXml::tagComment(const char *ptr,syntax_highlight hl, const AddrSpace *spc,uintb off) { *s << "getName(); - *s << "\" off=\"0x" << hex << off << "\">"; + a_v(*s,"space",spc->getName()); + a_v_u(*s,"off",off); + *s << '>'; xml_escape(*s,ptr); *s << ""; } @@ -276,8 +277,9 @@ void EmitXml::tagComment(const char *ptr,syntax_highlight hl, void EmitXml::tagLabel(const char *ptr,syntax_highlight hl, const AddrSpace *spc,uintb off) { *s << ""; } diff --git a/Ghidra/Features/Decompiler/src/decompile/datatests/convert.xml b/Ghidra/Features/Decompiler/src/decompile/datatests/convert.xml index 7c9823c426..467bd18f80 100644 --- a/Ghidra/Features/Decompiler/src/decompile/datatests/convert.xml +++ b/Ghidra/Features/Decompiler/src/decompile/datatests/convert.xml @@ -62,5 +62,5 @@ ffffffbfdff7ffffe811ffffffbf6100 recv_signed\(-0b1100110011001100\) recv_unsigned\(0b1110111011101110\) recv_unsigned\(0b11111111111111111111011111011111\) -recv_signed\('a'\) +recv_signed\(L'a'\) 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 db679bc9be..9311adf11b 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 @@ -1433,7 +1433,7 @@ public class SleighLanguage implements Language { if ((element instanceof OverlayAddressSpace)) { OverlayAddressSpace ospace = (OverlayAddressSpace) element; resBuf.append(" 0xfff).} * - * @param set the address set to look within. + * @param set the address set to look within (required). * @param monitor the current monitor. * @return Data the first undefined data within the address set, or null if there is none. */ public Data getFirstUndefinedData(AddressSetView set, TaskMonitor monitor) { + if (set.isEmpty()) { + return null; + } Memory mem = program.getMemory(); set = mem.intersect(set); + if (set.isEmpty()) { + return null; + } int i = 0; CodeUnitIterator it = getCodeUnits(set, true); @@ -2172,8 +2193,7 @@ public class CodeManager implements ErrorHandler, ManagerDB { private boolean exceedsLimitOn64BitAddressSegments(List
longSegmentAddressList, Address toAddr) { long maskedOffset = toAddr.getOffset() & 0xffffffff00000000L; - for (int i = 0; i < longSegmentAddressList.size(); i++) { - Address address = longSegmentAddressList.get(i); + for (Address address : longSegmentAddressList) { long offset = address.getOffset(); if ((offset & 0xffffffff00000000L) == maskedOffset) { return false; @@ -2419,6 +2439,7 @@ public class CodeManager implements ErrorHandler, ManagerDB { * @param forward if true the iterator returns all codeUnits from the given * start address to the end of the program, otherwise it returns all codeUnits * from the given start address to the start of the program. + * @return code unit iterator */ public CodeUnitIterator getCodeUnits(Address start, boolean forward) { @@ -2439,6 +2460,10 @@ public class CodeManager implements ErrorHandler, ManagerDB { bounds = program.getAddressFactory().getAddressSet(min, start); } + AddressSet set = mem.intersect(bounds); + if (set.isEmpty()) { + return CodeUnitIterator.EMPTY_ITERATOR; + } return new CodeUnitRecordIterator(this, getInstructions(start, forward), getDefinedData(start, forward), mem.intersect(bounds), forward); } @@ -2447,10 +2472,15 @@ public class CodeManager implements ErrorHandler, ManagerDB { * Returns an iterator over all codeUnits in the given addressSet. The iterator * will go from the lowest address to the largest or from the largest to the * lowest depending on the forward parameter. + * @param set the memory address set over which code units should be iterated (required) * @param forward determines if the iterator goes from lowest address to highest * or the other way around. + * @return code unit iterator */ public CodeUnitIterator getCodeUnits(AddressSetView set, boolean forward) { + if (set.isEmpty()) { + return CodeUnitIterator.EMPTY_ITERATOR; + } return new CodeUnitRecordIterator(this, getInstructions(set, forward), getDefinedData(set, forward), set, forward); } @@ -2677,9 +2707,8 @@ public class CodeManager implements ErrorHandler, ManagerDB { } } } - for (int i = 0; i < addrs.size(); i++) { + for (Address addr : addrs) { monitor.checkCanceled(); - Address addr = addrs.get(i); clearCodeUnits(addr, addr, false, monitor); } } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/CodeUnitRecordIterator.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/CodeUnitRecordIterator.java index f0be23e6c9..f0c3916fdd 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/CodeUnitRecordIterator.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/CodeUnitRecordIterator.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,11 +15,11 @@ */ package ghidra.program.database.code; +import java.util.Iterator; + import ghidra.program.model.address.*; import ghidra.program.model.listing.*; -import java.util.Iterator; - /** * Combines an Instruction iterator and Data iterator into a codeunit iterator */ @@ -42,7 +41,7 @@ class CodeUnitRecordIterator implements CodeUnitIterator { * @param codeMgr the code managaer * @param instIt the instruction iterator * @param dataIt the data iterator - * @param set the address set + * @param set the address set (required) * @param forward the iterator direction */ CodeUnitRecordIterator(CodeManager codeMgr, InstructionIterator instIt, DataIterator dataIt, diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/CommentsDBAdapter.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/CommentsDBAdapter.java index 52ec0066a9..6a444ab350 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/CommentsDBAdapter.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/CommentsDBAdapter.java @@ -235,7 +235,7 @@ abstract class CommentsDBAdapter { /** * Returns an address key iterator over the given address set in the given direction. - * @param addrSetView the set to iterator over. + * @param addrSetView the set to iterator over (null for all defined memory). * @param forward the direction to iterate. */ abstract AddressKeyIterator getKeys(AddressSetView set, boolean forward) throws IOException; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/map/AddressIndexKeyIterator.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/map/AddressIndexKeyIterator.java index 1b4b1fe178..2654b2fa27 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/map/AddressIndexKeyIterator.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/map/AddressIndexKeyIterator.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,13 +15,12 @@ */ package ghidra.program.database.map; -import ghidra.program.model.address.*; - import java.io.IOException; import java.util.List; import java.util.NoSuchElementException; import db.*; +import ghidra.program.model.address.*; /** * Iterator of indexed fields that are addresses. The longs returned are the address longs. @@ -95,7 +93,7 @@ public class AddressIndexKeyIterator implements DBLongIterator { * @param addrMap the address map * @param absolute if true, only absolute memory address encodings are considered, otherwise * only standard/relocatable address encodings are considered. - * @param set the set of addresses to iterator over. + * @param set the set of addresses to iterator over or null for all addresses. * @param atStart if true, iterates forward, otherwise iterates backwards. * @throws IOException if a database io error occurs. */ @@ -192,6 +190,7 @@ public class AddressIndexKeyIterator implements DBLongIterator { /** * @see db.DBLongIterator#hasNext() */ + @Override public boolean hasNext() throws IOException { if (it == null) { return false; @@ -217,6 +216,7 @@ public class AddressIndexKeyIterator implements DBLongIterator { /** * @see db.DBLongIterator#hasPrevious() */ + @Override public boolean hasPrevious() throws IOException { if (it == null) { return false; @@ -242,6 +242,7 @@ public class AddressIndexKeyIterator implements DBLongIterator { /** * @see db.DBLongIterator#next() */ + @Override public long next() throws IOException { if (hasNext()) { return ((LongField) it.next()).getLongValue(); @@ -252,6 +253,7 @@ public class AddressIndexKeyIterator implements DBLongIterator { /** * @see db.DBLongIterator#previous() */ + @Override public long previous() throws IOException { if (hasPrevious()) { return ((LongField) it.previous()).getLongValue(); @@ -262,6 +264,7 @@ public class AddressIndexKeyIterator implements DBLongIterator { /** * @see db.DBLongIterator#delete() */ + @Override public boolean delete() throws IOException { if (it != null) { return it.delete(); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/map/AddressIndexPrimaryKeyIterator.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/map/AddressIndexPrimaryKeyIterator.java index 6304f736f8..38fd096d83 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/map/AddressIndexPrimaryKeyIterator.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/map/AddressIndexPrimaryKeyIterator.java @@ -93,7 +93,7 @@ public class AddressIndexPrimaryKeyIterator implements DBFieldIterator { * @param addrMap the address map * @param absolute if true, only absolute memory address encodings are considered, otherwise * only standard/relocatable address encodings are considered. - * @param set the set of addresses to iterator over. + * @param set the set of addresses to iterator over or null for all addresses. * @param atStart if true, iterates forward, otherwise iterates backwards. * @throws IOException if a database io error occurs. */ diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/map/AddressKeyAddressIterator.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/map/AddressKeyAddressIterator.java index 6a61eddee4..e9f1227e25 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/map/AddressKeyAddressIterator.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/map/AddressKeyAddressIterator.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,15 +15,14 @@ */ package ghidra.program.database.map; -import ghidra.program.model.address.Address; -import ghidra.program.model.address.AddressIterator; - import java.io.IOException; import java.util.Iterator; import java.util.NoSuchElementException; import db.DBLongIterator; import db.util.ErrorHandler; +import ghidra.program.model.address.Address; +import ghidra.program.model.address.AddressIterator; /** * Converts an AddressKeyIterator or an addressKeyAddressIterator into an AddressIterator @@ -39,6 +37,7 @@ public class AddressKeyAddressIterator implements AddressIterator { /** * Constructor. * @param keyIter address key iterator, may be null. All long values must decode properly with the specified addrMap. + * @param forward true to iterate in the direction of increasing addresses. * @param addrMap address map * @param errHandler IO error handler (may be null) */ diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/map/AddressKeyIterator.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/map/AddressKeyIterator.java index 47678c2ad1..e58fad5acd 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/map/AddressKeyIterator.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/map/AddressKeyIterator.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,14 +15,13 @@ */ package ghidra.program.database.map; -import ghidra.program.model.address.*; - import java.io.IOException; import java.util.List; import java.util.NoSuchElementException; import db.DBLongIterator; import db.Table; +import ghidra.program.model.address.*; /** * Iterator of primary keys that are addresses. The longs returned are the address longs. @@ -31,6 +29,8 @@ import db.Table; public class AddressKeyIterator implements DBLongIterator { + public static final AddressKeyIterator EMPTY_ITERATOR = new AddressKeyIterator(); + private Table table; private List keyRangeList; @@ -40,7 +40,7 @@ public class AddressKeyIterator implements DBLongIterator { /** * Constructs an empty iterator. */ - public AddressKeyIterator() { + private AddressKeyIterator() { } /** @@ -96,7 +96,7 @@ public class AddressKeyIterator implements DBLongIterator { * Memory addresses encoded as Absolute are not included. * @param table the database table key by addresses * @param addrMap the address map - * @param set the address set to iterator over + * @param set the address set to iterator over (may be null for all defined memory) * @param startAddr the address at which to position the iterator, can be null. The exact * position of the iterator depends on the before parameter. * @param before positions the iterator before the start address,otherwise after @@ -115,7 +115,7 @@ public class AddressKeyIterator implements DBLongIterator { * @param addrMap the address map * @param absolute if true, only absolute memory address encodings are considered, otherwise * only standard/relocatable address encodings are considered. - * @param set the address set to iterator over + * @param set the address set to iterator over or null for all addresses. * @param startAddr the address at which to position the iterator, can be null. The exact * position of the iterator depends on the before parameter. * @param before positions the iterator before the start address,otherwise after @@ -180,6 +180,7 @@ public class AddressKeyIterator implements DBLongIterator { /** * @see db.DBLongIterator#hasNext() */ + @Override public boolean hasNext() throws IOException { if (it == null) { return false; @@ -203,6 +204,7 @@ public class AddressKeyIterator implements DBLongIterator { /** * @see db.DBLongIterator#hasPrevious() */ + @Override public boolean hasPrevious() throws IOException { if (it == null) { return false; @@ -226,6 +228,7 @@ public class AddressKeyIterator implements DBLongIterator { /** * @see db.DBLongIterator#next() */ + @Override public long next() throws IOException { if (hasNext()) { return it.next(); @@ -236,6 +239,7 @@ public class AddressKeyIterator implements DBLongIterator { /** * @see db.DBLongIterator#previous() */ + @Override public long previous() throws IOException { if (hasPrevious()) { return it.previous(); @@ -246,6 +250,7 @@ public class AddressKeyIterator implements DBLongIterator { /** * @see db.DBLongIterator#delete() */ + @Override public boolean delete() throws IOException { if (it != null) { return it.delete(); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/map/AddressKeyRecordIterator.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/map/AddressKeyRecordIterator.java index ba8100b658..c3f319f0fc 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/map/AddressKeyRecordIterator.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/map/AddressKeyRecordIterator.java @@ -15,13 +15,12 @@ */ package ghidra.program.database.map; -import ghidra.program.model.address.*; - import java.io.IOException; import java.util.Iterator; import java.util.List; import db.*; +import ghidra.program.model.address.*; /** * Returns a RecordIterator over records that are address keyed. Various constructors allow @@ -116,7 +115,7 @@ public class AddressKeyRecordIterator implements RecordIterator { * @param addrMap the address map * @param absolute if true, only absolute memory address encodings are considered, otherwise * only standard/relocatable address encodings are considered. - * @param set the address set to iterate over. + * @param set the address set to iterate over or null for all addresses * @param startAddr the address at which to position the iterator. The iterator will be positioned * either before or after the start address depending on the before parameter. If this parameter * is null, then the iterator will start either before the min address or after the max address @@ -174,6 +173,7 @@ public class AddressKeyRecordIterator implements RecordIterator { /** * @see db.RecordIterator#hasNext() */ + @Override public boolean hasNext() throws IOException { if (it == null) { return false; @@ -197,6 +197,7 @@ public class AddressKeyRecordIterator implements RecordIterator { /** * @see db.RecordIterator#hasPrevious() */ + @Override public boolean hasPrevious() throws IOException { if (it == null) { return false; @@ -220,6 +221,7 @@ public class AddressKeyRecordIterator implements RecordIterator { /** * @see db.RecordIterator#next() */ + @Override public DBRecord next() throws IOException { if (hasNext()) { return it.next(); @@ -230,6 +232,7 @@ public class AddressKeyRecordIterator implements RecordIterator { /** * @see db.RecordIterator#previous() */ + @Override public DBRecord previous() throws IOException { if (hasPrevious()) { return it.previous(); @@ -240,6 +243,7 @@ public class AddressKeyRecordIterator implements RecordIterator { /** * @see db.RecordIterator#delete() */ + @Override public boolean delete() throws IOException { if (it != null) { return it.delete(); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/map/AddressMap.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/map/AddressMap.java index 3f444c2c75..e2d1c143df 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/map/AddressMap.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/map/AddressMap.java @@ -65,7 +65,7 @@ public interface AddressMap { /** * Search for addr within the "sorted" keyRangeList and return the index of the * keyRange which contains the specified addr. - * @param keyRangeList + * @param keyRangeList key range list to search * @param addr address or null * @return index of the keyRange within the keyRangeList which contains addr * if it is contained in the list; otherwise, (-(insertion point) - 1). @@ -100,7 +100,7 @@ public interface AddressMap { * Generates a properly ordered list of database key ranges for a * a specified address set. If absolute encodings are requested, * only memory addresses will be included. - * @param set address set or null for all real address. + * @param set address set or null for all addresses. May not be null if create is true. * @param create true if a new keys may be generated, otherwise returned * key-ranges will be limited to those already defined. * @return "sorted" list of KeyRange objects @@ -113,12 +113,14 @@ public interface AddressMap { * "absoluteEncoding" method. If the program's default address space is segmented (i.e., SegmentedAddressSpace). * the address returned will be always be normalized to defined segmented memory blocks if possible. * @param value the long value to convert to an address. + * @return address decoded from long */ public Address decodeAddress(long value); /** * Returns the address factory associated with this map. * Null may be returned if map not associated with a specific address factory. + * @return associated {@link AddressFactory} or null */ public AddressFactory getAddressFactory(); @@ -140,7 +142,7 @@ public interface AddressMap { * Generates a properly ordered list of database key ranges for a * a specified address set. If absolute encodings are requested, * only memory addresses will be included. - * @param set address set or null for all real address. + * @param set address set or null for all addresses. May not be null if create is true. * @param absolute if true, absolute key encodings are returned, otherwise * standard/relocatable address key encodings are returned. * @param create true if a new keys may be generated, otherwise returned diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/properties/PropertyMapDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/properties/PropertyMapDB.java index aaf6bff622..05715ab767 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/properties/PropertyMapDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/properties/PropertyMapDB.java @@ -434,7 +434,7 @@ public abstract class PropertyMapDB implements PropertyMap { /** * Get an iterator over the long address keys which contain a property value. - * @param set + * @param set addresses over which to iterate (null indicates all defined memory regions) * @param atStart true if the iterator should be positioned at the start * of the range * @return long address iterator. @@ -442,9 +442,8 @@ public abstract class PropertyMapDB implements PropertyMap { */ public AddressKeyIterator getAddressKeyIterator(AddressSetView set, boolean atStart) throws IOException { - - if (propertyTable == null) { - return new AddressKeyIterator(); + if (propertyTable == null || (set != null && set.isEmpty())) { + return AddressKeyIterator.EMPTY_ITERATOR; } if (atStart) { return new AddressKeyIterator(propertyTable, addrMap, set, set.getMinAddress(), true); @@ -463,7 +462,7 @@ public abstract class PropertyMapDB implements PropertyMap { throws IOException { if (propertyTable == null) { - return new AddressKeyIterator(); + return AddressKeyIterator.EMPTY_ITERATOR; } return new AddressKeyIterator(propertyTable, addrMap, start, before); } @@ -481,7 +480,7 @@ public abstract class PropertyMapDB implements PropertyMap { throws IOException { if (propertyTable == null) { - return new AddressKeyIterator(); + return AddressKeyIterator.EMPTY_ITERATOR; } if (atStart) { return new AddressKeyIterator(propertyTable, addrMap, start, end, start, true); @@ -561,8 +560,8 @@ public abstract class PropertyMapDB implements PropertyMap { */ @Override public AddressIterator getPropertyIterator(AddressSetView asv, boolean forward) { - if (propertyTable == null) { - return new EmptyAddressIterator(); + if (propertyTable == null || (asv != null && asv.isEmpty())) { + return AddressIterator.EMPTY_ITERATOR; } AddressKeyIterator keyIter = null; try { diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/references/ReferenceDBManager.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/references/ReferenceDBManager.java index d773e80928..a8c5aba723 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/references/ReferenceDBManager.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/references/ReferenceDBManager.java @@ -946,6 +946,9 @@ public class ReferenceDBManager implements ReferenceManager, ManagerDB, ErrorHan @Override public AddressIterator getReferenceDestinationIterator(AddressSetView addrSet, boolean forward) { + if (addrSet != null && addrSet.isEmpty()) { + return AddressIterator.EMPTY_ITERATOR; + } try { return toAdapter.getToIterator(addrSet, forward); } @@ -973,6 +976,9 @@ public class ReferenceDBManager implements ReferenceManager, ManagerDB, ErrorHan @Override public AddressIterator getReferenceSourceIterator(AddressSetView addrSet, boolean forward) { + if (addrSet != null && addrSet.isEmpty()) { + return AddressIterator.EMPTY_ITERATOR; + } try { return fromAdapter.getFromIterator(addrSet, forward); } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/AddressSetFilteredSymbolIterator.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/AddressSetFilteredSymbolIterator.java index f927a33cde..e4c2068d19 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/AddressSetFilteredSymbolIterator.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/AddressSetFilteredSymbolIterator.java @@ -44,7 +44,7 @@ class AddressSetFilteredSymbolIterator implements SymbolIterator { /** * Construct a new AddressSetFilteredSymbolIterator. * @param symbolMgr the symbol manager - * @param set the address set to iterator over. + * @param set the address set to iterator over (required). * @param query the query to use as a filter * @param forward the direction of the iterator. */ diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/SymbolManager.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/SymbolManager.java index 9f946099fc..55307aafbe 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/SymbolManager.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/SymbolManager.java @@ -1204,6 +1204,9 @@ public class SymbolManager implements SymbolTable, ManagerDB { @Override public SymbolIterator getPrimarySymbolIterator(AddressSetView set, boolean forward) { + if (set.isEmpty()) { + return SymbolIterator.EMPTY_ITERATOR; + } Query query1 = new FieldMatchQuery(SymbolDatabaseAdapter.SYMBOL_DATA2_COL, new IntField(1)); Query query2 = new FieldMatchQuery(SymbolDatabaseAdapter.SYMBOL_TYPE_COL, new ByteField(SymbolType.LABEL.getID())); @@ -1216,6 +1219,9 @@ public class SymbolManager implements SymbolTable, ManagerDB { @Override public SymbolIterator getSymbols(AddressSetView set, SymbolType type, boolean forward) { + if (set.isEmpty()) { + return SymbolIterator.EMPTY_ITERATOR; + } Query query = new FieldMatchQuery(SymbolDatabaseAdapter.SYMBOL_TYPE_COL, new ByteField(type.getID())); return new AddressSetFilteredSymbolIterator(this, set, query, forward); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/AddressIterator.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/AddressIterator.java index 9f25ec93e2..8755135097 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/AddressIterator.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/AddressIterator.java @@ -16,6 +16,7 @@ package ghidra.program.model.address; import java.util.Iterator; +import java.util.NoSuchElementException; import util.CollectionUtils; @@ -30,20 +31,24 @@ import util.CollectionUtils; */ public interface AddressIterator extends Iterator
, Iterable
{ - /** - * Get the next address. - * - * @return the next address in the iteration. - */ - @Override - public Address next(); - /** - * Checks if there is a next address in the iteration. - * - * @return true if there is a next address. - */ - @Override - public boolean hasNext(); + public static final AddressIterator EMPTY_ITERATOR = new AddressIterator() { + + @Override + public boolean hasNext() { + return false; + } + + @Override + public Address next() { + throw new NoSuchElementException(); + } + + @Override + public Iterator
iterator() { + return this; + } + + }; } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/CodeUnitIterator.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/CodeUnitIterator.java index 685ee16b42..686f0b00a6 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/CodeUnitIterator.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/CodeUnitIterator.java @@ -26,14 +26,35 @@ import util.CollectionUtils; */ public interface CodeUnitIterator extends Iterator, Iterable { + public static final CodeUnitIterator EMPTY_ITERATOR = new CodeUnitIterator() { + + @Override + public boolean hasNext() { + return false; + } + + @Override + public CodeUnit next() { + return null; + } + + @Override + public Iterator iterator() { + return this; + } + + }; + /** - * Returns true if the iteration has more elements. + * Return true if there is a next CodeUnit. */ @Override public boolean hasNext(); /** - * Return the next code unit in the iteration. + * Get the next CodeUnit or null if no more CodeUnits. + *

NOTE: This deviates from the standard {@link Iterator} interface + * by returning null instead of throwing an exception. */ @Override public CodeUnit next(); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/Listing.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/Listing.java index 2ae6a3253c..6d2e66f220 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/Listing.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/Listing.java @@ -213,7 +213,7 @@ public interface Listing { * addresses are contained in the given address set will be returned by the * iterator. * - * @param addrSet the AddressRangeSet to iterate over. + * @param addrSet the AddressRangeSet to iterate over (required). * @param forward true means get iterator in forward direction * @return a CodeUnitIterator that is restricted to the give * AddressRangeSet. diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/symbol/ReferenceManager.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/symbol/ReferenceManager.java index 8b6026b19a..9af6c9d1dd 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/symbol/ReferenceManager.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/symbol/ReferenceManager.java @@ -288,7 +288,7 @@ public interface ReferenceManager { /** * Returns an iterator over all addresses that are the "From" address in a * reference, restricted by the given address set. - * @param addrSet the set of address to restrict the iterator. + * @param addrSet the set of address to restrict the iterator or null for all addresses. * @param forward true means to iterate in the forward direction */ public AddressIterator getReferenceSourceIterator(AddressSetView addrSet, boolean forward); @@ -304,7 +304,7 @@ public interface ReferenceManager { /** * Returns an iterator over all addresses that are the "To" address in a * memory reference, restricted by the given address set. - * @param addrSet the set of address to restrict the iterator. + * @param addrSet the set of address to restrict the iterator or null for all addresses. * @param forward true means to iterate in the forward direction */ public AddressIterator getReferenceDestinationIterator(AddressSetView addrSet, boolean forward); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/symbol/SymbolIterator.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/symbol/SymbolIterator.java index ab4ea9b8b8..f24454a74e 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/symbol/SymbolIterator.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/symbol/SymbolIterator.java @@ -25,13 +25,37 @@ import util.CollectionUtils; * @see CollectionUtils#asIterable */ public interface SymbolIterator extends Iterator, Iterable { + + public static final SymbolIterator EMPTY_ITERATOR = new SymbolIterator() { + + @Override + public Iterator iterator() { + return this; + } + + @Override + public Symbol next() { + return null; + } + + @Override + public boolean hasNext() { + return false; + } + }; + /** * Return true if there is a next symbol. */ + @Override public boolean hasNext(); /** - * Get the next symbol. + * Get the next symbol or null if no more symbols. + *

NOTE: This deviates from the standard {@link Iterator} interface + * by returning null instead of throwing an exception. */ + @Override public Symbol next(); + } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/symbol/SymbolTable.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/symbol/SymbolTable.java index 22fdfd0f20..c0553ce9cc 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/symbol/SymbolTable.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/symbol/SymbolTable.java @@ -420,7 +420,7 @@ public interface SymbolTable { /** * Returns all the symbols of the given type within the given address set. - * @param set the address set in which to look for symbols of the given type + * @param set the address set in which to look for symbols of the given type (required). * @param type the SymbolType to look for. * @param forward the direction within the addressSet to search * @return symbol iterator @@ -500,7 +500,7 @@ public interface SymbolTable { /** * Get an iterator over symbols at addresses in the given addressSet - * @param asv the set of address over which to iterate symbols. + * @param asv the set of address over which to iterate symbols (required). * @param forward true means the iterator is in the forward direction * @return symbol iterator */