diff options
author | Samuel Huang <huangs@chromium.org> | 2019-02-08 22:15:36 +0000 |
---|---|---|
committer | Copybara-Service <copybara-worker@google.com> | 2021-07-25 20:51:23 -0700 |
commit | 8afcc01dc2db45c4c4b3fe8839f194fafb581298 (patch) | |
tree | a40ae1102fe67dd57e4a661ffcf5f3afd224a3e7 /rel32_utils_unittest.cc | |
parent | b4168a2dd2d50920b228b69db09051e06cf990b1 (diff) | |
download | zucchini-8afcc01dc2db45c4c4b3fe8839f194fafb581298.tar.gz |
[Zucchini] ARM code: Add templated helpers to process ARM references.
Details:
* Add 2 classes and 1 helper function to rel32_utils.h.
* Rel32ReaderArm: ReferenceReader for ARM.
* Rel32WriterArm: ReferenceWriter for ARM.
* ArmCopyDisp(): Copies displacement data from one ARM instruction to
another (of the same type) to make them more similar. This will be
used for ReferenceBytesMixer.
* These funtions take ADDR_TRAITS template argument, which matches
ArmAddrTraits in arm_utils.h.
* Add extensive tests.
Bug: 918867
Change-Id: I7e7f8bee8cb2a8b49f7fd0e7115b39c49f576398
Reviewed-on: https://chromium-review.googlesource.com/c/1452780
Commit-Queue: Samuel Huang <huangs@chromium.org>
Reviewed-by: Etienne Pierre-Doray <etiennep@chromium.org>
Cr-Commit-Position: refs/heads/master@{#630488}
NOKEYCHECK=True
GitOrigin-RevId: 2975bfdf1c26e0df03a8821b979dd639cbbaf601
Diffstat (limited to 'rel32_utils_unittest.cc')
-rw-r--r-- | rel32_utils_unittest.cc | 435 |
1 files changed, 425 insertions, 10 deletions
diff --git a/rel32_utils_unittest.cc b/rel32_utils_unittest.cc index 29e8560..e3d34f4 100644 --- a/rel32_utils_unittest.cc +++ b/rel32_utils_unittest.cc @@ -7,11 +7,13 @@ #include <stdint.h> #include <memory> +#include <utility> #include <vector> #include "base/optional.h" #include "base/test/gtest_util.h" #include "components/zucchini/address_translator.h" +#include "components/zucchini/arm_utils.h" #include "components/zucchini/image_utils.h" #include "testing/gtest/include/gtest/gtest.h" @@ -31,7 +33,7 @@ class TestAddressTranslator : public AddressTranslator { // Checks that |reader| emits and only emits |expected_refs|, in order. void CheckReader(const std::vector<Reference>& expected_refs, - ReferenceReader* reader) { + std::unique_ptr<ReferenceReader> reader) { for (Reference expected_ref : expected_refs) { auto ref = reader->GetNext(); EXPECT_TRUE(ref.has_value()); @@ -40,6 +42,31 @@ void CheckReader(const std::vector<Reference>& expected_refs, EXPECT_EQ(base::nullopt, reader->GetNext()); // Nothing should be left. } +// Copies displacements from |bytes1| to |bytes2| and checks results against +// |bytes_exp_1_to_2|. Then repeats for |*bytes2| , |*byte1|, and +// |bytes_exp_2_to_1|. Empty expected bytes mean failure is expected. The copy +// function is specified by |copier|. +void CheckCopy(const std::vector<uint8_t>& bytes_exp_1_to_2, + const std::vector<uint8_t>& bytes_exp_2_to_1, + const std::vector<uint8_t>& bytes1, + const std::vector<uint8_t>& bytes2, + ArmCopyDispFun copier) { + auto run_test = [&copier](const std::vector<uint8_t>& bytes_exp, + const std::vector<uint8_t>& bytes_in, + std::vector<uint8_t> bytes_out) { + ConstBufferView buffer_in(&bytes_in[0], bytes_in.size()); + MutableBufferView buffer_out(&bytes_out[0], bytes_out.size()); + if (bytes_exp.empty()) { + EXPECT_FALSE(copier(buffer_in, 0U, buffer_out, 0U)); + } else { + EXPECT_TRUE(copier(buffer_in, 0U, buffer_out, 0U)); + EXPECT_EQ(bytes_exp, bytes_out); + } + }; + run_test(bytes_exp_1_to_2, bytes1, bytes2); + run_test(bytes_exp_2_to_1, bytes2, bytes1); +} + } // namespace TEST(Rel32UtilsTest, Rel32ReaderX86) { @@ -64,24 +91,24 @@ TEST(Rel32UtilsTest, Rel32ReaderX86) { std::vector<offset_t> rel32_locations = {0x0008U, 0x0010U, 0x0018U, 0x001CU}; // Generate everything. - Rel32ReaderX86 reader1(buffer, 0x0000U, 0x0020U, &rel32_locations, - translator); + auto reader1 = std::make_unique<Rel32ReaderX86>(buffer, 0x0000U, 0x0020U, + &rel32_locations, translator); CheckReader({{0x0008U, 0x0010U}, {0x0010U, 0x0014U}, {0x0018U, 0x0010U}, {0x001CU, 0x0004U}}, - &reader1); + std::move(reader1)); // Exclude last. - Rel32ReaderX86 reader2(buffer, 0x0000U, 0x001CU, &rel32_locations, - translator); + auto reader2 = std::make_unique<Rel32ReaderX86>(buffer, 0x0000U, 0x001CU, + &rel32_locations, translator); CheckReader({{0x0008U, 0x0010U}, {0x0010U, 0x0014U}, {0x0018U, 0x0010U}}, - &reader2); + std::move(reader2)); // Only find one. - Rel32ReaderX86 reader3(buffer, 0x000CU, 0x0018U, &rel32_locations, - translator); - CheckReader({{0x0010U, 0x0014U}}, &reader3); + auto reader3 = std::make_unique<Rel32ReaderX86>(buffer, 0x000CU, 0x0018U, + &rel32_locations, translator); + CheckReader({{0x0010U, 0x0014U}}, std::move(reader3)); } TEST(Rel32UtilsTest, Rel32WriterX86) { @@ -118,4 +145,392 @@ TEST(Rel32UtilsTest, Rel32WriterX86) { bytes); } +TEST(Rel32UtilsTest, Rel32ReaderArm_Arm32) { + constexpr offset_t kTestImageSize = 0x00100000U; + constexpr rva_t kRvaBegin = 0x00030000U; + TestAddressTranslator translator(kTestImageSize, kRvaBegin); + + // A24. + std::vector<uint8_t> bytes = { + 0xFF, 0xFF, 0xFF, 0xFF, // 00030000: (Filler) + 0xFF, 0xFF, 0xFF, 0xFF, // 00030004: (Filler) + 0x00, 0x00, 0x00, 0xEA, // 00030008: B 00030010 ; A24 + 0xFF, 0xFF, 0xFF, 0xFF, // 0003000C: (Filler) + 0xFF, 0xFF, 0xFF, 0xEB, // 00030010: BL 00030014 ; A24 + 0xFF, 0xFF, 0xFF, 0xFF, // 00030014: (Filler) + 0xFC, 0xFF, 0xFF, 0xEB, // 00030018: BL 00030010 ; A24 + 0xF8, 0xFF, 0xFF, 0xEA, // 0003001C: B 00030004 ; A24 + }; + ConstBufferView region(&bytes[0], bytes.size()); + // Specify rel32 locations directly, instead of parsing. + std::vector<offset_t> rel32_locations_A24 = {0x0008U, 0x0010U, 0x0018U, + 0x001CU}; + + // Generate everything. + auto reader1 = + std::make_unique<Rel32ReaderArm<Arm32Rel32Translator::AddrTraits_A24>>( + translator, region, rel32_locations_A24, 0x0000U, 0x0020U); + CheckReader({{0x0008U, 0x0010U}, + {0x0010U, 0x0014U}, + {0x0018U, 0x0010U}, + {0x001CU, 0x0004U}}, + std::move(reader1)); + + // Exclude last. + auto reader2 = + std::make_unique<Rel32ReaderArm<Arm32Rel32Translator::AddrTraits_A24>>( + translator, region, rel32_locations_A24, 0x0000U, 0x001CU); + CheckReader({{0x0008U, 0x0010U}, {0x0010U, 0x0014U}, {0x0018U, 0x0010U}}, + std::move(reader2)); + + // Only find one. + auto reader3 = + std::make_unique<Rel32ReaderArm<Arm32Rel32Translator::AddrTraits_A24>>( + translator, region, rel32_locations_A24, 0x000CU, 0x0018U); + CheckReader({{0x0010U, 0x0014U}}, std::move(reader3)); +} + +TEST(Rel32UtilsTest, Rel32WriterArm_Arm32_Easy) { + constexpr offset_t kTestImageSize = 0x00100000U; + constexpr rva_t kRvaBegin = 0x00030000U; + TestAddressTranslator translator(kTestImageSize, kRvaBegin); + + std::vector<uint8_t> bytes = { + 0xFF, 0xFF, // 00030000: (Filler) + 0x01, 0xDE, // 00030002: B 00030008 ; T8 + 0xFF, 0xFF, 0xFF, 0xFF, // 00030004: (Filler) + 0x01, 0xE0, // 00030008: B 0003000E ; T11 + 0xFF, 0xFF, // 0003000A: (Filler) + 0x80, 0xF3, 0x00, 0x80, // 0003000C: B 00030010 ; T20 + }; + MutableBufferView region(&bytes[0], bytes.size()); + + auto writer1 = + std::make_unique<Rel32WriterArm<Arm32Rel32Translator::AddrTraits_T8>>( + translator, region); + writer1->PutNext({0x0002U, 0x0004U}); + EXPECT_EQ(0xFF, bytes[0x02]); // 00030002: B 00030004 ; T8 + EXPECT_EQ(0xDE, bytes[0x03]); + + writer1->PutNext({0x0002U, 0x000AU}); + EXPECT_EQ(0x02, bytes[0x02]); // 00030002: B 0003000A ; T8 + EXPECT_EQ(0xDE, bytes[0x03]); + + auto writer2 = + std::make_unique<Rel32WriterArm<Arm32Rel32Translator::AddrTraits_T11>>( + translator, region); + writer2->PutNext({0x0008U, 0x0008U}); + EXPECT_EQ(0xFE, bytes[0x08]); // 00030008: B 00030008 ; T11 + EXPECT_EQ(0xE7, bytes[0x09]); + writer2->PutNext({0x0008U, 0x0010U}); + EXPECT_EQ(0x02, bytes[0x08]); // 00030008: B 00030010 ; T11 + EXPECT_EQ(0xE0, bytes[0x09]); + + auto writer3 = + std::make_unique<Rel32WriterArm<Arm32Rel32Translator::AddrTraits_T20>>( + translator, region); + writer3->PutNext({0x000CU, 0x000AU}); + EXPECT_EQ(0xBF, bytes[0x0C]); // 0003000C: B 0003000A ; T20 + EXPECT_EQ(0xF7, bytes[0x0D]); + EXPECT_EQ(0xFD, bytes[0x0E]); + EXPECT_EQ(0xAF, bytes[0x0F]); + writer3->PutNext({0x000CU, 0x0010U}); + EXPECT_EQ(0x80, bytes[0x0C]); // 0003000C: B 00030010 ; T20 + EXPECT_EQ(0xF3, bytes[0x0D]); + EXPECT_EQ(0x00, bytes[0x0E]); + EXPECT_EQ(0x80, bytes[0x0F]); +} + +TEST(Rel32UtilsTest, Rel32WriterArm_Arm32_Hard) { + constexpr offset_t kTestImageSize = 0x10000000U; + constexpr rva_t kRvaBegin = 0x0C030000U; + TestAddressTranslator translator(kTestImageSize, kRvaBegin); + + std::vector<uint8_t> bytes = { + 0xFF, 0xFF, // 0C030000: (Filler) + 0x00, 0xF0, 0x00, 0xB8, // 0C030002: B 0C030006 ; T24 + 0xFF, 0xFF, 0xFF, 0xFF, // 0C030006: (Filler) + 0x00, 0xF0, 0x7A, 0xE8, // 0C03000A: BLX 0C030100 ; T24 + 0xFF, 0xFF, // 0C03000E: (Filler) + 0x00, 0xF0, 0x7A, 0xE8, // 0C030010: BLX 0C030108 ; T24 + }; + MutableBufferView region(&bytes[0], bytes.size()); + + auto writer = + std::make_unique<Rel32WriterArm<Arm32Rel32Translator::AddrTraits_T24>>( + translator, region); + writer->PutNext({0x0002U, 0x0000U}); + EXPECT_EQ(0xFF, bytes[0x02]); // 0C030002: B 0C030000 ; T24 + EXPECT_EQ(0xF7, bytes[0x03]); + EXPECT_EQ(0xFD, bytes[0x04]); + EXPECT_EQ(0xBF, bytes[0x05]); + writer->PutNext({0x0002U, 0x0008U}); + EXPECT_EQ(0x00, bytes[0x02]); // 0C030002: B 0C030008 ; T24 + EXPECT_EQ(0xF0, bytes[0x03]); + EXPECT_EQ(0x01, bytes[0x04]); + EXPECT_EQ(0xB8, bytes[0x05]); + + // BLX complication, with location that's not 4-byte aligned. + writer->PutNext({0x000AU, 0x0010U}); + EXPECT_EQ(0x00, bytes[0x0A]); // 0C03000A: BLX 0C030010 ; T24 + EXPECT_EQ(0xF0, bytes[0x0B]); + EXPECT_EQ(0x02, bytes[0x0C]); + EXPECT_EQ(0xE8, bytes[0x0D]); + writer->PutNext({0x000AU, 0x0100U}); + EXPECT_EQ(0x00, bytes[0x0A]); // 0C03000A: BLX 0C030100 ; T24 + EXPECT_EQ(0xF0, bytes[0x0B]); + EXPECT_EQ(0x7A, bytes[0x0C]); + EXPECT_EQ(0xE8, bytes[0x0D]); + writer->PutNext({0x000AU, 0x0000U}); + EXPECT_EQ(0xFF, bytes[0x0A]); // 0C03000A: BLX 0C030000 ; T24 + EXPECT_EQ(0xF7, bytes[0x0B]); + EXPECT_EQ(0xFA, bytes[0x0C]); + EXPECT_EQ(0xEF, bytes[0x0D]); + + // BLX complication, with location that's 4-byte aligned. + writer->PutNext({0x0010U, 0x0010U}); + EXPECT_EQ(0xFF, bytes[0x10]); // 0C030010: BLX 0C030010 ; T24 + EXPECT_EQ(0xF7, bytes[0x11]); + EXPECT_EQ(0xFE, bytes[0x12]); + EXPECT_EQ(0xEF, bytes[0x13]); + writer->PutNext({0x0010U, 0x0108U}); + EXPECT_EQ(0x00, bytes[0x10]); // 0C030010: BLX 0C030108 ; T24 + EXPECT_EQ(0xF0, bytes[0x11]); + EXPECT_EQ(0x7A, bytes[0x12]); + EXPECT_EQ(0xE8, bytes[0x13]); +} + +// Test BLX encoding A2, which is an ARM instruction that switches to THUMB2, +// and therefore should have 2-byte alignment. +TEST(Rel32UtilsTest, Arm32SwitchToThumb2) { + constexpr offset_t kTestImageSize = 0x10000000U; + constexpr rva_t kRvaBegin = 0x08030000U; + TestAddressTranslator translator(kTestImageSize, kRvaBegin); + + std::vector<uint8_t> bytes = { + 0xFF, 0xFF, 0x00, 0x00, // 08030000: (Filler) + 0x00, 0x00, 0x00, 0xFA, // 08030004: BLX 0803000C ; A24 + }; + MutableBufferView region(&bytes[0], bytes.size()); + + auto writer = + std::make_unique<Rel32WriterArm<Arm32Rel32Translator::AddrTraits_A24>>( + translator, region); + + // To location that's 4-byte aligned. + writer->PutNext({0x0004U, 0x0100U}); + EXPECT_EQ(0x3D, bytes[0x04]); // 08030004: BLX 08030100 ; A24 + EXPECT_EQ(0x00, bytes[0x05]); + EXPECT_EQ(0x00, bytes[0x06]); + EXPECT_EQ(0xFA, bytes[0x07]); + + // To location that's 2-byte aligned but not 4-byte aligned. + writer->PutNext({0x0004U, 0x0052U}); + EXPECT_EQ(0x11, bytes[0x04]); // 08030004: BLX 08030052 ; A24 + EXPECT_EQ(0x00, bytes[0x05]); + EXPECT_EQ(0x00, bytes[0x06]); + EXPECT_EQ(0xFB, bytes[0x07]); + + // Clean slate code. + writer->PutNext({0x0004U, 0x000CU}); + EXPECT_EQ(0x00, bytes[0x04]); // 08030004: BLX 0803000C ; A24 + EXPECT_EQ(0x00, bytes[0x05]); + EXPECT_EQ(0x00, bytes[0x06]); + EXPECT_EQ(0xFA, bytes[0x07]); +} + +TEST(Rel32UtilsTest, ArmCopyDisp_Arm32) { + std::vector<uint8_t> expect_fail; + + // Successful A24. + ArmCopyDispFun copier_A24 = ArmCopyDisp<Arm32Rel32Translator::AddrTraits_A24>; + CheckCopy({0x12, 0x34, 0x56, 0xEB}, // 00000100: BL 0158D150 + {0xA0, 0xC0, 0x0E, 0x2A}, // 00000100: BCS 003B0388 + {0x12, 0x34, 0x56, 0x2A}, // 00000100: BCS 0158D150 + {0xA0, 0xC0, 0x0E, 0xEB}, // 00000100: BL 003B0388 + copier_A24); + + // Successful T8. + ArmCopyDispFun copier_T8 = ArmCopyDisp<Arm32Rel32Translator::AddrTraits_T8>; + CheckCopy({0x12, 0xD5}, // 00000100: BPL 00000128 + {0xAB, 0xD8}, // 00000100: BHI 0000005A + {0x12, 0xD8}, // 00000100: BHI 00000128 + {0xAB, 0xD5}, // 00000100: BPL 0000005A + copier_T8); + + // Successful T11. + ArmCopyDispFun copier_T11 = ArmCopyDisp<Arm32Rel32Translator::AddrTraits_T11>; + CheckCopy({0xF5, 0xE0}, // 00000100: B 000002EE + {0x12, 0xE7}, // 00000100: B FFFFFF28 + {0xF5, 0xE0}, // 00000100: B 000002EE + {0x12, 0xE7}, // 00000100: B FFFFFF28 + copier_T11); + + // Failure if wrong copier is used. + CheckCopy(expect_fail, expect_fail, {0xF5, 0xE0}, {0x12, 0xE7}, copier_T8); + + // Successful T20. + ArmCopyDispFun copier_T20 = ArmCopyDisp<Arm32Rel32Translator::AddrTraits_T20>; + CheckCopy({0x41, 0xF2, 0xA5, 0x88}, // 00000100: BLS.W 0008124E + {0x04, 0xF3, 0x3C, 0xA2}, // 00000100: BGT.W 0004457C + {0x01, 0xF3, 0xA5, 0x88}, // 00000100: BGT.W 0008124E + {0x44, 0xF2, 0x3C, 0xA2}, // 00000100: BLS.W 0004457C + copier_T20); + CheckCopy({0x7F, 0xF6, 0xFF, 0xAF}, // 00000100: BLS.W 00000102 + {0x00, 0xF3, 0x00, 0x80}, // 00000100: BGT.W 00000104 + {0x3F, 0xF7, 0xFF, 0xAF}, // 00000100: BGT.W 00000102 + {0x40, 0xF2, 0x00, 0x80}, // 00000100: BLS.W 00000104 + copier_T20); + + // Failure if wrong copier is used. + CheckCopy(expect_fail, expect_fail, {0x41, 0xF2, 0xA5, 0x88}, + {0x84, 0xF3, 0x3C, 0xA2}, copier_A24); + + // T24: Mix B encoding T4 and BL encoding T1. + ArmCopyDispFun copier_T24 = ArmCopyDisp<Arm32Rel32Translator::AddrTraits_T24>; + CheckCopy({0xFF, 0xF7, 0xFF, 0xFF}, // 00000100: BL 00000102 + {0x00, 0xF0, 0x00, 0x90}, // 00000100: B.W 00C00104 + {0xFF, 0xF7, 0xFF, 0xBF}, // 00000100: B.W 00000102 + {0x00, 0xF0, 0x00, 0xD0}, // 00000100: BL 00C00104 + copier_T24); + + // Mix B encoding T4 and BLX encoding T2. Note that the forward direction + // fails because B's target is invalid for BLX! It's possible to do "best + // effort" copying to reduce diff -- but right now we're not doing this. + CheckCopy(expect_fail, {0x00, 0xF0, 0x00, 0x90}, // 00000100: B.W 00C00104 + {0xFF, 0xF7, 0xFF, 0xBF}, // 00000100: B.W 00000102 + {0x00, 0xF0, 0x00, 0xC0}, // 00000100: BLX 00C00104 + copier_T24); + // Success if ow B's target is valid for BLX. + CheckCopy({0xFF, 0xF7, 0xFE, 0xEF}, // 00000100: BLX 00000100 + {0x00, 0xF0, 0x00, 0x90}, // 00000100: B.W 00C00104 + {0xFF, 0xF7, 0xFE, 0xBF}, // 00000100: B.W 00000100 + {0x00, 0xF0, 0x00, 0xC0}, // 00000100: BLX 00C00104 + copier_T24); +} + +TEST(Rel32UtilsTest, Rel32ReaderArm_AArch64) { + constexpr offset_t kTestImageSize = 0x00100000U; + constexpr rva_t kRvaBegin = 0x00030000U; + TestAddressTranslator translator(kTestImageSize, kRvaBegin); + + std::vector<uint8_t> bytes = { + 0xFF, 0xFF, 0xFF, 0xFF, // 00030000: (Filler) + 0xFF, 0xFF, 0xFF, 0xFF, // 00030004: (Filler) + 0x02, 0x00, 0x00, 0x14, // 00030008: B 00030010 ; Immd26 + 0xFF, 0xFF, 0xFF, 0xFF, // 0003000C: (Filler) + 0x25, 0x00, 0x00, 0x35, // 00030010: CBNZ R5,00030014 ; Immd19 + 0xFF, 0xFF, 0xFF, 0xFF, // 00030014: (Filler) + 0xCA, 0xFF, 0xFF, 0x54, // 00030018: BGE 00030010 ; Immd19 + 0x4C, 0xFF, 0x8F, 0x36, // 0003001C: TBZ X12,#17,00030004 ; Immd14 + }; + MutableBufferView region(&bytes[0], bytes.size()); + + // Generate Immd26. We specify rel32 locations directly. + std::vector<offset_t> rel32_locations_Immd26 = {0x0008U}; + auto reader1 = std::make_unique< + Rel32ReaderArm<AArch64Rel32Translator::AddrTraits_Immd26>>( + translator, region, rel32_locations_Immd26, 0x0000U, 0x0020U); + CheckReader({{0x0008U, 0x0010U}}, std::move(reader1)); + + // Generate Immd19. + std::vector<offset_t> rel32_locations_Immd19 = {0x0010U, 0x0018U}; + auto reader2 = std::make_unique< + Rel32ReaderArm<AArch64Rel32Translator::AddrTraits_Immd19>>( + translator, region, rel32_locations_Immd19, 0x0000U, 0x0020U); + CheckReader({{0x0010U, 0x0014U}, {0x0018U, 0x0010U}}, std::move(reader2)); + + // Generate Immd14. + std::vector<offset_t> rel32_locations_Immd14 = {0x001CU}; + auto reader3 = std::make_unique< + Rel32ReaderArm<AArch64Rel32Translator::AddrTraits_Immd14>>( + translator, region, rel32_locations_Immd14, 0x0000U, 0x0020U); + CheckReader({{0x001CU, 0x0004U}}, std::move(reader3)); +} + +TEST(Rel32UtilsTest, Rel32WriterArm_AArch64) { + constexpr offset_t kTestImageSize = 0x00100000U; + constexpr rva_t kRvaBegin = 0x00030000U; + TestAddressTranslator translator(kTestImageSize, kRvaBegin); + + std::vector<uint8_t> bytes = { + 0xFF, 0xFF, 0xFF, 0xFF, // 00030000: (Filler) + 0xFF, 0xFF, 0xFF, 0xFF, // 00030004: (Filler) + 0x02, 0x00, 0x00, 0x14, // 00030008: B 00030010 ; Immd26 + 0xFF, 0xFF, 0xFF, 0xFF, // 0003000C: (Filler) + 0x25, 0x00, 0x00, 0x35, // 00030010: CBNZ R5,00030014 ; Immd19 + 0xFF, 0xFF, 0xFF, 0xFF, // 00030014: (Filler) + 0xCA, 0xFF, 0xFF, 0x54, // 00030018: BGE 00030010 ; Immd19 + 0x4C, 0xFF, 0x8F, 0x36, // 0003001C: TBZ X12,#17,00030004 ; Immd14 + }; + MutableBufferView region(&bytes[0], bytes.size()); + + auto writer1 = std::make_unique< + Rel32WriterArm<AArch64Rel32Translator::AddrTraits_Immd26>>(translator, + region); + writer1->PutNext({0x0008U, 0x0000U}); + EXPECT_EQ(0xFE, bytes[0x08]); // 00030008: B 00030000 ; Immd26 + EXPECT_EQ(0xFF, bytes[0x09]); + EXPECT_EQ(0xFF, bytes[0x0A]); + EXPECT_EQ(0x17, bytes[0x0B]); + + auto writer2 = std::make_unique< + Rel32WriterArm<AArch64Rel32Translator::AddrTraits_Immd19>>(translator, + region); + writer2->PutNext({0x0010U, 0x0000U}); + EXPECT_EQ(0x85, bytes[0x10]); // 00030010: CBNZ R5,00030000 ; Immd19 + EXPECT_EQ(0xFF, bytes[0x11]); + EXPECT_EQ(0xFF, bytes[0x12]); + EXPECT_EQ(0x35, bytes[0x13]); + writer2->PutNext({0x0018U, 0x001CU}); + EXPECT_EQ(0x2A, bytes[0x18]); // 00030018: BGE 0003001C ; Immd19 + EXPECT_EQ(0x00, bytes[0x19]); + EXPECT_EQ(0x00, bytes[0x1A]); + EXPECT_EQ(0x54, bytes[0x1B]); + + auto writer3 = std::make_unique< + Rel32WriterArm<AArch64Rel32Translator::AddrTraits_Immd14>>(translator, + region); + writer3->PutNext({0x001CU, 0x0010U}); + EXPECT_EQ(0xAC, bytes[0x1C]); // 0003001C: TBZ X12,#17,00030010 ; Immd14 + EXPECT_EQ(0xFF, bytes[0x1D]); + EXPECT_EQ(0x8F, bytes[0x1E]); + EXPECT_EQ(0x36, bytes[0x1F]); +} + +TEST(Rel32UtilsTest, ArmCopyDisp_AArch64) { + std::vector<uint8_t> expect_fail; + + // Successful Imm26. + ArmCopyDispFun copier_Immd26 = + ArmCopyDisp<AArch64Rel32Translator::AddrTraits_Immd26>; + CheckCopy({0x12, 0x34, 0x56, 0x94}, // 00000100: BL 0158D148 + {0xA1, 0xC0, 0x0E, 0x17}, // 00000100: B FC3B0384 + {0x12, 0x34, 0x56, 0x14}, // 00000100: B 0158D148 + {0xA1, 0xC0, 0x0E, 0x97}, // 00000100: BL FC3B0384 + copier_Immd26); + + // Successful Imm19. + ArmCopyDispFun copier_Immd19 = + ArmCopyDisp<AArch64Rel32Translator::AddrTraits_Immd19>; + CheckCopy({0x24, 0x12, 0x34, 0x54}, // 00000100: BMI 00068344 + {0xD7, 0xA5, 0xFC, 0xB4}, // 00000100: CBZ X23,FFFF95B8 + {0x37, 0x12, 0x34, 0xB4}, // 00000100: CBZ X23,00068344 + {0xC4, 0xA5, 0xFC, 0x54}, // 00000100: BMI FFFF95B8 + copier_Immd19); + + // Successful Imm14. + ArmCopyDispFun copier_Immd14 = + ArmCopyDisp<AArch64Rel32Translator::AddrTraits_Immd14>; + CheckCopy({0x00, 0x00, 0x00, 0x36}, // 00000100: TBZ X0,#0,00000100 + {0xFF, 0xFF, 0xFF, 0xB7}, // 00000100: TBNZ ZR,#63,000000FC + {0x1F, 0x00, 0xF8, 0xB7}, // 00000100: TBNZ ZR,#63,00000100 + {0xE0, 0xFF, 0x07, 0x36}, // 00000100: TBZ X0,#0,000000FC + copier_Immd14); + + // Failure if wrong copier is used. + CheckCopy(expect_fail, expect_fail, {0x1F, 0x00, 0xF8, 0xB7}, + {0xE0, 0xFF, 0x07, 0x36}, copier_Immd26); +} + } // namespace zucchini |