From 062b6769f81567a3d132fd9b7fad4beb3b1ad207 Mon Sep 17 00:00:00 2001 From: caheckman <48068198+caheckman@users.noreply.github.com> Date: Wed, 21 Oct 2020 16:42:43 -0400 Subject: [PATCH 1/4] template stripping in FID --- .../ghidra_scripts/FidStatistics.java | 19 ++--- .../fid/service/MatchNameAnalysis.java | 78 +++++++++---------- .../feature/fid/service/NameVersions.java | 35 +++++++++ 3 files changed, 76 insertions(+), 56 deletions(-) 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/service/MatchNameAnalysis.java b/Ghidra/Features/FunctionID/src/main/java/ghidra/feature/fid/service/MatchNameAnalysis.java index f36aef1e3c..7a8fcd9c05 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 @@ -32,8 +32,10 @@ public class MatchNameAnalysis { private Set finalNameList = null; private TreeSet rawNames = null; private TreeSet similarBaseNames = null; + private TreeSet demangledNameNoTemplate = null; private TreeSet exactDemangledBaseNames = null; private TreeSet libraries = null; + private boolean demangleSelect = false; // True if either deamngledNameNoTemplate or exactDemangledBaseNames is unique private int mostOptimisticCount; // What is most optimistic (smallest) number of matches // Once duplicates and similar base names are taken into account @@ -43,6 +45,10 @@ public class MatchNameAnalysis { return finalNameList.size(); } + public boolean isDemangled() { + return demangleSelect; + } + public Iterator getRawNameIterator() { return rawNames.iterator(); } @@ -80,7 +86,9 @@ public class MatchNameAnalysis { rawNames = new TreeSet(); similarBaseNames = new TreeSet(); + demangledNameNoTemplate = new TreeSet(); exactDemangledBaseNames = new TreeSet(); + int cannotDetemplate = 0; int cannotDemangle = 0; for (FidMatch match : matches) { @@ -93,6 +101,12 @@ public class MatchNameAnalysis { if (nameVersions.rawName != null) { rawNames.add(nameVersions.rawName); // Dedup the raw names similarBaseNames.add(nameVersions.similarName); // Dedup names with underscores removed + if (nameVersions.demangledNoTemplate != null) { + demangledNameNoTemplate.add(nameVersions.demangledNoTemplate); + } + else { + cannotDetemplate += 1; + } if (nameVersions.demangledBaseName != null) { exactDemangledBaseNames.add(nameVersions.demangledBaseName); // Dedup demangled base name } @@ -111,8 +125,21 @@ public class MatchNameAnalysis { else { singleName = findCommonBaseName(); mostOptimisticCount = similarBaseNames.size(); + if (singleName == null) { + singleName = findCommonNoTemplate(cannotDetemplate); + if (singleName != null) { + demangleSelect = true; + } + if (demangledNameNoTemplate.size() > 0 && + demangledNameNoTemplate.size() < mostOptimisticCount) { + mostOptimisticCount = demangledNameNoTemplate.size(); + } + } if (singleName == null) { singleName = findCommonDemangledBaseName(cannotDemangle); + if (singleName != null) { + demangleSelect = true; + } if (exactDemangledBaseNames.size() > 0 && exactDemangledBaseNames.size() < mostOptimisticCount) { mostOptimisticCount = exactDemangledBaseNames.size(); @@ -183,35 +210,14 @@ public class MatchNameAnalysis { 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; + private String findCommonNoTemplate(int cannotDetemplate) { + if (cannotDetemplate > 0) { + return null; // Couldn't remove a parameters from everything, so we can't have a common template } - 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 (demangledNameNoTemplate.size() == 1) { + return demangledNameNoTemplate.first(); } - if (nesting != 0) { - return null; - } - return name.substring(0, pos1 + 1) + name.substring(pos2); + return null; } private String findCommonDemangledBaseName(int cannotDemangle) { @@ -219,22 +225,8 @@ public class MatchNameAnalysis { return null; // Couldn't demangle everything, so no way we can have a common base } if (exactDemangledBaseNames.size() == 1) { - return exactDemangledBaseNames.iterator().next(); + return exactDemangledBaseNames.first(); } - // 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; + return null; } } 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..f6670d3d00 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,13 @@ 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 NameVersions(String raw) { rawName = raw; similarName = null; + demangledNoTemplate = null; demangledBaseName = null; } @@ -55,6 +57,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 +121,7 @@ public class NameVersions { if (rawName != null) { DemangledObject demangledObj = demangle(program, rawName); if (demangledObj != null) { + result.demangledNoTemplate = removeTemplateParams(demangledObj); result.demangledBaseName = constructBaseName(demangledObj); } From 56166a720fad9aedffb3af3732ebc24397c96127 Mon Sep 17 00:00:00 2001 From: caheckman <48068198+caheckman@users.noreply.github.com> Date: Mon, 2 Nov 2020 13:50:00 -0500 Subject: [PATCH 2/4] Consolidate different dedup strategies --- .../fid/cmd/ApplyFidEntriesCommand.java | 24 ++- .../fid/service/MatchNameAnalysis.java | 171 +++++++++++------- .../feature/fid/service/NameVersions.java | 2 + 3 files changed, 119 insertions(+), 78 deletions(-) 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..ef79e92efb 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 @@ -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) { 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 7a8fcd9c05..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,66 +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 boolean demangleSelect = false; // True if either deamngledNameNoTemplate or exactDemangledBaseNames is unique - 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(); } - public boolean isDemangled() { - return demangleSelect; - } - + /** + * @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 cannotDetemplate = 0; - int cannotDemangle = 0; for (FidMatch match : matches) { monitor.checkCanceled(); @@ -99,55 +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.demangledNoTemplate != null) { + if (nameVersions.demangledNoTemplate != null && demangledNameNoTemplate != null) { demangledNameNoTemplate.add(nameVersions.demangledNoTemplate); } else { - cannotDetemplate += 1; + demangledNameNoTemplate = null; // Get rid of container if we can't strip everything } - if (nameVersions.demangledBaseName != null) { + 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 = findCommonNoTemplate(cannotDetemplate); - if (singleName != null) { - demangleSelect = true; - } - if (demangledNameNoTemplate.size() > 0 && - demangledNameNoTemplate.size() < mostOptimisticCount) { - mostOptimisticCount = demangledNameNoTemplate.size(); - } - } - if (singleName == null) { - singleName = findCommonDemangledBaseName(cannotDemangle); - if (singleName != null) { - demangleSelect = true; - } - 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()) { @@ -161,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(); @@ -203,29 +249,14 @@ 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(); - } - return null; - } - - private String findCommonNoTemplate(int cannotDetemplate) { - if (cannotDetemplate > 0) { - return null; // Couldn't remove a parameters from everything, so we can't have a common template - } - if (demangledNameNoTemplate.size() == 1) { - return demangledNameNoTemplate.first(); - } - return null; - } - - 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.first(); + if (rawNames.size() == 1 || similarBaseNames.size() == 1) { + return rawNames.first(); } return null; } 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 f6670d3d00..2cbd9116e8 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 @@ -23,6 +23,7 @@ public class NameVersions { 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; @@ -121,6 +122,7 @@ public class NameVersions { if (rawName != null) { DemangledObject demangledObj = demangle(program, rawName); if (demangledObj != null) { + result.demangledFull = demangledObj.getDemangledName(); result.demangledNoTemplate = removeTemplateParams(demangledObj); result.demangledBaseName = constructBaseName(demangledObj); } From 020b8ee1dd75bab006e13026ebdd3edd68ff115e Mon Sep 17 00:00:00 2001 From: caheckman <48068198+caheckman@users.noreply.github.com> Date: Wed, 4 Nov 2020 14:34:15 -0500 Subject: [PATCH 3/4] Put full demangle in FID comment --- .../src/main/java/ghidra/feature/fid/service/NameVersions.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 2cbd9116e8..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 @@ -122,7 +122,7 @@ public class NameVersions { if (rawName != null) { DemangledObject demangledObj = demangle(program, rawName); if (demangledObj != null) { - result.demangledFull = demangledObj.getDemangledName(); + result.demangledFull = demangledObj.getOriginalDemangled(); result.demangledNoTemplate = removeTemplateParams(demangledObj); result.demangledBaseName = constructBaseName(demangledObj); } From c9ede3ab171608ee1fb3f9f3da9ef99b1ead185a Mon Sep 17 00:00:00 2001 From: caheckman <48068198+caheckman@users.noreply.github.com> Date: Wed, 4 Nov 2020 17:25:58 -0500 Subject: [PATCH 4/4] Adjustments to FID_conflict labeling --- .../fid/cmd/ApplyFidEntriesCommand.java | 75 ++++++++++++------- 1 file changed, 46 insertions(+), 29 deletions(-) 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 ef79e92efb..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; @@ -340,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); @@ -376,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) { @@ -390,7 +393,6 @@ public class ApplyFidEntriesCommand extends BackgroundCommand { "Multiple likely matching functions"); } } - return unusedNames.size(); } /** @@ -440,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(); @@ -449,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; }