mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-05 19:42:36 +02:00
GP-2426: Refactor emulator to use trace access shims. Implement register mapping conventions.
This commit is contained in:
parent
975db1919c
commit
e4f18ad824
202 changed files with 8221 additions and 4199 deletions
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue