Fixed typo in AddressLabelInfo.
Improved implementation and error handling of AddressLabelInfo.
GP-2606
Refactored the package of 'AddressLabelInfo' to beghidra.program.model.lang. Removed the source and namespace variablesfrom AddressLabelInfo since they would always be global and IMPORTED.Removed a number of constructors to simplify AddressLabelInfo. Improvederror handing in SleighLanguage class.
GP-2606
Added javadocs to and throw exceptions from AddressLabelInfo constructors and removed extra constructors that were no longer needed. Added exception handling to AbsractProgramLoader and SleighLanguage for thrown exceptions. Made some variables in SleighLanguage private and removed an unnecessary null check.
GP-2606
Prepared test to help investigate volatility interplay settings.
GP-2606
Updated per review comments. SleighLanguage will parse pspec files andthen apply symbol element settings for volatility afterward. Simplifiedtests for SleighLanguage and the Decompiler.
GP-2606
Made updates based on review feedback. Address label info holds the endaddress of the label. Sleigh language will resolve symbol mutabilityafter parsing the pspec file such that the order of elements in the filewill not be a factor. Rearranged tests in preparation for improvement.Added a constructor to the ProgramBuilder class so that custom definedpspec files may be used in the tests.
GP-2606
Updated SpecXmlUtils decodeBoolean method to call decodeNullableBooleanfirst, however if the result is null, to return false instead of null.
GP-2606
Changed size of addressLabelInfo to sizeInBytes and added comment toisVolatile that specifies what null, false, or true mean in context.Incorporated improvements per review comments to SleighLanguage.
GP-2606
Added tests for the sleigh language volatility changes and thedecompiler itself.
GP-2606:
Updated SleighLanguage to alter 'volatileAddresses' based on the symbolelements and their attributes in pspec files. Added size and volatilevariables to AddressLabelInfo class. Added Boolean parser method to theSpecXmlUtils.java file.
GP-2606: Added optional volatile boolean and optional size integer attributes to the symbol element of processor specification (pspec) files.
This commit is contained in:
ghidraGander 2023-01-18 14:04:50 -05:00
parent 480f1e3a8b
commit dce57a7bf7
13 changed files with 801 additions and 102 deletions

View file

@ -31,6 +31,7 @@ import ghidra.framework.options.Options;
import ghidra.program.disassemble.Disassembler; import ghidra.program.disassemble.Disassembler;
import ghidra.program.model.address.*; import ghidra.program.model.address.*;
import ghidra.program.model.data.PointerDataType; import ghidra.program.model.data.PointerDataType;
import ghidra.program.model.lang.AddressLabelInfo;
import ghidra.program.model.listing.*; import ghidra.program.model.listing.*;
import ghidra.program.model.symbol.*; import ghidra.program.model.symbol.*;
import ghidra.program.model.util.*; import ghidra.program.model.util.*;

View file

@ -37,7 +37,6 @@ import ghidra.program.model.listing.*;
import ghidra.program.model.mem.InvalidAddressException; import ghidra.program.model.mem.InvalidAddressException;
import ghidra.program.model.mem.MemoryConflictException; import ghidra.program.model.mem.MemoryConflictException;
import ghidra.program.model.symbol.*; import ghidra.program.model.symbol.*;
import ghidra.program.model.util.AddressLabelInfo;
import ghidra.program.util.DefaultLanguageService; import ghidra.program.util.DefaultLanguageService;
import ghidra.program.util.GhidraProgramUtilities; import ghidra.program.util.GhidraProgramUtilities;
import ghidra.util.*; import ghidra.util.*;
@ -521,9 +520,7 @@ public abstract class AbstractProgramLoader implements Loader {
for (Register reg : lang.getRegisters()) { for (Register reg : lang.getRegisters()) {
Address addr = reg.getAddress(); Address addr = reg.getAddress();
if (addr.isMemoryAddress()) { if (addr.isMemoryAddress()) {
AddressLabelInfo info = new AddressLabelInfo(addr, reg.getName(), createSymbol(program, reg.getName(), addr, false, true, true);
reg.isBaseRegister(), SourceType.IMPORTED);
createSymbol(program, info, true);
} }
} }
// optionally create default symbols defined by pspec // optionally create default symbols defined by pspec
@ -531,7 +528,7 @@ public abstract class AbstractProgramLoader implements Loader {
boolean anchorSymbols = shouldAnchorSymbols(options); boolean anchorSymbols = shouldAnchorSymbols(options);
List<AddressLabelInfo> labels = lang.getDefaultSymbols(); List<AddressLabelInfo> labels = lang.getDefaultSymbols();
for (AddressLabelInfo info : labels) { for (AddressLabelInfo info : labels) {
createSymbol(program, info, anchorSymbols); createSymbol(program, info.getLabel(), info.getAddress(), info.isEntry(), info.isPrimary(), anchorSymbols);
} }
} }
GhidraProgramUtilities.removeAnalyzedFlag(program); GhidraProgramUtilities.removeAnalyzedFlag(program);
@ -541,42 +538,24 @@ public abstract class AbstractProgramLoader implements Loader {
} }
} }
private void createSymbol(Program program, AddressLabelInfo info, boolean anchorSymbols) { private static void createSymbol(Program program, String labelname, Address address, boolean isEntry, boolean isPrimary, boolean anchorSymbols) {
SymbolTable symTable = program.getSymbolTable(); SymbolTable symTable = program.getSymbolTable();
Address addr = info.getAddress(); Address addr = address;
Symbol s = symTable.getPrimarySymbol(addr); Symbol s = symTable.getPrimarySymbol(addr);
try { try {
if (s == null || s.getSource() == SourceType.IMPORTED) {
Namespace namespace = program.getGlobalNamespace(); Namespace namespace = program.getGlobalNamespace();
if (info.getScope() != null) { s = symTable.createLabel(addr, labelname, namespace, SourceType.IMPORTED);
namespace = info.getScope(); if (isEntry) {
}
s = symTable.createLabel(addr, info.getLabel(), namespace, info.getSource());
if (info.isEntry()) {
symTable.addExternalEntryPoint(addr); symTable.addExternalEntryPoint(addr);
} }
if (info.isPrimary()) { if (isPrimary) {
s.setPrimary(); s.setPrimary();
} }
if (anchorSymbols) { if (anchorSymbols) {
s.setPinned(true); s.setPinned(true);
} }
} }
else if (s.getSource() == SourceType.DEFAULT) { catch (InvalidInputException e) {
String labelName = info.getLabel();
if (s.getSymbolType() == SymbolType.FUNCTION) {
Function f = (Function) s.getObject();
f.setName(labelName, SourceType.IMPORTED);
}
else {
s.setName(labelName, SourceType.IMPORTED);
}
if (anchorSymbols) {
s.setPinned(true);
}
}
}
catch (DuplicateNameException | InvalidInputException e) {
// Nothing to do // Nothing to do
} }
} }

View file

