Merge remote-tracking branch

'origin/GP-2099_Dan_disassembleAsActions--SQUASHED'

Conflicts:
	Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/AbstractGhidraHeadedDebuggerGUITest.java
This commit is contained in:
Ryan Kurtz 2022-06-25 01:54:22 -04:00
commit 5098c04745
66 changed files with 2213 additions and 774 deletions

View file

@ -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);
}
}

View file

@ -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;
}
}

View file

@ -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<TraceProgramView> 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);
}
}

View file

@ -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());
}
}

View file

@ -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<LanguageID> 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<LanguageID> 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<DockingActionIf> 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<DockingActionIf> result, Trace trace) {
Language language = trace.getBaseLanguage();
if (language.getProcessor() == Processor.toProcessor("DATA")) {
return;
}
getActionsForLanguage(result, trace.getPlatformManager().getHostPlatform());
}
protected void getActionsForGuest(List<DockingActionIf> 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<DockingActionIf> result, Trace trace,
Address address) {
for (TraceGuestPlatform guest : trace.getPlatformManager().getGuestPlatforms()) {
getActionsForGuest(result, guest, address);
}
}
protected List<DockingActionIf> getActionsFor(List<DockingActionIf> result, Trace trace,
long snap, Address address) {
getActionsForHost(result, trace);
getActionsForAllGuests(result, trace, address);
return result;
}
@Override
public List<DockingActionIf> 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);
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -13,35 +13,34 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * 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.framework.cmd.TypedBackgroundCommand;
import ghidra.program.disassemble.Disassembler; import ghidra.program.disassemble.Disassembler;
import ghidra.program.model.address.*; import ghidra.program.model.address.*;
import ghidra.program.model.lang.*; import ghidra.program.model.lang.*;
import ghidra.program.model.listing.Instruction;
import ghidra.program.model.mem.MemBuffer; import ghidra.program.model.mem.MemBuffer;
import ghidra.program.model.mem.MemoryBufferImpl; import ghidra.trace.model.guest.TracePlatform;
import ghidra.program.model.util.CodeUnitInsertionException;
import ghidra.trace.model.guest.TraceGuestPlatform;
import ghidra.trace.model.program.TraceProgramView; import ghidra.trace.model.program.TraceProgramView;
import ghidra.util.MathUtilities; import ghidra.util.MathUtilities;
import ghidra.util.task.TaskMonitor; import ghidra.util.task.TaskMonitor;
public class DisassembleTraceCommand extends TypedBackgroundCommand<TraceProgramView> { public class TraceDisassembleCommand extends TypedBackgroundCommand<TraceProgramView> {
public static DisassembleTraceCommand create(TraceGuestPlatform guest, Address start,
AddressSetView restrictedSet) {
return guest == null ? new DisassembleTraceCommand(start, restrictedSet)
: new DisassembleGuestTraceCommand(guest, start, restrictedSet);
}
protected final TracePlatform platform;
protected final Address start; protected final Address start;
protected final AddressSetView restrictedSet; protected final AddressSetView restrictedSet;
protected RegisterValue initialContext; protected RegisterValue initialContext;
private AddressSetView disassembled; private AddressSetView disassembled;
public DisassembleTraceCommand(Address start, AddressSetView restrictedSet) { public TraceDisassembleCommand(TracePlatform platform, Address start,
AddressSetView restrictedSet) {
super("Disassemble", true, true, false); super("Disassemble", true, true, false);
this.platform = platform;
this.start = start; this.start = start;
this.restrictedSet = restrictedSet; this.restrictedSet = restrictedSet;
} }
@ -51,11 +50,12 @@ public class DisassembleTraceCommand extends TypedBackgroundCommand<TraceProgram
} }
protected Disassembler getDisassembler(TraceProgramView view, TaskMonitor monitor) { protected Disassembler getDisassembler(TraceProgramView view, TaskMonitor monitor) {
return Disassembler.getDisassembler(view, monitor, monitor::setMessage); return Disassembler.getDisassembler(platform.getLanguage(), platform.getAddressFactory(),
monitor, monitor::setMessage);
} }
protected MemBuffer getBuffer(TraceProgramView view) { protected MemBuffer getBuffer(TraceProgramView view) {
return new MemoryBufferImpl(view.getMemory(), start); return platform.getMappedMemBuffer(view.getSnap(), platform.mapHostToGuest(start));
} }
protected int computeLimit() { protected int computeLimit() {
@ -68,14 +68,12 @@ public class DisassembleTraceCommand extends TypedBackgroundCommand<TraceProgram
} }
protected AddressSetView writeBlock(TraceProgramView view, InstructionBlock block) { protected AddressSetView writeBlock(TraceProgramView view, InstructionBlock block) {
InstructionSet set = new InstructionSet(view.getAddressFactory()); InstructionSet set = new InstructionSet(platform.getAddressFactory());
set.addBlock(block); set.addBlock(block);
try { return view.getTrace()
return view.getListing().addInstructions(set, true); .getCodeManager()
} .instructions()
catch (CodeUnitInsertionException e) { .addInstructionSet(Range.atLeast(view.getSnap()), platform, set, true);
return new AddressSet();
}
} }
@Override @Override
@ -83,8 +81,21 @@ public class DisassembleTraceCommand extends TypedBackgroundCommand<TraceProgram
Disassembler disassembler = getDisassembler(view, monitor); Disassembler disassembler = getDisassembler(view, monitor);
MemBuffer buffer = getBuffer(view); MemBuffer buffer = getBuffer(view);
int limit = computeLimit(); int limit = computeLimit();
// TODO: limit is actually instruction count, not byte count :'(
InstructionBlock block = disassembler.pseudoDisassembleBlock(buffer, initialContext, limit); InstructionBlock block = disassembler.pseudoDisassembleBlock(buffer, initialContext, limit);
disassembled = writeBlock(view, block); if (block == null) {
return true; // Alignment issue. Just go silently.
}
InstructionBlock filtered = new InstructionBlock(block.getStartAddress());
for (Instruction ins : block) {
if (restrictedSet.contains(ins.getMaxAddress())) {
filtered.addInstruction(ins);
}
else {
break;
}
}
disassembled = writeBlock(view, filtered);
return true; return true;
} }

View file

@ -86,6 +86,9 @@ public interface RegisterLocationTrackingSpec extends LocationTrackingSpec {
return false; return false;
} }
Register register = computeRegister(coordinates); Register register = computeRegister(coordinates);
if (register == null) {
return false;
}
AddressRange regRng = TraceRegisterUtils.rangeForRegister(register); AddressRange regRng = TraceRegisterUtils.rangeForRegister(register);
return range.getRange().intersects(regRng); return range.getRange().intersects(regRng);
} }

View file

@ -73,6 +73,9 @@ public class DebuggerStateEditingPlugin extends AbstractDebuggerPlugin {
if (current.getTrace() == null) { if (current.getTrace() == null) {
return; return;
} }
if (editingService == null) {
return;
}
editingService.setCurrentMode(current.getTrace(), state.getUserData()); editingService.setCurrentMode(current.getTrace(), state.getUserData());
// TODO: Limit selectable modes? // TODO: Limit selectable modes?
// No sense showing Write Target, if the trace can never be live, again.... // No sense showing Write Target, if the trace can never be live, again....

View file

@ -348,6 +348,11 @@ public class DebuggerListingProvider extends CodeViewerProvider {
return false; return false;
} }
@Override
public boolean isDynamic() {
return true;
}
/** /**
* Check if this is the main dynamic listing. * Check if this is the main dynamic listing.
* *

View file

@ -396,6 +396,11 @@ public class DebuggerMemoryBytesProvider extends ProgramByteViewerComponentProvi
return false; return false;
} }
@Override
public boolean isDynamic() {
return true;
}
public boolean isMainViewer() { public boolean isMainViewer() {
return isMainViewer; return isMainViewer;
} }

View file

@ -123,12 +123,17 @@ public class DebuggerRegistersPlugin extends AbstractDebuggerPlugin {
public static String encodeSetsByCSpec( public static String encodeSetsByCSpec(
Map<LanguageCompilerSpecPair, LinkedHashSet<Register>> setsByCSpec) { Map<LanguageCompilerSpecPair, LinkedHashSet<Register>> setsByCSpec) {
return StringUtils.join(setsByCSpec.entrySet().stream().map(ent -> { return setsByCSpec.entrySet()
.stream()
.map(ent -> {
LanguageCompilerSpecPair lcsp = ent.getKey(); LanguageCompilerSpecPair lcsp = ent.getKey();
String regs = StringUtils.join( return lcsp.languageID + "/" + lcsp.compilerSpecID + ":" + ent.getValue()
ent.getValue().stream().map(Register::getName).collect(Collectors.toList()), ','); .stream()
return lcsp.languageID + "/" + lcsp.compilerSpecID + ":" + regs; .filter(r -> r != null)
}).collect(Collectors.toList()), ';'); .map(Register::getName)
.collect(Collectors.joining(","));
})
.collect(Collectors.joining(";"));
} }
@Override @Override

View file

@ -18,13 +18,13 @@ package ghidra.app.plugin.core.debug.mapping;
import java.util.Collection; import java.util.Collection;
import java.util.Set; 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.app.plugin.core.debug.workflow.DisassemblyInject;
import ghidra.framework.plugintool.PluginTool; import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.address.*; import ghidra.program.model.address.*;
import ghidra.program.model.lang.*; import ghidra.program.model.lang.*;
import ghidra.trace.model.Trace; 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.target.TraceObject;
import ghidra.trace.model.thread.TraceThread; import ghidra.trace.model.thread.TraceThread;
import ghidra.util.task.TaskMonitor; import ghidra.util.task.TaskMonitor;
@ -72,16 +72,14 @@ public abstract class AbstractDebuggerPlatformMapper implements DebuggerPlatform
if (isCancelSilently(start, snap)) { if (isCancelSilently(start, snap)) {
return DisassemblyResult.CANCELLED; return DisassemblyResult.CANCELLED;
} }
TraceGuestPlatform guest = TracePlatform platform = trace.getPlatformManager().getPlatform(getCompilerSpec(object));
trace.getPlatformManager().getGuestPlatform(getCompilerSpec(object));
Collection<DisassemblyInject> injects = getDisassemblyInjections(object); Collection<DisassemblyInject> injects = getDisassemblyInjections(object);
DisassembleTraceCommand dis = TraceDisassembleCommand dis = new TraceDisassembleCommand(platform, start, restricted);
DisassembleTraceCommand.create(guest, start, restricted); Language language = platform.getLanguage();
Language language = guest == null ? trace.getBaseLanguage() : guest.getLanguage();
AddressSet startSet = new AddressSet(start); AddressSet startSet = new AddressSet(start);
for (DisassemblyInject i : injects) { 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); boolean result = dis.applyToTyped(trace.getFixedProgramView(snap), monitor);
if (!result) { if (!result) {

View file

@ -28,6 +28,10 @@ public interface DebuggerPlatformMapper {
/** /**
* Prepare the given trace for interpretation under this mapper * Prepare the given trace for interpretation under this mapper
* *
* <p>
* Likely, this will need to modify the trace database. It must start its own transaction for
* doing so.
*
* @param trace the trace * @param trace the trace
* @param snap the snap * @param snap the snap
*/ */

View file

