Package ghidra.pcode.emu.jit.analysis

# Class JitDataFlowArithmetic

java.lang.Object
ghidra.pcode.emu.jit.analysis.JitDataFlowArithmetic

All Implemented Interfaces:
  `PcodeArithmetic<JitVal>`

public class JitDataFlowArithmetic extends Object implements PcodeArithmetic<JitVal>
A p-code arithmetic for interpreting p-code and constructing a use-def graph

This is used for intra-block data flow analysis. We leverage the same API as is used for concrete p-code interpretation, but we use it for an abstraction. The type of the interpretation is `T:=``JitVal`, which can consist of constants and variables in the use-def graph. The arithmetic must be provided to the `JitDataFlowExecutor`. The intra-block portions of the use-def graph are populated as each block is interpreted by the executor.

The general strategy for each of the arithmetic operations is to 1) generate the output SSA variable for the op, 2) generate the op node for the generated output and given inputs, 3) enter the op into the use-def graph as the definition of its output, 4) record the inputs and used by the new op, and finally 5) return the generated output.

There should only need to be one of these per data flow model, not per block.

- ## Nested Class Summary

  ## Nested classes/interfaces inherited from interface ghidra.pcode.exec.PcodeArithmetic

  `PcodeArithmetic.Purpose`

- ## Field Summary

  ### Fields inherited from interface ghidra.pcode.exec.PcodeArithmetic

  `SIZEOF_SIZEOF`

- ## Constructor Summary

  Constructors
  Constructor
  Description
  `JitDataFlowArithmetic(JitAnalysisContext context, JitDataFlowModel dfm)`
  Construct the arithmetic

- ## Method Summary

  Modifier and Type
  Method
  Description
  `JitVal`
  `binaryOp(int opcode, int sizeout, int sizein1, JitVal in1, int sizein2, JitVal in2)`
  Apply a binary operator to the given inputs
  `JitVal`
  `binaryOp(PcodeOp op, JitVal in1, JitVal in2)`
  Apply a binary operator to the given input
  `JitVal`
  `catenate(Varnode outVn, List<JitVal> parts)`
  Construct the catenation of the given values to form the given output varnode.
  `JitVal`
  `fromConst(byte[] value)`
  Convert the given constant concrete value to type `T` having the same size.
  `Class<JitVal>`
  `getDomain()`
  Get the type of values over which this arithmetic operates.
  `Endian`
  `getEndian()`
  Get the endianness of this arithmetic
  `JitVal`
  `modAfterLoad(int sizeinOffset, AddressSpace space, JitVal inOffset, int sizeinValue, JitVal inValue)`
  Apply any modifications after a value is loaded
  `JitVal`
  `modAfterLoad(PcodeOp op, AddressSpace space, JitVal inOffset, JitVal inValue)`
  Apply any modifications after a value is loaded
  `JitVal`
  `modBeforeStore(int sizeinOffset, AddressSpace space, JitVal inOffset, int sizeinValue, JitVal inValue)`
  Apply any modifications before a value is stored
  `JitVal`
  `modBeforeStore(PcodeOp op, AddressSpace space, JitVal inOffset, JitVal inValue)`
  Apply any modifications before a value is stored
  `JitVal`
  `shaveFromLeft(int amt, JitVal in1)`
  Remove `amt` bytes from the left of the value.
  `JitVal`
  `shaveFromRight(int amt, JitVal in1)`
  Remove `amt` bytes from the right of the value.
  `long`
  `sizeOf(JitVal value)`
  Get the size in bytes, if possible, of the given abstract value
  `JitVal`
  `subpiece(int size, int offset, JitVal v)`
  Compute the subpiece of a value.
  `byte[]`
  `toConcrete(JitVal value, PcodeArithmetic.Purpose purpose)`
  Convert, if possible, the given abstract value to a concrete byte array
  `JitVal`
  `truncFromLeft(Varnode in1Vn, int amt, JitVal in1)`
  Remove `amt` bytes from the left of the varnode.
  `JitVal`
  `truncFromRight(Varnode in1Vn, int amt, JitVal in1)`
  Remove `amt` bytes from the right of the varnode.
  `Varnode`
  `truncVnFromLeft(Varnode vn, int amt)`
  `Varnode`
  `truncVnFromRight(Varnode vn, int amt)`
  `JitVal`
  `unaryOp(int opcode, int sizeout, int sizein1, JitVal in1)`
  Apply a unary operator to the given input
  `JitVal`
  `unaryOp(PcodeOp op, JitVal in1)`
  Apply a unary operator to the given input

  ### Methods inherited from class java.lang.Object

  `clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait`

  ### Methods inherited from interface ghidra.pcode.exec.PcodeArithmetic

  `fromConst, fromConst, fromConst, fromConst, fromConst, fromConst, fromConst, fromConst, fromConst, fromConst, fromConst, isTrue, ptrAdd, ptrSub, sizeOfAbstract, toAddress, toBigInteger, toDouble, toFloat, toLong, toRegisterValue`

- ## Constructor Details

  - ### JitDataFlowArithmetic

    public JitDataFlowArithmetic(JitAnalysisContext context, JitDataFlowModel dfm)
    Construct the arithmetic
    Parameters:
      `context` - the analysis context
      `dfm` - the owning data flow model

- ## Method Details

  - ### getDomain

    public Class<JitVal> getDomain()
    Description copied from interface: `PcodeArithmetic`
    Get the type of values over which this arithmetic operates.
    Specified by:
      `getDomain` in interface `PcodeArithmetic<JitVal>`
    Returns:
      the domain

  - ### getEndian

    public Endian getEndian()
    Description copied from interface: `PcodeArithmetic`
    Get the endianness of this arithmetic

    Often T is a byte array, or at least represents one abstractly. Ideally, it is an array where each element is an abstraction of a byte. If that is the case, then the arithmetic likely has to interpret those bytes as integral values according to an endianness. This should return that endianness.

    If the abstraction has no notion of endianness, return null. In that case, the both `PcodeArithmetic.fromConst(BigInteger, int, boolean)` and `PcodeArithmetic.fromConst(long, int)` must be overridden. Furthermore, unless `PcodeArithmetic.toConcrete(Object, Purpose)` is guaranteed to throw an exception, then `PcodeArithmetic.toBigInteger(Object, Purpose)` and `PcodeArithmetic.toLong(Object, Purpose)` must also be overridden.

    Specified by:
      `getEndian` in interface `PcodeArithmetic<JitVal>`
    Returns:
      the endianness or null

  - ### truncVnFromRight

    public Varnode truncVnFromRight(Varnode vn, int amt)

  - ### truncFromRight

    public JitVal truncFromRight(Varnode in1Vn, int amt, JitVal in1)
    Remove `amt` bytes from the right of the varnode.

    "Right" is considered with respect to the machine endianness. If it is little endian, then the byte are shaved from the left of the value. This should be used when getting values from the state to remove pieces from off-cut values. It should be applied before the pieces are ordered according to machine endianness.

    Parameters:
      `in1Vn` - the varnode representing the input
      `amt` - the number of bytes to remove
      `in1` - the input (really a value read from the state)
    Returns:
      the resulting value

  - ### truncVnFromLeft

    public Varnode truncVnFromLeft(Varnode vn, int amt)

  - ### truncFromLeft

    public JitVal truncFromLeft(Varnode in1Vn, int amt, JitVal in1)
    Remove `amt` bytes from the left of the varnode.

    "Left" is considered with respect to the machine endianness. If it is little endian, then the byte are shaved from the right of the value. This should be used when getting values from the state to remove pieces from off-cut values. It should be applied before the pieces are ordered according to machine endianness.

    Parameters:
      `in1Vn` - the varnode representing the input
      `amt` - the number of bytes to remove
      `in1` - the input (really a value read from the state)
    Returns:
      the resulting value

  - ### shaveFromRight

    public JitVal shaveFromRight(int amt, JitVal in1)
    Remove `amt` bytes from the right of the value.

    The value is unaffected by the machine endianness, except to designate the output varnode.

    Parameters:
      `amt` - the number of bytes to remove
      `in1` - the input
    Returns:
      the output

  - ### shaveFromLeft

    public JitVal shaveFromLeft(int amt, JitVal in1)
    Remove `amt` bytes from the left of the value.

    The value is unaffected by the machine endianness, except to designate the output varnode.

    Parameters:
      `amt` - the number of bytes to remove
      `in1` - the input
    Returns:
      the output

  - ### subpiece

    public JitVal subpiece(int size, int offset, JitVal v)
    Compute the subpiece of a value.

    The result is added to the use-def graph. The output varnode is computed from the input varnode and the subpiece parameters. This is used to handle variable retrieval when an access only include parts of a value previously written. Consider the x86 assembly:

         MOV RAX, qword ptr [...]
         MOV dword ptr [...], EAX

    The second line reads `EAX`, which consists of only the lower part of `RAX`. Thus, we synthesize a subpiece op. These are distinct from an actual `PcodeOp.SUBPIECE` op, since we sometimes needs to filter out synthetic ops.

    Parameters:
      `size` - the size of the output variable in bytes
      `offset` - the subpiece offset (number of bytes shifted right)
      `v` - the input value
    Returns:
      the output value

  - ### catenate

    public JitVal catenate(Varnode outVn, List<JitVal> parts)
    Construct the catenation of the given values to form the given output varnode.

    The result is added to the use-def graph. This is used to handle variable retrieval when the pattern of accesses indicates catenation. Consider the x86 assembly:

         MOV AH, byte ptr [...]
         MOV AL, byte ptr [...]
         MOV word ptr [...], AX

    On the third line, the value in `AX` is the catenation of whatever values were written into `AH` and `AL`. Thus, we synthesize a catenation op node in the use-def graph.

    Parameters:
      `outVn` - the output varnode
      `parts` - the list of values to catenate, ordered by machine endianness
    Returns:
      the output value
    See Also:
      - `JitDataFlowState.MiniDFState.getDefinitions(AddressSpace, long, int)`

  - ### unaryOp

    public JitVal unaryOp(PcodeOp op, JitVal in1)
    Description copied from interface: `PcodeArithmetic`
    Apply a unary operator to the given input

    This provides the full p-code op, allowing deeper inspection of the code. For example, an arithmetic may wish to distinguish immediate (constant) values from variables. By default, this unpacks the details and defers to `PcodeArithmetic.unaryOp(int, int, int, Object)`.

    Specified by:
      `unaryOp` in interface `PcodeArithmetic<JitVal>`
    Parameters:
      `op` - the operation
      `in1` - the input value
    Returns:
      the output value

  - ### unaryOp

    public JitVal unaryOp(int opcode, int sizeout, int sizein1, JitVal in1)
    Description copied from interface: `PcodeArithmetic`
    Apply a unary operator to the given input

    Note the sizes of variables are given, because values don't necessarily have an intrinsic size. For example, a `BigInteger` may have a minimum encoding size, but that does not necessarily reflect the size of the variable from which is was read.

    Specified by:
      `unaryOp` in interface `PcodeArithmetic<JitVal>`
    Parameters:
      `opcode` - the p-code opcode
      `sizeout` - the size (in bytes) of the output variable
      `sizein1` - the size (in bytes) of the input variable
      `in1` - the input value
    Returns:
      the output value

  - ### binaryOp

    public JitVal binaryOp(PcodeOp op, JitVal in1, JitVal in2)
    Description copied from interface: `PcodeArithmetic`
    Apply a binary operator to the given input

    This provides the full p-code op, allowing deeper inspection of the code. For example, an arithmetic may wish to distinguish immediate (constant) values from variables. By default, this unpacks the details and defers to `PcodeArithmetic.binaryOp(int, int, int, Object, int, Object)`.

    Specified by:
      `binaryOp` in interface `PcodeArithmetic<JitVal>`
    Parameters:
      `op` - the operation
      `in1` - the first (left) input value
      `in2` - the second (right) input value
    Returns:
      the output value

  - ### binaryOp

    public JitVal binaryOp(int opcode, int sizeout, int sizein1, JitVal in1, int sizein2, JitVal in2)
    Description copied from interface: `PcodeArithmetic`
    Apply a binary operator to the given inputs

    Note the sizes of variables are given, because values don't necessarily have an intrinsic size. For example, a `BigInteger` may have a minimum encoding size, but that does not necessarily reflect the size of the variable from which is was read.

    Specified by:
      `binaryOp` in interface `PcodeArithmetic<JitVal>`
    Parameters:
      `opcode` - the operation's opcode. See `PcodeOp`.
      `sizeout` - the size (in bytes) of the output variable
      `sizein1` - the size (in bytes) of the first (left) input variable
      `in1` - the first (left) input value
      `sizein2` - the size (in bytes) of the second (right) input variable
      `in2` - the second (right) input value
    Returns:
      the output value

  - ### modBeforeStore

    public JitVal modBeforeStore(PcodeOp op, AddressSpace space, JitVal inOffset, JitVal inValue)
    Apply any modifications before a value is stored

    This provides the full p-code op, allowing deeper inspection of the code. NOTE: STORE ops always quantize the offset.

    We override this to record the `store` op into the use-def graph. As "output" we just return `inValue`. The executor will call `setVal`, but the state will just ignore it, because it will be an indirect memory write.

    Specified by:
      `modBeforeStore` in interface `PcodeArithmetic<JitVal>`
    Parameters:
      `op` - the operation
      `space` - the address space
      `inOffset` - the value used as the offset
      `inValue` - the value to store
    Returns:
      the modified value to store

  - ### modBeforeStore

    public JitVal modBeforeStore(int sizeinOffset, AddressSpace space, JitVal inOffset, int sizeinValue, JitVal inValue)
    Description copied from interface: `PcodeArithmetic`
    Apply any modifications before a value is stored

    This implements any abstractions associated with `PcodeOp.STORE`. This is called on the offset and the value before the value is actually stored into the state. NOTE: STORE ops always quantize the offset.

    Specified by:
      `modBeforeStore` in interface `PcodeArithmetic<JitVal>`
    Parameters:
      `sizeinOffset` - the size (in bytes) of the variable used for indirection
      `space` - the address space
      `inOffset` - the value used as the address (or offset)
      `sizeinValue` - the size (in bytes) of the variable to store and of the output variable
      `inValue` - the value to store
    Returns:
      the modified value to store

  - ### modAfterLoad

    public JitVal modAfterLoad(PcodeOp op, AddressSpace space, JitVal inOffset, JitVal inValue)
    Apply any modifications after a value is loaded

    This provides the full p-code op, allowing deeper inspection of the code. NOTE: LOAD ops always quantize the offset.

    We override this to record the `load` op into the use-def graph. For our `inValue`, the `state` will have just returned the `dummy indirect` variable definition. We must not "use" this. Instead, we must take our other parameters to construct the load op and return its output.

    Specified by:
      `modAfterLoad` in interface `PcodeArithmetic<JitVal>`
    Parameters:
      `op` - the operation
      `space` - the address space
      `inOffset` - the value used as the offset
      `inValue` - the value loaded
    Returns:
      the modified value loaded

  - ### modAfterLoad

    public JitVal modAfterLoad(int sizeinOffset, AddressSpace space, JitVal inOffset, int sizeinValue, JitVal inValue)
    Description copied from interface: `PcodeArithmetic`
    Apply any modifications after a value is loaded

    This implements any abstractions associated with `PcodeOp.LOAD`. This is called on the address/offset and the value after the value is actually loaded from the state. NOTE: LOAD ops always quantize the offset.

    Specified by:
      `modAfterLoad` in interface `PcodeArithmetic<JitVal>`
    Parameters:
      `sizeinOffset` - the size (in bytes) of the variable used for indirection
      `space` - the address space
      `inOffset` - the value used as the offset
      `sizeinValue` - the size (in bytes) of the variable loaded and of the output variable
      `inValue` - the value loaded
    Returns:
      the modified value loaded

  - ### fromConst

    public JitVal fromConst(byte[] value)
    Description copied from interface: `PcodeArithmetic`
    Convert the given constant concrete value to type `T` having the same size.
    Specified by:
      `fromConst` in interface `PcodeArithmetic<JitVal>`
    Parameters:
      `value` - the constant value
    Returns:
      the value as a `T`

  - ### toConcrete

    public byte[] toConcrete(JitVal value, PcodeArithmetic.Purpose purpose)
    Description copied from interface: `PcodeArithmetic`
    Convert, if possible, the given abstract value to a concrete byte array
    Specified by:
      `toConcrete` in interface `PcodeArithmetic<JitVal>`
    Parameters:
      `value` - the abstract value
      `purpose` - the purpose for which the emulator needs a concrete value
    Returns:
      the array

  - ### sizeOf

    public long sizeOf(JitVal value)
    Description copied from interface: `PcodeArithmetic`
    Get the size in bytes, if possible, of the given abstract value

    If the abstract value does not conceptually have a size, throw an exception.

    Specified by:
      `sizeOf` in interface `PcodeArithmetic<JitVal>`
    Parameters:
      `value` - the abstract value
    Returns:
      the size in bytes