From ef6fb310bbe689d312c6c01c235af7cd15718fbf Mon Sep 17 00:00:00 2001 From: Dan <46821332+nsadeveloper789@users.noreply.github.com> Date: Thu, 17 Nov 2022 08:50:12 -0500 Subject: [PATCH] GP-2814: Fix translation of address operands for guest instructions. --- .../listing/DebuggerListingProviderTest.java | 126 +++++++++++++++++- .../database/listing/DBTraceInstruction.java | 35 ++++- .../util/InstructionAdapterFromPrototype.java | 32 ++++- .../hover/ReferenceListingHoverPlugin.java | 7 +- .../hover/ScalarOperandListingHover.java | 5 +- .../core/hover/AbstractReferenceHover.java | 10 ++ .../program/model/listing/CodeUnitFormat.java | 5 +- .../AbstractGhidraHeadedIntegrationTest.java | 6 +- 8 files changed, 201 insertions(+), 25 deletions(-) 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 e8345c6468..40e428a591 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 @@ -17,6 +17,9 @@ package ghidra.app.plugin.core.debug.gui.listing; import static org.junit.Assert.*; +import java.awt.Component; +import java.awt.Point; +import java.awt.event.MouseEvent; import java.io.IOException; import java.math.BigInteger; import java.nio.ByteBuffer; @@ -29,10 +32,14 @@ import org.junit.experimental.categories.Category; import docking.menu.ActionState; import docking.menu.MultiStateDockingAction; import docking.widgets.EventTrigger; +import docking.widgets.fieldpanel.FieldPanel; import generic.test.category.NightlyCategory; +import generic.test.rule.Repeated; import generic.theme.GThemeDefaults.Colors; +import ghidra.app.plugin.assembler.*; import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin; 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.gui.AbstractGhidraHeadedDebuggerGUITest; 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.service.modules.DebuggerStaticMappingUtils; import ghidra.app.services.*; +import ghidra.app.util.viewer.listingpanel.ListingPanel; import ghidra.async.SwingExecutorService; import ghidra.framework.model.*; import ghidra.lifecycle.Unfinished; @@ -51,12 +59,13 @@ import ghidra.plugin.importer.ImporterPlugin; import ghidra.program.model.address.*; import ghidra.program.model.lang.Register; import ghidra.program.model.lang.RegisterValue; -import ghidra.program.util.ProgramLocation; -import ghidra.program.util.ProgramSelection; +import ghidra.program.model.listing.Instruction; +import ghidra.program.util.*; import ghidra.trace.database.ToyDBTraceBuilder; import ghidra.trace.database.memory.DBTraceMemoryManager; import ghidra.trace.database.stack.DBTraceStackManager; import ghidra.trace.model.*; +import ghidra.trace.model.guest.TraceGuestPlatform; import ghidra.trace.model.memory.*; import ghidra.trace.model.modules.TraceModule; import ghidra.trace.model.stack.TraceStack; @@ -1615,4 +1624,117 @@ public class DebuggerListingProviderTest extends AbstractGhidraHeadedDebuggerGUI // TODO: Test this independent of this particular action? 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()); + } } diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceInstruction.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceInstruction.java index 4ec65984e4..64b7827935 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceInstruction.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceInstruction.java @@ -17,6 +17,7 @@ package ghidra.trace.database.listing; import java.io.IOException; import java.math.BigInteger; +import java.nio.ByteBuffer; import java.util.*; import db.DBRecord; @@ -24,8 +25,7 @@ import ghidra.program.model.address.*; import ghidra.program.model.lang.*; import ghidra.program.model.listing.ContextChangeException; import ghidra.program.model.listing.FlowOverride; -import ghidra.program.model.mem.MemBuffer; -import ghidra.program.model.mem.MemoryAccessException; +import ghidra.program.model.mem.*; import ghidra.program.model.symbol.*; import ghidra.trace.database.DBTrace; import ghidra.trace.database.DBTraceUtils; @@ -106,11 +106,32 @@ public class DBTraceInstruction extends AbstractDBTraceCodeUnit"); + } else { sb.append(opElem.toString()); } @@ -120,7 +124,23 @@ public interface InstructionAdapterFromPrototype extends Instruction { @Override default List getDefaultOperandRepresentationList(int opIndex) { - return getPrototype().getOpRepresentationList(opIndex, getInstructionContext()); + // TODO: Cache this in the instruction? + List 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 diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/codebrowser/hover/ReferenceListingHoverPlugin.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/codebrowser/hover/ReferenceListingHoverPlugin.java index 0658dc1e98..e2b7f0b527 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/codebrowser/hover/ReferenceListingHoverPlugin.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/codebrowser/hover/ReferenceListingHoverPlugin.java @@ -41,7 +41,7 @@ import ghidra.framework.plugintool.util.PluginStatus; //@formatter:on public class ReferenceListingHoverPlugin extends Plugin { - private ReferenceListingHover referenceHoverService; + private final ReferenceListingHover referenceHoverService; public ReferenceListingHoverPlugin(PluginTool tool) { super(tool); @@ -66,4 +66,9 @@ public class ReferenceListingHoverPlugin extends Plugin { public void dispose() { referenceHoverService.dispose(); } + + /* testing */ + public ReferenceListingHover getReferenceHoverService() { + return referenceHoverService; + } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/codebrowser/hover/ScalarOperandListingHover.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/codebrowser/hover/ScalarOperandListingHover.java index f0e3b9eca6..7be617e956 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/codebrowser/hover/ScalarOperandListingHover.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/codebrowser/hover/ScalarOperandListingHover.java @@ -25,7 +25,6 @@ import ghidra.GhidraOptions; import ghidra.app.plugin.core.hover.AbstractScalarOperandHover; import ghidra.framework.plugintool.PluginTool; import ghidra.program.model.address.Address; -import ghidra.program.model.lang.InstructionPrototype; import ghidra.program.model.listing.Instruction; import ghidra.program.model.listing.Program; import ghidra.program.model.scalar.Scalar; @@ -96,9 +95,7 @@ public class ScalarOperandListingHover extends AbstractScalarOperandHover return operands[0]; } - InstructionPrototype prototype = instruction.getPrototype(); - List list = - prototype.getOpRepresentationList(opIndex, instruction.getInstructionContext()); + List list = instruction.getDefaultOperandRepresentationList(opIndex); if (list == null) { return null; } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/hover/AbstractReferenceHover.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/hover/AbstractReferenceHover.java index a7b641bd93..cdcd95932b 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/hover/AbstractReferenceHover.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/hover/AbstractReferenceHover.java @@ -66,6 +66,16 @@ public abstract class AbstractReferenceHover extends AbstractConfigurableHover { initialize(); } + /* testing */ + public ListingPanel getPanel() { + return panel; + } + + /* testing */ + public JToolTip getToolTip() { + return toolTip; + } + @Override public void dispose() { super.dispose(); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/program/model/listing/CodeUnitFormat.java b/Ghidra/Features/Base/src/main/java/ghidra/program/model/listing/CodeUnitFormat.java index 8596088f71..a99e432b3a 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/program/model/listing/CodeUnitFormat.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/program/model/listing/CodeUnitFormat.java @@ -21,7 +21,6 @@ import ghidra.app.util.NamespaceUtils; import ghidra.app.util.viewer.field.CommentUtils; import ghidra.program.model.address.*; import ghidra.program.model.data.*; -import ghidra.program.model.lang.InstructionPrototype; import ghidra.program.model.lang.Register; import ghidra.program.model.listing.CodeUnitFormatOptions.ShowBlockName; import ghidra.program.model.listing.CodeUnitFormatOptions.ShowNamespace; @@ -212,7 +211,6 @@ public class CodeUnitFormat { Program program = cu.getProgram(); Instruction instr = (Instruction) cu; - InstructionPrototype proto = instr.getPrototype(); if (!program.getLanguage().supportsPcode()) { // 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 - ArrayList representationList = - proto.getOpRepresentationList(opIndex, instr.getInstructionContext()); + List representationList = instr.getDefaultOperandRepresentationList(opIndex); if (representationList == null) { return new OperandRepresentationList(""); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/test/AbstractGhidraHeadedIntegrationTest.java b/Ghidra/Features/Base/src/main/java/ghidra/test/AbstractGhidraHeadedIntegrationTest.java index 1c0a54fbc4..7c0ce4bb5a 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/test/AbstractGhidraHeadedIntegrationTest.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/test/AbstractGhidraHeadedIntegrationTest.java @@ -27,7 +27,7 @@ import docking.DialogComponentProvider; import docking.action.DockingActionIf; import docking.widgets.fieldpanel.FieldPanel; import ghidra.GhidraTestApplicationLayout; -import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin; +import ghidra.app.plugin.core.codebrowser.AbstractCodeBrowserPlugin; import ghidra.framework.ApplicationConfiguration; import ghidra.framework.GhidraApplicationConfiguration; import ghidra.framework.model.*; @@ -192,11 +192,11 @@ public abstract class AbstractGhidraHeadedIntegrationTest * @param codeBrowser the CodeBrowserPlugin * @param clickCount the click count */ - public void click(CodeBrowserPlugin codeBrowser, int clickCount) { + public void click(AbstractCodeBrowserPlugin codeBrowser, int clickCount) { 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 // during the testing process, like when the tool is first loaded.