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