mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-05-09 20:18:01 +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() {
|
||||
@Override
|
||||
public AssemblyResolvedPatterns select(AssemblyResolutionResults rr,
|
||||
AssemblyPatternBlock ctx) throws AssemblySemanticException {
|
||||
public Selection select(AssemblyResolutionResults rr, AssemblyPatternBlock ctx)
|
||||
throws AssemblySemanticException {
|
||||
for (AssemblyResolvedPatterns res : filterCompatibleAndSort(rr, ctx)) {
|
||||
byte[] ins = res.getInstruction().getVals();
|
||||
// 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));
|
||||
continue;
|
||||
}
|
||||
return AssemblyResolution.resolved(res.getInstruction().fillMask(),
|
||||
res.getContext(), "Selected", null, null, null);
|
||||
return new Selection(res.getInstruction().fillMask(), res.getContext());
|
||||
}
|
||||
throw new AssemblySemanticException(semanticErrors);
|
||||
}
|
||||
|
||||
@@ -63,8 +63,8 @@ public class AssemblyThrasherDevScript extends GhidraScript {
|
||||
}
|
||||
|
||||
@Override
|
||||
public AssemblyResolvedPatterns select(AssemblyResolutionResults rr,
|
||||
AssemblyPatternBlock ctx) throws AssemblySemanticException {
|
||||
public Selection select(AssemblyResolutionResults rr, AssemblyPatternBlock ctx)
|
||||
throws AssemblySemanticException {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
boolean gotOne = false;
|
||||
boolean failedOne = false;
|
||||
|
||||
+2
-199
@@ -15,15 +15,7 @@
|
||||
*/
|
||||
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;
|
||||
import ghidra.app.plugin.assembler.sleigh.sem.AssemblyResolvedPatterns;
|
||||
|
||||
/**
|
||||
* 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
|
||||
* language.
|
||||
*/
|
||||
public interface Assembler {
|
||||
/**
|
||||
* 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);
|
||||
public interface Assembler extends GenericAssembler<AssemblyResolvedPatterns> {
|
||||
}
|
||||
|
||||
+5
-29
@@ -15,42 +15,18 @@
|
||||
*/
|
||||
package ghidra.app.plugin.assembler;
|
||||
|
||||
import ghidra.program.model.lang.Language;
|
||||
import ghidra.program.model.lang.LanguageID;
|
||||
import ghidra.app.plugin.assembler.sleigh.sem.AssemblyResolvedPatterns;
|
||||
import ghidra.program.model.listing.Program;
|
||||
|
||||
/**
|
||||
* An interface to build an assembler for a given language
|
||||
*/
|
||||
public interface AssemblerBuilder {
|
||||
/**
|
||||
* Get the ID of the language for which this instance builds an assembler
|
||||
*
|
||||
* @return the language ID
|
||||
*/
|
||||
public LanguageID getLanguageID();
|
||||
public interface AssemblerBuilder
|
||||
extends GenericAssemblerBuilder<AssemblyResolvedPatterns, Assembler> {
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
@Override
|
||||
public Assembler 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
|
||||
*/
|
||||
@Override
|
||||
public Assembler getAssembler(AssemblySelector selector, Program program);
|
||||
}
|
||||
|
||||
+14
-5
@@ -118,6 +118,16 @@ public class AssemblySelector {
|
||||
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.
|
||||
*
|
||||
@@ -134,16 +144,15 @@ public class AssemblySelector {
|
||||
* @param rr the collection of resolved constructors
|
||||
* @param ctx the applicable context.
|
||||
* @return a single resolved constructor with a full instruction mask.
|
||||
* @throws AssemblySemanticException
|
||||
* @throws AssemblySemanticException if all the given results are semantic errors
|
||||
*/
|
||||
public AssemblyResolvedPatterns select(AssemblyResolutionResults rr,
|
||||
AssemblyPatternBlock ctx) throws AssemblySemanticException {
|
||||
public Selection select(AssemblyResolutionResults rr, AssemblyPatternBlock ctx)
|
||||
throws AssemblySemanticException {
|
||||
List<AssemblyResolvedPatterns> sorted = filterCompatibleAndSort(rr, ctx);
|
||||
|
||||
// Pick just the first
|
||||
AssemblyResolvedPatterns res = sorted.get(0);
|
||||
// Just set the mask to ffs (effectively choosing 0 for the omitted bits)
|
||||
return AssemblyResolution.resolved(res.getInstruction().fillMask(), res.getContext(),
|
||||
"Selected", null, null, null);
|
||||
return new Selection(res.getInstruction().fillMask(), res.getContext());
|
||||
}
|
||||
}
|
||||
|
||||
+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;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
|
||||
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.symbol.AssemblyNumericSymbols;
|
||||
import ghidra.app.plugin.assembler.sleigh.util.DbgTimer;
|
||||
import ghidra.app.plugin.assembler.sleigh.tree.AssemblyParseBranch;
|
||||
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;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.listing.Program;
|
||||
|
||||
/**
|
||||
* 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
|
||||
* the assembler, please use {@link Assemblers#getAssembler(Program)} or similar.
|
||||
*/
|
||||
public class SleighAssembler implements Assembler {
|
||||
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 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;
|
||||
public class SleighAssembler extends AbstractSleighAssembler<AssemblyResolvedPatterns>
|
||||
implements Assembler {
|
||||
|
||||
/**
|
||||
* Construct a SleighAssembler.
|
||||
@@ -84,13 +42,11 @@ public class SleighAssembler implements Assembler {
|
||||
* @param defaultContext the default context for the language
|
||||
* @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) {
|
||||
this(selector, (SleighLanguage) program.getLanguage(), parser, defaultContext, ctxGraph);
|
||||
this.program = program;
|
||||
|
||||
this.listing = program.getListing();
|
||||
this.memory = program.getMemory();
|
||||
super(factory, selector, program, parser, defaultContext, ctxGraph);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -105,182 +61,16 @@ public class SleighAssembler implements Assembler {
|
||||
* @param defaultContext the default context for the language
|
||||
* @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) {
|
||||
this.selector = selector;
|
||||
this.lang = lang;
|
||||
this.parser = parser;
|
||||
this.defaultContext = defaultContext;
|
||||
this.ctxGraph = ctxGraph;
|
||||
super(factory, selector, lang, parser, defaultContext, ctxGraph);
|
||||
}
|
||||
|
||||
@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 = getContextAt(at);
|
||||
ctx = ctx.fillMask();
|
||||
return resolveTree(parse, at, ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AssemblyResolutionResults resolveTree(AssemblyParseResult parse, Address at,
|
||||
protected AssemblyTreeResolver newResolver(Address at, AssemblyParseBranch tree,
|
||||
AssemblyPatternBlock ctx) {
|
||||
if (parse.isError()) {
|
||||
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);
|
||||
return new AssemblyTreeResolver(factory, lang, at, tree, ctx, ctxGraph);
|
||||
}
|
||||
}
|
||||
|
||||
+17
-333
@@ -15,29 +15,11 @@
|
||||
*/
|
||||
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.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.app.plugin.processors.sleigh.SleighLanguage;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.util.SystemUtilities;
|
||||
|
||||
/**
|
||||
* 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
|
||||
* and expertise.
|
||||
*/
|
||||
public class SleighAssemblerBuilder implements AssemblerBuilder {
|
||||
protected static final DbgTimer dbg =
|
||||
SystemUtilities.isInTestingBatchMode() ? DbgTimer.INACTIVE : DbgTimer.ACTIVE;
|
||||
|
||||
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<>();
|
||||
public class SleighAssemblerBuilder
|
||||
extends AbstractSleighAssemblerBuilder<AssemblyResolvedPatterns, Assembler>
|
||||
implements AssemblerBuilder {
|
||||
|
||||
/**
|
||||
* Construct an assembler builder for the given SLEIGH language
|
||||
@@ -321,319 +292,32 @@ public class SleighAssemblerBuilder implements AssemblerBuilder {
|
||||
* @param lang the language
|
||||
*/
|
||||
public SleighAssemblerBuilder(SleighLanguage lang) {
|
||||
this.lang = 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);
|
||||
}
|
||||
super(lang);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LanguageID getLanguageID() {
|
||||
return lang.getLanguageID();
|
||||
protected AbstractAssemblyResolutionFactory< //
|
||||
AssemblyResolvedPatterns, AssemblyResolvedBackfill> newResolutionFactory() {
|
||||
return new DefaultAssemblyResolutionFactory();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SleighLanguage getLanguage() {
|
||||
return lang;
|
||||
protected SleighAssembler newAssembler(AssemblySelector selector) {
|
||||
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
|
||||
public SleighAssembler getAssembler(AssemblySelector selector) {
|
||||
generateAssembler();
|
||||
SleighAssembler asm = new SleighAssembler(selector, lang, parser, defaultContext, ctxGraph);
|
||||
return asm;
|
||||
return (SleighAssembler) super.getAssembler(selector);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SleighAssembler getAssembler(AssemblySelector selector, Program program) {
|
||||
generateAssembler();
|
||||
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;
|
||||
return (SleighAssembler) super.getAssembler(selector, program);
|
||||
}
|
||||
}
|
||||
|
||||
+27
-25
@@ -18,8 +18,7 @@ package ghidra.app.plugin.assembler.sleigh.expr;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import ghidra.app.plugin.assembler.sleigh.sem.AssemblyResolution;
|
||||
import ghidra.app.plugin.assembler.sleigh.sem.AssemblyResolvedPatterns;
|
||||
import ghidra.app.plugin.assembler.sleigh.sem.*;
|
||||
import ghidra.app.plugin.processors.sleigh.expression.BinaryExpression;
|
||||
import ghidra.app.plugin.processors.sleigh.expression.PatternExpression;
|
||||
|
||||
@@ -36,9 +35,9 @@ public abstract class AbstractBinaryExpressionSolver<T extends BinaryExpression>
|
||||
}
|
||||
|
||||
@Override
|
||||
public AssemblyResolution solve(T exp, MaskedLong goal, Map<String, Long> vals,
|
||||
AssemblyResolvedPatterns cur, Set<SolverHint> hints, String description)
|
||||
throws NeedsBackfillException {
|
||||
public AssemblyResolution solve(AbstractAssemblyResolutionFactory<?, ?> factory,
|
||||
T exp, MaskedLong goal, Map<String, Long> vals, AssemblyResolvedPatterns cur,
|
||||
Set<SolverHint> hints, String description) throws NeedsBackfillException {
|
||||
MaskedLong lval = solver.getValue(exp.getLeft(), vals, cur);
|
||||
MaskedLong rval = solver.getValue(exp.getRight(), vals, cur);
|
||||
|
||||
@@ -58,26 +57,27 @@ public abstract class AbstractBinaryExpressionSolver<T extends BinaryExpression>
|
||||
try {
|
||||
if (lval != null && rval != null) {
|
||||
MaskedLong cval = compute(lval, rval);
|
||||
return ConstantValueSolver.checkConstAgrees(cval, goal, description);
|
||||
return ConstantValueSolver.checkConstAgrees(factory, cval, goal, description);
|
||||
}
|
||||
else if (lval != null) {
|
||||
return solveRightSide(exp.getRight(), lval, goal, vals, cur, hints,
|
||||
return solveRightSide(factory, exp.getRight(), lval, goal, vals, cur, hints,
|
||||
description);
|
||||
}
|
||||
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 {
|
||||
// Each solver may provide a strategy for solving expression where both sides are
|
||||
// variable, e.g., two fields being concatenated via OR.
|
||||
return solveTwoSided(exp, goal, vals, cur, hints, description);
|
||||
return solveTwoSided(factory, exp, goal, vals, cur, hints, description);
|
||||
}
|
||||
}
|
||||
catch (NeedsBackfillException e) {
|
||||
throw e;
|
||||
}
|
||||
catch (SolverException e) {
|
||||
return AssemblyResolution.error(e.getMessage(), description);
|
||||
return factory.newErrorBuilder().error(e.getMessage()).description(description).build();
|
||||
}
|
||||
catch (AssertionError e) {
|
||||
dbg.println("While solving: " + exp + " (" + description + ")");
|
||||
@@ -85,23 +85,25 @@ public abstract class AbstractBinaryExpressionSolver<T extends BinaryExpression>
|
||||
}
|
||||
}
|
||||
|
||||
protected AssemblyResolution solveLeftSide(PatternExpression lexp, MaskedLong rval,
|
||||
MaskedLong goal, Map<String, Long> vals, AssemblyResolvedPatterns cur,
|
||||
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,
|
||||
protected AssemblyResolution solveLeftSide(AbstractAssemblyResolutionFactory<?, ?> factory,
|
||||
PatternExpression lexp, MaskedLong rval, MaskedLong goal, Map<String, Long> vals,
|
||||
AssemblyResolvedPatterns cur, Set<SolverHint> hints, String description)
|
||||
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_");
|
||||
}
|
||||
|
||||
|
||||
+4
-5
@@ -18,8 +18,7 @@ package ghidra.app.plugin.assembler.sleigh.expr;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import ghidra.app.plugin.assembler.sleigh.sem.AssemblyResolution;
|
||||
import ghidra.app.plugin.assembler.sleigh.sem.AssemblyResolvedPatterns;
|
||||
import ghidra.app.plugin.assembler.sleigh.sem.*;
|
||||
import ghidra.app.plugin.assembler.sleigh.util.DbgTimer;
|
||||
import ghidra.app.plugin.processors.sleigh.expression.PatternExpression;
|
||||
|
||||
@@ -54,9 +53,9 @@ public abstract class AbstractExpressionSolver<T extends PatternExpression> {
|
||||
* @return the resolution
|
||||
* @throws NeedsBackfillException if the expression refers to an undefined symbol
|
||||
*/
|
||||
public abstract AssemblyResolution solve(T exp, MaskedLong goal, Map<String, Long> vals,
|
||||
AssemblyResolvedPatterns cur, Set<SolverHint> hints,
|
||||
String description) throws NeedsBackfillException;
|
||||
public abstract AssemblyResolution solve(AbstractAssemblyResolutionFactory<?, ?> factory,
|
||||
T exp, MaskedLong goal, Map<String, Long> vals, AssemblyResolvedPatterns cur,
|
||||
Set<SolverHint> hints, String description) throws NeedsBackfillException;
|
||||
|
||||
/**
|
||||
* 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.Set;
|
||||
|
||||
import ghidra.app.plugin.assembler.sleigh.sem.AssemblyResolution;
|
||||
import ghidra.app.plugin.assembler.sleigh.sem.AssemblyResolvedPatterns;
|
||||
import ghidra.app.plugin.assembler.sleigh.sem.*;
|
||||
import ghidra.app.plugin.processors.sleigh.expression.UnaryExpression;
|
||||
|
||||
/**
|
||||
@@ -35,18 +34,18 @@ public abstract class AbstractUnaryExpressionSolver<T extends UnaryExpression>
|
||||
}
|
||||
|
||||
@Override
|
||||
public AssemblyResolution solve(T exp, MaskedLong goal, Map<String, Long> vals,
|
||||
AssemblyResolvedPatterns cur, Set<SolverHint> hints, String description)
|
||||
throws NeedsBackfillException {
|
||||
public AssemblyResolution solve(AbstractAssemblyResolutionFactory<?, ?> factory, T exp,
|
||||
MaskedLong goal, Map<String, Long> vals, AssemblyResolvedPatterns cur,
|
||||
Set<SolverHint> hints, String description) throws NeedsBackfillException {
|
||||
MaskedLong uval = solver.getValue(exp.getUnary(), vals, cur);
|
||||
try {
|
||||
if (uval != null && uval.isFullyDefined()) {
|
||||
MaskedLong cval = compute(uval);
|
||||
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);
|
||||
}
|
||||
/*
|
||||
|
||||
+12
-10
@@ -18,8 +18,7 @@ package ghidra.app.plugin.assembler.sleigh.expr;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import ghidra.app.plugin.assembler.sleigh.sem.AssemblyResolution;
|
||||
import ghidra.app.plugin.assembler.sleigh.sem.AssemblyResolvedPatterns;
|
||||
import ghidra.app.plugin.assembler.sleigh.sem.*;
|
||||
import ghidra.app.plugin.processors.sleigh.expression.ConstantValue;
|
||||
|
||||
/**
|
||||
@@ -36,11 +35,11 @@ public class ConstantValueSolver extends AbstractExpressionSolver<ConstantValue>
|
||||
}
|
||||
|
||||
@Override
|
||||
public AssemblyResolution solve(ConstantValue cv, MaskedLong goal, Map<String, Long> vals,
|
||||
AssemblyResolvedPatterns cur, Set<SolverHint> hints,
|
||||
String description) {
|
||||
public AssemblyResolution solve(AbstractAssemblyResolutionFactory<?, ?> factory,
|
||||
ConstantValue cv, MaskedLong goal, Map<String, Long> vals, AssemblyResolvedPatterns cur,
|
||||
Set<SolverHint> hints, String description) {
|
||||
MaskedLong value = getValue(cv, vals, cur);
|
||||
return checkConstAgrees(value, goal, description);
|
||||
return checkConstAgrees(factory, value, goal, description);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -60,12 +59,15 @@ public class ConstantValueSolver extends AbstractExpressionSolver<ConstantValue>
|
||||
return MaskedLong.fromLong(cv.getValue());
|
||||
}
|
||||
|
||||
static AssemblyResolution checkConstAgrees(MaskedLong value, MaskedLong goal,
|
||||
static AssemblyResolution checkConstAgrees(
|
||||
AbstractAssemblyResolutionFactory<?, ?> factory, MaskedLong value, MaskedLong goal,
|
||||
String description) {
|
||||
if (!value.agrees(goal)) {
|
||||
return AssemblyResolution.error(
|
||||
"Constant value " + value + " does not agree with child requirements", description);
|
||||
return factory.newErrorBuilder()
|
||||
.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
|
||||
public AssemblyResolution solve(ContextField cf, MaskedLong goal, Map<String, Long> vals,
|
||||
AssemblyResolvedPatterns cur, Set<SolverHint> hints, String description) {
|
||||
public AssemblyResolution solve(AbstractAssemblyResolutionFactory<?, ?> factory,
|
||||
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.
|
||||
if (!goal.isInRange(cf.maxValue(), cf.hasSignbit())) {
|
||||
return AssemblyResolution.error("Value " + goal + " is not valid for " + cf,
|
||||
description);
|
||||
return factory.newErrorBuilder()
|
||||
.error("Value " + goal + " is not valid for " + cf)
|
||||
.description(description)
|
||||
.build();
|
||||
}
|
||||
AssemblyPatternBlock block = AssemblyPatternBlock.fromContextField(cf, goal);
|
||||
return AssemblyResolution.contextOnly(block, description);
|
||||
return factory.contextOnly(block, description);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+10
-8
@@ -39,12 +39,14 @@ public class EndInstructionValueSolver extends AbstractExpressionSolver<EndInstr
|
||||
}
|
||||
|
||||
@Override
|
||||
public AssemblyResolution solve(EndInstructionValue iv, MaskedLong goal, Map<String, Long> vals,
|
||||
AssemblyResolvedPatterns cur, Set<SolverHint> hints, String description) {
|
||||
throw new AssertionError(
|
||||
"INTERNAL: Should never be asked to solve for " + AssemblyTreeResolver.INST_NEXT);
|
||||
public AssemblyResolution solve(AbstractAssemblyResolutionFactory<?, ?> factory,
|
||||
EndInstructionValue exp, MaskedLong goal, Map<String, Long> vals,
|
||||
AssemblyResolvedPatterns cur, Set<SolverHint> hints, String description)
|
||||
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
|
||||
* solve INST_NEXT == const.
|
||||
*/
|
||||
@@ -53,9 +55,9 @@ public class EndInstructionValueSolver extends AbstractExpressionSolver<EndInstr
|
||||
@Override
|
||||
public MaskedLong getValue(EndInstructionValue iv, Map<String, Long> vals,
|
||||
AssemblyResolvedPatterns cur) throws NeedsBackfillException {
|
||||
Long instNext = vals.get(AssemblyTreeResolver.INST_NEXT);
|
||||
Long instNext = vals.get(AbstractAssemblyTreeResolver.INST_NEXT);
|
||||
if (instNext == null) {
|
||||
throw new NeedsBackfillException(AssemblyTreeResolver.INST_NEXT);
|
||||
throw new NeedsBackfillException(AbstractAssemblyTreeResolver.INST_NEXT);
|
||||
}
|
||||
return MaskedLong.fromLong(instNext);
|
||||
}
|
||||
@@ -68,7 +70,7 @@ public class EndInstructionValueSolver extends AbstractExpressionSolver<EndInstr
|
||||
@Override
|
||||
public MaskedLong valueForResolution(EndInstructionValue exp, Map<String, Long> vals,
|
||||
AssemblyResolvedPatterns rc) {
|
||||
Long instNext = vals.get(AssemblyTreeResolver.INST_NEXT);
|
||||
Long instNext = vals.get(AbstractAssemblyTreeResolver.INST_NEXT);
|
||||
if (instNext == null) {
|
||||
/**
|
||||
* 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.Set;
|
||||
|
||||
import ghidra.app.plugin.assembler.sleigh.sem.AssemblyResolution;
|
||||
import ghidra.app.plugin.assembler.sleigh.sem.AssemblyResolvedPatterns;
|
||||
import ghidra.app.plugin.assembler.sleigh.sem.*;
|
||||
import ghidra.app.plugin.processors.sleigh.expression.LeftShiftExpression;
|
||||
import ghidra.util.Msg;
|
||||
|
||||
@@ -60,13 +59,14 @@ public class LeftShiftExpressionSolver extends AbstractBinaryExpressionSolver<Le
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AssemblyResolution solveTwoSided(LeftShiftExpression exp, MaskedLong goal,
|
||||
Map<String, Long> vals, AssemblyResolvedPatterns cur, Set<SolverHint> hints,
|
||||
String description) throws NeedsBackfillException, SolverException {
|
||||
protected AssemblyResolution solveTwoSided(AbstractAssemblyResolutionFactory<?, ?> factory,
|
||||
LeftShiftExpression exp, MaskedLong goal, Map<String, Long> vals,
|
||||
AssemblyResolvedPatterns cur, Set<SolverHint> hints, String description)
|
||||
throws NeedsBackfillException, SolverException {
|
||||
// Do not guess the same parameter recursively
|
||||
if (hints.contains(DefaultSolverHint.GUESSING_LEFT_SHIFT_AMOUNT)) {
|
||||
// NOTE: Nested left shifts ought to be written as a left shift by a sum
|
||||
return super.solveTwoSided(exp, goal, vals, 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
|
||||
// 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
|
||||
try {
|
||||
// NB. goal is already 0s, so just use it as subgoal for lhs of shift
|
||||
AssemblyResolution lres =
|
||||
solver.solve(exp.getLeft(), goal, vals, cur, hintsWithLShift, description);
|
||||
AssemblyResolution lres = solver.solve(factory, exp.getLeft(), goal, vals, cur,
|
||||
hintsWithLShift, description);
|
||||
if (lres.isError()) {
|
||||
throw new SolverException("Solving left:=0 failed");
|
||||
}
|
||||
@@ -97,13 +97,13 @@ public class LeftShiftExpressionSolver extends AbstractBinaryExpressionSolver<Le
|
||||
MaskedLong reqr = MaskedLong.fromLong(shift);
|
||||
MaskedLong reql = computeLeft(reqr, goal);
|
||||
|
||||
AssemblyResolution lres =
|
||||
solver.solve(exp.getLeft(), reql, vals, cur, hintsWithLShift, description);
|
||||
AssemblyResolution lres = solver.solve(factory, exp.getLeft(), reql, vals, cur,
|
||||
hintsWithLShift, description);
|
||||
if (lres.isError()) {
|
||||
throw new SolverException("Solving left failed");
|
||||
}
|
||||
AssemblyResolution rres =
|
||||
solver.solve(exp.getRight(), reqr, vals, cur, hints, description);
|
||||
solver.solve(factory, exp.getRight(), reqr, vals, cur, hints, description);
|
||||
if (rres.isError()) {
|
||||
throw new SolverException("Solving right failed");
|
||||
}
|
||||
@@ -121,6 +121,6 @@ public class LeftShiftExpressionSolver extends AbstractBinaryExpressionSolver<Le
|
||||
// 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.Set;
|
||||
|
||||
import ghidra.app.plugin.assembler.sleigh.sem.AssemblyResolution;
|
||||
import ghidra.app.plugin.assembler.sleigh.sem.AssemblyResolvedPatterns;
|
||||
import ghidra.app.plugin.assembler.sleigh.sem.*;
|
||||
import ghidra.app.plugin.processors.sleigh.expression.MultExpression;
|
||||
import ghidra.app.plugin.processors.sleigh.expression.PatternExpression;
|
||||
|
||||
@@ -102,25 +101,26 @@ public class MultExpressionSolver extends AbstractBinaryExpressionSolver<MultExp
|
||||
super(MultExpression.class);
|
||||
}
|
||||
|
||||
protected AssemblyResolution tryRep(PatternExpression lexp, MaskedLong rval, MaskedLong repGoal,
|
||||
MaskedLong goal, Map<String, Long> vals, AssemblyResolvedPatterns cur,
|
||||
Set<SolverHint> hints, String description) throws NeedsBackfillException {
|
||||
protected AssemblyResolution tryRep(AbstractAssemblyResolutionFactory<?, ?> factory,
|
||||
PatternExpression lexp, MaskedLong rval, MaskedLong repGoal, MaskedLong goal,
|
||||
Map<String, Long> vals, AssemblyResolvedPatterns cur, Set<SolverHint> hints,
|
||||
String description) throws NeedsBackfillException {
|
||||
MaskedLong lval = repGoal.divideUnsigned(rval);
|
||||
if (lval.multiply(rval).agrees(goal)) {
|
||||
return solver.solve(lexp, lval, vals, cur, hints, description);
|
||||
return solver.solve(factory, lexp, lval, vals, cur, hints, description);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AssemblyResolution solveLeftSide(PatternExpression lexp, MaskedLong rval,
|
||||
MaskedLong goal, Map<String, Long> vals, AssemblyResolvedPatterns cur,
|
||||
Set<SolverHint> hints, String description)
|
||||
protected AssemblyResolution solveLeftSide(AbstractAssemblyResolutionFactory<?, ?> factory,
|
||||
PatternExpression lexp, MaskedLong rval, MaskedLong goal, Map<String, Long> vals,
|
||||
AssemblyResolvedPatterns cur, Set<SolverHint> hints, String description)
|
||||
throws NeedsBackfillException, SolverException {
|
||||
// Try the usual case first
|
||||
ResultTracker tracker = new ResultTracker();
|
||||
AssemblyResolution sol = tracker.trySolverFunc(() -> {
|
||||
return super.solveLeftSide(lexp, rval, goal, vals, cur, hints, description);
|
||||
return super.solveLeftSide(factory, lexp, rval, goal, vals, cur, hints, description);
|
||||
});
|
||||
if (sol != null) {
|
||||
return sol;
|
||||
@@ -150,7 +150,8 @@ public class MultExpressionSolver extends AbstractBinaryExpressionSolver<MultExp
|
||||
if (reps > 0) {
|
||||
MaskedLong repRightGoal = MaskedLong.fromMaskAndValue(repMsk, repVal);
|
||||
sol = tracker.trySolverFunc(() -> {
|
||||
return tryRep(lexp, rval, repRightGoal, goal, vals, cur, hintsWithRepetition,
|
||||
return tryRep(factory, lexp, rval, repRightGoal, goal, vals, cur,
|
||||
hintsWithRepetition,
|
||||
description);
|
||||
});
|
||||
if (sol != null) {
|
||||
@@ -168,8 +169,8 @@ public class MultExpressionSolver extends AbstractBinaryExpressionSolver<MultExp
|
||||
repMsk = -1L >>> i;
|
||||
MaskedLong repLeftGoal = MaskedLong.fromMaskAndValue(repMsk, repVal);
|
||||
sol = tracker.trySolverFunc(() -> {
|
||||
return tryRep(lexp, rval, repLeftGoal, goal, vals, cur, hintsWithRepetition,
|
||||
description);
|
||||
return tryRep(factory, lexp, rval, repLeftGoal, goal, vals, cur,
|
||||
hintsWithRepetition, description);
|
||||
});
|
||||
if (sol != null) {
|
||||
return sol;
|
||||
@@ -180,11 +181,11 @@ public class MultExpressionSolver extends AbstractBinaryExpressionSolver<MultExp
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AssemblyResolution solveRightSide(PatternExpression rexp, MaskedLong lval,
|
||||
MaskedLong goal, Map<String, Long> vals, AssemblyResolvedPatterns cur,
|
||||
Set<SolverHint> hints, String 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 solveLeftSide(rexp, lval, goal, vals, cur, hints, description);
|
||||
return solveLeftSide(factory, rexp, lval, goal, vals, cur, hints, description);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+4
-3
@@ -39,9 +39,10 @@ public class Next2InstructionValueSolver extends AbstractExpressionSolver<Next2I
|
||||
}
|
||||
|
||||
@Override
|
||||
public AssemblyResolution solve(Next2InstructionValue iv, MaskedLong goal,
|
||||
Map<String, Long> vals,
|
||||
AssemblyResolvedPatterns cur, Set<SolverHint> hints, String description) {
|
||||
public AssemblyResolution solve(AbstractAssemblyResolutionFactory<?, ?> factory,
|
||||
Next2InstructionValue exp, MaskedLong goal, Map<String, Long> vals,
|
||||
AssemblyResolvedPatterns cur, Set<SolverHint> hints, String description)
|
||||
throws NeedsBackfillException {
|
||||
throw new AssertionError(
|
||||
"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"
|
||||
*
|
||||
* <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.
|
||||
*
|
||||
* @return the defining expression, or null if neither is available
|
||||
@@ -60,25 +60,31 @@ public class OperandValueSolver extends AbstractExpressionSolver<OperandValue> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public AssemblyResolution solve(OperandValue ov, MaskedLong goal, Map<String, Long> vals,
|
||||
AssemblyResolvedPatterns cur, Set<SolverHint> hints, String description)
|
||||
throws NeedsBackfillException {
|
||||
public AssemblyResolution solve(AbstractAssemblyResolutionFactory<?, ?> factory,
|
||||
OperandValue ov, MaskedLong goal, Map<String, Long> vals, AssemblyResolvedPatterns cur,
|
||||
Set<SolverHint> hints, String description) throws NeedsBackfillException {
|
||||
Constructor cons = ov.getConstructor();
|
||||
OperandSymbol sym = cons.getOperand(ov.getIndex());
|
||||
PatternExpression patexp = getDefiningExpression(sym);
|
||||
if (patexp == null) {
|
||||
if (goal.equals(MaskedLong.ZERO)) {
|
||||
return AssemblyResolution.nop(description, null, null);
|
||||
return factory.nop(description);
|
||||
}
|
||||
return AssemblyResolution.error("Operand " + sym.getName() +
|
||||
" is undefined and does not agree with child requirements", description);
|
||||
return factory.newErrorBuilder()
|
||||
.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()) {
|
||||
AssemblyResolvedError err = (AssemblyResolvedError) result;
|
||||
return AssemblyResolution.error(err.getError(),
|
||||
"Solution to " + sym.getName() + " := " + goal + " = " + patexp,
|
||||
List.of(result), null);
|
||||
return factory.newErrorBuilder()
|
||||
.error(err.getError())
|
||||
.description("Solution to " + sym.getName() + " := " + goal + " = " + patexp)
|
||||
.children(List.of(result))
|
||||
.build();
|
||||
}
|
||||
// TODO: Shifting here seems like a hack to me.
|
||||
// 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);
|
||||
}
|
||||
|
||||
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,
|
||||
String description) throws SolverException {
|
||||
/*
|
||||
@@ -68,7 +69,7 @@ public class OrExpressionSolver extends AbstractBinaryExpressionSolver<OrExpress
|
||||
fields.put(64L, new ConstantValue(0));
|
||||
long lo = 0;
|
||||
PatternExpression fieldExp = null;
|
||||
AssemblyResolvedPatterns result = AssemblyResolution.nop(description);
|
||||
AssemblyResolvedPatterns result = factory.nop(description);
|
||||
try (DbgCtx dc = dbg.start("Trying solution of field catenation")) {
|
||||
dbg.println("Original: " + goal + ":= " + exp);
|
||||
for (Map.Entry<Long, PatternExpression> ent : fields.entrySet()) {
|
||||
@@ -81,7 +82,7 @@ public class OrExpressionSolver extends AbstractBinaryExpressionSolver<OrExpress
|
||||
dbg.println("Part(" + hi + ":" + lo + "]:= " + fieldExp);
|
||||
MaskedLong part = goal.shiftLeft(64 - hi).shiftRightPositional(64 - hi + lo);
|
||||
dbg.println("Solving: " + part + ":= " + fieldExp);
|
||||
AssemblyResolution sol = solver.solve(fieldExp, part, vals, cur, hints,
|
||||
AssemblyResolution sol = solver.solve(factory, fieldExp, part, vals, cur, hints,
|
||||
description + " with shift " + lo);
|
||||
if (sol.isError()) {
|
||||
return sol;
|
||||
@@ -98,7 +99,8 @@ public class OrExpressionSolver extends AbstractBinaryExpressionSolver<OrExpress
|
||||
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,
|
||||
String description) throws SolverException {
|
||||
// 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
|
||||
dbg.println("Identified circular shift: value:= " + expValu1 + ", shift:= " + expShift +
|
||||
", size:= " + size + ", dir:= " + (dir == 1 ? "right" : "left"));
|
||||
return solveLeftCircularShift(expValu1, expShift, size, dir, goal, vals, cur, hints,
|
||||
description);
|
||||
return solveLeftCircularShift(factory, expValu1, expShift, size, dir, goal, vals, cur,
|
||||
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,
|
||||
AssemblyResolvedPatterns cur, Set<SolverHint> hints, String description)
|
||||
throws NeedsBackfillException, SolverException {
|
||||
@@ -194,12 +197,12 @@ public class OrExpressionSolver extends AbstractBinaryExpressionSolver<OrExpress
|
||||
throw new AssertionError("Should not have constants when solving special forms");
|
||||
}
|
||||
else if (valValue != null) {
|
||||
return solver.solve(expShift, computeCircShiftG(valValue, size, dir, goal), vals, cur,
|
||||
hints, description);
|
||||
return solver.solve(factory, expShift, computeCircShiftG(valValue, size, dir, goal),
|
||||
vals, cur, hints, description);
|
||||
}
|
||||
else if (valShift != null) {
|
||||
return solver.solve(expValue, computeCircShiftF(valShift, size, dir, goal), vals, cur,
|
||||
hints, description);
|
||||
return solver.solve(factory, expValue, computeCircShiftF(valShift, size, dir, goal),
|
||||
vals, cur, hints, description);
|
||||
}
|
||||
|
||||
// Oiy. Try guessing the shift amount, starting at 0
|
||||
@@ -213,14 +216,14 @@ public class OrExpressionSolver extends AbstractBinaryExpressionSolver<OrExpress
|
||||
try {
|
||||
MaskedLong reqShift = MaskedLong.fromLong(shift);
|
||||
MaskedLong reqValue = computeCircShiftF(reqShift, size, dir, goal);
|
||||
AssemblyResolution resValue = solver.solve(expValue, reqValue, vals, cur,
|
||||
AssemblyResolution resValue = solver.solve(factory, expValue, reqValue, vals, cur,
|
||||
hintsWithCircularShift, description);
|
||||
if (resValue.isError()) {
|
||||
AssemblyResolvedError err = (AssemblyResolvedError) resValue;
|
||||
throw new SolverException("Solving f failed: " + err.getError());
|
||||
}
|
||||
AssemblyResolution resShift =
|
||||
solver.solve(expShift, reqShift, vals, cur, hints, description);
|
||||
solver.solve(factory, expShift, reqShift, vals, cur, hints, description);
|
||||
if (resShift.isError()) {
|
||||
AssemblyResolvedError err = (AssemblyResolvedError) resShift;
|
||||
throw new SolverException("Solving g failed: " + err.getError());
|
||||
@@ -267,11 +270,12 @@ public class OrExpressionSolver extends AbstractBinaryExpressionSolver<OrExpress
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AssemblyResolution solveTwoSided(OrExpression exp, MaskedLong goal,
|
||||
Map<String, Long> vals, AssemblyResolvedPatterns cur, Set<SolverHint> hints,
|
||||
String description) throws NeedsBackfillException, SolverException {
|
||||
protected AssemblyResolution solveTwoSided(AbstractAssemblyResolutionFactory<?, ?> factory,
|
||||
OrExpression exp, MaskedLong goal, Map<String, Long> vals, AssemblyResolvedPatterns cur,
|
||||
Set<SolverHint> hints, String description)
|
||||
throws NeedsBackfillException, SolverException {
|
||||
try {
|
||||
return tryCatenationExpression(exp, goal, vals, cur, hints, description);
|
||||
return tryCatenationExpression(factory, exp, goal, vals, cur, hints, description);
|
||||
}
|
||||
catch (Exception e) {
|
||||
dbg.println("while solving: " + goal + "=:" + exp);
|
||||
@@ -279,7 +283,7 @@ public class OrExpressionSolver extends AbstractBinaryExpressionSolver<OrExpress
|
||||
}
|
||||
|
||||
try {
|
||||
return tryCircularShiftExpression(exp, goal, vals, cur, hints, description);
|
||||
return tryCircularShiftExpression(factory, exp, goal, vals, cur, hints, description);
|
||||
}
|
||||
catch (Exception e) {
|
||||
dbg.println("while solving: " + goal + "=:" + exp);
|
||||
@@ -291,21 +295,22 @@ public class OrExpressionSolver extends AbstractBinaryExpressionSolver<OrExpress
|
||||
long value = MATCHERS.val.get(match).getValue();
|
||||
PatternValue field = MATCHERS.fld.get(match);
|
||||
// Solve for equals, then either return that, or forbid it, depending on goal
|
||||
AssemblyResolution solution =
|
||||
solver.solve(field, MaskedLong.fromLong(value), vals, cur, hints, description);
|
||||
AssemblyResolution solution = solver.solve(factory, field, MaskedLong.fromLong(value),
|
||||
vals, cur, hints, description);
|
||||
if (goal.equals(MaskedLong.fromMaskAndValue(0, 1))) {
|
||||
return solution;
|
||||
}
|
||||
if (goal.equals(MaskedLong.fromMaskAndValue(1, 1))) {
|
||||
if (solution.isError()) {
|
||||
return AssemblyResolution.nop(description);
|
||||
return factory.nop(description);
|
||||
}
|
||||
if (solution.isBackfill()) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
AssemblyResolvedPatterns forbidden = (AssemblyResolvedPatterns) solution;
|
||||
forbidden = forbidden.withDescription("Solved 'not equals'");
|
||||
return AssemblyResolution.nop(description).withForbids(Set.of(forbidden));
|
||||
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 ghidra.app.plugin.assembler.sleigh.sem.AssemblyResolution;
|
||||
import ghidra.app.plugin.assembler.sleigh.sem.AssemblyResolvedPatterns;
|
||||
import ghidra.app.plugin.assembler.sleigh.sem.*;
|
||||
import ghidra.app.plugin.assembler.sleigh.util.DbgTimer;
|
||||
import ghidra.app.plugin.processors.sleigh.expression.PatternExpression;
|
||||
|
||||
@@ -115,11 +114,13 @@ public class RecursiveDescentSolver {
|
||||
* @return the encoded solution
|
||||
* @throws NeedsBackfillException a solution may exist, but a required symbol is missing
|
||||
*/
|
||||
protected AssemblyResolution solve(PatternExpression exp, MaskedLong goal,
|
||||
Map<String, Long> vals, AssemblyResolvedPatterns cur, Set<SolverHint> hints,
|
||||
String description) throws NeedsBackfillException {
|
||||
protected AssemblyResolution solve(AbstractAssemblyResolutionFactory<?, ?> factory,
|
||||
PatternExpression exp, MaskedLong goal, Map<String, Long> vals,
|
||||
AssemblyResolvedPatterns cur, Set<SolverHint> hints, String description)
|
||||
throws NeedsBackfillException {
|
||||
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) {
|
||||
DBG.println("Error solving " + exp + " = " + goal);
|
||||
@@ -152,10 +153,10 @@ public class RecursiveDescentSolver {
|
||||
* @return the encoded solution
|
||||
* @throws NeedsBackfillException a solution may exist, but a required symbol is missing
|
||||
*/
|
||||
public AssemblyResolution solve(PatternExpression exp, MaskedLong goal, Map<String, Long> vals,
|
||||
AssemblyResolvedPatterns cur, String description)
|
||||
throws NeedsBackfillException {
|
||||
return solve(exp, goal, vals, cur, Set.of(), description);
|
||||
public AssemblyResolution solve(AbstractAssemblyResolutionFactory<?, ?> factory,
|
||||
PatternExpression exp, MaskedLong goal, Map<String, Long> vals,
|
||||
AssemblyResolvedPatterns cur, String description) throws NeedsBackfillException {
|
||||
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.Set;
|
||||
|
||||
import ghidra.app.plugin.assembler.sleigh.sem.AssemblyResolution;
|
||||
import ghidra.app.plugin.assembler.sleigh.sem.AssemblyResolvedPatterns;
|
||||
import ghidra.app.plugin.assembler.sleigh.sem.*;
|
||||
import ghidra.app.plugin.processors.sleigh.expression.RightShiftExpression;
|
||||
import ghidra.util.Msg;
|
||||
|
||||
@@ -61,15 +60,16 @@ public class RightShiftExpressionSolver
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AssemblyResolution solveTwoSided(RightShiftExpression exp, MaskedLong goal,
|
||||
Map<String, Long> vals, AssemblyResolvedPatterns cur, Set<SolverHint> hints,
|
||||
String description) throws NeedsBackfillException, SolverException {
|
||||
protected AssemblyResolution solveTwoSided(AbstractAssemblyResolutionFactory<?, ?> factory,
|
||||
RightShiftExpression exp, MaskedLong goal, Map<String, Long> vals,
|
||||
AssemblyResolvedPatterns cur, Set<SolverHint> hints, String description)
|
||||
throws NeedsBackfillException, SolverException {
|
||||
// Do the similar thing as in {@link LeftShiftExpressionSolver}
|
||||
|
||||
// Do not guess the same parameter recursively
|
||||
if (hints.contains(DefaultSolverHint.GUESSING_RIGHT_SHIFT_AMOUNT)) {
|
||||
// NOTE: Nested right shifts ought to be written as a right shift by a sum
|
||||
return super.solveTwoSided(exp, goal, vals, cur, hints, description);
|
||||
return super.solveTwoSided(factory, exp, goal, vals, cur, hints, description);
|
||||
}
|
||||
|
||||
int maxShift = Long.numberOfLeadingZeros(goal.val);
|
||||
@@ -80,13 +80,13 @@ public class RightShiftExpressionSolver
|
||||
MaskedLong reqr = MaskedLong.fromLong(shift);
|
||||
MaskedLong reql = computeLeft(reqr, goal);
|
||||
|
||||
AssemblyResolution lres =
|
||||
solver.solve(exp.getLeft(), reql, vals, cur, hintsWithRShift, description);
|
||||
AssemblyResolution lres = solver.solve(factory, exp.getLeft(), reql, vals, cur,
|
||||
hintsWithRShift, description);
|
||||
if (lres.isError()) {
|
||||
throw new SolverException("Solving left failed");
|
||||
}
|
||||
AssemblyResolution rres =
|
||||
solver.solve(exp.getRight(), reqr, vals, cur, hints, description);
|
||||
solver.solve(factory, exp.getRight(), reqr, vals, cur, hints, description);
|
||||
if (rres.isError()) {
|
||||
throw new SolverException("Solving right failed");
|
||||
}
|
||||
@@ -104,6 +104,6 @@ public class RightShiftExpressionSolver
|
||||
// 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
|
||||
public AssemblyResolution solve(StartInstructionValue iv, MaskedLong goal,
|
||||
Map<String, Long> vals, AssemblyResolvedPatterns cur, Set<SolverHint> hints,
|
||||
String description) {
|
||||
public AssemblyResolution solve(AbstractAssemblyResolutionFactory<?, ?> factory,
|
||||
StartInstructionValue iv, MaskedLong goal, Map<String, Long> vals,
|
||||
AssemblyResolvedPatterns cur, Set<SolverHint> hints, String description) {
|
||||
throw new AssertionError(
|
||||
"INTERNAL: Should never be asked to solve for " + AssemblyTreeResolver.INST_START);
|
||||
}
|
||||
|
||||
+8
-5
@@ -35,15 +35,18 @@ public class TokenFieldSolver extends AbstractExpressionSolver<TokenField> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public AssemblyResolution solve(TokenField tf, MaskedLong goal, Map<String, Long> vals,
|
||||
AssemblyResolvedPatterns cur, Set<SolverHint> hints, String description) {
|
||||
public AssemblyResolution solve(AbstractAssemblyResolutionFactory<?, ?> factory,
|
||||
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.
|
||||
if (!goal.isInRange(tf.maxValue(), tf.hasSignbit())) {
|
||||
return AssemblyResolution.error("Value " + goal + " is not valid for " + tf,
|
||||
description);
|
||||
return factory.newErrorBuilder()
|
||||
.error("Value " + goal + " is not valid for " + tf)
|
||||
.description(description)
|
||||
.build();
|
||||
}
|
||||
AssemblyPatternBlock block = AssemblyPatternBlock.fromTokenField(tf, goal);
|
||||
return AssemblyResolution.instrOnly(block, description);
|
||||
return factory.instrOnly(block, description);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+9
-3
@@ -17,6 +17,7 @@ package ghidra.app.plugin.assembler.sleigh.grammars;
|
||||
|
||||
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.symbol.AssemblyNonTerminal;
|
||||
import ghidra.app.plugin.processors.sleigh.Constructor;
|
||||
@@ -33,14 +34,19 @@ import ghidra.app.plugin.processors.sleigh.pattern.DisjointPattern;
|
||||
*/
|
||||
public class AssemblyGrammar
|
||||
extends AbstractAssemblyGrammar<AssemblyNonTerminal, AssemblyProduction> {
|
||||
protected final AbstractAssemblyResolutionFactory<?, ?> factory;
|
||||
// a nested map of semantics by production, by constructor
|
||||
protected final Map<AssemblyProduction, Map<Constructor, AssemblyConstructorSemantic>> semanticsByProduction =
|
||||
new TreeMap<>();
|
||||
protected final Map<AssemblyProduction, //
|
||||
Map<Constructor, AssemblyConstructorSemantic>> semanticsByProduction = new TreeMap<>();
|
||||
protected final Map<Constructor, AssemblyConstructorSemantic> semanticsByConstructor =
|
||||
new HashMap<>();
|
||||
// a map of purely recursive, e.g., I => I, productions by name of LHS
|
||||
protected final Map<String, AssemblyProduction> pureRecursive = new TreeMap<>();
|
||||
|
||||
public AssemblyGrammar(AbstractAssemblyResolutionFactory<?, ?> factory) {
|
||||
this.factory = factory;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AssemblyProduction newProduction(AssemblyNonTerminal lhs,
|
||||
AssemblySentential<AssemblyNonTerminal> rhs) {
|
||||
@@ -73,7 +79,7 @@ public class AssemblyGrammar
|
||||
Map<Constructor, AssemblyConstructorSemantic> map =
|
||||
semanticsByProduction.computeIfAbsent(prod, p -> new TreeMap<>());
|
||||
AssemblyConstructorSemantic sem =
|
||||
map.computeIfAbsent(cons, c -> new AssemblyConstructorSemantic(cons, indices));
|
||||
map.computeIfAbsent(cons, c -> new AssemblyConstructorSemantic(factory, cons, indices));
|
||||
if (!indices.equals(sem.getOperandIndices())) {
|
||||
throw new IllegalStateException(
|
||||
"Productions of the same constructor must have same operand indices");
|
||||
|
||||
+4
@@ -28,4 +28,8 @@ public class AssemblyProduction extends AbstractAssemblyProduction<AssemblyNonTe
|
||||
AssemblySentential<AssemblyNonTerminal> 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 WhiteSpace() {
|
||||
super(" ");
|
||||
super(" ", null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -166,6 +166,11 @@ public class AssemblySentential<NT extends AssemblyNonTerminal>
|
||||
public Collection<String> getSuggestions(String got, AssemblyNumericSymbols symbols) {
|
||||
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.
|
||||
*/
|
||||
public void addCommaWS() {
|
||||
addSymbol(new AssemblyStringTerminal(","));
|
||||
addSymbol(new AssemblyStringTerminal(",", null));
|
||||
addWS();
|
||||
}
|
||||
|
||||
@@ -240,7 +245,7 @@ public class AssemblySentential<NT extends AssemblyNonTerminal>
|
||||
if (!Character.isLetterOrDigit(first)) {
|
||||
addWS();
|
||||
}
|
||||
addSymbol(new AssemblyStringTerminal(tstr));
|
||||
addSymbol(new AssemblyStringTerminal(tstr, null));
|
||||
char last = tstr.charAt(tstr.length() - 1);
|
||||
if (!str.endsWith(tstr)) {
|
||||
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
|
||||
*/
|
||||
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 int shift;
|
||||
protected final int length;
|
||||
|
||||
protected final int hash;
|
||||
protected volatile boolean hasHash = false;
|
||||
protected volatile int hash;
|
||||
|
||||
/**
|
||||
* Construct a node
|
||||
@@ -42,18 +44,21 @@ public abstract class AbstractAssemblyState {
|
||||
* @param shift the (right) shift in bytes for this operand
|
||||
* @param length the length of this operand
|
||||
*/
|
||||
protected AbstractAssemblyState(AssemblyTreeResolver resolver,
|
||||
protected AbstractAssemblyState(AbstractAssemblyTreeResolver<?> resolver,
|
||||
List<AssemblyConstructorSemantic> path, int shift, int length) {
|
||||
this.resolver = resolver;
|
||||
this.factory = resolver.factory;
|
||||
this.path = path;
|
||||
this.shift = shift;
|
||||
this.length = length;
|
||||
|
||||
this.hash = computeHash();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
if (!hasHash) {
|
||||
this.hash = computeHash();
|
||||
this.hasHash = true;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
@@ -77,6 +82,18 @@ public abstract class AbstractAssemblyState {
|
||||
protected abstract Stream<AssemblyResolvedPatterns> resolve(AssemblyResolvedPatterns fromRight,
|
||||
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
|
||||
*
|
||||
|
||||
+5
-5
@@ -29,7 +29,7 @@ import ghidra.app.plugin.assembler.sleigh.util.DbgTimer;
|
||||
* @param <N> the type of parse tree node to process
|
||||
*/
|
||||
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
|
||||
@@ -47,8 +47,8 @@ public abstract class AbstractAssemblyStateGenerator<N extends AssemblyParseTree
|
||||
path.stream().map(sem -> sem.getLocation()).collect(Collectors.joining(",")) + "]";
|
||||
}
|
||||
|
||||
final List<AssemblyConstructorSemantic> path;
|
||||
final int shift;
|
||||
public final List<AssemblyConstructorSemantic> path;
|
||||
public final int shift;
|
||||
|
||||
/**
|
||||
* 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 AssemblyResolvedPatterns fromLeft;
|
||||
|
||||
@@ -95,7 +95,7 @@ public abstract class AbstractAssemblyStateGenerator<N extends AssemblyParseTree
|
||||
* @param node the node from which to generate states
|
||||
* @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) {
|
||||
this.resolver = resolver;
|
||||
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
|
||||
* @return the farthest end byte
|
||||
*/
|
||||
protected static int computeEnd(List<AbstractAssemblyState> operands) {
|
||||
protected static int computeEnd(List<? extends AbstractAssemblyState> operands) {
|
||||
return operands.stream()
|
||||
.map(s -> s.shift + s.length)
|
||||
.reduce(0, Integer::max);
|
||||
@@ -61,7 +61,7 @@ public class AssemblyConstructState extends AbstractAssemblyState {
|
||||
* @param sem the selected SLEIGH 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,
|
||||
AssemblyConstructorSemantic sem, List<AbstractAssemblyState> children) {
|
||||
super(resolver, path, shift,
|
||||
@@ -150,8 +150,10 @@ public class AssemblyConstructState extends AbstractAssemblyState {
|
||||
})
|
||||
.filter(ar -> {
|
||||
if (ar == null) {
|
||||
errors.add(AssemblyResolution.error("Pattern conflict",
|
||||
"Resolving " + sem.getLocation() + " in " + path));
|
||||
errors.add(factory.newErrorBuilder()
|
||||
.error("Pattern conflict")
|
||||
.description("Resolving " + sem.getLocation() + " in " + path)
|
||||
.build());
|
||||
return false;
|
||||
}
|
||||
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.symbol.AssemblySymbol;
|
||||
import ghidra.app.plugin.assembler.sleigh.tree.AssemblyParseBranch;
|
||||
import ghidra.app.plugin.assembler.sleigh.tree.AssemblyParseTreeNode;
|
||||
import ghidra.app.plugin.assembler.sleigh.tree.*;
|
||||
import ghidra.app.plugin.assembler.sleigh.util.AsmUtil;
|
||||
import ghidra.app.plugin.processors.sleigh.Constructor;
|
||||
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 fromLeft the accumulated patterns from the left sibling or the parent
|
||||
*/
|
||||
public AssemblyConstructStateGenerator(AssemblyTreeResolver resolver, AssemblyParseBranch node,
|
||||
AssemblyResolvedPatterns fromLeft) {
|
||||
public AssemblyConstructStateGenerator(AbstractAssemblyTreeResolver<?> resolver,
|
||||
AssemblyParseBranch node, AssemblyResolvedPatterns fromLeft) {
|
||||
super(resolver, node, fromLeft);
|
||||
}
|
||||
|
||||
@@ -68,8 +67,9 @@ public class AssemblyConstructStateGenerator
|
||||
*/
|
||||
protected List<AssemblyParseTreeNode> orderOpNodes(AssemblyConstructorSemantic sem) {
|
||||
Constructor cons = sem.getConstructor();
|
||||
List<AssemblyParseTreeNode> result =
|
||||
Arrays.asList(new AssemblyParseTreeNode[cons.getNumOperands()]);
|
||||
AssemblyParseTreeNode[] arr = new AssemblyParseTreeNode[cons.getNumOperands()];
|
||||
List<AssemblyParseTreeNode> result = Arrays.asList(arr);
|
||||
Arrays.fill(arr, new AssemblyParseHiddenNode(resolver.grammar));
|
||||
int index = 0;
|
||||
AssemblyProduction production = node.getProduction();
|
||||
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
|
||||
* 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
|
||||
* 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
|
||||
* pass the patterns accumulated after context changes.
|
||||
* @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
|
||||
* empty list.
|
||||
* @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> {
|
||||
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 AbstractAssemblyResolutionFactory<?, ?> factory;
|
||||
protected final Constructor cons;
|
||||
protected final List<Integer> indices;
|
||||
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
|
||||
* 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.indices = Collections.unmodifiableList(indices);
|
||||
List<ContextChange> changes = new ArrayList<>(cons.getContextChanges());
|
||||
@@ -69,7 +72,7 @@ public class AssemblyConstructorSemantic implements Comparable<AssemblyConstruct
|
||||
* @param pat the pattern
|
||||
*/
|
||||
public void addPattern(DisjointPattern pat) {
|
||||
addPattern(AssemblyResolution.fromPattern(pat, cons.getMinimumLength(),
|
||||
addPattern(factory.fromPattern(pat, cons.getMinimumLength(),
|
||||
"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.
|
||||
AssemblyResolvedPatterns sibpat = AssemblyResolution.fromPattern(sibDP,
|
||||
AssemblyResolvedPatterns sibpat = factory.fromPattern(sibDP,
|
||||
sibcons.getMinimumLength(), "For specialization check", sibcons);
|
||||
AssemblyResolvedPatterns comb = pat.combine(sibpat);
|
||||
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
|
||||
if (!comb.ctx.equals(pat.ctx)) {
|
||||
if (!comb.getContext().equals(pat.getContext())) {
|
||||
return CONTINUE;
|
||||
}
|
||||
|
||||
@@ -295,7 +298,7 @@ public class AssemblyConstructorSemantic implements Comparable<AssemblyConstruct
|
||||
*
|
||||
* @param res the combined resolution requirements derived from the subconstructors
|
||||
* @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,
|
||||
Map<String, Long> vals) {
|
||||
@@ -320,12 +323,12 @@ public class AssemblyConstructorSemantic implements Comparable<AssemblyConstruct
|
||||
DBG.println("Masked out: " + res.lineToString());
|
||||
|
||||
// Now, solve
|
||||
AssemblyResolution sol = AssemblyTreeResolver.solveOrBackfill(
|
||||
cop.getPatternExpression(), reqval, vals, res, "Solution to " + cop);
|
||||
AssemblyResolution sol = factory.solveOrBackfill(cop.getPatternExpression(), reqval,
|
||||
vals, res, "Solution to " + cop);
|
||||
DBG.println("Solution: " + sol.lineToString());
|
||||
if (sol.isError()) {
|
||||
AssemblyResolvedError err = (AssemblyResolvedError) sol;
|
||||
return AssemblyResolution.error(err.getError(), res);
|
||||
return factory.error(err.getError(), res);
|
||||
}
|
||||
|
||||
// Now, forward the new requirements to my parents.
|
||||
@@ -333,8 +336,7 @@ public class AssemblyConstructorSemantic implements Comparable<AssemblyConstruct
|
||||
AssemblyResolvedPatterns solcon = (AssemblyResolvedPatterns) sol;
|
||||
AssemblyResolvedPatterns check = res.combine(solcon);
|
||||
if (null == check) {
|
||||
return AssemblyResolution.error(
|
||||
"A context change caused a conflict: " + sol, res);
|
||||
return factory.error("A context change caused a conflict: " + sol, res);
|
||||
}
|
||||
res = check;
|
||||
}
|
||||
|
||||
+6
-4
@@ -46,6 +46,7 @@ import ghidra.graph.algo.DijkstraShortestPathsAlgorithm;
|
||||
* much larger its LALR(1) parser would become.
|
||||
*/
|
||||
public class AssemblyContextGraph implements GImplicitDirectedGraph<Vertex, Edge> {
|
||||
protected final AbstractAssemblyResolutionFactory<?, ?> factory;
|
||||
protected final Map<String, Set<AssemblyConstructorSemantic>> semantics =
|
||||
LazyMap.lazyMap(new HashMap<>(), () -> new HashSet<>());
|
||||
protected final AssemblyGrammar grammar;
|
||||
@@ -72,7 +73,9 @@ public class AssemblyContextGraph implements GImplicitDirectedGraph<Vertex, Edge
|
||||
* @param lang the 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.lang = lang;
|
||||
|
||||
@@ -343,7 +346,7 @@ public class AssemblyContextGraph implements GImplicitDirectedGraph<Vertex, Edge
|
||||
Set<Edge> result = new HashSet<>();
|
||||
for (AssemblyConstructorSemantic sem : semantics.get(from.subtable)) {
|
||||
for (AssemblyResolvedPatterns rc : sem.patterns) {
|
||||
AssemblyPatternBlock pattern = rc.ctx;
|
||||
AssemblyPatternBlock pattern = rc.getContext();
|
||||
AssemblyPatternBlock outer = from.context.combine(pattern);
|
||||
if (outer == null) {
|
||||
continue;
|
||||
@@ -352,8 +355,7 @@ public class AssemblyContextGraph implements GImplicitDirectedGraph<Vertex, Edge
|
||||
continue;
|
||||
}
|
||||
|
||||
AssemblyResolvedPatterns orc =
|
||||
AssemblyResolution.contextOnly(outer, "For context transition");
|
||||
AssemblyResolvedPatterns orc = factory.contextOnly(outer, "For context transition");
|
||||
AssemblyResolvedPatterns irc = sem.applyContextChangesForward(Map.of(), orc);
|
||||
AssemblyPatternBlock inner = irc.getContext();
|
||||
|
||||
|
||||
+12
-6
@@ -15,11 +15,10 @@
|
||||
*/
|
||||
package ghidra.app.plugin.assembler.sleigh.sem;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.IntStream;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.stream.*;
|
||||
|
||||
import ghidra.app.plugin.assembler.sleigh.tree.AssemblyParseHiddenNode;
|
||||
import ghidra.app.plugin.assembler.sleigh.tree.AssemblyParseTreeNode;
|
||||
import ghidra.app.plugin.processors.sleigh.Constructor;
|
||||
import ghidra.app.plugin.processors.sleigh.symbol.SubtableSymbol;
|
||||
@@ -42,7 +41,7 @@ public class AssemblyHiddenConstructStateGenerator extends AssemblyConstructStat
|
||||
* @param subtableSym
|
||||
* @param fromLeft the accumulated patterns from the left sibling or the parent
|
||||
*/
|
||||
public AssemblyHiddenConstructStateGenerator(AssemblyTreeResolver resolver,
|
||||
public AssemblyHiddenConstructStateGenerator(AbstractAssemblyTreeResolver<?> resolver,
|
||||
SubtableSymbol subtableSym, AssemblyResolvedPatterns fromLeft) {
|
||||
super(resolver, null, fromLeft);
|
||||
this.subtableSym = subtableSym;
|
||||
@@ -56,10 +55,17 @@ public class AssemblyHiddenConstructStateGenerator extends AssemblyConstructStat
|
||||
.flatMap(sem -> applyConstructor(gc, sem));
|
||||
}
|
||||
|
||||
protected AssemblyParseTreeNode getFiller() {
|
||||
return new AssemblyParseHiddenNode(resolver.grammar);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<AssemblyParseTreeNode> orderOpNodes(AssemblyConstructorSemantic sem) {
|
||||
// Just provide null operands, since they're hidden, too.
|
||||
// Just provide hidden operands
|
||||
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;
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int computeHash() {
|
||||
return "NOP".hashCode();
|
||||
int result = getClass().hashCode();
|
||||
result *= 31;
|
||||
result += Integer.hashCode(shift);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof AssemblyNopState)) {
|
||||
return false;
|
||||
}
|
||||
AssemblyNopState that = (AssemblyNopState) obj;
|
||||
protected boolean partsEqual(AssemblyNopState that) {
|
||||
if (this.resolver != that.resolver) {
|
||||
return false;
|
||||
}
|
||||
@@ -50,6 +46,18 @@ public class AssemblyNopState extends AbstractAssemblyState {
|
||||
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
|
||||
public String toString() {
|
||||
return "NOP";
|
||||
|
||||
+4
-5
@@ -39,8 +39,8 @@ public class AssemblyNopStateGenerator
|
||||
* @param opSym the operand symbol
|
||||
* @param fromLeft the accumulated patterns from the left sibling or parent
|
||||
*/
|
||||
public AssemblyNopStateGenerator(AssemblyTreeResolver resolver, OperandSymbol opSym,
|
||||
AssemblyResolvedPatterns fromLeft) {
|
||||
public AssemblyNopStateGenerator(AbstractAssemblyTreeResolver<?> resolver,
|
||||
OperandSymbol opSym, AssemblyResolvedPatterns fromLeft) {
|
||||
super(resolver, null, fromLeft);
|
||||
this.opSym = opSym;
|
||||
}
|
||||
@@ -48,8 +48,7 @@ public class AssemblyNopStateGenerator
|
||||
@Override
|
||||
public Stream<AssemblyGeneratedPrototype> generate(GeneratorContext gc) {
|
||||
gc.dbg("Generating NOP for " + opSym);
|
||||
return Stream.of(
|
||||
new AssemblyGeneratedPrototype(new AssemblyNopState(resolver, gc.path, gc.shift, opSym),
|
||||
fromLeft));
|
||||
return Stream.of(new AssemblyGeneratedPrototype(
|
||||
new AssemblyNopState(resolver, gc.path, gc.shift, opSym), fromLeft));
|
||||
}
|
||||
}
|
||||
|
||||
+39
-14
@@ -47,7 +47,7 @@ public class AssemblyOperandState extends AbstractAssemblyState {
|
||||
* @param value the value of the operand
|
||||
* @param opSym the operand symbol
|
||||
*/
|
||||
public AssemblyOperandState(AssemblyTreeResolver resolver,
|
||||
public AssemblyOperandState(AbstractAssemblyTreeResolver<?> resolver,
|
||||
List<AssemblyConstructorSemantic> path, int shift, AssemblyTerminal terminal,
|
||||
long value, OperandSymbol opSym) {
|
||||
super(resolver, path, shift, opSym.getMinimumLength());
|
||||
@@ -58,18 +58,17 @@ public class AssemblyOperandState extends AbstractAssemblyState {
|
||||
|
||||
@Override
|
||||
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
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof AssemblyOperandState)) {
|
||||
return false;
|
||||
}
|
||||
AssemblyOperandState that = (AssemblyOperandState) obj;
|
||||
protected boolean partsEqual(AssemblyOperandState that) {
|
||||
if (this.resolver != that.resolver) {
|
||||
return false;
|
||||
}
|
||||
@@ -85,6 +84,18 @@ public class AssemblyOperandState extends AbstractAssemblyState {
|
||||
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
|
||||
public String toString() {
|
||||
return terminal + "=" + value + "(0x" + Long.toHexString(value) + ")";
|
||||
@@ -120,7 +131,7 @@ public class AssemblyOperandState extends AbstractAssemblyState {
|
||||
DBG.println("Equation: " + symExp + " = " + Long.toHexString(value));
|
||||
String desc = "Solution to " + opSym + " in " + Long.toHexString(value) + " = " + symExp;
|
||||
AssemblyResolution sol =
|
||||
AssemblyTreeResolver.solveOrBackfill(symExp, value, bitsize, resolver.vals, null, desc);
|
||||
factory.solveOrBackfill(symExp, value, bitsize, resolver.vals, null, desc);
|
||||
DBG.println("Solution: " + sol);
|
||||
AssemblyResolution shifted = sol.shift(shift);
|
||||
DBG.println("Shifted: " + shifted);
|
||||
@@ -143,8 +154,10 @@ public class AssemblyOperandState extends AbstractAssemblyState {
|
||||
}
|
||||
AssemblyResolution combined = fromRight.combine((AssemblyResolvedPatterns) sol);
|
||||
if (combined == null) {
|
||||
errors.add(
|
||||
AssemblyResolution.error("Pattern/operand conflict", "Resolving " + terminal));
|
||||
errors.add(factory.newErrorBuilder()
|
||||
.error("Pattern/operand conflict")
|
||||
.description("Resolving " + terminal)
|
||||
.build());
|
||||
return Stream.of();
|
||||
}
|
||||
AssemblyResolvedPatterns pats = (AssemblyResolvedPatterns) combined;
|
||||
@@ -152,4 +165,16 @@ public class AssemblyOperandState extends AbstractAssemblyState {
|
||||
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
|
||||
*
|
||||
* @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 opSym the operand symbol
|
||||
*/
|
||||
public AssemblyOperandStateGenerator(AssemblyTreeResolver resolver,
|
||||
public AssemblyOperandStateGenerator(AbstractAssemblyTreeResolver<?> resolver,
|
||||
AssemblyParseNumericToken node, OperandSymbol opSym,
|
||||
AssemblyResolvedPatterns fromLeft) {
|
||||
super(resolver, node, fromLeft);
|
||||
|
||||
+153
-3
@@ -17,8 +17,7 @@ package ghidra.app.plugin.assembler.sleigh.sem;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
import ghidra.app.plugin.assembler.sleigh.expr.MaskedLong;
|
||||
@@ -144,7 +143,7 @@ public class AssemblyPatternBlock implements Comparable<AssemblyPatternBlock> {
|
||||
AtomicLong msk = new AtomicLong();
|
||||
AtomicLong val = new AtomicLong();
|
||||
int i = 0;
|
||||
for (String hex : str.split(":")) {
|
||||
for (String hex : str.substring(pos).split(":")) {
|
||||
NumericUtilities.convertHexStringToMaskedValue(msk, val, hex, 2, 0, null);
|
||||
mask[i] = (byte) msk.get();
|
||||
vals[i] = (byte) val.get();
|
||||
@@ -265,6 +264,7 @@ public class AssemblyPatternBlock implements Comparable<AssemblyPatternBlock> {
|
||||
/**
|
||||
* Convert a register value into a pattern block
|
||||
*
|
||||
* <p>
|
||||
* This is used primarily to compute default context register values, and pass them into an
|
||||
* assembler.
|
||||
*
|
||||
@@ -604,9 +604,132 @@ public class AssemblyPatternBlock implements Comparable<AssemblyPatternBlock> {
|
||||
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
|
||||
*
|
||||
* <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
|
||||
*/
|
||||
public byte[] getVals() {
|
||||
@@ -616,12 +739,39 @@ public class AssemblyPatternBlock implements Comparable<AssemblyPatternBlock> {
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public byte[] getMask() {
|
||||
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
|
||||
*
|
||||
|
||||
+45
-298
@@ -15,285 +15,23 @@
|
||||
*/
|
||||
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;
|
||||
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();
|
||||
public interface AssemblyResolution extends Comparable<AssemblyResolution> {
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
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
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* <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 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
|
||||
* Describe this record including indented children, grandchildren, etc., each on its own line.
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return toString("");
|
||||
}
|
||||
String toString();
|
||||
|
||||
@Override
|
||||
public int compareTo(AssemblyResolution that) {
|
||||
return this.toString().compareTo(that.toString()); // LAZY
|
||||
}
|
||||
String getDescription();
|
||||
|
||||
List<AssemblyResolution> getChildren();
|
||||
|
||||
/**
|
||||
* Check if this record has children
|
||||
@@ -306,15 +44,30 @@ public abstract class AssemblyResolution implements Comparable<AssemblyResolutio
|
||||
* @see #childrenToString(String)
|
||||
* @return true if this record has children
|
||||
*/
|
||||
public boolean hasChildren() {
|
||||
if (children == null) {
|
||||
return false;
|
||||
}
|
||||
if (children.size() == 0) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
boolean hasChildren();
|
||||
|
||||
AssemblyResolution getRight();
|
||||
|
||||
/**
|
||||
* Display the resolution result in one line (omitting child details)
|
||||
*
|
||||
* @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
|
||||
@@ -325,26 +78,20 @@ public abstract class AssemblyResolution implements Comparable<AssemblyResolutio
|
||||
* @param amt the number of bytes to shift.
|
||||
* @return the result
|
||||
*/
|
||||
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);
|
||||
AssemblyResolution shift(int amt);
|
||||
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
* <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
|
||||
* 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
|
||||
* encodings, including error records describing the pruned intermediate results.
|
||||
*/
|
||||
public class AssemblyResolutionResults extends AbstractSetDecorator<AssemblyResolution> {
|
||||
protected static final DbgTimer DBG = AssemblyTreeResolver.DBG;
|
||||
protected static final DbgTimer DBG = AbstractAssemblyTreeResolver.DBG;
|
||||
|
||||
public interface Applicator {
|
||||
Iterable<? extends AssemblyResolution> getPatterns(AssemblyResolvedPatterns cur);
|
||||
|
||||
default AssemblyResolvedPatterns setDescription(
|
||||
AssemblyResolvedPatterns res, AssemblyResolution from) {
|
||||
AssemblyResolvedPatterns temp = res.withDescription(from.description);
|
||||
AssemblyResolvedPatterns temp = res.withDescription(from.getDescription());
|
||||
return temp;
|
||||
}
|
||||
|
||||
@@ -92,20 +92,10 @@ public class AssemblyResolutionResults extends AbstractSetDecorator<AssemblyReso
|
||||
resolutions = new LinkedHashSet<>();
|
||||
}
|
||||
|
||||
private AssemblyResolutionResults(Set<AssemblyResolution> resolutions) {
|
||||
protected AssemblyResolutionResults(Set<AssemblyResolution> 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
|
||||
public boolean add(AssemblyResolution ar) {
|
||||
return resolutions.add(ar);
|
||||
@@ -143,21 +133,22 @@ public class AssemblyResolutionResults extends AbstractSetDecorator<AssemblyReso
|
||||
return this.resolutions.remove(ar);
|
||||
}
|
||||
|
||||
protected AssemblyResolutionResults apply(Applicator applicator) {
|
||||
AssemblyResolutionResults results = new AssemblyResolutionResults();
|
||||
protected AssemblyResolutionResults apply(AbstractAssemblyResolutionFactory<?, ?> factory,
|
||||
Applicator applicator) {
|
||||
AssemblyResolutionResults results = factory.newAssemblyResolutionResults();
|
||||
for (AssemblyResolution res : this) {
|
||||
if (res.isError()) {
|
||||
results.add(res);
|
||||
continue;
|
||||
}
|
||||
AssemblyResolvedPatterns rc = (AssemblyResolvedPatterns) res;
|
||||
DBG.println("Current: " + rc.lineToString());
|
||||
for (AssemblyResolution pat : applicator.getPatterns(rc)) {
|
||||
DBG.println("Pattern: " + pat.lineToString());
|
||||
AssemblyResolvedPatterns combined = applicator.combine(rc, pat);
|
||||
AssemblyResolvedPatterns rp = (AssemblyResolvedPatterns) res;
|
||||
DBG.println("Current: " + rp.lineToString());
|
||||
for (AssemblyResolution ar : applicator.getPatterns(rp)) {
|
||||
DBG.println("Pattern: " + ar.lineToString());
|
||||
AssemblyResolvedPatterns combined = applicator.combine(rp, ar);
|
||||
DBG.println("Combined: " + (combined == null ? "(null)" : combined.lineToString()));
|
||||
if (combined == null) {
|
||||
results.add(AssemblyResolution.error(applicator.describeError(rc, pat), rc));
|
||||
results.add(factory.error(applicator.describeError(rp, ar), ar));
|
||||
continue;
|
||||
}
|
||||
results.add(applicator.finish(combined));
|
||||
@@ -166,14 +157,19 @@ public class AssemblyResolutionResults extends AbstractSetDecorator<AssemblyReso
|
||||
return results;
|
||||
}
|
||||
|
||||
protected AssemblyResolutionResults apply(
|
||||
protected AssemblyResolutionResults apply(AbstractAssemblyResolutionFactory<?, ?> factory,
|
||||
Function<AssemblyResolvedPatterns, AssemblyResolution> function) {
|
||||
return stream().map(res -> {
|
||||
assert !(res instanceof AssemblyResolvedBackfill);
|
||||
if (res.isError()) {
|
||||
return res;
|
||||
if (res instanceof AssemblyResolvedBackfill) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
return function.apply((AssemblyResolvedPatterns) res);
|
||||
}).collect(Collectors.toCollection(AssemblyResolutionResults::new));
|
||||
if (res instanceof AssemblyResolvedError err) {
|
||||
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 ghidra.app.plugin.assembler.sleigh.expr.*;
|
||||
import ghidra.app.plugin.processors.sleigh.expression.PatternExpression;
|
||||
import ghidra.app.plugin.assembler.sleigh.expr.RecursiveDescentSolver;
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
public interface AssemblyResolvedBackfill extends AssemblyResolution {
|
||||
|
||||
/**
|
||||
* 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)
|
||||
*/
|
||||
public int getInstructionLength() {
|
||||
return offset + inslen;
|
||||
}
|
||||
int getInstructionLength();
|
||||
|
||||
@Override
|
||||
public boolean isError() {
|
||||
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();
|
||||
}
|
||||
AssemblyResolvedBackfill shift(int amt);
|
||||
|
||||
/**
|
||||
* 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
|
||||
* 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
|
||||
@@ -130,22 +49,6 @@ public class AssemblyResolvedBackfill extends AssemblyResolution {
|
||||
* @param vals the defined symbols, usually the same, but with the missing symbol(s).
|
||||
* @return the solution result
|
||||
*/
|
||||
public AssemblyResolution solve(RecursiveDescentSolver solver, Map<String, Long> vals,
|
||||
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);
|
||||
}
|
||||
}
|
||||
AssemblyResolution solve(RecursiveDescentSolver solver, Map<String, Long> vals,
|
||||
AssemblyResolvedPatterns cur);
|
||||
}
|
||||
|
||||
+2
-77
@@ -15,83 +15,8 @@
|
||||
*/
|
||||
package ghidra.app.plugin.assembler.sleigh.sem;
|
||||
|
||||
import java.util.List;
|
||||
public interface AssemblyResolvedError extends AssemblyResolution {
|
||||
|
||||
/**
|
||||
* 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;
|
||||
String getError();
|
||||
|
||||
@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