mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-09-28 17:11:11 -09:00
GP-890: Added dialog for adding mappings and fixing various db/ui issues re/ length.
This commit is contained in:
@@ -0,0 +1,378 @@
|
||||
/* ###
|
||||
* 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.app.plugin.core.debug.gui.modules;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.awt.Font;
|
||||
import java.awt.event.*;
|
||||
import java.math.BigInteger;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
|
||||
import com.google.common.collect.Range;
|
||||
|
||||
import docking.DialogComponentProvider;
|
||||
import docking.widgets.model.GAddressRangeField;
|
||||
import docking.widgets.model.GLifespanField;
|
||||
import ghidra.app.services.DebuggerStaticMappingService;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
import ghidra.trace.model.*;
|
||||
import ghidra.trace.model.modules.TraceConflictedMappingException;
|
||||
import ghidra.util.MathUtilities;
|
||||
import ghidra.util.database.UndoableTransaction;
|
||||
import ghidra.util.layout.PairLayout;
|
||||
|
||||
public class DebuggerAddMappingDialog extends DialogComponentProvider {
|
||||
private static final String HEX_BIT64 = "0x" + BigInteger.ONE.shiftLeft(64).toString(16);
|
||||
|
||||
private DebuggerStaticMappingService mappingService;
|
||||
|
||||
private Program program;
|
||||
private Trace trace;
|
||||
|
||||
private final JLabel labelProg = new JLabel();
|
||||
private final GAddressRangeField fieldProgRange = new GAddressRangeField();
|
||||
private final JLabel labelTrace = new JLabel();
|
||||
private final GAddressRangeField fieldTraceRange = new GAddressRangeField();
|
||||
private final JTextField fieldLength = new JTextField();
|
||||
private final GLifespanField fieldSpan = new GLifespanField();
|
||||
|
||||
public DebuggerAddMappingDialog() {
|
||||
super("Add Static Mapping", false, false, true, false);
|
||||
|
||||
populateComponents();
|
||||
}
|
||||
|
||||
protected static void rigFocusAndEnter(Component c, Runnable runnable) {
|
||||
c.addFocusListener(new FocusAdapter() {
|
||||
@Override
|
||||
public void focusLost(FocusEvent e) {
|
||||
runnable.run();
|
||||
}
|
||||
});
|
||||
c.addKeyListener(new KeyAdapter() {
|
||||
@Override
|
||||
public void keyPressed(KeyEvent e) {
|
||||
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
|
||||
runnable.run();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected void populateComponents() {
|
||||
JPanel panel = new JPanel(new PairLayout(5, 5));
|
||||
|
||||
panel.setBorder(new EmptyBorder(10, 10, 10, 10));
|
||||
|
||||
panel.add(new JLabel("Program: "));
|
||||
panel.add(labelProg);
|
||||
|
||||
panel.add(new JLabel("Static Range: "));
|
||||
panel.add(fieldProgRange);
|
||||
|
||||
panel.add(new JLabel("Trace: "));
|
||||
panel.add(labelTrace);
|
||||
|
||||
panel.add(new JLabel("Dynamic Range: "));
|
||||
panel.add(fieldTraceRange);
|
||||
|
||||
panel.add(new JLabel("Length: "));
|
||||
fieldLength.setFont(Font.decode("monospaced"));
|
||||
panel.add(fieldLength);
|
||||
|
||||
panel.add(new JLabel("Lifespan: "));
|
||||
panel.add(fieldSpan);
|
||||
|
||||
rigFocusAndEnter(fieldProgRange, this::progRangeChanged);
|
||||
rigFocusAndEnter(fieldTraceRange, this::traceRangeChanged);
|
||||
rigFocusAndEnter(fieldLength, this::lengthChanged);
|
||||
rigFocusAndEnter(fieldSpan, this::spanChanged);
|
||||
|
||||
addWorkPanel(panel);
|
||||
|
||||
addApplyButton();
|
||||
addDismissButton();
|
||||
setDefaultButton(null);
|
||||
}
|
||||
|
||||
public void setMappingService(DebuggerStaticMappingService mappingService) {
|
||||
this.mappingService = mappingService;
|
||||
}
|
||||
|
||||
protected static void revalidateByLength(GAddressRangeField adjusted,
|
||||
GAddressRangeField other) {
|
||||
AddressRange adjRange = adjusted.getRange();
|
||||
if (adjRange == null) {
|
||||
return;
|
||||
}
|
||||
long lengthMinus1 = adjRange.getMaxAddress().subtract(adjRange.getMinAddress());
|
||||
AddressRange otherRange = other.getRange();
|
||||
if (otherRange == null) {
|
||||
return;
|
||||
}
|
||||
long maxLengthMinus1 =
|
||||
otherRange.getAddressSpace().getMaxAddress().subtract(otherRange.getMinAddress());
|
||||
if (Long.compareUnsigned(lengthMinus1, maxLengthMinus1) > 0) {
|
||||
adjusted.setRange(range(adjRange.getMinAddress(), maxLengthMinus1));
|
||||
}
|
||||
}
|
||||
|
||||
protected void revalidateProgRange() {
|
||||
/**
|
||||
* Not sure there's much to do here wrt/ the program. Might be nice to warn (but not
|
||||
* prohibit) when range is not in memory. NB: A range spanning all blocks will likely
|
||||
* contain some dead space.
|
||||
*/
|
||||
revalidateByLength(fieldProgRange, fieldTraceRange);
|
||||
}
|
||||
|
||||
protected void revalidateTraceRange() {
|
||||
// Similarly nothing to really do wrt/ the trace.
|
||||
revalidateByLength(fieldTraceRange, fieldProgRange);
|
||||
}
|
||||
|
||||
protected static long lengthMin(long a, long b) {
|
||||
if (a == 0) {
|
||||
return b;
|
||||
}
|
||||
if (b == 0) {
|
||||
return a;
|
||||
}
|
||||
return MathUtilities.unsignedMin(a, b);
|
||||
}
|
||||
|
||||
protected static long revalidateLengthByRange(AddressRange range, long length) {
|
||||
long maxLength =
|
||||
range.getAddressSpace().getMaxAddress().subtract(range.getMinAddress()) + 1;
|
||||
return lengthMin(length, maxLength);
|
||||
}
|
||||
|
||||
protected void setFieldLength(long length) {
|
||||
if (length == 0) {
|
||||
fieldLength.setText(HEX_BIT64);
|
||||
}
|
||||
else {
|
||||
fieldLength.setText("0x" + Long.toHexString(length));
|
||||
}
|
||||
}
|
||||
|
||||
public long getLength() {
|
||||
return parseLength(fieldLength.getText(), 1);
|
||||
}
|
||||
|
||||
protected void revalidateLength() {
|
||||
long length;
|
||||
if (fieldLength.getText().trim().startsWith("-")) {
|
||||
length = 1;
|
||||
}
|
||||
else {
|
||||
length = getLength();
|
||||
}
|
||||
length = revalidateLengthByRange(fieldProgRange.getRange(), length);
|
||||
length = revalidateLengthByRange(fieldTraceRange.getRange(), length);
|
||||
setFieldLength(length);
|
||||
}
|
||||
|
||||
protected void revalidateSpan() {
|
||||
// Yeah, nothing to do
|
||||
}
|
||||
|
||||
protected static AddressRange range(Address min, long lengthMinus1) {
|
||||
return new AddressRangeImpl(min, min.addWrap(lengthMinus1));
|
||||
}
|
||||
|
||||
protected void adjustLengthToProgRange() {
|
||||
long length = fieldProgRange.getRange().getLength();
|
||||
setFieldLength(length);
|
||||
}
|
||||
|
||||
protected void adjustLengthToTraceRange() {
|
||||
long length = fieldTraceRange.getRange().getLength();
|
||||
setFieldLength(length);
|
||||
}
|
||||
|
||||
protected void adjustRangeToLength(GAddressRangeField field) {
|
||||
AddressRange range = field.getRange();
|
||||
if (range == null) {
|
||||
return;
|
||||
}
|
||||
Address min = range.getMinAddress();
|
||||
field.setRange(range(min, getLength() - 1));
|
||||
}
|
||||
|
||||
protected void adjustProgRangeToLength() {
|
||||
adjustRangeToLength(fieldProgRange);
|
||||
}
|
||||
|
||||
protected void adjustTraceRangeToLength() {
|
||||
adjustRangeToLength(fieldTraceRange);
|
||||
}
|
||||
|
||||
protected void progRangeChanged() {
|
||||
revalidateProgRange();
|
||||
adjustLengthToProgRange();
|
||||
adjustTraceRangeToLength();
|
||||
}
|
||||
|
||||
protected void traceRangeChanged() {
|
||||
revalidateTraceRange();
|
||||
adjustLengthToTraceRange();
|
||||
adjustProgRangeToLength();
|
||||
}
|
||||
|
||||
protected void lengthChanged() {
|
||||
revalidateLength();
|
||||
adjustProgRangeToLength();
|
||||
adjustTraceRangeToLength();
|
||||
}
|
||||
|
||||
protected void spanChanged() {
|
||||
revalidateSpan();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a value from 1 to 1<<64. Any value outside the range is "clipped" into the range.
|
||||
*
|
||||
* <p>
|
||||
* Note that a returned value of 0 indicates 2 to the power 64, which is just 1 too high to fit
|
||||
* into a 64-bit long.
|
||||
*
|
||||
* @param text the text to parse
|
||||
* @param defaultVal the default value should parsing fail altogether
|
||||
* @return the length, where 0 indicates {@code 1 << 64}.
|
||||
*/
|
||||
protected static long parseLength(String text, long defaultVal) {
|
||||
text = text.trim();
|
||||
String post;
|
||||
int radix;
|
||||
if (text.startsWith("-")) {
|
||||
return 0;
|
||||
}
|
||||
if (text.startsWith("0x")) {
|
||||
post = text.substring(2);
|
||||
radix = 16;
|
||||
}
|
||||
else {
|
||||
post = text;
|
||||
radix = 10;
|
||||
}
|
||||
BigInteger bi;
|
||||
try {
|
||||
bi = new BigInteger(post, radix);
|
||||
}
|
||||
catch (NumberFormatException e) {
|
||||
return defaultVal;
|
||||
}
|
||||
if (bi.equals(BigInteger.ZERO)) {
|
||||
return 1;
|
||||
}
|
||||
if (bi.bitLength() > 64) {
|
||||
return 0; // indicates 2**64, the max length
|
||||
}
|
||||
return bi.longValue(); // Do not use exact. It checks bitLength again, and considers sign.
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void applyCallback() {
|
||||
TraceLocation from = new DefaultTraceLocation(trace, null, fieldSpan.getLifespan(),
|
||||
fieldTraceRange.getRange().getMinAddress());
|
||||
ProgramLocation to = new ProgramLocation(program,
|
||||
fieldProgRange.getRange().getMinAddress());
|
||||
|
||||
try (UndoableTransaction tid =
|
||||
UndoableTransaction.start(trace, "Add Static Mapping", false)) {
|
||||
mappingService.addMapping(from, to, getLength(), true);
|
||||
tid.commit();
|
||||
}
|
||||
catch (TraceConflictedMappingException e) {
|
||||
throw new AssertionError(e); // I said truncateExisting
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the values of the fields
|
||||
*
|
||||
* @param program the program
|
||||
* @param trace the trace
|
||||
* @param progStart the starting static address
|
||||
* @param traceStart the starting dynamic address
|
||||
* @param length the length (0 indicates the entire 64-bit range)
|
||||
* @param lifespan the lifespan
|
||||
* @throws AddressOverflowException if the length is too large for either space
|
||||
*/
|
||||
public void setValues(Program program, Trace trace, Address progStart,
|
||||
Address traceStart, long length, Range<Long> lifespan) throws AddressOverflowException {
|
||||
// NB. This dialog will not validate these. The caller is responsible.
|
||||
this.program = program;
|
||||
this.trace = trace;
|
||||
this.fieldProgRange.setAddressFactory(program.getAddressFactory());
|
||||
this.fieldProgRange.setRange(range(progStart, length - 1));
|
||||
this.fieldTraceRange.setAddressFactory(trace.getBaseAddressFactory());
|
||||
this.fieldTraceRange.setRange(range(traceStart, length - 1));
|
||||
setFieldLength(length);
|
||||
this.fieldSpan.setLifespan(lifespan);
|
||||
}
|
||||
|
||||
protected void setEnabled(boolean enabled) {
|
||||
applyButton.setEnabled(enabled);
|
||||
fieldProgRange.setEnabled(enabled);
|
||||
fieldTraceRange.setEnabled(enabled);
|
||||
fieldLength.setEnabled(enabled);
|
||||
fieldSpan.setEnabled(enabled);
|
||||
}
|
||||
|
||||
public void setTrace(Trace trace) {
|
||||
this.trace = trace;
|
||||
if (trace != null) {
|
||||
labelTrace.setText(trace.getName());
|
||||
fieldTraceRange.setAddressFactory(trace.getBaseAddressFactory());
|
||||
}
|
||||
else {
|
||||
labelTrace.setText("[No Trace]");
|
||||
fieldTraceRange.setAddressFactory(null);
|
||||
}
|
||||
if (program != null && trace != null) {
|
||||
traceRangeChanged();
|
||||
setEnabled(true);
|
||||
}
|
||||
else {
|
||||
setEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
public void setProgram(Program program) {
|
||||
this.program = program;
|
||||
if (program != null) {
|
||||
labelProg.setText(program.getName());
|
||||
fieldProgRange.setAddressFactory(program.getAddressFactory());
|
||||
}
|
||||
else {
|
||||
labelProg.setText("[No Program]");
|
||||
fieldProgRange.setAddressFactory(null);
|
||||
}
|
||||
if (program != null && trace != null) {
|
||||
progRangeChanged();
|
||||
setEnabled(true);
|
||||
}
|
||||
else {
|
||||
setEnabled(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,35 +15,30 @@
|
||||
*/
|
||||
package ghidra.app.plugin.core.debug.gui.modules;
|
||||
|
||||
import ghidra.app.events.ProgramLocationPluginEvent;
|
||||
import ghidra.app.events.ProgramSelectionPluginEvent;
|
||||
import ghidra.app.events.ProgramActivatedPluginEvent;
|
||||
import ghidra.app.plugin.PluginCategoryNames;
|
||||
import ghidra.app.plugin.core.debug.AbstractDebuggerPlugin;
|
||||
import ghidra.app.plugin.core.debug.DebuggerPluginPackage;
|
||||
import ghidra.app.plugin.core.debug.event.*;
|
||||
import ghidra.app.plugin.core.debug.event.TraceActivatedPluginEvent;
|
||||
import ghidra.app.services.DebuggerStaticMappingService;
|
||||
import ghidra.app.services.DebuggerTraceManagerService;
|
||||
import ghidra.framework.plugintool.*;
|
||||
import ghidra.framework.plugintool.util.PluginStatus;
|
||||
|
||||
@PluginInfo( //
|
||||
shortDescription = "Debugger static mapping manager", //
|
||||
description = "GUI to manage static mappings", //
|
||||
category = PluginCategoryNames.DEBUGGER, //
|
||||
packageName = DebuggerPluginPackage.NAME, //
|
||||
status = PluginStatus.RELEASED, //
|
||||
eventsConsumed = {
|
||||
TraceActivatedPluginEvent.class, //
|
||||
ProgramLocationPluginEvent.class, //
|
||||
ProgramSelectionPluginEvent.class, //
|
||||
TraceLocationPluginEvent.class, //
|
||||
TraceSelectionPluginEvent.class //
|
||||
}, //
|
||||
servicesRequired = { //
|
||||
DebuggerStaticMappingService.class, //
|
||||
DebuggerTraceManagerService.class, //
|
||||
} //
|
||||
)
|
||||
@PluginInfo(
|
||||
shortDescription = "Debugger static mapping manager",
|
||||
description = "GUI to manage static mappings",
|
||||
category = PluginCategoryNames.DEBUGGER,
|
||||
packageName = DebuggerPluginPackage.NAME,
|
||||
status = PluginStatus.RELEASED,
|
||||
eventsConsumed = {
|
||||
TraceActivatedPluginEvent.class,
|
||||
ProgramActivatedPluginEvent.class,
|
||||
},
|
||||
servicesRequired = {
|
||||
DebuggerStaticMappingService.class,
|
||||
DebuggerTraceManagerService.class,
|
||||
})
|
||||
public class DebuggerStaticMappingPlugin extends AbstractDebuggerPlugin {
|
||||
protected DebuggerStaticMappingProvider provider;
|
||||
|
||||
@@ -70,17 +65,9 @@ public class DebuggerStaticMappingPlugin extends AbstractDebuggerPlugin {
|
||||
TraceActivatedPluginEvent ev = (TraceActivatedPluginEvent) event;
|
||||
provider.setTrace(ev.getActiveCoordinates().getTrace());
|
||||
}
|
||||
if (event instanceof ProgramLocationPluginEvent) {
|
||||
provider.contextChanged();
|
||||
}
|
||||
if (event instanceof ProgramSelectionPluginEvent) {
|
||||
provider.contextChanged();
|
||||
}
|
||||
if (event instanceof TraceLocationPluginEvent) {
|
||||
provider.contextChanged();
|
||||
}
|
||||
if (event instanceof TraceSelectionPluginEvent) {
|
||||
provider.contextChanged();
|
||||
if (event instanceof ProgramActivatedPluginEvent) {
|
||||
ProgramActivatedPluginEvent ev = (ProgramActivatedPluginEvent) event;
|
||||
provider.setProgram(ev.getActiveProgram());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ package ghidra.app.plugin.core.debug.gui.modules;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.math.BigInteger;
|
||||
import java.net.URL;
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
@@ -41,15 +42,18 @@ import ghidra.framework.model.DomainObject;
|
||||
import ghidra.framework.plugintool.AutoService;
|
||||
import ghidra.framework.plugintool.ComponentProviderAdapter;
|
||||
import ghidra.framework.plugintool.annotation.AutoServiceConsumed;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressRange;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
import ghidra.program.util.ProgramSelection;
|
||||
import ghidra.trace.model.*;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.Trace.TraceStaticMappingChangeType;
|
||||
import ghidra.trace.model.modules.*;
|
||||
import ghidra.trace.model.TraceDomainObjectListener;
|
||||
import ghidra.trace.model.modules.TraceStaticMapping;
|
||||
import ghidra.trace.model.modules.TraceStaticMappingManager;
|
||||
import ghidra.trace.model.program.TraceProgramView;
|
||||
import ghidra.util.MathUtilities;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.database.UndoableTransaction;
|
||||
import ghidra.util.table.GhidraTableFilterPanel;
|
||||
|
||||
@@ -60,7 +64,7 @@ public class DebuggerStaticMappingProvider extends ComponentProviderAdapter
|
||||
DYNAMIC_ADDRESS("Dynamic Address", Address.class, StaticMappingRow::getTraceAddress),
|
||||
STATIC_URL("Static Program", URL.class, StaticMappingRow::getStaticProgramURL),
|
||||
STATIC_ADDRESS("Static Address", String.class, StaticMappingRow::getStaticAddress),
|
||||
LENGTH("Length", Long.class, StaticMappingRow::getLength),
|
||||
LENGTH("Length", BigInteger.class, StaticMappingRow::getBigLength),
|
||||
SHIFT("Shift", Long.class, StaticMappingRow::getShift),
|
||||
LIFESPAN("Lifespan", Range.class, StaticMappingRow::getLifespan);
|
||||
|
||||
@@ -113,6 +117,8 @@ public class DebuggerStaticMappingProvider extends ComponentProviderAdapter
|
||||
|
||||
private final DebuggerStaticMappingPlugin plugin;
|
||||
|
||||
private final DebuggerAddMappingDialog addMappingDialog;
|
||||
|
||||
@AutoServiceConsumed
|
||||
private DebuggerStaticMappingService mappingService;
|
||||
// TODO: Use events to track selections? This can only work with the main listings.
|
||||
@@ -146,6 +152,7 @@ public class DebuggerStaticMappingProvider extends ComponentProviderAdapter
|
||||
super(plugin.getTool(), DebuggerResources.TITLE_PROVIDER_MAPPINGS, plugin.getName(), null);
|
||||
this.plugin = plugin;
|
||||
|
||||
this.addMappingDialog = new DebuggerAddMappingDialog();
|
||||
this.autoWiring = AutoService.wireServicesConsumed(plugin, this);
|
||||
|
||||
setIcon(DebuggerResources.ICON_PROVIDER_MAPPINGS);
|
||||
@@ -157,6 +164,11 @@ public class DebuggerStaticMappingProvider extends ComponentProviderAdapter
|
||||
createActions();
|
||||
}
|
||||
|
||||
@AutoServiceConsumed
|
||||
private void setMappingService(DebuggerStaticMappingService mappingService) {
|
||||
addMappingDialog.setMappingService(mappingService);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addLocalAction(DockingActionIf action) {
|
||||
super.addLocalAction(action);
|
||||
@@ -215,7 +227,7 @@ public class DebuggerStaticMappingProvider extends ComponentProviderAdapter
|
||||
statAddrCol.setCellRenderer(CustomToStringCellRenderer.MONO_OBJECT);
|
||||
TableColumn lengthCol = columnModel.getColumn(StaticMappingTableColumns.LENGTH.ordinal());
|
||||
// TODO: Get user column settings working. Still, should default to Hex
|
||||
lengthCol.setCellRenderer(CustomToStringCellRenderer.MONO_LONG_HEX);
|
||||
lengthCol.setCellRenderer(CustomToStringCellRenderer.MONO_BIG_HEX);
|
||||
TableColumn shiftCol = columnModel.getColumn(StaticMappingTableColumns.SHIFT.ordinal());
|
||||
shiftCol.setCellRenderer(CustomToStringCellRenderer.MONO_LONG_HEX);
|
||||
}
|
||||
@@ -223,7 +235,6 @@ public class DebuggerStaticMappingProvider extends ComponentProviderAdapter
|
||||
protected void createActions() {
|
||||
actionAdd = AddAction.builder(plugin)
|
||||
.description("Add Mapping from Listing Selections")
|
||||
.enabledWhen(this::haveMappableSelections)
|
||||
.onAction(this::activatedAdd)
|
||||
.buildAndInstallLocal(this);
|
||||
actionRemove = RemoveAction.builder(plugin)
|
||||
@@ -240,38 +251,10 @@ public class DebuggerStaticMappingProvider extends ComponentProviderAdapter
|
||||
contextChanged();
|
||||
}
|
||||
|
||||
private boolean haveMappableSelections(ActionContext ignore) {
|
||||
// TODO: Use events to track selections/locations
|
||||
if (codeViewerService == null || listingService == null) {
|
||||
return false;
|
||||
}
|
||||
ProgramLocation progLoc = codeViewerService.getCurrentLocation();
|
||||
ProgramLocation traceLoc = listingService.getCurrentLocation();
|
||||
|
||||
if (progLoc == null || traceLoc == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ProgramSelection progSel = codeViewerService.getCurrentSelection();
|
||||
ProgramSelection traceSel = listingService.getCurrentSelection();
|
||||
|
||||
if (progSel != null && progSel.getNumAddressRanges() > 1) {
|
||||
return false;
|
||||
}
|
||||
if (traceSel != null && traceSel.getNumAddressRanges() > 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
long progLen = progSel == null ? 0 : progSel.getNumAddresses();
|
||||
long traceLen = traceSel == null ? 0 : traceSel.getNumAddresses();
|
||||
if (progLen == 0 && traceLen == 0) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void activatedAdd(ActionContext ignore) {
|
||||
// TODO: Use events to track selections/locations
|
||||
tool.showDialog(addMappingDialog);
|
||||
// Try to populate the dialog based on selection, if applicable
|
||||
|
||||
if (codeViewerService == null || listingService == null) {
|
||||
return;
|
||||
}
|
||||
@@ -303,17 +286,13 @@ public class DebuggerStaticMappingProvider extends ComponentProviderAdapter
|
||||
Address progStart = progLen != 0 ? progSel.getMinAddress() : progLoc.getAddress();
|
||||
Address traceStart = traceLen != 0 ? traceSel.getMinAddress() : traceLoc.getAddress();
|
||||
TraceProgramView view = (TraceProgramView) traceLoc.getProgram();
|
||||
TraceLocation from =
|
||||
new DefaultTraceLocation(currentTrace, null, Range.atLeast(view.getSnap()), traceStart);
|
||||
ProgramLocation to = new ProgramLocation(progLoc.getProgram(), progStart);
|
||||
|
||||
try (UndoableTransaction tid =
|
||||
UndoableTransaction.start(currentTrace, "Add Static Mapping", false)) {
|
||||
mappingService.addMapping(from, to, length, true);
|
||||
tid.commit();
|
||||
try {
|
||||
addMappingDialog.setValues(progLoc.getProgram(), currentTrace, progStart, traceStart,
|
||||
length, Range.atLeast(view.getSnap()));
|
||||
}
|
||||
catch (TraceConflictedMappingException e) {
|
||||
throw new AssertionError(e); // I said truncateExisting
|
||||
catch (AddressOverflowException e) {
|
||||
Msg.showError(this, null, "Add Mapping", "Error populating dialog");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -387,5 +366,11 @@ public class DebuggerStaticMappingProvider extends ComponentProviderAdapter
|
||||
currentTrace = trace;
|
||||
addNewListeners();
|
||||
loadMappings();
|
||||
|
||||
addMappingDialog.setTrace(trace);
|
||||
}
|
||||
|
||||
public void setProgram(Program program) {
|
||||
addMappingDialog.setProgram(program);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package ghidra.app.plugin.core.debug.gui.modules;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.net.URL;
|
||||
|
||||
import com.google.common.collect.Range;
|
||||
@@ -24,6 +25,7 @@ import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.modules.TraceStaticMapping;
|
||||
|
||||
public class StaticMappingRow {
|
||||
private static final BigInteger BIT64 = BigInteger.ONE.shiftLeft(64);
|
||||
private final TraceStaticMapping mapping;
|
||||
|
||||
public StaticMappingRow(TraceStaticMapping mapping) {
|
||||
@@ -54,6 +56,19 @@ public class StaticMappingRow {
|
||||
return mapping.getLength();
|
||||
}
|
||||
|
||||
public BigInteger getBigLength() {
|
||||
long length = mapping.getLength();
|
||||
if (length == 0) {
|
||||
return BIT64;
|
||||
}
|
||||
else if (length < 0) {
|
||||
return BigInteger.valueOf(length).add(BIT64);
|
||||
}
|
||||
else {
|
||||
return BigInteger.valueOf(length);
|
||||
}
|
||||
}
|
||||
|
||||
public long getShift() {
|
||||
return mapping.getShift();
|
||||
}
|
||||
|
||||
@@ -940,31 +940,36 @@ public class DebuggerStaticMappingServicePlugin extends Plugin
|
||||
if (toURL == null) {
|
||||
noProject();
|
||||
}
|
||||
try {
|
||||
Address start = from.getAddress();
|
||||
Address end = start.addNoWrap(length - 1);
|
||||
// Also check end in the destination
|
||||
Address toAddress = to.getByteAddress();
|
||||
toAddress.addNoWrap(length - 1); // Anticipate possible AddressOverflow
|
||||
AddressRangeImpl range = new AddressRangeImpl(start, end);
|
||||
if (truncateExisting) {
|
||||
long truncEnd = DBTraceUtils.lowerEndpoint(from.getLifespan()) - 1;
|
||||
for (TraceStaticMapping existing : List
|
||||
.copyOf(manager.findAllOverlapping(range, from.getLifespan()))) {
|
||||
existing.delete();
|
||||
if (Long.compareUnsigned(existing.getStartSnap(), truncEnd) < 0) {
|
||||
manager.add(existing.getTraceAddressRange(),
|
||||
Range.closed(existing.getStartSnap(), truncEnd),
|
||||
existing.getStaticProgramURL(), existing.getStaticAddress());
|
||||
}
|
||||
Address fromAddress = from.getAddress();
|
||||
Address toAddress = to.getByteAddress();
|
||||
long maxFromLengthMinus1 =
|
||||
fromAddress.getAddressSpace().getMaxAddress().subtract(fromAddress);
|
||||
long maxToLengthMinus1 =
|
||||
toAddress.getAddressSpace().getMaxAddress().subtract(toAddress);
|
||||
if (Long.compareUnsigned(length - 1, maxFromLengthMinus1) > 0) {
|
||||
throw new IllegalArgumentException("Length would cause address overflow in trace");
|
||||
}
|
||||
if (Long.compareUnsigned(length - 1, maxToLengthMinus1) > 0) {
|
||||
throw new IllegalArgumentException("Length would cause address overflow in program");
|
||||
}
|
||||
Address end = fromAddress.addWrap(length - 1);
|
||||
// Also check end in the destination
|
||||
AddressRangeImpl range = new AddressRangeImpl(fromAddress, end);
|
||||
Range<Long> fromLifespan = from.getLifespan();
|
||||
if (truncateExisting) {
|
||||
long truncEnd = DBTraceUtils.lowerEndpoint(fromLifespan) - 1;
|
||||
for (TraceStaticMapping existing : List
|
||||
.copyOf(manager.findAllOverlapping(range, fromLifespan))) {
|
||||
existing.delete();
|
||||
if (fromLifespan.hasLowerBound() &&
|
||||
Long.compare(existing.getStartSnap(), truncEnd) <= 0) {
|
||||
manager.add(existing.getTraceAddressRange(),
|
||||
Range.closed(existing.getStartSnap(), truncEnd),
|
||||
existing.getStaticProgramURL(), existing.getStaticAddress());
|
||||
}
|
||||
}
|
||||
manager.add(range, from.getLifespan(), toURL,
|
||||
toAddress.toString(true));
|
||||
}
|
||||
catch (AddressOverflowException e) {
|
||||
throw new IllegalArgumentException("Length would cause address overflow", e);
|
||||
}
|
||||
manager.add(range, fromLifespan, toURL, toAddress.toString(true));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -535,7 +535,7 @@ public interface DebuggerStaticMappingService {
|
||||
*
|
||||
* @param from the source trace location, including lifespan
|
||||
* @param to the destination program location
|
||||
* @param length the length of the mapped region
|
||||
* @param length the length of the mapped region, where 0 indicates {@code 1 << 64}.
|
||||
* @param truncateExisting true to delete or truncate the lifespan of overlapping entries
|
||||
* @throws TraceConflictedMappingException if a conflicting mapping overlaps the source and
|
||||
* {@code truncateExisting} is false.
|
||||
|
||||
@@ -106,7 +106,7 @@ public class DebuggerStaticMappingProviderTest extends AbstractGhidraHeadedDebug
|
||||
|
||||
@Test
|
||||
public void testAddAction() throws Exception {
|
||||
assertFalse(mappingsProvider.actionAdd.isEnabled());
|
||||
assertTrue(mappingsProvider.actionAdd.isEnabled());
|
||||
|
||||
createProgramFromTrace(tb.trace);
|
||||
intoProject(tb.trace);
|
||||
@@ -135,16 +135,18 @@ public class DebuggerStaticMappingProviderTest extends AbstractGhidraHeadedDebug
|
||||
programManager.openProgram(program);
|
||||
waitForSwing();
|
||||
|
||||
assertFalse(mappingsProvider.actionAdd.isEnabled());
|
||||
|
||||
ProgramSelection traceSel =
|
||||
new ProgramSelection(tb.addr(0xdeadbeefL), tb.addr(0xdeadbeefL + 0x0f));
|
||||
listingPlugin.getProvider().setSelection(traceSel);
|
||||
codeViewerPlugin.goTo(new ProgramLocation(program, addr(program, 0xc0de1234L)), true);
|
||||
waitForSwing();
|
||||
|
||||
assertTrue(mappingsProvider.actionAdd.isEnabled());
|
||||
performAction(mappingsProvider.actionAdd, true);
|
||||
performAction(mappingsProvider.actionAdd, false);
|
||||
|
||||
DebuggerAddMappingDialog dialog = waitForDialogComponent(DebuggerAddMappingDialog.class);
|
||||
dialog.applyCallback();
|
||||
dialog.close();
|
||||
waitForDomainObject(tb.trace);
|
||||
|
||||
TraceStaticMapping entry = Unique.assertOne(manager.getAllEntries());
|
||||
assertEquals(Range.atLeast(0L), entry.getLifespan());
|
||||
|
||||
Reference in New Issue
Block a user