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