mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2024-11-23 12:49:45 +00:00
Merge remote-tracking branch 'origin/GP-1967_Dan_objectBasedDisassembly-REBASED-1--SQUASHED'
This commit is contained in:
commit
9f7979957f
@ -222,7 +222,7 @@
|
||||
<interface name="Access" />
|
||||
<interface name="Attacher" />
|
||||
<interface name="Attachable" />
|
||||
<interface name="Launcher" />
|
||||
<!-- <interface name="Launcher" /> -->
|
||||
<interface name="Deletable" />
|
||||
<interface name="Detachable" />
|
||||
<interface name="Killable" />
|
||||
|
@ -2,6 +2,7 @@ AutoReadMemorySpec
|
||||
DebuggerBot
|
||||
DebuggerMappingOpinion
|
||||
DebuggerModelFactory
|
||||
DebuggerPlatformOpinion
|
||||
DebuggerProgramLaunchOpinion
|
||||
DisassemblyInject
|
||||
LocationTrackingSpec
|
||||
|
@ -0,0 +1,78 @@
|
||||
/* ###
|
||||
* 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.
|
||||
*/
|
||||
import ghidra.app.plugin.core.debug.DebuggerCoordinates;
|
||||
import ghidra.app.script.GhidraScript;
|
||||
import ghidra.app.services.*;
|
||||
import ghidra.dbg.DebuggerObjectModel;
|
||||
import ghidra.dbg.target.TargetObject;
|
||||
import ghidra.dbg.target.TargetRegisterBank;
|
||||
import ghidra.dbg.util.PathMatcher;
|
||||
|
||||
public class RefreshRegistersScript extends GhidraScript {
|
||||
@Override
|
||||
protected void run() throws Exception {
|
||||
// There is no need to fish this from the ObjectUpdateService, you can get it directly
|
||||
DebuggerModelService modelService = state.getTool().getService(DebuggerModelService.class);
|
||||
// The current model is retrieved with one method, no need to stream or filter
|
||||
DebuggerObjectModel model = modelService.getCurrentModel();
|
||||
|
||||
/**
|
||||
* Navigating a model generically requires some introspection. Use the schema. We used to
|
||||
* decouple the descriptions (RegisterContainer) from the values (RegisterBank), but we
|
||||
* always realize the two interfaces on the same no it seems. Still, we need to work with
|
||||
* values, so we search for all the Banks.
|
||||
*/
|
||||
PathMatcher allBanksMatcher =
|
||||
model.getRootSchema().searchFor(TargetRegisterBank.class, true);
|
||||
for (TargetObject objBank : allBanksMatcher.fetchSuccessors(model.fetchModelRoot().get())
|
||||
.get()
|
||||
.values()) {
|
||||
// Because of a bug in our path search, this type check is still necessary :(
|
||||
if (!(objBank instanceof TargetRegisterBank)) {
|
||||
continue;
|
||||
}
|
||||
TargetRegisterBank bank = (TargetRegisterBank) objBank;
|
||||
// This is equivalent to hitting the "Flush Caches" button in the Target Provider
|
||||
bank.invalidateCaches().get();
|
||||
// If you know the names of the registers to read
|
||||
bank.readRegistersNamed("rax", "rbx").get();
|
||||
// Values are coupled to elements, so we can also just refresh them to re-read all
|
||||
bank.fetchElements(true).get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Alternatively, to refresh just the bank for the current thread or frame, we need to
|
||||
* determine the active frame. We'll do that by asking the TraceManagerService. Then,
|
||||
* because that's in "trace land," we'll use the TraceRecorder to map that into "target
|
||||
* land." Generally, you'd then need to navigate the schema as before, but relative to the
|
||||
* target object. Because fetching registers is so common, this is already built in to the
|
||||
* recorder.
|
||||
*/
|
||||
DebuggerTraceManagerService traceManager =
|
||||
state.getTool().getService(DebuggerTraceManagerService.class);
|
||||
// There are also getCurreentTrace(), etc., if you want just the one thing
|
||||
DebuggerCoordinates current = traceManager.getCurrent();
|
||||
|
||||
// Now, we need to get the relevant recorder
|
||||
TraceRecorder recorder = modelService.getRecorder(current.getTrace());
|
||||
// There's a chance of an NPE here if there is no "current frame"
|
||||
TargetRegisterBank bank =
|
||||
recorder.getTargetRegisterBank(current.getThread(), current.getFrame());
|
||||
// Now do the same to the bank as before
|
||||
bank.invalidateCaches().get();
|
||||
bank.fetchElements(true).get();
|
||||
}
|
||||
}
|
@ -76,6 +76,7 @@ public interface DebuggerResources {
|
||||
ImageIcon ICON_TRACE = Trace.TRACE_ICON;
|
||||
ImageIcon ICON_THREAD = ResourceManager.loadImage("images/thread.png");
|
||||
ImageIcon ICON_PROGRAM = ProgramContentHandler.PROGRAM_ICON;
|
||||
ImageIcon ICON_PROCESSOR = ResourceManager.loadImage("images/kcmprocessor.png");
|
||||
|
||||
ImageIcon ICON_LAUNCH = ResourceManager.loadImage("images/launch.png");
|
||||
ImageIcon ICON_ATTACH = ResourceManager.loadImage("images/attach.png");
|
||||
@ -795,6 +796,24 @@ public interface DebuggerResources {
|
||||
}
|
||||
}
|
||||
|
||||
interface ChoosePlatformAction {
|
||||
String NAME = "Choose Platform";
|
||||
String GROUP = GROUP_MAPPING;
|
||||
String DESCRIPTION = "Manually select the target platform";
|
||||
Icon ICON = ICON_PROCESSOR;
|
||||
String HELP_ANCHOR = "choose_platform";
|
||||
|
||||
public static ActionBuilder builder(Plugin owner) {
|
||||
String ownerName = owner.getName();
|
||||
return new ActionBuilder(ownerName, NAME)
|
||||
.description(DESCRIPTION)
|
||||
.menuPath(DebuggerPluginPackage.NAME, NAME)
|
||||
.menuGroup(GROUP)
|
||||
.menuIcon(ICON)
|
||||
.helpLocation(new HelpLocation(ownerName, HELP_ANCHOR));
|
||||
}
|
||||
}
|
||||
|
||||
abstract class AbstractRecordAction extends DockingAction {
|
||||
public static final String NAME = "Record";
|
||||
public static final Icon ICON = ICON_TRACE;
|
||||
|
@ -16,6 +16,7 @@
|
||||
package ghidra.app.plugin.core.debug.gui.register;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.Objects;
|
||||
|
||||
import ghidra.program.model.data.DataType;
|
||||
import ghidra.program.model.lang.Language;
|
||||
@ -31,7 +32,7 @@ public class RegisterRow {
|
||||
public RegisterRow(DebuggerRegistersProvider provider, int number, Register register) {
|
||||
this.provider = provider;
|
||||
this.number = number;
|
||||
this.register = register;
|
||||
this.register = Objects.requireNonNull(register);
|
||||
this.favorite = provider.isFavorite(register);
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,95 @@
|
||||
/* ###
|
||||
* 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.mapping;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
|
||||
import ghidra.app.plugin.core.debug.workflow.DisassembleTraceCommand;
|
||||
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.target.TraceObject;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
public abstract class AbstractDebuggerPlatformMapper implements DebuggerPlatformMapper {
|
||||
protected final PluginTool tool;
|
||||
protected final Trace trace;
|
||||
|
||||
public AbstractDebuggerPlatformMapper(PluginTool tool, Trace trace) {
|
||||
this.tool = tool;
|
||||
this.trace = trace;
|
||||
}
|
||||
|
||||
protected boolean canInterpret(TraceObject newFocus, long snap, TraceObject env,
|
||||
String debugger, String arch, String os, Endian endian) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canInterpret(TraceObject newFocus, long snap) {
|
||||
TraceObject env = DebuggerPlatformOpinion.getEnvironment(newFocus, snap);
|
||||
if (env == null) {
|
||||
return canInterpret(newFocus, snap, env, null, null, null, null);
|
||||
}
|
||||
String debugger = DebuggerPlatformOpinion.getDebugggerFromEnv(env, snap);
|
||||
String arch = DebuggerPlatformOpinion.getArchitectureFromEnv(env, snap);
|
||||
String os = DebuggerPlatformOpinion.getOperatingSystemFromEnv(env, snap);
|
||||
Endian endian = DebuggerPlatformOpinion.getEndianFromEnv(env, snap);
|
||||
return canInterpret(newFocus, snap, env, debugger, arch, os, endian);
|
||||
}
|
||||
|
||||
protected boolean isCancelSilently(Address start, long snap) {
|
||||
return trace.getCodeManager().definedUnits().containsAddress(snap, start);
|
||||
}
|
||||
|
||||
protected Collection<DisassemblyInject> getDisassemblyInjections(TraceObject object) {
|
||||
return Set.of();
|
||||
}
|
||||
|
||||
protected abstract CompilerSpec getCompilerSpec(TraceObject object);
|
||||
|
||||
@Override
|
||||
public DisassemblyResult disassemble(TraceThread thread, TraceObject object,
|
||||
Address start, AddressSetView restricted, long snap, TaskMonitor monitor) {
|
||||
if (isCancelSilently(start, snap)) {
|
||||
return DisassemblyResult.CANCELLED;
|
||||
}
|
||||
TraceGuestPlatform guest =
|
||||
trace.getPlatformManager().getGuestPlatform(getCompilerSpec(object));
|
||||
|
||||
Collection<DisassemblyInject> injects = getDisassemblyInjections(object);
|
||||
DisassembleTraceCommand dis =
|
||||
DisassembleTraceCommand.create(guest, start, restricted);
|
||||
Language language = guest == null ? trace.getBaseLanguage() : guest.getLanguage();
|
||||
AddressSet startSet = new AddressSet(start);
|
||||
for (DisassemblyInject i : injects) {
|
||||
i.pre(tool, dis, trace, language, snap, null, startSet, restricted);
|
||||
}
|
||||
boolean result = dis.applyToTyped(trace.getFixedProgramView(snap), monitor);
|
||||
if (!result) {
|
||||
return DisassemblyResult.failed(dis.getStatusMsg());
|
||||
}
|
||||
for (DisassemblyInject i : injects) {
|
||||
i.post(tool, trace, snap, dis.getDisassembledAddressSet());
|
||||
}
|
||||
return DisassemblyResult.success(!dis.getDisassembledAddressSet().isEmpty());
|
||||
}
|
||||
}
|
@ -13,19 +13,26 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.trace.model.language;
|
||||
package ghidra.app.plugin.core.debug.mapping;
|
||||
|
||||
import java.util.Collection;
|
||||
import ghidra.program.model.lang.CompilerSpec;
|
||||
|
||||
import ghidra.program.model.lang.InstructionSet;
|
||||
import ghidra.program.model.lang.Language;
|
||||
public abstract class AbstractDebuggerPlatformOffer implements DebuggerPlatformOffer {
|
||||
private final String description;
|
||||
protected final CompilerSpec cSpec;
|
||||
|
||||
public interface TraceLanguageManager {
|
||||
Language getBaseLanguage();
|
||||
public AbstractDebuggerPlatformOffer(String description, CompilerSpec cSpec) {
|
||||
this.description = description;
|
||||
this.cSpec = cSpec;
|
||||
}
|
||||
|
||||
TraceGuestLanguage addGuestLanguage(Language language);
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
Collection<TraceGuestLanguage> getGuestLanguages();
|
||||
|
||||
InstructionSet mapGuestInstructionAddressesToHost(InstructionSet instructionSet);
|
||||
@Override
|
||||
public CompilerSpec getCompilerSpec() {
|
||||
return cSpec;
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
/* ###
|
||||
* 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.mapping;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import ghidra.program.model.lang.Endian;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.target.TraceObject;
|
||||
|
||||
public abstract class AbstractDebuggerPlatformOpinion implements DebuggerPlatformOpinion {
|
||||
|
||||
protected abstract Set<DebuggerPlatformOffer> getOffers(TraceObject object, long snap,
|
||||
TraceObject env, String debugger, String arch, String os, Endian endian);
|
||||
|
||||
@Override
|
||||
public Set<DebuggerPlatformOffer> getOffers(Trace trace, TraceObject object, long snap,
|
||||
boolean includeOverrides) {
|
||||
TraceObject env = DebuggerPlatformOpinion.getEnvironment(object, snap);
|
||||
if (env == null) {
|
||||
return getOffers(object, snap, env, null, null, null, null);
|
||||
}
|
||||
String debugger = DebuggerPlatformOpinion.getDebugggerFromEnv(env, snap);
|
||||
String arch = DebuggerPlatformOpinion.getArchitectureFromEnv(env, snap);
|
||||
String os = DebuggerPlatformOpinion.getOperatingSystemFromEnv(env, snap);
|
||||
Endian endian = DebuggerPlatformOpinion.getEndianFromEnv(env, snap);
|
||||
return getOffers(object, snap, env, debugger, arch, os, endian);
|
||||
}
|
||||
}
|
@ -71,7 +71,7 @@ public interface DebuggerMappingOpinion extends ExtensionPoint {
|
||||
*
|
||||
* @param target the target to be recorded, usually a process
|
||||
* @param includeOverrides true to include offers with negative confidence
|
||||
* @return a future which completes with the set of offers
|
||||
* @return a list of offers ordered highest confidence first
|
||||
*/
|
||||
public static List<DebuggerMappingOffer> queryOpinions(TargetObject target,
|
||||
boolean includeOverrides) {
|
||||
|
@ -0,0 +1,58 @@
|
||||
/* ###
|
||||
* 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.mapping;
|
||||
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressSetView;
|
||||
import ghidra.trace.model.target.TraceObject;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
/**
|
||||
* An object for interpreting a trace according to a chosen platform
|
||||
*/
|
||||
public interface DebuggerPlatformMapper {
|
||||
/**
|
||||
* Prepare the given trace for interpretation under this mapper
|
||||
*
|
||||
* @param trace the trace
|
||||
* @param snap the snap
|
||||
*/
|
||||
void addToTrace(long snap);
|
||||
|
||||
/**
|
||||
* When focus changes, decide if this mapper should remain active
|
||||
*
|
||||
* @param newFocus the newly-focused object
|
||||
* @param snap the snap, usually the current snap
|
||||
* @return true to remain active, false to select a new mapper
|
||||
*/
|
||||
boolean canInterpret(TraceObject newFocus, long snap);
|
||||
|
||||
/**
|
||||
* Disassemble starting at a given address and snap, limited to a given address set
|
||||
*
|
||||
* @param thread the thread if applicable
|
||||
* @param object the object for platform context
|
||||
* @param start the starting address
|
||||
* @param restricted the limit of disassembly
|
||||
* @param snap the snap, usually the current snap
|
||||
* @param monitor a monitor for the disassembler
|
||||
* @return the result
|
||||
*/
|
||||
DisassemblyResult disassemble(TraceThread thread, TraceObject object,
|
||||
Address start, AddressSetView restricted, long snap, TaskMonitor monitor);
|
||||
}
|
@ -0,0 +1,99 @@
|
||||
/* ###
|
||||
* 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.mapping;
|
||||
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.model.lang.*;
|
||||
import ghidra.program.util.DefaultLanguageService;
|
||||
import ghidra.trace.model.Trace;
|
||||
|
||||
/**
|
||||
* An offer to map from a trace to a Ghidra langauge / compiler
|
||||
*/
|
||||
public interface DebuggerPlatformOffer {
|
||||
|
||||
/**
|
||||
* Get a human-readable description of the offer.
|
||||
*
|
||||
* <p>
|
||||
* Generally, more detailed descriptions imply a higher confidence.
|
||||
*
|
||||
* @return the description
|
||||
*/
|
||||
String getDescription();
|
||||
|
||||
/**
|
||||
* Get the confidence of this offer.
|
||||
*
|
||||
* <p>
|
||||
* Offers with numerically higher confidence are preferred. Negative confidence values are
|
||||
* considered "manual overrides," and so are never selected automatically and are hidden from
|
||||
* prompts by default.
|
||||
*
|
||||
* <p>
|
||||
* TODO: Spec out some standard numbers. Maybe an enum?
|
||||
*
|
||||
* @return the confidence
|
||||
*/
|
||||
int getConfidence();
|
||||
|
||||
/**
|
||||
* Check if the confidence indicates this offer is a manual override.
|
||||
*
|
||||
* @return true if the confidence is negative
|
||||
*/
|
||||
default boolean isOverride() {
|
||||
return getConfidence() < 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the language to which this offer can map
|
||||
*
|
||||
* @return the langauge
|
||||
*/
|
||||
default Language getLanguage() {
|
||||
CompilerSpec cSpec = getCompilerSpec();
|
||||
return cSpec == null ? null : cSpec.getLanguage();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the compiler to which this offer can map
|
||||
*
|
||||
* @return the compiler spec
|
||||
*/
|
||||
CompilerSpec getCompilerSpec();
|
||||
|
||||
default CompilerSpec getCompilerSpec(LanguageID langID, CompilerSpecID cSpecID) {
|
||||
try {
|
||||
LanguageService langServ = DefaultLanguageService.getLanguageService();
|
||||
Language lang = langServ.getLanguage(langID);
|
||||
return cSpecID == null ? lang.getDefaultCompilerSpec()
|
||||
: lang.getCompilerSpecByID(cSpecID);
|
||||
}
|
||||
catch (LanguageNotFoundException | CompilerSpecNotFoundException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the mapper, which implements this offer
|
||||
*
|
||||
* @param tool the plugin tool
|
||||
* @param trace the trace the trace to be mapped
|
||||
* @return the mapper
|
||||
*/
|
||||
DebuggerPlatformMapper take(PluginTool tool, Trace trace);
|
||||
}
|
@ -0,0 +1,158 @@
|
||||
/* ###
|
||||
* 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.mapping;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import com.google.common.collect.Range;
|
||||
|
||||
import ghidra.dbg.target.TargetEnvironment;
|
||||
import ghidra.dbg.util.PathPredicates;
|
||||
import ghidra.program.model.lang.Endian;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.target.TraceObject;
|
||||
import ghidra.trace.model.target.TraceObjectValue;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.classfinder.ClassSearcher;
|
||||
import ghidra.util.classfinder.ExtensionPoint;
|
||||
|
||||
/**
|
||||
* An opinion governing analysis and display of a trace according to a platform (processor, ISA, OS,
|
||||
* ABI, etc.)
|
||||
*
|
||||
* <p>
|
||||
* This is meant for "object-based" traces, which may soon supplant "table-based" traces. The latter
|
||||
* requires mapping between the target model and the trace, and so the UI need not worry about
|
||||
* normalizing; however, without a mapping, nothing works. The former allows for direct recording of
|
||||
* the model into the trace without prior mapping. Instead, the choice of platform and
|
||||
* interpretation is performed by the front-end analysis and display. These are essentially the
|
||||
* counterpart to {@link DebuggerMappingOpinion}.
|
||||
*
|
||||
* <p>
|
||||
* The opinions are queried, each of which may produce zero or more scored offers. Depending on
|
||||
* context and automation, the top offer may be chosen automatically, or the user may be prompted to
|
||||
* select from a sorted list. The chosen offer is then applied. Application here means writing
|
||||
* metadata to the trace database, usually as "guest platforms." The analysis and display use that
|
||||
* metadata to interpret the trace data, e.g., to select a language when disassembling at the
|
||||
* program counter.
|
||||
*/
|
||||
public interface DebuggerPlatformOpinion extends ExtensionPoint {
|
||||
|
||||
Comparator<DebuggerPlatformOffer> HIGHEST_CONFIDENCE_FIRST =
|
||||
Comparator.comparing(o -> -o.getConfidence());
|
||||
|
||||
/**
|
||||
* Find the environment for the given object
|
||||
*
|
||||
* @param object the object, usually the user's focus
|
||||
* @param snap the current snap
|
||||
* @return the environment object, or null
|
||||
*/
|
||||
static TraceObject getEnvironment(TraceObject object, long snap) {
|
||||
if (object == null) {
|
||||
return null;
|
||||
}
|
||||
TraceObject root = object.getRoot();
|
||||
List<String> pathToEnv = root.getTargetSchema()
|
||||
.searchForSuitable(TargetEnvironment.class, object.getCanonicalPath().getKeyList());
|
||||
if (pathToEnv == null) {
|
||||
return null;
|
||||
}
|
||||
return root.getSuccessors(Range.singleton(snap), PathPredicates.pattern(pathToEnv))
|
||||
.findAny()
|
||||
.map(p -> p.getDestination(root))
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
static String getStringAttribute(TraceObject obj, long snap, String key) {
|
||||
TraceObjectValue val = obj.getValue(snap, key);
|
||||
if (val == null) {
|
||||
return null;
|
||||
}
|
||||
return val.getValue().toString();
|
||||
}
|
||||
|
||||
static String getDebugggerFromEnv(TraceObject env, long snap) {
|
||||
return getStringAttribute(env, snap, TargetEnvironment.DEBUGGER_ATTRIBUTE_NAME);
|
||||
}
|
||||
|
||||
static String getArchitectureFromEnv(TraceObject env, long snap) {
|
||||
return getStringAttribute(env, snap, TargetEnvironment.ARCH_ATTRIBUTE_NAME);
|
||||
}
|
||||
|
||||
static String getOperatingSystemFromEnv(TraceObject env, long snap) {
|
||||
return getStringAttribute(env, snap, TargetEnvironment.OS_ATTRIBUTE_NAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the endianness from the given environment
|
||||
*
|
||||
* @param env the environment object
|
||||
* @param snap the current snap
|
||||
* @return the endianness, or null
|
||||
*/
|
||||
static Endian getEndianFromEnv(TraceObject env, long snap) {
|
||||
String strEndian = getStringAttribute(env, snap, TargetEnvironment.ENDIAN_ATTRIBUTE_NAME);
|
||||
if (strEndian == null) {
|
||||
return null;
|
||||
}
|
||||
if (strEndian.toLowerCase().contains("little")) {
|
||||
return Endian.LITTLE;
|
||||
}
|
||||
if (strEndian.toLowerCase().contains("big")) {
|
||||
return Endian.BIG;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Query all known opinions for offers of platform interpretation
|
||||
*
|
||||
* @param trace the trace
|
||||
* @param object the object, usually the one in focus
|
||||
* @param snap the snap
|
||||
* @param includeOverrides true to include offers with negative confidence
|
||||
* @return the list of offers ordered highest confidence first
|
||||
*/
|
||||
static List<DebuggerPlatformOffer> queryOpinions(Trace trace, TraceObject object, long snap,
|
||||
boolean includeOverrides) {
|
||||
List<DebuggerPlatformOffer> result = new ArrayList<>();
|
||||
for (DebuggerPlatformOpinion opinion : ClassSearcher
|
||||
.getInstances(DebuggerPlatformOpinion.class)) {
|
||||
try {
|
||||
Set<DebuggerPlatformOffer> offers =
|
||||
opinion.getOffers(trace, object, snap, includeOverrides);
|
||||
result.addAll(offers);
|
||||
}
|
||||
catch (Exception e) {
|
||||
Msg.error(DebuggerPlatformOpinion.class,
|
||||
"Problem querying opinion " + opinion + " for platform offers: " + e);
|
||||
}
|
||||
}
|
||||
result.sort(HIGHEST_CONFIDENCE_FIRST);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render offers for the given object
|
||||
*
|
||||
* @param object the object, usually the one in focus
|
||||
* @param includeOverrides true to include offers with negative confidence
|
||||
* @return zero or more offers to interpret the target according to a platform
|
||||
*/
|
||||
Set<DebuggerPlatformOffer> getOffers(Trace trace, TraceObject object, long snap,
|
||||
boolean includeOverrides);
|
||||
}
|
@ -0,0 +1,90 @@
|
||||
/* ###
|
||||
* 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.mapping;
|
||||
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.lang.CompilerSpec;
|
||||
import ghidra.program.model.lang.Language;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.guest.TraceGuestPlatform;
|
||||
import ghidra.trace.model.guest.TracePlatformManager;
|
||||
import ghidra.trace.model.target.TraceObject;
|
||||
import ghidra.util.MathUtilities;
|
||||
|
||||
public class DefaultDebuggerPlatformMapper extends AbstractDebuggerPlatformMapper {
|
||||
|
||||
protected static boolean isHarvard(Language language) {
|
||||
return language.getDefaultSpace() != language.getDefaultDataSpace();
|
||||
}
|
||||
|
||||
protected final PluginTool tool;
|
||||
protected final CompilerSpec cSpec;
|
||||
|
||||
public DefaultDebuggerPlatformMapper(PluginTool tool, Trace trace, CompilerSpec cSpec) {
|
||||
super(tool, trace);
|
||||
validate(cSpec);
|
||||
this.tool = tool;
|
||||
this.cSpec = cSpec;
|
||||
}
|
||||
|
||||
protected void validate(CompilerSpec cSpec) {
|
||||
if (isHarvard(cSpec.getLanguage())) {
|
||||
throw new IllegalArgumentException("This mapper cannot handle Harvard guests");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CompilerSpec getCompilerSpec(TraceObject object) {
|
||||
return cSpec;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addToTrace(long snap) {
|
||||
TracePlatformManager platformManager = trace.getPlatformManager();
|
||||
TraceGuestPlatform platform = platformManager.getOrAddGuestPlatform(cSpec);
|
||||
if (platform == null) {
|
||||
return; // It's the host compiler spec
|
||||
}
|
||||
addMappedRanges(platform);
|
||||
}
|
||||
|
||||
protected void addMappedRanges(TraceGuestPlatform platform) {
|
||||
Trace trace = platform.getTrace();
|
||||
AddressSpace hostSpace = trace.getBaseAddressFactory().getDefaultAddressSpace();
|
||||
AddressSpace guestSpace = platform.getAddressFactory().getDefaultAddressSpace();
|
||||
long min = MathUtilities.unsignedMax(hostSpace.getMinAddress().getOffset(),
|
||||
guestSpace.getMinAddress().getOffset());
|
||||
long max = MathUtilities.unsignedMin(hostSpace.getMaxAddress().getOffset(),
|
||||
guestSpace.getMaxAddress().getOffset());
|
||||
Address hostStart = hostSpace.getAddress(min);
|
||||
Address guestStart = guestSpace.getAddress(min);
|
||||
|
||||
/*
|
||||
* TODO: I could perhaps do better, but assuming I'm the only source of mappings, this
|
||||
* should suffice.
|
||||
*/
|
||||
if (platform.getHostAddressSet().contains(hostStart)) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
platform.addMappedRange(hostStart, guestStart, max - min + 1);
|
||||
}
|
||||
catch (AddressOverflowException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
/* ###
|
||||
* 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.mapping;
|
||||
|
||||
public class DisassemblyResult {
|
||||
public static final DisassemblyResult SUCCESS = new DisassemblyResult(true, null);
|
||||
public static final DisassemblyResult CANCELLED = new DisassemblyResult(false, null);
|
||||
|
||||
public static DisassemblyResult failed(String errorMessage) {
|
||||
return new DisassemblyResult(false, errorMessage);
|
||||
}
|
||||
|
||||
public static DisassemblyResult success(boolean atLeastOne) {
|
||||
return atLeastOne ? SUCCESS : CANCELLED;
|
||||
}
|
||||
|
||||
private final boolean atLeastOne;
|
||||
private final String errorMessage;
|
||||
|
||||
public DisassemblyResult(boolean atLeastOne, String errorMessage) {
|
||||
this.atLeastOne = atLeastOne;
|
||||
this.errorMessage = errorMessage;
|
||||
}
|
||||
|
||||
public boolean isAtLeastOne() {
|
||||
return atLeastOne;
|
||||
}
|
||||
|
||||
public boolean isSuccess() {
|
||||
return errorMessage == null;
|
||||
}
|
||||
|
||||
public String getErrorMessage() {
|
||||
return errorMessage;
|
||||
}
|
||||
}
|
@ -0,0 +1,100 @@
|
||||
/* ###
|
||||
* 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.mapping.legacy;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import ghidra.app.plugin.core.debug.mapping.*;
|
||||
import ghidra.app.plugin.core.debug.workflow.DisassemblyInject;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.lifecycle.Transitional;
|
||||
import ghidra.program.model.lang.CompilerSpec;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.target.TraceObject;
|
||||
import ghidra.util.classfinder.ClassSearcher;
|
||||
|
||||
/**
|
||||
* An opinion which retains the front-end functionality when using the mapped recorder, i.e., when
|
||||
* displaying non-object-based traces.
|
||||
*/
|
||||
@Transitional
|
||||
public class LegacyDebuggerPlatformOpinion implements DebuggerPlatformOpinion {
|
||||
|
||||
protected static class LegacyDebuggerPlatformMapper extends AbstractDebuggerPlatformMapper {
|
||||
public LegacyDebuggerPlatformMapper(PluginTool tool, Trace trace) {
|
||||
super(tool, trace);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CompilerSpec getCompilerSpec(TraceObject object) {
|
||||
return trace.getBaseCompilerSpec();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addToTrace(long snap) {
|
||||
// Nothing to do
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canInterpret(TraceObject newFocus, long snap) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Collection<DisassemblyInject> getDisassemblyInjections(TraceObject object) {
|
||||
// Track an injects set using a listener instead?
|
||||
return ClassSearcher.getInstances(DisassemblyInject.class)
|
||||
.stream()
|
||||
.filter(i -> i.isApplicable(trace))
|
||||
.sorted(Comparator.comparing(i -> i.getPriority()))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
|
||||
enum Offers implements DebuggerPlatformOffer {
|
||||
LEGACY {
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Legacy (Already mapped by recorder)";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getConfidence() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompilerSpec getCompilerSpec() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DebuggerPlatformMapper take(PluginTool tool, Trace trace) {
|
||||
return new LegacyDebuggerPlatformMapper(tool, trace);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<DebuggerPlatformOffer> getOffers(Trace trace, TraceObject focus, long snap,
|
||||
boolean includeOverrides) {
|
||||
if (trace.getObjectManager().getRootObject() != null) {
|
||||
return Set.of();
|
||||
}
|
||||
return Set.of(Offers.LEGACY);
|
||||
}
|
||||
}
|
@ -17,16 +17,13 @@ package ghidra.app.plugin.core.debug.platform.arm;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
import ghidra.app.cmd.disassemble.DisassembleCommand;
|
||||
import ghidra.app.plugin.core.debug.workflow.DisassemblyInject;
|
||||
import ghidra.app.plugin.core.debug.workflow.DisassemblyInjectInfo;
|
||||
import ghidra.app.plugin.core.debug.workflow.*;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.model.address.AddressSetView;
|
||||
import ghidra.program.model.lang.Register;
|
||||
import ghidra.program.model.lang.RegisterValue;
|
||||
import ghidra.program.model.lang.*;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.memory.TraceMemoryRegisterSpace;
|
||||
import ghidra.trace.model.memory.TraceMemoryState;
|
||||
import ghidra.trace.model.program.TraceProgramView;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.util.Msg;
|
||||
|
||||
@ -61,36 +58,37 @@ public class ArmDisassemblyInject implements DisassemblyInject {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pre(PluginTool tool, DisassembleCommand command, TraceProgramView view,
|
||||
TraceThread thread, AddressSetView startSet, AddressSetView restricted) {
|
||||
public void pre(PluginTool tool, DisassembleTraceCommand command, Trace trace,
|
||||
Language language, long snap, TraceThread thread, AddressSetView startSet,
|
||||
AddressSetView restricted) {
|
||||
/**
|
||||
* TODO: There are probably several avenues to figure the TMode. The most important, I think
|
||||
* is the cpsr register, when it's available. For auto-pc, the trace recorder ought to have
|
||||
* recorded cpsr at the recorded tick.
|
||||
*/
|
||||
|
||||
Register cpsrReg = view.getLanguage().getRegister("cpsr");
|
||||
Register tModeReg = view.getLanguage().getRegister("TMode");
|
||||
Register cpsrReg = language.getRegister("cpsr");
|
||||
Register tModeReg = language.getRegister("TMode");
|
||||
|
||||
if (cpsrReg == null || tModeReg == null) {
|
||||
Msg.error(this, "No cpsr or TMode register in ARM language?: " +
|
||||
view.getLanguage().getLanguageID());
|
||||
Msg.error(this,
|
||||
"No cpsr or TMode register in ARM language?: " + language.getLanguageID());
|
||||
return;
|
||||
}
|
||||
|
||||
TraceMemoryRegisterSpace regs =
|
||||
view.getTrace().getMemoryManager().getMemoryRegisterSpace(thread, false);
|
||||
trace.getMemoryManager().getMemoryRegisterSpace(thread, false);
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
if (regs == null || regs.getState(view.getSnap(), cpsrReg) != TraceMemoryState.KNOWN) {
|
||||
if (regs == null || regs.getState(snap, cpsrReg) != TraceMemoryState.KNOWN) {
|
||||
command.setInitialContext(new RegisterValue(tModeReg, BigInteger.ONE));
|
||||
return;
|
||||
}
|
||||
|
||||
RegisterValue cpsrVal = regs.getValue(view.getSnap(), cpsrReg);
|
||||
RegisterValue cpsrVal = regs.getValue(snap, cpsrReg);
|
||||
if (isThumbMode(cpsrVal)) {
|
||||
command.setInitialContext(new RegisterValue(tModeReg, BigInteger.ONE));
|
||||
}
|
||||
|
@ -0,0 +1,83 @@
|
||||
/* ###
|
||||
* 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.platform.dbgeng;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
|
||||
import ghidra.app.plugin.core.debug.mapping.*;
|
||||
import ghidra.app.plugin.core.debug.workflow.DisassemblyInject;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.model.lang.*;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.target.TraceObject;
|
||||
|
||||
public class DbgengDebuggerPlatformOpinion extends AbstractDebuggerPlatformOpinion {
|
||||
protected static final LanguageID LANG_ID_X86_64 = new LanguageID("x86:LE:64:default");
|
||||
protected static final CompilerSpecID COMP_ID_VS = new CompilerSpecID("windows");
|
||||
protected static final Set<DisassemblyInject> INJECTS =
|
||||
Set.of(new DbgengX64DisassemblyInject());
|
||||
|
||||
protected static class DbgengX64DebuggerPlatformMapper extends DefaultDebuggerPlatformMapper {
|
||||
public DbgengX64DebuggerPlatformMapper(PluginTool tool, Trace trace, CompilerSpec cSpec) {
|
||||
super(tool, trace, cSpec);
|
||||
}
|
||||
// TODO: Map registers: efl,rfl,rflags->eflags
|
||||
|
||||
@Override
|
||||
protected Collection<DisassemblyInject> getDisassemblyInjections(TraceObject object) {
|
||||
return INJECTS;
|
||||
}
|
||||
}
|
||||
|
||||
enum Offers implements DebuggerPlatformOffer {
|
||||
// TODO: X86?
|
||||
X64 {
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Dbgeng on Windows x64";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getConfidence() {
|
||||
return 100;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompilerSpec getCompilerSpec() {
|
||||
return getCompilerSpec(LANG_ID_X86_64, COMP_ID_VS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DebuggerPlatformMapper take(PluginTool tool, Trace trace) {
|
||||
return new DbgengX64DebuggerPlatformMapper(tool, trace, getCompilerSpec());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Set<DebuggerPlatformOffer> getOffers(TraceObject object, long snap, TraceObject env,
|
||||
String debugger, String arch, String os, Endian endian) {
|
||||
if (debugger == null || arch == null || !debugger.toLowerCase().contains("dbg")) {
|
||||
return Set.of();
|
||||
}
|
||||
boolean is64Bit = arch.contains("x86_64") || arch.contains("x64_32");
|
||||
if (!is64Bit) {
|
||||
return Set.of();
|
||||
}
|
||||
return Set.of(Offers.X64);
|
||||
}
|
||||
}
|
@ -22,21 +22,20 @@ import java.util.Set;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import ghidra.app.cmd.disassemble.DisassembleCommand;
|
||||
import ghidra.app.plugin.core.debug.workflow.DisassemblyInject;
|
||||
import ghidra.app.plugin.core.debug.workflow.DisassemblyInjectInfo;
|
||||
import ghidra.app.plugin.core.debug.workflow.*;
|
||||
import ghidra.app.services.DebuggerModelService;
|
||||
import ghidra.app.services.TraceRecorder;
|
||||
import ghidra.app.util.bin.MemoryByteProvider;
|
||||
import ghidra.app.util.bin.ByteProvider;
|
||||
import ghidra.app.util.bin.MemBufferByteProvider;
|
||||
import ghidra.app.util.bin.format.pe.*;
|
||||
import ghidra.app.util.bin.format.pe.PortableExecutable.SectionLayout;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.lang.*;
|
||||
import ghidra.program.model.mem.MemBuffer;
|
||||
import ghidra.program.util.ProgramContextImpl;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.modules.TraceModule;
|
||||
import ghidra.trace.model.program.TraceProgramView;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
@ -50,9 +49,9 @@ public class DbgengX64DisassemblyInject implements DisassemblyInject {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pre(PluginTool tool, DisassembleCommand command, TraceProgramView view,
|
||||
TraceThread thread, AddressSetView startSet, AddressSetView restricted) {
|
||||
Trace trace = view.getTrace();
|
||||
public void pre(PluginTool tool, DisassembleTraceCommand command, Trace trace,
|
||||
Language language, long snap, TraceThread thread, AddressSetView startSet,
|
||||
AddressSetView restricted) {
|
||||
AddressRange first = startSet.getFirstRange();
|
||||
if (first == null) {
|
||||
return;
|
||||
@ -60,20 +59,19 @@ public class DbgengX64DisassemblyInject implements DisassemblyInject {
|
||||
DebuggerModelService modelService = tool.getService(DebuggerModelService.class);
|
||||
TraceRecorder recorder = modelService == null ? null : modelService.getRecorder(trace);
|
||||
Collection<? extends TraceModule> modules =
|
||||
trace.getModuleManager().getModulesAt(view.getSnap(), first.getMinAddress());
|
||||
trace.getModuleManager().getModulesAt(snap, first.getMinAddress());
|
||||
Set<Mode> modes = modules.stream()
|
||||
.map(m -> modeForModule(recorder, view, m))
|
||||
.map(m -> modeForModule(recorder, trace, snap, m))
|
||||
.filter(m -> m != Mode.UNK)
|
||||
.collect(Collectors.toSet());
|
||||
if (modes.size() != 1) {
|
||||
return;
|
||||
}
|
||||
Mode mode = modes.iterator().next();
|
||||
Language lang = trace.getBaseLanguage();
|
||||
Register addrsizeReg = lang.getRegister("addrsize");
|
||||
Register opsizeReg = lang.getRegister("opsize");
|
||||
ProgramContextImpl context = new ProgramContextImpl(lang);
|
||||
lang.applyContextSettings(context);
|
||||
Register addrsizeReg = language.getRegister("addrsize");
|
||||
Register opsizeReg = language.getRegister("opsize");
|
||||
ProgramContextImpl context = new ProgramContextImpl(language);
|
||||
language.applyContextSettings(context);
|
||||
RegisterValue ctxVal = context.getDisassemblyContext(first.getMinAddress());
|
||||
if (mode == Mode.X64) {
|
||||
command.setInitialContext(ctxVal
|
||||
@ -88,9 +86,9 @@ public class DbgengX64DisassemblyInject implements DisassemblyInject {
|
||||
// Shouldn't ever get anything else.
|
||||
}
|
||||
|
||||
protected Mode modeForModule(TraceRecorder recorder, TraceProgramView view,
|
||||
protected Mode modeForModule(TraceRecorder recorder, Trace trace, long snap,
|
||||
TraceModule module) {
|
||||
if (recorder != null && recorder.getSnap() == view.getSnap()) {
|
||||
if (recorder != null && recorder.getSnap() == snap) {
|
||||
AddressSet set = new AddressSet();
|
||||
set.add(module.getBase(), module.getBase()); // Recorder should read page
|
||||
try {
|
||||
@ -104,9 +102,10 @@ public class DbgengX64DisassemblyInject implements DisassemblyInject {
|
||||
// Try to parse whatever's there. If 0s, it'll come UNK.
|
||||
}
|
||||
}
|
||||
MemoryByteProvider mbp = new MemoryByteProvider(view.getMemory(), module.getBase());
|
||||
MemBuffer bufferAt = trace.getMemoryManager().getBufferAt(snap, module.getBase());
|
||||
ByteProvider bp = new MemBufferByteProvider(bufferAt);
|
||||
try {
|
||||
PortableExecutable pe = new PortableExecutable(mbp, SectionLayout.MEMORY, false, false);
|
||||
PortableExecutable pe = new PortableExecutable(bp, SectionLayout.MEMORY, false, false);
|
||||
NTHeader ntHeader = pe.getNTHeader();
|
||||
if (ntHeader == null) {
|
||||
return Mode.UNK;
|
||||
|
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.plugin.core.debug.platform.arm;
|
||||
package ghidra.app.plugin.core.debug.platform.frida;
|
||||
|
||||
import java.util.Set;
|
||||
|
@ -0,0 +1,134 @@
|
||||
/* ###
|
||||
* 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.platform.frida;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import ghidra.app.plugin.core.debug.mapping.*;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.model.lang.*;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.target.TraceObject;
|
||||
|
||||
public class FridaDebuggerPlatformOpinion extends AbstractDebuggerPlatformOpinion {
|
||||
protected static final LanguageID LANG_ID_AARCH64 = new LanguageID("AARCH64:LE:64:v8A");
|
||||
protected static final LanguageID LANG_ID_X86 = new LanguageID("x86:LE:32:default");
|
||||
protected static final LanguageID LANG_ID_X86_64 = new LanguageID("x86:LE:64:default");
|
||||
protected static final CompilerSpecID COMP_ID_DEFAULT = new CompilerSpecID("default");
|
||||
protected static final CompilerSpecID COMP_ID_GCC = new CompilerSpecID("gcc");
|
||||
protected static final CompilerSpecID COMP_ID_VS = new CompilerSpecID("windows");
|
||||
|
||||
protected static class FridaDebuggerPlatformMapper
|
||||
extends DefaultDebuggerPlatformMapper {
|
||||
public FridaDebuggerPlatformMapper(PluginTool tool, Trace trace,
|
||||
CompilerSpec cSpec) {
|
||||
super(tool, trace, cSpec);
|
||||
}
|
||||
// TODO: Map registers: rflags<->eflags for x86_64?
|
||||
}
|
||||
|
||||
enum Offers implements DebuggerPlatformOffer {
|
||||
AARCH64_MACOS("Frida on macOS Apple Silicon", LANG_ID_AARCH64, COMP_ID_DEFAULT),
|
||||
I386_LINUX("Frida on Linux i386", LANG_ID_X86, COMP_ID_GCC),
|
||||
I386_MACOS("Frida on macOS i386", LANG_ID_X86, COMP_ID_GCC),
|
||||
I386_WINDOWS("Frida on Windows x86", LANG_ID_X86, COMP_ID_VS),
|
||||
X86_64_LINUX("Frida on Linux x86_64", LANG_ID_X86_64, COMP_ID_GCC),
|
||||
X86_64_MACOS("Frida on macOS x86_64", LANG_ID_X86_64, COMP_ID_GCC),
|
||||
X86_64_WINDOWS("Frida on Windows x64", LANG_ID_X86_64, COMP_ID_VS);
|
||||
|
||||
final String description;
|
||||
final LanguageID langID;
|
||||
final CompilerSpecID cSpecID;
|
||||
|
||||
private Offers(String description, LanguageID langID, CompilerSpecID cSpecID) {
|
||||
this.description = description;
|
||||
this.langID = langID;
|
||||
this.cSpecID = cSpecID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getConfidence() {
|
||||
return 100;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompilerSpec getCompilerSpec() {
|
||||
return getCompilerSpec(langID, cSpecID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DebuggerPlatformMapper take(PluginTool tool, Trace trace) {
|
||||
// TODO: May need these per offer
|
||||
return new FridaDebuggerPlatformMapper(tool, trace, getCompilerSpec());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Set<DebuggerPlatformOffer> getOffers(TraceObject object, long snap, TraceObject env,
|
||||
String debugger, String arch, String os, Endian endian) {
|
||||
if (debugger == null || arch == null ||
|
||||
os == null | !debugger.toLowerCase().contains("frida")) {
|
||||
return Set.of();
|
||||
}
|
||||
String lcOS = os.toLowerCase();
|
||||
boolean isLinux = lcOS.contains("linux");
|
||||
boolean isMacOS = lcOS.contains("darwin") || lcOS.contains("macos");
|
||||
boolean isWindows = lcOS.contains("windows");
|
||||
String lcArch = arch.toLowerCase();
|
||||
// "arm" subsumes "arm64"
|
||||
boolean isARM = lcArch.contains("aarch64") || lcArch.contains("arm");
|
||||
boolean isI386 = lcArch.contains("ia32") || lcArch.contains("x86-32") ||
|
||||
lcArch.contains("x86_32") || lcArch.contains("i386");
|
||||
boolean isX86_64 = lcArch.contains("x64") || lcArch.contains("x86-64") ||
|
||||
lcArch.contains("x86_64") || lcArch.contains("x64-32") || lcArch.contains("x64_32");
|
||||
// TODO: i686? I'd think 32-bit,
|
||||
// but it was listed as 64-bit in FridaX86DebuggerMappingOpinion
|
||||
|
||||
if (isLinux) {
|
||||
if (isI386) {
|
||||
return Set.of(Offers.I386_LINUX);
|
||||
}
|
||||
if (isX86_64) {
|
||||
return Set.of(Offers.X86_64_LINUX);
|
||||
}
|
||||
}
|
||||
if (isMacOS) {
|
||||
if (isARM) {
|
||||
return Set.of(Offers.AARCH64_MACOS);
|
||||
}
|
||||
if (isI386) {
|
||||
return Set.of(Offers.I386_MACOS);
|
||||
}
|
||||
if (isX86_64) {
|
||||
return Set.of(Offers.X86_64_MACOS);
|
||||
}
|
||||
}
|
||||
if (isWindows) {
|
||||
if (isI386) {
|
||||
return Set.of(Offers.I386_WINDOWS);
|
||||
}
|
||||
if (isX86_64) {
|
||||
return Set.of(Offers.X86_64_WINDOWS);
|
||||
}
|
||||
}
|
||||
return Set.of();
|
||||
}
|
||||
}
|
@ -13,13 +13,12 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.plugin.core.debug.platform.arm;
|
||||
package ghidra.app.plugin.core.debug.platform.gdb;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
|
||||
import ghidra.app.plugin.core.debug.mapping.DebuggerMappingOffer;
|
||||
import ghidra.app.plugin.core.debug.platform.gdb.DefaultGdbDebuggerMappingOpinion;
|
||||
import ghidra.dbg.target.TargetObject;
|
||||
import ghidra.program.model.lang.*;
|
||||
import ghidra.program.util.DefaultLanguageService;
|
@ -0,0 +1,99 @@
|
||||
/* ###
|
||||
* 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.platform.gdb;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import ghidra.app.plugin.core.debug.mapping.*;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.model.lang.*;
|
||||
import ghidra.program.util.DefaultLanguageService;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.target.TraceObject;
|
||||
|
||||
public class GdbDebuggerPlatformOpinion extends AbstractDebuggerPlatformOpinion {
|
||||
public static final String EXTERNAL_TOOL = "gnu";
|
||||
public static final CompilerSpecID PREFERRED_CSPEC_ID = new CompilerSpecID("gcc");
|
||||
|
||||
private static final Map<Pair<String, Endian>, List<LanguageCompilerSpecPair>> CACHE =
|
||||
new HashMap<>();
|
||||
|
||||
public static List<LanguageCompilerSpecPair> getCompilerSpecsForGnu(String arch,
|
||||
Endian endian) {
|
||||
synchronized (CACHE) {
|
||||
return CACHE.computeIfAbsent(Pair.of(arch, endian), p -> {
|
||||
LanguageService langServ = DefaultLanguageService.getLanguageService();
|
||||
return langServ.getLanguageCompilerSpecPairs(
|
||||
new ExternalLanguageCompilerSpecQuery(arch, EXTERNAL_TOOL,
|
||||
endian, null, PREFERRED_CSPEC_ID));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
protected static class GdbDebuggerPlatformOffer extends AbstractDebuggerPlatformOffer {
|
||||
public static GdbDebuggerPlatformOffer fromArchLCSP(String arch,
|
||||
LanguageCompilerSpecPair lcsp)
|
||||
throws CompilerSpecNotFoundException, LanguageNotFoundException {
|
||||
return new GdbDebuggerPlatformOffer("Default GDB for " + arch, lcsp.getCompilerSpec());
|
||||
}
|
||||
|
||||
public GdbDebuggerPlatformOffer(String description, CompilerSpec cSpec) {
|
||||
super(description, cSpec);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getConfidence() {
|
||||
return 10;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DebuggerPlatformMapper take(PluginTool tool, Trace trace) {
|
||||
return new GdbDebuggerPlatformMapper(tool, trace, cSpec);
|
||||
}
|
||||
}
|
||||
|
||||
protected static class GdbDebuggerPlatformMapper extends DefaultDebuggerPlatformMapper {
|
||||
public GdbDebuggerPlatformMapper(PluginTool tool, Trace trace, CompilerSpec cSpec) {
|
||||
super(tool, trace, cSpec);
|
||||
}
|
||||
// TODO: eflags<->rflags for amd64 / x86-64
|
||||
}
|
||||
|
||||
protected Set<GdbDebuggerPlatformOffer> offersForLanguageAndCSpec(String arch, Endian endian,
|
||||
LanguageCompilerSpecPair lcsp)
|
||||
throws CompilerSpecNotFoundException, LanguageNotFoundException {
|
||||
return Set.of(GdbDebuggerPlatformOffer.fromArchLCSP("Default GDB for " + arch, lcsp));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Set<DebuggerPlatformOffer> getOffers(TraceObject object, long snap, TraceObject env,
|
||||
String debugger, String arch, String os, Endian endian) {
|
||||
if (!"gdb".equals(debugger.toLowerCase())) {
|
||||
return Set.of();
|
||||
}
|
||||
return getCompilerSpecsForGnu(arch, endian).stream().flatMap(lcsp -> {
|
||||
try {
|
||||
return offersForLanguageAndCSpec(arch, endian, lcsp).stream();
|
||||
}
|
||||
catch (CompilerSpecNotFoundException | LanguageNotFoundException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}).collect(Collectors.toSet());
|
||||
}
|
||||
}
|
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.plugin.core.debug.platform.jvm;
|
||||
package ghidra.app.plugin.core.debug.platform.jdi;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
@ -0,0 +1,90 @@
|
||||
/* ###
|
||||
* 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.platform.jdi;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import ghidra.app.plugin.core.debug.mapping.*;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.model.lang.*;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.target.TraceObject;
|
||||
|
||||
public class JdiDebuggerPlatformOpinion extends AbstractDebuggerPlatformOpinion {
|
||||
protected static final LanguageID LANG_ID_JAVA = new LanguageID("JVM:BE:32:default");
|
||||
protected static final LanguageID LANG_ID_DALVIK = new LanguageID("Dalvik:LE:32:default");
|
||||
protected static final CompilerSpecID COMP_ID_DEFAULT = new CompilerSpecID("default");
|
||||
|
||||
protected static class JdiDebuggerPlatformMapper extends DefaultDebuggerPlatformMapper {
|
||||
// TODO: Delete this class?
|
||||
public JdiDebuggerPlatformMapper(PluginTool tool, Trace trace, CompilerSpec cSpec) {
|
||||
super(tool, trace, cSpec);
|
||||
}
|
||||
}
|
||||
|
||||
enum Offers implements DebuggerPlatformOffer {
|
||||
JAVA_VM("Java Virtual Machine", LANG_ID_JAVA, COMP_ID_DEFAULT),
|
||||
DALVIK_VM("Dalvik Virtual Machine", LANG_ID_DALVIK, COMP_ID_DEFAULT);
|
||||
|
||||
final String description;
|
||||
final LanguageID langID;
|
||||
final CompilerSpecID cSpecID;
|
||||
|
||||
private Offers(String description, LanguageID langID, CompilerSpecID cSpecID) {
|
||||
this.description = description;
|
||||
this.langID = langID;
|
||||
this.cSpecID = cSpecID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getConfidence() {
|
||||
return 100;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompilerSpec getCompilerSpec() {
|
||||
return getCompilerSpec(langID, cSpecID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DebuggerPlatformMapper take(PluginTool tool, Trace trace) {
|
||||
return new JdiDebuggerPlatformMapper(tool, trace, getCompilerSpec());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Set<DebuggerPlatformOffer> getOffers(TraceObject object, long snap, TraceObject env,
|
||||
String debugger, String arch, String os, Endian endian) {
|
||||
if (debugger == null || arch == null || !debugger.contains("Java Debug Interface")) {
|
||||
return Set.of();
|
||||
}
|
||||
boolean isJava = arch.contains("OpenJDK");
|
||||
boolean isDalvik = arch.contains("Dalvik");
|
||||
// NOTE: Not worried about versions
|
||||
if (isJava) {
|
||||
return Set.of(Offers.JAVA_VM);
|
||||
}
|
||||
if (isDalvik) {
|
||||
return Set.of(Offers.DALVIK_VM);
|
||||
}
|
||||
return Set.of();
|
||||
}
|
||||
}
|
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.plugin.core.debug.platform.jvm;
|
||||
package ghidra.app.plugin.core.debug.platform.jdi;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
@ -22,7 +22,7 @@ import ghidra.app.plugin.core.debug.mapping.*;
|
||||
import ghidra.dbg.target.*;
|
||||
import ghidra.program.model.lang.*;
|
||||
|
||||
public class JdiJavaDebuggerMappingOpinion implements DebuggerMappingOpinion {
|
||||
public class JdiJvmDebuggerMappingOpinion implements DebuggerMappingOpinion {
|
||||
protected static final LanguageID LANG_ID_JAVA = new LanguageID("JVM:BE:32:default");
|
||||
protected static final CompilerSpecID COMP_ID_VS = new CompilerSpecID("default");
|
||||
|
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.plugin.core.debug.platform.arm;
|
||||
package ghidra.app.plugin.core.debug.platform.lldb;
|
||||
|
||||
import java.util.Set;
|
||||
|
@ -0,0 +1,134 @@
|
||||
/* ###
|
||||
* 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.platform.lldb;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import ghidra.app.plugin.core.debug.mapping.*;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.model.lang.*;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.target.TraceObject;
|
||||
|
||||
public class LldbDebuggerPlatformOpinion extends AbstractDebuggerPlatformOpinion {
|
||||
protected static final LanguageID LANG_ID_AARCH64 = new LanguageID("AARCH64:LE:64:v8A");
|
||||
protected static final LanguageID LANG_ID_X86 = new LanguageID("x86:LE:32:default");
|
||||
protected static final LanguageID LANG_ID_X86_64 = new LanguageID("x86:LE:64:default");
|
||||
protected static final CompilerSpecID COMP_ID_DEFAULT = new CompilerSpecID("default");
|
||||
protected static final CompilerSpecID COMP_ID_GCC = new CompilerSpecID("gcc");
|
||||
protected static final CompilerSpecID COMP_ID_VS = new CompilerSpecID("windows");
|
||||
|
||||
protected static class LldbDebuggerPlatformMapper
|
||||
extends DefaultDebuggerPlatformMapper {
|
||||
public LldbDebuggerPlatformMapper(PluginTool tool, Trace trace,
|
||||
CompilerSpec cSpec) {
|
||||
super(tool, trace, cSpec);
|
||||
}
|
||||
// TODO: Map registers: rflags<->eflags for x86_64?
|
||||
}
|
||||
|
||||
enum Offers implements DebuggerPlatformOffer {
|
||||
AARCH64_MACOS("LLDB on macOS Apple Silicon", LANG_ID_AARCH64, COMP_ID_DEFAULT),
|
||||
I386_LINUX("LLDB on Linux i386", LANG_ID_X86, COMP_ID_GCC),
|
||||
I386_MACOS("LLDB on macOS i386", LANG_ID_X86, COMP_ID_GCC),
|
||||
I386_WINDOWS("LLDB on Windows x86", LANG_ID_X86, COMP_ID_VS),
|
||||
X86_64_LINUX("LLDB on Linux x86_64", LANG_ID_X86_64, COMP_ID_GCC),
|
||||
X86_64_MACOS("LLDB on macOS x86_64", LANG_ID_X86_64, COMP_ID_GCC),
|
||||
X86_64_WINDOWS("LLDB on Windows x64", LANG_ID_X86_64, COMP_ID_VS);
|
||||
|
||||
final String description;
|
||||
final LanguageID langID;
|
||||
final CompilerSpecID cSpecID;
|
||||
|
||||
private Offers(String description, LanguageID langID, CompilerSpecID cSpecID) {
|
||||
this.description = description;
|
||||
this.langID = langID;
|
||||
this.cSpecID = cSpecID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getConfidence() {
|
||||
return 100;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompilerSpec getCompilerSpec() {
|
||||
return getCompilerSpec(langID, cSpecID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DebuggerPlatformMapper take(PluginTool tool, Trace trace) {
|
||||
// TODO: May need these per offer
|
||||
return new LldbDebuggerPlatformMapper(tool, trace, getCompilerSpec());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Set<DebuggerPlatformOffer> getOffers(TraceObject object, long snap, TraceObject env,
|
||||
String debugger, String arch, String os, Endian endian) {
|
||||
if (debugger == null || arch == null ||
|
||||
os == null | !debugger.toLowerCase().contains("lldb")) {
|
||||
return Set.of();
|
||||
}
|
||||
String lcOS = os.toLowerCase();
|
||||
boolean isLinux = lcOS.contains("linux");
|
||||
boolean isMacOS = lcOS.contains("darwin") || lcOS.contains("macos");
|
||||
boolean isWindows = lcOS.contains("windows");
|
||||
String lcArch = arch.toLowerCase();
|
||||
// "arm" subsumes "arm64"
|
||||
boolean isARM = lcArch.contains("aarch64") || lcArch.contains("arm");
|
||||
boolean isI386 = lcArch.contains("ia32") || lcArch.contains("x86-32") ||
|
||||
lcArch.contains("x86_32") || lcArch.contains("i386");
|
||||
boolean isX86_64 = lcArch.contains("x64") || lcArch.contains("x86-64") ||
|
||||
lcArch.contains("x86_64") || lcArch.contains("x64-32") || lcArch.contains("x64_32");
|
||||
// TODO: i686? I'd think 32-bit,
|
||||
// but it was listed as 64-bit in LldbX86DebuggerMappingOpinion
|
||||
|
||||
if (isLinux) {
|
||||
if (isI386) {
|
||||
return Set.of(Offers.I386_LINUX);
|
||||
}
|
||||
if (isX86_64) {
|
||||
return Set.of(Offers.X86_64_LINUX);
|
||||
}
|
||||
}
|
||||
if (isMacOS) {
|
||||
if (isARM) {
|
||||
return Set.of(Offers.AARCH64_MACOS);
|
||||
}
|
||||
if (isI386) {
|
||||
return Set.of(Offers.I386_MACOS);
|
||||
}
|
||||
if (isX86_64) {
|
||||
return Set.of(Offers.X86_64_MACOS);
|
||||
}
|
||||
}
|
||||
if (isWindows) {
|
||||
if (isI386) {
|
||||
return Set.of(Offers.I386_WINDOWS);
|
||||
}
|
||||
if (isX86_64) {
|
||||
return Set.of(Offers.X86_64_WINDOWS);
|
||||
}
|
||||
}
|
||||
return Set.of();
|
||||
}
|
||||
}
|
@ -516,6 +516,9 @@ public abstract class AbstractDebuggerProgramLaunchOffer implements DebuggerProg
|
||||
() -> onTimedOutRecorder(monitor, service, t));
|
||||
}).thenCompose(r -> {
|
||||
monitor.incrementProgress(1);
|
||||
if (r == null) {
|
||||
throw new CancellationException();
|
||||
}
|
||||
monitor.setMessage("Confirming program is mapped to target");
|
||||
CompletableFuture<Void> futureMapped = listenForMapping(mappingService, r);
|
||||
return AsyncTimer.DEFAULT_TIMER.mark()
|
||||
|
@ -50,7 +50,7 @@ public class EmptyDebuggerRegisterMapper implements DebuggerRegisterMapper {
|
||||
|
||||
@Override
|
||||
public Set<Register> getRegistersOnTarget() {
|
||||
return null;
|
||||
return Set.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -0,0 +1,139 @@
|
||||
/* ###
|
||||
* 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.service.platform;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import docking.action.builder.ActionBuilder;
|
||||
import ghidra.app.plugin.PluginCategoryNames;
|
||||
import ghidra.app.plugin.core.debug.DebuggerPluginPackage;
|
||||
import ghidra.app.plugin.core.debug.event.TraceClosedPluginEvent;
|
||||
import ghidra.app.plugin.core.debug.gui.DebuggerResources.ChoosePlatformAction;
|
||||
import ghidra.app.plugin.core.debug.mapping.*;
|
||||
import ghidra.app.services.DebuggerPlatformService;
|
||||
import ghidra.app.services.DebuggerTraceManagerService;
|
||||
import ghidra.framework.plugintool.*;
|
||||
import ghidra.framework.plugintool.annotation.AutoServiceConsumed;
|
||||
import ghidra.framework.plugintool.util.PluginStatus;
|
||||
import ghidra.lifecycle.Unfinished;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.target.TraceObject;
|
||||
|
||||
@PluginInfo(
|
||||
shortDescription = "Debugger platform service plugin",
|
||||
description = "Selects and manages platforms for the current focus",
|
||||
category = PluginCategoryNames.DEBUGGER,
|
||||
packageName = DebuggerPluginPackage.NAME,
|
||||
status = PluginStatus.RELEASED,
|
||||
eventsConsumed = {
|
||||
TraceClosedPluginEvent.class,
|
||||
},
|
||||
servicesRequired = {
|
||||
DebuggerTraceManagerService.class,
|
||||
},
|
||||
servicesProvided = {
|
||||
DebuggerPlatformService.class,
|
||||
})
|
||||
public class DebuggerPlatformServicePlugin extends Plugin implements DebuggerPlatformService {
|
||||
|
||||
@AutoServiceConsumed
|
||||
private DebuggerTraceManagerService traceManager;
|
||||
@SuppressWarnings("unused")
|
||||
private final AutoService.Wiring autoServiceWiring;
|
||||
|
||||
ActionBuilder actionChoosePlatform;
|
||||
|
||||
private final Map<Trace, DebuggerPlatformMapper> mappersByTrace = new HashMap<>();
|
||||
|
||||
public DebuggerPlatformServicePlugin(PluginTool tool) {
|
||||
super(tool);
|
||||
this.autoServiceWiring = AutoService.wireServicesProvidedAndConsumed(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void init() {
|
||||
super.init();
|
||||
createActions();
|
||||
}
|
||||
|
||||
protected void createActions() {
|
||||
actionChoosePlatform = ChoosePlatformAction.builder(this); // TODO:
|
||||
}
|
||||
|
||||
@Override
|
||||
public DebuggerPlatformMapper getMapper(Trace trace, TraceObject object, long snap) {
|
||||
/**
|
||||
* TODO: There's a chance different components fight over the current mapper. However, I
|
||||
* suspect all nodes in a trace will yield the same offers, so perhaps I should not worry.
|
||||
*/
|
||||
DebuggerPlatformMapper mapper;
|
||||
synchronized (mappersByTrace) {
|
||||
mapper = mappersByTrace.get(trace);
|
||||
if (mapper != null && mapper.canInterpret(object, snap)) {
|
||||
return mapper;
|
||||
}
|
||||
mapper = getNewMapper(trace, object, snap);
|
||||
if (mapper == null) {
|
||||
return null;
|
||||
}
|
||||
mappersByTrace.put(trace, mapper);
|
||||
}
|
||||
mapper.addToTrace(snap);
|
||||
// TODO: Fire a listener
|
||||
return mapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DebuggerPlatformMapper getNewMapper(Trace trace, TraceObject object, long snap) {
|
||||
if (!traceManager.getOpenTraces().contains(trace)) {
|
||||
throw new IllegalArgumentException("Trace is not opened in this tool");
|
||||
}
|
||||
for (DebuggerPlatformOffer offer : DebuggerPlatformOpinion.queryOpinions(trace, object,
|
||||
snap, false)) {
|
||||
return offer.take(tool, trace);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DebuggerPlatformMapper chooseMapper(Trace trace, TraceObject object, long snap) {
|
||||
return Unfinished.TODO();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCurrentMapperFor(Trace trace, DebuggerPlatformMapper mapper, long snap) {
|
||||
if (!traceManager.getOpenTraces().contains(trace)) {
|
||||
throw new IllegalArgumentException("Trace is not opened in this tool");
|
||||
}
|
||||
synchronized (mappersByTrace) {
|
||||
mappersByTrace.put(trace, mapper);
|
||||
}
|
||||
mapper.addToTrace(snap);
|
||||
// TODO: Fire a listener
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processEvent(PluginEvent event) {
|
||||
super.processEvent(event);
|
||||
if (event instanceof TraceClosedPluginEvent) {
|
||||
TraceClosedPluginEvent ev = (TraceClosedPluginEvent) event;
|
||||
synchronized (mappersByTrace) {
|
||||
mappersByTrace.remove(ev.getTrace());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -22,12 +22,15 @@ import javax.swing.event.ChangeListener;
|
||||
|
||||
import com.google.common.collect.Range;
|
||||
|
||||
import ghidra.app.cmd.disassemble.DisassembleCommand;
|
||||
import docking.DockingWindowManager;
|
||||
import docking.Tool;
|
||||
import ghidra.app.plugin.core.debug.mapping.DebuggerPlatformMapper;
|
||||
import ghidra.app.plugin.core.debug.mapping.DisassemblyResult;
|
||||
import ghidra.app.plugin.core.debug.service.workflow.*;
|
||||
import ghidra.app.services.DebuggerBot;
|
||||
import ghidra.app.services.DebuggerBotInfo;
|
||||
import ghidra.app.services.*;
|
||||
import ghidra.async.AsyncDebouncer;
|
||||
import ghidra.async.AsyncTimer;
|
||||
import ghidra.framework.cmd.BackgroundCommand;
|
||||
import ghidra.framework.model.DomainObject;
|
||||
import ghidra.framework.options.annotation.HelpInfo;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
@ -43,6 +46,8 @@ import ghidra.trace.model.listing.*;
|
||||
import ghidra.trace.model.memory.*;
|
||||
import ghidra.trace.model.program.TraceProgramView;
|
||||
import ghidra.trace.model.stack.*;
|
||||
import ghidra.trace.model.target.TraceObject;
|
||||
import ghidra.trace.model.thread.TraceObjectThread;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.trace.util.*;
|
||||
import ghidra.util.*;
|
||||
@ -207,14 +212,18 @@ public class DisassembleAtPcDebuggerBot implements DebuggerBot {
|
||||
if (pcVal == null) {
|
||||
return;
|
||||
}
|
||||
if (range == null || range.contains(pcVal)) {
|
||||
// NOTE: If non-0 frames are ever used, level should be passed in for injects
|
||||
disassemble(pcVal, stack.getThread(), snap);
|
||||
if (range != null && !range.contains(pcVal)) {
|
||||
return;
|
||||
}
|
||||
// NOTE: If non-0 frames are ever used, level should be passed in for injects
|
||||
disassemble(pcVal, stack.getThread(), snap);
|
||||
}
|
||||
|
||||
protected void disassembleRegPcVal(TraceThread thread, int frameLevel, long pcSnap,
|
||||
long memSnap) {
|
||||
if (pc == null) {
|
||||
return;
|
||||
}
|
||||
TraceData pcUnit = null;
|
||||
try (UndoableTransaction tid =
|
||||
UndoableTransaction.start(trace, "Disassemble: PC is code pointer", true)) {
|
||||
@ -258,6 +267,14 @@ public class DisassembleAtPcDebuggerBot implements DebuggerBot {
|
||||
return mrent.getKey().getY1();
|
||||
}
|
||||
|
||||
// TODO: TraceManager should instead track focus object, not thread
|
||||
protected TraceObject getObject(TraceThread thread) {
|
||||
if (!(thread instanceof TraceObjectThread)) {
|
||||
return null;
|
||||
}
|
||||
return ((TraceObjectThread) thread).getObject();
|
||||
}
|
||||
|
||||
protected void disassemble(Address start, TraceThread thread, long snap) {
|
||||
Long knownSnap = isKnownRWOrEverKnownRO(start, snap);
|
||||
if (knownSnap == null) {
|
||||
@ -290,39 +307,39 @@ public class DisassembleAtPcDebuggerBot implements DebuggerBot {
|
||||
|
||||
// TODO: Should I just keep a variable-snap view around?
|
||||
TraceProgramView view = trace.getFixedProgramView(ks);
|
||||
DisassembleCommand dis =
|
||||
new DisassembleCommand(start, disassemblable, true) {
|
||||
@Override
|
||||
public boolean applyTo(DomainObject obj, TaskMonitor monitor) {
|
||||
synchronized (injects) {
|
||||
try {
|
||||
if (codeManager.definedUnits().containsAddress(ks, start)) {
|
||||
return true;
|
||||
}
|
||||
for (DisassemblyInject i : injects) {
|
||||
i.pre(plugin.getTool(), this, view, thread,
|
||||
new AddressSet(start, start),
|
||||
disassemblable);
|
||||
}
|
||||
boolean result = super.applyTo(obj, monitor);
|
||||
if (!result) {
|
||||
Msg.error(this, "Auto-disassembly error: " + getStatusMsg());
|
||||
return true; // No pop-up errors
|
||||
}
|
||||
for (DisassemblyInject i : injects) {
|
||||
i.post(plugin.getTool(), view, getDisassembledAddressSet());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
catch (Throwable e) {
|
||||
Msg.error(this, "Auto-disassembly error: " + e);
|
||||
return true; // No pop-up errors
|
||||
}
|
||||
|
||||
BackgroundCommand cmd = new BackgroundCommand("Auto-disassemble", true, true, false) {
|
||||
@Override
|
||||
public boolean applyTo(DomainObject obj, TaskMonitor monitor) {
|
||||
try {
|
||||
DebuggerPlatformService platformService =
|
||||
findService(DebuggerPlatformService.class);
|
||||
if (platformService == null) {
|
||||
reportError("Cannot disassemble without the platform service");
|
||||
return true;
|
||||
}
|
||||
TraceObject object = getObject(thread);
|
||||
DebuggerPlatformMapper mapper =
|
||||
platformService.getMapper(trace, object, snap);
|
||||
if (mapper == null) {
|
||||
reportError("Cannot disassemble without a platform mapper");
|
||||
return true;
|
||||
}
|
||||
DisassemblyResult result = mapper.disassemble(thread, object, start,
|
||||
disassemblable, snap, monitor);
|
||||
if (result.isAtLeastOne() || result.isSuccess()) {
|
||||
return true;
|
||||
}
|
||||
reportError("Auto-disassembly error: " + result.getErrorMessage());
|
||||
}
|
||||
};
|
||||
catch (Exception e) {
|
||||
reportError("Auto-disassembly error: " + e, e);
|
||||
}
|
||||
return true; // No pop-up errors
|
||||
}
|
||||
};
|
||||
// TODO: Queue commands so no two for the same trace run concurrently
|
||||
plugin.getTool().executeBackgroundCommand(dis, view);
|
||||
plugin.getTool().executeBackgroundCommand(cmd, view);
|
||||
}
|
||||
}
|
||||
|
||||
@ -330,6 +347,47 @@ public class DisassembleAtPcDebuggerBot implements DebuggerBot {
|
||||
private final MultiToolTraceListenerManager<ForDisassemblyTraceListener> listeners =
|
||||
new MultiToolTraceListenerManager<>(ForDisassemblyTraceListener::new);
|
||||
|
||||
protected void reportError(String error) {
|
||||
reportError(error, null);
|
||||
}
|
||||
|
||||
protected void reportError(String error, Throwable t) {
|
||||
for (PluginTool tool : plugin.getProxyingPluginTools()) {
|
||||
Msg.error(this, error, t);
|
||||
tool.setStatusInfo(error, true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the given service among the open tools
|
||||
*
|
||||
* <p>
|
||||
* NOTE: This will prefer the service from the most-recently active tool first, only considering
|
||||
* those with the workflow service proxy enabled. This is important when considering the state
|
||||
* of said service.
|
||||
*
|
||||
* @param <T> the type of the service
|
||||
* @param cls the class of the service
|
||||
* @return the service, or null
|
||||
*/
|
||||
protected <T> T findService(Class<T> cls) {
|
||||
Collection<PluginTool> proxied = plugin.getProxyingPluginTools();
|
||||
List<DockingWindowManager> all = DockingWindowManager.getAllDockingWindowManagers();
|
||||
Collections.reverse(all);
|
||||
for (DockingWindowManager dwm : all) {
|
||||
Tool tool = dwm.getTool();
|
||||
if (!proxied.contains(tool)) {
|
||||
continue;
|
||||
}
|
||||
T t = tool.getService(cls);
|
||||
if (t == null) {
|
||||
continue;
|
||||
}
|
||||
return t;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return plugin != null;
|
||||
|
@ -0,0 +1,59 @@
|
||||
/* ###
|
||||
* 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);
|
||||
}
|
||||
}
|
@ -0,0 +1,94 @@
|
||||
/* ###
|
||||
* 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 ghidra.framework.cmd.TypedBackgroundCommand;
|
||||
import ghidra.program.disassemble.Disassembler;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.lang.*;
|
||||
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.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);
|
||||
}
|
||||
|
||||
protected final Address start;
|
||||
protected final AddressSetView restrictedSet;
|
||||
|
||||
protected RegisterValue initialContext;
|
||||
private AddressSetView disassembled;
|
||||
|
||||
public DisassembleTraceCommand(Address start, AddressSetView restrictedSet) {
|
||||
super("Disassemble", true, true, false);
|
||||
this.start = start;
|
||||
this.restrictedSet = restrictedSet;
|
||||
}
|
||||
|
||||
public void setInitialContext(RegisterValue initialContext) {
|
||||
this.initialContext = initialContext.getBaseRegisterValue();
|
||||
}
|
||||
|
||||
protected Disassembler getDisassembler(TraceProgramView view, TaskMonitor monitor) {
|
||||
return Disassembler.getDisassembler(view, monitor, monitor::setMessage);
|
||||
}
|
||||
|
||||
protected MemBuffer getBuffer(TraceProgramView view) {
|
||||
return new MemoryBufferImpl(view.getMemory(), start);
|
||||
}
|
||||
|
||||
protected int computeLimit() {
|
||||
AddressRange range = restrictedSet.getRangeContaining(start);
|
||||
if (range == null) {
|
||||
return 1;
|
||||
}
|
||||
return MathUtilities.unsignedMin(range.getMaxAddress().subtract(start) + 1,
|
||||
Integer.MAX_VALUE);
|
||||
}
|
||||
|
||||
protected AddressSetView writeBlock(TraceProgramView view, InstructionBlock block) {
|
||||
InstructionSet set = new InstructionSet(view.getAddressFactory());
|
||||
set.addBlock(block);
|
||||
try {
|
||||
return view.getListing().addInstructions(set, true);
|
||||
}
|
||||
catch (CodeUnitInsertionException e) {
|
||||
return new AddressSet();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean applyToTyped(TraceProgramView view, TaskMonitor monitor) {
|
||||
Disassembler disassembler = getDisassembler(view, monitor);
|
||||
MemBuffer buffer = getBuffer(view);
|
||||
int limit = computeLimit();
|
||||
InstructionBlock block = disassembler.pseudoDisassembleBlock(buffer, initialContext, limit);
|
||||
disassembled = writeBlock(view, block);
|
||||
return true;
|
||||
}
|
||||
|
||||
public AddressSetView getDisassembledAddressSet() {
|
||||
return disassembled;
|
||||
}
|
||||
}
|
@ -17,11 +17,10 @@ package ghidra.app.plugin.core.debug.workflow;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import ghidra.app.cmd.disassemble.DisassembleCommand;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.model.address.AddressSetView;
|
||||
import ghidra.program.model.lang.Language;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.program.TraceProgramView;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.classfinder.ExtensionPoint;
|
||||
@ -87,13 +86,16 @@ public interface DisassemblyInject extends ExtensionPoint {
|
||||
*
|
||||
* @param tool the tool that will execute the command
|
||||
* @param command the command to be configured, which is about to execute
|
||||
* @param view the view (trace, snap) which is about to be disassembled
|
||||
* @param trace the trace whose bytes to disassemble
|
||||
* @param language the language for the disassembler
|
||||
* @param snap the snap the snap at which to disassemble
|
||||
* @param thread the thread whose PC is being disassembled
|
||||
* @param startSet the starting address set, usually just the PC
|
||||
* @param restricted the set of disassemblable addresses
|
||||
*/
|
||||
default void pre(PluginTool tool, DisassembleCommand command, TraceProgramView view,
|
||||
TraceThread thread, AddressSetView startSet, AddressSetView restricted) {
|
||||
default void pre(PluginTool tool, DisassembleTraceCommand command, Trace trace,
|
||||
Language language, long snap, TraceThread thread, AddressSetView startSet,
|
||||
AddressSetView restricted) {
|
||||
}
|
||||
|
||||
/**
|
||||
@ -104,9 +106,10 @@ public interface DisassemblyInject extends ExtensionPoint {
|
||||
* The callback occurs within the command's background thread.
|
||||
*
|
||||
* @param tool the tool that just executed the disassembly command
|
||||
* @param view the view (trace, snap) which was just disassembled
|
||||
* @param trace the trace whose bytes were disassembled
|
||||
* @param snap the snap the snap at which disassembly was performed
|
||||
* @param disassembled the addresses that were actually disassembled
|
||||
*/
|
||||
default void post(PluginTool tool, TraceProgramView view, AddressSetView disassembled) {
|
||||
default void post(PluginTool tool, Trace trace, long snap, AddressSetView disassembled) {
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,74 @@
|
||||
/* ###
|
||||
* 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.services;
|
||||
|
||||
import ghidra.app.plugin.core.debug.mapping.DebuggerPlatformMapper;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.target.TraceObject;
|
||||
|
||||
/**
|
||||
* A service to manage the current mapper for active traces
|
||||
*/
|
||||
public interface DebuggerPlatformService {
|
||||
/**
|
||||
* Get a mapper applicable to the given object
|
||||
*
|
||||
* <p>
|
||||
* If the trace's current mapper is applicable to the object, it will be returned. Otherwise,
|
||||
* the service will query the opinions for a new mapper, as in
|
||||
* {@link #getNewMapper(TraceObject)} and set it as the current mapper before returning. If a
|
||||
* new mapper is set, the trace is also initialized for that mapper.
|
||||
*
|
||||
* @param trace the trace
|
||||
* @param object the object for which a mapper is desired
|
||||
* @param snap the snap, usually the current snap
|
||||
* @return the mapper, or null if no offer was provided
|
||||
*/
|
||||
DebuggerPlatformMapper getMapper(Trace trace, TraceObject object,
|
||||
long snap);
|
||||
|
||||
/**
|
||||
* Get a new mapper for the given object, ignoring the trace's current mapper
|
||||
*
|
||||
* <p>
|
||||
* This will not replace the trace's current mapper, nor will it initialize the trace for the
|
||||
* mapper.
|
||||
*
|
||||
* @param trace the trace
|
||||
* @param object the object for which a mapper is desired
|
||||
* @param snap the snap, usually the current snap
|
||||
* @return the mapper, or null if no offer was provided
|
||||
*/
|
||||
DebuggerPlatformMapper getNewMapper(Trace trace, TraceObject object, long snap);
|
||||
|
||||
/**
|
||||
* Display a dialog for the user to manually select a mapper for the given object
|
||||
*
|
||||
* @param object the object for which a mapper is desired
|
||||
* @param snap the snap, usually the current snap
|
||||
* @return the mapper, or null if the dialog was cancelled
|
||||
*/
|
||||
DebuggerPlatformMapper chooseMapper(Trace trace, TraceObject object, long snap);
|
||||
|
||||
/**
|
||||
* Set the current mapper for the trace and initialize the trace for the mapper
|
||||
*
|
||||
* @param trace the trace whose current mapper to set
|
||||
* @param mapper the mapper
|
||||
* @param snap the snap for initializing the trace
|
||||
*/
|
||||
void setCurrentMapperFor(Trace trace, DebuggerPlatformMapper mapper, long snap);
|
||||
}
|
@ -45,7 +45,7 @@ import ghidra.program.model.data.Undefined4DataType;
|
||||
import ghidra.program.model.lang.Language;
|
||||
import ghidra.program.model.util.CodeUnitInsertionException;
|
||||
import ghidra.trace.database.ToyDBTraceBuilder;
|
||||
import ghidra.trace.database.language.DBTraceGuestLanguage;
|
||||
import ghidra.trace.database.guest.DBTraceGuestPlatform;
|
||||
import ghidra.trace.model.memory.TraceMemoryFlag;
|
||||
import ghidra.trace.model.memory.TraceOverlappedRegionException;
|
||||
import ghidra.util.database.UndoableTransaction;
|
||||
@ -129,12 +129,13 @@ 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), tb.language, tb.buf(0xf4, 0));
|
||||
tb.addInstruction(0, tb.addr(0x4008), null, tb.buf(0xf4, 0));
|
||||
|
||||
Language x86 = getSLEIGH_X86_LANGUAGE();
|
||||
DBTraceGuestLanguage guest = tb.trace.getLanguageManager().addGuestLanguage(x86);
|
||||
DBTraceGuestPlatform guest =
|
||||
tb.trace.getPlatformManager().addGuestPlatform(x86.getDefaultCompilerSpec());
|
||||
guest.addMappedRange(tb.addr(0x4000), tb.addr(guest, 0x00400000), 0x1000);
|
||||
tb.addInstruction(0, tb.addr(0x400a), x86, tb.buf(0x90));
|
||||
tb.addInstruction(0, tb.addr(0x400a), guest, tb.buf(0x90));
|
||||
}
|
||||
waitForSwing();
|
||||
|
||||
|
@ -0,0 +1,62 @@
|
||||
/* ###
|
||||
* 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.mapping;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.model.lang.*;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.target.TraceObject;
|
||||
|
||||
public class TestDebuggerPlatformOpinion extends AbstractDebuggerPlatformOpinion {
|
||||
|
||||
enum Offers implements DebuggerPlatformOffer {
|
||||
X86_64 {
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Test x86-64";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getConfidence() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompilerSpec getCompilerSpec() {
|
||||
return getCompilerSpec(new LanguageID("x86:LE:64:default"), null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DebuggerPlatformMapper take(PluginTool tool, Trace trace) {
|
||||
return new DefaultDebuggerPlatformMapper(tool, trace, getCompilerSpec());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Set<DebuggerPlatformOffer> getOffers(TraceObject object, long snap,
|
||||
TraceObject env, String debugger, String arch, String os, Endian endian) {
|
||||
if (!"test".equals(debugger)) {
|
||||
return Set.of();
|
||||
}
|
||||
if (!"x86-64".equals(arch)) {
|
||||
return Set.of();
|
||||
}
|
||||
return Set.of(Offers.X86_64);
|
||||
}
|
||||
}
|
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.plugin.core.debug.platform.arm;
|
||||
package ghidra.app.plugin.core.debug.platform.gdb;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
@ -25,8 +25,8 @@ import org.junit.Test;
|
||||
|
||||
import ghidra.app.plugin.core.debug.mapping.DebuggerMappingOffer;
|
||||
import ghidra.app.plugin.core.debug.mapping.DebuggerMappingOpinion;
|
||||
import ghidra.app.plugin.core.debug.platform.arm.GdbArmDebuggerMappingOpinion.GdbAArch64Offer;
|
||||
import ghidra.app.plugin.core.debug.platform.arm.GdbArmDebuggerMappingOpinion.GdbArmOffer;
|
||||
import ghidra.app.plugin.core.debug.platform.gdb.GdbArmDebuggerMappingOpinion.GdbAArch64Offer;
|
||||
import ghidra.app.plugin.core.debug.platform.gdb.GdbArmDebuggerMappingOpinion.GdbArmOffer;
|
||||
import ghidra.dbg.model.TestDebuggerObjectModel;
|
||||
import ghidra.dbg.model.TestTargetProcess;
|
||||
import ghidra.program.model.lang.LanguageID;
|
@ -0,0 +1,168 @@
|
||||
/* ###
|
||||
* 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)));
|
||||
});
|
||||
}
|
||||
}
|
@ -38,7 +38,7 @@ import ghidra.trace.database.breakpoint.DBTraceBreakpointManager;
|
||||
import ghidra.trace.database.context.DBTraceRegisterContextManager;
|
||||
import ghidra.trace.database.data.DBTraceDataSettingsAdapter;
|
||||
import ghidra.trace.database.data.DBTraceDataTypeManager;
|
||||
import ghidra.trace.database.language.DBTraceLanguageManager;
|
||||
import ghidra.trace.database.guest.DBTracePlatformManager;
|
||||
import ghidra.trace.database.listing.DBTraceCodeManager;
|
||||
import ghidra.trace.database.listing.DBTraceCommentAdapter;
|
||||
import ghidra.trace.database.memory.DBTraceMemoryManager;
|
||||
@ -101,7 +101,7 @@ public class DBTrace extends DBCachedDomainObjectAdapter implements Trace, Trace
|
||||
@DependentService
|
||||
protected DBTraceEquateManager equateManager;
|
||||
@DependentService
|
||||
protected DBTraceLanguageManager languageManager;
|
||||
protected DBTracePlatformManager platformManager;
|
||||
@DependentService
|
||||
protected DBTraceMemoryManager memoryManager;
|
||||
@DependentService
|
||||
@ -283,12 +283,12 @@ public class DBTrace extends DBCachedDomainObjectAdapter implements Trace, Trace
|
||||
|
||||
@DependentService
|
||||
protected DBTraceCodeManager createCodeManager(DBTraceThreadManager threadManager,
|
||||
DBTraceLanguageManager languageManager, DBTraceDataTypeManager dataTypeManager,
|
||||
DBTracePlatformManager platformManager, DBTraceDataTypeManager dataTypeManager,
|
||||
DBTraceOverlaySpaceAdapter overlayAdapter, DBTraceReferenceManager referenceManager)
|
||||
throws CancelledException, IOException {
|
||||
return createTraceManager("Code Manager",
|
||||
(openMode, monitor) -> new DBTraceCodeManager(dbh, openMode, rwLock, monitor,
|
||||
baseLanguage, this, threadManager, languageManager, dataTypeManager, overlayAdapter,
|
||||
baseLanguage, this, threadManager, platformManager, dataTypeManager, overlayAdapter,
|
||||
referenceManager));
|
||||
}
|
||||
|
||||
@ -325,11 +325,11 @@ public class DBTrace extends DBCachedDomainObjectAdapter implements Trace, Trace
|
||||
}
|
||||
|
||||
@DependentService
|
||||
protected DBTraceLanguageManager createLanguageManager()
|
||||
protected DBTracePlatformManager createPlatformManager()
|
||||
throws CancelledException, IOException {
|
||||
return createTraceManager("Language Manager",
|
||||
(openMode, monitor) -> new DBTraceLanguageManager(dbh, openMode, rwLock, monitor,
|
||||
baseLanguage, this));
|
||||
(openMode, monitor) -> new DBTracePlatformManager(dbh, openMode, rwLock, monitor,
|
||||
baseCompilerSpec, this));
|
||||
}
|
||||
|
||||
@DependentService
|
||||
@ -373,11 +373,11 @@ public class DBTrace extends DBCachedDomainObjectAdapter implements Trace, Trace
|
||||
|
||||
@DependentService
|
||||
protected DBTraceRegisterContextManager createRegisterContextManager(
|
||||
DBTraceThreadManager threadManager, DBTraceLanguageManager languageManager)
|
||||
DBTraceThreadManager threadManager, DBTracePlatformManager platformManager)
|
||||
throws CancelledException, IOException {
|
||||
return createTraceManager("Context Manager",
|
||||
(openMode, monitor) -> new DBTraceRegisterContextManager(dbh, openMode, rwLock, monitor,
|
||||
baseLanguage, this, threadManager, languageManager));
|
||||
baseLanguage, this, threadManager, platformManager));
|
||||
}
|
||||
|
||||
@DependentService
|
||||
@ -497,8 +497,8 @@ public class DBTrace extends DBCachedDomainObjectAdapter implements Trace, Trace
|
||||
}
|
||||
|
||||
@Override
|
||||
public DBTraceLanguageManager getLanguageManager() {
|
||||
return languageManager;
|
||||
public DBTracePlatformManager getPlatformManager() {
|
||||
return platformManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -29,6 +29,7 @@ import com.google.common.collect.*;
|
||||
|
||||
import db.*;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.lang.CompilerSpecID;
|
||||
import ghidra.program.model.lang.LanguageID;
|
||||
import ghidra.program.model.symbol.RefType;
|
||||
import ghidra.program.model.symbol.RefTypeFactory;
|
||||
@ -161,6 +162,43 @@ public enum DBTraceUtils {
|
||||
}
|
||||
}
|
||||
|
||||
public static class CompilerSpecIDDBFieldCodec<OT extends DBAnnotatedObject>
|
||||
extends AbstractDBFieldCodec<CompilerSpecID, OT, StringField> {
|
||||
|
||||
public CompilerSpecIDDBFieldCodec(Class<OT> objectType, Field field, int column) {
|
||||
super(CompilerSpecID.class, objectType, StringField.class, field, column);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void store(CompilerSpecID value, StringField f) {
|
||||
f.setString(value == null ? null : value.getIdAsString());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doStore(OT obj, DBRecord record)
|
||||
throws IllegalArgumentException, IllegalAccessException {
|
||||
CompilerSpecID id = getValue(obj);
|
||||
if (id == null) {
|
||||
record.setString(column, null);
|
||||
}
|
||||
else {
|
||||
record.setString(column, id.getIdAsString());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doLoad(OT obj, DBRecord record)
|
||||
throws IllegalArgumentException, IllegalAccessException {
|
||||
String id = record.getString(column);
|
||||
if (id == null) {
|
||||
setValue(obj, null);
|
||||
}
|
||||
else {
|
||||
setValue(obj, new CompilerSpecID(id));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public abstract static class AbstractOffsetSnapDBFieldCodec<OT extends DBAnnotatedObject>
|
||||
extends AbstractDBFieldCodec<OffsetSnap, OT, BinaryField> {
|
||||
|
||||
|
@ -31,7 +31,7 @@ import ghidra.program.model.lang.*;
|
||||
import ghidra.program.model.listing.ProgramContext;
|
||||
import ghidra.program.util.ProgramContextImpl;
|
||||
import ghidra.trace.database.DBTrace;
|
||||
import ghidra.trace.database.language.DBTraceLanguageManager;
|
||||
import ghidra.trace.database.guest.DBTracePlatformManager;
|
||||
import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree;
|
||||
import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree.AbstractDBTraceAddressSnapRangePropertyMapData;
|
||||
import ghidra.trace.database.space.AbstractDBTraceSpaceBasedManager;
|
||||
@ -83,13 +83,13 @@ public class DBTraceRegisterContextManager extends
|
||||
}
|
||||
}
|
||||
|
||||
protected final DBTraceLanguageManager languageManager;
|
||||
protected final DBTracePlatformManager languageManager;
|
||||
|
||||
protected final Map<Language, ProgramContext> defaultContexts = new HashMap<>();
|
||||
|
||||
public DBTraceRegisterContextManager(DBHandle dbh, DBOpenMode openMode, ReadWriteLock lock,
|
||||
TaskMonitor monitor, Language baseLanguage, DBTrace trace,
|
||||
DBTraceThreadManager threadManager, DBTraceLanguageManager languageManager)
|
||||
DBTraceThreadManager threadManager, DBTracePlatformManager languageManager)
|
||||
throws VersionException, IOException {
|
||||
super(NAME, dbh, openMode, lock, monitor, baseLanguage, trace, threadManager);
|
||||
this.languageManager = languageManager;
|
||||
|
@ -32,6 +32,7 @@ import ghidra.program.model.lang.*;
|
||||
import ghidra.trace.database.DBTrace;
|
||||
import ghidra.trace.database.DBTraceUtils;
|
||||
import ghidra.trace.database.context.DBTraceRegisterContextManager.DBTraceRegisterContextEntry;
|
||||
import ghidra.trace.database.guest.DBTraceGuestPlatform.DBTraceGuestLanguage;
|
||||
import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapAddressSetView;
|
||||
import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapSpace;
|
||||
import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree.TraceAddressSnapRangeQuery;
|
||||
@ -70,8 +71,8 @@ public class DBTraceRegisterContextSpace implements TraceRegisterContextSpace, D
|
||||
super(store, record);
|
||||
}
|
||||
|
||||
void set(int langKey, Register register) {
|
||||
this.langKey = langKey;
|
||||
void set(DBTraceGuestLanguage guest, Register register) {
|
||||
this.langKey = (int) (guest == null ? -1 : guest.getKey());
|
||||
this.register = register.getName();
|
||||
update(LANGUAGE_COLUMN, REGISTER_COLUMN);
|
||||
}
|
||||
@ -110,7 +111,8 @@ public class DBTraceRegisterContextSpace implements TraceRegisterContextSpace, D
|
||||
|
||||
protected void loadRegisterValueMaps() throws VersionException {
|
||||
for (DBTraceRegisterEntry ent : registerStore.asMap().values()) {
|
||||
Language language = manager.languageManager.getLanguageByKey(ent.langKey);
|
||||
DBTraceGuestLanguage guest = manager.languageManager.getLanguageByKey(ent.langKey);
|
||||
Language language = guest == null ? manager.getBaseLanguage() : guest.getLanguage();
|
||||
Register register = language.getRegister(ent.register);
|
||||
ImmutablePair<Language, Register> pair = new ImmutablePair<>(language, register);
|
||||
if (ent.map == null) {
|
||||
@ -161,12 +163,12 @@ public class DBTraceRegisterContextSpace implements TraceRegisterContextSpace, D
|
||||
protected DBTraceAddressSnapRangePropertyMapSpace<byte[], DBTraceRegisterContextEntry> getRegisterValueMap(
|
||||
Language language, Register register, boolean createIfAbsent) {
|
||||
ImmutablePair<Language, Register> pair = new ImmutablePair<>(language, register);
|
||||
int langKey = manager.languageManager.getKeyForLanguage(language);
|
||||
DBTraceGuestLanguage guest = manager.languageManager.getLanguageByLanguage(language);
|
||||
if (createIfAbsent) {
|
||||
return registerValueMaps.computeIfAbsent(pair, t -> {
|
||||
try {
|
||||
DBTraceRegisterEntry ent = registerStore.create();
|
||||
ent.set(langKey, register);
|
||||
ent.set(guest, register);
|
||||
return createRegisterValueMap(t);
|
||||
}
|
||||
catch (VersionException e) {
|
||||
|
@ -13,8 +13,9 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.trace.database.language;
|
||||
package ghidra.trace.database.guest;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
@ -22,13 +23,17 @@ import com.google.common.collect.Range;
|
||||
|
||||
import db.DBRecord;
|
||||
import ghidra.app.util.PseudoInstruction;
|
||||
import ghidra.lifecycle.Internal;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.lang.*;
|
||||
import ghidra.program.model.listing.Instruction;
|
||||
import ghidra.program.model.mem.DumbMemBufferImpl;
|
||||
import ghidra.program.model.mem.MemBuffer;
|
||||
import ghidra.program.util.DefaultLanguageService;
|
||||
import ghidra.trace.database.DBTraceUtils.CompilerSpecIDDBFieldCodec;
|
||||
import ghidra.trace.database.DBTraceUtils.LanguageIDDBFieldCodec;
|
||||
import ghidra.trace.model.language.TraceGuestLanguage;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.guest.TraceGuestPlatform;
|
||||
import ghidra.util.LockHold;
|
||||
import ghidra.util.database.*;
|
||||
import ghidra.util.database.annot.*;
|
||||
@ -37,59 +42,129 @@ import ghidra.util.exception.VersionException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
@DBAnnotatedObjectInfo(version = 0)
|
||||
public class DBTraceGuestLanguage extends DBAnnotatedObject implements TraceGuestLanguage {
|
||||
public static final String TABLE_NAME = "Languages";
|
||||
public class DBTraceGuestPlatform extends DBAnnotatedObject implements TraceGuestPlatform {
|
||||
public static final String TABLE_NAME = "Platforms";
|
||||
|
||||
static final String LANGID_COLUMN_NAME = "ID";
|
||||
static final String VERSION_COLUMN_NAME = "Version";
|
||||
static final String MINOR_VERSION_COLUMN_NAME = "MinorVersion";
|
||||
@DBAnnotatedObjectInfo(version = 0)
|
||||
public static class DBTraceGuestLanguage extends DBAnnotatedObject {
|
||||
public static final String TABLE_NAME = "Languages";
|
||||
|
||||
@DBAnnotatedColumn(LANGID_COLUMN_NAME)
|
||||
static DBObjectColumn LANGID_COLUMN;
|
||||
@DBAnnotatedColumn(VERSION_COLUMN_NAME)
|
||||
static DBObjectColumn VERSION_COLUMN;
|
||||
@DBAnnotatedColumn(MINOR_VERSION_COLUMN_NAME)
|
||||
static DBObjectColumn MINOR_VERSION_COLUMN;
|
||||
static final String LANGID_COLUMN_NAME = "Lang";
|
||||
static final String VERSION_COLUMN_NAME = "Version";
|
||||
static final String MINOR_VERSION_COLUMN_NAME = "MinorVersion";
|
||||
|
||||
@DBAnnotatedField(column = LANGID_COLUMN_NAME, codec = LanguageIDDBFieldCodec.class)
|
||||
private LanguageID langID;
|
||||
@DBAnnotatedField(column = VERSION_COLUMN_NAME)
|
||||
private int version;
|
||||
@DBAnnotatedField(column = MINOR_VERSION_COLUMN_NAME)
|
||||
private int minorVersion;
|
||||
@DBAnnotatedColumn(LANGID_COLUMN_NAME)
|
||||
static DBObjectColumn LANGID_COLUMN;
|
||||
@DBAnnotatedColumn(VERSION_COLUMN_NAME)
|
||||
static DBObjectColumn VERSION_COLUMN;
|
||||
@DBAnnotatedColumn(MINOR_VERSION_COLUMN_NAME)
|
||||
static DBObjectColumn MINOR_VERSION_COLUMN;
|
||||
|
||||
private final DBTraceLanguageManager manager;
|
||||
@DBAnnotatedField(column = LANGID_COLUMN_NAME, codec = LanguageIDDBFieldCodec.class)
|
||||
private LanguageID langID;
|
||||
@DBAnnotatedField(column = VERSION_COLUMN_NAME)
|
||||
private int version;
|
||||
@DBAnnotatedField(column = MINOR_VERSION_COLUMN_NAME)
|
||||
private int minorVersion;
|
||||
|
||||
private Language guestLanguage;
|
||||
private Language language;
|
||||
|
||||
protected final NavigableMap<Address, DBTraceGuestLanguageMappedRange> rangesByHostAddress =
|
||||
public DBTraceGuestLanguage(DBCachedObjectStore<?> store, DBRecord record) {
|
||||
super(store, record);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void fresh(boolean created) throws IOException {
|
||||
super.fresh(created);
|
||||
if (created) {
|
||||
return;
|
||||
}
|
||||
LanguageService langServ = DefaultLanguageService.getLanguageService();
|
||||
language = langServ.getLanguage(langID);
|
||||
if (version != language.getVersion() || minorVersion != language.getMinorVersion()) {
|
||||
throw new IOException(new VersionException()); // TODO Upgrade
|
||||
}
|
||||
}
|
||||
|
||||
void set(Language language) {
|
||||
this.langID = language.getLanguageID();
|
||||
this.version = language.getVersion();
|
||||
this.minorVersion = language.getMinorVersion();
|
||||
update(LANGID_COLUMN, VERSION_COLUMN, MINOR_VERSION_COLUMN);
|
||||
this.language = language;
|
||||
}
|
||||
|
||||
public Language getLanguage() {
|
||||
return language;
|
||||
}
|
||||
}
|
||||
|
||||
static final String LANGKEY_COLUMN_NAME = "Lang";
|
||||
static final String CSPECID_COLUMN_NAME = "CSpec";
|
||||
|
||||
@DBAnnotatedColumn(LANGKEY_COLUMN_NAME)
|
||||
static DBObjectColumn LANGKEY_COLUMN;
|
||||
@DBAnnotatedColumn(CSPECID_COLUMN_NAME)
|
||||
static DBObjectColumn CSPECID_COLUMN;
|
||||
|
||||
@DBAnnotatedField(column = LANGKEY_COLUMN_NAME)
|
||||
private int langKey;
|
||||
@DBAnnotatedField(column = CSPECID_COLUMN_NAME, codec = CompilerSpecIDDBFieldCodec.class)
|
||||
private CompilerSpecID cSpecID;
|
||||
|
||||
private DBTraceGuestLanguage languageEntry;
|
||||
private CompilerSpec compilerSpec;
|
||||
|
||||
final DBTracePlatformManager manager;
|
||||
protected final NavigableMap<Address, DBTraceGuestPlatformMappedRange> rangesByHostAddress =
|
||||
new TreeMap<>();
|
||||
protected final AddressSet hostAddressSet = new AddressSet();
|
||||
|
||||
protected final NavigableMap<Address, DBTraceGuestLanguageMappedRange> rangesByGuestAddress =
|
||||
protected final NavigableMap<Address, DBTraceGuestPlatformMappedRange> rangesByGuestAddress =
|
||||
new TreeMap<>();
|
||||
protected final AddressSet guestAddressSet = new AddressSet();
|
||||
|
||||
public DBTraceGuestLanguage(DBTraceLanguageManager manager, DBCachedObjectStore<?> store,
|
||||
public DBTraceGuestPlatform(DBTracePlatformManager manager, DBCachedObjectStore<?> store,
|
||||
DBRecord record) {
|
||||
super(store, record);
|
||||
this.manager = manager;
|
||||
}
|
||||
|
||||
void setLanguage(Language language) {
|
||||
this.guestLanguage = language;
|
||||
this.langID = language.getLanguageID();
|
||||
this.version = language.getVersion();
|
||||
this.minorVersion = language.getMinorVersion();
|
||||
update(LANGID_COLUMN, VERSION_COLUMN, MINOR_VERSION_COLUMN);
|
||||
void set(CompilerSpec compilerSpec) {
|
||||
this.languageEntry = manager.getOrCreateLanguage(compilerSpec.getLanguage());
|
||||
this.langKey = (int) (languageEntry == null ? -1 : languageEntry.getKey());
|
||||
this.cSpecID = compilerSpec.getCompilerSpecID();
|
||||
update(LANGKEY_COLUMN, CSPECID_COLUMN);
|
||||
this.compilerSpec = compilerSpec;
|
||||
}
|
||||
|
||||
protected void deleteMappedRange(DBTraceGuestLanguageMappedRange range, TaskMonitor monitor)
|
||||
@Override
|
||||
protected void fresh(boolean created) throws IOException {
|
||||
super.fresh(created);
|
||||
if (created) {
|
||||
return;
|
||||
}
|
||||
this.languageEntry = manager.getLanguageByKey(langKey);
|
||||
if (languageEntry == null && langKey != -1) {
|
||||
throw new IOException("Platform table is corrupt. Missing language " + langKey);
|
||||
}
|
||||
compilerSpec = getLanguage().getCompilerSpecByID(cSpecID);
|
||||
if (compilerSpec == null) {
|
||||
throw new IOException(
|
||||
"Platform table is corrupt. Invalid compiler spec " + compilerSpec);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Trace getTrace() {
|
||||
return manager.trace;
|
||||
}
|
||||
|
||||
protected void deleteMappedRange(DBTraceGuestPlatformMappedRange range, TaskMonitor monitor)
|
||||
throws CancelledException {
|
||||
try (LockHold hold = LockHold.lock(manager.lock.writeLock())) {
|
||||
manager.trace.getCodeManager()
|
||||
.clearLanguage(Range.all(), range.getHostRange(),
|
||||
(int) getKey(), monitor);
|
||||
.clearPlatform(Range.all(), range.getHostRange(), this, monitor);
|
||||
manager.rangeMappingStore.delete(range);
|
||||
AddressRange hostRange = range.getHostRange();
|
||||
AddressRange guestRange = range.getGuestRange();
|
||||
@ -100,41 +175,43 @@ public class DBTraceGuestLanguage extends DBAnnotatedObject implements TraceGues
|
||||
}
|
||||
}
|
||||
|
||||
protected void doGetLanguage(LanguageService langServ)
|
||||
throws LanguageNotFoundException, VersionException {
|
||||
this.guestLanguage = langServ.getLanguage(langID);
|
||||
if (version != guestLanguage.getVersion() ||
|
||||
minorVersion != guestLanguage.getMinorVersion()) {
|
||||
throw new VersionException(); // TODO Upgrade
|
||||
}
|
||||
@Internal
|
||||
public DBTraceGuestLanguage getLanguageEntry() {
|
||||
return languageEntry;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Language getLanguage() {
|
||||
return guestLanguage;
|
||||
return languageEntry == null ? manager.baseLanguage : languageEntry.getLanguage();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompilerSpec getCompilerSpec() {
|
||||
return compilerSpec;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(TaskMonitor monitor) throws CancelledException {
|
||||
manager.deleteGuestLanguage(this, monitor);
|
||||
manager.deleteGuestPlatform(this, monitor);
|
||||
// TODO: Delete language once no platform uses it?
|
||||
}
|
||||
|
||||
@Override
|
||||
public DBTraceGuestLanguageMappedRange addMappedRange(Address hostStart, Address guestStart,
|
||||
public DBTraceGuestPlatformMappedRange addMappedRange(Address hostStart, Address guestStart,
|
||||
long length) throws AddressOverflowException {
|
||||
try (LockHold hold = LockHold.lock(manager.lock.writeLock())) {
|
||||
Address hostEnd = hostStart.addNoWrap(length - 1);
|
||||
Address hostEnd = hostStart.addWrap(length - 1);
|
||||
if (hostAddressSet.intersects(hostStart, hostEnd)) {
|
||||
// TODO: Check for compatibility and extend?
|
||||
throw new IllegalArgumentException(
|
||||
"Range overlaps existing host mapped range(s) for this guest language");
|
||||
}
|
||||
Address guestEnd = guestStart.addNoWrap(length - 1);
|
||||
Address guestEnd = guestStart.addWrap(length - 1);
|
||||
if (guestAddressSet.intersects(guestStart, guestEnd)) {
|
||||
throw new IllegalArgumentException("Range overlaps existing guest mapped range(s)");
|
||||
}
|
||||
DBTraceGuestLanguageMappedRange mappedRange = manager.rangeMappingStore.create();
|
||||
mappedRange.set(hostStart, guestLanguage, guestStart, length);
|
||||
DBTraceGuestPlatformMappedRange mappedRange = manager.rangeMappingStore.create();
|
||||
mappedRange.set(hostStart, this, guestStart, length);
|
||||
rangesByHostAddress.put(hostStart, mappedRange);
|
||||
rangesByGuestAddress.put(guestStart, mappedRange);
|
||||
hostAddressSet.add(mappedRange.getHostRange());
|
||||
@ -156,7 +233,7 @@ public class DBTraceGuestLanguage extends DBAnnotatedObject implements TraceGues
|
||||
@Override
|
||||
public Address mapHostToGuest(Address hostAddress) {
|
||||
try (LockHold hold = LockHold.lock(manager.lock.readLock())) {
|
||||
Entry<Address, DBTraceGuestLanguageMappedRange> floorEntry =
|
||||
Entry<Address, DBTraceGuestPlatformMappedRange> floorEntry =
|
||||
rangesByHostAddress.floorEntry(hostAddress);
|
||||
if (floorEntry == null) {
|
||||
return null;
|
||||
@ -168,7 +245,7 @@ public class DBTraceGuestLanguage extends DBAnnotatedObject implements TraceGues
|
||||
@Override
|
||||
public Address mapGuestToHost(Address guestAddress) {
|
||||
try (LockHold hold = LockHold.lock(manager.lock.readLock())) {
|
||||
Entry<Address, DBTraceGuestLanguageMappedRange> floorEntry =
|
||||
Entry<Address, DBTraceGuestPlatformMappedRange> floorEntry =
|
||||
rangesByGuestAddress.floorEntry(guestAddress);
|
||||
if (floorEntry == null) {
|
||||
return null;
|
||||
@ -186,12 +263,12 @@ public class DBTraceGuestLanguage extends DBAnnotatedObject implements TraceGues
|
||||
*/
|
||||
public Address mapGuestToHost(Address guestMin, Address guestMax) {
|
||||
try (LockHold hold = LockHold.lock(manager.lock.readLock())) {
|
||||
Entry<Address, DBTraceGuestLanguageMappedRange> floorEntry =
|
||||
Entry<Address, DBTraceGuestPlatformMappedRange> floorEntry =
|
||||
rangesByGuestAddress.floorEntry(guestMin);
|
||||
if (floorEntry == null) {
|
||||
return null;
|
||||
}
|
||||
DBTraceGuestLanguageMappedRange range = floorEntry.getValue();
|
||||
DBTraceGuestPlatformMappedRange range = floorEntry.getValue();
|
||||
if (!range.getGuestRange().contains(guestMax)) {
|
||||
return null;
|
||||
}
|
||||
@ -204,7 +281,7 @@ public class DBTraceGuestLanguage extends DBAnnotatedObject implements TraceGues
|
||||
/*return new DBTraceGuestLanguageMappedMemBuffer(manager.trace.getMemoryManager(), this, snap,
|
||||
guestAddress);*/
|
||||
return new DumbMemBufferImpl(
|
||||
new DBTraceGuestLanguageMappedMemory(manager.trace.getMemoryManager(), this, snap),
|
||||
new DBTraceGuestPlatformMappedMemory(manager.trace.getMemoryManager(), this, snap),
|
||||
guestAddress);
|
||||
}
|
||||
|
||||
@ -212,7 +289,7 @@ public class DBTraceGuestLanguage extends DBAnnotatedObject implements TraceGues
|
||||
public InstructionSet mapGuestInstructionAddressesToHost(InstructionSet instructionSet) {
|
||||
try (LockHold hold = LockHold.lock(manager.lock.readLock())) {
|
||||
Map<Address, InstructionBlock> blocksByNext = new HashMap<>();
|
||||
InstructionSet mappedSet = new InstructionSet(guestLanguage.getAddressFactory());
|
||||
InstructionSet mappedSet = new InstructionSet(getAddressFactory());
|
||||
for (InstructionBlock block : instructionSet) {
|
||||
for (Instruction instruction : block) {
|
||||
Address hostAddr =
|
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.trace.database.language;
|
||||
package ghidra.trace.database.guest;
|
||||
|
||||
import static ghidra.lifecycle.Unfinished.TODO;
|
||||
|
||||
@ -32,6 +32,7 @@ import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.mem.*;
|
||||
import ghidra.trace.database.memory.DBTraceMemoryManager;
|
||||
import ghidra.trace.database.memory.DBTraceMemorySpace;
|
||||
import ghidra.util.MathUtilities;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.exception.NotFoundException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
@ -39,15 +40,16 @@ import ghidra.util.task.TaskMonitor;
|
||||
/**
|
||||
* TODO: Document me
|
||||
*
|
||||
* <p>
|
||||
* Note this is the bare minimum to support {@link DumbMemBufferImpl}
|
||||
*/
|
||||
public class DBTraceGuestLanguageMappedMemory implements Memory {
|
||||
public class DBTraceGuestPlatformMappedMemory implements Memory {
|
||||
protected final DBTraceMemoryManager manager;
|
||||
protected final DBTraceGuestLanguage guest;
|
||||
protected final DBTraceGuestPlatform guest;
|
||||
protected final long snap;
|
||||
|
||||
public DBTraceGuestLanguageMappedMemory(DBTraceMemoryManager manager,
|
||||
DBTraceGuestLanguage guest, long snap) {
|
||||
public DBTraceGuestPlatformMappedMemory(DBTraceMemoryManager manager,
|
||||
DBTraceGuestPlatform guest, long snap) {
|
||||
this.manager = manager;
|
||||
this.guest = guest;
|
||||
this.snap = snap;
|
||||
@ -358,17 +360,17 @@ public class DBTraceGuestLanguageMappedMemory implements Memory {
|
||||
while (buffer.hasRemaining()) {
|
||||
int offset = buffer.position() - startPos;
|
||||
Address guestCur = guestStart.add(offset);
|
||||
Entry<Address, DBTraceGuestLanguageMappedRange> floorEntry =
|
||||
Entry<Address, DBTraceGuestPlatformMappedRange> floorEntry =
|
||||
guest.rangesByGuestAddress.floorEntry(guestCur);
|
||||
if (floorEntry == null) {
|
||||
return offset;
|
||||
}
|
||||
DBTraceGuestLanguageMappedRange range = floorEntry.getValue();
|
||||
DBTraceGuestPlatformMappedRange range = floorEntry.getValue();
|
||||
Address hostCur = range.mapGuestToHost(guestCur);
|
||||
if (hostCur == null) {
|
||||
return offset;
|
||||
}
|
||||
int lenToRead = (int) Math.min(buffer.remaining(),
|
||||
int lenToRead = MathUtilities.unsignedMin(buffer.remaining(),
|
||||
range.getGuestRange().getMaxAddress().subtract(guestStart) + 1);
|
||||
DBTraceMemorySpace hostSpace = manager.getMemorySpace(hostCur.getAddressSpace(), false);
|
||||
if (hostSpace == null) {
|
@ -13,22 +13,23 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.trace.database.language;
|
||||
package ghidra.trace.database.guest;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import db.DBRecord;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.lang.CompilerSpec;
|
||||
import ghidra.program.model.lang.Language;
|
||||
import ghidra.trace.model.language.TraceGuestLanguageMappedRange;
|
||||
import ghidra.trace.model.guest.TraceGuestPlatformMappedRange;
|
||||
import ghidra.util.database.*;
|
||||
import ghidra.util.database.annot.*;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
@DBAnnotatedObjectInfo(version = 0)
|
||||
public class DBTraceGuestLanguageMappedRange extends DBAnnotatedObject
|
||||
implements TraceGuestLanguageMappedRange {
|
||||
public class DBTraceGuestPlatformMappedRange extends DBAnnotatedObject
|
||||
implements TraceGuestPlatformMappedRange {
|
||||
public static final String TABLE_NAME = "LanguageMappings";
|
||||
|
||||
static final String HOST_SPACE_COLUMN_NAME = "HostSpace";
|
||||
@ -56,7 +57,7 @@ public class DBTraceGuestLanguageMappedRange extends DBAnnotatedObject
|
||||
@DBAnnotatedField(column = HOST_OFFSET_COLUMN_NAME)
|
||||
private long hostOffset;
|
||||
@DBAnnotatedField(column = GUEST_LANGUAGE_COLUMN_NAME)
|
||||
int guestLangKey;
|
||||
int guestPlatformKey;
|
||||
@DBAnnotatedField(column = GUEST_SPACE_COLUMN_NAME)
|
||||
private int guestSpace;
|
||||
@DBAnnotatedField(column = GUEST_OFFSET_COLUMN_NAME)
|
||||
@ -64,13 +65,13 @@ public class DBTraceGuestLanguageMappedRange extends DBAnnotatedObject
|
||||
@DBAnnotatedField(column = LENGTH_COLUMN_NAME)
|
||||
private long length;
|
||||
|
||||
private DBTraceLanguageManager manager;
|
||||
private DBTracePlatformManager manager;
|
||||
|
||||
private AddressRangeImpl hostRange;
|
||||
private Language guestLanguage;
|
||||
private DBTraceGuestPlatform guestPlatform;
|
||||
private AddressRangeImpl guestRange;
|
||||
|
||||
public DBTraceGuestLanguageMappedRange(DBTraceLanguageManager manager, DBCachedObjectStore<?> s,
|
||||
public DBTraceGuestPlatformMappedRange(DBTracePlatformManager manager, DBCachedObjectStore<?> s,
|
||||
DBRecord r) {
|
||||
super(s, r);
|
||||
this.manager = manager;
|
||||
@ -88,9 +89,9 @@ public class DBTraceGuestLanguageMappedRange extends DBAnnotatedObject
|
||||
Address hostEnd = hostStart.addNoWrap(length - 1);
|
||||
this.hostRange = new AddressRangeImpl(hostStart, hostEnd);
|
||||
|
||||
this.guestLanguage = manager.getLanguageByKey(guestLangKey);
|
||||
this.guestPlatform = manager.getPlatformByKey(guestPlatformKey);
|
||||
Address guestStart =
|
||||
guestLanguage.getAddressFactory().getAddress(guestSpace, guestOffset);
|
||||
guestPlatform.getAddressFactory().getAddress(guestSpace, guestOffset);
|
||||
Address guestEnd = guestStart.addNoWrap(length - 1);
|
||||
this.guestRange = new AddressRangeImpl(guestStart, guestEnd);
|
||||
}
|
||||
@ -99,19 +100,21 @@ public class DBTraceGuestLanguageMappedRange extends DBAnnotatedObject
|
||||
}
|
||||
}
|
||||
|
||||
void set(Address hostStart, Language guestLanguage, Address guestStart, long length) {
|
||||
this.hostRange = new AddressRangeImpl(hostStart, hostStart.add(length - 1));
|
||||
this.guestLanguage = guestLanguage;
|
||||
this.guestRange = new AddressRangeImpl(guestStart, guestStart.add(length - 1));
|
||||
|
||||
void set(Address hostStart, DBTraceGuestPlatform guestPlatform, Address guestStart,
|
||||
long length) {
|
||||
this.hostSpace = hostStart.getAddressSpace().getSpaceID();
|
||||
this.hostOffset = hostStart.getOffset();
|
||||
this.guestLangKey = manager.getKeyForLanguage(guestLanguage);
|
||||
this.guestPlatformKey = (int) guestPlatform.getKey();
|
||||
this.guestSpace = guestStart.getAddressSpace().getSpaceID();
|
||||
this.guestOffset = guestStart.getOffset();
|
||||
this.length = length;
|
||||
update(HOST_SPACE_COLUMN, HOST_OFFSET_COLUMN, GUEST_LANGUAGE_COLUMN, GUEST_SPACE_COLUMN,
|
||||
GUEST_OFFSET_COLUMN, LENGTH_COLUMN);
|
||||
|
||||
this.hostRange = new AddressRangeImpl(hostStart, hostStart.addWrap(length - 1));
|
||||
this.guestPlatform = guestPlatform;
|
||||
this.guestRange = new AddressRangeImpl(guestStart, guestStart.addWrap(length - 1));
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -119,14 +122,19 @@ public class DBTraceGuestLanguageMappedRange extends DBAnnotatedObject
|
||||
return manager.getBaseLanguage();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompilerSpec getHostCompilerSpec() {
|
||||
return manager.getBaseCompilerSpec();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressRange getHostRange() {
|
||||
return hostRange;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Language getGuestLanguage() {
|
||||
return guestLanguage;
|
||||
public DBTraceGuestPlatform getGuestPlatform() {
|
||||
return guestPlatform;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -154,6 +162,6 @@ public class DBTraceGuestLanguageMappedRange extends DBAnnotatedObject
|
||||
|
||||
@Override
|
||||
public void delete(TaskMonitor monitor) throws CancelledException {
|
||||
manager.languageStore.getObjectAt(guestLangKey).deleteMappedRange(this, monitor);
|
||||
manager.platformStore.getObjectAt(guestPlatformKey).deleteMappedRange(this, monitor);
|
||||
}
|
||||
}
|
@ -0,0 +1,298 @@
|
||||
/* ###
|
||||
* 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 java.io.IOException;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.locks.ReadWriteLock;
|
||||
|
||||
import db.DBHandle;
|
||||
import ghidra.lifecycle.Internal;
|
||||
import ghidra.program.model.lang.*;
|
||||
import ghidra.program.model.listing.Instruction;
|
||||
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.util.LockHold;
|
||||
import ghidra.util.database.*;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.exception.VersionException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
/*
|
||||
* TODO: Store mapping as from (host) address, to (guest) address, length. "to" must include
|
||||
* spaceId. It is not a problem if things overlap, as these are just informational in case an
|
||||
* instruction or reference comes along that needs mapping. This also determines what is visible
|
||||
* in program views of the mapped language. There should not be any overlaps in the same guest
|
||||
* language, however.
|
||||
*/
|
||||
public class DBTracePlatformManager implements DBTraceManager, TracePlatformManager {
|
||||
protected final DBHandle dbh;
|
||||
protected final ReadWriteLock lock;
|
||||
protected final Language baseLanguage;
|
||||
protected final CompilerSpec baseCompilerSpec;
|
||||
protected final DBTrace trace;
|
||||
|
||||
protected final DBCachedObjectStore<DBTraceGuestLanguage> languageStore;
|
||||
protected final DBCachedObjectStore<DBTraceGuestPlatform> platformStore;
|
||||
protected final Collection<TraceGuestPlatform> platformView;
|
||||
protected final Map<Language, DBTraceGuestLanguage> languagesByLanguage = new HashMap<>();
|
||||
protected final Map<CompilerSpec, DBTraceGuestPlatform> platformsByCompiler = new HashMap<>();
|
||||
|
||||
protected final DBCachedObjectStore<DBTraceGuestPlatformMappedRange> rangeMappingStore;
|
||||
|
||||
public DBTracePlatformManager(DBHandle dbh, DBOpenMode openMode, ReadWriteLock lock,
|
||||
TaskMonitor monitor, CompilerSpec baseCompilerSpec, DBTrace trace)
|
||||
throws VersionException, IOException {
|
||||
this.dbh = dbh;
|
||||
this.lock = lock;
|
||||
this.baseLanguage = baseCompilerSpec.getLanguage();
|
||||
this.baseCompilerSpec = baseCompilerSpec;
|
||||
this.trace = trace;
|
||||
|
||||
DBCachedObjectStoreFactory factory = trace.getStoreFactory();
|
||||
languageStore = factory.getOrCreateCachedStore(DBTraceGuestLanguage.TABLE_NAME,
|
||||
DBTraceGuestLanguage.class, DBTraceGuestLanguage::new, true);
|
||||
platformStore = factory.getOrCreateCachedStore(DBTraceGuestPlatform.TABLE_NAME,
|
||||
DBTraceGuestPlatform.class, (s, r) -> new DBTraceGuestPlatform(this, s, r), true);
|
||||
platformView = Collections.unmodifiableCollection(platformStore.asMap().values());
|
||||
|
||||
rangeMappingStore = factory.getOrCreateCachedStore(
|
||||
DBTraceGuestPlatformMappedRange.TABLE_NAME, DBTraceGuestPlatformMappedRange.class,
|
||||
(s, r) -> new DBTraceGuestPlatformMappedRange(this, s, r), true);
|
||||
|
||||
loadLanguages();
|
||||
loadPlatforms();
|
||||
loadPlatformMappings();
|
||||
}
|
||||
|
||||
protected void loadLanguages() {
|
||||
for (DBTraceGuestLanguage languageEntry : languageStore.asMap().values()) {
|
||||
languagesByLanguage.put(languageEntry.getLanguage(), languageEntry);
|
||||
}
|
||||
}
|
||||
|
||||
protected void loadPlatforms()
|
||||
throws LanguageNotFoundException, CompilerSpecNotFoundException, VersionException {
|
||||
for (DBTraceGuestPlatform platformEntry : platformStore.asMap().values()) {
|
||||
platformsByCompiler.put(platformEntry.getCompilerSpec(), platformEntry);
|
||||
}
|
||||
}
|
||||
|
||||
protected void loadPlatformMappings() {
|
||||
for (DBTraceGuestPlatformMappedRange langMapping : rangeMappingStore.asMap().values()) {
|
||||
DBTraceGuestPlatform mappedLanguage =
|
||||
platformStore.getObjectAt(langMapping.guestPlatformKey);
|
||||
mappedLanguage.rangesByHostAddress.put(langMapping.getHostRange().getMinAddress(),
|
||||
langMapping);
|
||||
mappedLanguage.rangesByGuestAddress.put(langMapping.getGuestRange().getMinAddress(),
|
||||
langMapping);
|
||||
}
|
||||
}
|
||||
|
||||
@Internal
|
||||
protected DBTraceGuestLanguage getOrCreateLanguage(Language language) {
|
||||
if (language == baseLanguage) {
|
||||
return null;
|
||||
}
|
||||
DBTraceGuestLanguage languageEntry = languagesByLanguage.get(language);
|
||||
if (languageEntry == null) {
|
||||
languageEntry = languageStore.create();
|
||||
languageEntry.set(language);
|
||||
languagesByLanguage.put(language, languageEntry);
|
||||
}
|
||||
return languageEntry;
|
||||
}
|
||||
|
||||
@Internal
|
||||
public DBTraceGuestLanguage getLanguageByKey(int key) {
|
||||
if (key == -1) {
|
||||
return null;
|
||||
}
|
||||
return languageStore.getObjectAt(key);
|
||||
}
|
||||
|
||||
@Internal
|
||||
public DBTraceGuestPlatform getPlatformByKey(int key) {
|
||||
if (key == -1) {
|
||||
return null;
|
||||
}
|
||||
return platformStore.getObjectAt(key);
|
||||
}
|
||||
|
||||
protected int getPlatformKeyForCompiler(CompilerSpec compiler) {
|
||||
if (Objects.equals(compiler, baseCompilerSpec)) {
|
||||
return -1;
|
||||
}
|
||||
return (int) platformsByCompiler.get(compiler).getKey();
|
||||
}
|
||||
|
||||
@Internal
|
||||
public DBTraceGuestLanguage getLanguageByLanguage(Language language) {
|
||||
if (Objects.equals(language, baseLanguage)) {
|
||||
return null;
|
||||
}
|
||||
return Objects.requireNonNull(languagesByLanguage.get(language));
|
||||
}
|
||||
|
||||
protected CompilerSpec getCompilerByKey(int compilerKey) {
|
||||
if (compilerKey == -1) {
|
||||
return baseCompilerSpec;
|
||||
}
|
||||
return platformStore.getObjectAt(compilerKey).getCompilerSpec();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dbError(IOException e) {
|
||||
trace.dbError(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateCache(boolean all) {
|
||||
languageStore.invalidateCache();
|
||||
platformStore.invalidateCache();
|
||||
rangeMappingStore.invalidateCache();
|
||||
languagesByLanguage.clear();
|
||||
platformsByCompiler.clear();
|
||||
try {
|
||||
loadLanguages();
|
||||
loadPlatforms();
|
||||
loadPlatformMappings();
|
||||
}
|
||||
catch (LanguageNotFoundException | CompilerSpecNotFoundException | VersionException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
protected void deleteGuestPlatform(DBTraceGuestPlatform platform, TaskMonitor monitor)
|
||||
throws CancelledException {
|
||||
try (LockHold hold = LockHold.lock(lock.writeLock())) {
|
||||
int platformKey = (int) platform.getKey();
|
||||
trace.getCodeManager().deletePlatform(platform, monitor);
|
||||
monitor.setMessage("Clearing guest platform range mappings");
|
||||
monitor.setMaximum(rangeMappingStore.getRecordCount());
|
||||
for (Iterator<DBTraceGuestPlatformMappedRange> it =
|
||||
rangeMappingStore.asMap().values().iterator(); it.hasNext();) {
|
||||
DBTraceGuestPlatformMappedRange range = it.next();
|
||||
if (platformKey != range.guestPlatformKey) {
|
||||
continue;
|
||||
}
|
||||
it.remove();
|
||||
}
|
||||
platformsByCompiler.remove(platform.getCompilerSpec());
|
||||
platformStore.delete(platform);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Language getBaseLanguage() {
|
||||
return trace.getBaseLanguage();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompilerSpec getBaseCompilerSpec() {
|
||||
return trace.getBaseCompilerSpec();
|
||||
}
|
||||
|
||||
protected DBTraceGuestPlatform doAddGuestPlatform(CompilerSpec compilerSpec) {
|
||||
DBTraceGuestPlatform platformEntry = platformStore.create();
|
||||
platformEntry.set(compilerSpec);
|
||||
platformsByCompiler.put(compilerSpec, platformEntry);
|
||||
return platformEntry;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DBTraceGuestPlatform addGuestPlatform(CompilerSpec compilerSpec) {
|
||||
if (compilerSpec.getCompilerSpecID()
|
||||
.equals(trace.getBaseCompilerSpec().getCompilerSpecID())) {
|
||||
throw new IllegalArgumentException("Base language cannot be a guest language");
|
||||
}
|
||||
try (LockHold hold = LockHold.lock(lock.writeLock())) {
|
||||
return doAddGuestPlatform(compilerSpec);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public DBTraceGuestPlatform getGuestPlatform(CompilerSpec compilerSpec) {
|
||||
try (LockHold hold = LockHold.lock(lock.readLock())) {
|
||||
return platformsByCompiler.get(compilerSpec);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public DBTraceGuestPlatform getOrAddGuestPlatform(CompilerSpec compilerSpec) {
|
||||
if (compilerSpec.getCompilerSpecID()
|
||||
.equals(trace.getBaseCompilerSpec().getCompilerSpecID())) {
|
||||
throw new IllegalArgumentException("Base language cannot be a guest language");
|
||||
}
|
||||
try (LockHold hold = LockHold.lock(lock.writeLock())) {
|
||||
DBTraceGuestPlatform exists = platformsByCompiler.get(compilerSpec);
|
||||
if (exists != null) {
|
||||
return exists;
|
||||
}
|
||||
return doAddGuestPlatform(compilerSpec);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<TraceGuestPlatform> getGuestPlatforms() {
|
||||
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;
|
||||
}
|
||||
if (!(platform instanceof DBTraceGuestPlatform)) {
|
||||
throw new IllegalArgumentException("Given platform does not belong to this trace");
|
||||
}
|
||||
DBTraceGuestPlatform dbPlatform = (DBTraceGuestPlatform) platform;
|
||||
if (dbPlatform.manager != this) {
|
||||
throw new IllegalArgumentException("Given platform does not belong to this trace");
|
||||
}
|
||||
if (dbPlatform.isDeleted()) {
|
||||
throw new IllegalArgumentException("Given platform has been deleted");
|
||||
}
|
||||
return dbPlatform;
|
||||
}
|
||||
}
|
@ -1,204 +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.trace.database.language;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.locks.ReadWriteLock;
|
||||
|
||||
import db.DBHandle;
|
||||
import ghidra.program.model.lang.*;
|
||||
import ghidra.program.model.listing.Instruction;
|
||||
import ghidra.program.util.DefaultLanguageService;
|
||||
import ghidra.trace.database.DBTrace;
|
||||
import ghidra.trace.database.DBTraceManager;
|
||||
import ghidra.trace.model.language.TraceGuestLanguage;
|
||||
import ghidra.trace.model.language.TraceLanguageManager;
|
||||
import ghidra.util.LockHold;
|
||||
import ghidra.util.database.*;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.exception.VersionException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
/*
|
||||
* TODO: Store mapping as from (host) address, to (guest) address, length. "to" must include
|
||||
* spaceId. It is not a problem if things overlap, as these are just informational in case an
|
||||
* instruction or reference comes along that needs mapping. This also determines what is visible
|
||||
* in program views of the mapped language. There should not be any overlaps in the same guest
|
||||
* language, however.
|
||||
*/
|
||||
public class DBTraceLanguageManager implements DBTraceManager, TraceLanguageManager {
|
||||
protected final DBHandle dbh;
|
||||
protected final ReadWriteLock lock;
|
||||
protected final Language baseLanguage;
|
||||
protected final DBTrace trace;
|
||||
|
||||
protected final DBCachedObjectStore<DBTraceGuestLanguage> languageStore;
|
||||
protected final Collection<TraceGuestLanguage> languageView;
|
||||
protected final Map<Language, DBTraceGuestLanguage> entriesByLanguage = new HashMap<>();
|
||||
|
||||
protected final DBCachedObjectStore<DBTraceGuestLanguageMappedRange> rangeMappingStore;
|
||||
|
||||
public DBTraceLanguageManager(DBHandle dbh, DBOpenMode openMode, ReadWriteLock lock,
|
||||
TaskMonitor monitor, Language baseLanguage, DBTrace trace)
|
||||
throws VersionException, IOException {
|
||||
this.dbh = dbh;
|
||||
this.lock = lock;
|
||||
this.baseLanguage = baseLanguage;
|
||||
this.trace = trace;
|
||||
|
||||
DBCachedObjectStoreFactory factory = trace.getStoreFactory();
|
||||
languageStore = factory.getOrCreateCachedStore(DBTraceGuestLanguage.TABLE_NAME,
|
||||
DBTraceGuestLanguage.class, (s, r) -> new DBTraceGuestLanguage(this, s, r), true);
|
||||
languageView = Collections.unmodifiableCollection(languageStore.asMap().values());
|
||||
|
||||
rangeMappingStore = factory.getOrCreateCachedStore(
|
||||
DBTraceGuestLanguageMappedRange.TABLE_NAME, DBTraceGuestLanguageMappedRange.class,
|
||||
(s, r) -> new DBTraceGuestLanguageMappedRange(this, s, r), true);
|
||||
|
||||
loadLanguages();
|
||||
loadLanguageMappings();
|
||||
}
|
||||
|
||||
protected void loadLanguages() throws LanguageNotFoundException, VersionException {
|
||||
LanguageService langServ = DefaultLanguageService.getLanguageService();
|
||||
for (DBTraceGuestLanguage langEnt : languageStore.asMap().values()) {
|
||||
langEnt.doGetLanguage(langServ);
|
||||
entriesByLanguage.put(langEnt.getLanguage(), langEnt);
|
||||
}
|
||||
}
|
||||
|
||||
protected void loadLanguageMappings() {
|
||||
for (DBTraceGuestLanguageMappedRange langMapping : rangeMappingStore.asMap().values()) {
|
||||
DBTraceGuestLanguage mappedLanguage =
|
||||
languageStore.getObjectAt(langMapping.guestLangKey);
|
||||
mappedLanguage.rangesByHostAddress.put(langMapping.getHostRange().getMinAddress(),
|
||||
langMapping);
|
||||
mappedLanguage.rangesByGuestAddress.put(langMapping.getGuestRange().getMinAddress(),
|
||||
langMapping);
|
||||
}
|
||||
}
|
||||
|
||||
// Internal
|
||||
public int getKeyForLanguage(Language language) {
|
||||
if (Objects.equals(language, baseLanguage)) {
|
||||
return -1;
|
||||
}
|
||||
return (int) entriesByLanguage.get(language).getKey();
|
||||
}
|
||||
|
||||
// Internal
|
||||
public Language getLanguageByKey(int langKey) {
|
||||
if (langKey == -1) {
|
||||
return baseLanguage;
|
||||
}
|
||||
return languageStore.getObjectAt(langKey).getLanguage();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dbError(IOException e) {
|
||||
trace.dbError(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateCache(boolean all) {
|
||||
languageStore.invalidateCache();
|
||||
rangeMappingStore.invalidateCache();
|
||||
entriesByLanguage.clear();
|
||||
try {
|
||||
loadLanguages();
|
||||
loadLanguageMappings();
|
||||
}
|
||||
catch (LanguageNotFoundException | VersionException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
protected void deleteGuestLanguage(DBTraceGuestLanguage langEnt, TaskMonitor monitor)
|
||||
throws CancelledException {
|
||||
try (LockHold hold = LockHold.lock(lock.writeLock())) {
|
||||
int langKey = (int) langEnt.getKey();
|
||||
trace.getCodeManager().deleteLanguage(langKey, monitor);
|
||||
monitor.setMessage("Clearing guest language range mappings");
|
||||
monitor.setMaximum(rangeMappingStore.getRecordCount());
|
||||
for (Iterator<DBTraceGuestLanguageMappedRange> it =
|
||||
rangeMappingStore.asMap().values().iterator(); it.hasNext();) {
|
||||
DBTraceGuestLanguageMappedRange range = it.next();
|
||||
if (langKey != range.guestLangKey) {
|
||||
continue;
|
||||
}
|
||||
it.remove();
|
||||
}
|
||||
entriesByLanguage.remove(langEnt.getLanguage());
|
||||
languageStore.delete(langEnt);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Language getBaseLanguage() {
|
||||
return trace.getBaseLanguage();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DBTraceGuestLanguage addGuestLanguage(Language language) {
|
||||
if (language.getLanguageID().equals(trace.getBaseLanguage().getLanguageID())) {
|
||||
throw new IllegalArgumentException("Base language cannot be a guest language");
|
||||
}
|
||||
try (LockHold hold = LockHold.lock(lock.writeLock())) {
|
||||
DBTraceGuestLanguage langEnt = languageStore.create();
|
||||
langEnt.setLanguage(language);
|
||||
entriesByLanguage.put(language, langEnt);
|
||||
return langEnt;
|
||||
}
|
||||
}
|
||||
|
||||
public DBTraceGuestLanguage getGuestLanguage(Language language) {
|
||||
try (LockHold hold = LockHold.lock(lock.readLock())) {
|
||||
return entriesByLanguage.get(language);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<TraceGuestLanguage> getGuestLanguages() {
|
||||
return languageView;
|
||||
}
|
||||
|
||||
protected Language getLanguageOf(InstructionSet instructionSet) {
|
||||
for (InstructionBlock block : instructionSet) {
|
||||
for (Instruction instruction : block) {
|
||||
return instruction.getPrototype().getLanguage();
|
||||
}
|
||||
}
|
||||
// No instructions, default to base language
|
||||
return trace.getBaseLanguage();
|
||||
}
|
||||
|
||||
@Override
|
||||
public InstructionSet mapGuestInstructionAddressesToHost(InstructionSet instructionSet) {
|
||||
try (LockHold hold = LockHold.lock(lock.readLock())) {
|
||||
Language language = getLanguageOf(instructionSet);
|
||||
if (language == trace.getBaseLanguage()) {
|
||||
return instructionSet; // Nothing to map
|
||||
}
|
||||
DBTraceGuestLanguage guest = trace.getLanguageManager().getGuestLanguage(language);
|
||||
if (guest == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"Instructions are in neither the base nor a guest language");
|
||||
}
|
||||
return guest.mapGuestInstructionAddressesToHost(instructionSet);
|
||||
}
|
||||
}
|
||||
}
|
@ -25,6 +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.thread.TraceThread;
|
||||
import ghidra.util.LockHold;
|
||||
|
||||
@ -82,6 +83,11 @@ public abstract class AbstractDBTraceDataComponent implements DBTraceDefinedData
|
||||
return root.getThread();
|
||||
}
|
||||
|
||||
@Override
|
||||
public TraceGuestPlatform getGuestPlatform() {
|
||||
return root.getGuestPlatform();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Language getLanguage() {
|
||||
return root.getLanguage();
|
||||
|
@ -25,10 +25,11 @@ import java.util.concurrent.locks.ReadWriteLock;
|
||||
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.cache.RemovalNotification;
|
||||
import com.google.common.collect.*;
|
||||
import com.google.common.collect.Range;
|
||||
|
||||
import db.DBHandle;
|
||||
import db.DBRecord;
|
||||
import ghidra.lifecycle.Internal;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.lang.*;
|
||||
import ghidra.program.model.listing.ContextChangeException;
|
||||
@ -41,7 +42,9 @@ import ghidra.trace.database.address.DBTraceOverlaySpaceAdapter;
|
||||
import ghidra.trace.database.address.DBTraceOverlaySpaceAdapter.AddressDBFieldCodec;
|
||||
import ghidra.trace.database.address.DBTraceOverlaySpaceAdapter.DecodesAddresses;
|
||||
import ghidra.trace.database.data.DBTraceDataTypeManager;
|
||||
import ghidra.trace.database.language.DBTraceLanguageManager;
|
||||
import ghidra.trace.database.guest.DBTraceGuestPlatform;
|
||||
import ghidra.trace.database.guest.DBTracePlatformManager;
|
||||
import ghidra.trace.database.guest.DBTraceGuestPlatform.DBTraceGuestLanguage;
|
||||
import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree.TraceAddressSnapRangeQuery;
|
||||
import ghidra.trace.database.space.AbstractDBTraceSpaceBasedManager;
|
||||
import ghidra.trace.database.space.DBTraceDelegatingManager;
|
||||
@ -90,7 +93,7 @@ public class DBTraceCodeManager
|
||||
static DBObjectColumn DELAY_COLUMN;
|
||||
|
||||
@DBAnnotatedField(column = LANGUAGE_COLUMN_NAME)
|
||||
private int langKey;
|
||||
private int languageKey;
|
||||
@DBAnnotatedField(column = BYTES_COLUMN_NAME)
|
||||
private byte[] bytes;
|
||||
@DBAnnotatedField(column = CONTEXT_COLUMN_NAME)
|
||||
@ -100,6 +103,8 @@ public class DBTraceCodeManager
|
||||
@DBAnnotatedField(column = DELAY_COLUMN_NAME)
|
||||
private boolean delaySlot;
|
||||
|
||||
private InstructionPrototype prototype;
|
||||
|
||||
private DBTraceCodeManager manager;
|
||||
|
||||
public DBTraceCodePrototypeEntry(DBTraceCodeManager manager, DBCachedObjectStore<?> store,
|
||||
@ -113,21 +118,33 @@ public class DBTraceCodeManager
|
||||
return manager.overlayAdapter;
|
||||
}
|
||||
|
||||
void set(int langKey, byte[] bytes, byte[] context, Address address, boolean delaySlot) {
|
||||
this.langKey = langKey;
|
||||
void set(DBTraceGuestLanguage languageEntry, byte[] bytes, byte[] context, Address address,
|
||||
boolean delaySlot) {
|
||||
this.languageKey = (int) (languageEntry == null ? -1 : languageEntry.getKey());
|
||||
this.bytes = bytes;
|
||||
this.context = context;
|
||||
this.address = address;
|
||||
this.delaySlot = delaySlot;
|
||||
update(LANGUAGE_COLUMN, BYTES_COLUMN, CONTEXT_COLUMN, ADDRESS_COLUMN, DELAY_COLUMN);
|
||||
this.prototype = parsePrototype();
|
||||
}
|
||||
|
||||
int getLanguageKey() {
|
||||
return langKey;
|
||||
@Override
|
||||
protected void fresh(boolean created) throws IOException {
|
||||
super.fresh(created);
|
||||
if (created) {
|
||||
return;
|
||||
}
|
||||
this.prototype = parsePrototype();
|
||||
}
|
||||
|
||||
InstructionPrototype parsePrototype() {
|
||||
Language language = manager.languageManager.getLanguageByKey(langKey);
|
||||
public InstructionPrototype getPrototype() {
|
||||
return prototype;
|
||||
}
|
||||
|
||||
private InstructionPrototype parsePrototype() {
|
||||
DBTraceGuestLanguage guest = manager.platformManager.getLanguageByKey(languageKey);
|
||||
Language language = guest == null ? manager.baseLanguage : guest.getLanguage();
|
||||
MemBuffer memBuffer = new ByteMemBufferImpl(address, bytes, language.isBigEndian());
|
||||
ProcessorContext ctx =
|
||||
new ProtoProcessorContext(getBaseContextValue(language, context, address));
|
||||
@ -168,13 +185,14 @@ public class DBTraceCodeManager
|
||||
return max;
|
||||
}
|
||||
|
||||
protected final DBTraceLanguageManager languageManager;
|
||||
protected final DBTracePlatformManager platformManager;
|
||||
protected final DBTraceDataTypeManager dataTypeManager;
|
||||
protected final DBTraceOverlaySpaceAdapter overlayAdapter;
|
||||
protected final DBTraceReferenceManager referenceManager;
|
||||
|
||||
protected final DBCachedObjectStore<DBTraceCodePrototypeEntry> protoStore;
|
||||
protected final BiMap<InstructionPrototype, Integer> protoMap = HashBiMap.create();
|
||||
protected final Map<InstructionPrototype, DBTraceCodePrototypeEntry> entriesByProto =
|
||||
new HashMap<>();
|
||||
|
||||
protected final DBTraceCodeUnitsMemoryView codeUnits = new DBTraceCodeUnitsMemoryView(this);
|
||||
protected final DBTraceInstructionsMemoryView instructions =
|
||||
@ -196,11 +214,11 @@ public class DBTraceCodeManager
|
||||
|
||||
public DBTraceCodeManager(DBHandle dbh, DBOpenMode openMode, ReadWriteLock lock,
|
||||
TaskMonitor monitor, Language baseLanguage, DBTrace trace,
|
||||
DBTraceThreadManager threadManager, DBTraceLanguageManager languageManager,
|
||||
DBTraceThreadManager threadManager, DBTracePlatformManager platformManager,
|
||||
DBTraceDataTypeManager dataTypeManager, DBTraceOverlaySpaceAdapter overlayAdapter,
|
||||
DBTraceReferenceManager referenceManager) throws IOException, VersionException {
|
||||
super(NAME, dbh, openMode, lock, monitor, baseLanguage, trace, threadManager);
|
||||
this.languageManager = languageManager;
|
||||
this.platformManager = platformManager;
|
||||
this.dataTypeManager = dataTypeManager;
|
||||
this.overlayAdapter = overlayAdapter;
|
||||
this.referenceManager = referenceManager;
|
||||
@ -230,7 +248,7 @@ public class DBTraceCodeManager
|
||||
// NOTE: Should already own write lock
|
||||
for (DBTraceCodePrototypeEntry protoEnt : protoStore.asMap().values()) {
|
||||
// NOTE: No need to check if it exists. This is only called on new or after clear
|
||||
protoMap.put(protoEnt.parsePrototype(), (int) protoEnt.getKey());
|
||||
entriesByProto.put(protoEnt.prototype, protoEnt);
|
||||
}
|
||||
}
|
||||
|
||||
@ -239,8 +257,8 @@ public class DBTraceCodeManager
|
||||
return Arrays.copyOfRange(bytes, bytes.length / 2, bytes.length);
|
||||
}
|
||||
|
||||
protected int doRecordPrototype(InstructionPrototype prototype, MemBuffer memBuffer,
|
||||
ProcessorContextView context) {
|
||||
protected DBTraceCodePrototypeEntry doRecordPrototype(InstructionPrototype prototype,
|
||||
DBTraceGuestLanguage guest, MemBuffer memBuffer, ProcessorContextView context) {
|
||||
DBTraceCodePrototypeEntry protoEnt = protoStore.create();
|
||||
byte[] bytes = new byte[prototype.getLength()];
|
||||
if (memBuffer.getBytes(bytes, 0) != bytes.length) {
|
||||
@ -255,20 +273,20 @@ public class DBTraceCodeManager
|
||||
RegisterValue value = context.getRegisterValue(baseCtxReg);
|
||||
ctx = value == null ? null : valueBytes(value);
|
||||
}
|
||||
protoEnt.set(languageManager.getKeyForLanguage(prototype.getLanguage()), bytes, ctx,
|
||||
memBuffer.getAddress(), prototype.isInDelaySlot());
|
||||
return (int) protoEnt.getKey();
|
||||
protoEnt.set(guest, bytes, ctx, memBuffer.getAddress(), prototype.isInDelaySlot());
|
||||
return protoEnt;
|
||||
}
|
||||
|
||||
protected int findOrRecordPrototype(InstructionPrototype prototype, MemBuffer memBuffer,
|
||||
ProcessorContextView context) {
|
||||
protected DBTraceCodePrototypeEntry findOrRecordPrototype(InstructionPrototype prototype,
|
||||
DBTraceGuestLanguage guest, MemBuffer memBuffer, ProcessorContextView context) {
|
||||
// NOTE: Must already have write lock
|
||||
return protoMap.computeIfAbsent(prototype,
|
||||
p -> doRecordPrototype(prototype, memBuffer, context));
|
||||
return entriesByProto.computeIfAbsent(prototype,
|
||||
p -> doRecordPrototype(prototype, guest, memBuffer, context));
|
||||
}
|
||||
|
||||
protected InstructionPrototype getPrototypeByKey(int key) {
|
||||
return protoMap.inverse().get(key);
|
||||
DBTraceCodePrototypeEntry protoEnt = protoStore.getObjectAt(key);
|
||||
return protoEnt == null ? null : protoEnt.prototype;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -326,35 +344,43 @@ public class DBTraceCodeManager
|
||||
return getForRegisterSpace(frame, createIfAbsent);
|
||||
}
|
||||
|
||||
// Internal
|
||||
@Internal
|
||||
public void replaceDataTypes(long oldID, long newID) {
|
||||
TODO();
|
||||
}
|
||||
|
||||
// Internal
|
||||
@Internal
|
||||
public void clearData(LinkedList<Long> deletedDataTypeIds, TaskMonitor monitor) {
|
||||
TODO();
|
||||
}
|
||||
|
||||
// Internal
|
||||
public void clearLanguage(Range<Long> span, AddressRange range, int langKey,
|
||||
@Internal
|
||||
public void clearPlatform(Range<Long> span, AddressRange range, DBTraceGuestPlatform guest,
|
||||
TaskMonitor monitor) throws CancelledException {
|
||||
delegateDeleteV(range.getAddressSpace(),
|
||||
m -> m.clearLanguage(span, range, langKey, monitor));
|
||||
m -> m.clearPlatform(span, range, guest, monitor));
|
||||
}
|
||||
|
||||
// Internal
|
||||
public void deleteLanguage(int langKey, TaskMonitor monitor) throws CancelledException {
|
||||
@Internal
|
||||
public void deletePlatform(DBTraceGuestPlatform guest, TaskMonitor monitor)
|
||||
throws CancelledException {
|
||||
// TODO: Use sub-monitors when available
|
||||
for (DBTraceCodeSpace codeSpace : memSpaces.values()) {
|
||||
codeSpace.clearLanguage(Range.all(), codeSpace.all, langKey, monitor);
|
||||
codeSpace.clearPlatform(Range.all(), codeSpace.all, guest, monitor);
|
||||
}
|
||||
for (DBTraceCodeRegisterSpace codeSpace : regSpaces.values()) {
|
||||
// TODO: I don't know any way to get guest instructions into register space
|
||||
// The mapping manager does (should) not allow guest register addresses
|
||||
// TODO: Test this if I ever get guest data units
|
||||
codeSpace.clearLanguage(Range.all(), codeSpace.all, langKey, monitor);
|
||||
// TODO: I think explicit per-thread/frame register spaces will be going away, anyway
|
||||
// They'll just be path-named overlays on register space?
|
||||
codeSpace.clearPlatform(Range.all(), codeSpace.all, guest, monitor);
|
||||
}
|
||||
}
|
||||
|
||||
@Internal
|
||||
public void deleteLangauge(DBTraceGuestLanguage guest, TaskMonitor monitor)
|
||||
throws CancelledException {
|
||||
monitor.setMessage("Clearing instruction prototypes");
|
||||
monitor.setMaximum(protoStore.getRecordCount());
|
||||
for (Iterator<DBTraceCodePrototypeEntry> it = protoStore.asMap().values().iterator(); it
|
||||
@ -362,11 +388,11 @@ public class DBTraceCodeManager
|
||||
monitor.checkCanceled();
|
||||
monitor.incrementProgress(1);
|
||||
DBTraceCodePrototypeEntry protoEnt = it.next();
|
||||
if (langKey != protoEnt.langKey) {
|
||||
if (protoEnt.prototype.getLanguage() != guest.getLanguage()) {
|
||||
continue;
|
||||
}
|
||||
it.remove();
|
||||
protoMap.inverse().remove((int) protoEnt.getKey());
|
||||
entriesByProto.remove(protoEnt.prototype);
|
||||
}
|
||||
}
|
||||
|
||||
@ -374,7 +400,7 @@ public class DBTraceCodeManager
|
||||
public void invalidateCache(boolean all) {
|
||||
try (LockHold hold = LockHold.lock(lock.writeLock())) {
|
||||
protoStore.invalidateCache();
|
||||
protoMap.clear();
|
||||
entriesByProto.clear();
|
||||
loadPrototypes();
|
||||
|
||||
super.invalidateCache(all);
|
||||
|
@ -32,6 +32,7 @@ import ghidra.program.model.util.CodeUnitInsertionException;
|
||||
import ghidra.trace.database.DBTrace;
|
||||
import ghidra.trace.database.DBTraceUtils;
|
||||
import ghidra.trace.database.data.DBTraceDataTypeManager;
|
||||
import ghidra.trace.database.guest.DBTraceGuestPlatform;
|
||||
import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapSpace;
|
||||
import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree.TraceAddressSnapRangeQuery;
|
||||
import ghidra.trace.database.space.AbstractDBTraceSpaceBasedManager.DBTraceSpaceEntry;
|
||||
@ -125,8 +126,8 @@ public class DBTraceCodeSpace implements TraceCodeSpace, DBTraceSpaceBased {
|
||||
return new DBTraceCodeUnitsView(this);
|
||||
}
|
||||
|
||||
void clearLanguage(Range<Long> span, AddressRange range, int langKey, TaskMonitor monitor)
|
||||
throws CancelledException {
|
||||
void clearPlatform(Range<Long> span, AddressRange range, DBTraceGuestPlatform guest,
|
||||
TaskMonitor monitor) throws CancelledException {
|
||||
// Note "makeWay" does not apply here.
|
||||
// Units should be enclosed by guest mapping.
|
||||
// TODO: Use sub-monitors when available
|
||||
@ -141,8 +142,7 @@ public class DBTraceCodeSpace implements TraceCodeSpace, DBTraceSpaceBased {
|
||||
TraceAddressSnapRangeQuery.intersecting(range, span)).values()) {
|
||||
monitor.checkCanceled();
|
||||
monitor.incrementProgress(1);
|
||||
if (langKey != manager.protoStore.getObjectAt(
|
||||
instruction.getPrototypeKey()).getLanguageKey()) {
|
||||
if (instruction.guest != 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 (langKey != dataUnit.getLanguageKey()) {
|
||||
if (dataUnit.guest != guest) {
|
||||
continue;
|
||||
}
|
||||
// TODO: I don't yet have guest-language data units.
|
||||
|
@ -26,7 +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.map.DBTraceAddressSnapRangePropertyMapTree;
|
||||
import ghidra.trace.model.guest.TraceGuestPlatform;
|
||||
import ghidra.util.LockHold;
|
||||
import ghidra.util.database.DBCachedObjectStore;
|
||||
import ghidra.util.database.DBObjectColumn;
|
||||
@ -37,11 +39,11 @@ public class DBTraceData extends AbstractDBTraceCodeUnit<DBTraceData>
|
||||
implements DBTraceDefinedDataAdapter {
|
||||
private static final String TABLE_NAME = "Data";
|
||||
|
||||
static final String LANGUAGE_COLUMN_NAME = "Language";
|
||||
static final String PLATFORM_COLUMN_NAME = "Platform";
|
||||
static final String DATATYPE_COLUMN_NAME = "DataType";
|
||||
|
||||
@DBAnnotatedColumn(LANGUAGE_COLUMN_NAME)
|
||||
static DBObjectColumn LANGUAGE_COLUMN;
|
||||
@DBAnnotatedColumn(PLATFORM_COLUMN_NAME)
|
||||
static DBObjectColumn PLATFORM_COLUMN;
|
||||
@DBAnnotatedColumn(DATATYPE_COLUMN_NAME)
|
||||
static DBObjectColumn DATATYPE_COLUMN;
|
||||
|
||||
@ -49,12 +51,12 @@ public class DBTraceData extends AbstractDBTraceCodeUnit<DBTraceData>
|
||||
return DBTraceUtils.tableName(TABLE_NAME, space, threadKey, frameLevel);
|
||||
}
|
||||
|
||||
@DBAnnotatedField(column = LANGUAGE_COLUMN_NAME)
|
||||
private int langKey;
|
||||
@DBAnnotatedField(column = PLATFORM_COLUMN_NAME)
|
||||
private int platformKey;
|
||||
@DBAnnotatedField(column = DATATYPE_COLUMN_NAME)
|
||||
private long dataTypeID;
|
||||
|
||||
protected Language language;
|
||||
protected DBTraceGuestPlatform guest;
|
||||
protected DataType dataType;
|
||||
protected DataType baseDataType;
|
||||
protected Settings defaultSettings;
|
||||
@ -73,9 +75,9 @@ public class DBTraceData extends AbstractDBTraceCodeUnit<DBTraceData>
|
||||
if (created) {
|
||||
return;
|
||||
}
|
||||
language = space.manager.languageManager.getLanguageByKey(langKey);
|
||||
if (language == null) {
|
||||
throw new IOException("Data table is corrupt. Missing language: " + langKey);
|
||||
guest = space.manager.platformManager.getPlatformByKey(platformKey);
|
||||
if (guest == null && platformKey != -1) {
|
||||
throw new IOException("Data table is corrupt. Missing platform: " + platformKey);
|
||||
}
|
||||
dataType = space.dataTypeManager.getDataType(dataTypeID);
|
||||
if (dataType == null) {
|
||||
@ -100,16 +102,17 @@ public class DBTraceData extends AbstractDBTraceCodeUnit<DBTraceData>
|
||||
return this;
|
||||
}
|
||||
|
||||
protected void set(Language language, DataType dataType) {
|
||||
this.language = language;
|
||||
this.langKey = space.manager.languageManager.getKeyForLanguage(language);
|
||||
protected void set(DBTraceGuestPlatform platform, DataType dataType) {
|
||||
this.platformKey = (int) (platform == null ? -1 : platform.getKey());
|
||||
this.dataTypeID = space.dataTypeManager.getResolvedID(dataType);
|
||||
update(PLATFORM_COLUMN, DATATYPE_COLUMN);
|
||||
|
||||
this.guest = platform;
|
||||
// Use the stored dataType, not the given one, in case it's different
|
||||
this.dataType = space.dataTypeManager.getDataType(dataTypeID);
|
||||
assert this.dataType != null;
|
||||
this.baseDataType = getBaseDataType(this.dataType);
|
||||
this.defaultSettings = this.dataType.getDefaultSettings();
|
||||
update(LANGUAGE_COLUMN, DATATYPE_COLUMN);
|
||||
this.baseDataType = getBaseDataType(this.dataType);
|
||||
}
|
||||
|
||||
protected int getDataTypeLength() {
|
||||
@ -117,7 +120,7 @@ public class DBTraceData extends AbstractDBTraceCodeUnit<DBTraceData>
|
||||
// TODO: Also need to know where this address maps into the other language's spaces....
|
||||
// NOTE: Using default data space for now
|
||||
// TODO: I may not need this Pointer check, as clone(dtm) should adjust already
|
||||
return language.getDefaultDataSpace().getPointerSize();
|
||||
return getLanguage().getDefaultDataSpace().getPointerSize();
|
||||
}
|
||||
return dataType.getLength(); // -1 is checked elsewhere
|
||||
}
|
||||
@ -129,6 +132,11 @@ public class DBTraceData extends AbstractDBTraceCodeUnit<DBTraceData>
|
||||
return dt;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TraceGuestPlatform getGuestPlatform() {
|
||||
return guest;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete() {
|
||||
try (LockHold hold = LockHold.lock(space.lock.writeLock())) {
|
||||
@ -149,11 +157,7 @@ public class DBTraceData extends AbstractDBTraceCodeUnit<DBTraceData>
|
||||
|
||||
@Override
|
||||
public Language getLanguage() {
|
||||
return language;
|
||||
}
|
||||
|
||||
int getLanguageKey() {
|
||||
return langKey;
|
||||
return guest == null ? space.baseLanguage : guest.getLanguage();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -145,7 +145,8 @@ public class DBTraceDefinedDataView extends AbstractBaseDBTraceDefinedUnitsView<
|
||||
}
|
||||
|
||||
DBTraceData created = space.dataMapSpace.put(tasr, null);
|
||||
created.set(space.baseLanguage, dataType);
|
||||
// TODO: data units with a guest platform
|
||||
created.set(null, dataType);
|
||||
// TODO: Explicitly remove undefined from cache, or let weak refs take care of it?
|
||||
|
||||
cacheForContaining.notifyNewEntry(lifespan, createdRange, created);
|
||||
|
@ -32,11 +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.language.DBTraceGuestLanguage;
|
||||
import ghidra.trace.database.guest.DBTraceGuestPlatform;
|
||||
import ghidra.trace.database.guest.DBTraceGuestPlatform.DBTraceGuestLanguage;
|
||||
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.listing.TraceInstruction;
|
||||
import ghidra.trace.model.symbol.TraceReference;
|
||||
import ghidra.trace.util.*;
|
||||
@ -59,9 +61,12 @@ public class DBTraceInstruction extends AbstractDBTraceCodeUnit<DBTraceInstructi
|
||||
private static final byte FLOWOVERRIDE_CLEAR_MASK = ~FLOWOVERRIDE_SET_MASK;
|
||||
private static final int FLOWOVERRIDE_SHIFT = 1;
|
||||
|
||||
static final String PLATFORM_COLUMN_NAME = "Platform";
|
||||
static final String PROTOTYPE_COLUMN_NAME = "Prototype";
|
||||
static final String FLAGS_COLUMN_NAME = "Flags";
|
||||
|
||||
@DBAnnotatedColumn(PLATFORM_COLUMN_NAME)
|
||||
static DBObjectColumn PLATFORM_COLUMN;
|
||||
@DBAnnotatedColumn(PROTOTYPE_COLUMN_NAME)
|
||||
static DBObjectColumn PROTOTYPE_COLUMN;
|
||||
@DBAnnotatedColumn(FLAGS_COLUMN_NAME)
|
||||
@ -100,6 +105,8 @@ public class DBTraceInstruction extends AbstractDBTraceCodeUnit<DBTraceInstructi
|
||||
}
|
||||
}
|
||||
|
||||
@DBAnnotatedField(column = PLATFORM_COLUMN_NAME)
|
||||
private int platformKey;
|
||||
@DBAnnotatedField(column = PROTOTYPE_COLUMN_NAME)
|
||||
private int prototypeKey;
|
||||
@DBAnnotatedField(column = FLAGS_COLUMN_NAME)
|
||||
@ -111,7 +118,7 @@ public class DBTraceInstruction extends AbstractDBTraceCodeUnit<DBTraceInstructi
|
||||
protected FlowOverride flowOverride;
|
||||
|
||||
protected ParserContext parserContext;
|
||||
protected DBTraceGuestLanguage guest;
|
||||
protected DBTraceGuestPlatform guest;
|
||||
protected InstructionContext instructionContext;
|
||||
|
||||
public DBTraceInstruction(DBTraceCodeSpace space,
|
||||
@ -120,17 +127,32 @@ public class DBTraceInstruction extends AbstractDBTraceCodeUnit<DBTraceInstructi
|
||||
super(space, tree, store, record);
|
||||
}
|
||||
|
||||
protected void doSetGuestMapping() {
|
||||
if (Objects.equals(prototype.getLanguage(), space.baseLanguage)) {
|
||||
guest = null;
|
||||
protected void doSetGuestMapping(final DBTraceGuestPlatform guest) {
|
||||
this.guest = guest;
|
||||
if (guest == null) {
|
||||
instructionContext = this;
|
||||
}
|
||||
else {
|
||||
guest = space.trace.getLanguageManager().getGuestLanguage(prototype.getLanguage());
|
||||
instructionContext = new GuestInstructionContext();
|
||||
}
|
||||
}
|
||||
|
||||
protected void set(DBTraceGuestPlatform guest, InstructionPrototype prototype,
|
||||
ProcessorContextView context) {
|
||||
this.platformKey = (int) (guest == null ? -1 : guest.getKey());
|
||||
// NOTE: Using "this" for the MemBuffer seems a bit precarious.
|
||||
DBTraceGuestLanguage languageEntry = guest == null ? null : guest.getLanguageEntry();
|
||||
this.prototypeKey = (int) space.manager
|
||||
.findOrRecordPrototype(prototype, languageEntry, this, context)
|
||||
.getKey();
|
||||
this.flowOverride = FlowOverride.NONE; // flags field is already consistent
|
||||
update(PLATFORM_COLUMN, PROTOTYPE_COLUMN, FLAGS_COLUMN);
|
||||
|
||||
// TODO: Can there be more in this context than the context register???
|
||||
doSetGuestMapping(guest);
|
||||
this.prototype = prototype;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void fresh(boolean created) throws IOException {
|
||||
super.fresh(created);
|
||||
@ -138,9 +160,12 @@ public class DBTraceInstruction extends AbstractDBTraceCodeUnit<DBTraceInstructi
|
||||
// Wait for something to set prototype
|
||||
return;
|
||||
}
|
||||
guest = space.manager.platformManager.getPlatformByKey(platformKey);
|
||||
if (guest == null && platformKey != -1) {
|
||||
throw new IOException("Instruction table is corrupt. Missing platform: " + platformKey);
|
||||
}
|
||||
prototype = space.manager.getPrototypeByKey(prototypeKey);
|
||||
if (prototype == null) {
|
||||
// TODO: Better to just load a sentinel? Why bail on the whole thing?
|
||||
Msg.error(this,
|
||||
"Instruction table is corrupt for address " + getMinAddress() +
|
||||
". Missing prototype " + prototypeKey);
|
||||
@ -148,7 +173,7 @@ public class DBTraceInstruction extends AbstractDBTraceCodeUnit<DBTraceInstructi
|
||||
}
|
||||
flowOverride = FlowOverride.values()[(flags & FLOWOVERRIDE_SET_MASK) >> FLOWOVERRIDE_SHIFT];
|
||||
|
||||
doSetGuestMapping();
|
||||
doSetGuestMapping(guest);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -161,17 +186,6 @@ public class DBTraceInstruction extends AbstractDBTraceCodeUnit<DBTraceInstructi
|
||||
return this;
|
||||
}
|
||||
|
||||
protected void set(InstructionPrototype prototype, ProcessorContextView context) {
|
||||
// TODO: Can there be more in this context than the context register???
|
||||
this.prototype = prototype;
|
||||
// NOTE: Using "this" for the MemBuffer seems a bit precarious.
|
||||
this.prototypeKey = space.manager.findOrRecordPrototype(prototype, this, context);
|
||||
this.flowOverride = FlowOverride.NONE;
|
||||
update(PROTOTYPE_COLUMN, FLAGS_COLUMN);
|
||||
|
||||
doSetGuestMapping();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete() {
|
||||
try (LockHold hold = LockHold.lock(space.lock.writeLock())) {
|
||||
@ -191,6 +205,11 @@ public class DBTraceInstruction extends AbstractDBTraceCodeUnit<DBTraceInstructi
|
||||
space.instructions.unitSpanChanged(oldSpan, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TraceGuestPlatform getGuestPlatform() {
|
||||
return guest;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Language getLanguage() {
|
||||
return prototype.getLanguage();
|
||||
|
@ -24,6 +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.listing.TraceInstructionsView;
|
||||
import ghidra.util.LockHold;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
@ -49,18 +50,17 @@ public class DBTraceInstructionsMemoryView
|
||||
|
||||
@Override
|
||||
public DBTraceInstruction create(Range<Long> lifespan, Address address,
|
||||
InstructionPrototype prototype, ProcessorContextView context)
|
||||
throws CodeUnitInsertionException {
|
||||
TraceGuestPlatform platform, InstructionPrototype prototype,
|
||||
ProcessorContextView context) throws CodeUnitInsertionException {
|
||||
return delegateWrite(address.getAddressSpace(),
|
||||
m -> m.create(lifespan, address, prototype, context));
|
||||
m -> m.create(lifespan, address, platform, prototype, context));
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressSetView addInstructionSet(Range<Long> lifespan, InstructionSet instructionSet,
|
||||
boolean overwrite) {
|
||||
InstructionSet mappedSet =
|
||||
manager.getTrace().getLanguageManager().mapGuestInstructionAddressesToHost(
|
||||
instructionSet);
|
||||
public AddressSetView addInstructionSet(Range<Long> lifespan, TraceGuestPlatform platform,
|
||||
InstructionSet instructionSet, boolean overwrite) {
|
||||
InstructionSet mappedSet = manager.platformManager
|
||||
.mapGuestInstructionAddressesToHost(platform, instructionSet);
|
||||
|
||||
Map<AddressSpace, InstructionSet> breakDown = new HashMap<>();
|
||||
// TODO: I'm not sure the consequences of breaking an instruction set down.
|
||||
@ -74,8 +74,8 @@ public class DBTraceInstructionsMemoryView
|
||||
try (LockHold hold = LockHold.lock(manager.writeLock())) {
|
||||
for (Entry<AddressSpace, InstructionSet> entry : breakDown.entrySet()) {
|
||||
DBTraceInstructionsView instructionsView = getForSpace(entry.getKey(), true);
|
||||
result.add(
|
||||
instructionsView.addInstructionSet(lifespan, entry.getValue(), overwrite));
|
||||
result.add(instructionsView.addInstructionSet(lifespan, platform, entry.getValue(),
|
||||
overwrite));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@ -29,9 +29,11 @@ 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.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.listing.TraceInstruction;
|
||||
import ghidra.trace.model.listing.TraceInstructionsView;
|
||||
@ -50,6 +52,8 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView
|
||||
|
||||
protected class InstructionBlockAdder {
|
||||
private final Set<Address> skipDelaySlots;
|
||||
private final Range<Long> lifespan;
|
||||
private final DBTraceGuestPlatform platform;
|
||||
private final InstructionBlock block;
|
||||
private final Address errorAddress;
|
||||
private final InstructionError conflict;
|
||||
@ -57,19 +61,22 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView
|
||||
|
||||
protected int count = 0;
|
||||
|
||||
private InstructionBlockAdder(Set<Address> skipDelaySlots, InstructionBlock block,
|
||||
Address errorAddress, InstructionError conflict, CodeUnit conflictCodeUnit) {
|
||||
private InstructionBlockAdder(Set<Address> skipDelaySlots, Range<Long> lifespan,
|
||||
DBTraceGuestPlatform platform, InstructionBlock block, Address errorAddress,
|
||||
InstructionError conflict, CodeUnit conflictCodeUnit) {
|
||||
this.skipDelaySlots = skipDelaySlots;
|
||||
this.lifespan = lifespan;
|
||||
this.platform = platform;
|
||||
this.block = block;
|
||||
this.errorAddress = errorAddress;
|
||||
this.conflict = conflict;
|
||||
this.conflictCodeUnit = conflictCodeUnit;
|
||||
}
|
||||
|
||||
protected Instruction doCreateInstruction(Range<Long> lifespan, Address address,
|
||||
protected Instruction doCreateInstruction(Address address,
|
||||
InstructionPrototype prototype, Instruction protoInstr) {
|
||||
try {
|
||||
Instruction created = doCreate(lifespan, address, prototype, protoInstr);
|
||||
Instruction created = doCreate(lifespan, address, platform, prototype, protoInstr);
|
||||
// copy override settings to replacement instruction
|
||||
if (protoInstr.isFallThroughOverridden()) {
|
||||
created.setFallThrough(protoInstr.getFallThrough());
|
||||
@ -97,8 +104,7 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView
|
||||
* @param areDelaySlots
|
||||
* @return
|
||||
*/
|
||||
protected Instruction doAddInstructions(Range<Long> lifespan, Iterator<Instruction> it,
|
||||
boolean areDelaySlots) {
|
||||
protected Instruction doAddInstructions(Iterator<Instruction> it, boolean areDelaySlots) {
|
||||
Instruction lastInstruction = null;
|
||||
while (it.hasNext()) {
|
||||
Instruction protoInstr = it.next();
|
||||
@ -139,10 +145,10 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView
|
||||
delayed.push(it.next());
|
||||
}
|
||||
lastInstruction = replaceIfNotNull(lastInstruction,
|
||||
doAddInstructions(lifespan, delayed.iterator(), true));
|
||||
doAddInstructions(delayed.iterator(), true));
|
||||
}
|
||||
lastInstruction =
|
||||
doCreateInstruction(lifespan, startAddress, prototype, protoInstr);
|
||||
doCreateInstruction(startAddress, prototype, protoInstr);
|
||||
}
|
||||
if (errorAddress != null && conflictCodeUnit == null &&
|
||||
errorAddress.compareTo(startAddress) <= 0) {
|
||||
@ -179,9 +185,22 @@ 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,
|
||||
InstructionPrototype prototype, ProcessorContextView context)
|
||||
DBTraceGuestPlatform platform, InstructionPrototype prototype,
|
||||
ProcessorContextView context)
|
||||
throws CodeUnitInsertionException, AddressOverflowException {
|
||||
if (!languagesAgree(platform, prototype)) {
|
||||
throw new IllegalArgumentException("Platform and prototype disagree in language");
|
||||
}
|
||||
|
||||
Address endAddress = address.addNoWrap(prototype.getLength() - 1);
|
||||
AddressRangeImpl createdRange = new AddressRangeImpl(address, endAddress);
|
||||
|
||||
@ -213,7 +232,7 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView
|
||||
doSetContexts(tasr, prototype.getLanguage(), context);
|
||||
|
||||
DBTraceInstruction created = space.instructionMapSpace.put(tasr, null);
|
||||
created.set(prototype, context);
|
||||
created.set(platform, prototype, context);
|
||||
|
||||
cacheForContaining.notifyNewEntry(lifespan, createdRange, created);
|
||||
cacheForSequence.notifyNewEntry(lifespan, createdRange, created);
|
||||
@ -227,10 +246,13 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView
|
||||
|
||||
@Override
|
||||
public DBTraceInstruction create(Range<Long> lifespan, Address address,
|
||||
InstructionPrototype prototype, ProcessorContextView context)
|
||||
TraceGuestPlatform platform, InstructionPrototype prototype,
|
||||
ProcessorContextView context)
|
||||
throws CodeUnitInsertionException {
|
||||
DBTraceGuestPlatform dbPlatform = space.manager.platformManager.assertMine(platform);
|
||||
try (LockHold hold = LockHold.lock(space.lock.writeLock())) {
|
||||
DBTraceInstruction created = doCreate(lifespan, address, prototype, context);
|
||||
DBTraceInstruction created =
|
||||
doCreate(lifespan, address, dbPlatform, prototype, context);
|
||||
space.trace.setChanged(new TraceChangeRecord<>(TraceCodeChangeType.ADDED,
|
||||
space, created, created));
|
||||
return created;
|
||||
@ -254,23 +276,26 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView
|
||||
OverlappingObjectIterator.CODE_UNIT, existing, OverlappingObjectIterator.CODE_UNIT);
|
||||
}
|
||||
|
||||
protected InstructionBlockAdder startAddingBlock(long startSnap, Set<Address> skipDelaySlots,
|
||||
InstructionBlock block) {
|
||||
protected InstructionBlockAdder startAddingBlock(Range<Long> lifespan,
|
||||
Set<Address> skipDelaySlots, DBTraceGuestPlatform platform, InstructionBlock block) {
|
||||
InstructionError conflict = block.getInstructionConflict();
|
||||
if (conflict == null) {
|
||||
return new InstructionBlockAdder(skipDelaySlots, block, null, null, null);
|
||||
return new InstructionBlockAdder(skipDelaySlots, lifespan, platform, block, null, null,
|
||||
null);
|
||||
}
|
||||
Address errorAddress = conflict.getInstructionAddress();
|
||||
if (errorAddress == null) {
|
||||
return null; // The whole block is considered in error
|
||||
}
|
||||
if (!conflict.getInstructionErrorType().isConflict) {
|
||||
return new InstructionBlockAdder(skipDelaySlots, block, errorAddress, conflict, null);
|
||||
return new InstructionBlockAdder(skipDelaySlots, lifespan, platform, block,
|
||||
errorAddress, conflict, null);
|
||||
}
|
||||
long startSnap = DBTraceUtils.lowerEndpoint(lifespan);
|
||||
CodeUnit conflictCodeUnit =
|
||||
space.definedUnits.getAt(startSnap, conflict.getConflictAddress());
|
||||
return new InstructionBlockAdder(skipDelaySlots, block, errorAddress, conflict,
|
||||
conflictCodeUnit);
|
||||
return new InstructionBlockAdder(skipDelaySlots, lifespan, platform, block, errorAddress,
|
||||
conflict, conflictCodeUnit);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -359,8 +384,9 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressSetView addInstructionSet(Range<Long> lifespan, InstructionSet instructionSet,
|
||||
boolean overwrite) {
|
||||
public AddressSetView addInstructionSet(Range<Long> lifespan, TraceGuestPlatform platform,
|
||||
InstructionSet instructionSet, boolean overwrite) {
|
||||
DBTraceGuestPlatform dbPlatform = space.manager.platformManager.assertMine(platform);
|
||||
// NOTE: Partly derived from CodeManager#addInstructions()
|
||||
// Attempted to factor more fluently
|
||||
AddressSet result = new AddressSet();
|
||||
@ -378,12 +404,12 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView
|
||||
|
||||
// Add blocks
|
||||
for (InstructionBlock block : instructionSet) {
|
||||
InstructionBlockAdder adder = startAddingBlock(startSnap, skipDelaySlots, block);
|
||||
InstructionBlockAdder adder =
|
||||
startAddingBlock(lifespan, skipDelaySlots, dbPlatform, block);
|
||||
if (adder == null) {
|
||||
continue;
|
||||
}
|
||||
Instruction lastInstruction =
|
||||
adder.doAddInstructions(lifespan, block.iterator(), false);
|
||||
Instruction lastInstruction = adder.doAddInstructions(block.iterator(), false);
|
||||
block.setInstructionsAddedCount(adder.count);
|
||||
if (lastInstruction != null) {
|
||||
Address maxAddress = DBTraceCodeManager.instructionMax(lastInstruction, true);
|
||||
|
@ -33,6 +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.listing.TraceData;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.trace.util.TraceAddressSpace;
|
||||
@ -80,6 +81,11 @@ public class UndefinedDBTraceData implements DBTraceDataAdapter, DBTraceSpaceKey
|
||||
return trace.getBaseLanguage();
|
||||
}
|
||||
|
||||
@Override
|
||||
public TraceGuestPlatform getGuestPlatform() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressRange getRange() {
|
||||
// TODO: Cache this?
|
||||
|
@ -725,27 +725,25 @@ 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?
|
||||
try (LockHold hold = program.trace.lockWrite()) {
|
||||
return codeOperations.instructions()
|
||||
.create(Range.atLeast(program.snap), addr,
|
||||
prototype, context);
|
||||
}
|
||||
// TODO: Per-platform views?
|
||||
return codeOperations.instructions()
|
||||
.create(Range.atLeast(program.snap), addr, null, prototype, context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressSetView addInstructions(InstructionSet instructionSet, boolean overwrite)
|
||||
throws CodeUnitInsertionException {
|
||||
// TODO: Per-platform views?
|
||||
return codeOperations.instructions()
|
||||
.addInstructionSet(Range.atLeast(program.snap),
|
||||
instructionSet, overwrite);
|
||||
.addInstructionSet(Range.atLeast(program.snap), null, instructionSet,
|
||||
overwrite);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Data createData(Address addr, DataType dataType, int length)
|
||||
throws CodeUnitInsertionException {
|
||||
return codeOperations.definedData()
|
||||
.create(Range.atLeast(program.snap), addr, dataType,
|
||||
length);
|
||||
.create(Range.atLeast(program.snap), addr, dataType, length);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -181,10 +181,16 @@ public class DBTraceObjectStack implements TraceObjectStack, DBTraceObjectInterf
|
||||
protected TraceStackFrame doGetFrame(int level) {
|
||||
TargetObjectSchema schema = object.getTargetSchema();
|
||||
PathPredicates matcher = schema.searchFor(TargetStackFrame.class, true);
|
||||
matcher = matcher.applyKeys(PathUtils.makeIndex(level));
|
||||
return object.getSuccessors(computeSpan(), matcher)
|
||||
PathPredicates decMatcher = matcher.applyKeys(PathUtils.makeIndex(level));
|
||||
PathPredicates hexMatcher = matcher.applyKeys("0x" + Integer.toHexString(level));
|
||||
Range<Long> span = computeSpan();
|
||||
return object.getSuccessors(span, decMatcher)
|
||||
.findAny()
|
||||
.map(p -> p.getDestination(object).queryInterface(TraceObjectStackFrame.class))
|
||||
.or(() -> object.getSuccessors(span, hexMatcher)
|
||||
.findAny()
|
||||
.map(p -> p.getDestination(object)
|
||||
.queryInterface(TraceObjectStackFrame.class)))
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
|
@ -65,7 +65,7 @@ public class DBTraceObjectStackFrame implements TraceObjectStackFrame, DBTraceOb
|
||||
}
|
||||
String index = PathUtils.parseIndex(k);
|
||||
try {
|
||||
return Integer.parseInt(index, 10); // TODO: How to know the radix?
|
||||
return Integer.decode(index);
|
||||
// TODO: Perhaps just have an attribute that is its level?
|
||||
}
|
||||
catch (NumberFormatException e) {
|
||||
|
@ -353,6 +353,32 @@ public class DBTraceObject extends DBAnnotatedObject implements TraceObject {
|
||||
}
|
||||
}
|
||||
|
||||
protected void collectNonRangedAttributes(List<? super DBTraceObjectValue> result) {
|
||||
for (DBTraceObjectValue val : manager.valuesByTriple
|
||||
.sub(new PrimaryTriple(this, "", Long.MIN_VALUE), true,
|
||||
new PrimaryTriple(this, "[", Long.MIN_VALUE), false)
|
||||
.values()) {
|
||||
result.add(val);
|
||||
}
|
||||
for (DBTraceObjectValue val : manager.valuesByTriple
|
||||
.tail(new PrimaryTriple(this, "\\", Long.MIN_VALUE), true)
|
||||
.values()) {
|
||||
if (val.getParent() != this) {
|
||||
break;
|
||||
}
|
||||
result.add(val);
|
||||
}
|
||||
}
|
||||
|
||||
protected void collectNonRangedElements(List<? super DBTraceObjectValue> result) {
|
||||
for (DBTraceObjectValue val : manager.valuesByTriple
|
||||
.sub(new PrimaryTriple(this, "[", Long.MIN_VALUE), true,
|
||||
new PrimaryTriple(this, "\\", Long.MIN_VALUE), false)
|
||||
.values()) {
|
||||
result.add(val);
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean doHasAnyNonRangedValues() {
|
||||
for (DBTraceObjectValue val : manager.valuesByTriple
|
||||
.tail(new PrimaryTriple(this, "", Long.MIN_VALUE), true)
|
||||
@ -377,6 +403,38 @@ public class DBTraceObject extends DBAnnotatedObject implements TraceObject {
|
||||
}
|
||||
}
|
||||
|
||||
protected void collectRangedAttributes(
|
||||
Collection<? super DBTraceObjectAddressRangeValue> result) {
|
||||
for (DBTraceAddressSnapRangePropertyMapSpace<DBTraceObjectAddressRangeValue, ?> space //
|
||||
: manager.rangeValueMap.getActiveMemorySpaces()) {
|
||||
for (DBTraceObjectAddressRangeValue val : space.values()) {
|
||||
if (val.getParent() != this) {
|
||||
continue;
|
||||
}
|
||||
if (!PathUtils.isName(val.getEntryKey())) {
|
||||
continue;
|
||||
}
|
||||
result.add(val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void collectRangedElements(
|
||||
Collection<? super DBTraceObjectAddressRangeValue> result) {
|
||||
for (DBTraceAddressSnapRangePropertyMapSpace<DBTraceObjectAddressRangeValue, ?> space //
|
||||
: manager.rangeValueMap.getActiveMemorySpaces()) {
|
||||
for (DBTraceObjectAddressRangeValue val : space.values()) {
|
||||
if (val.getParent() != this) {
|
||||
continue;
|
||||
}
|
||||
if (!PathUtils.isIndex(val.getEntryKey())) {
|
||||
continue;
|
||||
}
|
||||
result.add(val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean doHasAnyRangedValues() {
|
||||
for (DBTraceAddressSnapRangePropertyMapSpace<DBTraceObjectAddressRangeValue, ?> space //
|
||||
: manager.rangeValueMap.getActiveMemorySpaces()) {
|
||||
@ -415,45 +473,29 @@ public class DBTraceObject extends DBAnnotatedObject implements TraceObject {
|
||||
}
|
||||
}
|
||||
|
||||
protected Collection<? extends DBTraceObjectValue> doGetElements() {
|
||||
List<DBTraceObjectValue> result = new ArrayList<>();
|
||||
for (DBTraceObjectValue val : manager.valuesByTriple
|
||||
.sub(new PrimaryTriple(this, "[", Long.MIN_VALUE), true,
|
||||
new PrimaryTriple(this, "\\", Long.MIN_VALUE), false)
|
||||
.values()) {
|
||||
result.add(val);
|
||||
}
|
||||
protected Collection<? extends InternalTraceObjectValue> doGetElements() {
|
||||
List<InternalTraceObjectValue> result = new ArrayList<>();
|
||||
collectNonRangedElements(result);
|
||||
collectRangedElements(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<? extends DBTraceObjectValue> getElements() {
|
||||
public Collection<? extends InternalTraceObjectValue> getElements() {
|
||||
try (LockHold hold = manager.trace.lockRead()) {
|
||||
return doGetElements();
|
||||
}
|
||||
}
|
||||
|
||||
protected Collection<? extends DBTraceObjectValue> doGetAttributes() {
|
||||
List<DBTraceObjectValue> result = new ArrayList<>();
|
||||
for (DBTraceObjectValue val : manager.valuesByTriple
|
||||
.sub(new PrimaryTriple(this, "", Long.MIN_VALUE), true,
|
||||
new PrimaryTriple(this, "[", Long.MIN_VALUE), false)
|
||||
.values()) {
|
||||
result.add(val);
|
||||
}
|
||||
for (DBTraceObjectValue val : manager.valuesByTriple
|
||||
.tail(new PrimaryTriple(this, "\\", Long.MIN_VALUE), true)
|
||||
.values()) {
|
||||
if (val.getParent() != this) {
|
||||
break;
|
||||
}
|
||||
result.add(val);
|
||||
}
|
||||
protected Collection<? extends InternalTraceObjectValue> doGetAttributes() {
|
||||
List<InternalTraceObjectValue> result = new ArrayList<>();
|
||||
collectNonRangedAttributes(result);
|
||||
collectRangedAttributes(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<? extends DBTraceObjectValue> getAttributes() {
|
||||
public Collection<? extends InternalTraceObjectValue> getAttributes() {
|
||||
try (LockHold hold = manager.trace.lockRead()) {
|
||||
return doGetAttributes();
|
||||
}
|
||||
@ -525,7 +567,7 @@ public class DBTraceObject extends DBAnnotatedObject implements TraceObject {
|
||||
return floor;
|
||||
}
|
||||
|
||||
protected Stream<DBTraceObjectValue> doGetNonRangedValues(Range<Long> span, String key,
|
||||
protected Stream<DBTraceObjectValue> doGetOrderedNonRangedValues(Range<Long> span, String key,
|
||||
boolean forward) {
|
||||
DBCachedObjectIndex<PrimaryTriple, DBTraceObjectValue> sub = manager.valuesByTriple.sub(
|
||||
new PrimaryTriple(this, key, DBTraceUtils.lowerEndpoint(span)), true,
|
||||
@ -552,7 +594,7 @@ public class DBTraceObject extends DBAnnotatedObject implements TraceObject {
|
||||
return null;
|
||||
}
|
||||
|
||||
protected Stream<DBTraceObjectAddressRangeValue> doGetRangedValues(Range<Long> span,
|
||||
protected Stream<DBTraceObjectAddressRangeValue> doGetOrderedRangedValues(Range<Long> span,
|
||||
String key, boolean forward) {
|
||||
Rectangle2DDirection dir = forward
|
||||
? Rectangle2DDirection.BOTTOMMOST
|
||||
@ -590,8 +632,8 @@ public class DBTraceObject extends DBAnnotatedObject implements TraceObject {
|
||||
|
||||
protected Stream<InternalTraceObjectValue> doGetOrderedValues(Range<Long> span, String key,
|
||||
boolean forward) {
|
||||
Stream<DBTraceObjectValue> nrVals = doGetNonRangedValues(span, key, forward);
|
||||
Stream<DBTraceObjectAddressRangeValue> rVals = doGetRangedValues(span, key, forward);
|
||||
Stream<DBTraceObjectValue> nrVals = doGetOrderedNonRangedValues(span, key, forward);
|
||||
Stream<DBTraceObjectAddressRangeValue> rVals = doGetOrderedRangedValues(span, key, forward);
|
||||
Comparator<Long> order = forward ? Comparator.naturalOrder() : Comparator.reverseOrder();
|
||||
Comparator<InternalTraceObjectValue> comparator =
|
||||
Comparator.comparing(v -> v.getMinSnap(), order);
|
||||
@ -883,9 +925,14 @@ public class DBTraceObject extends DBAnnotatedObject implements TraceObject {
|
||||
manager.trace.setChanged(rec);
|
||||
for (TraceObjectInterface iface : ifaces.values()) {
|
||||
DBTraceObjectInterface dbIface = (DBTraceObjectInterface) iface;
|
||||
TraceChangeRecord<?, ?> evt = dbIface.translateEvent(rec);
|
||||
if (evt != null) {
|
||||
manager.trace.setChanged(evt);
|
||||
try {
|
||||
TraceChangeRecord<?, ?> evt = dbIface.translateEvent(rec);
|
||||
if (evt != null) {
|
||||
manager.trace.setChanged(evt);
|
||||
}
|
||||
}
|
||||
catch (Throwable t) {
|
||||
Msg.error(this, "Error while translating event " + rec + " for interface " + iface);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -50,7 +50,7 @@ public class InternalOrderedSuccessorsVisitor implements SpanIntersectingVisitor
|
||||
// Singleton path, so if I match, no successor can
|
||||
return VisitResult.INCLUDE_FINISH;
|
||||
}
|
||||
if (value.getChildOrNull() == null || predicates.successorCouldMatch(keyList, true)) {
|
||||
if (value.getChildOrNull() == null || !predicates.successorCouldMatch(keyList, true)) {
|
||||
return VisitResult.EXCLUDE_FINISH;
|
||||
}
|
||||
return VisitResult.EXCLUDE_CONTINUE;
|
||||
|
@ -60,7 +60,7 @@ public class InternalSuccessorsRelativeVisitor implements SpanIntersectingVisito
|
||||
return Stream.empty();
|
||||
}
|
||||
|
||||
Stream<? extends DBTraceObjectValue> attrStream;
|
||||
Stream<? extends InternalTraceObjectValue> attrStream;
|
||||
if (nextKeys.contains("")) {
|
||||
attrStream = object.doGetAttributes()
|
||||
.stream()
|
||||
@ -70,7 +70,7 @@ public class InternalSuccessorsRelativeVisitor implements SpanIntersectingVisito
|
||||
attrStream = Stream.empty();
|
||||
}
|
||||
|
||||
Stream<? extends DBTraceObjectValue> elemStream;
|
||||
Stream<? extends InternalTraceObjectValue> elemStream;
|
||||
if (nextKeys.contains("[]")) {
|
||||
elemStream = object.doGetElements()
|
||||
.stream()
|
||||
|
@ -32,7 +32,7 @@ import ghidra.trace.model.breakpoint.TraceBreakpoint;
|
||||
import ghidra.trace.model.breakpoint.TraceBreakpointManager;
|
||||
import ghidra.trace.model.context.TraceRegisterContextManager;
|
||||
import ghidra.trace.model.data.TraceBasedDataTypeManager;
|
||||
import ghidra.trace.model.language.TraceLanguageManager;
|
||||
import ghidra.trace.model.guest.TracePlatformManager;
|
||||
import ghidra.trace.model.listing.*;
|
||||
import ghidra.trace.model.memory.*;
|
||||
import ghidra.trace.model.modules.*;
|
||||
@ -399,7 +399,7 @@ public interface Trace extends DataTypeManagerDomainObject {
|
||||
|
||||
TraceEquateManager getEquateManager();
|
||||
|
||||
TraceLanguageManager getLanguageManager();
|
||||
TracePlatformManager getPlatformManager();
|
||||
|
||||
TraceMemoryManager getMemoryManager();
|
||||
|
||||
|
@ -0,0 +1,132 @@
|
||||
/* ###
|
||||
* 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;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
public interface TraceGuestPlatform {
|
||||
/**
|
||||
* Get the trace
|
||||
*
|
||||
* @return the trace
|
||||
*/
|
||||
Trace getTrace();
|
||||
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
* @param hostStart the starting host address (mapped to guestStart)
|
||||
* @param guestStart the starting guest address (mapped to hostStart)
|
||||
* @param length the length of the range to map
|
||||
* @return the mapped range
|
||||
* @throws AddressOverflowException if length is too long for either start
|
||||
*/
|
||||
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
|
||||
*/
|
||||
void delete(TaskMonitor monitor) throws CancelledException;
|
||||
}
|
@ -13,20 +13,23 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.trace.model.language;
|
||||
package ghidra.trace.model.guest;
|
||||
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressRange;
|
||||
import ghidra.program.model.lang.CompilerSpec;
|
||||
import ghidra.program.model.lang.Language;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
public interface TraceGuestLanguageMappedRange {
|
||||
public interface TraceGuestPlatformMappedRange {
|
||||
Language getHostLanguage();
|
||||
|
||||
CompilerSpec getHostCompilerSpec();
|
||||
|
||||
AddressRange getHostRange();
|
||||
|
||||
Language getGuestLanguage();
|
||||
TraceGuestPlatform getGuestPlatform();
|
||||
|
||||
AddressRange getGuestRange();
|
||||
|
@ -0,0 +1,74 @@
|
||||
/* ###
|
||||
* 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 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.
|
||||
*
|
||||
* <p>
|
||||
* TODO: Allow the placement of data units with alternative data organization.
|
||||
*/
|
||||
public interface TracePlatformManager {
|
||||
/**
|
||||
* Get the base language of the trace
|
||||
*
|
||||
* @return the language
|
||||
*/
|
||||
Language getBaseLanguage();
|
||||
|
||||
/**
|
||||
* Get the base compiler spec of the trace
|
||||
*
|
||||
* @return the compiler spec
|
||||
*/
|
||||
CompilerSpec getBaseCompilerSpec();
|
||||
|
||||
/**
|
||||
* Add a guest platform
|
||||
*
|
||||
* @param compilerSpec the compiler spec, which cannot be the base compiler spec
|
||||
* @return the new platform
|
||||
*/
|
||||
TraceGuestPlatform addGuestPlatform(CompilerSpec compilerSpec);
|
||||
|
||||
/**
|
||||
* Get the guest platform for the given compiler spec
|
||||
*
|
||||
* @param compilerSpec the compiler spec. For the base compiler spec, this will return null.
|
||||
* @return the platform, if found, or null
|
||||
*/
|
||||
TraceGuestPlatform getGuestPlatform(CompilerSpec compilerSpec);
|
||||
|
||||
/**
|
||||
* Get or add a platform for the given compiler spec
|
||||
*
|
||||
* @param compilerSpec the compiler spec
|
||||
* @return the new or existing platform, or null if compiler spec is the base compiler spec
|
||||
*/
|
||||
TraceGuestPlatform getOrAddGuestPlatform(CompilerSpec compilerSpec);
|
||||
|
||||
/**
|
||||
* Get all guest platforms
|
||||
*
|
||||
* @return the collection of platforms
|
||||
*/
|
||||
Collection<TraceGuestPlatform> getGuestPlatforms();
|
||||
}
|
@ -1,71 +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.trace.model.language;
|
||||
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.lang.InstructionSet;
|
||||
import ghidra.program.model.lang.Language;
|
||||
import ghidra.program.model.mem.MemBuffer;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
public interface TraceGuestLanguage {
|
||||
Language getLanguage();
|
||||
|
||||
TraceGuestLanguageMappedRange addMappedRange(Address hostStart, Address guestStart, long length)
|
||||
throws AddressOverflowException;
|
||||
|
||||
AddressSetView getHostAddressSet();
|
||||
|
||||
AddressSetView getGuestAddressSet();
|
||||
|
||||
Address mapHostToGuest(Address hostAddress);
|
||||
|
||||
Address mapGuestToHost(Address guestAddress);
|
||||
|
||||
/**
|
||||
* Get a memory buffer which presents the host bytes in the guest address space
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* Instructions which do not mapped 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
|
||||
*/
|
||||
void delete(TaskMonitor monitor) throws CancelledException;
|
||||
}
|
@ -25,6 +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.program.TraceProgramView;
|
||||
import ghidra.trace.model.symbol.TraceReference;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
@ -42,6 +43,13 @@ public interface TraceCodeUnit extends CodeUnit {
|
||||
*/
|
||||
Trace getTrace();
|
||||
|
||||
/**
|
||||
* If the unit is for a guest platform, get it
|
||||
*
|
||||
* @return the guest platform, or null if it's for the host platform
|
||||
*/
|
||||
TraceGuestPlatform getGuestPlatform();
|
||||
|
||||
@Override
|
||||
TraceProgramView getProgram();
|
||||
|
||||
|
@ -21,21 +21,37 @@ 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;
|
||||
|
||||
public interface TraceInstructionsView extends TraceBaseDefinedUnitsView<TraceInstruction> {
|
||||
TraceInstruction create(Range<Long> lifespan, Address address, InstructionPrototype prototype,
|
||||
ProcessorContextView context) throws CodeUnitInsertionException;
|
||||
/**
|
||||
* Create an instruction
|
||||
*
|
||||
* @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 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,
|
||||
InstructionPrototype prototype, ProcessorContextView context)
|
||||
throws CodeUnitInsertionException;
|
||||
|
||||
/**
|
||||
* TODO
|
||||
* Create several instructions
|
||||
*
|
||||
* NOTE: Does not throw {@link CodeUnitInsertionException}. Conflicts are instead recorded in
|
||||
* the {@code instructionSet}
|
||||
* <p>
|
||||
* <b>NOTE:</b> This does not throw {@link CodeUnitInsertionException}. Conflicts are instead
|
||||
* recorded in the {@code instructionSet}.
|
||||
*
|
||||
* @param lifespan the lifespan for all instruction units
|
||||
* @param platform the optional guest platform, null for the host
|
||||
* @param instructionSet the set of instructions to add
|
||||
* @param overwrite {@code true} to replace conflicting instructions
|
||||
* @return the address set of instructions actually added
|
||||
* @param overwrite true to replace conflicting instructions
|
||||
* @return the (host) address set of instructions actually added
|
||||
*/
|
||||
AddressSetView addInstructionSet(Range<Long> lifespan, InstructionSet instructionSet,
|
||||
boolean overwrite);
|
||||
AddressSetView addInstructionSet(Range<Long> lifespan, TraceGuestPlatform platform,
|
||||
InstructionSet instructionSet, boolean overwrite);
|
||||
}
|
||||
|
@ -106,7 +106,7 @@ public class DefaultTraceTimeViewport implements TraceTimeViewport {
|
||||
* may need the DB's lock, esp., considering user callbacks, then it must <em>first</em> acquire
|
||||
* the DB lock.
|
||||
*/
|
||||
protected final List<Range<Long>> ordered = new ArrayList<>(List.of(Range.singleton(0L)));
|
||||
protected final List<Range<Long>> ordered = new ArrayList<>();
|
||||
protected final RangeSet<Long> spanSet = TreeRangeSet.create();
|
||||
protected final ForSnapshotsListener listener = new ForSnapshotsListener();
|
||||
protected final ListenerSet<Runnable> changeListeners = new ListenerSet<>(Runnable.class);
|
||||
@ -114,6 +114,10 @@ public class DefaultTraceTimeViewport implements TraceTimeViewport {
|
||||
protected long snap = 0;
|
||||
|
||||
public DefaultTraceTimeViewport(Trace trace) {
|
||||
Range<Long> zero = Range.singleton(0L);
|
||||
spanSet.add(zero);
|
||||
ordered.add(zero);
|
||||
|
||||
this.trace = trace;
|
||||
trace.addCloseListener(listener);
|
||||
trace.addListener(listener);
|
||||
|
@ -26,7 +26,8 @@ import java.nio.charset.Charset;
|
||||
import java.nio.charset.CharsetEncoder;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import com.google.common.collect.Range;
|
||||
|
||||
@ -45,13 +46,12 @@ import ghidra.program.model.symbol.SourceType;
|
||||
import ghidra.program.model.util.CodeUnitInsertionException;
|
||||
import ghidra.program.util.DefaultLanguageService;
|
||||
import ghidra.trace.database.bookmark.*;
|
||||
import ghidra.trace.database.language.DBTraceGuestLanguage;
|
||||
import ghidra.trace.database.listing.*;
|
||||
import ghidra.trace.database.memory.DBTraceMemoryManager;
|
||||
import ghidra.trace.database.symbol.DBTraceReference;
|
||||
import ghidra.trace.database.thread.DBTraceThreadManager;
|
||||
import ghidra.trace.model.*;
|
||||
import ghidra.trace.model.language.TraceGuestLanguage;
|
||||
import ghidra.trace.model.guest.TraceGuestPlatform;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.database.DBOpenMode;
|
||||
@ -102,7 +102,7 @@ public class ToyDBTraceBuilder implements AutoCloseable {
|
||||
return addr(language, offset);
|
||||
}
|
||||
|
||||
public Address addr(TraceGuestLanguage lang, long offset) {
|
||||
public Address addr(TraceGuestPlatform lang, long offset) {
|
||||
return lang.getLanguage().getDefaultSpace().getAddress(offset);
|
||||
}
|
||||
|
||||
@ -114,7 +114,7 @@ public class ToyDBTraceBuilder implements AutoCloseable {
|
||||
return data(language, offset);
|
||||
}
|
||||
|
||||
public Address data(TraceGuestLanguage lang, long offset) {
|
||||
public Address data(TraceGuestPlatform lang, long offset) {
|
||||
return data(lang.getLanguage(), offset);
|
||||
}
|
||||
|
||||
@ -150,11 +150,11 @@ public class ToyDBTraceBuilder implements AutoCloseable {
|
||||
return drng(language, start, end);
|
||||
}
|
||||
|
||||
public AddressRange range(TraceGuestLanguage lang, long start, long end) {
|
||||
public AddressRange range(TraceGuestPlatform lang, long start, long end) {
|
||||
return range(lang.getLanguage(), start, end);
|
||||
}
|
||||
|
||||
public AddressRange drng(TraceGuestLanguage lang, long start, long end) {
|
||||
public AddressRange drng(TraceGuestPlatform lang, long start, long end) {
|
||||
return drng(lang.getLanguage(), start, end);
|
||||
}
|
||||
|
||||
@ -246,9 +246,10 @@ public class ToyDBTraceBuilder implements AutoCloseable {
|
||||
}
|
||||
|
||||
public DBTraceInstruction addInstruction(long snap, Address start,
|
||||
@SuppressWarnings("hiding") Language language) throws CodeUnitInsertionException {
|
||||
TraceGuestPlatform guest) throws CodeUnitInsertionException {
|
||||
DBTraceMemoryManager memory = trace.getMemoryManager();
|
||||
DBTraceCodeManager code = trace.getCodeManager();
|
||||
Language language = guest == null ? this.language : guest.getLanguage();
|
||||
Disassembler dis = Disassembler.getDisassembler(language, language.getAddressFactory(),
|
||||
new ConsoleTaskMonitor(), msg -> Msg.info(this, "Listener: " + msg));
|
||||
RegisterValue defaultContextValue = trace.getRegisterContextManager()
|
||||
@ -256,27 +257,25 @@ public class ToyDBTraceBuilder implements AutoCloseable {
|
||||
.getDefaultDisassemblyContext();
|
||||
|
||||
MemBuffer memBuf;
|
||||
if (language == null || Objects.equals(this.language, language)) {
|
||||
if (guest == null) {
|
||||
memBuf = memory.getBufferAt(snap, start);
|
||||
}
|
||||
else {
|
||||
DBTraceGuestLanguage guest = trace.getLanguageManager().getGuestLanguage(language);
|
||||
memBuf = guest.getMappedMemBuffer(snap, guest.mapHostToGuest(start));
|
||||
}
|
||||
InstructionBlock block = dis.pseudoDisassembleBlock(memBuf, defaultContextValue, 1);
|
||||
Instruction pseudoIns = block.iterator().next();
|
||||
return code.instructions()
|
||||
.create(Range.atLeast(snap), start, pseudoIns.getPrototype(),
|
||||
pseudoIns);
|
||||
.create(Range.atLeast(snap), start, guest, pseudoIns.getPrototype(), pseudoIns);
|
||||
}
|
||||
|
||||
public DBTraceInstruction addInstruction(long snap, Address start,
|
||||
@SuppressWarnings("hiding") Language language, ByteBuffer buf)
|
||||
TraceGuestPlatform guest, ByteBuffer buf)
|
||||
throws CodeUnitInsertionException {
|
||||
int length = buf.remaining();
|
||||
DBTraceMemoryManager memory = trace.getMemoryManager();
|
||||
memory.putBytes(snap, start, buf);
|
||||
DBTraceInstruction instruction = addInstruction(snap, start, language);
|
||||
DBTraceInstruction instruction = addInstruction(snap, start, guest);
|
||||
assertEquals(length, instruction.getLength());
|
||||
return instruction;
|
||||
}
|
||||
@ -345,4 +344,9 @@ public class ToyDBTraceBuilder implements AutoCloseable {
|
||||
public Language getLanguage(String id) throws LanguageNotFoundException {
|
||||
return languageService.getLanguage(new LanguageID(id));
|
||||
}
|
||||
|
||||
public CompilerSpec getCompiler(String langID, String compID)
|
||||
throws CompilerSpecNotFoundException, LanguageNotFoundException {
|
||||
return getLanguage(langID).getCompilerSpecByID(new CompilerSpecID(compID));
|
||||
}
|
||||
}
|
||||
|
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.trace.database.language;
|
||||
package ghidra.trace.database.guest;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
@ -23,24 +23,21 @@ import java.util.*;
|
||||
|
||||
import org.junit.*;
|
||||
|
||||
import ghidra.program.model.address.AddressOverflowException;
|
||||
import ghidra.program.model.lang.LanguageNotFoundException;
|
||||
import ghidra.test.AbstractGhidraHeadlessIntegrationTest;
|
||||
import ghidra.trace.database.ToyDBTraceBuilder;
|
||||
import ghidra.trace.model.language.TraceGuestLanguage;
|
||||
import ghidra.trace.database.guest.*;
|
||||
import ghidra.trace.model.guest.TraceGuestPlatform;
|
||||
import ghidra.util.database.UndoableTransaction;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.exception.VersionException;
|
||||
import ghidra.util.task.ConsoleTaskMonitor;
|
||||
|
||||
public class DBTraceLanguageManagerTest extends AbstractGhidraHeadlessIntegrationTest {
|
||||
public class DBTracePlatformManagerTest extends AbstractGhidraHeadlessIntegrationTest {
|
||||
protected ToyDBTraceBuilder b;
|
||||
protected DBTraceLanguageManager manager;
|
||||
protected DBTracePlatformManager manager;
|
||||
|
||||
@Before
|
||||
public void setUpLanguageManagerTest() throws IOException {
|
||||
b = new ToyDBTraceBuilder("Testing", "Toy:BE:64:default");
|
||||
manager = b.trace.getLanguageManager();
|
||||
manager = b.trace.getPlatformManager();
|
||||
}
|
||||
|
||||
@After
|
||||
@ -55,82 +52,108 @@ public class DBTraceLanguageManagerTest extends AbstractGhidraHeadlessIntegratio
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddGuestLanguage() throws LanguageNotFoundException {
|
||||
public void testGetBaseCompilerSpec() {
|
||||
assertEquals("default", manager.getBaseCompilerSpec().getCompilerSpecID().getIdAsString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddGuestPlatform() throws Throwable {
|
||||
try (UndoableTransaction tid = b.startTransaction()) {
|
||||
assertEquals(0, manager.languageStore.getRecordCount());
|
||||
manager.addGuestLanguage(b.getLanguage("x86:LE:32:default"));
|
||||
assertEquals(0, manager.platformStore.getRecordCount());
|
||||
manager.addGuestPlatform(b.getCompiler("x86:LE:32:default", "gcc"));
|
||||
assertEquals(1, manager.languageStore.getRecordCount());
|
||||
|
||||
try { // Cannot add base language as guest
|
||||
manager.addGuestLanguage(b.getLanguage("Toy:BE:64:default"));
|
||||
fail();
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
// pass
|
||||
}
|
||||
assertEquals(1, manager.platformStore.getRecordCount());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetGuestLanguages() throws LanguageNotFoundException {
|
||||
DBTraceGuestLanguage guest;
|
||||
public void testAddGuestPlatformHostCompilerErr() throws Throwable {
|
||||
try (UndoableTransaction tid = b.startTransaction()) {
|
||||
assertTrue(manager.getGuestLanguages().isEmpty());
|
||||
guest = manager.addGuestLanguage(b.getLanguage("x86:LE:32:default"));
|
||||
manager.addGuestPlatform(b.getLanguage("Toy:BE:64:default").getDefaultCompilerSpec());
|
||||
fail();
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
// pass, pending consistency check
|
||||
}
|
||||
|
||||
assertEquals(Set.of(guest), new HashSet<>(manager.getGuestLanguages()));
|
||||
assertEquals(0, manager.languageStore.getRecordCount());
|
||||
assertEquals(0, manager.platformStore.getRecordCount());
|
||||
assertTrue(manager.getGuestPlatforms().isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddLanguageThenUndo() throws IOException {
|
||||
public void testAddGuestPlatformHostLanguage() throws Throwable {
|
||||
try (UndoableTransaction tid = b.startTransaction()) {
|
||||
manager.addGuestLanguage(b.getLanguage("x86:LE:32:default"));
|
||||
assertEquals(0, manager.languageStore.getRecordCount());
|
||||
assertEquals(0, manager.platformStore.getRecordCount());
|
||||
manager.addGuestPlatform(b.getCompiler("Toy:BE:64:default", "long8"));
|
||||
assertEquals(0, manager.languageStore.getRecordCount());
|
||||
assertEquals(1, manager.platformStore.getRecordCount());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetGuestPlatforms() throws Throwable {
|
||||
DBTraceGuestPlatform guest;
|
||||
try (UndoableTransaction tid = b.startTransaction()) {
|
||||
assertTrue(manager.getGuestPlatforms().isEmpty());
|
||||
guest = manager.addGuestPlatform(b.getCompiler("x86:LE:32:default", "gcc"));
|
||||
}
|
||||
|
||||
assertEquals(Set.of(guest), new HashSet<>(manager.getGuestPlatforms()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddPlatformThenUndo() throws Throwable {
|
||||
try (UndoableTransaction tid = b.startTransaction()) {
|
||||
manager.addGuestPlatform(b.getCompiler("x86:LE:32:default", "gcc"));
|
||||
}
|
||||
|
||||
b.trace.undo();
|
||||
|
||||
assertTrue(manager.getGuestLanguages().isEmpty());
|
||||
assertTrue(manager.getGuestPlatforms().isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddLanguageThenSaveAndLoad()
|
||||
throws CancelledException, IOException, VersionException {
|
||||
public void testAddPlatformThenSaveAndLoad() throws Throwable {
|
||||
try (UndoableTransaction tid = b.startTransaction()) {
|
||||
manager.addGuestLanguage(b.getLanguage("x86:LE:32:default"));
|
||||
manager.addGuestPlatform(b.getCompiler("x86:LE:32:default", "gcc"));
|
||||
}
|
||||
|
||||
File saved = b.save();
|
||||
|
||||
try (ToyDBTraceBuilder r = new ToyDBTraceBuilder(saved)) {
|
||||
Collection<TraceGuestLanguage> guestLanguages =
|
||||
r.trace.getLanguageManager().getGuestLanguages();
|
||||
assertEquals(1, guestLanguages.size());
|
||||
Collection<TraceGuestPlatform> guestPlatforms =
|
||||
r.trace.getPlatformManager().getGuestPlatforms();
|
||||
assertEquals(1, guestPlatforms.size());
|
||||
TraceGuestPlatform platform = guestPlatforms.iterator().next();
|
||||
assertEquals("x86:LE:32:default",
|
||||
guestLanguages.iterator().next().getLanguage().getLanguageID().getIdAsString());
|
||||
platform.getLanguage().getLanguageID().getIdAsString());
|
||||
assertEquals("gcc", platform.getCompilerSpec().getCompilerSpecID().getIdAsString());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteGuestLanguage() throws LanguageNotFoundException, CancelledException {
|
||||
DBTraceGuestLanguage guest;
|
||||
public void testDeleteGuestPlatform() throws Throwable {
|
||||
DBTraceGuestPlatform guest;
|
||||
try (UndoableTransaction tid = b.startTransaction()) {
|
||||
guest = manager.addGuestLanguage(b.getLanguage("x86:LE:32:default"));
|
||||
guest = manager.addGuestPlatform(b.getCompiler("x86:LE:32:default", "gcc"));
|
||||
}
|
||||
|
||||
try (UndoableTransaction tid = b.startTransaction()) {
|
||||
guest.delete(new ConsoleTaskMonitor());
|
||||
}
|
||||
|
||||
assertEquals(0, manager.languageStore.getRecordCount());
|
||||
assertTrue(manager.entriesByLanguage.isEmpty());
|
||||
assertEquals(0, manager.platformStore.getRecordCount());
|
||||
assertTrue(manager.platformsByCompiler.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddMappedRange() throws LanguageNotFoundException, AddressOverflowException {
|
||||
public void testAddMappedRange() throws Throwable {
|
||||
try (UndoableTransaction tid = b.startTransaction()) {
|
||||
DBTraceGuestLanguage guest =
|
||||
manager.addGuestLanguage(b.getLanguage("x86:LE:32:default"));
|
||||
DBTraceGuestPlatform guest =
|
||||
manager.addGuestPlatform(b.getCompiler("x86:LE:32:default", "gcc"));
|
||||
|
||||
assertEquals(0, manager.rangeMappingStore.getRecordCount());
|
||||
guest.addMappedRange(b.addr(0x01000000), b.addr(guest, 0x02000000), 0x1000);
|
||||
@ -155,11 +178,10 @@ public class DBTraceLanguageManagerTest extends AbstractGhidraHeadlessIntegratio
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetHostAndGuestAddressSet()
|
||||
throws LanguageNotFoundException, AddressOverflowException {
|
||||
public void testGetHostAndGuestAddressSet() throws Throwable {
|
||||
try (UndoableTransaction tid = b.startTransaction()) {
|
||||
DBTraceGuestLanguage guest =
|
||||
manager.addGuestLanguage(b.getLanguage("x86:LE:32:default"));
|
||||
DBTraceGuestPlatform guest =
|
||||
manager.addGuestPlatform(b.getCompiler("x86:LE:32:default", "gcc"));
|
||||
assertEquals(b.set(), guest.getHostAddressSet());
|
||||
|
||||
guest.addMappedRange(b.addr(0x01000000), b.addr(guest, 0x02000000), 0x1000);
|
||||
@ -169,10 +191,10 @@ public class DBTraceLanguageManagerTest extends AbstractGhidraHeadlessIntegratio
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMapHostToGuest() throws LanguageNotFoundException, AddressOverflowException {
|
||||
public void testMapHostToGuest() throws Throwable {
|
||||
try (UndoableTransaction tid = b.startTransaction()) {
|
||||
DBTraceGuestLanguage guest =
|
||||
manager.addGuestLanguage(b.getLanguage("x86:LE:32:default"));
|
||||
DBTraceGuestPlatform guest =
|
||||
manager.addGuestPlatform(b.getCompiler("x86:LE:32:default", "gcc"));
|
||||
guest.addMappedRange(b.addr(0x01000000), b.addr(guest, 0x02000000), 0x1000);
|
||||
|
||||
assertNull(guest.mapHostToGuest(b.addr(0x00000000)));
|
||||
@ -185,10 +207,10 @@ public class DBTraceLanguageManagerTest extends AbstractGhidraHeadlessIntegratio
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMapGuestToHost() throws LanguageNotFoundException, AddressOverflowException {
|
||||
public void testMapGuestToHost() throws Throwable {
|
||||
try (UndoableTransaction tid = b.startTransaction()) {
|
||||
DBTraceGuestLanguage guest =
|
||||
manager.addGuestLanguage(b.getLanguage("x86:LE:32:default"));
|
||||
DBTraceGuestPlatform guest =
|
||||
manager.addGuestPlatform(b.getCompiler("x86:LE:32:default", "gcc"));
|
||||
guest.addMappedRange(b.addr(0x01000000), b.addr(guest, 0x02000000), 0x1000);
|
||||
|
||||
assertNull(guest.mapGuestToHost(b.addr(0x00000000)));
|
||||
@ -201,30 +223,28 @@ public class DBTraceLanguageManagerTest extends AbstractGhidraHeadlessIntegratio
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddMappedRangeThenSaveAndLoad()
|
||||
throws AddressOverflowException, CancelledException, IOException, VersionException {
|
||||
public void testAddMappedRangeThenSaveAndLoad() throws Throwable {
|
||||
try (UndoableTransaction tid = b.startTransaction()) {
|
||||
DBTraceGuestLanguage guest =
|
||||
manager.addGuestLanguage(b.getLanguage("x86:LE:32:default"));
|
||||
DBTraceGuestPlatform guest =
|
||||
manager.addGuestPlatform(b.getCompiler("x86:LE:32:default", "gcc"));
|
||||
guest.addMappedRange(b.addr(0x01000000), b.addr(guest, 0x02000000), 0x1000);
|
||||
}
|
||||
|
||||
File saved = b.save();
|
||||
|
||||
try (ToyDBTraceBuilder r = new ToyDBTraceBuilder(saved)) {
|
||||
TraceGuestLanguage guest =
|
||||
r.trace.getLanguageManager().getGuestLanguages().iterator().next();
|
||||
TraceGuestPlatform guest =
|
||||
r.trace.getPlatformManager().getGuestPlatforms().iterator().next();
|
||||
assertEquals(b.addr(guest, 0x02000800), guest.mapHostToGuest(b.addr(0x01000800)));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMappedRangeGetHostLanguage()
|
||||
throws LanguageNotFoundException, AddressOverflowException {
|
||||
public void testMappedRangeGetHostLanguage() throws Throwable {
|
||||
try (UndoableTransaction tid = b.startTransaction()) {
|
||||
DBTraceGuestLanguage guest =
|
||||
manager.addGuestLanguage(b.getLanguage("x86:LE:32:default"));
|
||||
DBTraceGuestLanguageMappedRange range =
|
||||
DBTraceGuestPlatform guest =
|
||||
manager.addGuestPlatform(b.getCompiler("x86:LE:32:default", "gcc"));
|
||||
DBTraceGuestPlatformMappedRange range =
|
||||
guest.addMappedRange(b.addr(0x01000000), b.addr(guest, 0x02000000), 0x1000);
|
||||
assertEquals("Toy:BE:64:default",
|
||||
range.getHostLanguage().getLanguageID().getIdAsString());
|
||||
@ -232,49 +252,44 @@ public class DBTraceLanguageManagerTest extends AbstractGhidraHeadlessIntegratio
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMappedRangeGetHostRange()
|
||||
throws LanguageNotFoundException, AddressOverflowException {
|
||||
public void testMappedRangeGetHostRange() throws Throwable {
|
||||
try (UndoableTransaction tid = b.startTransaction()) {
|
||||
DBTraceGuestLanguage guest =
|
||||
manager.addGuestLanguage(b.getLanguage("x86:LE:32:default"));
|
||||
DBTraceGuestLanguageMappedRange range =
|
||||
DBTraceGuestPlatform guest =
|
||||
manager.addGuestPlatform(b.getCompiler("x86:LE:32:default", "gcc"));
|
||||
DBTraceGuestPlatformMappedRange range =
|
||||
guest.addMappedRange(b.addr(0x01000000), b.addr(guest, 0x02000000), 0x1000);
|
||||
assertEquals(b.range(0x01000000, 0x01000fff), range.getHostRange());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMappedRangeGetGuestLanguage()
|
||||
throws LanguageNotFoundException, AddressOverflowException {
|
||||
public void testMappedRangeGetGuestPlatform() throws Throwable {
|
||||
try (UndoableTransaction tid = b.startTransaction()) {
|
||||
DBTraceGuestLanguage guest =
|
||||
manager.addGuestLanguage(b.getLanguage("x86:LE:32:default"));
|
||||
DBTraceGuestLanguageMappedRange range =
|
||||
DBTraceGuestPlatform guest =
|
||||
manager.addGuestPlatform(b.getCompiler("x86:LE:32:default", "gcc"));
|
||||
DBTraceGuestPlatformMappedRange range =
|
||||
guest.addMappedRange(b.addr(0x01000000), b.addr(guest, 0x02000000), 0x1000);
|
||||
assertEquals("x86:LE:32:default",
|
||||
range.getGuestLanguage().getLanguageID().getIdAsString());
|
||||
assertEquals(guest, range.getGuestPlatform());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMappedRangeGetGuestRange()
|
||||
throws LanguageNotFoundException, AddressOverflowException {
|
||||
public void testMappedRangeGetGuestRange() throws Throwable {
|
||||
try (UndoableTransaction tid = b.startTransaction()) {
|
||||
DBTraceGuestLanguage guest =
|
||||
manager.addGuestLanguage(b.getLanguage("x86:LE:32:default"));
|
||||
DBTraceGuestLanguageMappedRange range =
|
||||
DBTraceGuestPlatform guest =
|
||||
manager.addGuestPlatform(b.getCompiler("x86:LE:32:default", "gcc"));
|
||||
DBTraceGuestPlatformMappedRange range =
|
||||
guest.addMappedRange(b.addr(0x01000000), b.addr(guest, 0x02000000), 0x1000);
|
||||
assertEquals(b.range(guest, 0x02000000, 0x02000fff), range.getGuestRange());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteMappedRange()
|
||||
throws LanguageNotFoundException, AddressOverflowException, CancelledException {
|
||||
public void testDeleteMappedRange() throws Throwable {
|
||||
try (UndoableTransaction tid = b.startTransaction()) {
|
||||
DBTraceGuestLanguage guest =
|
||||
manager.addGuestLanguage(b.getLanguage("x86:LE:32:default"));
|
||||
DBTraceGuestLanguageMappedRange range =
|
||||
DBTraceGuestPlatform guest =
|
||||
manager.addGuestPlatform(b.getCompiler("x86:LE:32:default", "gcc"));
|
||||
DBTraceGuestPlatformMappedRange range =
|
||||
guest.addMappedRange(b.addr(0x01000000), b.addr(guest, 0x02000000), 0x1000);
|
||||
assertNotNull(guest.mapHostToGuest(b.addr(0x01000800))); // Sanity check
|
||||
assertNotNull(guest.mapGuestToHost(b.addr(guest, 0x02000800))); // Sanity check
|
||||
@ -291,12 +306,11 @@ public class DBTraceLanguageManagerTest extends AbstractGhidraHeadlessIntegratio
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteMappedRangeThenUndo()
|
||||
throws AddressOverflowException, IOException, CancelledException {
|
||||
DBTraceGuestLanguage guest;
|
||||
DBTraceGuestLanguageMappedRange range;
|
||||
public void testDeleteMappedRangeThenUndo() throws Throwable {
|
||||
DBTraceGuestPlatform guest;
|
||||
DBTraceGuestPlatformMappedRange range;
|
||||
try (UndoableTransaction tid = b.startTransaction()) {
|
||||
guest = manager.addGuestLanguage(b.getLanguage("x86:LE:32:default"));
|
||||
guest = manager.addGuestPlatform(b.getCompiler("x86:LE:32:default", "gcc"));
|
||||
range = guest.addMappedRange(b.addr(0x01000000), b.addr(guest, 0x02000000), 0x1000);
|
||||
assertNotNull(guest.mapHostToGuest(b.addr(0x01000800))); // Sanity check
|
||||
assertNotNull(guest.mapGuestToHost(b.addr(guest, 0x02000800))); // Sanity check
|
||||
@ -310,19 +324,17 @@ public class DBTraceLanguageManagerTest extends AbstractGhidraHeadlessIntegratio
|
||||
|
||||
b.trace.undo();
|
||||
|
||||
guest = manager.getGuestLanguage(b.getLanguage("x86:LE:32:default"));
|
||||
|
||||
guest = manager.getGuestPlatform(b.getCompiler("x86:LE:32:default", "gcc"));
|
||||
assertNotNull(guest.mapHostToGuest(b.addr(0x01000800)));
|
||||
assertNotNull(guest.mapGuestToHost(b.addr(guest, 0x02000800)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteGuestLanguageDeletesMappedRanges()
|
||||
throws LanguageNotFoundException, AddressOverflowException, CancelledException {
|
||||
public void testDeleteGuestPlatformDeletesMappedRanges() throws Throwable {
|
||||
// TODO: Check that it also deletes code units
|
||||
DBTraceGuestLanguage guest;
|
||||
DBTraceGuestPlatform guest;
|
||||
try (UndoableTransaction tid = b.startTransaction()) {
|
||||
guest = manager.addGuestLanguage(b.getLanguage("x86:LE:32:default"));
|
||||
guest = manager.addGuestPlatform(b.getCompiler("x86:LE:32:default", "gcc"));
|
||||
guest.addMappedRange(b.addr(0x01000000), b.addr(guest, 0x02000000), 0x1000);
|
||||
}
|
||||
|
||||
@ -333,12 +345,11 @@ public class DBTraceLanguageManagerTest extends AbstractGhidraHeadlessIntegratio
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteGuestLanguageThenUndo()
|
||||
throws AddressOverflowException, CancelledException, IOException {
|
||||
public void testDeleteGuestPlatformThenUndo() throws Throwable {
|
||||
// TODO: Check that it also deletes code units
|
||||
DBTraceGuestLanguage guest;
|
||||
DBTraceGuestPlatform guest;
|
||||
try (UndoableTransaction tid = b.startTransaction()) {
|
||||
guest = manager.addGuestLanguage(b.getLanguage("x86:LE:32:default"));
|
||||
guest = manager.addGuestPlatform(b.getCompiler("x86:LE:32:default", "gcc"));
|
||||
guest.addMappedRange(b.addr(0x01000000), b.addr(guest, 0x02000000), 0x1000);
|
||||
}
|
||||
|
||||
@ -348,7 +359,7 @@ public class DBTraceLanguageManagerTest extends AbstractGhidraHeadlessIntegratio
|
||||
|
||||
b.trace.undo();
|
||||
|
||||
guest = manager.getGuestLanguage(b.getLanguage("x86:LE:32:default"));
|
||||
guest = manager.getGuestPlatform(b.getCompiler("x86:LE:32:default", "gcc"));
|
||||
assertEquals(b.addr(guest, 0x02000800), guest.mapHostToGuest(b.addr(0x01000800)));
|
||||
}
|
||||
}
|
@ -40,7 +40,7 @@ import ghidra.program.model.util.CodeUnitInsertionException;
|
||||
import ghidra.test.AbstractGhidraHeadlessIntegrationTest;
|
||||
import ghidra.trace.database.ToyDBTraceBuilder;
|
||||
import ghidra.trace.database.context.DBTraceRegisterContextManager;
|
||||
import ghidra.trace.database.language.*;
|
||||
import ghidra.trace.database.guest.*;
|
||||
import ghidra.trace.model.ImmutableTraceAddressSnapRange;
|
||||
import ghidra.trace.model.listing.*;
|
||||
import ghidra.trace.model.stack.TraceStack;
|
||||
@ -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), b.language, b.buf(0xf4, 0));
|
||||
b.addInstruction(0, b.addr(0x4004), null, 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), b.language, b.buf(0xf4, 0));
|
||||
b.addInstruction(0, b.addr(0x4000), null, 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), b.language, b.buf(0xf4, 0));
|
||||
b.addInstruction(-10, b.addr(0x4000), null, b.buf(0xf4, 0));
|
||||
assertEquals(Range.closed(-10L, -6L), i4000.getLifespan());
|
||||
|
||||
TraceInstruction i4004 =
|
||||
b.addInstruction(-1, b.addr(0x4004), b.language, b.buf(0xf4, 0));
|
||||
b.addInstruction(-1, b.addr(0x4004), null, b.buf(0xf4, 0));
|
||||
assertEquals(Range.closed(-1L, -1L), i4004.getLifespan());
|
||||
|
||||
TraceInstruction i4008 =
|
||||
b.addInstruction(-10, b.addr(0x4008), b.language, b.buf(0xf4, 0));
|
||||
b.addInstruction(-10, b.addr(0x4008), null, 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), b.language, b.buf(0xf4, 0));
|
||||
b.addInstruction(0, b.addr(0x4000), null, 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), b.language, b.buf(0xf4, 0));
|
||||
b.addInstruction(0, b.addr(0x4000), null, 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), b.language);
|
||||
b.addInstruction(1, b.addr(0x4001), null);
|
||||
fail();
|
||||
}
|
||||
catch (CodeUnitInsertionException e) {
|
||||
// pass
|
||||
}
|
||||
|
||||
b.addInstruction(0, b.addr(0x4004), b.language, b.buf(0xf4, 0));
|
||||
b.addInstruction(0, b.addr(0x4004), null, 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), b.language);
|
||||
b.addInstruction(1, b.addr(0x4005), null);
|
||||
}
|
||||
catch (CodeUnitInsertionException e) {
|
||||
// pass
|
||||
@ -350,8 +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), b.language, b.buf(0xf4, 0));
|
||||
TraceInstruction i4005 = b.addInstruction(0, b.addr(0x4005), null, b.buf(0xf4, 0));
|
||||
i4005.setEndSnap(5);
|
||||
assertUndefinedFunc(v -> v.getAt(0, b.addr(0x4004)));
|
||||
assertInstructionFunc(i4005, v -> v.getAt(0, b.addr(0x4005)));
|
||||
@ -384,8 +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), b.language, b.buf(0xf4, 0));
|
||||
TraceInstruction i4005 = b.addInstruction(0, b.addr(0x4005), null, b.buf(0xf4, 0));
|
||||
i4005.setEndSnap(5);
|
||||
assertUndefinedFunc(v -> v.getContaining(0, b.addr(0x4004)));
|
||||
|
||||
@ -451,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), b.language, b.buf(0xf4, 0));
|
||||
i4008 = b.addInstruction(0, b.addr(0x4008), null, b.buf(0xf4, 0));
|
||||
i4008.setEndSnap(9);
|
||||
}
|
||||
|
||||
@ -593,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), b.language, b.buf(0xf4, 0));
|
||||
i4008 = b.addInstruction(0, b.addr(0x4008), null, b.buf(0xf4, 0));
|
||||
i4008.setEndSnap(9);
|
||||
}
|
||||
|
||||
@ -732,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), b.language, b.buf(0xf4, 0));
|
||||
i4008 = b.addInstruction(0, b.addr(0x4008), null, b.buf(0xf4, 0));
|
||||
i4008.setEndSnap(9);
|
||||
}
|
||||
|
||||
@ -872,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), b.language, b.buf(0xf4, 0));
|
||||
i4008 = b.addInstruction(0, b.addr(0x4008), null, b.buf(0xf4, 0));
|
||||
i4008.setEndSnap(9);
|
||||
}
|
||||
|
||||
@ -1086,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), b.language, b.buf(0xf4, 0));
|
||||
i4008 = b.addInstruction(0, b.addr(0x4008), null, b.buf(0xf4, 0));
|
||||
i4008.setEndSnap(9);
|
||||
}
|
||||
TraceData u3fff = manager.undefinedData().getAt(0, b.addr(0x3fff));
|
||||
@ -1139,7 +1137,7 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes
|
||||
|
||||
TraceInstruction iCodeMax;
|
||||
try (UndoableTransaction tid = b.startTransaction()) {
|
||||
iCodeMax = b.addInstruction(0, b.addr(-0x0002), b.language, b.buf(0xf4, 0));
|
||||
iCodeMax = b.addInstruction(0, b.addr(-0x0002), null, b.buf(0xf4, 0));
|
||||
}
|
||||
|
||||
assertEquals(iCodeMax, manager.codeUnits().getBefore(0, b.data(0x0000)));
|
||||
@ -1172,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), b.language, b.buf(0xf4, 0));
|
||||
iCodeMax = b.addInstruction(0, b.addr(-0x0002), null, b.buf(0xf4, 0));
|
||||
}
|
||||
TraceData uCodePre = manager.undefinedData().getAt(0, b.addr(-0x0003));
|
||||
assertUndefinedWithAddr(b.addr(-0x0003), uCodePre);
|
||||
@ -1211,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), b.language, b.buf(0xf4, 0));
|
||||
i4008 = b.addInstruction(0, b.addr(0x4008), null, b.buf(0xf4, 0));
|
||||
i4008.setEndSnap(9);
|
||||
}
|
||||
|
||||
@ -1346,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), b.language, b.buf(0xf4, 0));
|
||||
i4008 = b.addInstruction(0, b.addr(0x4008), null, b.buf(0xf4, 0));
|
||||
i4008.setEndSnap(9);
|
||||
}
|
||||
|
||||
@ -1416,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), b.language, b.buf(0xf4, 0));
|
||||
i4008 = b.addInstruction(0, b.addr(0x4008), null, b.buf(0xf4, 0));
|
||||
i4008.setEndSnap(9);
|
||||
}
|
||||
|
||||
@ -1491,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), b.language, b.buf(0xf4, 0));
|
||||
i4008 = b.addInstruction(0, b.addr(0x4008), null, b.buf(0xf4, 0));
|
||||
i4008.setEndSnap(9);
|
||||
}
|
||||
|
||||
@ -1575,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), b.language, b.buf(0xf4, 0));
|
||||
i4008 = b.addInstruction(0, b.addr(0x4008), null, b.buf(0xf4, 0));
|
||||
i4008.setEndSnap(9);
|
||||
}
|
||||
|
||||
@ -1674,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), b.language, b.buf(0xf4, 0));
|
||||
i4008 = b.addInstruction(0, b.addr(0x4008), null, b.buf(0xf4, 0));
|
||||
i4008.setEndSnap(9);
|
||||
|
||||
// Clear one of the data before a context space is created
|
||||
@ -1708,22 +1706,21 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore("Looks related to GP-479")
|
||||
public void testAddGuestInstructionThenRemoveAndDelete() throws AddressOverflowException,
|
||||
CodeUnitInsertionException, IOException, CancelledException {
|
||||
DBTraceLanguageManager langMan = b.trace.getLanguageManager();
|
||||
DBTracePlatformManager langMan = b.trace.getPlatformManager();
|
||||
Language x86 = getSLEIGH_X86_LANGUAGE();
|
||||
DBTraceGuestLanguage guest;
|
||||
DBTraceGuestLanguageMappedRange mappedRange;
|
||||
DBTraceGuestPlatform guest;
|
||||
DBTraceGuestPlatformMappedRange mappedRange;
|
||||
|
||||
TraceInstruction g4000;
|
||||
TraceInstruction i4001;
|
||||
TraceData d4003;
|
||||
try (UndoableTransaction tid = b.startTransaction()) {
|
||||
guest = langMan.addGuestLanguage(x86);
|
||||
guest = langMan.addGuestPlatform(x86.getDefaultCompilerSpec());
|
||||
mappedRange = guest.addMappedRange(b.addr(0x0000), b.addr(guest, 0x0000), 1L << 32);
|
||||
g4000 = b.addInstruction(0, b.addr(0x4000), x86, b.buf(0x90));
|
||||
i4001 = b.addInstruction(0, b.addr(0x4001), b.language, b.buf(0xf4, 0));
|
||||
g4000 = b.addInstruction(0, b.addr(0x4000), guest, b.buf(0x90));
|
||||
i4001 = b.addInstruction(0, b.addr(0x4001), null, b.buf(0xf4, 0));
|
||||
d4003 = b.addData(0, b.addr(0x4003), LongDataType.dataType, b.buf(1, 2, 3, 4));
|
||||
}
|
||||
|
||||
@ -1737,19 +1734,26 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes
|
||||
|
||||
b.trace.undo();
|
||||
|
||||
assertEquals(g4000, manager.codeUnits().getAt(0, b.addr(0x4000)));
|
||||
// NB. The range deletion also deletes the guest unit, so it'll have a new identity
|
||||
// TODO: Related to GP-479?
|
||||
g4000 = manager.instructions().getAt(0, b.addr(0x4000));
|
||||
assertNotNull(g4000);
|
||||
assertEquals(guest, g4000.getGuestPlatform());
|
||||
try (UndoableTransaction tid = b.startTransaction()) {
|
||||
guest.delete(new ConsoleTaskMonitor());
|
||||
}
|
||||
assertUndefinedWithAddr(b.addr(0x4000), manager.codeUnits().getAt(0, b.addr(0x4000)));
|
||||
assertEquals(i4001, manager.codeUnits().getAt(0, b.addr(0x4001)));
|
||||
assertEquals(d4003, manager.codeUnits().getAt(0, b.addr(0x4003)));
|
||||
// TODO: Definitely part of GP-479. These should be able to keep their identities.
|
||||
//assertEquals(i4001, manager.codeUnits().getAt(0, b.addr(0x4001)));
|
||||
//assertEquals(d4003, manager.codeUnits().getAt(0, b.addr(0x4003)));
|
||||
assertNotNull(manager.instructions().getAt(0, b.addr(0x4001)));
|
||||
assertNotNull(manager.definedData().getAt(0, b.addr(0x4003)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSaveAndLoad() throws Exception {
|
||||
try (UndoableTransaction tid = b.startTransaction()) {
|
||||
b.addInstruction(0, b.addr(0x4004), b.language, b.buf(0xf4, 0));
|
||||
b.addInstruction(0, b.addr(0x4004), null, b.buf(0xf4, 0));
|
||||
|
||||
TraceThread thread = b.getOrAddThread("Thread 1", 0);
|
||||
DBTraceCodeRegisterSpace regCode = manager.getCodeRegisterSpace(thread, true);
|
||||
@ -1797,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), b.language, b.buf(0xf4, 0));
|
||||
b.addInstruction(0, b.addr(0x4004), null, b.buf(0xf4, 0));
|
||||
|
||||
TraceThread thread = b.getOrAddThread("Thread 1", 0);
|
||||
DBTraceCodeRegisterSpace regCode = manager.getCodeRegisterSpace(thread, true);
|
||||
@ -1850,7 +1854,7 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes
|
||||
b.trace.getBaseAddressFactory().getDefaultAddressSpace());
|
||||
DBTraceCodeSpace space = manager.getCodeSpace(os, true);
|
||||
|
||||
b.addInstruction(0, os.getAddress(0x4004), b.language, b.buf(0xf4, 0));
|
||||
b.addInstruction(0, os.getAddress(0x4004), null, b.buf(0xf4, 0));
|
||||
|
||||
List<CodeUnit> all = new ArrayList<>();
|
||||
space.definedUnits().get(0, true).forEach(all::add);
|
||||
@ -1859,9 +1863,5 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Test using a context-sensitive language
|
||||
// TODO: Test using delay-slotted instructions (DBTraceCodemanager#instructionMax)
|
||||
// TODO: How are lifespans of delay-slotted instructions bound to thatof the jump?
|
||||
// TODO: In language manager, test deleting a language clears instructions
|
||||
// TODO: In language manager, test unmapping a language clears instructions
|
||||
}
|
||||
|
@ -50,7 +50,7 @@ import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree.TraceAdd
|
||||
import ghidra.trace.database.memory.DBTraceMemoryRegisterSpace;
|
||||
import ghidra.trace.database.memory.DBTraceMemorySpace;
|
||||
import ghidra.trace.database.symbol.DBTraceReference;
|
||||
import ghidra.trace.model.language.TraceGuestLanguage;
|
||||
import ghidra.trace.model.guest.TraceGuestPlatform;
|
||||
import ghidra.trace.model.listing.TraceData;
|
||||
import ghidra.trace.model.listing.TraceInstruction;
|
||||
import ghidra.trace.model.memory.TraceMemoryFlag;
|
||||
@ -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), b.language, b.buf(0xf4, 0));
|
||||
ins = b.addInstruction(0, b.addr(0x4004), null, 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), b.language, b.buf(0xf4, 0));
|
||||
i4004 = b.addInstruction(0, b.addr(0x4004), null, 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), b.language, b.buf(0xf4, 0));
|
||||
i4004 = b.addInstruction(0, b.addr(0x4004), null, b.buf(0xf4, 0));
|
||||
}
|
||||
|
||||
assertEquals(i4004.getProgram().getMemory(), i4004.getMemory());
|
||||
@ -300,14 +300,14 @@ public class DBTraceCodeUnitTest extends AbstractGhidraHeadlessIntegrationTest
|
||||
@Test
|
||||
public void testIsBigEndian() throws CodeUnitInsertionException, AddressOverflowException {
|
||||
Language x86 = getSLEIGH_X86_LANGUAGE();
|
||||
TraceGuestLanguage guest;
|
||||
TraceGuestPlatform guest;
|
||||
TraceInstruction i4004;
|
||||
TraceInstruction g4006;
|
||||
try (UndoableTransaction tid = b.startTransaction()) {
|
||||
i4004 = b.addInstruction(0, b.addr(0x4004), b.language, b.buf(0xf4, 0));
|
||||
guest = b.trace.getLanguageManager().addGuestLanguage(x86);
|
||||
i4004 = b.addInstruction(0, b.addr(0x4004), null, 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), x86, b.buf(0x90));
|
||||
g4006 = b.addInstruction(0, b.addr(0x4006), guest, b.buf(0x90));
|
||||
}
|
||||
|
||||
assertTrue(i4004.isBigEndian());
|
||||
@ -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), b.language, b.buf(0xf4, 0));
|
||||
i4006 = b.addInstruction(0, b.addr(0x4006), b.language, b.buf(0xf4, 0));
|
||||
i4004 = b.addInstruction(0, b.addr(0x4004), null, b.buf(0xf4, 0));
|
||||
i4006 = b.addInstruction(0, b.addr(0x4006), null, 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), b.language, b.buf(0xf4, 0));
|
||||
i4006 = b.addInstruction(0, b.addr(0x4006), b.language, b.buf(0xf4, 0));
|
||||
i4004 = b.addInstruction(0, b.addr(0x4004), null, b.buf(0xf4, 0));
|
||||
i4006 = b.addInstruction(0, b.addr(0x4006), null, 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), b.language);
|
||||
i4004_10 = b.addInstruction(10, b.addr(0x4004), null);
|
||||
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), b.language, b.buf(0xf4, 0));
|
||||
i4006 = b.addInstruction(0, b.addr(0x4006), b.language, b.buf(0xf4, 0));
|
||||
i4004 = b.addInstruction(0, b.addr(0x4004), null, b.buf(0xf4, 0));
|
||||
i4006 = b.addInstruction(0, b.addr(0x4006), null, 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), b.language, b.buf(0xf4, 0));
|
||||
i4006 = b.addInstruction(0, b.addr(0x4006), b.language, b.buf(0xf4, 0));
|
||||
i4004 = b.addInstruction(0, b.addr(0x4004), null, b.buf(0xf4, 0));
|
||||
i4006 = b.addInstruction(0, b.addr(0x4006), null, 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), b.language, b.buf(0xf4, 0));
|
||||
instruction = b.addInstruction(0, b.addr(0x4004), null, 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), b.language, b.buf(0xf4, 0));
|
||||
i4004 = b.addInstruction(0, b.addr(0x4004), null, b.buf(0xf4, 0));
|
||||
|
||||
d4000.setEndSnap(9);
|
||||
assertEquals(Range.closed(0L, 9L), d4000.getLifespan());
|
||||
@ -784,7 +784,7 @@ public class DBTraceCodeUnitTest extends AbstractGhidraHeadlessIntegrationTest
|
||||
@Test
|
||||
public void testGetBytes() throws Exception {
|
||||
Language x86 = getSLEIGH_X86_LANGUAGE();
|
||||
TraceGuestLanguage guest;
|
||||
TraceGuestPlatform guest;
|
||||
|
||||
TraceData data;
|
||||
TraceData und;
|
||||
@ -803,9 +803,9 @@ public class DBTraceCodeUnitTest extends AbstractGhidraHeadlessIntegrationTest
|
||||
DBTraceCodeRegisterSpace regCode = manager.getCodeRegisterSpace(thread, true);
|
||||
reg = regCode.definedData().create(Range.atLeast(0L), r4, PointerDataType.dataType);
|
||||
|
||||
guest = b.trace.getLanguageManager().addGuestLanguage(x86);
|
||||
guest = b.trace.getPlatformManager().addGuestPlatform(x86.getDefaultCompilerSpec());
|
||||
guest.addMappedRange(b.addr(0x0000), b.addr(guest, 0x0000), 1L << 32);
|
||||
lil = b.addInstruction(0, b.addr(0x4008), x86, b.buf(0xeb, 0xfe));
|
||||
lil = b.addInstruction(0, b.addr(0x4008), guest, b.buf(0xeb, 0xfe));
|
||||
}
|
||||
ByteBuffer buf;
|
||||
|
||||
@ -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), b.language, b.buf(0xc8, 0x47));
|
||||
i4004 = b.addInstruction(0, b.addr(0x4004), null, b.buf(0xc8, 0x47));
|
||||
assertEquals("add r4,#0x7", i4004.toString());
|
||||
i4006 = b.addInstruction(0, b.addr(0x4006), b.language, b.buf(0xf4, 0));
|
||||
i4006 = b.addInstruction(0, b.addr(0x4006), null, b.buf(0xf4, 0));
|
||||
assertEquals("ret", i4006.toString());
|
||||
i4008 = b.addInstruction(0, b.addr(0x4008), b.language, b.buf(0xff, 0xfc));
|
||||
i4008 = b.addInstruction(0, b.addr(0x4008), null, b.buf(0xff, 0xfc));
|
||||
assertEquals("call 0x00004004", i4008.toString());
|
||||
i400a = b.addInstruction(0, b.addr(0x400a), b.language, b.buf(0xf6, 0x40));
|
||||
i400a = b.addInstruction(0, b.addr(0x400a), null, 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), b.language, b.buf(0xf4, 0));
|
||||
i4004 = b.addInstruction(0, b.addr(0x4004), null, 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), b.language, b.buf(0xf4, 0));
|
||||
i4004 = b.addInstruction(0, b.addr(0x4004), null, 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), b.language, b.buf(0xf4, 0));
|
||||
i4004 = b.addInstruction(0, b.addr(0x4004), null, b.buf(0xf4, 0));
|
||||
d4006 = b.addData(0, b.addr(0x4006), PointerDataType.dataType,
|
||||
b.buf(0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00));
|
||||
}
|
||||
@ -1285,14 +1285,14 @@ public class DBTraceCodeUnitTest extends AbstractGhidraHeadlessIntegrationTest
|
||||
@Test
|
||||
public void testGetLanguage() throws CodeUnitInsertionException, AddressOverflowException {
|
||||
Language x86 = getSLEIGH_X86_LANGUAGE();
|
||||
TraceGuestLanguage guest;
|
||||
TraceGuestPlatform guest;
|
||||
TraceInstruction i4004;
|
||||
TraceInstruction g4006;
|
||||
try (UndoableTransaction tid = b.startTransaction()) {
|
||||
i4004 = b.addInstruction(0, b.addr(0x4004), b.language, b.buf(0xf4, 0));
|
||||
guest = b.trace.getLanguageManager().addGuestLanguage(x86);
|
||||
i4004 = b.addInstruction(0, b.addr(0x4004), null, 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), x86, b.buf(0x90));
|
||||
g4006 = b.addInstruction(0, b.addr(0x4006), guest, b.buf(0x90));
|
||||
}
|
||||
TraceData u4007 = manager.undefinedData().getAt(0, b.addr(0x4007));
|
||||
|
||||
@ -1305,7 +1305,7 @@ public class DBTraceCodeUnitTest extends AbstractGhidraHeadlessIntegrationTest
|
||||
public void testToString() throws CodeUnitInsertionException, AddressOverflowException,
|
||||
TraceOverlappedRegionException, DuplicateNameException {
|
||||
Language x86 = getSLEIGH_X86_LANGUAGE();
|
||||
TraceGuestLanguage guest;
|
||||
TraceGuestPlatform guest;
|
||||
TraceData d4000;
|
||||
TraceInstruction i4004;
|
||||
TraceInstruction g4006;
|
||||
@ -1316,13 +1316,13 @@ public class DBTraceCodeUnitTest extends AbstractGhidraHeadlessIntegrationTest
|
||||
.addRegion("myRegion", Range.atLeast(0L),
|
||||
b.range(0x4000, 0x4fff), TraceMemoryFlag.READ);
|
||||
|
||||
guest = b.trace.getLanguageManager().addGuestLanguage(x86);
|
||||
guest = b.trace.getPlatformManager().addGuestPlatform(x86.getDefaultCompilerSpec());
|
||||
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), b.language, b.buf(0xc8, 0x47));
|
||||
g4006 = b.addInstruction(0, b.addr(0x4006), x86, b.buf(0x90));
|
||||
i4007 = b.addInstruction(0, b.addr(0x4007), b.language, b.buf(0xff, 0xfd));
|
||||
i4004 = b.addInstruction(0, b.addr(0x4004), null, 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));
|
||||
}
|
||||
TraceData u4009 = manager.undefinedData().getAt(0, b.addr(0x4009));
|
||||
|
||||
|
@ -35,7 +35,7 @@ import ghidra.program.model.listing.CodeUnit;
|
||||
import ghidra.program.model.mem.MemoryBlock;
|
||||
import ghidra.test.AbstractGhidraHeadlessIntegrationTest;
|
||||
import ghidra.trace.database.ToyDBTraceBuilder;
|
||||
import ghidra.trace.database.language.DBTraceGuestLanguage;
|
||||
import ghidra.trace.database.guest.DBTraceGuestPlatform;
|
||||
import ghidra.trace.database.listing.*;
|
||||
import ghidra.trace.database.memory.DBTraceMemoryManager;
|
||||
import ghidra.trace.database.memory.DBTraceMemorySpace;
|
||||
@ -108,13 +108,15 @@ public class DBTraceDisassemblerIntegrationTest extends AbstractGhidraHeadlessIn
|
||||
b.trace.getMemoryManager().getMemorySpace(b.language.getDefaultSpace(), true);
|
||||
space.putBytes(0, b.addr(0x4000), b.buf(0x90));
|
||||
|
||||
DBTraceGuestLanguage guest = b.trace.getLanguageManager().addGuestLanguage(x86);
|
||||
DBTraceGuestPlatform guest =
|
||||
b.trace.getPlatformManager().addGuestPlatform(x86.getDefaultCompilerSpec());
|
||||
guest.addMappedRange(b.addr(0x4000), b.addr(guest, 0x00400000), 0x1000);
|
||||
|
||||
// TODO: The more I look, the more I think I need a fully-mapped program view :(
|
||||
// As annoying as it is, I plan to do it as a wrapper, not as an extension....
|
||||
// The disassembler uses bookmarks, context, etc. for feedback. It'd be nice to
|
||||
// have that
|
||||
/*
|
||||
* TODO: The more I look, the more I think I need a fully-mapped program view :( As
|
||||
* annoying as it is, I plan to do it as a wrapper, not as an extension.... The
|
||||
* disassembler uses bookmarks, context, etc. for feedback. It'd be nice to have that.
|
||||
*/
|
||||
RegisterValue defaultContextValue =
|
||||
b.trace.getRegisterContextManager()
|
||||
.getDefaultContext(x86)
|
||||
@ -125,7 +127,7 @@ public class DBTraceDisassemblerIntegrationTest extends AbstractGhidraHeadlessIn
|
||||
guest.getMappedMemBuffer(0, b.addr(guest, 0x00400000)), defaultContextValue, 1));
|
||||
|
||||
DBTraceCodeManager code = b.trace.getCodeManager();
|
||||
code.instructions().addInstructionSet(Range.closed(0L, 0L), set, false);
|
||||
code.instructions().addInstructionSet(Range.closed(0L, 0L), guest, set, false);
|
||||
|
||||
DBTraceInstruction ins = code.instructions().getAt(0, b.addr(0x4000));
|
||||
// TODO: This is great, but probably incomplete.
|
||||
|
@ -114,7 +114,7 @@ public class DBTraceProgramViewListingTest extends AbstractGhidraHeadlessIntegra
|
||||
CodeUnitInsertionException {
|
||||
Instruction ins;
|
||||
try (UndoableTransaction tid = b.startTransaction()) {
|
||||
ins = b.addInstruction(0, b.addr(0x4005), b.language, b.buf(0xf4, 0));
|
||||
ins = b.addInstruction(0, b.addr(0x4005), null, 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), b.language, b.buf(0xf4, 0));
|
||||
i4005 = b.addInstruction(0, b.addr(0x4005), null, 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), b.language, b.buf(0xf4, 0));
|
||||
i4005 = b.addInstruction(0, b.addr(0x4005), null, 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), b.language, b.buf(0xf4, 0));
|
||||
i4005 = b.addInstruction(0, b.addr(0x4005), null, 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), b.language, b.buf(0xf4, 0));
|
||||
i4005 = b.addInstruction(0, b.addr(0x4005), null, 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), b.language, b.buf(0xf4, 0));
|
||||
i4005 = b.addInstruction(0, b.addr(0x4005), null, 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), b.language, b.buf(0xf4, 0));
|
||||
i4005 = b.addInstruction(0, b.addr(0x4005), null, 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), b.language, b.buf(0xf4, 0));
|
||||
i4005 = b.addInstruction(0, b.addr(0x4005), null, 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), b.language, b.buf(0xf4, 0));
|
||||
i4005 = b.addInstruction(0, b.addr(0x4005), null, 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), b.language, b.buf(0xf4, 0));
|
||||
i4005 = b.addInstruction(0, b.addr(0x4005), null, 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), b.language, b.buf(0xf4, 0));
|
||||
i4007 = b.addInstruction(0, b.addr(0x4007), b.language, b.buf(0xf4, 0));
|
||||
i400a = b.addInstruction(0, b.addr(0x400a), b.language, b.buf(0xf4, 0));
|
||||
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));
|
||||
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), b.language, b.buf(0xf4, 0));
|
||||
b.addInstruction(0, b.addr(0x4005), null, 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), b.language, b.buf(0xf4, 0));
|
||||
b.addInstruction(0, b.addr(0x4005), null, 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), b.language, b.buf(0xf4, 0));
|
||||
b.addInstruction(0, b.addr(0x4005), null, 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), b.language, b.buf(0xf4, 0));
|
||||
b.addInstruction(0, b.addr(0x4005), null, 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), b.language, b.buf(0xf4, 0));
|
||||
b.addInstruction(0, b.addr(0x4005), null, 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), b.language, b.buf(0xf4, 0));
|
||||
b.addInstruction(0, b.addr(0x400e), null, 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), b.language, b.buf(0xf4, 0));
|
||||
b.addInstruction(0, b.addr(0x4005), null, 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), b.language, b.buf(0xf4, 0));
|
||||
b.addInstruction(0, b.addr(0x4005), null, 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), b.language, b.buf(0xf4, 0));
|
||||
b.addInstruction(0, b.addr(0x4005), null, 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), b.language, b.buf(0xf4, 0));
|
||||
b.addInstruction(0, b.addr(0x4005), null, 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), b.language, b.buf(0xf4, 0));
|
||||
b.addInstruction(0, b.addr(0x4005), null, 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), b.language, b.buf(0xf4, 0));
|
||||
i4005 = b.addInstruction(0, b.addr(0x4005), null, 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), b.language, b.buf(0xf4, 0));
|
||||
i4005 = b.addInstruction(0, b.addr(0x4005), null, b.buf(0xf4, 0));
|
||||
}
|
||||
assertEquals(i4005, listing.getDefinedCodeUnitBefore(b.addr(0x4006)));
|
||||
assertEquals(d4000, listing.getDefinedCodeUnitBefore(b.addr(0x4005)));
|
||||
|
@ -0,0 +1,42 @@
|
||||
/* ###
|
||||
* 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.framework.cmd;
|
||||
|
||||
import ghidra.framework.model.DomainObject;
|
||||
import ghidra.framework.model.UndoableDomainObject;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
public abstract class TypedBackgroundCommand<T extends UndoableDomainObject>
|
||||
extends BackgroundCommand {
|
||||
|
||||
public TypedBackgroundCommand(String name, boolean hasProgress, boolean canCancel,
|
||||
boolean isModal) {
|
||||
super(name, hasProgress, canCancel, isModal);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public boolean applyTo(DomainObject obj, TaskMonitor monitor) {
|
||||
return applyToTyped((T) obj, monitor);
|
||||
}
|
||||
|
||||
public abstract boolean applyToTyped(T obj, TaskMonitor monitor);
|
||||
|
||||
public void run(PluginTool tool, T obj) {
|
||||
tool.executeBackgroundCommand(this, obj);
|
||||
}
|
||||
}
|
@ -266,7 +266,8 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new list of all DockingWindowManager instances know to exist.
|
||||
* Returns a new list of all DockingWindowManager instances known to exist, ordered from least
|
||||
* to most-recently active.
|
||||
*
|
||||
* @return a new list of all DockingWindowManager instances know to exist.
|
||||
*/
|
||||
|
@ -14,6 +14,7 @@ data/languages/toy.cspec||GHIDRA||||END|
|
||||
data/languages/toy.ldefs||GHIDRA||||END|
|
||||
data/languages/toy.pspec||GHIDRA||reviewed||END|
|
||||
data/languages/toy.sinc||GHIDRA||||END|
|
||||
data/languages/toy64-long8.cspec||GHIDRA||||END|
|
||||
data/languages/toy64.cspec||GHIDRA||||END|
|
||||
data/languages/toy64_be.slaspec||GHIDRA||||END|
|
||||
data/languages/toy64_be_harvard.slaspec||GHIDRA||||END|
|
||||
|
@ -66,6 +66,7 @@
|
||||
id="Toy:BE:64:default">
|
||||
<description>Toy (test) processor 64-bit big-endian</description>
|
||||
<compiler name="default" spec="toy64.cspec" id="default"/>
|
||||
<compiler name="long8" spec="toy64-long8.cspec" id="long8"/>
|
||||
</language>
|
||||
<language processor="Toy"
|
||||
endian="big"
|
||||
|
90
Ghidra/Processors/Toy/data/languages/toy64-long8.cspec
Normal file
90
Ghidra/Processors/Toy/data/languages/toy64-long8.cspec
Normal file
@ -0,0 +1,90 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<compiler_spec>
|
||||
<data_organization>
|
||||
<absolute_max_alignment value="0" />
|
||||
<machine_alignment value="2" />
|
||||
<default_alignment value="1" />
|
||||
<default_pointer_alignment value="8" />
|
||||
<pointer_size value="8" />
|
||||
<wchar_size value="2" />
|
||||
<short_size value="2" />
|
||||
<integer_size value="4" />
|
||||
<long_size value="8" />
|
||||
<long_long_size value="8" />
|
||||
<float_size value="4" />
|
||||
<double_size value="8" />
|
||||
<long_double_size value="8" />
|
||||
<size_alignment_map>
|
||||
<entry size="1" alignment="1" />
|
||||
<entry size="2" alignment="2" />
|
||||
<entry size="4" alignment="4" />
|
||||
<entry size="8" alignment="8" />
|
||||
</size_alignment_map>
|
||||
</data_organization>
|
||||
<global>
|
||||
<range space="ram"/>
|
||||
</global>
|
||||
<stackpointer register="sp" space="ram"/>
|
||||
<default_proto>
|
||||
<prototype name="__stdcall" extrapop="unknown" stackshift="4">
|
||||
<input pointermax="16">
|
||||
<pentry minsize="1" maxsize="8">
|
||||
<register name="r12"/>
|
||||
</pentry>
|
||||
<pentry minsize="1" maxsize="8">
|
||||
<register name="r11"/>
|
||||
</pentry>
|
||||
<pentry minsize="1" maxsize="8">
|
||||
<register name="r10"/>
|
||||
</pentry>
|
||||
<pentry minsize="1" maxsize="8">
|
||||
<register name="r9"/>
|
||||
</pentry>
|
||||
<pentry minsize="1" maxsize="8">
|
||||
<register name="r8"/>
|
||||
</pentry>
|
||||
<pentry minsize="1" maxsize="500" align="4">
|
||||
<addr offset="0" space="stack"/>
|
||||
</pentry>
|
||||
</input>
|
||||
<output>
|
||||
<pentry minsize="1" maxsize="8">
|
||||
<register name="r12"/>
|
||||
</pentry>
|
||||
</output>
|
||||
<unaffected>
|
||||
<varnode space="ram" offset="0" size="8"/>
|
||||
<register name="sp"/>
|
||||
<register name="lr"/>
|
||||
<register name="r0"/>
|
||||
<register name="r1"/>
|
||||
<register name="r2"/>
|
||||
<register name="r3"/>
|
||||
<register name="r4"/>
|
||||
<register name="r5"/>
|
||||
<register name="r6"/>
|
||||
<register name="r7"/>
|
||||
</unaffected>
|
||||
</prototype>
|
||||
</default_proto>
|
||||
|
||||
<prototype name="__stackcall" extrapop="unknown" stackshift="4">
|
||||
<input pointermax="16">
|
||||
<pentry minsize="1" maxsize="500" align="4">
|
||||
<addr offset="0" space="stack"/>
|
||||
</pentry>
|
||||
</input>
|
||||
<output>
|
||||
<pentry minsize="1" maxsize="8">
|
||||
<register name="r12"/>
|
||||
</pentry>
|
||||
</output>
|
||||
<unaffected>
|
||||
<varnode space="ram" offset="0" size="8"/>
|
||||
<register name="sp"/>
|
||||
<register name="lr"/>
|
||||
</unaffected>
|
||||
</prototype>
|
||||
|
||||
</compiler_spec>
|
@ -91,6 +91,7 @@
|
||||
<compiler name="clang" spec="x86-64-win.cspec" id="clangwindows"/>
|
||||
<compiler name="gcc" spec="x86-64-gcc.cspec" id="gcc"/>
|
||||
<external_name tool="gnu" name="i386:x86-64:intel"/>
|
||||
<external_name tool="gnu" name="i386:x86-64"/>
|
||||
<external_name tool="IDA-PRO" name="metapc"/>
|
||||
<external_name tool="DWARF.register.mapping.file" name="x86-64.dwarf"/>
|
||||
</language>
|
||||
|
Loading…
Reference in New Issue
Block a user