1//===--- RuntimeDyldCOFFThumb.h --- COFF/Thumb specific code ---*- C++ --*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// COFF thumb support for MC-JIT runtime dynamic linker.
10//
11//===----------------------------------------------------------------------===//
12
13#ifndef LLVM_LIB_EXECUTIONENGINE_RUNTIMEDYLD_TARGETS_RUNTIMEDYLDCOFFTHUMB_H
14#define LLVM_LIB_EXECUTIONENGINE_RUNTIMEDYLD_TARGETS_RUNTIMEDYLDCOFFTHUMB_H
15
16#include "../RuntimeDyldCOFF.h"
17#include "llvm/ADT/SmallString.h"
18#include "llvm/BinaryFormat/COFF.h"
19#include "llvm/Object/COFF.h"
20
21#define DEBUG_TYPE "dyld"
22
23namespace llvm {
24
25static bool isThumbFunc(object::symbol_iterator Symbol,
26                        const object::ObjectFile &Obj,
27                        object::section_iterator Section) {
28  Expected<object::SymbolRef::Type> SymTypeOrErr = Symbol->getType();
29  if (!SymTypeOrErr) {
30    std::string Buf;
31    raw_string_ostream OS(Buf);
32    logAllUnhandledErrors(SymTypeOrErr.takeError(), OS);
33    report_fatal_error(Twine(OS.str()));
34  }
35
36  if (*SymTypeOrErr != object::SymbolRef::ST_Function)
37    return false;
38
39  // We check the IMAGE_SCN_MEM_16BIT flag in the section of the symbol to tell
40  // if it's thumb or not
41  return cast<object::COFFObjectFile>(Obj)
42             .getCOFFSection(*Section)
43             ->Characteristics &
44         COFF::IMAGE_SCN_MEM_16BIT;
45}
46
47class RuntimeDyldCOFFThumb : public RuntimeDyldCOFF {
48public:
49  RuntimeDyldCOFFThumb(RuntimeDyld::MemoryManager &MM,
50                       JITSymbolResolver &Resolver)
51      : RuntimeDyldCOFF(MM, Resolver, 4, COFF::IMAGE_REL_ARM_ADDR32) {}
52
53  unsigned getMaxStubSize() const override {
54    return 16; // 8-byte load instructions, 4-byte jump, 4-byte padding
55  }
56
57  Expected<JITSymbolFlags> getJITSymbolFlags(const SymbolRef &SR) override {
58
59    auto Flags = RuntimeDyldImpl::getJITSymbolFlags(SR);
60
61    if (!Flags) {
62      return Flags.takeError();
63    }
64    auto SectionIterOrErr = SR.getSection();
65    if (!SectionIterOrErr) {
66      return SectionIterOrErr.takeError();
67    }
68    SectionRef Sec = *SectionIterOrErr.get();
69    const object::COFFObjectFile *COFFObjPtr =
70        cast<object::COFFObjectFile>(Sec.getObject());
71    const coff_section *CoffSec = COFFObjPtr->getCOFFSection(Sec);
72    bool isThumb = CoffSec->Characteristics & COFF::IMAGE_SCN_MEM_16BIT;
73
74    Flags->getTargetFlags() = isThumb;
75
76    return Flags;
77  }
78
79  Align getStubAlignment() override { return Align(1); }
80
81  Expected<object::relocation_iterator>
82  processRelocationRef(unsigned SectionID,
83                       object::relocation_iterator RelI,
84                       const object::ObjectFile &Obj,
85                       ObjSectionToIDMap &ObjSectionToID,
86                       StubMap &Stubs) override {
87    auto Symbol = RelI->getSymbol();
88    if (Symbol == Obj.symbol_end())
89      report_fatal_error("Unknown symbol in relocation");
90
91    Expected<StringRef> TargetNameOrErr = Symbol->getName();
92    if (!TargetNameOrErr)
93      return TargetNameOrErr.takeError();
94    StringRef TargetName = *TargetNameOrErr;
95
96    auto SectionOrErr = Symbol->getSection();
97    if (!SectionOrErr)
98      return SectionOrErr.takeError();
99    auto Section = *SectionOrErr;
100
101    uint64_t RelType = RelI->getType();
102    uint64_t Offset = RelI->getOffset();
103
104    // Determine the Addend used to adjust the relocation value.
105    uint64_t Addend = 0;
106    SectionEntry &AddendSection = Sections[SectionID];
107    uintptr_t ObjTarget = AddendSection.getObjAddress() + Offset;
108    uint8_t *Displacement = (uint8_t *)ObjTarget;
109
110    switch (RelType) {
111    case COFF::IMAGE_REL_ARM_ADDR32:
112    case COFF::IMAGE_REL_ARM_ADDR32NB:
113    case COFF::IMAGE_REL_ARM_SECREL:
114      Addend = readBytesUnaligned(Displacement, 4);
115      break;
116    default:
117      break;
118    }
119
120#if !defined(NDEBUG)
121    SmallString<32> RelTypeName;
122    RelI->getTypeName(RelTypeName);
123#endif
124    LLVM_DEBUG(dbgs() << "\t\tIn Section " << SectionID << " Offset " << Offset
125                      << " RelType: " << RelTypeName << " TargetName: "
126                      << TargetName << " Addend " << Addend << "\n");
127
128    bool IsExtern = Section == Obj.section_end();
129    unsigned TargetSectionID = -1;
130    uint64_t TargetOffset = -1;
131
132    if (TargetName.starts_with(getImportSymbolPrefix())) {
133      TargetSectionID = SectionID;
134      TargetOffset = getDLLImportOffset(SectionID, Stubs, TargetName, true);
135      TargetName = StringRef();
136      IsExtern = false;
137    } else if (!IsExtern) {
138      if (auto TargetSectionIDOrErr =
139          findOrEmitSection(Obj, *Section, Section->isText(), ObjSectionToID))
140        TargetSectionID = *TargetSectionIDOrErr;
141      else
142        return TargetSectionIDOrErr.takeError();
143      if (RelType != COFF::IMAGE_REL_ARM_SECTION)
144        TargetOffset = getSymbolOffset(*Symbol);
145    }
146
147    if (IsExtern) {
148      RelocationEntry RE(SectionID, Offset, RelType, 0, -1, 0, 0, 0, false, 0);
149      addRelocationForSymbol(RE, TargetName);
150    } else {
151
152      // We need to find out if the relocation is relative to a thumb function
153      // so that we include the ISA selection bit when resolve the relocation
154      bool IsTargetThumbFunc = isThumbFunc(Symbol, Obj, Section);
155
156      switch (RelType) {
157      default: llvm_unreachable("unsupported relocation type");
158      case COFF::IMAGE_REL_ARM_ABSOLUTE:
159        // This relocation is ignored.
160        break;
161      case COFF::IMAGE_REL_ARM_ADDR32: {
162        RelocationEntry RE =
163            RelocationEntry(SectionID, Offset, RelType, Addend, TargetSectionID,
164                            TargetOffset, 0, 0, false, 0, IsTargetThumbFunc);
165        addRelocationForSection(RE, TargetSectionID);
166        break;
167      }
168      case COFF::IMAGE_REL_ARM_ADDR32NB: {
169        RelocationEntry RE =
170            RelocationEntry(SectionID, Offset, RelType, Addend, TargetSectionID,
171                            TargetOffset, 0, 0, false, 0);
172        addRelocationForSection(RE, TargetSectionID);
173        break;
174      }
175      case COFF::IMAGE_REL_ARM_SECTION: {
176        RelocationEntry RE =
177            RelocationEntry(TargetSectionID, Offset, RelType, 0);
178        addRelocationForSection(RE, TargetSectionID);
179        break;
180      }
181      case COFF::IMAGE_REL_ARM_SECREL: {
182        RelocationEntry RE =
183            RelocationEntry(SectionID, Offset, RelType, TargetOffset + Addend);
184        addRelocationForSection(RE, TargetSectionID);
185        break;
186      }
187      case COFF::IMAGE_REL_ARM_MOV32T: {
188        RelocationEntry RE =
189            RelocationEntry(SectionID, Offset, RelType, Addend, TargetSectionID,
190                            TargetOffset, 0, 0, false, 0, IsTargetThumbFunc);
191        addRelocationForSection(RE, TargetSectionID);
192        break;
193      }
194      case COFF::IMAGE_REL_ARM_BRANCH20T:
195      case COFF::IMAGE_REL_ARM_BRANCH24T:
196      case COFF::IMAGE_REL_ARM_BLX23T: {
197        RelocationEntry RE = RelocationEntry(SectionID, Offset, RelType,
198                                             TargetOffset + Addend, true, 0);
199        addRelocationForSection(RE, TargetSectionID);
200        break;
201      }
202      }
203    }
204
205    return ++RelI;
206  }
207
208  void resolveRelocation(const RelocationEntry &RE, uint64_t Value) override {
209    const auto Section = Sections[RE.SectionID];
210    uint8_t *Target = Section.getAddressWithOffset(RE.Offset);
211    int ISASelectionBit = RE.IsTargetThumbFunc ? 1 : 0;
212
213    switch (RE.RelType) {
214    default: llvm_unreachable("unsupported relocation type");
215    case COFF::IMAGE_REL_ARM_ABSOLUTE:
216      // This relocation is ignored.
217      break;
218    case COFF::IMAGE_REL_ARM_ADDR32: {
219      // The target's 32-bit VA.
220      uint64_t Result =
221          RE.Sections.SectionA == static_cast<uint32_t>(-1)
222              ? Value
223              : Sections[RE.Sections.SectionA].getLoadAddressWithOffset(RE.Addend);
224      Result |= ISASelectionBit;
225      assert(Result <= UINT32_MAX && "relocation overflow");
226      LLVM_DEBUG(dbgs() << "\t\tOffset: " << RE.Offset
227                        << " RelType: IMAGE_REL_ARM_ADDR32"
228                        << " TargetSection: " << RE.Sections.SectionA
229                        << " Value: " << format("0x%08" PRIx32, Result)
230                        << '\n');
231      writeBytesUnaligned(Result, Target, 4);
232      break;
233    }
234    case COFF::IMAGE_REL_ARM_ADDR32NB: {
235      // The target's 32-bit RVA.
236      // NOTE: use Section[0].getLoadAddress() as an approximation of ImageBase
237      uint64_t Result = Sections[RE.Sections.SectionA].getLoadAddress() -
238                        Sections[0].getLoadAddress() + RE.Addend;
239      assert(Result <= UINT32_MAX && "relocation overflow");
240      LLVM_DEBUG(dbgs() << "\t\tOffset: " << RE.Offset
241                        << " RelType: IMAGE_REL_ARM_ADDR32NB"
242                        << " TargetSection: " << RE.Sections.SectionA
243                        << " Value: " << format("0x%08" PRIx32, Result)
244                        << '\n');
245      Result |= ISASelectionBit;
246      writeBytesUnaligned(Result, Target, 4);
247      break;
248    }
249    case COFF::IMAGE_REL_ARM_SECTION:
250      // 16-bit section index of the section that contains the target.
251      assert(static_cast<uint32_t>(RE.SectionID) <= UINT16_MAX &&
252             "relocation overflow");
253      LLVM_DEBUG(dbgs() << "\t\tOffset: " << RE.Offset
254                        << " RelType: IMAGE_REL_ARM_SECTION Value: "
255                        << RE.SectionID << '\n');
256      writeBytesUnaligned(RE.SectionID, Target, 2);
257      break;
258    case COFF::IMAGE_REL_ARM_SECREL:
259      // 32-bit offset of the target from the beginning of its section.
260      assert(static_cast<uint64_t>(RE.Addend) <= UINT32_MAX &&
261             "relocation overflow");
262      LLVM_DEBUG(dbgs() << "\t\tOffset: " << RE.Offset
263                        << " RelType: IMAGE_REL_ARM_SECREL Value: " << RE.Addend
264                        << '\n');
265      writeBytesUnaligned(RE.Addend, Target, 2);
266      break;
267    case COFF::IMAGE_REL_ARM_MOV32T: {
268      // 32-bit VA of the target applied to a contiguous MOVW+MOVT pair.
269      uint64_t Result =
270          Sections[RE.Sections.SectionA].getLoadAddressWithOffset(RE.Addend);
271      assert(Result <= UINT32_MAX && "relocation overflow");
272      LLVM_DEBUG(dbgs() << "\t\tOffset: " << RE.Offset
273                        << " RelType: IMAGE_REL_ARM_MOV32T"
274                        << " TargetSection: " << RE.Sections.SectionA
275                        << " Value: " << format("0x%08" PRIx32, Result)
276                        << '\n');
277
278      // MOVW(T3): |11110|i|10|0|1|0|0|imm4|0|imm3|Rd|imm8|
279      //            imm32 = zext imm4:i:imm3:imm8
280      // MOVT(T1): |11110|i|10|1|1|0|0|imm4|0|imm3|Rd|imm8|
281      //            imm16 =      imm4:i:imm3:imm8
282
283      auto EncodeImmediate = [](uint8_t *Bytes, uint16_t Immediate)  {
284        Bytes[0] |= ((Immediate & 0xf000) >> 12);
285        Bytes[1] |= ((Immediate & 0x0800) >> 11);
286        Bytes[2] |= ((Immediate & 0x00ff) >>  0);
287        Bytes[3] |= (((Immediate & 0x0700) >>  8) << 4);
288      };
289
290      EncodeImmediate(&Target[0],
291                      (static_cast<uint32_t>(Result) >> 00) | ISASelectionBit);
292      EncodeImmediate(&Target[4], static_cast<uint32_t>(Result) >> 16);
293      break;
294    }
295    case COFF::IMAGE_REL_ARM_BRANCH20T: {
296      // The most significant 20-bits of the signed 21-bit relative displacement
297      uint64_t Value =
298          RE.Addend - (Sections[RE.SectionID].getLoadAddress() + RE.Offset) - 4;
299      assert(static_cast<int64_t>(RE.Addend) <= INT32_MAX &&
300             "relocation overflow");
301      assert(static_cast<int64_t>(RE.Addend) >= INT32_MIN &&
302             "relocation underflow");
303      LLVM_DEBUG(dbgs() << "\t\tOffset: " << RE.Offset
304                        << " RelType: IMAGE_REL_ARM_BRANCH20T"
305                        << " Value: " << static_cast<int32_t>(Value) << '\n');
306      static_cast<void>(Value);
307      llvm_unreachable("unimplemented relocation");
308      break;
309    }
310    case COFF::IMAGE_REL_ARM_BRANCH24T: {
311      // The most significant 24-bits of the signed 25-bit relative displacement
312      uint64_t Value =
313          RE.Addend - (Sections[RE.SectionID].getLoadAddress() + RE.Offset) - 4;
314      assert(static_cast<int64_t>(RE.Addend) <= INT32_MAX &&
315             "relocation overflow");
316      assert(static_cast<int64_t>(RE.Addend) >= INT32_MIN &&
317             "relocation underflow");
318      LLVM_DEBUG(dbgs() << "\t\tOffset: " << RE.Offset
319                        << " RelType: IMAGE_REL_ARM_BRANCH24T"
320                        << " Value: " << static_cast<int32_t>(Value) << '\n');
321      static_cast<void>(Value);
322      llvm_unreachable("unimplemented relocation");
323      break;
324    }
325    case COFF::IMAGE_REL_ARM_BLX23T: {
326      // The most significant 24-bits of the signed 25-bit relative displacement
327      uint64_t Value =
328          RE.Addend - (Sections[RE.SectionID].getLoadAddress() + RE.Offset) - 4;
329      assert(static_cast<int64_t>(RE.Addend) <= INT32_MAX &&
330             "relocation overflow");
331      assert(static_cast<int64_t>(RE.Addend) >= INT32_MIN &&
332             "relocation underflow");
333      LLVM_DEBUG(dbgs() << "\t\tOffset: " << RE.Offset
334                        << " RelType: IMAGE_REL_ARM_BLX23T"
335                        << " Value: " << static_cast<int32_t>(Value) << '\n');
336      static_cast<void>(Value);
337      llvm_unreachable("unimplemented relocation");
338      break;
339    }
340    }
341  }
342
343  void registerEHFrames() override {}
344};
345
346}
347
348#endif
349