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/BinaryFormat/MachO.h"
14 #include "llvm/MC/MCAsmInfo.h"
15 #include "llvm/MC/MCAsmLayout.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:
AArch64MachObjectWriter(uint32_t CPUType,uint32_t CPUSubtype)41 AArch64MachObjectWriter(uint32_t CPUType, uint32_t CPUSubtype)
42 : MCMachObjectTargetWriter(true /* is64Bit */, CPUType, CPUSubtype) {}
43
44 void recordRelocation(MachObjectWriter *Writer, MCAssembler &Asm,
45 const MCAsmLayout &Layout, const MCFragment *Fragment,
46 const MCFixup &Fixup, MCValue Target,
47 uint64_t &FixedValue) override;
48 };
49
50 } // end anonymous namespace
51
getAArch64FixupKindMachOInfo(const MCFixup & Fixup,unsigned & RelocType,const MCSymbolRefExpr * Sym,unsigned & Log2Size,const MCAssembler & Asm)52 bool AArch64MachObjectWriter::getAArch64FixupKindMachOInfo(
53 const MCFixup &Fixup, unsigned &RelocType, const MCSymbolRefExpr *Sym,
54 unsigned &Log2Size, const MCAssembler &Asm) {
55 RelocType = unsigned(MachO::ARM64_RELOC_UNSIGNED);
56 Log2Size = ~0U;
57
58 switch ((unsigned)Fixup.getKind()) {
59 default:
60 return false;
61
62 case FK_Data_1:
63 Log2Size = Log2_32(1);
64 return true;
65 case FK_Data_2:
66 Log2Size = Log2_32(2);
67 return true;
68 case FK_Data_4:
69 Log2Size = Log2_32(4);
70 if (Sym->getKind() == MCSymbolRefExpr::VK_GOT)
71 RelocType = unsigned(MachO::ARM64_RELOC_POINTER_TO_GOT);
72 return true;
73 case FK_Data_8:
74 Log2Size = Log2_32(8);
75 if (Sym->getKind() == MCSymbolRefExpr::VK_GOT)
76 RelocType = unsigned(MachO::ARM64_RELOC_POINTER_TO_GOT);
77 return true;
78 case AArch64::fixup_aarch64_add_imm12:
79 case AArch64::fixup_aarch64_ldst_imm12_scale1:
80 case AArch64::fixup_aarch64_ldst_imm12_scale2:
81 case AArch64::fixup_aarch64_ldst_imm12_scale4:
82 case AArch64::fixup_aarch64_ldst_imm12_scale8:
83 case AArch64::fixup_aarch64_ldst_imm12_scale16:
84 Log2Size = Log2_32(4);
85 switch (Sym->getKind()) {
86 default:
87 return false;
88 case MCSymbolRefExpr::VK_PAGEOFF:
89 RelocType = unsigned(MachO::ARM64_RELOC_PAGEOFF12);
90 return true;
91 case MCSymbolRefExpr::VK_GOTPAGEOFF:
92 RelocType = unsigned(MachO::ARM64_RELOC_GOT_LOAD_PAGEOFF12);
93 return true;
94 case MCSymbolRefExpr::VK_TLVPPAGEOFF:
95 RelocType = unsigned(MachO::ARM64_RELOC_TLVP_LOAD_PAGEOFF12);
96 return true;
97 }
98 case AArch64::fixup_aarch64_pcrel_adrp_imm21:
99 Log2Size = Log2_32(4);
100 // This encompasses the relocation for the whole 21-bit value.
101 switch (Sym->getKind()) {
102 default:
103 Asm.getContext().reportError(Fixup.getLoc(),
104 "ADR/ADRP relocations must be GOT relative");
105 return false;
106 case MCSymbolRefExpr::VK_PAGE:
107 RelocType = unsigned(MachO::ARM64_RELOC_PAGE21);
108 return true;
109 case MCSymbolRefExpr::VK_GOTPAGE:
110 RelocType = unsigned(MachO::ARM64_RELOC_GOT_LOAD_PAGE21);
111 return true;
112 case MCSymbolRefExpr::VK_TLVPPAGE:
113 RelocType = unsigned(MachO::ARM64_RELOC_TLVP_LOAD_PAGE21);
114 return true;
115 }
116 return true;
117 case AArch64::fixup_aarch64_pcrel_branch26:
118 case AArch64::fixup_aarch64_pcrel_call26:
119 Log2Size = Log2_32(4);
120 RelocType = unsigned(MachO::ARM64_RELOC_BRANCH26);
121 return true;
122 }
123 }
124
canUseLocalRelocation(const MCSectionMachO & Section,const MCSymbol & Symbol,unsigned Log2Size)125 static bool canUseLocalRelocation(const MCSectionMachO &Section,
126 const MCSymbol &Symbol, unsigned Log2Size) {
127 // Debug info sections can use local relocations.
128 if (Section.hasAttribute(MachO::S_ATTR_DEBUG))
129 return true;
130
131 // Otherwise, only pointer sized relocations are supported.
132 if (Log2Size != 3)
133 return false;
134
135 // But only if they don't point to a few forbidden sections.
136 if (!Symbol.isInSection())
137 return true;
138 const MCSectionMachO &RefSec = cast<MCSectionMachO>(Symbol.getSection());
139 if (RefSec.getType() == MachO::S_CSTRING_LITERALS)
140 return false;
141
142 if (RefSec.getSegmentName() == "__DATA" &&
143 RefSec.getSectionName() == "__objc_classrefs")
144 return false;
145
146 // FIXME: ld64 currently handles internal pointer-sized relocations
147 // incorrectly (applying the addend twice). We should be able to return true
148 // unconditionally by this point when that's fixed.
149 return false;
150 }
151
recordRelocation(MachObjectWriter * Writer,MCAssembler & Asm,const MCAsmLayout & Layout,const MCFragment * Fragment,const MCFixup & Fixup,MCValue Target,uint64_t & FixedValue)152 void AArch64MachObjectWriter::recordRelocation(
153 MachObjectWriter *Writer, MCAssembler &Asm, const MCAsmLayout &Layout,
154 const MCFragment *Fragment, const MCFixup &Fixup, MCValue Target,
155 uint64_t &FixedValue) {
156 unsigned IsPCRel = Writer->isFixupKindPCRel(Asm, Fixup.getKind());
157
158 // See <reloc.h>.
159 uint32_t FixupOffset = Layout.getFragmentOffset(Fragment);
160 unsigned Log2Size = 0;
161 int64_t Value = 0;
162 unsigned Index = 0;
163 unsigned Type = 0;
164 unsigned Kind = Fixup.getKind();
165 const MCSymbol *RelSymbol = nullptr;
166
167 FixupOffset += Fixup.getOffset();
168
169 // AArch64 pcrel relocation addends do not include the section offset.
170 if (IsPCRel)
171 FixedValue += FixupOffset;
172
173 // ADRP fixups use relocations for the whole symbol value and only
174 // put the addend in the instruction itself. Clear out any value the
175 // generic code figured out from the sybmol definition.
176 if (Kind == AArch64::fixup_aarch64_pcrel_adrp_imm21)
177 FixedValue = 0;
178
179 // imm19 relocations are for conditional branches, which require
180 // assembler local symbols. If we got here, that's not what we have,
181 // so complain loudly.
182 if (Kind == AArch64::fixup_aarch64_pcrel_branch19) {
183 Asm.getContext().reportError(Fixup.getLoc(),
184 "conditional branch requires assembler-local"
185 " label. '" +
186 Target.getSymA()->getSymbol().getName() +
187 "' is external.");
188 return;
189 }
190
191 // 14-bit branch relocations should only target internal labels, and so
192 // should never get here.
193 if (Kind == AArch64::fixup_aarch64_pcrel_branch14) {
194 Asm.getContext().reportError(Fixup.getLoc(),
195 "Invalid relocation on conditional branch!");
196 return;
197 }
198
199 if (!getAArch64FixupKindMachOInfo(Fixup, Type, Target.getSymA(), Log2Size,
200 Asm)) {
201 Asm.getContext().reportError(Fixup.getLoc(), "unknown AArch64 fixup kind!");
202 return;
203 }
204
205 Value = Target.getConstant();
206
207 if (Target.isAbsolute()) { // constant
208 // FIXME: Should this always be extern?
209 // SymbolNum of 0 indicates the absolute section.
210 Type = MachO::ARM64_RELOC_UNSIGNED;
211
212 if (IsPCRel) {
213 Asm.getContext().reportError(Fixup.getLoc(),
214 "PC relative absolute relocation!");
215 return;
216
217 // FIXME: x86_64 sets the type to a branch reloc here. Should we do
218 // something similar?
219 }
220 } else if (Target.getSymB()) { // A - B + constant
221 const MCSymbol *A = &Target.getSymA()->getSymbol();
222 const MCSymbol *A_Base = Asm.getAtom(*A);
223
224 const MCSymbol *B = &Target.getSymB()->getSymbol();
225 const MCSymbol *B_Base = Asm.getAtom(*B);
226
227 // Check for "_foo@got - .", which comes through here as:
228 // Ltmp0:
229 // ... _foo@got - Ltmp0
230 if (Target.getSymA()->getKind() == MCSymbolRefExpr::VK_GOT &&
231 Target.getSymB()->getKind() == MCSymbolRefExpr::VK_None &&
232 Layout.getSymbolOffset(*B) ==
233 Layout.getFragmentOffset(Fragment) + Fixup.getOffset()) {
234 // SymB is the PC, so use a PC-rel pointer-to-GOT relocation.
235 Type = MachO::ARM64_RELOC_POINTER_TO_GOT;
236 IsPCRel = 1;
237 MachO::any_relocation_info MRE;
238 MRE.r_word0 = FixupOffset;
239 MRE.r_word1 = (IsPCRel << 24) | (Log2Size << 25) | (Type << 28);
240 Writer->addRelocation(A_Base, Fragment->getParent(), MRE);
241 return;
242 } else if (Target.getSymA()->getKind() != MCSymbolRefExpr::VK_None ||
243 Target.getSymB()->getKind() != MCSymbolRefExpr::VK_None) {
244 // Otherwise, neither symbol can be modified.
245 Asm.getContext().reportError(Fixup.getLoc(),
246 "unsupported relocation of modified symbol");
247 return;
248 }
249
250 // We don't support PCrel relocations of differences.
251 if (IsPCRel) {
252 Asm.getContext().reportError(Fixup.getLoc(),
253 "unsupported pc-relative relocation of "
254 "difference");
255 return;
256 }
257
258 // AArch64 always uses external relocations. If there is no symbol to use as
259 // a base address (a local symbol with no preceding non-local symbol),
260 // error out.
261 //
262 // FIXME: We should probably just synthesize an external symbol and use
263 // that.
264 if (!A_Base) {
265 Asm.getContext().reportError(
266 Fixup.getLoc(),
267 "unsupported relocation of local symbol '" + A->getName() +
268 "'. Must have non-local symbol earlier in section.");
269 return;
270 }
271 if (!B_Base) {
272 Asm.getContext().reportError(
273 Fixup.getLoc(),
274 "unsupported relocation of local symbol '" + B->getName() +
275 "'. Must have non-local symbol earlier in section.");
276 return;
277 }
278
279 if (A_Base == B_Base && A_Base) {
280 Asm.getContext().reportError(
281 Fixup.getLoc(), "unsupported relocation with identical base");
282 return;
283 }
284
285 Value += (!A->getFragment() ? 0 : Writer->getSymbolAddress(*A, Layout)) -
286 (!A_Base || !A_Base->getFragment() ? 0 : Writer->getSymbolAddress(
287 *A_Base, Layout));
288 Value -= (!B->getFragment() ? 0 : Writer->getSymbolAddress(*B, Layout)) -
289 (!B_Base || !B_Base->getFragment() ? 0 : Writer->getSymbolAddress(
290 *B_Base, Layout));
291
292 Type = MachO::ARM64_RELOC_UNSIGNED;
293
294 MachO::any_relocation_info MRE;
295 MRE.r_word0 = FixupOffset;
296 MRE.r_word1 = (IsPCRel << 24) | (Log2Size << 25) | (Type << 28);
297 Writer->addRelocation(A_Base, Fragment->getParent(), MRE);
298
299 RelSymbol = B_Base;
300 Type = MachO::ARM64_RELOC_SUBTRACTOR;
301 } else { // A + constant
302 const MCSymbol *Symbol = &Target.getSymA()->getSymbol();
303 const MCSectionMachO &Section =
304 static_cast<const MCSectionMachO &>(*Fragment->getParent());
305
306 bool CanUseLocalRelocation =
307 canUseLocalRelocation(Section, *Symbol, Log2Size);
308 if (Symbol->isTemporary() && (Value || !CanUseLocalRelocation)) {
309 // Make sure that the symbol is actually in a section here. If it isn't,
310 // emit an error and exit.
311 if (!Symbol->isInSection()) {
312 Asm.getContext().reportError(
313 Fixup.getLoc(),
314 "unsupported relocation of local symbol '" + Symbol->getName() +
315 "'. Must have non-local symbol earlier in section.");
316 return;
317 }
318 const MCSection &Sec = Symbol->getSection();
319 if (!Asm.getContext().getAsmInfo()->isSectionAtomizableBySymbols(Sec))
320 Symbol->setUsedInReloc();
321 }
322
323 const MCSymbol *Base = Asm.getAtom(*Symbol);
324 // If the symbol is a variable it can either be in a section and
325 // we have a base or it is absolute and should have been expanded.
326 assert(!Symbol->isVariable() || Base);
327
328 // Relocations inside debug sections always use local relocations when
329 // possible. This seems to be done because the debugger doesn't fully
330 // understand relocation entries and expects to find values that
331 // have already been fixed up.
332 if (Symbol->isInSection()) {
333 if (Section.hasAttribute(MachO::S_ATTR_DEBUG))
334 Base = nullptr;
335 }
336
337 // AArch64 uses external relocations as much as possible. For debug
338 // sections, and for pointer-sized relocations (.quad), we allow section
339 // relocations. It's code sections that run into trouble.
340 if (Base) {
341 RelSymbol = Base;
342
343 // Add the local offset, if needed.
344 if (Base != Symbol)
345 Value +=
346 Layout.getSymbolOffset(*Symbol) - Layout.getSymbolOffset(*Base);
347 } else if (Symbol->isInSection()) {
348 if (!CanUseLocalRelocation) {
349 Asm.getContext().reportError(
350 Fixup.getLoc(),
351 "unsupported relocation of local symbol '" + Symbol->getName() +
352 "'. Must have non-local symbol earlier in section.");
353 return;
354 }
355 // Adjust the relocation to be section-relative.
356 // The index is the section ordinal (1-based).
357 const MCSection &Sec = Symbol->getSection();
358 Index = Sec.getOrdinal() + 1;
359 Value += Writer->getSymbolAddress(*Symbol, Layout);
360
361 if (IsPCRel)
362 Value -= Writer->getFragmentAddress(Fragment, Layout) +
363 Fixup.getOffset() + (1ULL << Log2Size);
364 } else {
365 llvm_unreachable(
366 "This constant variable should have been expanded during evaluation");
367 }
368 }
369
370 // If the relocation kind is Branch26, Page21, or Pageoff12, any addend
371 // is represented via an Addend relocation, not encoded directly into
372 // the instruction.
373 if ((Type == MachO::ARM64_RELOC_BRANCH26 ||
374 Type == MachO::ARM64_RELOC_PAGE21 ||
375 Type == MachO::ARM64_RELOC_PAGEOFF12) &&
376 Value) {
377 assert((Value & 0xff000000) == 0 && "Added relocation out of range!");
378
379 MachO::any_relocation_info MRE;
380 MRE.r_word0 = FixupOffset;
381 MRE.r_word1 =
382 (Index << 0) | (IsPCRel << 24) | (Log2Size << 25) | (Type << 28);
383 Writer->addRelocation(RelSymbol, Fragment->getParent(), MRE);
384
385 // Now set up the Addend relocation.
386 Type = MachO::ARM64_RELOC_ADDEND;
387 Index = Value;
388 RelSymbol = nullptr;
389 IsPCRel = 0;
390 Log2Size = 2;
391
392 // Put zero into the instruction itself. The addend is in the relocation.
393 Value = 0;
394 }
395
396 // If there's any addend left to handle, encode it in the instruction.
397 FixedValue = Value;
398
399 // struct relocation_info (8 bytes)
400 MachO::any_relocation_info MRE;
401 MRE.r_word0 = FixupOffset;
402 MRE.r_word1 =
403 (Index << 0) | (IsPCRel << 24) | (Log2Size << 25) | (Type << 28);
404 Writer->addRelocation(RelSymbol, Fragment->getParent(), MRE);
405 }
406
407 std::unique_ptr<MCObjectTargetWriter>
createAArch64MachObjectWriter(uint32_t CPUType,uint32_t CPUSubtype)408 llvm::createAArch64MachObjectWriter(uint32_t CPUType, uint32_t CPUSubtype) {
409 return llvm::make_unique<AArch64MachObjectWriter>(CPUType, CPUSubtype);
410 }
411