aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arm_utils.cc134
-rw-r--r--arm_utils.h45
-rw-r--r--arm_utils_unittest.cc221
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.