mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-05 19:42:36 +02:00
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:
commit
5098c04745
66 changed files with 2213 additions and 774 deletions
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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....
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
|
|
|
@ -396,6 +396,11 @@ public class DebuggerMemoryBytesProvider extends ProgramByteViewerComponentProvi
|
|||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDynamic() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean isMainViewer() {
|
||||
return isMainViewer;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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 -> {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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) {
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -591,6 +591,7 @@ public abstract class AbstractGhidraHeadedDebuggerGUITest
|
|||
}
|
||||
|
||||
waitForTasks();
|
||||
|
||||
env.dispose();
|
||||
}
|
||||
|
||||
|
|
|
@ -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 =
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)));
|
||||
});
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue