GP-2099: Add Disassemble and Patch actions for polyglot traces.

This commit is contained in:
Dan 2022-06-24 13:06:20 -04:00
parent 78de8e6d49
commit bec99cd71e
66 changed files with 2214 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 -> {
LanguageCompilerSpecPair lcsp = ent.getKey();
String regs = StringUtils.join(
ent.getValue().stream().map(Register::getName).collect(Collectors.toList()), ',');
return lcsp.languageID + "/" + lcsp.compilerSpecID + ":" + regs;
}).collect(Collectors.toList()), ';');
return setsByCSpec.entrySet()
.stream()
.map(ent -> {
LanguageCompilerSpecPair lcsp = ent.getKey();
return lcsp.languageID + "/" + lcsp.compilerSpecID + ":" + ent.getValue()
.stream()
.filter(r -> r != null)
.map(Register::getName)
.collect(Collectors.joining(","));
})
.collect(Collectors.joining(";"));
}
@Override

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,14 +55,26 @@ public class DefaultDebuggerPlatformMapper extends AbstractDebuggerPlatformMappe
@Override
public void addToTrace(long snap) {
TracePlatformManager platformManager = trace.getPlatformManager();
TraceGuestPlatform platform = platformManager.getOrAddGuestPlatform(cSpec);
if (platform == null) {
return; // It's the host compiler spec
try (UndoableTransaction tid = UndoableTransaction.start(trace, "Add guest " +
cSpec.getLanguage().getLanguageDescription() + "/" + cSpec.getCompilerSpecDescription(),
true)) {
TracePlatformManager platformManager = trace.getPlatformManager();
TraceGuestPlatform platform = platformManager.getOrAddGuestPlatform(cSpec);
if (platform == null) {
return; // It's the host compiler spec
}
addMappedRanges(platform);
}
addMappedRanges(platform);
}
/**
* Add mapped ranges if not already present
*
* <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

@ -589,6 +589,8 @@ public abstract class AbstractGhidraHeadedDebuggerGUITest
program.release(this);
}
waitForSwing();
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,27 +25,41 @@ import ghidra.trace.model.target.TraceObject;
public class TestDebuggerPlatformOpinion extends AbstractDebuggerPlatformOpinion {
enum Offers implements DebuggerPlatformOffer {
X86_64 {
@Override
public String getDescription() {
return "Test x86-64";
}
ARM_V8_LE("Test armv8le", "ARM:LE:32:v8", "default"),
X86_64("Test x86-64", "x86:LE:64:default", "gcc");
@Override
public int getConfidence() {
return 1;
}
private final String description;
private final LanguageCompilerSpecPair lcsp;
@Override
public CompilerSpec getCompilerSpec() {
return getCompilerSpec(new LanguageID("x86:LE:64:default"), null);
}
private Offers(String description, String langID, String cSpecID) {
this.description = description;
this.lcsp = new LanguageCompilerSpecPair(langID, cSpecID);
}
@Override
public DebuggerPlatformMapper take(PluginTool tool, Trace trace) {
return new DefaultDebuggerPlatformMapper(tool, trace, getCompilerSpec());
@Override
public String getDescription() {
return description;
}
@Override
public int getConfidence() {
return 1;
}
@Override
public CompilerSpec getCompilerSpec() {
try {
return lcsp.getCompilerSpec();
}
};
catch (LanguageNotFoundException | CompilerSpecNotFoundException e) {
throw new AssertionError(e);
}
}
@Override
public DebuggerPlatformMapper take(PluginTool tool, Trace trace) {
return new DefaultDebuggerPlatformMapper(tool, trace, getCompilerSpec());
}
}
@Override
@ -54,9 +68,12 @@ public class TestDebuggerPlatformOpinion extends AbstractDebuggerPlatformOpinion
if (!"test".equals(debugger)) {
return Set.of();
}
if (!"x86-64".equals(arch)) {
return Set.of();
if ("armv8le".equals(arch)) {
return Set.of(Offers.ARM_V8_LE);
}
return Set.of(Offers.X86_64);
if ("x86-64".equals(arch)) {
return Set.of(Offers.X86_64);
}
return Set.of();
}
}

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