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/BinaryFormat/COFF.h"
18#include "llvm/Object/COFF.h"
19
20#define DEBUG_TYPE "dyld"
21
22namespace llvm {
23
24static bool isThumbFunc(object::symbol_iterator Symbol,
25                        const object::ObjectFile &Obj,
26                        object::section_iterator Section) {
27  Expected<object::SymbolRef::Type> SymTypeOrErr = Symbol->getType();
28  if (!SymTypeOrErr) {
29    std::string Buf;
30    raw_string_ostream OS(Buf);
31    logAllUnhandledErrors(SymTypeOrErr.takeError(), OS);
32    OS.flush();
33    report_fatal_error(Buf);
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) {}
52
53  unsigned getMaxStubSize() const override {
54    return 16; // 8-byte load instructions, 4-byte jump, 4-byte padding
55  }
56
57  unsigned getStubAlignment() override { return 1; }
58
59  Expected<object::relocation_iterator>
60  processRelocationRef(unsigned SectionID,
61                       object::relocation_iterator RelI,
62                       const object::ObjectFile &Obj,
63                       ObjSectionToIDMap &ObjSectionToID,
64                       StubMap &Stubs) override {
65    auto Symbol = RelI->getSymbol();
66    if (Symbol == Obj.symbol_end())
67      report_fatal_error("Unknown symbol in relocation");
68
69    Expected<StringRef> TargetNameOrErr = Symbol->getName();
70    if (!TargetNameOrErr)
71      return TargetNameOrErr.takeError();
72    StringRef TargetName = *TargetNameOrErr;
73
74    auto SectionOrErr = Symbol->getSection();
75    if (!SectionOrErr)
76      return SectionOrErr.takeError();
77    auto Section = *SectionOrErr;
78
79    uint64_t RelType = RelI->getType();
80    uint64_t Offset = RelI->getOffset();
81
82    // Determine the Addend used to adjust the relocation value.
83    uint64_t Addend = 0;
84    SectionEntry &AddendSection = Sections[SectionID];
85    uintptr_t ObjTarget = AddendSection.getObjAddress() + Offset;
86    uint8_t *Displacement = (uint8_t *)ObjTarget;
87
88    switch (RelType) {
89    case COFF::IMAGE_REL_ARM_ADDR32:
90    case COFF::IMAGE_REL_ARM_ADDR32NB:
91    case COFF::IMAGE_REL_ARM_SECREL:
92      Addend = readBytesUnaligned(Displacement, 4);
93      break;
94    default:
95      break;
96    }
97
98#if !defined(NDEBUG)
99    SmallString<32> RelTypeName;
100    RelI->getTypeName(RelTypeName);
101#endif
102    LLVM_DEBUG(dbgs() << "\t\tIn Section " << SectionID << " Offset " << Offset
103                      << " RelType: " << RelTypeName << " TargetName: "
104                      << TargetName << " Addend " << Addend << "\n");
105
106    unsigned TargetSectionID = -1;
107    if (Section == Obj.section_end()) {
108      RelocationEntry RE(SectionID, Offset, RelType, 0, -1, 0, 0, 0, false, 0);
109      addRelocationForSymbol(RE, TargetName);
110    } else {
111      if (auto TargetSectionIDOrErr =
112          findOrEmitSection(Obj, *Section, Section->isText(), ObjSectionToID))
113        TargetSectionID = *TargetSectionIDOrErr;
114      else
115        return TargetSectionIDOrErr.takeError();
116
117      // We need to find out if the relocation is relative to a thumb function
118      // so that we include the ISA selection bit when resolve the relocation
119      bool IsTargetThumbFunc = isThumbFunc(Symbol, Obj, Section);
120
121      switch (RelType) {
122      default: llvm_unreachable("unsupported relocation type");
123      case COFF::IMAGE_REL_ARM_ABSOLUTE:
124        // This relocation is ignored.
125        break;
126      case COFF::IMAGE_REL_ARM_ADDR32: {
127        RelocationEntry RE = RelocationEntry(
128            SectionID, Offset, RelType, Addend, TargetSectionID,
129            getSymbolOffset(*Symbol), 0, 0, false, 0, IsTargetThumbFunc);
130        addRelocationForSection(RE, TargetSectionID);
131        break;
132      }
133      case COFF::IMAGE_REL_ARM_ADDR32NB: {
134        RelocationEntry RE =
135            RelocationEntry(SectionID, Offset, RelType, Addend, TargetSectionID,
136                            getSymbolOffset(*Symbol), 0, 0, false, 0);
137        addRelocationForSection(RE, TargetSectionID);
138        break;
139      }
140      case COFF::IMAGE_REL_ARM_SECTION: {
141        RelocationEntry RE =
142            RelocationEntry(TargetSectionID, Offset, RelType, 0);
143        addRelocationForSection(RE, TargetSectionID);
144        break;
145      }
146      case COFF::IMAGE_REL_ARM_SECREL: {
147        RelocationEntry RE = RelocationEntry(SectionID, Offset, RelType,
148                                             getSymbolOffset(*Symbol) + Addend);
149        addRelocationForSection(RE, TargetSectionID);
150        break;
151      }
152      case COFF::IMAGE_REL_ARM_MOV32T: {
153        RelocationEntry RE = RelocationEntry(
154            SectionID, Offset, RelType, Addend, TargetSectionID,
155            getSymbolOffset(*Symbol), 0, 0, false, 0, IsTargetThumbFunc);
156        addRelocationForSection(RE, TargetSectionID);
157        break;
158      }
159      case COFF::IMAGE_REL_ARM_BRANCH20T:
160      case COFF::IMAGE_REL_ARM_BRANCH24T:
161      case COFF::IMAGE_REL_ARM_BLX23T: {
162        RelocationEntry RE =
163            RelocationEntry(SectionID, Offset, RelType,
164                            getSymbolOffset(*Symbol) + Addend, true, 0);
165        addRelocationForSection(RE, TargetSectionID);
166        break;
167      }
168      }
169    }
170
171    return ++RelI;
172  }
173
174  void resolveRelocation(const RelocationEntry &RE, uint64_t Value) override {
175    const auto Section = Sections[RE.SectionID];
176    uint8_t *Target = Section.getAddressWithOffset(RE.Offset);
177    int ISASelectionBit = RE.IsTargetThumbFunc ? 1 : 0;
178
179    switch (RE.RelType) {
180    default: llvm_unreachable("unsupported relocation type");
181    case COFF::IMAGE_REL_ARM_ABSOLUTE:
182      // This relocation is ignored.
183      break;
184    case COFF::IMAGE_REL_ARM_ADDR32: {
185      // The target's 32-bit VA.
186      uint64_t Result =
187          RE.Sections.SectionA == static_cast<uint32_t>(-1)
188              ? Value
189              : Sections[RE.Sections.SectionA].getLoadAddressWithOffset(RE.Addend);
190      Result |= ISASelectionBit;
191      assert(Result <= UINT32_MAX && "relocation overflow");
192      LLVM_DEBUG(dbgs() << "\t\tOffset: " << RE.Offset
193                        << " RelType: IMAGE_REL_ARM_ADDR32"
194                        << " TargetSection: " << RE.Sections.SectionA
195                        << " Value: " << format("0x%08" PRIx32, Result)
196                        << '\n');
197      writeBytesUnaligned(Result, Target, 4);
198      break;
199    }
200    case COFF::IMAGE_REL_ARM_ADDR32NB: {
201      // The target's 32-bit RVA.
202      // NOTE: use Section[0].getLoadAddress() as an approximation of ImageBase
203      uint64_t Result = Sections[RE.Sections.SectionA].getLoadAddress() -
204                        Sections[0].getLoadAddress() + RE.Addend;
205      assert(Result <= UINT32_MAX && "relocation overflow");
206      LLVM_DEBUG(dbgs() << "\t\tOffset: " << RE.Offset
207                        << " RelType: IMAGE_REL_ARM_ADDR32NB"
208                        << " TargetSection: " << RE.Sections.SectionA
209                        << " Value: " << format("0x%08" PRIx32, Result)
210                        << '\n');
211      Result |= ISASelectionBit;
212      writeBytesUnaligned(Result, Target, 4);
213      break;
214    }
215    case COFF::IMAGE_REL_ARM_SECTION:
216      // 16-bit section index of the section that contains the target.
217      assert(static_cast<uint32_t>(RE.SectionID) <= UINT16_MAX &&
218             "relocation overflow");
219      LLVM_DEBUG(dbgs() << "\t\tOffset: " << RE.Offset
220                        << " RelType: IMAGE_REL_ARM_SECTION Value: "
221                        << RE.SectionID << '\n');
222      writeBytesUnaligned(RE.SectionID, Target, 2);
223      break;
224    case COFF::IMAGE_REL_ARM_SECREL:
225      // 32-bit offset of the target from the beginning of its section.
226      assert(static_cast<uint64_t>(RE.Addend) <= UINT32_MAX &&
227             "relocation overflow");
228      LLVM_DEBUG(dbgs() << "\t\tOffset: " << RE.Offset
229                        << " RelType: IMAGE_REL_ARM_SECREL Value: " << RE.Addend
230                        << '\n');
231      writeBytesUnaligned(RE.Addend, Target, 2);
232      break;
233    case COFF::IMAGE_REL_ARM_MOV32T: {
234      // 32-bit VA of the target applied to a contiguous MOVW+MOVT pair.
235      uint64_t Result =
236          Sections[RE.Sections.SectionA].getLoadAddressWithOffset(RE.Addend);
237      assert(Result <= UINT32_MAX && "relocation overflow");
238      LLVM_DEBUG(dbgs() << "\t\tOffset: " << RE.Offset
239                        << " RelType: IMAGE_REL_ARM_MOV32T"
240                        << " TargetSection: " << RE.Sections.SectionA
241                        << " Value: " << format("0x%08" PRIx32, Result)
242                        << '\n');
243
244      // MOVW(T3): |11110|i|10|0|1|0|0|imm4|0|imm3|Rd|imm8|
245      //            imm32 = zext imm4:i:imm3:imm8
246      // MOVT(T1): |11110|i|10|1|1|0|0|imm4|0|imm3|Rd|imm8|
247      //            imm16 =      imm4:i:imm3:imm8
248
249      auto EncodeImmediate = [](uint8_t *Bytes, uint16_t Immediate)  {
250        Bytes[0] |= ((Immediate & 0xf000) >> 12);
251        Bytes[1] |= ((Immediate & 0x0800) >> 11);
252        Bytes[2] |= ((Immediate & 0x00ff) >>  0);
253        Bytes[3] |= (((Immediate & 0x0700) >>  8) << 4);
254      };
255
256      EncodeImmediate(&Target[0],
257                      (static_cast<uint32_t>(Result) >> 00) | ISASelectionBit);
258      EncodeImmediate(&Target[4], static_cast<uint32_t>(Result) >> 16);
259
260      break;
261    }
262    case COFF::IMAGE_REL_ARM_BRANCH20T: {
263      // The most significant 20-bits of the signed 21-bit relative displacement
264      uint64_t Value =
265          RE.Addend - (Sections[RE.SectionID].getLoadAddress() + RE.Offset) - 4;
266      assert(static_cast<int64_t>(RE.Addend) <= INT32_MAX &&
267             "relocation overflow");
268      assert(static_cast<int64_t>(RE.Addend) >= INT32_MIN &&
269             "relocation underflow");
270      LLVM_DEBUG(dbgs() << "\t\tOffset: " << RE.Offset
271                        << " RelType: IMAGE_REL_ARM_BRANCH20T"
272                        << " Value: " << static_cast<int32_t>(Value) << '\n');
273      static_cast<void>(Value);
274      llvm_unreachable("unimplemented relocation");
275      break;
276    }
277    case COFF::IMAGE_REL_ARM_BRANCH24T: {
278      // The most significant 24-bits of the signed 25-bit relative displacement
279      uint64_t Value =
280          RE.Addend - (Sections[RE.SectionID].getLoadAddress() + RE.Offset) - 4;
281      assert(static_cast<int64_t>(RE.Addend) <= INT32_MAX &&
282             "relocation overflow");
283      assert(static_cast<int64_t>(RE.Addend) >= INT32_MIN &&
284             "relocation underflow");
285      LLVM_DEBUG(dbgs() << "\t\tOffset: " << RE.Offset
286                        << " RelType: IMAGE_REL_ARM_BRANCH24T"
287                        << " Value: " << static_cast<int32_t>(Value) << '\n');
288      static_cast<void>(Value);
289      llvm_unreachable("unimplemented relocation");
290      break;
291    }
292    case COFF::IMAGE_REL_ARM_BLX23T: {
293      // The most significant 24-bits of the signed 25-bit relative displacement
294      uint64_t Value =
295          RE.Addend - (Sections[RE.SectionID].getLoadAddress() + RE.Offset) - 4;
296      assert(static_cast<int64_t>(RE.Addend) <= INT32_MAX &&
297             "relocation overflow");
298      assert(static_cast<int64_t>(RE.Addend) >= INT32_MIN &&
299             "relocation underflow");
300      LLVM_DEBUG(dbgs() << "\t\tOffset: " << RE.Offset
301                        << " RelType: IMAGE_REL_ARM_BLX23T"
302                        << " Value: " << static_cast<int32_t>(Value) << '\n');
303      static_cast<void>(Value);
304      llvm_unreachable("unimplemented relocation");
305      break;
306    }
307    }
308  }
309
310  void registerEHFrames() override {}
311};
312
313}
314
315#endif
316