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 @@ - - - - + + + + + + + + + + + diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/plugin/processors/generic/MemoryBlockDefinition.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/plugin/processors/generic/MemoryBlockDefinition.java index d24eb7c288..408335cbef 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/plugin/processors/generic/MemoryBlockDefinition.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/plugin/processors/generic/MemoryBlockDefinition.java @@ -16,12 +16,13 @@ package ghidra.app.plugin.processors.generic; import ghidra.framework.store.LockException; -import ghidra.program.model.address.Address; -import ghidra.program.model.address.AddressOverflowException; +import ghidra.program.database.mem.ByteMappingScheme; +import ghidra.program.model.address.*; import ghidra.program.model.listing.Program; import ghidra.program.model.mem.*; import ghidra.util.XmlProgramUtilities; -import ghidra.util.exception.*; +import ghidra.util.exception.AssertException; +import ghidra.util.exception.CancelledException; import ghidra.util.task.TaskMonitor; import ghidra.util.xml.XmlAttributeException; import ghidra.util.xml.XmlUtilities; @@ -39,23 +40,68 @@ public class MemoryBlockDefinition { private String addressString; private int length; private boolean initialized; + private boolean overlay; private String bitMappedAddress; + private String byteMappedAddress; + private ByteMappingScheme byteMappingScheme; private boolean readPermission = true; private boolean writePermission = true; private boolean executePermission = false; private boolean volatilePermission = false; - @Override - public String toString() { - return blockName + " @ " + addressString + ", length=0x" + Integer.toHexString(length); - } - - public MemoryBlockDefinition(String blockName, String addressString, String bitMappedAddress, - String mode, String lengthString, String initializedString) + /** + * Construct 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); + } +} +