Merge remote-tracking branch 'origin/GP-3087_Dan_excludeExternalInEmulation--SQUASHED'

This commit is contained in:
Ryan Kurtz 2023-02-13 15:28:20 -05:00
commit 0565d03e69
8 changed files with 113 additions and 63 deletions

View file

@ -34,6 +34,7 @@ import ghidra.program.util.ProgramLocation;
import ghidra.trace.model.*; import ghidra.trace.model.*;
import ghidra.trace.model.modules.TraceConflictedMappingException; import ghidra.trace.model.modules.TraceConflictedMappingException;
import ghidra.util.MathUtilities; import ghidra.util.MathUtilities;
import ghidra.util.MessageType;
import ghidra.util.layout.PairLayout; import ghidra.util.layout.PairLayout;
public class DebuggerAddMappingDialog extends ReusableDialogComponentProvider { public class DebuggerAddMappingDialog extends ReusableDialogComponentProvider {
@ -52,7 +53,7 @@ public class DebuggerAddMappingDialog extends ReusableDialogComponentProvider {
private final GSpanField fieldSpan = new GSpanField(); private final GSpanField fieldSpan = new GSpanField();
public DebuggerAddMappingDialog() { public DebuggerAddMappingDialog() {
super("Add Static Mapping", false, false, true, false); super("Add Static Mapping", false, true, true, false);
populateComponents(); populateComponents();
} }
@ -103,6 +104,8 @@ public class DebuggerAddMappingDialog extends ReusableDialogComponentProvider {
rigFocusAndEnter(fieldLength, this::lengthChanged); rigFocusAndEnter(fieldLength, this::lengthChanged);
rigFocusAndEnter(fieldSpan, this::spanChanged); rigFocusAndEnter(fieldSpan, this::spanChanged);
fieldSpan.setLifespan(Lifespan.nowOn(0));
addWorkPanel(panel); addWorkPanel(panel);
addApplyButton(); addApplyButton();
@ -287,6 +290,12 @@ public class DebuggerAddMappingDialog extends ReusableDialogComponentProvider {
return bi.longValue(); // Do not use exact. It checks bitLength again, and considers sign. return bi.longValue(); // Do not use exact. It checks bitLength again, and considers sign.
} }
@Override
protected void dialogShown() {
super.dialogShown();
setStatusText("");
}
@Override @Override
protected void applyCallback() { protected void applyCallback() {
TraceLocation from = new DefaultTraceLocation(trace, null, fieldSpan.getLifespan(), TraceLocation from = new DefaultTraceLocation(trace, null, fieldSpan.getLifespan(),
@ -295,13 +304,20 @@ public class DebuggerAddMappingDialog extends ReusableDialogComponentProvider {
fieldProgRange.getRange().getMinAddress()); fieldProgRange.getRange().getMinAddress());
try { try {
mappingService.addMapping(from, to, getLength(), true); mappingService.addMapping(from, to, getLength(), false);
setStatusText("");
} }
catch (TraceConflictedMappingException e) { catch (TraceConflictedMappingException e) {
throw new AssertionError(e); // I said truncateExisting setStatusText(e.getMessage(), MessageType.ERROR);
} }
} }
@Override
protected void dismissCallback() {
setStatusText("");
super.dismissCallback();
}
/** /**
* Set the values of the fields * Set the values of the fields
* *

View file

@ -659,8 +659,13 @@ public class DebuggerModulesProvider extends ComponentProviderAdapter {
if (currentProgram == null || current.getTrace() == null) { if (currentProgram == null || current.getTrace() == null) {
return; return;
} }
staticMappingService.addIdentityMapping(current.getTrace(), currentProgram, try {
Lifespan.nowOn(traceManager.getCurrentSnap()), true); staticMappingService.addIdentityMapping(current.getTrace(), currentProgram,
Lifespan.nowOn(traceManager.getCurrentSnap()), false);
}
catch (TraceConflictedMappingException e) {
Msg.showError(this, null, "Map Identically", e.getMessage());
}
} }
private void activatedMapManually(ActionContext ignored) { private void activatedMapManually(ActionContext ignored) {

View file

@ -291,11 +291,11 @@ public class DebuggerStaticMappingProvider extends ComponentProviderAdapter
: traceLen == 0 ? progLen : MathUtilities.unsignedMin(progLen, traceLen); : traceLen == 0 ? progLen : MathUtilities.unsignedMin(progLen, traceLen);
Address progStart = progLen != 0 ? progSel.getMinAddress() : progLoc.getAddress(); Address progStart = progLen != 0 ? progSel.getMinAddress() : progLoc.getAddress();
Address traceStart = traceLen != 0 ? traceSel.getMinAddress() : traceLoc.getAddress(); Address traceStart = traceLen != 0 ? traceSel.getMinAddress() : traceLoc.getAddress();
TraceProgramView view = (TraceProgramView) traceLoc.getProgram(); long snap = traceManager.getCurrentSnap();
try { try {
addMappingDialog.setValues(progLoc.getProgram(), currentTrace, progStart, traceStart, addMappingDialog.setValues(progLoc.getProgram(), currentTrace, progStart, traceStart,
length, Lifespan.nowOn(view.getSnap())); length, Lifespan.nowOn(snap));
} }
catch (AddressOverflowException e) { catch (AddressOverflowException e) {
Msg.showError(this, null, "Add Mapping", "Error populating dialog"); Msg.showError(this, null, "Add Mapping", "Error populating dialog");

View file

@ -22,6 +22,7 @@ import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
import ghidra.app.plugin.core.debug.service.modules.DebuggerStaticMappingUtils; import ghidra.app.plugin.core.debug.service.modules.DebuggerStaticMappingUtils;
import ghidra.app.plugin.core.debug.service.modules.DebuggerStaticMappingUtils.Extrema;
import ghidra.app.services.DebuggerEmulationService; import ghidra.app.services.DebuggerEmulationService;
import ghidra.dbg.target.*; import ghidra.dbg.target.*;
import ghidra.dbg.target.schema.TargetObjectSchema; import ghidra.dbg.target.schema.TargetObjectSchema;
@ -41,7 +42,8 @@ import ghidra.trace.model.target.TraceObject;
import ghidra.trace.model.target.TraceObjectKeyPath; import ghidra.trace.model.target.TraceObjectKeyPath;
import ghidra.trace.model.thread.*; import ghidra.trace.model.thread.*;
import ghidra.trace.model.time.TraceSnapshot; import ghidra.trace.model.time.TraceSnapshot;
import ghidra.util.*; import ghidra.util.DifferenceAddressSetView;
import ghidra.util.NumericUtilities;
import ghidra.util.database.UndoableTransaction; import ghidra.util.database.UndoableTransaction;
import ghidra.util.exception.DuplicateNameException; import ghidra.util.exception.DuplicateNameException;
@ -116,18 +118,6 @@ public enum ProgramEmulationUtils {
return result; return result;
} }
static class Extrema {
Address min = null;
Address max = null;
void consider(AddressRange range) {
min = min == null ? range.getMinAddress()
: ComparatorMath.cmin(min, range.getMinAddress());
max = max == null ? range.getMaxAddress()
: ComparatorMath.cmax(max, range.getMaxAddress());
}
}
/** /**
* Create regions for each block in a program, without relocation, and map the program in * Create regions for each block in a program, without relocation, and map the program in
* *
@ -149,10 +139,7 @@ public enum ProgramEmulationUtils {
Map<AddressSpace, Extrema> extremaBySpace = new HashMap<>(); Map<AddressSpace, Extrema> extremaBySpace = new HashMap<>();
try { try {
for (MemoryBlock block : program.getMemory().getBlocks()) { for (MemoryBlock block : program.getMemory().getBlocks()) {
if (!block.isLoaded()) { if (!DebuggerStaticMappingUtils.isReal(block)) {
continue;
}
if (block.isOverlay()) {
continue; continue;
} }
AddressRange range = new AddressRangeImpl(block.getStart(), block.getEnd()); AddressRange range = new AddressRangeImpl(block.getStart(), block.getEnd());
@ -170,9 +157,8 @@ public enum ProgramEmulationUtils {
for (Extrema extrema : extremaBySpace.values()) { for (Extrema extrema : extremaBySpace.values()) {
DebuggerStaticMappingUtils.addMapping( DebuggerStaticMappingUtils.addMapping(
new DefaultTraceLocation(trace, null, Lifespan.nowOn(snapshot.getKey()), new DefaultTraceLocation(trace, null, Lifespan.nowOn(snapshot.getKey()),
extrema.min), extrema.getMin()),
new ProgramLocation(program, extrema.min), new ProgramLocation(program, extrema.getMin()), extrema.getLength(), false);
extrema.max.subtract(extrema.min), false);
} }
} }
catch (TraceOverlappedRegionException | DuplicateNameException catch (TraceOverlappedRegionException | DuplicateNameException

View file

@ -25,11 +25,13 @@ import ghidra.framework.model.ProjectData;
import ghidra.program.model.address.*; import ghidra.program.model.address.*;
import ghidra.program.model.listing.Library; import ghidra.program.model.listing.Library;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.MemoryBlock;
import ghidra.program.model.symbol.ExternalManager; import ghidra.program.model.symbol.ExternalManager;
import ghidra.program.util.ProgramLocation; import ghidra.program.util.ProgramLocation;
import ghidra.trace.model.*; import ghidra.trace.model.*;
import ghidra.trace.model.modules.*; import ghidra.trace.model.modules.*;
import ghidra.trace.model.program.TraceProgramView; import ghidra.trace.model.program.TraceProgramView;
import ghidra.util.ComparatorMath;
import ghidra.util.Msg; import ghidra.util.Msg;
public enum DebuggerStaticMappingUtils { public enum DebuggerStaticMappingUtils {
@ -153,42 +155,72 @@ public enum DebuggerStaticMappingUtils {
addMapping(fromLoc, toLoc, length, truncateExisting); addMapping(fromLoc, toLoc, length, truncateExisting);
} }
public static void addIdentityMapping(Trace from, Program toProgram, Lifespan lifespan, public static class Extrema {
boolean truncateExisting) { private Address min = null;
Map<String, Address> mins = new HashMap<>(); private Address max = null;
Map<String, Address> maxs = new HashMap<>();
for (AddressRange range : toProgram.getMemory().getAddressRanges()) { public void consider(AddressRange range) {
mins.compute(range.getAddressSpace().getName(), (n, min) -> { min = min == null ? range.getMinAddress()
Address can = range.getMinAddress(); : ComparatorMath.cmin(min, range.getMinAddress());
if (min == null || can.compareTo(min) < 0) { max = max == null ? range.getMaxAddress()
return can; : ComparatorMath.cmax(max, range.getMaxAddress());
}
return min;
});
maxs.compute(range.getAddressSpace().getName(), (n, max) -> {
Address can = range.getMaxAddress();
if (max == null || can.compareTo(max) > 0) {
return can;
}
return max;
});
} }
for (String name : mins.keySet()) {
AddressRange range = clippedRange(from, name, mins.get(name).getOffset(), public Address getMin() {
maxs.get(name).getOffset()); return min;
if (range == null) { }
public Address getMax() {
return max;
}
public long getLength() {
return max.subtract(min) + 1;
}
}
public static boolean isReal(MemoryBlock block) {
return block.isLoaded() && !block.isOverlay() && !block.isExternalBlock();
}
public static void addIdentityMapping(Trace from, Program toProgram, Lifespan lifespan,
boolean truncateExisting) throws TraceConflictedMappingException {
AddressSet failures = new AddressSet();
Set<TraceStaticMapping> conflicts = new HashSet<>();
Map<AddressSpace, Extrema> extremaBySpace = new HashMap<>();
for (MemoryBlock block : toProgram.getMemory().getBlocks()) {
if (!isReal(block)) {
continue;
}
AddressRange range = new AddressRangeImpl(block.getStart(), block.getEnd());
extremaBySpace.computeIfAbsent(range.getAddressSpace(), s -> new Extrema())
.consider(range);
}
for (Extrema extrema : extremaBySpace.values()) {
AddressRange fromRange =
clippedRange(from, extrema.getMin().getAddressSpace().getName(),
extrema.getMin().getOffset(), extrema.getMax().getOffset());
if (fromRange == null) {
continue; continue;
} }
try { try {
addMapping(new DefaultTraceLocation(from, null, lifespan, range.getMinAddress()), addMapping(
new ProgramLocation(toProgram, mins.get(name)), range.getLength(), new DefaultTraceLocation(from, null, lifespan, fromRange.getMinAddress()),
new ProgramLocation(toProgram, extrema.getMin()), fromRange.getLength(),
truncateExisting); truncateExisting);
} }
catch (TraceConflictedMappingException e) { catch (TraceConflictedMappingException e) {
failures.add(fromRange);
conflicts.addAll(e.getConflicts());
Msg.error(DebuggerStaticMappingUtils.class, Msg.error(DebuggerStaticMappingUtils.class,
"Could not add identity mapping " + range + ": " + e.getMessage()); "Could not add identity mapping " + fromRange + ": " + e.getMessage());
} }
} }
if (!failures.isEmpty()) {
throw new TraceConflictedMappingException("Conflicting mappings for " + failures,
conflicts);
}
} }
protected static AddressRange clippedRange(Trace trace, String spaceName, long min, protected static AddressRange clippedRange(Trace trace, String spaceName, long min,

View file

@ -112,6 +112,13 @@ public class DBTraceStaticMapping extends DBAnnotatedObject
this.manager = manager; this.manager = manager;
} }
@Override
public String toString() {
return String.format(
"Mapping: dynamic=%s,program=%s,address=%s,length=0x%x,shift=0x%x,lifespan=%s",
traceAddress, staticProgramURL, staticAddress, length, shift, lifespan);
}
@Override @Override
protected void fresh(boolean created) throws IOException { protected void fresh(boolean created) throws IOException {
if (created) { if (created) {

View file

@ -85,8 +85,9 @@ public class DBTraceStaticMappingManager implements TraceStaticMappingManager, D
DBTraceStaticMapping conflict = DBTraceStaticMapping conflict =
findAnyConflicting(range, lifespan, toProgramURL, toAddress); findAnyConflicting(range, lifespan, toProgramURL, toAddress);
if (conflict != null) { if (conflict != null) {
// TODO: Find all conflicts?
throw new TraceConflictedMappingException("Another mapping would conflict", throw new TraceConflictedMappingException("Another mapping would conflict",
conflict); Set.of(conflict));
} }
// TODO: A more sophisticated coverage check? // TODO: A more sophisticated coverage check?
// TODO: Better coalescing // TODO: Better coalescing
@ -133,8 +134,7 @@ public class DBTraceStaticMappingManager implements TraceStaticMappingManager, D
@Override @Override
public DBTraceStaticMapping findAnyConflicting(AddressRange range, Lifespan lifespan, public DBTraceStaticMapping findAnyConflicting(AddressRange range, Lifespan lifespan,
URL toProgramURL, URL toProgramURL, String toAddress) {
String toAddress) {
for (DBTraceStaticMapping mapping : mappingsByAddress.head(range.getMaxAddress(), for (DBTraceStaticMapping mapping : mappingsByAddress.head(range.getMaxAddress(),
true).descending().values()) { true).descending().values()) {
if (!mapping.conflictsWith(range, lifespan, toProgramURL, toAddress)) { if (!mapping.conflictsWith(range, lifespan, toProgramURL, toAddress)) {

View file

@ -15,15 +15,19 @@
*/ */
package ghidra.trace.model.modules; package ghidra.trace.model.modules;
public class TraceConflictedMappingException extends Exception { import java.util.Collection;
private final TraceStaticMapping conflict; import java.util.Set;
public TraceConflictedMappingException(String message, TraceStaticMapping conflict) { public class TraceConflictedMappingException extends RuntimeException {
super(message); private final Set<TraceStaticMapping> conflicts;
this.conflict = conflict;
public TraceConflictedMappingException(String message,
Collection<TraceStaticMapping> conflicts) {
super(message + ": " + conflicts);
this.conflicts = Set.copyOf(conflicts);
} }
public TraceStaticMapping getConflict() { public Set<TraceStaticMapping> getConflicts() {
return conflict; return conflicts;
} }
} }