diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/disassemble/AbstractTraceDisassembleAction.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/disassemble/AbstractTraceDisassembleAction.java new file mode 100644 index 0000000000..4879594355 --- /dev/null +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/disassemble/AbstractTraceDisassembleAction.java @@ -0,0 +1,69 @@ +/* ### + * 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.disassemble; + +import docking.ActionContext; +import docking.action.DockingAction; +import ghidra.app.context.ListingActionContext; +import ghidra.program.model.address.*; +import ghidra.program.model.lang.LanguageID; +import ghidra.program.model.listing.Program; +import ghidra.program.util.ProgramSelection; +import ghidra.trace.model.guest.TracePlatform; +import ghidra.trace.model.program.TraceProgramView; + +public abstract class AbstractTraceDisassembleAction extends DockingAction { + protected final DebuggerDisassemblerPlugin plugin; + + public AbstractTraceDisassembleAction(DebuggerDisassemblerPlugin plugin, String name) { + super(name, plugin.getName()); + this.plugin = plugin; + } + + protected abstract TracePlatform getPlatform(TraceProgramView view); + + protected abstract LanguageID getAlternativeLanguageID(); + + @Override + public void actionPerformed(ActionContext context) { + if (!(context instanceof ListingActionContext)) { + return; + } + ListingActionContext lac = (ListingActionContext) context; + Program program = lac.getProgram(); + if (!(program instanceof TraceProgramView)) { + return; + } + TraceProgramView view = (TraceProgramView) program; + Address address = lac.getAddress(); + AddressSpace space = address.getAddressSpace(); + AddressSetView set; + ProgramSelection selection = lac.getSelection(); + if (selection != null && !selection.isEmpty()) { + set = selection; + } + else { + set = program.getAddressFactory() + .getAddressSet(space.getMinAddress(), space.getMaxAddress()); + } + TracePlatform platform = getPlatform(view); + LanguageID altLangID = getAlternativeLanguageID(); + TraceDisassembleCommand dis = new TraceDisassembleCommand(platform, address, set); + dis.setInitialContext(DebuggerDisassemblerPlugin.deriveAlternativeDefaultContext( + platform.getLanguage(), altLangID, platform.mapHostToGuest(address))); + dis.run(plugin.getTool(), view); + } +} diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/disassemble/AbstractTracePatchInstructionAction.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/disassemble/AbstractTracePatchInstructionAction.java new file mode 100644 index 0000000000..e0a00e2cee --- /dev/null +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/disassemble/AbstractTracePatchInstructionAction.java @@ -0,0 +1,140 @@ +/* ### + * 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.disassemble; + +import docking.ActionContext; +import ghidra.app.plugin.assembler.Assembler; +import ghidra.app.plugin.assembler.Assemblers; +import ghidra.app.plugin.assembler.sleigh.sem.AssemblyPatternBlock; +import ghidra.app.plugin.core.assembler.AssemblyDualTextField; +import ghidra.app.plugin.core.assembler.PatchInstructionAction; +import ghidra.program.model.address.*; +import ghidra.program.model.lang.*; +import ghidra.program.model.listing.CodeUnit; +import ghidra.program.model.listing.Program; +import ghidra.program.model.mem.MemoryAccessException; +import ghidra.program.util.DefaultLanguageService; +import ghidra.trace.model.guest.TracePlatform; +import ghidra.trace.model.program.TraceProgramView; + +public abstract class AbstractTracePatchInstructionAction extends PatchInstructionAction { + protected final DebuggerDisassemblerPlugin plugin; + + public AbstractTracePatchInstructionAction(DebuggerDisassemblerPlugin plugin, String name) { + super(plugin, name); + this.plugin = plugin; + } + + protected abstract TracePlatform getPlatform(CodeUnit cu); + + protected RegisterValue getContextValue(CodeUnit cu) { + return DebuggerDisassemblerPlugin.deriveAlternativeDefaultContext( + getPlatform(cu).getLanguage(), getAlternativeLanguageID(cu), cu.getAddress()); + } + + protected LanguageID getAlternativeLanguageID(CodeUnit cu) { + return getPlatform(cu).getLanguage().getLanguageID(); + } + + @Override + protected AssemblyDualTextField newAssemblyDualTextField() { + return new AssemblyDualTextField() { + AssemblyPatternBlock ctx = null; + + @Override + protected AssemblyPatternBlock getContext() { + return ctx; + } + + @Override + public void setAddress(Address address) { + super.setAddress(address); + RegisterValue rv = getContextValue(getCodeUnit()); + ctx = AssemblyPatternBlock.fromRegisterValue(rv).fillMask(); + } + }; + } + + @Override + public boolean isAddToPopup(ActionContext context) { + if (!super.isAddToPopup(context)) { + return false; + } + CodeUnit cu = getCodeUnit(context); + return isApplicableToUnit(cu); + } + + @Override + public boolean isEnabledForContext(ActionContext context) { + if (!super.isEnabledForContext(context)) { + return false; + } + CodeUnit cu = getCodeUnit(context); + return isApplicableToUnit(cu); + } + + @Override + public void actionPerformed(ActionContext context) { + /* + * Ensure the load has happened. Otherwise, it happens during completion and cancels the + * action. + */ + try { + DefaultLanguageService.getLanguageService() + .getLanguage(getAlternativeLanguageID(getCodeUnit(context))); + } + catch (LanguageNotFoundException e) { + throw new AssertionError(e); // I just looked it up + } + super.actionPerformed(context); + } + + @Override + protected Language getLanguage(CodeUnit cu) { + return getPlatform(cu).getLanguage(); + } + + @Override + protected Assembler getAssembler(CodeUnit cu) { + return Assemblers.getAssembler(language); + } + + @Override + protected void applyPatch(byte[] data) throws MemoryAccessException { + TraceProgramView view = getView(); + if (view == null) { + return; + } + Address address = getAddress(); + // Get code unit and dependencies before invalidating it, just in case. + CodeUnit cu = getCodeUnit(); + RegisterValue contextValue = getContextValue(cu); + TracePlatform platform = getPlatform(cu); + view.getMemory().setBytes(address, data); // This invalidates cu + AddressSetView set = new AddressSet(address, address.add(data.length - 1)); + TraceDisassembleCommand dis = new TraceDisassembleCommand(platform, address, set); + dis.setInitialContext(contextValue); + dis.run(tool, view); + } + + protected TraceProgramView getView() { + Program program = getProgram(); + if (!(program instanceof TraceProgramView)) { + return null; + } + return (TraceProgramView) program; + } +} diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/disassemble/CurrentPlatformTraceDisassembleAction.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/disassemble/CurrentPlatformTraceDisassembleAction.java new file mode 100644 index 0000000000..be213364f6 --- /dev/null +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/disassemble/CurrentPlatformTraceDisassembleAction.java @@ -0,0 +1,117 @@ +/* ### + * 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.disassemble; + +import docking.ActionContext; +import docking.action.*; +import ghidra.app.context.ListingActionContext; +import ghidra.app.plugin.core.debug.disassemble.DebuggerDisassemblerPlugin.Reqs; +import ghidra.app.plugin.core.debug.mapping.DebuggerPlatformMapper; +import ghidra.app.plugin.core.debug.mapping.DisassemblyResult; +import ghidra.framework.cmd.TypedBackgroundCommand; +import ghidra.program.model.address.*; +import ghidra.program.model.listing.Program; +import ghidra.program.util.ProgramSelection; +import ghidra.trace.model.Trace; +import ghidra.trace.model.program.TraceProgramView; +import ghidra.trace.model.target.TraceObject; +import ghidra.trace.model.thread.TraceObjectThread; +import ghidra.trace.model.thread.TraceThread; +import ghidra.util.task.TaskMonitor; + +public class CurrentPlatformTraceDisassembleAction extends DockingAction { + private static final String NAME = "Disassemble"; + private static final String MENU_GROUP = "Disassembly"; + private static final KeyBindingData KEY_BINDING = new KeyBindingData("D"); + + private final DebuggerDisassemblerPlugin plugin; + + public CurrentPlatformTraceDisassembleAction(DebuggerDisassemblerPlugin plugin) { + super(NAME, plugin.getName()); + this.plugin = plugin; + + setPopupMenuData(new MenuData(new String[] { NAME }, MENU_GROUP)); + setKeyBindingData(KEY_BINDING); + } + + protected TraceObject getObject(TraceThread thread) { + if (!(thread instanceof TraceObjectThread)) { + return null; + } + return ((TraceObjectThread) thread).getObject(); + } + + protected Reqs getReqs(ActionContext context) { + if (plugin.platformService == null) { + return null; + } + if (!(context instanceof ListingActionContext)) { + return null; + } + ListingActionContext lac = (ListingActionContext) context; + Program program = lac.getProgram(); + if (!(program instanceof TraceProgramView)) { + return null; + } + TraceProgramView view = (TraceProgramView) program; + Trace trace = view.getTrace(); + TraceThread thread = plugin.traceManager == null ? null + : plugin.traceManager.getCurrentThreadFor(trace); + TraceObject object = getObject(thread); + DebuggerPlatformMapper mapper = + plugin.platformService.getMapper(trace, object, view.getSnap()); + return new Reqs(mapper, thread, object, view); + } + + @Override + public boolean isAddToPopup(ActionContext context) { + Reqs reqs = getReqs(context); + return reqs != null; + } + + @Override + public void actionPerformed(ActionContext context) { + Reqs reqs = getReqs(context); + if (reqs == null) { + return; + } + ListingActionContext lac = (ListingActionContext) context; + Address address = lac.getAddress(); + AddressSpace space = address.getAddressSpace(); + AddressSetView set; + ProgramSelection selection = lac.getSelection(); + if (selection != null && !selection.isEmpty()) { + set = selection; + } + else { + set = reqs.view.getAddressFactory() + .getAddressSet(space.getMinAddress(), space.getMaxAddress()); + } + TypedBackgroundCommand cmd = + new TypedBackgroundCommand<>(NAME, true, true, false) { + @Override + public boolean applyToTyped(TraceProgramView view, TaskMonitor monitor) { + DisassemblyResult result = reqs.mapper.disassemble( + reqs.thread, reqs.object, address, set, view.getSnap(), monitor); + if (!result.isSuccess()) { + plugin.getTool().setStatusInfo(result.getErrorMessage(), true); + } + return true; + } + }; + cmd.run(plugin.getTool(), reqs.view); + } +} diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/disassemble/CurrentPlatformTracePatchInstructionAction.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/disassemble/CurrentPlatformTracePatchInstructionAction.java new file mode 100644 index 0000000000..676fa0219d --- /dev/null +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/disassemble/CurrentPlatformTracePatchInstructionAction.java @@ -0,0 +1,50 @@ +/* ### + * 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.disassemble; + +import ghidra.program.model.lang.RegisterValue; +import ghidra.program.model.listing.CodeUnit; +import ghidra.trace.model.guest.TracePlatform; +import ghidra.trace.model.listing.TraceInstruction; + +public class CurrentPlatformTracePatchInstructionAction + extends AbstractTracePatchInstructionAction { + + public CurrentPlatformTracePatchInstructionAction(DebuggerDisassemblerPlugin plugin) { + super(plugin, "Patch Instruction"); + } + + @Override + protected boolean isApplicableToUnit(CodeUnit cu) { + if (!super.isApplicableToUnit(cu)) { + return false; + } + return cu instanceof TraceInstruction; + } + + @Override + protected TracePlatform getPlatform(CodeUnit cu) { + // Can safely cast because of isApplicableToUnit + TraceInstruction ins = (TraceInstruction) cu; + return ins.getPlatform(); + } + + @Override + protected RegisterValue getContextValue(CodeUnit cu) { + TraceInstruction ins = (TraceInstruction) cu; + return ins.getRegisterValue(ins.getBaseContextRegister()); + } +} diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/disassemble/DebuggerDisassemblerPlugin.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/disassemble/DebuggerDisassemblerPlugin.java new file mode 100644 index 0000000000..2ddc0b40b8 --- /dev/null +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/disassemble/DebuggerDisassemblerPlugin.java @@ -0,0 +1,231 @@ +/* ### + * 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.disassemble; + +import java.util.*; + +import docking.ActionContext; +import docking.Tool; +import docking.action.DockingActionIf; +import docking.actions.PopupActionProvider; +import generic.jar.ResourceFile; +import ghidra.app.context.ListingActionContext; +import ghidra.app.plugin.PluginCategoryNames; +import ghidra.app.plugin.core.debug.DebuggerPluginPackage; +import ghidra.app.plugin.core.debug.mapping.DebuggerPlatformMapper; +import ghidra.app.services.DebuggerPlatformService; +import ghidra.app.services.DebuggerTraceManagerService; +import ghidra.framework.plugintool.*; +import ghidra.framework.plugintool.AutoService.Wiring; +import ghidra.framework.plugintool.annotation.AutoServiceConsumed; +import ghidra.framework.plugintool.util.PluginStatus; +import ghidra.program.model.address.Address; +import ghidra.program.model.lang.*; +import ghidra.program.model.listing.Program; +import ghidra.program.util.DefaultLanguageService; +import ghidra.program.util.ProgramContextImpl; +import ghidra.trace.model.Trace; +import ghidra.trace.model.guest.TraceGuestPlatform; +import ghidra.trace.model.guest.TracePlatform; +import ghidra.trace.model.program.TraceProgramView; +import ghidra.trace.model.target.TraceObject; +import ghidra.trace.model.thread.TraceThread; + +@PluginInfo( + shortDescription = "Disassemble trace bytes in the debugger", + description = "Provides 'Disassemble as' actions for traces.", + category = PluginCategoryNames.DEBUGGER, + packageName = DebuggerPluginPackage.NAME, + status = PluginStatus.RELEASED, + eventsConsumed = { + }, + eventsProduced = { + }, + servicesRequired = { + DebuggerTraceManagerService.class, + DebuggerPlatformService.class, + }, + servicesProvided = { + }) +public class DebuggerDisassemblerPlugin extends Plugin implements PopupActionProvider { + + protected static class Reqs { + final DebuggerPlatformMapper mapper; + final TraceThread thread; + final TraceObject object; + final TraceProgramView view; + + public Reqs(DebuggerPlatformMapper mapper, TraceThread thread, TraceObject object, + TraceProgramView view) { + this.mapper = mapper; + this.thread = thread; + this.object = object; + this.view = view; + } + } + + protected static RegisterValue deriveAlternativeDefaultContext(Language language, + LanguageID alternative, Address address) { + LanguageService langServ = DefaultLanguageService.getLanguageService(); + Language altLang; + try { + altLang = langServ.getLanguage(alternative); + } + catch (LanguageNotFoundException e) { + // I just looked it up + throw new AssertionError(e); + } + + ProgramContextImpl ctx = new ProgramContextImpl(altLang); + altLang.applyContextSettings(ctx); + Address altAddress = altLang.getAddressFactory() + .getAddressSpace(address.getAddressSpace().getPhysicalSpace().getName()) + .getAddress(address.getOffset()); + + RegisterValue altVal = ctx.getDisassemblyContext(altAddress).getBaseRegisterValue(); + RegisterValue result = + new RegisterValue(language.getContextBaseRegister(), altVal.toBytes()); + return result; + } + + @AutoServiceConsumed + DebuggerTraceManagerService traceManager; + @AutoServiceConsumed + DebuggerPlatformService platformService; + @SuppressWarnings("unused") + private final Wiring autoServiceWiring; + + CurrentPlatformTraceDisassembleAction actionDisassemble; + CurrentPlatformTracePatchInstructionAction actionPatchInstruction; + + public DebuggerDisassemblerPlugin(PluginTool tool) { + super(tool); + this.autoServiceWiring = AutoService.wireServicesProvidedAndConsumed(this); + } + + @Override + protected void init() { + super.init(); + tool.addPopupActionProvider(this); + createActions(); + } + + protected void createActions() { + actionDisassemble = new CurrentPlatformTraceDisassembleAction(this); + actionPatchInstruction = new CurrentPlatformTracePatchInstructionAction(this); + + tool.addAction(actionDisassemble); + tool.addAction(actionPatchInstruction); + } + + /** + * Get languages which have the same parser, but alternative initial contexts + * + * @param language the language for which alternatives are desired + * @return the collections of languages + */ + protected Collection getAlternativeLanguageIDs(Language language) { + // One of the alternatives is the language's actual default + LanguageDescription desc = language.getLanguageDescription(); + if (!(desc instanceof SleighLanguageDescription)) { + return List.of(); + } + SleighLanguageDescription sld = (SleighLanguageDescription) desc; + ResourceFile slaFile = sld.getSlaFile(); + + List result = new ArrayList<>(); + LanguageService langServ = DefaultLanguageService.getLanguageService(); + for (LanguageDescription altDesc : langServ.getLanguageDescriptions(false)) { + if (!(altDesc instanceof SleighLanguageDescription)) { + continue; + } + SleighLanguageDescription altSld = (SleighLanguageDescription) altDesc; + if (!altSld.getSlaFile().equals(slaFile)) { + continue; + } + if (altSld.getEndian() != sld.getEndian()) { + // Memory endian, not necessarily instruction endian + continue; + } + result.add(altSld.getLanguageID()); + } + return result; + } + + protected void getActionsForLanguage(List result, + TracePlatform platform) { + for (LanguageID langID : getAlternativeLanguageIDs(platform.getLanguage())) { + result.add(new FixedPlatformTraceDisassembleAction(this, langID, platform)); + result.add(new FixedPlatformTracePatchInstructionAction(this, langID, platform)); + } + } + + protected void getActionsForHost(List result, Trace trace) { + Language language = trace.getBaseLanguage(); + if (language.getProcessor() == Processor.toProcessor("DATA")) { + return; + } + getActionsForLanguage(result, trace.getPlatformManager().getHostPlatform()); + } + + protected void getActionsForGuest(List result, + TraceGuestPlatform guest, Address hostAddress) { + if (!guest.getHostAddressSet().contains(hostAddress)) { + return; + } + /* + * TODO: May need to distinguish platform if many for same language, esp., if mapped + * differently + */ + getActionsForLanguage(result, guest); + } + + protected void getActionsForAllGuests(List result, Trace trace, + Address address) { + for (TraceGuestPlatform guest : trace.getPlatformManager().getGuestPlatforms()) { + getActionsForGuest(result, guest, address); + } + } + + protected List getActionsFor(List result, Trace trace, + long snap, Address address) { + getActionsForHost(result, trace); + getActionsForAllGuests(result, trace, address); + return result; + } + + @Override + public List getPopupActions(Tool tool, ActionContext context) { + if (!(context instanceof ListingActionContext)) { + return null; + } + /** + * I could use Navigatable.isDynamic, but it seems more appropriate, since the types are in + * scope here, to check for an actual trace. + */ + ListingActionContext lac = (ListingActionContext) context; + Address address = lac.getAddress(); + if (address == null) { + return null; + } + Program program = lac.getProgram(); + if (!(program instanceof TraceProgramView)) { + return null; + } + TraceProgramView view = (TraceProgramView) program; + return getActionsFor(new ArrayList<>(), view.getTrace(), view.getSnap(), address); + } +} diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/disassemble/FixedPlatformTraceDisassembleAction.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/disassemble/FixedPlatformTraceDisassembleAction.java new file mode 100644 index 0000000000..bb5e2a531b --- /dev/null +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/disassemble/FixedPlatformTraceDisassembleAction.java @@ -0,0 +1,47 @@ +/* ### + * 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.disassemble; + +import docking.action.MenuData; +import ghidra.program.model.lang.LanguageID; +import ghidra.trace.model.guest.TracePlatform; +import ghidra.trace.model.program.TraceProgramView; + +public class FixedPlatformTraceDisassembleAction extends AbstractTraceDisassembleAction { + private final LanguageID altLangID; + private final TracePlatform platform; + + public FixedPlatformTraceDisassembleAction(DebuggerDisassemblerPlugin plugin, + LanguageID altLangID, TracePlatform platform) { + super(plugin, "Disassemble Trace as " + altLangID); + this.altLangID = altLangID; + this.platform = platform; + + // TODO: Human-readable description? + setPopupMenuData( + new MenuData(new String[] { "Disassemble as " + altLangID }, "Disassembly")); + } + + @Override + protected TracePlatform getPlatform(TraceProgramView view) { + return platform; + } + + @Override + protected LanguageID getAlternativeLanguageID() { + return altLangID; + } +} diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/disassemble/FixedPlatformTracePatchInstructionAction.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/disassemble/FixedPlatformTracePatchInstructionAction.java new file mode 100644 index 0000000000..135de2fd7f --- /dev/null +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/disassemble/FixedPlatformTracePatchInstructionAction.java @@ -0,0 +1,44 @@ +/* ### + * 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.disassemble; + +import ghidra.program.model.lang.LanguageID; +import ghidra.program.model.listing.CodeUnit; +import ghidra.trace.model.guest.TracePlatform; + +public class FixedPlatformTracePatchInstructionAction extends AbstractTracePatchInstructionAction { + private final LanguageID altLangID; + private final TracePlatform platform; + + public FixedPlatformTracePatchInstructionAction(DebuggerDisassemblerPlugin plugin, + LanguageID altLangID, TracePlatform platform) { + super(plugin, "Patch Instruction using " + altLangID); + setKeyBindingData(null); + + this.altLangID = altLangID; + this.platform = platform; + } + + @Override + protected TracePlatform getPlatform(CodeUnit cu) { + return platform; + } + + @Override + protected LanguageID getAlternativeLanguageID(CodeUnit cu) { + return altLangID; + } +} 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/disassemble/TraceDisassembleCommand.java similarity index 65% rename from Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/workflow/DisassembleTraceCommand.java rename to Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/disassemble/TraceDisassembleCommand.java index f0a9481248..02b6a43ce4 100644 --- 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/disassemble/TraceDisassembleCommand.java @@ -13,35 +13,34 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.plugin.core.debug.workflow; +package ghidra.app.plugin.core.debug.disassemble; + +import com.google.common.collect.Range; import ghidra.framework.cmd.TypedBackgroundCommand; import ghidra.program.disassemble.Disassembler; import ghidra.program.model.address.*; import ghidra.program.model.lang.*; +import ghidra.program.model.listing.Instruction; 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.guest.TracePlatform; 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); - } +public class TraceDisassembleCommand extends TypedBackgroundCommand { + protected final TracePlatform platform; protected final Address start; protected final AddressSetView restrictedSet; protected RegisterValue initialContext; private AddressSetView disassembled; - public DisassembleTraceCommand(Address start, AddressSetView restrictedSet) { + public TraceDisassembleCommand(TracePlatform platform, Address start, + AddressSetView restrictedSet) { super("Disassemble", true, true, false); + this.platform = platform; this.start = start; this.restrictedSet = restrictedSet; } @@ -51,11 +50,12 @@ public class DisassembleTraceCommand extends TypedBackgroundCommand> setsByCSpec) { - return StringUtils.join(setsByCSpec.entrySet().stream().map(ent -> { - LanguageCompilerSpecPair lcsp = ent.getKey(); - String regs = StringUtils.join( - ent.getValue().stream().map(Register::getName).collect(Collectors.toList()), ','); - return lcsp.languageID + "/" + lcsp.compilerSpecID + ":" + regs; - }).collect(Collectors.toList()), ';'); + return setsByCSpec.entrySet() + .stream() + .map(ent -> { + LanguageCompilerSpecPair lcsp = ent.getKey(); + return lcsp.languageID + "/" + lcsp.compilerSpecID + ":" + ent.getValue() + .stream() + .filter(r -> r != null) + .map(Register::getName) + .collect(Collectors.joining(",")); + }) + .collect(Collectors.joining(";")); } @Override diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/AbstractDebuggerPlatformMapper.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/AbstractDebuggerPlatformMapper.java index aaddc4fe64..b1da0a65fe 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/AbstractDebuggerPlatformMapper.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/AbstractDebuggerPlatformMapper.java @@ -18,13 +18,13 @@ 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.disassemble.TraceDisassembleCommand; 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.guest.TracePlatform; import ghidra.trace.model.target.TraceObject; import ghidra.trace.model.thread.TraceThread; import ghidra.util.task.TaskMonitor; @@ -72,16 +72,14 @@ public abstract class AbstractDebuggerPlatformMapper implements DebuggerPlatform if (isCancelSilently(start, snap)) { return DisassemblyResult.CANCELLED; } - TraceGuestPlatform guest = - trace.getPlatformManager().getGuestPlatform(getCompilerSpec(object)); + TracePlatform platform = trace.getPlatformManager().getPlatform(getCompilerSpec(object)); Collection injects = getDisassemblyInjections(object); - DisassembleTraceCommand dis = - DisassembleTraceCommand.create(guest, start, restricted); - Language language = guest == null ? trace.getBaseLanguage() : guest.getLanguage(); + TraceDisassembleCommand dis = new TraceDisassembleCommand(platform, start, restricted); + Language language = platform.getLanguage(); AddressSet startSet = new AddressSet(start); for (DisassemblyInject i : injects) { - i.pre(tool, dis, trace, language, snap, null, startSet, restricted); + i.pre(tool, dis, trace, language, snap, thread, startSet, restricted); } boolean result = dis.applyToTyped(trace.getFixedProgramView(snap), monitor); if (!result) { 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 index ac7e7e551e..730d66cf46 100644 --- 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 @@ -28,6 +28,10 @@ public interface DebuggerPlatformMapper { /** * Prepare the given trace for interpretation under this mapper * + *

+ * Likely, this will need to modify the trace database. It must start its own transaction for + * doing so. + * * @param trace the trace * @param snap the snap */ diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/DefaultDebuggerPlatformMapper.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/DefaultDebuggerPlatformMapper.java index fc14beb139..e2d607933d 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/DefaultDebuggerPlatformMapper.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/DefaultDebuggerPlatformMapper.java @@ -24,6 +24,7 @@ import ghidra.trace.model.guest.TraceGuestPlatform; import ghidra.trace.model.guest.TracePlatformManager; import ghidra.trace.model.target.TraceObject; import ghidra.util.MathUtilities; +import ghidra.util.database.UndoableTransaction; public class DefaultDebuggerPlatformMapper extends AbstractDebuggerPlatformMapper { @@ -54,14 +55,26 @@ public class DefaultDebuggerPlatformMapper extends AbstractDebuggerPlatformMappe @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 + try (UndoableTransaction tid = UndoableTransaction.start(trace, "Add guest " + + cSpec.getLanguage().getLanguageDescription() + "/" + cSpec.getCompilerSpecDescription(), + true)) { + TracePlatformManager platformManager = trace.getPlatformManager(); + TraceGuestPlatform platform = platformManager.getOrAddGuestPlatform(cSpec); + if (platform == null) { + return; // It's the host compiler spec + } + addMappedRanges(platform); } - addMappedRanges(platform); } + /** + * Add mapped ranges if not already present + * + *

+ * A transaction is already started when this method is invoked. + * + * @param platform the platform + */ protected void addMappedRanges(TraceGuestPlatform platform) { Trace trace = platform.getTrace(); AddressSpace hostSpace = trace.getBaseAddressFactory().getDefaultAddressSpace(); 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 69eb316a22..3dde835fa8 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,7 +17,9 @@ package ghidra.app.plugin.core.debug.platform.arm; import java.math.BigInteger; -import ghidra.app.plugin.core.debug.workflow.*; +import ghidra.app.plugin.core.debug.disassemble.TraceDisassembleCommand; +import ghidra.app.plugin.core.debug.workflow.DisassemblyInject; +import ghidra.app.plugin.core.debug.workflow.DisassemblyInjectInfo; import ghidra.framework.plugintool.PluginTool; import ghidra.program.model.address.AddressSetView; import ghidra.program.model.lang.*; @@ -58,7 +60,7 @@ public class ArmDisassemblyInject implements DisassemblyInject { } @Override - public void pre(PluginTool tool, DisassembleTraceCommand command, Trace trace, + public void pre(PluginTool tool, TraceDisassembleCommand command, Trace trace, Language language, long snap, TraceThread thread, AddressSetView startSet, AddressSetView restricted) { /** @@ -79,7 +81,7 @@ public class ArmDisassemblyInject implements DisassemblyInject { TraceMemoryRegisterSpace regs = trace.getMemoryManager().getMemoryRegisterSpace(thread, false); /** - * Some variants (particularly Cortex-M) are missing cpsr This seems to indicate it only + * 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. */ @@ -87,6 +89,12 @@ public class ArmDisassemblyInject implements DisassemblyInject { command.setInitialContext(new RegisterValue(tModeReg, BigInteger.ONE)); return; } + /** + * TODO: Once we have register mapping figured out for object-based traces, we need to have + * this check the cpsr register there, instead. Better yet, regarding epsr and xpsr, we can + * actually check them, even though they don't exist in the slaspec, because we have access + * to the raw recorded register objects. + */ RegisterValue cpsrVal = regs.getValue(snap, cpsrReg); if (isThumbMode(cpsrVal)) { 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 e363bc812b..f3d5355aeb 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,6 +22,7 @@ import java.util.Set; import java.util.concurrent.*; import java.util.stream.Collectors; +import ghidra.app.plugin.core.debug.disassemble.TraceDisassembleCommand; import ghidra.app.plugin.core.debug.workflow.*; import ghidra.app.services.DebuggerModelService; import ghidra.app.services.TraceRecorder; @@ -49,7 +50,7 @@ public class DbgengX64DisassemblyInject implements DisassemblyInject { } @Override - public void pre(PluginTool tool, DisassembleTraceCommand command, Trace trace, + public void pre(PluginTool tool, TraceDisassembleCommand command, Trace trace, Language language, long snap, TraceThread thread, AddressSetView startSet, AddressSetView restricted) { AddressRange first = startSet.getFirstRange(); 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 index 2b76447483..f9bdbe240b 100644 --- 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 @@ -84,7 +84,7 @@ public class GdbDebuggerPlatformOpinion extends AbstractDebuggerPlatformOpinion @Override protected Set getOffers(TraceObject object, long snap, TraceObject env, String debugger, String arch, String os, Endian endian) { - if (!"gdb".equals(debugger.toLowerCase())) { + if (debugger == null || !"gdb".equals(debugger.toLowerCase())) { return Set.of(); } return getCompilerSpecsForGnu(arch, endian).stream().flatMap(lcsp -> { 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 deleted file mode 100644 index e388783a96..0000000000 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/workflow/DisassembleGuestTraceCommand.java +++ /dev/null @@ -1,59 +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.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/DisassemblyInject.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/workflow/DisassemblyInject.java index eda4a799f8..60c611b459 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,6 +17,7 @@ package ghidra.app.plugin.core.debug.workflow; import java.util.Arrays; +import ghidra.app.plugin.core.debug.disassemble.TraceDisassembleCommand; import ghidra.framework.plugintool.PluginTool; import ghidra.program.model.address.AddressSetView; import ghidra.program.model.lang.Language; @@ -93,7 +94,7 @@ public interface DisassemblyInject extends ExtensionPoint { * @param startSet the starting address set, usually just the PC * @param restricted the set of disassemblable addresses */ - default void pre(PluginTool tool, DisassembleTraceCommand command, Trace trace, + default void pre(PluginTool tool, TraceDisassembleCommand command, Trace trace, Language language, long snap, TraceThread thread, AddressSetView startSet, AddressSetView restricted) { } diff --git a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/disassemble/DebuggerDisassemblyTest.java b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/disassemble/DebuggerDisassemblyTest.java new file mode 100644 index 0000000000..e7a6ed91db --- /dev/null +++ b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/disassemble/DebuggerDisassemblyTest.java @@ -0,0 +1,632 @@ +/* ### + * 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.disassemble; + +import static org.junit.Assert.*; + +import java.io.IOException; +import java.math.BigInteger; +import java.nio.ByteBuffer; +import java.util.Set; +import java.util.function.Predicate; +import java.util.function.Supplier; +import java.util.stream.Collectors; + +import org.junit.*; + +import com.google.common.collect.Range; + +import docking.action.DockingActionIf; +import generic.Unique; +import ghidra.app.context.ListingActionContext; +import ghidra.app.plugin.core.assembler.AssemblerPluginTestHelper; +import ghidra.app.plugin.core.debug.gui.AbstractGhidraHeadedDebuggerGUITest; +import ghidra.app.plugin.core.debug.gui.listing.DebuggerListingPlugin; +import ghidra.app.plugin.core.debug.gui.listing.DebuggerListingProvider; +import ghidra.app.plugin.core.debug.service.editing.DebuggerStateEditingServicePlugin; +import ghidra.app.plugin.core.debug.service.platform.DebuggerPlatformServicePlugin; +import ghidra.app.plugin.core.debug.service.workflow.DebuggerWorkflowServiceProxyPlugin; +import ghidra.app.plugin.core.debug.workflow.DisassembleAtPcDebuggerBot; +import ghidra.app.services.*; +import ghidra.app.services.DebuggerStateEditingService.StateEditingMode; +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.address.Address; +import ghidra.program.model.address.AddressSet; +import ghidra.program.model.lang.LanguageID; +import ghidra.program.model.lang.RegisterValue; +import ghidra.program.model.listing.Instruction; +import ghidra.program.util.ProgramLocation; +import ghidra.program.util.ProgramSelection; +import ghidra.trace.database.listing.DBTraceInstruction; +import ghidra.trace.database.listing.DBTraceInstructionsMemoryView; +import ghidra.trace.database.memory.DBTraceMemoryManager; +import ghidra.trace.database.memory.DBTraceMemoryRegisterSpace; +import ghidra.trace.database.program.DBTraceVariableSnapProgramView; +import ghidra.trace.database.target.DBTraceObject; +import ghidra.trace.database.target.DBTraceObjectManager; +import ghidra.trace.model.guest.TraceGuestPlatform; +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.trace.model.thread.TraceThread; +import ghidra.util.database.UndoableTransaction; +import ghidra.util.task.TaskMonitor; + +public class DebuggerDisassemblyTest extends AbstractGhidraHeadedDebuggerGUITest { + protected DebuggerDisassemblerPlugin disassemblerPlugin; + protected DebuggerPlatformService platformService; + protected DebuggerListingProvider listingProvider; + protected SchemaContext ctx; + + @Before + public void setUpDisassemblyTest() throws Exception { + ctx = XmlSchemaContext.deserialize("" + // + "" + // + " " + // + " " + // + " " + // + " " + // + " " + // + " " + // + " " + // + " " + // + " " + // + " " + // + " " + // + " " + // + " " + // + " " + // + " " + // + " " + // + " " + // + " " + // + " " + // + " " + // + " " + // + " " + // + " " + // + " " + // + " " + // + " " + // + " " + // + " " + // + " " + // + " " + // + " " + // + " " + // + " " + // + " " + // + " " + // + " " + // + " " + // + ""); + + addPlugin(tool, DebuggerListingPlugin.class); + platformService = addPlugin(tool, DebuggerPlatformServicePlugin.class); + disassemblerPlugin = addPlugin(tool, DebuggerDisassemblerPlugin.class); + listingProvider = waitForComponentProvider(DebuggerListingProvider.class); + } + + protected void assertX86Nop(Instruction instruction) { + assertNotNull(instruction); + assertEquals("NOP", instruction.getMnemonicString()); + } + + protected void enableAutoDisassembly() throws Throwable { + DebuggerWorkflowService workflowService = + addPlugin(tool, DebuggerWorkflowServiceProxyPlugin.class); + Set disBot = workflowService.getAllBots() + .stream() + .filter(b -> b instanceof DisassembleAtPcDebuggerBot) + .collect(Collectors.toSet()); + assertEquals(1, disBot.size()); + workflowService.enableBots(disBot); + } + + protected TraceObjectThread createPolyglotTrace(String arch, long offset, + Supplier byteSupplier) throws IOException { + 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, arch); + + 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(offset, offset + 0xffff)); + // 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(offset)); + + DBTraceMemoryManager memory = tb.trace.getMemoryManager(); + ByteBuffer bytes = byteSupplier.get(); + assertEquals(bytes.remaining(), memory.putBytes(0, tb.addr(offset), bytes)); + } + TraceObjectThread thread = + objects.getObjectByCanonicalPath(TraceObjectKeyPath.parse("Targets[0].Threads[0]")) + .queryInterface(TraceObjectThread.class); + traceManager.activateThread(thread); + return thread; + } + + protected void createLegacyTrace(String langID, long offset, + Supplier byteSupplier) throws Throwable { + createAndOpenTrace(langID); + + try (UndoableTransaction tid = tb.startTransaction()) { + DBTraceMemoryManager memory = tb.trace.getMemoryManager(); + memory.createRegion("Memory[bin:.text]", 0, tb.range(offset, offset + 0xffff), + Set.of(TraceMemoryFlag.EXECUTE)); + ByteBuffer bytes = byteSupplier.get(); + assertEquals(bytes.remaining(), memory.putBytes(0, tb.addr(offset), bytes)); + } + traceManager.activateTrace(tb.trace); + } + + @Test + public void testAutoDisassembleX8664() throws Throwable { + enableAutoDisassembly(); + createPolyglotTrace("x86-64", 0x00400000, () -> tb.buf(0x90, 0x90, 0x90)); + + 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))); + }); + } + + @Test + public void testCurrentDisassembleActionHostArm() throws Throwable { + createLegacyTrace("ARM:LE:32:v8", 0x00400000, () -> tb.buf(0x1e, 0xff, 0x2f, 0xe1)); + + // Fabricate the cpsr so that ARM is used. Otherwise, it will assume Cortex-M, so THUMB + TraceThread thread; + try (UndoableTransaction tid = tb.startTransaction()) { + thread = tb.getOrAddThread("Threads[0]", 0); + DBTraceMemoryRegisterSpace regs = + tb.trace.getMemoryManager().getMemoryRegisterSpace(thread, true); + regs.setValue(0, new RegisterValue(tb.language.getRegister("cpsr"), BigInteger.ZERO)); + } + waitForDomainObject(tb.trace); + traceManager.activateThread(thread); + waitForSwing(); + + Address start = tb.addr(0x00400000); + + // Ensure the mapper is added to the trace + assertNotNull(platformService.getMapper(tb.trace, null, 0)); + + DBTraceVariableSnapProgramView view = tb.trace.getProgramView(); + ListingActionContext actionContext = new ListingActionContext(listingProvider, + listingProvider, view, new ProgramLocation(view, start), + new ProgramSelection(start, start.addWrap(3)), null); + performAction(disassemblerPlugin.actionDisassemble, actionContext, true); + waitForTasks(); + + DBTraceInstruction ins = tb.trace.getCodeManager().instructions().getAt(0, start); + assertNotNull(ins); + assertEquals("bx lr", ins.toString()); + assertEquals(4, ins.getLength()); + } + + @Test + public void testCurrentDisassembleActionHostThumb() throws Throwable { + createLegacyTrace("ARM:LE:32:v8", 0x00400000, () -> tb.buf(0x70, 0x47)); + + // Fabricate the cpsr so that THUMB is used, even though we could omit as in Cortex-M + TraceThread thread; + try (UndoableTransaction tid = tb.startTransaction()) { + thread = tb.getOrAddThread("Threads[0]", 0); + DBTraceMemoryRegisterSpace regs = + tb.trace.getMemoryManager().getMemoryRegisterSpace(thread, true); + regs.setValue(0, + new RegisterValue(tb.language.getRegister("cpsr"), BigInteger.ONE.shiftLeft(5))); + } + waitForDomainObject(tb.trace); + traceManager.activateThread(thread); + waitForSwing(); + + Address start = tb.addr(0x00400000); + + // Ensure the mapper is added to the trace + assertNotNull(platformService.getMapper(tb.trace, null, 0)); + + DBTraceVariableSnapProgramView view = tb.trace.getProgramView(); + ListingActionContext actionContext = new ListingActionContext(listingProvider, + listingProvider, view, new ProgramLocation(view, start), + new ProgramSelection(start, start.addWrap(3)), null); + performAction(disassemblerPlugin.actionDisassemble, actionContext, true); + waitForTasks(); + + DBTraceInstruction ins = tb.trace.getCodeManager().instructions().getAt(0, start); + assertNotNull(ins); + assertEquals("bx lr", ins.toString()); + assertEquals(2, ins.getLength()); + } + + @Test + public void testCurrentDisassembleActionGuestArm() throws Throwable { + TraceObjectThread thread = + createPolyglotTrace("armv8le", 0x00400000, () -> tb.buf(0x1e, 0xff, 0x2f, 0xe1)); + + // Set up registers so injects will select ARM + // TODO + + Address start = tb.addr(0x00400000); + + // Ensure the mapper is added to the trace + assertNotNull(platformService.getMapper(tb.trace, thread.getObject(), 0)); + + DBTraceVariableSnapProgramView view = tb.trace.getProgramView(); + ListingActionContext actionContext = new ListingActionContext(listingProvider, + listingProvider, view, new ProgramLocation(view, start), + new ProgramSelection(start, start.addWrap(3)), null); + performAction(disassemblerPlugin.actionDisassemble, actionContext, true); + waitForTasks(); + + DBTraceInstruction ins = tb.trace.getCodeManager().instructions().getAt(0, start); + assertNotNull(ins); + assertEquals("bx lr", ins.toString()); + assertEquals(4, ins.getLength()); + } + + @Test + @Ignore("TODO") + public void testCurrentDisassembleActionGuestThumb() throws Throwable { + TraceObjectThread thread = + createPolyglotTrace("armv8le", 0x00400000, () -> tb.buf(0x70, 0x47)); + + // Set up registers to injects will select THUMB + // TODO + + Address start = tb.addr(0x00400000); + + // Ensure the mapper is added to the trace + assertNotNull(platformService.getMapper(tb.trace, thread.getObject(), 0)); + + DBTraceVariableSnapProgramView view = tb.trace.getProgramView(); + ListingActionContext actionContext = new ListingActionContext(listingProvider, + listingProvider, view, new ProgramLocation(view, start), + new ProgramSelection(start, start.addWrap(3)), null); + performAction(disassemblerPlugin.actionDisassemble, actionContext, true); + waitForTasks(); + + DBTraceInstruction ins = tb.trace.getCodeManager().instructions().getAt(0, start); + assertNotNull(ins); + assertEquals("bx lr", ins.toString()); + assertEquals(2, ins.getLength()); + } + + protected void performFixedDisassembleAction(Address start, + Predicate actionPred) { + DBTraceVariableSnapProgramView view = tb.trace.getProgramView(); + ListingActionContext actionContext = new ListingActionContext(listingProvider, + listingProvider, view, new ProgramLocation(view, start), + new ProgramSelection(start, start.addWrap(3)), null); + DockingActionIf action = + Unique.assertOne(disassemblerPlugin.getPopupActions(tool, actionContext) + .stream() + .filter(a -> a.isAddToPopup(actionContext)) + .filter(actionPred)); + performAction(action, actionContext, true); + waitForTasks(); + } + + @Test + public void testFixedDisassembleActionsHostArm() throws Throwable { + createLegacyTrace("ARM:LE:32:v8", 0x00400000, () -> tb.buf(0x1e, 0xff, 0x2f, 0xe1)); + Address start = tb.addr(0x00400000); + + // Ensure the mapper is added to the trace + assertNotNull(platformService.getMapper(tb.trace, null, 0)); + + performFixedDisassembleAction(start, a -> !a.getName().contains("v8T")); + + DBTraceInstruction ins = tb.trace.getCodeManager().instructions().getAt(0, start); + assertNotNull(ins); + assertEquals("bx lr", ins.toString()); + assertEquals(4, ins.getLength()); + } + + @Test + public void testFixedDisassembleActionsGuestArm() throws Throwable { + TraceObjectThread thread = + createPolyglotTrace("armv8le", 0x00400000, () -> tb.buf(0x1e, 0xff, 0x2f, 0xe1)); + Address start = tb.addr(0x00400000); + + // Ensure the mapper is added to the trace + assertNotNull(platformService.getMapper(tb.trace, thread.getObject(), 0)); + + performFixedDisassembleAction(start, a -> !a.getName().contains("v8T")); + + DBTraceInstruction ins = tb.trace.getCodeManager().instructions().getAt(0, start); + assertNotNull(ins); + assertEquals("bx lr", ins.toString()); + assertEquals(4, ins.getLength()); + } + + @Test + public void testFixedDisassembleActionsGuestThumb() throws Throwable { + TraceObjectThread thread = + createPolyglotTrace("armv8le", 0x00400000, () -> tb.buf(0x70, 0x47)); + Address start = tb.addr(0x00400000); + + // Ensure the mapper is added to the trace + assertNotNull(platformService.getMapper(tb.trace, thread.getObject(), 0)); + + performFixedDisassembleAction(start, a -> a.getName().contains("v8T")); + + DBTraceInstruction ins = tb.trace.getCodeManager().instructions().getAt(0, start); + assertNotNull(ins); + assertEquals("bx lr", ins.toString()); + assertEquals(2, ins.getLength()); + } + + @Test + public void testCurrentAssembleActionHostArm() throws Throwable { + // Assemble actions will think read-only otherwise + DebuggerStateEditingService editingService = + addPlugin(tool, DebuggerStateEditingServicePlugin.class); + + createLegacyTrace("ARM:LE:32:v8", 0x00400000, () -> tb.buf(0x00, 0x00, 0x00, 0x00)); + Address start = tb.addr(0x00400000); + editingService.setCurrentMode(tb.trace, StateEditingMode.WRITE_TRACE); + + // Ensure the mapper is added to the trace + assertNotNull(platformService.getMapper(tb.trace, null, 0)); + + try (UndoableTransaction tid = tb.startTransaction()) { + tb.addInstruction(0, start, tb.host); + } + waitForDomainObject(tb.trace); + + DBTraceVariableSnapProgramView view = tb.trace.getProgramView(); + ListingActionContext actionContext = new ListingActionContext(listingProvider, + listingProvider, view, new ProgramLocation(view, start), + new ProgramSelection(start, start.addWrap(1)), null); + + assertTrue(disassemblerPlugin.actionPatchInstruction.isEnabledForContext(actionContext)); + AssemblerPluginTestHelper helper = + new AssemblerPluginTestHelper(disassemblerPlugin.actionPatchInstruction, null, + listingProvider, tb.trace.getProgramView()); + Instruction result = helper.patchInstructionAt(start, "andeq r0,r0,r0", "bx lr"); + + assertArrayEquals(tb.arr(0x1e, 0xff, 0x2f, 0xe1), result.getBytes()); + assertNull(result.getNext()); + } + + @Test + public void testCurrentAssembleActionHostThumb() throws Throwable { + // Assemble actions will think read-only otherwise + DebuggerStateEditingService editingService = + addPlugin(tool, DebuggerStateEditingServicePlugin.class); + + // Don't cheat here and choose v8T! + createLegacyTrace("ARM:LE:32:v8", 0x00400000, () -> tb.buf(0x00, 0x00)); + Address start = tb.addr(0x00400000); + editingService.setCurrentMode(tb.trace, StateEditingMode.WRITE_TRACE); + + // Ensure the mapper is added to the trace + assertNotNull(platformService.getMapper(tb.trace, null, 0)); + + try (UndoableTransaction tid = tb.startTransaction()) { + TraceDisassembleCommand dis = new TraceDisassembleCommand(tb.host, start, + new AddressSet(start, start.addWrap(1))); + dis.setInitialContext(DebuggerDisassemblerPlugin.deriveAlternativeDefaultContext( + tb.language, new LanguageID("ARM:LE:32:v8T"), start)); + dis.applyToTyped(tb.trace.getProgramView(), TaskMonitor.DUMMY); + } + waitForDomainObject(tb.trace); + + DBTraceVariableSnapProgramView view = tb.trace.getProgramView(); + ListingActionContext actionContext = new ListingActionContext(listingProvider, + listingProvider, view, new ProgramLocation(view, start), + new ProgramSelection(start, start.addWrap(1)), null); + + assertTrue(disassemblerPlugin.actionPatchInstruction.isEnabledForContext(actionContext)); + AssemblerPluginTestHelper helper = + new AssemblerPluginTestHelper(disassemblerPlugin.actionPatchInstruction, null, + listingProvider, tb.trace.getProgramView()); + Instruction result = helper.patchInstructionAt(start, "movs r0,r0", "bx lr"); + + assertArrayEquals(tb.arr(0x70, 0x47), result.getBytes()); + assertNull(result.getNext()); + } + + @Test + public void testCurrentAssembleActionGuestArm() throws Throwable { + // Assemble actions will think read-only otherwise + DebuggerStateEditingService editingService = + addPlugin(tool, DebuggerStateEditingServicePlugin.class); + + TraceObjectThread thread = + createPolyglotTrace("armv8le", 0x00400000, () -> tb.buf(0x00, 0x00, 0x00, 0x00)); + Address start = tb.addr(0x00400000); + editingService.setCurrentMode(tb.trace, StateEditingMode.WRITE_TRACE); + + // Ensure the mapper is added to the trace + assertNotNull(platformService.getMapper(tb.trace, thread.getObject(), 0)); + + TraceGuestPlatform guest = + Unique.assertOne(tb.trace.getPlatformManager().getGuestPlatforms()); + try (UndoableTransaction tid = tb.startTransaction()) { + tb.addInstruction(0, start, guest); + } + waitForDomainObject(tb.trace); + + DBTraceVariableSnapProgramView view = tb.trace.getProgramView(); + ListingActionContext actionContext = new ListingActionContext(listingProvider, + listingProvider, view, new ProgramLocation(view, start), + new ProgramSelection(start, start.addWrap(1)), null); + + assertTrue(disassemblerPlugin.actionPatchInstruction.isEnabledForContext(actionContext)); + AssemblerPluginTestHelper helper = + new AssemblerPluginTestHelper(disassemblerPlugin.actionPatchInstruction, null, + listingProvider, tb.trace.getProgramView()); + Instruction result = helper.patchInstructionAt(start, "andeq r0,r0,r0", "bx lr"); + + assertArrayEquals(tb.arr(0x1e, 0xff, 0x2f, 0xe1), result.getBytes()); + assertNull(result.getNext()); + } + + @Test + public void testCurrentAssembleActionGuestThumb() throws Throwable { + // Assemble actions will think read-only otherwise + DebuggerStateEditingService editingService = + addPlugin(tool, DebuggerStateEditingServicePlugin.class); + + TraceObjectThread thread = + createPolyglotTrace("armv8le", 0x00400000, () -> tb.buf(0x00, 0x00)); + Address start = tb.addr(0x00400000); + editingService.setCurrentMode(tb.trace, StateEditingMode.WRITE_TRACE); + + // Ensure the mapper is added to the trace + assertNotNull(platformService.getMapper(tb.trace, thread.getObject(), 0)); + + waitForPass(() -> Unique.assertOne(tb.trace.getPlatformManager().getGuestPlatforms())); + TraceGuestPlatform guest = + Unique.assertOne(tb.trace.getPlatformManager().getGuestPlatforms()); + try (UndoableTransaction tid = tb.startTransaction()) { + TraceDisassembleCommand dis = new TraceDisassembleCommand(guest, start, + new AddressSet(start, start.addWrap(1))); + dis.setInitialContext(DebuggerDisassemblerPlugin.deriveAlternativeDefaultContext( + guest.getLanguage(), new LanguageID("ARM:LE:32:v8T"), start)); + dis.applyToTyped(tb.trace.getProgramView(), TaskMonitor.DUMMY); + } + waitForDomainObject(tb.trace); + + DBTraceVariableSnapProgramView view = tb.trace.getProgramView(); + ListingActionContext actionContext = new ListingActionContext(listingProvider, + listingProvider, view, new ProgramLocation(view, start), + new ProgramSelection(start, start.addWrap(1)), null); + + assertTrue(disassemblerPlugin.actionPatchInstruction.isEnabledForContext(actionContext)); + AssemblerPluginTestHelper helper = + new AssemblerPluginTestHelper(disassemblerPlugin.actionPatchInstruction, null, + listingProvider, tb.trace.getProgramView()); + Instruction result = helper.patchInstructionAt(start, "movs r0,r0", "bx lr"); + + assertArrayEquals(tb.arr(0x70, 0x47), result.getBytes()); + assertNull(result.getNext()); + } + + protected Instruction performFixedAssembleAction(Address start, + Predicate actionPred, String assembly) { + DBTraceVariableSnapProgramView view = tb.trace.getProgramView(); + ListingActionContext actionContext = new ListingActionContext(listingProvider, + listingProvider, view, new ProgramLocation(view, start), + new ProgramSelection(start, start.addWrap(1)), null); + FixedPlatformTracePatchInstructionAction action = + Unique.assertOne(disassemblerPlugin.getPopupActions(tool, actionContext) + .stream() + .filter(a -> a instanceof FixedPlatformTracePatchInstructionAction) + .map(a -> (FixedPlatformTracePatchInstructionAction) a) + .filter(actionPred)); + + AssemblerPluginTestHelper helper = + new AssemblerPluginTestHelper(action, null, listingProvider, tb.trace.getProgramView()); + return helper.patchInstructionAt(start, "", assembly); + } + + @Test + public void testFixedAssembleActionsHostArm() throws Throwable { + // Assemble actions will think read-only otherwise + DebuggerStateEditingService editingService = + addPlugin(tool, DebuggerStateEditingServicePlugin.class); + + createLegacyTrace("ARM:LE:32:v8", 0x00400000, () -> tb.buf()); + Address start = tb.addr(0x00400000); + editingService.setCurrentMode(tb.trace, StateEditingMode.WRITE_TRACE); + + // Ensure the mapper is added to the trace + assertNotNull(platformService.getMapper(tb.trace, null, 0)); + + Instruction result = + performFixedAssembleAction(start, a -> !a.getName().contains("v8T"), "bx lr"); + + assertArrayEquals(tb.arr(0x1e, 0xff, 0x2f, 0xe1), result.getBytes()); + assertNull(result.getNext()); + } + + @Test + public void testFixedAssembleActionsGuestArm() throws Throwable { + // Assemble actions will think read-only otherwise + DebuggerStateEditingService editingService = + addPlugin(tool, DebuggerStateEditingServicePlugin.class); + + TraceObjectThread thread = createPolyglotTrace("armv8le", 0x00400000, () -> tb.buf()); + Address start = tb.addr(0x00400000); + editingService.setCurrentMode(tb.trace, StateEditingMode.WRITE_TRACE); + + // Ensure the mapper is added to the trace + assertNotNull(platformService.getMapper(tb.trace, thread.getObject(), 0)); + + Instruction result = + performFixedAssembleAction(start, a -> !a.getName().contains("v8T"), "bx lr"); + + assertArrayEquals(tb.arr(0x1e, 0xff, 0x2f, 0xe1), result.getBytes()); + assertNull(result.getNext()); + } + + @Test + public void testFixedAssembleActionsGuestThumb() throws Throwable { + // Assemble actions will think read-only otherwise + DebuggerStateEditingService editingService = + addPlugin(tool, DebuggerStateEditingServicePlugin.class); + + TraceObjectThread thread = createPolyglotTrace("armv8le", 0x00400000, () -> tb.buf()); + Address start = tb.addr(0x00400000); + editingService.setCurrentMode(tb.trace, StateEditingMode.WRITE_TRACE); + + // Ensure the mapper is added to the trace + assertNotNull(platformService.getMapper(tb.trace, thread.getObject(), 0)); + + Instruction result = + performFixedAssembleAction(start, a -> a.getName().contains("v8T"), "bx lr"); + + assertArrayEquals(tb.arr(0x70, 0x47), result.getBytes()); + assertNull(result.getNext()); + } +} diff --git a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/AbstractGhidraHeadedDebuggerGUITest.java b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/AbstractGhidraHeadedDebuggerGUITest.java index 705fce959b..338b16ad93 100644 --- a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/AbstractGhidraHeadedDebuggerGUITest.java +++ b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/AbstractGhidraHeadedDebuggerGUITest.java @@ -589,6 +589,8 @@ public abstract class AbstractGhidraHeadedDebuggerGUITest program.release(this); } + waitForSwing(); + env.dispose(); } 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 8976499190..b5302f2171 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 @@ -129,7 +129,7 @@ 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), null, tb.buf(0xf4, 0)); + tb.addInstruction(0, tb.addr(0x4008), tb.host, tb.buf(0xf4, 0)); Language x86 = getSLEIGH_X86_LANGUAGE(); DBTraceGuestPlatform guest = 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 index 4ffa1408df..64aed3469f 100644 --- 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 @@ -25,27 +25,41 @@ 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"; - } + ARM_V8_LE("Test armv8le", "ARM:LE:32:v8", "default"), + X86_64("Test x86-64", "x86:LE:64:default", "gcc"); - @Override - public int getConfidence() { - return 1; - } + private final String description; + private final LanguageCompilerSpecPair lcsp; - @Override - public CompilerSpec getCompilerSpec() { - return getCompilerSpec(new LanguageID("x86:LE:64:default"), null); - } + private Offers(String description, String langID, String cSpecID) { + this.description = description; + this.lcsp = new LanguageCompilerSpecPair(langID, cSpecID); + } - @Override - public DebuggerPlatformMapper take(PluginTool tool, Trace trace) { - return new DefaultDebuggerPlatformMapper(tool, trace, getCompilerSpec()); + @Override + public String getDescription() { + return description; + } + + @Override + public int getConfidence() { + return 1; + } + + @Override + public CompilerSpec getCompilerSpec() { + try { + return lcsp.getCompilerSpec(); } - }; + catch (LanguageNotFoundException | CompilerSpecNotFoundException e) { + throw new AssertionError(e); + } + } + + @Override + public DebuggerPlatformMapper take(PluginTool tool, Trace trace) { + return new DefaultDebuggerPlatformMapper(tool, trace, getCompilerSpec()); + } } @Override @@ -54,9 +68,12 @@ public class TestDebuggerPlatformOpinion extends AbstractDebuggerPlatformOpinion if (!"test".equals(debugger)) { return Set.of(); } - if (!"x86-64".equals(arch)) { - return Set.of(); + if ("armv8le".equals(arch)) { + return Set.of(Offers.ARM_V8_LE); } - return Set.of(Offers.X86_64); + if ("x86-64".equals(arch)) { + return Set.of(Offers.X86_64); + } + return Set.of(); } } 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 deleted file mode 100644 index 1f54d312df..0000000000 --- a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/workflow/DisassembleAtPcDebuggerBotTest.java +++ /dev/null @@ -1,168 +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.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/guest/DBTraceGuestPlatform.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/guest/DBTraceGuestPlatform.java index 338a05cbcf..d5e6325645 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/guest/DBTraceGuestPlatform.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/guest/DBTraceGuestPlatform.java @@ -42,7 +42,8 @@ import ghidra.util.exception.VersionException; import ghidra.util.task.TaskMonitor; @DBAnnotatedObjectInfo(version = 0) -public class DBTraceGuestPlatform extends DBAnnotatedObject implements TraceGuestPlatform { +public class DBTraceGuestPlatform extends DBAnnotatedObject + implements TraceGuestPlatform, InternalTracePlatform { public static final String TABLE_NAME = "Platforms"; @DBAnnotatedObjectInfo(version = 0) @@ -160,6 +161,16 @@ public class DBTraceGuestPlatform extends DBAnnotatedObject implements TraceGues return manager.trace; } + @Override + public int getIntKey() { + return (int) key; + } + + @Override + public boolean isGuest() { + return true; + } + protected void deleteMappedRange(DBTraceGuestPlatformMappedRange range, TaskMonitor monitor) throws CancelledException { try (LockHold hold = LockHold.lock(manager.lock.writeLock())) { @@ -175,6 +186,7 @@ public class DBTraceGuestPlatform extends DBAnnotatedObject implements TraceGues } } + @Override @Internal public DBTraceGuestLanguage getLanguageEntry() { return languageEntry; diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/guest/DBTraceGuestPlatformMappedRange.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/guest/DBTraceGuestPlatformMappedRange.java index a53565f040..ea5b72fbfe 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/guest/DBTraceGuestPlatformMappedRange.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/guest/DBTraceGuestPlatformMappedRange.java @@ -68,7 +68,7 @@ public class DBTraceGuestPlatformMappedRange extends DBAnnotatedObject private DBTracePlatformManager manager; private AddressRangeImpl hostRange; - private DBTraceGuestPlatform guestPlatform; + private DBTraceGuestPlatform platform; private AddressRangeImpl guestRange; public DBTraceGuestPlatformMappedRange(DBTracePlatformManager manager, DBCachedObjectStore s, @@ -83,28 +83,27 @@ public class DBTraceGuestPlatformMappedRange extends DBAnnotatedObject if (created) { return; } - try { - Address hostStart = - manager.getBaseLanguage().getAddressFactory().getAddress(hostSpace, hostOffset); - Address hostEnd = hostStart.addNoWrap(length - 1); - this.hostRange = new AddressRangeImpl(hostStart, hostEnd); + Address hostStart = + manager.trace.getBaseLanguage() + .getAddressFactory() + .getAddress(hostSpace, hostOffset); + Address hostEnd = hostStart.addWrap(length - 1); + this.hostRange = new AddressRangeImpl(hostStart, hostEnd); - this.guestPlatform = manager.getPlatformByKey(guestPlatformKey); - Address guestStart = - guestPlatform.getAddressFactory().getAddress(guestSpace, guestOffset); - Address guestEnd = guestStart.addNoWrap(length - 1); - this.guestRange = new AddressRangeImpl(guestStart, guestEnd); - } - catch (AddressOverflowException e) { - throw new RuntimeException("Database is corrupt or languages changed", e); + InternalTracePlatform platform = manager.getPlatformByKey(guestPlatformKey); + if (platform.isHost()) { + throw new IOException("Table is corrupt. Got host platform in guest mapping."); } + this.platform = (DBTraceGuestPlatform) platform; + Address guestStart = platform.getAddressFactory().getAddress(guestSpace, guestOffset); + Address guestEnd = guestStart.addWrap(length - 1); + this.guestRange = new AddressRangeImpl(guestStart, guestEnd); } - void set(Address hostStart, DBTraceGuestPlatform guestPlatform, Address guestStart, - long length) { + void set(Address hostStart, DBTraceGuestPlatform platform, Address guestStart, long length) { this.hostSpace = hostStart.getAddressSpace().getSpaceID(); this.hostOffset = hostStart.getOffset(); - this.guestPlatformKey = (int) guestPlatform.getKey(); + this.guestPlatformKey = (int) platform.getKey(); this.guestSpace = guestStart.getAddressSpace().getSpaceID(); this.guestOffset = guestStart.getOffset(); this.length = length; @@ -112,19 +111,19 @@ public class DBTraceGuestPlatformMappedRange extends DBAnnotatedObject GUEST_OFFSET_COLUMN, LENGTH_COLUMN); this.hostRange = new AddressRangeImpl(hostStart, hostStart.addWrap(length - 1)); - this.guestPlatform = guestPlatform; + this.platform = platform; this.guestRange = new AddressRangeImpl(guestStart, guestStart.addWrap(length - 1)); } @Override public Language getHostLanguage() { - return manager.getBaseLanguage(); + return manager.trace.getBaseLanguage(); } @Override public CompilerSpec getHostCompilerSpec() { - return manager.getBaseCompilerSpec(); + return manager.trace.getBaseCompilerSpec(); } @Override @@ -134,7 +133,7 @@ public class DBTraceGuestPlatformMappedRange extends DBAnnotatedObject @Override public DBTraceGuestPlatform getGuestPlatform() { - return guestPlatform; + return platform; } @Override diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/guest/DBTracePlatformManager.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/guest/DBTracePlatformManager.java index 005cb4a078..cf3156f53a 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/guest/DBTracePlatformManager.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/guest/DBTracePlatformManager.java @@ -21,14 +21,15 @@ import java.util.concurrent.locks.ReadWriteLock; import db.DBHandle; import ghidra.lifecycle.Internal; +import ghidra.program.model.address.Address; +import ghidra.program.model.address.AddressSetView; import ghidra.program.model.lang.*; -import ghidra.program.model.listing.Instruction; +import ghidra.program.model.mem.MemBuffer; 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.trace.model.Trace; +import ghidra.trace.model.guest.*; import ghidra.util.LockHold; import ghidra.util.database.*; import ghidra.util.exception.CancelledException; @@ -57,6 +58,68 @@ public class DBTracePlatformManager implements DBTraceManager, TracePlatformMana protected final DBCachedObjectStore rangeMappingStore; + protected final InternalTracePlatform hostPlatform = new InternalTracePlatform() { + @Override + public Trace getTrace() { + return trace; + } + + @Override + public DBTraceGuestLanguage getLanguageEntry() { + return null; + } + + @Override + public int getIntKey() { + return -1; + } + + @Override + public boolean isGuest() { + return false; + } + + @Override + public Language getLanguage() { + return trace.getBaseLanguage(); + } + + @Override + public CompilerSpec getCompilerSpec() { + return trace.getBaseCompilerSpec(); + } + + @Override + public AddressSetView getHostAddressSet() { + return trace.getBaseAddressFactory().getAddressSet(); + } + + @Override + public AddressSetView getGuestAddressSet() { + return trace.getBaseAddressFactory().getAddressSet(); + } + + @Override + public Address mapHostToGuest(Address hostAddress) { + return hostAddress; + } + + @Override + public Address mapGuestToHost(Address guestAddress) { + return guestAddress; + } + + @Override + public MemBuffer getMappedMemBuffer(long snap, Address guestAddress) { + return trace.getMemoryManager().getBufferAt(snap, guestAddress); + } + + @Override + public InstructionSet mapGuestInstructionAddressesToHost(InstructionSet set) { + return set; + } + }; + public DBTracePlatformManager(DBHandle dbh, DBOpenMode openMode, ReadWriteLock lock, TaskMonitor monitor, CompilerSpec baseCompilerSpec, DBTrace trace) throws VersionException, IOException { @@ -129,9 +192,9 @@ public class DBTracePlatformManager implements DBTraceManager, TracePlatformMana } @Internal - public DBTraceGuestPlatform getPlatformByKey(int key) { + public InternalTracePlatform getPlatformByKey(int key) { if (key == -1) { - return null; + return hostPlatform; } return platformStore.getObjectAt(key); } @@ -201,13 +264,8 @@ public class DBTracePlatformManager implements DBTraceManager, TracePlatformMana } @Override - public Language getBaseLanguage() { - return trace.getBaseLanguage(); - } - - @Override - public CompilerSpec getBaseCompilerSpec() { - return trace.getBaseCompilerSpec(); + public InternalTracePlatform getHostPlatform() { + return hostPlatform; } protected DBTraceGuestPlatform doAddGuestPlatform(CompilerSpec compilerSpec) { @@ -219,9 +277,9 @@ public class DBTracePlatformManager implements DBTraceManager, TracePlatformMana @Override public DBTraceGuestPlatform addGuestPlatform(CompilerSpec compilerSpec) { - if (compilerSpec.getCompilerSpecID() - .equals(trace.getBaseCompilerSpec().getCompilerSpecID())) { - throw new IllegalArgumentException("Base language cannot be a guest language"); + if (trace.getBaseCompilerSpec() == compilerSpec) { + throw new IllegalArgumentException( + "Base compiler spec cannot be a guest compiler spec"); } try (LockHold hold = LockHold.lock(lock.writeLock())) { return doAddGuestPlatform(compilerSpec); @@ -229,8 +287,11 @@ public class DBTracePlatformManager implements DBTraceManager, TracePlatformMana } @Override - public DBTraceGuestPlatform getGuestPlatform(CompilerSpec compilerSpec) { + public InternalTracePlatform getPlatform(CompilerSpec compilerSpec) { try (LockHold hold = LockHold.lock(lock.readLock())) { + if (trace.getBaseCompilerSpec() == compilerSpec) { + return hostPlatform; + } return platformsByCompiler.get(compilerSpec); } } @@ -255,33 +316,10 @@ public class DBTracePlatformManager implements DBTraceManager, TracePlatformMana 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; + public InternalTracePlatform assertMine(TracePlatform platform) { + if (platform == hostPlatform) { + return hostPlatform; } if (!(platform instanceof DBTraceGuestPlatform)) { throw new IllegalArgumentException("Given platform does not belong to this trace"); diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/guest/InternalTracePlatform.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/guest/InternalTracePlatform.java new file mode 100644 index 0000000000..02c2b3b960 --- /dev/null +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/guest/InternalTracePlatform.java @@ -0,0 +1,30 @@ +/* ### + * 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 ghidra.trace.database.guest.DBTraceGuestPlatform.DBTraceGuestLanguage; +import ghidra.trace.model.guest.TracePlatform; + +public interface InternalTracePlatform extends TracePlatform { + /** + * Get the entry's key in the table as an integer + * + * @return the key + */ + int getIntKey(); + + DBTraceGuestLanguage getLanguageEntry(); +} diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/AbstractBaseDBTraceCodeUnitsMemoryView.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/AbstractBaseDBTraceCodeUnitsMemoryView.java index 27f8fef1ac..8897ffcf64 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/AbstractBaseDBTraceCodeUnitsMemoryView.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/AbstractBaseDBTraceCodeUnitsMemoryView.java @@ -26,6 +26,7 @@ import generic.NestedIterator; import ghidra.program.model.address.*; import ghidra.trace.database.DBTraceUtils; import ghidra.trace.database.space.DBTraceDelegatingManager; +import ghidra.trace.model.Trace; import ghidra.trace.model.TraceAddressSnapRange; import ghidra.util.LockHold; @@ -40,6 +41,10 @@ public abstract class AbstractBaseDBTraceCodeUnitsMemoryView { @@ -29,6 +30,10 @@ public abstract class AbstractBaseDBTraceCodeUnitsView @DBAnnotatedField(column = DATATYPE_COLUMN_NAME) private long dataTypeID; - protected DBTraceGuestPlatform guest; + protected InternalTracePlatform platform; protected DataType dataType; protected DataType baseDataType; protected Settings defaultSettings; @@ -75,8 +75,8 @@ public class DBTraceData extends AbstractDBTraceCodeUnit if (created) { return; } - guest = space.manager.platformManager.getPlatformByKey(platformKey); - if (guest == null && platformKey != -1) { + platform = space.manager.platformManager.getPlatformByKey(platformKey); + if (platform == null) { throw new IOException("Data table is corrupt. Missing platform: " + platformKey); } dataType = space.dataTypeManager.getDataType(dataTypeID); @@ -102,12 +102,12 @@ public class DBTraceData extends AbstractDBTraceCodeUnit return this; } - protected void set(DBTraceGuestPlatform platform, DataType dataType) { - this.platformKey = (int) (platform == null ? -1 : platform.getKey()); + protected void set(InternalTracePlatform platform, DataType dataType) { + this.platformKey = platform.getIntKey(); this.dataTypeID = space.dataTypeManager.getResolvedID(dataType); update(PLATFORM_COLUMN, DATATYPE_COLUMN); - this.guest = platform; + this.platform = platform; // Use the stored dataType, not the given one, in case it's different this.dataType = space.dataTypeManager.getDataType(dataTypeID); assert this.dataType != null; @@ -133,8 +133,8 @@ public class DBTraceData extends AbstractDBTraceCodeUnit } @Override - public TraceGuestPlatform getGuestPlatform() { - return guest; + public TracePlatform getPlatform() { + return platform; } @Override @@ -157,7 +157,7 @@ public class DBTraceData extends AbstractDBTraceCodeUnit @Override public Language getLanguage() { - return guest == null ? space.baseLanguage : guest.getLanguage(); + return platform.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 766e3884cb..610a5c4b18 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 @@ -146,7 +146,7 @@ public class DBTraceDefinedDataView extends AbstractBaseDBTraceDefinedUnitsView< DBTraceData created = space.dataMapSpace.put(tasr, null); // TODO: data units with a guest platform - created.set(null, dataType); + created.set(space.trace.getPlatformManager().getHostPlatform(), 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 fa43e43bde..3c531416f9 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,13 +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.guest.DBTraceGuestPlatform; import ghidra.trace.database.guest.DBTraceGuestPlatform.DBTraceGuestLanguage; +import ghidra.trace.database.guest.InternalTracePlatform; 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.guest.TracePlatform; import ghidra.trace.model.listing.TraceInstruction; import ghidra.trace.model.symbol.TraceReference; import ghidra.trace.util.*; @@ -79,7 +79,7 @@ public class DBTraceInstruction extends AbstractDBTraceCodeUnit> FLOWOVERRIDE_SHIFT]; - doSetGuestMapping(guest); + doSetPlatformMapping(platform); } @Override @@ -206,8 +206,8 @@ public class DBTraceInstruction extends AbstractDBTraceCodeUnit hostFlows = new ArrayList<>(); for (Address g : guestFlows) { - Address h = guest.mapGuestToHost(g); + Address h = platform.mapGuestToHost(g); if (h != null) { hostFlows.add(h); } diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceInstructionsMemoryView.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceInstructionsMemoryView.java index 5cdf32e927..5f3fe2fd4d 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceInstructionsMemoryView.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceInstructionsMemoryView.java @@ -24,7 +24,7 @@ import com.google.common.collect.Range; import ghidra.program.model.address.*; import ghidra.program.model.lang.*; import ghidra.program.model.util.CodeUnitInsertionException; -import ghidra.trace.model.guest.TraceGuestPlatform; +import ghidra.trace.model.guest.TracePlatform; import ghidra.trace.model.listing.TraceInstructionsView; import ghidra.util.LockHold; import ghidra.util.exception.CancelledException; @@ -50,17 +50,16 @@ public class DBTraceInstructionsMemoryView @Override public DBTraceInstruction create(Range lifespan, Address address, - TraceGuestPlatform platform, InstructionPrototype prototype, + TracePlatform platform, InstructionPrototype prototype, ProcessorContextView context) throws CodeUnitInsertionException { return delegateWrite(address.getAddressSpace(), m -> m.create(lifespan, address, platform, prototype, context)); } @Override - public AddressSetView addInstructionSet(Range lifespan, TraceGuestPlatform platform, + public AddressSetView addInstructionSet(Range lifespan, TracePlatform platform, InstructionSet instructionSet, boolean overwrite) { - InstructionSet mappedSet = manager.platformManager - .mapGuestInstructionAddressesToHost(platform, instructionSet); + InstructionSet mappedSet = platform.mapGuestInstructionAddressesToHost(instructionSet); Map breakDown = new HashMap<>(); // TODO: I'm not sure the consequences of breaking an instruction set down. 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 49e23d0797..a1bc16897f 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,12 +29,12 @@ 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.guest.InternalTracePlatform; 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.guest.TracePlatform; import ghidra.trace.model.listing.TraceInstruction; import ghidra.trace.model.listing.TraceInstructionsView; import ghidra.trace.util.OverlappingObjectIterator; @@ -53,7 +53,7 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView protected class InstructionBlockAdder { private final Set

skipDelaySlots; private final Range lifespan; - private final DBTraceGuestPlatform platform; + private final InternalTracePlatform platform; private final InstructionBlock block; private final Address errorAddress; private final InstructionError conflict; @@ -62,7 +62,7 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView protected int count = 0; private InstructionBlockAdder(Set
skipDelaySlots, Range lifespan, - DBTraceGuestPlatform platform, InstructionBlock block, Address errorAddress, + InternalTracePlatform platform, InstructionBlock block, Address errorAddress, InstructionError conflict, CodeUnit conflictCodeUnit) { this.skipDelaySlots = skipDelaySlots; this.lifespan = lifespan; @@ -185,19 +185,11 @@ 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, - DBTraceGuestPlatform platform, InstructionPrototype prototype, + InternalTracePlatform platform, InstructionPrototype prototype, ProcessorContextView context) throws CodeUnitInsertionException, AddressOverflowException { - if (!languagesAgree(platform, prototype)) { + if (platform.getLanguage() != prototype.getLanguage()) { throw new IllegalArgumentException("Platform and prototype disagree in language"); } @@ -245,11 +237,10 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView } @Override - public DBTraceInstruction create(Range lifespan, Address address, - TraceGuestPlatform platform, InstructionPrototype prototype, - ProcessorContextView context) + public DBTraceInstruction create(Range lifespan, Address address, TracePlatform platform, + InstructionPrototype prototype, ProcessorContextView context) throws CodeUnitInsertionException { - DBTraceGuestPlatform dbPlatform = space.manager.platformManager.assertMine(platform); + InternalTracePlatform dbPlatform = space.manager.platformManager.assertMine(platform); try (LockHold hold = LockHold.lock(space.lock.writeLock())) { DBTraceInstruction created = doCreate(lifespan, address, dbPlatform, prototype, context); @@ -277,7 +268,7 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView } protected InstructionBlockAdder startAddingBlock(Range lifespan, - Set
skipDelaySlots, DBTraceGuestPlatform platform, InstructionBlock block) { + Set
skipDelaySlots, InternalTracePlatform platform, InstructionBlock block) { InstructionError conflict = block.getInstructionConflict(); if (conflict == null) { return new InstructionBlockAdder(skipDelaySlots, lifespan, platform, block, null, null, @@ -384,9 +375,9 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView } @Override - public AddressSetView addInstructionSet(Range lifespan, TraceGuestPlatform platform, + public AddressSetView addInstructionSet(Range lifespan, TracePlatform platform, InstructionSet instructionSet, boolean overwrite) { - DBTraceGuestPlatform dbPlatform = space.manager.platformManager.assertMine(platform); + InternalTracePlatform dbPlatform = space.manager.platformManager.assertMine(platform); // NOTE: Partly derived from CodeManager#addInstructions() // Attempted to factor more fluently AddressSet result = new AddressSet(); 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 ea21d51139..d55a17b78a 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,7 +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.guest.TracePlatform; import ghidra.trace.model.listing.TraceData; import ghidra.trace.model.thread.TraceThread; import ghidra.trace.util.TraceAddressSpace; @@ -82,8 +82,8 @@ public class UndefinedDBTraceData implements DBTraceDataAdapter, DBTraceSpaceKey } @Override - public TraceGuestPlatform getGuestPlatform() { - return null; + public TracePlatform getPlatform() { + return trace.getPlatformManager().getHostPlatform(); } @Override 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 90304a28d0..9da6b7ceac 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 @@ -37,6 +37,7 @@ import ghidra.program.model.symbol.SourceType; import ghidra.program.model.util.CodeUnitInsertionException; import ghidra.program.model.util.PropertyMap; import ghidra.trace.database.DBTrace; +import ghidra.trace.database.guest.InternalTracePlatform; import ghidra.trace.database.listing.UndefinedDBTraceData; import ghidra.trace.database.memory.DBTraceMemorySpace; import ghidra.trace.database.symbol.DBTraceFunctionSymbol; @@ -77,6 +78,7 @@ public abstract class AbstractDBTraceProgramViewListing implements TraceProgramV protected final DBTraceProgramView program; protected final TraceCodeOperations codeOperations; + protected final InternalTracePlatform platform; protected final DBTraceProgramViewRootModule rootModule; protected final Map fragmentsByRegion = @@ -94,6 +96,8 @@ public abstract class AbstractDBTraceProgramViewListing implements TraceProgramV TraceCodeOperations codeOperations) { this.program = program; this.codeOperations = codeOperations; + // TODO: Guest platform views? + this.platform = program.trace.getPlatformManager().getHostPlatform(); this.rootModule = new DBTraceProgramViewRootModule(this); } @@ -725,17 +729,15 @@ 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? - // TODO: Per-platform views? return codeOperations.instructions() - .create(Range.atLeast(program.snap), addr, null, prototype, context); + .create(Range.atLeast(program.snap), addr, platform, prototype, context); } @Override public AddressSetView addInstructions(InstructionSet instructionSet, boolean overwrite) throws CodeUnitInsertionException { - // TODO: Per-platform views? return codeOperations.instructions() - .addInstructionSet(Range.atLeast(program.snap), null, instructionSet, + .addInstructionSet(Range.atLeast(program.snap), platform, instructionSet, overwrite); } diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/thread/DBTraceThreadManager.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/thread/DBTraceThreadManager.java index 05da613b33..7283c9eac7 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/thread/DBTraceThreadManager.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/thread/DBTraceThreadManager.java @@ -24,6 +24,7 @@ import com.google.common.collect.Range; import db.DBHandle; import ghidra.trace.database.*; +import ghidra.trace.database.target.DBTraceObject; import ghidra.trace.database.target.DBTraceObjectManager; import ghidra.trace.model.Trace.TraceThreadChangeType; import ghidra.trace.model.thread.*; @@ -158,9 +159,8 @@ public class DBTraceThreadManager implements TraceThreadManager, DBTraceManager @Override public TraceThread getThread(long key) { if (objectManager.hasSchema()) { - return objectManager - .getObjectById(key) - .queryInterface(TraceObjectThread.class); + DBTraceObject object = objectManager.getObjectById(key); + return object == null ? null : object.queryInterface(TraceObjectThread.class); } return threadStore.getObjectAt(key); } 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 index 2bbd5f783d..d42311fe48 100644 --- 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 @@ -15,46 +15,15 @@ */ 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.program.model.address.Address; +import ghidra.program.model.address.AddressOverflowException; import ghidra.util.exception.CancelledException; import ghidra.util.task.TaskMonitor; -public interface TraceGuestPlatform { - /** - * Get the trace - * - * @return the trace - */ - Trace getTrace(); +public interface TraceGuestPlatform extends TracePlatform { /** - * 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 + * Add an address mapping from host to guest * * @param hostStart the starting host address (mapped to guestStart) * @param guestStart the starting guest address (mapped to hostStart) @@ -65,66 +34,6 @@ public interface TraceGuestPlatform { 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 */ diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/guest/TracePlatform.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/guest/TracePlatform.java new file mode 100644 index 0000000000..039f7cc8e3 --- /dev/null +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/guest/TracePlatform.java @@ -0,0 +1,129 @@ +/* ### + * 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; + +public interface TracePlatform { + /** + * Get the trace + * + * @return the trace + */ + Trace getTrace(); + + /** + * Check if this is a guest platform + * + * @return true for guest, false for host + */ + boolean isGuest(); + + /** + * Check if this is the host platform + * + * @return true for host, false for guest + */ + default boolean isHost() { + return !isGuest(); + } + + /** + * 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(); + + /** + * 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); +} 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 index 3807f622b5..2a471245fd 100644 --- 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 @@ -18,7 +18,6 @@ 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. @@ -28,18 +27,11 @@ import ghidra.program.model.lang.Language; */ public interface TracePlatformManager { /** - * Get the base language of the trace + * Get a platform representing the trace's base language and compiler spec * - * @return the language + * @return the host platform */ - Language getBaseLanguage(); - - /** - * Get the base compiler spec of the trace - * - * @return the compiler spec - */ - CompilerSpec getBaseCompilerSpec(); + TracePlatform getHostPlatform(); /** * Add a guest platform @@ -50,12 +42,12 @@ public interface TracePlatformManager { TraceGuestPlatform addGuestPlatform(CompilerSpec compilerSpec); /** - * Get the guest platform for the given compiler spec + * Get the platform for the given compiler spec * - * @param compilerSpec the compiler spec. For the base compiler spec, this will return null. + * @param compilerSpec the compiler spec * @return the platform, if found, or null */ - TraceGuestPlatform getGuestPlatform(CompilerSpec compilerSpec); + TracePlatform getPlatform(CompilerSpec compilerSpec); /** * Get or add a platform for the given compiler spec diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/listing/TraceBaseCodeUnitsView.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/listing/TraceBaseCodeUnitsView.java index b5f9d75b22..c64122eed7 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/listing/TraceBaseCodeUnitsView.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/listing/TraceBaseCodeUnitsView.java @@ -18,6 +18,7 @@ package ghidra.trace.model.listing; import com.google.common.collect.Range; import ghidra.program.model.address.*; +import ghidra.trace.model.Trace; import ghidra.trace.model.TraceAddressSnapRange; import ghidra.util.IntersectionAddressSetView; import ghidra.util.UnionAddressSetView; @@ -30,6 +31,13 @@ import ghidra.util.UnionAddressSetView; */ public interface TraceBaseCodeUnitsView { + /** + * Get the trace for this view + * + * @return the trace + */ + Trace getTrace(); + /** * Get the total number of defined units in this view * 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 d01b9d3290..f763820429 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,7 +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.guest.TracePlatform; import ghidra.trace.model.program.TraceProgramView; import ghidra.trace.model.symbol.TraceReference; import ghidra.trace.model.thread.TraceThread; @@ -44,11 +44,11 @@ public interface TraceCodeUnit extends CodeUnit { Trace getTrace(); /** - * If the unit is for a guest platform, get it + * Get the platform for this unit * - * @return the guest platform, or null if it's for the host platform + * @return the platform */ - TraceGuestPlatform getGuestPlatform(); + TracePlatform getPlatform(); @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 adb00b7bbd..0ae023ea5b 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,7 +21,7 @@ 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; +import ghidra.trace.model.guest.TracePlatform; public interface TraceInstructionsView extends TraceBaseDefinedUnitsView { /** @@ -29,16 +29,28 @@ public interface TraceInstructionsView extends TraceBaseDefinedUnitsView lifespan, Address address, TraceGuestPlatform platform, + TraceInstruction create(Range lifespan, Address address, TracePlatform platform, InstructionPrototype prototype, ProcessorContextView context) throws CodeUnitInsertionException; + /** + * Create an instruction for the host platform + * + * @see #create(Range, Address, TracePlatform, InstructionPrototype, ProcessorContextView) + */ + default TraceInstruction create(Range lifespan, Address address, + InstructionPrototype prototype, ProcessorContextView context) + throws CodeUnitInsertionException { + return create(lifespan, address, getTrace().getPlatformManager().getHostPlatform(), + prototype, context); + } + /** * Create several instructions * @@ -52,6 +64,17 @@ public interface TraceInstructionsView extends TraceBaseDefinedUnitsView lifespan, TraceGuestPlatform platform, + AddressSetView addInstructionSet(Range lifespan, TracePlatform platform, InstructionSet instructionSet, boolean overwrite); + + /** + * Create several instructions for the host platform + * + * @see #addInstructionSet(Range, TracePlatform, InstructionSet, boolean) + */ + default AddressSetView addInstructionSet(Range lifespan, InstructionSet instructionSet, + boolean overwrite) { + return addInstructionSet(lifespan, getTrace().getPlatformManager().getHostPlatform(), + instructionSet, overwrite); + } } 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 8c5c042843..c63c7b26c5 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 @@ -52,6 +52,7 @@ import ghidra.trace.database.symbol.DBTraceReference; import ghidra.trace.database.thread.DBTraceThreadManager; import ghidra.trace.model.*; import ghidra.trace.model.guest.TraceGuestPlatform; +import ghidra.trace.model.guest.TracePlatform; import ghidra.trace.model.thread.TraceThread; import ghidra.util.Msg; import ghidra.util.database.DBOpenMode; @@ -62,6 +63,7 @@ import ghidra.util.task.ConsoleTaskMonitor; public class ToyDBTraceBuilder implements AutoCloseable { public final Language language; public final DBTrace trace; + public final TracePlatform host; public final LanguageService languageService = DefaultLanguageService.getLanguageService(); public ToyDBTraceBuilder(File file) @@ -69,17 +71,20 @@ public class ToyDBTraceBuilder implements AutoCloseable { DBHandle handle = new DBHandle(file); this.trace = new DBTrace(handle, DBOpenMode.UPDATE, new ConsoleTaskMonitor(), this); this.language = trace.getBaseLanguage(); + this.host = trace.getPlatformManager().getHostPlatform(); } // TODO: A constructor for specifying compiler, too public ToyDBTraceBuilder(String name, String langID) throws IOException { this.language = languageService.getLanguage(new LanguageID(langID)); this.trace = new DBTrace(name, language.getDefaultCompilerSpec(), this); + this.host = trace.getPlatformManager().getHostPlatform(); } public ToyDBTraceBuilder(Trace trace) { this.language = trace.getBaseLanguage(); this.trace = (DBTrace) trace; + this.host = trace.getPlatformManager().getHostPlatform(); trace.addConsumer(this); } @@ -102,7 +107,7 @@ public class ToyDBTraceBuilder implements AutoCloseable { return addr(language, offset); } - public Address addr(TraceGuestPlatform lang, long offset) { + public Address addr(TracePlatform lang, long offset) { return lang.getLanguage().getDefaultSpace().getAddress(offset); } @@ -246,36 +251,28 @@ public class ToyDBTraceBuilder implements AutoCloseable { } public DBTraceInstruction addInstruction(long snap, Address start, - TraceGuestPlatform guest) throws CodeUnitInsertionException { - DBTraceMemoryManager memory = trace.getMemoryManager(); + TracePlatform platform) throws CodeUnitInsertionException { DBTraceCodeManager code = trace.getCodeManager(); - Language language = guest == null ? this.language : guest.getLanguage(); + Language language = platform.getLanguage(); Disassembler dis = Disassembler.getDisassembler(language, language.getAddressFactory(), new ConsoleTaskMonitor(), msg -> Msg.info(this, "Listener: " + msg)); RegisterValue defaultContextValue = trace.getRegisterContextManager() .getDefaultContext(language) .getDefaultDisassemblyContext(); - MemBuffer memBuf; - if (guest == null) { - memBuf = memory.getBufferAt(snap, start); - } - else { - memBuf = guest.getMappedMemBuffer(snap, guest.mapHostToGuest(start)); - } + MemBuffer memBuf = platform.getMappedMemBuffer(snap, platform.mapHostToGuest(start)); InstructionBlock block = dis.pseudoDisassembleBlock(memBuf, defaultContextValue, 1); Instruction pseudoIns = block.iterator().next(); return code.instructions() - .create(Range.atLeast(snap), start, guest, pseudoIns.getPrototype(), pseudoIns); + .create(Range.atLeast(snap), start, platform, pseudoIns.getPrototype(), pseudoIns); } - public DBTraceInstruction addInstruction(long snap, Address start, - TraceGuestPlatform guest, ByteBuffer buf) - throws CodeUnitInsertionException { + public DBTraceInstruction addInstruction(long snap, Address start, TracePlatform platform, + ByteBuffer buf) throws CodeUnitInsertionException { int length = buf.remaining(); DBTraceMemoryManager memory = trace.getMemoryManager(); memory.putBytes(snap, start, buf); - DBTraceInstruction instruction = addInstruction(snap, start, guest); + DBTraceInstruction instruction = addInstruction(snap, start, platform); assertEquals(length, instruction.getLength()); return instruction; } diff --git a/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/database/guest/DBTracePlatformManagerTest.java b/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/database/guest/DBTracePlatformManagerTest.java index 77e44db80d..76604cba67 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/database/guest/DBTracePlatformManagerTest.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/database/guest/DBTracePlatformManagerTest.java @@ -25,8 +25,8 @@ import org.junit.*; import ghidra.test.AbstractGhidraHeadlessIntegrationTest; import ghidra.trace.database.ToyDBTraceBuilder; -import ghidra.trace.database.guest.*; import ghidra.trace.model.guest.TraceGuestPlatform; +import ghidra.trace.model.guest.TracePlatform; import ghidra.util.database.UndoableTransaction; import ghidra.util.task.ConsoleTaskMonitor; @@ -46,14 +46,10 @@ public class DBTracePlatformManagerTest extends AbstractGhidraHeadlessIntegratio } @Test - public void testGetBaseLanguage() { - assertEquals("Toy:BE:64:default", - manager.getBaseLanguage().getLanguageID().getIdAsString()); - } - - @Test - public void testGetBaseCompilerSpec() { - assertEquals("default", manager.getBaseCompilerSpec().getCompilerSpecID().getIdAsString()); + public void testGetHostPlatform() throws Throwable { + TracePlatform host = b.trace.getPlatformManager().getHostPlatform(); + assertEquals("Toy:BE:64:default", host.getLanguage().getLanguageID().getIdAsString()); + assertEquals("default", host.getCompilerSpec().getCompilerSpecID().getIdAsString()); } @Test @@ -324,7 +320,8 @@ public class DBTracePlatformManagerTest extends AbstractGhidraHeadlessIntegratio b.trace.undo(); - guest = manager.getGuestPlatform(b.getCompiler("x86:LE:32:default", "gcc")); + guest = + (DBTraceGuestPlatform) manager.getPlatform(b.getCompiler("x86:LE:32:default", "gcc")); assertNotNull(guest.mapHostToGuest(b.addr(0x01000800))); assertNotNull(guest.mapGuestToHost(b.addr(guest, 0x02000800))); } @@ -359,7 +356,8 @@ public class DBTracePlatformManagerTest extends AbstractGhidraHeadlessIntegratio b.trace.undo(); - guest = manager.getGuestPlatform(b.getCompiler("x86:LE:32:default", "gcc")); + guest = + (DBTraceGuestPlatform) manager.getPlatform(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 2d4c1b19d7..d83a5a5dda 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 @@ -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), null, b.buf(0xf4, 0)); + b.addInstruction(0, b.addr(0x4004), b.host, 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), null, b.buf(0xf4, 0)); + b.addInstruction(0, b.addr(0x4000), b.host, 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), null, b.buf(0xf4, 0)); + b.addInstruction(-10, b.addr(0x4000), b.host, b.buf(0xf4, 0)); assertEquals(Range.closed(-10L, -6L), i4000.getLifespan()); TraceInstruction i4004 = - b.addInstruction(-1, b.addr(0x4004), null, b.buf(0xf4, 0)); + b.addInstruction(-1, b.addr(0x4004), b.host, b.buf(0xf4, 0)); assertEquals(Range.closed(-1L, -1L), i4004.getLifespan()); TraceInstruction i4008 = - b.addInstruction(-10, b.addr(0x4008), null, b.buf(0xf4, 0)); + b.addInstruction(-10, b.addr(0x4008), b.host, 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), null, b.buf(0xf4, 0)); + b.addInstruction(0, b.addr(0x4000), b.host, 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), null, b.buf(0xf4, 0)); + b.addInstruction(0, b.addr(0x4000), b.host, 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), null); + b.addInstruction(1, b.addr(0x4001), b.host); fail(); } catch (CodeUnitInsertionException e) { // pass } - b.addInstruction(0, b.addr(0x4004), null, b.buf(0xf4, 0)); + b.addInstruction(0, b.addr(0x4004), b.host, 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), null); + b.addInstruction(1, b.addr(0x4005), b.host); } catch (CodeUnitInsertionException e) { // pass @@ -350,7 +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), null, b.buf(0xf4, 0)); + TraceInstruction i4005 = b.addInstruction(0, b.addr(0x4005), b.host, b.buf(0xf4, 0)); i4005.setEndSnap(5); assertUndefinedFunc(v -> v.getAt(0, b.addr(0x4004))); assertInstructionFunc(i4005, v -> v.getAt(0, b.addr(0x4005))); @@ -383,7 +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), null, b.buf(0xf4, 0)); + TraceInstruction i4005 = b.addInstruction(0, b.addr(0x4005), b.host, b.buf(0xf4, 0)); i4005.setEndSnap(5); assertUndefinedFunc(v -> v.getContaining(0, b.addr(0x4004))); @@ -449,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), null, b.buf(0xf4, 0)); + i4008 = b.addInstruction(0, b.addr(0x4008), b.host, b.buf(0xf4, 0)); i4008.setEndSnap(9); } @@ -591,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), null, b.buf(0xf4, 0)); + i4008 = b.addInstruction(0, b.addr(0x4008), b.host, b.buf(0xf4, 0)); i4008.setEndSnap(9); } @@ -730,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), null, b.buf(0xf4, 0)); + i4008 = b.addInstruction(0, b.addr(0x4008), b.host, b.buf(0xf4, 0)); i4008.setEndSnap(9); } @@ -870,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), null, b.buf(0xf4, 0)); + i4008 = b.addInstruction(0, b.addr(0x4008), b.host, b.buf(0xf4, 0)); i4008.setEndSnap(9); } @@ -1084,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), null, b.buf(0xf4, 0)); + i4008 = b.addInstruction(0, b.addr(0x4008), b.host, b.buf(0xf4, 0)); i4008.setEndSnap(9); } TraceData u3fff = manager.undefinedData().getAt(0, b.addr(0x3fff)); @@ -1137,7 +1137,7 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes TraceInstruction iCodeMax; try (UndoableTransaction tid = b.startTransaction()) { - iCodeMax = b.addInstruction(0, b.addr(-0x0002), null, b.buf(0xf4, 0)); + iCodeMax = b.addInstruction(0, b.addr(-0x0002), b.host, b.buf(0xf4, 0)); } assertEquals(iCodeMax, manager.codeUnits().getBefore(0, b.data(0x0000))); @@ -1170,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), null, b.buf(0xf4, 0)); + iCodeMax = b.addInstruction(0, b.addr(-0x0002), b.host, b.buf(0xf4, 0)); } TraceData uCodePre = manager.undefinedData().getAt(0, b.addr(-0x0003)); assertUndefinedWithAddr(b.addr(-0x0003), uCodePre); @@ -1209,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), null, b.buf(0xf4, 0)); + i4008 = b.addInstruction(0, b.addr(0x4008), b.host, b.buf(0xf4, 0)); i4008.setEndSnap(9); } @@ -1344,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), null, b.buf(0xf4, 0)); + i4008 = b.addInstruction(0, b.addr(0x4008), b.host, b.buf(0xf4, 0)); i4008.setEndSnap(9); } @@ -1414,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), null, b.buf(0xf4, 0)); + i4008 = b.addInstruction(0, b.addr(0x4008), b.host, b.buf(0xf4, 0)); i4008.setEndSnap(9); } @@ -1489,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), null, b.buf(0xf4, 0)); + i4008 = b.addInstruction(0, b.addr(0x4008), b.host, b.buf(0xf4, 0)); i4008.setEndSnap(9); } @@ -1573,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), null, b.buf(0xf4, 0)); + i4008 = b.addInstruction(0, b.addr(0x4008), b.host, b.buf(0xf4, 0)); i4008.setEndSnap(9); } @@ -1672,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), null, b.buf(0xf4, 0)); + i4008 = b.addInstruction(0, b.addr(0x4008), b.host, b.buf(0xf4, 0)); i4008.setEndSnap(9); // Clear one of the data before a context space is created @@ -1720,7 +1720,7 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes guest = langMan.addGuestPlatform(x86.getDefaultCompilerSpec()); mappedRange = guest.addMappedRange(b.addr(0x0000), b.addr(guest, 0x0000), 1L << 32); g4000 = b.addInstruction(0, b.addr(0x4000), guest, b.buf(0x90)); - i4001 = b.addInstruction(0, b.addr(0x4001), null, b.buf(0xf4, 0)); + i4001 = b.addInstruction(0, b.addr(0x4001), b.host, b.buf(0xf4, 0)); d4003 = b.addData(0, b.addr(0x4003), LongDataType.dataType, b.buf(1, 2, 3, 4)); } @@ -1738,7 +1738,7 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes // TODO: Related to GP-479? g4000 = manager.instructions().getAt(0, b.addr(0x4000)); assertNotNull(g4000); - assertEquals(guest, g4000.getGuestPlatform()); + assertEquals(guest, g4000.getPlatform()); try (UndoableTransaction tid = b.startTransaction()) { guest.delete(new ConsoleTaskMonitor()); } @@ -1753,7 +1753,7 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes @Test public void testSaveAndLoad() throws Exception { try (UndoableTransaction tid = b.startTransaction()) { - b.addInstruction(0, b.addr(0x4004), null, b.buf(0xf4, 0)); + b.addInstruction(0, b.addr(0x4004), b.host, b.buf(0xf4, 0)); TraceThread thread = b.getOrAddThread("Thread 1", 0); DBTraceCodeRegisterSpace regCode = manager.getCodeRegisterSpace(thread, true); @@ -1801,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), null, b.buf(0xf4, 0)); + b.addInstruction(0, b.addr(0x4004), b.host, b.buf(0xf4, 0)); TraceThread thread = b.getOrAddThread("Thread 1", 0); DBTraceCodeRegisterSpace regCode = manager.getCodeRegisterSpace(thread, true); @@ -1854,7 +1854,7 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes b.trace.getBaseAddressFactory().getDefaultAddressSpace()); DBTraceCodeSpace space = manager.getCodeSpace(os, true); - b.addInstruction(0, os.getAddress(0x4004), null, b.buf(0xf4, 0)); + b.addInstruction(0, os.getAddress(0x4004), b.host, b.buf(0xf4, 0)); List all = new ArrayList<>(); space.definedUnits().get(0, true).forEach(all::add); 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 98873903f4..ce96581c1e 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 @@ -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), null, b.buf(0xf4, 0)); + ins = b.addInstruction(0, b.addr(0x4004), b.host, 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), null, b.buf(0xf4, 0)); + i4004 = b.addInstruction(0, b.addr(0x4004), b.host, 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), null, b.buf(0xf4, 0)); + i4004 = b.addInstruction(0, b.addr(0x4004), b.host, b.buf(0xf4, 0)); } assertEquals(i4004.getProgram().getMemory(), i4004.getMemory()); @@ -304,7 +304,7 @@ public class DBTraceCodeUnitTest extends AbstractGhidraHeadlessIntegrationTest TraceInstruction i4004; TraceInstruction g4006; try (UndoableTransaction tid = b.startTransaction()) { - i4004 = b.addInstruction(0, b.addr(0x4004), null, b.buf(0xf4, 0)); + i4004 = b.addInstruction(0, b.addr(0x4004), b.host, 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), guest, b.buf(0x90)); @@ -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), null, b.buf(0xf4, 0)); - i4006 = b.addInstruction(0, b.addr(0x4006), null, b.buf(0xf4, 0)); + i4004 = b.addInstruction(0, b.addr(0x4004), b.host, b.buf(0xf4, 0)); + i4006 = b.addInstruction(0, b.addr(0x4006), b.host, 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), null, b.buf(0xf4, 0)); - i4006 = b.addInstruction(0, b.addr(0x4006), null, b.buf(0xf4, 0)); + i4004 = b.addInstruction(0, b.addr(0x4004), b.host, b.buf(0xf4, 0)); + i4006 = b.addInstruction(0, b.addr(0x4006), b.host, 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), null); + i4004_10 = b.addInstruction(10, b.addr(0x4004), b.host); 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), null, b.buf(0xf4, 0)); - i4006 = b.addInstruction(0, b.addr(0x4006), null, b.buf(0xf4, 0)); + i4004 = b.addInstruction(0, b.addr(0x4004), b.host, b.buf(0xf4, 0)); + i4006 = b.addInstruction(0, b.addr(0x4006), b.host, 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), null, b.buf(0xf4, 0)); - i4006 = b.addInstruction(0, b.addr(0x4006), null, b.buf(0xf4, 0)); + i4004 = b.addInstruction(0, b.addr(0x4004), b.host, b.buf(0xf4, 0)); + i4006 = b.addInstruction(0, b.addr(0x4006), b.host, 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), null, b.buf(0xf4, 0)); + instruction = b.addInstruction(0, b.addr(0x4004), b.host, 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), null, b.buf(0xf4, 0)); + i4004 = b.addInstruction(0, b.addr(0x4004), b.host, b.buf(0xf4, 0)); d4000.setEndSnap(9); assertEquals(Range.closed(0L, 9L), d4000.getLifespan()); @@ -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), null, b.buf(0xc8, 0x47)); + i4004 = b.addInstruction(0, b.addr(0x4004), b.host, b.buf(0xc8, 0x47)); assertEquals("add r4,#0x7", i4004.toString()); - i4006 = b.addInstruction(0, b.addr(0x4006), null, b.buf(0xf4, 0)); + i4006 = b.addInstruction(0, b.addr(0x4006), b.host, b.buf(0xf4, 0)); assertEquals("ret", i4006.toString()); - i4008 = b.addInstruction(0, b.addr(0x4008), null, b.buf(0xff, 0xfc)); + i4008 = b.addInstruction(0, b.addr(0x4008), b.host, b.buf(0xff, 0xfc)); assertEquals("call 0x00004004", i4008.toString()); - i400a = b.addInstruction(0, b.addr(0x400a), null, b.buf(0xf6, 0x40)); + i400a = b.addInstruction(0, b.addr(0x400a), b.host, 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), null, b.buf(0xf4, 0)); + i4004 = b.addInstruction(0, b.addr(0x4004), b.host, 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), null, b.buf(0xf4, 0)); + i4004 = b.addInstruction(0, b.addr(0x4004), b.host, 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), null, b.buf(0xf4, 0)); + i4004 = b.addInstruction(0, b.addr(0x4004), b.host, b.buf(0xf4, 0)); d4006 = b.addData(0, b.addr(0x4006), PointerDataType.dataType, b.buf(0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00)); } @@ -1289,7 +1289,7 @@ public class DBTraceCodeUnitTest extends AbstractGhidraHeadlessIntegrationTest TraceInstruction i4004; TraceInstruction g4006; try (UndoableTransaction tid = b.startTransaction()) { - i4004 = b.addInstruction(0, b.addr(0x4004), null, b.buf(0xf4, 0)); + i4004 = b.addInstruction(0, b.addr(0x4004), b.host, 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), guest, b.buf(0x90)); @@ -1320,9 +1320,9 @@ public class DBTraceCodeUnitTest extends AbstractGhidraHeadlessIntegrationTest 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), null, b.buf(0xc8, 0x47)); + i4004 = b.addInstruction(0, b.addr(0x4004), b.host, 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)); + i4007 = b.addInstruction(0, b.addr(0x4007), b.host, 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/DBTraceProgramViewListingTest.java b/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/database/program/DBTraceProgramViewListingTest.java index f341e8c209..91ac722e54 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), null, b.buf(0xf4, 0)); + ins = b.addInstruction(0, b.addr(0x4005), b.host, 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), null, b.buf(0xf4, 0)); + i4005 = b.addInstruction(0, b.addr(0x4005), b.host, 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), null, b.buf(0xf4, 0)); + i4005 = b.addInstruction(0, b.addr(0x4005), b.host, 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), null, b.buf(0xf4, 0)); + i4005 = b.addInstruction(0, b.addr(0x4005), b.host, 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), null, b.buf(0xf4, 0)); + i4005 = b.addInstruction(0, b.addr(0x4005), b.host, 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), null, b.buf(0xf4, 0)); + i4005 = b.addInstruction(0, b.addr(0x4005), b.host, 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), null, b.buf(0xf4, 0)); + i4005 = b.addInstruction(0, b.addr(0x4005), b.host, 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), null, b.buf(0xf4, 0)); + i4005 = b.addInstruction(0, b.addr(0x4005), b.host, 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), null, b.buf(0xf4, 0)); + i4005 = b.addInstruction(0, b.addr(0x4005), b.host, 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), null, b.buf(0xf4, 0)); + i4005 = b.addInstruction(0, b.addr(0x4005), b.host, 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), 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)); + i4005 = b.addInstruction(0, b.addr(0x4005), b.host, b.buf(0xf4, 0)); + i4007 = b.addInstruction(0, b.addr(0x4007), b.host, b.buf(0xf4, 0)); + i400a = b.addInstruction(0, b.addr(0x400a), b.host, 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), null, b.buf(0xf4, 0)); + b.addInstruction(0, b.addr(0x4005), b.host, 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), null, b.buf(0xf4, 0)); + b.addInstruction(0, b.addr(0x4005), b.host, 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), null, b.buf(0xf4, 0)); + b.addInstruction(0, b.addr(0x4005), b.host, 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), null, b.buf(0xf4, 0)); + b.addInstruction(0, b.addr(0x4005), b.host, 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), null, b.buf(0xf4, 0)); + b.addInstruction(0, b.addr(0x4005), b.host, 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), null, b.buf(0xf4, 0)); + b.addInstruction(0, b.addr(0x400e), b.host, 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), null, b.buf(0xf4, 0)); + b.addInstruction(0, b.addr(0x4005), b.host, 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), null, b.buf(0xf4, 0)); + b.addInstruction(0, b.addr(0x4005), b.host, 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), null, b.buf(0xf4, 0)); + b.addInstruction(0, b.addr(0x4005), b.host, 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), null, b.buf(0xf4, 0)); + b.addInstruction(0, b.addr(0x4005), b.host, 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), null, b.buf(0xf4, 0)); + b.addInstruction(0, b.addr(0x4005), b.host, 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), null, b.buf(0xf4, 0)); + i4005 = b.addInstruction(0, b.addr(0x4005), b.host, 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), null, b.buf(0xf4, 0)); + i4005 = b.addInstruction(0, b.addr(0x4005), b.host, 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/generic/Unique.java b/Ghidra/Debug/ProposedUtils/src/main/java/generic/Unique.java index d87c78aeea..a8f7bb5ccc 100644 --- a/Ghidra/Debug/ProposedUtils/src/main/java/generic/Unique.java +++ b/Ghidra/Debug/ProposedUtils/src/main/java/generic/Unique.java @@ -15,7 +15,7 @@ */ package generic; -import java.util.Iterator; +import java.util.*; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -39,7 +39,12 @@ public interface Unique { } T result = it.next(); if (it.hasNext()) { - throw new AssertionError("Expected exactly one. Got many."); + List all = new ArrayList<>(); + all.add(result); + while (it.hasNext()) { + all.add(it.next()); + } + throw new AssertionError("Expected exactly one. Got many: " + all); } return result; } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/nav/Navigatable.java b/Ghidra/Features/Base/src/main/java/ghidra/app/nav/Navigatable.java index 977cd89fce..64fb41d58c 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/nav/Navigatable.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/nav/Navigatable.java @@ -24,9 +24,9 @@ import ghidra.program.util.ProgramSelection; /** * Interface for ComponentProviders to implement if they support basic navigation and selection - * capabilities. Implementing this interface will provide the provider with navigation history - * and actions that require navigation or selection. (Search Text, Search Memory, Select bytes, - * Select instructions, etc.) + * capabilities. Implementing this interface will provide the provider with navigation history and + * actions that require navigation or selection. (Search Text, Search Memory, Select bytes, Select + * instructions, etc.) */ public interface Navigatable { public static final long DEFAULT_NAVIGATABLE_ID = -1; @@ -35,6 +35,7 @@ public interface Navigatable { /** * Commands this navigatable to goto (display) the given program and location + * * @param program the program * * @param location the location in that program to display @@ -44,24 +45,27 @@ public interface Navigatable { /** * Returns the current location of this Navigatable + * * @return the current location of this Navigatable */ public ProgramLocation getLocation(); /** * Returns the current Program of this Navigatable + * * @return the current Program of this Navigatable */ public Program getProgram(); /** * Returns the view state for this navigatable + * * @return the view state for this navigatable */ public LocationMemento getMemento(); - /** - * Sets the view state for this navigatable. This is used later to restore the view state. + /** + * Sets the view state for this navigatable. This is used later to restore the view state. * * @param memento the state of this navigatable */ @@ -69,20 +73,32 @@ public interface Navigatable { /** * Returns an icon that represents this Navigatable + * * @return the icon */ public Icon getNavigatableIcon(); /** - * Returns true if this Navigatable is "connected". Navigatables are connected if they - * produce and consume location and selection events. + * Returns true if this Navigatable is "connected". Navigatables are connected if they produce + * and consume location and selection events. * * @return true if this Navigatable is "connected" */ public boolean isConnected(); + /** + * Return true if this Navigatable is part of the "dynamic analysis" or "debugger" user + * interface. + * + * @return tre if this Navigatable is "dynamic" + */ + default public boolean isDynamic() { + return false; + } + /** * Currently only the 'connected' windows support markers + * * @return true if this navigatable supports markers */ public boolean supportsMarkers(); @@ -94,12 +110,14 @@ public interface Navigatable { /** * Returns true if this provider is visible + * * @return true if visible */ public boolean isVisible(); /** * Tells this Navigatable to set its selection to the given selection + * * @param selection the selection to set. */ public void setSelection(ProgramSelection selection); @@ -113,43 +131,50 @@ public interface Navigatable { /** * Returns the current selection of this Navigatable + * * @return the current selection of this Navigatable */ public ProgramSelection getSelection(); /** * Returns the current highlight of this Navigatable + * * @return the current highlight of this Navigatable */ public ProgramSelection getHighlight(); /** * Returns the current text selection or null + * * @return the text selection */ public String getTextSelection(); /** * Adds a listener to be notified if this Navigatable is terminated + * * @param listener the listener to be notified when this Navigatable is closed */ public void addNavigatableListener(NavigatableRemovalListener listener); /** * Removes a listener to be notified if this Navigatable is terminated. - * @param listener the listener that no longer should be notified when this Navigatable is - * closed. + * + * @param listener the listener that no longer should be notified when this Navigatable is + * closed. */ public void removeNavigatableListener(NavigatableRemovalListener listener); /** * Returns true if this navigatable is no longer valid, false if it is still good + * * @return true if this navigatable is no longer valid, false if it is still good */ public boolean isDisposed(); /** * Returns true if this navigatable supports highlighting + * * @return true if this navigatable supports highlighting */ public boolean supportsHighlight(); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/assembler/AbstractPatchAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/assembler/AbstractPatchAction.java index f0bf95f198..507db7de5b 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/assembler/AbstractPatchAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/assembler/AbstractPatchAction.java @@ -57,6 +57,7 @@ public abstract class AbstractPatchAction extends DockingAction { private Program program; private Address address; + private CodeUnit codeUnit; private final KeyListener listenerForKeys = new KeyAdapter() { @Override @@ -165,6 +166,15 @@ public abstract class AbstractPatchAction extends DockingAction { return program; } + /** + * Get the code unit on which this action was invoked + * + * @return the current code unit + */ + protected CodeUnit getCodeUnit() { + return codeUnit; + } + /** * Get the address at which this action was invoked * @@ -276,14 +286,12 @@ public abstract class AbstractPatchAction extends DockingAction { @Override public boolean isAddToPopup(ActionContext context) { - if (!(context instanceof ListingActionContext)) { + CodeUnit cu = getCodeUnit(context); + if (cu == null || !isApplicableToUnit(cu)) { return false; } ListingActionContext lac = (ListingActionContext) context; - if (!isApplicableToUnit(lac.getCodeUnit())) { - return false; - } ComponentProvider provider = lac.getComponentProvider(); if (!(provider instanceof CodeViewerProvider)) { @@ -313,10 +321,9 @@ public abstract class AbstractPatchAction extends DockingAction { /** * Perform preparation and save any information needed later in {@link #accept()}. - * - * @param unit the code unit at the user's cursor */ - protected abstract void prepare(CodeUnit unit); + protected void prepare() { + } /** * Put the input fields in their place, show them, and place focus appropriately @@ -333,14 +340,26 @@ public abstract class AbstractPatchAction extends DockingAction { /** * Pre-fill the input fields and place the caret appropriately (usually at the end) - * - * @param unit the code unit at the user's cursor */ - protected abstract void fillInputs(CodeUnit unit); + protected abstract void fillInputs(); + + protected CodeUnit getCodeUnit(ActionContext context) { + if (!(context instanceof ListingActionContext)) { + return null; + } + + ListingActionContext lac = (ListingActionContext) context; + prepareLayout(lac); + if (codeViewerProvider.isReadOnly()) { + return null; + } + return lac.getCodeUnit(); + } @Override public void actionPerformed(ActionContext context) { - if (!(context instanceof ListingActionContext)) { + codeUnit = getCodeUnit(context); + if (codeUnit == null || !isApplicableToUnit(codeUnit)) { return; } @@ -349,10 +368,6 @@ public abstract class AbstractPatchAction extends DockingAction { if (codeViewerProvider.isReadOnly()) { return; } - CodeUnit cu = lac.getCodeUnit(); - if (!isApplicableToUnit(cu)) { - return; - } ProgramLocation cur = lac.getLocation(); program = cur.getProgram(); @@ -365,7 +380,7 @@ public abstract class AbstractPatchAction extends DockingAction { return; } - prepare(cu); + prepare(); ToolOptions displayOptions = tool.getOptions(GhidraOptions.CATEGORY_BROWSER_DISPLAY); Font font = displayOptions.getFont(GhidraOptions.OPTION_BASE_FONT, null); @@ -377,7 +392,7 @@ public abstract class AbstractPatchAction extends DockingAction { if (!showInputs(fieldPanel)) { return; } - fillInputs(cu); + fillInputs(); fieldLayoutManager.layoutContainer(fieldPanel); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/assembler/AssemblerPlugin.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/assembler/AssemblerPlugin.java index 468514d339..04e3dee701 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/assembler/AssemblerPlugin.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/assembler/AssemblerPlugin.java @@ -15,7 +15,9 @@ */ package ghidra.app.plugin.core.assembler; +import docking.ActionContext; import ghidra.app.CorePluginPackage; +import ghidra.app.context.ListingActionContext; import ghidra.app.plugin.PluginCategoryNames; import ghidra.app.plugin.ProgramPlugin; import ghidra.app.plugin.assembler.Assemblers; @@ -62,10 +64,29 @@ public class AssemblerPlugin extends ProgramPlugin { } private void createActions() { - patchInstructionAction = new PatchInstructionAction(this, "Patch Instruction"); + // Debugger provides its own "Patch Instruction" action + patchInstructionAction = new PatchInstructionAction(this) { + @Override + public boolean isEnabledForContext(ActionContext context) { + if (!super.isEnabledForContext(context)) { + return false; + } + ListingActionContext lac = (ListingActionContext) context; + return !lac.getNavigatable().isDynamic(); + } + + @Override + public boolean isAddToPopup(ActionContext context) { + if (!super.isAddToPopup(context)) { + return false; + } + ListingActionContext lac = (ListingActionContext) context; + return !lac.getNavigatable().isDynamic(); + } + }; tool.addAction(patchInstructionAction); - patchDataAction = new PatchDataAction(this, "Patch Data"); + patchDataAction = new PatchDataAction(this); tool.addAction(patchDataAction); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/assembler/AssemblyDualTextField.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/assembler/AssemblyDualTextField.java index 1b039ebfc9..ee226a3149 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/assembler/AssemblyDualTextField.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/assembler/AssemblyDualTextField.java @@ -38,11 +38,8 @@ import ghidra.app.plugin.processors.sleigh.*; import ghidra.framework.Application; import ghidra.framework.ApplicationConfiguration; import ghidra.program.model.address.Address; -import ghidra.program.model.lang.Language; import ghidra.program.model.lang.LanguageID; import ghidra.program.model.listing.Instruction; -import ghidra.program.model.listing.Program; -import ghidra.program.util.ProgramLocation; import ghidra.util.NumericUtilities; import resources.ResourceManager; @@ -71,7 +68,6 @@ public class AssemblyDualTextField { protected final AssemblyAutocompletionModel model = new AssemblyAutocompletionModel(); protected final AssemblyAutocompleter auto = new AssemblyAutocompleter(model); - protected Program program; protected Assembler assembler; protected Address address; protected Instruction existing; @@ -416,42 +412,36 @@ public class AssemblyDualTextField { } /** - * @see #setProgramLocation(Program, Address) + * Set the assembler to use + * + * @param assembler the assembler */ - public void setProgramLocation(ProgramLocation loc) { - setProgramLocation(loc.getProgram(), loc.getAddress()); + public void setAssembler(Assembler assembler) { + this.assembler = Objects.requireNonNull(assembler); } /** - * Set the current program location + * Set the address of the assembly instruction * *

- * This may cause the construction of a new assembler, if one suitable for the given program's - * language has not yet been built. + * Note this will reset the existing instruction to null to prevent its accidental re-use. See + * {@link #setExisting(Instruction)}. * - * @param program the program - * @param address the non-null address + * @param address the address */ - public void setProgramLocation(Program program, Address address) { - this.program = program; + public void setAddress(Address address) { this.address = Objects.requireNonNull(address); - this.existing = program.getListing().getInstructionAt(address); - - this.assembler = Assemblers.getAssembler(program); + this.existing = null; } /** - * Specify the language and address without binding to a program + * Set the "existing" instruction used for ordering proposed instructions by "most similar" * - * @param lang the language - * @param addr the address + * @see #computePreference(AssemblyResolvedPatterns) + * @param existing */ - public void setLanguageLocation(Language lang, Address addr) { - this.program = null; - this.address = addr; - this.existing = null; - - this.assembler = Assemblers.getAssembler(lang); + public void setExisting(Instruction existing) { + this.existing = existing; } /** @@ -704,7 +694,7 @@ public class AssemblyDualTextField { * @param existing the instruction, if any, currently under the user's cursor * @return a preference */ - protected int computePreference(AssemblyResolvedPatterns rc, Instruction existing) { + protected int computePreference(AssemblyResolvedPatterns rc) { if (existing == null) { return 0; } @@ -736,7 +726,7 @@ public class AssemblyDualTextField { * @return the collection of completion items */ protected Collection computeCompletions(String text) { - final AssemblyPatternBlock ctx = assembler.getContextAt(address); + final AssemblyPatternBlock ctx = getContext(); Set result = new TreeSet<>(); Collection parses = assembler.parseLine(text); @@ -757,7 +747,8 @@ public class AssemblyDualTextField { parses = assembler.parseLine(fullText); for (AssemblyParseResult parse : parses) { if (!parse.isError()) { - AssemblyResolutionResults sems = assembler.resolveTree(parse, address); + AssemblyResolutionResults sems = + assembler.resolveTree(parse, address, getContext()); for (AssemblyResolution ar : sems) { if (ar.isError()) { //result.add(new AssemblyError("", ar.toString())); @@ -766,7 +757,7 @@ public class AssemblyDualTextField { AssemblyResolvedPatterns rc = (AssemblyResolvedPatterns) ar; for (byte[] ins : rc.possibleInsVals(ctx)) { result.add(new AssemblyInstruction(text, Arrays.copyOf(ins, ins.length), - computePreference(rc, existing))); + computePreference(rc))); if (!exhaustUndefined) { break; } @@ -780,6 +771,15 @@ public class AssemblyDualTextField { return result; } + /** + * Get the context for filtering completed instructions in the auto-completer + * + * @return the context + */ + protected AssemblyPatternBlock getContext() { + return assembler.getContextAt(address).fillMask(); + } + /** * A demonstration of the assembly GUI outside of Ghidra */ @@ -807,7 +807,8 @@ public class AssemblyDualTextField { SleighLanguage lang = (SleighLanguage) provider.getLanguage(DEMO_LANG_ID); curAddr = lang.getDefaultSpace().getAddress(0); - input.setLanguageLocation(lang, curAddr); + input.setAssembler(Assemblers.getAssembler(lang)); + input.setAddress(curAddr); hbox.add(input.getAssemblyField()); hbox.add(input.getMnemonicField()); @@ -827,7 +828,7 @@ public class AssemblyDualTextField { asm.setText(asm.getText() + data); input.clear(); curAddr = curAddr.addWrap(ins.getData().length); - input.setLanguageLocation(lang, curAddr); + input.setAddress(curAddr); addrlabel.setText(String.format(ADDR_FORMAT, curAddr)); } }); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/assembler/PatchDataAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/assembler/PatchDataAction.java index cd1a666979..c4b5e8286d 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/assembler/PatchDataAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/assembler/PatchDataAction.java @@ -47,10 +47,14 @@ public class PatchDataAction extends AbstractPatchAction { private Data data; + public PatchDataAction(Plugin owner) { + this(owner, "Patch Data"); + } + public PatchDataAction(Plugin owner, String name) { super(owner, name); - setPopupMenuData(new MenuData(new String[] { "Patch Data" }, MENU_GROUP)); + setPopupMenuData(new MenuData(new String[] { name }, MENU_GROUP)); setKeyBindingData(new KeyBindingData(KEYBIND_PATCH_DATA)); setHelpLocation(new HelpLocation(owner.getName(), "patch_data")); @@ -81,9 +85,8 @@ public class PatchDataAction extends AbstractPatchAction { return true; } - @Override - protected void prepare(CodeUnit unit) { - data = (Data) unit; + protected Data getData() { + return (Data) getCodeUnit(); } @Override @@ -106,8 +109,8 @@ public class PatchDataAction extends AbstractPatchAction { } @Override - protected void fillInputs(CodeUnit unit) { - String repr = data.getDefaultValueRepresentation(); + protected void fillInputs() { + String repr = getData().getDefaultValueRepresentation(); input.setText(repr); input.setCaretPosition(repr.length()); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/assembler/PatchInstructionAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/assembler/PatchInstructionAction.java index 838a860012..84b1fbb6d9 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/assembler/PatchInstructionAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/assembler/PatchInstructionAction.java @@ -118,14 +118,14 @@ public class PatchInstructionAction extends AbstractPatchAction { private final Map> cache = LazyMap.lazyMap(new HashMap<>(), language -> new AssemblerConstructorWorker(language)); - /*test*/ final Map shownWarning = + /*test*/ static final Map SHOWN_WARNING = DefaultedMap.defaultedMap(new HashMap<>(), false); - /*test*/ final AssemblyDualTextField input = new AssemblyDualTextField(); + /*test*/ final AssemblyDualTextField input = newAssemblyDualTextField(); private final ListenerForAccept listenerForAccept = new ListenerForAccept(); - private Language language; - private Assembler assembler; + protected Language language; + protected Assembler assembler; // Callback to keep the autocompleter positioned under the fields private FieldPanelOverLayoutListener listenerToMoveAutocompleter = ev -> { @@ -135,10 +135,14 @@ public class PatchInstructionAction extends AbstractPatchAction { } }; + public PatchInstructionAction(Plugin owner) { + this(owner, "Patch Instruction"); + } + public PatchInstructionAction(Plugin owner, String name) { super(owner, name); - setPopupMenuData(new MenuData(new String[] { "Patch Instruction" }, MENU_GROUP)); + setPopupMenuData(new MenuData(new String[] { name }, MENU_GROUP)); setKeyBindingData(new KeyBindingData(KEYBIND_PATCH_INSTRUCTION)); setHelpLocation(new HelpLocation(owner.getName(), "patch_instruction")); @@ -151,6 +155,10 @@ public class PatchInstructionAction extends AbstractPatchAction { init(); } + protected AssemblyDualTextField newAssemblyDualTextField() { + return new AssemblyDualTextField(); + } + @Override public void dispose() { super.dispose(); @@ -182,24 +190,36 @@ public class PatchInstructionAction extends AbstractPatchAction { return true; } - @Override - protected void prepare(CodeUnit cu) { - language = cu.getProgram().getLanguage(); + protected void warnLanguage() { AssemblyRating rating = AssemblyRating.valueOf( language.getProperty(ASSEMBLY_RATING + ":" + language.getLanguageID(), AssemblyRating.UNRATED.name())); if (AssemblyRating.PLATINUM != rating) { String message = language.getProperty(ASSEMBLY_MESSAGE + ":" + language.getLanguageID(), rating.message); - if (!shownWarning.get(language)) { + if (!SHOWN_WARNING.get(language)) { Msg.showWarn(this, null, "Assembler Rating", "

" + message + "

"); - shownWarning.put(language, true); + SHOWN_WARNING.put(language, true); } } + } + protected Language getLanguage(CodeUnit cu) { + return cu.getProgram().getLanguage(); + } + + protected Assembler getAssembler(CodeUnit cu) { + return Assemblers.getAssembler(cu.getProgram()); + } + + @Override + protected void prepare() { + CodeUnit cu = getCodeUnit(); + language = getLanguage(cu); + warnLanguage(); cache.get(language).get(null); - assembler = Assemblers.getAssembler(cu.getProgram()); + assembler = getAssembler(cu); } @Override @@ -207,9 +227,19 @@ public class PatchInstructionAction extends AbstractPatchAction { input.setFont(font); } + protected Instruction getExistingInstruction() { + Program program = getProgram(); + if (program == null) { + return null; + } + return program.getListing().getInstructionAt(getAddress()); + } + @Override protected boolean showInputs(FieldPanel fieldPanel) { - input.setProgramLocation(getProgram(), getAddress()); + input.setAssembler(assembler); + input.setAddress(getAddress()); + input.setExisting(getExistingInstruction()); FieldLocation locMnem = findFieldLocation(getAddress(), "Mnemonic"); if (locMnem == null) { Msg.showError(this, fieldPanel, getName(), @@ -232,9 +262,10 @@ public class PatchInstructionAction extends AbstractPatchAction { } @Override - protected void fillInputs(CodeUnit unit) { - if (unit instanceof Instruction) { - Instruction ins = (Instruction) unit; + protected void fillInputs() { + CodeUnit cu = getCodeUnit(); + if (cu instanceof Instruction) { + Instruction ins = (Instruction) cu; String instr = ins.toString(); if (ins.isInDelaySlot()) { assert instr.startsWith("_"); @@ -269,6 +300,10 @@ public class PatchInstructionAction extends AbstractPatchAction { // Do nothing. User must select a completion item instead } + protected void applyPatch(byte[] data) throws MemoryAccessException { + assembler.patchProgram(data, getAddress()); + } + /** * Accept the given instruction selected by the user * @@ -279,7 +314,7 @@ public class PatchInstructionAction extends AbstractPatchAction { Address address = getAddress(); try (ProgramTransaction trans = ProgramTransaction.open(program, "Assemble @" + address + ": " + input.getText())) { - assembler.patchProgram(ins.getData(), address); + applyPatch(ins.getData()); trans.commit(); hide(); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/codebrowser/CodeViewerActionContext.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/codebrowser/CodeViewerActionContext.java index 5425f105c8..3ad0101c65 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/codebrowser/CodeViewerActionContext.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/codebrowser/CodeViewerActionContext.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -31,11 +30,4 @@ public class CodeViewerActionContext extends ListingActionContext implements super(provider, provider, location); } - /** - * @return true if underlying code viewer corresponds to a dynamic listing - */ - public boolean isDyanmicListing() { - return ((CodeViewerProvider) getComponentProvider()).isDynamicListing(); - } - } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/disassembler/DisassemblerPlugin.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/disassembler/DisassemblerPlugin.java index 29e65d12c1..68d51719dc 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/disassembler/DisassemblerPlugin.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/disassembler/DisassemblerPlugin.java @@ -21,7 +21,6 @@ import ghidra.app.cmd.disassemble.*; import ghidra.app.context.ListingActionContext; import ghidra.app.events.ProgramActivatedPluginEvent; import ghidra.app.plugin.PluginCategoryNames; -import ghidra.app.plugin.core.codebrowser.CodeViewerActionContext; import ghidra.framework.options.Options; import ghidra.framework.plugintool.*; import ghidra.framework.plugintool.util.PluginStatus; @@ -235,9 +234,6 @@ public class DisassemblerPlugin extends Plugin { Program currentProgram = context.getProgram(); DisassembleCommand cmd = null; - boolean isDynamicListing = (context instanceof CodeViewerActionContext && - ((CodeViewerActionContext) context).isDyanmicListing()); - if ((currentSelection != null) && (!currentSelection.isEmpty())) { cmd = new DisassembleCommand(currentSelection, null, true); } @@ -271,13 +267,18 @@ public class DisassemblerPlugin extends Plugin { } } if (cmd != null) { - cmd.enableCodeAnalysis(!isDynamicListing); // do not analyze debugger listing + // do not analyze debugger listing + cmd.enableCodeAnalysis(!context.getNavigatable().isDynamic()); tool.executeBackgroundCommand(cmd, currentProgram); } } boolean checkDisassemblyEnabled(ListingActionContext context, Address address, boolean followPtr) { + // Debugger now has its own Disassemble actions + if (context.getNavigatable().isDynamic()) { + return false; + } ProgramSelection currentSelection = context.getSelection(); Program currentProgram = context.getProgram(); if ((currentSelection != null) && (!currentSelection.isEmpty())) { diff --git a/Ghidra/Features/Base/src/test/java/ghidra/app/plugin/core/assembler/AssemblerPluginTestHelper.java b/Ghidra/Features/Base/src/test/java/ghidra/app/plugin/core/assembler/AssemblerPluginTestHelper.java index 8dea345212..9e81d81812 100644 --- a/Ghidra/Features/Base/src/test/java/ghidra/app/plugin/core/assembler/AssemblerPluginTestHelper.java +++ b/Ghidra/Features/Base/src/test/java/ghidra/app/plugin/core/assembler/AssemblerPluginTestHelper.java @@ -30,12 +30,12 @@ import ghidra.app.plugin.core.assembler.AssemblyDualTextField.AssemblyInstructio import ghidra.app.plugin.core.codebrowser.CodeViewerProvider; import ghidra.app.util.viewer.listingpanel.ListingPanel; import ghidra.program.model.address.Address; +import ghidra.program.model.lang.Language; import ghidra.program.model.listing.*; import ghidra.program.util.ProgramLocation; import ghidra.test.AbstractGhidraHeadedIntegrationTest; public class AssemblerPluginTestHelper { - public final AssemblerPlugin assemblerPlugin; private final CodeViewerProvider provider; private final Program program; @@ -46,20 +46,30 @@ public class AssemblerPluginTestHelper { private final Listing listing; - public AssemblerPluginTestHelper(AssemblerPlugin assemblerPlugin, CodeViewerProvider provider, - Program program) { - this.assemblerPlugin = assemblerPlugin; + public AssemblerPluginTestHelper(PatchInstructionAction patchInstructionAction, + PatchDataAction patchDataAction, CodeViewerProvider provider, Program program) { this.provider = provider; this.program = program; - this.patchInstructionAction = assemblerPlugin.patchInstructionAction; - this.patchDataAction = assemblerPlugin.patchDataAction; - this.instructionInput = assemblerPlugin.patchInstructionAction.input; - this.dataInput = assemblerPlugin.patchDataAction.input; + this.patchInstructionAction = patchInstructionAction; + this.patchDataAction = patchDataAction; + this.instructionInput = + patchInstructionAction == null ? null : patchInstructionAction.input; + this.dataInput = patchDataAction == null ? null : patchDataAction.input; this.listing = program.getListing(); // Snuff the assembler's warning prompt - patchInstructionAction.shownWarning.put(program.getLanguage(), true); + snuffWarning(program.getLanguage()); + } + + public AssemblerPluginTestHelper(AssemblerPlugin assemblerPlugin, CodeViewerProvider provider, + Program program) { + this(assemblerPlugin.patchInstructionAction, assemblerPlugin.patchDataAction, provider, + program); + } + + public void snuffWarning(Language language) { + PatchInstructionAction.SHOWN_WARNING.put(language, true); } public void assertDualFields() { @@ -69,12 +79,18 @@ public class AssemblerPluginTestHelper { } public List inputAndGetCompletions(String text) { - return AbstractGenericTest.runSwing(() -> { + AbstractGenericTest.runSwing(() -> { instructionInput.setText(text); instructionInput.auto.startCompletion(instructionInput.getOperandsField()); instructionInput.auto.flushUpdates(); - return instructionInput.auto.getSuggestions(); }); + return AbstractGenericTest.waitForValue(() -> AbstractGenericTest.runSwing(() -> { + List suggestions = instructionInput.auto.getSuggestions(); + if (suggestions.isEmpty()) { + return null; + } + return suggestions; + })); } public void goTo(Address address) { @@ -95,18 +111,21 @@ public class AssemblerPluginTestHelper { public Instruction patchInstructionAt(Address address, String expText, String newText) { goTo(address); + Language language = patchInstructionAction.getLanguage(listing.getCodeUnitAt(address)); + snuffWarning(language); - AbstractDockingTest.performAction(assemblerPlugin.patchInstructionAction, provider, true); + AbstractDockingTest.performAction(patchInstructionAction, provider, true); assertDualFields(); assertEquals(expText, instructionInput.getText()); - assertEquals(address, assemblerPlugin.patchInstructionAction.getAddress()); + assertEquals(address, patchInstructionAction.getAddress()); List completions = inputAndGetCompletions(newText); + assertFalse("There are no assembly completion options", completions.isEmpty()); AssemblyCompletion first = completions.get(0); assertTrue(first instanceof AssemblyInstruction); AssemblyInstruction ai = (AssemblyInstruction) first; - AbstractGenericTest.runSwing(() -> assemblerPlugin.patchInstructionAction.accept(ai)); + AbstractGenericTest.runSwing(() -> patchInstructionAction.accept(ai)); AbstractGhidraHeadedIntegrationTest.waitForProgram(program); return Objects.requireNonNull(listing.getInstructionAt(address)); @@ -115,14 +134,14 @@ public class AssemblerPluginTestHelper { public Data patchDataAt(Address address, String expText, String newText) { goTo(address); - AbstractDockingTest.performAction(assemblerPlugin.patchDataAction, provider, true); + AbstractDockingTest.performAction(patchDataAction, provider, true); assertTrue(dataInput.isVisible()); assertEquals(expText, dataInput.getText()); - assertEquals(address, assemblerPlugin.patchDataAction.getAddress()); + assertEquals(address, patchDataAction.getAddress()); AbstractGenericTest.runSwing(() -> { dataInput.setText(newText); - assemblerPlugin.patchDataAction.accept(); + patchDataAction.accept(); }); AbstractGhidraHeadedIntegrationTest.waitForProgram(program); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/plugin/assembler/sleigh/sem/AssemblyResolvedPatterns.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/plugin/assembler/sleigh/sem/AssemblyResolvedPatterns.java index 83a09f81a2..5cefcd1c36 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/plugin/assembler/sleigh/sem/AssemblyResolvedPatterns.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/plugin/assembler/sleigh/sem/AssemblyResolvedPatterns.java @@ -702,6 +702,10 @@ public class AssemblyResolvedPatterns extends AssemblyResolution { * @return the iterable */ public Iterable possibleInsVals(AssemblyPatternBlock forCtx) { + AssemblyPatternBlock ctxCompat = ctx.combine(forCtx); + if (ctxCompat == null) { + return List.of(); + } Predicate removeForbidden = (byte[] val) -> { for (AssemblyResolvedPatterns f : forbids) { // If the forbidden length is larger than us, we can ignore it diff --git a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/app/plugin/assembler/sleigh/ARMAssemblyTest.java b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/app/plugin/assembler/sleigh/ARMAssemblyTest.java index ee6920f4ed..1d53e2f94b 100644 --- a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/app/plugin/assembler/sleigh/ARMAssemblyTest.java +++ b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/app/plugin/assembler/sleigh/ARMAssemblyTest.java @@ -70,6 +70,11 @@ public class ARMAssemblyTest extends AbstractAssemblyTest { assertOneCompatRestExact("ands r0,r5", "28:40", THUMB, 0x00400000, "ands r0,r5"); } + @Test + public void testAssemble_T_movs_r0_r0() { + assertOneCompatRestExact("movs r0,r0", "00:00", THUMB, 0x00400000, "movs r0,r0"); + } + @Test public void testAssemble_T_bl_0x00008000() { // What makes this different from the above test is that it jumps backward diff --git a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/app/plugin/assembler/sleigh/AbstractAssemblyTest.java b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/app/plugin/assembler/sleigh/AbstractAssemblyTest.java index 6b526504dd..67902fa3bd 100644 --- a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/app/plugin/assembler/sleigh/AbstractAssemblyTest.java +++ b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/app/plugin/assembler/sleigh/AbstractAssemblyTest.java @@ -200,9 +200,8 @@ public abstract class AbstractAssemblyTest extends AbstractGenericTest { */ protected void checkAllExact(AssemblyResolutionResults rr, Collection disassembly, long addr, String ctxstr) { - final AssemblyPatternBlock ctx = - (ctxstr == null ? context.getDefault() : AssemblyPatternBlock.fromString(ctxstr)) - .fillMask(); + final AssemblyPatternBlock ctx = (ctxstr == null ? context.getDefault() + : AssemblyPatternBlock.fromString(ctxstr)).fillMask(); dbg.println("Checking each: " + disassembly + " ctx:" + ctx); boolean gotOne = false; boolean failedOne = false;