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