GP-2814: Fix translation of address operands for guest instructions.

This commit is contained in:
Dan 2022-11-17 08:50:12 -05:00
parent 346eef3727
commit ef6fb310bb
8 changed files with 201 additions and 25 deletions

View file

@ -17,6 +17,9 @@ package ghidra.app.plugin.core.debug.gui.listing;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import java.awt.Component;
import java.awt.Point;
import java.awt.event.MouseEvent;
import java.io.IOException; import java.io.IOException;
import java.math.BigInteger; import java.math.BigInteger;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
@ -29,10 +32,14 @@ import org.junit.experimental.categories.Category;
import docking.menu.ActionState; import docking.menu.ActionState;
import docking.menu.MultiStateDockingAction; import docking.menu.MultiStateDockingAction;
import docking.widgets.EventTrigger; import docking.widgets.EventTrigger;
import docking.widgets.fieldpanel.FieldPanel;
import generic.test.category.NightlyCategory; import generic.test.category.NightlyCategory;
import generic.test.rule.Repeated;
import generic.theme.GThemeDefaults.Colors; import generic.theme.GThemeDefaults.Colors;
import ghidra.app.plugin.assembler.*;
import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin; import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
import ghidra.app.plugin.core.codebrowser.CodeViewerProvider; import ghidra.app.plugin.core.codebrowser.CodeViewerProvider;
import ghidra.app.plugin.core.codebrowser.hover.ReferenceListingHoverPlugin;
import ghidra.app.plugin.core.debug.DebuggerCoordinates; import ghidra.app.plugin.core.debug.DebuggerCoordinates;
import ghidra.app.plugin.core.debug.gui.AbstractGhidraHeadedDebuggerGUITest; import ghidra.app.plugin.core.debug.gui.AbstractGhidraHeadedDebuggerGUITest;
import ghidra.app.plugin.core.debug.gui.DebuggerResources; import ghidra.app.plugin.core.debug.gui.DebuggerResources;
@ -44,6 +51,7 @@ import ghidra.app.plugin.core.debug.gui.console.DebuggerConsoleProvider.LogRow;
import ghidra.app.plugin.core.debug.gui.modules.DebuggerMissingModuleActionContext; import ghidra.app.plugin.core.debug.gui.modules.DebuggerMissingModuleActionContext;
import ghidra.app.plugin.core.debug.service.modules.DebuggerStaticMappingUtils; import ghidra.app.plugin.core.debug.service.modules.DebuggerStaticMappingUtils;
import ghidra.app.services.*; import ghidra.app.services.*;
import ghidra.app.util.viewer.listingpanel.ListingPanel;
import ghidra.async.SwingExecutorService; import ghidra.async.SwingExecutorService;
import ghidra.framework.model.*; import ghidra.framework.model.*;
import ghidra.lifecycle.Unfinished; import ghidra.lifecycle.Unfinished;
@ -51,12 +59,13 @@ import ghidra.plugin.importer.ImporterPlugin;
import ghidra.program.model.address.*; import ghidra.program.model.address.*;
import ghidra.program.model.lang.Register; import ghidra.program.model.lang.Register;
import ghidra.program.model.lang.RegisterValue; import ghidra.program.model.lang.RegisterValue;
import ghidra.program.util.ProgramLocation; import ghidra.program.model.listing.Instruction;
import ghidra.program.util.ProgramSelection; import ghidra.program.util.*;
import ghidra.trace.database.ToyDBTraceBuilder; import ghidra.trace.database.ToyDBTraceBuilder;
import ghidra.trace.database.memory.DBTraceMemoryManager; import ghidra.trace.database.memory.DBTraceMemoryManager;
import ghidra.trace.database.stack.DBTraceStackManager; import ghidra.trace.database.stack.DBTraceStackManager;
import ghidra.trace.model.*; import ghidra.trace.model.*;
import ghidra.trace.model.guest.TraceGuestPlatform;
import ghidra.trace.model.memory.*; import ghidra.trace.model.memory.*;
import ghidra.trace.model.modules.TraceModule; import ghidra.trace.model.modules.TraceModule;
import ghidra.trace.model.stack.TraceStack; import ghidra.trace.model.stack.TraceStack;
@ -1615,4 +1624,117 @@ public class DebuggerListingProviderTest extends AbstractGhidraHeadedDebuggerGUI
// TODO: Test this independent of this particular action? // TODO: Test this independent of this particular action?
assertNull(consolePlugin.getLogRow(ctx)); assertNull(consolePlugin.getLogRow(ctx));
} }
protected Instruction placeGuestInstruction(int guestRangeLength) throws Throwable {
try (UndoableTransaction tid = tb.startTransaction()) {
tb.trace.getMemoryManager()
.addRegion("Memory[.text]", Lifespan.nowOn(0), tb.range(0x00400000, 0x0040ffff),
Set.of(TraceMemoryFlag.READ, TraceMemoryFlag.EXECUTE));
TraceGuestPlatform toy = tb.trace.getPlatformManager()
.addGuestPlatform(getToyBE64Language().getDefaultCompilerSpec());
Address hostEntry = tb.addr(0x00400000);
Address guestEntry = tb.addr(toy, 0x00000000);
toy.addMappedRange(hostEntry, guestEntry, guestRangeLength);
Assembler asm = Assemblers.getAssembler(toy.getLanguage());
AssemblyBuffer buf = new AssemblyBuffer(asm, guestEntry);
buf.assemble("call 0x123");
Instruction callInstr =
tb.addInstruction(0, hostEntry, toy, ByteBuffer.wrap(buf.getBytes()));
return callInstr;
}
}
@Test
public void testGuestInstructionNavigation() throws Throwable {
createAndOpenTrace("DATA:BE:64:default");
Instruction callInstr = placeGuestInstruction(0x1000);
traceManager.activateTrace(tb.trace);
waitForSwing();
assertEquals("call 0x00400123", callInstr.toString());
listingPlugin.goTo(new OperandFieldLocation(tb.trace.getProgramView(), tb.addr(0x00400000),
null, null, null, 0, 0));
waitForPass(() -> assertEquals(tb.addr(0x00400000), listingPlugin.getCurrentAddress()));
click(listingPlugin, 2);
waitForPass(() -> assertEquals(tb.addr(0x00400123), listingPlugin.getCurrentAddress()));
}
@Test
public void testGuestInstructionNavigationUnmapped() throws Throwable {
createAndOpenTrace("DATA:BE:64:default");
Instruction callInstr = placeGuestInstruction(0x100);
traceManager.activateTrace(tb.trace);
waitForSwing();
assertEquals("call guest:ram:00000123", callInstr.toString());
listingPlugin.goTo(new OperandFieldLocation(tb.trace.getProgramView(), tb.addr(0x00400000),
null, null, null, 0, 0));
waitForPass(() -> assertEquals(tb.addr(0x00400000), listingPlugin.getCurrentAddress()));
click(listingPlugin, 2); // It should not move, nor crash
waitForPass(() -> assertEquals(tb.addr(0x00400000), listingPlugin.getCurrentAddress()));
}
private void triggerPopup(Point cursorPoint, Component eventSource) {
moveMouse(eventSource, cursorPoint.x, cursorPoint.y);
clickMouse(eventSource, MouseEvent.BUTTON1, cursorPoint.x, cursorPoint.y, 1, 0);
moveMouse(eventSource, cursorPoint.x + 5, cursorPoint.y);
}
@Test
public void testGuestInstructionHover() throws Throwable {
ReferenceListingHoverPlugin hoverPlugin =
addPlugin(tool, ReferenceListingHoverPlugin.class);
ListingPanel listingPanel = listingProvider.getListingPanel();
FieldPanel fieldPanel = listingPanel.getFieldPanel();
createAndOpenTrace("DATA:BE:64:default");
Instruction callInstr = placeGuestInstruction(0x1000);
traceManager.activateTrace(tb.trace);
waitForSwing();
assertEquals("call 0x00400123", callInstr.toString());
listingPlugin.goTo(new OperandFieldLocation(tb.trace.getProgramView(), tb.addr(0x00400000),
null, null, null, 0, 0));
waitForPass(() -> assertEquals(tb.addr(0x00400000), listingPlugin.getCurrentAddress()));
Point p = fieldPanel.getCursorPoint();
triggerPopup(p, fieldPanel);
waitForPass(() -> assertTrue(listingPanel.isHoverShowing()));
ListingPanel popupPanel = hoverPlugin.getReferenceHoverService().getPanel();
assertEquals(tb.addr(0x00400123), popupPanel.getProgramLocation().getAddress());
}
@Test
public void testGuestInstructionHoverUnmapped() throws Throwable {
addPlugin(tool, ReferenceListingHoverPlugin.class);
ListingPanel listingPanel = listingProvider.getListingPanel();
FieldPanel fieldPanel = listingPanel.getFieldPanel();
createAndOpenTrace("DATA:BE:64:default");
Instruction callInstr = placeGuestInstruction(0x100);
traceManager.activateTrace(tb.trace);
waitForSwing();
assertEquals("call guest:ram:00000123", callInstr.toString());
listingPlugin.goTo(new OperandFieldLocation(tb.trace.getProgramView(), tb.addr(0x00400000),
null, null, null, 0, 0));
waitForPass(() -> assertEquals(tb.addr(0x00400000), listingPlugin.getCurrentAddress()));
Point p = fieldPanel.getCursorPoint();
triggerPopup(p, fieldPanel);
listingPlugin.updateNow();
waitForSwing();
assertFalse(listingPanel.isHoverShowing());
}
} }

View file

@ -17,6 +17,7 @@ package ghidra.trace.database.listing;
import java.io.IOException; import java.io.IOException;
import java.math.BigInteger; import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.util.*; import java.util.*;
import db.DBRecord; import db.DBRecord;
@ -24,8 +25,7 @@ import ghidra.program.model.address.*;
import ghidra.program.model.lang.*; import ghidra.program.model.lang.*;
import ghidra.program.model.listing.ContextChangeException; import ghidra.program.model.listing.ContextChangeException;
import ghidra.program.model.listing.FlowOverride; import ghidra.program.model.listing.FlowOverride;
import ghidra.program.model.mem.MemBuffer; import ghidra.program.model.mem.*;
import ghidra.program.model.mem.MemoryAccessException;
import ghidra.program.model.symbol.*; import ghidra.program.model.symbol.*;
import ghidra.trace.database.DBTrace; import ghidra.trace.database.DBTrace;
import ghidra.trace.database.DBTraceUtils; import ghidra.trace.database.DBTraceUtils;
@ -106,11 +106,32 @@ public class DBTraceInstruction extends AbstractDBTraceCodeUnit<DBTraceInstructi
@Override @Override
public ParserContext getParserContext(Address instructionAddress) public ParserContext getParserContext(Address instructionAddress)
throws UnknownContextException, MemoryAccessException { throws UnknownContextException, MemoryAccessException {
// TODO: Does the given address need mapping?
return DBTraceInstruction.this.getParserContext(instructionAddress); return DBTraceInstruction.this.getParserContext(instructionAddress);
} }
} }
protected class GuestMemBuffer implements MemBufferAdapter {
@Override
public Address getAddress() {
return platform.mapHostToGuest(getX1());
}
@Override
public Memory getMemory() {
return null;
}
@Override
public boolean isBigEndian() {
return platform.getLanguage().isBigEndian();
}
@Override
public int getBytes(ByteBuffer buffer, int addressOffset) {
return DBTraceInstruction.this.getBytes(buffer, addressOffset);
}
}
@DBAnnotatedField(column = PLATFORM_COLUMN_NAME) @DBAnnotatedField(column = PLATFORM_COLUMN_NAME)
private int platformKey; private int platformKey;
@DBAnnotatedField(column = PROTOTYPE_COLUMN_NAME) @DBAnnotatedField(column = PROTOTYPE_COLUMN_NAME)
@ -126,6 +147,7 @@ public class DBTraceInstruction extends AbstractDBTraceCodeUnit<DBTraceInstructi
protected ParserContext parserContext; protected ParserContext parserContext;
protected InternalTracePlatform platform; protected InternalTracePlatform platform;
protected InstructionContext instructionContext; protected InstructionContext instructionContext;
protected MemBuffer memBuffer;
/** /**
* Construct an instruction unit * Construct an instruction unit
@ -150,9 +172,11 @@ public class DBTraceInstruction extends AbstractDBTraceCodeUnit<DBTraceInstructi
this.platform = platform; this.platform = platform;
if (platform.isHost()) { if (platform.isHost()) {
instructionContext = this; instructionContext = this;
memBuffer = this;
} }
else { else {
instructionContext = new GuestInstructionContext(); instructionContext = new GuestInstructionContext();
memBuffer = new GuestMemBuffer();
} }
} }
@ -708,12 +732,13 @@ public class DBTraceInstruction extends AbstractDBTraceCodeUnit<DBTraceInstructi
@Override @Override
public MemBuffer getMemBuffer() { public MemBuffer getMemBuffer() {
return this; return memBuffer;
} }
@Override @Override
public ParserContext getParserContext() throws MemoryAccessException { public ParserContext getParserContext() throws MemoryAccessException {
return parserContext == null ? parserContext = prototype.getParserContext(this, this) return parserContext == null
? parserContext = prototype.getParserContext(getMemBuffer(), getProcessorContext())
: parserContext; : parserContext;
} }

View file

@ -16,16 +16,18 @@
package ghidra.trace.util; package ghidra.trace.util;
import java.util.List; import java.util.List;
import java.util.stream.Collectors;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.lang.*; import ghidra.program.model.lang.*;
import ghidra.program.model.listing.Instruction;
import ghidra.program.model.listing.InstructionPcodeOverride; import ghidra.program.model.listing.InstructionPcodeOverride;
import ghidra.program.model.pcode.PcodeOp; import ghidra.program.model.pcode.PcodeOp;
import ghidra.program.model.scalar.Scalar; import ghidra.program.model.scalar.Scalar;
import ghidra.program.model.symbol.RefType; import ghidra.program.model.symbol.RefType;
import ghidra.trace.model.guest.TracePlatform;
import ghidra.trace.model.listing.TraceInstruction;
public interface InstructionAdapterFromPrototype extends Instruction { public interface InstructionAdapterFromPrototype extends TraceInstruction {
default String getFullString() { default String getFullString() {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
sb.append(getMnemonicString()); sb.append(getMnemonicString());
@ -68,7 +70,7 @@ public interface InstructionAdapterFromPrototype extends Instruction {
InstructionContext context = getInstructionContext(); InstructionContext context = getInstructionContext();
int opType = prototype.getOpType(opIndex, context); int opType = prototype.getOpType(opIndex, context);
if (OperandType.isAddress(opType)) { if (OperandType.isAddress(opType)) {
return prototype.getAddress(opIndex, context); return getPlatform().mapGuestToHost(prototype.getAddress(opIndex, context));
} }
return null; return null;
} }
@ -106,11 +108,13 @@ public interface InstructionAdapterFromPrototype extends Instruction {
} }
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
for (Object opElem : opList) { for (Object opElem : opList) {
if (opElem instanceof Address) { if (opElem instanceof Address opAddr) {
Address opAddr = (Address) opElem;
sb.append("0x"); sb.append("0x");
sb.append(opAddr.toString(false)); sb.append(opAddr.toString(false));
} }
else if (opElem == null) {
sb.append("<null>");
}
else { else {
sb.append(opElem.toString()); sb.append(opElem.toString());
} }
@ -120,7 +124,23 @@ public interface InstructionAdapterFromPrototype extends Instruction {
@Override @Override
default List<Object> getDefaultOperandRepresentationList(int opIndex) { default List<Object> getDefaultOperandRepresentationList(int opIndex) {
return getPrototype().getOpRepresentationList(opIndex, getInstructionContext()); // TODO: Cache this in the instruction?
List<Object> list =
getPrototype().getOpRepresentationList(opIndex, getInstructionContext());
TracePlatform platform = getPlatform();
if (platform.isHost()) {
return list;
}
return list.stream().map(obj -> {
if (obj instanceof Address addr) {
Address hostAddr = platform.mapGuestToHost(addr);
if (hostAddr == null) {
return "guest:" + addr.toString(true);
}
return hostAddr;
}
return obj;
}).collect(Collectors.toList());
} }
@Override @Override

View file

@ -41,7 +41,7 @@ import ghidra.framework.plugintool.util.PluginStatus;
//@formatter:on //@formatter:on
public class ReferenceListingHoverPlugin extends Plugin { public class ReferenceListingHoverPlugin extends Plugin {
private ReferenceListingHover referenceHoverService; private final ReferenceListingHover referenceHoverService;
public ReferenceListingHoverPlugin(PluginTool tool) { public ReferenceListingHoverPlugin(PluginTool tool) {
super(tool); super(tool);
@ -66,4 +66,9 @@ public class ReferenceListingHoverPlugin extends Plugin {
public void dispose() { public void dispose() {
referenceHoverService.dispose(); referenceHoverService.dispose();
} }
/* testing */
public ReferenceListingHover getReferenceHoverService() {
return referenceHoverService;
}
} }

