mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-04 18:29:37 +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 -> {
|
||||
return setsByCSpec.entrySet()
|
||||
.stream()
|
||||
.map(ent -> {
|
||||
LanguageCompilerSpecPair lcsp = ent.getKey();
|
||||
String regs = StringUtils.join(
|
||||
ent.getValue().stream().map(Register::getName).collect(Collectors.toList()), ',');
|
||||
return lcsp.languageID + "/" + lcsp.compilerSpecID + ":" + regs;
|
||||
}).collect(Collectors.toList()), ';');
|
||||
return lcsp.languageID + "/" + lcsp.compilerSpecID + ":" + ent.getValue()
|
||||
.stream()
|
||||
.filter(r -> r != null)
|
||||
.map(Register::getName)
|
||||
.collect(Collectors.joining(","));
|
||||
})
|
||||
.collect(Collectors.joining(";"));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -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,6 +55,9 @@ public class DefaultDebuggerPlatformMapper extends AbstractDebuggerPlatformMappe
|
|||
|
||||
@Override
|
||||
public void addToTrace(long snap) {
|
||||
try (UndoableTransaction tid = UndoableTransaction.start(trace, "Add guest " +
|
||||
cSpec.getLanguage().getLanguageDescription() + "/" + cSpec.getCompilerSpecDescription(),
|
||||
true)) {
|
||||
TracePlatformManager platformManager = trace.getPlatformManager();
|
||||
TraceGuestPlatform platform = platformManager.getOrAddGuestPlatform(cSpec);
|
||||
if (platform == null) {
|
||||
|
@ -61,7 +65,16 @@ public class DefaultDebuggerPlatformMapper extends AbstractDebuggerPlatformMappe
|
|||
}
|
||||
addMappedRanges(platform);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add mapped ranges if not already present
|
||||
*
|
||||
* <p>
|
||||
* A transaction is already started when this method is invoked.
|
||||
*
|
||||
* @param platform the platform
|
||||
*/
|
||||
protected void addMappedRanges(TraceGuestPlatform platform) {
|
||||
Trace trace = platform.getTrace();
|
||||
AddressSpace hostSpace = trace.getBaseAddressFactory().getDefaultAddressSpace();
|
||||
|
|
|
@ -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,10 +25,20 @@ import ghidra.trace.model.target.TraceObject;
|
|||
public class TestDebuggerPlatformOpinion extends AbstractDebuggerPlatformOpinion {
|
||||
|
||||
enum Offers implements DebuggerPlatformOffer {
|
||||
X86_64 {
|
||||
ARM_V8_LE("Test armv8le", "ARM:LE:32:v8", "default"),
|
||||
X86_64("Test x86-64", "x86:LE:64:default", "gcc");
|
||||
|
||||
private final String description;
|
||||
private final LanguageCompilerSpecPair lcsp;
|
||||
|
||||
private Offers(String description, String langID, String cSpecID) {
|
||||
this.description = description;
|
||||
this.lcsp = new LanguageCompilerSpecPair(langID, cSpecID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Test x86-64";
|
||||
return description;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -38,14 +48,18 @@ public class TestDebuggerPlatformOpinion extends AbstractDebuggerPlatformOpinion
|
|||
|
||||
@Override
|
||||
public CompilerSpec getCompilerSpec() {
|
||||
return getCompilerSpec(new LanguageID("x86:LE:64:default"), null);
|
||||
try {
|
||||
return lcsp.getCompilerSpec();
|
||||
}
|
||||
catch (LanguageNotFoundException | CompilerSpecNotFoundException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public DebuggerPlatformMapper take(PluginTool tool, Trace trace) {
|
||||
return new DefaultDebuggerPlatformMapper(tool, trace, getCompilerSpec());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -54,9 +68,12 @@ public class TestDebuggerPlatformOpinion extends AbstractDebuggerPlatformOpinion
|
|||
if (!"test".equals(debugger)) {
|
||||
return Set.of();
|
||||
}
|
||||
if (!"x86-64".equals(arch)) {
|
||||
return Set.of();
|
||||
if ("armv8le".equals(arch)) {
|
||||
return Set.of(Offers.ARM_V8_LE);
|
||||
}
|
||||
if ("x86-64".equals(arch)) {
|
||||
return Set.of(Offers.X86_64);
|
||||
}
|
||||
return Set.of();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)));
|
||||
});
|
||||
}
|
||||
}
|
|
@ -42,7 +42,8 @@ import ghidra.util.exception.VersionException;
|
|||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
@DBAnnotatedObjectInfo(version = 0)
|
||||
public class DBTraceGuestPlatform extends DBAnnotatedObject implements TraceGuestPlatform {
|
||||
public class DBTraceGuestPlatform extends DBAnnotatedObject
|
||||
implements TraceGuestPlatform, InternalTracePlatform {
|
||||
public static final String TABLE_NAME = "Platforms";
|
||||
|
||||
@DBAnnotatedObjectInfo(version = 0)
|
||||
|
@ -160,6 +161,16 @@ public class DBTraceGuestPlatform extends DBAnnotatedObject implements TraceGues
|
|||
return manager.trace;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getIntKey() {
|
||||
return (int) key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isGuest() {
|
||||
return true;
|
||||
}
|
||||
|
||||
protected void deleteMappedRange(DBTraceGuestPlatformMappedRange range, TaskMonitor monitor)
|
||||
throws CancelledException {
|
||||
try (LockHold hold = LockHold.lock(manager.lock.writeLock())) {
|
||||
|
@ -175,6 +186,7 @@ public class DBTraceGuestPlatform extends DBAnnotatedObject implements TraceGues
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Internal
|
||||
public DBTraceGuestLanguage getLanguageEntry() {
|
||||
return languageEntry;
|
||||
|
|
|
@ -68,7 +68,7 @@ public class DBTraceGuestPlatformMappedRange extends DBAnnotatedObject
|
|||
private DBTracePlatformManager manager;
|
||||
|
||||
private AddressRangeImpl hostRange;
|
||||
private DBTraceGuestPlatform guestPlatform;
|
||||
private DBTraceGuestPlatform platform;
|
||||
private AddressRangeImpl guestRange;
|
||||
|
||||
public DBTraceGuestPlatformMappedRange(DBTracePlatformManager manager, DBCachedObjectStore<?> s,
|
||||
|
@ -83,28 +83,27 @@ public class DBTraceGuestPlatformMappedRange extends DBAnnotatedObject
|
|||
if (created) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
Address hostStart =
|
||||
manager.getBaseLanguage().getAddressFactory().getAddress(hostSpace, hostOffset);
|
||||
Address hostEnd = hostStart.addNoWrap(length - 1);
|
||||
manager.trace.getBaseLanguage()
|
||||
.getAddressFactory()
|
||||
.getAddress(hostSpace, hostOffset);
|
||||
Address hostEnd = hostStart.addWrap(length - 1);
|
||||
this.hostRange = new AddressRangeImpl(hostStart, hostEnd);
|
||||
|
||||
this.guestPlatform = manager.getPlatformByKey(guestPlatformKey);
|
||||
Address guestStart =
|
||||
guestPlatform.getAddressFactory().getAddress(guestSpace, guestOffset);
|
||||
Address guestEnd = guestStart.addNoWrap(length - 1);
|
||||
InternalTracePlatform platform = manager.getPlatformByKey(guestPlatformKey);
|
||||
if (platform.isHost()) {
|
||||
throw new IOException("Table is corrupt. Got host platform in guest mapping.");
|
||||
}
|
||||
this.platform = (DBTraceGuestPlatform) platform;
|
||||
Address guestStart = platform.getAddressFactory().getAddress(guestSpace, guestOffset);
|
||||
Address guestEnd = guestStart.addWrap(length - 1);
|
||||
this.guestRange = new AddressRangeImpl(guestStart, guestEnd);
|
||||
}
|
||||
catch (AddressOverflowException e) {
|
||||
throw new RuntimeException("Database is corrupt or languages changed", e);
|
||||
}
|
||||
}
|
||||
|
||||
void set(Address hostStart, DBTraceGuestPlatform guestPlatform, Address guestStart,
|
||||
long length) {
|
||||
void set(Address hostStart, DBTraceGuestPlatform platform, Address guestStart, long length) {
|
||||
this.hostSpace = hostStart.getAddressSpace().getSpaceID();
|
||||
this.hostOffset = hostStart.getOffset();
|
||||
this.guestPlatformKey = (int) guestPlatform.getKey();
|
||||
this.guestPlatformKey = (int) platform.getKey();
|
||||
this.guestSpace = guestStart.getAddressSpace().getSpaceID();
|
||||
this.guestOffset = guestStart.getOffset();
|
||||
this.length = length;
|
||||
|
@ -112,19 +111,19 @@ public class DBTraceGuestPlatformMappedRange extends DBAnnotatedObject
|
|||
GUEST_OFFSET_COLUMN, LENGTH_COLUMN);
|
||||
|
||||
this.hostRange = new AddressRangeImpl(hostStart, hostStart.addWrap(length - 1));
|
||||
this.guestPlatform = guestPlatform;
|
||||
this.platform = platform;
|
||||
this.guestRange = new AddressRangeImpl(guestStart, guestStart.addWrap(length - 1));
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Language getHostLanguage() {
|
||||
return manager.getBaseLanguage();
|
||||
return manager.trace.getBaseLanguage();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompilerSpec getHostCompilerSpec() {
|
||||
return manager.getBaseCompilerSpec();
|
||||
return manager.trace.getBaseCompilerSpec();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -134,7 +133,7 @@ public class DBTraceGuestPlatformMappedRange extends DBAnnotatedObject
|
|||
|
||||
@Override
|
||||
public DBTraceGuestPlatform getGuestPlatform() {
|
||||
return guestPlatform;
|
||||
return platform;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -21,14 +21,15 @@ import java.util.concurrent.locks.ReadWriteLock;
|
|||
|
||||
import db.DBHandle;
|
||||
import ghidra.lifecycle.Internal;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressSetView;
|
||||
import ghidra.program.model.lang.*;
|
||||
import ghidra.program.model.listing.Instruction;
|
||||
import ghidra.program.model.mem.MemBuffer;
|
||||
import ghidra.trace.database.DBTrace;
|
||||
import ghidra.trace.database.DBTraceManager;
|
||||
import ghidra.trace.database.guest.DBTraceGuestPlatform.DBTraceGuestLanguage;
|
||||
import ghidra.trace.model.guest.TraceGuestPlatform;
|
||||
import ghidra.trace.model.guest.TracePlatformManager;
|
||||
import ghidra.trace.model.listing.TraceInstruction;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.guest.*;
|
||||
import ghidra.util.LockHold;
|
||||
import ghidra.util.database.*;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
|
@ -57,6 +58,68 @@ public class DBTracePlatformManager implements DBTraceManager, TracePlatformMana
|
|||
|
||||
protected final DBCachedObjectStore<DBTraceGuestPlatformMappedRange> rangeMappingStore;
|
||||
|
||||
protected final InternalTracePlatform hostPlatform = new InternalTracePlatform() {
|
||||
@Override
|
||||
public Trace getTrace() {
|
||||
return trace;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DBTraceGuestLanguage getLanguageEntry() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getIntKey() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isGuest() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Language getLanguage() {
|
||||
return trace.getBaseLanguage();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompilerSpec getCompilerSpec() {
|
||||
return trace.getBaseCompilerSpec();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressSetView getHostAddressSet() {
|
||||
return trace.getBaseAddressFactory().getAddressSet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressSetView getGuestAddressSet() {
|
||||
return trace.getBaseAddressFactory().getAddressSet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Address mapHostToGuest(Address hostAddress) {
|
||||
return hostAddress;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Address mapGuestToHost(Address guestAddress) {
|
||||
return guestAddress;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemBuffer getMappedMemBuffer(long snap, Address guestAddress) {
|
||||
return trace.getMemoryManager().getBufferAt(snap, guestAddress);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InstructionSet mapGuestInstructionAddressesToHost(InstructionSet set) {
|
||||
return set;
|
||||
}
|
||||
};
|
||||
|
||||
public DBTracePlatformManager(DBHandle dbh, DBOpenMode openMode, ReadWriteLock lock,
|
||||
TaskMonitor monitor, CompilerSpec baseCompilerSpec, DBTrace trace)
|
||||
throws VersionException, IOException {
|
||||
|
@ -129,9 +192,9 @@ public class DBTracePlatformManager implements DBTraceManager, TracePlatformMana
|
|||
}
|
||||
|
||||
@Internal
|
||||
public DBTraceGuestPlatform getPlatformByKey(int key) {
|
||||
public InternalTracePlatform getPlatformByKey(int key) {
|
||||
if (key == -1) {
|
||||
return null;
|
||||
return hostPlatform;
|
||||
}
|
||||
return platformStore.getObjectAt(key);
|
||||
}
|
||||
|
@ -201,13 +264,8 @@ public class DBTracePlatformManager implements DBTraceManager, TracePlatformMana
|
|||
}
|
||||
|
||||
@Override
|
||||
public Language getBaseLanguage() {
|
||||
return trace.getBaseLanguage();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompilerSpec getBaseCompilerSpec() {
|
||||
return trace.getBaseCompilerSpec();
|
||||
public InternalTracePlatform getHostPlatform() {
|
||||
return hostPlatform;
|
||||
}
|
||||
|
||||
protected DBTraceGuestPlatform doAddGuestPlatform(CompilerSpec compilerSpec) {
|
||||
|
@ -219,9 +277,9 @@ public class DBTracePlatformManager implements DBTraceManager, TracePlatformMana
|
|||
|
||||
@Override
|
||||
public DBTraceGuestPlatform addGuestPlatform(CompilerSpec compilerSpec) {
|
||||
if (compilerSpec.getCompilerSpecID()
|
||||
.equals(trace.getBaseCompilerSpec().getCompilerSpecID())) {
|
||||
throw new IllegalArgumentException("Base language cannot be a guest language");
|
||||
if (trace.getBaseCompilerSpec() == compilerSpec) {
|
||||
throw new IllegalArgumentException(
|
||||
"Base compiler spec cannot be a guest compiler spec");
|
||||
}
|
||||
try (LockHold hold = LockHold.lock(lock.writeLock())) {
|
||||
return doAddGuestPlatform(compilerSpec);
|
||||
|
@ -229,8 +287,11 @@ public class DBTracePlatformManager implements DBTraceManager, TracePlatformMana
|
|||
}
|
||||
|
||||
@Override
|
||||
public DBTraceGuestPlatform getGuestPlatform(CompilerSpec compilerSpec) {
|
||||
public InternalTracePlatform getPlatform(CompilerSpec compilerSpec) {
|
||||
try (LockHold hold = LockHold.lock(lock.readLock())) {
|
||||
if (trace.getBaseCompilerSpec() == compilerSpec) {
|
||||
return hostPlatform;
|
||||
}
|
||||
return platformsByCompiler.get(compilerSpec);
|
||||
}
|
||||
}
|
||||
|
@ -255,33 +316,10 @@ public class DBTracePlatformManager implements DBTraceManager, TracePlatformMana
|
|||
return platformView;
|
||||
}
|
||||
|
||||
protected TraceGuestPlatform getPlatformOf(InstructionSet instructionSet) {
|
||||
for (InstructionBlock block : instructionSet) {
|
||||
for (Instruction instruction : block) {
|
||||
if (!(instruction instanceof TraceInstruction)) {
|
||||
continue;
|
||||
}
|
||||
TraceInstruction traceInstruction = (TraceInstruction) instruction;
|
||||
return traceInstruction.getGuestPlatform();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public InstructionSet mapGuestInstructionAddressesToHost(TraceGuestPlatform platform,
|
||||
InstructionSet instructionSet) {
|
||||
try (LockHold hold = LockHold.lock(lock.readLock())) {
|
||||
if (platform == null) { // Instructions belong to the host platform
|
||||
return instructionSet;
|
||||
}
|
||||
return platform.mapGuestInstructionAddressesToHost(instructionSet);
|
||||
}
|
||||
}
|
||||
|
||||
@Internal
|
||||
public DBTraceGuestPlatform assertMine(TraceGuestPlatform platform) {
|
||||
if (platform == null) {
|
||||
return null;
|
||||
public InternalTracePlatform assertMine(TracePlatform platform) {
|
||||
if (platform == hostPlatform) {
|
||||
return hostPlatform;
|
||||
}
|
||||
if (!(platform instanceof DBTraceGuestPlatform)) {
|
||||
throw new IllegalArgumentException("Given platform does not belong to this trace");
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.trace.database.guest;
|
||||
|
||||
import ghidra.trace.database.guest.DBTraceGuestPlatform.DBTraceGuestLanguage;
|
||||
import ghidra.trace.model.guest.TracePlatform;
|
||||
|
||||
public interface InternalTracePlatform extends TracePlatform {
|
||||
/**
|
||||
* Get the entry's key in the table as an integer
|
||||
*
|
||||
* @return the key
|
||||
*/
|
||||
int getIntKey();
|
||||
|
||||
DBTraceGuestLanguage getLanguageEntry();
|
||||
}
|
|
@ -26,6 +26,7 @@ import generic.NestedIterator;
|
|||
import ghidra.program.model.address.*;
|
||||
import ghidra.trace.database.DBTraceUtils;
|
||||
import ghidra.trace.database.space.DBTraceDelegatingManager;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.TraceAddressSnapRange;
|
||||
import ghidra.util.LockHold;
|
||||
|
||||
|
@ -40,6 +41,10 @@ public abstract class AbstractBaseDBTraceCodeUnitsMemoryView<T extends DBTraceCo
|
|||
Collections2.transform(manager.getActiveMemorySpaces(), this::getView);
|
||||
}
|
||||
|
||||
public Trace getTrace() {
|
||||
return manager.getTrace();
|
||||
}
|
||||
|
||||
protected abstract M getView(DBTraceCodeSpace space);
|
||||
|
||||
protected T nullOrUndefined(long snap, Address address) {
|
||||
|
|
|
@ -19,6 +19,7 @@ import com.google.common.collect.Range;
|
|||
|
||||
import generic.NestedIterator;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.TraceAddressSnapRange;
|
||||
|
||||
public abstract class AbstractBaseDBTraceCodeUnitsView<T extends DBTraceCodeUnitAdapter> {
|
||||
|
@ -29,6 +30,10 @@ public abstract class AbstractBaseDBTraceCodeUnitsView<T extends DBTraceCodeUnit
|
|||
this.space = space;
|
||||
}
|
||||
|
||||
public Trace getTrace() {
|
||||
return space.manager.getTrace();
|
||||
}
|
||||
|
||||
public abstract int size();
|
||||
|
||||
public T getBefore(long snap, Address address) {
|
||||
|
|
|
@ -25,7 +25,7 @@ import ghidra.program.model.data.DataType;
|
|||
import ghidra.program.model.lang.Language;
|
||||
import ghidra.trace.database.DBTrace;
|
||||
import ghidra.trace.database.data.DBTraceDataSettingsAdapter.DBTraceDataSettingsSpace;
|
||||
import ghidra.trace.model.guest.TraceGuestPlatform;
|
||||
import ghidra.trace.model.guest.TracePlatform;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.util.LockHold;
|
||||
|
||||
|
@ -84,8 +84,8 @@ public abstract class AbstractDBTraceDataComponent implements DBTraceDefinedData
|
|||
}
|
||||
|
||||
@Override
|
||||
public TraceGuestPlatform getGuestPlatform() {
|
||||
return root.getGuestPlatform();
|
||||
public TracePlatform getPlatform() {
|
||||
return root.getPlatform();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -142,7 +142,7 @@ public class DBTraceCodeSpace implements TraceCodeSpace, DBTraceSpaceBased {
|
|||
TraceAddressSnapRangeQuery.intersecting(range, span)).values()) {
|
||||
monitor.checkCanceled();
|
||||
monitor.incrementProgress(1);
|
||||
if (instruction.guest != guest) {
|
||||
if (instruction.platform != guest) {
|
||||
continue;
|
||||
}
|
||||
instructionMapSpace.deleteData(instruction);
|
||||
|
@ -154,7 +154,7 @@ public class DBTraceCodeSpace implements TraceCodeSpace, DBTraceSpaceBased {
|
|||
TraceAddressSnapRangeQuery.intersecting(range, span)).values()) {
|
||||
monitor.checkCanceled();
|
||||
monitor.incrementProgress(1);
|
||||
if (dataUnit.guest != guest) {
|
||||
if (dataUnit.platform != guest) {
|
||||
continue;
|
||||
}
|
||||
// TODO: I don't yet have guest-language data units.
|
||||
|
|
|
@ -26,9 +26,9 @@ import ghidra.program.model.data.*;
|
|||
import ghidra.program.model.lang.Language;
|
||||
import ghidra.trace.database.DBTraceUtils;
|
||||
import ghidra.trace.database.data.DBTraceDataSettingsAdapter.DBTraceDataSettingsSpace;
|
||||
import ghidra.trace.database.guest.DBTraceGuestPlatform;
|
||||
import ghidra.trace.database.guest.InternalTracePlatform;
|
||||
import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree;
|
||||
import ghidra.trace.model.guest.TraceGuestPlatform;
|
||||
import ghidra.trace.model.guest.TracePlatform;
|
||||
import ghidra.util.LockHold;
|
||||
import ghidra.util.database.DBCachedObjectStore;
|
||||
import ghidra.util.database.DBObjectColumn;
|
||||
|
@ -56,7 +56,7 @@ public class DBTraceData extends AbstractDBTraceCodeUnit<DBTraceData>
|
|||
@DBAnnotatedField(column = DATATYPE_COLUMN_NAME)
|
||||
private long dataTypeID;
|
||||
|
||||
protected DBTraceGuestPlatform guest;
|
||||
protected InternalTracePlatform platform;
|
||||
protected DataType dataType;
|
||||
protected DataType baseDataType;
|
||||
protected Settings defaultSettings;
|
||||
|
@ -75,8 +75,8 @@ public class DBTraceData extends AbstractDBTraceCodeUnit<DBTraceData>
|
|||
if (created) {
|
||||
return;
|
||||
}
|
||||
guest = space.manager.platformManager.getPlatformByKey(platformKey);
|
||||
if (guest == null && platformKey != -1) {
|
||||
platform = space.manager.platformManager.getPlatformByKey(platformKey);
|
||||
if (platform == null) {
|
||||
throw new IOException("Data table is corrupt. Missing platform: " + platformKey);
|
||||
}
|
||||
dataType = space.dataTypeManager.getDataType(dataTypeID);
|
||||
|
@ -102,12 +102,12 @@ public class DBTraceData extends AbstractDBTraceCodeUnit<DBTraceData>
|
|||
return this;
|
||||
}
|
||||
|
||||
protected void set(DBTraceGuestPlatform platform, DataType dataType) {
|
||||
this.platformKey = (int) (platform == null ? -1 : platform.getKey());
|
||||
protected void set(InternalTracePlatform platform, DataType dataType) {
|
||||
this.platformKey = platform.getIntKey();
|
||||
this.dataTypeID = space.dataTypeManager.getResolvedID(dataType);
|
||||
update(PLATFORM_COLUMN, DATATYPE_COLUMN);
|
||||
|
||||
this.guest = platform;
|
||||
this.platform = platform;
|
||||
// Use the stored dataType, not the given one, in case it's different
|
||||
this.dataType = space.dataTypeManager.getDataType(dataTypeID);
|
||||
assert this.dataType != null;
|
||||
|
@ -133,8 +133,8 @@ public class DBTraceData extends AbstractDBTraceCodeUnit<DBTraceData>
|
|||
}
|
||||
|
||||
@Override
|
||||
public TraceGuestPlatform getGuestPlatform() {
|
||||
return guest;
|
||||
public TracePlatform getPlatform() {
|
||||
return platform;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -157,7 +157,7 @@ public class DBTraceData extends AbstractDBTraceCodeUnit<DBTraceData>
|
|||
|
||||
@Override
|
||||
public Language getLanguage() {
|
||||
return guest == null ? space.baseLanguage : guest.getLanguage();
|
||||
return platform.getLanguage();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -146,7 +146,7 @@ public class DBTraceDefinedDataView extends AbstractBaseDBTraceDefinedUnitsView<
|
|||
|
||||
DBTraceData created = space.dataMapSpace.put(tasr, null);
|
||||
// TODO: data units with a guest platform
|
||||
created.set(null, dataType);
|
||||
created.set(space.trace.getPlatformManager().getHostPlatform(), dataType);
|
||||
// TODO: Explicitly remove undefined from cache, or let weak refs take care of it?
|
||||
|
||||
cacheForContaining.notifyNewEntry(lifespan, createdRange, created);
|
||||
|
|
|
@ -32,13 +32,13 @@ import ghidra.program.model.symbol.*;
|
|||
import ghidra.trace.database.DBTraceUtils;
|
||||
import ghidra.trace.database.context.DBTraceRegisterContextManager;
|
||||
import ghidra.trace.database.context.DBTraceRegisterContextSpace;
|
||||
import ghidra.trace.database.guest.DBTraceGuestPlatform;
|
||||
import ghidra.trace.database.guest.DBTraceGuestPlatform.DBTraceGuestLanguage;
|
||||
import ghidra.trace.database.guest.InternalTracePlatform;
|
||||
import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree;
|
||||
import ghidra.trace.database.symbol.DBTraceReference;
|
||||
import ghidra.trace.database.symbol.DBTraceReferenceSpace;
|
||||
import ghidra.trace.model.Trace.TraceInstructionChangeType;
|
||||
import ghidra.trace.model.guest.TraceGuestPlatform;
|
||||
import ghidra.trace.model.guest.TracePlatform;
|
||||
import ghidra.trace.model.listing.TraceInstruction;
|
||||
import ghidra.trace.model.symbol.TraceReference;
|
||||
import ghidra.trace.util.*;
|
||||
|
@ -79,7 +79,7 @@ public class DBTraceInstruction extends AbstractDBTraceCodeUnit<DBTraceInstructi
|
|||
protected class GuestInstructionContext implements InstructionContext {
|
||||
@Override
|
||||
public Address getAddress() {
|
||||
return guest.mapHostToGuest(getX1());
|
||||
return platform.mapHostToGuest(getX1());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -118,7 +118,7 @@ public class DBTraceInstruction extends AbstractDBTraceCodeUnit<DBTraceInstructi
|
|||
protected FlowOverride flowOverride;
|
||||
|
||||
protected ParserContext parserContext;
|
||||
protected DBTraceGuestPlatform guest;
|
||||
protected InternalTracePlatform platform;
|
||||
protected InstructionContext instructionContext;
|
||||
|
||||
public DBTraceInstruction(DBTraceCodeSpace space,
|
||||
|
@ -127,9 +127,9 @@ public class DBTraceInstruction extends AbstractDBTraceCodeUnit<DBTraceInstructi
|
|||
super(space, tree, store, record);
|
||||
}
|
||||
|
||||
protected void doSetGuestMapping(final DBTraceGuestPlatform guest) {
|
||||
this.guest = guest;
|
||||
if (guest == null) {
|
||||
protected void doSetPlatformMapping(final InternalTracePlatform platform) {
|
||||
this.platform = platform;
|
||||
if (platform.isHost()) {
|
||||
instructionContext = this;
|
||||
}
|
||||
else {
|
||||
|
@ -137,11 +137,11 @@ public class DBTraceInstruction extends AbstractDBTraceCodeUnit<DBTraceInstructi
|
|||
}
|
||||
}
|
||||
|
||||
protected void set(DBTraceGuestPlatform guest, InstructionPrototype prototype,
|
||||
protected void set(InternalTracePlatform platform, InstructionPrototype prototype,
|
||||
ProcessorContextView context) {
|
||||
this.platformKey = (int) (guest == null ? -1 : guest.getKey());
|
||||
this.platformKey = platform.getIntKey();
|
||||
// NOTE: Using "this" for the MemBuffer seems a bit precarious.
|
||||
DBTraceGuestLanguage languageEntry = guest == null ? null : guest.getLanguageEntry();
|
||||
DBTraceGuestLanguage languageEntry = platform == null ? null : platform.getLanguageEntry();
|
||||
this.prototypeKey = (int) space.manager
|
||||
.findOrRecordPrototype(prototype, languageEntry, this, context)
|
||||
.getKey();
|
||||
|
@ -149,7 +149,7 @@ public class DBTraceInstruction extends AbstractDBTraceCodeUnit<DBTraceInstructi
|
|||
update(PLATFORM_COLUMN, PROTOTYPE_COLUMN, FLAGS_COLUMN);
|
||||
|
||||
// TODO: Can there be more in this context than the context register???
|
||||
doSetGuestMapping(guest);
|
||||
doSetPlatformMapping(platform);
|
||||
this.prototype = prototype;
|
||||
}
|
||||
|
||||
|
@ -160,8 +160,8 @@ public class DBTraceInstruction extends AbstractDBTraceCodeUnit<DBTraceInstructi
|
|||
// Wait for something to set prototype
|
||||
return;
|
||||
}
|
||||
guest = space.manager.platformManager.getPlatformByKey(platformKey);
|
||||
if (guest == null && platformKey != -1) {
|
||||
platform = space.manager.platformManager.getPlatformByKey(platformKey);
|
||||
if (platform == null) {
|
||||
throw new IOException("Instruction table is corrupt. Missing platform: " + platformKey);
|
||||
}
|
||||
prototype = space.manager.getPrototypeByKey(prototypeKey);
|
||||
|
@ -173,7 +173,7 @@ public class DBTraceInstruction extends AbstractDBTraceCodeUnit<DBTraceInstructi
|
|||
}
|
||||
flowOverride = FlowOverride.values()[(flags & FLOWOVERRIDE_SET_MASK) >> FLOWOVERRIDE_SHIFT];
|
||||
|
||||
doSetGuestMapping(guest);
|
||||
doSetPlatformMapping(platform);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -206,8 +206,8 @@ public class DBTraceInstruction extends AbstractDBTraceCodeUnit<DBTraceInstructi
|
|||
}
|
||||
|
||||
@Override
|
||||
public TraceGuestPlatform getGuestPlatform() {
|
||||
return guest;
|
||||
public TracePlatform getPlatform() {
|
||||
return platform;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -273,8 +273,7 @@ public class DBTraceInstruction extends AbstractDBTraceCodeUnit<DBTraceInstructi
|
|||
public Address getDefaultFallThrough() {
|
||||
try (LockHold hold = LockHold.lock(space.lock.readLock())) {
|
||||
Address fallThrough = getGuestDefaultFallThrough();
|
||||
return guest == null || fallThrough == null ? fallThrough
|
||||
: guest.mapGuestToHost(fallThrough);
|
||||
return platform.mapGuestToHost(fallThrough);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -392,12 +391,12 @@ public class DBTraceInstruction extends AbstractDBTraceCodeUnit<DBTraceInstructi
|
|||
public Address[] getDefaultFlows() {
|
||||
try (LockHold hold = LockHold.lock(space.lock.readLock())) {
|
||||
Address[] guestFlows = getGuestDefaultFlows();
|
||||
if (guest == null || guestFlows == null) {
|
||||
if (platform.isHost() || guestFlows == null) {
|
||||
return guestFlows;
|
||||
}
|
||||
List<Address> hostFlows = new ArrayList<>();
|
||||
for (Address g : guestFlows) {
|
||||
Address h = guest.mapGuestToHost(g);
|
||||
Address h = platform.mapGuestToHost(g);
|
||||
if (h != null) {
|
||||
hostFlows.add(h);
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ import com.google.common.collect.Range;
|
|||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.lang.*;
|
||||
import ghidra.program.model.util.CodeUnitInsertionException;
|
||||
import ghidra.trace.model.guest.TraceGuestPlatform;
|
||||
import ghidra.trace.model.guest.TracePlatform;
|
||||
import ghidra.trace.model.listing.TraceInstructionsView;
|
||||
import ghidra.util.LockHold;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
|
@ -50,17 +50,16 @@ public class DBTraceInstructionsMemoryView
|
|||
|
||||
@Override
|
||||
public DBTraceInstruction create(Range<Long> lifespan, Address address,
|
||||
TraceGuestPlatform platform, InstructionPrototype prototype,
|
||||
TracePlatform platform, InstructionPrototype prototype,
|
||||
ProcessorContextView context) throws CodeUnitInsertionException {
|
||||
return delegateWrite(address.getAddressSpace(),
|
||||
m -> m.create(lifespan, address, platform, prototype, context));
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressSetView addInstructionSet(Range<Long> lifespan, TraceGuestPlatform platform,
|
||||
public AddressSetView addInstructionSet(Range<Long> lifespan, TracePlatform platform,
|
||||
InstructionSet instructionSet, boolean overwrite) {
|
||||
InstructionSet mappedSet = manager.platformManager
|
||||
.mapGuestInstructionAddressesToHost(platform, instructionSet);
|
||||
InstructionSet mappedSet = platform.mapGuestInstructionAddressesToHost(instructionSet);
|
||||
|
||||
Map<AddressSpace, InstructionSet> breakDown = new HashMap<>();
|
||||
// TODO: I'm not sure the consequences of breaking an instruction set down.
|
||||
|
|
|
@ -29,12 +29,12 @@ import ghidra.program.model.util.CodeUnitInsertionException;
|
|||
import ghidra.trace.database.DBTraceUtils;
|
||||
import ghidra.trace.database.context.DBTraceRegisterContextManager;
|
||||
import ghidra.trace.database.context.DBTraceRegisterContextSpace;
|
||||
import ghidra.trace.database.guest.DBTraceGuestPlatform;
|
||||
import ghidra.trace.database.guest.InternalTracePlatform;
|
||||
import ghidra.trace.database.memory.DBTraceMemorySpace;
|
||||
import ghidra.trace.model.ImmutableTraceAddressSnapRange;
|
||||
import ghidra.trace.model.Trace.TraceCodeChangeType;
|
||||
import ghidra.trace.model.guest.TraceGuestPlatform;
|
||||
import ghidra.trace.model.TraceAddressSnapRange;
|
||||
import ghidra.trace.model.guest.TracePlatform;
|
||||
import ghidra.trace.model.listing.TraceInstruction;
|
||||
import ghidra.trace.model.listing.TraceInstructionsView;
|
||||
import ghidra.trace.util.OverlappingObjectIterator;
|
||||
|
@ -53,7 +53,7 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView
|
|||
protected class InstructionBlockAdder {
|
||||
private final Set<Address> skipDelaySlots;
|
||||
private final Range<Long> lifespan;
|
||||
private final DBTraceGuestPlatform platform;
|
||||
private final InternalTracePlatform platform;
|
||||
private final InstructionBlock block;
|
||||
private final Address errorAddress;
|
||||
private final InstructionError conflict;
|
||||
|
@ -62,7 +62,7 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView
|
|||
protected int count = 0;
|
||||
|
||||
private InstructionBlockAdder(Set<Address> skipDelaySlots, Range<Long> lifespan,
|
||||
DBTraceGuestPlatform platform, InstructionBlock block, Address errorAddress,
|
||||
InternalTracePlatform platform, InstructionBlock block, Address errorAddress,
|
||||
InstructionError conflict, CodeUnit conflictCodeUnit) {
|
||||
this.skipDelaySlots = skipDelaySlots;
|
||||
this.lifespan = lifespan;
|
||||
|
@ -185,19 +185,11 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView
|
|||
ctxSpace.setValue(language, newValue, tasr.getLifespan(), tasr.getRange());
|
||||
}
|
||||
|
||||
protected boolean languagesAgree(DBTraceGuestPlatform platform,
|
||||
InstructionPrototype prototype) {
|
||||
if (platform == null) {
|
||||
return prototype.getLanguage() == space.baseLanguage;
|
||||
}
|
||||
return prototype.getLanguage() == platform.getLanguage();
|
||||
}
|
||||
|
||||
protected DBTraceInstruction doCreate(Range<Long> lifespan, Address address,
|
||||
DBTraceGuestPlatform platform, InstructionPrototype prototype,
|
||||
InternalTracePlatform platform, InstructionPrototype prototype,
|
||||
ProcessorContextView context)
|
||||
throws CodeUnitInsertionException, AddressOverflowException {
|
||||
if (!languagesAgree(platform, prototype)) {
|
||||
if (platform.getLanguage() != prototype.getLanguage()) {
|
||||
throw new IllegalArgumentException("Platform and prototype disagree in language");
|
||||
}
|
||||
|
||||
|
@ -245,11 +237,10 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView
|
|||
}
|
||||
|
||||
@Override
|
||||
public DBTraceInstruction create(Range<Long> lifespan, Address address,
|
||||
TraceGuestPlatform platform, InstructionPrototype prototype,
|
||||
ProcessorContextView context)
|
||||
public DBTraceInstruction create(Range<Long> lifespan, Address address, TracePlatform platform,
|
||||
InstructionPrototype prototype, ProcessorContextView context)
|
||||
throws CodeUnitInsertionException {
|
||||
DBTraceGuestPlatform dbPlatform = space.manager.platformManager.assertMine(platform);
|
||||
InternalTracePlatform dbPlatform = space.manager.platformManager.assertMine(platform);
|
||||
try (LockHold hold = LockHold.lock(space.lock.writeLock())) {
|
||||
DBTraceInstruction created =
|
||||
doCreate(lifespan, address, dbPlatform, prototype, context);
|
||||
|
@ -277,7 +268,7 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView
|
|||
}
|
||||
|
||||
protected InstructionBlockAdder startAddingBlock(Range<Long> lifespan,
|
||||
Set<Address> skipDelaySlots, DBTraceGuestPlatform platform, InstructionBlock block) {
|
||||
Set<Address> skipDelaySlots, InternalTracePlatform platform, InstructionBlock block) {
|
||||
InstructionError conflict = block.getInstructionConflict();
|
||||
if (conflict == null) {
|
||||
return new InstructionBlockAdder(skipDelaySlots, lifespan, platform, block, null, null,
|
||||
|
@ -384,9 +375,9 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView
|
|||
}
|
||||
|
||||
@Override
|
||||
public AddressSetView addInstructionSet(Range<Long> lifespan, TraceGuestPlatform platform,
|
||||
public AddressSetView addInstructionSet(Range<Long> lifespan, TracePlatform platform,
|
||||
InstructionSet instructionSet, boolean overwrite) {
|
||||
DBTraceGuestPlatform dbPlatform = space.manager.platformManager.assertMine(platform);
|
||||
InternalTracePlatform dbPlatform = space.manager.platformManager.assertMine(platform);
|
||||
// NOTE: Partly derived from CodeManager#addInstructions()
|
||||
// Attempted to factor more fluently
|
||||
AddressSet result = new AddressSet();
|
||||
|
|
|
@ -33,7 +33,7 @@ import ghidra.trace.database.memory.DBTraceMemorySpace;
|
|||
import ghidra.trace.database.space.DBTraceSpaceKey;
|
||||
import ghidra.trace.model.ImmutableTraceAddressSnapRange;
|
||||
import ghidra.trace.model.TraceAddressSnapRange;
|
||||
import ghidra.trace.model.guest.TraceGuestPlatform;
|
||||
import ghidra.trace.model.guest.TracePlatform;
|
||||
import ghidra.trace.model.listing.TraceData;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.trace.util.TraceAddressSpace;
|
||||
|
@ -82,8 +82,8 @@ public class UndefinedDBTraceData implements DBTraceDataAdapter, DBTraceSpaceKey
|
|||
}
|
||||
|
||||
@Override
|
||||
public TraceGuestPlatform getGuestPlatform() {
|
||||
return null;
|
||||
public TracePlatform getPlatform() {
|
||||
return trace.getPlatformManager().getHostPlatform();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -37,6 +37,7 @@ import ghidra.program.model.symbol.SourceType;
|
|||
import ghidra.program.model.util.CodeUnitInsertionException;
|
||||
import ghidra.program.model.util.PropertyMap;
|
||||
import ghidra.trace.database.DBTrace;
|
||||
import ghidra.trace.database.guest.InternalTracePlatform;
|
||||
import ghidra.trace.database.listing.UndefinedDBTraceData;
|
||||
import ghidra.trace.database.memory.DBTraceMemorySpace;
|
||||
import ghidra.trace.database.symbol.DBTraceFunctionSymbol;
|
||||
|
@ -77,6 +78,7 @@ public abstract class AbstractDBTraceProgramViewListing implements TraceProgramV
|
|||
|
||||
protected final DBTraceProgramView program;
|
||||
protected final TraceCodeOperations codeOperations;
|
||||
protected final InternalTracePlatform platform;
|
||||
|
||||
protected final DBTraceProgramViewRootModule rootModule;
|
||||
protected final Map<TraceMemoryRegion, DBTraceProgramViewFragment> fragmentsByRegion =
|
||||
|
@ -94,6 +96,8 @@ public abstract class AbstractDBTraceProgramViewListing implements TraceProgramV
|
|||
TraceCodeOperations codeOperations) {
|
||||
this.program = program;
|
||||
this.codeOperations = codeOperations;
|
||||
// TODO: Guest platform views?
|
||||
this.platform = program.trace.getPlatformManager().getHostPlatform();
|
||||
|
||||
this.rootModule = new DBTraceProgramViewRootModule(this);
|
||||
}
|
||||
|
@ -725,17 +729,15 @@ public abstract class AbstractDBTraceProgramViewListing implements TraceProgramV
|
|||
public Instruction createInstruction(Address addr, InstructionPrototype prototype,
|
||||
MemBuffer memBuf, ProcessorContextView context) throws CodeUnitInsertionException {
|
||||
// TODO: Why memBuf? Can it vary from program memory?
|
||||
// TODO: Per-platform views?
|
||||
return codeOperations.instructions()
|
||||
.create(Range.atLeast(program.snap), addr, null, prototype, context);
|
||||
.create(Range.atLeast(program.snap), addr, platform, prototype, context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressSetView addInstructions(InstructionSet instructionSet, boolean overwrite)
|
||||
throws CodeUnitInsertionException {
|
||||
// TODO: Per-platform views?
|
||||
return codeOperations.instructions()
|
||||
.addInstructionSet(Range.atLeast(program.snap), null, instructionSet,
|
||||
.addInstructionSet(Range.atLeast(program.snap), platform, instructionSet,
|
||||
overwrite);
|
||||
}
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ import com.google.common.collect.Range;
|
|||
|
||||
import db.DBHandle;
|
||||
import ghidra.trace.database.*;
|
||||
import ghidra.trace.database.target.DBTraceObject;
|
||||
import ghidra.trace.database.target.DBTraceObjectManager;
|
||||
import ghidra.trace.model.Trace.TraceThreadChangeType;
|
||||
import ghidra.trace.model.thread.*;
|
||||
|
@ -158,9 +159,8 @@ public class DBTraceThreadManager implements TraceThreadManager, DBTraceManager
|
|||
@Override
|
||||
public TraceThread getThread(long key) {
|
||||
if (objectManager.hasSchema()) {
|
||||
return objectManager
|
||||
.getObjectById(key)
|
||||
.queryInterface(TraceObjectThread.class);
|
||||
DBTraceObject object = objectManager.getObjectById(key);
|
||||
return object == null ? null : object.queryInterface(TraceObjectThread.class);
|
||||
}
|
||||
return threadStore.getObjectAt(key);
|
||||
}
|
||||
|
|
|
@ -15,46 +15,15 @@
|
|||
*/
|
||||
package ghidra.trace.model.guest;
|
||||
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.lang.*;
|
||||
import ghidra.program.model.mem.MemBuffer;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressOverflowException;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
public interface TraceGuestPlatform {
|
||||
/**
|
||||
* Get the trace
|
||||
*
|
||||
* @return the trace
|
||||
*/
|
||||
Trace getTrace();
|
||||
public interface TraceGuestPlatform extends TracePlatform {
|
||||
|
||||
/**
|
||||
* Get the language of the guest platform
|
||||
*
|
||||
* @return the language
|
||||
*/
|
||||
Language getLanguage();
|
||||
|
||||
/**
|
||||
* Get the address factory of the guest platform
|
||||
*
|
||||
* @return the factory
|
||||
*/
|
||||
default AddressFactory getAddressFactory() {
|
||||
return getLanguage().getAddressFactory();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the compiler of the guest platform
|
||||
*
|
||||
* @return the compiler spec
|
||||
*/
|
||||
CompilerSpec getCompilerSpec();
|
||||
|
||||
/**
|
||||
* Add an adress mapping from host to guest
|
||||
* Add an address mapping from host to guest
|
||||
*
|
||||
* @param hostStart the starting host address (mapped to guestStart)
|
||||
* @param guestStart the starting guest address (mapped to hostStart)
|
||||
|
@ -65,66 +34,6 @@ public interface TraceGuestPlatform {
|
|||
TraceGuestPlatformMappedRange addMappedRange(Address hostStart, Address guestStart, long length)
|
||||
throws AddressOverflowException;
|
||||
|
||||
/**
|
||||
* Get the addresses in the host which are mapped to somewhere in the guest
|
||||
*
|
||||
* @return the address set
|
||||
*/
|
||||
AddressSetView getHostAddressSet();
|
||||
|
||||
/**
|
||||
* Get the addresses in the guest which are mapped to somehere in the host
|
||||
*
|
||||
* @return the address set
|
||||
*/
|
||||
AddressSetView getGuestAddressSet();
|
||||
|
||||
/**
|
||||
* Map an address from host to guest
|
||||
*
|
||||
* @param hostAddress the host address
|
||||
* @return the guest address
|
||||
*/
|
||||
Address mapHostToGuest(Address hostAddress);
|
||||
|
||||
/**
|
||||
* Map an address from guest to host
|
||||
*
|
||||
* @param guestAddress the guest address
|
||||
* @return the host address
|
||||
*/
|
||||
Address mapGuestToHost(Address guestAddress);
|
||||
|
||||
/**
|
||||
* Get a memory buffer, which presents the host bytes in the guest address space
|
||||
*
|
||||
* <p>
|
||||
* This, with pseudo-disassembly, is the primary mechanism for adding instructions in the guest
|
||||
* language.
|
||||
*
|
||||
* @param snap the snap, up to which the most recent memory changes are presented
|
||||
* @param guestAddress the starting address in the guest space
|
||||
* @return the mapped memory buffer
|
||||
*/
|
||||
MemBuffer getMappedMemBuffer(long snap, Address guestAddress);
|
||||
|
||||
/**
|
||||
* Copy the given instruction set, but with addresses mapped from the guest space to the host
|
||||
* space
|
||||
*
|
||||
* <p>
|
||||
* Instructions which do not map are silently ignored. If concerned, the caller ought to examine
|
||||
* the resulting instruction set and/or the resulting address set after it is added to the
|
||||
* trace. A single instruction cannot span two mapped ranges, even if the comprised bytes are
|
||||
* consecutive in the guest space. Mapping such an instruction back into the host space would
|
||||
* cause the instruction to be split in the middle, which is not possible. Thus, such
|
||||
* instructions are silently ignored.
|
||||
*
|
||||
* @param set the instruction set in the guest space
|
||||
* @return the instruction set in the host space
|
||||
*/
|
||||
InstructionSet mapGuestInstructionAddressesToHost(InstructionSet set);
|
||||
|
||||
/**
|
||||
* Remove the mapped language, including all code units of the language
|
||||
*/
|
||||
|
|
|
@ -0,0 +1,129 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.trace.model.guest;
|
||||
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.lang.*;
|
||||
import ghidra.program.model.mem.MemBuffer;
|
||||
import ghidra.trace.model.Trace;
|
||||
|
||||
public interface TracePlatform {
|
||||
/**
|
||||
* Get the trace
|
||||
*
|
||||
* @return the trace
|
||||
*/
|
||||
Trace getTrace();
|
||||
|
||||
/**
|
||||
* Check if this is a guest platform
|
||||
*
|
||||
* @return true for guest, false for host
|
||||
*/
|
||||
boolean isGuest();
|
||||
|
||||
/**
|
||||
* Check if this is the host platform
|
||||
*
|
||||
* @return true for host, false for guest
|
||||
*/
|
||||
default boolean isHost() {
|
||||
return !isGuest();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the language of the guest platform
|
||||
*
|
||||
* @return the language
|
||||
*/
|
||||
Language getLanguage();
|
||||
|
||||
/**
|
||||
* Get the address factory of the guest platform
|
||||
*
|
||||
* @return the factory
|
||||
*/
|
||||
default AddressFactory getAddressFactory() {
|
||||
return getLanguage().getAddressFactory();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the compiler of the guest platform
|
||||
*
|
||||
* @return the compiler spec
|
||||
*/
|
||||
CompilerSpec getCompilerSpec();
|
||||
|
||||
/**
|
||||
* Get the addresses in the host which are mapped to somewhere in the guest
|
||||
*
|
||||
* @return the address set
|
||||
*/
|
||||
AddressSetView getHostAddressSet();
|
||||
|
||||
/**
|
||||
* Get the addresses in the guest which are mapped to somehere in the host
|
||||
*
|
||||
* @return the address set
|
||||
*/
|
||||
AddressSetView getGuestAddressSet();
|
||||
|
||||
/**
|
||||
* Map an address from host to guest
|
||||
*
|
||||
* @param hostAddress the host address
|
||||
* @return the guest address
|
||||
*/
|
||||
Address mapHostToGuest(Address hostAddress);
|
||||
|
||||
/**
|
||||
* Map an address from guest to host
|
||||
*
|
||||
* @param guestAddress the guest address
|
||||
* @return the host address
|
||||
*/
|
||||
Address mapGuestToHost(Address guestAddress);
|
||||
|
||||
/**
|
||||
* Get a memory buffer, which presents the host bytes in the guest address space
|
||||
*
|
||||
* <p>
|
||||
* This, with pseudo-disassembly, is the primary mechanism for adding instructions in the guest
|
||||
* language.
|
||||
*
|
||||
* @param snap the snap, up to which the most recent memory changes are presented
|
||||
* @param guestAddress the starting address in the guest space
|
||||
* @return the mapped memory buffer
|
||||
*/
|
||||
MemBuffer getMappedMemBuffer(long snap, Address guestAddress);
|
||||
|
||||
/**
|
||||
* Copy the given instruction set, but with addresses mapped from the guest space to the host
|
||||
* space
|
||||
*
|
||||
* <p>
|
||||
* Instructions which do not map are silently ignored. If concerned, the caller ought to examine
|
||||
* the resulting instruction set and/or the resulting address set after it is added to the
|
||||
* trace. A single instruction cannot span two mapped ranges, even if the comprised bytes are
|
||||
* consecutive in the guest space. Mapping such an instruction back into the host space would
|
||||
* cause the instruction to be split in the middle, which is not possible. Thus, such
|
||||
* instructions are silently ignored.
|
||||
*
|
||||
* @param set the instruction set in the guest space
|
||||
* @return the instruction set in the host space
|
||||
*/
|
||||
InstructionSet mapGuestInstructionAddressesToHost(InstructionSet set);
|
||||
}
|
|
@ -18,7 +18,6 @@ package ghidra.trace.model.guest;
|
|||
import java.util.Collection;
|
||||
|
||||
import ghidra.program.model.lang.CompilerSpec;
|
||||
import ghidra.program.model.lang.Language;
|
||||
|
||||
/**
|
||||
* Allows the addition of "guest platforms" for disassembling in multiple languages.
|
||||
|
@ -28,18 +27,11 @@ import ghidra.program.model.lang.Language;
|
|||
*/
|
||||
public interface TracePlatformManager {
|
||||
/**
|
||||
* Get the base language of the trace
|
||||
* Get a platform representing the trace's base language and compiler spec
|
||||
*
|
||||
* @return the language
|
||||
* @return the host platform
|
||||
*/
|
||||
Language getBaseLanguage();
|
||||
|
||||
/**
|
||||
* Get the base compiler spec of the trace
|
||||
*
|
||||
* @return the compiler spec
|
||||
*/
|
||||
CompilerSpec getBaseCompilerSpec();
|
||||
TracePlatform getHostPlatform();
|
||||
|
||||
/**
|
||||
* Add a guest platform
|
||||
|
@ -50,12 +42,12 @@ public interface TracePlatformManager {
|
|||
TraceGuestPlatform addGuestPlatform(CompilerSpec compilerSpec);
|
||||
|
||||
/**
|
||||
* Get the guest platform for the given compiler spec
|
||||
* Get the platform for the given compiler spec
|
||||
*
|
||||
* @param compilerSpec the compiler spec. For the base compiler spec, this will return null.
|
||||
* @param compilerSpec the compiler spec
|
||||
* @return the platform, if found, or null
|
||||
*/
|
||||
TraceGuestPlatform getGuestPlatform(CompilerSpec compilerSpec);
|
||||
TracePlatform getPlatform(CompilerSpec compilerSpec);
|
||||
|
||||
/**
|
||||
* Get or add a platform for the given compiler spec
|
||||
|
|
|
@ -18,6 +18,7 @@ package ghidra.trace.model.listing;
|
|||
import com.google.common.collect.Range;
|
||||
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.TraceAddressSnapRange;
|
||||
import ghidra.util.IntersectionAddressSetView;
|
||||
import ghidra.util.UnionAddressSetView;
|
||||
|
@ -30,6 +31,13 @@ import ghidra.util.UnionAddressSetView;
|
|||
*/
|
||||
public interface TraceBaseCodeUnitsView<T extends TraceCodeUnit> {
|
||||
|
||||
/**
|
||||
* Get the trace for this view
|
||||
*
|
||||
* @return the trace
|
||||
*/
|
||||
Trace getTrace();
|
||||
|
||||
/**
|
||||
* Get the total number of <em>defined</em> units in this view
|
||||
*
|
||||
|
|
|
@ -25,7 +25,7 @@ import ghidra.program.model.listing.CodeUnit;
|
|||
import ghidra.program.model.util.TypeMismatchException;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.TraceAddressSnapRange;
|
||||
import ghidra.trace.model.guest.TraceGuestPlatform;
|
||||
import ghidra.trace.model.guest.TracePlatform;
|
||||
import ghidra.trace.model.program.TraceProgramView;
|
||||
import ghidra.trace.model.symbol.TraceReference;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
|
@ -44,11 +44,11 @@ public interface TraceCodeUnit extends CodeUnit {
|
|||
Trace getTrace();
|
||||
|
||||
/**
|
||||
* If the unit is for a guest platform, get it
|
||||
* Get the platform for this unit
|
||||
*
|
||||
* @return the guest platform, or null if it's for the host platform
|
||||
* @return the platform
|
||||
*/
|
||||
TraceGuestPlatform getGuestPlatform();
|
||||
TracePlatform getPlatform();
|
||||
|
||||
@Override
|
||||
TraceProgramView getProgram();
|
||||
|
|
|
@ -21,7 +21,7 @@ import ghidra.program.model.address.Address;
|
|||
import ghidra.program.model.address.AddressSetView;
|
||||
import ghidra.program.model.lang.*;
|
||||
import ghidra.program.model.util.CodeUnitInsertionException;
|
||||
import ghidra.trace.model.guest.TraceGuestPlatform;
|
||||
import ghidra.trace.model.guest.TracePlatform;
|
||||
|
||||
public interface TraceInstructionsView extends TraceBaseDefinedUnitsView<TraceInstruction> {
|
||||
/**
|
||||
|
@ -29,16 +29,28 @@ public interface TraceInstructionsView extends TraceBaseDefinedUnitsView<TraceIn
|
|||
*
|
||||
* @param lifespan the lifespan for the instruction unit
|
||||
* @param address the starting address of the instruction
|
||||
* @param platform the optional guest platform, null for the host
|
||||
* @param platform the platform
|
||||
* @param prototype the instruction prototype
|
||||
* @param context the input disassembly context for the instruction
|
||||
* @return the new instruction
|
||||
* @throws CodeUnitInsertionException if the instruction cannot be created
|
||||
*/
|
||||
TraceInstruction create(Range<Long> lifespan, Address address, TraceGuestPlatform platform,
|
||||
TraceInstruction create(Range<Long> lifespan, Address address, TracePlatform platform,
|
||||
InstructionPrototype prototype, ProcessorContextView context)
|
||||
throws CodeUnitInsertionException;
|
||||
|
||||
/**
|
||||
* Create an instruction for the host platform
|
||||
*
|
||||
* @see #create(Range, Address, TracePlatform, InstructionPrototype, ProcessorContextView)
|
||||
*/
|
||||
default TraceInstruction create(Range<Long> lifespan, Address address,
|
||||
InstructionPrototype prototype, ProcessorContextView context)
|
||||
throws CodeUnitInsertionException {
|
||||
return create(lifespan, address, getTrace().getPlatformManager().getHostPlatform(),
|
||||
prototype, context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create several instructions
|
||||
*
|
||||
|
@ -52,6 +64,17 @@ public interface TraceInstructionsView extends TraceBaseDefinedUnitsView<TraceIn
|
|||
* @param overwrite true to replace conflicting instructions
|
||||
* @return the (host) address set of instructions actually added
|
||||
*/
|
||||
AddressSetView addInstructionSet(Range<Long> lifespan, TraceGuestPlatform platform,
|
||||
AddressSetView addInstructionSet(Range<Long> lifespan, TracePlatform platform,
|
||||
InstructionSet instructionSet, boolean overwrite);
|
||||
|
||||
/**
|
||||
* Create several instructions for the host platform
|
||||
*
|
||||
* @see #addInstructionSet(Range, TracePlatform, InstructionSet, boolean)
|
||||
*/
|
||||
default AddressSetView addInstructionSet(Range<Long> lifespan, InstructionSet instructionSet,
|
||||
boolean overwrite) {
|
||||
return addInstructionSet(lifespan, getTrace().getPlatformManager().getHostPlatform(),
|
||||
instructionSet, overwrite);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -52,6 +52,7 @@ import ghidra.trace.database.symbol.DBTraceReference;
|
|||
import ghidra.trace.database.thread.DBTraceThreadManager;
|
||||
import ghidra.trace.model.*;
|
||||
import ghidra.trace.model.guest.TraceGuestPlatform;
|
||||
import ghidra.trace.model.guest.TracePlatform;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.database.DBOpenMode;
|
||||
|
@ -62,6 +63,7 @@ import ghidra.util.task.ConsoleTaskMonitor;
|
|||
public class ToyDBTraceBuilder implements AutoCloseable {
|
||||
public final Language language;
|
||||
public final DBTrace trace;
|
||||
public final TracePlatform host;
|
||||
public final LanguageService languageService = DefaultLanguageService.getLanguageService();
|
||||
|
||||
public ToyDBTraceBuilder(File file)
|
||||
|
@ -69,17 +71,20 @@ public class ToyDBTraceBuilder implements AutoCloseable {
|
|||
DBHandle handle = new DBHandle(file);
|
||||
this.trace = new DBTrace(handle, DBOpenMode.UPDATE, new ConsoleTaskMonitor(), this);
|
||||
this.language = trace.getBaseLanguage();
|
||||
this.host = trace.getPlatformManager().getHostPlatform();
|
||||
}
|
||||
|
||||
// TODO: A constructor for specifying compiler, too
|
||||
public ToyDBTraceBuilder(String name, String langID) throws IOException {
|
||||
this.language = languageService.getLanguage(new LanguageID(langID));
|
||||
this.trace = new DBTrace(name, language.getDefaultCompilerSpec(), this);
|
||||
this.host = trace.getPlatformManager().getHostPlatform();
|
||||
}
|
||||
|
||||
public ToyDBTraceBuilder(Trace trace) {
|
||||
this.language = trace.getBaseLanguage();
|
||||
this.trace = (DBTrace) trace;
|
||||
this.host = trace.getPlatformManager().getHostPlatform();
|
||||
trace.addConsumer(this);
|
||||
}
|
||||
|
||||
|
@ -102,7 +107,7 @@ public class ToyDBTraceBuilder implements AutoCloseable {
|
|||
return addr(language, offset);
|
||||
}
|
||||
|
||||
public Address addr(TraceGuestPlatform lang, long offset) {
|
||||
public Address addr(TracePlatform lang, long offset) {
|
||||
return lang.getLanguage().getDefaultSpace().getAddress(offset);
|
||||
}
|
||||
|
||||
|
@ -246,36 +251,28 @@ public class ToyDBTraceBuilder implements AutoCloseable {
|
|||
}
|
||||
|
||||
public DBTraceInstruction addInstruction(long snap, Address start,
|
||||
TraceGuestPlatform guest) throws CodeUnitInsertionException {
|
||||
DBTraceMemoryManager memory = trace.getMemoryManager();
|
||||
TracePlatform platform) throws CodeUnitInsertionException {
|
||||
DBTraceCodeManager code = trace.getCodeManager();
|
||||
Language language = guest == null ? this.language : guest.getLanguage();
|
||||
Language language = platform.getLanguage();
|
||||
Disassembler dis = Disassembler.getDisassembler(language, language.getAddressFactory(),
|
||||
new ConsoleTaskMonitor(), msg -> Msg.info(this, "Listener: " + msg));
|
||||
RegisterValue defaultContextValue = trace.getRegisterContextManager()
|
||||
.getDefaultContext(language)
|
||||
.getDefaultDisassemblyContext();
|
||||
|
||||
MemBuffer memBuf;
|
||||
if (guest == null) {
|
||||
memBuf = memory.getBufferAt(snap, start);
|
||||
}
|
||||
else {
|
||||
memBuf = guest.getMappedMemBuffer(snap, guest.mapHostToGuest(start));
|
||||
}
|
||||
MemBuffer memBuf = platform.getMappedMemBuffer(snap, platform.mapHostToGuest(start));
|
||||
InstructionBlock block = dis.pseudoDisassembleBlock(memBuf, defaultContextValue, 1);
|
||||
Instruction pseudoIns = block.iterator().next();
|
||||
return code.instructions()
|
||||
.create(Range.atLeast(snap), start, guest, pseudoIns.getPrototype(), pseudoIns);
|
||||
.create(Range.atLeast(snap), start, platform, pseudoIns.getPrototype(), pseudoIns);
|
||||
}
|
||||
|
||||
public DBTraceInstruction addInstruction(long snap, Address start,
|
||||
TraceGuestPlatform guest, ByteBuffer buf)
|
||||
throws CodeUnitInsertionException {
|
||||
public DBTraceInstruction addInstruction(long snap, Address start, TracePlatform platform,
|
||||
ByteBuffer buf) throws CodeUnitInsertionException {
|
||||
int length = buf.remaining();
|
||||
DBTraceMemoryManager memory = trace.getMemoryManager();
|
||||
memory.putBytes(snap, start, buf);
|
||||
DBTraceInstruction instruction = addInstruction(snap, start, guest);
|
||||
DBTraceInstruction instruction = addInstruction(snap, start, platform);
|
||||
assertEquals(length, instruction.getLength());
|
||||
return instruction;
|
||||
}
|
||||
|
|
|
@ -25,8 +25,8 @@ import org.junit.*;
|
|||
|
||||
import ghidra.test.AbstractGhidraHeadlessIntegrationTest;
|
||||
import ghidra.trace.database.ToyDBTraceBuilder;
|
||||
import ghidra.trace.database.guest.*;
|
||||
import ghidra.trace.model.guest.TraceGuestPlatform;
|
||||
import ghidra.trace.model.guest.TracePlatform;
|
||||
import ghidra.util.database.UndoableTransaction;
|
||||
import ghidra.util.task.ConsoleTaskMonitor;
|
||||
|
||||
|
@ -46,14 +46,10 @@ public class DBTracePlatformManagerTest extends AbstractGhidraHeadlessIntegratio
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testGetBaseLanguage() {
|
||||
assertEquals("Toy:BE:64:default",
|
||||
manager.getBaseLanguage().getLanguageID().getIdAsString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetBaseCompilerSpec() {
|
||||
assertEquals("default", manager.getBaseCompilerSpec().getCompilerSpecID().getIdAsString());
|
||||
public void testGetHostPlatform() throws Throwable {
|
||||
TracePlatform host = b.trace.getPlatformManager().getHostPlatform();
|
||||
assertEquals("Toy:BE:64:default", host.getLanguage().getLanguageID().getIdAsString());
|
||||
assertEquals("default", host.getCompilerSpec().getCompilerSpecID().getIdAsString());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -324,7 +320,8 @@ public class DBTracePlatformManagerTest extends AbstractGhidraHeadlessIntegratio
|
|||
|
||||
b.trace.undo();
|
||||
|
||||
guest = manager.getGuestPlatform(b.getCompiler("x86:LE:32:default", "gcc"));
|
||||
guest =
|
||||
(DBTraceGuestPlatform) manager.getPlatform(b.getCompiler("x86:LE:32:default", "gcc"));
|
||||
assertNotNull(guest.mapHostToGuest(b.addr(0x01000800)));
|
||||
assertNotNull(guest.mapGuestToHost(b.addr(guest, 0x02000800)));
|
||||
}
|
||||
|
@ -359,7 +356,8 @@ public class DBTracePlatformManagerTest extends AbstractGhidraHeadlessIntegratio
|
|||
|
||||
b.trace.undo();
|
||||
|
||||
guest = manager.getGuestPlatform(b.getCompiler("x86:LE:32:default", "gcc"));
|
||||
guest =
|
||||
(DBTraceGuestPlatform) manager.getPlatform(b.getCompiler("x86:LE:32:default", "gcc"));
|
||||
assertEquals(b.addr(guest, 0x02000800), guest.mapHostToGuest(b.addr(0x01000800)));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -187,7 +187,7 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes
|
|||
@Test
|
||||
public void testAddInstruction() throws CodeUnitInsertionException {
|
||||
try (UndoableTransaction tid = b.startTransaction()) {
|
||||
b.addInstruction(0, b.addr(0x4004), null, b.buf(0xf4, 0));
|
||||
b.addInstruction(0, b.addr(0x4004), b.host, b.buf(0xf4, 0));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -196,7 +196,7 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes
|
|||
try (UndoableTransaction tid = b.startTransaction()) {
|
||||
b.trace.getMemoryManager().putBytes(10, b.addr(0x4001), b.buf(0xaa));
|
||||
TraceInstruction i4000 =
|
||||
b.addInstruction(0, b.addr(0x4000), null, b.buf(0xf4, 0));
|
||||
b.addInstruction(0, b.addr(0x4000), b.host, b.buf(0xf4, 0));
|
||||
assertEquals(Range.closed(0L, 9L), i4000.getLifespan());
|
||||
}
|
||||
}
|
||||
|
@ -206,15 +206,15 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes
|
|||
try (UndoableTransaction tid = b.startTransaction()) {
|
||||
b.trace.getMemoryManager().putBytes(-5L, b.addr(0x4001), b.buf(0xaa));
|
||||
TraceInstruction i4000 =
|
||||
b.addInstruction(-10, b.addr(0x4000), null, b.buf(0xf4, 0));
|
||||
b.addInstruction(-10, b.addr(0x4000), b.host, b.buf(0xf4, 0));
|
||||
assertEquals(Range.closed(-10L, -6L), i4000.getLifespan());
|
||||
|
||||
TraceInstruction i4004 =
|
||||
b.addInstruction(-1, b.addr(0x4004), null, b.buf(0xf4, 0));
|
||||
b.addInstruction(-1, b.addr(0x4004), b.host, b.buf(0xf4, 0));
|
||||
assertEquals(Range.closed(-1L, -1L), i4004.getLifespan());
|
||||
|
||||
TraceInstruction i4008 =
|
||||
b.addInstruction(-10, b.addr(0x4008), null, b.buf(0xf4, 0));
|
||||
b.addInstruction(-10, b.addr(0x4008), b.host, b.buf(0xf4, 0));
|
||||
assertEquals(Range.closed(-10L, -1L), i4008.getLifespan());
|
||||
}
|
||||
}
|
||||
|
@ -223,7 +223,7 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes
|
|||
public void testPutBytesTruncatesInstruction() throws CodeUnitInsertionException {
|
||||
try (UndoableTransaction tid = b.startTransaction()) {
|
||||
TraceInstruction i4000 =
|
||||
b.addInstruction(0, b.addr(0x4000), null, b.buf(0xf4, 0));
|
||||
b.addInstruction(0, b.addr(0x4000), b.host, b.buf(0xf4, 0));
|
||||
assertEquals(b.addr(0x4001), i4000.getMaxAddress());
|
||||
assertEquals(Range.atLeast(0L), i4000.getLifespan());
|
||||
b.trace.getMemoryManager().putBytes(10, b.addr(0x4001), b.buf(1));
|
||||
|
@ -236,7 +236,7 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes
|
|||
public void testPutBytesDeletesInstruction() throws CodeUnitInsertionException {
|
||||
try (UndoableTransaction tid = b.startTransaction()) {
|
||||
TraceInstruction i4000 =
|
||||
b.addInstruction(0, b.addr(0x4000), null, b.buf(0xf4, 0));
|
||||
b.addInstruction(0, b.addr(0x4000), b.host, b.buf(0xf4, 0));
|
||||
assertEquals(b.addr(0x4001), i4000.getMaxAddress());
|
||||
assertEquals(Range.atLeast(0L), i4000.getLifespan());
|
||||
b.trace.getMemoryManager().putBytes(0, b.addr(0x4001), b.buf(1));
|
||||
|
@ -267,14 +267,14 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes
|
|||
}
|
||||
|
||||
try {
|
||||
b.addInstruction(1, b.addr(0x4001), null);
|
||||
b.addInstruction(1, b.addr(0x4001), b.host);
|
||||
fail();
|
||||
}
|
||||
catch (CodeUnitInsertionException e) {
|
||||
// pass
|
||||
}
|
||||
|
||||
b.addInstruction(0, b.addr(0x4004), null, b.buf(0xf4, 0));
|
||||
b.addInstruction(0, b.addr(0x4004), b.host, b.buf(0xf4, 0));
|
||||
|
||||
try {
|
||||
b.addData(1, b.addr(0x4005), ByteDataType.dataType, 1);
|
||||
|
@ -285,7 +285,7 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes
|
|||
}
|
||||
|
||||
try {
|
||||
b.addInstruction(1, b.addr(0x4005), null);
|
||||
b.addInstruction(1, b.addr(0x4005), b.host);
|
||||
}
|
||||
catch (CodeUnitInsertionException e) {
|
||||
// pass
|
||||
|
@ -350,7 +350,7 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes
|
|||
assertAllNullFunc(v -> v.getAt(9, b.addr(0x4003)));
|
||||
assertUndefinedFunc(v -> v.getAt(9, b.addr(0x4004)));
|
||||
|
||||
TraceInstruction i4005 = b.addInstruction(0, b.addr(0x4005), null, b.buf(0xf4, 0));
|
||||
TraceInstruction i4005 = b.addInstruction(0, b.addr(0x4005), b.host, b.buf(0xf4, 0));
|
||||
i4005.setEndSnap(5);
|
||||
assertUndefinedFunc(v -> v.getAt(0, b.addr(0x4004)));
|
||||
assertInstructionFunc(i4005, v -> v.getAt(0, b.addr(0x4005)));
|
||||
|
@ -383,7 +383,7 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes
|
|||
assertDataFunc(d4000, v -> v.getContaining(9, b.addr(0x4003)));
|
||||
assertUndefinedFunc(v -> v.getContaining(9, b.addr(0x4004)));
|
||||
|
||||
TraceInstruction i4005 = b.addInstruction(0, b.addr(0x4005), null, b.buf(0xf4, 0));
|
||||
TraceInstruction i4005 = b.addInstruction(0, b.addr(0x4005), b.host, b.buf(0xf4, 0));
|
||||
i4005.setEndSnap(5);
|
||||
assertUndefinedFunc(v -> v.getContaining(0, b.addr(0x4004)));
|
||||
|
||||
|
@ -449,7 +449,7 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes
|
|||
d4000.setEndSnap(9);
|
||||
d4004 = b.addData(0, b.addr(0x4004), IntegerDataType.dataType, b.buf(5, 6, 7, 8));
|
||||
d4004.setEndSnap(5);
|
||||
i4008 = b.addInstruction(0, b.addr(0x4008), null, b.buf(0xf4, 0));
|
||||
i4008 = b.addInstruction(0, b.addr(0x4008), b.host, b.buf(0xf4, 0));
|
||||
i4008.setEndSnap(9);
|
||||
}
|
||||
|
||||
|
@ -591,7 +591,7 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes
|
|||
d4000.setEndSnap(9);
|
||||
d4004 = b.addData(0, b.addr(0x4004), IntegerDataType.dataType, b.buf(5, 6, 7, 8));
|
||||
d4004.setEndSnap(5);
|
||||
i4008 = b.addInstruction(0, b.addr(0x4008), null, b.buf(0xf4, 0));
|
||||
i4008 = b.addInstruction(0, b.addr(0x4008), b.host, b.buf(0xf4, 0));
|
||||
i4008.setEndSnap(9);
|
||||
}
|
||||
|
||||
|
@ -730,7 +730,7 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes
|
|||
d4000.setEndSnap(9);
|
||||
d4004 = b.addData(0, b.addr(0x4004), IntegerDataType.dataType, b.buf(5, 6, 7, 8));
|
||||
d4004.setEndSnap(5);
|
||||
i4008 = b.addInstruction(0, b.addr(0x4008), null, b.buf(0xf4, 0));
|
||||
i4008 = b.addInstruction(0, b.addr(0x4008), b.host, b.buf(0xf4, 0));
|
||||
i4008.setEndSnap(9);
|
||||
}
|
||||
|
||||
|
@ -870,7 +870,7 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes
|
|||
d4000.setEndSnap(9);
|
||||
d4004 = b.addData(0, b.addr(0x4004), IntegerDataType.dataType, b.buf(5, 6, 7, 8));
|
||||
d4004.setEndSnap(5);
|
||||
i4008 = b.addInstruction(0, b.addr(0x4008), null, b.buf(0xf4, 0));
|
||||
i4008 = b.addInstruction(0, b.addr(0x4008), b.host, b.buf(0xf4, 0));
|
||||
i4008.setEndSnap(9);
|
||||
}
|
||||
|
||||
|
@ -1084,7 +1084,7 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes
|
|||
d4000.setEndSnap(9);
|
||||
d4004 = b.addData(0, b.addr(0x4004), IntegerDataType.dataType, b.buf(5, 6, 7, 8));
|
||||
d4004.setEndSnap(5);
|
||||
i4008 = b.addInstruction(0, b.addr(0x4008), null, b.buf(0xf4, 0));
|
||||
i4008 = b.addInstruction(0, b.addr(0x4008), b.host, b.buf(0xf4, 0));
|
||||
i4008.setEndSnap(9);
|
||||
}
|
||||
TraceData u3fff = manager.undefinedData().getAt(0, b.addr(0x3fff));
|
||||
|
@ -1137,7 +1137,7 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes
|
|||
|
||||
TraceInstruction iCodeMax;
|
||||
try (UndoableTransaction tid = b.startTransaction()) {
|
||||
iCodeMax = b.addInstruction(0, b.addr(-0x0002), null, b.buf(0xf4, 0));
|
||||
iCodeMax = b.addInstruction(0, b.addr(-0x0002), b.host, b.buf(0xf4, 0));
|
||||
}
|
||||
|
||||
assertEquals(iCodeMax, manager.codeUnits().getBefore(0, b.data(0x0000)));
|
||||
|
@ -1170,7 +1170,7 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes
|
|||
manager.undefinedData().getFloor(0, b.data(0x0003)));
|
||||
|
||||
try (UndoableTransaction tid = b.startTransaction()) {
|
||||
iCodeMax = b.addInstruction(0, b.addr(-0x0002), null, b.buf(0xf4, 0));
|
||||
iCodeMax = b.addInstruction(0, b.addr(-0x0002), b.host, b.buf(0xf4, 0));
|
||||
}
|
||||
TraceData uCodePre = manager.undefinedData().getAt(0, b.addr(-0x0003));
|
||||
assertUndefinedWithAddr(b.addr(-0x0003), uCodePre);
|
||||
|
@ -1209,7 +1209,7 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes
|
|||
try (UndoableTransaction tid = b.startTransaction()) {
|
||||
d4000 = b.addData(0, b.addr(0x4000), IntegerDataType.dataType, b.buf(1, 2, 3, 4));
|
||||
d4000.setEndSnap(9);
|
||||
i4008 = b.addInstruction(0, b.addr(0x4008), null, b.buf(0xf4, 0));
|
||||
i4008 = b.addInstruction(0, b.addr(0x4008), b.host, b.buf(0xf4, 0));
|
||||
i4008.setEndSnap(9);
|
||||
}
|
||||
|
||||
|
@ -1344,7 +1344,7 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes
|
|||
d4000.setEndSnap(9);
|
||||
d4004 = b.addData(0, b.addr(0x4004), IntegerDataType.dataType, b.buf(5, 6, 7, 8));
|
||||
d4004.setEndSnap(5);
|
||||
i4008 = b.addInstruction(0, b.addr(0x4008), null, b.buf(0xf4, 0));
|
||||
i4008 = b.addInstruction(0, b.addr(0x4008), b.host, b.buf(0xf4, 0));
|
||||
i4008.setEndSnap(9);
|
||||
}
|
||||
|
||||
|
@ -1414,7 +1414,7 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes
|
|||
d4000.setEndSnap(9);
|
||||
d4004 = b.addData(0, b.addr(0x4004), IntegerDataType.dataType, b.buf(5, 6, 7, 8));
|
||||
d4004.setEndSnap(5);
|
||||
i4008 = b.addInstruction(0, b.addr(0x4008), null, b.buf(0xf4, 0));
|
||||
i4008 = b.addInstruction(0, b.addr(0x4008), b.host, b.buf(0xf4, 0));
|
||||
i4008.setEndSnap(9);
|
||||
}
|
||||
|
||||
|
@ -1489,7 +1489,7 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes
|
|||
d4000.setEndSnap(9);
|
||||
d4004 = b.addData(0, b.addr(0x4004), IntegerDataType.dataType, b.buf(5, 6, 7, 8));
|
||||
d4004.setEndSnap(5);
|
||||
i4008 = b.addInstruction(0, b.addr(0x4008), null, b.buf(0xf4, 0));
|
||||
i4008 = b.addInstruction(0, b.addr(0x4008), b.host, b.buf(0xf4, 0));
|
||||
i4008.setEndSnap(9);
|
||||
}
|
||||
|
||||
|
@ -1573,7 +1573,7 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes
|
|||
d4000.setEndSnap(9);
|
||||
d4004 = b.addData(0, b.addr(0x4004), IntegerDataType.dataType, b.buf(5, 6, 7, 8));
|
||||
d4004.setEndSnap(5);
|
||||
i4008 = b.addInstruction(0, b.addr(0x4008), null, b.buf(0xf4, 0));
|
||||
i4008 = b.addInstruction(0, b.addr(0x4008), b.host, b.buf(0xf4, 0));
|
||||
i4008.setEndSnap(9);
|
||||
}
|
||||
|
||||
|
@ -1672,7 +1672,7 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes
|
|||
d4000.setEndSnap(9);
|
||||
d4004 = b.addData(0, b.addr(0x4004), IntegerDataType.dataType, b.buf(5, 6, 7, 8));
|
||||
d4004.setEndSnap(5);
|
||||
i4008 = b.addInstruction(0, b.addr(0x4008), null, b.buf(0xf4, 0));
|
||||
i4008 = b.addInstruction(0, b.addr(0x4008), b.host, b.buf(0xf4, 0));
|
||||
i4008.setEndSnap(9);
|
||||
|
||||
// Clear one of the data before a context space is created
|
||||
|
@ -1720,7 +1720,7 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes
|
|||
guest = langMan.addGuestPlatform(x86.getDefaultCompilerSpec());
|
||||
mappedRange = guest.addMappedRange(b.addr(0x0000), b.addr(guest, 0x0000), 1L << 32);
|
||||
g4000 = b.addInstruction(0, b.addr(0x4000), guest, b.buf(0x90));
|
||||
i4001 = b.addInstruction(0, b.addr(0x4001), null, b.buf(0xf4, 0));
|
||||
i4001 = b.addInstruction(0, b.addr(0x4001), b.host, b.buf(0xf4, 0));
|
||||
d4003 = b.addData(0, b.addr(0x4003), LongDataType.dataType, b.buf(1, 2, 3, 4));
|
||||
}
|
||||
|
||||
|
@ -1738,7 +1738,7 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes
|
|||
// TODO: Related to GP-479?
|
||||
g4000 = manager.instructions().getAt(0, b.addr(0x4000));
|
||||
assertNotNull(g4000);
|
||||
assertEquals(guest, g4000.getGuestPlatform());
|
||||
assertEquals(guest, g4000.getPlatform());
|
||||
try (UndoableTransaction tid = b.startTransaction()) {
|
||||
guest.delete(new ConsoleTaskMonitor());
|
||||
}
|
||||
|
@ -1753,7 +1753,7 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes
|
|||
@Test
|
||||
public void testSaveAndLoad() throws Exception {
|
||||
try (UndoableTransaction tid = b.startTransaction()) {
|
||||
b.addInstruction(0, b.addr(0x4004), null, b.buf(0xf4, 0));
|
||||
b.addInstruction(0, b.addr(0x4004), b.host, b.buf(0xf4, 0));
|
||||
|
||||
TraceThread thread = b.getOrAddThread("Thread 1", 0);
|
||||
DBTraceCodeRegisterSpace regCode = manager.getCodeRegisterSpace(thread, true);
|
||||
|
@ -1801,7 +1801,7 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes
|
|||
@Test
|
||||
public void testUndoThenRedo() throws Exception {
|
||||
try (UndoableTransaction tid = b.startTransaction()) {
|
||||
b.addInstruction(0, b.addr(0x4004), null, b.buf(0xf4, 0));
|
||||
b.addInstruction(0, b.addr(0x4004), b.host, b.buf(0xf4, 0));
|
||||
|
||||
TraceThread thread = b.getOrAddThread("Thread 1", 0);
|
||||
DBTraceCodeRegisterSpace regCode = manager.getCodeRegisterSpace(thread, true);
|
||||
|
@ -1854,7 +1854,7 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes
|
|||
b.trace.getBaseAddressFactory().getDefaultAddressSpace());
|
||||
DBTraceCodeSpace space = manager.getCodeSpace(os, true);
|
||||
|
||||
b.addInstruction(0, os.getAddress(0x4004), null, b.buf(0xf4, 0));
|
||||
b.addInstruction(0, os.getAddress(0x4004), b.host, b.buf(0xf4, 0));
|
||||
|
||||
List<CodeUnit> all = new ArrayList<>();
|
||||
space.definedUnits().get(0, true).forEach(all::add);
|
||||
|
|
|
@ -242,7 +242,7 @@ public class DBTraceCodeUnitTest extends AbstractGhidraHeadlessIntegrationTest
|
|||
TraceOverlappedRegionException, DuplicateNameException {
|
||||
TraceInstruction ins;
|
||||
try (UndoableTransaction tid = b.startTransaction()) {
|
||||
ins = b.addInstruction(0, b.addr(0x4004), null, b.buf(0xf4, 0));
|
||||
ins = b.addInstruction(0, b.addr(0x4004), b.host, b.buf(0xf4, 0));
|
||||
}
|
||||
TraceData und = manager.undefinedData().getAt(0, b.addr(0x4006));
|
||||
|
||||
|
@ -281,7 +281,7 @@ public class DBTraceCodeUnitTest extends AbstractGhidraHeadlessIntegrationTest
|
|||
public void testGetProgram() throws CodeUnitInsertionException {
|
||||
TraceInstruction i4004;
|
||||
try (UndoableTransaction tid = b.startTransaction()) {
|
||||
i4004 = b.addInstruction(0, b.addr(0x4004), null, b.buf(0xf4, 0));
|
||||
i4004 = b.addInstruction(0, b.addr(0x4004), b.host, b.buf(0xf4, 0));
|
||||
}
|
||||
|
||||
assertEquals(0, i4004.getProgram().getSnap());
|
||||
|
@ -291,7 +291,7 @@ public class DBTraceCodeUnitTest extends AbstractGhidraHeadlessIntegrationTest
|
|||
public void testGetMemory() throws CodeUnitInsertionException {
|
||||
TraceInstruction i4004;
|
||||
try (UndoableTransaction tid = b.startTransaction()) {
|
||||
i4004 = b.addInstruction(0, b.addr(0x4004), null, b.buf(0xf4, 0));
|
||||
i4004 = b.addInstruction(0, b.addr(0x4004), b.host, b.buf(0xf4, 0));
|
||||
}
|
||||
|
||||
assertEquals(i4004.getProgram().getMemory(), i4004.getMemory());
|
||||
|
@ -304,7 +304,7 @@ public class DBTraceCodeUnitTest extends AbstractGhidraHeadlessIntegrationTest
|
|||
TraceInstruction i4004;
|
||||
TraceInstruction g4006;
|
||||
try (UndoableTransaction tid = b.startTransaction()) {
|
||||
i4004 = b.addInstruction(0, b.addr(0x4004), null, b.buf(0xf4, 0));
|
||||
i4004 = b.addInstruction(0, b.addr(0x4004), b.host, b.buf(0xf4, 0));
|
||||
guest = b.trace.getPlatformManager().addGuestPlatform(x86.getDefaultCompilerSpec());
|
||||
guest.addMappedRange(b.addr(0x0000), b.addr(guest, 0x0000), 1L << 32);
|
||||
g4006 = b.addInstruction(0, b.addr(0x4006), guest, b.buf(0x90));
|
||||
|
@ -319,8 +319,8 @@ public class DBTraceCodeUnitTest extends AbstractGhidraHeadlessIntegrationTest
|
|||
TraceInstruction i4004;
|
||||
TraceInstruction i4006;
|
||||
try (UndoableTransaction tid = b.startTransaction()) {
|
||||
i4004 = b.addInstruction(0, b.addr(0x4004), null, b.buf(0xf4, 0));
|
||||
i4006 = b.addInstruction(0, b.addr(0x4006), null, b.buf(0xf4, 0));
|
||||
i4004 = b.addInstruction(0, b.addr(0x4004), b.host, b.buf(0xf4, 0));
|
||||
i4006 = b.addInstruction(0, b.addr(0x4006), b.host, b.buf(0xf4, 0));
|
||||
}
|
||||
assertFalse(i4004.hasProperty("myVoid"));
|
||||
|
||||
|
@ -455,8 +455,8 @@ public class DBTraceCodeUnitTest extends AbstractGhidraHeadlessIntegrationTest
|
|||
TraceInstruction i4004;
|
||||
TraceInstruction i4006;
|
||||
try (UndoableTransaction tid = b.startTransaction()) {
|
||||
i4004 = b.addInstruction(0, b.addr(0x4004), null, b.buf(0xf4, 0));
|
||||
i4006 = b.addInstruction(0, b.addr(0x4006), null, b.buf(0xf4, 0));
|
||||
i4004 = b.addInstruction(0, b.addr(0x4004), b.host, b.buf(0xf4, 0));
|
||||
i4006 = b.addInstruction(0, b.addr(0x4006), b.host, b.buf(0xf4, 0));
|
||||
}
|
||||
|
||||
try (UndoableTransaction tid = b.startTransaction()) {
|
||||
|
@ -510,7 +510,7 @@ public class DBTraceCodeUnitTest extends AbstractGhidraHeadlessIntegrationTest
|
|||
// TODO: Decide whether or not to shrink the comment lifespan with the unit lifespan
|
||||
assertEquals(Range.atLeast(0L), c4004.getLifespan());
|
||||
|
||||
i4004_10 = b.addInstruction(10, b.addr(0x4004), null);
|
||||
i4004_10 = b.addInstruction(10, b.addr(0x4004), b.host);
|
||||
i4004_10.setComment(CodeUnit.PRE_COMMENT, "Get this back in the mix");
|
||||
i4004_10.setComment(CodeUnit.EOL_COMMENT, "A different comment");
|
||||
}
|
||||
|
@ -538,8 +538,8 @@ public class DBTraceCodeUnitTest extends AbstractGhidraHeadlessIntegrationTest
|
|||
TraceInstruction i4006;
|
||||
TraceData d4008;
|
||||
try (UndoableTransaction tid = b.startTransaction()) {
|
||||
i4004 = b.addInstruction(0, b.addr(0x4004), null, b.buf(0xf4, 0));
|
||||
i4006 = b.addInstruction(0, b.addr(0x4006), null, b.buf(0xf4, 0));
|
||||
i4004 = b.addInstruction(0, b.addr(0x4004), b.host, b.buf(0xf4, 0));
|
||||
i4006 = b.addInstruction(0, b.addr(0x4006), b.host, b.buf(0xf4, 0));
|
||||
d4008 = b.addData(0, b.addr(0x4008), LongDataType.dataType, b.buf(1, 2, 3, 4));
|
||||
}
|
||||
|
||||
|
@ -564,8 +564,8 @@ public class DBTraceCodeUnitTest extends AbstractGhidraHeadlessIntegrationTest
|
|||
TraceInstruction i4006;
|
||||
try (UndoableTransaction tid = b.startTransaction()) {
|
||||
d4000 = b.addData(0, b.addr(0x4000), LongDataType.dataType, b.buf(1, 2, 3, 4));
|
||||
i4004 = b.addInstruction(0, b.addr(0x4004), null, b.buf(0xf4, 0));
|
||||
i4006 = b.addInstruction(0, b.addr(0x4006), null, b.buf(0xf4, 0));
|
||||
i4004 = b.addInstruction(0, b.addr(0x4004), b.host, b.buf(0xf4, 0));
|
||||
i4006 = b.addInstruction(0, b.addr(0x4006), b.host, b.buf(0xf4, 0));
|
||||
}
|
||||
Set<TraceReference> refs;
|
||||
|
||||
|
@ -708,7 +708,7 @@ public class DBTraceCodeUnitTest extends AbstractGhidraHeadlessIntegrationTest
|
|||
TraceData undefined;
|
||||
TraceData undReg;
|
||||
try (UndoableTransaction tid = b.startTransaction()) {
|
||||
instruction = b.addInstruction(0, b.addr(0x4004), null, b.buf(0xf4, 0));
|
||||
instruction = b.addInstruction(0, b.addr(0x4004), b.host, b.buf(0xf4, 0));
|
||||
undefined = manager.undefinedData().getAt(0, b.addr(0x4006));
|
||||
|
||||
thread = b.getOrAddThread("Thread 1", 0);
|
||||
|
@ -739,7 +739,7 @@ public class DBTraceCodeUnitTest extends AbstractGhidraHeadlessIntegrationTest
|
|||
TraceInstruction i4004;
|
||||
try (UndoableTransaction tid = b.startTransaction()) {
|
||||
d4000 = b.addData(0, b.addr(0x4000), LongDataType.dataType, b.buf(1, 2, 3, 4));
|
||||
i4004 = b.addInstruction(0, b.addr(0x4004), null, b.buf(0xf4, 0));
|
||||
i4004 = b.addInstruction(0, b.addr(0x4004), b.host, b.buf(0xf4, 0));
|
||||
|
||||
d4000.setEndSnap(9);
|
||||
assertEquals(Range.closed(0L, 9L), d4000.getLifespan());
|
||||
|
@ -1039,13 +1039,13 @@ public class DBTraceCodeUnitTest extends AbstractGhidraHeadlessIntegrationTest
|
|||
.addRegion("myRegion", Range.atLeast(0L),
|
||||
b.range(0x4000, 0x4fff), TraceMemoryFlag.READ);
|
||||
|
||||
i4004 = b.addInstruction(0, b.addr(0x4004), null, b.buf(0xc8, 0x47));
|
||||
i4004 = b.addInstruction(0, b.addr(0x4004), b.host, b.buf(0xc8, 0x47));
|
||||
assertEquals("add r4,#0x7", i4004.toString());
|
||||
i4006 = b.addInstruction(0, b.addr(0x4006), null, b.buf(0xf4, 0));
|
||||
i4006 = b.addInstruction(0, b.addr(0x4006), b.host, b.buf(0xf4, 0));
|
||||
assertEquals("ret", i4006.toString());
|
||||
i4008 = b.addInstruction(0, b.addr(0x4008), null, b.buf(0xff, 0xfc));
|
||||
i4008 = b.addInstruction(0, b.addr(0x4008), b.host, b.buf(0xff, 0xfc));
|
||||
assertEquals("call 0x00004004", i4008.toString());
|
||||
i400a = b.addInstruction(0, b.addr(0x400a), null, b.buf(0xf6, 0x40));
|
||||
i400a = b.addInstruction(0, b.addr(0x400a), b.host, b.buf(0xf6, 0x40));
|
||||
assertEquals("call r4", i400a.toString());
|
||||
}
|
||||
|
||||
|
@ -1191,7 +1191,7 @@ public class DBTraceCodeUnitTest extends AbstractGhidraHeadlessIntegrationTest
|
|||
|
||||
TraceInstruction i4004;
|
||||
try (UndoableTransaction tid = b.startTransaction()) {
|
||||
i4004 = b.addInstruction(0, b.addr(0x4004), null, b.buf(0xf4, 0));
|
||||
i4004 = b.addInstruction(0, b.addr(0x4004), b.host, b.buf(0xf4, 0));
|
||||
}
|
||||
|
||||
// TODO: Test with non-default context
|
||||
|
@ -1240,7 +1240,7 @@ public class DBTraceCodeUnitTest extends AbstractGhidraHeadlessIntegrationTest
|
|||
TraceData d4006;
|
||||
try (UndoableTransaction tid = b.startTransaction()) {
|
||||
d4000 = b.addData(0, b.addr(0x4000), LongDataType.dataType, b.buf(1, 2, 3, 4));
|
||||
i4004 = b.addInstruction(0, b.addr(0x4004), null, b.buf(0xf4, 0));
|
||||
i4004 = b.addInstruction(0, b.addr(0x4004), b.host, b.buf(0xf4, 0));
|
||||
d4006 = b.addData(0, b.addr(0x4006), PointerDataType.dataType,
|
||||
b.buf(0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00));
|
||||
}
|
||||
|
@ -1260,7 +1260,7 @@ public class DBTraceCodeUnitTest extends AbstractGhidraHeadlessIntegrationTest
|
|||
TraceData d4006;
|
||||
try (UndoableTransaction tid = b.startTransaction()) {
|
||||
d4000 = b.addData(0, b.addr(0x4000), LongDataType.dataType, b.buf(1, 2, 3, 4));
|
||||
i4004 = b.addInstruction(0, b.addr(0x4004), null, b.buf(0xf4, 0));
|
||||
i4004 = b.addInstruction(0, b.addr(0x4004), b.host, b.buf(0xf4, 0));
|
||||
d4006 = b.addData(0, b.addr(0x4006), PointerDataType.dataType,
|
||||
b.buf(0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00));
|
||||
}
|
||||
|
@ -1289,7 +1289,7 @@ public class DBTraceCodeUnitTest extends AbstractGhidraHeadlessIntegrationTest
|
|||
TraceInstruction i4004;
|
||||
TraceInstruction g4006;
|
||||
try (UndoableTransaction tid = b.startTransaction()) {
|
||||
i4004 = b.addInstruction(0, b.addr(0x4004), null, b.buf(0xf4, 0));
|
||||
i4004 = b.addInstruction(0, b.addr(0x4004), b.host, b.buf(0xf4, 0));
|
||||
guest = b.trace.getPlatformManager().addGuestPlatform(x86.getDefaultCompilerSpec());
|
||||
guest.addMappedRange(b.addr(0x0000), b.addr(guest, 0x0000), 1L << 32);
|
||||
g4006 = b.addInstruction(0, b.addr(0x4006), guest, b.buf(0x90));
|
||||
|
@ -1320,9 +1320,9 @@ public class DBTraceCodeUnitTest extends AbstractGhidraHeadlessIntegrationTest
|
|||
guest.addMappedRange(b.addr(0x0000), b.addr(guest, 0x0000), 1L << 32);
|
||||
|
||||
d4000 = b.addData(0, b.addr(0x4000), LongDataType.dataType, b.buf(1, 2, 3, 4));
|
||||
i4004 = b.addInstruction(0, b.addr(0x4004), null, b.buf(0xc8, 0x47));
|
||||
i4004 = b.addInstruction(0, b.addr(0x4004), b.host, b.buf(0xc8, 0x47));
|
||||
g4006 = b.addInstruction(0, b.addr(0x4006), guest, b.buf(0x90));
|
||||
i4007 = b.addInstruction(0, b.addr(0x4007), null, b.buf(0xff, 0xfd));
|
||||
i4007 = b.addInstruction(0, b.addr(0x4007), b.host, b.buf(0xff, 0xfd));
|
||||
}
|
||||
TraceData u4009 = manager.undefinedData().getAt(0, b.addr(0x4009));
|
||||
|
||||
|
|
|
@ -114,7 +114,7 @@ public class DBTraceProgramViewListingTest extends AbstractGhidraHeadlessIntegra
|
|||
CodeUnitInsertionException {
|
||||
Instruction ins;
|
||||
try (UndoableTransaction tid = b.startTransaction()) {
|
||||
ins = b.addInstruction(0, b.addr(0x4005), null, b.buf(0xf4, 0));
|
||||
ins = b.addInstruction(0, b.addr(0x4005), b.host, b.buf(0xf4, 0));
|
||||
}
|
||||
assertEquals("ret", ins.toString());
|
||||
}
|
||||
|
@ -138,7 +138,7 @@ public class DBTraceProgramViewListingTest extends AbstractGhidraHeadlessIntegra
|
|||
|
||||
Instruction i4005;
|
||||
try (UndoableTransaction tid = b.startTransaction()) {
|
||||
i4005 = b.addInstruction(0, b.addr(0x4005), null, b.buf(0xf4, 0));
|
||||
i4005 = b.addInstruction(0, b.addr(0x4005), b.host, b.buf(0xf4, 0));
|
||||
}
|
||||
assertEquals(i4005, listing.getCodeUnitAt(b.addr(0x4005)));
|
||||
assertNull(listing.getCodeUnitAt(b.addr(0x4006)));
|
||||
|
@ -163,7 +163,7 @@ public class DBTraceProgramViewListingTest extends AbstractGhidraHeadlessIntegra
|
|||
assertUndefined(listing.getCodeUnitContaining(b.addr(0x4005)));
|
||||
Instruction i4005;
|
||||
try (UndoableTransaction tid = b.startTransaction()) {
|
||||
i4005 = b.addInstruction(0, b.addr(0x4005), null, b.buf(0xf4, 0));
|
||||
i4005 = b.addInstruction(0, b.addr(0x4005), b.host, b.buf(0xf4, 0));
|
||||
}
|
||||
assertEquals(i4005, listing.getCodeUnitContaining(b.addr(0x4005)));
|
||||
assertEquals(i4005, listing.getCodeUnitContaining(b.addr(0x4006)));
|
||||
|
@ -189,7 +189,7 @@ public class DBTraceProgramViewListingTest extends AbstractGhidraHeadlessIntegra
|
|||
assertEquals(b.addr(0x4005), cu.getAddress());
|
||||
Instruction i4005;
|
||||
try (UndoableTransaction tid = b.startTransaction()) {
|
||||
i4005 = b.addInstruction(0, b.addr(0x4005), null, b.buf(0xf4, 0));
|
||||
i4005 = b.addInstruction(0, b.addr(0x4005), b.host, b.buf(0xf4, 0));
|
||||
}
|
||||
assertEquals(i4005, listing.getCodeUnitAfter(b.addr(0x4004)));
|
||||
assertUndefined(cu = listing.getCodeUnitAfter(b.addr(0x4005)));
|
||||
|
@ -215,7 +215,7 @@ public class DBTraceProgramViewListingTest extends AbstractGhidraHeadlessIntegra
|
|||
assertEquals(b.addr(0x4005), cu.getAddress());
|
||||
Instruction i4005;
|
||||
try (UndoableTransaction tid = b.startTransaction()) {
|
||||
i4005 = b.addInstruction(0, b.addr(0x4005), null, b.buf(0xf4, 0));
|
||||
i4005 = b.addInstruction(0, b.addr(0x4005), b.host, b.buf(0xf4, 0));
|
||||
}
|
||||
assertEquals(i4005, listing.getCodeUnitBefore(b.addr(0x4006)));
|
||||
assertUndefined(cu = listing.getCodeUnitBefore(b.addr(0x4005)));
|
||||
|
@ -291,7 +291,7 @@ public class DBTraceProgramViewListingTest extends AbstractGhidraHeadlessIntegra
|
|||
Instruction i4005;
|
||||
try (UndoableTransaction tid = b.startTransaction()) {
|
||||
d4000 = b.addData(0, b.addr(0x4000), Undefined4DataType.dataType, b.buf(1, 2, 3, 4));
|
||||
i4005 = b.addInstruction(0, b.addr(0x4005), null, b.buf(0xf4, 0));
|
||||
i4005 = b.addInstruction(0, b.addr(0x4005), b.host, b.buf(0xf4, 0));
|
||||
}
|
||||
|
||||
sample = takeN(10, listing.getCodeUnits(true));
|
||||
|
@ -355,7 +355,7 @@ public class DBTraceProgramViewListingTest extends AbstractGhidraHeadlessIntegra
|
|||
assertNull(listing.getInstructionAt(b.addr(0x4005)));
|
||||
Instruction i4005;
|
||||
try (UndoableTransaction tid = b.startTransaction()) {
|
||||
i4005 = b.addInstruction(0, b.addr(0x4005), null, b.buf(0xf4, 0));
|
||||
i4005 = b.addInstruction(0, b.addr(0x4005), b.host, b.buf(0xf4, 0));
|
||||
}
|
||||
assertEquals(i4005, listing.getInstructionAt(b.addr(0x4005)));
|
||||
assertNull(listing.getInstructionAt(b.addr(0x4006)));
|
||||
|
@ -368,7 +368,7 @@ public class DBTraceProgramViewListingTest extends AbstractGhidraHeadlessIntegra
|
|||
assertNull(listing.getInstructionContaining(b.addr(0x4005)));
|
||||
Instruction i4005;
|
||||
try (UndoableTransaction tid = b.startTransaction()) {
|
||||
i4005 = b.addInstruction(0, b.addr(0x4005), null, b.buf(0xf4, 0));
|
||||
i4005 = b.addInstruction(0, b.addr(0x4005), b.host, b.buf(0xf4, 0));
|
||||
}
|
||||
assertEquals(i4005, listing.getInstructionContaining(b.addr(0x4005)));
|
||||
assertEquals(i4005, listing.getInstructionContaining(b.addr(0x4006)));
|
||||
|
@ -381,7 +381,7 @@ public class DBTraceProgramViewListingTest extends AbstractGhidraHeadlessIntegra
|
|||
assertNull(listing.getInstructionAfter(b.addr(0x4004)));
|
||||
Instruction i4005;
|
||||
try (UndoableTransaction tid = b.startTransaction()) {
|
||||
i4005 = b.addInstruction(0, b.addr(0x4005), null, b.buf(0xf4, 0));
|
||||
i4005 = b.addInstruction(0, b.addr(0x4005), b.host, b.buf(0xf4, 0));
|
||||
}
|
||||
assertEquals(i4005, listing.getInstructionAfter(b.addr(0x4004)));
|
||||
assertNull(listing.getInstructionAfter(b.addr(0x4005)));
|
||||
|
@ -393,7 +393,7 @@ public class DBTraceProgramViewListingTest extends AbstractGhidraHeadlessIntegra
|
|||
assertNull(listing.getInstructionBefore(b.addr(0x4006)));
|
||||
Instruction i4005;
|
||||
try (UndoableTransaction tid = b.startTransaction()) {
|
||||
i4005 = b.addInstruction(0, b.addr(0x4005), null, b.buf(0xf4, 0));
|
||||
i4005 = b.addInstruction(0, b.addr(0x4005), b.host, b.buf(0xf4, 0));
|
||||
}
|
||||
assertEquals(i4005, listing.getInstructionBefore(b.addr(0x4006)));
|
||||
assertNull(listing.getInstructionBefore(b.addr(0x4005)));
|
||||
|
@ -421,9 +421,9 @@ public class DBTraceProgramViewListingTest extends AbstractGhidraHeadlessIntegra
|
|||
Instruction i4007;
|
||||
Instruction i400a;
|
||||
try (UndoableTransaction tid = b.startTransaction()) {
|
||||
i4005 = b.addInstruction(0, b.addr(0x4005), null, b.buf(0xf4, 0));
|
||||
i4007 = b.addInstruction(0, b.addr(0x4007), null, b.buf(0xf4, 0));
|
||||
i400a = b.addInstruction(0, b.addr(0x400a), null, b.buf(0xf4, 0));
|
||||
i4005 = b.addInstruction(0, b.addr(0x4005), b.host, b.buf(0xf4, 0));
|
||||
i4007 = b.addInstruction(0, b.addr(0x4007), b.host, b.buf(0xf4, 0));
|
||||
i400a = b.addInstruction(0, b.addr(0x400a), b.host, b.buf(0xf4, 0));
|
||||
b.addData(0, b.addr(0x400c), Undefined4DataType.dataType, b.buf(1, 2, 3, 4));
|
||||
}
|
||||
|
||||
|
@ -454,7 +454,7 @@ public class DBTraceProgramViewListingTest extends AbstractGhidraHeadlessIntegra
|
|||
|
||||
assertUndefined(listing.getDataAt(b.addr(0x4005)));
|
||||
try (UndoableTransaction tid = b.startTransaction()) {
|
||||
b.addInstruction(0, b.addr(0x4005), null, b.buf(0xf4, 0));
|
||||
b.addInstruction(0, b.addr(0x4005), b.host, b.buf(0xf4, 0));
|
||||
}
|
||||
assertNull(listing.getDataAt(b.addr(0x4005)));
|
||||
assertNull(listing.getDataAt(b.addr(0x4006)));
|
||||
|
@ -477,7 +477,7 @@ public class DBTraceProgramViewListingTest extends AbstractGhidraHeadlessIntegra
|
|||
|
||||
assertUndefined(listing.getDataContaining(b.addr(0x4005)));
|
||||
try (UndoableTransaction tid = b.startTransaction()) {
|
||||
b.addInstruction(0, b.addr(0x4005), null, b.buf(0xf4, 0));
|
||||
b.addInstruction(0, b.addr(0x4005), b.host, b.buf(0xf4, 0));
|
||||
}
|
||||
assertNull(listing.getDataContaining(b.addr(0x4005)));
|
||||
assertNull(listing.getDataContaining(b.addr(0x4006)));
|
||||
|
@ -502,7 +502,7 @@ public class DBTraceProgramViewListingTest extends AbstractGhidraHeadlessIntegra
|
|||
assertUndefined(cu = listing.getDataAfter(b.addr(0x4004)));
|
||||
assertEquals(b.addr(0x4005), cu.getAddress());
|
||||
try (UndoableTransaction tid = b.startTransaction()) {
|
||||
b.addInstruction(0, b.addr(0x4005), null, b.buf(0xf4, 0));
|
||||
b.addInstruction(0, b.addr(0x4005), b.host, b.buf(0xf4, 0));
|
||||
}
|
||||
assertUndefined(cu = listing.getDataAfter(b.addr(0x4004)));
|
||||
assertEquals(b.addr(0x4007), cu.getAddress());
|
||||
|
@ -526,7 +526,7 @@ public class DBTraceProgramViewListingTest extends AbstractGhidraHeadlessIntegra
|
|||
assertUndefined(cu = listing.getDataBefore(b.addr(0x4006)));
|
||||
assertEquals(b.addr(0x4005), cu.getAddress());
|
||||
try (UndoableTransaction tid = b.startTransaction()) {
|
||||
b.addInstruction(0, b.addr(0x4005), null, b.buf(0xf4, 0));
|
||||
b.addInstruction(0, b.addr(0x4005), b.host, b.buf(0xf4, 0));
|
||||
}
|
||||
assertUndefined(cu = listing.getDataBefore(b.addr(0x4007)));
|
||||
assertEquals(b.addr(0x4004), cu.getAddress());
|
||||
|
@ -600,7 +600,7 @@ public class DBTraceProgramViewListingTest extends AbstractGhidraHeadlessIntegra
|
|||
Data d4000;
|
||||
try (UndoableTransaction tid = b.startTransaction()) {
|
||||
d4000 = b.addData(0, b.addr(0x4000), Undefined4DataType.dataType, b.buf(1, 2, 3, 4));
|
||||
b.addInstruction(0, b.addr(0x4005), null, b.buf(0xf4, 0));
|
||||
b.addInstruction(0, b.addr(0x4005), b.host, b.buf(0xf4, 0));
|
||||
}
|
||||
|
||||
sample = takeN(10, listing.getData(true));
|
||||
|
@ -731,7 +731,7 @@ public class DBTraceProgramViewListingTest extends AbstractGhidraHeadlessIntegra
|
|||
d4004 = b.addData(0, b.addr(0x4004), Undefined4DataType.dataType, b.buf(5, 6, 7, 8));
|
||||
d400a =
|
||||
b.addData(0, b.addr(0x400a), Undefined4DataType.dataType, b.buf(10, 11, 12, 13));
|
||||
b.addInstruction(0, b.addr(0x400e), null, b.buf(0xf4, 0));
|
||||
b.addInstruction(0, b.addr(0x400e), b.host, b.buf(0xf4, 0));
|
||||
}
|
||||
|
||||
assertEquals(List.of(d4000, d4004, d400a), takeN(10, listing.getDefinedData(true)));
|
||||
|
@ -760,7 +760,7 @@ public class DBTraceProgramViewListingTest extends AbstractGhidraHeadlessIntegra
|
|||
|
||||
assertUndefined(listing.getUndefinedDataAt(b.addr(0x4005)));
|
||||
try (UndoableTransaction tid = b.startTransaction()) {
|
||||
b.addInstruction(0, b.addr(0x4005), null, b.buf(0xf4, 0));
|
||||
b.addInstruction(0, b.addr(0x4005), b.host, b.buf(0xf4, 0));
|
||||
}
|
||||
assertNull(listing.getUndefinedDataAt(b.addr(0x4005)));
|
||||
assertNull(listing.getUndefinedDataAt(b.addr(0x4006)));
|
||||
|
@ -783,7 +783,7 @@ public class DBTraceProgramViewListingTest extends AbstractGhidraHeadlessIntegra
|
|||
assertUndefined(cu = listing.getUndefinedDataAfter(b.addr(0x4004), TaskMonitor.DUMMY));
|
||||
assertEquals(b.addr(0x4005), cu.getAddress());
|
||||
try (UndoableTransaction tid = b.startTransaction()) {
|
||||
b.addInstruction(0, b.addr(0x4005), null, b.buf(0xf4, 0));
|
||||
b.addInstruction(0, b.addr(0x4005), b.host, b.buf(0xf4, 0));
|
||||
}
|
||||
assertUndefined(cu = listing.getUndefinedDataAfter(b.addr(0x4004), TaskMonitor.DUMMY));
|
||||
assertEquals(b.addr(0x4007), cu.getAddress());
|
||||
|
@ -805,7 +805,7 @@ public class DBTraceProgramViewListingTest extends AbstractGhidraHeadlessIntegra
|
|||
assertUndefined(cu = listing.getUndefinedDataBefore(b.addr(0x4006), TaskMonitor.DUMMY));
|
||||
assertEquals(b.addr(0x4005), cu.getAddress());
|
||||
try (UndoableTransaction tid = b.startTransaction()) {
|
||||
b.addInstruction(0, b.addr(0x4005), null, b.buf(0xf4, 0));
|
||||
b.addInstruction(0, b.addr(0x4005), b.host, b.buf(0xf4, 0));
|
||||
}
|
||||
assertUndefined(cu = listing.getUndefinedDataBefore(b.addr(0x4007), TaskMonitor.DUMMY));
|
||||
assertEquals(b.addr(0x4004), cu.getAddress());
|
||||
|
@ -816,7 +816,7 @@ public class DBTraceProgramViewListingTest extends AbstractGhidraHeadlessIntegra
|
|||
UnknownInstructionException, CodeUnitInsertionException {
|
||||
try (UndoableTransaction tid = b.startTransaction()) {
|
||||
b.addData(0, b.addr(0x4000), Undefined4DataType.dataType, b.buf(1, 2, 3, 4));
|
||||
b.addInstruction(0, b.addr(0x4005), null, b.buf(0xf4, 0));
|
||||
b.addInstruction(0, b.addr(0x4005), b.host, b.buf(0xf4, 0));
|
||||
}
|
||||
Data cu;
|
||||
|
||||
|
@ -834,7 +834,7 @@ public class DBTraceProgramViewListingTest extends AbstractGhidraHeadlessIntegra
|
|||
UnknownInstructionException, CodeUnitInsertionException, CancelledException {
|
||||
try (UndoableTransaction tid = b.startTransaction()) {
|
||||
b.addData(0, b.addr(0x4000), Undefined4DataType.dataType, b.buf(1, 2, 3, 4));
|
||||
b.addInstruction(0, b.addr(0x4005), null, b.buf(0xf4, 0));
|
||||
b.addInstruction(0, b.addr(0x4005), b.host, b.buf(0xf4, 0));
|
||||
}
|
||||
|
||||
TODO(); // Should I expect OTHER ranges in the undefined set?
|
||||
|
@ -849,7 +849,7 @@ public class DBTraceProgramViewListingTest extends AbstractGhidraHeadlessIntegra
|
|||
assertNull(listing.getDefinedCodeUnitAfter(b.addr(0x3fff)));
|
||||
Instruction i4005;
|
||||
try (UndoableTransaction tid = b.startTransaction()) {
|
||||
i4005 = b.addInstruction(0, b.addr(0x4005), null, b.buf(0xf4, 0));
|
||||
i4005 = b.addInstruction(0, b.addr(0x4005), b.host, b.buf(0xf4, 0));
|
||||
}
|
||||
assertEquals(i4005, listing.getDefinedCodeUnitAfter(b.addr(0x3fff)));
|
||||
assertNull(listing.getDefinedCodeUnitAfter(b.addr(0x4005)));
|
||||
|
@ -873,7 +873,7 @@ public class DBTraceProgramViewListingTest extends AbstractGhidraHeadlessIntegra
|
|||
assertNull(listing.getDefinedCodeUnitBefore(b.addr(0x4000)));
|
||||
Instruction i4005;
|
||||
try (UndoableTransaction tid = b.startTransaction()) {
|
||||
i4005 = b.addInstruction(0, b.addr(0x4005), null, b.buf(0xf4, 0));
|
||||
i4005 = b.addInstruction(0, b.addr(0x4005), b.host, b.buf(0xf4, 0));
|
||||
}
|
||||
assertEquals(i4005, listing.getDefinedCodeUnitBefore(b.addr(0x4006)));
|
||||
assertEquals(d4000, listing.getDefinedCodeUnitBefore(b.addr(0x4005)));
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
*/
|
||||
package generic;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
|
@ -39,7 +39,12 @@ public interface Unique {
|
|||
}
|
||||
T result = it.next();
|
||||
if (it.hasNext()) {
|
||||
throw new AssertionError("Expected exactly one. Got many.");
|
||||
List<T> all = new ArrayList<>();
|
||||
all.add(result);
|
||||
while (it.hasNext()) {
|
||||
all.add(it.next());
|
||||
}
|
||||
throw new AssertionError("Expected exactly one. Got many: " + all);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -24,9 +24,9 @@ import ghidra.program.util.ProgramSelection;
|
|||
|
||||
/**
|
||||
* Interface for ComponentProviders to implement if they support basic navigation and selection
|
||||
* capabilities. Implementing this interface will provide the provider with navigation history
|
||||
* and actions that require navigation or selection. (Search Text, Search Memory, Select bytes,
|
||||
* Select instructions, etc.)
|
||||
* capabilities. Implementing this interface will provide the provider with navigation history and
|
||||
* actions that require navigation or selection. (Search Text, Search Memory, Select bytes, Select
|
||||
* instructions, etc.)
|
||||
*/
|
||||
public interface Navigatable {
|
||||
public static final long DEFAULT_NAVIGATABLE_ID = -1;
|
||||
|
@ -35,6 +35,7 @@ public interface Navigatable {
|
|||
|
||||
/**
|
||||
* Commands this navigatable to goto (display) the given program and location
|
||||
*
|
||||
* @param program the program
|
||||
*
|
||||
* @param location the location in that program to display
|
||||
|
@ -44,18 +45,21 @@ public interface Navigatable {
|
|||
|
||||
/**
|
||||
* Returns the current location of this Navigatable
|
||||
*
|
||||
* @return the current location of this Navigatable
|
||||
*/
|
||||
public ProgramLocation getLocation();
|
||||
|
||||
/**
|
||||
* Returns the current Program of this Navigatable
|
||||
*
|
||||
* @return the current Program of this Navigatable
|
||||
*/
|
||||
public Program getProgram();
|
||||
|
||||
/**
|
||||
* Returns the view state for this navigatable
|
||||
*
|
||||
* @return the view state for this navigatable
|
||||
*/
|
||||
public LocationMemento getMemento();
|
||||
|
@ -69,20 +73,32 @@ public interface Navigatable {
|
|||
|
||||
/**
|
||||
* Returns an icon that represents this Navigatable
|
||||
*
|
||||
* @return the icon
|
||||
*/
|
||||
public Icon getNavigatableIcon();
|
||||
|
||||
/**
|
||||
* Returns true if this Navigatable is "connected". Navigatables are connected if they
|
||||
* produce and consume location and selection events.
|
||||
* Returns true if this Navigatable is "connected". Navigatables are connected if they produce
|
||||
* and consume location and selection events.
|
||||
*
|
||||
* @return true if this Navigatable is "connected"
|
||||
*/
|
||||
public boolean isConnected();
|
||||
|
||||
/**
|
||||
* Return true if this Navigatable is part of the "dynamic analysis" or "debugger" user
|
||||
* interface.
|
||||
*
|
||||
* @return tre if this Navigatable is "dynamic"
|
||||
*/
|
||||
default public boolean isDynamic() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Currently only the 'connected' windows support markers
|
||||
*
|
||||
* @return true if this navigatable supports markers
|
||||
*/
|
||||
public boolean supportsMarkers();
|
||||
|
@ -94,12 +110,14 @@ public interface Navigatable {
|
|||
|
||||
/**
|
||||
* Returns true if this provider is visible
|
||||
*
|
||||
* @return true if visible
|
||||
*/
|
||||
public boolean isVisible();
|
||||
|
||||
/**
|
||||
* Tells this Navigatable to set its selection to the given selection
|
||||
*
|
||||
* @param selection the selection to set.
|
||||
*/
|
||||
public void setSelection(ProgramSelection selection);
|
||||
|
@ -113,30 +131,35 @@ public interface Navigatable {
|
|||
|
||||
/**
|
||||
* Returns the current selection of this Navigatable
|
||||
*
|
||||
* @return the current selection of this Navigatable
|
||||
*/
|
||||
public ProgramSelection getSelection();
|
||||
|
||||
/**
|
||||
* Returns the current highlight of this Navigatable
|
||||
*
|
||||
* @return the current highlight of this Navigatable
|
||||
*/
|
||||
public ProgramSelection getHighlight();
|
||||
|
||||
/**
|
||||
* Returns the current text selection or null
|
||||
*
|
||||
* @return the text selection
|
||||
*/
|
||||
public String getTextSelection();
|
||||
|
||||
/**
|
||||
* Adds a listener to be notified if this Navigatable is terminated
|
||||
*
|
||||
* @param listener the listener to be notified when this Navigatable is closed
|
||||
*/
|
||||
public void addNavigatableListener(NavigatableRemovalListener listener);
|
||||
|
||||
/**
|
||||
* Removes a listener to be notified if this Navigatable is terminated.
|
||||
*
|
||||
* @param listener the listener that no longer should be notified when this Navigatable is
|
||||
* closed.
|
||||
*/
|
||||
|
@ -144,12 +167,14 @@ public interface Navigatable {
|
|||
|
||||
/**
|
||||
* Returns true if this navigatable is no longer valid, false if it is still good
|
||||
*
|
||||
* @return true if this navigatable is no longer valid, false if it is still good
|
||||
*/
|
||||
public boolean isDisposed();
|
||||
|
||||
/**
|
||||
* Returns true if this navigatable supports highlighting
|
||||
*
|
||||
* @return true if this navigatable supports highlighting
|
||||
*/
|
||||
public boolean supportsHighlight();
|
||||
|
|
|
@ -57,6 +57,7 @@ public abstract class AbstractPatchAction extends DockingAction {
|
|||
|
||||
private Program program;
|
||||
private Address address;
|
||||
private CodeUnit codeUnit;
|
||||
|
||||
private final KeyListener listenerForKeys = new KeyAdapter() {
|
||||
@Override
|
||||
|
@ -165,6 +166,15 @@ public abstract class AbstractPatchAction extends DockingAction {
|
|||
return program;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the code unit on which this action was invoked
|
||||
*
|
||||
* @return the current code unit
|
||||
*/
|
||||
protected CodeUnit getCodeUnit() {
|
||||
return codeUnit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the address at which this action was invoked
|
||||
*
|
||||
|
@ -276,14 +286,12 @@ public abstract class AbstractPatchAction extends DockingAction {
|
|||
|
||||
@Override
|
||||
public boolean isAddToPopup(ActionContext context) {
|
||||
if (!(context instanceof ListingActionContext)) {
|
||||
CodeUnit cu = getCodeUnit(context);
|
||||
if (cu == null || !isApplicableToUnit(cu)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ListingActionContext lac = (ListingActionContext) context;
|
||||
if (!isApplicableToUnit(lac.getCodeUnit())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ComponentProvider provider = lac.getComponentProvider();
|
||||
if (!(provider instanceof CodeViewerProvider)) {
|
||||
|
@ -313,10 +321,9 @@ public abstract class AbstractPatchAction extends DockingAction {
|
|||
|
||||
/**
|
||||
* Perform preparation and save any information needed later in {@link #accept()}.
|
||||
*
|
||||
* @param unit the code unit at the user's cursor
|
||||
*/
|
||||
protected abstract void prepare(CodeUnit unit);
|
||||
protected void prepare() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Put the input fields in their place, show them, and place focus appropriately
|
||||
|
@ -333,14 +340,26 @@ public abstract class AbstractPatchAction extends DockingAction {
|
|||
|
||||
/**
|
||||
* Pre-fill the input fields and place the caret appropriately (usually at the end)
|
||||
*
|
||||
* @param unit the code unit at the user's cursor
|
||||
*/
|
||||
protected abstract void fillInputs(CodeUnit unit);
|
||||
protected abstract void fillInputs();
|
||||
|
||||
protected CodeUnit getCodeUnit(ActionContext context) {
|
||||
if (!(context instanceof ListingActionContext)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
ListingActionContext lac = (ListingActionContext) context;
|
||||
prepareLayout(lac);
|
||||
if (codeViewerProvider.isReadOnly()) {
|
||||
return null;
|
||||
}
|
||||
return lac.getCodeUnit();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
if (!(context instanceof ListingActionContext)) {
|
||||
codeUnit = getCodeUnit(context);
|
||||
if (codeUnit == null || !isApplicableToUnit(codeUnit)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -349,10 +368,6 @@ public abstract class AbstractPatchAction extends DockingAction {
|
|||
if (codeViewerProvider.isReadOnly()) {
|
||||
return;
|
||||
}
|
||||
CodeUnit cu = lac.getCodeUnit();
|
||||
if (!isApplicableToUnit(cu)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ProgramLocation cur = lac.getLocation();
|
||||
program = cur.getProgram();
|
||||
|
@ -365,7 +380,7 @@ public abstract class AbstractPatchAction extends DockingAction {
|
|||
return;
|
||||
}
|
||||
|
||||
prepare(cu);
|
||||
prepare();
|
||||
|
||||
ToolOptions displayOptions = tool.getOptions(GhidraOptions.CATEGORY_BROWSER_DISPLAY);
|
||||
Font font = displayOptions.getFont(GhidraOptions.OPTION_BASE_FONT, null);
|
||||
|
@ -377,7 +392,7 @@ public abstract class AbstractPatchAction extends DockingAction {
|
|||
if (!showInputs(fieldPanel)) {
|
||||
return;
|
||||
}
|
||||
fillInputs(cu);
|
||||
fillInputs();
|
||||
|
||||
fieldLayoutManager.layoutContainer(fieldPanel);
|
||||
}
|
||||
|
|
|
@ -15,7 +15,9 @@
|
|||
*/
|
||||
package ghidra.app.plugin.core.assembler;
|
||||
|
||||
import docking.ActionContext;
|
||||
import ghidra.app.CorePluginPackage;
|
||||
import ghidra.app.context.ListingActionContext;
|
||||
import ghidra.app.plugin.PluginCategoryNames;
|
||||
import ghidra.app.plugin.ProgramPlugin;
|
||||
import ghidra.app.plugin.assembler.Assemblers;
|
||||
|
@ -62,10 +64,29 @@ public class AssemblerPlugin extends ProgramPlugin {
|
|||
}
|
||||
|
||||
private void createActions() {
|
||||
patchInstructionAction = new PatchInstructionAction(this, "Patch Instruction");
|
||||
// Debugger provides its own "Patch Instruction" action
|
||||
patchInstructionAction = new PatchInstructionAction(this) {
|
||||
@Override
|
||||
public boolean isEnabledForContext(ActionContext context) {
|
||||
if (!super.isEnabledForContext(context)) {
|
||||
return false;
|
||||
}
|
||||
ListingActionContext lac = (ListingActionContext) context;
|
||||
return !lac.getNavigatable().isDynamic();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAddToPopup(ActionContext context) {
|
||||
if (!super.isAddToPopup(context)) {
|
||||
return false;
|
||||
}
|
||||
ListingActionContext lac = (ListingActionContext) context;
|
||||
return !lac.getNavigatable().isDynamic();
|
||||
}
|
||||
};
|
||||
tool.addAction(patchInstructionAction);
|
||||
|
||||
patchDataAction = new PatchDataAction(this, "Patch Data");
|
||||
patchDataAction = new PatchDataAction(this);
|
||||
tool.addAction(patchDataAction);
|
||||
}
|
||||
|
||||
|
|
|
@ -38,11 +38,8 @@ import ghidra.app.plugin.processors.sleigh.*;
|
|||
import ghidra.framework.Application;
|
||||
import ghidra.framework.ApplicationConfiguration;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.lang.Language;
|
||||
import ghidra.program.model.lang.LanguageID;
|
||||
import ghidra.program.model.listing.Instruction;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
import ghidra.util.NumericUtilities;
|
||||
import resources.ResourceManager;
|
||||
|
||||
|
@ -71,7 +68,6 @@ public class AssemblyDualTextField {
|
|||
protected final AssemblyAutocompletionModel model = new AssemblyAutocompletionModel();
|
||||
protected final AssemblyAutocompleter auto = new AssemblyAutocompleter(model);
|
||||
|
||||
protected Program program;
|
||||
protected Assembler assembler;
|
||||
protected Address address;
|
||||
protected Instruction existing;
|
||||
|
@ -416,42 +412,36 @@ public class AssemblyDualTextField {
|
|||
}
|
||||
|
||||
/**
|
||||
* @see #setProgramLocation(Program, Address)
|
||||
* Set the assembler to use
|
||||
*
|
||||
* @param assembler the assembler
|
||||
*/
|
||||
public void setProgramLocation(ProgramLocation loc) {
|
||||
setProgramLocation(loc.getProgram(), loc.getAddress());
|
||||
public void setAssembler(Assembler assembler) {
|
||||
this.assembler = Objects.requireNonNull(assembler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the current program location
|
||||
* Set the address of the assembly instruction
|
||||
*
|
||||
* <p>
|
||||
* This may cause the construction of a new assembler, if one suitable for the given program's
|
||||
* language has not yet been built.
|
||||
* Note this will reset the existing instruction to null to prevent its accidental re-use. See
|
||||
* {@link #setExisting(Instruction)}.
|
||||
*
|
||||
* @param program the program
|
||||
* @param address the non-null address
|
||||
* @param address the address
|
||||
*/
|
||||
public void setProgramLocation(Program program, Address address) {
|
||||
this.program = program;
|
||||
public void setAddress(Address address) {
|
||||
this.address = Objects.requireNonNull(address);
|
||||
this.existing = program.getListing().getInstructionAt(address);
|
||||
|
||||
this.assembler = Assemblers.getAssembler(program);
|
||||
this.existing = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the language and address without binding to a program
|
||||
* Set the "existing" instruction used for ordering proposed instructions by "most similar"
|
||||
*
|
||||
* @param lang the language
|
||||
* @param addr the address
|
||||
* @see #computePreference(AssemblyResolvedPatterns)
|
||||
* @param existing
|
||||
*/
|
||||
public void setLanguageLocation(Language lang, Address addr) {
|
||||
this.program = null;
|
||||
this.address = addr;
|
||||
this.existing = null;
|
||||
|
||||
this.assembler = Assemblers.getAssembler(lang);
|
||||
public void setExisting(Instruction existing) {
|
||||
this.existing = existing;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -704,7 +694,7 @@ public class AssemblyDualTextField {
|
|||
* @param existing the instruction, if any, currently under the user's cursor
|
||||
* @return a preference
|
||||
*/
|
||||
protected int computePreference(AssemblyResolvedPatterns rc, Instruction existing) {
|
||||
protected int computePreference(AssemblyResolvedPatterns rc) {
|
||||
if (existing == null) {
|
||||
return 0;
|
||||
}
|
||||
|
@ -736,7 +726,7 @@ public class AssemblyDualTextField {
|
|||
* @return the collection of completion items
|
||||
*/
|
||||
protected Collection<AssemblyCompletion> computeCompletions(String text) {
|
||||
final AssemblyPatternBlock ctx = assembler.getContextAt(address);
|
||||
final AssemblyPatternBlock ctx = getContext();
|
||||
|
||||
Set<AssemblyCompletion> result = new TreeSet<>();
|
||||
Collection<AssemblyParseResult> parses = assembler.parseLine(text);
|
||||
|
@ -757,7 +747,8 @@ public class AssemblyDualTextField {
|
|||
parses = assembler.parseLine(fullText);
|
||||
for (AssemblyParseResult parse : parses) {
|
||||
if (!parse.isError()) {
|
||||
AssemblyResolutionResults sems = assembler.resolveTree(parse, address);
|
||||
AssemblyResolutionResults sems =
|
||||
assembler.resolveTree(parse, address, getContext());
|
||||
for (AssemblyResolution ar : sems) {
|
||||
if (ar.isError()) {
|
||||
//result.add(new AssemblyError("", ar.toString()));
|
||||
|
@ -766,7 +757,7 @@ public class AssemblyDualTextField {
|
|||
AssemblyResolvedPatterns rc = (AssemblyResolvedPatterns) ar;
|
||||
for (byte[] ins : rc.possibleInsVals(ctx)) {
|
||||
result.add(new AssemblyInstruction(text, Arrays.copyOf(ins, ins.length),
|
||||
computePreference(rc, existing)));
|
||||
computePreference(rc)));
|
||||
if (!exhaustUndefined) {
|
||||
break;
|
||||
}
|
||||
|
@ -780,6 +771,15 @@ public class AssemblyDualTextField {
|
|||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the context for filtering completed instructions in the auto-completer
|
||||
*
|
||||
* @return the context
|
||||
*/
|
||||
protected AssemblyPatternBlock getContext() {
|
||||
return assembler.getContextAt(address).fillMask();
|
||||
}
|
||||
|
||||
/**
|
||||
* A demonstration of the assembly GUI outside of Ghidra
|
||||
*/
|
||||
|
@ -807,7 +807,8 @@ public class AssemblyDualTextField {
|
|||
SleighLanguage lang = (SleighLanguage) provider.getLanguage(DEMO_LANG_ID);
|
||||
curAddr = lang.getDefaultSpace().getAddress(0);
|
||||
|
||||
input.setLanguageLocation(lang, curAddr);
|
||||
input.setAssembler(Assemblers.getAssembler(lang));
|
||||
input.setAddress(curAddr);
|
||||
|
||||
hbox.add(input.getAssemblyField());
|
||||
hbox.add(input.getMnemonicField());
|
||||
|
@ -827,7 +828,7 @@ public class AssemblyDualTextField {
|
|||
asm.setText(asm.getText() + data);
|
||||
input.clear();
|
||||
curAddr = curAddr.addWrap(ins.getData().length);
|
||||
input.setLanguageLocation(lang, curAddr);
|
||||
input.setAddress(curAddr);
|
||||
addrlabel.setText(String.format(ADDR_FORMAT, curAddr));
|
||||
}
|
||||
});
|
||||
|
|
|
@ -47,10 +47,14 @@ public class PatchDataAction extends AbstractPatchAction {
|
|||
|
||||
private Data data;
|
||||
|
||||
public PatchDataAction(Plugin owner) {
|
||||
this(owner, "Patch Data");
|
||||
}
|
||||
|
||||
public PatchDataAction(Plugin owner, String name) {
|
||||
super(owner, name);
|
||||
|
||||
setPopupMenuData(new MenuData(new String[] { "Patch Data" }, MENU_GROUP));
|
||||
setPopupMenuData(new MenuData(new String[] { name }, MENU_GROUP));
|
||||
setKeyBindingData(new KeyBindingData(KEYBIND_PATCH_DATA));
|
||||
setHelpLocation(new HelpLocation(owner.getName(), "patch_data"));
|
||||
|
||||
|
@ -81,9 +85,8 @@ public class PatchDataAction extends AbstractPatchAction {
|
|||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void prepare(CodeUnit unit) {
|
||||
data = (Data) unit;
|
||||
protected Data getData() {
|
||||
return (Data) getCodeUnit();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -106,8 +109,8 @@ public class PatchDataAction extends AbstractPatchAction {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void fillInputs(CodeUnit unit) {
|
||||
String repr = data.getDefaultValueRepresentation();
|
||||
protected void fillInputs() {
|
||||
String repr = getData().getDefaultValueRepresentation();
|
||||
input.setText(repr);
|
||||
input.setCaretPosition(repr.length());
|
||||
}
|
||||
|
|
|
@ -118,14 +118,14 @@ public class PatchInstructionAction extends AbstractPatchAction {
|
|||
private final Map<Language, CachingSwingWorker<Assembler>> cache =
|
||||
LazyMap.lazyMap(new HashMap<>(), language -> new AssemblerConstructorWorker(language));
|
||||
|
||||
/*test*/ final Map<Language, Boolean> shownWarning =
|
||||
/*test*/ static final Map<Language, Boolean> SHOWN_WARNING =
|
||||
DefaultedMap.defaultedMap(new HashMap<>(), false);
|
||||
|
||||
/*test*/ final AssemblyDualTextField input = new AssemblyDualTextField();
|
||||
/*test*/ final AssemblyDualTextField input = newAssemblyDualTextField();
|
||||
private final ListenerForAccept listenerForAccept = new ListenerForAccept();
|
||||
|
||||
private Language language;
|
||||
private Assembler assembler;
|
||||
protected Language language;
|
||||
protected Assembler assembler;
|
||||
|
||||
// Callback to keep the autocompleter positioned under the fields
|
||||
private FieldPanelOverLayoutListener listenerToMoveAutocompleter = ev -> {
|
||||
|
@ -135,10 +135,14 @@ public class PatchInstructionAction extends AbstractPatchAction {
|
|||
}
|
||||
};
|
||||
|
||||
public PatchInstructionAction(Plugin owner) {
|
||||
this(owner, "Patch Instruction");
|
||||
}
|
||||
|
||||
public PatchInstructionAction(Plugin owner, String name) {
|
||||
super(owner, name);
|
||||
|
||||
setPopupMenuData(new MenuData(new String[] { "Patch Instruction" }, MENU_GROUP));
|
||||
setPopupMenuData(new MenuData(new String[] { name }, MENU_GROUP));
|
||||
setKeyBindingData(new KeyBindingData(KEYBIND_PATCH_INSTRUCTION));
|
||||
setHelpLocation(new HelpLocation(owner.getName(), "patch_instruction"));
|
||||
|
||||
|
@ -151,6 +155,10 @@ public class PatchInstructionAction extends AbstractPatchAction {
|
|||
init();
|
||||
}
|
||||
|
||||
protected AssemblyDualTextField newAssemblyDualTextField() {
|
||||
return new AssemblyDualTextField();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
super.dispose();
|
||||
|
@ -182,24 +190,36 @@ public class PatchInstructionAction extends AbstractPatchAction {
|
|||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void prepare(CodeUnit cu) {
|
||||
language = cu.getProgram().getLanguage();
|
||||
protected void warnLanguage() {
|
||||
AssemblyRating rating = AssemblyRating.valueOf(
|
||||
language.getProperty(ASSEMBLY_RATING + ":" + language.getLanguageID(),
|
||||
AssemblyRating.UNRATED.name()));
|
||||
if (AssemblyRating.PLATINUM != rating) {
|
||||
String message = language.getProperty(ASSEMBLY_MESSAGE + ":" + language.getLanguageID(),
|
||||
rating.message);
|
||||
if (!shownWarning.get(language)) {
|
||||
if (!SHOWN_WARNING.get(language)) {
|
||||
Msg.showWarn(this, null, "Assembler Rating",
|
||||
"<html><body><p style='width: 300px;'>" + message + "</p></body></html>");
|
||||
shownWarning.put(language, true);
|
||||
SHOWN_WARNING.put(language, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected Language getLanguage(CodeUnit cu) {
|
||||
return cu.getProgram().getLanguage();
|
||||
}
|
||||
|
||||
protected Assembler getAssembler(CodeUnit cu) {
|
||||
return Assemblers.getAssembler(cu.getProgram());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void prepare() {
|
||||
CodeUnit cu = getCodeUnit();
|
||||
language = getLanguage(cu);
|
||||
warnLanguage();
|
||||
cache.get(language).get(null);
|
||||
assembler = Assemblers.getAssembler(cu.getProgram());
|
||||
assembler = getAssembler(cu);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -207,9 +227,19 @@ public class PatchInstructionAction extends AbstractPatchAction {
|
|||
input.setFont(font);
|
||||
}
|
||||
|
||||
protected Instruction getExistingInstruction() {
|
||||
Program program = getProgram();
|
||||
if (program == null) {
|
||||
return null;
|
||||
}
|
||||
return program.getListing().getInstructionAt(getAddress());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean showInputs(FieldPanel fieldPanel) {
|
||||
input.setProgramLocation(getProgram(), getAddress());
|
||||
input.setAssembler(assembler);
|
||||
input.setAddress(getAddress());
|
||||
input.setExisting(getExistingInstruction());
|
||||
FieldLocation locMnem = findFieldLocation(getAddress(), "Mnemonic");
|
||||
if (locMnem == null) {
|
||||
Msg.showError(this, fieldPanel, getName(),
|
||||
|
@ -232,9 +262,10 @@ public class PatchInstructionAction extends AbstractPatchAction {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void fillInputs(CodeUnit unit) {
|
||||
if (unit instanceof Instruction) {
|
||||
Instruction ins = (Instruction) unit;
|
||||
protected void fillInputs() {
|
||||
CodeUnit cu = getCodeUnit();
|
||||
if (cu instanceof Instruction) {
|
||||
Instruction ins = (Instruction) cu;
|
||||
String instr = ins.toString();
|
||||
if (ins.isInDelaySlot()) {
|
||||
assert instr.startsWith("_");
|
||||
|
@ -269,6 +300,10 @@ public class PatchInstructionAction extends AbstractPatchAction {
|
|||
// Do nothing. User must select a completion item instead
|
||||
}
|
||||
|
||||
protected void applyPatch(byte[] data) throws MemoryAccessException {
|
||||
assembler.patchProgram(data, getAddress());
|
||||
}
|
||||
|
||||
/**
|
||||
* Accept the given instruction selected by the user
|
||||
*
|
||||
|
@ -279,7 +314,7 @@ public class PatchInstructionAction extends AbstractPatchAction {
|
|||
Address address = getAddress();
|
||||
try (ProgramTransaction trans =
|
||||
ProgramTransaction.open(program, "Assemble @" + address + ": " + input.getText())) {
|
||||
assembler.patchProgram(ins.getData(), address);
|
||||
applyPatch(ins.getData());
|
||||
trans.commit();
|
||||
hide();
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -31,11 +30,4 @@ public class CodeViewerActionContext extends ListingActionContext implements
|
|||
super(provider, provider, location);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if underlying code viewer corresponds to a dynamic listing
|
||||
*/
|
||||
public boolean isDyanmicListing() {
|
||||
return ((CodeViewerProvider) getComponentProvider()).isDynamicListing();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -21,7 +21,6 @@ import ghidra.app.cmd.disassemble.*;
|
|||
import ghidra.app.context.ListingActionContext;
|
||||
import ghidra.app.events.ProgramActivatedPluginEvent;
|
||||
import ghidra.app.plugin.PluginCategoryNames;
|
||||
import ghidra.app.plugin.core.codebrowser.CodeViewerActionContext;
|
||||
import ghidra.framework.options.Options;
|
||||
import ghidra.framework.plugintool.*;
|
||||
import ghidra.framework.plugintool.util.PluginStatus;
|
||||
|
@ -235,9 +234,6 @@ public class DisassemblerPlugin extends Plugin {
|
|||
Program currentProgram = context.getProgram();
|
||||
DisassembleCommand cmd = null;
|
||||
|
||||
boolean isDynamicListing = (context instanceof CodeViewerActionContext &&
|
||||
((CodeViewerActionContext) context).isDyanmicListing());
|
||||
|
||||
if ((currentSelection != null) && (!currentSelection.isEmpty())) {
|
||||
cmd = new DisassembleCommand(currentSelection, null, true);
|
||||
}
|
||||
|
@ -271,13 +267,18 @@ public class DisassemblerPlugin extends Plugin {
|
|||
}
|
||||
}
|
||||
if (cmd != null) {
|
||||
cmd.enableCodeAnalysis(!isDynamicListing); // do not analyze debugger listing
|
||||
// do not analyze debugger listing
|
||||
cmd.enableCodeAnalysis(!context.getNavigatable().isDynamic());
|
||||
tool.executeBackgroundCommand(cmd, currentProgram);
|
||||
}
|
||||
}
|
||||
|
||||
boolean checkDisassemblyEnabled(ListingActionContext context, Address address,
|
||||
boolean followPtr) {
|
||||
// Debugger now has its own Disassemble actions
|
||||
if (context.getNavigatable().isDynamic()) {
|
||||
return false;
|
||||
}
|
||||
ProgramSelection currentSelection = context.getSelection();
|
||||
Program currentProgram = context.getProgram();
|
||||
if ((currentSelection != null) && (!currentSelection.isEmpty())) {
|
||||
|
|
|
@ -30,12 +30,12 @@ import ghidra.app.plugin.core.assembler.AssemblyDualTextField.AssemblyInstructio
|
|||
import ghidra.app.plugin.core.codebrowser.CodeViewerProvider;
|
||||
import ghidra.app.util.viewer.listingpanel.ListingPanel;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.lang.Language;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
import ghidra.test.AbstractGhidraHeadedIntegrationTest;
|
||||
|
||||
public class AssemblerPluginTestHelper {
|
||||
public final AssemblerPlugin assemblerPlugin;
|
||||
private final CodeViewerProvider provider;
|
||||
private final Program program;
|
||||
|
||||
|
@ -46,20 +46,30 @@ public class AssemblerPluginTestHelper {
|
|||
|
||||
private final Listing listing;
|
||||
|
||||
public AssemblerPluginTestHelper(AssemblerPlugin assemblerPlugin, CodeViewerProvider provider,
|
||||
Program program) {
|
||||
this.assemblerPlugin = assemblerPlugin;
|
||||
public AssemblerPluginTestHelper(PatchInstructionAction patchInstructionAction,
|
||||
PatchDataAction patchDataAction, CodeViewerProvider provider, Program program) {
|
||||
this.provider = provider;
|
||||
this.program = program;
|
||||
|
||||
this.patchInstructionAction = assemblerPlugin.patchInstructionAction;
|
||||
this.patchDataAction = assemblerPlugin.patchDataAction;
|
||||
this.instructionInput = assemblerPlugin.patchInstructionAction.input;
|
||||
this.dataInput = assemblerPlugin.patchDataAction.input;
|
||||
this.patchInstructionAction = patchInstructionAction;
|
||||
this.patchDataAction = patchDataAction;
|
||||
this.instructionInput =
|
||||
patchInstructionAction == null ? null : patchInstructionAction.input;
|
||||
this.dataInput = patchDataAction == null ? null : patchDataAction.input;
|
||||
this.listing = program.getListing();
|
||||
|
||||
// Snuff the assembler's warning prompt
|
||||
patchInstructionAction.shownWarning.put(program.getLanguage(), true);
|
||||
snuffWarning(program.getLanguage());
|
||||
}
|
||||
|
||||
public AssemblerPluginTestHelper(AssemblerPlugin assemblerPlugin, CodeViewerProvider provider,
|
||||
Program program) {
|
||||
this(assemblerPlugin.patchInstructionAction, assemblerPlugin.patchDataAction, provider,
|
||||
program);
|
||||
}
|
||||
|
||||
public void snuffWarning(Language language) {
|
||||
PatchInstructionAction.SHOWN_WARNING.put(language, true);
|
||||
}
|
||||
|
||||
public void assertDualFields() {
|
||||
|
@ -69,12 +79,18 @@ public class AssemblerPluginTestHelper {
|
|||
}
|
||||
|
||||
public List<AssemblyCompletion> inputAndGetCompletions(String text) {
|
||||
return AbstractGenericTest.runSwing(() -> {
|
||||
AbstractGenericTest.runSwing(() -> {
|
||||
instructionInput.setText(text);
|
||||
instructionInput.auto.startCompletion(instructionInput.getOperandsField());
|
||||
instructionInput.auto.flushUpdates();
|
||||
return instructionInput.auto.getSuggestions();
|
||||
});
|
||||
return AbstractGenericTest.waitForValue(() -> AbstractGenericTest.runSwing(() -> {
|
||||
List<AssemblyCompletion> suggestions = instructionInput.auto.getSuggestions();
|
||||
if (suggestions.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return suggestions;
|
||||
}));
|
||||
}
|
||||
|
||||
public void goTo(Address address) {
|
||||
|
@ -95,18 +111,21 @@ public class AssemblerPluginTestHelper {
|
|||
|
||||
public Instruction patchInstructionAt(Address address, String expText, String newText) {
|
||||
goTo(address);
|
||||
Language language = patchInstructionAction.getLanguage(listing.getCodeUnitAt(address));
|
||||
snuffWarning(language);
|
||||
|
||||
AbstractDockingTest.performAction(assemblerPlugin.patchInstructionAction, provider, true);
|
||||
AbstractDockingTest.performAction(patchInstructionAction, provider, true);
|
||||
assertDualFields();
|
||||
assertEquals(expText, instructionInput.getText());
|
||||
assertEquals(address, assemblerPlugin.patchInstructionAction.getAddress());
|
||||
assertEquals(address, patchInstructionAction.getAddress());
|
||||
|
||||
List<AssemblyCompletion> completions = inputAndGetCompletions(newText);
|
||||
assertFalse("There are no assembly completion options", completions.isEmpty());
|
||||
AssemblyCompletion first = completions.get(0);
|
||||
assertTrue(first instanceof AssemblyInstruction);
|
||||
AssemblyInstruction ai = (AssemblyInstruction) first;
|
||||
|
||||
AbstractGenericTest.runSwing(() -> assemblerPlugin.patchInstructionAction.accept(ai));
|
||||
AbstractGenericTest.runSwing(() -> patchInstructionAction.accept(ai));
|
||||
AbstractGhidraHeadedIntegrationTest.waitForProgram(program);
|
||||
|
||||
return Objects.requireNonNull(listing.getInstructionAt(address));
|
||||
|
@ -115,14 +134,14 @@ public class AssemblerPluginTestHelper {
|
|||
public Data patchDataAt(Address address, String expText, String newText) {
|
||||
goTo(address);
|
||||
|
||||
AbstractDockingTest.performAction(assemblerPlugin.patchDataAction, provider, true);
|
||||
AbstractDockingTest.performAction(patchDataAction, provider, true);
|
||||
assertTrue(dataInput.isVisible());
|
||||
assertEquals(expText, dataInput.getText());
|
||||
assertEquals(address, assemblerPlugin.patchDataAction.getAddress());
|
||||
assertEquals(address, patchDataAction.getAddress());
|
||||
|
||||
AbstractGenericTest.runSwing(() -> {
|
||||
dataInput.setText(newText);
|
||||
assemblerPlugin.patchDataAction.accept();
|
||||
patchDataAction.accept();
|
||||
});
|
||||
AbstractGhidraHeadedIntegrationTest.waitForProgram(program);
|
||||
|
||||
|
|
|
@ -702,6 +702,10 @@ public class AssemblyResolvedPatterns extends AssemblyResolution {
|
|||
* @return the iterable
|
||||
*/
|
||||
public Iterable<byte[]> possibleInsVals(AssemblyPatternBlock forCtx) {
|
||||
AssemblyPatternBlock ctxCompat = ctx.combine(forCtx);
|
||||
if (ctxCompat == null) {
|
||||
return List.of();
|
||||
}
|
||||
Predicate<byte[]> removeForbidden = (byte[] val) -> {
|
||||
for (AssemblyResolvedPatterns f : forbids) {
|
||||
// If the forbidden length is larger than us, we can ignore it
|
||||
|
|
|
@ -70,6 +70,11 @@ public class ARMAssemblyTest extends AbstractAssemblyTest {
|
|||
assertOneCompatRestExact("ands r0,r5", "28:40", THUMB, 0x00400000, "ands r0,r5");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAssemble_T_movs_r0_r0() {
|
||||
assertOneCompatRestExact("movs r0,r0", "00:00", THUMB, 0x00400000, "movs r0,r0");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAssemble_T_bl_0x00008000() {
|
||||
// What makes this different from the above test is that it jumps backward
|
||||
|
|
|
@ -200,9 +200,8 @@ public abstract class AbstractAssemblyTest extends AbstractGenericTest {
|
|||
*/
|
||||
protected void checkAllExact(AssemblyResolutionResults rr, Collection<String> disassembly,
|
||||
long addr, String ctxstr) {
|
||||
final AssemblyPatternBlock ctx =
|
||||
(ctxstr == null ? context.getDefault() : AssemblyPatternBlock.fromString(ctxstr))
|
||||
.fillMask();
|
||||
final AssemblyPatternBlock ctx = (ctxstr == null ? context.getDefault()
|
||||
: AssemblyPatternBlock.fromString(ctxstr)).fillMask();
|
||||
dbg.println("Checking each: " + disassembly + " ctx:" + ctx);
|
||||
boolean gotOne = false;
|
||||
boolean failedOne = false;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue