GT-2715 Fix stringable arrays label text generation with uninit mem.

(#272)

Check for uninitialized memory before treating as a string.
This commit is contained in:
dev747368 2019-03-29 14:04:57 -04:00
parent 26b2dfef94
commit dd7ffda876
8 changed files with 188 additions and 196 deletions

View File

@ -57,7 +57,7 @@ class ArrayDB extends DataTypeDB implements Array {
@Override
public Class<?> getValueClass(Settings settings) {
return DataTypeUtilities.getArrayValueClass(this, settings);
return getArrayValueClass(settings);
}
@Override
@ -354,18 +354,13 @@ class ArrayDB extends DataTypeDB implements Array {
@Override
public String getDefaultLabelPrefix(MemBuffer buf, Settings settings, int len,
DataTypeDisplayOptions options) {
String prefix =
ArrayStringable.getArrayStringableLabelPrefix(this, buf, settings, len, options);
return prefix != null ? prefix : super.getDefaultLabelPrefix(buf, settings, len, options);
return getArrayDefaultLabelPrefix(buf, settings, len, options);
}
@Override
public String getDefaultOffcutLabelPrefix(MemBuffer buf, Settings settings, int len,
DataTypeDisplayOptions options, int offcutLength) {
String prefix = ArrayStringable.getArrayStringableOffcutLabelPrefix(this, buf, settings,
len, options, offcutLength);
return prefix != null ? prefix
: super.getDefaultOffcutLabelPrefix(buf, settings, len, options, offcutLength);
return getArrayDefaultOffcutLabelPrefix(buf, settings, len, options, offcutLength);
}
@Override
@ -411,11 +406,11 @@ class ArrayDB extends DataTypeDB implements Array {
@Override
public Object getValue(MemBuffer buf, Settings settings, int length) {
return DataTypeUtilities.getArrayValue(this, buf, settings, length);
return getArrayValue(buf, settings, length);
}
@Override
public String getRepresentation(MemBuffer buf, Settings settings, int length) {
return DataTypeUtilities.getArrayRepresentation(this, buf, settings, length);
return getArrayRepresentation(buf, settings, length);
}
}

View File

@ -24,8 +24,6 @@ import ghidra.program.model.address.GlobalNamespace;
import ghidra.program.model.data.*;
import ghidra.program.model.data.Enum;
import ghidra.program.model.listing.Library;
import ghidra.program.model.mem.MemBuffer;
import ghidra.program.model.mem.MemoryAccessException;
import ghidra.program.model.symbol.Namespace;
import ghidra.util.UniversalID;
import ghidra.util.exception.AssertException;
@ -319,87 +317,6 @@ public class DataTypeUtilities {
return buf.toString();
}
/**
* Get the value object which corresponds to an array in memory. This will either be a
* String for the ArrayStringable case or null.
* @param arrayDt array data type
* @param buf data buffer
* @param settings data settings
* @param length length of array
* @return a String if it is an array of chars; otherwise null.
*/
public static Object getArrayValue(Array arrayDt, MemBuffer buf, Settings settings,
int length) {
if (!buf.getMemory().getAllInitializedAddressSet().contains(buf.getAddress())) {
return null;
}
ArrayStringable as = ArrayStringable.getArrayStringable(arrayDt.getDataType());
Object value = (as != null) ? as.getArrayString(buf, settings, length) : null;
return value;
// TODO
// For large array it is not scalable to create a java array object. Perhaps
// we could create a GhidraArray that can dish out objects.
// DataType dt = arrayDt.getDataType();
// Class<?> valueClass = dt.getValueClass(settings);
// if (valueClass != null) {
// int count = arrayDt.getNumElements();
// int elementLength = arrayDt.getElementLength();
// WrappedMemBuffer wrappedBuffer = new WrappedMemBuffer(buf, 0);
// Object[] array = (Object[]) java.lang.reflect.Array.newInstance(valueClass, count);
// for (int i = 0; i < count; i++) {
// wrappedBuffer.setBaseOffset(i * elementLength);
// array[i] = dt.getValue(wrappedBuffer, settings, elementLength);
// }
// return array;
// }
}
/**
* Get the representation which corresponds to an array in memory. This will either be a
* String for the ArrayStringable case or the empty string if it is not.
*
* @param arrayDt array data type
* @param buf data buffer
* @param settings data settings
* @param length length of array
* @return a String if it is an array of chars; otherwise empty string, never null.
*/
public static String getArrayRepresentation(Array arrayDt, MemBuffer buf, Settings settings,
int length) {
try {
buf.getByte(0); // test for uninitialized memory
ArrayStringable as = ArrayStringable.getArrayStringable(arrayDt.getDataType());
String value = (as != null) ? as.getArrayRepresentation(buf, settings, length) : null;
return (value != null) ? value : "";
}
catch (MemoryAccessException e) {
// ignore
}
return "";
}
/**
* Get the value Class of a specific arrayDt with settings
* ( see {@link #getArrayValueClass(Array, Settings)} ).
* @param settings the relevant settings to use or null for default.
* @return Class of the value to be returned by the array or null if it can vary
* or is unspecified (String or Array class will be returned).
*/
public static Class<?> getArrayValueClass(Array arrayDt, Settings settings) {
DataType dt = arrayDt.getDataType();
if (dt instanceof TypeDef) {
dt = ((TypeDef) dt).getBaseDataType();
}
if (dt instanceof ArrayStringable) {
if (((ArrayStringable) dt).hasStringValue(settings)) {
return String.class;
}
}
Class<?> valueClass = dt.getValueClass(settings);
return valueClass != null ? Array.class : null;
}
/**
* Create a data type category path derived from the specified namespace and rooted from
* the specified baseCategory

View File

@ -330,25 +330,25 @@ public abstract class AbstractIntegerDataType extends BuiltIn implements ArraySt
@Override
public String getArrayDefaultLabelPrefix(MemBuffer buf, Settings settings, int len,
DataTypeDisplayOptions options) {
if (!hasStringValue(settings)) {
return null;
if (hasStringValue(settings) && buf.isInitializedMemory()) {
return new StringDataInstance(this, settings, buf, len).getLabel(
AbstractStringDataType.DEFAULT_ABBREV_PREFIX + "_",
AbstractStringDataType.DEFAULT_LABEL_PREFIX, AbstractStringDataType.DEFAULT_LABEL,
options);
}
return new StringDataInstance(this, settings, buf, len).getLabel(
AbstractStringDataType.DEFAULT_ABBREV_PREFIX + "_",
AbstractStringDataType.DEFAULT_LABEL_PREFIX, AbstractStringDataType.DEFAULT_LABEL,
options);
return null;
}
@Override
public String getArrayDefaultOffcutLabelPrefix(MemBuffer buf, Settings settings, int len,
DataTypeDisplayOptions options, int offcutOffset) {
if (!hasStringValue(settings)) {
return null;
if (hasStringValue(settings) && buf.isInitializedMemory()) {
return new StringDataInstance(this, settings, buf, len).getOffcutLabelString(
AbstractStringDataType.DEFAULT_ABBREV_PREFIX + "_",
AbstractStringDataType.DEFAULT_LABEL_PREFIX, AbstractStringDataType.DEFAULT_LABEL,
options, offcutOffset);
}
return new StringDataInstance(this, settings, buf, len).getOffcutLabelString(
AbstractStringDataType.DEFAULT_ABBREV_PREFIX + "_",
AbstractStringDataType.DEFAULT_LABEL_PREFIX, AbstractStringDataType.DEFAULT_LABEL,
options, offcutOffset);
return null;
}
/**

View File

@ -15,6 +15,9 @@
*/
package ghidra.program.model.data;
import ghidra.docking.settings.Settings;
import ghidra.program.model.mem.MemBuffer;
/**
* Array interface
*/
@ -40,4 +43,129 @@ public interface Array extends DataType {
*/
DataType getDataType();
/**
* Get the appropriate string to use as the label prefix
* for an array, taking into account the actual data at the memory location.
* <p>
* See also {@link #getDefaultLabelPrefix()}
*
* @param buf memory buffer containing the bytes.
* @param settings the Settings object
* @param length the length of the data.
* @param options options for how to format the default label prefix.
* @return the label prefix or null if not applicable
*/
default public String getArrayDefaultLabelPrefix(MemBuffer buf, Settings settings, int len,
DataTypeDisplayOptions options) {
ArrayStringable stringableElementType = ArrayStringable.getArrayStringable(getDataType());
String prefix = (stringableElementType != null)
? stringableElementType.getArrayDefaultLabelPrefix(buf, settings, len, options)
: null;
return (prefix != null) ? prefix : getDefaultLabelPrefix();
}
/**
* Get the appropriate string to use as the offcut label prefix for an array, taking into
* account the actual data at the memory location.
* <p>
* See also {@link #getDefaultLabelPrefix()}
*
* @param buf memory buffer containing the bytes.
* @param settings the Settings object
* @param length the length of the data.
* @param options options for how to format the default label prefix.
* @param offcutLength offcut offset from start of buf
* @return the offcut label prefix or null if not applicable
*/
default public String getArrayDefaultOffcutLabelPrefix(MemBuffer buf, Settings settings,
int len, DataTypeDisplayOptions options, int offcutLength) {
ArrayStringable stringableElementType = ArrayStringable.getArrayStringable(getDataType());
String prefix = (stringableElementType != null)
? stringableElementType.getArrayDefaultOffcutLabelPrefix(buf, settings, len,
options, offcutLength)
: null;
return (prefix != null) ? prefix : getDefaultLabelPrefix(buf, settings, len, options);
}
/**
* Get the representation which corresponds to an array in memory. This will either be a
* String for the ArrayStringable case, "??" for uninitialized data,
* or the empty string if it is not.
*
* @param buf data buffer
* @param settings data settings
* @param length length of array
* @return a String if it is an array of chars; otherwise empty string, never null.
*/
default public String getArrayRepresentation(MemBuffer buf, Settings settings, int length) {
if (!buf.isInitializedMemory()) {
return StringDataInstance.UNKNOWN;
}
ArrayStringable stringableElementType = ArrayStringable.getArrayStringable(getDataType());
String value =
(stringableElementType != null && stringableElementType.hasStringValue(settings))
? new StringDataInstance(stringableElementType, settings, buf,
length).getStringRepresentation()
: null;
return (value != null) ? value : "";
}
/**
* Get the value object which corresponds to an array in memory. This will either be a
* String for the ArrayStringable case or null.
*
* @param buf data buffer
* @param settings data settings
* @param length length of array
* @return a String if it is an array of chars; otherwise null.
*/
default Object getArrayValue(MemBuffer buf, Settings settings, int length) {
if (!buf.getMemory().getAllInitializedAddressSet().contains(buf.getAddress())) {
return null;
}
ArrayStringable as = ArrayStringable.getArrayStringable(getDataType());
Object value = (as != null) ? as.getArrayString(buf, settings, length) : null;
return value;
// TODO
// For large array it is not scalable to create a java array object. Perhaps
// we could create a GhidraArray that can dish out objects.
// DataType dt = arrayDt.getDataType();
// Class<?> valueClass = dt.getValueClass(settings);
// if (valueClass != null) {
// int count = arrayDt.getNumElements();
// int elementLength = arrayDt.getElementLength();
// WrappedMemBuffer wrappedBuffer = new WrappedMemBuffer(buf, 0);
// Object[] array = (Object[]) java.lang.reflect.Array.newInstance(valueClass, count);
// for (int i = 0; i < count; i++) {
// wrappedBuffer.setBaseOffset(i * elementLength);
// array[i] = dt.getValue(wrappedBuffer, settings, elementLength);
// }
// return array;
// }
}
/**
* Get the value Class of a specific arrayDt with settings
* ( see {@link #getArrayValueClass(Array, Settings)} ).
*
* @param settings the relevant settings to use or null for default.
* @return Class of the value to be returned by the array or null if it can vary
* or is unspecified (String or Array class will be returned).
*/
default public Class<?> getArrayValueClass(Settings settings) {
DataType dt = getDataType();
if (dt instanceof TypeDef) {
dt = ((TypeDef) dt).getBaseDataType();
}
if (dt instanceof ArrayStringable) {
if (((ArrayStringable) dt).hasStringValue(settings)) {
return String.class;
}
}
Class<?> valueClass = dt.getValueClass(settings);
return valueClass != null ? Array.class : null;
}
}

View File

@ -165,7 +165,7 @@ public class ArrayDataType extends DataTypeImpl implements Array {
@Override
public Class<?> getValueClass(Settings settings) {
return DataTypeUtilities.getArrayValueClass(this, settings);
return getArrayValueClass(settings);
}
@Override
@ -253,18 +253,13 @@ public class ArrayDataType extends DataTypeImpl implements Array {
@Override
public String getDefaultLabelPrefix(MemBuffer buf, Settings settings, int len,
DataTypeDisplayOptions options) {
String prefix =
ArrayStringable.getArrayStringableLabelPrefix(this, buf, settings, len, options);
return prefix != null ? prefix : super.getDefaultLabelPrefix(buf, settings, len, options);
return getArrayDefaultLabelPrefix(buf, settings, len, options);
}
@Override
public String getDefaultOffcutLabelPrefix(MemBuffer buf, Settings settings, int len,
DataTypeDisplayOptions options, int offcutLength) {
String prefix = ArrayStringable.getArrayStringableOffcutLabelPrefix(this, buf, settings,
len, options, offcutLength);
return prefix != null ? prefix
: super.getDefaultOffcutLabelPrefix(buf, settings, len, options, offcutLength);
return getArrayDefaultOffcutLabelPrefix(buf, settings, len, options, offcutLength);
}
@Override
@ -274,12 +269,12 @@ public class ArrayDataType extends DataTypeImpl implements Array {
@Override
public Object getValue(MemBuffer buf, Settings settings, int length) {
return DataTypeUtilities.getArrayValue(this, buf, settings, length);
return getArrayValue(buf, settings, length);
}
@Override
public String getRepresentation(MemBuffer buf, Settings settings, int length) {
return DataTypeUtilities.getArrayRepresentation(this, buf, settings, length);
return getArrayRepresentation(buf, settings, length);
}
}

View File

@ -16,7 +16,6 @@
package ghidra.program.model.data;
import ghidra.docking.settings.Settings;
import ghidra.program.model.listing.Data;
import ghidra.program.model.mem.MemBuffer;
/**
@ -37,23 +36,6 @@ public interface ArrayStringable extends DataType {
*/
public boolean hasStringValue(Settings settings);
/**
* Returns a {@link StringDataInstance} representing this ArrayStringable's contents.
*
* <p>
* @param buf {@link MemBuffer} containing the data bytes.
* @param settings {@link Settings} object containing settings, usually the {@link Data}
* element.
* @param length number of bytes that this data object contains (ie. how big was the array)
* @return a new {@link StringDataInstance} representing this ArrayStringable's contents,
* never NULL. See {@link StringDataInstance#NULL_INSTANCE}.
*/
public default StringDataInstance getStringDataInstance(MemBuffer buf, Settings settings,
int length) {
return hasStringValue(settings) ? new StringDataInstance(this, settings, buf, length)
: StringDataInstance.NULL_INSTANCE;
}
/**
* For cases where an array of this type exists, get the array value as a String.
* When data corresponds to character data it should generally be expressed as a string.
@ -64,19 +46,10 @@ public interface ArrayStringable extends DataType {
* @return array value expressed as a string or null if data is not character data
*/
public default String getArrayString(MemBuffer buf, Settings settings, int length) {
return getStringDataInstance(buf, settings, length).getStringValue();
}
/**
* For cases where an array of this type exists, get the representation string which
* corresponds to the array (example: String for an array of chars).
* @param buf memory buffer containing the bytes.
* @param settings the Settings object
* @param length the length of the data.
* @return array representation or null of an array representation is not supported.
*/
public default String getArrayRepresentation(MemBuffer buf, Settings settings, int length) {
return getStringDataInstance(buf, settings, length).getStringRepresentation();
if (hasStringValue(settings) && buf.isInitializedMemory()) {
return new StringDataInstance(this, settings, buf, length).getStringValue();
}
return null;
}
/**
@ -126,40 +99,4 @@ public interface ArrayStringable extends DataType {
return (dt instanceof ArrayStringable) ? (ArrayStringable) dt : null;
}
/**
* Get the appropriate string to use as the label prefix
* for an array which corresponds to an ArrayStringable
* element data type.
* @param arrayDt array data type
* @param buf memory buffer containing the bytes.
* @param settings the Settings object
* @param length the length of the data.
* @param options options for how to format the default label prefix.
* @return the ArrayStringable label prefix or null if not applicable
*/
public static String getArrayStringableLabelPrefix(Array arrayDt, MemBuffer buf,
Settings settings, int len, DataTypeDisplayOptions options) {
ArrayStringable as = getArrayStringable(arrayDt.getDataType());
return (as != null) ? as.getArrayDefaultLabelPrefix(buf, settings, len, options) : null;
}
/**
* Get the appropriate string to use as the offcut label prefix
* for an array which corresponds to an ArrayStringable
* element data type.
* @param arrayDt array data type
* @param buf memory buffer containing the bytes.
* @param settings the Settings object
* @param length the length of the data.
* @param options options for how to format the default label prefix.
* @param offcutLength offcut offset from start of buf
* @return the ArrayStringable offcut label prefix or null if not applicable
*/
public static String getArrayStringableOffcutLabelPrefix(Array arrayDt, MemBuffer buf,
Settings settings, int len, DataTypeDisplayOptions options, int offcutLength) {
ArrayStringable as = getArrayStringable(arrayDt.getDataType());
return (as != null)
? as.getArrayDefaultOffcutLabelPrefix(buf, settings, len, options, offcutLength)
: null;
}
}

View File

@ -51,7 +51,7 @@ public class StringDataInstance {
* @return boolean true if string data.
*/
public static boolean isString(Data data) {
if (data == null) {
if (data == null || !data.isInitializedMemory()) {
return false;
}
DataType dt = data.getBaseDataType();
@ -89,36 +89,38 @@ public class StringDataInstance {
return ((AbstractStringDataType) dt).getStringDataInstance(data, data,
data.getLength());
}
if (dt instanceof Array) {
if (dt instanceof Array && !data.isInitializedMemory()) {
ArrayStringable arrayStringable =
ArrayStringable.getArrayStringable(((Array) dt).getDataType());
return arrayStringable.getStringDataInstance(data, data, data.getLength());
if (arrayStringable != null && arrayStringable.hasStringValue(data)) {
return new StringDataInstance(arrayStringable, data, data, data.getLength());
}
}
return NULL_INSTANCE;
}
/**
* Returns a new {@link StringDataInstance} using the bytes in the MemBuffer.
* <p>
* @param stringDataType {@link DataType} of the bytes in the buffer.
* @param dataType {@link DataType} of the bytes in the buffer.
* @param buf memory buffer containing the bytes.
* @param settings the Settings object
* @param length the length of the data.
* @return new {@link StringDataInstance}, never NULL. See {@link #NULL_INSTANCE}.
*/
public static StringDataInstance getStringDataInstance(DataType stringDataType, MemBuffer buf,
public static StringDataInstance getStringDataInstance(DataType dataType, MemBuffer buf,
Settings settings, int length) {
if (stringDataType instanceof AbstractStringDataType) {
return ((AbstractStringDataType) stringDataType).getStringDataInstance(buf, settings,
length);
if (dataType instanceof AbstractStringDataType) {
return ((AbstractStringDataType) dataType).getStringDataInstance(buf, settings, length);
}
if (stringDataType instanceof Array &&
((Array) stringDataType).getDataType() instanceof ArrayStringable) {
stringDataType = ((Array) stringDataType).getDataType();
if (dataType instanceof Array) {
dataType = ArrayStringable.getArrayStringable(((Array) dataType).getDataType());
}
if (stringDataType instanceof ArrayStringable &&
((ArrayStringable) stringDataType).hasStringValue(settings)) {
return ((ArrayStringable) stringDataType).getStringDataInstance(buf, settings, length);
if (dataType instanceof ArrayStringable &&
((ArrayStringable) dataType).hasStringValue(settings) && buf.isInitializedMemory()) {
return new StringDataInstance(dataType, settings, buf, length);
}
return NULL_INSTANCE;
}

View File

@ -51,6 +51,24 @@ import ghidra.program.model.address.Address;
*/
public interface MemBuffer {
/**
* Returns true if this buffer's starting address has valid data.
*
* @return boolean true if first byte of memory buffer can be read
*/
public default boolean isInitializedMemory() {
// TODO: possible alternate method of testing
//return getMemory().getAllInitializedAddressSet().contains(getAddress());
try {
getByte(0); // test for uninitialized memory
return true;
}
catch (MemoryAccessException e) {
// ignore
}
return false;
}
/**
* Get one byte from memory at the current position plus offset.
*