mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-05 10:49:34 +02:00
GP-1595: Add global control actions for Target, Trace, and Emulator
This commit is contained in:
parent
81d880f3ab
commit
034730b785
66 changed files with 3335 additions and 763 deletions
|
@ -22,8 +22,8 @@ import java.util.stream.Stream;
|
|||
import com.google.common.collect.Range;
|
||||
import com.google.common.collect.RangeSet;
|
||||
|
||||
import ghidra.dbg.target.TargetMethod;
|
||||
import ghidra.dbg.target.TargetObject;
|
||||
import ghidra.dbg.target.*;
|
||||
import ghidra.dbg.target.TargetExecutionStateful.TargetExecutionState;
|
||||
import ghidra.dbg.target.schema.TargetObjectSchema;
|
||||
import ghidra.dbg.util.PathPattern;
|
||||
import ghidra.dbg.util.PathPredicates;
|
||||
|
@ -570,4 +570,29 @@ public interface TraceObject extends TraceUniqueObject {
|
|||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the execution state, if applicable, of this object
|
||||
*
|
||||
* <p>
|
||||
* This searches for the conventional stateful object defining this object's execution state. If
|
||||
* such an object does not exist, null is returned. If one does exist, then its execution state
|
||||
* at the given snap is returned. If that state is null, it is assumed
|
||||
* {@link TargetExecutionState#INACTIVE}.
|
||||
*
|
||||
* @param snap the snap
|
||||
* @return the state or null
|
||||
*/
|
||||
default TargetExecutionState getExecutionState(long snap) {
|
||||
TraceObject stateful = querySuitableTargetInterface(TargetExecutionStateful.class);
|
||||
if (stateful == null) {
|
||||
return null;
|
||||
}
|
||||
TraceObjectValue stateVal =
|
||||
stateful.getAttribute(snap, TargetExecutionStateful.STATE_ATTRIBUTE_NAME);
|
||||
if (stateVal == null) {
|
||||
return TargetExecutionState.INACTIVE;
|
||||
}
|
||||
return TargetExecutionState.valueOf((String) stateVal.getValue());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -312,7 +312,7 @@ public class PatchStep implements Step {
|
|||
}
|
||||
|
||||
@Override
|
||||
public <T> void execute(PcodeThread<T> emuThread, Stepper<T> stepper, TaskMonitor monitor)
|
||||
public <T> void execute(PcodeThread<T> emuThread, Stepper stepper, TaskMonitor monitor)
|
||||
throws CancelledException {
|
||||
PcodeProgram prog = emuThread.getMachine().compileSleigh("schedule", sleigh + ";");
|
||||
emuThread.getExecutor().execute(prog, emuThread.getUseropLibrary());
|
||||
|
|
|
@ -0,0 +1,151 @@
|
|||
/* ###
|
||||
* 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.time.schedule;
|
||||
|
||||
import ghidra.pcode.emu.PcodeMachine;
|
||||
import ghidra.pcode.emu.PcodeThread;
|
||||
import ghidra.pcode.exec.*;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.trace.model.thread.TraceThreadManager;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
/**
|
||||
* A generator of an emulator's thread schedule
|
||||
*/
|
||||
public interface Scheduler {
|
||||
|
||||
/**
|
||||
* Create a scheduler that allocates all slices to a single thread
|
||||
*
|
||||
* @param thread the thread to schedule
|
||||
* @return the scheduler
|
||||
*/
|
||||
static Scheduler oneThread(TraceThread thread) {
|
||||
long key = thread == null ? -1 : thread.getKey();
|
||||
return new Scheduler() {
|
||||
@Override
|
||||
public TickStep nextSlice(Trace trace) {
|
||||
return new TickStep(key, 1000);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
interface RunResult {
|
||||
/**
|
||||
* Get the actual schedule executed
|
||||
*
|
||||
* <p>
|
||||
* It is possible for the machine to be interrupted mid-instruction. If this is the case,
|
||||
* the trace schedule will indicate the p-code steps taken.
|
||||
*
|
||||
* @return the schedule
|
||||
*/
|
||||
public TraceSchedule schedule();
|
||||
|
||||
/**
|
||||
* Get the error that interrupted execution
|
||||
*
|
||||
* <p>
|
||||
* Ideally, this is a {@link InterruptPcodeExecutionException}, indicating a breakpoint
|
||||
* trapped the emulator, but it could be a number of things:
|
||||
*
|
||||
* <ul>
|
||||
* <li>An instruction decode error</li>
|
||||
* <li>An unimplemented instruction</li>
|
||||
* <li>An unimplemented p-code userop</li>
|
||||
* <li>An error accessing the machine state</li>
|
||||
* <li>A runtime error in the implementation of a p-code userop</li>
|
||||
* <li>A runtime error in the implementation of the emulator, in which case, a bug should be
|
||||
* filed</li>
|
||||
* </ul>
|
||||
*
|
||||
* @return the error
|
||||
*/
|
||||
public Throwable error();
|
||||
}
|
||||
|
||||
/**
|
||||
* The result of running a machine
|
||||
*/
|
||||
record RecordRunResult(TraceSchedule schedule, Throwable error) implements RunResult {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the next step to schedule
|
||||
*
|
||||
* @return the (instruction-level) thread and tick count
|
||||
*/
|
||||
TickStep nextSlice(Trace trace);
|
||||
|
||||
/**
|
||||
* Run a machine according to the given schedule until it is interrupted
|
||||
*
|
||||
* <p>
|
||||
* This method will drop p-code steps from injections, including those from execution
|
||||
* breakpoints. The goal is to ensure that the returned schedule can be used to recover the same
|
||||
* state on a machine without injections. Unfortunately, injections which modify the machine
|
||||
* state, other than unique variables, will defeat that goal.
|
||||
*
|
||||
* @param trace the trace whose threads to schedule
|
||||
* @param eventThread the first thread to schedule if the scheduler doesn't specify
|
||||
* @param machine the machine to run
|
||||
* @param monitor a monitor for cancellation
|
||||
* @return the result of execution
|
||||
*/
|
||||
default RunResult run(Trace trace, TraceThread eventThread, PcodeMachine<?> machine,
|
||||
TaskMonitor monitor) {
|
||||
TraceThreadManager tm = trace.getThreadManager();
|
||||
TraceSchedule completedSteps = TraceSchedule.snap(0);
|
||||
PcodeThread<?> emuThread = null;
|
||||
int completedTicks = 0;
|
||||
try {
|
||||
while (true) {
|
||||
TickStep slice = nextSlice(trace);
|
||||
eventThread = slice.getThread(tm, eventThread);
|
||||
emuThread = machine.getThread(eventThread.getPath(), true);
|
||||
for (int i = 0; i < slice.tickCount; i++) {
|
||||
monitor.checkCanceled();
|
||||
emuThread.stepInstruction();
|
||||
completedTicks++;
|
||||
}
|
||||
completedSteps = completedSteps.steppedForward(eventThread, completedTicks);
|
||||
completedTicks = 0;
|
||||
}
|
||||
}
|
||||
catch (PcodeExecutionException e) {
|
||||
completedSteps = completedSteps.steppedForward(eventThread, completedTicks);
|
||||
if (emuThread.getInstruction() == null) {
|
||||
return new RecordRunResult(completedSteps, e);
|
||||
}
|
||||
PcodeFrame frame = e.getFrame();
|
||||
// Rewind one so stepping retries the op causing the error
|
||||
int count = frame.count() - 1;
|
||||
if (frame == null || count == 0) {
|
||||
// If we've decoded, but could execute the first op, just drop the p-code steps
|
||||
return new RecordRunResult(completedSteps, e);
|
||||
}
|
||||
// The +1 accounts for the decode step
|
||||
return new RecordRunResult(
|
||||
completedSteps.steppedPcodeForward(eventThread, count + 1), e);
|
||||
}
|
||||
catch (CancelledException e) {
|
||||
return new RecordRunResult(
|
||||
completedSteps.steppedForward(eventThread, completedTicks), e);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -384,8 +384,8 @@ public class Sequence implements Comparable<Sequence> {
|
|||
* @return the last trace thread stepped during execution
|
||||
* @throws CancelledException if execution is cancelled
|
||||
*/
|
||||
public <T> TraceThread execute(Trace trace, TraceThread eventThread, PcodeMachine<T> machine,
|
||||
Stepper<T> stepper, TaskMonitor monitor) throws CancelledException {
|
||||
public TraceThread execute(Trace trace, TraceThread eventThread, PcodeMachine<?> machine,
|
||||
Stepper stepper, TaskMonitor monitor) throws CancelledException {
|
||||
TraceThreadManager tm = trace.getThreadManager();
|
||||
TraceThread thread = eventThread;
|
||||
for (Step step : steps) {
|
||||
|
|
|
@ -66,7 +66,7 @@ public class SkipStep extends AbstractStep {
|
|||
}
|
||||
|
||||
@Override
|
||||
public <T> void execute(PcodeThread<T> emuThread, Stepper<T> stepper, TaskMonitor monitor)
|
||||
public <T> void execute(PcodeThread<T> emuThread, Stepper stepper, TaskMonitor monitor)
|
||||
throws CancelledException {
|
||||
for (int i = 0; i < tickCount; i++) {
|
||||
monitor.incrementProgress(1);
|
||||
|
|
|
@ -170,20 +170,20 @@ public interface Step extends Comparable<Step> {
|
|||
return compareStep(that).compareTo;
|
||||
}
|
||||
|
||||
default <T> TraceThread execute(TraceThreadManager tm, TraceThread eventThread,
|
||||
PcodeMachine<T> machine, Stepper<T> stepper, TaskMonitor monitor)
|
||||
default TraceThread execute(TraceThreadManager tm, TraceThread eventThread,
|
||||
PcodeMachine<?> machine, Stepper stepper, TaskMonitor monitor)
|
||||
throws CancelledException {
|
||||
TraceThread thread = getThread(tm, eventThread);
|
||||
if (machine == null) {
|
||||
// Just performing validation (specifically thread parts)
|
||||
return thread;
|
||||
}
|
||||
PcodeThread<T> emuThread = machine.getThread(thread.getPath(), true);
|
||||
PcodeThread<?> emuThread = machine.getThread(thread.getPath(), true);
|
||||
execute(emuThread, stepper, monitor);
|
||||
return thread;
|
||||
}
|
||||
|
||||
<T> void execute(PcodeThread<T> emuThread, Stepper<T> stepper, TaskMonitor monitor)
|
||||
<T> void execute(PcodeThread<T> emuThread, Stepper stepper, TaskMonitor monitor)
|
||||
throws CancelledException;
|
||||
|
||||
long coalescePatches(Language language, List<Step> steps);
|
||||
|
|
|
@ -17,44 +17,41 @@ package ghidra.trace.model.time.schedule;
|
|||
|
||||
import ghidra.pcode.emu.PcodeThread;
|
||||
|
||||
public interface Stepper<T> {
|
||||
@SuppressWarnings("rawtypes")
|
||||
public interface Stepper {
|
||||
enum Enum implements Stepper {
|
||||
INSTRUCTION {
|
||||
@Override
|
||||
public void tick(PcodeThread thread) {
|
||||
public void tick(PcodeThread<?> thread) {
|
||||
thread.stepInstruction();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void skip(PcodeThread thread) {
|
||||
public void skip(PcodeThread<?> thread) {
|
||||
thread.skipInstruction();
|
||||
}
|
||||
},
|
||||
PCODE {
|
||||
@Override
|
||||
public void tick(PcodeThread thread) {
|
||||
public void tick(PcodeThread<?> thread) {
|
||||
thread.stepPcodeOp();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void skip(PcodeThread thread) {
|
||||
public void skip(PcodeThread<?> thread) {
|
||||
thread.skipPcodeOp();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
static <T> Stepper<T> instruction() {
|
||||
static Stepper instruction() {
|
||||
return Enum.INSTRUCTION;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
static <T> Stepper<T> pcode() {
|
||||
static Stepper pcode() {
|
||||
return Enum.PCODE;
|
||||
}
|
||||
|
||||
void tick(PcodeThread<T> thread);
|
||||
void tick(PcodeThread<?> thread);
|
||||
|
||||
void skip(PcodeThread<T> thread);
|
||||
void skip(PcodeThread<?> thread);
|
||||
}
|
||||
|
|
|
@ -66,7 +66,7 @@ public class TickStep extends AbstractStep {
|
|||
}
|
||||
|
||||
@Override
|
||||
public <T> void execute(PcodeThread<T> emuThread, Stepper<T> stepper, TaskMonitor monitor)
|
||||
public <T> void execute(PcodeThread<T> emuThread, Stepper stepper, TaskMonitor monitor)
|
||||
throws CancelledException {
|
||||
for (int i = 0; i < tickCount; i++) {
|
||||
monitor.incrementProgress(1);
|
||||
|
|
|
@ -561,6 +561,32 @@ public class TraceSchedule implements Comparable<TraceSchedule> {
|
|||
return new TraceSchedule(snap, ticks, new Sequence());
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the schedule resulting from this schedule advanced by the given schedule
|
||||
*
|
||||
* <p>
|
||||
* This operation cannot be used to append instruction steps after p-code steps. Thus, if this
|
||||
* schedule contains any p-code steps and {@code} next has instruction steps, an error will be
|
||||
*
|
||||
* @param next the schedule to append. Its snap is ignored.
|
||||
* @return the complete schedule
|
||||
* @throws IllegalArgumentException if the result would have instruction steps following p-code
|
||||
* steps
|
||||
*/
|
||||
public TraceSchedule advanced(TraceSchedule next) {
|
||||
if (this.pSteps.isNop()) {
|
||||
Sequence ticks = this.steps.clone();
|
||||
ticks.advance(next.steps);
|
||||
return new TraceSchedule(this.snap, ticks, next.pSteps.clone());
|
||||
}
|
||||
else if (next.steps.isNop()) {
|
||||
Sequence pTicks = this.steps.clone();
|
||||
pTicks.advance(next.pSteps);
|
||||
return new TraceSchedule(this.snap, this.steps.clone(), pTicks);
|
||||
}
|
||||
throw new IllegalArgumentException("Cannot have instructions steps following p-code steps");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the threads involved in the schedule
|
||||
*
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue