mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-05-31 06:40:15 +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.program.model.listing.Instruction;
|
||||||
import ghidra.test.AbstractGhidraHeadlessIntegrationTest;
|
import ghidra.test.AbstractGhidraHeadlessIntegrationTest;
|
||||||
import ghidra.trace.database.ToyDBTraceBuilder;
|
import ghidra.trace.database.ToyDBTraceBuilder;
|
||||||
|
import ghidra.trace.database.context.DBTraceRegisterContextManager;
|
||||||
import ghidra.trace.model.memory.TraceMemoryFlag;
|
import ghidra.trace.model.memory.TraceMemoryFlag;
|
||||||
import ghidra.trace.model.memory.TraceMemoryManager;
|
import ghidra.trace.model.memory.TraceMemoryManager;
|
||||||
import ghidra.trace.model.thread.TraceThread;
|
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")) {
|
try (ToyDBTraceBuilder tb = new ToyDBTraceBuilder("Test", "x86:LE:64:default")) {
|
||||||
Language lang = tb.trace.getBaseLanguage();
|
Language lang = tb.trace.getBaseLanguage();
|
||||||
Register ctxReg = lang.getContextBaseRegister();
|
Register ctxReg = lang.getContextBaseRegister();
|
||||||
Register opsizeReg = lang.getRegister("opsize");
|
|
||||||
Register addrsizeReg = lang.getRegister("addrsize");
|
|
||||||
Register longModeReg = lang.getRegister("longMode");
|
Register longModeReg = lang.getRegister("longMode");
|
||||||
RegisterValue ctxVal = new RegisterValue(ctxReg)
|
RegisterValue ctxVal = new RegisterValue(ctxReg)
|
||||||
.assign(opsizeReg, BigInteger.ONE)
|
|
||||||
.assign(addrsizeReg, BigInteger.ONE)
|
|
||||||
.assign(longModeReg, BigInteger.ZERO);
|
.assign(longModeReg, BigInteger.ZERO);
|
||||||
|
DBTraceRegisterContextManager ctxManager = tb.trace.getRegisterContextManager();
|
||||||
try (UndoableTransaction tid = tb.startTransaction()) {
|
try (UndoableTransaction tid = tb.startTransaction()) {
|
||||||
tb.trace.getRegisterContextManager()
|
ctxManager.setValue(lang, ctxVal, Range.atLeast(0L),
|
||||||
.setValue(lang, ctxVal, Range.atLeast(0L),
|
tb.range(0x00400000, 0x00400002));
|
||||||
tb.range(0x00400000, 0x00400002));
|
|
||||||
}
|
}
|
||||||
TraceThread thread = initTrace(tb,
|
TraceThread thread = initTrace(tb,
|
||||||
List.of(
|
List.of(
|
||||||
@@ -891,6 +888,8 @@ public class TracePcodeEmulatorTest extends AbstractGhidraHeadlessIntegrationTes
|
|||||||
|
|
||||||
TracePcodeEmulator emu = new TracePcodeEmulator(tb.trace, 0);
|
TracePcodeEmulator emu = new TracePcodeEmulator(tb.trace, 0);
|
||||||
PcodeThread<byte[]> emuThread = emu.newThread(thread.getPath());
|
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.overrideContext(ctxVal);
|
||||||
emuThread.stepInstruction();
|
emuThread.stepInstruction();
|
||||||
emuThread.stepInstruction();
|
emuThread.stepInstruction();
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ public class AssemblyThrasherDevScript extends GhidraScript {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AssemblyResolvedConstructor select(AssemblyResolutionResults rr,
|
public AssemblyResolvedPatterns select(AssemblyResolutionResults rr,
|
||||||
AssemblyPatternBlock ctx) throws AssemblySemanticException {
|
AssemblyPatternBlock ctx) throws AssemblySemanticException {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
boolean gotOne = false;
|
boolean gotOne = false;
|
||||||
@@ -72,7 +72,7 @@ public class AssemblyThrasherDevScript extends GhidraScript {
|
|||||||
if (ar.isError()) {
|
if (ar.isError()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
AssemblyResolvedConstructor can = (AssemblyResolvedConstructor) ar;
|
AssemblyResolvedPatterns can = (AssemblyResolvedPatterns) ar;
|
||||||
if (can.getContext().combine(ctx) == null) {
|
if (can.getContext().combine(ctx) == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -704,7 +704,7 @@ public class AssemblyDualTextField {
|
|||||||
* @param existing the instruction, if any, currently under the user's cursor
|
* @param existing the instruction, if any, currently under the user's cursor
|
||||||
* @return a preference
|
* @return a preference
|
||||||
*/
|
*/
|
||||||
protected int computePreference(AssemblyResolvedConstructor rc, Instruction existing) {
|
protected int computePreference(AssemblyResolvedPatterns rc, Instruction existing) {
|
||||||
if (existing == null) {
|
if (existing == null) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -763,7 +763,7 @@ public class AssemblyDualTextField {
|
|||||||
//result.add(new AssemblyError("", ar.toString()));
|
//result.add(new AssemblyError("", ar.toString()));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
AssemblyResolvedConstructor rc = (AssemblyResolvedConstructor) ar;
|
AssemblyResolvedPatterns rc = (AssemblyResolvedPatterns) ar;
|
||||||
for (byte[] ins : rc.possibleInsVals(ctx)) {
|
for (byte[] ins : rc.possibleInsVals(ctx)) {
|
||||||
result.add(new AssemblyInstruction(text, Arrays.copyOf(ins, ins.length),
|
result.add(new AssemblyInstruction(text, Arrays.copyOf(ins, ins.length),
|
||||||
computePreference(rc, existing)));
|
computePreference(rc, existing)));
|
||||||
|
|||||||
+7
-6
@@ -42,7 +42,8 @@ public interface Assembler {
|
|||||||
* refer to pseudo instructions.
|
* refer to pseudo instructions.
|
||||||
*
|
*
|
||||||
* <p>
|
* <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 at the location where the resulting instructions should be placed
|
||||||
* @param listing a new-line separated or array sequence of instructions
|
* @param listing a new-line separated or array sequence of instructions
|
||||||
@@ -119,8 +120,8 @@ public interface Assembler {
|
|||||||
* results.
|
* results.
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* NOTE: The resolved instructions are given as masks and values. Where the mask does not cover,
|
* <b>NOTE:</b> The resolved instructions are given as masks and values. Where the mask does not
|
||||||
* you can choose any value.
|
* cover, you can choose any value.
|
||||||
*
|
*
|
||||||
* @param parse a parse result giving a valid tree
|
* @param parse a parse result giving a valid tree
|
||||||
* @param at the location of the start of the instruction
|
* @param at the location of the start of the instruction
|
||||||
@@ -139,8 +140,8 @@ public interface Assembler {
|
|||||||
* results.
|
* results.
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* NOTE: The resolved instructions are given as masks and values. Where the mask does not cover,
|
* <b>NOTE:</b> The resolved instructions are given as masks and values. Where the mask does not
|
||||||
* you can choose any value.
|
* cover, you can choose any value.
|
||||||
*
|
*
|
||||||
* @param parse a parse result giving a valid tree
|
* @param parse a parse result giving a valid tree
|
||||||
* @param at the location of the start of the instruction
|
* @param at the location of the start of the instruction
|
||||||
@@ -192,7 +193,7 @@ public interface Assembler {
|
|||||||
* @return the new {@link Instruction} code unit
|
* @return the new {@link Instruction} code unit
|
||||||
* @throws MemoryAccessException there is an issue writing the result to program memory
|
* @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;
|
throws MemoryAccessException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+4
@@ -25,18 +25,21 @@ import ghidra.program.model.listing.Program;
|
|||||||
public interface AssemblerBuilder {
|
public interface AssemblerBuilder {
|
||||||
/**
|
/**
|
||||||
* Get the ID of the language for which this instance builds an assembler
|
* Get the ID of the language for which this instance builds an assembler
|
||||||
|
*
|
||||||
* @return the language ID
|
* @return the language ID
|
||||||
*/
|
*/
|
||||||
public LanguageID getLanguageID();
|
public LanguageID getLanguageID();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the language for which this instance builds an assembler
|
* Get the language for which this instance builds an assembler
|
||||||
|
*
|
||||||
* @return the language
|
* @return the language
|
||||||
*/
|
*/
|
||||||
public Language getLanguage();
|
public Language getLanguage();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Build an assembler with the given selector callback
|
* Build an assembler with the given selector callback
|
||||||
|
*
|
||||||
* @param selector the selector callback
|
* @param selector the selector callback
|
||||||
* @return the built assembler
|
* @return the built assembler
|
||||||
*/
|
*/
|
||||||
@@ -44,6 +47,7 @@ public interface AssemblerBuilder {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Build an assembler with the given selector callback and program binding
|
* Build an assembler with the given selector callback and program binding
|
||||||
|
*
|
||||||
* @param selector the selector callback
|
* @param selector the selector callback
|
||||||
* @param program the bound program
|
* @param program the bound program
|
||||||
* @return the built assembler
|
* @return the built assembler
|
||||||
|
|||||||
+34
-27
@@ -19,19 +19,20 @@ import java.util.*;
|
|||||||
|
|
||||||
import ghidra.app.plugin.assembler.sleigh.parse.AssemblyParseResult;
|
import ghidra.app.plugin.assembler.sleigh.parse.AssemblyParseResult;
|
||||||
import ghidra.app.plugin.assembler.sleigh.sem.*;
|
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
|
* Provides a mechanism for pruning and selecting binary assembled instructions from the results of
|
||||||
* of parsing textual assembly instructions. There are two opportunities: After parsing, but before
|
* parsing textual assembly instructions. There are two opportunities: After parsing, but before
|
||||||
* semantic resolution, and after resolution. In the first opportunity, filtering is optional ---
|
* prototype generation, and after machine code generation. In the first opportunity, filtering is
|
||||||
* the user may discard any or all parse trees. The second is required, since only one instruction
|
* optional --- the user may discard any or all parse trees. The second is required, since only one
|
||||||
* may be placed at the desired address --- the user must select one instruction among the many
|
* instruction may be placed at the desired address --- the user must select one instruction among
|
||||||
* results, and if a mask is present, decide on a value for the omitted bits.
|
* 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
|
* 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
|
* assemblies. For example, an implementation may employ the syntax errors in order to produce code
|
||||||
* code completion suggestions in a GUI.
|
* completion suggestions in a GUI.
|
||||||
*/
|
*/
|
||||||
public class AssemblySelector {
|
public class AssemblySelector {
|
||||||
protected Set<AssemblyParseResult> syntaxErrors = new TreeSet<>();
|
protected Set<AssemblyParseResult> syntaxErrors = new TreeSet<>();
|
||||||
@@ -40,7 +41,7 @@ public class AssemblySelector {
|
|||||||
/**
|
/**
|
||||||
* A comparator on instruction length (shortest first), then bits lexicographically
|
* A comparator on instruction length (shortest first), then bits lexicographically
|
||||||
*/
|
*/
|
||||||
protected Comparator<AssemblyResolvedConstructor> compareBySizeThenBits = (a, b) -> {
|
protected Comparator<AssemblyResolvedPatterns> compareBySizeThenBits = (a, b) -> {
|
||||||
int result;
|
int result;
|
||||||
result = a.getInstructionLength() - b.getInstructionLength();
|
result = a.getInstructionLength() - b.getInstructionLength();
|
||||||
if (result != 0) {
|
if (result != 0) {
|
||||||
@@ -48,7 +49,7 @@ public class AssemblySelector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
result =
|
result =
|
||||||
SleighUtil.compareArrays(a.getInstruction().getVals(), b.getInstruction().getVals());
|
AsmUtil.compareArrays(a.getInstruction().getVals(), b.getInstruction().getVals());
|
||||||
if (result != 0) {
|
if (result != 0) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -58,16 +59,20 @@ public class AssemblySelector {
|
|||||||
/**
|
/**
|
||||||
* Filter a collection of parse trees.
|
* Filter a collection of parse trees.
|
||||||
*
|
*
|
||||||
* Generally, the assembly resolver considers every possible parsing of an assembly
|
* <p>
|
||||||
* instruction. If, for some reason, the user wishes to ignore certain trees (perhaps for
|
* Generally, the assembly resolver considers every possible parsing of an assembly instruction.
|
||||||
* efficiency, or perhaps because a certain form of instruction is desired), entire parse
|
* If, for some reason, the user wishes to ignore certain trees (perhaps for efficiency, or
|
||||||
* trees may be pruned here.
|
* 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
|
* <p>
|
||||||
* {@link AssemblySyntaxException}. Another option is to pass the erroneous result on for semantic
|
* It is possible that no trees pass the filter. In this case, this method ought to throw an
|
||||||
* analysis, in which case, the error is simply copied into an erroneous semantic result.
|
* {@link AssemblySyntaxException}. Another option is to pass the erroneous result on for
|
||||||
* Depending on preferences, this may simplify the overall filtering and error-handling logic.
|
* 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
|
* By default, no filtering is applied. If all the trees produce syntax errors, an exception is
|
||||||
* thrown.
|
* thrown.
|
||||||
*
|
*
|
||||||
@@ -95,10 +100,12 @@ public class AssemblySelector {
|
|||||||
/**
|
/**
|
||||||
* Select an instruction from the possible results.
|
* Select an instruction from the possible results.
|
||||||
*
|
*
|
||||||
* Must select precisely one resolved constructor from the results given back by the assembly
|
* <p>
|
||||||
* resolver. Precisely one. That means the mask of the returned result must consist of all 1s.
|
* This must select precisely one resolved constructor from the results given back by the
|
||||||
* Also, if no selection is suitable, an exception must be thrown.
|
* 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
|
* 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
|
* context and takes 0 for bits that fall outside the mask. If all possible resolutions produce
|
||||||
* errors, an exception is thrown.
|
* errors, an exception is thrown.
|
||||||
@@ -108,16 +115,16 @@ public class AssemblySelector {
|
|||||||
* @return a single resolved constructor with a full instruction mask.
|
* @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 {
|
AssemblyPatternBlock ctx) throws AssemblySemanticException {
|
||||||
List<AssemblyResolvedConstructor> sorted = new ArrayList<>();
|
List<AssemblyResolvedPatterns> sorted = new ArrayList<>();
|
||||||
// Select only non-erroneous results whose contexts are compatible.
|
// Select only non-erroneous results whose contexts are compatible.
|
||||||
for (AssemblyResolution ar : rr) {
|
for (AssemblyResolution ar : rr) {
|
||||||
if (ar.isError()) {
|
if (ar.isError()) {
|
||||||
semanticErrors.add((AssemblyResolvedError) ar);
|
semanticErrors.add((AssemblyResolvedError) ar);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
AssemblyResolvedConstructor rc = (AssemblyResolvedConstructor) ar;
|
AssemblyResolvedPatterns rc = (AssemblyResolvedPatterns) ar;
|
||||||
sorted.add(rc);
|
sorted.add(rc);
|
||||||
}
|
}
|
||||||
if (sorted.isEmpty()) {
|
if (sorted.isEmpty()) {
|
||||||
@@ -127,9 +134,9 @@ public class AssemblySelector {
|
|||||||
sorted.sort(compareBySizeThenBits);
|
sorted.sort(compareBySizeThenBits);
|
||||||
|
|
||||||
// Pick just the first
|
// 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)
|
// Just set the mask to ffs (effectively choosing 0 for the omitted bits)
|
||||||
return AssemblyResolution.resolved(res.getInstruction().fillMask(), res.getContext(),
|
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.
|
* Thrown when all resolutions of an assembly instruction result in semantic errors.
|
||||||
*
|
*
|
||||||
|
* <p>
|
||||||
* For SLEIGH, semantic errors amount to incompatible contexts
|
* For SLEIGH, semantic errors amount to incompatible contexts
|
||||||
*/
|
*/
|
||||||
public class AssemblySemanticException extends AssemblyException {
|
public class AssemblySemanticException extends AssemblyException {
|
||||||
@@ -37,6 +38,7 @@ public class AssemblySemanticException extends AssemblyException {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a semantic exception with the associated semantic errors
|
* Construct a semantic exception with the associated semantic errors
|
||||||
|
*
|
||||||
* @param errors the associated semantic errors
|
* @param errors the associated semantic errors
|
||||||
*/
|
*/
|
||||||
public AssemblySemanticException(Set<AssemblyResolvedError> errors) {
|
public AssemblySemanticException(Set<AssemblyResolvedError> errors) {
|
||||||
@@ -46,6 +48,7 @@ public class AssemblySemanticException extends AssemblyException {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the collection of associated semantic errors
|
* Get the collection of associated semantic errors
|
||||||
|
*
|
||||||
* @return the collection
|
* @return the collection
|
||||||
*/
|
*/
|
||||||
public Collection<AssemblyResolvedError> getErrors() {
|
public Collection<AssemblyResolvedError> getErrors() {
|
||||||
|
|||||||
+2
@@ -35,6 +35,7 @@ public class AssemblySyntaxException extends AssemblyException {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a syntax exception with the associated syntax errors
|
* Construct a syntax exception with the associated syntax errors
|
||||||
|
*
|
||||||
* @param errors the associated syntax errors
|
* @param errors the associated syntax errors
|
||||||
*/
|
*/
|
||||||
public AssemblySyntaxException(Set<AssemblyParseResult> errors) {
|
public AssemblySyntaxException(Set<AssemblyParseResult> errors) {
|
||||||
@@ -44,6 +45,7 @@ public class AssemblySyntaxException extends AssemblyException {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the collection of associated syntax errors
|
* Get the collection of associated syntax errors
|
||||||
|
*
|
||||||
* @return the collection
|
* @return the collection
|
||||||
*/
|
*/
|
||||||
public Collection<AssemblyParseResult> getErrors() {
|
public Collection<AssemblyParseResult> getErrors() {
|
||||||
|
|||||||
+16
-37
@@ -17,11 +17,12 @@ package ghidra.app.plugin.assembler.sleigh;
|
|||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.*;
|
import java.util.Collection;
|
||||||
|
|
||||||
import ghidra.app.plugin.assembler.*;
|
import ghidra.app.plugin.assembler.*;
|
||||||
import ghidra.app.plugin.assembler.sleigh.parse.*;
|
import ghidra.app.plugin.assembler.sleigh.parse.*;
|
||||||
import ghidra.app.plugin.assembler.sleigh.sem.*;
|
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.assembler.sleigh.util.DbgTimer;
|
||||||
import ghidra.app.plugin.processors.sleigh.SleighLanguage;
|
import ghidra.app.plugin.processors.sleigh.SleighLanguage;
|
||||||
import ghidra.program.disassemble.Disassembler;
|
import ghidra.program.disassemble.Disassembler;
|
||||||
@@ -32,17 +33,16 @@ import ghidra.program.model.lang.RegisterValue;
|
|||||||
import ghidra.program.model.listing.*;
|
import ghidra.program.model.listing.*;
|
||||||
import ghidra.program.model.mem.Memory;
|
import ghidra.program.model.mem.Memory;
|
||||||
import ghidra.program.model.mem.MemoryAccessException;
|
import ghidra.program.model.mem.MemoryAccessException;
|
||||||
import ghidra.program.model.symbol.*;
|
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An {@link Assembler} for a {@link SleighLanguage}.
|
* An {@link Assembler} for a {@link SleighLanguage}.
|
||||||
*
|
*
|
||||||
* To obtain one of these, please use {@link SleighAssemblerBuilder}, or better yet, the static
|
* <p>
|
||||||
* methods of {@link Assemblers}.
|
* 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 class SleighAssembler implements Assembler {
|
||||||
public static final int DEFAULT_MAX_RECURSION_DEPTH = 2; // TODO: Toss this
|
|
||||||
protected static final DbgTimer dbg = DbgTimer.INACTIVE;
|
protected static final DbgTimer dbg = DbgTimer.INACTIVE;
|
||||||
|
|
||||||
protected AssemblySelector selector;
|
protected AssemblySelector selector;
|
||||||
@@ -75,7 +75,8 @@ public class SleighAssembler implements Assembler {
|
|||||||
/**
|
/**
|
||||||
* Construct a SleighAssembler.
|
* 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 selector a method of selecting one result from many
|
||||||
* @param lang the SLEIGH language (must be same as to create the parser)
|
* @param lang the SLEIGH language (must be same as to create the parser)
|
||||||
@@ -93,7 +94,7 @@ public class SleighAssembler implements Assembler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Instruction patchProgram(AssemblyResolvedConstructor res, Address at)
|
public Instruction patchProgram(AssemblyResolvedPatterns res, Address at)
|
||||||
throws MemoryAccessException {
|
throws MemoryAccessException {
|
||||||
if (!res.getInstruction().isFullMask()) {
|
if (!res.getInstruction().isFullMask()) {
|
||||||
throw new AssemblySelectionError("Selected instruction must have a full mask.");
|
throw new AssemblySelectionError("Selected instruction must have a full mask.");
|
||||||
@@ -157,7 +158,7 @@ public class SleighAssembler implements Assembler {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<AssemblyParseResult> parseLine(String line) {
|
public Collection<AssemblyParseResult> parseLine(String line) {
|
||||||
return parser.parse(line, getProgramLabels());
|
return parser.parse(line, getNumericSymbols());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -173,13 +174,13 @@ public class SleighAssembler implements Assembler {
|
|||||||
if (parse.isError()) {
|
if (parse.isError()) {
|
||||||
AssemblyResolutionResults results = new AssemblyResolutionResults();
|
AssemblyResolutionResults results = new AssemblyResolutionResults();
|
||||||
AssemblyParseErrorResult err = (AssemblyParseErrorResult) parse;
|
AssemblyParseErrorResult err = (AssemblyParseErrorResult) parse;
|
||||||
results.add(AssemblyResolution.error(err.describeError(), "Parsing", null));
|
results.add(AssemblyResolution.error(err.describeError(), "Parsing"));
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
AssemblyParseAcceptResult acc = (AssemblyParseAcceptResult) parse;
|
AssemblyParseAcceptResult acc = (AssemblyParseAcceptResult) parse;
|
||||||
AssemblyTreeResolver tr =
|
AssemblyTreeResolver tr =
|
||||||
new AssemblyTreeResolver(lang, at.getOffset(), acc.getTree(), ctx, ctxGraph);
|
new AssemblyTreeResolver(lang, at, acc.getTree(), ctx, ctxGraph);
|
||||||
return tr.resolve();
|
return tr.resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -219,7 +220,7 @@ public class SleighAssembler implements Assembler {
|
|||||||
public byte[] assembleLine(Address at, String line, AssemblyPatternBlock ctx)
|
public byte[] assembleLine(Address at, String line, AssemblyPatternBlock ctx)
|
||||||
throws AssemblySemanticException, AssemblySyntaxException {
|
throws AssemblySemanticException, AssemblySyntaxException {
|
||||||
AssemblyResolutionResults results = resolveLine(at, line, ctx);
|
AssemblyResolutionResults results = resolveLine(at, line, ctx);
|
||||||
AssemblyResolvedConstructor res = selector.select(results, ctx);
|
AssemblyResolvedPatterns res = selector.select(results, ctx);
|
||||||
if (res == null) {
|
if (res == null) {
|
||||||
throw new AssemblySelectionError(
|
throw new AssemblySelectionError(
|
||||||
"Must select exactly one instruction. Report errors via AssemblySemanticError");
|
"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
|
* @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() {
|
protected AssemblyNumericSymbols getNumericSymbols() {
|
||||||
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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (program != null) {
|
if (program != null) {
|
||||||
final SymbolIterator it = program.getSymbolTable().getAllSymbols(false);
|
return AssemblyNumericSymbols.fromProgram(program);
|
||||||
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 labels;
|
return AssemblyNumericSymbols.fromLanguage(lang);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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.AssemblyGrammar;
|
||||||
import ghidra.app.plugin.assembler.sleigh.grammars.AssemblySentential;
|
import ghidra.app.plugin.assembler.sleigh.grammars.AssemblySentential;
|
||||||
import ghidra.app.plugin.assembler.sleigh.parse.AssemblyParser;
|
import ghidra.app.plugin.assembler.sleigh.parse.AssemblyParser;
|
||||||
import ghidra.app.plugin.assembler.sleigh.sem.AssemblyContextGraph;
|
import ghidra.app.plugin.assembler.sleigh.sem.*;
|
||||||
import ghidra.app.plugin.assembler.sleigh.sem.AssemblyDefaultContext;
|
|
||||||
import ghidra.app.plugin.assembler.sleigh.symbol.*;
|
import ghidra.app.plugin.assembler.sleigh.symbol.*;
|
||||||
import ghidra.app.plugin.assembler.sleigh.util.DbgTimer;
|
import ghidra.app.plugin.assembler.sleigh.util.DbgTimer;
|
||||||
import ghidra.app.plugin.assembler.sleigh.util.DbgTimer.DbgCtx;
|
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}
|
* 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.
|
* 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
|
* <p>
|
||||||
* have come along quite nicely. It's not quite as fast as disassembly, since after all, that's what
|
* SLEIGH-based assembly is a bit temperamental, since it essentially runs the disassembler
|
||||||
* SLEIGH was designed to do.
|
* 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
|
* <h2>A Review of Disassembly</h2>
|
||||||
* 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.
|
|
||||||
*
|
*
|
||||||
* To assemble, we first parse the textual instruction, yielding zero or more parse trees. No parse
|
* <p>
|
||||||
* trees implies an error. For each parse tree, we attempt to resolve the instruction bytes,
|
* Before diving into assembly, it may be helpful to review SLEIGH and disassembly, at least as far
|
||||||
* starting at the leaves and working upwards while tracking and solving context changes. The
|
* as I understand. SLEIGH is really a specification of three distinct things, all related by trees
|
||||||
* context changes must be considered in reverse. We <em>read</em> the context register of the
|
* of "constructors." 1) A mnemonic grammar, 2) A machine-code grammar, 3) Run-time semantics, i.e.,
|
||||||
* children (a disassembler would write). We then assume there is at most one variable in the
|
* p-code. The third is consumed primarily by the decompiler, the emulator, and other analysis, and
|
||||||
* expression, solve for it, and <em>write</em> the solution to the appropriate field (a
|
* is of little concern to the (dis)assembler. All three are tightly bound. A single constructor
|
||||||
* disassembler would read). If no solution exists, a semantic error is logged. Since it's possible
|
* specifies a production in both grammars, constraints for selecting the production, as well as the
|
||||||
* a production in the parse tree is associated with multiple constructors, different combinations
|
* generated run-time semantics. Consider an example:
|
||||||
* 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.
|
|
||||||
*
|
*
|
||||||
* Some productions are "purely recursive," e.g., {@code :^instruction} lines in the SLEIGH. These
|
* <pre>
|
||||||
* are ignored during parser construction. Let such a production be given as I => I. When resolving
|
* :ADD regD,imm8 is op=5 & regD & imm8 { regD = regD + imm8; }
|
||||||
* the parse tree to bytes, and we encounter a production with I on the left hand side, we then
|
* </pre>
|
||||||
* 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.
|
|
||||||
*
|
*
|
||||||
* After all the context changes and operands are resolved, we apply the constructor patterns and
|
* <p>
|
||||||
* proceed up the tree. Thus, each branch yields zero or more "resolved constructors," which each
|
* The colon indicates this constructor applies to the root "instruction" table. The mnemonic
|
||||||
* specify two masked blocks of data: one for the instruction, and one for the context. These are
|
* production precedes the <code>is</code> keyword. The machine-code constraints and production
|
||||||
* passed up to the parent production, which, having obtained results from all its children,
|
* follow. Finally, the semantics appear within braces.
|
||||||
* attempts to apply the corresponding constructors.
|
|
||||||
*
|
*
|
||||||
* Once we've resolved the root node, any resolved constructors returned are taken as successfully
|
* <p>
|
||||||
* assembled instruction bytes. If applicable, the corresponding context registers are compared to
|
* To support bitfield parsing, a list of token formats and fields within must be declared. The
|
||||||
* the context at the target address in the program and filtered for compatibility.
|
* 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 {
|
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 SleighLanguage lang;
|
||||||
protected AssemblyGrammar grammar;
|
protected AssemblyGrammar grammar;
|
||||||
@@ -220,6 +429,7 @@ public class SleighAssemblerBuilder implements AssemblerBuilder {
|
|||||||
/**
|
/**
|
||||||
* Convert the given operand symbol to an {@link AssemblySymbol}
|
* 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.
|
* 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
|
* @param cons the constructor to which the operand belongs
|
||||||
@@ -242,7 +452,9 @@ public class SleighAssemblerBuilder implements AssemblerBuilder {
|
|||||||
return built;
|
return built;
|
||||||
}
|
}
|
||||||
if (defsym == null) {
|
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) {
|
else if (defsym instanceof SubtableSymbol) {
|
||||||
built = new AssemblyNonTerminal(name);
|
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
|
* <p>
|
||||||
* size. However, the value exported by a constructor's pCode may have an explicit size given
|
* This handles a special case, where a constructor prints just one operand and exports that
|
||||||
* (in bytes). Thus, there is a special case, where a constructor prints just one operand and
|
* same operand, often with an explicit size, or as an address in a given space. In such cases,
|
||||||
* exports that same operand with an explicit size. In that case, the size of the operand is
|
* the listing displays that operand according to that exported size.
|
||||||
* printed according to that exported size.
|
|
||||||
*
|
*
|
||||||
* For disassembly, this information is used simply to truncate the bits before they are
|
* <p>
|
||||||
* displayed. For assembly, we must do two things: 1) Ensure that the provided value fits in the
|
* For assembly, this gives a few opportunities: 1) We can/must ensure the specified value fits,
|
||||||
* given size, and 2) Mask the goal when solving the pattern expression for the operand.
|
* 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 cons the constructor from which the production is being derived
|
||||||
* @param opsym the operand symbol corresponding to the grammatical symbol, whose size we wish
|
* @param opsym the operand symbol corresponding to the grammatical symbol, whose size we wish
|
||||||
* to determine.
|
* to determine.
|
||||||
* @return the size of the operand in bits
|
* @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();
|
ConstructTpl ctpl = cons.getTempl();
|
||||||
if (null == ctpl) {
|
if (null == ctpl) {
|
||||||
// No pcode, no size specification
|
// No pcode, no size specification
|
||||||
return 0;
|
return null;
|
||||||
}
|
}
|
||||||
HandleTpl htpl = ctpl.getResult();
|
HandleTpl htpl = ctpl.getResult();
|
||||||
if (null == htpl) {
|
if (null == htpl) {
|
||||||
// If nothing is exported, the size is unspecified
|
// If nothing is exported, the size is unspecified
|
||||||
return 0;
|
return null;
|
||||||
}
|
}
|
||||||
if (opsym.getIndex() != htpl.getOffsetOperandIndex()) {
|
if (opsym.getIndex() != htpl.getOffsetOperandIndex()) {
|
||||||
// If the export is not of the same operand, it does not specify its size
|
// 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()) {
|
if (sym.takesOperandIndex()) {
|
||||||
indices.add(index);
|
indices.add(index);
|
||||||
}
|
}
|
||||||
rhs.add(sym);
|
rhs.addSymbol(sym);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
String tstr = str.trim();
|
rhs.addSeparators(str);
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -384,7 +577,7 @@ public class SleighAssemblerBuilder implements AssemblerBuilder {
|
|||||||
// Ignore. We don't do pcode.
|
// Ignore. We don't do pcode.
|
||||||
}
|
}
|
||||||
else if (sym instanceof OperandSymbol) {
|
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) {
|
else if (sym instanceof ValueSymbol) {
|
||||||
// Ignore. These are now terminals
|
// Ignore. These are now terminals
|
||||||
|
|||||||
+32
-29
@@ -19,12 +19,12 @@ import java.util.Map;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import ghidra.app.plugin.assembler.sleigh.sem.AssemblyResolution;
|
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.BinaryExpression;
|
||||||
import ghidra.app.plugin.processors.sleigh.expression.PatternExpression;
|
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)
|
* @param <T> the type of expression solved (the operator)
|
||||||
*/
|
*/
|
||||||
@@ -37,10 +37,10 @@ public abstract class AbstractBinaryExpressionSolver<T extends BinaryExpression>
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AssemblyResolution solve(T exp, MaskedLong goal, Map<String, Long> vals,
|
public 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)
|
||||||
String description) throws NeedsBackfillException {
|
throws NeedsBackfillException {
|
||||||
MaskedLong lval = solver.getValue(exp.getLeft(), vals, res, cur);
|
MaskedLong lval = solver.getValue(exp.getLeft(), vals, cur);
|
||||||
MaskedLong rval = solver.getValue(exp.getRight(), vals, res, cur);
|
MaskedLong rval = solver.getValue(exp.getRight(), vals, cur);
|
||||||
|
|
||||||
if (lval != null && !lval.isFullyDefined()) {
|
if (lval != null && !lval.isFullyDefined()) {
|
||||||
if (!lval.isFullyUndefined()) {
|
if (!lval.isFullyUndefined()) {
|
||||||
@@ -61,23 +61,23 @@ public abstract class AbstractBinaryExpressionSolver<T extends BinaryExpression>
|
|||||||
return ConstantValueSolver.checkConstAgrees(cval, goal, description);
|
return ConstantValueSolver.checkConstAgrees(cval, goal, description);
|
||||||
}
|
}
|
||||||
else if (lval != null) {
|
else if (lval != null) {
|
||||||
return solveRightSide(exp.getRight(), lval, goal, vals, res, cur, hints,
|
return solveRightSide(exp.getRight(), lval, goal, vals, cur, hints,
|
||||||
description);
|
description);
|
||||||
}
|
}
|
||||||
else if (rval != null) {
|
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 {
|
else {
|
||||||
// Each solver may provide a strategy for solving expression where both sides are
|
// Each solver may provide a strategy for solving expression where both sides are
|
||||||
// variable, e.g., two fields being concatenated via OR.
|
// 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) {
|
catch (NeedsBackfillException e) {
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
catch (SolverException e) {
|
catch (SolverException e) {
|
||||||
return AssemblyResolution.error(e.getMessage(), description, null);
|
return AssemblyResolution.error(e.getMessage(), description);
|
||||||
}
|
}
|
||||||
catch (AssertionError e) {
|
catch (AssertionError e) {
|
||||||
dbg.println("While solving: " + exp + " (" + description + ")");
|
dbg.println("While solving: " + exp + " (" + description + ")");
|
||||||
@@ -86,30 +86,30 @@ public abstract class AbstractBinaryExpressionSolver<T extends BinaryExpression>
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected AssemblyResolution solveLeftSide(PatternExpression lexp, MaskedLong rval,
|
protected AssemblyResolution solveLeftSide(PatternExpression lexp, MaskedLong rval,
|
||||||
MaskedLong goal, Map<String, Long> vals, Map<Integer, Object> res,
|
MaskedLong goal, Map<String, Long> vals, AssemblyResolvedPatterns cur,
|
||||||
AssemblyResolvedConstructor cur, Set<SolverHint> hints, String description)
|
Set<SolverHint> hints, String description)
|
||||||
throws NeedsBackfillException, SolverException {
|
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,
|
protected AssemblyResolution solveRightSide(PatternExpression rexp, MaskedLong lval,
|
||||||
MaskedLong goal, Map<String, Long> vals, Map<Integer, Object> res,
|
MaskedLong goal, Map<String, Long> vals, AssemblyResolvedPatterns cur,
|
||||||
AssemblyResolvedConstructor cur, Set<SolverHint> hints, String description)
|
Set<SolverHint> hints, String description)
|
||||||
throws NeedsBackfillException, SolverException {
|
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,
|
protected AssemblyResolution solveTwoSided(T exp, MaskedLong goal, Map<String, Long> vals,
|
||||||
Map<Integer, Object> res, AssemblyResolvedConstructor cur, Set<SolverHint> hints,
|
AssemblyResolvedPatterns cur, Set<SolverHint> hints, String description)
|
||||||
String description) throws NeedsBackfillException, SolverException {
|
throws NeedsBackfillException, SolverException {
|
||||||
throw new NeedsBackfillException("_two_sided_");
|
throw new NeedsBackfillException("_two_sided_");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MaskedLong getValue(T exp, Map<String, Long> vals, Map<Integer, Object> res,
|
public MaskedLong getValue(T exp, Map<String, Long> vals, AssemblyResolvedPatterns cur)
|
||||||
AssemblyResolvedConstructor cur) throws NeedsBackfillException {
|
throws NeedsBackfillException {
|
||||||
MaskedLong lval = solver.getValue(exp.getLeft(), vals, res, cur);
|
MaskedLong lval = solver.getValue(exp.getLeft(), vals, cur);
|
||||||
MaskedLong rval = solver.getValue(exp.getRight(), vals, res, cur);
|
MaskedLong rval = solver.getValue(exp.getRight(), vals, cur);
|
||||||
if (lval != null && rval != null) {
|
if (lval != null && rval != null) {
|
||||||
MaskedLong cval = compute(lval, rval);
|
MaskedLong cval = compute(lval, rval);
|
||||||
return cval;
|
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
|
* 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 lval the left-hand-side value
|
||||||
* @param goal the result
|
* @param goal the result
|
||||||
* @return the right-hand-side value solution
|
* @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);
|
public abstract MaskedLong compute(MaskedLong lval, MaskedLong rval);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getInstructionLength(T exp, Map<Integer, Object> res) {
|
public int getInstructionLength(T exp) {
|
||||||
int ll = solver.getInstructionLength(exp.getLeft(), res);
|
int ll = solver.getInstructionLength(exp.getLeft());
|
||||||
int lr = solver.getInstructionLength(exp.getRight(), res);
|
int lr = solver.getInstructionLength(exp.getRight());
|
||||||
return Math.max(ll, lr);
|
return Math.max(ll, lr);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MaskedLong valueForResolution(T exp, AssemblyResolvedConstructor rc) {
|
public MaskedLong valueForResolution(T exp, Map<String, Long> vals,
|
||||||
MaskedLong lval = solver.valueForResolution(exp.getLeft(), rc);
|
AssemblyResolvedPatterns rc) {
|
||||||
MaskedLong rval = solver.valueForResolution(exp.getRight(), rc);
|
MaskedLong lval = solver.valueForResolution(exp.getLeft(), vals, rc);
|
||||||
|
MaskedLong rval = solver.valueForResolution(exp.getRight(), vals, rc);
|
||||||
return compute(lval, rval);
|
return compute(lval, rval);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+9
-9
@@ -19,7 +19,7 @@ import java.util.Map;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import ghidra.app.plugin.assembler.sleigh.sem.AssemblyResolution;
|
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.assembler.sleigh.util.DbgTimer;
|
||||||
import ghidra.app.plugin.processors.sleigh.expression.PatternExpression;
|
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 exp the expression to solve
|
||||||
* @param goal the desired value of the expression
|
* @param goal the desired value of the expression
|
||||||
* @param vals values of defined symbols
|
* @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 hints describes techniques applied by calling solvers
|
||||||
* @param description the description to give to resolved solutions
|
* @param description the description to give to resolved solutions
|
||||||
* @return the resolution
|
* @return the resolution
|
||||||
* @throws NeedsBackfillException if the expression refers to an undefined symbol
|
* @throws NeedsBackfillException if the expression refers to an undefined symbol
|
||||||
*/
|
*/
|
||||||
public abstract AssemblyResolution solve(T exp, MaskedLong goal, Map<String, Long> vals,
|
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;
|
String description) throws NeedsBackfillException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -64,33 +63,34 @@ public abstract class AbstractExpressionSolver<T extends PatternExpression> {
|
|||||||
*
|
*
|
||||||
* @param exp the expression
|
* @param exp the expression
|
||||||
* @param vals values of defined symbols
|
* @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
|
* @return the constant value, or null if it depends on a variable
|
||||||
* @throws NeedsBackfillException if the expression refers to an undefined symbol
|
* @throws NeedsBackfillException if the expression refers to an undefined symbol
|
||||||
*/
|
*/
|
||||||
public abstract MaskedLong getValue(T exp, Map<String, Long> vals, Map<Integer, Object> res,
|
public abstract MaskedLong getValue(T exp, Map<String, Long> vals,
|
||||||
AssemblyResolvedConstructor cur) throws NeedsBackfillException;
|
AssemblyResolvedPatterns cur) throws NeedsBackfillException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determines the length of the subconstructor that would be returned had the expression not
|
* Determines the length of the subconstructor that would be returned had the expression not
|
||||||
* depended on an undefined symbol.
|
* depended on an undefined symbol.
|
||||||
*
|
*
|
||||||
|
* <p>
|
||||||
* This is used by the backfilling process to ensure values are written to the correct offset
|
* This is used by the backfilling process to ensure values are written to the correct offset
|
||||||
*
|
*
|
||||||
* @param exp the expression
|
* @param exp the expression
|
||||||
* @param res the results of subconstructor resolutions (used for lengths)
|
|
||||||
* @return the length of filled in token field(s).
|
* @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
|
* Compute the value of the expression given the (possibly-intermediate) resolution
|
||||||
*
|
*
|
||||||
* @param exp the expression to evaluate
|
* @param exp the expression to evaluate
|
||||||
|
* @param vals values of defined symbols
|
||||||
* @param rc the resolution on which to evaluate it
|
* @param rc the resolution on which to evaluate it
|
||||||
* @return the result
|
* @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
|
* Register this particular solver with the general expression solver
|
||||||
|
|||||||
+17
-14
@@ -19,11 +19,11 @@ import java.util.Map;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import ghidra.app.plugin.assembler.sleigh.sem.AssemblyResolution;
|
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;
|
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)
|
* @param <T> the type of expression solved (the operator)
|
||||||
*/
|
*/
|
||||||
@@ -36,9 +36,9 @@ public abstract class AbstractUnaryExpressionSolver<T extends UnaryExpression>
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AssemblyResolution solve(T exp, MaskedLong goal, Map<String, Long> vals,
|
public 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)
|
||||||
String description) throws NeedsBackfillException {
|
throws NeedsBackfillException {
|
||||||
MaskedLong uval = solver.getValue(exp.getUnary(), vals, res, cur);
|
MaskedLong uval = solver.getValue(exp.getUnary(), vals, cur);
|
||||||
try {
|
try {
|
||||||
if (uval != null && uval.isFullyDefined()) {
|
if (uval != null && uval.isFullyDefined()) {
|
||||||
MaskedLong cval = compute(uval);
|
MaskedLong cval = compute(uval);
|
||||||
@@ -46,7 +46,7 @@ public abstract class AbstractUnaryExpressionSolver<T extends UnaryExpression>
|
|||||||
return ConstantValueSolver.checkConstAgrees(cval, goal, description);
|
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);
|
description);
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
@@ -60,9 +60,9 @@ public abstract class AbstractUnaryExpressionSolver<T extends UnaryExpression>
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MaskedLong getValue(T exp, Map<String, Long> vals, Map<Integer, Object> res,
|
public MaskedLong getValue(T exp, Map<String, Long> vals, AssemblyResolvedPatterns cur)
|
||||||
AssemblyResolvedConstructor cur) throws NeedsBackfillException {
|
throws NeedsBackfillException {
|
||||||
MaskedLong val = solver.getValue(exp.getUnary(), vals, res, cur);
|
MaskedLong val = solver.getValue(exp.getUnary(), vals, cur);
|
||||||
if (val != null) {
|
if (val != null) {
|
||||||
return compute(val);
|
return compute(val);
|
||||||
}
|
}
|
||||||
@@ -72,7 +72,9 @@ public abstract class AbstractUnaryExpressionSolver<T extends UnaryExpression>
|
|||||||
/**
|
/**
|
||||||
* Compute the input value given that the result is known
|
* 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
|
* @param goal the result
|
||||||
* @return the input value solution
|
* @return the input value solution
|
||||||
*/
|
*/
|
||||||
@@ -89,13 +91,14 @@ public abstract class AbstractUnaryExpressionSolver<T extends UnaryExpression>
|
|||||||
public abstract MaskedLong compute(MaskedLong val);
|
public abstract MaskedLong compute(MaskedLong val);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getInstructionLength(T exp, Map<Integer, Object> res) {
|
public int getInstructionLength(T exp) {
|
||||||
return solver.getInstructionLength(exp.getUnary(), res);
|
return solver.getInstructionLength(exp.getUnary());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MaskedLong valueForResolution(T exp, AssemblyResolvedConstructor rc) {
|
public MaskedLong valueForResolution(T exp, Map<String, Long> vals,
|
||||||
MaskedLong val = solver.valueForResolution(exp.getUnary(), rc);
|
AssemblyResolvedPatterns rc) {
|
||||||
|
MaskedLong val = solver.valueForResolution(exp.getUnary(), vals, rc);
|
||||||
return compute(val);
|
return compute(val);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+11
-10
@@ -19,12 +19,13 @@ import java.util.Map;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import ghidra.app.plugin.assembler.sleigh.sem.AssemblyResolution;
|
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;
|
import ghidra.app.plugin.processors.sleigh.expression.ConstantValue;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* "Solves" constant expressions
|
* "Solves" constant expressions
|
||||||
*
|
*
|
||||||
|
* <p>
|
||||||
* Essentially, this either evaluates successfully when asked for a constant value, or checks that
|
* 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.
|
* the goal is equal to the constant. Otherwise, there is no solution.
|
||||||
*/
|
*/
|
||||||
@@ -36,25 +37,26 @@ public class ConstantValueSolver extends AbstractExpressionSolver<ConstantValue>
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AssemblyResolution solve(ConstantValue cv, MaskedLong goal, Map<String, Long> vals,
|
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) {
|
String description) {
|
||||||
MaskedLong value = getValue(cv, vals, res, cur);
|
MaskedLong value = getValue(cv, vals, cur);
|
||||||
return checkConstAgrees(value, goal, description);
|
return checkConstAgrees(value, goal, description);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MaskedLong getValue(ConstantValue cv, Map<String, Long> vals, Map<Integer, Object> res,
|
public MaskedLong getValue(ConstantValue cv, Map<String, Long> vals,
|
||||||
AssemblyResolvedConstructor cur) {
|
AssemblyResolvedPatterns cur) {
|
||||||
return MaskedLong.fromLong(cv.getValue());
|
return MaskedLong.fromLong(cv.getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getInstructionLength(ConstantValue cv, Map<Integer, Object> res) {
|
public int getInstructionLength(ConstantValue cv) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MaskedLong valueForResolution(ConstantValue cv, AssemblyResolvedConstructor rc) {
|
public MaskedLong valueForResolution(ConstantValue cv, Map<String, Long> vals,
|
||||||
|
AssemblyResolvedPatterns rc) {
|
||||||
return MaskedLong.fromLong(cv.getValue());
|
return MaskedLong.fromLong(cv.getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,9 +64,8 @@ public class ConstantValueSolver extends AbstractExpressionSolver<ConstantValue>
|
|||||||
String description) {
|
String description) {
|
||||||
if (!value.agrees(goal)) {
|
if (!value.agrees(goal)) {
|
||||||
return AssemblyResolution.error(
|
return AssemblyResolution.error(
|
||||||
"Constant value " + value + " does not agree with child requirements", description,
|
"Constant value " + value + " does not agree with child requirements", description);
|
||||||
null);
|
|
||||||
}
|
}
|
||||||
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
|
* 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
|
* 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.
|
* space and format. Otherwise, there is no solution.
|
||||||
*/
|
*/
|
||||||
@@ -35,33 +36,33 @@ public class ContextFieldSolver extends AbstractExpressionSolver<ContextField> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AssemblyResolution solve(ContextField cf, MaskedLong goal, Map<String, Long> vals,
|
public AssemblyResolution solve(ContextField cf, MaskedLong goal, Map<String, Long> vals,
|
||||||
Map<Integer, Object> res, AssemblyResolvedConstructor cur, Set<SolverHint> hints,
|
AssemblyResolvedPatterns cur, Set<SolverHint> hints, String description) {
|
||||||
String description) {
|
|
||||||
assert cf.minValue() == 0; // In case someone decides to do signedness there.
|
assert cf.minValue() == 0; // In case someone decides to do signedness there.
|
||||||
if (!goal.isInRange(cf.maxValue(), cf.hasSignbit())) {
|
if (!goal.isInRange(cf.maxValue(), cf.hasSignbit())) {
|
||||||
return AssemblyResolution.error("Value " + goal + " is not valid for " + cf,
|
return AssemblyResolution.error("Value " + goal + " is not valid for " + cf,
|
||||||
description, null);
|
description);
|
||||||
}
|
}
|
||||||
AssemblyPatternBlock block = AssemblyPatternBlock.fromContextField(cf, goal);
|
AssemblyPatternBlock block = AssemblyPatternBlock.fromContextField(cf, goal);
|
||||||
return AssemblyResolution.contextOnly(block, description, null);
|
return AssemblyResolution.contextOnly(block, description);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MaskedLong getValue(ContextField cf, Map<String, Long> vals, Map<Integer, Object> res,
|
public MaskedLong getValue(ContextField cf, Map<String, Long> vals,
|
||||||
AssemblyResolvedConstructor cur) {
|
AssemblyResolvedPatterns cur) {
|
||||||
if (cur == null) {
|
if (cur == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return valueForResolution(cf, cur);
|
return valueForResolution(cf, vals, cur);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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
|
return 0; // this is a context field, not an instruction (token) field
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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;
|
int size = cf.getByteEnd() - cf.getByteStart() + 1;
|
||||||
MaskedLong res = rc.readContext(cf.getByteStart(), size);
|
MaskedLong res = rc.readContext(cf.getByteStart(), size);
|
||||||
res = res.shiftRight(cf.getShift());
|
res = res.shiftRight(cf.getShift());
|
||||||
|
|||||||
+2
-2
@@ -24,8 +24,8 @@ public enum DefaultSolverHint implements SolverHint {
|
|||||||
*/
|
*/
|
||||||
GUESSING_REPETITION,
|
GUESSING_REPETITION,
|
||||||
/**
|
/**
|
||||||
* A boolean or solver which matches a circular shift is solving the value having guessed a
|
* A boolean {@code or} solver which matches a circular shift is solving the value having
|
||||||
* shift
|
* guessed a shift
|
||||||
*/
|
*/
|
||||||
GUESSING_CIRCULAR_SHIFT_AMOUNT,
|
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;
|
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> {
|
public class DivExpressionSolver extends AbstractBinaryExpressionSolver<DivExpression> {
|
||||||
|
|
||||||
@@ -37,7 +37,8 @@ public class DivExpressionSolver extends AbstractBinaryExpressionSolver<DivExpre
|
|||||||
return MaskedLong.fromLong(1);
|
return MaskedLong.fromLong(1);
|
||||||
}
|
}
|
||||||
throw new SolverException(
|
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
|
@Override
|
||||||
|
|||||||
+20
-11
@@ -24,10 +24,13 @@ import ghidra.app.plugin.processors.sleigh.expression.EndInstructionValue;
|
|||||||
/**
|
/**
|
||||||
* "Solves" expressions of {@code inst_next}
|
* "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
|
* 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.
|
* 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> {
|
public class EndInstructionValueSolver extends AbstractExpressionSolver<EndInstructionValue> {
|
||||||
|
|
||||||
@@ -37,32 +40,38 @@ public class EndInstructionValueSolver extends AbstractExpressionSolver<EndInstr
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AssemblyResolution solve(EndInstructionValue iv, MaskedLong goal, Map<String, Long> vals,
|
public AssemblyResolution solve(EndInstructionValue iv, MaskedLong goal, Map<String, Long> vals,
|
||||||
Map<Integer, Object> res, AssemblyResolvedConstructor cur, Set<SolverHint> hints,
|
AssemblyResolvedPatterns cur, Set<SolverHint> hints, String description) {
|
||||||
String description) {
|
|
||||||
throw new AssertionError(
|
throw new AssertionError(
|
||||||
"INTERNAL: Should never be asked to solve for " + AssemblyTreeResolver.INST_NEXT);
|
"INTERNAL: Should never be asked to solve for " + AssemblyTreeResolver.INST_NEXT);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MaskedLong getValue(EndInstructionValue iv, Map<String, Long> vals,
|
public MaskedLong getValue(EndInstructionValue iv, Map<String, Long> vals,
|
||||||
Map<Integer, Object> res, AssemblyResolvedConstructor cur)
|
AssemblyResolvedPatterns cur) throws NeedsBackfillException {
|
||||||
throws NeedsBackfillException {
|
|
||||||
Long instNext = vals.get(AssemblyTreeResolver.INST_NEXT);
|
Long instNext = vals.get(AssemblyTreeResolver.INST_NEXT);
|
||||||
if (instNext == null) {
|
if (instNext == null) {
|
||||||
throw new NeedsBackfillException(AssemblyTreeResolver.INST_NEXT);
|
throw new NeedsBackfillException(AssemblyTreeResolver.INST_NEXT);
|
||||||
}
|
}
|
||||||
return MaskedLong.fromLong(vals.get(AssemblyTreeResolver.INST_NEXT));
|
return MaskedLong.fromLong(instNext);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getInstructionLength(EndInstructionValue iv, Map<Integer, Object> res) {
|
public int getInstructionLength(EndInstructionValue iv) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MaskedLong valueForResolution(EndInstructionValue exp, AssemblyResolvedConstructor rc) {
|
public MaskedLong valueForResolution(EndInstructionValue exp, Map<String, Long> vals,
|
||||||
// Would need to pass in symbol values, and perhaps consider child resolutions.
|
AssemblyResolvedPatterns rc) {
|
||||||
throw new UnsupportedOperationException(
|
Long instNext = vals.get(AssemblyTreeResolver.INST_NEXT);
|
||||||
"The solver should never ask for this value given a resolved constructor.");
|
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 java.util.Set;
|
||||||
|
|
||||||
import ghidra.app.plugin.assembler.sleigh.sem.AssemblyResolution;
|
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.app.plugin.processors.sleigh.expression.LeftShiftExpression;
|
||||||
import ghidra.util.Msg;
|
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> {
|
public class LeftShiftExpressionSolver extends AbstractBinaryExpressionSolver<LeftShiftExpression> {
|
||||||
|
|
||||||
@@ -61,13 +61,12 @@ public class LeftShiftExpressionSolver extends AbstractBinaryExpressionSolver<Le
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected AssemblyResolution solveTwoSided(LeftShiftExpression exp, MaskedLong goal,
|
protected AssemblyResolution solveTwoSided(LeftShiftExpression exp, MaskedLong goal,
|
||||||
Map<String, Long> vals, Map<Integer, Object> res, AssemblyResolvedConstructor cur,
|
Map<String, Long> vals, AssemblyResolvedPatterns cur, Set<SolverHint> hints,
|
||||||
Set<SolverHint> hints, String description)
|
String description) throws NeedsBackfillException, SolverException {
|
||||||
throws NeedsBackfillException, SolverException {
|
|
||||||
// Do not guess the same parameter recursively
|
// Do not guess the same parameter recursively
|
||||||
if (hints.contains(DefaultSolverHint.GUESSING_LEFT_SHIFT_AMOUNT)) {
|
if (hints.contains(DefaultSolverHint.GUESSING_LEFT_SHIFT_AMOUNT)) {
|
||||||
// NOTE: Nested left shifts ought to be written as a left shift by a sum
|
// 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
|
// 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
|
// 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???
|
// use of the leading zero count, at least AFAIK. Maybe to better restrict the max???
|
||||||
Set<SolverHint> hintsWithLShift =
|
Set<SolverHint> hintsWithLShift =
|
||||||
SolverHint.with(hints, DefaultSolverHint.GUESSING_LEFT_SHIFT_AMOUNT);
|
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--) {
|
for (int shift = maxShift; shift >= 0; shift--) {
|
||||||
try {
|
try {
|
||||||
MaskedLong reqr = MaskedLong.fromLong(shift);
|
MaskedLong reqr = MaskedLong.fromLong(shift);
|
||||||
MaskedLong reql = computeLeft(reqr, goal);
|
MaskedLong reql = computeLeft(reqr, goal);
|
||||||
|
|
||||||
AssemblyResolution lres =
|
AssemblyResolution lres =
|
||||||
solver.solve(exp.getLeft(), reql, vals, res, cur, hintsWithLShift, description);
|
solver.solve(exp.getLeft(), reql, vals, cur, hintsWithLShift, description);
|
||||||
if (lres.isError()) {
|
if (lres.isError()) {
|
||||||
throw new SolverException("Solving left failed");
|
throw new SolverException("Solving left failed");
|
||||||
}
|
}
|
||||||
AssemblyResolution rres =
|
AssemblyResolution rres =
|
||||||
solver.solve(exp.getRight(), reqr, vals, res, cur, hints, description);
|
solver.solve(exp.getRight(), reqr, vals, cur, hints, description);
|
||||||
if (rres.isError()) {
|
if (rres.isError()) {
|
||||||
throw new SolverException("Solving right failed");
|
throw new SolverException("Solving right failed");
|
||||||
}
|
}
|
||||||
AssemblyResolvedConstructor lsol = (AssemblyResolvedConstructor) lres;
|
AssemblyResolvedPatterns lsol = (AssemblyResolvedPatterns) lres;
|
||||||
AssemblyResolvedConstructor rsol = (AssemblyResolvedConstructor) rres;
|
AssemblyResolvedPatterns rsol = (AssemblyResolvedPatterns) rres;
|
||||||
AssemblyResolvedConstructor sol = lsol.combine(rsol);
|
AssemblyResolvedPatterns sol = lsol.combine(rsol);
|
||||||
if (sol == null) {
|
if (sol == null) {
|
||||||
throw new SolverException(
|
throw new SolverException(
|
||||||
"Left and right solutions conflict for shift=" + shift);
|
"Left and right solutions conflict for shift=" + shift);
|
||||||
@@ -105,6 +121,6 @@ public class LeftShiftExpressionSolver extends AbstractBinaryExpressionSolver<Le
|
|||||||
// try the next
|
// 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
|
* 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
|
* 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}.
|
* 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
|
* Get the mask as a long
|
||||||
*
|
*
|
||||||
|
* <p>
|
||||||
* Positions with a defined bit are {@code 1}; positions with an undefined bit are {@code 0}.
|
* Positions with a defined bit are {@code 1}; positions with an undefined bit are {@code 0}.
|
||||||
*
|
*
|
||||||
* @return the mask as a long
|
* @return the mask as a long
|
||||||
@@ -126,6 +128,7 @@ public class MaskedLong implements Comparable<MaskedLong> {
|
|||||||
/**
|
/**
|
||||||
* Apply an additional mask to this masked long
|
* 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}
|
* 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.
|
* 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
|
* 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.
|
* The leftmost defined bit is taken as the sign bit, and extended to the left.
|
||||||
*
|
*
|
||||||
* @return the sign-extended masked long
|
* @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
|
* 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.
|
* All bits to the left of the leftmost defined bit are set to 0.
|
||||||
*
|
*
|
||||||
* @return the zero-extended masked long
|
* @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
|
* 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
|
* 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
|
* 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.
|
* 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
|
* 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
|
* The shifted bits are the least significant {@code size} bits. The remaining bits are
|
||||||
* unaffected.
|
* unaffected.
|
||||||
*
|
*
|
||||||
@@ -247,6 +254,7 @@ public class MaskedLong implements Comparable<MaskedLong> {
|
|||||||
/**
|
/**
|
||||||
* Shift {@code size} bits @{code n) positions circularly in a given direction
|
* 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
|
* The shifted bits are the least significant {@code size} bits. The remaining bits are
|
||||||
* unaffected.
|
* unaffected.
|
||||||
*
|
*
|
||||||
@@ -265,6 +273,7 @@ public class MaskedLong implements Comparable<MaskedLong> {
|
|||||||
/**
|
/**
|
||||||
* Shift the bits @{code n} positions left
|
* Shift the bits @{code n} positions left
|
||||||
*
|
*
|
||||||
|
* <p>
|
||||||
* This implements both a signed and unsigned shift.
|
* This implements both a signed and unsigned shift.
|
||||||
*
|
*
|
||||||
* @param n the number of positions.
|
* @param n the number of positions.
|
||||||
@@ -282,6 +291,7 @@ public class MaskedLong implements Comparable<MaskedLong> {
|
|||||||
/**
|
/**
|
||||||
* Shift the bits {@code n} positions left
|
* Shift the bits {@code n} positions left
|
||||||
*
|
*
|
||||||
|
* <p>
|
||||||
* This implements both a signed and unsigned shift.
|
* This implements both a signed and unsigned shift.
|
||||||
*
|
*
|
||||||
* @param n the number of positions.
|
* @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
|
* 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
|
* 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
|
* normal right shift inserts zeros or sign bits. Additionally, if any ones would fall off the
|
||||||
* right, the inversion is undefined.
|
* 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
|
* 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
|
* 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
|
* normal right shift inserts zeros or sign bits. Additionally, if any ones would fall off the
|
||||||
* right, the inversion is undefined.
|
* right, the inversion is undefined.
|
||||||
@@ -337,6 +349,7 @@ public class MaskedLong implements Comparable<MaskedLong> {
|
|||||||
/**
|
/**
|
||||||
* Shift the bits arithmetically {@code n} positions right
|
* Shift the bits arithmetically {@code n} positions right
|
||||||
*
|
*
|
||||||
|
* <p>
|
||||||
* This implements a signed shift.
|
* This implements a signed shift.
|
||||||
*
|
*
|
||||||
* @param n the number of positions.
|
* @param n the number of positions.
|
||||||
@@ -352,6 +365,7 @@ public class MaskedLong implements Comparable<MaskedLong> {
|
|||||||
/**
|
/**
|
||||||
* Shift the bits arithmetically {@code n} positions right
|
* Shift the bits arithmetically {@code n} positions right
|
||||||
*
|
*
|
||||||
|
* <p>
|
||||||
* This implements a signed shift.
|
* This implements a signed shift.
|
||||||
*
|
*
|
||||||
* @param n the number of positions.
|
* @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
|
* 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
|
* 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
|
* 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.
|
* 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
|
* 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
|
* 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
|
* 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.
|
* 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
|
* Shift the bits logically {@code n} positions right
|
||||||
*
|
*
|
||||||
|
* <p>
|
||||||
* This implements an unsigned shift.
|
* This implements an unsigned shift.
|
||||||
*
|
*
|
||||||
* @param n the number of positions.
|
* @param n the number of positions.
|
||||||
@@ -435,6 +452,7 @@ public class MaskedLong implements Comparable<MaskedLong> {
|
|||||||
/**
|
/**
|
||||||
* Shift the bits logically {@code n} positions right
|
* Shift the bits logically {@code n} positions right
|
||||||
*
|
*
|
||||||
|
* <p>
|
||||||
* This implements an unsigned shift.
|
* This implements an unsigned shift.
|
||||||
*
|
*
|
||||||
* @param n the number of positions.
|
* @param n the number of positions.
|
||||||
@@ -451,6 +469,7 @@ public class MaskedLong implements Comparable<MaskedLong> {
|
|||||||
/**
|
/**
|
||||||
* Shift the bits positionally {@code n} positions right
|
* Shift the bits positionally {@code n} positions right
|
||||||
*
|
*
|
||||||
|
* <p>
|
||||||
* This fills the left with unknown bits
|
* This fills the left with unknown bits
|
||||||
*
|
*
|
||||||
* @param n
|
* @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
|
* 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
|
* 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
|
* normal left shift inserts zeros. Additionally, if any ones would fall off the left, the
|
||||||
* inversion is undefined.
|
* 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
|
* 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
|
* 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
|
* normal left shift inserts zeros. Additionally, if any ones would fall off the left, the
|
||||||
* inversion is undefined.
|
* inversion is undefined.
|
||||||
@@ -504,6 +525,7 @@ public class MaskedLong implements Comparable<MaskedLong> {
|
|||||||
/**
|
/**
|
||||||
* Reverse the least significant {@code n} bytes
|
* Reverse the least significant {@code n} bytes
|
||||||
*
|
*
|
||||||
|
* <p>
|
||||||
* This interprets the bits as an {@code n}-byte value and changes the endianness. Any bits
|
* 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.
|
* 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
|
* Compute the bitwise AND of this and another masked long
|
||||||
*
|
*
|
||||||
|
* <p>
|
||||||
* To handle unknown bits, the result is derived from the following truth table:
|
* To handle unknown bits, the result is derived from the following truth table:
|
||||||
*
|
*
|
||||||
* <pre>{@literal
|
* <pre>
|
||||||
* 0 x 1 <= A (this)
|
* 0 x 1 <= A (this)
|
||||||
* 0 0 0 0
|
* 0 0 0 0
|
||||||
* x 0 x x
|
* x 0 x x
|
||||||
* 1 0 x 1
|
* 1 0 x 1
|
||||||
* ^
|
* ^
|
||||||
* B (that)
|
* B (that)
|
||||||
* }</pre>
|
* </pre>
|
||||||
*
|
*
|
||||||
* @param that the other masked long ({@code B}).
|
* @param that the other masked long ({@code B}).
|
||||||
* @return the result.
|
* @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}
|
* Solves the expression {@code A & B = C, for B, given C and A}
|
||||||
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* To handle unknown bits, the solution is derived from the following truth table, where
|
* To handle unknown bits, the solution is derived from the following truth table, where
|
||||||
* {@code *} indicates no solution:
|
* {@code *} indicates no solution:
|
||||||
*
|
*
|
||||||
* <pre>{@literal
|
* <pre>
|
||||||
* 0 x 1 <= A (that)
|
* 0 x 1 <= A (that)
|
||||||
* 0 x x 0
|
* 0 x x 0
|
||||||
* x x x x
|
* x x x x
|
||||||
* 1 * 1 1
|
* 1 * 1 1
|
||||||
* ^
|
* ^
|
||||||
* B (this)
|
* B (this)
|
||||||
* }</pre>
|
* </pre>
|
||||||
*
|
*
|
||||||
* @param that the other masked long ({@code B}).
|
* @param that the other masked long ({@code B}).
|
||||||
* @return the result.
|
* @return the result.
|
||||||
@@ -587,16 +611,17 @@ public class MaskedLong implements Comparable<MaskedLong> {
|
|||||||
/**
|
/**
|
||||||
* Compute the bitwise OR of this and another masked long
|
* Compute the bitwise OR of this and another masked long
|
||||||
*
|
*
|
||||||
|
* <p>
|
||||||
* To handle unknown bits, the result is derived from the following truth table:
|
* To handle unknown bits, the result is derived from the following truth table:
|
||||||
*
|
*
|
||||||
* <pre>{@literal
|
* <pre>
|
||||||
* 0 x 1 <= A (this)
|
* 0 x 1 <= A (this)
|
||||||
* 0 0 x 1
|
* 0 0 x 1
|
||||||
* x x x 1
|
* x x x 1
|
||||||
* 1 1 1 1
|
* 1 1 1 1
|
||||||
* ^
|
* ^
|
||||||
* B (that)
|
* B (that)
|
||||||
* }</pre>
|
* </pre>
|
||||||
*
|
*
|
||||||
* @param that the other masked long ({@code B}).
|
* @param that the other masked long ({@code B}).
|
||||||
* @return the result.
|
* @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
|
* 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
|
* To handle unknown bits, the solution is derived from the following truth table, where
|
||||||
* {@code *} indicates no solution:
|
* {@code *} indicates no solution:
|
||||||
*
|
*
|
||||||
* <pre>{@literal
|
* <pre>
|
||||||
* 0 x 1 <= A (that)
|
* 0 x 1 <= A (that)
|
||||||
* 0 0 0 *
|
* 0 0 0 *
|
||||||
* x x x x
|
* x x x x
|
||||||
* 1 1 x x
|
* 1 1 x x
|
||||||
* ^
|
* ^
|
||||||
* B (this)
|
* B (this)
|
||||||
* }</pre>
|
* </pre>
|
||||||
*
|
*
|
||||||
* @param that the other masked long ({@code B}).
|
* @param that the other masked long ({@code B}).
|
||||||
* @return the result.
|
* @return the result.
|
||||||
@@ -658,16 +684,17 @@ public class MaskedLong implements Comparable<MaskedLong> {
|
|||||||
/**
|
/**
|
||||||
* Compute the bitwise XOR of this and another masked long
|
* Compute the bitwise XOR of this and another masked long
|
||||||
*
|
*
|
||||||
|
* <p>
|
||||||
* To handle unknown bits, the result is derived from the following truth table:
|
* To handle unknown bits, the result is derived from the following truth table:
|
||||||
*
|
*
|
||||||
* <pre>{@literal
|
* <pre>
|
||||||
* 0 x 1 <= A (this)
|
* 0 x 1 <= A (this)
|
||||||
* 0 0 x 1
|
* 0 0 x 1
|
||||||
* x x x x
|
* x x x x
|
||||||
* 1 1 x 0
|
* 1 1 x 0
|
||||||
* ^
|
* ^
|
||||||
* B (that)
|
* B (that)
|
||||||
* }</pre>
|
* </pre>
|
||||||
*
|
*
|
||||||
* @param that the other masked long ({@code B}).
|
* @param that the other masked long ({@code B}).
|
||||||
* @return the result.
|
* @return the result.
|
||||||
@@ -696,12 +723,13 @@ public class MaskedLong implements Comparable<MaskedLong> {
|
|||||||
/**
|
/**
|
||||||
* Compute the bitwise NOT
|
* Compute the bitwise NOT
|
||||||
*
|
*
|
||||||
|
* <p>
|
||||||
* To handle unknown bits, the result is derived from the following truth table:
|
* To handle unknown bits, the result is derived from the following truth table:
|
||||||
*
|
*
|
||||||
* <pre>{@literal
|
* <pre>
|
||||||
* 0 x 1 <= A (this)
|
* 0 x 1 <= A (this)
|
||||||
* 1 x 0
|
* 1 x 0
|
||||||
* }</pre>
|
* </pre>
|
||||||
*
|
*
|
||||||
* @return the result.
|
* @return the result.
|
||||||
*/
|
*/
|
||||||
@@ -769,7 +797,7 @@ public class MaskedLong implements Comparable<MaskedLong> {
|
|||||||
if (lmv == 2 || rmv == 2) {
|
if (lmv == 2 || rmv == 2) {
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
else if (lmv == 3 || rmv == 3) {
|
else if (lmv == 3 && rmv == 3) {
|
||||||
return 3;
|
return 3;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
@@ -893,6 +921,7 @@ public class MaskedLong implements Comparable<MaskedLong> {
|
|||||||
/**
|
/**
|
||||||
* Compute the arithmetic quotient as a solution to unsigned multiplication
|
* Compute the arithmetic quotient as a solution to unsigned multiplication
|
||||||
*
|
*
|
||||||
|
* <p>
|
||||||
* This is slightly different than {@link #divideUnsigned(MaskedLong)} in its treatment of
|
* This is slightly different than {@link #divideUnsigned(MaskedLong)} in its treatment of
|
||||||
* unknowns.
|
* unknowns.
|
||||||
*
|
*
|
||||||
@@ -924,6 +953,7 @@ public class MaskedLong implements Comparable<MaskedLong> {
|
|||||||
/**
|
/**
|
||||||
* Checks if this and another masked long agree
|
* Checks if this and another masked long agree
|
||||||
*
|
*
|
||||||
|
* <p>
|
||||||
* Two masked longs agree iff their corresponding defined bit positions are equal. Where either
|
* 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
|
* 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.
|
* 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
|
* 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
|
* 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
|
* 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
|
* 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
|
* 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
|
* 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
|
* positive power of 2. In other words, it defines a maximum number of bits, including the sign
|
||||||
* bit if applicable.
|
* bit if applicable.
|
||||||
*
|
*
|
||||||
|
* <p>
|
||||||
* The defined bits of this masked long are then checked to fall in the given range. The
|
* 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
|
* 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
|
* 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
|
* "Compare" two masked longs
|
||||||
*
|
*
|
||||||
|
* <p>
|
||||||
* This is not meant to reflect a numerical comparison. Rather, this is just to impose an
|
* 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.
|
* ordering for the sake of storing these in sorted collections.
|
||||||
*/
|
*/
|
||||||
@@ -1038,6 +1072,7 @@ public class MaskedLong implements Comparable<MaskedLong> {
|
|||||||
/**
|
/**
|
||||||
* Check for equality
|
* Check for equality
|
||||||
*
|
*
|
||||||
|
* <p>
|
||||||
* This will only return true if the other object is a masked long, even if this one is
|
* 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
|
* 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"
|
* 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;
|
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> {
|
public class MinusExpressionSolver extends AbstractUnaryExpressionSolver<MinusExpression> {
|
||||||
|
|
||||||
|
|||||||
+15
-16
@@ -19,12 +19,12 @@ import java.util.Map;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import ghidra.app.plugin.assembler.sleigh.sem.AssemblyResolution;
|
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.MultExpression;
|
||||||
import ghidra.app.plugin.processors.sleigh.expression.PatternExpression;
|
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> {
|
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,
|
protected AssemblyResolution tryRep(PatternExpression lexp, MaskedLong rval, MaskedLong repGoal,
|
||||||
MaskedLong goal, Map<String, Long> vals, Map<Integer, Object> res,
|
MaskedLong goal, Map<String, Long> vals, AssemblyResolvedPatterns cur,
|
||||||
AssemblyResolvedConstructor cur, Set<SolverHint> hints, String description)
|
Set<SolverHint> hints, String description) throws NeedsBackfillException {
|
||||||
throws NeedsBackfillException {
|
|
||||||
MaskedLong lval = repGoal.divideUnsigned(rval);
|
MaskedLong lval = repGoal.divideUnsigned(rval);
|
||||||
if (lval.multiply(rval).agrees(goal)) {
|
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;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected AssemblyResolution solveLeftSide(PatternExpression lexp, MaskedLong rval,
|
protected AssemblyResolution solveLeftSide(PatternExpression lexp, MaskedLong rval,
|
||||||
MaskedLong goal, Map<String, Long> vals, Map<Integer, Object> res,
|
MaskedLong goal, Map<String, Long> vals, AssemblyResolvedPatterns cur,
|
||||||
AssemblyResolvedConstructor cur, Set<SolverHint> hints, String description)
|
Set<SolverHint> hints, String description)
|
||||||
throws NeedsBackfillException, SolverException {
|
throws NeedsBackfillException, SolverException {
|
||||||
// Try the usual case first
|
// Try the usual case first
|
||||||
ResultTracker tracker = new ResultTracker();
|
ResultTracker tracker = new ResultTracker();
|
||||||
AssemblyResolution sol = tracker.trySolverFunc(() -> {
|
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) {
|
if (sol != null) {
|
||||||
return sol;
|
return sol;
|
||||||
@@ -151,8 +150,8 @@ public class MultExpressionSolver extends AbstractBinaryExpressionSolver<MultExp
|
|||||||
if (reps > 0) {
|
if (reps > 0) {
|
||||||
MaskedLong repRightGoal = MaskedLong.fromMaskAndValue(repMsk, repVal);
|
MaskedLong repRightGoal = MaskedLong.fromMaskAndValue(repMsk, repVal);
|
||||||
sol = tracker.trySolverFunc(() -> {
|
sol = tracker.trySolverFunc(() -> {
|
||||||
return tryRep(lexp, rval, repRightGoal, goal, vals, res, cur,
|
return tryRep(lexp, rval, repRightGoal, goal, vals, cur, hintsWithRepetition,
|
||||||
hintsWithRepetition, description);
|
description);
|
||||||
});
|
});
|
||||||
if (sol != null) {
|
if (sol != null) {
|
||||||
return sol;
|
return sol;
|
||||||
@@ -169,8 +168,8 @@ public class MultExpressionSolver extends AbstractBinaryExpressionSolver<MultExp
|
|||||||
repMsk = -1L >>> i;
|
repMsk = -1L >>> i;
|
||||||
MaskedLong repLeftGoal = MaskedLong.fromMaskAndValue(repMsk, repVal);
|
MaskedLong repLeftGoal = MaskedLong.fromMaskAndValue(repMsk, repVal);
|
||||||
sol = tracker.trySolverFunc(() -> {
|
sol = tracker.trySolverFunc(() -> {
|
||||||
return tryRep(lexp, rval, repLeftGoal, goal, vals, res, cur,
|
return tryRep(lexp, rval, repLeftGoal, goal, vals, cur, hintsWithRepetition,
|
||||||
hintsWithRepetition, description);
|
description);
|
||||||
});
|
});
|
||||||
if (sol != null) {
|
if (sol != null) {
|
||||||
return sol;
|
return sol;
|
||||||
@@ -182,10 +181,10 @@ public class MultExpressionSolver extends AbstractBinaryExpressionSolver<MultExp
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected AssemblyResolution solveRightSide(PatternExpression rexp, MaskedLong lval,
|
protected AssemblyResolution solveRightSide(PatternExpression rexp, MaskedLong lval,
|
||||||
MaskedLong goal, Map<String, Long> vals, Map<Integer, Object> res,
|
MaskedLong goal, Map<String, Long> vals, AssemblyResolvedPatterns cur,
|
||||||
AssemblyResolvedConstructor cur, Set<SolverHint> hints, String description)
|
Set<SolverHint> hints, String description)
|
||||||
throws NeedsBackfillException, SolverException {
|
throws NeedsBackfillException, SolverException {
|
||||||
return solveLeftSide(rexp, lval, goal, vals, res, cur, hints, description);
|
return solveLeftSide(rexp, lval, goal, vals, cur, hints, description);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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
|
* 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
|
* 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.
|
* 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>
|
* 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
|
* instruction, because the length of the current instruction is not known until resolution has
|
||||||
* finished.
|
* finished.
|
||||||
*
|
*
|
||||||
|
* <p>
|
||||||
* Backfill becomes a possibility when an expression depends on a symbol that is not (yet) defined.
|
* 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.
|
* 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
|
* Construct a backfill exception, resulting from the given missing symbol name
|
||||||
|
*
|
||||||
* @param symbol the missing symbol name
|
* @param symbol the missing symbol name
|
||||||
*/
|
*/
|
||||||
public NeedsBackfillException(String symbol) {
|
public NeedsBackfillException(String symbol) {
|
||||||
@@ -42,6 +45,7 @@ public class NeedsBackfillException extends SolverException {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the missing symbol name from the original solution attempt
|
* Retrieve the missing symbol name from the original solution attempt
|
||||||
|
*
|
||||||
* @return the missing symbol name
|
* @return the missing symbol name
|
||||||
*/
|
*/
|
||||||
public String getSymbol() {
|
public String getSymbol() {
|
||||||
|
|||||||
+1
-1
@@ -18,7 +18,7 @@ package ghidra.app.plugin.assembler.sleigh.expr;
|
|||||||
import ghidra.app.plugin.processors.sleigh.expression.NotExpression;
|
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> {
|
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
|
* Solves expressions of an operand value
|
||||||
*
|
*
|
||||||
|
* <p>
|
||||||
* These are a sort of named sub-expression, but they may also specify a shift in encoding.
|
* These are a sort of named sub-expression, but they may also specify a shift in encoding.
|
||||||
*/
|
*/
|
||||||
public class OperandValueSolver extends AbstractExpressionSolver<OperandValue> {
|
public class OperandValueSolver extends AbstractExpressionSolver<OperandValue> {
|
||||||
@@ -39,12 +40,13 @@ public class OperandValueSolver extends AbstractExpressionSolver<OperandValue> {
|
|||||||
/**
|
/**
|
||||||
* Obtains the "defining expression"
|
* Obtains the "defining expression"
|
||||||
*
|
*
|
||||||
|
* <p>
|
||||||
* This is either the symbols assigned defining expression, or the expression associated with
|
* This is either the symbols assigned defining expression, or the expression associated with
|
||||||
* its defining symbol.
|
* its defining symbol.
|
||||||
*
|
*
|
||||||
* @return the defining expression, or null if neither is available
|
* @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();
|
PatternExpression patexp = sym.getDefiningExpression();
|
||||||
if (patexp != null) {
|
if (patexp != null) {
|
||||||
return patexp;
|
return patexp;
|
||||||
@@ -59,62 +61,63 @@ public class OperandValueSolver extends AbstractExpressionSolver<OperandValue> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AssemblyResolution solve(OperandValue ov, MaskedLong goal, Map<String, Long> vals,
|
public AssemblyResolution solve(OperandValue ov, MaskedLong goal, Map<String, Long> vals,
|
||||||
Map<Integer, Object> res, AssemblyResolvedConstructor cur, Set<SolverHint> hints,
|
AssemblyResolvedPatterns cur, Set<SolverHint> hints, String description)
|
||||||
String description) throws NeedsBackfillException {
|
throws NeedsBackfillException {
|
||||||
Constructor cons = ov.getConstructor();
|
Constructor cons = ov.getConstructor();
|
||||||
OperandSymbol sym = cons.getOperand(ov.getIndex());
|
OperandSymbol sym = cons.getOperand(ov.getIndex());
|
||||||
PatternExpression patexp = getDefiningExpression(sym);
|
PatternExpression patexp = getDefiningExpression(sym);
|
||||||
if (patexp == null) {
|
if (patexp == null) {
|
||||||
if (goal.equals(MaskedLong.ZERO)) {
|
if (goal.equals(MaskedLong.ZERO)) {
|
||||||
return AssemblyResolution.nop(description, null);
|
return AssemblyResolution.nop(description, null, null);
|
||||||
}
|
}
|
||||||
return AssemblyResolution.error("Operand " + sym.getName() +
|
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()) {
|
if (result.isError()) {
|
||||||
AssemblyResolvedError err = (AssemblyResolvedError) result;
|
AssemblyResolvedError err = (AssemblyResolvedError) result;
|
||||||
return AssemblyResolution.error(err.getError(),
|
return AssemblyResolution.error(err.getError(),
|
||||||
"Solution to " + sym.getName() + " := " + goal + " = " + patexp,
|
"Solution to " + sym.getName() + " := " + goal + " = " + patexp,
|
||||||
List.of(result));
|
List.of(result), null);
|
||||||
}
|
}
|
||||||
// TODO: Shifting here seems like a hack to me.
|
// TODO: Shifting here seems like a hack to me.
|
||||||
// I assume this only comes at the top of an expression
|
// I assume this only comes at the top of an expression
|
||||||
AssemblyResolvedConstructor con = (AssemblyResolvedConstructor) result;
|
AssemblyResolvedPatterns con = (AssemblyResolvedPatterns) result;
|
||||||
int shamt = AssemblyTreeResolver.computeOffset(sym, cons, res);
|
int shamt = AssemblyTreeResolver.computeOffset(sym, cons);
|
||||||
return con.shift(shamt);
|
return con.shift(shamt);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MaskedLong getValue(OperandValue ov, Map<String, Long> vals, Map<Integer, Object> res,
|
public MaskedLong getValue(OperandValue ov, Map<String, Long> vals,
|
||||||
AssemblyResolvedConstructor cur) throws NeedsBackfillException {
|
AssemblyResolvedPatterns cur) throws NeedsBackfillException {
|
||||||
Constructor cons = ov.getConstructor();
|
Constructor cons = ov.getConstructor();
|
||||||
OperandSymbol sym = cons.getOperand(ov.getIndex());
|
OperandSymbol sym = cons.getOperand(ov.getIndex());
|
||||||
PatternExpression patexp = getDefiningExpression(sym);
|
PatternExpression patexp = getDefiningExpression(sym);
|
||||||
if (patexp == null) {
|
if (patexp == null) {
|
||||||
return MaskedLong.ZERO;
|
return MaskedLong.ZERO;
|
||||||
}
|
}
|
||||||
int shamt = AssemblyTreeResolver.computeOffset(sym, cons, res);
|
int shamt = AssemblyTreeResolver.computeOffset(sym, cons);
|
||||||
cur = cur == null ? null : cur.truncate(shamt);
|
cur = cur == null ? null : cur.truncate(shamt);
|
||||||
MaskedLong result = solver.getValue(patexp, vals, res, cur);
|
MaskedLong result = solver.getValue(patexp, vals, cur);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getInstructionLength(OperandValue ov, Map<Integer, Object> res) {
|
public int getInstructionLength(OperandValue ov) {
|
||||||
Constructor cons = ov.getConstructor();
|
Constructor cons = ov.getConstructor();
|
||||||
OperandSymbol sym = cons.getOperand(ov.getIndex());
|
OperandSymbol sym = cons.getOperand(ov.getIndex());
|
||||||
PatternExpression patexp = sym.getDefiningExpression();
|
PatternExpression patexp = sym.getDefiningExpression();
|
||||||
if (patexp == null) {
|
if (patexp == null) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
int length = solver.getInstructionLength(patexp, res);
|
int length = solver.getInstructionLength(patexp);
|
||||||
int shamt = AssemblyTreeResolver.computeOffset(sym, cons, res);
|
int shamt = AssemblyTreeResolver.computeOffset(sym, cons);
|
||||||
return length + shamt;
|
return length + shamt;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MaskedLong valueForResolution(OperandValue ov, AssemblyResolvedConstructor rc) {
|
public MaskedLong valueForResolution(OperandValue ov, Map<String, Long> vals,
|
||||||
|
AssemblyResolvedPatterns rc) {
|
||||||
Constructor cons = ov.getConstructor();
|
Constructor cons = ov.getConstructor();
|
||||||
OperandSymbol sym = cons.getOperand(ov.getIndex());
|
OperandSymbol sym = cons.getOperand(ov.getIndex());
|
||||||
PatternExpression patexp = sym.getDefiningExpression();
|
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.
|
// Since I'm using this just for context, ignore shifting for now.
|
||||||
//int shamt = AssemblyTreeResolver.computeOffset(sym, cons, rc.children);
|
//int shamt = AssemblyTreeResolver.computeOffset(sym, cons, rc.children);
|
||||||
// Children would be null here, anyway.
|
// 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
|
// NOTE: To be paranoid, I could check for the existence of TokenField in the expression
|
||||||
// And also check if a shift would be performed.
|
// 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 java.util.*;
|
||||||
|
|
||||||
|
import ghidra.app.plugin.assembler.sleigh.expr.match.ExpressionMatcher;
|
||||||
import ghidra.app.plugin.assembler.sleigh.sem.*;
|
import ghidra.app.plugin.assembler.sleigh.sem.*;
|
||||||
import ghidra.app.plugin.assembler.sleigh.util.DbgTimer.DbgCtx;
|
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.app.plugin.processors.sleigh.expression.*;
|
||||||
import ghidra.program.model.mem.MemoryAccessException;
|
|
||||||
import ghidra.util.Msg;
|
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> {
|
public class OrExpressionSolver extends AbstractBinaryExpressionSolver<OrExpression> {
|
||||||
static final PatternExpression DUMMY = new PatternExpression() {
|
protected static class Matchers implements ExpressionMatcher.Context {
|
||||||
@Override
|
protected ExpressionMatcher<ConstantValue> val = var(ConstantValue.class);
|
||||||
public long getValue(ParserWalker walker) throws MemoryAccessException {
|
protected ExpressionMatcher<ConstantValue> size = var(ConstantValue.class);
|
||||||
return 0;
|
protected ExpressionMatcher<PatternValue> fld = fldSz(size);
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
protected ExpressionMatcher<?> neqConst = or(
|
||||||
public void restoreXml(XmlPullParser parser, SleighLanguage lang) {
|
and(shr(sub(opnd(fld), val), size), cv(1)),
|
||||||
// Dummy intentionally left empty
|
and(shr(sub(val, opnd(fld)), size), cv(1)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
protected static final Matchers MATCHERS = new Matchers();
|
||||||
public String toString() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
public OrExpressionSolver() {
|
public OrExpressionSolver() {
|
||||||
super(OrExpression.class);
|
super(OrExpression.class);
|
||||||
@@ -62,8 +54,8 @@ public class OrExpressionSolver extends AbstractBinaryExpressionSolver<OrExpress
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected AssemblyResolution tryCatenationExpression(OrExpression exp, MaskedLong goal,
|
protected AssemblyResolution tryCatenationExpression(OrExpression exp, MaskedLong goal,
|
||||||
Map<String, Long> vals, Map<Integer, Object> res, AssemblyResolvedConstructor cur,
|
Map<String, Long> vals, AssemblyResolvedPatterns cur, Set<SolverHint> hints,
|
||||||
Set<SolverHint> hints, String description) throws SolverException {
|
String description) throws SolverException {
|
||||||
/*
|
/*
|
||||||
* If OR is being used to concatenate fields, then we can solve with some symbolic
|
* 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
|
* 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.
|
* component independently.
|
||||||
*/
|
*/
|
||||||
Map<Long, PatternExpression> fields = new TreeMap<>();
|
Map<Long, PatternExpression> fields = new TreeMap<>();
|
||||||
fields.put(0L, new ConstantValue(0));
|
collectComponentsOr(exp, 0, fields, vals, cur);
|
||||||
collectComponentsOr(exp, 0, fields, vals, res, cur);
|
fields.computeIfAbsent(0L, __ -> new ConstantValue(0));
|
||||||
fields.put(64L, new ConstantValue(0));
|
fields.put(64L, new ConstantValue(0));
|
||||||
long lo = 0;
|
long lo = 0;
|
||||||
PatternExpression fieldExp = null;
|
PatternExpression fieldExp = null;
|
||||||
AssemblyResolvedConstructor result = AssemblyResolution.nop(description, null);
|
AssemblyResolvedPatterns result = AssemblyResolution.nop(description);
|
||||||
try (DbgCtx dc = dbg.start("Trying solution of field catenation")) {
|
try (DbgCtx dc = dbg.start("Trying solution of field catenation")) {
|
||||||
dbg.println("Original: " + goal + ":= " + exp);
|
dbg.println("Original: " + goal + ":= " + exp);
|
||||||
for (Map.Entry<Long, PatternExpression> ent : fields.entrySet()) {
|
for (Map.Entry<Long, PatternExpression> ent : fields.entrySet()) {
|
||||||
@@ -89,12 +81,12 @@ public class OrExpressionSolver extends AbstractBinaryExpressionSolver<OrExpress
|
|||||||
dbg.println("Part(" + hi + ":" + lo + "]:= " + fieldExp);
|
dbg.println("Part(" + hi + ":" + lo + "]:= " + fieldExp);
|
||||||
MaskedLong part = goal.shiftLeft(64 - hi).shiftRightPositional(64 - hi + lo);
|
MaskedLong part = goal.shiftLeft(64 - hi).shiftRightPositional(64 - hi + lo);
|
||||||
dbg.println("Solving: " + part + ":= " + fieldExp);
|
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);
|
description + " with shift " + lo);
|
||||||
if (sol.isError()) {
|
if (sol.isError()) {
|
||||||
return sol;
|
return sol;
|
||||||
}
|
}
|
||||||
result = result.combine((AssemblyResolvedConstructor) sol);
|
result = result.combine((AssemblyResolvedPatterns) sol);
|
||||||
if (result == null) {
|
if (result == null) {
|
||||||
throw new SolverException("Solutions to individual fields produced conflict");
|
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,
|
protected AssemblyResolution tryCircularShiftExpression(OrExpression exp, MaskedLong goal,
|
||||||
Map<String, Long> vals, Map<Integer, Object> res, AssemblyResolvedConstructor cur,
|
Map<String, Long> vals, AssemblyResolvedPatterns cur, Set<SolverHint> hints,
|
||||||
Set<SolverHint> hints, String description) throws SolverException {
|
String description) throws SolverException {
|
||||||
// If OR is being used to accomplish a circular shift, then we can apply a clever solver.
|
// 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)
|
// We'll match against the patterns: (f << (C - g)) | (f >> g)
|
||||||
// (f >> (C - g)) | (f << g)
|
// (f >> (C - g)) | (f << g)
|
||||||
@@ -144,7 +136,7 @@ public class OrExpressionSolver extends AbstractBinaryExpressionSolver<OrExpress
|
|||||||
expShift = sub.getRight();
|
expShift = sub.getRight();
|
||||||
if (expShift.equals(s2)) {
|
if (expShift.equals(s2)) {
|
||||||
PatternExpression c = sub.getLeft();
|
PatternExpression c = sub.getLeft();
|
||||||
MaskedLong cc = solver.getValue(c, vals, res, cur);
|
MaskedLong cc = solver.getValue(c, vals, cur);
|
||||||
if (cc.isFullyDefined()) {
|
if (cc.isFullyDefined()) {
|
||||||
// the left side has the subtraction, so the overall shift is the opposite
|
// the left side has the subtraction, so the overall shift is the opposite
|
||||||
// of the direction of the shift on the left
|
// of the direction of the shift on the left
|
||||||
@@ -158,7 +150,7 @@ public class OrExpressionSolver extends AbstractBinaryExpressionSolver<OrExpress
|
|||||||
expShift = sub.getRight();
|
expShift = sub.getRight();
|
||||||
if (expShift.equals(s1)) {
|
if (expShift.equals(s1)) {
|
||||||
PatternExpression c = sub.getLeft();
|
PatternExpression c = sub.getLeft();
|
||||||
MaskedLong cc = solver.getValue(c, vals, res, cur);
|
MaskedLong cc = solver.getValue(c, vals, cur);
|
||||||
if (cc.isFullyDefined()) {
|
if (cc.isFullyDefined()) {
|
||||||
// the right side has the subtraction, so the overall shift is the same
|
// the right side has the subtraction, so the overall shift is the same
|
||||||
// as the direction of the shift on the left
|
// 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
|
// At this point, I know it's a circular shift
|
||||||
dbg.println("Identified circular shift: value:= " + expValu1 + ", shift:= " + expShift +
|
dbg.println("Identified circular shift: value:= " + expValu1 + ", shift:= " + expShift +
|
||||||
", size:= " + size + ", dir:= " + (dir == 1 ? "right" : "left"));
|
", 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);
|
description);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected AssemblyResolution solveLeftCircularShift(PatternExpression expValue,
|
protected AssemblyResolution solveLeftCircularShift(PatternExpression expValue,
|
||||||
PatternExpression expShift, int size, int dir, MaskedLong goal, Map<String, Long> vals,
|
PatternExpression expShift, int size, int dir, MaskedLong goal, Map<String, Long> vals,
|
||||||
Map<Integer, Object> res, AssemblyResolvedConstructor cur, Set<SolverHint> hints,
|
AssemblyResolvedPatterns cur, Set<SolverHint> hints, String description)
|
||||||
String description) throws NeedsBackfillException, SolverException {
|
throws NeedsBackfillException, SolverException {
|
||||||
MaskedLong valValue = solver.getValue(expValue, vals, res, cur);
|
MaskedLong valValue = solver.getValue(expValue, vals, cur);
|
||||||
MaskedLong valShift = solver.getValue(expShift, vals, res, cur);
|
MaskedLong valShift = solver.getValue(expShift, vals, cur);
|
||||||
|
|
||||||
if (valValue != null && !valValue.isFullyDefined()) {
|
if (valValue != null && !valValue.isFullyDefined()) {
|
||||||
if (!valValue.isFullyUndefined()) {
|
if (!valValue.isFullyUndefined()) {
|
||||||
@@ -202,12 +194,12 @@ public class OrExpressionSolver extends AbstractBinaryExpressionSolver<OrExpress
|
|||||||
throw new AssertionError("Should not have constants when solving special forms");
|
throw new AssertionError("Should not have constants when solving special forms");
|
||||||
}
|
}
|
||||||
else if (valValue != null) {
|
else if (valValue != null) {
|
||||||
return solver.solve(expShift, computeCircShiftG(valValue, size, dir, goal), vals, res,
|
return solver.solve(expShift, computeCircShiftG(valValue, size, dir, goal), vals, cur,
|
||||||
cur, hints, description);
|
hints, description);
|
||||||
}
|
}
|
||||||
else if (valShift != null) {
|
else if (valShift != null) {
|
||||||
return solver.solve(expValue, computeCircShiftF(valShift, size, dir, goal), vals, res,
|
return solver.solve(expValue, computeCircShiftF(valShift, size, dir, goal), vals, cur,
|
||||||
cur, hints, description);
|
hints, description);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Oiy. Try guessing the shift amount, starting at 0
|
// Oiy. Try guessing the shift amount, starting at 0
|
||||||
@@ -221,21 +213,21 @@ public class OrExpressionSolver extends AbstractBinaryExpressionSolver<OrExpress
|
|||||||
try {
|
try {
|
||||||
MaskedLong reqShift = MaskedLong.fromLong(shift);
|
MaskedLong reqShift = MaskedLong.fromLong(shift);
|
||||||
MaskedLong reqValue = computeCircShiftF(reqShift, size, dir, goal);
|
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);
|
hintsWithCircularShift, description);
|
||||||
if (resValue.isError()) {
|
if (resValue.isError()) {
|
||||||
AssemblyResolvedError err = (AssemblyResolvedError) resValue;
|
AssemblyResolvedError err = (AssemblyResolvedError) resValue;
|
||||||
throw new SolverException("Solving f failed: " + err.getError());
|
throw new SolverException("Solving f failed: " + err.getError());
|
||||||
}
|
}
|
||||||
AssemblyResolution resShift =
|
AssemblyResolution resShift =
|
||||||
solver.solve(expShift, reqShift, vals, res, cur, hints, description);
|
solver.solve(expShift, reqShift, vals, cur, hints, description);
|
||||||
if (resShift.isError()) {
|
if (resShift.isError()) {
|
||||||
AssemblyResolvedError err = (AssemblyResolvedError) resShift;
|
AssemblyResolvedError err = (AssemblyResolvedError) resShift;
|
||||||
throw new SolverException("Solving g failed: " + err.getError());
|
throw new SolverException("Solving g failed: " + err.getError());
|
||||||
}
|
}
|
||||||
AssemblyResolvedConstructor solValue = (AssemblyResolvedConstructor) resValue;
|
AssemblyResolvedPatterns solValue = (AssemblyResolvedPatterns) resValue;
|
||||||
AssemblyResolvedConstructor solShift = (AssemblyResolvedConstructor) resShift;
|
AssemblyResolvedPatterns solShift = (AssemblyResolvedPatterns) resShift;
|
||||||
AssemblyResolvedConstructor sol = solValue.combine(solShift);
|
AssemblyResolvedPatterns sol = solValue.combine(solShift);
|
||||||
if (sol == null) {
|
if (sol == null) {
|
||||||
throw new SolverException(
|
throw new SolverException(
|
||||||
"value and shift solutions conflict for shift=" + shift);
|
"value and shift solutions conflict for shift=" + shift);
|
||||||
@@ -276,11 +268,10 @@ public class OrExpressionSolver extends AbstractBinaryExpressionSolver<OrExpress
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected AssemblyResolution solveTwoSided(OrExpression exp, MaskedLong goal,
|
protected AssemblyResolution solveTwoSided(OrExpression exp, MaskedLong goal,
|
||||||
Map<String, Long> vals, Map<Integer, Object> res, AssemblyResolvedConstructor cur,
|
Map<String, Long> vals, AssemblyResolvedPatterns cur, Set<SolverHint> hints,
|
||||||
Set<SolverHint> hints, String description)
|
String description) throws NeedsBackfillException, SolverException {
|
||||||
throws NeedsBackfillException, SolverException {
|
|
||||||
try {
|
try {
|
||||||
return tryCatenationExpression(exp, goal, vals, res, cur, hints, description);
|
return tryCatenationExpression(exp, goal, vals, cur, hints, description);
|
||||||
}
|
}
|
||||||
catch (Exception e) {
|
catch (Exception e) {
|
||||||
dbg.println("while solving: " + goal + "=:" + exp);
|
dbg.println("while solving: " + goal + "=:" + exp);
|
||||||
@@ -288,46 +279,73 @@ public class OrExpressionSolver extends AbstractBinaryExpressionSolver<OrExpress
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return tryCircularShiftExpression(exp, goal, vals, res, cur, hints, description);
|
return tryCircularShiftExpression(exp, goal, vals, cur, hints, description);
|
||||||
}
|
}
|
||||||
catch (Exception e) {
|
catch (Exception e) {
|
||||||
dbg.println("while solving: " + goal + "=:" + exp);
|
dbg.println("while solving: " + goal + "=:" + exp);
|
||||||
dbg.println(e.getMessage());
|
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");
|
throw new SolverException("Could not solve two-sided OR");
|
||||||
}
|
}
|
||||||
|
|
||||||
void collectComponents(PatternExpression exp, long shift,
|
void collectComponents(PatternExpression exp, long shift,
|
||||||
Map<Long, PatternExpression> components, Map<String, Long> vals,
|
Map<Long, PatternExpression> components, Map<String, Long> vals,
|
||||||
Map<Integer, Object> res, AssemblyResolvedConstructor cur) throws SolverException {
|
AssemblyResolvedPatterns cur) throws SolverException {
|
||||||
if (exp instanceof OrExpression) {
|
if (exp instanceof OrExpression) {
|
||||||
collectComponentsOr((OrExpression) exp, shift, components, vals, res, cur);
|
collectComponentsOr((OrExpression) exp, shift, components, vals, cur);
|
||||||
}
|
}
|
||||||
else if (exp instanceof LeftShiftExpression) {
|
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) {
|
else if (exp instanceof RightShiftExpression) {
|
||||||
collectComponentsRight((RightShiftExpression) exp, shift, components, vals, res, cur);
|
collectComponentsRight((RightShiftExpression) exp, shift, components, vals, cur);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
assert shift < 64;
|
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,
|
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 {
|
throws SolverException {
|
||||||
collectComponents(exp.getLeft(), shift, components, vals, res, cur);
|
collectComponents(exp.getLeft(), shift, components, vals, cur);
|
||||||
collectComponents(exp.getRight(), shift, components, vals, res, cur);
|
collectComponents(exp.getRight(), shift, components, vals, cur);
|
||||||
}
|
}
|
||||||
|
|
||||||
void collectComponentsLeft(LeftShiftExpression exp, long shift,
|
void collectComponentsLeft(LeftShiftExpression exp, long shift,
|
||||||
Map<Long, PatternExpression> components, Map<String, Long> vals,
|
Map<Long, PatternExpression> components, Map<String, Long> vals,
|
||||||
Map<Integer, Object> res, AssemblyResolvedConstructor cur) throws SolverException {
|
AssemblyResolvedPatterns cur) throws SolverException {
|
||||||
MaskedLong adj;
|
MaskedLong adj;
|
||||||
try {
|
try {
|
||||||
adj = solver.getValue(exp.getRight(), vals, res, cur);
|
adj = solver.getValue(exp.getRight(), vals, cur);
|
||||||
}
|
}
|
||||||
catch (NeedsBackfillException e) {
|
catch (NeedsBackfillException e) {
|
||||||
throw new SolverException("Variable shifts break field catenation solver", 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()) {
|
if (adj == null || !adj.isFullyDefined()) {
|
||||||
throw new SolverException("Variable shifts break field catenation solver");
|
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,
|
void collectComponentsRight(RightShiftExpression exp, long shift,
|
||||||
Map<Long, PatternExpression> components, Map<String, Long> vals,
|
Map<Long, PatternExpression> components, Map<String, Long> vals,
|
||||||
Map<Integer, Object> res, AssemblyResolvedConstructor cur) throws SolverException {
|
AssemblyResolvedPatterns cur) throws SolverException {
|
||||||
MaskedLong adj;
|
MaskedLong adj;
|
||||||
try {
|
try {
|
||||||
adj = solver.getValue(exp.getRight(), vals, res, cur);
|
adj = solver.getValue(exp.getRight(), vals, cur);
|
||||||
}
|
}
|
||||||
catch (NeedsBackfillException e) {
|
catch (NeedsBackfillException e) {
|
||||||
throw new SolverException("Variable shifts break field catenation solver", 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()) {
|
if (adj == null || !adj.isFullyDefined()) {
|
||||||
throw new SolverException("Variable shifts break field catenation solver");
|
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;
|
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> {
|
public class PlusExpressionSolver extends AbstractBinaryExpressionSolver<PlusExpression> {
|
||||||
|
|
||||||
|
|||||||
+40
-42
@@ -18,24 +18,30 @@ package ghidra.app.plugin.assembler.sleigh.expr;
|
|||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
import ghidra.app.plugin.assembler.sleigh.sem.AssemblyResolution;
|
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.assembler.sleigh.util.DbgTimer;
|
||||||
import ghidra.app.plugin.processors.sleigh.expression.PatternExpression;
|
import ghidra.app.plugin.processors.sleigh.expression.PatternExpression;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This singleton class seeks solutions to {@link PatternExpression}s
|
* This singleton class seeks solutions to {@link PatternExpression}s
|
||||||
*
|
*
|
||||||
* It is called naive, because it does not perform algebraic transformations. Rather, it attempts to
|
* <p>
|
||||||
* fold constants, assuming there is a single variable in the expression, modifying the goal as it
|
* 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
|
* 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
|
* 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 {
|
public class RecursiveDescentSolver {
|
||||||
protected static final DbgTimer dbg = DbgTimer.INACTIVE;
|
protected static final DbgTimer DBG = DbgTimer.INACTIVE;
|
||||||
private static final RecursiveDescentSolver solver = new RecursiveDescentSolver();
|
private static final RecursiveDescentSolver INSTANCE = new RecursiveDescentSolver();
|
||||||
|
|
||||||
// A mapping from each subclass of PatternExpression to the appropriate solver
|
// A mapping from each subclass of PatternExpression to the appropriate solver
|
||||||
protected Map<Class<?>, AbstractExpressionSolver<?>> registry = new HashMap<>();
|
protected Map<Class<?>, AbstractExpressionSolver<?>> registry = new HashMap<>();
|
||||||
@@ -67,7 +73,7 @@ public class RecursiveDescentSolver {
|
|||||||
* @return the singleton instance
|
* @return the singleton instance
|
||||||
*/
|
*/
|
||||||
public static RecursiveDescentSolver getSolver() {
|
public static RecursiveDescentSolver getSolver() {
|
||||||
return solver;
|
return INSTANCE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -103,59 +109,52 @@ public class RecursiveDescentSolver {
|
|||||||
* @param exp the expression to solve
|
* @param exp the expression to solve
|
||||||
* @param goal the desired output (modulo a mask) of the expression
|
* @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 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 hints describes techniques applied by calling solvers
|
||||||
* @param description a description to attached to the encoded solution
|
* @param description a description to attached to the encoded solution
|
||||||
* @return the encoded solution
|
* @return the encoded solution
|
||||||
* @throws NeedsBackfillException a solution may exist, but a required symbol is missing
|
* @throws NeedsBackfillException a solution may exist, but a required symbol is missing
|
||||||
*/
|
*/
|
||||||
protected AssemblyResolution solve(PatternExpression exp, MaskedLong goal,
|
protected AssemblyResolution solve(PatternExpression exp, MaskedLong goal,
|
||||||
Map<String, Long> vals, Map<Integer, Object> res, AssemblyResolvedConstructor cur,
|
Map<String, Long> vals, AssemblyResolvedPatterns cur, Set<SolverHint> hints,
|
||||||
Set<SolverHint> hints, String description) throws NeedsBackfillException {
|
String description) throws NeedsBackfillException {
|
||||||
try {
|
try {
|
||||||
return getRegistered(exp.getClass()).solve(exp, goal, vals, res, cur, hints,
|
return getRegistered(exp.getClass()).solve(exp, goal, vals, cur, hints, description);
|
||||||
description);
|
|
||||||
}
|
}
|
||||||
catch (UnsupportedOperationException e) {
|
catch (UnsupportedOperationException e) {
|
||||||
dbg.println("Error solving " + exp + " = " + goal);
|
DBG.println("Error solving " + exp + " = " + goal);
|
||||||
throw e;
|
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.
|
* 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
|
* Generally speaking, the expression may only contain a single field, and the encoded result
|
||||||
* result represents that single variable. It must be absorbed into the overall instruction
|
* specifies the bits of the solved field. It must be absorbed into the overall assembly
|
||||||
* and/or context encoding.
|
* pattern.
|
||||||
*
|
*
|
||||||
* More realistically, however, these expressions may depend on quite a bit of extra
|
* <p>
|
||||||
* information. For example, PC-relative encodings (i.e., those involving {@code inst_start} or
|
* 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
|
* {@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
|
* 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
|
* 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
|
* {@link NeedsBackfillException}. The symbols, when known, are provided to the solver via the
|
||||||
* {@code vals} parameter.
|
* {@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 exp the expression to solve
|
||||||
* @param goal the desired output (modulo a mask) of the expression
|
* @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 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
|
* @param description a description to attached to the encoded solution
|
||||||
* @return the encoded solution
|
* @return the encoded solution
|
||||||
* @throws NeedsBackfillException a solution may exist, but a required symbol is missing
|
* @throws NeedsBackfillException a solution may exist, but a required symbol is missing
|
||||||
*/
|
*/
|
||||||
public AssemblyResolution solve(PatternExpression exp, MaskedLong goal, Map<String, Long> vals,
|
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 {
|
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 exp the (sub-)expression to fold
|
||||||
* @param vals any defined symbols (usually {@code inst_start}, and {@code inst_next})
|
* @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
|
* @return the masked solution
|
||||||
* @throws NeedsBackfillException it may be folded, but a required symbol is missing
|
* @throws NeedsBackfillException it may be folded, but a required symbol is missing
|
||||||
*/
|
*/
|
||||||
protected <T extends PatternExpression> MaskedLong getValue(T exp, Map<String, Long> vals,
|
protected <T extends PatternExpression> MaskedLong getValue(T exp, Map<String, Long> vals,
|
||||||
Map<Integer, Object> res, AssemblyResolvedConstructor cur)
|
AssemblyResolvedPatterns cur) throws NeedsBackfillException {
|
||||||
throws NeedsBackfillException {
|
MaskedLong value = getRegistered(exp.getClass()).getValue(exp, vals, cur);
|
||||||
MaskedLong value = getRegistered(exp.getClass()).getValue(exp, vals, res, cur);
|
DBG.println("Expression: " + value + " =: " + exp);
|
||||||
dbg.println("Expression: " + value + " =: " + exp);
|
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine the length of the instruction part of the encoded solution to the given expression
|
* 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
|
* 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
|
* 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.
|
* 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.
|
* The length can be determined by finding token fields in the expression.
|
||||||
*
|
*
|
||||||
* @param exp the expression, presumably containing a token field
|
* @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
|
* @return the anticipated length, in bytes, of the instruction encoding
|
||||||
*/
|
*/
|
||||||
public int getInstructionLength(PatternExpression exp, Map<Integer, Object> res) {
|
public int getInstructionLength(PatternExpression exp) {
|
||||||
return getRegistered(exp.getClass()).getInstructionLength(exp, res);
|
return getRegistered(exp.getClass()).getInstructionLength(exp);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compute the value of an expression given a (possibly-intermediate) resolution
|
* Compute the value of an expression given a (possibly-intermediate) resolution
|
||||||
*
|
*
|
||||||
* @param exp the expression to evaluate
|
* @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
|
* @return the result
|
||||||
*/
|
*/
|
||||||
public MaskedLong valueForResolution(PatternExpression exp, AssemblyResolvedConstructor rc) {
|
public MaskedLong valueForResolution(PatternExpression exp, Map<String, Long> vals,
|
||||||
return getRegistered(exp.getClass()).valueForResolution(exp, rc);
|
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 java.util.Set;
|
||||||
|
|
||||||
import ghidra.app.plugin.assembler.sleigh.sem.AssemblyResolution;
|
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.app.plugin.processors.sleigh.expression.RightShiftExpression;
|
||||||
import ghidra.util.Msg;
|
import ghidra.util.Msg;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@literal Solves expressions of the form A >> B}
|
* Solves expressions of the form {@code A >> B}
|
||||||
*/
|
*/
|
||||||
public class RightShiftExpressionSolver
|
public class RightShiftExpressionSolver
|
||||||
extends AbstractBinaryExpressionSolver<RightShiftExpression> {
|
extends AbstractBinaryExpressionSolver<RightShiftExpression> {
|
||||||
@@ -62,15 +62,14 @@ public class RightShiftExpressionSolver
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected AssemblyResolution solveTwoSided(RightShiftExpression exp, MaskedLong goal,
|
protected AssemblyResolution solveTwoSided(RightShiftExpression exp, MaskedLong goal,
|
||||||
Map<String, Long> vals, Map<Integer, Object> res, AssemblyResolvedConstructor cur,
|
Map<String, Long> vals, AssemblyResolvedPatterns cur, Set<SolverHint> hints,
|
||||||
Set<SolverHint> hints, String description)
|
String description) throws NeedsBackfillException, SolverException {
|
||||||
throws NeedsBackfillException, SolverException {
|
|
||||||
// Do the similar thing as in {@link LeftShiftExpressionSolver}
|
// Do the similar thing as in {@link LeftShiftExpressionSolver}
|
||||||
|
|
||||||
// Do not guess the same parameter recursively
|
// Do not guess the same parameter recursively
|
||||||
if (hints.contains(DefaultSolverHint.GUESSING_RIGHT_SHIFT_AMOUNT)) {
|
if (hints.contains(DefaultSolverHint.GUESSING_RIGHT_SHIFT_AMOUNT)) {
|
||||||
// NOTE: Nested right shifts ought to be written as a right shift by a sum
|
// 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);
|
int maxShift = Long.numberOfLeadingZeros(goal.val);
|
||||||
@@ -82,18 +81,18 @@ public class RightShiftExpressionSolver
|
|||||||
MaskedLong reql = computeLeft(reqr, goal);
|
MaskedLong reql = computeLeft(reqr, goal);
|
||||||
|
|
||||||
AssemblyResolution lres =
|
AssemblyResolution lres =
|
||||||
solver.solve(exp.getLeft(), reql, vals, res, cur, hintsWithRShift, description);
|
solver.solve(exp.getLeft(), reql, vals, cur, hintsWithRShift, description);
|
||||||
if (lres.isError()) {
|
if (lres.isError()) {
|
||||||
throw new SolverException("Solving left failed");
|
throw new SolverException("Solving left failed");
|
||||||
}
|
}
|
||||||
AssemblyResolution rres =
|
AssemblyResolution rres =
|
||||||
solver.solve(exp.getRight(), reqr, vals, res, cur, hints, description);
|
solver.solve(exp.getRight(), reqr, vals, cur, hints, description);
|
||||||
if (rres.isError()) {
|
if (rres.isError()) {
|
||||||
throw new SolverException("Solving right failed");
|
throw new SolverException("Solving right failed");
|
||||||
}
|
}
|
||||||
AssemblyResolvedConstructor lsol = (AssemblyResolvedConstructor) lres;
|
AssemblyResolvedPatterns lsol = (AssemblyResolvedPatterns) lres;
|
||||||
AssemblyResolvedConstructor rsol = (AssemblyResolvedConstructor) rres;
|
AssemblyResolvedPatterns rsol = (AssemblyResolvedPatterns) rres;
|
||||||
AssemblyResolvedConstructor sol = lsol.combine(rsol);
|
AssemblyResolvedPatterns sol = lsol.combine(rsol);
|
||||||
if (sol == null) {
|
if (sol == null) {
|
||||||
throw new SolverException(
|
throw new SolverException(
|
||||||
"Left and right solutions conflict for shift=" + shift);
|
"Left and right solutions conflict for shift=" + shift);
|
||||||
@@ -105,6 +104,6 @@ public class RightShiftExpressionSolver
|
|||||||
// try the next
|
// 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
|
* 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
|
* 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
|
* multiplication) both attempt to synthesize new goals for repetition. This sort of expression is
|
||||||
* common when decoding immediates in the AArch64 specification.
|
* common when decoding immediates in the AArch64 specification.
|
||||||
*
|
*
|
||||||
|
* <p>
|
||||||
* Using an interface implemented by an enumeration (instead of just using the enumeration directly)
|
* Using an interface implemented by an enumeration (instead of just using the enumeration directly)
|
||||||
* eases expansion by extension without modifying the core code.
|
* 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}
|
* "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
|
* Works like the constant solver, but takes the value of {@code inst_start}, which is given by the
|
||||||
* assembly address.
|
* assembly address.
|
||||||
*/
|
*/
|
||||||
@@ -35,28 +36,26 @@ public class StartInstructionValueSolver extends AbstractExpressionSolver<StartI
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AssemblyResolution solve(StartInstructionValue iv, MaskedLong goal,
|
public AssemblyResolution solve(StartInstructionValue iv, MaskedLong goal,
|
||||||
Map<String, Long> vals, Map<Integer, Object> res, AssemblyResolvedConstructor cur,
|
Map<String, Long> vals, AssemblyResolvedPatterns cur, Set<SolverHint> hints,
|
||||||
Set<SolverHint> hints, String description) {
|
String description) {
|
||||||
throw new AssertionError(
|
throw new AssertionError(
|
||||||
"INTERNAL: Should never be asked to solve for " + AssemblyTreeResolver.INST_START);
|
"INTERNAL: Should never be asked to solve for " + AssemblyTreeResolver.INST_START);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MaskedLong getValue(StartInstructionValue iv, Map<String, Long> vals,
|
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));
|
return MaskedLong.fromLong(vals.get(AssemblyTreeResolver.INST_START));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getInstructionLength(StartInstructionValue exp, Map<Integer, Object> res) {
|
public int getInstructionLength(StartInstructionValue exp) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MaskedLong valueForResolution(StartInstructionValue exp,
|
public MaskedLong valueForResolution(StartInstructionValue exp, Map<String, Long> vals,
|
||||||
AssemblyResolvedConstructor rc) {
|
AssemblyResolvedPatterns rc) {
|
||||||
// Would need to pass in symbol values.
|
return MaskedLong.fromLong(vals.get(AssemblyTreeResolver.INST_START));
|
||||||
throw new UnsupportedOperationException(
|
|
||||||
"The solver should never ask for this value given a resolved constructor.");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -18,7 +18,7 @@ package ghidra.app.plugin.assembler.sleigh.expr;
|
|||||||
import ghidra.app.plugin.processors.sleigh.expression.SubExpression;
|
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> {
|
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
|
* 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
|
* 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.
|
* space and format. Otherwise, there is no solution.
|
||||||
*/
|
*/
|
||||||
@@ -35,33 +36,33 @@ public class TokenFieldSolver extends AbstractExpressionSolver<TokenField> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AssemblyResolution solve(TokenField tf, MaskedLong goal, Map<String, Long> vals,
|
public AssemblyResolution solve(TokenField tf, MaskedLong goal, Map<String, Long> vals,
|
||||||
Map<Integer, Object> res, AssemblyResolvedConstructor cur, Set<SolverHint> hints,
|
AssemblyResolvedPatterns cur, Set<SolverHint> hints, String description) {
|
||||||
String description) {
|
|
||||||
assert tf.minValue() == 0; // In case someone decides to do signedness there.
|
assert tf.minValue() == 0; // In case someone decides to do signedness there.
|
||||||
if (!goal.isInRange(tf.maxValue(), tf.hasSignbit())) {
|
if (!goal.isInRange(tf.maxValue(), tf.hasSignbit())) {
|
||||||
return AssemblyResolution.error("Value " + goal + " is not valid for " + tf,
|
return AssemblyResolution.error("Value " + goal + " is not valid for " + tf,
|
||||||
description, null);
|
description);
|
||||||
}
|
}
|
||||||
AssemblyPatternBlock block = AssemblyPatternBlock.fromTokenField(tf, goal);
|
AssemblyPatternBlock block = AssemblyPatternBlock.fromTokenField(tf, goal);
|
||||||
return AssemblyResolution.instrOnly(block, description, null);
|
return AssemblyResolution.instrOnly(block, description);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MaskedLong getValue(TokenField tf, Map<String, Long> vals, Map<Integer, Object> res,
|
public MaskedLong getValue(TokenField tf, Map<String, Long> vals,
|
||||||
AssemblyResolvedConstructor cur) {
|
AssemblyResolvedPatterns cur) {
|
||||||
if (cur == null) {
|
if (cur == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return valueForResolution(tf, cur);
|
return valueForResolution(tf, vals, cur);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getInstructionLength(TokenField tf, Map<Integer, Object> res) {
|
public int getInstructionLength(TokenField tf) {
|
||||||
return tf.getByteEnd() + 1;
|
return tf.getByteEnd() + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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;
|
int size = tf.getByteEnd() - tf.getByteStart() + 1;
|
||||||
MaskedLong res = rc.readInstruction(tf.getByteStart(), size);
|
MaskedLong res = rc.readInstruction(tf.getByteStart(), size);
|
||||||
if (!tf.isBigEndian()) {
|
if (!tf.isBigEndian()) {
|
||||||
|
|||||||
+1
-1
@@ -18,7 +18,7 @@ package ghidra.app.plugin.assembler.sleigh.expr;
|
|||||||
import ghidra.app.plugin.processors.sleigh.expression.XorExpression;
|
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> {
|
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
|
* 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.
|
* <p>
|
||||||
* The left-hand side of the a production must be a single non-terminal, but the right-hand side
|
* As in classic computer science, a CFG consists of productions of non-terminals and terminals. The
|
||||||
* may be any string of symbols. To avoid overloading the term "String," here we call it a
|
* left-hand side of the a production must be a single non-terminal, but the right-hand side may be
|
||||||
* "Sentential."
|
* 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})
|
* To define a grammar, simply construct an appropriate subclass (probably {@link AssemblyGrammar})
|
||||||
* and call {@link #addProduction(AbstractAssemblyProduction)} or
|
* and call {@link #addProduction(AbstractAssemblyProduction)} or
|
||||||
* {@link #addProduction(AssemblyNonTerminal, AssemblySentential)}. The grammar object will collect
|
* {@link #addProduction(AssemblyNonTerminal, AssemblySentential)}.
|
||||||
* the non-terminals and terminals.
|
|
||||||
*
|
*
|
||||||
|
* <p>
|
||||||
* By default, the start symbol is taken from the left-hand side of the first production added to
|
* By default, the start symbol is taken from the left-hand side of the first production added to
|
||||||
* the grammar.
|
* the grammar.
|
||||||
*
|
*
|
||||||
@@ -71,6 +72,7 @@ public abstract class AbstractAssemblyGrammar<NT extends AssemblyNonTerminal, P
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a production to the grammar
|
* Add a production to the grammar
|
||||||
|
*
|
||||||
* @param lhs the left-hand side
|
* @param lhs the left-hand side
|
||||||
* @param rhs the right-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
|
* Add a production to the grammar
|
||||||
|
*
|
||||||
* @param prod the production
|
* @param prod the production
|
||||||
*/
|
*/
|
||||||
public void addProduction(P prod) {
|
public void addProduction(P prod) {
|
||||||
@@ -96,7 +99,7 @@ public abstract class AbstractAssemblyGrammar<NT extends AssemblyNonTerminal, P
|
|||||||
String lhsName = lhs.getName();
|
String lhsName = lhs.getName();
|
||||||
symbols.put(lhsName, lhs);
|
symbols.put(lhsName, lhs);
|
||||||
nonterminals.put(lhsName, lhs);
|
nonterminals.put(lhsName, lhs);
|
||||||
for (AssemblySymbol sym : prod) {
|
for (AssemblySymbol sym : prod.getRHS()) {
|
||||||
if (sym instanceof AssemblyNonTerminal) {
|
if (sym instanceof AssemblyNonTerminal) {
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
NT nt = (NT) sym;
|
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
|
* Check if the given production is purely recursive, i.e., of the form I => I
|
||||||
|
*
|
||||||
* @param prod the production to check
|
* @param prod the production to check
|
||||||
* @return true iff the production is purely recursive
|
* @return true iff the production is purely recursive
|
||||||
*/
|
*/
|
||||||
protected boolean isPureRecursive(P prod) {
|
protected boolean isPureRecursive(P prod) {
|
||||||
if (prod.size() != 1) {
|
if (prod.getRHS().size() != 1) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!prod.getLHS().equals(prod.getRHS().get(0))) {
|
if (!prod.getLHS().equals(prod.getRHS().getSymbol(0))) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@@ -130,6 +134,7 @@ public abstract class AbstractAssemblyGrammar<NT extends AssemblyNonTerminal, P
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Change the start symbol for the grammar
|
* Change the start symbol for the grammar
|
||||||
|
*
|
||||||
* @param nt the new start symbol
|
* @param nt the new start symbol
|
||||||
*/
|
*/
|
||||||
public void setStart(AssemblyNonTerminal nt) {
|
public void setStart(AssemblyNonTerminal nt) {
|
||||||
@@ -138,6 +143,7 @@ public abstract class AbstractAssemblyGrammar<NT extends AssemblyNonTerminal, P
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Change the start symbol for the grammar
|
* Change the start symbol for the grammar
|
||||||
|
*
|
||||||
* @param startName the name of the new start symbol
|
* @param startName the name of the new start symbol
|
||||||
*/
|
*/
|
||||||
public void setStartName(String startName) {
|
public void setStartName(String startName) {
|
||||||
@@ -146,6 +152,7 @@ public abstract class AbstractAssemblyGrammar<NT extends AssemblyNonTerminal, P
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the start symbol for the grammar
|
* Get the start symbol for the grammar
|
||||||
|
*
|
||||||
* @return the start symbol
|
* @return the start symbol
|
||||||
*/
|
*/
|
||||||
public NT getStart() {
|
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
|
* Get the name of the start symbol for the grammar
|
||||||
|
*
|
||||||
* @return the name of the start symbol
|
* @return the name of the start symbol
|
||||||
*/
|
*/
|
||||||
public String getStartName() {
|
public String getStartName() {
|
||||||
@@ -162,6 +170,7 @@ public abstract class AbstractAssemblyGrammar<NT extends AssemblyNonTerminal, P
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the named non-terminal
|
* Get the named non-terminal
|
||||||
|
*
|
||||||
* @param name the name of the desired non-terminal
|
* @param name the name of the desired non-terminal
|
||||||
* @return the non-terminal, or null if it is not in this grammar
|
* @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
|
* Get the named terminal
|
||||||
|
*
|
||||||
* @param name the name of the desired terminal
|
* @param name the name of the desired terminal
|
||||||
* @return the terminal, or null if it is not in this grammar
|
* @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
|
* Add all the productions of a given grammar to this one
|
||||||
|
*
|
||||||
* @param that the grammar whose productions to add
|
* @param that the grammar whose productions to add
|
||||||
*/
|
*/
|
||||||
public void combine(AbstractAssemblyGrammar<NT, P> that) {
|
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
|
* Print the productions of this grammar to the given stream
|
||||||
|
*
|
||||||
* @param out the stream
|
* @param out the stream
|
||||||
*/
|
*/
|
||||||
public void print(PrintStream out) {
|
public void print(PrintStream out) {
|
||||||
@@ -201,17 +213,19 @@ public abstract class AbstractAssemblyGrammar<NT extends AssemblyNonTerminal, P
|
|||||||
/**
|
/**
|
||||||
* Check that the grammar is consistent
|
* Check that the grammar is consistent
|
||||||
*
|
*
|
||||||
* The grammar is consistent if every non-terminal appearing in the grammar, also appears as
|
* <p>
|
||||||
* the left-hand side of some production. If not, such non-terminals are said to be undefined.
|
* 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
|
* @throws AssemblyGrammarException the grammar is inconsistent, i.e., contains undefined
|
||||||
* non-terminals.
|
* non-terminals.
|
||||||
*/
|
*/
|
||||||
public void verify() throws AssemblyGrammarException {
|
public void verify() throws AssemblyGrammarException {
|
||||||
if (!productions.containsKey(startName)) {
|
if (!productions.containsKey(startName)) {
|
||||||
throw new AssemblyGrammarException("Start symbol has no defining production");
|
throw new AssemblyGrammarException("Start symbol has no defining production");
|
||||||
}
|
}
|
||||||
for (P prod : productions.values()) {
|
for (P prod : productions.values()) {
|
||||||
for (AssemblySymbol sym : prod) {
|
for (AssemblySymbol sym : prod.getRHS()) {
|
||||||
if (sym instanceof AssemblyNonTerminal) {
|
if (sym instanceof AssemblyNonTerminal) {
|
||||||
AssemblyNonTerminal nt = (AssemblyNonTerminal) sym;
|
AssemblyNonTerminal nt = (AssemblyNonTerminal) sym;
|
||||||
if (!(productions.containsKey(nt.getName()))) {
|
if (!(productions.containsKey(nt.getName()))) {
|
||||||
@@ -233,6 +247,7 @@ public abstract class AbstractAssemblyGrammar<NT extends AssemblyNonTerminal, P
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the non-terminals
|
* Get the non-terminals
|
||||||
|
*
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public Collection<NT> nonTerminals() {
|
public Collection<NT> nonTerminals() {
|
||||||
@@ -241,6 +256,7 @@ public abstract class AbstractAssemblyGrammar<NT extends AssemblyNonTerminal, P
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the terminals
|
* Get the terminals
|
||||||
|
*
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public Collection<AssemblyTerminal> terminals() {
|
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
|
* Get all productions where the left-hand side non-terminal has the given name
|
||||||
|
*
|
||||||
* @param name the name of the non-terminal
|
* @param name the name of the non-terminal
|
||||||
* @return all productions "defining" the named 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
|
* Get all productions where the left-hand side is the given non-terminal
|
||||||
|
*
|
||||||
* @param nt the non-terminal whose defining productions to find
|
* @param nt the non-terminal whose defining productions to find
|
||||||
* @return all productions "defining" the given non-terminal
|
* @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
|
* Check if the grammar contains any symbol with the given name
|
||||||
|
*
|
||||||
* @param name the name to find
|
* @param name the name to find
|
||||||
* @return true iff a terminal or non-terminal has the given name
|
* @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;
|
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.AssemblyNonTerminal;
|
||||||
import ghidra.app.plugin.assembler.sleigh.symbol.AssemblySymbol;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines a production in a context-free grammar, usually for parsing mnemonic assembly
|
* 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
|
* @param <NT> the type of non-terminals
|
||||||
*/
|
*/
|
||||||
public abstract class AbstractAssemblyProduction<NT extends AssemblyNonTerminal>
|
public abstract class AbstractAssemblyProduction<NT extends AssemblyNonTerminal>
|
||||||
extends AbstractListDecorator<AssemblySymbol>
|
|
||||||
implements Comparable<AbstractAssemblyProduction<NT>> {
|
implements Comparable<AbstractAssemblyProduction<NT>> {
|
||||||
private final NT lhs;
|
private final NT lhs;
|
||||||
private final AssemblySentential<NT> rhs;
|
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
|
* Construct a production with the given LHS and RHS
|
||||||
|
*
|
||||||
* @param lhs the left-hand side
|
* @param lhs the left-hand side
|
||||||
* @param rhs the right-hand side
|
* @param rhs the right-hand side
|
||||||
*/
|
*/
|
||||||
@@ -47,16 +42,13 @@ public abstract class AbstractAssemblyProduction<NT extends AssemblyNonTerminal>
|
|||||||
this.rhs = rhs;
|
this.rhs = rhs;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected List<AssemblySymbol> decorated() {
|
|
||||||
return rhs;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the index of the production
|
* 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.
|
* within a grammar.
|
||||||
|
*
|
||||||
* @return the index
|
* @return the index
|
||||||
*/
|
*/
|
||||||
public int getIndex() {
|
public int getIndex() {
|
||||||
@@ -65,6 +57,7 @@ public abstract class AbstractAssemblyProduction<NT extends AssemblyNonTerminal>
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the left-hand side
|
* Get the left-hand side
|
||||||
|
*
|
||||||
* @return the LHS
|
* @return the LHS
|
||||||
*/
|
*/
|
||||||
public NT getLHS() {
|
public NT getLHS() {
|
||||||
@@ -73,6 +66,7 @@ public abstract class AbstractAssemblyProduction<NT extends AssemblyNonTerminal>
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the right-hand side
|
* Get the right-hand side
|
||||||
|
*
|
||||||
* @return the RHS
|
* @return the RHS
|
||||||
*/
|
*/
|
||||||
public AssemblySentential<NT> getRHS() {
|
public AssemblySentential<NT> getRHS() {
|
||||||
@@ -123,15 +117,12 @@ public abstract class AbstractAssemblyProduction<NT extends AssemblyNonTerminal>
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public AssemblySentential<NT> subList(int fromIndex, int toIndex) {
|
|
||||||
return rhs.subList(fromIndex, toIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the "name" of this production
|
* 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.
|
* This is mostly just notional and for debugging. The name is taken as the name of the LHS.
|
||||||
|
*
|
||||||
* @return the name of the LHS
|
* @return the name of the LHS
|
||||||
*/
|
*/
|
||||||
public String getName() {
|
public String getName() {
|
||||||
|
|||||||
+4
-3
@@ -20,9 +20,10 @@ import ghidra.app.plugin.assembler.sleigh.symbol.AssemblyExtendedNonTerminal;
|
|||||||
/**
|
/**
|
||||||
* Defines an "extended" grammar
|
* Defines an "extended" grammar
|
||||||
*
|
*
|
||||||
* "Extended grammar" as in a grammar extended with state numbers from an LR0 parser.
|
* <p>
|
||||||
* See <a href="http://web.cs.dal.ca/~sjackson/lalr1.html">LALR(1) Parsing</a> from Stephen Jackson
|
* "Extended grammar" as in a grammar extended with state numbers from an LR0 parser. See
|
||||||
* of Dalhousie University, Halifax, Nova Scotia, Canada.
|
* <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
|
public class AssemblyExtendedGrammar
|
||||||
extends AbstractAssemblyGrammar<AssemblyExtendedNonTerminal, AssemblyExtendedProduction> {
|
extends AbstractAssemblyGrammar<AssemblyExtendedNonTerminal, AssemblyExtendedProduction> {
|
||||||
|
|||||||
+3
@@ -29,6 +29,7 @@ public class AssemblyExtendedProduction
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct an extended production based on the given ancestor
|
* Construct an extended production based on the given ancestor
|
||||||
|
*
|
||||||
* @param lhs the extended left-hand side
|
* @param lhs the extended left-hand side
|
||||||
* @param rhs the extended right-hand side
|
* @param rhs the extended right-hand side
|
||||||
* @param finalState the end state of the final symbol of the RHS
|
* @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
|
* Get the final state of this production
|
||||||
|
*
|
||||||
* @return the end state of the last symbol of the RHS
|
* @return the end state of the last symbol of the RHS
|
||||||
*/
|
*/
|
||||||
public int getFinalState() {
|
public int getFinalState() {
|
||||||
@@ -57,6 +59,7 @@ public class AssemblyExtendedProduction
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the original production from which this production was derived
|
* Get the original production from which this production was derived
|
||||||
|
*
|
||||||
* @return the original production
|
* @return the original production
|
||||||
*/
|
*/
|
||||||
public AssemblyProduction getAncestor() {
|
public AssemblyProduction getAncestor() {
|
||||||
|
|||||||
+23
-13
@@ -17,8 +17,6 @@ package ghidra.app.plugin.assembler.sleigh.grammars;
|
|||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
import org.apache.commons.collections4.map.LazyMap;
|
|
||||||
|
|
||||||
import ghidra.app.plugin.assembler.sleigh.sem.AssemblyConstructorSemantic;
|
import ghidra.app.plugin.assembler.sleigh.sem.AssemblyConstructorSemantic;
|
||||||
import ghidra.app.plugin.assembler.sleigh.symbol.AssemblyNonTerminal;
|
import ghidra.app.plugin.assembler.sleigh.symbol.AssemblyNonTerminal;
|
||||||
import ghidra.app.plugin.processors.sleigh.Constructor;
|
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
|
* 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
|
* 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
|
* 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
|
* 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
|
public class AssemblyGrammar
|
||||||
extends AbstractAssemblyGrammar<AssemblyNonTerminal, AssemblyProduction> {
|
extends AbstractAssemblyGrammar<AssemblyNonTerminal, AssemblyProduction> {
|
||||||
// a nested map of semantics by production, by constructor
|
// a nested map of semantics by production, by constructor
|
||||||
protected final Map<AssemblyProduction, Map<Constructor, AssemblyConstructorSemantic>> semantics =
|
protected final Map<AssemblyProduction, Map<Constructor, AssemblyConstructorSemantic>> semanticsByProduction =
|
||||||
LazyMap.lazyMap(new TreeMap<>(), () -> new TreeMap<>());
|
new TreeMap<>();
|
||||||
|
protected final Map<Constructor, AssemblyConstructorSemantic> semanticsByConstructor =
|
||||||
|
new HashMap<>();
|
||||||
// a map of purely recursive, e.g., I => I, productions by name of LHS
|
// a map of purely recursive, e.g., I => I, productions by name of LHS
|
||||||
protected final Map<String, AssemblyProduction> pureRecursive = new TreeMap<>();
|
protected final Map<String, AssemblyProduction> pureRecursive = new TreeMap<>();
|
||||||
|
|
||||||
@@ -58,6 +59,7 @@ public class AssemblyGrammar
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a production associated with a SLEIGH constructor semantic
|
* Add a production associated with a SLEIGH constructor semantic
|
||||||
|
*
|
||||||
* @param lhs the left-hand side
|
* @param lhs the left-hand side
|
||||||
* @param rhs the right-hand side
|
* @param rhs the right-hand side
|
||||||
* @param pattern the pattern associated with the constructor
|
* @param pattern the pattern associated with the constructor
|
||||||
@@ -68,27 +70,32 @@ public class AssemblyGrammar
|
|||||||
DisjointPattern pattern, Constructor cons, List<Integer> indices) {
|
DisjointPattern pattern, Constructor cons, List<Integer> indices) {
|
||||||
AssemblyProduction prod = newProduction(lhs, rhs);
|
AssemblyProduction prod = newProduction(lhs, rhs);
|
||||||
addProduction(prod);
|
addProduction(prod);
|
||||||
Map<Constructor, AssemblyConstructorSemantic> map = semantics.get(prod);
|
Map<Constructor, AssemblyConstructorSemantic> map =
|
||||||
AssemblyConstructorSemantic sem = map.get(cons);
|
semanticsByProduction.computeIfAbsent(prod, p -> new TreeMap<>());
|
||||||
if (sem == null) {
|
AssemblyConstructorSemantic sem =
|
||||||
sem = new AssemblyConstructorSemantic(cons, indices);
|
map.computeIfAbsent(cons, c -> new AssemblyConstructorSemantic(cons, indices));
|
||||||
map.put(cons, sem);
|
if (!indices.equals(sem.getOperandIndices())) {
|
||||||
}
|
|
||||||
else if (!indices.equals(sem.getOperandIndices())) {
|
|
||||||
throw new IllegalStateException(
|
throw new IllegalStateException(
|
||||||
"Productions of the same constructor must have same operand indices");
|
"Productions of the same constructor must have same operand indices");
|
||||||
}
|
}
|
||||||
|
semanticsByConstructor.put(cons, sem);
|
||||||
|
|
||||||
sem.addPattern(pattern);
|
sem.addPattern(pattern);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the semantics associated with a given production
|
* Get the semantics associated with a given production
|
||||||
|
*
|
||||||
* @param prod the production
|
* @param prod the production
|
||||||
* @return all semantics associated with the given production
|
* @return all semantics associated with the given production
|
||||||
*/
|
*/
|
||||||
public Collection<AssemblyConstructorSemantic> getSemantics(AssemblyProduction prod) {
|
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
|
@Override
|
||||||
@@ -96,13 +103,15 @@ public class AssemblyGrammar
|
|||||||
super.combine(that);
|
super.combine(that);
|
||||||
if (that instanceof AssemblyGrammar) {
|
if (that instanceof AssemblyGrammar) {
|
||||||
AssemblyGrammar ag = (AssemblyGrammar) that;
|
AssemblyGrammar ag = (AssemblyGrammar) that;
|
||||||
this.semantics.putAll(ag.semantics);
|
this.semanticsByProduction.putAll(ag.semanticsByProduction);
|
||||||
|
this.semanticsByConstructor.putAll(ag.semanticsByConstructor);
|
||||||
this.pureRecursive.putAll(ag.pureRecursive);
|
this.pureRecursive.putAll(ag.pureRecursive);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all productions in the grammar that are purely recursive
|
* Get all productions in the grammar that are purely recursive
|
||||||
|
*
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public Collection<AssemblyProduction> getPureRecursive() {
|
public Collection<AssemblyProduction> getPureRecursive() {
|
||||||
@@ -111,6 +120,7 @@ public class AssemblyGrammar
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Obtain, if present, the purely recursive production having the given LHS
|
* Obtain, if present, the purely recursive production having the given LHS
|
||||||
|
*
|
||||||
* @param lhs the left-hand side
|
* @param lhs the left-hand side
|
||||||
* @return the desired production, or null
|
* @return the desired production, or null
|
||||||
*/
|
*/
|
||||||
|
|||||||
+136
-18
@@ -16,8 +16,9 @@
|
|||||||
package ghidra.app.plugin.assembler.sleigh.grammars;
|
package ghidra.app.plugin.assembler.sleigh.grammars;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.function.Consumer;
|
||||||
import org.apache.commons.collections4.list.AbstractListDecorator;
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import ghidra.app.plugin.assembler.sleigh.symbol.*;
|
import ghidra.app.plugin.assembler.sleigh.symbol.*;
|
||||||
import ghidra.app.plugin.assembler.sleigh.tree.AssemblyParseToken;
|
import ghidra.app.plugin.assembler.sleigh.tree.AssemblyParseToken;
|
||||||
@@ -25,29 +26,29 @@ import ghidra.app.plugin.assembler.sleigh.tree.AssemblyParseToken;
|
|||||||
/**
|
/**
|
||||||
* A "string" of symbols
|
* 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
|
* "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.
|
* 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
|
* @param <NT> the type of non-terminals
|
||||||
*/
|
*/
|
||||||
public class AssemblySentential<NT extends AssemblyNonTerminal> extends
|
public class AssemblySentential<NT extends AssemblyNonTerminal>
|
||||||
AbstractListDecorator<AssemblySymbol> implements Comparable<AssemblySentential<NT>> {
|
implements Comparable<AssemblySentential<NT>>, Iterable<AssemblySymbol> {
|
||||||
private List<AssemblySymbol> symbols;
|
private List<AssemblySymbol> symbols;
|
||||||
|
private final List<AssemblySymbol> unmodifiableSymbols;
|
||||||
private boolean finished = false;
|
private boolean finished = false;
|
||||||
public static final AssemblyStringTerminal WHITE_SPACE = new WhiteSpace();
|
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
|
* Construct a string from the given list of symbols
|
||||||
|
*
|
||||||
* @param symbols
|
* @param symbols
|
||||||
*/
|
*/
|
||||||
public AssemblySentential(List<? extends AssemblySymbol> symbols) {
|
public AssemblySentential(List<? extends AssemblySymbol> symbols) {
|
||||||
this.symbols = new ArrayList<>(symbols);
|
this.symbols = new ArrayList<>(symbols);
|
||||||
}
|
this.unmodifiableSymbols = Collections.unmodifiableList(symbols);
|
||||||
|
|
||||||
@Override
|
|
||||||
protected List<AssemblySymbol> decorated() {
|
|
||||||
return symbols;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -58,19 +59,22 @@ public class AssemblySentential<NT extends AssemblyNonTerminal> extends
|
|||||||
*/
|
*/
|
||||||
public AssemblySentential() {
|
public AssemblySentential() {
|
||||||
this.symbols = new ArrayList<>();
|
this.symbols = new ArrayList<>();
|
||||||
|
this.unmodifiableSymbols = Collections.unmodifiableList(symbols);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a string from any number of symbols
|
* Construct a string from any number of symbols
|
||||||
|
*
|
||||||
* @param syms
|
* @param syms
|
||||||
*/
|
*/
|
||||||
public AssemblySentential(AssemblySymbol... syms) {
|
public AssemblySentential(AssemblySymbol... syms) {
|
||||||
this.symbols = Arrays.asList(syms);
|
this.symbols = Arrays.asList(syms);
|
||||||
|
this.unmodifiableSymbols = Collections.unmodifiableList(symbols);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
if (symbols.size() == 0) {
|
if (symbols.isEmpty()) {
|
||||||
return "e";
|
return "e";
|
||||||
}
|
}
|
||||||
Iterator<? extends AssemblySymbol> symIt = symbols.iterator();
|
Iterator<? extends AssemblySymbol> symIt = symbols.iterator();
|
||||||
@@ -117,6 +121,7 @@ public class AssemblySentential<NT extends AssemblyNonTerminal> extends
|
|||||||
/**
|
/**
|
||||||
* A "whitespace" terminal
|
* A "whitespace" terminal
|
||||||
*
|
*
|
||||||
|
* <p>
|
||||||
* This terminal represents "optional" whitespace. "Optional" because in certain circumstances,
|
* This terminal represents "optional" whitespace. "Optional" because in certain circumstances,
|
||||||
* whitespace is not actually required, i.e., before or after a special character.
|
* 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
|
@Override
|
||||||
public Collection<AssemblyParseToken> match(String buffer, int pos, AssemblyGrammar grammar,
|
public Collection<AssemblyParseToken> match(String buffer, int pos, AssemblyGrammar grammar,
|
||||||
Map<String, Long> labels) {
|
AssemblyNumericSymbols symbols) {
|
||||||
if (buffer.length() == 0) {
|
if (buffer.length() == 0) {
|
||||||
return Collections.singleton(new WhiteSpaceParseToken(grammar, this, ""));
|
return Collections.singleton(new WhiteSpaceParseToken(grammar, this, ""));
|
||||||
}
|
}
|
||||||
@@ -158,7 +163,7 @@ public class AssemblySentential<NT extends AssemblyNonTerminal> extends
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<String> getSuggestions(String got, Map<String, Long> labels) {
|
public Collection<String> getSuggestions(String got, AssemblyNumericSymbols symbols) {
|
||||||
return Collections.singleton(" ");
|
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
|
* 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
|
* "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.
|
* 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
|
* @return true if whitespace was added
|
||||||
*/
|
*/
|
||||||
public boolean addWS() {
|
public boolean addWS() {
|
||||||
@@ -193,7 +210,95 @@ public class AssemblySentential<NT extends AssemblyNonTerminal> extends
|
|||||||
if (last != null) {
|
if (last != null) {
|
||||||
return false;
|
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
|
// 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() {
|
public void finish() {
|
||||||
if (finished) {
|
if (finished) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
symbols = Collections.unmodifiableList(symbols);
|
symbols = unmodifiableSymbols;
|
||||||
finished = true;
|
finished = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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));
|
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
|
* 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,
|
* 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.
|
* 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
|
* Compute the first and follow sets for every non-terminal in the given grammar
|
||||||
|
*
|
||||||
* @param grammar the grammar
|
* @param grammar the grammar
|
||||||
*/
|
*/
|
||||||
public AssemblyFirstFollow(AbstractAssemblyGrammar<?, ?> grammar) {
|
public AssemblyFirstFollow(AbstractAssemblyGrammar<?, ?> grammar) {
|
||||||
@@ -61,7 +63,7 @@ public class AssemblyFirstFollow {
|
|||||||
while (changed) {
|
while (changed) {
|
||||||
changed = false;
|
changed = false;
|
||||||
for (AbstractAssemblyProduction<?> prod : grammar) {
|
for (AbstractAssemblyProduction<?> prod : grammar) {
|
||||||
if (nullable.containsAll(prod)) {
|
if (nullable.containsAll(prod.getRHS().getSymbols())) {
|
||||||
changed |= nullable.add(prod.getLHS());
|
changed |= nullable.add(prod.getLHS());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -81,7 +83,7 @@ public class AssemblyFirstFollow {
|
|||||||
// Add the first of all each symbol
|
// Add the first of all each symbol
|
||||||
// Terminate after a terminal or non-nullable symbol
|
// Terminate after a terminal or non-nullable symbol
|
||||||
for (AbstractAssemblyProduction<?> prod : grammar) {
|
for (AbstractAssemblyProduction<?> prod : grammar) {
|
||||||
for (AssemblySymbol sym : prod) {
|
for (AssemblySymbol sym : prod.getRHS()) {
|
||||||
if (sym instanceof AssemblyNonTerminal) {
|
if (sym instanceof AssemblyNonTerminal) {
|
||||||
AssemblyNonTerminal nt = (AssemblyNonTerminal) sym;
|
AssemblyNonTerminal nt = (AssemblyNonTerminal) sym;
|
||||||
changed |= first.putAll(prod.getLHS(), first.get(nt));
|
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
|
// Finish the subwalk after a terminal or non-nullable symbol
|
||||||
// If you hit the end, add follow(LHS) to follow the current symbol
|
// If you hit the end, add follow(LHS) to follow the current symbol
|
||||||
for (AbstractAssemblyProduction<?> prod : grammar) {
|
for (AbstractAssemblyProduction<?> prod : grammar) {
|
||||||
nextX: for (int i = 0; i < prod.size(); i++) {
|
nextX: for (int i = 0; i < prod.getRHS().size(); i++) {
|
||||||
AssemblySymbol px = prod.get(i);
|
AssemblySymbol px = prod.getRHS().getSymbol(i);
|
||||||
if (px instanceof AssemblyNonTerminal) {
|
if (px instanceof AssemblyNonTerminal) {
|
||||||
AssemblyNonTerminal X = (AssemblyNonTerminal) px;
|
AssemblyNonTerminal X = (AssemblyNonTerminal) px;
|
||||||
int j;
|
int j;
|
||||||
for (j = i + 1; j < prod.size(); j++) {
|
for (j = i + 1; j < prod.getRHS().size(); j++) {
|
||||||
AssemblySymbol B = prod.get(j);
|
AssemblySymbol B = prod.getRHS().getSymbol(j);
|
||||||
if (B instanceof AssemblyNonTerminal) {
|
if (B instanceof AssemblyNonTerminal) {
|
||||||
AssemblyNonTerminal nt = (AssemblyNonTerminal) B;
|
AssemblyNonTerminal nt = (AssemblyNonTerminal) B;
|
||||||
changed |= follow.putAll(X, first.get(nt));
|
changed |= follow.putAll(X, first.get(nt));
|
||||||
@@ -149,7 +151,9 @@ public class AssemblyFirstFollow {
|
|||||||
/**
|
/**
|
||||||
* Get the nullable set
|
* Get the nullable set
|
||||||
*
|
*
|
||||||
|
* <p>
|
||||||
* That is the set of all non-terminals, which through some derivation, can produce epsilon.
|
* That is the set of all non-terminals, which through some derivation, can produce epsilon.
|
||||||
|
*
|
||||||
* @return the set
|
* @return the set
|
||||||
*/
|
*/
|
||||||
public Collection<AssemblyNonTerminal> getNullable() {
|
public Collection<AssemblyNonTerminal> getNullable() {
|
||||||
@@ -159,8 +163,10 @@ public class AssemblyFirstFollow {
|
|||||||
/**
|
/**
|
||||||
* Get the first set for a given non-terminal
|
* 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,
|
* That is the set of all terminals, which through some derivation from the given non-terminal,
|
||||||
* can appear first in a sentential form.
|
* can appear first in a sentential form.
|
||||||
|
*
|
||||||
* @param nt the non-terminal
|
* @param nt the non-terminal
|
||||||
* @return the set
|
* @return the set
|
||||||
*/
|
*/
|
||||||
@@ -171,8 +177,10 @@ public class AssemblyFirstFollow {
|
|||||||
/**
|
/**
|
||||||
* Get the follow set for a given non-terminal
|
* 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
|
* 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.
|
* appear immediately after the given non-terminal in a sentential form.
|
||||||
|
*
|
||||||
* @param nt the non-terminal
|
* @param nt the non-terminal
|
||||||
* @return the set
|
* @return the set
|
||||||
*/
|
*/
|
||||||
@@ -182,6 +190,7 @@ public class AssemblyFirstFollow {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* For debugging, print out the computed sets to the given stream
|
* For debugging, print out the computed sets to the given stream
|
||||||
|
*
|
||||||
* @param out the stream
|
* @param out the stream
|
||||||
*/
|
*/
|
||||||
public void print(PrintStream out) {
|
public void print(PrintStream out) {
|
||||||
|
|||||||
+1
@@ -40,6 +40,7 @@ public class AssemblyParseAcceptResult extends AssemblyParseResult {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the tree
|
* Get the tree
|
||||||
|
*
|
||||||
* @return the tree
|
* @return the tree
|
||||||
*/
|
*/
|
||||||
public AssemblyParseBranch getTree() {
|
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