ARMMachObjectWriter.cpp revision 360784
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/Twine.h"
13#include "llvm/BinaryFormat/MachO.h"
14#include "llvm/MC/MCAsmLayout.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#include "llvm/Support/ScopedPrinter.h"
25
26using namespace llvm;
27
28namespace {
29class 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
51public:
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
62static 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
139void ARMMachObjectWriter::
140RecordARMScatteredHalfRelocation(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                                     to_hexString(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    LLVM_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
253void 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                                     to_hexString(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
329bool 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
375void 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
506std::unique_ptr<MCObjectTargetWriter>
507llvm::createARMMachObjectWriter(bool Is64Bit, uint32_t CPUType,
508                                uint32_t CPUSubtype) {
509  return std::make_unique<ARMMachObjectWriter>(Is64Bit, CPUType, CPUSubtype);
510}
511