@ -24,6 +24,7 @@ import ghidra.trace.model.guest.TraceGuestPlatform;
import ghidra.trace.model.guest.TracePlatformManager; import ghidra.trace.model.guest.TracePlatformManager;
import ghidra.trace.model.target.TraceObject; import ghidra.trace.model.target.TraceObject;
import ghidra.util.MathUtilities; import ghidra.util.MathUtilities;
import ghidra.util.database.UndoableTransaction;
public class DefaultDebuggerPlatformMapper extends AbstractDebuggerPlatformMapper { public class DefaultDebuggerPlatformMapper extends AbstractDebuggerPlatformMapper {
@ -54,6 +55,9 @@ public class DefaultDebuggerPlatformMapper extends AbstractDebuggerPlatformMappe
@Override @Override
public void addToTrace(long snap) { public void addToTrace(long snap) {
try (UndoableTransaction tid = UndoableTransaction.start(trace, "Add guest " +
cSpec.getLanguage().getLanguageDescription() + "/" + cSpec.getCompilerSpecDescription(),
true)) {
TracePlatformManager platformManager = trace.getPlatformManager(); TracePlatformManager platformManager = trace.getPlatformManager();
TraceGuestPlatform platform = platformManager.getOrAddGuestPlatform(cSpec); TraceGuestPlatform platform = platformManager.getOrAddGuestPlatform(cSpec);
if (platform == null) { if (platform == null) {
@ -61,7 +65,16 @@ public class DefaultDebuggerPlatformMapper extends AbstractDebuggerPlatformMappe
} }
addMappedRanges(platform); addMappedRanges(platform);
} }
}
/**
* Add mapped ranges if not already present
*
* <p>
* A transaction is already started when this method is invoked.
*
* @param platform the platform
*/
protected void addMappedRanges(TraceGuestPlatform platform) { protected void addMappedRanges(TraceGuestPlatform platform) {
Trace trace = platform.getTrace(); Trace trace = platform.getTrace();
AddressSpace hostSpace = trace.getBaseAddressFactory().getDefaultAddressSpace(); AddressSpace hostSpace = trace.getBaseAddressFactory().getDefaultAddressSpace();

View file

@ -17,7 +17,9 @@ package ghidra.app.plugin.core.debug.platform.arm;
import java.math.BigInteger; 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.framework.plugintool.PluginTool;
import ghidra.program.model.address.AddressSetView; import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.lang.*; import ghidra.program.model.lang.*;
@ -58,7 +60,7 @@ public class ArmDisassemblyInject implements DisassemblyInject {
} }
@Override @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, Language language, long snap, TraceThread thread, AddressSetView startSet,
AddressSetView restricted) { AddressSetView restricted) {
/** /**
@ -79,7 +81,7 @@ public class ArmDisassemblyInject implements DisassemblyInject {
TraceMemoryRegisterSpace regs = TraceMemoryRegisterSpace regs =
trace.getMemoryManager().getMemoryRegisterSpace(thread, false); 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 * 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. * 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)); command.setInitialContext(new RegisterValue(tModeReg, BigInteger.ONE));
return; 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); RegisterValue cpsrVal = regs.getValue(snap, cpsrReg);
if (isThumbMode(cpsrVal)) { if (isThumbMode(cpsrVal)) {

View file

@ -22,6 +22,7 @@ import java.util.Set;
import java.util.concurrent.*; import java.util.concurrent.*;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import ghidra.app.plugin.core.debug.disassemble.TraceDisassembleCommand;
import ghidra.app.plugin.core.debug.workflow.*; import ghidra.app.plugin.core.debug.workflow.*;
import ghidra.app.services.DebuggerModelService; import ghidra.app.services.DebuggerModelService;
import ghidra.app.services.TraceRecorder; import ghidra.app.services.TraceRecorder;
@ -49,7 +50,7 @@ public class DbgengX64DisassemblyInject implements DisassemblyInject {
} }
@Override @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, Language language, long snap, TraceThread thread, AddressSetView startSet,
AddressSetView restricted) { AddressSetView restricted) {
AddressRange first = startSet.getFirstRange(); AddressRange first = startSet.getFirstRange();

View file

@ -84,7 +84,7 @@ public class GdbDebuggerPlatformOpinion extends AbstractDebuggerPlatformOpinion
@Override @Override
protected Set<DebuggerPlatformOffer> getOffers(TraceObject object, long snap, TraceObject env, protected Set<DebuggerPlatformOffer> getOffers(TraceObject object, long snap, TraceObject env,
String debugger, String arch, String os, Endian endian) { String debugger, String arch, String os, Endian endian) {
if (!"gdb".equals(debugger.toLowerCase())) { if (debugger == null || !"gdb".equals(debugger.toLowerCase())) {
return Set.of(); return Set.of();
} }
return getCompilerSpecsForGnu(arch, endian).stream().flatMap(lcsp -> { return getCompilerSpecsForGnu(arch, endian).stream().flatMap(lcsp -> {

View file

@ -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);
}
}

View file

@ -17,6 +17,7 @@ package ghidra.app.plugin.core.debug.workflow;
import java.util.Arrays; import java.util.Arrays;
import ghidra.app.plugin.core.debug.disassemble.TraceDisassembleCommand;
import ghidra.framework.plugintool.PluginTool; import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.address.AddressSetView; import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.lang.Language; 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 startSet the starting address set, usually just the PC
* @param restricted the set of disassemblable addresses * @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, Language language, long snap, TraceThread thread, AddressSetView startSet,
AddressSetView restricted) { AddressSetView restricted) {
} }

View file

@ -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("" + //
"<context>" + //
" <schema name='Session' elementResync='NEVER' attributeResync='ONCE'>" + //
" <attribute name='Targets' schema='TargetContainer' />" + //
" </schema>" + //
" <schema name='TargetContainer' canonical='yes' elementResync='NEVER' " + //
" attributeResync='ONCE'>" + //
" <element schema='Target' />" + //
" </schema>" + //
" <schema name='Target' elementResync='NEVER' attributeResync='NEVER'>" + //
" <interface name='Process' />" + //
" <interface name='Aggregate' />" + //
" <attribute name='Environment' schema='Environment' />" + //
" <attribute name='Memory' schema='Memory' />" + //
" <attribute name='Threads' schema='ThreadContainer' />" + //
" </schema>" + //
" <schema name='Environment' elementResync='NEVER' " + //
" attributeResync='NEVER'>" + //
" <interface name='Environment' />" + //
" </schema>" + //
" <schema name='Memory' canonical='yes' elementResync='NEVER' " + //
" attributeResync='NEVER'>" + //
" <element schema='MemoryRegion' />" + //
" </schema>" + //
" <schema name='MemoryRegion' elementResync='NEVER' attributeResync='NEVER'>" + //
" <interface name='MemoryRegion' />" + //
" </schema>" + //
" <schema name='ThreadContainer' canonical='yes' elementResync='NEVER' " + //
" attributeResync='NEVER'>" + //
" <element schema='Thread' />" + //
" </schema>" + //
" <schema name='Thread' elementResync='NEVER' attributeResync='NEVER'>" + //
" <interface name='Thread' />" + //
" <interface name='Aggregate' />" + //
" <attribute name='Stack' schema='Stack' />" + //
" </schema>" + //
" <schema name='Stack' canonical='yes' elementResync='NEVER' " + //
" attributeResync='NEVER'>" + //
" <interface name='Stack' />" + //
" <element schema='Frame' />" + //
" </schema>" + //
" <schema name='Frame' elementResync='NEVER' attributeResync='NEVER'>" + //
" <interface name='StackFrame' />" + //
" </schema>" + //
"</context>");
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<DebuggerBot> 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<ByteBuffer> 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<Long> 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<ByteBuffer> 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<DockingActionIf> 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<FixedPlatformTracePatchInstructionAction> 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());
}
}

View file

@ -591,6 +591,7 @@ public abstract class AbstractGhidraHeadedDebuggerGUITest
} }
waitForTasks(); waitForTasks();
env.dispose(); env.dispose();
} }

View file

@ -129,7 +129,7 @@ public class DebuggerManualTest extends AbstractGhidraHeadedDebuggerGUITest {
tb.trace.getThreadManager().createThread("Thread 2", 4); tb.trace.getThreadManager().createThread("Thread 2", 4);
tb.addData(0, tb.addr(0x4004), Undefined4DataType.dataType, tb.buf(6, 7, 8, 9)); 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(); Language x86 = getSLEIGH_X86_LANGUAGE();
DBTraceGuestPlatform guest = DBTraceGuestPlatform guest =

View file

@ -25,10 +25,20 @@ import ghidra.trace.model.target.TraceObject;
public class TestDebuggerPlatformOpinion extends AbstractDebuggerPlatformOpinion { public class TestDebuggerPlatformOpinion extends AbstractDebuggerPlatformOpinion {
enum Offers implements DebuggerPlatformOffer { enum Offers implements DebuggerPlatformOffer {
X86_64 { ARM_V8_LE("Test armv8le", "ARM:LE:32:v8", "default"),
X86_64("Test x86-64", "x86:LE:64:default", "gcc");
private final String description;
private final LanguageCompilerSpecPair lcsp;
private Offers(String description, String langID, String cSpecID) {
this.description = description;
this.lcsp = new LanguageCompilerSpecPair(langID, cSpecID);
}
@Override @Override
public String getDescription() { public String getDescription() {
return "Test x86-64"; return description;
} }
@Override @Override
@ -38,14 +48,18 @@ public class TestDebuggerPlatformOpinion extends AbstractDebuggerPlatformOpinion
@Override @Override
public CompilerSpec getCompilerSpec() { public CompilerSpec getCompilerSpec() {
return getCompilerSpec(new LanguageID("x86:LE:64:default"), null); try {
return lcsp.getCompilerSpec();
}
catch (LanguageNotFoundException | CompilerSpecNotFoundException e) {
throw new AssertionError(e);
}
} }
@Override @Override
public DebuggerPlatformMapper take(PluginTool tool, Trace trace) { public DebuggerPlatformMapper take(PluginTool tool, Trace trace) {
return new DefaultDebuggerPlatformMapper(tool, trace, getCompilerSpec()); return new DefaultDebuggerPlatformMapper(tool, trace, getCompilerSpec());
} }
};
} }
@Override @Override
@ -54,9 +68,12 @@ public class TestDebuggerPlatformOpinion extends AbstractDebuggerPlatformOpinion
if (!"test".equals(debugger)) { if (!"test".equals(debugger)) {
return Set.of(); return Set.of();
} }
if (!"x86-64".equals(arch)) { if ("armv8le".equals(arch)) {
return Set.of(); return Set.of(Offers.ARM_V8_LE);
} }
if ("x86-64".equals(arch)) {
return Set.of(Offers.X86_64); return Set.of(Offers.X86_64);
} }
return Set.of();
}
} }

View file

@ -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("" + //
"<context>" + //
" <schema name='Session' elementResync='NEVER' attributeResync='ONCE'>" + //
" <attribute name='Targets' schema='TargetContainer' />" + //
" </schema>" + //
" <schema name='TargetContainer' canonical='yes' elementResync='NEVER' " + //
" attributeResync='ONCE'>" + //
" <element schema='Target' />" + //
" </schema>" + //
" <schema name='Target' elementResync='NEVER' attributeResync='NEVER'>" + //
" <interface name='Process' />" + //
" <interface name='Aggregate' />" + //
" <attribute name='Environment' schema='Environment' />" + //
" <attribute name='Memory' schema='Memory' />" + //
" <attribute name='Threads' schema='ThreadContainer' />" + //
" </schema>" + //
" <schema name='Environment' elementResync='NEVER' " + //
" attributeResync='NEVER'>" + //
" <interface name='Environment' />" + //
" </schema>" + //
" <schema name='Memory' canonical='yes' elementResync='NEVER' " + //
" attributeResync='NEVER'>" + //
" <element schema='MemoryRegion' />" + //
" </schema>" + //
" <schema name='MemoryRegion' elementResync='NEVER' attributeResync='NEVER'>" + //
" <interface name='MemoryRegion' />" + //
" </schema>" + //
" <schema name='ThreadContainer' canonical='yes' elementResync='NEVER' " + //
" attributeResync='NEVER'>" + //
" <element schema='Thread' />" + //
" </schema>" + //
" <schema name='Thread' elementResync='NEVER' attributeResync='NEVER'>" + //
" <interface name='Thread' />" + //
" <interface name='Aggregate' />" + //
" <attribute name='Stack' schema='Stack' />" + //
" </schema>" + //
" <schema name='Stack' canonical='yes' elementResync='NEVER' " + //
" attributeResync='NEVER'>" + //
" <interface name='Stack' />" + //
" <element schema='Frame' />" + //
" </schema>" + //
" <schema name='Frame' elementResync='NEVER' attributeResync='NEVER'>" + //
" <interface name='StackFrame' />" + //
" </schema>" + //
"</context>");
DebuggerWorkflowService workflowService =
addPlugin(tool, DebuggerWorkflowServiceProxyPlugin.class);
addPlugin(tool, DebuggerListingPlugin.class);
addPlugin(tool, DebuggerPlatformServicePlugin.class);
Set<DebuggerBot> 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<Long> 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)));
});
}
}

View file

@ -42,7 +42,8 @@ import ghidra.util.exception.VersionException;
import ghidra.util.task.TaskMonitor; import ghidra.util.task.TaskMonitor;
@DBAnnotatedObjectInfo(version = 0) @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"; public static final String TABLE_NAME = "Platforms";
@DBAnnotatedObjectInfo(version = 0) @DBAnnotatedObjectInfo(version = 0)
@ -160,6 +161,16 @@ public class DBTraceGuestPlatform extends DBAnnotatedObject implements TraceGues
return manager.trace; return manager.trace;
} }
@Override
public int getIntKey() {
return (int) key;
}
@Override
public boolean isGuest() {
return true;
}
protected void deleteMappedRange(DBTraceGuestPlatformMappedRange range, TaskMonitor monitor) protected void deleteMappedRange(DBTraceGuestPlatformMappedRange range, TaskMonitor monitor)
throws CancelledException { throws CancelledException {
try (LockHold hold = LockHold.lock(manager.lock.writeLock())) { try (LockHold hold = LockHold.lock(manager.lock.writeLock())) {
@ -175,6 +186,7 @@ public class DBTraceGuestPlatform extends DBAnnotatedObject implements TraceGues
} }
} }
@Override
@Internal @Internal
public DBTraceGuestLanguage getLanguageEntry() { public DBTraceGuestLanguage getLanguageEntry() {
return languageEntry; return languageEntry;

View file

@ -68,7 +68,7 @@ public class DBTraceGuestPlatformMappedRange extends DBAnnotatedObject
private DBTracePlatformManager manager; private DBTracePlatformManager manager;
private AddressRangeImpl hostRange; private AddressRangeImpl hostRange;
private DBTraceGuestPlatform guestPlatform; private DBTraceGuestPlatform platform;
private AddressRangeImpl guestRange; private AddressRangeImpl guestRange;
public DBTraceGuestPlatformMappedRange(DBTracePlatformManager manager, DBCachedObjectStore<?> s, public DBTraceGuestPlatformMappedRange(DBTracePlatformManager manager, DBCachedObjectStore<?> s,
@ -83,28 +83,27 @@ public class DBTraceGuestPlatformMappedRange extends DBAnnotatedObject
if (created) { if (created) {
return; return;
} }
try {
Address hostStart = Address hostStart =
manager.getBaseLanguage().getAddressFactory().getAddress(hostSpace, hostOffset); manager.trace.getBaseLanguage()
Address hostEnd = hostStart.addNoWrap(length - 1); .getAddressFactory()
.getAddress(hostSpace, hostOffset);
Address hostEnd = hostStart.addWrap(length - 1);
this.hostRange = new AddressRangeImpl(hostStart, hostEnd); this.hostRange = new AddressRangeImpl(hostStart, hostEnd);
this.guestPlatform = manager.getPlatformByKey(guestPlatformKey); InternalTracePlatform platform = manager.getPlatformByKey(guestPlatformKey);
Address guestStart = if (platform.isHost()) {
guestPlatform.getAddressFactory().getAddress(guestSpace, guestOffset); throw new IOException("Table is corrupt. Got host platform in guest mapping.");
Address guestEnd = guestStart.addNoWrap(length - 1); }
this.platform = (DBTraceGuestPlatform) platform;
Address guestStart = platform.getAddressFactory().getAddress(guestSpace, guestOffset);
Address guestEnd = guestStart.addWrap(length - 1);
this.guestRange = new AddressRangeImpl(guestStart, guestEnd); this.guestRange = new AddressRangeImpl(guestStart, guestEnd);
} }
catch (AddressOverflowException e) {
throw new RuntimeException("Database is corrupt or languages changed", e);
}
}
void set(Address hostStart, DBTraceGuestPlatform guestPlatform, Address guestStart, void set(Address hostStart, DBTraceGuestPlatform platform, Address guestStart, long length) {
long length) {
this.hostSpace = hostStart.getAddressSpace().getSpaceID(); this.hostSpace = hostStart.getAddressSpace().getSpaceID();
this.hostOffset = hostStart.getOffset(); this.hostOffset = hostStart.getOffset();
this.guestPlatformKey = (int) guestPlatform.getKey(); this.guestPlatformKey = (int) platform.getKey();
this.guestSpace = guestStart.getAddressSpace().getSpaceID(); this.guestSpace = guestStart.getAddressSpace().getSpaceID();
this.guestOffset = guestStart.getOffset(); this.guestOffset = guestStart.getOffset();
this.length = length; this.length = length;
@ -112,19 +111,19 @@ public class DBTraceGuestPlatformMappedRange extends DBAnnotatedObject
GUEST_OFFSET_COLUMN, LENGTH_COLUMN); GUEST_OFFSET_COLUMN, LENGTH_COLUMN);
this.hostRange = new AddressRangeImpl(hostStart, hostStart.addWrap(length - 1)); this.hostRange = new AddressRangeImpl(hostStart, hostStart.addWrap(length - 1));
this.guestPlatform = guestPlatform; this.platform = platform;
this.guestRange = new AddressRangeImpl(guestStart, guestStart.addWrap(length - 1)); this.guestRange = new AddressRangeImpl(guestStart, guestStart.addWrap(length - 1));
} }
@Override @Override
public Language getHostLanguage() { public Language getHostLanguage() {
return manager.getBaseLanguage(); return manager.trace.getBaseLanguage();
} }
@Override @Override
public CompilerSpec getHostCompilerSpec() { public CompilerSpec getHostCompilerSpec() {
return manager.getBaseCompilerSpec(); return manager.trace.getBaseCompilerSpec();
} }
@Override @Override
@ -134,7 +133,7 @@ public class DBTraceGuestPlatformMappedRange extends DBAnnotatedObject
@Override @Override
public DBTraceGuestPlatform getGuestPlatform() { public DBTraceGuestPlatform getGuestPlatform() {
return guestPlatform; return platform;
} }
@Override @Override

View file

@ -21,14 +21,15 @@ import java.util.concurrent.locks.ReadWriteLock;
import db.DBHandle; import db.DBHandle;
import ghidra.lifecycle.Internal; 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.lang.*;
import ghidra.program.model.listing.Instruction; import ghidra.program.model.mem.MemBuffer;
import ghidra.trace.database.DBTrace; import ghidra.trace.database.DBTrace;
import ghidra.trace.database.DBTraceManager; import ghidra.trace.database.DBTraceManager;
import ghidra.trace.database.guest.DBTraceGuestPlatform.DBTraceGuestLanguage; import ghidra.trace.database.guest.DBTraceGuestPlatform.DBTraceGuestLanguage;
import ghidra.trace.model.guest.TraceGuestPlatform; import ghidra.trace.model.Trace;
import ghidra.trace.model.guest.TracePlatformManager; import ghidra.trace.model.guest.*;
import ghidra.trace.model.listing.TraceInstruction;
import ghidra.util.LockHold; import ghidra.util.LockHold;
import ghidra.util.database.*; import ghidra.util.database.*;
import ghidra.util.exception.CancelledException; import ghidra.util.exception.CancelledException;
@ -57,6 +58,68 @@ public class DBTracePlatformManager implements DBTraceManager, TracePlatformMana
protected final DBCachedObjectStore<DBTraceGuestPlatformMappedRange> rangeMappingStore; protected final DBCachedObjectStore<DBTraceGuestPlatformMappedRange> 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, public DBTracePlatformManager(DBHandle dbh, DBOpenMode openMode, ReadWriteLock lock,
TaskMonitor monitor, CompilerSpec baseCompilerSpec, DBTrace trace) TaskMonitor monitor, CompilerSpec baseCompilerSpec, DBTrace trace)
throws VersionException, IOException { throws VersionException, IOException {
@ -129,9 +192,9 @@ public class DBTracePlatformManager implements DBTraceManager, TracePlatformMana
} }
@Internal @Internal
public DBTraceGuestPlatform getPlatformByKey(int key) { public InternalTracePlatform getPlatformByKey(int key) {
if (key == -1) { if (key == -1) {
return null; return hostPlatform;
} }
return platformStore.getObjectAt(key); return platformStore.getObjectAt(key);
} }
@ -201,13 +264,8 @@ public class DBTracePlatformManager implements DBTraceManager, TracePlatformMana
} }
@Override @Override
public Language getBaseLanguage() { public InternalTracePlatform getHostPlatform() {
return trace.getBaseLanguage(); return hostPlatform;
}
@Override
public CompilerSpec getBaseCompilerSpec() {
return trace.getBaseCompilerSpec();
} }
protected DBTraceGuestPlatform doAddGuestPlatform(CompilerSpec compilerSpec) { protected DBTraceGuestPlatform doAddGuestPlatform(CompilerSpec compilerSpec) {
@ -219,9 +277,9 @@ public class DBTracePlatformManager implements DBTraceManager, TracePlatformMana
@Override @Override
public DBTraceGuestPlatform addGuestPlatform(CompilerSpec compilerSpec) { public DBTraceGuestPlatform addGuestPlatform(CompilerSpec compilerSpec) {
if (compilerSpec.getCompilerSpecID() if (trace.getBaseCompilerSpec() == compilerSpec) {
.equals(trace.getBaseCompilerSpec().getCompilerSpecID())) { throw new IllegalArgumentException(
throw new IllegalArgumentException("Base language cannot be a guest language"); "Base compiler spec cannot be a guest compiler spec");
} }
try (LockHold hold = LockHold.lock(lock.writeLock())) { try (LockHold hold = LockHold.lock(lock.writeLock())) {
return doAddGuestPlatform(compilerSpec); return doAddGuestPlatform(compilerSpec);
@ -229,8 +287,11 @@ public class DBTracePlatformManager implements DBTraceManager, TracePlatformMana
} }
@Override @Override
public DBTraceGuestPlatform getGuestPlatform(CompilerSpec compilerSpec) { public InternalTracePlatform getPlatform(CompilerSpec compilerSpec) {
try (LockHold hold = LockHold.lock(lock.readLock())) { try (LockHold hold = LockHold.lock(lock.readLock())) {
if (trace.getBaseCompilerSpec() == compilerSpec) {
return hostPlatform;
}
return platformsByCompiler.get(compilerSpec); return platformsByCompiler.get(compilerSpec);
} }
} }
@ -255,33 +316,10 @@ public class DBTracePlatformManager implements DBTraceManager, TracePlatformMana
return platformView; 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 @Internal
public DBTraceGuestPlatform assertMine(TraceGuestPlatform platform) { public InternalTracePlatform assertMine(TracePlatform platform) {
if (platform == null) { if (platform == hostPlatform) {
return null; return hostPlatform;
} }
if (!(platform instanceof DBTraceGuestPlatform)) { if (!(platform instanceof DBTraceGuestPlatform)) {
throw new IllegalArgumentException("Given platform does not belong to this trace"); throw new IllegalArgumentException("Given platform does not belong to this trace");

View file

@ -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();
}

View file

@ -26,6 +26,7 @@ import generic.NestedIterator;
import ghidra.program.model.address.*; import ghidra.program.model.address.*;
import ghidra.trace.database.DBTraceUtils; import ghidra.trace.database.DBTraceUtils;
import ghidra.trace.database.space.DBTraceDelegatingManager; import ghidra.trace.database.space.DBTraceDelegatingManager;
import ghidra.trace.model.Trace;
import ghidra.trace.model.TraceAddressSnapRange; import ghidra.trace.model.TraceAddressSnapRange;
import ghidra.util.LockHold; import ghidra.util.LockHold;
@ -40,6 +41,10 @@ public abstract class AbstractBaseDBTraceCodeUnitsMemoryView<T extends DBTraceCo
Collections2.transform(manager.getActiveMemorySpaces(), this::getView); Collections2.transform(manager.getActiveMemorySpaces(), this::getView);
} }
public Trace getTrace() {
return manager.getTrace();
}
protected abstract M getView(DBTraceCodeSpace space); protected abstract M getView(DBTraceCodeSpace space);
protected T nullOrUndefined(long snap, Address address) { protected T nullOrUndefined(long snap, Address address) {

View file

@ -19,6 +19,7 @@ import com.google.common.collect.Range;
import generic.NestedIterator; import generic.NestedIterator;
import ghidra.program.model.address.*; import ghidra.program.model.address.*;
import ghidra.trace.model.Trace;
import ghidra.trace.model.TraceAddressSnapRange; import ghidra.trace.model.TraceAddressSnapRange;
public abstract class AbstractBaseDBTraceCodeUnitsView<T extends DBTraceCodeUnitAdapter> { public abstract class AbstractBaseDBTraceCodeUnitsView<T extends DBTraceCodeUnitAdapter> {
@ -29,6 +30,10 @@ public abstract class AbstractBaseDBTraceCodeUnitsView<T extends DBTraceCodeUnit
this.space = space; this.space = space;
} }
public Trace getTrace() {
return space.manager.getTrace();
}
public abstract int size(); public abstract int size();
public T getBefore(long snap, Address address) { public T getBefore(long snap, Address address) {

View file

@ -25,7 +25,7 @@ import ghidra.program.model.data.DataType;
import ghidra.program.model.lang.Language; import ghidra.program.model.lang.Language;
import ghidra.trace.database.DBTrace; import ghidra.trace.database.DBTrace;
import ghidra.trace.database.data.DBTraceDataSettingsAdapter.DBTraceDataSettingsSpace; import ghidra.trace.database.data.DBTraceDataSettingsAdapter.DBTraceDataSettingsSpace;
import ghidra.trace.model.guest.TraceGuestPlatform; import ghidra.trace.model.guest.TracePlatform;
import ghidra.trace.model.thread.TraceThread; import ghidra.trace.model.thread.TraceThread;
import ghidra.util.LockHold; import ghidra.util.LockHold;
@ -84,8 +84,8 @@ public abstract class AbstractDBTraceDataComponent implements DBTraceDefinedData
} }
@Override @Override
public TraceGuestPlatform getGuestPlatform() { public TracePlatform getPlatform() {
return root.getGuestPlatform(); return root.getPlatform();
} }
@Override @Override

View file

@ -142,7 +142,7 @@ public class DBTraceCodeSpace implements TraceCodeSpace, DBTraceSpaceBased {
TraceAddressSnapRangeQuery.intersecting(range, span)).values()) { TraceAddressSnapRangeQuery.intersecting(range, span)).values()) {
monitor.checkCanceled(); monitor.checkCanceled();
monitor.incrementProgress(1); monitor.incrementProgress(1);
if (instruction.guest != guest) { if (instruction.platform != guest) {
continue; continue;
} }
instructionMapSpace.deleteData(instruction); instructionMapSpace.deleteData(instruction);
@ -154,7 +154,7 @@ public class DBTraceCodeSpace implements TraceCodeSpace, DBTraceSpaceBased {
TraceAddressSnapRangeQuery.intersecting(range, span)).values()) { TraceAddressSnapRangeQuery.intersecting(range, span)).values()) {
monitor.checkCanceled(); monitor.checkCanceled();
monitor.incrementProgress(1); monitor.incrementProgress(1);
if (dataUnit.guest != guest) { if (dataUnit.platform != guest) {
continue; continue;
} }
// TODO: I don't yet have guest-language data units. // TODO: I don't yet have guest-language data units.

View file

@ -26,9 +26,9 @@ import ghidra.program.model.data.*;
import ghidra.program.model.lang.Language; import ghidra.program.model.lang.Language;
import ghidra.trace.database.DBTraceUtils; import ghidra.trace.database.DBTraceUtils;
import ghidra.trace.database.data.DBTraceDataSettingsAdapter.DBTraceDataSettingsSpace; import ghidra.trace.database.data.DBTraceDataSettingsAdapter.DBTraceDataSettingsSpace;
import ghidra.trace.database.guest.DBTraceGuestPlatform; import ghidra.trace.database.guest.InternalTracePlatform;
import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree; import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree;
import ghidra.trace.model.guest.TraceGuestPlatform; import ghidra.trace.model.guest.TracePlatform;
import ghidra.util.LockHold; import ghidra.util.LockHold;
import ghidra.util.database.DBCachedObjectStore; import ghidra.util.database.DBCachedObjectStore;
import ghidra.util.database.DBObjectColumn; import ghidra.util.database.DBObjectColumn;
@ -56,7 +56,7 @@ public class DBTraceData extends AbstractDBTraceCodeUnit<DBTraceData>
@DBAnnotatedField(column = DATATYPE_COLUMN_NAME) @DBAnnotatedField(column = DATATYPE_COLUMN_NAME)
private long dataTypeID; private long dataTypeID;
protected DBTraceGuestPlatform guest; protected InternalTracePlatform platform;
protected DataType dataType; protected DataType dataType;
protected DataType baseDataType; protected DataType baseDataType;
protected Settings defaultSettings; protected Settings defaultSettings;
@ -75,8 +75,8 @@ public class DBTraceData extends AbstractDBTraceCodeUnit<DBTraceData>
if (created) { if (created) {
return; return;
} }
guest = space.manager.platformManager.getPlatformByKey(platformKey); platform = space.manager.platformManager.getPlatformByKey(platformKey);
if (guest == null && platformKey != -1) { if (platform == null) {
throw new IOException("Data table is corrupt. Missing platform: " + platformKey); throw new IOException("Data table is corrupt. Missing platform: " + platformKey);
} }
dataType = space.dataTypeManager.getDataType(dataTypeID); dataType = space.dataTypeManager.getDataType(dataTypeID);
@ -102,12 +102,12 @@ public class DBTraceData extends AbstractDBTraceCodeUnit<DBTraceData>
return this; return this;
} }
protected void set(DBTraceGuestPlatform platform, DataType dataType) { protected void set(InternalTracePlatform platform, DataType dataType) {
this.platformKey = (int) (platform == null ? -1 : platform.getKey()); this.platformKey = platform.getIntKey();
this.dataTypeID = space.dataTypeManager.getResolvedID(dataType); this.dataTypeID = space.dataTypeManager.getResolvedID(dataType);
update(PLATFORM_COLUMN, DATATYPE_COLUMN); update(PLATFORM_COLUMN, DATATYPE_COLUMN);
this.guest = platform; this.platform = platform;
// Use the stored dataType, not the given one, in case it's different // Use the stored dataType, not the given one, in case it's different
this.dataType = space.dataTypeManager.getDataType(dataTypeID); this.dataType = space.dataTypeManager.getDataType(dataTypeID);
assert this.dataType != null; assert this.dataType != null;
@ -133,8 +133,8 @@ public class DBTraceData extends AbstractDBTraceCodeUnit<DBTraceData>
} }
@Override @Override
public TraceGuestPlatform getGuestPlatform() { public TracePlatform getPlatform() {
return guest; return platform;
} }
@Override @Override
@ -157,7 +157,7 @@ public class DBTraceData extends AbstractDBTraceCodeUnit<DBTraceData>
@Override @Override
public Language getLanguage() { public Language getLanguage() {
return guest == null ? space.baseLanguage : guest.getLanguage(); return platform.getLanguage();
} }
@Override @Override

View file

@ -146,7 +146,7 @@ public class DBTraceDefinedDataView extends AbstractBaseDBTraceDefinedUnitsView<
DBTraceData created = space.dataMapSpace.put(tasr, null); DBTraceData created = space.dataMapSpace.put(tasr, null);
// TODO: data units with a guest platform // 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? // TODO: Explicitly remove undefined from cache, or let weak refs take care of it?
cacheForContaining.notifyNewEntry(lifespan, createdRange, created); cacheForContaining.notifyNewEntry(lifespan, createdRange, created);

View file

@ -32,13 +32,13 @@ import ghidra.program.model.symbol.*;
import ghidra.trace.database.DBTraceUtils; import ghidra.trace.database.DBTraceUtils;
import ghidra.trace.database.context.DBTraceRegisterContextManager; import ghidra.trace.database.context.DBTraceRegisterContextManager;
import ghidra.trace.database.context.DBTraceRegisterContextSpace; import ghidra.trace.database.context.DBTraceRegisterContextSpace;
import ghidra.trace.database.guest.DBTraceGuestPlatform;
import ghidra.trace.database.guest.DBTraceGuestPlatform.DBTraceGuestLanguage; import ghidra.trace.database.guest.DBTraceGuestPlatform.DBTraceGuestLanguage;
import ghidra.trace.database.guest.InternalTracePlatform;
import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree; import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree;
import ghidra.trace.database.symbol.DBTraceReference; import ghidra.trace.database.symbol.DBTraceReference;
import ghidra.trace.database.symbol.DBTraceReferenceSpace; import ghidra.trace.database.symbol.DBTraceReferenceSpace;
import ghidra.trace.model.Trace.TraceInstructionChangeType; 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.listing.TraceInstruction;
import ghidra.trace.model.symbol.TraceReference; import ghidra.trace.model.symbol.TraceReference;
import ghidra.trace.util.*; import ghidra.trace.util.*;
@ -79,7 +79,7 @@ public class DBTraceInstruction extends AbstractDBTraceCodeUnit<DBTraceInstructi
protected class GuestInstructionContext implements InstructionContext { protected class GuestInstructionContext implements InstructionContext {
@Override @Override
public Address getAddress() { public Address getAddress() {
return guest.mapHostToGuest(getX1()); return platform.mapHostToGuest(getX1());
} }
@Override @Override
@ -118,7 +118,7 @@ public class DBTraceInstruction extends AbstractDBTraceCodeUnit<DBTraceInstructi
protected FlowOverride flowOverride; protected FlowOverride flowOverride;
protected ParserContext parserContext; protected ParserContext parserContext;
protected DBTraceGuestPlatform guest; protected InternalTracePlatform platform;
protected InstructionContext instructionContext; protected InstructionContext instructionContext;
public DBTraceInstruction(DBTraceCodeSpace space, public DBTraceInstruction(DBTraceCodeSpace space,
@ -127,9 +127,9 @@ public class DBTraceInstruction extends AbstractDBTraceCodeUnit<DBTraceInstructi
super(space, tree, store, record); super(space, tree, store, record);
} }
protected void doSetGuestMapping(final DBTraceGuestPlatform guest) { protected void doSetPlatformMapping(final InternalTracePlatform platform) {
this.guest = guest; this.platform = platform;
if (guest == null) { if (platform.isHost()) {
instructionContext = this; instructionContext = this;
} }
else { else {
@ -137,11 +137,11 @@ public class DBTraceInstruction extends AbstractDBTraceCodeUnit<DBTraceInstructi
} }
} }
protected void set(DBTraceGuestPlatform guest, InstructionPrototype prototype, protected void set(InternalTracePlatform platform, InstructionPrototype prototype,
ProcessorContextView context) { ProcessorContextView context) {
this.platformKey = (int) (guest == null ? -1 : guest.getKey()); this.platformKey = platform.getIntKey();
// NOTE: Using "this" for the MemBuffer seems a bit precarious. // NOTE: Using "this" for the MemBuffer seems a bit precarious.
DBTraceGuestLanguage languageEntry = guest == null ? null : guest.getLanguageEntry(); DBTraceGuestLanguage languageEntry = platform == null ? null : platform.getLanguageEntry();
this.prototypeKey = (int) space.manager this.prototypeKey = (int) space.manager
.findOrRecordPrototype(prototype, languageEntry, this, context) .findOrRecordPrototype(prototype, languageEntry, this, context)
.getKey(); .getKey();
@ -149,7 +149,7 @@ public class DBTraceInstruction extends AbstractDBTraceCodeUnit<DBTraceInstructi
update(PLATFORM_COLUMN, PROTOTYPE_COLUMN, FLAGS_COLUMN); update(PLATFORM_COLUMN, PROTOTYPE_COLUMN, FLAGS_COLUMN);
// TODO: Can there be more in this context than the context register??? // TODO: Can there be more in this context than the context register???
doSetGuestMapping(guest); doSetPlatformMapping(platform);
this.prototype = prototype; this.prototype = prototype;
} }
@ -160,8 +160,8 @@ public class DBTraceInstruction extends AbstractDBTraceCodeUnit<DBTraceInstructi
// Wait for something to set prototype // Wait for something to set prototype
return; return;
} }
guest = space.manager.platformManager.getPlatformByKey(platformKey); platform = space.manager.platformManager.getPlatformByKey(platformKey);
if (guest == null && platformKey != -1) { if (platform == null) {
throw new IOException("Instruction table is corrupt. Missing platform: " + platformKey); throw new IOException("Instruction table is corrupt. Missing platform: " + platformKey);
} }
prototype = space.manager.getPrototypeByKey(prototypeKey); prototype = space.manager.getPrototypeByKey(prototypeKey);
@ -173,7 +173,7 @@ public class DBTraceInstruction extends AbstractDBTraceCodeUnit<DBTraceInstructi
} }
flowOverride = FlowOverride.values()[(flags & FLOWOVERRIDE_SET_MASK) >> FLOWOVERRIDE_SHIFT]; flowOverride = FlowOverride.values()[(flags & FLOWOVERRIDE_SET_MASK) >> FLOWOVERRIDE_SHIFT];
doSetGuestMapping(guest); doSetPlatformMapping(platform);
} }
@Override @Override
@ -206,8 +206,8 @@ public class DBTraceInstruction extends AbstractDBTraceCodeUnit<DBTraceInstructi
} }
@Override @Override
public TraceGuestPlatform getGuestPlatform() { public TracePlatform getPlatform() {
return guest; return platform;
} }
@Override @Override
@ -273,8 +273,7 @@ public class DBTraceInstruction extends AbstractDBTraceCodeUnit<DBTraceInstructi
public Address getDefaultFallThrough() { public Address getDefaultFallThrough() {
try (LockHold hold = LockHold.lock(space.lock.readLock())) { try (LockHold hold = LockHold.lock(space.lock.readLock())) {
Address fallThrough = getGuestDefaultFallThrough(); Address fallThrough = getGuestDefaultFallThrough();
return guest == null || fallThrough == null ? fallThrough return platform.mapGuestToHost(fallThrough);
: guest.mapGuestToHost(fallThrough);
} }
} }
@ -392,12 +391,12 @@ public class DBTraceInstruction extends AbstractDBTraceCodeUnit<DBTraceInstructi
public Address[] getDefaultFlows() { public Address[] getDefaultFlows() {
try (LockHold hold = LockHold.lock(space.lock.readLock())) { try (LockHold hold = LockHold.lock(space.lock.readLock())) {
Address[] guestFlows = getGuestDefaultFlows(); Address[] guestFlows = getGuestDefaultFlows();
if (guest == null || guestFlows == null) { if (platform.isHost() || guestFlows == null) {
return guestFlows; return guestFlows;
} }
List<Address> hostFlows = new ArrayList<>(); List<Address> hostFlows = new ArrayList<>();
for (Address g : guestFlows) { for (Address g : guestFlows) {
Address h = guest.mapGuestToHost(g); Address h = platform.mapGuestToHost(g);
if (h != null) { if (h != null) {
hostFlows.add(h); hostFlows.add(h);
} }

View file

@ -24,7 +24,7 @@ import com.google.common.collect.Range;
import ghidra.program.model.address.*; import ghidra.program.model.address.*;
import ghidra.program.model.lang.*; import ghidra.program.model.lang.*;
import ghidra.program.model.util.CodeUnitInsertionException; 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.trace.model.listing.TraceInstructionsView;
import ghidra.util.LockHold; import ghidra.util.LockHold;
import ghidra.util.exception.CancelledException; import ghidra.util.exception.CancelledException;
@ -50,17 +50,16 @@ public class DBTraceInstructionsMemoryView
@Override @Override
public DBTraceInstruction create(Range<Long> lifespan, Address address, public DBTraceInstruction create(Range<Long> lifespan, Address address,
TraceGuestPlatform platform, InstructionPrototype prototype, TracePlatform platform, InstructionPrototype prototype,
ProcessorContextView context) throws CodeUnitInsertionException { ProcessorContextView context) throws CodeUnitInsertionException {
return delegateWrite(address.getAddressSpace(), return delegateWrite(address.getAddressSpace(),
m -> m.create(lifespan, address, platform, prototype, context)); m -> m.create(lifespan, address, platform, prototype, context));
} }
@Override @Override
public AddressSetView addInstructionSet(Range<Long> lifespan, TraceGuestPlatform platform, public AddressSetView addInstructionSet(Range<Long> lifespan, TracePlatform platform,
InstructionSet instructionSet, boolean overwrite) { InstructionSet instructionSet, boolean overwrite) {
InstructionSet mappedSet = manager.platformManager InstructionSet mappedSet = platform.mapGuestInstructionAddressesToHost(instructionSet);
.mapGuestInstructionAddressesToHost(platform, instructionSet);
Map<AddressSpace, InstructionSet> breakDown = new HashMap<>(); Map<AddressSpace, InstructionSet> breakDown = new HashMap<>();
// TODO: I'm not sure the consequences of breaking an instruction set down. // TODO: I'm not sure the consequences of breaking an instruction set down.

View file

@ -29,12 +29,12 @@ import ghidra.program.model.util.CodeUnitInsertionException;
import ghidra.trace.database.DBTraceUtils; import ghidra.trace.database.DBTraceUtils;
import ghidra.trace.database.context.DBTraceRegisterContextManager; import ghidra.trace.database.context.DBTraceRegisterContextManager;
import ghidra.trace.database.context.DBTraceRegisterContextSpace; 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.database.memory.DBTraceMemorySpace;
import ghidra.trace.model.ImmutableTraceAddressSnapRange; import ghidra.trace.model.ImmutableTraceAddressSnapRange;
import ghidra.trace.model.Trace.TraceCodeChangeType; import ghidra.trace.model.Trace.TraceCodeChangeType;
import ghidra.trace.model.guest.TraceGuestPlatform;
import ghidra.trace.model.TraceAddressSnapRange; import ghidra.trace.model.TraceAddressSnapRange;
import ghidra.trace.model.guest.TracePlatform;
import ghidra.trace.model.listing.TraceInstruction; import ghidra.trace.model.listing.TraceInstruction;
import ghidra.trace.model.listing.TraceInstructionsView; import ghidra.trace.model.listing.TraceInstructionsView;
import ghidra.trace.util.OverlappingObjectIterator; import ghidra.trace.util.OverlappingObjectIterator;
@ -53,7 +53,7 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView
protected class InstructionBlockAdder { protected class InstructionBlockAdder {
private final Set<Address> skipDelaySlots; private final Set<Address> skipDelaySlots;
private final Range<Long> lifespan; private final Range<Long> lifespan;
private final DBTraceGuestPlatform platform; private final InternalTracePlatform platform;
private final InstructionBlock block; private final InstructionBlock block;
private final Address errorAddress; private final Address errorAddress;
private final InstructionError conflict; private final InstructionError conflict;
@ -62,7 +62,7 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView
protected int count = 0; protected int count = 0;
private InstructionBlockAdder(Set<Address> skipDelaySlots, Range<Long> lifespan, private InstructionBlockAdder(Set<Address> skipDelaySlots, Range<Long> lifespan,
DBTraceGuestPlatform platform, InstructionBlock block, Address errorAddress, InternalTracePlatform platform, InstructionBlock block, Address errorAddress,
InstructionError conflict, CodeUnit conflictCodeUnit) { InstructionError conflict, CodeUnit conflictCodeUnit) {
this.skipDelaySlots = skipDelaySlots; this.skipDelaySlots = skipDelaySlots;
this.lifespan = lifespan; this.lifespan = lifespan;
@ -185,19 +185,11 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView
ctxSpace.setValue(language, newValue, tasr.getLifespan(), tasr.getRange()); 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<Long> lifespan, Address address, protected DBTraceInstruction doCreate(Range<Long> lifespan, Address address,
DBTraceGuestPlatform platform, InstructionPrototype prototype, InternalTracePlatform platform, InstructionPrototype prototype,
ProcessorContextView context) ProcessorContextView context)
throws CodeUnitInsertionException, AddressOverflowException { throws CodeUnitInsertionException, AddressOverflowException {
if (!languagesAgree(platform, prototype)) { if (platform.getLanguage() != prototype.getLanguage()) {
throw new IllegalArgumentException("Platform and prototype disagree in language"); throw new IllegalArgumentException("Platform and prototype disagree in language");
} }
@ -245,11 +237,10 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView
} }
@Override @Override
public DBTraceInstruction create(Range<Long> lifespan, Address address, public DBTraceInstruction create(Range<Long> lifespan, Address address, TracePlatform platform,
TraceGuestPlatform platform, InstructionPrototype prototype, InstructionPrototype prototype, ProcessorContextView context)
ProcessorContextView context)
throws CodeUnitInsertionException { throws CodeUnitInsertionException {
DBTraceGuestPlatform dbPlatform = space.manager.platformManager.assertMine(platform); InternalTracePlatform dbPlatform = space.manager.platformManager.assertMine(platform);
try (LockHold hold = LockHold.lock(space.lock.writeLock())) { try (LockHold hold = LockHold.lock(space.lock.writeLock())) {
DBTraceInstruction created = DBTraceInstruction created =
doCreate(lifespan, address, dbPlatform, prototype, context); doCreate(lifespan, address, dbPlatform, prototype, context);
@ -277,7 +268,7 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView
} }
protected InstructionBlockAdder startAddingBlock(Range<Long> lifespan, protected InstructionBlockAdder startAddingBlock(Range<Long> lifespan,
Set<Address> skipDelaySlots, DBTraceGuestPlatform platform, InstructionBlock block) { Set<Address> skipDelaySlots, InternalTracePlatform platform, InstructionBlock block) {
InstructionError conflict = block.getInstructionConflict(); InstructionError conflict = block.getInstructionConflict();
if (conflict == null) { if (conflict == null) {
return new InstructionBlockAdder(skipDelaySlots, lifespan, platform, block, null, null, return new InstructionBlockAdder(skipDelaySlots, lifespan, platform, block, null, null,
@ -384,9 +375,9 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView
} }
@Override @Override
public AddressSetView addInstructionSet(Range<Long> lifespan, TraceGuestPlatform platform, public AddressSetView addInstructionSet(Range<Long> lifespan, TracePlatform platform,
InstructionSet instructionSet, boolean overwrite) { InstructionSet instructionSet, boolean overwrite) {
DBTraceGuestPlatform dbPlatform = space.manager.platformManager.assertMine(platform); InternalTracePlatform dbPlatform = space.manager.platformManager.assertMine(platform);
// NOTE: Partly derived from CodeManager#addInstructions() // NOTE: Partly derived from CodeManager#addInstructions()
// Attempted to factor more fluently // Attempted to factor more fluently
AddressSet result = new AddressSet(); AddressSet result = new AddressSet();

View file

@ -33,7 +33,7 @@ import ghidra.trace.database.memory.DBTraceMemorySpace;
import ghidra.trace.database.space.DBTraceSpaceKey; import ghidra.trace.database.space.DBTraceSpaceKey;
import ghidra.trace.model.ImmutableTraceAddressSnapRange; import ghidra.trace.model.ImmutableTraceAddressSnapRange;
import ghidra.trace.model.TraceAddressSnapRange; 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.listing.TraceData;
import ghidra.trace.model.thread.TraceThread; import ghidra.trace.model.thread.TraceThread;
import ghidra.trace.util.TraceAddressSpace; import ghidra.trace.util.TraceAddressSpace;
@ -82,8 +82,8 @@ public class UndefinedDBTraceData implements DBTraceDataAdapter, DBTraceSpaceKey
} }
@Override @Override
public TraceGuestPlatform getGuestPlatform() { public TracePlatform getPlatform() {
return null; return trace.getPlatformManager().getHostPlatform();
} }
@Override @Override

View file

@ -37,6 +37,7 @@ import ghidra.program.model.symbol.SourceType;
import ghidra.program.model.util.CodeUnitInsertionException; import ghidra.program.model.util.CodeUnitInsertionException;
import ghidra.program.model.util.PropertyMap; import ghidra.program.model.util.PropertyMap;
import ghidra.trace.database.DBTrace; import ghidra.trace.database.DBTrace;
import ghidra.trace.database.guest.InternalTracePlatform;
import ghidra.trace.database.listing.UndefinedDBTraceData; import ghidra.trace.database.listing.UndefinedDBTraceData;
import ghidra.trace.database.memory.DBTraceMemorySpace; import ghidra.trace.database.memory.DBTraceMemorySpace;
import ghidra.trace.database.symbol.DBTraceFunctionSymbol; import ghidra.trace.database.symbol.DBTraceFunctionSymbol;
@ -77,6 +78,7 @@ public abstract class AbstractDBTraceProgramViewListing implements TraceProgramV
protected final DBTraceProgramView program; protected final DBTraceProgramView program;
protected final TraceCodeOperations codeOperations; protected final TraceCodeOperations codeOperations;
protected final InternalTracePlatform platform;
protected final DBTraceProgramViewRootModule rootModule; protected final DBTraceProgramViewRootModule rootModule;
protected final Map<TraceMemoryRegion, DBTraceProgramViewFragment> fragmentsByRegion = protected final Map<TraceMemoryRegion, DBTraceProgramViewFragment> fragmentsByRegion =
@ -94,6 +96,8 @@ public abstract class AbstractDBTraceProgramViewListing implements TraceProgramV
TraceCodeOperations codeOperations) { TraceCodeOperations codeOperations) {
this.program = program; this.program = program;
this.codeOperations = codeOperations; this.codeOperations = codeOperations;
// TODO: Guest platform views?
this.platform = program.trace.getPlatformManager().getHostPlatform();
this.rootModule = new DBTraceProgramViewRootModule(this); this.rootModule = new DBTraceProgramViewRootModule(this);
} }
@ -725,17 +729,15 @@ public abstract class AbstractDBTraceProgramViewListing implements TraceProgramV
public Instruction createInstruction(Address addr, InstructionPrototype prototype, public Instruction createInstruction(Address addr, InstructionPrototype prototype,
MemBuffer memBuf, ProcessorContextView context) throws CodeUnitInsertionException { MemBuffer memBuf, ProcessorContextView context) throws CodeUnitInsertionException {
// TODO: Why memBuf? Can it vary from program memory? // TODO: Why memBuf? Can it vary from program memory?
// TODO: Per-platform views?
return codeOperations.instructions() return codeOperations.instructions()
.create(Range.atLeast(program.snap), addr, null, prototype, context); .create(Range.atLeast(program.snap), addr, platform, prototype, context);
} }
@Override @Override
public AddressSetView addInstructions(InstructionSet instructionSet, boolean overwrite) public AddressSetView addInstructions(InstructionSet instructionSet, boolean overwrite)
throws CodeUnitInsertionException { throws CodeUnitInsertionException {
// TODO: Per-platform views?
return codeOperations.instructions() return codeOperations.instructions()
.addInstructionSet(Range.atLeast(program.snap), null, instructionSet, .addInstructionSet(Range.atLeast(program.snap), platform, instructionSet,
overwrite); overwrite);
} }

View file

@ -24,6 +24,7 @@ import com.google.common.collect.Range;
import db.DBHandle; import db.DBHandle;
import ghidra.trace.database.*; import ghidra.trace.database.*;
import ghidra.trace.database.target.DBTraceObject;
import ghidra.trace.database.target.DBTraceObjectManager; import ghidra.trace.database.target.DBTraceObjectManager;
import ghidra.trace.model.Trace.TraceThreadChangeType; import ghidra.trace.model.Trace.TraceThreadChangeType;
import ghidra.trace.model.thread.*; import ghidra.trace.model.thread.*;
@ -158,9 +159,8 @@ public class DBTraceThreadManager implements TraceThreadManager, DBTraceManager
@Override @Override
public TraceThread getThread(long key) { public TraceThread getThread(long key) {
if (objectManager.hasSchema()) { if (objectManager.hasSchema()) {
return objectManager DBTraceObject object = objectManager.getObjectById(key);
.getObjectById(key) return object == null ? null : object.queryInterface(TraceObjectThread.class);
.queryInterface(TraceObjectThread.class);
} }
return threadStore.getObjectAt(key); return threadStore.getObjectAt(key);
} }

View file

@ -15,46 +15,15 @@
*/ */
package ghidra.trace.model.guest; package ghidra.trace.model.guest;
import ghidra.program.model.address.*; import ghidra.program.model.address.Address;
import ghidra.program.model.lang.*; import ghidra.program.model.address.AddressOverflowException;
import ghidra.program.model.mem.MemBuffer;
import ghidra.trace.model.Trace;
import ghidra.util.exception.CancelledException; import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor; import ghidra.util.task.TaskMonitor;
public interface TraceGuestPlatform { public interface TraceGuestPlatform extends TracePlatform {
/**
* Get the trace
*
* @return the trace
*/
Trace getTrace();
/** /**
* Get the language of the guest platform * Add an address mapping from host to guest
*
* @return the language
*/
Language getLanguage();
/**
* Get the address factory of the guest platform
*
* @return the factory
*/
default AddressFactory getAddressFactory() {
return getLanguage().getAddressFactory();
}
/**
* Get the compiler of the guest platform
*
* @return the compiler spec
*/
CompilerSpec getCompilerSpec();
/**
* Add an adress mapping from host to guest
* *
* @param hostStart the starting host address (mapped to guestStart) * @param hostStart the starting host address (mapped to guestStart)
* @param guestStart the starting guest address (mapped to hostStart) * @param guestStart the starting guest address (mapped to hostStart)
@ -65,66 +34,6 @@ public interface TraceGuestPlatform {
TraceGuestPlatformMappedRange addMappedRange(Address hostStart, Address guestStart, long length) TraceGuestPlatformMappedRange addMappedRange(Address hostStart, Address guestStart, long length)
throws AddressOverflowException; 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
*
* <p>
* 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
*
* <p>
* 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 * Remove the mapped language, including all code units of the language
*/ */

View file

@ -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
*
* <p>
* 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
*
* <p>
* 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);
}

View file

@ -18,7 +18,6 @@ package ghidra.trace.model.guest;
import java.util.Collection; import java.util.Collection;
import ghidra.program.model.lang.CompilerSpec; import ghidra.program.model.lang.CompilerSpec;
import ghidra.program.model.lang.Language;
/** /**
* Allows the addition of "guest platforms" for disassembling in multiple languages. * Allows the addition of "guest platforms" for disassembling in multiple languages.
@ -28,18 +27,11 @@ import ghidra.program.model.lang.Language;
*/ */
public interface TracePlatformManager { 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(); TracePlatform getHostPlatform();
/**
* Get the base compiler spec of the trace
*
* @return the compiler spec
*/
CompilerSpec getBaseCompilerSpec();
/** /**
* Add a guest platform * Add a guest platform
@ -50,12 +42,12 @@ public interface TracePlatformManager {
TraceGuestPlatform addGuestPlatform(CompilerSpec compilerSpec); 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 * @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 * Get or add a platform for the given compiler spec

View file

@ -18,6 +18,7 @@ package ghidra.trace.model.listing;
import com.google.common.collect.Range; import com.google.common.collect.Range;
import ghidra.program.model.address.*; import ghidra.program.model.address.*;
import ghidra.trace.model.Trace;
import ghidra.trace.model.TraceAddressSnapRange; import ghidra.trace.model.TraceAddressSnapRange;
import ghidra.util.IntersectionAddressSetView; import ghidra.util.IntersectionAddressSetView;
import ghidra.util.UnionAddressSetView; import ghidra.util.UnionAddressSetView;
@ -30,6 +31,13 @@ import ghidra.util.UnionAddressSetView;
*/ */
public interface TraceBaseCodeUnitsView<T extends TraceCodeUnit> { public interface TraceBaseCodeUnitsView<T extends TraceCodeUnit> {
/**
* Get the trace for this view
*
* @return the trace
*/
Trace getTrace();
/** /**
* Get the total number of <em>defined</em> units in this view * Get the total number of <em>defined</em> units in this view
* *

View file

@ -25,7 +25,7 @@ import ghidra.program.model.listing.CodeUnit;
import ghidra.program.model.util.TypeMismatchException; import ghidra.program.model.util.TypeMismatchException;
import ghidra.trace.model.Trace; import ghidra.trace.model.Trace;
import ghidra.trace.model.TraceAddressSnapRange; 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.program.TraceProgramView;
import ghidra.trace.model.symbol.TraceReference; import ghidra.trace.model.symbol.TraceReference;
import ghidra.trace.model.thread.TraceThread; import ghidra.trace.model.thread.TraceThread;
@ -44,11 +44,11 @@ public interface TraceCodeUnit extends CodeUnit {
Trace getTrace(); 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 @Override
TraceProgramView getProgram(); TraceProgramView getProgram();

View file

@ -21,7 +21,7 @@ import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSetView; import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.lang.*; import ghidra.program.model.lang.*;
import ghidra.program.model.util.CodeUnitInsertionException; import ghidra.program.model.util.CodeUnitInsertionException;
import ghidra.trace.model.guest.TraceGuestPlatform; import ghidra.trace.model.guest.TracePlatform;
public interface TraceInstructionsView extends TraceBaseDefinedUnitsView<TraceInstruction> { public interface TraceInstructionsView extends TraceBaseDefinedUnitsView<TraceInstruction> {
/** /**
@ -29,16 +29,28 @@ public interface TraceInstructionsView extends TraceBaseDefinedUnitsView<TraceIn
* *
* @param lifespan the lifespan for the instruction unit * @param lifespan the lifespan for the instruction unit
* @param address the starting address of the instruction * @param address the starting address of the instruction
* @param platform the optional guest platform, null for the host * @param platform the platform
* @param prototype the instruction prototype * @param prototype the instruction prototype
* @param context the input disassembly context for the instruction * @param context the input disassembly context for the instruction
* @return the new instruction * @return the new instruction
* @throws CodeUnitInsertionException if the instruction cannot be created * @throws CodeUnitInsertionException if the instruction cannot be created
*/ */
TraceInstruction create(Range<Long> lifespan, Address address, TraceGuestPlatform platform, TraceInstruction create(Range<Long> lifespan, Address address, TracePlatform platform,
InstructionPrototype prototype, ProcessorContextView context) InstructionPrototype prototype, ProcessorContextView context)
throws CodeUnitInsertionException; throws CodeUnitInsertionException;
/**
* Create an instruction for the host platform
*
* @see #create(Range, Address, TracePlatform, InstructionPrototype, ProcessorContextView)
*/
default TraceInstruction create(Range<Long> lifespan, Address address,
InstructionPrototype prototype, ProcessorContextView context)
throws CodeUnitInsertionException {
return create(lifespan, address, getTrace().getPlatformManager().getHostPlatform(),
prototype, context);
}
/** /**
* Create several instructions * Create several instructions
* *
@ -52,6 +64,17 @@ public interface TraceInstructionsView extends TraceBaseDefinedUnitsView<TraceIn
* @param overwrite true to replace conflicting instructions * @param overwrite true to replace conflicting instructions
* @return the (host) address set of instructions actually added * @return the (host) address set of instructions actually added
*/ */
AddressSetView addInstructionSet(Range<Long> lifespan, TraceGuestPlatform platform, AddressSetView addInstructionSet(Range<Long> lifespan, TracePlatform platform,
InstructionSet instructionSet, boolean overwrite); InstructionSet instructionSet, boolean overwrite);
/**
* Create several instructions for the host platform
*
* @see #addInstructionSet(Range, TracePlatform, InstructionSet, boolean)
*/
default AddressSetView addInstructionSet(Range<Long> lifespan, InstructionSet instructionSet,
boolean overwrite) {
return addInstructionSet(lifespan, getTrace().getPlatformManager().getHostPlatform(),
instructionSet, overwrite);
}
} }

View file

@ -52,6 +52,7 @@ import ghidra.trace.database.symbol.DBTraceReference;
import ghidra.trace.database.thread.DBTraceThreadManager; import ghidra.trace.database.thread.DBTraceThreadManager;
import ghidra.trace.model.*; import ghidra.trace.model.*;
import ghidra.trace.model.guest.TraceGuestPlatform; import ghidra.trace.model.guest.TraceGuestPlatform;
import ghidra.trace.model.guest.TracePlatform;
import ghidra.trace.model.thread.TraceThread; import ghidra.trace.model.thread.TraceThread;
import ghidra.util.Msg; import ghidra.util.Msg;
import ghidra.util.database.DBOpenMode; import ghidra.util.database.DBOpenMode;
@ -62,6 +63,7 @@ import ghidra.util.task.ConsoleTaskMonitor;
public class ToyDBTraceBuilder implements AutoCloseable { public class ToyDBTraceBuilder implements AutoCloseable {
public final Language language; public final Language language;
public final DBTrace trace; public final DBTrace trace;
public final TracePlatform host;
public final LanguageService languageService = DefaultLanguageService.getLanguageService(); public final LanguageService languageService = DefaultLanguageService.getLanguageService();
public ToyDBTraceBuilder(File file) public ToyDBTraceBuilder(File file)
@ -69,17 +71,20 @@ public class ToyDBTraceBuilder implements AutoCloseable {
DBHandle handle = new DBHandle(file); DBHandle handle = new DBHandle(file);
this.trace = new DBTrace(handle, DBOpenMode.UPDATE, new ConsoleTaskMonitor(), this); this.trace = new DBTrace(handle, DBOpenMode.UPDATE, new ConsoleTaskMonitor(), this);
this.language = trace.getBaseLanguage(); this.language = trace.getBaseLanguage();
this.host = trace.getPlatformManager().getHostPlatform();
} }
// TODO: A constructor for specifying compiler, too // TODO: A constructor for specifying compiler, too
public ToyDBTraceBuilder(String name, String langID) throws IOException { public ToyDBTraceBuilder(String name, String langID) throws IOException {
this.language = languageService.getLanguage(new LanguageID(langID)); this.language = languageService.getLanguage(new LanguageID(langID));
this.trace = new DBTrace(name, language.getDefaultCompilerSpec(), this); this.trace = new DBTrace(name, language.getDefaultCompilerSpec(), this);
this.host = trace.getPlatformManager().getHostPlatform();
} }
public ToyDBTraceBuilder(Trace trace) { public ToyDBTraceBuilder(Trace trace) {
this.language = trace.getBaseLanguage(); this.language = trace.getBaseLanguage();
this.trace = (DBTrace) trace; this.trace = (DBTrace) trace;
this.host = trace.getPlatformManager().getHostPlatform();
trace.addConsumer(this); trace.addConsumer(this);
} }
@ -102,7 +107,7 @@ public class ToyDBTraceBuilder implements AutoCloseable {
return addr(language, offset); return addr(language, offset);
} }
public Address addr(TraceGuestPlatform lang, long offset) { public Address addr(TracePlatform lang, long offset) {
return lang.getLanguage().getDefaultSpace().getAddress(offset); return lang.getLanguage().getDefaultSpace().getAddress(offset);
} }
@ -246,36 +251,28 @@ public class ToyDBTraceBuilder implements AutoCloseable {
} }
public DBTraceInstruction addInstruction(long snap, Address start, public DBTraceInstruction addInstruction(long snap, Address start,
TraceGuestPlatform guest) throws CodeUnitInsertionException { TracePlatform platform) throws CodeUnitInsertionException {
DBTraceMemoryManager memory = trace.getMemoryManager();
DBTraceCodeManager code = trace.getCodeManager(); DBTraceCodeManager code = trace.getCodeManager();
Language language = guest == null ? this.language : guest.getLanguage(); Language language = platform.getLanguage();
Disassembler dis = Disassembler.getDisassembler(language, language.getAddressFactory(), Disassembler dis = Disassembler.getDisassembler(language, language.getAddressFactory(),
new ConsoleTaskMonitor(), msg -> Msg.info(this, "Listener: " + msg)); new ConsoleTaskMonitor(), msg -> Msg.info(this, "Listener: " + msg));
RegisterValue defaultContextValue = trace.getRegisterContextManager() RegisterValue defaultContextValue = trace.getRegisterContextManager()
.getDefaultContext(language) .getDefaultContext(language)
.getDefaultDisassemblyContext(); .getDefaultDisassemblyContext();
MemBuffer memBuf; MemBuffer memBuf = platform.getMappedMemBuffer(snap, platform.mapHostToGuest(start));
if (guest == null) {
memBuf = memory.getBufferAt(snap, start);
}
else {
memBuf = guest.getMappedMemBuffer(snap, guest.mapHostToGuest(start));
}
InstructionBlock block = dis.pseudoDisassembleBlock(memBuf, defaultContextValue, 1); InstructionBlock block = dis.pseudoDisassembleBlock(memBuf, defaultContextValue, 1);
Instruction pseudoIns = block.iterator().next(); Instruction pseudoIns = block.iterator().next();
return code.instructions() 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, public DBTraceInstruction addInstruction(long snap, Address start, TracePlatform platform,
TraceGuestPlatform guest, ByteBuffer buf) ByteBuffer buf) throws CodeUnitInsertionException {
throws CodeUnitInsertionException {
int length = buf.remaining(); int length = buf.remaining();
DBTraceMemoryManager memory = trace.getMemoryManager(); DBTraceMemoryManager memory = trace.getMemoryManager();
memory.putBytes(snap, start, buf); memory.putBytes(snap, start, buf);
DBTraceInstruction instruction = addInstruction(snap, start, guest); DBTraceInstruction instruction = addInstruction(snap, start, platform);
assertEquals(length, instruction.getLength()); assertEquals(length, instruction.getLength());
return instruction; return instruction;
} }

View file

@ -25,8 +25,8 @@ import org.junit.*;
import ghidra.test.AbstractGhidraHeadlessIntegrationTest; import ghidra.test.AbstractGhidraHeadlessIntegrationTest;
import ghidra.trace.database.ToyDBTraceBuilder; import ghidra.trace.database.ToyDBTraceBuilder;
import ghidra.trace.database.guest.*;
import ghidra.trace.model.guest.TraceGuestPlatform; import ghidra.trace.model.guest.TraceGuestPlatform;
import ghidra.trace.model.guest.TracePlatform;
import ghidra.util.database.UndoableTransaction; import ghidra.util.database.UndoableTransaction;
import ghidra.util.task.ConsoleTaskMonitor; import ghidra.util.task.ConsoleTaskMonitor;
@ -46,14 +46,10 @@ public class DBTracePlatformManagerTest extends AbstractGhidraHeadlessIntegratio
} }
@Test @Test
public void testGetBaseLanguage() { public void testGetHostPlatform() throws Throwable {
assertEquals("Toy:BE:64:default", TracePlatform host = b.trace.getPlatformManager().getHostPlatform();
manager.getBaseLanguage().getLanguageID().getIdAsString()); assertEquals("Toy:BE:64:default", host.getLanguage().getLanguageID().getIdAsString());
} assertEquals("default", host.getCompilerSpec().getCompilerSpecID().getIdAsString());
@Test
public void testGetBaseCompilerSpec() {
assertEquals("default", manager.getBaseCompilerSpec().getCompilerSpecID().getIdAsString());
} }
@Test @Test
@ -324,7 +320,8 @@ public class DBTracePlatformManagerTest extends AbstractGhidraHeadlessIntegratio
b.trace.undo(); 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.mapHostToGuest(b.addr(0x01000800)));
assertNotNull(guest.mapGuestToHost(b.addr(guest, 0x02000800))); assertNotNull(guest.mapGuestToHost(b.addr(guest, 0x02000800)));
} }
@ -359,7 +356,8 @@ public class DBTracePlatformManagerTest extends AbstractGhidraHeadlessIntegratio
b.trace.undo(); 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))); assertEquals(b.addr(guest, 0x02000800), guest.mapHostToGuest(b.addr(0x01000800)));
} }
} }

View file

@ -187,7 +187,7 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes
@Test @Test
public void testAddInstruction() throws CodeUnitInsertionException { public void testAddInstruction() throws CodeUnitInsertionException {
try (UndoableTransaction tid = b.startTransaction()) { 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()) { try (UndoableTransaction tid = b.startTransaction()) {
b.trace.getMemoryManager().putBytes(10, b.addr(0x4001), b.buf(0xaa)); b.trace.getMemoryManager().putBytes(10, b.addr(0x4001), b.buf(0xaa));
TraceInstruction i4000 = 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()); assertEquals(Range.closed(0L, 9L), i4000.getLifespan());
} }
} }
@ -206,15 +206,15 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes
try (UndoableTransaction tid = b.startTransaction()) { try (UndoableTransaction tid = b.startTransaction()) {
b.trace.getMemoryManager().putBytes(-5L, b.addr(0x4001), b.buf(0xaa)); b.trace.getMemoryManager().putBytes(-5L, b.addr(0x4001), b.buf(0xaa));
TraceInstruction i4000 = 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()); assertEquals(Range.closed(-10L, -6L), i4000.getLifespan());
TraceInstruction i4004 = 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()); assertEquals(Range.closed(-1L, -1L), i4004.getLifespan());
TraceInstruction i4008 = 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()); assertEquals(Range.closed(-10L, -1L), i4008.getLifespan());
} }
} }
@ -223,7 +223,7 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes
public void testPutBytesTruncatesInstruction() throws CodeUnitInsertionException { public void testPutBytesTruncatesInstruction() throws CodeUnitInsertionException {
try (UndoableTransaction tid = b.startTransaction()) { try (UndoableTransaction tid = b.startTransaction()) {
TraceInstruction i4000 = 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(b.addr(0x4001), i4000.getMaxAddress());
assertEquals(Range.atLeast(0L), i4000.getLifespan()); assertEquals(Range.atLeast(0L), i4000.getLifespan());
b.trace.getMemoryManager().putBytes(10, b.addr(0x4001), b.buf(1)); 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 { public void testPutBytesDeletesInstruction() throws CodeUnitInsertionException {
try (UndoableTransaction tid = b.startTransaction()) { try (UndoableTransaction tid = b.startTransaction()) {
TraceInstruction i4000 = 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(b.addr(0x4001), i4000.getMaxAddress());
assertEquals(Range.atLeast(0L), i4000.getLifespan()); assertEquals(Range.atLeast(0L), i4000.getLifespan());
b.trace.getMemoryManager().putBytes(0, b.addr(0x4001), b.buf(1)); b.trace.getMemoryManager().putBytes(0, b.addr(0x4001), b.buf(1));
@ -267,14 +267,14 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes
} }
try { try {
b.addInstruction(1, b.addr(0x4001), null); b.addInstruction(1, b.addr(0x4001), b.host);
fail(); fail();
} }
catch (CodeUnitInsertionException e) { catch (CodeUnitInsertionException e) {
// pass // 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 { try {
b.addData(1, b.addr(0x4005), ByteDataType.dataType, 1); b.addData(1, b.addr(0x4005), ByteDataType.dataType, 1);
@ -285,7 +285,7 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes
} }
try { try {
b.addInstruction(1, b.addr(0x4005), null); b.addInstruction(1, b.addr(0x4005), b.host);
} }
catch (CodeUnitInsertionException e) { catch (CodeUnitInsertionException e) {
// pass // pass
@ -350,7 +350,7 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes
assertAllNullFunc(v -> v.getAt(9, b.addr(0x4003))); assertAllNullFunc(v -> v.getAt(9, b.addr(0x4003)));
assertUndefinedFunc(v -> v.getAt(9, b.addr(0x4004))); 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); i4005.setEndSnap(5);
assertUndefinedFunc(v -> v.getAt(0, b.addr(0x4004))); assertUndefinedFunc(v -> v.getAt(0, b.addr(0x4004)));
assertInstructionFunc(i4005, v -> v.getAt(0, b.addr(0x4005))); 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))); assertDataFunc(d4000, v -> v.getContaining(9, b.addr(0x4003)));
assertUndefinedFunc(v -> v.getContaining(9, b.addr(0x4004))); 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); i4005.setEndSnap(5);
assertUndefinedFunc(v -> v.getContaining(0, b.addr(0x4004))); assertUndefinedFunc(v -> v.getContaining(0, b.addr(0x4004)));
@ -449,7 +449,7 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes
d4000.setEndSnap(9); d4000.setEndSnap(9);
d4004 = b.addData(0, b.addr(0x4004), IntegerDataType.dataType, b.buf(5, 6, 7, 8)); d4004 = b.addData(0, b.addr(0x4004), IntegerDataType.dataType, b.buf(5, 6, 7, 8));
d4004.setEndSnap(5); 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); i4008.setEndSnap(9);
} }
@ -591,7 +591,7 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes
d4000.setEndSnap(9); d4000.setEndSnap(9);
d4004 = b.addData(0, b.addr(0x4004), IntegerDataType.dataType, b.buf(5, 6, 7, 8)); d4004 = b.addData(0, b.addr(0x4004), IntegerDataType.dataType, b.buf(5, 6, 7, 8));
d4004.setEndSnap(5); 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); i4008.setEndSnap(9);
} }
@ -730,7 +730,7 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes
d4000.setEndSnap(9); d4000.setEndSnap(9);
d4004 = b.addData(0, b.addr(0x4004), IntegerDataType.dataType, b.buf(5, 6, 7, 8)); d4004 = b.addData(0, b.addr(0x4004), IntegerDataType.dataType, b.buf(5, 6, 7, 8));
d4004.setEndSnap(5); 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); i4008.setEndSnap(9);
} }
@ -870,7 +870,7 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes
d4000.setEndSnap(9); d4000.setEndSnap(9);
d4004 = b.addData(0, b.addr(0x4004), IntegerDataType.dataType, b.buf(5, 6, 7, 8)); d4004 = b.addData(0, b.addr(0x4004), IntegerDataType.dataType, b.buf(5, 6, 7, 8));
d4004.setEndSnap(5); 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); i4008.setEndSnap(9);
} }
@ -1084,7 +1084,7 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes
d4000.setEndSnap(9); d4000.setEndSnap(9);
d4004 = b.addData(0, b.addr(0x4004), IntegerDataType.dataType, b.buf(5, 6, 7, 8)); d4004 = b.addData(0, b.addr(0x4004), IntegerDataType.dataType, b.buf(5, 6, 7, 8));
d4004.setEndSnap(5); 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); i4008.setEndSnap(9);
} }
TraceData u3fff = manager.undefinedData().getAt(0, b.addr(0x3fff)); TraceData u3fff = manager.undefinedData().getAt(0, b.addr(0x3fff));
@ -1137,7 +1137,7 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes
TraceInstruction iCodeMax; TraceInstruction iCodeMax;
try (UndoableTransaction tid = b.startTransaction()) { 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))); 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))); manager.undefinedData().getFloor(0, b.data(0x0003)));
try (UndoableTransaction tid = b.startTransaction()) { 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)); TraceData uCodePre = manager.undefinedData().getAt(0, b.addr(-0x0003));
assertUndefinedWithAddr(b.addr(-0x0003), uCodePre); assertUndefinedWithAddr(b.addr(-0x0003), uCodePre);
@ -1209,7 +1209,7 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes
try (UndoableTransaction tid = b.startTransaction()) { try (UndoableTransaction tid = b.startTransaction()) {
d4000 = b.addData(0, b.addr(0x4000), IntegerDataType.dataType, b.buf(1, 2, 3, 4)); d4000 = b.addData(0, b.addr(0x4000), IntegerDataType.dataType, b.buf(1, 2, 3, 4));
d4000.setEndSnap(9); 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); i4008.setEndSnap(9);
} }
@ -1344,7 +1344,7 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes
d4000.setEndSnap(9); d4000.setEndSnap(9);
d4004 = b.addData(0, b.addr(0x4004), IntegerDataType.dataType, b.buf(5, 6, 7, 8)); d4004 = b.addData(0, b.addr(0x4004), IntegerDataType.dataType, b.buf(5, 6, 7, 8));
d4004.setEndSnap(5); 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); i4008.setEndSnap(9);
} }
@ -1414,7 +1414,7 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes
d4000.setEndSnap(9); d4000.setEndSnap(9);
d4004 = b.addData(0, b.addr(0x4004), IntegerDataType.dataType, b.buf(5, 6, 7, 8)); d4004 = b.addData(0, b.addr(0x4004), IntegerDataType.dataType, b.buf(5, 6, 7, 8));
d4004.setEndSnap(5); 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); i4008.setEndSnap(9);
} }
@ -1489,7 +1489,7 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes
d4000.setEndSnap(9); d4000.setEndSnap(9);
d4004 = b.addData(0, b.addr(0x4004), IntegerDataType.dataType, b.buf(5, 6, 7, 8)); d4004 = b.addData(0, b.addr(0x4004), IntegerDataType.dataType, b.buf(5, 6, 7, 8));
d4004.setEndSnap(5); 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); i4008.setEndSnap(9);
} }
@ -1573,7 +1573,7 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes
d4000.setEndSnap(9); d4000.setEndSnap(9);
d4004 = b.addData(0, b.addr(0x4004), IntegerDataType.dataType, b.buf(5, 6, 7, 8)); d4004 = b.addData(0, b.addr(0x4004), IntegerDataType.dataType, b.buf(5, 6, 7, 8));
d4004.setEndSnap(5); 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); i4008.setEndSnap(9);
} }
@ -1672,7 +1672,7 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes
d4000.setEndSnap(9); d4000.setEndSnap(9);
d4004 = b.addData(0, b.addr(0x4004), IntegerDataType.dataType, b.buf(5, 6, 7, 8)); d4004 = b.addData(0, b.addr(0x4004), IntegerDataType.dataType, b.buf(5, 6, 7, 8));
d4004.setEndSnap(5); 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); i4008.setEndSnap(9);
// Clear one of the data before a context space is created // 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()); guest = langMan.addGuestPlatform(x86.getDefaultCompilerSpec());
mappedRange = guest.addMappedRange(b.addr(0x0000), b.addr(guest, 0x0000), 1L << 32); mappedRange = guest.addMappedRange(b.addr(0x0000), b.addr(guest, 0x0000), 1L << 32);
g4000 = b.addInstruction(0, b.addr(0x4000), guest, b.buf(0x90)); 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)); 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? // TODO: Related to GP-479?
g4000 = manager.instructions().getAt(0, b.addr(0x4000)); g4000 = manager.instructions().getAt(0, b.addr(0x4000));
assertNotNull(g4000); assertNotNull(g4000);
assertEquals(guest, g4000.getGuestPlatform()); assertEquals(guest, g4000.getPlatform());
try (UndoableTransaction tid = b.startTransaction()) { try (UndoableTransaction tid = b.startTransaction()) {
guest.delete(new ConsoleTaskMonitor()); guest.delete(new ConsoleTaskMonitor());
} }
@ -1753,7 +1753,7 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes
@Test @Test
public void testSaveAndLoad() throws Exception { public void testSaveAndLoad() throws Exception {
try (UndoableTransaction tid = b.startTransaction()) { 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); TraceThread thread = b.getOrAddThread("Thread 1", 0);
DBTraceCodeRegisterSpace regCode = manager.getCodeRegisterSpace(thread, true); DBTraceCodeRegisterSpace regCode = manager.getCodeRegisterSpace(thread, true);
@ -1801,7 +1801,7 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes
@Test @Test
public void testUndoThenRedo() throws Exception { public void testUndoThenRedo() throws Exception {
try (UndoableTransaction tid = b.startTransaction()) { 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); TraceThread thread = b.getOrAddThread("Thread 1", 0);
DBTraceCodeRegisterSpace regCode = manager.getCodeRegisterSpace(thread, true); DBTraceCodeRegisterSpace regCode = manager.getCodeRegisterSpace(thread, true);
@ -1854,7 +1854,7 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes
b.trace.getBaseAddressFactory().getDefaultAddressSpace()); b.trace.getBaseAddressFactory().getDefaultAddressSpace());
DBTraceCodeSpace space = manager.getCodeSpace(os, true); 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<CodeUnit> all = new ArrayList<>(); List<CodeUnit> all = new ArrayList<>();
space.definedUnits().get(0, true).forEach(all::add); space.definedUnits().get(0, true).forEach(all::add);

View file

@ -242,7 +242,7 @@ public class DBTraceCodeUnitTest extends AbstractGhidraHeadlessIntegrationTest
TraceOverlappedRegionException, DuplicateNameException { TraceOverlappedRegionException, DuplicateNameException {
TraceInstruction ins; TraceInstruction ins;
try (UndoableTransaction tid = b.startTransaction()) { 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)); TraceData und = manager.undefinedData().getAt(0, b.addr(0x4006));
@ -281,7 +281,7 @@ public class DBTraceCodeUnitTest extends AbstractGhidraHeadlessIntegrationTest
public void testGetProgram() throws CodeUnitInsertionException { public void testGetProgram() throws CodeUnitInsertionException {
TraceInstruction i4004; TraceInstruction i4004;
try (UndoableTransaction tid = b.startTransaction()) { 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()); assertEquals(0, i4004.getProgram().getSnap());
@ -291,7 +291,7 @@ public class DBTraceCodeUnitTest extends AbstractGhidraHeadlessIntegrationTest
public void testGetMemory() throws CodeUnitInsertionException { public void testGetMemory() throws CodeUnitInsertionException {
TraceInstruction i4004; TraceInstruction i4004;
try (UndoableTransaction tid = b.startTransaction()) { 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()); assertEquals(i4004.getProgram().getMemory(), i4004.getMemory());
@ -304,7 +304,7 @@ public class DBTraceCodeUnitTest extends AbstractGhidraHeadlessIntegrationTest
TraceInstruction i4004; TraceInstruction i4004;
TraceInstruction g4006; TraceInstruction g4006;
try (UndoableTransaction tid = b.startTransaction()) { 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 = b.trace.getPlatformManager().addGuestPlatform(x86.getDefaultCompilerSpec());
guest.addMappedRange(b.addr(0x0000), b.addr(guest, 0x0000), 1L << 32); guest.addMappedRange(b.addr(0x0000), b.addr(guest, 0x0000), 1L << 32);
g4006 = b.addInstruction(0, b.addr(0x4006), guest, b.buf(0x90)); g4006 = b.addInstruction(0, b.addr(0x4006), guest, b.buf(0x90));
@ -319,8 +319,8 @@ public class DBTraceCodeUnitTest extends AbstractGhidraHeadlessIntegrationTest
TraceInstruction i4004; TraceInstruction i4004;
TraceInstruction i4006; TraceInstruction i4006;
try (UndoableTransaction tid = b.startTransaction()) { 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));
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));
} }
assertFalse(i4004.hasProperty("myVoid")); assertFalse(i4004.hasProperty("myVoid"));
@ -455,8 +455,8 @@ public class DBTraceCodeUnitTest extends AbstractGhidraHeadlessIntegrationTest
TraceInstruction i4004; TraceInstruction i4004;
TraceInstruction i4006; TraceInstruction i4006;
try (UndoableTransaction tid = b.startTransaction()) { 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));
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));
} }
try (UndoableTransaction tid = b.startTransaction()) { 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 // TODO: Decide whether or not to shrink the comment lifespan with the unit lifespan
assertEquals(Range.atLeast(0L), c4004.getLifespan()); 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.PRE_COMMENT, "Get this back in the mix");
i4004_10.setComment(CodeUnit.EOL_COMMENT, "A different comment"); i4004_10.setComment(CodeUnit.EOL_COMMENT, "A different comment");
} }
@ -538,8 +538,8 @@ public class DBTraceCodeUnitTest extends AbstractGhidraHeadlessIntegrationTest
TraceInstruction i4006; TraceInstruction i4006;
TraceData d4008; TraceData d4008;
try (UndoableTransaction tid = b.startTransaction()) { 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));
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));
d4008 = b.addData(0, b.addr(0x4008), LongDataType.dataType, b.buf(1, 2, 3, 4)); 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; TraceInstruction i4006;
try (UndoableTransaction tid = b.startTransaction()) { try (UndoableTransaction tid = b.startTransaction()) {
d4000 = b.addData(0, b.addr(0x4000), LongDataType.dataType, b.buf(1, 2, 3, 4)); 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));
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));
} }
Set<TraceReference> refs; Set<TraceReference> refs;
@ -708,7 +708,7 @@ public class DBTraceCodeUnitTest extends AbstractGhidraHeadlessIntegrationTest
TraceData undefined; TraceData undefined;
TraceData undReg; TraceData undReg;
try (UndoableTransaction tid = b.startTransaction()) { 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)); undefined = manager.undefinedData().getAt(0, b.addr(0x4006));
thread = b.getOrAddThread("Thread 1", 0); thread = b.getOrAddThread("Thread 1", 0);
@ -739,7 +739,7 @@ public class DBTraceCodeUnitTest extends AbstractGhidraHeadlessIntegrationTest
TraceInstruction i4004; TraceInstruction i4004;
try (UndoableTransaction tid = b.startTransaction()) { try (UndoableTransaction tid = b.startTransaction()) {
d4000 = b.addData(0, b.addr(0x4000), LongDataType.dataType, b.buf(1, 2, 3, 4)); 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); d4000.setEndSnap(9);
assertEquals(Range.closed(0L, 9L), d4000.getLifespan()); assertEquals(Range.closed(0L, 9L), d4000.getLifespan());
@ -1039,13 +1039,13 @@ public class DBTraceCodeUnitTest extends AbstractGhidraHeadlessIntegrationTest
.addRegion("myRegion", Range.atLeast(0L), .addRegion("myRegion", Range.atLeast(0L),
b.range(0x4000, 0x4fff), TraceMemoryFlag.READ); 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()); 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()); 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()); 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()); assertEquals("call r4", i400a.toString());
} }
@ -1191,7 +1191,7 @@ public class DBTraceCodeUnitTest extends AbstractGhidraHeadlessIntegrationTest
TraceInstruction i4004; TraceInstruction i4004;
try (UndoableTransaction tid = b.startTransaction()) { 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 // TODO: Test with non-default context
@ -1240,7 +1240,7 @@ public class DBTraceCodeUnitTest extends AbstractGhidraHeadlessIntegrationTest
TraceData d4006; TraceData d4006;
try (UndoableTransaction tid = b.startTransaction()) { try (UndoableTransaction tid = b.startTransaction()) {
d4000 = b.addData(0, b.addr(0x4000), LongDataType.dataType, b.buf(1, 2, 3, 4)); 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, d4006 = b.addData(0, b.addr(0x4006), PointerDataType.dataType,
b.buf(0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00)); b.buf(0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00));
} }
@ -1260,7 +1260,7 @@ public class DBTraceCodeUnitTest extends AbstractGhidraHeadlessIntegrationTest
TraceData d4006; TraceData d4006;
try (UndoableTransaction tid = b.startTransaction()) { try (UndoableTransaction tid = b.startTransaction()) {
d4000 = b.addData(0, b.addr(0x4000), LongDataType.dataType, b.buf(1, 2, 3, 4)); 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, d4006 = b.addData(0, b.addr(0x4006), PointerDataType.dataType,
b.buf(0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00)); b.buf(0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00));
} }
@ -1289,7 +1289,7 @@ public class DBTraceCodeUnitTest extends AbstractGhidraHeadlessIntegrationTest
TraceInstruction i4004; TraceInstruction i4004;
TraceInstruction g4006; TraceInstruction g4006;
try (UndoableTransaction tid = b.startTransaction()) { 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 = b.trace.getPlatformManager().addGuestPlatform(x86.getDefaultCompilerSpec());
guest.addMappedRange(b.addr(0x0000), b.addr(guest, 0x0000), 1L << 32); guest.addMappedRange(b.addr(0x0000), b.addr(guest, 0x0000), 1L << 32);
g4006 = b.addInstruction(0, b.addr(0x4006), guest, b.buf(0x90)); 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); 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)); 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)); 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)); TraceData u4009 = manager.undefinedData().getAt(0, b.addr(0x4009));

View file

@ -114,7 +114,7 @@ public class DBTraceProgramViewListingTest extends AbstractGhidraHeadlessIntegra
CodeUnitInsertionException { CodeUnitInsertionException {
Instruction ins; Instruction ins;
try (UndoableTransaction tid = b.startTransaction()) { 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()); assertEquals("ret", ins.toString());
} }
@ -138,7 +138,7 @@ public class DBTraceProgramViewListingTest extends AbstractGhidraHeadlessIntegra
Instruction i4005; Instruction i4005;
try (UndoableTransaction tid = b.startTransaction()) { 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))); assertEquals(i4005, listing.getCodeUnitAt(b.addr(0x4005)));
assertNull(listing.getCodeUnitAt(b.addr(0x4006))); assertNull(listing.getCodeUnitAt(b.addr(0x4006)));
@ -163,7 +163,7 @@ public class DBTraceProgramViewListingTest extends AbstractGhidraHeadlessIntegra
assertUndefined(listing.getCodeUnitContaining(b.addr(0x4005))); assertUndefined(listing.getCodeUnitContaining(b.addr(0x4005)));
Instruction i4005; Instruction i4005;
try (UndoableTransaction tid = b.startTransaction()) { 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(0x4005)));
assertEquals(i4005, listing.getCodeUnitContaining(b.addr(0x4006))); assertEquals(i4005, listing.getCodeUnitContaining(b.addr(0x4006)));
@ -189,7 +189,7 @@ public class DBTraceProgramViewListingTest extends AbstractGhidraHeadlessIntegra
assertEquals(b.addr(0x4005), cu.getAddress()); assertEquals(b.addr(0x4005), cu.getAddress());
Instruction i4005; Instruction i4005;
try (UndoableTransaction tid = b.startTransaction()) { 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))); assertEquals(i4005, listing.getCodeUnitAfter(b.addr(0x4004)));
assertUndefined(cu = listing.getCodeUnitAfter(b.addr(0x4005))); assertUndefined(cu = listing.getCodeUnitAfter(b.addr(0x4005)));
@ -215,7 +215,7 @@ public class DBTraceProgramViewListingTest extends AbstractGhidraHeadlessIntegra
assertEquals(b.addr(0x4005), cu.getAddress()); assertEquals(b.addr(0x4005), cu.getAddress());
Instruction i4005; Instruction i4005;
try (UndoableTransaction tid = b.startTransaction()) { 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))); assertEquals(i4005, listing.getCodeUnitBefore(b.addr(0x4006)));
assertUndefined(cu = listing.getCodeUnitBefore(b.addr(0x4005))); assertUndefined(cu = listing.getCodeUnitBefore(b.addr(0x4005)));
@ -291,7 +291,7 @@ public class DBTraceProgramViewListingTest extends AbstractGhidraHeadlessIntegra
Instruction i4005; Instruction i4005;
try (UndoableTransaction tid = b.startTransaction()) { try (UndoableTransaction tid = b.startTransaction()) {
d4000 = b.addData(0, b.addr(0x4000), Undefined4DataType.dataType, b.buf(1, 2, 3, 4)); 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)); sample = takeN(10, listing.getCodeUnits(true));
@ -355,7 +355,7 @@ public class DBTraceProgramViewListingTest extends AbstractGhidraHeadlessIntegra
assertNull(listing.getInstructionAt(b.addr(0x4005))); assertNull(listing.getInstructionAt(b.addr(0x4005)));
Instruction i4005; Instruction i4005;
try (UndoableTransaction tid = b.startTransaction()) { 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))); assertEquals(i4005, listing.getInstructionAt(b.addr(0x4005)));
assertNull(listing.getInstructionAt(b.addr(0x4006))); assertNull(listing.getInstructionAt(b.addr(0x4006)));
@ -368,7 +368,7 @@ public class DBTraceProgramViewListingTest extends AbstractGhidraHeadlessIntegra
assertNull(listing.getInstructionContaining(b.addr(0x4005))); assertNull(listing.getInstructionContaining(b.addr(0x4005)));
Instruction i4005; Instruction i4005;
try (UndoableTransaction tid = b.startTransaction()) { 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(0x4005)));
assertEquals(i4005, listing.getInstructionContaining(b.addr(0x4006))); assertEquals(i4005, listing.getInstructionContaining(b.addr(0x4006)));
@ -381,7 +381,7 @@ public class DBTraceProgramViewListingTest extends AbstractGhidraHeadlessIntegra
assertNull(listing.getInstructionAfter(b.addr(0x4004))); assertNull(listing.getInstructionAfter(b.addr(0x4004)));
Instruction i4005; Instruction i4005;
try (UndoableTransaction tid = b.startTransaction()) { 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))); assertEquals(i4005, listing.getInstructionAfter(b.addr(0x4004)));
assertNull(listing.getInstructionAfter(b.addr(0x4005))); assertNull(listing.getInstructionAfter(b.addr(0x4005)));
@ -393,7 +393,7 @@ public class DBTraceProgramViewListingTest extends AbstractGhidraHeadlessIntegra
assertNull(listing.getInstructionBefore(b.addr(0x4006))); assertNull(listing.getInstructionBefore(b.addr(0x4006)));
Instruction i4005; Instruction i4005;
try (UndoableTransaction tid = b.startTransaction()) { 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))); assertEquals(i4005, listing.getInstructionBefore(b.addr(0x4006)));
assertNull(listing.getInstructionBefore(b.addr(0x4005))); assertNull(listing.getInstructionBefore(b.addr(0x4005)));
@ -421,9 +421,9 @@ public class DBTraceProgramViewListingTest extends AbstractGhidraHeadlessIntegra
Instruction i4007; Instruction i4007;
Instruction i400a; Instruction i400a;
try (UndoableTransaction tid = b.startTransaction()) { 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));
i4007 = b.addInstruction(0, b.addr(0x4007), null, b.buf(0xf4, 0)); i4007 = b.addInstruction(0, b.addr(0x4007), b.host, b.buf(0xf4, 0));
i400a = b.addInstruction(0, b.addr(0x400a), null, 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)); 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))); assertUndefined(listing.getDataAt(b.addr(0x4005)));
try (UndoableTransaction tid = b.startTransaction()) { 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(0x4005)));
assertNull(listing.getDataAt(b.addr(0x4006))); assertNull(listing.getDataAt(b.addr(0x4006)));
@ -477,7 +477,7 @@ public class DBTraceProgramViewListingTest extends AbstractGhidraHeadlessIntegra
assertUndefined(listing.getDataContaining(b.addr(0x4005))); assertUndefined(listing.getDataContaining(b.addr(0x4005)));
try (UndoableTransaction tid = b.startTransaction()) { 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(0x4005)));
assertNull(listing.getDataContaining(b.addr(0x4006))); assertNull(listing.getDataContaining(b.addr(0x4006)));
@ -502,7 +502,7 @@ public class DBTraceProgramViewListingTest extends AbstractGhidraHeadlessIntegra
assertUndefined(cu = listing.getDataAfter(b.addr(0x4004))); assertUndefined(cu = listing.getDataAfter(b.addr(0x4004)));
assertEquals(b.addr(0x4005), cu.getAddress()); assertEquals(b.addr(0x4005), cu.getAddress());
try (UndoableTransaction tid = b.startTransaction()) { 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))); assertUndefined(cu = listing.getDataAfter(b.addr(0x4004)));
assertEquals(b.addr(0x4007), cu.getAddress()); assertEquals(b.addr(0x4007), cu.getAddress());
@ -526,7 +526,7 @@ public class DBTraceProgramViewListingTest extends AbstractGhidraHeadlessIntegra
assertUndefined(cu = listing.getDataBefore(b.addr(0x4006))); assertUndefined(cu = listing.getDataBefore(b.addr(0x4006)));
assertEquals(b.addr(0x4005), cu.getAddress()); assertEquals(b.addr(0x4005), cu.getAddress());
try (UndoableTransaction tid = b.startTransaction()) { 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))); assertUndefined(cu = listing.getDataBefore(b.addr(0x4007)));
assertEquals(b.addr(0x4004), cu.getAddress()); assertEquals(b.addr(0x4004), cu.getAddress());
@ -600,7 +600,7 @@ public class DBTraceProgramViewListingTest extends AbstractGhidraHeadlessIntegra
Data d4000; Data d4000;
try (UndoableTransaction tid = b.startTransaction()) { try (UndoableTransaction tid = b.startTransaction()) {
d4000 = b.addData(0, b.addr(0x4000), Undefined4DataType.dataType, b.buf(1, 2, 3, 4)); 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)); 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)); d4004 = b.addData(0, b.addr(0x4004), Undefined4DataType.dataType, b.buf(5, 6, 7, 8));
d400a = d400a =
b.addData(0, b.addr(0x400a), Undefined4DataType.dataType, b.buf(10, 11, 12, 13)); 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))); 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))); assertUndefined(listing.getUndefinedDataAt(b.addr(0x4005)));
try (UndoableTransaction tid = b.startTransaction()) { 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(0x4005)));
assertNull(listing.getUndefinedDataAt(b.addr(0x4006))); assertNull(listing.getUndefinedDataAt(b.addr(0x4006)));
@ -783,7 +783,7 @@ public class DBTraceProgramViewListingTest extends AbstractGhidraHeadlessIntegra
assertUndefined(cu = listing.getUndefinedDataAfter(b.addr(0x4004), TaskMonitor.DUMMY)); assertUndefined(cu = listing.getUndefinedDataAfter(b.addr(0x4004), TaskMonitor.DUMMY));
assertEquals(b.addr(0x4005), cu.getAddress()); assertEquals(b.addr(0x4005), cu.getAddress());
try (UndoableTransaction tid = b.startTransaction()) { 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)); assertUndefined(cu = listing.getUndefinedDataAfter(b.addr(0x4004), TaskMonitor.DUMMY));
assertEquals(b.addr(0x4007), cu.getAddress()); assertEquals(b.addr(0x4007), cu.getAddress());
@ -805,7 +805,7 @@ public class DBTraceProgramViewListingTest extends AbstractGhidraHeadlessIntegra
assertUndefined(cu = listing.getUndefinedDataBefore(b.addr(0x4006), TaskMonitor.DUMMY)); assertUndefined(cu = listing.getUndefinedDataBefore(b.addr(0x4006), TaskMonitor.DUMMY));
assertEquals(b.addr(0x4005), cu.getAddress()); assertEquals(b.addr(0x4005), cu.getAddress());
try (UndoableTransaction tid = b.startTransaction()) { 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)); assertUndefined(cu = listing.getUndefinedDataBefore(b.addr(0x4007), TaskMonitor.DUMMY));
assertEquals(b.addr(0x4004), cu.getAddress()); assertEquals(b.addr(0x4004), cu.getAddress());
@ -816,7 +816,7 @@ public class DBTraceProgramViewListingTest extends AbstractGhidraHeadlessIntegra
UnknownInstructionException, CodeUnitInsertionException { UnknownInstructionException, CodeUnitInsertionException {
try (UndoableTransaction tid = b.startTransaction()) { try (UndoableTransaction tid = b.startTransaction()) {
b.addData(0, b.addr(0x4000), Undefined4DataType.dataType, b.buf(1, 2, 3, 4)); 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; Data cu;
@ -834,7 +834,7 @@ public class DBTraceProgramViewListingTest extends AbstractGhidraHeadlessIntegra
UnknownInstructionException, CodeUnitInsertionException, CancelledException { UnknownInstructionException, CodeUnitInsertionException, CancelledException {
try (UndoableTransaction tid = b.startTransaction()) { try (UndoableTransaction tid = b.startTransaction()) {
b.addData(0, b.addr(0x4000), Undefined4DataType.dataType, b.buf(1, 2, 3, 4)); 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? 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))); assertNull(listing.getDefinedCodeUnitAfter(b.addr(0x3fff)));
Instruction i4005; Instruction i4005;
try (UndoableTransaction tid = b.startTransaction()) { 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))); assertEquals(i4005, listing.getDefinedCodeUnitAfter(b.addr(0x3fff)));
assertNull(listing.getDefinedCodeUnitAfter(b.addr(0x4005))); assertNull(listing.getDefinedCodeUnitAfter(b.addr(0x4005)));
@ -873,7 +873,7 @@ public class DBTraceProgramViewListingTest extends AbstractGhidraHeadlessIntegra
assertNull(listing.getDefinedCodeUnitBefore(b.addr(0x4000))); assertNull(listing.getDefinedCodeUnitBefore(b.addr(0x4000)));
Instruction i4005; Instruction i4005;
try (UndoableTransaction tid = b.startTransaction()) { 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(i4005, listing.getDefinedCodeUnitBefore(b.addr(0x4006)));
assertEquals(d4000, listing.getDefinedCodeUnitBefore(b.addr(0x4005))); assertEquals(d4000, listing.getDefinedCodeUnitBefore(b.addr(0x4005)));

View file

@ -15,7 +15,7 @@
*/ */
package generic; package generic;
import java.util.Iterator; import java.util.*;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
@ -39,7 +39,12 @@ public interface Unique {
} }
T result = it.next(); T result = it.next();
if (it.hasNext()) { if (it.hasNext()) {
throw new AssertionError("Expected exactly one. Got many."); List<T> all = new ArrayList<>();
all.add(result);
while (it.hasNext()) {
all.add(it.next());
}
throw new AssertionError("Expected exactly one. Got many: " + all);
} }
return result; return result;
} }

View file

@ -24,9 +24,9 @@ import ghidra.program.util.ProgramSelection;
/** /**
* Interface for ComponentProviders to implement if they support basic navigation and selection * Interface for ComponentProviders to implement if they support basic navigation and selection
* capabilities. Implementing this interface will provide the provider with navigation history * capabilities. Implementing this interface will provide the provider with navigation history and
* and actions that require navigation or selection. (Search Text, Search Memory, Select bytes, * actions that require navigation or selection. (Search Text, Search Memory, Select bytes, Select
* Select instructions, etc.) * instructions, etc.)
*/ */
public interface Navigatable { public interface Navigatable {
public static final long DEFAULT_NAVIGATABLE_ID = -1; 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 * Commands this navigatable to goto (display) the given program and location
*
* @param program the program * @param program the program
* *
* @param location the location in that program to display * @param location the location in that program to display
@ -44,18 +45,21 @@ public interface Navigatable {
/** /**
* Returns the current location of this Navigatable * Returns the current location of this Navigatable
*
* @return the current location of this Navigatable * @return the current location of this Navigatable
*/ */
public ProgramLocation getLocation(); public ProgramLocation getLocation();
/** /**
* Returns the current Program of this Navigatable * Returns the current Program of this Navigatable
*
* @return the current Program of this Navigatable * @return the current Program of this Navigatable
*/ */
public Program getProgram(); public Program getProgram();
/** /**
* Returns the view state for this navigatable * Returns the view state for this navigatable
*
* @return the view state for this navigatable * @return the view state for this navigatable
*/ */
public LocationMemento getMemento(); public LocationMemento getMemento();
@ -69,20 +73,32 @@ public interface Navigatable {
/** /**
* Returns an icon that represents this Navigatable * Returns an icon that represents this Navigatable
*
* @return the icon * @return the icon
*/ */
public Icon getNavigatableIcon(); public Icon getNavigatableIcon();
/** /**
* Returns true if this Navigatable is "connected". Navigatables are connected if they * Returns true if this Navigatable is "connected". Navigatables are connected if they produce
* produce and consume location and selection events. * and consume location and selection events.
* *
* @return true if this Navigatable is "connected" * @return true if this Navigatable is "connected"
*/ */
public boolean isConnected(); 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 * Currently only the 'connected' windows support markers
*
* @return true if this navigatable supports markers * @return true if this navigatable supports markers
*/ */
public boolean supportsMarkers(); public boolean supportsMarkers();
@ -94,12 +110,14 @@ public interface Navigatable {
/** /**
* Returns true if this provider is visible * Returns true if this provider is visible
*
* @return true if visible * @return true if visible
*/ */
public boolean isVisible(); public boolean isVisible();
/** /**
* Tells this Navigatable to set its selection to the given selection * Tells this Navigatable to set its selection to the given selection
*
* @param selection the selection to set. * @param selection the selection to set.
*/ */
public void setSelection(ProgramSelection selection); public void setSelection(ProgramSelection selection);
@ -113,30 +131,35 @@ public interface Navigatable {
/** /**
* Returns the current selection of this Navigatable * Returns the current selection of this Navigatable
*
* @return the current selection of this Navigatable * @return the current selection of this Navigatable
*/ */
public ProgramSelection getSelection(); public ProgramSelection getSelection();
/** /**
* Returns the current highlight of this Navigatable * Returns the current highlight of this Navigatable
*
* @return the current highlight of this Navigatable * @return the current highlight of this Navigatable
*/ */
public ProgramSelection getHighlight(); public ProgramSelection getHighlight();
/** /**
* Returns the current text selection or null * Returns the current text selection or null
*
* @return the text selection * @return the text selection
*/ */
public String getTextSelection(); public String getTextSelection();
/** /**
* Adds a listener to be notified if this Navigatable is terminated * Adds a listener to be notified if this Navigatable is terminated
*
* @param listener the listener to be notified when this Navigatable is closed * @param listener the listener to be notified when this Navigatable is closed
*/ */
public void addNavigatableListener(NavigatableRemovalListener listener); public void addNavigatableListener(NavigatableRemovalListener listener);
/** /**
* Removes a listener to be notified if this Navigatable is terminated. * 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 * @param listener the listener that no longer should be notified when this Navigatable is
* closed. * closed.
*/ */
@ -144,12 +167,14 @@ public interface Navigatable {
/** /**
* Returns true if this navigatable is no longer valid, false if it is still good * 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 * @return true if this navigatable is no longer valid, false if it is still good
*/ */
public boolean isDisposed(); public boolean isDisposed();
/** /**
* Returns true if this navigatable supports highlighting * Returns true if this navigatable supports highlighting
*
* @return true if this navigatable supports highlighting * @return true if this navigatable supports highlighting
*/ */
public boolean supportsHighlight(); public boolean supportsHighlight();

View file

@ -57,6 +57,7 @@ public abstract class AbstractPatchAction extends DockingAction {
private Program program; private Program program;
private Address address; private Address address;
private CodeUnit codeUnit;
private final KeyListener listenerForKeys = new KeyAdapter() { private final KeyListener listenerForKeys = new KeyAdapter() {
@Override @Override
@ -165,6 +166,15 @@ public abstract class AbstractPatchAction extends DockingAction {
return program; 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 * Get the address at which this action was invoked
* *
@ -276,14 +286,12 @@ public abstract class AbstractPatchAction extends DockingAction {
@Override @Override
public boolean isAddToPopup(ActionContext context) { public boolean isAddToPopup(ActionContext context) {
if (!(context instanceof ListingActionContext)) { CodeUnit cu = getCodeUnit(context);
if (cu == null || !isApplicableToUnit(cu)) {
return false; return false;
} }
ListingActionContext lac = (ListingActionContext) context; ListingActionContext lac = (ListingActionContext) context;
if (!isApplicableToUnit(lac.getCodeUnit())) {
return false;
}
ComponentProvider provider = lac.getComponentProvider(); ComponentProvider provider = lac.getComponentProvider();
if (!(provider instanceof CodeViewerProvider)) { 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()}. * 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 * 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) * 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 @Override
public void actionPerformed(ActionContext context) { public void actionPerformed(ActionContext context) {
if (!(context instanceof ListingActionContext)) { codeUnit = getCodeUnit(context);
if (codeUnit == null || !isApplicableToUnit(codeUnit)) {
return; return;
} }
@ -349,10 +368,6 @@ public abstract class AbstractPatchAction extends DockingAction {
if (codeViewerProvider.isReadOnly()) { if (codeViewerProvider.isReadOnly()) {
return; return;
} }
CodeUnit cu = lac.getCodeUnit();
if (!isApplicableToUnit(cu)) {
return;
}
ProgramLocation cur = lac.getLocation(); ProgramLocation cur = lac.getLocation();
program = cur.getProgram(); program = cur.getProgram();
@ -365,7 +380,7 @@ public abstract class AbstractPatchAction extends DockingAction {
return; return;
} }
prepare(cu); prepare();
ToolOptions displayOptions = tool.getOptions(GhidraOptions.CATEGORY_BROWSER_DISPLAY); ToolOptions displayOptions = tool.getOptions(GhidraOptions.CATEGORY_BROWSER_DISPLAY);
Font font = displayOptions.getFont(GhidraOptions.OPTION_BASE_FONT, null); Font font = displayOptions.getFont(GhidraOptions.OPTION_BASE_FONT, null);
@ -377,7 +392,7 @@ public abstract class AbstractPatchAction extends DockingAction {
if (!showInputs(fieldPanel)) { if (!showInputs(fieldPanel)) {
return; return;
} }
fillInputs(cu); fillInputs();
fieldLayoutManager.layoutContainer(fieldPanel); fieldLayoutManager.layoutContainer(fieldPanel);
} }

View file

@ -15,7 +15,9 @@
*/ */
package ghidra.app.plugin.core.assembler; package ghidra.app.plugin.core.assembler;
import docking.ActionContext;
import ghidra.app.CorePluginPackage; import ghidra.app.CorePluginPackage;
import ghidra.app.context.ListingActionContext;
import ghidra.app.plugin.PluginCategoryNames; import ghidra.app.plugin.PluginCategoryNames;
import ghidra.app.plugin.ProgramPlugin; import ghidra.app.plugin.ProgramPlugin;
import ghidra.app.plugin.assembler.Assemblers; import ghidra.app.plugin.assembler.Assemblers;
@ -62,10 +64,29 @@ public class AssemblerPlugin extends ProgramPlugin {
} }
private void createActions() { 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); tool.addAction(patchInstructionAction);
patchDataAction = new PatchDataAction(this, "Patch Data"); patchDataAction = new PatchDataAction(this);
tool.addAction(patchDataAction); tool.addAction(patchDataAction);
} }

View file

@ -38,11 +38,8 @@ import ghidra.app.plugin.processors.sleigh.*;
import ghidra.framework.Application; import ghidra.framework.Application;
import ghidra.framework.ApplicationConfiguration; import ghidra.framework.ApplicationConfiguration;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.lang.Language;
import ghidra.program.model.lang.LanguageID; import ghidra.program.model.lang.LanguageID;
import ghidra.program.model.listing.Instruction; import ghidra.program.model.listing.Instruction;
import ghidra.program.model.listing.Program;
import ghidra.program.util.ProgramLocation;
import ghidra.util.NumericUtilities; import ghidra.util.NumericUtilities;
import resources.ResourceManager; import resources.ResourceManager;
@ -71,7 +68,6 @@ public class AssemblyDualTextField {
protected final AssemblyAutocompletionModel model = new AssemblyAutocompletionModel(); protected final AssemblyAutocompletionModel model = new AssemblyAutocompletionModel();
protected final AssemblyAutocompleter auto = new AssemblyAutocompleter(model); protected final AssemblyAutocompleter auto = new AssemblyAutocompleter(model);
protected Program program;
protected Assembler assembler; protected Assembler assembler;
protected Address address; protected Address address;
protected Instruction existing; 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) { public void setAssembler(Assembler assembler) {
setProgramLocation(loc.getProgram(), loc.getAddress()); this.assembler = Objects.requireNonNull(assembler);
} }
/** /**
* Set the current program location * Set the address of the assembly instruction
* *
* <p> * <p>
* This may cause the construction of a new assembler, if one suitable for the given program's * Note this will reset the existing instruction to null to prevent its accidental re-use. See
* language has not yet been built. * {@link #setExisting(Instruction)}.
* *
* @param program the program * @param address the address
* @param address the non-null address
*/ */
public void setProgramLocation(Program program, Address address) { public void setAddress(Address address) {
this.program = program;
this.address = Objects.requireNonNull(address); this.address = Objects.requireNonNull(address);
this.existing = program.getListing().getInstructionAt(address); this.existing = null;
this.assembler = Assemblers.getAssembler(program);
} }
/** /**
* 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 * @see #computePreference(AssemblyResolvedPatterns)
* @param addr the address * @param existing
*/ */
public void setLanguageLocation(Language lang, Address addr) { public void setExisting(Instruction existing) {
this.program = null; this.existing = existing;
this.address = addr;
this.existing = null;
this.assembler = Assemblers.getAssembler(lang);
} }
/** /**
@ -704,7 +694,7 @@ public class AssemblyDualTextField {
* @param existing the instruction, if any, currently under the user's cursor * @param existing the instruction, if any, currently under the user's cursor
* @return a preference * @return a preference
*/ */
protected int computePreference(AssemblyResolvedPatterns rc, Instruction existing) { protected int computePreference(AssemblyResolvedPatterns rc) {
if (existing == null) { if (existing == null) {
return 0; return 0;
} }
@ -736,7 +726,7 @@ public class AssemblyDualTextField {
* @return the collection of completion items * @return the collection of completion items
*/ */
protected Collection<AssemblyCompletion> computeCompletions(String text) { protected Collection<AssemblyCompletion> computeCompletions(String text) {
final AssemblyPatternBlock ctx = assembler.getContextAt(address); final AssemblyPatternBlock ctx = getContext();
Set<AssemblyCompletion> result = new TreeSet<>(); Set<AssemblyCompletion> result = new TreeSet<>();
Collection<AssemblyParseResult> parses = assembler.parseLine(text); Collection<AssemblyParseResult> parses = assembler.parseLine(text);
@ -757,7 +747,8 @@ public class AssemblyDualTextField {
parses = assembler.parseLine(fullText); parses = assembler.parseLine(fullText);
for (AssemblyParseResult parse : parses) { for (AssemblyParseResult parse : parses) {
if (!parse.isError()) { if (!parse.isError()) {
AssemblyResolutionResults sems = assembler.resolveTree(parse, address); AssemblyResolutionResults sems =
assembler.resolveTree(parse, address, getContext());
for (AssemblyResolution ar : sems) { for (AssemblyResolution ar : sems) {
if (ar.isError()) { if (ar.isError()) {
//result.add(new AssemblyError("", ar.toString())); //result.add(new AssemblyError("", ar.toString()));
@ -766,7 +757,7 @@ public class AssemblyDualTextField {
AssemblyResolvedPatterns rc = (AssemblyResolvedPatterns) ar; AssemblyResolvedPatterns rc = (AssemblyResolvedPatterns) ar;
for (byte[] ins : rc.possibleInsVals(ctx)) { for (byte[] ins : rc.possibleInsVals(ctx)) {
result.add(new AssemblyInstruction(text, Arrays.copyOf(ins, ins.length), result.add(new AssemblyInstruction(text, Arrays.copyOf(ins, ins.length),
computePreference(rc, existing))); computePreference(rc)));
if (!exhaustUndefined) { if (!exhaustUndefined) {
break; break;
} }
@ -780,6 +771,15 @@ public class AssemblyDualTextField {
return result; 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 * A demonstration of the assembly GUI outside of Ghidra
*/ */
@ -807,7 +807,8 @@ public class AssemblyDualTextField {
SleighLanguage lang = (SleighLanguage) provider.getLanguage(DEMO_LANG_ID); SleighLanguage lang = (SleighLanguage) provider.getLanguage(DEMO_LANG_ID);
curAddr = lang.getDefaultSpace().getAddress(0); curAddr = lang.getDefaultSpace().getAddress(0);
input.setLanguageLocation(lang, curAddr); input.setAssembler(Assemblers.getAssembler(lang));
input.setAddress(curAddr);
hbox.add(input.getAssemblyField()); hbox.add(input.getAssemblyField());
hbox.add(input.getMnemonicField()); hbox.add(input.getMnemonicField());
@ -827,7 +828,7 @@ public class AssemblyDualTextField {
asm.setText(asm.getText() + data); asm.setText(asm.getText() + data);
input.clear(); input.clear();
curAddr = curAddr.addWrap(ins.getData().length); curAddr = curAddr.addWrap(ins.getData().length);
input.setLanguageLocation(lang, curAddr); input.setAddress(curAddr);
addrlabel.setText(String.format(ADDR_FORMAT, curAddr)); addrlabel.setText(String.format(ADDR_FORMAT, curAddr));
} }
}); });

View file

@ -47,10 +47,14 @@ public class PatchDataAction extends AbstractPatchAction {
private Data data; private Data data;
public PatchDataAction(Plugin owner) {
this(owner, "Patch Data");
}
public PatchDataAction(Plugin owner, String name) { public PatchDataAction(Plugin owner, String name) {
super(owner, 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)); setKeyBindingData(new KeyBindingData(KEYBIND_PATCH_DATA));
setHelpLocation(new HelpLocation(owner.getName(), "patch_data")); setHelpLocation(new HelpLocation(owner.getName(), "patch_data"));
@ -81,9 +85,8 @@ public class PatchDataAction extends AbstractPatchAction {
return true; return true;
} }
@Override protected Data getData() {
protected void prepare(CodeUnit unit) { return (Data) getCodeUnit();
data = (Data) unit;
} }
@Override @Override
@ -106,8 +109,8 @@ public class PatchDataAction extends AbstractPatchAction {
} }
@Override @Override
protected void fillInputs(CodeUnit unit) { protected void fillInputs() {
String repr = data.getDefaultValueRepresentation(); String repr = getData().getDefaultValueRepresentation();
input.setText(repr); input.setText(repr);
input.setCaretPosition(repr.length()); input.setCaretPosition(repr.length());
} }

View file

@ -118,14 +118,14 @@ public class PatchInstructionAction extends AbstractPatchAction {
private final Map<Language, CachingSwingWorker<Assembler>> cache = private final Map<Language, CachingSwingWorker<Assembler>> cache =
LazyMap.lazyMap(new HashMap<>(), language -> new AssemblerConstructorWorker(language)); LazyMap.lazyMap(new HashMap<>(), language -> new AssemblerConstructorWorker(language));
/*test*/ final Map<Language, Boolean> shownWarning = /*test*/ static final Map<Language, Boolean> SHOWN_WARNING =
DefaultedMap.defaultedMap(new HashMap<>(), false); DefaultedMap.defaultedMap(new HashMap<>(), false);
/*test*/ final AssemblyDualTextField input = newAssemblyDualTextField(); /*test*/ final AssemblyDualTextField input = newAssemblyDualTextField();
private final ListenerForAccept listenerForAccept = new ListenerForAccept(); private final ListenerForAccept listenerForAccept = new ListenerForAccept();
private Language language; protected Language language;
private Assembler assembler; protected Assembler assembler;
// Callback to keep the autocompleter positioned under the fields // Callback to keep the autocompleter positioned under the fields
private FieldPanelOverLayoutListener listenerToMoveAutocompleter = ev -> { 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) { public PatchInstructionAction(Plugin owner, String name) {
super(owner, 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)); setKeyBindingData(new KeyBindingData(KEYBIND_PATCH_INSTRUCTION));
setHelpLocation(new HelpLocation(owner.getName(), "patch_instruction")); setHelpLocation(new HelpLocation(owner.getName(), "patch_instruction"));
@ -151,6 +155,10 @@ public class PatchInstructionAction extends AbstractPatchAction {
init(); init();
} }
protected AssemblyDualTextField newAssemblyDualTextField() {
return new AssemblyDualTextField();
}
@Override @Override
public void dispose() { public void dispose() {
super.dispose(); super.dispose();
@ -182,24 +190,36 @@ public class PatchInstructionAction extends AbstractPatchAction {
return true; return true;
} }
@Override protected void warnLanguage() {
protected void prepare(CodeUnit cu) {
language = cu.getProgram().getLanguage();
AssemblyRating rating = AssemblyRating.valueOf( AssemblyRating rating = AssemblyRating.valueOf(
language.getProperty(ASSEMBLY_RATING + ":" + language.getLanguageID(), language.getProperty(ASSEMBLY_RATING + ":" + language.getLanguageID(),
AssemblyRating.UNRATED.name())); AssemblyRating.UNRATED.name()));
if (AssemblyRating.PLATINUM != rating) { if (AssemblyRating.PLATINUM != rating) {
String message = language.getProperty(ASSEMBLY_MESSAGE + ":" + language.getLanguageID(), String message = language.getProperty(ASSEMBLY_MESSAGE + ":" + language.getLanguageID(),
rating.message); rating.message);
if (!shownWarning.get(language)) { if (!SHOWN_WARNING.get(language)) {
Msg.showWarn(this, null, "Assembler Rating", Msg.showWarn(this, null, "Assembler Rating",
"<html><body><p style='width: 300px;'>" + message + "</p></body></html>"); "<html><body><p style='width: 300px;'>" + message + "</p></body></html>");
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); cache.get(language).get(null);
assembler = Assemblers.getAssembler(cu.getProgram()); assembler = getAssembler(cu);
} }
@Override @Override
@ -207,9 +227,19 @@ public class PatchInstructionAction extends AbstractPatchAction {
input.setFont(font); input.setFont(font);
} }
protected Instruction getExistingInstruction() {
Program program = getProgram();
if (program == null) {
return null;
}
return program.getListing().getInstructionAt(getAddress());
}
@Override @Override
protected boolean showInputs(FieldPanel fieldPanel) { protected boolean showInputs(FieldPanel fieldPanel) {
input.setProgramLocation(getProgram(), getAddress()); input.setAssembler(assembler);
input.setAddress(getAddress());
input.setExisting(getExistingInstruction());
FieldLocation locMnem = findFieldLocation(getAddress(), "Mnemonic"); FieldLocation locMnem = findFieldLocation(getAddress(), "Mnemonic");
if (locMnem == null) { if (locMnem == null) {
Msg.showError(this, fieldPanel, getName(), Msg.showError(this, fieldPanel, getName(),
@ -232,9 +262,10 @@ public class PatchInstructionAction extends AbstractPatchAction {
} }
@Override @Override
protected void fillInputs(CodeUnit unit) { protected void fillInputs() {
if (unit instanceof Instruction) { CodeUnit cu = getCodeUnit();
Instruction ins = (Instruction) unit; if (cu instanceof Instruction) {
Instruction ins = (Instruction) cu;
String instr = ins.toString(); String instr = ins.toString();
if (ins.isInDelaySlot()) { if (ins.isInDelaySlot()) {
assert instr.startsWith("_"); assert instr.startsWith("_");
@ -269,6 +300,10 @@ public class PatchInstructionAction extends AbstractPatchAction {
// Do nothing. User must select a completion item instead // 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 * Accept the given instruction selected by the user
* *
@ -279,7 +314,7 @@ public class PatchInstructionAction extends AbstractPatchAction {
Address address = getAddress(); Address address = getAddress();
try (ProgramTransaction trans = try (ProgramTransaction trans =
ProgramTransaction.open(program, "Assemble @" + address + ": " + input.getText())) { ProgramTransaction.open(program, "Assemble @" + address + ": " + input.getText())) {
assembler.patchProgram(ins.getData(), address); applyPatch(ins.getData());
trans.commit(); trans.commit();
hide(); hide();
} }

View file

@ -1,6 +1,5 @@
/* ### /* ###
* IP: GHIDRA * IP: GHIDRA
* REVIEWED: YES
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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); super(provider, provider, location);
} }
/**
* @return true if underlying code viewer corresponds to a dynamic listing
*/
public boolean isDyanmicListing() {
return ((CodeViewerProvider) getComponentProvider()).isDynamicListing();
}
} }

View file

@ -21,7 +21,6 @@ import ghidra.app.cmd.disassemble.*;
import ghidra.app.context.ListingActionContext; import ghidra.app.context.ListingActionContext;
import ghidra.app.events.ProgramActivatedPluginEvent; import ghidra.app.events.ProgramActivatedPluginEvent;
import ghidra.app.plugin.PluginCategoryNames; import ghidra.app.plugin.PluginCategoryNames;
import ghidra.app.plugin.core.codebrowser.CodeViewerActionContext;
import ghidra.framework.options.Options; import ghidra.framework.options.Options;
import ghidra.framework.plugintool.*; import ghidra.framework.plugintool.*;
import ghidra.framework.plugintool.util.PluginStatus; import ghidra.framework.plugintool.util.PluginStatus;
@ -235,9 +234,6 @@ public class DisassemblerPlugin extends Plugin {
Program currentProgram = context.getProgram(); Program currentProgram = context.getProgram();
DisassembleCommand cmd = null; DisassembleCommand cmd = null;
boolean isDynamicListing = (context instanceof CodeViewerActionContext &&
((CodeViewerActionContext) context).isDyanmicListing());
if ((currentSelection != null) && (!currentSelection.isEmpty())) { if ((currentSelection != null) && (!currentSelection.isEmpty())) {
cmd = new DisassembleCommand(currentSelection, null, true); cmd = new DisassembleCommand(currentSelection, null, true);
} }
@ -271,13 +267,18 @@ public class DisassemblerPlugin extends Plugin {
} }
} }
if (cmd != null) { 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); tool.executeBackgroundCommand(cmd, currentProgram);
} }
} }
boolean checkDisassemblyEnabled(ListingActionContext context, Address address, boolean checkDisassemblyEnabled(ListingActionContext context, Address address,
boolean followPtr) { boolean followPtr) {
// Debugger now has its own Disassemble actions
if (context.getNavigatable().isDynamic()) {
return false;
}
ProgramSelection currentSelection = context.getSelection(); ProgramSelection currentSelection = context.getSelection();
Program currentProgram = context.getProgram(); Program currentProgram = context.getProgram();
if ((currentSelection != null) && (!currentSelection.isEmpty())) { if ((currentSelection != null) && (!currentSelection.isEmpty())) {

View file

@ -30,12 +30,12 @@ import ghidra.app.plugin.core.assembler.AssemblyDualTextField.AssemblyInstructio
import ghidra.app.plugin.core.codebrowser.CodeViewerProvider; import ghidra.app.plugin.core.codebrowser.CodeViewerProvider;
import ghidra.app.util.viewer.listingpanel.ListingPanel; import ghidra.app.util.viewer.listingpanel.ListingPanel;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.lang.Language;
import ghidra.program.model.listing.*; import ghidra.program.model.listing.*;
import ghidra.program.util.ProgramLocation; import ghidra.program.util.ProgramLocation;
import ghidra.test.AbstractGhidraHeadedIntegrationTest; import ghidra.test.AbstractGhidraHeadedIntegrationTest;
public class AssemblerPluginTestHelper { public class AssemblerPluginTestHelper {
public final AssemblerPlugin assemblerPlugin;
private final CodeViewerProvider provider; private final CodeViewerProvider provider;
private final Program program; private final Program program;
@ -46,20 +46,30 @@ public class AssemblerPluginTestHelper {
private final Listing listing; private final Listing listing;
public AssemblerPluginTestHelper(AssemblerPlugin assemblerPlugin, CodeViewerProvider provider, public AssemblerPluginTestHelper(PatchInstructionAction patchInstructionAction,
Program program) { PatchDataAction patchDataAction, CodeViewerProvider provider, Program program) {
this.assemblerPlugin = assemblerPlugin;
this.provider = provider; this.provider = provider;
this.program = program; this.program = program;
this.patchInstructionAction = assemblerPlugin.patchInstructionAction; this.patchInstructionAction = patchInstructionAction;
this.patchDataAction = assemblerPlugin.patchDataAction; this.patchDataAction = patchDataAction;
this.instructionInput = assemblerPlugin.patchInstructionAction.input; this.instructionInput =
this.dataInput = assemblerPlugin.patchDataAction.input; patchInstructionAction == null ? null : patchInstructionAction.input;
this.dataInput = patchDataAction == null ? null : patchDataAction.input;
this.listing = program.getListing(); this.listing = program.getListing();
// Snuff the assembler's warning prompt // 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() { public void assertDualFields() {
@ -69,12 +79,18 @@ public class AssemblerPluginTestHelper {
} }
public List<AssemblyCompletion> inputAndGetCompletions(String text) { public List<AssemblyCompletion> inputAndGetCompletions(String text) {
return AbstractGenericTest.runSwing(() -> { AbstractGenericTest.runSwing(() -> {
instructionInput.setText(text); instructionInput.setText(text);
instructionInput.auto.startCompletion(instructionInput.getOperandsField()); instructionInput.auto.startCompletion(instructionInput.getOperandsField());
instructionInput.auto.flushUpdates(); instructionInput.auto.flushUpdates();
return instructionInput.auto.getSuggestions();
}); });
return AbstractGenericTest.waitForValue(() -> AbstractGenericTest.runSwing(() -> {
List<AssemblyCompletion> suggestions = instructionInput.auto.getSuggestions();
if (suggestions.isEmpty()) {
return null;
}
return suggestions;
}));
} }
public void goTo(Address address) { public void goTo(Address address) {
@ -95,18 +111,21 @@ public class AssemblerPluginTestHelper {
public Instruction patchInstructionAt(Address address, String expText, String newText) { public Instruction patchInstructionAt(Address address, String expText, String newText) {
goTo(address); goTo(address);
Language language = patchInstructionAction.getLanguage(listing.getCodeUnitAt(address));
snuffWarning(language);
AbstractDockingTest.performAction(assemblerPlugin.patchInstructionAction, provider, true); AbstractDockingTest.performAction(patchInstructionAction, provider, true);
assertDualFields(); assertDualFields();
assertEquals(expText, instructionInput.getText()); assertEquals(expText, instructionInput.getText());
assertEquals(address, assemblerPlugin.patchInstructionAction.getAddress()); assertEquals(address, patchInstructionAction.getAddress());
List<AssemblyCompletion> completions = inputAndGetCompletions(newText); List<AssemblyCompletion> completions = inputAndGetCompletions(newText);
assertFalse("There are no assembly completion options", completions.isEmpty());
AssemblyCompletion first = completions.get(0); AssemblyCompletion first = completions.get(0);
assertTrue(first instanceof AssemblyInstruction); assertTrue(first instanceof AssemblyInstruction);
AssemblyInstruction ai = (AssemblyInstruction) first; AssemblyInstruction ai = (AssemblyInstruction) first;
AbstractGenericTest.runSwing(() -> assemblerPlugin.patchInstructionAction.accept(ai)); AbstractGenericTest.runSwing(() -> patchInstructionAction.accept(ai));
AbstractGhidraHeadedIntegrationTest.waitForProgram(program); AbstractGhidraHeadedIntegrationTest.waitForProgram(program);
return Objects.requireNonNull(listing.getInstructionAt(address)); return Objects.requireNonNull(listing.getInstructionAt(address));
@ -115,14 +134,14 @@ public class AssemblerPluginTestHelper {
public Data patchDataAt(Address address, String expText, String newText) { public Data patchDataAt(Address address, String expText, String newText) {
goTo(address); goTo(address);
AbstractDockingTest.performAction(assemblerPlugin.patchDataAction, provider, true); AbstractDockingTest.performAction(patchDataAction, provider, true);
assertTrue(dataInput.isVisible()); assertTrue(dataInput.isVisible());
assertEquals(expText, dataInput.getText()); assertEquals(expText, dataInput.getText());
assertEquals(address, assemblerPlugin.patchDataAction.getAddress()); assertEquals(address, patchDataAction.getAddress());
AbstractGenericTest.runSwing(() -> { AbstractGenericTest.runSwing(() -> {
dataInput.setText(newText); dataInput.setText(newText);
assemblerPlugin.patchDataAction.accept(); patchDataAction.accept();
}); });
AbstractGhidraHeadedIntegrationTest.waitForProgram(program); AbstractGhidraHeadedIntegrationTest.waitForProgram(program);

View file

@ -702,6 +702,10 @@ public class AssemblyResolvedPatterns extends AssemblyResolution {
* @return the iterable * @return the iterable
*/ */
public Iterable<byte[]> possibleInsVals(AssemblyPatternBlock forCtx) { public Iterable<byte[]> possibleInsVals(AssemblyPatternBlock forCtx) {
AssemblyPatternBlock ctxCompat = ctx.combine(forCtx);
if (ctxCompat == null) {
return List.of();
}
Predicate<byte[]> removeForbidden = (byte[] val) -> { Predicate<byte[]> removeForbidden = (byte[] val) -> {
for (AssemblyResolvedPatterns f : forbids) { for (AssemblyResolvedPatterns f : forbids) {
// If the forbidden length is larger than us, we can ignore it // If the forbidden length is larger than us, we can ignore it

View file

@ -70,6 +70,11 @@ public class ARMAssemblyTest extends AbstractAssemblyTest {
assertOneCompatRestExact("ands r0,r5", "28:40", THUMB, 0x00400000, "ands r0,r5"); 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 @Test
public void testAssemble_T_bl_0x00008000() { public void testAssemble_T_bl_0x00008000() {
// What makes this different from the above test is that it jumps backward // What makes this different from the above test is that it jumps backward

View file

@ -200,9 +200,8 @@ public abstract class AbstractAssemblyTest extends AbstractGenericTest {
*/ */
protected void checkAllExact(AssemblyResolutionResults rr, Collection<String> disassembly, protected void checkAllExact(AssemblyResolutionResults rr, Collection<String> disassembly,
long addr, String ctxstr) { long addr, String ctxstr) {
final AssemblyPatternBlock ctx = final AssemblyPatternBlock ctx = (ctxstr == null ? context.getDefault()
(ctxstr == null ? context.getDefault() : AssemblyPatternBlock.fromString(ctxstr)) : AssemblyPatternBlock.fromString(ctxstr)).fillMask();
.fillMask();
dbg.println("Checking each: " + disassembly + " ctx:" + ctx); dbg.println("Checking each: " + disassembly + " ctx:" + ctx);
boolean gotOne = false; boolean gotOne = false;
boolean failedOne = false; boolean failedOne = false;