| // Copyright 2020 The Fuchsia 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 "relocation.h" |
| |
| #include <fbl/span.h> |
| #include <gtest/gtest.h> |
| |
| namespace static_pie { |
| namespace { |
| |
| TEST(ApplyRelRelocs, EmptyTable) { |
| Program program{fbl::Span<std::byte>{}, LinkTimeAddr(0), RunTimeAddr(0)}; |
| ApplyRelRelocs(program, {}); |
| } |
| |
| TEST(ApplyRelRelocs, ApplyRelocs) { |
| uint64_t program[] = { |
| 0x00000000'00000000, 0x11111111'11111111, 0x22222222'22222222, |
| 0x33333333'33333333, 0x44444444'44444444, |
| }; |
| |
| // Apply two relocs, at index 1 and 3. |
| constexpr Elf64RelEntry entries[] = { |
| {LinkTimeAddr(8), Elf64RelInfo::OfType(ElfRelocType::kRelative)}, |
| {LinkTimeAddr(24), Elf64RelInfo::OfType(ElfRelocType::kRelative)}, |
| }; |
| ApplyRelRelocs(Program(fbl::as_writable_bytes(fbl::Span(program)), LinkTimeAddr(0), |
| RunTimeAddr{0xaaaaaaaa'aaaaaaaa}), |
| entries); |
| |
| // Ensure that the values are correct. |
| EXPECT_EQ(program[0], 0x00000000'00000000u); // no change |
| EXPECT_EQ(program[1], 0xbbbbbbbb'bbbbbbbbu); // updated |
| EXPECT_EQ(program[2], 0x22222222'22222222u); // no change |
| EXPECT_EQ(program[3], 0xdddddddd'ddddddddu); // updated |
| EXPECT_EQ(program[4], 0x44444444'44444444u); // no change |
| } |
| |
| TEST(ApplyRelaRelocs, EmptyTable) { |
| Program program{fbl::Span<std::byte>{}, LinkTimeAddr(0), RunTimeAddr(0)}; |
| ApplyRelaRelocs(program, {}); |
| } |
| |
| TEST(ApplyRelaRelocs, ApplyRelocs) { |
| uint64_t program[] = { |
| 0x00000000'00000000, 0xaeaeaeae'aeaeaeae, 0x22222222'22222222, |
| 0xeaeaeaea'eaeaeaea, 0x44444444'44444444, |
| }; |
| |
| // Apply two relocs, at index 1 and 3. |
| constexpr Elf64RelaEntry entries[] = { |
| {LinkTimeAddr(8), Elf64RelInfo::OfType(ElfRelocType::kRelative), 0x11111111'11111111}, |
| {LinkTimeAddr(24), Elf64RelInfo::OfType(ElfRelocType::kRelative), 0x33333333'33333333}}; |
| ApplyRelaRelocs(Program(fbl::as_writable_bytes(fbl::Span(program)), LinkTimeAddr(0), |
| RunTimeAddr(0xaaaaaaaa'aaaaaaaa)), |
| entries); |
| |
| // Ensure that the values are correct. |
| EXPECT_EQ(program[0], 0x00000000'00000000u); // no change |
| EXPECT_EQ(program[1], 0xbbbbbbbb'bbbbbbbbu); // updated |
| EXPECT_EQ(program[2], 0x22222222'22222222u); // no change |
| EXPECT_EQ(program[3], 0xdddddddd'ddddddddu); // updated |
| EXPECT_EQ(program[4], 0x44444444'44444444u); // no change |
| } |
| |
| TEST(ApplyRelrRelocs, EmptyTable) { |
| Program program{fbl::Span<std::byte>{}, LinkTimeAddr(0), RunTimeAddr(0)}; |
| ApplyRelrRelocs(program, {}); |
| } |
| |
| TEST(ApplyRelrRelocs, SingleReloc) { |
| // Update a single entry in the program. |
| uint64_t program[] = { |
| 0x00000000'00000000, |
| 0x00000000'00000001, |
| }; |
| constexpr uint64_t relocs[] = { |
| 0x00000000'00000008, |
| }; |
| ApplyRelrRelocs(Program(fbl::as_writable_bytes(fbl::Span(program)), LinkTimeAddr(0), |
| RunTimeAddr(0xffffffff'00000000)), |
| relocs); |
| EXPECT_EQ(program[1], 0xffffffff'00000001u); |
| } |
| |
| TEST(ApplyRelrRelocs, NoBitmaps) { |
| // Update 3 entries in the program, not using any bitmaps. |
| uint64_t program[] = { |
| 0x00000000'00000000, 0x00000000'00000001, 0x00000000'00000002, |
| 0x00000000'00000003, 0x00000000'00000004, 0x00000000'00000005, |
| }; |
| constexpr uint64_t relocs[] = { |
| 0x00000000'00000008, // update index 1. |
| 0x00000000'00000018, // update index 3. |
| 0x00000000'00000028, // update index 5. |
| }; |
| ApplyRelrRelocs(Program(fbl::as_writable_bytes(fbl::Span(program)), LinkTimeAddr(0), |
| RunTimeAddr(0xffffffff'00000000)), |
| relocs); |
| |
| EXPECT_EQ(program[0], 0x00000000'00000000u); |
| EXPECT_EQ(program[1], 0xffffffff'00000001u); |
| EXPECT_EQ(program[2], 0x00000000'00000002u); |
| EXPECT_EQ(program[3], 0xffffffff'00000003u); |
| EXPECT_EQ(program[4], 0x00000000'00000004u); |
| EXPECT_EQ(program[5], 0xffffffff'00000005u); |
| } |
| |
| TEST(ApplyRelrRelocs, SingleBitmap) { |
| // Update 3 entries in the program, using a bitmap. |
| uint64_t program[] = { |
| 0x00000000'00000000, 0x00000000'00000001, 0x00000000'00000002, |
| 0x00000000'00000003, 0x00000000'00000004, 0x00000000'00000005, |
| }; |
| constexpr uint64_t relocs[] = { |
| 0x00000000'00000008, // update index 1. |
| 0x00000000'00000015, // 0b10101 ; update index {prev + 2, prev + 4}. |
| }; |
| ApplyRelrRelocs(Program(fbl::as_writable_bytes(fbl::Span(program)), LinkTimeAddr(0), |
| RunTimeAddr(0xffffffff'00000000)), |
| relocs); |
| EXPECT_EQ(program[0], 0x00000000'00000000u); |
| EXPECT_EQ(program[1], 0xffffffff'00000001u); |
| EXPECT_EQ(program[2], 0x00000000'00000002u); |
| EXPECT_EQ(program[3], 0xffffffff'00000003u); |
| EXPECT_EQ(program[4], 0x00000000'00000004u); |
| EXPECT_EQ(program[5], 0xffffffff'00000005u); |
| } |
| |
| TEST(ApplyRelrRelocs, MultipleBitmaps) { |
| // Create a large program. |
| constexpr int kSize = 256; |
| std::array<uint64_t, kSize> program; |
| for (uint64_t i = 0; i < kSize; i++) { |
| program[i] = i; |
| } |
| |
| // Start at offset 1, and then update every second word. |
| constexpr uint64_t relocs[] = { |
| 0x00000000'00000008, // update index 1. |
| 0x55555555'55555555, // 0b0101010 ... 101010101 |
| 0xaaaaaaaa'aaaaaaab, // 0b1010101 ... 010101011 |
| }; |
| ApplyRelrRelocs(Program(fbl::as_writable_bytes(fbl::Span(program.data(), program.size())), |
| LinkTimeAddr(0), RunTimeAddr(0xffffffff'00000000)), |
| relocs); |
| |
| // Expect the first 1 + 63 + 63 odd offsets to be updated, while the rest remain unchanged. |
| for (uint64_t i = 0; i < kSize; i++) { |
| if (i % 2 == 1 && i <= 1 + 63 + 63) { |
| EXPECT_EQ(program[i], i + 0xffffffff'00000000u); |
| } else { |
| EXPECT_EQ(program[i], i); |
| } |
| } |
| } |
| |
| TEST(ApplyDynamicRelocs, EmptyTable) { |
| Program program{fbl::Span<std::byte>{}, LinkTimeAddr(0), RunTimeAddr(0)}; |
| ApplyDynamicRelocs(program, {}); |
| } |
| |
| // BinaryWriter allows joining raw structures into a contiguous region of memory. |
| class BinaryWriter { |
| public: |
| BinaryWriter() = default; |
| explicit BinaryWriter(LinkTimeAddr addr) : link_addr_(addr) {} |
| |
| // Append the given value onto the program. |
| // |
| // Return the LinkTimeAddr that the data was written to. |
| template <typename T> |
| LinkTimeAddr Write(T value) { |
| uint64_t offset = data_.size(); |
| const std::byte* ptr = reinterpret_cast<std::byte*>(&value); |
| data_.insert(data_.end(), ptr, ptr + sizeof(value)); |
| return link_addr_ + offset; |
| } |
| |
| fbl::Span<std::byte> data() { return {data_.data(), data_.size()}; } |
| |
| private: |
| std::vector<std::byte> data_; |
| LinkTimeAddr link_addr_{}; |
| }; |
| |
| TEST(ApplyDynamicRelocs, OneOfEach) { |
| // Create a fake "ELF program" with a rela, rel, and relr sections. |
| BinaryWriter writer; |
| |
| // Write out some program values. |
| writer.Write(uint64_t{0}); |
| LinkTimeAddr offset1 = writer.Write(uint64_t{1}); |
| LinkTimeAddr offset2 = writer.Write(uint64_t{2}); |
| LinkTimeAddr offset3 = writer.Write(uint64_t{3}); |
| |
| // Write out a single rel entry (patching offset1), rela entry (patching offset2), and relr entry |
| // (patching offset3). |
| LinkTimeAddr rel_table = writer.Write(Elf64RelEntry{ |
| .offset = offset1, |
| .info = Elf64RelInfo::OfType(ElfRelocType::kRelative), |
| }); |
| LinkTimeAddr rela_table = writer.Write(Elf64RelaEntry{ |
| .offset = offset2, .info = Elf64RelInfo::OfType(ElfRelocType::kRelative), .addend = 2}); |
| LinkTimeAddr relr_table = writer.Write(offset3); |
| |
| // Generate a dynamic table. |
| Elf64DynamicEntry dynamic[] = { |
| {.tag = DynamicArrayTag::kRel, .value = rel_table.value()}, |
| {.tag = DynamicArrayTag::kRelSize, .value = sizeof(Elf64RelEntry)}, |
| {.tag = DynamicArrayTag::kRelCount, .value = 1}, |
| |
| {.tag = DynamicArrayTag::kRela, .value = rela_table.value()}, |
| {.tag = DynamicArrayTag::kRelaSize, .value = sizeof(Elf64RelaEntry)}, |
| {.tag = DynamicArrayTag::kRelaCount, .value = 1}, |
| |
| {.tag = DynamicArrayTag::kRelr, .value = relr_table.value()}, |
| {.tag = DynamicArrayTag::kRelrSize, .value = sizeof(uint64_t)}, |
| |
| {.tag = DynamicArrayTag::kNull, .value = 0}, |
| }; |
| |
| // Apply an offset of 0x100. |
| Program program{writer.data(), LinkTimeAddr(0), RunTimeAddr(0x100)}; |
| ApplyDynamicRelocs(program, dynamic); |
| |
| // Expect that the data has been updated. |
| EXPECT_EQ(program.ReadWord(offset1), 0x101u); // patched from 0x1 -> 0x101 |
| EXPECT_EQ(program.ReadWord(offset2), 0x102u); // patched from 0x2 -> 0x102 |
| EXPECT_EQ(program.ReadWord(offset3), 0x103u); // patched from 0x3 -> 0x103 |
| } |
| |
| TEST(ApplyDynamicRelocs, RelCount) { |
| // Create a fake "ELF program" with a rel section. |
| BinaryWriter writer; |
| |
| // Write out a single program value. |
| LinkTimeAddr first_word = writer.Write(uint64_t{1}); |
| |
| // Write out a some rel entries, only the first of which is valid. |
| LinkTimeAddr rel_table = writer.Write(Elf64RelEntry{ |
| .offset = LinkTimeAddr(0), .info = Elf64RelInfo::OfType(ElfRelocType::kRelative)}); |
| writer.Write( |
| Elf64RelEntry{.offset = LinkTimeAddr(0), .info = Elf64RelInfo::OfType(ElfRelocType::kNone)}); |
| writer.Write( |
| Elf64RelEntry{.offset = LinkTimeAddr(0), .info = Elf64RelInfo::OfType(ElfRelocType::kNone)}); |
| |
| // Generate a dynamic table, with RelCount set to "1". |
| Elf64DynamicEntry dynamic[] = { |
| {.tag = DynamicArrayTag::kRel, .value = rel_table.value()}, |
| {.tag = DynamicArrayTag::kRelSize, .value = 3 * sizeof(Elf64RelEntry)}, |
| {.tag = DynamicArrayTag::kRelCount, .value = 1}, |
| |
| {.tag = DynamicArrayTag::kNull, .value = 0}, |
| }; |
| |
| // Apply an offset of 0x100. |
| Program program{writer.data(), LinkTimeAddr(0), RunTimeAddr(0x100)}; |
| ApplyDynamicRelocs(program, dynamic); |
| |
| // Expect the value is updated, and only the first reloc was applied. |
| EXPECT_EQ(program.ReadWord(first_word), 0x101u); // patched from 0x1 -> 0x101 |
| } |
| |
| TEST(ApplyDynamicRelocs, RelaCount) { |
| // Create a fake "ELF program" with a rela section. |
| BinaryWriter writer; |
| |
| // Write out a program value. |
| LinkTimeAddr first_word = writer.Write(uint64_t{1}); |
| |
| // Write out a some rela entries, only the first of which is valid. |
| LinkTimeAddr rela_table = |
| writer.Write(Elf64RelaEntry{.offset = LinkTimeAddr(0), |
| .info = Elf64RelInfo::OfType(ElfRelocType::kRelative), |
| .addend = 1}); |
| writer.Write( |
| Elf64RelaEntry{.offset = LinkTimeAddr(0), .info = Elf64RelInfo::OfType(ElfRelocType::kNone)}); |
| writer.Write( |
| Elf64RelaEntry{.offset = LinkTimeAddr(0), .info = Elf64RelInfo::OfType(ElfRelocType::kNone)}); |
| |
| // Generate a dynamic table, with RelaCount set to "1". |
| Elf64DynamicEntry dynamic[] = { |
| {.tag = DynamicArrayTag::kRela, .value = rela_table.value()}, |
| {.tag = DynamicArrayTag::kRelaSize, .value = 3 * sizeof(Elf64RelaEntry)}, |
| {.tag = DynamicArrayTag::kRelaCount, .value = 1}, |
| |
| {.tag = DynamicArrayTag::kNull, .value = 0}, |
| }; |
| |
| // Apply an offset of 0x100. |
| Program program{writer.data(), LinkTimeAddr(0), RunTimeAddr(0x100)}; |
| ApplyDynamicRelocs(program, dynamic); |
| |
| // Expect the value is updated, and only the first reloc was applied. |
| EXPECT_EQ(program.ReadWord(first_word), 0x101u); // patched from 0x1 -> 0x101 |
| } |
| |
| TEST(ApplyDynamicRelocs, NonZeroLinkAddress) { |
| // Create a fake "ELF program" with a rela, rel, and relr sections. |
| BinaryWriter writer(LinkTimeAddr(0x1000)); |
| |
| // Write out some program values. |
| // |
| // Each value refers to its own link address, assuming we were linked |
| // at address 0x1000. |
| LinkTimeAddr offset1 = writer.Write(uint64_t{0x1000}); |
| LinkTimeAddr offset2 = writer.Write(uint64_t{0x1008}); |
| LinkTimeAddr offset3 = writer.Write(uint64_t{0x1010}); |
| |
| // Write out rel/rela/relr entries patching each of the three values. |
| LinkTimeAddr rel_table = writer.Write(Elf64RelEntry{ |
| .offset = offset1, |
| .info = Elf64RelInfo::OfType(ElfRelocType::kRelative), |
| }); |
| LinkTimeAddr rela_table = writer.Write(Elf64RelaEntry{ |
| .offset = offset2, .info = Elf64RelInfo::OfType(ElfRelocType::kRelative), .addend = 0x1008}); |
| LinkTimeAddr relr_table = writer.Write(offset3); |
| |
| // Generate a dynamic table. |
| Elf64DynamicEntry dynamic[] = { |
| {.tag = DynamicArrayTag::kRel, .value = rel_table.value()}, |
| {.tag = DynamicArrayTag::kRelSize, .value = sizeof(Elf64RelEntry)}, |
| {.tag = DynamicArrayTag::kRelCount, .value = 1}, |
| |
| {.tag = DynamicArrayTag::kRela, .value = rela_table.value()}, |
| {.tag = DynamicArrayTag::kRelaSize, .value = sizeof(Elf64RelaEntry)}, |
| {.tag = DynamicArrayTag::kRelaCount, .value = 1}, |
| |
| {.tag = DynamicArrayTag::kRelr, .value = relr_table.value()}, |
| {.tag = DynamicArrayTag::kRelrSize, .value = sizeof(uint64_t)}, |
| |
| {.tag = DynamicArrayTag::kNull, .value = 0}, |
| }; |
| |
| // Load the program at address 0x2000. |
| Program program{writer.data(), LinkTimeAddr(0x1000), RunTimeAddr(0x2000)}; |
| ApplyDynamicRelocs(program, dynamic); |
| |
| // Expect that the data has been updated. |
| EXPECT_EQ(program.ReadWord(offset1), 0x2000u); |
| EXPECT_EQ(program.ReadWord(offset2), 0x2008u); |
| EXPECT_EQ(program.ReadWord(offset3), 0x2010u); |
| } |
| |
| } // namespace |
| } // namespace static_pie |