1 //===-- ARMMachObjectWriter.cpp - ARM Mach Object Writer ------------------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "MCTargetDesc/ARMBaseInfo.h"
10 #include "MCTargetDesc/ARMFixupKinds.h"
11 #include "MCTargetDesc/ARMMCTargetDesc.h"
12 #include "llvm/ADT/Twine.h"
13 #include "llvm/BinaryFormat/MachO.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/MCMachObjectWriter.h"
21 #include "llvm/MC/MCSection.h"
22 #include "llvm/MC/MCValue.h"
23 #include "llvm/Support/ErrorHandling.h"
24 #include "llvm/Support/ScopedPrinter.h"
29 class ARMMachObjectWriter
: public MCMachObjectTargetWriter
{
30 void RecordARMScatteredRelocation(MachObjectWriter
*Writer
,
31 const MCAssembler
&Asm
,
32 const MCAsmLayout
&Layout
,
33 const MCFragment
*Fragment
,
38 uint64_t &FixedValue
);
39 void RecordARMScatteredHalfRelocation(MachObjectWriter
*Writer
,
40 const MCAssembler
&Asm
,
41 const MCAsmLayout
&Layout
,
42 const MCFragment
*Fragment
,
43 const MCFixup
&Fixup
, MCValue Target
,
44 uint64_t &FixedValue
);
46 bool requiresExternRelocation(MachObjectWriter
*Writer
,
47 const MCAssembler
&Asm
,
48 const MCFragment
&Fragment
, unsigned RelocType
,
49 const MCSymbol
&S
, uint64_t FixedValue
);
52 ARMMachObjectWriter(bool Is64Bit
, uint32_t CPUType
, uint32_t CPUSubtype
)
53 : MCMachObjectTargetWriter(Is64Bit
, CPUType
, CPUSubtype
) {}
55 void recordRelocation(MachObjectWriter
*Writer
, MCAssembler
&Asm
,
56 const MCAsmLayout
&Layout
, const MCFragment
*Fragment
,
57 const MCFixup
&Fixup
, MCValue Target
,
58 uint64_t &FixedValue
) override
;
62 static bool getARMFixupKindMachOInfo(unsigned Kind
, unsigned &RelocType
,
64 RelocType
= unsigned(MachO::ARM_RELOC_VANILLA
);
72 Log2Size
= llvm::Log2_32(1);
75 Log2Size
= llvm::Log2_32(2);
78 Log2Size
= llvm::Log2_32(4);
81 Log2Size
= llvm::Log2_32(8);
84 // These fixups are expected to always be resolvable at assembly time and
85 // have no relocations supported.
86 case ARM::fixup_arm_ldst_pcrel_12
:
87 case ARM::fixup_arm_pcrel_10
:
88 case ARM::fixup_arm_adr_pcrel_12
:
89 case ARM::fixup_arm_thumb_br
:
92 // Handle 24-bit branch kinds.
93 case ARM::fixup_arm_condbranch
:
94 case ARM::fixup_arm_uncondbranch
:
95 case ARM::fixup_arm_uncondbl
:
96 case ARM::fixup_arm_condbl
:
97 case ARM::fixup_arm_blx
:
98 RelocType
= unsigned(MachO::ARM_RELOC_BR24
);
99 // Report as 'long', even though that is not quite accurate.
100 Log2Size
= llvm::Log2_32(4);
103 case ARM::fixup_t2_uncondbranch
:
104 case ARM::fixup_arm_thumb_bl
:
105 case ARM::fixup_arm_thumb_blx
:
106 RelocType
= unsigned(MachO::ARM_THUMB_RELOC_BR22
);
107 Log2Size
= llvm::Log2_32(4);
110 // For movw/movt r_type relocations they always have a pair following them and
111 // the r_length bits are used differently. The encoding of the r_length is as
113 // low bit of r_length:
114 // 0 - :lower16: for movw instructions
115 // 1 - :upper16: for movt instructions
116 // high bit of r_length:
117 // 0 - arm instructions
118 // 1 - thumb instructions
119 case ARM::fixup_arm_movt_hi16
:
120 RelocType
= unsigned(MachO::ARM_RELOC_HALF
);
123 case ARM::fixup_t2_movt_hi16
:
124 RelocType
= unsigned(MachO::ARM_RELOC_HALF
);
128 case ARM::fixup_arm_movw_lo16
:
129 RelocType
= unsigned(MachO::ARM_RELOC_HALF
);
132 case ARM::fixup_t2_movw_lo16
:
133 RelocType
= unsigned(MachO::ARM_RELOC_HALF
);
139 void ARMMachObjectWriter::
140 RecordARMScatteredHalfRelocation(MachObjectWriter
*Writer
,
141 const MCAssembler
&Asm
,
142 const MCAsmLayout
&Layout
,
143 const MCFragment
*Fragment
,
144 const MCFixup
&Fixup
,
146 uint64_t &FixedValue
) {
147 uint32_t FixupOffset
= Layout
.getFragmentOffset(Fragment
)+Fixup
.getOffset();
149 if (FixupOffset
& 0xff000000) {
150 Asm
.getContext().reportError(Fixup
.getLoc(),
151 "can not encode offset '0x" +
152 to_hexString(FixupOffset
) +
153 "' in resulting scattered relocation.");
157 unsigned IsPCRel
= Writer
->isFixupKindPCRel(Asm
, Fixup
.getKind());
158 unsigned Type
= MachO::ARM_RELOC_HALF
;
161 const MCSymbol
*A
= &Target
.getSymA()->getSymbol();
163 if (!A
->getFragment()) {
164 Asm
.getContext().reportError(Fixup
.getLoc(),
165 "symbol '" + A
->getName() +
166 "' can not be undefined in a subtraction expression");
170 uint32_t Value
= Writer
->getSymbolAddress(*A
, Layout
);
172 uint64_t SecAddr
= Writer
->getSectionAddress(A
->getFragment()->getParent());
173 FixedValue
+= SecAddr
;
175 if (const MCSymbolRefExpr
*B
= Target
.getSymB()) {
176 const MCSymbol
*SB
= &B
->getSymbol();
178 if (!SB
->getFragment()) {
179 Asm
.getContext().reportError(Fixup
.getLoc(),
180 "symbol '" + B
->getSymbol().getName() +
181 "' can not be undefined in a subtraction expression");
185 // Select the appropriate difference relocation type.
186 Type
= MachO::ARM_RELOC_HALF_SECTDIFF
;
187 Value2
= Writer
->getSymbolAddress(B
->getSymbol(), Layout
);
188 FixedValue
-= Writer
->getSectionAddress(SB
->getFragment()->getParent());
191 // Relocations are written out in reverse order, so the PAIR comes first.
192 // ARM_RELOC_HALF and ARM_RELOC_HALF_SECTDIFF abuse the r_length field:
194 // For these two r_type relocations they always have a pair following them and
195 // the r_length bits are used differently. The encoding of the r_length is as
197 // low bit of r_length:
198 // 0 - :lower16: for movw instructions
199 // 1 - :upper16: for movt instructions
200 // high bit of r_length:
201 // 0 - arm instructions
202 // 1 - thumb instructions
203 // the other half of the relocated expression is in the following pair
204 // relocation entry in the low 16 bits of r_address field.
205 unsigned ThumbBit
= 0;
206 unsigned MovtBit
= 0;
207 switch (Fixup
.getTargetKind()) {
209 case ARM::fixup_arm_movt_hi16
:
211 // The thumb bit shouldn't be set in the 'other-half' bit of the
212 // relocation, but it will be set in FixedValue if the base symbol
213 // is a thumb function. Clear it out here.
214 if (Asm
.isThumbFunc(A
))
215 FixedValue
&= 0xfffffffe;
217 case ARM::fixup_t2_movt_hi16
:
218 if (Asm
.isThumbFunc(A
))
219 FixedValue
&= 0xfffffffe;
222 case ARM::fixup_t2_movw_lo16
:
227 if (Type
== MachO::ARM_RELOC_HALF_SECTDIFF
) {
228 uint32_t OtherHalf
= MovtBit
229 ? (FixedValue
& 0xffff) : ((FixedValue
& 0xffff0000) >> 16);
231 MachO::any_relocation_info MRE
;
232 MRE
.r_word0
= ((OtherHalf
<< 0) |
233 (MachO::ARM_RELOC_PAIR
<< 24) |
238 MRE
.r_word1
= Value2
;
239 Writer
->addRelocation(nullptr, Fragment
->getParent(), MRE
);
242 MachO::any_relocation_info MRE
;
243 MRE
.r_word0
= ((FixupOffset
<< 0) |
250 Writer
->addRelocation(nullptr, Fragment
->getParent(), MRE
);
253 void ARMMachObjectWriter::RecordARMScatteredRelocation(MachObjectWriter
*Writer
,
254 const MCAssembler
&Asm
,
255 const MCAsmLayout
&Layout
,
256 const MCFragment
*Fragment
,
257 const MCFixup
&Fixup
,
261 uint64_t &FixedValue
) {
262 uint32_t FixupOffset
= Layout
.getFragmentOffset(Fragment
)+Fixup
.getOffset();
264 if (FixupOffset
& 0xff000000) {
265 Asm
.getContext().reportError(Fixup
.getLoc(),
266 "can not encode offset '0x" +
267 to_hexString(FixupOffset
) +
268 "' in resulting scattered relocation.");
272 unsigned IsPCRel
= Writer
->isFixupKindPCRel(Asm
, Fixup
.getKind());
275 const MCSymbol
*A
= &Target
.getSymA()->getSymbol();
277 if (!A
->getFragment()) {
278 Asm
.getContext().reportError(Fixup
.getLoc(),
279 "symbol '" + A
->getName() +
280 "' can not be undefined in a subtraction expression");
284 uint32_t Value
= Writer
->getSymbolAddress(*A
, Layout
);
285 uint64_t SecAddr
= Writer
->getSectionAddress(A
->getFragment()->getParent());
286 FixedValue
+= SecAddr
;
289 if (const MCSymbolRefExpr
*B
= Target
.getSymB()) {
290 assert(Type
== MachO::ARM_RELOC_VANILLA
&& "invalid reloc for 2 symbols");
291 const MCSymbol
*SB
= &B
->getSymbol();
293 if (!SB
->getFragment()) {
294 Asm
.getContext().reportError(Fixup
.getLoc(),
295 "symbol '" + B
->getSymbol().getName() +
296 "' can not be undefined in a subtraction expression");
300 // Select the appropriate difference relocation type.
301 Type
= MachO::ARM_RELOC_SECTDIFF
;
302 Value2
= Writer
->getSymbolAddress(B
->getSymbol(), Layout
);
303 FixedValue
-= Writer
->getSectionAddress(SB
->getFragment()->getParent());
306 // Relocations are written out in reverse order, so the PAIR comes first.
307 if (Type
== MachO::ARM_RELOC_SECTDIFF
||
308 Type
== MachO::ARM_RELOC_LOCAL_SECTDIFF
) {
309 MachO::any_relocation_info MRE
;
310 MRE
.r_word0
= ((0 << 0) |
311 (MachO::ARM_RELOC_PAIR
<< 24) |
315 MRE
.r_word1
= Value2
;
316 Writer
->addRelocation(nullptr, Fragment
->getParent(), MRE
);
319 MachO::any_relocation_info MRE
;
320 MRE
.r_word0
= ((FixupOffset
<< 0) |
326 Writer
->addRelocation(nullptr, Fragment
->getParent(), MRE
);
329 bool ARMMachObjectWriter::requiresExternRelocation(MachObjectWriter
*Writer
,
330 const MCAssembler
&Asm
,
331 const MCFragment
&Fragment
,
334 uint64_t FixedValue
) {
335 // Most cases can be identified purely from the symbol.
336 if (Writer
->doesSymbolRequireExternRelocation(S
))
338 int64_t Value
= (int64_t)FixedValue
; // The displacement is signed.
343 case MachO::ARM_RELOC_BR24
:
344 // An ARM call might be to a Thumb function, in which case the offset may
345 // not be encodable in the instruction and we must use an external
346 // relocation that explicitly mentions the function. Not a problem if it's
347 // to a temporary "Lwhatever" symbol though, and in fact trying to use an
348 // external relocation there causes more issues.
349 if (!S
.isTemporary())
352 // PC pre-adjustment of 8 for these instructions.
354 // ARM BL/BLX has a 25-bit offset.
357 case MachO::ARM_THUMB_RELOC_BR22
:
358 // PC pre-adjustment of 4 for these instructions.
360 // Thumb BL/BLX has a 24-bit offset.
363 // BL/BLX also use external relocations when an internal relocation
364 // would result in the target being out of range. This gives the linker
365 // enough information to generate a branch island.
366 Value
+= Writer
->getSectionAddress(&S
.getSection());
367 Value
-= Writer
->getSectionAddress(Fragment
.getParent());
368 // If the resultant value would be out of range for an internal relocation,
369 // use an external instead.
370 if (Value
> Range
|| Value
< -(Range
+ 1))
375 void ARMMachObjectWriter::recordRelocation(MachObjectWriter
*Writer
,
377 const MCAsmLayout
&Layout
,
378 const MCFragment
*Fragment
,
379 const MCFixup
&Fixup
, MCValue Target
,
380 uint64_t &FixedValue
) {
381 unsigned IsPCRel
= Writer
->isFixupKindPCRel(Asm
, Fixup
.getKind());
383 unsigned RelocType
= MachO::ARM_RELOC_VANILLA
;
384 if (!getARMFixupKindMachOInfo(Fixup
.getKind(), RelocType
, Log2Size
)) {
385 // If we failed to get fixup kind info, it's because there's no legal
386 // relocation type for the fixup kind. This happens when it's a fixup that's
387 // expected to always be resolvable at assembly time and not have any
388 // relocations needed.
389 Asm
.getContext().reportError(Fixup
.getLoc(),
390 "unsupported relocation on symbol");
394 // If this is a difference or a defined symbol plus an offset, then we need a
395 // scattered relocation entry. Differences always require scattered
397 if (Target
.getSymB()) {
398 if (RelocType
== MachO::ARM_RELOC_HALF
)
399 return RecordARMScatteredHalfRelocation(Writer
, Asm
, Layout
, Fragment
,
400 Fixup
, Target
, FixedValue
);
401 return RecordARMScatteredRelocation(Writer
, Asm
, Layout
, Fragment
, Fixup
,
402 Target
, RelocType
, Log2Size
,
406 // Get the symbol data, if any.
407 const MCSymbol
*A
= nullptr;
408 if (Target
.getSymA())
409 A
= &Target
.getSymA()->getSymbol();
411 // FIXME: For other platforms, we need to use scattered relocations for
412 // internal relocations with offsets. If this is an internal relocation with
413 // an offset, it also needs a scattered relocation entry.
415 // Is this right for ARM?
416 uint32_t Offset
= Target
.getConstant();
417 if (IsPCRel
&& RelocType
== MachO::ARM_RELOC_VANILLA
)
418 Offset
+= 1 << Log2Size
;
419 if (Offset
&& A
&& !Writer
->doesSymbolRequireExternRelocation(*A
) &&
420 RelocType
!= MachO::ARM_RELOC_HALF
)
421 return RecordARMScatteredRelocation(Writer
, Asm
, Layout
, Fragment
, Fixup
,
422 Target
, RelocType
, Log2Size
,
426 uint32_t FixupOffset
= Layout
.getFragmentOffset(Fragment
)+Fixup
.getOffset();
429 const MCSymbol
*RelSymbol
= nullptr;
431 if (Target
.isAbsolute()) { // constant
433 report_fatal_error("FIXME: relocations to absolute targets "
434 "not yet implemented");
436 // Resolve constant variables.
437 if (A
->isVariable()) {
439 if (A
->getVariableValue()->evaluateAsAbsolute(
440 Res
, Layout
, Writer
->getSectionAddressMap())) {
446 // Check whether we need an external or internal relocation.
447 if (requiresExternRelocation(Writer
, Asm
, *Fragment
, RelocType
, *A
,
451 // For external relocations, make sure to offset the fixup value to
452 // compensate for the addend of the symbol address, if it was
453 // undefined. This occurs with weak definitions, for example.
454 if (!A
->isUndefined())
455 FixedValue
-= Layout
.getSymbolOffset(*A
);
457 // The index is the section ordinal (1-based).
458 const MCSection
&Sec
= A
->getSection();
459 Index
= Sec
.getOrdinal() + 1;
460 FixedValue
+= Writer
->getSectionAddress(&Sec
);
463 FixedValue
-= Writer
->getSectionAddress(Fragment
->getParent());
465 // The type is determined by the fixup kind.
469 // struct relocation_info (8 bytes)
470 MachO::any_relocation_info MRE
;
471 MRE
.r_word0
= FixupOffset
;
473 (Index
<< 0) | (IsPCRel
<< 24) | (Log2Size
<< 25) | (Type
<< 28);
475 // Even when it's not a scattered relocation, movw/movt always uses
476 // a PAIR relocation.
477 if (Type
== MachO::ARM_RELOC_HALF
) {
478 // The entire addend is needed to correctly apply a relocation. One half is
479 // extracted from the instruction itself, the other comes from this
480 // PAIR. I.e. it's correct that we insert the high bits of the addend in the
481 // MOVW case here. relocation entries.
483 switch (Fixup
.getTargetKind()) {
485 case ARM::fixup_arm_movw_lo16
:
486 case ARM::fixup_t2_movw_lo16
:
487 Value
= (FixedValue
>> 16) & 0xffff;
489 case ARM::fixup_arm_movt_hi16
:
490 case ARM::fixup_t2_movt_hi16
:
491 Value
= FixedValue
& 0xffff;
494 MachO::any_relocation_info MREPair
;
495 MREPair
.r_word0
= Value
;
496 MREPair
.r_word1
= ((0xffffff << 0) |
498 (MachO::ARM_RELOC_PAIR
<< 28));
500 Writer
->addRelocation(nullptr, Fragment
->getParent(), MREPair
);
503 Writer
->addRelocation(RelSymbol
, Fragment
->getParent(), MRE
);
506 std::unique_ptr
<MCObjectTargetWriter
>
507 llvm::createARMMachObjectWriter(bool Is64Bit
, uint32_t CPUType
,
508 uint32_t CPUSubtype
) {
509 return std::make_unique
<ARMMachObjectWriter
>(Is64Bit
, CPUType
, CPUSubtype
);