brintos

brintos / llvm-project-archived public Read only

0
0
Text · 15.1 KiB · a22a17a Raw
438 lines · cpp
1//===-- AArch64MachObjectWriter.cpp - ARM Mach Object Writer --------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "MCTargetDesc/AArch64FixupKinds.h"10#include "MCTargetDesc/AArch64MCAsmInfo.h"11#include "MCTargetDesc/AArch64MCTargetDesc.h"12#include "llvm/ADT/Twine.h"13#include "llvm/BinaryFormat/MachO.h"14#include "llvm/MC/MCAsmInfo.h"15#include "llvm/MC/MCAsmInfoDarwin.h"16#include "llvm/MC/MCAssembler.h"17#include "llvm/MC/MCContext.h"18#include "llvm/MC/MCExpr.h"19#include "llvm/MC/MCFixup.h"20#include "llvm/MC/MCMachObjectWriter.h"21#include "llvm/MC/MCSection.h"22#include "llvm/MC/MCSectionMachO.h"23#include "llvm/MC/MCSymbol.h"24#include "llvm/MC/MCValue.h"25#include "llvm/Support/Casting.h"26#include "llvm/Support/MathExtras.h"27#include <cassert>28#include <cstdint>29 30using namespace llvm;31 32namespace {33 34class AArch64MachObjectWriter : public MCMachObjectTargetWriter {35  bool getAArch64FixupKindMachOInfo(const MCFixup &Fixup, unsigned &RelocType,36                                    AArch64::Specifier Spec, unsigned &Log2Size,37                                    const MCAssembler &Asm);38 39public:40  AArch64MachObjectWriter(uint32_t CPUType, uint32_t CPUSubtype, bool IsILP32)41      : MCMachObjectTargetWriter(!IsILP32 /* is64Bit */, CPUType, CPUSubtype) {}42 43  void recordRelocation(MachObjectWriter *Writer, MCAssembler &Asm,44                        const MCFragment *Fragment, const MCFixup &Fixup,45                        MCValue Target, uint64_t &FixedValue) override;46};47 48} // end anonymous namespace49 50bool AArch64MachObjectWriter::getAArch64FixupKindMachOInfo(51    const MCFixup &Fixup, unsigned &RelocType, AArch64::Specifier Spec,52    unsigned &Log2Size, const MCAssembler &Asm) {53  RelocType = unsigned(MachO::ARM64_RELOC_UNSIGNED);54  Log2Size = ~0U;55 56  switch (Fixup.getKind()) {57  default:58    return false;59 60  case FK_Data_1:61    Log2Size = Log2_32(1);62    return true;63  case FK_Data_2:64    Log2Size = Log2_32(2);65    return true;66  case FK_Data_4:67    Log2Size = Log2_32(4);68    if (Spec == AArch64::S_MACHO_GOT)69      RelocType = unsigned(MachO::ARM64_RELOC_POINTER_TO_GOT);70    return true;71  case FK_Data_8:72    Log2Size = Log2_32(8);73    if (Spec == AArch64::S_MACHO_GOT)74      RelocType = unsigned(MachO::ARM64_RELOC_POINTER_TO_GOT);75    return true;76  case AArch64::fixup_aarch64_add_imm12:77  case AArch64::fixup_aarch64_ldst_imm12_scale1:78  case AArch64::fixup_aarch64_ldst_imm12_scale2:79  case AArch64::fixup_aarch64_ldst_imm12_scale4:80  case AArch64::fixup_aarch64_ldst_imm12_scale8:81  case AArch64::fixup_aarch64_ldst_imm12_scale16:82    Log2Size = Log2_32(4);83    switch (Spec) {84    default:85      return false;86    case AArch64::S_MACHO_PAGEOFF:87      RelocType = unsigned(MachO::ARM64_RELOC_PAGEOFF12);88      return true;89    case AArch64::S_MACHO_GOTPAGEOFF:90      RelocType = unsigned(MachO::ARM64_RELOC_GOT_LOAD_PAGEOFF12);91      return true;92    case AArch64::S_MACHO_TLVPPAGEOFF:93      RelocType = unsigned(MachO::ARM64_RELOC_TLVP_LOAD_PAGEOFF12);94      return true;95    }96  case AArch64::fixup_aarch64_pcrel_adrp_imm21:97    Log2Size = Log2_32(4);98    // This encompasses the relocation for the whole 21-bit value.99    switch (Spec) {100    default:101      reportError(Fixup.getLoc(), "ADR/ADRP relocations must be GOT relative");102      return false;103    case AArch64::S_MACHO_PAGE:104      RelocType = unsigned(MachO::ARM64_RELOC_PAGE21);105      return true;106    case AArch64::S_MACHO_GOTPAGE:107      RelocType = unsigned(MachO::ARM64_RELOC_GOT_LOAD_PAGE21);108      return true;109    case AArch64::S_MACHO_TLVPPAGE:110      RelocType = unsigned(MachO::ARM64_RELOC_TLVP_LOAD_PAGE21);111      return true;112    }113    return true;114  case AArch64::fixup_aarch64_pcrel_branch26:115  case AArch64::fixup_aarch64_pcrel_call26:116    Log2Size = Log2_32(4);117    RelocType = unsigned(MachO::ARM64_RELOC_BRANCH26);118    return true;119  }120}121 122static bool canUseLocalRelocation(const MCSectionMachO &Section,123                                  const MCSymbol &Symbol, unsigned Log2Size) {124  // Debug info sections can use local relocations.125  if (Section.hasAttribute(MachO::S_ATTR_DEBUG))126    return true;127 128  // Otherwise, only pointer sized relocations are supported.129  if (Log2Size != 3)130    return false;131 132  // But only if they don't point to a few forbidden sections.133  if (!Symbol.isInSection())134    return true;135  const MCSectionMachO &RefSec =136      static_cast<MCSectionMachO &>(Symbol.getSection());137  if (RefSec.getType() == MachO::S_CSTRING_LITERALS)138    return false;139 140  if (RefSec.getSegmentName() == "__DATA" &&141      (RefSec.getName() == "__cfstring" ||142       RefSec.getName() == "__objc_classrefs"))143    return false;144 145  return true;146}147 148void AArch64MachObjectWriter::recordRelocation(149    MachObjectWriter *Writer, MCAssembler &Asm, const MCFragment *Fragment,150    const MCFixup &Fixup, MCValue Target, uint64_t &FixedValue) {151  unsigned IsPCRel = Fixup.isPCRel();152 153  // See <reloc.h>.154  uint32_t FixupOffset = Asm.getFragmentOffset(*Fragment);155  unsigned Log2Size = 0;156  int64_t Value = 0;157  unsigned Index = 0;158  unsigned Type = 0;159  unsigned Kind = Fixup.getKind();160  const MCSymbol *RelSymbol = nullptr;161 162  FixupOffset += Fixup.getOffset();163 164  // AArch64 pcrel relocation addends do not include the section offset.165  if (IsPCRel)166    FixedValue += FixupOffset;167 168  // ADRP fixups use relocations for the whole symbol value and only169  // put the addend in the instruction itself. Clear out any value the170  // generic code figured out from the sybmol definition.171  if (Kind == AArch64::fixup_aarch64_pcrel_adrp_imm21)172    FixedValue = 0;173 174  // imm19 relocations are for conditional branches, which require175  // assembler local symbols. If we got here, that's not what we have,176  // so complain loudly.177  if (Kind == AArch64::fixup_aarch64_pcrel_branch19) {178    reportError(Fixup.getLoc(), "conditional branch requires assembler-local"179                                " label. '" +180                                    Target.getAddSym()->getName() +181                                    "' is external.");182    return;183  }184 185  // 14-bit branch relocations should only target internal labels, and so186  // should never get here.187  if (Kind == AArch64::fixup_aarch64_pcrel_branch14) {188    reportError(Fixup.getLoc(), "Invalid relocation on conditional branch!");189    return;190  }191 192  if (!getAArch64FixupKindMachOInfo(Fixup, Type, Target.getSpecifier(),193                                    Log2Size, Asm)) {194    reportError(Fixup.getLoc(), "unknown AArch64 fixup kind!");195    return;196  }197 198  Value = Target.getConstant();199 200  if (Target.isAbsolute()) { // constant201    // FIXME: Should this always be extern?202    // SymbolNum of 0 indicates the absolute section.203    Type = MachO::ARM64_RELOC_UNSIGNED;204 205    if (IsPCRel) {206      reportError(Fixup.getLoc(), "PC relative absolute relocation!");207      return;208 209      // FIXME: x86_64 sets the type to a branch reloc here. Should we do210      // something similar?211    }212  } else if (auto *B = Target.getSubSym()) { // A - B + constant213    const MCSymbol *A = Target.getAddSym();214    const MCSymbol *A_Base = Writer->getAtom(*A);215    const MCSymbol *B_Base = Writer->getAtom(*B);216 217    // Check for "_foo@got - .", which comes through here as:218    // Ltmp0:219    //    ... _foo@got - Ltmp0220    if (Target.getSpecifier() == AArch64::S_MACHO_GOT &&221        Asm.getSymbolOffset(*B) ==222            Asm.getFragmentOffset(*Fragment) + Fixup.getOffset()) {223      // SymB is the PC, so use a PC-rel pointer-to-GOT relocation.224      Type = MachO::ARM64_RELOC_POINTER_TO_GOT;225      IsPCRel = 1;226      MachO::any_relocation_info MRE;227      MRE.r_word0 = FixupOffset;228      MRE.r_word1 = (IsPCRel << 24) | (Log2Size << 25) | (Type << 28);229      Writer->addRelocation(A_Base, Fragment->getParent(), MRE);230      return;231    } else if (Target.getSpecifier() != AArch64::S_None) {232      // Otherwise, neither symbol can be modified.233      reportError(Fixup.getLoc(), "unsupported relocation of modified symbol");234      return;235    }236 237    // We don't support PCrel relocations of differences.238    if (IsPCRel) {239      reportError(Fixup.getLoc(), "unsupported pc-relative relocation of "240                                  "difference");241      return;242    }243 244    // AArch64 always uses external relocations. If there is no symbol to use as245    // a base address (a local symbol with no preceding non-local symbol),246    // error out.247    //248    // FIXME: We should probably just synthesize an external symbol and use249    // that.250    if (!A_Base) {251      reportError(Fixup.getLoc(),252                  "unsupported relocation of local symbol '" + A->getName() +253                      "'. Must have non-local symbol earlier in section.");254      return;255    }256    if (!B_Base) {257      reportError(Fixup.getLoc(),258                  "unsupported relocation of local symbol '" + B->getName() +259                      "'. Must have non-local symbol earlier in section.");260      return;261    }262 263    if (A_Base == B_Base && A_Base) {264      reportError(Fixup.getLoc(), "unsupported relocation with identical base");265      return;266    }267 268    Value +=269        (!A->getFragment() ? 0 : Writer->getSymbolAddress(*A)) -270        (!A_Base || !A_Base->getFragment() ? 0271                                           : Writer->getSymbolAddress(*A_Base));272    Value -=273        (!B->getFragment() ? 0 : Writer->getSymbolAddress(*B)) -274        (!B_Base || !B_Base->getFragment() ? 0275                                           : Writer->getSymbolAddress(*B_Base));276 277    Type = MachO::ARM64_RELOC_UNSIGNED;278 279    MachO::any_relocation_info MRE;280    MRE.r_word0 = FixupOffset;281    MRE.r_word1 = (IsPCRel << 24) | (Log2Size << 25) | (Type << 28);282    Writer->addRelocation(A_Base, Fragment->getParent(), MRE);283 284    RelSymbol = B_Base;285    Type = MachO::ARM64_RELOC_SUBTRACTOR;286  } else { // A + constant287    const MCSymbol *Symbol = Target.getAddSym();288    const MCSectionMachO &Section =289        static_cast<const MCSectionMachO &>(*Fragment->getParent());290 291    bool CanUseLocalRelocation =292        canUseLocalRelocation(Section, *Symbol, Log2Size);293    if (Symbol->isTemporary() && (Value || !CanUseLocalRelocation)) {294      // Make sure that the symbol is actually in a section here. If it isn't,295      // emit an error and exit.296      if (!Symbol->isInSection()) {297        reportError(Fixup.getLoc(),298                    "unsupported relocation of local symbol '" +299                        Symbol->getName() +300                        "'. Must have non-local symbol earlier in section.");301        return;302      }303      const MCSection &Sec = Symbol->getSection();304      if (!MCAsmInfoDarwin::isSectionAtomizableBySymbols(Sec))305        Symbol->setUsedInReloc();306    }307 308    const MCSymbol *Base = Writer->getAtom(*Symbol);309 310    // If the symbol is a variable it can either be in a section and311    // we have a base or it is absolute and should have been expanded.312    assert(!Symbol->isVariable() || Base);313 314    // Relocations inside debug sections always use local relocations when315    // possible. This seems to be done because the debugger doesn't fully316    // understand relocation entries and expects to find values that317    // have already been fixed up.318    if (Symbol->isInSection()) {319      if (Section.hasAttribute(MachO::S_ATTR_DEBUG))320        Base = nullptr;321    }322 323    // AArch64 uses external relocations as much as possible. For debug324    // sections, and for pointer-sized relocations (.quad), we allow section325    // relocations.  It's code sections that run into trouble.326    if (Base) {327      RelSymbol = Base;328 329      // Add the local offset, if needed.330      if (Base != Symbol)331        Value += Asm.getSymbolOffset(*Symbol) - Asm.getSymbolOffset(*Base);332    } else if (Symbol->isInSection()) {333      if (!CanUseLocalRelocation) {334        reportError(Fixup.getLoc(),335                    "unsupported relocation of local symbol '" +336                        Symbol->getName() +337                        "'. Must have non-local symbol earlier in section.");338        return;339      }340      // Adjust the relocation to be section-relative.341      // The index is the section ordinal (1-based).342      const MCSection &Sec = Symbol->getSection();343      Index = Sec.getOrdinal() + 1;344      Value += Writer->getSymbolAddress(*Symbol);345 346      if (IsPCRel)347        Value -= Writer->getFragmentAddress(Asm, Fragment) + Fixup.getOffset() +348                 (1ULL << Log2Size);349    } else {350      llvm_unreachable(351          "This constant variable should have been expanded during evaluation");352    }353  }354 355  // If the relocation kind is Branch26, Page21, or Pageoff12, any addend356  // is represented via an Addend relocation, not encoded directly into357  // the instruction.358  if ((Type == MachO::ARM64_RELOC_BRANCH26 ||359       Type == MachO::ARM64_RELOC_PAGE21 ||360       Type == MachO::ARM64_RELOC_PAGEOFF12) &&361      Value) {362    if (!isInt<24>(Value)) {363      reportError(Fixup.getLoc(), "addend too big for relocation");364      return;365    }366 367    MachO::any_relocation_info MRE;368    MRE.r_word0 = FixupOffset;369    MRE.r_word1 =370        (Index << 0) | (IsPCRel << 24) | (Log2Size << 25) | (Type << 28);371    Writer->addRelocation(RelSymbol, Fragment->getParent(), MRE);372 373    // Now set up the Addend relocation.374    Type = MachO::ARM64_RELOC_ADDEND;375    Index = Value;376    RelSymbol = nullptr;377    IsPCRel = 0;378    Log2Size = 2;379 380    // Put zero into the instruction itself. The addend is in the relocation.381    Value = 0;382  }383 384  if (Target.getSpecifier() == AArch64::S_AUTH ||385      Target.getSpecifier() == AArch64::S_AUTHADDR) {386    auto *Expr = cast<AArch64AuthMCExpr>(Fixup.getValue());387 388    assert(Type == MachO::ARM64_RELOC_UNSIGNED);389 390    if (IsPCRel) {391      reportError(Fixup.getLoc(), "invalid PC relative auth relocation");392      return;393    }394 395    if (Log2Size != 3) {396      reportError(Fixup.getLoc(),397                  "invalid auth relocation size, must be 8 bytes");398      return;399    }400 401    if (Target.getSubSym()) {402      reportError(Fixup.getLoc(),403                  "invalid auth relocation, can't reference two symbols");404      return;405    }406 407    uint16_t Discriminator = Expr->getDiscriminator();408    AArch64PACKey::ID Key = Expr->getKey();409 410    if (!isInt<32>(Value)) {411      reportError(Fixup.getLoc(), "addend too big for relocation");412      return;413    }414 415    Type = MachO::ARM64_RELOC_AUTHENTICATED_POINTER;416    Value = (uint32_t(Value)) | (uint64_t(Discriminator) << 32) |417            (uint64_t(Expr->hasAddressDiversity()) << 48) |418            (uint64_t(Key) << 49) | (1ULL << 63);419  }420 421  // If there's any addend left to handle, encode it in the instruction.422  FixedValue = Value;423 424  // struct relocation_info (8 bytes)425  MachO::any_relocation_info MRE;426  MRE.r_word0 = FixupOffset;427  MRE.r_word1 =428      (Index << 0) | (IsPCRel << 24) | (Log2Size << 25) | (Type << 28);429  Writer->addRelocation(RelSymbol, Fragment->getParent(), MRE);430}431 432std::unique_ptr<MCObjectTargetWriter>433llvm::createAArch64MachObjectWriter(uint32_t CPUType, uint32_t CPUSubtype,434                                    bool IsILP32) {435  return std::make_unique<AArch64MachObjectWriter>(CPUType, CPUSubtype,436                                                   IsILP32);437}438