1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
6 
7 #include "Arm64.h"
8 
9 namespace mozilla {
10 namespace interceptor {
11 namespace arm64 {
12 
13 struct PCRelativeLoadTest {
14   // Bitmask to be ANDed with the instruction to isolate the bits that this
15   // instance is interested in
16   uint32_t mTestMask;
17   // The desired bits that we want to see after masking
18   uint32_t mMatchBits;
19   // If we match, mDecodeFn provide the code to decode the instruction.
20   LoadOrBranch (*mDecodeFn)(const uintptr_t aPC, const uint32_t aInst);
21 };
22 
ADRPDecode(const uintptr_t aPC,const uint32_t aInst)23 static LoadOrBranch ADRPDecode(const uintptr_t aPC, const uint32_t aInst) {
24   // Keep in mind that on Windows aarch64, uint32_t is little-endian
25   const uint32_t kMaskDataProcImmPcRelativeImmLo = 0x60000000;
26   const uint32_t kMaskDataProcImmPcRelativeImmHi = 0x00FFFFE0;
27 
28   uintptr_t base = aPC;
29   intptr_t offset = SignExtend<intptr_t>(
30       ((aInst & kMaskDataProcImmPcRelativeImmHi) >> 3) |
31           ((aInst & kMaskDataProcImmPcRelativeImmLo) >> 29),
32       21);
33 
34   base &= ~0xFFFULL;
35   offset <<= 12;
36 
37   uint8_t reg = aInst & 0x1F;
38 
39   return LoadOrBranch(base + offset, reg);
40 }
41 
BUncondImmDecode(const uintptr_t aPC,const uint32_t aInst)42 MFBT_API LoadOrBranch BUncondImmDecode(const uintptr_t aPC,
43                                        const uint32_t aInst) {
44   int32_t offset = SignExtend<int32_t>(aInst & 0x03FFFFFFU, 26);
45   return LoadOrBranch(aPC + offset);
46 }
47 
48 // Order is important here; more specific encoding tests must be placed before
49 // less specific encoding tests.
50 static const PCRelativeLoadTest gPCRelTests[] = {
51     {0x9FC00000, 0x10000000, nullptr},      // ADR
52     {0x9FC00000, 0x90000000, &ADRPDecode},  // ADRP
53     {0xFF000000, 0x58000000, nullptr},      // LDR (literal) 64-bit GPR
54     {0x3B000000, 0x18000000, nullptr},      // LDR (literal) (remaining forms)
55     {0x7C000000, 0x14000000, nullptr},      // B (unconditional immediate)
56     {0xFE000000, 0x54000000, nullptr},      // B.Cond
57     {0x7E000000, 0x34000000, nullptr},      // Compare and branch (imm)
58     {0x7E000000, 0x36000000, nullptr},      // Test and branch (imm)
59     {0xFE000000, 0xD6000000, nullptr}       // Unconditional branch (reg)
60 };
61 
62 /**
63  * In this function we interate through each entry in |gPCRelTests|, AND
64  * |aInst| with |test.mTestMask| to isolate the bits that we're interested in,
65  * then compare that result against |test.mMatchBits|. If we have a match,
66  * then that particular entry is applicable to |aInst|. If |test.mDecodeFn| is
67  * present, then we call it to decode the instruction. If it is not present,
68  * then we assume that this particular instruction is unsupported.
69  */
CheckForPCRel(const uintptr_t aPC,const uint32_t aInst)70 MFBT_API Result<LoadOrBranch, PCRelCheckError> CheckForPCRel(
71     const uintptr_t aPC, const uint32_t aInst) {
72   for (auto&& test : gPCRelTests) {
73     if ((aInst & test.mTestMask) == test.mMatchBits) {
74       if (!test.mDecodeFn) {
75         return Err(PCRelCheckError::NoDecoderAvailable);
76       }
77 
78       return test.mDecodeFn(aPC, aInst);
79     }
80   }
81 
82   return Err(PCRelCheckError::InstructionNotPCRel);
83 }
84 
85 }  // namespace arm64
86 }  // namespace interceptor
87 }  // namespace mozilla
88