blob: fc9b892fb079fcaa0855710e80f25a341ed6b74d [file] [log] [blame]
David Fangb88cdf62013-08-08 20:14:40 +00001//===-- 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"
Charles Davis1827bd82013-08-27 05:38:30 +000019#include "llvm/Object/MachOFormat.h"
David Fangb88cdf62013-08-08 20:14:40 +000020#include "llvm/Support/ErrorHandling.h"
21#include "llvm/Support/Format.h"
22
23using namespace llvm;
Charles Davis1827bd82013-08-27 05:38:30 +000024using namespace llvm::object;
David Fangb88cdf62013-08-08 20:14:40 +000025
26namespace {
27class PPCMachObjectWriter : public MCMachObjectTargetWriter {
28 bool RecordScatteredRelocation(MachObjectWriter *Writer,
29 const MCAssembler &Asm,
30 const MCAsmLayout &Layout,
31 const MCFragment *Fragment,
32 const MCFixup &Fixup, MCValue Target,
33 unsigned Log2Size, uint64_t &FixedValue);
34
35 void RecordPPCRelocation(MachObjectWriter *Writer, const MCAssembler &Asm,
36 const MCAsmLayout &Layout,
37 const MCFragment *Fragment, const MCFixup &Fixup,
38 MCValue Target, uint64_t &FixedValue);
39
40public:
41 PPCMachObjectWriter(bool Is64Bit, uint32_t CPUType, uint32_t CPUSubtype)
42 : MCMachObjectTargetWriter(Is64Bit, CPUType, CPUSubtype,
43 /*UseAggressiveSymbolFolding=*/Is64Bit) {}
44
45 void RecordRelocation(MachObjectWriter *Writer, const MCAssembler &Asm,
46 const MCAsmLayout &Layout, const MCFragment *Fragment,
47 const MCFixup &Fixup, MCValue Target,
48 uint64_t &FixedValue) {
49 if (Writer->is64Bit()) {
50 report_fatal_error("Relocation emission for MachO/PPC64 unimplemented.");
51 } else
52 RecordPPCRelocation(Writer, Asm, Layout, Fragment, Fixup, Target,
53 FixedValue);
54 }
55};
56}
57
58/// computes the log2 of the size of the relocation,
59/// used for relocation_info::r_length.
60static unsigned getFixupKindLog2Size(unsigned Kind) {
61 switch (Kind) {
62 default:
63 report_fatal_error("log2size(FixupKind): Unhandled fixup kind!");
64 case FK_PCRel_1:
65 case FK_Data_1:
66 return 0;
67 case FK_PCRel_2:
68 case FK_Data_2:
69 return 1;
70 case FK_PCRel_4:
71 case PPC::fixup_ppc_brcond14:
72 case PPC::fixup_ppc_half16:
73 case PPC::fixup_ppc_br24:
74 case FK_Data_4:
75 return 2;
76 case FK_PCRel_8:
77 case FK_Data_8:
78 return 3;
79 }
80 return 0;
81}
82
83/// Translates generic PPC fixup kind to Mach-O/PPC relocation type enum.
84/// Outline based on PPCELFObjectWriter::getRelocTypeInner().
85static unsigned getRelocType(const MCValue &Target,
86 const MCFixupKind FixupKind, // from
87 // Fixup.getKind()
88 const bool IsPCRel) {
89 const MCSymbolRefExpr::VariantKind Modifier =
90 Target.isAbsolute() ? MCSymbolRefExpr::VK_None
91 : Target.getSymA()->getKind();
92 // determine the type of the relocation
Charles Davis1827bd82013-08-27 05:38:30 +000093 unsigned Type = macho::RIT_Vanilla;
David Fangb88cdf62013-08-08 20:14:40 +000094 if (IsPCRel) { // relative to PC
95 switch ((unsigned)FixupKind) {
96 default:
97 report_fatal_error("Unimplemented fixup kind (relative)");
98 case PPC::fixup_ppc_br24:
Charles Davis1827bd82013-08-27 05:38:30 +000099 Type = macho::RIT_PPC_BR24; // R_PPC_REL24
David Fangb88cdf62013-08-08 20:14:40 +0000100 break;
101 case PPC::fixup_ppc_brcond14:
Charles Davis1827bd82013-08-27 05:38:30 +0000102 Type = macho::RIT_PPC_BR14;
David Fangb88cdf62013-08-08 20:14:40 +0000103 break;
104 case PPC::fixup_ppc_half16:
105 switch (Modifier) {
106 default:
107 llvm_unreachable("Unsupported modifier for half16 fixup");
108 case MCSymbolRefExpr::VK_PPC_HA:
Charles Davis1827bd82013-08-27 05:38:30 +0000109 Type = macho::RIT_PPC_HA16;
David Fangb88cdf62013-08-08 20:14:40 +0000110 break;
111 case MCSymbolRefExpr::VK_PPC_LO:
Charles Davis1827bd82013-08-27 05:38:30 +0000112 Type = macho::RIT_PPC_LO16;
David Fangb88cdf62013-08-08 20:14:40 +0000113 break;
114 case MCSymbolRefExpr::VK_PPC_HI:
Charles Davis1827bd82013-08-27 05:38:30 +0000115 Type = macho::RIT_PPC_HI16;
David Fangb88cdf62013-08-08 20:14:40 +0000116 break;
117 }
118 break;
119 }
120 } else {
121 switch ((unsigned)FixupKind) {
122 default:
123 report_fatal_error("Unimplemented fixup kind (absolute)!");
124 case PPC::fixup_ppc_half16:
125 switch (Modifier) {
126 default:
127 llvm_unreachable("Unsupported modifier for half16 fixup");
128 case MCSymbolRefExpr::VK_PPC_HA:
Charles Davis1827bd82013-08-27 05:38:30 +0000129 Type = macho::RIT_PPC_HA16_SECTDIFF;
David Fangb88cdf62013-08-08 20:14:40 +0000130 break;
131 case MCSymbolRefExpr::VK_PPC_LO:
Charles Davis1827bd82013-08-27 05:38:30 +0000132 Type = macho::RIT_PPC_LO16_SECTDIFF;
David Fangb88cdf62013-08-08 20:14:40 +0000133 break;
134 case MCSymbolRefExpr::VK_PPC_HI:
Charles Davis1827bd82013-08-27 05:38:30 +0000135 Type = macho::RIT_PPC_HI16_SECTDIFF;
David Fangb88cdf62013-08-08 20:14:40 +0000136 break;
137 }
138 break;
139 case FK_Data_4:
140 break;
141 case FK_Data_2:
142 break;
143 }
144 }
145 return Type;
146}
147
Charles Davis1827bd82013-08-27 05:38:30 +0000148static void makeRelocationInfo(macho::RelocationEntry &MRE,
David Fangb88cdf62013-08-08 20:14:40 +0000149 const uint32_t FixupOffset, const uint32_t Index,
150 const unsigned IsPCRel, const unsigned Log2Size,
151 const unsigned IsExtern, const unsigned Type) {
Charles Davis1827bd82013-08-27 05:38:30 +0000152 MRE.Word0 = FixupOffset;
David Fangb88cdf62013-08-08 20:14:40 +0000153 // The bitfield offsets that work (as determined by trial-and-error)
154 // are different than what is documented in the mach-o manuals.
Charles Davis1827bd82013-08-27 05:38:30 +0000155 // Is this an endianness issue w/ PPC?
156 MRE.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
David Fangb88cdf62013-08-08 20:14:40 +0000161}
162
163static void
Charles Davis1827bd82013-08-27 05:38:30 +0000164makeScatteredRelocationInfo(macho::RelocationEntry &MRE, const uint32_t Addr,
165 const unsigned Type, const unsigned Log2Size,
166 const unsigned IsPCRel, const uint32_t Value2) {
167 // For notes on bitfield positions and endianness, see:
168 // https://developer.apple.com/library/mac/documentation/developertools/conceptual/MachORuntime/Reference/reference.html#//apple_ref/doc/uid/20001298-scattered_relocation_entry
169 MRE.Word0 = ((Addr << 0) | (Type << 24) | (Log2Size << 28) | (IsPCRel << 30) |
170 macho::RF_Scattered);
171 MRE.Word1 = Value2;
David Fangb88cdf62013-08-08 20:14:40 +0000172}
173
174/// Compute fixup offset (address).
175static uint32_t getFixupOffset(const MCAsmLayout &Layout,
176 const MCFragment *Fragment,
177 const MCFixup &Fixup) {
178 uint32_t FixupOffset = Layout.getFragmentOffset(Fragment) + Fixup.getOffset();
179 // On Mach-O, ppc_fixup_half16 relocations must refer to the
180 // start of the instruction, not the second halfword, as ELF does
David Fang2f1b0b52013-08-08 21:29:30 +0000181 if (unsigned(Fixup.getKind()) == PPC::fixup_ppc_half16)
David Fangb88cdf62013-08-08 20:14:40 +0000182 FixupOffset &= ~uint32_t(3);
183 return FixupOffset;
184}
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
190bool PPCMachObjectWriter::RecordScatteredRelocation(
191 MachObjectWriter *Writer, const MCAssembler &Asm, const MCAsmLayout &Layout,
192 const MCFragment *Fragment, const MCFixup &Fixup, MCValue Target,
193 unsigned Log2Size, uint64_t &FixedValue) {
194 // caller already computes these, can we just pass and reuse?
195 const uint32_t FixupOffset = getFixupOffset(Layout, Fragment, Fixup);
196 const MCFixupKind FK = Fixup.getKind();
197 const unsigned IsPCRel = Writer->isFixupKindPCRel(Asm, FK);
198 const unsigned Type = getRelocType(Target, FK, IsPCRel);
199
200 // Is this a local or SECTDIFF relocation entry?
201 // SECTDIFF relocation entries have symbol subtractions,
202 // and require two entries, the first for the add-symbol value,
203 // the second for the subtract-symbol value.
204
205 // See <reloc.h>.
206 const MCSymbol *A = &Target.getSymA()->getSymbol();
207 MCSymbolData *A_SD = &Asm.getSymbolData(*A);
208
209 if (!A_SD->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_SD, Layout);
214 uint64_t SecAddr =
215 Writer->getSectionAddress(A_SD->getFragment()->getParent());
216 FixedValue += SecAddr;
217 uint32_t Value2 = 0;
218
219 if (const MCSymbolRefExpr *B = Target.getSymB()) {
220 MCSymbolData *B_SD = &Asm.getSymbolData(B->getSymbol());
221
222 if (!B_SD->getFragment())
223 report_fatal_error("symbol '" + B->getSymbol().getName() +
224 "' can not be undefined in a subtraction expression");
225
Charles Davis1827bd82013-08-27 05:38:30 +0000226 // FIXME: is Type correct? see include/llvm/Object/MachOFormat.h
David Fangb88cdf62013-08-08 20:14:40 +0000227 Value2 = Writer->getSymbolAddress(B_SD, Layout);
228 FixedValue -= Writer->getSectionAddress(B_SD->getFragment()->getParent());
229 }
230 // FIXME: does FixedValue get used??
231
232 // Relocations are written out in reverse order, so the PAIR comes first.
Charles Davis1827bd82013-08-27 05:38:30 +0000233 if (Type == macho::RIT_PPC_SECTDIFF || Type == macho::RIT_PPC_HI16_SECTDIFF ||
234 Type == macho::RIT_PPC_LO16_SECTDIFF ||
235 Type == macho::RIT_PPC_HA16_SECTDIFF ||
236 Type == macho::RIT_PPC_LO14_SECTDIFF ||
237 Type == macho::RIT_PPC_LOCAL_SECTDIFF) {
David Fangb88cdf62013-08-08 20:14:40 +0000238 // 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().FatalError(Fixup.getLoc(),
245 Twine("Section too large, can't encode "
246 "r_address (") +
247 Buffer + ") into 24 bits of scattered "
248 "relocation entry.");
249 llvm_unreachable("fatal error returned?!");
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) {
Charles Davis1827bd82013-08-27 05:38:30 +0000256 case macho::RIT_PPC_LO16_SECTDIFF:
David Fangb88cdf62013-08-08 20:14:40 +0000257 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;
Charles Davis1827bd82013-08-27 05:38:30 +0000265 case macho::RIT_PPC_HA16_SECTDIFF:
David Fangb88cdf62013-08-08 20:14:40 +0000266 other_half = FixedValue & 0xffff;
267 FixedValue =
268 ((FixedValue >> 16) + ((FixedValue & 0x8000) ? 1 : 0)) & 0xffff;
269 break;
Charles Davis1827bd82013-08-27 05:38:30 +0000270 case macho::RIT_PPC_HI16_SECTDIFF:
David Fangb88cdf62013-08-08 20:14:40 +0000271 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
Charles Davis1827bd82013-08-27 05:38:30 +0000279 macho::RelocationEntry MRE;
280 makeScatteredRelocationInfo(MRE, other_half, macho::RIT_Pair, Log2Size,
281 IsPCRel, Value2);
David Fangb88cdf62013-08-08 20:14:40 +0000282 Writer->addRelocation(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 }
Charles Davis1827bd82013-08-27 05:38:30 +0000294 macho::RelocationEntry MRE;
David Fangb88cdf62013-08-08 20:14:40 +0000295 makeScatteredRelocationInfo(MRE, FixupOffset, Type, Log2Size, IsPCRel, Value);
296 Writer->addRelocation(Fragment->getParent(), MRE);
297 return true;
298}
299
300// see PPCELFObjectWriter for a general outline of cases
301void 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?
Charles Davis1827bd82013-08-27 05:38:30 +0000315 RelocType != macho::RIT_PPC_BR24 && RelocType != macho::RIT_PPC_BR14) {
David Fangb88cdf62013-08-08 20:14:40 +0000316 RecordScatteredRelocation(Writer, Asm, Layout, Fragment, Fixup, Target,
317 Log2Size, FixedValue);
318 return;
319 }
320
321 // this doesn't seem right for RIT_PPC_BR24
322 // Get the symbol data, if any.
323 MCSymbolData *SD = 0;
324 if (Target.getSymA())
325 SD = &Asm.getSymbolData(Target.getSymA()->getSymbol());
326
327 // See <reloc.h>.
328 const uint32_t FixupOffset = getFixupOffset(Layout, Fragment, Fixup);
329 unsigned Index = 0;
330 unsigned IsExtern = 0;
331 unsigned Type = RelocType;
332
333 if (Target.isAbsolute()) { // constant
334 // SymbolNum of 0 indicates the absolute section.
335 //
336 // FIXME: Currently, these are never generated (see code below). I cannot
337 // find a case where they are actually emitted.
338 report_fatal_error("FIXME: relocations to absolute targets "
339 "not yet implemented");
340 // the above line stolen from ARM, not sure
341 } else {
342 // Resolve constant variables.
343 if (SD->getSymbol().isVariable()) {
344 int64_t Res;
345 if (SD->getSymbol().getVariableValue()->EvaluateAsAbsolute(
346 Res, Layout, Writer->getSectionAddressMap())) {
347 FixedValue = Res;
348 return;
349 }
350 }
351
352 // Check whether we need an external or internal relocation.
353 if (Writer->doesSymbolRequireExternRelocation(SD)) {
354 IsExtern = 1;
355 Index = SD->getIndex();
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 (!SD->Symbol->isUndefined())
360 FixedValue -= Layout.getSymbolOffset(SD);
361 } else {
362 // The index is the section ordinal (1-based).
363 const MCSectionData &SymSD =
364 Asm.getSectionData(SD->getSymbol().getSection());
365 Index = SymSD.getOrdinal() + 1;
366 FixedValue += Writer->getSectionAddress(&SymSD);
367 }
368 if (IsPCRel)
369 FixedValue -= Writer->getSectionAddress(Fragment->getParent());
370 }
371
Charles Davis1827bd82013-08-27 05:38:30 +0000372 // struct relocation_info (8 bytes)
373 macho::RelocationEntry MRE;
David Fangb88cdf62013-08-08 20:14:40 +0000374 makeRelocationInfo(MRE, FixupOffset, Index, IsPCRel, Log2Size, IsExtern,
375 Type);
376 Writer->addRelocation(Fragment->getParent(), MRE);
377}
378
379MCObjectWriter *llvm::createPPCMachObjectWriter(raw_ostream &OS, bool Is64Bit,
380 uint32_t CPUType,
381 uint32_t CPUSubtype) {
382 return createMachObjectWriter(
383 new PPCMachObjectWriter(Is64Bit, CPUType, CPUSubtype), OS,
384 /*IsLittleEndian=*/false);
385}