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"
24using namespace llvm;
25
26namespace {
27class AArch64MachObjectWriter : public MCMachObjectTargetWriter {
28  bool getAArch64FixupKindMachOInfo(const MCFixup &Fixup, unsigned &RelocType,
29                                  const MCSymbolRefExpr *Sym,
30                                  unsigned &Log2Size, const MCAssembler &Asm);
31
32public:
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
43bool 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
117static 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
144void 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
425MCObjectWriter *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