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