• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/PPCFixupKinds.h"
11 #include "MCTargetDesc/PPCMCTargetDesc.h"
12 #include "llvm/ADT/Twine.h"
13 #include "llvm/BinaryFormat/MachO.h"
14 #include "llvm/MC/MCAsmLayout.h"
15 #include "llvm/MC/MCAssembler.h"
16 #include "llvm/MC/MCContext.h"
17 #include "llvm/MC/MCMachObjectWriter.h"
18 #include "llvm/MC/MCSectionMachO.h"
19 #include "llvm/MC/MCValue.h"
20 #include "llvm/Support/ErrorHandling.h"
21 #include "llvm/Support/Format.h"
22 
23 using namespace llvm;
24 
25 namespace {
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);
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 
39 public:
PPCMachObjectWriter(bool Is64Bit,uint32_t CPUType,uint32_t CPUSubtype)40   PPCMachObjectWriter(bool Is64Bit, uint32_t CPUType, uint32_t CPUSubtype)
41       : MCMachObjectTargetWriter(Is64Bit, CPUType, CPUSubtype) {}
42 
recordRelocation(MachObjectWriter * Writer,MCAssembler & Asm,const MCAsmLayout & Layout,const MCFragment * Fragment,const MCFixup & Fixup,MCValue Target,uint64_t & FixedValue)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.
getFixupKindLog2Size(unsigned Kind)58 static 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().
getRelocType(const MCValue & Target,const MCFixupKind FixupKind,const bool IsPCRel)83 static 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 
makeRelocationInfo(MachO::any_relocation_info & MRE,const uint32_t FixupOffset,const uint32_t Index,const unsigned IsPCRel,const unsigned Log2Size,const unsigned IsExtern,const unsigned Type)146 static 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/BinaryFormat/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 
163 static void
makeScatteredRelocationInfo(MachO::any_relocation_info & MRE,const uint32_t Addr,const unsigned Type,const unsigned Log2Size,const unsigned IsPCRel,const uint32_t Value2)164 makeScatteredRelocationInfo(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).
getFixupOffset(const MCAsmLayout & Layout,const MCFragment * Fragment,const MCFixup & Fixup)176 static 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
recordScatteredRelocation(MachObjectWriter * Writer,const MCAssembler & Asm,const MCAsmLayout & Layout,const MCFragment * Fragment,const MCFixup & Fixup,MCValue Target,unsigned Log2Size,uint64_t & FixedValue)191 bool 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 '" + SB->getName() +
223                          "' can not be undefined in a subtraction expression");
224 
225     // FIXME: is Type correct? see include/llvm/BinaryFormat/MachO.h
226     Value2 = Writer->getSymbolAddress(*SB, 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
RecordPPCRelocation(MachObjectWriter * Writer,const MCAssembler & Asm,const MCAsmLayout & Layout,const MCFragment * Fragment,const MCFixup & Fixup,MCValue Target,uint64_t & FixedValue)301 void 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 
377 std::unique_ptr<MCObjectTargetWriter>
createPPCMachObjectWriter(bool Is64Bit,uint32_t CPUType,uint32_t CPUSubtype)378 llvm::createPPCMachObjectWriter(bool Is64Bit, uint32_t CPUType,
379                                 uint32_t CPUSubtype) {
380   return llvm::make_unique<PPCMachObjectWriter>(Is64Bit, CPUType, CPUSubtype);
381 }
382