GP-5213: Fix editing registers presented as primitives.

This commit is contained in:
Dan 2025-01-03 12:55:06 -05:00
parent 18ef5ceea3
commit e0ccc4883b
26 changed files with 384 additions and 287 deletions

View file

@ -28,7 +28,7 @@ import ghidra.trace.model.breakpoint.*;
import ghidra.trace.model.target.TraceObject;
import ghidra.trace.model.target.iface.TraceObjectTogglable;
import ghidra.trace.model.target.info.TraceObjectInterfaceUtils;
import ghidra.trace.model.target.path.PathMatcher;
import ghidra.trace.model.target.path.PathFilter;
import ghidra.trace.model.target.schema.TraceObjectSchema;
import ghidra.trace.model.thread.*;
import ghidra.trace.util.*;
@ -301,8 +301,8 @@ public class DBTraceObjectBreakpointLocation
return threads;
}
PathMatcher procMatcher = schema.searchFor(TraceObjectProcess.class, false);
return object.getAncestorsRoot(getLifespan(), procMatcher)
PathFilter procFilter = schema.searchFor(TraceObjectProcess.class, false);
return object.getAncestorsRoot(getLifespan(), procFilter)
.flatMap(proc -> proc.getSource(object)
.querySuccessorsInterface(getLifespan(),
TraceObjectThread.class, true))

View file