@ -143,6 +143,22 @@ public class ProgramBuilder {
program.setTemporary(true); // ignore changes program.setTemporary(true); // ignore changes
} }
/**
* Construct program builder using a full language object rather than a language id string
* @param name program name
* @param language Language object
* @param compilerSpecID compiler specification ID (if null default spec will be used)
* @param consumer program consumer (if null this builder will be used as consumer and must be disposed to release program)
* @throws Exception if there is an exception creating the program
*/
public ProgramBuilder(String name, Language language)
throws Exception {
CompilerSpec compilerSpec = language.getDefaultCompilerSpec();
program = new ProgramDB(name, language, compilerSpec, this);
setAnalyzed(true);
program.setTemporary(true); // ignore changes
}
/** /**
* Perform complete analysis on the built program. * Perform complete analysis on the built program.
* Limited analysis may already have been performed during disassembly - so it may not * Limited analysis may already have been performed during disassembly - so it may not

View file

@ -26,12 +26,12 @@ import ghidra.framework.store.LockException;
import ghidra.program.database.ProgramDB; import ghidra.program.database.ProgramDB;
import ghidra.program.database.function.OverlappingFunctionException; import ghidra.program.database.function.OverlappingFunctionException;
import ghidra.program.model.address.*; import ghidra.program.model.address.*;
import ghidra.program.model.lang.AddressLabelInfo;
import ghidra.program.model.lang.Language; import ghidra.program.model.lang.Language;
import ghidra.program.model.listing.Function; import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.*; import ghidra.program.model.mem.*;
import ghidra.program.model.symbol.*; import ghidra.program.model.symbol.*;
import ghidra.program.model.util.AddressLabelInfo;
import ghidra.test.AbstractGhidraHeadlessIntegrationTest; import ghidra.test.AbstractGhidraHeadlessIntegrationTest;
import ghidra.util.exception.InvalidInputException; import ghidra.util.exception.InvalidInputException;
import ghidra.util.exception.NotFoundException; import ghidra.util.exception.NotFoundException;

View file

@ -0,0 +1,190 @@
/* ###
* 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.database;
import org.junit.Assert;
import org.junit.Test;
import ghidra.app.plugin.processors.sleigh.SleighLanguageVolatilityTest;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Data;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.MemoryBlock;
public class ProgramVolatilityTest extends SleighLanguageVolatilityTest {
private Program prog;
String PORTFAddressString = "mem:0x31";
String PORTGAddressString = "mem:0x34";
boolean isPORTFVolatile;
boolean isPORTFMemoryVolatile;
boolean isPORTGVolatile;
boolean isPORTGMemoryVolatile;
boolean isPORTFDataVolatile;
boolean isPORTGDataVolatile;
public void setUp(Boolean symbolVolatile, Integer symbolSize, Boolean memoryVolatile, boolean reverse) throws Exception {
super.setUp(symbolVolatile, symbolSize, memoryVolatile, reverse);
ProgramBuilder builder = new ProgramBuilder("test", lang);
builder.createMemory("PORTF", "mem:0x31", 2); //last parameter is block length
builder.createMemory("PORTG", "mem:0x34", 2);
prog = builder.getProgram();
Address PORTFAddress = prog.getAddressFactory().getAddress(PORTFAddressString);
Address PORTGAddress = prog.getAddressFactory().getAddress(PORTGAddressString);
MemoryBlock PORTFMemoryBlock = prog.getMemory().getBlock(PORTFAddress);
MemoryBlock PORTGMemoryBlock = prog.getMemory().getBlock(PORTGAddress);
Data PORTFData = prog.getListing().getDataAt(PORTGAddress);
Data PORTGData = prog.getListing().getDataAt(PORTGAddress);
isPORTFVolatile = lang.isVolatile(PORTFAddress);
isPORTFMemoryVolatile = PORTFMemoryBlock.isVolatile();
isPORTFDataVolatile = PORTFData.isVolatile();
isPORTGVolatile = lang.isVolatile(PORTGAddress);
isPORTGMemoryVolatile = PORTGMemoryBlock.isVolatile();
isPORTGDataVolatile = PORTGData.isVolatile();
}
@Test
public void testProgramPORTFDefined() throws Exception {
setUp(null, null, null, false);
Assert.assertFalse(isPORTFVolatile);
Assert.assertFalse(isPORTFMemoryVolatile);
Assert.assertFalse(isPORTFDataVolatile);
setUp(false, null, null, false);
Assert.assertFalse(isPORTFVolatile);
Assert.assertFalse(isPORTFMemoryVolatile);
Assert.assertFalse(isPORTFDataVolatile);
setUp(true, null, null, false);
Assert.assertTrue(isPORTFVolatile);
Assert.assertFalse(isPORTFMemoryVolatile);
Assert.assertFalse(isPORTFDataVolatile);
}
@Test
public void testProgramPORTFSizeDefined() throws Exception {
setUp(null, 1, null, false);
Assert.assertFalse(isPORTFVolatile);
Assert.assertFalse(isPORTFMemoryVolatile);
Assert.assertFalse(isPORTFDataVolatile);
Assert.assertFalse(isPORTGVolatile);
Assert.assertFalse(isPORTGMemoryVolatile);
Assert.assertFalse(isPORTGDataVolatile);
setUp(false, 1, null, false);
Assert.assertFalse(isPORTFVolatile);
Assert.assertFalse(isPORTFMemoryVolatile);
Assert.assertFalse(isPORTFDataVolatile);
Assert.assertFalse(isPORTGVolatile);
Assert.assertFalse(isPORTGMemoryVolatile);
Assert.assertFalse(isPORTGDataVolatile);
setUp(true, 1, null, false);
Assert.assertTrue(isPORTFVolatile);
Assert.assertFalse(isPORTFMemoryVolatile);
Assert.assertFalse(isPORTFDataVolatile);
Assert.assertFalse(isPORTGVolatile);
Assert.assertFalse(isPORTGMemoryVolatile);
Assert.assertFalse(isPORTGDataVolatile);
setUp(null, 4, null, false);
Assert.assertFalse(isPORTFVolatile);
Assert.assertFalse(isPORTFMemoryVolatile);
Assert.assertFalse(isPORTFDataVolatile);
Assert.assertFalse(isPORTGVolatile);
Assert.assertFalse(isPORTGMemoryVolatile);
Assert.assertFalse(isPORTGDataVolatile);
setUp(false, 4, null, false);
Assert.assertFalse(isPORTFVolatile);
Assert.assertFalse(isPORTFMemoryVolatile);
Assert.assertFalse(isPORTFDataVolatile);
Assert.assertFalse(isPORTGVolatile);
Assert.assertFalse(isPORTGMemoryVolatile);
Assert.assertFalse(isPORTGDataVolatile);
setUp(true, 4, null, false); // setting portf to size 4 overwrites portg as well
Assert.assertTrue(isPORTFVolatile);
Assert.assertFalse(isPORTFMemoryVolatile);
Assert.assertFalse(isPORTFDataVolatile);
Assert.assertTrue(isPORTGVolatile);
Assert.assertFalse(isPORTGMemoryVolatile);
Assert.assertFalse(isPORTGDataVolatile);
}
@Test
public void testProgramMemoryDefinedVolatile() throws Exception {
setUp(null, null, null, false);
Assert.assertFalse(isPORTFVolatile);
Assert.assertFalse(isPORTFMemoryVolatile);
Assert.assertFalse(isPORTFDataVolatile);
setUp(null, null, false, false);
Assert.assertFalse(isPORTFVolatile);
Assert.assertFalse(isPORTFMemoryVolatile);
Assert.assertFalse(isPORTFDataVolatile);
setUp(null, null, true, false);
Assert.assertTrue(isPORTFVolatile);
Assert.assertFalse(isPORTFMemoryVolatile);
Assert.assertFalse(isPORTFDataVolatile);
}
@Test
public void testProgramPORTFandMemoryDefined() throws Exception {
setUp(true, null, true, false);
Assert.assertTrue(isPORTFVolatile);
Assert.assertFalse(isPORTFMemoryVolatile);
Assert.assertFalse(isPORTFDataVolatile);
setUp(false, null, true, false);
Assert.assertFalse(isPORTFVolatile);
Assert.assertFalse(isPORTFMemoryVolatile);
Assert.assertFalse(isPORTFDataVolatile);
setUp(true, null, false, false);
Assert.assertTrue(isPORTFVolatile);
Assert.assertFalse(isPORTFMemoryVolatile);
Assert.assertFalse(isPORTFDataVolatile);
setUp(false, null, false, false);
Assert.assertFalse(isPORTFVolatile);
Assert.assertFalse(isPORTFMemoryVolatile);
Assert.assertFalse(isPORTFDataVolatile);
}
}

View file

@ -0,0 +1,171 @@
/* ###
* 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.core.decompile;
import org.junit.After;
import org.junit.Assert;
import org.junit.Test;
import ghidra.app.decompiler.DecompInterface;
import ghidra.app.decompiler.DecompileOptions;
import ghidra.app.decompiler.DecompileResults;
import ghidra.app.plugin.processors.sleigh.SleighLanguageVolatilityTest;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressFormatException;
import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.Program;
import ghidra.program.database.ProgramBuilder;
import ghidra.util.task.TaskMonitor;
public class DecompilerPspecVolatilityTest extends SleighLanguageVolatilityTest {
private Program prog;
private DecompInterface decompiler;
private String functionBytes = "84 ff 02 c0 8d 9a 01 c0 8d 98 85 ff 02 c0 a4 9a 01 c0 a4 98 2f "
+ "b7 86 ff 05 c0 f8 94 90 91 02 01 90 68 04 c0 f8 94 90 91 02 01 9f 77 90 93 02 01"
+ " 2f bf 87 ff 02 c0 a3 9a 01 c0 a3 98 8f 9a 85 e0 8a 95 f1 f7 00 00 8f 98 08";
private int functionLength = 27;
private String addressString = "0x1000";
private String decompilation;
private String decompilerPORTFNotVolatileString = "DAT_mem_0031 = DAT_mem_0031";
private String decompilerPORTGNotVolatileString = "DAT_mem_0034 = DAT_mem_0034";
private boolean decompilerPORTFVolatile;
private boolean decompilerPORTGVolatile;
public void setUp(Boolean symbolVolatile, Integer symbolSize, Boolean memoryVolatile, boolean reverse) throws Exception {
super.setUp(symbolVolatile, symbolSize, memoryVolatile, reverse);
ProgramBuilder builder = new ProgramBuilder("test", lang);
builder.setBytes(addressString, functionBytes);
builder.disassemble(addressString, functionLength, false);
builder.createFunction(addressString);
prog = builder.getProgram();
if (decompiler != null) {
decompiler.dispose();
}
decompiler = new DecompInterface();
decompiler.openProgram(prog);
decompilation = getDecompilationString(addressString);
decompilerPORTFVolatile = !decompilation.contains(decompilerPORTFNotVolatileString);
decompilerPORTGVolatile = !decompilation.contains(decompilerPORTGNotVolatileString);
}
private String getDecompilationString(String address) throws AddressFormatException
{
Address addr = prog.getAddressFactory().getDefaultAddressSpace().getAddress(address);
Function func = prog.getListing().getFunctionAt(addr);
DecompileResults decompResults = decompiler.decompileFunction(func,
DecompileOptions.SUGGESTED_DECOMPILE_TIMEOUT_SECS, TaskMonitor.DUMMY);
String decompilation = decompResults.getDecompiledFunction().getC();
return decompilation;
}
@After
public void tearDown() throws Exception {
if (decompiler != null) {
decompiler.dispose();
}
}
@Test
public void testDecompileInterfaceReturnsAFunction() throws Exception {
setUp(null, null, false, false);
Assert.assertNotNull(decompilation);
}
@Test
public void testDecompilePORTFSymbolPspecSettings() throws Exception {
setUp(null, null, null, false);
//Decompiler should indicate mem:0x31 is not volatile
Assert.assertFalse(decompilerPORTFVolatile);
setUp(false, null, null, false);
//Decompiler should indicate mem:0x31 is not volatile
Assert.assertFalse(decompilerPORTFVolatile);
setUp(true, null, null, false);
//Decompiler should indicate mem:0x31 is volatile because the symbol element in the language
//pspec file defined the symbol at mem:0x31 to be volatile.
Assert.assertTrue(decompilerPORTFVolatile);
}
@Test
public void testDecompilePORTFMemoryPspecSettings() throws Exception {
setUp(null, null, true, false);
//Decompiler should indicate mem:0x31 is volatile because the pspec file includes a volatile
//element that defines the memory location that includes 0x31 as volatile.
Assert.assertTrue(decompilerPORTFVolatile);
setUp(null, null, false, false);
//Decompiler should indicate mem:0x31 is not volatile
Assert.assertFalse(decompilerPORTFVolatile);
setUp(null, null, null, false);
//Decompiler should indicate mem:0x31 is not volatile
Assert.assertFalse(decompilerPORTFVolatile);
setUp(false, null, true, false);
//Decompiler should indicate mem:0x31 is not volatile because the pspec file defines the
//symbol element PORTF as not volatile and that takes precedence over the pspec's volatile
//element.
Assert.assertFalse(decompilerPORTFVolatile);
setUp(true, null, true, false);
//Decompiler should indicate mem:0x31 is volatile
Assert.assertTrue(decompilerPORTFVolatile);
setUp(false, null, true, true);
//Decompiler should indicate mem:0x31 is not volatile
Assert.assertFalse(decompilerPORTFVolatile);
}
@Test
public void testDecompilePORFSizeOverwritesPORTG() throws Exception {
setUp(true, 1, null, false);
//Decompiler should indicate mem:0x31 and mem:0x34 are volatile
Assert.assertTrue(decompilerPORTFVolatile);
Assert.assertFalse(decompilerPORTGVolatile);
setUp(false, 4, true, false); //size of 4 addressable units 0x31, 0x32, 0x33 0x34
//Decompiler should indicate mem:0x31 and mem:0x34 are not volatile
Assert.assertFalse(decompilerPORTFVolatile);
Assert.assertFalse(decompilerPORTGVolatile);
setUp(true, 4, null, false);
//Decompiler should indicate mem:0x31 and mem:0x34 are volatile
Assert.assertTrue(decompilerPORTFVolatile);
Assert.assertTrue(decompilerPORTGVolatile);
}
}

View file

@ -39,7 +39,7 @@ import org.xml.sax.*;
*/ */
public class SpecXmlUtils { public class SpecXmlUtils {
static public boolean decodeBoolean(String val) { static public Boolean decodeNullableBoolean(String val) {
if (val!=null && val.length()!=0) { if (val!=null && val.length()!=0) {
switch(val.charAt(0)) { switch(val.charAt(0)) {
case 'y': case 'y':
@ -53,7 +53,15 @@ public class SpecXmlUtils {
default: default:
} }
} }
return false; // Should we throw an exception for bad encodings? return null; // Should we throw an exception for bad encodings?
}
static public boolean decodeBoolean(String val) {
Boolean returnValue = decodeNullableBoolean(val);
if (returnValue != null) {
return returnValue;
}
return false;
} }
static public String encodeBoolean(boolean val) { static public String encodeBoolean(boolean val) {

View file

@ -128,6 +128,14 @@
<element name="symbol"> <element name="symbol">
<attribute name="name"/> <attribute name="name"/>
<attribute name="address"/> <attribute name="address"/>
<optional>
<attribute name="volatile">
<ref name="boolean_type"/>
</attribute>
</optional>
<optional>
<attribute name="size"/>
</optional>
<optional> <optional>
<attribute name="entry"> <attribute name="entry">
<ref name="boolean_type"/> <ref name="boolean_type"/>

View file

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

View file

@ -13,63 +13,54 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package ghidra.program.model.util; package ghidra.program.model.lang;
import ghidra.program.model.address.Address; 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 * <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> { public class AddressLabelInfo implements Comparable<AddressLabelInfo> {
private Address addr; private Address addr;
private Address endAddr;
private String label; private String label;
private boolean isPrimary; private boolean isPrimary;
private Namespace scope;
private SourceType symbolSource;
private boolean isEntry; private boolean isEntry;
private ProcessorSymbolType processorSymbolType; private ProcessorSymbolType processorSymbolType;
private int sizeInBytes;
private Boolean isVolatile;
/** /**
* Constructs a new AddressLabelInfo object * Constructor for class AddressLabelInfo
* @param s symbol to initialize info from. *
* @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) { public AddressLabelInfo(Address addr, Integer sizeInBytes, String label, boolean isPrimary,
this.addr = s.getAddress(); boolean isEntry, ProcessorSymbolType type, Boolean isVolatile) throws AddressOverflowException {
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) {
this.addr = addr; 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.label = label;
this.isPrimary = isPrimary; this.isPrimary = isPrimary;
this.scope = scope;
this.symbolSource = symbolSource;
this.isEntry = isEntry; this.isEntry = isEntry;
this.processorSymbolType = type; this.processorSymbolType = type;
} this.isVolatile = isVolatile;
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);
} }
/** /**
@ -79,6 +70,13 @@ public class AddressLabelInfo implements Comparable<AddressLabelInfo> {
return addr; return addr;
} }
/**
* Returns the object's end address.
*/
public final Address getEndAddress() {
return endAddr;
}
/** /**
* Returns the object's label or alias. * Returns the object's label or alias.
*/ */
@ -86,6 +84,14 @@ public class AddressLabelInfo implements Comparable<AddressLabelInfo> {
return label; 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. * Returns whether the object is the primary label at the address.
*/ */
@ -94,10 +100,13 @@ public class AddressLabelInfo implements Comparable<AddressLabelInfo> {
} }
/** /**
* 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() { public final Boolean isVolatile() {
return scope; return isVolatile;
} }
/** /**
@ -140,10 +149,6 @@ public class AddressLabelInfo implements Comparable<AddressLabelInfo> {
return thisLabel.compareTo(addrLabel); return thisLabel.compareTo(addrLabel);
} }
public SourceType getSource() {
return symbolSource;
}
public boolean isEntry() { public boolean isEntry() {
return 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.DefaultProgramContext;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.MemBuffer; import ghidra.program.model.mem.MemBuffer;
import ghidra.program.model.util.AddressLabelInfo;
import ghidra.util.ManualEntry; import ghidra.util.ManualEntry;
import ghidra.util.task.TaskMonitor; 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.lang.*;
import ghidra.program.model.listing.DefaultProgramContext; import ghidra.program.model.listing.DefaultProgramContext;
import ghidra.program.model.mem.MemBuffer; import ghidra.program.model.mem.MemBuffer;
import ghidra.program.model.util.AddressLabelInfo;
import ghidra.util.ManualEntry; import ghidra.util.ManualEntry;
import ghidra.util.XmlProgramUtilities; import ghidra.util.XmlProgramUtilities;
import ghidra.util.task.TaskMonitor; import ghidra.util.task.TaskMonitor;

View file

@ -0,0 +1,291 @@
/* ###
* 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.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
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.
* @return 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) throws IOException {
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;
}
}