xref: /llvm-project/llvm/lib/Target/ARM/MCTargetDesc/ARMMachObjectWriter.cpp (revision 5be43db9b17e7cfc9e987f257221b0926551eb6e)
1 //===-- ARMMachObjectWriter.cpp - ARM Mach Object 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/ARMFixupKinds.h"
10 #include "MCTargetDesc/ARMMCTargetDesc.h"
11 #include "llvm/ADT/StringExtras.h"
12 #include "llvm/ADT/Twine.h"
13 #include "llvm/BinaryFormat/MachO.h"
14 #include "llvm/MC/MCAssembler.h"
15 #include "llvm/MC/MCContext.h"
16 #include "llvm/MC/MCExpr.h"
17 #include "llvm/MC/MCFixup.h"
18 #include "llvm/MC/MCMachObjectWriter.h"
19 #include "llvm/MC/MCSection.h"
20 #include "llvm/MC/MCValue.h"
21 #include "llvm/Support/ErrorHandling.h"
22 
23 using namespace llvm;
24 
25 namespace {
26 class ARMMachObjectWriter : public MCMachObjectTargetWriter {
27   void recordARMScatteredRelocation(MachObjectWriter *Writer,
28                                     const MCAssembler &Asm,
29                                     const MCFragment *Fragment,
30                                     const MCFixup &Fixup, MCValue Target,
31                                     unsigned Type, unsigned Log2Size,
32                                     uint64_t &FixedValue);
33   void recordARMScatteredHalfRelocation(MachObjectWriter *Writer,
34                                         const MCAssembler &Asm,
35                                         const MCFragment *Fragment,
36                                         const MCFixup &Fixup, MCValue Target,
37                                         uint64_t &FixedValue);
38 
39   bool requiresExternRelocation(MachObjectWriter *Writer,
40                                 const MCAssembler &Asm,
41                                 const MCFragment &Fragment, unsigned RelocType,
42                                 const MCSymbol &S, uint64_t FixedValue);
43 
44 public:
45   ARMMachObjectWriter(bool Is64Bit, uint32_t CPUType, uint32_t CPUSubtype)
46       : MCMachObjectTargetWriter(Is64Bit, CPUType, CPUSubtype) {}
47 
48   void recordRelocation(MachObjectWriter *Writer, MCAssembler &Asm,
49                         const MCFragment *Fragment, const MCFixup &Fixup,
50                         MCValue Target, uint64_t &FixedValue) override;
51 };
52 }
53 
54 static bool getARMFixupKindMachOInfo(unsigned Kind, unsigned &RelocType,
55                               unsigned &Log2Size) {
56   RelocType = unsigned(MachO::ARM_RELOC_VANILLA);
57   Log2Size = ~0U;
58 
59   switch (Kind) {
60   default:
61     return false;
62 
63   case FK_Data_1:
64     Log2Size = llvm::Log2_32(1);
65     return true;
66   case FK_Data_2:
67     Log2Size = llvm::Log2_32(2);
68     return true;
69   case FK_Data_4:
70     Log2Size = llvm::Log2_32(4);
71     return true;
72   case FK_Data_8:
73     Log2Size = llvm::Log2_32(8);
74     return false;
75 
76     // These fixups are expected to always be resolvable at assembly time and
77     // have no relocations supported.
78   case ARM::fixup_arm_ldst_pcrel_12:
79   case ARM::fixup_arm_pcrel_10:
80   case ARM::fixup_arm_adr_pcrel_12:
81   case ARM::fixup_arm_thumb_br:
82     return false;
83 
84     // Handle 24-bit branch kinds.
85   case ARM::fixup_arm_condbranch:
86   case ARM::fixup_arm_uncondbranch:
87   case ARM::fixup_arm_uncondbl:
88   case ARM::fixup_arm_condbl:
89   case ARM::fixup_arm_blx:
90     RelocType = unsigned(MachO::ARM_RELOC_BR24);
91     // Report as 'long', even though that is not quite accurate.
92     Log2Size = llvm::Log2_32(4);
93     return true;
94 
95   case ARM::fixup_t2_uncondbranch:
96   case ARM::fixup_arm_thumb_bl:
97   case ARM::fixup_arm_thumb_blx:
98     RelocType = unsigned(MachO::ARM_THUMB_RELOC_BR22);
99     Log2Size = llvm::Log2_32(4);
100     return true;
101 
102   // For movw/movt r_type relocations they always have a pair following them and
103   // the r_length bits are used differently.  The encoding of the r_length is as
104   // follows:
105   //   low bit of r_length:
106   //      0 - :lower16: for movw instructions
107   //      1 - :upper16: for movt instructions
108   //   high bit of r_length:
109   //      0 - arm instructions
110   //      1 - thumb instructions
111   case ARM::fixup_arm_movt_hi16:
112     RelocType = unsigned(MachO::ARM_RELOC_HALF);
113     Log2Size = 1;
114     return true;
115   case ARM::fixup_t2_movt_hi16:
116     RelocType = unsigned(MachO::ARM_RELOC_HALF);
117     Log2Size = 3;
118     return true;
119 
120   case ARM::fixup_arm_movw_lo16:
121     RelocType = unsigned(MachO::ARM_RELOC_HALF);
122     Log2Size = 0;
123     return true;
124   case ARM::fixup_t2_movw_lo16:
125     RelocType = unsigned(MachO::ARM_RELOC_HALF);
126     Log2Size = 2;
127     return true;
128   }
129 }
130 
131 void ARMMachObjectWriter::recordARMScatteredHalfRelocation(
132     MachObjectWriter *Writer, const MCAssembler &Asm,
133     const MCFragment *Fragment, const MCFixup &Fixup, MCValue Target,
134     uint64_t &FixedValue) {
135   uint32_t FixupOffset = Asm.getFragmentOffset(*Fragment) + Fixup.getOffset();
136 
137   if (FixupOffset & 0xff000000) {
138     Asm.getContext().reportError(Fixup.getLoc(),
139                                  "can not encode offset '0x" +
140                                      utohexstr(FixupOffset) +
141                                      "' in resulting scattered relocation.");
142     return;
143   }
144 
145   unsigned IsPCRel = Writer->isFixupKindPCRel(Asm, Fixup.getKind());
146   unsigned Type = MachO::ARM_RELOC_HALF;
147 
148   // See <reloc.h>.
149   const MCSymbol *A = &Target.getSymA()->getSymbol();
150 
151   if (!A->getFragment()) {
152     Asm.getContext().reportError(Fixup.getLoc(),
153                        "symbol '" + A->getName() +
154                        "' can not be undefined in a subtraction expression");
155     return;
156   }
157 
158   uint32_t Value = Writer->getSymbolAddress(*A, Asm);
159   uint32_t Value2 = 0;
160   uint64_t SecAddr = Writer->getSectionAddress(A->getFragment()->getParent());
161   FixedValue += SecAddr;
162 
163   if (const MCSymbolRefExpr *B = Target.getSymB()) {
164     const MCSymbol *SB = &B->getSymbol();
165 
166     if (!SB->getFragment()) {
167       Asm.getContext().reportError(Fixup.getLoc(),
168                          "symbol '" + B->getSymbol().getName() +
169                          "' can not be undefined in a subtraction expression");
170       return;
171     }
172 
173     // Select the appropriate difference relocation type.
174     Type = MachO::ARM_RELOC_HALF_SECTDIFF;
175     Value2 = Writer->getSymbolAddress(B->getSymbol(), Asm);
176     FixedValue -= Writer->getSectionAddress(SB->getFragment()->getParent());
177   }
178 
179   // Relocations are written out in reverse order, so the PAIR comes first.
180   // ARM_RELOC_HALF and ARM_RELOC_HALF_SECTDIFF abuse the r_length field:
181   //
182   // For these two r_type relocations they always have a pair following them and
183   // the r_length bits are used differently.  The encoding of the r_length is as
184   // follows:
185   //   low bit of r_length:
186   //      0 - :lower16: for movw instructions
187   //      1 - :upper16: for movt instructions
188   //   high bit of r_length:
189   //      0 - arm instructions
190   //      1 - thumb instructions
191   // the other half of the relocated expression is in the following pair
192   // relocation entry in the low 16 bits of r_address field.
193   unsigned ThumbBit = 0;
194   unsigned MovtBit = 0;
195   switch (Fixup.getTargetKind()) {
196   default: break;
197   case ARM::fixup_arm_movt_hi16:
198     MovtBit = 1;
199     // The thumb bit shouldn't be set in the 'other-half' bit of the
200     // relocation, but it will be set in FixedValue if the base symbol
201     // is a thumb function. Clear it out here.
202     if (Asm.isThumbFunc(A))
203       FixedValue &= 0xfffffffe;
204     break;
205   case ARM::fixup_t2_movt_hi16:
206     if (Asm.isThumbFunc(A))
207       FixedValue &= 0xfffffffe;
208     MovtBit = 1;
209     [[fallthrough]];
210   case ARM::fixup_t2_movw_lo16:
211     ThumbBit = 1;
212     break;
213   }
214 
215   if (Type == MachO::ARM_RELOC_HALF_SECTDIFF) {
216     uint32_t OtherHalf = MovtBit
217       ? (FixedValue & 0xffff) : ((FixedValue & 0xffff0000) >> 16);
218 
219     MachO::any_relocation_info MRE;
220     MRE.r_word0 = ((OtherHalf             <<  0) |
221                    (MachO::ARM_RELOC_PAIR << 24) |
222                    (MovtBit               << 28) |
223                    (ThumbBit              << 29) |
224                    (IsPCRel               << 30) |
225                    MachO::R_SCATTERED);
226     MRE.r_word1 = Value2;
227     Writer->addRelocation(nullptr, Fragment->getParent(), MRE);
228   }
229 
230   MachO::any_relocation_info MRE;
231   MRE.r_word0 = ((FixupOffset <<  0) |
232                  (Type        << 24) |
233                  (MovtBit     << 28) |
234                  (ThumbBit    << 29) |
235                  (IsPCRel     << 30) |
236                  MachO::R_SCATTERED);
237   MRE.r_word1 = Value;
238   Writer->addRelocation(nullptr, Fragment->getParent(), MRE);
239 }
240 
241 void ARMMachObjectWriter::recordARMScatteredRelocation(
242     MachObjectWriter *Writer, const MCAssembler &Asm,
243     const MCFragment *Fragment, const MCFixup &Fixup, MCValue Target,
244     unsigned Type, unsigned Log2Size, uint64_t &FixedValue) {
245   uint32_t FixupOffset = Asm.getFragmentOffset(*Fragment) + Fixup.getOffset();
246 
247   if (FixupOffset & 0xff000000) {
248     Asm.getContext().reportError(Fixup.getLoc(),
249                                  "can not encode offset '0x" +
250                                      utohexstr(FixupOffset) +
251                                      "' in resulting scattered relocation.");
252     return;
253   }
254 
255   unsigned IsPCRel = Writer->isFixupKindPCRel(Asm, Fixup.getKind());
256 
257   // See <reloc.h>.
258   const MCSymbol *A = &Target.getSymA()->getSymbol();
259 
260   if (!A->getFragment()) {
261     Asm.getContext().reportError(Fixup.getLoc(),
262                        "symbol '" + A->getName() +
263                        "' can not be undefined in a subtraction expression");
264     return;
265   }
266 
267   uint32_t Value = Writer->getSymbolAddress(*A, Asm);
268   uint64_t SecAddr = Writer->getSectionAddress(A->getFragment()->getParent());
269   FixedValue += SecAddr;
270   uint32_t Value2 = 0;
271 
272   if (const MCSymbolRefExpr *B = Target.getSymB()) {
273     assert(Type == MachO::ARM_RELOC_VANILLA && "invalid reloc for 2 symbols");
274     const MCSymbol *SB = &B->getSymbol();
275 
276     if (!SB->getFragment()) {
277       Asm.getContext().reportError(Fixup.getLoc(),
278                          "symbol '" + B->getSymbol().getName() +
279                          "' can not be undefined in a subtraction expression");
280       return;
281     }
282 
283     // Select the appropriate difference relocation type.
284     Type = MachO::ARM_RELOC_SECTDIFF;
285     Value2 = Writer->getSymbolAddress(B->getSymbol(), Asm);
286     FixedValue -= Writer->getSectionAddress(SB->getFragment()->getParent());
287   }
288 
289   // Relocations are written out in reverse order, so the PAIR comes first.
290   if (Type == MachO::ARM_RELOC_SECTDIFF ||
291       Type == MachO::ARM_RELOC_LOCAL_SECTDIFF) {
292     MachO::any_relocation_info MRE;
293     MRE.r_word0 = ((0                     <<  0) |
294                    (MachO::ARM_RELOC_PAIR << 24) |
295                    (Log2Size              << 28) |
296                    (IsPCRel               << 30) |
297                    MachO::R_SCATTERED);
298     MRE.r_word1 = Value2;
299     Writer->addRelocation(nullptr, Fragment->getParent(), MRE);
300   }
301 
302   MachO::any_relocation_info MRE;
303   MRE.r_word0 = ((FixupOffset <<  0) |
304                  (Type        << 24) |
305                  (Log2Size    << 28) |
306                  (IsPCRel     << 30) |
307                  MachO::R_SCATTERED);
308   MRE.r_word1 = Value;
309   Writer->addRelocation(nullptr, Fragment->getParent(), MRE);
310 }
311 
312 bool ARMMachObjectWriter::requiresExternRelocation(MachObjectWriter *Writer,
313                                                    const MCAssembler &Asm,
314                                                    const MCFragment &Fragment,
315                                                    unsigned RelocType,
316                                                    const MCSymbol &S,
317                                                    uint64_t FixedValue) {
318   // Most cases can be identified purely from the symbol.
319   if (Writer->doesSymbolRequireExternRelocation(S))
320     return true;
321   int64_t Value = (int64_t)FixedValue;  // The displacement is signed.
322   int64_t Range;
323   switch (RelocType) {
324   default:
325     return false;
326   case MachO::ARM_RELOC_BR24:
327     // An ARM call might be to a Thumb function, in which case the offset may
328     // not be encodable in the instruction and we must use an external
329     // relocation that explicitly mentions the function. Not a problem if it's
330     // to a temporary "Lwhatever" symbol though, and in fact trying to use an
331     // external relocation there causes more issues.
332     if (!S.isTemporary())
333        return true;
334 
335     // PC pre-adjustment of 8 for these instructions.
336     Value -= 8;
337     // ARM BL/BLX has a 25-bit offset.
338     Range = 0x1ffffff;
339     break;
340   case MachO::ARM_THUMB_RELOC_BR22:
341     // PC pre-adjustment of 4 for these instructions.
342     Value -= 4;
343     // Thumb BL/BLX has a 24-bit offset.
344     Range = 0xffffff;
345   }
346   // BL/BLX also use external relocations when an internal relocation
347   // would result in the target being out of range. This gives the linker
348   // enough information to generate a branch island.
349   Value += Writer->getSectionAddress(&S.getSection());
350   Value -= Writer->getSectionAddress(Fragment.getParent());
351   // If the resultant value would be out of range for an internal relocation,
352   // use an external instead.
353   if (Value > Range || Value < -(Range + 1))
354     return true;
355   return false;
356 }
357 
358 void ARMMachObjectWriter::recordRelocation(MachObjectWriter *Writer,
359                                            MCAssembler &Asm,
360                                            const MCFragment *Fragment,
361                                            const MCFixup &Fixup, MCValue Target,
362                                            uint64_t &FixedValue) {
363   unsigned IsPCRel = Writer->isFixupKindPCRel(Asm, Fixup.getKind());
364   unsigned Log2Size;
365   unsigned RelocType = MachO::ARM_RELOC_VANILLA;
366   if (!getARMFixupKindMachOInfo(Fixup.getKind(), RelocType, Log2Size)) {
367     // If we failed to get fixup kind info, it's because there's no legal
368     // relocation type for the fixup kind. This happens when it's a fixup that's
369     // expected to always be resolvable at assembly time and not have any
370     // relocations needed.
371     Asm.getContext().reportError(Fixup.getLoc(), "unsupported relocation type");
372     return;
373   }
374 
375   // If this is a difference or a defined symbol plus an offset, then we need a
376   // scattered relocation entry.  Differences always require scattered
377   // relocations.
378   if (Target.getSymB()) {
379     if (RelocType == MachO::ARM_RELOC_HALF)
380       return recordARMScatteredHalfRelocation(Writer, Asm, Fragment, Fixup,
381                                               Target, FixedValue);
382     return recordARMScatteredRelocation(Writer, Asm, Fragment, Fixup, Target,
383                                         RelocType, Log2Size, FixedValue);
384   }
385 
386   // Get the symbol data, if any.
387   const MCSymbol *A = nullptr;
388   if (Target.getSymA())
389     A = &Target.getSymA()->getSymbol();
390 
391   // FIXME: For other platforms, we need to use scattered relocations for
392   // internal relocations with offsets.  If this is an internal relocation with
393   // an offset, it also needs a scattered relocation entry.
394   //
395   // Is this right for ARM?
396   uint32_t Offset = Target.getConstant();
397   if (IsPCRel && RelocType == MachO::ARM_RELOC_VANILLA)
398     Offset += 1 << Log2Size;
399   if (Offset && A && !Writer->doesSymbolRequireExternRelocation(*A) &&
400       RelocType != MachO::ARM_RELOC_HALF)
401     return recordARMScatteredRelocation(Writer, Asm, Fragment, Fixup, Target,
402                                         RelocType, Log2Size, FixedValue);
403 
404   // See <reloc.h>.
405   uint32_t FixupOffset = Asm.getFragmentOffset(*Fragment) + Fixup.getOffset();
406   unsigned Index = 0;
407   unsigned Type = 0;
408   const MCSymbol *RelSymbol = nullptr;
409 
410   if (!A) { // constant
411     // FIXME! This is Target.isAbsolute() case as we check SymB above. We check
412     // !A to ensure that null pointer isn't dereferenced and suppress static
413     // analyzer warnings.
414     report_fatal_error("FIXME: relocations to absolute targets "
415                        "not yet implemented");
416   } else {
417     // Resolve constant variables.
418     if (A->isVariable()) {
419       int64_t Res;
420       if (A->getVariableValue()->evaluateAsAbsolute(
421               Res, Asm, Writer->getSectionAddressMap())) {
422         FixedValue = Res;
423         return;
424       }
425     }
426 
427     // Check whether we need an external or internal relocation.
428     if (requiresExternRelocation(Writer, Asm, *Fragment, RelocType, *A,
429                                  FixedValue)) {
430       RelSymbol = A;
431 
432       // For external relocations, make sure to offset the fixup value to
433       // compensate for the addend of the symbol address, if it was
434       // undefined. This occurs with weak definitions, for example.
435       if (!A->isUndefined())
436         FixedValue -= Asm.getSymbolOffset(*A);
437     } else {
438       // The index is the section ordinal (1-based).
439       const MCSection &Sec = A->getSection();
440       Index = Sec.getOrdinal() + 1;
441       FixedValue += Writer->getSectionAddress(&Sec);
442     }
443     if (IsPCRel)
444       FixedValue -= Writer->getSectionAddress(Fragment->getParent());
445 
446     // The type is determined by the fixup kind.
447     Type = RelocType;
448   }
449 
450   // struct relocation_info (8 bytes)
451   MachO::any_relocation_info MRE;
452   MRE.r_word0 = FixupOffset;
453   MRE.r_word1 =
454       (Index << 0) | (IsPCRel << 24) | (Log2Size << 25) | (Type << 28);
455 
456   // Even when it's not a scattered relocation, movw/movt always uses
457   // a PAIR relocation.
458   if (Type == MachO::ARM_RELOC_HALF) {
459     // The entire addend is needed to correctly apply a relocation. One half is
460     // extracted from the instruction itself, the other comes from this
461     // PAIR. I.e. it's correct that we insert the high bits of the addend in the
462     // MOVW case here.  relocation entries.
463     uint32_t Value = 0;
464     switch (Fixup.getTargetKind()) {
465     default: break;
466     case ARM::fixup_arm_movw_lo16:
467     case ARM::fixup_t2_movw_lo16:
468       Value = (FixedValue >> 16) & 0xffff;
469       break;
470     case ARM::fixup_arm_movt_hi16:
471     case ARM::fixup_t2_movt_hi16:
472       Value = FixedValue & 0xffff;
473       break;
474     }
475     MachO::any_relocation_info MREPair;
476     MREPair.r_word0 = Value;
477     MREPair.r_word1 = ((0xffffff              <<  0) |
478                        (Log2Size              << 25) |
479                        (MachO::ARM_RELOC_PAIR << 28));
480 
481     Writer->addRelocation(nullptr, Fragment->getParent(), MREPair);
482   }
483 
484   Writer->addRelocation(RelSymbol, Fragment->getParent(), MRE);
485 }
486 
487 std::unique_ptr<MCObjectTargetWriter>
488 llvm::createARMMachObjectWriter(bool Is64Bit, uint32_t CPUType,
489                                 uint32_t CPUSubtype) {
490   return std::make_unique<ARMMachObjectWriter>(Is64Bit, CPUType, CPUSubtype);
491 }
492