From bef0660e6a88a8e58fe1b5412caccb567107a1a3 Mon Sep 17 00:00:00 2001 From: Dan <46821332+nsadeveloper789@users.noreply.github.com> Date: Tue, 18 Feb 2025 18:23:30 +0000 Subject: [PATCH] GP-5314: Destroy LiveMemoryHandler --- .../app/services/DebuggerControlService.java | 10 +- .../debug/service/tracermi/OpenTrace.java | 50 +++- .../service/tracermi/TraceRmiHandler.java | 38 ++- .../AbstractTracePatchInstructionAction.java | 25 +- ...ntPlatformTracePatchInstructionAction.java | 9 +- .../DebuggerDisassemblerPlugin.java | 7 +- .../disassemble/TracePatchDataAction.java | 65 +++++ .../core/debug/gui/PasteIntoTargetMixin.java | 77 ++++++ .../gui/listing/DebuggerListingProvider.java | 23 ++ .../memory/DebuggerMemoryBytesProvider.java | 155 +++++++++--- .../control/DebuggerControlServicePlugin.java | 157 +----------- .../DebuggerDisassemblerPluginTestHelper.java | 7 +- .../DBTraceGuestPlatformMappedMemory.java | 14 +- .../AbstractDBTraceProgramViewMemory.java | 24 +- .../model/program/TraceProgramViewMemory.java | 10 - .../core/assembler/AssemblerPlugin.java | 63 +++-- .../core/assembler/PatchDataAction.java | 39 +-- .../main/java/ghidra/app/util/ByteCopier.java | 239 +++++++++--------- .../database/mem/MemoryManagerTest.java | 76 +----- .../plugin/core/checksums/MyTestMemory.java | 14 +- .../core/format/ByteBlockAccessException.java | 43 ++-- .../DB/src/main/java/db/Transaction.java | 45 ++-- .../data/DomainObjectTransactionManager.java | 21 +- .../framework/model/TransactionInfo.java | 8 +- .../program/database/mem/MemoryBlockDB.java | 19 +- .../program/database/mem/MemoryMapDB.java | 67 +---- .../program/model/mem/LiveMemoryHandler.java | 81 ------ .../program/model/mem/LiveMemoryListener.java | 23 -- .../java/ghidra/program/model/mem/Memory.java | 12 - .../ghidra/program/model/mem/StubMemory.java | 14 +- .../control/DebuggerControlPluginTest.java | 9 +- .../DebuggerMemoryBytesProviderTest.java | 6 +- 32 files changed, 667 insertions(+), 783 deletions(-) create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/disassemble/TracePatchDataAction.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/PasteIntoTargetMixin.java delete mode 100644 Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/mem/LiveMemoryHandler.java delete mode 100644 Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/mem/LiveMemoryListener.java diff --git a/Ghidra/Debug/Debugger-api/src/main/java/ghidra/app/services/DebuggerControlService.java b/Ghidra/Debug/Debugger-api/src/main/java/ghidra/app/services/DebuggerControlService.java index 87cefac0ea..f592f55f90 100644 --- a/Ghidra/Debug/Debugger-api/src/main/java/ghidra/app/services/DebuggerControlService.java +++ b/Ghidra/Debug/Debugger-api/src/main/java/ghidra/app/services/DebuggerControlService.java @@ -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); } diff --git a/Ghidra/Debug/Debugger-rmi-trace/src/main/java/ghidra/app/plugin/core/debug/service/tracermi/OpenTrace.java b/Ghidra/Debug/Debugger-rmi-trace/src/main/java/ghidra/app/plugin/core/debug/service/tracermi/OpenTrace.java index c4f0697308..412cbfd9e2 100644 --- a/Ghidra/Debug/Debugger-rmi-trace/src/main/java/ghidra/app/plugin/core/debug/service/tracermi/OpenTrace.java +++ b/Ghidra/Debug/Debugger-rmi-trace/src/main/java/ghidra/app/plugin/core/debug/service/tracermi/OpenTrace.java @@ -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; } diff --git a/Ghidra/Debug/Debugger-rmi-trace/src/main/java/ghidra/app/plugin/core/debug/service/tracermi/TraceRmiHandler.java b/Ghidra/Debug/Debugger-rmi-trace/src/main/java/ghidra/app/plugin/core/debug/service/tracermi/TraceRmiHandler.java index 8ffb81edee..8589e7efcc 100644 --- a/Ghidra/Debug/Debugger-rmi-trace/src/main/java/ghidra/app/plugin/core/debug/service/tracermi/TraceRmiHandler.java +++ b/Ghidra/Debug/Debugger-rmi-trace/src/main/java/ghidra/app/plugin/core/debug/service/tracermi/TraceRmiHandler.java @@ -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 diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/disassemble/AbstractTracePatchInstructionAction.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/disassemble/AbstractTracePatchInstructionAction.java index b9efb61aad..cfd3fd736d 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/disassemble/AbstractTracePatchInstructionAction.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/disassemble/AbstractTracePatchInstructionAction.java @@ -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) { diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/disassemble/CurrentPlatformTracePatchInstructionAction.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/disassemble/CurrentPlatformTracePatchInstructionAction.java index 676fa0219d..b763222cb7 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/disassemble/CurrentPlatformTracePatchInstructionAction.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/disassemble/CurrentPlatformTracePatchInstructionAction.java @@ -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 diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/disassemble/DebuggerDisassemblerPlugin.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/disassemble/DebuggerDisassemblerPlugin.java index 8d82536883..777e85b6f2 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/disassemble/DebuggerDisassemblerPlugin.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/disassemble/DebuggerDisassemblerPlugin.java @@ -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); } /** diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/disassemble/TracePatchDataAction.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/disassemble/TracePatchDataAction.java new file mode 100644 index 0000000000..57d4508837 --- /dev/null +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/disassemble/TracePatchDataAction.java @@ -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 + } +} diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/PasteIntoTargetMixin.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/PasteIntoTargetMixin.java new file mode 100644 index 0000000000..2dbe69d906 --- /dev/null +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/PasteIntoTargetMixin.java @@ -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; + } + } +} diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/listing/DebuggerListingProvider.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/listing/DebuggerListingProvider.java index c2c8e4a842..5acb99e7fb 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/listing/DebuggerListingProvider.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/listing/DebuggerListingProvider.java @@ -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; diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/memory/DebuggerMemoryBytesProvider.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/memory/DebuggerMemoryBytesProvider.java index d8eae4b92b..34316f89b9 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/memory/DebuggerMemoryBytesProvider.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/memory/DebuggerMemoryBytesProvider.java @@ -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} + * + *
+ * 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
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/control/DebuggerControlServicePlugin.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/control/DebuggerControlServicePlugin.java
index c1f21cdc68..508300556c 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/control/DebuggerControlServicePlugin.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/control/DebuggerControlServicePlugin.java
@@ -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
- * For trace views, this only redirects memory writes.
- */
- @Override
- void setLiveMemoryHandler(LiveMemoryHandler handler);
}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/assembler/AssemblerPlugin.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/assembler/AssemblerPlugin.java
index 8c196badf7..976403460f 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/assembler/AssemblerPlugin.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/assembler/AssemblerPlugin.java
@@ -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);
}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/assembler/PatchDataAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/assembler/PatchDataAction.java
index 40b026d0c0..c884e2a1e1 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/assembler/PatchDataAction.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/assembler/PatchDataAction.java
@@ -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) {
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/ByteCopier.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/ByteCopier.java
index 2054bd43d4..ee77db5759 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/ByteCopier.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/ByteCopier.java
@@ -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 An ByteBlockAccessException indicates that the attempted
- * access is not permitted. (i.e. Readable/Writeable) Constructs an ByteBlockAccessException with no detail message.
+ /**
+ * Construct an exception with no details
*/
- public ByteBlockAccessException() {
- super();
- }
-
-
- /**
- * 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);
+ }
+}
diff --git a/Ghidra/Framework/DB/src/main/java/db/Transaction.java b/Ghidra/Framework/DB/src/main/java/db/Transaction.java
index f1a48fc24c..714fd7a717 100644
--- a/Ghidra/Framework/DB/src/main/java/db/Transaction.java
+++ b/Ghidra/Framework/DB/src/main/java/db/Transaction.java
@@ -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.
+ *
+ *
+ * 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.
+ *
+ *
+ * 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.
+ *
+ *
+ * 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);
}
}
-
}
diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/data/DomainObjectTransactionManager.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/data/DomainObjectTransactionManager.java
index d9791b17d2..b4d98f16e7 100644
--- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/data/DomainObjectTransactionManager.java
+++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/data/DomainObjectTransactionManager.java
@@ -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).
+ *
+ *
* 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);
diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/model/TransactionInfo.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/model/TransactionInfo.java
index d2d5188716..5b12f0b781 100644
--- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/model/TransactionInfo.java
+++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/model/TransactionInfo.java
@@ -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();
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryBlockDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryBlockDB.java
index a77953eb56..b16b173f00 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryBlockDB.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryBlockDB.java
@@ -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();
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryMapDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryMapDB.java
index 65189abc37..6c262f3512 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryMapDB.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryMapDB.java
@@ -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