GP-4643: Add a JIT-accelerated p-code emulator (API/scripting only)

This commit is contained in:
Dan 2025-01-03 10:27:38 -05:00
parent 20285e267d
commit a8fae1fe5b
320 changed files with 32638 additions and 630 deletions

View file

@ -4,9 +4,9 @@
* 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.
@ -18,6 +18,7 @@ package ghidra.app.plugin.assembler;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import ghidra.app.plugin.assembler.sleigh.sem.AssemblyPatternBlock;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Program;
@ -68,6 +69,21 @@ public class AssemblyBuffer {
return entry.add(baos.size());
}
/**
* Assemble a line and append it to the buffer
*
* @param line the line
* @param ctx the assembly context
* @return the resulting bytes for the assembled instruction
* @throws AssemblySyntaxException if the instruction cannot be parsed
* @throws AssemblySemanticException if the instruction cannot be encoded
* @throws IOException if the buffer cannot be written
*/
public byte[] assemble(String line, AssemblyPatternBlock ctx)
throws AssemblySyntaxException, AssemblySemanticException, IOException {
return emit(asm.assembleLine(getNext(), line, ctx));
}
/**
* Assemble a line and append it to the buffer
*
@ -109,6 +125,28 @@ public class AssemblyBuffer {
*
* @param at the address of the instruction to patch
* @param line the line
* @param ctx the assembly context
* @return the resulting bytes for the assembled instruction
* @throws AssemblySyntaxException if the instruction cannot be parsed
* @throws AssemblySemanticException if the instruction cannot be encoded
* @throws IOException if the buffer cannot be written
*/
public byte[] assemble(Address at, String line, AssemblyPatternBlock ctx)
throws AssemblySyntaxException, AssemblySemanticException, IOException {
byte[] full = baos.toByteArray();
byte[] bytes = asm.assembleLine(at, line, ctx);
System.arraycopy(bytes, 0, full, (int) at.subtract(entry), bytes.length);
baos.reset();
baos.write(full);
return bytes;
}
/**
* Assemble a line and patch into the buffer
*
* @see #assemble(Address, String, AssemblyPatternBlock)
* @param at the address of the instruction to patch
* @param line the line
* @return the resulting bytes for the assembled instruction
* @throws AssemblySyntaxException if the instruction cannot be parsed
* @throws AssemblySemanticException if the instruction cannot be encoded
@ -148,4 +186,22 @@ public class AssemblyBuffer {
public byte[] getBytes() {
return baos.toByteArray();
}
/**
* Get the starting address
*
* @return the address
*/
public Address getEntry() {
return entry;
}
/**
* Get the assembler for this buffer
*
* @return the assembler
*/
public Assembler getAssembler() {
return asm;
}
}

View file

@ -31,6 +31,7 @@ import ghidra.util.Msg;
* {@link #formatOpTemplate(Appender, OpTpl)}. Otherwise, most formatting logic is implemented by
* the appender.
*
* @see StringPcodeFormatter for an example
* @param <T> the type of this formatter's output, e.g., {@link String}
* @param <A> the type of the appender
* @see AbstractAppender

View file

@ -4,9 +4,9 @@
* 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.
@ -19,7 +19,6 @@ import java.util.*;
import ghidra.app.plugin.processors.sleigh.template.*;
import ghidra.program.model.address.AddressFactory;
import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.lang.Language;
import ghidra.program.model.pcode.PcodeOp;
import ghidra.program.model.pcode.Varnode;
@ -37,11 +36,11 @@ public interface PcodeFormatter<T> {
}
/**
* Format the pcode ops with a specified {@link AddressFactory}. For use when the
* pcode ops can reference program-specific address spaces.
* Format the pcode ops with a specified {@link AddressFactory}. For use when the pcode ops can
* reference program-specific address spaces.
*
* @param language the language generating the p-code
* @param addrFactory addressFactory to use when generating pcodeop templates
* @param addrFactory addressFactory to use when generating pcodeop templates
* @param pcodeOps p-code ops to format
* @return the formatted result
*
@ -71,8 +70,8 @@ public interface PcodeFormatter<T> {
ArrayList<OpTpl> list = new ArrayList<OpTpl>();
HashMap<Integer, Integer> labelMap = new HashMap<Integer, Integer>(); // label offset to index map
for (PcodeOp pcodeOp : pcodeOps) {
for (int seq = 0; seq < pcodeOps.size(); seq++) {
PcodeOp pcodeOp = pcodeOps.get(seq);
int opcode = pcodeOp.getOpcode();
VarnodeTpl outputTpl = null;
@ -90,7 +89,7 @@ public interface PcodeFormatter<T> {
if (i == 0 && (opcode == PcodeOp.BRANCH || opcode == PcodeOp.CBRANCH)) {
// Handle internal branch destination represented by constant destination
if (input.isConstant()) {
int labelOffset = pcodeOp.getSeqnum().getTime() + (int) input.getOffset();
int labelOffset = seq + (int) input.getOffset();
int labelIndex;
if (labelMap.containsKey(labelOffset)) {
labelIndex = labelMap.get(labelOffset);
@ -117,6 +116,10 @@ public interface PcodeFormatter<T> {
Collections.sort(offsetList);
for (int i = offsetList.size() - 1; i >= 0; i--) {
int labelOffset = offsetList.get(i);
if (labelOffset > pcodeOps.size()) {
// Skip jumps out of this block/program
continue;
}
int labelIndex = labelMap.get(labelOffset);
OpTpl labelTpl = getLabelOpTemplate(addrFactory, labelIndex);
list.add(labelOffset, labelTpl);
@ -142,11 +145,7 @@ public interface PcodeFormatter<T> {
private static VarnodeTpl getVarnodeTpl(AddressFactory addrFactory, Varnode v) {
ConstTpl offsetTpl = new ConstTpl(ConstTpl.REAL, v.getOffset());
AddressSpace addressSpace = addrFactory.getAddressSpace(v.getSpace());
if (addressSpace == null) {
throw new IllegalArgumentException("Unknown varnode space ID: " + v.getSpace());
}
ConstTpl spaceTpl = new ConstTpl(addressSpace);
ConstTpl spaceTpl = new ConstTpl(v.getAddress().getAddressSpace());
ConstTpl sizeTpl = new ConstTpl(ConstTpl.REAL, v.getSize());
return new VarnodeTpl(spaceTpl, offsetTpl, sizeTpl);
}

View file

@ -4,9 +4,9 @@
* 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.
@ -24,8 +24,8 @@ import ghidra.util.SystemUtilities;
/**
* {@link FloatFormat} provides IEEE 754 floating-point encoding formats in support of
* floating-point data types and floating-point emulation. A combination of Java
* float/double and {@link BigFloat} are used to facilitate floating-point operations.
* floating-point data types and floating-point emulation. A combination of Java float/double and
* {@link BigFloat} are used to facilitate floating-point operations.
*/
public class FloatFormat {
@ -116,8 +116,8 @@ public class FloatFormat {
}
else if (size == 10) {
/**
* 80-bit double extended precision format
* See https://en.wikipedia.org/wiki/Extended_precision
* 80-bit double extended precision format. See
* https://en.wikipedia.org/wiki/Extended_precision
*/
signbit_pos = 79;
exp_pos = 64;
@ -155,6 +155,7 @@ public class FloatFormat {
/**
* Get the maximum finite {@link BigFloat} value for this format
*
* @return maximum finite {@link BigFloat} value
*/
public BigFloat getMaxBigFloat() {
@ -163,6 +164,7 @@ public class FloatFormat {
/**
* Get the minimum finite subnormal {@link BigFloat} value for this format
*
* @return minimum finite subnormal {@link BigFloat} value
*/
public BigFloat getMinBigFloat() {
@ -318,8 +320,8 @@ public class FloatFormat {
/**
* Decode {@code encoding} to a BigFloat using this format.
*
* The method {@link #decodeBigFloat(BigInteger)} should be used for encodings
* larger than 8 bytes.
* The method {@link #decodeBigFloat(BigInteger)} should be used for encodings larger than 8
* bytes.
*
* @param encoding the encoding
* @return the decoded value as a BigFloat
@ -495,7 +497,7 @@ public class FloatFormat {
switch (value.kind) {
case QUIET_NAN:
case SIGNALING_NAN:
return getNaNEncoding(false);
return getNaNEncoding(value.sign < 0);
case INFINITE:
return getInfinityEncoding(value.sign < 0);
case FINITE:
@ -621,8 +623,9 @@ public class FloatFormat {
}
/**
* Perform appropriate rounding and conversion to BigDecimal prior to generating
* a formatted decimal string of the specified BigFloat value.
* Perform appropriate rounding and conversion to BigDecimal prior to generating a formatted
* decimal string of the specified BigFloat value.
*
* @param bigFloat value
* @return decimal string representation
*/
@ -631,11 +634,13 @@ public class FloatFormat {
}
/**
* Perform appropriate rounding and conversion to BigDecimal prior to generating
* a formatted decimal string of the specified BigFloat value.
* Perform appropriate rounding and conversion to BigDecimal prior to generating a formatted
* decimal string of the specified BigFloat value.
*
* @param bigFloat value
* @param compact if true the precision will be reduced to a form which is still equivalent at
* the binary encoding level for this format. Enabling this will incur additional overhead.
* the binary encoding level for this format. Enabling this will incur additional
* overhead.
* @return decimal string representation
*/
public String toDecimalString(BigFloat bigFloat, boolean compact) {
@ -643,8 +648,8 @@ public class FloatFormat {
}
/**
* Convert an encoded value to a binary floating point representation.
* This is intended for diagnostic purposes only.
* Convert an encoded value to a binary floating point representation. This is intended for
* diagnostic purposes only.
*
* NB: this method should not be used if {@link #size}&gt;8
*
@ -692,8 +697,8 @@ public class FloatFormat {
}
/**
* Convert an encoded value to a binary floating point representation.
* This is intended for diagnostic purposes only.
* Convert an encoded value to a binary floating point representation. This is intended for
* diagnostic purposes only.
*
* @param encoding the encoding of a floating point value in this format
* @return a binary string representation of the encoded floating point {@code encoding}
@ -741,6 +746,7 @@ public class FloatFormat {
/**
* Convert a native float to {@link BigFloat} using 4-byte IEEE 754 encoding
*
* @param f a float
* @return {@link BigFloat} equal to {@code f}
*/
@ -750,6 +756,7 @@ public class FloatFormat {
/**
* Convert a native double to {@link BigFloat} using 8-byte IEEE 754 encoding
*
* @param d a double
* @return {@link BigFloat} equal to {@code f}
*/
@ -762,8 +769,9 @@ public class FloatFormat {
}
/**
* Get 4-byte binary encoding for the specified native float value.
* This is intended for diagnostic purposes only.
* Get 4-byte binary encoding for the specified native float value. This is intended for
* diagnostic purposes only.
*
* @param f a float
* @return binary representation of {@code f}
*/
@ -772,8 +780,9 @@ public class FloatFormat {
}
/**
* Get 8-byte binary encoding for the specified native double value.
* This is intended for diagnostic purposes only.
* Get 8-byte binary encoding for the specified native double value. This is intended for
* diagnostic purposes only.
*
* @param d a double
* @return binary representation of {@code f}
*/
@ -782,8 +791,9 @@ public class FloatFormat {
}
/**
* Get binary encoding for the specified rounded {@link BigFloat} value.
* This is intended for diagnostic purposes only.
* Get binary encoding for the specified rounded {@link BigFloat} value. This is intended for
* diagnostic purposes only.
*
* @param value floating point value
* @return binary representation of {@code value}
*/
@ -800,9 +810,11 @@ public class FloatFormat {
}
/**
* right shift and round to nearest even or left shift to an integer with lead bit at newLeadBit.
* right shift and round to nearest even or left shift to an integer with lead bit at
* newLeadBit.
*
* The final round up might cause a carry that propagates up, so this must be followed by a test.
* The final round up might cause a carry that propagates up, so this must be followed by a
* test.
*
* @param i integer representation of mantissa 1.xxxxx
* @param newLeadBit the bit position we want as a new lead bit
@ -866,8 +878,8 @@ public class FloatFormat {
/**
* Construct SmallFloat Data. (similar to BigFloat)
*
* @param fracbits number of fractional bits (positive non-zero value; includes additional
* implied bit if relavent).
* @param fracbits number of fractional bits (positive non-zero value; includes additional
* implied bit if relavent).
* @param expbits maximum number of bits in exponent
* @param kind the Kind, FINITE, INFINITE, ...
* @param sign +1 or -1
@ -1178,16 +1190,15 @@ public class FloatFormat {
}
/**
* Constructs a {@code BigFloat} initialized to the value
* represented by the specified decimal {@code String}, as performed
* by {@link BigDecimal#BigDecimal(String)}. Other values permitted
* are (case-insenstive): "NaN", "Infinity", "+Infinity", "-Infinity"
* (See {@link BigFloat#NAN}, {@link BigFloat#INFINITY}, {@link BigFloat#POSITIVE_INFINITY},
* Constructs a {@code BigFloat} initialized to the value represented by the specified decimal
* {@code String}, as performed by {@link BigDecimal#BigDecimal(String)}. Other values permitted
* are (case-insenstive): "NaN", "Infinity", "+Infinity", "-Infinity" (See {@link BigFloat#NAN},
* {@link BigFloat#INFINITY}, {@link BigFloat#POSITIVE_INFINITY},
* {@link BigFloat#NEGATIVE_INFINITY}).
*
* @param string the string to be parsed.
* @return value as a {@link BigFloat}
* @throws NullPointerException if the string is null
* @throws NullPointerException if the string is null
* @throws NumberFormatException if the string parse fails.
*/
public BigFloat getBigFloat(String string) throws NumberFormatException {
@ -1206,12 +1217,12 @@ public class FloatFormat {
}
/**
* Constructs a {@code BigFloat} initialized to the value
* represented by the specified {@code BigDecimal}.
* Constructs a {@code BigFloat} initialized to the value represented by the specified
* {@code BigDecimal}.
*
* @param value the decimal value.
* @return value as a {@link BigFloat}
* @throws NullPointerException if the string is null
* @throws NullPointerException if the string is null
* @throws NumberFormatException if the string parse fails.
*/
public BigFloat getBigFloat(BigDecimal value) {

View file

@ -4,9 +4,9 @@
* 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.
@ -16,33 +16,26 @@
package ghidra.program.model.address;
/**
* Class used represent "special addresses"
* Class used to represent "special addresses"
*/
public class SpecialAddress extends GenericAddress {
SpecialAddress(String name) {
super(new GenericAddressSpace(name, 0, 1, AddressSpace.TYPE_NONE, -1), 0);
}
/**
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
public String toString() {
return addrSpace.getName();
}
/**
* @see ghidra.program.model.address.Address#toString(boolean)
*/
@Override
public String toString(boolean showAddressSpace) {
public String toString(boolean showAddressSpace) {
return addrSpace.getName();
}
/**
* @see ghidra.program.model.address.Address#toString(java.lang.String)
*/
@Override
public String toString(String prefix) {
public String toString(String prefix) {
return addrSpace.getName();
}
}

View file

@ -62,7 +62,7 @@ public class PcodeOp {
public static final int INT_SLESS = 13; // Return TRUE if signed op1 < signed op2
public static final int INT_SLESSEQUAL = 14; // Return TRUE if signed op1 <= signed op2
public static final int INT_LESS = 15; // Return TRUE if unsigned op1 < unsigned op2
// Also indicates borrow on unsigned substraction
// Also indicates borrow on unsigned subtraction
public static final int INT_LESSEQUAL = 16; // Return TRUE if unsigned op1 <= unsigned op2
public static final int INT_ZEXT = 17; // Zero extend operand
public static final int INT_SEXT = 18; // Sign extend operand

View file

@ -4,9 +4,9 @@
* 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.
@ -15,7 +15,8 @@
*/
package ghidra.app.plugin.assembler.sleigh;
import static org.junit.Assert.*;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import org.junit.Test;
@ -281,6 +282,11 @@ public class x64AssemblyTest extends AbstractAssemblyTest {
"48:81:65:f8:00:00:ff:ff");
}
@Test
public void testAssemble_PUSH_R8() {
assertOneCompatRestExact("PUSH R8", "41:50");
}
//@Ignore("This is a demonstration of an issue with signedness and scalar print pieces.")
//@Test
public void testAssembly_AND_mRBP_n0x8m_0x80()