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