Home | History | Annotate | Download | only in MCTargetDesc
      1 //===-- AArch64MachObjectWriter.cpp - ARM Mach Object Writer --------------===//
      2 //
      3 //                     The LLVM Compiler Infrastructure
      4 //
      5 // This file is distributed under the University of Illinois Open Source
      6 // License. See LICENSE.TXT for details.
      7 //
      8 //===----------------------------------------------------------------------===//
      9 
     10 #include "MCTargetDesc/AArch64FixupKinds.h"
     11 #include "MCTargetDesc/AArch64MCTargetDesc.h"
     12 #include "llvm/ADT/Twine.h"
     13 #include "llvm/MC/MCAsmInfo.h"
     14 #include "llvm/MC/MCAsmLayout.h"
     15 #include "llvm/MC/MCAssembler.h"
     16 #include "llvm/MC/MCContext.h"
     17 #include "llvm/MC/MCExpr.h"
     18 #include "llvm/MC/MCFixup.h"
     19 #include "llvm/MC/MCMachObjectWriter.h"
     20 #include "llvm/MC/MCSectionMachO.h"
     21 #include "llvm/MC/MCValue.h"
     22 #include "llvm/Support/ErrorHandling.h"
     23 #include "llvm/Support/MachO.h"
     24 using namespace llvm;
     25 
     26 namespace {
     27 class AArch64MachObjectWriter : public MCMachObjectTargetWriter {
     28   bool getAArch64FixupKindMachOInfo(const MCFixup &Fixup, unsigned &RelocType,
     29                                   const MCSymbolRefExpr *Sym,
     30                                   unsigned &Log2Size, const MCAssembler &Asm);
     31 
     32 public:
     33   AArch64MachObjectWriter(uint32_t CPUType, uint32_t CPUSubtype)
     34       : MCMachObjectTargetWriter(true /* is64Bit */, CPUType, CPUSubtype) {}
     35 
     36   void recordRelocation(MachObjectWriter *Writer, MCAssembler &Asm,
     37                         const MCAsmLayout &Layout, const MCFragment *Fragment,
     38                         const MCFixup &Fixup, MCValue Target,
     39                         uint64_t &FixedValue) override;
     40 };
     41 }
     42 
     43 bool AArch64MachObjectWriter::getAArch64FixupKindMachOInfo(
     44     const MCFixup &Fixup, unsigned &RelocType, const MCSymbolRefExpr *Sym,
     45     unsigned &Log2Size, const MCAssembler &Asm) {
     46   RelocType = unsigned(MachO::ARM64_RELOC_UNSIGNED);
     47   Log2Size = ~0U;
     48 
     49   switch ((unsigned)Fixup.getKind()) {
     50   default:
     51     return false;
     52 
     53   case FK_Data_1:
     54     Log2Size = llvm::Log2_32(1);
     55     return true;
     56   case FK_Data_2:
     57     Log2Size = llvm::Log2_32(2);
     58     return true;
     59   case FK_Data_4:
     60     Log2Size = llvm::Log2_32(4);
     61     if (Sym->getKind() == MCSymbolRefExpr::VK_GOT)
     62       RelocType = unsigned(MachO::ARM64_RELOC_POINTER_TO_GOT);
     63     return true;
     64   case FK_Data_8:
     65     Log2Size = llvm::Log2_32(8);
     66     if (Sym->getKind() == MCSymbolRefExpr::VK_GOT)
     67       RelocType = unsigned(MachO::ARM64_RELOC_POINTER_TO_GOT);
     68     return true;
     69   case AArch64::fixup_aarch64_add_imm12:
     70   case AArch64::fixup_aarch64_ldst_imm12_scale1:
     71   case AArch64::fixup_aarch64_ldst_imm12_scale2:
     72   case AArch64::fixup_aarch64_ldst_imm12_scale4:
     73   case AArch64::fixup_aarch64_ldst_imm12_scale8:
     74   case AArch64::fixup_aarch64_ldst_imm12_scale16:
     75     Log2Size = llvm::Log2_32(4);
     76     switch (Sym->getKind()) {
     77     default:
     78       llvm_unreachable("Unexpected symbol reference variant kind!");
     79     case MCSymbolRefExpr::VK_PAGEOFF:
     80       RelocType = unsigned(MachO::ARM64_RELOC_PAGEOFF12);
     81       return true;
     82     case MCSymbolRefExpr::VK_GOTPAGEOFF:
     83       RelocType = unsigned(MachO::ARM64_RELOC_GOT_LOAD_PAGEOFF12);
     84       return true;
     85     case MCSymbolRefExpr::VK_TLVPPAGEOFF:
     86       RelocType = unsigned(MachO::ARM64_RELOC_TLVP_LOAD_PAGEOFF12);
     87       return true;
     88     }
     89   case AArch64::fixup_aarch64_pcrel_adrp_imm21:
     90     Log2Size = llvm::Log2_32(4);
     91     // This encompasses the relocation for the whole 21-bit value.
     92     switch (Sym->getKind()) {
     93     default: {
     94       Asm.getContext().reportError(Fixup.getLoc(),
     95                                    "ADR/ADRP relocations must be GOT relative");
     96       return false;
     97     }
     98     case MCSymbolRefExpr::VK_PAGE:
     99       RelocType = unsigned(MachO::ARM64_RELOC_PAGE21);
    100       return true;
    101     case MCSymbolRefExpr::VK_GOTPAGE:
    102       RelocType = unsigned(MachO::ARM64_RELOC_GOT_LOAD_PAGE21);
    103       return true;
    104     case MCSymbolRefExpr::VK_TLVPPAGE:
    105       RelocType = unsigned(MachO::ARM64_RELOC_TLVP_LOAD_PAGE21);
    106       return true;
    107     }
    108     return true;
    109   case AArch64::fixup_aarch64_pcrel_branch26:
    110   case AArch64::fixup_aarch64_pcrel_call26:
    111     Log2Size = llvm::Log2_32(4);
    112     RelocType = unsigned(MachO::ARM64_RELOC_BRANCH26);
    113     return true;
    114   }
    115 }
    116 
    117 static bool canUseLocalRelocation(const MCSectionMachO &Section,
    118                                   const MCSymbol &Symbol, unsigned Log2Size) {
    119   // Debug info sections can use local relocations.
    120   if (Section.hasAttribute(MachO::S_ATTR_DEBUG))
    121     return true;
    122 
    123   // Otherwise, only pointer sized relocations are supported.
    124   if (Log2Size != 3)
    125     return false;
    126 
    127   // But only if they don't point to a few forbidden sections.
    128   if (!Symbol.isInSection())
    129     return true;
    130   const MCSectionMachO &RefSec = cast<MCSectionMachO>(Symbol.getSection());
    131   if (RefSec.getType() == MachO::S_CSTRING_LITERALS)
    132     return false;
    133 
    134   if (RefSec.getSegmentName() == "__DATA" &&
    135       RefSec.getSectionName() == "__objc_classrefs")
    136     return false;
    137 
    138   // FIXME: ld64 currently handles internal pointer-sized relocations
    139   // incorrectly (applying the addend twice). We should be able to return true
    140   // unconditionally by this point when that's fixed.
    141   return false;
    142 }
    143 
    144 void AArch64MachObjectWriter::recordRelocation(
    145     MachObjectWriter *Writer, MCAssembler &Asm, const MCAsmLayout &Layout,
    146     const MCFragment *Fragment, const MCFixup &Fixup, MCValue Target,
    147     uint64_t &FixedValue) {
    148   unsigned IsPCRel = Writer->isFixupKindPCRel(Asm, Fixup.getKind());
    149 
    150   // See <reloc.h>.
    151   uint32_t FixupOffset = Layout.getFragmentOffset(Fragment);
    152   unsigned Log2Size = 0;
    153   int64_t Value = 0;
    154   unsigned Index = 0;
    155   unsigned Type = 0;
    156   unsigned Kind = Fixup.getKind();
    157   const MCSymbol *RelSymbol = nullptr;
    158 
    159   FixupOffset += Fixup.getOffset();
    160 
    161   // AArch64 pcrel relocation addends do not include the section offset.
    162   if (IsPCRel)
    163     FixedValue += FixupOffset;
    164 
    165   // ADRP fixups use relocations for the whole symbol value and only
    166   // put the addend in the instruction itself. Clear out any value the
    167   // generic code figured out from the sybmol definition.
    168   if (Kind == AArch64::fixup_aarch64_pcrel_adrp_imm21)
    169     FixedValue = 0;
    170 
    171   // imm19 relocations are for conditional branches, which require
    172   // assembler local symbols. If we got here, that's not what we have,
    173   // so complain loudly.
    174   if (Kind == AArch64::fixup_aarch64_pcrel_branch19) {
    175     Asm.getContext().reportError(Fixup.getLoc(),
    176                                  "conditional branch requires assembler-local"
    177                                  " label. '" +
    178                                      Target.getSymA()->getSymbol().getName() +
    179                                      "' is external.");
    180     return;
    181   }
    182 
    183   // 14-bit branch relocations should only target internal labels, and so
    184   // should never get here.
    185   if (Kind == AArch64::fixup_aarch64_pcrel_branch14) {
    186     Asm.getContext().reportError(Fixup.getLoc(),
    187                                  "Invalid relocation on conditional branch!");
    188     return;
    189   }
    190 
    191   if (!getAArch64FixupKindMachOInfo(Fixup, Type, Target.getSymA(), Log2Size,
    192                                     Asm)) {
    193     Asm.getContext().reportError(Fixup.getLoc(), "unknown AArch64 fixup kind!");
    194     return;
    195   }
    196 
    197   Value = Target.getConstant();
    198 
    199   if (Target.isAbsolute()) { // constant
    200     // FIXME: Should this always be extern?
    201     // SymbolNum of 0 indicates the absolute section.
    202     Type = MachO::ARM64_RELOC_UNSIGNED;
    203 
    204     if (IsPCRel) {
    205       Asm.getContext().reportError(Fixup.getLoc(),
    206                                    "PC relative absolute relocation!");
    207       return;
    208 
    209       // FIXME: x86_64 sets the type to a branch reloc here. Should we do
    210       // something similar?
    211     }
    212   } else if (Target.getSymB()) { // A - B + constant
    213     const MCSymbol *A = &Target.getSymA()->getSymbol();
    214     const MCSymbol *A_Base = Asm.getAtom(*A);
    215 
    216     const MCSymbol *B = &Target.getSymB()->getSymbol();
    217     const MCSymbol *B_Base = Asm.getAtom(*B);
    218 
    219     // Check for "_foo@got - .", which comes through here as:
    220     // Ltmp0:
    221     //    ... _foo@got - Ltmp0
    222     if (Target.getSymA()->getKind() == MCSymbolRefExpr::VK_GOT &&
    223         Target.getSymB()->getKind() == MCSymbolRefExpr::VK_None &&
    224         Layout.getSymbolOffset(*B) ==
    225             Layout.getFragmentOffset(Fragment) + Fixup.getOffset()) {
    226       // SymB is the PC, so use a PC-rel pointer-to-GOT relocation.
    227       Type = MachO::ARM64_RELOC_POINTER_TO_GOT;
    228       IsPCRel = 1;
    229       MachO::any_relocation_info MRE;
    230       MRE.r_word0 = FixupOffset;
    231       MRE.r_word1 = (IsPCRel << 24) | (Log2Size << 25) | (Type << 28);
    232       Writer->addRelocation(A_Base, Fragment->getParent(), MRE);
    233       return;
    234     } else if (Target.getSymA()->getKind() != MCSymbolRefExpr::VK_None ||
    235                Target.getSymB()->getKind() != MCSymbolRefExpr::VK_None) {
    236       // Otherwise, neither symbol can be modified.
    237       Asm.getContext().reportError(Fixup.getLoc(),
    238                                    "unsupported relocation of modified symbol");
    239       return;
    240     }
    241 
    242     // We don't support PCrel relocations of differences.
    243     if (IsPCRel) {
    244       Asm.getContext().reportError(Fixup.getLoc(),
    245                                    "unsupported pc-relative relocation of "
    246                                    "difference");
    247       return;
    248     }
    249 
    250     // AArch64 always uses external relocations. If there is no symbol to use as
    251     // a base address (a local symbol with no preceding non-local symbol),
    252     // error out.
    253     //
    254     // FIXME: We should probably just synthesize an external symbol and use
    255     // that.
    256     if (!A_Base) {
    257       Asm.getContext().reportError(
    258           Fixup.getLoc(),
    259           "unsupported relocation of local symbol '" + A->getName() +
    260               "'. Must have non-local symbol earlier in section.");
    261       return;
    262     }
    263     if (!B_Base) {
    264       Asm.getContext().reportError(
    265           Fixup.getLoc(),
    266           "unsupported relocation of local symbol '" + B->getName() +
    267               "'. Must have non-local symbol earlier in section.");
    268       return;
    269     }
    270 
    271     if (A_Base == B_Base && A_Base) {
    272       Asm.getContext().reportError(
    273           Fixup.getLoc(), "unsupported relocation with identical base");
    274       return;
    275     }
    276 
    277     Value += (!A->getFragment() ? 0 : Writer->getSymbolAddress(*A, Layout)) -
    278              (!A_Base || !A_Base->getFragment() ? 0 : Writer->getSymbolAddress(
    279                                                           *A_Base, Layout));
    280     Value -= (!B->getFragment() ? 0 : Writer->getSymbolAddress(*B, Layout)) -
    281              (!B_Base || !B_Base->getFragment() ? 0 : Writer->getSymbolAddress(
    282                                                           *B_Base, Layout));
    283 
    284     Type = MachO::ARM64_RELOC_UNSIGNED;
    285 
    286     MachO::any_relocation_info MRE;
    287     MRE.r_word0 = FixupOffset;
    288     MRE.r_word1 = (IsPCRel << 24) | (Log2Size << 25) | (Type << 28);
    289     Writer->addRelocation(A_Base, Fragment->getParent(), MRE);
    290 
    291     RelSymbol = B_Base;
    292     Type = MachO::ARM64_RELOC_SUBTRACTOR;
    293   } else { // A + constant
    294     const MCSymbol *Symbol = &Target.getSymA()->getSymbol();
    295     const MCSectionMachO &Section =
    296         static_cast<const MCSectionMachO &>(*Fragment->getParent());
    297 
    298     bool CanUseLocalRelocation =
    299         canUseLocalRelocation(Section, *Symbol, Log2Size);
    300     if (Symbol->isTemporary() && (Value || !CanUseLocalRelocation)) {
    301       const MCSection &Sec = Symbol->getSection();
    302       if (!Asm.getContext().getAsmInfo()->isSectionAtomizableBySymbols(Sec))
    303         Symbol->setUsedInReloc();
    304     }
    305 
    306     const MCSymbol *Base = Asm.getAtom(*Symbol);
    307 
    308     // If the symbol is a variable and we weren't able to get a Base for it
    309     // (i.e., it's not in the symbol table associated with a section) resolve
    310     // the relocation based its expansion instead.
    311     if (Symbol->isVariable() && !Base) {
    312       // If the evaluation is an absolute value, just use that directly
    313       // to keep things easy.
    314       int64_t Res;
    315       if (Symbol->getVariableValue()->evaluateAsAbsolute(
    316               Res, Layout, Writer->getSectionAddressMap())) {
    317         FixedValue = Res;
    318         return;
    319       }
    320 
    321       // FIXME: Will the Target we already have ever have any data in it
    322       // we need to preserve and merge with the new Target? How about
    323       // the FixedValue?
    324       if (!Symbol->getVariableValue()->evaluateAsRelocatable(Target, &Layout,
    325                                                              &Fixup)) {
    326         Asm.getContext().reportError(Fixup.getLoc(),
    327                                      "unable to resolve variable '" +
    328                                          Symbol->getName() + "'");
    329         return;
    330       }
    331       return recordRelocation(Writer, Asm, Layout, Fragment, Fixup, Target,
    332                               FixedValue);
    333     }
    334 
    335     // Relocations inside debug sections always use local relocations when
    336     // possible. This seems to be done because the debugger doesn't fully
    337     // understand relocation entries and expects to find values that
    338     // have already been fixed up.
    339     if (Symbol->isInSection()) {
    340       if (Section.hasAttribute(MachO::S_ATTR_DEBUG))
    341         Base = nullptr;
    342     }
    343 
    344     // AArch64 uses external relocations as much as possible. For debug
    345     // sections, and for pointer-sized relocations (.quad), we allow section
    346     // relocations.  It's code sections that run into trouble.
    347     if (Base) {
    348       RelSymbol = Base;
    349 
    350       // Add the local offset, if needed.
    351       if (Base != Symbol)
    352         Value +=
    353             Layout.getSymbolOffset(*Symbol) - Layout.getSymbolOffset(*Base);
    354     } else if (Symbol->isInSection()) {
    355       if (!CanUseLocalRelocation) {
    356         Asm.getContext().reportError(
    357             Fixup.getLoc(),
    358             "unsupported relocation of local symbol '" + Symbol->getName() +
    359                 "'. Must have non-local symbol earlier in section.");
    360         return;
    361       }
    362       // Adjust the relocation to be section-relative.
    363       // The index is the section ordinal (1-based).
    364       const MCSection &Sec = Symbol->getSection();
    365       Index = Sec.getOrdinal() + 1;
    366       Value += Writer->getSymbolAddress(*Symbol, Layout);
    367 
    368       if (IsPCRel)
    369         Value -= Writer->getFragmentAddress(Fragment, Layout) +
    370                  Fixup.getOffset() + (1ULL << Log2Size);
    371     } else {
    372       // Resolve constant variables.
    373       if (Symbol->isVariable()) {
    374         int64_t Res;
    375         if (Symbol->getVariableValue()->evaluateAsAbsolute(
    376                 Res, Layout, Writer->getSectionAddressMap())) {
    377           FixedValue = Res;
    378           return;
    379         }
    380       }
    381       Asm.getContext().reportError(Fixup.getLoc(),
    382                                   "unsupported relocation of variable '" +
    383                                       Symbol->getName() + "'");
    384       return;
    385     }
    386   }
    387 
    388   // If the relocation kind is Branch26, Page21, or Pageoff12, any addend
    389   // is represented via an Addend relocation, not encoded directly into
    390   // the instruction.
    391   if ((Type == MachO::ARM64_RELOC_BRANCH26 ||
    392        Type == MachO::ARM64_RELOC_PAGE21 ||
    393        Type == MachO::ARM64_RELOC_PAGEOFF12) &&
    394       Value) {
    395     assert((Value & 0xff000000) == 0 && "Added relocation out of range!");
    396 
    397     MachO::any_relocation_info MRE;
    398     MRE.r_word0 = FixupOffset;
    399     MRE.r_word1 =
    400         (Index << 0) | (IsPCRel << 24) | (Log2Size << 25) | (Type << 28);
    401     Writer->addRelocation(RelSymbol, Fragment->getParent(), MRE);
    402 
    403     // Now set up the Addend relocation.
    404     Type = MachO::ARM64_RELOC_ADDEND;
    405     Index = Value;
    406     RelSymbol = nullptr;
    407     IsPCRel = 0;
    408     Log2Size = 2;
    409 
    410     // Put zero into the instruction itself. The addend is in the relocation.
    411     Value = 0;
    412   }
    413 
    414   // If there's any addend left to handle, encode it in the instruction.
    415   FixedValue = Value;
    416 
    417   // struct relocation_info (8 bytes)
    418   MachO::any_relocation_info MRE;
    419   MRE.r_word0 = FixupOffset;
    420   MRE.r_word1 =
    421       (Index << 0) | (IsPCRel << 24) | (Log2Size << 25) | (Type << 28);
    422   Writer->addRelocation(RelSymbol, Fragment->getParent(), MRE);
    423 }
    424 
    425 MCObjectWriter *llvm::createAArch64MachObjectWriter(raw_pwrite_stream &OS,
    426                                                     uint32_t CPUType,
    427                                                     uint32_t CPUSubtype) {
    428   return createMachObjectWriter(
    429       new AArch64MachObjectWriter(CPUType, CPUSubtype), OS,
    430       /*IsLittleEndian=*/true);
    431 }
    432