blob: 26160a0961e5c2e92d978acc1659a1577a84a0d6 [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 <lib/static-pie/static-pie.h>
#include <atomic>
#include <climits>
#include <cstdint>
#include <fbl/span.h>
#include "elf-types.h"
namespace static_pie {
void ApplyRelaRelocs(Program program, fbl::Span<const Elf64RelaEntry> table, uint64_t base) {
// We require that all entries in the table are `R_RELATIVE` entries.
for (const Elf64RelaEntry& entry : table) {
ZX_DEBUG_ASSERT(entry.info.type() == ElfRelocType::kRelative);
// Patch in the relocation: set the memory value to `base + addend`.
program.WriteWord(entry.offset, base + entry.addend);
}
}
void ApplyRelRelocs(Program program, fbl::Span<const Elf64RelEntry> table, uint64_t base) {
// We require that all entries in the table are `R_RELATIVE` entries.
for (const Elf64RelEntry& entry : table) {
ZX_DEBUG_ASSERT(entry.info.type() == ElfRelocType::kRelative);
// Patch in the relocation: add `base` to memory value.
program.WriteWord(entry.offset, program.ReadWord(entry.offset) + base);
}
}
void ApplyRelrRelocs(Program program, fbl::Span<const uint64_t> table, uint64_t base) {
uint64_t address = 0;
for (uint64_t value : table) {
// If the value is an address (low bit is 0), simply patch it in.
if ((value & 1) == 0) {
ZX_DEBUG_ASSERT(value != 0);
program.WriteWord(value, program.ReadWord(value) + base);
address = value + sizeof(uint64_t);
continue;
}
// Otherwise, the value is a bitmap, indicating which of the next 63 words
// should be updated.
uint64_t bitmap = value >> 1;
uint64_t bitmap_address = address;
while (bitmap != 0) {
// Skip over `skip` words that need not be patched.
uint64_t skip = __builtin_ctzll(bitmap);
bitmap_address += skip * sizeof(uint64_t);
bitmap >>= (skip + 1);
// Patch this word.
program.WriteWord(bitmap_address, program.ReadWord(bitmap_address) + base);
bitmap_address += sizeof(uint64_t);
}
// Move `address` ahead 63 words.
constexpr uint64_t bits_per_bitmap = (sizeof(uint64_t) * CHAR_BIT) - 1;
address += sizeof(uint64_t) * bits_per_bitmap;
}
}
void ApplyDynamicRelocs(Program program, fbl::Span<const Elf64DynamicEntry> table, uint64_t base) {
// Locations and sizes of the rel, rela, and relr tables.
struct RelocationTable {
uint64_t start = 0; // Address of the table.
size_t size_bytes = 0; // Size of the table, in bytes.
// Number of R_RELATIVE entries in the table.
//
// These are required to be ordered first in the `.rel` and `.rela` table.
uint64_t num_relative_relocs = 0;
};
RelocationTable rel_table{};
RelocationTable rela_table{};
RelocationTable relr_table{};
// Process entries in the ".dynamic" table.
for (size_t i = 0; i < table.size() && table[i].tag != DynamicArrayTag::kNull; i++) {
switch (table[i].tag) {
// Rela table.
case DynamicArrayTag::kRela:
rela_table.start = table[i].value;
break;
case DynamicArrayTag::kRelaSize:
rela_table.size_bytes = table[i].value;
break;
case DynamicArrayTag::kRelaCount:
rela_table.num_relative_relocs = table[i].value;
break;
case DynamicArrayTag::kRelaEntrySize:
ZX_ASSERT(table[i].value == sizeof(Elf64RelaEntry));
break;
// Rel table.
case DynamicArrayTag::kRel:
rel_table.start = table[i].value;
break;
case DynamicArrayTag::kRelSize:
rel_table.size_bytes = table[i].value;
break;
case DynamicArrayTag::kRelCount:
rel_table.num_relative_relocs = table[i].value;
break;
case DynamicArrayTag::kRelEntrySize:
ZX_ASSERT(table[i].value != sizeof(Elf64RelEntry));
break;
// Relr table.
case DynamicArrayTag::kRelr:
relr_table.start = table[i].value;
break;
case DynamicArrayTag::kRelrSize:
relr_table.size_bytes = table[i].value;
break;
case DynamicArrayTag::kRelrEntrySize:
ZX_ASSERT(table[i].value == sizeof(uint64_t));
break;
default:
break;
}
}
// Apply any relocations.
{
fbl::Span<const uint64_t> table =
program.MapRegion<const uint64_t>(relr_table.start, relr_table.size_bytes);
ApplyRelrRelocs(program, table, base);
}
{
fbl::Span<const Elf64RelaEntry> table =
program.MapRegion<const Elf64RelaEntry>(rela_table.start, rela_table.size_bytes);
// Only the first `num_relative_relocs` will be R_RELATIVE entries.
ApplyRelaRelocs(program, table.subspan(0, rela_table.num_relative_relocs), base);
}
{
fbl::Span<const Elf64RelEntry> table =
program.MapRegion<const Elf64RelEntry>(rel_table.start, rel_table.size_bytes);
// Only the first `num_relative_relocs` will be R_RELATIVE entries.
ApplyRelRelocs(program, table.subspan(0, rel_table.num_relative_relocs), base);
}
}
} // namespace static_pie