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"
24 using namespace llvm;
25 
26 namespace {
27 class AArch64MachObjectWriter : public MCMachObjectTargetWriter {
28   bool getAArch64FixupKindMachOInfo(const MCFixup &Fixup, unsigned &RelocType,
29                                   const MCSymbolRefExpr *Sym,
30                                   unsigned &Log2Size, const MCAssembler &Asm);
31 
32 public:
AArch64MachObjectWriter(uint32_t CPUType,uint32_t CPUSubtype)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 
getAArch64FixupKindMachOInfo(const MCFixup & Fixup,unsigned & RelocType,const MCSymbolRefExpr * Sym,unsigned & Log2Size,const MCAssembler & Asm)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().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 
canUseLocalRelocation(const MCSectionMachO & Section,const MCSymbol & Symbol,unsigned Log2Size)117 static 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 
recordRelocation(MachObjectWriter * Writer,MCAssembler & Asm,const MCAsmLayout & Layout,const MCFragment * Fragment,const MCFixup & Fixup,MCValue Target,uint64_t & FixedValue)144 void 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 
createAArch64MachObjectWriter(raw_pwrite_stream & OS,uint32_t CPUType,uint32_t CPUSubtype)425 MCObjectWriter *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