LLVM 20.0.0git
ELF_loongarch.cpp
Go to the documentation of this file.
1//===--- ELF_loongarch.cpp - JIT linker implementation for ELF/loongarch --===//
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// ELF/loongarch jit-link implementation.
10//
11//===----------------------------------------------------------------------===//
12
18#include "llvm/Object/ELF.h"
20
21#include "EHFrameSupportImpl.h"
22#include "ELFLinkGraphBuilder.h"
23#include "JITLinkGeneric.h"
24
25#define DEBUG_TYPE "jitlink"
26
27using namespace llvm;
28using namespace llvm::jitlink;
29using namespace llvm::jitlink::loongarch;
30
31namespace {
32
33class ELFJITLinker_loongarch : public JITLinker<ELFJITLinker_loongarch> {
34 friend class JITLinker<ELFJITLinker_loongarch>;
35
36public:
37 ELFJITLinker_loongarch(std::unique_ptr<JITLinkContext> Ctx,
38 std::unique_ptr<LinkGraph> G,
39 PassConfiguration PassConfig)
40 : JITLinker(std::move(Ctx), std::move(G), std::move(PassConfig)) {}
41
42private:
43 Error applyFixup(LinkGraph &G, Block &B, const Edge &E) const {
44 return loongarch::applyFixup(G, B, E);
45 }
46};
47
48namespace {
49
50struct SymbolAnchor {
52 Symbol *Sym;
53 bool End; // true for the anchor of getOffset() + getSize()
54};
55
56struct BlockRelaxAux {
57 // This records symbol start and end offsets which will be adjusted according
58 // to the nearest RelocDeltas element.
60 // All edges that either 1) are R_LARCH_ALIGN or 2) have a R_LARCH_RELAX edge
61 // at the same offset.
63 // For RelaxEdges[I], the actual offset is RelaxEdges[I]->getOffset() - (I ?
64 // RelocDeltas[I - 1] : 0).
66 // For RelaxEdges[I], the actual type is EdgeKinds[I].
68 // List of rewritten instructions. Contains one raw encoded instruction per
69 // element in EdgeKinds that isn't Invalid or R_LARCH_ALIGN.
71};
72
73struct RelaxAux {
75};
76
77} // namespace
78
79static bool shouldRelax(const Section &S) {
80 return (S.getMemProt() & orc::MemProt::Exec) != orc::MemProt::None;
81}
82
83static bool isRelaxable(const Edge &E) {
84 switch (E.getKind()) {
85 default:
86 return false;
87 case AlignRelaxable:
88 return true;
89 }
90}
91
92static RelaxAux initRelaxAux(LinkGraph &G) {
93 RelaxAux Aux;
94 for (auto &S : G.sections()) {
95 if (!shouldRelax(S))
96 continue;
97 for (auto *B : S.blocks()) {
98 auto BlockEmplaceResult = Aux.Blocks.try_emplace(B);
99 assert(BlockEmplaceResult.second && "Block encountered twice");
100 auto &BlockAux = BlockEmplaceResult.first->second;
101
102 for (auto &E : B->edges())
103 if (isRelaxable(E))
104 BlockAux.RelaxEdges.push_back(&E);
105
106 if (BlockAux.RelaxEdges.empty()) {
107 Aux.Blocks.erase(BlockEmplaceResult.first);
108 continue;
109 }
110
111 const auto NumEdges = BlockAux.RelaxEdges.size();
112 BlockAux.RelocDeltas.resize(NumEdges, 0);
113 BlockAux.EdgeKinds.resize_for_overwrite(NumEdges);
114
115 // Store anchors (offset and offset+size) for symbols.
116 for (auto *Sym : S.symbols()) {
117 if (!Sym->isDefined() || &Sym->getBlock() != B)
118 continue;
119
120 BlockAux.Anchors.push_back({Sym->getOffset(), Sym, false});
121 BlockAux.Anchors.push_back(
122 {Sym->getOffset() + Sym->getSize(), Sym, true});
123 }
124 }
125 }
126
127 // Sort anchors by offset so that we can find the closest relocation
128 // efficiently. For a zero size symbol, ensure that its start anchor precedes
129 // its end anchor. For two symbols with anchors at the same offset, their
130 // order does not matter.
131 for (auto &BlockAuxIter : Aux.Blocks) {
132 llvm::sort(BlockAuxIter.second.Anchors, [](auto &A, auto &B) {
133 return std::make_pair(A.Offset, A.End) < std::make_pair(B.Offset, B.End);
134 });
135 }
136
137 return Aux;
138}
139
140static void relaxAlign(orc::ExecutorAddr Loc, const Edge &E, uint32_t &Remove,
141 Edge::Kind &NewEdgeKind) {
142 const uint64_t Addend =
143 !E.getTarget().isDefined() ? Log2_64(E.getAddend()) + 1 : E.getAddend();
144 const uint64_t AllBytes = (1ULL << (Addend & 0xff)) - 4;
145 const uint64_t Align = 1ULL << (Addend & 0xff);
146 const uint64_t MaxBytes = Addend >> 8;
147 const uint64_t Off = Loc.getValue() & (Align - 1);
148 const uint64_t CurBytes = Off == 0 ? 0 : Align - Off;
149 // All bytes beyond the alignment boundary should be removed.
150 // If emit bytes more than max bytes to emit, remove all.
151 if (MaxBytes != 0 && CurBytes > MaxBytes)
152 Remove = AllBytes;
153 else
154 Remove = AllBytes - CurBytes;
155
156 assert(static_cast<int32_t>(Remove) >= 0 &&
157 "R_LARCH_ALIGN needs expanding the content");
158 NewEdgeKind = AlignRelaxable;
159}
160
161static bool relaxBlock(LinkGraph &G, Block &Block, BlockRelaxAux &Aux) {
162 const auto BlockAddr = Block.getAddress();
163 bool Changed = false;
164 ArrayRef<SymbolAnchor> SA = ArrayRef(Aux.Anchors);
165 uint32_t Delta = 0;
166
167 Aux.EdgeKinds.assign(Aux.EdgeKinds.size(), Edge::Invalid);
168 Aux.Writes.clear();
169
170 for (auto [I, E] : llvm::enumerate(Aux.RelaxEdges)) {
171 const auto Loc = BlockAddr + E->getOffset() - Delta;
172 auto &Cur = Aux.RelocDeltas[I];
173 uint32_t Remove = 0;
174 switch (E->getKind()) {
175 case AlignRelaxable:
176 relaxAlign(Loc, *E, Remove, Aux.EdgeKinds[I]);
177 break;
178 default:
179 llvm_unreachable("Unexpected relaxable edge kind");
180 }
181
182 // For all anchors whose offsets are <= E->getOffset(), they are preceded by
183 // the previous relocation whose RelocDeltas value equals Delta.
184 // Decrease their offset and update their size.
185 for (; SA.size() && SA[0].Offset <= E->getOffset(); SA = SA.slice(1)) {
186 if (SA[0].End)
187 SA[0].Sym->setSize(SA[0].Offset - Delta - SA[0].Sym->getOffset());
188 else
189 SA[0].Sym->setOffset(SA[0].Offset - Delta);
190 }
191
192 Delta += Remove;
193 if (Delta != Cur) {
194 Cur = Delta;
195 Changed = true;
196 }
197 }
198
199 for (const SymbolAnchor &A : SA) {
200 if (A.End)
201 A.Sym->setSize(A.Offset - Delta - A.Sym->getOffset());
202 else
203 A.Sym->setOffset(A.Offset - Delta);
204 }
205
206 return Changed;
207}
208
209static bool relaxOnce(LinkGraph &G, RelaxAux &Aux) {
210 bool Changed = false;
211
212 for (auto &[B, BlockAux] : Aux.Blocks)
213 Changed |= relaxBlock(G, *B, BlockAux);
214
215 return Changed;
216}
217
218static void finalizeBlockRelax(LinkGraph &G, Block &Block, BlockRelaxAux &Aux) {
219 auto Contents = Block.getAlreadyMutableContent();
220 auto *Dest = Contents.data();
221 uint32_t Offset = 0;
222 uint32_t Delta = 0;
223
224 // Update section content: remove NOPs for R_LARCH_ALIGN and rewrite
225 // instructions for relaxed relocations.
226 for (auto [I, E] : llvm::enumerate(Aux.RelaxEdges)) {
227 uint32_t Remove = Aux.RelocDeltas[I] - Delta;
228 Delta = Aux.RelocDeltas[I];
229 if (Remove == 0 && Aux.EdgeKinds[I] == Edge::Invalid)
230 continue;
231
232 // Copy from last location to the current relocated location.
233 const auto Size = E->getOffset() - Offset;
234 std::memmove(Dest, Contents.data() + Offset, Size);
235 Dest += Size;
236 Offset = E->getOffset() + Remove;
237 }
238
239 std::memmove(Dest, Contents.data() + Offset, Contents.size() - Offset);
240
241 // Fixup edge offsets and kinds.
242 Delta = 0;
243 size_t I = 0;
244 for (auto &E : Block.edges()) {
245 E.setOffset(E.getOffset() - Delta);
246
247 if (I < Aux.RelaxEdges.size() && Aux.RelaxEdges[I] == &E) {
248 if (Aux.EdgeKinds[I] != Edge::Invalid)
249 E.setKind(Aux.EdgeKinds[I]);
250
251 Delta = Aux.RelocDeltas[I];
252 ++I;
253 }
254 }
255
256 // Remove AlignRelaxable edges: all other relaxable edges got modified and
257 // will be used later while linking. Alignment is entirely handled here so we
258 // don't need these edges anymore.
259 for (auto IE = Block.edges().begin(); IE != Block.edges().end();) {
260 if (IE->getKind() == AlignRelaxable)
261 IE = Block.removeEdge(IE);
262 else
263 ++IE;
264 }
265}
266
267static void finalizeRelax(LinkGraph &G, RelaxAux &Aux) {
268 for (auto &[B, BlockAux] : Aux.Blocks)
269 finalizeBlockRelax(G, *B, BlockAux);
270}
271
272static Error relax(LinkGraph &G) {
273 auto Aux = initRelaxAux(G);
274 while (relaxOnce(G, Aux)) {
275 }
276 finalizeRelax(G, Aux);
277 return Error::success();
278}
279
280template <typename ELFT>
281class ELFLinkGraphBuilder_loongarch : public ELFLinkGraphBuilder<ELFT> {
282private:
284 getRelocationKind(const uint32_t Type) {
285 using namespace loongarch;
286 switch (Type) {
287 case ELF::R_LARCH_64:
288 return Pointer64;
289 case ELF::R_LARCH_32:
290 return Pointer32;
291 case ELF::R_LARCH_32_PCREL:
292 return Delta32;
293 case ELF::R_LARCH_B16:
294 return Branch16PCRel;
295 case ELF::R_LARCH_B21:
296 return Branch21PCRel;
297 case ELF::R_LARCH_B26:
298 return Branch26PCRel;
299 case ELF::R_LARCH_PCALA_HI20:
300 return Page20;
301 case ELF::R_LARCH_PCALA_LO12:
302 return PageOffset12;
303 case ELF::R_LARCH_GOT_PC_HI20:
305 case ELF::R_LARCH_GOT_PC_LO12:
307 case ELF::R_LARCH_CALL36:
308 return Call36PCRel;
309 case ELF::R_LARCH_ADD6:
310 return Add6;
311 case ELF::R_LARCH_ADD8:
312 return Add8;
313 case ELF::R_LARCH_ADD16:
314 return Add16;
315 case ELF::R_LARCH_ADD32:
316 return Add32;
317 case ELF::R_LARCH_ADD64:
318 return Add64;
319 case ELF::R_LARCH_ADD_ULEB128:
320 return AddUleb128;
321 case ELF::R_LARCH_SUB6:
322 return Sub6;
323 case ELF::R_LARCH_SUB8:
324 return Sub8;
325 case ELF::R_LARCH_SUB16:
326 return Sub16;
327 case ELF::R_LARCH_SUB32:
328 return Sub32;
329 case ELF::R_LARCH_SUB64:
330 return Sub64;
331 case ELF::R_LARCH_SUB_ULEB128:
332 return SubUleb128;
333 case ELF::R_LARCH_ALIGN:
334 return AlignRelaxable;
335 }
336
337 return make_error<JITLinkError>(
338 "Unsupported loongarch relocation:" + formatv("{0:d}: ", Type) +
340 }
341
342 EdgeKind_loongarch getRelaxableRelocationKind(EdgeKind_loongarch Kind) {
343 // TODO: Implement more. Just ignore all relaxations now.
344 return Kind;
345 }
346
347 Error addRelocations() override {
348 LLVM_DEBUG(dbgs() << "Processing relocations:\n");
349
351 using Self = ELFLinkGraphBuilder_loongarch<ELFT>;
352 for (const auto &RelSect : Base::Sections)
353 if (Error Err = Base::forEachRelaRelocation(RelSect, this,
354 &Self::addSingleRelocation))
355 return Err;
356
357 return Error::success();
358 }
359
360 Error addSingleRelocation(const typename ELFT::Rela &Rel,
361 const typename ELFT::Shdr &FixupSect,
362 Block &BlockToFix) {
364
365 uint32_t Type = Rel.getType(false);
366 int64_t Addend = Rel.r_addend;
367
368 if (Type == ELF::R_LARCH_RELAX) {
369 if (BlockToFix.edges_empty())
370 return make_error<StringError>(
371 "R_LARCH_RELAX without preceding relocation",
373
374 auto &PrevEdge = *std::prev(BlockToFix.edges().end());
375 auto Kind = static_cast<EdgeKind_loongarch>(PrevEdge.getKind());
376 PrevEdge.setKind(getRelaxableRelocationKind(Kind));
377 return Error::success();
378 }
379
380 Expected<loongarch::EdgeKind_loongarch> Kind = getRelocationKind(Type);
381 if (!Kind)
382 return Kind.takeError();
383
384 uint32_t SymbolIndex = Rel.getSymbol(false);
385 auto ObjSymbol = Base::Obj.getRelocationSymbol(Rel, Base::SymTabSec);
386 if (!ObjSymbol)
387 return ObjSymbol.takeError();
388
389 Symbol *GraphSymbol = Base::getGraphSymbol(SymbolIndex);
390 if (!GraphSymbol)
391 return make_error<StringError>(
392 formatv("Could not find symbol at given index, did you add it to "
393 "JITSymbolTable? index: {0}, shndx: {1} Size of table: {2}",
394 SymbolIndex, (*ObjSymbol)->st_shndx,
395 Base::GraphSymbols.size()),
397
398 auto FixupAddress = orc::ExecutorAddr(FixupSect.sh_addr) + Rel.r_offset;
399 Edge::OffsetT Offset = FixupAddress - BlockToFix.getAddress();
400 Edge GE(*Kind, Offset, *GraphSymbol, Addend);
401 LLVM_DEBUG({
402 dbgs() << " ";
403 printEdge(dbgs(), BlockToFix, GE, loongarch::getEdgeKindName(*Kind));
404 dbgs() << "\n";
405 });
406
407 BlockToFix.addEdge(std::move(GE));
408
409 return Error::success();
410 }
411
412public:
413 ELFLinkGraphBuilder_loongarch(StringRef FileName,
414 const object::ELFFile<ELFT> &Obj,
415 std::shared_ptr<orc::SymbolStringPool> SSP,
416 Triple TT, SubtargetFeatures Features)
417 : ELFLinkGraphBuilder<ELFT>(Obj, std::move(SSP), std::move(TT),
418 std::move(Features), FileName,
420};
421
422Error buildTables_ELF_loongarch(LinkGraph &G) {
423 LLVM_DEBUG(dbgs() << "Visiting edges in graph:\n");
424
425 GOTTableManager GOT;
426 PLTTableManager PLT(GOT);
427 visitExistingEdges(G, GOT, PLT);
428 return Error::success();
429}
430
431} // namespace
432
433namespace llvm {
434namespace jitlink {
435
437 MemoryBufferRef ObjectBuffer, std::shared_ptr<orc::SymbolStringPool> SSP) {
438 LLVM_DEBUG({
439 dbgs() << "Building jitlink graph for new input "
440 << ObjectBuffer.getBufferIdentifier() << "...\n";
441 });
442
443 auto ELFObj = object::ObjectFile::createELFObjectFile(ObjectBuffer);
444 if (!ELFObj)
445 return ELFObj.takeError();
446
447 auto Features = (*ELFObj)->getFeatures();
448 if (!Features)
449 return Features.takeError();
450
451 if ((*ELFObj)->getArch() == Triple::loongarch64) {
452 auto &ELFObjFile = cast<object::ELFObjectFile<object::ELF64LE>>(**ELFObj);
453 return ELFLinkGraphBuilder_loongarch<object::ELF64LE>(
454 (*ELFObj)->getFileName(), ELFObjFile.getELFFile(),
455 std::move(SSP), (*ELFObj)->makeTriple(), std::move(*Features))
456 .buildGraph();
457 }
458
459 assert((*ELFObj)->getArch() == Triple::loongarch32 &&
460 "Invalid triple for LoongArch ELF object file");
461 auto &ELFObjFile = cast<object::ELFObjectFile<object::ELF32LE>>(**ELFObj);
462 return ELFLinkGraphBuilder_loongarch<object::ELF32LE>(
463 (*ELFObj)->getFileName(), ELFObjFile.getELFFile(), std::move(SSP),
464 (*ELFObj)->makeTriple(), std::move(*Features))
465 .buildGraph();
466}
467
468void link_ELF_loongarch(std::unique_ptr<LinkGraph> G,
469 std::unique_ptr<JITLinkContext> Ctx) {
471 const Triple &TT = G->getTargetTriple();
472 if (Ctx->shouldAddDefaultTargetPasses(TT)) {
473 // Add eh-frame passes.
474 Config.PrePrunePasses.push_back(DWARFRecordSectionSplitter(".eh_frame"));
475 Config.PrePrunePasses.push_back(
476 EHFrameEdgeFixer(".eh_frame", G->getPointerSize(), Pointer32, Pointer64,
478 Config.PrePrunePasses.push_back(EHFrameNullTerminator(".eh_frame"));
479
480 // Add a mark-live pass.
481 if (auto MarkLive = Ctx->getMarkLivePass(TT))
482 Config.PrePrunePasses.push_back(std::move(MarkLive));
483 else
484 Config.PrePrunePasses.push_back(markAllSymbolsLive);
485
486 // Add an in-place GOT/PLTStubs build pass.
487 Config.PostPrunePasses.push_back(buildTables_ELF_loongarch);
488
489 // Add a linker relaxation pass.
490 Config.PostAllocationPasses.push_back(relax);
491 }
492
493 if (auto Err = Ctx->modifyPassConfig(*G, Config))
494 return Ctx->notifyFailed(std::move(Err));
495
496 ELFJITLinker_loongarch::link(std::move(Ctx), std::move(G), std::move(Config));
497}
498
500
501} // namespace jitlink
502} // namespace llvm
static GCRegistry::Add< OcamlGC > B("ocaml", "ocaml 3.10-compatible GC")
static GCRegistry::Add< ErlangGC > A("erlang", "erlang-compatible garbage collector")
static GCRegistry::Add< CoreCLRGC > E("coreclr", "CoreCLR-compatible GC")
#define LLVM_DEBUG(...)
Definition: Debug.h:106
uint64_t Size
bool End
Definition: ELF_riscv.cpp:480
RelaxConfig Config
Definition: ELF_riscv.cpp:506
DenseMap< Block *, BlockRelaxAux > Blocks
Definition: ELF_riscv.cpp:507
SmallVector< uint32_t, 0 > Writes
Definition: ELF_riscv.cpp:497
SmallVector< uint32_t, 0 > RelocDeltas
Definition: ELF_riscv.cpp:492
SmallVector< Edge *, 0 > RelaxEdges
Definition: ELF_riscv.cpp:489
Symbol * Sym
Definition: ELF_riscv.cpp:479
SmallVector< Edge::Kind, 0 > EdgeKinds
Definition: ELF_riscv.cpp:494
SmallVector< SymbolAnchor, 0 > Anchors
Definition: ELF_riscv.cpp:486
#define I(x, y, z)
Definition: MD5.cpp:58
#define G(x, y, z)
Definition: MD5.cpp:56
assert(ImpDefSCC.getReg()==AMDGPU::SCC &&ImpDefSCC.isDef())
ArrayRef - Represent a constant reference to an array (0 or more elements consecutively in memory),...
Definition: ArrayRef.h:41
size_t size() const
size - Get the array size.
Definition: ArrayRef.h:168
ArrayRef< T > slice(size_t N, size_t M) const
slice(n, m) - Chop off the first N elements of the array, and keep M elements in the array.
Definition: ArrayRef.h:198
Lightweight error class with error context and mandatory checking.
Definition: Error.h:160
static ErrorSuccess success()
Create a success value.
Definition: Error.h:337
Tagged union holding either a T or a Error.
Definition: Error.h:481
StringRef getBufferIdentifier() const
T * data() const
Definition: ArrayRef.h:357
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
Manages the enabling and disabling of subtarget specific features.
Triple - Helper class for working with autoconf configuration names.
Definition: Triple.h:44
@ loongarch32
Definition: Triple.h:61
@ loongarch64
Definition: Triple.h:62
The instances of the Type class are immutable: once they are created, they are never changed.
Definition: Type.h:45
static Expected< std::unique_ptr< ObjectFile > > createELFObjectFile(MemoryBufferRef Object, bool InitContent=true)
Represents an address in the executor process.
uint64_t getValue() const
unique_function is a type-erasing functor similar to std::function.
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
@ EM_LOONGARCH
Definition: ELF.h:325
StringRef getELFRelocationTypeName(uint32_t Machine, uint32_t Type)
Definition: ELF.cpp:24
This is an optimization pass for GlobalISel generic memory operations.
Definition: AddressRanges.h:18
@ Offset
Definition: DWP.cpp:480
auto enumerate(FirstRange &&First, RestRanges &&...Rest)
Given two or more input ranges, returns a new range whose values are tuples (A, B,...
Definition: STLExtras.h:2448
std::error_code inconvertibleErrorCode()
The value returned by this function can be returned from convertToErrorCode for Error values where no...
Definition: Error.cpp:98
unsigned Log2_64(uint64_t Value)
Return the floor log base 2 of the specified value, -1 if the value is zero.
Definition: MathExtras.h:348
static Error getOffset(const SymbolRef &Sym, SectionRef Sec, uint64_t &Result)
auto formatv(bool Validate, const char *Fmt, Ts &&...Vals)
void sort(IteratorTy Start, IteratorTy End)
Definition: STLExtras.h:1664
raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
Definition: Debug.cpp:163
This struct is a compact representation of a valid (non-zero power of two) alignment.
Definition: Alignment.h:39