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

@ -31,6 +31,7 @@ import ghidra.framework.options.Options;
import ghidra.program.disassemble.Disassembler;
import ghidra.program.model.address.*;
import ghidra.program.model.data.PointerDataType;
import ghidra.program.model.lang.AddressLabelInfo;
import ghidra.program.model.listing.*;
import ghidra.program.model.symbol.*;
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.MemoryConflictException;
import ghidra.program.model.symbol.*;
import ghidra.program.model.util.AddressLabelInfo;
import ghidra.program.util.DefaultLanguageService;
import ghidra.program.util.GhidraProgramUtilities;
import ghidra.util.*;
@ -519,9 +518,7 @@ public abstract class AbstractProgramLoader implements Loader {
for (Register reg : lang.getRegisters()) {
Address addr = reg.getAddress();
if (addr.isMemoryAddress()) {
AddressLabelInfo info = new AddressLabelInfo(addr, reg.getName(),
reg.isBaseRegister(), SourceType.IMPORTED);
createSymbol(program, info, true);
createSymbol(program, reg.getName(), addr, false, true, true);
}
}
// optionally create default symbols defined by pspec
@ -529,7 +526,7 @@ public abstract class AbstractProgramLoader implements Loader {
boolean anchorSymbols = shouldAnchorSymbols(options);
List<AddressLabelInfo> labels = lang.getDefaultSymbols();
for (AddressLabelInfo info : labels) {
createSymbol(program, info, anchorSymbols);
createSymbol(program, info.getLabel(), info.getAddress(), info.isEntry(), info.isPrimary(), anchorSymbols);
}
}
GhidraProgramUtilities.removeAnalyzedFlag(program);
@ -539,42 +536,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();
Address addr = info.getAddress();
Address addr = address;
Symbol s = symTable.getPrimarySymbol(addr);
try {
if (s == null || s.getSource() == SourceType.IMPORTED) {
Namespace namespace = program.getGlobalNamespace();
if (info.getScope() != null) {
namespace = info.getScope();
}
s = symTable.createLabel(addr, info.getLabel(), namespace, info.getSource());
if (info.isEntry()) {
s = symTable.createLabel(addr, labelname, namespace, SourceType.IMPORTED);
if (isEntry) {
symTable.addExternalEntryPoint(addr);
}
if (info.isPrimary()) {
if (isPrimary) {
s.setPrimary();
}
if (anchorSymbols) {
s.setPinned(true);
}
}
else if (s.getSource() == SourceType.DEFAULT) {
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) {
catch (InvalidInputException e) {
// Nothing to do
}
}

View file

@ -140,6 +140,22 @@ public class ProgramBuilder {
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.
* 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.function.OverlappingFunctionException;
import ghidra.program.model.address.*;
import ghidra.program.model.lang.AddressLabelInfo;
import ghidra.program.model.lang.Language;
import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.*;
import ghidra.program.model.symbol.*;
import ghidra.program.model.util.AddressLabelInfo;
import ghidra.test.AbstractGhidraHeadlessIntegrationTest;
import ghidra.util.exception.InvalidInputException;
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

@ -559,12 +559,16 @@ public class FillOutStructureCmd extends BackgroundCommand {
return currentProgram.getDataTypeManager().getUniqueName(new CategoryPath(category), base);
}
private boolean sanityCheck(long offset) {
private boolean sanityCheck(long offset, long existingSize) {
if (offset < 0) {
return false; // offsets shouldn't be negative
}
if (offset < existingSize) {
return true; // we have room in the structure
}
if (offset > 0x1000) {
return false; // Arbitrary size cut-off to prevent creating huge structures
return false; // bigger than existing size; arbitrary cut-off to prevent huge structures
}
return true;
}
@ -648,7 +652,7 @@ public class FillOutStructureCmd extends BackgroundCommand {
long value = getSigned(inputs[1]);
newOff = currentRef.offset +
((pcodeOp.getOpcode() == PcodeOp.INT_ADD) ? value : (-value));
if (sanityCheck(newOff)) { // should this offset create a location in the structure?
if (sanityCheck(newOff, componentMap.getSize())) { // should this offset create a location in the structure?
putOnList(output, newOff, todoList, doneList);
// Don't do componentMap.addDataType() as data-type info here is likely uninformed
componentMap.setMinimumSize(newOff);
@ -659,7 +663,7 @@ public class FillOutStructureCmd extends BackgroundCommand {
break;
}
newOff = currentRef.offset + getSigned(inputs[1]) * inputs[2].getOffset();
if (sanityCheck(newOff)) { // should this offset create a location in the structure?
if (sanityCheck(newOff, componentMap.getSize())) { // should this offset create a location in the structure?
putOnList(output, newOff, todoList, doneList);
// Don't do componentMap.addReference() as data-type info here is likely uninformed
componentMap.setMinimumSize(newOff);
@ -670,7 +674,7 @@ public class FillOutStructureCmd extends BackgroundCommand {
break;
}
long subOff = currentRef.offset + getSigned(inputs[1]);
if (sanityCheck(subOff)) { // should this offset create a location in the structure?
if (sanityCheck(subOff, componentMap.getSize())) { // should this offset create a location in the structure?
putOnList(output, subOff, todoList, doneList);
// Don't do componentMap.addReference() as data-type info here is likely uninformed
componentMap.setMinimumSize(subOff);

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 {
static public boolean decodeBoolean(String val) {
static public Boolean decodeNullableBoolean(String val) {
if (val!=null && val.length()!=0) {
switch(val.charAt(0)) {
case 'y':
@ -53,7 +53,15 @@ public class SpecXmlUtils {
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) {

View file

@ -128,6 +128,14 @@
<element name="symbol">
<attribute name="name"/>
<attribute name="address"/>
<optional>
<attribute name="volatile">
<ref name="boolean_type"/>
</attribute>
</optional>
<optional>
<attribute name="size"/>
</optional>
<optional>
<attribute name="entry">
<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.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,11 +395,8 @@ public class SleighLanguage implements Language {
@Override
public boolean isVolatile(Address addr) {
if (volatileAddresses != null) {
return volatileAddresses.contains(addr);
}
return false;
}
@Override
public InstructionPrototype parse(MemBuffer buf, ProcessorContext context, boolean inDelaySlot)
@ -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,6 +67,14 @@ public class SleighLanguageProvider implements LanguageProvider {
public static synchronized SleighLanguageProvider getSleighLanguageProvider() {
if (instance == null) {
instance = new SleighLanguageProvider();
}
return instance;
}
/**
* Construct sleigh language provider (singleton use)
*/
private SleighLanguageProvider() {
try {
instance.createLanguages();
}
@ -75,13 +83,15 @@ public class SleighLanguageProvider implements LanguageProvider {
"Sleigh language provider initiailization failed", e);
}
}
return instance;
}
/**
* Construct sleigh language provider (singleton)
* 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
*/
private SleighLanguageProvider() {
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;
}
/**
@ -79,6 +70,13 @@ public class AddressLabelInfo implements Comparable<AddressLabelInfo> {
return addr;
}
/**
* Returns the object's end address.
*/
public final Address getEndAddress() {
return endAddr;
}
/**
* Returns the object's label or alias.
*/
@ -86,6 +84,14 @@ 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.
*/
@ -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() {
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;
}
}