GP-1539: Polish the DebuggerGoToDialog. Allow labels and plain addresses.

This commit is contained in:
Dan 2023-03-24 14:41:12 -04:00
parent 738e662e82
commit b51d423d4b
27 changed files with 759 additions and 103 deletions

View file

@ -134,10 +134,12 @@
<H3><A name="go_to"></A>Go To (G)</H3>
<P>This action is available whenever a trace is active in the listing. It prompts the user for
an address, which can be expressed in Sleigh, then attempts to navigate to it. The expression
is evaluated in the context of the current thread, frame, and point in time. If the current
trace is live and at the present, the target may be queried to retrieve any machine state
required to evaluate the expression.</P>
an address, which can be expressed in simple notation or <B>Sleigh</B>, then attempts to
navigate to it. The expression is evaluated in the context of the current thread, frame, and
point in time. If the current trace is live and at the present, the target may be queried to
retrieve any machine state required to evaluate the expression. The expression may be in terms
of labels, registers, and constants. Labels may come from the current trace or a program mapped
into the trace. Ambiguities are resolved arbitrarily.</P>
<TABLE width="100%">
<TBODY>
@ -147,6 +149,22 @@
</TBODY>
</TABLE>
<P>Some examples:</P>
<UL>
<LI><CODE>00401234</CODE> &mdash; A constant address in simple notation</LI>
<LI><CODE>0x00401234:8</CODE> &mdash; A constant address in Sleigh notation</LI>
<LI><CODE>main + 10</CODE> &mdash; 10 bytes past the address of "main"</LI>
<LI><CODE>RAX</CODE> &mdash; The address in RAX</LI>
<LI><CODE>RSP + 8</CODE> &mdash; The address of stack offset 8</LI>
<LI><CODE>*:8 (RSP+8)</CODE> &mdash; The address pointed to by stack offset 8</LI>
</UL>
<H3><A name="auto_sync_cursor_static"></A>Auto-Sync Cursor with Static Listing</H3>
<P>This action is always available, but only on the primary dynamic listing. It configures

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 7 KiB

Before After
Before After

View file

@ -78,11 +78,9 @@
<H3><A name="go_to"></A>Go To (G)</H3>
<P>This action is available whenever a trace is active in the window. It prompts the user for
an address, which can be expressed in Sleigh, then attempts to navigate to it. The expression
is evaluated in the context of the current thread, frame, and point in time. If the current
trace is live and at the present, the target may be queried to retrieve any machine state
required to evaluate the expression.</P>
<P>This action is equivalent to the same action in the <A href=
"help/topics/DebuggerListingPlugin/DebuggerListingPlugin.html#go_to">Dynamic Listing</A>
window.</P>
<TABLE width="100%">
<TBODY>

View file

@ -42,6 +42,10 @@
constant 0x7fff0004 is a known issue. Just use the target's pointer size in bytes &mdash; 8
in the example.</LI>
<LI><CODE>*:4 my_global</CODE>: Display 4 bytes starting at the label "my_global", e.g., to
read the <CODE>int</CODE> value there. The label may be in the trace or in a program mapped
to the trace. Ambiguities are resolved arbitrarily.</LI>
<LI><CODE>*:8 RSP</CODE>: Display 8 bytes of [ram] starting at the offset given by register
RSP, e.g., to read a <CODE>long</CODE> on the stack.</LI>

View file

