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                                  /*UseAggressiveSymbolFolding=*/true) {}
36 
37   void RecordRelocation(MachObjectWriter *Writer, MCAssembler &Asm,
38                         const MCAsmLayout &Layout, const MCFragment *Fragment,
39                         const MCFixup &Fixup, MCValue Target,
40                         uint64_t &FixedValue) override;
41 };
42 }
43 
getAArch64FixupKindMachOInfo(const MCFixup & Fixup,unsigned & RelocType,const MCSymbolRefExpr * Sym,unsigned & Log2Size,const MCAssembler & Asm)44 bool AArch64MachObjectWriter::getAArch64FixupKindMachOInfo(
45     const MCFixup &Fixup, unsigned &RelocType, const MCSymbolRefExpr *Sym,
46     unsigned &Log2Size, const MCAssembler &Asm) {
47   RelocType = unsigned(MachO::ARM64_RELOC_UNSIGNED);
48   Log2Size = ~0U;
49 
50   switch ((unsigned)Fixup.getKind()) {
51   default:
52     return false;
53 
54   case FK_Data_1:
55     Log2Size = llvm::Log2_32(1);
56     return true;
57   case FK_Data_2:
58     Log2Size = llvm::Log2_32(2);
59     return true;
60   case FK_Data_4:
61     Log2Size = llvm::Log2_32(4);
62     if (Sym->getKind() == MCSymbolRefExpr::VK_GOT)
63       RelocType = unsigned(MachO::ARM64_RELOC_POINTER_TO_GOT);
64     return true;
65   case FK_Data_8:
66     Log2Size = llvm::Log2_32(8);
67     if (Sym->getKind() == MCSymbolRefExpr::VK_GOT)
68       RelocType = unsigned(MachO::ARM64_RELOC_POINTER_TO_GOT);
69     return true;
70   case AArch64::fixup_aarch64_add_imm12:
71   case AArch64::fixup_aarch64_ldst_imm12_scale1:
72   case AArch64::fixup_aarch64_ldst_imm12_scale2:
73   case AArch64::fixup_aarch64_ldst_imm12_scale4:
74   case AArch64::fixup_aarch64_ldst_imm12_scale8:
75   case AArch64::fixup_aarch64_ldst_imm12_scale16:
76     Log2Size = llvm::Log2_32(4);
77     switch (Sym->getKind()) {
78     default:
79       llvm_unreachable("Unexpected symbol reference variant kind!");
80     case MCSymbolRefExpr::VK_PAGEOFF:
81       RelocType = unsigned(MachO::ARM64_RELOC_PAGEOFF12);
82       return true;
83     case MCSymbolRefExpr::VK_GOTPAGEOFF:
84       RelocType = unsigned(MachO::ARM64_RELOC_GOT_LOAD_PAGEOFF12);
85       return true;
86     case MCSymbolRefExpr::VK_TLVPPAGEOFF:
87       RelocType = unsigned(MachO::ARM64_RELOC_TLVP_LOAD_PAGEOFF12);
88       return true;
89     }
90   case AArch64::fixup_aarch64_pcrel_adrp_imm21:
91     Log2Size = llvm::Log2_32(4);
92     // This encompasses the relocation for the whole 21-bit value.
93     switch (Sym->getKind()) {
94     default:
95       Asm.getContext().FatalError(Fixup.getLoc(),
96                                   "ADR/ADRP relocations must be GOT relative");
97     case MCSymbolRefExpr::VK_PAGE:
98       RelocType = unsigned(MachO::ARM64_RELOC_PAGE21);
99       return true;
100     case MCSymbolRefExpr::VK_GOTPAGE:
101       RelocType = unsigned(MachO::ARM64_RELOC_GOT_LOAD_PAGE21);
102       return true;
103     case MCSymbolRefExpr::VK_TLVPPAGE:
104       RelocType = unsigned(MachO::ARM64_RELOC_TLVP_LOAD_PAGE21);
105       return true;
106     }
107     return true;
108   case AArch64::fixup_aarch64_pcrel_branch26:
109   case AArch64::fixup_aarch64_pcrel_call26:
110     Log2Size = llvm::Log2_32(4);
111     RelocType = unsigned(MachO::ARM64_RELOC_BRANCH26);
112     return true;
113   }
114 }
115 
canUseLocalRelocation(const MCSectionMachO & Section,const MCSymbol & Symbol,unsigned Log2Size)116 static bool canUseLocalRelocation(const MCSectionMachO &Section,
117                                   const MCSymbol &Symbol, unsigned Log2Size) {
118   // Debug info sections can use local relocations.
119   if (Section.hasAttribute(MachO::S_ATTR_DEBUG))
120     return true;
121 
122   // Otherwise, only pointer sized relocations are supported.
123   if (Log2Size != 3)
124     return false;
125 
126   // But only if they don't point to a few forbidden sections.
127   if (!Symbol.isInSection())
128     return true;
129   const MCSectionMachO &RefSec = cast<MCSectionMachO>(Symbol.getSection());
130   if (RefSec.getType() == MachO::S_CSTRING_LITERALS)
131     return false;
132 
133   if (RefSec.getSegmentName() == "__DATA" &&
134       RefSec.getSectionName() == "__cfstring")
135     return false;
136 
137   if (RefSec.getSegmentName() == "__DATA" &&
138       RefSec.getSectionName() == "__objc_classrefs")
139     return false;
140 
141   return true;
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 MCSymbolData *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().FatalError(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().FatalError(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().FatalError(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().FatalError(Fixup.getLoc(),
206                                   "PC relative absolute relocation!");
207 
208       // FIXME: x86_64 sets the type to a branch reloc here. Should we do
209       // something similar?
210     }
211   } else if (Target.getSymB()) { // A - B + constant
212     const MCSymbol *A = &Target.getSymA()->getSymbol();
213     const MCSymbolData &A_SD = Asm.getSymbolData(*A);
214     const MCSymbolData *A_Base = Asm.getAtom(&A_SD);
215 
216     const MCSymbol *B = &Target.getSymB()->getSymbol();
217     const MCSymbolData &B_SD = Asm.getSymbolData(*B);
218     const MCSymbolData *B_Base = Asm.getAtom(&B_SD);
219 
220     // Check for "_foo@got - .", which comes through here as:
221     // Ltmp0:
222     //    ... _foo@got - Ltmp0
223     if (Target.getSymA()->getKind() == MCSymbolRefExpr::VK_GOT &&
224         Target.getSymB()->getKind() == MCSymbolRefExpr::VK_None &&
225         Layout.getSymbolOffset(&B_SD) ==
226             Layout.getFragmentOffset(Fragment) + Fixup.getOffset()) {
227       // SymB is the PC, so use a PC-rel pointer-to-GOT relocation.
228       Type = MachO::ARM64_RELOC_POINTER_TO_GOT;
229       IsPCRel = 1;
230       MachO::any_relocation_info MRE;
231       MRE.r_word0 = FixupOffset;
232       MRE.r_word1 = (IsPCRel << 24) | (Log2Size << 25) | (Type << 28);
233       Writer->addRelocation(A_Base, Fragment->getParent(), MRE);
234       return;
235     } else if (Target.getSymA()->getKind() != MCSymbolRefExpr::VK_None ||
236                Target.getSymB()->getKind() != MCSymbolRefExpr::VK_None)
237       // Otherwise, neither symbol can be modified.
238       Asm.getContext().FatalError(Fixup.getLoc(),
239                                   "unsupported relocation of modified symbol");
240 
241     // We don't support PCrel relocations of differences.
242     if (IsPCRel)
243       Asm.getContext().FatalError(Fixup.getLoc(),
244                                   "unsupported pc-relative relocation of "
245                                   "difference");
246 
247     // AArch64 always uses external relocations. If there is no symbol to use as
248     // a base address (a local symbol with no preceding non-local symbol),
249     // error out.
250     //
251     // FIXME: We should probably just synthesize an external symbol and use
252     // that.
253     if (!A_Base)
254       Asm.getContext().FatalError(
255           Fixup.getLoc(),
256           "unsupported relocation of local symbol '" + A->getName() +
257               "'. Must have non-local symbol earlier in section.");
258     if (!B_Base)
259       Asm.getContext().FatalError(
260           Fixup.getLoc(),
261           "unsupported relocation of local symbol '" + B->getName() +
262               "'. Must have non-local symbol earlier in section.");
263 
264     if (A_Base == B_Base && A_Base)
265       Asm.getContext().FatalError(Fixup.getLoc(),
266                                   "unsupported relocation with identical base");
267 
268     Value += (!A_SD.getFragment() ? 0
269                                   : Writer->getSymbolAddress(&A_SD, Layout)) -
270              (!A_Base || !A_Base->getFragment()
271                   ? 0
272                   : Writer->getSymbolAddress(A_Base, Layout));
273     Value -= (!B_SD.getFragment() ? 0
274                                   : Writer->getSymbolAddress(&B_SD, Layout)) -
275              (!B_Base || !B_Base->getFragment()
276                   ? 0
277                   : Writer->getSymbolAddress(B_Base, Layout));
278 
279     Type = MachO::ARM64_RELOC_UNSIGNED;
280 
281     MachO::any_relocation_info MRE;
282     MRE.r_word0 = FixupOffset;
283     MRE.r_word1 = (IsPCRel << 24) | (Log2Size << 25) | (Type << 28);
284     Writer->addRelocation(A_Base, Fragment->getParent(), MRE);
285 
286     RelSymbol = B_Base;
287     Type = MachO::ARM64_RELOC_SUBTRACTOR;
288   } else { // A + constant
289     const MCSymbol *Symbol = &Target.getSymA()->getSymbol();
290     const MCSectionMachO &Section = static_cast<const MCSectionMachO &>(
291         Fragment->getParent()->getSection());
292 
293     bool CanUseLocalRelocation =
294         canUseLocalRelocation(Section, *Symbol, Log2Size);
295     if (Symbol->isTemporary() && (Value || !CanUseLocalRelocation)) {
296       const MCSection &Sec = Symbol->getSection();
297       if (!Asm.getContext().getAsmInfo()->isSectionAtomizableBySymbols(Sec))
298         Asm.addLocalUsedInReloc(*Symbol);
299     }
300 
301     const MCSymbolData &SD = Asm.getSymbolData(*Symbol);
302     const MCSymbolData *Base = Asm.getAtom(&SD);
303 
304     // If the symbol is a variable and we weren't able to get a Base for it
305     // (i.e., it's not in the symbol table associated with a section) resolve
306     // the relocation based its expansion instead.
307     if (Symbol->isVariable() && !Base) {
308       // If the evaluation is an absolute value, just use that directly
309       // to keep things easy.
310       int64_t Res;
311       if (SD.getSymbol().getVariableValue()->EvaluateAsAbsolute(
312               Res, Layout, Writer->getSectionAddressMap())) {
313         FixedValue = Res;
314         return;
315       }
316 
317       // FIXME: Will the Target we already have ever have any data in it
318       // we need to preserve and merge with the new Target? How about
319       // the FixedValue?
320       if (!Symbol->getVariableValue()->EvaluateAsRelocatable(Target, &Layout,
321                                                              &Fixup))
322         Asm.getContext().FatalError(Fixup.getLoc(),
323                                     "unable to resolve variable '" +
324                                         Symbol->getName() + "'");
325       return RecordRelocation(Writer, Asm, Layout, Fragment, Fixup, Target,
326                               FixedValue);
327     }
328 
329     // Relocations inside debug sections always use local relocations when
330     // possible. This seems to be done because the debugger doesn't fully
331     // understand relocation entries and expects to find values that
332     // have already been fixed up.
333     if (Symbol->isInSection()) {
334       if (Section.hasAttribute(MachO::S_ATTR_DEBUG))
335         Base = nullptr;
336     }
337 
338     // AArch64 uses external relocations as much as possible. For debug
339     // sections, and for pointer-sized relocations (.quad), we allow section
340     // relocations.  It's code sections that run into trouble.
341     if (Base) {
342       RelSymbol = Base;
343 
344       // Add the local offset, if needed.
345       if (Base != &SD)
346         Value += Layout.getSymbolOffset(&SD) - Layout.getSymbolOffset(Base);
347     } else if (Symbol->isInSection()) {
348       if (!CanUseLocalRelocation)
349         Asm.getContext().FatalError(
350             Fixup.getLoc(),
351             "unsupported relocation of local symbol '" + Symbol->getName() +
352                 "'. Must have non-local symbol earlier in section.");
353       // Adjust the relocation to be section-relative.
354       // The index is the section ordinal (1-based).
355       const MCSectionData &SymSD =
356           Asm.getSectionData(SD.getSymbol().getSection());
357       Index = SymSD.getOrdinal() + 1;
358       Value += Writer->getSymbolAddress(&SD, Layout);
359 
360       if (IsPCRel)
361         Value -= Writer->getFragmentAddress(Fragment, Layout) +
362                  Fixup.getOffset() + (1ULL << Log2Size);
363     } else {
364       // Resolve constant variables.
365       if (SD.getSymbol().isVariable()) {
366         int64_t Res;
367         if (SD.getSymbol().getVariableValue()->EvaluateAsAbsolute(
368                 Res, Layout, Writer->getSectionAddressMap())) {
369           FixedValue = Res;
370           return;
371         }
372       }
373       Asm.getContext().FatalError(Fixup.getLoc(),
374                                   "unsupported relocation of variable '" +
375                                       Symbol->getName() + "'");
376     }
377   }
378 
379   // If the relocation kind is Branch26, Page21, or Pageoff12, any addend
380   // is represented via an Addend relocation, not encoded directly into
381   // the instruction.
382   if ((Type == MachO::ARM64_RELOC_BRANCH26 ||
383        Type == MachO::ARM64_RELOC_PAGE21 ||
384        Type == MachO::ARM64_RELOC_PAGEOFF12) &&
385       Value) {
386     assert((Value & 0xff000000) == 0 && "Added relocation out of range!");
387 
388     MachO::any_relocation_info MRE;
389     MRE.r_word0 = FixupOffset;
390     MRE.r_word1 =
391         (Index << 0) | (IsPCRel << 24) | (Log2Size << 25) | (Type << 28);
392     Writer->addRelocation(RelSymbol, Fragment->getParent(), MRE);
393 
394     // Now set up the Addend relocation.
395     Type = MachO::ARM64_RELOC_ADDEND;
396     Index = Value;
397     RelSymbol = nullptr;
398     IsPCRel = 0;
399     Log2Size = 2;
400 
401     // Put zero into the instruction itself. The addend is in the relocation.
402     Value = 0;
403   }
404 
405   // If there's any addend left to handle, encode it in the instruction.
406   FixedValue = Value;
407 
408   // struct relocation_info (8 bytes)
409   MachO::any_relocation_info MRE;
410   MRE.r_word0 = FixupOffset;
411   MRE.r_word1 =
412       (Index << 0) | (IsPCRel << 24) | (Log2Size << 25) | (Type << 28);
413   Writer->addRelocation(RelSymbol, Fragment->getParent(), MRE);
414 }
415 
createAArch64MachObjectWriter(raw_pwrite_stream & OS,uint32_t CPUType,uint32_t CPUSubtype)416 MCObjectWriter *llvm::createAArch64MachObjectWriter(raw_pwrite_stream &OS,
417                                                     uint32_t CPUType,
418                                                     uint32_t CPUSubtype) {
419   return createMachObjectWriter(
420       new AArch64MachObjectWriter(CPUType, CPUSubtype), OS,
421       /*IsLittleEndian=*/true);
422 }
423