mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-02-09 20:20:16 +00:00
GP-432: Implementing dynamic memory search. Fixing actions to bind to focused listing.
This commit is contained in:
parent
22675e9f39
commit
fc885e7837
@ -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<S extends GeneratedMessageV3, R extends GeneratedMessageV3> {
|
||||
|
@ -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.
|
||||
*
|
||||
* <p>
|
||||
* 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 <em>not</em> 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");
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -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<DBTraceCommentEntry> {
|
||||
static final String TYPE_COLUMN_NAME = "Type";
|
||||
static final String COMMENT_COLUMN_NAME = "Comment";
|
||||
@ -89,6 +93,10 @@ public class DBTraceCommentAdapter
|
||||
void setLifespan(Range<Long> 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<TraceAddressSnapRange, String>(
|
||||
TraceCommentChangeType.byType(commentType),
|
||||
DBTraceSpaceKey.create(address.getAddressSpace(), null, 0),
|
||||
new ImmutableTraceAddressSnapRange(address, lifespan),
|
||||
oldValue, comment));
|
||||
}
|
||||
|
||||
public static String commentFromArray(String[] comment) {
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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<AddressRange> 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);
|
||||
|
@ -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);
|
||||
|
@ -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<TraceAddressSnapRange, String> {
|
||||
private static final Map<Integer, TraceCommentChangeType> 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<T, U>
|
||||
extends DefaultTraceChangeType<T, U> {
|
||||
public static final TraceCompositeDataChangeType<TraceAddressSnapRange, TraceData> ADDED =
|
||||
|
@ -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
|
||||
|
@ -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)) {
|
||||
|
@ -89,6 +89,11 @@ public class AddressIteratorAdapter extends NestedIterator<AddressRange, Address
|
||||
}
|
||||
}
|
||||
|
||||
public static Iterable<Address> forRange(AddressRange range, boolean forward) {
|
||||
return () -> forward ? new ForwardAddressIterator(range)
|
||||
: new BackwardAddressIterator(range);
|
||||
}
|
||||
|
||||
public AddressIteratorAdapter(Iterator<AddressRange> outer, boolean forward) {
|
||||
super(outer, forward ? ForwardAddressIterator::new : BackwardAddressIterator::new);
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -75,7 +75,7 @@ public class InstructionMnemonicOperandFieldSearcher extends ProgramDatabaseFiel
|
||||
|
||||
@Override
|
||||
protected Address advance(List<ProgramLocation> currentMatches) {
|
||||
Instruction instruction = iterator.next();
|
||||
Instruction instruction = iterator.hasNext() ? iterator.next() : null;
|
||||
Address nextAddress = null;
|
||||
if (instruction != null) {
|
||||
nextAddress = instruction.getMinAddress();
|
||||
|
@ -84,7 +84,7 @@ public class TableComponentProvider<T> 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<T> 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);
|
||||
|
Loading…
Reference in New Issue
Block a user