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