diff --git a/Ghidra/Debug/Debugger-api/src/main/java/ghidra/app/services/DebuggerPlatformService.java b/Ghidra/Debug/Debugger-api/src/main/java/ghidra/app/services/DebuggerPlatformService.java index b7957732b6..0cf60bc3e6 100644 --- a/Ghidra/Debug/Debugger-api/src/main/java/ghidra/app/services/DebuggerPlatformService.java +++ b/Ghidra/Debug/Debugger-api/src/main/java/ghidra/app/services/DebuggerPlatformService.java @@ -4,9 +4,9 @@ * 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. @@ -65,9 +65,11 @@ public interface DebuggerPlatformService { /** * Set the current mapper for the trace and initialize the trace for the mapper * - * @param trace the trace whose current mapper to set + * @param trace the trace whose mapper to assign and initialize + * @param focus the object of focus * @param mapper the mapper * @param snap the snap for initializing the trace */ - void setCurrentMapperFor(Trace trace, DebuggerPlatformMapper mapper, long snap); + void setCurrentMapperFor(Trace trace, TraceObject focus, DebuggerPlatformMapper mapper, + long snap); } diff --git a/Ghidra/Debug/Debugger-api/src/main/java/ghidra/debug/api/platform/DebuggerPlatformMapper.java b/Ghidra/Debug/Debugger-api/src/main/java/ghidra/debug/api/platform/DebuggerPlatformMapper.java index 8bc98f69c1..3d9deb683d 100644 --- a/Ghidra/Debug/Debugger-api/src/main/java/ghidra/debug/api/platform/DebuggerPlatformMapper.java +++ b/Ghidra/Debug/Debugger-api/src/main/java/ghidra/debug/api/platform/DebuggerPlatformMapper.java @@ -4,9 +4,9 @@ * 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. @@ -19,12 +19,40 @@ import ghidra.program.model.address.Address; import ghidra.program.model.address.AddressSetView; import ghidra.program.model.lang.CompilerSpec; import ghidra.program.model.lang.Language; +import ghidra.program.model.listing.Program; +import ghidra.trace.model.guest.TracePlatform; import ghidra.trace.model.target.TraceObject; import ghidra.trace.model.thread.TraceThread; import ghidra.util.task.TaskMonitor; /** * An object for interpreting a trace according to a chosen platform + * + *

+ * Platform selection is a bit of a work in progress, but the idea is to allow the mapper to choose + * relevant languages, compiler specifications, data organization, etc., based on the current + * debugger context. Most of these are fairly straightforward and relatively static. If the back-end + * creates the trace with an actual language (non-DATA), then there's a default mapper for "known + * hosts," (but this can be out prioritized by more complex mappers). If the back-end creates a + * trace with a DATA language (usually indicating it doesn't recognize the target architecture), + * then some pluggable examine the name of the debugger and its reported architecture to try to map + * it on the front end. There may not be any good opinions, in which case, the user can override + * with any language. That's the "simple" cases. + * + *

+ * In more complex cases, e.g., WoW64, the mapper may need to adjust the recommended language based + * on, e.g., the current program counter and loaded modules. Essentially, it must determine the CPUs + * current ISA mode and adjust accordingly. There are currently two known situations: 1) + * Disassembly, and 2) Data (namely pointer) Organization, controlled by the Compiler Spec. The + * selection logic differs slightly between the two. For disassembly, we allow the mapper specific + * control of the selected platform, based on the starting address. For data placement, we allow the + * mapper specific control of the selected platform, based on the current PC. Note that the starting + * address of the data itself may not always be relevant. At the moment, because of limitations in + * the {@link Program} API, we actually cannot support selection based on placement address. + * Instead, at the time we ask the mapper to add a platform to the trace + * ({@link #addToTrace(TraceObject, long)}), we provide the current focus and snap, so that it can + * derive the PC or whatever other context is necessary to make its decision. The returned platform + * is immediately set as current, so that data actions heed the chosen platform. */ public interface DebuggerPlatformMapper { @@ -32,18 +60,20 @@ public interface DebuggerPlatformMapper { * Get the compiler for a given object * * @param object the object + * @param snap the snap * @return the compiler spec */ - CompilerSpec getCompilerSpec(TraceObject object); + CompilerSpec getCompilerSpec(TraceObject object, long snap); /** * Get the language for a given object * * @param object the object + * @param snap the snap * @return the language */ - default Language getLangauge(TraceObject object) { - CompilerSpec cSpec = getCompilerSpec(object); + default Language getLangauge(TraceObject object, long snap) { + CompilerSpec cSpec = getCompilerSpec(object, snap); return cSpec == null ? null : cSpec.getLanguage(); } @@ -54,10 +84,11 @@ public interface DebuggerPlatformMapper { * Likely, this will need to modify the trace database. It must start its own transaction for * doing so. * - * @param trace the trace + * @param newFocus the newly-focused object * @param snap the snap + * @return the resulting platform, which may have already existed */ - void addToTrace(long snap); + TracePlatform addToTrace(TraceObject newFocus, long snap); /** * When focus changes, decide if this mapper should remain active @@ -71,6 +102,10 @@ public interface DebuggerPlatformMapper { /** * Disassemble starting at a given address and snap, limited to a given address set * + *

+ * Note that the mapper may use an alternative platform than that returned by + * {@link #addToTrace(TraceObject, long)}. + * * @param thread the thread if applicable * @param object the object for platform context * @param start the starting address diff --git a/Ghidra/Debug/Debugger-api/src/main/java/ghidra/debug/api/tracemgr/DebuggerCoordinates.java b/Ghidra/Debug/Debugger-api/src/main/java/ghidra/debug/api/tracemgr/DebuggerCoordinates.java index b7e5265cf0..fb71267384 100644 --- a/Ghidra/Debug/Debugger-api/src/main/java/ghidra/debug/api/tracemgr/DebuggerCoordinates.java +++ b/Ghidra/Debug/Debugger-api/src/main/java/ghidra/debug/api/tracemgr/DebuggerCoordinates.java @@ -128,13 +128,15 @@ public class DebuggerCoordinates { @Override public boolean equals(Object obj) { - if (!(obj instanceof DebuggerCoordinates)) { + if (!(obj instanceof DebuggerCoordinates that)) { return false; } - DebuggerCoordinates that = (DebuggerCoordinates) obj; if (!Objects.equals(this.trace, that.trace)) { return false; } + if (!Objects.equals(this.platform, that.platform)) { + return false; + } if (!Objects.equals(this.target, that.target)) { return false; } @@ -436,7 +438,6 @@ public class DebuggerCoordinates { if (!Objects.equals(this.trace, that.trace)) { return false; } - if (!Objects.equals(this.platform, that.platform)) { return false; } diff --git a/Ghidra/Debug/Debugger-rmi-trace/src/main/py/src/ghidratrace/client.py b/Ghidra/Debug/Debugger-rmi-trace/src/main/py/src/ghidratrace/client.py index b78b251376..90787214d7 100644 --- a/Ghidra/Debug/Debugger-rmi-trace/src/main/py/src/ghidratrace/client.py +++ b/Ghidra/Debug/Debugger-rmi-trace/src/main/py/src/ghidratrace/client.py @@ -1376,8 +1376,9 @@ class Receiver(Thread): Client._write_value( reply.xreply_invoke_method.return_value, result) except BaseException as e: - print("Error caused by front end") - traceback.print_exc() + print(f"Error caused by front end: {e}") + # TODO: Add a field to error for stacktrace, log it at front-end + # traceback.print_exc() reply.xreply_invoke_method.error = repr(e) self.client._send(reply) diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/platform/DebuggerPlatformPlugin.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/platform/DebuggerPlatformPlugin.java index a7f79e2a08..e047f7726b 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/platform/DebuggerPlatformPlugin.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/platform/DebuggerPlatformPlugin.java @@ -4,9 +4,9 @@ * 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. @@ -75,7 +75,7 @@ public class DebuggerPlatformPlugin extends Plugin { } } - protected interface ChooseMorePlatformsActon { + protected interface ChooseMorePlatformsAction { String NAME = DebuggerResources.NAME_CHOOSE_MORE_PLATFORMS; String TITLE = DebuggerResources.TITLE_CHOOSE_MORE_PLATFORMS; String DESCRIPTION = DebuggerResources.DESCRIPTION_CHOOSE_MORE_PLATFORMS; @@ -92,28 +92,13 @@ public class DebuggerPlatformPlugin extends Plugin { } } - protected static boolean sameCoordinates(DebuggerCoordinates a, DebuggerCoordinates b) { - if (!Objects.equals(a.getTrace(), b.getTrace())) { - return false; - } - if (!Objects.equals(a.getTime(), b.getTime())) { - return false; - } - if (!Objects.equals(a.getObject(), b.getObject())) { - return false; - } - return true; - } - protected class PlatformActionSet { private final Trace trace; - private DebuggerCoordinates current; private final Map actions = new LinkedHashMap<>(); public PlatformActionSet(Trace trace) { this.trace = trace; - this.current = traceManager.getCurrentFor(trace); } protected Set computePlatformOffers(boolean includeOverrides) { @@ -126,15 +111,16 @@ public class DebuggerPlatformPlugin extends Plugin { ToggleDockingAction action = ChoosePlatformAction.builder(DebuggerPlatformPlugin.this) .menuPath(DebuggerPluginPackage.NAME, ChoosePlatformAction.NAME, offer.getDescription()) - .onAction(ctx -> activatePlatform(offer)) + .onAction(ctx -> activatePlatformOffer(offer)) .build(); String[] path = action.getMenuBarData().getMenuPath(); tool.setMenuGroup(Arrays.copyOf(path, path.length - 1), ChoosePlatformAction.GROUP); return action; } - protected void activatePlatform(DebuggerPlatformOffer offer) { - platformService.setCurrentMapperFor(trace, offer.take(tool, trace), current.getSnap()); + protected void activatePlatformOffer(DebuggerPlatformOffer offer) { + platformService.setCurrentMapperFor(trace, current.getObject(), offer.take(tool, trace), + current.getSnap()); } protected void cleanOffers() { @@ -159,7 +145,7 @@ public class DebuggerPlatformPlugin extends Plugin { protected void addChosenOffer(DebuggerPlatformOffer offer) { ToggleDockingAction action = addOfferAction(offer); // NB. PluginEvent will cause selections to update - if (currentTrace == trace) { + if (current.getTrace() == trace) { tool.addAction(action); } } @@ -176,15 +162,6 @@ public class DebuggerPlatformPlugin extends Plugin { } } - protected void coordinatesActivated(DebuggerCoordinates coordinates) { - if (sameCoordinates(current, coordinates)) { - current = coordinates; - return; - } - current = coordinates; - updatePlatformOffers(); - } - protected void mapperActivated(DebuggerPlatformMapper mapper) { for (Entry ent : actions.entrySet()) { DebuggerPlatformOffer offer = ent.getKey(); @@ -201,7 +178,7 @@ public class DebuggerPlatformPlugin extends Plugin { @SuppressWarnings("unused") private final Wiring autoServiceWiring; - private Trace currentTrace; + private DebuggerCoordinates current = DebuggerCoordinates.NOWHERE; private final ChangeListener classChangeListener = evt -> this.classesChanged(); @@ -221,18 +198,19 @@ public class DebuggerPlatformPlugin extends Plugin { } protected void installActions() { - if (currentTrace == null) { + Trace trace = current.getTrace(); + if (trace == null) { return; } PlatformActionSet actions = - actionsChoosePlatform.computeIfAbsent(currentTrace, PlatformActionSet::new); + actionsChoosePlatform.computeIfAbsent(trace, PlatformActionSet::new); actions.updatePlatformOffers(); - actions.mapperActivated(platformService.getCurrentMapperFor(currentTrace)); + actions.mapperActivated(platformService.getCurrentMapperFor(trace)); actions.installActions(); } protected void uninstallActions() { - PlatformActionSet actions = actionsChoosePlatform.get(currentTrace); + PlatformActionSet actions = actionsChoosePlatform.get(current.getTrace()); if (actions != null) { actions.uninstallActions(); } @@ -240,8 +218,8 @@ public class DebuggerPlatformPlugin extends Plugin { protected void createActions() { installActions(); - actionMore = ChooseMorePlatformsActon.builder(this) - .enabledWhen(ctx -> currentTrace != null) + actionMore = ChooseMorePlatformsAction.builder(this) + .enabledWhen(ctx -> current.getTrace() != null) .onAction(this::activatedChooseMore) .buildAndInstall(tool); String[] path = actionMore.getMenuBarData().getMenuPath(); @@ -254,13 +232,11 @@ public class DebuggerPlatformPlugin extends Plugin { // Still initializing or finalizing return; } - // Sort of a backwards way to retrieve the current coordinates.... - PlatformActionSet actions = actionsChoosePlatform.get(currentTrace); + Trace trace = current.getTrace(); + PlatformActionSet actions = actionsChoosePlatform.get(trace); if (actions == null) { return; } - DebuggerCoordinates current = actions.current; - Trace trace = current.getTrace(); TraceObject object = current.getObject(); long snap = current.getSnap(); @@ -268,7 +244,7 @@ public class DebuggerPlatformPlugin extends Plugin { // Dialog allows Swing to do other things, so re-check platformService if (offer != null && platformService != null) { actions.addChosenOffer(offer); - platformService.setCurrentMapperFor(trace, offer.take(tool, trace), snap); + platformService.setCurrentMapperFor(trace, object, offer.take(tool, trace), snap); // NOTE: DebuggerPlatformPluginEvent will cause selection change } } @@ -282,21 +258,24 @@ public class DebuggerPlatformPlugin extends Plugin { } protected void coordinatesActivated(DebuggerCoordinates coordinates) { + if (Objects.equals(current, coordinates)) { + return; + } uninstallActions(); - this.currentTrace = coordinates.getTrace(); + this.current = coordinates; installActions(); tool.contextChanged(null); } protected void traceClosed(Trace trace) { - if (trace == currentTrace) { + if (trace == current.getTrace()) { coordinatesActivated(DebuggerCoordinates.NOWHERE); } actionsChoosePlatform.remove(trace); } protected void mapperActivated(Trace trace, DebuggerPlatformMapper mapper) { - if (trace != currentTrace) { + if (trace != current.getTrace()) { return; } PlatformActionSet actions = actionsChoosePlatform.get(trace); 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 61c2340c20..3674d2c27f 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 @@ -17,7 +17,6 @@ package ghidra.app.plugin.core.debug.mapping; import java.util.Collection; import java.util.Comparator; -import java.util.Map.Entry; import java.util.stream.Collectors; import ghidra.app.plugin.core.debug.disassemble.DisassemblyInject; @@ -28,7 +27,6 @@ import ghidra.framework.plugintool.PluginTool; import ghidra.program.model.address.*; import ghidra.program.model.lang.Endian; import ghidra.trace.model.Trace; -import ghidra.trace.model.TraceAddressSnapRange; import ghidra.trace.model.guest.TracePlatform; import ghidra.trace.model.listing.TraceInstruction; import ghidra.trace.model.memory.TraceMemoryOperations; @@ -75,6 +73,10 @@ public abstract class AbstractDebuggerPlatformMapper implements DebuggerPlatform TraceMemoryState.KNOWN); } + protected TracePlatform getDisassemblyPlatform(TraceObject object, Address start, long snap) { + return trace.getPlatformManager().getPlatform(getCompilerSpec(object, snap)); + } + protected Collection getDisassemblyInjections(TracePlatform platform) { return ClassSearcher.getInstances(DisassemblyInject.class) .stream() @@ -89,8 +91,8 @@ public abstract class AbstractDebuggerPlatformMapper implements DebuggerPlatform if (isCancelSilently(start, snap)) { return DisassemblyResult.CANCELLED; } - TracePlatform platform = trace.getPlatformManager().getPlatform(getCompilerSpec(object)); + TracePlatform platform = getDisassemblyPlatform(object, start, snap); Collection injects = getDisassemblyInjections(platform); TraceDisassembleCommand dis = new TraceDisassembleCommand(platform, start, restricted); AddressSet startSet = new AddressSet(start); diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/DefaultDebuggerPlatformMapper.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/DefaultDebuggerPlatformMapper.java index 3a8bc05668..2e5505839b 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/DefaultDebuggerPlatformMapper.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/DefaultDebuggerPlatformMapper.java @@ -4,9 +4,9 @@ * 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. @@ -49,24 +49,28 @@ public class DefaultDebuggerPlatformMapper extends AbstractDebuggerPlatformMappe } @Override - public CompilerSpec getCompilerSpec(TraceObject object) { + public CompilerSpec getCompilerSpec(TraceObject object, long snap) { return cSpec; } - @Override - public void addToTrace(long snap) { + protected TracePlatform addOrGetPlatform(CompilerSpec cSpec, long snap) { String description = "Add guest " + cSpec.getLanguage().getLanguageDescription() + "/" + cSpec.getCompilerSpecDescription(); try (Transaction tx = trace.openTransaction(description)) { TracePlatformManager platformManager = trace.getPlatformManager(); TracePlatform platform = platformManager.getOrAddPlatform(cSpec); - if (platform.isHost()) { - return; + if (!platform.isHost()) { + addMappedRanges((TraceGuestPlatform) platform); } - addMappedRanges((TraceGuestPlatform) platform); + return platform; } } + @Override + public TracePlatform addToTrace(TraceObject newFocus, long snap) { + return addOrGetPlatform(cSpec, snap); + } + /** * Add mapped ranges if not already present * diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/HostDebuggerPlatformOpinion.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/HostDebuggerPlatformOpinion.java index 4f3fd29ef8..a5289f32b9 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/HostDebuggerPlatformOpinion.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/HostDebuggerPlatformOpinion.java @@ -22,6 +22,7 @@ import ghidra.framework.plugintool.PluginTool; import ghidra.program.model.lang.CompilerSpec; import ghidra.program.model.lang.Processor; import ghidra.trace.model.Trace; +import ghidra.trace.model.guest.TracePlatform; import ghidra.trace.model.target.TraceObject; /** @@ -30,6 +31,8 @@ import ghidra.trace.model.target.TraceObject; * the real language must be mapped as a guest platform. */ public class HostDebuggerPlatformOpinion implements DebuggerPlatformOpinion { + // An alternative default had better mean it. + public static final int CONFIDENCE_HOST_KNOWN = 10000; protected static class HostDebuggerPlatformMapper extends AbstractDebuggerPlatformMapper { public HostDebuggerPlatformMapper(PluginTool tool, Trace trace) { @@ -37,13 +40,13 @@ public class HostDebuggerPlatformOpinion implements DebuggerPlatformOpinion { } @Override - public CompilerSpec getCompilerSpec(TraceObject object) { + public CompilerSpec getCompilerSpec(TraceObject object, long snap) { return trace.getBaseCompilerSpec(); } @Override - public void addToTrace(long snap) { - // Nothing to do + public TracePlatform addToTrace(TraceObject newFocus, long snap) { + return trace.getPlatformManager().getHostPlatform(); } @Override @@ -79,7 +82,7 @@ public class HostDebuggerPlatformOpinion implements DebuggerPlatformOpinion { @Override public int getConfidence() { - return 10000; // An alternative default had better mean it. Really. + return CONFIDENCE_HOST_KNOWN; } }; diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/dbgeng/DbgengDebuggerPlatformOpinion.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/dbgeng/DbgengDebuggerPlatformOpinion.java index e56248a251..46affd815f 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/dbgeng/DbgengDebuggerPlatformOpinion.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/dbgeng/DbgengDebuggerPlatformOpinion.java @@ -4,9 +4,9 @@ * 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. @@ -15,47 +15,166 @@ */ package ghidra.app.plugin.core.debug.platform.dbgeng; +import java.io.IOException; import java.util.Collection; import java.util.Set; +import java.util.concurrent.*; +import java.util.stream.Collectors; import ghidra.app.plugin.core.debug.disassemble.DisassemblyInject; +import ghidra.app.plugin.core.debug.gui.action.PCLocationTrackingSpec; import ghidra.app.plugin.core.debug.mapping.*; +import ghidra.app.services.DebuggerTargetService; +import ghidra.app.util.bin.ByteProvider; +import ghidra.app.util.bin.MemBufferByteProvider; +import ghidra.app.util.bin.format.pe.*; +import ghidra.app.util.bin.format.pe.PortableExecutable.SectionLayout; import ghidra.debug.api.platform.DebuggerPlatformMapper; +import ghidra.debug.api.target.Target; +import ghidra.debug.api.tracemgr.DebuggerCoordinates; import ghidra.framework.plugintool.PluginTool; +import ghidra.program.model.address.Address; +import ghidra.program.model.address.AddressSet; import ghidra.program.model.lang.*; +import ghidra.program.model.mem.MemBuffer; import ghidra.trace.model.Trace; import ghidra.trace.model.guest.TracePlatform; +import ghidra.trace.model.modules.TraceModule; import ghidra.trace.model.target.TraceObject; +import ghidra.util.Msg; +import ghidra.util.task.TaskMonitor; public class DbgengDebuggerPlatformOpinion extends AbstractDebuggerPlatformOpinion { protected static final LanguageID LANG_ID_X86_64 = new LanguageID("x86:LE:64:default"); + protected static final LanguageID LANG_ID_X86_64_32 = new LanguageID("x86:LE:64:compat32"); protected static final CompilerSpecID COMP_ID_VS = new CompilerSpecID("windows"); protected static final Set INJECTS = Set.of(new DbgengX64DisassemblyInject()); - protected static class DbgengX64DebuggerPlatformMapper extends DefaultDebuggerPlatformMapper { - public DbgengX64DebuggerPlatformMapper(PluginTool tool, Trace trace, CompilerSpec cSpec) { + enum Mode { + X64, X86, UNK; + + static Mode computeFor(PluginTool tool, Trace trace, Address address, long snap) { + DebuggerTargetService targetService = tool.getService(DebuggerTargetService.class); + Target target = targetService == null ? null : targetService.getTarget(trace); + Collection modules = + trace.getModuleManager().getModulesAt(snap, address); + Msg.debug(Mode.class, "Disassembling in modules: " + + modules.stream().map(m -> m.getName(snap)).collect(Collectors.joining(","))); + Set modes = modules.stream() + .map(m -> modeForModule(target, trace, snap, m)) + .filter(m -> m != UNK) + .collect(Collectors.toSet()); + Msg.debug(Mode.class, "Disassembling in mode(s): " + modes); + if (modes.size() != 1) { + return UNK; + } + return modes.iterator().next(); + } + + static Mode modeForModule(Target target, Trace trace, long snap, TraceModule module) { + if (target != null && target.getSnap() == snap) { + AddressSet set = new AddressSet(); + set.add(module.getBase(snap), module.getBase(snap)); // Recorder should read page + try { + target.readMemoryAsync(set, TaskMonitor.DUMMY).get(1, TimeUnit.SECONDS); + trace.flushEvents(); + } + catch (InterruptedException | ExecutionException | TimeoutException e) { + throw new AssertionError(e); + } + } + MemBuffer bufferAt = trace.getMemoryManager().getBufferAt(snap, module.getBase(snap)); + try (ByteProvider bp = new MemBufferByteProvider(bufferAt)) { + PortableExecutable pe = + new PortableExecutable(bp, SectionLayout.MEMORY, false, false); + NTHeader ntHeader = pe.getNTHeader(); + if (ntHeader == null) { + return UNK; + } + OptionalHeader optionalHeader = ntHeader.getOptionalHeader(); + if (optionalHeader == null) { + return UNK; // Really shouldn't happen, but who knows? + } + return optionalHeader.is64bit() ? X64 : X86; + } + catch (IOException e) { + Msg.warn(Mode.class, "Could not parse PE from trace: " + e); + return UNK; + } + } + } + + protected abstract static class AbstractDbgengX64DebuggerPlatformMapper + extends DefaultDebuggerPlatformMapper { + public AbstractDbgengX64DebuggerPlatformMapper(PluginTool tool, Trace trace, + CompilerSpec cSpec) { super(tool, trace, cSpec); } // TODO: Map registers: efl,rfl,rflags->eflags + @Override + protected TracePlatform getDisassemblyPlatform(TraceObject object, Address start, + long snap) { + CompilerSpec x64cs = Offer.X64.getCompilerSpec(); + return addOrGetPlatform(x64cs, snap); + } + @Override protected Collection getDisassemblyInjections(TracePlatform platform) { return INJECTS; } } - enum Offers implements DebuggerPlatformOffer { + protected static class DbgengX64DebuggerPlatformMapper + extends AbstractDbgengX64DebuggerPlatformMapper { + public DbgengX64DebuggerPlatformMapper(PluginTool tool, Trace trace, + CompilerSpec cSpec) { + super(tool, trace, cSpec); + } + } + + protected static class DbgengX64_32DebuggerPlatformMapper + extends AbstractDbgengX64DebuggerPlatformMapper { + public DbgengX64_32DebuggerPlatformMapper(PluginTool tool, Trace trace, + CompilerSpec cSpec) { + super(tool, trace, cSpec); + } + } + + protected static class DbgengWoW64DebuggerPlatformMapper + extends AbstractDbgengX64DebuggerPlatformMapper { + public DbgengWoW64DebuggerPlatformMapper(PluginTool tool, Trace trace, + CompilerSpec cSpec) { + super(tool, trace, cSpec); + } + + @Override + public TracePlatform addToTrace(TraceObject newFocus, long snap) { + DebuggerCoordinates coords = DebuggerCoordinates.NOWHERE.object(newFocus).snap(snap); + Address pc = PCLocationTrackingSpec.INSTANCE.computeTraceAddress(tool, coords); + if (pc == null) { + return addOrGetPlatform(Offer.X64_32.getCompilerSpec(), snap); + } + Offer sel = switch (Mode.computeFor(tool, trace, pc, snap)) { + case X64 -> Offer.X64; + default -> Offer.X64_32; + }; + return addOrGetPlatform(sel.getCompilerSpec(), snap); + } + } + + enum Offer implements DebuggerPlatformOffer { // TODO: X86? X64 { @Override public String getDescription() { - return "Dbgeng on Windows x64"; + return "Dbgeng x64 (64-bit module)"; } @Override public int getConfidence() { - return 100; + return HostDebuggerPlatformOpinion.CONFIDENCE_HOST_KNOWN + 10; } @Override @@ -72,7 +191,60 @@ public class DbgengDebuggerPlatformOpinion extends AbstractDebuggerPlatformOpini public boolean isCreatorOf(DebuggerPlatformMapper mapper) { return mapper.getClass() == DbgengX64DebuggerPlatformMapper.class; } - }; + }, + X64_32 { + @Override + public String getDescription() { + return "Dbgeng x64 (32-bit module)"; + } + + @Override + public int getConfidence() { + return HostDebuggerPlatformOpinion.CONFIDENCE_HOST_KNOWN + 10; + } + + @Override + public CompilerSpec getCompilerSpec() { + return getCompilerSpec(LANG_ID_X86_64_32, COMP_ID_VS); + } + + @Override + public DebuggerPlatformMapper take(PluginTool tool, Trace trace) { + return new DbgengX64_32DebuggerPlatformMapper(tool, trace, getCompilerSpec()); + } + + @Override + public boolean isCreatorOf(DebuggerPlatformMapper mapper) { + return mapper.getClass() == DbgengX64_32DebuggerPlatformMapper.class; + } + }, + WOW64 { + @Override + public String getDescription() { + return "Dbgeng x64 (WoW64)"; + } + + @Override + public int getConfidence() { + return HostDebuggerPlatformOpinion.CONFIDENCE_HOST_KNOWN + 20; + } + + @Override + public CompilerSpec getCompilerSpec() { + // Report x86-32 in opinions, even though we mix + return getCompilerSpec(LANG_ID_X86_64_32, COMP_ID_VS); + } + + @Override + public DebuggerPlatformMapper take(PluginTool tool, Trace trace) { + return new DbgengWoW64DebuggerPlatformMapper(tool, trace, getCompilerSpec()); + } + + @Override + public boolean isCreatorOf(DebuggerPlatformMapper mapper) { + return mapper.getClass() == DbgengWoW64DebuggerPlatformMapper.class; + } + },; } @Override @@ -85,6 +257,6 @@ public class DbgengDebuggerPlatformOpinion extends AbstractDebuggerPlatformOpini if (!is64Bit) { return Set.of(); } - return Set.of(Offers.X64); + return Set.of(Offer.X64, Offer.X64_32, Offer.WOW64); } } diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/dbgeng/DbgengX64DisassemblyInject.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/dbgeng/DbgengX64DisassemblyInject.java index 2c25fe9dad..ee25112275 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/dbgeng/DbgengX64DisassemblyInject.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/dbgeng/DbgengX64DisassemblyInject.java @@ -15,32 +15,19 @@ */ package ghidra.app.plugin.core.debug.platform.dbgeng; -import java.io.IOException; import java.math.BigInteger; -import java.util.Collection; -import java.util.Set; -import java.util.stream.Collectors; import ghidra.app.plugin.core.debug.disassemble.*; import ghidra.app.plugin.core.debug.disassemble.DisassemblyInjectInfo.PlatformInfo; -import ghidra.app.services.DebuggerTargetService; -import ghidra.app.util.bin.ByteProvider; -import ghidra.app.util.bin.MemBufferByteProvider; -import ghidra.app.util.bin.format.pe.*; -import ghidra.app.util.bin.format.pe.PortableExecutable.SectionLayout; -import ghidra.debug.api.target.Target; +import ghidra.app.plugin.core.debug.platform.dbgeng.DbgengDebuggerPlatformOpinion.Mode; import ghidra.framework.plugintool.PluginTool; -import ghidra.program.model.address.*; +import ghidra.program.model.address.AddressRange; +import ghidra.program.model.address.AddressSetView; import ghidra.program.model.lang.*; -import ghidra.program.model.mem.MemBuffer; import ghidra.program.util.ProgramContextImpl; import ghidra.trace.model.Trace; import ghidra.trace.model.guest.TracePlatform; -import ghidra.trace.model.modules.TraceModule; import ghidra.trace.model.thread.TraceThread; -import ghidra.util.Msg; -import ghidra.util.exception.CancelledException; -import ghidra.util.task.TaskMonitor; @DisassemblyInjectInfo( platforms = { @@ -48,11 +35,6 @@ import ghidra.util.task.TaskMonitor; @PlatformInfo(langID = "x86:LE:64:default", compilerID = "clangwindows"), }) public class DbgengX64DisassemblyInject implements DisassemblyInject { - - enum Mode { - X64, X86, UNK; - } - @Override public void pre(PluginTool tool, TraceDisassembleCommand command, TracePlatform platform, long snap, TraceThread thread, AddressSetView startSet, AddressSetView restricted) { @@ -61,21 +43,11 @@ public class DbgengX64DisassemblyInject implements DisassemblyInject { return; } Trace trace = platform.getTrace(); - DebuggerTargetService targetService = tool.getService(DebuggerTargetService.class); - Target target = targetService == null ? null : targetService.getTarget(trace); - Collection modules = - trace.getModuleManager().getModulesAt(snap, first.getMinAddress()); - Msg.debug(this, "Disassembling in modules: " + - modules.stream().map(m -> m.getName(snap)).collect(Collectors.joining(","))); - Set modes = modules.stream() - .map(m -> modeForModule(target, trace, snap, m)) - .filter(m -> m != Mode.UNK) - .collect(Collectors.toSet()); - Msg.debug(this, "Disassembling in mode(s): " + modes); - if (modes.size() != 1) { + + Mode mode = Mode.computeFor(tool, trace, first.getMinAddress(), snap); + if (mode == Mode.UNK) { return; } - Mode mode = modes.iterator().next(); Language language = platform.getLanguage(); Register longModeReg = language.getRegister("longMode"); @@ -96,36 +68,4 @@ public class DbgengX64DisassemblyInject implements DisassemblyInject { default -> throw new AssertionError(); }); } - - protected Mode modeForModule(Target target, Trace trace, long snap, - TraceModule module) { - if (target != null && target.getSnap() == snap) { - AddressSet set = new AddressSet(); - set.add(module.getBase(snap), module.getBase(snap)); // Recorder should read page - try { - target.readMemory(set, TaskMonitor.DUMMY); - trace.flushEvents(); - } - catch (CancelledException e) { - throw new AssertionError(e); - } - } - MemBuffer bufferAt = trace.getMemoryManager().getBufferAt(snap, module.getBase(snap)); - try (ByteProvider bp = new MemBufferByteProvider(bufferAt)) { - PortableExecutable pe = new PortableExecutable(bp, SectionLayout.MEMORY, false, false); - NTHeader ntHeader = pe.getNTHeader(); - if (ntHeader == null) { - return Mode.UNK; - } - OptionalHeader optionalHeader = ntHeader.getOptionalHeader(); - if (optionalHeader == null) { - return Mode.UNK; // Really shouldn't happen, but who knows? - } - return optionalHeader.is64bit() ? Mode.X64 : Mode.X86; - } - catch (IOException e) { - Msg.warn(this, "Could not parse PE from trace: " + e); - return Mode.UNK; - } - } } diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/platform/DebuggerPlatformServicePlugin.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/platform/DebuggerPlatformServicePlugin.java index b5c7461d3c..b3ab9d2eba 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/platform/DebuggerPlatformServicePlugin.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/platform/DebuggerPlatformServicePlugin.java @@ -4,9 +4,9 @@ * 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. @@ -21,7 +21,8 @@ import ghidra.app.plugin.PluginCategoryNames; import ghidra.app.plugin.core.debug.DebuggerPluginPackage; import ghidra.app.plugin.core.debug.event.DebuggerPlatformPluginEvent; import ghidra.app.plugin.core.debug.event.TraceClosedPluginEvent; -import ghidra.app.plugin.core.debug.mapping.*; +import ghidra.app.plugin.core.debug.mapping.DebuggerPlatformOffer; +import ghidra.app.plugin.core.debug.mapping.DebuggerPlatformOpinion; import ghidra.app.services.DebuggerPlatformService; import ghidra.app.services.DebuggerTraceManagerService; import ghidra.debug.api.platform.DebuggerPlatformMapper; @@ -29,6 +30,7 @@ import ghidra.framework.plugintool.*; import ghidra.framework.plugintool.annotation.AutoServiceConsumed; import ghidra.framework.plugintool.util.PluginStatus; import ghidra.trace.model.Trace; +import ghidra.trace.model.guest.TracePlatform; import ghidra.trace.model.target.TraceObject; @PluginInfo( @@ -85,7 +87,6 @@ public class DebuggerPlatformServicePlugin extends Plugin implements DebuggerPla } mappersByTrace.put(trace, mapper); } - mapper.addToTrace(snap); firePluginEvent(new DebuggerPlatformPluginEvent(getName(), trace, mapper)); return mapper; } @@ -103,7 +104,7 @@ public class DebuggerPlatformServicePlugin extends Plugin implements DebuggerPla } @Override - public void setCurrentMapperFor(Trace trace, DebuggerPlatformMapper mapper, long snap) { + public void setCurrentMapperFor(Trace trace, TraceObject focus, DebuggerPlatformMapper mapper, long snap) { Objects.requireNonNull(trace); Objects.requireNonNull(mapper); if (!traceManager.getOpenTraces().contains(trace)) { @@ -112,8 +113,11 @@ public class DebuggerPlatformServicePlugin extends Plugin implements DebuggerPla synchronized (mappersByTrace) { mappersByTrace.put(trace, mapper); } - mapper.addToTrace(snap); + TracePlatform platform = mapper.addToTrace(focus, snap); firePluginEvent(new DebuggerPlatformPluginEvent(getName(), trace, mapper)); + if (traceManager.getCurrentTrace() == trace) { + traceManager.activatePlatform(platform); + } } @Override diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/tracemgr/DebuggerTraceManagerServicePlugin.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/tracemgr/DebuggerTraceManagerServicePlugin.java index 74ba2f6b70..364528bb86 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/tracemgr/DebuggerTraceManagerServicePlugin.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/tracemgr/DebuggerTraceManagerServicePlugin.java @@ -535,8 +535,7 @@ public class DebuggerTraceManagerServicePlugin extends Plugin if (mapper == null) { return coordinates; } - TracePlatform platform = - getPlatformForMapper(coordinates.getTrace(), coordinates.getObject(), mapper); + TracePlatform platform = mapper.addToTrace(coordinates.getObject(), coordinates.getSnap()); return coordinates.platform(platform); } @@ -598,22 +597,22 @@ public class DebuggerTraceManagerServicePlugin extends Plugin return mode.followsPresent(); } - protected TracePlatform getPlatformForMapper(Trace trace, TraceObject object, - DebuggerPlatformMapper mapper) { - return trace.getPlatformManager().getPlatform(mapper.getCompilerSpec(object)); - } - protected void doPlatformMapperSelected(Trace trace, DebuggerPlatformMapper mapper) { synchronized (listenersByTrace) { if (!listenersByTrace.containsKey(trace)) { return; } LastCoords cur = lastCoordsByTrace.getOrDefault(trace, LastCoords.NEVER); - DebuggerCoordinates adj = - cur.coords.platform(getPlatformForMapper(trace, cur.coords.getObject(), mapper)); + TracePlatform platform = + mapper.addToTrace(cur.coords.getObject(), cur.coords.getSnap()); + if (cur.coords.getPlatform() == platform) { + return; + } + DebuggerCoordinates adj = cur.coords.platform(platform); lastCoordsByTrace.put(trace, cur.keepTime(adj)); if (trace == current.getTrace()) { current = adj; + trace.getProgramView().setPlatform(adj.getPlatform()); fireLocationEvent(adj, ActivationCause.MAPPER_CHANGED); } } @@ -789,6 +788,7 @@ public class DebuggerTraceManagerServicePlugin extends Plugin return; // We navigated elsewhere before emulation completed } varView.setSnap(snap); + varView.setPlatform(coordinates.getPlatform()); fireLocationEvent(coordinates, cause); }, cause == ActivationCause.EMU_STATE_EDIT ? SwingExecutorService.MAYBE_NOW // ProgramView may call .get on Swing thread @@ -1167,7 +1167,9 @@ public class DebuggerTraceManagerServicePlugin extends Plugin if (current.getTrace() != newTrace) { // The snap needs to match upon re-activating this trace. try { - newTrace.getProgramView().setSnap(coordinates.getViewSnap()); + TraceVariableSnapProgramView view = newTrace.getProgramView(); + view.setSnap(coordinates.getViewSnap()); + view.setPlatform(coordinates.getPlatform()); } catch (TraceClosedException e) { // Presumably, a closed event is queued diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/stack/AnalysisUnwoundFrame.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/stack/AnalysisUnwoundFrame.java index 35d23cb877..8e931d3ea3 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/stack/AnalysisUnwoundFrame.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/stack/AnalysisUnwoundFrame.java @@ -4,9 +4,9 @@ * 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. @@ -22,8 +22,8 @@ import java.util.stream.Collectors; import ghidra.app.plugin.core.bookmark.BookmarkNavigator; import ghidra.app.services.DebuggerControlService.StateEditor; -import ghidra.debug.api.tracemgr.DebuggerCoordinates; import ghidra.app.services.DebuggerStaticMappingService; +import ghidra.debug.api.tracemgr.DebuggerCoordinates; import ghidra.framework.plugintool.PluginTool; import ghidra.pcode.exec.BytesPcodeArithmetic; import ghidra.pcode.exec.PcodeExecutorState; @@ -352,7 +352,7 @@ public class AnalysisUnwoundFrame extends AbstractUnwoundFrame { spPlusParams.add(structure.getLength() - 1)), false, monitor); TraceData frame = trace.getCodeManager() .definedData() - .create(span, spPlusParams, structure); + .create(span, spPlusParams, platform, structure); frame.setComment(CodeUnit.PRE_COMMENT, getDescription()); TraceReferenceManager refs = trace.getReferenceManager(); refs.clearReferencesFrom(span, frame.getRange()); diff --git a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/platform/DebuggerPlatformPluginTest.java b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/platform/DebuggerPlatformPluginTest.java index d7c6609c08..8a7a490c3f 100644 --- a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/platform/DebuggerPlatformPluginTest.java +++ b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/platform/DebuggerPlatformPluginTest.java @@ -4,9 +4,9 @@ * 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. @@ -29,6 +29,7 @@ import ghidra.app.plugin.core.debug.gui.AbstractGhidraHeadedDebuggerTest; import ghidra.app.plugin.core.debug.mapping.DebuggerPlatformOffer; import ghidra.app.services.DebuggerPlatformService; import ghidra.debug.api.platform.DebuggerPlatformMapper; +import ghidra.debug.api.tracemgr.DebuggerCoordinates; import ghidra.program.model.lang.LanguageID; import ghidra.trace.database.ToyDBTraceBuilder; @@ -73,10 +74,12 @@ public class DebuggerPlatformPluginTest extends AbstractGhidraHeadedDebuggerTest public void testActionMore() throws Throwable { createAndOpenTrace("DATA:BE:64:default"); traceManager.activateTrace(tb.trace); - + chooseLanguageIDViaMore(new LanguageID("Toy:BE:64:default")); DebuggerPlatformMapper mapper = platformService.getCurrentMapperFor(tb.trace); - assertEquals(new LanguageID("Toy:BE:64:default"), mapper.getLangauge(null).getLanguageID()); + DebuggerCoordinates current = traceManager.getCurrent(); + assertEquals(new LanguageID("Toy:BE:64:default"), + mapper.getLangauge(current.getObject(), current.getSnap()).getLanguageID()); } @Test diff --git a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/service/emulation/DebuggerEmulationServiceTest.java b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/service/emulation/DebuggerEmulationServiceTest.java index 77f796e33e..5ec079b998 100644 --- a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/service/emulation/DebuggerEmulationServiceTest.java +++ b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/service/emulation/DebuggerEmulationServiceTest.java @@ -334,7 +334,7 @@ public class DebuggerEmulationServiceTest extends AbstractGhidraHeadedDebuggerTe .findAny() .orElse(null) .take(tool, tb.trace); - platformPlugin.setCurrentMapperFor(tb.trace, mapper, 0); + platformPlugin.setCurrentMapperFor(tb.trace, null, mapper, 0); waitForSwing(); waitForPass(() -> assertEquals(x64, traceManager.getCurrentPlatform().getLanguage())); diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/DBTrace.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/DBTrace.java index 09da21e3c8..69240ad4ea 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/DBTrace.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/DBTrace.java @@ -4,9 +4,9 @@ * 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. @@ -335,10 +335,11 @@ public class DBTrace extends DBCachedDomainObjectAdapter implements Trace, Trace } @DependentService - protected DBTraceDataTypeManager createDataTypeManager() + protected DBTraceDataTypeManager createDataTypeManager(DBTracePlatformManager platformManager) throws CancelledException, IOException { return createTraceManager("Data Type Manager", (openMode, - monitor) -> new DBTraceDataTypeManager(dbh, openMode, rwLock, monitor, this)); + monitor) -> new DBTraceDataTypeManager(dbh, openMode, rwLock, monitor, this, + platformManager.getHostPlatform())); } @DependentService @@ -352,7 +353,7 @@ public class DBTrace extends DBCachedDomainObjectAdapter implements Trace, Trace @DependentService protected DBTracePlatformManager createPlatformManager() throws CancelledException, IOException { - return createTraceManager("Language Manager", + return createTraceManager("Platform Manager", (openMode, monitor) -> new DBTracePlatformManager(dbh, openMode, rwLock, monitor, baseCompilerSpec, this)); } @@ -514,7 +515,7 @@ public class DBTrace extends DBCachedDomainObjectAdapter implements Trace, Trace } @Override - public DBTraceDataTypeManager getDataTypeManager() { + public DBTraceDataTypeManager getBaseDataTypeManager() { return dataTypeManager; } @@ -761,8 +762,6 @@ public class DBTrace extends DBCachedDomainObjectAdapter implements Trace, Trace } } - // TODO: Platform option? - public void setExecutablePath(String path) { getOptions(TRACE_INFO).setString(EXECUTABLE_PATH, path); } diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/data/DBTraceDataTypeManager.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/data/DBTraceDataTypeManager.java index 832d6e0e50..c54a3ce63e 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/data/DBTraceDataTypeManager.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/data/DBTraceDataTypeManager.java @@ -4,9 +4,9 @@ * 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. @@ -26,11 +26,12 @@ import ghidra.framework.data.OpenMode; import ghidra.framework.model.DomainFile; import ghidra.program.database.data.ProgramBasedDataTypeManagerDB; import ghidra.program.model.address.Address; -import ghidra.program.model.address.AddressFactory; import ghidra.program.model.data.*; -import ghidra.program.model.lang.*; import ghidra.trace.database.DBTrace; import ghidra.trace.database.DBTraceManager; +import ghidra.trace.database.guest.DBTraceGuestPlatform; +import ghidra.trace.database.guest.DBTracePlatformManager.DBTraceHostPlatform; +import ghidra.trace.database.guest.InternalTracePlatform; import ghidra.trace.model.data.TraceBasedDataTypeManager; import ghidra.util.InvalidNameException; import ghidra.util.UniversalID; @@ -41,35 +42,37 @@ import ghidra.util.task.TaskMonitor; public class DBTraceDataTypeManager extends ProgramBasedDataTypeManagerDB implements TraceBasedDataTypeManager, DBTraceManager { - protected final ReadWriteLock lock; // TODO: This lock object is not used + /** + * NOTE: This "read-write" lock is actually just a compatibility wrapper around the + * {@link ghidra.util.Lock} for the entire trace database. There was a time when I dreamed of + * using an actual read-write lock (though it's not known if that'd actually achieve any + * appreciable speed up); however, inheriting the existing DataTypeManager implementation + * required its lock to be used throughout the database. Rather than convert all my code (and + * lose the distinction of where I need write vs. read locks), I just wrapped the API. So no, + * this code does not refer to the wrapper, but it does still use the lock. I keep a reference + * to it here in case I ever need it. + */ + protected final ReadWriteLock lock; protected final DBTrace trace; + protected final InternalTracePlatform platform; - private static final String INSTANCE_TABLE_PREFIX = null; // placeholder only + private static String computePrefix(InternalTracePlatform platform) { + return switch (platform) { + case DBTraceHostPlatform host -> null; + case DBTraceGuestPlatform guest -> "Guest%d_".formatted(guest.getIntKey()); + default -> throw new AssertionError(); + }; + } public DBTraceDataTypeManager(DBHandle dbh, OpenMode openMode, ReadWriteLock lock, - TaskMonitor monitor, DBTrace trace) + TaskMonitor monitor, DBTrace trace, InternalTracePlatform platform) throws CancelledException, VersionException, IOException { - super(dbh, null, openMode, INSTANCE_TABLE_PREFIX, trace, trace.getLock(), monitor); + super(dbh, null, openMode, computePrefix(platform), trace, trace.getLock(), monitor); this.lock = lock; // TODO: nothing uses this local lock - not sure what its purpose is this.trace = trace; + this.platform = platform; - setProgramArchitecture(new ProgramArchitecture() { - - @Override - public Language getLanguage() { - return trace.getBaseLanguage(); - } - - @Override - public CompilerSpec getCompilerSpec() { - return trace.getBaseCompilerSpec(); - } - - @Override - public AddressFactory getAddressFactory() { - return trace.getBaseAddressFactory(); - } - }, null, false, monitor); + setProgramArchitecture(platform, null, false, monitor); if (openMode == OpenMode.CREATE) { saveDataOrganization(); @@ -106,6 +109,11 @@ public class DBTraceDataTypeManager extends ProgramBasedDataTypeManagerDB categoryRenamed(CategoryPath.ROOT, getCategory(CategoryPath.ROOT)); } + @Override + public InternalTracePlatform getPlatform() { + return platform; + } + @Override public void sourceArchiveChanged(UniversalID sourceArchiveID) { super.sourceArchiveChanged(sourceArchiveID); diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/guest/DBTraceGuestPlatform.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/guest/DBTraceGuestPlatform.java index 91903664c9..6336111657 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/guest/DBTraceGuestPlatform.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/guest/DBTraceGuestPlatform.java @@ -22,7 +22,9 @@ import java.util.Map.Entry; import org.apache.commons.lang3.tuple.Pair; import db.DBRecord; +import generic.jar.ResourceFile; import ghidra.app.util.PseudoInstruction; +import ghidra.framework.data.OpenMode; import ghidra.lifecycle.Internal; import ghidra.program.model.address.*; import ghidra.program.model.lang.*; @@ -32,8 +34,10 @@ import ghidra.program.model.mem.MemBuffer; import ghidra.program.util.DefaultLanguageService; import ghidra.trace.database.DBTraceUtils.CompilerSpecIDDBFieldCodec; import ghidra.trace.database.DBTraceUtils.LanguageIDDBFieldCodec; +import ghidra.trace.database.data.DBTraceDataTypeManager; import ghidra.trace.model.Lifespan; import ghidra.trace.model.Trace; +import ghidra.trace.model.data.TraceBasedDataTypeManager; import ghidra.trace.model.guest.TraceGuestPlatform; import ghidra.trace.model.guest.TraceGuestPlatformMappedRange; import ghidra.trace.util.*; @@ -156,6 +160,8 @@ public class DBTraceGuestPlatform extends DBAnnotatedObject new TreeMap<>(); protected final AddressSet guestAddressSet = new AddressSet(); + protected DBTraceDataTypeManager dataTypeManager; + public DBTraceGuestPlatform(DBTracePlatformManager manager, DBCachedObjectStore store, DBRecord record) { super(store, record); @@ -187,6 +193,12 @@ public class DBTraceGuestPlatform extends DBAnnotatedObject } } + protected void loadDataTypeManager(OpenMode openMode, TaskMonitor monitor) + throws CancelledException, VersionException, IOException { + this.dataTypeManager = new DBTraceDataTypeManager(manager.dbh, openMode, manager.lock, + monitor, manager.trace, this); + } + @Override public Trace getTrace() { return manager.trace; @@ -235,6 +247,11 @@ public class DBTraceGuestPlatform extends DBAnnotatedObject return compilerSpec; } + @Override + public TraceBasedDataTypeManager getDataTypeManager() { + return dataTypeManager; + } + @Override public void delete(TaskMonitor monitor) throws CancelledException { manager.deleteGuestPlatform(this, monitor); @@ -284,6 +301,12 @@ public class DBTraceGuestPlatform extends DBAnnotatedObject return next.getMaxAddress().add(1); } + private static ResourceFile getSlaFile(Language language) { + SleighLanguageDescription desc = + (SleighLanguageDescription) language.getLanguageDescription(); + return desc.getSlaFile(); + } + @Override public TraceGuestPlatformMappedRange addMappedRegisterRange() throws AddressOverflowException { @@ -292,8 +315,24 @@ public class DBTraceGuestPlatform extends DBAnnotatedObject if (guestRange == null) { return null; // No registers, so we're mapped! } - Address hostMin = manager.computeNextRegisterMin(); long size = guestRange.getLength(); + + /** + * If the two languages are really the same (have the same .sla file), then map + * registers identically. Such languages differ only in their default contextreg values. + */ + ResourceFile hostSla = getSlaFile(manager.hostPlatform.getLanguage()); + ResourceFile guestSla = getSlaFile(getLanguage()); + Address hostMin; + if (Objects.equals(hostSla, guestSla)) { + hostMin = manager.hostPlatform.getAddressFactory() + .getRegisterSpace() + .getAddress(guestRange.getMinAddress().getOffset()); + } + else { + hostMin = manager.computeNextRegisterMin(); + } + return addMappedRange(hostMin, guestRange.getMinAddress(), size); } } diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/guest/DBTracePlatformManager.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/guest/DBTracePlatformManager.java index 2d9a34826f..15afbf3003 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/guest/DBTracePlatformManager.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/guest/DBTracePlatformManager.java @@ -29,6 +29,7 @@ import ghidra.trace.database.DBTrace; import ghidra.trace.database.DBTraceManager; import ghidra.trace.database.guest.DBTraceGuestPlatform.DBTraceGuestLanguage; import ghidra.trace.model.Trace; +import ghidra.trace.model.data.TraceBasedDataTypeManager; import ghidra.trace.model.guest.*; import ghidra.trace.util.TraceChangeRecord; import ghidra.trace.util.TraceEvents; @@ -61,7 +62,8 @@ public class DBTracePlatformManager implements DBTraceManager, TracePlatformMana protected final DBCachedObjectStore rangeMappingStore; - protected final InternalTracePlatform hostPlatform = new InternalTracePlatform() { + @Internal + public class DBTraceHostPlatform implements InternalTracePlatform { @Override public Trace getTrace() { return trace; @@ -97,6 +99,11 @@ public class DBTracePlatformManager implements DBTraceManager, TracePlatformMana return trace.getBaseAddressFactory(); } + @Override + public TraceBasedDataTypeManager getDataTypeManager() { + return trace.getBaseDataTypeManager(); + } + @Override public AddressSetView getHostAddressSet() { return trace.getBaseAddressFactory().getAddressSet(); @@ -146,11 +153,13 @@ public class DBTracePlatformManager implements DBTraceManager, TracePlatformMana public InstructionSet mapGuestInstructionAddressesToHost(InstructionSet set) { return set; } - }; + } + + protected final InternalTracePlatform hostPlatform = new DBTraceHostPlatform(); public DBTracePlatformManager(DBHandle dbh, OpenMode openMode, ReadWriteLock lock, TaskMonitor monitor, CompilerSpec baseCompilerSpec, DBTrace trace) - throws VersionException, IOException { + throws VersionException, IOException, CancelledException { this.dbh = dbh; this.lock = lock; this.baseLanguage = baseCompilerSpec.getLanguage(); @@ -169,7 +178,7 @@ public class DBTracePlatformManager implements DBTraceManager, TracePlatformMana (s, r) -> new DBTraceGuestPlatformMappedRange(this, s, r), true); loadLanguages(); - loadPlatforms(); + loadPlatforms(openMode, monitor); loadPlatformMappings(); } @@ -179,9 +188,10 @@ public class DBTracePlatformManager implements DBTraceManager, TracePlatformMana } } - protected void loadPlatforms() - throws LanguageNotFoundException, CompilerSpecNotFoundException, VersionException { + protected void loadPlatforms(OpenMode openMode, TaskMonitor monitor) + throws VersionException, CancelledException, IOException { for (DBTraceGuestPlatform platformEntry : platformStore.asMap().values()) { + platformEntry.loadDataTypeManager(openMode, monitor); platformsByCompiler.put(platformEntry.getCompilerSpec(), platformEntry); } } @@ -263,10 +273,11 @@ public class DBTracePlatformManager implements DBTraceManager, TracePlatformMana platformsByCompiler.clear(); try { loadLanguages(); - loadPlatforms(); + // TODO: Or IMMUTABLE, if that was the original, and supported + loadPlatforms(OpenMode.UPDATE, TaskMonitor.DUMMY); loadPlatformMappings(); } - catch (LanguageNotFoundException | CompilerSpecNotFoundException | VersionException e) { + catch (IOException | VersionException | CancelledException e) { throw new AssertionError(e); } } @@ -300,6 +311,12 @@ public class DBTracePlatformManager implements DBTraceManager, TracePlatformMana protected DBTraceGuestPlatform doAddGuestPlatform(CompilerSpec compilerSpec) { DBTraceGuestPlatform platformEntry = platformStore.create(); platformEntry.set(compilerSpec); + try { + platformEntry.loadDataTypeManager(OpenMode.CREATE, TaskMonitor.DUMMY); + } + catch (CancelledException | VersionException | IOException e) { + throw new AssertionError(e); + } platformsByCompiler.put(compilerSpec, platformEntry); return platformEntry; } @@ -330,12 +347,11 @@ public class DBTracePlatformManager implements DBTraceManager, TracePlatformMana @Override public InternalTracePlatform getOrAddPlatform(CompilerSpec compilerSpec) { - if (compilerSpec.getCompilerSpecID() - .equals(trace.getBaseCompilerSpec().getCompilerSpecID())) { - return hostPlatform; - } DBTraceGuestPlatform platform; try (LockHold hold = LockHold.lock(lock.writeLock())) { + if (trace.getBaseCompilerSpec() == compilerSpec) { + return hostPlatform; + } DBTraceGuestPlatform exists = platformsByCompiler.get(compilerSpec); if (exists != null) { return exists; diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/guest/InternalTracePlatform.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/guest/InternalTracePlatform.java index 220acddcac..0004099c6e 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/guest/InternalTracePlatform.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/guest/InternalTracePlatform.java @@ -18,8 +18,7 @@ package ghidra.trace.database.guest; import java.util.*; import ghidra.program.model.address.*; -import ghidra.program.model.lang.Language; -import ghidra.program.model.lang.Register; +import ghidra.program.model.lang.*; import ghidra.program.model.symbol.SourceType; import ghidra.trace.database.guest.DBTraceGuestPlatform.DBTraceGuestLanguage; import ghidra.trace.model.guest.TracePlatform; @@ -34,7 +33,7 @@ import ghidra.util.LockHold; import ghidra.util.exception.DuplicateNameException; import ghidra.util.exception.InvalidInputException; -public interface InternalTracePlatform extends TracePlatform { +public interface InternalTracePlatform extends TracePlatform, ProgramArchitecture { String REG_MAP_BE = "__reg_map_be__"; String REG_MAP_LE = "__reg_map_le__"; @@ -51,6 +50,11 @@ public interface InternalTracePlatform extends TracePlatform { DBTraceGuestLanguage getLanguageEntry(); + @Override + default AddressFactory getAddressFactory() { + return TracePlatform.super.getAddressFactory(); + } + @Override default AddressRange getConventionalRegisterRange(AddressSpace space, Register register) { AddressRange result = mapGuestToHost(TraceRegisterUtils.rangeForRegister(register)); diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceCodeSpace.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceCodeSpace.java index 4055d999fb..f0966db6ff 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceCodeSpace.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceCodeSpace.java @@ -4,9 +4,9 @@ * 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. @@ -28,8 +28,8 @@ import ghidra.program.model.lang.Language; import ghidra.program.model.mem.*; import ghidra.program.model.util.CodeUnitInsertionException; import ghidra.trace.database.DBTrace; -import ghidra.trace.database.data.DBTraceDataTypeManager; import ghidra.trace.database.guest.DBTraceGuestPlatform; +import ghidra.trace.database.guest.DBTracePlatformManager; import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapSpace; import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree.TraceAddressSnapRangeQuery; import ghidra.trace.database.space.AbstractDBTraceSpaceBasedManager.DBTraceSpaceEntry; @@ -63,7 +63,7 @@ public class DBTraceCodeSpace implements TraceCodeSpace, DBTraceSpaceBased { protected final ReadWriteLock lock; protected final Language baseLanguage; protected final DBTrace trace; - protected final DBTraceDataTypeManager dataTypeManager; + protected final DBTracePlatformManager platformManager; protected final DBTraceReferenceManager referenceManager; protected final AddressRange all; @@ -99,7 +99,7 @@ public class DBTraceCodeSpace implements TraceCodeSpace, DBTraceSpaceBased { this.lock = manager.getLock(); this.baseLanguage = manager.getBaseLanguage(); this.trace = manager.getTrace(); - this.dataTypeManager = manager.dataTypeManager; + this.platformManager = manager.platformManager; this.referenceManager = manager.referenceManager; this.all = new AddressRangeImpl(space.getMinAddress(), space.getMaxAddress()); @@ -335,7 +335,7 @@ public class DBTraceCodeSpace implements TraceCodeSpace, DBTraceSpaceBased { if (reApply) { try { definedData.create(Lifespan.span(unitStartSnap, unitEndSnap), - unit.getAddress(), dataType, unit.getLength()); + unit.getAddress(), unit.getPlatform(), dataType, unit.getLength()); } catch (CodeUnitInsertionException e) { throw new AssertionError(e); diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceData.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceData.java index 75a7b5cd67..40bdce0a0e 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceData.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceData.java @@ -4,9 +4,9 @@ * 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. @@ -92,7 +92,7 @@ public class DBTraceData extends AbstractDBTraceCodeUnit if (platform == null) { throw new IOException("Data table is corrupt. Missing platform: " + platformKey); } - dataType = space.dataTypeManager.getDataType(dataTypeID); + dataType = platform.getDataTypeManager().getDataType(dataTypeID); if (dataType == null) { throw new IOException("Data table is corrupt. Missing datatype: " + dataTypeID); } @@ -128,7 +128,7 @@ public class DBTraceData extends AbstractDBTraceCodeUnit this.platform = platform; // Use the stored dataType, not the given one, in case it's different - this.dataType = space.dataTypeManager.getDataType(dataTypeID); + this.dataType = platform.getDataTypeManager().getDataType(dataTypeID); assert this.dataType != null; this.defaultSettings = this.dataType.getDefaultSettings(); this.baseDataType = getBaseDataType(this.dataType); diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceDefinedDataMemoryView.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceDefinedDataMemoryView.java index 270cf8c341..73e7ec5c16 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceDefinedDataMemoryView.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceDefinedDataMemoryView.java @@ -4,9 +4,9 @@ * 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. @@ -20,6 +20,7 @@ import ghidra.program.model.address.AddressRange; import ghidra.program.model.data.DataType; import ghidra.program.model.util.CodeUnitInsertionException; import ghidra.trace.model.Lifespan; +import ghidra.trace.model.guest.TracePlatform; import ghidra.trace.model.listing.TraceCodeManager; import ghidra.util.exception.CancelledException; import ghidra.util.task.TaskMonitor; @@ -52,15 +53,16 @@ public class DBTraceDefinedDataMemoryView } @Override - public DBTraceDataAdapter create(Lifespan lifespan, Address address, DataType dataType, - int length) throws CodeUnitInsertionException { + public DBTraceDataAdapter create(Lifespan lifespan, Address address, TracePlatform platform, + DataType dataType, int length) throws CodeUnitInsertionException { return delegateWrite(address.getAddressSpace(), - m -> m.create(lifespan, address, dataType, length)); + m -> m.create(lifespan, address, platform, dataType, length)); } @Override - public DBTraceDataAdapter create(Lifespan lifespan, Address address, DataType dataType) - throws CodeUnitInsertionException { - return delegateWrite(address.getAddressSpace(), m -> m.create(lifespan, address, dataType)); + public DBTraceDataAdapter create(Lifespan lifespan, Address address, TracePlatform platform, + DataType dataType) throws CodeUnitInsertionException { + return delegateWrite(address.getAddressSpace(), + m -> m.create(lifespan, address, platform, dataType)); } } diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceDefinedDataView.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceDefinedDataView.java index 342216676d..c24a208004 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceDefinedDataView.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceDefinedDataView.java @@ -4,9 +4,9 @@ * 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. @@ -19,8 +19,10 @@ import ghidra.program.model.address.*; import ghidra.program.model.data.*; import ghidra.program.model.mem.MemBuffer; import ghidra.program.model.util.CodeUnitInsertionException; +import ghidra.trace.database.guest.InternalTracePlatform; import ghidra.trace.database.memory.DBTraceMemorySpace; import ghidra.trace.model.*; +import ghidra.trace.model.guest.TracePlatform; import ghidra.trace.model.listing.TraceCodeSpace; import ghidra.trace.util.TraceChangeRecord; import ghidra.trace.util.TraceEvents; @@ -41,9 +43,9 @@ public class DBTraceDefinedDataView extends AbstractBaseDBTraceDefinedUnitsView< } @Override // NOTE: "Adapter" because using DataType.DEFAULT gives UndefinedDBTraceData - public DBTraceDataAdapter create(Lifespan lifespan, Address address, DataType dataType) - throws CodeUnitInsertionException { - return create(lifespan, address, dataType, dataType.getLength()); + public DBTraceDataAdapter create(Lifespan lifespan, Address address, TracePlatform platform, + DataType dataType) throws CodeUnitInsertionException { + return create(lifespan, address, platform, dataType, dataType.getLength()); } /** @@ -67,9 +69,12 @@ public class DBTraceDefinedDataView extends AbstractBaseDBTraceDefinedUnitsView< } @Override - // TODO: Probably add language parameter.... - public DBTraceDataAdapter create(Lifespan lifespan, Address address, DataType origType, - int origLength) throws CodeUnitInsertionException { + public DBTraceDataAdapter create(Lifespan lifespan, Address address, TracePlatform platform, + DataType origType, int origLength) throws CodeUnitInsertionException { + if (platform.getTrace() != getTrace() || + !(platform instanceof InternalTracePlatform iPlatform)) { + throw new IllegalArgumentException("Platform is not part of this trace"); + } try (LockHold hold = LockHold.lock(space.lock.writeLock())) { DBTraceMemorySpace memSpace = space.trace.getMemoryManager().get(space, true); // NOTE: User-given length could be ignored.... @@ -97,12 +102,11 @@ public class DBTraceDefinedDataView extends AbstractBaseDBTraceDefinedUnitsView< if (dataType == null) { throw new CodeUnitInsertionException("Failed to resolve data type"); } - // TODO: This clone may need to be sensitive to the unit's language. - dataType = dataType.clone(space.dataTypeManager); + DataTypeManager dtm = platform.getDataTypeManager(); + dataType = dataType.clone(dtm); if (isFunctionDefinition(dataType)) { - // TODO: This pointer will need to be sensitive to the unit's language. - dataType = new PointerDataType(dataType, dataType.getDataTypeManager()); + dataType = new PointerDataType(dataType, dtm); length = dataType.getLength(); } else if (dataType instanceof Dynamic) { @@ -112,8 +116,6 @@ public class DBTraceDefinedDataView extends AbstractBaseDBTraceDefinedUnitsView< MemBuffer buffer = memSpace.getBufferAt(startSnap, address); length = dyn.getLength(buffer, length); } - // TODO: Do I need to check for Pointer type here? - // Seems purpose is to adjust for language, but I think clone does that already else { length = dataType.getLength(); } @@ -143,10 +145,9 @@ public class DBTraceDefinedDataView extends AbstractBaseDBTraceDefinedUnitsView< return space.undefinedData.getAt(startSnap, address); } - long dataTypeID = space.dataTypeManager.getResolvedID(dataType); + long dataTypeID = dtm.getResolvedID(dataType); DBTraceData created = mapSpace.put(tasr, null); - // TODO: data units with a guest platform - created.set(space.trace.getPlatformManager().getHostPlatform(), dataTypeID); + created.set(iPlatform, dataTypeID); // TODO: Explicitly remove undefined from cache, or let weak refs take care of it? cacheForContaining.notifyNewEntry(tasr.getLifespan(), createdRange, created); diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/InternalTraceDefinedDataView.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/InternalTraceDefinedDataView.java index 08770062f7..9eb99f7361 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/InternalTraceDefinedDataView.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/InternalTraceDefinedDataView.java @@ -4,9 +4,9 @@ * 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. @@ -15,24 +15,60 @@ */ package ghidra.trace.database.listing; +import ghidra.lifecycle.Internal; +import ghidra.program.model.address.Address; import ghidra.program.model.address.AddressRange; import ghidra.program.model.data.DataType; import ghidra.program.model.lang.Register; import ghidra.program.model.util.CodeUnitInsertionException; import ghidra.trace.model.Lifespan; +import ghidra.trace.model.data.TraceBasedDataTypeManager; import ghidra.trace.model.guest.TracePlatform; import ghidra.trace.model.listing.TraceData; import ghidra.trace.model.listing.TraceDefinedDataView; import ghidra.trace.util.TraceRegisterUtils; +@Internal public interface InternalTraceDefinedDataView extends TraceDefinedDataView, InternalTraceBaseDefinedUnitsView { + default TracePlatform getPlatformOf(DataType type) { + if (type.getDataTypeManager() instanceof TraceBasedDataTypeManager dtm && + dtm.getTrace() == getTrace()) { + return dtm.getPlatform(); + } + /** + * TODO: Could we seek a nearest match in terms of data organization? Eh. Maybe not, because + * we'd also have to be concerned with whether there's a mapping at the desired address. + */ + return getTrace().getPlatformManager().getHostPlatform(); + } + @Override - default TraceData create(TracePlatform platform, Lifespan lifespan, Register register, + DBTraceDataAdapter create(Lifespan lifespan, Address address, TracePlatform platform, + DataType dataType) throws CodeUnitInsertionException; + + @Override + DBTraceDataAdapter create(Lifespan lifespan, Address address, TracePlatform platform, + DataType dataType, int length) throws CodeUnitInsertionException; + + @Override + default DBTraceDataAdapter create(Lifespan lifespan, Address address, DataType dataType, + int length) throws CodeUnitInsertionException { + return create(lifespan, address, getPlatformOf(dataType), dataType, length); + } + + @Override + default DBTraceDataAdapter create(Lifespan lifespan, Address address, DataType dataType) + throws CodeUnitInsertionException { + return create(lifespan, address, getPlatformOf(dataType), dataType); + } + + @Override + default DBTraceDataAdapter create(TracePlatform platform, Lifespan lifespan, Register register, DataType dataType) throws CodeUnitInsertionException { TraceRegisterUtils.requireByteBound(register); AddressRange range = platform.getConventionalRegisterRange(getSpace(), register); - return create(lifespan, range.getMinAddress(), dataType, (int) range.getLength()); + return create(lifespan, range.getMinAddress(), platform, dataType, (int) range.getLength()); } } diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/program/AbstractDBTraceProgramViewListing.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/program/AbstractDBTraceProgramViewListing.java index e1dbde8334..a4a765d09a 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/program/AbstractDBTraceProgramViewListing.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/program/AbstractDBTraceProgramViewListing.java @@ -35,7 +35,6 @@ import ghidra.program.model.symbol.SourceType; import ghidra.program.model.util.CodeUnitInsertionException; import ghidra.program.model.util.PropertyMap; import ghidra.trace.database.DBTrace; -import ghidra.trace.database.guest.InternalTracePlatform; import ghidra.trace.database.listing.UndefinedDBTraceData; import ghidra.trace.database.memory.DBTraceMemorySpace; import ghidra.trace.database.program.DBTraceProgramViewMemory.RegionEntry; @@ -77,7 +76,6 @@ public abstract class AbstractDBTraceProgramViewListing implements TraceProgramV protected final DBTraceProgramView program; protected final TraceCodeOperations codeOperations; - protected final InternalTracePlatform platform; protected final DBTraceProgramViewRootModule rootModule; protected final Map fragmentsByRegion = @@ -90,8 +88,7 @@ public abstract class AbstractDBTraceProgramViewListing implements TraceProgramV TraceCodeOperations codeOperations) { this.program = program; this.codeOperations = codeOperations; - // TODO: Guest platform views? - this.platform = program.trace.getPlatformManager().getHostPlatform(); + // TODO: Map addresses when platform is guest? this.rootModule = new DBTraceProgramViewRootModule(this); } @@ -751,7 +748,7 @@ public abstract class AbstractDBTraceProgramViewListing implements TraceProgramV range, s -> s == TraceMemoryState.KNOWN); long snap = mostRecent == null ? program.snap : mostRecent.getKey().getY2(); return codeOperations.instructions() - .create(Lifespan.nowOn(snap), addr, platform, prototype, context, + .create(Lifespan.nowOn(snap), addr, program.platform, prototype, context, forcedLengthOverride); } @@ -759,7 +756,7 @@ public abstract class AbstractDBTraceProgramViewListing implements TraceProgramV public AddressSetView addInstructions(InstructionSet instructionSet, boolean overwrite) throws CodeUnitInsertionException { return codeOperations.instructions() - .addInstructionSet(Lifespan.nowOn(program.snap), platform, instructionSet, + .addInstructionSet(Lifespan.nowOn(program.snap), program.platform, instructionSet, overwrite); } @@ -767,12 +764,13 @@ public abstract class AbstractDBTraceProgramViewListing implements TraceProgramV public Data createData(Address addr, DataType dataType, int length) throws CodeUnitInsertionException { return codeOperations.definedData() - .create(Lifespan.nowOn(program.snap), addr, dataType, length); + .create(Lifespan.nowOn(program.snap), addr, program.platform, dataType, length); } @Override public Data createData(Address addr, DataType dataType) throws CodeUnitInsertionException { - return codeOperations.definedData().create(Lifespan.nowOn(program.snap), addr, dataType); + return codeOperations.definedData() + .create(Lifespan.nowOn(program.snap), addr, program.platform, dataType); } @Override diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/program/DBTraceProgramView.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/program/DBTraceProgramView.java index 466e4fd00e..eab47e723c 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/program/DBTraceProgramView.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/program/DBTraceProgramView.java @@ -45,6 +45,7 @@ import ghidra.program.model.util.AddressSetPropertyMap; import ghidra.program.model.util.PropertyMapManager; import ghidra.program.util.*; import ghidra.trace.database.*; +import ghidra.trace.database.guest.InternalTracePlatform; import ghidra.trace.database.listing.DBTraceCodeSpace; import ghidra.trace.database.listing.DBTraceDefinedUnitsView; import ghidra.trace.database.memory.DBTraceMemorySpace; @@ -333,40 +334,40 @@ public class DBTraceProgramView implements TraceProgramView { range.getX1(), range.getX1(), null, null, null)); } - private void commentChanged(int commentType, TraceAddressSpace space, + private void commentChanged(CommentType commentType, TraceAddressSpace space, TraceAddressSnapRange range, String oldValue, String newValue) { DomainObjectEventQueues queues = isVisible(space, range); if (queues == null) { return; } queues.fireEvent( - new CommentChangeRecord(commentType, range.getX1(), oldValue, newValue)); + new CommentChangeRecord(commentType.ordinal(), range.getX1(), oldValue, newValue)); } private void commentEolChanged(TraceAddressSpace space, TraceAddressSnapRange range, String oldValue, String newValue) { - commentChanged(CodeUnit.EOL_COMMENT, space, range, oldValue, newValue); + commentChanged(CommentType.EOL, space, range, oldValue, newValue); } private void commentPlateChanged(TraceAddressSpace space, TraceAddressSnapRange range, String oldValue, String newValue) { - commentChanged(CodeUnit.PLATE_COMMENT, space, range, oldValue, newValue); + commentChanged(CommentType.PLATE, space, range, oldValue, newValue); } private void commentPostChanged(TraceAddressSpace space, TraceAddressSnapRange range, String oldValue, String newValue) { - commentChanged(CodeUnit.POST_COMMENT, space, range, oldValue, newValue); + commentChanged(CommentType.POST, space, range, oldValue, newValue); } private void commentPreChanged(TraceAddressSpace space, TraceAddressSnapRange range, String oldValue, String newValue) { - commentChanged(CodeUnit.PRE_COMMENT, space, range, oldValue, newValue); + commentChanged(CommentType.PRE, space, range, oldValue, newValue); } private void commentRepeatableChanged(TraceAddressSpace space, TraceAddressSnapRange range, String oldValue, String newValue) { // TODO: The "repeatable" semantics are not implemented, yet. - commentChanged(CodeUnit.REPEATABLE_COMMENT, space, range, oldValue, newValue); + commentChanged(CommentType.REPEATABLE, space, range, oldValue, newValue); } private void compositeDataAdded(TraceAddressSpace space, TraceAddressSnapRange range, @@ -697,6 +698,7 @@ public class DBTraceProgramView implements TraceProgramView { protected final Map regViewsByThread; protected long snap; + protected InternalTracePlatform platform; protected final DBTraceTimeViewport viewport; protected final Runnable viewportChangeListener = this::viewportChanged; @@ -715,6 +717,9 @@ public class DBTraceProgramView implements TraceProgramView { this.viewport = trace.createTimeViewport(); this.viewport.setSnap(snap); + // TODO: Initialize guest platform for fixed views? + this.platform = trace.getPlatformManager().getHostPlatform(); + this.eventQueues = new DomainObjectEventQueues(this, TIME_INTERVAL, trace.getLock()); this.regViewsByThread = new WeakValueHashMap<>(); @@ -833,7 +838,7 @@ public class DBTraceProgramView implements TraceProgramView { @Override public TraceBasedDataTypeManager getDataTypeManager() { - return trace.getDataTypeManager(); + return platform.getDataTypeManager(); } @Override diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/program/DBTraceVariableSnapProgramView.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/program/DBTraceVariableSnapProgramView.java index 9b42c75261..9cf17b3f31 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/program/DBTraceVariableSnapProgramView.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/program/DBTraceVariableSnapProgramView.java @@ -4,9 +4,9 @@ * 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. @@ -18,6 +18,8 @@ package ghidra.trace.database.program; import ghidra.program.model.lang.CompilerSpec; import ghidra.program.model.listing.CodeUnit; import ghidra.trace.database.DBTrace; +import ghidra.trace.database.guest.InternalTracePlatform; +import ghidra.trace.model.guest.TracePlatform; import ghidra.trace.model.program.TraceVariableSnapProgramView; /** @@ -48,4 +50,13 @@ public class DBTraceVariableSnapProgramView extends DBTraceProgramView // TODO: I could be more particular, but this seems to work fast enough, now. fireObjectRestored(); } + + @Override + public void setPlatform(TracePlatform platform) { + if (!(platform instanceof InternalTracePlatform iPlatform) || + platform.getTrace() != trace) { + throw new IllegalArgumentException("Platform is not in this trace"); + } + this.platform = iPlatform; + } } diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/Trace.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/Trace.java index 03408af765..9e7fa16137 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/Trace.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/Trace.java @@ -4,9 +4,9 @@ * 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. @@ -22,6 +22,7 @@ import javax.swing.Icon; import generic.theme.GIcon; import ghidra.lifecycle.Transitional; import ghidra.program.model.address.AddressFactory; +import ghidra.program.model.data.DataTypeManager; import ghidra.program.model.data.DataTypeManagerDomainObject; import ghidra.program.model.lang.CompilerSpec; import ghidra.program.model.lang.Language; @@ -31,6 +32,7 @@ import ghidra.trace.model.breakpoint.TraceBreakpoint; import ghidra.trace.model.breakpoint.TraceBreakpointManager; import ghidra.trace.model.context.TraceRegisterContextManager; import ghidra.trace.model.data.TraceBasedDataTypeManager; +import ghidra.trace.model.guest.TracePlatform; import ghidra.trace.model.guest.TracePlatformManager; import ghidra.trace.model.listing.TraceCodeManager; import ghidra.trace.model.listing.TraceCodeUnit; @@ -95,8 +97,19 @@ public interface Trace extends DataTypeManagerDomainObject { TraceCodeManager getCodeManager(); + /** + * {@inheritDoc} + * + *

+ * For traces, this gets the "base" or "host" {@link DataTypeManager}. For platform-specific + * managers, see {@link TracePlatform#getDataTypeManager()}. + */ @Override - TraceBasedDataTypeManager getDataTypeManager(); + default TraceBasedDataTypeManager getDataTypeManager() { + return getBaseDataTypeManager(); + } + + TraceBasedDataTypeManager getBaseDataTypeManager(); TraceEquateManager getEquateManager(); diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/data/TraceBasedDataTypeManager.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/data/TraceBasedDataTypeManager.java index f3531d6535..855511f559 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/data/TraceBasedDataTypeManager.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/data/TraceBasedDataTypeManager.java @@ -17,6 +17,7 @@ package ghidra.trace.model.data; import ghidra.program.model.data.*; import ghidra.trace.model.Trace; +import ghidra.trace.model.guest.TracePlatform; import ghidra.trace.model.program.TraceProgramView; /** @@ -36,6 +37,13 @@ public interface TraceBasedDataTypeManager extends ProgramBasedDataTypeManager { */ Trace getTrace(); + /** + * Get the platform for which this data type manager is provided + * + * @return the platform + */ + TracePlatform getPlatform(); + /** * TODO: Petition to have this replace * {@link TraceBasedDataTypeManager#resolve(DataType, DataTypeConflictHandler)} diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/guest/TracePlatform.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/guest/TracePlatform.java index 006180f0b5..c0c44f6983 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/guest/TracePlatform.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/guest/TracePlatform.java @@ -21,6 +21,7 @@ import ghidra.program.model.address.*; import ghidra.program.model.lang.*; import ghidra.program.model.mem.MemBuffer; import ghidra.trace.model.Trace; +import ghidra.trace.model.data.TraceBasedDataTypeManager; import ghidra.trace.model.memory.TraceObjectRegister; import ghidra.trace.model.symbol.TraceLabelSymbol; import ghidra.trace.model.target.TraceObject; @@ -83,10 +84,17 @@ public interface TracePlatform { /** * Get the compiler of the guest platform * - * @return the compiler spec + * @return the compiler specification */ CompilerSpec getCompilerSpec(); + /** + * Get the data type manager for this platform. + * + * @return the data type manager + */ + TraceBasedDataTypeManager getDataTypeManager(); + /** * Get the addresses in the host which are mapped to somewhere in the guest * diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/listing/TraceDefinedDataView.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/listing/TraceDefinedDataView.java index c10a315987..eccc02cdad 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/listing/TraceDefinedDataView.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/listing/TraceDefinedDataView.java @@ -4,9 +4,9 @@ * 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. @@ -16,6 +16,7 @@ package ghidra.trace.model.listing; import ghidra.program.model.address.Address; +import ghidra.program.model.data.DataOrganization; import ghidra.program.model.data.DataType; import ghidra.program.model.lang.Register; import ghidra.program.model.util.CodeUnitInsertionException; @@ -29,9 +30,14 @@ import ghidra.trace.model.guest.TracePlatform; * This view excludes instructions and default / undefined data units. */ public interface TraceDefinedDataView extends TraceBaseDefinedUnitsView { + /** * Create a data unit starting at the given address * + *

+ * If the given type is already part of this trace, its platform is used as is. If not, then it + * is resolved to the host platform. + * * @param lifespan the span for which the unit is effective * @param address the starting address * @param dataType the data type for the unit @@ -42,12 +48,31 @@ public interface TraceDefinedDataView extends TraceBaseDefinedUnitsView + * The given type is resolved to the given platform, even if the type already exists in the + * trace by another platform. + * + * @param lifespan the span for which the unit is effective + * @param address the starting address + * @param platform the platform for the type's {@link DataOrganization} + * @param dataType the data type for the unit + * @param length the length of the unit, -1 for unspecified + * @return the new data unit + * @throws CodeUnitInsertionException if there's a conflict + */ + TraceData create(Lifespan lifespan, Address address, TracePlatform platform, DataType dataType, + int length) throws CodeUnitInsertionException; + /** * Create a data unit of unspecified length starting at the given address * *

* The length will be determined by the data type, possibly by examining the bytes, e.g., a - * null-terminated UTF-8 string. + * null-terminated UTF-8 string. If the given type is already part of this trace, its platform + * is used as is. If not, then it is resolved to the host platform. * * @param lifespan the span for which the unit is effective * @param address the starting address @@ -58,12 +83,31 @@ public interface TraceDefinedDataView extends TraceBaseDefinedUnitsView + * The length will be determined by the data type, possibly by examining the bytes, e.g., a + * null-terminated UTF-8 string. The given type is resolved to the given platform, even if the + * type already exists in the trace by another platform. + * + * @param lifespan the span for which the unit is effective + * @param address the starting address + * @param platform the platform for the type's {@link DataOrganization} + * @param dataType the data type for the unit + * @return the new data unit + * @throws CodeUnitInsertionException if there's a conflict + */ + TraceData create(Lifespan lifespan, Address address, TracePlatform platform, DataType dataType) + throws CodeUnitInsertionException; + /** * Create a data unit on the given register * *

* If the register is memory mapped, this will delegate to the appropriate space. In those - * cases, the assignment affects all threads. + * cases, the assignment affects all threads. The type is resolved to the host platform, even if + * it already exists in the trace by another platform. * * @param lifespan the span for which the unit is effective * @param register the register to assign a data type @@ -82,7 +126,8 @@ public interface TraceDefinedDataView extends TraceBaseDefinedUnitsView * If the register is memory mapped, this will delegate to the appropriate space. In those - * cases, the assignment affects all threads. + * cases, the assignment affects all threads. The type is resolved to the given platform, even + * if it already exists in the trace by another platform. * * @param platform the platform whose language defines the register * @param lifespan the span for which the unit is effective diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/program/TraceVariableSnapProgramView.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/program/TraceVariableSnapProgramView.java index 67d2d852c0..f845c9b8c4 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/program/TraceVariableSnapProgramView.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/program/TraceVariableSnapProgramView.java @@ -4,9 +4,9 @@ * 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. @@ -15,11 +15,13 @@ */ package ghidra.trace.model.program; +import ghidra.trace.model.guest.TracePlatform; + public interface TraceVariableSnapProgramView extends TraceProgramView { /** * Seek to a particular snap * - * @param snap + * @param snap the snap */ void setSnap(long snap); @@ -29,4 +31,11 @@ public interface TraceVariableSnapProgramView extends TraceProgramView { default void seekLatest() { setSnap(getMaxSnap()); } + + /** + * Set the current platform, so that actions have context + * + * @param platform the platform + */ + void setPlatform(TracePlatform platform); } diff --git a/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/database/ToyDBTraceBuilder.java b/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/database/ToyDBTraceBuilder.java index 94d43a0e9e..7c38a1f340 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/database/ToyDBTraceBuilder.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/database/ToyDBTraceBuilder.java @@ -549,6 +549,23 @@ public class ToyDBTraceBuilder implements AutoCloseable { return code.definedData().create(Lifespan.nowOn(snap), start, type, length); } + /** + * Create a data unit + * + * @param snap the starting snap + * @param start the min address + * @param platform the platform for data organization + * @param type the data type of the unit + * @param length the length, or -1 for the type's default + * @return the new data unit + * @throws CodeUnitInsertionException if the unit cannot be created + */ + public DBTraceDataAdapter addData(long snap, Address start, TracePlatform platform, + DataType type, int length) throws CodeUnitInsertionException { + DBTraceCodeManager code = trace.getCodeManager(); + return code.definedData().create(Lifespan.nowOn(snap), start, platform, type, length); + } + /** * Create a data unit, first placing the given bytes * @@ -569,6 +586,27 @@ public class ToyDBTraceBuilder implements AutoCloseable { return data; } + /** + * Create a data unit, first placing the given bytes + * + * @param snap the starting snap + * @param start the min address + * @param platform the platform for data organization + * @param type the data type of the unit + * @param buf the bytes to place, which will become the unit's bytes + * @return the new data unit + * @throws CodeUnitInsertionException if the unit cannot be created + */ + public DBTraceDataAdapter addData(long snap, Address start, TracePlatform platform, + DataType type, ByteBuffer buf) throws CodeUnitInsertionException { + int length = buf.remaining(); + DBTraceMemoryManager memory = trace.getMemoryManager(); + memory.putBytes(snap, start, buf); + DBTraceDataAdapter data = addData(snap, start, platform, type, length); + assertEquals(length, data.getLength()); + return data; + } + /** * Create an instruction unit by disassembling existing bytes * @@ -838,6 +876,7 @@ public class ToyDBTraceBuilder implements AutoCloseable { * Get an object by its path pattern intersecting the given lifespan * * @param path the path pattern + * @param span the lifespan to search * @return the object or null */ public TraceObject objAny(String path, Lifespan span) { diff --git a/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/database/data/DBTraceDataTypeManagerTest.java b/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/database/data/DBTraceDataTypeManagerTest.java index 349a0f67b5..9a4ad26aa6 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/database/data/DBTraceDataTypeManagerTest.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/database/data/DBTraceDataTypeManagerTest.java @@ -4,9 +4,9 @@ * 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. @@ -46,7 +46,7 @@ public class DBTraceDataTypeManagerTest extends AbstractGhidraHeadlessIntegratio .getLanguage( new LanguageID("Toy:BE:64:default")); trace = new DBTrace("Testing", toyLanguage.getDefaultCompilerSpec(), this); - dtm = trace.getDataTypeManager(); + dtm = trace.getBaseDataTypeManager(); } @After diff --git a/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/database/listing/DBTraceCodeManagerTest.java b/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/database/listing/DBTraceCodeManagerTest.java index 676127b4c6..50ed138f2d 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/database/listing/DBTraceCodeManagerTest.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/database/listing/DBTraceCodeManagerTest.java @@ -1830,7 +1830,7 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes @Test public void testAddGuestInstructionThenRemoveAndDelete() throws AddressOverflowException, CodeUnitInsertionException, IOException, CancelledException { - DBTracePlatformManager langMan = b.trace.getPlatformManager(); + DBTracePlatformManager platMan = b.trace.getPlatformManager(); Language x86 = getSLEIGH_X86_LANGUAGE(); DBTraceGuestPlatform guest; DBTraceGuestPlatformMappedRange mappedRange; @@ -1839,7 +1839,7 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes TraceInstruction i4001; TraceData d4003; try (Transaction tx = b.startTransaction()) { - guest = langMan.addGuestPlatform(x86.getDefaultCompilerSpec()); + guest = platMan.addGuestPlatform(x86.getDefaultCompilerSpec()); mappedRange = guest.addMappedRange(b.addr(0x0000), b.addr(guest, 0x0000), 1L << 32); g4000 = b.addInstruction(0, b.addr(0x4000), guest, b.buf(0x90)); i4001 = b.addInstruction(0, b.addr(0x4001), b.host, b.buf(0xf4, 0)); @@ -1857,7 +1857,6 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes b.trace.undo(); // NB. The range deletion also deletes the guest unit, so it'll have a new identity - // TODO: Related to GP-479? g4000 = manager.instructions().getAt(0, b.addr(0x4000)); assertNotNull(g4000); assertEquals(guest, g4000.getPlatform()); @@ -1865,13 +1864,66 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes guest.delete(new ConsoleTaskMonitor()); } assertUndefinedWithAddr(b.addr(0x4000), manager.codeUnits().getAt(0, b.addr(0x4000))); - // TODO: Definitely part of GP-479. These should be able to keep their identities. - //assertEquals(i4001, manager.codeUnits().getAt(0, b.addr(0x4001))); - //assertEquals(d4003, manager.codeUnits().getAt(0, b.addr(0x4003))); + assertEquals(i4001, manager.codeUnits().getAt(0, b.addr(0x4001))); + assertEquals(d4003, manager.codeUnits().getAt(0, b.addr(0x4003))); assertNotNull(manager.instructions().getAt(0, b.addr(0x4001))); assertNotNull(manager.definedData().getAt(0, b.addr(0x4003))); } + @Test + public void testAddGuestDataThenRemoveAndDelete() throws Exception { + DBTracePlatformManager platMan = b.trace.getPlatformManager(); + Language x86 = getSLEIGH_X86_LANGUAGE(); + DBTraceGuestPlatform guest; + DBTraceGuestPlatformMappedRange mappedRange; + + TraceData hd4000; + TraceData gd4008; + TraceData gd400c; + try (Transaction tx = b.startTransaction()) { + guest = platMan.addGuestPlatform(x86.getDefaultCompilerSpec()); + mappedRange = guest.addMappedRange(b.addr(0x0000), b.addr(guest, 0x0000), 1L << 32); + hd4000 = b.addData(0, b.addr(0x4000), PointerDataType.dataType, + b.buf(0, 0, 0, 0, 0, 0, 0x40, 0x80)); + gd4008 = b.addData(0, b.addr(0x4008), guest, PointerDataType.dataType, + b.buf(0x81, 0x40, 0, 0)); + gd400c = b.addData(0, b.addr(0x400c), gd4008.getDataType(), b.buf(0x82, 0x40, 0, 0)); + } + assertEquals(b.host, hd4000.getPlatform()); + assertEquals(8, hd4000.getLength()); + assertEquals(guest, gd4008.getPlatform()); + assertEquals(4, gd4008.getLength()); + assertEquals(guest, gd400c.getPlatform()); + assertEquals(4, gd400c.getLength()); + + assertEquals(gd4008, manager.codeUnits().getAt(0, b.addr(0x4008))); + assertEquals(gd400c, manager.codeUnits().getAt(0, b.addr(0x400c))); + + try (Transaction tx = b.startTransaction()) { + mappedRange.delete(new ConsoleTaskMonitor()); + } + assertEquals(hd4000, manager.codeUnits().getAt(0, b.addr(0x4000))); + assertUndefinedWithAddr(b.addr(0x4008), manager.codeUnits().getAt(0, b.addr(0x4008))); + assertUndefinedWithAddr(b.addr(0x400c), manager.codeUnits().getAt(0, b.addr(0x400c))); + + b.trace.undo(); + + // NB. The range deletion also deletes the guest units, so they'll have new identities + gd4008 = manager.definedData().getAt(0, b.addr(0x4008)); + gd400c = manager.definedData().getAt(0, b.addr(0x400c)); + assertNotNull(gd4008); + assertNotNull(gd400c); + assertEquals(guest, gd4008.getPlatform()); + assertEquals(guest, gd400c.getPlatform()); + try (Transaction tx = b.startTransaction()) { + guest.delete(new ConsoleTaskMonitor()); + } + assertEquals(hd4000, manager.codeUnits().getAt(0, b.addr(0x4000))); + assertUndefinedWithAddr(b.addr(0x4008), manager.codeUnits().getAt(0, b.addr(0x4008))); + assertUndefinedWithAddr(b.addr(0x400c), manager.codeUnits().getAt(0, b.addr(0x400c))); + assertNotNull(manager.definedData().getAt(0, b.addr(0x4000))); + } + @Test public void testSaveAndLoad() throws Exception { try (Transaction tx = b.startTransaction()) { diff --git a/Ghidra/Debug/ProposedUtils/src/main/java/generic/depends/DependentServiceResolver.java b/Ghidra/Debug/ProposedUtils/src/main/java/generic/depends/DependentServiceResolver.java index a959410ecd..87b71b2366 100644 --- a/Ghidra/Debug/ProposedUtils/src/main/java/generic/depends/DependentServiceResolver.java +++ b/Ghidra/Debug/ProposedUtils/src/main/java/generic/depends/DependentServiceResolver.java @@ -4,9 +4,9 @@ * 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. @@ -17,7 +17,6 @@ package generic.depends; import java.lang.reflect.*; import java.util.*; -import java.util.Map.Entry; import generic.depends.err.*; @@ -137,6 +136,7 @@ public class DependentServiceResolver { public void injectServices(T obj) throws ServiceConstructionException { Map, Object> instancesByClass = new HashMap<>(); Map constructed = new HashMap<>(); + Map, Set> fieldsByClass = new HashMap<>(this.fieldsByClass); for (DependentServiceConstructor cons : ordered) { Object service = constructed.get(cons.method); if (service == null) { @@ -144,16 +144,18 @@ public class DependentServiceResolver { constructed.put(cons.method, service); } instancesByClass.put(cons.cls, service); - } - for (Entry, Set> entry : fieldsByClass.entrySet()) { - for (Field f : entry.getValue()) { + for (Field f : fieldsByClass.remove(cons.cls)) { try { - f.set(obj, instancesByClass.get(entry.getKey())); + f.set(obj, service); } catch (IllegalArgumentException | IllegalAccessException e) { throw new AssertionError(e); } } } + if (!fieldsByClass.isEmpty()) { + throw new ServiceConstructionException( + "No service constructor for " + fieldsByClass.keySet(), null); + } } }