xref: /freebsd/contrib/llvm-project/llvm/lib/ExecutionEngine/RuntimeDyld/Targets/RuntimeDyldCOFFThumb.h (revision 2e3507c25e42292b45a5482e116d278f5515d04d)
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   Align getStubAlignment() override { return Align(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     bool IsExtern = Section == Obj.section_end();
107     unsigned TargetSectionID = -1;
108     uint64_t TargetOffset = -1;
109 
110     if (TargetName.startswith(getImportSymbolPrefix())) {
111       TargetSectionID = SectionID;
112       TargetOffset = getDLLImportOffset(SectionID, Stubs, TargetName, true);
113       TargetName = StringRef();
114       IsExtern = false;
115     } else if (!IsExtern) {
116       if (auto TargetSectionIDOrErr =
117           findOrEmitSection(Obj, *Section, Section->isText(), ObjSectionToID))
118         TargetSectionID = *TargetSectionIDOrErr;
119       else
120         return TargetSectionIDOrErr.takeError();
121       if (RelType != COFF::IMAGE_REL_ARM_SECTION)
122         TargetOffset = getSymbolOffset(*Symbol);
123     }
124 
125     if (IsExtern) {
126       RelocationEntry RE(SectionID, Offset, RelType, 0, -1, 0, 0, 0, false, 0);
127       addRelocationForSymbol(RE, TargetName);
128     } else {
129 
130       // We need to find out if the relocation is relative to a thumb function
131       // so that we include the ISA selection bit when resolve the relocation
132       bool IsTargetThumbFunc = isThumbFunc(Symbol, Obj, Section);
133 
134       switch (RelType) {
135       default: llvm_unreachable("unsupported relocation type");
136       case COFF::IMAGE_REL_ARM_ABSOLUTE:
137         // This relocation is ignored.
138         break;
139       case COFF::IMAGE_REL_ARM_ADDR32: {
140         RelocationEntry RE =
141             RelocationEntry(SectionID, Offset, RelType, Addend, TargetSectionID,
142                             TargetOffset, 0, 0, false, 0, IsTargetThumbFunc);
143         addRelocationForSection(RE, TargetSectionID);
144         break;
145       }
146       case COFF::IMAGE_REL_ARM_ADDR32NB: {
147         RelocationEntry RE =
148             RelocationEntry(SectionID, Offset, RelType, Addend, TargetSectionID,
149                             TargetOffset, 0, 0, false, 0);
150         addRelocationForSection(RE, TargetSectionID);
151         break;
152       }
153       case COFF::IMAGE_REL_ARM_SECTION: {
154         RelocationEntry RE =
155             RelocationEntry(TargetSectionID, Offset, RelType, 0);
156         addRelocationForSection(RE, TargetSectionID);
157         break;
158       }
159       case COFF::IMAGE_REL_ARM_SECREL: {
160         RelocationEntry RE =
161             RelocationEntry(SectionID, Offset, RelType, TargetOffset + Addend);
162         addRelocationForSection(RE, TargetSectionID);
163         break;
164       }
165       case COFF::IMAGE_REL_ARM_MOV32T: {
166         RelocationEntry RE =
167             RelocationEntry(SectionID, Offset, RelType, Addend, TargetSectionID,
168                             TargetOffset, 0, 0, false, 0, IsTargetThumbFunc);
169         addRelocationForSection(RE, TargetSectionID);
170         break;
171       }
172       case COFF::IMAGE_REL_ARM_BRANCH20T:
173       case COFF::IMAGE_REL_ARM_BRANCH24T:
174       case COFF::IMAGE_REL_ARM_BLX23T: {
175         RelocationEntry RE = RelocationEntry(SectionID, Offset, RelType,
176                                              TargetOffset + Addend, true, 0);
177         addRelocationForSection(RE, TargetSectionID);
178         break;
179       }
180       }
181     }
182 
183     return ++RelI;
184   }
185 
186   void resolveRelocation(const RelocationEntry &RE, uint64_t Value) override {
187     const auto Section = Sections[RE.SectionID];
188     uint8_t *Target = Section.getAddressWithOffset(RE.Offset);
189     int ISASelectionBit = RE.IsTargetThumbFunc ? 1 : 0;
190 
191     switch (RE.RelType) {
192     default: llvm_unreachable("unsupported relocation type");
193     case COFF::IMAGE_REL_ARM_ABSOLUTE:
194       // This relocation is ignored.
195       break;
196     case COFF::IMAGE_REL_ARM_ADDR32: {
197       // The target's 32-bit VA.
198       uint64_t Result =
199           RE.Sections.SectionA == static_cast<uint32_t>(-1)
200               ? Value
201               : Sections[RE.Sections.SectionA].getLoadAddressWithOffset(RE.Addend);
202       Result |= ISASelectionBit;
203       assert(Result <= UINT32_MAX && "relocation overflow");
204       LLVM_DEBUG(dbgs() << "\t\tOffset: " << RE.Offset
205                         << " RelType: IMAGE_REL_ARM_ADDR32"
206                         << " TargetSection: " << RE.Sections.SectionA
207                         << " Value: " << format("0x%08" PRIx32, Result)
208                         << '\n');
209       writeBytesUnaligned(Result, Target, 4);
210       break;
211     }
212     case COFF::IMAGE_REL_ARM_ADDR32NB: {
213       // The target's 32-bit RVA.
214       // NOTE: use Section[0].getLoadAddress() as an approximation of ImageBase
215       uint64_t Result = Sections[RE.Sections.SectionA].getLoadAddress() -
216                         Sections[0].getLoadAddress() + RE.Addend;
217       assert(Result <= UINT32_MAX && "relocation overflow");
218       LLVM_DEBUG(dbgs() << "\t\tOffset: " << RE.Offset
219                         << " RelType: IMAGE_REL_ARM_ADDR32NB"
220                         << " TargetSection: " << RE.Sections.SectionA
221                         << " Value: " << format("0x%08" PRIx32, Result)
222                         << '\n');
223       Result |= ISASelectionBit;
224       writeBytesUnaligned(Result, Target, 4);
225       break;
226     }
227     case COFF::IMAGE_REL_ARM_SECTION:
228       // 16-bit section index of the section that contains the target.
229       assert(static_cast<uint32_t>(RE.SectionID) <= UINT16_MAX &&
230              "relocation overflow");
231       LLVM_DEBUG(dbgs() << "\t\tOffset: " << RE.Offset
232                         << " RelType: IMAGE_REL_ARM_SECTION Value: "
233                         << RE.SectionID << '\n');
234       writeBytesUnaligned(RE.SectionID, Target, 2);
235       break;
236     case COFF::IMAGE_REL_ARM_SECREL:
237       // 32-bit offset of the target from the beginning of its section.
238       assert(static_cast<uint64_t>(RE.Addend) <= UINT32_MAX &&
239              "relocation overflow");
240       LLVM_DEBUG(dbgs() << "\t\tOffset: " << RE.Offset
241                         << " RelType: IMAGE_REL_ARM_SECREL Value: " << RE.Addend
242                         << '\n');
243       writeBytesUnaligned(RE.Addend, Target, 2);
244       break;
245     case COFF::IMAGE_REL_ARM_MOV32T: {
246       // 32-bit VA of the target applied to a contiguous MOVW+MOVT pair.
247       uint64_t Result =
248           Sections[RE.Sections.SectionA].getLoadAddressWithOffset(RE.Addend);
249       assert(Result <= UINT32_MAX && "relocation overflow");
250       LLVM_DEBUG(dbgs() << "\t\tOffset: " << RE.Offset
251                         << " RelType: IMAGE_REL_ARM_MOV32T"
252                         << " TargetSection: " << RE.Sections.SectionA
253                         << " Value: " << format("0x%08" PRIx32, Result)
254                         << '\n');
255 
256       // MOVW(T3): |11110|i|10|0|1|0|0|imm4|0|imm3|Rd|imm8|
257       //            imm32 = zext imm4:i:imm3:imm8
258       // MOVT(T1): |11110|i|10|1|1|0|0|imm4|0|imm3|Rd|imm8|
259       //            imm16 =      imm4:i:imm3:imm8
260 
261       auto EncodeImmediate = [](uint8_t *Bytes, uint16_t Immediate)  {
262         Bytes[0] |= ((Immediate & 0xf000) >> 12);
263         Bytes[1] |= ((Immediate & 0x0800) >> 11);
264         Bytes[2] |= ((Immediate & 0x00ff) >>  0);
265         Bytes[3] |= (((Immediate & 0x0700) >>  8) << 4);
266       };
267 
268       EncodeImmediate(&Target[0],
269                       (static_cast<uint32_t>(Result) >> 00) | ISASelectionBit);
270       EncodeImmediate(&Target[4], static_cast<uint32_t>(Result) >> 16);
271       break;
272     }
273     case COFF::IMAGE_REL_ARM_BRANCH20T: {
274       // The most significant 20-bits of the signed 21-bit relative displacement
275       uint64_t Value =
276           RE.Addend - (Sections[RE.SectionID].getLoadAddress() + RE.Offset) - 4;
277       assert(static_cast<int64_t>(RE.Addend) <= INT32_MAX &&
278              "relocation overflow");
279       assert(static_cast<int64_t>(RE.Addend) >= INT32_MIN &&
280              "relocation underflow");
281       LLVM_DEBUG(dbgs() << "\t\tOffset: " << RE.Offset
282                         << " RelType: IMAGE_REL_ARM_BRANCH20T"
283                         << " Value: " << static_cast<int32_t>(Value) << '\n');
284       static_cast<void>(Value);
285       llvm_unreachable("unimplemented relocation");
286       break;
287     }
288     case COFF::IMAGE_REL_ARM_BRANCH24T: {
289       // The most significant 24-bits of the signed 25-bit relative displacement
290       uint64_t Value =
291           RE.Addend - (Sections[RE.SectionID].getLoadAddress() + RE.Offset) - 4;
292       assert(static_cast<int64_t>(RE.Addend) <= INT32_MAX &&
293              "relocation overflow");
294       assert(static_cast<int64_t>(RE.Addend) >= INT32_MIN &&
295              "relocation underflow");
296       LLVM_DEBUG(dbgs() << "\t\tOffset: " << RE.Offset
297                         << " RelType: IMAGE_REL_ARM_BRANCH24T"
298                         << " Value: " << static_cast<int32_t>(Value) << '\n');
299       static_cast<void>(Value);
300       llvm_unreachable("unimplemented relocation");
301       break;
302     }
303     case COFF::IMAGE_REL_ARM_BLX23T: {
304       // The most significant 24-bits of the signed 25-bit relative displacement
305       uint64_t Value =
306           RE.Addend - (Sections[RE.SectionID].getLoadAddress() + RE.Offset) - 4;
307       assert(static_cast<int64_t>(RE.Addend) <= INT32_MAX &&
308              "relocation overflow");
309       assert(static_cast<int64_t>(RE.Addend) >= INT32_MIN &&
310              "relocation underflow");
311       LLVM_DEBUG(dbgs() << "\t\tOffset: " << RE.Offset
312                         << " RelType: IMAGE_REL_ARM_BLX23T"
313                         << " Value: " << static_cast<int32_t>(Value) << '\n');
314       static_cast<void>(Value);
315       llvm_unreachable("unimplemented relocation");
316       break;
317     }
318     }
319   }
320 
321   void registerEHFrames() override {}
322 };
323 
324 }
325 
326 #endif
327