LLVM 20.0.0git
RuntimeDyldCOFFAArch64.h
Go to the documentation of this file.
1//===-- RuntimeDyldCOFFAArch64.h --- COFF/AArch64 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 AArch64 support for MC-JIT runtime dynamic linker.
10//
11//===----------------------------------------------------------------------===//
12
13#ifndef LLVM_LIB_EXECUTIONENGINE_RUNTIMEDYLD_TARGETS_RUNTIMEDYLDCOFFAARCH64_H
14#define LLVM_LIB_EXECUTIONENGINE_RUNTIMEDYLD_TARGETS_RUNTIMEDYLDCOFFAARCH64_H
15
16#include "../RuntimeDyldCOFF.h"
19#include "llvm/Object/COFF.h"
20#include "llvm/Support/Endian.h"
21
22#define DEBUG_TYPE "dyld"
23
24namespace llvm {
25
26// This relocation type is used for handling long branch instruction
27// through the Stub.
28enum InternalRelocationType : unsigned {
30};
31
32static void add16(uint8_t *p, int16_t v) {
33 using namespace llvm::support::endian;
34 write16le(p, read16le(p) + v);
35}
36
37static void or32le(void *P, int32_t V) {
38 using namespace llvm::support::endian;
39
40 write32le(P, read32le(P) | V);
41}
42
43static void write32AArch64Imm(uint8_t *T, uint64_t imm, uint32_t rangeLimit) {
44 using namespace llvm::support::endian;
45
46 uint32_t orig = read32le(T);
47 orig &= ~(0xFFF << 10);
48 write32le(T, orig | ((imm & (0xFFF >> rangeLimit)) << 10));
49}
50
51static void write32AArch64Ldr(uint8_t *T, uint64_t imm) {
52 using namespace llvm::support::endian;
53
54 uint32_t orig = read32le(T);
55 uint32_t size = orig >> 30;
56 // 0x04000000 indicates SIMD/FP registers
57 // 0x00800000 indicates 128 bit
58 if ((orig & 0x04800000) == 0x04800000)
59 size += 4;
60 if ((imm & ((1 << size) - 1)) != 0)
61 assert(0 && "misaligned ldr/str offset");
62 write32AArch64Imm(T, imm >> size, size);
63}
64
65static void write32AArch64Addr(void *T, uint64_t s, uint64_t p, int shift) {
66 using namespace llvm::support::endian;
67
68 uint64_t Imm = (s >> shift) - (p >> shift);
69 uint32_t ImmLo = (Imm & 0x3) << 29;
70 uint32_t ImmHi = (Imm & 0x1FFFFC) << 3;
71 uint64_t Mask = (0x3 << 29) | (0x1FFFFC << 3);
72 write32le(T, (read32le(T) & ~Mask) | ImmLo | ImmHi);
73}
74
76
77private:
78 // When a module is loaded we save the SectionID of the unwind
79 // sections in a table until we receive a request to register all
80 // unregisteredEH frame sections with the memory manager.
81 SmallVector<SID, 2> UnregisteredEHFrameSections;
82 SmallVector<SID, 2> RegisteredEHFrameSections;
83 uint64_t ImageBase;
84
85 // Fake an __ImageBase pointer by returning the section with the lowest adress
86 uint64_t getImageBase() {
87 if (!ImageBase) {
88 ImageBase = std::numeric_limits<uint64_t>::max();
89 for (const SectionEntry &Section : Sections)
90 // The Sections list may contain sections that weren't loaded for
91 // whatever reason: they may be debug sections, and ProcessAllSections
92 // is false, or they may be sections that contain 0 bytes. If the
93 // section isn't loaded, the load address will be 0, and it should not
94 // be included in the ImageBase calculation.
95 if (Section.getLoadAddress() != 0)
96 ImageBase = std::min(ImageBase, Section.getLoadAddress());
97 }
98 return ImageBase;
99 }
100
101public:
104 : RuntimeDyldCOFF(MM, Resolver, 8, COFF::IMAGE_REL_ARM64_ADDR64),
105 ImageBase(0) {}
106
107 Align getStubAlignment() override { return Align(8); }
108
109 unsigned getMaxStubSize() const override { return 20; }
110
111 std::tuple<uint64_t, uint64_t, uint64_t>
112 generateRelocationStub(unsigned SectionID, StringRef TargetName,
113 uint64_t Offset, uint64_t RelType, uint64_t Addend,
114 StubMap &Stubs) {
115 uintptr_t StubOffset;
116 SectionEntry &Section = Sections[SectionID];
117
118 RelocationValueRef OriginalRelValueRef;
119 OriginalRelValueRef.SectionID = SectionID;
120 OriginalRelValueRef.Offset = Offset;
121 OriginalRelValueRef.Addend = Addend;
122 OriginalRelValueRef.SymbolName = TargetName.data();
123
124 auto Stub = Stubs.find(OriginalRelValueRef);
125 if (Stub == Stubs.end()) {
126 LLVM_DEBUG(dbgs() << " Create a new stub function for "
127 << TargetName.data() << "\n");
128
129 StubOffset = Section.getStubOffset();
130 Stubs[OriginalRelValueRef] = StubOffset;
131 createStubFunction(Section.getAddressWithOffset(StubOffset));
132 Section.advanceStubOffset(getMaxStubSize());
133 } else {
134 LLVM_DEBUG(dbgs() << " Stub function found for " << TargetName.data()
135 << "\n");
136 StubOffset = Stub->second;
137 }
138
139 // Resolve original relocation to stub function.
140 const RelocationEntry RE(SectionID, Offset, RelType, Addend);
141 resolveRelocation(RE, Section.getLoadAddressWithOffset(StubOffset));
142
143 // adjust relocation info so resolution writes to the stub function
144 // Here an internal relocation type is used for resolving long branch via
145 // stub instruction.
146 Addend = 0;
147 Offset = StubOffset;
149
150 return std::make_tuple(Offset, RelType, Addend);
151 }
152
155 const object::ObjectFile &Obj,
156 ObjSectionToIDMap &ObjSectionToID,
157 StubMap &Stubs) override {
158 using namespace llvm::support::endian;
159
160 auto Symbol = RelI->getSymbol();
161 if (Symbol == Obj.symbol_end())
162 report_fatal_error("Unknown symbol in relocation");
163
164 Expected<StringRef> TargetNameOrErr = Symbol->getName();
165 if (!TargetNameOrErr)
166 return TargetNameOrErr.takeError();
167 StringRef TargetName = *TargetNameOrErr;
168
169 auto SectionOrErr = Symbol->getSection();
170 if (!SectionOrErr)
171 return SectionOrErr.takeError();
172 auto Section = *SectionOrErr;
173
174 uint64_t RelType = RelI->getType();
175 uint64_t Offset = RelI->getOffset();
176
177 // If there is no section, this must be an external reference.
178 bool IsExtern = Section == Obj.section_end();
179
180 // Determine the Addend used to adjust the relocation value.
181 uint64_t Addend = 0;
182 SectionEntry &AddendSection = Sections[SectionID];
183 uintptr_t ObjTarget = AddendSection.getObjAddress() + Offset;
184 uint8_t *Displacement = (uint8_t *)ObjTarget;
185
186 unsigned TargetSectionID = -1;
187 uint64_t TargetOffset = -1;
188
189 if (TargetName.starts_with(getImportSymbolPrefix())) {
190 TargetSectionID = SectionID;
191 TargetOffset = getDLLImportOffset(SectionID, Stubs, TargetName);
192 TargetName = StringRef();
193 IsExtern = false;
194 } else if (!IsExtern) {
195 if (auto TargetSectionIDOrErr = findOrEmitSection(
196 Obj, *Section, Section->isText(), ObjSectionToID))
197 TargetSectionID = *TargetSectionIDOrErr;
198 else
199 return TargetSectionIDOrErr.takeError();
200
201 TargetOffset = getSymbolOffset(*Symbol);
202 }
203
204 switch (RelType) {
209 Addend = read32le(Displacement);
210 break;
212 uint32_t orig = read32le(Displacement);
213 Addend = (orig & 0x03FFFFFF) << 2;
214
215 if (IsExtern)
216 std::tie(Offset, RelType, Addend) = generateRelocationStub(
217 SectionID, TargetName, Offset, RelType, Addend, Stubs);
218 break;
219 }
221 uint32_t orig = read32le(Displacement);
222 Addend = (orig & 0x00FFFFE0) >> 3;
223 break;
224 }
226 uint32_t orig = read32le(Displacement);
227 Addend = (orig & 0x000FFFE0) >> 3;
228 break;
229 }
232 uint32_t orig = read32le(Displacement);
233 Addend = ((orig >> 29) & 0x3) | ((orig >> 3) & 0x1FFFFC);
234 break;
235 }
238 uint32_t orig = read32le(Displacement);
239 Addend = ((orig >> 10) & 0xFFF);
240 break;
241 }
243 Addend = read64le(Displacement);
244 break;
245 }
246 default:
247 break;
248 }
249
250#if !defined(NDEBUG)
251 SmallString<32> RelTypeName;
252 RelI->getTypeName(RelTypeName);
253
254 LLVM_DEBUG(dbgs() << "\t\tIn Section " << SectionID << " Offset " << Offset
255 << " RelType: " << RelTypeName << " TargetName: "
256 << TargetName << " Addend " << Addend << "\n");
257#endif
258
259 if (IsExtern) {
260 RelocationEntry RE(SectionID, Offset, RelType, Addend);
261 addRelocationForSymbol(RE, TargetName);
262 } else {
263 RelocationEntry RE(SectionID, Offset, RelType, TargetOffset + Addend);
264 addRelocationForSection(RE, TargetSectionID);
265 }
266 return ++RelI;
267 }
268
270 using namespace llvm::support::endian;
271
272 const auto Section = Sections[RE.SectionID];
273 uint8_t *Target = Section.getAddressWithOffset(RE.Offset);
274 uint64_t FinalAddress = Section.getLoadAddressWithOffset(RE.Offset);
275
276 switch (RE.RelType) {
277 default:
278 llvm_unreachable("unsupported relocation type");
280 // This relocation is ignored.
281 break;
282 }
284 // The page base of the target, for ADRP instruction.
285 Value += RE.Addend;
286 write32AArch64Addr(Target, Value, FinalAddress, 12);
287 break;
288 }
290 // The 12-bit relative displacement to the target, for instruction ADR
291 Value += RE.Addend;
292 write32AArch64Addr(Target, Value, FinalAddress, 0);
293 break;
294 }
296 // The 12-bit page offset of the target,
297 // for instructions ADD/ADDS (immediate) with zero shift.
298 Value += RE.Addend;
299 write32AArch64Imm(Target, Value & 0xFFF, 0);
300 break;
301 }
303 // The 12-bit page offset of the target,
304 // for instruction LDR (indexed, unsigned immediate).
305 Value += RE.Addend;
307 break;
308 }
310 // The 32-bit VA of the target.
311 uint32_t VA = Value + RE.Addend;
312 write32le(Target, VA);
313 break;
314 }
316 // The target's 32-bit RVA.
317 uint64_t RVA = Value + RE.Addend - getImageBase();
318 write32le(Target, RVA);
319 break;
320 }
322 // Encode the immadiate value for generated Stub instruction (MOVZ)
323 or32le(Target + 12, ((Value + RE.Addend) & 0xFFFF) << 5);
324 or32le(Target + 8, ((Value + RE.Addend) & 0xFFFF0000) >> 11);
325 or32le(Target + 4, ((Value + RE.Addend) & 0xFFFF00000000) >> 27);
326 or32le(Target + 0, ((Value + RE.Addend) & 0xFFFF000000000000) >> 43);
327 break;
328 }
330 // The 26-bit relative displacement to the target, for B and BL
331 // instructions.
332 uint64_t PCRelVal = Value + RE.Addend - FinalAddress;
333 assert(isInt<28>(PCRelVal) && "Branch target is out of range.");
334 write32le(Target, (read32le(Target) & ~(0x03FFFFFF)) |
335 (PCRelVal & 0x0FFFFFFC) >> 2);
336 break;
337 }
339 // The 19-bit offset to the relocation target,
340 // for conditional B instruction.
341 uint64_t PCRelVal = Value + RE.Addend - FinalAddress;
342 assert(isInt<21>(PCRelVal) && "Branch target is out of range.");
343 write32le(Target, (read32le(Target) & ~(0x00FFFFE0)) |
344 (PCRelVal & 0x001FFFFC) << 3);
345 break;
346 }
348 // The 14-bit offset to the relocation target,
349 // for instructions TBZ and TBNZ.
350 uint64_t PCRelVal = Value + RE.Addend - FinalAddress;
351 assert(isInt<16>(PCRelVal) && "Branch target is out of range.");
352 write32le(Target, (read32le(Target) & ~(0x000FFFE0)) |
353 (PCRelVal & 0x0000FFFC) << 3);
354 break;
355 }
357 // The 64-bit VA of the relocation target.
358 write64le(Target, Value + RE.Addend);
359 break;
360 }
362 // 16-bit section index of the section that contains the target.
363 assert(static_cast<uint32_t>(RE.SectionID) <= UINT16_MAX &&
364 "relocation overflow");
366 break;
367 }
369 // 32-bit offset of the target from the beginning of its section.
370 assert(static_cast<int64_t>(RE.Addend) <= INT32_MAX &&
371 "Relocation overflow");
372 assert(static_cast<int64_t>(RE.Addend) >= INT32_MIN &&
373 "Relocation underflow");
374 write32le(Target, RE.Addend);
375 break;
376 }
378 // The 32-bit relative address from the byte following the relocation.
379 uint64_t Result = Value - FinalAddress - 4;
380 write32le(Target, Result + RE.Addend);
381 break;
382 }
383 }
384 }
385
386 void registerEHFrames() override {}
387};
388
389} // End namespace llvm
390
391#undef DEBUG_TYPE
392
393#endif // LLVM_LIB_EXECUTIONENGINE_RUNTIMEDYLD_TARGETS_RUNTIMEDYLDCOFFAARCH64_H
#define LLVM_DEBUG(...)
Definition: Debug.h:106
#define P(N)
assert(ImpDefSCC.getReg()==AMDGPU::SCC &&ImpDefSCC.isDef())
This file defines the SmallString class.
Tagged union holding either a T or a Error.
Definition: Error.h:481
Error takeError()
Take ownership of the stored error.
Definition: Error.h:608
Symbol resolution interface.
Definition: JITSymbol.h:371
RelocationEntry - used to represent relocations internally in the dynamic linker.
uint32_t RelType
RelType - relocation type.
uint64_t Offset
Offset - offset into the section.
int64_t Addend
Addend - the relocation addend encoded in the instruction itself.
unsigned SectionID
SectionID - the section this relocation points to.
Interface for looking up the initializer for a variable name, used by Init::resolveReferences.
Definition: Record.h:2203
void resolveRelocation(const RelocationEntry &RE, uint64_t Value) override
A object file specific relocation resolver.
unsigned getMaxStubSize() const override
RuntimeDyldCOFFAArch64(RuntimeDyld::MemoryManager &MM, JITSymbolResolver &Resolver)
std::tuple< uint64_t, uint64_t, uint64_t > generateRelocationStub(unsigned SectionID, StringRef TargetName, uint64_t Offset, uint64_t RelType, uint64_t Addend, StubMap &Stubs)
Expected< object::relocation_iterator > processRelocationRef(unsigned SectionID, object::relocation_iterator RelI, const object::ObjectFile &Obj, ObjSectionToIDMap &ObjSectionToID, StubMap &Stubs) override
Parses one or more object file relocations (some object files use relocation pairs) and stores it to ...
uint64_t getSymbolOffset(const SymbolRef &Sym)
static constexpr StringRef getImportSymbolPrefix()
uint64_t getDLLImportOffset(unsigned SectionID, StubMap &Stubs, StringRef Name, bool SetSectionIDMinus1=false)
std::map< SectionRef, unsigned > ObjSectionToIDMap
std::map< RelocationValueRef, uintptr_t > StubMap
void addRelocationForSymbol(const RelocationEntry &RE, StringRef SymbolName)
void addRelocationForSection(const RelocationEntry &RE, unsigned SectionID)
Expected< unsigned > findOrEmitSection(const ObjectFile &Obj, const SectionRef &Section, bool IsCode, ObjSectionToIDMap &LocalSections)
Find Section in LocalSections.
uint8_t * createStubFunction(uint8_t *Addr, unsigned AbiVariant=0)
Emits long jump instruction to Addr.
SectionEntry - represents a section emitted into memory by the dynamic linker.
uintptr_t getObjAddress() const
SmallString - A SmallString is just a SmallVector with methods and accessors that make it work better...
Definition: SmallString.h:26
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
Definition: SmallVector.h:1196
StringRef - Represent a constant reference to a string, i.e.
Definition: StringRef.h:51
bool starts_with(StringRef Prefix) const
Check if this string starts with the given Prefix.
Definition: StringRef.h:265
constexpr const char * data() const
data - Get a pointer to the start of the string (which may not be null terminated).
Definition: StringRef.h:144
Target - Wrapper for Target specific information.
LLVM Value Representation.
Definition: Value.h:74
This class is the base class for all object file types.
Definition: ObjectFile.h:229
virtual section_iterator section_end() const =0
virtual basic_symbol_iterator symbol_end() const =0
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
@ IMAGE_REL_ARM64_SECREL
Definition: COFF.h:408
@ IMAGE_REL_ARM64_BRANCH19
Definition: COFF.h:415
@ IMAGE_REL_ARM64_REL32
Definition: COFF.h:417
@ IMAGE_REL_ARM64_ADDR32
Definition: COFF.h:401
@ IMAGE_REL_ARM64_SECTION
Definition: COFF.h:413
@ IMAGE_REL_ARM64_ABSOLUTE
Definition: COFF.h:400
@ IMAGE_REL_ARM64_PAGEOFFSET_12A
Definition: COFF.h:406
@ IMAGE_REL_ARM64_BRANCH14
Definition: COFF.h:416
@ IMAGE_REL_ARM64_BRANCH26
Definition: COFF.h:403
@ IMAGE_REL_ARM64_PAGEOFFSET_12L
Definition: COFF.h:407
@ IMAGE_REL_ARM64_ADDR32NB
Definition: COFF.h:402
@ IMAGE_REL_ARM64_PAGEBASE_REL21
Definition: COFF.h:404
@ IMAGE_REL_ARM64_REL21
Definition: COFF.h:405
@ IMAGE_REL_ARM64_ADDR64
Definition: COFF.h:414
This is an optimization pass for GlobalISel generic memory operations.
Definition: AddressRanges.h:18
@ Offset
Definition: DWP.cpp:480
auto size(R &&Range, std::enable_if_t< std::is_base_of< std::random_access_iterator_tag, typename std::iterator_traits< decltype(Range.begin())>::iterator_category >::value, void > *=nullptr)
Get the size of a range.
Definition: STLExtras.h:1697
@ INTERNAL_REL_ARM64_LONG_BRANCH26
raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
Definition: Debug.cpp:163
void report_fatal_error(Error Err, bool gen_crash_diag=true)
Report a serious error, calling any installed error handler.
Definition: Error.cpp:167
static void write32AArch64Ldr(uint8_t *T, uint64_t imm)
static void or32le(void *P, int32_t V)
static void add16(uint8_t *p, int16_t v)
static void write32AArch64Imm(uint8_t *T, uint64_t imm, uint32_t rangeLimit)
static void write32AArch64Addr(void *T, uint64_t s, uint64_t p, int shift)
This struct is a compact representation of a valid (non-zero power of two) alignment.
Definition: Alignment.h:39