diff --git a/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/util/AsyncProtobufMessageChannel.java b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/util/AsyncProtobufMessageChannel.java index 2f9bd3e797..c1efeafc93 100644 --- a/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/util/AsyncProtobufMessageChannel.java +++ b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/util/AsyncProtobufMessageChannel.java @@ -26,7 +26,7 @@ import com.google.protobuf.*; import com.google.protobuf.CodedOutputStream.OutOfSpaceException; import ghidra.async.*; -import ghidra.comm.util.ByteBufferUtils; +import ghidra.util.ByteBufferUtils; import ghidra.util.Msg; public class AsyncProtobufMessageChannel { 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 cdc6637163..dcf3f6799a 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 @@ -381,10 +381,13 @@ public class DebuggerListingProvider extends CodeViewerProvider implements Listi protected final ForStaticSyncMappingChangeListener mappingChangeListener = new ForStaticSyncMappingChangeListener(); + protected final boolean isMainListing; + public DebuggerListingProvider(DebuggerListingPlugin plugin, FormatManager formatManager, boolean isConnected) { super(plugin, formatManager, isConnected); this.plugin = plugin; + this.isMainListing = isConnected; goToDialog = new DebuggerGoToDialog(this); @@ -420,6 +423,34 @@ public class DebuggerListingProvider extends CodeViewerProvider implements Listi setHelpLocation(DebuggerResources.HELP_PROVIDER_LISTING); } + @Override + public boolean isConnected() { + /* + * NB. Other plugins ask isConnected meaning the main static listing. We don't want to be + * mistaken for it. + */ + return false; + } + + /** + * Check if this is the main dynamic listing. + * + *

+ * The method {@link #isConnected()} is not quite the same as this, although the concepts are a + * little conflated, since before the debugger, no one else presented a listing that could claim + * to be "main" except the "connected" one. Here, we treat "connected" to mean that the address + * is synchronized exactly with the other providers. "Main" on the other hand, does not + * necessarily have that property, but it is still not a snapshot. It is the main + * listing presented by this plugin, and so it has certain unique features. Calling + * {@link DebuggerListingPlugin#getConnectedProvider()} will return the main dynamic listing, + * despite it not really being "connected." + * + * @return true if this is the main listing for the plugin. + */ + public boolean isMainListing() { + return isMainListing; + } + @Override public String getWindowGroup() { //TODO: Overriding this to align disconnected providers @@ -428,7 +459,7 @@ public class DebuggerListingProvider extends CodeViewerProvider implements Listi @Override public void writeDataState(SaveState saveState) { - if (!isConnected()) { + if (!isMainListing()) { current.writeDataState(tool, saveState, KEY_DEBUGGER_COORDINATES); } super.writeDataState(saveState); @@ -436,7 +467,7 @@ public class DebuggerListingProvider extends CodeViewerProvider implements Listi @Override public void readDataState(SaveState saveState) { - if (!isConnected()) { + if (!isMainListing()) { DebuggerCoordinates coordinates = DebuggerCoordinates.readDataState(tool, saveState, KEY_DEBUGGER_COORDINATES, true); coordinatesActivated(coordinates); @@ -466,7 +497,7 @@ public class DebuggerListingProvider extends CodeViewerProvider implements Listi CONFIG_STATE_HANDLER.readConfigState(this, saveState); actionTrackLocation.setCurrentActionStateByUserData(trackingSpec); - if (isConnected()) { + if (isMainListing()) { actionSyncToStaticListing.setSelected(syncToStaticListing); followsCurrentThread = true; } @@ -549,7 +580,7 @@ public class DebuggerListingProvider extends CodeViewerProvider implements Listi this.markerService = markerService; createNewStaticTrackingMarker(); - if (this.markerService != null && !isConnected()) { + if (this.markerService != null && !isMainListing()) { // NOTE: Connected provider marker listener is taken care of by CodeBrowserPlugin this.markerService.addChangeListener(markerChangeListener); } @@ -735,7 +766,7 @@ public class DebuggerListingProvider extends CodeViewerProvider implements Listi .onAction(this::activatedGoTo) .buildAndInstallLocal(this); - if (isConnected()) { + if (isMainListing()) { actionSyncToStaticListing = new SyncToStaticListingAction(); } else { @@ -999,7 +1030,7 @@ public class DebuggerListingProvider extends CodeViewerProvider implements Listi } public void setSyncToStaticListing(boolean sync) { - if (!isConnected()) { + if (!isMainListing()) { throw new IllegalStateException( "Only the main dynamic listing can be synced to the main static listing"); } @@ -1018,7 +1049,7 @@ public class DebuggerListingProvider extends CodeViewerProvider implements Listi } public void setFollowsCurrentThread(boolean follows) { - if (isConnected()) { + if (isMainListing()) { throw new IllegalStateException( "The main dynamic listing always follows the current trace and thread"); } diff --git a/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/util/ByteBufferUtils.java b/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/util/ByteBufferUtils.java deleted file mode 100644 index f37a8662c0..0000000000 --- a/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/util/ByteBufferUtils.java +++ /dev/null @@ -1,54 +0,0 @@ -/* ### - * 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.comm.util; - -import java.nio.ByteBuffer; - -/** - * Some utilities for manipulating a {@link ByteBuffer} - */ -public interface ByteBufferUtils { - /** - * Resize a write-mode buffer - * - * This preserves the buffer contents - * - * @param buf the buffer - * @param capacity the new capacity, greater or equal to the buffer's limit - * @return the new buffer - */ - public static ByteBuffer resize(ByteBuffer buf, int capacity) { - if (capacity < buf.limit()) { - throw new IllegalArgumentException("New capacity must fit current contents"); - } - buf.flip(); - ByteBuffer resized = ByteBuffer.allocate(capacity); - resized.put(buf); - return resized; - } - - /** - * Resize a write-mode buffer to twice its current capacity - * - * This preserves the buffer contents - * - * @param buf the buffer - * @return the new buffer - */ - public static ByteBuffer upsize(ByteBuffer buf) { - return resize(buf, buf.capacity() * 2); - } -} diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceCommentAdapter.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceCommentAdapter.java index 352e529aed..5fae79917a 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceCommentAdapter.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceCommentAdapter.java @@ -34,8 +34,12 @@ import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMap; import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree; import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree.AbstractDBTraceAddressSnapRangePropertyMapData; import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree.TraceAddressSnapRangeQuery; +import ghidra.trace.database.space.DBTraceSpaceKey; import ghidra.trace.database.thread.DBTraceThreadManager; +import ghidra.trace.model.ImmutableTraceAddressSnapRange; +import ghidra.trace.model.Trace.TraceCommentChangeType; import ghidra.trace.model.TraceAddressSnapRange; +import ghidra.trace.util.TraceChangeRecord; import ghidra.util.LockHold; import ghidra.util.database.*; import ghidra.util.database.annot.*; @@ -49,7 +53,7 @@ public class DBTraceCommentAdapter protected static final int MAX_COMMENT_TYPE = CodeUnit.REPEATABLE_COMMENT; @DBAnnotatedObjectInfo(version = 0) - protected static class DBTraceCommentEntry + public static class DBTraceCommentEntry extends AbstractDBTraceAddressSnapRangePropertyMapData { static final String TYPE_COLUMN_NAME = "Type"; static final String COMMENT_COLUMN_NAME = "Comment"; @@ -89,6 +93,10 @@ public class DBTraceCommentAdapter void setLifespan(Range lifespan) { super.doSetLifespan(lifespan); } + + public int getType() { + return type; + } } public DBTraceCommentAdapter(DBHandle dbh, DBOpenMode openMode, ReadWriteLock lock, @@ -106,10 +114,15 @@ public class DBTraceCommentAdapter if (commentType < MIN_COMMENT_TYPE || commentType > MAX_COMMENT_TYPE) { throw new IllegalArgumentException("commentType"); } + String oldValue = null; try (LockHold hold = LockHold.lock(lock.writeLock())) { for (DBTraceCommentEntry entry : reduce(TraceAddressSnapRangeQuery.intersecting( new AddressRangeImpl(address, address), lifespan)).values()) { if (entry.type == commentType) { + if (lifespan.hasLowerBound() && + entry.getLifespan().contains(lifespan.lowerEndpoint())) { + oldValue = entry.comment; + } makeWay(entry, lifespan); } } @@ -118,6 +131,11 @@ public class DBTraceCommentAdapter entry.set((byte) commentType, comment); } } + trace.setChanged(new TraceChangeRecord( + TraceCommentChangeType.byType(commentType), + DBTraceSpaceKey.create(address.getAddressSpace(), null, 0), + new ImmutableTraceAddressSnapRange(address, lifespan), + oldValue, comment)); } public static String commentFromArray(String[] comment) { diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/memory/DBTraceMemorySpace.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/memory/DBTraceMemorySpace.java index c73708808f..f8caac413f 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/memory/DBTraceMemorySpace.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/memory/DBTraceMemorySpace.java @@ -15,8 +15,6 @@ */ package ghidra.trace.database.memory; -import static ghidra.lifecycle.Unfinished.TODO; - import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; @@ -47,8 +45,8 @@ import ghidra.trace.model.Trace.*; import ghidra.trace.model.memory.*; import ghidra.trace.util.TraceChangeRecord; import ghidra.trace.util.TraceViewportSpanIterator; -import ghidra.util.LockHold; -import ghidra.util.MathUtilities; +import ghidra.util.*; +import ghidra.util.AddressIteratorAdapter; import ghidra.util.database.*; import ghidra.util.database.spatial.rect.Rectangle2DDirection; import ghidra.util.exception.DuplicateNameException; @@ -758,10 +756,59 @@ public class DBTraceMemorySpace implements Unfinished, TraceMemorySpace, DBTrace return len; } + protected Address doFindBytesInRange(long snap, AddressRange range, ByteBuffer data, + ByteBuffer mask, boolean forward, TaskMonitor monitor) { + int len = data.capacity(); + AddressRange rangeOfStarts = + new AddressRangeImpl(range.getMinAddress(), range.getMaxAddress().subtract(len - 1)); + ByteBuffer read = ByteBuffer.allocate(len); + for (Address addr : AddressIteratorAdapter.forRange(rangeOfStarts, forward)) { + monitor.incrementProgress(1); + if (monitor.isCancelled()) { + return null; + } + read.clear(); + int l = getBytes(snap, addr, read); + if (l != len) { + continue; + } + if (!ByteBufferUtils.maskedEquals(mask, data, read)) { + continue; + } + return addr; + } + return null; + } + @Override public Address findBytes(long snap, AddressRange range, ByteBuffer data, ByteBuffer mask, boolean forward, TaskMonitor monitor) { - return TODO(); + // ProgramDB uses the naive method with some skipping, so here we go.... + // TODO: This could be made faster by skipping over non-initialized blocks + // TODO: DFA method would be complicated by masks.... + int len = data.capacity(); + if (mask != null && mask.capacity() != len) { + throw new IllegalArgumentException("data and mask must have same capacity"); + } + if (len == 0 || range.getLength() > 0 && range.getLength() < len) { + return null; + } + + // TODO: Could do better, but have to worry about viewport, too + // This will reduce the search to ranges that have been written at any snap + // We could do for this and previous snaps, but that's where the viewport comes in. + // TODO: Potentially costly to pre-compute the set concretely + AddressSet known = new AddressSet( + stateMapSpace.getAddressSetView(Range.all(), s -> s == TraceMemoryState.KNOWN)) + .intersect(new AddressSet(range)); + monitor.initialize(known.getNumAddresses()); + for (AddressRange knownRange : known.getAddressRanges(forward)) { + Address found = doFindBytesInRange(snap, knownRange, data, mask, forward, monitor); + if (found != null) { + return found; + } + } + return null; } // TODO: Test this diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/program/AbstractDBTraceProgramViewListing.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/program/AbstractDBTraceProgramViewListing.java index 9888c57eea..fe45f80d9f 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/program/AbstractDBTraceProgramViewListing.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/program/AbstractDBTraceProgramViewListing.java @@ -424,8 +424,10 @@ public abstract class AbstractDBTraceProgramViewListing implements TraceProgramV @Override public AddressIterator getCommentAddressIterator(int commentType, AddressSetView addrSet, boolean forward) { - // TODO Auto-generated method stub - return null; + return new IntersectionAddressSetView(addrSet, program.viewport.unionedAddresses( + s -> program.trace.getCommentAdapter() + .getAddressSetView(Range.singleton(s), e -> e.getType() == commentType))) + .getAddresses(forward); } @Override diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/program/AbstractDBTraceProgramViewMemory.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/program/AbstractDBTraceProgramViewMemory.java index 76b8d5097f..77ad369a6b 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/program/AbstractDBTraceProgramViewMemory.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/program/AbstractDBTraceProgramViewMemory.java @@ -240,8 +240,10 @@ public abstract class AbstractDBTraceProgramViewMemory ByteBuffer bufBytes = ByteBuffer.wrap(bytes); ByteBuffer bufMasks = masks == null ? null : ByteBuffer.wrap(masks); + Address minAddr = forward ? startAddr : endAddr; + Address maxAddr = forward ? endAddr : startAddr; Iterator it = - program.getAddressFactory().getAddressSet(startAddr, endAddr).iterator(forward); + program.getAddressFactory().getAddressSet(minAddr, maxAddr).iterator(forward); while (it.hasNext()) { AddressRange range = it.next(); DBTraceMemorySpace space = memoryManager.getMemorySpace(range.getAddressSpace(), false); diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/program/DBTraceProgramView.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/program/DBTraceProgramView.java index 596da292e8..86bea1494a 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/program/DBTraceProgramView.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/program/DBTraceProgramView.java @@ -110,6 +110,12 @@ public class DBTraceProgramView implements TraceProgramView { listenFor(TraceCodeChangeType.FRAGMENT_CHANGED, this::codeFragmentChanged); listenFor(TraceCodeChangeType.DATA_TYPE_REPLACED, this::codeDataTypeReplaced); + listenFor(TraceCommentChangeType.EOL_CHANGED, this::commentEolChanged); + listenFor(TraceCommentChangeType.PLATE_CHANGED, this::commentPlateChanged); + listenFor(TraceCommentChangeType.POST_CHANGED, this::commentPostChanged); + listenFor(TraceCommentChangeType.PRE_CHANGED, this::commentPreChanged); + listenFor(TraceCommentChangeType.REPEATABLE_CHANGED, this::commentRepeatableChanged); + listenFor(TraceCompositeDataChangeType.ADDED, this::compositeDataAdded); listenFor(TraceCompositeDataChangeType.LIFESPAN_CHANGED, this::compositeLifespanChanged); @@ -328,6 +334,48 @@ public class DBTraceProgramView implements TraceProgramView { range.getX1(), range.getX2(), null, null, null)); } + private void commentChanged(int docrType, TraceAddressSpace space, + TraceAddressSnapRange range, + String oldValue, String newValue) { + DomainObjectEventQueues queues = isVisible(space, range); + if (queues == null) { + return; + } + queues.fireEvent(new ProgramChangeRecord(docrType, + range.getX1(), range.getX2(), null, oldValue, newValue)); + } + + private void commentEolChanged(TraceAddressSpace space, TraceAddressSnapRange range, + String oldValue, String newValue) { + commentChanged(ChangeManager.DOCR_EOL_COMMENT_CHANGED, space, range, oldValue, + newValue); + } + + private void commentPlateChanged(TraceAddressSpace space, TraceAddressSnapRange range, + String oldValue, String newValue) { + commentChanged(ChangeManager.DOCR_PLATE_COMMENT_CHANGED, space, range, oldValue, + newValue); + } + + private void commentPostChanged(TraceAddressSpace space, TraceAddressSnapRange range, + String oldValue, String newValue) { + commentChanged(ChangeManager.DOCR_POST_COMMENT_CHANGED, space, range, oldValue, + newValue); + } + + private void commentPreChanged(TraceAddressSpace space, TraceAddressSnapRange range, + String oldValue, String newValue) { + commentChanged(ChangeManager.DOCR_PRE_COMMENT_CHANGED, space, range, oldValue, + newValue); + } + + private void commentRepeatableChanged(TraceAddressSpace space, TraceAddressSnapRange range, + String oldValue, String newValue) { + // TODO: The "repeatable" semantics are not implemented, yet. + commentChanged(ChangeManager.DOCR_REPEATABLE_COMMENT_CHANGED, space, range, oldValue, + newValue); + } + private void compositeDataAdded(TraceAddressSpace space, TraceAddressSnapRange range, TraceData oldIsNull, TraceData added) { DomainObjectEventQueues queues = isCodeVisible(space, added); diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/Trace.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/Trace.java index 328c935c62..1f0dc98802 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/Trace.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/Trace.java @@ -15,6 +15,8 @@ */ package ghidra.trace.model; +import java.util.*; + import javax.swing.ImageIcon; import com.google.common.collect.Range; @@ -106,6 +108,33 @@ public interface Trace extends DataTypeManagerDomainObject { new TraceCodeChangeType<>(); } + public static final class TraceCommentChangeType + extends DefaultTraceChangeType { + private static final Map BY_TYPE = new HashMap<>(); + + public static final TraceCommentChangeType PLATE_CHANGED = + new TraceCommentChangeType(CodeUnit.PLATE_COMMENT); + public static final TraceCommentChangeType PRE_CHANGED = + new TraceCommentChangeType(CodeUnit.PRE_COMMENT); + public static final TraceCommentChangeType POST_CHANGED = + new TraceCommentChangeType(CodeUnit.POST_COMMENT); + public static final TraceCommentChangeType EOL_CHANGED = + new TraceCommentChangeType(CodeUnit.EOL_COMMENT); + public static final TraceCommentChangeType REPEATABLE_CHANGED = + new TraceCommentChangeType(CodeUnit.REPEATABLE_COMMENT); + + public static TraceCommentChangeType byType(int type) { + return Objects.requireNonNull(BY_TYPE.get(type)); + } + + public final int type; + + private TraceCommentChangeType(int type) { + this.type = type; + BY_TYPE.put(type, this); + } + } + public static final class TraceCompositeDataChangeType extends DefaultTraceChangeType { public static final TraceCompositeDataChangeType ADDED = diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/memory/TraceMemoryOperations.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/memory/TraceMemoryOperations.java index 7e59ecfc8c..0f5bc95cab 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/memory/TraceMemoryOperations.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/memory/TraceMemoryOperations.java @@ -418,7 +418,7 @@ public interface TraceMemoryOperations { * @param snap the time to search * @param range the address range to search * @param data the values to search for - * @param mask a mask on the bits of {@code data} + * @param mask a mask on the bits of {@code data}; or null to match all bytes exactly * @param forward true to return the match with the lowest address in {@code range}, false for * the highest address. * @param monitor a monitor for progress reporting and canceling diff --git a/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/database/memory/AbstractDBTraceMemoryManagerTest.java b/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/database/memory/AbstractDBTraceMemoryManagerTest.java index 84cf80f294..fc95f97fba 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/database/memory/AbstractDBTraceMemoryManagerTest.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/database/memory/AbstractDBTraceMemoryManagerTest.java @@ -42,6 +42,7 @@ import ghidra.trace.model.TraceAddressSnapRange; import ghidra.trace.model.memory.TraceMemoryState; import ghidra.util.database.*; import ghidra.util.task.ConsoleTaskMonitor; +import ghidra.util.task.TaskMonitor; public abstract class AbstractDBTraceMemoryManagerTest extends AbstractGhidraHeadlessIntegrationTest { @@ -715,6 +716,66 @@ public abstract class AbstractDBTraceMemoryManagerTest assertArrayEquals(arr(1, 2, 3, 4), read.array()); } + @Test + public void testFindBytes() { + try (UndoableTransaction tid = UndoableTransaction.start(trace, "Testing", true)) { + assertEquals(4, memory.putBytes(3, addr(0x4000), buf(1, 2, 3, 4))); + } + + try { + memory.findBytes(3, range(0x4000, 0x4003), buf(1, 2, 3, 4), buf(-1, -1, -1), + true, TaskMonitor.DUMMY); + } + catch (IllegalArgumentException e) { + // pass + } + + // Degenerate + assertNull( + memory.findBytes(2, range(0x4000, 0x4003), buf(), buf(), + true, TaskMonitor.DUMMY)); + + // Too soon + assertNull( + memory.findBytes(2, range(0x4000, 0x4003), buf(1, 2, 3, 4), buf(-1, -1, -1, -1), + true, TaskMonitor.DUMMY)); + + // Too small + assertNull( + memory.findBytes(3, range(0x4000, 0x4002), buf(1, 2, 3, 4), buf(-1, -1, -1, -1), + true, TaskMonitor.DUMMY)); + + // Too high + assertNull( + memory.findBytes(3, range(0x4001, 0x4004), buf(1, 2, 3, 4), buf(-1, -1, -1, -1), + true, TaskMonitor.DUMMY)); + + // Too low + assertNull( + memory.findBytes(3, range(0x3fff, 0x4002), buf(1, 2, 3, 4), buf(-1, -1, -1, -1), + true, TaskMonitor.DUMMY)); + + // Perfect match + assertEquals(addr(0x4000), + memory.findBytes(3, range(0x4000, 0x4003), buf(1, 2, 3, 4), buf(-1, -1, -1, -1), + true, TaskMonitor.DUMMY)); + + // Make it work for the match + assertEquals(addr(0x4000), + memory.findBytes(3, range(0x0, -1), buf(1, 2, 3, 4), buf(-1, -1, -1, -1), + true, TaskMonitor.DUMMY)); + + // Make it work for the match + assertEquals(addr(0x4000), + memory.findBytes(3, range(0x0, -1), buf(1), buf(-1), + true, TaskMonitor.DUMMY)); + + // Sub match + assertEquals(addr(0x4001), + memory.findBytes(3, range(0x4000, 0x4003), buf(2, 3, 4), buf(-1, -1, -1), + true, TaskMonitor.DUMMY)); + } + @Test public void testRemoveBytes() { try (UndoableTransaction tid = UndoableTransaction.start(trace, "Testing", true)) { diff --git a/Ghidra/Debug/ProposedUtils/src/main/java/ghidra/util/AddressIteratorAdapter.java b/Ghidra/Debug/ProposedUtils/src/main/java/ghidra/util/AddressIteratorAdapter.java index 43b1200a18..43954807f9 100644 --- a/Ghidra/Debug/ProposedUtils/src/main/java/ghidra/util/AddressIteratorAdapter.java +++ b/Ghidra/Debug/ProposedUtils/src/main/java/ghidra/util/AddressIteratorAdapter.java @@ -89,6 +89,11 @@ public class AddressIteratorAdapter extends NestedIterator forRange(AddressRange range, boolean forward) { + return () -> forward ? new ForwardAddressIterator(range) + : new BackwardAddressIterator(range); + } + public AddressIteratorAdapter(Iterator outer, boolean forward) { super(outer, forward ? ForwardAddressIterator::new : BackwardAddressIterator::new); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/searchtext/SearchTextPlugin.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/searchtext/SearchTextPlugin.java index b650e35c97..d159f7c2ee 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/searchtext/SearchTextPlugin.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/searchtext/SearchTextPlugin.java @@ -62,8 +62,7 @@ import ghidra.util.task.*; import resources.ResourceManager; /** - * Plugin to search text as it is displayed in the fields of the - * Code Browser. + * Plugin to search text as it is displayed in the fields of the Code Browser. */ //@formatter:off @PluginInfo( @@ -114,6 +113,7 @@ public class SearchTextPlugin extends ProgramPlugin implements OptionsChangeList /** * The constructor for the SearchTextPlugin. + * * @param plugintool The tool required by this plugin. */ public SearchTextPlugin(PluginTool plugintool) { @@ -260,10 +260,7 @@ public class SearchTextPlugin extends ProgramPlugin implements OptionsChangeList } private ProgramLocation getStartLocation() { - if (currentLocation == null) { - currentLocation = navigatable.getLocation(); - } - return currentLocation; + return currentLocation = navigatable.getLocation(); } private void searchNext(Program program, Navigatable searchNavigatable, Searcher textSearcher) { @@ -475,9 +472,8 @@ public class SearchTextPlugin extends ProgramPlugin implements OptionsChangeList searchDialog.setHasSelection(context.hasSelection()); } - CodeViewerService codeViewerService = tool.getService(CodeViewerService.class); String textSelection = navigatable.getTextSelection(); - ProgramLocation location = codeViewerService.getCurrentLocation(); + ProgramLocation location = navigatable.getLocation(); Address address = location.getAddress(); Listing listing = context.getProgram().getListing(); CodeUnit codeUnit = listing.getCodeUnitAt(address); @@ -499,6 +495,7 @@ public class SearchTextPlugin extends ProgramPlugin implements OptionsChangeList /** * Get the address set for the selection. + * * @return null if there is no selection */ private AddressSetView getAddressSet(Navigatable searchNavigatable, SearchOptions options) { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/searchtext/databasesearcher/InstructionMnemonicOperandFieldSearcher.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/searchtext/databasesearcher/InstructionMnemonicOperandFieldSearcher.java index bf6ad1f9fc..99a2ea80a9 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/searchtext/databasesearcher/InstructionMnemonicOperandFieldSearcher.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/searchtext/databasesearcher/InstructionMnemonicOperandFieldSearcher.java @@ -75,7 +75,7 @@ public class InstructionMnemonicOperandFieldSearcher extends ProgramDatabaseFiel @Override protected Address advance(List currentMatches) { - Instruction instruction = iterator.next(); + Instruction instruction = iterator.hasNext() ? iterator.next() : null; Address nextAddress = null; if (instruction != null) { nextAddress = instruction.getMinAddress(); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/table/TableComponentProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/table/TableComponentProvider.java index 8476fe75ae..4fe7009972 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/table/TableComponentProvider.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/table/TableComponentProvider.java @@ -84,7 +84,7 @@ public class TableComponentProvider extends ComponentProviderAdapter this.tableServicePlugin = plugin; this.navigatable = navigatable; - this.program = plugin.getProgram(); + this.program = navigatable.getProgram(); this.model = model; this.programName = programName; this.markerService = markerService; @@ -152,7 +152,6 @@ public class TableComponentProvider extends ComponentProviderAdapter selectAction = new MakeProgramSelectionAction(tableServicePlugin, table) { @Override protected ProgramSelection makeSelection(ActionContext context) { - ProgramSelection selection = table.getProgramSelection(); navigatable.goTo(program, new ProgramLocation(program, selection.getMinAddress())); navigatable.setSelection(selection);