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 
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); }
36 
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 
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 
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
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:
90   RuntimeDyldCOFFAArch64(RuntimeDyld::MemoryManager &MM,
91                          JITSymbolResolver &Resolver)
92       : RuntimeDyldCOFF(MM, Resolver), ImageBase(0) {}
93 
94   unsigned getStubAlignment() override { return 8; }
95 
96   unsigned getMaxStubSize() const override { return 20; }
97 
98   std::tuple<uint64_t, uint64_t, uint64_t>
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>
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       break;
213     }
214     default:
215       break;
216     }
217 
218 #if !defined(NDEBUG)
219     SmallString<32> RelTypeName;
220     RelI->getTypeName(RelTypeName);
221 
222     LLVM_DEBUG(dbgs() << "\t\tIn Section " << SectionID << " Offset " << Offset
223                       << " RelType: " << RelTypeName << " TargetName: "
224                       << TargetName << " Addend " << Addend << "\n");
225 #endif
226 
227     unsigned TargetSectionID = -1;
228     if (IsExtern) {
229       RelocationEntry RE(SectionID, Offset, RelType, Addend);
230       addRelocationForSymbol(RE, TargetName);
231     } else {
232       if (auto TargetSectionIDOrErr = findOrEmitSection(
233               Obj, *Section, Section->isText(), ObjSectionToID)) {
234         TargetSectionID = *TargetSectionIDOrErr;
235       } else
236         return TargetSectionIDOrErr.takeError();
237 
238       uint64_t TargetOffset = getSymbolOffset(*Symbol);
239       RelocationEntry RE(SectionID, Offset, RelType, TargetOffset + Addend);
240       addRelocationForSection(RE, TargetSectionID);
241     }
242     return ++RelI;
243   }
244 
245   void resolveRelocation(const RelocationEntry &RE, uint64_t Value) override {
246     const auto Section = Sections[RE.SectionID];
247     uint8_t *Target = Section.getAddressWithOffset(RE.Offset);
248     uint64_t FinalAddress = Section.getLoadAddressWithOffset(RE.Offset);
249 
250     switch (RE.RelType) {
251     default:
252       llvm_unreachable("unsupported relocation type");
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 
360   void registerEHFrames() override {}
361 };
362 
363 } // End namespace llvm
364 
365 #endif
366