1 //===-- AArch64MachObjectWriter.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/AArch64FixupKinds.h"
10 #include "MCTargetDesc/AArch64MCTargetDesc.h"
11 #include "llvm/ADT/Twine.h"
12 #include "llvm/BinaryFormat/MachO.h"
13 #include "llvm/MC/MCAsmInfo.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/MCFragment.h"
20 #include "llvm/MC/MCMachObjectWriter.h"
21 #include "llvm/MC/MCSection.h"
22 #include "llvm/MC/MCSectionMachO.h"
23 #include "llvm/MC/MCSymbol.h"
24 #include "llvm/MC/MCValue.h"
25 #include "llvm/Support/Casting.h"
26 #include "llvm/Support/MathExtras.h"
34 class AArch64MachObjectWriter
: public MCMachObjectTargetWriter
{
35 bool getAArch64FixupKindMachOInfo(const MCFixup
&Fixup
, unsigned &RelocType
,
36 const MCSymbolRefExpr
*Sym
,
37 unsigned &Log2Size
, const MCAssembler
&Asm
);
40 AArch64MachObjectWriter(uint32_t CPUType
, uint32_t CPUSubtype
, bool IsILP32
)
41 : MCMachObjectTargetWriter(!IsILP32
/* is64Bit */, CPUType
, CPUSubtype
) {}
43 void recordRelocation(MachObjectWriter
*Writer
, MCAssembler
&Asm
,
44 const MCAsmLayout
&Layout
, const MCFragment
*Fragment
,
45 const MCFixup
&Fixup
, MCValue Target
,
46 uint64_t &FixedValue
) override
;
49 } // end anonymous namespace
51 bool AArch64MachObjectWriter::getAArch64FixupKindMachOInfo(
52 const MCFixup
&Fixup
, unsigned &RelocType
, const MCSymbolRefExpr
*Sym
,
53 unsigned &Log2Size
, const MCAssembler
&Asm
) {
54 RelocType
= unsigned(MachO::ARM64_RELOC_UNSIGNED
);
57 switch (Fixup
.getTargetKind()) {
62 Log2Size
= Log2_32(1);
65 Log2Size
= Log2_32(2);
68 Log2Size
= Log2_32(4);
69 if (Sym
->getKind() == MCSymbolRefExpr::VK_GOT
)
70 RelocType
= unsigned(MachO::ARM64_RELOC_POINTER_TO_GOT
);
73 Log2Size
= Log2_32(8);
74 if (Sym
->getKind() == MCSymbolRefExpr::VK_GOT
)
75 RelocType
= unsigned(MachO::ARM64_RELOC_POINTER_TO_GOT
);
77 case AArch64::fixup_aarch64_add_imm12
:
78 case AArch64::fixup_aarch64_ldst_imm12_scale1
:
79 case AArch64::fixup_aarch64_ldst_imm12_scale2
:
80 case AArch64::fixup_aarch64_ldst_imm12_scale4
:
81 case AArch64::fixup_aarch64_ldst_imm12_scale8
:
82 case AArch64::fixup_aarch64_ldst_imm12_scale16
:
83 Log2Size
= Log2_32(4);
84 switch (Sym
->getKind()) {
87 case MCSymbolRefExpr::VK_PAGEOFF
:
88 RelocType
= unsigned(MachO::ARM64_RELOC_PAGEOFF12
);
90 case MCSymbolRefExpr::VK_GOTPAGEOFF
:
91 RelocType
= unsigned(MachO::ARM64_RELOC_GOT_LOAD_PAGEOFF12
);
93 case MCSymbolRefExpr::VK_TLVPPAGEOFF
:
94 RelocType
= unsigned(MachO::ARM64_RELOC_TLVP_LOAD_PAGEOFF12
);
97 case AArch64::fixup_aarch64_pcrel_adrp_imm21
:
98 Log2Size
= Log2_32(4);
99 // This encompasses the relocation for the whole 21-bit value.
100 switch (Sym
->getKind()) {
102 Asm
.getContext().reportError(Fixup
.getLoc(),
103 "ADR/ADRP relocations must be GOT relative");
105 case MCSymbolRefExpr::VK_PAGE
:
106 RelocType
= unsigned(MachO::ARM64_RELOC_PAGE21
);
108 case MCSymbolRefExpr::VK_GOTPAGE
:
109 RelocType
= unsigned(MachO::ARM64_RELOC_GOT_LOAD_PAGE21
);
111 case MCSymbolRefExpr::VK_TLVPPAGE
:
112 RelocType
= unsigned(MachO::ARM64_RELOC_TLVP_LOAD_PAGE21
);
116 case AArch64::fixup_aarch64_pcrel_branch26
:
117 case AArch64::fixup_aarch64_pcrel_call26
:
118 Log2Size
= Log2_32(4);
119 RelocType
= unsigned(MachO::ARM64_RELOC_BRANCH26
);
124 static bool canUseLocalRelocation(const MCSectionMachO
&Section
,
125 const MCSymbol
&Symbol
, unsigned Log2Size
) {
126 // Debug info sections can use local relocations.
127 if (Section
.hasAttribute(MachO::S_ATTR_DEBUG
))
130 // Otherwise, only pointer sized relocations are supported.
134 // But only if they don't point to a few forbidden sections.
135 if (!Symbol
.isInSection())
137 const MCSectionMachO
&RefSec
= cast
<MCSectionMachO
>(Symbol
.getSection());
138 if (RefSec
.getType() == MachO::S_CSTRING_LITERALS
)
141 if (RefSec
.getSegmentName() == "__DATA" &&
142 RefSec
.getSectionName() == "__objc_classrefs")
145 // FIXME: ld64 currently handles internal pointer-sized relocations
146 // incorrectly (applying the addend twice). We should be able to return true
147 // unconditionally by this point when that's fixed.
151 void AArch64MachObjectWriter::recordRelocation(
152 MachObjectWriter
*Writer
, MCAssembler
&Asm
, const MCAsmLayout
&Layout
,
153 const MCFragment
*Fragment
, const MCFixup
&Fixup
, MCValue Target
,
154 uint64_t &FixedValue
) {
155 unsigned IsPCRel
= Writer
->isFixupKindPCRel(Asm
, Fixup
.getKind());
158 uint32_t FixupOffset
= Layout
.getFragmentOffset(Fragment
);
159 unsigned Log2Size
= 0;
163 unsigned Kind
= Fixup
.getKind();
164 const MCSymbol
*RelSymbol
= nullptr;
166 FixupOffset
+= Fixup
.getOffset();
168 // AArch64 pcrel relocation addends do not include the section offset.
170 FixedValue
+= FixupOffset
;
172 // ADRP fixups use relocations for the whole symbol value and only
173 // put the addend in the instruction itself. Clear out any value the
174 // generic code figured out from the sybmol definition.
175 if (Kind
== AArch64::fixup_aarch64_pcrel_adrp_imm21
)
178 // imm19 relocations are for conditional branches, which require
179 // assembler local symbols. If we got here, that's not what we have,
180 // so complain loudly.
181 if (Kind
== AArch64::fixup_aarch64_pcrel_branch19
) {
182 Asm
.getContext().reportError(Fixup
.getLoc(),
183 "conditional branch requires assembler-local"
185 Target
.getSymA()->getSymbol().getName() +
190 // 14-bit branch relocations should only target internal labels, and so
191 // should never get here.
192 if (Kind
== AArch64::fixup_aarch64_pcrel_branch14
) {
193 Asm
.getContext().reportError(Fixup
.getLoc(),
194 "Invalid relocation on conditional branch!");
198 if (!getAArch64FixupKindMachOInfo(Fixup
, Type
, Target
.getSymA(), Log2Size
,
200 Asm
.getContext().reportError(Fixup
.getLoc(), "unknown AArch64 fixup kind!");
204 Value
= Target
.getConstant();
206 if (Target
.isAbsolute()) { // constant
207 // FIXME: Should this always be extern?
208 // SymbolNum of 0 indicates the absolute section.
209 Type
= MachO::ARM64_RELOC_UNSIGNED
;
212 Asm
.getContext().reportError(Fixup
.getLoc(),
213 "PC relative absolute relocation!");
216 // FIXME: x86_64 sets the type to a branch reloc here. Should we do
217 // something similar?
219 } else if (Target
.getSymB()) { // A - B + constant
220 const MCSymbol
*A
= &Target
.getSymA()->getSymbol();
221 const MCSymbol
*A_Base
= Asm
.getAtom(*A
);
223 const MCSymbol
*B
= &Target
.getSymB()->getSymbol();
224 const MCSymbol
*B_Base
= Asm
.getAtom(*B
);
226 // Check for "_foo@got - .", which comes through here as:
228 // ... _foo@got - Ltmp0
229 if (Target
.getSymA()->getKind() == MCSymbolRefExpr::VK_GOT
&&
230 Target
.getSymB()->getKind() == MCSymbolRefExpr::VK_None
&&
231 Layout
.getSymbolOffset(*B
) ==
232 Layout
.getFragmentOffset(Fragment
) + Fixup
.getOffset()) {
233 // SymB is the PC, so use a PC-rel pointer-to-GOT relocation.
234 Type
= MachO::ARM64_RELOC_POINTER_TO_GOT
;
236 MachO::any_relocation_info MRE
;
237 MRE
.r_word0
= FixupOffset
;
238 MRE
.r_word1
= (IsPCRel
<< 24) | (Log2Size
<< 25) | (Type
<< 28);
239 Writer
->addRelocation(A_Base
, Fragment
->getParent(), MRE
);
241 } else if (Target
.getSymA()->getKind() != MCSymbolRefExpr::VK_None
||
242 Target
.getSymB()->getKind() != MCSymbolRefExpr::VK_None
) {
243 // Otherwise, neither symbol can be modified.
244 Asm
.getContext().reportError(Fixup
.getLoc(),
245 "unsupported relocation of modified symbol");
249 // We don't support PCrel relocations of differences.
251 Asm
.getContext().reportError(Fixup
.getLoc(),
252 "unsupported pc-relative relocation of "
257 // AArch64 always uses external relocations. If there is no symbol to use as
258 // a base address (a local symbol with no preceding non-local symbol),
261 // FIXME: We should probably just synthesize an external symbol and use
264 Asm
.getContext().reportError(
266 "unsupported relocation of local symbol '" + A
->getName() +
267 "'. Must have non-local symbol earlier in section.");
271 Asm
.getContext().reportError(
273 "unsupported relocation of local symbol '" + B
->getName() +
274 "'. Must have non-local symbol earlier in section.");
278 if (A_Base
== B_Base
&& A_Base
) {
279 Asm
.getContext().reportError(
280 Fixup
.getLoc(), "unsupported relocation with identical base");
284 Value
+= (!A
->getFragment() ? 0 : Writer
->getSymbolAddress(*A
, Layout
)) -
285 (!A_Base
|| !A_Base
->getFragment() ? 0 : Writer
->getSymbolAddress(
287 Value
-= (!B
->getFragment() ? 0 : Writer
->getSymbolAddress(*B
, Layout
)) -
288 (!B_Base
|| !B_Base
->getFragment() ? 0 : Writer
->getSymbolAddress(
291 Type
= MachO::ARM64_RELOC_UNSIGNED
;
293 MachO::any_relocation_info MRE
;
294 MRE
.r_word0
= FixupOffset
;
295 MRE
.r_word1
= (IsPCRel
<< 24) | (Log2Size
<< 25) | (Type
<< 28);
296 Writer
->addRelocation(A_Base
, Fragment
->getParent(), MRE
);
299 Type
= MachO::ARM64_RELOC_SUBTRACTOR
;
300 } else { // A + constant
301 const MCSymbol
*Symbol
= &Target
.getSymA()->getSymbol();
302 const MCSectionMachO
&Section
=
303 static_cast<const MCSectionMachO
&>(*Fragment
->getParent());
305 bool CanUseLocalRelocation
=
306 canUseLocalRelocation(Section
, *Symbol
, Log2Size
);
307 if (Symbol
->isTemporary() && (Value
|| !CanUseLocalRelocation
)) {
308 // Make sure that the symbol is actually in a section here. If it isn't,
309 // emit an error and exit.
310 if (!Symbol
->isInSection()) {
311 Asm
.getContext().reportError(
313 "unsupported relocation of local symbol '" + Symbol
->getName() +
314 "'. Must have non-local symbol earlier in section.");
317 const MCSection
&Sec
= Symbol
->getSection();
318 if (!Asm
.getContext().getAsmInfo()->isSectionAtomizableBySymbols(Sec
))
319 Symbol
->setUsedInReloc();
322 const MCSymbol
*Base
= Asm
.getAtom(*Symbol
);
323 // If the symbol is a variable it can either be in a section and
324 // we have a base or it is absolute and should have been expanded.
325 assert(!Symbol
->isVariable() || Base
);
327 // Relocations inside debug sections always use local relocations when
328 // possible. This seems to be done because the debugger doesn't fully
329 // understand relocation entries and expects to find values that
330 // have already been fixed up.
331 if (Symbol
->isInSection()) {
332 if (Section
.hasAttribute(MachO::S_ATTR_DEBUG
))
336 // AArch64 uses external relocations as much as possible. For debug
337 // sections, and for pointer-sized relocations (.quad), we allow section
338 // relocations. It's code sections that run into trouble.
342 // Add the local offset, if needed.
345 Layout
.getSymbolOffset(*Symbol
) - Layout
.getSymbolOffset(*Base
);
346 } else if (Symbol
->isInSection()) {
347 if (!CanUseLocalRelocation
) {
348 Asm
.getContext().reportError(
350 "unsupported relocation of local symbol '" + Symbol
->getName() +
351 "'. Must have non-local symbol earlier in section.");
354 // Adjust the relocation to be section-relative.
355 // The index is the section ordinal (1-based).
356 const MCSection
&Sec
= Symbol
->getSection();
357 Index
= Sec
.getOrdinal() + 1;
358 Value
+= Writer
->getSymbolAddress(*Symbol
, Layout
);
361 Value
-= Writer
->getFragmentAddress(Fragment
, Layout
) +
362 Fixup
.getOffset() + (1ULL << Log2Size
);
365 "This constant variable should have been expanded during evaluation");
369 // If the relocation kind is Branch26, Page21, or Pageoff12, any addend
370 // is represented via an Addend relocation, not encoded directly into
372 if ((Type
== MachO::ARM64_RELOC_BRANCH26
||
373 Type
== MachO::ARM64_RELOC_PAGE21
||
374 Type
== MachO::ARM64_RELOC_PAGEOFF12
) &&
376 assert((Value
& 0xff000000) == 0 && "Added relocation out of range!");
378 MachO::any_relocation_info MRE
;
379 MRE
.r_word0
= FixupOffset
;
381 (Index
<< 0) | (IsPCRel
<< 24) | (Log2Size
<< 25) | (Type
<< 28);
382 Writer
->addRelocation(RelSymbol
, Fragment
->getParent(), MRE
);
384 // Now set up the Addend relocation.
385 Type
= MachO::ARM64_RELOC_ADDEND
;
391 // Put zero into the instruction itself. The addend is in the relocation.
395 // If there's any addend left to handle, encode it in the instruction.
398 // struct relocation_info (8 bytes)
399 MachO::any_relocation_info MRE
;
400 MRE
.r_word0
= FixupOffset
;
402 (Index
<< 0) | (IsPCRel
<< 24) | (Log2Size
<< 25) | (Type
<< 28);
403 Writer
->addRelocation(RelSymbol
, Fragment
->getParent(), MRE
);
406 std::unique_ptr
<MCObjectTargetWriter
>
407 llvm::createAArch64MachObjectWriter(uint32_t CPUType
, uint32_t CPUSubtype
,
409 return std::make_unique
<AArch64MachObjectWriter
>(CPUType
, CPUSubtype
,