goToSleigh(String spaceName, String expression) {
+ /**
+ * Go to the given address
+ *
+ *
+ * If parsing or evaluation fails, an exception is thrown, or the future completes
+ * exceptionally. If the address is successfully computed, then a result will be returned. The
+ * {@link GoToResult#address()} method gives the parsed or computed address. The
+ * {@link GoToResult#success()} method indicates whether the cursor was successfully set to that
+ * address.
+ *
+ * @param spaceName the name of the address space
+ * @param offset a simple offset or Sleigh expression
+ * @return the result
+ */
+ public CompletableFuture goTo(String spaceName, String offset) {
+ TracePlatform platform = current.getPlatform();
+ Language language = platform.getLanguage();
+ AddressSpace space = language.getAddressFactory().getAddressSpace(spaceName);
+ if (space == null) {
+ throw new IllegalArgumentException("No such address space: " + spaceName);
+ }
+ try {
+ Address address = space.getAddress(offset);
+ if (address == null) {
+ address = language.getAddressFactory().getAddress(offset);
+ }
+ if (address != null) {
+ return CompletableFuture
+ .completedFuture(new GoToResult(address, goToAddress(address)));
+ }
+ }
+ catch (AddressFormatException e) {
+ // Fall-through to try Sleigh
+ }
+ return goToSleigh(spaceName, offset);
+ }
+
+ protected CompletableFuture goToSleigh(String spaceName, String expression) {
TracePlatform platform = current.getPlatform();
Language language = platform.getLanguage();
if (!(language instanceof SleighLanguage)) {
throw new IllegalStateException("Current trace does not use Sleigh");
}
- SleighLanguage slang = (SleighLanguage) language;
AddressSpace space = language.getAddressFactory().getAddressSpace(spaceName);
if (space == null) {
throw new IllegalArgumentException("No such address space: " + spaceName);
}
- PcodeExpression expr = SleighProgramCompiler.compileExpression(slang, expression);
+ PcodeExpression expr = DebuggerPcodeUtils.compileExpression(tool, current, expression);
return goToSleigh(platform, space, expr);
}
- public CompletableFuture goToSleigh(TracePlatform platform, AddressSpace space,
+ protected CompletableFuture goToSleigh(TracePlatform platform, AddressSpace space,
PcodeExpression expression) {
PcodeExecutor executor = DebuggerPcodeUtils.executorForCoordinates(tool, current);
CompletableFuture result =
@@ -92,7 +135,7 @@ public abstract class DebuggerGoToTrait {
return result.thenApplyAsync(offset -> {
Address address = space.getAddress(
Utils.bytesToLong(offset, offset.length, expression.getLanguage().isBigEndian()));
- return goToAddress(platform.mapGuestToHost(address));
+ return new GoToResult(address, goToAddress(platform.mapGuestToHost(address)));
}, AsyncUtils.SWING_EXECUTOR);
}
}
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/action/DebuggerTrackLocationTrait.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/action/DebuggerTrackLocationTrait.java
index e9fcef819f..843d49f8ed 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/action/DebuggerTrackLocationTrait.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/action/DebuggerTrackLocationTrait.java
@@ -315,6 +315,13 @@ public class DebuggerTrackLocationTrait {
action.setCurrentActionStateByUserData(spec);
}
+ public GoToInput getDefaultGoToInput(ProgramLocation loc) {
+ if (tracker == null) {
+ return NoneLocationTrackingSpec.INSTANCE.getDefaultGoToInput(tool, current, loc);
+ }
+ return tracker.getDefaultGoToInput(tool, current, loc);
+ }
+
protected void locationTracked() {
// Listener method
}
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/action/GoToInput.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/action/GoToInput.java
new file mode 100644
index 0000000000..deede24f7d
--- /dev/null
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/action/GoToInput.java
@@ -0,0 +1,36 @@
+/* ###
+ * 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.app.plugin.core.debug.gui.action;
+
+import ghidra.program.model.address.Address;
+
+public record GoToInput(String space, String offset) {
+ public static GoToInput fromString(String string) {
+ if (string.contains(":")) {
+ String[] parts = string.split(":", 2);
+ return new GoToInput(parts[0], parts[1]);
+ }
+ return new GoToInput(null, string);
+ }
+
+ public static GoToInput fromAddress(Address address) {
+ return new GoToInput(address.getAddressSpace().getName(), address.toString(false));
+ }
+
+ public static GoToInput offsetOnly(String offset) {
+ return new GoToInput(null, offset);
+ }
+}
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/action/LocationTracker.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/action/LocationTracker.java
index 6fa2bb3acc..b2750c8ca9 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/action/LocationTracker.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/action/LocationTracker.java
@@ -20,6 +20,7 @@ import java.util.concurrent.CompletableFuture;
import ghidra.app.plugin.core.debug.DebuggerCoordinates;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.address.Address;
+import ghidra.program.util.ProgramLocation;
import ghidra.trace.model.TraceAddressSnapRange;
import ghidra.trace.model.guest.TracePlatform;
import ghidra.trace.model.stack.TraceStack;
@@ -53,6 +54,17 @@ public interface LocationTracker {
CompletableFuture computeTraceAddress(PluginTool tool,
DebuggerCoordinates coordinates);
+ /**
+ * Get the suggested input if the user activates "Go To" while this tracker is active
+ *
+ * @param tool the tool containing the provider
+ * @param coordinates the user's current coordinates
+ * @param location the user's current location
+ * @return the suggested address or Sleigh expression
+ */
+ GoToInput getDefaultGoToInput(PluginTool tool, DebuggerCoordinates coordinates,
+ ProgramLocation location);
+
// TODO: Is there a way to generalize these so that other dependencies need not
// have their own bespoke methods?
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/action/NoneLocationTrackingSpec.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/action/NoneLocationTrackingSpec.java
index f41121d0e8..443cb5896a 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/action/NoneLocationTrackingSpec.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/action/NoneLocationTrackingSpec.java
@@ -24,6 +24,7 @@ import ghidra.app.plugin.core.debug.gui.DebuggerResources.TrackLocationAction;
import ghidra.async.AsyncUtils;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.address.Address;
+import ghidra.program.util.ProgramLocation;
import ghidra.trace.model.TraceAddressSnapRange;
import ghidra.trace.model.stack.TraceStack;
import ghidra.trace.util.TraceAddressSpace;
@@ -69,6 +70,15 @@ public enum NoneLocationTrackingSpec implements LocationTrackingSpec, LocationTr
return AsyncUtils.nil();
}
+ @Override
+ public GoToInput getDefaultGoToInput(PluginTool tool, DebuggerCoordinates coordinates,
+ ProgramLocation location) {
+ if (location == null) {
+ return GoToInput.fromString("00000000");
+ }
+ return GoToInput.fromAddress(location.getAddress());
+ }
+
@Override
public boolean affectedByBytesChange(TraceAddressSpace space,
TraceAddressSnapRange range, DebuggerCoordinates coordinates) {
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/action/PCByStackLocationTrackingSpec.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/action/PCByStackLocationTrackingSpec.java
index 996552df13..dee6cd3193 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/action/PCByStackLocationTrackingSpec.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/action/PCByStackLocationTrackingSpec.java
@@ -23,6 +23,7 @@ import ghidra.app.plugin.core.debug.DebuggerCoordinates;
import ghidra.app.plugin.core.debug.gui.DebuggerResources.TrackLocationAction;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.address.Address;
+import ghidra.program.util.ProgramLocation;
import ghidra.trace.model.Trace;
import ghidra.trace.model.TraceAddressSnapRange;
import ghidra.trace.model.stack.TraceStack;
@@ -87,6 +88,17 @@ public enum PCByStackLocationTrackingSpec implements LocationTrackingSpec, Locat
return CompletableFuture.supplyAsync(() -> doComputeTraceAddress(tool, coordinates));
}
+ @Override
+ public GoToInput getDefaultGoToInput(PluginTool tool, DebuggerCoordinates coordinates,
+ ProgramLocation location) {
+ Address address = doComputeTraceAddress(tool, coordinates);
+ if (address == null) {
+ return NoneLocationTrackingSpec.INSTANCE.getDefaultGoToInput(tool, coordinates,
+ location);
+ }
+ return GoToInput.fromAddress(address);
+ }
+
// Note it does no good to override affectByRegChange. It must do what we'd avoid anyway.
@Override
public boolean affectedByStackChange(TraceStack stack, DebuggerCoordinates coordinates) {
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/action/PCLocationTrackingSpec.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/action/PCLocationTrackingSpec.java
index c2b69303cb..511800da22 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/action/PCLocationTrackingSpec.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/action/PCLocationTrackingSpec.java
@@ -23,6 +23,7 @@ import ghidra.app.plugin.core.debug.DebuggerCoordinates;
import ghidra.app.plugin.core.debug.gui.DebuggerResources.TrackLocationAction;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.address.Address;
+import ghidra.program.util.ProgramLocation;
import ghidra.trace.model.TraceAddressSnapRange;
import ghidra.trace.model.stack.TraceStack;
import ghidra.trace.util.TraceAddressSpace;
@@ -81,6 +82,12 @@ public enum PCLocationTrackingSpec implements LocationTrackingSpec, LocationTrac
});
}
+ @Override
+ public GoToInput getDefaultGoToInput(PluginTool tool, DebuggerCoordinates coordinates,
+ ProgramLocation location) {
+ return BY_REG.getDefaultGoToInput(tool, coordinates, location);
+ }
+
// Note it does no good to override affectByRegChange. It must do what we'd avoid anyway.
@Override
public boolean affectedByStackChange(TraceStack stack, DebuggerCoordinates coordinates) {
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/action/RegisterLocationTrackingSpec.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/action/RegisterLocationTrackingSpec.java
index 8af97f5b7c..7087147a73 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/action/RegisterLocationTrackingSpec.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/action/RegisterLocationTrackingSpec.java
@@ -22,6 +22,7 @@ import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.address.*;
import ghidra.program.model.lang.Register;
import ghidra.program.model.lang.RegisterValue;
+import ghidra.program.util.ProgramLocation;
import ghidra.trace.model.Trace;
import ghidra.trace.model.TraceAddressSnapRange;
import ghidra.trace.model.guest.TracePlatform;
@@ -91,6 +92,13 @@ public interface RegisterLocationTrackingSpec extends LocationTrackingSpec, Loca
return CompletableFuture.supplyAsync(() -> doComputeTraceAddress(tool, coordinates));
}
+ @Override
+ default GoToInput getDefaultGoToInput(PluginTool tool, DebuggerCoordinates coordinates,
+ ProgramLocation location) {
+ Register register = computeRegister(coordinates);
+ return GoToInput.offsetOnly(register.getName());
+ }
+
@Override
default boolean affectedByBytesChange(TraceAddressSpace space,
TraceAddressSnapRange range, DebuggerCoordinates coordinates) {
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/action/WatchLocationTrackingSpec.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/action/WatchLocationTrackingSpec.java
index 527b200a63..62afdfc624 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/action/WatchLocationTrackingSpec.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/action/WatchLocationTrackingSpec.java
@@ -24,15 +24,16 @@ import ghidra.app.plugin.core.debug.DebuggerCoordinates;
import ghidra.app.plugin.core.debug.gui.DebuggerResources;
import ghidra.app.plugin.core.debug.gui.DebuggerResources.TrackLocationAction;
import ghidra.app.plugin.core.debug.gui.watch.WatchRow;
-import ghidra.app.plugin.processors.sleigh.SleighLanguage;
import ghidra.async.AsyncUtils;
import ghidra.framework.plugintool.PluginTool;
import ghidra.pcode.exec.*;
import ghidra.pcode.exec.DebuggerPcodeUtils.WatchValue;
+import ghidra.pcode.exec.SleighUtils.AddressOf;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSetView;
-import ghidra.program.model.lang.Language;
+import ghidra.program.util.ProgramLocation;
import ghidra.trace.model.TraceAddressSnapRange;
+import ghidra.trace.model.guest.TracePlatform;
import ghidra.trace.model.stack.TraceStack;
import ghidra.trace.util.TraceAddressSpace;
@@ -44,6 +45,11 @@ public class WatchLocationTrackingSpec implements LocationTrackingSpec {
public static final String CONFIG_PREFIX = "TRACK_WATCH_";
private final String expression;
+ private final String label;
+
+ public static boolean isTrackable(WatchRow watch) {
+ return SleighUtils.recoverAddressOf(null, watch.getExpression()) != null;
+ }
/**
* Derive a tracking specification from the given watch
@@ -62,6 +68,8 @@ public class WatchLocationTrackingSpec implements LocationTrackingSpec {
*/
public WatchLocationTrackingSpec(String expression) {
this.expression = expression;
+ AddressOf addrOf = SleighUtils.recoverAddressOf(null, expression);
+ this.label = SleighUtils.generateSleighExpression(addrOf.offset());
}
@Override
@@ -97,7 +105,7 @@ public class WatchLocationTrackingSpec implements LocationTrackingSpec {
@Override
public String getLocationLabel() {
- return "&watch";
+ return label;
}
/**
@@ -124,18 +132,27 @@ public class WatchLocationTrackingSpec implements LocationTrackingSpec {
return AsyncUtils.nil();
}
return CompletableFuture.supplyAsync(() -> {
- Language language = current.getPlatform().getLanguage();
- if (!(language instanceof SleighLanguage slang)) {
- return null;
- }
- if (compiled == null || compiled.getLanguage() != language) {
- compiled = SleighProgramCompiler.compileExpression(slang, expression);
- }
+ compiled = DebuggerPcodeUtils.compileExpression(tool, current, expression);
WatchValue value = compiled.evaluate(asyncExec);
return value == null ? null : value.address();
});
}
+ @Override
+ public GoToInput getDefaultGoToInput(PluginTool tool, DebuggerCoordinates coordinates,
+ ProgramLocation location) {
+ TracePlatform platform = current.getPlatform();
+ String defaultSpace =
+ platform == null ? "ram" : platform.getLanguage().getDefaultSpace().getName();
+ AddressOf addrOf = SleighUtils.recoverAddressOf(defaultSpace, expression);
+ if (addrOf == null) {
+ return NoneLocationTrackingSpec.INSTANCE.getDefaultGoToInput(tool, coordinates,
+ location);
+ }
+ return new GoToInput(addrOf.space(),
+ SleighUtils.generateSleighExpression(addrOf.offset()));
+ }
+
@Override
public boolean affectedByBytesChange(TraceAddressSpace space, TraceAddressSnapRange range,
DebuggerCoordinates coordinates) {
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/action/WatchLocationTrackingSpecFactory.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/action/WatchLocationTrackingSpecFactory.java
index c5f4c7ad50..874afb0b82 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/action/WatchLocationTrackingSpecFactory.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/action/WatchLocationTrackingSpecFactory.java
@@ -38,6 +38,7 @@ public class WatchLocationTrackingSpecFactory implements LocationTrackingSpecFac
}
return watchesService.getWatches()
.stream()
+ .filter(WatchLocationTrackingSpec::isTrackable)
.map(WatchLocationTrackingSpec::fromWatch)
.collect(Collectors.toList());
}
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/listing/DebuggerListingProvider.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/listing/DebuggerListingProvider.java
index 148a749354..516e2393cd 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/listing/DebuggerListingProvider.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/listing/DebuggerListingProvider.java
@@ -184,8 +184,16 @@ public class DebuggerListingProvider extends CodeViewerProvider {
DebuggerListingProvider.this);
}
+ @Override
+ protected GoToInput getDefaultInput() {
+ return trackingTrait.getDefaultGoToInput(getLocation());
+ }
+
@Override
protected boolean goToAddress(Address address) {
+ if (syncTrait.isAutoSyncCursorWithStaticListing()) {
+ syncTrait.doAutoSyncCursorIntoStatic(new ProgramLocation(getProgram(), address));
+ }
return getListingPanel().goTo(address);
}
}
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/memory/DebuggerMemoryBytesProvider.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/memory/DebuggerMemoryBytesProvider.java
index 4774bb093a..8f20f1dc9a 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/memory/DebuggerMemoryBytesProvider.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/memory/DebuggerMemoryBytesProvider.java
@@ -103,13 +103,18 @@ public class DebuggerMemoryBytesProvider extends ProgramByteViewerComponentProvi
DebuggerMemoryBytesProvider.this);
}
+ @Override
+ protected GoToInput getDefaultInput() {
+ return trackingTrait.getDefaultGoToInput(currentLocation);
+ }
+
@Override
protected boolean goToAddress(Address address) {
TraceProgramView view = current.getView();
if (view == null) {
return false;
}
- return goTo(view, new ProgramLocation(view, address));
+ return DebuggerMemoryBytesProvider.this.goTo(view, new ProgramLocation(view, address));
}
}
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/watch/WatchRow.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/watch/WatchRow.java
index 06184ea615..436536ff30 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/watch/WatchRow.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/watch/WatchRow.java
@@ -21,7 +21,6 @@ import java.util.concurrent.CompletableFuture;
import db.Transaction;
import ghidra.app.plugin.core.debug.DebuggerCoordinates;
-import ghidra.app.plugin.processors.sleigh.SleighLanguage;
import ghidra.app.services.DataTypeManagerService;
import ghidra.app.services.DebuggerControlService;
import ghidra.app.services.DebuggerControlService.StateEditor;
@@ -96,7 +95,8 @@ public class WatchRow {
return;
}
try {
- compiled = SleighProgramCompiler.compileExpression(provider.language, expression);
+ compiled = DebuggerPcodeUtils.compileExpression(provider.getTool(), provider.current,
+ expression);
}
catch (Exception e) {
error = e;
@@ -106,7 +106,6 @@ public class WatchRow {
protected void reevaluate() {
blank();
- SleighLanguage language = provider.language;
PcodeExecutor executor = provider.asyncWatchExecutor;
PcodeExecutor prevExec = provider.prevValueExecutor;
if (executor == null) {
@@ -114,9 +113,7 @@ public class WatchRow {
return;
}
CompletableFuture.runAsync(() -> {
- if (compiled == null || compiled.getLanguage() != language) {
- recompile();
- }
+ recompile();
if (compiled == null) {
provider.contextChanged();
return;
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/pcode/exec/DebuggerPcodeUtils.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/pcode/exec/DebuggerPcodeUtils.java
index b8948a7873..177857ed5f 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/pcode/exec/DebuggerPcodeUtils.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/pcode/exec/DebuggerPcodeUtils.java
@@ -22,21 +22,34 @@ import java.util.Map.Entry;
import ghidra.app.plugin.core.debug.DebuggerCoordinates;
import ghidra.app.plugin.core.debug.service.emulation.*;
import ghidra.app.plugin.core.debug.service.emulation.data.DefaultPcodeDebuggerAccess;
+import ghidra.app.plugin.processors.sleigh.SleighException;
import ghidra.app.plugin.processors.sleigh.SleighLanguage;
+import ghidra.app.services.DebuggerStaticMappingService;
import ghidra.framework.plugintool.PluginTool;
import ghidra.pcode.emu.ThreadPcodeExecutorState;
import ghidra.pcode.exec.PcodeArithmetic.Purpose;
import ghidra.pcode.exec.PcodeExecutorStatePiece.Reason;
+import ghidra.pcode.exec.SleighProgramCompiler.ErrorCollectingPcodeParser;
import ghidra.pcode.exec.trace.*;
import ghidra.pcode.exec.trace.data.DefaultPcodeTraceAccess;
import ghidra.pcode.exec.trace.data.PcodeTraceDataAccess;
import ghidra.pcode.utils.Utils;
+import ghidra.pcodeCPort.slghsymbol.SleighSymbol;
+import ghidra.pcodeCPort.slghsymbol.VarnodeSymbol;
import ghidra.program.model.address.*;
import ghidra.program.model.lang.*;
+import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.MemBuffer;
+import ghidra.program.model.symbol.Symbol;
+import ghidra.program.model.symbol.SymbolType;
+import ghidra.program.util.ProgramLocation;
+import ghidra.sleigh.grammar.Location;
import ghidra.trace.model.Trace;
+import ghidra.trace.model.TraceLocation;
import ghidra.trace.model.guest.TracePlatform;
import ghidra.trace.model.memory.TraceMemoryState;
+import ghidra.trace.model.symbol.TraceSymbol;
+import ghidra.trace.model.symbol.TraceSymbolWithLifespan;
import ghidra.util.NumericUtilities;
/**
@@ -45,6 +58,120 @@ import ghidra.util.NumericUtilities;
public enum DebuggerPcodeUtils {
;
+ /**
+ * A p-code parser that can resolve labels from a trace or its mapped programs.
+ */
+ public static class LabelBoundPcodeParser extends ErrorCollectingPcodeParser {
+ record ProgSym(String sourceName, String nm, Address address) {
+ }
+
+ private final DebuggerStaticMappingService mappings;
+ private final DebuggerCoordinates coordinates;
+
+ /**
+ * Construct a parser bound to the given coordinates
+ *
+ * @param tool the tool for the mapping service
+ * @param coordinates the current coordinates for context
+ */
+ public LabelBoundPcodeParser(PluginTool tool, DebuggerCoordinates coordinates) {
+ super((SleighLanguage) coordinates.getPlatform().getLanguage());
+ this.mappings = tool.getService(DebuggerStaticMappingService.class);
+ this.coordinates = coordinates;
+ }
+
+ protected SleighSymbol createSleighConstant(String sourceName, String nm, Address address) {
+ return new VarnodeSymbol(new Location(sourceName, 0), nm, getConstantSpace(),
+ address.getOffset(), address.getAddressSpace().getPointerSize());
+ }
+
+ @Override
+ public SleighSymbol findSymbol(String nm) {
+ SleighSymbol symbol = null;
+ try {
+ symbol = super.findSymbol(nm);
+ }
+ catch (SleighException e) {
+ // leave null
+ }
+ if (symbol == null) {
+ symbol = findUserSymbol(nm);
+ }
+ if (symbol == null) {
+ throw new SleighException("Unknown register or label: '" + nm + "'");
+ }
+ return symbol;
+ }
+
+ protected SleighSymbol findUserSymbol(String nm) {
+ Trace trace = coordinates.getTrace();
+ long snap = coordinates.getSnap();
+ for (TraceSymbol symbol : trace.getSymbolManager()
+ .labelsAndFunctions()
+ .getNamed(nm)) {
+ if (symbol instanceof TraceSymbolWithLifespan lifeSym &&
+ !lifeSym.getLifespan().contains(snap)) {
+ continue;
+ }
+ return createSleighConstant(trace.getName(), nm, symbol.getAddress());
+ }
+ for (Program program : mappings.getOpenMappedProgramsAtSnap(trace, snap)) {
+ for (Symbol symbol : program.getSymbolTable().getSymbols(nm)) {
+ if (symbol.isDynamic() || symbol.isExternal()) {
+ continue;
+ }
+ if (symbol.getSymbolType() != SymbolType.FUNCTION &&
+ symbol.getSymbolType() != SymbolType.LABEL) {
+ continue;
+ }
+ TraceLocation tloc = mappings.getOpenMappedLocation(trace,
+ new ProgramLocation(program, symbol.getAddress()), snap);
+ if (tloc == null) {
+ return null;
+ }
+ return createSleighConstant(program.getName(), nm, tloc.getAddress());
+ }
+ }
+ return null;
+ }
+ }
+
+ /**
+ * Compile the given Sleigh source into a p-code program, resolving user labels
+ *
+ *
+ * The resulting program must only be used with a state bound to the same coordinates. Any
+ * symbols which are resolved to labels in the trace or its mapped programs are effectively
+ * substituted for their offsets. If a label moves, the program should be recompiled in order to
+ * update those substitutions.
+ *
+ * @param tool the tool for context
+ * @param coordinates the coordinates for the trace (and programs) from which labels can be
+ * resolved
+ * @see SleighProgramCompiler#compileProgram(PcodeParser, SleighLanguage, String, String,
+ * PcodeUseropLibrary)
+ */
+ public static PcodeProgram compileProgram(PluginTool tool, DebuggerCoordinates coordinates,
+ String sourceName, String source, PcodeUseropLibrary> library) {
+ return SleighProgramCompiler.compileProgram(new LabelBoundPcodeParser(tool, coordinates),
+ (SleighLanguage) coordinates.getPlatform().getLanguage(), sourceName, source, library);
+ }
+
+ /**
+ * Compile the given Sleigh expression into a p-code program, resolving user labels
+ *
+ *
+ * This has the same limitations as
+ * {@link #compileProgram(PluginTool, DebuggerCoordinates, String, String, PcodeUseropLibrary)}
+ *
+ * @see SleighProgramCompiler#compileExpression(PcodeParser, SleighLanguage, String)
+ */
+ public static PcodeExpression compileExpression(PluginTool tool,
+ DebuggerCoordinates coordinates, String source) {
+ return SleighProgramCompiler.compileExpression(new LabelBoundPcodeParser(tool, coordinates),
+ (SleighLanguage) coordinates.getPlatform().getLanguage(), source);
+ }
+
/**
* Get a p-code executor state for the given coordinates
*
@@ -76,7 +203,8 @@ public enum DebuggerPcodeUtils {
return shared;
}
PcodeExecutorState local = new RWTargetRegistersPcodeExecutorState(
- access.getDataForLocalState(coordinates.getThread(), coordinates.getFrame()), Mode.RW);
+ access.getDataForLocalState(coordinates.getThread(), coordinates.getFrame()),
+ Mode.RW);
return new ThreadPcodeExecutorState<>(shared, local) {
@Override
public void clear() {
@@ -319,7 +447,8 @@ public enum DebuggerPcodeUtils {
bytes.binaryOp(opcode, sizeout, sizein1, in1.bytes.bytes, sizein2,
in2.bytes.bytes)),
STATE.binaryOp(opcode, sizeout, sizein1, in1.state, sizein2, in2.state),
- location.binaryOp(opcode, sizeout, sizein1, in1.location, sizein2, in2.location),
+ location.binaryOp(opcode, sizeout, sizein1, in1.location, sizein2,
+ in2.location),
READS.binaryOp(opcode, sizeout, sizein1, in1.reads, sizein2, in2.reads));
}
@@ -495,7 +624,8 @@ public enum DebuggerPcodeUtils {
}
@Override
- public WatchValue getVar(AddressSpace space, WatchValue offset, int size, boolean quantize,
+ public WatchValue getVar(AddressSpace space, WatchValue offset, int size,
+ boolean quantize,
Reason reason) {
return piece.getVar(space, offset.bytes.bytes, size, quantize, reason);
}
diff --git a/Ghidra/Debug/Debugger/src/screen/java/ghidra/app/plugin/core/debug/gui/listing/DebuggerListingPluginScreenShots.java b/Ghidra/Debug/Debugger/src/screen/java/ghidra/app/plugin/core/debug/gui/listing/DebuggerListingPluginScreenShots.java
index eaeda76b88..841be8a71c 100644
--- a/Ghidra/Debug/Debugger/src/screen/java/ghidra/app/plugin/core/debug/gui/listing/DebuggerListingPluginScreenShots.java
+++ b/Ghidra/Debug/Debugger/src/screen/java/ghidra/app/plugin/core/debug/gui/listing/DebuggerListingPluginScreenShots.java
@@ -135,7 +135,7 @@ public class DebuggerListingPluginScreenShots extends GhidraScreenShotGenerator
performAction(listingProvider.actionGoTo, false);
DebuggerGoToDialog dialog = waitForDialogComponent(DebuggerGoToDialog.class);
- dialog.setExpression("RAX");
+ dialog.setOffset("RAX");
captureDialog(dialog);
}
diff --git a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/listing/DebuggerListingProviderTest.java b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/listing/DebuggerListingProviderTest.java
index 0aed8791e5..4366be04a0 100644
--- a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/listing/DebuggerListingProviderTest.java
+++ b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/listing/DebuggerListingProviderTest.java
@@ -844,23 +844,31 @@ public class DebuggerListingProviderTest extends AbstractGhidraHeadedDebuggerGUI
waitForSwing();
assertTrue(listingProvider.actionGoTo.isEnabled());
+
performAction(listingProvider.actionGoTo, false);
DebuggerGoToDialog dialog1 = waitForDialogComponent(DebuggerGoToDialog.class);
runSwing(() -> {
- dialog1.setExpression("r0");
+ dialog1.setOffset("00400123");
dialog1.okCallback();
});
-
waitForPass(
- () -> assertEquals(tb.addr(0x00401234), listingProvider.getLocation().getAddress()));
+ () -> assertEquals(tb.addr(0x00400123), listingProvider.getLocation().getAddress()));
performAction(listingProvider.actionGoTo, false);
DebuggerGoToDialog dialog2 = waitForDialogComponent(DebuggerGoToDialog.class);
runSwing(() -> {
- dialog2.setExpression("*:4 r0");
+ dialog2.setOffset("r0");
dialog2.okCallback();
});
+ waitForPass(
+ () -> assertEquals(tb.addr(0x00401234), listingProvider.getLocation().getAddress()));
+ performAction(listingProvider.actionGoTo, false);
+ DebuggerGoToDialog dialog3 = waitForDialogComponent(DebuggerGoToDialog.class);
+ runSwing(() -> {
+ dialog3.setOffset("*:4 r0");
+ dialog3.okCallback();
+ });
waitForPass(
() -> assertEquals(tb.addr(0x00404321), listingProvider.getLocation().getAddress()));
}
diff --git a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/memory/DebuggerMemoryBytesProviderTest.java b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/memory/DebuggerMemoryBytesProviderTest.java
index 724f7888ad..233d8f73d0 100644
--- a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/memory/DebuggerMemoryBytesProviderTest.java
+++ b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/memory/DebuggerMemoryBytesProviderTest.java
@@ -629,7 +629,7 @@ public class DebuggerMemoryBytesProviderTest extends AbstractGhidraHeadedDebugge
performAction(memBytesProvider.actionGoTo, false);
DebuggerGoToDialog dialog1 = waitForDialogComponent(DebuggerGoToDialog.class);
runSwing(() -> {
- dialog1.setExpression("r0");
+ dialog1.setOffset("r0");
dialog1.okCallback();
});
@@ -642,7 +642,7 @@ public class DebuggerMemoryBytesProviderTest extends AbstractGhidraHeadedDebugge
performAction(memBytesProvider.actionGoTo, false);
DebuggerGoToDialog dialog2 = waitForDialogComponent(DebuggerGoToDialog.class);
runSwing(() -> {
- dialog2.setExpression("*:4 r0");
+ dialog2.setOffset("*:4 r0");
dialog2.okCallback();
});
diff --git a/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/exec/SleighProgramCompiler.java b/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/exec/SleighProgramCompiler.java
index 54e80fe237..ae623db85a 100644
--- a/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/exec/SleighProgramCompiler.java
+++ b/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/exec/SleighProgramCompiler.java
@@ -16,11 +16,12 @@
package ghidra.pcode.exec;
import java.io.IOException;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
+import java.util.stream.Collectors;
import ghidra.app.plugin.processors.sleigh.*;
import ghidra.app.plugin.processors.sleigh.template.ConstructTpl;
+import ghidra.pcode.utils.MessageFormattingUtils;
import ghidra.pcodeCPort.pcoderaw.VarnodeData;
import ghidra.pcodeCPort.sleighbase.SleighBase;
import ghidra.pcodeCPort.slghsymbol.*;
@@ -47,6 +48,94 @@ public enum SleighProgramCompiler {
private static final String EXPRESSION_SOURCE_NAME = "expression";
public static final String NIL_SYMBOL_NAME = "__nil";
+ public interface PcodeLogEntry {
+ public static String formatList(List list) {
+ return list.stream().map(e -> e.format()).collect(Collectors.joining("\n"));
+ }
+
+ Location loc();
+
+ String msg();
+
+ String type();
+
+ default String format() {
+ return "%s: %s".formatted(type(), MessageFormattingUtils.format(loc(), msg()));
+ }
+ }
+
+ record PcodeError(Location loc, String msg) implements PcodeLogEntry {
+ @Override
+ public String type() {
+ return "ERROR";
+ }
+ }
+
+ record PcodeWarning(Location loc, String msg) implements PcodeLogEntry {
+ @Override
+ public String type() {
+ return "WARNING";
+ }
+ }
+
+ public static class DetailedSleighException extends SleighException {
+ private final List details;
+
+ public DetailedSleighException(List details) {
+ super(PcodeLogEntry.formatList(details));
+ this.details = List.copyOf(details);
+ }
+
+ public List getDetails() {
+ return details;
+ }
+ }
+
+ /**
+ * A p-code parser that provides programmatic access to error diagnostics.
+ */
+ public static class ErrorCollectingPcodeParser extends PcodeParser {
+ private final List entries = new ArrayList<>();
+
+ public ErrorCollectingPcodeParser(SleighLanguage language) {
+ super(language, UniqueLayout.INJECT.getOffset(language));
+ }
+
+ @Override
+ public void reportError(Location location, String msg) {
+ entries.add(new PcodeError(location, msg));
+ super.reportError(location, msg);
+ }
+
+ @Override
+ public void reportWarning(Location location, String msg) {
+ entries.add(new PcodeWarning(location, msg));
+ super.reportWarning(location, msg);
+ }
+
+ @Override
+ public ConstructTpl compilePcode(String pcodeStatements, String srcFile, int srcLine)
+ throws SleighException {
+ try {
+ return super.compilePcode(pcodeStatements, srcFile, srcLine);
+ }
+ finally {
+ if (getErrors() != 0) {
+ throw new DetailedSleighException(entries);
+ }
+ }
+ }
+
+ @Override
+ public SleighSymbol findSymbol(String nm) {
+ SleighSymbol symbol = super.findSymbol(nm);
+ if (symbol == null) {
+ throw new SleighException("Unknown register: '" + nm + "'");
+ }
+ return symbol;
+ }
+ }
+
/**
* Create a p-code parser for the given language
*
@@ -54,7 +143,7 @@ public enum SleighProgramCompiler {
* @return a parser
*/
public static PcodeParser createParser(SleighLanguage language) {
- return new PcodeParser(language, UniqueLayout.INJECT.getOffset(language));
+ return new ErrorCollectingPcodeParser(language);
}
/**
@@ -69,7 +158,7 @@ public enum SleighProgramCompiler {
*/
public static ConstructTpl compileTemplate(Language language, PcodeParser parser,
String sourceName, String source) {
- return parser.compilePcode(source, EXPRESSION_SOURCE_NAME, 1);
+ return parser.compilePcode(source, sourceName, 1);
}
/**
@@ -162,22 +251,22 @@ public enum SleighProgramCompiler {
}
/**
- * Compile the given Sleigh source into a simple p-code program
+ * Compile the given Sleigh source into a simple p-code program with the given parser
*
*
* This is suitable for modifying program state using Sleigh statements. Most likely, in
* scripting, or perhaps in a Sleigh repl. The library given during compilation must match the
* library given for execution, at least in its binding of userop IDs to symbols.
*
+ * @param the parser to use
* @param language the language of the target p-code machine
* @param sourceName a diagnostic name for the Sleigh source
* @param source the Sleigh source
* @param library the userop library or stub library for binding userop symbols
* @return the compiled p-code program
*/
- public static PcodeProgram compileProgram(SleighLanguage language, String sourceName,
- String source, PcodeUseropLibrary> library) {
- PcodeParser parser = createParser(language);
+ public static PcodeProgram compileProgram(PcodeParser parser, SleighLanguage language,
+ String sourceName, String source, PcodeUseropLibrary> library) {
Map symbols = library.getSymbols(language);
addParserSymbols(parser, symbols);
@@ -186,7 +275,18 @@ public enum SleighProgramCompiler {
}
/**
- * Compile the given Sleigh expression into a p-code program that can evaluate it
+ * Compile the given Sleigh source into a simple p-code program
+ *
+ * @see #compileProgram(PcodeParser, SleighLanguage, String, String, PcodeUseropLibrary)
+ */
+ public static PcodeProgram compileProgram(SleighLanguage language, String sourceName,
+ String source, PcodeUseropLibrary> library) {
+ return compileProgram(createParser(language), language, sourceName, source, library);
+ }
+
+ /**
+ * Compile the given Sleigh expression into a p-code program that can evaluate it, using the
+ * given parser
*
*
* TODO: Currently, expressions cannot be compiled for a user-supplied userop library. The
@@ -198,8 +298,8 @@ public enum SleighProgramCompiler {
* @return a p-code program whose {@link PcodeExpression#evaluate(PcodeExecutor)} method will
* evaluate the expression on the given executor and its state.
*/
- public static PcodeExpression compileExpression(SleighLanguage language, String expression) {
- PcodeParser parser = createParser(language);
+ public static PcodeExpression compileExpression(PcodeParser parser, SleighLanguage language,
+ String expression) {
Map symbols = PcodeExpression.CAPTURING.getSymbols(language);
addParserSymbols(parser, symbols);
@@ -208,6 +308,15 @@ public enum SleighProgramCompiler {
return constructProgram(PcodeExpression::new, language, template, symbols);
}
+ /**
+ * Compile the given Sleigh expression into a p-code program that can evaluate it
+ *
+ * @see #compileExpression(PcodeParser, SleighLanguage, String)
+ */
+ public static PcodeExpression compileExpression(SleighLanguage language, String expression) {
+ return compileExpression(createParser(language), language, expression);
+ }
+
/**
* Generate a Sleigh symbol for context when compiling a userop definition
*
diff --git a/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/exec/SleighUtils.java b/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/exec/SleighUtils.java
index 52332022fc..b0c0251289 100644
--- a/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/exec/SleighUtils.java
+++ b/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/exec/SleighUtils.java
@@ -290,6 +290,39 @@ public enum SleighUtils {
});
}
+ public static void matchDereference(Tree tree, Consumer onSpace, Consumer onSize,
+ Consumer onOffset) {
+ switch (tree.getChildCount()) {
+ case 3:
+ match(tree, SleighParser.OP_DEREFERENCE, onSpace, onSize, onOffset);
+ return;
+ case 2:
+ Tree child0 = tree.getChild(0);
+ switch (child0.getType()) {
+ case SleighParser.OP_IDENTIFIER:
+ match(tree, SleighParser.OP_DEREFERENCE, onSpace, onOffset);
+ return;
+ case SleighParser.OP_BIN_CONSTANT:
+ case SleighParser.OP_DEC_CONSTANT:
+ case SleighParser.OP_HEX_CONSTANT:
+ match(tree, SleighParser.OP_DEREFERENCE, onSize, onOffset);
+ return;
+ default:
+ throw new AssertionError(
+ "OP_DEREFERENCE with 2 children where child[0] is " +
+ SleighParser.tokenNames[child0.getType()]);
+ }
+ case 1:
+ match(tree, SleighParser.OP_DEREFERENCE, onOffset);
+ return;
+ default:
+ // Likely, the op is mismatched. Ensure the error message says so.
+ match(tree, SleighParser.OP_DEREFERENCE);
+ throw new AssertionError(
+ "OP_DEREFERENCE with " + tree.getChildCount() + " children");
+ }
+ }
+
/**
* Check if the given tree represents an unconditional breakpoint in the emulator
*
@@ -390,6 +423,36 @@ public enum SleighUtils {
}
}
+ public record AddressOf(String space, Tree offset) {
+ }
+
+ public static AddressOf recoverAddressOf(String defaultSpace, Tree tree) {
+ var l = new Object() {
+ String space = defaultSpace;
+ Tree offset;
+ };
+ matchDereference(tree, wantSpaceId -> {
+ match(wantSpaceId, SleighParser.OP_IDENTIFIER, id -> {
+ l.space = getIdentifier(id);
+ });
+ }, wantSize -> {
+ // I don't care about size
+ }, wantOffset -> {
+ l.offset = wantOffset;
+ });
+ return new AddressOf(l.space, removeParenthesisTree(Objects.requireNonNull(l.offset)));
+ }
+
+ public static AddressOf recoverAddressOf(String defaultSpace, String expression) {
+ try {
+ Tree tree = parseSleighExpression(expression);
+ return recoverAddressOf(defaultSpace, tree);
+ }
+ catch (SleighParseError | MismatchException e) {
+ return null;
+ }
+ }
+
/**
* Synthesize a tree (node)
*
diff --git a/Ghidra/Framework/Emulation/src/test/java/ghidra/pcode/exec/SleighProgramCompilerTest.java b/Ghidra/Framework/Emulation/src/test/java/ghidra/pcode/exec/SleighProgramCompilerTest.java
new file mode 100644
index 0000000000..a0c9b6124d
--- /dev/null
+++ b/Ghidra/Framework/Emulation/src/test/java/ghidra/pcode/exec/SleighProgramCompilerTest.java
@@ -0,0 +1,104 @@
+/* ###
+ * 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 static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import generic.Unique;
+import generic.test.AbstractGTest;
+import ghidra.GhidraTestApplicationLayout;
+import ghidra.app.plugin.processors.sleigh.SleighLanguage;
+import ghidra.app.plugin.processors.sleigh.SleighLanguageHelper;
+import ghidra.framework.Application;
+import ghidra.framework.ApplicationConfiguration;
+import ghidra.pcode.exec.SleighProgramCompiler.DetailedSleighException;
+import ghidra.pcode.exec.SleighProgramCompiler.PcodeLogEntry;
+import ghidra.sleigh.grammar.Location;
+import utility.function.ExceptionalCallback;
+
+public class SleighProgramCompilerTest extends AbstractGTest {
+ protected T rfail(String message) {
+ fail(message);
+ throw new AssertionError();
+ }
+
+ protected E expect(Class cls, ExceptionalCallback cb) {
+ try {
+ cb.call();
+ }
+ catch (Throwable e) {
+ if (!cls.isInstance(e)) {
+ e.printStackTrace();
+ return rfail("Expected " + cls + ". Got " + e.getClass());
+ }
+ return cls.cast(e);
+ }
+ return rfail("Expected " + cls + ". Got success");
+ }
+
+ @Before
+ public void setUp() throws IOException {
+ if (!Application.isInitialized()) {
+ Application.initializeApplication(
+ new GhidraTestApplicationLayout(new File(getTestDirectoryPath())),
+ new ApplicationConfiguration());
+ }
+ }
+
+ @Test
+ public void testCompileProgramErrLocations() throws Throwable {
+ SleighLanguage language = SleighLanguageHelper.getMockBE64Language();
+ DetailedSleighException exc = expect(DetailedSleighException.class, () -> {
+ PcodeProgram program =
+ SleighProgramCompiler.compileProgram(language, "test", "noreg = noreg;",
+ PcodeUseropLibrary.NIL);
+ // Shouldn't get here, but if we do, I'd like to see the program:
+ System.err.println(program);
+ });
+ PcodeLogEntry entry = Unique.assertOne(exc.getDetails());
+ Location loc = entry.loc();
+ assertEquals("test", loc.filename);
+ assertEquals(1, loc.lineno);
+ assertEquals(
+ "unknown start, end, next2, operand, epsilon, or varnode 'noreg' in varnode reference",
+ entry.msg());
+ }
+
+ @Test
+ public void testCompileExpressionErrLocations() throws Throwable {
+ SleighLanguage language = SleighLanguageHelper.getMockBE64Language();
+ DetailedSleighException exc = expect(DetailedSleighException.class, () -> {
+ PcodeProgram program = SleighProgramCompiler.compileExpression(language, "noreg");
+ // Shouldn't get here, but if we do, I'd like to see the program:
+ System.err.println(program);
+ });
+ PcodeLogEntry entry = Unique.assertOne(exc.getDetails());
+ // TODO: It'd be nice if loc included a column number and token length
+ Location loc = entry.loc();
+ assertEquals("expression", loc.filename);
+ assertEquals(1, loc.lineno);
+ assertEquals(
+ "unknown start, end, next2, operand, epsilon, or varnode 'noreg' in varnode reference",
+ entry.msg());
+ }
+}
diff --git a/Ghidra/Framework/Emulation/src/test/java/ghidra/pcode/exec/SleighUtilsTest.java b/Ghidra/Framework/Emulation/src/test/java/ghidra/pcode/exec/SleighUtilsTest.java
index 38c90810ef..43fb5a60cd 100644
--- a/Ghidra/Framework/Emulation/src/test/java/ghidra/pcode/exec/SleighUtilsTest.java
+++ b/Ghidra/Framework/Emulation/src/test/java/ghidra/pcode/exec/SleighUtilsTest.java
@@ -15,13 +15,13 @@
*/
package ghidra.pcode.exec;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.fail;
+import static org.junit.Assert.*;
import org.antlr.runtime.RecognitionException;
import org.antlr.runtime.tree.Tree;
import org.junit.Test;
+import ghidra.pcode.exec.SleighUtils.AddressOf;
import ghidra.pcode.exec.SleighUtils.SleighParseError;
public class SleighUtilsTest {
@@ -63,6 +63,40 @@ public class SleighUtilsTest {
}
}
+ @Test
+ public void testRecoverAddressOfMismatchErr() {
+ AddressOf addrOf = SleighUtils.recoverAddressOf(null, "ptr + 8");
+ assertNull(addrOf);
+ }
+
+ @Test
+ public void testRecoverAddressOfForm1() {
+ AddressOf addrOf = SleighUtils.recoverAddressOf(null, "*ptr");
+ assertEquals(null, addrOf.space());
+ assertEquals("ptr", SleighUtils.generateSleighExpression(addrOf.offset()));
+ }
+
+ @Test
+ public void testRecoverAddressOfForm2a() {
+ AddressOf addrOf = SleighUtils.recoverAddressOf(null, "*:8 ptr");
+ assertEquals(null, addrOf.space());
+ assertEquals("ptr", SleighUtils.generateSleighExpression(addrOf.offset()));
+ }
+
+ @Test
+ public void testRecoverAddressOfForm2b() {
+ AddressOf addrOf = SleighUtils.recoverAddressOf(null, "*[ram] ptr");
+ assertEquals("ram", addrOf.space());
+ assertEquals("ptr", SleighUtils.generateSleighExpression(addrOf.offset()));
+ }
+
+ @Test
+ public void testRecoverAddressOfForm3() {
+ AddressOf addrOf = SleighUtils.recoverAddressOf(null, "*[ram]:8 ptr");
+ assertEquals("ram", addrOf.space());
+ assertEquals("ptr", SleighUtils.generateSleighExpression(addrOf.offset()));
+ }
+
@Test
public void testRecoverConditionEqDec() {
assertEquals("RAX == 0",
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/PcodeParser.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/PcodeParser.java
index 8e64c318a8..19d8622dd6 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/PcodeParser.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/PcodeParser.java
@@ -155,7 +155,7 @@ public class PcodeParser extends PcodeCompile {
if (sym != null) {
return sym;
}
- return PcodeParser.this.sleigh.findSymbol(nm);
+ return sleigh.findSymbol(nm);
}
public SleighBase getSleigh() {