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/MC/MCAssembler.h"
     13 #include "llvm/MC/MCAsmLayout.h"
     14 #include "llvm/MC/MCContext.h"
     15 #include "llvm/MC/MCExpr.h"
     16 #include "llvm/MC/MCFixup.h"
     17 #include "llvm/MC/MCMachObjectWriter.h"
     18 #include "llvm/MC/MCSectionMachO.h"
     19 #include "llvm/MC/MCValue.h"
     20 #include "llvm/ADT/Twine.h"
     21 #include "llvm/Support/ErrorHandling.h"
     22 #include "llvm/Support/MachO.h"
     23 using namespace llvm;
     24 
     25 namespace {
     26 class AArch64MachObjectWriter : public MCMachObjectTargetWriter {
     27   bool getAArch64FixupKindMachOInfo(const MCFixup &Fixup, unsigned &RelocType,
     28                                   const MCSymbolRefExpr *Sym,
     29                                   unsigned &Log2Size, const MCAssembler &Asm);
     30 
     31 public:
     32   AArch64MachObjectWriter(uint32_t CPUType, uint32_t CPUSubtype)
     33       : MCMachObjectTargetWriter(true /* is64Bit */, CPUType, CPUSubtype,
     34                                  /*UseAggressiveSymbolFolding=*/true) {}
     35 
     36   void RecordRelocation(MachObjectWriter *Writer, const 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().FatalError(Fixup.getLoc(),
     95                                   "ADR/ADRP relocations must be GOT relative");
     96     case MCSymbolRefExpr::VK_PAGE:
     97       RelocType = unsigned(MachO::ARM64_RELOC_PAGE21);
     98       return true;
     99     case MCSymbolRefExpr::VK_GOTPAGE:
    100       RelocType = unsigned(MachO::ARM64_RELOC_GOT_LOAD_PAGE21);
    101       return true;
    102     case MCSymbolRefExpr::VK_TLVPPAGE:
    103       RelocType = unsigned(MachO::ARM64_RELOC_TLVP_LOAD_PAGE21);
    104       return true;
    105     }
    106     return true;
    107   case AArch64::fixup_aarch64_pcrel_branch26:
    108   case AArch64::fixup_aarch64_pcrel_call26:
    109     Log2Size = llvm::Log2_32(4);
    110     RelocType = unsigned(MachO::ARM64_RELOC_BRANCH26);
    111     return true;
    112   }
    113 }
    114 
    115 void AArch64MachObjectWriter::RecordRelocation(
    116     MachObjectWriter *Writer, const MCAssembler &Asm, const MCAsmLayout &Layout,
    117     const MCFragment *Fragment, const MCFixup &Fixup, MCValue Target,
    118     uint64_t &FixedValue) {
    119   unsigned IsPCRel = Writer->isFixupKindPCRel(Asm, Fixup.getKind());
    120 
    121   // See <reloc.h>.
    122   uint32_t FixupOffset = Layout.getFragmentOffset(Fragment);
    123   unsigned Log2Size = 0;
    124   int64_t Value = 0;
    125   unsigned Index = 0;
    126   unsigned IsExtern = 0;
    127   unsigned Type = 0;
    128   unsigned Kind = Fixup.getKind();
    129 
    130   FixupOffset += Fixup.getOffset();
    131 
    132   // AArch64 pcrel relocation addends do not include the section offset.
    133   if (IsPCRel)
    134     FixedValue += FixupOffset;
    135 
    136   // ADRP fixups use relocations for the whole symbol value and only
    137   // put the addend in the instruction itself. Clear out any value the
    138   // generic code figured out from the sybmol definition.
    139   if (Kind == AArch64::fixup_aarch64_pcrel_adrp_imm21)
    140     FixedValue = 0;
    141 
    142   // imm19 relocations are for conditional branches, which require
    143   // assembler local symbols. If we got here, that's not what we have,
    144   // so complain loudly.
    145   if (Kind == AArch64::fixup_aarch64_pcrel_branch19) {
    146     Asm.getContext().FatalError(Fixup.getLoc(),
    147                                 "conditional branch requires assembler-local"
    148                                 " label. '" +
    149                                     Target.getSymA()->getSymbol().getName() +
    150                                     "' is external.");
    151     return;
    152   }
    153 
    154   // 14-bit branch relocations should only target internal labels, and so
    155   // should never get here.
    156   if (Kind == AArch64::fixup_aarch64_pcrel_branch14) {
    157     Asm.getContext().FatalError(Fixup.getLoc(),
    158                                 "Invalid relocation on conditional branch!");
    159     return;
    160   }
    161 
    162   if (!getAArch64FixupKindMachOInfo(Fixup, Type, Target.getSymA(), Log2Size,
    163                                   Asm)) {
    164     Asm.getContext().FatalError(Fixup.getLoc(), "unknown AArch64 fixup kind!");
    165     return;
    166   }
    167 
    168   Value = Target.getConstant();
    169 
    170   if (Target.isAbsolute()) { // constant
    171     // FIXME: Should this always be extern?
    172     // SymbolNum of 0 indicates the absolute section.
    173     Type = MachO::ARM64_RELOC_UNSIGNED;
    174     Index = 0;
    175 
    176     if (IsPCRel) {
    177       IsExtern = 1;
    178       Asm.getContext().FatalError(Fixup.getLoc(),
    179                                   "PC relative absolute relocation!");
    180 
    181       // FIXME: x86_64 sets the type to a branch reloc here. Should we do
    182       // something similar?
    183     }
    184   } else if (Target.getSymB()) { // A - B + constant
    185     const MCSymbol *A = &Target.getSymA()->getSymbol();
    186     const MCSymbolData &A_SD = Asm.getSymbolData(*A);
    187     const MCSymbolData *A_Base = Asm.getAtom(&A_SD);
    188 
    189     const MCSymbol *B = &Target.getSymB()->getSymbol();
    190     const MCSymbolData &B_SD = Asm.getSymbolData(*B);
    191     const MCSymbolData *B_Base = Asm.getAtom(&B_SD);
    192 
    193     // Check for "_foo@got - .", which comes through here as:
    194     // Ltmp0:
    195     //    ... _foo@got - Ltmp0
    196     if (Target.getSymA()->getKind() == MCSymbolRefExpr::VK_GOT &&
    197         Target.getSymB()->getKind() == MCSymbolRefExpr::VK_None &&
    198         Layout.getSymbolOffset(&B_SD) ==
    199             Layout.getFragmentOffset(Fragment) + Fixup.getOffset()) {
    200       // SymB is the PC, so use a PC-rel pointer-to-GOT relocation.
    201       Index = A_Base->getIndex();
    202       IsExtern = 1;
    203       Type = MachO::ARM64_RELOC_POINTER_TO_GOT;
    204       IsPCRel = 1;
    205       MachO::any_relocation_info MRE;
    206       MRE.r_word0 = FixupOffset;
    207       MRE.r_word1 = ((Index << 0) | (IsPCRel << 24) | (Log2Size << 25) |
    208                      (IsExtern << 27) | (Type << 28));
    209       Writer->addRelocation(Fragment->getParent(), MRE);
    210       return;
    211     } else if (Target.getSymA()->getKind() != MCSymbolRefExpr::VK_None ||
    212                Target.getSymB()->getKind() != MCSymbolRefExpr::VK_None)
    213       // Otherwise, neither symbol can be modified.
    214       Asm.getContext().FatalError(Fixup.getLoc(),
    215                                   "unsupported relocation of modified symbol");
    216 
    217     // We don't support PCrel relocations of differences.
    218     if (IsPCRel)
    219       Asm.getContext().FatalError(Fixup.getLoc(),
    220                                   "unsupported pc-relative relocation of "
    221                                   "difference");
    222 
    223     // AArch64 always uses external relocations. If there is no symbol to use as
    224     // a base address (a local symbol with no preceding non-local symbol),
    225     // error out.
    226     //
    227     // FIXME: We should probably just synthesize an external symbol and use
    228     // that.
    229     if (!A_Base)
    230       Asm.getContext().FatalError(
    231           Fixup.getLoc(),
    232           "unsupported relocation of local symbol '" + A->getName() +
    233               "'. Must have non-local symbol earlier in section.");
    234     if (!B_Base)
    235       Asm.getContext().FatalError(
    236           Fixup.getLoc(),
    237           "unsupported relocation of local symbol '" + B->getName() +
    238               "'. Must have non-local symbol earlier in section.");
    239 
    240     if (A_Base == B_Base && A_Base)
    241       Asm.getContext().FatalError(Fixup.getLoc(),
    242                                   "unsupported relocation with identical base");
    243 
    244     Value += (!A_SD.getFragment() ? 0
    245                                   : Writer->getSymbolAddress(&A_SD, Layout)) -
    246              (!A_Base || !A_Base->getFragment()
    247                   ? 0
    248                   : Writer->getSymbolAddress(A_Base, Layout));
    249     Value -= (!B_SD.getFragment() ? 0
    250                                   : Writer->getSymbolAddress(&B_SD, Layout)) -
    251              (!B_Base || !B_Base->getFragment()
    252                   ? 0
    253                   : Writer->getSymbolAddress(B_Base, Layout));
    254 
    255     Index = A_Base->getIndex();
    256     IsExtern = 1;
    257     Type = MachO::ARM64_RELOC_UNSIGNED;
    258 
    259     MachO::any_relocation_info MRE;
    260     MRE.r_word0 = FixupOffset;
    261     MRE.r_word1 = ((Index << 0) | (IsPCRel << 24) | (Log2Size << 25) |
    262                    (IsExtern << 27) | (Type << 28));
    263     Writer->addRelocation(Fragment->getParent(), MRE);
    264 
    265     Index = B_Base->getIndex();
    266     IsExtern = 1;
    267     Type = MachO::ARM64_RELOC_SUBTRACTOR;
    268   } else { // A + constant
    269     const MCSymbol *Symbol = &Target.getSymA()->getSymbol();
    270     const MCSymbolData &SD = Asm.getSymbolData(*Symbol);
    271     const MCSymbolData *Base = Asm.getAtom(&SD);
    272     const MCSectionMachO &Section = static_cast<const MCSectionMachO &>(
    273         Fragment->getParent()->getSection());
    274 
    275     // If the symbol is a variable and we weren't able to get a Base for it
    276     // (i.e., it's not in the symbol table associated with a section) resolve
    277     // the relocation based its expansion instead.
    278     if (Symbol->isVariable() && !Base) {
    279       // If the evaluation is an absolute value, just use that directly
    280       // to keep things easy.
    281       int64_t Res;
    282       if (SD.getSymbol().getVariableValue()->EvaluateAsAbsolute(
    283               Res, Layout, Writer->getSectionAddressMap())) {
    284         FixedValue = Res;
    285         return;
    286       }
    287 
    288       // FIXME: Will the Target we already have ever have any data in it
    289       // we need to preserve and merge with the new Target? How about
    290       // the FixedValue?
    291       if (!Symbol->getVariableValue()->EvaluateAsRelocatable(Target, &Layout))
    292         Asm.getContext().FatalError(Fixup.getLoc(),
    293                                     "unable to resolve variable '" +
    294                                         Symbol->getName() + "'");
    295       return RecordRelocation(Writer, Asm, Layout, Fragment, Fixup, Target,
    296                               FixedValue);
    297     }
    298 
    299     // Relocations inside debug sections always use local relocations when
    300     // possible. This seems to be done because the debugger doesn't fully
    301     // understand relocation entries and expects to find values that
    302     // have already been fixed up.
    303     if (Symbol->isInSection()) {
    304       if (Section.hasAttribute(MachO::S_ATTR_DEBUG))
    305         Base = nullptr;
    306     }
    307 
    308     // AArch64 uses external relocations as much as possible. For debug
    309     // sections, and for pointer-sized relocations (.quad), we allow section
    310     // relocations.  It's code sections that run into trouble.
    311     if (Base) {
    312       Index = Base->getIndex();
    313       IsExtern = 1;
    314 
    315       // Add the local offset, if needed.
    316       if (Base != &SD)
    317         Value += Layout.getSymbolOffset(&SD) - Layout.getSymbolOffset(Base);
    318     } else if (Symbol->isInSection()) {
    319       // Pointer-sized relocations can use a local relocation. Otherwise,
    320       // we have to be in a debug info section.
    321       if (!Section.hasAttribute(MachO::S_ATTR_DEBUG) && Log2Size != 3)
    322         Asm.getContext().FatalError(
    323             Fixup.getLoc(),
    324             "unsupported relocation of local symbol '" + Symbol->getName() +
    325                 "'. Must have non-local symbol earlier in section.");
    326       // Adjust the relocation to be section-relative.
    327       // The index is the section ordinal (1-based).
    328       const MCSectionData &SymSD =
    329           Asm.getSectionData(SD.getSymbol().getSection());
    330       Index = SymSD.getOrdinal() + 1;
    331       IsExtern = 0;
    332       Value += Writer->getSymbolAddress(&SD, Layout);
    333 
    334       if (IsPCRel)
    335         Value -= Writer->getFragmentAddress(Fragment, Layout) +
    336                  Fixup.getOffset() + (1ULL << Log2Size);
    337     } else {
    338       // Resolve constant variables.
    339       if (SD.getSymbol().isVariable()) {
    340         int64_t Res;
    341         if (SD.getSymbol().getVariableValue()->EvaluateAsAbsolute(
    342                 Res, Layout, Writer->getSectionAddressMap())) {
    343           FixedValue = Res;
    344           return;
    345         }
    346       }
    347       Asm.getContext().FatalError(Fixup.getLoc(),
    348                                   "unsupported relocation of variable '" +
    349                                       Symbol->getName() + "'");
    350     }
    351   }
    352 
    353   // If the relocation kind is Branch26, Page21, or Pageoff12, any addend
    354   // is represented via an Addend relocation, not encoded directly into
    355   // the instruction.
    356   if ((Type == MachO::ARM64_RELOC_BRANCH26 ||
    357        Type == MachO::ARM64_RELOC_PAGE21 ||
    358        Type == MachO::ARM64_RELOC_PAGEOFF12) &&
    359       Value) {
    360     assert((Value & 0xff000000) == 0 && "Added relocation out of range!");
    361 
    362     MachO::any_relocation_info MRE;
    363     MRE.r_word0 = FixupOffset;
    364     MRE.r_word1 = ((Index << 0) | (IsPCRel << 24) | (Log2Size << 25) |
    365                    (IsExtern << 27) | (Type << 28));
    366     Writer->addRelocation(Fragment->getParent(), MRE);
    367 
    368     // Now set up the Addend relocation.
    369     Type = MachO::ARM64_RELOC_ADDEND;
    370     Index = Value;
    371     IsPCRel = 0;
    372     Log2Size = 2;
    373     IsExtern = 0;
    374 
    375     // Put zero into the instruction itself. The addend is in the relocation.
    376     Value = 0;
    377   }
    378 
    379   // If there's any addend left to handle, encode it in the instruction.
    380   FixedValue = Value;
    381 
    382   // struct relocation_info (8 bytes)
    383   MachO::any_relocation_info MRE;
    384   MRE.r_word0 = FixupOffset;
    385   MRE.r_word1 = ((Index << 0) | (IsPCRel << 24) | (Log2Size << 25) |
    386                  (IsExtern << 27) | (Type << 28));
    387   Writer->addRelocation(Fragment->getParent(), MRE);
    388 }
    389 
    390 MCObjectWriter *llvm::createAArch64MachObjectWriter(raw_ostream &OS,
    391                                                   uint32_t CPUType,
    392                                                   uint32_t CPUSubtype) {
    393   return createMachObjectWriter(
    394       new AArch64MachObjectWriter(CPUType, CPUSubtype), OS,
    395       /*IsLittleEndian=*/true);
    396 }
    397