Skip to content
Relocations.cpp 40.4 KiB
Newer Older
//===- Relocations.cpp ----------------------------------------------------===//
//
//                             The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file contains platform-independent functions to process relocations.
// I'll describe the overview of this file here.
//
// Simple relocations are easy to handle for the linker. For example,
// for R_X86_64_PC64 relocs, the linker just has to fix up locations
// with the relative offsets to the target symbols. It would just be
// reading records from relocation sections and applying them to output.
//
// But not all relocations are that easy to handle. For example, for
// R_386_GOTOFF relocs, the linker has to create new GOT entries for
// symbols if they don't exist, and fix up locations with GOT entry
// offsets from the beginning of GOT section. So there is more than
// fixing addresses in relocation processing.
//
// ELF defines a large number of complex relocations.
//
// The functions in this file analyze relocations and do whatever needs
// to be done. It includes, but not limited to, the following.
//
//  - create GOT/PLT entries
//  - create new relocations in .dynsym to let the dynamic linker resolve
//    them at runtime (since ELF supports dynamic linking, not all
//    relocations can be resolved at link-time)
//  - create COPY relocs and reserve space in .bss
//  - replace expensive relocs (in terms of runtime cost) with cheap ones
//  - error out infeasible combinations such as PIC and non-relative relocs
//
// Note that the functions in this file don't actually apply relocations
// because it doesn't know about the output file nor the output file buffer.
// It instead stores Relocation objects to InputSection's Relocations
// vector to let it apply later in InputSection::writeTo.
//
//===----------------------------------------------------------------------===//

#include "Relocations.h"
#include "Config.h"
#include "Memory.h"
#include "OutputSections.h"
#include "Strings.h"
#include "SymbolTable.h"
#include "SyntheticSections.h"
#include "Target.h"

#include "llvm/Support/Endian.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>

using namespace llvm;
using namespace llvm::ELF;
using namespace llvm::object;
using namespace llvm::support::endian;

namespace lld {
namespace elf {

static bool refersToGotEntry(RelExpr Expr) {
Sean Silva's avatar
Sean Silva committed
  return isRelExprOneOf<R_GOT, R_GOT_OFF, R_MIPS_GOT_LOCAL_PAGE, R_MIPS_GOT_OFF,
                        R_MIPS_GOT_OFF32, R_MIPS_TLSGD, R_MIPS_TLSLD,
                        R_GOT_PAGE_PC, R_GOT_PC, R_GOT_FROM_END, R_TLSGD,
                        R_TLSGD_PC, R_TLSDESC, R_TLSDESC_PAGE>(Expr);
static bool isPreemptible(const SymbolBody &Body, uint32_t Type) {
  // In case of MIPS GP-relative relocations always resolve to a definition
  // in a regular input file, ignoring the one-definition rule. So we,
  // for example, should not attempt to create a dynamic relocation even
  // if the target symbol is preemptible. There are two two MIPS GP-relative
  // relocations R_MIPS_GPREL16 and R_MIPS_GPREL32. But only R_MIPS_GPREL16
  // can be against a preemptible symbol.
  // To get MIPS relocation type we apply 0xff mask. In case of O32 ABI all
  // relocation types occupy eight bit. In case of N64 ABI we extract first
  // relocation from 3-in-1 packet because only the first relocation can
  // be against a real symbol.
  if (Config->EMachine == EM_MIPS && (Type & 0xff) == R_MIPS_GPREL16)
// This function is similar to the `handleTlsRelocation`. ARM and MIPS do not
// support any relaxations for TLS relocations so by factoring out ARM and MIPS
// handling in to the separate function we can simplify the code and do not
// pollute `handleTlsRelocation` by ARM and MIPS `ifs` statements.
template <class ELFT, class GOT>
static unsigned handleNoRelaxTlsRelocation(GOT *Got, uint32_t Type,
                                           SymbolBody &Body,
                                           InputSectionBase<ELFT> &C,
                                           typename ELFT::uint Offset,
                                           int64_t Addend, RelExpr Expr) {
  typedef typename ELFT::uint uintX_t;
  auto addModuleReloc = [](SymbolBody &Body, GOT *Got, uintX_t Off, bool LD) {
    // The Dynamic TLS Module Index Relocation can be statically resolved to 1
    // if we know that we are linking an executable. For ARM we resolve the
    // relocation when writing the Got. MIPS has a custom Got implementation
    // that writes the Module index in directly.
    if (!Body.isPreemptible() && !Config->pic() && Config->EMachine == EM_ARM)
      Got->Relocations.push_back(
          {R_ABS, Target->TlsModuleIndexRel, Off, 0, &Body});
    else {
      SymbolBody *Dest = LD ? nullptr : &Body;
      In<ELFT>::RelaDyn->addReloc(
          {Target->TlsModuleIndexRel, Got, Off, false, Dest, 0});
    }
  };
Rui Ueyama's avatar
Rui Ueyama committed
  if (isRelExprOneOf<R_MIPS_TLSLD, R_TLSLD_PC>(Expr)) {
    if (Got->addTlsIndex() && (Config->pic() || Config->EMachine == EM_ARM))
      addModuleReloc(Body, Got, Got->getTlsIndexOff(), true);
    C.Relocations.push_back({Expr, Type, Offset, Addend, &Body});
    return 1;
  }
  if (Target->isTlsGlobalDynamicRel(Type)) {
    if (Got->addDynTlsEntry(Body) &&
        (Body.isPreemptible() || Config->EMachine == EM_ARM)) {
      uintX_t Off = Got->getGlobalDynOffset(Body);
      addModuleReloc(Body, Got, Off, false);
      if (Body.isPreemptible())
        In<ELFT>::RelaDyn->addReloc({Target->TlsOffsetRel, Got,
                                     Off + (uintX_t)sizeof(uintX_t), false,
                                     &Body, 0});
    C.Relocations.push_back({Expr, Type, Offset, Addend, &Body});
// Returns the number of relocations processed.
template <class ELFT>
static unsigned
handleTlsRelocation(uint32_t Type, SymbolBody &Body, InputSectionBase<ELFT> &C,
                    typename ELFT::uint Offset, int64_t Addend, RelExpr Expr) {
  if (!(C.Flags & SHF_ALLOC))
    return 0;

  if (!Body.isTls())
    return 0;

  typedef typename ELFT::uint uintX_t;
  if (Config->EMachine == EM_ARM)
    return handleNoRelaxTlsRelocation<ELFT>(In<ELFT>::Got, Type, Body, C,
                                            Offset, Addend, Expr);
  if (Config->EMachine == EM_MIPS)
    return handleNoRelaxTlsRelocation<ELFT>(In<ELFT>::MipsGot, Type, Body, C,
                                            Offset, Addend, Expr);
  bool IsPreemptible = isPreemptible(Body, Type);
Rui Ueyama's avatar
Rui Ueyama committed
  if (isRelExprOneOf<R_TLSDESC, R_TLSDESC_PAGE, R_TLSDESC_CALL>(Expr) &&
      Config->Shared) {
    if (In<ELFT>::Got->addDynTlsEntry(Body)) {
      uintX_t Off = In<ELFT>::Got->getGlobalDynOffset(Body);
      In<ELFT>::RelaDyn->addReloc({Target->TlsDescRel, In<ELFT>::Got, Off,
                                   !IsPreemptible, &Body, 0});
    if (Expr != R_TLSDESC_CALL)
      C.Relocations.push_back({Expr, Type, Offset, Addend, &Body});
Rui Ueyama's avatar
Rui Ueyama committed
  if (isRelExprOneOf<R_TLSLD_PC, R_TLSLD>(Expr)) {
    // Local-Dynamic relocs can be relaxed to Local-Exec.
    if (!Config->Shared) {
      C.Relocations.push_back(
          {R_RELAX_TLS_LD_TO_LE, Type, Offset, Addend, &Body});
    if (In<ELFT>::Got->addTlsIndex())
      In<ELFT>::RelaDyn->addReloc({Target->TlsModuleIndexRel, In<ELFT>::Got,
                                   In<ELFT>::Got->getTlsIndexOff(), false,
                                   nullptr, 0});
    C.Relocations.push_back({Expr, Type, Offset, Addend, &Body});
    return 1;
  }

  // Local-Dynamic relocs can be relaxed to Local-Exec.
  if (Target->isTlsLocalDynamicRel(Type) && !Config->Shared) {
    C.Relocations.push_back(
        {R_RELAX_TLS_LD_TO_LE, Type, Offset, Addend, &Body});
Rui Ueyama's avatar
Rui Ueyama committed
  if (isRelExprOneOf<R_TLSDESC_PAGE, R_TLSDESC, R_TLSDESC_CALL>(Expr) ||
      Target->isTlsGlobalDynamicRel(Type)) {
    if (Config->Shared) {
      if (In<ELFT>::Got->addDynTlsEntry(Body)) {
        uintX_t Off = In<ELFT>::Got->getGlobalDynOffset(Body);
        In<ELFT>::RelaDyn->addReloc(
            {Target->TlsModuleIndexRel, In<ELFT>::Got, Off, false, &Body, 0});

        // If the symbol is preemptible we need the dynamic linker to write
        // the offset too.
Loading
Loading full blame...