mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-05 19:42:36 +02:00
GP-571: dbgeng schema implementation
This commit is contained in:
parent
eb66a90f6c
commit
c81a17405d
49 changed files with 397 additions and 80 deletions
|
@ -22,14 +22,14 @@ import java.util.concurrent.CompletableFuture;
|
|||
import ghidra.async.AsyncUtils;
|
||||
import ghidra.async.TypeSpec;
|
||||
import ghidra.dbg.attributes.TargetObjectRef;
|
||||
import ghidra.dbg.error.DebuggerModelNoSuchPathException;
|
||||
import ghidra.dbg.error.DebuggerModelTypeException;
|
||||
import ghidra.dbg.error.*;
|
||||
import ghidra.dbg.target.TargetMemory;
|
||||
import ghidra.dbg.target.TargetObject;
|
||||
import ghidra.dbg.target.schema.EnumerableTargetObjectSchema;
|
||||
import ghidra.dbg.target.schema.TargetObjectSchema;
|
||||
import ghidra.dbg.util.PathUtils;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.util.Msg;
|
||||
|
||||
/**
|
||||
* A debugger model, often a connection to an external debugger
|
||||
|
@ -220,6 +220,7 @@ public interface DebuggerObjectModel {
|
|||
/**
|
||||
* Create a reference to the given path in this model
|
||||
*
|
||||
* <p>
|
||||
* Note that the path is not checked until the object is fetched. Thus, it is possible for a
|
||||
* reference to refer to a non-existent object.
|
||||
*
|
||||
|
@ -238,6 +239,7 @@ public interface DebuggerObjectModel {
|
|||
/**
|
||||
* Fetch the attributes of a given model path
|
||||
*
|
||||
* <p>
|
||||
* Giving an empty path will retrieve the attributes of the root object. If the path does not
|
||||
* exist, the future completes with {@code null}.
|
||||
*
|
||||
|
@ -269,6 +271,7 @@ public interface DebuggerObjectModel {
|
|||
/**
|
||||
* Fetch the elements of a given model path
|
||||
*
|
||||
* <p>
|
||||
* Giving an empty path will retrieve all the top-level objects, i.e., elements of the root. If
|
||||
* the path does not exist, the future completes with {@code null}.
|
||||
*
|
||||
|
@ -300,6 +303,7 @@ public interface DebuggerObjectModel {
|
|||
/**
|
||||
* Fetch the root object of the model
|
||||
*
|
||||
* <p>
|
||||
* The root is a virtual object to contain all the top-level objects of the model tree. This
|
||||
* object represents the debugger itself.
|
||||
*
|
||||
|
@ -310,7 +314,6 @@ public interface DebuggerObjectModel {
|
|||
/**
|
||||
* Fetch the value at the given path
|
||||
*
|
||||
*
|
||||
* @param path the path of the value
|
||||
* @return a future completing with the value or with {@code null} if the path does not exist
|
||||
*/
|
||||
|
@ -462,6 +465,7 @@ public interface DebuggerObjectModel {
|
|||
/**
|
||||
* Invalidate the caches for every object known locally.
|
||||
*
|
||||
* <p>
|
||||
* Unlike, {@link TargetObject#invalidateCaches()}, this does not push the request to a remote
|
||||
* object. If the objects are proxies, just the proxies' caches are cleared. Again, this does
|
||||
* not apply to caches for the objects' children.
|
||||
|
@ -471,10 +475,30 @@ public interface DebuggerObjectModel {
|
|||
/**
|
||||
* Close the session and dispose the model
|
||||
*
|
||||
* <p>
|
||||
* For local sessions, terminate the debugger. For client sessions, disconnect.
|
||||
*
|
||||
* @return a future which completes when the session is closed
|
||||
*/
|
||||
public CompletableFuture<Void> close();
|
||||
|
||||
/**
|
||||
* A convenience for reporting errors conditionally
|
||||
*
|
||||
* <p>
|
||||
* If the message is ignorable, e.g., a {@link DebuggerModelTerminatingException}, then the
|
||||
* report will be reduced to a stack-free warning.
|
||||
*
|
||||
* @param origin the object producing the error
|
||||
* @param message the error message
|
||||
* @param ex the exception
|
||||
*/
|
||||
default void reportError(Object origin, String message, Throwable ex) {
|
||||
if (ex == null || DebuggerModelTerminatingException.isIgnorable(ex)) {
|
||||
Msg.warn(origin, message + ": " + ex);
|
||||
}
|
||||
else {
|
||||
Msg.error(origin, message, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,9 +50,6 @@ public interface TargetEnvironment<T extends TargetEnvironment<T>> extends Typed
|
|||
String DEBUGGER_ATTRIBUTE_NAME = PREFIX_INVISIBLE + "debugger";
|
||||
String OS_ATTRIBUTE_NAME = PREFIX_INVISIBLE + "os";
|
||||
String ENDIAN_ATTRIBUTE_NAME = PREFIX_INVISIBLE + "endian";
|
||||
String VISIBLE_ARCH_ATTRIBUTE_NAME = "arch";
|
||||
String VISIBLE_OS_ATTRIBUTE_NAME = "os";
|
||||
String VISIBLE_ENDIAN_ATTRIBUTE_NAME = "endian";
|
||||
|
||||
/**
|
||||
* Get a description of the target architecture
|
||||
|
@ -66,9 +63,9 @@ public interface TargetEnvironment<T extends TargetEnvironment<T>> extends Typed
|
|||
*
|
||||
* @return the target architecture
|
||||
*/
|
||||
@TargetAttributeType(name = VISIBLE_ARCH_ATTRIBUTE_NAME)
|
||||
@TargetAttributeType(name = ARCH_ATTRIBUTE_NAME, hidden = true)
|
||||
default String getArchitecture() {
|
||||
return getTypedAttributeNowByName(VISIBLE_ARCH_ATTRIBUTE_NAME, String.class, "");
|
||||
return getTypedAttributeNowByName(ARCH_ATTRIBUTE_NAME, String.class, "");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -101,9 +98,9 @@ public interface TargetEnvironment<T extends TargetEnvironment<T>> extends Typed
|
|||
*
|
||||
* @return the target operating system
|
||||
*/
|
||||
@TargetAttributeType(name = VISIBLE_OS_ATTRIBUTE_NAME)
|
||||
@TargetAttributeType(name = OS_ATTRIBUTE_NAME, hidden = true)
|
||||
default String getOperatingSystem() {
|
||||
return getTypedAttributeNowByName(VISIBLE_OS_ATTRIBUTE_NAME, String.class, "");
|
||||
return getTypedAttributeNowByName(OS_ATTRIBUTE_NAME, String.class, "");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -116,9 +113,9 @@ public interface TargetEnvironment<T extends TargetEnvironment<T>> extends Typed
|
|||
*
|
||||
* @return the target endianness
|
||||
*/
|
||||
@TargetAttributeType(name = VISIBLE_ENDIAN_ATTRIBUTE_NAME)
|
||||
@TargetAttributeType(name = ENDIAN_ATTRIBUTE_NAME, hidden = true)
|
||||
default String getEndian() {
|
||||
return getTypedAttributeNowByName(VISIBLE_ENDIAN_ATTRIBUTE_NAME, String.class, "");
|
||||
return getTypedAttributeNowByName(ENDIAN_ATTRIBUTE_NAME, String.class, "");
|
||||
}
|
||||
|
||||
// TODO: Devices? File System?
|
||||
|
|
|
@ -39,7 +39,6 @@ public interface TargetModule<T extends TargetModule<T>> extends TypedTargetObje
|
|||
Class<Private.Cls> tclass = (Class) TargetModule.class;
|
||||
TypeSpec<TargetModule<?>> TYPE = TypeSpec.auto();
|
||||
|
||||
String VISIBLE_RANGE_ATTRIBUTE_NAME = "range";
|
||||
String RANGE_ATTRIBUTE_NAME = PREFIX_INVISIBLE + "range";
|
||||
String MODULE_NAME_ATTRIBUTE_NAME = PREFIX_INVISIBLE + "module_name";
|
||||
|
||||
|
@ -54,9 +53,9 @@ public interface TargetModule<T extends TargetModule<T>> extends TypedTargetObje
|
|||
*
|
||||
* @return the base address, or {@code null}
|
||||
*/
|
||||
@TargetAttributeType(name = VISIBLE_RANGE_ATTRIBUTE_NAME, required = true)
|
||||
@TargetAttributeType(name = RANGE_ATTRIBUTE_NAME, required = true, hidden = true)
|
||||
public default AddressRange getRange() {
|
||||
return getTypedAttributeNowByName(VISIBLE_RANGE_ATTRIBUTE_NAME, AddressRange.class, null);
|
||||
return getTypedAttributeNowByName(RANGE_ATTRIBUTE_NAME, AddressRange.class, null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -221,7 +221,12 @@ public interface TargetObject extends TargetObjectRef {
|
|||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
/**
|
||||
* A conventional prefix of hidden attributes defined by the {@code TargetObject} interfaces
|
||||
*
|
||||
* <p>
|
||||
* When the "hidden" field of attributes can be overridden, this prefix should be removed
|
||||
*/
|
||||
String PREFIX_INVISIBLE = "_";
|
||||
|
||||
String DISPLAY_ATTRIBUTE_NAME = PREFIX_INVISIBLE + "display";
|
||||
|
|
|
@ -45,7 +45,6 @@ public interface TargetSection<T extends TargetSection<T>> extends TypedTargetOb
|
|||
|
||||
String MODULE_ATTRIBUTE_NAME = PREFIX_INVISIBLE + "module";
|
||||
String RANGE_ATTRIBUTE_NAME = PREFIX_INVISIBLE + "range";
|
||||
String VISIBLE_RANGE_ATTRIBUTE_NAME = "range";
|
||||
|
||||
/**
|
||||
* Get the module to which this section belongs
|
||||
|
@ -66,9 +65,9 @@ public interface TargetSection<T extends TargetSection<T>> extends TypedTargetOb
|
|||
*
|
||||
* @return the range
|
||||
*/
|
||||
@TargetAttributeType(name = VISIBLE_RANGE_ATTRIBUTE_NAME, required = true, fixed = true)
|
||||
@TargetAttributeType(name = RANGE_ATTRIBUTE_NAME, required = true, fixed = true)
|
||||
public default AddressRange getRange() {
|
||||
return getTypedAttributeNowByName(VISIBLE_RANGE_ATTRIBUTE_NAME, AddressRange.class, null);
|
||||
return getTypedAttributeNowByName(RANGE_ATTRIBUTE_NAME, AddressRange.class, null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -31,6 +31,7 @@ import ghidra.dbg.target.TargetObject;
|
|||
import ghidra.dbg.target.schema.DefaultTargetObjectSchema.DefaultAttributeSchema;
|
||||
import ghidra.dbg.target.schema.TargetObjectSchema.AttributeSchema;
|
||||
import ghidra.dbg.target.schema.TargetObjectSchema.SchemaName;
|
||||
import ghidra.util.Msg;
|
||||
import utilities.util.reflection.ReflectionUtilities;
|
||||
|
||||
public class AnnotatedSchemaContext extends DefaultSchemaContext {
|
||||
|
@ -115,8 +116,7 @@ public class AnnotatedSchemaContext extends DefaultSchemaContext {
|
|||
return Set.of(TargetObject.class);
|
||||
}
|
||||
throw new IllegalArgumentException("Getter " + getter +
|
||||
" for attribute must return primitive or subclass of " +
|
||||
TargetObjectRef.class);
|
||||
" for attribute must return primitive or subclass of " + TargetObjectRef.class);
|
||||
}
|
||||
|
||||
protected final Map<Class<? extends TargetObject>, SchemaName> namesByClass =
|
||||
|
@ -126,13 +126,14 @@ public class AnnotatedSchemaContext extends DefaultSchemaContext {
|
|||
|
||||
protected SchemaName nameFromAnnotatedClass(Class<? extends TargetObject> cls) {
|
||||
synchronized (namesByClass) {
|
||||
TargetObjectSchemaInfo info = cls.getAnnotation(TargetObjectSchemaInfo.class);
|
||||
if (info == null) {
|
||||
// TODO: Compile-time validation?
|
||||
Msg.warn(this, "Class " + cls + " is not annotated with @" +
|
||||
TargetObjectSchemaInfo.class.getSimpleName());
|
||||
return EnumerableTargetObjectSchema.OBJECT.getName();
|
||||
}
|
||||
return namesByClass.computeIfAbsent(cls, c -> {
|
||||
TargetObjectSchemaInfo info = cls.getAnnotation(TargetObjectSchemaInfo.class);
|
||||
if (info == null) {
|
||||
// TODO: Compile-time validation?
|
||||
throw new IllegalArgumentException("Class " + cls + " is not annotated with @" +
|
||||
TargetObjectSchemaInfo.class.getSimpleName());
|
||||
}
|
||||
String name = info.name();
|
||||
if (name.equals("")) {
|
||||
return new SchemaName(cls.getSimpleName());
|
||||
|
@ -166,8 +167,7 @@ public class AnnotatedSchemaContext extends DefaultSchemaContext {
|
|||
|
||||
AttributeSchema attrSchema;
|
||||
try {
|
||||
attrSchema =
|
||||
attributeSchemaFromAnnotatedMethod(declCls, method, at);
|
||||
attrSchema = attributeSchemaFromAnnotatedMethod(declCls, method, at);
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
throw new IllegalArgumentException(
|
||||
|
@ -209,7 +209,7 @@ public class AnnotatedSchemaContext extends DefaultSchemaContext {
|
|||
if (bounds.size() != 1) {
|
||||
// TODO: Compile-time validation?
|
||||
throw new IllegalArgumentException(
|
||||
"Could not identify unique element class: " + bounds);
|
||||
"Could not identify unique element class (" + bounds + ") for " + cls);
|
||||
}
|
||||
else {
|
||||
Class<? extends TargetObject> bound = bounds.iterator().next();
|
||||
|
@ -219,8 +219,8 @@ public class AnnotatedSchemaContext extends DefaultSchemaContext {
|
|||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
throw new IllegalArgumentException(
|
||||
"Could not get schema name from bound " + bound + " of " +
|
||||
cls + ".fetchElements()",
|
||||
"Could not get schema name from bound " + bound + " of " + cls +
|
||||
".fetchElements()",
|
||||
e);
|
||||
}
|
||||
builder.setDefaultElementSchema(schemaName);
|
||||
|
@ -245,30 +245,27 @@ public class AnnotatedSchemaContext extends DefaultSchemaContext {
|
|||
}
|
||||
|
||||
protected String attributeNameFromBean(String beanName, boolean isBool) {
|
||||
beanName = isBool
|
||||
? StringUtils.removeStartIgnoreCase(beanName, "is")
|
||||
beanName = isBool ? StringUtils.removeStartIgnoreCase(beanName, "is")
|
||||
: StringUtils.removeStartIgnoreCase(beanName, "get");
|
||||
if (beanName.equals("")) {
|
||||
throw new IllegalArgumentException("Attribute getter must have a name");
|
||||
}
|
||||
return beanName
|
||||
.replaceAll("([A-Z]+)([A-Z][a-z])", "$1_$2")
|
||||
return beanName.replaceAll("([A-Z]+)([A-Z][a-z])", "$1_$2")
|
||||
.replaceAll("([a-z])([A-Z])", "$1_$2")
|
||||
.toLowerCase();
|
||||
}
|
||||
|
||||
protected AttributeSchema attributeSchemaFromAnnotation(TargetAttributeType at) {
|
||||
return new DefaultAttributeSchema(at.name(), nameFromClass(at.type()),
|
||||
at.required(), at.fixed(), at.hidden());
|
||||
return new DefaultAttributeSchema(at.name(), nameFromClass(at.type()), at.required(),
|
||||
at.fixed(), at.hidden());
|
||||
}
|
||||
|
||||
protected AttributeSchema attributeSchemaFromAnnotatedMethod(Class<? extends TargetObject> cls,
|
||||
Method method, TargetAttributeType at) {
|
||||
if (method.getParameterCount() != 0) {
|
||||
// TODO: Compile-time validation?
|
||||
throw new IllegalArgumentException(
|
||||
"Non-getter method " + method + " is annotated with @" +
|
||||
TargetAttributeType.class.getSimpleName());
|
||||
throw new IllegalArgumentException("Non-getter method " + method +
|
||||
" is annotated with @" + TargetAttributeType.class.getSimpleName());
|
||||
}
|
||||
String name = at.name();
|
||||
Class<?> ret = method.getReturnType();
|
||||
|
@ -276,11 +273,10 @@ public class AnnotatedSchemaContext extends DefaultSchemaContext {
|
|||
name = attributeNameFromBean(method.getName(),
|
||||
EnumerableTargetObjectSchema.BOOL.getTypes().contains(ret));
|
||||
}
|
||||
SchemaName primitiveName =
|
||||
EnumerableTargetObjectSchema.nameForPrimitive(ret);
|
||||
SchemaName primitiveName = EnumerableTargetObjectSchema.nameForPrimitive(ret);
|
||||
if (primitiveName != null) {
|
||||
return new DefaultAttributeSchema(name, primitiveName,
|
||||
at.required(), at.fixed(), at.hidden());
|
||||
return new DefaultAttributeSchema(name, primitiveName, at.required(), at.fixed(),
|
||||
at.hidden());
|
||||
}
|
||||
Set<Class<? extends TargetObject>> bounds = getBoundsOfObjectAttributeGetter(cls, method);
|
||||
if (bounds.size() != 1) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue