GP-2133 added support for byte-mapped and overlay blocks to pspec

This commit is contained in:
ghidra1 2022-06-29 10:13:25 -04:00
parent 47cda95669
commit 418925edeb
8 changed files with 228 additions and 34 deletions

View File

@ -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());
}
}
}

View File

@ -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);

View File

@ -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));
}

View File

@ -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;
}

View File

@ -146,13 +146,26 @@
<element name="memory_block">
<attribute name="name"/>
<attribute name="start_address"/>
<optional> <attribute name="bit_mapped_address"/> </optional>
<optional> <attribute name="mode"/> </optional>
<optional> <attribute name="length"/> </optional>
<optional>
<attribute name="length"/>
<choice>
<attribute name="bit_mapped_address"/>
<!--
byte_mapped_address - mapped memory address and optional mapping ratio
Examples:
byte_mapped_address="rom:1000" - maps every byte starting at rom:1000
byte_mapped_address="rom:1000/1:2" - maps one byte for every two bytes
starting at rom:1000. Facilitates skip of padding bytes.
-->
<attribute name="byte_mapped_address"/>
<attribute name="initialized">
<ref name="boolean_type"/>
</attribute>
</choice>
<optional> <attribute name="mode"/> </optional>
<optional>
<attribute name="overlay">
<ref name="boolean_type"/>
</attribute>
</optional>
</element>
</oneOrMore>

View File

@ -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 <code>MemoryBlockDefinition</code> 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:
* <pre>
* r - read mode enabled
* w - write mode enabled
* x - execute mode enabled
* v - volatile mode enabled
* </pre>
* @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();
}
}

View File

@ -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";

View File

@ -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);
}
}