diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/AbstractDebuggerPlatformMapper.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/AbstractDebuggerPlatformMapper.java index 4fe13fc053..ae67a96147 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/AbstractDebuggerPlatformMapper.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/AbstractDebuggerPlatformMapper.java @@ -24,7 +24,8 @@ import ghidra.debug.api.platform.DebuggerPlatformMapper; import ghidra.debug.api.platform.DisassemblyResult; import ghidra.framework.plugintool.PluginTool; import ghidra.program.model.address.*; -import ghidra.program.model.lang.*; +import ghidra.program.model.lang.Endian; +import ghidra.program.model.lang.Language; import ghidra.trace.model.Trace; import ghidra.trace.model.guest.TracePlatform; import ghidra.trace.model.target.TraceObject; @@ -85,9 +86,10 @@ public abstract class AbstractDebuggerPlatformMapper implements DebuggerPlatform if (!result) { return DisassemblyResult.failed(dis.getStatusMsg()); } + AddressSetView actualSet = dis.getDisassembledAddressSet(); for (DisassemblyInject i : injects) { - i.post(tool, trace, snap, dis.getDisassembledAddressSet()); + i.post(tool, trace, snap, actualSet); } - return DisassemblyResult.success(!dis.getDisassembledAddressSet().isEmpty()); + return DisassemblyResult.success(actualSet != null && !actualSet.isEmpty()); } } 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 a3d61b246f..17354f42f8 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 @@ -19,6 +19,7 @@ import java.math.BigInteger; import java.util.*; import java.util.Map.Entry; +import ghidra.app.nav.NavigationUtils; 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; @@ -38,6 +39,7 @@ 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.Function; import ghidra.program.model.listing.Program; import ghidra.program.model.mem.MemBuffer; import ghidra.program.model.symbol.Symbol; @@ -102,7 +104,7 @@ public enum DebuggerPcodeUtils { * TODO: This may break things that check for the absence of a symbol * * I don't think it'll affect expressions, but it could later affect user Sleigh - * libraries than an expression might like to use. The better approach would be to + * libraries that an expression might like to use. The better approach would be to * incorporate a better error message into the Sleigh compiler, but it won't always * know the use case for a clear message. */ @@ -111,6 +113,42 @@ public enum DebuggerPcodeUtils { return symbol; } + protected SleighSymbol tryMap(String nm, Trace trace, long snap, Program program, + Symbol symbol, Address addr, List externals) { + TraceLocation tloc = + mappings.getOpenMappedLocation(trace, new ProgramLocation(program, addr), snap); + if (tloc == null) { + return null; + } + SleighSymbol mapped = createSleighConstant(program.getName(), nm, tloc.getAddress()); + if (!symbol.isExternal()) { + return mapped; + } + // Most externals will not map, but if one does, use it as a fallback + externals.add(mapped); + return null; + } + + protected SleighSymbol tryExternalLinkage(String nm, Trace trace, long snap, + Program program, Symbol symbol, List externals) { + if (!(symbol.getObject() instanceof Function func)) { + return null; + } + // This covers refs and thunks + Address[] extAddresses = + NavigationUtils.getExternalLinkageAddresses(program, symbol.getAddress()); + if (extAddresses == null) { + return null; + } + for (Address addr : extAddresses) { + SleighSymbol mapped = tryMap(nm, trace, snap, program, symbol, addr, externals); + if (mapped != null) { + return mapped; + } + } + return null; + } + protected SleighSymbol findUserSymbol(String nm) { Trace trace = coordinates.getTrace(); long snap = coordinates.getSnap(); @@ -121,23 +159,28 @@ public enum DebuggerPcodeUtils { } return createSleighConstant(trace.getName(), nm, symbol.getAddress()); } + List externals = new ArrayList<>(); 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; + SleighSymbol mapped = + tryMap(nm, trace, snap, program, symbol, symbol.getAddress(), externals); + if (mapped != null) { + return mapped; + } + mapped = tryExternalLinkage(nm, trace, snap, program, symbol, externals); + if (mapped != null) { + return mapped; } - return createSleighConstant(program.getName(), nm, tloc.getAddress()); } } + // TODO: Some way to prioritize among these? + for (SleighSymbol ext : externals) { + return ext; + } return null; } } 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 f23956d211..abb71ec99a 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 @@ -58,9 +58,12 @@ import ghidra.debug.api.tracemgr.DebuggerCoordinates; import ghidra.framework.model.*; import ghidra.plugin.importer.ImporterPlugin; import ghidra.program.model.address.*; +import ghidra.program.model.data.PointerDataType; import ghidra.program.model.lang.Register; import ghidra.program.model.lang.RegisterValue; -import ghidra.program.model.listing.Instruction; +import ghidra.program.model.listing.*; +import ghidra.program.model.symbol.RefType; +import ghidra.program.model.symbol.SourceType; import ghidra.program.util.*; import ghidra.trace.database.ToyDBTraceBuilder; import ghidra.trace.database.memory.DBTraceMemoryManager; @@ -877,6 +880,39 @@ public class DebuggerListingProviderTest extends AbstractGhidraHeadedDebuggerTes () -> assertEquals(tb.addr(0x00404321), listingProvider.getLocation().getAddress())); } + @Test + public void testActionGoToExternalLinkage() throws Exception { + createMappedTraceAndProgram(); + + AddressSpace ss = program.getAddressFactory().getDefaultAddressSpace(); + + try (Transaction tx = program.openTransaction("Add EXTERNAL and ref")) { + Function func = program.getExternalManager() + .addExtLocation("lib", "testExtFunc", null, SourceType.IMPORTED) + .createFunction(); + // This is the same construct as imported from a PE's IAT + Address dataAddr = ss.getAddress(0x00600123); + Data data = program.getListing().createData(dataAddr, PointerDataType.dataType); + data.addMnemonicReference(func.getEntryPoint(), RefType.EXTERNAL_REF, + SourceType.IMPORTED); + } + + waitForProgram(program); + traceManager.activateTrace(tb.trace); + waitForSwing(); + + assertTrue(listingProvider.actionGoTo.isEnabled()); + + performAction(listingProvider.actionGoTo, false); + DebuggerGoToDialog dialog1 = waitForDialogComponent(DebuggerGoToDialog.class); + runSwing(() -> { + dialog1.setOffset("testExtFunc"); + dialog1.okCallback(); + }); + waitForPass( + () -> assertEquals(tb.addr(0x00400123), listingProvider.getLocation().getAddress())); + } + @Test public void testActionTrackLocation() throws Exception { assertTrue(listingProvider.actionTrackLocation.isEnabled()); @@ -1457,7 +1493,8 @@ public class DebuggerListingProviderTest extends AbstractGhidraHeadedDebuggerTes traceManager.activate(DebuggerCoordinates.NOWHERE.thread(thread).snap(0)); waitForSwing(); - assertEquals(tb.addr(0x00401234), listingProvider.getLocation().getAddress()); + waitForPass( + () -> assertEquals(tb.addr(0x00401234), listingProvider.getLocation().getAddress())); try (Transaction tx = tb.startTransaction()) { TraceMemorySpace regs = mm.getMemoryRegisterSpace(thread, true); @@ -1465,7 +1502,8 @@ public class DebuggerListingProviderTest extends AbstractGhidraHeadedDebuggerTes } waitForDomainObject(tb.trace); - assertEquals(tb.addr(0x00404321), listingProvider.getLocation().getAddress()); + waitForPass( + () -> assertEquals(tb.addr(0x00404321), listingProvider.getLocation().getAddress())); } @Test