View file

@ -25,7 +25,6 @@ import ghidra.GhidraOptions;
import ghidra.app.plugin.core.hover.AbstractScalarOperandHover; import ghidra.app.plugin.core.hover.AbstractScalarOperandHover;
import ghidra.framework.plugintool.PluginTool; import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.lang.InstructionPrototype;
import ghidra.program.model.listing.Instruction; import ghidra.program.model.listing.Instruction;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
import ghidra.program.model.scalar.Scalar; import ghidra.program.model.scalar.Scalar;
@ -96,9 +95,7 @@ public class ScalarOperandListingHover extends AbstractScalarOperandHover
return operands[0]; return operands[0];
} }
InstructionPrototype prototype = instruction.getPrototype(); List<Object> list = instruction.getDefaultOperandRepresentationList(opIndex);
List<Object> list =
prototype.getOpRepresentationList(opIndex, instruction.getInstructionContext());
if (list == null) { if (list == null) {
return null; return null;
} }

View file

@ -66,6 +66,16 @@ public abstract class AbstractReferenceHover extends AbstractConfigurableHover {
initialize(); initialize();
} }
/* testing */
public ListingPanel getPanel() {
return panel;
}
/* testing */
public JToolTip getToolTip() {
return toolTip;
}
@Override @Override
public void dispose() { public void dispose() {
super.dispose(); super.dispose();

View file

@ -21,7 +21,6 @@ import ghidra.app.util.NamespaceUtils;
import ghidra.app.util.viewer.field.CommentUtils; import ghidra.app.util.viewer.field.CommentUtils;
import ghidra.program.model.address.*; import ghidra.program.model.address.*;
import ghidra.program.model.data.*; import ghidra.program.model.data.*;
import ghidra.program.model.lang.InstructionPrototype;
import ghidra.program.model.lang.Register; import ghidra.program.model.lang.Register;
import ghidra.program.model.listing.CodeUnitFormatOptions.ShowBlockName; import ghidra.program.model.listing.CodeUnitFormatOptions.ShowBlockName;
import ghidra.program.model.listing.CodeUnitFormatOptions.ShowNamespace; import ghidra.program.model.listing.CodeUnitFormatOptions.ShowNamespace;
@ -212,7 +211,6 @@ public class CodeUnitFormat {
Program program = cu.getProgram(); Program program = cu.getProgram();
Instruction instr = (Instruction) cu; Instruction instr = (Instruction) cu;
InstructionPrototype proto = instr.getPrototype();
if (!program.getLanguage().supportsPcode()) { if (!program.getLanguage().supportsPcode()) {
// Formatted mark-up only supported for languages which support PCode // Formatted mark-up only supported for languages which support PCode
@ -220,8 +218,7 @@ public class CodeUnitFormat {
} }
// Get raw representation list and map of registers contained within it // Get raw representation list and map of registers contained within it
ArrayList<Object> representationList = List<Object> representationList = instr.getDefaultOperandRepresentationList(opIndex);
proto.getOpRepresentationList(opIndex, instr.getInstructionContext());
if (representationList == null) { if (representationList == null) {
return new OperandRepresentationList("<BAD-Instruction>"); return new OperandRepresentationList("<BAD-Instruction>");
} }

View file

@ -27,7 +27,7 @@ import docking.DialogComponentProvider;
import docking.action.DockingActionIf; import docking.action.DockingActionIf;
import docking.widgets.fieldpanel.FieldPanel; import docking.widgets.fieldpanel.FieldPanel;
import ghidra.GhidraTestApplicationLayout; import ghidra.GhidraTestApplicationLayout;
import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin; import ghidra.app.plugin.core.codebrowser.AbstractCodeBrowserPlugin;
import ghidra.framework.ApplicationConfiguration; import ghidra.framework.ApplicationConfiguration;
import ghidra.framework.GhidraApplicationConfiguration; import ghidra.framework.GhidraApplicationConfiguration;
import ghidra.framework.model.*; import ghidra.framework.model.*;
@ -192,11 +192,11 @@ public abstract class AbstractGhidraHeadedIntegrationTest
* @param codeBrowser the CodeBrowserPlugin * @param codeBrowser the CodeBrowserPlugin
* @param clickCount the click count * @param clickCount the click count
*/ */
public void click(CodeBrowserPlugin codeBrowser, int clickCount) { public void click(AbstractCodeBrowserPlugin<?> codeBrowser, int clickCount) {
click(codeBrowser, clickCount, true); click(codeBrowser, clickCount, true);
} }
public void click(CodeBrowserPlugin codeBrowser, int clickCount, boolean wait) { public void click(AbstractCodeBrowserPlugin<?> codeBrowser, int clickCount, boolean wait) {
// make sure that the code browser is ready to go--sometimes it is not, due to timing // make sure that the code browser is ready to go--sometimes it is not, due to timing
// during the testing process, like when the tool is first loaded. // during the testing process, like when the tool is first loaded.