diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/emulation/AbstractReadsTargetPcodeExecutorState.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/emulation/AbstractReadsTargetPcodeExecutorState.java index 7ab942c468..811e1c65b8 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/emulation/AbstractReadsTargetPcodeExecutorState.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/emulation/AbstractReadsTargetPcodeExecutorState.java @@ -17,9 +17,6 @@ package ghidra.app.plugin.core.debug.service.emulation; import java.util.concurrent.*; -import com.google.common.collect.Range; -import com.google.common.primitives.UnsignedLong; - import ghidra.app.services.TraceRecorder; import ghidra.framework.plugintool.PluginTool; import ghidra.pcode.exec.AccessPcodeExecutionException; @@ -56,17 +53,19 @@ public abstract class AbstractReadsTargetPcodeExecutorState @Override public byte[] read(long offset, int size) { if (source != null) { - AddressSet uninitialized = new AddressSet(); - for (Range rng : cache.getUninitialized(offset, offset + size - 1) - .asRanges()) { - uninitialized.add(space.getAddress(lower(rng)), - space.getAddress(upper(rng))); - } + AddressSet uninitialized = + addrSet(cache.getUninitialized(offset, offset + size - 1)); if (uninitialized.isEmpty()) { return super.read(offset, size); } fillUninitialized(uninitialized); + + AddressSet unknown = + computeUnknown(addrSet(cache.getUninitialized(offset, offset + size - 1))); + if (!unknown.isEmpty()) { + warnUnknown(unknown); + } } // TODO: What to flush when bytes in the trace change? diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/emulation/ReadsTargetMemoryPcodeExecutorState.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/emulation/ReadsTargetMemoryPcodeExecutorState.java index 020c0f46d1..75ea77a7ba 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/emulation/ReadsTargetMemoryPcodeExecutorState.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/emulation/ReadsTargetMemoryPcodeExecutorState.java @@ -62,7 +62,6 @@ public class ReadsTargetMemoryPcodeExecutorState return; } } - Msg.warn(this, "Emulator read from UNKNOWN state: " + unknown); } protected boolean fillUnknownWithRecorder(AddressSet unknown) { @@ -92,7 +91,7 @@ public class ReadsTargetMemoryPcodeExecutorState long shift = mappedRng.getShift(); for (AddressRange subsrng : initialized.intersectRange(srng.getMinAddress(), srng.getMaxAddress())) { - Msg.warn(this, + Msg.debug(this, "Filling in unknown trace memory in emulator using mapped image: " + program + ": " + subsrng); long lower = subsrng.getMinAddress().getOffset(); @@ -107,6 +106,7 @@ public class ReadsTargetMemoryPcodeExecutorState " Partial read of " + subsrng + ". Got " + read + " bytes"); } + // write(lower - shift, data, 0 ,read); cache.putData(lower - shift, data, 0, read); } catch (MemoryAccessException | AddressOutOfBoundsException e) { diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/pcode/exec/trace/AbstractCheckedTraceCachedWriteBytesPcodeExecutorState.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/pcode/exec/trace/AbstractCheckedTraceCachedWriteBytesPcodeExecutorState.java index ef224fa769..b9d37a8915 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/pcode/exec/trace/AbstractCheckedTraceCachedWriteBytesPcodeExecutorState.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/pcode/exec/trace/AbstractCheckedTraceCachedWriteBytesPcodeExecutorState.java @@ -15,7 +15,6 @@ */ package ghidra.pcode.exec.trace; -import com.google.common.collect.Range; import com.google.common.collect.RangeSet; import com.google.common.primitives.UnsignedLong; @@ -32,20 +31,6 @@ public abstract class AbstractCheckedTraceCachedWriteBytesPcodeExecutorState super(space, source, snap); } - protected AddressRange addrRng(Range rng) { - Address start = space.getAddress(lower(rng)); - Address end = space.getAddress(upper(rng)); - return new AddressRangeImpl(start, end); - } - - protected AddressSet addrSet(RangeSet set) { - AddressSet result = new AddressSet(); - for (Range rng : set.asRanges()) { - result.add(addrRng(rng)); - } - return result; - } - @Override public byte[] read(long offset, int size) { RangeSet uninitialized = diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/pcode/exec/trace/TraceCachedWriteBytesPcodeExecutorState.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/pcode/exec/trace/TraceCachedWriteBytesPcodeExecutorState.java index cf57026461..4de3b9cfc1 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/pcode/exec/trace/TraceCachedWriteBytesPcodeExecutorState.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/pcode/exec/trace/TraceCachedWriteBytesPcodeExecutorState.java @@ -16,8 +16,7 @@ package ghidra.pcode.exec.trace; import java.nio.ByteBuffer; -import java.util.HashMap; -import java.util.Map; +import java.util.*; import com.google.common.collect.*; import com.google.common.primitives.UnsignedLong; @@ -27,8 +26,9 @@ import ghidra.pcode.exec.AbstractLongOffsetPcodeExecutorState; import ghidra.pcode.exec.BytesPcodeArithmetic; import ghidra.pcode.exec.trace.TraceCachedWriteBytesPcodeExecutorState.CachedSpace; import ghidra.pcode.utils.Utils; -import ghidra.program.model.address.Address; -import ghidra.program.model.address.AddressSpace; +import ghidra.program.model.address.*; +import ghidra.program.model.lang.Language; +import ghidra.program.model.lang.Register; import ghidra.program.model.mem.MemBuffer; import ghidra.program.model.mem.Memory; import ghidra.trace.model.Trace; @@ -36,6 +36,7 @@ import ghidra.trace.model.memory.TraceMemorySpace; import ghidra.trace.model.thread.TraceThread; import ghidra.trace.util.MemBufferAdapter; import ghidra.util.MathUtilities; +import ghidra.util.Msg; /** * A state which reads bytes from a trace, but caches writes internally. @@ -109,10 +110,10 @@ public class TraceCachedWriteBytesPcodeExecutorState this.snap = snap; } - public void write(long offset, byte[] val) { - cache.putData(offset, val); + public void write(long offset, byte[] buffer, int srcOffset, int length) { + cache.putData(offset, buffer, srcOffset, length); UnsignedLong uLoc = UnsignedLong.fromLongBits(offset); - UnsignedLong uEnd = UnsignedLong.fromLongBits(offset + val.length); + UnsignedLong uEnd = UnsignedLong.fromLongBits(offset + length); written.add(Range.closedOpen(uLoc, uEnd)); } @@ -150,6 +151,62 @@ public class TraceCachedWriteBytesPcodeExecutorState return data; } + protected AddressRange addrRng(Range rng) { + Address start = space.getAddress(lower(rng)); + Address end = space.getAddress(upper(rng)); + return new AddressRangeImpl(start, end); + } + + protected AddressSet addrSet(RangeSet set) { + AddressSet result = new AddressSet(); + for (Range rng : set.asRanges()) { + result.add(addrRng(rng)); + } + return result; + } + + protected Set getRegs(AddressSet set) { + // TODO: Should pass in language instead? + Language language = source.getTrace().getBaseLanguage(); + Set regs = new TreeSet<>(); + for (AddressRange rng : set) { + Register r = language.getRegister(rng.getMinAddress(), (int) rng.getLength()); + if (r != null) { + regs.add(r); + } + else { + regs.addAll(Arrays.asList(language.getRegisters(rng.getMinAddress()))); + } + } + return regs; + } + + protected void warnState(AddressSet set, String message) { + Set regs = getRegs(set); + if (regs.isEmpty()) { + Msg.warn(this, message + ": " + set); + } + else { + Msg.warn(this, message + ": " + set + " (registers " + regs + ")"); + } + } + + protected void warnUninit(RangeSet uninit) { + AddressSet uninitialized = addrSet(uninit); + Set regs = getRegs(uninitialized); + if (regs.isEmpty()) { + Msg.warn(this, "Emulator read from uninitialized state: " + uninit); + } + Msg.warn(this, "Emulator read from uninitialized state: " + uninit + + " (includes registers: " + regs + ")"); + } + + protected void warnUnknown(AddressSet unknown) { + Set regs = getRegs(unknown); + Msg.warn(this, "Emulator state initialized from UNKNOWN: " + unknown + + "(includes registers: " + regs + ")"); + } + public byte[] read(long offset, int size) { if (source != null) { // TODO: Warn or bail when reading UNKNOWN bytes @@ -157,6 +214,10 @@ public class TraceCachedWriteBytesPcodeExecutorState // NOTE: Cannot write those gaps, though!!! readUninitializedFromSource(cache.getUninitialized(offset, offset + size - 1)); } + RangeSet stillUninit = cache.getUninitialized(offset, offset + size - 1); + if (!stillUninit.isEmpty()) { + warnUninit(stillUninit); + } return readCached(offset, size); } @@ -251,7 +312,7 @@ public class TraceCachedWriteBytesPcodeExecutorState @Override protected void setInSpace(CachedSpace space, long offset, int size, byte[] val) { assert size == val.length; - space.write(offset, val); + space.write(offset, val, 0, val.length); } @Override diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/DBTraceUtils.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/DBTraceUtils.java index b26963eaaa..95ab01c72a 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/DBTraceUtils.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/DBTraceUtils.java @@ -19,7 +19,6 @@ import java.lang.reflect.Field; import java.net.MalformedURLException; import java.net.URL; import java.nio.ByteBuffer; -import java.nio.charset.Charset; import java.util.Iterator; import java.util.Objects; import java.util.function.BiConsumer; diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/program/DBTraceProgramViewMemorySpaceBlock.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/program/DBTraceProgramViewMemorySpaceBlock.java index 035cee92bf..4012121576 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/program/DBTraceProgramViewMemorySpaceBlock.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/program/DBTraceProgramViewMemorySpaceBlock.java @@ -15,16 +15,10 @@ */ package ghidra.trace.database.program; -import java.io.InputStream; -import java.util.*; - import ghidra.framework.store.LockException; -import ghidra.program.database.mem.ByteMappingScheme; -import ghidra.program.database.mem.FileBytes; -import ghidra.program.model.address.*; -import ghidra.program.model.mem.*; -import ghidra.trace.database.memory.DBTraceMemorySpace; -import ghidra.trace.model.memory.TraceMemorySpaceInputStream; +import ghidra.program.model.address.Address; +import ghidra.program.model.address.AddressSpace; +import ghidra.program.model.mem.MemoryBlock; public class DBTraceProgramViewMemorySpaceBlock extends AbstractDBTraceProgramViewMemoryBlock { diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/program/DBTraceProgramViewRootModule.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/program/DBTraceProgramViewRootModule.java index fc5d6b9f19..ffd6d1122e 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/program/DBTraceProgramViewRootModule.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/program/DBTraceProgramViewRootModule.java @@ -120,7 +120,7 @@ public class DBTraceProgramViewRootModule implements ProgramModule { try (LockHold hold = LockHold.lock(program.trace.getReadWriteLock().readLock())) { program.memory.forVisibleRegions(region -> names.add(region.getName())); } - return names.indexOf(names); + return names.indexOf(name); } @Override diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/program/DBTraceVariableSnapProgramView.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/program/DBTraceVariableSnapProgramView.java index e70952b179..9b42c75261 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/program/DBTraceVariableSnapProgramView.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/program/DBTraceVariableSnapProgramView.java @@ -15,8 +15,6 @@ */ package ghidra.trace.database.program; -import ghidra.framework.model.DomainObject; -import ghidra.framework.model.DomainObjectChangeRecord; import ghidra.program.model.lang.CompilerSpec; import ghidra.program.model.listing.CodeUnit; import ghidra.trace.database.DBTrace; diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/stack/DBTraceStackFrame.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/stack/DBTraceStackFrame.java index 5d5dd07c3a..2510b8612f 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/stack/DBTraceStackFrame.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/stack/DBTraceStackFrame.java @@ -21,7 +21,6 @@ import java.util.Objects; import db.DBRecord; import ghidra.lifecycle.Internal; import ghidra.program.model.address.Address; -import ghidra.program.model.address.AddressFactory; import ghidra.trace.database.address.DBTraceOverlaySpaceAdapter; import ghidra.trace.database.address.DBTraceOverlaySpaceAdapter.AddressDBFieldCodec; import ghidra.trace.database.address.DBTraceOverlaySpaceAdapter.DecodesAddresses; diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/symbol/AbstractDBTraceSymbol.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/symbol/AbstractDBTraceSymbol.java index 6284a5d9fc..fadde9a0e4 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/symbol/AbstractDBTraceSymbol.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/symbol/AbstractDBTraceSymbol.java @@ -61,6 +61,7 @@ public abstract class AbstractDBTraceSymbol extends DBAnnotatedObject private static final byte SOURCE_CLEAR = ~(SOURCE_MASK << SOURCE_SHIFT); private static final byte PRIMARY_MASK = 0x10; + @SuppressWarnings("unused") private static final int PRIMARY_CLEAR = ~PRIMARY_MASK; static final String NAME_COLUMN_NAME = "Name"; diff --git a/Ghidra/Debug/ProposedUtils/src/test/java/ghidra/generic/util/datastruct/SemisparseByteArrayTest.java b/Ghidra/Debug/ProposedUtils/src/test/java/ghidra/generic/util/datastruct/SemisparseByteArrayTest.java index 0743f5ca5e..1dd0636e7d 100644 --- a/Ghidra/Debug/ProposedUtils/src/test/java/ghidra/generic/util/datastruct/SemisparseByteArrayTest.java +++ b/Ghidra/Debug/ProposedUtils/src/test/java/ghidra/generic/util/datastruct/SemisparseByteArrayTest.java @@ -87,6 +87,16 @@ public class SemisparseByteArrayTest { assertEquals(HELLO_WORLD, new String(data)); } + @Test + public void testBoundaryAtUnsignedMax() { + SemisparseByteArray cache = new SemisparseByteArray(); + + cache.putData(-HW.length, HW); + byte[] data = new byte[HW.length]; + cache.getData(-HW.length, data); + assertEquals(HELLO_WORLD, new String(data)); + } + @Test public void testLarge() { Random rand = new Random();