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     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 
47 class RuntimeDyldCOFFThumb : public RuntimeDyldCOFF {
48 public:
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