LLVM 22.0.0git
ARMMachObjectWriter.cpp
Go to the documentation of this file.
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
12#include "llvm/ADT/Twine.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"
20#include "llvm/MC/MCSection.h"
22#include "llvm/MC/MCValue.h"
24
25using namespace llvm;
26
27namespace {
28class ARMMachObjectWriter : public MCMachObjectTargetWriter {
29 void recordARMScatteredRelocation(MachObjectWriter *Writer,
30 const MCAssembler &Asm,
31 const MCFragment *Fragment,
33 unsigned Type, unsigned Log2Size,
34 uint64_t &FixedValue);
35 void recordARMScatteredHalfRelocation(MachObjectWriter *Writer,
36 const MCAssembler &Asm,
37 const MCFragment *Fragment,
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
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) {
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 false;
77
78 // These fixups are expected to always be resolvable at assembly time and
79 // have no relocations supported.
84 return false;
85
86 // Handle 24-bit branch kinds.
93 // Report as 'long', even though that is not quite accurate.
94 Log2Size = llvm::Log2_32(4);
95 return true;
96
101 Log2Size = llvm::Log2_32(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
114 RelocType = unsigned(MachO::ARM_RELOC_HALF);
115 Log2Size = 1;
116 return true;
118 RelocType = unsigned(MachO::ARM_RELOC_HALF);
119 Log2Size = 3;
120 return true;
121
123 RelocType = unsigned(MachO::ARM_RELOC_HALF);
124 Log2Size = 0;
125 return true;
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(*Fragment) + Fixup.getOffset();
138
139 if (FixupOffset & 0xff000000) {
140 reportError(Fixup.getLoc(), "can not encode offset '0x" +
141 utohexstr(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(Fixup.getLoc(),
154 "symbol '" + A->getName() +
155 "' can not be undefined in a subtraction expression");
156 return;
157 }
158
159 uint32_t Value = Writer->getSymbolAddress(*A);
160 uint32_t Value2 = 0;
161 uint64_t SecAddr = Writer->getSectionAddress(A->getFragment()->getParent());
162 FixedValue += SecAddr;
163
164 if (const MCSymbol *SB = Target.getSubSym()) {
165 if (!SB->getFragment()) {
166 reportError(Fixup.getLoc(),
167 "symbol '" + SB->getName() +
168 "' can not be undefined in a subtraction expression");
169 return;
170 }
171
172 // Select the appropriate difference relocation type.
174 Value2 = Writer->getSymbolAddress(*SB);
175 FixedValue -= Writer->getSectionAddress(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;
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(A))
202 FixedValue &= 0xfffffffe;
203 break;
205 if (Asm.isThumbFunc(A))
206 FixedValue &= 0xfffffffe;
207 MovtBit = 1;
208 [[fallthrough]];
210 ThumbBit = 1;
211 break;
212 }
213
215 uint32_t OtherHalf = MovtBit
216 ? (FixedValue & 0xffff) : ((FixedValue & 0xffff0000) >> 16);
217
219 MRE.r_word0 = ((OtherHalf << 0) |
220 (MachO::ARM_RELOC_PAIR << 24) |
221 (MovtBit << 28) |
222 (ThumbBit << 29) |
223 (IsPCRel << 30) |
225 MRE.r_word1 = Value2;
226 Writer->addRelocation(nullptr, Fragment->getParent(), MRE);
227 }
228
230 MRE.r_word0 = ((FixupOffset << 0) |
231 (Type << 24) |
232 (MovtBit << 28) |
233 (ThumbBit << 29) |
234 (IsPCRel << 30) |
236 MRE.r_word1 = Value;
237 Writer->addRelocation(nullptr, 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(*Fragment) + Fixup.getOffset();
245
246 if (FixupOffset & 0xff000000) {
247 reportError(Fixup.getLoc(), "can not encode offset '0x" +
248 utohexstr(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(Fixup.getLoc(),
260 "symbol '" + A->getName() +
261 "' can not be undefined in a subtraction expression");
262 return;
263 }
264
265 uint32_t Value = Writer->getSymbolAddress(*A);
266 uint64_t SecAddr = Writer->getSectionAddress(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(Fixup.getLoc(),
275 "symbol '" + SB->getName() +
276 "' can not be undefined in a subtraction expression");
277 return;
278 }
279
280 // Select the appropriate difference relocation type.
282 Value2 = Writer->getSymbolAddress(*SB);
283 FixedValue -= Writer->getSectionAddress(SB->getFragment()->getParent());
284 }
285
286 // Relocations are written out in reverse order, so the PAIR comes first.
290 MRE.r_word0 = ((0 << 0) |
291 (MachO::ARM_RELOC_PAIR << 24) |
292 (Log2Size << 28) |
293 (IsPCRel << 30) |
295 MRE.r_word1 = Value2;
296 Writer->addRelocation(nullptr, Fragment->getParent(), MRE);
297 }
298
300 MRE.r_word0 = ((FixupOffset << 0) |
301 (Type << 24) |
302 (Log2Size << 28) |
303 (IsPCRel << 30) |
305 MRE.r_word1 = Value;
306 Writer->addRelocation(nullptr, 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.
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;
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;
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(&S.getSection());
347 Value -= Writer->getSectionAddress(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(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(Fixup.getLoc(), "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 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(*A) &&
395 RelocType != MachO::ARM_RELOC_HALF)
396 return recordARMScatteredRelocation(Writer, Asm, Fragment, Fixup, Target,
397 RelocType, Log2Size, FixedValue);
398
399 // See <reloc.h>.
400 uint32_t FixupOffset = Asm.getFragmentOffset(*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("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(Val, &Asm);
417 int64_t Res = Val.getConstant();
418 bool isAbs = Val.isAbsolute();
419 if (Relocatable && Val.getAddSym() && Val.getSubSym()) {
420 Res += Writer->getSymbolAddress(*Val.getAddSym()) -
421 Writer->getSymbolAddress(*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, RelocType, *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(*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);
445 }
446 if (IsPCRel)
447 FixedValue -= Writer->getSectionAddress(Fragment->getParent());
448
449 // The type is determined by the fixup kind.
450 Type = RelocType;
451 }
452
453 // struct relocation_info (8 bytes)
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.
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;
471 Value = (FixedValue >> 16) & 0xffff;
472 break;
475 Value = FixedValue & 0xffff;
476 break;
477 }
479 MREPair.r_word0 = Value;
480 MREPair.r_word1 = ((0xffffff << 0) |
481 (Log2Size << 25) |
482 (MachO::ARM_RELOC_PAIR << 28));
483
484 Writer->addRelocation(nullptr, Fragment->getParent(), MREPair);
485 }
486
487 Writer->addRelocation(RelSymbol, Fragment->getParent(), MRE);
488}
489
490std::unique_ptr<MCObjectTargetWriter>
492 uint32_t CPUSubtype) {
493 return std::make_unique<ARMMachObjectWriter>(Is64Bit, CPUType, CPUSubtype);
494}
495
496namespace {
497class ARMTargetMachOStreamer : public ARMTargetStreamer {
498public:
499 ARMTargetMachOStreamer(MCStreamer &S) : ARMTargetStreamer(S) {}
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
513 return new ARMTargetMachOStreamer(S);
514}
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
static bool getARMFixupKindMachOInfo(unsigned Kind, unsigned &RelocType, unsigned &Log2Size)
static Error reportError(StringRef Message)
static GCRegistry::Add< ErlangGC > A("erlang", "erlang-compatible garbage collector")
ConstantRange Range(APInt(BitWidth, Low), APInt(BitWidth, High))
PowerPC TLS Dynamic Call Fixup
This file contains some functions that are useful when dealing with strings.
virtual void emitThumbFunc(MCSymbol *Symbol)
Encode information on a single operation to perform on a byte sequence (e.g., an encoded instruction)...
Definition: MCFixup.h:61
MCSection * getParent() const
Definition: MCSection.h:158
virtual void recordRelocation(MachObjectWriter *Writer, MCAssembler &Asm, const MCFragment *Fragment, const MCFixup &Fixup, MCValue Target, uint64_t &FixedValue)=0
Streaming object file generation interface.
Instances of this class represent a uniqued identifier for a section in the current translation unit.
Definition: MCSection.h:496
unsigned getOrdinal() const
Definition: MCSection.h:588
Streaming machine code generation interface.
Definition: MCStreamer.h:220
MCSymbol - Instances of this class represent a symbol name in the MC file, and MCSymbols are created ...
Definition: MCSymbol.h:42
MCSection & getSection() const
Get the section associated with a defined, non-absolute symbol.
Definition: MCSymbol.h:251
bool isTemporary() const
isTemporary - Check if this is an assembler temporary symbol.
Definition: MCSymbol.h:205
Target specific streamer interface.
Definition: MCStreamer.h:93
MCStreamer & getStreamer()
Definition: MCStreamer.h:101
MCStreamer & Streamer
Definition: MCStreamer.h:95
const MCSymbol * getAddSym() const
Definition: MCValue.h:49
int64_t getConstant() const
Definition: MCValue.h:44
const MCSymbol * getSubSym() const
Definition: MCValue.h:51
bool isAbsolute() const
Is this an absolute (as opposed to relocatable) value.
Definition: MCValue.h:54
bool doesSymbolRequireExternRelocation(const MCSymbol &S)
uint64_t getSectionAddress(const MCSection *Sec) const
void addRelocation(const MCSymbol *RelSymbol, const MCSection *Sec, MachO::any_relocation_info &MRE)
uint64_t getSymbolAddress(const MCSymbol &S) const
Target - Wrapper for Target specific information.
The instances of the Type class are immutable: once they are created, they are never changed.
Definition: Type.h:45
LLVM Value Representation.
Definition: Value.h:75
@ fixup_arm_thumb_br
Definition: ARMFixupKinds.h:60
@ fixup_arm_adr_pcrel_12
Definition: ARMFixupKinds.h:45
@ fixup_arm_pcrel_10
Definition: ARMFixupKinds.h:29
@ fixup_arm_uncondbranch
Definition: ARMFixupKinds.h:51
@ fixup_arm_movw_lo16
Definition: ARMFixupKinds.h:98
@ fixup_t2_movt_hi16
Definition: ARMFixupKinds.h:99
@ fixup_arm_movt_hi16
Definition: ARMFixupKinds.h:97
@ fixup_arm_thumb_blx
Definition: ARMFixupKinds.h:84
@ fixup_t2_uncondbranch
Definition: ARMFixupKinds.h:57
@ fixup_arm_uncondbl
Definition: ARMFixupKinds.h:72
@ fixup_arm_condbl
Definition: ARMFixupKinds.h:75
@ fixup_arm_ldst_pcrel_12
Definition: ARMFixupKinds.h:18
@ fixup_arm_thumb_bl
Definition: ARMFixupKinds.h:81
@ fixup_t2_movw_lo16
@ fixup_arm_condbranch
Definition: ARMFixupKinds.h:49
@ R_SCATTERED
Definition: MachO.h:402
@ ARM_RELOC_PAIR
Definition: MachO.h:441
@ ARM_RELOC_BR24
Definition: MachO.h:445
@ ARM_THUMB_RELOC_BR22
Definition: MachO.h:446
@ ARM_RELOC_LOCAL_SECTDIFF
Definition: MachO.h:443
@ ARM_RELOC_HALF_SECTDIFF
Definition: MachO.h:449
@ ARM_RELOC_SECTDIFF
Definition: MachO.h:442
@ ARM_RELOC_HALF
Definition: MachO.h:448
@ ARM_RELOC_VANILLA
Definition: MachO.h:440
This is an optimization pass for GlobalISel generic memory operations.
Definition: AddressRanges.h:18
@ Offset
Definition: DWP.cpp:477
@ FK_Data_8
A eight-byte fixup.
Definition: MCFixup.h:37
@ FK_Data_1
A one-byte fixup.
Definition: MCFixup.h:34
@ FK_Data_4
A four-byte fixup.
Definition: MCFixup.h:36
@ FK_Data_2
A two-byte fixup.
Definition: MCFixup.h:35
MCTargetStreamer * createARMObjectTargetMachOStreamer(MCStreamer &S)
std::unique_ptr< MCObjectTargetWriter > createARMMachObjectWriter(bool Is64Bit, uint32_t CPUType, uint32_t CPUSubtype)
Construct an ARM Mach-O object writer.
unsigned Log2_32(uint32_t Value)
Return the floor log base 2 of the specified value, -1 if the value is zero.
Definition: MathExtras.h:336
LLVM_ABI void report_fatal_error(Error Err, bool gen_crash_diag=true)
Definition: Error.cpp:167