зеркало из https://github.com/mozilla/gecko-dev.git
222 строки
6.8 KiB
C++
222 строки
6.8 KiB
C++
/* -*- 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
|