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