1//===-- RuntimeDyldCOFFAArch64.h --- COFF/AArch64 specific code ---*- C++
2//-*-===//
3//
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
7//
8//===----------------------------------------------------------------------===//
9//
10// COFF AArch64 support for MC-JIT runtime dynamic linker.
11//
12//===----------------------------------------------------------------------===//
13
14#ifndef LLVM_LIB_EXECUTIONENGINE_RUNTIMEDYLD_TARGETS_RUNTIMEDYLDCOFFAARCH64_H
15#define LLVM_LIB_EXECUTIONENGINE_RUNTIMEDYLD_TARGETS_RUNTIMEDYLDCOFFAARCH64_H
16
17#include "../RuntimeDyldCOFF.h"
18#include "llvm/ADT/SmallString.h"
19#include "llvm/BinaryFormat/COFF.h"
20#include "llvm/Object/COFF.h"
21#include "llvm/Support/Endian.h"
22
23#define DEBUG_TYPE "dyld"
24
25using namespace llvm::support::endian;
26
27namespace llvm {
28
29// This relocation type is used for handling long branch instruction
30// through the Stub.
31enum InternalRelocationType : unsigned {
32 INTERNAL_REL_ARM64_LONG_BRANCH26 = 0x111,
33};
34
35static void add16(uint8_t *p, int16_t v) { write16le(P: p, V: read16le(P: p) + v); }
36static void or32le(void *P, int32_t V) { write32le(P, V: read32le(P) | V); }
37
38static void write32AArch64Imm(uint8_t *T, uint64_t imm, uint32_t rangeLimit) {
39 uint32_t orig = read32le(P: T);
40 orig &= ~(0xFFF << 10);
41 write32le(P: T, V: orig | ((imm & (0xFFF >> rangeLimit)) << 10));
42}
43
44static void write32AArch64Ldr(uint8_t *T, uint64_t imm) {
45 uint32_t orig = read32le(P: T);
46 uint32_t size = orig >> 30;
47 // 0x04000000 indicates SIMD/FP registers
48 // 0x00800000 indicates 128 bit
49 if ((orig & 0x04800000) == 0x04800000)
50 size += 4;
51 if ((imm & ((1 << size) - 1)) != 0)
52 assert(0 && "misaligned ldr/str offset");
53 write32AArch64Imm(T, imm: imm >> size, rangeLimit: size);
54}
55
56static void write32AArch64Addr(void *T, uint64_t s, uint64_t p, int shift) {
57 uint64_t Imm = (s >> shift) - (p >> shift);
58 uint32_t ImmLo = (Imm & 0x3) << 29;
59 uint32_t ImmHi = (Imm & 0x1FFFFC) << 3;
60 uint64_t Mask = (0x3 << 29) | (0x1FFFFC << 3);
61 write32le(P: T, V: (read32le(P: T) & ~Mask) | ImmLo | ImmHi);
62}
63
64class RuntimeDyldCOFFAArch64 : public RuntimeDyldCOFF {
65
66private:
67 // When a module is loaded we save the SectionID of the unwind
68 // sections in a table until we receive a request to register all
69 // unregisteredEH frame sections with the memory manager.
70 SmallVector<SID, 2> UnregisteredEHFrameSections;
71 SmallVector<SID, 2> RegisteredEHFrameSections;
72 uint64_t ImageBase;
73
74 // Fake an __ImageBase pointer by returning the section with the lowest adress
75 uint64_t getImageBase() {
76 if (!ImageBase) {
77 ImageBase = std::numeric_limits<uint64_t>::max();
78 for (const SectionEntry &Section : Sections)
79 // The Sections list may contain sections that weren't loaded for
80 // whatever reason: they may be debug sections, and ProcessAllSections
81 // is false, or they may be sections that contain 0 bytes. If the
82 // section isn't loaded, the load address will be 0, and it should not
83 // be included in the ImageBase calculation.
84 if (Section.getLoadAddress() != 0)
85 ImageBase = std::min(a: ImageBase, b: Section.getLoadAddress());
86 }
87 return ImageBase;
88 }
89
90public:
91 RuntimeDyldCOFFAArch64(RuntimeDyld::MemoryManager &MM,
92 JITSymbolResolver &Resolver)
93 : RuntimeDyldCOFF(MM, Resolver, 8, COFF::IMAGE_REL_ARM64_ADDR64),
94 ImageBase(0) {}
95
96 Align getStubAlignment() override { return Align(8); }
97
98 unsigned getMaxStubSize() const override { return 20; }
99
100 std::tuple<uint64_t, uint64_t, uint64_t>
101 generateRelocationStub(unsigned SectionID, StringRef TargetName,
102 uint64_t Offset, uint64_t RelType, uint64_t Addend,
103 StubMap &Stubs) {
104 uintptr_t StubOffset;
105 SectionEntry &Section = Sections[SectionID];
106
107 RelocationValueRef OriginalRelValueRef;
108 OriginalRelValueRef.SectionID = SectionID;
109 OriginalRelValueRef.Offset = Offset;
110 OriginalRelValueRef.Addend = Addend;
111 OriginalRelValueRef.SymbolName = TargetName.data();
112
113 auto Stub = Stubs.find(x: OriginalRelValueRef);
114 if (Stub == Stubs.end()) {
115 LLVM_DEBUG(dbgs() << " Create a new stub function for "
116 << TargetName.data() << "\n");
117
118 StubOffset = Section.getStubOffset();
119 Stubs[OriginalRelValueRef] = StubOffset;
120 createStubFunction(Addr: Section.getAddressWithOffset(OffsetBytes: StubOffset));
121 Section.advanceStubOffset(StubSize: getMaxStubSize());
122 } else {
123 LLVM_DEBUG(dbgs() << " Stub function found for " << TargetName.data()
124 << "\n");
125 StubOffset = Stub->second;
126 }
127
128 // Resolve original relocation to stub function.
129 const RelocationEntry RE(SectionID, Offset, RelType, Addend);
130 resolveRelocation(RE, Value: Section.getLoadAddressWithOffset(OffsetBytes: StubOffset));
131
132 // adjust relocation info so resolution writes to the stub function
133 // Here an internal relocation type is used for resolving long branch via
134 // stub instruction.
135 Addend = 0;
136 Offset = StubOffset;
137 RelType = INTERNAL_REL_ARM64_LONG_BRANCH26;
138
139 return std::make_tuple(args&: Offset, args&: RelType, args&: Addend);
140 }
141
142 Expected<object::relocation_iterator>
143 processRelocationRef(unsigned SectionID, object::relocation_iterator RelI,
144 const object::ObjectFile &Obj,
145 ObjSectionToIDMap &ObjSectionToID,
146 StubMap &Stubs) override {
147
148 auto Symbol = RelI->getSymbol();
149 if (Symbol == Obj.symbol_end())
150 report_fatal_error(reason: "Unknown symbol in relocation");
151
152 Expected<StringRef> TargetNameOrErr = Symbol->getName();
153 if (!TargetNameOrErr)
154 return TargetNameOrErr.takeError();
155 StringRef TargetName = *TargetNameOrErr;
156
157 auto SectionOrErr = Symbol->getSection();
158 if (!SectionOrErr)
159 return SectionOrErr.takeError();
160 auto Section = *SectionOrErr;
161
162 uint64_t RelType = RelI->getType();
163 uint64_t Offset = RelI->getOffset();
164
165 // If there is no section, this must be an external reference.
166 bool IsExtern = Section == Obj.section_end();
167
168 // Determine the Addend used to adjust the relocation value.
169 uint64_t Addend = 0;
170 SectionEntry &AddendSection = Sections[SectionID];
171 uintptr_t ObjTarget = AddendSection.getObjAddress() + Offset;
172 uint8_t *Displacement = (uint8_t *)ObjTarget;
173
174 unsigned TargetSectionID = -1;
175 uint64_t TargetOffset = -1;
176
177 if (TargetName.starts_with(Prefix: getImportSymbolPrefix())) {
178 TargetSectionID = SectionID;
179 TargetOffset = getDLLImportOffset(SectionID, Stubs, Name: TargetName);
180 TargetName = StringRef();
181 IsExtern = false;
182 } else if (!IsExtern) {
183 if (auto TargetSectionIDOrErr = findOrEmitSection(
184 Obj, Section: *Section, IsCode: Section->isText(), LocalSections&: ObjSectionToID))
185 TargetSectionID = *TargetSectionIDOrErr;
186 else
187 return TargetSectionIDOrErr.takeError();
188
189 TargetOffset = getSymbolOffset(Sym: *Symbol);
190 }
191
192 switch (RelType) {
193 case COFF::IMAGE_REL_ARM64_ADDR32:
194 case COFF::IMAGE_REL_ARM64_ADDR32NB:
195 case COFF::IMAGE_REL_ARM64_REL32:
196 case COFF::IMAGE_REL_ARM64_SECREL:
197 Addend = read32le(P: Displacement);
198 break;
199 case COFF::IMAGE_REL_ARM64_BRANCH26: {
200 uint32_t orig = read32le(P: Displacement);
201 Addend = (orig & 0x03FFFFFF) << 2;
202
203 if (IsExtern)
204 std::tie(args&: Offset, args&: RelType, args&: Addend) = generateRelocationStub(
205 SectionID, TargetName, Offset, RelType, Addend, Stubs);
206 break;
207 }
208 case COFF::IMAGE_REL_ARM64_BRANCH19: {
209 uint32_t orig = read32le(P: Displacement);
210 Addend = (orig & 0x00FFFFE0) >> 3;
211 break;
212 }
213 case COFF::IMAGE_REL_ARM64_BRANCH14: {
214 uint32_t orig = read32le(P: Displacement);
215 Addend = (orig & 0x000FFFE0) >> 3;
216 break;
217 }
218 case COFF::IMAGE_REL_ARM64_REL21:
219 case COFF::IMAGE_REL_ARM64_PAGEBASE_REL21: {
220 uint32_t orig = read32le(P: Displacement);
221 Addend = ((orig >> 29) & 0x3) | ((orig >> 3) & 0x1FFFFC);
222 break;
223 }
224 case COFF::IMAGE_REL_ARM64_PAGEOFFSET_12L:
225 case COFF::IMAGE_REL_ARM64_PAGEOFFSET_12A: {
226 uint32_t orig = read32le(P: Displacement);
227 Addend = ((orig >> 10) & 0xFFF);
228 break;
229 }
230 case COFF::IMAGE_REL_ARM64_ADDR64: {
231 Addend = read64le(P: Displacement);
232 break;
233 }
234 default:
235 break;
236 }
237
238#if !defined(NDEBUG)
239 SmallString<32> RelTypeName;
240 RelI->getTypeName(RelTypeName);
241
242 LLVM_DEBUG(dbgs() << "\t\tIn Section " << SectionID << " Offset " << Offset
243 << " RelType: " << RelTypeName << " TargetName: "
244 << TargetName << " Addend " << Addend << "\n");
245#endif
246
247 if (IsExtern) {
248 RelocationEntry RE(SectionID, Offset, RelType, Addend);
249 addRelocationForSymbol(RE, SymbolName: TargetName);
250 } else {
251 RelocationEntry RE(SectionID, Offset, RelType, TargetOffset + Addend);
252 addRelocationForSection(RE, SectionID: TargetSectionID);
253 }
254 return ++RelI;
255 }
256
257 void resolveRelocation(const RelocationEntry &RE, uint64_t Value) override {
258 const auto Section = Sections[RE.SectionID];
259 uint8_t *Target = Section.getAddressWithOffset(OffsetBytes: RE.Offset);
260 uint64_t FinalAddress = Section.getLoadAddressWithOffset(OffsetBytes: RE.Offset);
261
262 switch (RE.RelType) {
263 default:
264 llvm_unreachable("unsupported relocation type");
265 case COFF::IMAGE_REL_ARM64_ABSOLUTE: {
266 // This relocation is ignored.
267 break;
268 }
269 case COFF::IMAGE_REL_ARM64_PAGEBASE_REL21: {
270 // The page base of the target, for ADRP instruction.
271 Value += RE.Addend;
272 write32AArch64Addr(T: Target, s: Value, p: FinalAddress, shift: 12);
273 break;
274 }
275 case COFF::IMAGE_REL_ARM64_REL21: {
276 // The 12-bit relative displacement to the target, for instruction ADR
277 Value += RE.Addend;
278 write32AArch64Addr(T: Target, s: Value, p: FinalAddress, shift: 0);
279 break;
280 }
281 case COFF::IMAGE_REL_ARM64_PAGEOFFSET_12A: {
282 // The 12-bit page offset of the target,
283 // for instructions ADD/ADDS (immediate) with zero shift.
284 Value += RE.Addend;
285 write32AArch64Imm(T: Target, imm: Value & 0xFFF, rangeLimit: 0);
286 break;
287 }
288 case COFF::IMAGE_REL_ARM64_PAGEOFFSET_12L: {
289 // The 12-bit page offset of the target,
290 // for instruction LDR (indexed, unsigned immediate).
291 Value += RE.Addend;
292 write32AArch64Ldr(T: Target, imm: Value & 0xFFF);
293 break;
294 }
295 case COFF::IMAGE_REL_ARM64_ADDR32: {
296 // The 32-bit VA of the target.
297 uint32_t VA = Value + RE.Addend;
298 write32le(P: Target, V: VA);
299 break;
300 }
301 case COFF::IMAGE_REL_ARM64_ADDR32NB: {
302 // The target's 32-bit RVA.
303 uint64_t RVA = Value + RE.Addend - getImageBase();
304 write32le(P: Target, V: RVA);
305 break;
306 }
307 case INTERNAL_REL_ARM64_LONG_BRANCH26: {
308 // Encode the immadiate value for generated Stub instruction (MOVZ)
309 or32le(P: Target + 12, V: ((Value + RE.Addend) & 0xFFFF) << 5);
310 or32le(P: Target + 8, V: ((Value + RE.Addend) & 0xFFFF0000) >> 11);
311 or32le(P: Target + 4, V: ((Value + RE.Addend) & 0xFFFF00000000) >> 27);
312 or32le(P: Target + 0, V: ((Value + RE.Addend) & 0xFFFF000000000000) >> 43);
313 break;
314 }
315 case COFF::IMAGE_REL_ARM64_BRANCH26: {
316 // The 26-bit relative displacement to the target, for B and BL
317 // instructions.
318 uint64_t PCRelVal = Value + RE.Addend - FinalAddress;
319 assert(isInt<28>(PCRelVal) && "Branch target is out of range.");
320 write32le(P: Target, V: (read32le(P: Target) & ~(0x03FFFFFF)) |
321 (PCRelVal & 0x0FFFFFFC) >> 2);
322 break;
323 }
324 case COFF::IMAGE_REL_ARM64_BRANCH19: {
325 // The 19-bit offset to the relocation target,
326 // for conditional B instruction.
327 uint64_t PCRelVal = Value + RE.Addend - FinalAddress;
328 assert(isInt<21>(PCRelVal) && "Branch target is out of range.");
329 write32le(P: Target, V: (read32le(P: Target) & ~(0x00FFFFE0)) |
330 (PCRelVal & 0x001FFFFC) << 3);
331 break;
332 }
333 case COFF::IMAGE_REL_ARM64_BRANCH14: {
334 // The 14-bit offset to the relocation target,
335 // for instructions TBZ and TBNZ.
336 uint64_t PCRelVal = Value + RE.Addend - FinalAddress;
337 assert(isInt<16>(PCRelVal) && "Branch target is out of range.");
338 write32le(P: Target, V: (read32le(P: Target) & ~(0x000FFFE0)) |
339 (PCRelVal & 0x0000FFFC) << 3);
340 break;
341 }
342 case COFF::IMAGE_REL_ARM64_ADDR64: {
343 // The 64-bit VA of the relocation target.
344 write64le(P: Target, V: Value + RE.Addend);
345 break;
346 }
347 case COFF::IMAGE_REL_ARM64_SECTION: {
348 // 16-bit section index of the section that contains the target.
349 assert(static_cast<uint32_t>(RE.SectionID) <= UINT16_MAX &&
350 "relocation overflow");
351 add16(p: Target, v: RE.SectionID);
352 break;
353 }
354 case COFF::IMAGE_REL_ARM64_SECREL: {
355 // 32-bit offset of the target from the beginning of its section.
356 assert(static_cast<int64_t>(RE.Addend) <= INT32_MAX &&
357 "Relocation overflow");
358 assert(static_cast<int64_t>(RE.Addend) >= INT32_MIN &&
359 "Relocation underflow");
360 write32le(P: Target, V: RE.Addend);
361 break;
362 }
363 case COFF::IMAGE_REL_ARM64_REL32: {
364 // The 32-bit relative address from the byte following the relocation.
365 uint64_t Result = Value - FinalAddress - 4;
366 write32le(P: Target, V: Result + RE.Addend);
367 break;
368 }
369 }
370 }
371
372 void registerEHFrames() override {}
373};
374
375} // End namespace llvm
376
377#endif
378