@ -16,65 +16,69 @@
package ghidra.app.plugin.core.debug.gui.action;
import java.awt.BorderLayout;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import docking.DialogComponentProvider;
import ghidra.app.plugin.core.debug.gui.DebuggerResources;
import ghidra.app.plugin.core.debug.gui.action.DebuggerGoToTrait.GoToResult;
import ghidra.app.plugin.core.debug.gui.breakpoint.AbstractDebuggerSleighInputDialog;
import ghidra.app.plugin.core.debug.gui.listing.DebuggerListingPlugin;
import ghidra.app.plugin.processors.sleigh.SleighLanguage;
import ghidra.async.AsyncUtils;
import ghidra.program.model.address.AddressFactory;
import ghidra.program.model.address.AddressSpace;
import ghidra.util.MessageType;
import ghidra.util.Msg;
import ghidra.framework.plugintool.util.PluginUtils;
import ghidra.pcode.exec.SleighUtils;
import ghidra.program.model.address.*;
import ghidra.trace.model.guest.TracePlatform;
import ghidra.util.*;
public class DebuggerGoToDialog extends DialogComponentProvider {
public class DebuggerGoToDialog extends AbstractDebuggerSleighInputDialog {
private static final String TEXT = """
<html>
<body width="400px">
<p>
Enter an address or Sleigh expression. Press <b>F1</b> for help and examples.
</p>
</body>
</html>
""";
private final DebuggerGoToTrait trait;
private final DefaultComboBoxModel<String> modelSpaces;
final JTextField textExpression;
final JComboBox<String> comboSpaces;
public DebuggerGoToDialog(DebuggerGoToTrait trait) {
super("Go To", true, true, true, false);
super("Go To", TEXT);
setHelpLocation(new HelpLocation(
PluginUtils.getPluginNameFromClass(DebuggerListingPlugin.class),
DebuggerResources.GoToAction.HELP_ANCHOR));
this.trait = trait;
textExpression = new JTextField();
modelSpaces = new DefaultComboBoxModel<>();
comboSpaces = new JComboBox<>(modelSpaces);
JPanel panel = new JPanel(new BorderLayout());
panel.setBorder(new EmptyBorder(16, 16, 16, 16));
JLabel help = new JLabel(
"<html>Enter any sleigh expression to evaluate against the current thread.<br/>" +
"Note that constants and memory derefs must have a resolved size.<br/>" +
"Examples:<br/>" +
"<ul>" +
"<li>To go to a constant address: <code>0x00401234:4</code></li>" +
"<li>To go to the address in a register: <code>RAX</code></li>" +
"<li>To dereference the pointer at an address in a register: <code>*:8 RAX</code></li>" +
"</ul></html>");
help.getMaximumSize().width = 400;
panel.add(help, BorderLayout.NORTH);
Box box = Box.createHorizontalBox();
box.setBorder(new EmptyBorder(16, 0, 0, 0));
panel.add(box);
Box hbox = Box.createHorizontalBox();
hbox.add(comboSpaces);
hbox.add(new JLabel(":"));
panel.add(hbox, BorderLayout.WEST);
box.add(new JLabel("*["));
box.add(comboSpaces);
box.add(new JLabel("]"));
box.add(textExpression);
setFocusComponent(textInput);
addWorkPanel(panel);
setFocusComponent(textExpression);
addOKButton();
addCancelButton();
textInput.addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
okCallback();
e.consume();
}
}
});
}
protected void populateSpaces(SleighLanguage language) {
@ -94,19 +98,37 @@ public class DebuggerGoToDialog extends DialogComponentProvider {
}
}
@Override
protected void validate() {
TracePlatform platform = trait.current.getPlatform();
if (platform == null) {
throw new AssertionError("No current trace platform");
}
Address address = platform.getAddressFactory().getAddress(getInput());
if (address != null) {
return;
}
SleighUtils.parseSleighExpression(getInput());
}
@Override // public for tests
public void okCallback() {
CompletableFuture<Boolean> future;
validateAndMarkup();
if (!isValid) {
return;
}
CompletableFuture<GoToResult> future;
try {
future = trait.goToSleigh((String) comboSpaces.getSelectedItem(),
textExpression.getText());
future = trait.goTo((String) comboSpaces.getSelectedItem(), getInput());
}
catch (Throwable t) {
future = CompletableFuture.failedFuture(t);
}
future.thenAccept(success -> {
if (!success) {
setStatusText("Address not in trace", MessageType.ERROR, true);
future.thenAccept(result -> {
if (!result.success()) {
setStatusText("<html>Address <code>" + result.address() + "</code> not in trace", MessageType.ERROR,
true);
}
else {
close();
@ -124,12 +146,15 @@ public class DebuggerGoToDialog extends DialogComponentProvider {
close();
}
public void show(SleighLanguage language) {
public void show(SleighLanguage language, GoToInput defaultInput) {
populateSpaces(language);
trait.tool.showDialog(this);
if (language.getAddressFactory().getAddressSpace(defaultInput.space()) != null) {
comboSpaces.setSelectedItem(defaultInput.space());
}
prompt(trait.tool, defaultInput.offset());
}
public void setExpression(String expression) {
textExpression.setText(expression);
public void setOffset(String offset) {
textInput.setText(offset);
}
}

View file

@ -28,12 +28,17 @@ import ghidra.framework.plugintool.Plugin;
import ghidra.framework.plugintool.PluginTool;
import ghidra.pcode.exec.*;
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.trace.model.guest.TracePlatform;
public abstract class DebuggerGoToTrait {
/**
* @see DebuggerGoToTrait#goTo(String, String)
*/
public record GoToResult(Address address, Boolean success) {
}
protected DockingAction action;
protected final PluginTool tool;
@ -48,6 +53,8 @@ public abstract class DebuggerGoToTrait {
this.provider = provider;
}
protected abstract GoToInput getDefaultInput();
protected abstract boolean goToAddress(Address address);
public void goToCoordinates(DebuggerCoordinates coordinates) {
@ -66,25 +73,61 @@ public abstract class DebuggerGoToTrait {
private void activatedGoTo(ActionContext context) {
DebuggerGoToDialog goToDialog = new DebuggerGoToDialog(this);
TracePlatform platform = current.getPlatform();
goToDialog.show((SleighLanguage) platform.getLanguage());
goToDialog.show((SleighLanguage) platform.getLanguage(), getDefaultInput());
}
public CompletableFuture<Boolean> goToSleigh(String spaceName, String expression) {
/**
* Go to the given address
*
* <p>
* 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<GoToResult> 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<GoToResult> 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<Boolean> goToSleigh(TracePlatform platform, AddressSpace space,
protected CompletableFuture<GoToResult> goToSleigh(TracePlatform platform, AddressSpace space,
PcodeExpression expression) {
PcodeExecutor<byte[]> executor = DebuggerPcodeUtils.executorForCoordinates(tool, current);
CompletableFuture<byte[]> 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);
}
}

View file

@ -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
}

View file

@ -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);
}
}

View file

@ -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<Address> 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?

View file

@ -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) {

View file

@ -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) {

View file

@ -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) {

View file

@ -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) {

View file

@ -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) {

View file

@ -38,6 +38,7 @@ public class WatchLocationTrackingSpecFactory implements LocationTrackingSpecFac
}
return watchesService.getWatches()
.stream()
.filter(WatchLocationTrackingSpec::isTrackable)
.map(WatchLocationTrackingSpec::fromWatch)
.collect(Collectors.toList());
}

View file

@ -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);
}
}

View file

@ -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));
}
}

View file

@ -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<WatchValue> executor = provider.asyncWatchExecutor;
PcodeExecutor<byte[]> 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;

View file

@ -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
*
* <p>
* 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
*
* <p>
* 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<byte[]> 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);
}

View file

@ -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);
}

View file

@ -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()));
}

View file

@ -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();
});

View file

@ -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<PcodeLogEntry> 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<PcodeLogEntry> details;
public DetailedSleighException(List<PcodeLogEntry> details) {
super(PcodeLogEntry.formatList(details));
this.details = List.copyOf(details);
}
public List<PcodeLogEntry> getDetails() {
return details;
}
}
/**
* A p-code parser that provides programmatic access to error diagnostics.
*/
public static class ErrorCollectingPcodeParser extends PcodeParser {
private final List<PcodeLogEntry> 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
*
* <p>
* 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<Integer, UserOpSymbol> 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
*
* <p>
* 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<Integer, UserOpSymbol> 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
*

View file

@ -290,6 +290,39 @@ public enum SleighUtils {
});
}
public static void matchDereference(Tree tree, Consumer<Tree> onSpace, Consumer<Tree> onSize,
Consumer<Tree> 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)
*

View file

@ -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> T rfail(String message) {
fail(message);
throw new AssertionError();
}
protected <E extends Exception> E expect(Class<E> cls, ExceptionalCallback<E> 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());
}
}

View file

@ -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",

View file

@ -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() {