mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-05-31 14:20:11 +08:00
Consolidate different dedup strategies
This commit is contained in:
+16
-8
@@ -128,18 +128,16 @@ public class ApplyFidEntriesCommand extends BackgroundCommand {
|
|||||||
nameAnalysis.analyzeLibraries(result.matches, MAGIC_MULTIPLE_LIBRARY_LIMIT, monitor);
|
nameAnalysis.analyzeLibraries(result.matches, MAGIC_MULTIPLE_LIBRARY_LIMIT, monitor);
|
||||||
|
|
||||||
String newFunctionName = null;
|
String newFunctionName = null;
|
||||||
if (nameAnalysis.numNames() == 1) {
|
if (nameAnalysis.numNames() == 1) { // If all names are the same, up to a difference in '_' prefix
|
||||||
newFunctionName = nameAnalysis.getNameIterator().next();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nameAnalysis.numSimilarNames() == 1) { // If all names are the same, up to a difference in '_' prefix
|
|
||||||
bookmarkContents = "Library Function - Single Match, ";
|
bookmarkContents = "Library Function - Single Match, ";
|
||||||
plateCommentContents = "Library Function - Single Match";
|
plateCommentContents = "Library Function - Single Match";
|
||||||
|
newFunctionName = nameAnalysis.getNameIterator().next();
|
||||||
}
|
}
|
||||||
else { // If names are different in some way
|
else { // If names are different in some way
|
||||||
bookmarkContents = "Library Function - Multiple Matches, ";
|
bookmarkContents = "Library Function - Multiple Matches, ";
|
||||||
plateCommentContents = "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";
|
plateCommentContents = plateCommentContents + " With Same Base Name";
|
||||||
bookmarkContents = bookmarkContents + "Same ";
|
bookmarkContents = bookmarkContents + "Same ";
|
||||||
}
|
}
|
||||||
@@ -157,16 +155,26 @@ public class ApplyFidEntriesCommand extends BackgroundCommand {
|
|||||||
monitor);
|
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 {
|
private String listNames(TaskMonitor monitor) throws CancelledException {
|
||||||
StringBuilder buffer = new StringBuilder();
|
StringBuilder buffer = new StringBuilder();
|
||||||
|
|
||||||
int counter = 0;
|
int counter = 0;
|
||||||
|
|
||||||
Iterator<String> iterator = nameAnalysis.getNameIterator();
|
Iterator<String> iterator = nameAnalysis.getNameIterator();
|
||||||
while (iterator.hasNext()) {
|
while (iterator.hasNext()) {
|
||||||
monitor.checkCanceled();
|
monitor.checkCanceled();
|
||||||
|
String display = iterator.next();
|
||||||
|
NameVersions versions = nameAnalysis.getVersions(display);
|
||||||
|
if (versions != null && versions.demangledFull != null) {
|
||||||
|
display = versions.demangledFull;
|
||||||
|
}
|
||||||
buffer.append(' ');
|
buffer.append(' ');
|
||||||
buffer.append(iterator.next());
|
buffer.append(display);
|
||||||
buffer.append('\n');
|
buffer.append('\n');
|
||||||
counter++;
|
counter++;
|
||||||
if (counter > 3) {
|
if (counter > 3) {
|
||||||
|
|||||||
+101
-70
@@ -30,66 +30,131 @@ import ghidra.util.task.TaskMonitor;
|
|||||||
*/
|
*/
|
||||||
public class MatchNameAnalysis {
|
public class MatchNameAnalysis {
|
||||||
private Set<String> finalNameList = null;
|
private Set<String> finalNameList = null;
|
||||||
|
private TreeMap<String, NameVersions> versionMap = null;
|
||||||
private TreeSet<String> rawNames = null;
|
private TreeSet<String> rawNames = null;
|
||||||
private TreeSet<String> similarBaseNames = null;
|
private TreeSet<String> similarBaseNames = null;
|
||||||
private TreeSet<String> demangledNameNoTemplate = null;
|
private TreeSet<String> demangledNameNoTemplate = null;
|
||||||
private TreeSet<String> exactDemangledBaseNames = null;
|
private TreeSet<String> exactDemangledBaseNames = null;
|
||||||
private TreeSet<String> libraries = null;
|
private TreeSet<String> 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;
|
private float overallScore = 0.0f;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the number of deduped symbol names
|
||||||
|
*/
|
||||||
public int numNames() {
|
public int numNames() {
|
||||||
return finalNameList.size();
|
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<String> getRawNameIterator() {
|
public Iterator<String> getRawNameIterator() {
|
||||||
return rawNames.iterator();
|
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) {
|
public boolean containsRawName(String name) {
|
||||||
return rawNames.contains(name);
|
return rawNames.contains(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return an iterator to the final list of deduped symbol names
|
||||||
|
*/
|
||||||
public Iterator<String> getNameIterator() {
|
public Iterator<String> getNameIterator() {
|
||||||
return finalNameList.iterator();
|
return finalNameList.iterator();
|
||||||
}
|
}
|
||||||
|
|
||||||
public int numSimilarNames() {
|
/**
|
||||||
return similarBaseNames.size();
|
* @return the number of symbols in the deduped list of matching libraries
|
||||||
}
|
*/
|
||||||
|
|
||||||
public int numLibraries() {
|
public int numLibraries() {
|
||||||
return libraries.size();
|
return libraries.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return an iterator to the deduped list of matching libraries
|
||||||
|
*/
|
||||||
public Iterator<String> getLibraryIterator() {
|
public Iterator<String> getLibraryIterator() {
|
||||||
return libraries.iterator();
|
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() {
|
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() {
|
public float getOverallScore() {
|
||||||
return overallScore;
|
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<FidMatch> matches, Program program, TaskMonitor monitor)
|
public void analyzeNames(List<FidMatch> matches, Program program, TaskMonitor monitor)
|
||||||
throws CancelledException {
|
throws CancelledException {
|
||||||
|
|
||||||
|
versionMap = new TreeMap<String, NameVersions>();
|
||||||
rawNames = new TreeSet<String>();
|
rawNames = new TreeSet<String>();
|
||||||
similarBaseNames = new TreeSet<String>();
|
similarBaseNames = new TreeSet<String>();
|
||||||
demangledNameNoTemplate = new TreeSet<String>();
|
demangledNameNoTemplate = new TreeSet<String>();
|
||||||
exactDemangledBaseNames = new TreeSet<String>();
|
exactDemangledBaseNames = new TreeSet<String>();
|
||||||
int cannotDetemplate = 0;
|
|
||||||
int cannotDemangle = 0;
|
|
||||||
|
|
||||||
for (FidMatch match : matches) {
|
for (FidMatch match : matches) {
|
||||||
monitor.checkCanceled();
|
monitor.checkCanceled();
|
||||||
@@ -99,55 +164,27 @@ public class MatchNameAnalysis {
|
|||||||
NameVersions nameVersions = NameVersions.generate(function.getName(), program);
|
NameVersions nameVersions = NameVersions.generate(function.getName(), program);
|
||||||
// Put exact base names in a HashSet
|
// Put exact base names in a HashSet
|
||||||
if (nameVersions.rawName != null) {
|
if (nameVersions.rawName != null) {
|
||||||
|
versionMap.put(nameVersions.rawName, nameVersions);
|
||||||
rawNames.add(nameVersions.rawName); // Dedup the raw names
|
rawNames.add(nameVersions.rawName); // Dedup the raw names
|
||||||
similarBaseNames.add(nameVersions.similarName); // Dedup names with underscores removed
|
similarBaseNames.add(nameVersions.similarName); // Dedup names with underscores removed
|
||||||
if (nameVersions.demangledNoTemplate != null) {
|
if (nameVersions.demangledNoTemplate != null && demangledNameNoTemplate != null) {
|
||||||
demangledNameNoTemplate.add(nameVersions.demangledNoTemplate);
|
demangledNameNoTemplate.add(nameVersions.demangledNoTemplate);
|
||||||
}
|
}
|
||||||
else {
|
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
|
exactDemangledBaseNames.add(nameVersions.demangledBaseName); // Dedup demangled base name
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
cannotDemangle += 1;
|
exactDemangledBaseNames = null; // Get rid of container if we can't demangle everything
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String singleName = null;
|
|
||||||
mostOptimisticCount = rawNames.size();
|
|
||||||
finalNameList = rawNames;
|
finalNameList = rawNames;
|
||||||
if (rawNames.size() == 1) {
|
String singleName = findCommonBaseName();
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (singleName != null) {
|
if (singleName != null) {
|
||||||
mostOptimisticCount = 1;
|
|
||||||
finalNameList = Collections.singleton(singleName);
|
finalNameList = Collections.singleton(singleName);
|
||||||
}
|
}
|
||||||
else if (rawNames.size() > similarBaseNames.size()) {
|
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<FidMatch> matches,int libraryLimit,TaskMonitor monitor)
|
public void analyzeLibraries(Collection<FidMatch> matches,int libraryLimit,TaskMonitor monitor)
|
||||||
throws CancelledException {
|
throws CancelledException {
|
||||||
libraries = new TreeSet<String>();
|
libraries = new TreeSet<String>();
|
||||||
@@ -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() {
|
private String findCommonBaseName() {
|
||||||
if (similarBaseNames.size() == 1) {
|
if (rawNames.size() == 1 || similarBaseNames.size() == 1) {
|
||||||
return rawNames.iterator().next();
|
return rawNames.first();
|
||||||
}
|
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ public class NameVersions {
|
|||||||
public String similarName; // Name with underscores removed
|
public String similarName; // Name with underscores removed
|
||||||
public String demangledNoTemplate; // Demangled string with (first) template removed
|
public String demangledNoTemplate; // Demangled string with (first) template removed
|
||||||
public String demangledBaseName; // Base name of the demangled string
|
public String demangledBaseName; // Base name of the demangled string
|
||||||
|
public String demangledFull; // The full demangled signature
|
||||||
|
|
||||||
public NameVersions(String raw) {
|
public NameVersions(String raw) {
|
||||||
rawName = raw;
|
rawName = raw;
|
||||||
@@ -121,6 +122,7 @@ public class NameVersions {
|
|||||||
if (rawName != null) {
|
if (rawName != null) {
|
||||||
DemangledObject demangledObj = demangle(program, rawName);
|
DemangledObject demangledObj = demangle(program, rawName);
|
||||||
if (demangledObj != null) {
|
if (demangledObj != null) {
|
||||||
|
result.demangledFull = demangledObj.getDemangledName();
|
||||||
result.demangledNoTemplate = removeTemplateParams(demangledObj);
|
result.demangledNoTemplate = removeTemplateParams(demangledObj);
|
||||||
result.demangledBaseName = constructBaseName(demangledObj);
|
result.demangledBaseName = constructBaseName(demangledObj);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user