Line data Source code
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 0 : 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)
42 0 : : 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 :
52 0 : bool AArch64MachObjectWriter::getAArch64FixupKindMachOInfo(
53 : const MCFixup &Fixup, unsigned &RelocType, const MCSymbolRefExpr *Sym,
54 : unsigned &Log2Size, const MCAssembler &Asm) {
55 0 : RelocType = unsigned(MachO::ARM64_RELOC_UNSIGNED);
56 0 : Log2Size = ~0U;
57 :
58 0 : switch ((unsigned)Fixup.getKind()) {
59 : default:
60 : return false;
61 :
62 : case FK_Data_1:
63 0 : Log2Size = Log2_32(1);
64 0 : return true;
65 : case FK_Data_2:
66 0 : Log2Size = Log2_32(2);
67 0 : return true;
68 : case FK_Data_4:
69 0 : Log2Size = Log2_32(4);
70 0 : if (Sym->getKind() == MCSymbolRefExpr::VK_GOT)
71 0 : RelocType = unsigned(MachO::ARM64_RELOC_POINTER_TO_GOT);
72 : return true;
73 : case FK_Data_8:
74 0 : Log2Size = Log2_32(8);
75 0 : if (Sym->getKind() == MCSymbolRefExpr::VK_GOT)
76 0 : 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 0 : Log2Size = Log2_32(4);
85 0 : switch (Sym->getKind()) {
86 : default:
87 : return false;
88 0 : case MCSymbolRefExpr::VK_PAGEOFF:
89 0 : RelocType = unsigned(MachO::ARM64_RELOC_PAGEOFF12);
90 0 : return true;
91 0 : case MCSymbolRefExpr::VK_GOTPAGEOFF:
92 0 : RelocType = unsigned(MachO::ARM64_RELOC_GOT_LOAD_PAGEOFF12);
93 0 : return true;
94 0 : case MCSymbolRefExpr::VK_TLVPPAGEOFF:
95 0 : RelocType = unsigned(MachO::ARM64_RELOC_TLVP_LOAD_PAGEOFF12);
96 0 : return true;
97 : }
98 : case AArch64::fixup_aarch64_pcrel_adrp_imm21:
99 0 : Log2Size = Log2_32(4);
100 : // This encompasses the relocation for the whole 21-bit value.
101 0 : switch (Sym->getKind()) {
102 0 : default:
103 0 : Asm.getContext().reportError(Fixup.getLoc(),
104 : "ADR/ADRP relocations must be GOT relative");
105 0 : return false;
106 0 : case MCSymbolRefExpr::VK_PAGE:
107 0 : RelocType = unsigned(MachO::ARM64_RELOC_PAGE21);
108 0 : return true;
109 0 : case MCSymbolRefExpr::VK_GOTPAGE:
110 0 : RelocType = unsigned(MachO::ARM64_RELOC_GOT_LOAD_PAGE21);
111 0 : return true;
112 0 : case MCSymbolRefExpr::VK_TLVPPAGE:
113 0 : RelocType = unsigned(MachO::ARM64_RELOC_TLVP_LOAD_PAGE21);
114 0 : return true;
115 : }
116 : return true;
117 : case AArch64::fixup_aarch64_pcrel_branch26:
118 : case AArch64::fixup_aarch64_pcrel_call26:
119 0 : Log2Size = Log2_32(4);
120 0 : RelocType = unsigned(MachO::ARM64_RELOC_BRANCH26);
121 0 : return true;
122 : }
123 : }
124 :
125 211 : static bool canUseLocalRelocation(const MCSectionMachO &Section,
126 : const MCSymbol &Symbol, unsigned Log2Size) {
127 : // Debug info sections can use local relocations.
128 422 : if (Section.hasAttribute(MachO::S_ATTR_DEBUG))
129 : return true;
130 :
131 : // Otherwise, only pointer sized relocations are supported.
132 147 : if (Log2Size != 3)
133 : return false;
134 :
135 : // But only if they don't point to a few forbidden sections.
136 37 : if (!Symbol.isInSection())
137 : return true;
138 : const MCSectionMachO &RefSec = cast<MCSectionMachO>(Symbol.getSection());
139 60 : 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 :
152 239 : 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 239 : unsigned IsPCRel = Writer->isFixupKindPCRel(Asm, Fixup.getKind());
157 :
158 : // See <reloc.h>.
159 239 : uint32_t FixupOffset = Layout.getFragmentOffset(Fragment);
160 239 : unsigned Log2Size = 0;
161 : int64_t Value = 0;
162 : unsigned Index = 0;
163 239 : unsigned Type = 0;
164 239 : unsigned Kind = Fixup.getKind();
165 : const MCSymbol *RelSymbol = nullptr;
166 :
167 239 : FixupOffset += Fixup.getOffset();
168 :
169 : // AArch64 pcrel relocation addends do not include the section offset.
170 239 : if (IsPCRel)
171 69 : 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 239 : if (Kind == AArch64::fixup_aarch64_pcrel_adrp_imm21)
177 32 : 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 239 : if (Kind == AArch64::fixup_aarch64_pcrel_branch19) {
183 2 : Asm.getContext().reportError(Fixup.getLoc(),
184 : "conditional branch requires assembler-local"
185 1 : " label. '" +
186 2 : Target.getSymA()->getSymbol().getName() +
187 1 : "' is external.");
188 19 : return;
189 : }
190 :
191 : // 14-bit branch relocations should only target internal labels, and so
192 : // should never get here.
193 238 : if (Kind == AArch64::fixup_aarch64_pcrel_branch14) {
194 2 : Asm.getContext().reportError(Fixup.getLoc(),
195 : "Invalid relocation on conditional branch!");
196 1 : return;
197 : }
198 :
199 237 : if (!getAArch64FixupKindMachOInfo(Fixup, Type, Target.getSymA(), Log2Size,
200 : Asm)) {
201 28 : Asm.getContext().reportError(Fixup.getLoc(), "unknown AArch64 fixup kind!");
202 14 : return;
203 : }
204 :
205 223 : 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 0 : Type = MachO::ARM64_RELOC_UNSIGNED;
211 :
212 0 : if (IsPCRel) {
213 0 : Asm.getContext().reportError(Fixup.getLoc(),
214 : "PC relative absolute relocation!");
215 0 : return;
216 :
217 : // FIXME: x86_64 sets the type to a branch reloc here. Should we do
218 : // something similar?
219 : }
220 223 : } else if (Target.getSymB()) { // A - B + constant
221 12 : const MCSymbol *A = &Target.getSymA()->getSymbol();
222 12 : const MCSymbol *A_Base = Asm.getAtom(*A);
223 :
224 12 : const MCSymbol *B = &Target.getSymB()->getSymbol();
225 12 : 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 14 : if (Target.getSymA()->getKind() == MCSymbolRefExpr::VK_GOT &&
231 12 : Target.getSymB()->getKind() == MCSymbolRefExpr::VK_None &&
232 2 : Layout.getSymbolOffset(*B) ==
233 2 : Layout.getFragmentOffset(Fragment) + Fixup.getOffset()) {
234 : // SymB is the PC, so use a PC-rel pointer-to-GOT relocation.
235 2 : Type = MachO::ARM64_RELOC_POINTER_TO_GOT;
236 : IsPCRel = 1;
237 : MachO::any_relocation_info MRE;
238 2 : MRE.r_word0 = FixupOffset;
239 2 : MRE.r_word1 = (IsPCRel << 24) | (Log2Size << 25) | (Type << 28);
240 2 : Writer->addRelocation(A_Base, Fragment->getParent(), MRE);
241 : return;
242 10 : } else if (Target.getSymA()->getKind() != MCSymbolRefExpr::VK_None ||
243 10 : Target.getSymB()->getKind() != MCSymbolRefExpr::VK_None) {
244 : // Otherwise, neither symbol can be modified.
245 0 : Asm.getContext().reportError(Fixup.getLoc(),
246 : "unsupported relocation of modified symbol");
247 0 : return;
248 : }
249 :
250 : // We don't support PCrel relocations of differences.
251 10 : if (IsPCRel) {
252 0 : Asm.getContext().reportError(Fixup.getLoc(),
253 : "unsupported pc-relative relocation of "
254 : "difference");
255 0 : 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 10 : if (!A_Base) {
265 0 : Asm.getContext().reportError(
266 : Fixup.getLoc(),
267 0 : "unsupported relocation of local symbol '" + A->getName() +
268 0 : "'. Must have non-local symbol earlier in section.");
269 0 : return;
270 : }
271 10 : if (!B_Base) {
272 0 : Asm.getContext().reportError(
273 : Fixup.getLoc(),
274 0 : "unsupported relocation of local symbol '" + B->getName() +
275 0 : "'. Must have non-local symbol earlier in section.");
276 0 : return;
277 : }
278 :
279 10 : if (A_Base == B_Base && A_Base) {
280 0 : Asm.getContext().reportError(
281 : Fixup.getLoc(), "unsupported relocation with identical base");
282 0 : return;
283 : }
284 :
285 10 : Value += (!A->getFragment() ? 0 : Writer->getSymbolAddress(*A, Layout)) -
286 10 : (!A_Base || !A_Base->getFragment() ? 0 : Writer->getSymbolAddress(
287 : *A_Base, Layout));
288 10 : Value -= (!B->getFragment() ? 0 : Writer->getSymbolAddress(*B, Layout)) -
289 10 : (!B_Base || !B_Base->getFragment() ? 0 : Writer->getSymbolAddress(
290 : *B_Base, Layout));
291 :
292 10 : Type = MachO::ARM64_RELOC_UNSIGNED;
293 :
294 : MachO::any_relocation_info MRE;
295 10 : MRE.r_word0 = FixupOffset;
296 10 : MRE.r_word1 = (IsPCRel << 24) | (Log2Size << 25) | (Type << 28);
297 10 : Writer->addRelocation(A_Base, Fragment->getParent(), MRE);
298 :
299 : RelSymbol = B_Base;
300 10 : Type = MachO::ARM64_RELOC_SUBTRACTOR;
301 : } else { // A + constant
302 211 : const MCSymbol *Symbol = &Target.getSymA()->getSymbol();
303 : const MCSectionMachO &Section =
304 211 : static_cast<const MCSectionMachO &>(*Fragment->getParent());
305 :
306 : bool CanUseLocalRelocation =
307 211 : canUseLocalRelocation(Section, *Symbol, Log2Size);
308 211 : 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 14 : if (!Symbol->isInSection()) {
312 2 : Asm.getContext().reportError(
313 : Fixup.getLoc(),
314 1 : "unsupported relocation of local symbol '" + Symbol->getName() +
315 1 : "'. Must have non-local symbol earlier in section.");
316 1 : return;
317 : }
318 : const MCSection &Sec = Symbol->getSection();
319 13 : if (!Asm.getContext().getAsmInfo()->isSectionAtomizableBySymbols(Sec))
320 : Symbol->setUsedInReloc();
321 : }
322 :
323 210 : 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 210 : if (Symbol->isInSection()) {
333 302 : 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 147 : if (Base) {
341 : RelSymbol = Base;
342 :
343 : // Add the local offset, if needed.
344 147 : if (Base != Symbol)
345 2 : Value +=
346 2 : Layout.getSymbolOffset(*Symbol) - Layout.getSymbolOffset(*Base);
347 63 : } else if (Symbol->isInSection()) {
348 63 : if (!CanUseLocalRelocation) {
349 0 : Asm.getContext().reportError(
350 : Fixup.getLoc(),
351 0 : "unsupported relocation of local symbol '" + Symbol->getName() +
352 0 : "'. Must have non-local symbol earlier in section.");
353 0 : 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 63 : Index = Sec.getOrdinal() + 1;
359 63 : Value += Writer->getSymbolAddress(*Symbol, Layout);
360 :
361 63 : if (IsPCRel)
362 0 : Value -= Writer->getFragmentAddress(Fragment, Layout) +
363 0 : Fixup.getOffset() + (1ULL << Log2Size);
364 : } else {
365 0 : 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 220 : if ((Type == MachO::ARM64_RELOC_BRANCH26 ||
374 220 : Type == MachO::ARM64_RELOC_PAGE21 ||
375 83 : 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 6 : MRE.r_word0 = FixupOffset;
381 6 : MRE.r_word1 =
382 6 : (Index << 0) | (IsPCRel << 24) | (Log2Size << 25) | (Type << 28);
383 6 : Writer->addRelocation(RelSymbol, Fragment->getParent(), MRE);
384 :
385 : // Now set up the Addend relocation.
386 6 : Type = MachO::ARM64_RELOC_ADDEND;
387 6 : Index = Value;
388 : RelSymbol = nullptr;
389 : IsPCRel = 0;
390 6 : 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 220 : FixedValue = Value;
398 :
399 : // struct relocation_info (8 bytes)
400 : MachO::any_relocation_info MRE;
401 220 : MRE.r_word0 = FixupOffset;
402 220 : MRE.r_word1 =
403 220 : (Index << 0) | (IsPCRel << 24) | (Log2Size << 25) | (Type << 28);
404 220 : Writer->addRelocation(RelSymbol, Fragment->getParent(), MRE);
405 : }
406 :
407 : std::unique_ptr<MCObjectTargetWriter>
408 368 : llvm::createAArch64MachObjectWriter(uint32_t CPUType, uint32_t CPUSubtype) {
409 368 : return llvm::make_unique<AArch64MachObjectWriter>(CPUType, CPUSubtype);
410 : }
|