Skip to content

Commit

Permalink
GP-4374: 'Pure' Emulation uses Object-based trace.
Browse files Browse the repository at this point in the history
  • Loading branch information
nsadeveloper789 committed Mar 1, 2024
1 parent 63e64d5 commit 55b0720
Show file tree
Hide file tree
Showing 21 changed files with 463 additions and 180 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
*
* <p>
* In addition to the trace "coordinates" encapsulated by {@link PcodeTraceAccess}, this
* encapsulates the tool controlling a session and the session's trace recorder. This permits p-code
* encapsulates the tool controlling a session and the session's target. This permits p-code
* executor/emulator states to access target data and to access session data, e.g., data from mapped
* static images. It supports the same method chain pattern as {@link PcodeTraceAccess}, but
* starting with {@link DefaultPcodeDebuggerAccess}.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -310,8 +310,14 @@ private static TraceObjectKeyPath resolvePath(TraceThread thread, Integer frameL
if (frameLevel == null) {
return objThread.getCanonicalPath();
}
TraceStack stack =
thread.getTrace().getStackManager().getStack(thread, time.getSnap(), false);
TraceStack stack;
try {
stack = thread.getTrace().getStackManager().getStack(thread, time.getSnap(), false);
}
catch (IllegalStateException e) {
// Schema does not specify a stack
return objThread.getCanonicalPath();
}
if (stack == null) {
return objThread.getCanonicalPath();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@
*/
package ghidra.app.plugin.core.debug.disassemble;

import ghidra.app.plugin.core.debug.disassemble.DisassemblyInjectInfo.CompilerInfo;
import ghidra.app.plugin.core.debug.disassemble.DisassemblyInjectInfo.PlatformInfo;
import ghidra.debug.api.platform.DebuggerPlatformMapper;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.lang.Language;
import ghidra.trace.model.Trace;
import ghidra.trace.model.guest.TracePlatform;
import ghidra.trace.model.thread.TraceThread;
import ghidra.util.Msg;
import ghidra.util.classfinder.ExtensionPoint;
Expand All @@ -38,7 +38,7 @@
* configure the disassembler (namely seeding its context), the one invoked last will have "the last
* word." As such, each inject should avoid unnecessarily erasing existing context.
*/
@DisassemblyInjectInfo(compilers = {}) // Use as default
@DisassemblyInjectInfo(platforms = {}) // Use as default
public interface DisassemblyInject extends ExtensionPoint {
/**
* If present, get the information annotation on this inject
Expand All @@ -56,16 +56,16 @@ default DisassemblyInjectInfo getInfo() {
}

/**
* Check if this inject applies to the given trace
* Check if this inject applies to the given trace platform
*
* @param trace the trace to check
* @param platform the platform to check
* @return true if applicable, false otherwise
*/
default boolean isApplicable(Trace trace) {
for (CompilerInfo info : getInfo().compilers()) {
if (info.langID().equals(trace.getBaseLanguage().getLanguageID().toString())) {
default boolean isApplicable(TracePlatform platform) {
for (PlatformInfo info : getInfo().platforms()) {
if (info.langID().equals(platform.getLanguage().getLanguageID().toString())) {
if (info.compilerID().isBlank() || info.compilerID()
.equals(trace.getBaseCompilerSpec().getCompilerSpecID().toString())) {
.equals(platform.getCompilerSpec().getCompilerSpecID().toString())) {
return true;
}
}
Expand All @@ -86,36 +86,35 @@ default int getPriority() {
* A pre-auto disassembly hook
*
* <p>
* This hook is invoked by the {@link DisassembleAtPcDebuggerBot} before disassembly actually
* This hook is invoked by the {@link DebuggerPlatformMapper} before disassembly actually
* begins. The callback occurs within the command's background thread. In general, the inject
* should limit its operation to inspecting the trace database and configuring the command.
*
* @param tool the tool that will execute the command
* @param command the command to be configured, which is about to execute
* @param trace the trace whose bytes to disassemble
* @param language the language for the disassembler
* @param platform the trace platform for the disassembler
* @param snap the snap the snap at which to disassemble
* @param thread the thread whose PC is being disassembled
* @param startSet the starting address set, usually just the PC
* @param restricted the set of disassemblable addresses
*/
default void pre(PluginTool tool, TraceDisassembleCommand command, Trace trace,
Language language, long snap, TraceThread thread, AddressSetView startSet,
AddressSetView restricted) {
default void pre(PluginTool tool, TraceDisassembleCommand command, TracePlatform platform,
long snap, TraceThread thread, AddressSetView startSet, AddressSetView restricted) {
}

/**
* A post-auto disassembly hook
*
* <p>
* This hook is invoked by the {@link DisassembleAtPcDebuggerBot} after disassembly completes.
* The callback occurs within the command's background thread.
* This hook is invoked by the {@link DebuggerPlatformMapper} after disassembly completes. The
* callback occurs within the command's background thread.
*
* @param tool the tool that just executed the disassembly command
* @param trace the trace whose bytes were disassembled
* @param platform the trace platform for the disassembler
* @param snap the snap the snap at which disassembly was performed
* @param disassembled the addresses that were actually disassembled
*/
default void post(PluginTool tool, Trace trace, long snap, AddressSetView disassembled) {
default void post(PluginTool tool, TracePlatform platform, long snap,
AddressSetView disassembled) {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,18 @@

import java.lang.annotation.*;

import ghidra.trace.model.guest.TracePlatform;

/**
* Information about the applicability of a disassembly inject
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface DisassemblyInjectInfo {
/**
* A language-compiler-ID pair
* A language-compiler-ID pair identifying a trace platform
*/
public @interface CompilerInfo {
public @interface PlatformInfo {
/**
* The language ID, e.g., "x86:64:LE:default"
*
Expand All @@ -46,12 +48,12 @@
}

/**
* A list of language-compiler-ID pairs for which this inject applies
* A list of platforms for which this inject applies
*
* @see DisassemblyInject#isApplicable(ghidra.trace.model.Trace)
* @return the language-compiler-ID pairs
* @see DisassemblyInject#isApplicable(TracePlatform)
* @return the platforms
*/
CompilerInfo[] compilers();
PlatformInfo[] platforms();

/**
* The "position" of this inject's invocation
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@
public class LoadEmulatorAutoReadMemorySpec implements AutoReadMemorySpec {
public static final String CONFIG_NAME = "2_LOAD_EMULATOR";

@Override
public boolean equals(Object obj) {
return this.getClass() == obj.getClass();
}

@Override
public String getConfigName() {
return CONFIG_NAME;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@
public class NoneAutoReadMemorySpec implements AutoReadMemorySpec {
public static final String CONFIG_NAME = "0_READ_NONE";

@Override
public boolean equals(Object obj) {
return this.getClass() == obj.getClass();
}

@Override
public String getConfigName() {
return CONFIG_NAME;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@
public class VisibleAutoReadMemorySpec implements AutoReadMemorySpec {
public static final String CONFIG_NAME = "1_READ_VISIBLE";

@Override
public boolean equals(Object obj) {
return this.getClass() == obj.getClass();
}

@Override
public String getConfigName() {
return CONFIG_NAME;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@
public class VisibleROOnceAutoReadMemorySpec implements AutoReadMemorySpec {
public static final String CONFIG_NAME = "1_READ_VIS_RO_ONCE";

@Override
public boolean equals(Object obj) {
return this.getClass() == obj.getClass();
}

@Override
public String getConfigName() {
return CONFIG_NAME;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
package ghidra.app.plugin.core.debug.mapping;

import java.util.Collection;
import java.util.Set;
import java.util.Comparator;
import java.util.stream.Collectors;

import ghidra.app.plugin.core.debug.disassemble.DisassemblyInject;
import ghidra.app.plugin.core.debug.disassemble.TraceDisassembleCommand;
Expand All @@ -25,11 +26,11 @@
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.address.*;
import ghidra.program.model.lang.Endian;
import ghidra.program.model.lang.Language;
import ghidra.trace.model.Trace;
import ghidra.trace.model.guest.TracePlatform;
import ghidra.trace.model.target.TraceObject;
import ghidra.trace.model.thread.TraceThread;
import ghidra.util.classfinder.ClassSearcher;
import ghidra.util.task.TaskMonitor;

public abstract class AbstractDebuggerPlatformMapper implements DebuggerPlatformMapper {
Expand Down Expand Up @@ -63,8 +64,12 @@ protected boolean isCancelSilently(Address start, long snap) {
return trace.getCodeManager().instructions().getAt(snap, start) != null;
}

protected Collection<DisassemblyInject> getDisassemblyInjections(TraceObject object) {
return Set.of();
protected Collection<DisassemblyInject> getDisassemblyInjections(TracePlatform platform) {
return ClassSearcher.getInstances(DisassemblyInject.class)
.stream()
.filter(i -> i.isApplicable(platform))
.sorted(Comparator.comparing(i -> i.getPriority()))
.collect(Collectors.toList());
}

@Override
Expand All @@ -75,20 +80,19 @@ public DisassemblyResult disassemble(TraceThread thread, TraceObject object,
}
TracePlatform platform = trace.getPlatformManager().getPlatform(getCompilerSpec(object));

Collection<DisassemblyInject> injects = getDisassemblyInjections(object);
Collection<DisassemblyInject> injects = getDisassemblyInjections(platform);
TraceDisassembleCommand dis = new TraceDisassembleCommand(platform, start, restricted);
Language language = platform.getLanguage();
AddressSet startSet = new AddressSet(start);
for (DisassemblyInject i : injects) {
i.pre(tool, dis, trace, language, snap, thread, startSet, restricted);
i.pre(tool, dis, platform, snap, thread, startSet, restricted);
}
boolean result = dis.applyToTyped(trace.getFixedProgramView(snap), monitor);
if (!result) {
return DisassemblyResult.failed(dis.getStatusMsg());
}
AddressSetView actualSet = dis.getDisassembledAddressSet();
for (DisassemblyInject i : injects) {
i.post(tool, trace, snap, actualSet);
i.post(tool, platform, snap, actualSet);
}
return DisassemblyResult.success(actualSet != null && !actualSet.isEmpty());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,30 +13,25 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.plugin.core.debug.mapping.legacy;
package ghidra.app.plugin.core.debug.mapping;

import java.util.*;
import java.util.stream.Collectors;
import java.util.Set;

import ghidra.app.plugin.core.debug.disassemble.DisassemblyInject;
import ghidra.app.plugin.core.debug.mapping.*;
import ghidra.debug.api.platform.DebuggerPlatformMapper;
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.
* An opinion which just uses the trace's "host" platform, i.e., because the target created the
* trace with the correct host language. Other mappers assume the trace language is DATA, and that
* the real language must be mapped as a guest platform.
*/
@Transitional
public class LegacyDebuggerPlatformOpinion implements DebuggerPlatformOpinion {
public class HostDebuggerPlatformOpinion implements DebuggerPlatformOpinion {

protected static class LegacyDebuggerPlatformMapper extends AbstractDebuggerPlatformMapper {
public LegacyDebuggerPlatformMapper(PluginTool tool, Trace trace) {
protected static class HostDebuggerPlatformMapper extends AbstractDebuggerPlatformMapper {
public HostDebuggerPlatformMapper(PluginTool tool, Trace trace) {
super(tool, trace);
}

Expand All @@ -54,23 +49,13 @@ public void addToTrace(long snap) {
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 {
HOST {
@Override
public String getDescription() {
return "Legacy (Already mapped by recorder)";
return "Host/base (Language already chosen by target)";
}

@Override
Expand All @@ -85,22 +70,19 @@ public CompilerSpec getCompilerSpec() {

@Override
public DebuggerPlatformMapper take(PluginTool tool, Trace trace) {
return new LegacyDebuggerPlatformMapper(tool, trace);
return new HostDebuggerPlatformMapper(tool, trace);
}

@Override
public boolean isCreatorOf(DebuggerPlatformMapper mapper) {
return mapper.getClass() == LegacyDebuggerPlatformMapper.class;
return mapper.getClass() == HostDebuggerPlatformMapper.class;
}
};
}

@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);
return Set.of(Offers.HOST);
}
}
Loading

0 comments on commit 55b0720

Please sign in to comment.