Class JitDataFlowState

java.lang.Object
ghidra.pcode.emu.jit.analysis.JitDataFlowState
All Implemented Interfaces:
PcodeExecutorState<JitVal>, PcodeExecutorStatePiece<JitVal,JitVal>

public class JitDataFlowState extends Object implements PcodeExecutorState<JitVal>
An implementation of PcodeExecutorState for per-block data flow interpretation

In p-code interpretation, this interface's purpose is to store the current value of varnodes in the emulation/interpretation state. Here we implement it using T:=JitVal, and track the latest variable definition of vanodes in the data flow interpretation. The adaptation is fairly straightforward, except when varnode accesses do not match their latest definitions exactly, e.g., an access of EAX when the latest definition is for RAX. Thus, this state object may synthesize subpiece and catenate ops to model the "off-cut" use of one or more such definitions. Additionally, in preparation for inter-block data flow analysis, when no definition is present for a varnode (or part of a varnode) access, this state will synthesize phi ops. See setVar and getVar for details.

This state only serves to analyze data flow through register and unique variables. Because we know these are only accessible to the thread, we stand to save much execution time by bypassing the JitBytesPcodeExecutorState at run time. We can accomplish this by mapping these variables to suitable JVM local variables. Thus, we have one map of entries for register space and another for unique space. Accesses to other spaces do not mutate or read from either of those maps, but this class may generate a suitable JitVal for the use-def graph.

  • Method Details

    • getLanguage

      public Language getLanguage()
      Description copied from interface: PcodeExecutorStatePiece
      Get the language defining the address spaces of this state piece
      Specified by:
      getLanguage in interface PcodeExecutorStatePiece<JitVal,JitVal>
      Returns:
      the language
    • getArithmetic

      public JitDataFlowArithmetic getArithmetic()
      Description copied from interface: PcodeExecutorStatePiece
      Get the arithmetic used to manipulate values of the type stored by this state
      Specified by:
      getArithmetic in interface PcodeExecutorStatePiece<JitVal,JitVal>
      Returns:
      the arithmetic
    • streamPieces

      public Stream<PcodeExecutorStatePiece<?,?>> streamPieces()
      Description copied from interface: PcodeExecutorStatePiece
      Stream over the pieces within.

      If this piece is not a composition of others, then simply stream this piece in a singleton. Otherwise, stream the component pieces. (Do not include the composition itself, just the component pieces.)

      Specified by:
      streamPieces in interface PcodeExecutorStatePiece<JitVal,JitVal>
      Returns:
      the stream
    • setVar

      public void setVar(AddressSpace space, JitVal offset, int size, boolean quantize, JitVal val)
      Set the value of a variable

      This and getVar(AddressSpace, JitVal, int, boolean, Reason) are where we connect the interpretation to the maps of definitions we keep in this state. We examine the varnode's type first. We can't write to a constant, and that shouldn't be allowed anyway, so we warn if we observe that. We'll ignore any indirect writes, because those are denoted by store ops. We also don't do much here with direct writes. The writes to such variables are handled by JitMemoryOutVar. Such output variables are actually passed in as val here, but need only be stored in a map if they are register or unique variables.

      Specified by:
      setVar in interface PcodeExecutorStatePiece<JitVal,JitVal>
      Parameters:
      space - the address space
      offset - the offset within the space
      size - the size of the variable
      quantize - true to quantize to the language's "addressable unit"
      val - the value
      Implementation Notes:
      We use this only to log possible storage bypasses. All uniques will be bypassed. Registers must be written, but it is safe to bypass subsequent loads. Actually, with a pre-load of register values and a try-finally to write them, we optimize register access, too. Turns out registers and uniques get the same treatment, at least for now, partly for debugging purposes, but also because with crossbuilds, uniques must outlive their generating instruction. LATER: Can we examine an instruction's named blocks to determine which uniques need saving? LATER: It might also be possible (with significant changes to the strategy in JitVarScopeModel) to be more selective in which variables become live and are retired. But we must avoid the situation where we might "save" a variable that was never restored or written. That would effectively erase a variable that should have been left unmodified.

      Memory must be written. Unless we can determine for sure the memory is non-volatile, we must presume volatile, so no bypassing is allowed. LATER: We might consider assuming stack-based accesses are non-volatile, but I'm not sure that is appropriate either. Technically one thread may launch another, providing a ref to a stack variable it knows will live for the entire thread's life.

    • setVarInternal

      public void setVarInternal(AddressSpace space, JitVal offset, int size, JitVal val)
      Description copied from interface: PcodeExecutorStatePiece
      Set the value of a variable without issuing callbacks
      Specified by:
      setVarInternal in interface PcodeExecutorStatePiece<JitVal,JitVal>
      Parameters:
      space - the address space
      offset - the offset within the space
      size - the size of the variable
      val - the value
    • getDefinitions

      public List<JitVal> getDefinitions(Varnode varnode)
      Get an ordered list of all values involved in the latest definition of the given varnode.
      Parameters:
      varnode - the varnode whose definitions to retrieve
      Returns:
      the list of values
      See Also:
    • getDefinitions

      public List<JitVal> getDefinitions(Register register)
      Get an ordered list of all values involved in the latest definition of the given varnode.
      Parameters:
      register - the register whose definitions to retrieve
      Returns:
      the list of values
      See Also:
    • getVar

      public JitVal getVar(AddressSpace space, JitVal offset, int size, boolean quantize, PcodeExecutorStatePiece.Reason reason)
      Get the value of a variable

      This and setVar(AddressSpace, JitVal, int, boolean, JitVal) are where we connect the interpretation to the maps of definitions we keep in this state. We examine the varnode's type first. If it's a constant or memory variable, it just returns the appropriate JitConstVal, JitDirectMemoryVar, or JitIndirectMemoryVar. If it's a register or unique, then we retrieve the latest definition(s) as in JitDataFlowState.MiniDFState.getDefinitions(AddressSpace, long, int). In the simple case of an exact definition, we return it. Otherwise, this synthesizes the appropriate op(s), enters them into the use-def graph, and returns the final output.

      Specified by:
      getVar in interface PcodeExecutorStatePiece<JitVal,JitVal>
      Parameters:
      space - the address space
      offset - the offset within the space
      size - the size of the variable
      quantize - true to quantize to the language's "addressable unit"
      reason - the reason for reading the variable
      Returns:
      the value
    • getVarInternal

      public JitVal getVarInternal(AddressSpace space, JitVal offset, int size, PcodeExecutorStatePiece.Reason reason)
      Description copied from interface: PcodeExecutorStatePiece
      Get the value of a variable without issuing callbacks
      Specified by:
      getVarInternal in interface PcodeExecutorStatePiece<JitVal,JitVal>
      Parameters:
      space - the address space
      offset - the offset within the space
      size - the size of the variable
      reason - the reason for reading the variable
      Returns:
      the value
    • getRegisterValues

      public Map<Register,JitVal> getRegisterValues()
      Description copied from interface: PcodeExecutorStatePiece
      Get all register values known to this state

      When the state acts as a cache, it should only return those cached.

      Specified by:
      getRegisterValues in interface PcodeExecutorStatePiece<JitVal,JitVal>
      Returns:
      a map of registers and their values
    • getConcreteBuffer

      public MemBuffer getConcreteBuffer(Address address, PcodeArithmetic.Purpose purpose)
      Description copied from interface: PcodeExecutorStatePiece
      Bind a buffer of concrete bytes at the given start address
      Specified by:
      getConcreteBuffer in interface PcodeExecutorStatePiece<JitVal,JitVal>
      Parameters:
      address - the start address
      purpose - the reason why the emulator needs a concrete value
      Returns:
      a buffer
    • clear

      public void clear()
      Description copied from interface: PcodeExecutorStatePiece
      Erase the entire state or piece

      This is generally only useful when the state is itself a cache to another object. This will ensure the state is reading from that object rather than a stale cache. If this is not a cache, this could in fact clear the whole state, and the machine using it will be left in the dark.

      Specified by:
      clear in interface PcodeExecutorStatePiece<JitVal,JitVal>
    • fork

      Description copied from interface: PcodeExecutorStatePiece
      Create a deep copy of this state
      Specified by:
      fork in interface PcodeExecutorState<JitVal>
      Specified by:
      fork in interface PcodeExecutorStatePiece<JitVal,JitVal>
      Parameters:
      cb - callbacks to receive emulation events
      Returns:
      the copy
    • getVarnodesRead

      public Set<Varnode> getVarnodesRead()
      Get a complete catalog of all varnodes read, including overlapping, subregs, etc.
      Returns:
      the set of varnodes
    • getVarnodesWritten

      public Set<Varnode> getVarnodesWritten()
      Get a complete catalog of all varnodes written, including overlapping, subregs, etc.
      Returns:
      the set of varnodes
    • captureState

      public JitDataFlowState.MiniDFState captureState()
      Capture the current state of intra-block analysis.

      This may be required for follow-up op-use analysis by a JitCallOtherOpIf invoked using the standard strategy. All live varnodes at the time of the call must be considered used.

      Returns:
      the captured state