ARM64: initial backend import
[oota-llvm.git] / lib / Target / ARM64 / MCTargetDesc / ARM64MachObjectWriter.cpp
1 //===-- ARMMachObjectWriter.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/ARM64FixupKinds.h"
11 #include "MCTargetDesc/ARM64MCTargetDesc.h"
12 #include "llvm/MC/MCAssembler.h"
13 #include "llvm/MC/MCAsmLayout.h"
14 #include "llvm/MC/MCContext.h"
15 #include "llvm/MC/MCExpr.h"
16 #include "llvm/MC/MCFixup.h"
17 #include "llvm/MC/MCMachObjectWriter.h"
18 #include "llvm/MC/MCSectionMachO.h"
19 #include "llvm/MC/MCValue.h"
20 #include "llvm/ADT/Twine.h"
21 #include "llvm/Support/ErrorHandling.h"
22 #include "llvm/Support/MachO.h"
23 using namespace llvm;
24
25 namespace {
26 class ARM64MachObjectWriter : public MCMachObjectTargetWriter {
27   bool getARM64FixupKindMachOInfo(const MCFixup &Fixup, unsigned &RelocType,
28                                   const MCSymbolRefExpr *Sym,
29                                   unsigned &Log2Size, const MCAssembler &Asm);
30
31 public:
32   ARM64MachObjectWriter(uint32_t CPUType, uint32_t CPUSubtype)
33       : MCMachObjectTargetWriter(true /* is64Bit */, CPUType, CPUSubtype,
34                                  /*UseAggressiveSymbolFolding=*/true) {}
35
36   void RecordRelocation(MachObjectWriter *Writer, const MCAssembler &Asm,
37                         const MCAsmLayout &Layout, const MCFragment *Fragment,
38                         const MCFixup &Fixup, MCValue Target,
39                         uint64_t &FixedValue);
40 };
41 }
42
43 bool ARM64MachObjectWriter::getARM64FixupKindMachOInfo(
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 ARM64::fixup_arm64_add_imm12:
70   case ARM64::fixup_arm64_ldst_imm12_scale1:
71   case ARM64::fixup_arm64_ldst_imm12_scale2:
72   case ARM64::fixup_arm64_ldst_imm12_scale4:
73   case ARM64::fixup_arm64_ldst_imm12_scale8:
74   case ARM64::fixup_arm64_ldst_imm12_scale16:
75     Log2Size = llvm::Log2_32(4);
76     switch (Sym->getKind()) {
77     default:
78       assert(0 && "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 ARM64::fixup_arm64_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().FatalError(Fixup.getLoc(),
95                                   "ADR/ADRP relocations must be GOT relative");
96     case MCSymbolRefExpr::VK_PAGE:
97       RelocType = unsigned(MachO::ARM64_RELOC_PAGE21);
98       return true;
99     case MCSymbolRefExpr::VK_GOTPAGE:
100       RelocType = unsigned(MachO::ARM64_RELOC_GOT_LOAD_PAGE21);
101       return true;
102     case MCSymbolRefExpr::VK_TLVPPAGE:
103       RelocType = unsigned(MachO::ARM64_RELOC_TLVP_LOAD_PAGE21);
104       return true;
105     }
106     return true;
107   case ARM64::fixup_arm64_pcrel_branch26:
108   case ARM64::fixup_arm64_pcrel_call26:
109     Log2Size = llvm::Log2_32(4);
110     RelocType = unsigned(MachO::ARM64_RELOC_BRANCH26);
111     return true;
112   }
113 }
114
115 void ARM64MachObjectWriter::RecordRelocation(
116     MachObjectWriter *Writer, const MCAssembler &Asm, const MCAsmLayout &Layout,
117     const MCFragment *Fragment, const MCFixup &Fixup, MCValue Target,
118     uint64_t &FixedValue) {
119   unsigned IsPCRel = Writer->isFixupKindPCRel(Asm, Fixup.getKind());
120
121   // See <reloc.h>.
122   uint32_t FixupOffset = Layout.getFragmentOffset(Fragment);
123   unsigned Log2Size = 0;
124   int64_t Value = 0;
125   unsigned Index = 0;
126   unsigned IsExtern = 0;
127   unsigned Type = 0;
128   unsigned Kind = Fixup.getKind();
129
130   FixupOffset += Fixup.getOffset();
131
132   // ARM64 pcrel relocation addends do not include the section offset.
133   if (IsPCRel)
134     FixedValue += FixupOffset;
135
136   // ADRP fixups use relocations for the whole symbol value and only
137   // put the addend in the instruction itself. Clear out any value the
138   // generic code figured out from the sybmol definition.
139   if (Kind == ARM64::fixup_arm64_pcrel_adrp_imm21 ||
140       Kind == ARM64::fixup_arm64_pcrel_imm19)
141     FixedValue = 0;
142
143   // imm19 relocations are for conditional branches, which require
144   // assembler local symbols. If we got here, that's not what we have,
145   // so complain loudly.
146   if (Kind == ARM64::fixup_arm64_pcrel_imm19) {
147     Asm.getContext().FatalError(Fixup.getLoc(),
148                                 "conditional branch requires assembler-local"
149                                 " label. '" +
150                                     Target.getSymA()->getSymbol().getName() +
151                                     "' is external.");
152     return;
153   }
154
155   // 14-bit branch relocations should only target internal labels, and so
156   // should never get here.
157   if (Kind == ARM64::fixup_arm64_pcrel_branch14) {
158     Asm.getContext().FatalError(Fixup.getLoc(),
159                                 "Invalid relocation on conditional branch!");
160     return;
161   }
162
163   if (!getARM64FixupKindMachOInfo(Fixup, Type, Target.getSymA(), Log2Size,
164                                   Asm)) {
165     Asm.getContext().FatalError(Fixup.getLoc(), "unknown ARM64 fixup kind!");
166     return;
167   }
168
169   Value = Target.getConstant();
170
171   if (Target.isAbsolute()) { // constant
172     // FIXME: Should this always be extern?
173     // SymbolNum of 0 indicates the absolute section.
174     Type = MachO::ARM64_RELOC_UNSIGNED;
175     Index = 0;
176
177     if (IsPCRel) {
178       IsExtern = 1;
179       Asm.getContext().FatalError(Fixup.getLoc(),
180                                   "PC relative absolute relocation!");
181
182       // FIXME: x86_64 sets the type to a branch reloc here. Should we do
183       // something similar?
184     }
185   } else if (Target.getSymB()) { // A - B + constant
186     const MCSymbol *A = &Target.getSymA()->getSymbol();
187     MCSymbolData &A_SD = Asm.getSymbolData(*A);
188     const MCSymbolData *A_Base = Asm.getAtom(&A_SD);
189
190     const MCSymbol *B = &Target.getSymB()->getSymbol();
191     MCSymbolData &B_SD = Asm.getSymbolData(*B);
192     const MCSymbolData *B_Base = Asm.getAtom(&B_SD);
193
194     // Check for "_foo@got - .", which comes through here as:
195     // Ltmp0:
196     //    ... _foo@got - Ltmp0
197     if (Target.getSymA()->getKind() == MCSymbolRefExpr::VK_GOT &&
198         Target.getSymB()->getKind() == MCSymbolRefExpr::VK_None &&
199         Layout.getSymbolOffset(&B_SD) ==
200             Layout.getFragmentOffset(Fragment) + Fixup.getOffset()) {
201       // SymB is the PC, so use a PC-rel pointer-to-GOT relocation.
202       Index = A_Base->getIndex();
203       IsExtern = 1;
204       Type = MachO::ARM64_RELOC_POINTER_TO_GOT;
205       IsPCRel = 1;
206       MachO::any_relocation_info MRE;
207       MRE.r_word0 = FixupOffset;
208       MRE.r_word1 = ((Index << 0) | (IsPCRel << 24) | (Log2Size << 25) |
209                      (IsExtern << 27) | (Type << 28));
210       Writer->addRelocation(Fragment->getParent(), MRE);
211       return;
212     } else if (Target.getSymA()->getKind() != MCSymbolRefExpr::VK_None ||
213                Target.getSymB()->getKind() != MCSymbolRefExpr::VK_None)
214       // Otherwise, neither symbol can be modified.
215       Asm.getContext().FatalError(Fixup.getLoc(),
216                                   "unsupported relocation of modified symbol");
217
218     // We don't support PCrel relocations of differences.
219     if (IsPCRel)
220       Asm.getContext().FatalError(Fixup.getLoc(),
221                                   "unsupported pc-relative relocation of "
222                                   "difference");
223
224     // ARM64 always uses external relocations. If there is no symbol to use as
225     // a base address (a local symbol with no preceeding non-local symbol),
226     // error out.
227     //
228     // FIXME: We should probably just synthesize an external symbol and use
229     // that.
230     if (!A_Base)
231       Asm.getContext().FatalError(
232           Fixup.getLoc(),
233           "unsupported relocation of local symbol '" + A->getName() +
234               "'. Must have non-local symbol earlier in section.");
235     if (!B_Base)
236       Asm.getContext().FatalError(
237           Fixup.getLoc(),
238           "unsupported relocation of local symbol '" + B->getName() +
239               "'. Must have non-local symbol earlier in section.");
240
241     if (A_Base == B_Base && A_Base)
242       Asm.getContext().FatalError(Fixup.getLoc(),
243                                   "unsupported relocation with identical base");
244
245     Value += (A_SD.getFragment() == NULL ? 0 : Writer->getSymbolAddress(
246                                                    &A_SD, Layout)) -
247              (A_Base == NULL || A_Base->getFragment() == NULL
248                   ? 0
249                   : Writer->getSymbolAddress(A_Base, Layout));
250     Value -= (B_SD.getFragment() == NULL ? 0 : Writer->getSymbolAddress(
251                                                    &B_SD, Layout)) -
252              (B_Base == NULL || B_Base->getFragment() == NULL
253                   ? 0
254                   : Writer->getSymbolAddress(B_Base, Layout));
255
256     Index = A_Base->getIndex();
257     IsExtern = 1;
258     Type = MachO::ARM64_RELOC_UNSIGNED;
259
260     MachO::any_relocation_info MRE;
261     MRE.r_word0 = FixupOffset;
262     MRE.r_word1 = ((Index << 0) | (IsPCRel << 24) | (Log2Size << 25) |
263                    (IsExtern << 27) | (Type << 28));
264     Writer->addRelocation(Fragment->getParent(), MRE);
265
266     Index = B_Base->getIndex();
267     IsExtern = 1;
268     Type = MachO::ARM64_RELOC_SUBTRACTOR;
269   } else { // A + constant
270     const MCSymbol *Symbol = &Target.getSymA()->getSymbol();
271     MCSymbolData &SD = Asm.getSymbolData(*Symbol);
272     const MCSymbolData *Base = Asm.getAtom(&SD);
273     const MCSectionMachO &Section = static_cast<const MCSectionMachO &>(
274         Fragment->getParent()->getSection());
275
276     // If the symbol is a variable and we weren't able to get a Base for it
277     // (i.e., it's not in the symbol table associated with a section) resolve
278     // the relocation based its expansion instead.
279     if (Symbol->isVariable() && !Base) {
280       // If the evaluation is an absolute value, just use that directly
281       // to keep things easy.
282       int64_t Res;
283       if (SD.getSymbol().getVariableValue()->EvaluateAsAbsolute(
284               Res, Layout, Writer->getSectionAddressMap())) {
285         FixedValue = Res;
286         return;
287       }
288
289       // FIXME: Will the Target we already have ever have any data in it
290       // we need to preserve and merge with the new Target? How about
291       // the FixedValue?
292       if (!Symbol->getVariableValue()->EvaluateAsRelocatable(Target, &Layout))
293         Asm.getContext().FatalError(Fixup.getLoc(),
294                                     "unable to resolve variable '" +
295                                         Symbol->getName() + "'");
296       return RecordRelocation(Writer, Asm, Layout, Fragment, Fixup, Target,
297                               FixedValue);
298     }
299
300     // Relocations inside debug sections always use local relocations when
301     // possible. This seems to be done because the debugger doesn't fully
302     // understand relocation entries and expects to find values that
303     // have already been fixed up.
304     if (Symbol->isInSection()) {
305       if (Section.hasAttribute(MachO::S_ATTR_DEBUG))
306         Base = 0;
307     }
308
309     // ARM64 uses external relocations as much as possible. For debug sections,
310     // and for pointer-sized relocations (.quad), we allow section relocations.
311     // It's code sections that run into trouble.
312     if (Base) {
313       Index = Base->getIndex();
314       IsExtern = 1;
315
316       // Add the local offset, if needed.
317       if (Base != &SD)
318         Value += Layout.getSymbolOffset(&SD) - Layout.getSymbolOffset(Base);
319     } else if (Symbol->isInSection()) {
320       // Pointer-sized relocations can use a local relocation. Otherwise,
321       // we have to be in a debug info section.
322       if (!Section.hasAttribute(MachO::S_ATTR_DEBUG) && Log2Size != 3)
323         Asm.getContext().FatalError(
324             Fixup.getLoc(),
325             "unsupported relocation of local symbol '" + Symbol->getName() +
326                 "'. Must have non-local symbol earlier in section.");
327       // Adjust the relocation to be section-relative.
328       // The index is the section ordinal (1-based).
329       const MCSectionData &SymSD =
330           Asm.getSectionData(SD.getSymbol().getSection());
331       Index = SymSD.getOrdinal() + 1;
332       IsExtern = 0;
333       Value += Writer->getSymbolAddress(&SD, Layout);
334
335       if (IsPCRel)
336         Value -= Writer->getFragmentAddress(Fragment, Layout) +
337                  Fixup.getOffset() + (1 << Log2Size);
338     } else {
339       // Resolve constant variables.
340       if (SD.getSymbol().isVariable()) {
341         int64_t Res;
342         if (SD.getSymbol().getVariableValue()->EvaluateAsAbsolute(
343                 Res, Layout, Writer->getSectionAddressMap())) {
344           FixedValue = Res;
345           return;
346         }
347       }
348       Asm.getContext().FatalError(Fixup.getLoc(),
349                                   "unsupported relocation of variable '" +
350                                       Symbol->getName() + "'");
351     }
352   }
353
354   // If the relocation kind is Branch26, Page21, or Pageoff12, any addend
355   // is represented via an Addend relocation, not encoded directly into
356   // the instruction.
357   if ((Type == MachO::ARM64_RELOC_BRANCH26 ||
358        Type == MachO::ARM64_RELOC_PAGE21 ||
359        Type == MachO::ARM64_RELOC_PAGEOFF12) &&
360       Value) {
361     assert((Value & 0xff000000) == 0 && "Added relocation out of range!");
362
363     MachO::any_relocation_info MRE;
364     MRE.r_word0 = FixupOffset;
365     MRE.r_word1 = ((Index << 0) | (IsPCRel << 24) | (Log2Size << 25) |
366                    (IsExtern << 27) | (Type << 28));
367     Writer->addRelocation(Fragment->getParent(), MRE);
368
369     // Now set up the Addend relocation.
370     Type = MachO::ARM64_RELOC_ADDEND;
371     Index = Value;
372     IsPCRel = 0;
373     Log2Size = 2;
374     IsExtern = 0;
375
376     // Put zero into the instruction itself. The addend is in the relocation.
377     Value = 0;
378   }
379
380   // If there's any addend left to handle, encode it in the instruction.
381   FixedValue = Value;
382
383   // struct relocation_info (8 bytes)
384   MachO::any_relocation_info MRE;
385   MRE.r_word0 = FixupOffset;
386   MRE.r_word1 = ((Index << 0) | (IsPCRel << 24) | (Log2Size << 25) |
387                  (IsExtern << 27) | (Type << 28));
388   Writer->addRelocation(Fragment->getParent(), MRE);
389 }
390
391 MCObjectWriter *llvm::createARM64MachObjectWriter(raw_ostream &OS,
392                                                   uint32_t CPUType,
393                                                   uint32_t CPUSubtype) {
394   return createMachObjectWriter(new ARM64MachObjectWriter(CPUType, CPUSubtype),
395                                 OS, /*IsLittleEndian=*/true);
396 }