From 67d52f4fccbcb871d4f9bb4654dacb46c6c47a2d Mon Sep 17 00:00:00 2001 From: Dan <46821332+nsadeveloper789@users.noreply.github.com> Date: Fri, 17 Jun 2022 11:41:33 -0400 Subject: [PATCH] GP-1967: Fix auto-disassembly for object-based traces, incl. framework support. --- .../dbgmodel/model/impl/dbgmodel_schema.xml | 2 +- .../Debugger/data/ExtensionPoint.manifest | 1 + .../RefreshRegistersScript.java | 78 +++++ .../core/debug/gui/DebuggerResources.java | 19 ++ .../core/debug/gui/register/RegisterRow.java | 3 +- .../AbstractDebuggerPlatformMapper.java | 95 ++++++ .../AbstractDebuggerPlatformOffer.java} | 27 +- .../AbstractDebuggerPlatformOpinion.java | 42 +++ .../debug/mapping/DebuggerMappingOpinion.java | 2 +- .../debug/mapping/DebuggerPlatformMapper.java | 58 ++++ .../debug/mapping/DebuggerPlatformOffer.java | 99 ++++++ .../mapping/DebuggerPlatformOpinion.java | 158 ++++++++++ .../DefaultDebuggerPlatformMapper.java | 90 ++++++ .../core/debug/mapping/DisassemblyResult.java | 49 +++ .../legacy/LegacyDebuggerPlatformOpinion.java | 100 ++++++ .../platform/arm/ArmDisassemblyInject.java | 28 +- .../dbgeng/DbgengDebuggerPlatformOpinion.java | 83 +++++ .../dbgeng/DbgengX64DisassemblyInject.java | 37 ++- .../FridaArmDebuggerMappingOpinion.java | 2 +- .../frida/FridaDebuggerPlatformOpinion.java | 134 ++++++++ .../GdbArmDebuggerMappingOpinion.java | 3 +- .../gdb/GdbDebuggerPlatformOpinion.java | 99 ++++++ .../JdiDalvikDebuggerMappingOpinion.java | 2 +- .../jdi/JdiDebuggerPlatformOpinion.java | 90 ++++++ .../JdiJvmDebuggerMappingOpinion.java} | 4 +- .../LldbArmDebuggerMappingOpinion.java | 2 +- .../lldb/LldbDebuggerPlatformOpinion.java | 134 ++++++++ .../AbstractDebuggerProgramLaunchOffer.java | 3 + .../record/EmptyDebuggerRegisterMapper.java | 2 +- .../DebuggerPlatformServicePlugin.java | 139 ++++++++ .../workflow/DisassembleAtPcDebuggerBot.java | 130 +++++--- .../DisassembleGuestTraceCommand.java | 59 ++++ .../workflow/DisassembleTraceCommand.java | 94 ++++++ .../debug/workflow/DisassemblyInject.java | 17 +- .../app/services/DebuggerPlatformService.java | 74 +++++ .../core/debug/gui/DebuggerManualTest.java | 9 +- .../mapping/TestDebuggerPlatformOpinion.java | 62 ++++ .../GdbArmDebuggerMappingOpinionTest.java | 6 +- .../DisassembleAtPcDebuggerBotTest.java | 168 ++++++++++ .../java/ghidra/trace/database/DBTrace.java | 22 +- .../ghidra/trace/database/DBTraceUtils.java | 38 +++ .../DBTraceRegisterContextManager.java | 6 +- .../context/DBTraceRegisterContextSpace.java | 12 +- .../DBTraceGuestPlatform.java} | 183 +++++++---- .../DBTraceGuestPlatformMappedMemory.java} | 18 +- .../DBTraceGuestPlatformMappedRange.java} | 46 +-- .../guest/DBTracePlatformManager.java | 298 ++++++++++++++++++ .../language/DBTraceLanguageManager.java | 204 ------------ .../listing/AbstractDBTraceDataComponent.java | 6 + .../database/listing/DBTraceCodeManager.java | 98 +++--- .../database/listing/DBTraceCodeSpace.java | 10 +- .../trace/database/listing/DBTraceData.java | 44 +-- .../listing/DBTraceDefinedDataView.java | 3 +- .../database/listing/DBTraceInstruction.java | 57 ++-- .../DBTraceInstructionsMemoryView.java | 20 +- .../listing/DBTraceInstructionsView.java | 72 +++-- .../listing/UndefinedDBTraceData.java | 6 + .../AbstractDBTraceProgramViewListing.java | 16 +- .../database/stack/DBTraceObjectStack.java | 10 +- .../stack/DBTraceObjectStackFrame.java | 2 +- .../trace/database/target/DBTraceObject.java | 113 +++++-- .../InternalOrderedSuccessorsVisitor.java | 2 +- .../InternalSuccessorsRelativeVisitor.java | 4 +- .../main/java/ghidra/trace/model/Trace.java | 4 +- .../trace/model/guest/TraceGuestPlatform.java | 132 ++++++++ .../TraceGuestPlatformMappedRange.java} | 9 +- .../model/guest/TracePlatformManager.java | 74 +++++ .../model/language/TraceGuestLanguage.java | 71 ----- .../trace/model/listing/TraceCodeUnit.java | 8 + .../model/listing/TraceInstructionsView.java | 34 +- .../trace/util/DefaultTraceTimeViewport.java | 6 +- .../trace/database/ToyDBTraceBuilder.java | 32 +- .../DBTracePlatformManagerTest.java} | 213 +++++++------ .../listing/DBTraceCodeManagerTest.java | 90 +++--- .../database/listing/DBTraceCodeUnitTest.java | 76 ++--- .../DBTraceDisassemblerIntegrationTest.java | 16 +- .../DBTraceProgramViewListingTest.java | 52 +-- .../framework/cmd/TypedBackgroundCommand.java | 42 +++ .../java/docking/DockingWindowManager.java | 3 +- Ghidra/Processors/Toy/certification.manifest | 1 + .../Processors/Toy/data/languages/toy.ldefs | 1 + .../Toy/data/languages/toy64-long8.cspec | 90 ++++++ .../Processors/x86/data/languages/x86.ldefs | 1 + 83 files changed, 3561 insertions(+), 888 deletions(-) create mode 100644 Ghidra/Debug/Debugger/ghidra_scripts/RefreshRegistersScript.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/AbstractDebuggerPlatformMapper.java rename Ghidra/Debug/{Framework-TraceModeling/src/main/java/ghidra/trace/model/language/TraceLanguageManager.java => Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/AbstractDebuggerPlatformOffer.java} (52%) create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/AbstractDebuggerPlatformOpinion.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/DebuggerPlatformMapper.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/DebuggerPlatformOffer.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/DebuggerPlatformOpinion.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/DefaultDebuggerPlatformMapper.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/DisassemblyResult.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/legacy/LegacyDebuggerPlatformOpinion.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/dbgeng/DbgengDebuggerPlatformOpinion.java rename Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/{arm => frida}/FridaArmDebuggerMappingOpinion.java (97%) create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/frida/FridaDebuggerPlatformOpinion.java rename Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/{arm => gdb}/GdbArmDebuggerMappingOpinion.java (95%) create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/gdb/GdbDebuggerPlatformOpinion.java rename Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/{jvm => jdi}/JdiDalvikDebuggerMappingOpinion.java (98%) create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/jdi/JdiDebuggerPlatformOpinion.java rename Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/{jvm/JdiJavaDebuggerMappingOpinion.java => jdi/JdiJvmDebuggerMappingOpinion.java} (95%) rename Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/{arm => lldb}/LldbArmDebuggerMappingOpinion.java (98%) create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/lldb/LldbDebuggerPlatformOpinion.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/platform/DebuggerPlatformServicePlugin.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/workflow/DisassembleGuestTraceCommand.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/workflow/DisassembleTraceCommand.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/services/DebuggerPlatformService.java create mode 100644 Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/mapping/TestDebuggerPlatformOpinion.java rename Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/platform/{arm => gdb}/GdbArmDebuggerMappingOpinionTest.java (95%) create mode 100644 Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/workflow/DisassembleAtPcDebuggerBotTest.java rename Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/{language/DBTraceGuestLanguage.java => guest/DBTraceGuestPlatform.java} (54%) rename Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/{language/DBTraceGuestLanguageMappedMemory.java => guest/DBTraceGuestPlatformMappedMemory.java} (96%) rename Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/{language/DBTraceGuestLanguageMappedRange.java => guest/DBTraceGuestPlatformMappedRange.java} (80%) create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/guest/DBTracePlatformManager.java delete mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/language/DBTraceLanguageManager.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/guest/TraceGuestPlatform.java rename Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/{language/TraceGuestLanguageMappedRange.java => guest/TraceGuestPlatformMappedRange.java} (83%) create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/guest/TracePlatformManager.java delete mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/language/TraceGuestLanguage.java rename Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/database/{language/DBTraceLanguageManagerTest.java => guest/DBTracePlatformManagerTest.java} (58%) create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/framework/cmd/TypedBackgroundCommand.java create mode 100644 Ghidra/Processors/Toy/data/languages/toy64-long8.cspec diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/resources/agent/dbgmodel/model/impl/dbgmodel_schema.xml b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/resources/agent/dbgmodel/model/impl/dbgmodel_schema.xml index 8ca99de662..bbc3117a29 100644 --- a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/resources/agent/dbgmodel/model/impl/dbgmodel_schema.xml +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/resources/agent/dbgmodel/model/impl/dbgmodel_schema.xml @@ -222,7 +222,7 @@ - + diff --git a/Ghidra/Debug/Debugger/data/ExtensionPoint.manifest b/Ghidra/Debug/Debugger/data/ExtensionPoint.manifest index bcbfbe4845..8a0ed8a9a0 100644 --- a/Ghidra/Debug/Debugger/data/ExtensionPoint.manifest +++ b/Ghidra/Debug/Debugger/data/ExtensionPoint.manifest @@ -2,6 +2,7 @@ AutoReadMemorySpec DebuggerBot DebuggerMappingOpinion DebuggerModelFactory +DebuggerPlatformOpinion DebuggerProgramLaunchOpinion DisassemblyInject LocationTrackingSpec diff --git a/Ghidra/Debug/Debugger/ghidra_scripts/RefreshRegistersScript.java b/Ghidra/Debug/Debugger/ghidra_scripts/RefreshRegistersScript.java new file mode 100644 index 0000000000..18234e2400 --- /dev/null +++ b/Ghidra/Debug/Debugger/ghidra_scripts/RefreshRegistersScript.java @@ -0,0 +1,78 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import ghidra.app.plugin.core.debug.DebuggerCoordinates; +import ghidra.app.script.GhidraScript; +import ghidra.app.services.*; +import ghidra.dbg.DebuggerObjectModel; +import ghidra.dbg.target.TargetObject; +import ghidra.dbg.target.TargetRegisterBank; +import ghidra.dbg.util.PathMatcher; + +public class RefreshRegistersScript extends GhidraScript { + @Override + protected void run() throws Exception { + // There is no need to fish this from the ObjectUpdateService, you can get it directly + DebuggerModelService modelService = state.getTool().getService(DebuggerModelService.class); + // The current model is retrieved with one method, no need to stream or filter + DebuggerObjectModel model = modelService.getCurrentModel(); + + /** + * Navigating a model generically requires some introspection. Use the schema. We used to + * decouple the descriptions (RegisterContainer) from the values (RegisterBank), but we + * always realize the two interfaces on the same no it seems. Still, we need to work with + * values, so we search for all the Banks. + */ + PathMatcher allBanksMatcher = + model.getRootSchema().searchFor(TargetRegisterBank.class, true); + for (TargetObject objBank : allBanksMatcher.fetchSuccessors(model.fetchModelRoot().get()) + .get() + .values()) { + // Because of a bug in our path search, this type check is still necessary :( + if (!(objBank instanceof TargetRegisterBank)) { + continue; + } + TargetRegisterBank bank = (TargetRegisterBank) objBank; + // This is equivalent to hitting the "Flush Caches" button in the Target Provider + bank.invalidateCaches().get(); + // If you know the names of the registers to read + bank.readRegistersNamed("rax", "rbx").get(); + // Values are coupled to elements, so we can also just refresh them to re-read all + bank.fetchElements(true).get(); + } + + /** + * Alternatively, to refresh just the bank for the current thread or frame, we need to + * determine the active frame. We'll do that by asking the TraceManagerService. Then, + * because that's in "trace land," we'll use the TraceRecorder to map that into "target + * land." Generally, you'd then need to navigate the schema as before, but relative to the + * target object. Because fetching registers is so common, this is already built in to the + * recorder. + */ + DebuggerTraceManagerService traceManager = + state.getTool().getService(DebuggerTraceManagerService.class); + // There are also getCurreentTrace(), etc., if you want just the one thing + DebuggerCoordinates current = traceManager.getCurrent(); + + // Now, we need to get the relevant recorder + TraceRecorder recorder = modelService.getRecorder(current.getTrace()); + // There's a chance of an NPE here if there is no "current frame" + TargetRegisterBank bank = + recorder.getTargetRegisterBank(current.getThread(), current.getFrame()); + // Now do the same to the bank as before + bank.invalidateCaches().get(); + bank.fetchElements(true).get(); + } +} diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/DebuggerResources.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/DebuggerResources.java index 5f834dc155..1b288160a9 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/DebuggerResources.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/DebuggerResources.java @@ -76,6 +76,7 @@ public interface DebuggerResources { ImageIcon ICON_TRACE = Trace.TRACE_ICON; ImageIcon ICON_THREAD = ResourceManager.loadImage("images/thread.png"); ImageIcon ICON_PROGRAM = ProgramContentHandler.PROGRAM_ICON; + ImageIcon ICON_PROCESSOR = ResourceManager.loadImage("images/kcmprocessor.png"); ImageIcon ICON_LAUNCH = ResourceManager.loadImage("images/launch.png"); ImageIcon ICON_ATTACH = ResourceManager.loadImage("images/attach.png"); @@ -795,6 +796,24 @@ public interface DebuggerResources { } } + interface ChoosePlatformAction { + String NAME = "Choose Platform"; + String GROUP = GROUP_MAPPING; + String DESCRIPTION = "Manually select the target platform"; + Icon ICON = ICON_PROCESSOR; + String HELP_ANCHOR = "choose_platform"; + + public static ActionBuilder builder(Plugin owner) { + String ownerName = owner.getName(); + return new ActionBuilder(ownerName, NAME) + .description(DESCRIPTION) + .menuPath(DebuggerPluginPackage.NAME, NAME) + .menuGroup(GROUP) + .menuIcon(ICON) + .helpLocation(new HelpLocation(ownerName, HELP_ANCHOR)); + } + } + abstract class AbstractRecordAction extends DockingAction { public static final String NAME = "Record"; public static final Icon ICON = ICON_TRACE; diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/register/RegisterRow.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/register/RegisterRow.java index 8844502dbb..e2ee378a3a 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/register/RegisterRow.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/register/RegisterRow.java @@ -16,6 +16,7 @@ package ghidra.app.plugin.core.debug.gui.register; import java.math.BigInteger; +import java.util.Objects; import ghidra.program.model.data.DataType; import ghidra.program.model.lang.Language; @@ -31,7 +32,7 @@ public class RegisterRow { public RegisterRow(DebuggerRegistersProvider provider, int number, Register register) { this.provider = provider; this.number = number; - this.register = register; + this.register = Objects.requireNonNull(register); this.favorite = provider.isFavorite(register); } 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 new file mode 100644 index 0000000000..aaddc4fe64 --- /dev/null +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/AbstractDebuggerPlatformMapper.java @@ -0,0 +1,95 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.plugin.core.debug.mapping; + +import java.util.Collection; +import java.util.Set; + +import ghidra.app.plugin.core.debug.workflow.DisassembleTraceCommand; +import ghidra.app.plugin.core.debug.workflow.DisassemblyInject; +import ghidra.framework.plugintool.PluginTool; +import ghidra.program.model.address.*; +import ghidra.program.model.lang.*; +import ghidra.trace.model.Trace; +import ghidra.trace.model.guest.TraceGuestPlatform; +import ghidra.trace.model.target.TraceObject; +import ghidra.trace.model.thread.TraceThread; +import ghidra.util.task.TaskMonitor; + +public abstract class AbstractDebuggerPlatformMapper implements DebuggerPlatformMapper { + protected final PluginTool tool; + protected final Trace trace; + + public AbstractDebuggerPlatformMapper(PluginTool tool, Trace trace) { + this.tool = tool; + this.trace = trace; + } + + protected boolean canInterpret(TraceObject newFocus, long snap, TraceObject env, + String debugger, String arch, String os, Endian endian) { + return true; + } + + @Override + public boolean canInterpret(TraceObject newFocus, long snap) { + TraceObject env = DebuggerPlatformOpinion.getEnvironment(newFocus, snap); + if (env == null) { + return canInterpret(newFocus, snap, env, null, null, null, null); + } + String debugger = DebuggerPlatformOpinion.getDebugggerFromEnv(env, snap); + String arch = DebuggerPlatformOpinion.getArchitectureFromEnv(env, snap); + String os = DebuggerPlatformOpinion.getOperatingSystemFromEnv(env, snap); + Endian endian = DebuggerPlatformOpinion.getEndianFromEnv(env, snap); + return canInterpret(newFocus, snap, env, debugger, arch, os, endian); + } + + protected boolean isCancelSilently(Address start, long snap) { + return trace.getCodeManager().definedUnits().containsAddress(snap, start); + } + + protected Collection getDisassemblyInjections(TraceObject object) { + return Set.of(); + } + + protected abstract CompilerSpec getCompilerSpec(TraceObject object); + + @Override + public DisassemblyResult disassemble(TraceThread thread, TraceObject object, + Address start, AddressSetView restricted, long snap, TaskMonitor monitor) { + if (isCancelSilently(start, snap)) { + return DisassemblyResult.CANCELLED; + } + TraceGuestPlatform guest = + trace.getPlatformManager().getGuestPlatform(getCompilerSpec(object)); + + Collection injects = getDisassemblyInjections(object); + DisassembleTraceCommand dis = + DisassembleTraceCommand.create(guest, start, restricted); + Language language = guest == null ? trace.getBaseLanguage() : guest.getLanguage(); + AddressSet startSet = new AddressSet(start); + for (DisassemblyInject i : injects) { + i.pre(tool, dis, trace, language, snap, null, startSet, restricted); + } + boolean result = dis.applyToTyped(trace.getFixedProgramView(snap), monitor); + if (!result) { + return DisassemblyResult.failed(dis.getStatusMsg()); + } + for (DisassemblyInject i : injects) { + i.post(tool, trace, snap, dis.getDisassembledAddressSet()); + } + return DisassemblyResult.success(!dis.getDisassembledAddressSet().isEmpty()); + } +} diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/language/TraceLanguageManager.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/AbstractDebuggerPlatformOffer.java similarity index 52% rename from Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/language/TraceLanguageManager.java rename to Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/AbstractDebuggerPlatformOffer.java index bf8f1c15db..0e190f1514 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/language/TraceLanguageManager.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/AbstractDebuggerPlatformOffer.java @@ -13,19 +13,26 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.trace.model.language; +package ghidra.app.plugin.core.debug.mapping; -import java.util.Collection; +import ghidra.program.model.lang.CompilerSpec; -import ghidra.program.model.lang.InstructionSet; -import ghidra.program.model.lang.Language; +public abstract class AbstractDebuggerPlatformOffer implements DebuggerPlatformOffer { + private final String description; + protected final CompilerSpec cSpec; -public interface TraceLanguageManager { - Language getBaseLanguage(); + public AbstractDebuggerPlatformOffer(String description, CompilerSpec cSpec) { + this.description = description; + this.cSpec = cSpec; + } - TraceGuestLanguage addGuestLanguage(Language language); + @Override + public String getDescription() { + return description; + } - Collection getGuestLanguages(); - - InstructionSet mapGuestInstructionAddressesToHost(InstructionSet instructionSet); + @Override + public CompilerSpec getCompilerSpec() { + return cSpec; + } } diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/AbstractDebuggerPlatformOpinion.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/AbstractDebuggerPlatformOpinion.java new file mode 100644 index 0000000000..6a9234f164 --- /dev/null +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/AbstractDebuggerPlatformOpinion.java @@ -0,0 +1,42 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.plugin.core.debug.mapping; + +import java.util.Set; + +import ghidra.program.model.lang.Endian; +import ghidra.trace.model.Trace; +import ghidra.trace.model.target.TraceObject; + +public abstract class AbstractDebuggerPlatformOpinion implements DebuggerPlatformOpinion { + + protected abstract Set getOffers(TraceObject object, long snap, + TraceObject env, String debugger, String arch, String os, Endian endian); + + @Override + public Set getOffers(Trace trace, TraceObject object, long snap, + boolean includeOverrides) { + TraceObject env = DebuggerPlatformOpinion.getEnvironment(object, snap); + if (env == null) { + return getOffers(object, snap, env, null, null, null, null); + } + String debugger = DebuggerPlatformOpinion.getDebugggerFromEnv(env, snap); + String arch = DebuggerPlatformOpinion.getArchitectureFromEnv(env, snap); + String os = DebuggerPlatformOpinion.getOperatingSystemFromEnv(env, snap); + Endian endian = DebuggerPlatformOpinion.getEndianFromEnv(env, snap); + return getOffers(object, snap, env, debugger, arch, os, endian); + } +} diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/DebuggerMappingOpinion.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/DebuggerMappingOpinion.java index 05bafefc0a..03d9da0bbd 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/DebuggerMappingOpinion.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/DebuggerMappingOpinion.java @@ -71,7 +71,7 @@ public interface DebuggerMappingOpinion extends ExtensionPoint { * * @param target the target to be recorded, usually a process * @param includeOverrides true to include offers with negative confidence - * @return a future which completes with the set of offers + * @return a list of offers ordered highest confidence first */ public static List queryOpinions(TargetObject target, boolean includeOverrides) { diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/DebuggerPlatformMapper.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/DebuggerPlatformMapper.java new file mode 100644 index 0000000000..ac7e7e551e --- /dev/null +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/DebuggerPlatformMapper.java @@ -0,0 +1,58 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.plugin.core.debug.mapping; + +import ghidra.program.model.address.Address; +import ghidra.program.model.address.AddressSetView; +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 + */ +public interface DebuggerPlatformMapper { + /** + * Prepare the given trace for interpretation under this mapper + * + * @param trace the trace + * @param snap the snap + */ + void addToTrace(long snap); + + /** + * When focus changes, decide if this mapper should remain active + * + * @param newFocus the newly-focused object + * @param snap the snap, usually the current snap + * @return true to remain active, false to select a new mapper + */ + boolean canInterpret(TraceObject newFocus, long snap); + + /** + * Disassemble starting at a given address and snap, limited to a given address set + * + * @param thread the thread if applicable + * @param object the object for platform context + * @param start the starting address + * @param restricted the limit of disassembly + * @param snap the snap, usually the current snap + * @param monitor a monitor for the disassembler + * @return the result + */ + DisassemblyResult disassemble(TraceThread thread, TraceObject object, + Address start, AddressSetView restricted, long snap, TaskMonitor monitor); +} diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/DebuggerPlatformOffer.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/DebuggerPlatformOffer.java new file mode 100644 index 0000000000..714bb427b7 --- /dev/null +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/DebuggerPlatformOffer.java @@ -0,0 +1,99 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.plugin.core.debug.mapping; + +import ghidra.framework.plugintool.PluginTool; +import ghidra.program.model.lang.*; +import ghidra.program.util.DefaultLanguageService; +import ghidra.trace.model.Trace; + +/** + * An offer to map from a trace to a Ghidra langauge / compiler + */ +public interface DebuggerPlatformOffer { + + /** + * Get a human-readable description of the offer. + * + *

+ * Generally, more detailed descriptions imply a higher confidence. + * + * @return the description + */ + String getDescription(); + + /** + * Get the confidence of this offer. + * + *

+ * Offers with numerically higher confidence are preferred. Negative confidence values are + * considered "manual overrides," and so are never selected automatically and are hidden from + * prompts by default. + * + *

+ * TODO: Spec out some standard numbers. Maybe an enum? + * + * @return the confidence + */ + int getConfidence(); + + /** + * Check if the confidence indicates this offer is a manual override. + * + * @return true if the confidence is negative + */ + default boolean isOverride() { + return getConfidence() < 0; + } + + /** + * Get the language to which this offer can map + * + * @return the langauge + */ + default Language getLanguage() { + CompilerSpec cSpec = getCompilerSpec(); + return cSpec == null ? null : cSpec.getLanguage(); + } + + /** + * Get the compiler to which this offer can map + * + * @return the compiler spec + */ + CompilerSpec getCompilerSpec(); + + default CompilerSpec getCompilerSpec(LanguageID langID, CompilerSpecID cSpecID) { + try { + LanguageService langServ = DefaultLanguageService.getLanguageService(); + Language lang = langServ.getLanguage(langID); + return cSpecID == null ? lang.getDefaultCompilerSpec() + : lang.getCompilerSpecByID(cSpecID); + } + catch (LanguageNotFoundException | CompilerSpecNotFoundException e) { + throw new AssertionError(e); + } + } + + /** + * Get the mapper, which implements this offer + * + * @param tool the plugin tool + * @param trace the trace the trace to be mapped + * @return the mapper + */ + DebuggerPlatformMapper take(PluginTool tool, Trace trace); +} diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/DebuggerPlatformOpinion.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/DebuggerPlatformOpinion.java new file mode 100644 index 0000000000..b82654f117 --- /dev/null +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/DebuggerPlatformOpinion.java @@ -0,0 +1,158 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.plugin.core.debug.mapping; + +import java.util.*; + +import com.google.common.collect.Range; + +import ghidra.dbg.target.TargetEnvironment; +import ghidra.dbg.util.PathPredicates; +import ghidra.program.model.lang.Endian; +import ghidra.trace.model.Trace; +import ghidra.trace.model.target.TraceObject; +import ghidra.trace.model.target.TraceObjectValue; +import ghidra.util.Msg; +import ghidra.util.classfinder.ClassSearcher; +import ghidra.util.classfinder.ExtensionPoint; + +/** + * An opinion governing analysis and display of a trace according to a platform (processor, ISA, OS, + * ABI, etc.) + * + *

+ * This is meant for "object-based" traces, which may soon supplant "table-based" traces. The latter + * requires mapping between the target model and the trace, and so the UI need not worry about + * normalizing; however, without a mapping, nothing works. The former allows for direct recording of + * the model into the trace without prior mapping. Instead, the choice of platform and + * interpretation is performed by the front-end analysis and display. These are essentially the + * counterpart to {@link DebuggerMappingOpinion}. + * + *

+ * The opinions are queried, each of which may produce zero or more scored offers. Depending on + * context and automation, the top offer may be chosen automatically, or the user may be prompted to + * select from a sorted list. The chosen offer is then applied. Application here means writing + * metadata to the trace database, usually as "guest platforms." The analysis and display use that + * metadata to interpret the trace data, e.g., to select a language when disassembling at the + * program counter. + */ +public interface DebuggerPlatformOpinion extends ExtensionPoint { + + Comparator HIGHEST_CONFIDENCE_FIRST = + Comparator.comparing(o -> -o.getConfidence()); + + /** + * Find the environment for the given object + * + * @param object the object, usually the user's focus + * @param snap the current snap + * @return the environment object, or null + */ + static TraceObject getEnvironment(TraceObject object, long snap) { + if (object == null) { + return null; + } + TraceObject root = object.getRoot(); + List pathToEnv = root.getTargetSchema() + .searchForSuitable(TargetEnvironment.class, object.getCanonicalPath().getKeyList()); + if (pathToEnv == null) { + return null; + } + return root.getSuccessors(Range.singleton(snap), PathPredicates.pattern(pathToEnv)) + .findAny() + .map(p -> p.getDestination(root)) + .orElse(null); + } + + static String getStringAttribute(TraceObject obj, long snap, String key) { + TraceObjectValue val = obj.getValue(snap, key); + if (val == null) { + return null; + } + return val.getValue().toString(); + } + + static String getDebugggerFromEnv(TraceObject env, long snap) { + return getStringAttribute(env, snap, TargetEnvironment.DEBUGGER_ATTRIBUTE_NAME); + } + + static String getArchitectureFromEnv(TraceObject env, long snap) { + return getStringAttribute(env, snap, TargetEnvironment.ARCH_ATTRIBUTE_NAME); + } + + static String getOperatingSystemFromEnv(TraceObject env, long snap) { + return getStringAttribute(env, snap, TargetEnvironment.OS_ATTRIBUTE_NAME); + } + + /** + * Get the endianness from the given environment + * + * @param env the environment object + * @param snap the current snap + * @return the endianness, or null + */ + static Endian getEndianFromEnv(TraceObject env, long snap) { + String strEndian = getStringAttribute(env, snap, TargetEnvironment.ENDIAN_ATTRIBUTE_NAME); + if (strEndian == null) { + return null; + } + if (strEndian.toLowerCase().contains("little")) { + return Endian.LITTLE; + } + if (strEndian.toLowerCase().contains("big")) { + return Endian.BIG; + } + return null; + } + + /** + * Query all known opinions for offers of platform interpretation + * + * @param trace the trace + * @param object the object, usually the one in focus + * @param snap the snap + * @param includeOverrides true to include offers with negative confidence + * @return the list of offers ordered highest confidence first + */ + static List queryOpinions(Trace trace, TraceObject object, long snap, + boolean includeOverrides) { + List result = new ArrayList<>(); + for (DebuggerPlatformOpinion opinion : ClassSearcher + .getInstances(DebuggerPlatformOpinion.class)) { + try { + Set offers = + opinion.getOffers(trace, object, snap, includeOverrides); + result.addAll(offers); + } + catch (Exception e) { + Msg.error(DebuggerPlatformOpinion.class, + "Problem querying opinion " + opinion + " for platform offers: " + e); + } + } + result.sort(HIGHEST_CONFIDENCE_FIRST); + return result; + } + + /** + * Render offers for the given object + * + * @param object the object, usually the one in focus + * @param includeOverrides true to include offers with negative confidence + * @return zero or more offers to interpret the target according to a platform + */ + Set getOffers(Trace trace, TraceObject object, long snap, + boolean includeOverrides); +} 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 new file mode 100644 index 0000000000..fc14beb139 --- /dev/null +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/DefaultDebuggerPlatformMapper.java @@ -0,0 +1,90 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.plugin.core.debug.mapping; + +import ghidra.framework.plugintool.PluginTool; +import ghidra.program.model.address.*; +import ghidra.program.model.lang.CompilerSpec; +import ghidra.program.model.lang.Language; +import ghidra.trace.model.Trace; +import ghidra.trace.model.guest.TraceGuestPlatform; +import ghidra.trace.model.guest.TracePlatformManager; +import ghidra.trace.model.target.TraceObject; +import ghidra.util.MathUtilities; + +public class DefaultDebuggerPlatformMapper extends AbstractDebuggerPlatformMapper { + + protected static boolean isHarvard(Language language) { + return language.getDefaultSpace() != language.getDefaultDataSpace(); + } + + protected final PluginTool tool; + protected final CompilerSpec cSpec; + + public DefaultDebuggerPlatformMapper(PluginTool tool, Trace trace, CompilerSpec cSpec) { + super(tool, trace); + validate(cSpec); + this.tool = tool; + this.cSpec = cSpec; + } + + protected void validate(CompilerSpec cSpec) { + if (isHarvard(cSpec.getLanguage())) { + throw new IllegalArgumentException("This mapper cannot handle Harvard guests"); + } + } + + @Override + protected CompilerSpec getCompilerSpec(TraceObject object) { + return cSpec; + } + + @Override + public void addToTrace(long snap) { + TracePlatformManager platformManager = trace.getPlatformManager(); + TraceGuestPlatform platform = platformManager.getOrAddGuestPlatform(cSpec); + if (platform == null) { + return; // It's the host compiler spec + } + addMappedRanges(platform); + } + + protected void addMappedRanges(TraceGuestPlatform platform) { + Trace trace = platform.getTrace(); + AddressSpace hostSpace = trace.getBaseAddressFactory().getDefaultAddressSpace(); + AddressSpace guestSpace = platform.getAddressFactory().getDefaultAddressSpace(); + long min = MathUtilities.unsignedMax(hostSpace.getMinAddress().getOffset(), + guestSpace.getMinAddress().getOffset()); + long max = MathUtilities.unsignedMin(hostSpace.getMaxAddress().getOffset(), + guestSpace.getMaxAddress().getOffset()); + Address hostStart = hostSpace.getAddress(min); + Address guestStart = guestSpace.getAddress(min); + + /* + * TODO: I could perhaps do better, but assuming I'm the only source of mappings, this + * should suffice. + */ + if (platform.getHostAddressSet().contains(hostStart)) { + return; + } + try { + platform.addMappedRange(hostStart, guestStart, max - min + 1); + } + catch (AddressOverflowException e) { + throw new AssertionError(e); + } + } +} diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/DisassemblyResult.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/DisassemblyResult.java new file mode 100644 index 0000000000..25364d9574 --- /dev/null +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/DisassemblyResult.java @@ -0,0 +1,49 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.plugin.core.debug.mapping; + +public class DisassemblyResult { + public static final DisassemblyResult SUCCESS = new DisassemblyResult(true, null); + public static final DisassemblyResult CANCELLED = new DisassemblyResult(false, null); + + public static DisassemblyResult failed(String errorMessage) { + return new DisassemblyResult(false, errorMessage); + } + + public static DisassemblyResult success(boolean atLeastOne) { + return atLeastOne ? SUCCESS : CANCELLED; + } + + private final boolean atLeastOne; + private final String errorMessage; + + public DisassemblyResult(boolean atLeastOne, String errorMessage) { + this.atLeastOne = atLeastOne; + this.errorMessage = errorMessage; + } + + public boolean isAtLeastOne() { + return atLeastOne; + } + + public boolean isSuccess() { + return errorMessage == null; + } + + public String getErrorMessage() { + return errorMessage; + } +} diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/legacy/LegacyDebuggerPlatformOpinion.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/legacy/LegacyDebuggerPlatformOpinion.java new file mode 100644 index 0000000000..ce4834f6b4 --- /dev/null +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/legacy/LegacyDebuggerPlatformOpinion.java @@ -0,0 +1,100 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.plugin.core.debug.mapping.legacy; + +import java.util.*; +import java.util.stream.Collectors; + +import ghidra.app.plugin.core.debug.mapping.*; +import ghidra.app.plugin.core.debug.workflow.DisassemblyInject; +import ghidra.framework.plugintool.PluginTool; +import ghidra.lifecycle.Transitional; +import ghidra.program.model.lang.CompilerSpec; +import ghidra.trace.model.Trace; +import ghidra.trace.model.target.TraceObject; +import ghidra.util.classfinder.ClassSearcher; + +/** + * An opinion which retains the front-end functionality when using the mapped recorder, i.e., when + * displaying non-object-based traces. + */ +@Transitional +public class LegacyDebuggerPlatformOpinion implements DebuggerPlatformOpinion { + + protected static class LegacyDebuggerPlatformMapper extends AbstractDebuggerPlatformMapper { + public LegacyDebuggerPlatformMapper(PluginTool tool, Trace trace) { + super(tool, trace); + } + + @Override + protected CompilerSpec getCompilerSpec(TraceObject object) { + return trace.getBaseCompilerSpec(); + } + + @Override + public void addToTrace(long snap) { + // Nothing to do + } + + @Override + public boolean canInterpret(TraceObject newFocus, long snap) { + return true; + } + + @Override + protected Collection getDisassemblyInjections(TraceObject object) { + // Track an injects set using a listener instead? + return ClassSearcher.getInstances(DisassemblyInject.class) + .stream() + .filter(i -> i.isApplicable(trace)) + .sorted(Comparator.comparing(i -> i.getPriority())) + .collect(Collectors.toList()); + } + } + + enum Offers implements DebuggerPlatformOffer { + LEGACY { + @Override + public String getDescription() { + return "Legacy (Already mapped by recorder)"; + } + + @Override + public int getConfidence() { + return 1; + } + + @Override + public CompilerSpec getCompilerSpec() { + return null; + } + + @Override + public DebuggerPlatformMapper take(PluginTool tool, Trace trace) { + return new LegacyDebuggerPlatformMapper(tool, trace); + } + }; + } + + @Override + public Set getOffers(Trace trace, TraceObject focus, long snap, + boolean includeOverrides) { + if (trace.getObjectManager().getRootObject() != null) { + return Set.of(); + } + return Set.of(Offers.LEGACY); + } +} diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/arm/ArmDisassemblyInject.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/arm/ArmDisassemblyInject.java index 3adf2ff9e0..69eb316a22 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/arm/ArmDisassemblyInject.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/arm/ArmDisassemblyInject.java @@ -17,16 +17,13 @@ package ghidra.app.plugin.core.debug.platform.arm; import java.math.BigInteger; -import ghidra.app.cmd.disassemble.DisassembleCommand; -import ghidra.app.plugin.core.debug.workflow.DisassemblyInject; -import ghidra.app.plugin.core.debug.workflow.DisassemblyInjectInfo; +import ghidra.app.plugin.core.debug.workflow.*; import ghidra.framework.plugintool.PluginTool; import ghidra.program.model.address.AddressSetView; -import ghidra.program.model.lang.Register; -import ghidra.program.model.lang.RegisterValue; +import ghidra.program.model.lang.*; +import ghidra.trace.model.Trace; import ghidra.trace.model.memory.TraceMemoryRegisterSpace; import ghidra.trace.model.memory.TraceMemoryState; -import ghidra.trace.model.program.TraceProgramView; import ghidra.trace.model.thread.TraceThread; import ghidra.util.Msg; @@ -61,36 +58,37 @@ public class ArmDisassemblyInject implements DisassemblyInject { } @Override - public void pre(PluginTool tool, DisassembleCommand command, TraceProgramView view, - TraceThread thread, AddressSetView startSet, AddressSetView restricted) { + public void pre(PluginTool tool, DisassembleTraceCommand command, Trace trace, + Language language, long snap, TraceThread thread, AddressSetView startSet, + AddressSetView restricted) { /** * TODO: There are probably several avenues to figure the TMode. The most important, I think * is the cpsr register, when it's available. For auto-pc, the trace recorder ought to have * recorded cpsr at the recorded tick. */ - Register cpsrReg = view.getLanguage().getRegister("cpsr"); - Register tModeReg = view.getLanguage().getRegister("TMode"); + Register cpsrReg = language.getRegister("cpsr"); + Register tModeReg = language.getRegister("TMode"); if (cpsrReg == null || tModeReg == null) { - Msg.error(this, "No cpsr or TMode register in ARM language?: " + - view.getLanguage().getLanguageID()); + Msg.error(this, + "No cpsr or TMode register in ARM language?: " + language.getLanguageID()); return; } TraceMemoryRegisterSpace regs = - view.getTrace().getMemoryManager().getMemoryRegisterSpace(thread, false); + trace.getMemoryManager().getMemoryRegisterSpace(thread, false); /** * Some variants (particularly Cortex-M) are missing cpsr This seems to indicate it only * supports THUMB. There is an epsr (xpsr in gdb), but we don't have it in our models, and * its TMode bit must be set, or it will fault. */ - if (regs == null || regs.getState(view.getSnap(), cpsrReg) != TraceMemoryState.KNOWN) { + if (regs == null || regs.getState(snap, cpsrReg) != TraceMemoryState.KNOWN) { command.setInitialContext(new RegisterValue(tModeReg, BigInteger.ONE)); return; } - RegisterValue cpsrVal = regs.getValue(view.getSnap(), cpsrReg); + RegisterValue cpsrVal = regs.getValue(snap, cpsrReg); if (isThumbMode(cpsrVal)) { command.setInitialContext(new RegisterValue(tModeReg, BigInteger.ONE)); } 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 new file mode 100644 index 0000000000..176f88a033 --- /dev/null +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/dbgeng/DbgengDebuggerPlatformOpinion.java @@ -0,0 +1,83 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.plugin.core.debug.platform.dbgeng; + +import java.util.Collection; +import java.util.Set; + +import ghidra.app.plugin.core.debug.mapping.*; +import ghidra.app.plugin.core.debug.workflow.DisassemblyInject; +import ghidra.framework.plugintool.PluginTool; +import ghidra.program.model.lang.*; +import ghidra.trace.model.Trace; +import ghidra.trace.model.target.TraceObject; + +public class DbgengDebuggerPlatformOpinion extends AbstractDebuggerPlatformOpinion { + protected static final LanguageID LANG_ID_X86_64 = new LanguageID("x86:LE:64:default"); + 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) { + super(tool, trace, cSpec); + } + // TODO: Map registers: efl,rfl,rflags->eflags + + @Override + protected Collection getDisassemblyInjections(TraceObject object) { + return INJECTS; + } + } + + enum Offers implements DebuggerPlatformOffer { + // TODO: X86? + X64 { + @Override + public String getDescription() { + return "Dbgeng on Windows x64"; + } + + @Override + public int getConfidence() { + return 100; + } + + @Override + public CompilerSpec getCompilerSpec() { + return getCompilerSpec(LANG_ID_X86_64, COMP_ID_VS); + } + + @Override + public DebuggerPlatformMapper take(PluginTool tool, Trace trace) { + return new DbgengX64DebuggerPlatformMapper(tool, trace, getCompilerSpec()); + } + }; + } + + @Override + protected Set getOffers(TraceObject object, long snap, TraceObject env, + String debugger, String arch, String os, Endian endian) { + if (debugger == null || arch == null || !debugger.toLowerCase().contains("dbg")) { + return Set.of(); + } + boolean is64Bit = arch.contains("x86_64") || arch.contains("x64_32"); + if (!is64Bit) { + return Set.of(); + } + return Set.of(Offers.X64); + } +} 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 8e80b52395..e363bc812b 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 @@ -22,21 +22,20 @@ import java.util.Set; import java.util.concurrent.*; import java.util.stream.Collectors; -import ghidra.app.cmd.disassemble.DisassembleCommand; -import ghidra.app.plugin.core.debug.workflow.DisassemblyInject; -import ghidra.app.plugin.core.debug.workflow.DisassemblyInjectInfo; +import ghidra.app.plugin.core.debug.workflow.*; import ghidra.app.services.DebuggerModelService; import ghidra.app.services.TraceRecorder; -import ghidra.app.util.bin.MemoryByteProvider; +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.framework.plugintool.PluginTool; import ghidra.program.model.address.*; 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.modules.TraceModule; -import ghidra.trace.model.program.TraceProgramView; import ghidra.trace.model.thread.TraceThread; import ghidra.util.Msg; import ghidra.util.task.TaskMonitor; @@ -50,9 +49,9 @@ public class DbgengX64DisassemblyInject implements DisassemblyInject { } @Override - public void pre(PluginTool tool, DisassembleCommand command, TraceProgramView view, - TraceThread thread, AddressSetView startSet, AddressSetView restricted) { - Trace trace = view.getTrace(); + public void pre(PluginTool tool, DisassembleTraceCommand command, Trace trace, + Language language, long snap, TraceThread thread, AddressSetView startSet, + AddressSetView restricted) { AddressRange first = startSet.getFirstRange(); if (first == null) { return; @@ -60,20 +59,19 @@ public class DbgengX64DisassemblyInject implements DisassemblyInject { DebuggerModelService modelService = tool.getService(DebuggerModelService.class); TraceRecorder recorder = modelService == null ? null : modelService.getRecorder(trace); Collection modules = - trace.getModuleManager().getModulesAt(view.getSnap(), first.getMinAddress()); + trace.getModuleManager().getModulesAt(snap, first.getMinAddress()); Set modes = modules.stream() - .map(m -> modeForModule(recorder, view, m)) + .map(m -> modeForModule(recorder, trace, snap, m)) .filter(m -> m != Mode.UNK) .collect(Collectors.toSet()); if (modes.size() != 1) { return; } Mode mode = modes.iterator().next(); - Language lang = trace.getBaseLanguage(); - Register addrsizeReg = lang.getRegister("addrsize"); - Register opsizeReg = lang.getRegister("opsize"); - ProgramContextImpl context = new ProgramContextImpl(lang); - lang.applyContextSettings(context); + Register addrsizeReg = language.getRegister("addrsize"); + Register opsizeReg = language.getRegister("opsize"); + ProgramContextImpl context = new ProgramContextImpl(language); + language.applyContextSettings(context); RegisterValue ctxVal = context.getDisassemblyContext(first.getMinAddress()); if (mode == Mode.X64) { command.setInitialContext(ctxVal @@ -88,9 +86,9 @@ public class DbgengX64DisassemblyInject implements DisassemblyInject { // Shouldn't ever get anything else. } - protected Mode modeForModule(TraceRecorder recorder, TraceProgramView view, + protected Mode modeForModule(TraceRecorder recorder, Trace trace, long snap, TraceModule module) { - if (recorder != null && recorder.getSnap() == view.getSnap()) { + if (recorder != null && recorder.getSnap() == snap) { AddressSet set = new AddressSet(); set.add(module.getBase(), module.getBase()); // Recorder should read page try { @@ -104,9 +102,10 @@ public class DbgengX64DisassemblyInject implements DisassemblyInject { // Try to parse whatever's there. If 0s, it'll come UNK. } } - MemoryByteProvider mbp = new MemoryByteProvider(view.getMemory(), module.getBase()); + MemBuffer bufferAt = trace.getMemoryManager().getBufferAt(snap, module.getBase()); + ByteProvider bp = new MemBufferByteProvider(bufferAt); try { - PortableExecutable pe = new PortableExecutable(mbp, SectionLayout.MEMORY, false, false); + PortableExecutable pe = new PortableExecutable(bp, SectionLayout.MEMORY, false, false); NTHeader ntHeader = pe.getNTHeader(); if (ntHeader == null) { return Mode.UNK; diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/arm/FridaArmDebuggerMappingOpinion.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/frida/FridaArmDebuggerMappingOpinion.java similarity index 97% rename from Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/arm/FridaArmDebuggerMappingOpinion.java rename to Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/frida/FridaArmDebuggerMappingOpinion.java index 44e50c831f..894c514c48 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/arm/FridaArmDebuggerMappingOpinion.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/frida/FridaArmDebuggerMappingOpinion.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.plugin.core.debug.platform.arm; +package ghidra.app.plugin.core.debug.platform.frida; import java.util.Set; diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/frida/FridaDebuggerPlatformOpinion.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/frida/FridaDebuggerPlatformOpinion.java new file mode 100644 index 0000000000..1ff3247f48 --- /dev/null +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/frida/FridaDebuggerPlatformOpinion.java @@ -0,0 +1,134 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.plugin.core.debug.platform.frida; + +import java.util.Set; + +import ghidra.app.plugin.core.debug.mapping.*; +import ghidra.framework.plugintool.PluginTool; +import ghidra.program.model.lang.*; +import ghidra.trace.model.Trace; +import ghidra.trace.model.target.TraceObject; + +public class FridaDebuggerPlatformOpinion extends AbstractDebuggerPlatformOpinion { + protected static final LanguageID LANG_ID_AARCH64 = new LanguageID("AARCH64:LE:64:v8A"); + protected static final LanguageID LANG_ID_X86 = new LanguageID("x86:LE:32:default"); + protected static final LanguageID LANG_ID_X86_64 = new LanguageID("x86:LE:64:default"); + protected static final CompilerSpecID COMP_ID_DEFAULT = new CompilerSpecID("default"); + protected static final CompilerSpecID COMP_ID_GCC = new CompilerSpecID("gcc"); + protected static final CompilerSpecID COMP_ID_VS = new CompilerSpecID("windows"); + + protected static class FridaDebuggerPlatformMapper + extends DefaultDebuggerPlatformMapper { + public FridaDebuggerPlatformMapper(PluginTool tool, Trace trace, + CompilerSpec cSpec) { + super(tool, trace, cSpec); + } + // TODO: Map registers: rflags<->eflags for x86_64? + } + + enum Offers implements DebuggerPlatformOffer { + AARCH64_MACOS("Frida on macOS Apple Silicon", LANG_ID_AARCH64, COMP_ID_DEFAULT), + I386_LINUX("Frida on Linux i386", LANG_ID_X86, COMP_ID_GCC), + I386_MACOS("Frida on macOS i386", LANG_ID_X86, COMP_ID_GCC), + I386_WINDOWS("Frida on Windows x86", LANG_ID_X86, COMP_ID_VS), + X86_64_LINUX("Frida on Linux x86_64", LANG_ID_X86_64, COMP_ID_GCC), + X86_64_MACOS("Frida on macOS x86_64", LANG_ID_X86_64, COMP_ID_GCC), + X86_64_WINDOWS("Frida on Windows x64", LANG_ID_X86_64, COMP_ID_VS); + + final String description; + final LanguageID langID; + final CompilerSpecID cSpecID; + + private Offers(String description, LanguageID langID, CompilerSpecID cSpecID) { + this.description = description; + this.langID = langID; + this.cSpecID = cSpecID; + } + + @Override + public String getDescription() { + return description; + } + + @Override + public int getConfidence() { + return 100; + } + + @Override + public CompilerSpec getCompilerSpec() { + return getCompilerSpec(langID, cSpecID); + } + + @Override + public DebuggerPlatformMapper take(PluginTool tool, Trace trace) { + // TODO: May need these per offer + return new FridaDebuggerPlatformMapper(tool, trace, getCompilerSpec()); + } + } + + @Override + protected Set getOffers(TraceObject object, long snap, TraceObject env, + String debugger, String arch, String os, Endian endian) { + if (debugger == null || arch == null || + os == null | !debugger.toLowerCase().contains("frida")) { + return Set.of(); + } + String lcOS = os.toLowerCase(); + boolean isLinux = lcOS.contains("linux"); + boolean isMacOS = lcOS.contains("darwin") || lcOS.contains("macos"); + boolean isWindows = lcOS.contains("windows"); + String lcArch = arch.toLowerCase(); + // "arm" subsumes "arm64" + boolean isARM = lcArch.contains("aarch64") || lcArch.contains("arm"); + boolean isI386 = lcArch.contains("ia32") || lcArch.contains("x86-32") || + lcArch.contains("x86_32") || lcArch.contains("i386"); + boolean isX86_64 = lcArch.contains("x64") || lcArch.contains("x86-64") || + lcArch.contains("x86_64") || lcArch.contains("x64-32") || lcArch.contains("x64_32"); + // TODO: i686? I'd think 32-bit, + // but it was listed as 64-bit in FridaX86DebuggerMappingOpinion + + if (isLinux) { + if (isI386) { + return Set.of(Offers.I386_LINUX); + } + if (isX86_64) { + return Set.of(Offers.X86_64_LINUX); + } + } + if (isMacOS) { + if (isARM) { + return Set.of(Offers.AARCH64_MACOS); + } + if (isI386) { + return Set.of(Offers.I386_MACOS); + } + if (isX86_64) { + return Set.of(Offers.X86_64_MACOS); + } + } + if (isWindows) { + if (isI386) { + return Set.of(Offers.I386_WINDOWS); + } + if (isX86_64) { + return Set.of(Offers.X86_64_WINDOWS); + } + } + return Set.of(); + } +} diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/arm/GdbArmDebuggerMappingOpinion.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/gdb/GdbArmDebuggerMappingOpinion.java similarity index 95% rename from Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/arm/GdbArmDebuggerMappingOpinion.java rename to Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/gdb/GdbArmDebuggerMappingOpinion.java index f64b045b44..66090dbb93 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/arm/GdbArmDebuggerMappingOpinion.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/gdb/GdbArmDebuggerMappingOpinion.java @@ -13,13 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.plugin.core.debug.platform.arm; +package ghidra.app.plugin.core.debug.platform.gdb; import java.util.Collection; import java.util.Set; import ghidra.app.plugin.core.debug.mapping.DebuggerMappingOffer; -import ghidra.app.plugin.core.debug.platform.gdb.DefaultGdbDebuggerMappingOpinion; import ghidra.dbg.target.TargetObject; import ghidra.program.model.lang.*; import ghidra.program.util.DefaultLanguageService; diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/gdb/GdbDebuggerPlatformOpinion.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/gdb/GdbDebuggerPlatformOpinion.java new file mode 100644 index 0000000000..2b76447483 --- /dev/null +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/gdb/GdbDebuggerPlatformOpinion.java @@ -0,0 +1,99 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.plugin.core.debug.platform.gdb; + +import java.util.*; +import java.util.stream.Collectors; + +import org.apache.commons.lang3.tuple.Pair; + +import ghidra.app.plugin.core.debug.mapping.*; +import ghidra.framework.plugintool.PluginTool; +import ghidra.program.model.lang.*; +import ghidra.program.util.DefaultLanguageService; +import ghidra.trace.model.Trace; +import ghidra.trace.model.target.TraceObject; + +public class GdbDebuggerPlatformOpinion extends AbstractDebuggerPlatformOpinion { + public static final String EXTERNAL_TOOL = "gnu"; + public static final CompilerSpecID PREFERRED_CSPEC_ID = new CompilerSpecID("gcc"); + + private static final Map, List> CACHE = + new HashMap<>(); + + public static List getCompilerSpecsForGnu(String arch, + Endian endian) { + synchronized (CACHE) { + return CACHE.computeIfAbsent(Pair.of(arch, endian), p -> { + LanguageService langServ = DefaultLanguageService.getLanguageService(); + return langServ.getLanguageCompilerSpecPairs( + new ExternalLanguageCompilerSpecQuery(arch, EXTERNAL_TOOL, + endian, null, PREFERRED_CSPEC_ID)); + }); + } + } + + protected static class GdbDebuggerPlatformOffer extends AbstractDebuggerPlatformOffer { + public static GdbDebuggerPlatformOffer fromArchLCSP(String arch, + LanguageCompilerSpecPair lcsp) + throws CompilerSpecNotFoundException, LanguageNotFoundException { + return new GdbDebuggerPlatformOffer("Default GDB for " + arch, lcsp.getCompilerSpec()); + } + + public GdbDebuggerPlatformOffer(String description, CompilerSpec cSpec) { + super(description, cSpec); + } + + @Override + public int getConfidence() { + return 10; + } + + @Override + public DebuggerPlatformMapper take(PluginTool tool, Trace trace) { + return new GdbDebuggerPlatformMapper(tool, trace, cSpec); + } + } + + protected static class GdbDebuggerPlatformMapper extends DefaultDebuggerPlatformMapper { + public GdbDebuggerPlatformMapper(PluginTool tool, Trace trace, CompilerSpec cSpec) { + super(tool, trace, cSpec); + } + // TODO: eflags<->rflags for amd64 / x86-64 + } + + protected Set offersForLanguageAndCSpec(String arch, Endian endian, + LanguageCompilerSpecPair lcsp) + throws CompilerSpecNotFoundException, LanguageNotFoundException { + return Set.of(GdbDebuggerPlatformOffer.fromArchLCSP("Default GDB for " + arch, lcsp)); + } + + @Override + protected Set getOffers(TraceObject object, long snap, TraceObject env, + String debugger, String arch, String os, Endian endian) { + if (!"gdb".equals(debugger.toLowerCase())) { + return Set.of(); + } + return getCompilerSpecsForGnu(arch, endian).stream().flatMap(lcsp -> { + try { + return offersForLanguageAndCSpec(arch, endian, lcsp).stream(); + } + catch (CompilerSpecNotFoundException | LanguageNotFoundException e) { + throw new AssertionError(e); + } + }).collect(Collectors.toSet()); + } +} diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/jvm/JdiDalvikDebuggerMappingOpinion.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/jdi/JdiDalvikDebuggerMappingOpinion.java similarity index 98% rename from Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/jvm/JdiDalvikDebuggerMappingOpinion.java rename to Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/jdi/JdiDalvikDebuggerMappingOpinion.java index e05b82e795..9ed15f80ee 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/jvm/JdiDalvikDebuggerMappingOpinion.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/jdi/JdiDalvikDebuggerMappingOpinion.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.plugin.core.debug.platform.jvm; +package ghidra.app.plugin.core.debug.platform.jdi; import java.util.Collection; import java.util.Set; diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/jdi/JdiDebuggerPlatformOpinion.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/jdi/JdiDebuggerPlatformOpinion.java new file mode 100644 index 0000000000..a3a1345164 --- /dev/null +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/jdi/JdiDebuggerPlatformOpinion.java @@ -0,0 +1,90 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.plugin.core.debug.platform.jdi; + +import java.util.Set; + +import ghidra.app.plugin.core.debug.mapping.*; +import ghidra.framework.plugintool.PluginTool; +import ghidra.program.model.lang.*; +import ghidra.trace.model.Trace; +import ghidra.trace.model.target.TraceObject; + +public class JdiDebuggerPlatformOpinion extends AbstractDebuggerPlatformOpinion { + protected static final LanguageID LANG_ID_JAVA = new LanguageID("JVM:BE:32:default"); + protected static final LanguageID LANG_ID_DALVIK = new LanguageID("Dalvik:LE:32:default"); + protected static final CompilerSpecID COMP_ID_DEFAULT = new CompilerSpecID("default"); + + protected static class JdiDebuggerPlatformMapper extends DefaultDebuggerPlatformMapper { + // TODO: Delete this class? + public JdiDebuggerPlatformMapper(PluginTool tool, Trace trace, CompilerSpec cSpec) { + super(tool, trace, cSpec); + } + } + + enum Offers implements DebuggerPlatformOffer { + JAVA_VM("Java Virtual Machine", LANG_ID_JAVA, COMP_ID_DEFAULT), + DALVIK_VM("Dalvik Virtual Machine", LANG_ID_DALVIK, COMP_ID_DEFAULT); + + final String description; + final LanguageID langID; + final CompilerSpecID cSpecID; + + private Offers(String description, LanguageID langID, CompilerSpecID cSpecID) { + this.description = description; + this.langID = langID; + this.cSpecID = cSpecID; + } + + @Override + public String getDescription() { + return description; + } + + @Override + public int getConfidence() { + return 100; + } + + @Override + public CompilerSpec getCompilerSpec() { + return getCompilerSpec(langID, cSpecID); + } + + @Override + public DebuggerPlatformMapper take(PluginTool tool, Trace trace) { + return new JdiDebuggerPlatformMapper(tool, trace, getCompilerSpec()); + } + } + + @Override + protected Set getOffers(TraceObject object, long snap, TraceObject env, + String debugger, String arch, String os, Endian endian) { + if (debugger == null || arch == null || !debugger.contains("Java Debug Interface")) { + return Set.of(); + } + boolean isJava = arch.contains("OpenJDK"); + boolean isDalvik = arch.contains("Dalvik"); + // NOTE: Not worried about versions + if (isJava) { + return Set.of(Offers.JAVA_VM); + } + if (isDalvik) { + return Set.of(Offers.DALVIK_VM); + } + return Set.of(); + } +} diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/jvm/JdiJavaDebuggerMappingOpinion.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/jdi/JdiJvmDebuggerMappingOpinion.java similarity index 95% rename from Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/jvm/JdiJavaDebuggerMappingOpinion.java rename to Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/jdi/JdiJvmDebuggerMappingOpinion.java index 8bcb5041b2..08449559e3 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/jvm/JdiJavaDebuggerMappingOpinion.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/jdi/JdiJvmDebuggerMappingOpinion.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.plugin.core.debug.platform.jvm; +package ghidra.app.plugin.core.debug.platform.jdi; import java.util.Collection; import java.util.Set; @@ -22,7 +22,7 @@ import ghidra.app.plugin.core.debug.mapping.*; import ghidra.dbg.target.*; import ghidra.program.model.lang.*; -public class JdiJavaDebuggerMappingOpinion implements DebuggerMappingOpinion { +public class JdiJvmDebuggerMappingOpinion implements DebuggerMappingOpinion { protected static final LanguageID LANG_ID_JAVA = new LanguageID("JVM:BE:32:default"); protected static final CompilerSpecID COMP_ID_VS = new CompilerSpecID("default"); diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/arm/LldbArmDebuggerMappingOpinion.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/lldb/LldbArmDebuggerMappingOpinion.java similarity index 98% rename from Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/arm/LldbArmDebuggerMappingOpinion.java rename to Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/lldb/LldbArmDebuggerMappingOpinion.java index fa7d6e85a0..04842b9a1b 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/arm/LldbArmDebuggerMappingOpinion.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/lldb/LldbArmDebuggerMappingOpinion.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.plugin.core.debug.platform.arm; +package ghidra.app.plugin.core.debug.platform.lldb; import java.util.Set; diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/lldb/LldbDebuggerPlatformOpinion.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/lldb/LldbDebuggerPlatformOpinion.java new file mode 100644 index 0000000000..4548ba78fe --- /dev/null +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/lldb/LldbDebuggerPlatformOpinion.java @@ -0,0 +1,134 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.plugin.core.debug.platform.lldb; + +import java.util.Set; + +import ghidra.app.plugin.core.debug.mapping.*; +import ghidra.framework.plugintool.PluginTool; +import ghidra.program.model.lang.*; +import ghidra.trace.model.Trace; +import ghidra.trace.model.target.TraceObject; + +public class LldbDebuggerPlatformOpinion extends AbstractDebuggerPlatformOpinion { + protected static final LanguageID LANG_ID_AARCH64 = new LanguageID("AARCH64:LE:64:v8A"); + protected static final LanguageID LANG_ID_X86 = new LanguageID("x86:LE:32:default"); + protected static final LanguageID LANG_ID_X86_64 = new LanguageID("x86:LE:64:default"); + protected static final CompilerSpecID COMP_ID_DEFAULT = new CompilerSpecID("default"); + protected static final CompilerSpecID COMP_ID_GCC = new CompilerSpecID("gcc"); + protected static final CompilerSpecID COMP_ID_VS = new CompilerSpecID("windows"); + + protected static class LldbDebuggerPlatformMapper + extends DefaultDebuggerPlatformMapper { + public LldbDebuggerPlatformMapper(PluginTool tool, Trace trace, + CompilerSpec cSpec) { + super(tool, trace, cSpec); + } + // TODO: Map registers: rflags<->eflags for x86_64? + } + + enum Offers implements DebuggerPlatformOffer { + AARCH64_MACOS("LLDB on macOS Apple Silicon", LANG_ID_AARCH64, COMP_ID_DEFAULT), + I386_LINUX("LLDB on Linux i386", LANG_ID_X86, COMP_ID_GCC), + I386_MACOS("LLDB on macOS i386", LANG_ID_X86, COMP_ID_GCC), + I386_WINDOWS("LLDB on Windows x86", LANG_ID_X86, COMP_ID_VS), + X86_64_LINUX("LLDB on Linux x86_64", LANG_ID_X86_64, COMP_ID_GCC), + X86_64_MACOS("LLDB on macOS x86_64", LANG_ID_X86_64, COMP_ID_GCC), + X86_64_WINDOWS("LLDB on Windows x64", LANG_ID_X86_64, COMP_ID_VS); + + final String description; + final LanguageID langID; + final CompilerSpecID cSpecID; + + private Offers(String description, LanguageID langID, CompilerSpecID cSpecID) { + this.description = description; + this.langID = langID; + this.cSpecID = cSpecID; + } + + @Override + public String getDescription() { + return description; + } + + @Override + public int getConfidence() { + return 100; + } + + @Override + public CompilerSpec getCompilerSpec() { + return getCompilerSpec(langID, cSpecID); + } + + @Override + public DebuggerPlatformMapper take(PluginTool tool, Trace trace) { + // TODO: May need these per offer + return new LldbDebuggerPlatformMapper(tool, trace, getCompilerSpec()); + } + } + + @Override + protected Set getOffers(TraceObject object, long snap, TraceObject env, + String debugger, String arch, String os, Endian endian) { + if (debugger == null || arch == null || + os == null | !debugger.toLowerCase().contains("lldb")) { + return Set.of(); + } + String lcOS = os.toLowerCase(); + boolean isLinux = lcOS.contains("linux"); + boolean isMacOS = lcOS.contains("darwin") || lcOS.contains("macos"); + boolean isWindows = lcOS.contains("windows"); + String lcArch = arch.toLowerCase(); + // "arm" subsumes "arm64" + boolean isARM = lcArch.contains("aarch64") || lcArch.contains("arm"); + boolean isI386 = lcArch.contains("ia32") || lcArch.contains("x86-32") || + lcArch.contains("x86_32") || lcArch.contains("i386"); + boolean isX86_64 = lcArch.contains("x64") || lcArch.contains("x86-64") || + lcArch.contains("x86_64") || lcArch.contains("x64-32") || lcArch.contains("x64_32"); + // TODO: i686? I'd think 32-bit, + // but it was listed as 64-bit in LldbX86DebuggerMappingOpinion + + if (isLinux) { + if (isI386) { + return Set.of(Offers.I386_LINUX); + } + if (isX86_64) { + return Set.of(Offers.X86_64_LINUX); + } + } + if (isMacOS) { + if (isARM) { + return Set.of(Offers.AARCH64_MACOS); + } + if (isI386) { + return Set.of(Offers.I386_MACOS); + } + if (isX86_64) { + return Set.of(Offers.X86_64_MACOS); + } + } + if (isWindows) { + if (isI386) { + return Set.of(Offers.I386_WINDOWS); + } + if (isX86_64) { + return Set.of(Offers.X86_64_WINDOWS); + } + } + return Set.of(); + } +} diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/model/launch/AbstractDebuggerProgramLaunchOffer.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/model/launch/AbstractDebuggerProgramLaunchOffer.java index 8ccbc840e3..d64f1ed587 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/model/launch/AbstractDebuggerProgramLaunchOffer.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/model/launch/AbstractDebuggerProgramLaunchOffer.java @@ -516,6 +516,9 @@ public abstract class AbstractDebuggerProgramLaunchOffer implements DebuggerProg () -> onTimedOutRecorder(monitor, service, t)); }).thenCompose(r -> { monitor.incrementProgress(1); + if (r == null) { + throw new CancellationException(); + } monitor.setMessage("Confirming program is mapped to target"); CompletableFuture futureMapped = listenForMapping(mappingService, r); return AsyncTimer.DEFAULT_TIMER.mark() diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/model/record/EmptyDebuggerRegisterMapper.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/model/record/EmptyDebuggerRegisterMapper.java index 0713157671..e9224e9563 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/model/record/EmptyDebuggerRegisterMapper.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/model/record/EmptyDebuggerRegisterMapper.java @@ -50,7 +50,7 @@ public class EmptyDebuggerRegisterMapper implements DebuggerRegisterMapper { @Override public Set getRegistersOnTarget() { - return null; + return Set.of(); } @Override 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 new file mode 100644 index 0000000000..fb64b1e1cc --- /dev/null +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/platform/DebuggerPlatformServicePlugin.java @@ -0,0 +1,139 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.plugin.core.debug.service.platform; + +import java.util.HashMap; +import java.util.Map; + +import docking.action.builder.ActionBuilder; +import ghidra.app.plugin.PluginCategoryNames; +import ghidra.app.plugin.core.debug.DebuggerPluginPackage; +import ghidra.app.plugin.core.debug.event.TraceClosedPluginEvent; +import ghidra.app.plugin.core.debug.gui.DebuggerResources.ChoosePlatformAction; +import ghidra.app.plugin.core.debug.mapping.*; +import ghidra.app.services.DebuggerPlatformService; +import ghidra.app.services.DebuggerTraceManagerService; +import ghidra.framework.plugintool.*; +import ghidra.framework.plugintool.annotation.AutoServiceConsumed; +import ghidra.framework.plugintool.util.PluginStatus; +import ghidra.lifecycle.Unfinished; +import ghidra.trace.model.Trace; +import ghidra.trace.model.target.TraceObject; + +@PluginInfo( + shortDescription = "Debugger platform service plugin", + description = "Selects and manages platforms for the current focus", + category = PluginCategoryNames.DEBUGGER, + packageName = DebuggerPluginPackage.NAME, + status = PluginStatus.RELEASED, + eventsConsumed = { + TraceClosedPluginEvent.class, + }, + servicesRequired = { + DebuggerTraceManagerService.class, + }, + servicesProvided = { + DebuggerPlatformService.class, + }) +public class DebuggerPlatformServicePlugin extends Plugin implements DebuggerPlatformService { + + @AutoServiceConsumed + private DebuggerTraceManagerService traceManager; + @SuppressWarnings("unused") + private final AutoService.Wiring autoServiceWiring; + + ActionBuilder actionChoosePlatform; + + private final Map mappersByTrace = new HashMap<>(); + + public DebuggerPlatformServicePlugin(PluginTool tool) { + super(tool); + this.autoServiceWiring = AutoService.wireServicesProvidedAndConsumed(this); + } + + @Override + protected void init() { + super.init(); + createActions(); + } + + protected void createActions() { + actionChoosePlatform = ChoosePlatformAction.builder(this); // TODO: + } + + @Override + public DebuggerPlatformMapper getMapper(Trace trace, TraceObject object, long snap) { + /** + * TODO: There's a chance different components fight over the current mapper. However, I + * suspect all nodes in a trace will yield the same offers, so perhaps I should not worry. + */ + DebuggerPlatformMapper mapper; + synchronized (mappersByTrace) { + mapper = mappersByTrace.get(trace); + if (mapper != null && mapper.canInterpret(object, snap)) { + return mapper; + } + mapper = getNewMapper(trace, object, snap); + if (mapper == null) { + return null; + } + mappersByTrace.put(trace, mapper); + } + mapper.addToTrace(snap); + // TODO: Fire a listener + return mapper; + } + + @Override + public DebuggerPlatformMapper getNewMapper(Trace trace, TraceObject object, long snap) { + if (!traceManager.getOpenTraces().contains(trace)) { + throw new IllegalArgumentException("Trace is not opened in this tool"); + } + for (DebuggerPlatformOffer offer : DebuggerPlatformOpinion.queryOpinions(trace, object, + snap, false)) { + return offer.take(tool, trace); + } + return null; + } + + @Override + public DebuggerPlatformMapper chooseMapper(Trace trace, TraceObject object, long snap) { + return Unfinished.TODO(); + } + + @Override + public void setCurrentMapperFor(Trace trace, DebuggerPlatformMapper mapper, long snap) { + if (!traceManager.getOpenTraces().contains(trace)) { + throw new IllegalArgumentException("Trace is not opened in this tool"); + } + synchronized (mappersByTrace) { + mappersByTrace.put(trace, mapper); + } + mapper.addToTrace(snap); + // TODO: Fire a listener + } + + @Override + public void processEvent(PluginEvent event) { + super.processEvent(event); + if (event instanceof TraceClosedPluginEvent) { + TraceClosedPluginEvent ev = (TraceClosedPluginEvent) event; + synchronized (mappersByTrace) { + mappersByTrace.remove(ev.getTrace()); + } + } + } +} diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/workflow/DisassembleAtPcDebuggerBot.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/workflow/DisassembleAtPcDebuggerBot.java index 5370389b50..8130f49b1e 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/workflow/DisassembleAtPcDebuggerBot.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/workflow/DisassembleAtPcDebuggerBot.java @@ -22,12 +22,15 @@ import javax.swing.event.ChangeListener; import com.google.common.collect.Range; -import ghidra.app.cmd.disassemble.DisassembleCommand; +import docking.DockingWindowManager; +import docking.Tool; +import ghidra.app.plugin.core.debug.mapping.DebuggerPlatformMapper; +import ghidra.app.plugin.core.debug.mapping.DisassemblyResult; import ghidra.app.plugin.core.debug.service.workflow.*; -import ghidra.app.services.DebuggerBot; -import ghidra.app.services.DebuggerBotInfo; +import ghidra.app.services.*; import ghidra.async.AsyncDebouncer; import ghidra.async.AsyncTimer; +import ghidra.framework.cmd.BackgroundCommand; import ghidra.framework.model.DomainObject; import ghidra.framework.options.annotation.HelpInfo; import ghidra.framework.plugintool.PluginTool; @@ -43,6 +46,8 @@ import ghidra.trace.model.listing.*; import ghidra.trace.model.memory.*; import ghidra.trace.model.program.TraceProgramView; import ghidra.trace.model.stack.*; +import ghidra.trace.model.target.TraceObject; +import ghidra.trace.model.thread.TraceObjectThread; import ghidra.trace.model.thread.TraceThread; import ghidra.trace.util.*; import ghidra.util.*; @@ -207,14 +212,18 @@ public class DisassembleAtPcDebuggerBot implements DebuggerBot { if (pcVal == null) { return; } - if (range == null || range.contains(pcVal)) { - // NOTE: If non-0 frames are ever used, level should be passed in for injects - disassemble(pcVal, stack.getThread(), snap); + if (range != null && !range.contains(pcVal)) { + return; } + // NOTE: If non-0 frames are ever used, level should be passed in for injects + disassemble(pcVal, stack.getThread(), snap); } protected void disassembleRegPcVal(TraceThread thread, int frameLevel, long pcSnap, long memSnap) { + if (pc == null) { + return; + } TraceData pcUnit = null; try (UndoableTransaction tid = UndoableTransaction.start(trace, "Disassemble: PC is code pointer", true)) { @@ -258,6 +267,14 @@ public class DisassembleAtPcDebuggerBot implements DebuggerBot { return mrent.getKey().getY1(); } + // TODO: TraceManager should instead track focus object, not thread + protected TraceObject getObject(TraceThread thread) { + if (!(thread instanceof TraceObjectThread)) { + return null; + } + return ((TraceObjectThread) thread).getObject(); + } + protected void disassemble(Address start, TraceThread thread, long snap) { Long knownSnap = isKnownRWOrEverKnownRO(start, snap); if (knownSnap == null) { @@ -290,39 +307,39 @@ public class DisassembleAtPcDebuggerBot implements DebuggerBot { // TODO: Should I just keep a variable-snap view around? TraceProgramView view = trace.getFixedProgramView(ks); - DisassembleCommand dis = - new DisassembleCommand(start, disassemblable, true) { - @Override - public boolean applyTo(DomainObject obj, TaskMonitor monitor) { - synchronized (injects) { - try { - if (codeManager.definedUnits().containsAddress(ks, start)) { - return true; - } - for (DisassemblyInject i : injects) { - i.pre(plugin.getTool(), this, view, thread, - new AddressSet(start, start), - disassemblable); - } - boolean result = super.applyTo(obj, monitor); - if (!result) { - Msg.error(this, "Auto-disassembly error: " + getStatusMsg()); - return true; // No pop-up errors - } - for (DisassemblyInject i : injects) { - i.post(plugin.getTool(), view, getDisassembledAddressSet()); - } - return true; - } - catch (Throwable e) { - Msg.error(this, "Auto-disassembly error: " + e); - return true; // No pop-up errors - } + + BackgroundCommand cmd = new BackgroundCommand("Auto-disassemble", true, true, false) { + @Override + public boolean applyTo(DomainObject obj, TaskMonitor monitor) { + try { + DebuggerPlatformService platformService = + findService(DebuggerPlatformService.class); + if (platformService == null) { + reportError("Cannot disassemble without the platform service"); + return true; } + TraceObject object = getObject(thread); + DebuggerPlatformMapper mapper = + platformService.getMapper(trace, object, snap); + if (mapper == null) { + reportError("Cannot disassemble without a platform mapper"); + return true; + } + DisassemblyResult result = mapper.disassemble(thread, object, start, + disassemblable, snap, monitor); + if (result.isAtLeastOne() || result.isSuccess()) { + return true; + } + reportError("Auto-disassembly error: " + result.getErrorMessage()); } - }; + catch (Exception e) { + reportError("Auto-disassembly error: " + e, e); + } + return true; // No pop-up errors + } + }; // TODO: Queue commands so no two for the same trace run concurrently - plugin.getTool().executeBackgroundCommand(dis, view); + plugin.getTool().executeBackgroundCommand(cmd, view); } } @@ -330,6 +347,47 @@ public class DisassembleAtPcDebuggerBot implements DebuggerBot { private final MultiToolTraceListenerManager listeners = new MultiToolTraceListenerManager<>(ForDisassemblyTraceListener::new); + protected void reportError(String error) { + reportError(error, null); + } + + protected void reportError(String error, Throwable t) { + for (PluginTool tool : plugin.getProxyingPluginTools()) { + Msg.error(this, error, t); + tool.setStatusInfo(error, true); + } + } + + /** + * Find the given service among the open tools + * + *

+ * NOTE: This will prefer the service from the most-recently active tool first, only considering + * those with the workflow service proxy enabled. This is important when considering the state + * of said service. + * + * @param the type of the service + * @param cls the class of the service + * @return the service, or null + */ + protected T findService(Class cls) { + Collection proxied = plugin.getProxyingPluginTools(); + List all = DockingWindowManager.getAllDockingWindowManagers(); + Collections.reverse(all); + for (DockingWindowManager dwm : all) { + Tool tool = dwm.getTool(); + if (!proxied.contains(tool)) { + continue; + } + T t = tool.getService(cls); + if (t == null) { + continue; + } + return t; + } + return null; + } + @Override public boolean isEnabled() { return plugin != null; diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/workflow/DisassembleGuestTraceCommand.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/workflow/DisassembleGuestTraceCommand.java new file mode 100644 index 0000000000..e388783a96 --- /dev/null +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/workflow/DisassembleGuestTraceCommand.java @@ -0,0 +1,59 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.plugin.core.debug.workflow; + +import com.google.common.collect.Range; + +import ghidra.program.disassemble.Disassembler; +import ghidra.program.model.address.Address; +import ghidra.program.model.address.AddressSetView; +import ghidra.program.model.lang.InstructionBlock; +import ghidra.program.model.lang.InstructionSet; +import ghidra.program.model.mem.MemBuffer; +import ghidra.trace.model.guest.TraceGuestPlatform; +import ghidra.trace.model.program.TraceProgramView; +import ghidra.util.task.TaskMonitor; + +public class DisassembleGuestTraceCommand extends DisassembleTraceCommand { + protected final TraceGuestPlatform guest; + + public DisassembleGuestTraceCommand(TraceGuestPlatform guest, Address start, + AddressSetView restrictedSet) { + super(start, restrictedSet); + this.guest = guest; + } + + @Override + protected Disassembler getDisassembler(TraceProgramView view, TaskMonitor monitor) { + return Disassembler.getDisassembler(guest.getLanguage(), guest.getAddressFactory(), monitor, + monitor::setMessage); + } + + @Override + protected MemBuffer getBuffer(TraceProgramView view) { + return guest.getMappedMemBuffer(view.getSnap(), guest.mapHostToGuest(start)); + } + + @Override + protected AddressSetView writeBlock(TraceProgramView view, InstructionBlock block) { + InstructionSet set = new InstructionSet(guest.getAddressFactory()); + set.addBlock(block); + return view.getTrace() + .getCodeManager() + .instructions() + .addInstructionSet(Range.atLeast(view.getSnap()), guest, set, true); + } +} diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/workflow/DisassembleTraceCommand.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/workflow/DisassembleTraceCommand.java new file mode 100644 index 0000000000..f0a9481248 --- /dev/null +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/workflow/DisassembleTraceCommand.java @@ -0,0 +1,94 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.plugin.core.debug.workflow; + +import ghidra.framework.cmd.TypedBackgroundCommand; +import ghidra.program.disassemble.Disassembler; +import ghidra.program.model.address.*; +import ghidra.program.model.lang.*; +import ghidra.program.model.mem.MemBuffer; +import ghidra.program.model.mem.MemoryBufferImpl; +import ghidra.program.model.util.CodeUnitInsertionException; +import ghidra.trace.model.guest.TraceGuestPlatform; +import ghidra.trace.model.program.TraceProgramView; +import ghidra.util.MathUtilities; +import ghidra.util.task.TaskMonitor; + +public class DisassembleTraceCommand extends TypedBackgroundCommand { + public static DisassembleTraceCommand create(TraceGuestPlatform guest, Address start, + AddressSetView restrictedSet) { + return guest == null ? new DisassembleTraceCommand(start, restrictedSet) + : new DisassembleGuestTraceCommand(guest, start, restrictedSet); + } + + protected final Address start; + protected final AddressSetView restrictedSet; + + protected RegisterValue initialContext; + private AddressSetView disassembled; + + public DisassembleTraceCommand(Address start, AddressSetView restrictedSet) { + super("Disassemble", true, true, false); + this.start = start; + this.restrictedSet = restrictedSet; + } + + public void setInitialContext(RegisterValue initialContext) { + this.initialContext = initialContext.getBaseRegisterValue(); + } + + protected Disassembler getDisassembler(TraceProgramView view, TaskMonitor monitor) { + return Disassembler.getDisassembler(view, monitor, monitor::setMessage); + } + + protected MemBuffer getBuffer(TraceProgramView view) { + return new MemoryBufferImpl(view.getMemory(), start); + } + + protected int computeLimit() { + AddressRange range = restrictedSet.getRangeContaining(start); + if (range == null) { + return 1; + } + return MathUtilities.unsignedMin(range.getMaxAddress().subtract(start) + 1, + Integer.MAX_VALUE); + } + + protected AddressSetView writeBlock(TraceProgramView view, InstructionBlock block) { + InstructionSet set = new InstructionSet(view.getAddressFactory()); + set.addBlock(block); + try { + return view.getListing().addInstructions(set, true); + } + catch (CodeUnitInsertionException e) { + return new AddressSet(); + } + } + + @Override + public boolean applyToTyped(TraceProgramView view, TaskMonitor monitor) { + Disassembler disassembler = getDisassembler(view, monitor); + MemBuffer buffer = getBuffer(view); + int limit = computeLimit(); + InstructionBlock block = disassembler.pseudoDisassembleBlock(buffer, initialContext, limit); + disassembled = writeBlock(view, block); + return true; + } + + public AddressSetView getDisassembledAddressSet() { + return disassembled; + } +} diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/workflow/DisassemblyInject.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/workflow/DisassemblyInject.java index 6bf587380f..eda4a799f8 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/workflow/DisassemblyInject.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/workflow/DisassemblyInject.java @@ -17,11 +17,10 @@ package ghidra.app.plugin.core.debug.workflow; import java.util.Arrays; -import ghidra.app.cmd.disassemble.DisassembleCommand; import ghidra.framework.plugintool.PluginTool; import ghidra.program.model.address.AddressSetView; +import ghidra.program.model.lang.Language; import ghidra.trace.model.Trace; -import ghidra.trace.model.program.TraceProgramView; import ghidra.trace.model.thread.TraceThread; import ghidra.util.Msg; import ghidra.util.classfinder.ExtensionPoint; @@ -87,13 +86,16 @@ public interface DisassemblyInject extends ExtensionPoint { * * @param tool the tool that will execute the command * @param command the command to be configured, which is about to execute - * @param view the view (trace, snap) which is about to be disassembled + * @param trace the trace whose bytes to disassemble + * @param language the language for the disassembler + * @param snap the snap the snap at which to disassemble * @param thread the thread whose PC is being disassembled * @param startSet the starting address set, usually just the PC * @param restricted the set of disassemblable addresses */ - default void pre(PluginTool tool, DisassembleCommand command, TraceProgramView view, - TraceThread thread, AddressSetView startSet, AddressSetView restricted) { + default void pre(PluginTool tool, DisassembleTraceCommand command, Trace trace, + Language language, long snap, TraceThread thread, AddressSetView startSet, + AddressSetView restricted) { } /** @@ -104,9 +106,10 @@ public interface DisassemblyInject extends ExtensionPoint { * The callback occurs within the command's background thread. * * @param tool the tool that just executed the disassembly command - * @param view the view (trace, snap) which was just disassembled + * @param trace the trace whose bytes were disassembled + * @param snap the snap the snap at which disassembly was performed * @param disassembled the addresses that were actually disassembled */ - default void post(PluginTool tool, TraceProgramView view, AddressSetView disassembled) { + default void post(PluginTool tool, Trace trace, long snap, AddressSetView disassembled) { } } diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/services/DebuggerPlatformService.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/services/DebuggerPlatformService.java new file mode 100644 index 0000000000..7dab95f9b4 --- /dev/null +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/services/DebuggerPlatformService.java @@ -0,0 +1,74 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.services; + +import ghidra.app.plugin.core.debug.mapping.DebuggerPlatformMapper; +import ghidra.trace.model.Trace; +import ghidra.trace.model.target.TraceObject; + +/** + * A service to manage the current mapper for active traces + */ +public interface DebuggerPlatformService { + /** + * Get a mapper applicable to the given object + * + *

+ * If the trace's current mapper is applicable to the object, it will be returned. Otherwise, + * the service will query the opinions for a new mapper, as in + * {@link #getNewMapper(TraceObject)} and set it as the current mapper before returning. If a + * new mapper is set, the trace is also initialized for that mapper. + * + * @param trace the trace + * @param object the object for which a mapper is desired + * @param snap the snap, usually the current snap + * @return the mapper, or null if no offer was provided + */ + DebuggerPlatformMapper getMapper(Trace trace, TraceObject object, + long snap); + + /** + * Get a new mapper for the given object, ignoring the trace's current mapper + * + *

+ * This will not replace the trace's current mapper, nor will it initialize the trace for the + * mapper. + * + * @param trace the trace + * @param object the object for which a mapper is desired + * @param snap the snap, usually the current snap + * @return the mapper, or null if no offer was provided + */ + DebuggerPlatformMapper getNewMapper(Trace trace, TraceObject object, long snap); + + /** + * Display a dialog for the user to manually select a mapper for the given object + * + * @param object the object for which a mapper is desired + * @param snap the snap, usually the current snap + * @return the mapper, or null if the dialog was cancelled + */ + DebuggerPlatformMapper chooseMapper(Trace trace, TraceObject object, long snap); + + /** + * Set the current mapper for the trace and initialize the trace for the mapper + * + * @param trace the trace whose current mapper to set + * @param mapper the mapper + * @param snap the snap for initializing the trace + */ + void setCurrentMapperFor(Trace trace, DebuggerPlatformMapper mapper, long snap); +} diff --git a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/DebuggerManualTest.java b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/DebuggerManualTest.java index 9ee084271b..8976499190 100644 --- a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/DebuggerManualTest.java +++ b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/DebuggerManualTest.java @@ -45,7 +45,7 @@ import ghidra.program.model.data.Undefined4DataType; import ghidra.program.model.lang.Language; import ghidra.program.model.util.CodeUnitInsertionException; import ghidra.trace.database.ToyDBTraceBuilder; -import ghidra.trace.database.language.DBTraceGuestLanguage; +import ghidra.trace.database.guest.DBTraceGuestPlatform; import ghidra.trace.model.memory.TraceMemoryFlag; import ghidra.trace.model.memory.TraceOverlappedRegionException; import ghidra.util.database.UndoableTransaction; @@ -129,12 +129,13 @@ public class DebuggerManualTest extends AbstractGhidraHeadedDebuggerGUITest { tb.trace.getThreadManager().createThread("Thread 2", 4); tb.addData(0, tb.addr(0x4004), Undefined4DataType.dataType, tb.buf(6, 7, 8, 9)); - tb.addInstruction(0, tb.addr(0x4008), tb.language, tb.buf(0xf4, 0)); + tb.addInstruction(0, tb.addr(0x4008), null, tb.buf(0xf4, 0)); Language x86 = getSLEIGH_X86_LANGUAGE(); - DBTraceGuestLanguage guest = tb.trace.getLanguageManager().addGuestLanguage(x86); + DBTraceGuestPlatform guest = + tb.trace.getPlatformManager().addGuestPlatform(x86.getDefaultCompilerSpec()); guest.addMappedRange(tb.addr(0x4000), tb.addr(guest, 0x00400000), 0x1000); - tb.addInstruction(0, tb.addr(0x400a), x86, tb.buf(0x90)); + tb.addInstruction(0, tb.addr(0x400a), guest, tb.buf(0x90)); } waitForSwing(); diff --git a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/mapping/TestDebuggerPlatformOpinion.java b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/mapping/TestDebuggerPlatformOpinion.java new file mode 100644 index 0000000000..4ffa1408df --- /dev/null +++ b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/mapping/TestDebuggerPlatformOpinion.java @@ -0,0 +1,62 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.plugin.core.debug.mapping; + +import java.util.Set; + +import ghidra.framework.plugintool.PluginTool; +import ghidra.program.model.lang.*; +import ghidra.trace.model.Trace; +import ghidra.trace.model.target.TraceObject; + +public class TestDebuggerPlatformOpinion extends AbstractDebuggerPlatformOpinion { + + enum Offers implements DebuggerPlatformOffer { + X86_64 { + @Override + public String getDescription() { + return "Test x86-64"; + } + + @Override + public int getConfidence() { + return 1; + } + + @Override + public CompilerSpec getCompilerSpec() { + return getCompilerSpec(new LanguageID("x86:LE:64:default"), null); + } + + @Override + public DebuggerPlatformMapper take(PluginTool tool, Trace trace) { + return new DefaultDebuggerPlatformMapper(tool, trace, getCompilerSpec()); + } + }; + } + + @Override + protected Set getOffers(TraceObject object, long snap, + TraceObject env, String debugger, String arch, String os, Endian endian) { + if (!"test".equals(debugger)) { + return Set.of(); + } + if (!"x86-64".equals(arch)) { + return Set.of(); + } + return Set.of(Offers.X86_64); + } +} diff --git a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/platform/arm/GdbArmDebuggerMappingOpinionTest.java b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/platform/gdb/GdbArmDebuggerMappingOpinionTest.java similarity index 95% rename from Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/platform/arm/GdbArmDebuggerMappingOpinionTest.java rename to Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/platform/gdb/GdbArmDebuggerMappingOpinionTest.java index a216515b1a..af7a580cc1 100644 --- a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/platform/arm/GdbArmDebuggerMappingOpinionTest.java +++ b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/platform/gdb/GdbArmDebuggerMappingOpinionTest.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.plugin.core.debug.platform.arm; +package ghidra.app.plugin.core.debug.platform.gdb; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -25,8 +25,8 @@ import org.junit.Test; import ghidra.app.plugin.core.debug.mapping.DebuggerMappingOffer; import ghidra.app.plugin.core.debug.mapping.DebuggerMappingOpinion; -import ghidra.app.plugin.core.debug.platform.arm.GdbArmDebuggerMappingOpinion.GdbAArch64Offer; -import ghidra.app.plugin.core.debug.platform.arm.GdbArmDebuggerMappingOpinion.GdbArmOffer; +import ghidra.app.plugin.core.debug.platform.gdb.GdbArmDebuggerMappingOpinion.GdbAArch64Offer; +import ghidra.app.plugin.core.debug.platform.gdb.GdbArmDebuggerMappingOpinion.GdbArmOffer; import ghidra.dbg.model.TestDebuggerObjectModel; import ghidra.dbg.model.TestTargetProcess; import ghidra.program.model.lang.LanguageID; diff --git a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/workflow/DisassembleAtPcDebuggerBotTest.java b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/workflow/DisassembleAtPcDebuggerBotTest.java new file mode 100644 index 0000000000..1f54d312df --- /dev/null +++ b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/workflow/DisassembleAtPcDebuggerBotTest.java @@ -0,0 +1,168 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.plugin.core.debug.workflow; + +import static org.junit.Assert.*; + +import java.util.Set; +import java.util.stream.Collectors; + +import org.junit.Before; +import org.junit.Test; + +import com.google.common.collect.Range; + +import ghidra.app.plugin.core.debug.gui.AbstractGhidraHeadedDebuggerGUITest; +import ghidra.app.plugin.core.debug.gui.listing.DebuggerListingPlugin; +import ghidra.app.plugin.core.debug.service.platform.DebuggerPlatformServicePlugin; +import ghidra.app.plugin.core.debug.service.workflow.DebuggerWorkflowServiceProxyPlugin; +import ghidra.app.services.DebuggerBot; +import ghidra.app.services.DebuggerWorkflowService; +import ghidra.dbg.target.TargetEnvironment; +import ghidra.dbg.target.schema.SchemaContext; +import ghidra.dbg.target.schema.TargetObjectSchema.SchemaName; +import ghidra.dbg.target.schema.XmlSchemaContext; +import ghidra.program.model.listing.Instruction; +import ghidra.trace.database.listing.DBTraceInstructionsMemoryView; +import ghidra.trace.database.memory.DBTraceMemoryManager; +import ghidra.trace.database.target.DBTraceObject; +import ghidra.trace.database.target.DBTraceObjectManager; +import ghidra.trace.model.memory.TraceMemoryFlag; +import ghidra.trace.model.memory.TraceObjectMemoryRegion; +import ghidra.trace.model.stack.TraceObjectStackFrame; +import ghidra.trace.model.target.TraceObject.ConflictResolution; +import ghidra.trace.model.target.TraceObjectKeyPath; +import ghidra.trace.model.thread.TraceObjectThread; +import ghidra.util.database.UndoableTransaction; + +public class DisassembleAtPcDebuggerBotTest extends AbstractGhidraHeadedDebuggerGUITest { + protected SchemaContext ctx; + + @Before + public void setUpDisassembleAtPcTest() throws Exception { + ctx = XmlSchemaContext.deserialize("" + // + "" + // + " " + // + " " + // + " " + // + " " + // + " " + // + " " + // + " " + // + " " + // + " " + // + " " + // + " " + // + " " + // + " " + // + " " + // + " " + // + " " + // + " " + // + " " + // + " " + // + " " + // + " " + // + " " + // + " " + // + " " + // + " " + // + " " + // + " " + // + " " + // + " " + // + " " + // + " " + // + " " + // + " " + // + " " + // + " " + // + " " + // + " " + // + ""); + + DebuggerWorkflowService workflowService = + addPlugin(tool, DebuggerWorkflowServiceProxyPlugin.class); + addPlugin(tool, DebuggerListingPlugin.class); + addPlugin(tool, DebuggerPlatformServicePlugin.class); + + Set disBot = workflowService.getAllBots() + .stream() + .filter(b -> b instanceof DisassembleAtPcDebuggerBot) + .collect(Collectors.toSet()); + assertEquals(1, disBot.size()); + workflowService.enableBots(disBot); + } + + protected void assertX86Nop(Instruction instruction) { + assertNotNull(instruction); + assertEquals("NOP", instruction.getMnemonicString()); + } + + @Test + public void testDisassembleX8664() throws Throwable { + createAndOpenTrace("DATA:BE:64:default"); + + DBTraceObjectManager objects = tb.trace.getObjectManager(); + try (UndoableTransaction tid = tb.startTransaction()) { + objects.createRootObject(ctx.getSchema(new SchemaName("Session"))); + DBTraceObject env = + objects.createObject(TraceObjectKeyPath.parse("Targets[0].Environment")); + assertEquals(ctx.getSchema(new SchemaName("Environment")), env.getTargetSchema()); + Range zeroOn = Range.atLeast(0L); + env.insert(zeroOn, ConflictResolution.DENY); + env.setAttribute(zeroOn, TargetEnvironment.DEBUGGER_ATTRIBUTE_NAME, "test"); + env.setAttribute(zeroOn, TargetEnvironment.ARCH_ATTRIBUTE_NAME, "x86-64"); + + DBTraceObject objBinText = + objects.createObject(TraceObjectKeyPath.parse("Targets[0].Memory[bin:.text]")); + TraceObjectMemoryRegion binText = + objBinText.queryInterface(TraceObjectMemoryRegion.class); + binText.addFlags(zeroOn, Set.of(TraceMemoryFlag.EXECUTE)); + binText.setRange(zeroOn, tb.range(0x00400000, 0x0040ffff)); + // TODO: Why doesn't setRange work after insert? + objBinText.insert(zeroOn, ConflictResolution.DENY); + + DBTraceObject objFrame = + objects.createObject(TraceObjectKeyPath.parse("Targets[0].Threads[0].Stack[0]")); + objFrame.insert(zeroOn, ConflictResolution.DENY); + TraceObjectStackFrame frame = objFrame.queryInterface(TraceObjectStackFrame.class); + frame.setProgramCounter(zeroOn, tb.addr(0x00400000)); + + DBTraceMemoryManager memory = tb.trace.getMemoryManager(); + memory.putBytes(0, tb.addr(0x00400000), tb.buf(0x90, 0x90, 0x90)); + } + TraceObjectThread thread = + objects.getObjectByCanonicalPath(TraceObjectKeyPath.parse("Targets[0].Threads[0]")) + .queryInterface(TraceObjectThread.class); + traceManager.activateThread(thread); + + getSLEIGH_X86_64_LANGUAGE(); // So that the load isn't charged against the time-out + + waitForPass(() -> { + DBTraceInstructionsMemoryView instructions = tb.trace.getCodeManager().instructions(); + assertX86Nop(instructions.getAt(0, tb.addr(0x00400000))); + assertX86Nop(instructions.getAt(0, tb.addr(0x00400001))); + assertX86Nop(instructions.getAt(0, tb.addr(0x00400002))); + assertNull(instructions.getAt(0, tb.addr(0x00400003))); + }); + } +} 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 8fd0e18bb2..7431972f8f 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 @@ -38,7 +38,7 @@ import ghidra.trace.database.breakpoint.DBTraceBreakpointManager; import ghidra.trace.database.context.DBTraceRegisterContextManager; import ghidra.trace.database.data.DBTraceDataSettingsAdapter; import ghidra.trace.database.data.DBTraceDataTypeManager; -import ghidra.trace.database.language.DBTraceLanguageManager; +import ghidra.trace.database.guest.DBTracePlatformManager; import ghidra.trace.database.listing.DBTraceCodeManager; import ghidra.trace.database.listing.DBTraceCommentAdapter; import ghidra.trace.database.memory.DBTraceMemoryManager; @@ -101,7 +101,7 @@ public class DBTrace extends DBCachedDomainObjectAdapter implements Trace, Trace @DependentService protected DBTraceEquateManager equateManager; @DependentService - protected DBTraceLanguageManager languageManager; + protected DBTracePlatformManager platformManager; @DependentService protected DBTraceMemoryManager memoryManager; @DependentService @@ -283,12 +283,12 @@ public class DBTrace extends DBCachedDomainObjectAdapter implements Trace, Trace @DependentService protected DBTraceCodeManager createCodeManager(DBTraceThreadManager threadManager, - DBTraceLanguageManager languageManager, DBTraceDataTypeManager dataTypeManager, + DBTracePlatformManager platformManager, DBTraceDataTypeManager dataTypeManager, DBTraceOverlaySpaceAdapter overlayAdapter, DBTraceReferenceManager referenceManager) throws CancelledException, IOException { return createTraceManager("Code Manager", (openMode, monitor) -> new DBTraceCodeManager(dbh, openMode, rwLock, monitor, - baseLanguage, this, threadManager, languageManager, dataTypeManager, overlayAdapter, + baseLanguage, this, threadManager, platformManager, dataTypeManager, overlayAdapter, referenceManager)); } @@ -325,11 +325,11 @@ public class DBTrace extends DBCachedDomainObjectAdapter implements Trace, Trace } @DependentService - protected DBTraceLanguageManager createLanguageManager() + protected DBTracePlatformManager createPlatformManager() throws CancelledException, IOException { return createTraceManager("Language Manager", - (openMode, monitor) -> new DBTraceLanguageManager(dbh, openMode, rwLock, monitor, - baseLanguage, this)); + (openMode, monitor) -> new DBTracePlatformManager(dbh, openMode, rwLock, monitor, + baseCompilerSpec, this)); } @DependentService @@ -373,11 +373,11 @@ public class DBTrace extends DBCachedDomainObjectAdapter implements Trace, Trace @DependentService protected DBTraceRegisterContextManager createRegisterContextManager( - DBTraceThreadManager threadManager, DBTraceLanguageManager languageManager) + DBTraceThreadManager threadManager, DBTracePlatformManager platformManager) throws CancelledException, IOException { return createTraceManager("Context Manager", (openMode, monitor) -> new DBTraceRegisterContextManager(dbh, openMode, rwLock, monitor, - baseLanguage, this, threadManager, languageManager)); + baseLanguage, this, threadManager, platformManager)); } @DependentService @@ -497,8 +497,8 @@ public class DBTrace extends DBCachedDomainObjectAdapter implements Trace, Trace } @Override - public DBTraceLanguageManager getLanguageManager() { - return languageManager; + public DBTracePlatformManager getPlatformManager() { + return platformManager; } @Override diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/DBTraceUtils.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/DBTraceUtils.java index 2fdbfd661d..980bcf8cc8 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/DBTraceUtils.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/DBTraceUtils.java @@ -29,6 +29,7 @@ import com.google.common.collect.*; import db.*; import ghidra.program.model.address.*; +import ghidra.program.model.lang.CompilerSpecID; import ghidra.program.model.lang.LanguageID; import ghidra.program.model.symbol.RefType; import ghidra.program.model.symbol.RefTypeFactory; @@ -161,6 +162,43 @@ public enum DBTraceUtils { } } + public static class CompilerSpecIDDBFieldCodec + extends AbstractDBFieldCodec { + + public CompilerSpecIDDBFieldCodec(Class objectType, Field field, int column) { + super(CompilerSpecID.class, objectType, StringField.class, field, column); + } + + @Override + public void store(CompilerSpecID value, StringField f) { + f.setString(value == null ? null : value.getIdAsString()); + } + + @Override + protected void doStore(OT obj, DBRecord record) + throws IllegalArgumentException, IllegalAccessException { + CompilerSpecID id = getValue(obj); + if (id == null) { + record.setString(column, null); + } + else { + record.setString(column, id.getIdAsString()); + } + } + + @Override + protected void doLoad(OT obj, DBRecord record) + throws IllegalArgumentException, IllegalAccessException { + String id = record.getString(column); + if (id == null) { + setValue(obj, null); + } + else { + setValue(obj, new CompilerSpecID(id)); + } + } + } + public abstract static class AbstractOffsetSnapDBFieldCodec extends AbstractDBFieldCodec { diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/context/DBTraceRegisterContextManager.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/context/DBTraceRegisterContextManager.java index 428e96267d..32fcb289e6 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/context/DBTraceRegisterContextManager.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/context/DBTraceRegisterContextManager.java @@ -31,7 +31,7 @@ import ghidra.program.model.lang.*; import ghidra.program.model.listing.ProgramContext; import ghidra.program.util.ProgramContextImpl; import ghidra.trace.database.DBTrace; -import ghidra.trace.database.language.DBTraceLanguageManager; +import ghidra.trace.database.guest.DBTracePlatformManager; import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree; import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree.AbstractDBTraceAddressSnapRangePropertyMapData; import ghidra.trace.database.space.AbstractDBTraceSpaceBasedManager; @@ -83,13 +83,13 @@ public class DBTraceRegisterContextManager extends } } - protected final DBTraceLanguageManager languageManager; + protected final DBTracePlatformManager languageManager; protected final Map defaultContexts = new HashMap<>(); public DBTraceRegisterContextManager(DBHandle dbh, DBOpenMode openMode, ReadWriteLock lock, TaskMonitor monitor, Language baseLanguage, DBTrace trace, - DBTraceThreadManager threadManager, DBTraceLanguageManager languageManager) + DBTraceThreadManager threadManager, DBTracePlatformManager languageManager) throws VersionException, IOException { super(NAME, dbh, openMode, lock, monitor, baseLanguage, trace, threadManager); this.languageManager = languageManager; diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/context/DBTraceRegisterContextSpace.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/context/DBTraceRegisterContextSpace.java index 6fe1b74a13..575c3aba71 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/context/DBTraceRegisterContextSpace.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/context/DBTraceRegisterContextSpace.java @@ -32,6 +32,7 @@ import ghidra.program.model.lang.*; import ghidra.trace.database.DBTrace; import ghidra.trace.database.DBTraceUtils; import ghidra.trace.database.context.DBTraceRegisterContextManager.DBTraceRegisterContextEntry; +import ghidra.trace.database.guest.DBTraceGuestPlatform.DBTraceGuestLanguage; import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapAddressSetView; import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapSpace; import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree.TraceAddressSnapRangeQuery; @@ -70,8 +71,8 @@ public class DBTraceRegisterContextSpace implements TraceRegisterContextSpace, D super(store, record); } - void set(int langKey, Register register) { - this.langKey = langKey; + void set(DBTraceGuestLanguage guest, Register register) { + this.langKey = (int) (guest == null ? -1 : guest.getKey()); this.register = register.getName(); update(LANGUAGE_COLUMN, REGISTER_COLUMN); } @@ -110,7 +111,8 @@ public class DBTraceRegisterContextSpace implements TraceRegisterContextSpace, D protected void loadRegisterValueMaps() throws VersionException { for (DBTraceRegisterEntry ent : registerStore.asMap().values()) { - Language language = manager.languageManager.getLanguageByKey(ent.langKey); + DBTraceGuestLanguage guest = manager.languageManager.getLanguageByKey(ent.langKey); + Language language = guest == null ? manager.getBaseLanguage() : guest.getLanguage(); Register register = language.getRegister(ent.register); ImmutablePair pair = new ImmutablePair<>(language, register); if (ent.map == null) { @@ -161,12 +163,12 @@ public class DBTraceRegisterContextSpace implements TraceRegisterContextSpace, D protected DBTraceAddressSnapRangePropertyMapSpace getRegisterValueMap( Language language, Register register, boolean createIfAbsent) { ImmutablePair pair = new ImmutablePair<>(language, register); - int langKey = manager.languageManager.getKeyForLanguage(language); + DBTraceGuestLanguage guest = manager.languageManager.getLanguageByLanguage(language); if (createIfAbsent) { return registerValueMaps.computeIfAbsent(pair, t -> { try { DBTraceRegisterEntry ent = registerStore.create(); - ent.set(langKey, register); + ent.set(guest, register); return createRegisterValueMap(t); } catch (VersionException e) { diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/language/DBTraceGuestLanguage.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/guest/DBTraceGuestPlatform.java similarity index 54% rename from Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/language/DBTraceGuestLanguage.java rename to Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/guest/DBTraceGuestPlatform.java index 70ee216a31..338a05cbcf 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/language/DBTraceGuestLanguage.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/guest/DBTraceGuestPlatform.java @@ -13,8 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.trace.database.language; +package ghidra.trace.database.guest; +import java.io.IOException; import java.util.*; import java.util.Map.Entry; @@ -22,13 +23,17 @@ import com.google.common.collect.Range; import db.DBRecord; import ghidra.app.util.PseudoInstruction; +import ghidra.lifecycle.Internal; import ghidra.program.model.address.*; import ghidra.program.model.lang.*; import ghidra.program.model.listing.Instruction; import ghidra.program.model.mem.DumbMemBufferImpl; 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.model.language.TraceGuestLanguage; +import ghidra.trace.model.Trace; +import ghidra.trace.model.guest.TraceGuestPlatform; import ghidra.util.LockHold; import ghidra.util.database.*; import ghidra.util.database.annot.*; @@ -37,59 +42,129 @@ import ghidra.util.exception.VersionException; import ghidra.util.task.TaskMonitor; @DBAnnotatedObjectInfo(version = 0) -public class DBTraceGuestLanguage extends DBAnnotatedObject implements TraceGuestLanguage { - public static final String TABLE_NAME = "Languages"; +public class DBTraceGuestPlatform extends DBAnnotatedObject implements TraceGuestPlatform { + public static final String TABLE_NAME = "Platforms"; - static final String LANGID_COLUMN_NAME = "ID"; - static final String VERSION_COLUMN_NAME = "Version"; - static final String MINOR_VERSION_COLUMN_NAME = "MinorVersion"; + @DBAnnotatedObjectInfo(version = 0) + public static class DBTraceGuestLanguage extends DBAnnotatedObject { + public static final String TABLE_NAME = "Languages"; - @DBAnnotatedColumn(LANGID_COLUMN_NAME) - static DBObjectColumn LANGID_COLUMN; - @DBAnnotatedColumn(VERSION_COLUMN_NAME) - static DBObjectColumn VERSION_COLUMN; - @DBAnnotatedColumn(MINOR_VERSION_COLUMN_NAME) - static DBObjectColumn MINOR_VERSION_COLUMN; + static final String LANGID_COLUMN_NAME = "Lang"; + static final String VERSION_COLUMN_NAME = "Version"; + static final String MINOR_VERSION_COLUMN_NAME = "MinorVersion"; - @DBAnnotatedField(column = LANGID_COLUMN_NAME, codec = LanguageIDDBFieldCodec.class) - private LanguageID langID; - @DBAnnotatedField(column = VERSION_COLUMN_NAME) - private int version; - @DBAnnotatedField(column = MINOR_VERSION_COLUMN_NAME) - private int minorVersion; + @DBAnnotatedColumn(LANGID_COLUMN_NAME) + static DBObjectColumn LANGID_COLUMN; + @DBAnnotatedColumn(VERSION_COLUMN_NAME) + static DBObjectColumn VERSION_COLUMN; + @DBAnnotatedColumn(MINOR_VERSION_COLUMN_NAME) + static DBObjectColumn MINOR_VERSION_COLUMN; - private final DBTraceLanguageManager manager; + @DBAnnotatedField(column = LANGID_COLUMN_NAME, codec = LanguageIDDBFieldCodec.class) + private LanguageID langID; + @DBAnnotatedField(column = VERSION_COLUMN_NAME) + private int version; + @DBAnnotatedField(column = MINOR_VERSION_COLUMN_NAME) + private int minorVersion; - private Language guestLanguage; + private Language language; - protected final NavigableMap rangesByHostAddress = + public DBTraceGuestLanguage(DBCachedObjectStore store, DBRecord record) { + super(store, record); + } + + @Override + protected void fresh(boolean created) throws IOException { + super.fresh(created); + if (created) { + return; + } + LanguageService langServ = DefaultLanguageService.getLanguageService(); + language = langServ.getLanguage(langID); + if (version != language.getVersion() || minorVersion != language.getMinorVersion()) { + throw new IOException(new VersionException()); // TODO Upgrade + } + } + + void set(Language language) { + this.langID = language.getLanguageID(); + this.version = language.getVersion(); + this.minorVersion = language.getMinorVersion(); + update(LANGID_COLUMN, VERSION_COLUMN, MINOR_VERSION_COLUMN); + this.language = language; + } + + public Language getLanguage() { + return language; + } + } + + static final String LANGKEY_COLUMN_NAME = "Lang"; + static final String CSPECID_COLUMN_NAME = "CSpec"; + + @DBAnnotatedColumn(LANGKEY_COLUMN_NAME) + static DBObjectColumn LANGKEY_COLUMN; + @DBAnnotatedColumn(CSPECID_COLUMN_NAME) + static DBObjectColumn CSPECID_COLUMN; + + @DBAnnotatedField(column = LANGKEY_COLUMN_NAME) + private int langKey; + @DBAnnotatedField(column = CSPECID_COLUMN_NAME, codec = CompilerSpecIDDBFieldCodec.class) + private CompilerSpecID cSpecID; + + private DBTraceGuestLanguage languageEntry; + private CompilerSpec compilerSpec; + + final DBTracePlatformManager manager; + protected final NavigableMap rangesByHostAddress = new TreeMap<>(); protected final AddressSet hostAddressSet = new AddressSet(); - protected final NavigableMap rangesByGuestAddress = + protected final NavigableMap rangesByGuestAddress = new TreeMap<>(); protected final AddressSet guestAddressSet = new AddressSet(); - public DBTraceGuestLanguage(DBTraceLanguageManager manager, DBCachedObjectStore store, + public DBTraceGuestPlatform(DBTracePlatformManager manager, DBCachedObjectStore store, DBRecord record) { super(store, record); this.manager = manager; } - void setLanguage(Language language) { - this.guestLanguage = language; - this.langID = language.getLanguageID(); - this.version = language.getVersion(); - this.minorVersion = language.getMinorVersion(); - update(LANGID_COLUMN, VERSION_COLUMN, MINOR_VERSION_COLUMN); + void set(CompilerSpec compilerSpec) { + this.languageEntry = manager.getOrCreateLanguage(compilerSpec.getLanguage()); + this.langKey = (int) (languageEntry == null ? -1 : languageEntry.getKey()); + this.cSpecID = compilerSpec.getCompilerSpecID(); + update(LANGKEY_COLUMN, CSPECID_COLUMN); + this.compilerSpec = compilerSpec; } - protected void deleteMappedRange(DBTraceGuestLanguageMappedRange range, TaskMonitor monitor) + @Override + protected void fresh(boolean created) throws IOException { + super.fresh(created); + if (created) { + return; + } + this.languageEntry = manager.getLanguageByKey(langKey); + if (languageEntry == null && langKey != -1) { + throw new IOException("Platform table is corrupt. Missing language " + langKey); + } + compilerSpec = getLanguage().getCompilerSpecByID(cSpecID); + if (compilerSpec == null) { + throw new IOException( + "Platform table is corrupt. Invalid compiler spec " + compilerSpec); + } + } + + @Override + public Trace getTrace() { + return manager.trace; + } + + protected void deleteMappedRange(DBTraceGuestPlatformMappedRange range, TaskMonitor monitor) throws CancelledException { try (LockHold hold = LockHold.lock(manager.lock.writeLock())) { manager.trace.getCodeManager() - .clearLanguage(Range.all(), range.getHostRange(), - (int) getKey(), monitor); + .clearPlatform(Range.all(), range.getHostRange(), this, monitor); manager.rangeMappingStore.delete(range); AddressRange hostRange = range.getHostRange(); AddressRange guestRange = range.getGuestRange(); @@ -100,41 +175,43 @@ public class DBTraceGuestLanguage extends DBAnnotatedObject implements TraceGues } } - protected void doGetLanguage(LanguageService langServ) - throws LanguageNotFoundException, VersionException { - this.guestLanguage = langServ.getLanguage(langID); - if (version != guestLanguage.getVersion() || - minorVersion != guestLanguage.getMinorVersion()) { - throw new VersionException(); // TODO Upgrade - } + @Internal + public DBTraceGuestLanguage getLanguageEntry() { + return languageEntry; } @Override public Language getLanguage() { - return guestLanguage; + return languageEntry == null ? manager.baseLanguage : languageEntry.getLanguage(); + } + + @Override + public CompilerSpec getCompilerSpec() { + return compilerSpec; } @Override public void delete(TaskMonitor monitor) throws CancelledException { - manager.deleteGuestLanguage(this, monitor); + manager.deleteGuestPlatform(this, monitor); + // TODO: Delete language once no platform uses it? } @Override - public DBTraceGuestLanguageMappedRange addMappedRange(Address hostStart, Address guestStart, + public DBTraceGuestPlatformMappedRange addMappedRange(Address hostStart, Address guestStart, long length) throws AddressOverflowException { try (LockHold hold = LockHold.lock(manager.lock.writeLock())) { - Address hostEnd = hostStart.addNoWrap(length - 1); + Address hostEnd = hostStart.addWrap(length - 1); if (hostAddressSet.intersects(hostStart, hostEnd)) { // TODO: Check for compatibility and extend? throw new IllegalArgumentException( "Range overlaps existing host mapped range(s) for this guest language"); } - Address guestEnd = guestStart.addNoWrap(length - 1); + Address guestEnd = guestStart.addWrap(length - 1); if (guestAddressSet.intersects(guestStart, guestEnd)) { throw new IllegalArgumentException("Range overlaps existing guest mapped range(s)"); } - DBTraceGuestLanguageMappedRange mappedRange = manager.rangeMappingStore.create(); - mappedRange.set(hostStart, guestLanguage, guestStart, length); + DBTraceGuestPlatformMappedRange mappedRange = manager.rangeMappingStore.create(); + mappedRange.set(hostStart, this, guestStart, length); rangesByHostAddress.put(hostStart, mappedRange); rangesByGuestAddress.put(guestStart, mappedRange); hostAddressSet.add(mappedRange.getHostRange()); @@ -156,7 +233,7 @@ public class DBTraceGuestLanguage extends DBAnnotatedObject implements TraceGues @Override public Address mapHostToGuest(Address hostAddress) { try (LockHold hold = LockHold.lock(manager.lock.readLock())) { - Entry floorEntry = + Entry floorEntry = rangesByHostAddress.floorEntry(hostAddress); if (floorEntry == null) { return null; @@ -168,7 +245,7 @@ public class DBTraceGuestLanguage extends DBAnnotatedObject implements TraceGues @Override public Address mapGuestToHost(Address guestAddress) { try (LockHold hold = LockHold.lock(manager.lock.readLock())) { - Entry floorEntry = + Entry floorEntry = rangesByGuestAddress.floorEntry(guestAddress); if (floorEntry == null) { return null; @@ -186,12 +263,12 @@ public class DBTraceGuestLanguage extends DBAnnotatedObject implements TraceGues */ public Address mapGuestToHost(Address guestMin, Address guestMax) { try (LockHold hold = LockHold.lock(manager.lock.readLock())) { - Entry floorEntry = + Entry floorEntry = rangesByGuestAddress.floorEntry(guestMin); if (floorEntry == null) { return null; } - DBTraceGuestLanguageMappedRange range = floorEntry.getValue(); + DBTraceGuestPlatformMappedRange range = floorEntry.getValue(); if (!range.getGuestRange().contains(guestMax)) { return null; } @@ -204,7 +281,7 @@ public class DBTraceGuestLanguage extends DBAnnotatedObject implements TraceGues /*return new DBTraceGuestLanguageMappedMemBuffer(manager.trace.getMemoryManager(), this, snap, guestAddress);*/ return new DumbMemBufferImpl( - new DBTraceGuestLanguageMappedMemory(manager.trace.getMemoryManager(), this, snap), + new DBTraceGuestPlatformMappedMemory(manager.trace.getMemoryManager(), this, snap), guestAddress); } @@ -212,7 +289,7 @@ public class DBTraceGuestLanguage extends DBAnnotatedObject implements TraceGues public InstructionSet mapGuestInstructionAddressesToHost(InstructionSet instructionSet) { try (LockHold hold = LockHold.lock(manager.lock.readLock())) { Map blocksByNext = new HashMap<>(); - InstructionSet mappedSet = new InstructionSet(guestLanguage.getAddressFactory()); + InstructionSet mappedSet = new InstructionSet(getAddressFactory()); for (InstructionBlock block : instructionSet) { for (Instruction instruction : block) { Address hostAddr = diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/language/DBTraceGuestLanguageMappedMemory.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/guest/DBTraceGuestPlatformMappedMemory.java similarity index 96% rename from Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/language/DBTraceGuestLanguageMappedMemory.java rename to Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/guest/DBTraceGuestPlatformMappedMemory.java index 3d305012ee..7ca211644b 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/language/DBTraceGuestLanguageMappedMemory.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/guest/DBTraceGuestPlatformMappedMemory.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.trace.database.language; +package ghidra.trace.database.guest; import static ghidra.lifecycle.Unfinished.TODO; @@ -32,6 +32,7 @@ import ghidra.program.model.listing.Program; import ghidra.program.model.mem.*; import ghidra.trace.database.memory.DBTraceMemoryManager; import ghidra.trace.database.memory.DBTraceMemorySpace; +import ghidra.util.MathUtilities; import ghidra.util.exception.CancelledException; import ghidra.util.exception.NotFoundException; import ghidra.util.task.TaskMonitor; @@ -39,15 +40,16 @@ import ghidra.util.task.TaskMonitor; /** * TODO: Document me * + *

* Note this is the bare minimum to support {@link DumbMemBufferImpl} */ -public class DBTraceGuestLanguageMappedMemory implements Memory { +public class DBTraceGuestPlatformMappedMemory implements Memory { protected final DBTraceMemoryManager manager; - protected final DBTraceGuestLanguage guest; + protected final DBTraceGuestPlatform guest; protected final long snap; - public DBTraceGuestLanguageMappedMemory(DBTraceMemoryManager manager, - DBTraceGuestLanguage guest, long snap) { + public DBTraceGuestPlatformMappedMemory(DBTraceMemoryManager manager, + DBTraceGuestPlatform guest, long snap) { this.manager = manager; this.guest = guest; this.snap = snap; @@ -358,17 +360,17 @@ public class DBTraceGuestLanguageMappedMemory implements Memory { while (buffer.hasRemaining()) { int offset = buffer.position() - startPos; Address guestCur = guestStart.add(offset); - Entry floorEntry = + Entry floorEntry = guest.rangesByGuestAddress.floorEntry(guestCur); if (floorEntry == null) { return offset; } - DBTraceGuestLanguageMappedRange range = floorEntry.getValue(); + DBTraceGuestPlatformMappedRange range = floorEntry.getValue(); Address hostCur = range.mapGuestToHost(guestCur); if (hostCur == null) { return offset; } - int lenToRead = (int) Math.min(buffer.remaining(), + int lenToRead = MathUtilities.unsignedMin(buffer.remaining(), range.getGuestRange().getMaxAddress().subtract(guestStart) + 1); DBTraceMemorySpace hostSpace = manager.getMemorySpace(hostCur.getAddressSpace(), false); if (hostSpace == null) { diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/language/DBTraceGuestLanguageMappedRange.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/guest/DBTraceGuestPlatformMappedRange.java similarity index 80% rename from Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/language/DBTraceGuestLanguageMappedRange.java rename to Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/guest/DBTraceGuestPlatformMappedRange.java index 2386b499c4..a53565f040 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/language/DBTraceGuestLanguageMappedRange.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/guest/DBTraceGuestPlatformMappedRange.java @@ -13,22 +13,23 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.trace.database.language; +package ghidra.trace.database.guest; import java.io.IOException; import db.DBRecord; import ghidra.program.model.address.*; +import ghidra.program.model.lang.CompilerSpec; import ghidra.program.model.lang.Language; -import ghidra.trace.model.language.TraceGuestLanguageMappedRange; +import ghidra.trace.model.guest.TraceGuestPlatformMappedRange; import ghidra.util.database.*; import ghidra.util.database.annot.*; import ghidra.util.exception.CancelledException; import ghidra.util.task.TaskMonitor; @DBAnnotatedObjectInfo(version = 0) -public class DBTraceGuestLanguageMappedRange extends DBAnnotatedObject - implements TraceGuestLanguageMappedRange { +public class DBTraceGuestPlatformMappedRange extends DBAnnotatedObject + implements TraceGuestPlatformMappedRange { public static final String TABLE_NAME = "LanguageMappings"; static final String HOST_SPACE_COLUMN_NAME = "HostSpace"; @@ -56,7 +57,7 @@ public class DBTraceGuestLanguageMappedRange extends DBAnnotatedObject @DBAnnotatedField(column = HOST_OFFSET_COLUMN_NAME) private long hostOffset; @DBAnnotatedField(column = GUEST_LANGUAGE_COLUMN_NAME) - int guestLangKey; + int guestPlatformKey; @DBAnnotatedField(column = GUEST_SPACE_COLUMN_NAME) private int guestSpace; @DBAnnotatedField(column = GUEST_OFFSET_COLUMN_NAME) @@ -64,13 +65,13 @@ public class DBTraceGuestLanguageMappedRange extends DBAnnotatedObject @DBAnnotatedField(column = LENGTH_COLUMN_NAME) private long length; - private DBTraceLanguageManager manager; + private DBTracePlatformManager manager; private AddressRangeImpl hostRange; - private Language guestLanguage; + private DBTraceGuestPlatform guestPlatform; private AddressRangeImpl guestRange; - public DBTraceGuestLanguageMappedRange(DBTraceLanguageManager manager, DBCachedObjectStore s, + public DBTraceGuestPlatformMappedRange(DBTracePlatformManager manager, DBCachedObjectStore s, DBRecord r) { super(s, r); this.manager = manager; @@ -88,9 +89,9 @@ public class DBTraceGuestLanguageMappedRange extends DBAnnotatedObject Address hostEnd = hostStart.addNoWrap(length - 1); this.hostRange = new AddressRangeImpl(hostStart, hostEnd); - this.guestLanguage = manager.getLanguageByKey(guestLangKey); + this.guestPlatform = manager.getPlatformByKey(guestPlatformKey); Address guestStart = - guestLanguage.getAddressFactory().getAddress(guestSpace, guestOffset); + guestPlatform.getAddressFactory().getAddress(guestSpace, guestOffset); Address guestEnd = guestStart.addNoWrap(length - 1); this.guestRange = new AddressRangeImpl(guestStart, guestEnd); } @@ -99,19 +100,21 @@ public class DBTraceGuestLanguageMappedRange extends DBAnnotatedObject } } - void set(Address hostStart, Language guestLanguage, Address guestStart, long length) { - this.hostRange = new AddressRangeImpl(hostStart, hostStart.add(length - 1)); - this.guestLanguage = guestLanguage; - this.guestRange = new AddressRangeImpl(guestStart, guestStart.add(length - 1)); - + void set(Address hostStart, DBTraceGuestPlatform guestPlatform, Address guestStart, + long length) { this.hostSpace = hostStart.getAddressSpace().getSpaceID(); this.hostOffset = hostStart.getOffset(); - this.guestLangKey = manager.getKeyForLanguage(guestLanguage); + this.guestPlatformKey = (int) guestPlatform.getKey(); this.guestSpace = guestStart.getAddressSpace().getSpaceID(); this.guestOffset = guestStart.getOffset(); this.length = length; update(HOST_SPACE_COLUMN, HOST_OFFSET_COLUMN, GUEST_LANGUAGE_COLUMN, GUEST_SPACE_COLUMN, GUEST_OFFSET_COLUMN, LENGTH_COLUMN); + + this.hostRange = new AddressRangeImpl(hostStart, hostStart.addWrap(length - 1)); + this.guestPlatform = guestPlatform; + this.guestRange = new AddressRangeImpl(guestStart, guestStart.addWrap(length - 1)); + } @Override @@ -119,14 +122,19 @@ public class DBTraceGuestLanguageMappedRange extends DBAnnotatedObject return manager.getBaseLanguage(); } + @Override + public CompilerSpec getHostCompilerSpec() { + return manager.getBaseCompilerSpec(); + } + @Override public AddressRange getHostRange() { return hostRange; } @Override - public Language getGuestLanguage() { - return guestLanguage; + public DBTraceGuestPlatform getGuestPlatform() { + return guestPlatform; } @Override @@ -154,6 +162,6 @@ public class DBTraceGuestLanguageMappedRange extends DBAnnotatedObject @Override public void delete(TaskMonitor monitor) throws CancelledException { - manager.languageStore.getObjectAt(guestLangKey).deleteMappedRange(this, monitor); + manager.platformStore.getObjectAt(guestPlatformKey).deleteMappedRange(this, monitor); } } 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 new file mode 100644 index 0000000000..005cb4a078 --- /dev/null +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/guest/DBTracePlatformManager.java @@ -0,0 +1,298 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.trace.database.guest; + +import java.io.IOException; +import java.util.*; +import java.util.concurrent.locks.ReadWriteLock; + +import db.DBHandle; +import ghidra.lifecycle.Internal; +import ghidra.program.model.lang.*; +import ghidra.program.model.listing.Instruction; +import ghidra.trace.database.DBTrace; +import ghidra.trace.database.DBTraceManager; +import ghidra.trace.database.guest.DBTraceGuestPlatform.DBTraceGuestLanguage; +import ghidra.trace.model.guest.TraceGuestPlatform; +import ghidra.trace.model.guest.TracePlatformManager; +import ghidra.trace.model.listing.TraceInstruction; +import ghidra.util.LockHold; +import ghidra.util.database.*; +import ghidra.util.exception.CancelledException; +import ghidra.util.exception.VersionException; +import ghidra.util.task.TaskMonitor; + +/* + * TODO: Store mapping as from (host) address, to (guest) address, length. "to" must include + * spaceId. It is not a problem if things overlap, as these are just informational in case an + * instruction or reference comes along that needs mapping. This also determines what is visible + * in program views of the mapped language. There should not be any overlaps in the same guest + * language, however. + */ +public class DBTracePlatformManager implements DBTraceManager, TracePlatformManager { + protected final DBHandle dbh; + protected final ReadWriteLock lock; + protected final Language baseLanguage; + protected final CompilerSpec baseCompilerSpec; + protected final DBTrace trace; + + protected final DBCachedObjectStore languageStore; + protected final DBCachedObjectStore platformStore; + protected final Collection platformView; + protected final Map languagesByLanguage = new HashMap<>(); + protected final Map platformsByCompiler = new HashMap<>(); + + protected final DBCachedObjectStore rangeMappingStore; + + public DBTracePlatformManager(DBHandle dbh, DBOpenMode openMode, ReadWriteLock lock, + TaskMonitor monitor, CompilerSpec baseCompilerSpec, DBTrace trace) + throws VersionException, IOException { + this.dbh = dbh; + this.lock = lock; + this.baseLanguage = baseCompilerSpec.getLanguage(); + this.baseCompilerSpec = baseCompilerSpec; + this.trace = trace; + + DBCachedObjectStoreFactory factory = trace.getStoreFactory(); + languageStore = factory.getOrCreateCachedStore(DBTraceGuestLanguage.TABLE_NAME, + DBTraceGuestLanguage.class, DBTraceGuestLanguage::new, true); + platformStore = factory.getOrCreateCachedStore(DBTraceGuestPlatform.TABLE_NAME, + DBTraceGuestPlatform.class, (s, r) -> new DBTraceGuestPlatform(this, s, r), true); + platformView = Collections.unmodifiableCollection(platformStore.asMap().values()); + + rangeMappingStore = factory.getOrCreateCachedStore( + DBTraceGuestPlatformMappedRange.TABLE_NAME, DBTraceGuestPlatformMappedRange.class, + (s, r) -> new DBTraceGuestPlatformMappedRange(this, s, r), true); + + loadLanguages(); + loadPlatforms(); + loadPlatformMappings(); + } + + protected void loadLanguages() { + for (DBTraceGuestLanguage languageEntry : languageStore.asMap().values()) { + languagesByLanguage.put(languageEntry.getLanguage(), languageEntry); + } + } + + protected void loadPlatforms() + throws LanguageNotFoundException, CompilerSpecNotFoundException, VersionException { + for (DBTraceGuestPlatform platformEntry : platformStore.asMap().values()) { + platformsByCompiler.put(platformEntry.getCompilerSpec(), platformEntry); + } + } + + protected void loadPlatformMappings() { + for (DBTraceGuestPlatformMappedRange langMapping : rangeMappingStore.asMap().values()) { + DBTraceGuestPlatform mappedLanguage = + platformStore.getObjectAt(langMapping.guestPlatformKey); + mappedLanguage.rangesByHostAddress.put(langMapping.getHostRange().getMinAddress(), + langMapping); + mappedLanguage.rangesByGuestAddress.put(langMapping.getGuestRange().getMinAddress(), + langMapping); + } + } + + @Internal + protected DBTraceGuestLanguage getOrCreateLanguage(Language language) { + if (language == baseLanguage) { + return null; + } + DBTraceGuestLanguage languageEntry = languagesByLanguage.get(language); + if (languageEntry == null) { + languageEntry = languageStore.create(); + languageEntry.set(language); + languagesByLanguage.put(language, languageEntry); + } + return languageEntry; + } + + @Internal + public DBTraceGuestLanguage getLanguageByKey(int key) { + if (key == -1) { + return null; + } + return languageStore.getObjectAt(key); + } + + @Internal + public DBTraceGuestPlatform getPlatformByKey(int key) { + if (key == -1) { + return null; + } + return platformStore.getObjectAt(key); + } + + protected int getPlatformKeyForCompiler(CompilerSpec compiler) { + if (Objects.equals(compiler, baseCompilerSpec)) { + return -1; + } + return (int) platformsByCompiler.get(compiler).getKey(); + } + + @Internal + public DBTraceGuestLanguage getLanguageByLanguage(Language language) { + if (Objects.equals(language, baseLanguage)) { + return null; + } + return Objects.requireNonNull(languagesByLanguage.get(language)); + } + + protected CompilerSpec getCompilerByKey(int compilerKey) { + if (compilerKey == -1) { + return baseCompilerSpec; + } + return platformStore.getObjectAt(compilerKey).getCompilerSpec(); + } + + @Override + public void dbError(IOException e) { + trace.dbError(e); + } + + @Override + public void invalidateCache(boolean all) { + languageStore.invalidateCache(); + platformStore.invalidateCache(); + rangeMappingStore.invalidateCache(); + languagesByLanguage.clear(); + platformsByCompiler.clear(); + try { + loadLanguages(); + loadPlatforms(); + loadPlatformMappings(); + } + catch (LanguageNotFoundException | CompilerSpecNotFoundException | VersionException e) { + throw new AssertionError(e); + } + } + + protected void deleteGuestPlatform(DBTraceGuestPlatform platform, TaskMonitor monitor) + throws CancelledException { + try (LockHold hold = LockHold.lock(lock.writeLock())) { + int platformKey = (int) platform.getKey(); + trace.getCodeManager().deletePlatform(platform, monitor); + monitor.setMessage("Clearing guest platform range mappings"); + monitor.setMaximum(rangeMappingStore.getRecordCount()); + for (Iterator it = + rangeMappingStore.asMap().values().iterator(); it.hasNext();) { + DBTraceGuestPlatformMappedRange range = it.next(); + if (platformKey != range.guestPlatformKey) { + continue; + } + it.remove(); + } + platformsByCompiler.remove(platform.getCompilerSpec()); + platformStore.delete(platform); + } + } + + @Override + public Language getBaseLanguage() { + return trace.getBaseLanguage(); + } + + @Override + public CompilerSpec getBaseCompilerSpec() { + return trace.getBaseCompilerSpec(); + } + + protected DBTraceGuestPlatform doAddGuestPlatform(CompilerSpec compilerSpec) { + DBTraceGuestPlatform platformEntry = platformStore.create(); + platformEntry.set(compilerSpec); + platformsByCompiler.put(compilerSpec, platformEntry); + return platformEntry; + } + + @Override + public DBTraceGuestPlatform addGuestPlatform(CompilerSpec compilerSpec) { + if (compilerSpec.getCompilerSpecID() + .equals(trace.getBaseCompilerSpec().getCompilerSpecID())) { + throw new IllegalArgumentException("Base language cannot be a guest language"); + } + try (LockHold hold = LockHold.lock(lock.writeLock())) { + return doAddGuestPlatform(compilerSpec); + } + } + + @Override + public DBTraceGuestPlatform getGuestPlatform(CompilerSpec compilerSpec) { + try (LockHold hold = LockHold.lock(lock.readLock())) { + return platformsByCompiler.get(compilerSpec); + } + } + + @Override + public DBTraceGuestPlatform getOrAddGuestPlatform(CompilerSpec compilerSpec) { + if (compilerSpec.getCompilerSpecID() + .equals(trace.getBaseCompilerSpec().getCompilerSpecID())) { + throw new IllegalArgumentException("Base language cannot be a guest language"); + } + try (LockHold hold = LockHold.lock(lock.writeLock())) { + DBTraceGuestPlatform exists = platformsByCompiler.get(compilerSpec); + if (exists != null) { + return exists; + } + return doAddGuestPlatform(compilerSpec); + } + } + + @Override + public Collection getGuestPlatforms() { + return platformView; + } + + protected TraceGuestPlatform getPlatformOf(InstructionSet instructionSet) { + for (InstructionBlock block : instructionSet) { + for (Instruction instruction : block) { + if (!(instruction instanceof TraceInstruction)) { + continue; + } + TraceInstruction traceInstruction = (TraceInstruction) instruction; + return traceInstruction.getGuestPlatform(); + } + } + return null; + } + + public InstructionSet mapGuestInstructionAddressesToHost(TraceGuestPlatform platform, + InstructionSet instructionSet) { + try (LockHold hold = LockHold.lock(lock.readLock())) { + if (platform == null) { // Instructions belong to the host platform + return instructionSet; + } + return platform.mapGuestInstructionAddressesToHost(instructionSet); + } + } + + @Internal + public DBTraceGuestPlatform assertMine(TraceGuestPlatform platform) { + if (platform == null) { + return null; + } + if (!(platform instanceof DBTraceGuestPlatform)) { + throw new IllegalArgumentException("Given platform does not belong to this trace"); + } + DBTraceGuestPlatform dbPlatform = (DBTraceGuestPlatform) platform; + if (dbPlatform.manager != this) { + throw new IllegalArgumentException("Given platform does not belong to this trace"); + } + if (dbPlatform.isDeleted()) { + throw new IllegalArgumentException("Given platform has been deleted"); + } + return dbPlatform; + } +} diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/language/DBTraceLanguageManager.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/language/DBTraceLanguageManager.java deleted file mode 100644 index 2aa4d7fe6c..0000000000 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/language/DBTraceLanguageManager.java +++ /dev/null @@ -1,204 +0,0 @@ -/* ### - * IP: GHIDRA - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package ghidra.trace.database.language; - -import java.io.IOException; -import java.util.*; -import java.util.concurrent.locks.ReadWriteLock; - -import db.DBHandle; -import ghidra.program.model.lang.*; -import ghidra.program.model.listing.Instruction; -import ghidra.program.util.DefaultLanguageService; -import ghidra.trace.database.DBTrace; -import ghidra.trace.database.DBTraceManager; -import ghidra.trace.model.language.TraceGuestLanguage; -import ghidra.trace.model.language.TraceLanguageManager; -import ghidra.util.LockHold; -import ghidra.util.database.*; -import ghidra.util.exception.CancelledException; -import ghidra.util.exception.VersionException; -import ghidra.util.task.TaskMonitor; - -/* - * TODO: Store mapping as from (host) address, to (guest) address, length. "to" must include - * spaceId. It is not a problem if things overlap, as these are just informational in case an - * instruction or reference comes along that needs mapping. This also determines what is visible - * in program views of the mapped language. There should not be any overlaps in the same guest - * language, however. - */ -public class DBTraceLanguageManager implements DBTraceManager, TraceLanguageManager { - protected final DBHandle dbh; - protected final ReadWriteLock lock; - protected final Language baseLanguage; - protected final DBTrace trace; - - protected final DBCachedObjectStore languageStore; - protected final Collection languageView; - protected final Map entriesByLanguage = new HashMap<>(); - - protected final DBCachedObjectStore rangeMappingStore; - - public DBTraceLanguageManager(DBHandle dbh, DBOpenMode openMode, ReadWriteLock lock, - TaskMonitor monitor, Language baseLanguage, DBTrace trace) - throws VersionException, IOException { - this.dbh = dbh; - this.lock = lock; - this.baseLanguage = baseLanguage; - this.trace = trace; - - DBCachedObjectStoreFactory factory = trace.getStoreFactory(); - languageStore = factory.getOrCreateCachedStore(DBTraceGuestLanguage.TABLE_NAME, - DBTraceGuestLanguage.class, (s, r) -> new DBTraceGuestLanguage(this, s, r), true); - languageView = Collections.unmodifiableCollection(languageStore.asMap().values()); - - rangeMappingStore = factory.getOrCreateCachedStore( - DBTraceGuestLanguageMappedRange.TABLE_NAME, DBTraceGuestLanguageMappedRange.class, - (s, r) -> new DBTraceGuestLanguageMappedRange(this, s, r), true); - - loadLanguages(); - loadLanguageMappings(); - } - - protected void loadLanguages() throws LanguageNotFoundException, VersionException { - LanguageService langServ = DefaultLanguageService.getLanguageService(); - for (DBTraceGuestLanguage langEnt : languageStore.asMap().values()) { - langEnt.doGetLanguage(langServ); - entriesByLanguage.put(langEnt.getLanguage(), langEnt); - } - } - - protected void loadLanguageMappings() { - for (DBTraceGuestLanguageMappedRange langMapping : rangeMappingStore.asMap().values()) { - DBTraceGuestLanguage mappedLanguage = - languageStore.getObjectAt(langMapping.guestLangKey); - mappedLanguage.rangesByHostAddress.put(langMapping.getHostRange().getMinAddress(), - langMapping); - mappedLanguage.rangesByGuestAddress.put(langMapping.getGuestRange().getMinAddress(), - langMapping); - } - } - - // Internal - public int getKeyForLanguage(Language language) { - if (Objects.equals(language, baseLanguage)) { - return -1; - } - return (int) entriesByLanguage.get(language).getKey(); - } - - // Internal - public Language getLanguageByKey(int langKey) { - if (langKey == -1) { - return baseLanguage; - } - return languageStore.getObjectAt(langKey).getLanguage(); - } - - @Override - public void dbError(IOException e) { - trace.dbError(e); - } - - @Override - public void invalidateCache(boolean all) { - languageStore.invalidateCache(); - rangeMappingStore.invalidateCache(); - entriesByLanguage.clear(); - try { - loadLanguages(); - loadLanguageMappings(); - } - catch (LanguageNotFoundException | VersionException e) { - throw new AssertionError(e); - } - } - - protected void deleteGuestLanguage(DBTraceGuestLanguage langEnt, TaskMonitor monitor) - throws CancelledException { - try (LockHold hold = LockHold.lock(lock.writeLock())) { - int langKey = (int) langEnt.getKey(); - trace.getCodeManager().deleteLanguage(langKey, monitor); - monitor.setMessage("Clearing guest language range mappings"); - monitor.setMaximum(rangeMappingStore.getRecordCount()); - for (Iterator it = - rangeMappingStore.asMap().values().iterator(); it.hasNext();) { - DBTraceGuestLanguageMappedRange range = it.next(); - if (langKey != range.guestLangKey) { - continue; - } - it.remove(); - } - entriesByLanguage.remove(langEnt.getLanguage()); - languageStore.delete(langEnt); - } - } - - @Override - public Language getBaseLanguage() { - return trace.getBaseLanguage(); - } - - @Override - public DBTraceGuestLanguage addGuestLanguage(Language language) { - if (language.getLanguageID().equals(trace.getBaseLanguage().getLanguageID())) { - throw new IllegalArgumentException("Base language cannot be a guest language"); - } - try (LockHold hold = LockHold.lock(lock.writeLock())) { - DBTraceGuestLanguage langEnt = languageStore.create(); - langEnt.setLanguage(language); - entriesByLanguage.put(language, langEnt); - return langEnt; - } - } - - public DBTraceGuestLanguage getGuestLanguage(Language language) { - try (LockHold hold = LockHold.lock(lock.readLock())) { - return entriesByLanguage.get(language); - } - } - - @Override - public Collection getGuestLanguages() { - return languageView; - } - - protected Language getLanguageOf(InstructionSet instructionSet) { - for (InstructionBlock block : instructionSet) { - for (Instruction instruction : block) { - return instruction.getPrototype().getLanguage(); - } - } - // No instructions, default to base language - return trace.getBaseLanguage(); - } - - @Override - public InstructionSet mapGuestInstructionAddressesToHost(InstructionSet instructionSet) { - try (LockHold hold = LockHold.lock(lock.readLock())) { - Language language = getLanguageOf(instructionSet); - if (language == trace.getBaseLanguage()) { - return instructionSet; // Nothing to map - } - DBTraceGuestLanguage guest = trace.getLanguageManager().getGuestLanguage(language); - if (guest == null) { - throw new IllegalArgumentException( - "Instructions are in neither the base nor a guest language"); - } - return guest.mapGuestInstructionAddressesToHost(instructionSet); - } - } -} diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/AbstractDBTraceDataComponent.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/AbstractDBTraceDataComponent.java index 7c27b424d4..8bc84649fc 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/AbstractDBTraceDataComponent.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/AbstractDBTraceDataComponent.java @@ -25,6 +25,7 @@ import ghidra.program.model.data.DataType; import ghidra.program.model.lang.Language; import ghidra.trace.database.DBTrace; import ghidra.trace.database.data.DBTraceDataSettingsAdapter.DBTraceDataSettingsSpace; +import ghidra.trace.model.guest.TraceGuestPlatform; import ghidra.trace.model.thread.TraceThread; import ghidra.util.LockHold; @@ -82,6 +83,11 @@ public abstract class AbstractDBTraceDataComponent implements DBTraceDefinedData return root.getThread(); } + @Override + public TraceGuestPlatform getGuestPlatform() { + return root.getGuestPlatform(); + } + @Override public Language getLanguage() { return root.getLanguage(); diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceCodeManager.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceCodeManager.java index 02395493d3..4789e9777d 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceCodeManager.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceCodeManager.java @@ -25,10 +25,11 @@ import java.util.concurrent.locks.ReadWriteLock; import com.google.common.cache.CacheBuilder; import com.google.common.cache.RemovalNotification; -import com.google.common.collect.*; +import com.google.common.collect.Range; import db.DBHandle; import db.DBRecord; +import ghidra.lifecycle.Internal; import ghidra.program.model.address.*; import ghidra.program.model.lang.*; import ghidra.program.model.listing.ContextChangeException; @@ -41,7 +42,9 @@ import ghidra.trace.database.address.DBTraceOverlaySpaceAdapter; import ghidra.trace.database.address.DBTraceOverlaySpaceAdapter.AddressDBFieldCodec; import ghidra.trace.database.address.DBTraceOverlaySpaceAdapter.DecodesAddresses; import ghidra.trace.database.data.DBTraceDataTypeManager; -import ghidra.trace.database.language.DBTraceLanguageManager; +import ghidra.trace.database.guest.DBTraceGuestPlatform; +import ghidra.trace.database.guest.DBTracePlatformManager; +import ghidra.trace.database.guest.DBTraceGuestPlatform.DBTraceGuestLanguage; import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree.TraceAddressSnapRangeQuery; import ghidra.trace.database.space.AbstractDBTraceSpaceBasedManager; import ghidra.trace.database.space.DBTraceDelegatingManager; @@ -90,7 +93,7 @@ public class DBTraceCodeManager static DBObjectColumn DELAY_COLUMN; @DBAnnotatedField(column = LANGUAGE_COLUMN_NAME) - private int langKey; + private int languageKey; @DBAnnotatedField(column = BYTES_COLUMN_NAME) private byte[] bytes; @DBAnnotatedField(column = CONTEXT_COLUMN_NAME) @@ -100,6 +103,8 @@ public class DBTraceCodeManager @DBAnnotatedField(column = DELAY_COLUMN_NAME) private boolean delaySlot; + private InstructionPrototype prototype; + private DBTraceCodeManager manager; public DBTraceCodePrototypeEntry(DBTraceCodeManager manager, DBCachedObjectStore store, @@ -113,21 +118,33 @@ public class DBTraceCodeManager return manager.overlayAdapter; } - void set(int langKey, byte[] bytes, byte[] context, Address address, boolean delaySlot) { - this.langKey = langKey; + void set(DBTraceGuestLanguage languageEntry, byte[] bytes, byte[] context, Address address, + boolean delaySlot) { + this.languageKey = (int) (languageEntry == null ? -1 : languageEntry.getKey()); this.bytes = bytes; this.context = context; this.address = address; this.delaySlot = delaySlot; update(LANGUAGE_COLUMN, BYTES_COLUMN, CONTEXT_COLUMN, ADDRESS_COLUMN, DELAY_COLUMN); + this.prototype = parsePrototype(); } - int getLanguageKey() { - return langKey; + @Override + protected void fresh(boolean created) throws IOException { + super.fresh(created); + if (created) { + return; + } + this.prototype = parsePrototype(); } - InstructionPrototype parsePrototype() { - Language language = manager.languageManager.getLanguageByKey(langKey); + public InstructionPrototype getPrototype() { + return prototype; + } + + private InstructionPrototype parsePrototype() { + DBTraceGuestLanguage guest = manager.platformManager.getLanguageByKey(languageKey); + Language language = guest == null ? manager.baseLanguage : guest.getLanguage(); MemBuffer memBuffer = new ByteMemBufferImpl(address, bytes, language.isBigEndian()); ProcessorContext ctx = new ProtoProcessorContext(getBaseContextValue(language, context, address)); @@ -168,13 +185,14 @@ public class DBTraceCodeManager return max; } - protected final DBTraceLanguageManager languageManager; + protected final DBTracePlatformManager platformManager; protected final DBTraceDataTypeManager dataTypeManager; protected final DBTraceOverlaySpaceAdapter overlayAdapter; protected final DBTraceReferenceManager referenceManager; protected final DBCachedObjectStore protoStore; - protected final BiMap protoMap = HashBiMap.create(); + protected final Map entriesByProto = + new HashMap<>(); protected final DBTraceCodeUnitsMemoryView codeUnits = new DBTraceCodeUnitsMemoryView(this); protected final DBTraceInstructionsMemoryView instructions = @@ -196,11 +214,11 @@ public class DBTraceCodeManager public DBTraceCodeManager(DBHandle dbh, DBOpenMode openMode, ReadWriteLock lock, TaskMonitor monitor, Language baseLanguage, DBTrace trace, - DBTraceThreadManager threadManager, DBTraceLanguageManager languageManager, + DBTraceThreadManager threadManager, DBTracePlatformManager platformManager, DBTraceDataTypeManager dataTypeManager, DBTraceOverlaySpaceAdapter overlayAdapter, DBTraceReferenceManager referenceManager) throws IOException, VersionException { super(NAME, dbh, openMode, lock, monitor, baseLanguage, trace, threadManager); - this.languageManager = languageManager; + this.platformManager = platformManager; this.dataTypeManager = dataTypeManager; this.overlayAdapter = overlayAdapter; this.referenceManager = referenceManager; @@ -230,7 +248,7 @@ public class DBTraceCodeManager // NOTE: Should already own write lock for (DBTraceCodePrototypeEntry protoEnt : protoStore.asMap().values()) { // NOTE: No need to check if it exists. This is only called on new or after clear - protoMap.put(protoEnt.parsePrototype(), (int) protoEnt.getKey()); + entriesByProto.put(protoEnt.prototype, protoEnt); } } @@ -239,8 +257,8 @@ public class DBTraceCodeManager return Arrays.copyOfRange(bytes, bytes.length / 2, bytes.length); } - protected int doRecordPrototype(InstructionPrototype prototype, MemBuffer memBuffer, - ProcessorContextView context) { + protected DBTraceCodePrototypeEntry doRecordPrototype(InstructionPrototype prototype, + DBTraceGuestLanguage guest, MemBuffer memBuffer, ProcessorContextView context) { DBTraceCodePrototypeEntry protoEnt = protoStore.create(); byte[] bytes = new byte[prototype.getLength()]; if (memBuffer.getBytes(bytes, 0) != bytes.length) { @@ -255,20 +273,20 @@ public class DBTraceCodeManager RegisterValue value = context.getRegisterValue(baseCtxReg); ctx = value == null ? null : valueBytes(value); } - protoEnt.set(languageManager.getKeyForLanguage(prototype.getLanguage()), bytes, ctx, - memBuffer.getAddress(), prototype.isInDelaySlot()); - return (int) protoEnt.getKey(); + protoEnt.set(guest, bytes, ctx, memBuffer.getAddress(), prototype.isInDelaySlot()); + return protoEnt; } - protected int findOrRecordPrototype(InstructionPrototype prototype, MemBuffer memBuffer, - ProcessorContextView context) { + protected DBTraceCodePrototypeEntry findOrRecordPrototype(InstructionPrototype prototype, + DBTraceGuestLanguage guest, MemBuffer memBuffer, ProcessorContextView context) { // NOTE: Must already have write lock - return protoMap.computeIfAbsent(prototype, - p -> doRecordPrototype(prototype, memBuffer, context)); + return entriesByProto.computeIfAbsent(prototype, + p -> doRecordPrototype(prototype, guest, memBuffer, context)); } protected InstructionPrototype getPrototypeByKey(int key) { - return protoMap.inverse().get(key); + DBTraceCodePrototypeEntry protoEnt = protoStore.getObjectAt(key); + return protoEnt == null ? null : protoEnt.prototype; } @Override @@ -326,35 +344,43 @@ public class DBTraceCodeManager return getForRegisterSpace(frame, createIfAbsent); } - // Internal + @Internal public void replaceDataTypes(long oldID, long newID) { TODO(); } - // Internal + @Internal public void clearData(LinkedList deletedDataTypeIds, TaskMonitor monitor) { TODO(); } - // Internal - public void clearLanguage(Range span, AddressRange range, int langKey, + @Internal + public void clearPlatform(Range span, AddressRange range, DBTraceGuestPlatform guest, TaskMonitor monitor) throws CancelledException { delegateDeleteV(range.getAddressSpace(), - m -> m.clearLanguage(span, range, langKey, monitor)); + m -> m.clearPlatform(span, range, guest, monitor)); } - // Internal - public void deleteLanguage(int langKey, TaskMonitor monitor) throws CancelledException { + @Internal + public void deletePlatform(DBTraceGuestPlatform guest, TaskMonitor monitor) + throws CancelledException { // TODO: Use sub-monitors when available for (DBTraceCodeSpace codeSpace : memSpaces.values()) { - codeSpace.clearLanguage(Range.all(), codeSpace.all, langKey, monitor); + codeSpace.clearPlatform(Range.all(), codeSpace.all, guest, monitor); } for (DBTraceCodeRegisterSpace codeSpace : regSpaces.values()) { // TODO: I don't know any way to get guest instructions into register space // The mapping manager does (should) not allow guest register addresses // TODO: Test this if I ever get guest data units - codeSpace.clearLanguage(Range.all(), codeSpace.all, langKey, monitor); + // TODO: I think explicit per-thread/frame register spaces will be going away, anyway + // They'll just be path-named overlays on register space? + codeSpace.clearPlatform(Range.all(), codeSpace.all, guest, monitor); } + } + + @Internal + public void deleteLangauge(DBTraceGuestLanguage guest, TaskMonitor monitor) + throws CancelledException { monitor.setMessage("Clearing instruction prototypes"); monitor.setMaximum(protoStore.getRecordCount()); for (Iterator it = protoStore.asMap().values().iterator(); it @@ -362,11 +388,11 @@ public class DBTraceCodeManager monitor.checkCanceled(); monitor.incrementProgress(1); DBTraceCodePrototypeEntry protoEnt = it.next(); - if (langKey != protoEnt.langKey) { + if (protoEnt.prototype.getLanguage() != guest.getLanguage()) { continue; } it.remove(); - protoMap.inverse().remove((int) protoEnt.getKey()); + entriesByProto.remove(protoEnt.prototype); } } @@ -374,7 +400,7 @@ public class DBTraceCodeManager public void invalidateCache(boolean all) { try (LockHold hold = LockHold.lock(lock.writeLock())) { protoStore.invalidateCache(); - protoMap.clear(); + entriesByProto.clear(); loadPrototypes(); super.invalidateCache(all); 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 0e4d7f91b8..5df815aeac 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 @@ -32,6 +32,7 @@ import ghidra.program.model.util.CodeUnitInsertionException; import ghidra.trace.database.DBTrace; import ghidra.trace.database.DBTraceUtils; import ghidra.trace.database.data.DBTraceDataTypeManager; +import ghidra.trace.database.guest.DBTraceGuestPlatform; import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapSpace; import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree.TraceAddressSnapRangeQuery; import ghidra.trace.database.space.AbstractDBTraceSpaceBasedManager.DBTraceSpaceEntry; @@ -125,8 +126,8 @@ public class DBTraceCodeSpace implements TraceCodeSpace, DBTraceSpaceBased { return new DBTraceCodeUnitsView(this); } - void clearLanguage(Range span, AddressRange range, int langKey, TaskMonitor monitor) - throws CancelledException { + void clearPlatform(Range span, AddressRange range, DBTraceGuestPlatform guest, + TaskMonitor monitor) throws CancelledException { // Note "makeWay" does not apply here. // Units should be enclosed by guest mapping. // TODO: Use sub-monitors when available @@ -141,8 +142,7 @@ public class DBTraceCodeSpace implements TraceCodeSpace, DBTraceSpaceBased { TraceAddressSnapRangeQuery.intersecting(range, span)).values()) { monitor.checkCanceled(); monitor.incrementProgress(1); - if (langKey != manager.protoStore.getObjectAt( - instruction.getPrototypeKey()).getLanguageKey()) { + if (instruction.guest != guest) { continue; } instructionMapSpace.deleteData(instruction); @@ -154,7 +154,7 @@ public class DBTraceCodeSpace implements TraceCodeSpace, DBTraceSpaceBased { TraceAddressSnapRangeQuery.intersecting(range, span)).values()) { monitor.checkCanceled(); monitor.incrementProgress(1); - if (langKey != dataUnit.getLanguageKey()) { + if (dataUnit.guest != guest) { continue; } // TODO: I don't yet have guest-language data units. 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 c16c32a13e..58373cb5bf 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 @@ -26,7 +26,9 @@ import ghidra.program.model.data.*; import ghidra.program.model.lang.Language; import ghidra.trace.database.DBTraceUtils; import ghidra.trace.database.data.DBTraceDataSettingsAdapter.DBTraceDataSettingsSpace; +import ghidra.trace.database.guest.DBTraceGuestPlatform; import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree; +import ghidra.trace.model.guest.TraceGuestPlatform; import ghidra.util.LockHold; import ghidra.util.database.DBCachedObjectStore; import ghidra.util.database.DBObjectColumn; @@ -37,11 +39,11 @@ public class DBTraceData extends AbstractDBTraceCodeUnit implements DBTraceDefinedDataAdapter { private static final String TABLE_NAME = "Data"; - static final String LANGUAGE_COLUMN_NAME = "Language"; + static final String PLATFORM_COLUMN_NAME = "Platform"; static final String DATATYPE_COLUMN_NAME = "DataType"; - @DBAnnotatedColumn(LANGUAGE_COLUMN_NAME) - static DBObjectColumn LANGUAGE_COLUMN; + @DBAnnotatedColumn(PLATFORM_COLUMN_NAME) + static DBObjectColumn PLATFORM_COLUMN; @DBAnnotatedColumn(DATATYPE_COLUMN_NAME) static DBObjectColumn DATATYPE_COLUMN; @@ -49,12 +51,12 @@ public class DBTraceData extends AbstractDBTraceCodeUnit return DBTraceUtils.tableName(TABLE_NAME, space, threadKey, frameLevel); } - @DBAnnotatedField(column = LANGUAGE_COLUMN_NAME) - private int langKey; + @DBAnnotatedField(column = PLATFORM_COLUMN_NAME) + private int platformKey; @DBAnnotatedField(column = DATATYPE_COLUMN_NAME) private long dataTypeID; - protected Language language; + protected DBTraceGuestPlatform guest; protected DataType dataType; protected DataType baseDataType; protected Settings defaultSettings; @@ -73,9 +75,9 @@ public class DBTraceData extends AbstractDBTraceCodeUnit if (created) { return; } - language = space.manager.languageManager.getLanguageByKey(langKey); - if (language == null) { - throw new IOException("Data table is corrupt. Missing language: " + langKey); + guest = space.manager.platformManager.getPlatformByKey(platformKey); + if (guest == null && platformKey != -1) { + throw new IOException("Data table is corrupt. Missing platform: " + platformKey); } dataType = space.dataTypeManager.getDataType(dataTypeID); if (dataType == null) { @@ -100,16 +102,17 @@ public class DBTraceData extends AbstractDBTraceCodeUnit return this; } - protected void set(Language language, DataType dataType) { - this.language = language; - this.langKey = space.manager.languageManager.getKeyForLanguage(language); + protected void set(DBTraceGuestPlatform platform, DataType dataType) { + this.platformKey = (int) (platform == null ? -1 : platform.getKey()); this.dataTypeID = space.dataTypeManager.getResolvedID(dataType); + update(PLATFORM_COLUMN, DATATYPE_COLUMN); + + this.guest = platform; // Use the stored dataType, not the given one, in case it's different this.dataType = space.dataTypeManager.getDataType(dataTypeID); assert this.dataType != null; - this.baseDataType = getBaseDataType(this.dataType); this.defaultSettings = this.dataType.getDefaultSettings(); - update(LANGUAGE_COLUMN, DATATYPE_COLUMN); + this.baseDataType = getBaseDataType(this.dataType); } protected int getDataTypeLength() { @@ -117,7 +120,7 @@ public class DBTraceData extends AbstractDBTraceCodeUnit // TODO: Also need to know where this address maps into the other language's spaces.... // NOTE: Using default data space for now // TODO: I may not need this Pointer check, as clone(dtm) should adjust already - return language.getDefaultDataSpace().getPointerSize(); + return getLanguage().getDefaultDataSpace().getPointerSize(); } return dataType.getLength(); // -1 is checked elsewhere } @@ -129,6 +132,11 @@ public class DBTraceData extends AbstractDBTraceCodeUnit return dt; } + @Override + public TraceGuestPlatform getGuestPlatform() { + return guest; + } + @Override public void delete() { try (LockHold hold = LockHold.lock(space.lock.writeLock())) { @@ -149,11 +157,7 @@ public class DBTraceData extends AbstractDBTraceCodeUnit @Override public Language getLanguage() { - return language; - } - - int getLanguageKey() { - return langKey; + return guest == null ? space.baseLanguage : guest.getLanguage(); } @Override 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 102e1289f7..766e3884cb 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 @@ -145,7 +145,8 @@ public class DBTraceDefinedDataView extends AbstractBaseDBTraceDefinedUnitsView< } DBTraceData created = space.dataMapSpace.put(tasr, null); - created.set(space.baseLanguage, dataType); + // TODO: data units with a guest platform + created.set(null, dataType); // TODO: Explicitly remove undefined from cache, or let weak refs take care of it? cacheForContaining.notifyNewEntry(lifespan, createdRange, created); diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceInstruction.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceInstruction.java index 83f32ce23e..fa43e43bde 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceInstruction.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceInstruction.java @@ -32,11 +32,13 @@ import ghidra.program.model.symbol.*; import ghidra.trace.database.DBTraceUtils; import ghidra.trace.database.context.DBTraceRegisterContextManager; import ghidra.trace.database.context.DBTraceRegisterContextSpace; -import ghidra.trace.database.language.DBTraceGuestLanguage; +import ghidra.trace.database.guest.DBTraceGuestPlatform; +import ghidra.trace.database.guest.DBTraceGuestPlatform.DBTraceGuestLanguage; import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree; import ghidra.trace.database.symbol.DBTraceReference; import ghidra.trace.database.symbol.DBTraceReferenceSpace; import ghidra.trace.model.Trace.TraceInstructionChangeType; +import ghidra.trace.model.guest.TraceGuestPlatform; import ghidra.trace.model.listing.TraceInstruction; import ghidra.trace.model.symbol.TraceReference; import ghidra.trace.util.*; @@ -59,9 +61,12 @@ public class DBTraceInstruction extends AbstractDBTraceCodeUnit> FLOWOVERRIDE_SHIFT]; - doSetGuestMapping(); + doSetGuestMapping(guest); } @Override @@ -161,17 +186,6 @@ public class DBTraceInstruction extends AbstractDBTraceCodeUnit lifespan, Address address, - InstructionPrototype prototype, ProcessorContextView context) - throws CodeUnitInsertionException { + TraceGuestPlatform platform, InstructionPrototype prototype, + ProcessorContextView context) throws CodeUnitInsertionException { return delegateWrite(address.getAddressSpace(), - m -> m.create(lifespan, address, prototype, context)); + m -> m.create(lifespan, address, platform, prototype, context)); } @Override - public AddressSetView addInstructionSet(Range lifespan, InstructionSet instructionSet, - boolean overwrite) { - InstructionSet mappedSet = - manager.getTrace().getLanguageManager().mapGuestInstructionAddressesToHost( - instructionSet); + public AddressSetView addInstructionSet(Range lifespan, TraceGuestPlatform platform, + InstructionSet instructionSet, boolean overwrite) { + InstructionSet mappedSet = manager.platformManager + .mapGuestInstructionAddressesToHost(platform, instructionSet); Map breakDown = new HashMap<>(); // TODO: I'm not sure the consequences of breaking an instruction set down. @@ -74,8 +74,8 @@ public class DBTraceInstructionsMemoryView try (LockHold hold = LockHold.lock(manager.writeLock())) { for (Entry entry : breakDown.entrySet()) { DBTraceInstructionsView instructionsView = getForSpace(entry.getKey(), true); - result.add( - instructionsView.addInstructionSet(lifespan, entry.getValue(), overwrite)); + result.add(instructionsView.addInstructionSet(lifespan, platform, entry.getValue(), + overwrite)); } return result; } diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceInstructionsView.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceInstructionsView.java index a7d16110da..49e23d0797 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceInstructionsView.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceInstructionsView.java @@ -29,9 +29,11 @@ import ghidra.program.model.util.CodeUnitInsertionException; import ghidra.trace.database.DBTraceUtils; import ghidra.trace.database.context.DBTraceRegisterContextManager; import ghidra.trace.database.context.DBTraceRegisterContextSpace; +import ghidra.trace.database.guest.DBTraceGuestPlatform; import ghidra.trace.database.memory.DBTraceMemorySpace; import ghidra.trace.model.ImmutableTraceAddressSnapRange; import ghidra.trace.model.Trace.TraceCodeChangeType; +import ghidra.trace.model.guest.TraceGuestPlatform; import ghidra.trace.model.TraceAddressSnapRange; import ghidra.trace.model.listing.TraceInstruction; import ghidra.trace.model.listing.TraceInstructionsView; @@ -50,6 +52,8 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView protected class InstructionBlockAdder { private final Set

skipDelaySlots; + private final Range lifespan; + private final DBTraceGuestPlatform platform; private final InstructionBlock block; private final Address errorAddress; private final InstructionError conflict; @@ -57,19 +61,22 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView protected int count = 0; - private InstructionBlockAdder(Set
skipDelaySlots, InstructionBlock block, - Address errorAddress, InstructionError conflict, CodeUnit conflictCodeUnit) { + private InstructionBlockAdder(Set
skipDelaySlots, Range lifespan, + DBTraceGuestPlatform platform, InstructionBlock block, Address errorAddress, + InstructionError conflict, CodeUnit conflictCodeUnit) { this.skipDelaySlots = skipDelaySlots; + this.lifespan = lifespan; + this.platform = platform; this.block = block; this.errorAddress = errorAddress; this.conflict = conflict; this.conflictCodeUnit = conflictCodeUnit; } - protected Instruction doCreateInstruction(Range lifespan, Address address, + protected Instruction doCreateInstruction(Address address, InstructionPrototype prototype, Instruction protoInstr) { try { - Instruction created = doCreate(lifespan, address, prototype, protoInstr); + Instruction created = doCreate(lifespan, address, platform, prototype, protoInstr); // copy override settings to replacement instruction if (protoInstr.isFallThroughOverridden()) { created.setFallThrough(protoInstr.getFallThrough()); @@ -97,8 +104,7 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView * @param areDelaySlots * @return */ - protected Instruction doAddInstructions(Range lifespan, Iterator it, - boolean areDelaySlots) { + protected Instruction doAddInstructions(Iterator it, boolean areDelaySlots) { Instruction lastInstruction = null; while (it.hasNext()) { Instruction protoInstr = it.next(); @@ -139,10 +145,10 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView delayed.push(it.next()); } lastInstruction = replaceIfNotNull(lastInstruction, - doAddInstructions(lifespan, delayed.iterator(), true)); + doAddInstructions(delayed.iterator(), true)); } lastInstruction = - doCreateInstruction(lifespan, startAddress, prototype, protoInstr); + doCreateInstruction(startAddress, prototype, protoInstr); } if (errorAddress != null && conflictCodeUnit == null && errorAddress.compareTo(startAddress) <= 0) { @@ -179,9 +185,22 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView ctxSpace.setValue(language, newValue, tasr.getLifespan(), tasr.getRange()); } + protected boolean languagesAgree(DBTraceGuestPlatform platform, + InstructionPrototype prototype) { + if (platform == null) { + return prototype.getLanguage() == space.baseLanguage; + } + return prototype.getLanguage() == platform.getLanguage(); + } + protected DBTraceInstruction doCreate(Range lifespan, Address address, - InstructionPrototype prototype, ProcessorContextView context) + DBTraceGuestPlatform platform, InstructionPrototype prototype, + ProcessorContextView context) throws CodeUnitInsertionException, AddressOverflowException { + if (!languagesAgree(platform, prototype)) { + throw new IllegalArgumentException("Platform and prototype disagree in language"); + } + Address endAddress = address.addNoWrap(prototype.getLength() - 1); AddressRangeImpl createdRange = new AddressRangeImpl(address, endAddress); @@ -213,7 +232,7 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView doSetContexts(tasr, prototype.getLanguage(), context); DBTraceInstruction created = space.instructionMapSpace.put(tasr, null); - created.set(prototype, context); + created.set(platform, prototype, context); cacheForContaining.notifyNewEntry(lifespan, createdRange, created); cacheForSequence.notifyNewEntry(lifespan, createdRange, created); @@ -227,10 +246,13 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView @Override public DBTraceInstruction create(Range lifespan, Address address, - InstructionPrototype prototype, ProcessorContextView context) + TraceGuestPlatform platform, InstructionPrototype prototype, + ProcessorContextView context) throws CodeUnitInsertionException { + DBTraceGuestPlatform dbPlatform = space.manager.platformManager.assertMine(platform); try (LockHold hold = LockHold.lock(space.lock.writeLock())) { - DBTraceInstruction created = doCreate(lifespan, address, prototype, context); + DBTraceInstruction created = + doCreate(lifespan, address, dbPlatform, prototype, context); space.trace.setChanged(new TraceChangeRecord<>(TraceCodeChangeType.ADDED, space, created, created)); return created; @@ -254,23 +276,26 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView OverlappingObjectIterator.CODE_UNIT, existing, OverlappingObjectIterator.CODE_UNIT); } - protected InstructionBlockAdder startAddingBlock(long startSnap, Set
skipDelaySlots, - InstructionBlock block) { + protected InstructionBlockAdder startAddingBlock(Range lifespan, + Set
skipDelaySlots, DBTraceGuestPlatform platform, InstructionBlock block) { InstructionError conflict = block.getInstructionConflict(); if (conflict == null) { - return new InstructionBlockAdder(skipDelaySlots, block, null, null, null); + return new InstructionBlockAdder(skipDelaySlots, lifespan, platform, block, null, null, + null); } Address errorAddress = conflict.getInstructionAddress(); if (errorAddress == null) { return null; // The whole block is considered in error } if (!conflict.getInstructionErrorType().isConflict) { - return new InstructionBlockAdder(skipDelaySlots, block, errorAddress, conflict, null); + return new InstructionBlockAdder(skipDelaySlots, lifespan, platform, block, + errorAddress, conflict, null); } + long startSnap = DBTraceUtils.lowerEndpoint(lifespan); CodeUnit conflictCodeUnit = space.definedUnits.getAt(startSnap, conflict.getConflictAddress()); - return new InstructionBlockAdder(skipDelaySlots, block, errorAddress, conflict, - conflictCodeUnit); + return new InstructionBlockAdder(skipDelaySlots, lifespan, platform, block, errorAddress, + conflict, conflictCodeUnit); } /** @@ -359,8 +384,9 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView } @Override - public AddressSetView addInstructionSet(Range lifespan, InstructionSet instructionSet, - boolean overwrite) { + public AddressSetView addInstructionSet(Range lifespan, TraceGuestPlatform platform, + InstructionSet instructionSet, boolean overwrite) { + DBTraceGuestPlatform dbPlatform = space.manager.platformManager.assertMine(platform); // NOTE: Partly derived from CodeManager#addInstructions() // Attempted to factor more fluently AddressSet result = new AddressSet(); @@ -378,12 +404,12 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView // Add blocks for (InstructionBlock block : instructionSet) { - InstructionBlockAdder adder = startAddingBlock(startSnap, skipDelaySlots, block); + InstructionBlockAdder adder = + startAddingBlock(lifespan, skipDelaySlots, dbPlatform, block); if (adder == null) { continue; } - Instruction lastInstruction = - adder.doAddInstructions(lifespan, block.iterator(), false); + Instruction lastInstruction = adder.doAddInstructions(block.iterator(), false); block.setInstructionsAddedCount(adder.count); if (lastInstruction != null) { Address maxAddress = DBTraceCodeManager.instructionMax(lastInstruction, true); diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/UndefinedDBTraceData.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/UndefinedDBTraceData.java index 74b9624585..ea21d51139 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/UndefinedDBTraceData.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/UndefinedDBTraceData.java @@ -33,6 +33,7 @@ import ghidra.trace.database.memory.DBTraceMemorySpace; import ghidra.trace.database.space.DBTraceSpaceKey; import ghidra.trace.model.ImmutableTraceAddressSnapRange; import ghidra.trace.model.TraceAddressSnapRange; +import ghidra.trace.model.guest.TraceGuestPlatform; import ghidra.trace.model.listing.TraceData; import ghidra.trace.model.thread.TraceThread; import ghidra.trace.util.TraceAddressSpace; @@ -80,6 +81,11 @@ public class UndefinedDBTraceData implements DBTraceDataAdapter, DBTraceSpaceKey return trace.getBaseLanguage(); } + @Override + public TraceGuestPlatform getGuestPlatform() { + return null; + } + @Override public AddressRange getRange() { // TODO: Cache this? 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 15aa7d9b6d..90304a28d0 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 @@ -725,27 +725,25 @@ public abstract class AbstractDBTraceProgramViewListing implements TraceProgramV public Instruction createInstruction(Address addr, InstructionPrototype prototype, MemBuffer memBuf, ProcessorContextView context) throws CodeUnitInsertionException { // TODO: Why memBuf? Can it vary from program memory? - try (LockHold hold = program.trace.lockWrite()) { - return codeOperations.instructions() - .create(Range.atLeast(program.snap), addr, - prototype, context); - } + // TODO: Per-platform views? + return codeOperations.instructions() + .create(Range.atLeast(program.snap), addr, null, prototype, context); } @Override public AddressSetView addInstructions(InstructionSet instructionSet, boolean overwrite) throws CodeUnitInsertionException { + // TODO: Per-platform views? return codeOperations.instructions() - .addInstructionSet(Range.atLeast(program.snap), - instructionSet, overwrite); + .addInstructionSet(Range.atLeast(program.snap), null, instructionSet, + overwrite); } @Override public Data createData(Address addr, DataType dataType, int length) throws CodeUnitInsertionException { return codeOperations.definedData() - .create(Range.atLeast(program.snap), addr, dataType, - length); + .create(Range.atLeast(program.snap), addr, dataType, length); } @Override diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/stack/DBTraceObjectStack.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/stack/DBTraceObjectStack.java index bf04fbdaa4..a4c981d0aa 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/stack/DBTraceObjectStack.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/stack/DBTraceObjectStack.java @@ -181,10 +181,16 @@ public class DBTraceObjectStack implements TraceObjectStack, DBTraceObjectInterf protected TraceStackFrame doGetFrame(int level) { TargetObjectSchema schema = object.getTargetSchema(); PathPredicates matcher = schema.searchFor(TargetStackFrame.class, true); - matcher = matcher.applyKeys(PathUtils.makeIndex(level)); - return object.getSuccessors(computeSpan(), matcher) + PathPredicates decMatcher = matcher.applyKeys(PathUtils.makeIndex(level)); + PathPredicates hexMatcher = matcher.applyKeys("0x" + Integer.toHexString(level)); + Range span = computeSpan(); + return object.getSuccessors(span, decMatcher) .findAny() .map(p -> p.getDestination(object).queryInterface(TraceObjectStackFrame.class)) + .or(() -> object.getSuccessors(span, hexMatcher) + .findAny() + .map(p -> p.getDestination(object) + .queryInterface(TraceObjectStackFrame.class))) .orElse(null); } diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/stack/DBTraceObjectStackFrame.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/stack/DBTraceObjectStackFrame.java index ed25c063f3..4b8fb64e50 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/stack/DBTraceObjectStackFrame.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/stack/DBTraceObjectStackFrame.java @@ -65,7 +65,7 @@ public class DBTraceObjectStackFrame implements TraceObjectStackFrame, DBTraceOb } String index = PathUtils.parseIndex(k); try { - return Integer.parseInt(index, 10); // TODO: How to know the radix? + return Integer.decode(index); // TODO: Perhaps just have an attribute that is its level? } catch (NumberFormatException e) { diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/target/DBTraceObject.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/target/DBTraceObject.java index 19debaeaca..ffa3f42f13 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/target/DBTraceObject.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/target/DBTraceObject.java @@ -353,6 +353,32 @@ public class DBTraceObject extends DBAnnotatedObject implements TraceObject { } } + protected void collectNonRangedAttributes(List result) { + for (DBTraceObjectValue val : manager.valuesByTriple + .sub(new PrimaryTriple(this, "", Long.MIN_VALUE), true, + new PrimaryTriple(this, "[", Long.MIN_VALUE), false) + .values()) { + result.add(val); + } + for (DBTraceObjectValue val : manager.valuesByTriple + .tail(new PrimaryTriple(this, "\\", Long.MIN_VALUE), true) + .values()) { + if (val.getParent() != this) { + break; + } + result.add(val); + } + } + + protected void collectNonRangedElements(List result) { + for (DBTraceObjectValue val : manager.valuesByTriple + .sub(new PrimaryTriple(this, "[", Long.MIN_VALUE), true, + new PrimaryTriple(this, "\\", Long.MIN_VALUE), false) + .values()) { + result.add(val); + } + } + protected boolean doHasAnyNonRangedValues() { for (DBTraceObjectValue val : manager.valuesByTriple .tail(new PrimaryTriple(this, "", Long.MIN_VALUE), true) @@ -377,6 +403,38 @@ public class DBTraceObject extends DBAnnotatedObject implements TraceObject { } } + protected void collectRangedAttributes( + Collection result) { + for (DBTraceAddressSnapRangePropertyMapSpace space // + : manager.rangeValueMap.getActiveMemorySpaces()) { + for (DBTraceObjectAddressRangeValue val : space.values()) { + if (val.getParent() != this) { + continue; + } + if (!PathUtils.isName(val.getEntryKey())) { + continue; + } + result.add(val); + } + } + } + + protected void collectRangedElements( + Collection result) { + for (DBTraceAddressSnapRangePropertyMapSpace space // + : manager.rangeValueMap.getActiveMemorySpaces()) { + for (DBTraceObjectAddressRangeValue val : space.values()) { + if (val.getParent() != this) { + continue; + } + if (!PathUtils.isIndex(val.getEntryKey())) { + continue; + } + result.add(val); + } + } + } + protected boolean doHasAnyRangedValues() { for (DBTraceAddressSnapRangePropertyMapSpace space // : manager.rangeValueMap.getActiveMemorySpaces()) { @@ -415,45 +473,29 @@ public class DBTraceObject extends DBAnnotatedObject implements TraceObject { } } - protected Collection doGetElements() { - List result = new ArrayList<>(); - for (DBTraceObjectValue val : manager.valuesByTriple - .sub(new PrimaryTriple(this, "[", Long.MIN_VALUE), true, - new PrimaryTriple(this, "\\", Long.MIN_VALUE), false) - .values()) { - result.add(val); - } + protected Collection doGetElements() { + List result = new ArrayList<>(); + collectNonRangedElements(result); + collectRangedElements(result); return result; } @Override - public Collection getElements() { + public Collection getElements() { try (LockHold hold = manager.trace.lockRead()) { return doGetElements(); } } - protected Collection doGetAttributes() { - List result = new ArrayList<>(); - for (DBTraceObjectValue val : manager.valuesByTriple - .sub(new PrimaryTriple(this, "", Long.MIN_VALUE), true, - new PrimaryTriple(this, "[", Long.MIN_VALUE), false) - .values()) { - result.add(val); - } - for (DBTraceObjectValue val : manager.valuesByTriple - .tail(new PrimaryTriple(this, "\\", Long.MIN_VALUE), true) - .values()) { - if (val.getParent() != this) { - break; - } - result.add(val); - } + protected Collection doGetAttributes() { + List result = new ArrayList<>(); + collectNonRangedAttributes(result); + collectRangedAttributes(result); return result; } @Override - public Collection getAttributes() { + public Collection getAttributes() { try (LockHold hold = manager.trace.lockRead()) { return doGetAttributes(); } @@ -525,7 +567,7 @@ public class DBTraceObject extends DBAnnotatedObject implements TraceObject { return floor; } - protected Stream doGetNonRangedValues(Range span, String key, + protected Stream doGetOrderedNonRangedValues(Range span, String key, boolean forward) { DBCachedObjectIndex sub = manager.valuesByTriple.sub( new PrimaryTriple(this, key, DBTraceUtils.lowerEndpoint(span)), true, @@ -552,7 +594,7 @@ public class DBTraceObject extends DBAnnotatedObject implements TraceObject { return null; } - protected Stream doGetRangedValues(Range span, + protected Stream doGetOrderedRangedValues(Range span, String key, boolean forward) { Rectangle2DDirection dir = forward ? Rectangle2DDirection.BOTTOMMOST @@ -590,8 +632,8 @@ public class DBTraceObject extends DBAnnotatedObject implements TraceObject { protected Stream doGetOrderedValues(Range span, String key, boolean forward) { - Stream nrVals = doGetNonRangedValues(span, key, forward); - Stream rVals = doGetRangedValues(span, key, forward); + Stream nrVals = doGetOrderedNonRangedValues(span, key, forward); + Stream rVals = doGetOrderedRangedValues(span, key, forward); Comparator order = forward ? Comparator.naturalOrder() : Comparator.reverseOrder(); Comparator comparator = Comparator.comparing(v -> v.getMinSnap(), order); @@ -883,9 +925,14 @@ public class DBTraceObject extends DBAnnotatedObject implements TraceObject { manager.trace.setChanged(rec); for (TraceObjectInterface iface : ifaces.values()) { DBTraceObjectInterface dbIface = (DBTraceObjectInterface) iface; - TraceChangeRecord evt = dbIface.translateEvent(rec); - if (evt != null) { - manager.trace.setChanged(evt); + try { + TraceChangeRecord evt = dbIface.translateEvent(rec); + if (evt != null) { + manager.trace.setChanged(evt); + } + } + catch (Throwable t) { + Msg.error(this, "Error while translating event " + rec + " for interface " + iface); } } } diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/target/InternalOrderedSuccessorsVisitor.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/target/InternalOrderedSuccessorsVisitor.java index 6012fedf2d..9deced413c 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/target/InternalOrderedSuccessorsVisitor.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/target/InternalOrderedSuccessorsVisitor.java @@ -50,7 +50,7 @@ public class InternalOrderedSuccessorsVisitor implements SpanIntersectingVisitor // Singleton path, so if I match, no successor can return VisitResult.INCLUDE_FINISH; } - if (value.getChildOrNull() == null || predicates.successorCouldMatch(keyList, true)) { + if (value.getChildOrNull() == null || !predicates.successorCouldMatch(keyList, true)) { return VisitResult.EXCLUDE_FINISH; } return VisitResult.EXCLUDE_CONTINUE; diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/target/InternalSuccessorsRelativeVisitor.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/target/InternalSuccessorsRelativeVisitor.java index ec13c70842..0f40c644d3 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/target/InternalSuccessorsRelativeVisitor.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/target/InternalSuccessorsRelativeVisitor.java @@ -60,7 +60,7 @@ public class InternalSuccessorsRelativeVisitor implements SpanIntersectingVisito return Stream.empty(); } - Stream attrStream; + Stream attrStream; if (nextKeys.contains("")) { attrStream = object.doGetAttributes() .stream() @@ -70,7 +70,7 @@ public class InternalSuccessorsRelativeVisitor implements SpanIntersectingVisito attrStream = Stream.empty(); } - Stream elemStream; + Stream elemStream; if (nextKeys.contains("[]")) { elemStream = object.doGetElements() .stream() 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 3a9d798ee4..f44bebf149 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 @@ -32,7 +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.language.TraceLanguageManager; +import ghidra.trace.model.guest.TracePlatformManager; import ghidra.trace.model.listing.*; import ghidra.trace.model.memory.*; import ghidra.trace.model.modules.*; @@ -399,7 +399,7 @@ public interface Trace extends DataTypeManagerDomainObject { TraceEquateManager getEquateManager(); - TraceLanguageManager getLanguageManager(); + TracePlatformManager getPlatformManager(); TraceMemoryManager getMemoryManager(); diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/guest/TraceGuestPlatform.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/guest/TraceGuestPlatform.java new file mode 100644 index 0000000000..2bbd5f783d --- /dev/null +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/guest/TraceGuestPlatform.java @@ -0,0 +1,132 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.trace.model.guest; + +import ghidra.program.model.address.*; +import ghidra.program.model.lang.*; +import ghidra.program.model.mem.MemBuffer; +import ghidra.trace.model.Trace; +import ghidra.util.exception.CancelledException; +import ghidra.util.task.TaskMonitor; + +public interface TraceGuestPlatform { + /** + * Get the trace + * + * @return the trace + */ + Trace getTrace(); + + /** + * Get the language of the guest platform + * + * @return the language + */ + Language getLanguage(); + + /** + * Get the address factory of the guest platform + * + * @return the factory + */ + default AddressFactory getAddressFactory() { + return getLanguage().getAddressFactory(); + } + + /** + * Get the compiler of the guest platform + * + * @return the compiler spec + */ + CompilerSpec getCompilerSpec(); + + /** + * Add an adress mapping from host to guest + * + * @param hostStart the starting host address (mapped to guestStart) + * @param guestStart the starting guest address (mapped to hostStart) + * @param length the length of the range to map + * @return the mapped range + * @throws AddressOverflowException if length is too long for either start + */ + TraceGuestPlatformMappedRange addMappedRange(Address hostStart, Address guestStart, long length) + throws AddressOverflowException; + + /** + * Get the addresses in the host which are mapped to somewhere in the guest + * + * @return the address set + */ + AddressSetView getHostAddressSet(); + + /** + * Get the addresses in the guest which are mapped to somehere in the host + * + * @return the address set + */ + AddressSetView getGuestAddressSet(); + + /** + * Map an address from host to guest + * + * @param hostAddress the host address + * @return the guest address + */ + Address mapHostToGuest(Address hostAddress); + + /** + * Map an address from guest to host + * + * @param guestAddress the guest address + * @return the host address + */ + Address mapGuestToHost(Address guestAddress); + + /** + * Get a memory buffer, which presents the host bytes in the guest address space + * + *

+ * This, with pseudo-disassembly, is the primary mechanism for adding instructions in the guest + * language. + * + * @param snap the snap, up to which the most recent memory changes are presented + * @param guestAddress the starting address in the guest space + * @return the mapped memory buffer + */ + MemBuffer getMappedMemBuffer(long snap, Address guestAddress); + + /** + * Copy the given instruction set, but with addresses mapped from the guest space to the host + * space + * + *

+ * Instructions which do not map are silently ignored. If concerned, the caller ought to examine + * the resulting instruction set and/or the resulting address set after it is added to the + * trace. A single instruction cannot span two mapped ranges, even if the comprised bytes are + * consecutive in the guest space. Mapping such an instruction back into the host space would + * cause the instruction to be split in the middle, which is not possible. Thus, such + * instructions are silently ignored. + * + * @param set the instruction set in the guest space + * @return the instruction set in the host space + */ + InstructionSet mapGuestInstructionAddressesToHost(InstructionSet set); + + /** + * Remove the mapped language, including all code units of the language + */ + void delete(TaskMonitor monitor) throws CancelledException; +} diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/language/TraceGuestLanguageMappedRange.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/guest/TraceGuestPlatformMappedRange.java similarity index 83% rename from Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/language/TraceGuestLanguageMappedRange.java rename to Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/guest/TraceGuestPlatformMappedRange.java index 666eb38589..b382999be6 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/language/TraceGuestLanguageMappedRange.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/guest/TraceGuestPlatformMappedRange.java @@ -13,20 +13,23 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.trace.model.language; +package ghidra.trace.model.guest; import ghidra.program.model.address.Address; import ghidra.program.model.address.AddressRange; +import ghidra.program.model.lang.CompilerSpec; import ghidra.program.model.lang.Language; import ghidra.util.exception.CancelledException; import ghidra.util.task.TaskMonitor; -public interface TraceGuestLanguageMappedRange { +public interface TraceGuestPlatformMappedRange { Language getHostLanguage(); + CompilerSpec getHostCompilerSpec(); + AddressRange getHostRange(); - Language getGuestLanguage(); + TraceGuestPlatform getGuestPlatform(); AddressRange getGuestRange(); diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/guest/TracePlatformManager.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/guest/TracePlatformManager.java new file mode 100644 index 0000000000..3807f622b5 --- /dev/null +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/guest/TracePlatformManager.java @@ -0,0 +1,74 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.trace.model.guest; + +import java.util.Collection; + +import ghidra.program.model.lang.CompilerSpec; +import ghidra.program.model.lang.Language; + +/** + * Allows the addition of "guest platforms" for disassembling in multiple languages. + * + *

+ * TODO: Allow the placement of data units with alternative data organization. + */ +public interface TracePlatformManager { + /** + * Get the base language of the trace + * + * @return the language + */ + Language getBaseLanguage(); + + /** + * Get the base compiler spec of the trace + * + * @return the compiler spec + */ + CompilerSpec getBaseCompilerSpec(); + + /** + * Add a guest platform + * + * @param compilerSpec the compiler spec, which cannot be the base compiler spec + * @return the new platform + */ + TraceGuestPlatform addGuestPlatform(CompilerSpec compilerSpec); + + /** + * Get the guest platform for the given compiler spec + * + * @param compilerSpec the compiler spec. For the base compiler spec, this will return null. + * @return the platform, if found, or null + */ + TraceGuestPlatform getGuestPlatform(CompilerSpec compilerSpec); + + /** + * Get or add a platform for the given compiler spec + * + * @param compilerSpec the compiler spec + * @return the new or existing platform, or null if compiler spec is the base compiler spec + */ + TraceGuestPlatform getOrAddGuestPlatform(CompilerSpec compilerSpec); + + /** + * Get all guest platforms + * + * @return the collection of platforms + */ + Collection getGuestPlatforms(); +} diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/language/TraceGuestLanguage.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/language/TraceGuestLanguage.java deleted file mode 100644 index 24227e3795..0000000000 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/language/TraceGuestLanguage.java +++ /dev/null @@ -1,71 +0,0 @@ -/* ### - * IP: GHIDRA - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package ghidra.trace.model.language; - -import ghidra.program.model.address.*; -import ghidra.program.model.lang.InstructionSet; -import ghidra.program.model.lang.Language; -import ghidra.program.model.mem.MemBuffer; -import ghidra.util.exception.CancelledException; -import ghidra.util.task.TaskMonitor; - -public interface TraceGuestLanguage { - Language getLanguage(); - - TraceGuestLanguageMappedRange addMappedRange(Address hostStart, Address guestStart, long length) - throws AddressOverflowException; - - AddressSetView getHostAddressSet(); - - AddressSetView getGuestAddressSet(); - - Address mapHostToGuest(Address hostAddress); - - Address mapGuestToHost(Address guestAddress); - - /** - * Get a memory buffer which presents the host bytes in the guest address space - * - * This, with pseudo-disassembly, is the primary mechanism for adding instructions in the guest - * language. - * - * @param snap the snap, up to which the most recent memory changes are presented - * @param guestAddress the starting address in the guest space - * @return the mapped memory buffer - */ - MemBuffer getMappedMemBuffer(long snap, Address guestAddress); - - /** - * Copy the given instruction set, but with addresses mapped from the guest space to the host - * space - * - * Instructions which do not mapped are silently ignored. If concerned, the caller ought to - * examine the resulting instruction set and/or the resulting address set after it is added to - * the trace. A single instruction cannot span two mapped ranges, even if the comprised bytes - * are consecutive in the guest space. Mapping such an instruction back into the host space - * would cause the instruction to be split in the middle, which is not possible. Thus, such - * instructions are silently ignored. - * - * @param set the instruction set in the guest space - * @return the instruction set in the host space - */ - InstructionSet mapGuestInstructionAddressesToHost(InstructionSet set); - - /** - * Remove the mapped language, including all code units of the language - */ - void delete(TaskMonitor monitor) throws CancelledException; -} diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/listing/TraceCodeUnit.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/listing/TraceCodeUnit.java index 33e3cdd693..d01b9d3290 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/listing/TraceCodeUnit.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/listing/TraceCodeUnit.java @@ -25,6 +25,7 @@ import ghidra.program.model.listing.CodeUnit; import ghidra.program.model.util.TypeMismatchException; import ghidra.trace.model.Trace; import ghidra.trace.model.TraceAddressSnapRange; +import ghidra.trace.model.guest.TraceGuestPlatform; import ghidra.trace.model.program.TraceProgramView; import ghidra.trace.model.symbol.TraceReference; import ghidra.trace.model.thread.TraceThread; @@ -42,6 +43,13 @@ public interface TraceCodeUnit extends CodeUnit { */ Trace getTrace(); + /** + * If the unit is for a guest platform, get it + * + * @return the guest platform, or null if it's for the host platform + */ + TraceGuestPlatform getGuestPlatform(); + @Override TraceProgramView getProgram(); diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/listing/TraceInstructionsView.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/listing/TraceInstructionsView.java index 6932d1ad7a..adb00b7bbd 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/listing/TraceInstructionsView.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/listing/TraceInstructionsView.java @@ -21,21 +21,37 @@ import ghidra.program.model.address.Address; import ghidra.program.model.address.AddressSetView; import ghidra.program.model.lang.*; import ghidra.program.model.util.CodeUnitInsertionException; +import ghidra.trace.model.guest.TraceGuestPlatform; public interface TraceInstructionsView extends TraceBaseDefinedUnitsView { - TraceInstruction create(Range lifespan, Address address, InstructionPrototype prototype, - ProcessorContextView context) throws CodeUnitInsertionException; + /** + * Create an instruction + * + * @param lifespan the lifespan for the instruction unit + * @param address the starting address of the instruction + * @param platform the optional guest platform, null for the host + * @param prototype the instruction prototype + * @param context the input disassembly context for the instruction + * @return the new instruction + * @throws CodeUnitInsertionException if the instruction cannot be created + */ + TraceInstruction create(Range lifespan, Address address, TraceGuestPlatform platform, + InstructionPrototype prototype, ProcessorContextView context) + throws CodeUnitInsertionException; /** - * TODO + * Create several instructions * - * NOTE: Does not throw {@link CodeUnitInsertionException}. Conflicts are instead recorded in - * the {@code instructionSet} + *

+ * NOTE: This does not throw {@link CodeUnitInsertionException}. Conflicts are instead + * recorded in the {@code instructionSet}. * + * @param lifespan the lifespan for all instruction units + * @param platform the optional guest platform, null for the host * @param instructionSet the set of instructions to add - * @param overwrite {@code true} to replace conflicting instructions - * @return the address set of instructions actually added + * @param overwrite true to replace conflicting instructions + * @return the (host) address set of instructions actually added */ - AddressSetView addInstructionSet(Range lifespan, InstructionSet instructionSet, - boolean overwrite); + AddressSetView addInstructionSet(Range lifespan, TraceGuestPlatform platform, + InstructionSet instructionSet, boolean overwrite); } diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/util/DefaultTraceTimeViewport.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/util/DefaultTraceTimeViewport.java index 4dec7b42e3..d59c0baa52 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/util/DefaultTraceTimeViewport.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/util/DefaultTraceTimeViewport.java @@ -106,7 +106,7 @@ public class DefaultTraceTimeViewport implements TraceTimeViewport { * may need the DB's lock, esp., considering user callbacks, then it must first acquire * the DB lock. */ - protected final List> ordered = new ArrayList<>(List.of(Range.singleton(0L))); + protected final List> ordered = new ArrayList<>(); protected final RangeSet spanSet = TreeRangeSet.create(); protected final ForSnapshotsListener listener = new ForSnapshotsListener(); protected final ListenerSet changeListeners = new ListenerSet<>(Runnable.class); @@ -114,6 +114,10 @@ public class DefaultTraceTimeViewport implements TraceTimeViewport { protected long snap = 0; public DefaultTraceTimeViewport(Trace trace) { + Range zero = Range.singleton(0L); + spanSet.add(zero); + ordered.add(zero); + this.trace = trace; trace.addCloseListener(listener); trace.addListener(listener); 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 3f9aa4c654..8c5c042843 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 @@ -26,7 +26,8 @@ import java.nio.charset.Charset; import java.nio.charset.CharsetEncoder; import java.nio.file.Files; import java.nio.file.Path; -import java.util.*; +import java.util.Collection; +import java.util.List; import com.google.common.collect.Range; @@ -45,13 +46,12 @@ import ghidra.program.model.symbol.SourceType; import ghidra.program.model.util.CodeUnitInsertionException; import ghidra.program.util.DefaultLanguageService; import ghidra.trace.database.bookmark.*; -import ghidra.trace.database.language.DBTraceGuestLanguage; import ghidra.trace.database.listing.*; import ghidra.trace.database.memory.DBTraceMemoryManager; import ghidra.trace.database.symbol.DBTraceReference; import ghidra.trace.database.thread.DBTraceThreadManager; import ghidra.trace.model.*; -import ghidra.trace.model.language.TraceGuestLanguage; +import ghidra.trace.model.guest.TraceGuestPlatform; import ghidra.trace.model.thread.TraceThread; import ghidra.util.Msg; import ghidra.util.database.DBOpenMode; @@ -102,7 +102,7 @@ public class ToyDBTraceBuilder implements AutoCloseable { return addr(language, offset); } - public Address addr(TraceGuestLanguage lang, long offset) { + public Address addr(TraceGuestPlatform lang, long offset) { return lang.getLanguage().getDefaultSpace().getAddress(offset); } @@ -114,7 +114,7 @@ public class ToyDBTraceBuilder implements AutoCloseable { return data(language, offset); } - public Address data(TraceGuestLanguage lang, long offset) { + public Address data(TraceGuestPlatform lang, long offset) { return data(lang.getLanguage(), offset); } @@ -150,11 +150,11 @@ public class ToyDBTraceBuilder implements AutoCloseable { return drng(language, start, end); } - public AddressRange range(TraceGuestLanguage lang, long start, long end) { + public AddressRange range(TraceGuestPlatform lang, long start, long end) { return range(lang.getLanguage(), start, end); } - public AddressRange drng(TraceGuestLanguage lang, long start, long end) { + public AddressRange drng(TraceGuestPlatform lang, long start, long end) { return drng(lang.getLanguage(), start, end); } @@ -246,9 +246,10 @@ public class ToyDBTraceBuilder implements AutoCloseable { } public DBTraceInstruction addInstruction(long snap, Address start, - @SuppressWarnings("hiding") Language language) throws CodeUnitInsertionException { + TraceGuestPlatform guest) throws CodeUnitInsertionException { DBTraceMemoryManager memory = trace.getMemoryManager(); DBTraceCodeManager code = trace.getCodeManager(); + Language language = guest == null ? this.language : guest.getLanguage(); Disassembler dis = Disassembler.getDisassembler(language, language.getAddressFactory(), new ConsoleTaskMonitor(), msg -> Msg.info(this, "Listener: " + msg)); RegisterValue defaultContextValue = trace.getRegisterContextManager() @@ -256,27 +257,25 @@ public class ToyDBTraceBuilder implements AutoCloseable { .getDefaultDisassemblyContext(); MemBuffer memBuf; - if (language == null || Objects.equals(this.language, language)) { + if (guest == null) { memBuf = memory.getBufferAt(snap, start); } else { - DBTraceGuestLanguage guest = trace.getLanguageManager().getGuestLanguage(language); memBuf = guest.getMappedMemBuffer(snap, guest.mapHostToGuest(start)); } InstructionBlock block = dis.pseudoDisassembleBlock(memBuf, defaultContextValue, 1); Instruction pseudoIns = block.iterator().next(); return code.instructions() - .create(Range.atLeast(snap), start, pseudoIns.getPrototype(), - pseudoIns); + .create(Range.atLeast(snap), start, guest, pseudoIns.getPrototype(), pseudoIns); } public DBTraceInstruction addInstruction(long snap, Address start, - @SuppressWarnings("hiding") Language language, ByteBuffer buf) + TraceGuestPlatform guest, ByteBuffer buf) throws CodeUnitInsertionException { int length = buf.remaining(); DBTraceMemoryManager memory = trace.getMemoryManager(); memory.putBytes(snap, start, buf); - DBTraceInstruction instruction = addInstruction(snap, start, language); + DBTraceInstruction instruction = addInstruction(snap, start, guest); assertEquals(length, instruction.getLength()); return instruction; } @@ -345,4 +344,9 @@ public class ToyDBTraceBuilder implements AutoCloseable { public Language getLanguage(String id) throws LanguageNotFoundException { return languageService.getLanguage(new LanguageID(id)); } + + public CompilerSpec getCompiler(String langID, String compID) + throws CompilerSpecNotFoundException, LanguageNotFoundException { + return getLanguage(langID).getCompilerSpecByID(new CompilerSpecID(compID)); + } } diff --git a/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/database/language/DBTraceLanguageManagerTest.java b/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/database/guest/DBTracePlatformManagerTest.java similarity index 58% rename from Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/database/language/DBTraceLanguageManagerTest.java rename to Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/database/guest/DBTracePlatformManagerTest.java index 38858d115d..77e44db80d 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/database/language/DBTraceLanguageManagerTest.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/database/guest/DBTracePlatformManagerTest.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.trace.database.language; +package ghidra.trace.database.guest; import static org.junit.Assert.*; @@ -23,24 +23,21 @@ import java.util.*; import org.junit.*; -import ghidra.program.model.address.AddressOverflowException; -import ghidra.program.model.lang.LanguageNotFoundException; import ghidra.test.AbstractGhidraHeadlessIntegrationTest; import ghidra.trace.database.ToyDBTraceBuilder; -import ghidra.trace.model.language.TraceGuestLanguage; +import ghidra.trace.database.guest.*; +import ghidra.trace.model.guest.TraceGuestPlatform; import ghidra.util.database.UndoableTransaction; -import ghidra.util.exception.CancelledException; -import ghidra.util.exception.VersionException; import ghidra.util.task.ConsoleTaskMonitor; -public class DBTraceLanguageManagerTest extends AbstractGhidraHeadlessIntegrationTest { +public class DBTracePlatformManagerTest extends AbstractGhidraHeadlessIntegrationTest { protected ToyDBTraceBuilder b; - protected DBTraceLanguageManager manager; + protected DBTracePlatformManager manager; @Before public void setUpLanguageManagerTest() throws IOException { b = new ToyDBTraceBuilder("Testing", "Toy:BE:64:default"); - manager = b.trace.getLanguageManager(); + manager = b.trace.getPlatformManager(); } @After @@ -55,82 +52,108 @@ public class DBTraceLanguageManagerTest extends AbstractGhidraHeadlessIntegratio } @Test - public void testAddGuestLanguage() throws LanguageNotFoundException { + public void testGetBaseCompilerSpec() { + assertEquals("default", manager.getBaseCompilerSpec().getCompilerSpecID().getIdAsString()); + } + + @Test + public void testAddGuestPlatform() throws Throwable { try (UndoableTransaction tid = b.startTransaction()) { assertEquals(0, manager.languageStore.getRecordCount()); - manager.addGuestLanguage(b.getLanguage("x86:LE:32:default")); + assertEquals(0, manager.platformStore.getRecordCount()); + manager.addGuestPlatform(b.getCompiler("x86:LE:32:default", "gcc")); assertEquals(1, manager.languageStore.getRecordCount()); - - try { // Cannot add base language as guest - manager.addGuestLanguage(b.getLanguage("Toy:BE:64:default")); - fail(); - } - catch (IllegalArgumentException e) { - // pass - } + assertEquals(1, manager.platformStore.getRecordCount()); } } @Test - public void testGetGuestLanguages() throws LanguageNotFoundException { - DBTraceGuestLanguage guest; + public void testAddGuestPlatformHostCompilerErr() throws Throwable { try (UndoableTransaction tid = b.startTransaction()) { - assertTrue(manager.getGuestLanguages().isEmpty()); - guest = manager.addGuestLanguage(b.getLanguage("x86:LE:32:default")); + manager.addGuestPlatform(b.getLanguage("Toy:BE:64:default").getDefaultCompilerSpec()); + fail(); + } + catch (IllegalArgumentException e) { + // pass, pending consistency check } - assertEquals(Set.of(guest), new HashSet<>(manager.getGuestLanguages())); + assertEquals(0, manager.languageStore.getRecordCount()); + assertEquals(0, manager.platformStore.getRecordCount()); + assertTrue(manager.getGuestPlatforms().isEmpty()); } @Test - public void testAddLanguageThenUndo() throws IOException { + public void testAddGuestPlatformHostLanguage() throws Throwable { try (UndoableTransaction tid = b.startTransaction()) { - manager.addGuestLanguage(b.getLanguage("x86:LE:32:default")); + assertEquals(0, manager.languageStore.getRecordCount()); + assertEquals(0, manager.platformStore.getRecordCount()); + manager.addGuestPlatform(b.getCompiler("Toy:BE:64:default", "long8")); + assertEquals(0, manager.languageStore.getRecordCount()); + assertEquals(1, manager.platformStore.getRecordCount()); + } + } + + @Test + public void testGetGuestPlatforms() throws Throwable { + DBTraceGuestPlatform guest; + try (UndoableTransaction tid = b.startTransaction()) { + assertTrue(manager.getGuestPlatforms().isEmpty()); + guest = manager.addGuestPlatform(b.getCompiler("x86:LE:32:default", "gcc")); + } + + assertEquals(Set.of(guest), new HashSet<>(manager.getGuestPlatforms())); + } + + @Test + public void testAddPlatformThenUndo() throws Throwable { + try (UndoableTransaction tid = b.startTransaction()) { + manager.addGuestPlatform(b.getCompiler("x86:LE:32:default", "gcc")); } b.trace.undo(); - assertTrue(manager.getGuestLanguages().isEmpty()); + assertTrue(manager.getGuestPlatforms().isEmpty()); } @Test - public void testAddLanguageThenSaveAndLoad() - throws CancelledException, IOException, VersionException { + public void testAddPlatformThenSaveAndLoad() throws Throwable { try (UndoableTransaction tid = b.startTransaction()) { - manager.addGuestLanguage(b.getLanguage("x86:LE:32:default")); + manager.addGuestPlatform(b.getCompiler("x86:LE:32:default", "gcc")); } File saved = b.save(); try (ToyDBTraceBuilder r = new ToyDBTraceBuilder(saved)) { - Collection guestLanguages = - r.trace.getLanguageManager().getGuestLanguages(); - assertEquals(1, guestLanguages.size()); + Collection guestPlatforms = + r.trace.getPlatformManager().getGuestPlatforms(); + assertEquals(1, guestPlatforms.size()); + TraceGuestPlatform platform = guestPlatforms.iterator().next(); assertEquals("x86:LE:32:default", - guestLanguages.iterator().next().getLanguage().getLanguageID().getIdAsString()); + platform.getLanguage().getLanguageID().getIdAsString()); + assertEquals("gcc", platform.getCompilerSpec().getCompilerSpecID().getIdAsString()); } } @Test - public void testDeleteGuestLanguage() throws LanguageNotFoundException, CancelledException { - DBTraceGuestLanguage guest; + public void testDeleteGuestPlatform() throws Throwable { + DBTraceGuestPlatform guest; try (UndoableTransaction tid = b.startTransaction()) { - guest = manager.addGuestLanguage(b.getLanguage("x86:LE:32:default")); + guest = manager.addGuestPlatform(b.getCompiler("x86:LE:32:default", "gcc")); } try (UndoableTransaction tid = b.startTransaction()) { guest.delete(new ConsoleTaskMonitor()); } - assertEquals(0, manager.languageStore.getRecordCount()); - assertTrue(manager.entriesByLanguage.isEmpty()); + assertEquals(0, manager.platformStore.getRecordCount()); + assertTrue(manager.platformsByCompiler.isEmpty()); } @Test - public void testAddMappedRange() throws LanguageNotFoundException, AddressOverflowException { + public void testAddMappedRange() throws Throwable { try (UndoableTransaction tid = b.startTransaction()) { - DBTraceGuestLanguage guest = - manager.addGuestLanguage(b.getLanguage("x86:LE:32:default")); + DBTraceGuestPlatform guest = + manager.addGuestPlatform(b.getCompiler("x86:LE:32:default", "gcc")); assertEquals(0, manager.rangeMappingStore.getRecordCount()); guest.addMappedRange(b.addr(0x01000000), b.addr(guest, 0x02000000), 0x1000); @@ -155,11 +178,10 @@ public class DBTraceLanguageManagerTest extends AbstractGhidraHeadlessIntegratio } @Test - public void testGetHostAndGuestAddressSet() - throws LanguageNotFoundException, AddressOverflowException { + public void testGetHostAndGuestAddressSet() throws Throwable { try (UndoableTransaction tid = b.startTransaction()) { - DBTraceGuestLanguage guest = - manager.addGuestLanguage(b.getLanguage("x86:LE:32:default")); + DBTraceGuestPlatform guest = + manager.addGuestPlatform(b.getCompiler("x86:LE:32:default", "gcc")); assertEquals(b.set(), guest.getHostAddressSet()); guest.addMappedRange(b.addr(0x01000000), b.addr(guest, 0x02000000), 0x1000); @@ -169,10 +191,10 @@ public class DBTraceLanguageManagerTest extends AbstractGhidraHeadlessIntegratio } @Test - public void testMapHostToGuest() throws LanguageNotFoundException, AddressOverflowException { + public void testMapHostToGuest() throws Throwable { try (UndoableTransaction tid = b.startTransaction()) { - DBTraceGuestLanguage guest = - manager.addGuestLanguage(b.getLanguage("x86:LE:32:default")); + DBTraceGuestPlatform guest = + manager.addGuestPlatform(b.getCompiler("x86:LE:32:default", "gcc")); guest.addMappedRange(b.addr(0x01000000), b.addr(guest, 0x02000000), 0x1000); assertNull(guest.mapHostToGuest(b.addr(0x00000000))); @@ -185,10 +207,10 @@ public class DBTraceLanguageManagerTest extends AbstractGhidraHeadlessIntegratio } @Test - public void testMapGuestToHost() throws LanguageNotFoundException, AddressOverflowException { + public void testMapGuestToHost() throws Throwable { try (UndoableTransaction tid = b.startTransaction()) { - DBTraceGuestLanguage guest = - manager.addGuestLanguage(b.getLanguage("x86:LE:32:default")); + DBTraceGuestPlatform guest = + manager.addGuestPlatform(b.getCompiler("x86:LE:32:default", "gcc")); guest.addMappedRange(b.addr(0x01000000), b.addr(guest, 0x02000000), 0x1000); assertNull(guest.mapGuestToHost(b.addr(0x00000000))); @@ -201,30 +223,28 @@ public class DBTraceLanguageManagerTest extends AbstractGhidraHeadlessIntegratio } @Test - public void testAddMappedRangeThenSaveAndLoad() - throws AddressOverflowException, CancelledException, IOException, VersionException { + public void testAddMappedRangeThenSaveAndLoad() throws Throwable { try (UndoableTransaction tid = b.startTransaction()) { - DBTraceGuestLanguage guest = - manager.addGuestLanguage(b.getLanguage("x86:LE:32:default")); + DBTraceGuestPlatform guest = + manager.addGuestPlatform(b.getCompiler("x86:LE:32:default", "gcc")); guest.addMappedRange(b.addr(0x01000000), b.addr(guest, 0x02000000), 0x1000); } File saved = b.save(); try (ToyDBTraceBuilder r = new ToyDBTraceBuilder(saved)) { - TraceGuestLanguage guest = - r.trace.getLanguageManager().getGuestLanguages().iterator().next(); + TraceGuestPlatform guest = + r.trace.getPlatformManager().getGuestPlatforms().iterator().next(); assertEquals(b.addr(guest, 0x02000800), guest.mapHostToGuest(b.addr(0x01000800))); } } @Test - public void testMappedRangeGetHostLanguage() - throws LanguageNotFoundException, AddressOverflowException { + public void testMappedRangeGetHostLanguage() throws Throwable { try (UndoableTransaction tid = b.startTransaction()) { - DBTraceGuestLanguage guest = - manager.addGuestLanguage(b.getLanguage("x86:LE:32:default")); - DBTraceGuestLanguageMappedRange range = + DBTraceGuestPlatform guest = + manager.addGuestPlatform(b.getCompiler("x86:LE:32:default", "gcc")); + DBTraceGuestPlatformMappedRange range = guest.addMappedRange(b.addr(0x01000000), b.addr(guest, 0x02000000), 0x1000); assertEquals("Toy:BE:64:default", range.getHostLanguage().getLanguageID().getIdAsString()); @@ -232,49 +252,44 @@ public class DBTraceLanguageManagerTest extends AbstractGhidraHeadlessIntegratio } @Test - public void testMappedRangeGetHostRange() - throws LanguageNotFoundException, AddressOverflowException { + public void testMappedRangeGetHostRange() throws Throwable { try (UndoableTransaction tid = b.startTransaction()) { - DBTraceGuestLanguage guest = - manager.addGuestLanguage(b.getLanguage("x86:LE:32:default")); - DBTraceGuestLanguageMappedRange range = + DBTraceGuestPlatform guest = + manager.addGuestPlatform(b.getCompiler("x86:LE:32:default", "gcc")); + DBTraceGuestPlatformMappedRange range = guest.addMappedRange(b.addr(0x01000000), b.addr(guest, 0x02000000), 0x1000); assertEquals(b.range(0x01000000, 0x01000fff), range.getHostRange()); } } @Test - public void testMappedRangeGetGuestLanguage() - throws LanguageNotFoundException, AddressOverflowException { + public void testMappedRangeGetGuestPlatform() throws Throwable { try (UndoableTransaction tid = b.startTransaction()) { - DBTraceGuestLanguage guest = - manager.addGuestLanguage(b.getLanguage("x86:LE:32:default")); - DBTraceGuestLanguageMappedRange range = + DBTraceGuestPlatform guest = + manager.addGuestPlatform(b.getCompiler("x86:LE:32:default", "gcc")); + DBTraceGuestPlatformMappedRange range = guest.addMappedRange(b.addr(0x01000000), b.addr(guest, 0x02000000), 0x1000); - assertEquals("x86:LE:32:default", - range.getGuestLanguage().getLanguageID().getIdAsString()); + assertEquals(guest, range.getGuestPlatform()); } } @Test - public void testMappedRangeGetGuestRange() - throws LanguageNotFoundException, AddressOverflowException { + public void testMappedRangeGetGuestRange() throws Throwable { try (UndoableTransaction tid = b.startTransaction()) { - DBTraceGuestLanguage guest = - manager.addGuestLanguage(b.getLanguage("x86:LE:32:default")); - DBTraceGuestLanguageMappedRange range = + DBTraceGuestPlatform guest = + manager.addGuestPlatform(b.getCompiler("x86:LE:32:default", "gcc")); + DBTraceGuestPlatformMappedRange range = guest.addMappedRange(b.addr(0x01000000), b.addr(guest, 0x02000000), 0x1000); assertEquals(b.range(guest, 0x02000000, 0x02000fff), range.getGuestRange()); } } @Test - public void testDeleteMappedRange() - throws LanguageNotFoundException, AddressOverflowException, CancelledException { + public void testDeleteMappedRange() throws Throwable { try (UndoableTransaction tid = b.startTransaction()) { - DBTraceGuestLanguage guest = - manager.addGuestLanguage(b.getLanguage("x86:LE:32:default")); - DBTraceGuestLanguageMappedRange range = + DBTraceGuestPlatform guest = + manager.addGuestPlatform(b.getCompiler("x86:LE:32:default", "gcc")); + DBTraceGuestPlatformMappedRange range = guest.addMappedRange(b.addr(0x01000000), b.addr(guest, 0x02000000), 0x1000); assertNotNull(guest.mapHostToGuest(b.addr(0x01000800))); // Sanity check assertNotNull(guest.mapGuestToHost(b.addr(guest, 0x02000800))); // Sanity check @@ -291,12 +306,11 @@ public class DBTraceLanguageManagerTest extends AbstractGhidraHeadlessIntegratio } @Test - public void testDeleteMappedRangeThenUndo() - throws AddressOverflowException, IOException, CancelledException { - DBTraceGuestLanguage guest; - DBTraceGuestLanguageMappedRange range; + public void testDeleteMappedRangeThenUndo() throws Throwable { + DBTraceGuestPlatform guest; + DBTraceGuestPlatformMappedRange range; try (UndoableTransaction tid = b.startTransaction()) { - guest = manager.addGuestLanguage(b.getLanguage("x86:LE:32:default")); + guest = manager.addGuestPlatform(b.getCompiler("x86:LE:32:default", "gcc")); range = guest.addMappedRange(b.addr(0x01000000), b.addr(guest, 0x02000000), 0x1000); assertNotNull(guest.mapHostToGuest(b.addr(0x01000800))); // Sanity check assertNotNull(guest.mapGuestToHost(b.addr(guest, 0x02000800))); // Sanity check @@ -310,19 +324,17 @@ public class DBTraceLanguageManagerTest extends AbstractGhidraHeadlessIntegratio b.trace.undo(); - guest = manager.getGuestLanguage(b.getLanguage("x86:LE:32:default")); - + guest = manager.getGuestPlatform(b.getCompiler("x86:LE:32:default", "gcc")); assertNotNull(guest.mapHostToGuest(b.addr(0x01000800))); assertNotNull(guest.mapGuestToHost(b.addr(guest, 0x02000800))); } @Test - public void testDeleteGuestLanguageDeletesMappedRanges() - throws LanguageNotFoundException, AddressOverflowException, CancelledException { + public void testDeleteGuestPlatformDeletesMappedRanges() throws Throwable { // TODO: Check that it also deletes code units - DBTraceGuestLanguage guest; + DBTraceGuestPlatform guest; try (UndoableTransaction tid = b.startTransaction()) { - guest = manager.addGuestLanguage(b.getLanguage("x86:LE:32:default")); + guest = manager.addGuestPlatform(b.getCompiler("x86:LE:32:default", "gcc")); guest.addMappedRange(b.addr(0x01000000), b.addr(guest, 0x02000000), 0x1000); } @@ -333,12 +345,11 @@ public class DBTraceLanguageManagerTest extends AbstractGhidraHeadlessIntegratio } @Test - public void testDeleteGuestLanguageThenUndo() - throws AddressOverflowException, CancelledException, IOException { + public void testDeleteGuestPlatformThenUndo() throws Throwable { // TODO: Check that it also deletes code units - DBTraceGuestLanguage guest; + DBTraceGuestPlatform guest; try (UndoableTransaction tid = b.startTransaction()) { - guest = manager.addGuestLanguage(b.getLanguage("x86:LE:32:default")); + guest = manager.addGuestPlatform(b.getCompiler("x86:LE:32:default", "gcc")); guest.addMappedRange(b.addr(0x01000000), b.addr(guest, 0x02000000), 0x1000); } @@ -348,7 +359,7 @@ public class DBTraceLanguageManagerTest extends AbstractGhidraHeadlessIntegratio b.trace.undo(); - guest = manager.getGuestLanguage(b.getLanguage("x86:LE:32:default")); + guest = manager.getGuestPlatform(b.getCompiler("x86:LE:32:default", "gcc")); assertEquals(b.addr(guest, 0x02000800), guest.mapHostToGuest(b.addr(0x01000800))); } } 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 52c0525161..2d4c1b19d7 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 @@ -40,7 +40,7 @@ import ghidra.program.model.util.CodeUnitInsertionException; import ghidra.test.AbstractGhidraHeadlessIntegrationTest; import ghidra.trace.database.ToyDBTraceBuilder; import ghidra.trace.database.context.DBTraceRegisterContextManager; -import ghidra.trace.database.language.*; +import ghidra.trace.database.guest.*; import ghidra.trace.model.ImmutableTraceAddressSnapRange; import ghidra.trace.model.listing.*; import ghidra.trace.model.stack.TraceStack; @@ -187,7 +187,7 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes @Test public void testAddInstruction() throws CodeUnitInsertionException { try (UndoableTransaction tid = b.startTransaction()) { - b.addInstruction(0, b.addr(0x4004), b.language, b.buf(0xf4, 0)); + b.addInstruction(0, b.addr(0x4004), null, b.buf(0xf4, 0)); } } @@ -196,7 +196,7 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes try (UndoableTransaction tid = b.startTransaction()) { b.trace.getMemoryManager().putBytes(10, b.addr(0x4001), b.buf(0xaa)); TraceInstruction i4000 = - b.addInstruction(0, b.addr(0x4000), b.language, b.buf(0xf4, 0)); + b.addInstruction(0, b.addr(0x4000), null, b.buf(0xf4, 0)); assertEquals(Range.closed(0L, 9L), i4000.getLifespan()); } } @@ -206,15 +206,15 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes try (UndoableTransaction tid = b.startTransaction()) { b.trace.getMemoryManager().putBytes(-5L, b.addr(0x4001), b.buf(0xaa)); TraceInstruction i4000 = - b.addInstruction(-10, b.addr(0x4000), b.language, b.buf(0xf4, 0)); + b.addInstruction(-10, b.addr(0x4000), null, b.buf(0xf4, 0)); assertEquals(Range.closed(-10L, -6L), i4000.getLifespan()); TraceInstruction i4004 = - b.addInstruction(-1, b.addr(0x4004), b.language, b.buf(0xf4, 0)); + b.addInstruction(-1, b.addr(0x4004), null, b.buf(0xf4, 0)); assertEquals(Range.closed(-1L, -1L), i4004.getLifespan()); TraceInstruction i4008 = - b.addInstruction(-10, b.addr(0x4008), b.language, b.buf(0xf4, 0)); + b.addInstruction(-10, b.addr(0x4008), null, b.buf(0xf4, 0)); assertEquals(Range.closed(-10L, -1L), i4008.getLifespan()); } } @@ -223,7 +223,7 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes public void testPutBytesTruncatesInstruction() throws CodeUnitInsertionException { try (UndoableTransaction tid = b.startTransaction()) { TraceInstruction i4000 = - b.addInstruction(0, b.addr(0x4000), b.language, b.buf(0xf4, 0)); + b.addInstruction(0, b.addr(0x4000), null, b.buf(0xf4, 0)); assertEquals(b.addr(0x4001), i4000.getMaxAddress()); assertEquals(Range.atLeast(0L), i4000.getLifespan()); b.trace.getMemoryManager().putBytes(10, b.addr(0x4001), b.buf(1)); @@ -236,7 +236,7 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes public void testPutBytesDeletesInstruction() throws CodeUnitInsertionException { try (UndoableTransaction tid = b.startTransaction()) { TraceInstruction i4000 = - b.addInstruction(0, b.addr(0x4000), b.language, b.buf(0xf4, 0)); + b.addInstruction(0, b.addr(0x4000), null, b.buf(0xf4, 0)); assertEquals(b.addr(0x4001), i4000.getMaxAddress()); assertEquals(Range.atLeast(0L), i4000.getLifespan()); b.trace.getMemoryManager().putBytes(0, b.addr(0x4001), b.buf(1)); @@ -267,14 +267,14 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes } try { - b.addInstruction(1, b.addr(0x4001), b.language); + b.addInstruction(1, b.addr(0x4001), null); fail(); } catch (CodeUnitInsertionException e) { // pass } - b.addInstruction(0, b.addr(0x4004), b.language, b.buf(0xf4, 0)); + b.addInstruction(0, b.addr(0x4004), null, b.buf(0xf4, 0)); try { b.addData(1, b.addr(0x4005), ByteDataType.dataType, 1); @@ -285,7 +285,7 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes } try { - b.addInstruction(1, b.addr(0x4005), b.language); + b.addInstruction(1, b.addr(0x4005), null); } catch (CodeUnitInsertionException e) { // pass @@ -350,8 +350,7 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes assertAllNullFunc(v -> v.getAt(9, b.addr(0x4003))); assertUndefinedFunc(v -> v.getAt(9, b.addr(0x4004))); - TraceInstruction i4005 = - b.addInstruction(0, b.addr(0x4005), b.language, b.buf(0xf4, 0)); + TraceInstruction i4005 = b.addInstruction(0, b.addr(0x4005), null, b.buf(0xf4, 0)); i4005.setEndSnap(5); assertUndefinedFunc(v -> v.getAt(0, b.addr(0x4004))); assertInstructionFunc(i4005, v -> v.getAt(0, b.addr(0x4005))); @@ -384,8 +383,7 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes assertDataFunc(d4000, v -> v.getContaining(9, b.addr(0x4003))); assertUndefinedFunc(v -> v.getContaining(9, b.addr(0x4004))); - TraceInstruction i4005 = - b.addInstruction(0, b.addr(0x4005), b.language, b.buf(0xf4, 0)); + TraceInstruction i4005 = b.addInstruction(0, b.addr(0x4005), null, b.buf(0xf4, 0)); i4005.setEndSnap(5); assertUndefinedFunc(v -> v.getContaining(0, b.addr(0x4004))); @@ -451,7 +449,7 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes d4000.setEndSnap(9); d4004 = b.addData(0, b.addr(0x4004), IntegerDataType.dataType, b.buf(5, 6, 7, 8)); d4004.setEndSnap(5); - i4008 = b.addInstruction(0, b.addr(0x4008), b.language, b.buf(0xf4, 0)); + i4008 = b.addInstruction(0, b.addr(0x4008), null, b.buf(0xf4, 0)); i4008.setEndSnap(9); } @@ -593,7 +591,7 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes d4000.setEndSnap(9); d4004 = b.addData(0, b.addr(0x4004), IntegerDataType.dataType, b.buf(5, 6, 7, 8)); d4004.setEndSnap(5); - i4008 = b.addInstruction(0, b.addr(0x4008), b.language, b.buf(0xf4, 0)); + i4008 = b.addInstruction(0, b.addr(0x4008), null, b.buf(0xf4, 0)); i4008.setEndSnap(9); } @@ -732,7 +730,7 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes d4000.setEndSnap(9); d4004 = b.addData(0, b.addr(0x4004), IntegerDataType.dataType, b.buf(5, 6, 7, 8)); d4004.setEndSnap(5); - i4008 = b.addInstruction(0, b.addr(0x4008), b.language, b.buf(0xf4, 0)); + i4008 = b.addInstruction(0, b.addr(0x4008), null, b.buf(0xf4, 0)); i4008.setEndSnap(9); } @@ -872,7 +870,7 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes d4000.setEndSnap(9); d4004 = b.addData(0, b.addr(0x4004), IntegerDataType.dataType, b.buf(5, 6, 7, 8)); d4004.setEndSnap(5); - i4008 = b.addInstruction(0, b.addr(0x4008), b.language, b.buf(0xf4, 0)); + i4008 = b.addInstruction(0, b.addr(0x4008), null, b.buf(0xf4, 0)); i4008.setEndSnap(9); } @@ -1086,7 +1084,7 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes d4000.setEndSnap(9); d4004 = b.addData(0, b.addr(0x4004), IntegerDataType.dataType, b.buf(5, 6, 7, 8)); d4004.setEndSnap(5); - i4008 = b.addInstruction(0, b.addr(0x4008), b.language, b.buf(0xf4, 0)); + i4008 = b.addInstruction(0, b.addr(0x4008), null, b.buf(0xf4, 0)); i4008.setEndSnap(9); } TraceData u3fff = manager.undefinedData().getAt(0, b.addr(0x3fff)); @@ -1139,7 +1137,7 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes TraceInstruction iCodeMax; try (UndoableTransaction tid = b.startTransaction()) { - iCodeMax = b.addInstruction(0, b.addr(-0x0002), b.language, b.buf(0xf4, 0)); + iCodeMax = b.addInstruction(0, b.addr(-0x0002), null, b.buf(0xf4, 0)); } assertEquals(iCodeMax, manager.codeUnits().getBefore(0, b.data(0x0000))); @@ -1172,7 +1170,7 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes manager.undefinedData().getFloor(0, b.data(0x0003))); try (UndoableTransaction tid = b.startTransaction()) { - iCodeMax = b.addInstruction(0, b.addr(-0x0002), b.language, b.buf(0xf4, 0)); + iCodeMax = b.addInstruction(0, b.addr(-0x0002), null, b.buf(0xf4, 0)); } TraceData uCodePre = manager.undefinedData().getAt(0, b.addr(-0x0003)); assertUndefinedWithAddr(b.addr(-0x0003), uCodePre); @@ -1211,7 +1209,7 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes try (UndoableTransaction tid = b.startTransaction()) { d4000 = b.addData(0, b.addr(0x4000), IntegerDataType.dataType, b.buf(1, 2, 3, 4)); d4000.setEndSnap(9); - i4008 = b.addInstruction(0, b.addr(0x4008), b.language, b.buf(0xf4, 0)); + i4008 = b.addInstruction(0, b.addr(0x4008), null, b.buf(0xf4, 0)); i4008.setEndSnap(9); } @@ -1346,7 +1344,7 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes d4000.setEndSnap(9); d4004 = b.addData(0, b.addr(0x4004), IntegerDataType.dataType, b.buf(5, 6, 7, 8)); d4004.setEndSnap(5); - i4008 = b.addInstruction(0, b.addr(0x4008), b.language, b.buf(0xf4, 0)); + i4008 = b.addInstruction(0, b.addr(0x4008), null, b.buf(0xf4, 0)); i4008.setEndSnap(9); } @@ -1416,7 +1414,7 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes d4000.setEndSnap(9); d4004 = b.addData(0, b.addr(0x4004), IntegerDataType.dataType, b.buf(5, 6, 7, 8)); d4004.setEndSnap(5); - i4008 = b.addInstruction(0, b.addr(0x4008), b.language, b.buf(0xf4, 0)); + i4008 = b.addInstruction(0, b.addr(0x4008), null, b.buf(0xf4, 0)); i4008.setEndSnap(9); } @@ -1491,7 +1489,7 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes d4000.setEndSnap(9); d4004 = b.addData(0, b.addr(0x4004), IntegerDataType.dataType, b.buf(5, 6, 7, 8)); d4004.setEndSnap(5); - i4008 = b.addInstruction(0, b.addr(0x4008), b.language, b.buf(0xf4, 0)); + i4008 = b.addInstruction(0, b.addr(0x4008), null, b.buf(0xf4, 0)); i4008.setEndSnap(9); } @@ -1575,7 +1573,7 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes d4000.setEndSnap(9); d4004 = b.addData(0, b.addr(0x4004), IntegerDataType.dataType, b.buf(5, 6, 7, 8)); d4004.setEndSnap(5); - i4008 = b.addInstruction(0, b.addr(0x4008), b.language, b.buf(0xf4, 0)); + i4008 = b.addInstruction(0, b.addr(0x4008), null, b.buf(0xf4, 0)); i4008.setEndSnap(9); } @@ -1674,7 +1672,7 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes d4000.setEndSnap(9); d4004 = b.addData(0, b.addr(0x4004), IntegerDataType.dataType, b.buf(5, 6, 7, 8)); d4004.setEndSnap(5); - i4008 = b.addInstruction(0, b.addr(0x4008), b.language, b.buf(0xf4, 0)); + i4008 = b.addInstruction(0, b.addr(0x4008), null, b.buf(0xf4, 0)); i4008.setEndSnap(9); // Clear one of the data before a context space is created @@ -1708,22 +1706,21 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes } @Test - @Ignore("Looks related to GP-479") public void testAddGuestInstructionThenRemoveAndDelete() throws AddressOverflowException, CodeUnitInsertionException, IOException, CancelledException { - DBTraceLanguageManager langMan = b.trace.getLanguageManager(); + DBTracePlatformManager langMan = b.trace.getPlatformManager(); Language x86 = getSLEIGH_X86_LANGUAGE(); - DBTraceGuestLanguage guest; - DBTraceGuestLanguageMappedRange mappedRange; + DBTraceGuestPlatform guest; + DBTraceGuestPlatformMappedRange mappedRange; TraceInstruction g4000; TraceInstruction i4001; TraceData d4003; try (UndoableTransaction tid = b.startTransaction()) { - guest = langMan.addGuestLanguage(x86); + guest = langMan.addGuestPlatform(x86.getDefaultCompilerSpec()); mappedRange = guest.addMappedRange(b.addr(0x0000), b.addr(guest, 0x0000), 1L << 32); - g4000 = b.addInstruction(0, b.addr(0x4000), x86, b.buf(0x90)); - i4001 = b.addInstruction(0, b.addr(0x4001), b.language, b.buf(0xf4, 0)); + g4000 = b.addInstruction(0, b.addr(0x4000), guest, b.buf(0x90)); + i4001 = b.addInstruction(0, b.addr(0x4001), null, b.buf(0xf4, 0)); d4003 = b.addData(0, b.addr(0x4003), LongDataType.dataType, b.buf(1, 2, 3, 4)); } @@ -1737,19 +1734,26 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes b.trace.undo(); - assertEquals(g4000, manager.codeUnits().getAt(0, b.addr(0x4000))); + // 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.getGuestPlatform()); try (UndoableTransaction tid = b.startTransaction()) { guest.delete(new ConsoleTaskMonitor()); } assertUndefinedWithAddr(b.addr(0x4000), manager.codeUnits().getAt(0, b.addr(0x4000))); - assertEquals(i4001, manager.codeUnits().getAt(0, b.addr(0x4001))); - assertEquals(d4003, manager.codeUnits().getAt(0, b.addr(0x4003))); + // 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))); + assertNotNull(manager.instructions().getAt(0, b.addr(0x4001))); + assertNotNull(manager.definedData().getAt(0, b.addr(0x4003))); } @Test public void testSaveAndLoad() throws Exception { try (UndoableTransaction tid = b.startTransaction()) { - b.addInstruction(0, b.addr(0x4004), b.language, b.buf(0xf4, 0)); + b.addInstruction(0, b.addr(0x4004), null, b.buf(0xf4, 0)); TraceThread thread = b.getOrAddThread("Thread 1", 0); DBTraceCodeRegisterSpace regCode = manager.getCodeRegisterSpace(thread, true); @@ -1797,7 +1801,7 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes @Test public void testUndoThenRedo() throws Exception { try (UndoableTransaction tid = b.startTransaction()) { - b.addInstruction(0, b.addr(0x4004), b.language, b.buf(0xf4, 0)); + b.addInstruction(0, b.addr(0x4004), null, b.buf(0xf4, 0)); TraceThread thread = b.getOrAddThread("Thread 1", 0); DBTraceCodeRegisterSpace regCode = manager.getCodeRegisterSpace(thread, true); @@ -1850,7 +1854,7 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes b.trace.getBaseAddressFactory().getDefaultAddressSpace()); DBTraceCodeSpace space = manager.getCodeSpace(os, true); - b.addInstruction(0, os.getAddress(0x4004), b.language, b.buf(0xf4, 0)); + b.addInstruction(0, os.getAddress(0x4004), null, b.buf(0xf4, 0)); List all = new ArrayList<>(); space.definedUnits().get(0, true).forEach(all::add); @@ -1859,9 +1863,5 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes } } - // TODO: Test using a context-sensitive language - // TODO: Test using delay-slotted instructions (DBTraceCodemanager#instructionMax) // TODO: How are lifespans of delay-slotted instructions bound to thatof the jump? - // TODO: In language manager, test deleting a language clears instructions - // TODO: In language manager, test unmapping a language clears instructions } diff --git a/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/database/listing/DBTraceCodeUnitTest.java b/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/database/listing/DBTraceCodeUnitTest.java index a2f342e9fb..98873903f4 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/database/listing/DBTraceCodeUnitTest.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/database/listing/DBTraceCodeUnitTest.java @@ -50,7 +50,7 @@ import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree.TraceAdd import ghidra.trace.database.memory.DBTraceMemoryRegisterSpace; import ghidra.trace.database.memory.DBTraceMemorySpace; import ghidra.trace.database.symbol.DBTraceReference; -import ghidra.trace.model.language.TraceGuestLanguage; +import ghidra.trace.model.guest.TraceGuestPlatform; import ghidra.trace.model.listing.TraceData; import ghidra.trace.model.listing.TraceInstruction; import ghidra.trace.model.memory.TraceMemoryFlag; @@ -242,7 +242,7 @@ public class DBTraceCodeUnitTest extends AbstractGhidraHeadlessIntegrationTest TraceOverlappedRegionException, DuplicateNameException { TraceInstruction ins; try (UndoableTransaction tid = b.startTransaction()) { - ins = b.addInstruction(0, b.addr(0x4004), b.language, b.buf(0xf4, 0)); + ins = b.addInstruction(0, b.addr(0x4004), null, b.buf(0xf4, 0)); } TraceData und = manager.undefinedData().getAt(0, b.addr(0x4006)); @@ -281,7 +281,7 @@ public class DBTraceCodeUnitTest extends AbstractGhidraHeadlessIntegrationTest public void testGetProgram() throws CodeUnitInsertionException { TraceInstruction i4004; try (UndoableTransaction tid = b.startTransaction()) { - i4004 = b.addInstruction(0, b.addr(0x4004), b.language, b.buf(0xf4, 0)); + i4004 = b.addInstruction(0, b.addr(0x4004), null, b.buf(0xf4, 0)); } assertEquals(0, i4004.getProgram().getSnap()); @@ -291,7 +291,7 @@ public class DBTraceCodeUnitTest extends AbstractGhidraHeadlessIntegrationTest public void testGetMemory() throws CodeUnitInsertionException { TraceInstruction i4004; try (UndoableTransaction tid = b.startTransaction()) { - i4004 = b.addInstruction(0, b.addr(0x4004), b.language, b.buf(0xf4, 0)); + i4004 = b.addInstruction(0, b.addr(0x4004), null, b.buf(0xf4, 0)); } assertEquals(i4004.getProgram().getMemory(), i4004.getMemory()); @@ -300,14 +300,14 @@ public class DBTraceCodeUnitTest extends AbstractGhidraHeadlessIntegrationTest @Test public void testIsBigEndian() throws CodeUnitInsertionException, AddressOverflowException { Language x86 = getSLEIGH_X86_LANGUAGE(); - TraceGuestLanguage guest; + TraceGuestPlatform guest; TraceInstruction i4004; TraceInstruction g4006; try (UndoableTransaction tid = b.startTransaction()) { - i4004 = b.addInstruction(0, b.addr(0x4004), b.language, b.buf(0xf4, 0)); - guest = b.trace.getLanguageManager().addGuestLanguage(x86); + i4004 = b.addInstruction(0, b.addr(0x4004), null, b.buf(0xf4, 0)); + guest = b.trace.getPlatformManager().addGuestPlatform(x86.getDefaultCompilerSpec()); guest.addMappedRange(b.addr(0x0000), b.addr(guest, 0x0000), 1L << 32); - g4006 = b.addInstruction(0, b.addr(0x4006), x86, b.buf(0x90)); + g4006 = b.addInstruction(0, b.addr(0x4006), guest, b.buf(0x90)); } assertTrue(i4004.isBigEndian()); @@ -319,8 +319,8 @@ public class DBTraceCodeUnitTest extends AbstractGhidraHeadlessIntegrationTest TraceInstruction i4004; TraceInstruction i4006; try (UndoableTransaction tid = b.startTransaction()) { - i4004 = b.addInstruction(0, b.addr(0x4004), b.language, b.buf(0xf4, 0)); - i4006 = b.addInstruction(0, b.addr(0x4006), b.language, b.buf(0xf4, 0)); + i4004 = b.addInstruction(0, b.addr(0x4004), null, b.buf(0xf4, 0)); + i4006 = b.addInstruction(0, b.addr(0x4006), null, b.buf(0xf4, 0)); } assertFalse(i4004.hasProperty("myVoid")); @@ -455,8 +455,8 @@ public class DBTraceCodeUnitTest extends AbstractGhidraHeadlessIntegrationTest TraceInstruction i4004; TraceInstruction i4006; try (UndoableTransaction tid = b.startTransaction()) { - i4004 = b.addInstruction(0, b.addr(0x4004), b.language, b.buf(0xf4, 0)); - i4006 = b.addInstruction(0, b.addr(0x4006), b.language, b.buf(0xf4, 0)); + i4004 = b.addInstruction(0, b.addr(0x4004), null, b.buf(0xf4, 0)); + i4006 = b.addInstruction(0, b.addr(0x4006), null, b.buf(0xf4, 0)); } try (UndoableTransaction tid = b.startTransaction()) { @@ -510,7 +510,7 @@ public class DBTraceCodeUnitTest extends AbstractGhidraHeadlessIntegrationTest // TODO: Decide whether or not to shrink the comment lifespan with the unit lifespan assertEquals(Range.atLeast(0L), c4004.getLifespan()); - i4004_10 = b.addInstruction(10, b.addr(0x4004), b.language); + i4004_10 = b.addInstruction(10, b.addr(0x4004), null); i4004_10.setComment(CodeUnit.PRE_COMMENT, "Get this back in the mix"); i4004_10.setComment(CodeUnit.EOL_COMMENT, "A different comment"); } @@ -538,8 +538,8 @@ public class DBTraceCodeUnitTest extends AbstractGhidraHeadlessIntegrationTest TraceInstruction i4006; TraceData d4008; try (UndoableTransaction tid = b.startTransaction()) { - i4004 = b.addInstruction(0, b.addr(0x4004), b.language, b.buf(0xf4, 0)); - i4006 = b.addInstruction(0, b.addr(0x4006), b.language, b.buf(0xf4, 0)); + i4004 = b.addInstruction(0, b.addr(0x4004), null, b.buf(0xf4, 0)); + i4006 = b.addInstruction(0, b.addr(0x4006), null, b.buf(0xf4, 0)); d4008 = b.addData(0, b.addr(0x4008), LongDataType.dataType, b.buf(1, 2, 3, 4)); } @@ -564,8 +564,8 @@ public class DBTraceCodeUnitTest extends AbstractGhidraHeadlessIntegrationTest TraceInstruction i4006; try (UndoableTransaction tid = b.startTransaction()) { d4000 = b.addData(0, b.addr(0x4000), LongDataType.dataType, b.buf(1, 2, 3, 4)); - i4004 = b.addInstruction(0, b.addr(0x4004), b.language, b.buf(0xf4, 0)); - i4006 = b.addInstruction(0, b.addr(0x4006), b.language, b.buf(0xf4, 0)); + i4004 = b.addInstruction(0, b.addr(0x4004), null, b.buf(0xf4, 0)); + i4006 = b.addInstruction(0, b.addr(0x4006), null, b.buf(0xf4, 0)); } Set refs; @@ -708,7 +708,7 @@ public class DBTraceCodeUnitTest extends AbstractGhidraHeadlessIntegrationTest TraceData undefined; TraceData undReg; try (UndoableTransaction tid = b.startTransaction()) { - instruction = b.addInstruction(0, b.addr(0x4004), b.language, b.buf(0xf4, 0)); + instruction = b.addInstruction(0, b.addr(0x4004), null, b.buf(0xf4, 0)); undefined = manager.undefinedData().getAt(0, b.addr(0x4006)); thread = b.getOrAddThread("Thread 1", 0); @@ -739,7 +739,7 @@ public class DBTraceCodeUnitTest extends AbstractGhidraHeadlessIntegrationTest TraceInstruction i4004; try (UndoableTransaction tid = b.startTransaction()) { d4000 = b.addData(0, b.addr(0x4000), LongDataType.dataType, b.buf(1, 2, 3, 4)); - i4004 = b.addInstruction(0, b.addr(0x4004), b.language, b.buf(0xf4, 0)); + i4004 = b.addInstruction(0, b.addr(0x4004), null, b.buf(0xf4, 0)); d4000.setEndSnap(9); assertEquals(Range.closed(0L, 9L), d4000.getLifespan()); @@ -784,7 +784,7 @@ public class DBTraceCodeUnitTest extends AbstractGhidraHeadlessIntegrationTest @Test public void testGetBytes() throws Exception { Language x86 = getSLEIGH_X86_LANGUAGE(); - TraceGuestLanguage guest; + TraceGuestPlatform guest; TraceData data; TraceData und; @@ -803,9 +803,9 @@ public class DBTraceCodeUnitTest extends AbstractGhidraHeadlessIntegrationTest DBTraceCodeRegisterSpace regCode = manager.getCodeRegisterSpace(thread, true); reg = regCode.definedData().create(Range.atLeast(0L), r4, PointerDataType.dataType); - guest = b.trace.getLanguageManager().addGuestLanguage(x86); + guest = b.trace.getPlatformManager().addGuestPlatform(x86.getDefaultCompilerSpec()); guest.addMappedRange(b.addr(0x0000), b.addr(guest, 0x0000), 1L << 32); - lil = b.addInstruction(0, b.addr(0x4008), x86, b.buf(0xeb, 0xfe)); + lil = b.addInstruction(0, b.addr(0x4008), guest, b.buf(0xeb, 0xfe)); } ByteBuffer buf; @@ -1039,13 +1039,13 @@ public class DBTraceCodeUnitTest extends AbstractGhidraHeadlessIntegrationTest .addRegion("myRegion", Range.atLeast(0L), b.range(0x4000, 0x4fff), TraceMemoryFlag.READ); - i4004 = b.addInstruction(0, b.addr(0x4004), b.language, b.buf(0xc8, 0x47)); + i4004 = b.addInstruction(0, b.addr(0x4004), null, b.buf(0xc8, 0x47)); assertEquals("add r4,#0x7", i4004.toString()); - i4006 = b.addInstruction(0, b.addr(0x4006), b.language, b.buf(0xf4, 0)); + i4006 = b.addInstruction(0, b.addr(0x4006), null, b.buf(0xf4, 0)); assertEquals("ret", i4006.toString()); - i4008 = b.addInstruction(0, b.addr(0x4008), b.language, b.buf(0xff, 0xfc)); + i4008 = b.addInstruction(0, b.addr(0x4008), null, b.buf(0xff, 0xfc)); assertEquals("call 0x00004004", i4008.toString()); - i400a = b.addInstruction(0, b.addr(0x400a), b.language, b.buf(0xf6, 0x40)); + i400a = b.addInstruction(0, b.addr(0x400a), null, b.buf(0xf6, 0x40)); assertEquals("call r4", i400a.toString()); } @@ -1191,7 +1191,7 @@ public class DBTraceCodeUnitTest extends AbstractGhidraHeadlessIntegrationTest TraceInstruction i4004; try (UndoableTransaction tid = b.startTransaction()) { - i4004 = b.addInstruction(0, b.addr(0x4004), b.language, b.buf(0xf4, 0)); + i4004 = b.addInstruction(0, b.addr(0x4004), null, b.buf(0xf4, 0)); } // TODO: Test with non-default context @@ -1240,7 +1240,7 @@ public class DBTraceCodeUnitTest extends AbstractGhidraHeadlessIntegrationTest TraceData d4006; try (UndoableTransaction tid = b.startTransaction()) { d4000 = b.addData(0, b.addr(0x4000), LongDataType.dataType, b.buf(1, 2, 3, 4)); - i4004 = b.addInstruction(0, b.addr(0x4004), b.language, b.buf(0xf4, 0)); + i4004 = b.addInstruction(0, b.addr(0x4004), null, b.buf(0xf4, 0)); d4006 = b.addData(0, b.addr(0x4006), PointerDataType.dataType, b.buf(0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00)); } @@ -1260,7 +1260,7 @@ public class DBTraceCodeUnitTest extends AbstractGhidraHeadlessIntegrationTest TraceData d4006; try (UndoableTransaction tid = b.startTransaction()) { d4000 = b.addData(0, b.addr(0x4000), LongDataType.dataType, b.buf(1, 2, 3, 4)); - i4004 = b.addInstruction(0, b.addr(0x4004), b.language, b.buf(0xf4, 0)); + i4004 = b.addInstruction(0, b.addr(0x4004), null, b.buf(0xf4, 0)); d4006 = b.addData(0, b.addr(0x4006), PointerDataType.dataType, b.buf(0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00)); } @@ -1285,14 +1285,14 @@ public class DBTraceCodeUnitTest extends AbstractGhidraHeadlessIntegrationTest @Test public void testGetLanguage() throws CodeUnitInsertionException, AddressOverflowException { Language x86 = getSLEIGH_X86_LANGUAGE(); - TraceGuestLanguage guest; + TraceGuestPlatform guest; TraceInstruction i4004; TraceInstruction g4006; try (UndoableTransaction tid = b.startTransaction()) { - i4004 = b.addInstruction(0, b.addr(0x4004), b.language, b.buf(0xf4, 0)); - guest = b.trace.getLanguageManager().addGuestLanguage(x86); + i4004 = b.addInstruction(0, b.addr(0x4004), null, b.buf(0xf4, 0)); + guest = b.trace.getPlatformManager().addGuestPlatform(x86.getDefaultCompilerSpec()); guest.addMappedRange(b.addr(0x0000), b.addr(guest, 0x0000), 1L << 32); - g4006 = b.addInstruction(0, b.addr(0x4006), x86, b.buf(0x90)); + g4006 = b.addInstruction(0, b.addr(0x4006), guest, b.buf(0x90)); } TraceData u4007 = manager.undefinedData().getAt(0, b.addr(0x4007)); @@ -1305,7 +1305,7 @@ public class DBTraceCodeUnitTest extends AbstractGhidraHeadlessIntegrationTest public void testToString() throws CodeUnitInsertionException, AddressOverflowException, TraceOverlappedRegionException, DuplicateNameException { Language x86 = getSLEIGH_X86_LANGUAGE(); - TraceGuestLanguage guest; + TraceGuestPlatform guest; TraceData d4000; TraceInstruction i4004; TraceInstruction g4006; @@ -1316,13 +1316,13 @@ public class DBTraceCodeUnitTest extends AbstractGhidraHeadlessIntegrationTest .addRegion("myRegion", Range.atLeast(0L), b.range(0x4000, 0x4fff), TraceMemoryFlag.READ); - guest = b.trace.getLanguageManager().addGuestLanguage(x86); + guest = b.trace.getPlatformManager().addGuestPlatform(x86.getDefaultCompilerSpec()); guest.addMappedRange(b.addr(0x0000), b.addr(guest, 0x0000), 1L << 32); d4000 = b.addData(0, b.addr(0x4000), LongDataType.dataType, b.buf(1, 2, 3, 4)); - i4004 = b.addInstruction(0, b.addr(0x4004), b.language, b.buf(0xc8, 0x47)); - g4006 = b.addInstruction(0, b.addr(0x4006), x86, b.buf(0x90)); - i4007 = b.addInstruction(0, b.addr(0x4007), b.language, b.buf(0xff, 0xfd)); + i4004 = b.addInstruction(0, b.addr(0x4004), null, b.buf(0xc8, 0x47)); + g4006 = b.addInstruction(0, b.addr(0x4006), guest, b.buf(0x90)); + i4007 = b.addInstruction(0, b.addr(0x4007), null, b.buf(0xff, 0xfd)); } TraceData u4009 = manager.undefinedData().getAt(0, b.addr(0x4009)); diff --git a/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/database/program/DBTraceDisassemblerIntegrationTest.java b/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/database/program/DBTraceDisassemblerIntegrationTest.java index 14b1cecc00..a996c98fd8 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/database/program/DBTraceDisassemblerIntegrationTest.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/database/program/DBTraceDisassemblerIntegrationTest.java @@ -35,7 +35,7 @@ import ghidra.program.model.listing.CodeUnit; import ghidra.program.model.mem.MemoryBlock; import ghidra.test.AbstractGhidraHeadlessIntegrationTest; import ghidra.trace.database.ToyDBTraceBuilder; -import ghidra.trace.database.language.DBTraceGuestLanguage; +import ghidra.trace.database.guest.DBTraceGuestPlatform; import ghidra.trace.database.listing.*; import ghidra.trace.database.memory.DBTraceMemoryManager; import ghidra.trace.database.memory.DBTraceMemorySpace; @@ -108,13 +108,15 @@ public class DBTraceDisassemblerIntegrationTest extends AbstractGhidraHeadlessIn b.trace.getMemoryManager().getMemorySpace(b.language.getDefaultSpace(), true); space.putBytes(0, b.addr(0x4000), b.buf(0x90)); - DBTraceGuestLanguage guest = b.trace.getLanguageManager().addGuestLanguage(x86); + DBTraceGuestPlatform guest = + b.trace.getPlatformManager().addGuestPlatform(x86.getDefaultCompilerSpec()); guest.addMappedRange(b.addr(0x4000), b.addr(guest, 0x00400000), 0x1000); - // TODO: The more I look, the more I think I need a fully-mapped program view :( - // As annoying as it is, I plan to do it as a wrapper, not as an extension.... - // The disassembler uses bookmarks, context, etc. for feedback. It'd be nice to - // have that + /* + * TODO: The more I look, the more I think I need a fully-mapped program view :( As + * annoying as it is, I plan to do it as a wrapper, not as an extension.... The + * disassembler uses bookmarks, context, etc. for feedback. It'd be nice to have that. + */ RegisterValue defaultContextValue = b.trace.getRegisterContextManager() .getDefaultContext(x86) @@ -125,7 +127,7 @@ public class DBTraceDisassemblerIntegrationTest extends AbstractGhidraHeadlessIn guest.getMappedMemBuffer(0, b.addr(guest, 0x00400000)), defaultContextValue, 1)); DBTraceCodeManager code = b.trace.getCodeManager(); - code.instructions().addInstructionSet(Range.closed(0L, 0L), set, false); + code.instructions().addInstructionSet(Range.closed(0L, 0L), guest, set, false); DBTraceInstruction ins = code.instructions().getAt(0, b.addr(0x4000)); // TODO: This is great, but probably incomplete. diff --git a/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/database/program/DBTraceProgramViewListingTest.java b/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/database/program/DBTraceProgramViewListingTest.java index 09c5cbdd84..f341e8c209 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/database/program/DBTraceProgramViewListingTest.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/database/program/DBTraceProgramViewListingTest.java @@ -114,7 +114,7 @@ public class DBTraceProgramViewListingTest extends AbstractGhidraHeadlessIntegra CodeUnitInsertionException { Instruction ins; try (UndoableTransaction tid = b.startTransaction()) { - ins = b.addInstruction(0, b.addr(0x4005), b.language, b.buf(0xf4, 0)); + ins = b.addInstruction(0, b.addr(0x4005), null, b.buf(0xf4, 0)); } assertEquals("ret", ins.toString()); } @@ -138,7 +138,7 @@ public class DBTraceProgramViewListingTest extends AbstractGhidraHeadlessIntegra Instruction i4005; try (UndoableTransaction tid = b.startTransaction()) { - i4005 = b.addInstruction(0, b.addr(0x4005), b.language, b.buf(0xf4, 0)); + i4005 = b.addInstruction(0, b.addr(0x4005), null, b.buf(0xf4, 0)); } assertEquals(i4005, listing.getCodeUnitAt(b.addr(0x4005))); assertNull(listing.getCodeUnitAt(b.addr(0x4006))); @@ -163,7 +163,7 @@ public class DBTraceProgramViewListingTest extends AbstractGhidraHeadlessIntegra assertUndefined(listing.getCodeUnitContaining(b.addr(0x4005))); Instruction i4005; try (UndoableTransaction tid = b.startTransaction()) { - i4005 = b.addInstruction(0, b.addr(0x4005), b.language, b.buf(0xf4, 0)); + i4005 = b.addInstruction(0, b.addr(0x4005), null, b.buf(0xf4, 0)); } assertEquals(i4005, listing.getCodeUnitContaining(b.addr(0x4005))); assertEquals(i4005, listing.getCodeUnitContaining(b.addr(0x4006))); @@ -189,7 +189,7 @@ public class DBTraceProgramViewListingTest extends AbstractGhidraHeadlessIntegra assertEquals(b.addr(0x4005), cu.getAddress()); Instruction i4005; try (UndoableTransaction tid = b.startTransaction()) { - i4005 = b.addInstruction(0, b.addr(0x4005), b.language, b.buf(0xf4, 0)); + i4005 = b.addInstruction(0, b.addr(0x4005), null, b.buf(0xf4, 0)); } assertEquals(i4005, listing.getCodeUnitAfter(b.addr(0x4004))); assertUndefined(cu = listing.getCodeUnitAfter(b.addr(0x4005))); @@ -215,7 +215,7 @@ public class DBTraceProgramViewListingTest extends AbstractGhidraHeadlessIntegra assertEquals(b.addr(0x4005), cu.getAddress()); Instruction i4005; try (UndoableTransaction tid = b.startTransaction()) { - i4005 = b.addInstruction(0, b.addr(0x4005), b.language, b.buf(0xf4, 0)); + i4005 = b.addInstruction(0, b.addr(0x4005), null, b.buf(0xf4, 0)); } assertEquals(i4005, listing.getCodeUnitBefore(b.addr(0x4006))); assertUndefined(cu = listing.getCodeUnitBefore(b.addr(0x4005))); @@ -291,7 +291,7 @@ public class DBTraceProgramViewListingTest extends AbstractGhidraHeadlessIntegra Instruction i4005; try (UndoableTransaction tid = b.startTransaction()) { d4000 = b.addData(0, b.addr(0x4000), Undefined4DataType.dataType, b.buf(1, 2, 3, 4)); - i4005 = b.addInstruction(0, b.addr(0x4005), b.language, b.buf(0xf4, 0)); + i4005 = b.addInstruction(0, b.addr(0x4005), null, b.buf(0xf4, 0)); } sample = takeN(10, listing.getCodeUnits(true)); @@ -355,7 +355,7 @@ public class DBTraceProgramViewListingTest extends AbstractGhidraHeadlessIntegra assertNull(listing.getInstructionAt(b.addr(0x4005))); Instruction i4005; try (UndoableTransaction tid = b.startTransaction()) { - i4005 = b.addInstruction(0, b.addr(0x4005), b.language, b.buf(0xf4, 0)); + i4005 = b.addInstruction(0, b.addr(0x4005), null, b.buf(0xf4, 0)); } assertEquals(i4005, listing.getInstructionAt(b.addr(0x4005))); assertNull(listing.getInstructionAt(b.addr(0x4006))); @@ -368,7 +368,7 @@ public class DBTraceProgramViewListingTest extends AbstractGhidraHeadlessIntegra assertNull(listing.getInstructionContaining(b.addr(0x4005))); Instruction i4005; try (UndoableTransaction tid = b.startTransaction()) { - i4005 = b.addInstruction(0, b.addr(0x4005), b.language, b.buf(0xf4, 0)); + i4005 = b.addInstruction(0, b.addr(0x4005), null, b.buf(0xf4, 0)); } assertEquals(i4005, listing.getInstructionContaining(b.addr(0x4005))); assertEquals(i4005, listing.getInstructionContaining(b.addr(0x4006))); @@ -381,7 +381,7 @@ public class DBTraceProgramViewListingTest extends AbstractGhidraHeadlessIntegra assertNull(listing.getInstructionAfter(b.addr(0x4004))); Instruction i4005; try (UndoableTransaction tid = b.startTransaction()) { - i4005 = b.addInstruction(0, b.addr(0x4005), b.language, b.buf(0xf4, 0)); + i4005 = b.addInstruction(0, b.addr(0x4005), null, b.buf(0xf4, 0)); } assertEquals(i4005, listing.getInstructionAfter(b.addr(0x4004))); assertNull(listing.getInstructionAfter(b.addr(0x4005))); @@ -393,7 +393,7 @@ public class DBTraceProgramViewListingTest extends AbstractGhidraHeadlessIntegra assertNull(listing.getInstructionBefore(b.addr(0x4006))); Instruction i4005; try (UndoableTransaction tid = b.startTransaction()) { - i4005 = b.addInstruction(0, b.addr(0x4005), b.language, b.buf(0xf4, 0)); + i4005 = b.addInstruction(0, b.addr(0x4005), null, b.buf(0xf4, 0)); } assertEquals(i4005, listing.getInstructionBefore(b.addr(0x4006))); assertNull(listing.getInstructionBefore(b.addr(0x4005))); @@ -421,9 +421,9 @@ public class DBTraceProgramViewListingTest extends AbstractGhidraHeadlessIntegra Instruction i4007; Instruction i400a; try (UndoableTransaction tid = b.startTransaction()) { - i4005 = b.addInstruction(0, b.addr(0x4005), b.language, b.buf(0xf4, 0)); - i4007 = b.addInstruction(0, b.addr(0x4007), b.language, b.buf(0xf4, 0)); - i400a = b.addInstruction(0, b.addr(0x400a), b.language, b.buf(0xf4, 0)); + i4005 = b.addInstruction(0, b.addr(0x4005), null, b.buf(0xf4, 0)); + i4007 = b.addInstruction(0, b.addr(0x4007), null, b.buf(0xf4, 0)); + i400a = b.addInstruction(0, b.addr(0x400a), null, b.buf(0xf4, 0)); b.addData(0, b.addr(0x400c), Undefined4DataType.dataType, b.buf(1, 2, 3, 4)); } @@ -454,7 +454,7 @@ public class DBTraceProgramViewListingTest extends AbstractGhidraHeadlessIntegra assertUndefined(listing.getDataAt(b.addr(0x4005))); try (UndoableTransaction tid = b.startTransaction()) { - b.addInstruction(0, b.addr(0x4005), b.language, b.buf(0xf4, 0)); + b.addInstruction(0, b.addr(0x4005), null, b.buf(0xf4, 0)); } assertNull(listing.getDataAt(b.addr(0x4005))); assertNull(listing.getDataAt(b.addr(0x4006))); @@ -477,7 +477,7 @@ public class DBTraceProgramViewListingTest extends AbstractGhidraHeadlessIntegra assertUndefined(listing.getDataContaining(b.addr(0x4005))); try (UndoableTransaction tid = b.startTransaction()) { - b.addInstruction(0, b.addr(0x4005), b.language, b.buf(0xf4, 0)); + b.addInstruction(0, b.addr(0x4005), null, b.buf(0xf4, 0)); } assertNull(listing.getDataContaining(b.addr(0x4005))); assertNull(listing.getDataContaining(b.addr(0x4006))); @@ -502,7 +502,7 @@ public class DBTraceProgramViewListingTest extends AbstractGhidraHeadlessIntegra assertUndefined(cu = listing.getDataAfter(b.addr(0x4004))); assertEquals(b.addr(0x4005), cu.getAddress()); try (UndoableTransaction tid = b.startTransaction()) { - b.addInstruction(0, b.addr(0x4005), b.language, b.buf(0xf4, 0)); + b.addInstruction(0, b.addr(0x4005), null, b.buf(0xf4, 0)); } assertUndefined(cu = listing.getDataAfter(b.addr(0x4004))); assertEquals(b.addr(0x4007), cu.getAddress()); @@ -526,7 +526,7 @@ public class DBTraceProgramViewListingTest extends AbstractGhidraHeadlessIntegra assertUndefined(cu = listing.getDataBefore(b.addr(0x4006))); assertEquals(b.addr(0x4005), cu.getAddress()); try (UndoableTransaction tid = b.startTransaction()) { - b.addInstruction(0, b.addr(0x4005), b.language, b.buf(0xf4, 0)); + b.addInstruction(0, b.addr(0x4005), null, b.buf(0xf4, 0)); } assertUndefined(cu = listing.getDataBefore(b.addr(0x4007))); assertEquals(b.addr(0x4004), cu.getAddress()); @@ -600,7 +600,7 @@ public class DBTraceProgramViewListingTest extends AbstractGhidraHeadlessIntegra Data d4000; try (UndoableTransaction tid = b.startTransaction()) { d4000 = b.addData(0, b.addr(0x4000), Undefined4DataType.dataType, b.buf(1, 2, 3, 4)); - b.addInstruction(0, b.addr(0x4005), b.language, b.buf(0xf4, 0)); + b.addInstruction(0, b.addr(0x4005), null, b.buf(0xf4, 0)); } sample = takeN(10, listing.getData(true)); @@ -731,7 +731,7 @@ public class DBTraceProgramViewListingTest extends AbstractGhidraHeadlessIntegra d4004 = b.addData(0, b.addr(0x4004), Undefined4DataType.dataType, b.buf(5, 6, 7, 8)); d400a = b.addData(0, b.addr(0x400a), Undefined4DataType.dataType, b.buf(10, 11, 12, 13)); - b.addInstruction(0, b.addr(0x400e), b.language, b.buf(0xf4, 0)); + b.addInstruction(0, b.addr(0x400e), null, b.buf(0xf4, 0)); } assertEquals(List.of(d4000, d4004, d400a), takeN(10, listing.getDefinedData(true))); @@ -760,7 +760,7 @@ public class DBTraceProgramViewListingTest extends AbstractGhidraHeadlessIntegra assertUndefined(listing.getUndefinedDataAt(b.addr(0x4005))); try (UndoableTransaction tid = b.startTransaction()) { - b.addInstruction(0, b.addr(0x4005), b.language, b.buf(0xf4, 0)); + b.addInstruction(0, b.addr(0x4005), null, b.buf(0xf4, 0)); } assertNull(listing.getUndefinedDataAt(b.addr(0x4005))); assertNull(listing.getUndefinedDataAt(b.addr(0x4006))); @@ -783,7 +783,7 @@ public class DBTraceProgramViewListingTest extends AbstractGhidraHeadlessIntegra assertUndefined(cu = listing.getUndefinedDataAfter(b.addr(0x4004), TaskMonitor.DUMMY)); assertEquals(b.addr(0x4005), cu.getAddress()); try (UndoableTransaction tid = b.startTransaction()) { - b.addInstruction(0, b.addr(0x4005), b.language, b.buf(0xf4, 0)); + b.addInstruction(0, b.addr(0x4005), null, b.buf(0xf4, 0)); } assertUndefined(cu = listing.getUndefinedDataAfter(b.addr(0x4004), TaskMonitor.DUMMY)); assertEquals(b.addr(0x4007), cu.getAddress()); @@ -805,7 +805,7 @@ public class DBTraceProgramViewListingTest extends AbstractGhidraHeadlessIntegra assertUndefined(cu = listing.getUndefinedDataBefore(b.addr(0x4006), TaskMonitor.DUMMY)); assertEquals(b.addr(0x4005), cu.getAddress()); try (UndoableTransaction tid = b.startTransaction()) { - b.addInstruction(0, b.addr(0x4005), b.language, b.buf(0xf4, 0)); + b.addInstruction(0, b.addr(0x4005), null, b.buf(0xf4, 0)); } assertUndefined(cu = listing.getUndefinedDataBefore(b.addr(0x4007), TaskMonitor.DUMMY)); assertEquals(b.addr(0x4004), cu.getAddress()); @@ -816,7 +816,7 @@ public class DBTraceProgramViewListingTest extends AbstractGhidraHeadlessIntegra UnknownInstructionException, CodeUnitInsertionException { try (UndoableTransaction tid = b.startTransaction()) { b.addData(0, b.addr(0x4000), Undefined4DataType.dataType, b.buf(1, 2, 3, 4)); - b.addInstruction(0, b.addr(0x4005), b.language, b.buf(0xf4, 0)); + b.addInstruction(0, b.addr(0x4005), null, b.buf(0xf4, 0)); } Data cu; @@ -834,7 +834,7 @@ public class DBTraceProgramViewListingTest extends AbstractGhidraHeadlessIntegra UnknownInstructionException, CodeUnitInsertionException, CancelledException { try (UndoableTransaction tid = b.startTransaction()) { b.addData(0, b.addr(0x4000), Undefined4DataType.dataType, b.buf(1, 2, 3, 4)); - b.addInstruction(0, b.addr(0x4005), b.language, b.buf(0xf4, 0)); + b.addInstruction(0, b.addr(0x4005), null, b.buf(0xf4, 0)); } TODO(); // Should I expect OTHER ranges in the undefined set? @@ -849,7 +849,7 @@ public class DBTraceProgramViewListingTest extends AbstractGhidraHeadlessIntegra assertNull(listing.getDefinedCodeUnitAfter(b.addr(0x3fff))); Instruction i4005; try (UndoableTransaction tid = b.startTransaction()) { - i4005 = b.addInstruction(0, b.addr(0x4005), b.language, b.buf(0xf4, 0)); + i4005 = b.addInstruction(0, b.addr(0x4005), null, b.buf(0xf4, 0)); } assertEquals(i4005, listing.getDefinedCodeUnitAfter(b.addr(0x3fff))); assertNull(listing.getDefinedCodeUnitAfter(b.addr(0x4005))); @@ -873,7 +873,7 @@ public class DBTraceProgramViewListingTest extends AbstractGhidraHeadlessIntegra assertNull(listing.getDefinedCodeUnitBefore(b.addr(0x4000))); Instruction i4005; try (UndoableTransaction tid = b.startTransaction()) { - i4005 = b.addInstruction(0, b.addr(0x4005), b.language, b.buf(0xf4, 0)); + i4005 = b.addInstruction(0, b.addr(0x4005), null, b.buf(0xf4, 0)); } assertEquals(i4005, listing.getDefinedCodeUnitBefore(b.addr(0x4006))); assertEquals(d4000, listing.getDefinedCodeUnitBefore(b.addr(0x4005))); diff --git a/Ghidra/Debug/ProposedUtils/src/main/java/ghidra/framework/cmd/TypedBackgroundCommand.java b/Ghidra/Debug/ProposedUtils/src/main/java/ghidra/framework/cmd/TypedBackgroundCommand.java new file mode 100644 index 0000000000..b5ff519b3b --- /dev/null +++ b/Ghidra/Debug/ProposedUtils/src/main/java/ghidra/framework/cmd/TypedBackgroundCommand.java @@ -0,0 +1,42 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.framework.cmd; + +import ghidra.framework.model.DomainObject; +import ghidra.framework.model.UndoableDomainObject; +import ghidra.framework.plugintool.PluginTool; +import ghidra.util.task.TaskMonitor; + +public abstract class TypedBackgroundCommand + extends BackgroundCommand { + + public TypedBackgroundCommand(String name, boolean hasProgress, boolean canCancel, + boolean isModal) { + super(name, hasProgress, canCancel, isModal); + } + + @Override + @SuppressWarnings("unchecked") + public boolean applyTo(DomainObject obj, TaskMonitor monitor) { + return applyToTyped((T) obj, monitor); + } + + public abstract boolean applyToTyped(T obj, TaskMonitor monitor); + + public void run(PluginTool tool, T obj) { + tool.executeBackgroundCommand(this, obj); + } +} diff --git a/Ghidra/Framework/Docking/src/main/java/docking/DockingWindowManager.java b/Ghidra/Framework/Docking/src/main/java/docking/DockingWindowManager.java index 18b0d44d76..a89c66c022 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/DockingWindowManager.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/DockingWindowManager.java @@ -266,7 +266,8 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder } /** - * Returns a new list of all DockingWindowManager instances know to exist. + * Returns a new list of all DockingWindowManager instances known to exist, ordered from least + * to most-recently active. * * @return a new list of all DockingWindowManager instances know to exist. */ diff --git a/Ghidra/Processors/Toy/certification.manifest b/Ghidra/Processors/Toy/certification.manifest index c9e9252d33..0e477e8798 100644 --- a/Ghidra/Processors/Toy/certification.manifest +++ b/Ghidra/Processors/Toy/certification.manifest @@ -14,6 +14,7 @@ data/languages/toy.cspec||GHIDRA||||END| data/languages/toy.ldefs||GHIDRA||||END| data/languages/toy.pspec||GHIDRA||reviewed||END| data/languages/toy.sinc||GHIDRA||||END| +data/languages/toy64-long8.cspec||GHIDRA||||END| data/languages/toy64.cspec||GHIDRA||||END| data/languages/toy64_be.slaspec||GHIDRA||||END| data/languages/toy64_be_harvard.slaspec||GHIDRA||||END| diff --git a/Ghidra/Processors/Toy/data/languages/toy.ldefs b/Ghidra/Processors/Toy/data/languages/toy.ldefs index 530b23ab5c..7bd3ec76cd 100644 --- a/Ghidra/Processors/Toy/data/languages/toy.ldefs +++ b/Ghidra/Processors/Toy/data/languages/toy.ldefs @@ -66,6 +66,7 @@ id="Toy:BE:64:default"> Toy (test) processor 64-bit big-endian + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Ghidra/Processors/x86/data/languages/x86.ldefs b/Ghidra/Processors/x86/data/languages/x86.ldefs index 5e5dbc8438..2556a1f229 100644 --- a/Ghidra/Processors/x86/data/languages/x86.ldefs +++ b/Ghidra/Processors/x86/data/languages/x86.ldefs @@ -91,6 +91,7 @@ +