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