1 //===-- PPCMachObjectWriter.cpp - PPC Mach-O Writer -----------------------===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
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"
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);
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);
40 PPCMachObjectWriter(bool Is64Bit, uint32_t CPUType, uint32_t CPUSubtype)
41 : MCMachObjectTargetWriter(Is64Bit, CPUType, CPUSubtype,
42 /*UseAggressiveSymbolFolding=*/Is64Bit) {}
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) {
48 if (Writer->is64Bit()) {
49 report_fatal_error("Relocation emission for MachO/PPC64 unimplemented.");
51 RecordPPCRelocation(Writer, Asm, Layout, Fragment, Fixup, Target,
57 /// computes the log2 of the size of the relocation,
58 /// used for relocation_info::r_length.
59 static unsigned getFixupKindLog2Size(unsigned Kind) {
62 report_fatal_error("log2size(FixupKind): Unhandled fixup kind!");
70 case PPC::fixup_ppc_brcond14:
71 case PPC::fixup_ppc_half16:
72 case PPC::fixup_ppc_br24:
82 /// Translates generic PPC fixup kind to Mach-O/PPC relocation type enum.
83 /// Outline based on PPCELFObjectWriter::getRelocTypeInner().
84 static unsigned getRelocType(const MCValue &Target,
85 const MCFixupKind FixupKind, // from
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) {
96 report_fatal_error("Unimplemented fixup kind (relative)");
97 case PPC::fixup_ppc_br24:
98 Type = MachO::PPC_RELOC_BR24; // R_PPC_REL24
100 case PPC::fixup_ppc_brcond14:
101 Type = MachO::PPC_RELOC_BR14;
103 case PPC::fixup_ppc_half16:
106 llvm_unreachable("Unsupported modifier for half16 fixup");
107 case MCSymbolRefExpr::VK_PPC_HA:
108 Type = MachO::PPC_RELOC_HA16;
110 case MCSymbolRefExpr::VK_PPC_LO:
111 Type = MachO::PPC_RELOC_LO16;
113 case MCSymbolRefExpr::VK_PPC_HI:
114 Type = MachO::PPC_RELOC_HI16;
120 switch ((unsigned)FixupKind) {
122 report_fatal_error("Unimplemented fixup kind (absolute)!");
123 case PPC::fixup_ppc_half16:
126 llvm_unreachable("Unsupported modifier for half16 fixup");
127 case MCSymbolRefExpr::VK_PPC_HA:
128 Type = MachO::PPC_RELOC_HA16_SECTDIFF;
130 case MCSymbolRefExpr::VK_PPC_LO:
131 Type = MachO::PPC_RELOC_LO16_SECTDIFF;
133 case MCSymbolRefExpr::VK_PPC_HI:
134 Type = MachO::PPC_RELOC_HI16_SECTDIFF;
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
165 makeScatteredRelocationInfo(MachO::scattered_relocation_info &MRE,
166 const uint32_t Addr, const unsigned Type,
167 const unsigned Log2Size, const unsigned IsPCRel,
168 const uint32_t Value2) {
169 MRE.r_scattered = true;
170 MRE.r_pcrel = IsPCRel;
171 MRE.r_length = Log2Size;
173 MRE.r_address = Addr;
174 MRE.r_value = Value2;
177 /// Compute fixup offset (address).
178 static uint32_t getFixupOffset(const MCAsmLayout &Layout,
179 const MCFragment *Fragment,
180 const MCFixup &Fixup) {
181 uint32_t FixupOffset = Layout.getFragmentOffset(Fragment) + Fixup.getOffset();
182 // On Mach-O, ppc_fixup_half16 relocations must refer to the
183 // start of the instruction, not the second halfword, as ELF does
184 if (unsigned(Fixup.getKind()) == PPC::fixup_ppc_half16)
185 FixupOffset &= ~uint32_t(3);
189 /// \return false if falling back to using non-scattered relocation,
190 /// otherwise true for normal scattered relocation.
191 /// based on X86MachObjectWriter::RecordScatteredRelocation
192 /// and ARMMachObjectWriter::RecordScatteredRelocation
193 bool PPCMachObjectWriter::RecordScatteredRelocation(
194 MachObjectWriter *Writer, const MCAssembler &Asm, const MCAsmLayout &Layout,
195 const MCFragment *Fragment, const MCFixup &Fixup, MCValue Target,
196 unsigned Log2Size, uint64_t &FixedValue) {
197 // caller already computes these, can we just pass and reuse?
198 const uint32_t FixupOffset = getFixupOffset(Layout, Fragment, Fixup);
199 const MCFixupKind FK = Fixup.getKind();
200 const unsigned IsPCRel = Writer->isFixupKindPCRel(Asm, FK);
201 const unsigned Type = getRelocType(Target, FK, IsPCRel);
203 // Is this a local or SECTDIFF relocation entry?
204 // SECTDIFF relocation entries have symbol subtractions,
205 // and require two entries, the first for the add-symbol value,
206 // the second for the subtract-symbol value.
209 const MCSymbol *A = &Target.getSymA()->getSymbol();
210 MCSymbolData *A_SD = &Asm.getSymbolData(*A);
212 if (!A_SD->getFragment())
213 report_fatal_error("symbol '" + A->getName() +
214 "' can not be undefined in a subtraction expression");
216 uint32_t Value = Writer->getSymbolAddress(A_SD, Layout);
218 Writer->getSectionAddress(A_SD->getFragment()->getParent());
219 FixedValue += SecAddr;
222 if (const MCSymbolRefExpr *B = Target.getSymB()) {
223 MCSymbolData *B_SD = &Asm.getSymbolData(B->getSymbol());
225 if (!B_SD->getFragment())
226 report_fatal_error("symbol '" + B->getSymbol().getName() +
227 "' can not be undefined in a subtraction expression");
229 // FIXME: is Type correct? see include/llvm/Support/MachO.h
230 Value2 = Writer->getSymbolAddress(B_SD, Layout);
231 FixedValue -= Writer->getSectionAddress(B_SD->getFragment()->getParent());
233 // FIXME: does FixedValue get used??
235 // Relocations are written out in reverse order, so the PAIR comes first.
236 if (Type == MachO::PPC_RELOC_SECTDIFF ||
237 Type == MachO::PPC_RELOC_HI16_SECTDIFF ||
238 Type == MachO::PPC_RELOC_LO16_SECTDIFF ||
239 Type == MachO::PPC_RELOC_HA16_SECTDIFF ||
240 Type == MachO::PPC_RELOC_LO14_SECTDIFF ||
241 Type == MachO::PPC_RELOC_LOCAL_SECTDIFF) {
242 // X86 had this piece, but ARM does not
243 // If the offset is too large to fit in a scattered relocation,
244 // we're hosed. It's an unfortunate limitation of the MachO format.
245 if (FixupOffset > 0xffffff) {
247 format("0x%x", FixupOffset).print(Buffer, sizeof(Buffer));
248 Asm.getContext().FatalError(Fixup.getLoc(),
249 Twine("Section too large, can't encode "
251 Buffer + ") into 24 bits of scattered "
252 "relocation entry.");
253 llvm_unreachable("fatal error returned?!");
256 // Is this supposed to follow MCTarget/PPCAsmBackend.cpp:adjustFixupValue()?
257 // see PPCMCExpr::EvaluateAsRelocatableImpl()
258 uint32_t other_half = 0;
260 case MachO::PPC_RELOC_LO16_SECTDIFF:
261 other_half = (FixedValue >> 16) & 0xffff;
262 // applyFixupOffset longer extracts the high part because it now assumes
263 // this was already done.
264 // It looks like this is not true for the FixedValue needed with Mach-O
266 // So we need to adjust FixedValue again here.
267 FixedValue &= 0xffff;
269 case MachO::PPC_RELOC_HA16_SECTDIFF:
270 other_half = FixedValue & 0xffff;
272 ((FixedValue >> 16) + ((FixedValue & 0x8000) ? 1 : 0)) & 0xffff;
274 case MachO::PPC_RELOC_HI16_SECTDIFF:
275 other_half = FixedValue & 0xffff;
276 FixedValue = (FixedValue >> 16) & 0xffff;
279 llvm_unreachable("Invalid PPC scattered relocation type.");
283 MachO::scattered_relocation_info MRE;
284 makeScatteredRelocationInfo(MRE, other_half, MachO::GENERIC_RELOC_PAIR,
285 Log2Size, IsPCRel, Value2);
286 Writer->addRelocation(Fragment->getParent(), MRE);
288 // If the offset is more than 24-bits, it won't fit in a scattered
289 // relocation offset field, so we fall back to using a non-scattered
290 // relocation. This is a bit risky, as if the offset reaches out of
291 // the block and the linker is doing scattered loading on this
292 // symbol, things can go badly.
294 // Required for 'as' compatibility.
295 if (FixupOffset > 0xffffff)
298 MachO::scattered_relocation_info MRE;
299 makeScatteredRelocationInfo(MRE, FixupOffset, Type, Log2Size, IsPCRel, Value);
300 Writer->addRelocation(Fragment->getParent(), MRE);
304 // see PPCELFObjectWriter for a general outline of cases
305 void PPCMachObjectWriter::RecordPPCRelocation(
306 MachObjectWriter *Writer, const MCAssembler &Asm, const MCAsmLayout &Layout,
307 const MCFragment *Fragment, const MCFixup &Fixup, MCValue Target,
308 uint64_t &FixedValue) {
309 const MCFixupKind FK = Fixup.getKind(); // unsigned
310 const unsigned Log2Size = getFixupKindLog2Size(FK);
311 const bool IsPCRel = Writer->isFixupKindPCRel(Asm, FK);
312 const unsigned RelocType = getRelocType(Target, FK, IsPCRel);
314 // If this is a difference or a defined symbol plus an offset, then we need a
315 // scattered relocation entry. Differences always require scattered
317 if (Target.getSymB() &&
318 // Q: are branch targets ever scattered?
319 RelocType != MachO::PPC_RELOC_BR24 &&
320 RelocType != MachO::PPC_RELOC_BR14) {
321 RecordScatteredRelocation(Writer, Asm, Layout, Fragment, Fixup, Target,
322 Log2Size, FixedValue);
326 // this doesn't seem right for RIT_PPC_BR24
327 // Get the symbol data, if any.
328 MCSymbolData *SD = 0;
329 if (Target.getSymA())
330 SD = &Asm.getSymbolData(Target.getSymA()->getSymbol());
333 const uint32_t FixupOffset = getFixupOffset(Layout, Fragment, Fixup);
335 unsigned IsExtern = 0;
336 unsigned Type = RelocType;
338 if (Target.isAbsolute()) { // constant
339 // SymbolNum of 0 indicates the absolute section.
341 // FIXME: Currently, these are never generated (see code below). I cannot
342 // find a case where they are actually emitted.
343 report_fatal_error("FIXME: relocations to absolute targets "
344 "not yet implemented");
345 // the above line stolen from ARM, not sure
347 // Resolve constant variables.
348 if (SD->getSymbol().isVariable()) {
350 if (SD->getSymbol().getVariableValue()->EvaluateAsAbsolute(
351 Res, Layout, Writer->getSectionAddressMap())) {
357 // Check whether we need an external or internal relocation.
358 if (Writer->doesSymbolRequireExternRelocation(SD)) {
360 Index = SD->getIndex();
361 // For external relocations, make sure to offset the fixup value to
362 // compensate for the addend of the symbol address, if it was
363 // undefined. This occurs with weak definitions, for example.
364 if (!SD->Symbol->isUndefined())
365 FixedValue -= Layout.getSymbolOffset(SD);
367 // The index is the section ordinal (1-based).
368 const MCSectionData &SymSD =
369 Asm.getSectionData(SD->getSymbol().getSection());
370 Index = SymSD.getOrdinal() + 1;
371 FixedValue += Writer->getSectionAddress(&SymSD);
374 FixedValue -= Writer->getSectionAddress(Fragment->getParent());
377 MachO::any_relocation_info MRE;
378 makeRelocationInfo(MRE, FixupOffset, Index, IsPCRel, Log2Size, IsExtern,
380 Writer->addRelocation(Fragment->getParent(), MRE);
383 MCObjectWriter *llvm::createPPCMachObjectWriter(raw_ostream &OS, bool Is64Bit,
385 uint32_t CPUSubtype) {
386 return createMachObjectWriter(
387 new PPCMachObjectWriter(Is64Bit, CPUType, CPUSubtype), OS,
388 /*IsLittleEndian=*/false);