mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-05-27 23:17:03 +08:00
GP-4185: Make Assembler more extensible
This commit is contained in:
+3
-4
@@ -85,8 +85,8 @@ public class StackUnwinderTest extends AbstractGhidraHeadedDebuggerTest {
|
|||||||
|
|
||||||
public static final AssemblySelector NO_16BIT_CALLS = new AssemblySelector() {
|
public static final AssemblySelector NO_16BIT_CALLS = new AssemblySelector() {
|
||||||
@Override
|
@Override
|
||||||
public AssemblyResolvedPatterns select(AssemblyResolutionResults rr,
|
public Selection select(AssemblyResolutionResults rr, AssemblyPatternBlock ctx)
|
||||||
AssemblyPatternBlock ctx) throws AssemblySemanticException {
|
throws AssemblySemanticException {
|
||||||
for (AssemblyResolvedPatterns res : filterCompatibleAndSort(rr, ctx)) {
|
for (AssemblyResolvedPatterns res : filterCompatibleAndSort(rr, ctx)) {
|
||||||
byte[] ins = res.getInstruction().getVals();
|
byte[] ins = res.getInstruction().getVals();
|
||||||
// HACK to avoid 16-bit CALL.... TODO: Why does this happen?
|
// HACK to avoid 16-bit CALL.... TODO: Why does this happen?
|
||||||
@@ -95,8 +95,7 @@ public class StackUnwinderTest extends AbstractGhidraHeadedDebuggerTest {
|
|||||||
"Filtered 16-bit call " + NumericUtilities.convertBytesToString(ins));
|
"Filtered 16-bit call " + NumericUtilities.convertBytesToString(ins));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
return AssemblyResolution.resolved(res.getInstruction().fillMask(),
|
return new Selection(res.getInstruction().fillMask(), res.getContext());
|
||||||
res.getContext(), "Selected", null, null, null);
|
|
||||||
}
|
}
|
||||||
throw new AssemblySemanticException(semanticErrors);
|
throw new AssemblySemanticException(semanticErrors);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,8 +63,8 @@ public class AssemblyThrasherDevScript extends GhidraScript {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AssemblyResolvedPatterns select(AssemblyResolutionResults rr,
|
public Selection select(AssemblyResolutionResults rr, AssemblyPatternBlock ctx)
|
||||||
AssemblyPatternBlock ctx) throws AssemblySemanticException {
|
throws AssemblySemanticException {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
boolean gotOne = false;
|
boolean gotOne = false;
|
||||||
boolean failedOne = false;
|
boolean failedOne = false;
|
||||||
|
|||||||
+2
-199
@@ -15,15 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package ghidra.app.plugin.assembler;
|
package ghidra.app.plugin.assembler;
|
||||||
|
|
||||||
import java.util.Collection;
|
import ghidra.app.plugin.assembler.sleigh.sem.AssemblyResolvedPatterns;
|
||||||
|
|
||||||
import ghidra.app.plugin.assembler.sleigh.parse.AssemblyParseResult;
|
|
||||||
import ghidra.app.plugin.assembler.sleigh.sem.*;
|
|
||||||
import ghidra.program.model.address.Address;
|
|
||||||
import ghidra.program.model.address.AddressOverflowException;
|
|
||||||
import ghidra.program.model.listing.Instruction;
|
|
||||||
import ghidra.program.model.listing.InstructionIterator;
|
|
||||||
import ghidra.program.model.mem.MemoryAccessException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The primary interface for performing assembly in Ghidra.
|
* The primary interface for performing assembly in Ghidra.
|
||||||
@@ -32,194 +24,5 @@ import ghidra.program.model.mem.MemoryAccessException;
|
|||||||
* Use the {@link Assemblers} class to obtain a suitable implementation for a given program or
|
* Use the {@link Assemblers} class to obtain a suitable implementation for a given program or
|
||||||
* language.
|
* language.
|
||||||
*/
|
*/
|
||||||
public interface Assembler {
|
public interface Assembler extends GenericAssembler<AssemblyResolvedPatterns> {
|
||||||
/**
|
|
||||||
* Assemble a sequence of instructions and place them at the given address.
|
|
||||||
*
|
|
||||||
* <p>
|
|
||||||
* This method is only valid if the assembler is bound to a program. An instance may optionally
|
|
||||||
* implement this method without a program binding. In that case, the returned iterator will
|
|
||||||
* refer to pseudo instructions.
|
|
||||||
*
|
|
||||||
* <p>
|
|
||||||
* <b>NOTE:</b> There must be an active transaction on the bound program for this method to
|
|
||||||
* succeed.
|
|
||||||
*
|
|
||||||
* @param at the location where the resulting instructions should be placed
|
|
||||||
* @param listing a new-line separated or array sequence of instructions
|
|
||||||
* @return an iterator over the resulting instructions
|
|
||||||
* @throws AssemblySyntaxException a textual instruction is non well-formed
|
|
||||||
* @throws AssemblySemanticException a well-formed instruction cannot be assembled
|
|
||||||
* @throws MemoryAccessException there is an issue writing the result to program memory
|
|
||||||
* @throws AddressOverflowException the resulting block is beyond the valid address range
|
|
||||||
*/
|
|
||||||
public InstructionIterator assemble(Address at, String... listing)
|
|
||||||
throws AssemblySyntaxException,
|
|
||||||
AssemblySemanticException, MemoryAccessException, AddressOverflowException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Assemble a line instruction at the given address.
|
|
||||||
*
|
|
||||||
* <p>
|
|
||||||
* This method is valid with or without a bound program. Even if bound, the program is not
|
|
||||||
* modified; however, the appropriate context information is taken from the bound program.
|
|
||||||
* Without a program, the language's default context is taken at the given location.
|
|
||||||
*
|
|
||||||
* @param at the location of the start of the instruction
|
|
||||||
* @param line the textual assembly code
|
|
||||||
* @return the binary machine code, suitable for placement at the given address
|
|
||||||
* @throws AssemblySyntaxException the textual instruction is not well-formed
|
|
||||||
* @throws AssemblySemanticException the the well-formed instruction cannot be assembled
|
|
||||||
*/
|
|
||||||
public byte[] assembleLine(Address at, String line)
|
|
||||||
throws AssemblySyntaxException, AssemblySemanticException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Assemble a line instruction at the given address, assuming the given context.
|
|
||||||
*
|
|
||||||
* <p>
|
|
||||||
* This method works like {@link #assembleLine(Address, String)} except that it allows you to
|
|
||||||
* override the assumed context at that location.
|
|
||||||
*
|
|
||||||
* @param at the location of the start of the instruction
|
|
||||||
* @param line the textual assembly code
|
|
||||||
* @param ctx the context register value at the start of the instruction
|
|
||||||
* @return the results of semantic resolution (from all parse results)
|
|
||||||
* @throws AssemblySyntaxException the textual instruction is not well-formed
|
|
||||||
* @throws AssemblySemanticException the well-formed instruction cannot be assembled
|
|
||||||
*/
|
|
||||||
public byte[] assembleLine(Address at, String line, AssemblyPatternBlock ctx)
|
|
||||||
throws AssemblySemanticException, AssemblySyntaxException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parse a line instruction.
|
|
||||||
*
|
|
||||||
* <p>
|
|
||||||
* Generally, you should just use {@link #assembleLine(Address, String)}, but if you'd like
|
|
||||||
* access to the parse trees outside of an {@link AssemblySelector}, then this may be an
|
|
||||||
* acceptable option. Most notably, this is an excellent way to obtain suggestions for
|
|
||||||
* auto-completion.
|
|
||||||
*
|
|
||||||
* <p>
|
|
||||||
* Each item in the returned collection is either a complete parse tree, or a syntax error
|
|
||||||
* Because all parse paths are attempted, it's possible to get many mixed results. For example,
|
|
||||||
* The input line may be a valid instruction; however, there may be suggestions to continue the
|
|
||||||
* line toward another valid instruction.
|
|
||||||
*
|
|
||||||
* @param line the line (or partial line) to parse
|
|
||||||
* @return the results of parsing
|
|
||||||
*/
|
|
||||||
public Collection<AssemblyParseResult> parseLine(String line);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Resolve a given parse tree at the given address, assuming the given context
|
|
||||||
*
|
|
||||||
* <p>
|
|
||||||
* Each item in the returned collection is either a completely resolved instruction, or a
|
|
||||||
* semantic error. Because all resolutions are attempted, it's possible to get many mixed
|
|
||||||
* results.
|
|
||||||
*
|
|
||||||
* <p>
|
|
||||||
* <b>NOTE:</b> The resolved instructions are given as masks and values. Where the mask does not
|
|
||||||
* cover, you can choose any value.
|
|
||||||
*
|
|
||||||
* @param parse a parse result giving a valid tree
|
|
||||||
* @param at the location of the start of the instruction
|
|
||||||
* @param ctx the context register value at the start of the instruction
|
|
||||||
* @return the results of semantic resolution
|
|
||||||
*/
|
|
||||||
public AssemblyResolutionResults resolveTree(AssemblyParseResult parse, Address at,
|
|
||||||
AssemblyPatternBlock ctx);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Resolve a given parse tree at the given address.
|
|
||||||
*
|
|
||||||
* <p>
|
|
||||||
* Each item in the returned collection is either a completely resolved instruction, or a
|
|
||||||
* semantic error. Because all resolutions are attempted, it's possible to get many mixed
|
|
||||||
* results.
|
|
||||||
*
|
|
||||||
* <p>
|
|
||||||
* <b>NOTE:</b> The resolved instructions are given as masks and values. Where the mask does not
|
|
||||||
* cover, you can choose any value.
|
|
||||||
*
|
|
||||||
* @param parse a parse result giving a valid tree
|
|
||||||
* @param at the location of the start of the instruction
|
|
||||||
* @return the results of semantic resolution
|
|
||||||
*/
|
|
||||||
public AssemblyResolutionResults resolveTree(AssemblyParseResult parse, Address at);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Assemble a line instruction at the given address.
|
|
||||||
*
|
|
||||||
* <p>
|
|
||||||
* This method works like {@link #resolveLine(Address, String, AssemblyPatternBlock)}, except
|
|
||||||
* that it derives the context using {@link #getContextAt(Address)}.
|
|
||||||
*
|
|
||||||
* @param at the location of the start of the instruction
|
|
||||||
* @param line the textual assembly code
|
|
||||||
* @return the collection of semantic resolution results
|
|
||||||
* @throws AssemblySyntaxException the textual instruction is not well-formed
|
|
||||||
*/
|
|
||||||
public AssemblyResolutionResults resolveLine(Address at, String line)
|
|
||||||
throws AssemblySyntaxException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Assemble a line instruction at the given address, assuming the given context.
|
|
||||||
*
|
|
||||||
* <p>
|
|
||||||
* This method works like {@link #assembleLine(Address, String, AssemblyPatternBlock)}, except
|
|
||||||
* that it returns all possible resolutions for the parse trees that pass the
|
|
||||||
* {@link AssemblySelector}.
|
|
||||||
*
|
|
||||||
* @param at the location of the start of the instruction
|
|
||||||
* @param line the textual assembly code
|
|
||||||
* @param ctx the context register value at the start of the instruction
|
|
||||||
* @return the collection of semantic resolution results
|
|
||||||
* @throws AssemblySyntaxException the textual instruction is not well-formed
|
|
||||||
*/
|
|
||||||
public AssemblyResolutionResults resolveLine(Address at, String line, AssemblyPatternBlock ctx)
|
|
||||||
throws AssemblySyntaxException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Place a resolved (and fully-masked) instruction into the bound program.
|
|
||||||
*
|
|
||||||
* <p>
|
|
||||||
* This method is not valid without a program binding. Also, this method must be called during a
|
|
||||||
* program database transaction.
|
|
||||||
*
|
|
||||||
* @param res the resolved and fully-masked instruction
|
|
||||||
* @param at the location of the start of the instruction
|
|
||||||
* @return the new {@link Instruction} code unit
|
|
||||||
* @throws MemoryAccessException there is an issue writing the result to program memory
|
|
||||||
*/
|
|
||||||
public Instruction patchProgram(AssemblyResolvedPatterns res, Address at)
|
|
||||||
throws MemoryAccessException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Place instruction bytes into the bound program.
|
|
||||||
*
|
|
||||||
* <p>
|
|
||||||
* This method is not valid without a program binding. Also, this method must be called during a
|
|
||||||
* program database transaction.
|
|
||||||
*
|
|
||||||
* @param insbytes the instruction data
|
|
||||||
* @param at the location of the start of the instruction
|
|
||||||
* @return an iterator over the disassembled instructions
|
|
||||||
* @throws MemoryAccessException there is an issue writing the result to program memory
|
|
||||||
*/
|
|
||||||
public InstructionIterator patchProgram(byte[] insbytes, Address at)
|
|
||||||
throws MemoryAccessException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the context at a given address
|
|
||||||
*
|
|
||||||
* <p>
|
|
||||||
* If there is a program binding, this will extract the actual context at the given address.
|
|
||||||
* Otherwise, it will obtain the default context at the given address for the language.
|
|
||||||
*
|
|
||||||
* @param addr the address
|
|
||||||
* @return the context
|
|
||||||
*/
|
|
||||||
public AssemblyPatternBlock getContextAt(Address addr);
|
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-29
@@ -15,42 +15,18 @@
|
|||||||
*/
|
*/
|
||||||
package ghidra.app.plugin.assembler;
|
package ghidra.app.plugin.assembler;
|
||||||
|
|
||||||
import ghidra.program.model.lang.Language;
|
import ghidra.app.plugin.assembler.sleigh.sem.AssemblyResolvedPatterns;
|
||||||
import ghidra.program.model.lang.LanguageID;
|
|
||||||
import ghidra.program.model.listing.Program;
|
import ghidra.program.model.listing.Program;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An interface to build an assembler for a given language
|
* An interface to build an assembler for a given language
|
||||||
*/
|
*/
|
||||||
public interface AssemblerBuilder {
|
public interface AssemblerBuilder
|
||||||
/**
|
extends GenericAssemblerBuilder<AssemblyResolvedPatterns, Assembler> {
|
||||||
* Get the ID of the language for which this instance builds an assembler
|
|
||||||
*
|
|
||||||
* @return the language ID
|
|
||||||
*/
|
|
||||||
public LanguageID getLanguageID();
|
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Get the language for which this instance builds an assembler
|
|
||||||
*
|
|
||||||
* @return the language
|
|
||||||
*/
|
|
||||||
public Language getLanguage();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Build an assembler with the given selector callback
|
|
||||||
*
|
|
||||||
* @param selector the selector callback
|
|
||||||
* @return the built assembler
|
|
||||||
*/
|
|
||||||
public Assembler getAssembler(AssemblySelector selector);
|
public Assembler getAssembler(AssemblySelector selector);
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Build an assembler with the given selector callback and program binding
|
|
||||||
*
|
|
||||||
* @param selector the selector callback
|
|
||||||
* @param program the bound program
|
|
||||||
* @return the built assembler
|
|
||||||
*/
|
|
||||||
public Assembler getAssembler(AssemblySelector selector, Program program);
|
public Assembler getAssembler(AssemblySelector selector, Program program);
|
||||||
}
|
}
|
||||||
|
|||||||
+14
-5
@@ -118,6 +118,16 @@ public class AssemblySelector {
|
|||||||
return sorted;
|
return sorted;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A resolved selection from the results given to
|
||||||
|
* {@link AssemblySelector#select(AssemblyResolutionResults, AssemblyPatternBlock)}
|
||||||
|
*
|
||||||
|
* @param ins the resolved instructions bytes, ideally with a full mask
|
||||||
|
* @param ctx the resolved context bytes for compatibility checks
|
||||||
|
*/
|
||||||
|
public record Selection(AssemblyPatternBlock ins, AssemblyPatternBlock ctx) {
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Select an instruction from the possible results.
|
* Select an instruction from the possible results.
|
||||||
*
|
*
|
||||||
@@ -134,16 +144,15 @@ public class AssemblySelector {
|
|||||||
* @param rr the collection of resolved constructors
|
* @param rr the collection of resolved constructors
|
||||||
* @param ctx the applicable context.
|
* @param ctx the applicable context.
|
||||||
* @return a single resolved constructor with a full instruction mask.
|
* @return a single resolved constructor with a full instruction mask.
|
||||||
* @throws AssemblySemanticException
|
* @throws AssemblySemanticException if all the given results are semantic errors
|
||||||
*/
|
*/
|
||||||
public AssemblyResolvedPatterns select(AssemblyResolutionResults rr,
|
public Selection select(AssemblyResolutionResults rr, AssemblyPatternBlock ctx)
|
||||||
AssemblyPatternBlock ctx) throws AssemblySemanticException {
|
throws AssemblySemanticException {
|
||||||
List<AssemblyResolvedPatterns> sorted = filterCompatibleAndSort(rr, ctx);
|
List<AssemblyResolvedPatterns> sorted = filterCompatibleAndSort(rr, ctx);
|
||||||
|
|
||||||
// Pick just the first
|
// Pick just the first
|
||||||
AssemblyResolvedPatterns 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 new Selection(res.getInstruction().fillMask(), res.getContext());
|
||||||
"Selected", null, null, null);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+218
@@ -0,0 +1,218 @@
|
|||||||
|
/* ###
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
import ghidra.app.plugin.assembler.sleigh.parse.AssemblyParseResult;
|
||||||
|
import ghidra.app.plugin.assembler.sleigh.sem.*;
|
||||||
|
import ghidra.program.model.address.Address;
|
||||||
|
import ghidra.program.model.address.AddressOverflowException;
|
||||||
|
import ghidra.program.model.listing.Instruction;
|
||||||
|
import ghidra.program.model.listing.InstructionIterator;
|
||||||
|
import ghidra.program.model.mem.MemoryAccessException;
|
||||||
|
|
||||||
|
public interface GenericAssembler<RP extends AssemblyResolvedPatterns> {
|
||||||
|
/**
|
||||||
|
* Assemble a sequence of instructions and place them at the given address.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This method is only valid if the assembler is bound to a program. An instance may optionally
|
||||||
|
* implement this method without a program binding. In that case, the returned iterator will
|
||||||
|
* refer to pseudo instructions.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* <b>NOTE:</b> There must be an active transaction on the bound program for this method to
|
||||||
|
* succeed.
|
||||||
|
*
|
||||||
|
* @param at the location where the resulting instructions should be placed
|
||||||
|
* @param listing a new-line separated or array sequence of instructions
|
||||||
|
* @return an iterator over the resulting instructions
|
||||||
|
* @throws AssemblySyntaxException a textual instruction is non well-formed
|
||||||
|
* @throws AssemblySemanticException a well-formed instruction cannot be assembled
|
||||||
|
* @throws MemoryAccessException there is an issue writing the result to program memory
|
||||||
|
* @throws AddressOverflowException the resulting block is beyond the valid address range
|
||||||
|
*/
|
||||||
|
public InstructionIterator assemble(Address at, String... listing)
|
||||||
|
throws AssemblySyntaxException,
|
||||||
|
AssemblySemanticException, MemoryAccessException, AddressOverflowException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assemble a line instruction at the given address.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This method is valid with or without a bound program. Even if bound, the program is not
|
||||||
|
* modified; however, the appropriate context information is taken from the bound program.
|
||||||
|
* Without a program, the language's default context is taken at the given location.
|
||||||
|
*
|
||||||
|
* @param at the location of the start of the instruction
|
||||||
|
* @param line the textual assembly code
|
||||||
|
* @return the binary machine code, suitable for placement at the given address
|
||||||
|
* @throws AssemblySyntaxException the textual instruction is not well-formed
|
||||||
|
* @throws AssemblySemanticException the the well-formed instruction cannot be assembled
|
||||||
|
*/
|
||||||
|
public byte[] assembleLine(Address at, String line)
|
||||||
|
throws AssemblySyntaxException, AssemblySemanticException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assemble a line instruction at the given address, assuming the given context.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This method works like {@link #assembleLine(Address, String)} except that it allows you to
|
||||||
|
* override the assumed context at that location.
|
||||||
|
*
|
||||||
|
* @param at the location of the start of the instruction
|
||||||
|
* @param line the textual assembly code
|
||||||
|
* @param ctx the context register value at the start of the instruction
|
||||||
|
* @return the results of semantic resolution (from all parse results)
|
||||||
|
* @throws AssemblySyntaxException the textual instruction is not well-formed
|
||||||
|
* @throws AssemblySemanticException the well-formed instruction cannot be assembled
|
||||||
|
*/
|
||||||
|
public byte[] assembleLine(Address at, String line, AssemblyPatternBlock ctx)
|
||||||
|
throws AssemblySemanticException, AssemblySyntaxException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse a line instruction.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Generally, you should just use {@link #assembleLine(Address, String)}, but if you'd like
|
||||||
|
* access to the parse trees outside of an {@link AssemblySelector}, then this may be an
|
||||||
|
* acceptable option. Most notably, this is an excellent way to obtain suggestions for
|
||||||
|
* auto-completion.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Each item in the returned collection is either a complete parse tree, or a syntax error
|
||||||
|
* Because all parse paths are attempted, it's possible to get many mixed results. For example,
|
||||||
|
* The input line may be a valid instruction; however, there may be suggestions to continue the
|
||||||
|
* line toward another valid instruction.
|
||||||
|
*
|
||||||
|
* @param line the line (or partial line) to parse
|
||||||
|
* @return the results of parsing
|
||||||
|
*/
|
||||||
|
public Collection<AssemblyParseResult> parseLine(String line);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve a given parse tree at the given address, assuming the given context
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Each item in the returned collection is either a completely resolved instruction, or a
|
||||||
|
* semantic error. Because all resolutions are attempted, it's possible to get many mixed
|
||||||
|
* results.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* <b>NOTE:</b> The resolved instructions are given as masks and values. Where the mask does not
|
||||||
|
* cover, you can choose any value.
|
||||||
|
*
|
||||||
|
* @param parse a parse result giving a valid tree
|
||||||
|
* @param at the location of the start of the instruction
|
||||||
|
* @param ctx the context register value at the start of the instruction
|
||||||
|
* @return the results of semantic resolution
|
||||||
|
*/
|
||||||
|
public AssemblyResolutionResults resolveTree(AssemblyParseResult parse, Address at,
|
||||||
|
AssemblyPatternBlock ctx);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve a given parse tree at the given address.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Each item in the returned collection is either a completely resolved instruction, or a
|
||||||
|
* semantic error. Because all resolutions are attempted, it's possible to get many mixed
|
||||||
|
* results.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* <b>NOTE:</b> The resolved instructions are given as masks and values. Where the mask does not
|
||||||
|
* cover, you can choose any value.
|
||||||
|
*
|
||||||
|
* @param parse a parse result giving a valid tree
|
||||||
|
* @param at the location of the start of the instruction
|
||||||
|
* @return the results of semantic resolution
|
||||||
|
*/
|
||||||
|
public AssemblyResolutionResults resolveTree(AssemblyParseResult parse, Address at);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assemble a line instruction at the given address.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This method works like {@link #resolveLine(Address, String, AssemblyPatternBlock)}, except
|
||||||
|
* that it derives the context using {@link #getContextAt(Address)}.
|
||||||
|
*
|
||||||
|
* @param at the location of the start of the instruction
|
||||||
|
* @param line the textual assembly code
|
||||||
|
* @return the collection of semantic resolution results
|
||||||
|
* @throws AssemblySyntaxException the textual instruction is not well-formed
|
||||||
|
*/
|
||||||
|
public AssemblyResolutionResults resolveLine(Address at, String line)
|
||||||
|
throws AssemblySyntaxException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assemble a line instruction at the given address, assuming the given context.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This method works like {@link #assembleLine(Address, String, AssemblyPatternBlock)}, except
|
||||||
|
* that it returns all possible resolutions for the parse trees that pass the
|
||||||
|
* {@link AssemblySelector}.
|
||||||
|
*
|
||||||
|
* @param at the location of the start of the instruction
|
||||||
|
* @param line the textual assembly code
|
||||||
|
* @param ctx the context register value at the start of the instruction
|
||||||
|
* @return the collection of semantic resolution results
|
||||||
|
* @throws AssemblySyntaxException the textual instruction is not well-formed
|
||||||
|
*/
|
||||||
|
public AssemblyResolutionResults resolveLine(Address at, String line, AssemblyPatternBlock ctx)
|
||||||
|
throws AssemblySyntaxException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Place a resolved (and fully-masked) instruction into the bound program.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This method is not valid without a program binding. Also, this method must be called during a
|
||||||
|
* program database transaction.
|
||||||
|
*
|
||||||
|
* @param res the resolved and fully-masked instruction
|
||||||
|
* @param at the location of the start of the instruction
|
||||||
|
* @return the new {@link Instruction} code unit
|
||||||
|
* @throws MemoryAccessException there is an issue writing the result to program memory
|
||||||
|
*/
|
||||||
|
public Instruction patchProgram(AssemblyResolvedPatterns res, Address at)
|
||||||
|
throws MemoryAccessException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Place instruction bytes into the bound program.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This method is not valid without a program binding. Also, this method must be called during a
|
||||||
|
* program database transaction.
|
||||||
|
*
|
||||||
|
* @param insbytes the instruction data
|
||||||
|
* @param at the location of the start of the instruction
|
||||||
|
* @return an iterator over the disassembled instructions
|
||||||
|
* @throws MemoryAccessException there is an issue writing the result to program memory
|
||||||
|
*/
|
||||||
|
public InstructionIterator patchProgram(byte[] insbytes, Address at)
|
||||||
|
throws MemoryAccessException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the context at a given address
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* If there is a program binding, this will extract the actual context at the given address.
|
||||||
|
* Otherwise, it will obtain the default context at the given address for the language.
|
||||||
|
*
|
||||||
|
* @param addr the address
|
||||||
|
* @return the context
|
||||||
|
*/
|
||||||
|
public AssemblyPatternBlock getContextAt(Address addr);
|
||||||
|
}
|
||||||
+55
@@ -0,0 +1,55 @@
|
|||||||
|
/* ###
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
import ghidra.app.plugin.assembler.sleigh.sem.AssemblyResolvedPatterns;
|
||||||
|
import ghidra.program.model.lang.Language;
|
||||||
|
import ghidra.program.model.lang.LanguageID;
|
||||||
|
import ghidra.program.model.listing.Program;
|
||||||
|
|
||||||
|
public interface GenericAssemblerBuilder< //
|
||||||
|
RP extends AssemblyResolvedPatterns, A extends GenericAssembler<RP>> {
|
||||||
|
/**
|
||||||
|
* Get the ID of the language for which this instance builds an assembler
|
||||||
|
*
|
||||||
|
* @return the language ID
|
||||||
|
*/
|
||||||
|
public LanguageID getLanguageID();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the language for which this instance builds an assembler
|
||||||
|
*
|
||||||
|
* @return the language
|
||||||
|
*/
|
||||||
|
public Language getLanguage();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build an assembler with the given selector callback
|
||||||
|
*
|
||||||
|
* @param selector the selector callback
|
||||||
|
* @return the built assembler
|
||||||
|
*/
|
||||||
|
public A getAssembler(AssemblySelector selector);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build an assembler with the given selector callback and program binding
|
||||||
|
*
|
||||||
|
* @param selector the selector callback
|
||||||
|
* @param program the bound program
|
||||||
|
* @return the built assembler
|
||||||
|
*/
|
||||||
|
public A getAssembler(AssemblySelector selector, Program program);
|
||||||
|
}
|
||||||
+279
@@ -0,0 +1,279 @@
|
|||||||
|
/* ###
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
import ghidra.app.plugin.assembler.*;
|
||||||
|
import ghidra.app.plugin.assembler.AssemblySelector.Selection;
|
||||||
|
import ghidra.app.plugin.assembler.sleigh.parse.*;
|
||||||
|
import ghidra.app.plugin.assembler.sleigh.sem.*;
|
||||||
|
import ghidra.app.plugin.assembler.sleigh.symbol.AssemblyNumericSymbols;
|
||||||
|
import ghidra.app.plugin.assembler.sleigh.tree.AssemblyParseBranch;
|
||||||
|
import ghidra.app.plugin.assembler.sleigh.util.DbgTimer;
|
||||||
|
import ghidra.app.plugin.processors.sleigh.SleighLanguage;
|
||||||
|
import ghidra.framework.model.DomainObjectChangedEvent;
|
||||||
|
import ghidra.framework.model.DomainObjectListener;
|
||||||
|
import ghidra.program.disassemble.Disassembler;
|
||||||
|
import ghidra.program.disassemble.DisassemblerMessageListener;
|
||||||
|
import ghidra.program.model.address.*;
|
||||||
|
import ghidra.program.model.lang.Register;
|
||||||
|
import ghidra.program.model.lang.RegisterValue;
|
||||||
|
import ghidra.program.model.listing.*;
|
||||||
|
import ghidra.program.model.mem.Memory;
|
||||||
|
import ghidra.program.model.mem.MemoryAccessException;
|
||||||
|
import ghidra.program.util.ChangeManager;
|
||||||
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
|
public abstract class AbstractSleighAssembler<RP extends AssemblyResolvedPatterns>
|
||||||
|
implements GenericAssembler<RP> {
|
||||||
|
protected static final DbgTimer dbg = DbgTimer.INACTIVE;
|
||||||
|
|
||||||
|
protected class ListenerForSymbolsRefresh implements DomainObjectListener {
|
||||||
|
@Override
|
||||||
|
public void domainObjectChanged(DomainObjectChangedEvent ev) {
|
||||||
|
if (ev.containsEvent(ChangeManager.DOCR_SYMBOL_ADDED) ||
|
||||||
|
ev.containsEvent(ChangeManager.DOCR_SYMBOL_ADDRESS_CHANGED) ||
|
||||||
|
ev.containsEvent(ChangeManager.DOCR_SYMBOL_REMOVED) ||
|
||||||
|
ev.containsEvent(ChangeManager.DOCR_SYMBOL_RENAMED)) {
|
||||||
|
synchronized (lock) {
|
||||||
|
symbols = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final Object lock = new Object();
|
||||||
|
|
||||||
|
protected final SleighLanguage lang;
|
||||||
|
protected final Program program;
|
||||||
|
protected final Listing listing;
|
||||||
|
protected final Memory memory;
|
||||||
|
|
||||||
|
protected final AbstractAssemblyResolutionFactory<RP, ?> factory;
|
||||||
|
protected final AssemblySelector selector;
|
||||||
|
protected final AssemblyParser parser;
|
||||||
|
protected final AssemblyDefaultContext defaultContext;
|
||||||
|
protected final AssemblyContextGraph ctxGraph;
|
||||||
|
|
||||||
|
protected AssemblyNumericSymbols symbols;
|
||||||
|
|
||||||
|
protected AbstractSleighAssembler(AbstractAssemblyResolutionFactory<RP, ?> factory,
|
||||||
|
AssemblySelector selector, Program program, AssemblyParser parser,
|
||||||
|
AssemblyDefaultContext defaultContext, AssemblyContextGraph ctxGraph) {
|
||||||
|
this.factory = factory;
|
||||||
|
this.selector = selector;
|
||||||
|
this.program = program;
|
||||||
|
this.parser = parser;
|
||||||
|
this.defaultContext = defaultContext;
|
||||||
|
this.ctxGraph = ctxGraph;
|
||||||
|
|
||||||
|
this.lang = (SleighLanguage) program.getLanguage();
|
||||||
|
this.listing = program.getListing();
|
||||||
|
this.memory = program.getMemory();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected AbstractSleighAssembler(AbstractAssemblyResolutionFactory<RP, ?> factory,
|
||||||
|
AssemblySelector selector, SleighLanguage lang, AssemblyParser parser,
|
||||||
|
AssemblyDefaultContext defaultContext, AssemblyContextGraph ctxGraph) {
|
||||||
|
this.factory = factory;
|
||||||
|
this.selector = selector;
|
||||||
|
this.lang = lang;
|
||||||
|
this.parser = parser;
|
||||||
|
this.defaultContext = defaultContext;
|
||||||
|
this.ctxGraph = ctxGraph;
|
||||||
|
|
||||||
|
this.program = null;
|
||||||
|
this.listing = null;
|
||||||
|
this.memory = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract AbstractAssemblyTreeResolver<RP> newResolver(Address at,
|
||||||
|
AssemblyParseBranch tree, AssemblyPatternBlock ctx);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Instruction patchProgram(AssemblyResolvedPatterns res, Address at)
|
||||||
|
throws MemoryAccessException {
|
||||||
|
if (!res.getInstruction().isFullMask()) {
|
||||||
|
throw new AssemblySelectionError("Selected instruction must have a full mask.");
|
||||||
|
}
|
||||||
|
return patchProgram(res.getInstruction().getVals(), at).next();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InstructionIterator patchProgram(byte[] insbytes, Address at)
|
||||||
|
throws MemoryAccessException {
|
||||||
|
if (insbytes.length == 0) {
|
||||||
|
return listing.getInstructions(new AddressSet(), true);
|
||||||
|
}
|
||||||
|
Address end = at.add(insbytes.length - 1);
|
||||||
|
listing.clearCodeUnits(at, end, false);
|
||||||
|
memory.setBytes(at, insbytes);
|
||||||
|
AddressSet set = new AddressSet(at, end);
|
||||||
|
|
||||||
|
// Creating this at construction causes it to assess memory flags too early.
|
||||||
|
Disassembler dis = Disassembler.getDisassembler(program, TaskMonitor.DUMMY,
|
||||||
|
DisassemblerMessageListener.IGNORE);
|
||||||
|
dis.disassemble(at, set);
|
||||||
|
return listing.getInstructions(set, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InstructionIterator assemble(Address at, String... assembly)
|
||||||
|
throws AssemblySyntaxException, AssemblySemanticException, MemoryAccessException,
|
||||||
|
AddressOverflowException {
|
||||||
|
Address start = at;
|
||||||
|
ByteArrayOutputStream buf = new ByteArrayOutputStream();
|
||||||
|
for (String part : assembly) {
|
||||||
|
for (String line : part.split("\n")) {
|
||||||
|
RegisterValue rv = program.getProgramContext().getDisassemblyContext(at);
|
||||||
|
dbg.println(rv);
|
||||||
|
AssemblyPatternBlock ctx = AssemblyPatternBlock.fromRegisterValue(rv);
|
||||||
|
ctx = ctx.fillMask();
|
||||||
|
byte[] insbytes = assembleLine(at, line, ctx);
|
||||||
|
if (insbytes == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
buf.write(insbytes);
|
||||||
|
}
|
||||||
|
catch (IOException e) {
|
||||||
|
throw new AssertionError(e);
|
||||||
|
}
|
||||||
|
at = at.addNoWrap(insbytes.length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return patchProgram(buf.toByteArray(), start);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] assembleLine(Address at, String line)
|
||||||
|
throws AssemblySyntaxException, AssemblySemanticException {
|
||||||
|
AssemblyPatternBlock ctx = defaultContext.getDefaultAt(at);
|
||||||
|
ctx = ctx.fillMask();
|
||||||
|
return assembleLine(at, line, ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<AssemblyParseResult> parseLine(String line) {
|
||||||
|
return parser.parse(line, getNumericSymbols());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AssemblyResolutionResults resolveTree(AssemblyParseResult parse, Address at,
|
||||||
|
AssemblyPatternBlock ctx) {
|
||||||
|
if (parse.isError()) {
|
||||||
|
AssemblyResolutionResults results = factory.newAssemblyResolutionResults();
|
||||||
|
AssemblyParseErrorResult err = (AssemblyParseErrorResult) parse;
|
||||||
|
results.add(factory.newErrorBuilder()
|
||||||
|
.error(err.describeError())
|
||||||
|
.description("Parsing")
|
||||||
|
.build());
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
AssemblyParseAcceptResult acc = (AssemblyParseAcceptResult) parse;
|
||||||
|
AbstractAssemblyTreeResolver<RP> tr = newResolver(at, acc.getTree(), ctx);
|
||||||
|
return tr.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AssemblyResolutionResults resolveTree(AssemblyParseResult parse, Address at) {
|
||||||
|
AssemblyPatternBlock ctx = getContextAt(at);
|
||||||
|
return resolveTree(parse, at, ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AssemblyResolutionResults resolveLine(Address at, String line)
|
||||||
|
throws AssemblySyntaxException {
|
||||||
|
return resolveLine(at, line, getContextAt(at).fillMask());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AssemblyResolutionResults resolveLine(Address at, String line, AssemblyPatternBlock ctx)
|
||||||
|
throws AssemblySyntaxException {
|
||||||
|
|
||||||
|
if (!ctx.isFullMask()) {
|
||||||
|
throw new AssemblyError(
|
||||||
|
"Context must be fully-specified (full length, no shift, no unknowns)");
|
||||||
|
}
|
||||||
|
if (lang.getContextBaseRegister() != Register.NO_CONTEXT &&
|
||||||
|
ctx.length() < lang.getContextBaseRegister().getMinimumByteSize()) {
|
||||||
|
throw new AssemblyError(
|
||||||
|
"Context must be fully-specified (full length, no shift, no unknowns)");
|
||||||
|
}
|
||||||
|
Collection<AssemblyParseResult> parse = parseLine(line);
|
||||||
|
parse = selector.filterParse(parse);
|
||||||
|
if (!parse.iterator().hasNext()) { // Iterator.isEmpty()???
|
||||||
|
throw new AssemblySelectionError(
|
||||||
|
"Must select at least one parse result. Report errors via AssemblySyntaxError");
|
||||||
|
}
|
||||||
|
AssemblyResolutionResults results = factory.newAssemblyResolutionResults();
|
||||||
|
for (AssemblyParseResult p : parse) {
|
||||||
|
results.absorb(resolveTree(p, at, ctx));
|
||||||
|
}
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] assembleLine(Address at, String line, AssemblyPatternBlock ctx)
|
||||||
|
throws AssemblySemanticException, AssemblySyntaxException {
|
||||||
|
AssemblyResolutionResults results = resolveLine(at, line, ctx);
|
||||||
|
Selection sel = selector.select(results, ctx);
|
||||||
|
if (sel == null) {
|
||||||
|
throw new AssemblySelectionError(
|
||||||
|
"Must select exactly one instruction. Report errors via AssemblySemanticError");
|
||||||
|
}
|
||||||
|
if (!sel.ins().isFullMask()) {
|
||||||
|
throw new AssemblySelectionError("Selected instruction must have a full mask.");
|
||||||
|
}
|
||||||
|
if (sel.ctx().combine(ctx) == null) {
|
||||||
|
throw new AssemblySelectionError("Selected instruction must have compatible context");
|
||||||
|
}
|
||||||
|
return sel.ins().getVals();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A convenience to obtain assembly symbols
|
||||||
|
*
|
||||||
|
* @return the map
|
||||||
|
*/
|
||||||
|
protected AssemblyNumericSymbols getNumericSymbols() {
|
||||||
|
synchronized (lock) {
|
||||||
|
if (symbols != null) {
|
||||||
|
return symbols;
|
||||||
|
}
|
||||||
|
if (program == null) {
|
||||||
|
symbols = AssemblyNumericSymbols.fromLanguage(lang);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
symbols = AssemblyNumericSymbols.fromProgram(program);
|
||||||
|
}
|
||||||
|
return symbols;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AssemblyPatternBlock getContextAt(Address addr) {
|
||||||
|
if (program != null) {
|
||||||
|
RegisterValue rv = program.getProgramContext().getDisassemblyContext(addr);
|
||||||
|
return AssemblyPatternBlock.fromRegisterValue(rv);
|
||||||
|
}
|
||||||
|
return defaultContext.getDefaultAt(addr);
|
||||||
|
}
|
||||||
|
}
|
||||||
+397
@@ -0,0 +1,397 @@
|
|||||||
|
/* ###
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
import org.apache.commons.collections4.MultiMapUtils;
|
||||||
|
import org.apache.commons.collections4.MultiValuedMap;
|
||||||
|
import org.apache.commons.collections4.multimap.HashSetValuedHashMap;
|
||||||
|
|
||||||
|
import ghidra.app.plugin.assembler.*;
|
||||||
|
import ghidra.app.plugin.assembler.sleigh.grammars.AssemblyGrammar;
|
||||||
|
import ghidra.app.plugin.assembler.sleigh.grammars.AssemblySentential;
|
||||||
|
import ghidra.app.plugin.assembler.sleigh.parse.AssemblyParser;
|
||||||
|
import ghidra.app.plugin.assembler.sleigh.sem.*;
|
||||||
|
import ghidra.app.plugin.assembler.sleigh.symbol.*;
|
||||||
|
import ghidra.app.plugin.assembler.sleigh.util.DbgTimer;
|
||||||
|
import ghidra.app.plugin.assembler.sleigh.util.DbgTimer.DbgCtx;
|
||||||
|
import ghidra.app.plugin.languages.sleigh.SleighLanguages;
|
||||||
|
import ghidra.app.plugin.languages.sleigh.SubtableEntryVisitor;
|
||||||
|
import ghidra.app.plugin.processors.sleigh.*;
|
||||||
|
import ghidra.app.plugin.processors.sleigh.pattern.DisjointPattern;
|
||||||
|
import ghidra.app.plugin.processors.sleigh.symbol.*;
|
||||||
|
import ghidra.app.plugin.processors.sleigh.template.ConstructTpl;
|
||||||
|
import ghidra.app.plugin.processors.sleigh.template.HandleTpl;
|
||||||
|
import ghidra.program.model.lang.LanguageID;
|
||||||
|
import ghidra.program.model.listing.Program;
|
||||||
|
import ghidra.util.SystemUtilities;
|
||||||
|
|
||||||
|
public abstract class AbstractSleighAssemblerBuilder< //
|
||||||
|
RP extends AssemblyResolvedPatterns, A extends GenericAssembler<RP>>
|
||||||
|
implements GenericAssemblerBuilder<RP, A> {
|
||||||
|
protected static final DbgTimer dbg =
|
||||||
|
SystemUtilities.isInTestingBatchMode() ? DbgTimer.INACTIVE : DbgTimer.ACTIVE;
|
||||||
|
|
||||||
|
protected final SleighLanguage lang;
|
||||||
|
protected final AbstractAssemblyResolutionFactory<RP, ?> factory;
|
||||||
|
protected AssemblyGrammar grammar;
|
||||||
|
protected AssemblyDefaultContext defaultContext;
|
||||||
|
protected AssemblyContextGraph ctxGraph;
|
||||||
|
protected AssemblyParser parser;
|
||||||
|
|
||||||
|
protected boolean generated = false;
|
||||||
|
|
||||||
|
// A cache for symbols converted during grammar construction
|
||||||
|
protected Map<String, AssemblySymbol> builtSymbols = new HashMap<>();
|
||||||
|
|
||||||
|
public AbstractSleighAssemblerBuilder(SleighLanguage lang) {
|
||||||
|
this.lang = lang;
|
||||||
|
this.factory = newResolutionFactory();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract AbstractAssemblyResolutionFactory<RP, ?> newResolutionFactory();
|
||||||
|
|
||||||
|
protected abstract A newAssembler(AssemblySelector selector);
|
||||||
|
|
||||||
|
protected abstract A newAssembler(AssemblySelector selector,
|
||||||
|
Program program);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LanguageID getLanguageID() {
|
||||||
|
return lang.getLanguageID();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SleighLanguage getLanguage() {
|
||||||
|
return lang;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Do the actual work to construct an assembler from a SLEIGH language
|
||||||
|
*
|
||||||
|
* @throws SleighException if there's an issue accessing the language
|
||||||
|
*/
|
||||||
|
protected void generateAssembler() throws SleighException {
|
||||||
|
if (generated) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
generated = true;
|
||||||
|
try {
|
||||||
|
buildGrammar();
|
||||||
|
grammar.verify();
|
||||||
|
buildContext();
|
||||||
|
buildContextGraph();
|
||||||
|
buildParser();
|
||||||
|
}
|
||||||
|
catch (SleighException e) {
|
||||||
|
// Not sure this can actually happen here
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public A getAssembler(AssemblySelector selector) {
|
||||||
|
generateAssembler();
|
||||||
|
return newAssembler(selector);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public A getAssembler(AssemblySelector selector, Program program) {
|
||||||
|
generateAssembler();
|
||||||
|
return newAssembler(selector, program);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invert a varnode list to a map suitable for use with {@link AssemblyStringMapTerminal}
|
||||||
|
*
|
||||||
|
* @param vnlist the varnode list symbol
|
||||||
|
* @return the inverted string map
|
||||||
|
*/
|
||||||
|
protected MultiValuedMap<String, Integer> invVarnodeList(VarnodeListSymbol vnlist) {
|
||||||
|
MultiValuedMap<String, Integer> result = new HashSetValuedHashMap<>();
|
||||||
|
int index = -1;
|
||||||
|
for (VarnodeSymbol vnsym : vnlist.getVarnodeTable()) {
|
||||||
|
index++;
|
||||||
|
if (vnsym != null) {
|
||||||
|
// nulls are _ in the spec, meaning the index is undefined.
|
||||||
|
result.put(vnsym.getName(), index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return MultiMapUtils.unmodifiableMultiValuedMap(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invert a value map to a map suitable for use with {@link AssemblyNumericMapTerminal}
|
||||||
|
*
|
||||||
|
* @param vm the value map symbol
|
||||||
|
* @return the inverted numeric map
|
||||||
|
*/
|
||||||
|
protected Map<Long, Integer> invValueMap(ValueMapSymbol vm) {
|
||||||
|
Map<Long, Integer> result = new HashMap<>();
|
||||||
|
List<Long> map = vm.getMap();
|
||||||
|
for (int i = 0; i < map.size(); i++) {
|
||||||
|
long v = map.get(i);
|
||||||
|
result.put(v, i);
|
||||||
|
}
|
||||||
|
return Collections.unmodifiableMap(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invert a name table to a map suitable for use with {@link AssemblyStringMapTerminal}
|
||||||
|
*
|
||||||
|
* @param ns the name symbol
|
||||||
|
* @return the inverted string map
|
||||||
|
*/
|
||||||
|
protected MultiValuedMap<String, Integer> invNameSymbol(NameSymbol ns) {
|
||||||
|
MultiValuedMap<String, Integer> result = new HashSetValuedHashMap<>();
|
||||||
|
int index = -1;
|
||||||
|
for (String name : ns.getNameTable()) {
|
||||||
|
index++;
|
||||||
|
if (name != null) {
|
||||||
|
result.put(name, index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return MultiMapUtils.unmodifiableMultiValuedMap(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert the given operand symbol to an {@link AssemblySymbol}
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* For subtables, this results in a non-terminal, for all others, the result in a terminal.
|
||||||
|
*
|
||||||
|
* @param cons the constructor to which the operand belongs
|
||||||
|
* @param opsym the operand symbol to convert
|
||||||
|
* @return the converted assembly grammar symbol
|
||||||
|
*/
|
||||||
|
protected AssemblySymbol getSymbolFor(Constructor cons, OperandSymbol opsym) {
|
||||||
|
TripleSymbol defsym = opsym.getDefiningSymbol();
|
||||||
|
// If the symbol has no defining symbol, that means the name is only valid in the local
|
||||||
|
// scope. We must keep them unique.
|
||||||
|
String name;
|
||||||
|
if (defsym == null) {
|
||||||
|
name = cons.getParent().getName() + ":" + opsym.getName();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
name = opsym.getName();
|
||||||
|
}
|
||||||
|
AssemblySymbol built = builtSymbols.get(name);
|
||||||
|
if (built != null) {
|
||||||
|
return built;
|
||||||
|
}
|
||||||
|
if (defsym == null) {
|
||||||
|
HandleTpl htpl = getHandleTpl(cons, opsym);
|
||||||
|
built = htpl == null ? new AssemblyNumericTerminal(name, 0, null)
|
||||||
|
: new AssemblyNumericTerminal(name, htpl.getSize(), htpl.getAddressSpace());
|
||||||
|
}
|
||||||
|
else if (defsym instanceof SubtableSymbol) {
|
||||||
|
built = new AssemblyNonTerminal(name);
|
||||||
|
}
|
||||||
|
else if (defsym instanceof VarnodeListSymbol vnListSym) {
|
||||||
|
built = new AssemblyStringMapTerminal(name, invVarnodeList(vnListSym));
|
||||||
|
}
|
||||||
|
else if (defsym instanceof VarnodeSymbol vnSym) {
|
||||||
|
built = new AssemblyStringTerminal(name, vnSym);
|
||||||
|
// Does this need to consume an operand? It seems not.
|
||||||
|
}
|
||||||
|
else if (defsym instanceof ValueMapSymbol vnMapSym) {
|
||||||
|
built = new AssemblyNumericMapTerminal(name, invValueMap(vnMapSym));
|
||||||
|
}
|
||||||
|
else if (defsym instanceof NameSymbol nameSym) {
|
||||||
|
built = new AssemblyStringMapTerminal(name, invNameSymbol(nameSym));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new RuntimeException("Unknown symbol for " + name + ": " + defsym);
|
||||||
|
}
|
||||||
|
builtSymbols.put(name, built);
|
||||||
|
return built;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtain the p-code result handle for the given operand
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This handles a special case, where a constructor prints just one operand and exports that
|
||||||
|
* same operand, often with an explicit size, or as an address in a given space. In such cases,
|
||||||
|
* the listing displays that operand according to that exported size.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* For assembly, this gives a few opportunities: 1) We can/must ensure the specified value fits,
|
||||||
|
* by checking the size. 2) We can/must mask the goal when solving the defining pattern
|
||||||
|
* expression for the operand. 3)) We can/must check that a label's address space matches that
|
||||||
|
* represented by the operand, when used for a numeric terminal.
|
||||||
|
*
|
||||||
|
* @param cons the constructor from which the production is being derived
|
||||||
|
* @param opsym the operand symbol corresponding to the grammatical symbol, whose size we wish
|
||||||
|
* to determine.
|
||||||
|
* @return the size of the operand in bits
|
||||||
|
*/
|
||||||
|
protected HandleTpl getHandleTpl(Constructor cons, OperandSymbol opsym) {
|
||||||
|
ConstructTpl ctpl = cons.getTempl();
|
||||||
|
if (null == ctpl) {
|
||||||
|
// No pcode, no size specification
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
HandleTpl htpl = ctpl.getResult();
|
||||||
|
if (null == htpl) {
|
||||||
|
// If nothing is exported, the size is unspecified
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (opsym.getIndex() != htpl.getOffsetOperandIndex()) {
|
||||||
|
// If the export is not of the same operand, it does not specify its size
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return htpl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build a portion of the grammar representing a table of constructors
|
||||||
|
*
|
||||||
|
* @param subtable the table
|
||||||
|
* @return the partial grammar
|
||||||
|
*/
|
||||||
|
protected AssemblyGrammar buildSubGrammar(SubtableSymbol subtable) {
|
||||||
|
final AssemblyGrammar subgrammar = new AssemblyGrammar(factory);
|
||||||
|
final AssemblyNonTerminal lhs = new AssemblyNonTerminal(subtable.getName());
|
||||||
|
SleighLanguages.traverseConstructors(subtable, new SubtableEntryVisitor() {
|
||||||
|
@Override
|
||||||
|
public int visit(DisjointPattern pattern, Constructor cons) {
|
||||||
|
AssemblySentential<AssemblyNonTerminal> rhs = new AssemblySentential<>();
|
||||||
|
List<Integer> indices = new ArrayList<>();
|
||||||
|
for (String str : cons.getPrintPieces()) {
|
||||||
|
if (str.length() != 0) {
|
||||||
|
if (str.charAt(0) == '\n') {
|
||||||
|
int index = str.charAt(1) - 'A';
|
||||||
|
OperandSymbol opsym = cons.getOperand(index);
|
||||||
|
AssemblySymbol sym = getSymbolFor(cons, opsym);
|
||||||
|
if (sym.takesOperandIndex()) {
|
||||||
|
indices.add(index);
|
||||||
|
}
|
||||||
|
rhs.addSymbol(sym);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
rhs.addSeparators(str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
addProduction(subgrammar, lhs, rhs, pattern, cons, indices);
|
||||||
|
return CONTINUE;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return subgrammar;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extension point: Allows a chance to modify or derive a new production from a given one.
|
||||||
|
*
|
||||||
|
* @param subgrammar the sub-grammar for the sub-table symbol being processed
|
||||||
|
* @see AssemblyGrammar#addProduction(AssemblyNonTerminal, AssemblySentential, DisjointPattern,
|
||||||
|
* Constructor, List)
|
||||||
|
*/
|
||||||
|
protected void addProduction(AssemblyGrammar subgrammar, AssemblyNonTerminal lhs,
|
||||||
|
AssemblySentential<AssemblyNonTerminal> rhs, DisjointPattern pattern, Constructor cons,
|
||||||
|
List<Integer> indices) {
|
||||||
|
subgrammar.addProduction(lhs, rhs, pattern, cons, indices);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build the full grammar for the language
|
||||||
|
*/
|
||||||
|
protected void buildGrammar() {
|
||||||
|
try (DbgCtx dc = dbg.start("Building grammar")) {
|
||||||
|
grammar = new AssemblyGrammar(factory);
|
||||||
|
for (Symbol sym : lang.getSymbolTable().getSymbolList()) {
|
||||||
|
if (sym instanceof SubtableSymbol) {
|
||||||
|
SubtableSymbol subtable = (SubtableSymbol) sym;
|
||||||
|
grammar.combine(buildSubGrammar(subtable));
|
||||||
|
}
|
||||||
|
else if (sym instanceof VarnodeSymbol) {
|
||||||
|
// Ignore. This just becomes a string terminal
|
||||||
|
}
|
||||||
|
else if (sym instanceof StartSymbol) {
|
||||||
|
// Ignore. We handle inst_start in semantic processing
|
||||||
|
}
|
||||||
|
else if (sym instanceof EndSymbol) {
|
||||||
|
// Ignore. We handle inst_next in semantic processing
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (sym instanceof Next2Symbol) {
|
||||||
|
// Ignore. We handle inst_next2 in semantic processing
|
||||||
|
}
|
||||||
|
else if (sym instanceof UseropSymbol) {
|
||||||
|
// Ignore. We don't do pcode.
|
||||||
|
}
|
||||||
|
else if (sym instanceof OperandSymbol) {
|
||||||
|
// Ignore. These are terminals, or will be produced by their defining symbols
|
||||||
|
}
|
||||||
|
else if (sym instanceof ValueSymbol) {
|
||||||
|
// Ignore. These are now terminals
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new RuntimeException("Unexpected type: " + sym.getClass());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
grammar.setStartName("instruction");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build the default context for the language
|
||||||
|
*/
|
||||||
|
protected void buildContext() {
|
||||||
|
defaultContext = new AssemblyDefaultContext(lang);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build the context transition graph for the language
|
||||||
|
*/
|
||||||
|
protected void buildContextGraph() {
|
||||||
|
try (DbgCtx dc = dbg.start("Building context graph")) {
|
||||||
|
ctxGraph = new AssemblyContextGraph(factory, lang, grammar);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build the parser for the language
|
||||||
|
*/
|
||||||
|
protected void buildParser() {
|
||||||
|
try (DbgCtx dc = dbg.start("Building parser")) {
|
||||||
|
parser = new AssemblyParser(grammar);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the built grammar for the language
|
||||||
|
*
|
||||||
|
* @return the grammar
|
||||||
|
*/
|
||||||
|
protected AssemblyGrammar getGrammar() {
|
||||||
|
return grammar;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the built parser for the language
|
||||||
|
*
|
||||||
|
* @return the parser
|
||||||
|
*/
|
||||||
|
protected AssemblyParser getParser() {
|
||||||
|
return parser;
|
||||||
|
}
|
||||||
|
}
|
||||||
+16
-226
@@ -15,28 +15,13 @@
|
|||||||
*/
|
*/
|
||||||
package ghidra.app.plugin.assembler.sleigh;
|
package ghidra.app.plugin.assembler.sleigh;
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
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.AssemblyParser;
|
||||||
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.tree.AssemblyParseBranch;
|
||||||
import ghidra.app.plugin.assembler.sleigh.util.DbgTimer;
|
|
||||||
import ghidra.app.plugin.processors.sleigh.SleighLanguage;
|
import ghidra.app.plugin.processors.sleigh.SleighLanguage;
|
||||||
import ghidra.framework.model.DomainObjectChangedEvent;
|
import ghidra.program.model.address.Address;
|
||||||
import ghidra.framework.model.DomainObjectListener;
|
import ghidra.program.model.listing.Program;
|
||||||
import ghidra.program.disassemble.Disassembler;
|
|
||||||
import ghidra.program.disassemble.DisassemblerMessageListener;
|
|
||||||
import ghidra.program.model.address.*;
|
|
||||||
import ghidra.program.model.lang.Register;
|
|
||||||
import ghidra.program.model.lang.RegisterValue;
|
|
||||||
import ghidra.program.model.listing.*;
|
|
||||||
import ghidra.program.model.mem.Memory;
|
|
||||||
import ghidra.program.model.mem.MemoryAccessException;
|
|
||||||
import ghidra.program.util.ChangeManager;
|
|
||||||
import ghidra.util.task.TaskMonitor;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An {@link Assembler} for a {@link SleighLanguage}.
|
* An {@link Assembler} for a {@link SleighLanguage}.
|
||||||
@@ -45,35 +30,8 @@ import ghidra.util.task.TaskMonitor;
|
|||||||
* For documentation on how the SLEIGH assembler works, see {@link SleighAssemblerBuilder}. To use
|
* For documentation on how the SLEIGH assembler works, see {@link SleighAssemblerBuilder}. To use
|
||||||
* the assembler, please use {@link Assemblers#getAssembler(Program)} or similar.
|
* the assembler, please use {@link Assemblers#getAssembler(Program)} or similar.
|
||||||
*/
|
*/
|
||||||
public class SleighAssembler implements Assembler {
|
public class SleighAssembler extends AbstractSleighAssembler<AssemblyResolvedPatterns>
|
||||||
protected static final DbgTimer dbg = DbgTimer.INACTIVE;
|
implements Assembler {
|
||||||
|
|
||||||
protected class ListenerForSymbolsRefresh implements DomainObjectListener {
|
|
||||||
@Override
|
|
||||||
public void domainObjectChanged(DomainObjectChangedEvent ev) {
|
|
||||||
if (ev.containsEvent(ChangeManager.DOCR_SYMBOL_ADDED) ||
|
|
||||||
ev.containsEvent(ChangeManager.DOCR_SYMBOL_ADDRESS_CHANGED) ||
|
|
||||||
ev.containsEvent(ChangeManager.DOCR_SYMBOL_REMOVED) ||
|
|
||||||
ev.containsEvent(ChangeManager.DOCR_SYMBOL_RENAMED)) {
|
|
||||||
synchronized (lock) {
|
|
||||||
symbols = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected final Object lock = new Object();
|
|
||||||
|
|
||||||
protected AssemblySelector selector;
|
|
||||||
protected Program program;
|
|
||||||
protected Listing listing;
|
|
||||||
protected Memory memory;
|
|
||||||
protected AssemblyParser parser;
|
|
||||||
protected AssemblyDefaultContext defaultContext;
|
|
||||||
protected AssemblyContextGraph ctxGraph;
|
|
||||||
protected SleighLanguage lang;
|
|
||||||
|
|
||||||
protected AssemblyNumericSymbols symbols;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a SleighAssembler.
|
* Construct a SleighAssembler.
|
||||||
@@ -84,13 +42,11 @@ public class SleighAssembler implements Assembler {
|
|||||||
* @param defaultContext the default context for the language
|
* @param defaultContext the default context for the language
|
||||||
* @param ctxGraph the context graph
|
* @param ctxGraph the context graph
|
||||||
*/
|
*/
|
||||||
protected SleighAssembler(AssemblySelector selector, Program program, AssemblyParser parser,
|
protected SleighAssembler(
|
||||||
|
AbstractAssemblyResolutionFactory<AssemblyResolvedPatterns, ?> factory,
|
||||||
|
AssemblySelector selector, Program program, AssemblyParser parser,
|
||||||
AssemblyDefaultContext defaultContext, AssemblyContextGraph ctxGraph) {
|
AssemblyDefaultContext defaultContext, AssemblyContextGraph ctxGraph) {
|
||||||
this(selector, (SleighLanguage) program.getLanguage(), parser, defaultContext, ctxGraph);
|
super(factory, selector, program, parser, defaultContext, ctxGraph);
|
||||||
this.program = program;
|
|
||||||
|
|
||||||
this.listing = program.getListing();
|
|
||||||
this.memory = program.getMemory();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -105,182 +61,16 @@ public class SleighAssembler implements Assembler {
|
|||||||
* @param defaultContext the default context for the language
|
* @param defaultContext the default context for the language
|
||||||
* @param ctxGraph the context graph
|
* @param ctxGraph the context graph
|
||||||
*/
|
*/
|
||||||
protected SleighAssembler(AssemblySelector selector, SleighLanguage lang, AssemblyParser parser,
|
protected SleighAssembler(
|
||||||
|
AbstractAssemblyResolutionFactory<AssemblyResolvedPatterns, ?> factory,
|
||||||
|
AssemblySelector selector, SleighLanguage lang, AssemblyParser parser,
|
||||||
AssemblyDefaultContext defaultContext, AssemblyContextGraph ctxGraph) {
|
AssemblyDefaultContext defaultContext, AssemblyContextGraph ctxGraph) {
|
||||||
this.selector = selector;
|
super(factory, selector, lang, parser, defaultContext, ctxGraph);
|
||||||
this.lang = lang;
|
|
||||||
this.parser = parser;
|
|
||||||
this.defaultContext = defaultContext;
|
|
||||||
this.ctxGraph = ctxGraph;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Instruction patchProgram(AssemblyResolvedPatterns res, Address at)
|
protected AssemblyTreeResolver newResolver(Address at, AssemblyParseBranch tree,
|
||||||
throws MemoryAccessException {
|
|
||||||
if (!res.getInstruction().isFullMask()) {
|
|
||||||
throw new AssemblySelectionError("Selected instruction must have a full mask.");
|
|
||||||
}
|
|
||||||
return patchProgram(res.getInstruction().getVals(), at).next();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public InstructionIterator patchProgram(byte[] insbytes, Address at)
|
|
||||||
throws MemoryAccessException {
|
|
||||||
if (insbytes.length == 0) {
|
|
||||||
return listing.getInstructions(new AddressSet(), true);
|
|
||||||
}
|
|
||||||
Address end = at.add(insbytes.length - 1);
|
|
||||||
listing.clearCodeUnits(at, end, false);
|
|
||||||
memory.setBytes(at, insbytes);
|
|
||||||
AddressSet set = new AddressSet(at, end);
|
|
||||||
|
|
||||||
// Creating this at construction causes it to assess memory flags too early.
|
|
||||||
Disassembler dis = Disassembler.getDisassembler(program, TaskMonitor.DUMMY,
|
|
||||||
DisassemblerMessageListener.IGNORE);
|
|
||||||
dis.disassemble(at, set);
|
|
||||||
return listing.getInstructions(set, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public InstructionIterator assemble(Address at, String... assembly)
|
|
||||||
throws AssemblySyntaxException, AssemblySemanticException, MemoryAccessException,
|
|
||||||
AddressOverflowException {
|
|
||||||
Address start = at;
|
|
||||||
ByteArrayOutputStream buf = new ByteArrayOutputStream();
|
|
||||||
for (String part : assembly) {
|
|
||||||
for (String line : part.split("\n")) {
|
|
||||||
RegisterValue rv = program.getProgramContext().getDisassemblyContext(at);
|
|
||||||
dbg.println(rv);
|
|
||||||
AssemblyPatternBlock ctx = AssemblyPatternBlock.fromRegisterValue(rv);
|
|
||||||
ctx = ctx.fillMask();
|
|
||||||
byte[] insbytes = assembleLine(at, line, ctx);
|
|
||||||
if (insbytes == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
buf.write(insbytes);
|
|
||||||
}
|
|
||||||
catch (IOException e) {
|
|
||||||
throw new AssertionError(e);
|
|
||||||
}
|
|
||||||
at = at.addNoWrap(insbytes.length);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return patchProgram(buf.toByteArray(), start);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public byte[] assembleLine(Address at, String line)
|
|
||||||
throws AssemblySyntaxException, AssemblySemanticException {
|
|
||||||
AssemblyPatternBlock ctx = defaultContext.getDefaultAt(at);
|
|
||||||
ctx = ctx.fillMask();
|
|
||||||
return assembleLine(at, line, ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Collection<AssemblyParseResult> parseLine(String line) {
|
|
||||||
return parser.parse(line, getNumericSymbols());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AssemblyResolutionResults resolveTree(AssemblyParseResult parse, Address at) {
|
|
||||||
AssemblyPatternBlock ctx = getContextAt(at);
|
|
||||||
ctx = ctx.fillMask();
|
|
||||||
return resolveTree(parse, at, ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AssemblyResolutionResults resolveTree(AssemblyParseResult parse, Address at,
|
|
||||||
AssemblyPatternBlock ctx) {
|
AssemblyPatternBlock ctx) {
|
||||||
if (parse.isError()) {
|
return new AssemblyTreeResolver(factory, lang, at, tree, ctx, ctxGraph);
|
||||||
AssemblyResolutionResults results = new AssemblyResolutionResults();
|
|
||||||
AssemblyParseErrorResult err = (AssemblyParseErrorResult) parse;
|
|
||||||
results.add(AssemblyResolution.error(err.describeError(), "Parsing"));
|
|
||||||
return results;
|
|
||||||
}
|
|
||||||
|
|
||||||
AssemblyParseAcceptResult acc = (AssemblyParseAcceptResult) parse;
|
|
||||||
AssemblyTreeResolver tr =
|
|
||||||
new AssemblyTreeResolver(lang, at, acc.getTree(), ctx, ctxGraph);
|
|
||||||
return tr.resolve();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AssemblyResolutionResults resolveLine(Address at, String line)
|
|
||||||
throws AssemblySyntaxException {
|
|
||||||
return resolveLine(at, line, getContextAt(at).fillMask());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AssemblyResolutionResults resolveLine(Address at, String line, AssemblyPatternBlock ctx)
|
|
||||||
throws AssemblySyntaxException {
|
|
||||||
|
|
||||||
if (!ctx.isFullMask()) {
|
|
||||||
throw new AssemblyError(
|
|
||||||
"Context must be fully-specified (full length, no shift, no unknowns)");
|
|
||||||
}
|
|
||||||
if (lang.getContextBaseRegister() != Register.NO_CONTEXT &&
|
|
||||||
ctx.length() < lang.getContextBaseRegister().getMinimumByteSize()) {
|
|
||||||
throw new AssemblyError(
|
|
||||||
"Context must be fully-specified (full length, no shift, no unknowns)");
|
|
||||||
}
|
|
||||||
Collection<AssemblyParseResult> parse = parseLine(line);
|
|
||||||
parse = selector.filterParse(parse);
|
|
||||||
if (!parse.iterator().hasNext()) { // Iterator.isEmpty()???
|
|
||||||
throw new AssemblySelectionError(
|
|
||||||
"Must select at least one parse result. Report errors via AssemblySyntaxError");
|
|
||||||
}
|
|
||||||
AssemblyResolutionResults results = new AssemblyResolutionResults();
|
|
||||||
for (AssemblyParseResult p : parse) {
|
|
||||||
results.absorb(resolveTree(p, at, ctx));
|
|
||||||
}
|
|
||||||
return results;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public byte[] assembleLine(Address at, String line, AssemblyPatternBlock ctx)
|
|
||||||
throws AssemblySemanticException, AssemblySyntaxException {
|
|
||||||
AssemblyResolutionResults results = resolveLine(at, line, ctx);
|
|
||||||
AssemblyResolvedPatterns res = selector.select(results, ctx);
|
|
||||||
if (res == null) {
|
|
||||||
throw new AssemblySelectionError(
|
|
||||||
"Must select exactly one instruction. Report errors via AssemblySemanticError");
|
|
||||||
}
|
|
||||||
if (!res.getInstruction().isFullMask()) {
|
|
||||||
throw new AssemblySelectionError("Selected instruction must have a full mask.");
|
|
||||||
}
|
|
||||||
if (res.getContext().combine(ctx) == null) {
|
|
||||||
throw new AssemblySelectionError("Selected instruction must have compatible context");
|
|
||||||
}
|
|
||||||
return res.getInstruction().getVals();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A convenience to obtain assembly symbols
|
|
||||||
*
|
|
||||||
* @return the map
|
|
||||||
*/
|
|
||||||
protected AssemblyNumericSymbols getNumericSymbols() {
|
|
||||||
synchronized (lock) {
|
|
||||||
if (symbols != null) {
|
|
||||||
return symbols;
|
|
||||||
}
|
|
||||||
if (program == null) {
|
|
||||||
symbols = AssemblyNumericSymbols.fromLanguage(lang);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
symbols = AssemblyNumericSymbols.fromProgram(program);
|
|
||||||
}
|
|
||||||
return symbols;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AssemblyPatternBlock getContextAt(Address addr) {
|
|
||||||
if (program != null) {
|
|
||||||
RegisterValue rv = program.getProgramContext().getDisassemblyContext(addr);
|
|
||||||
return AssemblyPatternBlock.fromRegisterValue(rv);
|
|
||||||
}
|
|
||||||
return defaultContext.getDefaultAt(addr);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+17
-333
@@ -15,29 +15,11 @@
|
|||||||
*/
|
*/
|
||||||
package ghidra.app.plugin.assembler.sleigh;
|
package ghidra.app.plugin.assembler.sleigh;
|
||||||
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
import org.apache.commons.collections4.MultiValuedMap;
|
|
||||||
import org.apache.commons.collections4.multimap.HashSetValuedHashMap;
|
|
||||||
|
|
||||||
import ghidra.app.plugin.assembler.*;
|
import ghidra.app.plugin.assembler.*;
|
||||||
import ghidra.app.plugin.assembler.sleigh.grammars.AssemblyGrammar;
|
|
||||||
import ghidra.app.plugin.assembler.sleigh.grammars.AssemblySentential;
|
|
||||||
import ghidra.app.plugin.assembler.sleigh.parse.AssemblyParser;
|
import ghidra.app.plugin.assembler.sleigh.parse.AssemblyParser;
|
||||||
import ghidra.app.plugin.assembler.sleigh.sem.*;
|
import ghidra.app.plugin.assembler.sleigh.sem.*;
|
||||||
import ghidra.app.plugin.assembler.sleigh.symbol.*;
|
import ghidra.app.plugin.processors.sleigh.SleighLanguage;
|
||||||
import ghidra.app.plugin.assembler.sleigh.util.DbgTimer;
|
|
||||||
import ghidra.app.plugin.assembler.sleigh.util.DbgTimer.DbgCtx;
|
|
||||||
import ghidra.app.plugin.languages.sleigh.SleighLanguages;
|
|
||||||
import ghidra.app.plugin.languages.sleigh.SubtableEntryVisitor;
|
|
||||||
import ghidra.app.plugin.processors.sleigh.*;
|
|
||||||
import ghidra.app.plugin.processors.sleigh.pattern.DisjointPattern;
|
|
||||||
import ghidra.app.plugin.processors.sleigh.symbol.*;
|
|
||||||
import ghidra.app.plugin.processors.sleigh.template.ConstructTpl;
|
|
||||||
import ghidra.app.plugin.processors.sleigh.template.HandleTpl;
|
|
||||||
import ghidra.program.model.lang.LanguageID;
|
|
||||||
import ghidra.program.model.listing.Program;
|
import ghidra.program.model.listing.Program;
|
||||||
import ghidra.util.SystemUtilities;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An {@link AssemblerBuilder} capable of supporting almost any {@link SleighLanguage}
|
* An {@link AssemblerBuilder} capable of supporting almost any {@link SleighLanguage}
|
||||||
@@ -300,20 +282,9 @@ import ghidra.util.SystemUtilities;
|
|||||||
* implementation includes optional diagnostics, but even then, decoding them takes some familiarity
|
* implementation includes optional diagnostics, but even then, decoding them takes some familiarity
|
||||||
* and expertise.
|
* and expertise.
|
||||||
*/
|
*/
|
||||||
public class SleighAssemblerBuilder implements AssemblerBuilder {
|
public class SleighAssemblerBuilder
|
||||||
protected static final DbgTimer dbg =
|
extends AbstractSleighAssemblerBuilder<AssemblyResolvedPatterns, Assembler>
|
||||||
SystemUtilities.isInTestingBatchMode() ? DbgTimer.INACTIVE : DbgTimer.ACTIVE;
|
implements AssemblerBuilder {
|
||||||
|
|
||||||
protected SleighLanguage lang;
|
|
||||||
protected AssemblyGrammar grammar;
|
|
||||||
protected AssemblyDefaultContext defaultContext;
|
|
||||||
protected AssemblyContextGraph ctxGraph;
|
|
||||||
protected AssemblyParser parser;
|
|
||||||
|
|
||||||
protected boolean generated = false;
|
|
||||||
|
|
||||||
// A cache for symbols converted during grammar construction
|
|
||||||
protected Map<String, AssemblySymbol> builtSymbols = new HashMap<>();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct an assembler builder for the given SLEIGH language
|
* Construct an assembler builder for the given SLEIGH language
|
||||||
@@ -321,319 +292,32 @@ public class SleighAssemblerBuilder implements AssemblerBuilder {
|
|||||||
* @param lang the language
|
* @param lang the language
|
||||||
*/
|
*/
|
||||||
public SleighAssemblerBuilder(SleighLanguage lang) {
|
public SleighAssemblerBuilder(SleighLanguage lang) {
|
||||||
this.lang = lang;
|
super(lang);
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Do the actual work to construct an assembler from a SLEIGH language
|
|
||||||
*
|
|
||||||
* @throws SleighException if there's an issue accessing the language
|
|
||||||
*/
|
|
||||||
protected void generateAssembler() throws SleighException {
|
|
||||||
if (generated) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
generated = true;
|
|
||||||
try {
|
|
||||||
buildGrammar();
|
|
||||||
grammar.verify();
|
|
||||||
buildContext();
|
|
||||||
buildContextGraph();
|
|
||||||
buildParser();
|
|
||||||
}
|
|
||||||
catch (SleighException e) {
|
|
||||||
// Not sure this can actually happen here
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
catch (Exception e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public LanguageID getLanguageID() {
|
protected AbstractAssemblyResolutionFactory< //
|
||||||
return lang.getLanguageID();
|
AssemblyResolvedPatterns, AssemblyResolvedBackfill> newResolutionFactory() {
|
||||||
|
return new DefaultAssemblyResolutionFactory();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SleighLanguage getLanguage() {
|
protected SleighAssembler newAssembler(AssemblySelector selector) {
|
||||||
return lang;
|
return new SleighAssembler(factory, selector, lang, parser, defaultContext, ctxGraph);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected SleighAssembler newAssembler(AssemblySelector selector, Program program) {
|
||||||
|
return new SleighAssembler(factory, selector, program, parser, defaultContext, ctxGraph);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SleighAssembler getAssembler(AssemblySelector selector) {
|
public SleighAssembler getAssembler(AssemblySelector selector) {
|
||||||
generateAssembler();
|
return (SleighAssembler) super.getAssembler(selector);
|
||||||
SleighAssembler asm = new SleighAssembler(selector, lang, parser, defaultContext, ctxGraph);
|
|
||||||
return asm;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SleighAssembler getAssembler(AssemblySelector selector, Program program) {
|
public SleighAssembler getAssembler(AssemblySelector selector, Program program) {
|
||||||
generateAssembler();
|
return (SleighAssembler) super.getAssembler(selector, program);
|
||||||
return new SleighAssembler(selector, program, parser, defaultContext, ctxGraph);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Invert a varnode list to a map suitable for use with {@link AssemblyStringMapTerminal}
|
|
||||||
*
|
|
||||||
* @param vnlist the varnode list symbol
|
|
||||||
* @return the inverted string map
|
|
||||||
*/
|
|
||||||
protected MultiValuedMap<String, Integer> invVarnodeList(VarnodeListSymbol vnlist) {
|
|
||||||
MultiValuedMap<String, Integer> result = new HashSetValuedHashMap<>();
|
|
||||||
int index = -1;
|
|
||||||
for (VarnodeSymbol vnsym : vnlist.getVarnodeTable()) {
|
|
||||||
index++;
|
|
||||||
if (vnsym != null) {
|
|
||||||
// nulls are _ in the spec, meaning the index is undefined.
|
|
||||||
result.put(vnsym.getName(), index);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Invert a value map to a map suitable for use with {@link AssemblyNumericMapTerminal}
|
|
||||||
*
|
|
||||||
* @param vm the value map symbol
|
|
||||||
* @return the inverted numeric map
|
|
||||||
*/
|
|
||||||
protected Map<Long, Integer> invValueMap(ValueMapSymbol vm) {
|
|
||||||
Map<Long, Integer> result = new HashMap<>();
|
|
||||||
List<Long> map = vm.getMap();
|
|
||||||
for (int i = 0; i < map.size(); i++) {
|
|
||||||
long v = map.get(i);
|
|
||||||
result.put(v, i);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Invert a name table to a map suitable for use with {@link AssemblyStringMapTerminal}
|
|
||||||
*
|
|
||||||
* @param ns the name symbol
|
|
||||||
* @return the inverted string map
|
|
||||||
*/
|
|
||||||
protected MultiValuedMap<String, Integer> invNameSymbol(NameSymbol ns) {
|
|
||||||
MultiValuedMap<String, Integer> result = new HashSetValuedHashMap<>();
|
|
||||||
int index = -1;
|
|
||||||
for (String name : ns.getNameTable()) {
|
|
||||||
index++;
|
|
||||||
if (name != null) {
|
|
||||||
result.put(name, index);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert the given operand symbol to an {@link AssemblySymbol}
|
|
||||||
*
|
|
||||||
* <p>
|
|
||||||
* For subtables, this results in a non-terminal, for all others, the result in a terminal.
|
|
||||||
*
|
|
||||||
* @param cons the constructor to which the operand belongs
|
|
||||||
* @param opsym the operand symbol to convert
|
|
||||||
* @return the converted assembly grammar symbol
|
|
||||||
*/
|
|
||||||
protected AssemblySymbol getSymbolFor(Constructor cons, OperandSymbol opsym) {
|
|
||||||
TripleSymbol defsym = opsym.getDefiningSymbol();
|
|
||||||
// If the symbol has no defining symbol, that means the name is only valid in the local
|
|
||||||
// scope. We must keep them unique.
|
|
||||||
String name;
|
|
||||||
if (defsym == null) {
|
|
||||||
name = cons.getParent().getName() + ":" + opsym.getName();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
name = opsym.getName();
|
|
||||||
}
|
|
||||||
AssemblySymbol built = builtSymbols.get(name);
|
|
||||||
if (built != null) {
|
|
||||||
return built;
|
|
||||||
}
|
|
||||||
if (defsym == null) {
|
|
||||||
HandleTpl htpl = getHandleTpl(cons, opsym);
|
|
||||||
built = htpl == null ? new AssemblyNumericTerminal(name, 0, null)
|
|
||||||
: new AssemblyNumericTerminal(name, htpl.getSize(), htpl.getAddressSpace());
|
|
||||||
}
|
|
||||||
else if (defsym instanceof SubtableSymbol) {
|
|
||||||
built = new AssemblyNonTerminal(name);
|
|
||||||
}
|
|
||||||
else if (defsym instanceof VarnodeListSymbol) {
|
|
||||||
built = new AssemblyStringMapTerminal(name, invVarnodeList((VarnodeListSymbol) defsym));
|
|
||||||
}
|
|
||||||
else if (defsym instanceof VarnodeSymbol) {
|
|
||||||
built = new AssemblyStringTerminal(name);
|
|
||||||
// Does this need to consume an operand? It seems not.
|
|
||||||
}
|
|
||||||
else if (defsym instanceof ValueMapSymbol) {
|
|
||||||
built = new AssemblyNumericMapTerminal(name, invValueMap((ValueMapSymbol) defsym));
|
|
||||||
}
|
|
||||||
else if (defsym instanceof NameSymbol) {
|
|
||||||
built = new AssemblyStringMapTerminal(name, invNameSymbol((NameSymbol) defsym));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw new RuntimeException("Unknown symbol for " + name + ": " + defsym);
|
|
||||||
}
|
|
||||||
builtSymbols.put(name, built);
|
|
||||||
return built;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Obtain the p-code result handle for the given operand
|
|
||||||
*
|
|
||||||
* <p>
|
|
||||||
* This handles a special case, where a constructor prints just one operand and exports that
|
|
||||||
* same operand, often with an explicit size, or as an address in a given space. In such cases,
|
|
||||||
* the listing displays that operand according to that exported size.
|
|
||||||
*
|
|
||||||
* <p>
|
|
||||||
* For assembly, this gives a few opportunities: 1) We can/must ensure the specified value fits,
|
|
||||||
* by checking the size. 2) We can/must mask the goal when solving the defining pattern
|
|
||||||
* expression for the operand. 3)) We can/must check that a label's address space matches that
|
|
||||||
* represented by the operand, when used for a numeric terminal.
|
|
||||||
*
|
|
||||||
* @param cons the constructor from which the production is being derived
|
|
||||||
* @param opsym the operand symbol corresponding to the grammatical symbol, whose size we wish
|
|
||||||
* to determine.
|
|
||||||
* @return the size of the operand in bits
|
|
||||||
*/
|
|
||||||
protected HandleTpl getHandleTpl(Constructor cons, OperandSymbol opsym) {
|
|
||||||
ConstructTpl ctpl = cons.getTempl();
|
|
||||||
if (null == ctpl) {
|
|
||||||
// No pcode, no size specification
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
HandleTpl htpl = ctpl.getResult();
|
|
||||||
if (null == htpl) {
|
|
||||||
// If nothing is exported, the size is unspecified
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (opsym.getIndex() != htpl.getOffsetOperandIndex()) {
|
|
||||||
// If the export is not of the same operand, it does not specify its size
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return htpl;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Build a portion of the grammar representing a table of constructors
|
|
||||||
*
|
|
||||||
* @param subtable the table
|
|
||||||
* @return the partial grammar
|
|
||||||
*/
|
|
||||||
protected AssemblyGrammar buildSubGrammar(SubtableSymbol subtable) {
|
|
||||||
final AssemblyGrammar subgrammar = new AssemblyGrammar();
|
|
||||||
final AssemblyNonTerminal lhs = new AssemblyNonTerminal(subtable.getName());
|
|
||||||
SleighLanguages.traverseConstructors(subtable, new SubtableEntryVisitor() {
|
|
||||||
@Override
|
|
||||||
public int visit(DisjointPattern pattern, Constructor cons) {
|
|
||||||
AssemblySentential<AssemblyNonTerminal> rhs = new AssemblySentential<>();
|
|
||||||
List<Integer> indices = new ArrayList<>();
|
|
||||||
for (String str : cons.getPrintPieces()) {
|
|
||||||
if (str.length() != 0) {
|
|
||||||
if (str.charAt(0) == '\n') {
|
|
||||||
int index = str.charAt(1) - 'A';
|
|
||||||
OperandSymbol opsym = cons.getOperand(index);
|
|
||||||
AssemblySymbol sym = getSymbolFor(cons, opsym);
|
|
||||||
if (sym.takesOperandIndex()) {
|
|
||||||
indices.add(index);
|
|
||||||
}
|
|
||||||
rhs.addSymbol(sym);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
rhs.addSeparators(str);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
subgrammar.addProduction(lhs, rhs, pattern, cons, indices);
|
|
||||||
return CONTINUE;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return subgrammar;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Build the full grammar for the language
|
|
||||||
*/
|
|
||||||
protected void buildGrammar() {
|
|
||||||
try (DbgCtx dc = dbg.start("Building grammar")) {
|
|
||||||
grammar = new AssemblyGrammar();
|
|
||||||
for (Symbol sym : lang.getSymbolTable().getSymbolList()) {
|
|
||||||
if (sym instanceof SubtableSymbol) {
|
|
||||||
SubtableSymbol subtable = (SubtableSymbol) sym;
|
|
||||||
grammar.combine(buildSubGrammar(subtable));
|
|
||||||
}
|
|
||||||
else if (sym instanceof VarnodeSymbol) {
|
|
||||||
// Ignore. This just becomes a string terminal
|
|
||||||
}
|
|
||||||
else if (sym instanceof StartSymbol) {
|
|
||||||
// Ignore. We handle inst_start in semantic processing
|
|
||||||
}
|
|
||||||
else if (sym instanceof EndSymbol) {
|
|
||||||
// Ignore. We handle inst_next in semantic processing
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (sym instanceof Next2Symbol) {
|
|
||||||
// Ignore. We handle inst_next2 in semantic processing
|
|
||||||
}
|
|
||||||
else if (sym instanceof UseropSymbol) {
|
|
||||||
// Ignore. We don't do pcode.
|
|
||||||
}
|
|
||||||
else if (sym instanceof OperandSymbol) {
|
|
||||||
// Ignore. These are terminals, or will be produced by their defining symbols
|
|
||||||
}
|
|
||||||
else if (sym instanceof ValueSymbol) {
|
|
||||||
// Ignore. These are now terminals
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw new RuntimeException("Unexpected type: " + sym.getClass());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
grammar.setStartName("instruction");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Build the default context for the language
|
|
||||||
*/
|
|
||||||
protected void buildContext() {
|
|
||||||
defaultContext = new AssemblyDefaultContext(lang);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Build the context transition graph for the language
|
|
||||||
*/
|
|
||||||
protected void buildContextGraph() {
|
|
||||||
try (DbgCtx dc = dbg.start("Building context graph")) {
|
|
||||||
ctxGraph = new AssemblyContextGraph(lang, grammar);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Build the parser for the language
|
|
||||||
*/
|
|
||||||
protected void buildParser() {
|
|
||||||
try (DbgCtx dc = dbg.start("Building parser")) {
|
|
||||||
parser = new AssemblyParser(grammar);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the built grammar for the language
|
|
||||||
*
|
|
||||||
* @return the grammar
|
|
||||||
*/
|
|
||||||
protected AssemblyGrammar getGrammar() {
|
|
||||||
return grammar;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the built parser for the language
|
|
||||||
*
|
|
||||||
* @return the parser
|
|
||||||
*/
|
|
||||||
protected AssemblyParser getParser() {
|
|
||||||
return parser;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+27
-25
@@ -18,8 +18,7 @@ package ghidra.app.plugin.assembler.sleigh.expr;
|
|||||||
import java.util.Map;
|
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.*;
|
||||||
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;
|
||||||
|
|
||||||
@@ -36,9 +35,9 @@ public abstract class AbstractBinaryExpressionSolver<T extends BinaryExpression>
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AssemblyResolution solve(T exp, MaskedLong goal, Map<String, Long> vals,
|
public AssemblyResolution solve(AbstractAssemblyResolutionFactory<?, ?> factory,
|
||||||
AssemblyResolvedPatterns cur, Set<SolverHint> hints, String description)
|
T exp, MaskedLong goal, Map<String, Long> vals, AssemblyResolvedPatterns cur,
|
||||||
throws NeedsBackfillException {
|
Set<SolverHint> hints, String description) throws NeedsBackfillException {
|
||||||
MaskedLong lval = solver.getValue(exp.getLeft(), vals, cur);
|
MaskedLong lval = solver.getValue(exp.getLeft(), vals, cur);
|
||||||
MaskedLong rval = solver.getValue(exp.getRight(), vals, cur);
|
MaskedLong rval = solver.getValue(exp.getRight(), vals, cur);
|
||||||
|
|
||||||
@@ -58,26 +57,27 @@ public abstract class AbstractBinaryExpressionSolver<T extends BinaryExpression>
|
|||||||
try {
|
try {
|
||||||
if (lval != null && rval != null) {
|
if (lval != null && rval != null) {
|
||||||
MaskedLong cval = compute(lval, rval);
|
MaskedLong cval = compute(lval, rval);
|
||||||
return ConstantValueSolver.checkConstAgrees(cval, goal, description);
|
return ConstantValueSolver.checkConstAgrees(factory, cval, goal, description);
|
||||||
}
|
}
|
||||||
else if (lval != null) {
|
else if (lval != null) {
|
||||||
return solveRightSide(exp.getRight(), lval, goal, vals, cur, hints,
|
return solveRightSide(factory, exp.getRight(), lval, goal, vals, cur, hints,
|
||||||
description);
|
description);
|
||||||
}
|
}
|
||||||
else if (rval != null) {
|
else if (rval != null) {
|
||||||
return solveLeftSide(exp.getLeft(), rval, goal, vals, cur, hints, description);
|
return solveLeftSide(factory, 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, cur, hints, description);
|
return solveTwoSided(factory, 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);
|
return factory.newErrorBuilder().error(e.getMessage()).description(description).build();
|
||||||
}
|
}
|
||||||
catch (AssertionError e) {
|
catch (AssertionError e) {
|
||||||
dbg.println("While solving: " + exp + " (" + description + ")");
|
dbg.println("While solving: " + exp + " (" + description + ")");
|
||||||
@@ -85,23 +85,25 @@ public abstract class AbstractBinaryExpressionSolver<T extends BinaryExpression>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected AssemblyResolution solveLeftSide(PatternExpression lexp, MaskedLong rval,
|
protected AssemblyResolution solveLeftSide(AbstractAssemblyResolutionFactory<?, ?> factory,
|
||||||
MaskedLong goal, Map<String, Long> vals, AssemblyResolvedPatterns cur,
|
PatternExpression lexp, MaskedLong rval, MaskedLong goal, Map<String, Long> vals,
|
||||||
Set<SolverHint> hints, String description)
|
|
||||||
throws NeedsBackfillException, SolverException {
|
|
||||||
return solver.solve(lexp, computeLeft(rval, goal), vals, cur, hints, description);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected AssemblyResolution solveRightSide(PatternExpression rexp, MaskedLong lval,
|
|
||||||
MaskedLong goal, Map<String, Long> vals, AssemblyResolvedPatterns cur,
|
|
||||||
Set<SolverHint> hints, String description)
|
|
||||||
throws NeedsBackfillException, SolverException {
|
|
||||||
return solver.solve(rexp, computeRight(lval, goal), vals, cur, hints, description);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected AssemblyResolution solveTwoSided(T exp, MaskedLong goal, Map<String, Long> vals,
|
|
||||||
AssemblyResolvedPatterns cur, Set<SolverHint> hints, String description)
|
AssemblyResolvedPatterns cur, Set<SolverHint> hints, String description)
|
||||||
throws NeedsBackfillException, SolverException {
|
throws NeedsBackfillException, SolverException {
|
||||||
|
return solver.solve(factory, lexp, computeLeft(rval, goal), vals, cur, hints, description);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected AssemblyResolution solveRightSide(AbstractAssemblyResolutionFactory<?, ?> factory,
|
||||||
|
PatternExpression rexp, MaskedLong lval, MaskedLong goal, Map<String, Long> vals,
|
||||||
|
AssemblyResolvedPatterns cur, Set<SolverHint> hints, String description)
|
||||||
|
throws NeedsBackfillException, SolverException {
|
||||||
|
return solver.solve(factory, rexp, computeRight(lval, goal), vals, cur, hints,
|
||||||
|
description);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected AssemblyResolution solveTwoSided(AbstractAssemblyResolutionFactory<?, ?> factory,
|
||||||
|
T exp, MaskedLong goal, Map<String, Long> vals, AssemblyResolvedPatterns cur,
|
||||||
|
Set<SolverHint> hints, String description)
|
||||||
|
throws NeedsBackfillException, SolverException {
|
||||||
throw new NeedsBackfillException("_two_sided_");
|
throw new NeedsBackfillException("_two_sided_");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+4
-5
@@ -18,8 +18,7 @@ package ghidra.app.plugin.assembler.sleigh.expr;
|
|||||||
import java.util.Map;
|
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.*;
|
||||||
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;
|
||||||
|
|
||||||
@@ -54,9 +53,9 @@ public abstract class AbstractExpressionSolver<T extends PatternExpression> {
|
|||||||
* @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(AbstractAssemblyResolutionFactory<?, ?> factory,
|
||||||
AssemblyResolvedPatterns cur, Set<SolverHint> hints,
|
T exp, MaskedLong goal, Map<String, Long> vals, AssemblyResolvedPatterns cur,
|
||||||
String description) throws NeedsBackfillException;
|
Set<SolverHint> hints, String description) throws NeedsBackfillException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attempt to get a constant value for the expression
|
* Attempt to get a constant value for the expression
|
||||||
|
|||||||
+6
-7
@@ -18,8 +18,7 @@ package ghidra.app.plugin.assembler.sleigh.expr;
|
|||||||
import java.util.Map;
|
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.*;
|
||||||
import ghidra.app.plugin.assembler.sleigh.sem.AssemblyResolvedPatterns;
|
|
||||||
import ghidra.app.plugin.processors.sleigh.expression.UnaryExpression;
|
import ghidra.app.plugin.processors.sleigh.expression.UnaryExpression;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -35,18 +34,18 @@ public abstract class AbstractUnaryExpressionSolver<T extends UnaryExpression>
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AssemblyResolution solve(T exp, MaskedLong goal, Map<String, Long> vals,
|
public AssemblyResolution solve(AbstractAssemblyResolutionFactory<?, ?> factory, T exp,
|
||||||
AssemblyResolvedPatterns cur, Set<SolverHint> hints, String description)
|
MaskedLong goal, Map<String, Long> vals, AssemblyResolvedPatterns cur,
|
||||||
throws NeedsBackfillException {
|
Set<SolverHint> hints, String description) throws NeedsBackfillException {
|
||||||
MaskedLong uval = solver.getValue(exp.getUnary(), vals, 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);
|
||||||
if (cval != null) {
|
if (cval != null) {
|
||||||
return ConstantValueSolver.checkConstAgrees(cval, goal, description);
|
return ConstantValueSolver.checkConstAgrees(factory, cval, goal, description);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return solver.solve(exp.getUnary(), computeInverse(goal), vals, cur, hints,
|
return solver.solve(factory, exp.getUnary(), computeInverse(goal), vals, cur, hints,
|
||||||
description);
|
description);
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
|
|||||||
+12
-10
@@ -18,8 +18,7 @@ package ghidra.app.plugin.assembler.sleigh.expr;
|
|||||||
import java.util.Map;
|
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.*;
|
||||||
import ghidra.app.plugin.assembler.sleigh.sem.AssemblyResolvedPatterns;
|
|
||||||
import ghidra.app.plugin.processors.sleigh.expression.ConstantValue;
|
import ghidra.app.plugin.processors.sleigh.expression.ConstantValue;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -36,11 +35,11 @@ public class ConstantValueSolver extends AbstractExpressionSolver<ConstantValue>
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AssemblyResolution solve(ConstantValue cv, MaskedLong goal, Map<String, Long> vals,
|
public AssemblyResolution solve(AbstractAssemblyResolutionFactory<?, ?> factory,
|
||||||
AssemblyResolvedPatterns cur, Set<SolverHint> hints,
|
ConstantValue cv, MaskedLong goal, Map<String, Long> vals, AssemblyResolvedPatterns cur,
|
||||||
String description) {
|
Set<SolverHint> hints, String description) {
|
||||||
MaskedLong value = getValue(cv, vals, cur);
|
MaskedLong value = getValue(cv, vals, cur);
|
||||||
return checkConstAgrees(value, goal, description);
|
return checkConstAgrees(factory, value, goal, description);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -60,12 +59,15 @@ public class ConstantValueSolver extends AbstractExpressionSolver<ConstantValue>
|
|||||||
return MaskedLong.fromLong(cv.getValue());
|
return MaskedLong.fromLong(cv.getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
static AssemblyResolution checkConstAgrees(MaskedLong value, MaskedLong goal,
|
static AssemblyResolution checkConstAgrees(
|
||||||
|
AbstractAssemblyResolutionFactory<?, ?> factory, MaskedLong value, MaskedLong goal,
|
||||||
String description) {
|
String description) {
|
||||||
if (!value.agrees(goal)) {
|
if (!value.agrees(goal)) {
|
||||||
return AssemblyResolution.error(
|
return factory.newErrorBuilder()
|
||||||
"Constant value " + value + " does not agree with child requirements", description);
|
.error("Constant value " + value + " does not agree with child requirements")
|
||||||
|
.description(description)
|
||||||
|
.build();
|
||||||
}
|
}
|
||||||
return AssemblyResolution.nop(description, null, null);
|
return factory.nop(description);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+8
-5
@@ -35,15 +35,18 @@ public class ContextFieldSolver extends AbstractExpressionSolver<ContextField> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AssemblyResolution solve(ContextField cf, MaskedLong goal, Map<String, Long> vals,
|
public AssemblyResolution solve(AbstractAssemblyResolutionFactory<?, ?> factory,
|
||||||
AssemblyResolvedPatterns cur, Set<SolverHint> hints, String description) {
|
ContextField cf, MaskedLong goal, Map<String, Long> vals, AssemblyResolvedPatterns cur,
|
||||||
|
Set<SolverHint> hints, 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 factory.newErrorBuilder()
|
||||||
description);
|
.error("Value " + goal + " is not valid for " + cf)
|
||||||
|
.description(description)
|
||||||
|
.build();
|
||||||
}
|
}
|
||||||
AssemblyPatternBlock block = AssemblyPatternBlock.fromContextField(cf, goal);
|
AssemblyPatternBlock block = AssemblyPatternBlock.fromContextField(cf, goal);
|
||||||
return AssemblyResolution.contextOnly(block, description);
|
return factory.contextOnly(block, description);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
+10
-8
@@ -39,12 +39,14 @@ public class EndInstructionValueSolver extends AbstractExpressionSolver<EndInstr
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AssemblyResolution solve(EndInstructionValue iv, MaskedLong goal, Map<String, Long> vals,
|
public AssemblyResolution solve(AbstractAssemblyResolutionFactory<?, ?> factory,
|
||||||
AssemblyResolvedPatterns cur, Set<SolverHint> hints, String description) {
|
EndInstructionValue exp, MaskedLong goal, Map<String, Long> vals,
|
||||||
throw new AssertionError(
|
AssemblyResolvedPatterns cur, Set<SolverHint> hints, String description)
|
||||||
"INTERNAL: Should never be asked to solve for " + AssemblyTreeResolver.INST_NEXT);
|
throws NeedsBackfillException {
|
||||||
|
throw new AssertionError("INTERNAL: Should never be asked to solve for " +
|
||||||
|
AbstractAssemblyTreeResolver.INST_NEXT);
|
||||||
/**
|
/**
|
||||||
* I suppose we could instead throw NeedsBackfillExcpeiton(INST_NEXT) here, too, but this
|
* I suppose we could instead throw NeedsBackfillException(INST_NEXT) here, too, but this
|
||||||
* serves as a sanity check on the SLEIGH spec. I can't think of a good reason to try to
|
* serves as a sanity check on the SLEIGH spec. I can't think of a good reason to try to
|
||||||
* solve INST_NEXT == const.
|
* solve INST_NEXT == const.
|
||||||
*/
|
*/
|
||||||
@@ -53,9 +55,9 @@ public class EndInstructionValueSolver extends AbstractExpressionSolver<EndInstr
|
|||||||
@Override
|
@Override
|
||||||
public MaskedLong getValue(EndInstructionValue iv, Map<String, Long> vals,
|
public MaskedLong getValue(EndInstructionValue iv, Map<String, Long> vals,
|
||||||
AssemblyResolvedPatterns cur) throws NeedsBackfillException {
|
AssemblyResolvedPatterns cur) throws NeedsBackfillException {
|
||||||
Long instNext = vals.get(AssemblyTreeResolver.INST_NEXT);
|
Long instNext = vals.get(AbstractAssemblyTreeResolver.INST_NEXT);
|
||||||
if (instNext == null) {
|
if (instNext == null) {
|
||||||
throw new NeedsBackfillException(AssemblyTreeResolver.INST_NEXT);
|
throw new NeedsBackfillException(AbstractAssemblyTreeResolver.INST_NEXT);
|
||||||
}
|
}
|
||||||
return MaskedLong.fromLong(instNext);
|
return MaskedLong.fromLong(instNext);
|
||||||
}
|
}
|
||||||
@@ -68,7 +70,7 @@ public class EndInstructionValueSolver extends AbstractExpressionSolver<EndInstr
|
|||||||
@Override
|
@Override
|
||||||
public MaskedLong valueForResolution(EndInstructionValue exp, Map<String, Long> vals,
|
public MaskedLong valueForResolution(EndInstructionValue exp, Map<String, Long> vals,
|
||||||
AssemblyResolvedPatterns rc) {
|
AssemblyResolvedPatterns rc) {
|
||||||
Long instNext = vals.get(AssemblyTreeResolver.INST_NEXT);
|
Long instNext = vals.get(AbstractAssemblyTreeResolver.INST_NEXT);
|
||||||
if (instNext == null) {
|
if (instNext == null) {
|
||||||
/**
|
/**
|
||||||
* This method is used in forward state construction, so just leave unknown. This may
|
* This method is used in forward state construction, so just leave unknown. This may
|
||||||
|
|||||||
+12
-12
@@ -18,8 +18,7 @@ package ghidra.app.plugin.assembler.sleigh.expr;
|
|||||||
import java.util.Map;
|
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.*;
|
||||||
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;
|
||||||
|
|
||||||
@@ -60,13 +59,14 @@ public class LeftShiftExpressionSolver extends AbstractBinaryExpressionSolver<Le
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected AssemblyResolution solveTwoSided(LeftShiftExpression exp, MaskedLong goal,
|
protected AssemblyResolution solveTwoSided(AbstractAssemblyResolutionFactory<?, ?> factory,
|
||||||
Map<String, Long> vals, AssemblyResolvedPatterns cur, Set<SolverHint> hints,
|
LeftShiftExpression exp, MaskedLong goal, Map<String, Long> vals,
|
||||||
String description) throws NeedsBackfillException, SolverException {
|
AssemblyResolvedPatterns cur, Set<SolverHint> hints, String description)
|
||||||
|
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, cur, hints, description);
|
return super.solveTwoSided(factory, 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
|
||||||
@@ -79,8 +79,8 @@ public class LeftShiftExpressionSolver extends AbstractBinaryExpressionSolver<Le
|
|||||||
// If the goal is 0s, then any shift will do, so long as the shifted value is 0
|
// If the goal is 0s, then any shift will do, so long as the shifted value is 0
|
||||||
try {
|
try {
|
||||||
// NB. goal is already 0s, so just use it as subgoal for lhs of shift
|
// NB. goal is already 0s, so just use it as subgoal for lhs of shift
|
||||||
AssemblyResolution lres =
|
AssemblyResolution lres = solver.solve(factory, exp.getLeft(), goal, vals, cur,
|
||||||
solver.solve(exp.getLeft(), goal, vals, cur, hintsWithLShift, description);
|
hintsWithLShift, description);
|
||||||
if (lres.isError()) {
|
if (lres.isError()) {
|
||||||
throw new SolverException("Solving left:=0 failed");
|
throw new SolverException("Solving left:=0 failed");
|
||||||
}
|
}
|
||||||
@@ -97,13 +97,13 @@ public class LeftShiftExpressionSolver extends AbstractBinaryExpressionSolver<Le
|
|||||||
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(factory, exp.getLeft(), reql, vals, cur,
|
||||||
solver.solve(exp.getLeft(), reql, vals, cur, hintsWithLShift, description);
|
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, cur, hints, description);
|
solver.solve(factory, 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");
|
||||||
}
|
}
|
||||||
@@ -121,6 +121,6 @@ public class LeftShiftExpressionSolver extends AbstractBinaryExpressionSolver<Le
|
|||||||
// try the next
|
// try the next
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return super.solveTwoSided(exp, goal, vals, cur, hints, description);
|
return super.solveTwoSided(factory, exp, goal, vals, cur, hints, description);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+18
-17
@@ -18,8 +18,7 @@ package ghidra.app.plugin.assembler.sleigh.expr;
|
|||||||
import java.util.Map;
|
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.*;
|
||||||
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;
|
||||||
|
|
||||||
@@ -102,25 +101,26 @@ public class MultExpressionSolver extends AbstractBinaryExpressionSolver<MultExp
|
|||||||
super(MultExpression.class);
|
super(MultExpression.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected AssemblyResolution tryRep(PatternExpression lexp, MaskedLong rval, MaskedLong repGoal,
|
protected AssemblyResolution tryRep(AbstractAssemblyResolutionFactory<?, ?> factory,
|
||||||
MaskedLong goal, Map<String, Long> vals, AssemblyResolvedPatterns cur,
|
PatternExpression lexp, MaskedLong rval, MaskedLong repGoal, MaskedLong goal,
|
||||||
Set<SolverHint> hints, String description) throws NeedsBackfillException {
|
Map<String, Long> vals, AssemblyResolvedPatterns cur, Set<SolverHint> hints,
|
||||||
|
String description) 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, cur, hints, description);
|
return solver.solve(factory, lexp, lval, vals, cur, hints, description);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected AssemblyResolution solveLeftSide(PatternExpression lexp, MaskedLong rval,
|
protected AssemblyResolution solveLeftSide(AbstractAssemblyResolutionFactory<?, ?> factory,
|
||||||
MaskedLong goal, Map<String, Long> vals, AssemblyResolvedPatterns cur,
|
PatternExpression lexp, MaskedLong rval, MaskedLong goal, Map<String, Long> vals,
|
||||||
Set<SolverHint> hints, String description)
|
AssemblyResolvedPatterns cur, 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, cur, hints, description);
|
return super.solveLeftSide(factory, lexp, rval, goal, vals, cur, hints, description);
|
||||||
});
|
});
|
||||||
if (sol != null) {
|
if (sol != null) {
|
||||||
return sol;
|
return sol;
|
||||||
@@ -150,7 +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, cur, hintsWithRepetition,
|
return tryRep(factory, lexp, rval, repRightGoal, goal, vals, cur,
|
||||||
|
hintsWithRepetition,
|
||||||
description);
|
description);
|
||||||
});
|
});
|
||||||
if (sol != null) {
|
if (sol != null) {
|
||||||
@@ -168,8 +169,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, cur, hintsWithRepetition,
|
return tryRep(factory, lexp, rval, repLeftGoal, goal, vals, cur,
|
||||||
description);
|
hintsWithRepetition, description);
|
||||||
});
|
});
|
||||||
if (sol != null) {
|
if (sol != null) {
|
||||||
return sol;
|
return sol;
|
||||||
@@ -180,11 +181,11 @@ public class MultExpressionSolver extends AbstractBinaryExpressionSolver<MultExp
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected AssemblyResolution solveRightSide(PatternExpression rexp, MaskedLong lval,
|
protected AssemblyResolution solveRightSide(AbstractAssemblyResolutionFactory<?, ?> factory,
|
||||||
MaskedLong goal, Map<String, Long> vals, AssemblyResolvedPatterns cur,
|
PatternExpression rexp, MaskedLong lval, MaskedLong goal, Map<String, Long> vals,
|
||||||
Set<SolverHint> hints, String description)
|
AssemblyResolvedPatterns cur, Set<SolverHint> hints, String description)
|
||||||
throws NeedsBackfillException, SolverException {
|
throws NeedsBackfillException, SolverException {
|
||||||
return solveLeftSide(rexp, lval, goal, vals, cur, hints, description);
|
return solveLeftSide(factory, rexp, lval, goal, vals, cur, hints, description);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
+4
-3
@@ -39,9 +39,10 @@ public class Next2InstructionValueSolver extends AbstractExpressionSolver<Next2I
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AssemblyResolution solve(Next2InstructionValue iv, MaskedLong goal,
|
public AssemblyResolution solve(AbstractAssemblyResolutionFactory<?, ?> factory,
|
||||||
Map<String, Long> vals,
|
Next2InstructionValue exp, MaskedLong goal, Map<String, Long> vals,
|
||||||
AssemblyResolvedPatterns cur, Set<SolverHint> hints, String description) {
|
AssemblyResolvedPatterns cur, Set<SolverHint> hints, String description)
|
||||||
|
throws NeedsBackfillException {
|
||||||
throw new AssertionError(
|
throw new AssertionError(
|
||||||
"INTERNAL: Should never be asked to solve for " + AssemblyTreeResolver.INST_NEXT2);
|
"INTERNAL: Should never be asked to solve for " + AssemblyTreeResolver.INST_NEXT2);
|
||||||
}
|
}
|
||||||
|
|||||||
+17
-11
@@ -41,7 +41,7 @@ public class OperandValueSolver extends AbstractExpressionSolver<OperandValue> {
|
|||||||
* Obtains the "defining expression"
|
* Obtains the "defining expression"
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* This is either the symbols assigned defining expression, or the expression associated with
|
* This is either the symbol's 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
|
||||||
@@ -60,25 +60,31 @@ public class OperandValueSolver extends AbstractExpressionSolver<OperandValue> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AssemblyResolution solve(OperandValue ov, MaskedLong goal, Map<String, Long> vals,
|
public AssemblyResolution solve(AbstractAssemblyResolutionFactory<?, ?> factory,
|
||||||
AssemblyResolvedPatterns cur, Set<SolverHint> hints, String description)
|
OperandValue ov, MaskedLong goal, Map<String, Long> vals, AssemblyResolvedPatterns cur,
|
||||||
throws NeedsBackfillException {
|
Set<SolverHint> hints, String description) 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, null);
|
return factory.nop(description);
|
||||||
}
|
}
|
||||||
return AssemblyResolution.error("Operand " + sym.getName() +
|
return factory.newErrorBuilder()
|
||||||
" is undefined and does not agree with child requirements", description);
|
.error("Operand " + sym.getName() +
|
||||||
|
" is undefined and does not agree with child requirements")
|
||||||
|
.description(description)
|
||||||
|
.build();
|
||||||
}
|
}
|
||||||
AssemblyResolution result = solver.solve(patexp, goal, vals, cur, hints, description);
|
AssemblyResolution result =
|
||||||
|
solver.solve(factory, 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 factory.newErrorBuilder()
|
||||||
"Solution to " + sym.getName() + " := " + goal + " = " + patexp,
|
.error(err.getError())
|
||||||
List.of(result), null);
|
.description("Solution to " + sym.getName() + " := " + goal + " = " + patexp)
|
||||||
|
.children(List.of(result))
|
||||||
|
.build();
|
||||||
}
|
}
|
||||||
// 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
|
||||||
|
|||||||
+27
-22
@@ -53,7 +53,8 @@ public class OrExpressionSolver extends AbstractBinaryExpressionSolver<OrExpress
|
|||||||
return goal.invOr(rval);
|
return goal.invOr(rval);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected AssemblyResolution tryCatenationExpression(OrExpression exp, MaskedLong goal,
|
protected AssemblyResolution tryCatenationExpression(
|
||||||
|
AbstractAssemblyResolutionFactory<?, ?> factory, OrExpression exp, MaskedLong goal,
|
||||||
Map<String, Long> vals, AssemblyResolvedPatterns cur, Set<SolverHint> hints,
|
Map<String, Long> vals, AssemblyResolvedPatterns cur, Set<SolverHint> hints,
|
||||||
String description) throws SolverException {
|
String description) throws SolverException {
|
||||||
/*
|
/*
|
||||||
@@ -68,7 +69,7 @@ public class OrExpressionSolver extends AbstractBinaryExpressionSolver<OrExpress
|
|||||||
fields.put(64L, new ConstantValue(0));
|
fields.put(64L, new ConstantValue(0));
|
||||||
long lo = 0;
|
long lo = 0;
|
||||||
PatternExpression fieldExp = null;
|
PatternExpression fieldExp = null;
|
||||||
AssemblyResolvedPatterns result = AssemblyResolution.nop(description);
|
AssemblyResolvedPatterns result = factory.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()) {
|
||||||
@@ -81,7 +82,7 @@ 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, cur, hints,
|
AssemblyResolution sol = solver.solve(factory, fieldExp, part, vals, cur, hints,
|
||||||
description + " with shift " + lo);
|
description + " with shift " + lo);
|
||||||
if (sol.isError()) {
|
if (sol.isError()) {
|
||||||
return sol;
|
return sol;
|
||||||
@@ -98,7 +99,8 @@ public class OrExpressionSolver extends AbstractBinaryExpressionSolver<OrExpress
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected AssemblyResolution tryCircularShiftExpression(OrExpression exp, MaskedLong goal,
|
protected AssemblyResolution tryCircularShiftExpression(
|
||||||
|
AbstractAssemblyResolutionFactory<?, ?> factory, OrExpression exp, MaskedLong goal,
|
||||||
Map<String, Long> vals, AssemblyResolvedPatterns cur, Set<SolverHint> hints,
|
Map<String, Long> vals, AssemblyResolvedPatterns cur, 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.
|
||||||
@@ -166,11 +168,12 @@ 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, cur, hints,
|
return solveLeftCircularShift(factory, expValu1, expShift, size, dir, goal, vals, cur,
|
||||||
description);
|
hints, description);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected AssemblyResolution solveLeftCircularShift(PatternExpression expValue,
|
protected AssemblyResolution solveLeftCircularShift(
|
||||||
|
AbstractAssemblyResolutionFactory<?, ?> factory, 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,
|
||||||
AssemblyResolvedPatterns cur, Set<SolverHint> hints, String description)
|
AssemblyResolvedPatterns cur, Set<SolverHint> hints, String description)
|
||||||
throws NeedsBackfillException, SolverException {
|
throws NeedsBackfillException, SolverException {
|
||||||
@@ -194,12 +197,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, cur,
|
return solver.solve(factory, expShift, computeCircShiftG(valValue, size, dir, goal),
|
||||||
hints, description);
|
vals, cur, hints, description);
|
||||||
}
|
}
|
||||||
else if (valShift != null) {
|
else if (valShift != null) {
|
||||||
return solver.solve(expValue, computeCircShiftF(valShift, size, dir, goal), vals, cur,
|
return solver.solve(factory, expValue, computeCircShiftF(valShift, size, dir, goal),
|
||||||
hints, description);
|
vals, cur, hints, description);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Oiy. Try guessing the shift amount, starting at 0
|
// Oiy. Try guessing the shift amount, starting at 0
|
||||||
@@ -213,14 +216,14 @@ 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, cur,
|
AssemblyResolution resValue = solver.solve(factory, 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, cur, hints, description);
|
solver.solve(factory, 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());
|
||||||
@@ -267,11 +270,12 @@ public class OrExpressionSolver extends AbstractBinaryExpressionSolver<OrExpress
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected AssemblyResolution solveTwoSided(OrExpression exp, MaskedLong goal,
|
protected AssemblyResolution solveTwoSided(AbstractAssemblyResolutionFactory<?, ?> factory,
|
||||||
Map<String, Long> vals, AssemblyResolvedPatterns cur, Set<SolverHint> hints,
|
OrExpression exp, MaskedLong goal, Map<String, Long> vals, AssemblyResolvedPatterns cur,
|
||||||
String description) throws NeedsBackfillException, SolverException {
|
Set<SolverHint> hints, String description)
|
||||||
|
throws NeedsBackfillException, SolverException {
|
||||||
try {
|
try {
|
||||||
return tryCatenationExpression(exp, goal, vals, cur, hints, description);
|
return tryCatenationExpression(factory, exp, goal, vals, cur, hints, description);
|
||||||
}
|
}
|
||||||
catch (Exception e) {
|
catch (Exception e) {
|
||||||
dbg.println("while solving: " + goal + "=:" + exp);
|
dbg.println("while solving: " + goal + "=:" + exp);
|
||||||
@@ -279,7 +283,7 @@ public class OrExpressionSolver extends AbstractBinaryExpressionSolver<OrExpress
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return tryCircularShiftExpression(exp, goal, vals, cur, hints, description);
|
return tryCircularShiftExpression(factory, exp, goal, vals, cur, hints, description);
|
||||||
}
|
}
|
||||||
catch (Exception e) {
|
catch (Exception e) {
|
||||||
dbg.println("while solving: " + goal + "=:" + exp);
|
dbg.println("while solving: " + goal + "=:" + exp);
|
||||||
@@ -291,21 +295,22 @@ public class OrExpressionSolver extends AbstractBinaryExpressionSolver<OrExpress
|
|||||||
long value = MATCHERS.val.get(match).getValue();
|
long value = MATCHERS.val.get(match).getValue();
|
||||||
PatternValue field = MATCHERS.fld.get(match);
|
PatternValue field = MATCHERS.fld.get(match);
|
||||||
// Solve for equals, then either return that, or forbid it, depending on goal
|
// Solve for equals, then either return that, or forbid it, depending on goal
|
||||||
AssemblyResolution solution =
|
AssemblyResolution solution = solver.solve(factory, field, MaskedLong.fromLong(value),
|
||||||
solver.solve(field, MaskedLong.fromLong(value), vals, cur, hints, description);
|
vals, cur, hints, description);
|
||||||
if (goal.equals(MaskedLong.fromMaskAndValue(0, 1))) {
|
if (goal.equals(MaskedLong.fromMaskAndValue(0, 1))) {
|
||||||
return solution;
|
return solution;
|
||||||
}
|
}
|
||||||
if (goal.equals(MaskedLong.fromMaskAndValue(1, 1))) {
|
if (goal.equals(MaskedLong.fromMaskAndValue(1, 1))) {
|
||||||
if (solution.isError()) {
|
if (solution.isError()) {
|
||||||
return AssemblyResolution.nop(description);
|
return factory.nop(description);
|
||||||
}
|
}
|
||||||
if (solution.isBackfill()) {
|
if (solution.isBackfill()) {
|
||||||
throw new AssertionError();
|
throw new AssertionError();
|
||||||
}
|
}
|
||||||
AssemblyResolvedPatterns forbidden = (AssemblyResolvedPatterns) solution;
|
AssemblyResolvedPatterns forbidden = (AssemblyResolvedPatterns) solution;
|
||||||
forbidden = forbidden.withDescription("Solved 'not equals'");
|
forbidden = forbidden.withDescription("Solved 'not equals'");
|
||||||
return AssemblyResolution.nop(description).withForbids(Set.of(forbidden));
|
AssemblyResolvedPatterns rp = factory.nop(description);
|
||||||
|
return rp.withForbids(Set.of(forbidden));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+11
-10
@@ -17,8 +17,7 @@ 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.*;
|
||||||
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;
|
||||||
|
|
||||||
@@ -115,11 +114,13 @@ public class RecursiveDescentSolver {
|
|||||||
* @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(AbstractAssemblyResolutionFactory<?, ?> factory,
|
||||||
Map<String, Long> vals, AssemblyResolvedPatterns cur, Set<SolverHint> hints,
|
PatternExpression exp, MaskedLong goal, Map<String, Long> vals,
|
||||||
String description) throws NeedsBackfillException {
|
AssemblyResolvedPatterns cur, Set<SolverHint> hints, String description)
|
||||||
|
throws NeedsBackfillException {
|
||||||
try {
|
try {
|
||||||
return getRegistered(exp.getClass()).solve(exp, goal, vals, cur, hints, description);
|
return getRegistered(exp.getClass()).solve(factory, exp, goal, vals, cur, hints,
|
||||||
|
description);
|
||||||
}
|
}
|
||||||
catch (UnsupportedOperationException e) {
|
catch (UnsupportedOperationException e) {
|
||||||
DBG.println("Error solving " + exp + " = " + goal);
|
DBG.println("Error solving " + exp + " = " + goal);
|
||||||
@@ -152,10 +153,10 @@ public class RecursiveDescentSolver {
|
|||||||
* @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(AbstractAssemblyResolutionFactory<?, ?> factory,
|
||||||
AssemblyResolvedPatterns cur, String description)
|
PatternExpression exp, MaskedLong goal, Map<String, Long> vals,
|
||||||
throws NeedsBackfillException {
|
AssemblyResolvedPatterns cur, String description) throws NeedsBackfillException {
|
||||||
return solve(exp, goal, vals, cur, Set.of(), description);
|
return solve(factory, exp, goal, vals, cur, Set.of(), description);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+10
-10
@@ -18,8 +18,7 @@ package ghidra.app.plugin.assembler.sleigh.expr;
|
|||||||
import java.util.Map;
|
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.*;
|
||||||
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;
|
||||||
|
|
||||||
@@ -61,15 +60,16 @@ public class RightShiftExpressionSolver
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected AssemblyResolution solveTwoSided(RightShiftExpression exp, MaskedLong goal,
|
protected AssemblyResolution solveTwoSided(AbstractAssemblyResolutionFactory<?, ?> factory,
|
||||||
Map<String, Long> vals, AssemblyResolvedPatterns cur, Set<SolverHint> hints,
|
RightShiftExpression exp, MaskedLong goal, Map<String, Long> vals,
|
||||||
String description) throws NeedsBackfillException, SolverException {
|
AssemblyResolvedPatterns cur, Set<SolverHint> hints, String description)
|
||||||
|
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, cur, hints, description);
|
return super.solveTwoSided(factory, exp, goal, vals, cur, hints, description);
|
||||||
}
|
}
|
||||||
|
|
||||||
int maxShift = Long.numberOfLeadingZeros(goal.val);
|
int maxShift = Long.numberOfLeadingZeros(goal.val);
|
||||||
@@ -80,13 +80,13 @@ public class RightShiftExpressionSolver
|
|||||||
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(factory, exp.getLeft(), reql, vals, cur,
|
||||||
solver.solve(exp.getLeft(), reql, vals, cur, hintsWithRShift, description);
|
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, cur, hints, description);
|
solver.solve(factory, 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");
|
||||||
}
|
}
|
||||||
@@ -104,6 +104,6 @@ public class RightShiftExpressionSolver
|
|||||||
// try the next
|
// try the next
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return super.solveTwoSided(exp, goal, vals, cur, hints, description);
|
return super.solveTwoSided(factory, exp, goal, vals, cur, hints, description);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-3
@@ -35,9 +35,9 @@ public class StartInstructionValueSolver extends AbstractExpressionSolver<StartI
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AssemblyResolution solve(StartInstructionValue iv, MaskedLong goal,
|
public AssemblyResolution solve(AbstractAssemblyResolutionFactory<?, ?> factory,
|
||||||
Map<String, Long> vals, AssemblyResolvedPatterns cur, Set<SolverHint> hints,
|
StartInstructionValue iv, MaskedLong goal, Map<String, Long> vals,
|
||||||
String description) {
|
AssemblyResolvedPatterns cur, Set<SolverHint> hints, 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);
|
||||||
}
|
}
|
||||||
|
|||||||
+8
-5
@@ -35,15 +35,18 @@ public class TokenFieldSolver extends AbstractExpressionSolver<TokenField> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AssemblyResolution solve(TokenField tf, MaskedLong goal, Map<String, Long> vals,
|
public AssemblyResolution solve(AbstractAssemblyResolutionFactory<?, ?> factory,
|
||||||
AssemblyResolvedPatterns cur, Set<SolverHint> hints, String description) {
|
TokenField tf, MaskedLong goal, Map<String, Long> vals, AssemblyResolvedPatterns cur,
|
||||||
|
Set<SolverHint> hints, 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 factory.newErrorBuilder()
|
||||||
description);
|
.error("Value " + goal + " is not valid for " + tf)
|
||||||
|
.description(description)
|
||||||
|
.build();
|
||||||
}
|
}
|
||||||
AssemblyPatternBlock block = AssemblyPatternBlock.fromTokenField(tf, goal);
|
AssemblyPatternBlock block = AssemblyPatternBlock.fromTokenField(tf, goal);
|
||||||
return AssemblyResolution.instrOnly(block, description);
|
return factory.instrOnly(block, description);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
+9
-3
@@ -17,6 +17,7 @@ package ghidra.app.plugin.assembler.sleigh.grammars;
|
|||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
|
import ghidra.app.plugin.assembler.sleigh.sem.AbstractAssemblyResolutionFactory;
|
||||||
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;
|
||||||
@@ -33,14 +34,19 @@ import ghidra.app.plugin.processors.sleigh.pattern.DisjointPattern;
|
|||||||
*/
|
*/
|
||||||
public class AssemblyGrammar
|
public class AssemblyGrammar
|
||||||
extends AbstractAssemblyGrammar<AssemblyNonTerminal, AssemblyProduction> {
|
extends AbstractAssemblyGrammar<AssemblyNonTerminal, AssemblyProduction> {
|
||||||
|
protected final AbstractAssemblyResolutionFactory<?, ?> factory;
|
||||||
// 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>> semanticsByProduction =
|
protected final Map<AssemblyProduction, //
|
||||||
new TreeMap<>();
|
Map<Constructor, AssemblyConstructorSemantic>> semanticsByProduction = new TreeMap<>();
|
||||||
protected final Map<Constructor, AssemblyConstructorSemantic> semanticsByConstructor =
|
protected final Map<Constructor, AssemblyConstructorSemantic> semanticsByConstructor =
|
||||||
new HashMap<>();
|
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<>();
|
||||||
|
|
||||||
|
public AssemblyGrammar(AbstractAssemblyResolutionFactory<?, ?> factory) {
|
||||||
|
this.factory = factory;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected AssemblyProduction newProduction(AssemblyNonTerminal lhs,
|
protected AssemblyProduction newProduction(AssemblyNonTerminal lhs,
|
||||||
AssemblySentential<AssemblyNonTerminal> rhs) {
|
AssemblySentential<AssemblyNonTerminal> rhs) {
|
||||||
@@ -73,7 +79,7 @@ public class AssemblyGrammar
|
|||||||
Map<Constructor, AssemblyConstructorSemantic> map =
|
Map<Constructor, AssemblyConstructorSemantic> map =
|
||||||
semanticsByProduction.computeIfAbsent(prod, p -> new TreeMap<>());
|
semanticsByProduction.computeIfAbsent(prod, p -> new TreeMap<>());
|
||||||
AssemblyConstructorSemantic sem =
|
AssemblyConstructorSemantic sem =
|
||||||
map.computeIfAbsent(cons, c -> new AssemblyConstructorSemantic(cons, indices));
|
map.computeIfAbsent(cons, c -> new AssemblyConstructorSemantic(factory, cons, indices));
|
||||||
if (!indices.equals(sem.getOperandIndices())) {
|
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");
|
||||||
|
|||||||
+4
@@ -28,4 +28,8 @@ public class AssemblyProduction extends AbstractAssemblyProduction<AssemblyNonTe
|
|||||||
AssemblySentential<AssemblyNonTerminal> rhs) {
|
AssemblySentential<AssemblyNonTerminal> rhs) {
|
||||||
super(lhs, rhs);
|
super(lhs, rhs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isConstructor() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+8
-3
@@ -127,7 +127,7 @@ public class AssemblySentential<NT extends AssemblyNonTerminal>
|
|||||||
*/
|
*/
|
||||||
private static class WhiteSpace extends AssemblyStringTerminal {
|
private static class WhiteSpace extends AssemblyStringTerminal {
|
||||||
private WhiteSpace() {
|
private WhiteSpace() {
|
||||||
super(" ");
|
super(" ", null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -166,6 +166,11 @@ public class AssemblySentential<NT extends AssemblyNonTerminal>
|
|||||||
public Collection<String> getSuggestions(String got, AssemblyNumericSymbols symbols) {
|
public Collection<String> getSuggestions(String got, AssemblyNumericSymbols symbols) {
|
||||||
return Collections.singleton(" ");
|
return Collections.singleton(" ");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isWhiteSpace() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -217,7 +222,7 @@ public class AssemblySentential<NT extends AssemblyNonTerminal>
|
|||||||
* Add a comma followed by optional whitespace.
|
* Add a comma followed by optional whitespace.
|
||||||
*/
|
*/
|
||||||
public void addCommaWS() {
|
public void addCommaWS() {
|
||||||
addSymbol(new AssemblyStringTerminal(","));
|
addSymbol(new AssemblyStringTerminal(",", null));
|
||||||
addWS();
|
addWS();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -240,7 +245,7 @@ public class AssemblySentential<NT extends AssemblyNonTerminal>
|
|||||||
if (!Character.isLetterOrDigit(first)) {
|
if (!Character.isLetterOrDigit(first)) {
|
||||||
addWS();
|
addWS();
|
||||||
}
|
}
|
||||||
addSymbol(new AssemblyStringTerminal(tstr));
|
addSymbol(new AssemblyStringTerminal(tstr, null));
|
||||||
char last = tstr.charAt(tstr.length() - 1);
|
char last = tstr.charAt(tstr.length() - 1);
|
||||||
if (!str.endsWith(tstr)) {
|
if (!str.endsWith(tstr)) {
|
||||||
addWS();
|
addWS();
|
||||||
|
|||||||
+170
@@ -0,0 +1,170 @@
|
|||||||
|
/* ###
|
||||||
|
* 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.sem;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The (often intermediate) result of assembly
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* These may represent a successful construction ({@link AssemblyResolvedPatterns}, a future field
|
||||||
|
* ({@link AssemblyResolvedBackfill}), or an error ({@link AssemblyResolvedError}).
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This class also provides the static factory methods for constructing any of its subclasses.
|
||||||
|
*/
|
||||||
|
public abstract class AbstractAssemblyResolution implements AssemblyResolution {
|
||||||
|
protected final AbstractAssemblyResolutionFactory<?, ?> factory;
|
||||||
|
protected final String description;
|
||||||
|
protected final List<AssemblyResolution> children;
|
||||||
|
protected final AssemblyResolution right;
|
||||||
|
|
||||||
|
private boolean hashed = false;
|
||||||
|
private int hash;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a resolution
|
||||||
|
*
|
||||||
|
* @param description a textual description used as part of {@link #toString()}
|
||||||
|
* @param children for record keeping, any children used in constructing this resolution
|
||||||
|
*/
|
||||||
|
protected AbstractAssemblyResolution(AbstractAssemblyResolutionFactory<?, ?> factory,
|
||||||
|
String description, List<? extends AssemblyResolution> children,
|
||||||
|
AssemblyResolution right) {
|
||||||
|
this.factory = factory;
|
||||||
|
this.description = description;
|
||||||
|
this.children = children == null ? List.of() : Collections.unmodifiableList(children);
|
||||||
|
this.right = right;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
if (!hashed) {
|
||||||
|
hash = computeHash();
|
||||||
|
hashed = true;
|
||||||
|
}
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract int computeHash();
|
||||||
|
|
||||||
|
/* ********************************************************************************************
|
||||||
|
* Misc
|
||||||
|
*/
|
||||||
|
|
||||||
|
protected List<AssemblyResolution> getAllRight() {
|
||||||
|
List<AssemblyResolution> result = new ArrayList<>();
|
||||||
|
collectAllRight(result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void collectAllRight(Collection<AssemblyResolution> into) {
|
||||||
|
into.add(this);
|
||||||
|
if (right == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
right.collectAllRight(into);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the child portion of {@link #toString()}
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* If a subclass has another, possible additional, notion of children that it would like to
|
||||||
|
* include in {@link #toString()}, it must override this method.
|
||||||
|
*
|
||||||
|
* @see #hasChildren()
|
||||||
|
* @param indent the current indentation
|
||||||
|
* @return the indented description for each child on its own line
|
||||||
|
*/
|
||||||
|
protected String childrenToString(String indent) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
for (AssemblyResolution child : children) {
|
||||||
|
sb.append(child.toString(indent) + "\n");
|
||||||
|
}
|
||||||
|
return sb.substring(0, sb.length() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString(String indent) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append(indent);
|
||||||
|
sb.append(lineToString());
|
||||||
|
if (hasChildren()) {
|
||||||
|
sb.append(":\n");
|
||||||
|
String newIndent = indent + " ";
|
||||||
|
sb.append(childrenToString(newIndent));
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return toString("");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(AssemblyResolution that) {
|
||||||
|
return this.toString().compareTo(that.toString()); // LAZY
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasChildren() {
|
||||||
|
if (children == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (children.size() == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public abstract AssemblyResolution shift(int amt);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get this same resolution, but without any right siblings
|
||||||
|
*
|
||||||
|
* @return the resolution
|
||||||
|
*/
|
||||||
|
public AssemblyResolution withoutRight() {
|
||||||
|
return withRight(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get this same resolution, but with the given right sibling
|
||||||
|
*
|
||||||
|
* @return the resolution
|
||||||
|
*/
|
||||||
|
public abstract AssemblyResolution withRight(AssemblyResolution right);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDescription() {
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<AssemblyResolution> getChildren() {
|
||||||
|
return children;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AssemblyResolution getRight() {
|
||||||
|
return right;
|
||||||
|
}
|
||||||
|
}
|
||||||
+442
@@ -0,0 +1,442 @@
|
|||||||
|
/* ###
|
||||||
|
* 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.sem;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
|
|
||||||
|
import ghidra.app.plugin.assembler.sleigh.expr.*;
|
||||||
|
import ghidra.app.plugin.assembler.sleigh.sem.AbstractAssemblyResolutionFactory.AbstractAssemblyResolvedBackfillBuilder;
|
||||||
|
import ghidra.app.plugin.assembler.sleigh.sem.AbstractAssemblyResolutionFactory.AbstractAssemblyResolvedPatternsBuilder;
|
||||||
|
import ghidra.app.plugin.processors.sleigh.Constructor;
|
||||||
|
import ghidra.app.plugin.processors.sleigh.expression.PatternExpression;
|
||||||
|
import ghidra.app.plugin.processors.sleigh.pattern.DisjointPattern;
|
||||||
|
|
||||||
|
public abstract class AbstractAssemblyResolutionFactory< //
|
||||||
|
RP extends AssemblyResolvedPatterns, //
|
||||||
|
BF extends AssemblyResolvedBackfill> {
|
||||||
|
protected static final RecursiveDescentSolver SOLVER = RecursiveDescentSolver.getSolver();
|
||||||
|
protected static final String INS = "ins:";
|
||||||
|
protected static final String CTX = "ctx:";
|
||||||
|
protected static final String SEP = ",";
|
||||||
|
|
||||||
|
public abstract static class AbstractAssemblyResolutionBuilder< //
|
||||||
|
B extends AbstractAssemblyResolutionBuilder<B, T>, //
|
||||||
|
T extends AssemblyResolution> {
|
||||||
|
protected String description;
|
||||||
|
protected List<AssemblyResolution> children;
|
||||||
|
protected AssemblyResolution right;
|
||||||
|
|
||||||
|
public void copyFromDefault(AbstractAssemblyResolution ar) {
|
||||||
|
this.description = ar.description;
|
||||||
|
this.children = ar.children;
|
||||||
|
this.right = ar.right;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
protected B self() {
|
||||||
|
return (B) this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public B description(String description) {
|
||||||
|
this.description = description;
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
public B children(List<AssemblyResolution> children) {
|
||||||
|
this.children = children;
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
public B right(AssemblyResolution right) {
|
||||||
|
this.right = right;
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract T build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract static class AbstractAssemblyResolvedPatternsBuilder< //
|
||||||
|
RP extends AssemblyResolvedPatterns> extends
|
||||||
|
AbstractAssemblyResolutionBuilder<AbstractAssemblyResolvedPatternsBuilder<RP>, RP> {
|
||||||
|
protected Constructor cons;
|
||||||
|
protected AssemblyPatternBlock ins;
|
||||||
|
protected AssemblyPatternBlock ctx;
|
||||||
|
protected Set<AssemblyResolvedBackfill> backfills;
|
||||||
|
protected Set<AssemblyResolvedPatterns> forbids;
|
||||||
|
|
||||||
|
public void copyFromDefault(DefaultAssemblyResolvedPatterns rp) {
|
||||||
|
super.copyFromDefault(rp);
|
||||||
|
this.cons = rp.cons;
|
||||||
|
this.ins = rp.ins;
|
||||||
|
this.ctx = rp.ctx;
|
||||||
|
this.backfills = rp.backfills;
|
||||||
|
this.forbids = rp.forbids;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract static class AbstractAssemblyResolvedBackfillBuilder< //
|
||||||
|
BF extends AssemblyResolvedBackfill> extends
|
||||||
|
AbstractAssemblyResolutionBuilder<AbstractAssemblyResolvedBackfillBuilder<BF>, BF> {
|
||||||
|
protected PatternExpression exp;
|
||||||
|
protected MaskedLong goal;
|
||||||
|
protected int inslen;
|
||||||
|
protected int offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class DefaultAssemblyResolvedPatternBuilder
|
||||||
|
extends AbstractAssemblyResolvedPatternsBuilder<AssemblyResolvedPatterns> {
|
||||||
|
@Override
|
||||||
|
protected AssemblyResolvedPatterns build() {
|
||||||
|
return new DefaultAssemblyResolvedPatterns(AbstractAssemblyResolutionFactory.this,
|
||||||
|
description, cons, children, right, ins, ctx, backfills, forbids);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class DefaultAssemblyResolvedBackfillBuilder
|
||||||
|
extends AbstractAssemblyResolvedBackfillBuilder<AssemblyResolvedBackfill> {
|
||||||
|
@Override
|
||||||
|
protected AssemblyResolvedBackfill build() {
|
||||||
|
return new DefaultAssemblyResolvedBackfill(AbstractAssemblyResolutionFactory.this,
|
||||||
|
description, exp, goal, inslen, offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class AssemblyResolvedErrorBuilder extends
|
||||||
|
AbstractAssemblyResolutionBuilder<AssemblyResolvedErrorBuilder, AssemblyResolvedError> {
|
||||||
|
protected String error;
|
||||||
|
|
||||||
|
public AssemblyResolvedErrorBuilder error(String error) {
|
||||||
|
this.error = error;
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AssemblyResolvedError build() {
|
||||||
|
return new DefaultAssemblyResolvedError(AbstractAssemblyResolutionFactory.this,
|
||||||
|
description, children, right, error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract AbstractAssemblyResolvedPatternsBuilder<RP> newPatternsBuilder();
|
||||||
|
|
||||||
|
public abstract AbstractAssemblyResolvedBackfillBuilder<BF> newBackfillBuilder();
|
||||||
|
|
||||||
|
public AssemblyResolvedErrorBuilder newErrorBuilder() {
|
||||||
|
return new AssemblyResolvedErrorBuilder();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct an immutable single-entry result set consisting of the one given resolution
|
||||||
|
*
|
||||||
|
* @param rp the single resolution entry
|
||||||
|
* @return the new resolution set
|
||||||
|
*/
|
||||||
|
protected AssemblyResolutionResults singleton(AssemblyResolution one) {
|
||||||
|
return results(Set.of(one));
|
||||||
|
}
|
||||||
|
|
||||||
|
public AssemblyResolutionResults newAssemblyResolutionResults() {
|
||||||
|
return new AssemblyResolutionResults();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected AssemblyResolutionResults results(Set<AssemblyResolution> col) {
|
||||||
|
return new AssemblyResolutionResults(col);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempt to solve an expression
|
||||||
|
*
|
||||||
|
* @param exp the expression to solve
|
||||||
|
* @param goal the desired value of the expression
|
||||||
|
* @param cur the resolved constructor so far
|
||||||
|
* @param description a description of the result
|
||||||
|
* @return the encoded solution, or a backfill record
|
||||||
|
*/
|
||||||
|
protected AssemblyResolution solveOrBackfill(PatternExpression exp, MaskedLong goal,
|
||||||
|
Map<String, Long> vals, AssemblyResolvedPatterns cur, String description) {
|
||||||
|
try {
|
||||||
|
return SOLVER.solve(this, exp, goal, vals, cur, description);
|
||||||
|
}
|
||||||
|
catch (NeedsBackfillException bf) {
|
||||||
|
int fieldLength = SOLVER.getInstructionLength(exp);
|
||||||
|
return backfill(exp, goal, fieldLength, description);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempt to solve an expression
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Converts the given goal and bits count to a {@link MaskedLong} and then solves as before. As
|
||||||
|
* a special case, if {@code bits == 0}, the goal is considered fully-defined (as if
|
||||||
|
* {@code bits == 64}).
|
||||||
|
*
|
||||||
|
* @see #solveOrBackfill(PatternExpression, MaskedLong, AssemblyResolvedPatterns, String)
|
||||||
|
*/
|
||||||
|
protected AssemblyResolution solveOrBackfill(PatternExpression exp, long goal, int bits,
|
||||||
|
Map<String, Long> vals, AssemblyResolvedPatterns cur, String description) {
|
||||||
|
long msk;
|
||||||
|
if (bits == 0 || bits >= 64) {
|
||||||
|
msk = -1L;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
msk = ~(-1L << bits);
|
||||||
|
}
|
||||||
|
return solveOrBackfill(exp, MaskedLong.fromMaskAndValue(msk, goal), vals, cur, description);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempt to solve an expression
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Converts the given goal to a fully-defined {@link MaskedLong} and then solves.
|
||||||
|
*
|
||||||
|
* @see #solveOrBackfill(PatternExpression, MaskedLong, Map, AssemblyResolvedPatterns, String)
|
||||||
|
*/
|
||||||
|
protected AssemblyResolution solveOrBackfill(PatternExpression exp, long goal,
|
||||||
|
Map<String, Long> vals, AssemblyResolvedPatterns cur, String description) {
|
||||||
|
return solveOrBackfill(exp, MaskedLong.fromLong(goal), vals, cur, description);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AssemblyResolvedErrorBuilder errorBuilder(String error, AssemblyResolution res) {
|
||||||
|
var builder = newErrorBuilder();
|
||||||
|
builder.error = error;
|
||||||
|
builder.description = res.getDescription();
|
||||||
|
builder.children = res.getChildren();
|
||||||
|
builder.right = res.getRight();
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build an error resolution record, based on an intermediate SLEIGH constructor record
|
||||||
|
*
|
||||||
|
* @param error a description of the error
|
||||||
|
* @param res the constructor record that was being populated when the error occurred
|
||||||
|
* @return the new error resolution
|
||||||
|
*/
|
||||||
|
public AssemblyResolution error(String error, AssemblyResolution res) {
|
||||||
|
return errorBuilder(error, res).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected AbstractAssemblyResolvedPatternsBuilder<RP> nopBuilder(String description) {
|
||||||
|
var builder = newPatternsBuilder();
|
||||||
|
builder.ins = AssemblyPatternBlock.nop();
|
||||||
|
builder.ctx = AssemblyPatternBlock.nop();
|
||||||
|
builder.description = description;
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtain a new "blank" resolved SLEIGH constructor record
|
||||||
|
*
|
||||||
|
* @param description a description of the resolution
|
||||||
|
* @return the new resolution
|
||||||
|
*/
|
||||||
|
public RP nop(String description) {
|
||||||
|
return nopBuilder(description).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected AbstractAssemblyResolvedPatternsBuilder<RP> nopBuilder(String description,
|
||||||
|
List<AssemblyResolution> children, AssemblyResolution right) {
|
||||||
|
var builder = newPatternsBuilder();
|
||||||
|
builder.ins = AssemblyPatternBlock.nop();
|
||||||
|
builder.ctx = AssemblyPatternBlock.nop();
|
||||||
|
builder.description = description;
|
||||||
|
builder.children = children;
|
||||||
|
builder.right = right;
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtain a new "blank" resolved SLEIGH constructor record
|
||||||
|
*
|
||||||
|
* @param description a description of the resolution
|
||||||
|
* @param children any children that will be involved in populating this record
|
||||||
|
* @return the new resolution
|
||||||
|
*/
|
||||||
|
public RP nop(String description, List<AssemblyResolution> children, AssemblyResolution right) {
|
||||||
|
return nopBuilder(description, children, right).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public AbstractAssemblyResolvedBackfillBuilder<BF> backfillBuilder(PatternExpression exp,
|
||||||
|
MaskedLong goal, int inslen, String description) {
|
||||||
|
var builder = newBackfillBuilder();
|
||||||
|
builder.exp = exp;
|
||||||
|
builder.goal = goal;
|
||||||
|
builder.inslen = inslen;
|
||||||
|
builder.description = description;
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build a backfill record to attach to a successful resolution result
|
||||||
|
*
|
||||||
|
* @param exp the expression depending on a missing symbol
|
||||||
|
* @param goal the desired value of the expression
|
||||||
|
* @param inslen the length of instruction portion expected in the future solution
|
||||||
|
* @param description a description of the backfill record
|
||||||
|
* @return the new record
|
||||||
|
*/
|
||||||
|
public AssemblyResolution backfill(PatternExpression exp, MaskedLong goal, int inslen,
|
||||||
|
String description) {
|
||||||
|
return backfillBuilder(exp, goal, inslen, description).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected AbstractAssemblyResolvedPatternsBuilder<RP> resolvedBuilder(AssemblyPatternBlock ins,
|
||||||
|
AssemblyPatternBlock ctx, String description, Constructor cons,
|
||||||
|
List<AssemblyResolution> children, AssemblyResolution right) {
|
||||||
|
var builder = newPatternsBuilder();
|
||||||
|
builder.ins = ins;
|
||||||
|
builder.ctx = ctx;
|
||||||
|
builder.description = description;
|
||||||
|
builder.cons = cons;
|
||||||
|
builder.children = children;
|
||||||
|
builder.right = right;
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build the result of successfully resolving a SLEIGH constructor
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* <b>NOTE:</b> This is not used strictly for resolved SLEIGH constructors. It may also be used
|
||||||
|
* to store intermediates, e.g., encoded operands, during constructor resolution.
|
||||||
|
*
|
||||||
|
* @param ins the instruction pattern block
|
||||||
|
* @param ctx the context pattern block
|
||||||
|
* @param description a description of the resolution
|
||||||
|
* @param cons the constructor, or null
|
||||||
|
* @param children the children of this constructor, or null
|
||||||
|
* @return the new resolution
|
||||||
|
*/
|
||||||
|
public RP resolved(AssemblyPatternBlock ins, AssemblyPatternBlock ctx, String description,
|
||||||
|
Constructor cons, List<AssemblyResolution> children, AssemblyResolution right) {
|
||||||
|
return resolvedBuilder(ins, ctx, description, cons, children, right).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected AbstractAssemblyResolvedPatternsBuilder<RP> instrOnlyBuilder(AssemblyPatternBlock ins,
|
||||||
|
String description) {
|
||||||
|
var builder = newPatternsBuilder();
|
||||||
|
builder.ins = ins;
|
||||||
|
builder.ctx = AssemblyPatternBlock.nop();
|
||||||
|
builder.description = description;
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build an instruction-only successful resolution result
|
||||||
|
*
|
||||||
|
* @param ins the instruction pattern block
|
||||||
|
* @param description a description of the resolution
|
||||||
|
* @return the new resolution
|
||||||
|
* @see #resolved(AssemblyPatternBlock, AssemblyPatternBlock, String, Constructor, List,
|
||||||
|
* AssemblyResolution)
|
||||||
|
*/
|
||||||
|
public RP instrOnly(AssemblyPatternBlock ins, String description) {
|
||||||
|
return instrOnlyBuilder(ins, description).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected AbstractAssemblyResolvedPatternsBuilder<RP> contextOnlyBuilder(
|
||||||
|
AssemblyPatternBlock ctx, String description) {
|
||||||
|
var builder = newPatternsBuilder();
|
||||||
|
builder.ins = AssemblyPatternBlock.nop();
|
||||||
|
builder.ctx = ctx;
|
||||||
|
builder.description = description;
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build a context-only successful resolution result
|
||||||
|
*
|
||||||
|
* @param ctx the context pattern block
|
||||||
|
* @param description a description of the resolution
|
||||||
|
* @return the new resolution
|
||||||
|
* @see #resolved(AssemblyPatternBlock, AssemblyPatternBlock, String, Constructor, List,
|
||||||
|
* AssemblyResolution)
|
||||||
|
*/
|
||||||
|
public RP contextOnly(AssemblyPatternBlock ctx, String description) {
|
||||||
|
return contextOnlyBuilder(ctx, description).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected AbstractAssemblyResolvedPatternsBuilder<RP> fromPatternBuilder(DisjointPattern pat,
|
||||||
|
int minLen, String description, Constructor cons) {
|
||||||
|
var builder = newPatternsBuilder();
|
||||||
|
builder.ins = AssemblyPatternBlock.fromPattern(pat, minLen, false);
|
||||||
|
builder.ctx = AssemblyPatternBlock.fromPattern(pat, 0, true);
|
||||||
|
builder.description = description;
|
||||||
|
builder.cons = cons;
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build a successful resolution result from a SLEIGH constructor's patterns
|
||||||
|
*
|
||||||
|
* @param pat the constructor's pattern
|
||||||
|
* @param description a description of the resolution
|
||||||
|
* @return the new resolution
|
||||||
|
*/
|
||||||
|
public RP fromPattern(DisjointPattern pat, int minLen,
|
||||||
|
String description, Constructor cons) {
|
||||||
|
return fromPatternBuilder(pat, minLen, description, cons).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected AbstractAssemblyResolvedPatternsBuilder<RP> fromStringBuilder(String str,
|
||||||
|
String description, List<AssemblyResolution> children) {
|
||||||
|
var builder = newPatternsBuilder();
|
||||||
|
builder.description = description;
|
||||||
|
builder.children = children;
|
||||||
|
if (str.startsWith(INS)) {
|
||||||
|
int end = str.indexOf(SEP);
|
||||||
|
if (end == -1) {
|
||||||
|
end = str.length();
|
||||||
|
}
|
||||||
|
builder.ins = AssemblyPatternBlock.fromString(str.substring(INS.length(), end));
|
||||||
|
str = str.substring(end);
|
||||||
|
if (str.startsWith(SEP)) {
|
||||||
|
str = str.substring(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (str.startsWith(CTX)) {
|
||||||
|
int end = str.length();
|
||||||
|
builder.ctx = AssemblyPatternBlock.fromString(str.substring(CTX.length(), end));
|
||||||
|
str = str.substring(end);
|
||||||
|
}
|
||||||
|
if (str.length() != 0) {
|
||||||
|
throw new IllegalArgumentException(str);
|
||||||
|
}
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build a new successful SLEIGH constructor resolution from a string representation
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This was used primarily in testing, to specify expected results.
|
||||||
|
*
|
||||||
|
* @param str the string representation: "{@code ins:[pattern],ctx:[pattern]}"
|
||||||
|
* @see ghidra.util.NumericUtilities#convertHexStringToMaskedValue(AtomicLong, AtomicLong,
|
||||||
|
* String, int, int, String) NumericUtilities.convertHexStringToMaskedValue(AtomicLong,
|
||||||
|
* AtomicLong, String, int, int, String)
|
||||||
|
* @param description a description of the resolution
|
||||||
|
* @param children any children involved in the resolution
|
||||||
|
* @return the decoded resolution
|
||||||
|
*/
|
||||||
|
public AssemblyResolvedPatterns fromString(String str, String description,
|
||||||
|
List<AssemblyResolution> children) {
|
||||||
|
return fromStringBuilder(str, description, children).build();
|
||||||
|
}
|
||||||
|
}
|
||||||
+23
-6
@@ -25,14 +25,16 @@ import ghidra.app.plugin.assembler.sleigh.util.DbgTimer;
|
|||||||
* Base for a node in an assembly prototype
|
* Base for a node in an assembly prototype
|
||||||
*/
|
*/
|
||||||
public abstract class AbstractAssemblyState {
|
public abstract class AbstractAssemblyState {
|
||||||
protected static final DbgTimer DBG = AssemblyTreeResolver.DBG;
|
protected static final DbgTimer DBG = AbstractAssemblyTreeResolver.DBG;
|
||||||
|
|
||||||
protected final AssemblyTreeResolver resolver;
|
protected final AbstractAssemblyTreeResolver<?> resolver;
|
||||||
|
protected final AbstractAssemblyResolutionFactory<?, ?> factory;
|
||||||
protected final List<AssemblyConstructorSemantic> path;
|
protected final List<AssemblyConstructorSemantic> path;
|
||||||
protected final int shift;
|
protected final int shift;
|
||||||
protected final int length;
|
protected final int length;
|
||||||
|
|
||||||
protected final int hash;
|
protected volatile boolean hasHash = false;
|
||||||
|
protected volatile int hash;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a node
|
* Construct a node
|
||||||
@@ -42,18 +44,21 @@ public abstract class AbstractAssemblyState {
|
|||||||
* @param shift the (right) shift in bytes for this operand
|
* @param shift the (right) shift in bytes for this operand
|
||||||
* @param length the length of this operand
|
* @param length the length of this operand
|
||||||
*/
|
*/
|
||||||
protected AbstractAssemblyState(AssemblyTreeResolver resolver,
|
protected AbstractAssemblyState(AbstractAssemblyTreeResolver<?> resolver,
|
||||||
List<AssemblyConstructorSemantic> path, int shift, int length) {
|
List<AssemblyConstructorSemantic> path, int shift, int length) {
|
||||||
this.resolver = resolver;
|
this.resolver = resolver;
|
||||||
|
this.factory = resolver.factory;
|
||||||
this.path = path;
|
this.path = path;
|
||||||
this.shift = shift;
|
this.shift = shift;
|
||||||
this.length = length;
|
this.length = length;
|
||||||
|
|
||||||
this.hash = computeHash();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
|
if (!hasHash) {
|
||||||
|
this.hash = computeHash();
|
||||||
|
this.hasHash = true;
|
||||||
|
}
|
||||||
return hash;
|
return hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,6 +82,18 @@ public abstract class AbstractAssemblyState {
|
|||||||
protected abstract Stream<AssemblyResolvedPatterns> resolve(AssemblyResolvedPatterns fromRight,
|
protected abstract Stream<AssemblyResolvedPatterns> resolve(AssemblyResolvedPatterns fromRight,
|
||||||
Collection<AssemblyResolvedError> errors);
|
Collection<AssemblyResolvedError> errors);
|
||||||
|
|
||||||
|
public AbstractAssemblyTreeResolver<?> getResolver() {
|
||||||
|
return resolver;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<AssemblyConstructorSemantic> getPath() {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getShift() {
|
||||||
|
return shift;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the length in bytes of the operand represented by this node
|
* Get the length in bytes of the operand represented by this node
|
||||||
*
|
*
|
||||||
|
|||||||
+5
-5
@@ -29,7 +29,7 @@ import ghidra.app.plugin.assembler.sleigh.util.DbgTimer;
|
|||||||
* @param <N> the type of parse tree node to process
|
* @param <N> the type of parse tree node to process
|
||||||
*/
|
*/
|
||||||
public abstract class AbstractAssemblyStateGenerator<N extends AssemblyParseTreeNode> {
|
public abstract class AbstractAssemblyStateGenerator<N extends AssemblyParseTreeNode> {
|
||||||
protected static final DbgTimer DBG = AssemblyTreeResolver.DBG;
|
protected static final DbgTimer DBG = AbstractAssemblyTreeResolver.DBG;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Context to pass along as states are generated
|
* Context to pass along as states are generated
|
||||||
@@ -47,8 +47,8 @@ public abstract class AbstractAssemblyStateGenerator<N extends AssemblyParseTree
|
|||||||
path.stream().map(sem -> sem.getLocation()).collect(Collectors.joining(",")) + "]";
|
path.stream().map(sem -> sem.getLocation()).collect(Collectors.joining(",")) + "]";
|
||||||
}
|
}
|
||||||
|
|
||||||
final List<AssemblyConstructorSemantic> path;
|
public final List<AssemblyConstructorSemantic> path;
|
||||||
final int shift;
|
public final int shift;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a context
|
* Construct a context
|
||||||
@@ -84,7 +84,7 @@ public abstract class AbstractAssemblyStateGenerator<N extends AssemblyParseTree
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected final AssemblyTreeResolver resolver;
|
protected final AbstractAssemblyTreeResolver<?> resolver;
|
||||||
protected final N node;
|
protected final N node;
|
||||||
protected final AssemblyResolvedPatterns fromLeft;
|
protected final AssemblyResolvedPatterns fromLeft;
|
||||||
|
|
||||||
@@ -95,7 +95,7 @@ public abstract class AbstractAssemblyStateGenerator<N extends AssemblyParseTree
|
|||||||
* @param node the node from which to generate states
|
* @param node the node from which to generate states
|
||||||
* @param fromLeft the accumulated patterns from the left sibling or the parent
|
* @param fromLeft the accumulated patterns from the left sibling or the parent
|
||||||
*/
|
*/
|
||||||
public AbstractAssemblyStateGenerator(AssemblyTreeResolver resolver, N node,
|
public AbstractAssemblyStateGenerator(AbstractAssemblyTreeResolver<?> resolver, N node,
|
||||||
AssemblyResolvedPatterns fromLeft) {
|
AssemblyResolvedPatterns fromLeft) {
|
||||||
this.resolver = resolver;
|
this.resolver = resolver;
|
||||||
this.node = node;
|
this.node = node;
|
||||||
|
|||||||
+527
File diff suppressed because it is too large
Load Diff
+6
-4
@@ -39,7 +39,7 @@ public class AssemblyConstructState extends AbstractAssemblyState {
|
|||||||
* @param operands the operands
|
* @param operands the operands
|
||||||
* @return the farthest end byte
|
* @return the farthest end byte
|
||||||
*/
|
*/
|
||||||
protected static int computeEnd(List<AbstractAssemblyState> operands) {
|
protected static int computeEnd(List<? extends AbstractAssemblyState> operands) {
|
||||||
return operands.stream()
|
return operands.stream()
|
||||||
.map(s -> s.shift + s.length)
|
.map(s -> s.shift + s.length)
|
||||||
.reduce(0, Integer::max);
|
.reduce(0, Integer::max);
|
||||||
@@ -61,7 +61,7 @@ public class AssemblyConstructState extends AbstractAssemblyState {
|
|||||||
* @param sem the selected SLEIGH constructor
|
* @param sem the selected SLEIGH constructor
|
||||||
* @param children the child state for each operand in the constructor
|
* @param children the child state for each operand in the constructor
|
||||||
*/
|
*/
|
||||||
public AssemblyConstructState(AssemblyTreeResolver resolver,
|
public AssemblyConstructState(AbstractAssemblyTreeResolver<?> resolver,
|
||||||
List<AssemblyConstructorSemantic> path, int shift,
|
List<AssemblyConstructorSemantic> path, int shift,
|
||||||
AssemblyConstructorSemantic sem, List<AbstractAssemblyState> children) {
|
AssemblyConstructorSemantic sem, List<AbstractAssemblyState> children) {
|
||||||
super(resolver, path, shift,
|
super(resolver, path, shift,
|
||||||
@@ -150,8 +150,10 @@ public class AssemblyConstructState extends AbstractAssemblyState {
|
|||||||
})
|
})
|
||||||
.filter(ar -> {
|
.filter(ar -> {
|
||||||
if (ar == null) {
|
if (ar == null) {
|
||||||
errors.add(AssemblyResolution.error("Pattern conflict",
|
errors.add(factory.newErrorBuilder()
|
||||||
"Resolving " + sem.getLocation() + " in " + path));
|
.error("Pattern conflict")
|
||||||
|
.description("Resolving " + sem.getLocation() + " in " + path)
|
||||||
|
.build());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
+8
-8
@@ -20,8 +20,7 @@ import java.util.stream.Stream;
|
|||||||
|
|
||||||
import ghidra.app.plugin.assembler.sleigh.grammars.AssemblyProduction;
|
import ghidra.app.plugin.assembler.sleigh.grammars.AssemblyProduction;
|
||||||
import ghidra.app.plugin.assembler.sleigh.symbol.AssemblySymbol;
|
import ghidra.app.plugin.assembler.sleigh.symbol.AssemblySymbol;
|
||||||
import ghidra.app.plugin.assembler.sleigh.tree.AssemblyParseBranch;
|
import ghidra.app.plugin.assembler.sleigh.tree.*;
|
||||||
import ghidra.app.plugin.assembler.sleigh.tree.AssemblyParseTreeNode;
|
|
||||||
import ghidra.app.plugin.assembler.sleigh.util.AsmUtil;
|
import ghidra.app.plugin.assembler.sleigh.util.AsmUtil;
|
||||||
import ghidra.app.plugin.processors.sleigh.Constructor;
|
import ghidra.app.plugin.processors.sleigh.Constructor;
|
||||||
import ghidra.app.plugin.processors.sleigh.symbol.OperandSymbol;
|
import ghidra.app.plugin.processors.sleigh.symbol.OperandSymbol;
|
||||||
@@ -43,8 +42,8 @@ public class AssemblyConstructStateGenerator
|
|||||||
* @param node the node from which to generate states
|
* @param node the node from which to generate states
|
||||||
* @param fromLeft the accumulated patterns from the left sibling or the parent
|
* @param fromLeft the accumulated patterns from the left sibling or the parent
|
||||||
*/
|
*/
|
||||||
public AssemblyConstructStateGenerator(AssemblyTreeResolver resolver, AssemblyParseBranch node,
|
public AssemblyConstructStateGenerator(AbstractAssemblyTreeResolver<?> resolver,
|
||||||
AssemblyResolvedPatterns fromLeft) {
|
AssemblyParseBranch node, AssemblyResolvedPatterns fromLeft) {
|
||||||
super(resolver, node, fromLeft);
|
super(resolver, node, fromLeft);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,8 +67,9 @@ public class AssemblyConstructStateGenerator
|
|||||||
*/
|
*/
|
||||||
protected List<AssemblyParseTreeNode> orderOpNodes(AssemblyConstructorSemantic sem) {
|
protected List<AssemblyParseTreeNode> orderOpNodes(AssemblyConstructorSemantic sem) {
|
||||||
Constructor cons = sem.getConstructor();
|
Constructor cons = sem.getConstructor();
|
||||||
List<AssemblyParseTreeNode> result =
|
AssemblyParseTreeNode[] arr = new AssemblyParseTreeNode[cons.getNumOperands()];
|
||||||
Arrays.asList(new AssemblyParseTreeNode[cons.getNumOperands()]);
|
List<AssemblyParseTreeNode> result = Arrays.asList(arr);
|
||||||
|
Arrays.fill(arr, new AssemblyParseHiddenNode(resolver.grammar));
|
||||||
int index = 0;
|
int index = 0;
|
||||||
AssemblyProduction production = node.getProduction();
|
AssemblyProduction production = node.getProduction();
|
||||||
List<AssemblyParseTreeNode> substitutions = node.getSubstitutions();
|
List<AssemblyParseTreeNode> substitutions = node.getSubstitutions();
|
||||||
@@ -167,13 +167,13 @@ public class AssemblyConstructStateGenerator
|
|||||||
* child operand boundary is numbered. The offset base must always refer to an operand to the
|
* child operand boundary is numbered. The offset base must always refer to an operand to the
|
||||||
* left.
|
* left.
|
||||||
*
|
*
|
||||||
* @param parentGc the generator context for othis node
|
* @param parentGc the generator context for this node
|
||||||
* @param childGcs a list to collect the generator context for each child operand. The root
|
* @param childGcs a list to collect the generator context for each child operand. The root
|
||||||
* invocation should pass a fixed-length mutable list of nulls, one for each child.
|
* invocation should pass a fixed-length mutable list of nulls, one for each child.
|
||||||
* @param fromLeft the accumulated patterns from the left sibling. The root invocation should
|
* @param fromLeft the accumulated patterns from the left sibling. The root invocation should
|
||||||
* pass the patterns accumulated after context changes.
|
* pass the patterns accumulated after context changes.
|
||||||
* @param sem the selected SLEIGH constructor, whose operands to generate
|
* @param sem the selected SLEIGH constructor, whose operands to generate
|
||||||
* @param opOrdered the paresd children ordered as the constructor's operands
|
* @param opOrdered the parsed children ordered as the constructor's operands
|
||||||
* @param children the list of children generated so far. The root invocation should pass the
|
* @param children the list of children generated so far. The root invocation should pass the
|
||||||
* empty list.
|
* empty list.
|
||||||
* @return the stream of generated (sub) prototypes
|
* @return the stream of generated (sub) prototypes
|
||||||
|
|||||||
+13
-11
@@ -36,9 +36,10 @@ import ghidra.app.plugin.processors.sleigh.symbol.SubtableSymbol;
|
|||||||
*/
|
*/
|
||||||
public class AssemblyConstructorSemantic implements Comparable<AssemblyConstructorSemantic> {
|
public class AssemblyConstructorSemantic implements Comparable<AssemblyConstructorSemantic> {
|
||||||
protected static final RecursiveDescentSolver SOLVER = RecursiveDescentSolver.getSolver();
|
protected static final RecursiveDescentSolver SOLVER = RecursiveDescentSolver.getSolver();
|
||||||
protected static final DbgTimer DBG = AssemblyTreeResolver.DBG;
|
protected static final DbgTimer DBG = AbstractAssemblyTreeResolver.DBG;
|
||||||
|
|
||||||
protected final Set<AssemblyResolvedPatterns> patterns = new HashSet<>();
|
protected final Set<AssemblyResolvedPatterns> patterns = new HashSet<>();
|
||||||
|
protected final AbstractAssemblyResolutionFactory<?, ?> factory;
|
||||||
protected final Constructor cons;
|
protected final Constructor cons;
|
||||||
protected final List<Integer> indices;
|
protected final List<Integer> indices;
|
||||||
protected final List<ContextChange> contextChanges;
|
protected final List<ContextChange> contextChanges;
|
||||||
@@ -54,7 +55,9 @@ public class AssemblyConstructorSemantic implements Comparable<AssemblyConstruct
|
|||||||
* @param indices the indices of RHS non-terminals in the associated production that represent
|
* @param indices the indices of RHS non-terminals in the associated production that represent
|
||||||
* an operand in the SLEIGH constructor
|
* an operand in the SLEIGH constructor
|
||||||
*/
|
*/
|
||||||
public AssemblyConstructorSemantic(Constructor cons, List<Integer> indices) {
|
public AssemblyConstructorSemantic(AbstractAssemblyResolutionFactory<?, ?> factory,
|
||||||
|
Constructor cons, List<Integer> indices) {
|
||||||
|
this.factory = factory;
|
||||||
this.cons = cons;
|
this.cons = cons;
|
||||||
this.indices = Collections.unmodifiableList(indices);
|
this.indices = Collections.unmodifiableList(indices);
|
||||||
List<ContextChange> changes = new ArrayList<>(cons.getContextChanges());
|
List<ContextChange> changes = new ArrayList<>(cons.getContextChanges());
|
||||||
@@ -69,7 +72,7 @@ public class AssemblyConstructorSemantic implements Comparable<AssemblyConstruct
|
|||||||
* @param pat the pattern
|
* @param pat the pattern
|
||||||
*/
|
*/
|
||||||
public void addPattern(DisjointPattern pat) {
|
public void addPattern(DisjointPattern pat) {
|
||||||
addPattern(AssemblyResolution.fromPattern(pat, cons.getMinimumLength(),
|
addPattern(factory.fromPattern(pat, cons.getMinimumLength(),
|
||||||
"Generated constructor pattern " + getLocation(), cons));
|
"Generated constructor pattern " + getLocation(), cons));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -238,7 +241,7 @@ public class AssemblyConstructorSemantic implements Comparable<AssemblyConstruct
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
// If the two patterns cannot be combined, then they are disjoint.
|
// If the two patterns cannot be combined, then they are disjoint.
|
||||||
AssemblyResolvedPatterns sibpat = AssemblyResolution.fromPattern(sibDP,
|
AssemblyResolvedPatterns sibpat = factory.fromPattern(sibDP,
|
||||||
sibcons.getMinimumLength(), "For specialization check", sibcons);
|
sibcons.getMinimumLength(), "For specialization check", sibcons);
|
||||||
AssemblyResolvedPatterns comb = pat.combine(sibpat);
|
AssemblyResolvedPatterns comb = pat.combine(sibpat);
|
||||||
if (null == comb) {
|
if (null == comb) {
|
||||||
@@ -258,7 +261,7 @@ public class AssemblyConstructorSemantic implements Comparable<AssemblyConstruct
|
|||||||
}
|
}
|
||||||
|
|
||||||
// We can't apply the line number rule unless the sibling's context is an overset
|
// We can't apply the line number rule unless the sibling's context is an overset
|
||||||
if (!comb.ctx.equals(pat.ctx)) {
|
if (!comb.getContext().equals(pat.getContext())) {
|
||||||
return CONTINUE;
|
return CONTINUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -295,7 +298,7 @@ public class AssemblyConstructorSemantic implements Comparable<AssemblyConstruct
|
|||||||
*
|
*
|
||||||
* @param res the combined resolution requirements derived from the subconstructors
|
* @param res the combined resolution requirements derived from the subconstructors
|
||||||
* @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})
|
||||||
* @return the resolution with context changes applied in reverse, or an error
|
* @return the resolution with context changes applied in reverse, or an error
|
||||||
*/
|
*/
|
||||||
public AssemblyResolution solveContextChanges(AssemblyResolvedPatterns res,
|
public AssemblyResolution solveContextChanges(AssemblyResolvedPatterns res,
|
||||||
Map<String, Long> vals) {
|
Map<String, Long> vals) {
|
||||||
@@ -320,12 +323,12 @@ public class AssemblyConstructorSemantic implements Comparable<AssemblyConstruct
|
|||||||
DBG.println("Masked out: " + res.lineToString());
|
DBG.println("Masked out: " + res.lineToString());
|
||||||
|
|
||||||
// Now, solve
|
// Now, solve
|
||||||
AssemblyResolution sol = AssemblyTreeResolver.solveOrBackfill(
|
AssemblyResolution sol = factory.solveOrBackfill(cop.getPatternExpression(), reqval,
|
||||||
cop.getPatternExpression(), reqval, vals, res, "Solution to " + cop);
|
vals, res, "Solution to " + cop);
|
||||||
DBG.println("Solution: " + sol.lineToString());
|
DBG.println("Solution: " + sol.lineToString());
|
||||||
if (sol.isError()) {
|
if (sol.isError()) {
|
||||||
AssemblyResolvedError err = (AssemblyResolvedError) sol;
|
AssemblyResolvedError err = (AssemblyResolvedError) sol;
|
||||||
return AssemblyResolution.error(err.getError(), res);
|
return factory.error(err.getError(), res);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now, forward the new requirements to my parents.
|
// Now, forward the new requirements to my parents.
|
||||||
@@ -333,8 +336,7 @@ public class AssemblyConstructorSemantic implements Comparable<AssemblyConstruct
|
|||||||
AssemblyResolvedPatterns solcon = (AssemblyResolvedPatterns) sol;
|
AssemblyResolvedPatterns solcon = (AssemblyResolvedPatterns) sol;
|
||||||
AssemblyResolvedPatterns check = res.combine(solcon);
|
AssemblyResolvedPatterns check = res.combine(solcon);
|
||||||
if (null == check) {
|
if (null == check) {
|
||||||
return AssemblyResolution.error(
|
return factory.error("A context change caused a conflict: " + sol, res);
|
||||||
"A context change caused a conflict: " + sol, res);
|
|
||||||
}
|
}
|
||||||
res = check;
|
res = check;
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-4
@@ -46,6 +46,7 @@ import ghidra.graph.algo.DijkstraShortestPathsAlgorithm;
|
|||||||
* much larger its LALR(1) parser would become.
|
* much larger its LALR(1) parser would become.
|
||||||
*/
|
*/
|
||||||
public class AssemblyContextGraph implements GImplicitDirectedGraph<Vertex, Edge> {
|
public class AssemblyContextGraph implements GImplicitDirectedGraph<Vertex, Edge> {
|
||||||
|
protected final AbstractAssemblyResolutionFactory<?, ?> factory;
|
||||||
protected final Map<String, Set<AssemblyConstructorSemantic>> semantics =
|
protected final Map<String, Set<AssemblyConstructorSemantic>> semantics =
|
||||||
LazyMap.lazyMap(new HashMap<>(), () -> new HashSet<>());
|
LazyMap.lazyMap(new HashMap<>(), () -> new HashSet<>());
|
||||||
protected final AssemblyGrammar grammar;
|
protected final AssemblyGrammar grammar;
|
||||||
@@ -72,7 +73,9 @@ public class AssemblyContextGraph implements GImplicitDirectedGraph<Vertex, Edge
|
|||||||
* @param lang the language
|
* @param lang the language
|
||||||
* @param grammar the grammar derived from the given language
|
* @param grammar the grammar derived from the given language
|
||||||
*/
|
*/
|
||||||
public AssemblyContextGraph(SleighLanguage lang, AssemblyGrammar grammar) {
|
public AssemblyContextGraph(AbstractAssemblyResolutionFactory<?, ?> factory,
|
||||||
|
SleighLanguage lang, AssemblyGrammar grammar) {
|
||||||
|
this.factory = factory;
|
||||||
this.grammar = grammar;
|
this.grammar = grammar;
|
||||||
this.lang = lang;
|
this.lang = lang;
|
||||||
|
|
||||||
@@ -343,7 +346,7 @@ public class AssemblyContextGraph implements GImplicitDirectedGraph<Vertex, Edge
|
|||||||
Set<Edge> result = new HashSet<>();
|
Set<Edge> result = new HashSet<>();
|
||||||
for (AssemblyConstructorSemantic sem : semantics.get(from.subtable)) {
|
for (AssemblyConstructorSemantic sem : semantics.get(from.subtable)) {
|
||||||
for (AssemblyResolvedPatterns rc : sem.patterns) {
|
for (AssemblyResolvedPatterns rc : sem.patterns) {
|
||||||
AssemblyPatternBlock pattern = rc.ctx;
|
AssemblyPatternBlock pattern = rc.getContext();
|
||||||
AssemblyPatternBlock outer = from.context.combine(pattern);
|
AssemblyPatternBlock outer = from.context.combine(pattern);
|
||||||
if (outer == null) {
|
if (outer == null) {
|
||||||
continue;
|
continue;
|
||||||
@@ -352,8 +355,7 @@ public class AssemblyContextGraph implements GImplicitDirectedGraph<Vertex, Edge
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
AssemblyResolvedPatterns orc =
|
AssemblyResolvedPatterns orc = factory.contextOnly(outer, "For context transition");
|
||||||
AssemblyResolution.contextOnly(outer, "For context transition");
|
|
||||||
AssemblyResolvedPatterns irc = sem.applyContextChangesForward(Map.of(), orc);
|
AssemblyResolvedPatterns irc = sem.applyContextChangesForward(Map.of(), orc);
|
||||||
AssemblyPatternBlock inner = irc.getContext();
|
AssemblyPatternBlock inner = irc.getContext();
|
||||||
|
|
||||||
|
|||||||
+12
-6
@@ -15,11 +15,10 @@
|
|||||||
*/
|
*/
|
||||||
package ghidra.app.plugin.assembler.sleigh.sem;
|
package ghidra.app.plugin.assembler.sleigh.sem;
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.IntStream;
|
import java.util.stream.*;
|
||||||
import java.util.stream.Stream;
|
|
||||||
|
|
||||||
|
import ghidra.app.plugin.assembler.sleigh.tree.AssemblyParseHiddenNode;
|
||||||
import ghidra.app.plugin.assembler.sleigh.tree.AssemblyParseTreeNode;
|
import ghidra.app.plugin.assembler.sleigh.tree.AssemblyParseTreeNode;
|
||||||
import ghidra.app.plugin.processors.sleigh.Constructor;
|
import ghidra.app.plugin.processors.sleigh.Constructor;
|
||||||
import ghidra.app.plugin.processors.sleigh.symbol.SubtableSymbol;
|
import ghidra.app.plugin.processors.sleigh.symbol.SubtableSymbol;
|
||||||
@@ -42,7 +41,7 @@ public class AssemblyHiddenConstructStateGenerator extends AssemblyConstructStat
|
|||||||
* @param subtableSym
|
* @param subtableSym
|
||||||
* @param fromLeft the accumulated patterns from the left sibling or the parent
|
* @param fromLeft the accumulated patterns from the left sibling or the parent
|
||||||
*/
|
*/
|
||||||
public AssemblyHiddenConstructStateGenerator(AssemblyTreeResolver resolver,
|
public AssemblyHiddenConstructStateGenerator(AbstractAssemblyTreeResolver<?> resolver,
|
||||||
SubtableSymbol subtableSym, AssemblyResolvedPatterns fromLeft) {
|
SubtableSymbol subtableSym, AssemblyResolvedPatterns fromLeft) {
|
||||||
super(resolver, null, fromLeft);
|
super(resolver, null, fromLeft);
|
||||||
this.subtableSym = subtableSym;
|
this.subtableSym = subtableSym;
|
||||||
@@ -56,10 +55,17 @@ public class AssemblyHiddenConstructStateGenerator extends AssemblyConstructStat
|
|||||||
.flatMap(sem -> applyConstructor(gc, sem));
|
.flatMap(sem -> applyConstructor(gc, sem));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected AssemblyParseTreeNode getFiller() {
|
||||||
|
return new AssemblyParseHiddenNode(resolver.grammar);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected List<AssemblyParseTreeNode> orderOpNodes(AssemblyConstructorSemantic sem) {
|
protected List<AssemblyParseTreeNode> orderOpNodes(AssemblyConstructorSemantic sem) {
|
||||||
// Just provide null operands, since they're hidden, too.
|
// Just provide hidden operands
|
||||||
Constructor cons = sem.getConstructor();
|
Constructor cons = sem.getConstructor();
|
||||||
return Arrays.asList(new AssemblyParseTreeNode[cons.getNumOperands()]);
|
AssemblyParseTreeNode hidden = getFiller();
|
||||||
|
return IntStream.range(0, cons.getNumOperands())
|
||||||
|
.mapToObj(i -> hidden)
|
||||||
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+20
-12
@@ -22,25 +22,21 @@ import java.util.stream.Stream;
|
|||||||
import ghidra.app.plugin.processors.sleigh.symbol.OperandSymbol;
|
import ghidra.app.plugin.processors.sleigh.symbol.OperandSymbol;
|
||||||
|
|
||||||
public class AssemblyNopState extends AbstractAssemblyState {
|
public class AssemblyNopState extends AbstractAssemblyState {
|
||||||
public AssemblyNopState(AssemblyTreeResolver resolver, List<AssemblyConstructorSemantic> path,
|
|
||||||
int shift, OperandSymbol opSym) {
|
public AssemblyNopState(AbstractAssemblyTreeResolver<?> resolver,
|
||||||
|
List<AssemblyConstructorSemantic> path, int shift, OperandSymbol opSym) {
|
||||||
super(resolver, path, shift, opSym.getMinimumLength());
|
super(resolver, path, shift, opSym.getMinimumLength());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int computeHash() {
|
public int computeHash() {
|
||||||
return "NOP".hashCode();
|
int result = getClass().hashCode();
|
||||||
|
result *= 31;
|
||||||
|
result += Integer.hashCode(shift);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
protected boolean partsEqual(AssemblyNopState that) {
|
||||||
public boolean equals(Object obj) {
|
|
||||||
if (this == obj) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (!(obj instanceof AssemblyNopState)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
AssemblyNopState that = (AssemblyNopState) obj;
|
|
||||||
if (this.resolver != that.resolver) {
|
if (this.resolver != that.resolver) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -50,6 +46,18 @@ public class AssemblyNopState extends AbstractAssemblyState {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (this.getClass() != obj.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
AssemblyNopState that = (AssemblyNopState) obj;
|
||||||
|
return partsEqual(that);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "NOP";
|
return "NOP";
|
||||||
|
|||||||
+4
-5
@@ -39,8 +39,8 @@ public class AssemblyNopStateGenerator
|
|||||||
* @param opSym the operand symbol
|
* @param opSym the operand symbol
|
||||||
* @param fromLeft the accumulated patterns from the left sibling or parent
|
* @param fromLeft the accumulated patterns from the left sibling or parent
|
||||||
*/
|
*/
|
||||||
public AssemblyNopStateGenerator(AssemblyTreeResolver resolver, OperandSymbol opSym,
|
public AssemblyNopStateGenerator(AbstractAssemblyTreeResolver<?> resolver,
|
||||||
AssemblyResolvedPatterns fromLeft) {
|
OperandSymbol opSym, AssemblyResolvedPatterns fromLeft) {
|
||||||
super(resolver, null, fromLeft);
|
super(resolver, null, fromLeft);
|
||||||
this.opSym = opSym;
|
this.opSym = opSym;
|
||||||
}
|
}
|
||||||
@@ -48,8 +48,7 @@ public class AssemblyNopStateGenerator
|
|||||||
@Override
|
@Override
|
||||||
public Stream<AssemblyGeneratedPrototype> generate(GeneratorContext gc) {
|
public Stream<AssemblyGeneratedPrototype> generate(GeneratorContext gc) {
|
||||||
gc.dbg("Generating NOP for " + opSym);
|
gc.dbg("Generating NOP for " + opSym);
|
||||||
return Stream.of(
|
return Stream.of(new AssemblyGeneratedPrototype(
|
||||||
new AssemblyGeneratedPrototype(new AssemblyNopState(resolver, gc.path, gc.shift, opSym),
|
new AssemblyNopState(resolver, gc.path, gc.shift, opSym), fromLeft));
|
||||||
fromLeft));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+39
-14
@@ -47,7 +47,7 @@ public class AssemblyOperandState extends AbstractAssemblyState {
|
|||||||
* @param value the value of the operand
|
* @param value the value of the operand
|
||||||
* @param opSym the operand symbol
|
* @param opSym the operand symbol
|
||||||
*/
|
*/
|
||||||
public AssemblyOperandState(AssemblyTreeResolver resolver,
|
public AssemblyOperandState(AbstractAssemblyTreeResolver<?> resolver,
|
||||||
List<AssemblyConstructorSemantic> path, int shift, AssemblyTerminal terminal,
|
List<AssemblyConstructorSemantic> path, int shift, AssemblyTerminal terminal,
|
||||||
long value, OperandSymbol opSym) {
|
long value, OperandSymbol opSym) {
|
||||||
super(resolver, path, shift, opSym.getMinimumLength());
|
super(resolver, path, shift, opSym.getMinimumLength());
|
||||||
@@ -58,18 +58,17 @@ public class AssemblyOperandState extends AbstractAssemblyState {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int computeHash() {
|
public int computeHash() {
|
||||||
return Objects.hash(getClass(), shift, value, opSym);
|
int result = getClass().hashCode();
|
||||||
|
result *= 31;
|
||||||
|
result += Integer.hashCode(shift);
|
||||||
|
result *= 31;
|
||||||
|
result += Long.hashCode(value);
|
||||||
|
result *= 31;
|
||||||
|
result += opSym.hashCode();
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
protected boolean partsEqual(AssemblyOperandState that) {
|
||||||
public boolean equals(Object obj) {
|
|
||||||
if (this == obj) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (!(obj instanceof AssemblyOperandState)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
AssemblyOperandState that = (AssemblyOperandState) obj;
|
|
||||||
if (this.resolver != that.resolver) {
|
if (this.resolver != that.resolver) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -85,6 +84,18 @@ public class AssemblyOperandState extends AbstractAssemblyState {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (this.getClass() != obj.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
AssemblyOperandState that = (AssemblyOperandState) obj;
|
||||||
|
return partsEqual(that);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return terminal + "=" + value + "(0x" + Long.toHexString(value) + ")";
|
return terminal + "=" + value + "(0x" + Long.toHexString(value) + ")";
|
||||||
@@ -120,7 +131,7 @@ public class AssemblyOperandState extends AbstractAssemblyState {
|
|||||||
DBG.println("Equation: " + symExp + " = " + Long.toHexString(value));
|
DBG.println("Equation: " + symExp + " = " + Long.toHexString(value));
|
||||||
String desc = "Solution to " + opSym + " in " + Long.toHexString(value) + " = " + symExp;
|
String desc = "Solution to " + opSym + " in " + Long.toHexString(value) + " = " + symExp;
|
||||||
AssemblyResolution sol =
|
AssemblyResolution sol =
|
||||||
AssemblyTreeResolver.solveOrBackfill(symExp, value, bitsize, resolver.vals, null, desc);
|
factory.solveOrBackfill(symExp, value, bitsize, resolver.vals, null, desc);
|
||||||
DBG.println("Solution: " + sol);
|
DBG.println("Solution: " + sol);
|
||||||
AssemblyResolution shifted = sol.shift(shift);
|
AssemblyResolution shifted = sol.shift(shift);
|
||||||
DBG.println("Shifted: " + shifted);
|
DBG.println("Shifted: " + shifted);
|
||||||
@@ -143,8 +154,10 @@ public class AssemblyOperandState extends AbstractAssemblyState {
|
|||||||
}
|
}
|
||||||
AssemblyResolution combined = fromRight.combine((AssemblyResolvedPatterns) sol);
|
AssemblyResolution combined = fromRight.combine((AssemblyResolvedPatterns) sol);
|
||||||
if (combined == null) {
|
if (combined == null) {
|
||||||
errors.add(
|
errors.add(factory.newErrorBuilder()
|
||||||
AssemblyResolution.error("Pattern/operand conflict", "Resolving " + terminal));
|
.error("Pattern/operand conflict")
|
||||||
|
.description("Resolving " + terminal)
|
||||||
|
.build());
|
||||||
return Stream.of();
|
return Stream.of();
|
||||||
}
|
}
|
||||||
AssemblyResolvedPatterns pats = (AssemblyResolvedPatterns) combined;
|
AssemblyResolvedPatterns pats = (AssemblyResolvedPatterns) combined;
|
||||||
@@ -152,4 +165,16 @@ public class AssemblyOperandState extends AbstractAssemblyState {
|
|||||||
return Stream.of(pats.withRight(fromRight).withConstructor(null));
|
return Stream.of(pats.withRight(fromRight).withConstructor(null));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public AssemblyTerminal getTerminal() {
|
||||||
|
return terminal;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public OperandSymbol getOperandSymbol() {
|
||||||
|
return opSym;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -35,11 +35,11 @@ public class AssemblyOperandStateGenerator
|
|||||||
* Construct the operand state generator
|
* Construct the operand state generator
|
||||||
*
|
*
|
||||||
* @param resolver the resolver
|
* @param resolver the resolver
|
||||||
* @param node the ndoe from which to generate the state
|
* @param node the node from which to generate the state
|
||||||
* @param fromLeft the accumulated patterns from the left sibling or parent
|
* @param fromLeft the accumulated patterns from the left sibling or parent
|
||||||
* @param opSym the operand symbol
|
* @param opSym the operand symbol
|
||||||
*/
|
*/
|
||||||
public AssemblyOperandStateGenerator(AssemblyTreeResolver resolver,
|
public AssemblyOperandStateGenerator(AbstractAssemblyTreeResolver<?> resolver,
|
||||||
AssemblyParseNumericToken node, OperandSymbol opSym,
|
AssemblyParseNumericToken node, OperandSymbol opSym,
|
||||||
AssemblyResolvedPatterns fromLeft) {
|
AssemblyResolvedPatterns fromLeft) {
|
||||||
super(resolver, node, fromLeft);
|
super(resolver, node, fromLeft);
|
||||||
|
|||||||
+153
-3
@@ -17,8 +17,7 @@ package ghidra.app.plugin.assembler.sleigh.sem;
|
|||||||
|
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.util.Arrays;
|
import java.util.*;
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.concurrent.atomic.AtomicLong;
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
|
|
||||||
import ghidra.app.plugin.assembler.sleigh.expr.MaskedLong;
|
import ghidra.app.plugin.assembler.sleigh.expr.MaskedLong;
|
||||||
@@ -144,7 +143,7 @@ public class AssemblyPatternBlock implements Comparable<AssemblyPatternBlock> {
|
|||||||
AtomicLong msk = new AtomicLong();
|
AtomicLong msk = new AtomicLong();
|
||||||
AtomicLong val = new AtomicLong();
|
AtomicLong val = new AtomicLong();
|
||||||
int i = 0;
|
int i = 0;
|
||||||
for (String hex : str.split(":")) {
|
for (String hex : str.substring(pos).split(":")) {
|
||||||
NumericUtilities.convertHexStringToMaskedValue(msk, val, hex, 2, 0, null);
|
NumericUtilities.convertHexStringToMaskedValue(msk, val, hex, 2, 0, null);
|
||||||
mask[i] = (byte) msk.get();
|
mask[i] = (byte) msk.get();
|
||||||
vals[i] = (byte) val.get();
|
vals[i] = (byte) val.get();
|
||||||
@@ -265,6 +264,7 @@ public class AssemblyPatternBlock implements Comparable<AssemblyPatternBlock> {
|
|||||||
/**
|
/**
|
||||||
* Convert a register value into a pattern block
|
* Convert a register value into a pattern block
|
||||||
*
|
*
|
||||||
|
* <p>
|
||||||
* This is used primarily to compute default context register values, and pass them into an
|
* This is used primarily to compute default context register values, and pass them into an
|
||||||
* assembler.
|
* assembler.
|
||||||
*
|
*
|
||||||
@@ -604,9 +604,132 @@ public class AssemblyPatternBlock implements Comparable<AssemblyPatternBlock> {
|
|||||||
return new AssemblyPatternBlock(offset, newMask, newVals);
|
return new AssemblyPatternBlock(offset, newMask, newVals);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set all bits that are known (1 in mask) in {@code other} to unknown.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Other must have the same or shorter length than this.
|
||||||
|
*
|
||||||
|
* @param other the other pattern block whose mask bits are examined
|
||||||
|
* @return a copy of this pattern with mask bits set to unknown
|
||||||
|
*/
|
||||||
|
public AssemblyPatternBlock maskOut(AssemblyPatternBlock other) {
|
||||||
|
assert this.length() >= other.length();
|
||||||
|
|
||||||
|
byte[] newMask = Arrays.copyOf(this.mask, this.mask.length);
|
||||||
|
byte[] newVals = Arrays.copyOf(this.vals, this.vals.length);
|
||||||
|
|
||||||
|
for (int i = this.offset; i < Math.min(this.length(), other.length()); i++) {
|
||||||
|
if (i < this.offset || i < other.offset) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
newMask[i - this.offset] &= (~other.mask[i - other.offset] & 0xff);
|
||||||
|
newVals[i - this.offset] &= (~other.mask[i - other.offset] & 0xff);
|
||||||
|
}
|
||||||
|
return new AssemblyPatternBlock(offset, newMask, newVals);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*test access*/
|
||||||
|
static byte[] bitShiftRightByteArray(byte[] input, int amount) {
|
||||||
|
byte[] newMask = new byte[input.length];
|
||||||
|
for (int i = input.length - 1; i >= 0; i--) {
|
||||||
|
// Add the lower bits of the next byte to the previously processed byte
|
||||||
|
if (i < input.length - 1) {
|
||||||
|
newMask[i + 1] = (byte) (newMask[i + 1] | ((input[i] << (8 - amount) & 0xff)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shift down the bits of this byte
|
||||||
|
newMask[i] = (byte) (((input[i]) & 0xff) >> amount);
|
||||||
|
}
|
||||||
|
return newMask;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove all unknown bits from both left and right
|
||||||
|
*
|
||||||
|
* @return new value without any left or right unknown bits (but may have unknown bits in the
|
||||||
|
* middle)
|
||||||
|
*/
|
||||||
|
public AssemblyPatternBlock trim() {
|
||||||
|
|
||||||
|
var minNonZeroMask = Integer.MAX_VALUE;
|
||||||
|
var maxNonZeroMask = -1;
|
||||||
|
for (int i = 0; i < this.mask.length; i++) {
|
||||||
|
if (mask[i] != 0) {
|
||||||
|
minNonZeroMask = Math.min(minNonZeroMask, i);
|
||||||
|
maxNonZeroMask = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (maxNonZeroMask == -1) {
|
||||||
|
return AssemblyPatternBlock.nop();
|
||||||
|
}
|
||||||
|
|
||||||
|
var bitShiftAmount = Integer.numberOfTrailingZeros(mask[maxNonZeroMask]);
|
||||||
|
|
||||||
|
var newMask = bitShiftRightByteArray(
|
||||||
|
Arrays.copyOfRange(mask, minNonZeroMask, maxNonZeroMask + 1), bitShiftAmount);
|
||||||
|
var newVals = bitShiftRightByteArray(
|
||||||
|
Arrays.copyOfRange(vals, minNonZeroMask, maxNonZeroMask + 1), bitShiftAmount);
|
||||||
|
|
||||||
|
if (newMask[0] == 0) {
|
||||||
|
newMask = Arrays.copyOfRange(newMask, 1, newMask.length);
|
||||||
|
newVals = Arrays.copyOfRange(newVals, 1, newVals.length);
|
||||||
|
}
|
||||||
|
return new AssemblyPatternBlock(0, newMask, newVals);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an array representing the full value of the pattern
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This is a copy of the {@link #getVals()} array, but with 0s prepended to apply the offset.
|
||||||
|
* See {@link #getOffset()}.
|
||||||
|
*
|
||||||
|
* @return the array
|
||||||
|
*/
|
||||||
|
public byte[] getValsAll() {
|
||||||
|
byte[] out = new byte[offset + vals.length];
|
||||||
|
|
||||||
|
for (int i = 0; i < offset; i++) {
|
||||||
|
out[i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < vals.length; i++) {
|
||||||
|
out[offset + i] = vals[i];
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an array representing the full mask of the pattern
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This is a copy of the {@link #getMask()} array, but with 0s prepended to apply the offset.
|
||||||
|
* See {@link #getOffset()}.
|
||||||
|
*
|
||||||
|
* @return the array
|
||||||
|
*/
|
||||||
|
public byte[] getMaskAll() {
|
||||||
|
byte[] out = new byte[offset + mask.length];
|
||||||
|
|
||||||
|
for (int i = 0; i < offset; i++) {
|
||||||
|
out[i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < mask.length; i++) {
|
||||||
|
out[offset + i] = mask[i];
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the values array
|
* Get the values array
|
||||||
*
|
*
|
||||||
|
* <p>
|
||||||
|
* Modifications to the returned array will affect the pattern block. It is <em>not</em> a copy.
|
||||||
|
* Furthermore, the offset is not incorporated. See {@link #getOffset()}. For a copy of the
|
||||||
|
* array with offset applied, use {@link #getValsAll()}.
|
||||||
|
*
|
||||||
* @return the array
|
* @return the array
|
||||||
*/
|
*/
|
||||||
public byte[] getVals() {
|
public byte[] getVals() {
|
||||||
@@ -616,12 +739,39 @@ public class AssemblyPatternBlock implements Comparable<AssemblyPatternBlock> {
|
|||||||
/**
|
/**
|
||||||
* Get the mask array
|
* Get the mask array
|
||||||
*
|
*
|
||||||
|
* <p>
|
||||||
|
* Modifications to the returned array will affect the pattern block. It is <em>not</em> a copy.
|
||||||
|
* Furthermore, the offset is not incorporated. See {@link #getOffset()}. For a copy of the
|
||||||
|
* array with offset applied, use {@link #getMaskAll()}.
|
||||||
|
*
|
||||||
|
*
|
||||||
* @return the array
|
* @return the array
|
||||||
*/
|
*/
|
||||||
public byte[] getMask() {
|
public byte[] getMask() {
|
||||||
return mask;
|
return mask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mask the given {@code unmasked} value with the mask contained in this pattern block.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* The returned {@link AssemblyPatternBlock} has an identical mask as {@code this} but with a
|
||||||
|
* value taken from the given {@code unmasked}.
|
||||||
|
*
|
||||||
|
* @param unmasked the value to be masked into the result
|
||||||
|
* @return a combination of the given unmasked value and this mask
|
||||||
|
*/
|
||||||
|
public AssemblyPatternBlock getMaskedValue(byte[] unmasked) {
|
||||||
|
assert offset + mask.length <= unmasked.length;
|
||||||
|
var newVals = Arrays.copyOfRange(unmasked, offset, offset + mask.length);
|
||||||
|
|
||||||
|
for (int i = 0; i < newVals.length; i++) {
|
||||||
|
newVals[i] = (byte) ((newVals[i] & mask[i]) & 0xff);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new AssemblyPatternBlock(offset, mask, newVals);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the number of undefined bytes preceding the mask and values arrays
|
* Get the number of undefined bytes preceding the mask and values arrays
|
||||||
*
|
*
|
||||||
|
|||||||
+45
-298
@@ -15,285 +15,23 @@
|
|||||||
*/
|
*/
|
||||||
package ghidra.app.plugin.assembler.sleigh.sem;
|
package ghidra.app.plugin.assembler.sleigh.sem;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import ghidra.app.plugin.assembler.sleigh.expr.MaskedLong;
|
public interface AssemblyResolution extends Comparable<AssemblyResolution> {
|
||||||
import ghidra.app.plugin.processors.sleigh.Constructor;
|
|
||||||
import ghidra.app.plugin.processors.sleigh.expression.PatternExpression;
|
|
||||||
import ghidra.app.plugin.processors.sleigh.pattern.DisjointPattern;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The (often intermediate) result of assembly
|
|
||||||
*
|
|
||||||
* <p>
|
|
||||||
* These may represent a successful construction ({@link AssemblyResolvedPatterns}, a future field
|
|
||||||
* ({@link AssemblyResolvedBackfill}), or an error ({@link AssemblyResolvedError}).
|
|
||||||
*
|
|
||||||
* <p>
|
|
||||||
* This class also provides the static factory methods for constructing any of its subclasses.
|
|
||||||
*/
|
|
||||||
public abstract class AssemblyResolution implements Comparable<AssemblyResolution> {
|
|
||||||
protected final String description;
|
|
||||||
protected final List<AssemblyResolution> children;
|
|
||||||
protected final AssemblyResolution right;
|
|
||||||
|
|
||||||
private boolean hashed = false;
|
|
||||||
private int hash;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
if (!hashed) {
|
|
||||||
hash = computeHash();
|
|
||||||
hashed = true;
|
|
||||||
}
|
|
||||||
return hash;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract int computeHash();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a resolution
|
* {@inheritDoc}
|
||||||
*
|
|
||||||
* @param description a textual description used as part of {@link #toString()}
|
|
||||||
* @param children for record keeping, any children used in constructing this resolution
|
|
||||||
*/
|
|
||||||
AssemblyResolution(String description, List<? extends AssemblyResolution> children,
|
|
||||||
AssemblyResolution right) {
|
|
||||||
this.description = description;
|
|
||||||
this.children = children == null ? List.of() : Collections.unmodifiableList(children);
|
|
||||||
this.right = right;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ********************************************************************************************
|
|
||||||
* Static factory methods
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Build the result of successfully resolving a SLEIGH constructor
|
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* <b>NOTE:</b> This is not used strictly for resolved SLEIGH constructors. It may also be used
|
* Describe this record including indented children, grandchildren, etc., each on its own line.
|
||||||
* to store intermediates, e.g., encoded operands, during constructor resolution.
|
|
||||||
*
|
|
||||||
* @param ins the instruction pattern block
|
|
||||||
* @param ctx the context pattern block
|
|
||||||
* @param description a description of the resolution
|
|
||||||
* @param cons the constructor, or null
|
|
||||||
* @param children the children of this constructor, or null
|
|
||||||
* @return the new resolution
|
|
||||||
*/
|
|
||||||
public static AssemblyResolvedPatterns resolved(AssemblyPatternBlock ins,
|
|
||||||
AssemblyPatternBlock ctx, String description, Constructor cons,
|
|
||||||
List<? extends AssemblyResolution> children, AssemblyResolution right) {
|
|
||||||
return new AssemblyResolvedPatterns(description, cons, children, right, ins, ctx, null,
|
|
||||||
null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Build an instruction-only successful resolution result
|
|
||||||
*
|
|
||||||
* @param ins the instruction pattern block
|
|
||||||
* @param description a description of the resolution
|
|
||||||
* @return the new resolution
|
|
||||||
* @see #resolved(AssemblyPatternBlock, AssemblyPatternBlock, String, Constructor, List, AssemblyResolution)
|
|
||||||
*/
|
|
||||||
public static AssemblyResolvedPatterns instrOnly(AssemblyPatternBlock ins,
|
|
||||||
String description) {
|
|
||||||
return resolved(ins, AssemblyPatternBlock.nop(), description, null, null, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Build a context-only successful resolution result
|
|
||||||
*
|
|
||||||
* @param ctx the context pattern block
|
|
||||||
* @param description a description of the resolution
|
|
||||||
* @return the new resolution
|
|
||||||
* @see #resolved(AssemblyPatternBlock, AssemblyPatternBlock, String, Constructor, List, AssemblyResolution)
|
|
||||||
*/
|
|
||||||
public static AssemblyResolvedPatterns contextOnly(AssemblyPatternBlock ctx,
|
|
||||||
String description) {
|
|
||||||
return resolved(AssemblyPatternBlock.nop(), ctx, description, null, null, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Build a successful resolution result from a SLEIGH constructor's patterns
|
|
||||||
*
|
|
||||||
* @param pat the constructor's pattern
|
|
||||||
* @param description a description of the resolution
|
|
||||||
* @return the new resolution
|
|
||||||
*/
|
|
||||||
public static AssemblyResolvedPatterns fromPattern(DisjointPattern pat, int minLen,
|
|
||||||
String description, Constructor cons) {
|
|
||||||
AssemblyPatternBlock ins = AssemblyPatternBlock.fromPattern(pat, minLen, false);
|
|
||||||
AssemblyPatternBlock ctx = AssemblyPatternBlock.fromPattern(pat, 0, true);
|
|
||||||
return resolved(ins, ctx, description, cons, null, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Build a backfill record to attach to a successful resolution result
|
|
||||||
*
|
|
||||||
* @param exp the expression depending on a missing symbol
|
|
||||||
* @param goal the desired value of the expression
|
|
||||||
* @param inslen the length of instruction portion expected in the future solution
|
|
||||||
* @param description a description of the backfill record
|
|
||||||
* @return the new record
|
|
||||||
*/
|
|
||||||
public static AssemblyResolvedBackfill backfill(PatternExpression exp, MaskedLong goal,
|
|
||||||
int inslen, String description) {
|
|
||||||
return new AssemblyResolvedBackfill(description, exp, goal, inslen, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Obtain a new "blank" resolved SLEIGH constructor record
|
|
||||||
*
|
|
||||||
* @param description a description of the resolution
|
|
||||||
* @param children any children that will be involved in populating this record
|
|
||||||
* @return the new resolution
|
|
||||||
*/
|
|
||||||
public static AssemblyResolvedPatterns nop(String description,
|
|
||||||
List<? extends AssemblyResolution> children, AssemblyResolution right) {
|
|
||||||
return resolved(AssemblyPatternBlock.nop(), AssemblyPatternBlock.nop(), description, null,
|
|
||||||
children, right);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Obtain a new "blank" resolved SLEIGH constructor record
|
|
||||||
*
|
|
||||||
* @param description a description of the resolution
|
|
||||||
* @return the new resolution
|
|
||||||
*/
|
|
||||||
public static AssemblyResolvedPatterns nop(String description) {
|
|
||||||
return resolved(AssemblyPatternBlock.nop(), AssemblyPatternBlock.nop(), description, null,
|
|
||||||
null, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Build an error resolution record
|
|
||||||
*
|
|
||||||
* @param error a description of the error
|
|
||||||
* @param description a description of what the resolver was doing when the error ocurred
|
|
||||||
* @param children any children involved in generating the error
|
|
||||||
* @return the new resolution
|
|
||||||
*/
|
|
||||||
public static AssemblyResolvedError error(String error, String description,
|
|
||||||
List<? extends AssemblyResolution> children, AssemblyResolution right) {
|
|
||||||
return new AssemblyResolvedError(description, children, right, error);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Build an error resolution record
|
|
||||||
*
|
|
||||||
* @param error a description of the error
|
|
||||||
* @param description a description of what the resolver was doing when the error occurred
|
|
||||||
* @return the new resolution
|
|
||||||
*/
|
|
||||||
public static AssemblyResolvedError error(String error, String description) {
|
|
||||||
return new AssemblyResolvedError(description, null, null, error);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Build an error resolution record, based on an intermediate SLEIGH constructor record
|
|
||||||
*
|
|
||||||
* @param error a description of the error
|
|
||||||
* @param res the constructor record that was being populated when the error ocurred
|
|
||||||
* @return the new error resolution
|
|
||||||
*/
|
|
||||||
public static AssemblyResolution error(String error, AssemblyResolvedPatterns res) {
|
|
||||||
return error(error, res.description, res.children, res.right);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ********************************************************************************************
|
|
||||||
* Abstract methods
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if this record describes an error
|
|
||||||
*
|
|
||||||
* @return true if the record is an error
|
|
||||||
*/
|
|
||||||
public abstract boolean isError();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if this record describes a backfill
|
|
||||||
*
|
|
||||||
* @return true if the record is a backfill
|
|
||||||
*/
|
|
||||||
public abstract boolean isBackfill();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Display the resolution result in one line (omitting child details)
|
|
||||||
*
|
|
||||||
* @return the display description
|
|
||||||
*/
|
|
||||||
protected abstract String lineToString();
|
|
||||||
|
|
||||||
/* ********************************************************************************************
|
|
||||||
* Misc
|
|
||||||
*/
|
|
||||||
|
|
||||||
protected List<AssemblyResolution> getAllRight() {
|
|
||||||
List<AssemblyResolution> result = new ArrayList<>();
|
|
||||||
collectAllRight(result);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void collectAllRight(Collection<AssemblyResolution> into) {
|
|
||||||
into.add(this);
|
|
||||||
if (right == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
right.collectAllRight(into);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the child portion of {@link #toString()}
|
|
||||||
*
|
|
||||||
* <p>
|
|
||||||
* If a subclass has another, possible additional, notion of children that it would like to
|
|
||||||
* include in {@link #toString()}, it must override this method.
|
|
||||||
*
|
|
||||||
* @see #hasChildren()
|
|
||||||
* @param indent the current indentation
|
|
||||||
* @return the indented description for each child on its own line
|
|
||||||
*/
|
|
||||||
protected String childrenToString(String indent) {
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
for (AssemblyResolution child : children) {
|
|
||||||
sb.append(child.toString(indent) + "\n");
|
|
||||||
}
|
|
||||||
return sb.substring(0, sb.length() - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Used only by parents: get a multi-line description of this record, indented
|
|
||||||
*
|
|
||||||
* @param indent the current indentation
|
|
||||||
* @return the indented description
|
|
||||||
*/
|
|
||||||
public String toString(String indent) {
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
sb.append(indent);
|
|
||||||
sb.append(lineToString());
|
|
||||||
if (hasChildren()) {
|
|
||||||
sb.append(":\n");
|
|
||||||
String newIndent = indent + " ";
|
|
||||||
sb.append(childrenToString(newIndent));
|
|
||||||
}
|
|
||||||
return sb.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Describe this record including indented children, grandchildren, etc., each on its own line
|
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
String toString();
|
||||||
return toString("");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
String getDescription();
|
||||||
public int compareTo(AssemblyResolution that) {
|
|
||||||
return this.toString().compareTo(that.toString()); // LAZY
|
List<AssemblyResolution> getChildren();
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if this record has children
|
* Check if this record has children
|
||||||
@@ -306,15 +44,30 @@ public abstract class AssemblyResolution implements Comparable<AssemblyResolutio
|
|||||||
* @see #childrenToString(String)
|
* @see #childrenToString(String)
|
||||||
* @return true if this record has children
|
* @return true if this record has children
|
||||||
*/
|
*/
|
||||||
public boolean hasChildren() {
|
boolean hasChildren();
|
||||||
if (children == null) {
|
|
||||||
return false;
|
AssemblyResolution getRight();
|
||||||
}
|
|
||||||
if (children.size() == 0) {
|
/**
|
||||||
return false;
|
* Display the resolution result in one line (omitting child details)
|
||||||
}
|
*
|
||||||
return true;
|
* @return the display description
|
||||||
}
|
*/
|
||||||
|
String lineToString();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if this record describes a backfill
|
||||||
|
*
|
||||||
|
* @return true if the record is a backfill
|
||||||
|
*/
|
||||||
|
boolean isBackfill();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if this record describes an error
|
||||||
|
*
|
||||||
|
* @return true if the record is an error
|
||||||
|
*/
|
||||||
|
boolean isError();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shift the resolution's instruction pattern to the right, if applicable
|
* Shift the resolution's instruction pattern to the right, if applicable
|
||||||
@@ -325,26 +78,20 @@ public abstract class AssemblyResolution implements Comparable<AssemblyResolutio
|
|||||||
* @param amt the number of bytes to shift.
|
* @param amt the number of bytes to shift.
|
||||||
* @return the result
|
* @return the result
|
||||||
*/
|
*/
|
||||||
public abstract AssemblyResolution shift(int amt);
|
AssemblyResolution shift(int amt);
|
||||||
|
|
||||||
/**
|
|
||||||
* Get this same resolution, but without any right siblings
|
|
||||||
*
|
|
||||||
* @return the resolution
|
|
||||||
*/
|
|
||||||
public AssemblyResolution withoutRight() {
|
|
||||||
return withRight(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get this same resolution, but with the given right sibling
|
|
||||||
*
|
|
||||||
* @return the resolution
|
|
||||||
*/
|
|
||||||
public abstract AssemblyResolution withRight(AssemblyResolution right);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get this same resolution, pushing its right siblings down to its children
|
* Get this same resolution, pushing its right siblings down to its children
|
||||||
*/
|
*/
|
||||||
public abstract AssemblyResolution parent(String description, int opCount);
|
AssemblyResolution parent(String description, int opCount);
|
||||||
|
|
||||||
|
void collectAllRight(Collection<AssemblyResolution> into);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used only by parents: get a multi-line description of this record, indented
|
||||||
|
*
|
||||||
|
* @param indent the current indentation
|
||||||
|
* @return the indented description
|
||||||
|
*/
|
||||||
|
String toString(String indent);
|
||||||
}
|
}
|
||||||
|
|||||||
+24
-28
@@ -27,21 +27,21 @@ import ghidra.app.plugin.assembler.sleigh.util.DbgTimer;
|
|||||||
* A set of possible assembly resolutions for a single SLEIGH constructor
|
* A set of possible assembly resolutions for a single SLEIGH constructor
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* Since the assembler works from the leaves up, it unclear in what context a given token appears.
|
* Since the assembler works from the leaves up, it's unclear in what context a given token appears.
|
||||||
* Thus, every possible encoding is collected and passed upward. As resolution continues, many of
|
* Thus, every possible encoding is collected and passed upward. As resolution continues, many of
|
||||||
* the possible encodings are pruned out. When the resolver reaches the root, we end up with every
|
* the possible encodings are pruned out. When the resolver reaches the root, we end up with every
|
||||||
* possible encoding (less some prefixes) of an instruction. This object stores the possible
|
* possible encoding (less some prefixes) of an instruction. This object stores the possible
|
||||||
* encodings, including error records describing the pruned intermediate results.
|
* encodings, including error records describing the pruned intermediate results.
|
||||||
*/
|
*/
|
||||||
public class AssemblyResolutionResults extends AbstractSetDecorator<AssemblyResolution> {
|
public class AssemblyResolutionResults extends AbstractSetDecorator<AssemblyResolution> {
|
||||||
protected static final DbgTimer DBG = AssemblyTreeResolver.DBG;
|
protected static final DbgTimer DBG = AbstractAssemblyTreeResolver.DBG;
|
||||||
|
|
||||||
public interface Applicator {
|
public interface Applicator {
|
||||||
Iterable<? extends AssemblyResolution> getPatterns(AssemblyResolvedPatterns cur);
|
Iterable<? extends AssemblyResolution> getPatterns(AssemblyResolvedPatterns cur);
|
||||||
|
|
||||||
default AssemblyResolvedPatterns setDescription(
|
default AssemblyResolvedPatterns setDescription(
|
||||||
AssemblyResolvedPatterns res, AssemblyResolution from) {
|
AssemblyResolvedPatterns res, AssemblyResolution from) {
|
||||||
AssemblyResolvedPatterns temp = res.withDescription(from.description);
|
AssemblyResolvedPatterns temp = res.withDescription(from.getDescription());
|
||||||
return temp;
|
return temp;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,20 +92,10 @@ public class AssemblyResolutionResults extends AbstractSetDecorator<AssemblyReso
|
|||||||
resolutions = new LinkedHashSet<>();
|
resolutions = new LinkedHashSet<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
private AssemblyResolutionResults(Set<AssemblyResolution> resolutions) {
|
protected AssemblyResolutionResults(Set<AssemblyResolution> resolutions) {
|
||||||
this.resolutions = resolutions;
|
this.resolutions = resolutions;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Construct an immutable single-entry set consisting of the one given resolution
|
|
||||||
*
|
|
||||||
* @param rc the single resolution entry
|
|
||||||
* @return the new resolution set
|
|
||||||
*/
|
|
||||||
public static AssemblyResolutionResults singleton(AssemblyResolvedPatterns rc) {
|
|
||||||
return new AssemblyResolutionResults(Collections.singleton(rc));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean add(AssemblyResolution ar) {
|
public boolean add(AssemblyResolution ar) {
|
||||||
return resolutions.add(ar);
|
return resolutions.add(ar);
|
||||||
@@ -143,21 +133,22 @@ public class AssemblyResolutionResults extends AbstractSetDecorator<AssemblyReso
|
|||||||
return this.resolutions.remove(ar);
|
return this.resolutions.remove(ar);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected AssemblyResolutionResults apply(Applicator applicator) {
|
protected AssemblyResolutionResults apply(AbstractAssemblyResolutionFactory<?, ?> factory,
|
||||||
AssemblyResolutionResults results = new AssemblyResolutionResults();
|
Applicator applicator) {
|
||||||
|
AssemblyResolutionResults results = factory.newAssemblyResolutionResults();
|
||||||
for (AssemblyResolution res : this) {
|
for (AssemblyResolution res : this) {
|
||||||
if (res.isError()) {
|
if (res.isError()) {
|
||||||
results.add(res);
|
results.add(res);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
AssemblyResolvedPatterns rc = (AssemblyResolvedPatterns) res;
|
AssemblyResolvedPatterns rp = (AssemblyResolvedPatterns) res;
|
||||||
DBG.println("Current: " + rc.lineToString());
|
DBG.println("Current: " + rp.lineToString());
|
||||||
for (AssemblyResolution pat : applicator.getPatterns(rc)) {
|
for (AssemblyResolution ar : applicator.getPatterns(rp)) {
|
||||||
DBG.println("Pattern: " + pat.lineToString());
|
DBG.println("Pattern: " + ar.lineToString());
|
||||||
AssemblyResolvedPatterns combined = applicator.combine(rc, pat);
|
AssemblyResolvedPatterns combined = applicator.combine(rp, ar);
|
||||||
DBG.println("Combined: " + (combined == null ? "(null)" : combined.lineToString()));
|
DBG.println("Combined: " + (combined == null ? "(null)" : combined.lineToString()));
|
||||||
if (combined == null) {
|
if (combined == null) {
|
||||||
results.add(AssemblyResolution.error(applicator.describeError(rc, pat), rc));
|
results.add(factory.error(applicator.describeError(rp, ar), ar));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
results.add(applicator.finish(combined));
|
results.add(applicator.finish(combined));
|
||||||
@@ -166,14 +157,19 @@ public class AssemblyResolutionResults extends AbstractSetDecorator<AssemblyReso
|
|||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected AssemblyResolutionResults apply(
|
protected AssemblyResolutionResults apply(AbstractAssemblyResolutionFactory<?, ?> factory,
|
||||||
Function<AssemblyResolvedPatterns, AssemblyResolution> function) {
|
Function<AssemblyResolvedPatterns, AssemblyResolution> function) {
|
||||||
return stream().map(res -> {
|
return stream().map(res -> {
|
||||||
assert !(res instanceof AssemblyResolvedBackfill);
|
if (res instanceof AssemblyResolvedBackfill) {
|
||||||
if (res.isError()) {
|
throw new AssertionError();
|
||||||
return res;
|
|
||||||
}
|
}
|
||||||
return function.apply((AssemblyResolvedPatterns) res);
|
if (res instanceof AssemblyResolvedError err) {
|
||||||
}).collect(Collectors.toCollection(AssemblyResolutionResults::new));
|
return err;
|
||||||
|
}
|
||||||
|
if (res instanceof AssemblyResolvedPatterns rp) {
|
||||||
|
return function.apply(rp);
|
||||||
|
}
|
||||||
|
throw new AssertionError();
|
||||||
|
}).collect(Collectors.toCollection(factory::newAssemblyResolutionResults));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-104
@@ -17,66 +17,9 @@ package ghidra.app.plugin.assembler.sleigh.sem;
|
|||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import ghidra.app.plugin.assembler.sleigh.expr.*;
|
import ghidra.app.plugin.assembler.sleigh.expr.RecursiveDescentSolver;
|
||||||
import ghidra.app.plugin.processors.sleigh.expression.PatternExpression;
|
|
||||||
|
|
||||||
/**
|
public interface AssemblyResolvedBackfill extends AssemblyResolution {
|
||||||
* A {@link AssemblyResolution} indicating the need to solve an expression in the future
|
|
||||||
*
|
|
||||||
* <p>
|
|
||||||
* Such records are collected within a {@link AssemblyResolvedPatterns} and then solved just before
|
|
||||||
* the final result(s) are assembled. This is typically required by instructions that refer to the
|
|
||||||
* {@code inst_next} symbol.
|
|
||||||
*
|
|
||||||
* <p>
|
|
||||||
* <b>NOTE:</b> These are used internally. The user ought never to see these from the assembly API.
|
|
||||||
*/
|
|
||||||
public class AssemblyResolvedBackfill extends AssemblyResolution {
|
|
||||||
protected final PatternExpression exp;
|
|
||||||
protected final MaskedLong goal;
|
|
||||||
protected final int inslen;
|
|
||||||
protected final int offset;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected int computeHash() {
|
|
||||||
int result = 0;
|
|
||||||
result += exp.hashCode();
|
|
||||||
result *= 31;
|
|
||||||
result += goal.hashCode();
|
|
||||||
result *= 31;
|
|
||||||
result += inslen;
|
|
||||||
result *= 31;
|
|
||||||
result += offset;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see {@link AssemblyResolution#backfill(PatternExpression, MaskedLong, Map, int, String)}
|
|
||||||
*/
|
|
||||||
AssemblyResolvedBackfill(String description, PatternExpression exp, MaskedLong goal, int inslen,
|
|
||||||
int offset) {
|
|
||||||
super(description, null, null);
|
|
||||||
this.exp = exp;
|
|
||||||
this.goal = goal;
|
|
||||||
this.inslen = inslen;
|
|
||||||
this.offset = offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Duplicate this record
|
|
||||||
*
|
|
||||||
* @return the duplicate
|
|
||||||
*/
|
|
||||||
AssemblyResolvedBackfill copy() {
|
|
||||||
AssemblyResolvedBackfill cp =
|
|
||||||
new AssemblyResolvedBackfill(description, exp, goal, inslen, offset);
|
|
||||||
return cp;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AssemblyResolvedBackfill withRight(AssemblyResolution right) {
|
|
||||||
throw new AssertionError();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the expected length of the instruction portion of the future encoding
|
* Get the expected length of the instruction portion of the future encoding
|
||||||
@@ -86,39 +29,15 @@ public class AssemblyResolvedBackfill extends AssemblyResolution {
|
|||||||
*
|
*
|
||||||
* @return the total expected length (including the offset)
|
* @return the total expected length (including the offset)
|
||||||
*/
|
*/
|
||||||
public int getInstructionLength() {
|
int getInstructionLength();
|
||||||
return offset + inslen;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isError() {
|
AssemblyResolvedBackfill shift(int amt);
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isBackfill() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected String lineToString() {
|
|
||||||
return "Backfill (len:" + inslen + ",off:" + offset + ") " + goal + " := " + exp + " (" +
|
|
||||||
description + ")";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AssemblyResolvedBackfill shift(int amt) {
|
|
||||||
return new AssemblyResolvedBackfill(description, exp, goal, inslen, offset + amt);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AssemblyResolution parent(String description, int opCount) {
|
|
||||||
throw new AssertionError();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attempt (again) to solve the expression that generated this backfill record
|
* Attempt (again) to solve the expression that generated this backfill record
|
||||||
*
|
*
|
||||||
|
* <p>
|
||||||
* This will attempt to solve the same expression and goal again, using the same parameters as
|
* This will attempt to solve the same expression and goal again, using the same parameters as
|
||||||
* were given to the original attempt, except with additional defined symbols. Typically, the
|
* were given to the original attempt, except with additional defined symbols. Typically, the
|
||||||
* symbol that required backfill is {@code inst_next}. This method will not throw
|
* symbol that required backfill is {@code inst_next}. This method will not throw
|
||||||
@@ -130,22 +49,6 @@ public class AssemblyResolvedBackfill extends AssemblyResolution {
|
|||||||
* @param vals the defined symbols, usually the same, but with the missing symbol(s).
|
* @param vals the defined symbols, usually the same, but with the missing symbol(s).
|
||||||
* @return the solution result
|
* @return the solution result
|
||||||
*/
|
*/
|
||||||
public AssemblyResolution solve(RecursiveDescentSolver solver, Map<String, Long> vals,
|
AssemblyResolution solve(RecursiveDescentSolver solver, Map<String, Long> vals,
|
||||||
AssemblyResolvedPatterns cur) {
|
AssemblyResolvedPatterns cur);
|
||||||
try {
|
|
||||||
AssemblyResolution ar =
|
|
||||||
solver.solve(exp, goal, vals, cur.truncate(offset), description);
|
|
||||||
if (ar.isError()) {
|
|
||||||
return ar;
|
|
||||||
}
|
|
||||||
AssemblyResolvedPatterns rc = (AssemblyResolvedPatterns) ar;
|
|
||||||
return rc.shift(offset);
|
|
||||||
}
|
|
||||||
catch (NeedsBackfillException e) {
|
|
||||||
return AssemblyResolution.error("Solution still requires backfill", description);
|
|
||||||
}
|
|
||||||
catch (UnsupportedOperationException e) {
|
|
||||||
return AssemblyResolution.error("Unsupported: " + e.getMessage(), description);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-77
@@ -15,83 +15,8 @@
|
|||||||
*/
|
*/
|
||||||
package ghidra.app.plugin.assembler.sleigh.sem;
|
package ghidra.app.plugin.assembler.sleigh.sem;
|
||||||
|
|
||||||
import java.util.List;
|
public interface AssemblyResolvedError extends AssemblyResolution {
|
||||||
|
|
||||||
/**
|
String getError();
|
||||||
* A {@link AssemblyResolution} indicating the occurrence of a (usually semantic) error
|
|
||||||
*
|
|
||||||
* <p>
|
|
||||||
* The description should indicate where the error occurred. The error message should explain the
|
|
||||||
* actual error. To help the user diagnose the nature of the error, errors in sub-constructors
|
|
||||||
* should be placed as children of an error given by the parent constructor.
|
|
||||||
*/
|
|
||||||
public class AssemblyResolvedError extends AssemblyResolution {
|
|
||||||
protected final String error;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected int computeHash() {
|
|
||||||
return error.hashCode();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object obj) {
|
|
||||||
if (!(obj instanceof AssemblyResolvedError)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
AssemblyResolvedError that = (AssemblyResolvedError) obj;
|
|
||||||
if (!this.error.equals(that.error)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see AssemblyResolution#error(String, String, List)
|
|
||||||
*/
|
|
||||||
AssemblyResolvedError(String description, List<? extends AssemblyResolution> children,
|
|
||||||
AssemblyResolution right, String error) {
|
|
||||||
super(description, children, right);
|
|
||||||
AssemblyTreeResolver.DBG.println(error);
|
|
||||||
this.error = error;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isError() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isBackfill() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a description of the error
|
|
||||||
*
|
|
||||||
* @return the description
|
|
||||||
*/
|
|
||||||
public String getError() {
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String lineToString() {
|
|
||||||
return error + " (" + description + ")";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AssemblyResolution shift(int amt) {
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AssemblyResolution withRight(AssemblyResolution right) {
|
|
||||||
return new AssemblyResolvedError(description, null, right, error);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AssemblyResolution parent(String description, int opCount) {
|
|
||||||
List<AssemblyResolution> allRight = getAllRight();
|
|
||||||
return new AssemblyResolvedError(description, allRight, null, error);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
+198
-655
File diff suppressed because it is too large
Load Diff
+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.sem;
|
||||||
|
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import ghidra.app.plugin.assembler.sleigh.tree.AssemblyParseToken;
|
||||||
|
import ghidra.app.plugin.processors.sleigh.symbol.OperandSymbol;
|
||||||
|
|
||||||
|
public class AssemblyStringStateGenerator
|
||||||
|
extends AbstractAssemblyStateGenerator<AssemblyParseToken> {
|
||||||
|
protected final OperandSymbol opSym;
|
||||||
|
|
||||||
|
public AssemblyStringStateGenerator(AbstractAssemblyTreeResolver<?> resolver,
|
||||||
|
AssemblyParseToken node, OperandSymbol opSym, AssemblyResolvedPatterns fromLeft) {
|
||||||
|
super(resolver, node, fromLeft);
|
||||||
|
this.opSym = opSym;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Stream<AssemblyGeneratedPrototype> generate(GeneratorContext gc) {
|
||||||
|
return Stream.of(new AssemblyGeneratedPrototype(
|
||||||
|
new AssemblyOperandState(resolver, gc.path, gc.shift, node.getSym(), 0, opSym),
|
||||||
|
fromLeft));
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user