1//===-- ARMMachObjectWriter.cpp - ARM Mach Object Writer ------------------===//
2//
3//                     The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9
10#include "MCTargetDesc/ARMBaseInfo.h"
11#include "MCTargetDesc/ARMFixupKinds.h"
12#include "llvm/ADT/Twine.h"
13#include "llvm/MC/MCAssembler.h"
14#include "llvm/MC/MCAsmLayout.h"
15#include "llvm/MC/MCMachObjectWriter.h"
16#include "llvm/MC/MCExpr.h"
17#include "llvm/MC/MCFixup.h"
18#include "llvm/MC/MCFixupKindInfo.h"
19#include "llvm/MC/MCValue.h"
20#include "llvm/Object/MachOFormat.h"
21#include "llvm/Support/ErrorHandling.h"
22using namespace llvm;
23using namespace llvm::object;
24
25namespace {
26class ARMMachObjectWriter : public MCMachObjectTargetWriter {
27  void RecordARMScatteredRelocation(MachObjectWriter *Writer,
28                                    const MCAssembler &Asm,
29                                    const MCAsmLayout &Layout,
30                                    const MCFragment *Fragment,
31                                    const MCFixup &Fixup,
32                                    MCValue Target,
33                                    unsigned Log2Size,
34                                    uint64_t &FixedValue);
35  void RecordARMMovwMovtRelocation(MachObjectWriter *Writer,
36                                   const MCAssembler &Asm,
37                                   const MCAsmLayout &Layout,
38                                   const MCFragment *Fragment,
39                                   const MCFixup &Fixup, MCValue Target,
40                                   uint64_t &FixedValue);
41
42public:
43  ARMMachObjectWriter(bool Is64Bit, uint32_t CPUType,
44                      uint32_t CPUSubtype)
45    : MCMachObjectTargetWriter(Is64Bit, CPUType, CPUSubtype,
46                               /*UseAggressiveSymbolFolding=*/true) {}
47
48  void RecordRelocation(MachObjectWriter *Writer,
49                        const MCAssembler &Asm, const MCAsmLayout &Layout,
50                        const MCFragment *Fragment, const MCFixup &Fixup,
51                        MCValue Target, uint64_t &FixedValue);
52};
53}
54
55static bool getARMFixupKindMachOInfo(unsigned Kind, unsigned &RelocType,
56                              unsigned &Log2Size) {
57  RelocType = unsigned(macho::RIT_Vanilla);
58  Log2Size = ~0U;
59
60  switch (Kind) {
61  default:
62    return false;
63
64  case FK_Data_1:
65    Log2Size = llvm::Log2_32(1);
66    return true;
67  case FK_Data_2:
68    Log2Size = llvm::Log2_32(2);
69    return true;
70  case FK_Data_4:
71    Log2Size = llvm::Log2_32(4);
72    return true;
73  case FK_Data_8:
74    Log2Size = llvm::Log2_32(8);
75    return true;
76
77    // Handle 24-bit branch kinds.
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_condbranch:
82  case ARM::fixup_arm_uncondbranch:
83    RelocType = unsigned(macho::RIT_ARM_Branch24Bit);
84    // Report as 'long', even though that is not quite accurate.
85    Log2Size = llvm::Log2_32(4);
86    return true;
87
88    // Handle Thumb branches.
89  case ARM::fixup_arm_thumb_br:
90    RelocType = unsigned(macho::RIT_ARM_ThumbBranch22Bit);
91    Log2Size = llvm::Log2_32(2);
92    return true;
93
94  case ARM::fixup_t2_uncondbranch:
95  case ARM::fixup_arm_thumb_bl:
96  case ARM::fixup_arm_thumb_blx:
97    RelocType = unsigned(macho::RIT_ARM_ThumbBranch22Bit);
98    Log2Size = llvm::Log2_32(4);
99    return true;
100
101  case ARM::fixup_arm_movt_hi16:
102  case ARM::fixup_arm_movt_hi16_pcrel:
103  case ARM::fixup_t2_movt_hi16:
104  case ARM::fixup_t2_movt_hi16_pcrel:
105    RelocType = unsigned(macho::RIT_ARM_HalfDifference);
106    // Report as 'long', even though that is not quite accurate.
107    Log2Size = llvm::Log2_32(4);
108    return true;
109
110  case ARM::fixup_arm_movw_lo16:
111  case ARM::fixup_arm_movw_lo16_pcrel:
112  case ARM::fixup_t2_movw_lo16:
113  case ARM::fixup_t2_movw_lo16_pcrel:
114    RelocType = unsigned(macho::RIT_ARM_Half);
115    // Report as 'long', even though that is not quite accurate.
116    Log2Size = llvm::Log2_32(4);
117    return true;
118  }
119}
120
121void ARMMachObjectWriter::
122RecordARMMovwMovtRelocation(MachObjectWriter *Writer,
123                            const MCAssembler &Asm,
124                            const MCAsmLayout &Layout,
125                            const MCFragment *Fragment,
126                            const MCFixup &Fixup,
127                            MCValue Target,
128                            uint64_t &FixedValue) {
129  uint32_t FixupOffset = Layout.getFragmentOffset(Fragment)+Fixup.getOffset();
130  unsigned IsPCRel = Writer->isFixupKindPCRel(Asm, Fixup.getKind());
131  unsigned Type = macho::RIT_ARM_Half;
132
133  // See <reloc.h>.
134  const MCSymbol *A = &Target.getSymA()->getSymbol();
135  MCSymbolData *A_SD = &Asm.getSymbolData(*A);
136
137  if (!A_SD->getFragment())
138    report_fatal_error("symbol '" + A->getName() +
139                       "' can not be undefined in a subtraction expression");
140
141  uint32_t Value = Writer->getSymbolAddress(A_SD, Layout);
142  uint32_t Value2 = 0;
143  uint64_t SecAddr =
144    Writer->getSectionAddress(A_SD->getFragment()->getParent());
145  FixedValue += SecAddr;
146
147  if (const MCSymbolRefExpr *B = Target.getSymB()) {
148    MCSymbolData *B_SD = &Asm.getSymbolData(B->getSymbol());
149
150    if (!B_SD->getFragment())
151      report_fatal_error("symbol '" + B->getSymbol().getName() +
152                         "' can not be undefined in a subtraction expression");
153
154    // Select the appropriate difference relocation type.
155    Type = macho::RIT_ARM_HalfDifference;
156    Value2 = Writer->getSymbolAddress(B_SD, Layout);
157    FixedValue -= Writer->getSectionAddress(B_SD->getFragment()->getParent());
158  }
159
160  // Relocations are written out in reverse order, so the PAIR comes first.
161  // ARM_RELOC_HALF and ARM_RELOC_HALF_SECTDIFF abuse the r_length field:
162  //
163  // For these two r_type relocations they always have a pair following them and
164  // the r_length bits are used differently.  The encoding of the r_length is as
165  // follows:
166  //   low bit of r_length:
167  //      0 - :lower16: for movw instructions
168  //      1 - :upper16: for movt instructions
169  //   high bit of r_length:
170  //      0 - arm instructions
171  //      1 - thumb instructions
172  // the other half of the relocated expression is in the following pair
173  // relocation entry in the the low 16 bits of r_address field.
174  unsigned ThumbBit = 0;
175  unsigned MovtBit = 0;
176  switch ((unsigned)Fixup.getKind()) {
177  default: break;
178  case ARM::fixup_arm_movt_hi16:
179  case ARM::fixup_arm_movt_hi16_pcrel:
180    MovtBit = 1;
181    break;
182  case ARM::fixup_t2_movt_hi16:
183  case ARM::fixup_t2_movt_hi16_pcrel:
184    MovtBit = 1;
185    // Fallthrough
186  case ARM::fixup_t2_movw_lo16:
187  case ARM::fixup_t2_movw_lo16_pcrel:
188    ThumbBit = 1;
189    break;
190  }
191
192
193  if (Type == macho::RIT_ARM_HalfDifference) {
194    uint32_t OtherHalf = MovtBit
195      ? (FixedValue & 0xffff) : ((FixedValue & 0xffff0000) >> 16);
196
197    macho::RelocationEntry MRE;
198    MRE.Word0 = ((OtherHalf       <<  0) |
199                 (macho::RIT_Pair << 24) |
200                 (MovtBit         << 28) |
201                 (ThumbBit        << 29) |
202                 (IsPCRel         << 30) |
203                 macho::RF_Scattered);
204    MRE.Word1 = Value2;
205    Writer->addRelocation(Fragment->getParent(), MRE);
206  }
207
208  macho::RelocationEntry MRE;
209  MRE.Word0 = ((FixupOffset <<  0) |
210               (Type        << 24) |
211               (MovtBit     << 28) |
212               (ThumbBit    << 29) |
213               (IsPCRel     << 30) |
214               macho::RF_Scattered);
215  MRE.Word1 = Value;
216  Writer->addRelocation(Fragment->getParent(), MRE);
217}
218
219void ARMMachObjectWriter::RecordARMScatteredRelocation(MachObjectWriter *Writer,
220                                                    const MCAssembler &Asm,
221                                                    const MCAsmLayout &Layout,
222                                                    const MCFragment *Fragment,
223                                                    const MCFixup &Fixup,
224                                                    MCValue Target,
225                                                    unsigned Log2Size,
226                                                    uint64_t &FixedValue) {
227  uint32_t FixupOffset = Layout.getFragmentOffset(Fragment)+Fixup.getOffset();
228  unsigned IsPCRel = Writer->isFixupKindPCRel(Asm, Fixup.getKind());
229  unsigned Type = macho::RIT_Vanilla;
230
231  // See <reloc.h>.
232  const MCSymbol *A = &Target.getSymA()->getSymbol();
233  MCSymbolData *A_SD = &Asm.getSymbolData(*A);
234
235  if (!A_SD->getFragment())
236    report_fatal_error("symbol '" + A->getName() +
237                       "' can not be undefined in a subtraction expression");
238
239  uint32_t Value = Writer->getSymbolAddress(A_SD, Layout);
240  uint64_t SecAddr = Writer->getSectionAddress(A_SD->getFragment()->getParent());
241  FixedValue += SecAddr;
242  uint32_t Value2 = 0;
243
244  if (const MCSymbolRefExpr *B = Target.getSymB()) {
245    MCSymbolData *B_SD = &Asm.getSymbolData(B->getSymbol());
246
247    if (!B_SD->getFragment())
248      report_fatal_error("symbol '" + B->getSymbol().getName() +
249                         "' can not be undefined in a subtraction expression");
250
251    // Select the appropriate difference relocation type.
252    Type = macho::RIT_Difference;
253    Value2 = Writer->getSymbolAddress(B_SD, Layout);
254    FixedValue -= Writer->getSectionAddress(B_SD->getFragment()->getParent());
255  }
256
257  // Relocations are written out in reverse order, so the PAIR comes first.
258  if (Type == macho::RIT_Difference ||
259      Type == macho::RIT_Generic_LocalDifference) {
260    macho::RelocationEntry MRE;
261    MRE.Word0 = ((0         <<  0) |
262                 (macho::RIT_Pair  << 24) |
263                 (Log2Size  << 28) |
264                 (IsPCRel   << 30) |
265                 macho::RF_Scattered);
266    MRE.Word1 = Value2;
267    Writer->addRelocation(Fragment->getParent(), MRE);
268  }
269
270  macho::RelocationEntry MRE;
271  MRE.Word0 = ((FixupOffset <<  0) |
272               (Type        << 24) |
273               (Log2Size    << 28) |
274               (IsPCRel     << 30) |
275               macho::RF_Scattered);
276  MRE.Word1 = Value;
277  Writer->addRelocation(Fragment->getParent(), MRE);
278}
279
280void ARMMachObjectWriter::RecordRelocation(MachObjectWriter *Writer,
281                                           const MCAssembler &Asm,
282                                           const MCAsmLayout &Layout,
283                                           const MCFragment *Fragment,
284                                           const MCFixup &Fixup,
285                                           MCValue Target,
286                                           uint64_t &FixedValue) {
287  unsigned IsPCRel = Writer->isFixupKindPCRel(Asm, Fixup.getKind());
288  unsigned Log2Size;
289  unsigned RelocType = macho::RIT_Vanilla;
290  if (!getARMFixupKindMachOInfo(Fixup.getKind(), RelocType, Log2Size)) {
291    report_fatal_error("unknown ARM fixup kind!");
292    return;
293  }
294
295  // If this is a difference or a defined symbol plus an offset, then we need a
296  // scattered relocation entry.  Differences always require scattered
297  // relocations.
298  if (Target.getSymB()) {
299    if (RelocType == macho::RIT_ARM_Half ||
300        RelocType == macho::RIT_ARM_HalfDifference)
301      return RecordARMMovwMovtRelocation(Writer, Asm, Layout, Fragment, Fixup,
302                                         Target, FixedValue);
303    return RecordARMScatteredRelocation(Writer, Asm, Layout, Fragment, Fixup,
304                                        Target, Log2Size, FixedValue);
305  }
306
307  // Get the symbol data, if any.
308  MCSymbolData *SD = 0;
309  if (Target.getSymA())
310    SD = &Asm.getSymbolData(Target.getSymA()->getSymbol());
311
312  // FIXME: For other platforms, we need to use scattered relocations for
313  // internal relocations with offsets.  If this is an internal relocation with
314  // an offset, it also needs a scattered relocation entry.
315  //
316  // Is this right for ARM?
317  uint32_t Offset = Target.getConstant();
318  if (IsPCRel && RelocType == macho::RIT_Vanilla)
319    Offset += 1 << Log2Size;
320  if (Offset && SD && !Writer->doesSymbolRequireExternRelocation(SD))
321    return RecordARMScatteredRelocation(Writer, Asm, Layout, Fragment, Fixup,
322                                        Target, Log2Size, FixedValue);
323
324  // See <reloc.h>.
325  uint32_t FixupOffset = Layout.getFragmentOffset(Fragment)+Fixup.getOffset();
326  unsigned Index = 0;
327  unsigned IsExtern = 0;
328  unsigned Type = 0;
329
330  if (Target.isAbsolute()) { // constant
331    // FIXME!
332    report_fatal_error("FIXME: relocations to absolute targets "
333                       "not yet implemented");
334  } else {
335    // Resolve constant variables.
336    if (SD->getSymbol().isVariable()) {
337      int64_t Res;
338      if (SD->getSymbol().getVariableValue()->EvaluateAsAbsolute(
339            Res, Layout, Writer->getSectionAddressMap())) {
340        FixedValue = Res;
341        return;
342      }
343    }
344
345    // Check whether we need an external or internal relocation.
346    if (Writer->doesSymbolRequireExternRelocation(SD)) {
347      IsExtern = 1;
348      Index = SD->getIndex();
349
350      // For external relocations, make sure to offset the fixup value to
351      // compensate for the addend of the symbol address, if it was
352      // undefined. This occurs with weak definitions, for example.
353      if (!SD->Symbol->isUndefined())
354        FixedValue -= Layout.getSymbolOffset(SD);
355    } else {
356      // The index is the section ordinal (1-based).
357      const MCSectionData &SymSD = Asm.getSectionData(
358        SD->getSymbol().getSection());
359      Index = SymSD.getOrdinal() + 1;
360      FixedValue += Writer->getSectionAddress(&SymSD);
361    }
362    if (IsPCRel)
363      FixedValue -= Writer->getSectionAddress(Fragment->getParent());
364
365    // The type is determined by the fixup kind.
366    Type = RelocType;
367  }
368
369  // struct relocation_info (8 bytes)
370  macho::RelocationEntry MRE;
371  MRE.Word0 = FixupOffset;
372  MRE.Word1 = ((Index     <<  0) |
373               (IsPCRel   << 24) |
374               (Log2Size  << 25) |
375               (IsExtern  << 27) |
376               (Type      << 28));
377  Writer->addRelocation(Fragment->getParent(), MRE);
378}
379
380MCObjectWriter *llvm::createARMMachObjectWriter(raw_ostream &OS,
381                                                bool Is64Bit,
382                                                uint32_t CPUType,
383                                                uint32_t CPUSubtype) {
384  return createMachObjectWriter(new ARMMachObjectWriter(Is64Bit,
385                                                        CPUType,
386                                                        CPUSubtype),
387                                OS, /*IsLittleEndian=*/true);
388}
389