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 // throught 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, read16le(p) + v); }
36 static void or32le(void *P, int32_t V) { write32le(P, read32le(P) | V); }
37 
38 static void write32AArch64Imm(uint8_t *T, uint64_t imm, uint32_t rangeLimit) {
39   uint32_t orig = read32le(T);
40   orig &= ~(0xFFF << 10);
41   write32le(T, orig | ((imm & (0xFFF >> rangeLimit)) << 10));
42 }
43 
44 static void write32AArch64Ldr(uint8_t *T, uint64_t imm) {
45   uint32_t orig = read32le(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 >> size, 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(T, (read32le(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(ImageBase, 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(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(Section.getAddressWithOffset(StubOffset));
121       Section.advanceStubOffset(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, Section.getLoadAddressWithOffset(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(Offset, RelType, 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("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.startswith(getImportSymbolPrefix())) {
178       TargetSectionID = SectionID;
179       TargetOffset = getDLLImportOffset(SectionID, Stubs, TargetName);
180       TargetName = StringRef();
181       IsExtern = false;
182     } else if (!IsExtern) {
183       if (auto TargetSectionIDOrErr = findOrEmitSection(
184               Obj, *Section, Section->isText(), ObjSectionToID))
185         TargetSectionID = *TargetSectionIDOrErr;
186       else
187         return TargetSectionIDOrErr.takeError();
188 
189       TargetOffset = getSymbolOffset(*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(Displacement);
198       break;
199     case COFF::IMAGE_REL_ARM64_BRANCH26: {
200       uint32_t orig = read32le(Displacement);
201       Addend = (orig & 0x03FFFFFF) << 2;
202 
203       if (IsExtern)
204         std::tie(Offset, RelType, Addend) = generateRelocationStub(
205             SectionID, TargetName, Offset, RelType, Addend, Stubs);
206       break;
207     }
208     case COFF::IMAGE_REL_ARM64_BRANCH19: {
209       uint32_t orig = read32le(Displacement);
210       Addend = (orig & 0x00FFFFE0) >> 3;
211       break;
212     }
213     case COFF::IMAGE_REL_ARM64_BRANCH14: {
214       uint32_t orig = read32le(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(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(Displacement);
227       Addend = ((orig >> 10) & 0xFFF);
228       break;
229     }
230     case COFF::IMAGE_REL_ARM64_ADDR64: {
231       Addend = read64le(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, TargetName);
250     } else {
251       RelocationEntry RE(SectionID, Offset, RelType, TargetOffset + Addend);
252       addRelocationForSection(RE, 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(RE.Offset);
260     uint64_t FinalAddress = Section.getLoadAddressWithOffset(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(Target, Value, FinalAddress, 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(Target, Value, FinalAddress, 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(Target, Value & 0xFFF, 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(Target, 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(Target, 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(Target, RVA);
305       break;
306     }
307     case INTERNAL_REL_ARM64_LONG_BRANCH26: {
308       // Encode the immadiate value for generated Stub instruction (MOVZ)
309       or32le(Target + 12, ((Value + RE.Addend) & 0xFFFF) << 5);
310       or32le(Target + 8, ((Value + RE.Addend) & 0xFFFF0000) >> 11);
311       or32le(Target + 4, ((Value + RE.Addend) & 0xFFFF00000000) >> 27);
312       or32le(Target + 0, ((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(Target, (read32le(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(Target, (read32le(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(Target, (read32le(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(Target, 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(Target, 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(Target, 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(Target, Result + RE.Addend);
367       break;
368     }
369     }
370   }
371 
372   void registerEHFrames() override {}
373 };
374 
375 } // End namespace llvm
376 
377 #endif
378