| // Copyright 2017 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/rel32_utils.h" |
| |
| #include <stdint.h> |
| |
| #include <deque> |
| #include <memory> |
| #include <utility> |
| #include <vector> |
| |
| #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" |
| #include "third_party/abseil-cpp/absl/types/optional.h" |
| |
| namespace zucchini { |
| |
| namespace { |
| |
| // A trivial AddressTranslator that applies constant shift. |
| class TestAddressTranslator : public AddressTranslator { |
| public: |
| TestAddressTranslator(offset_t image_size, rva_t rva_begin) { |
| DCHECK_GE(rva_begin, 0U); |
| CHECK_EQ(AddressTranslator::kSuccess, |
| Initialize({{0, image_size, rva_begin, image_size}})); |
| } |
| }; |
| |
| // Checks that |reader| emits and only emits |expected_refs|, in order. |
| void CheckReader(const std::vector<Reference>& expected_refs, |
| std::unique_ptr<ReferenceReader> reader) { |
| for (Reference expected_ref : expected_refs) { |
| auto ref = reader->GetNext(); |
| EXPECT_TRUE(ref.has_value()); |
| EXPECT_EQ(expected_ref, ref.value()); |
| } |
| EXPECT_EQ(absl::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) { |
| constexpr offset_t kTestImageSize = 0x00100000U; |
| constexpr rva_t kRvaBegin = 0x00030000U; |
| TestAddressTranslator translator(kTestImageSize, kRvaBegin); |
| |
| // For simplicity, test data is not real X86 machine code. We are only |
| // including rel32 targets, without the full instructions. |
| std::vector<uint8_t> bytes = { |
| 0xFF, 0xFF, 0xFF, 0xFF, // 00030000: (Filler) |
| 0xFF, 0xFF, 0xFF, 0xFF, // 0003000C: (Filler) |
| 0x04, 0x00, 0x00, 0x00, // 00030008: 00030010 |
| 0xFF, 0xFF, 0xFF, 0xFF, // 0003000C: (Filler) |
| 0x00, 0x00, 0x00, 0x00, // 00030010: 00030014 |
| 0xFF, 0xFF, 0xFF, 0xFF, // 00030014: (Filler) |
| 0xF4, 0xFF, 0xFF, 0xFF, // 00030018: 00030010 |
| 0xE4, 0xFF, 0xFF, 0xFF, // 0003001C: 00030004 |
| }; |
| ConstBufferView buffer(bytes.data(), bytes.size()); |
| // Specify rel32 locations directly, instead of parsing. |
| std::deque<offset_t> rel32_locations = {0x0008U, 0x0010U, 0x0018U, 0x001CU}; |
| |
| // Generate everything. |
| auto reader1 = std::make_unique<Rel32ReaderX86>(buffer, 0x0000U, 0x0020U, |
| &rel32_locations, translator); |
| CheckReader({{0x0008U, 0x0010U}, |
| {0x0010U, 0x0014U}, |
| {0x0018U, 0x0010U}, |
| {0x001CU, 0x0004U}}, |
| std::move(reader1)); |
| |
| // Exclude last. |
| auto reader2 = std::make_unique<Rel32ReaderX86>(buffer, 0x0000U, 0x001CU, |
| &rel32_locations, translator); |
| CheckReader({{0x0008U, 0x0010U}, {0x0010U, 0x0014U}, {0x0018U, 0x0010U}}, |
| std::move(reader2)); |
| |
| // Only find one. |
| auto reader3 = std::make_unique<Rel32ReaderX86>(buffer, 0x000CU, 0x0018U, |
| &rel32_locations, translator); |
| CheckReader({{0x0010U, 0x0014U}}, std::move(reader3)); |
| } |
| |
| TEST(Rel32UtilsTest, Rel32WriterX86) { |
| constexpr offset_t kTestImageSize = 0x00100000U; |
| constexpr rva_t kRvaBegin = 0x00030000U; |
| TestAddressTranslator translator(kTestImageSize, kRvaBegin); |
| |
| std::vector<uint8_t> bytes(32, 0xFF); |
| MutableBufferView buffer(bytes.data(), bytes.size()); |
| |
| Rel32WriterX86 writer(buffer, translator); |
| writer.PutNext({0x0008U, 0x0010U}); |
| EXPECT_EQ(0x00000004U, buffer.read<uint32_t>(0x08)); // 00030008: 00030010 |
| |
| writer.PutNext({0x0010U, 0x0014U}); |
| EXPECT_EQ(0x00000000U, buffer.read<uint32_t>(0x10)); // 00030010: 00030014 |
| |
| writer.PutNext({0x0018U, 0x0010U}); |
| EXPECT_EQ(0xFFFFFFF4U, buffer.read<uint32_t>(0x18)); // 00030018: 00030010 |
| |
| writer.PutNext({0x001CU, 0x0004U}); |
| EXPECT_EQ(0xFFFFFFE4U, buffer.read<uint32_t>(0x1C)); // 0003001C: 00030004 |
| |
| EXPECT_EQ(std::vector<uint8_t>({ |
| 0xFF, 0xFF, 0xFF, 0xFF, // 00030000: (Filler) |
| 0xFF, 0xFF, 0xFF, 0xFF, // 00030004: (Filler) |
| 0x04, 0x00, 0x00, 0x00, // 00030008: 00030010 |
| 0xFF, 0xFF, 0xFF, 0xFF, // 0003000C: (Filler) |
| 0x00, 0x00, 0x00, 0x00, // 00030010: 00030014 |
| 0xFF, 0xFF, 0xFF, 0xFF, // 00030014: (Filler) |
| 0xF4, 0xFF, 0xFF, 0xFF, // 00030018: 00030010 |
| 0xE4, 0xFF, 0xFF, 0xFF, // 0003001C: 00030004 |
| }), |
| bytes); |
| } |
| |
| TEST(Rel32UtilsTest, Rel32ReaderArm_AArch32) { |
| 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::deque<offset_t> rel32_locations_A24 = {0x0008U, 0x0010U, 0x0018U, |
| 0x001CU}; |
| |
| // Generate everything. |
| auto reader1 = |
| std::make_unique<Rel32ReaderArm<AArch32Rel32Translator::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<AArch32Rel32Translator::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<AArch32Rel32Translator::AddrTraits_A24>>( |
| translator, region, rel32_locations_A24, 0x000CU, 0x0018U); |
| CheckReader({{0x0010U, 0x0014U}}, std::move(reader3)); |
| } |
| |
| TEST(Rel32UtilsTest, Rel32WriterArm_AArch32_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<AArch32Rel32Translator::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<AArch32Rel32Translator::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<AArch32Rel32Translator::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_AArch32_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<AArch32Rel32Translator::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, AArch32SwitchToThumb2) { |
| 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<AArch32Rel32Translator::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_AArch32) { |
| std::vector<uint8_t> expect_fail; |
| |
| // Successful A24. |
| ArmCopyDispFun copier_A24 = |
| ArmCopyDisp<AArch32Rel32Translator::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<AArch32Rel32Translator::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<AArch32Rel32Translator::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<AArch32Rel32Translator::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<AArch32Rel32Translator::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::deque<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::deque<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::deque<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 |