GP-2426: Refactor emulator to use trace access shims. Implement register mapping conventions.

This commit is contained in:
Dan 2022-09-13 16:02:02 -04:00
parent 975db1919c
commit e4f18ad824
202 changed files with 8221 additions and 4199 deletions

View file

@ -21,8 +21,7 @@ import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
import ghidra.dbg.agent.DefaultTargetObject;
import ghidra.dbg.target.TargetAggregate;
import ghidra.dbg.target.TargetObject;
import ghidra.dbg.target.*;
import ghidra.dbg.target.schema.DefaultTargetObjectSchema.DefaultAttributeSchema;
import ghidra.dbg.util.*;
import ghidra.dbg.util.CollectionUtils.Delta;
@ -906,4 +905,82 @@ public interface TargetObjectSchema {
schema.validateTypeAndInterfaces(element, parentPath, ent.getKey(), strict);
}
}
/**
* Search for a suitable register container
*
* <p>
* This will try with and without considerations for frames. If the schema indicates that
* register containers are not contained within frames, then frameLevel must be 0, otherwise
* this will return empty. If dependent on frameLevel, this will return two singleton paths: one
* for a decimal index and another for a hexadecimal index. If not, this will return a singleton
* path. If it fails to find a unique container, this will return empty.
*
* <p>
* <b>NOTE:</b> This must be used at the top of the search scope, probably the root schema. For
* example, to search the entire model for a register container related to {@code myObject}:
*
* <pre>
* for (PathPattern regPattern : myObject.getModel()
* .getSchema()
* .searchForRegisterContainer(0, myObject.getPath())) {
* TargetObject objRegs = myObject.getModel().getModelObject(regPattern.getSingletonPath());
* if (objRegs != null) {
* // found it
* }
* }
* </pre>
*
* <p>
* This places some conventional restrictions / expectations on models where registers are given
* on a frame-by-frame basis. The schema should present the {@link TargetRegisterContainer} as
* the same object or a successor to {@link TargetStackFrame}, which must in turn be a successor
* to {@link TargetThread}. The frame level (usually an index) must be in the path from thread
* to stack frame. There can be no wild cards between the frame and the register container. For
* example, the container for {@code Threads[1]} may be {@code Threads[1].Stack[n].Registers},
* where {@code n} is the frame level. {@code Threads[1]} would have the {@link TargetThread}
* interface, {@code Threads[1].Stack[0]} would have the {@link TargetStackFrame} interface, and
* {@code Threads[1].Stack[0].Registers} would have the {@link TargetRegisterContainer}
* interface. Note it is not sufficient for {@link TargetRegisterContainer} to be a successor of
* {@link TargetThread} with a single index between. There <em>must</em> be an intervening
* {@link TargetStackFrame}, and the frame level (index) must precede it.
*
* @param frameLevel the frameLevel, must be 0 if not applicable
* @path the path of the seed object relative to the root
* @return the predicates where the register container should be found, possibly empty
*/
default PathPredicates searchForRegisterContainer(int frameLevel, List<String> path) {
List<String> simple = searchForSuitable(TargetRegisterContainer.class, path);
if (simple != null) {
return frameLevel == 0 ? PathPredicates.pattern(simple) : PathPredicates.EMPTY;
}
List<String> threadPath = searchForAncestor(TargetThread.class, path);
if (threadPath == null) {
return PathPredicates.EMPTY;
}
PathPattern framePatternRelThread =
getSuccessorSchema(threadPath).searchFor(TargetStackFrame.class, false)
.getSingletonPattern();
if (framePatternRelThread == null) {
return PathPredicates.EMPTY;
}
if (framePatternRelThread.countWildcards() != 1) {
return null;
}
PathMatcher result = new PathMatcher();
for (String index : List.of(Integer.toString(frameLevel),
"0x" + Integer.toHexString(frameLevel))) {
List<String> framePathRelThread =
framePatternRelThread.applyKeys(index).getSingletonPath();
List<String> framePath = PathUtils.extend(threadPath, framePathRelThread);
List<String> regsPath =
searchForSuitable(TargetRegisterContainer.class, framePath);
if (regsPath != null) {
result.addPattern(regsPath);
}
}
return result;
}
}

View file

