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
* limitations under the License.
*/
package ghidra.app.plugin.core.debug.workflow;
package ghidra.app.plugin.core.debug.disassemble;
import com.google.common.collect.Range;
import ghidra.framework.cmd.TypedBackgroundCommand;
import ghidra.program.disassemble.Disassembler;
import ghidra.program.model.address.*;
import ghidra.program.model.lang.*;
import ghidra.program.model.listing.Instruction;
import ghidra.program.model.mem.MemBuffer;
import ghidra.program.model.mem.MemoryBufferImpl;
import ghidra.program.model.util.CodeUnitInsertionException;
import ghidra.trace.model.guest.TraceGuestPlatform;
import ghidra.trace.model.guest.TracePlatform;
import ghidra.trace.model.program.TraceProgramView;
import ghidra.util.MathUtilities;
import ghidra.util.task.TaskMonitor;
public class DisassembleTraceCommand extends TypedBackgroundCommand<TraceProgramView> {
public static DisassembleTraceCommand create(TraceGuestPlatform guest, Address start,
AddressSetView restrictedSet) {
return guest == null ? new DisassembleTraceCommand(start, restrictedSet)
: new DisassembleGuestTraceCommand(guest, start, restrictedSet);
}
public class TraceDisassembleCommand extends TypedBackgroundCommand<TraceProgramView> {
protected final TracePlatform platform;
protected final Address start;
protected final AddressSetView restrictedSet;
protected RegisterValue initialContext;
private AddressSetView disassembled;
public DisassembleTraceCommand(Address start, AddressSetView restrictedSet) {
public TraceDisassembleCommand(TracePlatform platform, Address start,
AddressSetView restrictedSet) {
super("Disassemble", true, true, false);
this.platform = platform;
this.start = start;
this.restrictedSet = restrictedSet;
}
@ -51,11 +50,12 @@ public class DisassembleTraceCommand extends TypedBackgroundCommand<TraceProgram
}
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) {
return new MemoryBufferImpl(view.getMemory(), start);
return platform.getMappedMemBuffer(view.getSnap(), platform.mapHostToGuest(start));
}
protected int computeLimit() {
@ -68,14 +68,12 @@ public class DisassembleTraceCommand extends TypedBackgroundCommand<TraceProgram
}
protected AddressSetView writeBlock(TraceProgramView view, InstructionBlock block) {
InstructionSet set = new InstructionSet(view.getAddressFactory());
InstructionSet set = new InstructionSet(platform.getAddressFactory());
set.addBlock(block);
try {
return view.getListing().addInstructions(set, true);
}
catch (CodeUnitInsertionException e) {
return new AddressSet();
}
return view.getTrace()
.getCodeManager()
.instructions()
.addInstructionSet(Range.atLeast(view.getSnap()), platform, set, true);
}
@Override
@ -83,8 +81,21 @@ public class DisassembleTraceCommand extends TypedBackgroundCommand<TraceProgram
Disassembler disassembler = getDisassembler(view, monitor);
MemBuffer buffer = getBuffer(view);
int limit = computeLimit();
// TODO: limit is actually instruction count, not byte count :'(
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;
}

View file

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

View file

