| // Copyright (c) 2012 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 "courgette/encoded_program.h" |
| |
| #include <stddef.h> |
| #include <stdint.h> |
| |
| #include <memory> |
| #include <vector> |
| |
| #include "base/stl_util.h" |
| #include "courgette/image_utils.h" |
| #include "courgette/label_manager.h" |
| #include "courgette/streams.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace courgette { |
| |
| namespace { |
| |
| // Helper class to instantiate RVAToLabel while managing allocation. |
| class TestLabelManager : public LabelManager { |
| public: |
| void RawAddLabel(int index, RVA rva) { |
| labels_.push_back(Label(rva, index)); // Don't care about |count_|. |
| } |
| }; |
| |
| // Creates a simple new program with given addresses. The orders of elements |
| // in |abs32_specs| and |rel32_specs| are important. |
| std::unique_ptr<EncodedProgram> CreateTestProgram( |
| const TestLabelManager& abs32_label_manager, |
| const TestLabelManager& rel32_label_manager) { |
| std::unique_ptr<EncodedProgram> program(new EncodedProgram()); |
| |
| uint32_t base = 0x00900000; |
| program->set_image_base(base); |
| |
| EXPECT_TRUE(program->ImportLabels(abs32_label_manager, rel32_label_manager)); |
| |
| EXPECT_TRUE(program->AddOrigin(0)); // Start at base. |
| |
| // Add instructions. Since we're using TestLabelManager, Labels are sorted in |
| // the order they're added via Add(). |
| for (const Label& label : abs32_label_manager.Labels()) |
| EXPECT_TRUE(program->AddAbs32(label.index_)); |
| for (const Label& label : rel32_label_manager.Labels()) |
| EXPECT_TRUE(program->AddRel32(label.index_)); |
| |
| return program; |
| } |
| |
| bool CompareSink(const uint8_t expected[], |
| size_t num_expected, |
| SinkStream* ss) { |
| size_t n = ss->Length(); |
| if (num_expected != n) |
| return false; |
| const uint8_t* buffer = ss->Buffer(); |
| return memcmp(&expected[0], buffer, n) == 0; |
| } |
| |
| } // namespace |
| |
| // Create a simple program with a few addresses and references and |
| // check that the bits produced are as expected. |
| TEST(EncodedProgramTest, Test) { |
| // ABS32 index 7 <-- base + 4. |
| TestLabelManager abs32_label_manager; |
| abs32_label_manager.RawAddLabel(7, 4); |
| // REL32 index 5 <-- base + 0. |
| TestLabelManager rel32_label_manager; |
| rel32_label_manager.RawAddLabel(5, 0); |
| |
| std::unique_ptr<EncodedProgram> program( |
| CreateTestProgram(abs32_label_manager, rel32_label_manager)); |
| |
| // Serialize and deserialize. |
| SinkStreamSet sinks; |
| EXPECT_TRUE(program->WriteTo(&sinks)); |
| program.reset(); |
| |
| SinkStream sink; |
| bool can_collect = sinks.CopyTo(&sink); |
| EXPECT_TRUE(can_collect); |
| |
| const void* buffer = sink.Buffer(); |
| size_t length = sink.Length(); |
| |
| SourceStreamSet sources; |
| bool can_get_source_streams = sources.Init(buffer, length); |
| EXPECT_TRUE(can_get_source_streams); |
| |
| std::unique_ptr<EncodedProgram> encoded2(new EncodedProgram()); |
| bool can_read = encoded2->ReadFrom(&sources); |
| EXPECT_TRUE(can_read); |
| |
| // Finally, try to assemble. |
| SinkStream assembled; |
| bool can_assemble = encoded2->AssembleTo(&assembled); |
| EXPECT_TRUE(can_assemble); |
| encoded2.reset(); |
| |
| const uint8_t golden[] = { |
| 0x04, 0x00, 0x90, |
| 0x00, // ABS32 to base + 4 |
| 0xF8, 0xFF, 0xFF, |
| 0xFF // REL32 from next line to base + 2 |
| }; |
| EXPECT_TRUE(CompareSink(golden, base::size(golden), &assembled)); |
| } |
| |
| // A larger test with multiple addresses. We encode the program and check the |
| // contents of the address streams. |
| TEST(EncodedProgramTest, TestWriteAddress) { |
| // Absolute addresses by index: [_, _, _, 2, _, 23, _, 11]. |
| TestLabelManager abs32_label_manager; |
| abs32_label_manager.RawAddLabel(7, 11); |
| abs32_label_manager.RawAddLabel(3, 2); |
| abs32_label_manager.RawAddLabel(5, 23); |
| // Relative addresses by index: [16, 7, _, 32]. |
| TestLabelManager rel32_label_manager; |
| rel32_label_manager.RawAddLabel(0, 16); |
| rel32_label_manager.RawAddLabel(3, 32); |
| rel32_label_manager.RawAddLabel(1, 7); |
| |
| std::unique_ptr<EncodedProgram> program( |
| CreateTestProgram(abs32_label_manager, rel32_label_manager)); |
| |
| SinkStreamSet sinks; |
| EXPECT_TRUE(program->WriteTo(&sinks)); |
| program.reset(); |
| |
| // Check indexes and addresses in sinks. |
| const uint8_t golden_abs32_indexes[] = { |
| 0x03, 0x07, 0x03, 0x05 // 3 indexes: [7, 3, 5]. |
| }; |
| EXPECT_TRUE(CompareSink(golden_abs32_indexes, |
| base::size(golden_abs32_indexes), |
| sinks.stream(kStreamAbs32Indexes))); |
| |
| const uint8_t golden_rel32_indexes[] = { |
| 0x03, 0x00, 0x03, 0x01 // 3 indexes: [0, 3, 1]. |
| }; |
| EXPECT_TRUE(CompareSink(golden_rel32_indexes, |
| base::size(golden_rel32_indexes), |
| sinks.stream(kStreamRel32Indexes))); |
| |
| // Addresses: [_, _, _, 2, _, 23, _, 11]. |
| // Padded: [0, 0, 0, 2, 2, 23, 23, 11]. |
| // Delta: [0, 0, 0, 2, 0, 21, 0, -12]. |
| // Hex: [0, 0, 0, 0x02, 0, 0x15, 0, 0xFFFFFFF4]. |
| // Complement neg: [0, 0, 0, 0x02, 0, 0x15, 0, (0x0B)]. |
| // Varint32 Signed: [0, 0, 0, 0x04, 0, 0x2A, 0, 0x17]. |
| const uint8_t golden_abs32_addresses[] = { |
| 0x08, // 8 address deltas. |
| 0x00, 0x00, 0x00, 0x04, 0x00, 0x2A, 0x00, 0x17, |
| }; |
| EXPECT_TRUE(CompareSink(golden_abs32_addresses, |
| base::size(golden_abs32_addresses), |
| sinks.stream(kStreamAbs32Addresses))); |
| |
| // Addresses: [16, 7, _, 32]. |
| // Padded: [16, 7, 7, 32]. |
| // Delta: [16, -9, 0, 25]. |
| // Hex: [0x10, 0xFFFFFFF7, 0, 0x19]. |
| // Complement Neg: [0x10, (0x08), 0, 0x19]. |
| // Varint32 Signed: [0x20, 0x11, 0, 0x32]. |
| const uint8_t golden_rel32_addresses[] = { |
| 0x04, // 4 address deltas. |
| 0x20, 0x11, 0x00, 0x32, |
| }; |
| EXPECT_TRUE(CompareSink(golden_rel32_addresses, |
| base::size(golden_rel32_addresses), |
| sinks.stream(kStreamRel32Addresses))); |
| } |
| |
| } // namespace courgette |