blob: 3a44372ea704a5f31294ac8631213403a18591db [file] [log] [blame]
// Copyright 2021 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.
#ifndef SRC_LIB_ELFLDLTL_INCLUDE_LIB_ELFLDLTL_MACHINE_H_
#define SRC_LIB_ELFLDLTL_INCLUDE_LIB_ELFLDLTL_MACHINE_H_
#include <cstdint>
#include <optional>
#include "constants.h"
namespace elfldltl {
// This is specialized to give the machine-specific details on relocation.
template <ElfMachine Machine>
struct RelocationTraits;
// This is the prototypical specialization that serves to document the
// RelocationTraits API. It does not correspond to an actual machine
// format; actual files with EM_NONE should not be produced or consumed
// using these relocation types. But this can be used in unit tests.
template <>
struct RelocationTraits<ElfMachine::kNone> {
// This lists a small subset of the relocation type codes for the machine.
// This doesn't define each per-machine type with its own canonical name.
// Instead it only the types used by modern dynamic linking ABIs. Each of
// the few types actually supported for dynamic linking has the same
// semantics across machines, but each machine its own different name and
// type code for each one. This type uses a uniform set of names for these,
// but with the actual type values each machine encodes in Elf::Rel::type().
// The semantics associated with each type name are described below.
//
// In pseudo-code expressions below, these variables are used:
// * `Base` is the load bias of the relocated module (i.e. the difference
// between its runtime load address and its first PT_LOAD's p_vaddr).
// * `SymbolBase` is the load bias of the module defining this symbol.
// * `SymbolValue` is the st_value of the defining module's symbol.
// * `Addend` is r_addend or equivalent extracted (signed) value.
//
// The datum being relocated is located at Base + r_offset.
enum class Type : uint32_t {
// This type should never appear but has always been assigned with value
// zero in every ABI. Historically some linkers have occasionally produced
// filler entries with this type that should be ignored.
kNone,
// These types can touch anywhere in initialized data or the GOT.
// Theoretically they might not always be aligned in some ABIs, but this
// implementation only supports naturally aligned relocation targets.
// Misaligned targets cannot arise from standard C/C++ initializers, since
// address-holding types require natural alignment in every ABI.
kRelative, // Base + Addend
kAbsolute, // SymbolBase + SymbolValue + Addend
// GOT types do not use the addend.
kPlt, // SymbolBase + SymbolValue
// This is a GOT type that stores the TLS module ID of the defining module.
// The GOT slot is used in arguments to the ABI's runtime callback to
// resolve thread-local references in the GD/LD TLS model.
kTlsModule,
// TLS "address" types use SymbolValue + Addend as the value stored.
kTlsAbsolute, // Relative to the thread pointer (static TLS).
kTlsRelative, // Relative to the symbol-defining module's TLS block.
};
// These types are not available on every machine and so are not included in
// the enum. Instead, they are defined as constexpr std::optional<uint32_t>.
// This is like kAbsolute but without the addend, so when not doing lazy PLT
// fixup it's exactly the same as kPlt: SymbolBase + SymbolValue. Some
// machines don't have a separate GOT type at all and just use kAbsolute.
static constexpr std::optional<uint32_t> kGot = std::nullopt;
// TLSDESC is the only type that doesn't store exactly one word.
// It stores into two adjacent GOT slots at Base + r_offset.
//
// This is the modern alternative to using kTlsModule + kTlsRelative; it
// performs better at runtime and so is always the preferred form for the
// compiler to generate. The first slot gets filled at runtime with the PC
// address of a callback function. Compiled code calls this using a special
// calling convention that passes in the address of the GOT slots and gets
// back a per-thread location or offset (details vary by machine, but it's a
// bespoke convention that minimizes register spills for efficiency). The
// addend applies to, and for REL format is stored in, the *second* slot.
// The runtime setup updates that slot to hold state used by its callback.
static constexpr std::optional<uint32_t> kTlsDesc = std::nullopt;
// TODO(fxbug.dev/84273): TLS computations
};
// Specialization for AArch64. TODO(mcgrathr): Different types used for same
// things on ILP32, vs ELFCLASS32 LP64 wrt GOT size et al: LP64 reloc all types
// >255, don't fit in Elf32::Rel::r_info.
template <>
struct RelocationTraits<ElfMachine::kAarch64> {
enum class Type : uint32_t {
kNone = 0, // R_AARCH64_NONE
kRelative = 1027, // R_AARCH64_RELATIVE
kAbsolute = 257, // R_AARCH64_ABS64
kPlt = 1026, // R_AARCH64_JUMP_SLOT
kTlsAbsolute = 1030, // R_AARCH64_TLS_TPREL64
kTlsRelative = 1029, // R_AARCH64_TLS_DTPREL64
kTlsModule = 1028, // R_AARCH64_TLS_DTPMOD64
};
static constexpr std::optional<uint32_t> kGot = 1025; // R_AARCH64_GLOB_DAT
static constexpr std::optional<uint32_t> kTlsDesc = 1031; // R_AARCH64_TLSDESC
};
// Specialization for x86-64.
template <>
struct RelocationTraits<ElfMachine::kX86_64> {
enum class Type : uint32_t {
kNone = 0, // R_X86_64_NONE
kRelative = 8, // R_X86_64_RELATIVE
kAbsolute = 1, // R_X86_64_64
kPlt = 7, // R_X86_64_JUMP_SLOT
kTlsAbsolute = 18, // R_X86_64_TPOFF64
kTlsRelative = 17, // R_X86_64_DTPOFF64
kTlsModule = 16, // R_X86_64_DTPMOD64
};
static constexpr std::optional<uint32_t> kGot = 6; // R_X86_64_GLOB_DAT
static constexpr std::optional<uint32_t> kTlsDesc = 36; // R_X86_64_TLSDESC
};
// Specialization for i386.
template <>
struct RelocationTraits<ElfMachine::k386> {
enum class Type : uint32_t {
kNone = 0, // R_386_NONE
kRelative = 8, // R_386_RELATIVE
kAbsolute = 1, // R_386_64
kPlt = 7, // R_386_JUMP_SLOT
kTlsAbsolute = 37, // R_386_TPOFF32
kTlsRelative = 36, // R_386_DTPOFF32
kTlsModule = 35, // R_386_DTPMOD32
};
static constexpr std::optional<uint32_t> kGot = 6; // R_386_GLOB_DAT
static constexpr std::optional<uint32_t> kTlsDesc = 41; // R_386_TLS_DESC
};
// Specialization for RISCV.
template <>
struct RelocationTraits<ElfMachine::kRiscv> {
enum class Type : uint32_t {
kNone = 0, // R_RISCV_NONE
kRelative = 3, // R_RISCV_RELATIVE
kAbsolute = 2, // R_RISCV_64
kGot = kAbsolute, // R_RISCV_64
kPlt = 5, // R_RISCV_JUMP_SLOT
kTlsAbsolute = 10, // R_RISCV_TPRELF64
kTlsRelative = 9, // R_RISCV_DTPREL64
kTlsModule = 7, // R_RISCV_DTPMOD64
};
// RISCV doesn't have a separate GOT type, since the semantics are the same
// as kAbsolute anyway.
static constexpr std::optional<uint32_t> kGot = std::nullopt;
// RISCV is unfortunately missing TLSDESC.
// https://github.com/riscv-non-isa/riscv-elf-psabi-doc/issues/94 tracks
// getting it specified.
static constexpr std::optional<uint32_t> kTlsDesc = std::nullopt;
};
// This should list all the fully-defined specializations except for kNone.
template <template <ElfMachine...> class Template>
using AllSupportedMachines = Template< //
ElfMachine::kAarch64, ElfMachine::kX86_64, ElfMachine::k386, ElfMachine::kRiscv>;
} // namespace elfldltl
#endif // SRC_LIB_ELFLDLTL_INCLUDE_LIB_ELFLDLTL_MACHINE_H_