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