mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-03 17:59:46 +02:00
GP-5864: Add EmulatorUtilities
This commit is contained in:
parent
39c6a6db59
commit
d577925d75
43 changed files with 990 additions and 273 deletions
|
@ -28,6 +28,7 @@ import ghidra.app.plugin.core.debug.service.modules.DebuggerStaticMappingUtils;
|
|||
import ghidra.app.plugin.core.debug.service.modules.DebuggerStaticMappingUtils.Extrema;
|
||||
import ghidra.app.services.DebuggerEmulationService;
|
||||
import ghidra.framework.model.DomainFile;
|
||||
import ghidra.pcode.emu.EmulatorUtilities;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.lang.*;
|
||||
import ghidra.program.model.listing.Program;
|
||||
|
@ -142,7 +143,7 @@ public class ProgramEmulationUtils {
|
|||
EMU_SESSION_SCHEMA = EMU_CTX.getSchema(new SchemaName("EmuSession"));
|
||||
}
|
||||
|
||||
public static final String BLOCK_NAME_STACK = "STACK";
|
||||
public static final String BLOCK_NAME_STACK = EmulatorUtilities.BLOCK_NAME_STACK;
|
||||
|
||||
/**
|
||||
* Conventional prefix for first snapshot to identify "pure emulation" traces.
|
||||
|
@ -428,7 +429,7 @@ public class ProgramEmulationUtils {
|
|||
final AddressRange alloc;
|
||||
if (cSpec.stackGrowsNegative()) {
|
||||
Address max = spAddr.subtractWrap(1);
|
||||
Address min = spAddr.subtractWrapSpace(size);
|
||||
Address min = spAddr.subtractWrap(size);
|
||||
if (min.compareTo(max) > 0) {
|
||||
alloc = new AddressRangeImpl(max.getAddressSpace().getMinAddress(), max);
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ import java.util.*;
|
|||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import generic.util.MergeSortingIterator;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.trace.model.*;
|
||||
import ghidra.trace.model.Lifespan.DefaultLifeSet;
|
||||
|
@ -370,7 +371,7 @@ public class DBTraceTimeViewport implements TraceTimeViewport {
|
|||
List<Iterator<T>> iters = getOrderedSpans().stream()
|
||||
.map(rng -> iterFunc.apply(rng.lmax()))
|
||||
.collect(Collectors.toList());
|
||||
return new UniqIterator<>(new MergeSortingIterator<>(iters, comparator));
|
||||
return new DistinctIterator<>(new MergeSortingIterator<>(iters, comparator));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -19,7 +19,7 @@ import java.util.Collections;
|
|||
import java.util.Iterator;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
|
||||
import generic.NestedIterator;
|
||||
import generic.util.FlattenedIterator;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.trace.database.DBTraceUtils;
|
||||
import ghidra.trace.database.space.DBTraceDelegatingManager;
|
||||
|
@ -361,7 +361,7 @@ public abstract class AbstractBaseDBTraceCodeUnitsMemoryView<T extends DBTraceCo
|
|||
* @return an iterable of units
|
||||
*/
|
||||
public Iterable<? extends T> get(long snap, AddressSetView set, boolean forward) {
|
||||
return () -> NestedIterator.start(set.iterator(forward),
|
||||
return () -> FlattenedIterator.start(set.iterator(forward),
|
||||
r -> get(snap, r, forward).iterator());
|
||||
}
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
*/
|
||||
package ghidra.trace.database.listing;
|
||||
|
||||
import generic.NestedIterator;
|
||||
import generic.util.FlattenedIterator;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.trace.model.*;
|
||||
import ghidra.trace.model.listing.TraceBaseCodeUnitsView;
|
||||
|
@ -139,7 +139,7 @@ public abstract class AbstractBaseDBTraceCodeUnitsView<T extends DBTraceCodeUnit
|
|||
* @see TraceBaseCodeUnitsView#get(long, AddressSetView, boolean)
|
||||
*/
|
||||
public Iterable<? extends T> get(long snap, AddressSetView set, boolean forward) {
|
||||
return () -> NestedIterator.start(set.iterator(forward),
|
||||
return () -> FlattenedIterator.start(set.iterator(forward),
|
||||
r -> this.get(snap, r, forward).iterator());
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ import java.util.Collection;
|
|||
import java.util.Iterator;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
import generic.util.MergeSortingIterator;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.trace.model.TraceAddressSnapRange;
|
||||
import ghidra.trace.model.listing.TraceCodeUnit;
|
||||
|
|
|
@ -20,7 +20,8 @@ import java.util.*;
|
|||
|
||||
import org.apache.commons.collections4.IteratorUtils;
|
||||
|
||||
import generic.NestedIterator;
|
||||
import generic.util.MergeSortingIterator;
|
||||
import generic.util.FlattenedIterator;
|
||||
import ghidra.program.database.ProgramDB;
|
||||
import ghidra.program.database.code.InstructionDB;
|
||||
import ghidra.program.database.function.OverlappingFunctionException;
|
||||
|
@ -348,7 +349,7 @@ public abstract class AbstractDBTraceProgramViewListing implements TraceProgramV
|
|||
}
|
||||
// TODO: The property map doesn't heed forking.
|
||||
return new WrappingCodeUnitIterator(
|
||||
NestedIterator
|
||||
FlattenedIterator
|
||||
.start(map.getAddressSetView(Lifespan.at(program.snap)).iterator(forward),
|
||||
rng -> getTopCodeIterator(
|
||||
s -> codeOperations.codeUnits().get(s, rng, forward).iterator(),
|
||||
|
@ -371,7 +372,7 @@ public abstract class AbstractDBTraceProgramViewListing implements TraceProgramV
|
|||
}
|
||||
// TODO: The property map doesn't heed forking.
|
||||
return new WrappingCodeUnitIterator(
|
||||
NestedIterator
|
||||
FlattenedIterator
|
||||
.start(map.getAddressSetView(Lifespan.at(program.snap)).iterator(addr, forward),
|
||||
rng -> getTopCodeIterator(
|
||||
s -> codeOperations.codeUnits().get(s, rng, forward).iterator(),
|
||||
|
@ -394,7 +395,7 @@ public abstract class AbstractDBTraceProgramViewListing implements TraceProgramV
|
|||
return new WrappingCodeUnitIterator(Collections.emptyIterator());
|
||||
}
|
||||
// TODO: The property map doesn't heed forking.
|
||||
return new WrappingCodeUnitIterator(NestedIterator.start(
|
||||
return new WrappingCodeUnitIterator(FlattenedIterator.start(
|
||||
new IntersectionAddressSetView(map.getAddressSetView(Lifespan.at(program.snap)),
|
||||
addrSet).iterator(forward),
|
||||
rng -> getTopCodeIterator(
|
||||
|
|
|
@ -23,7 +23,7 @@ import java.util.function.Predicate;
|
|||
|
||||
import javax.help.UnsupportedOperationException;
|
||||
|
||||
import generic.NestedIterator;
|
||||
import generic.util.FlattenedIterator;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.lang.Register;
|
||||
import ghidra.program.model.listing.Variable;
|
||||
|
@ -242,7 +242,7 @@ public abstract class AbstractDBTraceProgramViewReferenceManager implements Refe
|
|||
protected Iterator<Reference> getReferenceIteratorForSnap(long snap, Address startAddr) {
|
||||
AddressIterator addresses =
|
||||
refs.getReferenceSources(Lifespan.at(snap)).getAddresses(startAddr, true);
|
||||
return NestedIterator.start(addresses, a -> {
|
||||
return FlattenedIterator.start(addresses, a -> {
|
||||
return refs.getReferencesFrom(snap, a).iterator();
|
||||
});
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ import javax.swing.Icon;
|
|||
|
||||
import org.apache.commons.collections4.IteratorUtils;
|
||||
|
||||
import generic.NestedIterator;
|
||||
import generic.util.FlattenedIterator;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.listing.Bookmark;
|
||||
import ghidra.program.model.listing.BookmarkType;
|
||||
|
@ -310,7 +310,7 @@ public class DBTraceProgramViewBookmarkManager implements TraceProgramViewBookma
|
|||
@Override
|
||||
public Iterator<Bookmark> getBookmarksIterator() {
|
||||
// TODO: This seems terribly inefficient. We'll have to see how/when it's used.
|
||||
return NestedIterator.start(bookmarkManager.getActiveSpaces().iterator(),
|
||||
return FlattenedIterator.start(bookmarkManager.getActiveSpaces().iterator(),
|
||||
space -> filteredIterator(space.getAllBookmarks().iterator(),
|
||||
bm -> program.viewport.containsAnyUpper(bm.getLifespan())));
|
||||
}
|
||||
|
@ -327,7 +327,7 @@ public class DBTraceProgramViewBookmarkManager implements TraceProgramViewBookma
|
|||
AddressSet allMemory = factory.getAddressSet();
|
||||
AddressSet within = forward ? factory.getAddressSet(startAddress, allMemory.getMaxAddress())
|
||||
: factory.getAddressSet(allMemory.getMinAddress(), startAddress);
|
||||
return NestedIterator.start(within.iterator(forward), rng -> {
|
||||
return FlattenedIterator.start(within.iterator(forward), rng -> {
|
||||
DBTraceBookmarkSpace space =
|
||||
bookmarkManager.getBookmarkSpace(rng.getAddressSpace(), false);
|
||||
if (space == null) {
|
||||
|
|
|
@ -20,7 +20,7 @@ import java.util.*;
|
|||
import java.util.Map.Entry;
|
||||
import java.util.function.Function;
|
||||
|
||||
import generic.NestedIterator;
|
||||
import generic.util.FlattenedIterator;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.lang.*;
|
||||
import ghidra.program.model.listing.ContextChangeException;
|
||||
|
@ -110,7 +110,7 @@ public class DBTraceProgramViewProgramContext extends AbstractProgramContext {
|
|||
setRegisterValue(start, end, new RegisterValue(register, value));
|
||||
}
|
||||
|
||||
private static class NestedAddressRangeIterator<U> extends NestedIterator<U, AddressRange>
|
||||
private static class NestedAddressRangeIterator<U> extends FlattenedIterator<U, AddressRange>
|
||||
implements AddressRangeIterator {
|
||||
protected NestedAddressRangeIterator(Iterator<U> it,
|
||||
Function<U, Iterator<? extends AddressRange>> f) {
|
||||
|
|
|
@ -18,8 +18,7 @@ package ghidra.trace.database.program;
|
|||
import java.util.*;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
import generic.NestedIterator;
|
||||
import generic.util.PeekableIterator;
|
||||
import generic.util.*;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.program.model.symbol.*;
|
||||
|
@ -342,7 +341,7 @@ public class DBTraceProgramViewSymbolTable implements SymbolTable {
|
|||
|
||||
@Override
|
||||
public SymbolIterator getSymbols(AddressSetView set, SymbolType type, boolean forward) {
|
||||
return new SymbolIteratorAdapter(NestedIterator.start(set.iterator(), range -> {
|
||||
return new SymbolIteratorAdapter(FlattenedIterator.start(set.iterator(), range -> {
|
||||
if (range.getAddressSpace().isMemorySpace()) {
|
||||
if (type == SymbolType.LABEL) {
|
||||
return symbolManager.labels()
|
||||
|
@ -426,7 +425,7 @@ public class DBTraceProgramViewSymbolTable implements SymbolTable {
|
|||
|
||||
@Override
|
||||
public SymbolIterator getPrimarySymbolIterator(AddressSetView asv, boolean forward) {
|
||||
return new PrimarySymbolIterator(NestedIterator.start(asv.iterator(forward),
|
||||
return new PrimarySymbolIterator(FlattenedIterator.start(asv.iterator(forward),
|
||||
range -> symbolManager.labels()
|
||||
.getIntersecting(Lifespan.at(program.snap), range, true, forward)
|
||||
.iterator()));
|
||||
|
|
|
@ -19,7 +19,7 @@ import java.util.*;
|
|||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.function.*;
|
||||
|
||||
import generic.NestedIterator;
|
||||
import generic.util.FlattenedIterator;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.util.LockHold;
|
||||
|
||||
|
@ -191,7 +191,7 @@ public interface DBTraceDelegatingManager<M> {
|
|||
return new AbstractCollection<>() {
|
||||
@Override
|
||||
public Iterator<T> iterator() {
|
||||
return NestedIterator.start(spaces.iterator(), func.andThen(Iterable::iterator));
|
||||
return FlattenedIterator.start(spaces.iterator(), func.andThen(Iterable::iterator));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -19,7 +19,7 @@ import java.io.IOException;
|
|||
import java.util.concurrent.locks.ReadWriteLock;
|
||||
|
||||
import db.DBHandle;
|
||||
import generic.NestedIterator;
|
||||
import generic.util.FlattenedIterator;
|
||||
import ghidra.framework.data.OpenMode;
|
||||
import ghidra.program.model.address.AddressSetView;
|
||||
import ghidra.trace.database.DBTrace;
|
||||
|
@ -119,7 +119,7 @@ public class DBTraceStackManager implements TraceStackManager, DBTraceManager {
|
|||
@Override
|
||||
// TODO: Should probably include a lifespan parameter?
|
||||
public Iterable<TraceStackFrame> getFramesIn(AddressSetView set) {
|
||||
return () -> NestedIterator.start(set.iterator(),
|
||||
return () -> FlattenedIterator.start(set.iterator(),
|
||||
rng -> trace.getObjectManager()
|
||||
.getObjectsIntersecting(Lifespan.ALL, rng, TraceStackFrame.KEY_PC,
|
||||
TraceStackFrame.class)
|
||||
|
|
|
@ -18,9 +18,9 @@ package ghidra.trace.database.symbol;
|
|||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import generic.util.MergeSortingIterator;
|
||||
import ghidra.trace.model.symbol.*;
|
||||
import ghidra.util.LazyCollection;
|
||||
import ghidra.util.MergeSortingIterator;
|
||||
|
||||
public class DBTraceSymbolMultipleTypesView<T extends AbstractDBTraceSymbol>
|
||||
implements TraceSymbolView<T> {
|
||||
|
|
|
@ -20,11 +20,11 @@ import java.util.Map.Entry;
|
|||
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import generic.util.AbstractPeekableIterator;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressRange;
|
||||
import ghidra.program.model.listing.CodeUnit;
|
||||
import ghidra.trace.model.TraceAddressSnapRange;
|
||||
import ghidra.util.AbstractPeekableIterator;
|
||||
|
||||
/**
|
||||
* An iterator of overlapping objects return from two given iterators.
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
package ghidra.trace.util;
|
||||
|
||||
import generic.util.AbstractPeekableIterator;
|
||||
import ghidra.trace.model.Lifespan;
|
||||
import ghidra.trace.model.Lifespan.DefaultLifeSet;
|
||||
import ghidra.trace.model.Lifespan.MutableLifeSet;
|
||||
|
@ -22,7 +23,6 @@ import ghidra.trace.model.Trace;
|
|||
import ghidra.trace.model.time.TraceSnapshot;
|
||||
import ghidra.trace.model.time.TraceTimeManager;
|
||||
import ghidra.trace.model.time.schedule.TraceSchedule;
|
||||
import ghidra.util.AbstractPeekableIterator;
|
||||
|
||||
public class TraceViewportSpanIterator extends AbstractPeekableIterator<Lifespan> {
|
||||
private final TraceTimeManager timeManager;
|
||||
|
|
|
@ -20,7 +20,7 @@ import static ghidra.util.MathUtilities.cmin;
|
|||
|
||||
import java.util.Iterator;
|
||||
|
||||
import ghidra.util.AbstractPeekableIterator;
|
||||
import generic.util.AbstractPeekableIterator;
|
||||
import ghidra.util.AddressIteratorAdapter;
|
||||
|
||||
/**
|
||||
|
|
|
@ -18,23 +18,24 @@ package ghidra.util;
|
|||
import java.util.Iterator;
|
||||
import java.util.Objects;
|
||||
|
||||
import generic.util.AbstractPeekableIterator;
|
||||
import generic.util.PeekableIterator;
|
||||
import generic.util.PeekableIterators;
|
||||
|
||||
/**
|
||||
* A filtering iterator which removes repeated objects
|
||||
*
|
||||
* <p>
|
||||
* This operates in style to the uniq command on UNIX, which only removes immediate repeats. To
|
||||
* obtain a truly unique iteration, the wrapped iterator must visit elements in sorted order.
|
||||
* This iterator only removes immediate repeats (similar to the uniq command on UNIX). To obtain a
|
||||
* truly distinct iteration, the wrapped iterator must visit elements in sorted order.
|
||||
*
|
||||
* @param <T> the type of elements
|
||||
*/
|
||||
public class UniqIterator<T> extends AbstractPeekableIterator<T> {
|
||||
public class DistinctIterator<T> extends AbstractPeekableIterator<T> {
|
||||
protected boolean first;
|
||||
protected T last;
|
||||
protected final PeekableIterator<T> wrapped;
|
||||
|
||||
public UniqIterator(Iterator<T> wrapped) {
|
||||
public DistinctIterator(Iterator<T> wrapped) {
|
||||
this.wrapped = PeekableIterators.castOrWrap(wrapped);
|
||||
}
|
||||
|
|
@ -22,7 +22,7 @@ import java.util.function.Consumer;
|
|||
import java.util.stream.Stream;
|
||||
|
||||
import db.DBRecord;
|
||||
import generic.NestedIterator;
|
||||
import generic.util.FlattenedIterator;
|
||||
import generic.util.PeekableIterator;
|
||||
import ghidra.util.LockHold;
|
||||
import ghidra.util.database.*;
|
||||
|
@ -289,7 +289,7 @@ public abstract class AbstractConstraintsTree< //
|
|||
}
|
||||
nodes.add(n);
|
||||
}
|
||||
return NestedIterator.start(nodes.iterator(), n -> iterator(n, query));
|
||||
return FlattenedIterator.start(nodes.iterator(), n -> iterator(n, query));
|
||||
}
|
||||
|
||||
protected Iterator<DR> orderedIterator(Q query) {
|
||||
|
|
|
@ -0,0 +1,280 @@
|
|||
/* ###
|
||||
* 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.pcode.emu;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.Arrays;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
import ghidra.pcode.exec.PcodeArithmetic;
|
||||
import ghidra.pcode.exec.PcodeExecutorState;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.lang.*;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.program.model.mem.MemoryAccessException;
|
||||
import ghidra.program.model.mem.MemoryBlock;
|
||||
import ghidra.util.DifferenceAddressSetView;
|
||||
import ghidra.util.Msg;
|
||||
|
||||
/**
|
||||
* Utilities for working with plain emulators (not trace- or debugger-bound) and programs in
|
||||
* scripts.
|
||||
*/
|
||||
public enum EmulatorUtilities {
|
||||
;
|
||||
/**
|
||||
* The conventional name of the memory block used to specify the location of the stack. This
|
||||
* should only be the case for single-threaded emulation.
|
||||
*/
|
||||
public static final String BLOCK_NAME_STACK = "STACK";
|
||||
|
||||
/**
|
||||
* The default block size used to copy program bytes into the emulator
|
||||
*/
|
||||
public static final int DEFAULT_BLOCK_SIZE = 4096;
|
||||
/**
|
||||
* The default max size to assume for the stack
|
||||
*/
|
||||
public static final int DEFAULT_STACK_SIZE = 0x4000;
|
||||
/**
|
||||
* These utilities will avoid choosing a stack range lower than this bound, as most platforms
|
||||
* will never map this page (even in kernel space) so that 0-valued pointers are never valid.
|
||||
*/
|
||||
public static final long PAGE_ZERO_END = 0x1000;
|
||||
|
||||
/**
|
||||
* Copy the bytes from the given program into the given emulator's memory.
|
||||
*
|
||||
* <p>
|
||||
* This copies each initialized block of memory from the given program into the emulator's
|
||||
* shared machine state. Because the machine can have memory of any given type, it will use the
|
||||
* machine's arithmetic to create values from the program's concrete data. Data is copied in
|
||||
* blocks of the given size, which can be tweaked for performance. The default value, used by
|
||||
* {@link #loadProgram(PcodeMachine, Program)} is {@value #DEFAULT_BLOCK_SIZE}.
|
||||
*
|
||||
* @param <T> the type of values used by the emulator
|
||||
* @param machine the emulator whose memory to initialize
|
||||
* @param program the program whose bytes should be copied into the emulator
|
||||
* @param blockSize the size of the temporary buffer used for copying
|
||||
* @throws MemoryAccessException if the program's memory cannot be read
|
||||
*/
|
||||
public static <T> void loadProgram(PcodeMachine<T> machine, Program program, int blockSize)
|
||||
throws MemoryAccessException {
|
||||
byte[] buf = new byte[blockSize];
|
||||
PcodeExecutorState<T> state = machine.getSharedState();
|
||||
for (MemoryBlock block : program.getMemory().getBlocks()) {
|
||||
if (!block.isInitialized()) {
|
||||
continue;
|
||||
}
|
||||
for (AddressRange rng : new AddressRangeChunker(block.getAddressRange(),
|
||||
buf.length)) {
|
||||
int len = block.getBytes(rng.getMinAddress(), buf);
|
||||
state.setConcrete(rng.getMinAddress(),
|
||||
len == buf.length ? buf : Arrays.copyOf(buf, len));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy the bytes from the given program into the given emulator's memory.
|
||||
*
|
||||
* @see #loadProgram(PcodeMachine, Program, int)
|
||||
* @param machine the emulator whose memory to initialize
|
||||
* @param program the program whose bytes should be copied into the emulator
|
||||
* @throws MemoryAccessException if the program's memory cannot be read
|
||||
*/
|
||||
public static void loadProgram(PcodeMachine<?> machine, Program program)
|
||||
throws MemoryAccessException {
|
||||
loadProgram(machine, program, DEFAULT_BLOCK_SIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Choose an assumed stack range by examining the entry point for a contextual value of the
|
||||
* stack pointer.
|
||||
*
|
||||
* @param program the program whose context to examine
|
||||
* @param entry the entry point where context should be examined
|
||||
* @param stackSize the assumed max size of the stack
|
||||
* @return the range assumed to be reserved for the stack, or null if no stack pointer value is
|
||||
* in the context
|
||||
*/
|
||||
public static AddressRange chooseStackRangeFromContext(Program program, Address entry,
|
||||
int stackSize) {
|
||||
ProgramContext ctx = program.getProgramContext();
|
||||
CompilerSpec cSpec = program.getCompilerSpec();
|
||||
Register sp = cSpec.getStackPointer();
|
||||
RegisterValue spVal = ctx.getRegisterValue(sp, entry);
|
||||
if (spVal == null || !spVal.hasValue()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Address spAddr = cSpec.getStackBaseSpace().getAddress(spVal.getUnsignedValue().longValue());
|
||||
if (cSpec.stackGrowsNegative()) {
|
||||
Address max = spAddr.subtractWrap(1);
|
||||
Address min = spAddr.subtractWrap(stackSize);
|
||||
if (min.compareTo(max) > 0) {
|
||||
return new AddressRangeImpl(max.getAddressSpace().getMinAddress(), max);
|
||||
}
|
||||
return new AddressRangeImpl(min, max);
|
||||
}
|
||||
// Grows positive
|
||||
Address min = spAddr;
|
||||
Address max = spAddr.addWrap(stackSize - 1);
|
||||
if (min.compareTo(max) > 0) {
|
||||
return new AddressRangeImpl(min, min.getAddressSpace().getMaxAddress());
|
||||
}
|
||||
return new AddressRangeImpl(min, max);
|
||||
}
|
||||
|
||||
/**
|
||||
* Choose an assumed stack range by examining the program's memory map for a
|
||||
* {@value #BLOCK_NAME_STACK} block.
|
||||
*
|
||||
* @param program the program to examine
|
||||
* @return the range assumed to be reserved for the stack, or null if no
|
||||
* {@value #BLOCK_NAME_STACK} block is found.
|
||||
*/
|
||||
public static AddressRange chooseStackRangeFromBlock(Program program) {
|
||||
AddressSpace space = program.getCompilerSpec().getStackBaseSpace();
|
||||
MemoryBlock stackBlock = program.getMemory().getBlock(BLOCK_NAME_STACK);
|
||||
if (stackBlock == null) {
|
||||
return null;
|
||||
}
|
||||
if (space != stackBlock.getStart().getAddressSpace().getPhysicalSpace()) {
|
||||
Msg.showError(EmulatorUtilities.class, null, "Invalid STACK block",
|
||||
"The STACK block must be in the stack's base space. Ignoring.");
|
||||
return null;
|
||||
}
|
||||
return new AddressRangeImpl(
|
||||
stackBlock.getStart().getPhysicalAddress(),
|
||||
stackBlock.getEnd().getPhysicalAddress());
|
||||
}
|
||||
|
||||
/**
|
||||
* Choose an assumed stack range
|
||||
*
|
||||
* <p>
|
||||
* This will first examine the entry point's context for a stack pointer value using
|
||||
* {@link #chooseStackRangeFromContext(Program, Address, int)}. Then, it will examine the
|
||||
* progam's memory map using {@link #chooseStackRangeFromBlock(Program)}. Finally, it will
|
||||
* search for a slack address range of the requested size. That is, it seeks a range that does
|
||||
* not intersect any existing memory block. If possible, this will avoid choosing a stack range
|
||||
* that intersects [0, 4096), so that 0-valued pointers are in fact invalid.
|
||||
*
|
||||
* <p>
|
||||
* Note that a stack is not formally "allocated." Instead, the range is used to initialize a
|
||||
* thread's stack pointer. Unless instrumentation is added to detect a stack overflow, nothing
|
||||
* really prevents the program from exceeding the returned range. Thus, {@code stackSize} should
|
||||
* be large enough to accommodate the target. Additionally, the user or client code should be
|
||||
* prepared for undefined behavior caused by an unmitigated stack overflow.
|
||||
*
|
||||
* @param program the program
|
||||
* @param entry the entry point, in case context there defines an initial stack pointer
|
||||
* @param stackSize the maximum expected size of the stack
|
||||
* @return the chosen range assumed to be used for the stack
|
||||
*/
|
||||
public static AddressRange chooseStackRange(Program program, Address entry, int stackSize) {
|
||||
AddressRange customByContext = chooseStackRangeFromContext(program, entry, stackSize);
|
||||
if (customByContext != null) {
|
||||
return customByContext;
|
||||
}
|
||||
AddressRange customByBlock = chooseStackRangeFromBlock(program);
|
||||
if (customByBlock != null) {
|
||||
return customByBlock;
|
||||
}
|
||||
// Search for a range of the given size outside any block
|
||||
AddressSpace space = program.getCompilerSpec().getStackBaseSpace();
|
||||
Address max = space.getMaxAddress();
|
||||
AddressSet eligible;
|
||||
if (max.getOffsetAsBigInteger().compareTo(BigInteger.valueOf(0x1000)) < 0) {
|
||||
eligible = new AddressSet(space.getMinAddress(), max);
|
||||
}
|
||||
else {
|
||||
eligible = new AddressSet(space.getAddress(0x1000), max);
|
||||
}
|
||||
|
||||
AddressSetView left = new DifferenceAddressSetView(eligible, program.getMemory());
|
||||
for (AddressRange candidate : left) {
|
||||
if (Long.compareUnsigned(candidate.getLength(), stackSize) >= 0) {
|
||||
try {
|
||||
return new AddressRangeImpl(candidate.getMinAddress(), stackSize);
|
||||
}
|
||||
catch (AddressOverflowException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
|
||||
/**
|
||||
* Choose an assumed stack range of size {@value #DEFAULT_STACK_SIZE}
|
||||
*
|
||||
* @see #chooseStackRange(Program, Address, int)
|
||||
* @param program the program
|
||||
* @param entry the entry point, in case context there defines an initial stack pointer
|
||||
* @return the chosen range assumed to be used for the stack
|
||||
*/
|
||||
public static AddressRange chooseStackRange(Program program, Address entry) {
|
||||
return chooseStackRange(program, entry, DEFAULT_STACK_SIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare a thread to emulate a given function
|
||||
*
|
||||
* @param <T> the type of values in the emulator
|
||||
* @param thread the thread whose state to initialize
|
||||
* @param function the function to prepare to enter
|
||||
* @param stackSize the maximum expected size of the stack
|
||||
*/
|
||||
public static <T> void initializeForFunction(PcodeThread<T> thread, Function function,
|
||||
int stackSize) {
|
||||
PcodeArithmetic<T> arithmetic = thread.getArithmetic();
|
||||
|
||||
Program program = function.getProgram();
|
||||
Address entry = function.getEntryPoint();
|
||||
CompilerSpec cSpec = program.getCompilerSpec();
|
||||
Register sp = cSpec.getStackPointer();
|
||||
ThreadPcodeExecutorState<T> state = thread.getState();
|
||||
|
||||
ProgramProcessorContext ctx =
|
||||
new ProgramProcessorContext(program.getProgramContext(), entry);
|
||||
for (Register reg : ctx.getRegisters()) {
|
||||
if (!reg.isBaseRegister()) {
|
||||
continue;
|
||||
}
|
||||
RegisterValue rv = ctx.getRegisterValue(reg);
|
||||
if (rv == null || !rv.hasAnyValue()) {
|
||||
continue;
|
||||
}
|
||||
/**
|
||||
* NOTE: In theory, there's no need to combine masked values, if this is a fresh
|
||||
* emulator. If I had to guess, the client would want their values to take precedence,
|
||||
* so they should overwrite the values after calling this method. Combining can be
|
||||
* problematic, because the emulator could return some abstraction for the current
|
||||
* value.
|
||||
*/
|
||||
state.setRegisterValue(rv);
|
||||
}
|
||||
|
||||
AddressRange stack = chooseStackRange(program, entry);
|
||||
long stackOffset = cSpec.stackGrowsNegative() ? stack.getMaxAddress().getOffset() + 1
|
||||
: stack.getMinAddress().getOffset();
|
||||
state.setVar(sp, arithmetic.fromConst(stackOffset, sp.getMinimumByteSize()));
|
||||
|
||||
thread.overrideCounter(entry);
|
||||
}
|
||||
}
|
|
@ -15,12 +15,12 @@
|
|||
*/
|
||||
package ghidra.pcode.exec;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.Map;
|
||||
|
||||
import ghidra.pcode.exec.PcodeArithmetic.Purpose;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.lang.Language;
|
||||
import ghidra.program.model.lang.Register;
|
||||
import ghidra.program.model.lang.*;
|
||||
import ghidra.program.model.mem.MemBuffer;
|
||||
import ghidra.program.model.pcode.Varnode;
|
||||
|
||||
|
@ -277,4 +277,175 @@ public interface PcodeExecutorStatePiece<A, T> {
|
|||
* dark.
|
||||
*/
|
||||
void clear();
|
||||
|
||||
/**
|
||||
* Convenience to set a variable to a concrete value
|
||||
*
|
||||
* @param address the address in memory
|
||||
* @param value the value
|
||||
*/
|
||||
default void setConcrete(Address address, byte[] value) {
|
||||
setVar(address, value.length, false, getArithmetic().fromConst(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience to inspect the concrete value of a variable
|
||||
*
|
||||
* @param address the address in memory
|
||||
* @param size the number of bytes to inspect
|
||||
* @return the value
|
||||
* @throws ConcretionError if the value cannot be made concrete
|
||||
*/
|
||||
default byte[] inspectConcrete(Address address, int size) {
|
||||
return getArithmetic().toConcrete(getVar(address, size, false, Reason.INSPECT),
|
||||
Purpose.INSPECT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience to set a variable to a concrete value as a {@link BigInteger}
|
||||
*
|
||||
* @param address the address is memory
|
||||
* @param size the size of the variable (in bytes)
|
||||
* @param value the value
|
||||
*/
|
||||
default void setBigInteger(Address address, int size, BigInteger value) {
|
||||
setVar(address, size, false, getArithmetic().fromConst(value, size));
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience to inspect the concrete value of a variable as a {@link BigInteger}
|
||||
*
|
||||
* @param address the address in memory
|
||||
* @param size the number of bytes to inspect
|
||||
* @return the value
|
||||
* @throws ConcretionError if the value cannot be made concrete
|
||||
*/
|
||||
default BigInteger inspectBigInteger(Address address, int size) {
|
||||
return getArithmetic().toBigInteger(getVar(address, size, false, Reason.INSPECT),
|
||||
Purpose.INSPECT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience to set a variable to a concrete value as a {@code long}
|
||||
*
|
||||
* @param address the address is memory
|
||||
* @param value the value
|
||||
*/
|
||||
default void setLong(Address address, long value) {
|
||||
setVar(address, Long.BYTES, false, getArithmetic().fromConst(value, Long.BYTES));
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience to inspect the concrete value of a variable as a {@code long}
|
||||
*
|
||||
* @param address the address in memory
|
||||
* @return the value
|
||||
* @throws ConcretionError if the value cannot be made concrete
|
||||
*/
|
||||
default long inspectLong(Address address) {
|
||||
return getArithmetic().toLong(getVar(address, Long.BYTES, false, Reason.INSPECT),
|
||||
Purpose.INSPECT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience to set a variable to a concrete value as an {@code int}
|
||||
*
|
||||
* @param address the address is memory
|
||||
* @param value the value
|
||||
*/
|
||||
default void setInt(Address address, int value) {
|
||||
setVar(address, Integer.BYTES, false, getArithmetic().fromConst(value, Integer.BYTES));
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience to inspect the concrete value of a variable as an {@code int}
|
||||
*
|
||||
* @param address the address in memory
|
||||
* @return the value
|
||||
* @throws ConcretionError if the value cannot be made concrete
|
||||
*/
|
||||
default int inspectInt(Address address) {
|
||||
return (int) getArithmetic().toLong(getVar(address, Integer.BYTES, false, Reason.INSPECT),
|
||||
Purpose.INSPECT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience to set a variable to a concrete value as a {@code short}
|
||||
*
|
||||
* @param address the address is memory
|
||||
* @param value the value
|
||||
*/
|
||||
default void setShort(Address address, short value) {
|
||||
setVar(address, Short.BYTES, false, getArithmetic().fromConst(value, Short.BYTES));
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience to inspect the concrete value of a variable as a {@code short}
|
||||
*
|
||||
* @param address the address in memory
|
||||
* @return the value
|
||||
* @throws ConcretionError if the value cannot be made concrete
|
||||
*/
|
||||
default short inspectShort(Address address) {
|
||||
return (short) getArithmetic().toLong(getVar(address, Short.BYTES, false, Reason.INSPECT),
|
||||
Purpose.INSPECT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience to set a variable to a concrete value as a {@code byte}
|
||||
*
|
||||
* @param address the address is memory
|
||||
* @param value the value
|
||||
*/
|
||||
default void setByte(Address address, byte value) {
|
||||
setVar(address, Byte.BYTES, false, getArithmetic().fromConst(value, Byte.BYTES));
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience to inspect the concrete value of a variable as a {@code byte}
|
||||
*
|
||||
* @param address the address in memory
|
||||
* @return the value
|
||||
* @throws ConcretionError if the value cannot be made concrete
|
||||
*/
|
||||
default byte inspectByte(Address address) {
|
||||
return (byte) getArithmetic().toLong(getVar(address, Byte.BYTES, false, Reason.INSPECT),
|
||||
Purpose.INSPECT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience to set a register variable to a concrete value as a {@link RegisterValue}
|
||||
*
|
||||
* <p>
|
||||
* <b>NOTE:</b> The register from the given value does not have to match the given register, but
|
||||
* their <em>sizes</em> should at least match. This permits simpler moving of values from one
|
||||
* register to another. If the sizes do not match, the behavior is undefined.
|
||||
*
|
||||
* @param register the register
|
||||
* @param value the value
|
||||
*/
|
||||
default void setRegisterValue(Register register, RegisterValue value) {
|
||||
setVar(register, getArithmetic().fromConst(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience to set a register variable to a concrete value as a {@link RegisterValue}
|
||||
*
|
||||
* @param value the value
|
||||
*/
|
||||
default void setRegisterValue(RegisterValue value) {
|
||||
setRegisterValue(value.getRegister(), value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience to inspect the concrete value of a register variable as a {@link RegisterValue}
|
||||
*
|
||||
* @param register the register
|
||||
* @return the value
|
||||
* @throws ConcretionError if the value cannot be made concrete
|
||||
*/
|
||||
default RegisterValue inspectRegisterValue(Register register) {
|
||||
return getArithmetic().toRegisterValue(register, getVar(register, Reason.INSPECT),
|
||||
Purpose.INSPECT);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,16 +13,28 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.util;
|
||||
package generic.util;
|
||||
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
import generic.util.PeekableIterator;
|
||||
|
||||
/**
|
||||
* An implementation of {@link PeekableIterator} that only requires a way to seek out the next
|
||||
* element. This will keep that element in hand until the next element is actually requested by the
|
||||
* client. This does not invoke the search until the next element is required, either because the
|
||||
* client called next or else wants to peek at it.
|
||||
*
|
||||
* @param <T> the type of elements
|
||||
*/
|
||||
public abstract class AbstractPeekableIterator<T> implements PeekableIterator<T> {
|
||||
protected T next = null;
|
||||
protected boolean soughtNext = false;
|
||||
|
||||
/**
|
||||
* Find the next element in this iterator, because the client called either {@link #next} or
|
||||
* {@link #peek()}.
|
||||
*
|
||||
* @return the next element
|
||||
*/
|
||||
protected abstract T seekNext();
|
||||
|
||||
private void checkSeekNext() {
|
||||
|
@ -52,5 +64,4 @@ public abstract class AbstractPeekableIterator<T> implements PeekableIterator<T>
|
|||
}
|
||||
return next;
|
||||
}
|
||||
|
||||
}
|
|
@ -13,25 +13,36 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package generic;
|
||||
package generic.util;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* TODO Document me
|
||||
* Given an "outer" iterator and a mapping from its elements to "inner" iterators, this is a
|
||||
* flattened iterator over elements from the inner iterators.
|
||||
*
|
||||
* Note the innerFactory may return null to skip an outer element.
|
||||
*
|
||||
* TODO: Test innerFactory returning null.
|
||||
*
|
||||
* @param <O>
|
||||
* @param <I>
|
||||
* @param <O> the type of elements in the outer iterator
|
||||
* @param <I> the type of elements in the inner and flattened iterators
|
||||
*/
|
||||
public class FlattenedIterator<O, I> implements Iterator<I> {
|
||||
/**
|
||||
* Create a flattened iterator
|
||||
* <p>
|
||||
* This iterates over each element of {@code outer} and applies the given {@code innerFactory}
|
||||
* to generate an "inner" iterator. The returned iterator will produce elements from the inner
|
||||
* iterators as if concatentated. This is essentially a flat-map operation on iterators. Note
|
||||
* the {@code innerFactory} may return null to skip an outer element.
|
||||
*
|
||||
* @param <O> the type of elements in the outer iterator
|
||||
* @param <I> the type of elements in the inner and flattened iterators
|
||||
* @param outer the outer iterator
|
||||
* @param innerFactory a mapping from outer elements to inner iterators
|
||||
* @return the flattened iterator
|
||||
*/
|
||||
public class NestedIterator<O, I> implements Iterator<I> {
|
||||
public static <O, I> Iterator<I> start(Iterator<O> outer,
|
||||
Function<O, Iterator<? extends I>> innerFactory) {
|
||||
return new NestedIterator<>(outer, innerFactory);
|
||||
return new FlattenedIterator<>(outer, innerFactory);
|
||||
}
|
||||
|
||||
protected final Iterator<O> outer;
|
||||
|
@ -40,7 +51,8 @@ public class NestedIterator<O, I> implements Iterator<I> {
|
|||
protected Iterator<? extends I> inner;
|
||||
protected Iterator<? extends I> preppedInner;
|
||||
|
||||
protected NestedIterator(Iterator<O> outer, Function<O, Iterator<? extends I>> innerFactory) {
|
||||
protected FlattenedIterator(Iterator<O> outer,
|
||||
Function<O, Iterator<? extends I>> innerFactory) {
|
||||
this.outer = outer;
|
||||
this.innerFactory = innerFactory;
|
||||
}
|
|
@ -13,12 +13,15 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.util;
|
||||
package generic.util;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import generic.util.PeekableIterator;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.PriorityQueue;
|
||||
|
||||
/**
|
||||
* An iterator which merges sorted iterators according to a comparator
|
||||
|
@ -87,13 +90,16 @@ public class MergeSortingIterator<T> implements PeekableIterator<T> {
|
|||
|
||||
/**
|
||||
* Construct a merge-sorting iterator which generates labeled values
|
||||
*
|
||||
* <p>
|
||||
* The map of iterators is a map of entries, each giving a label and an iterator to be merged.
|
||||
* Each iterator must return values as sorted by the given comparator. The entries returned by
|
||||
* the combined iterator give the values in sorted order, but each has a the key indicating
|
||||
* which given iterator returned that value. Note that the returned entry may be re-used by the
|
||||
* underlying implementation, so users needing to keep the entry should create a copy.
|
||||
* <p>
|
||||
* The purpose of the iterator is to know which iterator provided a given entry in the merged
|
||||
* result. While this has general utility, at the moment, it is only used in our tests to verify
|
||||
* proper operation of the merge-sorting implementation.
|
||||
*
|
||||
* @param iterMap a map of labeled iterators
|
||||
* @param comparator the comparator of values
|
||||
|
@ -110,6 +116,12 @@ public class MergeSortingIterator<T> implements PeekableIterator<T> {
|
|||
protected final Comparator<? super T> comparator;
|
||||
protected final PriorityQueue<PeekableIterator<? extends T>> queue;
|
||||
|
||||
/**
|
||||
* Construct a merge sorting iterator
|
||||
*
|
||||
* @param iterators a collection of iterators to merge
|
||||
* @param comparator the comparator defining how the input and output iterators are sorted
|
||||
*/
|
||||
public MergeSortingIterator(Iterable<? extends Iterator<? extends T>> iterators,
|
||||
Comparator<? super T> comparator) {
|
||||
this.comparator = comparator;
|
|
@ -26,10 +26,11 @@ import java.util.NoSuchElementException;
|
|||
public interface PeekableIterator<T> extends Iterator<T> {
|
||||
|
||||
/**
|
||||
* Returns the item that would be returned by calling {@link #next()}, but does not
|
||||
* increment the iterator as <code>next</code> would.
|
||||
* Returns the item that would be returned by calling {@link #next()}, but without incrementing
|
||||
* the iterator.
|
||||
*
|
||||
* @return the item that would be returned by calling {@link #next()}
|
||||
* @throws NoSuchElementException if there is no element to peek at
|
||||
*/
|
||||
public T peek() throws NoSuchElementException;
|
||||
}
|
||||
|
|
|
@ -13,19 +13,29 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.util;
|
||||
package generic.util;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
import generic.util.PeekableIterator;
|
||||
import generic.util.WrappingPeekableIterator;
|
||||
|
||||
/**
|
||||
* Utilities for working with a {@link PeekableIterator}
|
||||
*/
|
||||
public enum PeekableIterators {
|
||||
;
|
||||
public static <T> PeekableIterator<T> castOrWrap(Iterator<T> it) {
|
||||
if (it instanceof PeekableIterator) {
|
||||
return (PeekableIterator<T>) it;
|
||||
/**
|
||||
* Ensure that the given iterator is peekable
|
||||
*
|
||||
* <p>
|
||||
* If it is already peekable, this casts it as such. If not, it wraps it in a peekable iterator.
|
||||
*
|
||||
* @param <E> the type of elements
|
||||
* @param it the iterator
|
||||
* @return the peekable iterator
|
||||
*/
|
||||
public static <E> PeekableIterator<E> castOrWrap(Iterator<E> it) {
|
||||
if (it instanceof PeekableIterator<E> pi) {
|
||||
return pi;
|
||||
}
|
||||
return new WrappingPeekableIterator<T>(it);
|
||||
return new WrappingPeekableIterator<E>(it);
|
||||
}
|
||||
}
|
|
@ -16,59 +16,37 @@
|
|||
package generic.util;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
/**
|
||||
* An implementation of {@link PeekableIterator} that can take a Java {@link Iterator} and
|
||||
* wrap it to implement the {@link PeekableIterator} interface.
|
||||
* An implementation of {@link PeekableIterator} that can take a Java {@link Iterator} and wrap it
|
||||
* to implement the {@link PeekableIterator} interface.
|
||||
*
|
||||
* @param <T> the type of the iterator
|
||||
*/
|
||||
public class WrappingPeekableIterator<T> implements PeekableIterator<T> {
|
||||
public class WrappingPeekableIterator<T> extends AbstractPeekableIterator<T> {
|
||||
|
||||
private Iterator<T> iterator;
|
||||
private T peek;
|
||||
private boolean peeked;
|
||||
private final Iterator<T> iterator;
|
||||
|
||||
/**
|
||||
* Wrap the given iterator
|
||||
*
|
||||
* @see PeekableIterators#castOrWrap(Iterator)
|
||||
* @param iterator the iterator
|
||||
*/
|
||||
public WrappingPeekableIterator(Iterator<T> iterator) {
|
||||
this.iterator = iterator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
if (peeked) {
|
||||
return true;
|
||||
}
|
||||
return iterator.hasNext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public T next() {
|
||||
if (peeked) {
|
||||
peeked = false;
|
||||
return peek;
|
||||
}
|
||||
return iterator.next();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public T peek() throws NoSuchElementException {
|
||||
if (peeked) {
|
||||
return peek;
|
||||
protected T seekNext() {
|
||||
if (!iterator.hasNext()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!hasNext()) {
|
||||
throw new NoSuchElementException();
|
||||
return iterator.next();
|
||||
}
|
||||
|
||||
peek = next();
|
||||
peeked = true;
|
||||
return peek;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -13,20 +13,26 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package generic;
|
||||
package generic.util;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.collections4.IteratorUtils;
|
||||
import org.junit.Test;
|
||||
|
||||
public class NestedIteratorTest {
|
||||
public class FlattenedIteratorTest {
|
||||
@Test
|
||||
public void testEmptyOuter() {
|
||||
List<Object> result =
|
||||
IteratorUtils.toList(NestedIterator.start(Collections.emptyIterator(), o -> {
|
||||
IteratorUtils.toList(FlattenedIterator.start(Collections.emptyIterator(), o -> {
|
||||
fail();
|
||||
return null;
|
||||
}));
|
||||
|
@ -36,27 +42,27 @@ public class NestedIteratorTest {
|
|||
@Test
|
||||
public void testSingleOuterEmptyInner() {
|
||||
List<Object> result = IteratorUtils.toList(
|
||||
NestedIterator.start(List.of("Test").iterator(), s -> Collections.emptyIterator()));
|
||||
FlattenedIterator.start(List.of("Test").iterator(), s -> Collections.emptyIterator()));
|
||||
assertTrue(result.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDoubleOuterEmptyInner() {
|
||||
List<Object> result = IteratorUtils.toList(
|
||||
NestedIterator.start(List.of("T1", "T2").iterator(), s -> Collections.emptyIterator()));
|
||||
FlattenedIterator.start(List.of("T1", "T2").iterator(), s -> Collections.emptyIterator()));
|
||||
assertTrue(result.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSingleOuterSingleInner() {
|
||||
List<String> result = IteratorUtils
|
||||
.toList(NestedIterator.start(List.of(0).iterator(), n -> List.of("Test").iterator()));
|
||||
.toList(FlattenedIterator.start(List.of(0).iterator(), n -> List.of("Test").iterator()));
|
||||
assertEquals(List.of("Test"), result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFirstEmptySecondSingleton() {
|
||||
List<String> result = IteratorUtils.toList(NestedIterator.start(List.of(0, 1).iterator(),
|
||||
List<String> result = IteratorUtils.toList(FlattenedIterator.start(List.of(0, 1).iterator(),
|
||||
n -> n == 0 ? Collections.emptyIterator() : List.of("Test").iterator()));
|
||||
assertEquals(List.of("Test"), result);
|
||||
}
|
||||
|
@ -64,20 +70,20 @@ public class NestedIteratorTest {
|
|||
@Test
|
||||
public void testSingleOuterDoubleInner() {
|
||||
List<String> result = IteratorUtils.toList(
|
||||
NestedIterator.start(List.of(0).iterator(), n -> List.of("T1", "T2").iterator()));
|
||||
FlattenedIterator.start(List.of(0).iterator(), n -> List.of("T1", "T2").iterator()));
|
||||
assertEquals(List.of("T1", "T2"), result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDoubleOuterDoubleInner() {
|
||||
List<String> result = IteratorUtils.toList(NestedIterator.start(List.of(0, 1).iterator(),
|
||||
List<String> result = IteratorUtils.toList(FlattenedIterator.start(List.of(0, 1).iterator(),
|
||||
n -> (n == 0 ? List.of("T1", "T2") : List.of("T3", "T4")).iterator()));
|
||||
assertEquals(List.of("T1", "T2", "T3", "T4"), result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultipleHasNextCalls() {
|
||||
Iterator<String> it = NestedIterator.start(List.of(0, 1).iterator(),
|
||||
Iterator<String> it = FlattenedIterator.start(List.of(0, 1).iterator(),
|
||||
n -> (n == 0 ? List.of("T1", "T2") : List.of("T3", "T4")).iterator());
|
||||
assertTrue(it.hasNext());
|
||||
assertTrue(it.hasNext());
|
||||
|
@ -100,7 +106,7 @@ public class NestedIteratorTest {
|
|||
|
||||
@Test
|
||||
public void testNoHasNextCalls() {
|
||||
Iterator<String> it = NestedIterator.start(List.of(0, 1).iterator(),
|
||||
Iterator<String> it = FlattenedIterator.start(List.of(0, 1).iterator(),
|
||||
n -> (n == 0 ? List.of("T1", "T2") : List.of("T3", "T4")).iterator());
|
||||
assertEquals("T1", it.next());
|
||||
assertEquals("T2", it.next());
|
||||
|
@ -116,7 +122,7 @@ public class NestedIteratorTest {
|
|||
List<String> b = new ArrayList<>(List.of("T3", "T4"));
|
||||
List<List<String>> listList = new ArrayList<>(List.of(a, b));
|
||||
|
||||
Iterator<String> it = NestedIterator.start(listList.iterator(), l -> l.iterator());
|
||||
Iterator<String> it = FlattenedIterator.start(listList.iterator(), l -> l.iterator());
|
||||
assertEquals("T1", it.next());
|
||||
assertEquals("T2", it.next());
|
||||
assertTrue(it.hasNext()); // Odd to do this right before a remove, but....
|
|
@ -20,8 +20,23 @@ import java.util.List;
|
|||
|
||||
import ghidra.program.model.address.*;
|
||||
|
||||
/**
|
||||
* An abstract implementation of {@link AddressSetView} that provides suitable defaults for as many
|
||||
* of the required methods as reasonable.
|
||||
*/
|
||||
public abstract class AbstractAddressSetView implements AddressSetView {
|
||||
|
||||
/**
|
||||
* Adjust the given start address so that if it lands in the given iterator's next range, that
|
||||
* range would be entirely included.
|
||||
*
|
||||
* @param rev the iterator. Note only the first element, if present, of this iterator is
|
||||
* considered. The client must ensure it's properly positioned.
|
||||
* @param start the proposed start address
|
||||
* @param forward true for forward iteration, i.e., the adjustment will be to the range's
|
||||
* minimum address, if applicable.
|
||||
* @return the adjusted start
|
||||
*/
|
||||
protected static Address fixStart(AddressRangeIterator rev, Address start, boolean forward) {
|
||||
if (!rev.hasNext()) {
|
||||
return start;
|
|
@ -18,11 +18,14 @@ package ghidra.util;
|
|||
import java.util.Iterator;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
import generic.NestedIterator;
|
||||
import generic.util.FlattenedIterator;
|
||||
import generic.util.PeekableIterator;
|
||||
import ghidra.program.model.address.*;
|
||||
|
||||
public class AddressIteratorAdapter extends NestedIterator<AddressRange, Address>
|
||||
/**
|
||||
* Convert an {@link AddressRange} iterator to an {@link AddressIterator}.
|
||||
*/
|
||||
public class AddressIteratorAdapter extends FlattenedIterator<AddressRange, Address>
|
||||
implements AddressIterator {
|
||||
|
||||
protected static class ForwardAddressIterator implements PeekableIterator<Address> {
|
||||
|
@ -89,15 +92,39 @@ public class AddressIteratorAdapter extends NestedIterator<AddressRange, Address
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterate over the addresses in the given range
|
||||
*
|
||||
* @param range the range
|
||||
* @param forward true to iterate forward, false for backward
|
||||
* @return the iterable
|
||||
*/
|
||||
public static Iterable<Address> forRange(AddressRange range, boolean forward) {
|
||||
return () -> forward ? new ForwardAddressIterator(range)
|
||||
: new BackwardAddressIterator(range);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct an {@link AddressIterator} over the given address ranges
|
||||
*
|
||||
* @param outer an iterator of address ranges
|
||||
* @param forward true for forward iteration. Otherwise backward iteration. This flag must be
|
||||
* consistent with the order of the given outer iterator.
|
||||
*/
|
||||
public AddressIteratorAdapter(Iterator<AddressRange> outer, boolean forward) {
|
||||
super(outer, forward ? ForwardAddressIterator::new : BackwardAddressIterator::new);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct an {@link AddressIterator} over the given address ranges, truncating the initial
|
||||
* range to the given start
|
||||
*
|
||||
* @param outer the iterator of address ranges, the first of which must contain or come after
|
||||
* the given start.
|
||||
* @param start the starting address
|
||||
* @param forward true for forward iteration. Otherwise backward iteration. This flag must be
|
||||
* consistent with the order of the given outer iterator.o
|
||||
*/
|
||||
public AddressIteratorAdapter(Iterator<AddressRange> outer, Address start, boolean forward) {
|
||||
super(outer, forward ? ar -> {
|
||||
if (!ar.contains(start)) {
|
|
@ -18,14 +18,29 @@ package ghidra.util;
|
|||
import java.util.Comparator;
|
||||
|
||||
import ghidra.program.model.address.AddressRange;
|
||||
import ghidra.program.model.address.AddressSet;
|
||||
|
||||
/**
|
||||
* Comparators used for sorting address ranges
|
||||
*/
|
||||
public enum AddressRangeComparators implements Comparator<AddressRange> {
|
||||
/**
|
||||
* Compare ranges by their minimum address and order them smallest first.
|
||||
*/
|
||||
FORWARD {
|
||||
@Override
|
||||
public int compare(AddressRange a, AddressRange b) {
|
||||
return a.getMinAddress().compareTo(b.getMinAddress());
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Compare ranges by their maximum address and order them largest first.
|
||||
*
|
||||
* @implNote Which address is compared might not ordinarily matter, since {@link AddressSet}
|
||||
* requires a disjoint union of ranges. However, these comparators often compare
|
||||
* ranges from different sets, e.g., in order to merge two or more iterators. Thus, in
|
||||
* reverse, we want to ensure ranges are ordered by their <em>maximum</em> address.
|
||||
*/
|
||||
BACKWARD {
|
||||
@Override
|
||||
public int compare(AddressRange a, AddressRange b) {
|
|
@ -24,6 +24,10 @@ import org.apache.commons.collections4.IteratorUtils;
|
|||
import ghidra.program.model.address.*;
|
||||
import ghidra.util.TwoWayBreakdownAddressRangeIterator.Which;
|
||||
|
||||
/**
|
||||
* Utilities for manipulating iterators over {@link AddressRange}s. Notably, this allows the
|
||||
* creation of lazily computed set operations on {@link AddressSetView}s.
|
||||
*/
|
||||
public enum AddressRangeIterators {
|
||||
;
|
||||
|
||||
|
@ -50,13 +54,30 @@ public enum AddressRangeIterators {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility for satisfying the type checker. This just forwards the method calls so that an
|
||||
* {@link Iterator} over {@link AddressRange} can be used where an {@link AddressRangeIterator}
|
||||
* is required. If only Java had type aliasing....
|
||||
*
|
||||
* @param it the iterator
|
||||
* @return the wrapper, or the same iterator if it is already an {@link AddressRangeIterator}
|
||||
*/
|
||||
public static AddressRangeIterator castOrWrap(Iterator<AddressRange> it) {
|
||||
if (it instanceof AddressRangeIterator) {
|
||||
return (AddressRangeIterator) it;
|
||||
if (it instanceof AddressRangeIterator ari) {
|
||||
return ari;
|
||||
}
|
||||
return new WrappingAddressRangeIterator(it);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an iterator over the union of address ranges in the given iterators
|
||||
*
|
||||
* @see UnionAddressSetView
|
||||
* @param iterators the iterators to union
|
||||
* @param forward true for forward iteration. The given iterators must all return ranges in the
|
||||
* order indicated by this flag.
|
||||
* @return the iterator over the union
|
||||
*/
|
||||
public static AddressRangeIterator union(Collection<Iterator<AddressRange>> iterators,
|
||||
boolean forward) {
|
||||
return new UnionAddressRangeIterator(iterators, forward);
|
||||
|
@ -70,6 +91,17 @@ public enum AddressRangeIterators {
|
|||
: range.getMinAddress().compareTo(start) <= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an iterator over the difference between two address range iterators
|
||||
*
|
||||
* @see DifferenceAddressSetView
|
||||
* @param a the minuend
|
||||
* @param b the subtrahend
|
||||
* @param start the starting address, or null
|
||||
* @param forward true for forward iteration. The given iterators must all return ranges in the
|
||||
* order indicated by this flag.
|
||||
* @return the iterator over the difference
|
||||
*/
|
||||
public static AddressRangeIterator subtract(Iterator<AddressRange> a, Iterator<AddressRange> b,
|
||||
Address start, boolean forward) {
|
||||
return new WrappingAddressRangeIterator(IteratorUtils.transformedIterator(
|
||||
|
@ -78,6 +110,17 @@ public enum AddressRangeIterators {
|
|||
e -> e.getKey()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an iterator over the symmetric difference between two address range iterators
|
||||
*
|
||||
* @see SymmetricDifferenceAddressSetView
|
||||
* @param a the first iterator
|
||||
* @param b the second iterator
|
||||
* @param start the starting address, or null
|
||||
* @param forward true for forward iteration. The given iterators must all return ranges in the
|
||||
* order indicated by this flag.
|
||||
* @return the iterator over the symmetric difference
|
||||
*/
|
||||
public static AddressRangeIterator xor(Iterator<AddressRange> a, Iterator<AddressRange> b,
|
||||
Address start, boolean forward) {
|
||||
Iterator<Entry<AddressRange, Which>> eit =
|
||||
|
@ -93,6 +136,16 @@ public enum AddressRangeIterators {
|
|||
return new WrappingAddressRangeIterator(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an iterator over the intersection between two address range iterators
|
||||
*
|
||||
* @see IntersectionAddressSetView
|
||||
* @param a the first iterator
|
||||
* @param b the second iterator
|
||||
* @param forward true for forward iteration. The given iterators must all return ranges in the
|
||||
* order indicated by this flag.
|
||||
* @return the iterator over the symmetric difference
|
||||
*/
|
||||
public static AddressRangeIterator intersect(Iterator<AddressRange> a, Iterator<AddressRange> b,
|
||||
boolean forward) {
|
||||
return new WrappingAddressRangeIterator(IteratorUtils.transformedIterator(
|
|
@ -17,12 +17,33 @@ package ghidra.util;
|
|||
|
||||
import java.util.Iterator;
|
||||
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressRange;
|
||||
import ghidra.program.model.address.AddressRangeImpl;
|
||||
import ghidra.program.model.address.AddressRangeIterator;
|
||||
import ghidra.program.model.address.AddressSetView;
|
||||
|
||||
/**
|
||||
* A lazily computed {@link AddressSetView} defined as the difference between two given
|
||||
* {@link AddressSetView}s.
|
||||
* <p>
|
||||
* This is equivalent to using {@link AddressSetView#subtract(AddressSetView)}, but does not
|
||||
* materialize the difference. The choice of one over the other depends on the number of ranges in
|
||||
* the inputs and the frequency of use of the result. With few ranges, or in cases where you need to
|
||||
* access the entire result, anyway, just use the normal {@link AddressRange}. In cases with many,
|
||||
* many ranges and where only a small part of the result needs to be computed, use this view. It may
|
||||
* also be advantageous to use this view if the inputs are themselves computed lazily.
|
||||
*/
|
||||
public class DifferenceAddressSetView extends AbstractAddressSetView {
|
||||
private final AddressSetView a;
|
||||
private final AddressSetView b;
|
||||
|
||||
/**
|
||||
* Construct the difference between two address sets
|
||||
*
|
||||
* @param a the minuend
|
||||
* @param b the subtrahend
|
||||
*/
|
||||
public DifferenceAddressSetView(AddressSetView a, AddressSetView b) {
|
||||
this.a = a;
|
||||
this.b = b;
|
|
@ -18,12 +18,33 @@ package ghidra.util;
|
|||
import static ghidra.util.MathUtilities.cmax;
|
||||
import static ghidra.util.MathUtilities.cmin;
|
||||
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressRange;
|
||||
import ghidra.program.model.address.AddressRangeIterator;
|
||||
import ghidra.program.model.address.AddressSetView;
|
||||
import ghidra.program.model.address.EmptyAddressRangeIterator;
|
||||
|
||||
/**
|
||||
* A lazily computed {@link AddressSetView} defined as the intersection of two given
|
||||
* {@link AddressSetView}s.
|
||||
* <p>
|
||||
* This is equivalent to using {@link AddressSetView#intersect(AddressSetView)}, but does not
|
||||
* materialize the intersection. The choice of one over the other depends on the number of ranges in
|
||||
* the inputs and the frequency of use of the result. With few ranges, or in cases where you need to
|
||||
* access the entire result, anyway, just use the normal {@link AddressRange}. In cases with many,
|
||||
* many ranges and where only a small part of the result needs to be computed, use this view. It may
|
||||
* also be advantageous to use this view if the inputs are themselves computed lazily.
|
||||
*/
|
||||
public class IntersectionAddressSetView extends AbstractAddressSetView {
|
||||
private final AddressSetView a;
|
||||
private final AddressSetView b;
|
||||
|
||||
/**
|
||||
* Construct the intersection of two address sets
|
||||
*
|
||||
* @param a the first set
|
||||
* @param b the second set
|
||||
*/
|
||||
public IntersectionAddressSetView(AddressSetView a, AddressSetView b) {
|
||||
this.a = a;
|
||||
this.b = b;
|
|
@ -15,12 +15,34 @@
|
|||
*/
|
||||
package ghidra.util;
|
||||
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressRange;
|
||||
import ghidra.program.model.address.AddressRangeIterator;
|
||||
import ghidra.program.model.address.AddressSetView;
|
||||
|
||||
/**
|
||||
* A lazily computed {@link AddressSetView} defined as the symmetric difference between two given
|
||||
* {@link AddressSetView}s.
|
||||
* <p>
|
||||
* There is no equivalent method in {@link AddressSetView}, but it could be computed using a
|
||||
* combination of {@link AddressSetView#subtract(AddressSetView)} and
|
||||
* {@link AddressSetView#union(AddressSetView)}. However, this class does not materialize the
|
||||
* result. The choice of one over the other depends on the number of ranges in the inputs and the
|
||||
* frequency of use of the result. With few ranges, or in cases where you need to access the entire
|
||||
* result, anyway, just use the normal {@link AddressRange}. In cases with many, many ranges and
|
||||
* where only a small part of the result needs to be computed, use this view. It may also be
|
||||
* advantageous to use this if the inputs are themselves computed lazily.
|
||||
*/
|
||||
public class SymmetricDifferenceAddressSetView extends AbstractAddressSetView {
|
||||
private final AddressSetView a;
|
||||
private final AddressSetView b;
|
||||
|
||||
/**
|
||||
* Construct the symmetric difference between two address sets
|
||||
*
|
||||
* @param a the first set
|
||||
* @param b the second set
|
||||
*/
|
||||
public SymmetricDifferenceAddressSetView(AddressSetView a, AddressSetView b) {
|
||||
this.a = a;
|
||||
this.b = b;
|
|
@ -21,37 +21,91 @@ import static ghidra.util.MathUtilities.cmin;
|
|||
import java.util.Iterator;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import generic.util.PeekableIterator;
|
||||
import generic.util.*;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.util.TwoWayBreakdownAddressRangeIterator.Which;
|
||||
|
||||
/**
|
||||
* An iterator that takes two iterators over address ranges and "breaks down" where they do and do
|
||||
* not overlap. Consider one iterator L that contains only [1,3], and another R that contains only
|
||||
* [2,4]. The two could be plotted:
|
||||
*
|
||||
* <pre>
|
||||
* 1 2 3 4
|
||||
* [---L---]
|
||||
* [---R---]
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* This will return an iterator over range-which pairs. "Which" indicates which iterators include
|
||||
* the given range, {@link Which#LEFT}, {@link Which#RIGHT}, or {@link Which#BOTH}. There is no
|
||||
* {@code NONE}, so gaps are omitted. For the example above:
|
||||
*
|
||||
* <pre>
|
||||
* 1 2 3 4
|
||||
* [L][-B--][R]
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* This supports the computation of difference, symmetric difference, and intersection. <b>NOTE:</b>
|
||||
* Clients cannot save the entries returned by the iterator. The entry is only valid during
|
||||
* iteration, and it is reused by the iterator for each subsequent entry.
|
||||
*/
|
||||
public class TwoWayBreakdownAddressRangeIterator
|
||||
extends AbstractPeekableIterator<Entry<AddressRange, Which>> {
|
||||
|
||||
/**
|
||||
* Indicates which of the input iterators contain a range
|
||||
*/
|
||||
public enum Which {
|
||||
LEFT(true, false), RIGHT(false, true), BOTH(true, true);
|
||||
/** Only the left included the range */
|
||||
LEFT(true, false),
|
||||
/** Only the right included the range */
|
||||
RIGHT(false, true),
|
||||
/** Both included the range */
|
||||
BOTH(true, true);
|
||||
|
||||
private Which(boolean includesLeft, boolean includesRight) {
|
||||
this.includesLeft = includesLeft;
|
||||
this.includesRight = includesRight;
|
||||
}
|
||||
|
||||
/** Indicates the the left iterator includes this range */
|
||||
public final boolean includesLeft;
|
||||
/** Indicates that the right iterator includes this range */
|
||||
public final boolean includesRight;
|
||||
|
||||
/**
|
||||
* Check if this range is included in the difference: {@code left - right}
|
||||
*
|
||||
* @return true if included
|
||||
*/
|
||||
public boolean inSubtract() {
|
||||
return this == LEFT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this range is included in the symmetric difference: {@code left Δ right}
|
||||
*
|
||||
* @return true if included
|
||||
*/
|
||||
public boolean inXor() {
|
||||
return this == LEFT || this == RIGHT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this range is included in the intersection: {@code left ∩ right}
|
||||
*
|
||||
* @return true if included
|
||||
*/
|
||||
public boolean inIntersect() {
|
||||
return this == BOTH;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A mutable map entry
|
||||
*/
|
||||
public static class MyEntry implements Entry<AddressRange, Which> {
|
||||
private AddressRange key;
|
||||
private Which val;
|
||||
|
@ -81,6 +135,14 @@ public class TwoWayBreakdownAddressRangeIterator
|
|||
|
||||
private final MyEntry entry = new MyEntry();
|
||||
|
||||
/**
|
||||
* Create an iterator that "breaks down" the two address range iterators.
|
||||
*
|
||||
* @param lit the iterator of ranges on the left
|
||||
* @param rit the iterator of ranges on the right
|
||||
* @param forward true for forward iteration, false for reverse. The input iterators must be
|
||||
* ordered according to this flag.
|
||||
*/
|
||||
public TwoWayBreakdownAddressRangeIterator(Iterator<AddressRange> lit,
|
||||
Iterator<AddressRange> rit, boolean forward) {
|
||||
this.lit = PeekableIterators.castOrWrap(lit);
|
|
@ -21,17 +21,26 @@ import static ghidra.util.MathUtilities.cmin;
|
|||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
|
||||
import generic.util.AbstractPeekableIterator;
|
||||
import generic.util.MergeSortingIterator;
|
||||
import generic.util.PeekableIterator;
|
||||
import ghidra.program.model.address.*;
|
||||
import generic.util.PeekableIterators;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressRange;
|
||||
import ghidra.program.model.address.AddressRangeImpl;
|
||||
import ghidra.program.model.address.AddressRangeIterator;
|
||||
|
||||
/**
|
||||
* The iterator implementation backing several methods in {@link UnionAddressSetView}
|
||||
*/
|
||||
public class UnionAddressRangeIterator extends AbstractPeekableIterator<AddressRange>
|
||||
implements AddressRangeIterator {
|
||||
private final PeekableIterator<AddressRange> mit;
|
||||
private final PeekableIterator<AddressRange> it;
|
||||
private final boolean forward;
|
||||
|
||||
/**
|
||||
* Coalesce (by union) ranges from a single iterator
|
||||
*
|
||||
* <p>
|
||||
* The ranges must be returned in order: in the forward direction, by increasing min address; in
|
||||
* the reverse direction, by decreasing max address.
|
||||
*
|
||||
|
@ -39,13 +48,13 @@ public class UnionAddressRangeIterator extends AbstractPeekableIterator<AddressR
|
|||
* @param forward true to coalesce in the forward direction, false for reverse
|
||||
*/
|
||||
public UnionAddressRangeIterator(Iterator<AddressRange> it, boolean forward) {
|
||||
this.mit = PeekableIterators.castOrWrap(it);
|
||||
this.it = PeekableIterators.castOrWrap(it);
|
||||
this.forward = forward;
|
||||
}
|
||||
|
||||
/**
|
||||
* Union into a single range iterator, several range iterators
|
||||
*
|
||||
* <p>
|
||||
* The ranges will be coalesced so that each returned range is disconnected from any other. The
|
||||
* ranges of each iterator must be returned in order by direction. While not recommended, the
|
||||
* ranges of each iterator may overlap, so long as they are sorted as in
|
||||
|
@ -56,7 +65,7 @@ public class UnionAddressRangeIterator extends AbstractPeekableIterator<AddressR
|
|||
*/
|
||||
public UnionAddressRangeIterator(Collection<Iterator<AddressRange>> iterators,
|
||||
boolean forward) {
|
||||
this.mit = new MergeSortingIterator<AddressRange>(iterators,
|
||||
this.it = new MergeSortingIterator<AddressRange>(iterators,
|
||||
forward ? AddressRangeComparators.FORWARD : AddressRangeComparators.BACKWARD);
|
||||
this.forward = forward;
|
||||
}
|
||||
|
@ -68,18 +77,18 @@ public class UnionAddressRangeIterator extends AbstractPeekableIterator<AddressR
|
|||
|
||||
@Override
|
||||
protected AddressRange seekNext() {
|
||||
if (!mit.hasNext()) {
|
||||
if (!it.hasNext()) {
|
||||
return null;
|
||||
}
|
||||
AddressRange peek = mit.peek();
|
||||
AddressRange peek = it.peek();
|
||||
Address min = peek.getMinAddress();
|
||||
Address max = peek.getMaxAddress();
|
||||
while (true) {
|
||||
mit.next();
|
||||
if (!mit.hasNext()) {
|
||||
it.next();
|
||||
if (!it.hasNext()) {
|
||||
break;
|
||||
}
|
||||
peek = mit.peek();
|
||||
peek = it.peek();
|
||||
if (peek.getAddressSpace() != min.getAddressSpace()) {
|
||||
break;
|
||||
}
|
|
@ -21,15 +21,43 @@ import static ghidra.util.MathUtilities.cmin;
|
|||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressRange;
|
||||
import ghidra.program.model.address.AddressRangeIterator;
|
||||
import ghidra.program.model.address.AddressSetView;
|
||||
|
||||
/**
|
||||
* A lazily computed {@link AddressSetView} defined as the union of many given
|
||||
* {@link AddressSetView}s.
|
||||
* <p>
|
||||
* This is equivalent to using {@link AddressSetView#union(AddressSetView)}, but does not
|
||||
* materialize the difference. The choice of one over the other depends on the number of ranges in
|
||||
* the inputs and the frequency of use of the result. With few ranges, or in cases where you need to
|
||||
* access the entire result, anyway, just use the normal {@link AddressRange}. In cases with many,
|
||||
* many ranges and where only a small part of the result needs to be computed, use this view. It may
|
||||
* also be advantageous to use this if the inputs are themselves computed lazily.
|
||||
* <p>
|
||||
* This follows the conventions expected of an {@link AddressSetView} in that the returned ranges
|
||||
* are disjoint. Thus, it will combine intersecting and abutting ranges from among the inputs. For
|
||||
* example, the union of [[1,2]] and [[3,4]] is [[1,4]].
|
||||
*/
|
||||
public class UnionAddressSetView extends AbstractAddressSetView {
|
||||
private final Collection<AddressSetView> views;
|
||||
|
||||
/**
|
||||
* Construct the union of the given address set views
|
||||
*
|
||||
* @param views the input sets
|
||||
*/
|
||||
public UnionAddressSetView(AddressSetView... views) {
|
||||
this(Arrays.asList(views));
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct the union of the given address set views
|
||||
*
|
||||
* @param views the input sets
|
||||
*/
|
||||
public UnionAddressSetView(Collection<AddressSetView> views) {
|
||||
this.views = views;
|
||||
}
|
|
@ -19,19 +19,16 @@ import static org.junit.Assert.*;
|
|||
|
||||
import java.util.*;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import generic.test.AbstractGTest;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.lang.*;
|
||||
import ghidra.program.util.DefaultLanguageService;
|
||||
import ghidra.test.AbstractGhidraHeadlessIntegrationTest;
|
||||
|
||||
public class DifferenceAddressSetViewTest extends AbstractGhidraHeadlessIntegrationTest {
|
||||
protected Language toy;
|
||||
public class DifferenceAddressSetViewTest extends AbstractGTest {
|
||||
protected AddressSpace space = new GenericAddressSpace("ram", 64, AddressSpace.TYPE_RAM, 1);
|
||||
|
||||
protected Address addr(long offset) {
|
||||
return toy.getAddressFactory().getDefaultAddressSpace().getAddress(offset);
|
||||
return space.getAddress(offset);
|
||||
}
|
||||
|
||||
protected AddressRange rng(long min, long max) {
|
||||
|
@ -54,12 +51,6 @@ public class DifferenceAddressSetViewTest extends AbstractGhidraHeadlessIntegrat
|
|||
return result;
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUpIteratorTest() throws LanguageNotFoundException {
|
||||
toy = DefaultLanguageService.getLanguageService().getLanguage(
|
||||
new LanguageID("Toy:BE:64:default"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCounts() {
|
||||
AddressSetView difference;
|
|
@ -19,19 +19,16 @@ import static org.junit.Assert.*;
|
|||
|
||||
import java.util.*;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import generic.test.AbstractGTest;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.lang.*;
|
||||
import ghidra.program.util.DefaultLanguageService;
|
||||
import ghidra.test.AbstractGhidraHeadlessIntegrationTest;
|
||||
|
||||
public class IntersectionAddressSetViewTest extends AbstractGhidraHeadlessIntegrationTest {
|
||||
protected Language toy;
|
||||
public class IntersectionAddressSetViewTest extends AbstractGTest {
|
||||
protected AddressSpace space = new GenericAddressSpace("ram", 64, AddressSpace.TYPE_RAM, 1);
|
||||
|
||||
protected Address addr(long offset) {
|
||||
return toy.getAddressFactory().getDefaultAddressSpace().getAddress(offset);
|
||||
return space.getAddress(offset);
|
||||
}
|
||||
|
||||
protected AddressRange rng(long min, long max) {
|
||||
|
@ -54,12 +51,6 @@ public class IntersectionAddressSetViewTest extends AbstractGhidraHeadlessIntegr
|
|||
return result;
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUpIteratorTest() throws LanguageNotFoundException {
|
||||
toy = DefaultLanguageService.getLanguageService().getLanguage(
|
||||
new LanguageID("Toy:BE:64:default"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCounts() {
|
||||
AddressSetView intersection;
|
|
@ -22,6 +22,8 @@ import java.util.Map.Entry;
|
|||
|
||||
import org.junit.Test;
|
||||
|
||||
import generic.util.MergeSortingIterator;
|
||||
|
||||
public class MergeSortingIteratorTest {
|
||||
@Test
|
||||
public void testEmptyMap() {
|
|
@ -19,19 +19,16 @@ import static org.junit.Assert.*;
|
|||
|
||||
import java.util.*;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import generic.test.AbstractGTest;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.lang.*;
|
||||
import ghidra.program.util.DefaultLanguageService;
|
||||
import ghidra.test.AbstractGhidraHeadlessIntegrationTest;
|
||||
|
||||
public class SymmetricDifferenceAddressSetViewTest extends AbstractGhidraHeadlessIntegrationTest {
|
||||
protected Language toy;
|
||||
public class SymmetricDifferenceAddressSetViewTest extends AbstractGTest {
|
||||
protected AddressSpace space = new GenericAddressSpace("ram", 64, AddressSpace.TYPE_RAM, 1);
|
||||
|
||||
protected Address addr(long offset) {
|
||||
return toy.getAddressFactory().getDefaultAddressSpace().getAddress(offset);
|
||||
return space.getAddress(offset);
|
||||
}
|
||||
|
||||
protected AddressRange rng(long min, long max) {
|
||||
|
@ -54,12 +51,6 @@ public class SymmetricDifferenceAddressSetViewTest extends AbstractGhidraHeadles
|
|||
return result;
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUpIteratorTest() throws LanguageNotFoundException {
|
||||
toy = DefaultLanguageService.getLanguageService().getLanguage(
|
||||
new LanguageID("Toy:BE:64:default"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCounts() {
|
||||
AddressSetView xor;
|
|
@ -22,17 +22,15 @@ import java.util.Map.Entry;
|
|||
|
||||
import org.apache.commons.lang3.tuple.ImmutablePair;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import generic.test.AbstractGTest;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.lang.*;
|
||||
import ghidra.program.util.DefaultLanguageService;
|
||||
import ghidra.test.AbstractGhidraHeadlessIntegrationTest;
|
||||
import ghidra.util.TwoWayBreakdownAddressRangeIterator.Which;
|
||||
|
||||
public class TwoWayBreakdownAddressRangeIteratorTest extends AbstractGhidraHeadlessIntegrationTest {
|
||||
protected Language toy;
|
||||
public class TwoWayBreakdownAddressRangeIteratorTest extends AbstractGTest {
|
||||
protected AddressSpace rom = new GenericAddressSpace("rom", 64, AddressSpace.TYPE_RAM, 1);
|
||||
protected AddressSpace ram = new GenericAddressSpace("ram", 64, AddressSpace.TYPE_RAM, 2);
|
||||
|
||||
protected TwoWayBreakdownAddressRangeIterator makeIterator(AddressSet a, AddressSet b,
|
||||
boolean forward) {
|
||||
|
@ -41,11 +39,11 @@ public class TwoWayBreakdownAddressRangeIteratorTest extends AbstractGhidraHeadl
|
|||
}
|
||||
|
||||
protected Address addr(long offset) {
|
||||
return toy.getAddressFactory().getDefaultAddressSpace().getAddress(offset);
|
||||
return rom.getAddress(offset);
|
||||
}
|
||||
|
||||
protected Address dAddr(long offset) {
|
||||
return toy.getAddressFactory().getAddressSpace("data").getAddress(offset);
|
||||
return ram.getAddress(offset);
|
||||
}
|
||||
|
||||
protected AddressRange rng(long min, long max) {
|
||||
|
@ -87,12 +85,6 @@ public class TwoWayBreakdownAddressRangeIteratorTest extends AbstractGhidraHeadl
|
|||
return result;
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUpIteratorTest() throws LanguageNotFoundException {
|
||||
toy = DefaultLanguageService.getLanguageService()
|
||||
.getLanguage(new LanguageID("Toy:BE:64:harvard"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBothEmpty() {
|
||||
AddressSet a = new AddressSet();
|
|
@ -19,23 +19,21 @@ import static org.junit.Assert.*;
|
|||
|
||||
import java.util.*;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import generic.test.AbstractGTest;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.lang.*;
|
||||
import ghidra.program.util.DefaultLanguageService;
|
||||
import ghidra.test.AbstractGhidraHeadlessIntegrationTest;
|
||||
|
||||
public class UnionAddressSetViewTest extends AbstractGhidraHeadlessIntegrationTest {
|
||||
protected Language toy;
|
||||
public class UnionAddressSetViewTest extends AbstractGTest {
|
||||
protected AddressSpace rom = new GenericAddressSpace("rom", 64, AddressSpace.TYPE_RAM, 1);
|
||||
protected AddressSpace ram = new GenericAddressSpace("ram", 64, AddressSpace.TYPE_RAM, 2);
|
||||
|
||||
protected Address addr(long offset) {
|
||||
return toy.getDefaultSpace().getAddress(offset);
|
||||
return rom.getAddress(offset);
|
||||
}
|
||||
|
||||
protected Address daddr(long offset) {
|
||||
return toy.getDefaultDataSpace().getAddress(offset);
|
||||
protected Address dAddr(long offset) {
|
||||
return ram.getAddress(offset);
|
||||
}
|
||||
|
||||
protected AddressRange rng(long min, long max) {
|
||||
|
@ -43,7 +41,7 @@ public class UnionAddressSetViewTest extends AbstractGhidraHeadlessIntegrationTe
|
|||
}
|
||||
|
||||
protected AddressRange drng(long min, long max) {
|
||||
return new AddressRangeImpl(daddr(min), daddr(max));
|
||||
return new AddressRangeImpl(dAddr(min), dAddr(max));
|
||||
}
|
||||
|
||||
protected AddressSet set(AddressRange... ranges) {
|
||||
|
@ -62,12 +60,6 @@ public class UnionAddressSetViewTest extends AbstractGhidraHeadlessIntegrationTe
|
|||
return result;
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUpIteratorTest() throws LanguageNotFoundException {
|
||||
toy = DefaultLanguageService.getLanguageService()
|
||||
.getLanguage(new LanguageID("Toy:BE:64:harvard"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCounts() {
|
||||
AddressSetView union;
|
Loading…
Add table
Add a link
Reference in a new issue