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 
30 using namespace llvm;
31 
32 namespace {
33 
34 class AArch64MachObjectWriter : public MCMachObjectTargetWriter {
35   bool getAArch64FixupKindMachOInfo(const MCFixup &Fixup, unsigned &RelocType,
36                                   const MCSymbolRefExpr *Sym,
37                                   unsigned &Log2Size, const MCAssembler &Asm);
38 
39 public:
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 
51 bool 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 
124 static 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.getName() == "__cfstring" ||
143        RefSec.getName() == "__objc_classrefs"))
144     return false;
145 
146   return true;
147 }
148 
149 void AArch64MachObjectWriter::recordRelocation(
150     MachObjectWriter *Writer, MCAssembler &Asm, const MCAsmLayout &Layout,
151     const MCFragment *Fragment, const MCFixup &Fixup, MCValue Target,
152     uint64_t &FixedValue) {
153   unsigned IsPCRel = Writer->isFixupKindPCRel(Asm, Fixup.getKind());
154 
155   // See <reloc.h>.
156   uint32_t FixupOffset = Layout.getFragmentOffset(Fragment);
157   unsigned Log2Size = 0;
158   int64_t Value = 0;
159   unsigned Index = 0;
160   unsigned Type = 0;
161   unsigned Kind = Fixup.getKind();
162   const MCSymbol *RelSymbol = nullptr;
163 
164   FixupOffset += Fixup.getOffset();
165 
166   // AArch64 pcrel relocation addends do not include the section offset.
167   if (IsPCRel)
168     FixedValue += FixupOffset;
169 
170   // ADRP fixups use relocations for the whole symbol value and only
171   // put the addend in the instruction itself. Clear out any value the
172   // generic code figured out from the sybmol definition.
173   if (Kind == AArch64::fixup_aarch64_pcrel_adrp_imm21)
174     FixedValue = 0;
175 
176   // imm19 relocations are for conditional branches, which require
177   // assembler local symbols. If we got here, that's not what we have,
178   // so complain loudly.
179   if (Kind == AArch64::fixup_aarch64_pcrel_branch19) {
180     Asm.getContext().reportError(Fixup.getLoc(),
181                                  "conditional branch requires assembler-local"
182                                  " label. '" +
183                                      Target.getSymA()->getSymbol().getName() +
184                                      "' is external.");
185     return;
186   }
187 
188   // 14-bit branch relocations should only target internal labels, and so
189   // should never get here.
190   if (Kind == AArch64::fixup_aarch64_pcrel_branch14) {
191     Asm.getContext().reportError(Fixup.getLoc(),
192                                  "Invalid relocation on conditional branch!");
193     return;
194   }
195 
196   if (!getAArch64FixupKindMachOInfo(Fixup, Type, Target.getSymA(), Log2Size,
197                                     Asm)) {
198     Asm.getContext().reportError(Fixup.getLoc(), "unknown AArch64 fixup kind!");
199     return;
200   }
201 
202   Value = Target.getConstant();
203 
204   if (Target.isAbsolute()) { // constant
205     // FIXME: Should this always be extern?
206     // SymbolNum of 0 indicates the absolute section.
207     Type = MachO::ARM64_RELOC_UNSIGNED;
208 
209     if (IsPCRel) {
210       Asm.getContext().reportError(Fixup.getLoc(),
211                                    "PC relative absolute relocation!");
212       return;
213 
214       // FIXME: x86_64 sets the type to a branch reloc here. Should we do
215       // something similar?
216     }
217   } else if (Target.getSymB()) { // A - B + constant
218     const MCSymbol *A = &Target.getSymA()->getSymbol();
219     const MCSymbol *A_Base = Asm.getAtom(*A);
220 
221     const MCSymbol *B = &Target.getSymB()->getSymbol();
222     const MCSymbol *B_Base = Asm.getAtom(*B);
223 
224     // Check for "_foo@got - .", which comes through here as:
225     // Ltmp0:
226     //    ... _foo@got - Ltmp0
227     if (Target.getSymA()->getKind() == MCSymbolRefExpr::VK_GOT &&
228         Target.getSymB()->getKind() == MCSymbolRefExpr::VK_None &&
229         Layout.getSymbolOffset(*B) ==
230             Layout.getFragmentOffset(Fragment) + Fixup.getOffset()) {
231       // SymB is the PC, so use a PC-rel pointer-to-GOT relocation.
232       Type = MachO::ARM64_RELOC_POINTER_TO_GOT;
233       IsPCRel = 1;
234       MachO::any_relocation_info MRE;
235       MRE.r_word0 = FixupOffset;
236       MRE.r_word1 = (IsPCRel << 24) | (Log2Size << 25) | (Type << 28);
237       Writer->addRelocation(A_Base, Fragment->getParent(), MRE);
238       return;
239     } else if (Target.getSymA()->getKind() != MCSymbolRefExpr::VK_None ||
240                Target.getSymB()->getKind() != MCSymbolRefExpr::VK_None) {
241       // Otherwise, neither symbol can be modified.
242       Asm.getContext().reportError(Fixup.getLoc(),
243                                    "unsupported relocation of modified symbol");
244       return;
245     }
246 
247     // We don't support PCrel relocations of differences.
248     if (IsPCRel) {
249       Asm.getContext().reportError(Fixup.getLoc(),
250                                    "unsupported pc-relative relocation of "
251                                    "difference");
252       return;
253     }
254 
255     // AArch64 always uses external relocations. If there is no symbol to use as
256     // a base address (a local symbol with no preceding non-local symbol),
257     // error out.
258     //
259     // FIXME: We should probably just synthesize an external symbol and use
260     // that.
261     if (!A_Base) {
262       Asm.getContext().reportError(
263           Fixup.getLoc(),
264           "unsupported relocation of local symbol '" + A->getName() +
265               "'. Must have non-local symbol earlier in section.");
266       return;
267     }
268     if (!B_Base) {
269       Asm.getContext().reportError(
270           Fixup.getLoc(),
271           "unsupported relocation of local symbol '" + B->getName() +
272               "'. Must have non-local symbol earlier in section.");
273       return;
274     }
275 
276     if (A_Base == B_Base && A_Base) {
277       Asm.getContext().reportError(
278           Fixup.getLoc(), "unsupported relocation with identical base");
279       return;
280     }
281 
282     Value += (!A->getFragment() ? 0 : Writer->getSymbolAddress(*A, Layout)) -
283              (!A_Base || !A_Base->getFragment() ? 0 : Writer->getSymbolAddress(
284                                                           *A_Base, Layout));
285     Value -= (!B->getFragment() ? 0 : Writer->getSymbolAddress(*B, Layout)) -
286              (!B_Base || !B_Base->getFragment() ? 0 : Writer->getSymbolAddress(
287                                                           *B_Base, Layout));
288 
289     Type = MachO::ARM64_RELOC_UNSIGNED;
290 
291     MachO::any_relocation_info MRE;
292     MRE.r_word0 = FixupOffset;
293     MRE.r_word1 = (IsPCRel << 24) | (Log2Size << 25) | (Type << 28);
294     Writer->addRelocation(A_Base, Fragment->getParent(), MRE);
295 
296     RelSymbol = B_Base;
297     Type = MachO::ARM64_RELOC_SUBTRACTOR;
298   } else { // A + constant
299     const MCSymbol *Symbol = &Target.getSymA()->getSymbol();
300     const MCSectionMachO &Section =
301         static_cast<const MCSectionMachO &>(*Fragment->getParent());
302 
303     bool CanUseLocalRelocation =
304         canUseLocalRelocation(Section, *Symbol, Log2Size);
305     if (Symbol->isTemporary() && (Value || !CanUseLocalRelocation)) {
306       // Make sure that the symbol is actually in a section here. If it isn't,
307       // emit an error and exit.
308       if (!Symbol->isInSection()) {
309         Asm.getContext().reportError(
310             Fixup.getLoc(),
311             "unsupported relocation of local symbol '" + Symbol->getName() +
312                 "'. Must have non-local symbol earlier in section.");
313         return;
314       }
315       const MCSection &Sec = Symbol->getSection();
316       if (!Asm.getContext().getAsmInfo()->isSectionAtomizableBySymbols(Sec))
317         Symbol->setUsedInReloc();
318     }
319 
320     const MCSymbol *Base = Asm.getAtom(*Symbol);
321     // If the symbol is a variable it can either be in a section and
322     // we have a base or it is absolute and should have been expanded.
323     assert(!Symbol->isVariable() || Base);
324 
325     // Relocations inside debug sections always use local relocations when
326     // possible. This seems to be done because the debugger doesn't fully
327     // understand relocation entries and expects to find values that
328     // have already been fixed up.
329     if (Symbol->isInSection()) {
330       if (Section.hasAttribute(MachO::S_ATTR_DEBUG))
331         Base = nullptr;
332     }
333 
334     // AArch64 uses external relocations as much as possible. For debug
335     // sections, and for pointer-sized relocations (.quad), we allow section
336     // relocations.  It's code sections that run into trouble.
337     if (Base) {
338       RelSymbol = Base;
339 
340       // Add the local offset, if needed.
341       if (Base != Symbol)
342         Value +=
343             Layout.getSymbolOffset(*Symbol) - Layout.getSymbolOffset(*Base);
344     } else if (Symbol->isInSection()) {
345       if (!CanUseLocalRelocation) {
346         Asm.getContext().reportError(
347             Fixup.getLoc(),
348             "unsupported relocation of local symbol '" + Symbol->getName() +
349                 "'. Must have non-local symbol earlier in section.");
350         return;
351       }
352       // Adjust the relocation to be section-relative.
353       // The index is the section ordinal (1-based).
354       const MCSection &Sec = Symbol->getSection();
355       Index = Sec.getOrdinal() + 1;
356       Value += Writer->getSymbolAddress(*Symbol, Layout);
357 
358       if (IsPCRel)
359         Value -= Writer->getFragmentAddress(Fragment, Layout) +
360                  Fixup.getOffset() + (1ULL << Log2Size);
361     } else {
362       llvm_unreachable(
363           "This constant variable should have been expanded during evaluation");
364     }
365   }
366 
367   // If the relocation kind is Branch26, Page21, or Pageoff12, any addend
368   // is represented via an Addend relocation, not encoded directly into
369   // the instruction.
370   if ((Type == MachO::ARM64_RELOC_BRANCH26 ||
371        Type == MachO::ARM64_RELOC_PAGE21 ||
372        Type == MachO::ARM64_RELOC_PAGEOFF12) &&
373       Value) {
374     if (!isInt<24>(Value)) {
375       Asm.getContext().reportError(Fixup.getLoc(),
376                                    "addend too big for relocation");
377       return;
378     }
379 
380     MachO::any_relocation_info MRE;
381     MRE.r_word0 = FixupOffset;
382     MRE.r_word1 =
383         (Index << 0) | (IsPCRel << 24) | (Log2Size << 25) | (Type << 28);
384     Writer->addRelocation(RelSymbol, Fragment->getParent(), MRE);
385 
386     // Now set up the Addend relocation.
387     Type = MachO::ARM64_RELOC_ADDEND;
388     Index = Value;
389     RelSymbol = nullptr;
390     IsPCRel = 0;
391     Log2Size = 2;
392 
393     // Put zero into the instruction itself. The addend is in the relocation.
394     Value = 0;
395   }
396 
397   // If there's any addend left to handle, encode it in the instruction.
398   FixedValue = Value;
399 
400   // struct relocation_info (8 bytes)
401   MachO::any_relocation_info MRE;
402   MRE.r_word0 = FixupOffset;
403   MRE.r_word1 =
404       (Index << 0) | (IsPCRel << 24) | (Log2Size << 25) | (Type << 28);
405   Writer->addRelocation(RelSymbol, Fragment->getParent(), MRE);
406 }
407 
408 std::unique_ptr<MCObjectTargetWriter>
409 llvm::createAArch64MachObjectWriter(uint32_t CPUType, uint32_t CPUSubtype,
410                                     bool IsILP32) {
411   return std::make_unique<AArch64MachObjectWriter>(CPUType, CPUSubtype,
412                                                    IsILP32);
413 }
414