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(Value: 1); |
62 | return true; |
63 | case FK_Data_2: |
64 | Log2Size = Log2_32(Value: 2); |
65 | return true; |
66 | case FK_Data_4: |
67 | Log2Size = Log2_32(Value: 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(Value: 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(Value: 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(Value: 4); |
98 | // This encompasses the relocation for the whole 21-bit value. |
99 | switch (Sym->getKind()) { |
100 | default: |
101 | Asm.getContext().reportError(L: Fixup.getLoc(), |
102 | Msg: "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(Value: 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(Value: 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>(Val&: 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, Kind: Fixup.getKind()); |
152 | |
153 | // See <reloc.h>. |
154 | uint32_t FixupOffset = Asm.getFragmentOffset(F: *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(L: Fixup.getLoc(), |
179 | Msg: "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(L: Fixup.getLoc(), |
190 | Msg: "Invalid relocation on conditional branch!" ); |
191 | return; |
192 | } |
193 | |
194 | if (!getAArch64FixupKindMachOInfo(Fixup, RelocType&: Type, Sym: Target.getSymA(), Log2Size, |
195 | Asm)) { |
196 | Asm.getContext().reportError(L: Fixup.getLoc(), Msg: "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(L: Fixup.getLoc(), |
209 | Msg: "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(S: *A); |
218 | |
219 | const MCSymbol *B = &Target.getSymB()->getSymbol(); |
220 | const MCSymbol *B_Base = Writer->getAtom(S: *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(S: *B) == |
228 | Asm.getFragmentOffset(F: *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(RelSymbol: A_Base, Sec: 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(L: Fixup.getLoc(), |
241 | Msg: "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(L: Fixup.getLoc(), |
248 | Msg: "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 | L: Fixup.getLoc(), |
262 | Msg: "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 | L: Fixup.getLoc(), |
269 | Msg: "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 | L: Fixup.getLoc(), Msg: "unsupported relocation with identical base" ); |
277 | return; |
278 | } |
279 | |
280 | Value += (!A->getFragment() ? 0 : Writer->getSymbolAddress(S: *A, Asm)) - |
281 | (!A_Base || !A_Base->getFragment() |
282 | ? 0 |
283 | : Writer->getSymbolAddress(S: *A_Base, Asm)); |
284 | Value -= (!B->getFragment() ? 0 : Writer->getSymbolAddress(S: *B, Asm)) - |
285 | (!B_Base || !B_Base->getFragment() |
286 | ? 0 |
287 | : Writer->getSymbolAddress(S: *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(RelSymbol: A_Base, Sec: 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: *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 | L: Fixup.getLoc(), |
311 | Msg: "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(Section: Sec)) |
317 | Symbol->setUsedInReloc(); |
318 | } |
319 | |
320 | const MCSymbol *Base = Writer->getAtom(S: *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(Value: 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(S: *Symbol) - Asm.getSymbolOffset(S: *Base); |
344 | } else if (Symbol->isInSection()) { |
345 | if (!CanUseLocalRelocation) { |
346 | Asm.getContext().reportError( |
347 | L: Fixup.getLoc(), |
348 | Msg: "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(S: *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>(x: Value)) { |
375 | Asm.getContext().reportError(L: Fixup.getLoc(), |
376 | Msg: "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, Sec: 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, Sec: 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>(args&: CPUType, args&: CPUSubtype, |
412 | args&: IsILP32); |
413 | } |
414 | |