1 //===-- RuntimeDyldCOFFAArch64.h --- COFF/AArch64 specific code ---*- C++
4 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5 // See https://llvm.org/LICENSE.txt for license information.
6 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
8 //===----------------------------------------------------------------------===//
10 // COFF AArch64 support for MC-JIT runtime dynamic linker.
12 //===----------------------------------------------------------------------===//
14 #ifndef LLVM_LIB_EXECUTIONENGINE_RUNTIMEDYLD_TARGETS_RUNTIMEDYLDCOFFAARCH64_H
15 #define LLVM_LIB_EXECUTIONENGINE_RUNTIMEDYLD_TARGETS_RUNTIMEDYLDCOFFAARCH64_H
17 #include "../RuntimeDyldCOFF.h"
18 #include "llvm/BinaryFormat/COFF.h"
19 #include "llvm/Object/COFF.h"
20 #include "llvm/Support/Endian.h"
22 #define DEBUG_TYPE "dyld"
24 using namespace llvm::support::endian
;
28 // This relocation type is used for handling long branch instruction
30 enum InternalRelocationType
: unsigned {
31 INTERNAL_REL_ARM64_LONG_BRANCH26
= 0x111,
34 static void add16(uint8_t *p
, int16_t v
) { write16le(p
, read16le(p
) + v
); }
35 static void or32le(void *P
, int32_t V
) { write32le(P
, read32le(P
) | V
); }
37 static void write32AArch64Imm(uint8_t *T
, uint64_t imm
, uint32_t rangeLimit
) {
38 uint32_t orig
= read32le(T
);
39 orig
&= ~(0xFFF << 10);
40 write32le(T
, orig
| ((imm
& (0xFFF >> rangeLimit
)) << 10));
43 static void write32AArch64Ldr(uint8_t *T
, uint64_t imm
) {
44 uint32_t orig
= read32le(T
);
45 uint32_t size
= orig
>> 30;
46 // 0x04000000 indicates SIMD/FP registers
47 // 0x00800000 indicates 128 bit
48 if ((orig
& 0x04800000) == 0x04800000)
50 if ((imm
& ((1 << size
) - 1)) != 0)
51 assert(0 && "misaligned ldr/str offset");
52 write32AArch64Imm(T
, imm
>> size
, size
);
55 static void write32AArch64Addr(void *T
, uint64_t s
, uint64_t p
, int shift
) {
56 uint64_t Imm
= (s
>> shift
) - (p
>> shift
);
57 uint32_t ImmLo
= (Imm
& 0x3) << 29;
58 uint32_t ImmHi
= (Imm
& 0x1FFFFC) << 3;
59 uint64_t Mask
= (0x3 << 29) | (0x1FFFFC << 3);
60 write32le(T
, (read32le(T
) & ~Mask
) | ImmLo
| ImmHi
);
63 class RuntimeDyldCOFFAArch64
: public RuntimeDyldCOFF
{
66 // When a module is loaded we save the SectionID of the unwind
67 // sections in a table until we receive a request to register all
68 // unregisteredEH frame sections with the memory manager.
69 SmallVector
<SID
, 2> UnregisteredEHFrameSections
;
70 SmallVector
<SID
, 2> RegisteredEHFrameSections
;
73 // Fake an __ImageBase pointer by returning the section with the lowest adress
74 uint64_t getImageBase() {
76 ImageBase
= std::numeric_limits
<uint64_t>::max();
77 for (const SectionEntry
&Section
: Sections
)
78 // The Sections list may contain sections that weren't loaded for
79 // whatever reason: they may be debug sections, and ProcessAllSections
80 // is false, or they may be sections that contain 0 bytes. If the
81 // section isn't loaded, the load address will be 0, and it should not
82 // be included in the ImageBase calculation.
83 if (Section
.getLoadAddress() != 0)
84 ImageBase
= std::min(ImageBase
, Section
.getLoadAddress());
90 RuntimeDyldCOFFAArch64(RuntimeDyld::MemoryManager
&MM
,
91 JITSymbolResolver
&Resolver
)
92 : RuntimeDyldCOFF(MM
, Resolver
, 8, COFF::IMAGE_REL_ARM64_ADDR64
),
95 unsigned getStubAlignment() override
{ return 8; }
97 unsigned getMaxStubSize() const override
{ return 20; }
99 std::tuple
<uint64_t, uint64_t, uint64_t>
100 generateRelocationStub(unsigned SectionID
, StringRef TargetName
,
101 uint64_t Offset
, uint64_t RelType
, uint64_t Addend
,
103 uintptr_t StubOffset
;
104 SectionEntry
&Section
= Sections
[SectionID
];
106 RelocationValueRef OriginalRelValueRef
;
107 OriginalRelValueRef
.SectionID
= SectionID
;
108 OriginalRelValueRef
.Offset
= Offset
;
109 OriginalRelValueRef
.Addend
= Addend
;
110 OriginalRelValueRef
.SymbolName
= TargetName
.data();
112 auto Stub
= Stubs
.find(OriginalRelValueRef
);
113 if (Stub
== Stubs
.end()) {
114 LLVM_DEBUG(dbgs() << " Create a new stub function for "
115 << TargetName
.data() << "\n");
117 StubOffset
= Section
.getStubOffset();
118 Stubs
[OriginalRelValueRef
] = StubOffset
;
119 createStubFunction(Section
.getAddressWithOffset(StubOffset
));
120 Section
.advanceStubOffset(getMaxStubSize());
122 LLVM_DEBUG(dbgs() << " Stub function found for " << TargetName
.data()
124 StubOffset
= Stub
->second
;
127 // Resolve original relocation to stub function.
128 const RelocationEntry
RE(SectionID
, Offset
, RelType
, Addend
);
129 resolveRelocation(RE
, Section
.getLoadAddressWithOffset(StubOffset
));
131 // adjust relocation info so resolution writes to the stub function
132 // Here an internal relocation type is used for resolving long branch via
136 RelType
= INTERNAL_REL_ARM64_LONG_BRANCH26
;
138 return std::make_tuple(Offset
, RelType
, Addend
);
141 Expected
<object::relocation_iterator
>
142 processRelocationRef(unsigned SectionID
, object::relocation_iterator RelI
,
143 const object::ObjectFile
&Obj
,
144 ObjSectionToIDMap
&ObjSectionToID
,
145 StubMap
&Stubs
) override
{
147 auto Symbol
= RelI
->getSymbol();
148 if (Symbol
== Obj
.symbol_end())
149 report_fatal_error("Unknown symbol in relocation");
151 Expected
<StringRef
> TargetNameOrErr
= Symbol
->getName();
152 if (!TargetNameOrErr
)
153 return TargetNameOrErr
.takeError();
154 StringRef TargetName
= *TargetNameOrErr
;
156 auto SectionOrErr
= Symbol
->getSection();
158 return SectionOrErr
.takeError();
159 auto Section
= *SectionOrErr
;
161 uint64_t RelType
= RelI
->getType();
162 uint64_t Offset
= RelI
->getOffset();
164 // If there is no section, this must be an external reference.
165 bool IsExtern
= Section
== Obj
.section_end();
167 // Determine the Addend used to adjust the relocation value.
169 SectionEntry
&AddendSection
= Sections
[SectionID
];
170 uintptr_t ObjTarget
= AddendSection
.getObjAddress() + Offset
;
171 uint8_t *Displacement
= (uint8_t *)ObjTarget
;
173 unsigned TargetSectionID
= -1;
174 uint64_t TargetOffset
= -1;
176 if (TargetName
.startswith(getImportSymbolPrefix())) {
177 TargetSectionID
= SectionID
;
178 TargetOffset
= getDLLImportOffset(SectionID
, Stubs
, TargetName
);
179 TargetName
= StringRef();
181 } else if (!IsExtern
) {
182 if (auto TargetSectionIDOrErr
= findOrEmitSection(
183 Obj
, *Section
, Section
->isText(), ObjSectionToID
))
184 TargetSectionID
= *TargetSectionIDOrErr
;
186 return TargetSectionIDOrErr
.takeError();
188 TargetOffset
= getSymbolOffset(*Symbol
);
192 case COFF::IMAGE_REL_ARM64_ADDR32
:
193 case COFF::IMAGE_REL_ARM64_ADDR32NB
:
194 case COFF::IMAGE_REL_ARM64_REL32
:
195 case COFF::IMAGE_REL_ARM64_SECREL
:
196 Addend
= read32le(Displacement
);
198 case COFF::IMAGE_REL_ARM64_BRANCH26
: {
199 uint32_t orig
= read32le(Displacement
);
200 Addend
= (orig
& 0x03FFFFFF) << 2;
203 std::tie(Offset
, RelType
, Addend
) = generateRelocationStub(
204 SectionID
, TargetName
, Offset
, RelType
, Addend
, Stubs
);
207 case COFF::IMAGE_REL_ARM64_BRANCH19
: {
208 uint32_t orig
= read32le(Displacement
);
209 Addend
= (orig
& 0x00FFFFE0) >> 3;
212 case COFF::IMAGE_REL_ARM64_BRANCH14
: {
213 uint32_t orig
= read32le(Displacement
);
214 Addend
= (orig
& 0x000FFFE0) >> 3;
217 case COFF::IMAGE_REL_ARM64_REL21
:
218 case COFF::IMAGE_REL_ARM64_PAGEBASE_REL21
: {
219 uint32_t orig
= read32le(Displacement
);
220 Addend
= ((orig
>> 29) & 0x3) | ((orig
>> 3) & 0x1FFFFC);
223 case COFF::IMAGE_REL_ARM64_PAGEOFFSET_12L
:
224 case COFF::IMAGE_REL_ARM64_PAGEOFFSET_12A
: {
225 uint32_t orig
= read32le(Displacement
);
226 Addend
= ((orig
>> 10) & 0xFFF);
229 case COFF::IMAGE_REL_ARM64_ADDR64
: {
230 Addend
= read64le(Displacement
);
238 SmallString
<32> RelTypeName
;
239 RelI
->getTypeName(RelTypeName
);
241 LLVM_DEBUG(dbgs() << "\t\tIn Section " << SectionID
<< " Offset " << Offset
242 << " RelType: " << RelTypeName
<< " TargetName: "
243 << TargetName
<< " Addend " << Addend
<< "\n");
247 RelocationEntry
RE(SectionID
, Offset
, RelType
, Addend
);
248 addRelocationForSymbol(RE
, TargetName
);
250 RelocationEntry
RE(SectionID
, Offset
, RelType
, TargetOffset
+ Addend
);
251 addRelocationForSection(RE
, TargetSectionID
);
256 void resolveRelocation(const RelocationEntry
&RE
, uint64_t Value
) override
{
257 const auto Section
= Sections
[RE
.SectionID
];
258 uint8_t *Target
= Section
.getAddressWithOffset(RE
.Offset
);
259 uint64_t FinalAddress
= Section
.getLoadAddressWithOffset(RE
.Offset
);
261 switch (RE
.RelType
) {
263 llvm_unreachable("unsupported relocation type");
264 case COFF::IMAGE_REL_ARM64_ABSOLUTE
: {
265 // This relocation is ignored.
268 case COFF::IMAGE_REL_ARM64_PAGEBASE_REL21
: {
269 // The page base of the target, for ADRP instruction.
271 write32AArch64Addr(Target
, Value
, FinalAddress
, 12);
274 case COFF::IMAGE_REL_ARM64_REL21
: {
275 // The 12-bit relative displacement to the target, for instruction ADR
277 write32AArch64Addr(Target
, Value
, FinalAddress
, 0);
280 case COFF::IMAGE_REL_ARM64_PAGEOFFSET_12A
: {
281 // The 12-bit page offset of the target,
282 // for instructions ADD/ADDS (immediate) with zero shift.
284 write32AArch64Imm(Target
, Value
& 0xFFF, 0);
287 case COFF::IMAGE_REL_ARM64_PAGEOFFSET_12L
: {
288 // The 12-bit page offset of the target,
289 // for instruction LDR (indexed, unsigned immediate).
291 write32AArch64Ldr(Target
, Value
& 0xFFF);
294 case COFF::IMAGE_REL_ARM64_ADDR32
: {
295 // The 32-bit VA of the target.
296 uint32_t VA
= Value
+ RE
.Addend
;
297 write32le(Target
, VA
);
300 case COFF::IMAGE_REL_ARM64_ADDR32NB
: {
301 // The target's 32-bit RVA.
302 uint64_t RVA
= Value
+ RE
.Addend
- getImageBase();
303 write32le(Target
, RVA
);
306 case INTERNAL_REL_ARM64_LONG_BRANCH26
: {
307 // Encode the immadiate value for generated Stub instruction (MOVZ)
308 or32le(Target
+ 12, ((Value
+ RE
.Addend
) & 0xFFFF) << 5);
309 or32le(Target
+ 8, ((Value
+ RE
.Addend
) & 0xFFFF0000) >> 11);
310 or32le(Target
+ 4, ((Value
+ RE
.Addend
) & 0xFFFF00000000) >> 27);
311 or32le(Target
+ 0, ((Value
+ RE
.Addend
) & 0xFFFF000000000000) >> 43);
314 case COFF::IMAGE_REL_ARM64_BRANCH26
: {
315 // The 26-bit relative displacement to the target, for B and BL
317 uint64_t PCRelVal
= Value
+ RE
.Addend
- FinalAddress
;
318 assert(isInt
<28>(PCRelVal
) && "Branch target is out of range.");
319 write32le(Target
, (read32le(Target
) & ~(0x03FFFFFF)) |
320 (PCRelVal
& 0x0FFFFFFC) >> 2);
323 case COFF::IMAGE_REL_ARM64_BRANCH19
: {
324 // The 19-bit offset to the relocation target,
325 // for conditional B instruction.
326 uint64_t PCRelVal
= Value
+ RE
.Addend
- FinalAddress
;
327 assert(isInt
<21>(PCRelVal
) && "Branch target is out of range.");
328 write32le(Target
, (read32le(Target
) & ~(0x00FFFFE0)) |
329 (PCRelVal
& 0x001FFFFC) << 3);
332 case COFF::IMAGE_REL_ARM64_BRANCH14
: {
333 // The 14-bit offset to the relocation target,
334 // for instructions TBZ and TBNZ.
335 uint64_t PCRelVal
= Value
+ RE
.Addend
- FinalAddress
;
336 assert(isInt
<16>(PCRelVal
) && "Branch target is out of range.");
337 write32le(Target
, (read32le(Target
) & ~(0x000FFFE0)) |
338 (PCRelVal
& 0x0000FFFC) << 3);
341 case COFF::IMAGE_REL_ARM64_ADDR64
: {
342 // The 64-bit VA of the relocation target.
343 write64le(Target
, Value
+ RE
.Addend
);
346 case COFF::IMAGE_REL_ARM64_SECTION
: {
347 // 16-bit section index of the section that contains the target.
348 assert(static_cast<uint32_t>(RE
.SectionID
) <= UINT16_MAX
&&
349 "relocation overflow");
350 add16(Target
, RE
.SectionID
);
353 case COFF::IMAGE_REL_ARM64_SECREL
: {
354 // 32-bit offset of the target from the beginning of its section.
355 assert(static_cast<int64_t>(RE
.Addend
) <= INT32_MAX
&&
356 "Relocation overflow");
357 assert(static_cast<int64_t>(RE
.Addend
) >= INT32_MIN
&&
358 "Relocation underflow");
359 write32le(Target
, RE
.Addend
);
362 case COFF::IMAGE_REL_ARM64_REL32
: {
363 // The 32-bit relative address from the byte following the relocation.
364 uint64_t Result
= Value
- FinalAddress
- 4;
365 write32le(Target
, Result
+ RE
.Addend
);
371 void registerEHFrames() override
{}
374 } // End namespace llvm