Merge remote-tracking branch 'origin/patch'

This commit is contained in:
ghidra1 2023-01-19 08:21:32 -05:00
commit 07a35dd91a
15 changed files with 828 additions and 117 deletions

View file

@ -45,8 +45,6 @@ import ghidra.program.model.mem.MemBuffer;
import ghidra.program.model.mem.MemoryAccessException;
import ghidra.program.model.pcode.ElementId;
import ghidra.program.model.pcode.Encoder;
import ghidra.program.model.symbol.SourceType;
import ghidra.program.model.util.AddressLabelInfo;
import ghidra.program.model.util.ProcessorSymbolType;
import ghidra.sleigh.grammar.SleighPreprocessor;
import ghidra.sleigh.grammar.SourceFileIndexer;
@ -93,9 +91,11 @@ public class SleighLanguage implements Language {
/**
* Non-null if a space should yes segmented addressing
*/
String segmentedspace = "";
String segmentType = "";
AddressSet volatileAddresses;
private String segmentedspace = "";
private String segmentType = "";
private AddressSet volatileAddresses;
private AddressSet volatileSymbolAddresses;
private AddressSet nonVolatileSymbolAddresses;
private ContextCache contextcache = null;
/**
* Cached instruction prototypes
@ -156,7 +156,7 @@ public class SleighLanguage implements Language {
registerBuilder = new RegisterBuilder();
loadRegisters(registerBuilder);
readRemainingSpecification();
buildVolatileSymbolAddresses();
xrefRegisters();
instructProtoMap = new LinkedHashMap<>();
@ -164,6 +164,18 @@ public class SleighLanguage implements Language {
initParallelHelper();
}
private void buildVolatileSymbolAddresses() {
if (volatileAddresses == null) {
volatileAddresses = new AddressSet();
}
if (volatileSymbolAddresses != null) {
volatileAddresses.add(volatileSymbolAddresses);
}
if (nonVolatileSymbolAddresses != null) {
volatileAddresses.delete(nonVolatileSymbolAddresses);
}
}
private boolean isSLAWrongVersion(ResourceFile slaFile) {
XmlPullParser parser = null;
try {
@ -383,10 +395,7 @@ public class SleighLanguage implements Language {
@Override
public boolean isVolatile(Address addr) {
if (volatileAddresses != null) {
return volatileAddresses.contains(addr);
}
return false;
return volatileAddresses.contains(addr);
}
@Override
@ -798,15 +807,38 @@ public class SleighLanguage implements Language {
String typeString = symbol.getAttribute("type");
ProcessorSymbolType type = ProcessorSymbolType.getType(typeString);
boolean isEntry = SpecXmlUtils.decodeBoolean(symbol.getAttribute("entry"));
Address address = addressFactory.getAddress(addressString);
if (address == null) {
Address startAddress = addressFactory.getAddress(addressString);
int rangeSize = SpecXmlUtils.decodeInt(symbol.getAttribute("size"));
Boolean isVolatile = SpecXmlUtils.decodeNullableBoolean(
symbol.getAttribute("volatile"));
if (startAddress == null) {
Msg.error(this, "invalid symbol address \"" + addressString + "\": " +
description.getSpecFile());
}
else {
AddressLabelInfo info = new AddressLabelInfo(address, labelName, false,
null, SourceType.IMPORTED, isEntry, type);
AddressLabelInfo info;
try {
info = new AddressLabelInfo(startAddress, rangeSize, labelName, false,
isEntry, type, isVolatile);
} catch (AddressOverflowException e) {
throw new XmlParseException("invalid symbol definition: " + labelName, e);
}
defaultSymbols.add(info);
if (isVolatile != null) {
Address endAddress = info.getEndAddress();
if (isVolatile) {
if (volatileSymbolAddresses == null) {
volatileSymbolAddresses = new AddressSet();
}
volatileSymbolAddresses.addRange(startAddress, endAddress);
} else {
if (nonVolatileSymbolAddresses == null) {
nonVolatileSymbolAddresses = new AddressSet();
}
// punch a hole in the volatile address space.
nonVolatileSymbolAddresses.addRange(startAddress, endAddress);
}
}
}
// skip the end tag
parser.end(symbol);
@ -864,7 +896,7 @@ public class SleighLanguage implements Language {
read(parser);
}
catch (XmlParseException e) {
Msg.error(this, e.getMessage());
Msg.error(this, "Failed to parse Sleigh Specification ("+ specFile.getName() + "): " + e.getMessage());
}
finally {
parser.dispose();

View file

@ -67,21 +67,31 @@ public class SleighLanguageProvider implements LanguageProvider {
public static synchronized SleighLanguageProvider getSleighLanguageProvider() {
if (instance == null) {
instance = new SleighLanguageProvider();
try {
instance.createLanguages();
}
catch (Exception e) {
Msg.error(SleighLanguageProvider.class,
"Sleigh language provider initiailization failed", e);
}
}
return instance;
}
/**
* Construct sleigh language provider (singleton)
* Construct sleigh language provider (singleton use)
*/
private SleighLanguageProvider() {
try {
instance.createLanguages();
}
catch (Exception e) {
Msg.error(SleighLanguageProvider.class,
"Sleigh language provider initiailization failed", e);
}
}
/**
* Construct language provider (intended for test use only)
* @param ldefsFile language definitions file
* @throws SAXException if parse error occurs
* @throws IOException if IO error occurs
*/
SleighLanguageProvider(ResourceFile ldefsFile) throws SAXException, IOException {
createLanguages(ldefsFile);
}
private void createLanguages() throws Exception {
@ -91,7 +101,7 @@ public class SleighLanguageProvider implements LanguageProvider {
}
}
private void createLanguages(ResourceFile file) throws Exception {
private void createLanguages(ResourceFile file) throws SAXException, IOException {
try {
SleighLanguageValidator.validateLdefsFile(file);
createLanguageDescriptions(file);
@ -168,7 +178,8 @@ public class SleighLanguageProvider implements LanguageProvider {
return d;
}
private void createLanguageDescriptions(final ResourceFile specFile) throws Exception {
private void createLanguageDescriptions(final ResourceFile specFile)
throws SAXException, IOException {
ErrorHandler errHandler = new ErrorHandler() {
@Override
public void error(SAXParseException exception) throws SAXException {

View file

@ -13,63 +13,54 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.program.model.util;
package ghidra.program.model.lang;
import ghidra.program.model.address.Address;
import ghidra.program.model.symbol.*;
import ghidra.program.model.address.AddressOverflowException;
import ghidra.program.model.util.ProcessorSymbolType;
/**
* <CODE>AddressLabelInfo</CODE> is a utility class for storing
* an <CODE>Address</CODE> and a corresponding label or alias together.
* an <CODE>Address</CODE> together with a corresponding language-defined
* label or alias that is within the global namespace which is
* established with a SourceType of IMPORTED within a program.
*/
public class AddressLabelInfo implements Comparable<AddressLabelInfo> {
private Address addr;
private Address endAddr;
private String label;
private boolean isPrimary;
private Namespace scope;
private SourceType symbolSource;
private boolean isEntry;
private ProcessorSymbolType processorSymbolType;
private int sizeInBytes;
private Boolean isVolatile;
/**
* Constructs a new AddressLabelInfo object
* @param s symbol to initialize info from.
* Constructor for class AddressLabelInfo
*
* @param addr Address object that describes the memory address
* @param sizeInBytes Integer describing the Size in bytes that the label applies to.
* @param label String label or alias for the Address
* @param isPrimary boolean describes if this object is the primary label for the Address 'addr'
* @param isEntry boolean describes if this object is an entry label for the Address 'addr'
* @param type ProcessorSymbolType the type of symbol
* @param isVolatile Boolean describes if the memory at this address is volatile
*/
public AddressLabelInfo(Symbol s) {
this.addr = s.getAddress();
this.label = s.getName();
this.isPrimary = s.isPrimary();
scope = s.getParentNamespace();
symbolSource = s.getSource();
isEntry = s.isExternalEntryPoint();
}
public AddressLabelInfo(Address addr, String label, boolean isPrimary, Namespace scope,
SourceType symbolSource, boolean isEntry) {
this(addr, label, isPrimary, scope, symbolSource, isEntry, null);
}
public AddressLabelInfo(Address addr, String label, boolean isPrimary, Namespace scope,
SourceType symbolSource, boolean isEntry, ProcessorSymbolType type) {
public AddressLabelInfo(Address addr, Integer sizeInBytes, String label, boolean isPrimary,
boolean isEntry, ProcessorSymbolType type, Boolean isVolatile) throws AddressOverflowException {
this.addr = addr;
if ( sizeInBytes == null || sizeInBytes <= 0 ) {
// Default size in addressable units
this.sizeInBytes = addr.getAddressSpace().getAddressableUnitSize();
} else {
this.sizeInBytes = sizeInBytes;
}
this.endAddr = this.addr.addNoWrap(this.sizeInBytes-1);
this.label = label;
this.isPrimary = isPrimary;
this.scope = scope;
this.symbolSource = symbolSource;
this.isEntry = isEntry;
this.processorSymbolType = type;
}
public AddressLabelInfo(Address addr, String label, boolean isPrimary, SourceType symbolSource) {
this(addr, label, isPrimary, null, symbolSource, false);
}
/**
* Constructs a new AddressLabelInfo object with only address information
* @param addr the address to store in this object
*/
public AddressLabelInfo(Address addr) {
this(addr, null, false, null, SourceType.DEFAULT, false);
this.isVolatile = isVolatile;
}
/**
@ -78,7 +69,14 @@ public class AddressLabelInfo implements Comparable<AddressLabelInfo> {
public final Address getAddress() {
return addr;
}
/**
* Returns the object's end address.
*/
public final Address getEndAddress() {
return endAddr;
}
/**
* Returns the object's label or alias.
*/
@ -86,18 +84,29 @@ public class AddressLabelInfo implements Comparable<AddressLabelInfo> {
return label;
}
/**
* Returns the object's size in bytes. Always non-zero positive value and defaults to
* addressable unit size of associated address space.
*/
public final int getByteSize() {
return sizeInBytes;
}
/**
* Returns whether the object is the primary label at the address.
*/
public final boolean isPrimary() {
return isPrimary;
}
/**
* Returns the scope for the symbol.
* Returns whether the object is volatile.
* Boolean.False when the address is explicitly not volatile.
* Boolean.True when the address is volatile.
* NULL when the volatility is not defined at this address.
*/
public Namespace getScope() {
return scope;
public final Boolean isVolatile() {
return isVolatile;
}
/**
@ -140,10 +149,6 @@ public class AddressLabelInfo implements Comparable<AddressLabelInfo> {
return thisLabel.compareTo(addrLabel);
}
public SourceType getSource() {
return symbolSource;
}
public boolean isEntry() {
return isEntry;
}

View file

@ -24,7 +24,6 @@ import ghidra.program.model.address.*;
import ghidra.program.model.listing.DefaultProgramContext;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.MemBuffer;
import ghidra.program.model.util.AddressLabelInfo;
import ghidra.util.ManualEntry;
import ghidra.util.task.TaskMonitor;

View file

@ -29,7 +29,6 @@ import ghidra.program.model.address.*;
import ghidra.program.model.lang.*;
import ghidra.program.model.listing.DefaultProgramContext;
import ghidra.program.model.mem.MemBuffer;
import ghidra.program.model.util.AddressLabelInfo;
import ghidra.util.ManualEntry;
import ghidra.util.XmlProgramUtilities;
import ghidra.util.task.TaskMonitor;

View file

@ -0,0 +1,288 @@
/* ###
* 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.app.plugin.processors.sleigh;
import java.io.*;
import org.junit.Assert;
import org.junit.Test;
import generic.jar.ResourceFile;
import generic.test.AbstractGenericTest;
import ghidra.framework.Application;
import ghidra.program.model.address.Address;
import ghidra.program.model.lang.LanguageID;
public class SleighLanguageVolatilityTest extends AbstractGenericTest{
protected SleighLanguage lang;
protected String PORTFAddressString = "mem:0x31";
protected String PORTGAddressString = "mem:0x34";
protected boolean isPORTFVolatile;
protected boolean isPORTGVolatile;
/**
* Constructs a string based on parameters, and uses that as the content of a custom pspec file.
* Parameters effect the volatility of the symbol "PORTF".
* The pspec file is read by the SleighLanguage object which tracks volatile addresses.
* @param symbolVolatile Nullable boolean value that specifies the symbol PORTF volatility setting.
* @param symbolSize Nullable integer value specifying the symbol PORTF size in bytes.
* @param memoryVolatile Nullable boolean value that specifies the volatility setting of the
* memory location that includes PORTF.
* @param reverseOrder boolean, reverseOrder refers to the order that 'volatile' and
* 'default_symbols' elements appear in the pspec file.
* @throws Exception if unexpected error occurs
* @returns the data
*/
public void setUp(Boolean symbolVolatile, Integer symbolSize, Boolean memoryVolatile, boolean reverseOrder) throws Exception {
//symbolVolatile and symbolSize are in reference to the symbol PORTF. However, setting a
//size that is too large will overwrite other symbols such as PING, DDRG or PORTG.
String defaultSymbolsElement =
" <default_symbols>\r\n"
+ " <symbol name=\"RESET\" address=\"code:0x0000\" entry=\"true\"/>\r\n"
+ " <symbol name=\"INT0\" address=\"code:0x0002\" entry=\"true\"/>\r\n"
+ " <symbol name=\"INT1\" address=\"code:0x0004\" entry=\"true\"/>\r\n"
+ " <symbol name=\"PORTE\" address=\"mem:0x2e\"/>\r\n"
+ " <symbol name=\"PINF\" address=\"mem:0x2f\"/>\r\n"
+ " <symbol name=\"DDRF\" address=\"mem:0x30\"/>\r\n"
+ " <symbol name=\"PORTF\" address=\"mem:0x31\"";
defaultSymbolsElement += symbolVolatile==null ? "" : " volatile=\"" + symbolVolatile.toString() + "\"";
defaultSymbolsElement += symbolSize==null ? "" : " size=\"" + symbolSize.toString() + "\"";
defaultSymbolsElement += " />\r\n"
+ " <symbol name=\"PING\" address=\"mem:0x32\"/>\r\n"
+ " <symbol name=\"DDRG\" address=\"mem:0x33\"/>\r\n"
+ " <symbol name=\"PORTG\" address=\"mem:0x34\"/>\r\n"
+ " <symbol name=\"TIFR0\" address=\"mem:0x35\"/>\r\n"
+ " </default_symbols>\r\n";
//memoryVolatile null will not set the memory range 0x20 to 0x57 as volatile.
//memoryVolatile true will set the memory range 0x20 to 0x57 to volatile.
//memoryVolatile false will exclude the address of PORTF (0x31) from the volatility setting.
//Example:
// "<range space=\"mem\" first=\"0x20\" last=\"0x30\"/>"
// "<range space=\"mem\" first=\"0x32\" last=\"0x57\"/>"
String volatileElement =
" <volatile outputop=\"write_volatile\" inputop=\"read_volatile\">\r\n";
volatileElement += memoryVolatile == null ? "" :
memoryVolatile ?
"<range space=\"mem\" first=\"0x20\" last=\"0x57\"/>\r\n"
:
"<range space=\"mem\" first=\"0x20\" last=\"0x30\"/>\r\n"
+ "<range space=\"mem\" first=\"0x32\" last=\"0x57\"/>\r\n";
volatileElement += " <range space=\"mem\" first=\"0x60\" last=\"0xff\"/>\r\n"
+ " </volatile>\r\n";
//This variable represents the content of a pspec file.
//The original pspec file this is based on is the avr8 atmega256.pspec.
String pspecContentString =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n"
+ "\r\n"
+ "<processor_spec>\r\n"
+ "\r\n"
+ " <programcounter register=\"PC\"/> \r\n"
+ " <data_space space=\"mem\"/>\r\n";
pspecContentString += reverseOrder ? volatileElement : defaultSymbolsElement;
pspecContentString += " \r\n"
+ " <context_data>\r\n"
+ " <tracked_set space=\"code\">\r\n"
+ " <set name=\"R1\" val=\"0\"/>\r\n"
+ " </tracked_set>\r\n"
+ " </context_data>\r\n"
+ " \r\n";
pspecContentString += reverseOrder ? defaultSymbolsElement : volatileElement;
pspecContentString += "\r\n"
+ " <default_memory_blocks>\r\n"
+ " <memory_block name=\"regalias\" start_address=\"mem:0x00\" length=\"0x20\" initialized=\"false\"/>\r\n"
+ " <memory_block name=\"iospace\" start_address=\"mem:0x20\" length=\"0x1e0\" initialized=\"false\"/>\r\n"
+ " <memory_block name=\"sram\" start_address=\"mem:0x200\" length=\"0x4000\" initialized=\"false\"/>\r\n"
+ " <memory_block name=\"codebyte\" start_address=\"codebyte:0x0\" length=\"0x40000\" byte_mapped_address=\"code:0x0\"/>\r\n"
+ " </default_memory_blocks>\r\n"
+ "\r\n"
+ "\r\n"
+ "</processor_spec>\r\n"
+ "";
String languageIDString = "avr8:LE:16:atmega256Test";
LanguageID langId = new LanguageID(languageIDString);
ResourceFile pspecFile = createCustomPspecFile("atmega256", pspecContentString);
ResourceFile ldefFile = createTempLdefsFile("avr8", pspecFile);
SleighLanguageProvider provider = new SleighLanguageProvider(ldefFile);
lang = (SleighLanguage) provider.getLanguage(langId);
Address PORTFAddress = lang.getAddressFactory().getAddress(PORTFAddressString);
Address PORTGAddress = lang.getAddressFactory().getAddress(PORTGAddressString);
isPORTFVolatile = lang.isVolatile(PORTFAddress);
isPORTGVolatile = lang.isVolatile(PORTGAddress);
}
@Test
public void testPORTFWithSymbolVolatility() throws Exception {
setUp(null, null, null, false);
Assert.assertFalse(isPORTFVolatile);
setUp(false, null, null, false);
Assert.assertFalse(isPORTFVolatile);
setUp(true, null, null, false);
Assert.assertTrue(isPORTFVolatile);
}
@Test
public void testPORTFWithSize() throws Exception {
setUp(null, 1, null, false);
Assert.assertFalse(isPORTFVolatile);
Assert.assertFalse(isPORTGVolatile);
setUp(false, 1, null, false);
Assert.assertFalse(isPORTFVolatile);
Assert.assertFalse(isPORTGVolatile);
setUp(true, 1, null, false);
Assert.assertTrue(isPORTFVolatile);
Assert.assertFalse(isPORTGVolatile);
setUp(null, 4, null, false);
Assert.assertFalse(isPORTFVolatile);
Assert.assertFalse(isPORTGVolatile);
setUp(false, 4, null, false);
Assert.assertFalse(isPORTFVolatile);
Assert.assertFalse(isPORTGVolatile);
setUp(true, 4, null, false); // setting portf to size 4 overwrites portg as well
Assert.assertTrue(isPORTFVolatile);
Assert.assertTrue(isPORTGVolatile);
}
@Test
public void testPORTFNoSizeOrSymbolVolatility() throws Exception {
setUp(null, null, null, false);
Assert.assertFalse(isPORTFVolatile);
setUp(null, null, false, false);
Assert.assertFalse(isPORTFVolatile);
setUp(null, null, true, false);
Assert.assertTrue(isPORTFVolatile);
}
@Test
public void testPORTFNoSize() throws Exception {
setUp(true, null, true, false);
Assert.assertTrue(isPORTFVolatile);
setUp(false, null, true, false);
Assert.assertFalse(isPORTFVolatile);
setUp(true, null, false, false);
Assert.assertTrue(isPORTFVolatile);
setUp(false, null, false, false);
Assert.assertFalse(isPORTFVolatile);
}
@Test
public void testReverseSettingPORTFVolatile() throws Exception {
setUp(false, null, null, true);
Assert.assertFalse(isPORTFVolatile);
setUp(true, null, null, true);
Assert.assertTrue(isPORTFVolatile);
}
private ResourceFile createTempLdefsFile(String name, ResourceFile pspecFile) {
String pspecFilename = pspecFile.getName();
return createCustomLdefFile("avr8", pspecFilename);
}
public ResourceFile createCustomPspecFile(String name, String content) {
File newPspecFile = null;
try {
newPspecFile = File.createTempFile(name, ".pspec");
BufferedWriter bw = new BufferedWriter(new FileWriter(newPspecFile));
bw.write(content);
bw.close();
}
catch(IOException e){
System.err.println("Error creating test pspec file.");
}
newPspecFile.deleteOnExit();
return new ResourceFile(newPspecFile);
}
public ResourceFile createCustomLdefFile(String name, String pspecFilename) {
Iterable<ResourceFile> files = Application.findFilesByExtensionInApplication(".ldefs");
ResourceFile originalLdefFile = null;
for (ResourceFile file : files) {
if (file.getName().equals(name + ".ldefs"))
{
originalLdefFile = file;
break;
}
}
try {
File editedPspecFile = File.createTempFile(name, ".ldefs");
BufferedReader br = new BufferedReader(new FileReader(originalLdefFile.getFile(false)));
BufferedWriter bw = new BufferedWriter(new FileWriter(editedPspecFile));
String s;
while ((s = br.readLine()) != null) {
//if the string is defining a filename, edit that line
String originalPspecFilename = "atmega256.pspec";
if ( s.contains(originalPspecFilename) )
{
s = s.replace(originalPspecFilename, pspecFilename);
}
if (s.contains("avr8:LE:16:atmega256"))
{
s = s.replace("avr8:LE:16:atmega256", "avr8:LE:16:atmega256Test");
}
bw.write(s);
bw.newLine();
}
bw.close();
br.close();
editedPspecFile.deleteOnExit();
return new ResourceFile(editedPspecFile);
}
catch(IOException e) {
System.err.println("Error creating test pspec file.");
}
return null;
}
}