xref: /llvm-project/llvm/lib/Target/AArch64/MCTargetDesc/AArch64MachObjectWriter.cpp (revision 464fa3b3b047518699689b57c473c87701986593)
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/AArch64MCExpr.h"
11 #include "MCTargetDesc/AArch64MCTargetDesc.h"
12 #include "llvm/ADT/Twine.h"
13 #include "llvm/BinaryFormat/MachO.h"
14 #include "llvm/MC/MCAsmInfo.h"
15 #include "llvm/MC/MCAsmInfoDarwin.h"
16 #include "llvm/MC/MCAssembler.h"
17 #include "llvm/MC/MCContext.h"
18 #include "llvm/MC/MCExpr.h"
19 #include "llvm/MC/MCFixup.h"
20 #include "llvm/MC/MCFragment.h"
21 #include "llvm/MC/MCMachObjectWriter.h"
22 #include "llvm/MC/MCSection.h"
23 #include "llvm/MC/MCSectionMachO.h"
24 #include "llvm/MC/MCSymbol.h"
25 #include "llvm/MC/MCValue.h"
26 #include "llvm/Support/Casting.h"
27 #include "llvm/Support/MathExtras.h"
28 #include <cassert>
29 #include <cstdint>
30 
31 using namespace llvm;
32 
33 namespace {
34 
35 class AArch64MachObjectWriter : public MCMachObjectTargetWriter {
36   bool getAArch64FixupKindMachOInfo(const MCFixup &Fixup, unsigned &RelocType,
37                                   const MCSymbolRefExpr *Sym,
38                                   unsigned &Log2Size, const MCAssembler &Asm);
39 
40 public:
41   AArch64MachObjectWriter(uint32_t CPUType, uint32_t CPUSubtype, bool IsILP32)
42       : MCMachObjectTargetWriter(!IsILP32 /* is64Bit */, CPUType, CPUSubtype) {}
43 
44   void recordRelocation(MachObjectWriter *Writer, MCAssembler &Asm,
45                         const MCFragment *Fragment, const MCFixup &Fixup,
46                         MCValue Target, 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 MCFragment *Fragment,
151     const MCFixup &Fixup, MCValue Target, uint64_t &FixedValue) {
152   unsigned IsPCRel = Writer->isFixupKindPCRel(Asm, Fixup.getKind());
153 
154   // See <reloc.h>.
155   uint32_t FixupOffset = Asm.getFragmentOffset(*Fragment);
156   unsigned Log2Size = 0;
157   int64_t Value = 0;
158   unsigned Index = 0;
159   unsigned Type = 0;
160   unsigned Kind = Fixup.getKind();
161   const MCSymbol *RelSymbol = nullptr;
162 
163   FixupOffset += Fixup.getOffset();
164 
165   // AArch64 pcrel relocation addends do not include the section offset.
166   if (IsPCRel)
167     FixedValue += FixupOffset;
168 
169   // ADRP fixups use relocations for the whole symbol value and only
170   // put the addend in the instruction itself. Clear out any value the
171   // generic code figured out from the sybmol definition.
172   if (Kind == AArch64::fixup_aarch64_pcrel_adrp_imm21)
173     FixedValue = 0;
174 
175   // imm19 relocations are for conditional branches, which require
176   // assembler local symbols. If we got here, that's not what we have,
177   // so complain loudly.
178   if (Kind == AArch64::fixup_aarch64_pcrel_branch19) {
179     Asm.getContext().reportError(Fixup.getLoc(),
180                                  "conditional branch requires assembler-local"
181                                  " label. '" +
182                                      Target.getSymA()->getSymbol().getName() +
183                                      "' is external.");
184     return;
185   }
186 
187   // 14-bit branch relocations should only target internal labels, and so
188   // should never get here.
189   if (Kind == AArch64::fixup_aarch64_pcrel_branch14) {
190     Asm.getContext().reportError(Fixup.getLoc(),
191                                  "Invalid relocation on conditional branch!");
192     return;
193   }
194 
195   if (!getAArch64FixupKindMachOInfo(Fixup, Type, Target.getSymA(), Log2Size,
196                                     Asm)) {
197     Asm.getContext().reportError(Fixup.getLoc(), "unknown AArch64 fixup kind!");
198     return;
199   }
200 
201   Value = Target.getConstant();
202 
203   if (Target.isAbsolute()) { // constant
204     // FIXME: Should this always be extern?
205     // SymbolNum of 0 indicates the absolute section.
206     Type = MachO::ARM64_RELOC_UNSIGNED;
207 
208     if (IsPCRel) {
209       Asm.getContext().reportError(Fixup.getLoc(),
210                                    "PC relative absolute relocation!");
211       return;
212 
213       // FIXME: x86_64 sets the type to a branch reloc here. Should we do
214       // something similar?
215     }
216   } else if (Target.getSymB()) { // A - B + constant
217     const MCSymbol *A = &Target.getSymA()->getSymbol();
218     const MCSymbol *A_Base = Writer->getAtom(*A);
219 
220     const MCSymbol *B = &Target.getSymB()->getSymbol();
221     const MCSymbol *B_Base = Writer->getAtom(*B);
222 
223     // Check for "_foo@got - .", which comes through here as:
224     // Ltmp0:
225     //    ... _foo@got - Ltmp0
226     if (Target.getSymA()->getKind() == MCSymbolRefExpr::VK_GOT &&
227         Target.getSymB()->getKind() == MCSymbolRefExpr::VK_None &&
228         Asm.getSymbolOffset(*B) ==
229             Asm.getFragmentOffset(*Fragment) + Fixup.getOffset()) {
230       // SymB is the PC, so use a PC-rel pointer-to-GOT relocation.
231       Type = MachO::ARM64_RELOC_POINTER_TO_GOT;
232       IsPCRel = 1;
233       MachO::any_relocation_info MRE;
234       MRE.r_word0 = FixupOffset;
235       MRE.r_word1 = (IsPCRel << 24) | (Log2Size << 25) | (Type << 28);
236       Writer->addRelocation(A_Base, Fragment->getParent(), MRE);
237       return;
238     } else if (Target.getSymA()->getKind() != MCSymbolRefExpr::VK_None ||
239                Target.getSymB()->getKind() != MCSymbolRefExpr::VK_None) {
240       // Otherwise, neither symbol can be modified.
241       Asm.getContext().reportError(Fixup.getLoc(),
242                                    "unsupported relocation of modified symbol");
243       return;
244     }
245 
246     // We don't support PCrel relocations of differences.
247     if (IsPCRel) {
248       Asm.getContext().reportError(Fixup.getLoc(),
249                                    "unsupported pc-relative relocation of "
250                                    "difference");
251       return;
252     }
253 
254     // AArch64 always uses external relocations. If there is no symbol to use as
255     // a base address (a local symbol with no preceding non-local symbol),
256     // error out.
257     //
258     // FIXME: We should probably just synthesize an external symbol and use
259     // that.
260     if (!A_Base) {
261       Asm.getContext().reportError(
262           Fixup.getLoc(),
263           "unsupported relocation of local symbol '" + A->getName() +
264               "'. Must have non-local symbol earlier in section.");
265       return;
266     }
267     if (!B_Base) {
268       Asm.getContext().reportError(
269           Fixup.getLoc(),
270           "unsupported relocation of local symbol '" + B->getName() +
271               "'. Must have non-local symbol earlier in section.");
272       return;
273     }
274 
275     if (A_Base == B_Base && A_Base) {
276       Asm.getContext().reportError(
277           Fixup.getLoc(), "unsupported relocation with identical base");
278       return;
279     }
280 
281     Value += (!A->getFragment() ? 0 : Writer->getSymbolAddress(*A, Asm)) -
282              (!A_Base || !A_Base->getFragment()
283                   ? 0
284                   : Writer->getSymbolAddress(*A_Base, Asm));
285     Value -= (!B->getFragment() ? 0 : Writer->getSymbolAddress(*B, Asm)) -
286              (!B_Base || !B_Base->getFragment()
287                   ? 0
288                   : Writer->getSymbolAddress(*B_Base, Asm));
289 
290     Type = MachO::ARM64_RELOC_UNSIGNED;
291 
292     MachO::any_relocation_info MRE;
293     MRE.r_word0 = FixupOffset;
294     MRE.r_word1 = (IsPCRel << 24) | (Log2Size << 25) | (Type << 28);
295     Writer->addRelocation(A_Base, Fragment->getParent(), MRE);
296 
297     RelSymbol = B_Base;
298     Type = MachO::ARM64_RELOC_SUBTRACTOR;
299   } else { // A + constant
300     const MCSymbol *Symbol = &Target.getSymA()->getSymbol();
301     const MCSectionMachO &Section =
302         static_cast<const MCSectionMachO &>(*Fragment->getParent());
303 
304     bool CanUseLocalRelocation =
305         canUseLocalRelocation(Section, *Symbol, Log2Size);
306     if (Symbol->isTemporary() && (Value || !CanUseLocalRelocation)) {
307       // Make sure that the symbol is actually in a section here. If it isn't,
308       // emit an error and exit.
309       if (!Symbol->isInSection()) {
310         Asm.getContext().reportError(
311             Fixup.getLoc(),
312             "unsupported relocation of local symbol '" + Symbol->getName() +
313                 "'. Must have non-local symbol earlier in section.");
314         return;
315       }
316       const MCSection &Sec = Symbol->getSection();
317       if (!MCAsmInfoDarwin::isSectionAtomizableBySymbols(Sec))
318         Symbol->setUsedInReloc();
319     }
320 
321     const MCSymbol *Base = Writer->getAtom(*Symbol);
322 
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 += Asm.getSymbolOffset(*Symbol) - Asm.getSymbolOffset(*Base);
345     } else if (Symbol->isInSection()) {
346       if (!CanUseLocalRelocation) {
347         Asm.getContext().reportError(
348             Fixup.getLoc(),
349             "unsupported relocation of local symbol '" + Symbol->getName() +
350                 "'. Must have non-local symbol earlier in section.");
351         return;
352       }
353       // Adjust the relocation to be section-relative.
354       // The index is the section ordinal (1-based).
355       const MCSection &Sec = Symbol->getSection();
356       Index = Sec.getOrdinal() + 1;
357       Value += Writer->getSymbolAddress(*Symbol, Asm);
358 
359       if (IsPCRel)
360         Value -= Writer->getFragmentAddress(Asm, Fragment) + Fixup.getOffset() +
361                  (1ULL << Log2Size);
362     } else {
363       llvm_unreachable(
364           "This constant variable should have been expanded during evaluation");
365     }
366   }
367 
368   // If the relocation kind is Branch26, Page21, or Pageoff12, any addend
369   // is represented via an Addend relocation, not encoded directly into
370   // the instruction.
371   if ((Type == MachO::ARM64_RELOC_BRANCH26 ||
372        Type == MachO::ARM64_RELOC_PAGE21 ||
373        Type == MachO::ARM64_RELOC_PAGEOFF12) &&
374       Value) {
375     if (!isInt<24>(Value)) {
376       Asm.getContext().reportError(Fixup.getLoc(),
377                                    "addend too big for relocation");
378       return;
379     }
380 
381     MachO::any_relocation_info MRE;
382     MRE.r_word0 = FixupOffset;
383     MRE.r_word1 =
384         (Index << 0) | (IsPCRel << 24) | (Log2Size << 25) | (Type << 28);
385     Writer->addRelocation(RelSymbol, Fragment->getParent(), MRE);
386 
387     // Now set up the Addend relocation.
388     Type = MachO::ARM64_RELOC_ADDEND;
389     Index = Value;
390     RelSymbol = nullptr;
391     IsPCRel = 0;
392     Log2Size = 2;
393 
394     // Put zero into the instruction itself. The addend is in the relocation.
395     Value = 0;
396   }
397 
398   if (Target.getRefKind() == AArch64MCExpr::VK_AUTH ||
399       Target.getRefKind() == AArch64MCExpr::VK_AUTHADDR) {
400     auto *Expr = cast<AArch64AuthMCExpr>(Fixup.getValue());
401 
402     assert(Type == MachO::ARM64_RELOC_UNSIGNED);
403 
404     if (IsPCRel) {
405       Asm.getContext().reportError(Fixup.getLoc(),
406                                    "invalid PC relative auth relocation");
407       return;
408     }
409 
410     if (Log2Size != 3) {
411       Asm.getContext().reportError(
412           Fixup.getLoc(), "invalid auth relocation size, must be 8 bytes");
413       return;
414     }
415 
416     if (Target.getSymB()) {
417       Asm.getContext().reportError(
418           Fixup.getLoc(),
419           "invalid auth relocation, can't reference two symbols");
420       return;
421     }
422 
423     uint16_t Discriminator = Expr->getDiscriminator();
424     AArch64PACKey::ID Key = Expr->getKey();
425 
426     if (!isInt<32>(Value)) {
427       Asm.getContext().reportError(Fixup.getLoc(),
428                                    "addend too big for relocation");
429       return;
430     }
431 
432     Type = MachO::ARM64_RELOC_AUTHENTICATED_POINTER;
433     Value = (uint32_t(Value)) | (uint64_t(Discriminator) << 32) |
434             (uint64_t(Expr->hasAddressDiversity()) << 48) |
435             (uint64_t(Key) << 49) | (1ULL << 63);
436   }
437 
438   // If there's any addend left to handle, encode it in the instruction.
439   FixedValue = Value;
440 
441   // struct relocation_info (8 bytes)
442   MachO::any_relocation_info MRE;
443   MRE.r_word0 = FixupOffset;
444   MRE.r_word1 =
445       (Index << 0) | (IsPCRel << 24) | (Log2Size << 25) | (Type << 28);
446   Writer->addRelocation(RelSymbol, Fragment->getParent(), MRE);
447 }
448 
449 std::unique_ptr<MCObjectTargetWriter>
450 llvm::createAArch64MachObjectWriter(uint32_t CPUType, uint32_t CPUSubtype,
451                                     bool IsILP32) {
452   return std::make_unique<AArch64MachObjectWriter>(CPUType, CPUSubtype,
453                                                    IsILP32);
454 }
455