diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/AbstractProgramLoader.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/AbstractProgramLoader.java
index d3dcd239fc..423022cf29 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/AbstractProgramLoader.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/AbstractProgramLoader.java
@@ -33,6 +33,7 @@ import ghidra.program.database.function.OverlappingFunctionException;
import ghidra.program.model.address.*;
import ghidra.program.model.lang.*;
import ghidra.program.model.listing.*;
+import ghidra.program.model.mem.InvalidAddressException;
import ghidra.program.model.mem.MemoryConflictException;
import ghidra.program.model.symbol.*;
import ghidra.program.model.util.AddressLabelInfo;
@@ -335,10 +336,11 @@ public abstract class AbstractProgramLoader implements Loader {
blockDef);
log.appendMsg(" >> " + e.getMessage());
}
- catch (DuplicateNameException e) {
+ catch (InvalidAddressException e) {
log.appendMsg(
- "Failed to add language defined memory block due to name conflict " +
+ "Failed to add language defined memory block due to invalid address: " +
blockDef);
+ log.appendMsg(" >> Processor specification error (pspec): " + e.getMessage());
}
}
}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/xml/DataTypesXmlMgr.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/xml/DataTypesXmlMgr.java
index 8878b87ed4..8c4326e122 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/xml/DataTypesXmlMgr.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/xml/DataTypesXmlMgr.java
@@ -295,7 +295,8 @@ public class DataTypesXmlMgr {
return true;
}
- private boolean processStructure(XmlTreeNode root, boolean firstPass) {
+ private boolean processStructure(XmlTreeNode root, boolean firstPass)
+ throws XmlAttributeException {
XmlElement element = root.getStartElement();
String name = element.getAttribute("NAME");
CategoryPath path = getCategoryPath(element);
diff --git a/Ghidra/Framework/Generic/src/main/java/ghidra/util/xml/XmlParserElement.java b/Ghidra/Framework/Generic/src/main/java/ghidra/util/xml/XmlParserElement.java
index debce70481..28cd98effa 100644
--- a/Ghidra/Framework/Generic/src/main/java/ghidra/util/xml/XmlParserElement.java
+++ b/Ghidra/Framework/Generic/src/main/java/ghidra/util/xml/XmlParserElement.java
@@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
- * REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -153,7 +152,7 @@ public class XmlParserElement {
* @return the boolean value of the specified attribute
* @throws XmlAttributeException if no attribute exists with the specified name
*/
- public boolean getAttrValueAsBool(String attrName) {
+ public boolean getAttrValueAsBool(String attrName) throws XmlAttributeException {
String val = getAttrValue(attrName);
if (val == null) {
throw new XmlAttributeException("Element: "+name+": attribute "+attrName+" does not exist.");
@@ -171,7 +170,7 @@ public class XmlParserElement {
* @return the integer value of the specified attribute
* @throws XmlAttributeException if no attribute exists with the specified name
*/
- public int getAttrValueAsInt(String attrName) {
+ public int getAttrValueAsInt(String attrName) throws XmlAttributeException {
try {
String intStr = getAttrValue(attrName);
return XmlUtilities.parseInt(intStr);
@@ -189,7 +188,7 @@ public class XmlParserElement {
* @return the long value of the specified attribute
* @throws XmlAttributeException if no attribute exists with the specified name
*/
- public long getAttrValueAsLong(String attrName) {
+ public long getAttrValueAsLong(String attrName) throws XmlAttributeException {
try {
String longStr = getAttrValue(attrName);
boolean isNegative = longStr.startsWith("-");
@@ -220,7 +219,7 @@ public class XmlParserElement {
* @return the double value of the specified attribute
* @throws XmlAttributeException if no attribute exists with the specified name
*/
- public double getAttrValueAsDouble(String attrName) {
+ public double getAttrValueAsDouble(String attrName) throws XmlAttributeException {
try {
return Double.parseDouble(getAttrValue(attrName));
}
diff --git a/Ghidra/Framework/Generic/src/main/java/ghidra/util/xml/XmlUtilities.java b/Ghidra/Framework/Generic/src/main/java/ghidra/util/xml/XmlUtilities.java
index 485a919e87..e161422a55 100644
--- a/Ghidra/Framework/Generic/src/main/java/ghidra/util/xml/XmlUtilities.java
+++ b/Ghidra/Framework/Generic/src/main/java/ghidra/util/xml/XmlUtilities.java
@@ -569,7 +569,7 @@ public class XmlUtilities {
* @throws XmlAttributeException if the string in not one of y,n,true,false
* or null.
*/
- public static boolean parseBoolean(String boolStr) {
+ public static boolean parseBoolean(String boolStr) throws XmlAttributeException {
if (boolStr == null) {
return false;
}
diff --git a/Ghidra/Framework/SoftwareModeling/data/languages/processor_spec.rxg b/Ghidra/Framework/SoftwareModeling/data/languages/processor_spec.rxg
index beac886305..588eaf47b4 100644
--- a/Ghidra/Framework/SoftwareModeling/data/languages/processor_spec.rxg
+++ b/Ghidra/Framework/SoftwareModeling/data/languages/processor_spec.rxg
@@ -146,13 +146,26 @@
MemoryBlockDefinition
using a text-based specified.
+ * Intended for use when parsing XML.
+ * @param blockName memory block name (required)
+ * @param addressString start of memory block (required, see {@link AddressFactory#getAddress(String)}).
+ * @param bitMappedAddress optional specification of data source address for bit-mapped memory
+ * block (may be null)
+ * @param byteMappedAddressRatio optional specification of data source address for byte-mapped
+ * memory block which may include optional byte mapping ratio, e.g., "rom:1000/2:4" (may be
+ * null). The default mapping ratio is 1-byte to 1-source-byte (1:1), although other
+ * decimations may be specified using a mapping ratio. When specifying a mapping ratio both
+ * values must be in the range 1..127 where the right (source-byte count) value must be
+ * greater-than-or-equal to the left value (e.g., 2:4).
+ * @param mode block mode as concatenation of the following mode indicator characters:
+ *
+ * r - read mode enabled + * w - write mode enabled + * x - execute mode enabled + * v - volatile mode enabled + *+ * @param lengthString length of memory block in bytes (required) + * @param initializedString boolean (y | n | true | false) indicating if memory block is + * initialialized or not (must be null for mapped block specification) + * @param overlayString boolean (y | n | true | false) indicating if memory block is an overlay + * (false assumed if null). + * @throws XmlAttributeException if parse failure occurs (NOTE: address parsing is not performed) + */ + private MemoryBlockDefinition(String blockName, String addressString, String bitMappedAddress, + String byteMappedAddressRatio, String mode, String lengthString, + String initializedString, String overlayString) throws XmlAttributeException { + this.blockName = blockName; this.addressString = addressString; this.bitMappedAddress = bitMappedAddress; + + if (byteMappedAddressRatio != null) { + if (bitMappedAddress != null) { + throw new XmlAttributeException( + "may not specify both bit_mapped_address and byte_mapped_address"); + } + int index = byteMappedAddressRatio.indexOf('/'); + if (index > 0) { + byteMappingScheme = + new ByteMappingScheme(byteMappedAddressRatio.substring(index + 1)); + byteMappedAddress = byteMappedAddressRatio.substring(0, index); + } + else { + // 1:1 mapping scheme assumed (null byteMappingScheme) + byteMappedAddress = byteMappedAddressRatio; + } + } + if (mode != null) { mode = mode.toLowerCase(); readPermission = mode.indexOf('r') >= 0; @@ -69,42 +115,73 @@ public class MemoryBlockDefinition { catch (NumberFormatException e) { throw new XmlAttributeException(lengthString + " is not a valid integer"); } - initialized = XmlUtilities.parseBoolean(initializedString); + if (initializedString != null) { + if (bitMappedAddress != null || byteMappedAddress != null) { + throw new XmlAttributeException( + "mapped block specifications must not specify initialized attribute"); + } + initialized = XmlUtilities.parseBoolean(initializedString); + } + overlay = XmlUtilities.parseBoolean(overlayString); } - public MemoryBlockDefinition(XmlElement element) { + public MemoryBlockDefinition(XmlElement element) throws XmlAttributeException { this(element.getAttribute("name"), element.getAttribute("start_address"), - element.getAttribute("bit_mapped_address"), element.getAttribute("mode"), - element.getAttribute("length"), element.getAttribute("initialized")); + element.getAttribute("bit_mapped_address"), element.getAttribute("byte_mapped_address"), + element.getAttribute("mode"), element.getAttribute("length"), + element.getAttribute("initialized"), element.getAttribute("overlay")); } + private static Address parseAddress(String addressString, Program program, String description) + throws InvalidAddressException { + Address addr = XmlProgramUtilities.parseAddress(program.getAddressFactory(), addressString); + if (addr == null) { + throw new InvalidAddressException( + "Invalid " + description + " in memory block definition: " + addressString); + } + return addr; + } + + /** + * Create memory block within specified program based upon this block specification. + * @param program target program + * @throws LockException if program does not have exclusive access required when adding memory blocks. + * @throws MemoryConflictException if this specification conflicts with an existing memory block in program + * @throws AddressOverflowException if memory space constraints are violated by block specification + * @throws InvalidAddressException if address defined by this block specification is invalid + * for the specified program. May also indicate an improperly formatted address attribute. + */ public void createBlock(Program program) throws LockException, MemoryConflictException, - AddressOverflowException, DuplicateNameException { + AddressOverflowException, InvalidAddressException { if (blockName == null || addressString == null || length <= 0) { return; } Memory mem = program.getMemory(); - Address addr = XmlProgramUtilities.parseAddress(program.getAddressFactory(), addressString); + Address addr = parseAddress(addressString, program, "block address"); MemoryBlock block; if (bitMappedAddress != null) { - Address mappedAddr = - XmlProgramUtilities.parseAddress(program.getAddressFactory(), bitMappedAddress); - block = mem.createBitMappedBlock(blockName, addr, mappedAddr, length, false); + Address mappedAddr = parseAddress(bitMappedAddress, program, "bit-mapped address"); + block = mem.createBitMappedBlock(blockName, addr, mappedAddr, length, overlay); + } + else if (byteMappedAddress != null) { + Address mappedAddr = parseAddress(byteMappedAddress, program, "byte-mapped address"); + block = mem.createByteMappedBlock(blockName, addr, mappedAddr, length, + byteMappingScheme, overlay); } else if (initialized) { try { block = mem.createInitializedBlock(blockName, addr, length, (byte) 0, - TaskMonitor.DUMMY, false); + TaskMonitor.DUMMY, overlay); } catch (CancelledException e) { throw new AssertException(e); // unexpected } } else { - block = mem.createUninitializedBlock(blockName, addr, length, false); + block = mem.createUninitializedBlock(blockName, addr, length, overlay); } block.setRead(readPermission); block.setWrite(writePermission); @@ -112,4 +189,36 @@ public class MemoryBlockDefinition { block.setVolatile(volatilePermission); } + @Override + public String toString() { + StringBuilder buf = new StringBuilder(blockName); + buf.append(':'); + if (overlay) { + buf.append("overlay"); + } + buf.append(" start_address="); + buf.append(addressString); + if (initialized) { + buf.append(", initialized "); + } + else if (bitMappedAddress != null) { + buf.append(", bit_mapped_address="); + buf.append(bitMappedAddress); + } + else if (byteMappedAddress != null) { + buf.append(", byte_mapped_address="); + buf.append(byteMappedAddress); + if (byteMappingScheme != null) { + buf.append('/'); + buf.append(byteMappingScheme.toString()); + } + } + else { + buf.append(", uninitialized"); + } + buf.append(", length=0x"); + buf.append(Integer.toHexString(length)); + return buf.toString(); + } + } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/ByteMappingScheme.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/ByteMappingScheme.java index dbbc7dc825..2ccc30a862 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/ByteMappingScheme.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/ByteMappingScheme.java @@ -69,6 +69,34 @@ public class ByteMappingScheme { this.nonMappedByteCount = mappedSourceByteCount - mappedByteCount; } + /** + * Construct byte mapping scheme specified as a ratio of mapped bytes to source bytes. + * The two integer values in the range 1..127 are seperated by a ':' character. The number of + * mapped bytes must be less-than or equal to the number of source bytes. + * @param mappingScheme mapping scheme in string form (e.g., "2:4"). + * @throws IllegalArgumentException if invalid mapping scheme specified + */ + public ByteMappingScheme(String mappingScheme) { + + int index = mappingScheme.indexOf(':'); + if (index < 0) { + throw new IllegalArgumentException("invalid mapping scheme: " + mappingScheme); + } + String mappedByteCountStr = mappingScheme.substring(0, index); + String sourceByteCountStr = mappingScheme.substring(index + 1); + + try { + mappedByteCount = Integer.parseInt(mappedByteCountStr); + mappedSourceByteCount = Integer.parseInt(sourceByteCountStr); + } + catch (NumberFormatException e) { + throw new IllegalArgumentException("invalid mapping scheme: " + mappingScheme); + } + + validateMappingScheme(mappedByteCount, mappedSourceByteCount); + this.nonMappedByteCount = mappedSourceByteCount - mappedByteCount; + } + @Override public String toString() { String ratioStr = "1:1"; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/mem/InvalidAddressException.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/mem/InvalidAddressException.java new file mode 100644 index 0000000000..151f71910e --- /dev/null +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/mem/InvalidAddressException.java @@ -0,0 +1,42 @@ +/* ### + * 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.program.model.mem; + +import ghidra.util.exception.UsrException; + +/** + * Exception for invalid address either due to improper format + * or address not defined within target + */ +public class InvalidAddressException extends UsrException { + + /** + * Constructs a new InvalidAddressException + */ + public InvalidAddressException() { + super(); + } + + /** + * Constructs a new InvalidAddressException with a detailed message. + * + * @param msg detailed message + */ + public InvalidAddressException(String msg) { + super(msg); + } +} +