Class JitPassageDecoder

java.lang.Object
ghidra.pcode.emu.jit.decode.JitPassageDecoder

public class JitPassageDecoder extends Object
The decoder of a JitPassage to support JIT-accelerated p-code emulation.

When the emulator encounters an address (and contextreg value) that it has not previously translated, it must decode a passage seeded at that required entry point. It must then translate the passage and collects all the resulting entry points, and finally invoke the passage's run method for the required entry point.

Decoding a Passage

Decode starts with a single seed, which is the entry point required by the emulator. As such, that seed must be among the entry points exported by the translator. Decode occurs one stride at a time. Starting with the seed, we decode a stride by disassembling linearly until: 1) We encounter an instruction without fall through, 2) there's already an entry point to a translated passage at an encountered address, or 3) a user injection fails to specify control flow. Case 1 is the normal expected case. For example, when the decoder encounters an unconditional branch, the stride is terminated. Case 2 is meant to reduce duplicative translations, but it does come at some cost during decode time. Suppose execution branches into the middle of a previously translated basic block. (Note that basic blocks are only broken apart using branches in the same passage, so it is possible some branch encountered later would jump into another passage's basic block.) That previously translated passage will not have exposed an entry point at that branch target, so the emulator will begin decoding using the branch target as the seed. Ideally, the resulting passage will consist of a single stride that terminates at an existing entry point. The emulator will translate and execute the passage, which should exit at that entry point, where the emulator can then continue execution. Case 3 is just to ensure execution does not get caught in a translated infinite loop. There will still be an infinite loop, but it can be interrupted while execution is in the emulator's logic rather than the translated logic.

As the stride decoder processes each instruction, it interprets its p-code, along with any generated by user injects, to collect branch targets. For direct branches (branch, cbranch, and call), the target address (and appropriate contextreg value) is added to the queue of seeds, unless that target is already decoded in this passage. A bit of control flow analysis is required to determine whether each instruction (with user injects) has fall through. We borrow the JitControlFlowModel.BlockSplitter from the JitControlFlowModel to accomplish this. We append a "probe" p-code op at the very end, and then once we have the (miniature) control flow graph, we check if there's a path from instruction start to the probe op. If there is, then we can fall through, so decode proceeds to the next instruction. If not, the stride is terminated, so the decoder starts a new stride at the next seed, unless we've met the p-code op, instruction, or stride quota.

The seed queue is a list of JitPassage.ExtBranch records. Each stride is decoded by removing a seed from that queue, decoding instructions, emitting ops, and then creating an JitPassage.IntBranch record targeting the first op of the newly-decoded instruction. The from field is taken from the seed JitPassage.ExtBranch record. Decode will likely terminate before this queue is emptied, in which case, those remaining external branches will become part of the passage's branches. Direct branches to instructions already included in the passage, p-code relative branches, and queued external branches to instructions which have since been decoded all become JitPassage.IntBranch records, too. For indirect branches (branchind, callind, and return), we create JitPassage.IndBranch records. For error cases (e.g., unimplemented), we create JitPassage.ErrBranch records.

  • Constructor Details

    • JitPassageDecoder

      public JitPassageDecoder(JitPcodeThread thread)
      Construct a passage decoder
      Parameters:
      thread - the thread whose instruction decoder, context, and userop library to use.
  • Method Details

    • decodePassage

      public JitPassage decodePassage(Address seed, RegisterValue ctxIn, int maxOps)
      Decode a passage starting at the given seed
      Parameters:
      seed - the seed address
      ctxIn - the seed contextreg value
      maxOps - the maximum-ish number of p-code ops to emit
      Returns:
      the decoded passage
      See Also:
    • decodePassage

      public JitPassage decodePassage(JitPassage.AddrCtx seed, int maxOps)
      Decode a passage starting at the given seed

      We provide a maxOps parameter so that the configured option can be overridden. In particular, the bytecode emitter may exceed the maximum size of a Java method, in which case we must abort, re-decode with fewer ops, and retry. Whether this back off should persist in the configuration is yet to be determined. Output size can vary wildly depending on the number of basic blocks, scope transitions, nature of the ops, etc. We ought to be able to provide a reasonable default value that mostly avoids retries, because each retry essentially wastes an entire JIT translation. On the other hand, if we choose too small a value, we lose some of the benefits of translating the control flow and keeping variables in JVM locals.

      Parameters:
      seed - the required entry point, where decode will start
      maxOps - the maximum-ish number of p-codes to emit
      Returns:
      the decoded passage