blob: 6e1f191bbe5ca5c38d3af7b29e262fda86069898 [file] [log] [blame]
// 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>{}};
ApplyRelRelocs(program, {}, 0);
}
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[] = {
{8, Elf64RelInfo::OfType(ElfRelocType::kRelative)},
{24, Elf64RelInfo::OfType(ElfRelocType::kRelative)},
};
ApplyRelRelocs(Program{fbl::as_writable_bytes(fbl::Span(program))}, entries,
/*base=*/0xaaaaaaaa'aaaaaaaa);
// 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>{}};
ApplyRelaRelocs(program, {}, 0);
}
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[] = {
{8, Elf64RelInfo::OfType(ElfRelocType::kRelative), 0x11111111'11111111},
{24, Elf64RelInfo::OfType(ElfRelocType::kRelative), 0x33333333'33333333}};
ApplyRelaRelocs(Program{fbl::as_writable_bytes(fbl::Span(program))}, entries,
/*base=*/0xaaaaaaaa'aaaaaaaa);
// 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>{}};
ApplyRelaRelocs(program, {}, 0);
}
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))}, relocs,
/*base=*/0xffffffff'00000000);
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))}, relocs,
/*base=*/0xffffffff'00000000);
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))}, relocs,
/*base=*/0xffffffff'00000000);
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()))},
relocs,
/*base=*/0xffffffff'00000000);
// 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>{}};
ApplyDynamicRelocs(program, {}, 0);
}
// BinaryWriter allows joining raw structures into a contiguous region of memory.
class BinaryWriter {
public:
// Append the given value onto the program.
//
// Return the offset that the data was written to.
template <typename T>
uint64_t 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 offset;
}
fbl::Span<std::byte> data() { return {data_.data(), data_.size()}; }
private:
std::vector<std::byte> data_;
};
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});
uint64_t offset1 = writer.Write(uint64_t{1});
uint64_t offset2 = writer.Write(uint64_t{2});
uint64_t offset3 = writer.Write(uint64_t{3});
// Write out a single rel entry (patching offset1), rela entry (patching offset2), and relr entry
// (patching offset3).
uint64_t rel_table = writer.Write(Elf64RelEntry{
.offset = offset1,
.info = Elf64RelInfo::OfType(ElfRelocType::kRelative),
});
uint64_t rela_table = writer.Write(Elf64RelaEntry{
.offset = offset2, .info = Elf64RelInfo::OfType(ElfRelocType::kRelative), .addend = 2});
uint64_t relr_table = writer.Write(offset3);
// Generate a dynamic table.
Elf64DynamicEntry dynamic[] = {
{.tag = DynamicArrayTag::kRel, .value = rel_table},
{.tag = DynamicArrayTag::kRelSize, .value = sizeof(Elf64RelEntry)},
{.tag = DynamicArrayTag::kRelCount, .value = 1},
{.tag = DynamicArrayTag::kRela, .value = rela_table},
{.tag = DynamicArrayTag::kRelaSize, .value = sizeof(Elf64RelaEntry)},
{.tag = DynamicArrayTag::kRelaCount, .value = 1},
{.tag = DynamicArrayTag::kRelr, .value = relr_table},
{.tag = DynamicArrayTag::kRelrSize, .value = sizeof(uint64_t)},
{.tag = DynamicArrayTag::kNull, .value = 0},
};
// Apply an offset of 0x100.
Program program{writer.data()};
ApplyDynamicRelocs(program, dynamic, /*base=*/0x100);
// 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 program value.
writer.Write(uint64_t{1});
// Write out a some rel entries, only the first of which is valid.
uint64_t rel_table = writer.Write(
Elf64RelEntry{.offset = 0, .info = Elf64RelInfo::OfType(ElfRelocType::kRelative)});
writer.Write(Elf64RelEntry{.offset = 0, .info = Elf64RelInfo::OfType(ElfRelocType::kNone)});
writer.Write(Elf64RelEntry{.offset = 0, .info = Elf64RelInfo::OfType(ElfRelocType::kNone)});
// Generate a dynamic table, with RelCount set to "1".
Elf64DynamicEntry dynamic[] = {
{.tag = DynamicArrayTag::kRel, .value = rel_table},
{.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()};
ApplyDynamicRelocs(program, dynamic, /*base=*/0x100);
// Expect the value is updated, and only the first reloc was applied.
EXPECT_EQ(program.ReadWord(0), 0x101u); // patched from 0x1 -> 0x101
}
TEST(ApplyDynamicRelocs, RelaCount) {
// Create a fake "ELF program" with a rela section.
BinaryWriter writer;
// Write out a program value.
writer.Write(uint64_t{1});
// Write out a some rela entries, only the first of which is valid.
uint64_t rela_table = writer.Write(Elf64RelaEntry{
.offset = 0, .info = Elf64RelInfo::OfType(ElfRelocType::kRelative), .addend = 1});
writer.Write(Elf64RelaEntry{.offset = 0, .info = Elf64RelInfo::OfType(ElfRelocType::kNone)});
writer.Write(Elf64RelaEntry{.offset = 0, .info = Elf64RelInfo::OfType(ElfRelocType::kNone)});
// Generate a dynamic table, with RelaCount set to "1".
Elf64DynamicEntry dynamic[] = {
{.tag = DynamicArrayTag::kRela, .value = rela_table},
{.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()};
ApplyDynamicRelocs(program, dynamic, /*base=*/0x100);
// Expect the value is updated, and only the first reloc was applied.
EXPECT_EQ(program.ReadWord(0), 0x101u); // patched from 0x1 -> 0x101
}
} // namespace
} // namespace static_pie