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