GP-1403 Allow unrestricted clearing of settings. Improve Pointer-Typedef

error condition feedback in listing.  Various tweaks to settings-based
pointer calculations.
This commit is contained in:
ghidra1 2022-03-23 14:18:24 -04:00
parent 362bd6b5cb
commit abce9bbf85
16 changed files with 215 additions and 71 deletions

View file

@ -15,12 +15,12 @@
*/
package ghidra.pcode.emulate;
import java.math.BigInteger;
import ghidra.pcode.memstate.MemoryState;
import ghidra.program.model.address.Address;
import ghidra.program.model.mem.*;
import java.math.BigInteger;
/**
* <code>MemoryStateBuffer</code> provides a MemBuffer for instruction parsing use
* which wraps an emulator MemoryState. This implementation wraps all specified
@ -111,8 +111,7 @@ public class EmulateMemoryStateBuffer implements MemBuffer {
@Override
public Memory getMemory() {
// Make sure Sleigh language provider does not call this method
throw new UnsupportedOperationException();
return null;
}
@Override

View file

@ -120,7 +120,7 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
private SettingsCache<Long> settingsCache = new SettingsCache<>(200);
private List<DataType> sortedDataTypes;
private Map<Long, Set<String>> enumValueMap;
private Map<String, Set<String>> suggestedSettingsValuesMap = new HashMap<>();
private Map<String, Set<String>> previouslyUsedSettingsValuesMap = new HashMap<>();
private List<InvalidatedListener> invalidatedListeners = new ArrayList<>();
protected DataTypeManagerChangeListenerHandler defaultListener =
@ -679,7 +679,7 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
SettingDB setting = new SettingDB(rec, settingsAdapter.getSettingName(rec));
settingsCache.put(dataTypeId, name, setting);
if (strValue != null) {
Set<String> suggestions = suggestedSettingsValuesMap.get(name);
Set<String> suggestions = previouslyUsedSettingsValuesMap.get(name);
if (suggestions != null) {
// only cache suggestion if suggestions previously requested
suggestions.add(strValue);
@ -702,7 +702,6 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
Set<String> set = new TreeSet<>();
try {
settingsAdapter.addAllValues(settingsDefinition.getStorageKey(), set);
settingsDefinition.addPreferredValues(this, set);
}
catch (IOException e) {
errHandler.dbError(e);
@ -716,11 +715,17 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
* @return suggested values or empty array if none
*/
String[] getSuggestedValues(StringSettingsDefinition settingsDefinition) {
if (!settingsDefinition.supportsSuggestedValues()) {
return Settings.EMPTY_STRING_ARRAY;
}
lock.acquire();
try {
Set<String> set = suggestedSettingsValuesMap
Set<String> previouslyUsedSet = previouslyUsedSettingsValuesMap
.computeIfAbsent(settingsDefinition.getStorageKey(),
n -> generateSuggestions(settingsDefinition));
// Last-minute additions are not cached since suggested values may change
Set<String> set = new TreeSet<>(previouslyUsedSet); // copy before updating
settingsDefinition.addPreferredValues(this, set);
if (set.isEmpty()) {
return Settings.EMPTY_STRING_ARRAY;
}
@ -3240,7 +3245,6 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
fireInvalidated();
updateFavorites();
idsToDataTypeMap.clear();
suggestedSettingsValuesMap.clear();
}
finally {
lock.release();

View file

@ -109,6 +109,25 @@ class DataTypeSettingsDB implements Settings {
* @return true if change permitted
*/
private boolean checkSetting(String type, String name) {
if (!checkImmutableSetting(type, name)) {
return false;
}
if (name != null && allowedSettingPredicate != null &&
!allowedSettingPredicate.apply(name)) {
Msg.warn(this, "Ignored disallowed setting '" + name + "'");
return false;
}
return true;
}
/**
* Check for immutable settings and log error of modification not permitted.
* Does not check for other setting restrictions.
* @param type setting type or null
* @param name setting name or null
* @return true if change permitted
*/
private boolean checkImmutableSetting(String type, String name) {
if (locked) {
String typeStr = "";
if (type != null) {
@ -123,11 +142,6 @@ class DataTypeSettingsDB implements Settings {
nameStr);
return false;
}
if (name != null && allowedSettingPredicate != null &&
!allowedSettingPredicate.apply(name)) {
Msg.warn(this, "Ignored disallowed setting '" + name + "'");
return false;
}
return true;
}
@ -205,14 +219,14 @@ class DataTypeSettingsDB implements Settings {
@Override
public void clearSetting(String name) {
if (checkSetting(null, name) && dataMgr.clearSetting(dataTypeID, name)) {
if (checkImmutableSetting(null, name) && dataMgr.clearSetting(dataTypeID, name)) {
settingsChanged();
}
}
@Override
public void clearAllSettings() {
if (checkSetting(null, null) && dataMgr.clearAllSettings(dataTypeID)) {
if (checkImmutableSetting(null, null) && dataMgr.clearAllSettings(dataTypeID)) {
settingsChanged();
}
}

View file

@ -415,6 +415,9 @@ class TypedefDB extends DataTypeDB implements TypeDef {
@Override
public String getDefaultLabelPrefix() {
if (isAutoNamed()) {
return getDataType().getDefaultLabelPrefix();
}
return getName();
}

View file

@ -21,6 +21,7 @@ import java.util.Collection;
import ghidra.docking.settings.Settings;
import ghidra.docking.settings.SettingsDefinition;
import ghidra.program.model.mem.MemBuffer;
import ghidra.program.model.scalar.Scalar;
import ghidra.util.InvalidNameException;
import ghidra.util.UniversalID;
import ghidra.util.exception.DuplicateNameException;
@ -281,10 +282,11 @@ public interface DataType {
public URL getDocs();
/**
* Get the data in the form of the appropriate Object for this DataType.
* Get the interpretted data value in the form of the appropriate Object for this DataType.
* This method must return a value consistent with {@link #getValueClass(Settings)}.
* <p>
* For instance if the datatype is an AddressDT, return an Address object. a Byte, return a
* Scalar* (maybe this should be a Byte) a Float, return a Float
* For instance, if this datatype is a {@link Pointer} an Address object or null should be returned.
* A Byte, returns a {@link Scalar} object.
*
* @param buf the data buffer.
* @param settings the settings to use.
@ -328,7 +330,8 @@ public interface DataType {
throws DataTypeEncodeException;
/**
* Get the Class of the value to be returned by this datatype.
* Get the Class of the value Object to be returned by this datatype
* (see {@link #getValue(MemBuffer, Settings, int)}).
*
* @param settings the relevant settings to use or null for default.
* @return Class of the value to be returned by this datatype or null if it can vary or is

View file

@ -16,6 +16,7 @@
package ghidra.program.model.data;
import java.util.*;
import java.util.function.Consumer;
import ghidra.docking.settings.Settings;
import ghidra.program.database.data.DataTypeUtilities;
@ -24,6 +25,7 @@ import ghidra.program.model.listing.*;
import ghidra.program.model.mem.*;
import ghidra.program.model.symbol.*;
import ghidra.util.DataConverter;
import ghidra.util.StringUtilities;
/**
* Basic implementation for a pointer dataType
@ -37,6 +39,7 @@ public class PointerDataType extends BuiltIn implements Pointer {
public static final String POINTER_NAME = "pointer";
public static final String POINTER_LABEL_PREFIX = "PTR";
public static final String POINTER_LOOP_LABEL_PREFIX = "PTR_LOOP";
public static final String NOT_A_POINTER = "NaP";
// NOTE: order dictates auto-name attribute ordering (order should not be changed)
private static TypeDefSettingsDefinition[] POINTER_TYPEDEF_SETTINGS_DEFS =
@ -345,14 +348,16 @@ public class PointerDataType extends BuiltIn implements Pointer {
/**
* Generate an address value based upon bytes stored at the specified buf
* location. The following settings, if specified, may influence the generated address:
* location. Interpretation of settings may depend on access to a {@link Memory}
* object associated with the specified {@link MemBuffer} buf.
* <BR>
* The following pointer-typedef settings are supported:
* <ul>
* <li>{@link AddressSpaceSettingsDefinition}</li>
* <li>{@link OffsetMaskSettingsDefinition}</li>
* <li>{@link OffsetShiftSettingsDefinition}</li>
* <li>{@link PointerTypeSettingsDefinition}</li>
* </ul>
* The default address space will be the same as the buffer's associated memory address.
* Interpretation of settings may depend on access to a {@link Memory} object associated
* with the specified {@link MemBuffer} buf.
*
* @param buf memory buffer positioned to stored pointer
* @param size pointer size in bytes
@ -360,31 +365,40 @@ public class PointerDataType extends BuiltIn implements Pointer {
* @return address value or null if unusable buf or data
*/
public static Address getAddressValue(MemBuffer buf, int size, Settings settings) {
return getAddressValue(buf, size, settings, msg -> {
/* ignore */});
}
/**
* Generate an address value based upon bytes stored at the specified buf
* location. Interpretation of settings may depend on access to a {@link Memory}
* object associated with the specified {@link MemBuffer} buf.
* <BR>
* The following pointer-typedef settings are supported:
* <ul>
* <li>{@link AddressSpaceSettingsDefinition}</li>
* <li>{@link OffsetMaskSettingsDefinition}</li>
* <li>{@link OffsetShiftSettingsDefinition}</li>
* <li>{@link PointerTypeSettingsDefinition}</li>
* </ul>
*
* @param buf memory buffer positioned to stored pointer
* @param size pointer size in bytes
* @param settings settings which may influence address generation
* @param errorHandler if null returned an error may be conveyed to this errorHandler
* @return address value or null if unusable buf or data
*/
public static Address getAddressValue(MemBuffer buf, int size, Settings settings,
Consumer<String> errorHandler) {
String spaceName = AddressSpaceSettingsDefinition.DEF.getValue(settings);
AddressSpace targetSpace = null;
Memory mem = buf.getMemory();
if (mem != null) {
Program program = mem.getProgram();
String spaceName = AddressSpaceSettingsDefinition.DEF.getValue(settings);
if (spaceName != null) {
// this space may be ignored if pointer type specified
targetSpace = program.getAddressFactory().getAddressSpace(spaceName);
}
}
if (targetSpace == null) {
// default to address space associated with mem buffer
targetSpace = buf.getAddress().getAddressSpace();
}
if (targetSpace instanceof SegmentedAddressSpace) {
// ignore other settings with SegmentedAddressSpace use
return getAddressValue(buf, size, targetSpace);
}
Long offset = getStoredOffset(buf, size);
if (offset == null) {
// Insufficient bytes
errorHandler.accept("Insufficient data");
return null;
}
@ -397,7 +411,7 @@ public class PointerDataType extends BuiltIn implements Pointer {
int shift = (int) OffsetShiftSettingsDefinition.DEF.getValue(settings);
if (shift < 0) {
addrOffset >>= -shift;
addrOffset >>>= -shift; // avoid sign-extension
}
else {
addrOffset <<= shift;
@ -405,42 +419,103 @@ public class PointerDataType extends BuiltIn implements Pointer {
try {
PointerType choice = PointerTypeSettingsDefinition.DEF.getType(settings);
if (choice != PointerType.DEFAULT && spaceName != null) {
errorHandler.accept("Address Space and Pointer Type settings conflict");
return null;
}
// Address space setting ignored if Pointer Type has been specified
if (choice == PointerType.IMAGE_BASE_RELATIVE && mem != null) {
if (choice == PointerType.IMAGE_BASE_RELATIVE) {
if (addrOffset == 0) {
// Done for consistency with old ImageBaseOffsetDataType.
// A 0 relative offset is considerd invalid (NaP)
return null;
return null; // NaP without error
}
if (mem == null) {
errorHandler.accept("Memory not specified");
}
// must ignore AddressSpaceSettingsDefinition
Address imageBase = mem.getProgram().getImageBase();
targetSpace = imageBase.getAddressSpace();
return imageBase.add(addrOffset * targetSpace.getAddressableUnitSize());
return imageBase.addWrap(addrOffset * targetSpace.getAddressableUnitSize());
}
else if (choice == PointerType.RELATIVE) {
// must ignore AddressSpaceSettingsDefinition
Address base = buf.getAddress();
targetSpace = base.getAddressSpace();
return base.add(addrOffset * targetSpace.getAddressableUnitSize());
return base.addWrap(addrOffset * targetSpace.getAddressableUnitSize());
}
if (choice == PointerType.FILE_OFFSET) {
if (mem != null) {
else if (choice == PointerType.FILE_OFFSET) {
if (mem == null) {
errorHandler.accept("Memory not specified");
}
else if (mem.getAllFileBytes().size() == 0) {
errorHandler.accept("No File bytes used");
}
else {
List<Address> addressList = mem.locateAddressesForFileOffset(addrOffset);
if (addressList.size() == 1) {
return addressList.get(0);
}
if (addressList.size() > 1) {
errorHandler.accept(
"Non-unique File offset mapping: 0x" + Long.toHexString(addrOffset));
}
else {
errorHandler.accept(
"File offset mapping not found: 0x" + Long.toHexString(addrOffset));
}
}
return null;
}
else if (choice != PointerType.DEFAULT) {
errorHandler.accept("Unsupported pointer type: " + choice.toString());
return null;
}
if (spaceName != null) {
if (mem == null) {
errorHandler.accept("Memory not specified");
return null;
}
Program program = mem.getProgram();
targetSpace = program.getAddressFactory().getAddressSpace(spaceName);
if (targetSpace == null) {
errorHandler.accept(
"Address space not defined: " + spaceName + ":" + formatOffset(addrOffset));
return null;
}
}
if (targetSpace == null) {
targetSpace = buf.getAddress().getAddressSpace();
}
if (targetSpace instanceof SegmentedAddressSpace) {
if (size != 2 && size != 4) {
errorHandler.accept("Unsupported segmented address size: " + size);
return null;
}
if (mask != 0 || shift != 0) {
errorHandler.accept("Unsupported mask/shift setting for segmented address");
return null;
}
return getSegmentedAddressValue(buf, size, addrOffset);
}
return targetSpace.getAddress(addrOffset, true);
}
catch (AddressOutOfBoundsException e) {
// offset too large
catch (IllegalArgumentException | AddressOutOfBoundsException e) {
errorHandler.accept(e.toString());
}
return null;
}
private static String formatOffset(long offset) {
String offsetStr = Long.toHexString(offset);
int len = offsetStr.length();
len = len - (len % 4) + 4; // multiple of 4-digits
return StringUtilities.pad(offsetStr, '0', len);
}
/**
* Get signed offset value based upon bytes stored at the specified buf
* location
@ -554,7 +629,7 @@ public class PointerDataType extends BuiltIn implements Pointer {
Address addr = (Address) getValue(buf, settings, len);
if (addr == null) { // could not create address, so return "Not a pointer (NaP)"
return "NaP";
return NOT_A_POINTER;
}
return addr.toString();
}

View file

@ -122,6 +122,9 @@ public class TypedefDataType extends GenericDataType implements TypeDef {
@Override
public String getDefaultLabelPrefix() {
if (isAutoNamed()) {
return getDataType().getDefaultLabelPrefix();
}
return getName();
}

View file

@ -294,7 +294,7 @@ public class DataStub implements Data {
@Override
public Memory getMemory() {
throw new UnsupportedOperationException();
return null;
}
@Override

View file

@ -290,7 +290,7 @@ public class InstructionStub implements Instruction {
@Override
public Memory getMemory() {
throw new UnsupportedOperationException();
return null;
}
@Override

View file

@ -67,7 +67,7 @@ public class ByteMemBufferImpl implements MemBuffer {
@Override
public Memory getMemory() {
throw new UnsupportedOperationException("Can't get memory from ByteMemBuffer");
return null;
}
@Override

View file

@ -114,7 +114,7 @@ public interface MemBuffer {
/**
* Get the Memory object actually used by the MemBuffer.
*
* @return the Memory used by this MemBuffer.
* @return the Memory used by this MemBuffer or null if not available.
*/
public Memory getMemory();