@ -118,6 +118,11 @@ public class PathMatcher implements PathPredicates {
return patterns.iterator().next();
}
@Override
public Collection<PathPattern> getPatterns() {
return patterns;
}
protected void coalesceWilds(Set<String> result) {
if (result.contains("")) {
result.removeIf(PathUtils::isName);
@ -179,10 +184,10 @@ public class PathMatcher implements PathPredicates {
}
@Override
public PathMatcher applyKeys(List<String> indices) {
public PathMatcher applyKeys(Align align, List<String> indices) {
PathMatcher result = new PathMatcher();
for (PathPattern pat : patterns) {
result.addPattern(pat.applyKeys(indices));
result.addPattern(pat.applyKeys(align, indices));
}
return result;
}

View file

@ -199,6 +199,11 @@ public class PathPattern implements PathPredicates {
return this;
}
@Override
public Collection<PathPattern> getPatterns() {
return List.of(this);
}
@Override
public Set<String> getNextKeys(List<String> path) {
if (path.size() >= pattern.size()) {
@ -257,22 +262,26 @@ public class PathPattern implements PathPredicates {
}
@Override
public PathPattern applyKeys(List<String> indices) {
List<String> result = new ArrayList<>(pattern.size());
Iterator<String> it = indices.iterator();
for (String pat : pattern) {
if (it.hasNext() && isWildcard(pat)) {
String index = it.next();
public PathPattern applyKeys(Align align, List<String> indices) {
List<String> result = Arrays.asList(new String[pattern.size()]);
ListIterator<String> iit = align.iterator(indices);
ListIterator<String> pit = align.iterator(pattern);
while (pit.hasNext()) {
int i = pit.nextIndex();
String pat = pit.next();
if (iit.hasNext() && isWildcard(pat)) {
String index = iit.next();
if (PathUtils.isIndex(pat)) {
result.add(PathUtils.makeKey(index));
result.set(i, PathUtils.makeKey(index));
}
else {
// NB. Rare for attribute wildcards, but just in case
result.add(index);
result.set(i, index);
}
}
else {
result.add(pat);
result.set(i, pat);
}
}
return new PathPattern(result);

View file

@ -17,13 +17,108 @@ package ghidra.dbg.util;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.stream.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import ghidra.async.AsyncFence;
import ghidra.dbg.target.TargetObject;
import ghidra.dbg.util.PathUtils.PathComparator;
import ghidra.util.ReversedListIterator;
public interface PathPredicates {
PathPredicates EMPTY = new PathPredicates() {
@Override
public PathPredicates or(PathPredicates that) {
return that;
}
@Override
public boolean matches(List<String> path) {
return false;
}
@Override
public boolean successorCouldMatch(List<String> path, boolean strict) {
return false;
}
@Override
public boolean ancestorMatches(List<String> path, boolean strict) {
return false;
}
@Override
public boolean ancestorCouldMatchRight(List<String> path, boolean strict) {
return false;
}
@Override
public Set<String> getNextKeys(List<String> path) {
return Set.of();
}
@Override
public Set<String> getNextNames(List<String> path) {
return Set.of();
}
@Override
public Set<String> getNextIndices(List<String> path) {
return Set.of();
}
@Override
public Set<String> getPrevKeys(List<String> path) {
return Set.of();
}
@Override
public List<String> getSingletonPath() {
return null;
}
@Override
public PathPattern getSingletonPattern() {
return null;
}
@Override
public Collection<PathPattern> getPatterns() {
return List.of();
}
@Override
public PathPredicates removeRight(int count) {
return this;
}
@Override
public PathPredicates applyKeys(Align align, List<String> keys) {
return this;
}
@Override
public boolean isEmpty() {
return true;
}
};
enum Align {
LEFT {
@Override
<T> ListIterator<T> iterator(List<T> list) {
return list.listIterator();
}
},
RIGHT {
@Override
<T> ListIterator<T> iterator(List<T> list) {
return new ReversedListIterator<>(list.listIterator(list.size()));
}
};
abstract <T> ListIterator<T> iterator(List<T> list);
}
static boolean keyMatches(String pat, String key) {
if (key.equals(pat)) {
@ -163,6 +258,13 @@ public interface PathPredicates {
*/
PathPattern getSingletonPattern();
/**
* Get the patterns of this predicate
*
* @return the patterns
*/
Collection<PathPattern> getPatterns();
/**
* Remove count elements from the right
*
@ -313,33 +415,44 @@ public interface PathPredicates {
* Substitute wildcards from left to right for the given list of keys
*
* <p>
* Takes each pattern and substitutes its wildcards for the given indices, starting from the
* left and working right. This object is unmodified, and the result is returned.
* Takes each pattern and substitutes its wildcards for the given indices, according to the
* given alignment. This object is unmodified, and the result is returned.
*
* <p>
* If there are fewer wildcards in a pattern than given, only the left-most keys are taken. If
* there are fewer keys than wildcards in a pattern, then the right-most wildcards are left in
* the resulting pattern.
* If there are fewer wildcards in a pattern than given, only the first keys are taken. If there
* are fewer keys than wildcards in a pattern, then the remaining wildcards are left in the
* resulting pattern. In this manner, the left-most wildcards are substituted for the left-most
* indices, or the right-most wildcards are substituted for the right-most indices, depending on
* the alignment.
*
* @param align the end to align
* @param keys the keys to substitute
* @return the pattern or matcher with the applied substitutions
*/
PathPredicates applyKeys(List<String> keys);
PathPredicates applyKeys(Align align, List<String> keys);
default PathPredicates applyKeys(Stream<String> keys) {
return applyKeys(keys.collect(Collectors.toList()));
default PathPredicates applyKeys(Align align, String... keys) {
return applyKeys(align, List.of(keys));
}
default PathPredicates applyKeys(String... keys) {
return applyKeys(List.of(keys));
return applyKeys(Align.LEFT, keys);
}
default PathPredicates applyIntKeys(int radix, List<Integer> keys) {
return applyKeys(keys.stream().map(k -> Integer.toString(k, radix)));
default PathPredicates applyIntKeys(int radix, Align align, List<Integer> keys) {
return applyKeys(align,
keys.stream().map(k -> Integer.toString(k, radix)).collect(Collectors.toList()));
}
default PathPredicates applyIntKeys(int radix, Align align, int... keys) {
return applyKeys(align,
IntStream.of(keys)
.mapToObj(k -> Integer.toString(k, radix))
.collect(Collectors.toList()));
}
default PathPredicates applyIntKeys(int... keys) {
return applyKeys(IntStream.of(keys).mapToObj(k -> Integer.toString(k)));
return applyIntKeys(10, Align.LEFT, keys);
}
/**