1303231Sdim//===--- RuntimeDyldCOFFThumb.h --- COFF/Thumb specific code ---*- C++ --*-===//
2303231Sdim//
3353358Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4353358Sdim// See https://llvm.org/LICENSE.txt for license information.
5353358Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6303231Sdim//
7303231Sdim//===----------------------------------------------------------------------===//
8303231Sdim//
9303231Sdim// COFF thumb support for MC-JIT runtime dynamic linker.
10303231Sdim//
11303231Sdim//===----------------------------------------------------------------------===//
12303231Sdim
13303231Sdim#ifndef LLVM_LIB_EXECUTIONENGINE_RUNTIMEDYLD_TARGETS_RUNTIMEDYLDCOFFTHUMB_H
14303231Sdim#define LLVM_LIB_EXECUTIONENGINE_RUNTIMEDYLD_TARGETS_RUNTIMEDYLDCOFFTHUMB_H
15303231Sdim
16321369Sdim#include "../RuntimeDyldCOFF.h"
17321369Sdim#include "llvm/BinaryFormat/COFF.h"
18303231Sdim#include "llvm/Object/COFF.h"
19303231Sdim
20303231Sdim#define DEBUG_TYPE "dyld"
21303231Sdim
22303231Sdimnamespace llvm {
23303231Sdim
24353358Sdimstatic bool isThumbFunc(object::symbol_iterator Symbol,
25353358Sdim                        const object::ObjectFile &Obj,
26353358Sdim                        object::section_iterator Section) {
27353358Sdim  Expected<object::SymbolRef::Type> SymTypeOrErr = Symbol->getType();
28314564Sdim  if (!SymTypeOrErr) {
29314564Sdim    std::string Buf;
30314564Sdim    raw_string_ostream OS(Buf);
31344779Sdim    logAllUnhandledErrors(SymTypeOrErr.takeError(), OS);
32314564Sdim    OS.flush();
33314564Sdim    report_fatal_error(Buf);
34314564Sdim  }
35314564Sdim
36353358Sdim  if (*SymTypeOrErr != object::SymbolRef::ST_Function)
37314564Sdim    return false;
38314564Sdim
39314564Sdim  // We check the IMAGE_SCN_MEM_16BIT flag in the section of the symbol to tell
40314564Sdim  // if it's thumb or not
41353358Sdim  return cast<object::COFFObjectFile>(Obj)
42353358Sdim             .getCOFFSection(*Section)
43353358Sdim             ->Characteristics &
44314564Sdim         COFF::IMAGE_SCN_MEM_16BIT;
45314564Sdim}
46314564Sdim
47303231Sdimclass RuntimeDyldCOFFThumb : public RuntimeDyldCOFF {
48303231Sdimpublic:
49303231Sdim  RuntimeDyldCOFFThumb(RuntimeDyld::MemoryManager &MM,
50314564Sdim                       JITSymbolResolver &Resolver)
51303231Sdim      : RuntimeDyldCOFF(MM, Resolver) {}
52303231Sdim
53353358Sdim  unsigned getMaxStubSize() const override {
54303231Sdim    return 16; // 8-byte load instructions, 4-byte jump, 4-byte padding
55303231Sdim  }
56303231Sdim
57303231Sdim  unsigned getStubAlignment() override { return 1; }
58303231Sdim
59353358Sdim  Expected<object::relocation_iterator>
60303231Sdim  processRelocationRef(unsigned SectionID,
61353358Sdim                       object::relocation_iterator RelI,
62353358Sdim                       const object::ObjectFile &Obj,
63303231Sdim                       ObjSectionToIDMap &ObjSectionToID,
64303231Sdim                       StubMap &Stubs) override {
65303231Sdim    auto Symbol = RelI->getSymbol();
66303231Sdim    if (Symbol == Obj.symbol_end())
67303231Sdim      report_fatal_error("Unknown symbol in relocation");
68303231Sdim
69303231Sdim    Expected<StringRef> TargetNameOrErr = Symbol->getName();
70303231Sdim    if (!TargetNameOrErr)
71303231Sdim      return TargetNameOrErr.takeError();
72303231Sdim    StringRef TargetName = *TargetNameOrErr;
73303231Sdim
74303231Sdim    auto SectionOrErr = Symbol->getSection();
75303231Sdim    if (!SectionOrErr)
76303231Sdim      return SectionOrErr.takeError();
77303231Sdim    auto Section = *SectionOrErr;
78303231Sdim
79303231Sdim    uint64_t RelType = RelI->getType();
80303231Sdim    uint64_t Offset = RelI->getOffset();
81303231Sdim
82303231Sdim    // Determine the Addend used to adjust the relocation value.
83303231Sdim    uint64_t Addend = 0;
84303231Sdim    SectionEntry &AddendSection = Sections[SectionID];
85303231Sdim    uintptr_t ObjTarget = AddendSection.getObjAddress() + Offset;
86303231Sdim    uint8_t *Displacement = (uint8_t *)ObjTarget;
87303231Sdim
88303231Sdim    switch (RelType) {
89303231Sdim    case COFF::IMAGE_REL_ARM_ADDR32:
90303231Sdim    case COFF::IMAGE_REL_ARM_ADDR32NB:
91303231Sdim    case COFF::IMAGE_REL_ARM_SECREL:
92303231Sdim      Addend = readBytesUnaligned(Displacement, 4);
93303231Sdim      break;
94303231Sdim    default:
95303231Sdim      break;
96303231Sdim    }
97303231Sdim
98303231Sdim#if !defined(NDEBUG)
99303231Sdim    SmallString<32> RelTypeName;
100303231Sdim    RelI->getTypeName(RelTypeName);
101303231Sdim#endif
102341825Sdim    LLVM_DEBUG(dbgs() << "\t\tIn Section " << SectionID << " Offset " << Offset
103341825Sdim                      << " RelType: " << RelTypeName << " TargetName: "
104341825Sdim                      << TargetName << " Addend " << Addend << "\n");
105303231Sdim
106303231Sdim    unsigned TargetSectionID = -1;
107303231Sdim    if (Section == Obj.section_end()) {
108303231Sdim      RelocationEntry RE(SectionID, Offset, RelType, 0, -1, 0, 0, 0, false, 0);
109303231Sdim      addRelocationForSymbol(RE, TargetName);
110303231Sdim    } else {
111303231Sdim      if (auto TargetSectionIDOrErr =
112303231Sdim          findOrEmitSection(Obj, *Section, Section->isText(), ObjSectionToID))
113303231Sdim        TargetSectionID = *TargetSectionIDOrErr;
114303231Sdim      else
115303231Sdim        return TargetSectionIDOrErr.takeError();
116303231Sdim
117314564Sdim      // We need to find out if the relocation is relative to a thumb function
118314564Sdim      // so that we include the ISA selection bit when resolve the relocation
119314564Sdim      bool IsTargetThumbFunc = isThumbFunc(Symbol, Obj, Section);
120314564Sdim
121303231Sdim      switch (RelType) {
122303231Sdim      default: llvm_unreachable("unsupported relocation type");
123303231Sdim      case COFF::IMAGE_REL_ARM_ABSOLUTE:
124303231Sdim        // This relocation is ignored.
125303231Sdim        break;
126314564Sdim      case COFF::IMAGE_REL_ARM_ADDR32: {
127314564Sdim        RelocationEntry RE = RelocationEntry(
128314564Sdim            SectionID, Offset, RelType, Addend, TargetSectionID,
129314564Sdim            getSymbolOffset(*Symbol), 0, 0, false, 0, IsTargetThumbFunc);
130314564Sdim        addRelocationForSection(RE, TargetSectionID);
131314564Sdim        break;
132314564Sdim      }
133303231Sdim      case COFF::IMAGE_REL_ARM_ADDR32NB: {
134303231Sdim        RelocationEntry RE =
135303231Sdim            RelocationEntry(SectionID, Offset, RelType, Addend, TargetSectionID,
136303231Sdim                            getSymbolOffset(*Symbol), 0, 0, false, 0);
137303231Sdim        addRelocationForSection(RE, TargetSectionID);
138303231Sdim        break;
139303231Sdim      }
140303231Sdim      case COFF::IMAGE_REL_ARM_SECTION: {
141303231Sdim        RelocationEntry RE =
142303231Sdim            RelocationEntry(TargetSectionID, Offset, RelType, 0);
143303231Sdim        addRelocationForSection(RE, TargetSectionID);
144303231Sdim        break;
145303231Sdim      }
146303231Sdim      case COFF::IMAGE_REL_ARM_SECREL: {
147303231Sdim        RelocationEntry RE = RelocationEntry(SectionID, Offset, RelType,
148303231Sdim                                             getSymbolOffset(*Symbol) + Addend);
149303231Sdim        addRelocationForSection(RE, TargetSectionID);
150303231Sdim        break;
151303231Sdim      }
152303231Sdim      case COFF::IMAGE_REL_ARM_MOV32T: {
153314564Sdim        RelocationEntry RE = RelocationEntry(
154314564Sdim            SectionID, Offset, RelType, Addend, TargetSectionID,
155314564Sdim            getSymbolOffset(*Symbol), 0, 0, false, 0, IsTargetThumbFunc);
156303231Sdim        addRelocationForSection(RE, TargetSectionID);
157303231Sdim        break;
158303231Sdim      }
159303231Sdim      case COFF::IMAGE_REL_ARM_BRANCH20T:
160303231Sdim      case COFF::IMAGE_REL_ARM_BRANCH24T:
161303231Sdim      case COFF::IMAGE_REL_ARM_BLX23T: {
162303231Sdim        RelocationEntry RE =
163303231Sdim            RelocationEntry(SectionID, Offset, RelType,
164303231Sdim                            getSymbolOffset(*Symbol) + Addend, true, 0);
165303231Sdim        addRelocationForSection(RE, TargetSectionID);
166303231Sdim        break;
167303231Sdim      }
168303231Sdim      }
169303231Sdim    }
170303231Sdim
171303231Sdim    return ++RelI;
172303231Sdim  }
173303231Sdim
174303231Sdim  void resolveRelocation(const RelocationEntry &RE, uint64_t Value) override {
175303231Sdim    const auto Section = Sections[RE.SectionID];
176303231Sdim    uint8_t *Target = Section.getAddressWithOffset(RE.Offset);
177314564Sdim    int ISASelectionBit = RE.IsTargetThumbFunc ? 1 : 0;
178303231Sdim
179303231Sdim    switch (RE.RelType) {
180303231Sdim    default: llvm_unreachable("unsupported relocation type");
181303231Sdim    case COFF::IMAGE_REL_ARM_ABSOLUTE:
182303231Sdim      // This relocation is ignored.
183303231Sdim      break;
184303231Sdim    case COFF::IMAGE_REL_ARM_ADDR32: {
185303231Sdim      // The target's 32-bit VA.
186303231Sdim      uint64_t Result =
187303231Sdim          RE.Sections.SectionA == static_cast<uint32_t>(-1)
188303231Sdim              ? Value
189303231Sdim              : Sections[RE.Sections.SectionA].getLoadAddressWithOffset(RE.Addend);
190314564Sdim      Result |= ISASelectionBit;
191327952Sdim      assert(Result <= UINT32_MAX && "relocation overflow");
192341825Sdim      LLVM_DEBUG(dbgs() << "\t\tOffset: " << RE.Offset
193341825Sdim                        << " RelType: IMAGE_REL_ARM_ADDR32"
194341825Sdim                        << " TargetSection: " << RE.Sections.SectionA
195341825Sdim                        << " Value: " << format("0x%08" PRIx32, Result)
196341825Sdim                        << '\n');
197303231Sdim      writeBytesUnaligned(Result, Target, 4);
198303231Sdim      break;
199303231Sdim    }
200303231Sdim    case COFF::IMAGE_REL_ARM_ADDR32NB: {
201303231Sdim      // The target's 32-bit RVA.
202303231Sdim      // NOTE: use Section[0].getLoadAddress() as an approximation of ImageBase
203303231Sdim      uint64_t Result = Sections[RE.Sections.SectionA].getLoadAddress() -
204303231Sdim                        Sections[0].getLoadAddress() + RE.Addend;
205327952Sdim      assert(Result <= UINT32_MAX && "relocation overflow");
206341825Sdim      LLVM_DEBUG(dbgs() << "\t\tOffset: " << RE.Offset
207341825Sdim                        << " RelType: IMAGE_REL_ARM_ADDR32NB"
208341825Sdim                        << " TargetSection: " << RE.Sections.SectionA
209341825Sdim                        << " Value: " << format("0x%08" PRIx32, Result)
210341825Sdim                        << '\n');
211314564Sdim      Result |= ISASelectionBit;
212303231Sdim      writeBytesUnaligned(Result, Target, 4);
213303231Sdim      break;
214303231Sdim    }
215303231Sdim    case COFF::IMAGE_REL_ARM_SECTION:
216303231Sdim      // 16-bit section index of the section that contains the target.
217327952Sdim      assert(static_cast<uint32_t>(RE.SectionID) <= UINT16_MAX &&
218303231Sdim             "relocation overflow");
219341825Sdim      LLVM_DEBUG(dbgs() << "\t\tOffset: " << RE.Offset
220341825Sdim                        << " RelType: IMAGE_REL_ARM_SECTION Value: "
221341825Sdim                        << RE.SectionID << '\n');
222303231Sdim      writeBytesUnaligned(RE.SectionID, Target, 2);
223303231Sdim      break;
224303231Sdim    case COFF::IMAGE_REL_ARM_SECREL:
225303231Sdim      // 32-bit offset of the target from the beginning of its section.
226327952Sdim      assert(static_cast<uint64_t>(RE.Addend) <= UINT32_MAX &&
227303231Sdim             "relocation overflow");
228341825Sdim      LLVM_DEBUG(dbgs() << "\t\tOffset: " << RE.Offset
229341825Sdim                        << " RelType: IMAGE_REL_ARM_SECREL Value: " << RE.Addend
230341825Sdim                        << '\n');
231303231Sdim      writeBytesUnaligned(RE.Addend, Target, 2);
232303231Sdim      break;
233303231Sdim    case COFF::IMAGE_REL_ARM_MOV32T: {
234303231Sdim      // 32-bit VA of the target applied to a contiguous MOVW+MOVT pair.
235303231Sdim      uint64_t Result =
236303231Sdim          Sections[RE.Sections.SectionA].getLoadAddressWithOffset(RE.Addend);
237327952Sdim      assert(Result <= UINT32_MAX && "relocation overflow");
238341825Sdim      LLVM_DEBUG(dbgs() << "\t\tOffset: " << RE.Offset
239341825Sdim                        << " RelType: IMAGE_REL_ARM_MOV32T"
240341825Sdim                        << " TargetSection: " << RE.Sections.SectionA
241341825Sdim                        << " Value: " << format("0x%08" PRIx32, Result)
242341825Sdim                        << '\n');
243303231Sdim
244303231Sdim      // MOVW(T3): |11110|i|10|0|1|0|0|imm4|0|imm3|Rd|imm8|
245303231Sdim      //            imm32 = zext imm4:i:imm3:imm8
246303231Sdim      // MOVT(T1): |11110|i|10|1|1|0|0|imm4|0|imm3|Rd|imm8|
247303231Sdim      //            imm16 =      imm4:i:imm3:imm8
248303231Sdim
249303231Sdim      auto EncodeImmediate = [](uint8_t *Bytes, uint16_t Immediate)  {
250303231Sdim        Bytes[0] |= ((Immediate & 0xf000) >> 12);
251303231Sdim        Bytes[1] |= ((Immediate & 0x0800) >> 11);
252303231Sdim        Bytes[2] |= ((Immediate & 0x00ff) >>  0);
253314564Sdim        Bytes[3] |= (((Immediate & 0x0700) >>  8) << 4);
254303231Sdim      };
255303231Sdim
256314564Sdim      EncodeImmediate(&Target[0],
257314564Sdim                      (static_cast<uint32_t>(Result) >> 00) | ISASelectionBit);
258303231Sdim      EncodeImmediate(&Target[4], static_cast<uint32_t>(Result) >> 16);
259303231Sdim
260303231Sdim      break;
261303231Sdim    }
262303231Sdim    case COFF::IMAGE_REL_ARM_BRANCH20T: {
263303231Sdim      // The most significant 20-bits of the signed 21-bit relative displacement
264303231Sdim      uint64_t Value =
265303231Sdim          RE.Addend - (Sections[RE.SectionID].getLoadAddress() + RE.Offset) - 4;
266327952Sdim      assert(static_cast<int64_t>(RE.Addend) <= INT32_MAX &&
267303231Sdim             "relocation overflow");
268327952Sdim      assert(static_cast<int64_t>(RE.Addend) >= INT32_MIN &&
269303231Sdim             "relocation underflow");
270341825Sdim      LLVM_DEBUG(dbgs() << "\t\tOffset: " << RE.Offset
271341825Sdim                        << " RelType: IMAGE_REL_ARM_BRANCH20T"
272341825Sdim                        << " Value: " << static_cast<int32_t>(Value) << '\n');
273303231Sdim      static_cast<void>(Value);
274303231Sdim      llvm_unreachable("unimplemented relocation");
275303231Sdim      break;
276303231Sdim    }
277303231Sdim    case COFF::IMAGE_REL_ARM_BRANCH24T: {
278303231Sdim      // The most significant 24-bits of the signed 25-bit relative displacement
279303231Sdim      uint64_t Value =
280303231Sdim          RE.Addend - (Sections[RE.SectionID].getLoadAddress() + RE.Offset) - 4;
281327952Sdim      assert(static_cast<int64_t>(RE.Addend) <= INT32_MAX &&
282303231Sdim             "relocation overflow");
283327952Sdim      assert(static_cast<int64_t>(RE.Addend) >= INT32_MIN &&
284303231Sdim             "relocation underflow");
285341825Sdim      LLVM_DEBUG(dbgs() << "\t\tOffset: " << RE.Offset
286341825Sdim                        << " RelType: IMAGE_REL_ARM_BRANCH24T"
287341825Sdim                        << " Value: " << static_cast<int32_t>(Value) << '\n');
288303231Sdim      static_cast<void>(Value);
289303231Sdim      llvm_unreachable("unimplemented relocation");
290303231Sdim      break;
291303231Sdim    }
292303231Sdim    case COFF::IMAGE_REL_ARM_BLX23T: {
293303231Sdim      // The most significant 24-bits of the signed 25-bit relative displacement
294303231Sdim      uint64_t Value =
295303231Sdim          RE.Addend - (Sections[RE.SectionID].getLoadAddress() + RE.Offset) - 4;
296327952Sdim      assert(static_cast<int64_t>(RE.Addend) <= INT32_MAX &&
297303231Sdim             "relocation overflow");
298327952Sdim      assert(static_cast<int64_t>(RE.Addend) >= INT32_MIN &&
299303231Sdim             "relocation underflow");
300341825Sdim      LLVM_DEBUG(dbgs() << "\t\tOffset: " << RE.Offset
301341825Sdim                        << " RelType: IMAGE_REL_ARM_BLX23T"
302341825Sdim                        << " Value: " << static_cast<int32_t>(Value) << '\n');
303303231Sdim      static_cast<void>(Value);
304303231Sdim      llvm_unreachable("unimplemented relocation");
305303231Sdim      break;
306303231Sdim    }
307303231Sdim    }
308303231Sdim  }
309303231Sdim
310303231Sdim  void registerEHFrames() override {}
311303231Sdim};
312303231Sdim
313303231Sdim}
314303231Sdim
315303231Sdim#endif
316