mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-03 01:39:21 +02:00
GP-5314: Destroy LiveMemoryHandler
This commit is contained in:
parent
7c74de60e6
commit
bef0660e6a
32 changed files with 667 additions and 783 deletions
|
@ -4,9 +4,9 @@
|
|||
* 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.
|
||||
|
@ -24,7 +24,6 @@ import ghidra.pcode.utils.Utils;
|
|||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.lang.Register;
|
||||
import ghidra.program.model.lang.RegisterValue;
|
||||
import ghidra.program.model.mem.LiveMemoryHandler;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.program.TraceProgramView;
|
||||
|
||||
|
@ -53,9 +52,6 @@ public interface DebuggerControlService {
|
|||
}
|
||||
}
|
||||
|
||||
interface StateEditingMemoryHandler extends StateEditor, LiveMemoryHandler {
|
||||
}
|
||||
|
||||
interface ControlModeChangeListener {
|
||||
void modeChanged(Trace trace, ControlMode mode);
|
||||
}
|
||||
|
@ -78,5 +74,5 @@ public interface DebuggerControlService {
|
|||
*/
|
||||
StateEditor createStateEditor(Trace trace);
|
||||
|
||||
StateEditingMemoryHandler createStateEditor(TraceProgramView view);
|
||||
StateEditor createStateEditor(TraceProgramView view);
|
||||
}
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
* 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.
|
||||
|
@ -17,6 +17,9 @@ package ghidra.app.plugin.core.debug.service.tracermi;
|
|||
|
||||
import ghidra.app.plugin.core.debug.service.tracermi.TraceRmiHandler.*;
|
||||
import ghidra.debug.api.tracermi.TraceRmiError;
|
||||
import ghidra.framework.data.DomainObjectAdapterDB;
|
||||
import ghidra.framework.model.TransactionInfo;
|
||||
import ghidra.framework.model.TransactionListener;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.lang.Register;
|
||||
import ghidra.rmi.trace.TraceRmi.*;
|
||||
|
@ -29,12 +32,51 @@ class OpenTrace implements ValueDecoder {
|
|||
final DoId doId;
|
||||
final Trace trace;
|
||||
final TraceRmiTarget target;
|
||||
final CurrentTxListener txListener;
|
||||
TraceSnapshot lastSnapshot;
|
||||
|
||||
class CurrentTxListener implements TransactionListener {
|
||||
boolean undoable;
|
||||
|
||||
public void markNotUndoable() {
|
||||
undoable = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transactionStarted(DomainObjectAdapterDB domainObj, TransactionInfo tx) {
|
||||
undoable = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transactionEnded(DomainObjectAdapterDB domainObj) {
|
||||
if (!undoable) {
|
||||
trace.clearUndo();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void undoStackChanged(DomainObjectAdapterDB domainObj) {
|
||||
// NOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void undoRedoOccurred(DomainObjectAdapterDB domainObj) {
|
||||
// NOP
|
||||
}
|
||||
}
|
||||
|
||||
OpenTrace(DoId doId, Trace trace, TraceRmiTarget target) {
|
||||
this.doId = doId;
|
||||
this.trace = trace;
|
||||
this.target = target;
|
||||
this.txListener = new CurrentTxListener();
|
||||
|
||||
trace.addTransactionListener(txListener);
|
||||
}
|
||||
|
||||
public void dispose(TraceRmiHandler consumer) {
|
||||
trace.removeTransactionListener(txListener);
|
||||
trace.release(consumer);
|
||||
}
|
||||
|
||||
public TraceSnapshot createSnapshot(Snap snap, String description) {
|
||||
|
@ -55,7 +97,7 @@ class OpenTrace implements ValueDecoder {
|
|||
TraceObject object =
|
||||
trace.getObjectManager().getObjectByCanonicalPath(TraceRmiHandler.toKeyPath(path));
|
||||
if (required && object == null) {
|
||||
throw new InvalidObjPathError();
|
||||
throw new InvalidObjPathError(path.getPath());
|
||||
}
|
||||
return object;
|
||||
}
|
||||
|
@ -78,7 +120,7 @@ class OpenTrace implements ValueDecoder {
|
|||
public AddressSpace getSpace(String name, boolean required) {
|
||||
AddressSpace space = trace.getBaseAddressFactory().getAddressSpace(name);
|
||||
if (required && space == null) {
|
||||
throw new NoSuchAddressSpaceError();
|
||||
throw new NoSuchAddressSpaceError(name);
|
||||
}
|
||||
return space;
|
||||
}
|
||||
|
|
|
@ -90,9 +90,15 @@ public class TraceRmiHandler extends AbstractTraceRmiConnection {
|
|||
}
|
||||
|
||||
protected static class InvalidObjPathError extends TraceRmiError {
|
||||
public InvalidObjPathError(String path) {
|
||||
super(path);
|
||||
}
|
||||
}
|
||||
|
||||
protected static class NoSuchAddressSpaceError extends TraceRmiError {
|
||||
public NoSuchAddressSpaceError(String name) {
|
||||
super(name);
|
||||
}
|
||||
}
|
||||
|
||||
protected static class InvalidSchemaError extends TraceRmiError {
|
||||
|
@ -292,8 +298,8 @@ public class TraceRmiHandler extends AbstractTraceRmiConnection {
|
|||
synchronized (openTxes) {
|
||||
while (!openTxes.isEmpty()) {
|
||||
Tid nextKey = openTxes.keySet().iterator().next();
|
||||
OpenTx open = openTxes.remove(nextKey);
|
||||
open.tx.close();
|
||||
OpenTx openTx = openTxes.remove(nextKey);
|
||||
openTx.tx.close();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -309,7 +315,7 @@ public class TraceRmiHandler extends AbstractTraceRmiConnection {
|
|||
// OK. Move on
|
||||
}
|
||||
}
|
||||
open.trace.release(this);
|
||||
open.dispose(this);
|
||||
}
|
||||
closed.complete(null);
|
||||
plugin.listeners.invoke().disconnected(this);
|
||||
|
@ -840,7 +846,7 @@ public class TraceRmiHandler extends AbstractTraceRmiConnection {
|
|||
protected ReplyCloseTrace handleCloseTrace(RequestCloseTrace req) {
|
||||
OpenTrace open = requireOpenTrace(req.getOid());
|
||||
openTraces.removeById(open.doId);
|
||||
open.trace.release(this);
|
||||
open.dispose(this);
|
||||
return ReplyCloseTrace.getDefaultInstance();
|
||||
}
|
||||
|
||||
|
@ -968,13 +974,27 @@ public class TraceRmiHandler extends AbstractTraceRmiConnection {
|
|||
Msg.error(this, "Back-end debugger aborted a transaction!");
|
||||
tx.tx.abortOnClose();
|
||||
}
|
||||
tx.tx.close();
|
||||
|
||||
OpenTrace open = requireOpenTrace(tx.txId.doId);
|
||||
if (!tx.undoable) {
|
||||
open.trace.clearUndo();
|
||||
/**
|
||||
* The listener is invoked via runLater, so we must do the same here, so that events are
|
||||
* processed in the order emitted.
|
||||
*/
|
||||
Swing.runLater(() -> open.txListener.markNotUndoable());
|
||||
}
|
||||
|
||||
tx.tx.close();
|
||||
|
||||
final boolean restoreEvents;
|
||||
synchronized (openTxes) {
|
||||
restoreEvents = openTxes.keySet()
|
||||
.stream()
|
||||
.noneMatch(id -> id.doId.domObjId == req.getOid().getId());
|
||||
}
|
||||
if (restoreEvents) {
|
||||
open.trace.setEventsEnabled(true);
|
||||
}
|
||||
// TODO: Check for other transactions on the same trace?
|
||||
open.trace.setEventsEnabled(true);
|
||||
return ReplyEndTx.getDefaultInstance();
|
||||
}
|
||||
|
||||
|
@ -1278,7 +1298,7 @@ public class TraceRmiHandler extends AbstractTraceRmiConnection {
|
|||
@Override
|
||||
public void forceCloseTrace(Trace trace) {
|
||||
OpenTrace open = openTraces.removeByTrace(trace);
|
||||
open.trace.release(this);
|
||||
open.dispose(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
* 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.
|
||||
|
@ -15,12 +15,16 @@
|
|||
*/
|
||||
package ghidra.app.plugin.core.debug.disassemble;
|
||||
|
||||
import java.util.concurrent.*;
|
||||
|
||||
import docking.ActionContext;
|
||||
import ghidra.app.plugin.assembler.Assembler;
|
||||
import ghidra.app.plugin.assembler.Assemblers;
|
||||
import ghidra.app.plugin.assembler.sleigh.sem.AssemblyPatternBlock;
|
||||
import ghidra.app.plugin.core.assembler.AssemblyDualTextField;
|
||||
import ghidra.app.plugin.core.assembler.PatchInstructionAction;
|
||||
import ghidra.app.services.DebuggerControlService;
|
||||
import ghidra.app.services.DebuggerControlService.StateEditor;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.lang.*;
|
||||
import ghidra.program.model.listing.CodeUnit;
|
||||
|
@ -118,12 +122,25 @@ public abstract class AbstractTracePatchInstructionAction extends PatchInstructi
|
|||
if (view == null) {
|
||||
return;
|
||||
}
|
||||
DebuggerControlService controlService = tool.getService(DebuggerControlService.class);
|
||||
if (controlService == null) {
|
||||
return;
|
||||
}
|
||||
StateEditor editor = controlService.createStateEditor(view);
|
||||
Address address = getAddress();
|
||||
// Get code unit and dependencies before invalidating it, just in case.
|
||||
|
||||
// Get code unit and dependencies before invalidating it.
|
||||
CodeUnit cu = getCodeUnit();
|
||||
RegisterValue contextValue = getContextValue(cu);
|
||||
TracePlatform platform = getPlatform(cu);
|
||||
view.getMemory().setBytes(address, data); // This invalidates cu
|
||||
|
||||
try {
|
||||
editor.setVariable(address, data).get(1, TimeUnit.SECONDS);
|
||||
}
|
||||
catch (InterruptedException | ExecutionException | TimeoutException e) {
|
||||
throw new MemoryAccessException("Couldn't patch", e);
|
||||
}
|
||||
|
||||
AddressSetView set = new AddressSet(address, address.add(data.length - 1));
|
||||
TraceDisassembleCommand dis = new TraceDisassembleCommand(platform, address, set);
|
||||
if (contextValue != null) {
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
* 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.
|
||||
|
@ -29,10 +29,7 @@ public class CurrentPlatformTracePatchInstructionAction
|
|||
|
||||
@Override
|
||||
protected boolean isApplicableToUnit(CodeUnit cu) {
|
||||
if (!super.isApplicableToUnit(cu)) {
|
||||
return false;
|
||||
}
|
||||
return cu instanceof TraceInstruction;
|
||||
return super.isApplicableToUnit(cu) && cu instanceof TraceInstruction;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
* 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.
|
||||
|
@ -170,6 +170,7 @@ public class DebuggerDisassemblerPlugin extends Plugin implements PopupActionPro
|
|||
|
||||
CurrentPlatformTraceDisassembleAction actionDisassemble;
|
||||
CurrentPlatformTracePatchInstructionAction actionPatchInstruction;
|
||||
TracePatchDataAction actionPatchData;
|
||||
|
||||
public DebuggerDisassemblerPlugin(PluginTool tool) {
|
||||
super(tool);
|
||||
|
@ -185,9 +186,11 @@ public class DebuggerDisassemblerPlugin extends Plugin implements PopupActionPro
|
|||
protected void createActions() {
|
||||
actionDisassemble = new CurrentPlatformTraceDisassembleAction(this);
|
||||
actionPatchInstruction = new CurrentPlatformTracePatchInstructionAction(this);
|
||||
actionPatchData = new TracePatchDataAction(this);
|
||||
|
||||
tool.addAction(actionDisassemble);
|
||||
tool.addAction(actionPatchInstruction);
|
||||
tool.addAction(actionPatchData);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
/* ###
|
||||
* 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.disassemble;
|
||||
|
||||
import java.util.concurrent.*;
|
||||
|
||||
import ghidra.app.plugin.core.assembler.PatchDataAction;
|
||||
import ghidra.app.services.DebuggerControlService;
|
||||
import ghidra.app.services.DebuggerControlService.StateEditor;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressRange;
|
||||
import ghidra.program.model.listing.CodeUnit;
|
||||
import ghidra.program.model.mem.MemoryAccessException;
|
||||
import ghidra.program.model.util.CodeUnitInsertionException;
|
||||
import ghidra.trace.model.listing.TraceData;
|
||||
import ghidra.trace.model.program.TraceProgramView;
|
||||
|
||||
public class TracePatchDataAction extends PatchDataAction {
|
||||
protected final DebuggerDisassemblerPlugin plugin;
|
||||
|
||||
public TracePatchDataAction(DebuggerDisassemblerPlugin plugin) {
|
||||
super(plugin);
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isApplicableToUnit(CodeUnit cu) {
|
||||
return super.isApplicableToUnit(cu) && cu instanceof TraceData;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void applyPatch(AddressRange rng, byte[] encoded)
|
||||
throws MemoryAccessException, CodeUnitInsertionException {
|
||||
if (!(getProgram() instanceof TraceProgramView view)) {
|
||||
return;
|
||||
}
|
||||
DebuggerControlService controlService = tool.getService(DebuggerControlService.class);
|
||||
if (controlService == null) {
|
||||
return;
|
||||
}
|
||||
StateEditor editor = controlService.createStateEditor(view);
|
||||
Address address = getAddress();
|
||||
|
||||
try {
|
||||
editor.setVariable(address, encoded).get(1, TimeUnit.SECONDS);
|
||||
}
|
||||
catch (InterruptedException | ExecutionException | TimeoutException e) {
|
||||
throw new MemoryAccessException("Couldn't patch", e);
|
||||
}
|
||||
// Let the trace do everything regarding existing units
|
||||
}
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
/* ###
|
||||
* 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;
|
||||
|
||||
import java.util.concurrent.*;
|
||||
|
||||
import ghidra.app.services.DebuggerConsoleService;
|
||||
import ghidra.app.services.DebuggerControlService;
|
||||
import ghidra.app.services.DebuggerControlService.StateEditor;
|
||||
import ghidra.debug.api.tracemgr.DebuggerCoordinates;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
import ghidra.trace.model.program.TraceProgramView;
|
||||
import ghidra.util.Msg;
|
||||
|
||||
public interface PasteIntoTargetMixin {
|
||||
default boolean doHasEnoughSpace(Program program, Address address, int byteCount) {
|
||||
/**
|
||||
* I don't care about code units. Just check that it's within the physical bounds and valid
|
||||
* memory (considering Force Full View). FFV is handled within the trace view's memory.
|
||||
*/
|
||||
final Address end;
|
||||
try {
|
||||
end = address.addNoWrap(byteCount - 1);
|
||||
}
|
||||
catch (AddressOverflowException e) {
|
||||
return false;
|
||||
}
|
||||
AddressSetView range = new AddressSet(address, end);
|
||||
if (!program.getMemory().intersect(range).equals(range)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
default boolean doPasteBytes(PluginTool tool, DebuggerControlService controlService,
|
||||
DebuggerConsoleService consoleService, DebuggerCoordinates current,
|
||||
ProgramLocation location, byte[] bytes) {
|
||||
if (!(location.getProgram() instanceof TraceProgramView view)) {
|
||||
tool.setStatusInfo("Not a trace?", true);
|
||||
return false;
|
||||
}
|
||||
StateEditor editor = controlService.createStateEditor(current);
|
||||
try {
|
||||
editor.setVariable(location.getByteAddress(), bytes)
|
||||
.get(1, TimeUnit.SECONDS);
|
||||
return true;
|
||||
}
|
||||
catch (InterruptedException | ExecutionException | TimeoutException e) {
|
||||
if (consoleService == null) {
|
||||
Msg.showError(this, null, "Paste Error",
|
||||
"Couldn't paste into " + location.getProgram(),
|
||||
e);
|
||||
}
|
||||
else {
|
||||
consoleService.log(DebuggerResources.ICON_LOG_ERROR,
|
||||
"Couldn't paste into " + view, e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -291,6 +291,24 @@ public class DebuggerListingProvider extends CodeViewerProvider {
|
|||
}
|
||||
|
||||
protected class ForListingClipboardProvider extends CodeBrowserClipboardProvider {
|
||||
protected class PasteIntoTargetCommand extends PasteByteStringCommand
|
||||
implements PasteIntoTargetMixin {
|
||||
protected PasteIntoTargetCommand(String string) {
|
||||
super(string);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean hasEnoughSpace(Program program, Address address, int byteCount) {
|
||||
return doHasEnoughSpace(program, address, byteCount);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean pasteBytes(Program program, byte[] bytes) {
|
||||
return doPasteBytes(tool, controlService, consoleService, current, currentLocation,
|
||||
bytes);
|
||||
}
|
||||
}
|
||||
|
||||
protected ForListingClipboardProvider() {
|
||||
super(DebuggerListingProvider.this.tool, DebuggerListingProvider.this);
|
||||
}
|
||||
|
@ -317,6 +335,11 @@ public class DebuggerListingProvider extends CodeViewerProvider {
|
|||
}
|
||||
return super.canPaste(availableFlavors);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean pasteByteString(String string) {
|
||||
return tool.execute(new PasteIntoTargetCommand(string), currentProgram);
|
||||
}
|
||||
}
|
||||
|
||||
private final DebuggerListingPlugin plugin;
|
||||
|
|
|
@ -21,8 +21,10 @@ import java.awt.event.MouseAdapter;
|
|||
import java.awt.event.MouseEvent;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.math.BigInteger;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
|
@ -40,9 +42,9 @@ import ghidra.app.plugin.core.debug.gui.DebuggerResources.FollowsCurrentThreadAc
|
|||
import ghidra.app.plugin.core.debug.gui.action.*;
|
||||
import ghidra.app.plugin.core.debug.gui.action.AutoReadMemorySpec.AutoReadMemorySpecConfigFieldCodec;
|
||||
import ghidra.app.plugin.core.format.ByteBlock;
|
||||
import ghidra.app.services.DebuggerControlService;
|
||||
import ghidra.app.plugin.core.format.ByteBlockAccessException;
|
||||
import ghidra.app.services.*;
|
||||
import ghidra.app.services.DebuggerControlService.ControlModeChangeListener;
|
||||
import ghidra.app.services.DebuggerTraceManagerService;
|
||||
import ghidra.debug.api.action.GoToInput;
|
||||
import ghidra.debug.api.action.LocationTrackingSpec;
|
||||
import ghidra.debug.api.tracemgr.DebuggerCoordinates;
|
||||
|
@ -165,6 +167,50 @@ public class DebuggerMemoryBytesProvider extends ProgramByteViewerComponentProvi
|
|||
}
|
||||
}
|
||||
|
||||
protected class ForBytesClipboardProvider extends ByteViewerClipboardProvider {
|
||||
protected class PasteIntoTargetCommand extends PasteByteStringCommand
|
||||
implements PasteIntoTargetMixin {
|
||||
protected PasteIntoTargetCommand(String string) {
|
||||
super(string);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean hasEnoughSpace(Program program, Address address, int byteCount) {
|
||||
return doHasEnoughSpace(program, address, byteCount);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean pasteBytes(Program program, byte[] bytes) {
|
||||
return doPasteBytes(tool, controlService, consoleService, current, currentLocation,
|
||||
bytes);
|
||||
}
|
||||
}
|
||||
|
||||
protected ForBytesClipboardProvider() {
|
||||
super(DebuggerMemoryBytesProvider.this, DebuggerMemoryBytesProvider.this.tool);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canPaste(DataFlavor[] availableFlavors) {
|
||||
if (controlService == null) {
|
||||
return false;
|
||||
}
|
||||
Trace trace = current.getTrace();
|
||||
if (trace == null) {
|
||||
return false;
|
||||
}
|
||||
if (!controlService.getCurrentMode(trace).canEdit(current)) {
|
||||
return false;
|
||||
}
|
||||
return super.canPaste(availableFlavors);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean pasteByteString(String string) {
|
||||
return tool.execute(new PasteIntoTargetCommand(string), currentProgram);
|
||||
}
|
||||
}
|
||||
|
||||
private final AutoReadMemorySpec defaultReadMemorySpec =
|
||||
AutoReadMemorySpec.fromConfigName(VisibleROOnceAutoReadMemorySpec.CONFIG_NAME);
|
||||
|
||||
|
@ -172,6 +218,8 @@ public class DebuggerMemoryBytesProvider extends ProgramByteViewerComponentProvi
|
|||
|
||||
@AutoServiceConsumed
|
||||
private DebuggerTraceManagerService traceManager;
|
||||
@AutoServiceConsumed
|
||||
private DebuggerConsoleService consoleService;
|
||||
//@AutoServiceConsumed via method
|
||||
private DebuggerControlService controlService;
|
||||
@SuppressWarnings("unused")
|
||||
|
@ -272,23 +320,83 @@ public class DebuggerMemoryBytesProvider extends ProgramByteViewerComponentProvi
|
|||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Override where edits are allowed and direct sets through the control service.
|
||||
*/
|
||||
class TargetByteBlock extends MemoryByteBlock {
|
||||
protected TargetByteBlock(Program program, Memory memory, MemoryBlock block) {
|
||||
super(program, memory, block);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* <p>
|
||||
* Overridden to ignore existing instructions. Let them be clobbered!
|
||||
*/
|
||||
@Override
|
||||
protected boolean editAllowed(Address addr, long length) {
|
||||
return controlService != null;
|
||||
}
|
||||
|
||||
protected ByteBuffer alloc(int size) {
|
||||
return ByteBuffer.allocate(size)
|
||||
.order(isBigEndian()
|
||||
? ByteOrder.BIG_ENDIAN
|
||||
: ByteOrder.LITTLE_ENDIAN);
|
||||
}
|
||||
|
||||
protected void doSet(Address address, ByteBuffer buffer) throws ByteBlockAccessException {
|
||||
checkEditsAllowed(address, buffer.capacity());
|
||||
try {
|
||||
controlService.createStateEditor(current)
|
||||
.setVariable(address, buffer.array())
|
||||
.get(1, TimeUnit.SECONDS);
|
||||
}
|
||||
catch (InterruptedException | ExecutionException | TimeoutException e) {
|
||||
throw new ByteBlockAccessException("Could not set target memory", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setByte(BigInteger index, byte value) throws ByteBlockAccessException {
|
||||
doSet(getAddress(index), alloc(Byte.BYTES).put(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setShort(BigInteger index, short value) throws ByteBlockAccessException {
|
||||
doSet(getAddress(index), alloc(Short.BYTES).putShort(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setInt(BigInteger index, int value) throws ByteBlockAccessException {
|
||||
doSet(getAddress(index), alloc(Integer.BYTES).putInt(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLong(BigInteger index, long value) throws ByteBlockAccessException {
|
||||
doSet(getAddress(index), alloc(Long.BYTES).putLong(value));
|
||||
}
|
||||
}
|
||||
|
||||
class TargetByteBlockSet extends ProgramByteBlockSet {
|
||||
protected TargetByteBlockSet(ByteBlockChangeManager changeManager) {
|
||||
super(DebuggerMemoryBytesProvider.this, DebuggerMemoryBytesProvider.this.program,
|
||||
changeManager);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MemoryByteBlock newMemoryByteBlock(Memory memory, MemoryBlock memBlock) {
|
||||
return new TargetByteBlock(program, memory, memBlock);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ProgramByteBlockSet newByteBlockSet(ByteBlockChangeManager changeManager) {
|
||||
if (program == null) {
|
||||
return null;
|
||||
}
|
||||
// A bit of work to get it to ignore existing instructions. Let them be clobbered!
|
||||
return new ProgramByteBlockSet(this, program, changeManager) {
|
||||
@Override
|
||||
protected MemoryByteBlock newMemoryByteBlock(Memory memory, MemoryBlock memBlock) {
|
||||
return new MemoryByteBlock(program, memory, memBlock) {
|
||||
@Override
|
||||
protected boolean editAllowed(Address addr, long length) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
return new TargetByteBlockSet(changeManager);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -373,22 +481,7 @@ public class DebuggerMemoryBytesProvider extends ProgramByteViewerComponentProvi
|
|||
|
||||
@Override
|
||||
protected ByteViewerClipboardProvider newClipboardProvider() {
|
||||
return new ByteViewerClipboardProvider(this, tool) {
|
||||
@Override
|
||||
public boolean canPaste(DataFlavor[] availableFlavors) {
|
||||
if (controlService == null) {
|
||||
return false;
|
||||
}
|
||||
Trace trace = current.getTrace();
|
||||
if (trace == null) {
|
||||
return false;
|
||||
}
|
||||
if (!controlService.getCurrentMode(trace).canEdit(current)) {
|
||||
return false;
|
||||
}
|
||||
return super.canPaste(availableFlavors);
|
||||
}
|
||||
};
|
||||
return new ForBytesClipboardProvider();
|
||||
}
|
||||
|
||||
@AutoServiceConsumed
|
||||
|
|
|
@ -15,14 +15,13 @@
|
|||
*/
|
||||
package ghidra.app.plugin.core.debug.service.control;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
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.*;
|
||||
import ghidra.app.services.DebuggerTraceManagerService.ActivationCause;
|
||||
import ghidra.debug.api.control.ControlMode;
|
||||
|
@ -31,11 +30,8 @@ import ghidra.framework.plugintool.*;
|
|||
import ghidra.framework.plugintool.annotation.AutoServiceConsumed;
|
||||
import ghidra.framework.plugintool.util.PluginStatus;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.mem.*;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.Trace.TraceProgramViewListener;
|
||||
import ghidra.trace.model.program.TraceProgramView;
|
||||
import ghidra.trace.model.program.TraceProgramViewMemory;
|
||||
import ghidra.util.datastruct.ListenerSet;
|
||||
|
||||
@PluginInfo(
|
||||
|
@ -45,9 +41,7 @@ import ghidra.util.datastruct.ListenerSet;
|
|||
packageName = DebuggerPluginPackage.NAME,
|
||||
status = PluginStatus.RELEASED,
|
||||
eventsConsumed = {
|
||||
TraceOpenedPluginEvent.class,
|
||||
TraceActivatedPluginEvent.class,
|
||||
TraceClosedPluginEvent.class,
|
||||
},
|
||||
servicesRequired = {
|
||||
DebuggerTraceManagerService.class,
|
||||
|
@ -115,8 +109,7 @@ public class DebuggerControlServicePlugin extends AbstractDebuggerPlugin
|
|||
}
|
||||
}
|
||||
|
||||
public class FollowsViewStateEditor extends AbstractStateEditor
|
||||
implements StateEditingMemoryHandler {
|
||||
public class FollowsViewStateEditor extends AbstractStateEditor {
|
||||
private final TraceProgramView view;
|
||||
|
||||
public FollowsViewStateEditor(TraceProgramView view) {
|
||||
|
@ -132,81 +125,11 @@ public class DebuggerControlServicePlugin extends AbstractDebuggerPlugin
|
|||
public DebuggerCoordinates getCoordinates() {
|
||||
return traceManager.resolveView(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearCache() {
|
||||
// Nothing to do
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte getByte(Address addr) throws MemoryAccessException {
|
||||
ByteBuffer buf = ByteBuffer.allocate(1);
|
||||
view.getTrace().getMemoryManager().getViewBytes(view.getSnap(), addr, buf);
|
||||
return buf.get(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBytes(Address address, byte[] buffer, int startIndex, int size)
|
||||
throws MemoryAccessException {
|
||||
return view.getTrace()
|
||||
.getMemoryManager()
|
||||
.getViewBytes(view.getSnap(), address,
|
||||
ByteBuffer.wrap(buffer, startIndex, size));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putByte(Address address, byte value) throws MemoryAccessException {
|
||||
try {
|
||||
setVariable(address, new byte[] { value }).get(1, TimeUnit.SECONDS);
|
||||
}
|
||||
catch (ExecutionException e) {
|
||||
throw new MemoryAccessException("Failed to write " + address + ": " + e.getCause());
|
||||
}
|
||||
catch (TimeoutException | InterruptedException e) {
|
||||
throw new MemoryAccessException("Failed to write " + address + ": " + e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int putBytes(Address address, byte[] source, int startIndex, int size)
|
||||
throws MemoryAccessException {
|
||||
try {
|
||||
setVariable(address, Arrays.copyOfRange(source, startIndex, startIndex + size))
|
||||
.get(1, TimeUnit.SECONDS);
|
||||
}
|
||||
catch (ExecutionException e) {
|
||||
throw new MemoryAccessException("Failed to write " + address + ": " + e.getCause());
|
||||
}
|
||||
catch (TimeoutException | InterruptedException e) {
|
||||
throw new MemoryAccessException("Failed to write " + address + ": " + e);
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addLiveMemoryListener(LiveMemoryListener listener) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeLiveMemoryListener(LiveMemoryListener listener) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
protected class ListenerForEditorInstallation implements TraceProgramViewListener {
|
||||
@Override
|
||||
public void viewCreated(TraceProgramView view) {
|
||||
installMemoryEditor(view);
|
||||
}
|
||||
}
|
||||
|
||||
//@AutoServiceConsumed // via method
|
||||
@AutoServiceConsumed
|
||||
private DebuggerTraceManagerService traceManager;
|
||||
|
||||
protected final ListenerForEditorInstallation listenerForEditorInstallation =
|
||||
new ListenerForEditorInstallation();
|
||||
|
||||
private final Map<Trace, ControlMode> currentModes = new HashMap<>();
|
||||
|
||||
private final ListenerSet<ControlModeChangeListener> listeners =
|
||||
|
@ -219,7 +142,6 @@ public class DebuggerControlServicePlugin extends AbstractDebuggerPlugin
|
|||
@Override
|
||||
protected void dispose() {
|
||||
super.dispose();
|
||||
uninstallAllMemoryEditors();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -265,7 +187,7 @@ public class DebuggerControlServicePlugin extends AbstractDebuggerPlugin
|
|||
}
|
||||
|
||||
@Override
|
||||
public StateEditingMemoryHandler createStateEditor(TraceProgramView view) {
|
||||
public StateEditor createStateEditor(TraceProgramView view) {
|
||||
return new FollowsViewStateEditor(view);
|
||||
}
|
||||
|
||||
|
@ -292,78 +214,11 @@ public class DebuggerControlServicePlugin extends AbstractDebuggerPlugin
|
|||
}
|
||||
}
|
||||
|
||||
protected void installMemoryEditor(TraceProgramView view) {
|
||||
TraceProgramViewMemory memory = view.getMemory();
|
||||
if (memory.getLiveMemoryHandler() != null) {
|
||||
return;
|
||||
}
|
||||
memory.setLiveMemoryHandler(createStateEditor(view));
|
||||
}
|
||||
|
||||
protected void uninstallMemoryEditor(TraceProgramView view) {
|
||||
TraceProgramViewMemory memory = view.getMemory();
|
||||
LiveMemoryHandler handler = memory.getLiveMemoryHandler();
|
||||
if (!(handler instanceof StateEditingMemoryHandler)) {
|
||||
return;
|
||||
}
|
||||
StateEditingMemoryHandler editor = (StateEditingMemoryHandler) handler;
|
||||
if (editor.getService() != this) {
|
||||
return;
|
||||
}
|
||||
memory.setLiveMemoryHandler(null);
|
||||
}
|
||||
|
||||
protected void installAllMemoryEditors(Trace trace) {
|
||||
trace.addProgramViewListener(listenerForEditorInstallation);
|
||||
for (TraceProgramView view : trace.getAllProgramViews()) {
|
||||
installMemoryEditor(view);
|
||||
}
|
||||
}
|
||||
|
||||
protected void installAllMemoryEditors() {
|
||||
if (traceManager == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (Trace trace : traceManager.getOpenTraces()) {
|
||||
installAllMemoryEditors(trace);
|
||||
}
|
||||
}
|
||||
|
||||
protected void uninstallAllMemoryEditors(Trace trace) {
|
||||
trace.removeProgramViewListener(listenerForEditorInstallation);
|
||||
for (TraceProgramView view : trace.getAllProgramViews()) {
|
||||
uninstallMemoryEditor(view);
|
||||
}
|
||||
}
|
||||
|
||||
protected void uninstallAllMemoryEditors() {
|
||||
if (traceManager == null) {
|
||||
return;
|
||||
}
|
||||
for (Trace trace : traceManager.getOpenTraces()) {
|
||||
uninstallAllMemoryEditors(trace);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processEvent(PluginEvent event) {
|
||||
super.processEvent(event);
|
||||
if (event instanceof TraceOpenedPluginEvent ev) {
|
||||
installAllMemoryEditors(ev.getTrace());
|
||||
}
|
||||
else if (event instanceof TraceActivatedPluginEvent ev) {
|
||||
if (event instanceof TraceActivatedPluginEvent ev) {
|
||||
coordinatesActivated(ev.getActiveCoordinates(), ev.getCause());
|
||||
}
|
||||
else if (event instanceof TraceClosedPluginEvent ev) {
|
||||
uninstallAllMemoryEditors(ev.getTrace());
|
||||
}
|
||||
}
|
||||
|
||||
@AutoServiceConsumed
|
||||
private void setTraceManager(DebuggerTraceManagerService traceManager) {
|
||||
uninstallAllMemoryEditors();
|
||||
this.traceManager = traceManager;
|
||||
installAllMemoryEditors();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
* 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.
|
||||
|
@ -22,6 +22,7 @@ import ghidra.program.model.listing.Program;
|
|||
public class DebuggerDisassemblerPluginTestHelper extends AssemblerPluginTestHelper {
|
||||
public DebuggerDisassemblerPluginTestHelper(DebuggerDisassemblerPlugin disassemblerPlugin,
|
||||
CodeViewerProvider provider, Program program) {
|
||||
super(disassemblerPlugin.actionPatchInstruction, null, provider, program);
|
||||
super(disassemblerPlugin.actionPatchInstruction, disassemblerPlugin.actionPatchData,
|
||||
provider, program);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
* 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.
|
||||
|
@ -225,16 +225,6 @@ public class DBTraceGuestPlatformMappedMemory implements Memory {
|
|||
return guest.getLanguage().isBigEndian();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLiveMemoryHandler(LiveMemoryHandler handler) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public LiveMemoryHandler getLiveMemoryHandler() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemoryBlock createInitializedBlock(String name, Address start, InputStream is,
|
||||
long length, TaskMonitor monitor, boolean overlay)
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
* 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.
|
||||
|
@ -47,8 +47,6 @@ public abstract class AbstractDBTraceProgramViewMemory
|
|||
protected boolean forceFullView = false;
|
||||
protected long snap;
|
||||
|
||||
protected LiveMemoryHandler memoryWriteRedirect;
|
||||
|
||||
private static final int CACHE_PAGE_COUNT = 3;
|
||||
protected final ByteCache cache = new ByteCache(CACHE_PAGE_COUNT) {
|
||||
@Override
|
||||
|
@ -162,16 +160,6 @@ public abstract class AbstractDBTraceProgramViewMemory
|
|||
return program.getLanguage().isBigEndian();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLiveMemoryHandler(LiveMemoryHandler handler) {
|
||||
this.memoryWriteRedirect = handler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LiveMemoryHandler getLiveMemoryHandler() {
|
||||
return memoryWriteRedirect;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemoryBlock createInitializedBlock(String name, Address start, InputStream is,
|
||||
long length, TaskMonitor monitor, boolean overlay)
|
||||
|
@ -339,10 +327,6 @@ public abstract class AbstractDBTraceProgramViewMemory
|
|||
|
||||
@Override
|
||||
public void setByte(Address addr, byte value) throws MemoryAccessException {
|
||||
if (memoryWriteRedirect != null) {
|
||||
memoryWriteRedirect.putByte(addr, value);
|
||||
return;
|
||||
}
|
||||
DBTraceMemorySpace space = memoryManager.getMemorySpace(addr.getAddressSpace(), true);
|
||||
if (space.putBytes(snap, addr, ByteBuffer.wrap(new byte[] { value })) != 1) {
|
||||
throw new MemoryAccessException();
|
||||
|
@ -352,10 +336,6 @@ public abstract class AbstractDBTraceProgramViewMemory
|
|||
@Override
|
||||
public void setBytes(Address addr, byte[] source, int sIndex, int size)
|
||||
throws MemoryAccessException {
|
||||
if (memoryWriteRedirect != null) {
|
||||
memoryWriteRedirect.putBytes(addr, source, sIndex, size);
|
||||
return;
|
||||
}
|
||||
DBTraceMemorySpace space = memoryManager.getMemorySpace(addr.getAddressSpace(), true);
|
||||
if (space.putBytes(snap, addr, ByteBuffer.wrap(source, sIndex, size)) != size) {
|
||||
throw new MemoryAccessException();
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
*/
|
||||
package ghidra.trace.model.program;
|
||||
|
||||
import ghidra.program.model.mem.LiveMemoryHandler;
|
||||
import ghidra.program.model.mem.Memory;
|
||||
|
||||
public interface TraceProgramViewMemory extends Memory, SnapSpecificTraceView {
|
||||
|
@ -25,13 +24,4 @@ public interface TraceProgramViewMemory extends Memory, SnapSpecificTraceView {
|
|||
void setForceFullView(boolean forceFullView);
|
||||
|
||||
boolean isForceFullView();
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* <p>
|
||||
* For trace views, this only redirects memory writes.
|
||||
*/
|
||||
@Override
|
||||
void setLiveMemoryHandler(LiveMemoryHandler handler);
|
||||
}
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
* 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.
|
||||
|
@ -39,13 +39,18 @@ import ghidra.program.model.mem.MemBuffer;
|
|||
* The API for instruction assembly is available from {@link Assemblers}. For data assembly, the API
|
||||
* is in {@link DataType#encodeRepresentation(String, MemBuffer, Settings, int)}.
|
||||
*/
|
||||
@PluginInfo(status = PluginStatus.RELEASED, packageName = CorePluginPackage.NAME, category = "Patching", shortDescription = "Assembler", description = "This plugin provides functionality for assembly patching. " +
|
||||
"The assembler supports most processor languages also supported by the " +
|
||||
"disassembler. Depending on the particular processor, your mileage may vary. " +
|
||||
"We are in the process of testing and improving support for all our processors. " +
|
||||
"You can access the assembler by pressing Ctrl-Shift-G, and then modifying the " +
|
||||
"instruction in place. As you type, a content assist will guide you and provide " +
|
||||
"assembled bytes when you have a complete instruction.")
|
||||
@PluginInfo(
|
||||
status = PluginStatus.RELEASED,
|
||||
packageName = CorePluginPackage.NAME,
|
||||
category = "Patching",
|
||||
shortDescription = "Assembler",
|
||||
description = "This plugin provides functionality for assembly patching. " +
|
||||
"The assembler supports most processor languages also supported by the " +
|
||||
"disassembler. Depending on the particular processor, your mileage may vary. " +
|
||||
"We are in the process of testing and improving support for all our processors. " +
|
||||
"You can access the assembler by pressing Ctrl-Shift-G, and then modifying the " +
|
||||
"instruction in place. As you type, a content assist will guide you and provide " +
|
||||
"assembled bytes when you have a complete instruction.")
|
||||
public class AssemblerPlugin extends ProgramPlugin {
|
||||
public static final String ASSEMBLER_NAME = "Assembler";
|
||||
|
||||
|
@ -58,35 +63,39 @@ public class AssemblerPlugin extends ProgramPlugin {
|
|||
}
|
||||
|
||||
private void createActions() {
|
||||
// Debugger provides its own "Patch Instruction" action
|
||||
// Debugger provides its own "Patch" actions
|
||||
patchInstructionAction = new PatchInstructionAction(this) {
|
||||
@Override
|
||||
public boolean isEnabledForContext(ActionContext context) {
|
||||
if (!super.isEnabledForContext(context)) {
|
||||
return false;
|
||||
}
|
||||
if (!(context instanceof ListingActionContext)) {
|
||||
return false;
|
||||
}
|
||||
ListingActionContext lac = (ListingActionContext) context;
|
||||
return !lac.getNavigatable().isDynamic();
|
||||
return super.isEnabledForContext(context) &&
|
||||
context instanceof ListingActionContext lac &&
|
||||
!lac.getNavigatable().isDynamic();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAddToPopup(ActionContext context) {
|
||||
if (!super.isAddToPopup(context)) {
|
||||
return false;
|
||||
}
|
||||
if (!(context instanceof ListingActionContext)) {
|
||||
return false;
|
||||
}
|
||||
ListingActionContext lac = (ListingActionContext) context;
|
||||
return !lac.getNavigatable().isDynamic();
|
||||
return super.isAddToPopup(context) &&
|
||||
context instanceof ListingActionContext lac &&
|
||||
!lac.getNavigatable().isDynamic();
|
||||
}
|
||||
};
|
||||
tool.addAction(patchInstructionAction);
|
||||
|
||||
patchDataAction = new PatchDataAction(this);
|
||||
patchDataAction = new PatchDataAction(this) {
|
||||
@Override
|
||||
public boolean isEnabledForContext(ActionContext context) {
|
||||
return super.isEnabledForContext(context) &&
|
||||
context instanceof ListingActionContext lac &&
|
||||
!lac.getNavigatable().isDynamic();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAddToPopup(ActionContext context) {
|
||||
return super.isAddToPopup(context) &&
|
||||
context instanceof ListingActionContext lac &&
|
||||
!lac.getNavigatable().isDynamic();
|
||||
}
|
||||
};
|
||||
tool.addAction(patchDataAction);
|
||||
}
|
||||
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
* 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.
|
||||
|
@ -73,14 +73,7 @@ public class PatchDataAction extends AbstractPatchAction {
|
|||
|
||||
@Override
|
||||
protected boolean isApplicableToUnit(CodeUnit cu) {
|
||||
if (!(cu instanceof Data)) {
|
||||
return false;
|
||||
}
|
||||
Data data = (Data) cu;
|
||||
if (!data.getBaseDataType().isEncodable()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
return cu instanceof Data data && data.getBaseDataType().isEncodable();
|
||||
}
|
||||
|
||||
protected Data getData() {
|
||||
|
@ -113,6 +106,23 @@ public class PatchDataAction extends AbstractPatchAction {
|
|||
input.setCaretPosition(repr.length());
|
||||
}
|
||||
|
||||
protected void applyPatch(AddressRange rng, byte[] encoded)
|
||||
throws MemoryAccessException, CodeUnitInsertionException {
|
||||
Program program = getProgram();
|
||||
Address address = getAddress();
|
||||
Data data = getData();
|
||||
DataType dt = data.getBaseDataType();
|
||||
|
||||
int oldLength = data.getLength();
|
||||
if (encoded.length != oldLength) {
|
||||
program.getListing().clearCodeUnits(address, rng.getMaxAddress(), false);
|
||||
}
|
||||
program.getMemory().setBytes(address, encoded);
|
||||
if (encoded.length != oldLength) {
|
||||
program.getListing().createData(address, dt, encoded.length);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept() {
|
||||
Program program = getProgram();
|
||||
|
@ -135,14 +145,7 @@ public class PatchDataAction extends AbstractPatchAction {
|
|||
}
|
||||
try (Transaction tx =
|
||||
program.openTransaction("Patch Data @" + address + ": " + input.getText())) {
|
||||
int oldLength = data.getLength();
|
||||
if (encoded.length != oldLength) {
|
||||
program.getListing().clearCodeUnits(address, rng.getMaxAddress(), false);
|
||||
}
|
||||
program.getMemory().setBytes(address, encoded);
|
||||
if (encoded.length != oldLength) {
|
||||
program.getListing().createData(address, dt, encoded.length);
|
||||
}
|
||||
applyPatch(rng, encoded);
|
||||
hide();
|
||||
}
|
||||
catch (MemoryAccessException e) {
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
* 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.
|
||||
|
@ -25,7 +25,6 @@ import docking.dnd.GenericDataFlavor;
|
|||
import docking.dnd.StringTransferable;
|
||||
import docking.widgets.OptionDialog;
|
||||
import ghidra.framework.cmd.Command;
|
||||
import ghidra.framework.model.DomainObject;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.listing.*;
|
||||
|
@ -38,8 +37,7 @@ import ghidra.util.*;
|
|||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
/**
|
||||
* Base class that can copy bytes into a Transferable object, and paste
|
||||
* bytes into a program.
|
||||
* Base class that can copy bytes into a Transferable object, and paste bytes into a program.
|
||||
*
|
||||
*/
|
||||
public abstract class ByteCopier {
|
||||
|
@ -381,125 +379,12 @@ public abstract class ByteCopier {
|
|||
}
|
||||
|
||||
protected boolean pasteByteString(final String string) {
|
||||
Command cmd = new Command() {
|
||||
|
||||
private String status = "Pasting";
|
||||
|
||||
@Override
|
||||
public boolean applyTo(DomainObject domainObject) {
|
||||
if (!(domainObject instanceof Program)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String validString = string;
|
||||
if (!isOnlyAsciiBytes(string)) {
|
||||
tool.setStatusInfo("Pasted string contained non-text ascii bytes. " +
|
||||
"Only the ascii will be used.", true);
|
||||
validString = keepOnlyAsciiBytes(string);
|
||||
}
|
||||
|
||||
byte[] bytes = getBytes(validString);
|
||||
if (bytes == null) {
|
||||
status = "Improper data format. Expected sequence of hex bytes";
|
||||
tool.beep();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Ensure that we are not writing over instructions
|
||||
Program program = (Program) domainObject;
|
||||
Address address = currentLocation.getAddress();
|
||||
if (!hasEnoughSpace(program, address, bytes.length)) {
|
||||
status =
|
||||
"Not enough space to paste all bytes. Encountered data or instructions.";
|
||||
tool.beep();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Ask the user before pasting a string into the program. Since having a string in
|
||||
// the clipboard is so common, this is to prevent an accidental paste.
|
||||
if (!confirmPaste(validString)) {
|
||||
return true; // the user cancelled; the command is successful
|
||||
}
|
||||
|
||||
boolean pastedAllBytes = pasteBytes(program, bytes);
|
||||
if (!pastedAllBytes) {
|
||||
tool.setStatusInfo("Not all bytes were pasted due to memory access issues",
|
||||
true);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean pasteBytes(Program program, byte[] bytes) {
|
||||
|
||||
// note: loop one byte at a time here, since Memory will validate all addresses
|
||||
// before pasting any bytes
|
||||
boolean foundError = false;
|
||||
Address address = currentLocation.getAddress();
|
||||
Memory memory = program.getMemory();
|
||||
for (byte element : bytes) {
|
||||
try {
|
||||
memory.setByte(address, element);
|
||||
}
|
||||
catch (MemoryAccessException e) {
|
||||
// Keep trying the remaining bytes. Should we just stop in this case?
|
||||
foundError = true;
|
||||
}
|
||||
address = address.next();
|
||||
}
|
||||
return !foundError;
|
||||
}
|
||||
|
||||
private boolean confirmPaste(String validString) {
|
||||
|
||||
// create a truncated version of the string to show in the dialog
|
||||
String partialText = validString.length() < 40 ? validString
|
||||
: validString.substring(0, 40) + " ...";
|
||||
int result = OptionDialog.showYesNoDialog(null, "Paste String Into Program?",
|
||||
"Are you sure you want to paste the string \"" + partialText +
|
||||
"\"\n into the program's memory?");
|
||||
|
||||
return result != OptionDialog.NO_OPTION;
|
||||
}
|
||||
|
||||
private boolean hasEnoughSpace(Program program, Address address, int byteCount) {
|
||||
Listing listing = program.getListing();
|
||||
for (int i = 0; i < byteCount;) {
|
||||
if (address == null) {
|
||||
status = "Not enough addresses to paste bytes";
|
||||
tool.beep();
|
||||
return false;
|
||||
}
|
||||
CodeUnit codeUnit = listing.getCodeUnitContaining(address);
|
||||
if (!(codeUnit instanceof Data) || ((Data) codeUnit).isDefined()) {
|
||||
status = "Cannot paste on top of defined instructions/data";
|
||||
tool.beep();
|
||||
return false;
|
||||
}
|
||||
int length = codeUnit.getLength();
|
||||
i += length;
|
||||
address = codeUnit.getMaxAddress().next();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getStatusMsg() {
|
||||
return status;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Paste";
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
return tool.execute(cmd, currentProgram);
|
||||
return tool.execute(new PasteByteStringCommand(string), currentProgram);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a Transferable from the given text.
|
||||
*
|
||||
* @param text text used to create a Transferable
|
||||
* @return a Transferable
|
||||
*/
|
||||
|
@ -511,8 +396,120 @@ public abstract class ByteCopier {
|
|||
// Inner Classes
|
||||
//==================================================================================================
|
||||
|
||||
protected class PasteByteStringCommand implements Command<Program> {
|
||||
protected final String string;
|
||||
private String status = "Pasting";
|
||||
|
||||
protected PasteByteStringCommand(String string) {
|
||||
this.string = string;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean applyTo(Program program) {
|
||||
String validString = string;
|
||||
if (!isOnlyAsciiBytes(string)) {
|
||||
tool.setStatusInfo("Pasted string contained non-text ascii bytes. " +
|
||||
"Only the ascii will be used.", true);
|
||||
validString = keepOnlyAsciiBytes(string);
|
||||
}
|
||||
|
||||
byte[] bytes = getBytes(validString);
|
||||
if (bytes == null) {
|
||||
status = "Improper data format. Expected sequence of hex bytes";
|
||||
tool.beep();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Ensure that we are not writing over instructions
|
||||
Address address = currentLocation.getAddress();
|
||||
if (!hasEnoughSpace(program, address, bytes.length)) {
|
||||
status =
|
||||
"Not enough space to paste all bytes. Encountered data or instructions.";
|
||||
tool.beep();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Ask the user before pasting a string into the program. Since having a string in
|
||||
// the clipboard is so common, this is to prevent an accidental paste.
|
||||
if (!confirmPaste(validString)) {
|
||||
return true; // the user cancelled; the command is successful
|
||||
}
|
||||
|
||||
boolean pastedAllBytes = pasteBytes(program, bytes);
|
||||
if (!pastedAllBytes) {
|
||||
tool.setStatusInfo("Not all bytes were pasted due to memory access issues",
|
||||
true);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected boolean pasteBytes(Program program, byte[] bytes) {
|
||||
|
||||
// note: loop one byte at a time here, since Memory will validate all addresses
|
||||
// before pasting any bytes
|
||||
boolean foundError = false;
|
||||
Address address = currentLocation.getAddress();
|
||||
Memory memory = program.getMemory();
|
||||
for (byte element : bytes) {
|
||||
try {
|
||||
memory.setByte(address, element);
|
||||
}
|
||||
catch (MemoryAccessException e) {
|
||||
// Keep trying the remaining bytes. Should we just stop in this case?
|
||||
foundError = true;
|
||||
}
|
||||
address = address.next();
|
||||
}
|
||||
return !foundError;
|
||||
}
|
||||
|
||||
protected boolean confirmPaste(String validString) {
|
||||
|
||||
// create a truncated version of the string to show in the dialog
|
||||
String partialText = validString.length() < 40 ? validString
|
||||
: validString.substring(0, 40) + " ...";
|
||||
int result = OptionDialog.showYesNoDialog(null, "Paste String Into Program?",
|
||||
"Are you sure you want to paste the string \"" + partialText +
|
||||
"\"\n into the program's memory?");
|
||||
|
||||
return result != OptionDialog.NO_OPTION;
|
||||
}
|
||||
|
||||
protected boolean hasEnoughSpace(Program program, Address address, int byteCount) {
|
||||
Listing listing = program.getListing();
|
||||
for (int i = 0; i < byteCount;) {
|
||||
if (address == null) {
|
||||
status = "Not enough addresses to paste bytes";
|
||||
tool.beep();
|
||||
return false;
|
||||
}
|
||||
CodeUnit codeUnit = listing.getCodeUnitContaining(address);
|
||||
if (!(codeUnit instanceof Data data) || data.isDefined()) {
|
||||
status = "Cannot paste on top of defined instructions/data";
|
||||
tool.beep();
|
||||
return false;
|
||||
}
|
||||
int length = codeUnit.getLength();
|
||||
i += length;
|
||||
address = codeUnit.getMaxAddress().next();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getStatusMsg() {
|
||||
return status;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Paste";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An iterator of bytes from memory. This class exists because the {@link MemoryByteIterator}
|
||||
* An iterator of bytes from memory. This class exists because the {@link MemoryByteIterator}
|
||||
* throws an exception from its next() method, which will not work for us.
|
||||
*/
|
||||
private static class ByteIterator implements Iterator<Byte> {
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
* 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.
|
||||
|
@ -486,78 +486,6 @@ public class MemoryManagerTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
assertEquals(block.isWrite(), newBlock.isWrite());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLiveMemory() throws Exception {
|
||||
|
||||
mem.createInitializedBlock("Test", addr(0), 0x1000, (byte) 0x55, null, false);
|
||||
|
||||
LiveMemoryHandler testHandler = new LiveMemoryHandler() {
|
||||
@Override
|
||||
public void clearCache() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte getByte(Address addr) throws MemoryAccessException {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBytes(Address addr, byte[] dest, int dIndex, int size)
|
||||
throws MemoryAccessException {
|
||||
for (int i = 0; i < size; ++i) {
|
||||
dest[dIndex + i] = (byte) i;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putByte(Address addr, byte value) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int putBytes(Address address, byte[] source, int sIndex, int size)
|
||||
throws MemoryAccessException {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addLiveMemoryListener(LiveMemoryListener listener) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeLiveMemoryListener(LiveMemoryListener listener) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
assertEquals((byte) 0x55, mem.getByte(addr(0x500)));
|
||||
|
||||
mem.setLiveMemoryHandler(testHandler);
|
||||
|
||||
byte[] bytes = new byte[5];
|
||||
mem.getBytes(addr(0x1000), bytes);
|
||||
|
||||
for (int i = 0; i < bytes.length; ++i) {
|
||||
assertEquals(i, bytes[i]);
|
||||
}
|
||||
|
||||
assertEquals((byte) 0, mem.getByte(addr(0x500)));
|
||||
|
||||
mem.setLiveMemoryHandler(null);
|
||||
|
||||
try {
|
||||
mem.getBytes(addr(0x1000), bytes);
|
||||
Assert.fail();
|
||||
}
|
||||
catch (MemoryAccessException e) {
|
||||
}
|
||||
|
||||
assertEquals((byte) 0x55, mem.getByte(addr(0x500)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSplitBlock() throws Exception {
|
||||
createBlock("Test", addr(0), 100);
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
* 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.
|
||||
|
@ -67,16 +67,6 @@ public class MyTestMemory extends AddressSet implements Memory {
|
|||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLiveMemoryHandler(LiveMemoryHandler handler) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public LiveMemoryHandler getLiveMemoryHandler() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemoryBlock createInitializedBlock(String name, Address start, InputStream is,
|
||||
long length, TaskMonitor monitor, boolean overlay)
|
||||
|
|
|
@ -18,26 +18,33 @@ package ghidra.app.plugin.core.format;
|
|||
import ghidra.util.exception.UsrException;
|
||||
|
||||
/**
|
||||
* <p>An ByteBlockAccessException indicates that the attempted
|
||||
* access is not permitted. (i.e. Readable/Writeable)</p>
|
||||
*
|
||||
* A {@code ByteBlockAccessException} indicates that the attempted access is not permitted. (i.e.
|
||||
* Readable/Writeable)
|
||||
*/
|
||||
public class ByteBlockAccessException extends UsrException {
|
||||
/**
|
||||
* <p>Constructs an ByteBlockAccessException with no detail message.
|
||||
/**
|
||||
* Construct an exception with no details
|
||||
*/
|
||||
public ByteBlockAccessException() {
|
||||
super();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* <p>Constructs an ByteBlockAccessException with the specified
|
||||
* detail message.
|
||||
public ByteBlockAccessException() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct an exception with the specified message
|
||||
*
|
||||
* @param message The message.
|
||||
* @param message the message
|
||||
*/
|
||||
public ByteBlockAccessException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
public ByteBlockAccessException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct an exception with the specified message and cause
|
||||
*
|
||||
* @param message the message
|
||||
* @param cause the cause
|
||||
*/
|
||||
public ByteBlockAccessException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
* 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.
|
||||
|
@ -49,19 +49,24 @@ public abstract class Transaction implements AutoCloseable {
|
|||
}
|
||||
|
||||
/**
|
||||
* End this transaction if currently active.
|
||||
* @param commit true if changes should be commited, false if all changes in this transaction
|
||||
* should be discarded (i.e., rollback). If this is a "sub-transaction" and commit is false,
|
||||
* the larger transaction will rollback upon completion.
|
||||
* @return true if changes have been commited or false if nothing to commit or commit parameter
|
||||
* was specified as false.
|
||||
* End this transaction if currently active.
|
||||
*
|
||||
* @param commit true if changes should be committed, false if all changes in this transaction
|
||||
* should be discarded (i.e., rollback). If this is a "sub-transaction" and commit is
|
||||
* false, the larger transaction will rollback upon completion.
|
||||
* @return true if changes have been committed or false if nothing to commit or commit parameter
|
||||
* was specified as false.
|
||||
*/
|
||||
abstract protected boolean endTransaction(@SuppressWarnings("hiding") boolean commit);
|
||||
|
||||
/**
|
||||
* Determine if this is a sub-transaction to a larger transaction. If true is returned the
|
||||
* larger transaction will not complete until all sub-transactions have ended. The larger
|
||||
* transaction will rollback upon completion if any of the sub-transactions do not commit.
|
||||
* Determine if this is a sub-transaction to a larger transaction.
|
||||
*
|
||||
* <p>
|
||||
* If true is returned the larger transaction will not complete until all sub-transactions have
|
||||
* ended. The larger transaction will rollback upon completion if any of the sub-transactions do
|
||||
* not commit.
|
||||
*
|
||||
* @return true if this is a sub-transaction, else false.
|
||||
*/
|
||||
public boolean isSubTransaction() {
|
||||
|
@ -69,16 +74,21 @@ public abstract class Transaction implements AutoCloseable {
|
|||
}
|
||||
|
||||
/**
|
||||
* Mark transaction for rollback/non-commit upon closing. A subsequent invocation of
|
||||
* {@link #commitOnClose()} will alter this state prior to closing.
|
||||
* Mark transaction for rollback/non-commit upon closing.
|
||||
*
|
||||
* <p>
|
||||
* A subsequent invocation of {@link #commitOnClose()} will alter this state prior to closing.
|
||||
*/
|
||||
public void abortOnClose() {
|
||||
commit = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark transaction for commit upon closing. This state is assumed by default. A subsequent
|
||||
* invocation of {@link #abortOnClose()} will alter this state prior to closing.
|
||||
* Mark transaction for commit upon closing.
|
||||
*
|
||||
* <p>
|
||||
* This state is assumed by default. A subsequent invocation of {@link #abortOnClose()} will
|
||||
* alter this state prior to closing.
|
||||
*/
|
||||
public void commitOnClose() {
|
||||
commit = true;
|
||||
|
@ -106,7 +116,9 @@ public abstract class Transaction implements AutoCloseable {
|
|||
|
||||
/**
|
||||
* End this transaction if active using the current commit state.
|
||||
* See {@link #commitOnClose()}, {@link #abortOnClose()}.
|
||||
*
|
||||
* @see #commitOnClose()
|
||||
* @see #abortOnClose()
|
||||
*/
|
||||
@Override
|
||||
public void close() {
|
||||
|
@ -115,5 +127,4 @@ public abstract class Transaction implements AutoCloseable {
|
|||
endTransaction(commit);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
* 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.
|
||||
|
@ -20,8 +20,7 @@ import java.util.*;
|
|||
|
||||
import ghidra.framework.model.*;
|
||||
import ghidra.framework.model.TransactionInfo.Status;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.SystemUtilities;
|
||||
import ghidra.util.*;
|
||||
import ghidra.util.datastruct.WeakDataStructureFactory;
|
||||
import ghidra.util.datastruct.WeakSet;
|
||||
|
||||
|
@ -192,9 +191,11 @@ class DomainObjectTransactionManager extends AbstractTransactionManager {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the undo stack depth.
|
||||
* (The number of items on the undo stack)
|
||||
* Returns the undo stack depth (The number of items on the undo stack).
|
||||
*
|
||||
* <p>
|
||||
* This method is for JUnits.
|
||||
*
|
||||
* @return the undo stack depth
|
||||
*/
|
||||
@Override
|
||||
|
@ -333,7 +334,7 @@ class DomainObjectTransactionManager extends AbstractTransactionManager {
|
|||
}
|
||||
|
||||
void notifyStartTransaction(TransactionInfo tx) {
|
||||
SystemUtilities.runSwingLater(() -> {
|
||||
Swing.runLater(() -> {
|
||||
for (TransactionListener listener : transactionListeners) {
|
||||
listener.transactionStarted(domainObj, tx);
|
||||
listener.undoStackChanged(domainObj);
|
||||
|
@ -342,7 +343,7 @@ class DomainObjectTransactionManager extends AbstractTransactionManager {
|
|||
}
|
||||
|
||||
void notifyEndTransaction() {
|
||||
SystemUtilities.runSwingLater(() -> {
|
||||
Swing.runLater(() -> {
|
||||
for (TransactionListener listener : transactionListeners) {
|
||||
listener.transactionEnded(domainObj);
|
||||
listener.undoStackChanged(domainObj);
|
||||
|
@ -351,7 +352,7 @@ class DomainObjectTransactionManager extends AbstractTransactionManager {
|
|||
}
|
||||
|
||||
void notifyUndoStackChanged() {
|
||||
SystemUtilities.runSwingLater(() -> {
|
||||
Swing.runLater(() -> {
|
||||
for (TransactionListener listener : transactionListeners) {
|
||||
listener.undoStackChanged(domainObj);
|
||||
}
|
||||
|
@ -359,7 +360,7 @@ class DomainObjectTransactionManager extends AbstractTransactionManager {
|
|||
}
|
||||
|
||||
void notifyUndoRedo() {
|
||||
SystemUtilities.runSwingLater(() -> {
|
||||
Swing.runLater(() -> {
|
||||
for (TransactionListener listener : transactionListeners) {
|
||||
listener.undoRedoOccurred(domainObj);
|
||||
listener.undoStackChanged(domainObj);
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
* 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.
|
||||
|
@ -46,8 +46,8 @@ public interface TransactionInfo {
|
|||
|
||||
/**
|
||||
* Determine if the corresponding transaction, and all of its sub-transactions, has been
|
||||
* comitted to the underlying database.
|
||||
* @return true if the corresponding transaction has been comitted, else false.
|
||||
* committed to the underlying database.
|
||||
* @return true if the corresponding transaction has been committed, else false.
|
||||
*/
|
||||
public boolean hasCommittedDBTransaction();
|
||||
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
* 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.
|
||||
|
@ -362,9 +362,6 @@ public class MemoryBlockDB implements MemoryBlock {
|
|||
|
||||
@Override
|
||||
public byte getByte(Address addr) throws MemoryAccessException {
|
||||
if (memMap.getLiveMemoryHandler() != null) {
|
||||
return memMap.getByte(addr);
|
||||
}
|
||||
checkValid();
|
||||
long offset = getBlockOffset(addr);
|
||||
return getByte(offset);
|
||||
|
@ -378,10 +375,6 @@ public class MemoryBlockDB implements MemoryBlock {
|
|||
@Override
|
||||
public int getBytes(Address addr, byte[] b, int off, int len)
|
||||
throws IndexOutOfBoundsException, MemoryAccessException {
|
||||
if (memMap.getLiveMemoryHandler() != null) {
|
||||
return memMap.getBytes(addr, b, off, len);
|
||||
}
|
||||
|
||||
checkValid();
|
||||
long offset = getBlockOffset(addr);
|
||||
return getBytes(offset, b, off, len);
|
||||
|
@ -389,10 +382,6 @@ public class MemoryBlockDB implements MemoryBlock {
|
|||
|
||||
@Override
|
||||
public void putByte(Address addr, byte b) throws MemoryAccessException {
|
||||
if (memMap.getLiveMemoryHandler() != null) {
|
||||
memMap.setByte(addr, b);
|
||||
return;
|
||||
}
|
||||
long offset = getBlockOffset(addr);
|
||||
memMap.lock.acquire();
|
||||
try {
|
||||
|
@ -414,10 +403,6 @@ public class MemoryBlockDB implements MemoryBlock {
|
|||
@Override
|
||||
public int putBytes(Address addr, byte[] b, int off, int len)
|
||||
throws IndexOutOfBoundsException, MemoryAccessException {
|
||||
if (memMap.getLiveMemoryHandler() != null) {
|
||||
memMap.setBytes(addr, b, off, len);
|
||||
return len;
|
||||
}
|
||||
memMap.lock.acquire();
|
||||
try {
|
||||
checkValid();
|
||||
|
|
|
@ -40,7 +40,7 @@ import ghidra.util.task.TaskMonitor;
|
|||
/**
|
||||
* The database memory map manager.
|
||||
*/
|
||||
public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
|
||||
public class MemoryMapDB implements Memory, ManagerDB {
|
||||
|
||||
private ProgramDB program;
|
||||
private AddressMapDB addrMap;
|
||||
|
@ -68,7 +68,6 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
|
|||
}
|
||||
|
||||
private MemoryBlock lastBlock;// the last accessed block
|
||||
private LiveMemoryHandler liveMemory;
|
||||
|
||||
// lazy hashmap of block names to blocks, must be reloaded if blocks are removed or added
|
||||
private HashMap<String, MemoryBlock> nameBlockMap = new HashMap<>();
|
||||
|
@ -260,9 +259,6 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
|
|||
initializeBlocks();
|
||||
buildAddressSets(true);
|
||||
}
|
||||
if (liveMemory != null) {
|
||||
liveMemory.clearCache();
|
||||
}
|
||||
addrMap.memoryMapChanged(this);
|
||||
}
|
||||
|
||||
|
@ -359,10 +355,6 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
|
|||
|
||||
@Override
|
||||
public AddressSetView getLoadedAndInitializedAddressSet() {
|
||||
if (liveMemory != null) {
|
||||
return this; // all memory is initialized!
|
||||
}
|
||||
|
||||
MemoryAddressSetViews localAddrSetViews = buildAddressSets(false);
|
||||
return new AddressSetViewAdapter(localAddrSetViews.initializedAndLoaded);
|
||||
}
|
||||
|
@ -586,29 +578,6 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
|
|||
return defaultEndian == BIG_ENDIAN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLiveMemoryHandler(LiveMemoryHandler handler) {
|
||||
lock.acquire();
|
||||
try {
|
||||
if (liveMemory != null) {
|
||||
liveMemory.removeLiveMemoryListener(this);
|
||||
}
|
||||
liveMemory = handler;
|
||||
if (liveMemory != null) {
|
||||
liveMemory.addLiveMemoryListener(this);
|
||||
}
|
||||
program.invalidate();
|
||||
}
|
||||
finally {
|
||||
lock.release();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public LiveMemoryHandler getLiveMemoryHandler() {
|
||||
return liveMemory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemoryBlock createInitializedBlock(String name, Address start, long size,
|
||||
byte initialValue, TaskMonitor monitor, boolean overlay) throws LockException,
|
||||
|
@ -976,10 +945,6 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
|
|||
lock.acquire();
|
||||
try {
|
||||
program.checkExclusiveAccess();
|
||||
if (liveMemory != null) {
|
||||
throw new MemoryBlockException(
|
||||
"Memory move operation not permitted while live memory is active");
|
||||
}
|
||||
checkBlock(block);
|
||||
MemoryBlockDB memBlock = (MemoryBlockDB) block;
|
||||
|
||||
|
@ -1026,10 +991,6 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
|
|||
lock.acquire();
|
||||
try {
|
||||
program.checkExclusiveAccess();
|
||||
if (liveMemory != null) {
|
||||
throw new MemoryBlockException(
|
||||
"Memory split operation not permitted while live memory is active");
|
||||
}
|
||||
checkBlock(block);
|
||||
MemoryBlockDB memBlock = (MemoryBlockDB) block;
|
||||
if (!memBlock.contains(addr)) {
|
||||
|
@ -1115,10 +1076,6 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
|
|||
throws MemoryBlockException, LockException {
|
||||
|
||||
program.checkExclusiveAccess();
|
||||
if (liveMemory != null) {
|
||||
throw new MemoryBlockException(
|
||||
"Memory join operation not permitted while live memory is active");
|
||||
}
|
||||
|
||||
checkBlockForJoining(block1);
|
||||
checkBlockForJoining(block2);
|
||||
|
@ -1433,9 +1390,6 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
|
|||
|
||||
@Override
|
||||
public byte getByte(Address addr) throws MemoryAccessException {
|
||||
if (liveMemory != null) {
|
||||
return liveMemory.getByte(addr);
|
||||
}
|
||||
MemoryBlock block = getBlockDB(addr);
|
||||
if (block == null) {
|
||||
throw new MemoryAccessException(
|
||||
|
@ -1452,9 +1406,6 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
|
|||
@Override
|
||||
public int getBytes(Address addr, byte[] dest, int dIndex, int size)
|
||||
throws MemoryAccessException {
|
||||
if (liveMemory != null) {
|
||||
return liveMemory.getBytes(addr, dest, dIndex, size);
|
||||
}
|
||||
int numRead = 0;
|
||||
long lastRead = 0;
|
||||
while (numRead < size) {
|
||||
|
@ -1685,11 +1636,6 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
|
|||
|
||||
@Override
|
||||
public void setByte(Address addr, byte value) throws MemoryAccessException {
|
||||
if (liveMemory != null) {
|
||||
liveMemory.putByte(addr, value);
|
||||
fireBytesChanged(addr, 1);
|
||||
return;
|
||||
}
|
||||
lock.acquire();
|
||||
try {
|
||||
MemoryBlock block = getBlock(addr);
|
||||
|
@ -1714,12 +1660,6 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
|
|||
@Override
|
||||
public void setBytes(Address address, byte[] source, int sIndex, int size)
|
||||
throws MemoryAccessException {
|
||||
if (liveMemory != null) {
|
||||
int cnt = liveMemory.putBytes(address, source, sIndex, size);
|
||||
fireBytesChanged(address, cnt);
|
||||
return;
|
||||
}
|
||||
|
||||
lock.acquire();
|
||||
try {
|
||||
Address addr = address;
|
||||
|
@ -2149,11 +2089,6 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
|
|||
return super.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void memoryChanged(Address addr, int size) {
|
||||
fireBytesChanged(addr, size);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressRangeIterator getAddressRanges(Address start, boolean forward) {
|
||||
return allAddrSet.getAddressRanges(start, forward);
|
||||
|
|
|
@ -1,81 +0,0 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* 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.model.mem;
|
||||
|
||||
import ghidra.program.model.address.Address;
|
||||
|
||||
/**
|
||||
* Live memory handler interface.
|
||||
*/
|
||||
public interface LiveMemoryHandler {
|
||||
|
||||
/**
|
||||
* Called when the memory map is re-initializing. Usually after an undo or redo.
|
||||
*/
|
||||
public void clearCache();
|
||||
|
||||
/**
|
||||
* Gets the byte at the given address.
|
||||
* @param addr the address of the byte to be retrieved
|
||||
* @return the byte at the given address.
|
||||
* @throws MemoryAccessException if the byte can't be read.
|
||||
*/
|
||||
public byte getByte(Address addr) throws MemoryAccessException;
|
||||
|
||||
/**
|
||||
* Get the bytes at the given address and size and put them into the destination buffer.
|
||||
* @param address the address of the first byte to be retrieved.
|
||||
* @param buffer the byte buffer in which to place the bytes.
|
||||
* @param startIndex the starting index in the buffer to put the first byte.
|
||||
* @param size the number of bytes to retrieve and put in the buffer.
|
||||
* @return the number of bytes placed into the given buffer.
|
||||
* @throws MemoryAccessException if the bytes can't be read.
|
||||
*/
|
||||
public int getBytes(Address address, byte[] buffer, int startIndex, int size) throws MemoryAccessException;
|
||||
|
||||
/**
|
||||
* Writes the given byte value to the address in memory.
|
||||
* @param address the address whose byte is to be updated to the new value.
|
||||
* @param value the value to set at the given address.
|
||||
* @throws MemoryAccessException if the value can not be written to the memory.
|
||||
*/
|
||||
public void putByte(Address address, byte value) throws MemoryAccessException;
|
||||
|
||||
/**
|
||||
* Writes the given bytes to memory starting at the given address.
|
||||
* @param address the address in memory to write the bytes.
|
||||
* @param source the buffer containing the byte values to be written to memory.
|
||||
* @param startIndex the starting index in the buffer to get byte values.
|
||||
* @param size the number of bytes to write to memory.
|
||||
* @return the number of bytes written to memory.
|
||||
* @throws MemoryAccessException if the bytes can't be written to memory.
|
||||
*/
|
||||
public int putBytes(Address address, byte[] source, int startIndex, int size) throws MemoryAccessException;
|
||||
|
||||
/**
|
||||
* Adds a LiveMemoryListener to this handler. The listener will be notified when memory
|
||||
* bytes change.
|
||||
* @param listener the listener to be notified of memory byte value changes.
|
||||
*/
|
||||
public void addLiveMemoryListener(LiveMemoryListener listener);
|
||||
|
||||
/**
|
||||
* Removes the LiveMemoryListener from this handler.
|
||||
* @param listener the listener to be removed.
|
||||
*/
|
||||
public void removeLiveMemoryListener(LiveMemoryListener listener);
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* 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.model.mem;
|
||||
|
||||
import ghidra.program.model.address.Address;
|
||||
|
||||
public interface LiveMemoryListener {
|
||||
public void memoryChanged(Address addr, int size);
|
||||
}
|
|
@ -151,18 +151,6 @@ public interface Memory extends AddressSetView {
|
|||
return block != null && block.isExternalBlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the live memory handler
|
||||
* @param handler the live memory handler
|
||||
*/
|
||||
public void setLiveMemoryHandler(LiveMemoryHandler handler);
|
||||
|
||||
/**
|
||||
* Returns the live memory handler instance used by this memory.
|
||||
* @return the live memory handler
|
||||
*/
|
||||
public LiveMemoryHandler getLiveMemoryHandler();
|
||||
|
||||
/**
|
||||
* Create an initialized memory block based upon a data {@link InputStream} and add it to
|
||||
* this Memory.
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
* 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.
|
||||
|
@ -175,16 +175,6 @@ public class StubMemory extends AddressSet implements Memory {
|
|||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLiveMemoryHandler(LiveMemoryHandler handler) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public LiveMemoryHandler getLiveMemoryHandler() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemoryBlock createInitializedBlock(String name, Address start, InputStream is,
|
||||
long length, TaskMonitor monitor, boolean overlay) {
|
||||
|
|
|
@ -35,8 +35,6 @@ import docking.dnd.GClipboard;
|
|||
import docking.widgets.OptionDialog;
|
||||
import generic.Unique;
|
||||
import ghidra.app.plugin.assembler.*;
|
||||
import ghidra.app.plugin.core.assembler.AssemblerPlugin;
|
||||
import ghidra.app.plugin.core.assembler.AssemblerPluginTestHelper;
|
||||
import ghidra.app.plugin.core.clipboard.ClipboardPlugin;
|
||||
import ghidra.app.plugin.core.codebrowser.CodeViewerProvider;
|
||||
import ghidra.app.plugin.core.debug.disassemble.DebuggerDisassemblerPlugin;
|
||||
|
@ -465,7 +463,8 @@ public class DebuggerControlPluginTest extends AbstractGhidraHeadedDebuggerInteg
|
|||
|
||||
@Test
|
||||
public void testPatchDataActionInDynamicListingEmu() throws Throwable {
|
||||
AssemblerPlugin assemblerPlugin = addPlugin(tool, AssemblerPlugin.class);
|
||||
DebuggerDisassemblerPlugin disassemblerPlugin =
|
||||
addPlugin(tool, DebuggerDisassemblerPlugin.class);
|
||||
|
||||
assertFalse(controlPlugin.actionControlMode.isEnabled());
|
||||
|
||||
|
@ -482,8 +481,8 @@ public class DebuggerControlPluginTest extends AbstractGhidraHeadedDebuggerInteg
|
|||
}
|
||||
|
||||
CodeViewerProvider listingProvider = listingPlugin.getProvider();
|
||||
AssemblerPluginTestHelper helper =
|
||||
new AssemblerPluginTestHelper(assemblerPlugin, listingProvider, view);
|
||||
DebuggerDisassemblerPluginTestHelper helper =
|
||||
new DebuggerDisassemblerPluginTestHelper(disassemblerPlugin, listingProvider, view);
|
||||
|
||||
traceManager.activateTrace(tb.trace);
|
||||
waitForSwing();
|
||||
|
|
|
@ -1178,11 +1178,7 @@ public class DebuggerMemoryBytesProviderTest extends AbstractGhidraHeadedDebugge
|
|||
performAction(actionPaste, memBytesProvider, false);
|
||||
OptionDialog confirm = waitForDialogComponent(OptionDialog.class);
|
||||
pressButtonByText(confirm, "Yes");
|
||||
// TODO: This shouldn't be separate calls per byte!
|
||||
handleWriteMemInvocation(process, tb.addr(0x55550800), new Bytes(0x42));
|
||||
handleWriteMemInvocation(process, tb.addr(0x55550801), new Bytes(0x53));
|
||||
handleWriteMemInvocation(process, tb.addr(0x55550802), new Bytes(0x64));
|
||||
handleWriteMemInvocation(process, tb.addr(0x55550803), new Bytes(0x75));
|
||||
handleWriteMemInvocation(process, tb.addr(0x55550800), new Bytes(0x42, 0x53, 0x64, 0x75));
|
||||
|
||||
performAction(actionEdit);
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue