LCOV - code coverage report
Current view: top level - lib/Target/AArch64/MCTargetDesc - AArch64MachObjectWriter.cpp (source / functions) Hit Total Coverage
Test: llvm-toolchain.info Lines: 149 193 77.2 %
Date: 2017-09-14 15:23:50 Functions: 5 6 83.3 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13