1 | //===-- X86MachObjectWriter.cpp - X86 Mach-O 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/X86FixupKinds.h" |
10 | #include "MCTargetDesc/X86MCAsmInfo.h" |
11 | #include "MCTargetDesc/X86MCTargetDesc.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/MCMachObjectWriter.h" |
19 | #include "llvm/MC/MCSectionMachO.h" |
20 | #include "llvm/MC/MCValue.h" |
21 | #include "llvm/Support/ErrorHandling.h" |
22 | #include "llvm/Support/Format.h" |
23 | |
24 | using namespace llvm; |
25 | |
26 | namespace { |
27 | class X86MachObjectWriter : public MCMachObjectTargetWriter { |
28 | bool recordScatteredRelocation(MachObjectWriter *Writer, |
29 | const MCAssembler &Asm, |
30 | const MCFragment *Fragment, |
31 | const MCFixup &Fixup, |
32 | MCValue Target, |
33 | unsigned Log2Size, |
34 | uint64_t &FixedValue); |
35 | void recordTLVPRelocation(MachObjectWriter *Writer, |
36 | const MCAssembler &Asm, |
37 | const MCFragment *Fragment, |
38 | const MCFixup &Fixup, |
39 | MCValue Target, |
40 | uint64_t &FixedValue); |
41 | |
42 | void RecordX86Relocation(MachObjectWriter *Writer, |
43 | const MCAssembler &Asm, |
44 | const MCFragment *Fragment, |
45 | const MCFixup &Fixup, |
46 | MCValue Target, |
47 | uint64_t &FixedValue); |
48 | void RecordX86_64Relocation(MachObjectWriter *Writer, MCAssembler &Asm, |
49 | const MCFragment *Fragment, const MCFixup &Fixup, |
50 | MCValue Target, uint64_t &FixedValue); |
51 | |
52 | public: |
53 | X86MachObjectWriter(bool Is64Bit, uint32_t CPUType, uint32_t CPUSubtype) |
54 | : MCMachObjectTargetWriter(Is64Bit, CPUType, CPUSubtype) {} |
55 | |
56 | void recordRelocation(MachObjectWriter *Writer, MCAssembler &Asm, |
57 | const MCFragment *Fragment, const MCFixup &Fixup, |
58 | MCValue Target, uint64_t &FixedValue) override { |
59 | if (Writer->is64Bit()) |
60 | RecordX86_64Relocation(Writer, Asm, Fragment, Fixup, Target, FixedValue); |
61 | else |
62 | RecordX86Relocation(Writer, Asm, Fragment, Fixup, Target, FixedValue); |
63 | } |
64 | }; |
65 | } // namespace |
66 | |
67 | static bool isFixupKindRIPRel(unsigned Kind) { |
68 | return Kind == X86::reloc_riprel_4byte || |
69 | Kind == X86::reloc_riprel_4byte_movq_load || |
70 | Kind == X86::reloc_riprel_4byte_movq_load_rex2 || |
71 | Kind == X86::reloc_riprel_4byte_relax || |
72 | Kind == X86::reloc_riprel_4byte_relax_rex || |
73 | Kind == X86::reloc_riprel_4byte_relax_rex2 || |
74 | Kind == X86::reloc_riprel_4byte_relax_evex; |
75 | } |
76 | |
77 | static unsigned getFixupKindLog2Size(unsigned Kind) { |
78 | switch (Kind) { |
79 | default: |
80 | llvm_unreachable("invalid fixup kind!" ); |
81 | case FK_PCRel_1: |
82 | case FK_Data_1: return 0; |
83 | case FK_PCRel_2: |
84 | case FK_Data_2: return 1; |
85 | case FK_PCRel_4: |
86 | // FIXME: Remove these!!! |
87 | case X86::reloc_riprel_4byte: |
88 | case X86::reloc_riprel_4byte_relax: |
89 | case X86::reloc_riprel_4byte_relax_rex: |
90 | case X86::reloc_riprel_4byte_relax_rex2: |
91 | case X86::reloc_riprel_4byte_movq_load: |
92 | case X86::reloc_riprel_4byte_movq_load_rex2: |
93 | case X86::reloc_signed_4byte: |
94 | case X86::reloc_signed_4byte_relax: |
95 | case X86::reloc_branch_4byte_pcrel: |
96 | case X86::reloc_riprel_4byte_relax_evex: |
97 | case FK_Data_4: return 2; |
98 | case FK_Data_8: return 3; |
99 | } |
100 | } |
101 | |
102 | void X86MachObjectWriter::RecordX86_64Relocation( |
103 | MachObjectWriter *Writer, MCAssembler &Asm, const MCFragment *Fragment, |
104 | const MCFixup &Fixup, MCValue Target, uint64_t &FixedValue) { |
105 | unsigned IsPCRel = Fixup.isPCRel(); |
106 | unsigned IsRIPRel = isFixupKindRIPRel(Kind: Fixup.getKind()); |
107 | unsigned Log2Size = getFixupKindLog2Size(Kind: Fixup.getKind()); |
108 | |
109 | // See <reloc.h>. |
110 | uint32_t FixupOffset = Asm.getFragmentOffset(F: *Fragment) + Fixup.getOffset(); |
111 | uint32_t FixupAddress = |
112 | Writer->getFragmentAddress(Asm, Fragment) + Fixup.getOffset(); |
113 | int64_t Value = 0; |
114 | unsigned Index = 0; |
115 | unsigned IsExtern = 0; |
116 | unsigned Type = 0; |
117 | const MCSymbol *RelSymbol = nullptr; |
118 | |
119 | Value = Target.getConstant(); |
120 | |
121 | if (IsPCRel) { |
122 | // Compensate for the relocation offset, Darwin x86_64 relocations only have |
123 | // the addend and appear to have attempted to define it to be the actual |
124 | // expression addend without the PCrel bias. However, instructions with data |
125 | // following the relocation are not accommodated for (see comment below |
126 | // regarding SIGNED{1,2,4}), so it isn't exactly that either. |
127 | Value += 1LL << Log2Size; |
128 | } |
129 | |
130 | if (Target.isAbsolute()) { // constant |
131 | // SymbolNum of 0 indicates the absolute section. |
132 | Type = MachO::X86_64_RELOC_UNSIGNED; |
133 | |
134 | // FIXME: I believe this is broken, I don't think the linker can understand |
135 | // it. I think it would require a local relocation, but I'm not sure if that |
136 | // would work either. The official way to get an absolute PCrel relocation |
137 | // is to use an absolute symbol (which we don't support yet). |
138 | if (IsPCRel) { |
139 | IsExtern = 1; |
140 | Type = MachO::X86_64_RELOC_BRANCH; |
141 | } |
142 | } else if (Target.getSubSym()) { // A - B + constant |
143 | const MCSymbol *A = Target.getAddSym(); |
144 | if (A->isTemporary()) |
145 | A = &Writer->findAliasedSymbol(Sym: *A); |
146 | const MCSymbol *A_Base = Writer->getAtom(S: *A); |
147 | |
148 | const MCSymbol *B = Target.getSubSym(); |
149 | if (B->isTemporary()) |
150 | B = &Writer->findAliasedSymbol(Sym: *B); |
151 | const MCSymbol *B_Base = Writer->getAtom(S: *B); |
152 | |
153 | // Neither symbol can be modified. |
154 | if (Target.getSpecifier()) { |
155 | reportError(L: Fixup.getLoc(), Msg: "unsupported relocation of modified symbol" ); |
156 | return; |
157 | } |
158 | |
159 | // We don't support PCrel relocations of differences. Darwin 'as' doesn't |
160 | // implement most of these correctly. |
161 | if (IsPCRel) { |
162 | reportError(L: Fixup.getLoc(), |
163 | Msg: "unsupported pc-relative relocation of difference" ); |
164 | return; |
165 | } |
166 | |
167 | // The support for the situation where one or both of the symbols would |
168 | // require a local relocation is handled just like if the symbols were |
169 | // external. This is certainly used in the case of debug sections where the |
170 | // section has only temporary symbols and thus the symbols don't have base |
171 | // symbols. This is encoded using the section ordinal and non-extern |
172 | // relocation entries. |
173 | |
174 | // Darwin 'as' doesn't emit correct relocations for this (it ends up with a |
175 | // single SIGNED relocation); reject it for now. Except the case where both |
176 | // symbols don't have a base, equal but both NULL. |
177 | if (A_Base == B_Base && A_Base) { |
178 | reportError(L: Fixup.getLoc(), Msg: "unsupported relocation with identical base" ); |
179 | return; |
180 | } |
181 | |
182 | // A subtraction expression where either symbol is undefined is a |
183 | // non-relocatable expression. |
184 | if (A->isUndefined() || B->isUndefined()) { |
185 | StringRef Name = A->isUndefined() ? A->getName() : B->getName(); |
186 | reportError( |
187 | L: Fixup.getLoc(), |
188 | Msg: "unsupported relocation with subtraction expression, symbol '" + |
189 | Name + "' can not be undefined in a subtraction expression" ); |
190 | return; |
191 | } |
192 | |
193 | Value += Writer->getSymbolAddress(S: *A) - |
194 | (!A_Base ? 0 : Writer->getSymbolAddress(S: *A_Base)); |
195 | Value -= Writer->getSymbolAddress(S: *B) - |
196 | (!B_Base ? 0 : Writer->getSymbolAddress(S: *B_Base)); |
197 | |
198 | if (!A_Base) |
199 | Index = A->getFragment()->getParent()->getOrdinal() + 1; |
200 | Type = MachO::X86_64_RELOC_UNSIGNED; |
201 | |
202 | MachO::any_relocation_info MRE; |
203 | MRE.r_word0 = FixupOffset; |
204 | MRE.r_word1 = |
205 | (Index << 0) | (IsPCRel << 24) | (Log2Size << 25) | (Type << 28); |
206 | Writer->addRelocation(RelSymbol: A_Base, Sec: Fragment->getParent(), MRE); |
207 | |
208 | if (B_Base) |
209 | RelSymbol = B_Base; |
210 | else |
211 | Index = B->getFragment()->getParent()->getOrdinal() + 1; |
212 | Type = MachO::X86_64_RELOC_SUBTRACTOR; |
213 | } else { |
214 | const MCSymbol *Symbol = Target.getAddSym(); |
215 | if (Symbol->isTemporary() && Value) { |
216 | const MCSection &Sec = Symbol->getSection(); |
217 | if (!MCAsmInfoDarwin::isSectionAtomizableBySymbols(Section: Sec)) |
218 | Symbol->setUsedInReloc(); |
219 | } |
220 | RelSymbol = Writer->getAtom(S: *Symbol); |
221 | |
222 | // Relocations inside debug sections always use local relocations when |
223 | // possible. This seems to be done because the debugger doesn't fully |
224 | // understand x86_64 relocation entries, and expects to find values that |
225 | // have already been fixed up. |
226 | if (Symbol->isInSection()) { |
227 | const MCSectionMachO &Section = |
228 | static_cast<const MCSectionMachO &>(*Fragment->getParent()); |
229 | if (Section.hasAttribute(Value: MachO::S_ATTR_DEBUG)) |
230 | RelSymbol = nullptr; |
231 | } |
232 | |
233 | // x86_64 almost always uses external relocations, except when there is no |
234 | // symbol to use as a base address (a local symbol with no preceding |
235 | // non-local symbol). |
236 | if (RelSymbol) { |
237 | // Add the local offset, if needed. |
238 | if (RelSymbol != Symbol) |
239 | Value += Asm.getSymbolOffset(S: *Symbol) - Asm.getSymbolOffset(S: *RelSymbol); |
240 | } else if (Symbol->isInSection() && !Symbol->isVariable()) { |
241 | // The index is the section ordinal (1-based). |
242 | Index = Symbol->getFragment()->getParent()->getOrdinal() + 1; |
243 | Value += Writer->getSymbolAddress(S: *Symbol); |
244 | |
245 | if (IsPCRel) |
246 | Value -= FixupAddress + (1 << Log2Size); |
247 | } else if (Symbol->isVariable()) { |
248 | FixedValue = Writer->getSymbolAddress(S: *Symbol); |
249 | return; |
250 | } else { |
251 | reportError(L: Fixup.getLoc(), |
252 | Msg: "unsupported relocation of undefined symbol '" + |
253 | Symbol->getName() + "'" ); |
254 | return; |
255 | } |
256 | |
257 | auto Specifier = Target.getSpecifier(); |
258 | if (IsPCRel) { |
259 | if (IsRIPRel) { |
260 | if (Specifier == X86::S_GOTPCREL) { |
261 | // x86_64 distinguishes movq foo@GOTPCREL so that the linker can |
262 | // rewrite the movq to an leaq at link time if the symbol ends up in |
263 | // the same linkage unit. |
264 | if (Fixup.getTargetKind() == X86::reloc_riprel_4byte_movq_load) |
265 | Type = MachO::X86_64_RELOC_GOT_LOAD; |
266 | else |
267 | Type = MachO::X86_64_RELOC_GOT; |
268 | } else if (Specifier == X86::S_TLVP) { |
269 | Type = MachO::X86_64_RELOC_TLV; |
270 | } else if (Specifier) { |
271 | reportError(L: Fixup.getLoc(), |
272 | Msg: "unsupported symbol modifier in relocation" ); |
273 | return; |
274 | } else { |
275 | Type = MachO::X86_64_RELOC_SIGNED; |
276 | |
277 | // The Darwin x86_64 relocation format has a problem where it cannot |
278 | // encode an address (L<foo> + <constant>) which is outside the atom |
279 | // containing L<foo>. Generally, this shouldn't occur but it does |
280 | // happen when we have a RIPrel instruction with data following the |
281 | // relocation entry (e.g., movb $012, L0(%rip)). Even with the PCrel |
282 | // adjustment Darwin x86_64 uses, the offset is still negative and the |
283 | // linker has no way to recognize this. |
284 | // |
285 | // To work around this, Darwin uses several special relocation types |
286 | // to indicate the offsets. However, the specification or |
287 | // implementation of these seems to also be incomplete; they should |
288 | // adjust the addend as well based on the actual encoded instruction |
289 | // (the additional bias), but instead appear to just look at the final |
290 | // offset. |
291 | switch (-(Target.getConstant() + (1LL << Log2Size))) { |
292 | case 1: Type = MachO::X86_64_RELOC_SIGNED_1; break; |
293 | case 2: Type = MachO::X86_64_RELOC_SIGNED_2; break; |
294 | case 4: Type = MachO::X86_64_RELOC_SIGNED_4; break; |
295 | } |
296 | } |
297 | } else { |
298 | if (Specifier) { |
299 | reportError(L: Fixup.getLoc(), |
300 | Msg: "unsupported symbol modifier in branch relocation" ); |
301 | return; |
302 | } |
303 | |
304 | Type = MachO::X86_64_RELOC_BRANCH; |
305 | } |
306 | } else { |
307 | if (Specifier == X86::S_GOT) { |
308 | Type = MachO::X86_64_RELOC_GOT; |
309 | } else if (Specifier == X86::S_GOTPCREL) { |
310 | // GOTPCREL is allowed as a modifier on non-PCrel instructions, in which |
311 | // case all we do is set the PCrel bit in the relocation entry; this is |
312 | // used with exception handling, for example. The source is required to |
313 | // include any necessary offset directly. |
314 | Type = MachO::X86_64_RELOC_GOT; |
315 | IsPCRel = 1; |
316 | } else if (Specifier == X86::S_TLVP) { |
317 | reportError(L: Fixup.getLoc(), |
318 | Msg: "TLVP symbol modifier should have been rip-rel" ); |
319 | return; |
320 | } else if (Specifier) { |
321 | reportError(L: Fixup.getLoc(), |
322 | Msg: "unsupported symbol modifier in relocation" ); |
323 | return; |
324 | } else { |
325 | Type = MachO::X86_64_RELOC_UNSIGNED; |
326 | if (Fixup.getTargetKind() == X86::reloc_signed_4byte) { |
327 | reportError( |
328 | L: Fixup.getLoc(), |
329 | Msg: "32-bit absolute addressing is not supported in 64-bit mode" ); |
330 | return; |
331 | } |
332 | } |
333 | } |
334 | } |
335 | |
336 | // x86_64 always writes custom values into the fixups. |
337 | FixedValue = Value; |
338 | |
339 | // struct relocation_info (8 bytes) |
340 | MachO::any_relocation_info MRE; |
341 | MRE.r_word0 = FixupOffset; |
342 | MRE.r_word1 = (Index << 0) | (IsPCRel << 24) | (Log2Size << 25) | |
343 | (IsExtern << 27) | (Type << 28); |
344 | Writer->addRelocation(RelSymbol, Sec: Fragment->getParent(), MRE); |
345 | } |
346 | |
347 | bool X86MachObjectWriter::recordScatteredRelocation(MachObjectWriter *Writer, |
348 | const MCAssembler &Asm, |
349 | const MCFragment *Fragment, |
350 | const MCFixup &Fixup, |
351 | MCValue Target, |
352 | unsigned Log2Size, |
353 | uint64_t &FixedValue) { |
354 | uint64_t OriginalFixedValue = FixedValue; |
355 | uint32_t FixupOffset = Asm.getFragmentOffset(F: *Fragment) + Fixup.getOffset(); |
356 | unsigned IsPCRel = Fixup.isPCRel(); |
357 | unsigned Type = MachO::GENERIC_RELOC_VANILLA; |
358 | |
359 | // See <reloc.h>. |
360 | const MCSymbol *A = Target.getAddSym(); |
361 | |
362 | if (!A->getFragment()) { |
363 | reportError(L: Fixup.getLoc(), |
364 | Msg: "symbol '" + A->getName() + |
365 | "' can not be undefined in a subtraction expression" ); |
366 | return false; |
367 | } |
368 | |
369 | uint32_t Value = Writer->getSymbolAddress(S: *A); |
370 | uint64_t SecAddr = Writer->getSectionAddress(Sec: A->getFragment()->getParent()); |
371 | FixedValue += SecAddr; |
372 | uint32_t Value2 = 0; |
373 | |
374 | if (const MCSymbol *SB = Target.getSubSym()) { |
375 | if (!SB->getFragment()) { |
376 | reportError(L: Fixup.getLoc(), |
377 | Msg: "symbol '" + SB->getName() + |
378 | "' can not be undefined in a subtraction expression" ); |
379 | return false; |
380 | } |
381 | |
382 | // Select the appropriate difference relocation type. |
383 | // |
384 | // Note that there is no longer any semantic difference between these two |
385 | // relocation types from the linkers point of view, this is done solely for |
386 | // pedantic compatibility with 'as'. |
387 | Type = A->isExternal() ? (unsigned)MachO::GENERIC_RELOC_SECTDIFF |
388 | : (unsigned)MachO::GENERIC_RELOC_LOCAL_SECTDIFF; |
389 | Value2 = Writer->getSymbolAddress(S: *SB); |
390 | FixedValue -= Writer->getSectionAddress(Sec: SB->getFragment()->getParent()); |
391 | } |
392 | |
393 | // Relocations are written out in reverse order, so the PAIR comes first. |
394 | if (Type == MachO::GENERIC_RELOC_SECTDIFF || |
395 | Type == MachO::GENERIC_RELOC_LOCAL_SECTDIFF) { |
396 | // If the offset is too large to fit in a scattered relocation, |
397 | // we're hosed. It's an unfortunate limitation of the MachO format. |
398 | if (FixupOffset > 0xffffff) { |
399 | char Buffer[32]; |
400 | format(Fmt: "0x%x" , Vals: FixupOffset).print(Buffer, BufferSize: sizeof(Buffer)); |
401 | reportError(L: Fixup.getLoc(), Msg: Twine("Section too large, can't encode " |
402 | "r_address (" ) + |
403 | Buffer + |
404 | ") into 24 bits of scattered " |
405 | "relocation entry." ); |
406 | return false; |
407 | } |
408 | |
409 | MachO::any_relocation_info MRE; |
410 | MRE.r_word0 = ((0 << 0) | // r_address |
411 | (MachO::GENERIC_RELOC_PAIR << 24) | // r_type |
412 | (Log2Size << 28) | |
413 | (IsPCRel << 30) | |
414 | MachO::R_SCATTERED); |
415 | MRE.r_word1 = Value2; |
416 | Writer->addRelocation(RelSymbol: nullptr, Sec: Fragment->getParent(), MRE); |
417 | } else { |
418 | // If the offset is more than 24-bits, it won't fit in a scattered |
419 | // relocation offset field, so we fall back to using a non-scattered |
420 | // relocation. This is a bit risky, as if the offset reaches out of |
421 | // the block and the linker is doing scattered loading on this |
422 | // symbol, things can go badly. |
423 | // |
424 | // Required for 'as' compatibility. |
425 | if (FixupOffset > 0xffffff) { |
426 | FixedValue = OriginalFixedValue; |
427 | return false; |
428 | } |
429 | } |
430 | |
431 | MachO::any_relocation_info MRE; |
432 | MRE.r_word0 = ((FixupOffset << 0) | |
433 | (Type << 24) | |
434 | (Log2Size << 28) | |
435 | (IsPCRel << 30) | |
436 | MachO::R_SCATTERED); |
437 | MRE.r_word1 = Value; |
438 | Writer->addRelocation(RelSymbol: nullptr, Sec: Fragment->getParent(), MRE); |
439 | return true; |
440 | } |
441 | |
442 | void X86MachObjectWriter::recordTLVPRelocation(MachObjectWriter *Writer, |
443 | const MCAssembler &Asm, |
444 | const MCFragment *Fragment, |
445 | const MCFixup &Fixup, |
446 | MCValue Target, |
447 | uint64_t &FixedValue) { |
448 | const MCSymbol *SymA = Target.getAddSym(); |
449 | assert(Target.getSpecifier() == X86::S_TLVP && !is64Bit() && |
450 | "Should only be called with a 32-bit TLVP relocation!" ); |
451 | |
452 | unsigned Log2Size = getFixupKindLog2Size(Kind: Fixup.getKind()); |
453 | uint32_t Value = Asm.getFragmentOffset(F: *Fragment) + Fixup.getOffset(); |
454 | unsigned IsPCRel = 0; |
455 | |
456 | // We're only going to have a second symbol in pic mode and it'll be a |
457 | // subtraction from the picbase. For 32-bit pic the addend is the difference |
458 | // between the picbase and the next address. For 32-bit static the addend is |
459 | // zero. |
460 | if (auto *SymB = Target.getSubSym()) { |
461 | // If this is a subtraction then we're pcrel. |
462 | uint32_t FixupAddress = |
463 | Writer->getFragmentAddress(Asm, Fragment) + Fixup.getOffset(); |
464 | IsPCRel = 1; |
465 | FixedValue = |
466 | FixupAddress - Writer->getSymbolAddress(S: *SymB) + Target.getConstant(); |
467 | FixedValue += 1ULL << Log2Size; |
468 | } else { |
469 | FixedValue = 0; |
470 | } |
471 | |
472 | // struct relocation_info (8 bytes) |
473 | MachO::any_relocation_info MRE; |
474 | MRE.r_word0 = Value; |
475 | MRE.r_word1 = |
476 | (IsPCRel << 24) | (Log2Size << 25) | (MachO::GENERIC_RELOC_TLV << 28); |
477 | Writer->addRelocation(RelSymbol: SymA, Sec: Fragment->getParent(), MRE); |
478 | } |
479 | |
480 | void X86MachObjectWriter::RecordX86Relocation(MachObjectWriter *Writer, |
481 | const MCAssembler &Asm, |
482 | const MCFragment *Fragment, |
483 | const MCFixup &Fixup, |
484 | MCValue Target, |
485 | uint64_t &FixedValue) { |
486 | unsigned IsPCRel = Fixup.isPCRel(); |
487 | unsigned Log2Size = getFixupKindLog2Size(Kind: Fixup.getKind()); |
488 | const MCSymbol *A = Target.getAddSym(); |
489 | |
490 | // If this is a 32-bit TLVP reloc it's handled a bit differently. |
491 | if (A && Target.getSpecifier() == X86::S_TLVP) { |
492 | recordTLVPRelocation(Writer, Asm, Fragment, Fixup, Target, FixedValue); |
493 | return; |
494 | } |
495 | |
496 | // If this is a difference or a defined symbol plus an offset, then we need a |
497 | // scattered relocation entry. Differences always require scattered |
498 | // relocations. |
499 | if (Target.getSubSym()) { |
500 | recordScatteredRelocation(Writer, Asm, Fragment, Fixup, Target, Log2Size, |
501 | FixedValue); |
502 | return; |
503 | } |
504 | |
505 | // If this is an internal relocation with an offset, it also needs a scattered |
506 | // relocation entry. |
507 | uint32_t Offset = Target.getConstant(); |
508 | if (IsPCRel) |
509 | Offset += 1 << Log2Size; |
510 | |
511 | // Try to record the scattered relocation if needed. Fall back to non |
512 | // scattered if necessary (see comments in recordScatteredRelocation() |
513 | // for details). |
514 | if (Offset && A && !Writer->doesSymbolRequireExternRelocation(S: *A) && |
515 | recordScatteredRelocation(Writer, Asm, Fragment, Fixup, Target, Log2Size, |
516 | FixedValue)) |
517 | return; |
518 | |
519 | // See <reloc.h>. |
520 | uint32_t FixupOffset = Asm.getFragmentOffset(F: *Fragment) + Fixup.getOffset(); |
521 | unsigned Index = 0; |
522 | unsigned Type = 0; |
523 | const MCSymbol *RelSymbol = nullptr; |
524 | |
525 | if (Target.isAbsolute()) { // constant |
526 | // SymbolNum of 0 indicates the absolute section. |
527 | // |
528 | // FIXME: Currently, these are never generated (see code below). I cannot |
529 | // find a case where they are actually emitted. |
530 | Type = MachO::GENERIC_RELOC_VANILLA; |
531 | } else { |
532 | assert(A && "Unknown symbol data" ); |
533 | |
534 | // Resolve constant variables. |
535 | if (A->isVariable()) { |
536 | MCValue Val; |
537 | bool Relocatable = |
538 | A->getVariableValue()->evaluateAsRelocatable(Res&: Val, Asm: &Asm); |
539 | int64_t Res = Val.getConstant(); |
540 | bool isAbs = Val.isAbsolute(); |
541 | if (Relocatable && Val.getAddSym() && Val.getSubSym()) { |
542 | Res += Writer->getSymbolAddress(S: *Val.getAddSym()) - |
543 | Writer->getSymbolAddress(S: *Val.getSubSym()); |
544 | isAbs = true; |
545 | } |
546 | if (isAbs) { |
547 | FixedValue = Res; |
548 | return; |
549 | } |
550 | } |
551 | |
552 | // Check whether we need an external or internal relocation. |
553 | if (Writer->doesSymbolRequireExternRelocation(S: *A)) { |
554 | RelSymbol = A; |
555 | // For external relocations, make sure to offset the fixup value to |
556 | // compensate for the addend of the symbol address, if it was |
557 | // undefined. This occurs with weak definitions, for example. |
558 | if (!A->isUndefined()) |
559 | FixedValue -= Asm.getSymbolOffset(S: *A); |
560 | } else { |
561 | // The index is the section ordinal (1-based). |
562 | const MCSection &Sec = A->getSection(); |
563 | Index = Sec.getOrdinal() + 1; |
564 | FixedValue += Writer->getSectionAddress(Sec: &Sec); |
565 | } |
566 | if (IsPCRel) |
567 | FixedValue -= Writer->getSectionAddress(Sec: Fragment->getParent()); |
568 | |
569 | Type = MachO::GENERIC_RELOC_VANILLA; |
570 | } |
571 | |
572 | // struct relocation_info (8 bytes) |
573 | MachO::any_relocation_info MRE; |
574 | MRE.r_word0 = FixupOffset; |
575 | MRE.r_word1 = |
576 | (Index << 0) | (IsPCRel << 24) | (Log2Size << 25) | (Type << 28); |
577 | Writer->addRelocation(RelSymbol, Sec: Fragment->getParent(), MRE); |
578 | } |
579 | |
580 | std::unique_ptr<MCObjectTargetWriter> |
581 | llvm::createX86MachObjectWriter(bool Is64Bit, uint32_t CPUType, |
582 | uint32_t CPUSubtype) { |
583 | return std::make_unique<X86MachObjectWriter>(args&: Is64Bit, args&: CPUType, args&: CPUSubtype); |
584 | } |
585 | |