Merge remote-tracking branch 'origin/GP-1529_Dan_emuInterruptExtern--SQUASHED'

This commit is contained in:
Ryan Kurtz
2023-03-30 07:59:31 -04:00
25 changed files with 274 additions and 100 deletions

View File

@@ -869,11 +869,12 @@ public interface Span<N, S extends Span<N, S>> extends Comparable<S> {
@Override
public S bound() {
S result = domain.empty();
for (Entry<S, V> entry : spanTree.values()) {
result = result.bound(entry.getKey());
if (spanTree.isEmpty()) {
return domain.empty();
}
return result;
S first = spanTree.firstEntry().getValue().getKey();
S last = spanTree.lastEntry().getValue().getKey();
return first.bound(last);
}
@Override

View File

@@ -165,16 +165,16 @@ public class AdaptedEmulator implements Emulator {
}
@Override
protected void readUninitializedFromBacking(ULongSpanSet uninitialized) {
protected ULongSpanSet readUninitializedFromBacking(ULongSpanSet uninitialized) {
if (uninitialized.isEmpty()) {
return;
return uninitialized;
}
if (backing.loadImage == null) {
if (space.isUniqueSpace()) {
throw new AccessPcodeExecutionException(
"Attempted to read from uninitialized unique: " + uninitialized);
}
return;
return uninitialized;
}
ULongSpan bound = uninitialized.bound();
byte[] data = new byte[(int) bound.length()];
@@ -184,6 +184,7 @@ public class AdaptedEmulator implements Emulator {
bytes.putData(span.min(), data, (int) (span.min() - bound.min()),
(int) span.length());
}
return bytes.getUninitialized(bound.min(), bound.max());
}
@Override

View File

@@ -159,7 +159,8 @@ public class DefaultPcodeThread<T> implements PcodeThread<T> {
* @param state the composite state assigned to the thread
*/
public PcodeThreadExecutor(DefaultPcodeThread<T> thread) {
super(thread.language, thread.arithmetic, thread.state, Reason.EXECUTE);
// NB. The executor itself is not decoding. So reads are in fact data reads.
super(thread.language, thread.arithmetic, thread.state, Reason.EXECUTE_READ);
this.thread = thread;
}

View File

@@ -112,7 +112,7 @@ public class ModifiedPcodeThread<T> extends DefaultPcodeThread<T> {
* These two exist as a way to integrate the language-specific injects that are already
* written for {@link Emulator}.
*/
emulate = new GlueEmulate(language, new AdaptedMemoryState<>(state, Reason.EXECUTE) {
emulate = new GlueEmulate(language, new AdaptedMemoryState<>(state, Reason.EXECUTE_READ) {
@Override
public void setMemoryBank(MemoryBank bank) {
// Ignore

View File

@@ -17,6 +17,7 @@ package ghidra.pcode.emu;
import ghidra.app.util.PseudoInstruction;
import ghidra.pcode.emulate.InstructionDecodeException;
import ghidra.pcode.exec.DecodePcodeExecutionException;
import ghidra.pcode.exec.PcodeArithmetic.Purpose;
import ghidra.pcode.exec.PcodeExecutorState;
import ghidra.program.disassemble.Disassembler;
@@ -87,7 +88,7 @@ public class SleighInstructionDecoder implements InstructionDecoder {
block = disassembler.pseudoDisassembleBlock(
state.getConcreteBuffer(address, Purpose.DECODE), context, 1);
if (block == null || block.isEmpty()) {
throw new InstructionDecodeException(lastMsg, address);
throw new DecodePcodeExecutionException(lastMsg, address);
}
instruction = (PseudoInstruction) block.getInstructionAt(address);
lengthWithDelays = computeLength();

View File

@@ -25,6 +25,7 @@ import ghidra.program.model.lang.Language;
import ghidra.program.model.lang.Register;
import ghidra.program.model.mem.*;
import ghidra.program.model.pcode.PcodeOp;
import ghidra.program.model.pcode.Varnode;
import ghidra.util.Msg;
/**
@@ -41,16 +42,21 @@ public abstract class AbstractBytesPcodeExecutorStatePiece<S extends BytesPcodeE
protected class StateMemBuffer implements MemBufferMixin {
protected final Address address;
protected final BytesPcodeExecutorStateSpace<?> source;
protected final Reason reason;
/**
* Construct a buffer bound to the given space, at the given address
*
* @param address the address
* @param source the space
* @param reason the reason this buffer reads from the state, as in
* {@link PcodeExecutorStatePiece#getVar(Varnode, Reason)}
*/
public StateMemBuffer(Address address, BytesPcodeExecutorStateSpace<?> source) {
public StateMemBuffer(Address address, BytesPcodeExecutorStateSpace<?> source,
Reason reason) {
this.address = address;
this.source = source;
this.reason = reason;
}
@Override
@@ -70,8 +76,8 @@ public abstract class AbstractBytesPcodeExecutorStatePiece<S extends BytesPcodeE
@Override
public int getBytes(ByteBuffer buffer, int addressOffset) {
byte[] data = source.read(address.getOffset() + addressOffset, buffer.remaining(),
Reason.EXECUTE);
byte[] data =
source.read(address.getOffset() + addressOffset, buffer.remaining(), reason);
buffer.put(data);
return data.length;
}
@@ -160,7 +166,8 @@ public abstract class AbstractBytesPcodeExecutorStatePiece<S extends BytesPcodeE
@Override
public MemBuffer getConcreteBuffer(Address address, PcodeArithmetic.Purpose purpose) {
return new StateMemBuffer(address, getForSpace(address.getAddressSpace(), false));
return new StateMemBuffer(address, getForSpace(address.getAddressSpace(), false),
purpose.reason());
}
@Override

View File

@@ -18,7 +18,7 @@ package ghidra.pcode.exec;
import java.util.*;
import generic.ULongSpan;
import generic.ULongSpan.ULongSpanSet;
import generic.ULongSpan.*;
import ghidra.generic.util.datastruct.SemisparseByteArray;
import ghidra.pcode.exec.PcodeExecutorStatePiece.Reason;
import ghidra.program.model.address.*;
@@ -77,8 +77,10 @@ public class BytesPcodeExecutorStateSpace<B> {
* Extension point: Read from backing into this space, when acting as a cache.
*
* @param uninitialized the ranges which need to be read.
* @return the ranges which remain uninitialized
*/
protected void readUninitializedFromBacking(ULongSpanSet uninitialized) {
protected ULongSpanSet readUninitializedFromBacking(ULongSpanSet uninitialized) {
return uninitialized;
}
/**
@@ -95,9 +97,15 @@ public class BytesPcodeExecutorStateSpace<B> {
}
protected AddressRange addrRng(ULongSpan span) {
Address start = space.getAddress(span.min());
Address end = space.getAddress(span.max());
return new AddressRangeImpl(start, end);
return new AddressRangeImpl(
space.getAddress(span.min()),
space.getAddress(span.max()));
}
protected ULongSpan spanRng(AddressRange range) {
return ULongSpan.span(
range.getMinAddress().getOffset(),
range.getMaxAddress().getOffset());
}
protected AddressSet addrSet(ULongSpanSet set) {
@@ -108,6 +116,20 @@ public class BytesPcodeExecutorStateSpace<B> {
return result;
}
/**
* This assumes without assertion that the set is contained in this space
*
* @param set the address set
* @return the unsigned long span set
*/
protected ULongSpanSet spanSet(AddressSetView set) {
MutableULongSpanSet result = new DefaultULongSpanSet();
for (AddressRange range : set) {
result.add(spanRng(range));
}
return result;
}
protected Set<Register> getRegs(AddressSetView set) {
Set<Register> regs = new TreeSet<>();
for (AddressRange rng : set) {
@@ -151,12 +173,32 @@ public class BytesPcodeExecutorStateSpace<B> {
* @return the bytes read
*/
public byte[] read(long offset, int size, Reason reason) {
if (backing != null) {
readUninitializedFromBacking(bytes.getUninitialized(offset, offset + size - 1));
ULongSpanSet uninitialized = bytes.getUninitialized(offset, offset + size - 1);
if (uninitialized.isEmpty()) {
return readBytes(offset, size, reason);
}
ULongSpanSet stillUninit = bytes.getUninitialized(offset, offset + size - 1);
if (!stillUninit.isEmpty() && reason == Reason.EXECUTE) {
warnUninit(stillUninit);
if (backing != null) {
uninitialized = readUninitializedFromBacking(uninitialized);
if (uninitialized.isEmpty()) {
return readBytes(offset, size, reason);
}
}
Iterator<ULongSpan> it =
uninitialized.complement(ULongSpan.extent(offset, size)).iterator();
if (it.hasNext()) {
ULongSpan init = it.next();
if (init.min().longValue() == offset) {
return readBytes(offset, (int) init.length(), reason);
}
}
if (reason == Reason.EXECUTE_READ) {
warnUninit(uninitialized);
}
else if (reason == Reason.EXECUTE_DECODE) {
throw new DecodePcodeExecutionException("Cannot decode uninitialized memory",
space.getAddress(offset));
}
return readBytes(offset, size, reason);
}

View File

@@ -0,0 +1,31 @@
/* ###
* 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.exec;
import ghidra.program.model.address.Address;
public class DecodePcodeExecutionException extends PcodeExecutionException {
private final Address pc;
public DecodePcodeExecutionException(String message, Address pc) {
super(message + ", PC=" + pc);
this.pc = pc;
}
public Address getProgramCounter() {
return pc;
}
}

View File

@@ -17,6 +17,7 @@ package ghidra.pcode.exec;
import java.math.BigInteger;
import ghidra.pcode.exec.PcodeExecutorStatePiece.Reason;
import ghidra.pcode.opbehavior.*;
import ghidra.pcode.utils.Utils;
import ghidra.program.model.lang.Endian;
@@ -49,21 +50,31 @@ public interface PcodeArithmetic<T> {
*/
enum Purpose {
/** The value is needed to parse an instruction */
DECODE,
DECODE(Reason.EXECUTE_DECODE),
/** The value is needed for disassembly context */
CONTEXT,
CONTEXT(Reason.EXECUTE_READ),
/** The value is needed to decide a conditional branch */
CONDITION,
CONDITION(Reason.EXECUTE_READ),
/** The value will be used as the address of an indirect branch */
BRANCH,
BRANCH(Reason.EXECUTE_READ),
/** The value will be used as the address of a value to load */
LOAD,
LOAD(Reason.EXECUTE_READ),
/** The value will be used as the address of a value to store */
STORE,
STORE(Reason.EXECUTE_READ),
/** Some other reason, perhaps for userop library use */
OTHER,
OTHER(Reason.EXECUTE_READ),
/** The user or a tool is inspecting the value */
INSPECT
INSPECT(Reason.INSPECT);
private final Reason reason;
private Purpose(Reason reason) {
this.reason = reason;
}
public Reason reason() {
return reason;
}
}
/**

View File

@@ -47,9 +47,11 @@ public interface PcodeExecutorStatePiece<A, T> {
enum Reason {
/** The value is needed as the default program counter or disassembly context */
RE_INIT,
/** The value is needed by the emulator in the course of execution */
EXECUTE,
/** The value is being inspected */
/** The value is being read by the emulator as data in the course of execution */
EXECUTE_READ,
/** The value is being decoded by the emulator as an instruction for execution */
EXECUTE_DECODE,
/** The value is being inspected by something other than an emulator */
INSPECT
}

View File

@@ -59,7 +59,7 @@ public class AnnotatedPcodeUseropLibraryTest extends AbstractGTest {
protected PcodeExecutor<byte[]> createBytesExecutor(SleighLanguage language) throws Exception {
PcodeExecutorState<byte[]> state = new BytesPcodeExecutorState(language);
PcodeArithmetic<byte[]> arithmetic = BytesPcodeArithmetic.forLanguage(language);
return new PcodeExecutor<>(language, arithmetic, state, Reason.EXECUTE);
return new PcodeExecutor<>(language, arithmetic, state, Reason.EXECUTE_READ);
}
protected <T> void executeSleigh(PcodeExecutor<T> executor, PcodeUseropLibrary<T> library,

View File

@@ -802,8 +802,10 @@ public class Disassembler implements DisassemblerConflictHandler {
disassembleInstructionBlock(block, blockMemBuffer, null, limit, null, false);
}
catch (Exception e) {
Msg.error(this, "Pseudo block disassembly failure at " + blockMemBuffer.getAddress() +
": " + e.getMessage(), e);
String message = "Pseudo block disassembly failure at " + blockMemBuffer.getAddress() +
": " + e.getMessage();
Msg.error(this, message, e);
reportMessage(message);
}
finally {