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
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.
58static 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().
83static 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
146static 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
163static void
164makeScatteredRelocationInfo(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).
176static 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
191bool 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
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?
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
377MCObjectWriter *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