aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--BUILD.gn3
-rw-r--r--arm_utils.cc521
-rw-r--r--arm_utils.h407
-rw-r--r--arm_utils_unittest.cc645
-rw-r--r--reference_bytes_mixer.cc11
-rw-r--r--reference_bytes_mixer.h8
-rw-r--r--zucchini_gen.cc4
7 files changed, 1587 insertions, 12 deletions
diff --git a/BUILD.gn b/BUILD.gn
index b15da9c..00bacd8 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -31,6 +31,8 @@ static_library("zucchini_lib") {
"address_translator.cc",
"address_translator.h",
"algorithm.h",
+ "arm_utils.cc",
+ "arm_utils.h",
"binary_data_histogram.cc",
"binary_data_histogram.h",
"buffer_sink.cc",
@@ -156,6 +158,7 @@ test("zucchini_unittests") {
"abs32_utils_unittest.cc",
"address_translator_unittest.cc",
"algorithm_unittest.cc",
+ "arm_utils_unittest.cc",
"binary_data_histogram_unittest.cc",
"buffer_sink_unittest.cc",
"buffer_source_unittest.cc",
diff --git a/arm_utils.cc b/arm_utils.cc
new file mode 100644
index 0000000..4665281
--- /dev/null
+++ b/arm_utils.cc
@@ -0,0 +1,521 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/zucchini/arm_utils.h"
+
+#include "components/zucchini/algorithm.h"
+
+namespace zucchini {
+
+/******** Arm32Rel32Translator ********/
+
+Arm32Rel32Translator::Arm32Rel32Translator() = default;
+
+// The mapping between ARM instruction "Code" to "Displacement" involves complex
+// bit manipulation. The comments below annotate bits mappings using a string.
+// * Bits are listed from highest-order to lowerst-order (like in the manual).
+// * '0' and '1' denote literals.
+// * Uppercase letters denote a single bit in "Code". For example, 'S' denotes
+// a sign bit that gets extended in "Displacement". To follow naming in the
+// manual, these may enumerated, and written as "(I1)", "(I2)", etc.
+// * Lowercase letters denote bit fields with orders preserved.
+
+// static
+ArmAlign Arm32Rel32Translator::DecodeA24(uint32_t code32, arm_disp_t* disp) {
+ // Handle multiple instructions. Let cccc != 1111:
+ // B encoding A1:
+ // Code: cccc1010 Siiiiiii iiiiiiii iiiiiiii
+ // Displacement: SSSSSSSi iiiiiiii iiiiiiii iiiiii00
+ // BL encoding A1:
+ // Code: cccc1011 Siiiiiii iiiiiiii iiiiiiii
+ // Displacement: SSSSSSSi iiiiiiii iiiiiiii iiiiii00
+ // BLX encoding A2:
+ // Code: 1111101H Siiiiiii iiiiiiii iiiiiiii
+ // Displacement: SSSSSSSi iiiiiiii iiiiiiii iiiiiiH0
+ uint8_t bits = GetUnsignedBits<24, 27>(code32);
+ if (bits == 0xA || bits == 0xB) { // B, BL, or BLX.
+ *disp = GetSignedBits<0, 23>(code32) << 2;
+ uint8_t cond = GetUnsignedBits<28, 31>(code32);
+ if (cond == 0xF) { // BLX.
+ uint32_t H = GetBit<24>(code32);
+ *disp |= H << 1;
+ return kArmAlign2;
+ }
+ return kArmAlign4;
+ }
+ return kArmAlignFail;
+}
+
+// static
+bool Arm32Rel32Translator::EncodeA24(arm_disp_t disp, uint32_t* code32) {
+ uint32_t t = *code32;
+ uint8_t bits = GetUnsignedBits<24, 27>(t);
+ if (bits == 0xA || bits == 0xB) {
+ // B, BL, or BLX.
+ if (!SignedFit<26>(disp)) // Detect overflow.
+ return false;
+ uint8_t cond = GetUnsignedBits<28, 31>(t);
+ if (cond == 0xF) {
+ if (disp % 2) // BLX (encoding A2) requires 2-byte alignment.
+ return false;
+ uint32_t H = GetBit<1>(disp);
+ t = (t & 0xFEFFFFFF) | (H << 24);
+ } else {
+ if (disp % 4) // B and BL require 4-byte alignment.
+ return false;
+ }
+ t = (t & 0xFF000000) | ((disp >> 2) & 0x00FFFFFF);
+ *code32 = t;
+ return true;
+ }
+ return false;
+}
+
+// static
+bool Arm32Rel32Translator::ReadA24(rva_t instr_rva,
+ uint32_t code32,
+ rva_t* target_rva) {
+ arm_disp_t disp;
+ ArmAlign align = DecodeA24(code32, &disp);
+ if (align == kArmAlignFail)
+ return false;
+ *target_rva = GetArmTargetRvaFromDisp(instr_rva, disp, align);
+ return true;
+}
+
+// static
+bool Arm32Rel32Translator::WriteA24(rva_t instr_rva,
+ rva_t target_rva,
+ uint32_t* code32) {
+ // Dummy decode to get |align|.
+ arm_disp_t dummy_disp;
+ ArmAlign align = DecodeA24(*code32, &dummy_disp);
+ if (align == kArmAlignFail)
+ return false;
+ arm_disp_t disp = GetArmDispFromTargetRva(instr_rva, target_rva, align);
+ return EncodeA24(disp, code32);
+}
+
+// static
+ArmAlign Arm32Rel32Translator::DecodeT8(uint16_t code16, arm_disp_t* disp) {
+ if ((code16 & 0xF000) == 0xD000 && (code16 & 0x0F00) != 0x0F00) {
+ // B encoding T1:
+ // Code: 1101cccc Siiiiiii
+ // Displacement: SSSSSSSS SSSSSSSS SSSSSSSS iiiiiii0
+ *disp = GetSignedBits<0, 7>(code16) << 1;
+ return kArmAlign2;
+ }
+ return kArmAlignFail;
+}
+
+// static
+bool Arm32Rel32Translator::EncodeT8(arm_disp_t disp, uint16_t* code16) {
+ uint16_t t = *code16;
+ if ((t & 0xF000) == 0xD000 && (t & 0x0F00) != 0x0F00) {
+ if (disp % 2) // Require 2-byte alignment.
+ return false;
+ if (!SignedFit<9>(disp)) // Detect overflow.
+ return false;
+ t = (t & 0xFF00) | ((disp >> 1) & 0x00FF);
+ *code16 = t;
+ return true;
+ }
+ return false;
+}
+
+// static
+bool Arm32Rel32Translator::ReadT8(rva_t instr_rva,
+ uint16_t code16,
+ rva_t* target_rva) {
+ arm_disp_t disp;
+ ArmAlign align = DecodeT8(code16, &disp);
+ if (align == kArmAlignFail)
+ return false;
+ *target_rva = GetThumb2TargetRvaFromDisp(instr_rva, disp, align);
+ return true;
+}
+
+// static
+bool Arm32Rel32Translator::WriteT8(rva_t instr_rva,
+ rva_t target_rva,
+ uint16_t* code16) {
+ arm_disp_t disp =
+ GetThumb2DispFromTargetRva(instr_rva, target_rva, kArmAlign2);
+ return EncodeT8(disp, code16);
+}
+
+// static
+ArmAlign Arm32Rel32Translator::DecodeT11(uint16_t code16, arm_disp_t* disp) {
+ if ((code16 & 0xF800) == 0xE000) {
+ // B encoding T2:
+ // Code: 11100Sii iiiiiiii
+ // Displacement: SSSSSSSS SSSSSSSS SSSSSiii iiiiiii0
+ *disp = GetSignedBits<0, 10>(code16) << 1;
+ return kArmAlign2;
+ }
+ return kArmAlignFail;
+}
+
+// static
+bool Arm32Rel32Translator::EncodeT11(arm_disp_t disp, uint16_t* code16) {
+ uint16_t t = *code16;
+ if ((t & 0xF800) == 0xE000) {
+ if (disp % 2) // Require 2-byte alignment.
+ return false;
+ if (!SignedFit<12>(disp)) // Detect overflow.
+ return false;
+ t = (t & 0xF800) | ((disp >> 1) & 0x07FF);
+ *code16 = t;
+ return true;
+ }
+ return false;
+}
+
+// static
+bool Arm32Rel32Translator::ReadT11(rva_t instr_rva,
+ uint16_t code16,
+ rva_t* target_rva) {
+ arm_disp_t disp;
+ ArmAlign align = DecodeT11(code16, &disp);
+ if (align == kArmAlignFail)
+ return false;
+ *target_rva = GetThumb2TargetRvaFromDisp(instr_rva, disp, align);
+ return true;
+}
+
+// static
+bool Arm32Rel32Translator::WriteT11(rva_t instr_rva,
+ rva_t target_rva,
+ uint16_t* code16) {
+ arm_disp_t disp =
+ GetThumb2DispFromTargetRva(instr_rva, target_rva, kArmAlign2);
+ return EncodeT11(disp, code16);
+}
+
+// static
+ArmAlign Arm32Rel32Translator::DecodeT21(uint32_t code32, arm_disp_t* disp) {
+ if ((code32 & 0xF800D000) == 0xF0008000 &&
+ (code32 & 0x03C00000) != 0x03C00000) {
+ // B encoding T3. Note the reversal of "(J1)" and "(J2)".
+ // Code: 11110Scc cciiiiii 10(J1)0(J2)jjj jjjjjjjj
+ // Displacement: SSSSSSSS SSSS(J2)(J1)ii iiiijjjj jjjjjjj0
+ uint32_t imm11 = GetUnsignedBits<0, 10>(code32); // jj...j.
+ uint32_t J2 = GetBit<11>(code32);
+ uint32_t J1 = GetBit<13>(code32);
+ uint32_t imm6 = GetUnsignedBits<16, 21>(code32); // ii...i.
+ uint32_t S = GetBit<26>(code32);
+ uint32_t t = (imm6 << 12) | (imm11 << 1);
+ t |= (S << 20) | (J2 << 19) | (J1 << 18);
+ *disp = SignExtend<20, int32_t>(t);
+ return kArmAlign2;
+ }
+ return kArmAlignFail;
+}
+
+// static
+bool Arm32Rel32Translator::EncodeT21(arm_disp_t disp, uint32_t* code32) {
+ uint32_t t = *code32;
+ if ((t & 0xF800D000) == 0xF0008000 && (t & 0x03C00000) != 0x03C00000) {
+ if (disp % 2) // Require 2-byte alignment.
+ return false;
+ if (!SignedFit<21>(disp)) // Detect overflow.
+ return false;
+ uint32_t S = GetBit<20>(disp);
+ uint32_t J2 = GetBit<19>(disp);
+ uint32_t J1 = GetBit<18>(disp);
+ uint32_t imm6 = GetUnsignedBits<12, 17>(disp); // ii...i.
+ uint32_t imm11 = GetUnsignedBits<1, 11>(disp); // jj...j.
+ t &= 0xFBC0D000;
+ t |= (S << 26) | (imm6 << 16) | (J1 << 13) | (J2 << 11) | imm11;
+ *code32 = t;
+ return true;
+ }
+ return false;
+}
+
+// static
+bool Arm32Rel32Translator::ReadT21(rva_t instr_rva,
+ uint32_t code32,
+ rva_t* target_rva) {
+ arm_disp_t disp;
+ ArmAlign align = DecodeT21(code32, &disp);
+ if (align == kArmAlignFail)
+ return false;
+ *target_rva = GetThumb2TargetRvaFromDisp(instr_rva, disp, align);
+ return true;
+}
+
+// static
+bool Arm32Rel32Translator::WriteT21(rva_t instr_rva,
+ rva_t target_rva,
+ uint32_t* code32) {
+ arm_disp_t disp =
+ GetThumb2DispFromTargetRva(instr_rva, target_rva, kArmAlign2);
+ return EncodeT21(disp, code32);
+}
+
+// static
+ArmAlign Arm32Rel32Translator::DecodeT24(uint32_t code32, arm_disp_t* disp) {
+ uint32_t bits = code32 & 0xF800D000;
+ if (bits == 0xF0009000 || bits == 0xF000D000 || bits == 0xF000C000) {
+ // Let I1 = J1 ^ S ^ 1, I2 = J2 ^ S ^ 1.
+ // B encoding T4:
+ // Code: 11110Sii iiiiiiii 10(J1)1(J2)jjj jjjjjjjj
+ // Displacement: SSSSSSSS (I1)(I2)iiiiii iiiijjjj jjjjjjj0
+ // BL encoding T1:
+ // Code: 11110Sii iiiiiiii 11(J1)1(J2)jjj jjjjjjjj
+ // Displacement: SSSSSSSS (I1)(I2)iiiiii iiiijjjj jjjjjjj0
+ // BLX encoding T2: H should be 0:
+ // Code: 11110Sii iiiiiiii 11(J1)0(J2)jjj jjjjjjjH
+ // Displacement: SSSSSSSS (I1)(I2)iiiiii iiiijjjj jjjjjjH0
+ uint32_t imm11 = GetUnsignedBits<0, 10>(code32); // jj...j.
+ uint32_t J2 = GetBit<11>(code32);
+ uint32_t J1 = GetBit<13>(code32);
+ uint32_t imm10 = GetUnsignedBits<16, 25>(code32); // ii...i.
+ uint32_t S = GetBit<26>(code32);
+ uint32_t t = (imm10 << 12) | (imm11 << 1);
+ t |= (S << 24) | ((J1 ^ S ^ 1) << 23) | ((J2 ^ S ^ 1) << 22);
+ 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;
+ if (bits == 0xF000C000) {
+ uint32_t H = GetBit<0>(code32);
+ if (H)
+ return kArmAlignFail; // Illegal instruction: H must be 0.
+ align_by = kArmAlign4;
+ }
+ *disp = static_cast<int32_t>(t);
+ return align_by;
+ }
+ return kArmAlignFail;
+}
+
+// static
+bool Arm32Rel32Translator::EncodeT24(arm_disp_t disp, uint32_t* code32) {
+ uint32_t t = *code32;
+ uint32_t bits = t & 0xF800D000;
+ if (bits == 0xF0009000 || bits == 0xF000D000 || bits == 0xF000C000) {
+ if (disp % 2) // Require 2-byte alignment.
+ return false;
+ // BLX encoding T2 requires H == 0, and that |disp| results in |target_rva|
+ // with a 4-byte aligned address.
+ if (bits == 0xF000C000) {
+ uint32_t H = GetBit<1>(disp);
+ if (H)
+ return false; // Illegal |disp|: H must be 0.
+ }
+ if (!SignedFit<25>(disp)) // Detect overflow.
+ return false;
+ uint32_t imm11 = GetUnsignedBits<1, 11>(disp); // jj...j.
+ uint32_t imm10 = GetUnsignedBits<12, 21>(disp); // ii...i.
+ uint32_t I2 = GetBit<22>(disp);
+ uint32_t I1 = GetBit<23>(disp);
+ uint32_t S = GetBit<24>(disp);
+ t &= 0xF800D000;
+ t |= (S << 26) | (imm10 << 16) | ((I1 ^ S ^ 1) << 13) |
+ ((I2 ^ S ^ 1) << 11) | imm11;
+ *code32 = t;
+ return true;
+ }
+ return false;
+}
+
+// static
+bool Arm32Rel32Translator::ReadT24(rva_t instr_rva,
+ uint32_t code32,
+ rva_t* target_rva) {
+ arm_disp_t disp;
+ ArmAlign align = DecodeT24(code32, &disp);
+ if (align == kArmAlignFail)
+ return false;
+ *target_rva = GetThumb2TargetRvaFromDisp(instr_rva, disp, align);
+ return true;
+}
+
+// static
+bool Arm32Rel32Translator::WriteT24(rva_t instr_rva,
+ rva_t target_rva,
+ uint32_t* code32) {
+ // Dummy decode to get |align|.
+ arm_disp_t dummy_disp;
+ ArmAlign align = DecodeT24(*code32, &dummy_disp);
+ if (align == kArmAlignFail)
+ return false;
+ arm_disp_t disp = GetThumb2DispFromTargetRva(instr_rva, target_rva, align);
+ return EncodeT24(disp, code32);
+}
+
+/******** AArch64Rel32Translator ********/
+
+AArch64Rel32Translator::AArch64Rel32Translator() = default;
+
+// static
+ArmAlign AArch64Rel32Translator::DecodeImmd14(uint32_t code32,
+ arm_disp_t* disp) {
+ // TBZ:
+ // Code: b0110110 bbbbbSii iiiiiiii iiittttt
+ // Displacement: SSSSSSSS SSSSSSSS Siiiiiii iiiiii00
+ // TBNZ:
+ // Code: b0110111 bbbbbSii iiiiiiii iiittttt
+ // Displacement: SSSSSSSS SSSSSSSS Siiiiiii iiiiii00
+ uint32_t bits = code32 & 0x7F000000;
+ if (bits == 0x36000000 || bits == 0x37000000) {
+ *disp = GetSignedBits<5, 18>(code32) << 2;
+ return kArmAlign4;
+ }
+ return kArmAlignFail;
+}
+
+// static
+bool AArch64Rel32Translator::EncodeImmd14(arm_disp_t disp, uint32_t* code32) {
+ uint32_t t = *code32;
+ uint32_t bits = t & 0x7F000000;
+ if (bits == 0x36000000 || bits == 0x37000000) {
+ if (disp % 4) // Require 4-byte alignment.
+ return false;
+ if (!SignedFit<16>(disp)) // Detect overflow.
+ return false;
+ uint32_t imm14 = GetUnsignedBits<2, 15>(disp); // ii...i.
+ t &= 0xFFF8001F;
+ t |= imm14 << 5;
+ *code32 = t;
+ return true;
+ }
+ return false;
+}
+
+// static
+bool AArch64Rel32Translator::ReadImmd14(rva_t instr_rva,
+ uint32_t code32,
+ rva_t* target_rva) {
+ arm_disp_t disp;
+ if (DecodeImmd14(code32, &disp) == kArmAlignFail)
+ return false;
+ *target_rva = GetTargetRvaFromDisp(instr_rva, disp);
+ return true;
+}
+
+// static
+bool AArch64Rel32Translator::WriteImmd14(rva_t instr_rva,
+ rva_t target_rva,
+ uint32_t* code32) {
+ arm_disp_t disp = GetDispFromTargetRva(instr_rva, target_rva);
+ return EncodeImmd14(disp, code32);
+}
+
+// static
+ArmAlign AArch64Rel32Translator::DecodeImmd19(uint32_t code32,
+ arm_disp_t* disp) {
+ // B.cond:
+ // Code: 01010100 Siiiiiii iiiiiiii iii0cccc
+ // Displacement: SSSSSSSS SSSSiiii iiiiiiii iiiiii00
+ // CBZ:
+ // Code: z0110100 Siiiiiii iiiiiiii iiittttt
+ // Displacement: SSSSSSSS SSSSiiii iiiiiiii iiiiii00
+ // CBNZ:
+ // Code: z0110101 Siiiiiii iiiiiiii iiittttt
+ // Displacement: SSSSSSSS SSSSiiii iiiiiiii iiiiii00
+ uint32_t bits1 = code32 & 0xFF000010;
+ uint32_t bits2 = code32 & 0x7F000000;
+ if (bits1 == 0x54000000 || bits2 == 0x34000000 || bits2 == 0x35000000) {
+ *disp = GetSignedBits<5, 23>(code32) << 2;
+ return kArmAlign4;
+ }
+ return kArmAlignFail;
+}
+
+// static
+bool AArch64Rel32Translator::EncodeImmd19(arm_disp_t disp, uint32_t* code32) {
+ uint32_t t = *code32;
+ uint32_t bits1 = t & 0xFF000010;
+ uint32_t bits2 = t & 0x7F000000;
+ if (bits1 == 0x54000000 || bits2 == 0x34000000 || bits2 == 0x35000000) {
+ if (disp % 4) // Require 4-byte alignment.
+ return false;
+ if (!SignedFit<21>(disp)) // Detect overflow.
+ return false;
+ uint32_t imm19 = GetUnsignedBits<2, 20>(disp); // ii...i.
+ t &= 0xFF00001F;
+ t |= imm19 << 5;
+ *code32 = t;
+ return true;
+ }
+ return false;
+}
+
+// static
+bool AArch64Rel32Translator::ReadImmd19(rva_t instr_rva,
+ uint32_t code32,
+ rva_t* target_rva) {
+ arm_disp_t disp;
+ if (DecodeImmd19(code32, &disp) == kArmAlignFail)
+ return false;
+ *target_rva = GetTargetRvaFromDisp(instr_rva, disp);
+ return true;
+}
+
+// static
+bool AArch64Rel32Translator::WriteImmd19(rva_t instr_rva,
+ rva_t target_rva,
+ uint32_t* code32) {
+ arm_disp_t disp = GetDispFromTargetRva(instr_rva, target_rva);
+ return EncodeImmd19(disp, code32);
+}
+
+// static
+ArmAlign AArch64Rel32Translator::DecodeImmd26(uint32_t code32,
+ arm_disp_t* disp) {
+ // B:
+ // Code: 000101Si iiiiiiii iiiiiiii iiiiiiii
+ // Displacement: SSSSSiii iiiiiiii iiiiiiii iiiiii00
+ // BL:
+ // Code: 100101Si iiiiiiii iiiiiiii iiiiiiii
+ // Displacement: SSSSSiii iiiiiiii iiiiiiii iiiiii00
+ uint32_t bits = code32 & 0xFC000000;
+ if (bits == 0x14000000 || bits == 0x94000000) {
+ *disp = GetSignedBits<0, 25>(code32) << 2;
+ return kArmAlign4;
+ }
+ return kArmAlignFail;
+}
+
+// static
+bool AArch64Rel32Translator::EncodeImmd26(arm_disp_t disp, uint32_t* code32) {
+ uint32_t t = *code32;
+ uint32_t bits = t & 0xFC000000;
+ if (bits == 0x14000000 || bits == 0x94000000) {
+ if (disp % 4) // Require 4-byte alignment.
+ return false;
+ if (!SignedFit<28>(disp)) // Detect overflow.
+ return false;
+ uint32_t imm26 = GetUnsignedBits<2, 27>(disp); // ii...i.
+ t &= 0xFC000000;
+ t |= imm26;
+ *code32 = t;
+ return true;
+ }
+ return false;
+}
+
+// static
+bool AArch64Rel32Translator::ReadImmd26(rva_t instr_rva,
+ uint32_t code32,
+ rva_t* target_rva) {
+ arm_disp_t disp;
+ if (DecodeImmd26(code32, &disp) == kArmAlignFail)
+ return false;
+ *target_rva = GetTargetRvaFromDisp(instr_rva, disp);
+ return true;
+}
+
+// static
+bool AArch64Rel32Translator::WriteImmd26(rva_t instr_rva,
+ rva_t target_rva,
+ uint32_t* code32) {
+ arm_disp_t disp = GetDispFromTargetRva(instr_rva, target_rva);
+ return EncodeImmd26(disp, code32);
+}
+
+} // namespace zucchini
diff --git a/arm_utils.h b/arm_utils.h
new file mode 100644
index 0000000..03eb9f4
--- /dev/null
+++ b/arm_utils.h
@@ -0,0 +1,407 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_ZUCCHINI_ARM_UTILS_H_
+#define COMPONENTS_ZUCCHINI_ARM_UTILS_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "components/zucchini/address_translator.h"
+#include "components/zucchini/buffer_view.h"
+
+namespace zucchini {
+
+// References:
+// * ARM32 (32-bit ARM, AKA AArch32):
+// https://static.docs.arm.com/ddi0406/c/DDI0406C_C_arm_architecture_reference_manual.pdf
+// * AArch64 (64-bit ARM):
+// https://static.docs.arm.com/ddi0487/da/DDI0487D_a_armv8_arm.pdf
+
+// Definitions (non-official, but used in this file):
+// * |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.
+// * |code|: Instruction code: ARM instruction code as seen in manual. In ARM
+// mode and for AArch64, this is a 32-bit int. In THUMB2 mode, this may be a
+// 16-bit or 32-bit int.
+// * |disp|: Displacement: For branch instructions (e.g.: B, BL, BLX, and
+// conditional varieties) this is the value encoded in instruction bytes.
+// * PC: Program Counter: In ARM mode this is |instr_rva + 8|; in THUMB2 mode
+// this is |instr_rva + 4|; for AArch64 this is |instr_rva|.
+// * |target_rva|: Target RVA: The RVA targeted by a branch instruction.
+//
+// These are related by:
+// |code| = Fetch(image data at offset(|instr_rva|)).
+// |disp| = Decode(|code|).
+// PC = |instr_rva| + {8 in ARM mode, 4 in THUMB2 mode, 0 for AArch64}.
+// |target_rva| = PC + |disp| - (see "BLX complication" below)
+//
+// Example 1 (ARM mode):
+// 00103050: 00 01 02 EA B 00183458
+// |instr_rva| = 0x00103050 (4-byte aligned).
+// |code| = 0xEA020100 (little endian fetched from data).
+// |disp| = 0x00080400 (decoded from |code| with A24 -> B encoding T1).
+// PC = |instr_rva| + 8 = 0x00103058 (ARM mode).
+// |target_rva| = PC + |disp| = 0x00183458.
+//
+// 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).
+// |disp| = 0x00000402 (decoded from |code| with T24 -> BL encoding T1).
+// PC = |instr_rva| + 4 = 0x001030A6 (THUMB2 mode).
+// |target_rva| = PC + |disp| = 0x001034A8.
+//
+// Example 3 (AArch64):
+// 0000000000305070: 03 02 01 14 B 000000000034587C
+// |instr_rva| = 0x00305070 (4-byte aligned, assumed to fit in 32-bit).
+// |code| = 0x14010203 (little endian fetchd from data).
+// |disp| = 0x0004080C (decoded from |code| with Immd -> B).
+// 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.
+
+using arm_disp_t = int32_t;
+
+// Alignment requirement for |target_rva|, useful for |disp| <-> |target_rva|
+// (also requires |instr_rva|). Alignment is determined by parsing |code| in
+// *Decode() functions. kArmAlignFail is also defined to indicate parse failure.
+// Alignments can be 2 or 4. These values are also used in the enum, so
+// |x % align| with |x & (align - 1)| to compute alignment.
+enum ArmAlign : uint32_t {
+ kArmAlignFail = 0U,
+ kArmAlign2 = 2U,
+ kArmAlign4 = 4U,
+};
+
+// Traits for rel32 address types (technically rel64 for AArch64 -- but we
+// assume values are small enough), which form collections of strategies to
+// process each rel32 address type.
+template <typename ENUM_ADDR_TYPE,
+ ENUM_ADDR_TYPE ADDR_TYPE,
+ typename CODE_T,
+ CODE_T (*FETCH)(ConstBufferView, offset_t),
+ void (*STORE)(MutableBufferView, offset_t, CODE_T),
+ ArmAlign (*DECODE)(CODE_T, arm_disp_t*),
+ bool (*ENCODE)(arm_disp_t, CODE_T*),
+ bool (*READ)(rva_t, CODE_T, rva_t*),
+ bool (*WRITE)(rva_t, rva_t, CODE_T*)>
+class ArmAddrTraits {
+ public:
+ static constexpr ENUM_ADDR_TYPE addr_type = ADDR_TYPE;
+ using code_t = CODE_T;
+ static constexpr CODE_T (*Fetch)(ConstBufferView, offset_t) = FETCH;
+ static constexpr void (*Store)(MutableBufferView, offset_t, CODE_T) = STORE;
+ static constexpr ArmAlign (*Decode)(CODE_T, arm_disp_t*) = DECODE;
+ static constexpr bool (*Encode)(arm_disp_t, CODE_T*) = ENCODE;
+ static constexpr bool (*Read)(rva_t, CODE_T, rva_t*) = READ;
+ static constexpr bool (*Write)(rva_t, rva_t, CODE_T*) = WRITE;
+};
+
+// Given THUMB2 instruction |code16|, returns 2 if it's from a 16-bit THUMB2
+// instruction, or 4 if it's from a 32-bit THUMB2 instruction.
+inline int GetThumb2InstructionSize(uint16_t code16) {
+ return ((code16 & 0xF000) == 0xF000 || (code16 & 0xF800) == 0xE800) ? 4 : 2;
+}
+
+// A translator for ARM mode and THUMB2 mode with static functions that
+// translate among |code|, |disp|, and |target_rva|.
+class Arm32Rel32Translator {
+ public:
+ // Rel32 address types enumeration.
+ enum AddrType : uint8_t {
+ ADDR_NONE = 0xFF,
+ ADDR_A24 = 0,
+ ADDR_T8,
+ ADDR_T11,
+ ADDR_T21,
+ ADDR_T24,
+ NUM_ADDR_TYPE
+ };
+
+ Arm32Rel32Translator();
+
+ // Fetches the 32-bit ARM instruction |code| at |view[idx]|.
+ static inline uint32_t FetchArmCode32(ConstBufferView view, offset_t idx) {
+ return view.read<uint32_t>(idx);
+ }
+
+ // Fetches the 16-bit THUMB2 instruction |code| at |view[idx]|.
+ static inline uint16_t FetchThumb2Code16(ConstBufferView view, offset_t idx) {
+ return view.read<uint16_t>(idx);
+ }
+
+ // Fetches the 32-bit THUMB2 instruction |code| at |view[idx]|.
+ static inline uint32_t FetchThumb2Code32(ConstBufferView view, offset_t idx) {
+ // By convention, 32-bit THUMB2 instructions are written (as seen later) as:
+ // [byte3, byte2, byte1, byte0].
+ // However (assuming little-endian ARM) the in-memory representation is
+ // [byte2, byte3, byte0, byte1].
+ return (static_cast<uint32_t>(view.read<uint16_t>(idx)) << 16) |
+ view.read<uint16_t>(idx + 2);
+ }
+
+ // Stores the 32-bit ARM instruction |code| to |mutable_view[idx]|.
+ static inline void StoreArmCode32(MutableBufferView mutable_view,
+ offset_t idx,
+ uint32_t code) {
+ mutable_view.write<uint32_t>(idx, code);
+ }
+
+ // Stores the 16-bit THUMB2 instruction |code| to |mutable_view[idx]|.
+ static inline void StoreThumb2Code16(MutableBufferView mutable_view,
+ offset_t idx,
+ uint16_t code) {
+ mutable_view.write<uint16_t>(idx, code);
+ }
+
+ // Stores the next 32-bit THUMB2 instruction |code| to |mutable_view[idx]|.
+ static inline void StoreThumb2Code32(MutableBufferView mutable_view,
+ offset_t idx,
+ uint32_t code) {
+ mutable_view.write<uint16_t>(idx, static_cast<uint16_t>(code >> 16));
+ mutable_view.write<uint16_t>(idx + 2, static_cast<uint16_t>(code & 0xFFFF));
+ }
+
+ // The following functions convert |code| (16-bit or 32-bit) from/to |disp|
+ // or |target_rva|, for specific branch instruction types.
+ // Read*() and write*() functions convert between |code| and |target_rva|.
+ // * Decode*() determines whether |code16/code32| is a branch instruction
+ // of a specific type. If so, then extracts |*disp| and returns the required
+ // ArmAlign. Otherwise returns kArmAlignFail.
+ // * Encode*() determines whether |*code16/*code32| is a branch instruction of
+ // a specific type, and whether it can accommodate |disp|. If so, then
+ // re-encodes |*code32| using |disp|, and returns true. Otherwise returns
+ // false.
+ // * Read*() is similar to Decode*(), but on success, extracts |*target_rva|
+ // using |instr_rva| as aid, performs the proper alignment, and returns
+ // true. Otherwise returns false.
+ // * Write*() is similar to Encode*(), takes |target_rva| instead, and uses
+ // |instr_rva| as aid.
+ static ArmAlign DecodeA24(uint32_t code32, arm_disp_t* disp);
+ static bool EncodeA24(arm_disp_t disp, uint32_t* code32);
+ static bool ReadA24(rva_t instr_rva, uint32_t code32, rva_t* target_rva);
+ static bool WriteA24(rva_t instr_rva, rva_t target_rva, uint32_t* code32);
+
+ static ArmAlign DecodeT8(uint16_t code16, arm_disp_t* disp);
+ static bool EncodeT8(arm_disp_t disp, uint16_t* code16);
+ static bool ReadT8(rva_t instr_rva, uint16_t code16, rva_t* target_rva);
+ static bool WriteT8(rva_t instr_rva, rva_t target_rva, uint16_t* code16);
+
+ static ArmAlign DecodeT11(uint16_t code16, arm_disp_t* disp);
+ static bool EncodeT11(arm_disp_t disp, uint16_t* code16);
+ static bool ReadT11(rva_t instr_rva, uint16_t code16, rva_t* target_rva);
+ static bool WriteT11(rva_t instr_rva, rva_t target_rva, uint16_t* code16);
+
+ static ArmAlign DecodeT21(uint32_t code32, arm_disp_t* disp);
+ static bool EncodeT21(arm_disp_t disp, uint32_t* code32);
+ static bool ReadT21(rva_t instr_rva, uint32_t code32, rva_t* target_rva);
+ static bool WriteT21(rva_t instr_rva, rva_t target_rva, uint32_t* code32);
+
+ static ArmAlign DecodeT24(uint32_t code32, arm_disp_t* disp);
+ static bool EncodeT24(arm_disp_t disp, uint32_t* code32);
+ static bool ReadT24(rva_t instr_rva, uint32_t code32, rva_t* target_rva);
+ static bool WriteT24(rva_t instr_rva, rva_t target_rva, uint32_t* code32);
+
+ // Computes |target_rva| from |instr_rva| and |disp| in ARM mode.
+ static inline rva_t GetArmTargetRvaFromDisp(rva_t instr_rva,
+ arm_disp_t disp,
+ ArmAlign align) {
+ rva_t ret = static_cast<rva_t>(instr_rva + 8 + disp);
+ // Align down.
+ DCHECK_NE(align, kArmAlignFail);
+ return ret - (ret & static_cast<rva_t>(align - 1));
+ }
+
+ // Computes |target_rva| from |instr_rva| and |disp| in THUMB2 mode.
+ static inline rva_t GetThumb2TargetRvaFromDisp(rva_t instr_rva,
+ arm_disp_t disp,
+ ArmAlign align) {
+ rva_t ret = static_cast<rva_t>(instr_rva + 4 + disp);
+ // Align down.
+ DCHECK_NE(align, kArmAlignFail);
+ return ret - (ret & static_cast<rva_t>(align - 1));
+ }
+
+ // Computes |disp| from |instr_rva| and |target_rva| in ARM mode.
+ static inline arm_disp_t GetArmDispFromTargetRva(rva_t instr_rva,
+ rva_t target_rva,
+ ArmAlign align) {
+ // Assumes that |instr_rva + 8| does not overflow.
+ arm_disp_t ret = static_cast<arm_disp_t>(target_rva) -
+ static_cast<arm_disp_t>(instr_rva + 8);
+ // Align up.
+ DCHECK_NE(align, kArmAlignFail);
+ return ret + ((-ret) & static_cast<arm_disp_t>(align - 1));
+ }
+
+ // Computes |disp| from |instr_rva| and |target_rva| in THUMB2 mode.
+ static inline arm_disp_t GetThumb2DispFromTargetRva(rva_t instr_rva,
+ rva_t target_rva,
+ ArmAlign align) {
+ // Assumes that |instr_rva + 4| does not overflow.
+ arm_disp_t ret = static_cast<arm_disp_t>(target_rva) -
+ static_cast<arm_disp_t>(instr_rva + 4);
+ // Align up.
+ DCHECK_NE(align, kArmAlignFail);
+ return ret + ((-ret) & static_cast<arm_disp_t>(align - 1));
+ }
+
+ // Strategies to process each rel32 address type.
+ using AddrTraits_A24 = ArmAddrTraits<AddrType,
+ ADDR_A24,
+ uint32_t,
+ FetchArmCode32,
+ StoreArmCode32,
+ DecodeA24,
+ EncodeA24,
+ ReadA24,
+ WriteA24>;
+ using AddrTraits_T8 = ArmAddrTraits<AddrType,
+ ADDR_T8,
+ uint16_t,
+ FetchThumb2Code16,
+ StoreThumb2Code16,
+ DecodeT8,
+ EncodeT8,
+ ReadT8,
+ WriteT8>;
+ using AddrTraits_T11 = ArmAddrTraits<AddrType,
+ ADDR_T11,
+ uint16_t,
+ FetchThumb2Code16,
+ StoreThumb2Code16,
+ DecodeT11,
+ EncodeT11,
+ ReadT11,
+ WriteT11>;
+ using AddrTraits_T21 = ArmAddrTraits<AddrType,
+ ADDR_T21,
+ uint32_t,
+ FetchThumb2Code32,
+ StoreThumb2Code32,
+ DecodeT21,
+ EncodeT21,
+ ReadT21,
+ WriteT21>;
+ using AddrTraits_T24 = ArmAddrTraits<AddrType,
+ ADDR_T24,
+ uint32_t,
+ FetchThumb2Code32,
+ StoreThumb2Code32,
+ DecodeT24,
+ EncodeT24,
+ ReadT24,
+ WriteT24>;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Arm32Rel32Translator);
+};
+
+// Translator for AArch64, which is simpler than 32-bit ARM. Although pointers
+// are 64-bit, displacements are within 32-bit.
+class AArch64Rel32Translator {
+ public:
+ // Rel64 address types enumeration.
+ enum AddrType : uint8_t {
+ ADDR_NONE = 0xFF,
+ ADDR_IMMD14 = 0,
+ ADDR_IMMD19,
+ ADDR_IMMD26,
+ NUM_ADDR_TYPE
+ };
+
+ // Although RVA for 64-bit architecture can be 64-bit in length, we make the
+ // bold assumption that for ELF images that RVA will stay nicely in 32-bit!
+ AArch64Rel32Translator();
+
+ static inline uint32_t FetchCode32(ConstBufferView view, offset_t idx) {
+ return view.read<uint32_t>(idx);
+ }
+
+ static inline void StoreCode32(MutableBufferView mutable_view,
+ offset_t idx,
+ uint32_t code) {
+ mutable_view.write<uint32_t>(idx, code);
+ }
+
+ // Conversion functions for |code32| from/to |disp| or |target_rva|, similar
+ // to the counterparts in Arm32Rel32Translator.
+ static ArmAlign DecodeImmd14(uint32_t code32, arm_disp_t* disp);
+ static bool EncodeImmd14(arm_disp_t disp, uint32_t* code32);
+ static bool ReadImmd14(rva_t instr_rva, uint32_t code32, rva_t* target_rva);
+ static bool WriteImmd14(rva_t instr_rva, rva_t target_rva, uint32_t* code32);
+
+ static ArmAlign DecodeImmd19(uint32_t code32, arm_disp_t* disp);
+ static bool EncodeImmd19(arm_disp_t disp, uint32_t* code32);
+ static bool ReadImmd19(rva_t instr_rva, uint32_t code32, rva_t* target_rva);
+ static bool WriteImmd19(rva_t instr_rva, rva_t target_rva, uint32_t* code32);
+
+ static ArmAlign DecodeImmd26(uint32_t code32, arm_disp_t* disp);
+ static bool EncodeImmd26(arm_disp_t disp, uint32_t* code32);
+ static bool ReadImmd26(rva_t instr_rva, uint32_t code32, rva_t* target_rva);
+ static bool WriteImmd26(rva_t instr_rva, rva_t target_rva, uint32_t* code32);
+
+ static inline rva_t GetTargetRvaFromDisp(rva_t instr_rva, arm_disp_t disp) {
+ return static_cast<rva_t>(instr_rva + disp);
+ }
+
+ static inline arm_disp_t GetDispFromTargetRva(rva_t instr_rva,
+ rva_t target_rva) {
+ return static_cast<arm_disp_t>(target_rva - instr_rva);
+ }
+
+ // Strategies to process each rel32 address type.
+ using AddrTraits_Immd14 = ArmAddrTraits<AddrType,
+ ADDR_IMMD14,
+ uint32_t,
+ FetchCode32,
+ StoreCode32,
+ DecodeImmd14,
+ EncodeImmd14,
+ ReadImmd14,
+ WriteImmd14>;
+ using AddrTraits_Immd19 = ArmAddrTraits<AddrType,
+ ADDR_IMMD19,
+ uint32_t,
+ FetchCode32,
+ StoreCode32,
+ DecodeImmd19,
+ EncodeImmd19,
+ ReadImmd19,
+ WriteImmd19>;
+ using AddrTraits_Immd26 = ArmAddrTraits<AddrType,
+ ADDR_IMMD26,
+ uint32_t,
+ FetchCode32,
+ StoreCode32,
+ DecodeImmd26,
+ EncodeImmd26,
+ ReadImmd26,
+ WriteImmd26>;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(AArch64Rel32Translator);
+};
+
+} // namespace zucchini
+
+#endif // COMPONENTS_ZUCCHINI_ARM_UTILS_H_
diff --git a/arm_utils_unittest.cc b/arm_utils_unittest.cc
new file mode 100644
index 0000000..c6d513d
--- /dev/null
+++ b/arm_utils_unittest.cc
@@ -0,0 +1,645 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/zucchini/arm_utils.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <algorithm>
+#include <cctype>
+#include <initializer_list>
+#include <map>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include "base/logging.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace zucchini {
+
+namespace {
+
+// "Clean slate" |code|s for branch instruction encodings with |disp| = 0, and
+// if applicable, |cond| = 0.
+uint32_t kCleanSlateB_A1 = 0x0A000000; // A24.
+uint32_t kCleanSlateBL_A1 = 0x0B000000; // A24.
+uint32_t kCleanSlateBLX_A2 = 0xFA000000; // A24.
+uint16_t kCleanSlateB_T1 = 0xD000; // T8.
+uint16_t kCleanSlateB_T2 = 0xE000; // T11.
+uint32_t kCleanSlateB_T3 = 0xF0008000; // T21.
+// For T4 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.
+
+// For AArch64.
+uint32_t kCleanSlate64TBZw = 0x36000000; // Immd14.
+uint32_t kCleanSlate64TBZz = 0xB6000000; // Immd14.
+uint32_t kCleanSlate64TBNZw = 0x37000000; // Immd14.
+uint32_t kCleanSlate64TBNZz = 0xB7000000; // Immd14.
+uint32_t kCleanSlate64Bcond = 0x54000000; // Immd19.
+uint32_t kCleanSlate64CBZw = 0x34000000; // Immd19.
+uint32_t kCleanSlate64CBZz = 0xB4000000; // Immd19.
+uint32_t kCleanSlate64CBNZw = 0x35000000; // Immd19.
+uint32_t kCleanSlate64CBNZz = 0xB5000000; // Immd19.
+uint32_t kCleanSlate64B = 0x14000000; // Immd26.
+uint32_t kCleanSlate64BL = 0x94000000; // Immd26.
+
+// Special case: Cond = 0xE => AL.
+uint32_t kCleanSlateBAL_A1 = kCleanSlateB_A1 | (0xE << 28); //
+
+// Test helper: Extracts |components| from |value| (may be |code| or |disp|)
+// based on |pattern|. Also performs consistency checks. On success, writes to
+// |*components| and returns true. Otherwise returns false.
+// Example (all numbers are in binary):
+// |pattern| = "11110Scc cciiiiii 10(J1)0(J2)jjj jjjj...."
+// |value| = 11110111 00111000 10 1 0 0 111 11000101
+// Result: Noting that all 0's and 1's are consistent, returns true with:
+// |*components| = {S: 1, c: 1100, i: 111000, J1: 1, J2: 0, j: 1111100}
+// Rules for |pattern|:
+// * Spaces are ignored.
+// * '.' means "don't care".
+// * '0' and '1' are expected literals; mismatch leads to failure.
+// * A variable name is specified as:
+// * A single letter.
+// * "(var)", where "var" is a name that begins with a letter.
+// * If a variable's first letter is uppercase, then it's a singleton bit.
+// * If repeated, consistency check is applied (must be identical).
+// * If a variable's first letter is lowercase, then it spans multiple bits.
+// * These need not be contiguous, but order is preserved (big-endian).
+static bool SplitBits(const std::string& pattern,
+ uint32_t value,
+ std::map<std::string, uint32_t>* components) {
+ CHECK(components);
+
+ // Split |pattern| into |token_list|.
+ std::vector<std::string> token_list;
+ size_t bracket_start = std::string::npos;
+ for (size_t i = 0; i < pattern.size(); ++i) {
+ char ch = pattern[i];
+ if (bracket_start == std::string::npos) {
+ if (ch == '(')
+ bracket_start = i + 1;
+ else if (ch != ' ') // Ignore space.
+ token_list.push_back(std::string(1, ch));
+ } else if (ch == ')') {
+ token_list.push_back(pattern.substr(bracket_start, i - bracket_start));
+ bracket_start = std::string::npos;
+ }
+ }
+ CHECK_EQ(std::string::npos, bracket_start); // No dangling "(".
+
+ // Process each token.
+ size_t num_tokens = token_list.size();
+ std::map<std::string, uint32_t> temp_components;
+ CHECK(num_tokens == 32 || (num_tokens == 16 && value <= 0xFFFF));
+ for (size_t i = 0; i < num_tokens; ++i) {
+ const std::string& token = token_list[i];
+ CHECK(!token.empty());
+ uint32_t bit = (value >> (num_tokens - 1 - i)) & 1;
+ if (token == "0" || token == "1") {
+ if (token[0] != static_cast<char>('0' + bit))
+ return false; // Fail: Mismatch.
+ } else if (isupper(token[0])) {
+ if (temp_components.count(token)) {
+ if (temp_components[token] != bit)
+ return false; // Fail: Singleton bit not uniform.
+ } else {
+ temp_components[token] = bit;
+ }
+ } else if (islower(token[0])) {
+ temp_components[token] = (temp_components[token] << 1) | bit;
+ } else if (token != ".") {
+ return false; // Fail: Unrecognized token.
+ }
+ }
+ components->swap(temp_components);
+ return true;
+}
+
+// ARM32 or AArch64 instruction specification for tests. May be 16-bit or 32-bit
+// (determined by INT_T).
+template <typename INT_T>
+struct ArmRelInstruction {
+ ArmRelInstruction(const std::string& code_pattern_in, INT_T code)
+ : code_pattern(code_pattern_in), clean_slate_code(code) {}
+
+ // Code pattern for SplitBits().
+ std::string code_pattern;
+
+ // "Clean slate" |code| encodes |disp| = 0.
+ INT_T clean_slate_code;
+};
+
+// Tester for Decode/Encode functions for ARM.
+template <typename TRAITS>
+class ArmTranslatorEncodeDecodeTest {
+ public:
+ using CODE_T = typename TRAITS::code_t;
+
+ ArmTranslatorEncodeDecodeTest() {}
+
+ // For each instruction (with |clean_slate_code| in |instr_list|) and for each
+ // |disp| in |good_disp_list|, forms |code| with |encode_fun()| and checks for
+ // success. Extracts |disp_out| with |decode_fun()| and checks that it's the
+ // original |disp|. For each (|disp|, |code|) pair, extracts components using
+ // SplitBits(), and checks that components from |toks_list| are identical. For
+ // each |disp| in |bad_disp_list|, checks that |decode_fun_()| fails.
+ void Run(const std::string& disp_pattern,
+ const std::vector<std::string>& toks_list,
+ const std::vector<ArmRelInstruction<CODE_T>>& instr_list,
+ const std::vector<arm_disp_t>& good_disp_list,
+ const std::vector<arm_disp_t>& bad_disp_list) {
+ ArmAlign (*decode_fun)(CODE_T, arm_disp_t*) = TRAITS::Decode;
+ bool (*encode_fun)(arm_disp_t, CODE_T*) = TRAITS::Encode;
+
+ for (const ArmRelInstruction<CODE_T> instr : instr_list) {
+ // Parse clean slate code bytes, and ensure it's well-formed.
+ std::map<std::string, uint32_t> clean_slate_code_components;
+ EXPECT_TRUE(SplitBits(instr.code_pattern, instr.clean_slate_code,
+ &clean_slate_code_components));
+
+ for (arm_disp_t disp : good_disp_list) {
+ CODE_T code = instr.clean_slate_code;
+ // Encode |disp| to |code|.
+ EXPECT_TRUE((*encode_fun)(disp, &code)) << disp;
+ arm_disp_t disp_out = 0;
+
+ // Extract components (performs consistency checks) and compare.
+ std::map<std::string, uint32_t> disp_components;
+ EXPECT_TRUE(SplitBits(disp_pattern, static_cast<uint32_t>(disp),
+ &disp_components));
+ std::map<std::string, uint32_t> code_components;
+ EXPECT_TRUE(SplitBits(instr.code_pattern, code, &code_components));
+ for (const std::string& tok : toks_list) {
+ EXPECT_EQ(1U, disp_components.count(tok)) << tok;
+ EXPECT_EQ(1U, code_components.count(tok)) << tok;
+ EXPECT_EQ(disp_components[tok], code_components[tok]) << tok;
+ }
+
+ // Decode |code| to |disp_out|, check fidelity.
+ EXPECT_NE(kArmAlignFail, (*decode_fun)(code, &disp_out));
+ EXPECT_EQ(disp, disp_out);
+
+ // Sanity check: Re-encode |disp| into |code|, ensure no change.
+ CODE_T code_copy = code;
+ EXPECT_TRUE((*encode_fun)(disp, &code));
+ EXPECT_EQ(code_copy, code);
+
+ // Encode 0, ensure we get clean slate |code| back.
+ EXPECT_TRUE((*encode_fun)(0, &code));
+ EXPECT_EQ(instr.clean_slate_code, code);
+ }
+
+ for (arm_disp_t disp : bad_disp_list) {
+ CODE_T code = instr.clean_slate_code;
+ EXPECT_FALSE((*encode_fun)(disp, &code)) << disp;
+ // Value does not get modified after failure.
+ EXPECT_EQ(instr.clean_slate_code, code);
+ }
+ }
+ }
+};
+
+} // namespace
+
+// Test for test helper.
+TEST(ArmUtilsTest, SplitBits) {
+ // If |expected| == "BAD" then we expect failure.
+ auto run_test = [](const std::string& expected, const std::string& pattern,
+ uint32_t value) {
+ std::map<std::string, uint32_t> components;
+ if (expected == "BAD") {
+ EXPECT_FALSE(SplitBits(pattern, value, &components));
+ EXPECT_TRUE(components.empty());
+ } else {
+ EXPECT_TRUE(SplitBits(pattern, value, &components));
+ std::ostringstream oss;
+ // Not using AsHex<>, since number of digits is not fixed.
+ oss << std::uppercase << std::hex;
+ std::string sep = "";
+ for (auto it : components) {
+ oss << sep << it.first << "=" << it.second;
+ sep = ",";
+ }
+ EXPECT_EQ(expected, oss.str());
+ }
+ };
+
+ run_test("a=ABCD0123", "aaaaaaaa aaaaaaaa aaaaaaaa aaaaaaaa", 0xABCD0123);
+ run_test("a=ABCD,b=123", "aaaaaaaa aaaaaaaa bbbbbbbb bbbbbbbb", 0xABCD0123);
+ run_test("a=23,b=1,c=CD,d=AB", "dddddddd cccccccc bbbbbbbb aaaaaaaa",
+ 0xABCD0123);
+ run_test("", "........ ........ ........ ........", 0xABCD0123);
+ run_test("t=AC02", " tttt.... tt tt.... tttt....tttt.... ", 0xABCD0123);
+
+ run_test("a=8,b=C,c=E,d1=F", "aaaabbbb cccc(d1)(d1)(d1)(d1)", 0x8CEF);
+ run_test("a=F,b=7,c=3,d1=1", "abc(d1)abc(d1) abc(d1)abc(d1)", 0x8CEF);
+
+ run_test("A1=0,X=1", "(A1)XX(A1) X(A1)(A1)(A1) (X)(A1)(X)X(X)(X)X(A1)",
+ 0x68BE);
+ run_test("BAD", "(A1)XX(A1) X(A1)(A1)(A1) (X)(A1)(X)X(X)(X)X(A1)", 0x68BF);
+ run_test("BAD", "(A1)XX(A1) X(A1)(A1)(A1) (X)(A1)(X)X(X)(X)X(A1)", 0x683E);
+
+ run_test("A=1,B=0,a=C", "AAAAaaaa BBBB01..", 0xFC06);
+ run_test("A=1,B=0,a=4", "AAAAaaaa BBBB01..", 0xF406);
+ run_test("A=0,B=1,a=C", "AAAAaaaa BBBB01..", 0x0CF5);
+ run_test("BAD", "AAAAaaaa BBBB01..", 0xEC06); // Non-uniform A.
+ run_test("BAD", "AAAAaaaa BBBB01..", 0xFC16); // Non-uniform B.
+ run_test("BAD", "AAAAaaaa BBBB01..", 0xFC02); // Constant mismatch.
+}
+
+TEST(Arm32Rel32Translator, Fetch) {
+ std::vector<uint8_t> bytes = {0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE};
+ ConstBufferView region(&bytes[0], bytes.size());
+ Arm32Rel32Translator translator;
+ EXPECT_EQ(0x76543210U, translator.FetchArmCode32(region, 0U));
+ EXPECT_EQ(0xFEDCBA98U, translator.FetchArmCode32(region, 4U));
+
+ EXPECT_EQ(0x3210U, translator.FetchThumb2Code16(region, 0U));
+ EXPECT_EQ(0xFEDCU, translator.FetchThumb2Code16(region, 6U));
+
+ EXPECT_EQ(0x32107654U, translator.FetchThumb2Code32(region, 0U));
+ EXPECT_EQ(0xBA98FEDCU, translator.FetchThumb2Code32(region, 4U));
+}
+
+TEST(Arm32Rel32Translator, Store) {
+ std::vector<uint8_t> expected = {
+ 0xFF, 0xFF, 0xFF, 0xFF, // Padding.
+ 0x10, 0x32, 0x54, 0x76, // ARM 32-bit.
+ 0xFF, 0xFF, // Padding.
+ 0x42, 0x86, // THUMB2 16-bit.
+ 0xFF, 0xFF, // Padding.
+ 0xDC, 0xFE, 0x98, 0xBA, // THUMB2 32-bit.
+ 0xFF, 0xFF, 0xFF, 0xFF // Padding.
+ };
+
+ std::vector<uint8_t> bytes(4 * 2 + 2 * 3 + 4 * 2, 0xFF);
+ MutableBufferView region(&bytes[0], bytes.size());
+ CHECK_EQ(expected.size(), bytes.size());
+
+ Arm32Rel32Translator translator;
+ translator.StoreArmCode32(region, 4U, 0x76543210U);
+ translator.StoreThumb2Code16(region, 10U, 0x8642U);
+ translator.StoreThumb2Code32(region, 14U, 0xFEDCBA98U);
+
+ EXPECT_EQ(expected, bytes);
+}
+
+// Detailed test of Encode/Decode: Check valid and invalid |disp| for various
+// clean slate |code| cases. Also check |disp| and |code| binary components,
+// which in Arm32Rel32Translator comments.
+TEST(Arm32Rel32Translator, EncodeDecode) {
+ // A24 tests.
+ ArmTranslatorEncodeDecodeTest<Arm32Rel32Translator::AddrTraits_A24> test_A24;
+ for (int cond = 0; cond <= 0x0E; ++cond) {
+ ArmRelInstruction<uint32_t> B_A1_cond("cccc1010 Siiiiiii iiiiiiii iiiiiiii",
+ kCleanSlateB_A1 | (cond << 28));
+ ArmRelInstruction<uint32_t> BL_A1_cond(
+ "cccc1011 Siiiiiii iiiiiiii iiiiiiii", kCleanSlateBL_A1 | (cond << 28));
+ test_A24.Run("SSSSSSSi iiiiiiii iiiiiiii iiiiii00", {"S", "i"},
+ {B_A1_cond, BL_A1_cond},
+ {0x01FFFFFC, -0x02000000, 0, 4, -4, 0x40, 0x44},
+ {2, -2, 0x41, 0x42, 0x43, 0x02000000, -0x02000004});
+ }
+ // BLX encoding A2, which has 2-byte alignment.
+ ArmRelInstruction<uint32_t> BLX_A2("1111101H Siiiiiii iiiiiiii iiiiiiii",
+ kCleanSlateBLX_A2);
+ test_A24.Run("SSSSSSSi iiiiiiii iiiiiiii iiiiiiH0", {"S", "i", "H"}, {BLX_A2},
+ {0x01FFFFFC, 0x01FFFFFE, -0x02000000, 0, 2, -2, 4, 0x40, 0x42},
+ {1, -1, 0x41, 0x43, 0x02000000, -0x02000002});
+
+ // T8 tests.
+ ArmTranslatorEncodeDecodeTest<Arm32Rel32Translator::AddrTraits_T8> test_T8;
+ for (int cond = 0; cond <= 0x0E; ++cond) {
+ ArmRelInstruction<uint16_t> B_T1_cond("1101cccc Siiiiiii",
+ kCleanSlateB_T1 | (cond << 8));
+ test_T8.Run("SSSSSSSS SSSSSSSS SSSSSSSS iiiiiii0", {"S", "i"}, {B_T1_cond},
+ {0x00FE, -0x0100, 0, 2, -2, 4, 0x40, 0x42},
+ {1, -1, 0x41, 0x43, 0x0100, -0x0102});
+ }
+ ArmRelInstruction<uint16_t> B_T1_invalid("11011111 ........",
+ kCleanSlateB_T1 | (0x0F << 8));
+ test_T8.Run("........ ........ ........ ........", std::vector<std::string>(),
+ {B_T1_invalid}, std::vector<arm_disp_t>(),
+ {0x00FE, -0x0100, 0, 2, 4, 0x40, 0x41, 0x0100, -0x0102});
+
+ // T11 tests.
+ ArmTranslatorEncodeDecodeTest<Arm32Rel32Translator::AddrTraits_T11> test_T11;
+ ArmRelInstruction<uint16_t> B_T2("11100Sii iiiiiiii", kCleanSlateB_T2);
+ test_T11.Run("SSSSSSSS SSSSSSSS SSSSSiii iiiiiii0", {"S", "i"}, {B_T2},
+ {0x07FE, -0x0800, 0, 2, -2, 4, 0x40, 0x42},
+ {1, -1, 0x41, 0x43, 0x0800, -0x0802});
+
+ // T21 tests.
+ ArmTranslatorEncodeDecodeTest<Arm32Rel32Translator::AddrTraits_T21> test_T21;
+ for (int cond = 0; cond <= 0x0E; ++cond) {
+ ArmRelInstruction<uint32_t> B_T3_cond(
+ "11110Scc cciiiiii 10(J1)0(J2)jjj jjjjjjjj",
+ kCleanSlateB_T3 | (cond << 22));
+ test_T21.Run("SSSSSSSS SSSS(J2)(J1)ii iiiijjjj jjjjjjj0",
+ {"S", "J2", "J1", "i", "j"}, {B_T3_cond},
+ {0x000FFFFE, -0x00100000, 0, 2, -2, 4, 0x40, 0x42},
+ {1, -1, 0x41, 0x43, 0x00100000, -0x00100002});
+ }
+ ArmRelInstruction<uint32_t> B_T3_invalid(
+ "11110.11 11...... 10.0.... ........", kCleanSlateB_T3 | (0x0F << 22));
+ test_T21.Run("........ ........ ........ ........",
+ std::vector<std::string>(), {B_T3_invalid},
+ std::vector<arm_disp_t>(),
+ {0x000FFFFE, -0x00100000, 0, 2, 4, 0x40, 0x42, 1, 0x41, 0x43,
+ 0x00100000, -0x00100002});
+
+ // T24 tests.
+ ArmTranslatorEncodeDecodeTest<Arm32Rel32Translator::AddrTraits_T24> test_T24;
+ // "Clean slate" means J1 = J2 = 1, so we include 0x00002800.
+ ArmRelInstruction<uint32_t> B_T4("11110Sii iiiiiiii 10(J1)1(J2)jjj jjjjjjjj",
+ kCleanSlateB_T4);
+ ArmRelInstruction<uint32_t> BL_T1("11110Sii iiiiiiii 11(J1)1(J2)jjj jjjjjjjj",
+ kCleanSlateBL_T1);
+ test_T24.Run("SSSSSSSS (I1)(I2)iiiiii iiiijjjj jjjjjjj0",
+ {"S", "i", "j"}, // Skip "J1", "J2", "I1", "I2" checks.
+ {B_T4, BL_T1},
+ {0x00FFFFFE, -0x01000000, 0, 2, -2, 4, -4, 0x40, 0x42},
+ {1, -1, 0x41, 0x43, 0x01000000, -0x01000002});
+
+ // For BLX encoding T2, |disp| must be multiple of 4.
+ ArmRelInstruction<uint32_t> BLX_T2(
+ "11110Sii iiiiiiii 11(J1)0(J2)jjj jjjjjjj0", kCleanSlateBLX_T2);
+ test_T24.Run(
+ "SSSSSSSS (I1)(I2)iiiiii iiiijjjj jjjjjj00",
+ {"S", "i", "j"}, // Skip "J1", "J2", "I1", "I2" checks.
+ {BLX_T2}, {0x00FFFFFC, -0x01000000, 0, 4, -4, 0x40},
+ {1, -1, 2, -2, 0x41, 0x42, 0x43, 0x00FFFFFE, 0x01000000, -0x01000002});
+}
+
+TEST(Arm32Rel32Translator, WriteRead) {
+ // TODO(huangs): Implement.
+}
+
+// Typical usage in |target_rva| extraction.
+TEST(Arm32Rel32Translator, Main) {
+ // ARM mode (32-bit).
+ // 00103050: 00 01 02 EA B 00183458 ; B encoding A1 (cond = AL).
+ {
+ rva_t instr_rva = 0x00103050U;
+ Arm32Rel32Translator translator;
+ std::vector<uint8_t> bytes = {0x00, 0x01, 0x02, 0xEA};
+ MutableBufferView region(&bytes[0], bytes.size());
+ uint32_t code = translator.FetchArmCode32(region, 0U);
+ EXPECT_EQ(0xEA020100U, code);
+
+ // |code| <-> |disp|.
+ arm_disp_t disp = 0;
+ EXPECT_EQ(kArmAlign4, translator.DecodeA24(code, &disp));
+ EXPECT_EQ(+0x00080400, disp);
+
+ uint32_t code_from_disp = kCleanSlateBAL_A1;
+ EXPECT_TRUE(translator.EncodeA24(disp, &code_from_disp));
+ EXPECT_EQ(code, code_from_disp);
+
+ // |code| <-> |target_rva|.
+ rva_t target_rva = kInvalidRva;
+ EXPECT_TRUE(translator.ReadA24(instr_rva, code, &target_rva));
+ // 0x00103050 + 8 + 0x00080400.
+ EXPECT_EQ(0x00183458U, target_rva);
+
+ uint32_t code_from_rva = kCleanSlateBAL_A1;
+ EXPECT_TRUE(translator.WriteA24(instr_rva, target_rva, &code_from_rva));
+ EXPECT_EQ(code, code_from_rva);
+ }
+
+ // THUMB2 mode (16-bit).
+ // 001030A2: F3 E7 B 0010308C ; B encoding T2.
+ {
+ rva_t instr_rva = 0x001030A2U;
+ Arm32Rel32Translator translator;
+ std::vector<uint8_t> bytes = {0xF3, 0xE7};
+ MutableBufferView region(&bytes[0], bytes.size());
+ uint16_t code = translator.FetchThumb2Code16(region, 0U);
+ // Sii iiiiiiii = 111 11110011 = -1101 = -0x0D.
+ EXPECT_EQ(0xE7F3U, code);
+
+ // |code| <-> |disp|.
+ arm_disp_t disp = 0;
+ EXPECT_EQ(kArmAlign2, translator.DecodeT11(code, &disp));
+ EXPECT_EQ(-0x0000001A, disp); // -0x0D * 2 = -0x1A.
+
+ uint16_t code_from_disp = kCleanSlateB_T2;
+ EXPECT_TRUE(translator.EncodeT11(disp, &code_from_disp));
+ EXPECT_EQ(code, code_from_disp);
+
+ // |code| <-> |target_rva|.
+ rva_t target_rva = kInvalidRva;
+ EXPECT_TRUE(translator.ReadT11(instr_rva, code, &target_rva));
+ // 0x001030A2 + 4 - 0x0000001A.
+ EXPECT_EQ(0x0010308CU, target_rva);
+
+ uint16_t code_from_rva = kCleanSlateB_T2;
+ EXPECT_TRUE(translator.WriteT11(instr_rva, target_rva, &code_from_rva));
+ EXPECT_EQ(code, code_from_rva);
+ }
+
+ // THUMB2 mode (32-bit).
+ // 001030A2: 00 F0 01 FA BL 001034A8 ; BL encoding T1.
+ {
+ rva_t instr_rva = 0x001030A2U;
+ Arm32Rel32Translator translator;
+ std::vector<uint8_t> bytes = {0x00, 0xF0, 0x01, 0xFA};
+ MutableBufferView region(&bytes[0], bytes.size());
+ uint32_t code = translator.FetchThumb2Code32(region, 0U);
+ EXPECT_EQ(0xF000FA01U, code);
+
+ // |code| <-> |disp|.
+ arm_disp_t disp = 0;
+ EXPECT_EQ(kArmAlign2, translator.DecodeT24(code, &disp));
+ EXPECT_EQ(+0x00000402, disp);
+
+ uint32_t code_from_disp = kCleanSlateBL_T1;
+ EXPECT_TRUE(translator.EncodeT24(disp, &code_from_disp));
+ EXPECT_EQ(code, code_from_disp);
+
+ // |code| <-> |target_rva|.
+ rva_t target_rva = kInvalidRva;
+ EXPECT_TRUE(translator.ReadT24(instr_rva, code, &target_rva));
+ // 0x001030A2 + 4 + 0x00000002.
+ EXPECT_EQ(0x001034A8U, target_rva);
+
+ uint32_t code_from_rva = kCleanSlateBL_T1;
+ EXPECT_TRUE(translator.WriteT24(instr_rva, target_rva, &code_from_rva));
+ EXPECT_EQ(code, code_from_rva);
+ }
+}
+
+TEST(Arm32Rel32Translator, BLXComplication) {
+ auto run_test = [](rva_t instr_rva,
+ std::vector<uint8_t> bytes, // Pass by value.
+ uint32_t expected_code, arm_disp_t expected_disp,
+ uint32_t clean_slate_code, rva_t expected_target_rva) {
+ Arm32Rel32Translator translator;
+ MutableBufferView region(&bytes[0], bytes.size());
+ uint32_t code = translator.FetchThumb2Code32(region, 0U);
+ EXPECT_EQ(expected_code, code);
+
+ // |code| <-> |disp|.
+ arm_disp_t disp = 0;
+ EXPECT_TRUE(translator.DecodeT24(code, &disp));
+ EXPECT_EQ(expected_disp, disp);
+
+ uint32_t code_from_disp = clean_slate_code;
+ EXPECT_TRUE(translator.EncodeT24(disp, &code_from_disp));
+ EXPECT_EQ(code, code_from_disp);
+
+ // |code| <-> |target_rva|.
+ rva_t target_rva = kInvalidRva;
+ EXPECT_TRUE(translator.ReadT24(instr_rva, code, &target_rva));
+ EXPECT_EQ(expected_target_rva, target_rva);
+
+ uint32_t code_from_rva = clean_slate_code;
+ EXPECT_TRUE(translator.WriteT24(instr_rva, target_rva, &code_from_rva));
+ EXPECT_EQ(code, code_from_rva);
+ };
+
+ // No complication, 4-byte aligned.
+ // 001030A0: 01 F0 06 B0 B 005040B0 ; B encoding T4.
+ run_test(0x001030A0U, // Multiple of 4.
+ {0x01, 0xF0, 0x06, 0xB0}, 0xF001B006U, 0x0040100C, kCleanSlateB_T4,
+ // "Canonical" |target_rva|: 0x001030A0 + 4 + 0x0040100C.
+ 0x005040B0U);
+
+ // No complication, not 4-byte aligned.
+ // 001030A2: 01 F0 06 B0 B 005040B2 ; B encoding T4.
+ run_test(0x001030A2U, // Shift by 2: Not multiple of 4.
+ {0x01, 0xF0, 0x06, 0xB0}, 0xF001B006U, 0x0040100C, kCleanSlateB_T4,
+ // Shifted by 2: 0x001030A2 + 4 + 0x0040100C.
+ 0x005040B2U);
+
+ // Repeat the above, but use BLX instead of B.
+
+ // BLX complication, 4-byte aligned.
+ // 001030A0: 01 F0 06 E0 BLX 005040B0 ; BLX encoding T2.
+ run_test(0x001030A0U, // Multiple of 4.
+ {0x01, 0xF0, 0x06, 0xE0}, 0xF001E006U, 0x0040100C, kCleanSlateBLX_T2,
+ // Canonical again: align_down_4(0x001030A0 + 4 + 0x0040100C).
+ 0x005040B0U);
+
+ // BLX complication, not 4-byte aligned.
+ // 001030A2: 01 F0 06 E0 BLX 005040B0 ; BLX encoding T2.
+ run_test(0x001030A2U, // Shift by 2: Not multiple of 4.
+ {0x01, 0xF0, 0x06, 0xE0}, 0xF001E006U, 0x0040100C, kCleanSlateBLX_T2,
+ // No shift: align_down_4(0x001030A2 + 4 + 0x0040100C).
+ 0x005040B0U);
+}
+
+TEST(AArch64Rel32Translator, FetchStore) {
+ std::vector<uint8_t> bytes = {0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE};
+ std::vector<uint8_t> expected = {0xAB, 0x33, 0x22, 0x11,
+ 0x69, 0x5A, 0xFF, 0x00};
+ MutableBufferView region(&bytes[0], bytes.size());
+ AArch64Rel32Translator translator;
+ EXPECT_EQ(0x76543210U, translator.FetchCode32(region, 0U));
+ EXPECT_EQ(0xFEDCBA98U, translator.FetchCode32(region, 4U));
+
+ translator.StoreCode32(region, 0U, 0x112233ABU);
+ translator.StoreCode32(region, 4U, 0x00FF5A69);
+ EXPECT_EQ(expected, bytes);
+}
+
+TEST(AArch64Rel32Translator, EncodeDecode) {
+ // Immd14 tests.
+ ArmTranslatorEncodeDecodeTest<AArch64Rel32Translator::AddrTraits_Immd14>
+ test_immd14;
+ for (int b40 : {0, 1, 7, 31}) {
+ uint32_t b40_mask = b40 << 19;
+ for (int Rt : {0, 1, 15, 30}) {
+ uint32_t mask = b40_mask | Rt;
+ ArmRelInstruction<uint32_t> TBZw_Rt("00110110 bbbbbSii iiiiiiii iiittttt",
+ kCleanSlate64TBZw | mask);
+ ArmRelInstruction<uint32_t> TBZz_Rt("10110110 bbbbbSii iiiiiiii iiittttt",
+ kCleanSlate64TBZz | mask);
+ ArmRelInstruction<uint32_t> TBNZw_Rt(
+ "00110111 bbbbbSii iiiiiiii iiittttt", kCleanSlate64TBNZw | mask);
+ ArmRelInstruction<uint32_t> TBNZz_Rt(
+ "10110111 bbbbbSii iiiiiiii iiittttt", kCleanSlate64TBNZz | mask);
+ test_immd14.Run("SSSSSSSS SSSSSSSS Siiiiiii iiiiii00", {"S", "i"},
+ {TBZw_Rt, TBZz_Rt, TBNZw_Rt, TBNZz_Rt},
+ {0x00007FFC, -0x00008000, 0, 4, -4, 0x40, 0x44},
+ {2, -2, 0x41, 0x42, 0x43, 0x00008000, -0x00008004});
+ }
+ }
+
+ // Immd19 tests.
+ ArmTranslatorEncodeDecodeTest<AArch64Rel32Translator::AddrTraits_Immd19>
+ test_immd19;
+ for (int cond = 0; cond <= 0x0E; ++cond) {
+ ArmRelInstruction<uint32_t> B_cond("01010100 Siiiiiii iiiiiiii iii0cccc",
+ kCleanSlate64Bcond | cond);
+ test_immd19.Run("SSSSSSSS SSSSiiii iiiiiiii iiiiii00", {"S", "i"}, {B_cond},
+ {0x000FFFFC, -0x00100000, 0, 4, -4, 0x40, 0x44},
+ {2, -2, 0x41, 0x42, 0x43, 0x00100000, -0x00100004});
+ }
+ for (int Rt : {0, 1, 15, 30}) {
+ ArmRelInstruction<uint32_t> CBZw_Rt("00110100 Siiiiiii iiiiiiii iiittttt",
+ kCleanSlate64CBZw | Rt);
+ ArmRelInstruction<uint32_t> CBZz_Rt("10110100 Siiiiiii iiiiiiii iiittttt",
+ kCleanSlate64CBZz | Rt);
+ ArmRelInstruction<uint32_t> CBNZw_Rt("00110101 Siiiiiii iiiiiiii iiittttt",
+ kCleanSlate64CBNZw | Rt);
+ ArmRelInstruction<uint32_t> CBNZz_Rt("10110101 Siiiiiii iiiiiiii iiittttt",
+ kCleanSlate64CBNZz | Rt);
+ test_immd19.Run("SSSSSSSS SSSSiiii iiiiiiii iiiiii00", {"S", "i"},
+ {CBZw_Rt, CBZz_Rt, CBNZw_Rt, CBNZz_Rt},
+ {0x000FFFFC, -0x00100000, 0, 4, -4, 0x40, 0x44},
+ {2, -2, 0x41, 0x42, 0x43, 0x00100000, -0x00100004});
+ }
+
+ // Immd26 tests.
+ ArmTranslatorEncodeDecodeTest<AArch64Rel32Translator::AddrTraits_Immd26>
+ test_immd26;
+ ArmRelInstruction<uint32_t> B("000101Si iiiiiiii iiiiiiii iiiiiiii",
+ kCleanSlate64B);
+ ArmRelInstruction<uint32_t> BL("100101Si iiiiiiii iiiiiiii iiiiiiii",
+ kCleanSlate64BL);
+ test_immd26.Run("SSSSSiii iiiiiiii iiiiiiii iiiiii00", {"S", "i"}, {B, BL},
+ {0x07FFFFFC, -0x08000000, 0, 4, -4, 0x40, 0x44},
+ {2, -2, 0x41, 0x42, 0x43, 0x08000000, -0x08000004});
+}
+
+TEST(AArch64Rel32Translator, WriteRead) {
+ // TODO(huangs): Implement.
+}
+
+// Typical usage in |target_rva| extraction.
+TEST(AArch64Rel32Translator, Main) {
+ // 00103050: 02 01 02 14 B 00183458
+ rva_t instr_rva = 0x00103050U;
+ AArch64Rel32Translator translator;
+ std::vector<uint8_t> bytes = {0x02, 0x01, 0x02, 0x14};
+ MutableBufferView region(&bytes[0], bytes.size());
+ uint32_t code = translator.FetchCode32(region, 0U);
+ EXPECT_EQ(0x14020102U, code);
+
+ // |code| <-> |disp|.
+ arm_disp_t disp = 0;
+ EXPECT_TRUE(translator.DecodeImmd26(code, &disp));
+ EXPECT_EQ(+0x00080408, disp);
+
+ uint32_t code_from_disp = kCleanSlate64B;
+ EXPECT_TRUE(translator.EncodeImmd26(disp, &code_from_disp));
+ EXPECT_EQ(code, code_from_disp);
+
+ // |code| <-> |target_rva|.
+ rva_t target_rva = kInvalidRva;
+ EXPECT_TRUE(translator.ReadImmd26(instr_rva, code, &target_rva));
+ // 0x00103050 + 0 + 0x00080408.
+ EXPECT_EQ(0x00183458U, target_rva);
+
+ uint32_t code_from_rva = kCleanSlate64B;
+ EXPECT_TRUE(translator.WriteImmd26(instr_rva, target_rva, &code_from_rva));
+ EXPECT_EQ(code, code_from_rva);
+}
+
+} // namespace zucchini
diff --git a/reference_bytes_mixer.cc b/reference_bytes_mixer.cc
index c0d5ca3..a073a6a 100644
--- a/reference_bytes_mixer.cc
+++ b/reference_bytes_mixer.cc
@@ -35,12 +35,11 @@ int ReferenceBytesMixer::NumBytes(uint8_t type) const {
}
// Base class implementation is a stub that should not be called.
-ConstBufferView ReferenceBytesMixer::Mix(
- uint8_t type,
- ConstBufferView::const_iterator old_base,
- offset_t old_offset,
- ConstBufferView::const_iterator new_base,
- offset_t new_offset) {
+ConstBufferView ReferenceBytesMixer::Mix(uint8_t type,
+ ConstBufferView old_view,
+ offset_t old_offset,
+ ConstBufferView new_view,
+ offset_t new_offset) {
NOTREACHED() << "Stub.";
return ConstBufferView();
}
diff --git a/reference_bytes_mixer.h b/reference_bytes_mixer.h
index 9bc8f2d..f8e351e 100644
--- a/reference_bytes_mixer.h
+++ b/reference_bytes_mixer.h
@@ -72,14 +72,14 @@ class ReferenceBytesMixer {
virtual int NumBytes(uint8_t type) const;
// Computes mixed reference bytes by combining (a) "payload bits" from an
- // "old" reference of |type| at |old_base[old_offset]| with (b) "operation
- // bits" from a "new" reference of |type| at |new_base[new_offset]|. Returns
+ // "old" reference of |type| at |old_view[old_offset]| with (b) "operation
+ // bits" from a "new" reference of |type| at |new_view[new_offset]|. Returns
// the result as ConstBufferView, which is valid only until the next call to
// Mix().
virtual ConstBufferView Mix(uint8_t type,
- ConstBufferView::const_iterator old_base,
+ ConstBufferView old_view,
offset_t old_offset,
- ConstBufferView::const_iterator new_base,
+ ConstBufferView new_view,
offset_t new_offset);
private:
diff --git a/zucchini_gen.cc b/zucchini_gen.cc
index ac7e33b..3735d0f 100644
--- a/zucchini_gen.cc
+++ b/zucchini_gen.cc
@@ -147,8 +147,8 @@ bool GenerateRawDelta(ConstBufferView old_image,
int num_bytes = reference_bytes_mixer->NumBytes(type_tag.value());
if (num_bytes) {
ConstBufferView mixed_ref_bytes = reference_bytes_mixer->Mix(
- type_tag.value(), old_image.begin(), equivalence.src_offset + i,
- new_image.begin(), equivalence.dst_offset + i);
+ type_tag.value(), old_image, equivalence.src_offset + i,
+ new_image, equivalence.dst_offset + i);
for (int j = 0; j < num_bytes; ++j) {
int8_t diff =
mixed_ref_bytes[j] - old_image[equivalence.src_offset + i + j];