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