DXR is a code search and navigation tool aimed at making sense of large projects. It supports full-text and regex searches as well as structural queries.

Implementation

Mercurial (a58561ad1899)

VCS Links

Line Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef mozilla_recordreplay_Assembler_h
#define mozilla_recordreplay_Assembler_h

#include "InfallibleVector.h"

#include <utility>

namespace mozilla {
namespace recordreplay {

// Assembler for x64 instructions. This is a simple assembler that is primarily
// designed for use in copying instructions from a function that is being
// redirected.
class Assembler {
 public:
  // Create an assembler that allocates its own instruction storage. Assembled
  // code will never be reclaimed by the system.
  Assembler();

  // Create an assembler that uses the specified memory range for instruction
  // storage.
  Assembler(uint8_t* aStorage, size_t aSize);

  ~Assembler();

  // Mark the point at which we start copying an instruction in the original
  // range.
  void NoteOriginalInstruction(uint8_t* aIp);

  // Get the address where the next assembled instruction will be placed.
  uint8_t* Current();

  ///////////////////////////////////////////////////////////////////////////////
  // Routines for assembling instructions in new instruction storage
  ///////////////////////////////////////////////////////////////////////////////

  // Jump to aTarget. If aTarget is in the range of instructions being copied,
  // the target will be the copy of aTarget instead.
  void Jump(void* aTarget);

  // Conditionally jump to aTarget, depending on the short jump opcode aCode.
  // If aTarget is in the range of instructions being copied, the target will
  // be the copy of aTarget instead.
  void ConditionalJump(uint8_t aCode, void* aTarget);

  // Copy an instruction verbatim from aIp.
  void CopyInstruction(uint8_t* aIp, size_t aSize);

  // push/pop %rax
  void PushRax();
  void PopRax();

  // jump *%rax
  void JumpToRax();

  // call *%rax
  void CallRax();

  // movq/movl/movb 0(%rax), %rax
  void LoadRax(size_t aWidth);

  // cmpq %rax, 0(%rsp)
  void CompareRaxWithTopOfStack();

  // push/pop %rbx
  void PushRbx();
  void PopRbx();

  // movq/movl/movb %rbx, 0(%rax)
  void StoreRbxToRax(size_t aWidth);

  // cmpq/cmpb $literal8, %rax
  void CompareValueWithRax(uint8_t aValue, size_t aWidth);

  // movq $value, %rax
  void MoveImmediateToRax(void* aValue);

  // movq %rax, register
  void MoveRaxToRegister(/*ud_type*/ int aRegister);

  // movq register, %rax
  void MoveRegisterToRax(/*ud_type*/ int aRegister);

  // xchgb register, 0(%rbx)
  void ExchangeByteRegisterWithAddressAtRbx(/*ud_type*/ int aRegister);

  // xchgb $bl, 0(%rax)
  void ExchangeByteRbxWithAddressAtRax();

  // Normalize a Udis86 register to its 8 byte version, returning UD_NONE/zero
  // for unexpected registers.
  static /*ud_type*/ int NormalizeRegister(/*ud_type*/ int aRegister);

  ///////////////////////////////////////////////////////////////////////////////
  // Routines for assembling instructions at arbitrary locations
  ///////////////////////////////////////////////////////////////////////////////

  // Return whether it is possible to patch a short jump to aTarget from aIp.
  static bool CanPatchShortJump(uint8_t* aIp, void* aTarget);

  // Patch a short jump to aTarget at aIp.
  static void PatchShortJump(uint8_t* aIp, void* aTarget);

  // Patch a long jump to aTarget at aIp. Rax may be clobbered.
  static void PatchJumpClobberRax(uint8_t* aIp, void* aTarget);

  // Patch the value used in an earlier MoveImmediateToRax call.
  static void PatchMoveImmediateToRax(uint8_t* aIp, void* aValue);

  // Patch an int3 breakpoint instruction at Ip.
  static void PatchClobber(uint8_t* aIp);

 private:
  // Patch a jump that doesn't clobber any instructions.
  static void PatchJump(uint8_t* aIp, void* aTarget);

  // Consume some instruction storage.
  void Advance(size_t aSize);

  // The maximum amount we can write at a time without a jump potentially
  // being introduced into the instruction stream.
  static const size_t MaximumAdvance = 20;

  inline size_t CountBytes() { return 0; }

  template <typename... Tail>
  inline size_t CountBytes(uint8_t aByte, Tail... aMoreBytes) {
    return 1 + CountBytes(aMoreBytes...);
  }

  inline void CopyBytes(uint8_t* aIp) {}

  template <typename... Tail>
  inline void CopyBytes(uint8_t* aIp, uint8_t aByte, Tail... aMoreBytes) {
    *aIp = aByte;
    CopyBytes(aIp + 1, aMoreBytes...);
  }

  // Write a complete instruction with bytes specified as parameters.
  template <typename... ByteList>
  inline void NewInstruction(ByteList... aBytes) {
    size_t numBytes = CountBytes(aBytes...);
    MOZ_RELEASE_ASSERT(numBytes <= MaximumAdvance);
    uint8_t* ip = Current();
    CopyBytes(ip, aBytes...);
    Advance(numBytes);
  }

  // Storage for assembling new instructions.
  uint8_t* mCursor;
  uint8_t* mCursorEnd;
  bool mCanAllocateStorage;

  // Association between the instruction original and copy pointers, for all
  // instructions that have been copied.
  InfallibleVector<std::pair<uint8_t*, uint8_t*>> mCopiedInstructions;

  // For jumps we have copied, association between the source (in generated
  // code) and target (in the original code) of the jump. These will be updated
  // to refer to their copy (if there is one) in generated code in the
  // assembler's destructor.
  InfallibleVector<std::pair<uint8_t*, uint8_t*>> mJumps;
};

// The number of instruction bytes required for a short jump.
static const size_t ShortJumpBytes = 2;

// The number of instruction bytes required for a jump that may clobber rax.
static const size_t JumpBytesClobberRax = 12;

// The maximum byte length of an x86/x64 instruction.
static const size_t MaximumInstructionLength = 15;

// Make a region of memory RWX.
void UnprotectExecutableMemory(uint8_t* aAddress, size_t aSize);

}  // namespace recordreplay
}  // namespace mozilla

#endif  // mozilla_recordreplay_Assembler_h