xref: /freebsd/contrib/llvm-project/llvm/lib/Target/AArch64/MCTargetDesc/AArch64MachObjectWriter.cpp (revision 5036d9652a5701d00e9e40ea942c278e9f77d33d)
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/MCAsmInfoDarwin.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 MCFragment *Fragment, const MCFixup &Fixup,
45                         MCValue Target, uint64_t &FixedValue) override;
46 };
47 
48 } // end anonymous namespace
49 
50 bool AArch64MachObjectWriter::getAArch64FixupKindMachOInfo(
51     const MCFixup &Fixup, unsigned &RelocType, const MCSymbolRefExpr *Sym,
52     unsigned &Log2Size, const MCAssembler &Asm) {
53   RelocType = unsigned(MachO::ARM64_RELOC_UNSIGNED);
54   Log2Size = ~0U;
55 
56   switch (Fixup.getTargetKind()) {
57   default:
58     return false;
59 
60   case FK_Data_1:
61     Log2Size = Log2_32(1);
62     return true;
63   case FK_Data_2:
64     Log2Size = Log2_32(2);
65     return true;
66   case FK_Data_4:
67     Log2Size = Log2_32(4);
68     if (Sym->getKind() == MCSymbolRefExpr::VK_GOT)
69       RelocType = unsigned(MachO::ARM64_RELOC_POINTER_TO_GOT);
70     return true;
71   case FK_Data_8:
72     Log2Size = Log2_32(8);
73     if (Sym->getKind() == MCSymbolRefExpr::VK_GOT)
74       RelocType = unsigned(MachO::ARM64_RELOC_POINTER_TO_GOT);
75     return true;
76   case AArch64::fixup_aarch64_add_imm12:
77   case AArch64::fixup_aarch64_ldst_imm12_scale1:
78   case AArch64::fixup_aarch64_ldst_imm12_scale2:
79   case AArch64::fixup_aarch64_ldst_imm12_scale4:
80   case AArch64::fixup_aarch64_ldst_imm12_scale8:
81   case AArch64::fixup_aarch64_ldst_imm12_scale16:
82     Log2Size = Log2_32(4);
83     switch (Sym->getKind()) {
84     default:
85       return false;
86     case MCSymbolRefExpr::VK_PAGEOFF:
87       RelocType = unsigned(MachO::ARM64_RELOC_PAGEOFF12);
88       return true;
89     case MCSymbolRefExpr::VK_GOTPAGEOFF:
90       RelocType = unsigned(MachO::ARM64_RELOC_GOT_LOAD_PAGEOFF12);
91       return true;
92     case MCSymbolRefExpr::VK_TLVPPAGEOFF:
93       RelocType = unsigned(MachO::ARM64_RELOC_TLVP_LOAD_PAGEOFF12);
94       return true;
95     }
96   case AArch64::fixup_aarch64_pcrel_adrp_imm21:
97     Log2Size = Log2_32(4);
98     // This encompasses the relocation for the whole 21-bit value.
99     switch (Sym->getKind()) {
100     default:
101       Asm.getContext().reportError(Fixup.getLoc(),
102                                    "ADR/ADRP relocations must be GOT relative");
103       return false;
104     case MCSymbolRefExpr::VK_PAGE:
105       RelocType = unsigned(MachO::ARM64_RELOC_PAGE21);
106       return true;
107     case MCSymbolRefExpr::VK_GOTPAGE:
108       RelocType = unsigned(MachO::ARM64_RELOC_GOT_LOAD_PAGE21);
109       return true;
110     case MCSymbolRefExpr::VK_TLVPPAGE:
111       RelocType = unsigned(MachO::ARM64_RELOC_TLVP_LOAD_PAGE21);
112       return true;
113     }
114     return true;
115   case AArch64::fixup_aarch64_pcrel_branch26:
116   case AArch64::fixup_aarch64_pcrel_call26:
117     Log2Size = Log2_32(4);
118     RelocType = unsigned(MachO::ARM64_RELOC_BRANCH26);
119     return true;
120   }
121 }
122 
123 static bool canUseLocalRelocation(const MCSectionMachO &Section,
124                                   const MCSymbol &Symbol, unsigned Log2Size) {
125   // Debug info sections can use local relocations.
126   if (Section.hasAttribute(MachO::S_ATTR_DEBUG))
127     return true;
128 
129   // Otherwise, only pointer sized relocations are supported.
130   if (Log2Size != 3)
131     return false;
132 
133   // But only if they don't point to a few forbidden sections.
134   if (!Symbol.isInSection())
135     return true;
136   const MCSectionMachO &RefSec = cast<MCSectionMachO>(Symbol.getSection());
137   if (RefSec.getType() == MachO::S_CSTRING_LITERALS)
138     return false;
139 
140   if (RefSec.getSegmentName() == "__DATA" &&
141       (RefSec.getName() == "__cfstring" ||
142        RefSec.getName() == "__objc_classrefs"))
143     return false;
144 
145   return true;
146 }
147 
148 void AArch64MachObjectWriter::recordRelocation(
149     MachObjectWriter *Writer, MCAssembler &Asm, const MCFragment *Fragment,
150     const MCFixup &Fixup, MCValue Target, uint64_t &FixedValue) {
151   unsigned IsPCRel = Writer->isFixupKindPCRel(Asm, Fixup.getKind());
152 
153   // See <reloc.h>.
154   uint32_t FixupOffset = Asm.getFragmentOffset(*Fragment);
155   unsigned Log2Size = 0;
156   int64_t Value = 0;
157   unsigned Index = 0;
158   unsigned Type = 0;
159   unsigned Kind = Fixup.getKind();
160   const MCSymbol *RelSymbol = nullptr;
161 
162   FixupOffset += Fixup.getOffset();
163 
164   // AArch64 pcrel relocation addends do not include the section offset.
165   if (IsPCRel)
166     FixedValue += FixupOffset;
167 
168   // ADRP fixups use relocations for the whole symbol value and only
169   // put the addend in the instruction itself. Clear out any value the
170   // generic code figured out from the sybmol definition.
171   if (Kind == AArch64::fixup_aarch64_pcrel_adrp_imm21)
172     FixedValue = 0;
173 
174   // imm19 relocations are for conditional branches, which require
175   // assembler local symbols. If we got here, that's not what we have,
176   // so complain loudly.
177   if (Kind == AArch64::fixup_aarch64_pcrel_branch19) {
178     Asm.getContext().reportError(Fixup.getLoc(),
179                                  "conditional branch requires assembler-local"
180                                  " label. '" +
181                                      Target.getSymA()->getSymbol().getName() +
182                                      "' is external.");
183     return;
184   }
185 
186   // 14-bit branch relocations should only target internal labels, and so
187   // should never get here.
188   if (Kind == AArch64::fixup_aarch64_pcrel_branch14) {
189     Asm.getContext().reportError(Fixup.getLoc(),
190                                  "Invalid relocation on conditional branch!");
191     return;
192   }
193 
194   if (!getAArch64FixupKindMachOInfo(Fixup, Type, Target.getSymA(), Log2Size,
195                                     Asm)) {
196     Asm.getContext().reportError(Fixup.getLoc(), "unknown AArch64 fixup kind!");
197     return;
198   }
199 
200   Value = Target.getConstant();
201 
202   if (Target.isAbsolute()) { // constant
203     // FIXME: Should this always be extern?
204     // SymbolNum of 0 indicates the absolute section.
205     Type = MachO::ARM64_RELOC_UNSIGNED;
206 
207     if (IsPCRel) {
208       Asm.getContext().reportError(Fixup.getLoc(),
209                                    "PC relative absolute relocation!");
210       return;
211 
212       // FIXME: x86_64 sets the type to a branch reloc here. Should we do
213       // something similar?
214     }
215   } else if (Target.getSymB()) { // A - B + constant
216     const MCSymbol *A = &Target.getSymA()->getSymbol();
217     const MCSymbol *A_Base = Writer->getAtom(*A);
218 
219     const MCSymbol *B = &Target.getSymB()->getSymbol();
220     const MCSymbol *B_Base = Writer->getAtom(*B);
221 
222     // Check for "_foo@got - .", which comes through here as:
223     // Ltmp0:
224     //    ... _foo@got - Ltmp0
225     if (Target.getSymA()->getKind() == MCSymbolRefExpr::VK_GOT &&
226         Target.getSymB()->getKind() == MCSymbolRefExpr::VK_None &&
227         Asm.getSymbolOffset(*B) ==
228             Asm.getFragmentOffset(*Fragment) + Fixup.getOffset()) {
229       // SymB is the PC, so use a PC-rel pointer-to-GOT relocation.
230       Type = MachO::ARM64_RELOC_POINTER_TO_GOT;
231       IsPCRel = 1;
232       MachO::any_relocation_info MRE;
233       MRE.r_word0 = FixupOffset;
234       MRE.r_word1 = (IsPCRel << 24) | (Log2Size << 25) | (Type << 28);
235       Writer->addRelocation(A_Base, Fragment->getParent(), MRE);
236       return;
237     } else if (Target.getSymA()->getKind() != MCSymbolRefExpr::VK_None ||
238                Target.getSymB()->getKind() != MCSymbolRefExpr::VK_None) {
239       // Otherwise, neither symbol can be modified.
240       Asm.getContext().reportError(Fixup.getLoc(),
241                                    "unsupported relocation of modified symbol");
242       return;
243     }
244 
245     // We don't support PCrel relocations of differences.
246     if (IsPCRel) {
247       Asm.getContext().reportError(Fixup.getLoc(),
248                                    "unsupported pc-relative relocation of "
249                                    "difference");
250       return;
251     }
252 
253     // AArch64 always uses external relocations. If there is no symbol to use as
254     // a base address (a local symbol with no preceding non-local symbol),
255     // error out.
256     //
257     // FIXME: We should probably just synthesize an external symbol and use
258     // that.
259     if (!A_Base) {
260       Asm.getContext().reportError(
261           Fixup.getLoc(),
262           "unsupported relocation of local symbol '" + A->getName() +
263               "'. Must have non-local symbol earlier in section.");
264       return;
265     }
266     if (!B_Base) {
267       Asm.getContext().reportError(
268           Fixup.getLoc(),
269           "unsupported relocation of local symbol '" + B->getName() +
270               "'. Must have non-local symbol earlier in section.");
271       return;
272     }
273 
274     if (A_Base == B_Base && A_Base) {
275       Asm.getContext().reportError(
276           Fixup.getLoc(), "unsupported relocation with identical base");
277       return;
278     }
279 
280     Value += (!A->getFragment() ? 0 : Writer->getSymbolAddress(*A, Asm)) -
281              (!A_Base || !A_Base->getFragment()
282                   ? 0
283                   : Writer->getSymbolAddress(*A_Base, Asm));
284     Value -= (!B->getFragment() ? 0 : Writer->getSymbolAddress(*B, Asm)) -
285              (!B_Base || !B_Base->getFragment()
286                   ? 0
287                   : Writer->getSymbolAddress(*B_Base, Asm));
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 (!MCAsmInfoDarwin::isSectionAtomizableBySymbols(Sec))
317         Symbol->setUsedInReloc();
318     }
319 
320     const MCSymbol *Base = Writer->getAtom(*Symbol);
321 
322     // If the symbol is a variable it can either be in a section and
323     // we have a base or it is absolute and should have been expanded.
324     assert(!Symbol->isVariable() || Base);
325 
326     // Relocations inside debug sections always use local relocations when
327     // possible. This seems to be done because the debugger doesn't fully
328     // understand relocation entries and expects to find values that
329     // have already been fixed up.
330     if (Symbol->isInSection()) {
331       if (Section.hasAttribute(MachO::S_ATTR_DEBUG))
332         Base = nullptr;
333     }
334 
335     // AArch64 uses external relocations as much as possible. For debug
336     // sections, and for pointer-sized relocations (.quad), we allow section
337     // relocations.  It's code sections that run into trouble.
338     if (Base) {
339       RelSymbol = Base;
340 
341       // Add the local offset, if needed.
342       if (Base != Symbol)
343         Value += Asm.getSymbolOffset(*Symbol) - Asm.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, Asm);
357 
358       if (IsPCRel)
359         Value -= Writer->getFragmentAddress(Asm, Fragment) + Fixup.getOffset() +
360                  (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