mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-05-10 05:58:11 +08:00
Merge remote-tracking branch 'origin/GP-1426_Dan_asmWoW64--SQUASHED'
This commit is contained in:
+6
-7
@@ -38,6 +38,7 @@ import ghidra.program.model.lang.*;
|
||||
import ghidra.program.model.listing.Instruction;
|
||||
import ghidra.test.AbstractGhidraHeadlessIntegrationTest;
|
||||
import ghidra.trace.database.ToyDBTraceBuilder;
|
||||
import ghidra.trace.database.context.DBTraceRegisterContextManager;
|
||||
import ghidra.trace.model.memory.TraceMemoryFlag;
|
||||
import ghidra.trace.model.memory.TraceMemoryManager;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
@@ -864,17 +865,13 @@ public class TracePcodeEmulatorTest extends AbstractGhidraHeadlessIntegrationTes
|
||||
try (ToyDBTraceBuilder tb = new ToyDBTraceBuilder("Test", "x86:LE:64:default")) {
|
||||
Language lang = tb.trace.getBaseLanguage();
|
||||
Register ctxReg = lang.getContextBaseRegister();
|
||||
Register opsizeReg = lang.getRegister("opsize");
|
||||
Register addrsizeReg = lang.getRegister("addrsize");
|
||||
Register longModeReg = lang.getRegister("longMode");
|
||||
RegisterValue ctxVal = new RegisterValue(ctxReg)
|
||||
.assign(opsizeReg, BigInteger.ONE)
|
||||
.assign(addrsizeReg, BigInteger.ONE)
|
||||
.assign(longModeReg, BigInteger.ZERO);
|
||||
DBTraceRegisterContextManager ctxManager = tb.trace.getRegisterContextManager();
|
||||
try (UndoableTransaction tid = tb.startTransaction()) {
|
||||
tb.trace.getRegisterContextManager()
|
||||
.setValue(lang, ctxVal, Range.atLeast(0L),
|
||||
tb.range(0x00400000, 0x00400002));
|
||||
ctxManager.setValue(lang, ctxVal, Range.atLeast(0L),
|
||||
tb.range(0x00400000, 0x00400002));
|
||||
}
|
||||
TraceThread thread = initTrace(tb,
|
||||
List.of(
|
||||
@@ -891,6 +888,8 @@ public class TracePcodeEmulatorTest extends AbstractGhidraHeadlessIntegrationTes
|
||||
|
||||
TracePcodeEmulator emu = new TracePcodeEmulator(tb.trace, 0);
|
||||
PcodeThread<byte[]> emuThread = emu.newThread(thread.getPath());
|
||||
// TODO: Seems the Trace-bound thread ought to know to do this in reInitialize()
|
||||
ctxVal = ctxManager.getValueWithDefault(lang, ctxReg, 0, tb.addr(0x00400000));
|
||||
emuThread.overrideContext(ctxVal);
|
||||
emuThread.stepInstruction();
|
||||
emuThread.stepInstruction();
|
||||
|
||||
@@ -63,7 +63,7 @@ public class AssemblyThrasherDevScript extends GhidraScript {
|
||||
}
|
||||
|
||||
@Override
|
||||
public AssemblyResolvedConstructor select(AssemblyResolutionResults rr,
|
||||
public AssemblyResolvedPatterns select(AssemblyResolutionResults rr,
|
||||
AssemblyPatternBlock ctx) throws AssemblySemanticException {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
boolean gotOne = false;
|
||||
@@ -72,7 +72,7 @@ public class AssemblyThrasherDevScript extends GhidraScript {
|
||||
if (ar.isError()) {
|
||||
continue;
|
||||
}
|
||||
AssemblyResolvedConstructor can = (AssemblyResolvedConstructor) ar;
|
||||
AssemblyResolvedPatterns can = (AssemblyResolvedPatterns) ar;
|
||||
if (can.getContext().combine(ctx) == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
+2
-2
@@ -704,7 +704,7 @@ public class AssemblyDualTextField {
|
||||
* @param existing the instruction, if any, currently under the user's cursor
|
||||
* @return a preference
|
||||
*/
|
||||
protected int computePreference(AssemblyResolvedConstructor rc, Instruction existing) {
|
||||
protected int computePreference(AssemblyResolvedPatterns rc, Instruction existing) {
|
||||
if (existing == null) {
|
||||
return 0;
|
||||
}
|
||||
@@ -763,7 +763,7 @@ public class AssemblyDualTextField {
|
||||
//result.add(new AssemblyError("", ar.toString()));
|
||||
continue;
|
||||
}
|
||||
AssemblyResolvedConstructor rc = (AssemblyResolvedConstructor) ar;
|
||||
AssemblyResolvedPatterns rc = (AssemblyResolvedPatterns) ar;
|
||||
for (byte[] ins : rc.possibleInsVals(ctx)) {
|
||||
result.add(new AssemblyInstruction(text, Arrays.copyOf(ins, ins.length),
|
||||
computePreference(rc, existing)));
|
||||
|
||||
+7
-6
@@ -42,7 +42,8 @@ public interface Assembler {
|
||||
* refer to pseudo instructions.
|
||||
*
|
||||
* <p>
|
||||
* NOTE: There must be an active transaction on the bound program for this method to succeed.
|
||||
* <b>NOTE:</b> There must be an active transaction on the bound program for this method to
|
||||
* succeed.
|
||||
*
|
||||
* @param at the location where the resulting instructions should be placed
|
||||
* @param listing a new-line separated or array sequence of instructions
|
||||
@@ -119,8 +120,8 @@ public interface Assembler {
|
||||
* results.
|
||||
*
|
||||
* <p>
|
||||
* NOTE: The resolved instructions are given as masks and values. Where the mask does not cover,
|
||||
* you can choose any value.
|
||||
* <b>NOTE:</b> The resolved instructions are given as masks and values. Where the mask does not
|
||||
* cover, you can choose any value.
|
||||
*
|
||||
* @param parse a parse result giving a valid tree
|
||||
* @param at the location of the start of the instruction
|
||||
@@ -139,8 +140,8 @@ public interface Assembler {
|
||||
* results.
|
||||
*
|
||||
* <p>
|
||||
* NOTE: The resolved instructions are given as masks and values. Where the mask does not cover,
|
||||
* you can choose any value.
|
||||
* <b>NOTE:</b> The resolved instructions are given as masks and values. Where the mask does not
|
||||
* cover, you can choose any value.
|
||||
*
|
||||
* @param parse a parse result giving a valid tree
|
||||
* @param at the location of the start of the instruction
|
||||
@@ -192,7 +193,7 @@ public interface Assembler {
|
||||
* @return the new {@link Instruction} code unit
|
||||
* @throws MemoryAccessException there is an issue writing the result to program memory
|
||||
*/
|
||||
public Instruction patchProgram(AssemblyResolvedConstructor res, Address at)
|
||||
public Instruction patchProgram(AssemblyResolvedPatterns res, Address at)
|
||||
throws MemoryAccessException;
|
||||
|
||||
/**
|
||||
|
||||
+4
@@ -25,18 +25,21 @@ import ghidra.program.model.listing.Program;
|
||||
public interface AssemblerBuilder {
|
||||
/**
|
||||
* Get the ID of the language for which this instance builds an assembler
|
||||
*
|
||||
* @return the language ID
|
||||
*/
|
||||
public LanguageID getLanguageID();
|
||||
|
||||
/**
|
||||
* Get the language for which this instance builds an assembler
|
||||
*
|
||||
* @return the language
|
||||
*/
|
||||
public Language getLanguage();
|
||||
|
||||
/**
|
||||
* Build an assembler with the given selector callback
|
||||
*
|
||||
* @param selector the selector callback
|
||||
* @return the built assembler
|
||||
*/
|
||||
@@ -44,6 +47,7 @@ public interface AssemblerBuilder {
|
||||
|
||||
/**
|
||||
* Build an assembler with the given selector callback and program binding
|
||||
*
|
||||
* @param selector the selector callback
|
||||
* @param program the bound program
|
||||
* @return the built assembler
|
||||
|
||||
+35
-28
@@ -19,19 +19,20 @@ import java.util.*;
|
||||
|
||||
import ghidra.app.plugin.assembler.sleigh.parse.AssemblyParseResult;
|
||||
import ghidra.app.plugin.assembler.sleigh.sem.*;
|
||||
import ghidra.app.plugin.assembler.sleigh.util.SleighUtil;
|
||||
import ghidra.app.plugin.assembler.sleigh.util.AsmUtil;
|
||||
|
||||
/**
|
||||
* Provides a mechanism for pruning and selecting binary assembled instructions from the results
|
||||
* of parsing textual assembly instructions. There are two opportunities: After parsing, but before
|
||||
* semantic resolution, and after resolution. In the first opportunity, filtering is optional ---
|
||||
* the user may discard any or all parse trees. The second is required, since only one instruction
|
||||
* may be placed at the desired address --- the user must select one instruction among the many
|
||||
* results, and if a mask is present, decide on a value for the omitted bits.
|
||||
* Provides a mechanism for pruning and selecting binary assembled instructions from the results of
|
||||
* parsing textual assembly instructions. There are two opportunities: After parsing, but before
|
||||
* prototype generation, and after machine code generation. In the first opportunity, filtering is
|
||||
* optional --- the user may discard any or all parse trees. The second is required, since only one
|
||||
* instruction may be placed at the desired address --- the user must select one instruction among
|
||||
* the many results, and if a mask is present, decide on a value for the omitted bits.
|
||||
*
|
||||
* <p>
|
||||
* Extensions of this class are also suitable for collecting diagnostic information about attempted
|
||||
* assemblies. For example, an implementation may employ the syntax errors in order to produce
|
||||
* code completion suggestions in a GUI.
|
||||
* assemblies. For example, an implementation may employ the syntax errors in order to produce code
|
||||
* completion suggestions in a GUI.
|
||||
*/
|
||||
public class AssemblySelector {
|
||||
protected Set<AssemblyParseResult> syntaxErrors = new TreeSet<>();
|
||||
@@ -40,7 +41,7 @@ public class AssemblySelector {
|
||||
/**
|
||||
* A comparator on instruction length (shortest first), then bits lexicographically
|
||||
*/
|
||||
protected Comparator<AssemblyResolvedConstructor> compareBySizeThenBits = (a, b) -> {
|
||||
protected Comparator<AssemblyResolvedPatterns> compareBySizeThenBits = (a, b) -> {
|
||||
int result;
|
||||
result = a.getInstructionLength() - b.getInstructionLength();
|
||||
if (result != 0) {
|
||||
@@ -48,7 +49,7 @@ public class AssemblySelector {
|
||||
}
|
||||
|
||||
result =
|
||||
SleighUtil.compareArrays(a.getInstruction().getVals(), b.getInstruction().getVals());
|
||||
AsmUtil.compareArrays(a.getInstruction().getVals(), b.getInstruction().getVals());
|
||||
if (result != 0) {
|
||||
return result;
|
||||
}
|
||||
@@ -58,16 +59,20 @@ public class AssemblySelector {
|
||||
/**
|
||||
* Filter a collection of parse trees.
|
||||
*
|
||||
* Generally, the assembly resolver considers every possible parsing of an assembly
|
||||
* instruction. If, for some reason, the user wishes to ignore certain trees (perhaps for
|
||||
* efficiency, or perhaps because a certain form of instruction is desired), entire parse
|
||||
* trees may be pruned here.
|
||||
* <p>
|
||||
* Generally, the assembly resolver considers every possible parsing of an assembly instruction.
|
||||
* If, for some reason, the user wishes to ignore certain trees (perhaps for efficiency, or
|
||||
* perhaps because a certain form of instruction is desired), entire parse trees may be pruned
|
||||
* here.
|
||||
*
|
||||
* It's possible that no trees pass the filter. In this case, this method ought to throw an
|
||||
* {@link AssemblySyntaxException}. Another option is to pass the erroneous result on for semantic
|
||||
* analysis, in which case, the error is simply copied into an erroneous semantic result.
|
||||
* Depending on preferences, this may simplify the overall filtering and error-handling logic.
|
||||
* <p>
|
||||
* It is possible that no trees pass the filter. In this case, this method ought to throw an
|
||||
* {@link AssemblySyntaxException}. Another option is to pass the erroneous result on for
|
||||
* semantic analysis, in which case, the error is simply copied into an erroneous semantic
|
||||
* result. Depending on preferences, this may simplify the overall filtering and error-handling
|
||||
* logic.
|
||||
*
|
||||
* <p>
|
||||
* By default, no filtering is applied. If all the trees produce syntax errors, an exception is
|
||||
* thrown.
|
||||
*
|
||||
@@ -95,10 +100,12 @@ public class AssemblySelector {
|
||||
/**
|
||||
* Select an instruction from the possible results.
|
||||
*
|
||||
* Must select precisely one resolved constructor from the results given back by the assembly
|
||||
* resolver. Precisely one. That means the mask of the returned result must consist of all 1s.
|
||||
* Also, if no selection is suitable, an exception must be thrown.
|
||||
* <p>
|
||||
* This must select precisely one resolved constructor from the results given back by the
|
||||
* assembly resolver. This further implies the mask of the returned result must consist of all
|
||||
* 1s. If no selection is suitable, this must throw an exception.
|
||||
*
|
||||
* <p>
|
||||
* By default, this method selects the shortest instruction that is compatible with the given
|
||||
* context and takes 0 for bits that fall outside the mask. If all possible resolutions produce
|
||||
* errors, an exception is thrown.
|
||||
@@ -106,18 +113,18 @@ public class AssemblySelector {
|
||||
* @param rr the collection of resolved constructors
|
||||
* @param ctx the applicable context.
|
||||
* @return a single resolved constructor with a full instruction mask.
|
||||
* @throws AssemblySemanticException
|
||||
* @throws AssemblySemanticException
|
||||
*/
|
||||
public AssemblyResolvedConstructor select(AssemblyResolutionResults rr,
|
||||
public AssemblyResolvedPatterns select(AssemblyResolutionResults rr,
|
||||
AssemblyPatternBlock ctx) throws AssemblySemanticException {
|
||||
List<AssemblyResolvedConstructor> sorted = new ArrayList<>();
|
||||
List<AssemblyResolvedPatterns> sorted = new ArrayList<>();
|
||||
// Select only non-erroneous results whose contexts are compatible.
|
||||
for (AssemblyResolution ar : rr) {
|
||||
if (ar.isError()) {
|
||||
semanticErrors.add((AssemblyResolvedError) ar);
|
||||
continue;
|
||||
}
|
||||
AssemblyResolvedConstructor rc = (AssemblyResolvedConstructor) ar;
|
||||
AssemblyResolvedPatterns rc = (AssemblyResolvedPatterns) ar;
|
||||
sorted.add(rc);
|
||||
}
|
||||
if (sorted.isEmpty()) {
|
||||
@@ -127,9 +134,9 @@ public class AssemblySelector {
|
||||
sorted.sort(compareBySizeThenBits);
|
||||
|
||||
// Pick just the first
|
||||
AssemblyResolvedConstructor res = sorted.get(0);
|
||||
AssemblyResolvedPatterns res = sorted.get(0);
|
||||
// Just set the mask to ffs (effectively choosing 0 for the omitted bits)
|
||||
return AssemblyResolution.resolved(res.getInstruction().fillMask(), res.getContext(),
|
||||
"Selected", null);
|
||||
"Selected", null, null, null);
|
||||
}
|
||||
}
|
||||
|
||||
+3
@@ -26,6 +26,7 @@ import ghidra.app.plugin.assembler.sleigh.sem.AssemblyResolvedError;
|
||||
/**
|
||||
* Thrown when all resolutions of an assembly instruction result in semantic errors.
|
||||
*
|
||||
* <p>
|
||||
* For SLEIGH, semantic errors amount to incompatible contexts
|
||||
*/
|
||||
public class AssemblySemanticException extends AssemblyException {
|
||||
@@ -37,6 +38,7 @@ public class AssemblySemanticException extends AssemblyException {
|
||||
|
||||
/**
|
||||
* Construct a semantic exception with the associated semantic errors
|
||||
*
|
||||
* @param errors the associated semantic errors
|
||||
*/
|
||||
public AssemblySemanticException(Set<AssemblyResolvedError> errors) {
|
||||
@@ -46,6 +48,7 @@ public class AssemblySemanticException extends AssemblyException {
|
||||
|
||||
/**
|
||||
* Get the collection of associated semantic errors
|
||||
*
|
||||
* @return the collection
|
||||
*/
|
||||
public Collection<AssemblyResolvedError> getErrors() {
|
||||
|
||||
+2
@@ -35,6 +35,7 @@ public class AssemblySyntaxException extends AssemblyException {
|
||||
|
||||
/**
|
||||
* Construct a syntax exception with the associated syntax errors
|
||||
*
|
||||
* @param errors the associated syntax errors
|
||||
*/
|
||||
public AssemblySyntaxException(Set<AssemblyParseResult> errors) {
|
||||
@@ -44,6 +45,7 @@ public class AssemblySyntaxException extends AssemblyException {
|
||||
|
||||
/**
|
||||
* Get the collection of associated syntax errors
|
||||
*
|
||||
* @return the collection
|
||||
*/
|
||||
public Collection<AssemblyParseResult> getErrors() {
|
||||
|
||||
+16
-37
@@ -17,11 +17,12 @@ package ghidra.app.plugin.assembler.sleigh;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
import java.util.Collection;
|
||||
|
||||
import ghidra.app.plugin.assembler.*;
|
||||
import ghidra.app.plugin.assembler.sleigh.parse.*;
|
||||
import ghidra.app.plugin.assembler.sleigh.sem.*;
|
||||
import ghidra.app.plugin.assembler.sleigh.symbol.AssemblyNumericSymbols;
|
||||
import ghidra.app.plugin.assembler.sleigh.util.DbgTimer;
|
||||
import ghidra.app.plugin.processors.sleigh.SleighLanguage;
|
||||
import ghidra.program.disassemble.Disassembler;
|
||||
@@ -32,17 +33,16 @@ import ghidra.program.model.lang.RegisterValue;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.program.model.mem.Memory;
|
||||
import ghidra.program.model.mem.MemoryAccessException;
|
||||
import ghidra.program.model.symbol.*;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
/**
|
||||
* An {@link Assembler} for a {@link SleighLanguage}.
|
||||
*
|
||||
* To obtain one of these, please use {@link SleighAssemblerBuilder}, or better yet, the static
|
||||
* methods of {@link Assemblers}.
|
||||
* <p>
|
||||
* For documentation on how the SLEIGH assembler works, see {@link SleighAssemblerBuilder}. To use
|
||||
* the assembler, please use {@link Assemblers#getAssembler(Program)} or similar.
|
||||
*/
|
||||
public class SleighAssembler implements Assembler {
|
||||
public static final int DEFAULT_MAX_RECURSION_DEPTH = 2; // TODO: Toss this
|
||||
protected static final DbgTimer dbg = DbgTimer.INACTIVE;
|
||||
|
||||
protected AssemblySelector selector;
|
||||
@@ -75,7 +75,8 @@ public class SleighAssembler implements Assembler {
|
||||
/**
|
||||
* Construct a SleighAssembler.
|
||||
*
|
||||
* NOTE: This variant does not permit {@link #assemble(Address, String...)}.
|
||||
* <p>
|
||||
* <b>NOTE:</b> This variant does not permit {@link #assemble(Address, String...)}.
|
||||
*
|
||||
* @param selector a method of selecting one result from many
|
||||
* @param lang the SLEIGH language (must be same as to create the parser)
|
||||
@@ -93,7 +94,7 @@ public class SleighAssembler implements Assembler {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Instruction patchProgram(AssemblyResolvedConstructor res, Address at)
|
||||
public Instruction patchProgram(AssemblyResolvedPatterns res, Address at)
|
||||
throws MemoryAccessException {
|
||||
if (!res.getInstruction().isFullMask()) {
|
||||
throw new AssemblySelectionError("Selected instruction must have a full mask.");
|
||||
@@ -157,7 +158,7 @@ public class SleighAssembler implements Assembler {
|
||||
|
||||
@Override
|
||||
public Collection<AssemblyParseResult> parseLine(String line) {
|
||||
return parser.parse(line, getProgramLabels());
|
||||
return parser.parse(line, getNumericSymbols());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -173,13 +174,13 @@ public class SleighAssembler implements Assembler {
|
||||
if (parse.isError()) {
|
||||
AssemblyResolutionResults results = new AssemblyResolutionResults();
|
||||
AssemblyParseErrorResult err = (AssemblyParseErrorResult) parse;
|
||||
results.add(AssemblyResolution.error(err.describeError(), "Parsing", null));
|
||||
results.add(AssemblyResolution.error(err.describeError(), "Parsing"));
|
||||
return results;
|
||||
}
|
||||
|
||||
AssemblyParseAcceptResult acc = (AssemblyParseAcceptResult) parse;
|
||||
AssemblyTreeResolver tr =
|
||||
new AssemblyTreeResolver(lang, at.getOffset(), acc.getTree(), ctx, ctxGraph);
|
||||
new AssemblyTreeResolver(lang, at, acc.getTree(), ctx, ctxGraph);
|
||||
return tr.resolve();
|
||||
}
|
||||
|
||||
@@ -219,7 +220,7 @@ public class SleighAssembler implements Assembler {
|
||||
public byte[] assembleLine(Address at, String line, AssemblyPatternBlock ctx)
|
||||
throws AssemblySemanticException, AssemblySyntaxException {
|
||||
AssemblyResolutionResults results = resolveLine(at, line, ctx);
|
||||
AssemblyResolvedConstructor res = selector.select(results, ctx);
|
||||
AssemblyResolvedPatterns res = selector.select(results, ctx);
|
||||
if (res == null) {
|
||||
throw new AssemblySelectionError(
|
||||
"Must select exactly one instruction. Report errors via AssemblySemanticError");
|
||||
@@ -234,37 +235,15 @@ public class SleighAssembler implements Assembler {
|
||||
}
|
||||
|
||||
/**
|
||||
* A convenience to obtain a map of program labels strings to long values
|
||||
* A convenience to obtain assembly symbols
|
||||
*
|
||||
* @return the map
|
||||
*
|
||||
* {@literal TODO Use a Map<String, Address> instead so that, if possible, symbol values can be checked}
|
||||
* lest they be an invalid substitution for a given operand.
|
||||
*/
|
||||
protected Map<String, Long> getProgramLabels() {
|
||||
Map<String, Long> labels = new HashMap<>();
|
||||
for (Register reg : lang.getRegisters()) {
|
||||
// TODO/HACK: There ought to be a better mechanism describing suitable symbolic
|
||||
// substitutions for a given operand.
|
||||
if (!"register".equals(reg.getAddressSpace().getName())) {
|
||||
labels.put(reg.getName(), (long) reg.getOffset());
|
||||
}
|
||||
}
|
||||
protected AssemblyNumericSymbols getNumericSymbols() {
|
||||
if (program != null) {
|
||||
final SymbolIterator it = program.getSymbolTable().getAllSymbols(false);
|
||||
while (it.hasNext()) {
|
||||
Symbol sym = it.next();
|
||||
if (sym.isExternal()) {
|
||||
continue; // skip externals - will generally be referenced indirectly not directly
|
||||
}
|
||||
SymbolType symbolType = sym.getSymbolType();
|
||||
if (symbolType != SymbolType.LABEL && symbolType != SymbolType.FUNCTION) {
|
||||
continue;
|
||||
}
|
||||
labels.put(sym.getName(), sym.getAddress().getOffset());
|
||||
}
|
||||
return AssemblyNumericSymbols.fromProgram(program);
|
||||
}
|
||||
return labels;
|
||||
return AssemblyNumericSymbols.fromLanguage(lang);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+275
-82
@@ -24,8 +24,7 @@ import ghidra.app.plugin.assembler.*;
|
||||
import ghidra.app.plugin.assembler.sleigh.grammars.AssemblyGrammar;
|
||||
import ghidra.app.plugin.assembler.sleigh.grammars.AssemblySentential;
|
||||
import ghidra.app.plugin.assembler.sleigh.parse.AssemblyParser;
|
||||
import ghidra.app.plugin.assembler.sleigh.sem.AssemblyContextGraph;
|
||||
import ghidra.app.plugin.assembler.sleigh.sem.AssemblyDefaultContext;
|
||||
import ghidra.app.plugin.assembler.sleigh.sem.*;
|
||||
import ghidra.app.plugin.assembler.sleigh.symbol.*;
|
||||
import ghidra.app.plugin.assembler.sleigh.util.DbgTimer;
|
||||
import ghidra.app.plugin.assembler.sleigh.util.DbgTimer.DbgCtx;
|
||||
@@ -43,57 +42,267 @@ import ghidra.util.SystemUtilities;
|
||||
/**
|
||||
* An {@link AssemblerBuilder} capable of supporting almost any {@link SleighLanguage}
|
||||
*
|
||||
* <p>
|
||||
* To build an assembler, please use a static method of the {@link Assemblers} class.
|
||||
*
|
||||
* SLEIGH-based assembly is a bit of an experimental feature at this time. Nevertheless, it seems to
|
||||
* have come along quite nicely. It's not quite as fast as disassembly, since after all, that's what
|
||||
* SLEIGH was designed to do.
|
||||
* <p>
|
||||
* SLEIGH-based assembly is a bit temperamental, since it essentially runs the disassembler
|
||||
* backwards. The process is tenuous, but works well enough for interactive single-instruction
|
||||
* assembly. It is not nearly as fast as disassembly, since after all, SLEIGH was not designed for
|
||||
* assembly. The assembler is great for interactive patching and for building small samples in unit
|
||||
* tests. For other cases, a real tool chain is likely more appropriate.
|
||||
*
|
||||
* Overall, the method is fairly simple, though its implementation is a bit more complex. First, we
|
||||
* gather every pair of pattern and constructor by traversing the decision tree used by disassembly.
|
||||
* We then use the "print pieces" to construct a context-free grammar. Each production is associated
|
||||
* with the one-or-more constructors with the same sequence of print pieces. We then build a LALR(1)
|
||||
* parser for the generated grammar. This now constitutes a generic parser for the given language.
|
||||
* Note that this step takes some time, and may be better suited as a build-time step. Because
|
||||
* SLEIGH specifications are not generally concerned with eliminating ambiguity of printed
|
||||
* instructions (rather, it only does so for instruction bytes), we must consider that the grammar
|
||||
* could be ambiguous. To handle this, the action/goto table is permitted multiple entries per cell,
|
||||
* and we allow backtracking. There are also cases where tokens are not actually separated by
|
||||
* spaces. For example, in the {@code ia.sinc} file, there is JMP ... and J^cc, meaning, the lexer
|
||||
* must consider J as a token as well as JMP, introducing another source of possible backtracking.
|
||||
* Despite that, parsing is completed fairly quickly.
|
||||
* <h2>A Review of Disassembly</h2>
|
||||
*
|
||||
* To assemble, we first parse the textual instruction, yielding zero or more parse trees. No parse
|
||||
* trees implies an error. For each parse tree, we attempt to resolve the instruction bytes,
|
||||
* starting at the leaves and working upwards while tracking and solving context changes. The
|
||||
* context changes must be considered in reverse. We <em>read</em> the context register of the
|
||||
* children (a disassembler would write). We then assume there is at most one variable in the
|
||||
* expression, solve for it, and <em>write</em> the solution to the appropriate field (a
|
||||
* disassembler would read). If no solution exists, a semantic error is logged. Since it's possible
|
||||
* a production in the parse tree is associated with multiple constructors, different combinations
|
||||
* of constructors are explored as we move upward in the tree. If all possible combinations yield
|
||||
* semantic errors, then the overall result is an error.
|
||||
* <p>
|
||||
* Before diving into assembly, it may be helpful to review SLEIGH and disassembly, at least as far
|
||||
* as I understand. SLEIGH is really a specification of three distinct things, all related by trees
|
||||
* of "constructors." 1) A mnemonic grammar, 2) A machine-code grammar, 3) Run-time semantics, i.e.,
|
||||
* p-code. The third is consumed primarily by the decompiler, the emulator, and other analysis, and
|
||||
* is of little concern to the (dis)assembler. All three are tightly bound. A single constructor
|
||||
* specifies a production in both grammars, constraints for selecting the production, as well as the
|
||||
* generated run-time semantics. Consider an example:
|
||||
*
|
||||
* Some productions are "purely recursive," e.g., {@code :^instruction} lines in the SLEIGH. These
|
||||
* are ignored during parser construction. Let such a production be given as I => I. When resolving
|
||||
* the parse tree to bytes, and we encounter a production with I on the left hand side, we then
|
||||
* consider the possible application of the production I => I and its consequential constructors.
|
||||
* Ideally, we could repeat this indefinitely, stopping when all further applications result in
|
||||
* semantic errors; however, there is no guarantee in the SLEIGH specification that such an
|
||||
* algorithm will actually halt, so a maximum number (default of 1) of applications are attempted.
|
||||
* <pre>
|
||||
* :ADD regD,imm8 is op=5 & regD & imm8 { regD = regD + imm8; }
|
||||
* </pre>
|
||||
*
|
||||
* After all the context changes and operands are resolved, we apply the constructor patterns and
|
||||
* proceed up the tree. Thus, each branch yields zero or more "resolved constructors," which each
|
||||
* specify two masked blocks of data: one for the instruction, and one for the context. These are
|
||||
* passed up to the parent production, which, having obtained results from all its children,
|
||||
* attempts to apply the corresponding constructors.
|
||||
* <p>
|
||||
* The colon indicates this constructor applies to the root "instruction" table. The mnemonic
|
||||
* production precedes the <code>is</code> keyword. The machine-code constraints and production
|
||||
* follow. Finally, the semantics appear within braces.
|
||||
*
|
||||
* Once we've resolved the root node, any resolved constructors returned are taken as successfully
|
||||
* assembled instruction bytes. If applicable, the corresponding context registers are compared to
|
||||
* the context at the target address in the program and filtered for compatibility.
|
||||
* <p>
|
||||
* To support bitfield parsing, a list of token formats and fields within must be declared. The
|
||||
* machine-code production may specify constraints in terms of those fields. Such constraints become
|
||||
* patterns that the parser uses to choose a constructor. For example, we may have
|
||||
* <code>op=(0,3);regD=(4,7);imm8=(8,15)</code>. In little endian, this would indicate a 2-byte
|
||||
* token:
|
||||
*
|
||||
* <pre>
|
||||
* +-4----+-4----+-8----------+
|
||||
* | regD | op | imm8 |
|
||||
* +------+------+------------+
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* Thus, this constructor is assigned the pattern <code>0101....</code>, which handles
|
||||
* <code>op=5</code>. <code>regD</code> and <code>imm8</code> remain as operands. The operands of
|
||||
* the machine-code production refer to fields and subtables. During disassembly, those operands are
|
||||
* parsed in the order named: left to right, depth first. For the (root) instruction table and each
|
||||
* subtable, the disassembler selects exactly one constructor. The parser may only examine one
|
||||
* machine-code token at a time; however, the token can be large (32 bits is common), and it may
|
||||
* make several sub-table decisions based on fields within a single token, essentially allowing it
|
||||
* to look ahead and parse those fields out of order. In the example, the parser will technically
|
||||
* examine the <code>op</code> field before parsing <code>regD</code>.
|
||||
*
|
||||
* <p>
|
||||
* When parsing a table or subtable, if no constructor's constraints can be matched, parsing fails.
|
||||
* Each token is some number of bytes in size. The parser advances to the next token when it
|
||||
* encounters a semicolon in the machine-code production. Note that when the parser returns to a
|
||||
* parent constructor, i.e., the PDA pops its stack, the parser may return to a previous token. If
|
||||
* that behavior is not desired, a machine-code production may contain ellipses, causing the parser
|
||||
* to advance to the next token, even considering those tokens already examined by operands to the
|
||||
* ellipses' left. Once all operands of the selected instruction constructor have been parsed, the
|
||||
* resulting constructor tree ("prototype") is recorded and returned.
|
||||
*
|
||||
* <p>
|
||||
* To display the instruction's mnemonic, the prototype is walked, generating the tokens ("print
|
||||
* pieces") from the mnemonic production of each constructor. The walk is ordered according to that
|
||||
* mnemonic production. The mnemonic grammar consists of syntactic text and symbols. Any symbols it
|
||||
* uses must also appear in the machine-code production. Where the symbol is a sub-table, it behaves
|
||||
* like a non-terminal in the grammar: It generates the print pieces of the constructor selected for
|
||||
* the sub-table. Where the symbol is a field, it behaves like a terminal. It displays the numeric
|
||||
* value of the field, or in the case of attached names, e.g., register names, it displays the name.
|
||||
*
|
||||
* <p>
|
||||
* To complicate matters, but greatly increase the capability of the disassembler, SLEIGH introduces
|
||||
* temporary symbols and context to the disassembler. A temporary symbol allows the computation of
|
||||
* displayed values from fields. (The value may also be used by the p-code generator.) For example,
|
||||
* a language may permit the expression of immediates as a value and a shift. Temporary symbols
|
||||
* permit the effective value to be computed and displayed. Thus, a temporary symbol is valid in the
|
||||
* mnemonic production. Context serves at least two purposes: 1) To propagate auxiliary information
|
||||
* to sub-tables during disassembly, and 2) To handle persistent state changes in a processor that
|
||||
* modify its decoder, e.g., ARM in THUMB mode. The latter is accomplished by marking regions of
|
||||
* memory with this contextual information. Context is implemented by introducing a context
|
||||
* register. It behaves like a special mutable token, initialized from the disassembler's memory,
|
||||
* the context marked at the instruction's start address, or the language's default context. Like
|
||||
* token fields, context fields can be referred to by a constructor's machine-code production,
|
||||
* either to form constraints or to parse as operands. Fields may be modified by including mutations
|
||||
* in the constructor. Mutations and temporary symbols are defined by assigning an expression to the
|
||||
* field or symbol. Those expressions may refer to other fields and temporary symbols in the scope
|
||||
* of that constructor. Since mutations are meant to be propagated down, they must be applied in
|
||||
* pre-order during parsing. Note that context is not saved on any sort of stack, thus it is
|
||||
* possible for context mutations in a sub-table operand (and its sub-table operands) to affect
|
||||
* parsing of sibling sub-table operands to the right.
|
||||
*
|
||||
* <p>
|
||||
* When disassembling entire subroutines, the disassembler must propagate context changes from
|
||||
* instruction to instruction. Some bits of the context register are marked "global." Those bits,
|
||||
* when instruction parsing succeeds, are taken as the "output context" of the resulting
|
||||
* instruction. Propagation follows from a recursive traversal disassembly strategy, i.e., it heeds
|
||||
* the branch targets of the instruction. The generated p-code is used to determine whether the
|
||||
* instruction has branches and/or fall-through. If the output context differs from the default
|
||||
* context, the disassembler saves it as the initial context for the next instruction. If the
|
||||
* instruction has a branch target, the output context is marked at the target address.
|
||||
*
|
||||
* <h2>Assembly</h2>
|
||||
*
|
||||
* <p>
|
||||
* Conceptually, assembly is a straightforward reversal of the disassembly process; however, the
|
||||
* actual implementation is far more complex. To assemble an instruction there are three distinct
|
||||
* phases: 1) Parsing, 2) Prototype generation, 3) Machine code generation. Each phase may take
|
||||
* advantage of pre-computed artifacts.
|
||||
*
|
||||
* <h3>Parsing Assembly Mnemonics</h3>
|
||||
*
|
||||
* <p>
|
||||
* To parse, we pre-compute a LALR(1) parser based on mnemonic grammar. Because different
|
||||
* constructors may specify the same mnemonic production as others in the same table, we have to
|
||||
* associate all such constructors to the production. This step takes some time, and may be better
|
||||
* suited as a build-time step. Because SLEIGH specifications are not generally concerned with
|
||||
* eliminating ambiguity of printed instructions (rather, it only does so for instruction bytes), we
|
||||
* must consider that the grammar could be ambiguous. To handle this, the action/goto table is
|
||||
* permitted multiple entries per cell, and we allow backtracking. There are also cases where tokens
|
||||
* are not actually separated by spaces. For example, in the {@code ia.sinc} file, there is JMP, and
|
||||
* J^cc, meaning, the lexer must consider J as a token as well as JMP, introducing another source of
|
||||
* possible backtracking. Despite that, parsing an instruction is fairly quick, since the sentences
|
||||
* are rather short. The pre-compute part of this process is implemented in {@link #buildGrammar()}
|
||||
* and {@link #buildParser()}. Parsing is then encapsulated in {@link AssemblyParser}.
|
||||
*
|
||||
* <h3>Prototype Generation</h3>
|
||||
*
|
||||
* <p>
|
||||
* To generate prototypes, we examine each resulting parse tree. If there are no parse trees, then a
|
||||
* syntax errors is reported. Otherwise, for each tree, starting at the root production, we consider
|
||||
* all associated constructors, matching each print piece to its corresponding operand on the
|
||||
* machine-code side. For sub-table operands, the production substituted for the associated
|
||||
* non-terminal guides generation, recursively. For other operands, the associated terminal provides
|
||||
* the value or name. To mimic the token advancement of the disassembler, a shift is computed and
|
||||
* stored for each operand. Computing the shift requires computing each operand's length, and so
|
||||
* once the root of each prototype is generated, the instruction length is also known. Patterns and
|
||||
* mutations are applied to mimic the disassembly process: pre-ordered, depth first, left to right,
|
||||
* heeding the computed shift. If a pattern or mutation for a constructor conflicts with what's been
|
||||
* generated so far, the constructor is pruned. If all possible constructors for a sub-table operand
|
||||
* are pruned, then the containing constructor is also pruned.
|
||||
*
|
||||
* <p>
|
||||
* In some cases, an operand appears in the machine-code production, but not the mnemonic
|
||||
* production: so-called "hidden operands." These pose a potential issue for the assembler, because
|
||||
* nothing syntactic can guide prototype generation. For hidden sub-table operands, we must consider
|
||||
* all constructors in the table. Furthermore, all operands of those constructors are considered
|
||||
* "hidden," and so we exhaust recursively. For other hidden operands, the value is left
|
||||
* unspecified. The prototype generation process is encapsulated in
|
||||
* {@link AssemblyConstructStateGenerator}.
|
||||
*
|
||||
* <h3>Machine Code Generation</h3>
|
||||
*
|
||||
* <p>
|
||||
* Machine code generation is a complex process, but it follows a straightforward reversal of the
|
||||
* disassembler's parse phase. For each prototype, we start at the leaves (non-sub-table operands)
|
||||
* and proceed upwards. This is still a depth-first traversal, but unlike disassembly, generation
|
||||
* proceeds in post-order and right to left, as follows. Starting at the root:
|
||||
*
|
||||
* <ol>
|
||||
* <li>Resolve operands from right to left, descending into sub-table operands.</li>
|
||||
* <li>Solve context mutations, in reverse order.
|
||||
* <li>Apply the required patterns
|
||||
* </ol>
|
||||
*
|
||||
* <p>
|
||||
* Note that for a single prototype, a constructor has already been selected for each sub-table
|
||||
* operand. The resolution of sub-table operands follows the same process as for the root
|
||||
* constructor.
|
||||
*
|
||||
* <p>
|
||||
* For other operands, resolution proceeds by solving the operand's defining expression set equal to
|
||||
* the value specified by the terminal. The resulting values are written into their respective token
|
||||
* or context fields, generating an "assembly pattern." An assembly pattern is simply a masked bit
|
||||
* sequence recording what is expected in the instruction buffer and context register. Each bit is
|
||||
* 0, 1, or unspecified. In many cases, the "defining expression" is simply a field, so "solving"
|
||||
* degenerates simply to "writing" the specified value into the field. Solving expressions is only
|
||||
* required when a terminal defines the value of a temporary symbol. If the value is unspecified,
|
||||
* i.e., it is a hidden operand, then no fields are written. Thus, hidden non-sub-table operands
|
||||
* generate empty patterns.
|
||||
*
|
||||
* <p>
|
||||
* As machine code generation proceeds right to left in a constructor, the resulting assembly
|
||||
* patterns are accumulated. If a generated pattern conflicts with that accumulated so far, the
|
||||
* pattern is pruned, likely halting generation of the current prototype. Once all operands have
|
||||
* been successfully resolved, the constructor's context mutations are solved. These tend to get
|
||||
* complicated since some fields may have values defined by the accumulated pattern, and some may
|
||||
* not. The changes are processed in reverse order from specified in the constructor, since fields
|
||||
* may be mutated in a way that forms data dependences among them. To solve, the field on the
|
||||
* left-hand side of the mutation is read, then it is set equal to the right-hand size and passed to
|
||||
* the solver. Because, from the disassembly perspective, the left-hand side is about to be written,
|
||||
* its value is cleared before passed to the solver. If successful, the solver returns patterns that
|
||||
* satisfy the equation. Resolution accumulates the patterns. If solving fails, or the patterns
|
||||
* conflict, it is pruned. Finally, the patterns required to select the constructor are applied,
|
||||
* again pruning conflicts. Note that a constructor may specify multiple patterns, e.g., if a
|
||||
* constraint is <code>op == 5 || op == 6</code>. Thus, overall, it is possible a single prototype
|
||||
* will generate multiple assembly patterns. This process is encapsulated in
|
||||
* {@link AssemblyConstructState}.
|
||||
*
|
||||
* <h3>Handling Context and Prefixes</h3>
|
||||
*
|
||||
* <p>
|
||||
* Once the root constructor has been completely resolved, the resulting instruction patterns
|
||||
* comprise the generated instruction bytes. However, we must consider the context pattern, too. In
|
||||
* practice, the assembler is invoked at a particular address, and the program database may provide
|
||||
* an initial context (as marked during previous disassembly). In other words, when patching an
|
||||
* instruction, we have to keep any persistent context in place. Thus, we can further cull patterns
|
||||
* whose context does not match. This intuition is frustrated by the possibility of constructors
|
||||
* with the mnemonic production <code>^instruction</code>, though. These "pure recursive"
|
||||
* constructors are often (ab)used to handle instruction prefixes, e.g.:
|
||||
*
|
||||
* <code>
|
||||
* :^instruction is prefixed=0 & byte=0xff; instruction [ prefixed=1; ] {}
|
||||
* </code>
|
||||
*
|
||||
* <p>
|
||||
* There are no syntactic elements that would cue the assembly parser to use this constructor.
|
||||
* Instead, we rely on the context register. Were it not for these kinds of constructors, we could
|
||||
* use the saved context as input to the prototype generation phase; however, we cannot. Instead, we
|
||||
* use the empty context and delay this step until after machine code generation. During assembler
|
||||
* construction, we pre-compute a "context transition graph." The mnemonic production
|
||||
* <code>[instruction] => [instruction]</code> has associated with it all pure recursive
|
||||
* constructors. Naturally, that production cannot be included in the parser, as it would generate
|
||||
* increasingly deep parse trees <em>ad infinitum</em>. The graph starts with a seed node: the
|
||||
* language's default context. Then each pure recursive constructor is considered as an edge,
|
||||
* leading to the node resulting from applying that constructor, mimicking disassembly. This
|
||||
* proceeds for each unvisited node until no new nodes are produced. This component is encapsulated
|
||||
* in {@link AssemblyContextGraph}.
|
||||
*
|
||||
* <p>
|
||||
* To generate prefixes, we seek the shortest paths from nodes whose context pattern match the
|
||||
* initial context to nodes whose context pattern matches the generated assembly pattern. Note that
|
||||
* the shortest path may be the zero-length path. If no paths are found, assembly fails. Machine
|
||||
* code generation then proceeds by considering each path, and resolving the constructors in
|
||||
* reverse, in the same manner as constructors from the prototype are resolved. Note that the
|
||||
* patterns may need to be shifted to accommodate prefix tokens. This is accomplished by examining
|
||||
* the shift of the nested instruction operand for each constructor. This process is implemented in
|
||||
* {@link AssemblyTreeResolver#resolveRootRecursion(AssemblyResolutionResults)}.
|
||||
*
|
||||
* <h3>Final Steps</h3>
|
||||
*
|
||||
* <p>
|
||||
* As a final fail safe, the generated instructions are fed back through the disassembler and the
|
||||
* resulting constructor trees are compared. If not equivalent, the instruction is dropped. It is
|
||||
* possible (common in fact) that the generated assembly instruction pattern is not fully defined.
|
||||
* By default, the assembler will substitute 0 for each undefined bit. However, the assembler API
|
||||
* allows the retrieval of the generated pattern, since a user may wish to substitute other values.
|
||||
*
|
||||
* <p>
|
||||
* If, in the end, no instructions are generated, a semantic error is reported. Often, the
|
||||
* description is unwieldy, since it comprises a list of reasons each pattern was pruned. From the
|
||||
* user side, it is usually sufficient to say, "sorry." From the language developer side, it may be
|
||||
* useful to manually reconstruct the prototype and discover the conflicts. To that end, the
|
||||
* implementation includes optional diagnostics, but even then, decoding them takes some familiarity
|
||||
* and expertise.
|
||||
*/
|
||||
public class SleighAssemblerBuilder implements AssemblerBuilder {
|
||||
protected static final DbgTimer dbg = SystemUtilities.isInTestingBatchMode() ? DbgTimer.INACTIVE : DbgTimer.ACTIVE;
|
||||
protected static final DbgTimer dbg =
|
||||
SystemUtilities.isInTestingBatchMode() ? DbgTimer.INACTIVE : DbgTimer.ACTIVE;
|
||||
|
||||
protected SleighLanguage lang;
|
||||
protected AssemblyGrammar grammar;
|
||||
@@ -220,6 +429,7 @@ public class SleighAssemblerBuilder implements AssemblerBuilder {
|
||||
/**
|
||||
* Convert the given operand symbol to an {@link AssemblySymbol}
|
||||
*
|
||||
* <p>
|
||||
* For subtables, this results in a non-terminal, for all others, the result in a terminal.
|
||||
*
|
||||
* @param cons the constructor to which the operand belongs
|
||||
@@ -242,7 +452,9 @@ public class SleighAssemblerBuilder implements AssemblerBuilder {
|
||||
return built;
|
||||
}
|
||||
if (defsym == null) {
|
||||
built = new AssemblyNumericTerminal(name, getBitSize(cons, opsym));
|
||||
HandleTpl htpl = getHandleTpl(cons, opsym);
|
||||
built = htpl == null ? new AssemblyNumericTerminal(name, 0, null)
|
||||
: new AssemblyNumericTerminal(name, htpl.getSize(), htpl.getAddressSpace());
|
||||
}
|
||||
else if (defsym instanceof SubtableSymbol) {
|
||||
built = new AssemblyNonTerminal(name);
|
||||
@@ -268,39 +480,40 @@ public class SleighAssemblerBuilder implements AssemblerBuilder {
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain the size in bits of a textual operand.
|
||||
* Obtain the p-code result handle for the given operand
|
||||
*
|
||||
* This is a little odd, since the variables in pattern expressions do not have an explicit
|
||||
* size. However, the value exported by a constructor's pCode may have an explicit size given
|
||||
* (in bytes). Thus, there is a special case, where a constructor prints just one operand and
|
||||
* exports that same operand with an explicit size. In that case, the size of the operand is
|
||||
* printed according to that exported size.
|
||||
* <p>
|
||||
* This handles a special case, where a constructor prints just one operand and exports that
|
||||
* same operand, often with an explicit size, or as an address in a given space. In such cases,
|
||||
* the listing displays that operand according to that exported size.
|
||||
*
|
||||
* For disassembly, this information is used simply to truncate the bits before they are
|
||||
* displayed. For assembly, we must do two things: 1) Ensure that the provided value fits in the
|
||||
* given size, and 2) Mask the goal when solving the pattern expression for the operand.
|
||||
* <p>
|
||||
* For assembly, this gives a few opportunities: 1) We can/must ensure the specified value fits,
|
||||
* by checking the size. 2) We can/must mask the goal when solving the defining pattern
|
||||
* expression for the operand. 3)) We can/must check that a label's address space matches that
|
||||
* represented by the operand, when used for a numeric terminal.
|
||||
*
|
||||
* @param cons the constructor from which the production is being derived
|
||||
* @param opsym the operand symbol corresponding to the grammatical symbol, whose size we wish
|
||||
* to determine.
|
||||
* @return the size of the operand in bits
|
||||
*/
|
||||
protected int getBitSize(Constructor cons, OperandSymbol opsym) {
|
||||
protected HandleTpl getHandleTpl(Constructor cons, OperandSymbol opsym) {
|
||||
ConstructTpl ctpl = cons.getTempl();
|
||||
if (null == ctpl) {
|
||||
// No pcode, no size specification
|
||||
return 0;
|
||||
return null;
|
||||
}
|
||||
HandleTpl htpl = ctpl.getResult();
|
||||
if (null == htpl) {
|
||||
// If nothing is exported, the size is unspecified
|
||||
return 0;
|
||||
return null;
|
||||
}
|
||||
if (opsym.getIndex() != htpl.getOffsetOperandIndex()) {
|
||||
// If the export is not of the same operand, it does not specify its size
|
||||
return 0;
|
||||
return null;
|
||||
}
|
||||
return htpl.getSize();
|
||||
return htpl;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -326,30 +539,10 @@ public class SleighAssemblerBuilder implements AssemblerBuilder {
|
||||
if (sym.takesOperandIndex()) {
|
||||
indices.add(index);
|
||||
}
|
||||
rhs.add(sym);
|
||||
rhs.addSymbol(sym);
|
||||
}
|
||||
else {
|
||||
String tstr = str.trim();
|
||||
if (tstr.equals("")) {
|
||||
rhs.addWS();
|
||||
}
|
||||
else {
|
||||
char first = tstr.charAt(0);
|
||||
if (!str.startsWith(tstr)) {
|
||||
rhs.addWS();
|
||||
}
|
||||
if (!Character.isLetterOrDigit(first)) {
|
||||
rhs.addWS();
|
||||
}
|
||||
rhs.add(new AssemblyStringTerminal(str.trim()));
|
||||
char last = tstr.charAt(tstr.length() - 1);
|
||||
if (!str.endsWith(tstr)) {
|
||||
rhs.addWS();
|
||||
}
|
||||
if (!Character.isLetterOrDigit(last)) {
|
||||
rhs.addWS();
|
||||
}
|
||||
}
|
||||
rhs.addSeparators(str);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -384,7 +577,7 @@ public class SleighAssemblerBuilder implements AssemblerBuilder {
|
||||
// Ignore. We don't do pcode.
|
||||
}
|
||||
else if (sym instanceof OperandSymbol) {
|
||||
// Ignore. These are terminals, or will be produced by there defining symbol
|
||||
// Ignore. These are terminals, or will be produced by their defining symbols
|
||||
}
|
||||
else if (sym instanceof ValueSymbol) {
|
||||
// Ignore. These are now terminals
|
||||
|
||||
+32
-29
@@ -19,12 +19,12 @@ import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import ghidra.app.plugin.assembler.sleigh.sem.AssemblyResolution;
|
||||
import ghidra.app.plugin.assembler.sleigh.sem.AssemblyResolvedConstructor;
|
||||
import ghidra.app.plugin.assembler.sleigh.sem.AssemblyResolvedPatterns;
|
||||
import ghidra.app.plugin.processors.sleigh.expression.BinaryExpression;
|
||||
import ghidra.app.plugin.processors.sleigh.expression.PatternExpression;
|
||||
|
||||
/**
|
||||
* A solver that handles expressions of the form A [OP] B
|
||||
* A solver that handles expressions of the form {@code A [OP] B}
|
||||
*
|
||||
* @param <T> the type of expression solved (the operator)
|
||||
*/
|
||||
@@ -37,10 +37,10 @@ public abstract class AbstractBinaryExpressionSolver<T extends BinaryExpression>
|
||||
|
||||
@Override
|
||||
public AssemblyResolution solve(T exp, MaskedLong goal, Map<String, Long> vals,
|
||||
Map<Integer, Object> res, AssemblyResolvedConstructor cur, Set<SolverHint> hints,
|
||||
String description) throws NeedsBackfillException {
|
||||
MaskedLong lval = solver.getValue(exp.getLeft(), vals, res, cur);
|
||||
MaskedLong rval = solver.getValue(exp.getRight(), vals, res, cur);
|
||||
AssemblyResolvedPatterns cur, Set<SolverHint> hints, String description)
|
||||
throws NeedsBackfillException {
|
||||
MaskedLong lval = solver.getValue(exp.getLeft(), vals, cur);
|
||||
MaskedLong rval = solver.getValue(exp.getRight(), vals, cur);
|
||||
|
||||
if (lval != null && !lval.isFullyDefined()) {
|
||||
if (!lval.isFullyUndefined()) {
|
||||
@@ -61,23 +61,23 @@ public abstract class AbstractBinaryExpressionSolver<T extends BinaryExpression>
|
||||
return ConstantValueSolver.checkConstAgrees(cval, goal, description);
|
||||
}
|
||||
else if (lval != null) {
|
||||
return solveRightSide(exp.getRight(), lval, goal, vals, res, cur, hints,
|
||||
return solveRightSide(exp.getRight(), lval, goal, vals, cur, hints,
|
||||
description);
|
||||
}
|
||||
else if (rval != null) {
|
||||
return solveLeftSide(exp.getLeft(), rval, goal, vals, res, cur, hints, description);
|
||||
return solveLeftSide(exp.getLeft(), rval, goal, vals, cur, hints, description);
|
||||
}
|
||||
else {
|
||||
// Each solver may provide a strategy for solving expression where both sides are
|
||||
// variable, e.g., two fields being concatenated via OR.
|
||||
return solveTwoSided(exp, goal, vals, res, cur, hints, description);
|
||||
return solveTwoSided(exp, goal, vals, cur, hints, description);
|
||||
}
|
||||
}
|
||||
catch (NeedsBackfillException e) {
|
||||
throw e;
|
||||
}
|
||||
catch (SolverException e) {
|
||||
return AssemblyResolution.error(e.getMessage(), description, null);
|
||||
return AssemblyResolution.error(e.getMessage(), description);
|
||||
}
|
||||
catch (AssertionError e) {
|
||||
dbg.println("While solving: " + exp + " (" + description + ")");
|
||||
@@ -86,30 +86,30 @@ public abstract class AbstractBinaryExpressionSolver<T extends BinaryExpression>
|
||||
}
|
||||
|
||||
protected AssemblyResolution solveLeftSide(PatternExpression lexp, MaskedLong rval,
|
||||
MaskedLong goal, Map<String, Long> vals, Map<Integer, Object> res,
|
||||
AssemblyResolvedConstructor cur, Set<SolverHint> hints, String description)
|
||||
MaskedLong goal, Map<String, Long> vals, AssemblyResolvedPatterns cur,
|
||||
Set<SolverHint> hints, String description)
|
||||
throws NeedsBackfillException, SolverException {
|
||||
return solver.solve(lexp, computeLeft(rval, goal), vals, res, cur, hints, description);
|
||||
return solver.solve(lexp, computeLeft(rval, goal), vals, cur, hints, description);
|
||||
}
|
||||
|
||||
protected AssemblyResolution solveRightSide(PatternExpression rexp, MaskedLong lval,
|
||||
MaskedLong goal, Map<String, Long> vals, Map<Integer, Object> res,
|
||||
AssemblyResolvedConstructor cur, Set<SolverHint> hints, String description)
|
||||
MaskedLong goal, Map<String, Long> vals, AssemblyResolvedPatterns cur,
|
||||
Set<SolverHint> hints, String description)
|
||||
throws NeedsBackfillException, SolverException {
|
||||
return solver.solve(rexp, computeRight(lval, goal), vals, res, cur, hints, description);
|
||||
return solver.solve(rexp, computeRight(lval, goal), vals, cur, hints, description);
|
||||
}
|
||||
|
||||
protected AssemblyResolution solveTwoSided(T exp, MaskedLong goal, Map<String, Long> vals,
|
||||
Map<Integer, Object> res, AssemblyResolvedConstructor cur, Set<SolverHint> hints,
|
||||
String description) throws NeedsBackfillException, SolverException {
|
||||
AssemblyResolvedPatterns cur, Set<SolverHint> hints, String description)
|
||||
throws NeedsBackfillException, SolverException {
|
||||
throw new NeedsBackfillException("_two_sided_");
|
||||
}
|
||||
|
||||
@Override
|
||||
public MaskedLong getValue(T exp, Map<String, Long> vals, Map<Integer, Object> res,
|
||||
AssemblyResolvedConstructor cur) throws NeedsBackfillException {
|
||||
MaskedLong lval = solver.getValue(exp.getLeft(), vals, res, cur);
|
||||
MaskedLong rval = solver.getValue(exp.getRight(), vals, res, cur);
|
||||
public MaskedLong getValue(T exp, Map<String, Long> vals, AssemblyResolvedPatterns cur)
|
||||
throws NeedsBackfillException {
|
||||
MaskedLong lval = solver.getValue(exp.getLeft(), vals, cur);
|
||||
MaskedLong rval = solver.getValue(exp.getRight(), vals, cur);
|
||||
if (lval != null && rval != null) {
|
||||
MaskedLong cval = compute(lval, rval);
|
||||
return cval;
|
||||
@@ -130,7 +130,9 @@ public abstract class AbstractBinaryExpressionSolver<T extends BinaryExpression>
|
||||
/**
|
||||
* Compute the right-hand-side value given that the result and the left are known
|
||||
*
|
||||
* NOTE: Assumes commutativity by default
|
||||
* <p>
|
||||
* <b>NOTE:</b> Assumes commutativity by default
|
||||
*
|
||||
* @param lval the left-hand-side value
|
||||
* @param goal the result
|
||||
* @return the right-hand-side value solution
|
||||
@@ -150,16 +152,17 @@ public abstract class AbstractBinaryExpressionSolver<T extends BinaryExpression>
|
||||
public abstract MaskedLong compute(MaskedLong lval, MaskedLong rval);
|
||||
|
||||
@Override
|
||||
public int getInstructionLength(T exp, Map<Integer, Object> res) {
|
||||
int ll = solver.getInstructionLength(exp.getLeft(), res);
|
||||
int lr = solver.getInstructionLength(exp.getRight(), res);
|
||||
public int getInstructionLength(T exp) {
|
||||
int ll = solver.getInstructionLength(exp.getLeft());
|
||||
int lr = solver.getInstructionLength(exp.getRight());
|
||||
return Math.max(ll, lr);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MaskedLong valueForResolution(T exp, AssemblyResolvedConstructor rc) {
|
||||
MaskedLong lval = solver.valueForResolution(exp.getLeft(), rc);
|
||||
MaskedLong rval = solver.valueForResolution(exp.getRight(), rc);
|
||||
public MaskedLong valueForResolution(T exp, Map<String, Long> vals,
|
||||
AssemblyResolvedPatterns rc) {
|
||||
MaskedLong lval = solver.valueForResolution(exp.getLeft(), vals, rc);
|
||||
MaskedLong rval = solver.valueForResolution(exp.getRight(), vals, rc);
|
||||
return compute(lval, rval);
|
||||
}
|
||||
}
|
||||
|
||||
+9
-9
@@ -19,7 +19,7 @@ import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import ghidra.app.plugin.assembler.sleigh.sem.AssemblyResolution;
|
||||
import ghidra.app.plugin.assembler.sleigh.sem.AssemblyResolvedConstructor;
|
||||
import ghidra.app.plugin.assembler.sleigh.sem.AssemblyResolvedPatterns;
|
||||
import ghidra.app.plugin.assembler.sleigh.util.DbgTimer;
|
||||
import ghidra.app.plugin.processors.sleigh.expression.PatternExpression;
|
||||
|
||||
@@ -49,14 +49,13 @@ public abstract class AbstractExpressionSolver<T extends PatternExpression> {
|
||||
* @param exp the expression to solve
|
||||
* @param goal the desired value of the expression
|
||||
* @param vals values of defined symbols
|
||||
* @param res the results of subconstructor resolutions (used for lengths)
|
||||
* @param hints describes techniques applied by calling solvers
|
||||
* @param description the description to give to resolved solutions
|
||||
* @return the resolution
|
||||
* @throws NeedsBackfillException if the expression refers to an undefined symbol
|
||||
*/
|
||||
public abstract AssemblyResolution solve(T exp, MaskedLong goal, Map<String, Long> vals,
|
||||
Map<Integer, Object> res, AssemblyResolvedConstructor cur, Set<SolverHint> hints,
|
||||
AssemblyResolvedPatterns cur, Set<SolverHint> hints,
|
||||
String description) throws NeedsBackfillException;
|
||||
|
||||
/**
|
||||
@@ -64,33 +63,34 @@ public abstract class AbstractExpressionSolver<T extends PatternExpression> {
|
||||
*
|
||||
* @param exp the expression
|
||||
* @param vals values of defined symbols
|
||||
* @param res the results of subconstructor resolutions (used for lengths)
|
||||
* @return the constant value, or null if it depends on a variable
|
||||
* @throws NeedsBackfillException if the expression refers to an undefined symbol
|
||||
*/
|
||||
public abstract MaskedLong getValue(T exp, Map<String, Long> vals, Map<Integer, Object> res,
|
||||
AssemblyResolvedConstructor cur) throws NeedsBackfillException;
|
||||
public abstract MaskedLong getValue(T exp, Map<String, Long> vals,
|
||||
AssemblyResolvedPatterns cur) throws NeedsBackfillException;
|
||||
|
||||
/**
|
||||
* Determines the length of the subconstructor that would be returned had the expression not
|
||||
* depended on an undefined symbol.
|
||||
*
|
||||
* <p>
|
||||
* This is used by the backfilling process to ensure values are written to the correct offset
|
||||
*
|
||||
* @param exp the expression
|
||||
* @param res the results of subconstructor resolutions (used for lengths)
|
||||
* @return the length of filled in token field(s).
|
||||
*/
|
||||
public abstract int getInstructionLength(T exp, Map<Integer, Object> res);
|
||||
public abstract int getInstructionLength(T exp);
|
||||
|
||||
/**
|
||||
* Compute the value of the expression given the (possibly-intermediate) resolution
|
||||
*
|
||||
* @param exp the expression to evaluate
|
||||
* @param vals values of defined symbols
|
||||
* @param rc the resolution on which to evaluate it
|
||||
* @return the result
|
||||
*/
|
||||
public abstract MaskedLong valueForResolution(T exp, AssemblyResolvedConstructor rc);
|
||||
public abstract MaskedLong valueForResolution(T exp, Map<String, Long> vals,
|
||||
AssemblyResolvedPatterns rc);
|
||||
|
||||
/**
|
||||
* Register this particular solver with the general expression solver
|
||||
|
||||
+17
-14
@@ -19,11 +19,11 @@ import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import ghidra.app.plugin.assembler.sleigh.sem.AssemblyResolution;
|
||||
import ghidra.app.plugin.assembler.sleigh.sem.AssemblyResolvedConstructor;
|
||||
import ghidra.app.plugin.assembler.sleigh.sem.AssemblyResolvedPatterns;
|
||||
import ghidra.app.plugin.processors.sleigh.expression.UnaryExpression;
|
||||
|
||||
/**
|
||||
* A solver that handles expressions of the form [OP]A
|
||||
* A solver that handles expressions of the form {@code [OP]A}
|
||||
*
|
||||
* @param <T> the type of expression solved (the operator)
|
||||
*/
|
||||
@@ -36,9 +36,9 @@ public abstract class AbstractUnaryExpressionSolver<T extends UnaryExpression>
|
||||
|
||||
@Override
|
||||
public AssemblyResolution solve(T exp, MaskedLong goal, Map<String, Long> vals,
|
||||
Map<Integer, Object> res, AssemblyResolvedConstructor cur, Set<SolverHint> hints,
|
||||
String description) throws NeedsBackfillException {
|
||||
MaskedLong uval = solver.getValue(exp.getUnary(), vals, res, cur);
|
||||
AssemblyResolvedPatterns cur, Set<SolverHint> hints, String description)
|
||||
throws NeedsBackfillException {
|
||||
MaskedLong uval = solver.getValue(exp.getUnary(), vals, cur);
|
||||
try {
|
||||
if (uval != null && uval.isFullyDefined()) {
|
||||
MaskedLong cval = compute(uval);
|
||||
@@ -46,7 +46,7 @@ public abstract class AbstractUnaryExpressionSolver<T extends UnaryExpression>
|
||||
return ConstantValueSolver.checkConstAgrees(cval, goal, description);
|
||||
}
|
||||
}
|
||||
return solver.solve(exp.getUnary(), computeInverse(goal), vals, res, cur, hints,
|
||||
return solver.solve(exp.getUnary(), computeInverse(goal), vals, cur, hints,
|
||||
description);
|
||||
}
|
||||
/*
|
||||
@@ -60,9 +60,9 @@ public abstract class AbstractUnaryExpressionSolver<T extends UnaryExpression>
|
||||
}
|
||||
|
||||
@Override
|
||||
public MaskedLong getValue(T exp, Map<String, Long> vals, Map<Integer, Object> res,
|
||||
AssemblyResolvedConstructor cur) throws NeedsBackfillException {
|
||||
MaskedLong val = solver.getValue(exp.getUnary(), vals, res, cur);
|
||||
public MaskedLong getValue(T exp, Map<String, Long> vals, AssemblyResolvedPatterns cur)
|
||||
throws NeedsBackfillException {
|
||||
MaskedLong val = solver.getValue(exp.getUnary(), vals, cur);
|
||||
if (val != null) {
|
||||
return compute(val);
|
||||
}
|
||||
@@ -72,7 +72,9 @@ public abstract class AbstractUnaryExpressionSolver<T extends UnaryExpression>
|
||||
/**
|
||||
* Compute the input value given that the result is known
|
||||
*
|
||||
* NOTE: Assumes an involution by default
|
||||
* <p>
|
||||
* <b>NOTE:</b> Assumes an involution by default
|
||||
*
|
||||
* @param goal the result
|
||||
* @return the input value solution
|
||||
*/
|
||||
@@ -89,13 +91,14 @@ public abstract class AbstractUnaryExpressionSolver<T extends UnaryExpression>
|
||||
public abstract MaskedLong compute(MaskedLong val);
|
||||
|
||||
@Override
|
||||
public int getInstructionLength(T exp, Map<Integer, Object> res) {
|
||||
return solver.getInstructionLength(exp.getUnary(), res);
|
||||
public int getInstructionLength(T exp) {
|
||||
return solver.getInstructionLength(exp.getUnary());
|
||||
}
|
||||
|
||||
@Override
|
||||
public MaskedLong valueForResolution(T exp, AssemblyResolvedConstructor rc) {
|
||||
MaskedLong val = solver.valueForResolution(exp.getUnary(), rc);
|
||||
public MaskedLong valueForResolution(T exp, Map<String, Long> vals,
|
||||
AssemblyResolvedPatterns rc) {
|
||||
MaskedLong val = solver.valueForResolution(exp.getUnary(), vals, rc);
|
||||
return compute(val);
|
||||
}
|
||||
}
|
||||
|
||||
+11
-10
@@ -19,12 +19,13 @@ import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import ghidra.app.plugin.assembler.sleigh.sem.AssemblyResolution;
|
||||
import ghidra.app.plugin.assembler.sleigh.sem.AssemblyResolvedConstructor;
|
||||
import ghidra.app.plugin.assembler.sleigh.sem.AssemblyResolvedPatterns;
|
||||
import ghidra.app.plugin.processors.sleigh.expression.ConstantValue;
|
||||
|
||||
/**
|
||||
* "Solves" constant expressions
|
||||
*
|
||||
* <p>
|
||||
* Essentially, this either evaluates successfully when asked for a constant value, or checks that
|
||||
* the goal is equal to the constant. Otherwise, there is no solution.
|
||||
*/
|
||||
@@ -36,25 +37,26 @@ public class ConstantValueSolver extends AbstractExpressionSolver<ConstantValue>
|
||||
|
||||
@Override
|
||||
public AssemblyResolution solve(ConstantValue cv, MaskedLong goal, Map<String, Long> vals,
|
||||
Map<Integer, Object> res, AssemblyResolvedConstructor cur, Set<SolverHint> hints,
|
||||
AssemblyResolvedPatterns cur, Set<SolverHint> hints,
|
||||
String description) {
|
||||
MaskedLong value = getValue(cv, vals, res, cur);
|
||||
MaskedLong value = getValue(cv, vals, cur);
|
||||
return checkConstAgrees(value, goal, description);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MaskedLong getValue(ConstantValue cv, Map<String, Long> vals, Map<Integer, Object> res,
|
||||
AssemblyResolvedConstructor cur) {
|
||||
public MaskedLong getValue(ConstantValue cv, Map<String, Long> vals,
|
||||
AssemblyResolvedPatterns cur) {
|
||||
return MaskedLong.fromLong(cv.getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getInstructionLength(ConstantValue cv, Map<Integer, Object> res) {
|
||||
public int getInstructionLength(ConstantValue cv) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MaskedLong valueForResolution(ConstantValue cv, AssemblyResolvedConstructor rc) {
|
||||
public MaskedLong valueForResolution(ConstantValue cv, Map<String, Long> vals,
|
||||
AssemblyResolvedPatterns rc) {
|
||||
return MaskedLong.fromLong(cv.getValue());
|
||||
}
|
||||
|
||||
@@ -62,9 +64,8 @@ public class ConstantValueSolver extends AbstractExpressionSolver<ConstantValue>
|
||||
String description) {
|
||||
if (!value.agrees(goal)) {
|
||||
return AssemblyResolution.error(
|
||||
"Constant value " + value + " does not agree with child requirements", description,
|
||||
null);
|
||||
"Constant value " + value + " does not agree with child requirements", description);
|
||||
}
|
||||
return AssemblyResolution.nop(description, null);
|
||||
return AssemblyResolution.nop(description, null, null);
|
||||
}
|
||||
}
|
||||
|
||||
+10
-9
@@ -24,6 +24,7 @@ import ghidra.app.plugin.processors.sleigh.expression.ContextField;
|
||||
/**
|
||||
* Solves expressions of a context register field
|
||||
*
|
||||
* <p>
|
||||
* Essentially, this just encodes the goal into the field, if it can be represented in the given
|
||||
* space and format. Otherwise, there is no solution.
|
||||
*/
|
||||
@@ -35,33 +36,33 @@ public class ContextFieldSolver extends AbstractExpressionSolver<ContextField> {
|
||||
|
||||
@Override
|
||||
public AssemblyResolution solve(ContextField cf, MaskedLong goal, Map<String, Long> vals,
|
||||
Map<Integer, Object> res, AssemblyResolvedConstructor cur, Set<SolverHint> hints,
|
||||
String description) {
|
||||
AssemblyResolvedPatterns cur, Set<SolverHint> hints, String description) {
|
||||
assert cf.minValue() == 0; // In case someone decides to do signedness there.
|
||||
if (!goal.isInRange(cf.maxValue(), cf.hasSignbit())) {
|
||||
return AssemblyResolution.error("Value " + goal + " is not valid for " + cf,
|
||||
description, null);
|
||||
description);
|
||||
}
|
||||
AssemblyPatternBlock block = AssemblyPatternBlock.fromContextField(cf, goal);
|
||||
return AssemblyResolution.contextOnly(block, description, null);
|
||||
return AssemblyResolution.contextOnly(block, description);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MaskedLong getValue(ContextField cf, Map<String, Long> vals, Map<Integer, Object> res,
|
||||
AssemblyResolvedConstructor cur) {
|
||||
public MaskedLong getValue(ContextField cf, Map<String, Long> vals,
|
||||
AssemblyResolvedPatterns cur) {
|
||||
if (cur == null) {
|
||||
return null;
|
||||
}
|
||||
return valueForResolution(cf, cur);
|
||||
return valueForResolution(cf, vals, cur);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getInstructionLength(ContextField cf, Map<Integer, Object> res) {
|
||||
public int getInstructionLength(ContextField cf) {
|
||||
return 0; // this is a context field, not an instruction (token) field
|
||||
}
|
||||
|
||||
@Override
|
||||
public MaskedLong valueForResolution(ContextField cf, AssemblyResolvedConstructor rc) {
|
||||
public MaskedLong valueForResolution(ContextField cf, Map<String, Long> vals,
|
||||
AssemblyResolvedPatterns rc) {
|
||||
int size = cf.getByteEnd() - cf.getByteStart() + 1;
|
||||
MaskedLong res = rc.readContext(cf.getByteStart(), size);
|
||||
res = res.shiftRight(cf.getShift());
|
||||
|
||||
+2
-2
@@ -24,8 +24,8 @@ public enum DefaultSolverHint implements SolverHint {
|
||||
*/
|
||||
GUESSING_REPETITION,
|
||||
/**
|
||||
* A boolean or solver which matches a circular shift is solving the value having guessed a
|
||||
* shift
|
||||
* A boolean {@code or} solver which matches a circular shift is solving the value having
|
||||
* guessed a shift
|
||||
*/
|
||||
GUESSING_CIRCULAR_SHIFT_AMOUNT,
|
||||
/**
|
||||
|
||||
+3
-2
@@ -18,7 +18,7 @@ package ghidra.app.plugin.assembler.sleigh.expr;
|
||||
import ghidra.app.plugin.processors.sleigh.expression.DivExpression;
|
||||
|
||||
/**
|
||||
* Solves expressions of the form A / B
|
||||
* Solves expressions of the form {@code A / B}
|
||||
*/
|
||||
public class DivExpressionSolver extends AbstractBinaryExpressionSolver<DivExpression> {
|
||||
|
||||
@@ -37,7 +37,8 @@ public class DivExpressionSolver extends AbstractBinaryExpressionSolver<DivExpre
|
||||
return MaskedLong.fromLong(1);
|
||||
}
|
||||
throw new SolverException(
|
||||
"Encountered a division of the form A / x = B, where A != B. x has many solutions not easily expressed with masking.");
|
||||
"Encountered a division of the form A / x = B, where A != B. x has many solutions " +
|
||||
"not easily expressed with masking.");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+20
-11
@@ -24,10 +24,13 @@ import ghidra.app.plugin.processors.sleigh.expression.EndInstructionValue;
|
||||
/**
|
||||
* "Solves" expressions of {@code inst_next}
|
||||
*
|
||||
* <p>
|
||||
* Works like the constant solver, but takes the value of {@code inst_next}, which is given by the
|
||||
* assembly address and the resulting instruction length.
|
||||
*
|
||||
* NOTE: This solver requires backfill.
|
||||
* <p>
|
||||
* <b>NOTE:</b> This solver requires backfill, since the value of {@code inst_next} is not known
|
||||
* until possible prefixes have been considered.
|
||||
*/
|
||||
public class EndInstructionValueSolver extends AbstractExpressionSolver<EndInstructionValue> {
|
||||
|
||||
@@ -37,32 +40,38 @@ public class EndInstructionValueSolver extends AbstractExpressionSolver<EndInstr
|
||||
|
||||
@Override
|
||||
public AssemblyResolution solve(EndInstructionValue iv, MaskedLong goal, Map<String, Long> vals,
|
||||
Map<Integer, Object> res, AssemblyResolvedConstructor cur, Set<SolverHint> hints,
|
||||
String description) {
|
||||
AssemblyResolvedPatterns cur, Set<SolverHint> hints, String description) {
|
||||
throw new AssertionError(
|
||||
"INTERNAL: Should never be asked to solve for " + AssemblyTreeResolver.INST_NEXT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MaskedLong getValue(EndInstructionValue iv, Map<String, Long> vals,
|
||||
Map<Integer, Object> res, AssemblyResolvedConstructor cur)
|
||||
throws NeedsBackfillException {
|
||||
AssemblyResolvedPatterns cur) throws NeedsBackfillException {
|
||||
Long instNext = vals.get(AssemblyTreeResolver.INST_NEXT);
|
||||
if (instNext == null) {
|
||||
throw new NeedsBackfillException(AssemblyTreeResolver.INST_NEXT);
|
||||
}
|
||||
return MaskedLong.fromLong(vals.get(AssemblyTreeResolver.INST_NEXT));
|
||||
return MaskedLong.fromLong(instNext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getInstructionLength(EndInstructionValue iv, Map<Integer, Object> res) {
|
||||
public int getInstructionLength(EndInstructionValue iv) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MaskedLong valueForResolution(EndInstructionValue exp, AssemblyResolvedConstructor rc) {
|
||||
// Would need to pass in symbol values, and perhaps consider child resolutions.
|
||||
throw new UnsupportedOperationException(
|
||||
"The solver should never ask for this value given a resolved constructor.");
|
||||
public MaskedLong valueForResolution(EndInstructionValue exp, Map<String, Long> vals,
|
||||
AssemblyResolvedPatterns rc) {
|
||||
Long instNext = vals.get(AssemblyTreeResolver.INST_NEXT);
|
||||
if (instNext == null) {
|
||||
/**
|
||||
* This method is used in forward state construction, so just leave unknown. This may
|
||||
* cause unresolvable trees to get generated, but we can't know that until we try to
|
||||
* resolve them.
|
||||
*/
|
||||
return MaskedLong.UNKS;
|
||||
}
|
||||
return MaskedLong.fromLong(instNext);
|
||||
}
|
||||
}
|
||||
|
||||
+28
-12
@@ -19,12 +19,12 @@ import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import ghidra.app.plugin.assembler.sleigh.sem.AssemblyResolution;
|
||||
import ghidra.app.plugin.assembler.sleigh.sem.AssemblyResolvedConstructor;
|
||||
import ghidra.app.plugin.assembler.sleigh.sem.AssemblyResolvedPatterns;
|
||||
import ghidra.app.plugin.processors.sleigh.expression.LeftShiftExpression;
|
||||
import ghidra.util.Msg;
|
||||
|
||||
/**
|
||||
* {@literal Solves expressions of the form A << B}
|
||||
* Solves expressions of the form {@code A << B}
|
||||
*/
|
||||
public class LeftShiftExpressionSolver extends AbstractBinaryExpressionSolver<LeftShiftExpression> {
|
||||
|
||||
@@ -61,13 +61,12 @@ public class LeftShiftExpressionSolver extends AbstractBinaryExpressionSolver<Le
|
||||
|
||||
@Override
|
||||
protected AssemblyResolution solveTwoSided(LeftShiftExpression exp, MaskedLong goal,
|
||||
Map<String, Long> vals, Map<Integer, Object> res, AssemblyResolvedConstructor cur,
|
||||
Set<SolverHint> hints, String description)
|
||||
throws NeedsBackfillException, SolverException {
|
||||
Map<String, Long> vals, AssemblyResolvedPatterns cur, Set<SolverHint> hints,
|
||||
String description) throws NeedsBackfillException, SolverException {
|
||||
// Do not guess the same parameter recursively
|
||||
if (hints.contains(DefaultSolverHint.GUESSING_LEFT_SHIFT_AMOUNT)) {
|
||||
// NOTE: Nested left shifts ought to be written as a left shift by a sum
|
||||
return super.solveTwoSided(exp, goal, vals, res, cur, hints, description);
|
||||
return super.solveTwoSided(exp, goal, vals, cur, hints, description);
|
||||
}
|
||||
// Count the number of zeros to the right, and consider this the maximum shift value
|
||||
// Any higher shift amount would produce too many zeros to the right
|
||||
@@ -76,24 +75,41 @@ public class LeftShiftExpressionSolver extends AbstractBinaryExpressionSolver<Le
|
||||
// use of the leading zero count, at least AFAIK. Maybe to better restrict the max???
|
||||
Set<SolverHint> hintsWithLShift =
|
||||
SolverHint.with(hints, DefaultSolverHint.GUESSING_LEFT_SHIFT_AMOUNT);
|
||||
if (maxShift == 64) {
|
||||
// If the goal is 0s, then any shift will do, so long as the shifted value is 0
|
||||
try {
|
||||
// NB. goal is already 0s, so just use it as subgoal for lhs of shift
|
||||
AssemblyResolution lres =
|
||||
solver.solve(exp.getLeft(), goal, vals, cur, hintsWithLShift, description);
|
||||
if (lres.isError()) {
|
||||
throw new SolverException("Solving left:=0 failed");
|
||||
}
|
||||
// If this works, then the rhs can have any value, so nothing to solve for
|
||||
return lres;
|
||||
}
|
||||
catch (SolverException | UnsupportedOperationException e) {
|
||||
Msg.trace(this, "Trying left:=0 in shift resulted in " + e);
|
||||
// Fall through to the guessing method
|
||||
}
|
||||
}
|
||||
for (int shift = maxShift; shift >= 0; shift--) {
|
||||
try {
|
||||
MaskedLong reqr = MaskedLong.fromLong(shift);
|
||||
MaskedLong reql = computeLeft(reqr, goal);
|
||||
|
||||
AssemblyResolution lres =
|
||||
solver.solve(exp.getLeft(), reql, vals, res, cur, hintsWithLShift, description);
|
||||
solver.solve(exp.getLeft(), reql, vals, cur, hintsWithLShift, description);
|
||||
if (lres.isError()) {
|
||||
throw new SolverException("Solving left failed");
|
||||
}
|
||||
AssemblyResolution rres =
|
||||
solver.solve(exp.getRight(), reqr, vals, res, cur, hints, description);
|
||||
solver.solve(exp.getRight(), reqr, vals, cur, hints, description);
|
||||
if (rres.isError()) {
|
||||
throw new SolverException("Solving right failed");
|
||||
}
|
||||
AssemblyResolvedConstructor lsol = (AssemblyResolvedConstructor) lres;
|
||||
AssemblyResolvedConstructor rsol = (AssemblyResolvedConstructor) rres;
|
||||
AssemblyResolvedConstructor sol = lsol.combine(rsol);
|
||||
AssemblyResolvedPatterns lsol = (AssemblyResolvedPatterns) lres;
|
||||
AssemblyResolvedPatterns rsol = (AssemblyResolvedPatterns) rres;
|
||||
AssemblyResolvedPatterns sol = lsol.combine(rsol);
|
||||
if (sol == null) {
|
||||
throw new SolverException(
|
||||
"Left and right solutions conflict for shift=" + shift);
|
||||
@@ -105,6 +121,6 @@ public class LeftShiftExpressionSolver extends AbstractBinaryExpressionSolver<Le
|
||||
// try the next
|
||||
}
|
||||
}
|
||||
return super.solveTwoSided(exp, goal, vals, res, cur, hints, description);
|
||||
return super.solveTwoSided(exp, goal, vals, cur, hints, description);
|
||||
}
|
||||
}
|
||||
|
||||
+48
-13
@@ -45,6 +45,7 @@ public class MaskedLong implements Comparable<MaskedLong> {
|
||||
/**
|
||||
* Create a masked value from a mask and a long
|
||||
*
|
||||
* <p>
|
||||
* Any positions in {@code msk} set to 0 create an {@code x} in the corresponding position of
|
||||
* the result. Otherwise, the position takes the corresponding bit from {@code val}.
|
||||
*
|
||||
@@ -92,6 +93,7 @@ public class MaskedLong implements Comparable<MaskedLong> {
|
||||
/**
|
||||
* Get the mask as a long
|
||||
*
|
||||
* <p>
|
||||
* Positions with a defined bit are {@code 1}; positions with an undefined bit are {@code 0}.
|
||||
*
|
||||
* @return the mask as a long
|
||||
@@ -126,6 +128,7 @@ public class MaskedLong implements Comparable<MaskedLong> {
|
||||
/**
|
||||
* Apply an additional mask to this masked long
|
||||
*
|
||||
* <p>
|
||||
* Any {@code 0} bit in {@code msk} will result in an undefined bit in the result. {@code 1}
|
||||
* bits result in a copy of the corresponding bit in the result.
|
||||
*
|
||||
@@ -139,6 +142,7 @@ public class MaskedLong implements Comparable<MaskedLong> {
|
||||
/**
|
||||
* Sign extend the masked value, according to its mask, to a full long
|
||||
*
|
||||
* <p>
|
||||
* The leftmost defined bit is taken as the sign bit, and extended to the left.
|
||||
*
|
||||
* @return the sign-extended masked long
|
||||
@@ -151,6 +155,7 @@ public class MaskedLong implements Comparable<MaskedLong> {
|
||||
/**
|
||||
* Zero extend the masked value, according to its mask, to a full long
|
||||
*
|
||||
* <p>
|
||||
* All bits to the left of the leftmost defined bit are set to 0.
|
||||
*
|
||||
* @return the zero-extended masked long
|
||||
@@ -199,6 +204,7 @@ public class MaskedLong implements Comparable<MaskedLong> {
|
||||
/**
|
||||
* Combine this and another masked long into one, by taking defined bits from either
|
||||
*
|
||||
* <p>
|
||||
* If this masked long agrees with the other, then the two are combined. For each bit position
|
||||
* in the result, the defined bit from either corresponding position is taken. If neither is
|
||||
* defined, then the position is undefined in the result. If both are defined, they must agree.
|
||||
@@ -217,6 +223,7 @@ public class MaskedLong implements Comparable<MaskedLong> {
|
||||
/**
|
||||
* Shift {@code size} bits @{code n) positions circularly in a given direction
|
||||
*
|
||||
* <p>
|
||||
* The shifted bits are the least significant {@code size} bits. The remaining bits are
|
||||
* unaffected.
|
||||
*
|
||||
@@ -247,6 +254,7 @@ public class MaskedLong implements Comparable<MaskedLong> {
|
||||
/**
|
||||
* Shift {@code size} bits @{code n) positions circularly in a given direction
|
||||
*
|
||||
* <p>
|
||||
* The shifted bits are the least significant {@code size} bits. The remaining bits are
|
||||
* unaffected.
|
||||
*
|
||||
@@ -265,6 +273,7 @@ public class MaskedLong implements Comparable<MaskedLong> {
|
||||
/**
|
||||
* Shift the bits @{code n} positions left
|
||||
*
|
||||
* <p>
|
||||
* This implements both a signed and unsigned shift.
|
||||
*
|
||||
* @param n the number of positions.
|
||||
@@ -282,6 +291,7 @@ public class MaskedLong implements Comparable<MaskedLong> {
|
||||
/**
|
||||
* Shift the bits {@code n} positions left
|
||||
*
|
||||
* <p>
|
||||
* This implements both a signed and unsigned shift.
|
||||
*
|
||||
* @param n the number of positions.
|
||||
@@ -297,6 +307,7 @@ public class MaskedLong implements Comparable<MaskedLong> {
|
||||
/**
|
||||
* Invert a left shift of {@code n} positions, that is shift right
|
||||
*
|
||||
* <p>
|
||||
* This is different from a normal shift right, in that it inserts unknowns at the left. The
|
||||
* normal right shift inserts zeros or sign bits. Additionally, if any ones would fall off the
|
||||
* right, the inversion is undefined.
|
||||
@@ -319,6 +330,7 @@ public class MaskedLong implements Comparable<MaskedLong> {
|
||||
/**
|
||||
* Invert a left shift of {@code n} positions, that is shift right
|
||||
*
|
||||
* <p>
|
||||
* This is different from a normal shift right, in that it inserts unknowns at the left. The
|
||||
* normal right shift inserts zeros or sign bits. Additionally, if any ones would fall off the
|
||||
* right, the inversion is undefined.
|
||||
@@ -337,6 +349,7 @@ public class MaskedLong implements Comparable<MaskedLong> {
|
||||
/**
|
||||
* Shift the bits arithmetically {@code n} positions right
|
||||
*
|
||||
* <p>
|
||||
* This implements a signed shift.
|
||||
*
|
||||
* @param n the number of positions.
|
||||
@@ -352,6 +365,7 @@ public class MaskedLong implements Comparable<MaskedLong> {
|
||||
/**
|
||||
* Shift the bits arithmetically {@code n} positions right
|
||||
*
|
||||
* <p>
|
||||
* This implements a signed shift.
|
||||
*
|
||||
* @param n the number of positions.
|
||||
@@ -367,6 +381,7 @@ public class MaskedLong implements Comparable<MaskedLong> {
|
||||
/**
|
||||
* Invert an arithmetic right shift of {@code n} positions, that is shift left
|
||||
*
|
||||
* <p>
|
||||
* This is different from a normal shift left, in that it inserts unknowns at the right. The
|
||||
* normal left shift inserts zeros. Additionally, all bits that fall off the left must match the
|
||||
* resulting sign bit, or else the inversion is undefined.
|
||||
@@ -400,6 +415,7 @@ public class MaskedLong implements Comparable<MaskedLong> {
|
||||
/**
|
||||
* Invert an arithmetic right shift of {@code n} positions, that is shift left
|
||||
*
|
||||
* <p>
|
||||
* This is different from a normal shift left, in that it inserts unknowns at the right. The
|
||||
* normal left shift inserts zeros. Additionally, all bits that fall off the left must match the
|
||||
* resulting sign bit, or else the inversion is undefined.
|
||||
@@ -418,6 +434,7 @@ public class MaskedLong implements Comparable<MaskedLong> {
|
||||
/**
|
||||
* Shift the bits logically {@code n} positions right
|
||||
*
|
||||
* <p>
|
||||
* This implements an unsigned shift.
|
||||
*
|
||||
* @param n the number of positions.
|
||||
@@ -435,6 +452,7 @@ public class MaskedLong implements Comparable<MaskedLong> {
|
||||
/**
|
||||
* Shift the bits logically {@code n} positions right
|
||||
*
|
||||
* <p>
|
||||
* This implements an unsigned shift.
|
||||
*
|
||||
* @param n the number of positions.
|
||||
@@ -451,6 +469,7 @@ public class MaskedLong implements Comparable<MaskedLong> {
|
||||
/**
|
||||
* Shift the bits positionally {@code n} positions right
|
||||
*
|
||||
* <p>
|
||||
* This fills the left with unknown bits
|
||||
*
|
||||
* @param n
|
||||
@@ -463,6 +482,7 @@ public class MaskedLong implements Comparable<MaskedLong> {
|
||||
/**
|
||||
* Invert a logical right shift of {@code n} positions, that is shift left
|
||||
*
|
||||
* <p>
|
||||
* This is different from a normal shift left, in that it inserts unknowns at the right. The
|
||||
* normal left shift inserts zeros. Additionally, if any ones would fall off the left, the
|
||||
* inversion is undefined.
|
||||
@@ -486,6 +506,7 @@ public class MaskedLong implements Comparable<MaskedLong> {
|
||||
/**
|
||||
* Invert a logical right shift of {@code n} positions, that is shift left
|
||||
*
|
||||
* <p>
|
||||
* This is different from a normal shift left, in that it inserts unknowns at the right. The
|
||||
* normal left shift inserts zeros. Additionally, if any ones would fall off the left, the
|
||||
* inversion is undefined.
|
||||
@@ -504,6 +525,7 @@ public class MaskedLong implements Comparable<MaskedLong> {
|
||||
/**
|
||||
* Reverse the least significant {@code n} bytes
|
||||
*
|
||||
* <p>
|
||||
* This interprets the bits as an {@code n}-byte value and changes the endianness. Any bits
|
||||
* outside of the interpretation are truncated, i.e., become unknown.
|
||||
*
|
||||
@@ -517,16 +539,17 @@ public class MaskedLong implements Comparable<MaskedLong> {
|
||||
/**
|
||||
* Compute the bitwise AND of this and another masked long
|
||||
*
|
||||
* <p>
|
||||
* To handle unknown bits, the result is derived from the following truth table:
|
||||
*
|
||||
* <pre>{@literal
|
||||
* <pre>
|
||||
* 0 x 1 <= A (this)
|
||||
* 0 0 0 0
|
||||
* x 0 x x
|
||||
* 1 0 x 1
|
||||
* ^
|
||||
* B (that)
|
||||
* }</pre>
|
||||
* </pre>
|
||||
*
|
||||
* @param that the other masked long ({@code B}).
|
||||
* @return the result.
|
||||
@@ -547,18 +570,19 @@ public class MaskedLong implements Comparable<MaskedLong> {
|
||||
|
||||
/**
|
||||
* Solves the expression {@code A & B = C, for B, given C and A}
|
||||
*
|
||||
* <p>
|
||||
* To handle unknown bits, the solution is derived from the following truth table, where
|
||||
* {@code *} indicates no solution:
|
||||
*
|
||||
* <pre>{@literal
|
||||
* <pre>
|
||||
* 0 x 1 <= A (that)
|
||||
* 0 x x 0
|
||||
* x x x x
|
||||
* 1 * 1 1
|
||||
* ^
|
||||
* B (this)
|
||||
* }</pre>
|
||||
* </pre>
|
||||
*
|
||||
* @param that the other masked long ({@code B}).
|
||||
* @return the result.
|
||||
@@ -587,16 +611,17 @@ public class MaskedLong implements Comparable<MaskedLong> {
|
||||
/**
|
||||
* Compute the bitwise OR of this and another masked long
|
||||
*
|
||||
* <p>
|
||||
* To handle unknown bits, the result is derived from the following truth table:
|
||||
*
|
||||
* <pre>{@literal
|
||||
* <pre>
|
||||
* 0 x 1 <= A (this)
|
||||
* 0 0 x 1
|
||||
* x x x 1
|
||||
* 1 1 1 1
|
||||
* ^
|
||||
* B (that)
|
||||
* }</pre>
|
||||
* </pre>
|
||||
*
|
||||
* @param that the other masked long ({@code B}).
|
||||
* @return the result.
|
||||
@@ -620,17 +645,18 @@ public class MaskedLong implements Comparable<MaskedLong> {
|
||||
/**
|
||||
* Solves the expression A | B = C, for B, given C and A
|
||||
*
|
||||
* <p>
|
||||
* To handle unknown bits, the solution is derived from the following truth table, where
|
||||
* {@code *} indicates no solution:
|
||||
*
|
||||
* <pre>{@literal
|
||||
* <pre>
|
||||
* 0 x 1 <= A (that)
|
||||
* 0 0 0 *
|
||||
* x x x x
|
||||
* 1 1 x x
|
||||
* ^
|
||||
* B (this)
|
||||
* }</pre>
|
||||
* </pre>
|
||||
*
|
||||
* @param that the other masked long ({@code B}).
|
||||
* @return the result.
|
||||
@@ -658,16 +684,17 @@ public class MaskedLong implements Comparable<MaskedLong> {
|
||||
/**
|
||||
* Compute the bitwise XOR of this and another masked long
|
||||
*
|
||||
* <p>
|
||||
* To handle unknown bits, the result is derived from the following truth table:
|
||||
*
|
||||
* <pre>{@literal
|
||||
* <pre>
|
||||
* 0 x 1 <= A (this)
|
||||
* 0 0 x 1
|
||||
* x x x x
|
||||
* 1 1 x 0
|
||||
* ^
|
||||
* B (that)
|
||||
* }</pre>
|
||||
* </pre>
|
||||
*
|
||||
* @param that the other masked long ({@code B}).
|
||||
* @return the result.
|
||||
@@ -696,12 +723,13 @@ public class MaskedLong implements Comparable<MaskedLong> {
|
||||
/**
|
||||
* Compute the bitwise NOT
|
||||
*
|
||||
* <p>
|
||||
* To handle unknown bits, the result is derived from the following truth table:
|
||||
*
|
||||
* <pre>{@literal
|
||||
* <pre>
|
||||
* 0 x 1 <= A (this)
|
||||
* 1 x 0
|
||||
* }</pre>
|
||||
* </pre>
|
||||
*
|
||||
* @return the result.
|
||||
*/
|
||||
@@ -769,7 +797,7 @@ public class MaskedLong implements Comparable<MaskedLong> {
|
||||
if (lmv == 2 || rmv == 2) {
|
||||
return 2;
|
||||
}
|
||||
else if (lmv == 3 || rmv == 3) {
|
||||
else if (lmv == 3 && rmv == 3) {
|
||||
return 3;
|
||||
}
|
||||
return 0;
|
||||
@@ -893,6 +921,7 @@ public class MaskedLong implements Comparable<MaskedLong> {
|
||||
/**
|
||||
* Compute the arithmetic quotient as a solution to unsigned multiplication
|
||||
*
|
||||
* <p>
|
||||
* This is slightly different than {@link #divideUnsigned(MaskedLong)} in its treatment of
|
||||
* unknowns.
|
||||
*
|
||||
@@ -924,6 +953,7 @@ public class MaskedLong implements Comparable<MaskedLong> {
|
||||
/**
|
||||
* Checks if this and another masked long agree
|
||||
*
|
||||
* <p>
|
||||
* Two masked longs agree iff their corresponding defined bit positions are equal. Where either
|
||||
* or both positions are undefined, no check is applied. In the case that both masked longs are
|
||||
* fully-defined, this is the same as an equality check on the values.
|
||||
@@ -942,6 +972,7 @@ public class MaskedLong implements Comparable<MaskedLong> {
|
||||
/**
|
||||
* Checks if this and a long agree
|
||||
*
|
||||
* <p>
|
||||
* The masked long agrees with the given long iff the masked long's defined bit positions agree
|
||||
* with the corresponding bit positions in the given long. Where there are undefined bits, no
|
||||
* check is applied. In the case that the masked long is fully-defined, this is the same as an
|
||||
@@ -978,10 +1009,12 @@ public class MaskedLong implements Comparable<MaskedLong> {
|
||||
/**
|
||||
* Check if the masked value falls within a given range
|
||||
*
|
||||
* <p>
|
||||
* The range is defined by a maximum and a signedness. The maximum must be one less than a
|
||||
* positive power of 2. In other words, it defines a maximum number of bits, including the sign
|
||||
* bit if applicable.
|
||||
*
|
||||
* <p>
|
||||
* The defined bits of this masked long are then checked to fall in the given range. The
|
||||
* effective value is derived by sign/zero extending the value according to its mask. In
|
||||
* general, if any {@code 1} bits exist outside of the given max, the value is rejected, unless
|
||||
@@ -1013,6 +1046,7 @@ public class MaskedLong implements Comparable<MaskedLong> {
|
||||
/**
|
||||
* "Compare" two masked longs
|
||||
*
|
||||
* <p>
|
||||
* This is not meant to reflect a numerical comparison. Rather, this is just to impose an
|
||||
* ordering for the sake of storing these in sorted collections.
|
||||
*/
|
||||
@@ -1038,6 +1072,7 @@ public class MaskedLong implements Comparable<MaskedLong> {
|
||||
/**
|
||||
* Check for equality
|
||||
*
|
||||
* <p>
|
||||
* This will only return true if the other object is a masked long, even if this one is
|
||||
* fully-defined, and the value is equal to a given long (or {@link Long}). The other masked
|
||||
* long must have the same mask and value to be considered equal. For other sorts of "equality"
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@ package ghidra.app.plugin.assembler.sleigh.expr;
|
||||
import ghidra.app.plugin.processors.sleigh.expression.MinusExpression;
|
||||
|
||||
/**
|
||||
* Solves expressions of the form -A
|
||||
* Solves expressions of the form {@code -A}
|
||||
*/
|
||||
public class MinusExpressionSolver extends AbstractUnaryExpressionSolver<MinusExpression> {
|
||||
|
||||
|
||||
+15
-16
@@ -19,12 +19,12 @@ import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import ghidra.app.plugin.assembler.sleigh.sem.AssemblyResolution;
|
||||
import ghidra.app.plugin.assembler.sleigh.sem.AssemblyResolvedConstructor;
|
||||
import ghidra.app.plugin.assembler.sleigh.sem.AssemblyResolvedPatterns;
|
||||
import ghidra.app.plugin.processors.sleigh.expression.MultExpression;
|
||||
import ghidra.app.plugin.processors.sleigh.expression.PatternExpression;
|
||||
|
||||
/**
|
||||
* Solves expressions of the form A * B
|
||||
* Solves expressions of the form {@code A * B}
|
||||
*/
|
||||
public class MultExpressionSolver extends AbstractBinaryExpressionSolver<MultExpression> {
|
||||
|
||||
@@ -103,25 +103,24 @@ public class MultExpressionSolver extends AbstractBinaryExpressionSolver<MultExp
|
||||
}
|
||||
|
||||
protected AssemblyResolution tryRep(PatternExpression lexp, MaskedLong rval, MaskedLong repGoal,
|
||||
MaskedLong goal, Map<String, Long> vals, Map<Integer, Object> res,
|
||||
AssemblyResolvedConstructor cur, Set<SolverHint> hints, String description)
|
||||
throws NeedsBackfillException {
|
||||
MaskedLong goal, Map<String, Long> vals, AssemblyResolvedPatterns cur,
|
||||
Set<SolverHint> hints, String description) throws NeedsBackfillException {
|
||||
MaskedLong lval = repGoal.divideUnsigned(rval);
|
||||
if (lval.multiply(rval).agrees(goal)) {
|
||||
return solver.solve(lexp, lval, vals, res, cur, hints, description);
|
||||
return solver.solve(lexp, lval, vals, cur, hints, description);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AssemblyResolution solveLeftSide(PatternExpression lexp, MaskedLong rval,
|
||||
MaskedLong goal, Map<String, Long> vals, Map<Integer, Object> res,
|
||||
AssemblyResolvedConstructor cur, Set<SolverHint> hints, String description)
|
||||
MaskedLong goal, Map<String, Long> vals, AssemblyResolvedPatterns cur,
|
||||
Set<SolverHint> hints, String description)
|
||||
throws NeedsBackfillException, SolverException {
|
||||
// Try the usual case first
|
||||
ResultTracker tracker = new ResultTracker();
|
||||
AssemblyResolution sol = tracker.trySolverFunc(() -> {
|
||||
return super.solveLeftSide(lexp, rval, goal, vals, res, cur, hints, description);
|
||||
return super.solveLeftSide(lexp, rval, goal, vals, cur, hints, description);
|
||||
});
|
||||
if (sol != null) {
|
||||
return sol;
|
||||
@@ -151,8 +150,8 @@ public class MultExpressionSolver extends AbstractBinaryExpressionSolver<MultExp
|
||||
if (reps > 0) {
|
||||
MaskedLong repRightGoal = MaskedLong.fromMaskAndValue(repMsk, repVal);
|
||||
sol = tracker.trySolverFunc(() -> {
|
||||
return tryRep(lexp, rval, repRightGoal, goal, vals, res, cur,
|
||||
hintsWithRepetition, description);
|
||||
return tryRep(lexp, rval, repRightGoal, goal, vals, cur, hintsWithRepetition,
|
||||
description);
|
||||
});
|
||||
if (sol != null) {
|
||||
return sol;
|
||||
@@ -169,8 +168,8 @@ public class MultExpressionSolver extends AbstractBinaryExpressionSolver<MultExp
|
||||
repMsk = -1L >>> i;
|
||||
MaskedLong repLeftGoal = MaskedLong.fromMaskAndValue(repMsk, repVal);
|
||||
sol = tracker.trySolverFunc(() -> {
|
||||
return tryRep(lexp, rval, repLeftGoal, goal, vals, res, cur,
|
||||
hintsWithRepetition, description);
|
||||
return tryRep(lexp, rval, repLeftGoal, goal, vals, cur, hintsWithRepetition,
|
||||
description);
|
||||
});
|
||||
if (sol != null) {
|
||||
return sol;
|
||||
@@ -182,10 +181,10 @@ public class MultExpressionSolver extends AbstractBinaryExpressionSolver<MultExp
|
||||
|
||||
@Override
|
||||
protected AssemblyResolution solveRightSide(PatternExpression rexp, MaskedLong lval,
|
||||
MaskedLong goal, Map<String, Long> vals, Map<Integer, Object> res,
|
||||
AssemblyResolvedConstructor cur, Set<SolverHint> hints, String description)
|
||||
MaskedLong goal, Map<String, Long> vals, AssemblyResolvedPatterns cur,
|
||||
Set<SolverHint> hints, String description)
|
||||
throws NeedsBackfillException, SolverException {
|
||||
return solveLeftSide(rexp, lval, goal, vals, res, cur, hints, description);
|
||||
return solveLeftSide(rexp, lval, goal, vals, cur, hints, description);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+5
-1
@@ -18,13 +18,15 @@ package ghidra.app.plugin.assembler.sleigh.expr;
|
||||
/**
|
||||
* An exception to indicate that the solution of an expression is not yet known
|
||||
*
|
||||
* <p>
|
||||
* Furthermore, it cannot be determined whether or not the expression is even solvable. When this
|
||||
* exception is thrown, a backfill record is placed on the encoded resolution indicating that
|
||||
* exception is thrown, a backfill record is placed on the encoded resolution indicating that the
|
||||
* resolver must attempt to solve the expression again, once the encoding is otherwise complete.
|
||||
* This is needed, most notably, when an encoding depends on the address of the <em>next</em>
|
||||
* instruction, because the length of the current instruction is not known until resolution has
|
||||
* finished.
|
||||
*
|
||||
* <p>
|
||||
* Backfill becomes a possibility when an expression depends on a symbol that is not (yet) defined.
|
||||
* Thus, as a matter of good record keeping, the exception takes the name of the missing symbol.
|
||||
*/
|
||||
@@ -33,6 +35,7 @@ public class NeedsBackfillException extends SolverException {
|
||||
|
||||
/**
|
||||
* Construct a backfill exception, resulting from the given missing symbol name
|
||||
*
|
||||
* @param symbol the missing symbol name
|
||||
*/
|
||||
public NeedsBackfillException(String symbol) {
|
||||
@@ -42,6 +45,7 @@ public class NeedsBackfillException extends SolverException {
|
||||
|
||||
/**
|
||||
* Retrieve the missing symbol name from the original solution attempt
|
||||
*
|
||||
* @return the missing symbol name
|
||||
*/
|
||||
public String getSymbol() {
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@ package ghidra.app.plugin.assembler.sleigh.expr;
|
||||
import ghidra.app.plugin.processors.sleigh.expression.NotExpression;
|
||||
|
||||
/**
|
||||
* Solves expressions of the form ~A
|
||||
* Solves expressions of the form {@code ~A}
|
||||
*/
|
||||
public class NotExpressionSolver extends AbstractUnaryExpressionSolver<NotExpression> {
|
||||
|
||||
|
||||
+21
-18
@@ -28,6 +28,7 @@ import ghidra.app.plugin.processors.sleigh.symbol.TripleSymbol;
|
||||
/**
|
||||
* Solves expressions of an operand value
|
||||
*
|
||||
* <p>
|
||||
* These are a sort of named sub-expression, but they may also specify a shift in encoding.
|
||||
*/
|
||||
public class OperandValueSolver extends AbstractExpressionSolver<OperandValue> {
|
||||
@@ -39,12 +40,13 @@ public class OperandValueSolver extends AbstractExpressionSolver<OperandValue> {
|
||||
/**
|
||||
* Obtains the "defining expression"
|
||||
*
|
||||
* <p>
|
||||
* This is either the symbols assigned defining expression, or the expression associated with
|
||||
* its defining symbol.
|
||||
*
|
||||
* @return the defining expression, or null if neither is available
|
||||
*/
|
||||
protected PatternExpression getDefiningExpression(OperandSymbol sym) {
|
||||
public static PatternExpression getDefiningExpression(OperandSymbol sym) {
|
||||
PatternExpression patexp = sym.getDefiningExpression();
|
||||
if (patexp != null) {
|
||||
return patexp;
|
||||
@@ -59,62 +61,63 @@ public class OperandValueSolver extends AbstractExpressionSolver<OperandValue> {
|
||||
|
||||
@Override
|
||||
public AssemblyResolution solve(OperandValue ov, MaskedLong goal, Map<String, Long> vals,
|
||||
Map<Integer, Object> res, AssemblyResolvedConstructor cur, Set<SolverHint> hints,
|
||||
String description) throws NeedsBackfillException {
|
||||
AssemblyResolvedPatterns cur, Set<SolverHint> hints, String description)
|
||||
throws NeedsBackfillException {
|
||||
Constructor cons = ov.getConstructor();
|
||||
OperandSymbol sym = cons.getOperand(ov.getIndex());
|
||||
PatternExpression patexp = getDefiningExpression(sym);
|
||||
if (patexp == null) {
|
||||
if (goal.equals(MaskedLong.ZERO)) {
|
||||
return AssemblyResolution.nop(description, null);
|
||||
return AssemblyResolution.nop(description, null, null);
|
||||
}
|
||||
return AssemblyResolution.error("Operand " + sym.getName() +
|
||||
" is undefined and does not agree with child requirements", description, null);
|
||||
" is undefined and does not agree with child requirements", description);
|
||||
}
|
||||
AssemblyResolution result = solver.solve(patexp, goal, vals, res, cur, hints, description);
|
||||
AssemblyResolution result = solver.solve(patexp, goal, vals, cur, hints, description);
|
||||
if (result.isError()) {
|
||||
AssemblyResolvedError err = (AssemblyResolvedError) result;
|
||||
return AssemblyResolution.error(err.getError(),
|
||||
"Solution to " + sym.getName() + " := " + goal + " = " + patexp,
|
||||
List.of(result));
|
||||
List.of(result), null);
|
||||
}
|
||||
// TODO: Shifting here seems like a hack to me.
|
||||
// I assume this only comes at the top of an expression
|
||||
AssemblyResolvedConstructor con = (AssemblyResolvedConstructor) result;
|
||||
int shamt = AssemblyTreeResolver.computeOffset(sym, cons, res);
|
||||
AssemblyResolvedPatterns con = (AssemblyResolvedPatterns) result;
|
||||
int shamt = AssemblyTreeResolver.computeOffset(sym, cons);
|
||||
return con.shift(shamt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MaskedLong getValue(OperandValue ov, Map<String, Long> vals, Map<Integer, Object> res,
|
||||
AssemblyResolvedConstructor cur) throws NeedsBackfillException {
|
||||
public MaskedLong getValue(OperandValue ov, Map<String, Long> vals,
|
||||
AssemblyResolvedPatterns cur) throws NeedsBackfillException {
|
||||
Constructor cons = ov.getConstructor();
|
||||
OperandSymbol sym = cons.getOperand(ov.getIndex());
|
||||
PatternExpression patexp = getDefiningExpression(sym);
|
||||
if (patexp == null) {
|
||||
return MaskedLong.ZERO;
|
||||
}
|
||||
int shamt = AssemblyTreeResolver.computeOffset(sym, cons, res);
|
||||
int shamt = AssemblyTreeResolver.computeOffset(sym, cons);
|
||||
cur = cur == null ? null : cur.truncate(shamt);
|
||||
MaskedLong result = solver.getValue(patexp, vals, res, cur);
|
||||
MaskedLong result = solver.getValue(patexp, vals, cur);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getInstructionLength(OperandValue ov, Map<Integer, Object> res) {
|
||||
public int getInstructionLength(OperandValue ov) {
|
||||
Constructor cons = ov.getConstructor();
|
||||
OperandSymbol sym = cons.getOperand(ov.getIndex());
|
||||
PatternExpression patexp = sym.getDefiningExpression();
|
||||
if (patexp == null) {
|
||||
return 0;
|
||||
}
|
||||
int length = solver.getInstructionLength(patexp, res);
|
||||
int shamt = AssemblyTreeResolver.computeOffset(sym, cons, res);
|
||||
int length = solver.getInstructionLength(patexp);
|
||||
int shamt = AssemblyTreeResolver.computeOffset(sym, cons);
|
||||
return length + shamt;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MaskedLong valueForResolution(OperandValue ov, AssemblyResolvedConstructor rc) {
|
||||
public MaskedLong valueForResolution(OperandValue ov, Map<String, Long> vals,
|
||||
AssemblyResolvedPatterns rc) {
|
||||
Constructor cons = ov.getConstructor();
|
||||
OperandSymbol sym = cons.getOperand(ov.getIndex());
|
||||
PatternExpression patexp = sym.getDefiningExpression();
|
||||
@@ -135,7 +138,7 @@ public class OperandValueSolver extends AbstractExpressionSolver<OperandValue> {
|
||||
// Since I'm using this just for context, ignore shifting for now.
|
||||
//int shamt = AssemblyTreeResolver.computeOffset(sym, cons, rc.children);
|
||||
// Children would be null here, anyway.
|
||||
return solver.valueForResolution(patexp, rc);
|
||||
return solver.valueForResolution(patexp, vals, rc);
|
||||
// NOTE: To be paranoid, I could check for the existence of TokenField in the expression
|
||||
// And also check if a shift would be performed.
|
||||
}
|
||||
|
||||
+81
-63
@@ -17,35 +17,27 @@ package ghidra.app.plugin.assembler.sleigh.expr;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import ghidra.app.plugin.assembler.sleigh.expr.match.ExpressionMatcher;
|
||||
import ghidra.app.plugin.assembler.sleigh.sem.*;
|
||||
import ghidra.app.plugin.assembler.sleigh.util.DbgTimer.DbgCtx;
|
||||
import ghidra.app.plugin.processors.sleigh.ParserWalker;
|
||||
import ghidra.app.plugin.processors.sleigh.SleighLanguage;
|
||||
import ghidra.app.plugin.processors.sleigh.expression.*;
|
||||
import ghidra.program.model.mem.MemoryAccessException;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.xml.XmlPullParser;
|
||||
|
||||
/**
|
||||
* Solves expressions of the form A | B
|
||||
* Solves expressions of the form {@code A | B}
|
||||
*/
|
||||
public class OrExpressionSolver extends AbstractBinaryExpressionSolver<OrExpression> {
|
||||
static final PatternExpression DUMMY = new PatternExpression() {
|
||||
@Override
|
||||
public long getValue(ParserWalker walker) throws MemoryAccessException {
|
||||
return 0;
|
||||
}
|
||||
protected static class Matchers implements ExpressionMatcher.Context {
|
||||
protected ExpressionMatcher<ConstantValue> val = var(ConstantValue.class);
|
||||
protected ExpressionMatcher<ConstantValue> size = var(ConstantValue.class);
|
||||
protected ExpressionMatcher<PatternValue> fld = fldSz(size);
|
||||
|
||||
@Override
|
||||
public void restoreXml(XmlPullParser parser, SleighLanguage lang) {
|
||||
// Dummy intentionally left empty
|
||||
}
|
||||
protected ExpressionMatcher<?> neqConst = or(
|
||||
and(shr(sub(opnd(fld), val), size), cv(1)),
|
||||
and(shr(sub(val, opnd(fld)), size), cv(1)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
protected static final Matchers MATCHERS = new Matchers();
|
||||
|
||||
public OrExpressionSolver() {
|
||||
super(OrExpression.class);
|
||||
@@ -62,8 +54,8 @@ public class OrExpressionSolver extends AbstractBinaryExpressionSolver<OrExpress
|
||||
}
|
||||
|
||||
protected AssemblyResolution tryCatenationExpression(OrExpression exp, MaskedLong goal,
|
||||
Map<String, Long> vals, Map<Integer, Object> res, AssemblyResolvedConstructor cur,
|
||||
Set<SolverHint> hints, String description) throws SolverException {
|
||||
Map<String, Long> vals, AssemblyResolvedPatterns cur, Set<SolverHint> hints,
|
||||
String description) throws SolverException {
|
||||
/*
|
||||
* If OR is being used to concatenate fields, then we can solve with some symbolic
|
||||
* manipulation. We'll descend to see if this is a tree of ORs with SHIFTs or fields at the
|
||||
@@ -71,12 +63,12 @@ public class OrExpressionSolver extends AbstractBinaryExpressionSolver<OrExpress
|
||||
* component independently.
|
||||
*/
|
||||
Map<Long, PatternExpression> fields = new TreeMap<>();
|
||||
fields.put(0L, new ConstantValue(0));
|
||||
collectComponentsOr(exp, 0, fields, vals, res, cur);
|
||||
collectComponentsOr(exp, 0, fields, vals, cur);
|
||||
fields.computeIfAbsent(0L, __ -> new ConstantValue(0));
|
||||
fields.put(64L, new ConstantValue(0));
|
||||
long lo = 0;
|
||||
PatternExpression fieldExp = null;
|
||||
AssemblyResolvedConstructor result = AssemblyResolution.nop(description, null);
|
||||
AssemblyResolvedPatterns result = AssemblyResolution.nop(description);
|
||||
try (DbgCtx dc = dbg.start("Trying solution of field catenation")) {
|
||||
dbg.println("Original: " + goal + ":= " + exp);
|
||||
for (Map.Entry<Long, PatternExpression> ent : fields.entrySet()) {
|
||||
@@ -89,12 +81,12 @@ public class OrExpressionSolver extends AbstractBinaryExpressionSolver<OrExpress
|
||||
dbg.println("Part(" + hi + ":" + lo + "]:= " + fieldExp);
|
||||
MaskedLong part = goal.shiftLeft(64 - hi).shiftRightPositional(64 - hi + lo);
|
||||
dbg.println("Solving: " + part + ":= " + fieldExp);
|
||||
AssemblyResolution sol = solver.solve(fieldExp, part, vals, res, cur, hints,
|
||||
AssemblyResolution sol = solver.solve(fieldExp, part, vals, cur, hints,
|
||||
description + " with shift " + lo);
|
||||
if (sol.isError()) {
|
||||
return sol;
|
||||
}
|
||||
result = result.combine((AssemblyResolvedConstructor) sol);
|
||||
result = result.combine((AssemblyResolvedPatterns) sol);
|
||||
if (result == null) {
|
||||
throw new SolverException("Solutions to individual fields produced conflict");
|
||||
}
|
||||
@@ -107,8 +99,8 @@ public class OrExpressionSolver extends AbstractBinaryExpressionSolver<OrExpress
|
||||
}
|
||||
|
||||
protected AssemblyResolution tryCircularShiftExpression(OrExpression exp, MaskedLong goal,
|
||||
Map<String, Long> vals, Map<Integer, Object> res, AssemblyResolvedConstructor cur,
|
||||
Set<SolverHint> hints, String description) throws SolverException {
|
||||
Map<String, Long> vals, AssemblyResolvedPatterns cur, Set<SolverHint> hints,
|
||||
String description) throws SolverException {
|
||||
// If OR is being used to accomplish a circular shift, then we can apply a clever solver.
|
||||
// We'll match against the patterns: (f << (C - g)) | (f >> g)
|
||||
// (f >> (C - g)) | (f << g)
|
||||
@@ -144,7 +136,7 @@ public class OrExpressionSolver extends AbstractBinaryExpressionSolver<OrExpress
|
||||
expShift = sub.getRight();
|
||||
if (expShift.equals(s2)) {
|
||||
PatternExpression c = sub.getLeft();
|
||||
MaskedLong cc = solver.getValue(c, vals, res, cur);
|
||||
MaskedLong cc = solver.getValue(c, vals, cur);
|
||||
if (cc.isFullyDefined()) {
|
||||
// the left side has the subtraction, so the overall shift is the opposite
|
||||
// of the direction of the shift on the left
|
||||
@@ -158,7 +150,7 @@ public class OrExpressionSolver extends AbstractBinaryExpressionSolver<OrExpress
|
||||
expShift = sub.getRight();
|
||||
if (expShift.equals(s1)) {
|
||||
PatternExpression c = sub.getLeft();
|
||||
MaskedLong cc = solver.getValue(c, vals, res, cur);
|
||||
MaskedLong cc = solver.getValue(c, vals, cur);
|
||||
if (cc.isFullyDefined()) {
|
||||
// the right side has the subtraction, so the overall shift is the same
|
||||
// as the direction of the shift on the left
|
||||
@@ -174,16 +166,16 @@ public class OrExpressionSolver extends AbstractBinaryExpressionSolver<OrExpress
|
||||
// At this point, I know it's a circular shift
|
||||
dbg.println("Identified circular shift: value:= " + expValu1 + ", shift:= " + expShift +
|
||||
", size:= " + size + ", dir:= " + (dir == 1 ? "right" : "left"));
|
||||
return solveLeftCircularShift(expValu1, expShift, size, dir, goal, vals, res, cur, hints,
|
||||
return solveLeftCircularShift(expValu1, expShift, size, dir, goal, vals, cur, hints,
|
||||
description);
|
||||
}
|
||||
|
||||
protected AssemblyResolution solveLeftCircularShift(PatternExpression expValue,
|
||||
PatternExpression expShift, int size, int dir, MaskedLong goal, Map<String, Long> vals,
|
||||
Map<Integer, Object> res, AssemblyResolvedConstructor cur, Set<SolverHint> hints,
|
||||
String description) throws NeedsBackfillException, SolverException {
|
||||
MaskedLong valValue = solver.getValue(expValue, vals, res, cur);
|
||||
MaskedLong valShift = solver.getValue(expShift, vals, res, cur);
|
||||
AssemblyResolvedPatterns cur, Set<SolverHint> hints, String description)
|
||||
throws NeedsBackfillException, SolverException {
|
||||
MaskedLong valValue = solver.getValue(expValue, vals, cur);
|
||||
MaskedLong valShift = solver.getValue(expShift, vals, cur);
|
||||
|
||||
if (valValue != null && !valValue.isFullyDefined()) {
|
||||
if (!valValue.isFullyUndefined()) {
|
||||
@@ -202,12 +194,12 @@ public class OrExpressionSolver extends AbstractBinaryExpressionSolver<OrExpress
|
||||
throw new AssertionError("Should not have constants when solving special forms");
|
||||
}
|
||||
else if (valValue != null) {
|
||||
return solver.solve(expShift, computeCircShiftG(valValue, size, dir, goal), vals, res,
|
||||
cur, hints, description);
|
||||
return solver.solve(expShift, computeCircShiftG(valValue, size, dir, goal), vals, cur,
|
||||
hints, description);
|
||||
}
|
||||
else if (valShift != null) {
|
||||
return solver.solve(expValue, computeCircShiftF(valShift, size, dir, goal), vals, res,
|
||||
cur, hints, description);
|
||||
return solver.solve(expValue, computeCircShiftF(valShift, size, dir, goal), vals, cur,
|
||||
hints, description);
|
||||
}
|
||||
|
||||
// Oiy. Try guessing the shift amount, starting at 0
|
||||
@@ -221,21 +213,21 @@ public class OrExpressionSolver extends AbstractBinaryExpressionSolver<OrExpress
|
||||
try {
|
||||
MaskedLong reqShift = MaskedLong.fromLong(shift);
|
||||
MaskedLong reqValue = computeCircShiftF(reqShift, size, dir, goal);
|
||||
AssemblyResolution resValue = solver.solve(expValue, reqValue, vals, res, cur,
|
||||
AssemblyResolution resValue = solver.solve(expValue, reqValue, vals, cur,
|
||||
hintsWithCircularShift, description);
|
||||
if (resValue.isError()) {
|
||||
AssemblyResolvedError err = (AssemblyResolvedError) resValue;
|
||||
throw new SolverException("Solving f failed: " + err.getError());
|
||||
}
|
||||
AssemblyResolution resShift =
|
||||
solver.solve(expShift, reqShift, vals, res, cur, hints, description);
|
||||
solver.solve(expShift, reqShift, vals, cur, hints, description);
|
||||
if (resShift.isError()) {
|
||||
AssemblyResolvedError err = (AssemblyResolvedError) resShift;
|
||||
throw new SolverException("Solving g failed: " + err.getError());
|
||||
}
|
||||
AssemblyResolvedConstructor solValue = (AssemblyResolvedConstructor) resValue;
|
||||
AssemblyResolvedConstructor solShift = (AssemblyResolvedConstructor) resShift;
|
||||
AssemblyResolvedConstructor sol = solValue.combine(solShift);
|
||||
AssemblyResolvedPatterns solValue = (AssemblyResolvedPatterns) resValue;
|
||||
AssemblyResolvedPatterns solShift = (AssemblyResolvedPatterns) resShift;
|
||||
AssemblyResolvedPatterns sol = solValue.combine(solShift);
|
||||
if (sol == null) {
|
||||
throw new SolverException(
|
||||
"value and shift solutions conflict for shift=" + shift);
|
||||
@@ -276,11 +268,10 @@ public class OrExpressionSolver extends AbstractBinaryExpressionSolver<OrExpress
|
||||
|
||||
@Override
|
||||
protected AssemblyResolution solveTwoSided(OrExpression exp, MaskedLong goal,
|
||||
Map<String, Long> vals, Map<Integer, Object> res, AssemblyResolvedConstructor cur,
|
||||
Set<SolverHint> hints, String description)
|
||||
throws NeedsBackfillException, SolverException {
|
||||
Map<String, Long> vals, AssemblyResolvedPatterns cur, Set<SolverHint> hints,
|
||||
String description) throws NeedsBackfillException, SolverException {
|
||||
try {
|
||||
return tryCatenationExpression(exp, goal, vals, res, cur, hints, description);
|
||||
return tryCatenationExpression(exp, goal, vals, cur, hints, description);
|
||||
}
|
||||
catch (Exception e) {
|
||||
dbg.println("while solving: " + goal + "=:" + exp);
|
||||
@@ -288,46 +279,73 @@ public class OrExpressionSolver extends AbstractBinaryExpressionSolver<OrExpress
|
||||
}
|
||||
|
||||
try {
|
||||
return tryCircularShiftExpression(exp, goal, vals, res, cur, hints, description);
|
||||
return tryCircularShiftExpression(exp, goal, vals, cur, hints, description);
|
||||
}
|
||||
catch (Exception e) {
|
||||
dbg.println("while solving: " + goal + "=:" + exp);
|
||||
dbg.println(e.getMessage());
|
||||
}
|
||||
|
||||
Map<ExpressionMatcher<?>, PatternExpression> match = MATCHERS.neqConst.match(exp);
|
||||
if (match != null) {
|
||||
long value = MATCHERS.val.get(match).getValue();
|
||||
PatternValue field = MATCHERS.fld.get(match);
|
||||
// Solve for equals, then either return that, or forbid it, depending on goal
|
||||
AssemblyResolution solution =
|
||||
solver.solve(field, MaskedLong.fromLong(value), vals, cur, hints, description);
|
||||
if (goal.equals(MaskedLong.fromMaskAndValue(0, 1))) {
|
||||
return solution;
|
||||
}
|
||||
if (goal.equals(MaskedLong.fromMaskAndValue(1, 1))) {
|
||||
if (solution.isError()) {
|
||||
return AssemblyResolution.nop(description);
|
||||
}
|
||||
if (solution.isBackfill()) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
AssemblyResolvedPatterns forbidden = (AssemblyResolvedPatterns) solution;
|
||||
forbidden = forbidden.withDescription("Solved 'not equals'");
|
||||
return AssemblyResolution.nop(description).withForbids(Set.of(forbidden));
|
||||
}
|
||||
}
|
||||
|
||||
throw new SolverException("Could not solve two-sided OR");
|
||||
}
|
||||
|
||||
void collectComponents(PatternExpression exp, long shift,
|
||||
Map<Long, PatternExpression> components, Map<String, Long> vals,
|
||||
Map<Integer, Object> res, AssemblyResolvedConstructor cur) throws SolverException {
|
||||
AssemblyResolvedPatterns cur) throws SolverException {
|
||||
if (exp instanceof OrExpression) {
|
||||
collectComponentsOr((OrExpression) exp, shift, components, vals, res, cur);
|
||||
collectComponentsOr((OrExpression) exp, shift, components, vals, cur);
|
||||
}
|
||||
else if (exp instanceof LeftShiftExpression) {
|
||||
collectComponentsLeft((LeftShiftExpression) exp, shift, components, vals, res, cur);
|
||||
collectComponentsLeft((LeftShiftExpression) exp, shift, components, vals, cur);
|
||||
}
|
||||
else if (exp instanceof RightShiftExpression) {
|
||||
collectComponentsRight((RightShiftExpression) exp, shift, components, vals, res, cur);
|
||||
collectComponentsRight((RightShiftExpression) exp, shift, components, vals, cur);
|
||||
}
|
||||
else {
|
||||
assert shift < 64;
|
||||
components.put(shift, exp);
|
||||
PatternExpression conflict = components.put(shift, exp);
|
||||
if (conflict != null) {
|
||||
throw new SolverException("Two 'fields' at the same shift indicates conflict");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void collectComponentsOr(OrExpression exp, long shift, Map<Long, PatternExpression> components,
|
||||
Map<String, Long> vals, Map<Integer, Object> res, AssemblyResolvedConstructor cur)
|
||||
Map<String, Long> vals, AssemblyResolvedPatterns cur)
|
||||
throws SolverException {
|
||||
collectComponents(exp.getLeft(), shift, components, vals, res, cur);
|
||||
collectComponents(exp.getRight(), shift, components, vals, res, cur);
|
||||
collectComponents(exp.getLeft(), shift, components, vals, cur);
|
||||
collectComponents(exp.getRight(), shift, components, vals, cur);
|
||||
}
|
||||
|
||||
void collectComponentsLeft(LeftShiftExpression exp, long shift,
|
||||
Map<Long, PatternExpression> components, Map<String, Long> vals,
|
||||
Map<Integer, Object> res, AssemblyResolvedConstructor cur) throws SolverException {
|
||||
AssemblyResolvedPatterns cur) throws SolverException {
|
||||
MaskedLong adj;
|
||||
try {
|
||||
adj = solver.getValue(exp.getRight(), vals, res, cur);
|
||||
adj = solver.getValue(exp.getRight(), vals, cur);
|
||||
}
|
||||
catch (NeedsBackfillException e) {
|
||||
throw new SolverException("Variable shifts break field catenation solver", e);
|
||||
@@ -335,15 +353,15 @@ public class OrExpressionSolver extends AbstractBinaryExpressionSolver<OrExpress
|
||||
if (adj == null || !adj.isFullyDefined()) {
|
||||
throw new SolverException("Variable shifts break field catenation solver");
|
||||
}
|
||||
collectComponents(exp.getLeft(), shift + adj.val, components, vals, res, cur);
|
||||
collectComponents(exp.getLeft(), shift + adj.val, components, vals, cur);
|
||||
}
|
||||
|
||||
void collectComponentsRight(RightShiftExpression exp, long shift,
|
||||
Map<Long, PatternExpression> components, Map<String, Long> vals,
|
||||
Map<Integer, Object> res, AssemblyResolvedConstructor cur) throws SolverException {
|
||||
AssemblyResolvedPatterns cur) throws SolverException {
|
||||
MaskedLong adj;
|
||||
try {
|
||||
adj = solver.getValue(exp.getRight(), vals, res, cur);
|
||||
adj = solver.getValue(exp.getRight(), vals, cur);
|
||||
}
|
||||
catch (NeedsBackfillException e) {
|
||||
throw new SolverException("Variable shifts break field catenation solver", e);
|
||||
@@ -351,6 +369,6 @@ public class OrExpressionSolver extends AbstractBinaryExpressionSolver<OrExpress
|
||||
if (adj == null || !adj.isFullyDefined()) {
|
||||
throw new SolverException("Variable shifts break field catenation solver");
|
||||
}
|
||||
collectComponents(exp.getLeft(), shift - adj.val, components, vals, res, cur);
|
||||
collectComponents(exp.getLeft(), shift - adj.val, components, vals, cur);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@ package ghidra.app.plugin.assembler.sleigh.expr;
|
||||
import ghidra.app.plugin.processors.sleigh.expression.PlusExpression;
|
||||
|
||||
/**
|
||||
* Solves expressions of the form A + B
|
||||
* Solves expressions of the form {@code A + B}
|
||||
*/
|
||||
public class PlusExpressionSolver extends AbstractBinaryExpressionSolver<PlusExpression> {
|
||||
|
||||
|
||||
+40
-42
@@ -18,24 +18,30 @@ package ghidra.app.plugin.assembler.sleigh.expr;
|
||||
import java.util.*;
|
||||
|
||||
import ghidra.app.plugin.assembler.sleigh.sem.AssemblyResolution;
|
||||
import ghidra.app.plugin.assembler.sleigh.sem.AssemblyResolvedConstructor;
|
||||
import ghidra.app.plugin.assembler.sleigh.sem.AssemblyResolvedPatterns;
|
||||
import ghidra.app.plugin.assembler.sleigh.util.DbgTimer;
|
||||
import ghidra.app.plugin.processors.sleigh.expression.PatternExpression;
|
||||
|
||||
/**
|
||||
* This singleton class seeks solutions to {@link PatternExpression}s
|
||||
*
|
||||
* It is called naive, because it does not perform algebraic transformations. Rather, it attempts to
|
||||
* fold constants, assuming there is a single variable in the expression, modifying the goal as it
|
||||
* <p>
|
||||
* It is rather naive. It does not perform algebraic transformations. Instead, it attempts to fold
|
||||
* constants, assuming there is a single variable in the expression, modifying the goal as it
|
||||
* descends toward that variable. If it finds a variable, i.e., token or context field, it encodes
|
||||
* the solution, positioned in the field. If the expression is constant, it checks that the goal
|
||||
* agrees. If not, an error is returned.
|
||||
* agrees. If not, an error is returned. There are some common cases where it is forced to solve
|
||||
* expressions involving multiple variables. Those cases are addressed in the derivatives of
|
||||
* {@link AbstractBinaryExpressionSolver} where the situation can be detected. One common example is
|
||||
* field concatenation using the {@code (A << 4) | B} pattern.
|
||||
*
|
||||
* TODO This whole mechanism ought to just be factored directly into {@link PatternExpression}.
|
||||
* <p>
|
||||
* TODO: Perhaps this whole mechanism ought to just be factored directly into
|
||||
* {@link PatternExpression}.
|
||||
*/
|
||||
public class RecursiveDescentSolver {
|
||||
protected static final DbgTimer dbg = DbgTimer.INACTIVE;
|
||||
private static final RecursiveDescentSolver solver = new RecursiveDescentSolver();
|
||||
protected static final DbgTimer DBG = DbgTimer.INACTIVE;
|
||||
private static final RecursiveDescentSolver INSTANCE = new RecursiveDescentSolver();
|
||||
|
||||
// A mapping from each subclass of PatternExpression to the appropriate solver
|
||||
protected Map<Class<?>, AbstractExpressionSolver<?>> registry = new HashMap<>();
|
||||
@@ -67,7 +73,7 @@ public class RecursiveDescentSolver {
|
||||
* @return the singleton instance
|
||||
*/
|
||||
public static RecursiveDescentSolver getSolver() {
|
||||
return solver;
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -103,59 +109,52 @@ public class RecursiveDescentSolver {
|
||||
* @param exp the expression to solve
|
||||
* @param goal the desired output (modulo a mask) of the expression
|
||||
* @param vals any defined symbols (usually {@code inst_start}, and {@code inst_next})
|
||||
* @param res resolved subconstructors, by operand index (see method details)
|
||||
* @param hints describes techniques applied by calling solvers
|
||||
* @param description a description to attached to the encoded solution
|
||||
* @return the encoded solution
|
||||
* @throws NeedsBackfillException a solution may exist, but a required symbol is missing
|
||||
*/
|
||||
protected AssemblyResolution solve(PatternExpression exp, MaskedLong goal,
|
||||
Map<String, Long> vals, Map<Integer, Object> res, AssemblyResolvedConstructor cur,
|
||||
Set<SolverHint> hints, String description) throws NeedsBackfillException {
|
||||
Map<String, Long> vals, AssemblyResolvedPatterns cur, Set<SolverHint> hints,
|
||||
String description) throws NeedsBackfillException {
|
||||
try {
|
||||
return getRegistered(exp.getClass()).solve(exp, goal, vals, res, cur, hints,
|
||||
description);
|
||||
return getRegistered(exp.getClass()).solve(exp, goal, vals, cur, hints, description);
|
||||
}
|
||||
catch (UnsupportedOperationException e) {
|
||||
dbg.println("Error solving " + exp + " = " + goal);
|
||||
DBG.println("Error solving " + exp + " = " + goal);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Solve a given expression, assuming it outputs a given masked value
|
||||
* Solve a given expression, given a masked-value goal
|
||||
*
|
||||
* <p>
|
||||
* From a simplified perspective, we need only the expression and the desired value to solve it.
|
||||
* Generally speaking, the expression may have only contain a single variable, and the encoded
|
||||
* result represents that single variable. It must be absorbed into the overall instruction
|
||||
* and/or context encoding.
|
||||
* Generally speaking, the expression may only contain a single field, and the encoded result
|
||||
* specifies the bits of the solved field. It must be absorbed into the overall assembly
|
||||
* pattern.
|
||||
*
|
||||
* More realistically, however, these expressions may depend on quite a bit of extra
|
||||
* information. For example, PC-relative encodings (i.e., those involving {@code inst_start} or
|
||||
* <p>
|
||||
* More realistically, these expressions may depend on quite a bit of extra information. For
|
||||
* example, PC-relative encodings (i.e., those involving {@code inst_start} or
|
||||
* {@code inst_next}, need to know the starting address of the resulting instruction. {@code
|
||||
* inst_start} must be provided to the solver by the assembler. {@code inst_next} cannot be
|
||||
* known until the instruction length is known. Thus, expressions using it always result in a
|
||||
* {@link NeedsBackfillException}. The symbols, when known, are provided to the solver via the
|
||||
* {@code vals} parameter.
|
||||
*
|
||||
* Expressions involving {@link OperandValueSolver}s are a little more complicated, because they
|
||||
* specify an offset that affects its encoding in the instruction. To compute this offset, the
|
||||
* lengths of other surrounding operands must be known. Thus, when solving a context change for
|
||||
* a given constructor, its resolved subconstructors must be provided to the solver via the
|
||||
* {@code res} parameter.
|
||||
*
|
||||
* @param exp the expression to solve
|
||||
* @param goal the desired output (modulo a mask) of the expression
|
||||
* @param vals any defined symbols (usually {@code inst_start}, and {@code inst_next})
|
||||
* @param res resolved subconstructors, by operand index (see method details)
|
||||
* @param description a description to attached to the encoded solution
|
||||
* @return the encoded solution
|
||||
* @throws NeedsBackfillException a solution may exist, but a required symbol is missing
|
||||
*/
|
||||
public AssemblyResolution solve(PatternExpression exp, MaskedLong goal, Map<String, Long> vals,
|
||||
Map<Integer, Object> res, AssemblyResolvedConstructor cur, String description)
|
||||
AssemblyResolvedPatterns cur, String description)
|
||||
throws NeedsBackfillException {
|
||||
return solve(exp, goal, vals, res, cur, Set.of(), description);
|
||||
return solve(exp, goal, vals, cur, Set.of(), description);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -163,45 +162,44 @@ public class RecursiveDescentSolver {
|
||||
*
|
||||
* @param exp the (sub-)expression to fold
|
||||
* @param vals any defined symbols (usually {@code inst_start}, and {@code inst_next})
|
||||
* @param res resolved subconstructors, by operand index (see
|
||||
* {@link #solve(PatternExpression, MaskedLong, Map, Map, AssemblyResolvedConstructor, String)})
|
||||
* @return the masked solution
|
||||
* @throws NeedsBackfillException it may be folded, but a required symbol is missing
|
||||
*/
|
||||
protected <T extends PatternExpression> MaskedLong getValue(T exp, Map<String, Long> vals,
|
||||
Map<Integer, Object> res, AssemblyResolvedConstructor cur)
|
||||
throws NeedsBackfillException {
|
||||
MaskedLong value = getRegistered(exp.getClass()).getValue(exp, vals, res, cur);
|
||||
dbg.println("Expression: " + value + " =: " + exp);
|
||||
AssemblyResolvedPatterns cur) throws NeedsBackfillException {
|
||||
MaskedLong value = getRegistered(exp.getClass()).getValue(exp, vals, cur);
|
||||
DBG.println("Expression: " + value + " =: " + exp);
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the length of the instruction part of the encoded solution to the given expression
|
||||
*
|
||||
* <p>
|
||||
* This is used to keep operands in their appropriate position when backfilling becomes
|
||||
* applicable. Normally, the instruction length is taken from the encoding of a solution, but if
|
||||
* the solution cannot be determined yet, the instruction length must still be obtained.
|
||||
*
|
||||
* <p>
|
||||
* The length can be determined by finding token fields in the expression.
|
||||
*
|
||||
* @param exp the expression, presumably containing a token field
|
||||
* @param res resolved subconstructors, by operand index (see
|
||||
* {@link #solve(PatternExpression, MaskedLong, Map, Map, AssemblyResolvedConstructor, String)})
|
||||
* @return the anticipated length, in bytes, of the instruction encoding
|
||||
*/
|
||||
public int getInstructionLength(PatternExpression exp, Map<Integer, Object> res) {
|
||||
return getRegistered(exp.getClass()).getInstructionLength(exp, res);
|
||||
public int getInstructionLength(PatternExpression exp) {
|
||||
return getRegistered(exp.getClass()).getInstructionLength(exp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the value of an expression given a (possibly-intermediate) resolution
|
||||
*
|
||||
* @param exp the expression to evaluate
|
||||
* @param rc the resolution on which to evalute it
|
||||
* @param vals values of defined symbols
|
||||
* @param rc the resolution on which to evaluate it
|
||||
* @return the result
|
||||
*/
|
||||
public MaskedLong valueForResolution(PatternExpression exp, AssemblyResolvedConstructor rc) {
|
||||
return getRegistered(exp.getClass()).valueForResolution(exp, rc);
|
||||
public MaskedLong valueForResolution(PatternExpression exp, Map<String, Long> vals,
|
||||
AssemblyResolvedPatterns rc) {
|
||||
return getRegistered(exp.getClass()).valueForResolution(exp, vals, rc);
|
||||
}
|
||||
}
|
||||
|
||||
+11
-12
@@ -19,12 +19,12 @@ import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import ghidra.app.plugin.assembler.sleigh.sem.AssemblyResolution;
|
||||
import ghidra.app.plugin.assembler.sleigh.sem.AssemblyResolvedConstructor;
|
||||
import ghidra.app.plugin.assembler.sleigh.sem.AssemblyResolvedPatterns;
|
||||
import ghidra.app.plugin.processors.sleigh.expression.RightShiftExpression;
|
||||
import ghidra.util.Msg;
|
||||
|
||||
/**
|
||||
* {@literal Solves expressions of the form A >> B}
|
||||
* Solves expressions of the form {@code A >> B}
|
||||
*/
|
||||
public class RightShiftExpressionSolver
|
||||
extends AbstractBinaryExpressionSolver<RightShiftExpression> {
|
||||
@@ -62,15 +62,14 @@ public class RightShiftExpressionSolver
|
||||
|
||||
@Override
|
||||
protected AssemblyResolution solveTwoSided(RightShiftExpression exp, MaskedLong goal,
|
||||
Map<String, Long> vals, Map<Integer, Object> res, AssemblyResolvedConstructor cur,
|
||||
Set<SolverHint> hints, String description)
|
||||
throws NeedsBackfillException, SolverException {
|
||||
Map<String, Long> vals, AssemblyResolvedPatterns cur, Set<SolverHint> hints,
|
||||
String description) throws NeedsBackfillException, SolverException {
|
||||
// Do the similar thing as in {@link LeftShiftExpressionSolver}
|
||||
|
||||
// Do not guess the same parameter recursively
|
||||
if (hints.contains(DefaultSolverHint.GUESSING_RIGHT_SHIFT_AMOUNT)) {
|
||||
// NOTE: Nested right shifts ought to be written as a right shift by a sum
|
||||
return super.solveTwoSided(exp, goal, vals, res, cur, hints, description);
|
||||
return super.solveTwoSided(exp, goal, vals, cur, hints, description);
|
||||
}
|
||||
|
||||
int maxShift = Long.numberOfLeadingZeros(goal.val);
|
||||
@@ -82,18 +81,18 @@ public class RightShiftExpressionSolver
|
||||
MaskedLong reql = computeLeft(reqr, goal);
|
||||
|
||||
AssemblyResolution lres =
|
||||
solver.solve(exp.getLeft(), reql, vals, res, cur, hintsWithRShift, description);
|
||||
solver.solve(exp.getLeft(), reql, vals, cur, hintsWithRShift, description);
|
||||
if (lres.isError()) {
|
||||
throw new SolverException("Solving left failed");
|
||||
}
|
||||
AssemblyResolution rres =
|
||||
solver.solve(exp.getRight(), reqr, vals, res, cur, hints, description);
|
||||
solver.solve(exp.getRight(), reqr, vals, cur, hints, description);
|
||||
if (rres.isError()) {
|
||||
throw new SolverException("Solving right failed");
|
||||
}
|
||||
AssemblyResolvedConstructor lsol = (AssemblyResolvedConstructor) lres;
|
||||
AssemblyResolvedConstructor rsol = (AssemblyResolvedConstructor) rres;
|
||||
AssemblyResolvedConstructor sol = lsol.combine(rsol);
|
||||
AssemblyResolvedPatterns lsol = (AssemblyResolvedPatterns) lres;
|
||||
AssemblyResolvedPatterns rsol = (AssemblyResolvedPatterns) rres;
|
||||
AssemblyResolvedPatterns sol = lsol.combine(rsol);
|
||||
if (sol == null) {
|
||||
throw new SolverException(
|
||||
"Left and right solutions conflict for shift=" + shift);
|
||||
@@ -105,6 +104,6 @@ public class RightShiftExpressionSolver
|
||||
// try the next
|
||||
}
|
||||
}
|
||||
return super.solveTwoSided(exp, goal, vals, res, cur, hints, description);
|
||||
return super.solveTwoSided(exp, goal, vals, cur, hints, description);
|
||||
}
|
||||
}
|
||||
|
||||
+3
-1
@@ -20,11 +20,13 @@ import java.util.*;
|
||||
/**
|
||||
* A type for solver hints
|
||||
*
|
||||
* Hints inform "sub-"solvers of the techniques already being applied by the calling solvers. This
|
||||
* <p>
|
||||
* Hints inform sub-solvers of the techniques already being applied by the calling solvers. This
|
||||
* helps prevent situations where, e.g., two multiplication solvers (applied to repeated or nested
|
||||
* multiplication) both attempt to synthesize new goals for repetition. This sort of expression is
|
||||
* common when decoding immediates in the AArch64 specification.
|
||||
*
|
||||
* <p>
|
||||
* Using an interface implemented by an enumeration (instead of just using the enumeration directly)
|
||||
* eases expansion by extension without modifying the core code.
|
||||
*
|
||||
|
||||
+8
-9
@@ -24,6 +24,7 @@ import ghidra.app.plugin.processors.sleigh.expression.StartInstructionValue;
|
||||
/**
|
||||
* "Solves" expression of {@code inst_start}
|
||||
*
|
||||
* <p>
|
||||
* Works like the constant solver, but takes the value of {@code inst_start}, which is given by the
|
||||
* assembly address.
|
||||
*/
|
||||
@@ -35,28 +36,26 @@ public class StartInstructionValueSolver extends AbstractExpressionSolver<StartI
|
||||
|
||||
@Override
|
||||
public AssemblyResolution solve(StartInstructionValue iv, MaskedLong goal,
|
||||
Map<String, Long> vals, Map<Integer, Object> res, AssemblyResolvedConstructor cur,
|
||||
Set<SolverHint> hints, String description) {
|
||||
Map<String, Long> vals, AssemblyResolvedPatterns cur, Set<SolverHint> hints,
|
||||
String description) {
|
||||
throw new AssertionError(
|
||||
"INTERNAL: Should never be asked to solve for " + AssemblyTreeResolver.INST_START);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MaskedLong getValue(StartInstructionValue iv, Map<String, Long> vals,
|
||||
Map<Integer, Object> res, AssemblyResolvedConstructor cur) {
|
||||
AssemblyResolvedPatterns cur) {
|
||||
return MaskedLong.fromLong(vals.get(AssemblyTreeResolver.INST_START));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getInstructionLength(StartInstructionValue exp, Map<Integer, Object> res) {
|
||||
public int getInstructionLength(StartInstructionValue exp) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MaskedLong valueForResolution(StartInstructionValue exp,
|
||||
AssemblyResolvedConstructor rc) {
|
||||
// Would need to pass in symbol values.
|
||||
throw new UnsupportedOperationException(
|
||||
"The solver should never ask for this value given a resolved constructor.");
|
||||
public MaskedLong valueForResolution(StartInstructionValue exp, Map<String, Long> vals,
|
||||
AssemblyResolvedPatterns rc) {
|
||||
return MaskedLong.fromLong(vals.get(AssemblyTreeResolver.INST_START));
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@ package ghidra.app.plugin.assembler.sleigh.expr;
|
||||
import ghidra.app.plugin.processors.sleigh.expression.SubExpression;
|
||||
|
||||
/**
|
||||
* Solves expressions of the form A - B
|
||||
* Solves expressions of the form {@code A - B}
|
||||
*/
|
||||
public class SubExpressionSolver extends AbstractBinaryExpressionSolver<SubExpression> {
|
||||
|
||||
|
||||
+10
-9
@@ -24,6 +24,7 @@ import ghidra.app.plugin.processors.sleigh.expression.TokenField;
|
||||
/**
|
||||
* Solves expressions of a token (instruction encoding) field
|
||||
*
|
||||
* <p>
|
||||
* Essentially, this just encodes the goal into the field, if it can be represented in the given
|
||||
* space and format. Otherwise, there is no solution.
|
||||
*/
|
||||
@@ -35,33 +36,33 @@ public class TokenFieldSolver extends AbstractExpressionSolver<TokenField> {
|
||||
|
||||
@Override
|
||||
public AssemblyResolution solve(TokenField tf, MaskedLong goal, Map<String, Long> vals,
|
||||
Map<Integer, Object> res, AssemblyResolvedConstructor cur, Set<SolverHint> hints,
|
||||
String description) {
|
||||
AssemblyResolvedPatterns cur, Set<SolverHint> hints, String description) {
|
||||
assert tf.minValue() == 0; // In case someone decides to do signedness there.
|
||||
if (!goal.isInRange(tf.maxValue(), tf.hasSignbit())) {
|
||||
return AssemblyResolution.error("Value " + goal + " is not valid for " + tf,
|
||||
description, null);
|
||||
description);
|
||||
}
|
||||
AssemblyPatternBlock block = AssemblyPatternBlock.fromTokenField(tf, goal);
|
||||
return AssemblyResolution.instrOnly(block, description, null);
|
||||
return AssemblyResolution.instrOnly(block, description);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MaskedLong getValue(TokenField tf, Map<String, Long> vals, Map<Integer, Object> res,
|
||||
AssemblyResolvedConstructor cur) {
|
||||
public MaskedLong getValue(TokenField tf, Map<String, Long> vals,
|
||||
AssemblyResolvedPatterns cur) {
|
||||
if (cur == null) {
|
||||
return null;
|
||||
}
|
||||
return valueForResolution(tf, cur);
|
||||
return valueForResolution(tf, vals, cur);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getInstructionLength(TokenField tf, Map<Integer, Object> res) {
|
||||
public int getInstructionLength(TokenField tf) {
|
||||
return tf.getByteEnd() + 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MaskedLong valueForResolution(TokenField tf, AssemblyResolvedConstructor rc) {
|
||||
public MaskedLong valueForResolution(TokenField tf, Map<String, Long> vals,
|
||||
AssemblyResolvedPatterns rc) {
|
||||
int size = tf.getByteEnd() - tf.getByteStart() + 1;
|
||||
MaskedLong res = rc.readInstruction(tf.getByteStart(), size);
|
||||
if (!tf.isBigEndian()) {
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@ package ghidra.app.plugin.assembler.sleigh.expr;
|
||||
import ghidra.app.plugin.processors.sleigh.expression.XorExpression;
|
||||
|
||||
/**
|
||||
* Solves expressions of the form A $xor B
|
||||
* Solves expressions of the form {@code A $xor B}
|
||||
*/
|
||||
public class XorExpressionSolver extends AbstractBinaryExpressionSolver<XorExpression> {
|
||||
|
||||
|
||||
+122
@@ -0,0 +1,122 @@
|
||||
/* ###
|
||||
* 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.assembler.sleigh.expr.match;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import ghidra.app.plugin.processors.sleigh.expression.*;
|
||||
|
||||
/**
|
||||
* Base implementation for expression matchers
|
||||
*
|
||||
* @param <T> the type of expression matched
|
||||
*/
|
||||
public abstract class AbstractExpressionMatcher<T extends PatternExpression>
|
||||
implements ExpressionMatcher<T> {
|
||||
protected final Set<Class<? extends T>> ops;
|
||||
|
||||
public AbstractExpressionMatcher(Set<Class<? extends T>> ops) {
|
||||
this.ops = Set.copyOf(ops);
|
||||
}
|
||||
|
||||
public AbstractExpressionMatcher(Class<? extends T> cls) {
|
||||
this.ops = Set.of(cls);
|
||||
}
|
||||
|
||||
protected T opMatches(PatternExpression expression) {
|
||||
return ops.stream()
|
||||
.filter(op -> op.isInstance(expression))
|
||||
.map(op -> op.cast(expression))
|
||||
.findAny()
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
protected abstract boolean matchDetails(T expression,
|
||||
Map<ExpressionMatcher<?>, PatternExpression> result);
|
||||
|
||||
@Override
|
||||
public boolean match(PatternExpression expression,
|
||||
Map<ExpressionMatcher<?>, PatternExpression> result) {
|
||||
T t = opMatches(expression);
|
||||
if (t == null) {
|
||||
return false;
|
||||
}
|
||||
if (!matchDetails(t, result)) {
|
||||
return false;
|
||||
}
|
||||
return recordResult(t, result);
|
||||
}
|
||||
|
||||
protected boolean recordResult(PatternExpression expression,
|
||||
Map<ExpressionMatcher<?>, PatternExpression> result) {
|
||||
PatternExpression already = result.put(this, expression);
|
||||
if (already == null) {
|
||||
return true;
|
||||
}
|
||||
return expressionsIdenticallyDefined(already, expression);
|
||||
}
|
||||
|
||||
protected static boolean expressionsIdenticallyDefined(PatternExpression a,
|
||||
PatternExpression b) {
|
||||
if (a.getClass() != b.getClass()) {
|
||||
return false;
|
||||
}
|
||||
if (a instanceof EndInstructionValue) {
|
||||
return true;
|
||||
}
|
||||
if (a instanceof StartInstructionValue) {
|
||||
return true;
|
||||
}
|
||||
if (a instanceof ConstantValue) {
|
||||
ConstantValue ca = (ConstantValue) a;
|
||||
ConstantValue cb = (ConstantValue) b;
|
||||
return ca.getValue() == cb.getValue();
|
||||
}
|
||||
if (a instanceof UnaryExpression) {
|
||||
UnaryExpression ua = (UnaryExpression) a;
|
||||
UnaryExpression ub = (UnaryExpression) b;
|
||||
return expressionsIdenticallyDefined(ua.getUnary(), ub.getUnary());
|
||||
}
|
||||
if (a instanceof BinaryExpression) {
|
||||
BinaryExpression ba = (BinaryExpression) a;
|
||||
BinaryExpression bb = (BinaryExpression) b;
|
||||
return expressionsIdenticallyDefined(ba.getLeft(), bb.getLeft()) &&
|
||||
expressionsIdenticallyDefined(ba.getRight(), bb.getRight());
|
||||
}
|
||||
if (a instanceof TokenField) {
|
||||
TokenField ta = (TokenField) a;
|
||||
TokenField tb = (TokenField) b;
|
||||
return ta.getBitStart() == tb.getBitStart() &&
|
||||
ta.getBitEnd() == tb.getBitEnd() &&
|
||||
ta.hasSignbit() == tb.hasSignbit();
|
||||
}
|
||||
if (a instanceof ContextField) {
|
||||
ContextField ca = (ContextField) a;
|
||||
ContextField cb = (ContextField) b;
|
||||
return ca.getStartBit() == cb.getStartBit() &&
|
||||
ca.getEndBit() == cb.getEndBit() &&
|
||||
ca.hasSignbit() == cb.hasSignbit();
|
||||
}
|
||||
if (a instanceof OperandValue) {
|
||||
OperandValue va = (OperandValue) a;
|
||||
OperandValue vb = (OperandValue) b;
|
||||
return va.getConstructor() == vb.getConstructor() &&
|
||||
va.getIndex() == vb.getIndex();
|
||||
}
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
/* ###
|
||||
* 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.assembler.sleigh.expr.match;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import ghidra.app.plugin.processors.sleigh.expression.PatternExpression;
|
||||
|
||||
/**
|
||||
* A matcher which accept any expression of the required type
|
||||
*
|
||||
* <p>
|
||||
* This requires no further consideration of the expressions operands. If the type matches, the
|
||||
* expression matches.
|
||||
*
|
||||
* @param <T> the type to match
|
||||
*/
|
||||
public class AnyMatcher<T extends PatternExpression> extends AbstractExpressionMatcher<T> {
|
||||
public static AnyMatcher<PatternExpression> any() {
|
||||
return new AnyMatcher<>(PatternExpression.class);
|
||||
}
|
||||
|
||||
public AnyMatcher(Set<Class<? extends T>> ops) {
|
||||
super(ops);
|
||||
}
|
||||
|
||||
public AnyMatcher(Class<T> cls) {
|
||||
super(cls);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean matchDetails(T expression,
|
||||
Map<ExpressionMatcher<?>, PatternExpression> result) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
/* ###
|
||||
* 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.assembler.sleigh.expr.match;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import ghidra.app.plugin.processors.sleigh.expression.BinaryExpression;
|
||||
import ghidra.app.plugin.processors.sleigh.expression.PatternExpression;
|
||||
|
||||
/**
|
||||
* A matcher for a binary expression
|
||||
*
|
||||
* <p>
|
||||
* If the required type matches, the matching descends to the left then right operands.
|
||||
*
|
||||
* @param <T> the type of expression matched
|
||||
*/
|
||||
public class BinaryExpressionMatcher<T extends BinaryExpression>
|
||||
extends AbstractExpressionMatcher<T> {
|
||||
|
||||
/**
|
||||
* A matcher for binary expression allowing commutativity
|
||||
*
|
||||
* <p>
|
||||
* This behaves the same as {@link BinaryExpressionMatcher}, but if the first attempt fails, the
|
||||
* operand match is re-attempted with the operands swapped.
|
||||
*
|
||||
* @param <T> the type of expression matched
|
||||
*/
|
||||
public static class Commutative<T extends BinaryExpression> extends BinaryExpressionMatcher<T> {
|
||||
public Commutative(Set<Class<? extends T>> ops,
|
||||
ExpressionMatcher<?> leftMatcher, ExpressionMatcher<?> rightMatcher) {
|
||||
super(ops, leftMatcher, rightMatcher);
|
||||
}
|
||||
|
||||
public Commutative(Class<T> cls, ExpressionMatcher<?> leftMatcher,
|
||||
ExpressionMatcher<?> rightMatcher) {
|
||||
super(cls, leftMatcher, rightMatcher);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean matchDetails(T expression,
|
||||
Map<ExpressionMatcher<?>, PatternExpression> result) {
|
||||
Set<ExpressionMatcher<?>> reset = new HashSet<>(result.keySet());
|
||||
if (leftMatcher.match(expression.getLeft(), result) &&
|
||||
rightMatcher.match(expression.getRight(), result)) {
|
||||
return true;
|
||||
}
|
||||
result.keySet().retainAll(reset);
|
||||
return rightMatcher.match(expression.getLeft(), result) &&
|
||||
leftMatcher.match(expression.getRight(), result);
|
||||
}
|
||||
}
|
||||
|
||||
protected final ExpressionMatcher<?> leftMatcher;
|
||||
protected final ExpressionMatcher<?> rightMatcher;
|
||||
|
||||
public BinaryExpressionMatcher(Set<Class<? extends T>> ops,
|
||||
ExpressionMatcher<?> leftMatcher, ExpressionMatcher<?> rightMatcher) {
|
||||
super(ops);
|
||||
this.leftMatcher = leftMatcher;
|
||||
this.rightMatcher = rightMatcher;
|
||||
}
|
||||
|
||||
public BinaryExpressionMatcher(Class<T> cls, ExpressionMatcher<?> leftMatcher,
|
||||
ExpressionMatcher<?> rightMatcher) {
|
||||
super(cls);
|
||||
this.leftMatcher = leftMatcher;
|
||||
this.rightMatcher = rightMatcher;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean matchDetails(T expression,
|
||||
Map<ExpressionMatcher<?>, PatternExpression> result) {
|
||||
return leftMatcher.match(expression.getLeft(), result) &&
|
||||
rightMatcher.match(expression.getRight(), result);
|
||||
}
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
/* ###
|
||||
* 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.assembler.sleigh.expr.match;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import ghidra.app.plugin.processors.sleigh.expression.ConstantValue;
|
||||
import ghidra.app.plugin.processors.sleigh.expression.PatternExpression;
|
||||
|
||||
/**
|
||||
* A matcher for a given constant value
|
||||
*/
|
||||
public class ConstantValueMatcher extends AbstractExpressionMatcher<ConstantValue> {
|
||||
protected final long value;
|
||||
|
||||
public ConstantValueMatcher(long value) {
|
||||
super(ConstantValue.class);
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean matchDetails(ConstantValue expression,
|
||||
Map<ExpressionMatcher<?>, PatternExpression> result) {
|
||||
return expression.getValue() == value;
|
||||
}
|
||||
}
|
||||
+309
@@ -0,0 +1,309 @@
|
||||
/* ###
|
||||
* 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.assembler.sleigh.expr.match;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import ghidra.app.plugin.processors.sleigh.expression.*;
|
||||
|
||||
/**
|
||||
* A matcher for a form of patten expression
|
||||
*
|
||||
* <p>
|
||||
* Some solvers may need to apply sophisticated heuristics to recognize certain forms that commonly
|
||||
* occur in pattern expressions. These can certainly be programmed manually, but for many cases, the
|
||||
* form recognition can be accomplished by describing the form as an expression matcher. For a
|
||||
* shorter syntax to construct such matchers. See {@link Context}.
|
||||
*
|
||||
* @param <T> the type of expression matched
|
||||
*/
|
||||
public interface ExpressionMatcher<T extends PatternExpression> {
|
||||
|
||||
/**
|
||||
* Attempt to match the given expression, recording the substitutions if successful
|
||||
*
|
||||
* @param expression the expression to match
|
||||
* @return a map of matchers to substituted expressions
|
||||
*/
|
||||
default Map<ExpressionMatcher<?>, PatternExpression> match(PatternExpression expression) {
|
||||
Map<ExpressionMatcher<?>, PatternExpression> result = new HashMap<>();
|
||||
if (match(expression, result)) {
|
||||
return result;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the expression substituted for this matcher from a previous successful match
|
||||
*
|
||||
* <p>
|
||||
* Calling this on the root matcher is relatively useless, as it would simply return the
|
||||
* expression passed to {@link #match(PatternExpression)}. Instead, sub-matchers should be saved
|
||||
* in a variable, allowing their values to be retrieved. See {@link Context}, for an example.
|
||||
*
|
||||
* @param results the previous match results
|
||||
* @return the substituted expression
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
default T get(Map<ExpressionMatcher<?>, PatternExpression> results) {
|
||||
return (T) results.get(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to match the given expression, recording substitutions in the given map
|
||||
*
|
||||
* <p>
|
||||
* Even if the match was unsuccessful, the result map may contain attempted substitutions. Thus,
|
||||
* the map should be discarded if unsuccessful.
|
||||
*
|
||||
* @param expression the expression to match
|
||||
* @param result a map to store matchers to substituted expressions
|
||||
* @return true if successful, false if not
|
||||
*/
|
||||
boolean match(PatternExpression expression,
|
||||
Map<ExpressionMatcher<?>, PatternExpression> result);
|
||||
|
||||
/**
|
||||
* A context for defining expression matcher succinctly
|
||||
*
|
||||
* <p>
|
||||
* Implementations of this interface have easy access to factory methods for each kind of
|
||||
* {@link PatternExpression}. Additionally, the class itself provide a convenient container for
|
||||
* saving important sub-matchers, so that important sub-expression can be readily retrieved. For
|
||||
* example:
|
||||
*
|
||||
* <pre>
|
||||
* static class MyMatchers implements ExpressionMatcher.Context {
|
||||
* ExpressionMatcher<ConstantValue> shamt = var(ConstantValue.class);
|
||||
* ExpressionMatcher<LeftShiftExpression> exp = shl(var(), shamt);
|
||||
* }
|
||||
*
|
||||
* static final MyMatchers MATCHERS = new MyMatchers();
|
||||
*
|
||||
* public long getConstantShift(PatternExpression expression) {
|
||||
* Map<ExpressionMatcher<?>, PatternExpression> result = MATCHERS.exp.match(expression);
|
||||
* if (result == null) {
|
||||
* return -1;
|
||||
* }
|
||||
* return MATCHERS.shamt.get(result).getValue();
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* Saving a sub-matcher to a field (as in the example) also permits that sub-matcher to appear
|
||||
* in multiple places. In that case, the sub-matcher must match identical expressions wherever
|
||||
* it appears. For example, if {@code cv} matches any constant value, then {@code plus(cv, cv)}
|
||||
* would match {@code 2 + 2}, but not {@code 2 + 3}.
|
||||
*/
|
||||
interface Context {
|
||||
|
||||
/**
|
||||
* Match the form {@code L & R} or {@code R & L}
|
||||
*
|
||||
* @param left the matcher for the left operand
|
||||
* @param right the matcher for the right operand
|
||||
* @return the matcher
|
||||
*/
|
||||
default ExpressionMatcher<AndExpression> and(ExpressionMatcher<?> left,
|
||||
ExpressionMatcher<?> right) {
|
||||
return new BinaryExpressionMatcher.Commutative<>(AndExpression.class, left, right);
|
||||
}
|
||||
|
||||
/**
|
||||
* Match the form {@code L / R}
|
||||
*
|
||||
* @param left the matcher for the dividend
|
||||
* @param right the matcher for the divisor
|
||||
* @return the matcher for the quotient
|
||||
*/
|
||||
default ExpressionMatcher<DivExpression> div(ExpressionMatcher<?> left,
|
||||
ExpressionMatcher<?> right) {
|
||||
return new BinaryExpressionMatcher<>(DivExpression.class, left, right);
|
||||
}
|
||||
|
||||
/**
|
||||
* Match the form {@code L << R}
|
||||
*
|
||||
* @param left the matcher for the left operand
|
||||
* @param right the matcher for the shift amount
|
||||
* @return the matcher
|
||||
*/
|
||||
default ExpressionMatcher<LeftShiftExpression> shl(ExpressionMatcher<?> left,
|
||||
ExpressionMatcher<?> right) {
|
||||
return new BinaryExpressionMatcher<>(LeftShiftExpression.class, left, right);
|
||||
}
|
||||
|
||||
/**
|
||||
* Match the form {@code L * R} or {@code R * L}
|
||||
*
|
||||
* @param left the matcher for the left factor
|
||||
* @param right the matcher for the right factor
|
||||
* @return the matcher for the product
|
||||
*/
|
||||
default ExpressionMatcher<MultExpression> mul(ExpressionMatcher<?> left,
|
||||
ExpressionMatcher<?> right) {
|
||||
return new BinaryExpressionMatcher.Commutative<>(MultExpression.class, left, right);
|
||||
}
|
||||
|
||||
/**
|
||||
* Match the form {@code L | R} or {@code R | L}
|
||||
*
|
||||
* @param left the matcher for the left operand
|
||||
* @param right the matcher for the right operand
|
||||
* @return the matcher
|
||||
*/
|
||||
default ExpressionMatcher<OrExpression> or(ExpressionMatcher<?> left,
|
||||
ExpressionMatcher<?> right) {
|
||||
return new BinaryExpressionMatcher.Commutative<>(OrExpression.class, left, right);
|
||||
}
|
||||
|
||||
/**
|
||||
* Match the form {@code L + R} or {@code R + L}
|
||||
*
|
||||
* @param left the matcher for the left term
|
||||
* @param right the matcher for the right term
|
||||
* @return the matcher for the sum
|
||||
*/
|
||||
default ExpressionMatcher<PlusExpression> plus(ExpressionMatcher<?> left,
|
||||
ExpressionMatcher<?> right) {
|
||||
return new BinaryExpressionMatcher<>(PlusExpression.class, left, right);
|
||||
}
|
||||
|
||||
/**
|
||||
* Match the form {@code L >> R}
|
||||
*
|
||||
* @param left the matcher for the left operand
|
||||
* @param right the matcher for the shift amount
|
||||
* @return the matcher
|
||||
*/
|
||||
default ExpressionMatcher<RightShiftExpression> shr(ExpressionMatcher<?> left,
|
||||
ExpressionMatcher<?> right) {
|
||||
return new BinaryExpressionMatcher<>(RightShiftExpression.class, left, right);
|
||||
}
|
||||
|
||||
/**
|
||||
* Match the form {@code L - R}
|
||||
*
|
||||
* @param left the matcher for the left term
|
||||
* @param right the matcher for the right term
|
||||
* @return the matcher for the difference
|
||||
*/
|
||||
default ExpressionMatcher<SubExpression> sub(ExpressionMatcher<?> left,
|
||||
ExpressionMatcher<?> right) {
|
||||
return new BinaryExpressionMatcher<>(SubExpression.class, left, right);
|
||||
}
|
||||
|
||||
/**
|
||||
* Match the form {@code L $xor R} or {@code R $xor L}
|
||||
*
|
||||
* @param left the matcher for the left operand
|
||||
* @param right the matcher for the right operand
|
||||
* @return the matcher
|
||||
*/
|
||||
default ExpressionMatcher<XorExpression> xor(ExpressionMatcher<?> left,
|
||||
ExpressionMatcher<?> right) {
|
||||
return new BinaryExpressionMatcher<>(XorExpression.class, left, right);
|
||||
}
|
||||
|
||||
/**
|
||||
* Match a given constant value
|
||||
*
|
||||
* <p>
|
||||
* <b>NOTE:</b> To match an unspecified constant value, use {@link #var(Class)} with
|
||||
* {@link ConstantValue}.
|
||||
*
|
||||
* @param value the value to match
|
||||
* @return the matcher
|
||||
*/
|
||||
default ExpressionMatcher<ConstantValue> cv(long value) {
|
||||
return new ConstantValueMatcher(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Match any expression
|
||||
*
|
||||
* <p>
|
||||
* This matches any expression without consideration of its operands, except insofar when it
|
||||
* appears in multiple places, it will check that subsequent matches are identical to the
|
||||
* first.
|
||||
*
|
||||
* @return the matcher
|
||||
*/
|
||||
default ExpressionMatcher<PatternExpression> var() {
|
||||
return AnyMatcher.any();
|
||||
}
|
||||
|
||||
/**
|
||||
* Match any expression of the given type
|
||||
*
|
||||
* @param <T> the type of expression to match
|
||||
* @param cls the class of expression to match
|
||||
* @return the matcher
|
||||
*/
|
||||
default <T extends PatternExpression> ExpressionMatcher<T> var(Class<T> cls) {
|
||||
return new AnyMatcher<>(cls);
|
||||
}
|
||||
|
||||
/**
|
||||
* Match an operand value
|
||||
*
|
||||
* <p>
|
||||
* Typically, this must wrap any use of a field, since that field is considered an operand
|
||||
* from the constructor's perspective.
|
||||
*
|
||||
* @param def the matcher for the operand's defining expression.
|
||||
* @return the operand matcher
|
||||
*/
|
||||
default ExpressionMatcher<OperandValue> opnd(ExpressionMatcher<?> def) {
|
||||
return new OperandValueMatcher(def);
|
||||
}
|
||||
|
||||
/**
|
||||
* Match a field by its size
|
||||
*
|
||||
* <p>
|
||||
* This matches either a {@link TokenField} or a {@link ContextField}. If matched, it then
|
||||
* passes a {@link ConstantValue} of the field's size (in bits) into the given size matcher.
|
||||
*
|
||||
* @param size the matcher for the field's size
|
||||
* @return the field matcher
|
||||
*/
|
||||
default ExpressionMatcher<PatternValue> fldSz(ExpressionMatcher<?> size) {
|
||||
return new FieldSizeMatcher(size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Match the form {@code -U}
|
||||
*
|
||||
* @param unary the child matcher
|
||||
* @return the matcher
|
||||
*/
|
||||
default ExpressionMatcher<MinusExpression> neg(ExpressionMatcher<?> unary) {
|
||||
return new UnaryExpressionMatcher<>(MinusExpression.class, unary);
|
||||
}
|
||||
|
||||
/**
|
||||
* Match the form {@code ~U}
|
||||
*
|
||||
* @param unary the child matcher
|
||||
* @return the matcher
|
||||
*/
|
||||
default ExpressionMatcher<NotExpression> not(ExpressionMatcher<?> unary) {
|
||||
return new UnaryExpressionMatcher<>(NotExpression.class, unary);
|
||||
}
|
||||
}
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
/* ###
|
||||
* 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.assembler.sleigh.expr.match;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import ghidra.app.plugin.processors.sleigh.expression.*;
|
||||
|
||||
/**
|
||||
* A matcher for a token or context field, constrained by its size in bits
|
||||
*/
|
||||
public class FieldSizeMatcher extends AbstractExpressionMatcher<PatternValue> {
|
||||
protected final ExpressionMatcher<?> sizeMatcher;
|
||||
|
||||
public FieldSizeMatcher(ExpressionMatcher<?> sizeMatcher) {
|
||||
super(Set.of(ContextField.class, TokenField.class));
|
||||
this.sizeMatcher = sizeMatcher;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean matchDetails(PatternValue expression,
|
||||
Map<ExpressionMatcher<?>, PatternExpression> result) {
|
||||
if (expression instanceof ContextField) {
|
||||
ContextField cf = (ContextField) expression;
|
||||
long size = cf.getEndBit() - cf.getStartBit() + 1;
|
||||
return sizeMatcher.match(new ConstantValue(size), result);
|
||||
}
|
||||
if (expression instanceof TokenField) {
|
||||
TokenField tf = (TokenField) expression;
|
||||
long size = tf.getBitEnd() - tf.getBitStart() + 1;
|
||||
return sizeMatcher.match(new ConstantValue(size), result);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.plugin.assembler.sleigh.expr.match;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import ghidra.app.plugin.assembler.sleigh.expr.OperandValueSolver;
|
||||
import ghidra.app.plugin.processors.sleigh.expression.OperandValue;
|
||||
import ghidra.app.plugin.processors.sleigh.expression.PatternExpression;
|
||||
import ghidra.app.plugin.processors.sleigh.symbol.OperandSymbol;
|
||||
|
||||
/**
|
||||
* A matcher for a constructor's operand value, constrained by its defining expression
|
||||
*/
|
||||
public class OperandValueMatcher extends AbstractExpressionMatcher<OperandValue> {
|
||||
protected final ExpressionMatcher<?> defMatcher;
|
||||
|
||||
public OperandValueMatcher(ExpressionMatcher<?> defMatcher) {
|
||||
super(OperandValue.class);
|
||||
this.defMatcher = defMatcher;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean matchDetails(OperandValue expression,
|
||||
Map<ExpressionMatcher<?>, PatternExpression> result) {
|
||||
OperandSymbol opSym = expression.getConstructor().getOperand(expression.getIndex());
|
||||
return defMatcher.match(OperandValueSolver.getDefiningExpression(opSym), result);
|
||||
}
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
/* ###
|
||||
* 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.assembler.sleigh.expr.match;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import ghidra.app.plugin.processors.sleigh.expression.PatternExpression;
|
||||
import ghidra.app.plugin.processors.sleigh.expression.UnaryExpression;
|
||||
|
||||
/**
|
||||
* A matcher for a unnary expression
|
||||
*
|
||||
* <p>
|
||||
* If the required type matches, the matching descends to the child operand.
|
||||
*
|
||||
* @param <T> the type of expression matched
|
||||
*/
|
||||
public class UnaryExpressionMatcher<T extends UnaryExpression>
|
||||
extends AbstractExpressionMatcher<T> {
|
||||
protected final ExpressionMatcher<?> unaryMatcher;
|
||||
|
||||
public UnaryExpressionMatcher(Set<Class<? extends T>> ops, ExpressionMatcher<?> unaryMatcher) {
|
||||
super(ops);
|
||||
this.unaryMatcher = unaryMatcher;
|
||||
}
|
||||
|
||||
public UnaryExpressionMatcher(Class<T> cls, ExpressionMatcher<?> unaryMatcher) {
|
||||
super(cls);
|
||||
this.unaryMatcher = unaryMatcher;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean matchDetails(T expression,
|
||||
Map<ExpressionMatcher<?>, PatternExpression> result) {
|
||||
return unaryMatcher.match(expression.getUnary(), result);
|
||||
}
|
||||
}
|
||||
+32
-13
@@ -34,16 +34,17 @@ import ghidra.generic.util.datastruct.TreeSetValuedTreeMap;
|
||||
/**
|
||||
* Defines a context-free grammar, usually for the purpose of parsing mnemonic assembly instructions
|
||||
*
|
||||
* As in classic computer science, a CFG consists of productions of non-terminals and terminals.
|
||||
* The left-hand side of the a production must be a single non-terminal, but the right-hand side
|
||||
* may be any string of symbols. To avoid overloading the term "String," here we call it a
|
||||
* "Sentential."
|
||||
* <p>
|
||||
* As in classic computer science, a CFG consists of productions of non-terminals and terminals. The
|
||||
* left-hand side of the a production must be a single non-terminal, but the right-hand side may be
|
||||
* any string of symbols. To avoid overloading the term "String," here we call it a "Sentential."
|
||||
*
|
||||
* <p>
|
||||
* To define a grammar, simply construct an appropriate subclass (probably {@link AssemblyGrammar})
|
||||
* and call {@link #addProduction(AbstractAssemblyProduction)} or
|
||||
* {@link #addProduction(AssemblyNonTerminal, AssemblySentential)}. The grammar object will collect
|
||||
* the non-terminals and terminals.
|
||||
* {@link #addProduction(AssemblyNonTerminal, AssemblySentential)}.
|
||||
*
|
||||
* <p>
|
||||
* By default, the start symbol is taken from the left-hand side of the first production added to
|
||||
* the grammar.
|
||||
*
|
||||
@@ -71,6 +72,7 @@ public abstract class AbstractAssemblyGrammar<NT extends AssemblyNonTerminal, P
|
||||
|
||||
/**
|
||||
* Add a production to the grammar
|
||||
*
|
||||
* @param lhs the left-hand side
|
||||
* @param rhs the right-hand side
|
||||
*/
|
||||
@@ -81,6 +83,7 @@ public abstract class AbstractAssemblyGrammar<NT extends AssemblyNonTerminal, P
|
||||
|
||||
/**
|
||||
* Add a production to the grammar
|
||||
*
|
||||
* @param prod the production
|
||||
*/
|
||||
public void addProduction(P prod) {
|
||||
@@ -96,7 +99,7 @@ public abstract class AbstractAssemblyGrammar<NT extends AssemblyNonTerminal, P
|
||||
String lhsName = lhs.getName();
|
||||
symbols.put(lhsName, lhs);
|
||||
nonterminals.put(lhsName, lhs);
|
||||
for (AssemblySymbol sym : prod) {
|
||||
for (AssemblySymbol sym : prod.getRHS()) {
|
||||
if (sym instanceof AssemblyNonTerminal) {
|
||||
@SuppressWarnings("unchecked")
|
||||
NT nt = (NT) sym;
|
||||
@@ -115,14 +118,15 @@ public abstract class AbstractAssemblyGrammar<NT extends AssemblyNonTerminal, P
|
||||
|
||||
/**
|
||||
* Check if the given production is purely recursive, i.e., of the form I => I
|
||||
*
|
||||
* @param prod the production to check
|
||||
* @return true iff the production is purely recursive
|
||||
*/
|
||||
protected boolean isPureRecursive(P prod) {
|
||||
if (prod.size() != 1) {
|
||||
if (prod.getRHS().size() != 1) {
|
||||
return false;
|
||||
}
|
||||
if (!prod.getLHS().equals(prod.getRHS().get(0))) {
|
||||
if (!prod.getLHS().equals(prod.getRHS().getSymbol(0))) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -130,6 +134,7 @@ public abstract class AbstractAssemblyGrammar<NT extends AssemblyNonTerminal, P
|
||||
|
||||
/**
|
||||
* Change the start symbol for the grammar
|
||||
*
|
||||
* @param nt the new start symbol
|
||||
*/
|
||||
public void setStart(AssemblyNonTerminal nt) {
|
||||
@@ -138,6 +143,7 @@ public abstract class AbstractAssemblyGrammar<NT extends AssemblyNonTerminal, P
|
||||
|
||||
/**
|
||||
* Change the start symbol for the grammar
|
||||
*
|
||||
* @param startName the name of the new start symbol
|
||||
*/
|
||||
public void setStartName(String startName) {
|
||||
@@ -146,6 +152,7 @@ public abstract class AbstractAssemblyGrammar<NT extends AssemblyNonTerminal, P
|
||||
|
||||
/**
|
||||
* Get the start symbol for the grammar
|
||||
*
|
||||
* @return the start symbol
|
||||
*/
|
||||
public NT getStart() {
|
||||
@@ -154,6 +161,7 @@ public abstract class AbstractAssemblyGrammar<NT extends AssemblyNonTerminal, P
|
||||
|
||||
/**
|
||||
* Get the name of the start symbol for the grammar
|
||||
*
|
||||
* @return the name of the start symbol
|
||||
*/
|
||||
public String getStartName() {
|
||||
@@ -162,6 +170,7 @@ public abstract class AbstractAssemblyGrammar<NT extends AssemblyNonTerminal, P
|
||||
|
||||
/**
|
||||
* Get the named non-terminal
|
||||
*
|
||||
* @param name the name of the desired non-terminal
|
||||
* @return the non-terminal, or null if it is not in this grammar
|
||||
*/
|
||||
@@ -171,6 +180,7 @@ public abstract class AbstractAssemblyGrammar<NT extends AssemblyNonTerminal, P
|
||||
|
||||
/**
|
||||
* Get the named terminal
|
||||
*
|
||||
* @param name the name of the desired terminal
|
||||
* @return the terminal, or null if it is not in this grammar
|
||||
*/
|
||||
@@ -180,6 +190,7 @@ public abstract class AbstractAssemblyGrammar<NT extends AssemblyNonTerminal, P
|
||||
|
||||
/**
|
||||
* Add all the productions of a given grammar to this one
|
||||
*
|
||||
* @param that the grammar whose productions to add
|
||||
*/
|
||||
public void combine(AbstractAssemblyGrammar<NT, P> that) {
|
||||
@@ -190,6 +201,7 @@ public abstract class AbstractAssemblyGrammar<NT extends AssemblyNonTerminal, P
|
||||
|
||||
/**
|
||||
* Print the productions of this grammar to the given stream
|
||||
*
|
||||
* @param out the stream
|
||||
*/
|
||||
public void print(PrintStream out) {
|
||||
@@ -201,17 +213,19 @@ public abstract class AbstractAssemblyGrammar<NT extends AssemblyNonTerminal, P
|
||||
/**
|
||||
* Check that the grammar is consistent
|
||||
*
|
||||
* The grammar is consistent if every non-terminal appearing in the grammar, also appears as
|
||||
* the left-hand side of some production. If not, such non-terminals are said to be undefined.
|
||||
* <p>
|
||||
* The grammar is consistent if every non-terminal appearing in the grammar also appears as the
|
||||
* left-hand side of some production. If not, such non-terminals are said to be undefined.
|
||||
*
|
||||
* @throws AssemblyGrammarException the grammar is inconsistent, i.e., contains undefined
|
||||
* non-terminals.
|
||||
* non-terminals.
|
||||
*/
|
||||
public void verify() throws AssemblyGrammarException {
|
||||
if (!productions.containsKey(startName)) {
|
||||
throw new AssemblyGrammarException("Start symbol has no defining production");
|
||||
}
|
||||
for (P prod : productions.values()) {
|
||||
for (AssemblySymbol sym : prod) {
|
||||
for (AssemblySymbol sym : prod.getRHS()) {
|
||||
if (sym instanceof AssemblyNonTerminal) {
|
||||
AssemblyNonTerminal nt = (AssemblyNonTerminal) sym;
|
||||
if (!(productions.containsKey(nt.getName()))) {
|
||||
@@ -233,6 +247,7 @@ public abstract class AbstractAssemblyGrammar<NT extends AssemblyNonTerminal, P
|
||||
|
||||
/**
|
||||
* Get the non-terminals
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Collection<NT> nonTerminals() {
|
||||
@@ -241,6 +256,7 @@ public abstract class AbstractAssemblyGrammar<NT extends AssemblyNonTerminal, P
|
||||
|
||||
/**
|
||||
* Get the terminals
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Collection<AssemblyTerminal> terminals() {
|
||||
@@ -249,6 +265,7 @@ public abstract class AbstractAssemblyGrammar<NT extends AssemblyNonTerminal, P
|
||||
|
||||
/**
|
||||
* Get all productions where the left-hand side non-terminal has the given name
|
||||
*
|
||||
* @param name the name of the non-terminal
|
||||
* @return all productions "defining" the named non-terminal
|
||||
*/
|
||||
@@ -261,6 +278,7 @@ public abstract class AbstractAssemblyGrammar<NT extends AssemblyNonTerminal, P
|
||||
|
||||
/**
|
||||
* Get all productions where the left-hand side is the given non-terminal
|
||||
*
|
||||
* @param nt the non-terminal whose defining productions to find
|
||||
* @return all productions "defining" the given non-terminal
|
||||
*/
|
||||
@@ -270,6 +288,7 @@ public abstract class AbstractAssemblyGrammar<NT extends AssemblyNonTerminal, P
|
||||
|
||||
/**
|
||||
* Check if the grammar contains any symbol with the given name
|
||||
*
|
||||
* @param name the name to find
|
||||
* @return true iff a terminal or non-terminal has the given name
|
||||
*/
|
||||
|
||||
+8
-17
@@ -15,12 +15,7 @@
|
||||
*/
|
||||
package ghidra.app.plugin.assembler.sleigh.grammars;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.collections4.list.AbstractListDecorator;
|
||||
|
||||
import ghidra.app.plugin.assembler.sleigh.symbol.AssemblyNonTerminal;
|
||||
import ghidra.app.plugin.assembler.sleigh.symbol.AssemblySymbol;
|
||||
|
||||
/**
|
||||
* Defines a production in a context-free grammar, usually for parsing mnemonic assembly
|
||||
@@ -29,7 +24,6 @@ import ghidra.app.plugin.assembler.sleigh.symbol.AssemblySymbol;
|
||||
* @param <NT> the type of non-terminals
|
||||
*/
|
||||
public abstract class AbstractAssemblyProduction<NT extends AssemblyNonTerminal>
|
||||
extends AbstractListDecorator<AssemblySymbol>
|
||||
implements Comparable<AbstractAssemblyProduction<NT>> {
|
||||
private final NT lhs;
|
||||
private final AssemblySentential<NT> rhs;
|
||||
@@ -38,6 +32,7 @@ public abstract class AbstractAssemblyProduction<NT extends AssemblyNonTerminal>
|
||||
|
||||
/**
|
||||
* Construct a production with the given LHS and RHS
|
||||
*
|
||||
* @param lhs the left-hand side
|
||||
* @param rhs the right-hand side
|
||||
*/
|
||||
@@ -47,16 +42,13 @@ public abstract class AbstractAssemblyProduction<NT extends AssemblyNonTerminal>
|
||||
this.rhs = rhs;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<AssemblySymbol> decorated() {
|
||||
return rhs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the index of the production
|
||||
*
|
||||
* Instead of using deep comparison, the index is often used as the identify of the production
|
||||
* <p>
|
||||
* Instead of using deep comparison, the index is often used as the identity of the production
|
||||
* within a grammar.
|
||||
*
|
||||
* @return the index
|
||||
*/
|
||||
public int getIndex() {
|
||||
@@ -65,6 +57,7 @@ public abstract class AbstractAssemblyProduction<NT extends AssemblyNonTerminal>
|
||||
|
||||
/**
|
||||
* Get the left-hand side
|
||||
*
|
||||
* @return the LHS
|
||||
*/
|
||||
public NT getLHS() {
|
||||
@@ -73,6 +66,7 @@ public abstract class AbstractAssemblyProduction<NT extends AssemblyNonTerminal>
|
||||
|
||||
/**
|
||||
* Get the right-hand side
|
||||
*
|
||||
* @return the RHS
|
||||
*/
|
||||
public AssemblySentential<NT> getRHS() {
|
||||
@@ -123,15 +117,12 @@ public abstract class AbstractAssemblyProduction<NT extends AssemblyNonTerminal>
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AssemblySentential<NT> subList(int fromIndex, int toIndex) {
|
||||
return rhs.subList(fromIndex, toIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the "name" of this production
|
||||
*
|
||||
* <p>
|
||||
* This is mostly just notional and for debugging. The name is taken as the name of the LHS.
|
||||
*
|
||||
* @return the name of the LHS
|
||||
*/
|
||||
public String getName() {
|
||||
|
||||
+4
-3
@@ -20,9 +20,10 @@ import ghidra.app.plugin.assembler.sleigh.symbol.AssemblyExtendedNonTerminal;
|
||||
/**
|
||||
* Defines an "extended" grammar
|
||||
*
|
||||
* "Extended grammar" as in a grammar extended with state numbers from an LR0 parser.
|
||||
* See <a href="http://web.cs.dal.ca/~sjackson/lalr1.html">LALR(1) Parsing</a> from Stephen Jackson
|
||||
* of Dalhousie University, Halifax, Nova Scotia, Canada.
|
||||
* <p>
|
||||
* "Extended grammar" as in a grammar extended with state numbers from an LR0 parser. See
|
||||
* <a href="http://web.cs.dal.ca/~sjackson/lalr1.html">LALR(1) Parsing</a> from Stephen Jackson of
|
||||
* Dalhousie University, Halifax, Nova Scotia, Canada.
|
||||
*/
|
||||
public class AssemblyExtendedGrammar
|
||||
extends AbstractAssemblyGrammar<AssemblyExtendedNonTerminal, AssemblyExtendedProduction> {
|
||||
|
||||
+3
@@ -29,6 +29,7 @@ public class AssemblyExtendedProduction
|
||||
|
||||
/**
|
||||
* Construct an extended production based on the given ancestor
|
||||
*
|
||||
* @param lhs the extended left-hand side
|
||||
* @param rhs the extended right-hand side
|
||||
* @param finalState the end state of the final symbol of the RHS
|
||||
@@ -49,6 +50,7 @@ public class AssemblyExtendedProduction
|
||||
|
||||
/**
|
||||
* Get the final state of this production
|
||||
*
|
||||
* @return the end state of the last symbol of the RHS
|
||||
*/
|
||||
public int getFinalState() {
|
||||
@@ -57,6 +59,7 @@ public class AssemblyExtendedProduction
|
||||
|
||||
/**
|
||||
* Get the original production from which this production was derived
|
||||
*
|
||||
* @return the original production
|
||||
*/
|
||||
public AssemblyProduction getAncestor() {
|
||||
|
||||
+23
-13
@@ -17,8 +17,6 @@ package ghidra.app.plugin.assembler.sleigh.grammars;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import org.apache.commons.collections4.map.LazyMap;
|
||||
|
||||
import ghidra.app.plugin.assembler.sleigh.sem.AssemblyConstructorSemantic;
|
||||
import ghidra.app.plugin.assembler.sleigh.symbol.AssemblyNonTerminal;
|
||||
import ghidra.app.plugin.processors.sleigh.Constructor;
|
||||
@@ -27,6 +25,7 @@ import ghidra.app.plugin.processors.sleigh.pattern.DisjointPattern;
|
||||
/**
|
||||
* Defines a context free grammar, used to parse mnemonic assembly instructions
|
||||
*
|
||||
* <p>
|
||||
* This stores the CFG and the associated semantics for each production. It also has mechanisms for
|
||||
* tracking "purely recursive" productions. These are productions of the form I => I, and they
|
||||
* necessarily create ambiguity. Thus, when constructing a parser, it is useful to identify them
|
||||
@@ -35,8 +34,10 @@ import ghidra.app.plugin.processors.sleigh.pattern.DisjointPattern;
|
||||
public class AssemblyGrammar
|
||||
extends AbstractAssemblyGrammar<AssemblyNonTerminal, AssemblyProduction> {
|
||||
// a nested map of semantics by production, by constructor
|
||||
protected final Map<AssemblyProduction, Map<Constructor, AssemblyConstructorSemantic>> semantics =
|
||||
LazyMap.lazyMap(new TreeMap<>(), () -> new TreeMap<>());
|
||||
protected final Map<AssemblyProduction, Map<Constructor, AssemblyConstructorSemantic>> semanticsByProduction =
|
||||
new TreeMap<>();
|
||||
protected final Map<Constructor, AssemblyConstructorSemantic> semanticsByConstructor =
|
||||
new HashMap<>();
|
||||
// a map of purely recursive, e.g., I => I, productions by name of LHS
|
||||
protected final Map<String, AssemblyProduction> pureRecursive = new TreeMap<>();
|
||||
|
||||
@@ -58,6 +59,7 @@ public class AssemblyGrammar
|
||||
|
||||
/**
|
||||
* Add a production associated with a SLEIGH constructor semantic
|
||||
*
|
||||
* @param lhs the left-hand side
|
||||
* @param rhs the right-hand side
|
||||
* @param pattern the pattern associated with the constructor
|
||||
@@ -68,27 +70,32 @@ public class AssemblyGrammar
|
||||
DisjointPattern pattern, Constructor cons, List<Integer> indices) {
|
||||
AssemblyProduction prod = newProduction(lhs, rhs);
|
||||
addProduction(prod);
|
||||
Map<Constructor, AssemblyConstructorSemantic> map = semantics.get(prod);
|
||||
AssemblyConstructorSemantic sem = map.get(cons);
|
||||
if (sem == null) {
|
||||
sem = new AssemblyConstructorSemantic(cons, indices);
|
||||
map.put(cons, sem);
|
||||
}
|
||||
else if (!indices.equals(sem.getOperandIndices())) {
|
||||
Map<Constructor, AssemblyConstructorSemantic> map =
|
||||
semanticsByProduction.computeIfAbsent(prod, p -> new TreeMap<>());
|
||||
AssemblyConstructorSemantic sem =
|
||||
map.computeIfAbsent(cons, c -> new AssemblyConstructorSemantic(cons, indices));
|
||||
if (!indices.equals(sem.getOperandIndices())) {
|
||||
throw new IllegalStateException(
|
||||
"Productions of the same constructor must have same operand indices");
|
||||
}
|
||||
semanticsByConstructor.put(cons, sem);
|
||||
|
||||
sem.addPattern(pattern);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the semantics associated with a given production
|
||||
*
|
||||
* @param prod the production
|
||||
* @return all semantics associated with the given production
|
||||
*/
|
||||
public Collection<AssemblyConstructorSemantic> getSemantics(AssemblyProduction prod) {
|
||||
return Collections.unmodifiableCollection(semantics.get(prod).values());
|
||||
return Collections.unmodifiableCollection(
|
||||
semanticsByProduction.computeIfAbsent(prod, p -> new TreeMap<>()).values());
|
||||
}
|
||||
|
||||
public AssemblyConstructorSemantic getSemantic(Constructor cons) {
|
||||
return semanticsByConstructor.get(cons);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -96,13 +103,15 @@ public class AssemblyGrammar
|
||||
super.combine(that);
|
||||
if (that instanceof AssemblyGrammar) {
|
||||
AssemblyGrammar ag = (AssemblyGrammar) that;
|
||||
this.semantics.putAll(ag.semantics);
|
||||
this.semanticsByProduction.putAll(ag.semanticsByProduction);
|
||||
this.semanticsByConstructor.putAll(ag.semanticsByConstructor);
|
||||
this.pureRecursive.putAll(ag.pureRecursive);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all productions in the grammar that are purely recursive
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Collection<AssemblyProduction> getPureRecursive() {
|
||||
@@ -111,6 +120,7 @@ public class AssemblyGrammar
|
||||
|
||||
/**
|
||||
* Obtain, if present, the purely recursive production having the given LHS
|
||||
*
|
||||
* @param lhs the left-hand side
|
||||
* @return the desired production, or null
|
||||
*/
|
||||
|
||||
+136
-18
@@ -16,8 +16,9 @@
|
||||
package ghidra.app.plugin.assembler.sleigh.grammars;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import org.apache.commons.collections4.list.AbstractListDecorator;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import ghidra.app.plugin.assembler.sleigh.symbol.*;
|
||||
import ghidra.app.plugin.assembler.sleigh.tree.AssemblyParseToken;
|
||||
@@ -25,29 +26,29 @@ import ghidra.app.plugin.assembler.sleigh.tree.AssemblyParseToken;
|
||||
/**
|
||||
* A "string" of symbols
|
||||
*
|
||||
* To avoid overloading the word "String", we call this a "sentential". Technically, to be a
|
||||
* <p>
|
||||
* To avoid overloading the word "string", we call this a "sentential". Technically, to be a
|
||||
* "sentential" in the classic sense, it must be a possible element in the derivation of a sentence
|
||||
* in the grammar starting with the start symbol. We ignore that if only for the sake of naming.
|
||||
*
|
||||
* @param <NT> the type of non-terminals
|
||||
*/
|
||||
public class AssemblySentential<NT extends AssemblyNonTerminal> extends
|
||||
AbstractListDecorator<AssemblySymbol> implements Comparable<AssemblySentential<NT>> {
|
||||
public class AssemblySentential<NT extends AssemblyNonTerminal>
|
||||
implements Comparable<AssemblySentential<NT>>, Iterable<AssemblySymbol> {
|
||||
private List<AssemblySymbol> symbols;
|
||||
private final List<AssemblySymbol> unmodifiableSymbols;
|
||||
private boolean finished = false;
|
||||
public static final AssemblyStringTerminal WHITE_SPACE = new WhiteSpace();
|
||||
private static final Pattern PAT_COMMA_WS = Pattern.compile(",\\s+");
|
||||
|
||||
/**
|
||||
* Construct a string from the given list of symbols
|
||||
*
|
||||
* @param symbols
|
||||
*/
|
||||
public AssemblySentential(List<? extends AssemblySymbol> symbols) {
|
||||
this.symbols = new ArrayList<>(symbols);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<AssemblySymbol> decorated() {
|
||||
return symbols;
|
||||
this.unmodifiableSymbols = Collections.unmodifiableList(symbols);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -58,19 +59,22 @@ public class AssemblySentential<NT extends AssemblyNonTerminal> extends
|
||||
*/
|
||||
public AssemblySentential() {
|
||||
this.symbols = new ArrayList<>();
|
||||
this.unmodifiableSymbols = Collections.unmodifiableList(symbols);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a string from any number of symbols
|
||||
*
|
||||
* @param syms
|
||||
*/
|
||||
public AssemblySentential(AssemblySymbol... syms) {
|
||||
this.symbols = Arrays.asList(syms);
|
||||
this.unmodifiableSymbols = Collections.unmodifiableList(symbols);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (symbols.size() == 0) {
|
||||
if (symbols.isEmpty()) {
|
||||
return "e";
|
||||
}
|
||||
Iterator<? extends AssemblySymbol> symIt = symbols.iterator();
|
||||
@@ -117,6 +121,7 @@ public class AssemblySentential<NT extends AssemblyNonTerminal> extends
|
||||
/**
|
||||
* A "whitespace" terminal
|
||||
*
|
||||
* <p>
|
||||
* This terminal represents "optional" whitespace. "Optional" because in certain circumstances,
|
||||
* whitespace is not actually required, i.e., before or after a special character.
|
||||
*/
|
||||
@@ -132,7 +137,7 @@ public class AssemblySentential<NT extends AssemblyNonTerminal> extends
|
||||
|
||||
@Override
|
||||
public Collection<AssemblyParseToken> match(String buffer, int pos, AssemblyGrammar grammar,
|
||||
Map<String, Long> labels) {
|
||||
AssemblyNumericSymbols symbols) {
|
||||
if (buffer.length() == 0) {
|
||||
return Collections.singleton(new WhiteSpaceParseToken(grammar, this, ""));
|
||||
}
|
||||
@@ -158,7 +163,7 @@ public class AssemblySentential<NT extends AssemblyNonTerminal> extends
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<String> getSuggestions(String got, Map<String, Long> labels) {
|
||||
public Collection<String> getSuggestions(String got, AssemblyNumericSymbols symbols) {
|
||||
return Collections.singleton(" ");
|
||||
}
|
||||
}
|
||||
@@ -175,6 +180,7 @@ public class AssemblySentential<NT extends AssemblyNonTerminal> extends
|
||||
/**
|
||||
* The token consumed by a whitespace terminal when it anticipates the end of input
|
||||
*
|
||||
* <p>
|
||||
* "Expected" tokens given by a parse machine when this is the last token it has consumed are
|
||||
* not valid suggestions. The machine should instead suggest a whitespace character.
|
||||
*/
|
||||
@@ -185,7 +191,18 @@ public class AssemblySentential<NT extends AssemblyNonTerminal> extends
|
||||
}
|
||||
|
||||
/**
|
||||
* Add "optional" whitespace, if not already preceded by whitespace
|
||||
* Add a symbol to the right of this sentential
|
||||
*
|
||||
* @param symbol the symbol to add
|
||||
* @return true
|
||||
*/
|
||||
public boolean addSymbol(AssemblySymbol symbol) {
|
||||
return symbols.add(symbol);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add optional whitespace, if not already preceded by whitespace
|
||||
*
|
||||
* @return true if whitespace was added
|
||||
*/
|
||||
public boolean addWS() {
|
||||
@@ -193,7 +210,95 @@ public class AssemblySentential<NT extends AssemblyNonTerminal> extends
|
||||
if (last != null) {
|
||||
return false;
|
||||
}
|
||||
return add(WHITE_SPACE);
|
||||
return addSymbol(WHITE_SPACE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a comma followed by optional whitespace.
|
||||
*/
|
||||
public void addCommaWS() {
|
||||
addSymbol(new AssemblyStringTerminal(","));
|
||||
addWS();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a syntactic terminal element, but with consideration for optional whitespace surrounding
|
||||
* special characters
|
||||
*
|
||||
* @param str the expected terminal
|
||||
*/
|
||||
public void addSeparatorPart(String str) {
|
||||
String tstr = str.trim();
|
||||
if (tstr.equals("")) {
|
||||
addWS();
|
||||
return;
|
||||
}
|
||||
char first = tstr.charAt(0);
|
||||
if (!str.startsWith(tstr)) {
|
||||
addWS();
|
||||
}
|
||||
if (!Character.isLetterOrDigit(first)) {
|
||||
addWS();
|
||||
}
|
||||
addSymbol(new AssemblyStringTerminal(tstr));
|
||||
char last = tstr.charAt(tstr.length() - 1);
|
||||
if (!str.endsWith(tstr)) {
|
||||
addWS();
|
||||
}
|
||||
if (!Character.isLetterOrDigit(last)) {
|
||||
addWS();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the symbols in this sentential
|
||||
*
|
||||
* @return the symbols;
|
||||
*/
|
||||
public List<AssemblySymbol> getSymbols() {
|
||||
return unmodifiableSymbols;
|
||||
}
|
||||
|
||||
public AssemblySymbol getSymbol(int pos) {
|
||||
return symbols.get(pos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Split the given string into pieces matched by the pattern, and the pieces between
|
||||
*
|
||||
* <p>
|
||||
* This invokes the given callbacks as the string is processed from left to right.
|
||||
*
|
||||
* @param str the string to split
|
||||
* @param pat the pattern to match
|
||||
* @param matched the callback for matched portions
|
||||
* @param unmatched the callback for unmatched portions
|
||||
*/
|
||||
private static void forMatchUnmatch(String str, Pattern pat, Consumer<String> matched,
|
||||
Consumer<String> unmatched) {
|
||||
int startU = 0;
|
||||
Matcher mat = pat.matcher(str);
|
||||
while (mat.find()) {
|
||||
if (startU < mat.start()) {
|
||||
unmatched.accept(str.substring(startU, mat.start()));
|
||||
}
|
||||
matched.accept(mat.group());
|
||||
startU = mat.end();
|
||||
}
|
||||
if (startU < str.length()) {
|
||||
unmatched.accept(str.substring(startU));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a syntactic terminal element, but considering that commas contained within may be
|
||||
* followed by optional whitespace
|
||||
*
|
||||
* @param str the expected terminal
|
||||
*/
|
||||
public void addSeparators(String str) {
|
||||
// NB. When displaying print pieces, the disassembler replaces all ",\\s+" with ","
|
||||
forMatchUnmatch(str, PAT_COMMA_WS, matched -> addCommaWS(), this::addSeparatorPart);
|
||||
}
|
||||
|
||||
// If the right-most symbol is whitespace, return it
|
||||
@@ -209,18 +314,31 @@ public class AssemblySentential<NT extends AssemblyNonTerminal> extends
|
||||
}
|
||||
|
||||
/**
|
||||
* Trim leading and trailing whitespace, and make the string immutable
|
||||
* Trim leading and trailing whitespace, and make the sentential immutable
|
||||
*/
|
||||
public void finish() {
|
||||
if (finished) {
|
||||
return;
|
||||
}
|
||||
symbols = Collections.unmodifiableList(symbols);
|
||||
symbols = unmodifiableSymbols;
|
||||
finished = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AssemblySentential<NT> subList(int fromIndex, int toIndex) {
|
||||
public Iterator<AssemblySymbol> iterator() {
|
||||
return unmodifiableSymbols.iterator();
|
||||
}
|
||||
|
||||
public AssemblySentential<NT> sub(int fromIndex, int toIndex) {
|
||||
return new AssemblySentential<>(symbols.subList(fromIndex, toIndex));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of symbols, including whitespace, in this sentential
|
||||
*
|
||||
* @return the number of symbols
|
||||
*/
|
||||
public int size() {
|
||||
return symbols.size();
|
||||
}
|
||||
}
|
||||
|
||||
+15
-6
@@ -28,6 +28,7 @@ import ghidra.generic.util.datastruct.TreeSetValuedTreeMap;
|
||||
/**
|
||||
* A class to compute the first and follow of every non-terminal in a grammar
|
||||
*
|
||||
* <p>
|
||||
* See Alfred V. Aho, Monica S. Lam, Ravi Sethi, Jeffrey D. Ullman, <i>Compilers: Principles,
|
||||
* Techniques, & Tools</i>. Bostom, MA: Pearson, 2007, pp. 220-2.
|
||||
*/
|
||||
@@ -43,6 +44,7 @@ public class AssemblyFirstFollow {
|
||||
|
||||
/**
|
||||
* Compute the first and follow sets for every non-terminal in the given grammar
|
||||
*
|
||||
* @param grammar the grammar
|
||||
*/
|
||||
public AssemblyFirstFollow(AbstractAssemblyGrammar<?, ?> grammar) {
|
||||
@@ -61,7 +63,7 @@ public class AssemblyFirstFollow {
|
||||
while (changed) {
|
||||
changed = false;
|
||||
for (AbstractAssemblyProduction<?> prod : grammar) {
|
||||
if (nullable.containsAll(prod)) {
|
||||
if (nullable.containsAll(prod.getRHS().getSymbols())) {
|
||||
changed |= nullable.add(prod.getLHS());
|
||||
}
|
||||
}
|
||||
@@ -81,7 +83,7 @@ public class AssemblyFirstFollow {
|
||||
// Add the first of all each symbol
|
||||
// Terminate after a terminal or non-nullable symbol
|
||||
for (AbstractAssemblyProduction<?> prod : grammar) {
|
||||
for (AssemblySymbol sym : prod) {
|
||||
for (AssemblySymbol sym : prod.getRHS()) {
|
||||
if (sym instanceof AssemblyNonTerminal) {
|
||||
AssemblyNonTerminal nt = (AssemblyNonTerminal) sym;
|
||||
changed |= first.putAll(prod.getLHS(), first.get(nt));
|
||||
@@ -116,13 +118,13 @@ public class AssemblyFirstFollow {
|
||||
// Finish the subwalk after a terminal or non-nullable symbol
|
||||
// If you hit the end, add follow(LHS) to follow the current symbol
|
||||
for (AbstractAssemblyProduction<?> prod : grammar) {
|
||||
nextX: for (int i = 0; i < prod.size(); i++) {
|
||||
AssemblySymbol px = prod.get(i);
|
||||
nextX: for (int i = 0; i < prod.getRHS().size(); i++) {
|
||||
AssemblySymbol px = prod.getRHS().getSymbol(i);
|
||||
if (px instanceof AssemblyNonTerminal) {
|
||||
AssemblyNonTerminal X = (AssemblyNonTerminal) px;
|
||||
int j;
|
||||
for (j = i + 1; j < prod.size(); j++) {
|
||||
AssemblySymbol B = prod.get(j);
|
||||
for (j = i + 1; j < prod.getRHS().size(); j++) {
|
||||
AssemblySymbol B = prod.getRHS().getSymbol(j);
|
||||
if (B instanceof AssemblyNonTerminal) {
|
||||
AssemblyNonTerminal nt = (AssemblyNonTerminal) B;
|
||||
changed |= follow.putAll(X, first.get(nt));
|
||||
@@ -149,7 +151,9 @@ public class AssemblyFirstFollow {
|
||||
/**
|
||||
* Get the nullable set
|
||||
*
|
||||
* <p>
|
||||
* That is the set of all non-terminals, which through some derivation, can produce epsilon.
|
||||
*
|
||||
* @return the set
|
||||
*/
|
||||
public Collection<AssemblyNonTerminal> getNullable() {
|
||||
@@ -159,8 +163,10 @@ public class AssemblyFirstFollow {
|
||||
/**
|
||||
* Get the first set for a given non-terminal
|
||||
*
|
||||
* <p>
|
||||
* That is the set of all terminals, which through some derivation from the given non-terminal,
|
||||
* can appear first in a sentential form.
|
||||
*
|
||||
* @param nt the non-terminal
|
||||
* @return the set
|
||||
*/
|
||||
@@ -171,8 +177,10 @@ public class AssemblyFirstFollow {
|
||||
/**
|
||||
* Get the follow set for a given non-terminal
|
||||
*
|
||||
* <p>
|
||||
* That is the set of all terminals, which through some derivation from the start symbol, can
|
||||
* appear immediately after the given non-terminal in a sentential form.
|
||||
*
|
||||
* @param nt the non-terminal
|
||||
* @return the set
|
||||
*/
|
||||
@@ -182,6 +190,7 @@ public class AssemblyFirstFollow {
|
||||
|
||||
/**
|
||||
* For debugging, print out the computed sets to the given stream
|
||||
*
|
||||
* @param out the stream
|
||||
*/
|
||||
public void print(PrintStream out) {
|
||||
|
||||
+1
@@ -40,6 +40,7 @@ public class AssemblyParseAcceptResult extends AssemblyParseResult {
|
||||
|
||||
/**
|
||||
* Get the tree
|
||||
*
|
||||
* @return the tree
|
||||
*/
|
||||
public AssemblyParseBranch getTree() {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user