]>
Commit | Line | Data |
---|---|---|
1a4d82fc JJ |
1 | //===-- PPCMachObjectWriter.cpp - PPC Mach-O 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/PPCMCTargetDesc.h" | |
11 | #include "MCTargetDesc/PPCFixupKinds.h" | |
12 | #include "llvm/ADT/Twine.h" | |
13 | #include "llvm/MC/MCAsmLayout.h" | |
14 | #include "llvm/MC/MCAssembler.h" | |
15 | #include "llvm/MC/MCContext.h" | |
16 | #include "llvm/MC/MCMachObjectWriter.h" | |
17 | #include "llvm/MC/MCSectionMachO.h" | |
18 | #include "llvm/MC/MCValue.h" | |
19 | #include "llvm/Support/ErrorHandling.h" | |
20 | #include "llvm/Support/Format.h" | |
21 | #include "llvm/Support/MachO.h" | |
22 | ||
23 | using namespace llvm; | |
24 | ||
25 | namespace { | |
26 | class PPCMachObjectWriter : public MCMachObjectTargetWriter { | |
27 | bool RecordScatteredRelocation(MachObjectWriter *Writer, | |
28 | const MCAssembler &Asm, | |
29 | const MCAsmLayout &Layout, | |
30 | const MCFragment *Fragment, | |
31 | const MCFixup &Fixup, MCValue Target, | |
32 | unsigned Log2Size, uint64_t &FixedValue); | |
33 | ||
34 | void RecordPPCRelocation(MachObjectWriter *Writer, const MCAssembler &Asm, | |
35 | const MCAsmLayout &Layout, | |
36 | const MCFragment *Fragment, const MCFixup &Fixup, | |
37 | MCValue Target, uint64_t &FixedValue); | |
38 | ||
39 | public: | |
40 | PPCMachObjectWriter(bool Is64Bit, uint32_t CPUType, uint32_t CPUSubtype) | |
41 | : MCMachObjectTargetWriter(Is64Bit, CPUType, CPUSubtype, | |
42 | /*UseAggressiveSymbolFolding=*/Is64Bit) {} | |
43 | ||
44 | void RecordRelocation(MachObjectWriter *Writer, const MCAssembler &Asm, | |
45 | const MCAsmLayout &Layout, const MCFragment *Fragment, | |
46 | const MCFixup &Fixup, MCValue Target, | |
47 | uint64_t &FixedValue) override { | |
48 | if (Writer->is64Bit()) { | |
49 | report_fatal_error("Relocation emission for MachO/PPC64 unimplemented."); | |
50 | } else | |
51 | RecordPPCRelocation(Writer, Asm, Layout, Fragment, Fixup, Target, | |
52 | FixedValue); | |
53 | } | |
54 | }; | |
55 | } | |
56 | ||
57 | /// computes the log2 of the size of the relocation, | |
58 | /// used for relocation_info::r_length. | |
59 | static unsigned getFixupKindLog2Size(unsigned Kind) { | |
60 | switch (Kind) { | |
61 | default: | |
62 | report_fatal_error("log2size(FixupKind): Unhandled fixup kind!"); | |
63 | case FK_PCRel_1: | |
64 | case FK_Data_1: | |
65 | return 0; | |
66 | case FK_PCRel_2: | |
67 | case FK_Data_2: | |
68 | return 1; | |
69 | case FK_PCRel_4: | |
70 | case PPC::fixup_ppc_brcond14: | |
71 | case PPC::fixup_ppc_half16: | |
72 | case PPC::fixup_ppc_br24: | |
73 | case FK_Data_4: | |
74 | return 2; | |
75 | case FK_PCRel_8: | |
76 | case FK_Data_8: | |
77 | return 3; | |
78 | } | |
79 | return 0; | |
80 | } | |
81 | ||
82 | /// Translates generic PPC fixup kind to Mach-O/PPC relocation type enum. | |
83 | /// Outline based on PPCELFObjectWriter::GetRelocType(). | |
84 | static unsigned getRelocType(const MCValue &Target, | |
85 | const MCFixupKind FixupKind, // from | |
86 | // Fixup.getKind() | |
87 | const bool IsPCRel) { | |
88 | const MCSymbolRefExpr::VariantKind Modifier = | |
89 | Target.isAbsolute() ? MCSymbolRefExpr::VK_None | |
90 | : Target.getSymA()->getKind(); | |
91 | // determine the type of the relocation | |
92 | unsigned Type = MachO::GENERIC_RELOC_VANILLA; | |
93 | if (IsPCRel) { // relative to PC | |
94 | switch ((unsigned)FixupKind) { | |
95 | default: | |
96 | report_fatal_error("Unimplemented fixup kind (relative)"); | |
97 | case PPC::fixup_ppc_br24: | |
98 | Type = MachO::PPC_RELOC_BR24; // R_PPC_REL24 | |
99 | break; | |
100 | case PPC::fixup_ppc_brcond14: | |
101 | Type = MachO::PPC_RELOC_BR14; | |
102 | break; | |
103 | case PPC::fixup_ppc_half16: | |
104 | switch (Modifier) { | |
105 | default: | |
106 | llvm_unreachable("Unsupported modifier for half16 fixup"); | |
107 | case MCSymbolRefExpr::VK_PPC_HA: | |
108 | Type = MachO::PPC_RELOC_HA16; | |
109 | break; | |
110 | case MCSymbolRefExpr::VK_PPC_LO: | |
111 | Type = MachO::PPC_RELOC_LO16; | |
112 | break; | |
113 | case MCSymbolRefExpr::VK_PPC_HI: | |
114 | Type = MachO::PPC_RELOC_HI16; | |
115 | break; | |
116 | } | |
117 | break; | |
118 | } | |
119 | } else { | |
120 | switch ((unsigned)FixupKind) { | |
121 | default: | |
122 | report_fatal_error("Unimplemented fixup kind (absolute)!"); | |
123 | case PPC::fixup_ppc_half16: | |
124 | switch (Modifier) { | |
125 | default: | |
126 | llvm_unreachable("Unsupported modifier for half16 fixup"); | |
127 | case MCSymbolRefExpr::VK_PPC_HA: | |
128 | Type = MachO::PPC_RELOC_HA16_SECTDIFF; | |
129 | break; | |
130 | case MCSymbolRefExpr::VK_PPC_LO: | |
131 | Type = MachO::PPC_RELOC_LO16_SECTDIFF; | |
132 | break; | |
133 | case MCSymbolRefExpr::VK_PPC_HI: | |
134 | Type = MachO::PPC_RELOC_HI16_SECTDIFF; | |
135 | break; | |
136 | } | |
137 | break; | |
138 | case FK_Data_4: | |
139 | break; | |
140 | case FK_Data_2: | |
141 | break; | |
142 | } | |
143 | } | |
144 | return Type; | |
145 | } | |
146 | ||
147 | static void makeRelocationInfo(MachO::any_relocation_info &MRE, | |
148 | const uint32_t FixupOffset, const uint32_t Index, | |
149 | const unsigned IsPCRel, const unsigned Log2Size, | |
150 | const unsigned IsExtern, const unsigned Type) { | |
151 | MRE.r_word0 = FixupOffset; | |
152 | // The bitfield offsets that work (as determined by trial-and-error) | |
153 | // are different than what is documented in the mach-o manuals. | |
154 | // This appears to be an endianness issue; reversing the order of the | |
155 | // documented bitfields in <llvm/Support/MachO.h> fixes this (but | |
156 | // breaks x86/ARM assembly). | |
157 | MRE.r_word1 = ((Index << 8) | // was << 0 | |
158 | (IsPCRel << 7) | // was << 24 | |
159 | (Log2Size << 5) | // was << 25 | |
160 | (IsExtern << 4) | // was << 27 | |
161 | (Type << 0)); // was << 28 | |
162 | } | |
163 | ||
164 | static void | |
165 | makeScatteredRelocationInfo(MachO::any_relocation_info &MRE, | |
166 | const uint32_t Addr, const unsigned Type, | |
167 | const unsigned Log2Size, const unsigned IsPCRel, | |
168 | const uint32_t Value2) { | |
169 | // For notes on bitfield positions and endianness, see: | |
170 | // https://developer.apple.com/library/mac/documentation/developertools/conceptual/MachORuntime/Reference/reference.html#//apple_ref/doc/uid/20001298-scattered_relocation_entry | |
171 | MRE.r_word0 = ((Addr << 0) | (Type << 24) | (Log2Size << 28) | | |
172 | (IsPCRel << 30) | MachO::R_SCATTERED); | |
173 | MRE.r_word1 = Value2; | |
174 | } | |
175 | ||
176 | /// Compute fixup offset (address). | |
177 | static uint32_t getFixupOffset(const MCAsmLayout &Layout, | |
178 | const MCFragment *Fragment, | |
179 | const MCFixup &Fixup) { | |
180 | uint32_t FixupOffset = Layout.getFragmentOffset(Fragment) + Fixup.getOffset(); | |
181 | // On Mach-O, ppc_fixup_half16 relocations must refer to the | |
182 | // start of the instruction, not the second halfword, as ELF does | |
183 | if (unsigned(Fixup.getKind()) == PPC::fixup_ppc_half16) | |
184 | FixupOffset &= ~uint32_t(3); | |
185 | return FixupOffset; | |
186 | } | |
187 | ||
188 | /// \return false if falling back to using non-scattered relocation, | |
189 | /// otherwise true for normal scattered relocation. | |
190 | /// based on X86MachObjectWriter::RecordScatteredRelocation | |
191 | /// and ARMMachObjectWriter::RecordScatteredRelocation | |
192 | bool PPCMachObjectWriter::RecordScatteredRelocation( | |
193 | MachObjectWriter *Writer, const MCAssembler &Asm, const MCAsmLayout &Layout, | |
194 | const MCFragment *Fragment, const MCFixup &Fixup, MCValue Target, | |
195 | unsigned Log2Size, uint64_t &FixedValue) { | |
196 | // caller already computes these, can we just pass and reuse? | |
197 | const uint32_t FixupOffset = getFixupOffset(Layout, Fragment, Fixup); | |
198 | const MCFixupKind FK = Fixup.getKind(); | |
199 | const unsigned IsPCRel = Writer->isFixupKindPCRel(Asm, FK); | |
200 | const unsigned Type = getRelocType(Target, FK, IsPCRel); | |
201 | ||
202 | // Is this a local or SECTDIFF relocation entry? | |
203 | // SECTDIFF relocation entries have symbol subtractions, | |
204 | // and require two entries, the first for the add-symbol value, | |
205 | // the second for the subtract-symbol value. | |
206 | ||
207 | // See <reloc.h>. | |
208 | const MCSymbol *A = &Target.getSymA()->getSymbol(); | |
209 | const MCSymbolData *A_SD = &Asm.getSymbolData(*A); | |
210 | ||
211 | if (!A_SD->getFragment()) | |
212 | report_fatal_error("symbol '" + A->getName() + | |
213 | "' can not be undefined in a subtraction expression"); | |
214 | ||
215 | uint32_t Value = Writer->getSymbolAddress(A_SD, Layout); | |
216 | uint64_t SecAddr = | |
217 | Writer->getSectionAddress(A_SD->getFragment()->getParent()); | |
218 | FixedValue += SecAddr; | |
219 | uint32_t Value2 = 0; | |
220 | ||
221 | if (const MCSymbolRefExpr *B = Target.getSymB()) { | |
222 | const MCSymbolData *B_SD = &Asm.getSymbolData(B->getSymbol()); | |
223 | ||
224 | if (!B_SD->getFragment()) | |
225 | report_fatal_error("symbol '" + B->getSymbol().getName() + | |
226 | "' can not be undefined in a subtraction expression"); | |
227 | ||
228 | // FIXME: is Type correct? see include/llvm/Support/MachO.h | |
229 | Value2 = Writer->getSymbolAddress(B_SD, Layout); | |
230 | FixedValue -= Writer->getSectionAddress(B_SD->getFragment()->getParent()); | |
231 | } | |
232 | // FIXME: does FixedValue get used?? | |
233 | ||
234 | // Relocations are written out in reverse order, so the PAIR comes first. | |
235 | if (Type == MachO::PPC_RELOC_SECTDIFF || | |
236 | Type == MachO::PPC_RELOC_HI16_SECTDIFF || | |
237 | Type == MachO::PPC_RELOC_LO16_SECTDIFF || | |
238 | Type == MachO::PPC_RELOC_HA16_SECTDIFF || | |
239 | Type == MachO::PPC_RELOC_LO14_SECTDIFF || | |
240 | Type == MachO::PPC_RELOC_LOCAL_SECTDIFF) { | |
241 | // X86 had this piece, but ARM does not | |
242 | // If the offset is too large to fit in a scattered relocation, | |
243 | // we're hosed. It's an unfortunate limitation of the MachO format. | |
244 | if (FixupOffset > 0xffffff) { | |
245 | char Buffer[32]; | |
246 | format("0x%x", FixupOffset).print(Buffer, sizeof(Buffer)); | |
247 | Asm.getContext().FatalError(Fixup.getLoc(), | |
248 | Twine("Section too large, can't encode " | |
249 | "r_address (") + | |
250 | Buffer + ") into 24 bits of scattered " | |
251 | "relocation entry."); | |
252 | llvm_unreachable("fatal error returned?!"); | |
253 | } | |
254 | ||
255 | // Is this supposed to follow MCTarget/PPCAsmBackend.cpp:adjustFixupValue()? | |
256 | // see PPCMCExpr::EvaluateAsRelocatableImpl() | |
257 | uint32_t other_half = 0; | |
258 | switch (Type) { | |
259 | case MachO::PPC_RELOC_LO16_SECTDIFF: | |
260 | other_half = (FixedValue >> 16) & 0xffff; | |
261 | // applyFixupOffset longer extracts the high part because it now assumes | |
262 | // this was already done. | |
263 | // It looks like this is not true for the FixedValue needed with Mach-O | |
264 | // relocs. | |
265 | // So we need to adjust FixedValue again here. | |
266 | FixedValue &= 0xffff; | |
267 | break; | |
268 | case MachO::PPC_RELOC_HA16_SECTDIFF: | |
269 | other_half = FixedValue & 0xffff; | |
270 | FixedValue = | |
271 | ((FixedValue >> 16) + ((FixedValue & 0x8000) ? 1 : 0)) & 0xffff; | |
272 | break; | |
273 | case MachO::PPC_RELOC_HI16_SECTDIFF: | |
274 | other_half = FixedValue & 0xffff; | |
275 | FixedValue = (FixedValue >> 16) & 0xffff; | |
276 | break; | |
277 | default: | |
278 | llvm_unreachable("Invalid PPC scattered relocation type."); | |
279 | break; | |
280 | } | |
281 | ||
282 | MachO::any_relocation_info MRE; | |
283 | makeScatteredRelocationInfo(MRE, other_half, MachO::GENERIC_RELOC_PAIR, | |
284 | Log2Size, IsPCRel, Value2); | |
285 | Writer->addRelocation(Fragment->getParent(), MRE); | |
286 | } else { | |
287 | // If the offset is more than 24-bits, it won't fit in a scattered | |
288 | // relocation offset field, so we fall back to using a non-scattered | |
289 | // relocation. This is a bit risky, as if the offset reaches out of | |
290 | // the block and the linker is doing scattered loading on this | |
291 | // symbol, things can go badly. | |
292 | // | |
293 | // Required for 'as' compatibility. | |
294 | if (FixupOffset > 0xffffff) | |
295 | return false; | |
296 | } | |
297 | MachO::any_relocation_info MRE; | |
298 | makeScatteredRelocationInfo(MRE, FixupOffset, Type, Log2Size, IsPCRel, Value); | |
299 | Writer->addRelocation(Fragment->getParent(), MRE); | |
300 | return true; | |
301 | } | |
302 | ||
303 | // see PPCELFObjectWriter for a general outline of cases | |
304 | void PPCMachObjectWriter::RecordPPCRelocation( | |
305 | MachObjectWriter *Writer, const MCAssembler &Asm, const MCAsmLayout &Layout, | |
306 | const MCFragment *Fragment, const MCFixup &Fixup, MCValue Target, | |
307 | uint64_t &FixedValue) { | |
308 | const MCFixupKind FK = Fixup.getKind(); // unsigned | |
309 | const unsigned Log2Size = getFixupKindLog2Size(FK); | |
310 | const bool IsPCRel = Writer->isFixupKindPCRel(Asm, FK); | |
311 | const unsigned RelocType = getRelocType(Target, FK, IsPCRel); | |
312 | ||
313 | // If this is a difference or a defined symbol plus an offset, then we need a | |
314 | // scattered relocation entry. Differences always require scattered | |
315 | // relocations. | |
316 | if (Target.getSymB() && | |
317 | // Q: are branch targets ever scattered? | |
318 | RelocType != MachO::PPC_RELOC_BR24 && | |
319 | RelocType != MachO::PPC_RELOC_BR14) { | |
320 | RecordScatteredRelocation(Writer, Asm, Layout, Fragment, Fixup, Target, | |
321 | Log2Size, FixedValue); | |
322 | return; | |
323 | } | |
324 | ||
325 | // this doesn't seem right for RIT_PPC_BR24 | |
326 | // Get the symbol data, if any. | |
327 | const MCSymbolData *SD = nullptr; | |
328 | if (Target.getSymA()) | |
329 | SD = &Asm.getSymbolData(Target.getSymA()->getSymbol()); | |
330 | ||
331 | // See <reloc.h>. | |
332 | const uint32_t FixupOffset = getFixupOffset(Layout, Fragment, Fixup); | |
333 | unsigned Index = 0; | |
334 | unsigned IsExtern = 0; | |
335 | unsigned Type = RelocType; | |
336 | ||
337 | if (Target.isAbsolute()) { // constant | |
338 | // SymbolNum of 0 indicates the absolute section. | |
339 | // | |
340 | // FIXME: Currently, these are never generated (see code below). I cannot | |
341 | // find a case where they are actually emitted. | |
342 | report_fatal_error("FIXME: relocations to absolute targets " | |
343 | "not yet implemented"); | |
344 | // the above line stolen from ARM, not sure | |
345 | } else { | |
346 | // Resolve constant variables. | |
347 | if (SD->getSymbol().isVariable()) { | |
348 | int64_t Res; | |
349 | if (SD->getSymbol().getVariableValue()->EvaluateAsAbsolute( | |
350 | Res, Layout, Writer->getSectionAddressMap())) { | |
351 | FixedValue = Res; | |
352 | return; | |
353 | } | |
354 | } | |
355 | ||
356 | // Check whether we need an external or internal relocation. | |
357 | if (Writer->doesSymbolRequireExternRelocation(SD)) { | |
358 | IsExtern = 1; | |
359 | Index = SD->getIndex(); | |
360 | // For external relocations, make sure to offset the fixup value to | |
361 | // compensate for the addend of the symbol address, if it was | |
362 | // undefined. This occurs with weak definitions, for example. | |
85aaf69f | 363 | if (!SD->getSymbol().isUndefined()) |
1a4d82fc JJ |
364 | FixedValue -= Layout.getSymbolOffset(SD); |
365 | } else { | |
366 | // The index is the section ordinal (1-based). | |
367 | const MCSectionData &SymSD = | |
368 | Asm.getSectionData(SD->getSymbol().getSection()); | |
369 | Index = SymSD.getOrdinal() + 1; | |
370 | FixedValue += Writer->getSectionAddress(&SymSD); | |
371 | } | |
372 | if (IsPCRel) | |
373 | FixedValue -= Writer->getSectionAddress(Fragment->getParent()); | |
374 | } | |
375 | ||
376 | // struct relocation_info (8 bytes) | |
377 | MachO::any_relocation_info MRE; | |
378 | makeRelocationInfo(MRE, FixupOffset, Index, IsPCRel, Log2Size, IsExtern, | |
379 | Type); | |
380 | Writer->addRelocation(Fragment->getParent(), MRE); | |
381 | } | |
382 | ||
383 | MCObjectWriter *llvm::createPPCMachObjectWriter(raw_ostream &OS, bool Is64Bit, | |
384 | uint32_t CPUType, | |
385 | uint32_t CPUSubtype) { | |
386 | return createMachObjectWriter( | |
387 | new PPCMachObjectWriter(Is64Bit, CPUType, CPUSubtype), OS, | |
388 | /*IsLittleEndian=*/false); | |
389 | } |