Fixed tests failing to recent Version Tracking updates. Added a

priority to Version Tracking address correlators.
This commit is contained in:
dragonmacher 2024-07-23 18:01:38 -04:00
parent 220d6d9f58
commit 0fbd23653d
24 changed files with 488 additions and 293 deletions

View file

@ -44,7 +44,6 @@ public class AnalysisPriority {
* *
*/ */
/** /**
* Defines a full format analysis as the first priority for automatic analysis. * Defines a full format analysis as the first priority for automatic analysis.
* These are the first analyzers that will run after import. * These are the first analyzers that will run after import.
@ -78,7 +77,7 @@ public class AnalysisPriority {
* instructions <code>AnalyzerType.INSTRUCTIONS</code>. It is also useful for * instructions <code>AnalyzerType.INSTRUCTIONS</code>. It is also useful for
* those analyzers that depend on code, but want to analyze flow, such as non-returning * those analyzers that depend on code, but want to analyze flow, such as non-returning
* functions, that should happen before functions are widely laid down. If * functions, that should happen before functions are widely laid down. If
* bad flow is not fixed at an early priority, switch stmt recovery, function * bad flow is not fixed at an early priority, switch statement recovery, function
* boundaries, etc... may need to be redone and bad stuff cleaned up. * boundaries, etc... may need to be redone and bad stuff cleaned up.
*/ */
public final static AnalysisPriority CODE_ANALYSIS = DISASSEMBLY.getNext("CODE"); public final static AnalysisPriority CODE_ANALYSIS = DISASSEMBLY.getNext("CODE");
@ -114,8 +113,8 @@ public class AnalysisPriority {
DATA_ANALYSIS.getNext("FUNCTION ID"); DATA_ANALYSIS.getNext("FUNCTION ID");
/** /**
* Defines data type propogation as the ninth priority for automatic analysis. * Defines data type propagation as the ninth priority for automatic analysis.
* Data type propogation analysis should hapen as late as possible so that all basic code * Data type propagation analysis should happen as late as possible so that all basic code
* recovery, reference analysis, etc... has taken place. * recovery, reference analysis, etc... has taken place.
*/ */
public final static AnalysisPriority DATA_TYPE_PROPOGATION = public final static AnalysisPriority DATA_TYPE_PROPOGATION =
@ -136,6 +135,7 @@ public class AnalysisPriority {
/** /**
* Construct a new priority object. * Construct a new priority object.
* @param name the name
* @param priority priority to use * @param priority priority to use
*/ */
public AnalysisPriority(String name, int priority) { public AnalysisPriority(String name, int priority) {
@ -145,6 +145,7 @@ public class AnalysisPriority {
/** /**
* Return the priority specified for this analysis priority. * Return the priority specified for this analysis priority.
* @return the priority specified for this analysis priority.
*/ */
public int priority() { public int priority() {
return priority; return priority;
@ -160,7 +161,7 @@ public class AnalysisPriority {
} }
/** /**
* Get a piority that is a little lower than this one. * Get a priority that is a little lower than this one.
* *
* @return a lower priority * @return a lower priority
*/ */
@ -170,6 +171,7 @@ public class AnalysisPriority {
/** /**
* Return first gross priority. * Return first gross priority.
* @param name the name
* @return first gross priority * @return first gross priority
*/ */
public static AnalysisPriority getInitial(String name) { public static AnalysisPriority getInitial(String name) {
@ -178,6 +180,7 @@ public class AnalysisPriority {
/** /**
* Get the next gross priority. * Get the next gross priority.
* @param nextName the next name
* @return return next gross priority * @return return next gross priority
*/ */
public AnalysisPriority getNext(String nextName) { public AnalysisPriority getNext(String nextName) {

View file

@ -31,6 +31,9 @@ import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor; import ghidra.util.task.TaskMonitor;
public class CodeCompareAddressCorrelation implements AddressCorrelation { public class CodeCompareAddressCorrelation implements AddressCorrelation {
public static final String NAME = "CodeCompareAddressCorrelator";
static enum CorrelationKind { static enum CorrelationKind {
CODE_COMPARE, LCS, PARAMETERS; CODE_COMPARE, LCS, PARAMETERS;
} }
@ -65,15 +68,16 @@ public class CodeCompareAddressCorrelation implements AddressCorrelation {
} }
@Override @Override
public AddressRange getCorrelatedDestinationRange(Address sourceAddress, TaskMonitor monitor) public AddressCorrelationRange getCorrelatedDestinationRange(Address sourceAddress,
throws CancelledException { TaskMonitor monitor) throws CancelledException {
initialize(monitor); initialize(monitor);
CorrelationContainer container = cachedForwardAddressMap.get(sourceAddress); CorrelationContainer container = cachedForwardAddressMap.get(sourceAddress);
if (container == null) { if (container == null) {
return null; return null;
} }
return container.range;
return new AddressCorrelationRange(container.range, getName());
} }
private static final Comparator<CodeUnit> CUCOMPARATOR = new Comparator<CodeUnit>() { private static final Comparator<CodeUnit> CUCOMPARATOR = new Comparator<CodeUnit>() {
@ -446,6 +450,6 @@ public class CodeCompareAddressCorrelation implements AddressCorrelation {
@Override @Override
public String getName() { public String getName() {
return "CodeCompareAddressCorrelator"; return NAME;
} }
} }

View file

@ -20,9 +20,9 @@ import ghidra.framework.options.ToolOptions;
import ghidra.program.model.lang.Language; import ghidra.program.model.lang.Language;
import ghidra.program.model.listing.*; import ghidra.program.model.listing.*;
import ghidra.program.util.AddressCorrelation; import ghidra.program.util.AddressCorrelation;
import ghidra.program.util.DiscoverableAddressCorrelator; import ghidra.program.util.AddressCorrelator;
public class CodeCompareAddressCorrelator implements DiscoverableAddressCorrelator { public class CodeCompareAddressCorrelator implements AddressCorrelator {
private static final String OPTIONS_NAME = "CodeCompareAddressCorrelator"; private static final String OPTIONS_NAME = "CodeCompareAddressCorrelator";
@ -40,7 +40,9 @@ public class CodeCompareAddressCorrelator implements DiscoverableAddressCorrelat
Language l1 = p1.getLanguage(); Language l1 = p1.getLanguage();
Language l2 = p2.getLanguage(); Language l2 = p2.getLanguage();
if (l1.getLanguageID().equals(l2.getLanguageID())) { if (l1.getLanguageID().equals(l2.getLanguageID())) {
return null; // this correlator is best used with different architectures // this correlator is best used with different architectures, assuming we have simpler
// and faster correlators that should be run with the same language
return null;
} }
return new CodeCompareAddressCorrelation(sourceFunction, destinationFunction); return new CodeCompareAddressCorrelation(sourceFunction, destinationFunction);
@ -65,4 +67,11 @@ public class CodeCompareAddressCorrelator implements DiscoverableAddressCorrelat
public Options getDefaultOptions() { public Options getDefaultOptions() {
return new ToolOptions(OPTIONS_NAME); return new ToolOptions(OPTIONS_NAME);
} }
@Override
public int getPriority() {
// Run just above the last chance priority, which allows this correlator to be the fallback,
// general purpose correlator.
return LATE_CHANCE_PRIORITY - PRIORITY_OFFSET;
}
} }

View file

@ -28,6 +28,8 @@ dependencies {
api project(":Base") api project(":Base")
runtimeOnly project(":CodeCompare") runtimeOnly project(":CodeCompare")
testImplementation project(path: ':CodeCompare', configuration: 'testArtifacts')
testImplementation project(path: ':Project', configuration: 'testArtifacts') testImplementation project(path: ':Project', configuration: 'testArtifacts')
testImplementation project(path: ':SoftwareModeling', configuration: 'testArtifacts') testImplementation project(path: ':SoftwareModeling', configuration: 'testArtifacts')
} }

View file

@ -25,20 +25,13 @@ import ghidra.program.util.*;
* This is the correlator of last resort. It is the last correlator to be checked when trying to * This is the correlator of last resort. It is the last correlator to be checked when trying to
* acquire a correlation. * acquire a correlation.
*/ */
public class LastResortAddressCorrelator implements AddressCorrelator { public class LinearAddressCorrelator implements AddressCorrelator {
private static final String CORRELATOR_NAME = "LastResortAddressCorrelator"; private static final String CORRELATOR_NAME = "LastResortAddressCorrelator";
private ToolOptions options = new ToolOptions(CORRELATOR_NAME); private ToolOptions options = new ToolOptions(CORRELATOR_NAME);
public LastResortAddressCorrelator() {
}
@Override @Override
public AddressCorrelation correlate(Function sourceFunction, Function destinationFunction) { public AddressCorrelation correlate(Function sourceFunction, Function destinationFunction) {
if (sourceFunction.getProgram().getLanguage().getLanguageDescription().getProcessor().equals(
destinationFunction.getProgram().getLanguage().getLanguageDescription().getProcessor())) {
return new VTHashedFunctionAddressCorrelation(sourceFunction, destinationFunction);
}
return new LinearFunctionAddressCorrelation(sourceFunction, destinationFunction); return new LinearFunctionAddressCorrelation(sourceFunction, destinationFunction);
} }
@ -61,4 +54,9 @@ public class LastResortAddressCorrelator implements AddressCorrelator {
public Options getDefaultOptions() { public Options getDefaultOptions() {
return new ToolOptions(CORRELATOR_NAME); return new ToolOptions(CORRELATOR_NAME);
} }
@Override
public int getPriority() {
return LATE_CHANCE_PRIORITY;
}
} }

View file

@ -21,6 +21,7 @@ import java.util.Map;
import ghidra.program.model.address.*; import ghidra.program.model.address.*;
import ghidra.program.model.listing.*; import ghidra.program.model.listing.*;
import ghidra.program.util.AddressCorrelation; import ghidra.program.util.AddressCorrelation;
import ghidra.program.util.AddressCorrelationRange;
import ghidra.util.exception.CancelledException; import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor; import ghidra.util.task.TaskMonitor;
@ -52,29 +53,37 @@ public class StraightLineCorrelation implements AddressCorrelation {
} }
@Override @Override
public AddressRange getCorrelatedDestinationRange(Address sourceAddress, TaskMonitor monitor) public AddressCorrelationRange getCorrelatedDestinationRange(Address sourceAddress,
throws CancelledException { TaskMonitor monitor) throws CancelledException {
initialize(monitor); initialize(monitor);
return cachedForwardAddressMap.get(sourceAddress); AddressRange range = cachedForwardAddressMap.get(sourceAddress);
return new AddressCorrelationRange(range, getName());
} }
private void initialize(TaskMonitor monitor) throws CancelledException { private void initialize(TaskMonitor monitor) throws CancelledException {
if (cachedForwardAddressMap != null) return; if (cachedForwardAddressMap != null) {
return;
}
cachedForwardAddressMap = new HashMap<Address, AddressRange>(); cachedForwardAddressMap = new HashMap<Address, AddressRange>();
AddressSetView sourceAddressSet = (sourceFunction != null) ? sourceFunction.getBody() : null; AddressSetView sourceAddressSet =
AddressSetView destinationAddressSet = (destinationFunction != null) ? destinationFunction.getBody() : null; (sourceFunction != null) ? sourceFunction.getBody() : null;
AddressSetView destinationAddressSet =
(destinationFunction != null) ? destinationFunction.getBody() : null;
if (sourceAddressSet == null || destinationAddressSet == null) if (sourceAddressSet == null || destinationAddressSet == null) {
return; return;
}
CodeUnitIterator srcIter = sourceFunction.getProgram().getListing().getCodeUnits(sourceAddressSet, true); CodeUnitIterator srcIter =
CodeUnitIterator destIter = destinationFunction.getProgram().getListing().getCodeUnits(destinationAddressSet, true); sourceFunction.getProgram().getListing().getCodeUnits(sourceAddressSet, true);
CodeUnitIterator destIter =
destinationFunction.getProgram().getListing().getCodeUnits(destinationAddressSet, true);
monitor.setMessage("Defining address ranges..."); monitor.setMessage("Defining address ranges...");
monitor.initialize(sourceAddressSet.getNumAddresses()); monitor.initialize(sourceAddressSet.getNumAddresses());
while(srcIter.hasNext() && destIter.hasNext()) { while (srcIter.hasNext() && destIter.hasNext()) {
CodeUnit srcCodeUnit = srcIter.next(); CodeUnit srcCodeUnit = srcIter.next();
CodeUnit destCodeUnit = destIter.next(); CodeUnit destCodeUnit = destIter.next();
String srcMnemonic = srcCodeUnit.getMnemonicString(); String srcMnemonic = srcCodeUnit.getMnemonicString();
@ -84,8 +93,9 @@ public class StraightLineCorrelation implements AddressCorrelation {
monitor.incrementProgress(srcCodeUnit.getLength()); monitor.incrementProgress(srcCodeUnit.getLength());
defineRange(cachedForwardAddressMap, srcCodeUnit, destCodeUnit); defineRange(cachedForwardAddressMap, srcCodeUnit, destCodeUnit);
} }
else else {
break; // First mismatch we break out of the loop break; // First mismatch we break out of the loop
}
} }
computeParamCorrelation(); computeParamCorrelation();
} }
@ -124,13 +134,12 @@ public class StraightLineCorrelation implements AddressCorrelation {
* @param sourceCodeUnit is the source code unit * @param sourceCodeUnit is the source code unit
* @param destinationCodeUnit is the matching destination code unit * @param destinationCodeUnit is the matching destination code unit
*/ */
private static void defineRange(Map<Address, AddressRange> map, private static void defineRange(Map<Address, AddressRange> map, CodeUnit sourceCodeUnit,
CodeUnit sourceCodeUnit, CodeUnit destinationCodeUnit) { CodeUnit destinationCodeUnit) {
Address minAddress = sourceCodeUnit.getMinAddress(); Address minAddress = sourceCodeUnit.getMinAddress();
Address maxAddress = sourceCodeUnit.getMaxAddress(); Address maxAddress = sourceCodeUnit.getMaxAddress();
AddressRangeImpl toRange = AddressRangeImpl toRange = new AddressRangeImpl(destinationCodeUnit.getMinAddress(),
new AddressRangeImpl(destinationCodeUnit.getMinAddress(), destinationCodeUnit.getMaxAddress());
destinationCodeUnit.getMaxAddress());
while (!minAddress.equals(maxAddress)) { while (!minAddress.equals(maxAddress)) {
map.put(minAddress, toRange); map.put(minAddress, toRange);
minAddress = minAddress.next(); minAddress = minAddress.next();

View file

@ -17,7 +17,8 @@ package ghidra.feature.vt.api.correlator.address;
import static ghidra.util.datastruct.Duo.Side.*; import static ghidra.util.datastruct.Duo.Side.*;
import ghidra.program.model.address.*; import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressRangeImpl;
import ghidra.program.model.correlate.HashedFunctionAddressCorrelation; import ghidra.program.model.correlate.HashedFunctionAddressCorrelation;
import ghidra.program.model.listing.Function; import ghidra.program.model.listing.Function;
import ghidra.program.model.mem.MemoryAccessException; import ghidra.program.model.mem.MemoryAccessException;
@ -57,15 +58,16 @@ public class VTHashedFunctionAddressCorrelation implements AddressCorrelation {
} }
@Override @Override
public AddressRange getCorrelatedDestinationRange(Address sourceAddress, TaskMonitor monitor) public AddressCorrelationRange getCorrelatedDestinationRange(Address sourceAddress,
throws CancelledException { TaskMonitor monitor) throws CancelledException {
try { try {
initializeCorrelation(monitor); initializeCorrelation(monitor);
Address destinationAddress = addressCorrelation.getAddress(RIGHT, sourceAddress); Address destinationAddress = addressCorrelation.getAddress(RIGHT, sourceAddress);
if (destinationAddress == null) { if (destinationAddress == null) {
return null; // No matching destination. return null; // No matching destination.
} }
return new AddressRangeImpl(destinationAddress, destinationAddress); AddressRangeImpl range = new AddressRangeImpl(destinationAddress, destinationAddress);
return new AddressCorrelationRange(range, getName());
} }
catch (MemoryAccessException e) { catch (MemoryAccessException e) {
Msg.error(this, "Could not create HashedFunctionAddressCorrelation", e); Msg.error(this, "Could not create HashedFunctionAddressCorrelation", e);
@ -81,15 +83,14 @@ public class VTHashedFunctionAddressCorrelation implements AddressCorrelation {
* @throws CancelledException if the user cancels * @throws CancelledException if the user cancels
* @throws MemoryAccessException if either function's memory can't be accessed. * @throws MemoryAccessException if either function's memory can't be accessed.
*/ */
private void initializeCorrelation(TaskMonitor monitor) throws CancelledException, private void initializeCorrelation(TaskMonitor monitor)
MemoryAccessException { throws CancelledException, MemoryAccessException {
if (addressCorrelation != null) { if (addressCorrelation != null) {
return; return;
} }
if (sourceFunction != null && destinationFunction != null) { if (sourceFunction != null && destinationFunction != null) {
addressCorrelation = addressCorrelation =
new HashedFunctionAddressCorrelation(sourceFunction, destinationFunction, new HashedFunctionAddressCorrelation(sourceFunction, destinationFunction, monitor);
monitor);
} }
else { else {
addressCorrelation = new DummyListingAddressCorrelation(); addressCorrelation = new DummyListingAddressCorrelation();

View file

@ -0,0 +1,72 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.feature.vt.api.correlator.address;
import ghidra.framework.options.Options;
import ghidra.framework.options.ToolOptions;
import ghidra.program.model.lang.Language;
import ghidra.program.model.listing.Data;
import ghidra.program.model.listing.Function;
import ghidra.program.util.AddressCorrelation;
import ghidra.program.util.AddressCorrelator;
/**
* An address correlator that may use the {@link VTHashedFunctionAddressCorrelation}.
*/
public class VTHashedFunctionAddressCorrelator implements AddressCorrelator {
public static final String NAME = "VTHashedFunctionAddressCorrelator";
private ToolOptions options = new ToolOptions(NAME);
@Override
public AddressCorrelation correlate(Function sourceFunction, Function destinationFunction) {
Language sourceLanguage = sourceFunction.getProgram().getLanguage();
Language destinationLanguage = destinationFunction.getProgram().getLanguage();
if (sourceLanguage.getProcessor().equals(destinationLanguage.getProcessor())) {
return new VTHashedFunctionAddressCorrelation(sourceFunction, destinationFunction);
}
return null;
}
@Override
public AddressCorrelation correlate(Data sourceData, Data destinationData) {
return null;
}
@Override
public ToolOptions getOptions() {
return options;
}
@Override
public void setOptions(ToolOptions options) {
this.options = options.copy();
}
@Override
public Options getDefaultOptions() {
return new ToolOptions(NAME);
}
@Override
public int getPriority() {
// Run just above default / discovered correlators. Correlator authors can change their
// priority to take precedence over this correlator.
return DEFAULT_PRIORITY - PRIORITY_OFFSET;
}
}

View file

@ -44,6 +44,7 @@ public interface VTMarkupItem {
* address and the address source must be set prior to calling this method. * address and the address source must be set prior to calling this method.
* *
* @param applyAction the type of apply action to take when applying the given markup item * @param applyAction the type of apply action to take when applying the given markup item
* @param options the options
* @throws VersionTrackingApplyException if an error occurred while attempting to apply the * @throws VersionTrackingApplyException if an error occurred while attempting to apply the
* markup item. * markup item.
*/ */
@ -94,7 +95,7 @@ public interface VTMarkupItem {
* Returns the editable status of this markup item's destination address. * Returns the editable status of this markup item's destination address.
* *
* @return the editable status of this markup item's destination address. * @return the editable status of this markup item's destination address.
* @see #setDestinationAddress(Address, String) * @see #setDestinationAddress(Address)
*/ */
public VTMarkupItemDestinationAddressEditStatus getDestinationAddressEditStatus(); public VTMarkupItemDestinationAddressEditStatus getDestinationAddressEditStatus();
@ -110,7 +111,6 @@ public interface VTMarkupItem {
* *
* @param status The <b>considered</b> status to set * @param status The <b>considered</b> status to set
* @throws IllegalStateException if you call this method on an applied item * @throws IllegalStateException if you call this method on an applied item
* @see #setUnconsidered()
*/ */
public void setConsidered(VTMarkupItemConsideredStatus status); public void setConsidered(VTMarkupItemConsideredStatus status);
@ -124,6 +124,7 @@ public interface VTMarkupItem {
* Returns an optional description of the current markup item status. For example, if there * Returns an optional description of the current markup item status. For example, if there
* status is {@link VTMarkupItemStatus#FAILED_APPLY}, then this method should return a * status is {@link VTMarkupItemStatus#FAILED_APPLY}, then this method should return a
* description of the failure. * description of the failure.
* @return the description.
*/ */
public String getStatusDescription(); public String getStatusDescription();

View file

@ -15,9 +15,12 @@
*/ */
package ghidra.feature.vt.api.markuptype; package ghidra.feature.vt.api.markuptype;
import java.util.List;
import ghidra.feature.vt.api.main.*; import ghidra.feature.vt.api.main.*;
import ghidra.feature.vt.gui.util.*; import ghidra.feature.vt.gui.util.VTMatchApplyChoices;
import ghidra.feature.vt.gui.util.VTMatchApplyChoices.CommentChoices; import ghidra.feature.vt.gui.util.VTMatchApplyChoices.CommentChoices;
import ghidra.feature.vt.gui.util.VTOptionDefines;
import ghidra.framework.options.Options; import ghidra.framework.options.Options;
import ghidra.framework.options.ToolOptions; import ghidra.framework.options.ToolOptions;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
@ -26,8 +29,6 @@ import ghidra.program.model.listing.Program;
import ghidra.program.util.PlateFieldLocation; import ghidra.program.util.PlateFieldLocation;
import ghidra.program.util.ProgramLocation; import ghidra.program.util.ProgramLocation;
import java.util.List;
public class PlateCommentMarkupType extends CommentMarkupType { public class PlateCommentMarkupType extends CommentMarkupType {
//================================================================================================== //==================================================================================================
@ -102,14 +103,16 @@ public class PlateCommentMarkupType extends CommentMarkupType {
options.setEnum(VTOptionDefines.PLATE_COMMENT, CommentChoices.APPEND_TO_EXISTING); options.setEnum(VTOptionDefines.PLATE_COMMENT, CommentChoices.APPEND_TO_EXISTING);
break; break;
case ADD_AS_PRIMARY: case ADD_AS_PRIMARY:
throw new IllegalArgumentException(getDisplayName() + throw new IllegalArgumentException(
" markup items cannot perform an Add As Primary action."); getDisplayName() + " markup items cannot perform an Add As Primary action.");
case REPLACE_DEFAULT_ONLY: case REPLACE_DEFAULT_ONLY:
throw new IllegalArgumentException(getDisplayName() + throw new IllegalArgumentException(getDisplayName() +
" markup items cannot perform a Replace Default Only action."); " markup items cannot perform a Replace Default Only action.");
case REPLACE: case REPLACE:
options.setEnum(VTOptionDefines.PLATE_COMMENT, CommentChoices.OVERWRITE_EXISTING); options.setEnum(VTOptionDefines.PLATE_COMMENT, CommentChoices.OVERWRITE_EXISTING);
break; break;
default:
break;
} }
return options; return options;
} }
@ -124,7 +127,7 @@ public class PlateCommentMarkupType extends CommentMarkupType {
if (vtMarkupItem.getSourceAddress().equals(association.getSourceAddress())) { if (vtMarkupItem.getSourceAddress().equals(association.getSourceAddress())) {
// Set Plate destination to destination function's entry point. // Set Plate destination to destination function's entry point.
vtMarkupItem.setDefaultDestinationAddress(association.getDestinationAddress(), vtMarkupItem.setDefaultDestinationAddress(association.getDestinationAddress(),
VTMarkupItem.FUNCTION_ADDRESS_SOURCE); VTMarkupItem.FUNCTION_ADDRESS_SOURCE);
} }
} }
return markupItems; return markupItems;

View file

@ -22,7 +22,8 @@ import org.jdom.Element;
import generic.cache.FixedSizeMRUCachingFactory; import generic.cache.FixedSizeMRUCachingFactory;
import generic.stl.Pair; import generic.stl.Pair;
import ghidra.feature.vt.api.correlator.address.ExactMatchAddressCorrelator; import ghidra.feature.vt.api.correlator.address.ExactMatchAddressCorrelator;
import ghidra.feature.vt.api.correlator.address.LastResortAddressCorrelator; import ghidra.feature.vt.api.correlator.address.VTHashedFunctionAddressCorrelator;
import ghidra.features.codecompare.correlator.CodeCompareAddressCorrelator;
import ghidra.framework.options.*; import ghidra.framework.options.*;
import ghidra.program.model.listing.Data; import ghidra.program.model.listing.Data;
import ghidra.program.model.listing.Function; import ghidra.program.model.listing.Function;
@ -37,6 +38,20 @@ public class AddressCorrelatorManager {
private static final int DATA_CORRELATION_CACHE_SIZE = 5; private static final int DATA_CORRELATION_CACHE_SIZE = 5;
private static final int FUNCTION_CORRELATION_CACHE_SIZE = 5; private static final int FUNCTION_CORRELATION_CACHE_SIZE = 5;
private static final Comparator<? super AddressCorrelator> CORRELATOR_COMPARATOR = (c1, c2) -> {
int p1 = c1.getPriority();
int p2 = c2.getPriority();
int d = p1 - p2;
if (d != 0) {
return d;
}
// pick something as a tie-breaker
String n1 = c1.getClass().getSimpleName();
String n2 = c2.getClass().getSimpleName();
return n1.compareTo(n2);
};
private List<AddressCorrelator> correlatorList; private List<AddressCorrelator> correlatorList;
@ -55,22 +70,25 @@ public class AddressCorrelatorManager {
private void initializeAddressCorrelators(VTSessionSupplier sessionSupplier) { private void initializeAddressCorrelators(VTSessionSupplier sessionSupplier) {
correlatorList.add(new ExactMatchAddressCorrelator(sessionSupplier)); correlatorList.add(new ExactMatchAddressCorrelator(sessionSupplier));
correlatorList.add(new VTHashedFunctionAddressCorrelator());
correlatorList.add(new CodeCompareAddressCorrelator());
// Note: at the time of writing this comment, the linear address correlator will not be
// executed. The VTHashedFunctionAddressCorrelator handles correlation between programs
// with the same architecture and the CodeCompareAddressCorrelator handles correlation
// between programs with different architectures.
// correlatorList.add(new LinearAddressCorrelator());
correlatorList.addAll(initializeAddressCorrelators()); correlatorList.addAll(initializeAddressCorrelators());
correlatorList.sort(CORRELATOR_COMPARATOR);
} }
private List<AddressCorrelator> initializeAddressCorrelators() { private List<AddressCorrelator> initializeAddressCorrelators() {
List<DiscoverableAddressCorrelator> instances = List<DiscoverableAddressCorrelator> instances =
ClassSearcher.getInstances(DiscoverableAddressCorrelator.class); ClassSearcher.getInstances(DiscoverableAddressCorrelator.class);
List<AddressCorrelator> addressCorrelatorList = new ArrayList<AddressCorrelator>(instances); return new ArrayList<AddressCorrelator>(instances);
Collections.sort(addressCorrelatorList,
(o1, o2) -> o1.getClass().getSimpleName().compareTo(o2.getClass().getSimpleName()));
// Put the LastResortCorrelator in case a better address correlation isn't found.
addressCorrelatorList.add(new LastResortAddressCorrelator());
return addressCorrelatorList;
} }
public AddressCorrelation getCorrelator(Function source, Function destination) { public AddressCorrelation getCorrelator(Function source, Function destination) {
@ -82,6 +100,7 @@ public class AddressCorrelatorManager {
} }
private AddressCorrelation getFunctionCorrelator(Function source, Function destination) { private AddressCorrelation getFunctionCorrelator(Function source, Function destination) {
for (AddressCorrelator correlator : correlatorList) { for (AddressCorrelator correlator : correlatorList) {
AddressCorrelation correlation = correlator.correlate(source, destination); AddressCorrelation correlation = correlator.correlate(source, destination);
if (correlation != null) { if (correlation != null) {
@ -92,12 +111,14 @@ public class AddressCorrelatorManager {
} }
private AddressCorrelation getDataCorrelator(Data source, Data destination) { private AddressCorrelation getDataCorrelator(Data source, Data destination) {
for (AddressCorrelator correlator : correlatorList) { for (AddressCorrelator correlator : correlatorList) {
AddressCorrelation correlation = correlator.correlate(source, destination); AddressCorrelation correlation = correlator.correlate(source, destination);
if (correlation != null) { if (correlation != null) {
return correlation; return correlation;
} }
} }
return null; return null;
} }

View file

@ -1153,13 +1153,18 @@ public class VTMatchTableProvider extends ComponentProviderAdapter
@Override @Override
public boolean isSubFilterOf(Filter<VTMatch> otherFilter) { public boolean isSubFilterOf(Filter<VTMatch> otherFilter) {
if (columnFilter == null) {
return false; Class<?> clazz = getClass();
Class<?> otherClazz = otherFilter.getClass();
if (!clazz.equals(otherClazz)) {
return false; // must be the same class
} }
if (otherFilter instanceof VTColumnFilter otherColumnFilter) {
return columnFilter.isSubFilterOf(otherColumnFilter.columnFilter); VTColumnFilter otherColumnFilter = (VTColumnFilter) otherFilter;
if (columnFilter == null && otherColumnFilter.columnFilter == null) {
return true;
} }
return false; return columnFilter.isSubFilterOf(otherColumnFilter.columnFilter);
} }
} }

View file

@ -25,10 +25,10 @@ import ghidra.feature.vt.api.util.VersionTrackingApplyException;
import ghidra.feature.vt.gui.plugin.VTController; import ghidra.feature.vt.gui.plugin.VTController;
import ghidra.feature.vt.gui.util.MatchInfo; import ghidra.feature.vt.gui.util.MatchInfo;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressRange;
import ghidra.program.model.listing.Data; import ghidra.program.model.listing.Data;
import ghidra.program.model.listing.Function; import ghidra.program.model.listing.Function;
import ghidra.program.util.AddressCorrelation; import ghidra.program.util.AddressCorrelation;
import ghidra.program.util.AddressCorrelationRange;
import ghidra.util.exception.AssertException; import ghidra.util.exception.AssertException;
import ghidra.util.exception.CancelledException; import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor; import ghidra.util.task.TaskMonitor;
@ -133,13 +133,16 @@ public class ClearMatchTask extends VtTask {
return; return;
} }
Address destinationAddress = null; AddressCorrelationRange range =
AddressRange range =
correlation.getCorrelatedDestinationRange(markupItem.getSourceAddress(), monitor); correlation.getCorrelatedDestinationRange(markupItem.getSourceAddress(), monitor);
Address destinationAddress = null;
String source = null;
if (range != null) { if (range != null) {
destinationAddress = range.getMinAddress(); destinationAddress = range.getMinAddress();
source = range.getCorrelatorName();
} }
markupItem.setDefaultDestinationAddress(destinationAddress, correlation.getName()); markupItem.setDefaultDestinationAddress(destinationAddress, source);
} }
private void clearMatch(VTMatch match) { private void clearMatch(VTMatch match) {

View file

@ -21,8 +21,8 @@ import ghidra.feature.vt.api.db.VTSessionDB;
import ghidra.feature.vt.api.main.*; import ghidra.feature.vt.api.main.*;
import ghidra.feature.vt.api.util.VersionTrackingApplyException; import ghidra.feature.vt.api.util.VersionTrackingApplyException;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressRange;
import ghidra.program.util.AddressCorrelation; import ghidra.program.util.AddressCorrelation;
import ghidra.program.util.AddressCorrelationRange;
import ghidra.util.exception.CancelledException; import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor; import ghidra.util.task.TaskMonitor;
@ -90,11 +90,11 @@ public class UnapplyMarkupItemTask extends VtTask {
Address destinationAddress = null; Address destinationAddress = null;
String source = null; String source = null;
if (correlation != null) { if (correlation != null) {
AddressRange range = AddressCorrelationRange range =
correlation.getCorrelatedDestinationRange(markupItem.getSourceAddress(), monitor); correlation.getCorrelatedDestinationRange(markupItem.getSourceAddress(), monitor);
if (range != null) { if (range != null) {
destinationAddress = range.getMinAddress(); destinationAddress = range.getMinAddress();
source = correlation.getName(); source = range.getCorrelatorName();
} }
} }

View file

@ -27,6 +27,7 @@ import ghidra.program.model.address.*;
import ghidra.program.model.listing.*; import ghidra.program.model.listing.*;
import ghidra.program.model.symbol.*; import ghidra.program.model.symbol.*;
import ghidra.program.util.AddressCorrelation; import ghidra.program.util.AddressCorrelation;
import ghidra.program.util.AddressCorrelationRange;
import ghidra.util.exception.CancelledException; import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor; import ghidra.util.task.TaskMonitor;
@ -191,7 +192,8 @@ public class ImpliedMatchUtils {
Address srcRefFromAddress = sourceRef.getFromAddress(); Address srcRefFromAddress = sourceRef.getFromAddress();
// Get the destination reference address corresponding to the given source reference address // Get the destination reference address corresponding to the given source reference address
AddressRange range = correlator.getCorrelatedDestinationRange(srcRefFromAddress, monitor); AddressCorrelationRange range =
correlator.getCorrelatedDestinationRange(srcRefFromAddress, monitor);
if (range == null) { if (range == null) {
return null; return null;
} }

View file

@ -147,8 +147,8 @@ public class MatchInfo {
return markupItemsCache.get(monitor); return markupItemsCache.get(monitor);
} }
private void setDefaultDestination(VTMarkupItem markupItem, private void setDefaultDestination(VTMarkupItem markupItem, AddressCorrelation correlation,
AddressCorrelation addressTranslator, TaskMonitor monitor) throws CancelledException { TaskMonitor monitor) throws CancelledException {
Address destinationAddress = getDestinationAddress(markupItem); Address destinationAddress = getDestinationAddress(markupItem);
Address sourceAddress = markupItem.getSourceAddress(); Address sourceAddress = markupItem.getSourceAddress();
@ -158,12 +158,12 @@ public class MatchInfo {
} }
String destinationAddressSource = null; String destinationAddressSource = null;
if (addressTranslator != null) { if (correlation != null) {
AddressRange correlatedDestinationRange = AddressCorrelationRange range =
addressTranslator.getCorrelatedDestinationRange(sourceAddress, monitor); correlation.getCorrelatedDestinationRange(sourceAddress, monitor);
if (correlatedDestinationRange != null) { if (range != null) {
destinationAddress = correlatedDestinationRange.getMinAddress(); destinationAddress = range.getMinAddress();
destinationAddressSource = addressTranslator.getName(); destinationAddressSource = range.getCorrelatorName();
} }
} }
@ -204,22 +204,23 @@ public class MatchInfo {
return null; return null;
} }
AddressCorrelation addressTranslator = getAddressTranslator(correlatorMgr); AddressCorrelation correlation = getAddressTranslator(correlatorMgr);
if (addressTranslator == null) { if (correlation == null) {
return null; return null;
} }
AddressRange correlatedDestinationRange = null;
AddressCorrelationRange range = null;
try { try {
correlatedDestinationRange = range = correlation.getCorrelatedDestinationRange(sourceAddress, TaskMonitor.DUMMY);
addressTranslator.getCorrelatedDestinationRange(sourceAddress, TaskMonitor.DUMMY);
} }
catch (CancelledException e) { catch (CancelledException e) {
// check for null below // check for null below
} }
if (correlatedDestinationRange == null) {
if (range == null) {
return null; return null;
} }
return correlatedDestinationRange.getMinAddress(); return range.getMinAddress();
} }
public VTMarkupItem getCurrentMarkupForLocation(ProgramLocation programLocation, public VTMarkupItem getCurrentMarkupForLocation(ProgramLocation programLocation,

View file

@ -17,7 +17,7 @@ package ghidra.feature.vt.api;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import ghidra.feature.vt.api.correlator.address.LastResortAddressCorrelator; import ghidra.feature.vt.api.correlator.address.LinearAddressCorrelator;
import ghidra.feature.vt.gui.plugin.*; import ghidra.feature.vt.gui.plugin.*;
import ghidra.framework.options.Options; import ghidra.framework.options.Options;
import ghidra.framework.options.SaveState; import ghidra.framework.options.SaveState;
@ -61,7 +61,7 @@ public class VTControllerTest extends AbstractGhidraHeadedIntegrationTest {
assertNotNull("The controller did not find any correlators", correlator); assertNotNull("The controller did not find any correlators", correlator);
// set some options settings // set some options settings
Options options = correlator.getOptions(LastResortAddressCorrelator.class); Options options = correlator.getOptions(LinearAddressCorrelator.class);
String testDefaultValue = "Test Default Value"; String testDefaultValue = "Test Default Value";
String testOptionKey = "Test Option Name"; String testOptionKey = "Test Option Name";
String value = options.getString(testOptionKey, testDefaultValue); String value = options.getString(testOptionKey, testDefaultValue);
@ -70,7 +70,7 @@ public class VTControllerTest extends AbstractGhidraHeadedIntegrationTest {
String firstNewOptionValue = "New Option Value"; String firstNewOptionValue = "New Option Value";
options.setString(testOptionKey, firstNewOptionValue); options.setString(testOptionKey, firstNewOptionValue);
assertEquals(firstNewOptionValue, options.getString(testOptionKey, null)); assertEquals(firstNewOptionValue, options.getString(testOptionKey, null));
correlator.setOptions(LastResortAddressCorrelator.class, options); correlator.setOptions(LinearAddressCorrelator.class, options);
// save the options // save the options
SaveState saveState = new SaveState(); SaveState saveState = new SaveState();
controller.writeConfigState(saveState); controller.writeConfigState(saveState);
@ -78,12 +78,12 @@ public class VTControllerTest extends AbstractGhidraHeadedIntegrationTest {
// change the options // change the options
String secondNewValue = "Second New Value"; String secondNewValue = "Second New Value";
options.setString(testOptionKey, secondNewValue); options.setString(testOptionKey, secondNewValue);
correlator.setOptions(LastResortAddressCorrelator.class, options); correlator.setOptions(LinearAddressCorrelator.class, options);
// pull the values again and make sure they are still correct (that writing the config // pull the values again and make sure they are still correct (that writing the config
// state did not change the cached controller and options) // state did not change the cached controller and options)
correlator = controller.getCorrelator(); correlator = controller.getCorrelator();
options = correlator.getOptions(LastResortAddressCorrelator.class); options = correlator.getOptions(LinearAddressCorrelator.class);
assertEquals(secondNewValue, options.getString(testOptionKey, null)); assertEquals(secondNewValue, options.getString(testOptionKey, null));
// restore the options // restore the options
@ -93,7 +93,7 @@ public class VTControllerTest extends AbstractGhidraHeadedIntegrationTest {
// (we have to pull the correlator and options again, as changing the config state may // (we have to pull the correlator and options again, as changing the config state may
// change the cached values in the controller) // change the cached values in the controller)
correlator = controller.getCorrelator(); correlator = controller.getCorrelator();
options = correlator.getOptions(LastResortAddressCorrelator.class); options = correlator.getOptions(LinearAddressCorrelator.class);
assertEquals(firstNewOptionValue, options.getString(testOptionKey, null)); assertEquals(firstNewOptionValue, options.getString(testOptionKey, null));
} }
} }

View file

@ -30,14 +30,12 @@ import ghidra.feature.vt.api.markuptype.*;
import ghidra.feature.vt.api.util.Stringable; import ghidra.feature.vt.api.util.Stringable;
import ghidra.feature.vt.gui.VTTestEnv; import ghidra.feature.vt.gui.VTTestEnv;
import ghidra.feature.vt.gui.plugin.VTController; import ghidra.feature.vt.gui.plugin.VTController;
import ghidra.features.codecompare.correlator.CodeCompareAddressCorrelation;
import ghidra.program.database.ProgramBuilder; import ghidra.program.database.ProgramBuilder;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressRange;
import ghidra.program.model.listing.*; import ghidra.program.model.listing.*;
import ghidra.program.model.symbol.SourceType; import ghidra.program.model.symbol.SourceType;
import ghidra.program.util.AddressCorrelation;
import ghidra.test.AbstractGhidraHeadedIntegrationTest; import ghidra.test.AbstractGhidraHeadedIntegrationTest;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor; import ghidra.util.task.TaskMonitor;
/** /**
@ -91,12 +89,13 @@ public class AddressCorrelationTest extends AbstractGhidraHeadedIntegrationTest
createSession(TEST_SOURCE_PROGRAM_NAME, TEST_DESTINATION_PROGRAM_NAME); createSession(TEST_SOURCE_PROGRAM_NAME, TEST_DESTINATION_PROGRAM_NAME);
vtTestEnv.showTool(); vtTestEnv.showTool();
addComment(CodeUnit.EOL_COMMENT, "0x0041222b", "Exact bytes comment."); addComment(CodeUnit.EOL_COMMENT, "0x0041222b", "Exact bytes comment.");
addProgramCorrelation(new ExactMatchBytesProgramCorrelatorFactory());
useMatch("0x00412210", "0x004121f0"); runCorrelator(new ExactMatchBytesProgramCorrelatorFactory());
checkAddressCorrelation(StraightLineCorrelation.NAME); selectMatch("0x00412210", "0x004121f0");
checkCommentMarkup(EolCommentMarkupType.INSTANCE, "0x0041222b", "Exact bytes comment.",
"0x0041220b"); validateCommentMarkupItems(EolCommentMarkupType.INSTANCE, "0x0041222b",
checkMarkupDestinationSource(StraightLineCorrelation.NAME, false); "Exact bytes comment.", "0x0041220b");
validateMarkupDestinationAddress(StraightLineCorrelation.NAME, false);
} }
@Test @Test
@ -105,12 +104,13 @@ public class AddressCorrelationTest extends AbstractGhidraHeadedIntegrationTest
createSession(TEST_SOURCE_PROGRAM_NAME, TEST_DESTINATION_PROGRAM_NAME); createSession(TEST_SOURCE_PROGRAM_NAME, TEST_DESTINATION_PROGRAM_NAME);
vtTestEnv.showTool(); vtTestEnv.showTool();
addComment(CodeUnit.PRE_COMMENT, "0x00412988", "Exact mnemonics comment."); addComment(CodeUnit.PRE_COMMENT, "0x00412988", "Exact mnemonics comment.");
addProgramCorrelation(new ExactMatchMnemonicsProgramCorrelatorFactory());
useMatch("0x00412950", "0x00412930"); runCorrelator(new ExactMatchMnemonicsProgramCorrelatorFactory());
checkAddressCorrelation(StraightLineCorrelation.NAME); selectMatch("0x00412950", "0x00412930");
checkCommentMarkup(PreCommentMarkupType.INSTANCE, "0x00412988", "Exact mnemonics comment.",
"0x00412968"); validateCommentMarkupItems(PreCommentMarkupType.INSTANCE, "0x00412988",
checkMarkupDestinationSource(StraightLineCorrelation.NAME, false); "Exact mnemonics comment.", "0x00412968");
validateMarkupDestinationAddress(StraightLineCorrelation.NAME, false);
} }
@Test @Test
@ -119,12 +119,13 @@ public class AddressCorrelationTest extends AbstractGhidraHeadedIntegrationTest
createSession(TEST_SOURCE_PROGRAM_NAME, TEST_DESTINATION_PROGRAM_NAME); createSession(TEST_SOURCE_PROGRAM_NAME, TEST_DESTINATION_PROGRAM_NAME);
vtTestEnv.showTool(); vtTestEnv.showTool();
addComment(CodeUnit.POST_COMMENT, "0x004129a2", "Exact instructions comment."); addComment(CodeUnit.POST_COMMENT, "0x004129a2", "Exact instructions comment.");
addProgramCorrelation(new ExactMatchInstructionsProgramCorrelatorFactory());
useMatch("0x00412950", "0x00412930"); runCorrelator(new ExactMatchInstructionsProgramCorrelatorFactory());
checkAddressCorrelation(StraightLineCorrelation.NAME); selectMatch("0x00412950", "0x00412930");
checkCommentMarkup(PostCommentMarkupType.INSTANCE, "0x004129a2",
validateCommentMarkupItems(PostCommentMarkupType.INSTANCE, "0x004129a2",
"Exact instructions comment.", "0x00412982"); "Exact instructions comment.", "0x00412982");
checkMarkupDestinationSource(StraightLineCorrelation.NAME, false); validateMarkupDestinationAddress(StraightLineCorrelation.NAME, false);
} }
@Test @Test
@ -135,14 +136,15 @@ public class AddressCorrelationTest extends AbstractGhidraHeadedIntegrationTest
vtTestEnv.showTool(); vtTestEnv.showTool();
addComment(CodeUnit.EOL_COMMENT, "0x004126dd", "Similar name eol comment."); addComment(CodeUnit.EOL_COMMENT, "0x004126dd", "Similar name eol comment.");
addComment(CodeUnit.PRE_COMMENT, "0x004126d7", "Similar name pre comment."); addComment(CodeUnit.PRE_COMMENT, "0x004126d7", "Similar name pre comment.");
addProgramCorrelation(new SimilarSymbolNameProgramCorrelatorFactory());
useMatch("0x00412690", "0x00412720"); runCorrelator(new SimilarSymbolNameProgramCorrelatorFactory());
checkAddressCorrelation(VTHashedFunctionAddressCorrelation.NAME); selectMatch("0x00412690", "0x00412720");
checkCommentMarkup(EolCommentMarkupType.INSTANCE, "0x004126dd", "Similar name eol comment.",
"0x0041277f"); validateCommentMarkupItems(EolCommentMarkupType.INSTANCE, "0x004126dd",
checkCommentMarkup(PreCommentMarkupType.INSTANCE, "0x004126d7", "Similar name pre comment.", "Similar name eol comment.", "0x0041277f");
"NO_ADDRESS"); validateCommentMarkupItems(PreCommentMarkupType.INSTANCE, "0x004126d7",
checkMarkupDestinationSource(VTHashedFunctionAddressCorrelation.NAME, true); "Similar name pre comment.", "NO_ADDRESS");
validateMarkupDestinationAddress(VTHashedFunctionAddressCorrelation.NAME, true);
} }
@Test @Test
@ -154,15 +156,26 @@ public class AddressCorrelationTest extends AbstractGhidraHeadedIntegrationTest
createSession(languageProgram1, languageProgram2); createSession(languageProgram1, languageProgram2);
vtTestEnv.showTool(); vtTestEnv.showTool();
// add source comment
addComment(CodeUnit.PLATE_COMMENT, "0x00401003", addComment(CodeUnit.PLATE_COMMENT, "0x00401003",
"Similar name plate comment not at entry."); "Similar name plate comment not at entry.");
addProgramCorrelation(new SimilarSymbolNameProgramCorrelatorFactory());
useMatch("0x00401000", "0x00402000"); // create correlation run
checkAddressCorrelation(LinearFunctionAddressCorrelation.NAME); runCorrelator(new SimilarSymbolNameProgramCorrelatorFactory());
// Check for our linear function correlation even though destination will be offcut.
checkCommentMarkup(PlateCommentMarkupType.INSTANCE, "0x00401003", // accept a match
"Similar name plate comment not at entry.", "0x00402003"); selectMatch("0x00401000", "0x00402000");
checkMarkupDestinationSource(LinearFunctionAddressCorrelation.NAME, false);
// Verify the entry point plate comment markup has a destination address.
validateCommentMarkupItems(PlateCommentMarkupType.INSTANCE, "0x00401000",
"First plate comment.", "0x00402000");
// The non-entry point plate comment markup has no address found when using the
// Code Compare correlators
validateCommentMarkupItems(PlateCommentMarkupType.INSTANCE, "0x00401003",
"Similar name plate comment not at entry.", "NO_ADDRESS");
validateMarkupDestinationAddress(CodeCompareAddressCorrelation.NAME, true);
} }
@Test @Test
@ -170,18 +183,18 @@ public class AddressCorrelationTest extends AbstractGhidraHeadedIntegrationTest
// Test a function match created by the Similar Symbol Name correlator where the // Test a function match created by the Similar Symbol Name correlator where the
// two programs are for different languages but the same processor with different // two programs are for different languages but the same processor with different
// instructions. // instructions.
Program languageProgram1 = buildProgram1("language1"); Program p1 = buildProgram1("language1");
Program languageProgram2 = buildProgram3("language3"); Program p2 = buildProgram3("language3");
createSession(p1, p2);
createSession(languageProgram1, languageProgram2);
vtTestEnv.showTool(); vtTestEnv.showTool();
addComment(CodeUnit.EOL_COMMENT, "0x00401003", "Similar name eol comment."); addComment(CodeUnit.EOL_COMMENT, "0x00401003", "Similar name eol comment.");
addProgramCorrelation(new SimilarSymbolNameProgramCorrelatorFactory());
useMatch("0x00401000", "0x00402000"); runCorrelator(new SimilarSymbolNameProgramCorrelatorFactory());
checkAddressCorrelation(VTHashedFunctionAddressCorrelation.NAME); selectMatch("0x00401000", "0x00402000");
checkCommentMarkup(EolCommentMarkupType.INSTANCE, "0x00401003", "Similar name eol comment.",
"NO_ADDRESS"); validateCommentMarkupItems(EolCommentMarkupType.INSTANCE, "0x00401003",
checkMarkupDestinationSource(VTHashedFunctionAddressCorrelation.NAME, true); "Similar name eol comment.", "NO_ADDRESS");
validateMarkupDestinationAddress(VTHashedFunctionAddressCorrelation.NAME, true);
} }
@Test @Test
@ -193,23 +206,24 @@ public class AddressCorrelationTest extends AbstractGhidraHeadedIntegrationTest
// processors with matching instructions. // processors with matching instructions.
Program p1 = buildProgram1("language1"); Program p1 = buildProgram1("language1");
Program p2 = buildProgram2("language2"); Program p2 = buildProgram2("language2");
createSession(p1, p2); createSession(p1, p2);
vtTestEnv.showTool(); vtTestEnv.showTool();
addComment(CodeUnit.PLATE_COMMENT, "0x00401000", "First plate comment."); addComment(CodeUnit.PLATE_COMMENT, "0x00401000", "First plate comment.");
addComment(CodeUnit.PLATE_COMMENT, "0x00401003", "Second plate comment."); addComment(CodeUnit.PLATE_COMMENT, "0x00401003", "Second plate comment.");
addProgramCorrelation(new SimilarSymbolNameProgramCorrelatorFactory());
useMatch("0x00401000", "0x00402000"); runCorrelator(new SimilarSymbolNameProgramCorrelatorFactory());
checkAddressCorrelation(LinearFunctionAddressCorrelation.NAME); selectMatch("0x00401000", "0x00402000");
// Verify the entry point plate comment markup has a destination address. // Verify the entry point plate comment markup has a destination address.
checkCommentMarkup(PlateCommentMarkupType.INSTANCE, "0x00401000", "First plate comment.", validateCommentMarkupItems(PlateCommentMarkupType.INSTANCE, "0x00401000",
"0x00402000"); "First plate comment.", "0x00402000");
assertEquals(addr("0x00402000", p2), getMatchingDestAddress("0x00401000")); // Expects a correlated address.
// Verify the non-entry point plate comment markup also has a destination address. // The non-entry point plate comment markup has no address found when using the
checkCommentMarkup(PlateCommentMarkupType.INSTANCE, "0x00401003", "Second plate comment.", // Code Compare correlators
"0x00402003"); validateCommentMarkupItems(PlateCommentMarkupType.INSTANCE, "0x00401003",
assertEquals(addr("0x00402003", p2), getMatchingDestAddress("0x00401003")); // Expects a correlated address. "Second plate comment.", "NO_ADDRESS");
checkMarkupDestinationSource(LinearFunctionAddressCorrelation.NAME, true);
validateMarkupDestinationAddress(CodeCompareAddressCorrelation.NAME, true);
} }
@Test @Test
@ -219,53 +233,29 @@ public class AddressCorrelationTest extends AbstractGhidraHeadedIntegrationTest
// function entry point as the destination. This uses a function match created by // function entry point as the destination. This uses a function match created by
// the Similar Symbol Name correlator where the two programs are for different // the Similar Symbol Name correlator where the two programs are for different
// languages but the same processor with different instructions. // languages but the same processor with different instructions.
Program languageProgram1 = buildProgram1("language1"); Program p1 = buildProgram1("language1");
Program languageProgram2 = buildProgram3("language3"); Program p2 = buildProgram3("language3");
createSession(p1, p2);
createSession(languageProgram1, languageProgram2);
vtTestEnv.showTool(); vtTestEnv.showTool();
addComment(CodeUnit.PLATE_COMMENT, "0x00401000", "First plate comment."); addComment(CodeUnit.PLATE_COMMENT, "0x00401000", "First plate comment.");
addComment(CodeUnit.PLATE_COMMENT, "0x00401003", "Second plate comment."); addComment(CodeUnit.PLATE_COMMENT, "0x00401003", "Second plate comment.");
addProgramCorrelation(new SimilarSymbolNameProgramCorrelatorFactory());
useMatch("0x00401000", "0x00402000"); runCorrelator(new SimilarSymbolNameProgramCorrelatorFactory());
checkAddressCorrelation(VTHashedFunctionAddressCorrelation.NAME); selectMatch("0x00401000", "0x00402000");
// Verify the entry point plate comment markup has a destination address. // Verify the entry point plate comment markup has a destination address.
checkCommentMarkup(PlateCommentMarkupType.INSTANCE, "0x00401000", "First plate comment.", validateCommentMarkupItems(PlateCommentMarkupType.INSTANCE, "0x00401000",
"0x00402000"); "First plate comment.", "0x00402000");
assertNull(getMatchingDestAddress("0x00401000")); // Expects no correlated address.
// Verify the non-entry point plate comment markup does not have a destination address. // Verify the non-entry point plate comment markup does not have a destination address.
checkCommentMarkup(PlateCommentMarkupType.INSTANCE, "0x00401003", "Second plate comment.", validateCommentMarkupItems(PlateCommentMarkupType.INSTANCE, "0x00401003",
"NO_ADDRESS"); "Second plate comment.", "NO_ADDRESS");
assertNull(getMatchingDestAddress("0x00401003")); // Expects no correlated address. validateMarkupDestinationAddress(VTHashedFunctionAddressCorrelation.NAME, true);
checkMarkupDestinationSource(VTHashedFunctionAddressCorrelation.NAME, true);
} }
//================================================================================================== //==================================================================================================
// Helper Methods // Helper Methods
//================================================================================================== //==================================================================================================
/**
* Gets the destination address that has been correlated to the indicated source address
* for the current function match that has been established in the test.
* @param srcAddressStr the source address
* @return the matching destination address. Otherwise null if the address correlation for
* the match couldn't determine a matching address.
*/
private Address getMatchingDestAddress(String srcAddressStr) {
Address srcAddress = addr(srcAddressStr, sourceProgram);
AddressCorrelation actualCorrelator =
controller.getCorrelator(sourceFunction, destinationFunction);
AddressRange destRange = null;
try {
destRange =
actualCorrelator.getCorrelatedDestinationRange(srcAddress, TaskMonitor.DUMMY);
}
catch (CancelledException e) {
// Do nothing. let this return null.
}
return (destRange != null) ? destRange.getMinAddress() : null;
}
private Program buildProgram1(String name) throws Exception { private Program buildProgram1(String name) throws Exception {
ProgramBuilder builder = new ProgramBuilder(name, ProgramBuilder._X86); ProgramBuilder builder = new ProgramBuilder(name, ProgramBuilder._X86);
try { try {
@ -357,7 +347,7 @@ public class AddressCorrelationTest extends AbstractGhidraHeadedIntegrationTest
* when creating matches. * when creating matches.
* @param correlatorFactory the factory for the desired program correlator. * @param correlatorFactory the factory for the desired program correlator.
*/ */
protected void addProgramCorrelation(VTProgramCorrelatorFactory correlatorFactory) { protected void runCorrelator(VTProgramCorrelatorFactory correlatorFactory) {
try { try {
correlator = vtTestEnv.correlate(correlatorFactory, null, TaskMonitor.DUMMY); correlator = vtTestEnv.correlate(correlatorFactory, null, TaskMonitor.DUMMY);
} }
@ -374,22 +364,6 @@ public class AddressCorrelationTest extends AbstractGhidraHeadedIntegrationTest
Boolean.TRUE); Boolean.TRUE);
} }
/**
* Checks the address correlation being used for the current testMatch and its current
* sourceFunction and destinationFunction to verify it is the one indicated by the
* addressCorrelationName.
* @param addressCorrelationName the name of the expected address correlation for determining
* the destination address of markup for the current function match.
*/
private void checkAddressCorrelation(String addressCorrelationName) {
AddressCorrelation actualCorrelator =
controller.getCorrelator(sourceFunction, destinationFunction);
String actualCorrelatorName = actualCorrelator.getName();
assertTrue("Unexpected address correlation of " + actualCorrelatorName +
" when expecting " + addressCorrelationName + ".",
actualCorrelatorName.equals(addressCorrelationName));
}
/** /**
* Checks all the markup items for the testMatch to see that their destination address * Checks all the markup items for the testMatch to see that their destination address
* has been determined by the address correlation that is indicated by the addressCorrelationName * has been determined by the address correlation that is indicated by the addressCorrelationName
@ -397,7 +371,7 @@ public class AddressCorrelationTest extends AbstractGhidraHeadedIntegrationTest
* @param addressCorrelationName the name of the expected address correlation for determining * @param addressCorrelationName the name of the expected address correlation for determining
* the destination address of non-function entry point markup items. * the destination address of non-function entry point markup items.
*/ */
private void checkMarkupDestinationSource(String addressCorrelationName, private void validateMarkupDestinationAddress(String addressCorrelationName,
boolean canBeNoAddress) { boolean canBeNoAddress) {
Collection<VTMarkupItem> appliableMarkupItems = Collection<VTMarkupItem> appliableMarkupItems =
controller.getMatchInfo(testMatch).getAppliableMarkupItems(TaskMonitor.DUMMY); // Initialize the cache. controller.getMatchInfo(testMatch).getAppliableMarkupItems(TaskMonitor.DUMMY); // Initialize the cache.
@ -430,7 +404,7 @@ public class AddressCorrelationTest extends AbstractGhidraHeadedIntegrationTest
* @param destination the destination address * @param destination the destination address
* @return the match or null if the indicated match isn't found. * @return the match or null if the indicated match isn't found.
*/ */
protected VTMatch getMatch(Address source, Address destination) { private VTMatch getMatch(Address source, Address destination) {
List<VTMatchSet> matchSets = session.getMatchSets(); List<VTMatchSet> matchSets = session.getMatchSets();
// Get matchSet 2 since 0 is manual matches and 1 is implied matches. // Get matchSet 2 since 0 is manual matches and 1 is implied matches.
VTMatchSet vtMatchSet = matchSets.get(2); VTMatchSet vtMatchSet = matchSets.get(2);
@ -448,7 +422,7 @@ public class AddressCorrelationTest extends AbstractGhidraHeadedIntegrationTest
* @param sourceAddressString the source address of the match's association. * @param sourceAddressString the source address of the match's association.
* @param destinationAddressString the destination address of the match's association. * @param destinationAddressString the destination address of the match's association.
*/ */
protected void useMatch(String sourceAddressString, String destinationAddressString) { private void selectMatch(String sourceAddressString, String destinationAddressString) {
sourceAddress = addr(sourceAddressString, sourceProgram); sourceAddress = addr(sourceAddressString, sourceProgram);
destinationAddress = addr(destinationAddressString, destinationProgram); destinationAddress = addr(destinationAddressString, destinationProgram);
@ -488,13 +462,13 @@ public class AddressCorrelationTest extends AbstractGhidraHeadedIntegrationTest
* @param desiredCommentMarkupType the comment markup type we are checking * @param desiredCommentMarkupType the comment markup type we are checking
* @param sourceAddressString the source address of the markup * @param sourceAddressString the source address of the markup
* @param comment the expected comment * @param comment the expected comment
* @param destinationAddressString the expected destination address for the markup * @param expectedDestAddrString the expected destination address for the markup
*/ */
private void checkCommentMarkup(VTMarkupType desiredCommentMarkupType, private void validateCommentMarkupItems(VTMarkupType desiredCommentMarkupType,
String sourceAddressString, String comment, String destinationAddressString) { String sourceAddressString, String comment, String expectedDestAddrString) {
Address srcAddress = addr(sourceAddressString, sourceProgram); Address srcAddress = addr(sourceAddressString, sourceProgram);
Address destAddress = destinationAddressString.equals("NO_ADDRESS") ? Address.NO_ADDRESS Address expectedDestAddr = expectedDestAddrString.equals("NO_ADDRESS") ? Address.NO_ADDRESS
: addr(destinationAddressString, destinationProgram); : addr(expectedDestAddrString, destinationProgram);
Collection<VTMarkupItem> appliableMarkupItems = Collection<VTMarkupItem> appliableMarkupItems =
controller.getMatchInfo(testMatch).getAppliableMarkupItems(TaskMonitor.DUMMY); // Initialize the cache. controller.getMatchInfo(testMatch).getAppliableMarkupItems(TaskMonitor.DUMMY); // Initialize the cache.
@ -521,18 +495,19 @@ public class AddressCorrelationTest extends AbstractGhidraHeadedIntegrationTest
} }
boolean isNoAddress = boolean isNoAddress =
markupDestAddress == null || markupDestAddress == Address.NO_ADDRESS; markupDestAddress == null || markupDestAddress == Address.NO_ADDRESS;
if (destAddress == Address.NO_ADDRESS) { if (expectedDestAddr == Address.NO_ADDRESS) {
assertTrue("Unexpected destination address of NO_ADDRESS for " + assertTrue("Unexpected destination address of NO_ADDRESS for " +
vtMarkupItem.getMarkupType().getDisplayName() + " markup @ " + vtMarkupItem.getMarkupType().getDisplayName() + " markup @ " +
vtMarkupItem.getSourceAddress().toString() + ".", isNoAddress); vtMarkupItem.getSourceAddress().toString() + ".", isNoAddress);
return; return;
} }
assertTrue( assertTrue(
"Unexpected destination address of " + markupDestAddress.toString() + "Unexpected destination address of " + markupDestAddress.toString() +
" when expecting " + destAddress.toString() + " for " + " when expecting " + expectedDestAddr.toString() + " for " +
vtMarkupItem.getMarkupType().getDisplayName() + " markup @ " + vtMarkupItem.getMarkupType().getDisplayName() + " markup @ " +
vtMarkupItem.getSourceAddress().toString() + ".", vtMarkupItem.getSourceAddress().toString() + ".",
markupDestAddress.equals(destAddress)); markupDestAddress.equals(expectedDestAddr));
return; return;
} }
} }

View file

@ -1,6 +1,5 @@
/* ### /* ###
* IP: GHIDRA * IP: GHIDRA
* REVIEWED: YES
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -35,10 +34,10 @@ import ghidra.util.datastruct.LRUMap;
public class FixedSizeMRUCachingFactory<K, V> implements Factory<K, V> { public class FixedSizeMRUCachingFactory<K, V> implements Factory<K, V> {
private LRUMap<K, V> cache; private LRUMap<K, V> cache;
private Factory<K, V> delegate; private Factory<K, V> factory;
public FixedSizeMRUCachingFactory(Factory<K, V> factory, int size) { public FixedSizeMRUCachingFactory(Factory<K, V> factory, int size) {
this.delegate = factory; this.factory = factory;
this.cache = new LRUMap<K, V>(size); this.cache = new LRUMap<K, V>(size);
} }
@ -49,7 +48,7 @@ public class FixedSizeMRUCachingFactory<K, V> implements Factory<K, V> {
return value; return value;
} }
value = delegate.get(key); value = factory.get(key);
cache.put(key, value); cache.put(key, value);
return value; return value;
} }

View file

@ -16,7 +16,6 @@
package ghidra.program.util; package ghidra.program.util;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressRange;
import ghidra.util.exception.CancelledException; import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor; import ghidra.util.task.TaskMonitor;
@ -31,19 +30,20 @@ public interface AddressCorrelation {
* Returns the AddressRange of a set of addresses in the destination * Returns the AddressRange of a set of addresses in the destination
* program that correlates to corresponding range in the source program. * program that correlates to corresponding range in the source program.
* *
* @param sourceAddress * @param sourceAddress the source program address
* the source program address
* @param monitor the task monitor * @param monitor the task monitor
* @return the destination program address range, or null if the source program address maps * @return the destination program address range, or null if there is not address range mapped
* to one that is "deleted" in the destination program
* @throws CancelledException if cancelled * @throws CancelledException if cancelled
*/ */
public AddressRange getCorrelatedDestinationRange(Address sourceAddress, TaskMonitor monitor) public AddressCorrelationRange getCorrelatedDestinationRange(Address sourceAddress,
throws CancelledException; TaskMonitor monitor) throws CancelledException;
/** /**
* Returns the name of the correlating algorithm. * This method is no longer part of the API. Leaving a default implementation to reduce
* @return the name of the correlating algorithm. * breaking clients.
* @return the simple class name of the implementing class
*/ */
public String getName(); public default String getName() {
return getClass().getSimpleName();
}
} }

View file

@ -0,0 +1,48 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.program.util;
import java.util.Objects;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressRange;
/**
* A simple object that holds an {@link AddressCorrelation} address range and then name of the
* correlation.s
*/
public class AddressCorrelationRange {
private AddressRange range;
private String correlatorName;
public AddressCorrelationRange(AddressRange range, String correlatorName) {
this.range = Objects.requireNonNull(range);
this.correlatorName = Objects.requireNonNull(correlatorName);
}
public Address getMinAddress() {
return range.getMinAddress();
}
public AddressRange getRange() {
return range;
}
public String getCorrelatorName() {
return correlatorName;
}
}

View file

@ -23,9 +23,34 @@ import ghidra.program.model.listing.Function;
/** /**
* Interface for address correlation algorithms that can generate an address mapping from one * Interface for address correlation algorithms that can generate an address mapping from one
* set of program addresses to another. * set of program addresses to another.
* <p>
* This interface supplies a {@link #getPriority() priority} of {@link #DEFAULT_PRIORITY}.
* {@link DiscoverableAddressCorrelator discoverable} correlators can change this priority to be a
* lower value to be run before the supplied system correlators. Generally, the more specific or
* restrictive a correlator, the earlier (higher priority) it should be.
*/ */
public interface AddressCorrelator { public interface AddressCorrelator {
/**
* The default priority. This applies to client-supplied {@link DiscoverableAddressCorrelator}s
*/
public static final int DEFAULT_PRIORITY = 500;
/**
* A high priority (low number value) for correlators that should used before other correlators
*/
public static final int EARLY_PRIORITY = 100;
/**
* A low priority (high number value) for correlators that should used after other correlators
*/
public static final int LATE_CHANCE_PRIORITY = 1000;
/**
* A value used to raise or lower priorities.
*/
public static final int PRIORITY_OFFSET = 10;
/** /**
* Returns an address mapping from one function to another. * Returns an address mapping from one function to another.
* @param sourceFunction the source function. * @param sourceFunction the source function.
@ -61,4 +86,18 @@ public interface AddressCorrelator {
* @return the options with the default settings for this correlator. * @return the options with the default settings for this correlator.
*/ */
public Options getDefaultOptions(); public Options getDefaultOptions();
/**
* Returns a number based on an arbitrary number scheme that dictates the order that correlators
* should be used. If a correlator returns a null value from one of the {@code correlate()}
* methods, then the next highest priority correlator will be called, and so on until a non-null
* correlation is found or all correlators have been called.
* <p>
* A lower number value is a higher priority. See {@link #DEFAULT_PRIORITY}.
*
* @return the priority
*/
public default int getPriority() {
return DEFAULT_PRIORITY;
}
} }

View file

@ -15,7 +15,8 @@
*/ */
package ghidra.program.util; package ghidra.program.util;
import ghidra.program.model.address.*; import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressRangeImpl;
import ghidra.program.model.listing.Data; import ghidra.program.model.listing.Data;
import ghidra.util.exception.CancelledException; import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor; import ghidra.util.task.TaskMonitor;
@ -30,13 +31,14 @@ public class LinearDataAddressCorrelation implements AddressCorrelation {
} }
@Override @Override
public AddressRange getCorrelatedDestinationRange(Address sourceAddress, TaskMonitor monitor) public AddressCorrelationRange getCorrelatedDestinationRange(Address sourceAddress,
throws CancelledException { TaskMonitor monitor) throws CancelledException {
long offset = sourceAddress.getOffset(); long offset = sourceAddress.getOffset();
long base = sourceData.getAddress().getOffset(); long base = sourceData.getAddress().getOffset();
long delta = offset - base; long delta = offset - base;
Address address = destinationData.getAddress().add(delta); Address address = destinationData.getAddress().add(delta);
return new AddressRangeImpl(address, address); AddressRangeImpl range = new AddressRangeImpl(address, address);
return new AddressCorrelationRange(range, getName());
} }
@Override @Override

View file

@ -13,13 +13,13 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package ghidra.feature.vt.api.correlator.address; package ghidra.program.util;
import java.util.*; import java.util.HashMap;
import java.util.Map;
import ghidra.program.model.address.*; import ghidra.program.model.address.*;
import ghidra.program.model.listing.*; import ghidra.program.model.listing.*;
import ghidra.program.util.AddressCorrelation;
import ghidra.util.exception.CancelledException; import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor; import ghidra.util.task.TaskMonitor;
@ -31,7 +31,7 @@ public class LinearFunctionAddressCorrelation implements AddressCorrelation {
private final Function sourceFunction; private final Function sourceFunction;
private final Function destinationFunction; private final Function destinationFunction;
LinearFunctionAddressCorrelation(Function sourceFunction, Function destinationFunction) { public LinearFunctionAddressCorrelation(Function sourceFunction, Function destinationFunction) {
this.sourceFunction = sourceFunction; this.sourceFunction = sourceFunction;
this.destinationFunction = destinationFunction; this.destinationFunction = destinationFunction;
} }
@ -42,8 +42,8 @@ public class LinearFunctionAddressCorrelation implements AddressCorrelation {
} }
@Override @Override
public AddressRange getCorrelatedDestinationRange(Address sourceAddress, TaskMonitor monitor) public AddressCorrelationRange getCorrelatedDestinationRange(Address sourceAddress,
throws CancelledException { TaskMonitor monitor) throws CancelledException {
initialize(monitor); initialize(monitor);
AddressRange toRange = cachedForwardAddressMap.get(sourceAddress); AddressRange toRange = cachedForwardAddressMap.get(sourceAddress);
if (toRange == null) { if (toRange == null) {
@ -51,7 +51,7 @@ public class LinearFunctionAddressCorrelation implements AddressCorrelation {
Address destinationAddress = getDestinationAddress(percentOffset); Address destinationAddress = getDestinationAddress(percentOffset);
toRange = new AddressRangeImpl(destinationAddress, destinationAddress); toRange = new AddressRangeImpl(destinationAddress, destinationAddress);
} }
return toRange; return new AddressCorrelationRange(toRange, getName());
} }
private void initialize(TaskMonitor monitor) { private void initialize(TaskMonitor monitor) {
@ -66,9 +66,7 @@ public class LinearFunctionAddressCorrelation implements AddressCorrelation {
long accumulatedLength = 0; long accumulatedLength = 0;
Iterator<AddressRange> iterator = srcBody.iterator(); for (AddressRange range : srcBody) {
while (iterator.hasNext()) {
AddressRange range = iterator.next();
if (range.getMaxAddress().compareTo(address) < 0) { if (range.getMaxAddress().compareTo(address) < 0) {
accumulatedLength += range.getLength(); accumulatedLength += range.getLength();
} }