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="Access" />
<interface name="Attacher" /> <interface name="Attacher" />
<interface name="Attachable" /> <interface name="Attachable" />
<interface name="Launcher" /> <!-- <interface name="Launcher" /> -->
<interface name="Deletable" /> <interface name="Deletable" />
<interface name="Detachable" /> <interface name="Detachable" />
<interface name="Killable" /> <interface name="Killable" />

View file

@ -2,6 +2,7 @@ AutoReadMemorySpec
DebuggerBot DebuggerBot
DebuggerMappingOpinion DebuggerMappingOpinion
DebuggerModelFactory DebuggerModelFactory
DebuggerPlatformOpinion
DebuggerProgramLaunchOpinion DebuggerProgramLaunchOpinion
DisassemblyInject DisassemblyInject
LocationTrackingSpec 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_TRACE = Trace.TRACE_ICON;
ImageIcon ICON_THREAD = ResourceManager.loadImage("images/thread.png"); ImageIcon ICON_THREAD = ResourceManager.loadImage("images/thread.png");
ImageIcon ICON_PROGRAM = ProgramContentHandler.PROGRAM_ICON; ImageIcon ICON_PROGRAM = ProgramContentHandler.PROGRAM_ICON;
ImageIcon ICON_PROCESSOR = ResourceManager.loadImage("images/kcmprocessor.png");
ImageIcon ICON_LAUNCH = ResourceManager.loadImage("images/launch.png"); ImageIcon ICON_LAUNCH = ResourceManager.loadImage("images/launch.png");
ImageIcon ICON_ATTACH = ResourceManager.loadImage("images/attach.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 { abstract class AbstractRecordAction extends DockingAction {
public static final String NAME = "Record"; public static final String NAME = "Record";
public static final Icon ICON = ICON_TRACE; public static final Icon ICON = ICON_TRACE;

View file

@ -16,6 +16,7 @@
package ghidra.app.plugin.core.debug.gui.register; package ghidra.app.plugin.core.debug.gui.register;
import java.math.BigInteger; import java.math.BigInteger;
import java.util.Objects;
import ghidra.program.model.data.DataType; import ghidra.program.model.data.DataType;
import ghidra.program.model.lang.Language; import ghidra.program.model.lang.Language;
@ -31,7 +32,7 @@ public class RegisterRow {
public RegisterRow(DebuggerRegistersProvider provider, int number, Register register) { public RegisterRow(DebuggerRegistersProvider provider, int number, Register register) {
this.provider = provider; this.provider = provider;
this.number = number; this.number = number;
this.register = register; this.register = Objects.requireNonNull(register);
this.favorite = provider.isFavorite(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 * See the License for the specific language governing permissions and
* limitations under the License. * 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; public abstract class AbstractDebuggerPlatformOffer implements DebuggerPlatformOffer {
import ghidra.program.model.lang.Language; private final String description;
protected final CompilerSpec cSpec;
public interface TraceLanguageManager { public AbstractDebuggerPlatformOffer(String description, CompilerSpec cSpec) {
Language getBaseLanguage(); this.description = description;
this.cSpec = cSpec;
}
TraceGuestLanguage addGuestLanguage(Language language); @Override
public String getDescription() {
return description;
}
Collection<TraceGuestLanguage> getGuestLanguages(); @Override
public CompilerSpec getCompilerSpec() {
InstructionSet mapGuestInstructionAddressesToHost(InstructionSet instructionSet); 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 target the target to be recorded, usually a process
* @param includeOverrides true to include offers with negative confidence * @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, public static List<DebuggerMappingOffer> queryOpinions(TargetObject target,
boolean includeOverrides) { 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 java.math.BigInteger;
import ghidra.app.cmd.disassemble.DisassembleCommand; import ghidra.app.plugin.core.debug.workflow.*;
import ghidra.app.plugin.core.debug.workflow.DisassemblyInject;
import ghidra.app.plugin.core.debug.workflow.DisassemblyInjectInfo;
import ghidra.framework.plugintool.PluginTool; import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.address.AddressSetView; import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.lang.Register; import ghidra.program.model.lang.*;
import ghidra.program.model.lang.RegisterValue; import ghidra.trace.model.Trace;
import ghidra.trace.model.memory.TraceMemoryRegisterSpace; import ghidra.trace.model.memory.TraceMemoryRegisterSpace;
import ghidra.trace.model.memory.TraceMemoryState; import ghidra.trace.model.memory.TraceMemoryState;
import ghidra.trace.model.program.TraceProgramView;
import ghidra.trace.model.thread.TraceThread; import ghidra.trace.model.thread.TraceThread;
import ghidra.util.Msg; import ghidra.util.Msg;
@ -61,36 +58,37 @@ public class ArmDisassemblyInject implements DisassemblyInject {
} }
@Override @Override
public void pre(PluginTool tool, DisassembleCommand command, TraceProgramView view, public void pre(PluginTool tool, DisassembleTraceCommand command, Trace trace,
TraceThread thread, AddressSetView startSet, AddressSetView restricted) { 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 * 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 * is the cpsr register, when it's available. For auto-pc, the trace recorder ought to have
* recorded cpsr at the recorded tick. * recorded cpsr at the recorded tick.
*/ */
Register cpsrReg = view.getLanguage().getRegister("cpsr"); Register cpsrReg = language.getRegister("cpsr");
Register tModeReg = view.getLanguage().getRegister("TMode"); Register tModeReg = language.getRegister("TMode");
if (cpsrReg == null || tModeReg == null) { if (cpsrReg == null || tModeReg == null) {
Msg.error(this, "No cpsr or TMode register in ARM language?: " + Msg.error(this,
view.getLanguage().getLanguageID()); "No cpsr or TMode register in ARM language?: " + language.getLanguageID());
return; return;
} }
TraceMemoryRegisterSpace regs = 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 * 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 * 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. * 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)); command.setInitialContext(new RegisterValue(tModeReg, BigInteger.ONE));
return; return;
} }
RegisterValue cpsrVal = regs.getValue(view.getSnap(), cpsrReg); RegisterValue cpsrVal = regs.getValue(snap, cpsrReg);
if (isThumbMode(cpsrVal)) { if (isThumbMode(cpsrVal)) {
command.setInitialContext(new RegisterValue(tModeReg, BigInteger.ONE)); 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.concurrent.*;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import ghidra.app.cmd.disassemble.DisassembleCommand; import ghidra.app.plugin.core.debug.workflow.*;
import ghidra.app.plugin.core.debug.workflow.DisassemblyInject;
import ghidra.app.plugin.core.debug.workflow.DisassemblyInjectInfo;
import ghidra.app.services.DebuggerModelService; import ghidra.app.services.DebuggerModelService;
import ghidra.app.services.TraceRecorder; 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.*;
import ghidra.app.util.bin.format.pe.PortableExecutable.SectionLayout; import ghidra.app.util.bin.format.pe.PortableExecutable.SectionLayout;
import ghidra.framework.plugintool.PluginTool; import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.address.*; import ghidra.program.model.address.*;
import ghidra.program.model.lang.*; import ghidra.program.model.lang.*;
import ghidra.program.model.mem.MemBuffer;
import ghidra.program.util.ProgramContextImpl; import ghidra.program.util.ProgramContextImpl;
import ghidra.trace.model.Trace; import ghidra.trace.model.Trace;
import ghidra.trace.model.modules.TraceModule; import ghidra.trace.model.modules.TraceModule;
import ghidra.trace.model.program.TraceProgramView;
import ghidra.trace.model.thread.TraceThread; import ghidra.trace.model.thread.TraceThread;
import ghidra.util.Msg; import ghidra.util.Msg;
import ghidra.util.task.TaskMonitor; import ghidra.util.task.TaskMonitor;
@ -50,9 +49,9 @@ public class DbgengX64DisassemblyInject implements DisassemblyInject {
} }
@Override @Override
public void pre(PluginTool tool, DisassembleCommand command, TraceProgramView view, public void pre(PluginTool tool, DisassembleTraceCommand command, Trace trace,
TraceThread thread, AddressSetView startSet, AddressSetView restricted) { Language language, long snap, TraceThread thread, AddressSetView startSet,
Trace trace = view.getTrace(); AddressSetView restricted) {
AddressRange first = startSet.getFirstRange(); AddressRange first = startSet.getFirstRange();
if (first == null) { if (first == null) {
return; return;
@ -60,20 +59,19 @@ public class DbgengX64DisassemblyInject implements DisassemblyInject {
DebuggerModelService modelService = tool.getService(DebuggerModelService.class); DebuggerModelService modelService = tool.getService(DebuggerModelService.class);
TraceRecorder recorder = modelService == null ? null : modelService.getRecorder(trace); TraceRecorder recorder = modelService == null ? null : modelService.getRecorder(trace);
Collection<? extends TraceModule> modules = Collection<? extends TraceModule> modules =
trace.getModuleManager().getModulesAt(view.getSnap(), first.getMinAddress()); trace.getModuleManager().getModulesAt(snap, first.getMinAddress());
Set<Mode> modes = modules.stream() Set<Mode> modes = modules.stream()
.map(m -> modeForModule(recorder, view, m)) .map(m -> modeForModule(recorder, trace, snap, m))
.filter(m -> m != Mode.UNK) .filter(m -> m != Mode.UNK)
.collect(Collectors.toSet()); .collect(Collectors.toSet());
if (modes.size() != 1) { if (modes.size() != 1) {
return; return;
} }
Mode mode = modes.iterator().next(); Mode mode = modes.iterator().next();
Language lang = trace.getBaseLanguage(); Register addrsizeReg = language.getRegister("addrsize");
Register addrsizeReg = lang.getRegister("addrsize"); Register opsizeReg = language.getRegister("opsize");
Register opsizeReg = lang.getRegister("opsize"); ProgramContextImpl context = new ProgramContextImpl(language);
ProgramContextImpl context = new ProgramContextImpl(lang); language.applyContextSettings(context);
lang.applyContextSettings(context);
RegisterValue ctxVal = context.getDisassemblyContext(first.getMinAddress()); RegisterValue ctxVal = context.getDisassemblyContext(first.getMinAddress());
if (mode == Mode.X64) { if (mode == Mode.X64) {
command.setInitialContext(ctxVal command.setInitialContext(ctxVal
@ -88,9 +86,9 @@ public class DbgengX64DisassemblyInject implements DisassemblyInject {
// Shouldn't ever get anything else. // Shouldn't ever get anything else.
} }
protected Mode modeForModule(TraceRecorder recorder, TraceProgramView view, protected Mode modeForModule(TraceRecorder recorder, Trace trace, long snap,
TraceModule module) { TraceModule module) {
if (recorder != null && recorder.getSnap() == view.getSnap()) { if (recorder != null && recorder.getSnap() == snap) {
AddressSet set = new AddressSet(); AddressSet set = new AddressSet();
set.add(module.getBase(), module.getBase()); // Recorder should read page set.add(module.getBase(), module.getBase()); // Recorder should read page
try { try {
@ -104,9 +102,10 @@ public class DbgengX64DisassemblyInject implements DisassemblyInject {
// Try to parse whatever's there. If 0s, it'll come UNK. // 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 { try {
PortableExecutable pe = new PortableExecutable(mbp, SectionLayout.MEMORY, false, false); PortableExecutable pe = new PortableExecutable(bp, SectionLayout.MEMORY, false, false);
NTHeader ntHeader = pe.getNTHeader(); NTHeader ntHeader = pe.getNTHeader();
if (ntHeader == null) { if (ntHeader == null) {
return Mode.UNK; return Mode.UNK;

View file

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package ghidra.app.plugin.core.debug.platform.arm; package ghidra.app.plugin.core.debug.platform.frida;
import java.util.Set; 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 * See the License for the specific language governing permissions and
* limitations under the License. * 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.Collection;
import java.util.Set; import java.util.Set;
import ghidra.app.plugin.core.debug.mapping.DebuggerMappingOffer; import ghidra.app.plugin.core.debug.mapping.DebuggerMappingOffer;
import ghidra.app.plugin.core.debug.platform.gdb.DefaultGdbDebuggerMappingOpinion;
import ghidra.dbg.target.TargetObject; import ghidra.dbg.target.TargetObject;
import ghidra.program.model.lang.*; import ghidra.program.model.lang.*;
import ghidra.program.util.DefaultLanguageService; 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 * See the License for the specific language governing permissions and
* limitations under the License. * 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.Collection;
import java.util.Set; 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 * See the License for the specific language governing permissions and
* limitations under the License. * 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.Collection;
import java.util.Set; import java.util.Set;
@ -22,7 +22,7 @@ import ghidra.app.plugin.core.debug.mapping.*;
import ghidra.dbg.target.*; import ghidra.dbg.target.*;
import ghidra.program.model.lang.*; 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 LanguageID LANG_ID_JAVA = new LanguageID("JVM:BE:32:default");
protected static final CompilerSpecID COMP_ID_VS = new CompilerSpecID("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 * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package ghidra.app.plugin.core.debug.platform.arm; package ghidra.app.plugin.core.debug.platform.lldb;
import java.util.Set; 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)); () -> onTimedOutRecorder(monitor, service, t));
}).thenCompose(r -> { }).thenCompose(r -> {
monitor.incrementProgress(1); monitor.incrementProgress(1);
if (r == null) {
throw new CancellationException();
}
monitor.setMessage("Confirming program is mapped to target"); monitor.setMessage("Confirming program is mapped to target");
CompletableFuture<Void> futureMapped = listenForMapping(mappingService, r); CompletableFuture<Void> futureMapped = listenForMapping(mappingService, r);
return AsyncTimer.DEFAULT_TIMER.mark() return AsyncTimer.DEFAULT_TIMER.mark()

View file

@ -50,7 +50,7 @@ public class EmptyDebuggerRegisterMapper implements DebuggerRegisterMapper {
@Override @Override
public Set<Register> getRegistersOnTarget() { public Set<Register> getRegistersOnTarget() {
return null; return Set.of();
} }
@Override @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 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.plugin.core.debug.service.workflow.*;
import ghidra.app.services.DebuggerBot; import ghidra.app.services.*;
import ghidra.app.services.DebuggerBotInfo;
import ghidra.async.AsyncDebouncer; import ghidra.async.AsyncDebouncer;
import ghidra.async.AsyncTimer; import ghidra.async.AsyncTimer;
import ghidra.framework.cmd.BackgroundCommand;
import ghidra.framework.model.DomainObject; import ghidra.framework.model.DomainObject;
import ghidra.framework.options.annotation.HelpInfo; import ghidra.framework.options.annotation.HelpInfo;
import ghidra.framework.plugintool.PluginTool; import ghidra.framework.plugintool.PluginTool;
@ -43,6 +46,8 @@ import ghidra.trace.model.listing.*;
import ghidra.trace.model.memory.*; import ghidra.trace.model.memory.*;
import ghidra.trace.model.program.TraceProgramView; import ghidra.trace.model.program.TraceProgramView;
import ghidra.trace.model.stack.*; 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.model.thread.TraceThread;
import ghidra.trace.util.*; import ghidra.trace.util.*;
import ghidra.util.*; import ghidra.util.*;
@ -207,14 +212,18 @@ public class DisassembleAtPcDebuggerBot implements DebuggerBot {
if (pcVal == null) { if (pcVal == null) {
return; return;
} }
if (range == null || range.contains(pcVal)) { if (range != null && !range.contains(pcVal)) {
// NOTE: If non-0 frames are ever used, level should be passed in for injects return;
disassemble(pcVal, stack.getThread(), snap);
} }
// 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, protected void disassembleRegPcVal(TraceThread thread, int frameLevel, long pcSnap,
long memSnap) { long memSnap) {
if (pc == null) {
return;
}
TraceData pcUnit = null; TraceData pcUnit = null;
try (UndoableTransaction tid = try (UndoableTransaction tid =
UndoableTransaction.start(trace, "Disassemble: PC is code pointer", true)) { UndoableTransaction.start(trace, "Disassemble: PC is code pointer", true)) {
@ -258,6 +267,14 @@ public class DisassembleAtPcDebuggerBot implements DebuggerBot {
return mrent.getKey().getY1(); 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) { protected void disassemble(Address start, TraceThread thread, long snap) {
Long knownSnap = isKnownRWOrEverKnownRO(start, snap); Long knownSnap = isKnownRWOrEverKnownRO(start, snap);
if (knownSnap == null) { if (knownSnap == null) {
@ -290,39 +307,39 @@ public class DisassembleAtPcDebuggerBot implements DebuggerBot {
// TODO: Should I just keep a variable-snap view around? // TODO: Should I just keep a variable-snap view around?
TraceProgramView view = trace.getFixedProgramView(ks); TraceProgramView view = trace.getFixedProgramView(ks);
DisassembleCommand dis =
new DisassembleCommand(start, disassemblable, true) { BackgroundCommand cmd = new BackgroundCommand("Auto-disassemble", true, true, false) {
@Override @Override
public boolean applyTo(DomainObject obj, TaskMonitor monitor) { public boolean applyTo(DomainObject obj, TaskMonitor monitor) {
synchronized (injects) { try {
try { DebuggerPlatformService platformService =
if (codeManager.definedUnits().containsAddress(ks, start)) { findService(DebuggerPlatformService.class);
return true; if (platformService == null) {
} reportError("Cannot disassemble without the platform service");
for (DisassemblyInject i : injects) { return true;
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
}
} }
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 // 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 = private final MultiToolTraceListenerManager<ForDisassemblyTraceListener> listeners =
new MultiToolTraceListenerManager<>(ForDisassemblyTraceListener::new); 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 @Override
public boolean isEnabled() { public boolean isEnabled() {
return plugin != null; 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 java.util.Arrays;
import ghidra.app.cmd.disassemble.DisassembleCommand;
import ghidra.framework.plugintool.PluginTool; import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.address.AddressSetView; import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.lang.Language;
import ghidra.trace.model.Trace; import ghidra.trace.model.Trace;
import ghidra.trace.model.program.TraceProgramView;
import ghidra.trace.model.thread.TraceThread; import ghidra.trace.model.thread.TraceThread;
import ghidra.util.Msg; import ghidra.util.Msg;
import ghidra.util.classfinder.ExtensionPoint; import ghidra.util.classfinder.ExtensionPoint;
@ -87,13 +86,16 @@ public interface DisassemblyInject extends ExtensionPoint {
* *
* @param tool the tool that will execute the command * @param tool the tool that will execute the command
* @param command the command to be configured, which is about to execute * @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 thread the thread whose PC is being disassembled
* @param startSet the starting address set, usually just the PC * @param startSet the starting address set, usually just the PC
* @param restricted the set of disassemblable addresses * @param restricted the set of disassemblable addresses
*/ */
default void pre(PluginTool tool, DisassembleCommand command, TraceProgramView view, default void pre(PluginTool tool, DisassembleTraceCommand command, Trace trace,
TraceThread thread, AddressSetView startSet, AddressSetView restricted) { 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. * The callback occurs within the command's background thread.
* *
* @param tool the tool that just executed the disassembly command * @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 * @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.lang.Language;
import ghidra.program.model.util.CodeUnitInsertionException; import ghidra.program.model.util.CodeUnitInsertionException;
import ghidra.trace.database.ToyDBTraceBuilder; 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.TraceMemoryFlag;
import ghidra.trace.model.memory.TraceOverlappedRegionException; import ghidra.trace.model.memory.TraceOverlappedRegionException;
import ghidra.util.database.UndoableTransaction; import ghidra.util.database.UndoableTransaction;
@ -129,12 +129,13 @@ public class DebuggerManualTest extends AbstractGhidraHeadedDebuggerGUITest {
tb.trace.getThreadManager().createThread("Thread 2", 4); tb.trace.getThreadManager().createThread("Thread 2", 4);
tb.addData(0, tb.addr(0x4004), Undefined4DataType.dataType, tb.buf(6, 7, 8, 9)); 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(); 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); 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(); 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 * See the License for the specific language governing permissions and
* limitations under the License. * 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.assertFalse;
import static org.junit.Assert.assertTrue; 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.DebuggerMappingOffer;
import ghidra.app.plugin.core.debug.mapping.DebuggerMappingOpinion; 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.gdb.GdbArmDebuggerMappingOpinion.GdbAArch64Offer;
import ghidra.app.plugin.core.debug.platform.arm.GdbArmDebuggerMappingOpinion.GdbArmOffer; import ghidra.app.plugin.core.debug.platform.gdb.GdbArmDebuggerMappingOpinion.GdbArmOffer;
import ghidra.dbg.model.TestDebuggerObjectModel; import ghidra.dbg.model.TestDebuggerObjectModel;
import ghidra.dbg.model.TestTargetProcess; import ghidra.dbg.model.TestTargetProcess;
import ghidra.program.model.lang.LanguageID; 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.context.DBTraceRegisterContextManager;
import ghidra.trace.database.data.DBTraceDataSettingsAdapter; import ghidra.trace.database.data.DBTraceDataSettingsAdapter;
import ghidra.trace.database.data.DBTraceDataTypeManager; 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.DBTraceCodeManager;
import ghidra.trace.database.listing.DBTraceCommentAdapter; import ghidra.trace.database.listing.DBTraceCommentAdapter;
import ghidra.trace.database.memory.DBTraceMemoryManager; import ghidra.trace.database.memory.DBTraceMemoryManager;
@ -101,7 +101,7 @@ public class DBTrace extends DBCachedDomainObjectAdapter implements Trace, Trace
@DependentService @DependentService
protected DBTraceEquateManager equateManager; protected DBTraceEquateManager equateManager;
@DependentService @DependentService
protected DBTraceLanguageManager languageManager; protected DBTracePlatformManager platformManager;
@DependentService @DependentService
protected DBTraceMemoryManager memoryManager; protected DBTraceMemoryManager memoryManager;
@DependentService @DependentService
@ -283,12 +283,12 @@ public class DBTrace extends DBCachedDomainObjectAdapter implements Trace, Trace
@DependentService @DependentService
protected DBTraceCodeManager createCodeManager(DBTraceThreadManager threadManager, protected DBTraceCodeManager createCodeManager(DBTraceThreadManager threadManager,
DBTraceLanguageManager languageManager, DBTraceDataTypeManager dataTypeManager, DBTracePlatformManager platformManager, DBTraceDataTypeManager dataTypeManager,
DBTraceOverlaySpaceAdapter overlayAdapter, DBTraceReferenceManager referenceManager) DBTraceOverlaySpaceAdapter overlayAdapter, DBTraceReferenceManager referenceManager)
throws CancelledException, IOException { throws CancelledException, IOException {
return createTraceManager("Code Manager", return createTraceManager("Code Manager",
(openMode, monitor) -> new DBTraceCodeManager(dbh, openMode, rwLock, monitor, (openMode, monitor) -> new DBTraceCodeManager(dbh, openMode, rwLock, monitor,
baseLanguage, this, threadManager, languageManager, dataTypeManager, overlayAdapter, baseLanguage, this, threadManager, platformManager, dataTypeManager, overlayAdapter,
referenceManager)); referenceManager));
} }
@ -325,11 +325,11 @@ public class DBTrace extends DBCachedDomainObjectAdapter implements Trace, Trace
} }
@DependentService @DependentService
protected DBTraceLanguageManager createLanguageManager() protected DBTracePlatformManager createPlatformManager()
throws CancelledException, IOException { throws CancelledException, IOException {
return createTraceManager("Language Manager", return createTraceManager("Language Manager",
(openMode, monitor) -> new DBTraceLanguageManager(dbh, openMode, rwLock, monitor, (openMode, monitor) -> new DBTracePlatformManager(dbh, openMode, rwLock, monitor,
baseLanguage, this)); baseCompilerSpec, this));
} }
@DependentService @DependentService
@ -373,11 +373,11 @@ public class DBTrace extends DBCachedDomainObjectAdapter implements Trace, Trace
@DependentService @DependentService
protected DBTraceRegisterContextManager createRegisterContextManager( protected DBTraceRegisterContextManager createRegisterContextManager(
DBTraceThreadManager threadManager, DBTraceLanguageManager languageManager) DBTraceThreadManager threadManager, DBTracePlatformManager platformManager)
throws CancelledException, IOException { throws CancelledException, IOException {
return createTraceManager("Context Manager", return createTraceManager("Context Manager",
(openMode, monitor) -> new DBTraceRegisterContextManager(dbh, openMode, rwLock, monitor, (openMode, monitor) -> new DBTraceRegisterContextManager(dbh, openMode, rwLock, monitor,
baseLanguage, this, threadManager, languageManager)); baseLanguage, this, threadManager, platformManager));
} }
@DependentService @DependentService
@ -497,8 +497,8 @@ public class DBTrace extends DBCachedDomainObjectAdapter implements Trace, Trace
} }
@Override @Override
public DBTraceLanguageManager getLanguageManager() { public DBTracePlatformManager getPlatformManager() {
return languageManager; return platformManager;
} }
@Override @Override

View file

@ -29,6 +29,7 @@ import com.google.common.collect.*;
import db.*; import db.*;
import ghidra.program.model.address.*; import ghidra.program.model.address.*;
import ghidra.program.model.lang.CompilerSpecID;
import ghidra.program.model.lang.LanguageID; import ghidra.program.model.lang.LanguageID;
import ghidra.program.model.symbol.RefType; import ghidra.program.model.symbol.RefType;
import ghidra.program.model.symbol.RefTypeFactory; 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> public abstract static class AbstractOffsetSnapDBFieldCodec<OT extends DBAnnotatedObject>
extends AbstractDBFieldCodec<OffsetSnap, OT, BinaryField> { 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.model.listing.ProgramContext;
import ghidra.program.util.ProgramContextImpl; import ghidra.program.util.ProgramContextImpl;
import ghidra.trace.database.DBTrace; 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;
import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree.AbstractDBTraceAddressSnapRangePropertyMapData; import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree.AbstractDBTraceAddressSnapRangePropertyMapData;
import ghidra.trace.database.space.AbstractDBTraceSpaceBasedManager; 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<>(); protected final Map<Language, ProgramContext> defaultContexts = new HashMap<>();
public DBTraceRegisterContextManager(DBHandle dbh, DBOpenMode openMode, ReadWriteLock lock, public DBTraceRegisterContextManager(DBHandle dbh, DBOpenMode openMode, ReadWriteLock lock,
TaskMonitor monitor, Language baseLanguage, DBTrace trace, TaskMonitor monitor, Language baseLanguage, DBTrace trace,
DBTraceThreadManager threadManager, DBTraceLanguageManager languageManager) DBTraceThreadManager threadManager, DBTracePlatformManager languageManager)
throws VersionException, IOException { throws VersionException, IOException {
super(NAME, dbh, openMode, lock, monitor, baseLanguage, trace, threadManager); super(NAME, dbh, openMode, lock, monitor, baseLanguage, trace, threadManager);
this.languageManager = languageManager; this.languageManager = languageManager;

View file

@ -32,6 +32,7 @@ import ghidra.program.model.lang.*;
import ghidra.trace.database.DBTrace; import ghidra.trace.database.DBTrace;
import ghidra.trace.database.DBTraceUtils; import ghidra.trace.database.DBTraceUtils;
import ghidra.trace.database.context.DBTraceRegisterContextManager.DBTraceRegisterContextEntry; 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.DBTraceAddressSnapRangePropertyMapAddressSetView;
import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapSpace; import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapSpace;
import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree.TraceAddressSnapRangeQuery; import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree.TraceAddressSnapRangeQuery;
@ -70,8 +71,8 @@ public class DBTraceRegisterContextSpace implements TraceRegisterContextSpace, D
super(store, record); super(store, record);
} }
void set(int langKey, Register register) { void set(DBTraceGuestLanguage guest, Register register) {
this.langKey = langKey; this.langKey = (int) (guest == null ? -1 : guest.getKey());
this.register = register.getName(); this.register = register.getName();
update(LANGUAGE_COLUMN, REGISTER_COLUMN); update(LANGUAGE_COLUMN, REGISTER_COLUMN);
} }
@ -110,7 +111,8 @@ public class DBTraceRegisterContextSpace implements TraceRegisterContextSpace, D
protected void loadRegisterValueMaps() throws VersionException { protected void loadRegisterValueMaps() throws VersionException {
for (DBTraceRegisterEntry ent : registerStore.asMap().values()) { 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); Register register = language.getRegister(ent.register);
ImmutablePair<Language, Register> pair = new ImmutablePair<>(language, register); ImmutablePair<Language, Register> pair = new ImmutablePair<>(language, register);
if (ent.map == null) { if (ent.map == null) {
@ -161,12 +163,12 @@ public class DBTraceRegisterContextSpace implements TraceRegisterContextSpace, D
protected DBTraceAddressSnapRangePropertyMapSpace<byte[], DBTraceRegisterContextEntry> getRegisterValueMap( protected DBTraceAddressSnapRangePropertyMapSpace<byte[], DBTraceRegisterContextEntry> getRegisterValueMap(
Language language, Register register, boolean createIfAbsent) { Language language, Register register, boolean createIfAbsent) {
ImmutablePair<Language, Register> pair = new ImmutablePair<>(language, register); ImmutablePair<Language, Register> pair = new ImmutablePair<>(language, register);
int langKey = manager.languageManager.getKeyForLanguage(language); DBTraceGuestLanguage guest = manager.languageManager.getLanguageByLanguage(language);
if (createIfAbsent) { if (createIfAbsent) {
return registerValueMaps.computeIfAbsent(pair, t -> { return registerValueMaps.computeIfAbsent(pair, t -> {
try { try {
DBTraceRegisterEntry ent = registerStore.create(); DBTraceRegisterEntry ent = registerStore.create();
ent.set(langKey, register); ent.set(guest, register);
return createRegisterValueMap(t); return createRegisterValueMap(t);
} }
catch (VersionException e) { catch (VersionException e) {

View file

@ -13,8 +13,9 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package ghidra.trace.database.language; package ghidra.trace.database.guest;
import java.io.IOException;
import java.util.*; import java.util.*;
import java.util.Map.Entry; import java.util.Map.Entry;
@ -22,13 +23,17 @@ import com.google.common.collect.Range;
import db.DBRecord; import db.DBRecord;
import ghidra.app.util.PseudoInstruction; import ghidra.app.util.PseudoInstruction;
import ghidra.lifecycle.Internal;
import ghidra.program.model.address.*; import ghidra.program.model.address.*;
import ghidra.program.model.lang.*; import ghidra.program.model.lang.*;
import ghidra.program.model.listing.Instruction; import ghidra.program.model.listing.Instruction;
import ghidra.program.model.mem.DumbMemBufferImpl; import ghidra.program.model.mem.DumbMemBufferImpl;
import ghidra.program.model.mem.MemBuffer; 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.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.LockHold;
import ghidra.util.database.*; import ghidra.util.database.*;
import ghidra.util.database.annot.*; import ghidra.util.database.annot.*;
@ -37,59 +42,129 @@ import ghidra.util.exception.VersionException;
import ghidra.util.task.TaskMonitor; import ghidra.util.task.TaskMonitor;
@DBAnnotatedObjectInfo(version = 0) @DBAnnotatedObjectInfo(version = 0)
public class DBTraceGuestLanguage extends DBAnnotatedObject implements TraceGuestLanguage { public class DBTraceGuestPlatform extends DBAnnotatedObject implements TraceGuestPlatform {
public static final String TABLE_NAME = "Languages"; public static final String TABLE_NAME = "Platforms";
static final String LANGID_COLUMN_NAME = "ID"; @DBAnnotatedObjectInfo(version = 0)
static final String VERSION_COLUMN_NAME = "Version"; public static class DBTraceGuestLanguage extends DBAnnotatedObject {
static final String MINOR_VERSION_COLUMN_NAME = "MinorVersion"; public static final String TABLE_NAME = "Languages";
@DBAnnotatedColumn(LANGID_COLUMN_NAME) static final String LANGID_COLUMN_NAME = "Lang";
static DBObjectColumn LANGID_COLUMN; static final String VERSION_COLUMN_NAME = "Version";
@DBAnnotatedColumn(VERSION_COLUMN_NAME) static final String MINOR_VERSION_COLUMN_NAME = "MinorVersion";
static DBObjectColumn VERSION_COLUMN;
@DBAnnotatedColumn(MINOR_VERSION_COLUMN_NAME)
static DBObjectColumn MINOR_VERSION_COLUMN;
@DBAnnotatedField(column = LANGID_COLUMN_NAME, codec = LanguageIDDBFieldCodec.class) @DBAnnotatedColumn(LANGID_COLUMN_NAME)
private LanguageID langID; static DBObjectColumn LANGID_COLUMN;
@DBAnnotatedField(column = VERSION_COLUMN_NAME) @DBAnnotatedColumn(VERSION_COLUMN_NAME)
private int version; static DBObjectColumn VERSION_COLUMN;
@DBAnnotatedField(column = MINOR_VERSION_COLUMN_NAME) @DBAnnotatedColumn(MINOR_VERSION_COLUMN_NAME)
private int minorVersion; 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<>(); new TreeMap<>();
protected final AddressSet hostAddressSet = new AddressSet(); protected final AddressSet hostAddressSet = new AddressSet();
protected final NavigableMap<Address, DBTraceGuestLanguageMappedRange> rangesByGuestAddress = protected final NavigableMap<Address, DBTraceGuestPlatformMappedRange> rangesByGuestAddress =
new TreeMap<>(); new TreeMap<>();
protected final AddressSet guestAddressSet = new AddressSet(); protected final AddressSet guestAddressSet = new AddressSet();
public DBTraceGuestLanguage(DBTraceLanguageManager manager, DBCachedObjectStore<?> store, public DBTraceGuestPlatform(DBTracePlatformManager manager, DBCachedObjectStore<?> store,
DBRecord record) { DBRecord record) {
super(store, record); super(store, record);
this.manager = manager; this.manager = manager;
} }
void setLanguage(Language language) { void set(CompilerSpec compilerSpec) {
this.guestLanguage = language; this.languageEntry = manager.getOrCreateLanguage(compilerSpec.getLanguage());
this.langID = language.getLanguageID(); this.langKey = (int) (languageEntry == null ? -1 : languageEntry.getKey());
this.version = language.getVersion(); this.cSpecID = compilerSpec.getCompilerSpecID();
this.minorVersion = language.getMinorVersion(); update(LANGKEY_COLUMN, CSPECID_COLUMN);
update(LANGID_COLUMN, VERSION_COLUMN, MINOR_VERSION_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 { throws CancelledException {
try (LockHold hold = LockHold.lock(manager.lock.writeLock())) { try (LockHold hold = LockHold.lock(manager.lock.writeLock())) {
manager.trace.getCodeManager() manager.trace.getCodeManager()
.clearLanguage(Range.all(), range.getHostRange(), .clearPlatform(Range.all(), range.getHostRange(), this, monitor);
(int) getKey(), monitor);
manager.rangeMappingStore.delete(range); manager.rangeMappingStore.delete(range);
AddressRange hostRange = range.getHostRange(); AddressRange hostRange = range.getHostRange();
AddressRange guestRange = range.getGuestRange(); AddressRange guestRange = range.getGuestRange();
@ -100,41 +175,43 @@ public class DBTraceGuestLanguage extends DBAnnotatedObject implements TraceGues
} }
} }
protected void doGetLanguage(LanguageService langServ) @Internal
throws LanguageNotFoundException, VersionException { public DBTraceGuestLanguage getLanguageEntry() {
this.guestLanguage = langServ.getLanguage(langID); return languageEntry;
if (version != guestLanguage.getVersion() ||
minorVersion != guestLanguage.getMinorVersion()) {
throw new VersionException(); // TODO Upgrade
}
} }
@Override @Override
public Language getLanguage() { public Language getLanguage() {
return guestLanguage; return languageEntry == null ? manager.baseLanguage : languageEntry.getLanguage();
}
@Override
public CompilerSpec getCompilerSpec() {
return compilerSpec;
} }
@Override @Override
public void delete(TaskMonitor monitor) throws CancelledException { public void delete(TaskMonitor monitor) throws CancelledException {
manager.deleteGuestLanguage(this, monitor); manager.deleteGuestPlatform(this, monitor);
// TODO: Delete language once no platform uses it?
} }
@Override @Override
public DBTraceGuestLanguageMappedRange addMappedRange(Address hostStart, Address guestStart, public DBTraceGuestPlatformMappedRange addMappedRange(Address hostStart, Address guestStart,
long length) throws AddressOverflowException { long length) throws AddressOverflowException {
try (LockHold hold = LockHold.lock(manager.lock.writeLock())) { 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)) { if (hostAddressSet.intersects(hostStart, hostEnd)) {
// TODO: Check for compatibility and extend? // TODO: Check for compatibility and extend?
throw new IllegalArgumentException( throw new IllegalArgumentException(
"Range overlaps existing host mapped range(s) for this guest language"); "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)) { if (guestAddressSet.intersects(guestStart, guestEnd)) {
throw new IllegalArgumentException("Range overlaps existing guest mapped range(s)"); throw new IllegalArgumentException("Range overlaps existing guest mapped range(s)");
} }
DBTraceGuestLanguageMappedRange mappedRange = manager.rangeMappingStore.create(); DBTraceGuestPlatformMappedRange mappedRange = manager.rangeMappingStore.create();
mappedRange.set(hostStart, guestLanguage, guestStart, length); mappedRange.set(hostStart, this, guestStart, length);
rangesByHostAddress.put(hostStart, mappedRange); rangesByHostAddress.put(hostStart, mappedRange);
rangesByGuestAddress.put(guestStart, mappedRange); rangesByGuestAddress.put(guestStart, mappedRange);
hostAddressSet.add(mappedRange.getHostRange()); hostAddressSet.add(mappedRange.getHostRange());
@ -156,7 +233,7 @@ public class DBTraceGuestLanguage extends DBAnnotatedObject implements TraceGues
@Override @Override
public Address mapHostToGuest(Address hostAddress) { public Address mapHostToGuest(Address hostAddress) {
try (LockHold hold = LockHold.lock(manager.lock.readLock())) { try (LockHold hold = LockHold.lock(manager.lock.readLock())) {
Entry<Address, DBTraceGuestLanguageMappedRange> floorEntry = Entry<Address, DBTraceGuestPlatformMappedRange> floorEntry =
rangesByHostAddress.floorEntry(hostAddress); rangesByHostAddress.floorEntry(hostAddress);
if (floorEntry == null) { if (floorEntry == null) {
return null; return null;
@ -168,7 +245,7 @@ public class DBTraceGuestLanguage extends DBAnnotatedObject implements TraceGues
@Override @Override
public Address mapGuestToHost(Address guestAddress) { public Address mapGuestToHost(Address guestAddress) {
try (LockHold hold = LockHold.lock(manager.lock.readLock())) { try (LockHold hold = LockHold.lock(manager.lock.readLock())) {
Entry<Address, DBTraceGuestLanguageMappedRange> floorEntry = Entry<Address, DBTraceGuestPlatformMappedRange> floorEntry =
rangesByGuestAddress.floorEntry(guestAddress); rangesByGuestAddress.floorEntry(guestAddress);
if (floorEntry == null) { if (floorEntry == null) {
return null; return null;
@ -186,12 +263,12 @@ public class DBTraceGuestLanguage extends DBAnnotatedObject implements TraceGues
*/ */
public Address mapGuestToHost(Address guestMin, Address guestMax) { public Address mapGuestToHost(Address guestMin, Address guestMax) {
try (LockHold hold = LockHold.lock(manager.lock.readLock())) { try (LockHold hold = LockHold.lock(manager.lock.readLock())) {
Entry<Address, DBTraceGuestLanguageMappedRange> floorEntry = Entry<Address, DBTraceGuestPlatformMappedRange> floorEntry =
rangesByGuestAddress.floorEntry(guestMin); rangesByGuestAddress.floorEntry(guestMin);
if (floorEntry == null) { if (floorEntry == null) {
return null; return null;
} }
DBTraceGuestLanguageMappedRange range = floorEntry.getValue(); DBTraceGuestPlatformMappedRange range = floorEntry.getValue();
if (!range.getGuestRange().contains(guestMax)) { if (!range.getGuestRange().contains(guestMax)) {
return null; return null;
} }
@ -204,7 +281,7 @@ public class DBTraceGuestLanguage extends DBAnnotatedObject implements TraceGues
/*return new DBTraceGuestLanguageMappedMemBuffer(manager.trace.getMemoryManager(), this, snap, /*return new DBTraceGuestLanguageMappedMemBuffer(manager.trace.getMemoryManager(), this, snap,
guestAddress);*/ guestAddress);*/
return new DumbMemBufferImpl( return new DumbMemBufferImpl(
new DBTraceGuestLanguageMappedMemory(manager.trace.getMemoryManager(), this, snap), new DBTraceGuestPlatformMappedMemory(manager.trace.getMemoryManager(), this, snap),
guestAddress); guestAddress);
} }
@ -212,7 +289,7 @@ public class DBTraceGuestLanguage extends DBAnnotatedObject implements TraceGues
public InstructionSet mapGuestInstructionAddressesToHost(InstructionSet instructionSet) { public InstructionSet mapGuestInstructionAddressesToHost(InstructionSet instructionSet) {
try (LockHold hold = LockHold.lock(manager.lock.readLock())) { try (LockHold hold = LockHold.lock(manager.lock.readLock())) {
Map<Address, InstructionBlock> blocksByNext = new HashMap<>(); Map<Address, InstructionBlock> blocksByNext = new HashMap<>();
InstructionSet mappedSet = new InstructionSet(guestLanguage.getAddressFactory()); InstructionSet mappedSet = new InstructionSet(getAddressFactory());
for (InstructionBlock block : instructionSet) { for (InstructionBlock block : instructionSet) {
for (Instruction instruction : block) { for (Instruction instruction : block) {
Address hostAddr = Address hostAddr =

View file

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package ghidra.trace.database.language; package ghidra.trace.database.guest;
import static ghidra.lifecycle.Unfinished.TODO; import static ghidra.lifecycle.Unfinished.TODO;
@ -32,6 +32,7 @@ import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.*; import ghidra.program.model.mem.*;
import ghidra.trace.database.memory.DBTraceMemoryManager; import ghidra.trace.database.memory.DBTraceMemoryManager;
import ghidra.trace.database.memory.DBTraceMemorySpace; import ghidra.trace.database.memory.DBTraceMemorySpace;
import ghidra.util.MathUtilities;
import ghidra.util.exception.CancelledException; import ghidra.util.exception.CancelledException;
import ghidra.util.exception.NotFoundException; import ghidra.util.exception.NotFoundException;
import ghidra.util.task.TaskMonitor; import ghidra.util.task.TaskMonitor;
@ -39,15 +40,16 @@ import ghidra.util.task.TaskMonitor;
/** /**
* TODO: Document me * TODO: Document me
* *
* <p>
* Note this is the bare minimum to support {@link DumbMemBufferImpl} * 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 DBTraceMemoryManager manager;
protected final DBTraceGuestLanguage guest; protected final DBTraceGuestPlatform guest;
protected final long snap; protected final long snap;
public DBTraceGuestLanguageMappedMemory(DBTraceMemoryManager manager, public DBTraceGuestPlatformMappedMemory(DBTraceMemoryManager manager,
DBTraceGuestLanguage guest, long snap) { DBTraceGuestPlatform guest, long snap) {
this.manager = manager; this.manager = manager;
this.guest = guest; this.guest = guest;
this.snap = snap; this.snap = snap;
@ -358,17 +360,17 @@ public class DBTraceGuestLanguageMappedMemory implements Memory {
while (buffer.hasRemaining()) { while (buffer.hasRemaining()) {
int offset = buffer.position() - startPos; int offset = buffer.position() - startPos;
Address guestCur = guestStart.add(offset); Address guestCur = guestStart.add(offset);
Entry<Address, DBTraceGuestLanguageMappedRange> floorEntry = Entry<Address, DBTraceGuestPlatformMappedRange> floorEntry =
guest.rangesByGuestAddress.floorEntry(guestCur); guest.rangesByGuestAddress.floorEntry(guestCur);
if (floorEntry == null) { if (floorEntry == null) {
return offset; return offset;
} }
DBTraceGuestLanguageMappedRange range = floorEntry.getValue(); DBTraceGuestPlatformMappedRange range = floorEntry.getValue();
Address hostCur = range.mapGuestToHost(guestCur); Address hostCur = range.mapGuestToHost(guestCur);
if (hostCur == null) { if (hostCur == null) {
return offset; return offset;
} }
int lenToRead = (int) Math.min(buffer.remaining(), int lenToRead = MathUtilities.unsignedMin(buffer.remaining(),
range.getGuestRange().getMaxAddress().subtract(guestStart) + 1); range.getGuestRange().getMaxAddress().subtract(guestStart) + 1);
DBTraceMemorySpace hostSpace = manager.getMemorySpace(hostCur.getAddressSpace(), false); DBTraceMemorySpace hostSpace = manager.getMemorySpace(hostCur.getAddressSpace(), false);
if (hostSpace == null) { if (hostSpace == null) {

View file

@ -13,22 +13,23 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package ghidra.trace.database.language; package ghidra.trace.database.guest;
import java.io.IOException; import java.io.IOException;
import db.DBRecord; import db.DBRecord;
import ghidra.program.model.address.*; import ghidra.program.model.address.*;
import ghidra.program.model.lang.CompilerSpec;
import ghidra.program.model.lang.Language; 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.*;
import ghidra.util.database.annot.*; import ghidra.util.database.annot.*;
import ghidra.util.exception.CancelledException; import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor; import ghidra.util.task.TaskMonitor;
@DBAnnotatedObjectInfo(version = 0) @DBAnnotatedObjectInfo(version = 0)
public class DBTraceGuestLanguageMappedRange extends DBAnnotatedObject public class DBTraceGuestPlatformMappedRange extends DBAnnotatedObject
implements TraceGuestLanguageMappedRange { implements TraceGuestPlatformMappedRange {
public static final String TABLE_NAME = "LanguageMappings"; public static final String TABLE_NAME = "LanguageMappings";
static final String HOST_SPACE_COLUMN_NAME = "HostSpace"; static final String HOST_SPACE_COLUMN_NAME = "HostSpace";
@ -56,7 +57,7 @@ public class DBTraceGuestLanguageMappedRange extends DBAnnotatedObject
@DBAnnotatedField(column = HOST_OFFSET_COLUMN_NAME) @DBAnnotatedField(column = HOST_OFFSET_COLUMN_NAME)
private long hostOffset; private long hostOffset;
@DBAnnotatedField(column = GUEST_LANGUAGE_COLUMN_NAME) @DBAnnotatedField(column = GUEST_LANGUAGE_COLUMN_NAME)
int guestLangKey; int guestPlatformKey;
@DBAnnotatedField(column = GUEST_SPACE_COLUMN_NAME) @DBAnnotatedField(column = GUEST_SPACE_COLUMN_NAME)
private int guestSpace; private int guestSpace;
@DBAnnotatedField(column = GUEST_OFFSET_COLUMN_NAME) @DBAnnotatedField(column = GUEST_OFFSET_COLUMN_NAME)
@ -64,13 +65,13 @@ public class DBTraceGuestLanguageMappedRange extends DBAnnotatedObject
@DBAnnotatedField(column = LENGTH_COLUMN_NAME) @DBAnnotatedField(column = LENGTH_COLUMN_NAME)
private long length; private long length;
private DBTraceLanguageManager manager; private DBTracePlatformManager manager;
private AddressRangeImpl hostRange; private AddressRangeImpl hostRange;
private Language guestLanguage; private DBTraceGuestPlatform guestPlatform;
private AddressRangeImpl guestRange; private AddressRangeImpl guestRange;
public DBTraceGuestLanguageMappedRange(DBTraceLanguageManager manager, DBCachedObjectStore<?> s, public DBTraceGuestPlatformMappedRange(DBTracePlatformManager manager, DBCachedObjectStore<?> s,
DBRecord r) { DBRecord r) {
super(s, r); super(s, r);
this.manager = manager; this.manager = manager;
@ -88,9 +89,9 @@ public class DBTraceGuestLanguageMappedRange extends DBAnnotatedObject
Address hostEnd = hostStart.addNoWrap(length - 1); Address hostEnd = hostStart.addNoWrap(length - 1);
this.hostRange = new AddressRangeImpl(hostStart, hostEnd); this.hostRange = new AddressRangeImpl(hostStart, hostEnd);
this.guestLanguage = manager.getLanguageByKey(guestLangKey); this.guestPlatform = manager.getPlatformByKey(guestPlatformKey);
Address guestStart = Address guestStart =
guestLanguage.getAddressFactory().getAddress(guestSpace, guestOffset); guestPlatform.getAddressFactory().getAddress(guestSpace, guestOffset);
Address guestEnd = guestStart.addNoWrap(length - 1); Address guestEnd = guestStart.addNoWrap(length - 1);
this.guestRange = new AddressRangeImpl(guestStart, guestEnd); 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) { void set(Address hostStart, DBTraceGuestPlatform guestPlatform, Address guestStart,
this.hostRange = new AddressRangeImpl(hostStart, hostStart.add(length - 1)); long length) {
this.guestLanguage = guestLanguage;
this.guestRange = new AddressRangeImpl(guestStart, guestStart.add(length - 1));
this.hostSpace = hostStart.getAddressSpace().getSpaceID(); this.hostSpace = hostStart.getAddressSpace().getSpaceID();
this.hostOffset = hostStart.getOffset(); this.hostOffset = hostStart.getOffset();
this.guestLangKey = manager.getKeyForLanguage(guestLanguage); this.guestPlatformKey = (int) guestPlatform.getKey();
this.guestSpace = guestStart.getAddressSpace().getSpaceID(); this.guestSpace = guestStart.getAddressSpace().getSpaceID();
this.guestOffset = guestStart.getOffset(); this.guestOffset = guestStart.getOffset();
this.length = length; this.length = length;
update(HOST_SPACE_COLUMN, HOST_OFFSET_COLUMN, GUEST_LANGUAGE_COLUMN, GUEST_SPACE_COLUMN, update(HOST_SPACE_COLUMN, HOST_OFFSET_COLUMN, GUEST_LANGUAGE_COLUMN, GUEST_SPACE_COLUMN,
GUEST_OFFSET_COLUMN, LENGTH_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 @Override
@ -119,14 +122,19 @@ public class DBTraceGuestLanguageMappedRange extends DBAnnotatedObject
return manager.getBaseLanguage(); return manager.getBaseLanguage();
} }
@Override
public CompilerSpec getHostCompilerSpec() {
return manager.getBaseCompilerSpec();
}
@Override @Override
public AddressRange getHostRange() { public AddressRange getHostRange() {
return hostRange; return hostRange;
} }
@Override @Override
public Language getGuestLanguage() { public DBTraceGuestPlatform getGuestPlatform() {
return guestLanguage; return guestPlatform;
} }
@Override @Override
@ -154,6 +162,6 @@ public class DBTraceGuestLanguageMappedRange extends DBAnnotatedObject
@Override @Override
public void delete(TaskMonitor monitor) throws CancelledException { 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.program.model.lang.Language;
import ghidra.trace.database.DBTrace; import ghidra.trace.database.DBTrace;
import ghidra.trace.database.data.DBTraceDataSettingsAdapter.DBTraceDataSettingsSpace; import ghidra.trace.database.data.DBTraceDataSettingsAdapter.DBTraceDataSettingsSpace;
import ghidra.trace.model.guest.TraceGuestPlatform;
import ghidra.trace.model.thread.TraceThread; import ghidra.trace.model.thread.TraceThread;
import ghidra.util.LockHold; import ghidra.util.LockHold;
@ -82,6 +83,11 @@ public abstract class AbstractDBTraceDataComponent implements DBTraceDefinedData
return root.getThread(); return root.getThread();
} }
@Override
public TraceGuestPlatform getGuestPlatform() {
return root.getGuestPlatform();
}
@Override @Override
public Language getLanguage() { public Language getLanguage() {
return root.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.CacheBuilder;
import com.google.common.cache.RemovalNotification; import com.google.common.cache.RemovalNotification;
import com.google.common.collect.*; import com.google.common.collect.Range;
import db.DBHandle; import db.DBHandle;
import db.DBRecord; import db.DBRecord;
import ghidra.lifecycle.Internal;
import ghidra.program.model.address.*; import ghidra.program.model.address.*;
import ghidra.program.model.lang.*; import ghidra.program.model.lang.*;
import ghidra.program.model.listing.ContextChangeException; 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.AddressDBFieldCodec;
import ghidra.trace.database.address.DBTraceOverlaySpaceAdapter.DecodesAddresses; import ghidra.trace.database.address.DBTraceOverlaySpaceAdapter.DecodesAddresses;
import ghidra.trace.database.data.DBTraceDataTypeManager; 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.map.DBTraceAddressSnapRangePropertyMapTree.TraceAddressSnapRangeQuery;
import ghidra.trace.database.space.AbstractDBTraceSpaceBasedManager; import ghidra.trace.database.space.AbstractDBTraceSpaceBasedManager;
import ghidra.trace.database.space.DBTraceDelegatingManager; import ghidra.trace.database.space.DBTraceDelegatingManager;
@ -90,7 +93,7 @@ public class DBTraceCodeManager
static DBObjectColumn DELAY_COLUMN; static DBObjectColumn DELAY_COLUMN;
@DBAnnotatedField(column = LANGUAGE_COLUMN_NAME) @DBAnnotatedField(column = LANGUAGE_COLUMN_NAME)
private int langKey; private int languageKey;
@DBAnnotatedField(column = BYTES_COLUMN_NAME) @DBAnnotatedField(column = BYTES_COLUMN_NAME)
private byte[] bytes; private byte[] bytes;
@DBAnnotatedField(column = CONTEXT_COLUMN_NAME) @DBAnnotatedField(column = CONTEXT_COLUMN_NAME)
@ -100,6 +103,8 @@ public class DBTraceCodeManager
@DBAnnotatedField(column = DELAY_COLUMN_NAME) @DBAnnotatedField(column = DELAY_COLUMN_NAME)
private boolean delaySlot; private boolean delaySlot;
private InstructionPrototype prototype;
private DBTraceCodeManager manager; private DBTraceCodeManager manager;
public DBTraceCodePrototypeEntry(DBTraceCodeManager manager, DBCachedObjectStore<?> store, public DBTraceCodePrototypeEntry(DBTraceCodeManager manager, DBCachedObjectStore<?> store,
@ -113,21 +118,33 @@ public class DBTraceCodeManager
return manager.overlayAdapter; return manager.overlayAdapter;
} }
void set(int langKey, byte[] bytes, byte[] context, Address address, boolean delaySlot) { void set(DBTraceGuestLanguage languageEntry, byte[] bytes, byte[] context, Address address,
this.langKey = langKey; boolean delaySlot) {
this.languageKey = (int) (languageEntry == null ? -1 : languageEntry.getKey());
this.bytes = bytes; this.bytes = bytes;
this.context = context; this.context = context;
this.address = address; this.address = address;
this.delaySlot = delaySlot; this.delaySlot = delaySlot;
update(LANGUAGE_COLUMN, BYTES_COLUMN, CONTEXT_COLUMN, ADDRESS_COLUMN, DELAY_COLUMN); update(LANGUAGE_COLUMN, BYTES_COLUMN, CONTEXT_COLUMN, ADDRESS_COLUMN, DELAY_COLUMN);
this.prototype = parsePrototype();
} }
int getLanguageKey() { @Override
return langKey; protected void fresh(boolean created) throws IOException {
super.fresh(created);
if (created) {
return;
}
this.prototype = parsePrototype();
} }
InstructionPrototype parsePrototype() { public InstructionPrototype getPrototype() {
Language language = manager.languageManager.getLanguageByKey(langKey); 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()); MemBuffer memBuffer = new ByteMemBufferImpl(address, bytes, language.isBigEndian());
ProcessorContext ctx = ProcessorContext ctx =
new ProtoProcessorContext(getBaseContextValue(language, context, address)); new ProtoProcessorContext(getBaseContextValue(language, context, address));
@ -168,13 +185,14 @@ public class DBTraceCodeManager
return max; return max;
} }
protected final DBTraceLanguageManager languageManager; protected final DBTracePlatformManager platformManager;
protected final DBTraceDataTypeManager dataTypeManager; protected final DBTraceDataTypeManager dataTypeManager;
protected final DBTraceOverlaySpaceAdapter overlayAdapter; protected final DBTraceOverlaySpaceAdapter overlayAdapter;
protected final DBTraceReferenceManager referenceManager; protected final DBTraceReferenceManager referenceManager;
protected final DBCachedObjectStore<DBTraceCodePrototypeEntry> protoStore; 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 DBTraceCodeUnitsMemoryView codeUnits = new DBTraceCodeUnitsMemoryView(this);
protected final DBTraceInstructionsMemoryView instructions = protected final DBTraceInstructionsMemoryView instructions =
@ -196,11 +214,11 @@ public class DBTraceCodeManager
public DBTraceCodeManager(DBHandle dbh, DBOpenMode openMode, ReadWriteLock lock, public DBTraceCodeManager(DBHandle dbh, DBOpenMode openMode, ReadWriteLock lock,
TaskMonitor monitor, Language baseLanguage, DBTrace trace, TaskMonitor monitor, Language baseLanguage, DBTrace trace,
DBTraceThreadManager threadManager, DBTraceLanguageManager languageManager, DBTraceThreadManager threadManager, DBTracePlatformManager platformManager,
DBTraceDataTypeManager dataTypeManager, DBTraceOverlaySpaceAdapter overlayAdapter, DBTraceDataTypeManager dataTypeManager, DBTraceOverlaySpaceAdapter overlayAdapter,
DBTraceReferenceManager referenceManager) throws IOException, VersionException { DBTraceReferenceManager referenceManager) throws IOException, VersionException {
super(NAME, dbh, openMode, lock, monitor, baseLanguage, trace, threadManager); super(NAME, dbh, openMode, lock, monitor, baseLanguage, trace, threadManager);
this.languageManager = languageManager; this.platformManager = platformManager;
this.dataTypeManager = dataTypeManager; this.dataTypeManager = dataTypeManager;
this.overlayAdapter = overlayAdapter; this.overlayAdapter = overlayAdapter;
this.referenceManager = referenceManager; this.referenceManager = referenceManager;
@ -230,7 +248,7 @@ public class DBTraceCodeManager
// NOTE: Should already own write lock // NOTE: Should already own write lock
for (DBTraceCodePrototypeEntry protoEnt : protoStore.asMap().values()) { for (DBTraceCodePrototypeEntry protoEnt : protoStore.asMap().values()) {
// NOTE: No need to check if it exists. This is only called on new or after clear // 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); return Arrays.copyOfRange(bytes, bytes.length / 2, bytes.length);
} }
protected int doRecordPrototype(InstructionPrototype prototype, MemBuffer memBuffer, protected DBTraceCodePrototypeEntry doRecordPrototype(InstructionPrototype prototype,
ProcessorContextView context) { DBTraceGuestLanguage guest, MemBuffer memBuffer, ProcessorContextView context) {
DBTraceCodePrototypeEntry protoEnt = protoStore.create(); DBTraceCodePrototypeEntry protoEnt = protoStore.create();
byte[] bytes = new byte[prototype.getLength()]; byte[] bytes = new byte[prototype.getLength()];
if (memBuffer.getBytes(bytes, 0) != bytes.length) { if (memBuffer.getBytes(bytes, 0) != bytes.length) {
@ -255,20 +273,20 @@ public class DBTraceCodeManager
RegisterValue value = context.getRegisterValue(baseCtxReg); RegisterValue value = context.getRegisterValue(baseCtxReg);
ctx = value == null ? null : valueBytes(value); ctx = value == null ? null : valueBytes(value);
} }
protoEnt.set(languageManager.getKeyForLanguage(prototype.getLanguage()), bytes, ctx, protoEnt.set(guest, bytes, ctx, memBuffer.getAddress(), prototype.isInDelaySlot());
memBuffer.getAddress(), prototype.isInDelaySlot()); return protoEnt;
return (int) protoEnt.getKey();
} }
protected int findOrRecordPrototype(InstructionPrototype prototype, MemBuffer memBuffer, protected DBTraceCodePrototypeEntry findOrRecordPrototype(InstructionPrototype prototype,
ProcessorContextView context) { DBTraceGuestLanguage guest, MemBuffer memBuffer, ProcessorContextView context) {
// NOTE: Must already have write lock // NOTE: Must already have write lock
return protoMap.computeIfAbsent(prototype, return entriesByProto.computeIfAbsent(prototype,
p -> doRecordPrototype(prototype, memBuffer, context)); p -> doRecordPrototype(prototype, guest, memBuffer, context));
} }
protected InstructionPrototype getPrototypeByKey(int key) { protected InstructionPrototype getPrototypeByKey(int key) {
return protoMap.inverse().get(key); DBTraceCodePrototypeEntry protoEnt = protoStore.getObjectAt(key);
return protoEnt == null ? null : protoEnt.prototype;
} }
@Override @Override
@ -326,35 +344,43 @@ public class DBTraceCodeManager
return getForRegisterSpace(frame, createIfAbsent); return getForRegisterSpace(frame, createIfAbsent);
} }
// Internal @Internal
public void replaceDataTypes(long oldID, long newID) { public void replaceDataTypes(long oldID, long newID) {
TODO(); TODO();
} }
// Internal @Internal
public void clearData(LinkedList<Long> deletedDataTypeIds, TaskMonitor monitor) { public void clearData(LinkedList<Long> deletedDataTypeIds, TaskMonitor monitor) {
TODO(); TODO();
} }
// Internal @Internal
public void clearLanguage(Range<Long> span, AddressRange range, int langKey, public void clearPlatform(Range<Long> span, AddressRange range, DBTraceGuestPlatform guest,
TaskMonitor monitor) throws CancelledException { TaskMonitor monitor) throws CancelledException {
delegateDeleteV(range.getAddressSpace(), delegateDeleteV(range.getAddressSpace(),
m -> m.clearLanguage(span, range, langKey, monitor)); m -> m.clearPlatform(span, range, guest, monitor));
} }
// Internal @Internal
public void deleteLanguage(int langKey, TaskMonitor monitor) throws CancelledException { public void deletePlatform(DBTraceGuestPlatform guest, TaskMonitor monitor)
throws CancelledException {
// TODO: Use sub-monitors when available // TODO: Use sub-monitors when available
for (DBTraceCodeSpace codeSpace : memSpaces.values()) { 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()) { for (DBTraceCodeRegisterSpace codeSpace : regSpaces.values()) {
// TODO: I don't know any way to get guest instructions into register space // TODO: I don't know any way to get guest instructions into register space
// The mapping manager does (should) not allow guest register addresses // The mapping manager does (should) not allow guest register addresses
// TODO: Test this if I ever get guest data units // 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.setMessage("Clearing instruction prototypes");
monitor.setMaximum(protoStore.getRecordCount()); monitor.setMaximum(protoStore.getRecordCount());
for (Iterator<DBTraceCodePrototypeEntry> it = protoStore.asMap().values().iterator(); it for (Iterator<DBTraceCodePrototypeEntry> it = protoStore.asMap().values().iterator(); it
@ -362,11 +388,11 @@ public class DBTraceCodeManager
monitor.checkCanceled(); monitor.checkCanceled();
monitor.incrementProgress(1); monitor.incrementProgress(1);
DBTraceCodePrototypeEntry protoEnt = it.next(); DBTraceCodePrototypeEntry protoEnt = it.next();
if (langKey != protoEnt.langKey) { if (protoEnt.prototype.getLanguage() != guest.getLanguage()) {
continue; continue;
} }
it.remove(); it.remove();
protoMap.inverse().remove((int) protoEnt.getKey()); entriesByProto.remove(protoEnt.prototype);
} }
} }
@ -374,7 +400,7 @@ public class DBTraceCodeManager
public void invalidateCache(boolean all) { public void invalidateCache(boolean all) {
try (LockHold hold = LockHold.lock(lock.writeLock())) { try (LockHold hold = LockHold.lock(lock.writeLock())) {
protoStore.invalidateCache(); protoStore.invalidateCache();
protoMap.clear(); entriesByProto.clear();
loadPrototypes(); loadPrototypes();
super.invalidateCache(all); super.invalidateCache(all);

View file

@ -32,6 +32,7 @@ import ghidra.program.model.util.CodeUnitInsertionException;
import ghidra.trace.database.DBTrace; import ghidra.trace.database.DBTrace;
import ghidra.trace.database.DBTraceUtils; import ghidra.trace.database.DBTraceUtils;
import ghidra.trace.database.data.DBTraceDataTypeManager; import ghidra.trace.database.data.DBTraceDataTypeManager;
import ghidra.trace.database.guest.DBTraceGuestPlatform;
import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapSpace; import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapSpace;
import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree.TraceAddressSnapRangeQuery; import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree.TraceAddressSnapRangeQuery;
import ghidra.trace.database.space.AbstractDBTraceSpaceBasedManager.DBTraceSpaceEntry; import ghidra.trace.database.space.AbstractDBTraceSpaceBasedManager.DBTraceSpaceEntry;
@ -125,8 +126,8 @@ public class DBTraceCodeSpace implements TraceCodeSpace, DBTraceSpaceBased {
return new DBTraceCodeUnitsView(this); return new DBTraceCodeUnitsView(this);
} }
void clearLanguage(Range<Long> span, AddressRange range, int langKey, TaskMonitor monitor) void clearPlatform(Range<Long> span, AddressRange range, DBTraceGuestPlatform guest,
throws CancelledException { TaskMonitor monitor) throws CancelledException {
// Note "makeWay" does not apply here. // Note "makeWay" does not apply here.
// Units should be enclosed by guest mapping. // Units should be enclosed by guest mapping.
// TODO: Use sub-monitors when available // TODO: Use sub-monitors when available
@ -141,8 +142,7 @@ public class DBTraceCodeSpace implements TraceCodeSpace, DBTraceSpaceBased {
TraceAddressSnapRangeQuery.intersecting(range, span)).values()) { TraceAddressSnapRangeQuery.intersecting(range, span)).values()) {
monitor.checkCanceled(); monitor.checkCanceled();
monitor.incrementProgress(1); monitor.incrementProgress(1);
if (langKey != manager.protoStore.getObjectAt( if (instruction.guest != guest) {
instruction.getPrototypeKey()).getLanguageKey()) {
continue; continue;
} }
instructionMapSpace.deleteData(instruction); instructionMapSpace.deleteData(instruction);
@ -154,7 +154,7 @@ public class DBTraceCodeSpace implements TraceCodeSpace, DBTraceSpaceBased {
TraceAddressSnapRangeQuery.intersecting(range, span)).values()) { TraceAddressSnapRangeQuery.intersecting(range, span)).values()) {
monitor.checkCanceled(); monitor.checkCanceled();
monitor.incrementProgress(1); monitor.incrementProgress(1);
if (langKey != dataUnit.getLanguageKey()) { if (dataUnit.guest != guest) {
continue; continue;
} }
// TODO: I don't yet have guest-language data units. // 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.program.model.lang.Language;
import ghidra.trace.database.DBTraceUtils; import ghidra.trace.database.DBTraceUtils;
import ghidra.trace.database.data.DBTraceDataSettingsAdapter.DBTraceDataSettingsSpace; import ghidra.trace.database.data.DBTraceDataSettingsAdapter.DBTraceDataSettingsSpace;
import ghidra.trace.database.guest.DBTraceGuestPlatform;
import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree; import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree;
import ghidra.trace.model.guest.TraceGuestPlatform;
import ghidra.util.LockHold; import ghidra.util.LockHold;
import ghidra.util.database.DBCachedObjectStore; import ghidra.util.database.DBCachedObjectStore;
import ghidra.util.database.DBObjectColumn; import ghidra.util.database.DBObjectColumn;
@ -37,11 +39,11 @@ public class DBTraceData extends AbstractDBTraceCodeUnit<DBTraceData>
implements DBTraceDefinedDataAdapter { implements DBTraceDefinedDataAdapter {
private static final String TABLE_NAME = "Data"; 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"; static final String DATATYPE_COLUMN_NAME = "DataType";
@DBAnnotatedColumn(LANGUAGE_COLUMN_NAME) @DBAnnotatedColumn(PLATFORM_COLUMN_NAME)
static DBObjectColumn LANGUAGE_COLUMN; static DBObjectColumn PLATFORM_COLUMN;
@DBAnnotatedColumn(DATATYPE_COLUMN_NAME) @DBAnnotatedColumn(DATATYPE_COLUMN_NAME)
static DBObjectColumn DATATYPE_COLUMN; static DBObjectColumn DATATYPE_COLUMN;
@ -49,12 +51,12 @@ public class DBTraceData extends AbstractDBTraceCodeUnit<DBTraceData>
return DBTraceUtils.tableName(TABLE_NAME, space, threadKey, frameLevel); return DBTraceUtils.tableName(TABLE_NAME, space, threadKey, frameLevel);
} }
@DBAnnotatedField(column = LANGUAGE_COLUMN_NAME) @DBAnnotatedField(column = PLATFORM_COLUMN_NAME)
private int langKey; private int platformKey;
@DBAnnotatedField(column = DATATYPE_COLUMN_NAME) @DBAnnotatedField(column = DATATYPE_COLUMN_NAME)
private long dataTypeID; private long dataTypeID;
protected Language language; protected DBTraceGuestPlatform guest;
protected DataType dataType; protected DataType dataType;
protected DataType baseDataType; protected DataType baseDataType;
protected Settings defaultSettings; protected Settings defaultSettings;
@ -73,9 +75,9 @@ public class DBTraceData extends AbstractDBTraceCodeUnit<DBTraceData>
if (created) { if (created) {
return; return;
} }
language = space.manager.languageManager.getLanguageByKey(langKey); guest = space.manager.platformManager.getPlatformByKey(platformKey);
if (language == null) { if (guest == null && platformKey != -1) {
throw new IOException("Data table is corrupt. Missing language: " + langKey); throw new IOException("Data table is corrupt. Missing platform: " + platformKey);
} }
dataType = space.dataTypeManager.getDataType(dataTypeID); dataType = space.dataTypeManager.getDataType(dataTypeID);
if (dataType == null) { if (dataType == null) {
@ -100,16 +102,17 @@ public class DBTraceData extends AbstractDBTraceCodeUnit<DBTraceData>
return this; return this;
} }
protected void set(Language language, DataType dataType) { protected void set(DBTraceGuestPlatform platform, DataType dataType) {
this.language = language; this.platformKey = (int) (platform == null ? -1 : platform.getKey());
this.langKey = space.manager.languageManager.getKeyForLanguage(language);
this.dataTypeID = space.dataTypeManager.getResolvedID(dataType); 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 // Use the stored dataType, not the given one, in case it's different
this.dataType = space.dataTypeManager.getDataType(dataTypeID); this.dataType = space.dataTypeManager.getDataType(dataTypeID);
assert this.dataType != null; assert this.dataType != null;
this.baseDataType = getBaseDataType(this.dataType);
this.defaultSettings = this.dataType.getDefaultSettings(); this.defaultSettings = this.dataType.getDefaultSettings();
update(LANGUAGE_COLUMN, DATATYPE_COLUMN); this.baseDataType = getBaseDataType(this.dataType);
} }
protected int getDataTypeLength() { 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.... // TODO: Also need to know where this address maps into the other language's spaces....
// NOTE: Using default data space for now // NOTE: Using default data space for now
// TODO: I may not need this Pointer check, as clone(dtm) should adjust already // 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 return dataType.getLength(); // -1 is checked elsewhere
} }
@ -129,6 +132,11 @@ public class DBTraceData extends AbstractDBTraceCodeUnit<DBTraceData>
return dt; return dt;
} }
@Override
public TraceGuestPlatform getGuestPlatform() {
return guest;
}
@Override @Override
public void delete() { public void delete() {
try (LockHold hold = LockHold.lock(space.lock.writeLock())) { try (LockHold hold = LockHold.lock(space.lock.writeLock())) {
@ -149,11 +157,7 @@ public class DBTraceData extends AbstractDBTraceCodeUnit<DBTraceData>
@Override @Override
public Language getLanguage() { public Language getLanguage() {
return language; return guest == null ? space.baseLanguage : guest.getLanguage();
}
int getLanguageKey() {
return langKey;
} }
@Override @Override

View file

@ -145,7 +145,8 @@ public class DBTraceDefinedDataView extends AbstractBaseDBTraceDefinedUnitsView<
} }
DBTraceData created = space.dataMapSpace.put(tasr, null); 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? // TODO: Explicitly remove undefined from cache, or let weak refs take care of it?
cacheForContaining.notifyNewEntry(lifespan, createdRange, created); 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.DBTraceUtils;
import ghidra.trace.database.context.DBTraceRegisterContextManager; import ghidra.trace.database.context.DBTraceRegisterContextManager;
import ghidra.trace.database.context.DBTraceRegisterContextSpace; 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.map.DBTraceAddressSnapRangePropertyMapTree;
import ghidra.trace.database.symbol.DBTraceReference; import ghidra.trace.database.symbol.DBTraceReference;
import ghidra.trace.database.symbol.DBTraceReferenceSpace; import ghidra.trace.database.symbol.DBTraceReferenceSpace;
import ghidra.trace.model.Trace.TraceInstructionChangeType; import ghidra.trace.model.Trace.TraceInstructionChangeType;
import ghidra.trace.model.guest.TraceGuestPlatform;
import ghidra.trace.model.listing.TraceInstruction; import ghidra.trace.model.listing.TraceInstruction;
import ghidra.trace.model.symbol.TraceReference; import ghidra.trace.model.symbol.TraceReference;
import ghidra.trace.util.*; 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 byte FLOWOVERRIDE_CLEAR_MASK = ~FLOWOVERRIDE_SET_MASK;
private static final int FLOWOVERRIDE_SHIFT = 1; private static final int FLOWOVERRIDE_SHIFT = 1;
static final String PLATFORM_COLUMN_NAME = "Platform";
static final String PROTOTYPE_COLUMN_NAME = "Prototype"; static final String PROTOTYPE_COLUMN_NAME = "Prototype";
static final String FLAGS_COLUMN_NAME = "Flags"; static final String FLAGS_COLUMN_NAME = "Flags";
@DBAnnotatedColumn(PLATFORM_COLUMN_NAME)
static DBObjectColumn PLATFORM_COLUMN;
@DBAnnotatedColumn(PROTOTYPE_COLUMN_NAME) @DBAnnotatedColumn(PROTOTYPE_COLUMN_NAME)
static DBObjectColumn PROTOTYPE_COLUMN; static DBObjectColumn PROTOTYPE_COLUMN;
@DBAnnotatedColumn(FLAGS_COLUMN_NAME) @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) @DBAnnotatedField(column = PROTOTYPE_COLUMN_NAME)
private int prototypeKey; private int prototypeKey;
@DBAnnotatedField(column = FLAGS_COLUMN_NAME) @DBAnnotatedField(column = FLAGS_COLUMN_NAME)
@ -111,7 +118,7 @@ public class DBTraceInstruction extends AbstractDBTraceCodeUnit<DBTraceInstructi
protected FlowOverride flowOverride; protected FlowOverride flowOverride;
protected ParserContext parserContext; protected ParserContext parserContext;
protected DBTraceGuestLanguage guest; protected DBTraceGuestPlatform guest;
protected InstructionContext instructionContext; protected InstructionContext instructionContext;
public DBTraceInstruction(DBTraceCodeSpace space, public DBTraceInstruction(DBTraceCodeSpace space,
@ -120,17 +127,32 @@ public class DBTraceInstruction extends AbstractDBTraceCodeUnit<DBTraceInstructi
super(space, tree, store, record); super(space, tree, store, record);
} }
protected void doSetGuestMapping() { protected void doSetGuestMapping(final DBTraceGuestPlatform guest) {
if (Objects.equals(prototype.getLanguage(), space.baseLanguage)) { this.guest = guest;
guest = null; if (guest == null) {
instructionContext = this; instructionContext = this;
} }
else { else {
guest = space.trace.getLanguageManager().getGuestLanguage(prototype.getLanguage());
instructionContext = new GuestInstructionContext(); 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 @Override
protected void fresh(boolean created) throws IOException { protected void fresh(boolean created) throws IOException {
super.fresh(created); super.fresh(created);
@ -138,9 +160,12 @@ public class DBTraceInstruction extends AbstractDBTraceCodeUnit<DBTraceInstructi
// Wait for something to set prototype // Wait for something to set prototype
return; 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); prototype = space.manager.getPrototypeByKey(prototypeKey);
if (prototype == null) { if (prototype == null) {
// TODO: Better to just load a sentinel? Why bail on the whole thing?
Msg.error(this, Msg.error(this,
"Instruction table is corrupt for address " + getMinAddress() + "Instruction table is corrupt for address " + getMinAddress() +
". Missing prototype " + prototypeKey); ". Missing prototype " + prototypeKey);
@ -148,7 +173,7 @@ public class DBTraceInstruction extends AbstractDBTraceCodeUnit<DBTraceInstructi
} }
flowOverride = FlowOverride.values()[(flags & FLOWOVERRIDE_SET_MASK) >> FLOWOVERRIDE_SHIFT]; flowOverride = FlowOverride.values()[(flags & FLOWOVERRIDE_SET_MASK) >> FLOWOVERRIDE_SHIFT];
doSetGuestMapping(); doSetGuestMapping(guest);
} }
@Override @Override
@ -161,17 +186,6 @@ public class DBTraceInstruction extends AbstractDBTraceCodeUnit<DBTraceInstructi
return this; 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 @Override
public void delete() { public void delete() {
try (LockHold hold = LockHold.lock(space.lock.writeLock())) { try (LockHold hold = LockHold.lock(space.lock.writeLock())) {
@ -191,6 +205,11 @@ public class DBTraceInstruction extends AbstractDBTraceCodeUnit<DBTraceInstructi
space.instructions.unitSpanChanged(oldSpan, this); space.instructions.unitSpanChanged(oldSpan, this);
} }
@Override
public TraceGuestPlatform getGuestPlatform() {
return guest;
}
@Override @Override
public Language getLanguage() { public Language getLanguage() {
return prototype.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.address.*;
import ghidra.program.model.lang.*; import ghidra.program.model.lang.*;
import ghidra.program.model.util.CodeUnitInsertionException; import ghidra.program.model.util.CodeUnitInsertionException;
import ghidra.trace.model.guest.TraceGuestPlatform;
import ghidra.trace.model.listing.TraceInstructionsView; import ghidra.trace.model.listing.TraceInstructionsView;
import ghidra.util.LockHold; import ghidra.util.LockHold;
import ghidra.util.exception.CancelledException; import ghidra.util.exception.CancelledException;
@ -49,18 +50,17 @@ public class DBTraceInstructionsMemoryView
@Override @Override
public DBTraceInstruction create(Range<Long> lifespan, Address address, public DBTraceInstruction create(Range<Long> lifespan, Address address,
InstructionPrototype prototype, ProcessorContextView context) TraceGuestPlatform platform, InstructionPrototype prototype,
throws CodeUnitInsertionException { ProcessorContextView context) throws CodeUnitInsertionException {
return delegateWrite(address.getAddressSpace(), return delegateWrite(address.getAddressSpace(),
m -> m.create(lifespan, address, prototype, context)); m -> m.create(lifespan, address, platform, prototype, context));
} }
@Override @Override
public AddressSetView addInstructionSet(Range<Long> lifespan, InstructionSet instructionSet, public AddressSetView addInstructionSet(Range<Long> lifespan, TraceGuestPlatform platform,
boolean overwrite) { InstructionSet instructionSet, boolean overwrite) {
InstructionSet mappedSet = InstructionSet mappedSet = manager.platformManager
manager.getTrace().getLanguageManager().mapGuestInstructionAddressesToHost( .mapGuestInstructionAddressesToHost(platform, instructionSet);
instructionSet);
Map<AddressSpace, InstructionSet> breakDown = new HashMap<>(); Map<AddressSpace, InstructionSet> breakDown = new HashMap<>();
// TODO: I'm not sure the consequences of breaking an instruction set down. // 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())) { try (LockHold hold = LockHold.lock(manager.writeLock())) {
for (Entry<AddressSpace, InstructionSet> entry : breakDown.entrySet()) { for (Entry<AddressSpace, InstructionSet> entry : breakDown.entrySet()) {
DBTraceInstructionsView instructionsView = getForSpace(entry.getKey(), true); DBTraceInstructionsView instructionsView = getForSpace(entry.getKey(), true);
result.add( result.add(instructionsView.addInstructionSet(lifespan, platform, entry.getValue(),
instructionsView.addInstructionSet(lifespan, entry.getValue(), overwrite)); overwrite));
} }
return result; return result;
} }

View file

@ -29,9 +29,11 @@ import ghidra.program.model.util.CodeUnitInsertionException;
import ghidra.trace.database.DBTraceUtils; import ghidra.trace.database.DBTraceUtils;
import ghidra.trace.database.context.DBTraceRegisterContextManager; import ghidra.trace.database.context.DBTraceRegisterContextManager;
import ghidra.trace.database.context.DBTraceRegisterContextSpace; import ghidra.trace.database.context.DBTraceRegisterContextSpace;
import ghidra.trace.database.guest.DBTraceGuestPlatform;
import ghidra.trace.database.memory.DBTraceMemorySpace; import ghidra.trace.database.memory.DBTraceMemorySpace;
import ghidra.trace.model.ImmutableTraceAddressSnapRange; import ghidra.trace.model.ImmutableTraceAddressSnapRange;
import ghidra.trace.model.Trace.TraceCodeChangeType; import ghidra.trace.model.Trace.TraceCodeChangeType;
import ghidra.trace.model.guest.TraceGuestPlatform;
import ghidra.trace.model.TraceAddressSnapRange; import ghidra.trace.model.TraceAddressSnapRange;
import ghidra.trace.model.listing.TraceInstruction; import ghidra.trace.model.listing.TraceInstruction;
import ghidra.trace.model.listing.TraceInstructionsView; import ghidra.trace.model.listing.TraceInstructionsView;
@ -50,6 +52,8 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView
protected class InstructionBlockAdder { protected class InstructionBlockAdder {
private final Set<Address> skipDelaySlots; private final Set<Address> skipDelaySlots;
private final Range<Long> lifespan;
private final DBTraceGuestPlatform platform;
private final InstructionBlock block; private final InstructionBlock block;
private final Address errorAddress; private final Address errorAddress;
private final InstructionError conflict; private final InstructionError conflict;
@ -57,19 +61,22 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView
protected int count = 0; protected int count = 0;
private InstructionBlockAdder(Set<Address> skipDelaySlots, InstructionBlock block, private InstructionBlockAdder(Set<Address> skipDelaySlots, Range<Long> lifespan,
Address errorAddress, InstructionError conflict, CodeUnit conflictCodeUnit) { DBTraceGuestPlatform platform, InstructionBlock block, Address errorAddress,
InstructionError conflict, CodeUnit conflictCodeUnit) {
this.skipDelaySlots = skipDelaySlots; this.skipDelaySlots = skipDelaySlots;
this.lifespan = lifespan;
this.platform = platform;
this.block = block; this.block = block;
this.errorAddress = errorAddress; this.errorAddress = errorAddress;
this.conflict = conflict; this.conflict = conflict;
this.conflictCodeUnit = conflictCodeUnit; this.conflictCodeUnit = conflictCodeUnit;
} }
protected Instruction doCreateInstruction(Range<Long> lifespan, Address address, protected Instruction doCreateInstruction(Address address,
InstructionPrototype prototype, Instruction protoInstr) { InstructionPrototype prototype, Instruction protoInstr) {
try { try {
Instruction created = doCreate(lifespan, address, prototype, protoInstr); Instruction created = doCreate(lifespan, address, platform, prototype, protoInstr);
// copy override settings to replacement instruction // copy override settings to replacement instruction
if (protoInstr.isFallThroughOverridden()) { if (protoInstr.isFallThroughOverridden()) {
created.setFallThrough(protoInstr.getFallThrough()); created.setFallThrough(protoInstr.getFallThrough());
@ -97,8 +104,7 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView
* @param areDelaySlots * @param areDelaySlots
* @return * @return
*/ */
protected Instruction doAddInstructions(Range<Long> lifespan, Iterator<Instruction> it, protected Instruction doAddInstructions(Iterator<Instruction> it, boolean areDelaySlots) {
boolean areDelaySlots) {
Instruction lastInstruction = null; Instruction lastInstruction = null;
while (it.hasNext()) { while (it.hasNext()) {
Instruction protoInstr = it.next(); Instruction protoInstr = it.next();
@ -139,10 +145,10 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView
delayed.push(it.next()); delayed.push(it.next());
} }
lastInstruction = replaceIfNotNull(lastInstruction, lastInstruction = replaceIfNotNull(lastInstruction,
doAddInstructions(lifespan, delayed.iterator(), true)); doAddInstructions(delayed.iterator(), true));
} }
lastInstruction = lastInstruction =
doCreateInstruction(lifespan, startAddress, prototype, protoInstr); doCreateInstruction(startAddress, prototype, protoInstr);
} }
if (errorAddress != null && conflictCodeUnit == null && if (errorAddress != null && conflictCodeUnit == null &&
errorAddress.compareTo(startAddress) <= 0) { errorAddress.compareTo(startAddress) <= 0) {
@ -179,9 +185,22 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView
ctxSpace.setValue(language, newValue, tasr.getLifespan(), tasr.getRange()); 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, protected DBTraceInstruction doCreate(Range<Long> lifespan, Address address,
InstructionPrototype prototype, ProcessorContextView context) DBTraceGuestPlatform platform, InstructionPrototype prototype,
ProcessorContextView context)
throws CodeUnitInsertionException, AddressOverflowException { throws CodeUnitInsertionException, AddressOverflowException {
if (!languagesAgree(platform, prototype)) {
throw new IllegalArgumentException("Platform and prototype disagree in language");
}
Address endAddress = address.addNoWrap(prototype.getLength() - 1); Address endAddress = address.addNoWrap(prototype.getLength() - 1);
AddressRangeImpl createdRange = new AddressRangeImpl(address, endAddress); AddressRangeImpl createdRange = new AddressRangeImpl(address, endAddress);
@ -213,7 +232,7 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView
doSetContexts(tasr, prototype.getLanguage(), context); doSetContexts(tasr, prototype.getLanguage(), context);
DBTraceInstruction created = space.instructionMapSpace.put(tasr, null); DBTraceInstruction created = space.instructionMapSpace.put(tasr, null);
created.set(prototype, context); created.set(platform, prototype, context);
cacheForContaining.notifyNewEntry(lifespan, createdRange, created); cacheForContaining.notifyNewEntry(lifespan, createdRange, created);
cacheForSequence.notifyNewEntry(lifespan, createdRange, created); cacheForSequence.notifyNewEntry(lifespan, createdRange, created);
@ -227,10 +246,13 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView
@Override @Override
public DBTraceInstruction create(Range<Long> lifespan, Address address, public DBTraceInstruction create(Range<Long> lifespan, Address address,
InstructionPrototype prototype, ProcessorContextView context) TraceGuestPlatform platform, InstructionPrototype prototype,
ProcessorContextView context)
throws CodeUnitInsertionException { throws CodeUnitInsertionException {
DBTraceGuestPlatform dbPlatform = space.manager.platformManager.assertMine(platform);
try (LockHold hold = LockHold.lock(space.lock.writeLock())) { 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.trace.setChanged(new TraceChangeRecord<>(TraceCodeChangeType.ADDED,
space, created, created)); space, created, created));
return created; return created;
@ -254,23 +276,26 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView
OverlappingObjectIterator.CODE_UNIT, existing, OverlappingObjectIterator.CODE_UNIT); OverlappingObjectIterator.CODE_UNIT, existing, OverlappingObjectIterator.CODE_UNIT);
} }
protected InstructionBlockAdder startAddingBlock(long startSnap, Set<Address> skipDelaySlots, protected InstructionBlockAdder startAddingBlock(Range<Long> lifespan,
InstructionBlock block) { Set<Address> skipDelaySlots, DBTraceGuestPlatform platform, InstructionBlock block) {
InstructionError conflict = block.getInstructionConflict(); InstructionError conflict = block.getInstructionConflict();
if (conflict == null) { 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(); Address errorAddress = conflict.getInstructionAddress();
if (errorAddress == null) { if (errorAddress == null) {
return null; // The whole block is considered in error return null; // The whole block is considered in error
} }
if (!conflict.getInstructionErrorType().isConflict) { 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 = CodeUnit conflictCodeUnit =
space.definedUnits.getAt(startSnap, conflict.getConflictAddress()); space.definedUnits.getAt(startSnap, conflict.getConflictAddress());
return new InstructionBlockAdder(skipDelaySlots, block, errorAddress, conflict, return new InstructionBlockAdder(skipDelaySlots, lifespan, platform, block, errorAddress,
conflictCodeUnit); conflict, conflictCodeUnit);
} }
/** /**
@ -359,8 +384,9 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView
} }
@Override @Override
public AddressSetView addInstructionSet(Range<Long> lifespan, InstructionSet instructionSet, public AddressSetView addInstructionSet(Range<Long> lifespan, TraceGuestPlatform platform,
boolean overwrite) { InstructionSet instructionSet, boolean overwrite) {
DBTraceGuestPlatform dbPlatform = space.manager.platformManager.assertMine(platform);
// NOTE: Partly derived from CodeManager#addInstructions() // NOTE: Partly derived from CodeManager#addInstructions()
// Attempted to factor more fluently // Attempted to factor more fluently
AddressSet result = new AddressSet(); AddressSet result = new AddressSet();
@ -378,12 +404,12 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView
// Add blocks // Add blocks
for (InstructionBlock block : instructionSet) { for (InstructionBlock block : instructionSet) {
InstructionBlockAdder adder = startAddingBlock(startSnap, skipDelaySlots, block); InstructionBlockAdder adder =
startAddingBlock(lifespan, skipDelaySlots, dbPlatform, block);
if (adder == null) { if (adder == null) {
continue; continue;
} }
Instruction lastInstruction = Instruction lastInstruction = adder.doAddInstructions(block.iterator(), false);
adder.doAddInstructions(lifespan, block.iterator(), false);
block.setInstructionsAddedCount(adder.count); block.setInstructionsAddedCount(adder.count);
if (lastInstruction != null) { if (lastInstruction != null) {
Address maxAddress = DBTraceCodeManager.instructionMax(lastInstruction, true); 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.database.space.DBTraceSpaceKey;
import ghidra.trace.model.ImmutableTraceAddressSnapRange; import ghidra.trace.model.ImmutableTraceAddressSnapRange;
import ghidra.trace.model.TraceAddressSnapRange; import ghidra.trace.model.TraceAddressSnapRange;
import ghidra.trace.model.guest.TraceGuestPlatform;
import ghidra.trace.model.listing.TraceData; import ghidra.trace.model.listing.TraceData;
import ghidra.trace.model.thread.TraceThread; import ghidra.trace.model.thread.TraceThread;
import ghidra.trace.util.TraceAddressSpace; import ghidra.trace.util.TraceAddressSpace;
@ -80,6 +81,11 @@ public class UndefinedDBTraceData implements DBTraceDataAdapter, DBTraceSpaceKey
return trace.getBaseLanguage(); return trace.getBaseLanguage();
} }
@Override
public TraceGuestPlatform getGuestPlatform() {
return null;
}
@Override @Override
public AddressRange getRange() { public AddressRange getRange() {
// TODO: Cache this? // TODO: Cache this?

View file

@ -725,27 +725,25 @@ public abstract class AbstractDBTraceProgramViewListing implements TraceProgramV
public Instruction createInstruction(Address addr, InstructionPrototype prototype, public Instruction createInstruction(Address addr, InstructionPrototype prototype,
MemBuffer memBuf, ProcessorContextView context) throws CodeUnitInsertionException { MemBuffer memBuf, ProcessorContextView context) throws CodeUnitInsertionException {
// TODO: Why memBuf? Can it vary from program memory? // TODO: Why memBuf? Can it vary from program memory?
try (LockHold hold = program.trace.lockWrite()) { // TODO: Per-platform views?
return codeOperations.instructions() return codeOperations.instructions()
.create(Range.atLeast(program.snap), addr, .create(Range.atLeast(program.snap), addr, null, prototype, context);
prototype, context);
}
} }
@Override @Override
public AddressSetView addInstructions(InstructionSet instructionSet, boolean overwrite) public AddressSetView addInstructions(InstructionSet instructionSet, boolean overwrite)
throws CodeUnitInsertionException { throws CodeUnitInsertionException {
// TODO: Per-platform views?
return codeOperations.instructions() return codeOperations.instructions()
.addInstructionSet(Range.atLeast(program.snap), .addInstructionSet(Range.atLeast(program.snap), null, instructionSet,
instructionSet, overwrite); overwrite);
} }
@Override @Override
public Data createData(Address addr, DataType dataType, int length) public Data createData(Address addr, DataType dataType, int length)
throws CodeUnitInsertionException { throws CodeUnitInsertionException {
return codeOperations.definedData() return codeOperations.definedData()
.create(Range.atLeast(program.snap), addr, dataType, .create(Range.atLeast(program.snap), addr, dataType, length);
length);
} }
@Override @Override

View file

@ -181,10 +181,16 @@ public class DBTraceObjectStack implements TraceObjectStack, DBTraceObjectInterf
protected TraceStackFrame doGetFrame(int level) { protected TraceStackFrame doGetFrame(int level) {
TargetObjectSchema schema = object.getTargetSchema(); TargetObjectSchema schema = object.getTargetSchema();
PathPredicates matcher = schema.searchFor(TargetStackFrame.class, true); PathPredicates matcher = schema.searchFor(TargetStackFrame.class, true);
matcher = matcher.applyKeys(PathUtils.makeIndex(level)); PathPredicates decMatcher = matcher.applyKeys(PathUtils.makeIndex(level));
return object.getSuccessors(computeSpan(), matcher) PathPredicates hexMatcher = matcher.applyKeys("0x" + Integer.toHexString(level));
Range<Long> span = computeSpan();
return object.getSuccessors(span, decMatcher)
.findAny() .findAny()
.map(p -> p.getDestination(object).queryInterface(TraceObjectStackFrame.class)) .map(p -> p.getDestination(object).queryInterface(TraceObjectStackFrame.class))
.or(() -> object.getSuccessors(span, hexMatcher)
.findAny()
.map(p -> p.getDestination(object)
.queryInterface(TraceObjectStackFrame.class)))
.orElse(null); .orElse(null);
} }

View file

@ -65,7 +65,7 @@ public class DBTraceObjectStackFrame implements TraceObjectStackFrame, DBTraceOb
} }
String index = PathUtils.parseIndex(k); String index = PathUtils.parseIndex(k);
try { 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? // TODO: Perhaps just have an attribute that is its level?
} }
catch (NumberFormatException e) { 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() { protected boolean doHasAnyNonRangedValues() {
for (DBTraceObjectValue val : manager.valuesByTriple for (DBTraceObjectValue val : manager.valuesByTriple
.tail(new PrimaryTriple(this, "", Long.MIN_VALUE), true) .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() { protected boolean doHasAnyRangedValues() {
for (DBTraceAddressSnapRangePropertyMapSpace<DBTraceObjectAddressRangeValue, ?> space // for (DBTraceAddressSnapRangePropertyMapSpace<DBTraceObjectAddressRangeValue, ?> space //
: manager.rangeValueMap.getActiveMemorySpaces()) { : manager.rangeValueMap.getActiveMemorySpaces()) {
@ -415,45 +473,29 @@ public class DBTraceObject extends DBAnnotatedObject implements TraceObject {
} }
} }
protected Collection<? extends DBTraceObjectValue> doGetElements() { protected Collection<? extends InternalTraceObjectValue> doGetElements() {
List<DBTraceObjectValue> result = new ArrayList<>(); List<InternalTraceObjectValue> result = new ArrayList<>();
for (DBTraceObjectValue val : manager.valuesByTriple collectNonRangedElements(result);
.sub(new PrimaryTriple(this, "[", Long.MIN_VALUE), true, collectRangedElements(result);
new PrimaryTriple(this, "\\", Long.MIN_VALUE), false)
.values()) {
result.add(val);
}
return result; return result;
} }
@Override @Override
public Collection<? extends DBTraceObjectValue> getElements() { public Collection<? extends InternalTraceObjectValue> getElements() {
try (LockHold hold = manager.trace.lockRead()) { try (LockHold hold = manager.trace.lockRead()) {
return doGetElements(); return doGetElements();
} }
} }
protected Collection<? extends DBTraceObjectValue> doGetAttributes() { protected Collection<? extends InternalTraceObjectValue> doGetAttributes() {
List<DBTraceObjectValue> result = new ArrayList<>(); List<InternalTraceObjectValue> result = new ArrayList<>();
for (DBTraceObjectValue val : manager.valuesByTriple collectNonRangedAttributes(result);
.sub(new PrimaryTriple(this, "", Long.MIN_VALUE), true, collectRangedAttributes(result);
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);
}
return result; return result;
} }
@Override @Override
public Collection<? extends DBTraceObjectValue> getAttributes() { public Collection<? extends InternalTraceObjectValue> getAttributes() {
try (LockHold hold = manager.trace.lockRead()) { try (LockHold hold = manager.trace.lockRead()) {
return doGetAttributes(); return doGetAttributes();
} }
@ -525,7 +567,7 @@ public class DBTraceObject extends DBAnnotatedObject implements TraceObject {
return floor; return floor;
} }
protected Stream<DBTraceObjectValue> doGetNonRangedValues(Range<Long> span, String key, protected Stream<DBTraceObjectValue> doGetOrderedNonRangedValues(Range<Long> span, String key,
boolean forward) { boolean forward) {
DBCachedObjectIndex<PrimaryTriple, DBTraceObjectValue> sub = manager.valuesByTriple.sub( DBCachedObjectIndex<PrimaryTriple, DBTraceObjectValue> sub = manager.valuesByTriple.sub(
new PrimaryTriple(this, key, DBTraceUtils.lowerEndpoint(span)), true, new PrimaryTriple(this, key, DBTraceUtils.lowerEndpoint(span)), true,
@ -552,7 +594,7 @@ public class DBTraceObject extends DBAnnotatedObject implements TraceObject {
return null; return null;
} }
protected Stream<DBTraceObjectAddressRangeValue> doGetRangedValues(Range<Long> span, protected Stream<DBTraceObjectAddressRangeValue> doGetOrderedRangedValues(Range<Long> span,
String key, boolean forward) { String key, boolean forward) {
Rectangle2DDirection dir = forward Rectangle2DDirection dir = forward
? Rectangle2DDirection.BOTTOMMOST ? Rectangle2DDirection.BOTTOMMOST
@ -590,8 +632,8 @@ public class DBTraceObject extends DBAnnotatedObject implements TraceObject {
protected Stream<InternalTraceObjectValue> doGetOrderedValues(Range<Long> span, String key, protected Stream<InternalTraceObjectValue> doGetOrderedValues(Range<Long> span, String key,
boolean forward) { boolean forward) {
Stream<DBTraceObjectValue> nrVals = doGetNonRangedValues(span, key, forward); Stream<DBTraceObjectValue> nrVals = doGetOrderedNonRangedValues(span, key, forward);
Stream<DBTraceObjectAddressRangeValue> rVals = doGetRangedValues(span, key, forward); Stream<DBTraceObjectAddressRangeValue> rVals = doGetOrderedRangedValues(span, key, forward);
Comparator<Long> order = forward ? Comparator.naturalOrder() : Comparator.reverseOrder(); Comparator<Long> order = forward ? Comparator.naturalOrder() : Comparator.reverseOrder();
Comparator<InternalTraceObjectValue> comparator = Comparator<InternalTraceObjectValue> comparator =
Comparator.comparing(v -> v.getMinSnap(), order); Comparator.comparing(v -> v.getMinSnap(), order);
@ -883,9 +925,14 @@ public class DBTraceObject extends DBAnnotatedObject implements TraceObject {
manager.trace.setChanged(rec); manager.trace.setChanged(rec);
for (TraceObjectInterface iface : ifaces.values()) { for (TraceObjectInterface iface : ifaces.values()) {
DBTraceObjectInterface dbIface = (DBTraceObjectInterface) iface; DBTraceObjectInterface dbIface = (DBTraceObjectInterface) iface;
TraceChangeRecord<?, ?> evt = dbIface.translateEvent(rec); try {
if (evt != null) { TraceChangeRecord<?, ?> evt = dbIface.translateEvent(rec);
manager.trace.setChanged(evt); 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 // Singleton path, so if I match, no successor can
return VisitResult.INCLUDE_FINISH; 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_FINISH;
} }
return VisitResult.EXCLUDE_CONTINUE; return VisitResult.EXCLUDE_CONTINUE;

View file

@ -60,7 +60,7 @@ public class InternalSuccessorsRelativeVisitor implements SpanIntersectingVisito
return Stream.empty(); return Stream.empty();
} }
Stream<? extends DBTraceObjectValue> attrStream; Stream<? extends InternalTraceObjectValue> attrStream;
if (nextKeys.contains("")) { if (nextKeys.contains("")) {
attrStream = object.doGetAttributes() attrStream = object.doGetAttributes()
.stream() .stream()
@ -70,7 +70,7 @@ public class InternalSuccessorsRelativeVisitor implements SpanIntersectingVisito
attrStream = Stream.empty(); attrStream = Stream.empty();
} }
Stream<? extends DBTraceObjectValue> elemStream; Stream<? extends InternalTraceObjectValue> elemStream;
if (nextKeys.contains("[]")) { if (nextKeys.contains("[]")) {
elemStream = object.doGetElements() elemStream = object.doGetElements()
.stream() .stream()

View file

@ -32,7 +32,7 @@ import ghidra.trace.model.breakpoint.TraceBreakpoint;
import ghidra.trace.model.breakpoint.TraceBreakpointManager; import ghidra.trace.model.breakpoint.TraceBreakpointManager;
import ghidra.trace.model.context.TraceRegisterContextManager; import ghidra.trace.model.context.TraceRegisterContextManager;
import ghidra.trace.model.data.TraceBasedDataTypeManager; 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.listing.*;
import ghidra.trace.model.memory.*; import ghidra.trace.model.memory.*;
import ghidra.trace.model.modules.*; import ghidra.trace.model.modules.*;
@ -399,7 +399,7 @@ public interface Trace extends DataTypeManagerDomainObject {
TraceEquateManager getEquateManager(); TraceEquateManager getEquateManager();
TraceLanguageManager getLanguageManager(); TracePlatformManager getPlatformManager();
TraceMemoryManager getMemoryManager(); 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 * See the License for the specific language governing permissions and
* limitations under the License. * 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.Address;
import ghidra.program.model.address.AddressRange; import ghidra.program.model.address.AddressRange;
import ghidra.program.model.lang.CompilerSpec;
import ghidra.program.model.lang.Language; import ghidra.program.model.lang.Language;
import ghidra.util.exception.CancelledException; import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor; import ghidra.util.task.TaskMonitor;
public interface TraceGuestLanguageMappedRange { public interface TraceGuestPlatformMappedRange {
Language getHostLanguage(); Language getHostLanguage();
CompilerSpec getHostCompilerSpec();
AddressRange getHostRange(); AddressRange getHostRange();
Language getGuestLanguage(); TraceGuestPlatform getGuestPlatform();
AddressRange getGuestRange(); 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.program.model.util.TypeMismatchException;
import ghidra.trace.model.Trace; import ghidra.trace.model.Trace;
import ghidra.trace.model.TraceAddressSnapRange; import ghidra.trace.model.TraceAddressSnapRange;
import ghidra.trace.model.guest.TraceGuestPlatform;
import ghidra.trace.model.program.TraceProgramView; import ghidra.trace.model.program.TraceProgramView;
import ghidra.trace.model.symbol.TraceReference; import ghidra.trace.model.symbol.TraceReference;
import ghidra.trace.model.thread.TraceThread; import ghidra.trace.model.thread.TraceThread;
@ -42,6 +43,13 @@ public interface TraceCodeUnit extends CodeUnit {
*/ */
Trace getTrace(); 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 @Override
TraceProgramView getProgram(); TraceProgramView getProgram();

View file

@ -21,21 +21,37 @@ import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSetView; import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.lang.*; import ghidra.program.model.lang.*;
import ghidra.program.model.util.CodeUnitInsertionException; import ghidra.program.model.util.CodeUnitInsertionException;
import ghidra.trace.model.guest.TraceGuestPlatform;
public interface TraceInstructionsView extends TraceBaseDefinedUnitsView<TraceInstruction> { 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 * <p>
* the {@code instructionSet} * <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 instructionSet the set of instructions to add
* @param overwrite {@code true} to replace conflicting instructions * @param overwrite true to replace conflicting instructions
* @return the address set of instructions actually added * @return the (host) address set of instructions actually added
*/ */
AddressSetView addInstructionSet(Range<Long> lifespan, InstructionSet instructionSet, AddressSetView addInstructionSet(Range<Long> lifespan, TraceGuestPlatform platform,
boolean overwrite); 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 * may need the DB's lock, esp., considering user callbacks, then it must <em>first</em> acquire
* the DB lock. * 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 RangeSet<Long> spanSet = TreeRangeSet.create();
protected final ForSnapshotsListener listener = new ForSnapshotsListener(); protected final ForSnapshotsListener listener = new ForSnapshotsListener();
protected final ListenerSet<Runnable> changeListeners = new ListenerSet<>(Runnable.class); protected final ListenerSet<Runnable> changeListeners = new ListenerSet<>(Runnable.class);
@ -114,6 +114,10 @@ public class DefaultTraceTimeViewport implements TraceTimeViewport {
protected long snap = 0; protected long snap = 0;
public DefaultTraceTimeViewport(Trace trace) { public DefaultTraceTimeViewport(Trace trace) {
Range<Long> zero = Range.singleton(0L);
spanSet.add(zero);
ordered.add(zero);
this.trace = trace; this.trace = trace;
trace.addCloseListener(listener); trace.addCloseListener(listener);
trace.addListener(listener); trace.addListener(listener);

View file

@ -26,7 +26,8 @@ import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder; import java.nio.charset.CharsetEncoder;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.*; import java.util.Collection;
import java.util.List;
import com.google.common.collect.Range; 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.model.util.CodeUnitInsertionException;
import ghidra.program.util.DefaultLanguageService; import ghidra.program.util.DefaultLanguageService;
import ghidra.trace.database.bookmark.*; import ghidra.trace.database.bookmark.*;
import ghidra.trace.database.language.DBTraceGuestLanguage;
import ghidra.trace.database.listing.*; import ghidra.trace.database.listing.*;
import ghidra.trace.database.memory.DBTraceMemoryManager; import ghidra.trace.database.memory.DBTraceMemoryManager;
import ghidra.trace.database.symbol.DBTraceReference; import ghidra.trace.database.symbol.DBTraceReference;
import ghidra.trace.database.thread.DBTraceThreadManager; import ghidra.trace.database.thread.DBTraceThreadManager;
import ghidra.trace.model.*; import ghidra.trace.model.*;
import ghidra.trace.model.language.TraceGuestLanguage; import ghidra.trace.model.guest.TraceGuestPlatform;
import ghidra.trace.model.thread.TraceThread; import ghidra.trace.model.thread.TraceThread;
import ghidra.util.Msg; import ghidra.util.Msg;
import ghidra.util.database.DBOpenMode; import ghidra.util.database.DBOpenMode;
@ -102,7 +102,7 @@ public class ToyDBTraceBuilder implements AutoCloseable {
return addr(language, offset); return addr(language, offset);
} }
public Address addr(TraceGuestLanguage lang, long offset) { public Address addr(TraceGuestPlatform lang, long offset) {
return lang.getLanguage().getDefaultSpace().getAddress(offset); return lang.getLanguage().getDefaultSpace().getAddress(offset);
} }
@ -114,7 +114,7 @@ public class ToyDBTraceBuilder implements AutoCloseable {
return data(language, offset); return data(language, offset);
} }
public Address data(TraceGuestLanguage lang, long offset) { public Address data(TraceGuestPlatform lang, long offset) {
return data(lang.getLanguage(), offset); return data(lang.getLanguage(), offset);
} }
@ -150,11 +150,11 @@ public class ToyDBTraceBuilder implements AutoCloseable {
return drng(language, start, end); 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); 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); return drng(lang.getLanguage(), start, end);
} }
@ -246,9 +246,10 @@ public class ToyDBTraceBuilder implements AutoCloseable {
} }
public DBTraceInstruction addInstruction(long snap, Address start, public DBTraceInstruction addInstruction(long snap, Address start,
@SuppressWarnings("hiding") Language language) throws CodeUnitInsertionException { TraceGuestPlatform guest) throws CodeUnitInsertionException {
DBTraceMemoryManager memory = trace.getMemoryManager(); DBTraceMemoryManager memory = trace.getMemoryManager();
DBTraceCodeManager code = trace.getCodeManager(); DBTraceCodeManager code = trace.getCodeManager();
Language language = guest == null ? this.language : guest.getLanguage();
Disassembler dis = Disassembler.getDisassembler(language, language.getAddressFactory(), Disassembler dis = Disassembler.getDisassembler(language, language.getAddressFactory(),
new ConsoleTaskMonitor(), msg -> Msg.info(this, "Listener: " + msg)); new ConsoleTaskMonitor(), msg -> Msg.info(this, "Listener: " + msg));
RegisterValue defaultContextValue = trace.getRegisterContextManager() RegisterValue defaultContextValue = trace.getRegisterContextManager()
@ -256,27 +257,25 @@ public class ToyDBTraceBuilder implements AutoCloseable {
.getDefaultDisassemblyContext(); .getDefaultDisassemblyContext();
MemBuffer memBuf; MemBuffer memBuf;
if (language == null || Objects.equals(this.language, language)) { if (guest == null) {
memBuf = memory.getBufferAt(snap, start); memBuf = memory.getBufferAt(snap, start);
} }
else { else {
DBTraceGuestLanguage guest = trace.getLanguageManager().getGuestLanguage(language);
memBuf = guest.getMappedMemBuffer(snap, guest.mapHostToGuest(start)); memBuf = guest.getMappedMemBuffer(snap, guest.mapHostToGuest(start));
} }
InstructionBlock block = dis.pseudoDisassembleBlock(memBuf, defaultContextValue, 1); InstructionBlock block = dis.pseudoDisassembleBlock(memBuf, defaultContextValue, 1);
Instruction pseudoIns = block.iterator().next(); Instruction pseudoIns = block.iterator().next();
return code.instructions() return code.instructions()
.create(Range.atLeast(snap), start, pseudoIns.getPrototype(), .create(Range.atLeast(snap), start, guest, pseudoIns.getPrototype(), pseudoIns);
pseudoIns);
} }
public DBTraceInstruction addInstruction(long snap, Address start, public DBTraceInstruction addInstruction(long snap, Address start,
@SuppressWarnings("hiding") Language language, ByteBuffer buf) TraceGuestPlatform guest, ByteBuffer buf)
throws CodeUnitInsertionException { throws CodeUnitInsertionException {
int length = buf.remaining(); int length = buf.remaining();
DBTraceMemoryManager memory = trace.getMemoryManager(); DBTraceMemoryManager memory = trace.getMemoryManager();
memory.putBytes(snap, start, buf); memory.putBytes(snap, start, buf);
DBTraceInstruction instruction = addInstruction(snap, start, language); DBTraceInstruction instruction = addInstruction(snap, start, guest);
assertEquals(length, instruction.getLength()); assertEquals(length, instruction.getLength());
return instruction; return instruction;
} }
@ -345,4 +344,9 @@ public class ToyDBTraceBuilder implements AutoCloseable {
public Language getLanguage(String id) throws LanguageNotFoundException { public Language getLanguage(String id) throws LanguageNotFoundException {
return languageService.getLanguage(new LanguageID(id)); 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 * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package ghidra.trace.database.language; package ghidra.trace.database.guest;
import static org.junit.Assert.*; import static org.junit.Assert.*;
@ -23,24 +23,21 @@ import java.util.*;
import org.junit.*; import org.junit.*;
import ghidra.program.model.address.AddressOverflowException;
import ghidra.program.model.lang.LanguageNotFoundException;
import ghidra.test.AbstractGhidraHeadlessIntegrationTest; import ghidra.test.AbstractGhidraHeadlessIntegrationTest;
import ghidra.trace.database.ToyDBTraceBuilder; 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.database.UndoableTransaction;
import ghidra.util.exception.CancelledException;
import ghidra.util.exception.VersionException;
import ghidra.util.task.ConsoleTaskMonitor; import ghidra.util.task.ConsoleTaskMonitor;
public class DBTraceLanguageManagerTest extends AbstractGhidraHeadlessIntegrationTest { public class DBTracePlatformManagerTest extends AbstractGhidraHeadlessIntegrationTest {
protected ToyDBTraceBuilder b; protected ToyDBTraceBuilder b;
protected DBTraceLanguageManager manager; protected DBTracePlatformManager manager;
@Before @Before
public void setUpLanguageManagerTest() throws IOException { public void setUpLanguageManagerTest() throws IOException {
b = new ToyDBTraceBuilder("Testing", "Toy:BE:64:default"); b = new ToyDBTraceBuilder("Testing", "Toy:BE:64:default");
manager = b.trace.getLanguageManager(); manager = b.trace.getPlatformManager();
} }
@After @After
@ -55,82 +52,108 @@ public class DBTraceLanguageManagerTest extends AbstractGhidraHeadlessIntegratio
} }
@Test @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()) { try (UndoableTransaction tid = b.startTransaction()) {
assertEquals(0, manager.languageStore.getRecordCount()); 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()); assertEquals(1, manager.languageStore.getRecordCount());
assertEquals(1, manager.platformStore.getRecordCount());
try { // Cannot add base language as guest
manager.addGuestLanguage(b.getLanguage("Toy:BE:64:default"));
fail();
}
catch (IllegalArgumentException e) {
// pass
}
} }
} }
@Test @Test
public void testGetGuestLanguages() throws LanguageNotFoundException { public void testAddGuestPlatformHostCompilerErr() throws Throwable {
DBTraceGuestLanguage guest;
try (UndoableTransaction tid = b.startTransaction()) { try (UndoableTransaction tid = b.startTransaction()) {
assertTrue(manager.getGuestLanguages().isEmpty()); manager.addGuestPlatform(b.getLanguage("Toy:BE:64:default").getDefaultCompilerSpec());
guest = manager.addGuestLanguage(b.getLanguage("x86:LE:32:default")); 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 @Test
public void testAddLanguageThenUndo() throws IOException { public void testAddGuestPlatformHostLanguage() throws Throwable {
try (UndoableTransaction tid = b.startTransaction()) { 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(); b.trace.undo();
assertTrue(manager.getGuestLanguages().isEmpty()); assertTrue(manager.getGuestPlatforms().isEmpty());
} }
@Test @Test
public void testAddLanguageThenSaveAndLoad() public void testAddPlatformThenSaveAndLoad() throws Throwable {
throws CancelledException, IOException, VersionException {
try (UndoableTransaction tid = b.startTransaction()) { 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(); File saved = b.save();
try (ToyDBTraceBuilder r = new ToyDBTraceBuilder(saved)) { try (ToyDBTraceBuilder r = new ToyDBTraceBuilder(saved)) {
Collection<TraceGuestLanguage> guestLanguages = Collection<TraceGuestPlatform> guestPlatforms =
r.trace.getLanguageManager().getGuestLanguages(); r.trace.getPlatformManager().getGuestPlatforms();
assertEquals(1, guestLanguages.size()); assertEquals(1, guestPlatforms.size());
TraceGuestPlatform platform = guestPlatforms.iterator().next();
assertEquals("x86:LE:32:default", assertEquals("x86:LE:32:default",
guestLanguages.iterator().next().getLanguage().getLanguageID().getIdAsString()); platform.getLanguage().getLanguageID().getIdAsString());
assertEquals("gcc", platform.getCompilerSpec().getCompilerSpecID().getIdAsString());
} }
} }
@Test @Test
public void testDeleteGuestLanguage() throws LanguageNotFoundException, CancelledException { public void testDeleteGuestPlatform() throws Throwable {
DBTraceGuestLanguage guest; DBTraceGuestPlatform guest;
try (UndoableTransaction tid = b.startTransaction()) { 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()) { try (UndoableTransaction tid = b.startTransaction()) {
guest.delete(new ConsoleTaskMonitor()); guest.delete(new ConsoleTaskMonitor());
} }
assertEquals(0, manager.languageStore.getRecordCount()); assertEquals(0, manager.platformStore.getRecordCount());
assertTrue(manager.entriesByLanguage.isEmpty()); assertTrue(manager.platformsByCompiler.isEmpty());
} }
@Test @Test
public void testAddMappedRange() throws LanguageNotFoundException, AddressOverflowException { public void testAddMappedRange() throws Throwable {
try (UndoableTransaction tid = b.startTransaction()) { try (UndoableTransaction tid = b.startTransaction()) {
DBTraceGuestLanguage guest = DBTraceGuestPlatform guest =
manager.addGuestLanguage(b.getLanguage("x86:LE:32:default")); manager.addGuestPlatform(b.getCompiler("x86:LE:32:default", "gcc"));
assertEquals(0, manager.rangeMappingStore.getRecordCount()); assertEquals(0, manager.rangeMappingStore.getRecordCount());
guest.addMappedRange(b.addr(0x01000000), b.addr(guest, 0x02000000), 0x1000); guest.addMappedRange(b.addr(0x01000000), b.addr(guest, 0x02000000), 0x1000);
@ -155,11 +178,10 @@ public class DBTraceLanguageManagerTest extends AbstractGhidraHeadlessIntegratio
} }
@Test @Test
public void testGetHostAndGuestAddressSet() public void testGetHostAndGuestAddressSet() throws Throwable {
throws LanguageNotFoundException, AddressOverflowException {
try (UndoableTransaction tid = b.startTransaction()) { try (UndoableTransaction tid = b.startTransaction()) {
DBTraceGuestLanguage guest = DBTraceGuestPlatform guest =
manager.addGuestLanguage(b.getLanguage("x86:LE:32:default")); manager.addGuestPlatform(b.getCompiler("x86:LE:32:default", "gcc"));
assertEquals(b.set(), guest.getHostAddressSet()); assertEquals(b.set(), guest.getHostAddressSet());
guest.addMappedRange(b.addr(0x01000000), b.addr(guest, 0x02000000), 0x1000); guest.addMappedRange(b.addr(0x01000000), b.addr(guest, 0x02000000), 0x1000);
@ -169,10 +191,10 @@ public class DBTraceLanguageManagerTest extends AbstractGhidraHeadlessIntegratio
} }
@Test @Test
public void testMapHostToGuest() throws LanguageNotFoundException, AddressOverflowException { public void testMapHostToGuest() throws Throwable {
try (UndoableTransaction tid = b.startTransaction()) { try (UndoableTransaction tid = b.startTransaction()) {
DBTraceGuestLanguage guest = DBTraceGuestPlatform guest =
manager.addGuestLanguage(b.getLanguage("x86:LE:32:default")); manager.addGuestPlatform(b.getCompiler("x86:LE:32:default", "gcc"));
guest.addMappedRange(b.addr(0x01000000), b.addr(guest, 0x02000000), 0x1000); guest.addMappedRange(b.addr(0x01000000), b.addr(guest, 0x02000000), 0x1000);
assertNull(guest.mapHostToGuest(b.addr(0x00000000))); assertNull(guest.mapHostToGuest(b.addr(0x00000000)));
@ -185,10 +207,10 @@ public class DBTraceLanguageManagerTest extends AbstractGhidraHeadlessIntegratio
} }
@Test @Test
public void testMapGuestToHost() throws LanguageNotFoundException, AddressOverflowException { public void testMapGuestToHost() throws Throwable {
try (UndoableTransaction tid = b.startTransaction()) { try (UndoableTransaction tid = b.startTransaction()) {
DBTraceGuestLanguage guest = DBTraceGuestPlatform guest =
manager.addGuestLanguage(b.getLanguage("x86:LE:32:default")); manager.addGuestPlatform(b.getCompiler("x86:LE:32:default", "gcc"));
guest.addMappedRange(b.addr(0x01000000), b.addr(guest, 0x02000000), 0x1000); guest.addMappedRange(b.addr(0x01000000), b.addr(guest, 0x02000000), 0x1000);
assertNull(guest.mapGuestToHost(b.addr(0x00000000))); assertNull(guest.mapGuestToHost(b.addr(0x00000000)));
@ -201,30 +223,28 @@ public class DBTraceLanguageManagerTest extends AbstractGhidraHeadlessIntegratio
} }
@Test @Test
public void testAddMappedRangeThenSaveAndLoad() public void testAddMappedRangeThenSaveAndLoad() throws Throwable {
throws AddressOverflowException, CancelledException, IOException, VersionException {
try (UndoableTransaction tid = b.startTransaction()) { try (UndoableTransaction tid = b.startTransaction()) {
DBTraceGuestLanguage guest = DBTraceGuestPlatform guest =
manager.addGuestLanguage(b.getLanguage("x86:LE:32:default")); manager.addGuestPlatform(b.getCompiler("x86:LE:32:default", "gcc"));
guest.addMappedRange(b.addr(0x01000000), b.addr(guest, 0x02000000), 0x1000); guest.addMappedRange(b.addr(0x01000000), b.addr(guest, 0x02000000), 0x1000);
} }
File saved = b.save(); File saved = b.save();
try (ToyDBTraceBuilder r = new ToyDBTraceBuilder(saved)) { try (ToyDBTraceBuilder r = new ToyDBTraceBuilder(saved)) {
TraceGuestLanguage guest = TraceGuestPlatform guest =
r.trace.getLanguageManager().getGuestLanguages().iterator().next(); r.trace.getPlatformManager().getGuestPlatforms().iterator().next();
assertEquals(b.addr(guest, 0x02000800), guest.mapHostToGuest(b.addr(0x01000800))); assertEquals(b.addr(guest, 0x02000800), guest.mapHostToGuest(b.addr(0x01000800)));
} }
} }
@Test @Test
public void testMappedRangeGetHostLanguage() public void testMappedRangeGetHostLanguage() throws Throwable {
throws LanguageNotFoundException, AddressOverflowException {
try (UndoableTransaction tid = b.startTransaction()) { try (UndoableTransaction tid = b.startTransaction()) {
DBTraceGuestLanguage guest = DBTraceGuestPlatform guest =
manager.addGuestLanguage(b.getLanguage("x86:LE:32:default")); manager.addGuestPlatform(b.getCompiler("x86:LE:32:default", "gcc"));
DBTraceGuestLanguageMappedRange range = DBTraceGuestPlatformMappedRange range =
guest.addMappedRange(b.addr(0x01000000), b.addr(guest, 0x02000000), 0x1000); guest.addMappedRange(b.addr(0x01000000), b.addr(guest, 0x02000000), 0x1000);
assertEquals("Toy:BE:64:default", assertEquals("Toy:BE:64:default",
range.getHostLanguage().getLanguageID().getIdAsString()); range.getHostLanguage().getLanguageID().getIdAsString());
@ -232,49 +252,44 @@ public class DBTraceLanguageManagerTest extends AbstractGhidraHeadlessIntegratio
} }
@Test @Test
public void testMappedRangeGetHostRange() public void testMappedRangeGetHostRange() throws Throwable {
throws LanguageNotFoundException, AddressOverflowException {
try (UndoableTransaction tid = b.startTransaction()) { try (UndoableTransaction tid = b.startTransaction()) {
DBTraceGuestLanguage guest = DBTraceGuestPlatform guest =
manager.addGuestLanguage(b.getLanguage("x86:LE:32:default")); manager.addGuestPlatform(b.getCompiler("x86:LE:32:default", "gcc"));
DBTraceGuestLanguageMappedRange range = DBTraceGuestPlatformMappedRange range =
guest.addMappedRange(b.addr(0x01000000), b.addr(guest, 0x02000000), 0x1000); guest.addMappedRange(b.addr(0x01000000), b.addr(guest, 0x02000000), 0x1000);
assertEquals(b.range(0x01000000, 0x01000fff), range.getHostRange()); assertEquals(b.range(0x01000000, 0x01000fff), range.getHostRange());
} }
} }
@Test @Test
public void testMappedRangeGetGuestLanguage() public void testMappedRangeGetGuestPlatform() throws Throwable {
throws LanguageNotFoundException, AddressOverflowException {
try (UndoableTransaction tid = b.startTransaction()) { try (UndoableTransaction tid = b.startTransaction()) {
DBTraceGuestLanguage guest = DBTraceGuestPlatform guest =
manager.addGuestLanguage(b.getLanguage("x86:LE:32:default")); manager.addGuestPlatform(b.getCompiler("x86:LE:32:default", "gcc"));
DBTraceGuestLanguageMappedRange range = DBTraceGuestPlatformMappedRange range =
guest.addMappedRange(b.addr(0x01000000), b.addr(guest, 0x02000000), 0x1000); guest.addMappedRange(b.addr(0x01000000), b.addr(guest, 0x02000000), 0x1000);
assertEquals("x86:LE:32:default", assertEquals(guest, range.getGuestPlatform());
range.getGuestLanguage().getLanguageID().getIdAsString());
} }
} }
@Test @Test
public void testMappedRangeGetGuestRange() public void testMappedRangeGetGuestRange() throws Throwable {
throws LanguageNotFoundException, AddressOverflowException {
try (UndoableTransaction tid = b.startTransaction()) { try (UndoableTransaction tid = b.startTransaction()) {
DBTraceGuestLanguage guest = DBTraceGuestPlatform guest =
manager.addGuestLanguage(b.getLanguage("x86:LE:32:default")); manager.addGuestPlatform(b.getCompiler("x86:LE:32:default", "gcc"));
DBTraceGuestLanguageMappedRange range = DBTraceGuestPlatformMappedRange range =
guest.addMappedRange(b.addr(0x01000000), b.addr(guest, 0x02000000), 0x1000); guest.addMappedRange(b.addr(0x01000000), b.addr(guest, 0x02000000), 0x1000);
assertEquals(b.range(guest, 0x02000000, 0x02000fff), range.getGuestRange()); assertEquals(b.range(guest, 0x02000000, 0x02000fff), range.getGuestRange());
} }
} }
@Test @Test
public void testDeleteMappedRange() public void testDeleteMappedRange() throws Throwable {
throws LanguageNotFoundException, AddressOverflowException, CancelledException {
try (UndoableTransaction tid = b.startTransaction()) { try (UndoableTransaction tid = b.startTransaction()) {
DBTraceGuestLanguage guest = DBTraceGuestPlatform guest =
manager.addGuestLanguage(b.getLanguage("x86:LE:32:default")); manager.addGuestPlatform(b.getCompiler("x86:LE:32:default", "gcc"));
DBTraceGuestLanguageMappedRange range = DBTraceGuestPlatformMappedRange range =
guest.addMappedRange(b.addr(0x01000000), b.addr(guest, 0x02000000), 0x1000); guest.addMappedRange(b.addr(0x01000000), b.addr(guest, 0x02000000), 0x1000);
assertNotNull(guest.mapHostToGuest(b.addr(0x01000800))); // Sanity check assertNotNull(guest.mapHostToGuest(b.addr(0x01000800))); // Sanity check
assertNotNull(guest.mapGuestToHost(b.addr(guest, 0x02000800))); // Sanity check assertNotNull(guest.mapGuestToHost(b.addr(guest, 0x02000800))); // Sanity check
@ -291,12 +306,11 @@ public class DBTraceLanguageManagerTest extends AbstractGhidraHeadlessIntegratio
} }
@Test @Test
public void testDeleteMappedRangeThenUndo() public void testDeleteMappedRangeThenUndo() throws Throwable {
throws AddressOverflowException, IOException, CancelledException { DBTraceGuestPlatform guest;
DBTraceGuestLanguage guest; DBTraceGuestPlatformMappedRange range;
DBTraceGuestLanguageMappedRange range;
try (UndoableTransaction tid = b.startTransaction()) { 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); range = guest.addMappedRange(b.addr(0x01000000), b.addr(guest, 0x02000000), 0x1000);
assertNotNull(guest.mapHostToGuest(b.addr(0x01000800))); // Sanity check assertNotNull(guest.mapHostToGuest(b.addr(0x01000800))); // Sanity check
assertNotNull(guest.mapGuestToHost(b.addr(guest, 0x02000800))); // Sanity check assertNotNull(guest.mapGuestToHost(b.addr(guest, 0x02000800))); // Sanity check
@ -310,19 +324,17 @@ public class DBTraceLanguageManagerTest extends AbstractGhidraHeadlessIntegratio
b.trace.undo(); 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.mapHostToGuest(b.addr(0x01000800)));
assertNotNull(guest.mapGuestToHost(b.addr(guest, 0x02000800))); assertNotNull(guest.mapGuestToHost(b.addr(guest, 0x02000800)));
} }
@Test @Test
public void testDeleteGuestLanguageDeletesMappedRanges() public void testDeleteGuestPlatformDeletesMappedRanges() throws Throwable {
throws LanguageNotFoundException, AddressOverflowException, CancelledException {
// TODO: Check that it also deletes code units // TODO: Check that it also deletes code units
DBTraceGuestLanguage guest; DBTraceGuestPlatform guest;
try (UndoableTransaction tid = b.startTransaction()) { 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); guest.addMappedRange(b.addr(0x01000000), b.addr(guest, 0x02000000), 0x1000);
} }
@ -333,12 +345,11 @@ public class DBTraceLanguageManagerTest extends AbstractGhidraHeadlessIntegratio
} }
@Test @Test
public void testDeleteGuestLanguageThenUndo() public void testDeleteGuestPlatformThenUndo() throws Throwable {
throws AddressOverflowException, CancelledException, IOException {
// TODO: Check that it also deletes code units // TODO: Check that it also deletes code units
DBTraceGuestLanguage guest; DBTraceGuestPlatform guest;
try (UndoableTransaction tid = b.startTransaction()) { 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); guest.addMappedRange(b.addr(0x01000000), b.addr(guest, 0x02000000), 0x1000);
} }
@ -348,7 +359,7 @@ public class DBTraceLanguageManagerTest extends AbstractGhidraHeadlessIntegratio
b.trace.undo(); 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))); 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.test.AbstractGhidraHeadlessIntegrationTest;
import ghidra.trace.database.ToyDBTraceBuilder; import ghidra.trace.database.ToyDBTraceBuilder;
import ghidra.trace.database.context.DBTraceRegisterContextManager; 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.ImmutableTraceAddressSnapRange;
import ghidra.trace.model.listing.*; import ghidra.trace.model.listing.*;
import ghidra.trace.model.stack.TraceStack; import ghidra.trace.model.stack.TraceStack;
@ -187,7 +187,7 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes
@Test @Test
public void testAddInstruction() throws CodeUnitInsertionException { public void testAddInstruction() throws CodeUnitInsertionException {
try (UndoableTransaction tid = b.startTransaction()) { 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()) { try (UndoableTransaction tid = b.startTransaction()) {
b.trace.getMemoryManager().putBytes(10, b.addr(0x4001), b.buf(0xaa)); b.trace.getMemoryManager().putBytes(10, b.addr(0x4001), b.buf(0xaa));
TraceInstruction i4000 = 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()); assertEquals(Range.closed(0L, 9L), i4000.getLifespan());
} }
} }
@ -206,15 +206,15 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes
try (UndoableTransaction tid = b.startTransaction()) { try (UndoableTransaction tid = b.startTransaction()) {
b.trace.getMemoryManager().putBytes(-5L, b.addr(0x4001), b.buf(0xaa)); b.trace.getMemoryManager().putBytes(-5L, b.addr(0x4001), b.buf(0xaa));
TraceInstruction i4000 = 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()); assertEquals(Range.closed(-10L, -6L), i4000.getLifespan());
TraceInstruction i4004 = 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()); assertEquals(Range.closed(-1L, -1L), i4004.getLifespan());
TraceInstruction i4008 = 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()); assertEquals(Range.closed(-10L, -1L), i4008.getLifespan());
} }
} }
@ -223,7 +223,7 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes
public void testPutBytesTruncatesInstruction() throws CodeUnitInsertionException { public void testPutBytesTruncatesInstruction() throws CodeUnitInsertionException {
try (UndoableTransaction tid = b.startTransaction()) { try (UndoableTransaction tid = b.startTransaction()) {
TraceInstruction i4000 = 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(b.addr(0x4001), i4000.getMaxAddress());
assertEquals(Range.atLeast(0L), i4000.getLifespan()); assertEquals(Range.atLeast(0L), i4000.getLifespan());
b.trace.getMemoryManager().putBytes(10, b.addr(0x4001), b.buf(1)); 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 { public void testPutBytesDeletesInstruction() throws CodeUnitInsertionException {
try (UndoableTransaction tid = b.startTransaction()) { try (UndoableTransaction tid = b.startTransaction()) {
TraceInstruction i4000 = 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(b.addr(0x4001), i4000.getMaxAddress());
assertEquals(Range.atLeast(0L), i4000.getLifespan()); assertEquals(Range.atLeast(0L), i4000.getLifespan());
b.trace.getMemoryManager().putBytes(0, b.addr(0x4001), b.buf(1)); b.trace.getMemoryManager().putBytes(0, b.addr(0x4001), b.buf(1));
@ -267,14 +267,14 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes
} }
try { try {
b.addInstruction(1, b.addr(0x4001), b.language); b.addInstruction(1, b.addr(0x4001), null);
fail(); fail();
} }
catch (CodeUnitInsertionException e) { catch (CodeUnitInsertionException e) {
// pass // 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 { try {
b.addData(1, b.addr(0x4005), ByteDataType.dataType, 1); b.addData(1, b.addr(0x4005), ByteDataType.dataType, 1);
@ -285,7 +285,7 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes
} }
try { try {
b.addInstruction(1, b.addr(0x4005), b.language); b.addInstruction(1, b.addr(0x4005), null);
} }
catch (CodeUnitInsertionException e) { catch (CodeUnitInsertionException e) {
// pass // pass
@ -350,8 +350,7 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes
assertAllNullFunc(v -> v.getAt(9, b.addr(0x4003))); assertAllNullFunc(v -> v.getAt(9, b.addr(0x4003)));
assertUndefinedFunc(v -> v.getAt(9, b.addr(0x4004))); assertUndefinedFunc(v -> v.getAt(9, b.addr(0x4004)));
TraceInstruction i4005 = TraceInstruction i4005 = b.addInstruction(0, b.addr(0x4005), null, b.buf(0xf4, 0));
b.addInstruction(0, b.addr(0x4005), b.language, b.buf(0xf4, 0));
i4005.setEndSnap(5); i4005.setEndSnap(5);
assertUndefinedFunc(v -> v.getAt(0, b.addr(0x4004))); assertUndefinedFunc(v -> v.getAt(0, b.addr(0x4004)));
assertInstructionFunc(i4005, v -> v.getAt(0, b.addr(0x4005))); 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))); assertDataFunc(d4000, v -> v.getContaining(9, b.addr(0x4003)));
assertUndefinedFunc(v -> v.getContaining(9, b.addr(0x4004))); assertUndefinedFunc(v -> v.getContaining(9, b.addr(0x4004)));
TraceInstruction i4005 = TraceInstruction i4005 = b.addInstruction(0, b.addr(0x4005), null, b.buf(0xf4, 0));
b.addInstruction(0, b.addr(0x4005), b.language, b.buf(0xf4, 0));
i4005.setEndSnap(5); i4005.setEndSnap(5);
assertUndefinedFunc(v -> v.getContaining(0, b.addr(0x4004))); assertUndefinedFunc(v -> v.getContaining(0, b.addr(0x4004)));
@ -451,7 +449,7 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes
d4000.setEndSnap(9); d4000.setEndSnap(9);
d4004 = b.addData(0, b.addr(0x4004), IntegerDataType.dataType, b.buf(5, 6, 7, 8)); d4004 = b.addData(0, b.addr(0x4004), IntegerDataType.dataType, b.buf(5, 6, 7, 8));
d4004.setEndSnap(5); 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); i4008.setEndSnap(9);
} }
@ -593,7 +591,7 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes
d4000.setEndSnap(9); d4000.setEndSnap(9);
d4004 = b.addData(0, b.addr(0x4004), IntegerDataType.dataType, b.buf(5, 6, 7, 8)); d4004 = b.addData(0, b.addr(0x4004), IntegerDataType.dataType, b.buf(5, 6, 7, 8));
d4004.setEndSnap(5); 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); i4008.setEndSnap(9);
} }
@ -732,7 +730,7 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes
d4000.setEndSnap(9); d4000.setEndSnap(9);
d4004 = b.addData(0, b.addr(0x4004), IntegerDataType.dataType, b.buf(5, 6, 7, 8)); d4004 = b.addData(0, b.addr(0x4004), IntegerDataType.dataType, b.buf(5, 6, 7, 8));
d4004.setEndSnap(5); 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); i4008.setEndSnap(9);
} }
@ -872,7 +870,7 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes
d4000.setEndSnap(9); d4000.setEndSnap(9);
d4004 = b.addData(0, b.addr(0x4004), IntegerDataType.dataType, b.buf(5, 6, 7, 8)); d4004 = b.addData(0, b.addr(0x4004), IntegerDataType.dataType, b.buf(5, 6, 7, 8));
d4004.setEndSnap(5); 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); i4008.setEndSnap(9);
} }
@ -1086,7 +1084,7 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes
d4000.setEndSnap(9); d4000.setEndSnap(9);
d4004 = b.addData(0, b.addr(0x4004), IntegerDataType.dataType, b.buf(5, 6, 7, 8)); d4004 = b.addData(0, b.addr(0x4004), IntegerDataType.dataType, b.buf(5, 6, 7, 8));
d4004.setEndSnap(5); 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); i4008.setEndSnap(9);
} }
TraceData u3fff = manager.undefinedData().getAt(0, b.addr(0x3fff)); TraceData u3fff = manager.undefinedData().getAt(0, b.addr(0x3fff));
@ -1139,7 +1137,7 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes
TraceInstruction iCodeMax; TraceInstruction iCodeMax;
try (UndoableTransaction tid = b.startTransaction()) { 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))); 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))); manager.undefinedData().getFloor(0, b.data(0x0003)));
try (UndoableTransaction tid = b.startTransaction()) { 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)); TraceData uCodePre = manager.undefinedData().getAt(0, b.addr(-0x0003));
assertUndefinedWithAddr(b.addr(-0x0003), uCodePre); assertUndefinedWithAddr(b.addr(-0x0003), uCodePre);
@ -1211,7 +1209,7 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes
try (UndoableTransaction tid = b.startTransaction()) { try (UndoableTransaction tid = b.startTransaction()) {
d4000 = b.addData(0, b.addr(0x4000), IntegerDataType.dataType, b.buf(1, 2, 3, 4)); d4000 = b.addData(0, b.addr(0x4000), IntegerDataType.dataType, b.buf(1, 2, 3, 4));
d4000.setEndSnap(9); 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); i4008.setEndSnap(9);
} }
@ -1346,7 +1344,7 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes
d4000.setEndSnap(9); d4000.setEndSnap(9);
d4004 = b.addData(0, b.addr(0x4004), IntegerDataType.dataType, b.buf(5, 6, 7, 8)); d4004 = b.addData(0, b.addr(0x4004), IntegerDataType.dataType, b.buf(5, 6, 7, 8));
d4004.setEndSnap(5); 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); i4008.setEndSnap(9);
} }
@ -1416,7 +1414,7 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes
d4000.setEndSnap(9); d4000.setEndSnap(9);
d4004 = b.addData(0, b.addr(0x4004), IntegerDataType.dataType, b.buf(5, 6, 7, 8)); d4004 = b.addData(0, b.addr(0x4004), IntegerDataType.dataType, b.buf(5, 6, 7, 8));
d4004.setEndSnap(5); 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); i4008.setEndSnap(9);
} }
@ -1491,7 +1489,7 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes
d4000.setEndSnap(9); d4000.setEndSnap(9);
d4004 = b.addData(0, b.addr(0x4004), IntegerDataType.dataType, b.buf(5, 6, 7, 8)); d4004 = b.addData(0, b.addr(0x4004), IntegerDataType.dataType, b.buf(5, 6, 7, 8));
d4004.setEndSnap(5); 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); i4008.setEndSnap(9);
} }
@ -1575,7 +1573,7 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes
d4000.setEndSnap(9); d4000.setEndSnap(9);
d4004 = b.addData(0, b.addr(0x4004), IntegerDataType.dataType, b.buf(5, 6, 7, 8)); d4004 = b.addData(0, b.addr(0x4004), IntegerDataType.dataType, b.buf(5, 6, 7, 8));
d4004.setEndSnap(5); 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); i4008.setEndSnap(9);
} }
@ -1674,7 +1672,7 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes
d4000.setEndSnap(9); d4000.setEndSnap(9);
d4004 = b.addData(0, b.addr(0x4004), IntegerDataType.dataType, b.buf(5, 6, 7, 8)); d4004 = b.addData(0, b.addr(0x4004), IntegerDataType.dataType, b.buf(5, 6, 7, 8));
d4004.setEndSnap(5); 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); i4008.setEndSnap(9);
// Clear one of the data before a context space is created // Clear one of the data before a context space is created
@ -1708,22 +1706,21 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes
} }
@Test @Test
@Ignore("Looks related to GP-479")
public void testAddGuestInstructionThenRemoveAndDelete() throws AddressOverflowException, public void testAddGuestInstructionThenRemoveAndDelete() throws AddressOverflowException,
CodeUnitInsertionException, IOException, CancelledException { CodeUnitInsertionException, IOException, CancelledException {
DBTraceLanguageManager langMan = b.trace.getLanguageManager(); DBTracePlatformManager langMan = b.trace.getPlatformManager();
Language x86 = getSLEIGH_X86_LANGUAGE(); Language x86 = getSLEIGH_X86_LANGUAGE();
DBTraceGuestLanguage guest; DBTraceGuestPlatform guest;
DBTraceGuestLanguageMappedRange mappedRange; DBTraceGuestPlatformMappedRange mappedRange;
TraceInstruction g4000; TraceInstruction g4000;
TraceInstruction i4001; TraceInstruction i4001;
TraceData d4003; TraceData d4003;
try (UndoableTransaction tid = b.startTransaction()) { 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); mappedRange = guest.addMappedRange(b.addr(0x0000), b.addr(guest, 0x0000), 1L << 32);
g4000 = b.addInstruction(0, b.addr(0x4000), x86, b.buf(0x90)); g4000 = b.addInstruction(0, b.addr(0x4000), guest, b.buf(0x90));
i4001 = b.addInstruction(0, b.addr(0x4001), b.language, b.buf(0xf4, 0)); 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)); 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(); 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()) { try (UndoableTransaction tid = b.startTransaction()) {
guest.delete(new ConsoleTaskMonitor()); guest.delete(new ConsoleTaskMonitor());
} }
assertUndefinedWithAddr(b.addr(0x4000), manager.codeUnits().getAt(0, b.addr(0x4000))); assertUndefinedWithAddr(b.addr(0x4000), manager.codeUnits().getAt(0, b.addr(0x4000)));
assertEquals(i4001, manager.codeUnits().getAt(0, b.addr(0x4001))); // TODO: Definitely part of GP-479. These should be able to keep their identities.
assertEquals(d4003, manager.codeUnits().getAt(0, b.addr(0x4003))); //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 @Test
public void testSaveAndLoad() throws Exception { public void testSaveAndLoad() throws Exception {
try (UndoableTransaction tid = b.startTransaction()) { 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); TraceThread thread = b.getOrAddThread("Thread 1", 0);
DBTraceCodeRegisterSpace regCode = manager.getCodeRegisterSpace(thread, true); DBTraceCodeRegisterSpace regCode = manager.getCodeRegisterSpace(thread, true);
@ -1797,7 +1801,7 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes
@Test @Test
public void testUndoThenRedo() throws Exception { public void testUndoThenRedo() throws Exception {
try (UndoableTransaction tid = b.startTransaction()) { 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); TraceThread thread = b.getOrAddThread("Thread 1", 0);
DBTraceCodeRegisterSpace regCode = manager.getCodeRegisterSpace(thread, true); DBTraceCodeRegisterSpace regCode = manager.getCodeRegisterSpace(thread, true);
@ -1850,7 +1854,7 @@ public class DBTraceCodeManagerTest extends AbstractGhidraHeadlessIntegrationTes
b.trace.getBaseAddressFactory().getDefaultAddressSpace()); b.trace.getBaseAddressFactory().getDefaultAddressSpace());
DBTraceCodeSpace space = manager.getCodeSpace(os, true); 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<>(); List<CodeUnit> all = new ArrayList<>();
space.definedUnits().get(0, true).forEach(all::add); 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: 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.DBTraceMemoryRegisterSpace;
import ghidra.trace.database.memory.DBTraceMemorySpace; import ghidra.trace.database.memory.DBTraceMemorySpace;
import ghidra.trace.database.symbol.DBTraceReference; 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.TraceData;
import ghidra.trace.model.listing.TraceInstruction; import ghidra.trace.model.listing.TraceInstruction;
import ghidra.trace.model.memory.TraceMemoryFlag; import ghidra.trace.model.memory.TraceMemoryFlag;
@ -242,7 +242,7 @@ public class DBTraceCodeUnitTest extends AbstractGhidraHeadlessIntegrationTest
TraceOverlappedRegionException, DuplicateNameException { TraceOverlappedRegionException, DuplicateNameException {
TraceInstruction ins; TraceInstruction ins;
try (UndoableTransaction tid = b.startTransaction()) { 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)); TraceData und = manager.undefinedData().getAt(0, b.addr(0x4006));
@ -281,7 +281,7 @@ public class DBTraceCodeUnitTest extends AbstractGhidraHeadlessIntegrationTest
public void testGetProgram() throws CodeUnitInsertionException { public void testGetProgram() throws CodeUnitInsertionException {
TraceInstruction i4004; TraceInstruction i4004;
try (UndoableTransaction tid = b.startTransaction()) { 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()); assertEquals(0, i4004.getProgram().getSnap());
@ -291,7 +291,7 @@ public class DBTraceCodeUnitTest extends AbstractGhidraHeadlessIntegrationTest
public void testGetMemory() throws CodeUnitInsertionException { public void testGetMemory() throws CodeUnitInsertionException {
TraceInstruction i4004; TraceInstruction i4004;
try (UndoableTransaction tid = b.startTransaction()) { 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()); assertEquals(i4004.getProgram().getMemory(), i4004.getMemory());
@ -300,14 +300,14 @@ public class DBTraceCodeUnitTest extends AbstractGhidraHeadlessIntegrationTest
@Test @Test
public void testIsBigEndian() throws CodeUnitInsertionException, AddressOverflowException { public void testIsBigEndian() throws CodeUnitInsertionException, AddressOverflowException {
Language x86 = getSLEIGH_X86_LANGUAGE(); Language x86 = getSLEIGH_X86_LANGUAGE();
TraceGuestLanguage guest; TraceGuestPlatform guest;
TraceInstruction i4004; TraceInstruction i4004;
TraceInstruction g4006; TraceInstruction g4006;
try (UndoableTransaction tid = b.startTransaction()) { 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));
guest = b.trace.getLanguageManager().addGuestLanguage(x86); guest = b.trace.getPlatformManager().addGuestPlatform(x86.getDefaultCompilerSpec());
guest.addMappedRange(b.addr(0x0000), b.addr(guest, 0x0000), 1L << 32); 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()); assertTrue(i4004.isBigEndian());
@ -319,8 +319,8 @@ public class DBTraceCodeUnitTest extends AbstractGhidraHeadlessIntegrationTest
TraceInstruction i4004; TraceInstruction i4004;
TraceInstruction i4006; TraceInstruction i4006;
try (UndoableTransaction tid = b.startTransaction()) { 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));
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));
} }
assertFalse(i4004.hasProperty("myVoid")); assertFalse(i4004.hasProperty("myVoid"));
@ -455,8 +455,8 @@ public class DBTraceCodeUnitTest extends AbstractGhidraHeadlessIntegrationTest
TraceInstruction i4004; TraceInstruction i4004;
TraceInstruction i4006; TraceInstruction i4006;
try (UndoableTransaction tid = b.startTransaction()) { 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));
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));
} }
try (UndoableTransaction tid = b.startTransaction()) { 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 // TODO: Decide whether or not to shrink the comment lifespan with the unit lifespan
assertEquals(Range.atLeast(0L), c4004.getLifespan()); 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.PRE_COMMENT, "Get this back in the mix");
i4004_10.setComment(CodeUnit.EOL_COMMENT, "A different comment"); i4004_10.setComment(CodeUnit.EOL_COMMENT, "A different comment");
} }
@ -538,8 +538,8 @@ public class DBTraceCodeUnitTest extends AbstractGhidraHeadlessIntegrationTest
TraceInstruction i4006; TraceInstruction i4006;
TraceData d4008; TraceData d4008;
try (UndoableTransaction tid = b.startTransaction()) { 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));
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));
d4008 = b.addData(0, b.addr(0x4008), LongDataType.dataType, b.buf(1, 2, 3, 4)); 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; TraceInstruction i4006;
try (UndoableTransaction tid = b.startTransaction()) { try (UndoableTransaction tid = b.startTransaction()) {
d4000 = b.addData(0, b.addr(0x4000), LongDataType.dataType, b.buf(1, 2, 3, 4)); 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));
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));
} }
Set<TraceReference> refs; Set<TraceReference> refs;
@ -708,7 +708,7 @@ public class DBTraceCodeUnitTest extends AbstractGhidraHeadlessIntegrationTest
TraceData undefined; TraceData undefined;
TraceData undReg; TraceData undReg;
try (UndoableTransaction tid = b.startTransaction()) { 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)); undefined = manager.undefinedData().getAt(0, b.addr(0x4006));
thread = b.getOrAddThread("Thread 1", 0); thread = b.getOrAddThread("Thread 1", 0);
@ -739,7 +739,7 @@ public class DBTraceCodeUnitTest extends AbstractGhidraHeadlessIntegrationTest
TraceInstruction i4004; TraceInstruction i4004;
try (UndoableTransaction tid = b.startTransaction()) { try (UndoableTransaction tid = b.startTransaction()) {
d4000 = b.addData(0, b.addr(0x4000), LongDataType.dataType, b.buf(1, 2, 3, 4)); 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); d4000.setEndSnap(9);
assertEquals(Range.closed(0L, 9L), d4000.getLifespan()); assertEquals(Range.closed(0L, 9L), d4000.getLifespan());
@ -784,7 +784,7 @@ public class DBTraceCodeUnitTest extends AbstractGhidraHeadlessIntegrationTest
@Test @Test
public void testGetBytes() throws Exception { public void testGetBytes() throws Exception {
Language x86 = getSLEIGH_X86_LANGUAGE(); Language x86 = getSLEIGH_X86_LANGUAGE();
TraceGuestLanguage guest; TraceGuestPlatform guest;
TraceData data; TraceData data;
TraceData und; TraceData und;
@ -803,9 +803,9 @@ public class DBTraceCodeUnitTest extends AbstractGhidraHeadlessIntegrationTest
DBTraceCodeRegisterSpace regCode = manager.getCodeRegisterSpace(thread, true); DBTraceCodeRegisterSpace regCode = manager.getCodeRegisterSpace(thread, true);
reg = regCode.definedData().create(Range.atLeast(0L), r4, PointerDataType.dataType); 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); 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; ByteBuffer buf;
@ -1039,13 +1039,13 @@ public class DBTraceCodeUnitTest extends AbstractGhidraHeadlessIntegrationTest
.addRegion("myRegion", Range.atLeast(0L), .addRegion("myRegion", Range.atLeast(0L),
b.range(0x4000, 0x4fff), TraceMemoryFlag.READ); 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()); 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()); 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()); 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()); assertEquals("call r4", i400a.toString());
} }
@ -1191,7 +1191,7 @@ public class DBTraceCodeUnitTest extends AbstractGhidraHeadlessIntegrationTest
TraceInstruction i4004; TraceInstruction i4004;
try (UndoableTransaction tid = b.startTransaction()) { 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 // TODO: Test with non-default context
@ -1240,7 +1240,7 @@ public class DBTraceCodeUnitTest extends AbstractGhidraHeadlessIntegrationTest
TraceData d4006; TraceData d4006;
try (UndoableTransaction tid = b.startTransaction()) { try (UndoableTransaction tid = b.startTransaction()) {
d4000 = b.addData(0, b.addr(0x4000), LongDataType.dataType, b.buf(1, 2, 3, 4)); 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, d4006 = b.addData(0, b.addr(0x4006), PointerDataType.dataType,
b.buf(0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00)); b.buf(0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00));
} }
@ -1260,7 +1260,7 @@ public class DBTraceCodeUnitTest extends AbstractGhidraHeadlessIntegrationTest
TraceData d4006; TraceData d4006;
try (UndoableTransaction tid = b.startTransaction()) { try (UndoableTransaction tid = b.startTransaction()) {
d4000 = b.addData(0, b.addr(0x4000), LongDataType.dataType, b.buf(1, 2, 3, 4)); 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, d4006 = b.addData(0, b.addr(0x4006), PointerDataType.dataType,
b.buf(0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00)); b.buf(0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00));
} }
@ -1285,14 +1285,14 @@ public class DBTraceCodeUnitTest extends AbstractGhidraHeadlessIntegrationTest
@Test @Test
public void testGetLanguage() throws CodeUnitInsertionException, AddressOverflowException { public void testGetLanguage() throws CodeUnitInsertionException, AddressOverflowException {
Language x86 = getSLEIGH_X86_LANGUAGE(); Language x86 = getSLEIGH_X86_LANGUAGE();
TraceGuestLanguage guest; TraceGuestPlatform guest;
TraceInstruction i4004; TraceInstruction i4004;
TraceInstruction g4006; TraceInstruction g4006;
try (UndoableTransaction tid = b.startTransaction()) { 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));
guest = b.trace.getLanguageManager().addGuestLanguage(x86); guest = b.trace.getPlatformManager().addGuestPlatform(x86.getDefaultCompilerSpec());
guest.addMappedRange(b.addr(0x0000), b.addr(guest, 0x0000), 1L << 32); 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)); TraceData u4007 = manager.undefinedData().getAt(0, b.addr(0x4007));
@ -1305,7 +1305,7 @@ public class DBTraceCodeUnitTest extends AbstractGhidraHeadlessIntegrationTest
public void testToString() throws CodeUnitInsertionException, AddressOverflowException, public void testToString() throws CodeUnitInsertionException, AddressOverflowException,
TraceOverlappedRegionException, DuplicateNameException { TraceOverlappedRegionException, DuplicateNameException {
Language x86 = getSLEIGH_X86_LANGUAGE(); Language x86 = getSLEIGH_X86_LANGUAGE();
TraceGuestLanguage guest; TraceGuestPlatform guest;
TraceData d4000; TraceData d4000;
TraceInstruction i4004; TraceInstruction i4004;
TraceInstruction g4006; TraceInstruction g4006;
@ -1316,13 +1316,13 @@ public class DBTraceCodeUnitTest extends AbstractGhidraHeadlessIntegrationTest
.addRegion("myRegion", Range.atLeast(0L), .addRegion("myRegion", Range.atLeast(0L),
b.range(0x4000, 0x4fff), TraceMemoryFlag.READ); 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); 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)); 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)); i4004 = b.addInstruction(0, b.addr(0x4004), null, b.buf(0xc8, 0x47));
g4006 = b.addInstruction(0, b.addr(0x4006), x86, b.buf(0x90)); g4006 = b.addInstruction(0, b.addr(0x4006), guest, b.buf(0x90));
i4007 = b.addInstruction(0, b.addr(0x4007), b.language, b.buf(0xff, 0xfd)); i4007 = b.addInstruction(0, b.addr(0x4007), null, b.buf(0xff, 0xfd));
} }
TraceData u4009 = manager.undefinedData().getAt(0, b.addr(0x4009)); 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.program.model.mem.MemoryBlock;
import ghidra.test.AbstractGhidraHeadlessIntegrationTest; import ghidra.test.AbstractGhidraHeadlessIntegrationTest;
import ghidra.trace.database.ToyDBTraceBuilder; 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.listing.*;
import ghidra.trace.database.memory.DBTraceMemoryManager; import ghidra.trace.database.memory.DBTraceMemoryManager;
import ghidra.trace.database.memory.DBTraceMemorySpace; import ghidra.trace.database.memory.DBTraceMemorySpace;
@ -108,13 +108,15 @@ public class DBTraceDisassemblerIntegrationTest extends AbstractGhidraHeadlessIn
b.trace.getMemoryManager().getMemorySpace(b.language.getDefaultSpace(), true); b.trace.getMemoryManager().getMemorySpace(b.language.getDefaultSpace(), true);
space.putBytes(0, b.addr(0x4000), b.buf(0x90)); 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); 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.... * TODO: The more I look, the more I think I need a fully-mapped program view :( As
// The disassembler uses bookmarks, context, etc. for feedback. It'd be nice to * annoying as it is, I plan to do it as a wrapper, not as an extension.... The
// have that * disassembler uses bookmarks, context, etc. for feedback. It'd be nice to have that.
*/
RegisterValue defaultContextValue = RegisterValue defaultContextValue =
b.trace.getRegisterContextManager() b.trace.getRegisterContextManager()
.getDefaultContext(x86) .getDefaultContext(x86)
@ -125,7 +127,7 @@ public class DBTraceDisassemblerIntegrationTest extends AbstractGhidraHeadlessIn
guest.getMappedMemBuffer(0, b.addr(guest, 0x00400000)), defaultContextValue, 1)); guest.getMappedMemBuffer(0, b.addr(guest, 0x00400000)), defaultContextValue, 1));
DBTraceCodeManager code = b.trace.getCodeManager(); 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)); DBTraceInstruction ins = code.instructions().getAt(0, b.addr(0x4000));
// TODO: This is great, but probably incomplete. // TODO: This is great, but probably incomplete.

View file

@ -114,7 +114,7 @@ public class DBTraceProgramViewListingTest extends AbstractGhidraHeadlessIntegra
CodeUnitInsertionException { CodeUnitInsertionException {
Instruction ins; Instruction ins;
try (UndoableTransaction tid = b.startTransaction()) { 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()); assertEquals("ret", ins.toString());
} }
@ -138,7 +138,7 @@ public class DBTraceProgramViewListingTest extends AbstractGhidraHeadlessIntegra
Instruction i4005; Instruction i4005;
try (UndoableTransaction tid = b.startTransaction()) { 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))); assertEquals(i4005, listing.getCodeUnitAt(b.addr(0x4005)));
assertNull(listing.getCodeUnitAt(b.addr(0x4006))); assertNull(listing.getCodeUnitAt(b.addr(0x4006)));
@ -163,7 +163,7 @@ public class DBTraceProgramViewListingTest extends AbstractGhidraHeadlessIntegra
assertUndefined(listing.getCodeUnitContaining(b.addr(0x4005))); assertUndefined(listing.getCodeUnitContaining(b.addr(0x4005)));
Instruction i4005; Instruction i4005;
try (UndoableTransaction tid = b.startTransaction()) { 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(0x4005)));
assertEquals(i4005, listing.getCodeUnitContaining(b.addr(0x4006))); assertEquals(i4005, listing.getCodeUnitContaining(b.addr(0x4006)));
@ -189,7 +189,7 @@ public class DBTraceProgramViewListingTest extends AbstractGhidraHeadlessIntegra
assertEquals(b.addr(0x4005), cu.getAddress()); assertEquals(b.addr(0x4005), cu.getAddress());
Instruction i4005; Instruction i4005;
try (UndoableTransaction tid = b.startTransaction()) { 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))); assertEquals(i4005, listing.getCodeUnitAfter(b.addr(0x4004)));
assertUndefined(cu = listing.getCodeUnitAfter(b.addr(0x4005))); assertUndefined(cu = listing.getCodeUnitAfter(b.addr(0x4005)));
@ -215,7 +215,7 @@ public class DBTraceProgramViewListingTest extends AbstractGhidraHeadlessIntegra
assertEquals(b.addr(0x4005), cu.getAddress()); assertEquals(b.addr(0x4005), cu.getAddress());
Instruction i4005; Instruction i4005;
try (UndoableTransaction tid = b.startTransaction()) { 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))); assertEquals(i4005, listing.getCodeUnitBefore(b.addr(0x4006)));
assertUndefined(cu = listing.getCodeUnitBefore(b.addr(0x4005))); assertUndefined(cu = listing.getCodeUnitBefore(b.addr(0x4005)));
@ -291,7 +291,7 @@ public class DBTraceProgramViewListingTest extends AbstractGhidraHeadlessIntegra
Instruction i4005; Instruction i4005;
try (UndoableTransaction tid = b.startTransaction()) { try (UndoableTransaction tid = b.startTransaction()) {
d4000 = b.addData(0, b.addr(0x4000), Undefined4DataType.dataType, b.buf(1, 2, 3, 4)); 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)); sample = takeN(10, listing.getCodeUnits(true));
@ -355,7 +355,7 @@ public class DBTraceProgramViewListingTest extends AbstractGhidraHeadlessIntegra
assertNull(listing.getInstructionAt(b.addr(0x4005))); assertNull(listing.getInstructionAt(b.addr(0x4005)));
Instruction i4005; Instruction i4005;
try (UndoableTransaction tid = b.startTransaction()) { 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))); assertEquals(i4005, listing.getInstructionAt(b.addr(0x4005)));
assertNull(listing.getInstructionAt(b.addr(0x4006))); assertNull(listing.getInstructionAt(b.addr(0x4006)));
@ -368,7 +368,7 @@ public class DBTraceProgramViewListingTest extends AbstractGhidraHeadlessIntegra
assertNull(listing.getInstructionContaining(b.addr(0x4005))); assertNull(listing.getInstructionContaining(b.addr(0x4005)));
Instruction i4005; Instruction i4005;
try (UndoableTransaction tid = b.startTransaction()) { 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(0x4005)));
assertEquals(i4005, listing.getInstructionContaining(b.addr(0x4006))); assertEquals(i4005, listing.getInstructionContaining(b.addr(0x4006)));
@ -381,7 +381,7 @@ public class DBTraceProgramViewListingTest extends AbstractGhidraHeadlessIntegra
assertNull(listing.getInstructionAfter(b.addr(0x4004))); assertNull(listing.getInstructionAfter(b.addr(0x4004)));
Instruction i4005; Instruction i4005;
try (UndoableTransaction tid = b.startTransaction()) { 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))); assertEquals(i4005, listing.getInstructionAfter(b.addr(0x4004)));
assertNull(listing.getInstructionAfter(b.addr(0x4005))); assertNull(listing.getInstructionAfter(b.addr(0x4005)));
@ -393,7 +393,7 @@ public class DBTraceProgramViewListingTest extends AbstractGhidraHeadlessIntegra
assertNull(listing.getInstructionBefore(b.addr(0x4006))); assertNull(listing.getInstructionBefore(b.addr(0x4006)));
Instruction i4005; Instruction i4005;
try (UndoableTransaction tid = b.startTransaction()) { 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))); assertEquals(i4005, listing.getInstructionBefore(b.addr(0x4006)));
assertNull(listing.getInstructionBefore(b.addr(0x4005))); assertNull(listing.getInstructionBefore(b.addr(0x4005)));
@ -421,9 +421,9 @@ public class DBTraceProgramViewListingTest extends AbstractGhidraHeadlessIntegra
Instruction i4007; Instruction i4007;
Instruction i400a; Instruction i400a;
try (UndoableTransaction tid = b.startTransaction()) { 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));
i4007 = b.addInstruction(0, b.addr(0x4007), b.language, b.buf(0xf4, 0)); i4007 = b.addInstruction(0, b.addr(0x4007), null, b.buf(0xf4, 0));
i400a = b.addInstruction(0, b.addr(0x400a), b.language, 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)); 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))); assertUndefined(listing.getDataAt(b.addr(0x4005)));
try (UndoableTransaction tid = b.startTransaction()) { 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(0x4005)));
assertNull(listing.getDataAt(b.addr(0x4006))); assertNull(listing.getDataAt(b.addr(0x4006)));
@ -477,7 +477,7 @@ public class DBTraceProgramViewListingTest extends AbstractGhidraHeadlessIntegra
assertUndefined(listing.getDataContaining(b.addr(0x4005))); assertUndefined(listing.getDataContaining(b.addr(0x4005)));
try (UndoableTransaction tid = b.startTransaction()) { 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(0x4005)));
assertNull(listing.getDataContaining(b.addr(0x4006))); assertNull(listing.getDataContaining(b.addr(0x4006)));
@ -502,7 +502,7 @@ public class DBTraceProgramViewListingTest extends AbstractGhidraHeadlessIntegra
assertUndefined(cu = listing.getDataAfter(b.addr(0x4004))); assertUndefined(cu = listing.getDataAfter(b.addr(0x4004)));
assertEquals(b.addr(0x4005), cu.getAddress()); assertEquals(b.addr(0x4005), cu.getAddress());
try (UndoableTransaction tid = b.startTransaction()) { 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))); assertUndefined(cu = listing.getDataAfter(b.addr(0x4004)));
assertEquals(b.addr(0x4007), cu.getAddress()); assertEquals(b.addr(0x4007), cu.getAddress());
@ -526,7 +526,7 @@ public class DBTraceProgramViewListingTest extends AbstractGhidraHeadlessIntegra
assertUndefined(cu = listing.getDataBefore(b.addr(0x4006))); assertUndefined(cu = listing.getDataBefore(b.addr(0x4006)));
assertEquals(b.addr(0x4005), cu.getAddress()); assertEquals(b.addr(0x4005), cu.getAddress());
try (UndoableTransaction tid = b.startTransaction()) { 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))); assertUndefined(cu = listing.getDataBefore(b.addr(0x4007)));
assertEquals(b.addr(0x4004), cu.getAddress()); assertEquals(b.addr(0x4004), cu.getAddress());
@ -600,7 +600,7 @@ public class DBTraceProgramViewListingTest extends AbstractGhidraHeadlessIntegra
Data d4000; Data d4000;
try (UndoableTransaction tid = b.startTransaction()) { try (UndoableTransaction tid = b.startTransaction()) {
d4000 = b.addData(0, b.addr(0x4000), Undefined4DataType.dataType, b.buf(1, 2, 3, 4)); 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)); 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)); d4004 = b.addData(0, b.addr(0x4004), Undefined4DataType.dataType, b.buf(5, 6, 7, 8));
d400a = d400a =
b.addData(0, b.addr(0x400a), Undefined4DataType.dataType, b.buf(10, 11, 12, 13)); 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))); 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))); assertUndefined(listing.getUndefinedDataAt(b.addr(0x4005)));
try (UndoableTransaction tid = b.startTransaction()) { 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(0x4005)));
assertNull(listing.getUndefinedDataAt(b.addr(0x4006))); assertNull(listing.getUndefinedDataAt(b.addr(0x4006)));
@ -783,7 +783,7 @@ public class DBTraceProgramViewListingTest extends AbstractGhidraHeadlessIntegra
assertUndefined(cu = listing.getUndefinedDataAfter(b.addr(0x4004), TaskMonitor.DUMMY)); assertUndefined(cu = listing.getUndefinedDataAfter(b.addr(0x4004), TaskMonitor.DUMMY));
assertEquals(b.addr(0x4005), cu.getAddress()); assertEquals(b.addr(0x4005), cu.getAddress());
try (UndoableTransaction tid = b.startTransaction()) { 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)); assertUndefined(cu = listing.getUndefinedDataAfter(b.addr(0x4004), TaskMonitor.DUMMY));
assertEquals(b.addr(0x4007), cu.getAddress()); assertEquals(b.addr(0x4007), cu.getAddress());
@ -805,7 +805,7 @@ public class DBTraceProgramViewListingTest extends AbstractGhidraHeadlessIntegra
assertUndefined(cu = listing.getUndefinedDataBefore(b.addr(0x4006), TaskMonitor.DUMMY)); assertUndefined(cu = listing.getUndefinedDataBefore(b.addr(0x4006), TaskMonitor.DUMMY));
assertEquals(b.addr(0x4005), cu.getAddress()); assertEquals(b.addr(0x4005), cu.getAddress());
try (UndoableTransaction tid = b.startTransaction()) { 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)); assertUndefined(cu = listing.getUndefinedDataBefore(b.addr(0x4007), TaskMonitor.DUMMY));
assertEquals(b.addr(0x4004), cu.getAddress()); assertEquals(b.addr(0x4004), cu.getAddress());
@ -816,7 +816,7 @@ public class DBTraceProgramViewListingTest extends AbstractGhidraHeadlessIntegra
UnknownInstructionException, CodeUnitInsertionException { UnknownInstructionException, CodeUnitInsertionException {
try (UndoableTransaction tid = b.startTransaction()) { try (UndoableTransaction tid = b.startTransaction()) {
b.addData(0, b.addr(0x4000), Undefined4DataType.dataType, b.buf(1, 2, 3, 4)); 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; Data cu;
@ -834,7 +834,7 @@ public class DBTraceProgramViewListingTest extends AbstractGhidraHeadlessIntegra
UnknownInstructionException, CodeUnitInsertionException, CancelledException { UnknownInstructionException, CodeUnitInsertionException, CancelledException {
try (UndoableTransaction tid = b.startTransaction()) { try (UndoableTransaction tid = b.startTransaction()) {
b.addData(0, b.addr(0x4000), Undefined4DataType.dataType, b.buf(1, 2, 3, 4)); 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? 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))); assertNull(listing.getDefinedCodeUnitAfter(b.addr(0x3fff)));
Instruction i4005; Instruction i4005;
try (UndoableTransaction tid = b.startTransaction()) { 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))); assertEquals(i4005, listing.getDefinedCodeUnitAfter(b.addr(0x3fff)));
assertNull(listing.getDefinedCodeUnitAfter(b.addr(0x4005))); assertNull(listing.getDefinedCodeUnitAfter(b.addr(0x4005)));
@ -873,7 +873,7 @@ public class DBTraceProgramViewListingTest extends AbstractGhidraHeadlessIntegra
assertNull(listing.getDefinedCodeUnitBefore(b.addr(0x4000))); assertNull(listing.getDefinedCodeUnitBefore(b.addr(0x4000)));
Instruction i4005; Instruction i4005;
try (UndoableTransaction tid = b.startTransaction()) { 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(i4005, listing.getDefinedCodeUnitBefore(b.addr(0x4006)));
assertEquals(d4000, listing.getDefinedCodeUnitBefore(b.addr(0x4005))); 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. * @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.ldefs||GHIDRA||||END|
data/languages/toy.pspec||GHIDRA||reviewed||END| data/languages/toy.pspec||GHIDRA||reviewed||END|
data/languages/toy.sinc||GHIDRA||||END| data/languages/toy.sinc||GHIDRA||||END|
data/languages/toy64-long8.cspec||GHIDRA||||END|
data/languages/toy64.cspec||GHIDRA||||END| data/languages/toy64.cspec||GHIDRA||||END|
data/languages/toy64_be.slaspec||GHIDRA||||END| data/languages/toy64_be.slaspec||GHIDRA||||END|
data/languages/toy64_be_harvard.slaspec||GHIDRA||||END| data/languages/toy64_be_harvard.slaspec||GHIDRA||||END|

View file

@ -66,6 +66,7 @@
id="Toy:BE:64:default"> id="Toy:BE:64:default">
<description>Toy (test) processor 64-bit big-endian</description> <description>Toy (test) processor 64-bit big-endian</description>
<compiler name="default" spec="toy64.cspec" id="default"/> <compiler name="default" spec="toy64.cspec" id="default"/>
<compiler name="long8" spec="toy64-long8.cspec" id="long8"/>
</language> </language>
<language processor="Toy" <language processor="Toy"
endian="big" 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="clang" spec="x86-64-win.cspec" id="clangwindows"/>
<compiler name="gcc" spec="x86-64-gcc.cspec" id="gcc"/> <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:intel"/>
<external_name tool="gnu" name="i386:x86-64"/>
<external_name tool="IDA-PRO" name="metapc"/> <external_name tool="IDA-PRO" name="metapc"/>
<external_name tool="DWARF.register.mapping.file" name="x86-64.dwarf"/> <external_name tool="DWARF.register.mapping.file" name="x86-64.dwarf"/>
</language> </language>