mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-09-28 17:11:11 -09:00
Merge remote-tracking branch 'origin/GP-1529_Dan_emuInterruptExtern--SQUASHED'
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user