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