diff --git a/Ghidra/Configurations/Public_Release/src/global/docs/ChangeHistory.html b/Ghidra/Configurations/Public_Release/src/global/docs/ChangeHistory.html index a049921985..dbff8b8aba 100644 --- a/Ghidra/Configurations/Public_Release/src/global/docs/ChangeHistory.html +++ b/Ghidra/Configurations/Public_Release/src/global/docs/ChangeHistory.html @@ -152,6 +152,7 @@
  • Decompiler. Addressed various situations where the Decompiler unexpectedly removes active instructions as dead code after renaming or retyping a stack location. If the location was really an array element or structure field, renaming forced the Decompiler to treat the location as a distinct variable. Subsequently, the Decompiler thought that indirect references based before the location could not alias any following stack locations, which could then by considered dead. As of the 9.2 release, the Decompiler's renaming action no longer switches an annotation to forcing if it wasn't already. A retyping action, although it is forcing, won't trigger alias blocking for atomic data-types (this is configurable). (GP-248, Issue #524, #873)
  • Decompiler. Fixed decompiler memory issues reported by a community security researcher. (GP-267)
  • Decompiler. Fix for Decompiler error: Pcode: XML comms: Missing symref attribute in <high> tag. (GP-352, Issue #2360)
  • +
  • Decompiler. Fixed bug preventing the decompiler from seeing Equates attached to "compare" instructions. (GP-369, Issue #2386)
  • Demangler. Fixed the GnuDemangler to parse the full namespace for operator symbols. (GT-3474, Issue #1441, #1448)
  • Demangler. Fixed numerous GNU Demangler parsing issues. Most notable is the added support for C++ Lambda functions. (GT-3545, Issue #1457, #1569)
  • Demangler. Updated the GNU Demangler to correctly parse and apply C++ strings using the unnamed type syntax. (GT-3645)
  • diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.cc index e620e780dc..a097b4a446 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.cc @@ -5509,6 +5509,7 @@ int4 RuleEqual2Zero::applyOp(PcodeOp *op,Funcdata &data) if (vn2->isConstant()) { Address val(vn2->getSpace(),uintb_negate(vn2->getOffset()-1,vn2->getSize())); unnegvn = data.newVarnode(vn2->getSize(),val); + unnegvn->copySymbolIfValid(vn2); // Propagate any markup posvn = vn; } else { 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 index 659c518a1e..65b6e960ea 100644 --- 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 @@ -23,6 +23,7 @@ import org.junit.Test; import docking.widgets.fieldpanel.field.Field; import docking.widgets.fieldpanel.support.FieldLocation; +import ghidra.app.cmd.equate.SetEquateCmd; import ghidra.app.cmd.function.CreateFunctionCmd; import ghidra.app.cmd.function.DeleteFunctionCmd; import ghidra.app.cmd.label.RenameLabelCmd; @@ -134,6 +135,13 @@ public class HighSymbolTest extends AbstractDecompilerTest { waitForDecompiler(); } + private void applyEquate(String equateName, Address addr, long equateValue) { + modifyProgram(p -> { + SetEquateCmd cmd = new SetEquateCmd(equateName, addr, 0, equateValue); + cmd.applyTo(program); + }); + } + private void renameExisting(HighSymbol highSymbol, ClangToken tokenAtCursor, String newName) { SymbolEntry oldEntry = highSymbol.getFirstWholeMap(); long oldId = highSymbol.getId(); @@ -401,4 +409,62 @@ public class HighSymbolTest extends AbstractDecompilerTest { assertTrue(highSymbol.isNameLocked()); assertTrue(highSymbol.isTypeLocked()); } + + @Test + public void testHighSymbol_convert() { + Address subAddr = addr(0x10015ac); + String equateName = "00000000000000000000000001010011b"; + int equateValue = 0x53; + applyEquate(equateName, subAddr, equateValue); + decompile("10015ac"); + ClangTextField line = getLineContaining("if (param"); + FieldLocation loc = loc(line.getLineNumber(), 20); + ClangToken token = line.getToken(loc); + assertTrue(token.getText().equals("0b01010011")); + HighVariable variable = token.getHighVariable(); + assertTrue(variable instanceof HighConstant); + HighSymbol highSymbol = variable.getSymbol(); + assertTrue(highSymbol instanceof EquateSymbol); + EquateSymbol eqSymbol = (EquateSymbol) highSymbol; + assertEquals(eqSymbol.getConvert(), EquateSymbol.FORMAT_BIN); + assertEquals(eqSymbol.getValue(), equateValue); + } + + @Test + public void testHighSymbol_dualequates() { + // Two equates on the same value at different locations + // One a convert, and one a label + Address convAddr = addr(0x100165a); + String convName = "141"; + int convValue = 0x8d; + applyEquate(convName, convAddr, convValue); + Address eqAddr = addr(0x10015f1); + String eqName = "BIGEQUATE"; + int eqValue = 0x8d; + applyEquate(eqName, eqAddr, eqValue); + decompile("10015ac"); + ClangTextField line = getLineContaining(",DAT_010056a8"); + FieldLocation loc = loc(line.getLineNumber(), 23); + ClangToken token = line.getToken(loc); + assertTrue(token.getText().equals("141")); + HighVariable variable = token.getHighVariable(); + assertTrue(variable instanceof HighConstant); + HighSymbol highSymbol = variable.getSymbol(); + assertTrue(highSymbol instanceof EquateSymbol); + EquateSymbol eqSymbol = (EquateSymbol) highSymbol; + assertEquals(eqSymbol.getConvert(), EquateSymbol.FORMAT_DEC); + assertEquals(eqSymbol.getValue(), convValue); + + line = getLineContaining("DAT_010056a8 = "); + loc = loc(line.getLineNumber(), 39); + token = line.getToken(loc); + assertTrue(token.getText().equals(eqName)); + variable = token.getHighVariable(); + assertTrue(variable instanceof HighConstant); + highSymbol = variable.getSymbol(); + assertTrue(highSymbol instanceof EquateSymbol); + eqSymbol = (EquateSymbol) highSymbol; + assertEquals(eqSymbol.getConvert(), 0); + assertEquals(eqSymbol.getValue(), eqValue); + } } diff --git a/Ghidra/Features/FunctionID/ghidra_scripts/FidStatistics.java b/Ghidra/Features/FunctionID/ghidra_scripts/FidStatistics.java index bde8d5e6b6..90310869f1 100644 --- a/Ghidra/Features/FunctionID/ghidra_scripts/FidStatistics.java +++ b/Ghidra/Features/FunctionID/ghidra_scripts/FidStatistics.java @@ -405,11 +405,6 @@ public class FidStatistics extends GhidraScript { } String funcName = chooseFunctionName(result); NameVersions nameVersions = NameVersions.generate(funcName, program); - String strippedTemplateName = null; - if (nameVersions.demangledBaseName != null) { - strippedTemplateName = - MatchNameAnalysis.removeTemplateParams(nameVersions.demangledBaseName); - } boolean exactNameMatch = false; Iterator iter = matchAnalysis.getRawNameIterator(); while(iter.hasNext()) { @@ -450,14 +445,12 @@ public class FidStatistics extends GhidraScript { exactNameMatch = true; break; } - if (matchNames.demangledBaseName != null && strippedTemplateName != null) { - String strippedName = - MatchNameAnalysis.removeTemplateParams(matchNames.demangledBaseName); - if (strippedName != null) { - if (checkNames(strippedName, strippedTemplateName)) { - exactNameMatch = true; - break; - } + if (nameVersions.demangledNoTemplate != null && + matchNames.demangledNoTemplate != null) { + if (checkNames(nameVersions.demangledNoTemplate, + matchNames.demangledNoTemplate)) { + exactNameMatch = true; + break; } } } diff --git a/Ghidra/Features/FunctionID/src/main/java/ghidra/feature/fid/cmd/ApplyFidEntriesCommand.java b/Ghidra/Features/FunctionID/src/main/java/ghidra/feature/fid/cmd/ApplyFidEntriesCommand.java index 8f6f04daa2..f1fee20eae 100644 --- a/Ghidra/Features/FunctionID/src/main/java/ghidra/feature/fid/cmd/ApplyFidEntriesCommand.java +++ b/Ghidra/Features/FunctionID/src/main/java/ghidra/feature/fid/cmd/ApplyFidEntriesCommand.java @@ -41,7 +41,7 @@ public class ApplyFidEntriesCommand extends BackgroundCommand { private MatchNameAnalysis nameAnalysis = new MatchNameAnalysis(); private AddressSet affectedLocations = new AddressSet(); - private TreeMap multiMatchNames = new TreeMap(); + private TreeMap> multiMatchNames = new TreeMap>(); private LinkedList
    conflictFunctions = new LinkedList
    (); private boolean alwaysApplyFidLabels; private float scoreThreshold; @@ -128,18 +128,16 @@ public class ApplyFidEntriesCommand extends BackgroundCommand { nameAnalysis.analyzeLibraries(result.matches, MAGIC_MULTIPLE_LIBRARY_LIMIT, monitor); String newFunctionName = null; - if (nameAnalysis.numNames() == 1) { - newFunctionName = nameAnalysis.getNameIterator().next(); - } - - if (nameAnalysis.numSimilarNames() == 1) { // If all names are the same, up to a difference in '_' prefix + if (nameAnalysis.numNames() == 1) { // If all names are the same, up to a difference in '_' prefix bookmarkContents = "Library Function - Single Match, "; plateCommentContents = "Library Function - Single Match"; + newFunctionName = nameAnalysis.getNameIterator().next(); } else { // If names are different in some way bookmarkContents = "Library Function - Multiple Matches, "; plateCommentContents = "Library Function - Multiple Matches"; - if (nameAnalysis.numNames() == 1) { + if (nameAnalysis.getMostOptimisticCount() == 1) { + newFunctionName = nameAnalysis.getMostOptimisticName(); plateCommentContents = plateCommentContents + " With Same Base Name"; bookmarkContents = bookmarkContents + "Same "; } @@ -157,16 +155,26 @@ public class ApplyFidEntriesCommand extends BackgroundCommand { monitor); } + /** + * Construct list of names as they should appear in the comment for the function. + * @param monitor is the task monitor + * @return the list of names as a formatted String + * @throws CancelledException if the user cancels the task + */ private String listNames(TaskMonitor monitor) throws CancelledException { StringBuilder buffer = new StringBuilder(); int counter = 0; - Iterator iterator = nameAnalysis.getNameIterator(); while (iterator.hasNext()) { monitor.checkCanceled(); + String display = iterator.next(); + NameVersions versions = nameAnalysis.getVersions(display); + if (versions != null && versions.demangledFull != null) { + display = versions.demangledFull; + } buffer.append(' '); - buffer.append(iterator.next()); + buffer.append(display); buffer.append('\n'); counter++; if (counter > 3) { @@ -332,32 +340,30 @@ public class ApplyFidEntriesCommand extends BackgroundCommand { // Also checks those locations to see if there is only one label left and if so, removes the "FID conflict" bookmark. private void removeConflictSymbols(Function function, String matchName, TaskMonitor monitor) { - Address addr = multiMatchNames.get(matchName); - if (addr == null) { + List
    list = multiMatchNames.get(matchName); + if (list == null) { return; } Program program = function.getProgram(); - int numSymbols = deleteSymbol(matchName, addr, program); - if (numSymbols <= 1) { - // Only one symbol left, delete the "FID conflict" bookmark - BookmarkManager bookmarkManager = program.getBookmarkManager(); - Bookmark bookmark = bookmarkManager.getBookmark(addr, BookmarkType.ANALYSIS, - FIDCONFLICT_BOOKMARK_CATEGORY); - if (bookmark != null) { - bookmarkManager.removeBookmark(bookmark); + for (Address addr : list) { + int numSymbols = deleteSymbol(matchName, addr, program); + if (numSymbols <= 1) { + // Only one symbol left, delete the "FID conflict" bookmark + BookmarkManager bookmarkManager = program.getBookmarkManager(); + Bookmark bookmark = bookmarkManager.getBookmark(addr, BookmarkType.ANALYSIS, + FIDCONFLICT_BOOKMARK_CATEGORY); + if (bookmark != null) { + bookmarkManager.removeBookmark(bookmark); + } } } } - private int addFunctionLabelMultipleMatches(Function function, TaskMonitor monitor) + private void addFunctionLabelMultipleMatches(Function function, TaskMonitor monitor) throws CancelledException { Program program = function.getProgram(); - if (nameAnalysis.numNames() >= MAGIC_MULTIPLE_MATCH_LIMIT) { - return nameAnalysis.numNames(); - } - Symbol symbol = function.getSymbol(); boolean preexistingSymbol = (symbol != null && symbol.getSource() != SourceType.DEFAULT); @@ -368,7 +374,12 @@ public class ApplyFidEntriesCommand extends BackgroundCommand { for (String functionName : unusedNames) { monitor.checkCanceled(); addSymbolToFunction(function, functionName); - multiMatchNames.put(functionName, addr); + List
    list = multiMatchNames.get(functionName); + if (list == null) { + list = new LinkedList
    (); + multiMatchNames.put(functionName, list); + } + list.add(addr); } if (unusedNames.size() > 1) { @@ -382,7 +393,6 @@ public class ApplyFidEntriesCommand extends BackgroundCommand { "Multiple likely matching functions"); } } - return unusedNames.size(); } /** @@ -432,7 +442,7 @@ public class ApplyFidEntriesCommand extends BackgroundCommand { * Takes a set of FID matching names and returns a subset that includes only names that don't exist * somewhere else in the program. */ - private static Set getFIDNamesThatDontExistSomewhereElse(Program program, + private Set getFIDNamesThatDontExistSomewhereElse(Program program, Iterator iter) { Set unusedNames = new HashSet(); @@ -441,27 +451,42 @@ public class ApplyFidEntriesCommand extends BackgroundCommand { String name = iter.next(); if (!nameExistsSomewhereElse(symbolTable, name)) { unusedNames.add(name); + if (unusedNames.size() > MAGIC_MULTIPLE_MATCH_LIMIT) { + break; + } } } return unusedNames; } + private static boolean containsPrimarySymbol(SymbolTable symTab, String name) { + List syms = symTab.getSymbols(name, null); + for (Symbol symbol : syms) { + SymbolType type = symbol.getSymbolType(); + if (type != SymbolType.FUNCTION && type != SymbolType.LABEL) { + continue; + } + if (symbol.isPrimary()) { + return true; + } + } + return false; + } + //Check to see if other functions exist with the same baseName or _baseName or __baseName - private static boolean nameExistsSomewhereElse(SymbolTable symTab, String baseName) { - + private boolean nameExistsSomewhereElse(SymbolTable symTab, String baseName) { + if (multiMatchNames.containsKey(baseName)) { + // If this name is part of a multimatch, don't treat as a definitive label + return false; + } //I did it this way because doing it with an iterator and wildcard was really really slow - List globalSymbols = symTab.getLabelOrFunctionSymbols(baseName, null); - if (!globalSymbols.isEmpty()) { + if (containsPrimarySymbol(symTab, baseName)) { return true; } - - globalSymbols = symTab.getLabelOrFunctionSymbols("_" + baseName, null); - if (!globalSymbols.isEmpty()) { + if (containsPrimarySymbol(symTab, "_" + baseName)) { return true; } - - globalSymbols = symTab.getLabelOrFunctionSymbols("__" + baseName, null); - if (!globalSymbols.isEmpty()) { + if (containsPrimarySymbol(symTab, "__" + baseName)) { return true; } diff --git a/Ghidra/Features/FunctionID/src/main/java/ghidra/feature/fid/service/MatchNameAnalysis.java b/Ghidra/Features/FunctionID/src/main/java/ghidra/feature/fid/service/MatchNameAnalysis.java index f36aef1e3c..f142a455fd 100644 --- a/Ghidra/Features/FunctionID/src/main/java/ghidra/feature/fid/service/MatchNameAnalysis.java +++ b/Ghidra/Features/FunctionID/src/main/java/ghidra/feature/fid/service/MatchNameAnalysis.java @@ -30,58 +30,131 @@ import ghidra.util.task.TaskMonitor; */ public class MatchNameAnalysis { private Set finalNameList = null; + private TreeMap versionMap = null; private TreeSet rawNames = null; private TreeSet similarBaseNames = null; + private TreeSet demangledNameNoTemplate = null; private TreeSet exactDemangledBaseNames = null; private TreeSet libraries = null; - private int mostOptimisticCount; // What is most optimistic (smallest) number of matches - // Once duplicates and similar base names are taken into account private float overallScore = 0.0f; + /** + * @return the number of deduped symbol names + */ public int numNames() { return finalNameList.size(); } + /** + * @return an iterator to the deduped list of raw names (similar names are not collapsed) + */ public Iterator getRawNameIterator() { return rawNames.iterator(); } + /** + * Check if the given name is contained in the list of matches + * @param name is the given name + * @return true if the name is in the list + */ public boolean containsRawName(String name) { return rawNames.contains(name); } + /** + * @return an iterator to the final list of deduped symbol names + */ public Iterator getNameIterator() { return finalNameList.iterator(); } - public int numSimilarNames() { - return similarBaseNames.size(); - } - + /** + * @return the number of symbols in the deduped list of matching libraries + */ public int numLibraries() { return libraries.size(); } + /** + * @return an iterator to the deduped list of matching libraries + */ public Iterator getLibraryIterator() { return libraries.iterator(); } + /** + * Get an object with all the given versions of the given raw name + * @param raw is the raw name + * @return the corresponding NameVersions object + */ + public NameVersions getVersions(String raw) { + return versionMap.get(raw); + } + + /** + * Run through all deduping strategies and return the number of unique symbols given + * by the best strategy. + * @return number of unique symbols given by the optimal deduping strategy + */ public int getMostOptimisticCount() { - return mostOptimisticCount; + int count = rawNames.size(); + if (similarBaseNames.size() < count) { + count = similarBaseNames.size(); + } + if (demangledNameNoTemplate != null && demangledNameNoTemplate.size() < count) { + count = demangledNameNoTemplate.size(); + } + if (exactDemangledBaseNames != null && exactDemangledBaseNames.size() < count) { + count = exactDemangledBaseNames.size(); + } + return count; + } + + /** + * Run through ALL deduping strategies and if one results in a single label, return that label. + * Otherwise return null. + * @return a unique name describing all matches or null + */ + public String getMostOptimisticName() { + if (rawNames.size() == 1) { + return rawNames.first(); + } + if (similarBaseNames.size() == 1) { + return similarBaseNames.first(); + } + if (demangledNameNoTemplate != null && demangledNameNoTemplate.size() == 1) { + return demangledNameNoTemplate.first(); + } + if (exactDemangledBaseNames != null && exactDemangledBaseNames.size() == 1) { + return exactDemangledBaseNames.first(); + } + return null; } public float getOverallScore() { return overallScore; } + /** + * Analyze a list of FID matches from a single address, deciding on the final list of symbols + * that will be associated with the address by deduping and omitting similar names. + * The final list is in finalNameList. + * Demangling and template stripping can produce an even shorter list than finalNameList. + * This is optionally available through getMostOptimisticName(). + * @param matches is the set of FID matches to analyze + * @param program is the Program + * @param monitor is the monitor + * @throws CancelledException if the user cancels the task + */ public void analyzeNames(List matches, Program program, TaskMonitor monitor) throws CancelledException { + versionMap = new TreeMap(); rawNames = new TreeSet(); similarBaseNames = new TreeSet(); + demangledNameNoTemplate = new TreeSet(); exactDemangledBaseNames = new TreeSet(); - int cannotDemangle = 0; for (FidMatch match : matches) { monitor.checkCanceled(); @@ -91,36 +164,27 @@ public class MatchNameAnalysis { NameVersions nameVersions = NameVersions.generate(function.getName(), program); // Put exact base names in a HashSet if (nameVersions.rawName != null) { + versionMap.put(nameVersions.rawName, nameVersions); rawNames.add(nameVersions.rawName); // Dedup the raw names similarBaseNames.add(nameVersions.similarName); // Dedup names with underscores removed - if (nameVersions.demangledBaseName != null) { + if (nameVersions.demangledNoTemplate != null && demangledNameNoTemplate != null) { + demangledNameNoTemplate.add(nameVersions.demangledNoTemplate); + } + else { + demangledNameNoTemplate = null; // Get rid of container if we can't strip everything + } + if (nameVersions.demangledBaseName != null && exactDemangledBaseNames != null) { exactDemangledBaseNames.add(nameVersions.demangledBaseName); // Dedup demangled base name } else { - cannotDemangle += 1; + exactDemangledBaseNames = null; // Get rid of container if we can't demangle everything } } } - String singleName = null; - mostOptimisticCount = rawNames.size(); finalNameList = rawNames; - if (rawNames.size() == 1) { - singleName = rawNames.first(); - } - else { - singleName = findCommonBaseName(); - mostOptimisticCount = similarBaseNames.size(); - if (singleName == null) { - singleName = findCommonDemangledBaseName(cannotDemangle); - if (exactDemangledBaseNames.size() > 0 && - exactDemangledBaseNames.size() < mostOptimisticCount) { - mostOptimisticCount = exactDemangledBaseNames.size(); - } - } - } + String singleName = findCommonBaseName(); if (singleName != null) { - mostOptimisticCount = 1; finalNameList = Collections.singleton(singleName); } else if (rawNames.size() > similarBaseNames.size()) { @@ -134,6 +198,15 @@ public class MatchNameAnalysis { } } + /** + * Collect strings describing the library descriptor of a set of FID matches. + * Dedup the list trying to get the size down below a specified limit, stripping + * version and family information from the library string if necessary. + * @param matches is the set of FID matches + * @param libraryLimit is the specified size limit + * @param monitor is a task monitor + * @throws CancelledException if the user cancels the task + */ public void analyzeLibraries(Collection matches,int libraryLimit,TaskMonitor monitor) throws CancelledException { libraries = new TreeSet(); @@ -176,65 +249,15 @@ public class MatchNameAnalysis { } } + /** + * Make a final decision based on the deduping strategies if there is a single + * matching name that describes all matches. + * @return the single matching name or null + */ private String findCommonBaseName() { - if (similarBaseNames.size() == 1) { - return rawNames.iterator().next(); + if (rawNames.size() == 1 || similarBaseNames.size() == 1) { + return rawNames.first(); } return null; } - - /** - * If there exists an initial set of template parameters bracketed by '<' and '>' - * in this name, strip them from the name. - * @param name is the function name to strip - * @return the stripped name or null if no parameters present - */ - public static String removeTemplateParams(String name) { - int pos1 = name.indexOf('<'); - if (pos1 < 0) { - return null; - } - int nesting = 1; - int pos2; - for (pos2 = pos1 + 1; pos2 < name.length(); ++pos2) { - char c = name.charAt(pos2); - if (c == '<') { - nesting += 1; - } - else if (c == '>') { - nesting -= 1; - if (nesting == 0) { - break; - } - } - } - if (nesting != 0) { - return null; - } - return name.substring(0, pos1 + 1) + name.substring(pos2); - } - - private String findCommonDemangledBaseName(int cannotDemangle) { - if (cannotDemangle > 0) { - return null; // Couldn't demangle everything, so no way we can have a common base - } - if (exactDemangledBaseNames.size() == 1) { - return exactDemangledBaseNames.iterator().next(); - } - // If we don't have a unique demangled name, try excising template parameters - String finalName = null; - for (String name : exactDemangledBaseNames) { - String templateFree = removeTemplateParams(name); - if (templateFree == null) { - return null; // At least one name has no template parameters - } - if (finalName == null) { - finalName = templateFree; - } - else if (!finalName.equals(templateFree)) { - return null; - } - } - return finalName; - } } diff --git a/Ghidra/Features/FunctionID/src/main/java/ghidra/feature/fid/service/NameVersions.java b/Ghidra/Features/FunctionID/src/main/java/ghidra/feature/fid/service/NameVersions.java index 4a4225eaed..e2f32cb7ad 100644 --- a/Ghidra/Features/FunctionID/src/main/java/ghidra/feature/fid/service/NameVersions.java +++ b/Ghidra/Features/FunctionID/src/main/java/ghidra/feature/fid/service/NameVersions.java @@ -21,11 +21,14 @@ import ghidra.program.model.listing.Program; public class NameVersions { public String rawName; // Original name public String similarName; // Name with underscores removed + public String demangledNoTemplate; // Demangled string with (first) template removed public String demangledBaseName; // Base name of the demangled string + public String demangledFull; // The full demangled signature public NameVersions(String raw) { rawName = raw; similarName = null; + demangledNoTemplate = null; demangledBaseName = null; } @@ -55,6 +58,38 @@ public class NameVersions { return name; } + /** + * If there exists an initial set of template parameters bracketed by '<' and '>' + * in the given demangled name, strip them. + * @param demangledObj is the object holding the demangled name + * @return the stripped name or null if no parameters present + */ + private static String removeTemplateParams(DemangledObject demangledObj) { + String name = demangledObj.getDemangledName(); + int pos1 = name.indexOf('<'); + if (pos1 < 0) { + return null; + } + int nesting = 1; + int pos2; + for (pos2 = pos1 + 1; pos2 < name.length(); ++pos2) { + char c = name.charAt(pos2); + if (c == '<') { + nesting += 1; + } + else if (c == '>') { + nesting -= 1; + if (nesting == 0) { + break; + } + } + } + if (nesting != 0) { + return null; + } + return name.substring(0, pos1 + 1) + name.substring(pos2); + } + private static String constructBaseName(DemangledObject demangledObj) { String origName = demangledObj.getName(); String name = origName.replaceFirst("_*", ""); @@ -87,6 +122,8 @@ public class NameVersions { if (rawName != null) { DemangledObject demangledObj = demangle(program, rawName); if (demangledObj != null) { + result.demangledFull = demangledObj.getOriginalDemangled(); + result.demangledNoTemplate = removeTemplateParams(demangledObj); result.demangledBaseName = constructBaseName(demangledObj); } diff --git a/Ghidra/Features/GraphServices/certification.manifest b/Ghidra/Features/GraphServices/certification.manifest index 96cfda73b6..37be0115f7 100644 --- a/Ghidra/Features/GraphServices/certification.manifest +++ b/Ghidra/Features/GraphServices/certification.manifest @@ -5,6 +5,7 @@ ##MODULE IP: LGPL 2.1 ##MODULE IP: MIT ##MODULE IP: Oxygen Icons - LGPL 3.0 +##MODULE IP: Tango Icons - Public Domain Module.manifest||GHIDRA||||END| build.gradle||GHIDRA||||END| src/main/help/help/TOC_Source.xml||GHIDRA||||END| @@ -17,7 +18,9 @@ src/main/help/help/topics/GraphServices/images/DefaultGraphDisplay.png||GHIDRA|| src/main/help/help/topics/GraphServices/images/ExportDialog.png||GHIDRA||||END| src/main/resources/images/Lasso.png||GHIDRA||||END| src/main/resources/images/magnifier.png||FAMFAMFAM Icons - CC 2.5|||famfamfam silk icon set|END| +src/main/resources/images/project-open.png||Oxygen Icons - LGPL 3.0|||Oxygen icon theme (dual license; LGPL or CC-SA-3.0)|END| src/main/resources/images/redspheregraph.png||GHIDRA||||END| src/main/resources/images/sat2.png||GHIDRA||||END| src/main/resources/images/tree.png||GHIDRA||||END| +src/main/resources/images/view-fullscreen.png||Tango Icons - Public Domain|||tango icon set|END| src/main/resources/jungrapht.properties||GHIDRA||||END| diff --git a/Ghidra/Features/GraphServices/src/main/help/help/topics/GraphServices/images/DefaultGraphDisplay.png b/Ghidra/Features/GraphServices/src/main/help/help/topics/GraphServices/images/DefaultGraphDisplay.png index c39a2cdf1e..38e16becc3 100644 Binary files a/Ghidra/Features/GraphServices/src/main/help/help/topics/GraphServices/images/DefaultGraphDisplay.png and b/Ghidra/Features/GraphServices/src/main/help/help/topics/GraphServices/images/DefaultGraphDisplay.png differ diff --git a/Ghidra/Features/GraphServices/src/main/help/help/topics/GraphServices/images/ExportDialog.png b/Ghidra/Features/GraphServices/src/main/help/help/topics/GraphServices/images/ExportDialog.png index 089efa0baa..e0023c1ef4 100644 Binary files a/Ghidra/Features/GraphServices/src/main/help/help/topics/GraphServices/images/ExportDialog.png and b/Ghidra/Features/GraphServices/src/main/help/help/topics/GraphServices/images/ExportDialog.png differ diff --git a/Ghidra/Features/GraphServices/src/main/java/ghidra/graph/visualization/DefaultDisplayGraphIcons.java b/Ghidra/Features/GraphServices/src/main/java/ghidra/graph/visualization/DefaultDisplayGraphIcons.java index d90ae4628d..8072f37859 100644 --- a/Ghidra/Features/GraphServices/src/main/java/ghidra/graph/visualization/DefaultDisplayGraphIcons.java +++ b/Ghidra/Features/GraphServices/src/main/java/ghidra/graph/visualization/DefaultDisplayGraphIcons.java @@ -27,10 +27,12 @@ final class DefaultDisplayGraphIcons { private DefaultDisplayGraphIcons() { } - public static final Icon SATELLITE_VIEW_ICON = Icons.get("images/network-wireless-16.png"); + public static final Icon SATELLITE_VIEW_ICON = Icons.get("images/project-open.png"); public static final Icon VIEW_MAGNIFIER_ICON = Icons.get("images/magnifier.png"); public static final Icon PROGRAM_GRAPH_ICON = Icons.get("images/redspheregraph.png"); public static final Icon LAYOUT_ALGORITHM_ICON = Icons.get("images/katomic.png"); public static final Icon LASSO_ICON = Icons.get("images/Lasso.png"); public static final Icon FILTER_ICON = Icons.CONFIGURE_FILTER_ICON; + public static final Icon FIT_TO_WINDOW = Icons.get("images/view-fullscreen.png"); + } diff --git a/Ghidra/Features/GraphServices/src/main/java/ghidra/graph/visualization/DefaultGraphDisplay.java b/Ghidra/Features/GraphServices/src/main/java/ghidra/graph/visualization/DefaultGraphDisplay.java index f665f710c0..c5747d3f34 100644 --- a/Ghidra/Features/GraphServices/src/main/java/ghidra/graph/visualization/DefaultGraphDisplay.java +++ b/Ghidra/Features/GraphServices/src/main/java/ghidra/graph/visualization/DefaultGraphDisplay.java @@ -304,8 +304,8 @@ public class DefaultGraphDisplay implements GraphDisplay { // create an icon button to reset the view transformations to identity (scaled to layout) new ActionBuilder("Reset View", ACTION_OWNER) - .description("Reset all view transforms to center graph in display") - .toolBarIcon(Icons.REFRESH_ICON) + .description("Fit Graph to Window") + .toolBarIcon(DefaultDisplayGraphIcons.FIT_TO_WINDOW) .onAction(context -> viewer.scaleToLayout()) .buildAndInstallLocal(componentProvider); diff --git a/Ghidra/Features/GraphServices/src/main/resources/images/project-open.png b/Ghidra/Features/GraphServices/src/main/resources/images/project-open.png new file mode 100644 index 0000000000..be14845f70 Binary files /dev/null and b/Ghidra/Features/GraphServices/src/main/resources/images/project-open.png differ diff --git a/Ghidra/Features/GraphServices/src/main/resources/images/view-fullscreen.png b/Ghidra/Features/GraphServices/src/main/resources/images/view-fullscreen.png new file mode 100644 index 0000000000..ffdabd4e97 Binary files /dev/null and b/Ghidra/Features/GraphServices/src/main/resources/images/view-fullscreen.png differ 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 bf07755f4d..aa81f85dce 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 @@ -59,6 +59,10 @@ public class EquateSymbol extends HighSymbol { public long getValue() { return value; } + public int getConvert() { + return convert; + } + @Override public void restoreXML(XmlPullParser parser) throws PcodeXMLException { XmlElement symel = parser.start("equatesymbol"); 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 b58cdc553c..436670ed45 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 @@ -476,12 +476,9 @@ public class LocalSymbolMap { symbolMap.put(uniqueId, sym); } - private void newEquateSymbol(long uniqueId, String nm, long val, long hash, Address addr, - TreeMap constantSymbolMap) { - HighSymbol eqSymbol = constantSymbolMap.get(nm); - if (eqSymbol != null) { - return; // New reference to same symbol - } + private EquateSymbol newEquateSymbol(long uniqueId, String nm, long val, long hash, + Address addr) { + EquateSymbol eqSymbol; if (uniqueId == 0) { uniqueId = getNextId(); } @@ -494,7 +491,7 @@ public class LocalSymbolMap { eqSymbol = new EquateSymbol(uniqueId, conv, val, func, addr, hash); } //Do NOT setTypeLock - constantSymbolMap.put(nm, eqSymbol); + return eqSymbol; } /** @@ -502,7 +499,6 @@ public class LocalSymbolMap { * @param dbFunction is the function to pull equates for */ private void grabEquates(Function dbFunction) { - TreeMap constantSymbolMap = null; // Find named constants via Equates Program program = dbFunction.getProgram(); EquateTable equateTable = program.getEquateTable(); @@ -516,34 +512,21 @@ public class LocalSymbolMap { continue; } long hash[] = DynamicHash.calcConstantHash(instr, eq.getValue()); - for (long element : hash) { - if (constantSymbolMap == null) { - constantSymbolMap = new TreeMap(); - } - newEquateSymbol(0, eq.getDisplayName(), eq.getValue(), element, defAddr, - constantSymbolMap); + if (hash.length == 0) { + continue; } - } - } + Arrays.sort(hash); // Sort in preparation for deduping + String displayName = eq.getDisplayName(); + long eqValue = eq.getValue(); -// TODO: Find typed constants via DataTypeReferences -// -- for each datatype reference within the scope of the function -// MappedVarKey key = new MappedVarKey(AddressSpace.HASH_SPACE.getAddress(hash),defAddr); -// DynamicSymbol sym = constantSymbolMap.get(key); -// String name = sym != null ? sym.getName() : null; -// sym = new DynamicSymbol(name, dt, dt.getLength(), hash, defAddr, func, 0); // format?? -// if (name != null) { -// sym.setTypeLock(true); -// } -// sym.setTypeLock(true); -// sym.setReadOnly(true); -// - -// Add constant dynamic symbols to map - if (constantSymbolMap != null) { - for (HighSymbol sym : constantSymbolMap.values()) { - long id = getNextId(); - symbolMap.put(id, sym); + EquateSymbol eqSymbol; + for (int i = 0; i < hash.length; ++i) { + if (i != 0 && hash[i - 1] == hash[i]) { + continue; // Found a duplicate, skip it + } + eqSymbol = newEquateSymbol(0, displayName, eqValue, hash[i], defAddr); + symbolMap.put(eqSymbol.getId(), eqSymbol); + } } } } diff --git a/Ghidra/Processors/MIPS/src/main/java/ghidra/app/plugin/core/analysis/MipsAddressAnalyzer.java b/Ghidra/Processors/MIPS/src/main/java/ghidra/app/plugin/core/analysis/MipsAddressAnalyzer.java index e240ca3429..49063c5678 100644 --- a/Ghidra/Processors/MIPS/src/main/java/ghidra/app/plugin/core/analysis/MipsAddressAnalyzer.java +++ b/Ghidra/Processors/MIPS/src/main/java/ghidra/app/plugin/core/analysis/MipsAddressAnalyzer.java @@ -349,7 +349,13 @@ public class MipsAddressAnalyzer extends ConstantPropagationAnalyzer { BigInteger val = context.getValue(reg, false); if (val != null) { long lval = val.longValue(); - Address refAddr = instr.getMinAddress().getNewAddress(lval); + Address refAddr = null; + try { + refAddr = instr.getMinAddress().getNewAddress(lval); + } catch (AddressOutOfBoundsException e) { + // invalid reference + return; + } if ((lval > 4096 || lval < 0) && lval != 0xffff && program.getMemory().contains(refAddr)) {