@ -15,12 +15,10 @@
*/
package ghidra.trace.database.guest;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.util.List;
import java.util.stream.Stream;
import ghidra.pcode.utils.Utils;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.lang.Register;
@ -31,8 +29,8 @@ import ghidra.trace.model.memory.*;
import ghidra.trace.model.symbol.*;
import ghidra.trace.model.target.*;
import ghidra.trace.model.target.path.KeyPath;
import ghidra.trace.model.target.path.PathFilter;
import ghidra.trace.model.target.path.PathFilter.Align;
import ghidra.trace.model.target.path.PathMatcher;
import ghidra.trace.model.target.schema.TraceObjectSchema;
import ghidra.trace.util.TraceChangeRecord;
import ghidra.trace.util.TraceEvents;
@ -51,108 +49,6 @@ public enum DBTraceObjectRegisterSupport {
}
};
static class RegisterValueException extends Exception {
public RegisterValueException(String message) {
super(message);
}
}
static class LazyValues {
private final TraceObjectValue registerValue;
private BigInteger value;
private int bitLength = -1;
private byte[] be;
private byte[] le;
public LazyValues(TraceObjectValue registerValue) {
this.registerValue = registerValue;
}
BigInteger convertRegisterValueToBigInteger() throws RegisterValueException {
Object val = registerValue.getValue();
if (val instanceof String s) {
try {
return new BigInteger(s, 16);
}
catch (NumberFormatException e) {
throw new RegisterValueException(
"Invalid register value " + s + ". Must be hex digits only.");
}
}
else if (val instanceof byte[] arr) {
// NOTE: Reg object values are always big endian
return new BigInteger(1, arr);
}
else if (val instanceof Byte b) {
return BigInteger.valueOf(b);
}
else if (val instanceof Short s) {
return BigInteger.valueOf(s);
}
else if (val instanceof Integer i) {
return BigInteger.valueOf(i);
}
else if (val instanceof Long l) {
return BigInteger.valueOf(l);
}
else if (val instanceof Address a) {
return a.getOffsetAsBigInteger();
}
throw new RegisterValueException(
"Cannot convert register value: (" + registerValue.getValue().getClass() + ") '" +
registerValue.getValue() +
"'");
}
int getRegisterValueBitLength() throws RegisterValueException {
Object objBitLength = registerValue.getParent()
.getValue(registerValue.getMinSnap(), TraceObjectRegister.KEY_BITLENGTH)
.getValue();
if (!(objBitLength instanceof Number)) {
throw new RegisterValueException(
"Register length is not numeric: (" + objBitLength.getClass() + ") '" +
objBitLength + "'");
}
return ((Number) objBitLength).intValue();
}
BigInteger getValue() throws RegisterValueException {
if (value != null) {
return value;
}
return value = convertRegisterValueToBigInteger();
}
int getBitLength() throws RegisterValueException {
if (bitLength != -1) {
return bitLength;
}
return bitLength = getRegisterValueBitLength();
}
int getByteLength() throws RegisterValueException {
return (getBitLength() + 7) / 8;
}
byte[] getBytesBigEndian() throws RegisterValueException {
if (be != null) {
return be;
}
return be = Utils.bigIntegerToBytes(getValue(), getByteLength(), true);
}
byte[] getBytesLittleEndian() throws RegisterValueException {
if (le != null) {
return le;
}
return le = Utils.bigIntegerToBytes(getValue(), getByteLength(), false);
}
public byte[] getBytes(boolean isBigEndian) throws RegisterValueException {
return isBigEndian ? getBytesBigEndian() : getBytesLittleEndian();
}
}
protected AddressSpace findRegisterOverlay(TraceObject object) {
TraceObject container = object
.findCanonicalAncestorsInterface(TraceObjectRegisterContainer.class)
@ -173,7 +69,8 @@ public enum DBTraceObjectRegisterSupport {
}
protected void onValueCreatedTransferToPlatformRegister(TraceObjectValue registerValue,
TracePlatform platform, String name, LazyValues lazy) throws RegisterValueException {
TracePlatform platform, String name, RegisterValueConverter rvc)
throws RegisterValueException {
Register register = platform.getLanguage().getRegister(name);
if (register == null) {
return;
@ -188,7 +85,7 @@ public enum DBTraceObjectRegisterSupport {
long minSnap = registerValue.getMinSnap();
if (hostSpace.isMemorySpace()) {
mem.getMemorySpace(hostSpace, true)
.setValue(platform, minSnap, new RegisterValue(register, lazy.getValue()));
.setValue(platform, minSnap, new RegisterValue(register, rvc.getValue()));
}
else if (hostSpace.isRegisterSpace()) {
AddressSpace overlay = findRegisterOverlay(registerValue);
@ -196,7 +93,7 @@ public enum DBTraceObjectRegisterSupport {
return;
}
mem.getMemorySpace(overlay, true)
.setValue(platform, minSnap, new RegisterValue(register, lazy.getValue()));
.setValue(platform, minSnap, new RegisterValue(register, rvc.getValue()));
}
else {
throw new AssertionError();
@ -205,10 +102,10 @@ public enum DBTraceObjectRegisterSupport {
protected void transferValueToPlatformRegister(TraceObjectValue registerValue,
TracePlatform platform, TraceMemorySpace mem, Register register) {
LazyValues lazy = new LazyValues(registerValue);
RegisterValueConverter rvc = new RegisterValueConverter(registerValue);
try {
mem.setValue(platform, registerValue.getMinSnap(),
new RegisterValue(register, lazy.getValue()));
new RegisterValue(register, rvc.getValue()));
}
catch (RegisterValueException e) {
Msg.error(this, e.getMessage());
@ -278,10 +175,10 @@ public enum DBTraceObjectRegisterSupport {
Address address = mem.getAddressSpace().getOverlayAddress(label.getAddress());
for (TraceObjectValue registerValue : it(registerObject.getOrderedValues(
label.getLifespan(), TraceObjectRegister.KEY_VALUE, true))) {
LazyValues lazy = new LazyValues(registerValue);
RegisterValueConverter rvc = new RegisterValueConverter(registerValue);
try {
long minSnap = registerValue.getMinSnap();
mem.putBytes(minSnap, address, ByteBuffer.wrap(lazy.getBytes(isBigEndian)));
mem.putBytes(minSnap, address, ByteBuffer.wrap(rvc.getBytes(isBigEndian)));
}
catch (RegisterValueException e) {
Msg.error(this, e.getMessage());
@ -293,15 +190,15 @@ public enum DBTraceObjectRegisterSupport {
throws RegisterValueException {
TraceObject registerObject = registerValue.getParent();
Trace trace = registerValue.getTrace();
LazyValues lazy = new LazyValues(registerValue);
RegisterValueConverter rvc = new RegisterValueConverter(registerValue);
String name = getRegisterName(registerObject);
TracePlatformManager platformManager = trace.getPlatformManager();
onValueCreatedTransferToPlatformRegister(registerValue, platformManager.getHostPlatform(),
name, lazy);
name, rvc);
for (TracePlatform platform : platformManager.getGuestPlatforms()) {
onValueCreatedTransferToPlatformRegister(registerValue, platform, name, lazy);
onValueCreatedTransferToPlatformRegister(registerValue, platform, name, rvc);
}
TraceNamespaceSymbolView namespaces = trace.getSymbolManager().namespaces();
@ -311,7 +208,7 @@ public enum DBTraceObjectRegisterSupport {
for (TraceLabelSymbol label : trace.getSymbolManager()
.labels()
.getChildrenNamed(name, nsRegMapBE)) {
transferRegisterValueToLabel(registerValue, label, lazy.getBytesBigEndian());
transferRegisterValueToLabel(registerValue, label, rvc.getBytesBigEndian());
}
}
TraceNamespaceSymbol nsRegMapLE =
@ -320,7 +217,7 @@ public enum DBTraceObjectRegisterSupport {
for (TraceLabelSymbol label : trace.getSymbolManager()
.labels()
.getChildrenNamed(name, nsRegMapLE)) {
transferRegisterValueToLabel(registerValue, label, lazy.getBytesLittleEndian());
transferRegisterValueToLabel(registerValue, label, rvc.getBytesLittleEndian());
}
}
}
@ -349,10 +246,10 @@ public enum DBTraceObjectRegisterSupport {
if (schema == null) {
return;
}
PathMatcher matcher = schema.searchFor(TraceObjectRegister.class, true);
matcher = matcher.applyKeys(Align.RIGHT, List.of(label.getName()));
PathFilter filter = schema.searchFor(TraceObjectRegister.class, true);
PathFilter applied = filter.applyKeys(Align.RIGHT, List.of(label.getName()));
for (TraceObjectValPath path : it(
objectManager.getValuePaths(label.getLifespan(), matcher))) {
objectManager.getValuePaths(label.getLifespan(), applied))) {
Object regRaw = path.getDestinationValue(objectManager.getRootObject());
if (regRaw instanceof TraceObject regObj) {
transferRegisterObjectToLabel(regObj, label, isBigEndian);

View file

@ -26,9 +26,8 @@ import ghidra.trace.model.guest.TracePlatform;
import ghidra.trace.model.memory.TraceObjectRegister;
import ghidra.trace.model.symbol.*;
import ghidra.trace.model.target.TraceObject;
import ghidra.trace.model.target.path.KeyPath;
import ghidra.trace.model.target.path.*;
import ghidra.trace.model.target.path.PathFilter.Align;
import ghidra.trace.model.target.path.PathMatcher;
import ghidra.trace.model.target.schema.TraceObjectSchema;
import ghidra.trace.util.TraceRegisterUtils;
import ghidra.util.LockHold;
@ -112,34 +111,31 @@ public interface InternalTracePlatform extends TracePlatform {
}
@Override
default PathMatcher getConventionalRegisterPath(TraceObjectSchema schema, KeyPath path,
default PathFilter getConventionalRegisterPath(TraceObjectSchema schema, KeyPath path,
Collection<String> names) {
PathMatcher matcher = schema.searchFor(TraceObjectRegister.class, path, true);
if (matcher.isNone()) {
return matcher;
PathFilter filter = schema.searchFor(TraceObjectRegister.class, path, true);
if (filter.isNone()) {
return PathFilter.NONE;
}
PathMatcher result = new PathMatcher();
for (String name : names) {
result.addAll(matcher.applyKeys(Align.RIGHT, List.of(name)));
}
return result;
return PathMatcher.any(names.stream()
.flatMap(n -> filter.applyKeys(Align.RIGHT, List.of(n)).getPatterns().stream()));
}
@Override
default PathMatcher getConventionalRegisterPath(TraceObjectSchema schema, KeyPath path,
default PathFilter getConventionalRegisterPath(TraceObjectSchema schema, KeyPath path,
Register register) {
return getConventionalRegisterPath(schema, path,
getConventionalRegisterObjectNames(register));
}
@Override
default PathMatcher getConventionalRegisterPath(TraceObject container, Register register) {
default PathFilter getConventionalRegisterPath(TraceObject container, Register register) {
return getConventionalRegisterPath(container.getSchema(),
container.getCanonicalPath(), register);
}
@Override
default PathMatcher getConventionalRegisterPath(AddressSpace space, Register register) {
default PathFilter getConventionalRegisterPath(AddressSpace space, Register register) {
KeyPath path = KeyPath.parse(space.getName());
TraceObjectSchema rootSchema = getTrace().getObjectManager().getRootSchema();
if (rootSchema == null) {

View file

@ -28,8 +28,8 @@ import ghidra.trace.model.target.TraceObject;
import ghidra.trace.model.target.iface.TraceObjectInterface;
import ghidra.trace.model.target.info.TraceObjectInterfaceUtils;
import ghidra.trace.model.target.path.KeyPath;
import ghidra.trace.model.target.path.PathFilter;
import ghidra.trace.model.target.path.PathFilter.Align;
import ghidra.trace.model.target.path.PathMatcher;
import ghidra.trace.model.target.schema.TraceObjectSchema;
import ghidra.trace.util.*;
import ghidra.util.LockHold;
@ -255,8 +255,8 @@ public class DBTraceObjectModule implements TraceObjectModule, DBTraceObjectInte
@Override
public TraceObjectSection getSectionByName(String sectionName) {
PathMatcher matcher = object.getSchema().searchFor(TraceObjectSection.class, true);
PathMatcher applied = matcher.applyKeys(Align.LEFT, List.of(sectionName));
PathFilter filter = object.getSchema().searchFor(TraceObjectSection.class, true);
PathFilter applied = filter.applyKeys(Align.LEFT, List.of(sectionName));
return object.getSuccessors(getLifespan(), applied)
.map(p -> p.getDestination(object).queryInterface(TraceObjectSection.class))
.findAny()

View file

@ -24,7 +24,8 @@ import ghidra.trace.database.target.DBTraceObjectInterface;
import ghidra.trace.model.Lifespan;
import ghidra.trace.model.stack.*;
import ghidra.trace.model.target.TraceObject;
import ghidra.trace.model.target.path.*;
import ghidra.trace.model.target.path.KeyPath;
import ghidra.trace.model.target.path.PathFilter;
import ghidra.trace.model.target.schema.TraceObjectSchema;
import ghidra.trace.model.thread.TraceObjectThread;
import ghidra.trace.model.thread.TraceThread;
@ -100,9 +101,9 @@ public class DBTraceObjectStack implements TraceObjectStack, DBTraceObjectInterf
protected TraceObjectStackFrame doAddStackFrame(int level) {
try (LockHold hold = object.getTrace().lockWrite()) {
PathMatcher matcher =
PathFilter filter =
object.getSchema().searchFor(TraceObjectStackFrame.class, true);
KeyPath relPath = matcher.applyKeys(KeyPath.makeIndex(level)).getSingletonPath();
KeyPath relPath = filter.applyKeys(KeyPath.makeIndex(level)).getSingletonPath();
if (relPath == null) {
throw new IllegalStateException("Could not determine where to create new frame");
}

View file

@ -32,7 +32,8 @@ import ghidra.trace.model.Lifespan;
import ghidra.trace.model.stack.*;
import ghidra.trace.model.target.TraceObject;
import ghidra.trace.model.target.iface.TraceObjectInterface;
import ghidra.trace.model.target.path.*;
import ghidra.trace.model.target.path.KeyPath;
import ghidra.trace.model.target.path.PathFilter;
import ghidra.trace.model.thread.TraceObjectThread;
import ghidra.trace.model.thread.TraceThread;
import ghidra.trace.util.TraceChangeRecord;
@ -97,12 +98,12 @@ public class DBTraceStackManager implements TraceStackManager, DBTraceManager {
public static PathFilter single(TraceObject seed,
Class<? extends TraceObjectInterface> targetIf) {
PathMatcher stackMatcher = seed.getSchema().searchFor(targetIf, false);
if (stackMatcher.getSingletonPath() == null) {
PathFilter stackFilter = seed.getSchema().searchFor(targetIf, false);
if (stackFilter.getSingletonPath() == null) {
throw new IllegalStateException("Schema doesn't provide a unique " +
targetIf.getSimpleName() + " for " + seed.getCanonicalPath());
}
return stackMatcher.getSingletonPattern();
return stackFilter.getSingletonPattern();
}
protected TraceObjectStack doGetOrAddObjectStack(TraceThread thread, long snap,

View file

@ -36,7 +36,8 @@ import ghidra.trace.model.target.*;
import ghidra.trace.model.target.iface.TraceObjectInterface;
import ghidra.trace.model.target.info.TraceObjectInterfaceFactory.Constructor;
import ghidra.trace.model.target.info.TraceObjectInterfaceUtils;
import ghidra.trace.model.target.path.*;
import ghidra.trace.model.target.path.KeyPath;
import ghidra.trace.model.target.path.PathFilter;
import ghidra.trace.model.target.schema.TraceObjectSchema;
import ghidra.trace.util.TraceChangeRecord;
import ghidra.trace.util.TraceEvents;
@ -681,8 +682,8 @@ public class DBTraceObject extends DBAnnotatedObject implements TraceObject {
public Stream<? extends TraceObjectValPath> findAncestorsInterface(Lifespan span,
Class<? extends TraceObjectInterface> iface) {
// This is a sort of meet-in-the-middle. The type search must originate from the root
PathMatcher matcher = getManager().getRootSchema().searchFor(iface, false);
return getAncestorsRoot(span, matcher);
PathFilter filter = getManager().getRootSchema().searchFor(iface, false);
return getAncestorsRoot(span, filter);
}
@Override
@ -694,8 +695,8 @@ public class DBTraceObject extends DBAnnotatedObject implements TraceObject {
public TraceObject findOrCreateCanonicalAncestorInterface(
Class<? extends TraceObjectInterface> iface) {
PathMatcher matcher = getManager().getRootSchema().searchFor(iface, false);
return path.streamMatchingAncestry(matcher)
PathFilter filter = getManager().getRootSchema().searchFor(iface, false);
return path.streamMatchingAncestry(filter)
.limit(1)
.map(kp -> manager.createObject(kp))
.findAny()
@ -711,9 +712,9 @@ public class DBTraceObject extends DBAnnotatedObject implements TraceObject {
public Stream<? extends TraceObject> findCanonicalAncestorsInterface(
Class<? extends TraceObjectInterface> iface) {
// This is a sort of meet-in-the-middle. The type search must originate from the root
PathMatcher matcher = getManager().getRootSchema().searchFor(iface, false);
PathFilter filter = getManager().getRootSchema().searchFor(iface, false);
try (LockHold hold = manager.trace.lockRead()) {
return path.streamMatchingAncestry(matcher)
return path.streamMatchingAncestry(filter)
.map(kp -> manager.getObjectByCanonicalPath(kp));
}
}
@ -741,8 +742,8 @@ public class DBTraceObject extends DBAnnotatedObject implements TraceObject {
@Override
public Stream<? extends TraceObjectValPath> findSuccessorsInterface(Lifespan span,
Class<? extends TraceObjectInterface> iface, boolean requireCanonical) {
PathMatcher matcher = getSchema().searchFor(iface, requireCanonical);
return getSuccessors(span, matcher).filter(p -> isActuallyInterface(p, iface));
PathFilter filter = getSchema().searchFor(iface, requireCanonical);
return getSuccessors(span, filter).filter(p -> isActuallyInterface(p, iface));
}
@Override

View file

@ -469,8 +469,8 @@ public class DBTraceObjectManager implements TraceObjectManager, DBTraceManager
if (rootSchema == null) {
throw new IllegalStateException("There is no schema. Create a root object.");
}
PathMatcher matcher = rootSchema.searchFor(iface, true);
return getValuePaths(span, matcher).filter(p -> {
PathFilter filter = rootSchema.searchFor(iface, true);
return getValuePaths(span, filter).filter(p -> {
TraceObject object = p.getDestination(getRootObject());
if (object == null) {
Msg.error(this, "NULL VALUE! " + p.getLastEntry());

View file

@ -25,7 +25,7 @@ import ghidra.trace.model.memory.TraceObjectRegister;
import ghidra.trace.model.symbol.TraceLabelSymbol;
import ghidra.trace.model.target.TraceObject;
import ghidra.trace.model.target.path.KeyPath;
import ghidra.trace.model.target.path.PathMatcher;
import ghidra.trace.model.target.path.PathFilter;
import ghidra.trace.model.target.schema.TraceObjectSchema;
/**
@ -192,7 +192,7 @@ public interface TracePlatform {
* @param names the possible names of the register on the target
* @return the path matcher, possibly empty
*/
PathMatcher getConventionalRegisterPath(TraceObjectSchema schema, KeyPath path,
PathFilter getConventionalRegisterPath(TraceObjectSchema schema, KeyPath path,
Collection<String> names);
/**
@ -207,7 +207,7 @@ public interface TracePlatform {
* @param register the platform register
* @return the path matcher, possibly empty
*/
PathMatcher getConventionalRegisterPath(TraceObjectSchema schema, KeyPath path,
PathFilter getConventionalRegisterPath(TraceObjectSchema schema, KeyPath path,
Register register);
/**
@ -218,7 +218,7 @@ public interface TracePlatform {
* @param register the platform register
* @return that path matcher, possibly empty, or null if the trace has no root schema
*/
PathMatcher getConventionalRegisterPath(TraceObject container, Register register);
PathFilter getConventionalRegisterPath(TraceObject container, Register register);
/**
* Get the expected path where an object defining the register value would be
@ -228,7 +228,7 @@ public interface TracePlatform {
* @param register the platform register
* @return the path matcher, or null if there is no root schema
*/
PathMatcher getConventionalRegisterPath(AddressSpace overlay, Register register);
PathFilter getConventionalRegisterPath(AddressSpace overlay, Register register);
/**
* Add a label the conventionally maps the value of a {@link TraceObjectRegister} in the object

View file

@ -0,0 +1,119 @@
/* ###
* 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.memory;
import java.math.BigInteger;
import ghidra.pcode.utils.Utils;
import ghidra.program.model.address.Address;
import ghidra.trace.model.target.TraceObjectValue;
public class RegisterValueConverter {
private final TraceObjectValue registerValue;
private BigInteger value;
private int bitLength = -1;
private byte[] be;
private byte[] le;
public RegisterValueConverter(TraceObjectValue registerValue) {
this.registerValue = registerValue;
}
public static BigInteger convertValueToBigInteger(Object val) throws RegisterValueException {
if (val instanceof String s) {
try {
return new BigInteger(s, 16);
}
catch (NumberFormatException e) {
throw new RegisterValueException(
"Invalid register value " + s + ". Must be hex digits only.");
}
}
else if (val instanceof byte[] arr) {
// NOTE: Reg object values are always big endian
return new BigInteger(1, arr);
}
else if (val instanceof Byte b) {
return BigInteger.valueOf(b);
}
else if (val instanceof Short s) {
return BigInteger.valueOf(s);
}
else if (val instanceof Integer i) {
return BigInteger.valueOf(i);
}
else if (val instanceof Long l) {
return BigInteger.valueOf(l);
}
else if (val instanceof Address a) {
return a.getOffsetAsBigInteger();
}
throw new RegisterValueException(
"Cannot convert register value: (" + val.getClass() + ") '" + val + "'");
}
BigInteger convertRegisterValueToBigInteger() throws RegisterValueException {
return convertValueToBigInteger(registerValue.getValue());
}
int getRegisterValueBitLength() throws RegisterValueException {
Object objBitLength = registerValue.getParent()
.getValue(registerValue.getMinSnap(), TraceObjectRegister.KEY_BITLENGTH)
.getValue();
if (!(objBitLength instanceof Number numBitLength)) {
throw new RegisterValueException(
"Register length is not numeric: (" + objBitLength.getClass() + ") '" +
objBitLength + "'");
}
return numBitLength.intValue();
}
public BigInteger getValue() throws RegisterValueException {
if (value != null) {
return value;
}
return value = convertRegisterValueToBigInteger();
}
int getBitLength() throws RegisterValueException {
if (bitLength != -1) {
return bitLength;
}
return bitLength = getRegisterValueBitLength();
}
int getByteLength() throws RegisterValueException {
return (getBitLength() + 7) / 8;
}
public byte[] getBytesBigEndian() throws RegisterValueException {
if (be != null) {
return be;
}
return be = Utils.bigIntegerToBytes(getValue(), getByteLength(), true);
}
public byte[] getBytesLittleEndian() throws RegisterValueException {
if (le != null) {
return le;
}
return le = Utils.bigIntegerToBytes(getValue(), getByteLength(), false);
}
public byte[] getBytes(boolean isBigEndian) throws RegisterValueException {
return isBigEndian ? getBytesBigEndian() : getBytesLittleEndian();
}
}

View file

@ -0,0 +1,22 @@
/* ###
* 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.memory;
public class RegisterValueException extends Exception {
public RegisterValueException(String message) {
super(message);
}
}

View file

@ -42,8 +42,8 @@ import ghidra.trace.model.thread.TraceObjectThread;
* a preferred presentation. In the tree convention, each register is presented with this interface.
* The name is taken from the object key, the length in bits is given in the attribute
* {@link #KEY_BITLENGTH}, and the value is given in the attribute
* {@link TraceObjectInterface#KEY_VALUE}. Some connectors may present registers as primitive
* children of the container, but that convention is discouraged.</li>
* {@link TraceObjectInterface#KEY_VALUE}. Alternatively, connectors may present registers as
* primitive children of the container.</li>
* </ol>
*
* <p>

View file

@ -491,7 +491,8 @@ public interface TraceObject extends TraceUniqueObject {
* <p>
* If an object has a disjoint life, i.e., multiple canonical parents, then only the
* least-recent of those is traversed. Aliased keys are excluded; those can't be canonical
* anyway.
* anyway. By definition, a primitive value is not canonical, even if it is the final value in
* the path.
*
* @param relativeFilter filter on the relative path from this object to desired successors
* @return the stream of value paths

View file

@ -79,8 +79,8 @@ public interface PathFilter {
}
@Override
public Collection<PathPattern> getPatterns() {
return List.of();
public Set<PathPattern> getPatterns() {
return Set.of();
}
@Override
@ -263,7 +263,7 @@ public interface PathFilter {
*
* @return the patterns
*/
Collection<PathPattern> getPatterns();
Set<PathPattern> getPatterns();
/**
* Remove count elements from the right

View file

@ -17,24 +17,30 @@ package ghidra.trace.model.target.path;
import java.util.*;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.apache.commons.lang3.StringUtils;
public class PathMatcher implements PathFilter {
protected static final Set<String> WILD_SINGLETON = Set.of("");
protected final Set<PathPattern> patterns = new HashSet<>();
protected final Set<PathPattern> patterns;
public void addPattern(KeyPath pattern) {
patterns.add(new PathPattern(pattern));
public static PathMatcher any(Stream<PathPattern> patterns) {
return new PathMatcher(patterns.collect(Collectors.toUnmodifiableSet()));
}
public void addPattern(PathPattern pattern) {
patterns.add(pattern);
public static PathMatcher any(Collection<PathFilter> filters) {
return any(filters.stream().flatMap(f -> f.getPatterns().stream()));
}
public void addAll(PathMatcher matcher) {
patterns.addAll(matcher.patterns);
public static PathMatcher any(PathFilter... filters) {
return any(Stream.of(filters).flatMap(f -> f.getPatterns().stream()));
}
PathMatcher(Set<PathPattern> patterns) {
this.patterns = patterns;
}
@Override
@ -59,19 +65,10 @@ public class PathMatcher implements PathFilter {
@Override
public PathFilter or(PathFilter that) {
PathMatcher result = new PathMatcher();
result.patterns.addAll(this.patterns);
if (that instanceof PathMatcher) {
PathMatcher matcher = (PathMatcher) that;
result.patterns.addAll(matcher.patterns);
}
else if (that instanceof PathPattern) {
result.patterns.add((PathPattern) that);
}
else {
throw new AssertionError();
}
return result;
Set<PathPattern> patterns = new HashSet<>();
patterns.addAll(this.patterns);
patterns.addAll(that.getPatterns());
return new PathMatcher(Collections.unmodifiableSet(patterns));
}
/**
@ -123,7 +120,7 @@ public class PathMatcher implements PathFilter {
}
@Override
public Collection<PathPattern> getPatterns() {
public Set<PathPattern> getPatterns() {
return patterns;
}
@ -189,19 +186,19 @@ public class PathMatcher implements PathFilter {
@Override
public PathMatcher applyKeys(Align align, List<String> indices) {
PathMatcher result = new PathMatcher();
for (PathPattern pat : patterns) {
result.addPattern(pat.applyKeys(align, indices));
Set<PathPattern> patterns = new HashSet<>();
for (PathPattern pat : this.patterns) {
patterns.add(pat.applyKeys(align, indices));
}
return result;
return new PathMatcher(Collections.unmodifiableSet(patterns));
}
@Override
public PathMatcher removeRight(int count) {
PathMatcher result = new PathMatcher();
for (PathPattern pat : patterns) {
pat.doRemoveRight(count, result);
Set<PathPattern> patterns = new HashSet<>();
for (PathPattern pat : this.patterns) {
pat.doRemoveRight(count, patterns);
}
return result;
return new PathMatcher(Collections.unmodifiableSet(patterns));
}
}

View file

@ -78,19 +78,7 @@ public class PathPattern implements PathFilter {
if (this.equals(that)) {
return this;
}
PathMatcher result = new PathMatcher();
result.addPattern(this);
if (that instanceof PathPattern) {
result.addPattern(this);
}
else if (that instanceof PathMatcher) {
PathMatcher matcher = (PathMatcher) that;
result.patterns.addAll(matcher.patterns);
}
else {
throw new AssertionError();
}
return result;
return PathMatcher.any(this, that);
}
public static boolean isWildcard(String pat) {
@ -190,8 +178,8 @@ public class PathPattern implements PathFilter {
}
@Override
public Collection<PathPattern> getPatterns() {
return List.of(this);
public Set<PathPattern> getPatterns() {
return Set.of(this);
}
@Override
@ -315,18 +303,18 @@ public class PathPattern implements PathFilter {
return result;
}
void doRemoveRight(int count, PathMatcher result) {
void doRemoveRight(int count, Set<PathPattern> result) {
KeyPath parent = pattern.parent(count);
if (parent == null) {
return;
}
result.addPattern(parent);
result.add(new PathPattern(parent));
}
@Override
public PathMatcher removeRight(int count) {
PathMatcher result = new PathMatcher();
doRemoveRight(count, result);
return result;
Set<PathPattern> patterns = new HashSet<>();
doRemoveRight(count, patterns);
return new PathMatcher(Collections.unmodifiableSet(patterns));
}
}

View file

@ -24,7 +24,7 @@ import ghidra.trace.model.TraceExecutionState;
import ghidra.trace.model.target.TraceObject;
import ghidra.trace.model.target.iface.TraceObjectInterface;
import ghidra.trace.model.target.path.KeyPath;
import ghidra.trace.model.target.path.PathMatcher;
import ghidra.trace.model.target.path.PathFilter;
/**
* The schemas common to all contexts, as they describe the primitive and built-in types.
@ -202,9 +202,9 @@ public enum PrimitiveTraceObjectSchema implements TraceObjectSchema {
}
@Override
public PathMatcher searchFor(Class<? extends TraceObjectInterface> type,
public PathFilter searchFor(Class<? extends TraceObjectInterface> type,
boolean requireCanonical) {
return new PathMatcher();
return PathFilter.NONE;
}
@Override

View file

@ -422,7 +422,7 @@ public interface TraceObjectSchema {
* @param requireCanonical only return patterns matching a canonical location for the type
* @return a set of patterns where such objects could be found
*/
default PathMatcher searchFor(Class<? extends TraceObjectInterface> type,
default PathFilter searchFor(Class<? extends TraceObjectInterface> type,
boolean requireCanonical) {
return searchFor(type, KeyPath.ROOT, requireCanonical);
}
@ -439,15 +439,15 @@ public interface TraceObjectSchema {
* @param requireCanonical only return patterns matching a canonical location for the type
* @return a set of patterns where such objects could be found
*/
default PathMatcher searchFor(Class<? extends TraceObjectInterface> type, KeyPath prefix,
default PathFilter searchFor(Class<? extends TraceObjectInterface> type, KeyPath prefix,
boolean requireCanonical) {
if (type == TraceObjectInterface.class) {
throw new IllegalArgumentException("Must provide a specific interface");
}
PathMatcher result = new PathMatcher();
Private.searchFor(this, result, prefix, true, type, false, requireCanonical,
Set<PathPattern> patterns = new HashSet<>();
Private.searchFor(this, patterns, prefix, true, type, false, requireCanonical,
new HashSet<>());
return result;
return PathMatcher.any(patterns.stream());
}
class Private {
@ -574,7 +574,7 @@ public interface TraceObjectSchema {
}
}
private static void searchFor(TraceObjectSchema sch, PathMatcher result,
private static void searchFor(TraceObjectSchema sch, Set<PathPattern> patterns,
KeyPath prefix, boolean parentIsCanonical,
Class<? extends TraceObjectInterface> type,
boolean requireAggregate, boolean requireCanonical,
@ -583,7 +583,7 @@ public interface TraceObjectSchema {
return;
}
if (sch.getInterfaces().contains(type) && (parentIsCanonical || !requireCanonical)) {
result.addPattern(prefix);
patterns.add(new PathPattern(prefix));
return;
}
if (!visited.add(sch)) {
@ -597,24 +597,24 @@ public interface TraceObjectSchema {
for (Entry<String, SchemaName> ent : sch.getElementSchemas().entrySet()) {
KeyPath extended = prefix.index(ent.getKey());
TraceObjectSchema elemSchema = ctx.getSchema(ent.getValue());
searchFor(elemSchema, result, extended, isCanonical, type, requireAggregate,
searchFor(elemSchema, patterns, extended, isCanonical, type, requireAggregate,
requireCanonical, visited);
}
KeyPath deExtended = prefix.key("[]");
TraceObjectSchema deSchema = ctx.getSchema(sch.getDefaultElementSchema());
searchFor(deSchema, result, deExtended, isCanonical, type, requireAggregate,
searchFor(deSchema, patterns, deExtended, isCanonical, type, requireAggregate,
requireCanonical, visited);
for (Entry<String, AttributeSchema> ent : sch.getAttributeSchemas().entrySet()) {
KeyPath extended = prefix.key(ent.getKey());
TraceObjectSchema attrSchema = ctx.getSchema(ent.getValue().getSchema());
searchFor(attrSchema, result, extended, isCanonical, type, requireAggregate,
searchFor(attrSchema, patterns, extended, isCanonical, type, requireAggregate,
requireCanonical, visited);
}
KeyPath daExtended = prefix.key("");
TraceObjectSchema daSchema =
ctx.getSchema(sch.getDefaultAttributeSchema().getSchema());
searchFor(daSchema, result, daExtended, isCanonical, type, requireAggregate,
searchFor(daSchema, patterns, daExtended, isCanonical, type, requireAggregate,
requireCanonical, visited);
visited.remove(sch);
@ -803,14 +803,14 @@ public interface TraceObjectSchema {
* @return the filter for finding objects
*/
default PathFilter filterForSuitable(Class<? extends TraceObjectInterface> type, KeyPath path) {
PathMatcher result = new PathMatcher();
Set<PathPattern> patterns = new HashSet<>();
Set<TraceObjectSchema> visited = new HashSet<>();
List<TraceObjectSchema> schemas = getSuccessorSchemas(path);
for (; path != null; path = path.parent()) {
TraceObjectSchema schema = schemas.get(path.size());
Private.searchFor(schema, result, path, false, type, true, false, visited);
Private.searchFor(schema, patterns, path, false, type, true, false, visited);
}
return result;
return PathMatcher.any(patterns.stream());
}
/**
@ -1036,7 +1036,7 @@ public interface TraceObjectSchema {
return null;
}
PathMatcher result = new PathMatcher();
Set<PathPattern> patterns = new HashSet<>();
for (String index : List.of(Integer.toString(frameLevel),
"0x" + Integer.toHexString(frameLevel))) {
KeyPath framePathRelStack =
@ -1044,10 +1044,10 @@ public interface TraceObjectSchema {
KeyPath framePath = stackPath.extend(framePathRelStack);
KeyPath regsPath = searchForSuitable(TraceObjectRegisterContainer.class, framePath);
if (regsPath != null) {
result.addPattern(regsPath);
patterns.add(new PathPattern(regsPath));
}
}
return result;
return PathMatcher.any(patterns.stream());
}
/**

View file

@ -37,7 +37,7 @@ import ghidra.trace.model.memory.TraceObjectRegister;
import ghidra.trace.model.target.TraceObject;
import ghidra.trace.model.target.TraceObject.ConflictResolution;
import ghidra.trace.model.target.path.KeyPath;
import ghidra.trace.model.target.path.PathMatcher;
import ghidra.trace.model.target.path.PathFilter;
import ghidra.trace.model.target.schema.SchemaContext;
import ghidra.trace.model.target.schema.TraceObjectSchema.SchemaName;
import ghidra.trace.model.target.schema.XmlSchemaContext;
@ -428,9 +428,9 @@ public class DBTraceObjectRegisterSupportTest extends AbstractGhidraHeadlessInte
x86.getConventionalRegisterRange(overlay, EAX));
}
protected static void assertMatches(String path, PathMatcher matcher) {
String message = matcher + " does not match " + path;
assertTrue(message, matcher.matches(KeyPath.parse(path)));
protected static void assertMatches(String path, PathFilter filter) {
String message = filter + " does not match " + path;
assertTrue(message, filter.matches(KeyPath.parse(path)));
}
@Test
@ -475,10 +475,10 @@ public class DBTraceObjectRegisterSupportTest extends AbstractGhidraHeadlessInte
.createOverlayAddressSpace("Targets[0].Threads[0].Registers", registers);
}
PathMatcher matcher = b.host.getConventionalRegisterPath(overlay, r0);
assertMatches("Targets[0].Threads[0].Registers.User[r0]", matcher);
assertMatches("Targets[0].Threads[0].Registers.User[a0]", matcher);
assertMatches("Targets[0].Threads[0].Registers.User[R0]", matcher);
assertMatches("Targets[0].Threads[0].Registers.User[A0]", matcher);
PathFilter filter = b.host.getConventionalRegisterPath(overlay, r0);
assertMatches("Targets[0].Threads[0].Registers.User[r0]", filter);
assertMatches("Targets[0].Threads[0].Registers.User[a0]", filter);
assertMatches("Targets[0].Threads[0].Registers.User[R0]", filter);
assertMatches("Targets[0].Threads[0].Registers.User[A0]", filter);
}
}

View file

@ -569,6 +569,13 @@ public class DBTraceObjectManagerTest extends AbstractGhidraHeadlessIntegrationT
.collect(Collectors.toList()));
}
@Test
public void testGetCanonicalSuccessors() {
populateModel(3);
assertEquals(3, root.getCanonicalSuccessors(PathFilter.parse("Targets[]")).count());
assertEquals(0, root.getCanonicalSuccessors(PathFilter.parse("anAttribute")).count());
}
@Test
public void testSetValue_TruncatesOrDeletes() {
try (Transaction tx = b.startTransaction()) {