diff options
-rw-r--r-- | arm_utils.cc | 134 | ||||
-rw-r--r-- | arm_utils.h | 45 | ||||
-rw-r--r-- | arm_utils_unittest.cc | 221 |
3 files changed, 351 insertions, 49 deletions
diff --git a/arm_utils.cc b/arm_utils.cc index 25630bd..1eb01c3 100644 --- a/arm_utils.cc +++ b/arm_utils.cc @@ -8,6 +8,14 @@ namespace zucchini { +namespace { + +inline bool IsMisaligned(rva_t rva, ArmAlign align) { + return (rva & (align - 1)) != 0; +} + +} // namespace + /******** Arm32Rel32Translator ********/ Arm32Rel32Translator::Arm32Rel32Translator() = default; @@ -76,11 +84,14 @@ bool Arm32Rel32Translator::EncodeA24(arm_disp_t disp, uint32_t* code32) { bool Arm32Rel32Translator::ReadA24(rva_t instr_rva, uint32_t code32, rva_t* target_rva) { + constexpr ArmAlign kInstrAlign = kArmAlign4; + if (IsMisaligned(instr_rva, kInstrAlign)) + return false; arm_disp_t disp; - ArmAlign align = DecodeA24(code32, &disp); - if (align == kArmAlignFail) + ArmAlign target_align = DecodeA24(code32, &disp); + if (target_align == kArmAlignFail) return false; - *target_rva = GetArmTargetRvaFromDisp(instr_rva, disp, align); + *target_rva = GetArmTargetRvaFromDisp(instr_rva, disp, target_align); return true; } @@ -88,12 +99,16 @@ bool Arm32Rel32Translator::ReadA24(rva_t instr_rva, bool Arm32Rel32Translator::WriteA24(rva_t instr_rva, rva_t target_rva, uint32_t* code32) { - // Dummy decode to get |align|. + constexpr ArmAlign kInstrAlign = kArmAlign4; + if (IsMisaligned(instr_rva, kInstrAlign)) + return false; + // Dummy decode to get |target_align|. arm_disp_t dummy_disp; - ArmAlign align = DecodeA24(*code32, &dummy_disp); - if (align == kArmAlignFail) + ArmAlign target_align = DecodeA24(*code32, &dummy_disp); + if (target_align == kArmAlignFail || IsMisaligned(target_rva, target_align)) return false; - arm_disp_t disp = GetArmDispFromTargetRva(instr_rva, target_rva, align); + arm_disp_t disp = + GetArmDispFromTargetRva(instr_rva, target_rva, target_align); return EncodeA24(disp, code32); } @@ -128,11 +143,14 @@ bool Arm32Rel32Translator::EncodeT8(arm_disp_t disp, uint16_t* code16) { bool Arm32Rel32Translator::ReadT8(rva_t instr_rva, uint16_t code16, rva_t* target_rva) { + constexpr ArmAlign kInstrAlign = kArmAlign2; + if (IsMisaligned(instr_rva, kInstrAlign)) + return false; arm_disp_t disp; - ArmAlign align = DecodeT8(code16, &disp); - if (align == kArmAlignFail) + ArmAlign target_align = DecodeT8(code16, &disp); + if (target_align == kArmAlignFail) return false; - *target_rva = GetThumb2TargetRvaFromDisp(instr_rva, disp, align); + *target_rva = GetThumb2TargetRvaFromDisp(instr_rva, disp, target_align); return true; } @@ -140,8 +158,14 @@ bool Arm32Rel32Translator::ReadT8(rva_t instr_rva, bool Arm32Rel32Translator::WriteT8(rva_t instr_rva, rva_t target_rva, uint16_t* code16) { + constexpr ArmAlign kInstrAlign = kArmAlign2; + constexpr ArmAlign kTargetAlign = kArmAlign2; + if (IsMisaligned(instr_rva, kInstrAlign) || + IsMisaligned(target_rva, kTargetAlign)) { + return false; + } arm_disp_t disp = - GetThumb2DispFromTargetRva(instr_rva, target_rva, kArmAlign2); + GetThumb2DispFromTargetRva(instr_rva, target_rva, kTargetAlign); return EncodeT8(disp, code16); } @@ -176,11 +200,14 @@ bool Arm32Rel32Translator::EncodeT11(arm_disp_t disp, uint16_t* code16) { bool Arm32Rel32Translator::ReadT11(rva_t instr_rva, uint16_t code16, rva_t* target_rva) { + constexpr ArmAlign kInstrAlign = kArmAlign2; + if (IsMisaligned(instr_rva, kInstrAlign)) + return false; arm_disp_t disp; - ArmAlign align = DecodeT11(code16, &disp); - if (align == kArmAlignFail) + ArmAlign target_align = DecodeT11(code16, &disp); + if (target_align == kArmAlignFail) return false; - *target_rva = GetThumb2TargetRvaFromDisp(instr_rva, disp, align); + *target_rva = GetThumb2TargetRvaFromDisp(instr_rva, disp, target_align); return true; } @@ -188,8 +215,14 @@ bool Arm32Rel32Translator::ReadT11(rva_t instr_rva, bool Arm32Rel32Translator::WriteT11(rva_t instr_rva, rva_t target_rva, uint16_t* code16) { + constexpr ArmAlign kInstrAlign = kArmAlign2; + constexpr ArmAlign kTargetAlign = kArmAlign2; + if (IsMisaligned(instr_rva, kInstrAlign) || + IsMisaligned(target_rva, kTargetAlign)) { + return false; + } arm_disp_t disp = - GetThumb2DispFromTargetRva(instr_rva, target_rva, kArmAlign2); + GetThumb2DispFromTargetRva(instr_rva, target_rva, kTargetAlign); return EncodeT11(disp, code16); } @@ -238,11 +271,14 @@ bool Arm32Rel32Translator::EncodeT20(arm_disp_t disp, uint32_t* code32) { bool Arm32Rel32Translator::ReadT20(rva_t instr_rva, uint32_t code32, rva_t* target_rva) { + constexpr ArmAlign kInstrAlign = kArmAlign2; + if (IsMisaligned(instr_rva, kInstrAlign)) + return false; arm_disp_t disp; - ArmAlign align = DecodeT20(code32, &disp); - if (align == kArmAlignFail) + ArmAlign target_align = DecodeT20(code32, &disp); + if (target_align == kArmAlignFail) return false; - *target_rva = GetThumb2TargetRvaFromDisp(instr_rva, disp, align); + *target_rva = GetThumb2TargetRvaFromDisp(instr_rva, disp, target_align); return true; } @@ -250,8 +286,14 @@ bool Arm32Rel32Translator::ReadT20(rva_t instr_rva, bool Arm32Rel32Translator::WriteT20(rva_t instr_rva, rva_t target_rva, uint32_t* code32) { + constexpr ArmAlign kInstrAlign = kArmAlign2; + constexpr ArmAlign kTargetAlign = kArmAlign2; + if (IsMisaligned(instr_rva, kInstrAlign) || + IsMisaligned(target_rva, kTargetAlign)) { + return false; + } arm_disp_t disp = - GetThumb2DispFromTargetRva(instr_rva, target_rva, kArmAlign2); + GetThumb2DispFromTargetRva(instr_rva, target_rva, kTargetAlign); return EncodeT20(disp, code32); } @@ -279,15 +321,15 @@ ArmAlign Arm32Rel32Translator::DecodeT24(uint32_t code32, arm_disp_t* disp) { t = SignExtend<24, int32_t>(t); // BLX encoding T2 requires final target to be 4-byte aligned by rounding // downward. This is applied to |t| *after* clipping. - ArmAlign align_by = kArmAlign2; + ArmAlign target_align = kArmAlign2; if (bits == 0xF000C000) { uint32_t H = GetBit<0>(code32); if (H) return kArmAlignFail; // Illegal instruction: H must be 0. - align_by = kArmAlign4; + target_align = kArmAlign4; } *disp = static_cast<int32_t>(t); - return align_by; + return target_align; } return kArmAlignFail; } @@ -326,11 +368,14 @@ bool Arm32Rel32Translator::EncodeT24(arm_disp_t disp, uint32_t* code32) { bool Arm32Rel32Translator::ReadT24(rva_t instr_rva, uint32_t code32, rva_t* target_rva) { + constexpr ArmAlign kInstrAlign = kArmAlign2; + if (IsMisaligned(instr_rva, kInstrAlign)) + return false; arm_disp_t disp; - ArmAlign align = DecodeT24(code32, &disp); - if (align == kArmAlignFail) + ArmAlign target_align = DecodeT24(code32, &disp); + if (target_align == kArmAlignFail) return false; - *target_rva = GetThumb2TargetRvaFromDisp(instr_rva, disp, align); + *target_rva = GetThumb2TargetRvaFromDisp(instr_rva, disp, target_align); return true; } @@ -338,12 +383,16 @@ bool Arm32Rel32Translator::ReadT24(rva_t instr_rva, bool Arm32Rel32Translator::WriteT24(rva_t instr_rva, rva_t target_rva, uint32_t* code32) { - // Dummy decode to get |align|. + constexpr ArmAlign kInstrAlign = kArmAlign2; + if (IsMisaligned(instr_rva, kInstrAlign)) + return false; + // Dummy decode to get |target_align|. arm_disp_t dummy_disp; - ArmAlign align = DecodeT24(*code32, &dummy_disp); - if (align == kArmAlignFail) + ArmAlign target_align = DecodeT24(*code32, &dummy_disp); + if (target_align == kArmAlignFail || IsMisaligned(target_rva, target_align)) return false; - arm_disp_t disp = GetThumb2DispFromTargetRva(instr_rva, target_rva, align); + arm_disp_t disp = + GetThumb2DispFromTargetRva(instr_rva, target_rva, target_align); return EncodeT24(disp, code32); } @@ -390,6 +439,9 @@ bool AArch64Rel32Translator::EncodeImmd14(arm_disp_t disp, uint32_t* code32) { bool AArch64Rel32Translator::ReadImmd14(rva_t instr_rva, uint32_t code32, rva_t* target_rva) { + constexpr ArmAlign kInstrAlign = kArmAlign4; + if (IsMisaligned(instr_rva, kInstrAlign)) + return false; arm_disp_t disp; if (DecodeImmd14(code32, &disp) == kArmAlignFail) return false; @@ -401,6 +453,12 @@ bool AArch64Rel32Translator::ReadImmd14(rva_t instr_rva, bool AArch64Rel32Translator::WriteImmd14(rva_t instr_rva, rva_t target_rva, uint32_t* code32) { + constexpr ArmAlign kInstrAlign = kArmAlign4; + constexpr ArmAlign kTargetAlign = kArmAlign4; + if (IsMisaligned(instr_rva, kInstrAlign) || + IsMisaligned(target_rva, kTargetAlign)) { + return false; + } arm_disp_t disp = GetDispFromTargetRva(instr_rva, target_rva); return EncodeImmd14(disp, code32); } @@ -449,6 +507,9 @@ bool AArch64Rel32Translator::EncodeImmd19(arm_disp_t disp, uint32_t* code32) { bool AArch64Rel32Translator::ReadImmd19(rva_t instr_rva, uint32_t code32, rva_t* target_rva) { + constexpr ArmAlign kInstrAlign = kArmAlign4; + if (IsMisaligned(instr_rva, kInstrAlign)) + return false; arm_disp_t disp; if (DecodeImmd19(code32, &disp) == kArmAlignFail) return false; @@ -460,6 +521,12 @@ bool AArch64Rel32Translator::ReadImmd19(rva_t instr_rva, bool AArch64Rel32Translator::WriteImmd19(rva_t instr_rva, rva_t target_rva, uint32_t* code32) { + constexpr ArmAlign kInstrAlign = kArmAlign4; + constexpr ArmAlign kTargetAlign = kArmAlign4; + if (IsMisaligned(instr_rva, kInstrAlign) || + IsMisaligned(target_rva, kTargetAlign)) { + return false; + } arm_disp_t disp = GetDispFromTargetRva(instr_rva, target_rva); return EncodeImmd19(disp, code32); } @@ -503,6 +570,9 @@ bool AArch64Rel32Translator::EncodeImmd26(arm_disp_t disp, uint32_t* code32) { bool AArch64Rel32Translator::ReadImmd26(rva_t instr_rva, uint32_t code32, rva_t* target_rva) { + constexpr ArmAlign kInstrAlign = kArmAlign4; + if (IsMisaligned(instr_rva, kInstrAlign)) + return false; arm_disp_t disp; if (DecodeImmd26(code32, &disp) == kArmAlignFail) return false; @@ -514,6 +584,12 @@ bool AArch64Rel32Translator::ReadImmd26(rva_t instr_rva, bool AArch64Rel32Translator::WriteImmd26(rva_t instr_rva, rva_t target_rva, uint32_t* code32) { + constexpr ArmAlign kInstrAlign = kArmAlign4; + constexpr ArmAlign kTargetAlign = kArmAlign4; + if (IsMisaligned(instr_rva, kInstrAlign) || + IsMisaligned(target_rva, kTargetAlign)) { + return false; + } arm_disp_t disp = GetDispFromTargetRva(instr_rva, target_rva); return EncodeImmd26(disp, code32); } diff --git a/arm_utils.h b/arm_utils.h index 7d95400..eb9b343 100644 --- a/arm_utils.h +++ b/arm_utils.h @@ -21,7 +21,7 @@ namespace zucchini { // * AArch64 (64-bit ARM): // https://static.docs.arm.com/ddi0487/da/DDI0487D_a_armv8_arm.pdf -// Definitions (non-official, but used in this file): +// Definitions (used in Zucchini): // * |instr_rva|: Instruction RVA: The RVA where an instruction is located. In // ARM mode and for AArch64 this is 4-byte aligned; in THUMB2 mode this is // 2-byte aligned. @@ -51,7 +51,7 @@ namespace zucchini { // Example 2 (THUMB2 mode): // 001030A2: 00 F0 01 FA BL 001034A8 // |instr_rva| = 0x001030A2 (2-byte aligned). -// |code| = 0xF000FA01 (special THUMB2 mode data fetch method). +// |code| = 0xF000FA01 (special THUMB2 mode data fetch). // |disp| = 0x00000402 (decoded from |code| with T24 -> BL encoding T1). // PC = |instr_rva| + 4 = 0x001030A6 (THUMB2 mode). // |target_rva| = PC + |disp| = 0x001034A8. @@ -64,20 +64,26 @@ namespace zucchini { // PC = |instr_rva| = 0x00305070 (AArch64). // |target_rva| = PC + |disp| = 0x0034587C. -// BLX complication: BLX encoding T2 transits mode from THUMB2 to ARM. Therefore -// |target_rva| must be 4-byte aligned; it's not just PC + |disp|. In THUMB2 -// encoding, |disp| is required to be multiple of 4 (so H = 0, where H is the -// bit corresponding to 2). |target_rva| becomes PC + |disp| rounded down to the -// nearest 4-byte aligned address. We have two alternatives to handle this -// complexity (this affects |disp|'s definition): -// (1) Handle in |code| <-> |disp|: Let |disp| be |target_rva| - PC. For -// BLX encoding T2, we'd examine |instr_rva| % 4 and adjust accordingly. -// (2) Handle in |disp| <-> |target_rva|: Let |disp| be the value stored in -// |code|. Computation involving |target_rva| would require |instr_rva| and -// prior |code| extraction, from which we deduce expected target alignment -// (4 for ARM mode; 2 for THUMB2 mode except for 4 for BLX encoding T2) and -// adjust accordingly. -// We adopt (2) since |code| <-> |disp| is useful, and should be made simple. +// BLX complication: BLX transits between ARM mode and THUMB2 mode, and branches +// to an address. Therefore |instr_rva| must align by the "old" mode, and +// |target_rva| must align by the "new" mode. In particular: +// * BLX encoding A2 (ARM -> THUMB2): |instr_rva| is 4-byte aligned with +// PC = |instr_rva| + 8; |target_rva| is 2-byte aligned, and so |disp| is +// 2-byte aligned. +// * BLX encoding T2 (THUMB2 -> ARM): |instr_rva| is 2-byte aligned with +// PC = |instr_rva| + 4; |target_rva| is 4-byte aligned. Complication: BLX +// encoding T2 stores a bit |H| that corresponds to "2" in binary, but |H| +// must be set to 0. Thus the encoded value is effectively 4-byte aligned. So +// when computing |target_rva| by adding PC (2-byte aligned) to the stored +// value (4-byte aligned), the result must be rounded down to the nearest +// 4-byte aligned address. +// The last situation creates ambiguity in how |disp| is defined! Alternatives: +// (1) |disp| := |target_rva| - PC: So |code| <-> |disp| for BLX encoding T2, +// requires |instr_rva| % 4 to be determined, and adjustments made. +// (2) |disp| := Value stored in |code|: So |disp| <-> |target_rva| for BLX +// encoding T2 requires adjustment: |disp| -> |target_rva| needs to round +// down, whereas |target_rva| -> |disp| needs to round up. +// We adopt (2) to simplify |code| <-> |disp|, since that gets used. using arm_disp_t = int32_t; @@ -129,6 +135,13 @@ class Arm32Rel32Translator { // Rel32 address types enumeration. enum AddrType : uint8_t { ADDR_NONE = 0xFF, + // Naming: Here "A24" represents ARM mode instructions where |code| + // dedicates 24 bits (including sign bit) to specify |disp|. Similarly, "T8" + // represents THUMB2 mode instructions with 8 bits for |disp|. Currently + // only {A24, T8, T11, T20, T24} are defined. These are not to be confused + // with "B encoding A1", "B encoding T3", etc., which are specific encoding + // schemes given by the manual for the "B" (or other) instructions (only + // {A1, A2, T1, T2, T3, T4} are seen). ADDR_A24 = 0, ADDR_T8, ADDR_T11, diff --git a/arm_utils_unittest.cc b/arm_utils_unittest.cc index 1b328ee..2b669d0 100644 --- a/arm_utils_unittest.cc +++ b/arm_utils_unittest.cc @@ -16,6 +16,7 @@ #include <vector> #include "base/logging.h" +#include "components/zucchini/address_translator.h" #include "testing/gtest/include/gtest/gtest.h" namespace zucchini { @@ -30,7 +31,7 @@ uint32_t kCleanSlateBLX_A2 = 0xFA000000; // A24. uint16_t kCleanSlateB_T1 = 0xD000; // T8. uint16_t kCleanSlateB_T2 = 0xE000; // T11. uint32_t kCleanSlateB_T3 = 0xF0008000; // T20. -// For T4 encodings, |disp| = 0 means J1 = J2 = 1, so include 0x00002800. +// For T24 encodings, |disp| = 0 means J1 = J2 = 1, so include 0x00002800. uint32_t kCleanSlateB_T4 = 0xF0009000 | 0x00002800; // T24. uint32_t kCleanSlateBL_T1 = 0xF000D000 | 0x00002800; // T24. uint32_t kCleanSlateBLX_T2 = 0xF000C000 | 0x00002800; // T24. @@ -134,7 +135,7 @@ struct ArmRelInstruction { INT_T clean_slate_code; }; -// Tester for Decode/Encode functions for ARM. +// Tester for ARM Encode / Decode functions for |disp| <-> |code|. template <typename TRAITS> class ArmTranslatorEncodeDecodeTest { public: @@ -204,6 +205,60 @@ class ArmTranslatorEncodeDecodeTest { } }; +// Tester for ARM Write / Read functions for |target_rva| <-> |code|. +template <typename TRAITS> +class ArmTranslatorWriteReadTest { + public: + using CODE_T = typename TRAITS::code_t; + + ArmTranslatorWriteReadTest() {} + + // Expects successful Write() to |clean_slate_code| for each |target_rva_list| + // RVA, using each |instr_rva_list| RVA, and that the resulting |code| leads + // to successful Read(), which recovers |instr_rva|. + void Accept(CODE_T clean_slate_code, + const std::vector<rva_t>& instr_rva_list, + const std::vector<rva_t>& target_rva_list) { + bool (*read_fun)(rva_t, CODE_T, rva_t*) = TRAITS::Read; + bool (*write_fun)(rva_t, rva_t, CODE_T*) = TRAITS::Write; + + for (rva_t instr_rva : instr_rva_list) { + for (rva_t target_rva : target_rva_list) { + CODE_T code = clean_slate_code; + // Write |target_rva| to |code|. + EXPECT_TRUE((*write_fun)(instr_rva, target_rva, &code)) << target_rva; + rva_t target_rva_out = kInvalidRva; + + // Read |code| to |target_rva_out|, check fidelity. + EXPECT_TRUE((*read_fun)(instr_rva, code, &target_rva_out)); + EXPECT_EQ(target_rva, target_rva_out); + + // Sanity check: Rewrite |target_rva| into |code|, ensure no change. + CODE_T code_copy = code; + EXPECT_TRUE((*write_fun)(instr_rva, target_rva, &code)); + EXPECT_EQ(code_copy, code); + } + } + } + + // Expects failed Write() to |clean_slate_code| for each |target_rva_list| + // RVA, using each |instr_rva_list| RVA. + void Reject(CODE_T clean_slate_code, + const std::vector<rva_t>& instr_rva_list, + const std::vector<rva_t>& target_rva_list) { + bool (*write_fun)(rva_t, rva_t, CODE_T*) = TRAITS::Write; + + for (rva_t instr_rva : instr_rva_list) { + for (rva_t target_rva : target_rva_list) { + CODE_T code = clean_slate_code; + EXPECT_FALSE((*write_fun)(instr_rva, target_rva, &code)) << target_rva; + // Output variable is unmodified after failure. + EXPECT_EQ(clean_slate_code, code); + } + } + } +}; + } // namespace // Test for test helper. @@ -377,7 +432,108 @@ TEST(Arm32Rel32Translator, EncodeDecode) { } TEST(Arm32Rel32Translator, WriteRead) { - // TODO(huangs): Implement. + std::vector<rva_t> aligned4; + std::vector<rva_t> misaligned4; + std::vector<rva_t> aligned2; + std::vector<rva_t> misaligned2; + for (rva_t rva = 0x1FFC; rva <= 0x2010; ++rva) { + ((rva % 4 == 0) ? aligned4 : misaligned4).push_back(rva); + ((rva % 2 == 0) ? aligned2 : misaligned2).push_back(rva); + } + CHECK_EQ(6U, aligned4.size()); + CHECK_EQ(15U, misaligned4.size()); + CHECK_EQ(11U, aligned2.size()); + CHECK_EQ(10U, misaligned2.size()); + + // Helpers to convert an instruction's RVA to PC. + auto pcArm = [](rva_t instr_rva) -> rva_t { return instr_rva + 8; }; + auto pcThumb2 = [](rva_t instr_rva) -> rva_t { return instr_rva + 4; }; + + // A24 tests. + ArmTranslatorWriteReadTest<Arm32Rel32Translator::AddrTraits_A24> test_A24; + for (uint32_t clean_slate_code : {kCleanSlateB_A1, kCleanSlateBL_A1}) { + test_A24.Accept(clean_slate_code, aligned4, aligned4); + test_A24.Reject(clean_slate_code, aligned4, misaligned4); + test_A24.Reject(clean_slate_code, misaligned4, aligned4); + test_A24.Reject(clean_slate_code, misaligned4, misaligned4); + // Signed (24 + 2)-bit range, 4-byte aligned: [-0x02000000, 0x01FFFFFC]. + test_A24.Accept(clean_slate_code, {0x15000000}, + {pcArm(0x13000000), pcArm(0x16FFFFFC)}); + test_A24.Reject(clean_slate_code, {0x15000000}, + {pcArm(0x13000000 - 4), pcArm(0x16FFFFFC + 4)}); + } + + // BLX complication: ARM -> THUMB2. + test_A24.Accept(kCleanSlateBLX_A2, aligned4, aligned2); + test_A24.Reject(kCleanSlateBLX_A2, aligned4, misaligned2); + test_A24.Reject(kCleanSlateBLX_A2, misaligned4, aligned2); + test_A24.Reject(kCleanSlateBLX_A2, misaligned4, misaligned2); + test_A24.Accept(kCleanSlateBLX_A2, {0x15000000}, + {pcArm(0x13000000), pcArm(0x16FFFFFE)}); + test_A24.Reject(kCleanSlateBLX_A2, {0x15000000}, + {pcArm(0x13000000 - 4), pcArm(0x13000000 - 2), + pcArm(0x16FFFFFE + 2), pcArm(0x16FFFFFE + 4)}); + + // T8 tests. + ArmTranslatorWriteReadTest<Arm32Rel32Translator::AddrTraits_T8> test_T8; + test_T8.Accept(kCleanSlateB_T1, aligned2, aligned2); + test_T8.Reject(kCleanSlateB_T1, aligned2, misaligned2); + test_T8.Reject(kCleanSlateB_T1, misaligned2, aligned2); + test_T8.Reject(kCleanSlateB_T1, misaligned2, misaligned2); + // Signed (8 + 1)-bit range, 2-byte aligned: [-0x0100, 0x00FE]. + test_T8.Accept(kCleanSlateB_T1, {0x10000500}, + {pcThumb2(0x10000400), pcThumb2(0x100005FE)}); + test_T8.Reject(kCleanSlateB_T1, {0x10000500}, + {pcThumb2(0x10000400 - 2), pcThumb2(0x100005FE + 2)}); + + // T11 tests. + ArmTranslatorWriteReadTest<Arm32Rel32Translator::AddrTraits_T11> test_T11; + test_T11.Accept(kCleanSlateB_T2, aligned2, aligned2); + test_T11.Reject(kCleanSlateB_T2, aligned2, misaligned2); + test_T11.Reject(kCleanSlateB_T2, misaligned2, aligned2); + test_T11.Reject(kCleanSlateB_T2, misaligned2, misaligned2); + // Signed (11 + 1)-bit range, 2-byte aligned: [-0x0800, 0x07FE]. + test_T11.Accept(kCleanSlateB_T2, {0x10003000}, + {pcThumb2(0x10002800), pcThumb2(0x100037FE)}); + test_T11.Reject(kCleanSlateB_T2, {0x10003000}, + {pcThumb2(0x10002800 - 2), pcThumb2(0x100037FE + 2)}); + + // T20 tests. + ArmTranslatorWriteReadTest<Arm32Rel32Translator::AddrTraits_T20> test_T20; + test_T20.Accept(kCleanSlateB_T3, aligned2, aligned2); + test_T20.Reject(kCleanSlateB_T3, aligned2, misaligned2); + test_T20.Reject(kCleanSlateB_T3, misaligned2, aligned2); + test_T20.Reject(kCleanSlateB_T3, misaligned2, misaligned2); + // Signed (20 + 1)-bit range, 2-byte aligned: [-0x00100000, 0x000FFFFE]. + test_T20.Accept(kCleanSlateB_T3, {0x10300000}, + {pcThumb2(0x10200000), pcThumb2(0x103FFFFE)}); + test_T20.Reject(kCleanSlateB_T3, {0x10300000}, + {pcThumb2(0x10200000 - 2), pcThumb2(0x103FFFFE + 2)}); + + // T24 tests. + ArmTranslatorWriteReadTest<Arm32Rel32Translator::AddrTraits_T24> test_T24; + for (uint32_t clean_slate_code : {kCleanSlateB_T4, kCleanSlateBL_T1}) { + test_T24.Accept(clean_slate_code, aligned2, aligned2); + test_T24.Reject(clean_slate_code, aligned2, misaligned2); + test_T24.Reject(clean_slate_code, misaligned2, aligned2); + test_T24.Reject(clean_slate_code, misaligned2, misaligned2); + // Signed (24 + 1)-bit range, 2-byte aligned: [-0x01000000, 0x00FFFFFE]. + test_T24.Accept(clean_slate_code, {0x16000000}, + {pcThumb2(0x15000000), pcThumb2(0x16FFFFFE)}); + test_T24.Reject(clean_slate_code, {0x16000000}, + {pcThumb2(0x15000000 - 2), pcThumb2(0x16FFFFFE + 2)}); + } + + // BLX complication: THUMB2 -> ARM. + test_T24.Accept(kCleanSlateBLX_T2, aligned2, aligned4); + test_T24.Reject(kCleanSlateBLX_T2, aligned2, misaligned4); + test_T24.Reject(kCleanSlateBLX_T2, misaligned2, aligned4); + test_T24.Reject(kCleanSlateBLX_T2, misaligned2, misaligned4); + test_T24.Accept(kCleanSlateBLX_T2, {0x16000000}, + {pcThumb2(0x15000000), pcThumb2(0x16FFFFFC)}); + test_T24.Reject(kCleanSlateBLX_T2, {0x16000000}, + {pcThumb2(0x15000000 - 4), pcThumb2(0x15000000 - 2), + pcThumb2(0x16FFFFFC + 2), pcThumb2(0x16FFFFFC + 4)}); } // Typical usage in |target_rva| extraction. @@ -609,7 +765,64 @@ TEST(AArch64Rel32Translator, EncodeDecode) { } TEST(AArch64Rel32Translator, WriteRead) { - // TODO(huangs): Implement. + std::vector<rva_t> aligned4; + std::vector<rva_t> misaligned4; + for (rva_t rva = 0x1FFC; rva <= 0x2010; ++rva) { + ((rva % 4 == 0) ? aligned4 : misaligned4).push_back(rva); + } + CHECK_EQ(6U, aligned4.size()); + CHECK_EQ(15U, misaligned4.size()); + + // Helper to convert an instruction's RVA to PC. + auto pcAArch64 = [](rva_t instr_rva) -> rva_t { return instr_rva; }; + + // Immd14 tests. + ArmTranslatorWriteReadTest<AArch64Rel32Translator::AddrTraits_Immd14> + test_immd14; + for (uint32_t clean_slate_code : {kCleanSlate64TBZw, kCleanSlate64TBZz, + kCleanSlate64TBNZw, kCleanSlate64TBNZz}) { + test_immd14.Accept(clean_slate_code, aligned4, aligned4); + test_immd14.Reject(clean_slate_code, aligned4, misaligned4); + test_immd14.Reject(clean_slate_code, misaligned4, aligned4); + test_immd14.Reject(clean_slate_code, misaligned4, misaligned4); + // Signed (14 + 2)-bit range, 4-byte aligned: [-0x00008000, 0x00007FFC]. + test_immd14.Accept(clean_slate_code, {0x10040000}, + {pcAArch64(0x10038000), pcAArch64(0x10047FFC)}); + test_immd14.Reject(clean_slate_code, {0x15000000}, + {pcAArch64(0x10038000 - 4), pcAArch64(0x10047FFC + 4)}); + } + + // Immd19 tests. + ArmTranslatorWriteReadTest<AArch64Rel32Translator::AddrTraits_Immd19> + test_immd19; + for (uint32_t clean_slate_code : + {kCleanSlate64Bcond, kCleanSlate64CBZw, kCleanSlate64CBZz, + kCleanSlate64CBNZw, kCleanSlate64CBNZz}) { + test_immd19.Accept(clean_slate_code, aligned4, aligned4); + test_immd19.Reject(clean_slate_code, aligned4, misaligned4); + test_immd19.Reject(clean_slate_code, misaligned4, aligned4); + test_immd19.Reject(clean_slate_code, misaligned4, misaligned4); + // Signed (19 + 2)-bit range, 4-byte aligned: [-0x00100000, 0x000FFFFC]. + test_immd19.Accept(clean_slate_code, {0x10300000}, + {pcAArch64(0x10200000), pcAArch64(0x103FFFFC)}); + test_immd19.Reject(clean_slate_code, {0x10300000}, + {pcAArch64(0x10200000 - 4), pcAArch64(0x103FFFFC + 4)}); + } + + // Immd26 tests. + ArmTranslatorWriteReadTest<AArch64Rel32Translator::AddrTraits_Immd26> + test_immd26; + for (uint32_t clean_slate_code : {kCleanSlate64B, kCleanSlate64BL}) { + test_immd26.Accept(clean_slate_code, aligned4, aligned4); + test_immd26.Reject(clean_slate_code, aligned4, misaligned4); + test_immd26.Reject(clean_slate_code, misaligned4, aligned4); + test_immd26.Reject(clean_slate_code, misaligned4, misaligned4); + // Signed (26 + 2)-bit range, 4-byte aligned: [-0x08000000, 0x07FFFFFC]. + test_immd26.Accept(clean_slate_code, {0x30000000}, + {pcAArch64(0x28000000), pcAArch64(0x37FFFFFC)}); + test_immd26.Reject(clean_slate_code, {0x30000000}, + {pcAArch64(0x28000000 - 4), pcAArch64(0x37FFFFFC + 4)}); + } } // Typical usage in |target_rva| extraction. |