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