xref: /freebsd/contrib/llvm-project/llvm/lib/ExecutionEngine/RuntimeDyld/Targets/RuntimeDyldCOFFThumb.h (revision 5f757f3ff9144b609b3c433dfd370cc6bdc191ad)
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 
23 namespace llvm {
24 
25 static 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 
47 class RuntimeDyldCOFFThumb : public RuntimeDyldCOFF {
48 public:
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