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