1259698Sdim//===-- PPCMachObjectWriter.cpp - PPC Mach-O Writer -----------------------===//
2259698Sdim//
3259698Sdim//                     The LLVM Compiler Infrastructure
4259698Sdim//
5259698Sdim// This file is distributed under the University of Illinois Open Source
6259698Sdim// License. See LICENSE.TXT for details.
7259698Sdim//
8259698Sdim//===----------------------------------------------------------------------===//
9259698Sdim
10259698Sdim#include "MCTargetDesc/PPCMCTargetDesc.h"
11259698Sdim#include "MCTargetDesc/PPCFixupKinds.h"
12259698Sdim#include "llvm/ADT/Twine.h"
13259698Sdim#include "llvm/MC/MCAsmLayout.h"
14259698Sdim#include "llvm/MC/MCAssembler.h"
15259698Sdim#include "llvm/MC/MCContext.h"
16259698Sdim#include "llvm/MC/MCMachObjectWriter.h"
17259698Sdim#include "llvm/MC/MCSectionMachO.h"
18259698Sdim#include "llvm/MC/MCValue.h"
19259698Sdim#include "llvm/Support/ErrorHandling.h"
20259698Sdim#include "llvm/Support/Format.h"
21259698Sdim#include "llvm/Support/MachO.h"
22259698Sdim
23259698Sdimusing namespace llvm;
24259698Sdim
25259698Sdimnamespace {
26259698Sdimclass PPCMachObjectWriter : public MCMachObjectTargetWriter {
27288943Sdim  bool recordScatteredRelocation(MachObjectWriter *Writer,
28259698Sdim                                 const MCAssembler &Asm,
29259698Sdim                                 const MCAsmLayout &Layout,
30259698Sdim                                 const MCFragment *Fragment,
31259698Sdim                                 const MCFixup &Fixup, MCValue Target,
32259698Sdim                                 unsigned Log2Size, uint64_t &FixedValue);
33259698Sdim
34259698Sdim  void RecordPPCRelocation(MachObjectWriter *Writer, const MCAssembler &Asm,
35259698Sdim                           const MCAsmLayout &Layout,
36259698Sdim                           const MCFragment *Fragment, const MCFixup &Fixup,
37259698Sdim                           MCValue Target, uint64_t &FixedValue);
38259698Sdim
39259698Sdimpublic:
40259698Sdim  PPCMachObjectWriter(bool Is64Bit, uint32_t CPUType, uint32_t CPUSubtype)
41288943Sdim      : MCMachObjectTargetWriter(Is64Bit, CPUType, CPUSubtype) {}
42259698Sdim
43288943Sdim  void recordRelocation(MachObjectWriter *Writer, MCAssembler &Asm,
44259698Sdim                        const MCAsmLayout &Layout, const MCFragment *Fragment,
45259698Sdim                        const MCFixup &Fixup, MCValue Target,
46276479Sdim                        uint64_t &FixedValue) override {
47259698Sdim    if (Writer->is64Bit()) {
48259698Sdim      report_fatal_error("Relocation emission for MachO/PPC64 unimplemented.");
49259698Sdim    } else
50259698Sdim      RecordPPCRelocation(Writer, Asm, Layout, Fragment, Fixup, Target,
51259698Sdim                          FixedValue);
52259698Sdim  }
53259698Sdim};
54259698Sdim}
55259698Sdim
56259698Sdim/// computes the log2 of the size of the relocation,
57259698Sdim/// used for relocation_info::r_length.
58259698Sdimstatic unsigned getFixupKindLog2Size(unsigned Kind) {
59259698Sdim  switch (Kind) {
60259698Sdim  default:
61259698Sdim    report_fatal_error("log2size(FixupKind): Unhandled fixup kind!");
62259698Sdim  case FK_PCRel_1:
63259698Sdim  case FK_Data_1:
64259698Sdim    return 0;
65259698Sdim  case FK_PCRel_2:
66259698Sdim  case FK_Data_2:
67259698Sdim    return 1;
68259698Sdim  case FK_PCRel_4:
69259698Sdim  case PPC::fixup_ppc_brcond14:
70259698Sdim  case PPC::fixup_ppc_half16:
71259698Sdim  case PPC::fixup_ppc_br24:
72259698Sdim  case FK_Data_4:
73259698Sdim    return 2;
74259698Sdim  case FK_PCRel_8:
75259698Sdim  case FK_Data_8:
76259698Sdim    return 3;
77259698Sdim  }
78259698Sdim  return 0;
79259698Sdim}
80259698Sdim
81259698Sdim/// Translates generic PPC fixup kind to Mach-O/PPC relocation type enum.
82280031Sdim/// Outline based on PPCELFObjectWriter::GetRelocType().
83259698Sdimstatic unsigned getRelocType(const MCValue &Target,
84259698Sdim                             const MCFixupKind FixupKind, // from
85259698Sdim                                                          // Fixup.getKind()
86259698Sdim                             const bool IsPCRel) {
87259698Sdim  const MCSymbolRefExpr::VariantKind Modifier =
88259698Sdim      Target.isAbsolute() ? MCSymbolRefExpr::VK_None
89259698Sdim                          : Target.getSymA()->getKind();
90259698Sdim  // determine the type of the relocation
91259698Sdim  unsigned Type = MachO::GENERIC_RELOC_VANILLA;
92259698Sdim  if (IsPCRel) { // relative to PC
93259698Sdim    switch ((unsigned)FixupKind) {
94259698Sdim    default:
95259698Sdim      report_fatal_error("Unimplemented fixup kind (relative)");
96259698Sdim    case PPC::fixup_ppc_br24:
97259698Sdim      Type = MachO::PPC_RELOC_BR24; // R_PPC_REL24
98259698Sdim      break;
99259698Sdim    case PPC::fixup_ppc_brcond14:
100259698Sdim      Type = MachO::PPC_RELOC_BR14;
101259698Sdim      break;
102259698Sdim    case PPC::fixup_ppc_half16:
103259698Sdim      switch (Modifier) {
104259698Sdim      default:
105259698Sdim        llvm_unreachable("Unsupported modifier for half16 fixup");
106259698Sdim      case MCSymbolRefExpr::VK_PPC_HA:
107259698Sdim        Type = MachO::PPC_RELOC_HA16;
108259698Sdim        break;
109259698Sdim      case MCSymbolRefExpr::VK_PPC_LO:
110259698Sdim        Type = MachO::PPC_RELOC_LO16;
111259698Sdim        break;
112259698Sdim      case MCSymbolRefExpr::VK_PPC_HI:
113259698Sdim        Type = MachO::PPC_RELOC_HI16;
114259698Sdim        break;
115259698Sdim      }
116259698Sdim      break;
117259698Sdim    }
118259698Sdim  } else {
119259698Sdim    switch ((unsigned)FixupKind) {
120259698Sdim    default:
121259698Sdim      report_fatal_error("Unimplemented fixup kind (absolute)!");
122259698Sdim    case PPC::fixup_ppc_half16:
123259698Sdim      switch (Modifier) {
124259698Sdim      default:
125259698Sdim        llvm_unreachable("Unsupported modifier for half16 fixup");
126259698Sdim      case MCSymbolRefExpr::VK_PPC_HA:
127259698Sdim        Type = MachO::PPC_RELOC_HA16_SECTDIFF;
128259698Sdim        break;
129259698Sdim      case MCSymbolRefExpr::VK_PPC_LO:
130259698Sdim        Type = MachO::PPC_RELOC_LO16_SECTDIFF;
131259698Sdim        break;
132259698Sdim      case MCSymbolRefExpr::VK_PPC_HI:
133259698Sdim        Type = MachO::PPC_RELOC_HI16_SECTDIFF;
134259698Sdim        break;
135259698Sdim      }
136259698Sdim      break;
137259698Sdim    case FK_Data_4:
138259698Sdim      break;
139259698Sdim    case FK_Data_2:
140259698Sdim      break;
141259698Sdim    }
142259698Sdim  }
143259698Sdim  return Type;
144259698Sdim}
145259698Sdim
146259698Sdimstatic void makeRelocationInfo(MachO::any_relocation_info &MRE,
147259698Sdim                               const uint32_t FixupOffset, const uint32_t Index,
148259698Sdim                               const unsigned IsPCRel, const unsigned Log2Size,
149259698Sdim                               const unsigned IsExtern, const unsigned Type) {
150259698Sdim  MRE.r_word0 = FixupOffset;
151259698Sdim  // The bitfield offsets that work (as determined by trial-and-error)
152259698Sdim  // are different than what is documented in the mach-o manuals.
153259698Sdim  // This appears to be an endianness issue; reversing the order of the
154259698Sdim  // documented bitfields in <llvm/Support/MachO.h> fixes this (but
155259698Sdim  // breaks x86/ARM assembly).
156259698Sdim  MRE.r_word1 = ((Index << 8) |    // was << 0
157259698Sdim                 (IsPCRel << 7) |  // was << 24
158259698Sdim                 (Log2Size << 5) | // was << 25
159259698Sdim                 (IsExtern << 4) | // was << 27
160259698Sdim                 (Type << 0));     // was << 28
161259698Sdim}
162259698Sdim
163259698Sdimstatic void
164259698SdimmakeScatteredRelocationInfo(MachO::any_relocation_info &MRE,
165259698Sdim                            const uint32_t Addr, const unsigned Type,
166259698Sdim                            const unsigned Log2Size, const unsigned IsPCRel,
167259698Sdim                            const uint32_t Value2) {
168259698Sdim  // For notes on bitfield positions and endianness, see:
169259698Sdim  // https://developer.apple.com/library/mac/documentation/developertools/conceptual/MachORuntime/Reference/reference.html#//apple_ref/doc/uid/20001298-scattered_relocation_entry
170259698Sdim  MRE.r_word0 = ((Addr << 0) | (Type << 24) | (Log2Size << 28) |
171259698Sdim                 (IsPCRel << 30) | MachO::R_SCATTERED);
172259698Sdim  MRE.r_word1 = Value2;
173259698Sdim}
174259698Sdim
175259698Sdim/// Compute fixup offset (address).
176259698Sdimstatic uint32_t getFixupOffset(const MCAsmLayout &Layout,
177259698Sdim                               const MCFragment *Fragment,
178259698Sdim                               const MCFixup &Fixup) {
179259698Sdim  uint32_t FixupOffset = Layout.getFragmentOffset(Fragment) + Fixup.getOffset();
180259698Sdim  // On Mach-O, ppc_fixup_half16 relocations must refer to the
181259698Sdim  // start of the instruction, not the second halfword, as ELF does
182259698Sdim  if (unsigned(Fixup.getKind()) == PPC::fixup_ppc_half16)
183259698Sdim    FixupOffset &= ~uint32_t(3);
184259698Sdim  return FixupOffset;
185259698Sdim}
186259698Sdim
187259698Sdim/// \return false if falling back to using non-scattered relocation,
188259698Sdim/// otherwise true for normal scattered relocation.
189288943Sdim/// based on X86MachObjectWriter::recordScatteredRelocation
190288943Sdim/// and ARMMachObjectWriter::recordScatteredRelocation
191288943Sdimbool PPCMachObjectWriter::recordScatteredRelocation(
192259698Sdim    MachObjectWriter *Writer, const MCAssembler &Asm, const MCAsmLayout &Layout,
193259698Sdim    const MCFragment *Fragment, const MCFixup &Fixup, MCValue Target,
194259698Sdim    unsigned Log2Size, uint64_t &FixedValue) {
195259698Sdim  // caller already computes these, can we just pass and reuse?
196259698Sdim  const uint32_t FixupOffset = getFixupOffset(Layout, Fragment, Fixup);
197259698Sdim  const MCFixupKind FK = Fixup.getKind();
198259698Sdim  const unsigned IsPCRel = Writer->isFixupKindPCRel(Asm, FK);
199259698Sdim  const unsigned Type = getRelocType(Target, FK, IsPCRel);
200259698Sdim
201259698Sdim  // Is this a local or SECTDIFF relocation entry?
202259698Sdim  // SECTDIFF relocation entries have symbol subtractions,
203259698Sdim  // and require two entries, the first for the add-symbol value,
204259698Sdim  // the second for the subtract-symbol value.
205259698Sdim
206259698Sdim  // See <reloc.h>.
207259698Sdim  const MCSymbol *A = &Target.getSymA()->getSymbol();
208259698Sdim
209288943Sdim  if (!A->getFragment())
210259698Sdim    report_fatal_error("symbol '" + A->getName() +
211259698Sdim                       "' can not be undefined in a subtraction expression");
212259698Sdim
213288943Sdim  uint32_t Value = Writer->getSymbolAddress(*A, Layout);
214288943Sdim  uint64_t SecAddr = Writer->getSectionAddress(A->getFragment()->getParent());
215259698Sdim  FixedValue += SecAddr;
216259698Sdim  uint32_t Value2 = 0;
217259698Sdim
218259698Sdim  if (const MCSymbolRefExpr *B = Target.getSymB()) {
219288943Sdim    const MCSymbol *SB = &B->getSymbol();
220259698Sdim
221288943Sdim    if (!SB->getFragment())
222259698Sdim      report_fatal_error("symbol '" + B->getSymbol().getName() +
223259698Sdim                         "' can not be undefined in a subtraction expression");
224259698Sdim
225259698Sdim    // FIXME: is Type correct? see include/llvm/Support/MachO.h
226288943Sdim    Value2 = Writer->getSymbolAddress(B->getSymbol(), Layout);
227288943Sdim    FixedValue -= Writer->getSectionAddress(SB->getFragment()->getParent());
228259698Sdim  }
229259698Sdim  // FIXME: does FixedValue get used??
230259698Sdim
231259698Sdim  // Relocations are written out in reverse order, so the PAIR comes first.
232259698Sdim  if (Type == MachO::PPC_RELOC_SECTDIFF ||
233259698Sdim      Type == MachO::PPC_RELOC_HI16_SECTDIFF ||
234259698Sdim      Type == MachO::PPC_RELOC_LO16_SECTDIFF ||
235259698Sdim      Type == MachO::PPC_RELOC_HA16_SECTDIFF ||
236259698Sdim      Type == MachO::PPC_RELOC_LO14_SECTDIFF ||
237259698Sdim      Type == MachO::PPC_RELOC_LOCAL_SECTDIFF) {
238259698Sdim    // X86 had this piece, but ARM does not
239259698Sdim    // If the offset is too large to fit in a scattered relocation,
240259698Sdim    // we're hosed. It's an unfortunate limitation of the MachO format.
241259698Sdim    if (FixupOffset > 0xffffff) {
242259698Sdim      char Buffer[32];
243259698Sdim      format("0x%x", FixupOffset).print(Buffer, sizeof(Buffer));
244296417Sdim      Asm.getContext().reportError(Fixup.getLoc(),
245259698Sdim                                  Twine("Section too large, can't encode "
246259698Sdim                                        "r_address (") +
247259698Sdim                                      Buffer + ") into 24 bits of scattered "
248259698Sdim                                               "relocation entry.");
249296417Sdim      return false;
250259698Sdim    }
251259698Sdim
252259698Sdim    // Is this supposed to follow MCTarget/PPCAsmBackend.cpp:adjustFixupValue()?
253288943Sdim    // see PPCMCExpr::evaluateAsRelocatableImpl()
254259698Sdim    uint32_t other_half = 0;
255259698Sdim    switch (Type) {
256259698Sdim    case MachO::PPC_RELOC_LO16_SECTDIFF:
257259698Sdim      other_half = (FixedValue >> 16) & 0xffff;
258259698Sdim      // applyFixupOffset longer extracts the high part because it now assumes
259259698Sdim      // this was already done.
260259698Sdim      // It looks like this is not true for the FixedValue needed with Mach-O
261259698Sdim      // relocs.
262259698Sdim      // So we need to adjust FixedValue again here.
263259698Sdim      FixedValue &= 0xffff;
264259698Sdim      break;
265259698Sdim    case MachO::PPC_RELOC_HA16_SECTDIFF:
266259698Sdim      other_half = FixedValue & 0xffff;
267259698Sdim      FixedValue =
268259698Sdim          ((FixedValue >> 16) + ((FixedValue & 0x8000) ? 1 : 0)) & 0xffff;
269259698Sdim      break;
270259698Sdim    case MachO::PPC_RELOC_HI16_SECTDIFF:
271259698Sdim      other_half = FixedValue & 0xffff;
272259698Sdim      FixedValue = (FixedValue >> 16) & 0xffff;
273259698Sdim      break;
274259698Sdim    default:
275259698Sdim      llvm_unreachable("Invalid PPC scattered relocation type.");
276259698Sdim      break;
277259698Sdim    }
278259698Sdim
279259698Sdim    MachO::any_relocation_info MRE;
280259698Sdim    makeScatteredRelocationInfo(MRE, other_half, MachO::GENERIC_RELOC_PAIR,
281259698Sdim                                Log2Size, IsPCRel, Value2);
282288943Sdim    Writer->addRelocation(nullptr, Fragment->getParent(), MRE);
283259698Sdim  } else {
284259698Sdim    // If the offset is more than 24-bits, it won't fit in a scattered
285259698Sdim    // relocation offset field, so we fall back to using a non-scattered
286259698Sdim    // relocation. This is a bit risky, as if the offset reaches out of
287259698Sdim    // the block and the linker is doing scattered loading on this
288259698Sdim    // symbol, things can go badly.
289259698Sdim    //
290259698Sdim    // Required for 'as' compatibility.
291259698Sdim    if (FixupOffset > 0xffffff)
292259698Sdim      return false;
293259698Sdim  }
294259698Sdim  MachO::any_relocation_info MRE;
295259698Sdim  makeScatteredRelocationInfo(MRE, FixupOffset, Type, Log2Size, IsPCRel, Value);
296288943Sdim  Writer->addRelocation(nullptr, Fragment->getParent(), MRE);
297259698Sdim  return true;
298259698Sdim}
299259698Sdim
300259698Sdim// see PPCELFObjectWriter for a general outline of cases
301259698Sdimvoid PPCMachObjectWriter::RecordPPCRelocation(
302259698Sdim    MachObjectWriter *Writer, const MCAssembler &Asm, const MCAsmLayout &Layout,
303259698Sdim    const MCFragment *Fragment, const MCFixup &Fixup, MCValue Target,
304259698Sdim    uint64_t &FixedValue) {
305259698Sdim  const MCFixupKind FK = Fixup.getKind(); // unsigned
306259698Sdim  const unsigned Log2Size = getFixupKindLog2Size(FK);
307259698Sdim  const bool IsPCRel = Writer->isFixupKindPCRel(Asm, FK);
308259698Sdim  const unsigned RelocType = getRelocType(Target, FK, IsPCRel);
309259698Sdim
310259698Sdim  // If this is a difference or a defined symbol plus an offset, then we need a
311259698Sdim  // scattered relocation entry. Differences always require scattered
312259698Sdim  // relocations.
313259698Sdim  if (Target.getSymB() &&
314259698Sdim      // Q: are branch targets ever scattered?
315259698Sdim      RelocType != MachO::PPC_RELOC_BR24 &&
316259698Sdim      RelocType != MachO::PPC_RELOC_BR14) {
317288943Sdim    recordScatteredRelocation(Writer, Asm, Layout, Fragment, Fixup, Target,
318259698Sdim                              Log2Size, FixedValue);
319259698Sdim    return;
320259698Sdim  }
321259698Sdim
322259698Sdim  // this doesn't seem right for RIT_PPC_BR24
323259698Sdim  // Get the symbol data, if any.
324288943Sdim  const MCSymbol *A = nullptr;
325259698Sdim  if (Target.getSymA())
326288943Sdim    A = &Target.getSymA()->getSymbol();
327259698Sdim
328259698Sdim  // See <reloc.h>.
329259698Sdim  const uint32_t FixupOffset = getFixupOffset(Layout, Fragment, Fixup);
330259698Sdim  unsigned Index = 0;
331259698Sdim  unsigned Type = RelocType;
332259698Sdim
333288943Sdim  const MCSymbol *RelSymbol = nullptr;
334259698Sdim  if (Target.isAbsolute()) { // constant
335259698Sdim                             // SymbolNum of 0 indicates the absolute section.
336259698Sdim                             //
337259698Sdim    // FIXME: Currently, these are never generated (see code below). I cannot
338259698Sdim    // find a case where they are actually emitted.
339259698Sdim    report_fatal_error("FIXME: relocations to absolute targets "
340259698Sdim                       "not yet implemented");
341259698Sdim    // the above line stolen from ARM, not sure
342259698Sdim  } else {
343259698Sdim    // Resolve constant variables.
344288943Sdim    if (A->isVariable()) {
345259698Sdim      int64_t Res;
346288943Sdim      if (A->getVariableValue()->evaluateAsAbsolute(
347259698Sdim              Res, Layout, Writer->getSectionAddressMap())) {
348259698Sdim        FixedValue = Res;
349259698Sdim        return;
350259698Sdim      }
351259698Sdim    }
352259698Sdim
353259698Sdim    // Check whether we need an external or internal relocation.
354288943Sdim    if (Writer->doesSymbolRequireExternRelocation(*A)) {
355288943Sdim      RelSymbol = A;
356259698Sdim      // For external relocations, make sure to offset the fixup value to
357259698Sdim      // compensate for the addend of the symbol address, if it was
358259698Sdim      // undefined. This occurs with weak definitions, for example.
359288943Sdim      if (!A->isUndefined())
360288943Sdim        FixedValue -= Layout.getSymbolOffset(*A);
361259698Sdim    } else {
362259698Sdim      // The index is the section ordinal (1-based).
363288943Sdim      const MCSection &Sec = A->getSection();
364288943Sdim      Index = Sec.getOrdinal() + 1;
365288943Sdim      FixedValue += Writer->getSectionAddress(&Sec);
366259698Sdim    }
367259698Sdim    if (IsPCRel)
368259698Sdim      FixedValue -= Writer->getSectionAddress(Fragment->getParent());
369259698Sdim  }
370259698Sdim
371259698Sdim  // struct relocation_info (8 bytes)
372259698Sdim  MachO::any_relocation_info MRE;
373288943Sdim  makeRelocationInfo(MRE, FixupOffset, Index, IsPCRel, Log2Size, false, Type);
374288943Sdim  Writer->addRelocation(RelSymbol, Fragment->getParent(), MRE);
375259698Sdim}
376259698Sdim
377288943SdimMCObjectWriter *llvm::createPPCMachObjectWriter(raw_pwrite_stream &OS,
378288943Sdim                                                bool Is64Bit, uint32_t CPUType,
379259698Sdim                                                uint32_t CPUSubtype) {
380259698Sdim  return createMachObjectWriter(
381259698Sdim      new PPCMachObjectWriter(Is64Bit, CPUType, CPUSubtype), OS,
382259698Sdim      /*IsLittleEndian=*/false);
383259698Sdim}
384