xref: /freebsd/contrib/llvm-project/llvm/lib/Target/ARM/MCTargetDesc/ARMMachObjectWriter.cpp (revision 2da066ef6d85d3f7cd8aaec14369d66254836536)
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 false;
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(), "unsupported relocation type");
390     return;
391   }
392 
393   // If this is a difference or a defined symbol plus an offset, then we need a
394   // scattered relocation entry.  Differences always require scattered
395   // relocations.
396   if (Target.getSymB()) {
397     if (RelocType == MachO::ARM_RELOC_HALF)
398       return RecordARMScatteredHalfRelocation(Writer, Asm, Layout, Fragment,
399                                               Fixup, Target, FixedValue);
400     return RecordARMScatteredRelocation(Writer, Asm, Layout, Fragment, Fixup,
401                                         Target, RelocType, Log2Size,
402                                         FixedValue);
403   }
404 
405   // Get the symbol data, if any.
406   const MCSymbol *A = nullptr;
407   if (Target.getSymA())
408     A = &Target.getSymA()->getSymbol();
409 
410   // FIXME: For other platforms, we need to use scattered relocations for
411   // internal relocations with offsets.  If this is an internal relocation with
412   // an offset, it also needs a scattered relocation entry.
413   //
414   // Is this right for ARM?
415   uint32_t Offset = Target.getConstant();
416   if (IsPCRel && RelocType == MachO::ARM_RELOC_VANILLA)
417     Offset += 1 << Log2Size;
418   if (Offset && A && !Writer->doesSymbolRequireExternRelocation(*A) &&
419       RelocType != MachO::ARM_RELOC_HALF)
420     return RecordARMScatteredRelocation(Writer, Asm, Layout, Fragment, Fixup,
421                                         Target, RelocType, Log2Size,
422                                         FixedValue);
423 
424   // See <reloc.h>.
425   uint32_t FixupOffset = Layout.getFragmentOffset(Fragment)+Fixup.getOffset();
426   unsigned Index = 0;
427   unsigned Type = 0;
428   const MCSymbol *RelSymbol = nullptr;
429 
430   if (!A) { // constant
431     // FIXME! This is Target.isAbsolute() case as we check SymB above. We check
432     // !A to ensure that null pointer isn't dereferenced and suppress static
433     // analyzer warnings.
434     report_fatal_error("FIXME: relocations to absolute targets "
435                        "not yet implemented");
436   } else {
437     // Resolve constant variables.
438     if (A->isVariable()) {
439       int64_t Res;
440       if (A->getVariableValue()->evaluateAsAbsolute(
441               Res, Layout, Writer->getSectionAddressMap())) {
442         FixedValue = Res;
443         return;
444       }
445     }
446 
447     // Check whether we need an external or internal relocation.
448     if (requiresExternRelocation(Writer, Asm, *Fragment, RelocType, *A,
449                                  FixedValue)) {
450       RelSymbol = A;
451 
452       // For external relocations, make sure to offset the fixup value to
453       // compensate for the addend of the symbol address, if it was
454       // undefined. This occurs with weak definitions, for example.
455       if (!A->isUndefined())
456         FixedValue -= Layout.getSymbolOffset(*A);
457     } else {
458       // The index is the section ordinal (1-based).
459       const MCSection &Sec = A->getSection();
460       Index = Sec.getOrdinal() + 1;
461       FixedValue += Writer->getSectionAddress(&Sec);
462     }
463     if (IsPCRel)
464       FixedValue -= Writer->getSectionAddress(Fragment->getParent());
465 
466     // The type is determined by the fixup kind.
467     Type = RelocType;
468   }
469 
470   // struct relocation_info (8 bytes)
471   MachO::any_relocation_info MRE;
472   MRE.r_word0 = FixupOffset;
473   MRE.r_word1 =
474       (Index << 0) | (IsPCRel << 24) | (Log2Size << 25) | (Type << 28);
475 
476   // Even when it's not a scattered relocation, movw/movt always uses
477   // a PAIR relocation.
478   if (Type == MachO::ARM_RELOC_HALF) {
479     // The entire addend is needed to correctly apply a relocation. One half is
480     // extracted from the instruction itself, the other comes from this
481     // PAIR. I.e. it's correct that we insert the high bits of the addend in the
482     // MOVW case here.  relocation entries.
483     uint32_t Value = 0;
484     switch (Fixup.getTargetKind()) {
485     default: break;
486     case ARM::fixup_arm_movw_lo16:
487     case ARM::fixup_t2_movw_lo16:
488       Value = (FixedValue >> 16) & 0xffff;
489       break;
490     case ARM::fixup_arm_movt_hi16:
491     case ARM::fixup_t2_movt_hi16:
492       Value = FixedValue & 0xffff;
493       break;
494     }
495     MachO::any_relocation_info MREPair;
496     MREPair.r_word0 = Value;
497     MREPair.r_word1 = ((0xffffff              <<  0) |
498                        (Log2Size              << 25) |
499                        (MachO::ARM_RELOC_PAIR << 28));
500 
501     Writer->addRelocation(nullptr, Fragment->getParent(), MREPair);
502   }
503 
504   Writer->addRelocation(RelSymbol, Fragment->getParent(), MRE);
505 }
506 
507 std::unique_ptr<MCObjectTargetWriter>
508 llvm::createARMMachObjectWriter(bool Is64Bit, uint32_t CPUType,
509                                 uint32_t CPUSubtype) {
510   return std::make_unique<ARMMachObjectWriter>(Is64Bit, CPUType, CPUSubtype);
511 }
512