@ -73,6 +73,9 @@ public class DebuggerStateEditingPlugin extends AbstractDebuggerPlugin {
if (current.getTrace() == null) {
return;
}
if (editingService == null) {
return;
}
editingService.setCurrentMode(current.getTrace(), state.getUserData());
// TODO: Limit selectable modes?
// 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;
}
@Override
public boolean isDynamic() {
return true;
}
/**
* Check if this is the main dynamic listing.
*

View file

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

View file

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

View file

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

View file

@ -28,6 +28,10 @@ public interface DebuggerPlatformMapper {
/**
* 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 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.target.TraceObject;
import ghidra.util.MathUtilities;
import ghidra.util.database.UndoableTransaction;
public class DefaultDebuggerPlatformMapper extends AbstractDebuggerPlatformMapper {
@ -54,6 +55,9 @@ public class DefaultDebuggerPlatformMapper extends AbstractDebuggerPlatformMappe
@Override
public void addToTrace(long snap) {
try (UndoableTransaction tid = UndoableTransaction.start(trace, "Add guest " +
cSpec.getLanguage().getLanguageDescription() + "/" + cSpec.getCompilerSpecDescription(),
true)) {
TracePlatformManager platformManager = trace.getPlatformManager();
TraceGuestPlatform platform = platformManager.getOrAddGuestPlatform(cSpec);
if (platform == null) {
@ -61,7 +65,16 @@ public class DefaultDebuggerPlatformMapper extends AbstractDebuggerPlatformMappe
}
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) {
Trace trace = platform.getTrace();
AddressSpace hostSpace = trace.getBaseAddressFactory().getDefaultAddressSpace();

View file

@ -17,7 +17,9 @@ package ghidra.app.plugin.core.debug.platform.arm;
import java.math.BigInteger;
import ghidra.app.plugin.core.debug.workflow.*;
import ghidra.app.plugin.core.debug.disassemble.TraceDisassembleCommand;
import ghidra.app.plugin.core.debug.workflow.DisassemblyInject;
import ghidra.app.plugin.core.debug.workflow.DisassemblyInjectInfo;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.lang.*;
@ -58,7 +60,7 @@ public class ArmDisassemblyInject implements DisassemblyInject {
}
@Override
public void pre(PluginTool tool, DisassembleTraceCommand command, Trace trace,
public void pre(PluginTool tool, TraceDisassembleCommand command, Trace trace,
Language language, long snap, TraceThread thread, AddressSetView startSet,
AddressSetView restricted) {
/**
@ -79,7 +81,7 @@ public class ArmDisassemblyInject implements DisassemblyInject {
TraceMemoryRegisterSpace regs =
trace.getMemoryManager().getMemoryRegisterSpace(thread, false);
/**
* Some variants (particularly Cortex-M) are missing cpsr This seems to indicate it only
* Some variants (particularly Cortex-M) are missing cpsr. This seems to indicate it only
* supports THUMB. There is an epsr (xpsr in gdb), but we don't have it in our models, and
* its TMode bit must be set, or it will fault.
*/
@ -87,6 +89,12 @@ public class ArmDisassemblyInject implements DisassemblyInject {
command.setInitialContext(new RegisterValue(tModeReg, BigInteger.ONE));
return;
}
/**
* TODO: Once we have register mapping figured out for object-based traces, we need to have
* this check the cpsr register there, instead. Better yet, regarding epsr and xpsr, we can
* actually check them, even though they don't exist in the slaspec, because we have access
* to the raw recorded register objects.
*/
RegisterValue cpsrVal = regs.getValue(snap, cpsrReg);
if (isThumbMode(cpsrVal)) {

View file

@ -22,6 +22,7 @@ import java.util.Set;
import java.util.concurrent.*;
import java.util.stream.Collectors;
import ghidra.app.plugin.core.debug.disassemble.TraceDisassembleCommand;
import ghidra.app.plugin.core.debug.workflow.*;
import ghidra.app.services.DebuggerModelService;
import ghidra.app.services.TraceRecorder;
@ -49,7 +50,7 @@ public class DbgengX64DisassemblyInject implements DisassemblyInject {
}
@Override
public void pre(PluginTool tool, DisassembleTraceCommand command, Trace trace,
public void pre(PluginTool tool, TraceDisassembleCommand command, Trace trace,
Language language, long snap, TraceThread thread, AddressSetView startSet,
AddressSetView restricted) {
AddressRange first = startSet.getFirstRange();

View file

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

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 ghidra.app.plugin.core.debug.disassemble.TraceDisassembleCommand;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.lang.Language;
@ -93,7 +94,7 @@ public interface DisassemblyInject extends ExtensionPoint {
* @param startSet the starting address set, usually just the PC
* @param restricted the set of disassemblable addresses
*/
default void pre(PluginTool tool, DisassembleTraceCommand command, Trace trace,
default void pre(PluginTool tool, TraceDisassembleCommand command, Trace trace,
Language language, long snap, TraceThread thread, AddressSetView startSet,
AddressSetView restricted) {
}

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();
env.dispose();
}

View file

@ -129,7 +129,7 @@ public class DebuggerManualTest extends AbstractGhidraHeadedDebuggerGUITest {
tb.trace.getThreadManager().createThread("Thread 2", 4);
tb.addData(0, tb.addr(0x4004), Undefined4DataType.dataType, tb.buf(6, 7, 8, 9));
tb.addInstruction(0, tb.addr(0x4008), null, tb.buf(0xf4, 0));
tb.addInstruction(0, tb.addr(0x4008), tb.host, tb.buf(0xf4, 0));
Language x86 = getSLEIGH_X86_LANGUAGE();
DBTraceGuestPlatform guest =

View file

@ -25,10 +25,20 @@ import ghidra.trace.model.target.TraceObject;
public class TestDebuggerPlatformOpinion extends AbstractDebuggerPlatformOpinion {
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
public String getDescription() {
return "Test x86-64";
return description;
}
@Override
@ -38,14 +48,18 @@ public class TestDebuggerPlatformOpinion extends AbstractDebuggerPlatformOpinion
@Override
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
public DebuggerPlatformMapper take(PluginTool tool, Trace trace) {
return new DefaultDebuggerPlatformMapper(tool, trace, getCompilerSpec());
}
};
}
@Override
@ -54,9 +68,12 @@ public class TestDebuggerPlatformOpinion extends AbstractDebuggerPlatformOpinion
if (!"test".equals(debugger)) {
return Set.of();
}
if (!"x86-64".equals(arch)) {
return Set.of();
if ("armv8le".equals(arch)) {
return Set.of(Offers.ARM_V8_LE);
}
if ("x86-64".equals(arch)) {
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;
@DBAnnotatedObjectInfo(version = 0)
public class DBTraceGuestPlatform extends DBAnnotatedObject implements TraceGuestPlatform {
public class DBTraceGuestPlatform extends DBAnnotatedObject
implements TraceGuestPlatform, InternalTracePlatform {
public static final String TABLE_NAME = "Platforms";
@DBAnnotatedObjectInfo(version = 0)
@ -160,6 +161,16 @@ public class DBTraceGuestPlatform extends DBAnnotatedObject implements TraceGues
return manager.trace;
}
@Override
public int getIntKey() {
return (int) key;
}
@Override
public boolean isGuest() {
return true;
}
protected void deleteMappedRange(DBTraceGuestPlatformMappedRange range, TaskMonitor monitor)
throws CancelledException {
try (LockHold hold = LockHold.lock(manager.lock.writeLock())) {
@ -175,6 +186,7 @@ public class DBTraceGuestPlatform extends DBAnnotatedObject implements TraceGues
}
}
@Override
@Internal
public DBTraceGuestLanguage getLanguageEntry() {
return languageEntry;

View file

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

View file

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

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

View file

@ -19,6 +19,7 @@ import com.google.common.collect.Range;
import generic.NestedIterator;
import ghidra.program.model.address.*;
import ghidra.trace.model.Trace;
import ghidra.trace.model.TraceAddressSnapRange;
public abstract class AbstractBaseDBTraceCodeUnitsView<T extends DBTraceCodeUnitAdapter> {
@ -29,6 +30,10 @@ public abstract class AbstractBaseDBTraceCodeUnitsView<T extends DBTraceCodeUnit
this.space = space;
}
public Trace getTrace() {
return space.manager.getTrace();
}
public abstract int size();
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.trace.database.DBTrace;
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.util.LockHold;
@ -84,8 +84,8 @@ public abstract class AbstractDBTraceDataComponent implements DBTraceDefinedData
}
@Override
public TraceGuestPlatform getGuestPlatform() {
return root.getGuestPlatform();
public TracePlatform getPlatform() {
return root.getPlatform();
}
@Override

View file

@ -142,7 +142,7 @@ public class DBTraceCodeSpace implements TraceCodeSpace, DBTraceSpaceBased {
TraceAddressSnapRangeQuery.intersecting(range, span)).values()) {
monitor.checkCanceled();
monitor.incrementProgress(1);
if (instruction.guest != guest) {
if (instruction.platform != guest) {
continue;
}
instructionMapSpace.deleteData(instruction);
@ -154,7 +154,7 @@ public class DBTraceCodeSpace implements TraceCodeSpace, DBTraceSpaceBased {
TraceAddressSnapRangeQuery.intersecting(range, span)).values()) {
monitor.checkCanceled();
monitor.incrementProgress(1);
if (dataUnit.guest != guest) {
if (dataUnit.platform != guest) {
continue;
}
// 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.trace.database.DBTraceUtils;
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.model.guest.TraceGuestPlatform;
import ghidra.trace.model.guest.TracePlatform;
import ghidra.util.LockHold;
import ghidra.util.database.DBCachedObjectStore;
import ghidra.util.database.DBObjectColumn;
@ -56,7 +56,7 @@ public class DBTraceData extends AbstractDBTraceCodeUnit<DBTraceData>
@DBAnnotatedField(column = DATATYPE_COLUMN_NAME)
private long dataTypeID;
protected DBTraceGuestPlatform guest;
protected InternalTracePlatform platform;
protected DataType dataType;
protected DataType baseDataType;
protected Settings defaultSettings;
@ -75,8 +75,8 @@ public class DBTraceData extends AbstractDBTraceCodeUnit<DBTraceData>
if (created) {
return;
}
guest = space.manager.platformManager.getPlatformByKey(platformKey);
if (guest == null && platformKey != -1) {
platform = space.manager.platformManager.getPlatformByKey(platformKey);
if (platform == null) {
throw new IOException("Data table is corrupt. Missing platform: " + platformKey);
}
dataType = space.dataTypeManager.getDataType(dataTypeID);
@ -102,12 +102,12 @@ public class DBTraceData extends AbstractDBTraceCodeUnit<DBTraceData>
return this;
}
protected void set(DBTraceGuestPlatform platform, DataType dataType) {
this.platformKey = (int) (platform == null ? -1 : platform.getKey());
protected void set(InternalTracePlatform platform, DataType dataType) {
this.platformKey = platform.getIntKey();
this.dataTypeID = space.dataTypeManager.getResolvedID(dataType);
update(PLATFORM_COLUMN, DATATYPE_COLUMN);
this.guest = platform;
this.platform = platform;
// Use the stored dataType, not the given one, in case it's different
this.dataType = space.dataTypeManager.getDataType(dataTypeID);
assert this.dataType != null;
@ -133,8 +133,8 @@ public class DBTraceData extends AbstractDBTraceCodeUnit<DBTraceData>
}
@Override
public TraceGuestPlatform getGuestPlatform() {
return guest;
public TracePlatform getPlatform() {
return platform;
}
@Override
@ -157,7 +157,7 @@ public class DBTraceData extends AbstractDBTraceCodeUnit<DBTraceData>
@Override
public Language getLanguage() {
return guest == null ? space.baseLanguage : guest.getLanguage();
return platform.getLanguage();
}
@Override

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -15,46 +15,15 @@
*/
package ghidra.trace.model.guest;
import ghidra.program.model.address.*;
import ghidra.program.model.lang.*;
import ghidra.program.model.mem.MemBuffer;
import ghidra.trace.model.Trace;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressOverflowException;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
public interface TraceGuestPlatform {
/**
* Get the trace
*
* @return the trace
*/
Trace getTrace();
public interface TraceGuestPlatform extends TracePlatform {
/**
* Get the language of the guest platform
*
* @return the language
*/
Language getLanguage();
/**
* Get the address factory of the guest platform
*
* @return the factory
*/
default AddressFactory getAddressFactory() {
return getLanguage().getAddressFactory();
}
/**
* Get the compiler of the guest platform
*
* @return the compiler spec
*/
CompilerSpec getCompilerSpec();
/**
* Add an adress mapping from host to guest
* Add an address mapping from host to guest
*
* @param hostStart the starting host address (mapped to guestStart)
* @param guestStart the starting guest address (mapped to hostStart)
@ -65,66 +34,6 @@ public interface TraceGuestPlatform {
TraceGuestPlatformMappedRange addMappedRange(Address hostStart, Address guestStart, long length)
throws AddressOverflowException;
/**
* Get the addresses in the host which are mapped to somewhere in the guest
*
* @return the address set
*/
AddressSetView getHostAddressSet();
/**
* Get the addresses in the guest which are mapped to somehere in the host
*
* @return the address set
*/
AddressSetView getGuestAddressSet();
/**
* Map an address from host to guest
*
* @param hostAddress the host address
* @return the guest address
*/
Address mapHostToGuest(Address hostAddress);
/**
* Map an address from guest to host
*
* @param guestAddress the guest address
* @return the host address
*/
Address mapGuestToHost(Address guestAddress);
/**
* Get a memory buffer, which presents the host bytes in the guest address space
*
* <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
*/

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 ghidra.program.model.lang.CompilerSpec;
import ghidra.program.model.lang.Language;
/**
* Allows the addition of "guest platforms" for disassembling in multiple languages.
@ -28,18 +27,11 @@ import ghidra.program.model.lang.Language;
*/
public interface TracePlatformManager {
/**
* Get the base language of the trace
* Get a platform representing the trace's base language and compiler spec
*
* @return the language
* @return the host platform
*/
Language getBaseLanguage();
/**
* Get the base compiler spec of the trace
*
* @return the compiler spec
*/
CompilerSpec getBaseCompilerSpec();
TracePlatform getHostPlatform();
/**
* Add a guest platform
@ -50,12 +42,12 @@ public interface TracePlatformManager {
TraceGuestPlatform addGuestPlatform(CompilerSpec compilerSpec);
/**
* Get the guest platform for the given compiler spec
* Get the platform for the given compiler spec
*
* @param compilerSpec the compiler spec. For the base compiler spec, this will return null.
* @param compilerSpec the compiler spec
* @return the platform, if found, or null
*/
TraceGuestPlatform getGuestPlatform(CompilerSpec compilerSpec);
TracePlatform getPlatform(CompilerSpec compilerSpec);
/**
* Get or add a platform for the given compiler spec

View file

@ -18,6 +18,7 @@ package ghidra.trace.model.listing;
import com.google.common.collect.Range;
import ghidra.program.model.address.*;
import ghidra.trace.model.Trace;
import ghidra.trace.model.TraceAddressSnapRange;
import ghidra.util.IntersectionAddressSetView;
import ghidra.util.UnionAddressSetView;
@ -30,6 +31,13 @@ import ghidra.util.UnionAddressSetView;
*/
public interface TraceBaseCodeUnitsView<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
*

View file

@ -25,7 +25,7 @@ import ghidra.program.model.listing.CodeUnit;
import ghidra.program.model.util.TypeMismatchException;
import ghidra.trace.model.Trace;
import ghidra.trace.model.TraceAddressSnapRange;
import ghidra.trace.model.guest.TraceGuestPlatform;
import ghidra.trace.model.guest.TracePlatform;
import ghidra.trace.model.program.TraceProgramView;
import ghidra.trace.model.symbol.TraceReference;
import ghidra.trace.model.thread.TraceThread;
@ -44,11 +44,11 @@ public interface TraceCodeUnit extends CodeUnit {
Trace getTrace();
/**
* If the unit is for a guest platform, get it
* Get the platform for this unit
*
* @return the guest platform, or null if it's for the host platform
* @return the platform
*/
TraceGuestPlatform getGuestPlatform();
TracePlatform getPlatform();
@Override
TraceProgramView getProgram();

View file

@ -21,7 +21,7 @@ import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.lang.*;
import ghidra.program.model.util.CodeUnitInsertionException;
import ghidra.trace.model.guest.TraceGuestPlatform;
import ghidra.trace.model.guest.TracePlatform;
public interface TraceInstructionsView extends TraceBaseDefinedUnitsView<TraceInstruction> {
/**
@ -29,16 +29,28 @@ public interface TraceInstructionsView extends TraceBaseDefinedUnitsView<TraceIn
*
* @param lifespan the lifespan for the instruction unit
* @param address the starting address of the instruction
* @param platform the optional guest platform, null for the host
* @param platform the platform
* @param prototype the instruction prototype
* @param context the input disassembly context for the instruction
* @return the new instruction
* @throws CodeUnitInsertionException if the instruction cannot be created
*/
TraceInstruction create(Range<Long> lifespan, Address address, TraceGuestPlatform platform,
TraceInstruction create(Range<Long> lifespan, Address address, TracePlatform platform,
InstructionPrototype prototype, ProcessorContextView context)
throws CodeUnitInsertionException;
/**
* Create an instruction for the host platform
*
* @see #create(Range, Address, TracePlatform, InstructionPrototype, ProcessorContextView)
*/
default TraceInstruction create(Range<Long> lifespan, Address address,
InstructionPrototype prototype, ProcessorContextView context)
throws CodeUnitInsertionException {
return create(lifespan, address, getTrace().getPlatformManager().getHostPlatform(),
prototype, context);
}
/**
* Create several instructions
*
@ -52,6 +64,17 @@ public interface TraceInstructionsView extends TraceBaseDefinedUnitsView<TraceIn
* @param overwrite true to replace conflicting instructions
* @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);
/**
* 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.model.*;
import ghidra.trace.model.guest.TraceGuestPlatform;
import ghidra.trace.model.guest.TracePlatform;
import ghidra.trace.model.thread.TraceThread;
import ghidra.util.Msg;
import ghidra.util.database.DBOpenMode;
@ -62,6 +63,7 @@ import ghidra.util.task.ConsoleTaskMonitor;
public class ToyDBTraceBuilder implements AutoCloseable {
public final Language language;
public final DBTrace trace;
public final TracePlatform host;
public final LanguageService languageService = DefaultLanguageService.getLanguageService();
public ToyDBTraceBuilder(File file)
@ -69,17 +71,20 @@ public class ToyDBTraceBuilder implements AutoCloseable {
DBHandle handle = new DBHandle(file);
this.trace = new DBTrace(handle, DBOpenMode.UPDATE, new ConsoleTaskMonitor(), this);
this.language = trace.getBaseLanguage();
this.host = trace.getPlatformManager().getHostPlatform();
}
// TODO: A constructor for specifying compiler, too
public ToyDBTraceBuilder(String name, String langID) throws IOException {
this.language = languageService.getLanguage(new LanguageID(langID));
this.trace = new DBTrace(name, language.getDefaultCompilerSpec(), this);
this.host = trace.getPlatformManager().getHostPlatform();
}
public ToyDBTraceBuilder(Trace trace) {
this.language = trace.getBaseLanguage();
this.trace = (DBTrace) trace;
this.host = trace.getPlatformManager().getHostPlatform();
trace.addConsumer(this);
}
@ -102,7 +107,7 @@ public class ToyDBTraceBuilder implements AutoCloseable {
return addr(language, offset);
}
public Address addr(TraceGuestPlatform lang, long offset) {
public Address addr(TracePlatform lang, long offset) {
return lang.getLanguage().getDefaultSpace().getAddress(offset);
}
@ -246,36 +251,28 @@ public class ToyDBTraceBuilder implements AutoCloseable {
}
public DBTraceInstruction addInstruction(long snap, Address start,
TraceGuestPlatform guest) throws CodeUnitInsertionException {
DBTraceMemoryManager memory = trace.getMemoryManager();
TracePlatform platform) throws CodeUnitInsertionException {
DBTraceCodeManager code = trace.getCodeManager();
Language language = guest == null ? this.language : guest.getLanguage();
Language language = platform.getLanguage();
Disassembler dis = Disassembler.getDisassembler(language, language.getAddressFactory(),
new ConsoleTaskMonitor(), msg -> Msg.info(this, "Listener: " + msg));
RegisterValue defaultContextValue = trace.getRegisterContextManager()
.getDefaultContext(language)
.getDefaultDisassemblyContext();
MemBuffer memBuf;
if (guest == null) {
memBuf = memory.getBufferAt(snap, start);
}
else {
memBuf = guest.getMappedMemBuffer(snap, guest.mapHostToGuest(start));
}
MemBuffer memBuf = platform.getMappedMemBuffer(snap, platform.mapHostToGuest(start));
InstructionBlock block = dis.pseudoDisassembleBlock(memBuf, defaultContextValue, 1);
Instruction pseudoIns = block.iterator().next();
return code.instructions()
.create(Range.atLeast(snap), start, guest, pseudoIns.getPrototype(), pseudoIns);
.create(Range.atLeast(snap), start, platform, pseudoIns.getPrototype(), pseudoIns);
}
public DBTraceInstruction addInstruction(long snap, Address start,
TraceGuestPlatform guest, ByteBuffer buf)
throws CodeUnitInsertionException {
public DBTraceInstruction addInstruction(long snap, Address start, TracePlatform platform,
ByteBuffer buf) throws CodeUnitInsertionException {
int length = buf.remaining();
DBTraceMemoryManager memory = trace.getMemoryManager();
memory.putBytes(snap, start, buf);
DBTraceInstruction instruction = addInstruction(snap, start, guest);
DBTraceInstruction instruction = addInstruction(snap, start, platform);
assertEquals(length, instruction.getLength());
return instruction;
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -57,6 +57,7 @@ public abstract class AbstractPatchAction extends DockingAction {
private Program program;
private Address address;
private CodeUnit codeUnit;
private final KeyListener listenerForKeys = new KeyAdapter() {
@Override
@ -165,6 +166,15 @@ public abstract class AbstractPatchAction extends DockingAction {
return program;
}
/**
* Get the code unit on which this action was invoked
*
* @return the current code unit
*/
protected CodeUnit getCodeUnit() {
return codeUnit;
}
/**
* Get the address at which this action was invoked
*
@ -276,14 +286,12 @@ public abstract class AbstractPatchAction extends DockingAction {
@Override
public boolean isAddToPopup(ActionContext context) {
if (!(context instanceof ListingActionContext)) {
CodeUnit cu = getCodeUnit(context);
if (cu == null || !isApplicableToUnit(cu)) {
return false;
}
ListingActionContext lac = (ListingActionContext) context;
if (!isApplicableToUnit(lac.getCodeUnit())) {
return false;
}
ComponentProvider provider = lac.getComponentProvider();
if (!(provider instanceof CodeViewerProvider)) {
@ -313,10 +321,9 @@ public abstract class AbstractPatchAction extends DockingAction {
/**
* Perform preparation and save any information needed later in {@link #accept()}.
*
* @param unit the code unit at the user's cursor
*/
protected abstract void prepare(CodeUnit unit);
protected void prepare() {
}
/**
* Put the input fields in their place, show them, and place focus appropriately
@ -333,14 +340,26 @@ public abstract class AbstractPatchAction extends DockingAction {
/**
* Pre-fill the input fields and place the caret appropriately (usually at the end)
*
* @param unit the code unit at the user's cursor
*/
protected abstract void fillInputs(CodeUnit unit);
protected abstract void fillInputs();
protected CodeUnit getCodeUnit(ActionContext context) {
if (!(context instanceof ListingActionContext)) {
return null;
}
ListingActionContext lac = (ListingActionContext) context;
prepareLayout(lac);
if (codeViewerProvider.isReadOnly()) {
return null;
}
return lac.getCodeUnit();
}
@Override
public void actionPerformed(ActionContext context) {
if (!(context instanceof ListingActionContext)) {
codeUnit = getCodeUnit(context);
if (codeUnit == null || !isApplicableToUnit(codeUnit)) {
return;
}
@ -349,10 +368,6 @@ public abstract class AbstractPatchAction extends DockingAction {
if (codeViewerProvider.isReadOnly()) {
return;
}
CodeUnit cu = lac.getCodeUnit();
if (!isApplicableToUnit(cu)) {
return;
}
ProgramLocation cur = lac.getLocation();
program = cur.getProgram();
@ -365,7 +380,7 @@ public abstract class AbstractPatchAction extends DockingAction {
return;
}
prepare(cu);
prepare();
ToolOptions displayOptions = tool.getOptions(GhidraOptions.CATEGORY_BROWSER_DISPLAY);
Font font = displayOptions.getFont(GhidraOptions.OPTION_BASE_FONT, null);
@ -377,7 +392,7 @@ public abstract class AbstractPatchAction extends DockingAction {
if (!showInputs(fieldPanel)) {
return;
}
fillInputs(cu);
fillInputs();
fieldLayoutManager.layoutContainer(fieldPanel);
}

View file

@ -15,7 +15,9 @@
*/
package ghidra.app.plugin.core.assembler;
import docking.ActionContext;
import ghidra.app.CorePluginPackage;
import ghidra.app.context.ListingActionContext;
import ghidra.app.plugin.PluginCategoryNames;
import ghidra.app.plugin.ProgramPlugin;
import ghidra.app.plugin.assembler.Assemblers;
@ -62,10 +64,29 @@ public class AssemblerPlugin extends ProgramPlugin {
}
private void createActions() {
patchInstructionAction = new PatchInstructionAction(this, "Patch Instruction");
// Debugger provides its own "Patch Instruction" action
patchInstructionAction = new PatchInstructionAction(this) {
@Override
public boolean isEnabledForContext(ActionContext context) {
if (!super.isEnabledForContext(context)) {
return false;
}
ListingActionContext lac = (ListingActionContext) context;
return !lac.getNavigatable().isDynamic();
}
@Override
public boolean isAddToPopup(ActionContext context) {
if (!super.isAddToPopup(context)) {
return false;
}
ListingActionContext lac = (ListingActionContext) context;
return !lac.getNavigatable().isDynamic();
}
};
tool.addAction(patchInstructionAction);
patchDataAction = new PatchDataAction(this, "Patch Data");
patchDataAction = new PatchDataAction(this);
tool.addAction(patchDataAction);
}

View file

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

View file

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

View file

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

View file

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -31,11 +30,4 @@ public class CodeViewerActionContext extends ListingActionContext implements
super(provider, provider, location);
}
/**
* @return true if underlying code viewer corresponds to a dynamic listing
*/
public boolean isDyanmicListing() {
return ((CodeViewerProvider) getComponentProvider()).isDynamicListing();
}
}

View file

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

View file

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

View file

@ -702,6 +702,10 @@ public class AssemblyResolvedPatterns extends AssemblyResolution {
* @return the iterable
*/
public Iterable<byte[]> possibleInsVals(AssemblyPatternBlock forCtx) {
AssemblyPatternBlock ctxCompat = ctx.combine(forCtx);
if (ctxCompat == null) {
return List.of();
}
Predicate<byte[]> removeForbidden = (byte[] val) -> {
for (AssemblyResolvedPatterns f : forbids) {
// 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");
}
@Test
public void testAssemble_T_movs_r0_r0() {
assertOneCompatRestExact("movs r0,r0", "00:00", THUMB, 0x00400000, "movs r0,r0");
}
@Test
public void testAssemble_T_bl_0x00008000() {
// What makes this different from the above test is that it jumps backward

View file

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