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