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 | |
25 | using namespace llvm::support::endian; |
26 | |
27 | namespace llvm { |
28 | |
29 | // This relocation type is used for handling long branch instruction |
30 | // through the Stub. |
31 | enum InternalRelocationType : unsigned { |
32 | INTERNAL_REL_ARM64_LONG_BRANCH26 = 0x111, |
33 | }; |
34 | |
35 | static void add16(uint8_t *p, int16_t v) { write16le(P: p, V: read16le(P: p) + v); } |
36 | static void or32le(void *P, int32_t V) { write32le(P, V: read32le(P) | V); } |
37 | |
38 | static 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 | |
44 | static 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 | |
56 | static 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 | |
64 | class RuntimeDyldCOFFAArch64 : public RuntimeDyldCOFF { |
65 | |
66 | private: |
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 | |
90 | public: |
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 | |