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(E: SymTypeOrErr.takeError(), OS); |
33 | report_fatal_error(reason: 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>(Val: Obj) |
42 | .getCOFFSection(Section: *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(Sym: 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>(Val: Sec.getObject()); |
71 | const coff_section *CoffSec = COFFObjPtr->getCOFFSection(Section: 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(reason: "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(Src: Displacement, Size: 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(Prefix: getImportSymbolPrefix())) { |
133 | TargetSectionID = SectionID; |
134 | TargetOffset = getDLLImportOffset(SectionID, Stubs, Name: TargetName, SetSectionIDMinus1: true); |
135 | TargetName = StringRef(); |
136 | IsExtern = false; |
137 | } else if (!IsExtern) { |
138 | if (auto TargetSectionIDOrErr = |
139 | findOrEmitSection(Obj, Section: *Section, IsCode: Section->isText(), LocalSections&: ObjSectionToID)) |
140 | TargetSectionID = *TargetSectionIDOrErr; |
141 | else |
142 | return TargetSectionIDOrErr.takeError(); |
143 | if (RelType != COFF::IMAGE_REL_ARM_SECTION) |
144 | TargetOffset = getSymbolOffset(Sym: *Symbol); |
145 | } |
146 | |
147 | if (IsExtern) { |
148 | RelocationEntry RE(SectionID, Offset, RelType, 0, -1, 0, 0, 0, false, 0); |
149 | addRelocationForSymbol(RE, SymbolName: 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, SectionID: 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, SectionID: TargetSectionID); |
173 | break; |
174 | } |
175 | case COFF::IMAGE_REL_ARM_SECTION: { |
176 | RelocationEntry RE = |
177 | RelocationEntry(TargetSectionID, Offset, RelType, 0); |
178 | addRelocationForSection(RE, SectionID: TargetSectionID); |
179 | break; |
180 | } |
181 | case COFF::IMAGE_REL_ARM_SECREL: { |
182 | RelocationEntry RE = |
183 | RelocationEntry(SectionID, Offset, RelType, TargetOffset + Addend); |
184 | addRelocationForSection(RE, SectionID: 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, SectionID: 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, SectionID: 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(OffsetBytes: 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(OffsetBytes: 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(Value: Result, Dst: Target, Size: 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(Value: Result, Dst: Target, Size: 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(Value: RE.SectionID, Dst: Target, Size: 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(Value: RE.Addend, Dst: Target, Size: 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(OffsetBytes: 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 | |