GP-1967: Fix auto-disassembly for object-based traces, incl. framework support.

This commit is contained in:
Dan 2022-06-17 11:41:33 -04:00
parent 858cba5e90
commit 67d52f4fcc
83 changed files with 3561 additions and 888 deletions

View file

@ -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" />

View file

@ -2,6 +2,7 @@ AutoReadMemorySpec
DebuggerBot
DebuggerMappingOpinion
DebuggerModelFactory
DebuggerPlatformOpinion
DebuggerProgramLaunchOpinion
DisassemblyInject
LocationTrackingSpec

View file

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

View file

@ -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;

View file

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

View file

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

View file

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

View file

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

View file

@ -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) {

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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;

View file

@ -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;

View file

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

View file

@ -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;

View file

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

View file

@ -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;

View file

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

View file

@ -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");

View file

@ -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;

View file

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

View file

@ -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()

View file

@ -50,7 +50,7 @@ public class EmptyDebuggerRegisterMapper implements DebuggerRegisterMapper {
@Override
public Set<Register> getRegistersOnTarget() {
return null;
return Set.of();
}
@Override

View file

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

View file

@ -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;

View file

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

View file

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

View file

@ -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) {
}
}

View file

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

View file

@ -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();

View file

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

View file

@ -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;

View file

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

View file

@ -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

View file

@ -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> {

View file

@ -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;

View file

@ -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) {

View file

@ -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 =

View file

@ -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) {

View file

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

View file

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

View file

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

View file

@ -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();

View file

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

View file

@ -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.

View file

@ -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

View file

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

View file

@ -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();

View file

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

View file

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

View file

@ -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?

View file

@ -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

View file

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

View file

@ -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) {

View file

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

View file

@ -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;

View file

@ -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()

View file

@ -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();

View file

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

View file

@ -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();

View file

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

View file

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

View file

@ -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();

View file

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

View file

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

View file

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

View file

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

View file

@ -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
}

View file

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

View file

@ -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.

View file

@ -114,7 +114,7 @@ public class DBTraceProgramViewListingTest extends AbstractGhidraHeadlessIntegra
CodeUnitInsertionException {
Instruction ins;
try (UndoableTransaction tid = b.startTransaction()) {
ins = b.addInstruction(0, b.addr(0x4005), 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)));

View file

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

View file

@ -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.
*/

View file

@ -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|

View file

@ -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"

View 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>

View file

@ -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>