summaryrefslogtreecommitdiffstats
path: root/mozglue/misc/interceptor/Arm64.h
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--mozglue/misc/interceptor/Arm64.h221
1 files changed, 221 insertions, 0 deletions
diff --git a/mozglue/misc/interceptor/Arm64.h b/mozglue/misc/interceptor/Arm64.h
new file mode 100644
index 0000000000..ebb30ecd6b
--- /dev/null
+++ b/mozglue/misc/interceptor/Arm64.h
@@ -0,0 +1,221 @@
+/* -*- 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 https://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_interceptor_Arm64_h
+#define mozilla_interceptor_Arm64_h
+
+#include <type_traits>
+
+#include "mozilla/Assertions.h"
+#include "mozilla/CheckedInt.h"
+#include "mozilla/MathAlgorithms.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/Result.h"
+#include "mozilla/Saturate.h"
+#include "mozilla/Types.h"
+
+namespace mozilla {
+namespace interceptor {
+namespace arm64 {
+
+// clang-format off
+enum class IntegerConditionCode : uint8_t {
+ // From the ARMv8 Architectural Reference Manual, Section C1.2.4
+ // Description Condition Flags
+ EQ = 0b0000, // == Z == 1
+ NE = 0b0001, // != Z == 0
+ CS = 0b0010, // carry set C == 1
+ HS = 0b0010, // carry set (alias) C == 1
+ CC = 0b0011, // carry clear C == 0
+ LO = 0b0011, // carry clear (alias) C == 0
+ MI = 0b0100, // < 0 N == 1
+ PL = 0b0101, // >= 0 N == 0
+ VS = 0b0110, // overflow V == 1
+ VC = 0b0111, // no overflow V == 0
+ HI = 0b1000, // unsigned > C == 1 && Z == 0
+ LS = 0b1001, // unsigned <= !(C == 1 && Z == 0)
+ GE = 0b1010, // signed >= N == V
+ LT = 0b1011, // signed < N != V
+ GT = 0b1100, // signed > Z == 0 && N == V
+ LE = 0b1101, // signed <= !(Z == 0 && N == V)
+ AL = 0b1110, // unconditional <Any>
+ NV = 0b1111 // unconditional (but AL is the preferred encoding)
+};
+// clang-format on
+
+struct LoadOrBranch {
+ enum class Type {
+ Load,
+ Branch,
+ };
+
+ // Load constructor
+ LoadOrBranch(const uintptr_t aAbsAddress, const uint8_t aDestReg)
+ : mType(Type::Load), mAbsAddress(aAbsAddress), mDestReg(aDestReg) {
+ MOZ_ASSERT(aDestReg < 32);
+ }
+
+ // Unconditional branch constructor
+ explicit LoadOrBranch(const uintptr_t aAbsAddress)
+ : mType(Type::Branch),
+ mAbsAddress(aAbsAddress),
+ mCond(IntegerConditionCode::AL) {}
+
+ // Conditional branch constructor
+ LoadOrBranch(const uintptr_t aAbsAddress, const IntegerConditionCode aCond)
+ : mType(Type::Branch), mAbsAddress(aAbsAddress), mCond(aCond) {}
+
+ Type mType;
+
+ // The absolute address to be loaded into a register, or branched to
+ uintptr_t mAbsAddress;
+
+ union {
+ // The destination register for the load
+ uint8_t mDestReg;
+
+ // The condition code for the branch
+ IntegerConditionCode mCond;
+ };
+};
+
+enum class PCRelCheckError {
+ InstructionNotPCRel,
+ NoDecoderAvailable,
+};
+
+MFBT_API Result<LoadOrBranch, PCRelCheckError> CheckForPCRel(
+ const uintptr_t aPC, const uint32_t aInst);
+
+/**
+ * Casts |aValue| to a |ResultT| via sign extension.
+ *
+ * This function should be used when extracting signed immediate values from
+ * an instruction.
+ *
+ * @param aValue The value to be sign extended. This value should already be
+ * isolated from the remainder of the instruction's bits and
+ * shifted all the way to the right.
+ * @param aNumValidBits The number of bits in |aValue| that contain the
+ * immediate signed value, including the sign bit.
+ */
+template <typename ResultT>
+inline ResultT SignExtend(const uint32_t aValue, const uint8_t aNumValidBits) {
+ static_assert(std::is_integral_v<ResultT> && std::is_signed_v<ResultT>,
+ "ResultT must be a signed integral type");
+ MOZ_ASSERT(aNumValidBits < 32U && aNumValidBits > 1);
+
+ using UnsignedResultT = std::decay_t<std::make_unsigned_t<ResultT>>;
+
+ const uint8_t kResultWidthBits = sizeof(ResultT) * 8;
+
+ // Shift left unsigned
+ const uint8_t shiftAmt = kResultWidthBits - aNumValidBits;
+ UnsignedResultT shiftedLeft = static_cast<UnsignedResultT>(aValue)
+ << shiftAmt;
+
+ // Now shift right signed
+ auto result = static_cast<ResultT>(shiftedLeft);
+ result >>= shiftAmt;
+
+ return result;
+}
+
+inline static uint32_t BuildUnconditionalBranchToRegister(const uint32_t aReg) {
+ MOZ_ASSERT(aReg < 32);
+ // BR aReg
+ return 0xD61F0000 | (aReg << 5);
+}
+
+MFBT_API LoadOrBranch BUncondImmDecode(const uintptr_t aPC,
+ const uint32_t aInst);
+
+/**
+ * If |aTarget| is more than 128MB away from |aPC|, we need to use a veneer.
+ */
+inline static bool IsVeneerRequired(const uintptr_t aPC,
+ const uintptr_t aTarget) {
+ detail::Saturate<intptr_t> saturated(aTarget);
+ saturated -= aPC;
+
+ uintptr_t absDiff = Abs(saturated.value());
+
+ return absDiff >= 0x08000000U;
+}
+
+inline static bool IsUnconditionalBranchImm(const uint32_t aInst) {
+ return (aInst & 0xFC000000U) == 0x14000000U;
+}
+
+inline static Maybe<uint32_t> BuildUnconditionalBranchImm(
+ const uintptr_t aPC, const uintptr_t aTarget) {
+ detail::Saturate<intptr_t> saturated(aTarget);
+ saturated -= aPC;
+
+ CheckedInt<int32_t> offset(saturated.value());
+ if (!offset.isValid()) {
+ return Nothing();
+ }
+
+ // offset should be a multiple of 4
+ MOZ_ASSERT(offset.value() % 4 == 0);
+ if (offset.value() % 4) {
+ return Nothing();
+ }
+
+ offset /= 4;
+ if (!offset.isValid()) {
+ return Nothing();
+ }
+
+ int32_t signbits = offset.value() & 0xFE000000;
+ // Ensure that offset is small enough to fit into the 26 bit region.
+ // We check that the sign bits are either all ones or all zeros.
+ MOZ_ASSERT(signbits == 0xFE000000 || !signbits);
+ if (signbits && signbits != 0xFE000000) {
+ return Nothing();
+ }
+
+ int32_t masked = offset.value() & 0x03FFFFFF;
+
+ // B imm26
+ return Some(0x14000000U | masked);
+}
+
+/**
+ * Allocate and construct a veneer that provides an absolute 64-bit branch to
+ * the hook function.
+ */
+template <typename TrampPoolT>
+inline static uintptr_t MakeVeneer(TrampPoolT& aTrampPool, void* aPrimaryTramp,
+ const uintptr_t aDestAddress) {
+ auto maybeVeneer = aTrampPool.GetNextTrampoline();
+ if (!maybeVeneer) {
+ return 0;
+ }
+
+ Trampoline<typename TrampPoolT::MMPolicyT> veneer(
+ std::move(maybeVeneer.ref()));
+
+ // Write the same header information that is used for trampolines
+ veneer.WriteEncodedPointer(nullptr);
+ veneer.WriteEncodedPointer(aPrimaryTramp);
+
+ veneer.StartExecutableCode();
+
+ // Register 16 is explicitly intended for veneers in ARM64, so we use that
+ // register without fear of clobbering anything important.
+ veneer.WriteLoadLiteral(aDestAddress, 16);
+ veneer.WriteInstruction(BuildUnconditionalBranchToRegister(16));
+
+ return reinterpret_cast<uintptr_t>(veneer.EndExecutableCode());
+}
+
+} // namespace arm64
+} // namespace interceptor
+} // namespace mozilla
+
+#endif // mozilla_interceptor_Arm64_h