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 | |
13 | #include "llvm/ExecutionEngine/JITLink/ELF_loongarch.h" |
14 | #include "llvm/BinaryFormat/ELF.h" |
15 | #include "llvm/ExecutionEngine/JITLink/DWARFRecordSectionSplitter.h" |
16 | #include "llvm/ExecutionEngine/JITLink/JITLink.h" |
17 | #include "llvm/ExecutionEngine/JITLink/loongarch.h" |
18 | #include "llvm/Object/ELF.h" |
19 | #include "llvm/Object/ELFObjectFile.h" |
20 | |
21 | #include "EHFrameSupportImpl.h" |
22 | #include "ELFLinkGraphBuilder.h" |
23 | #include "JITLinkGeneric.h" |
24 | |
25 | #define DEBUG_TYPE "jitlink" |
26 | |
27 | using namespace llvm; |
28 | using namespace llvm::jitlink; |
29 | using namespace llvm::jitlink::loongarch; |
30 | |
31 | namespace { |
32 | |
33 | class ELFJITLinker_loongarch : public JITLinker<ELFJITLinker_loongarch> { |
34 | friend class JITLinker<ELFJITLinker_loongarch>; |
35 | |
36 | public: |
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 | |
42 | private: |
43 | Error applyFixup(LinkGraph &G, Block &B, const Edge &E) const { |
44 | return loongarch::applyFixup(G, B, E); |
45 | } |
46 | }; |
47 | |
48 | template <typename ELFT> |
49 | class ELFLinkGraphBuilder_loongarch : public ELFLinkGraphBuilder<ELFT> { |
50 | private: |
51 | static Expected<loongarch::EdgeKind_loongarch> |
52 | getRelocationKind(const uint32_t Type) { |
53 | using namespace loongarch; |
54 | switch (Type) { |
55 | case ELF::R_LARCH_64: |
56 | return Pointer64; |
57 | case ELF::R_LARCH_32: |
58 | return Pointer32; |
59 | case ELF::R_LARCH_32_PCREL: |
60 | return Delta32; |
61 | case ELF::R_LARCH_B26: |
62 | return Branch26PCRel; |
63 | case ELF::R_LARCH_PCALA_HI20: |
64 | return Page20; |
65 | case ELF::R_LARCH_PCALA_LO12: |
66 | return PageOffset12; |
67 | case ELF::R_LARCH_GOT_PC_HI20: |
68 | return RequestGOTAndTransformToPage20; |
69 | case ELF::R_LARCH_GOT_PC_LO12: |
70 | return RequestGOTAndTransformToPageOffset12; |
71 | } |
72 | |
73 | return make_error<JITLinkError>( |
74 | Args: "Unsupported loongarch relocation:" + formatv(Fmt: "{0:d}: " , Vals: Type) + |
75 | object::getELFRelocationTypeName(Machine: ELF::EM_LOONGARCH, Type)); |
76 | } |
77 | |
78 | Error addRelocations() override { |
79 | LLVM_DEBUG(dbgs() << "Processing relocations:\n" ); |
80 | |
81 | using Base = ELFLinkGraphBuilder<ELFT>; |
82 | using Self = ELFLinkGraphBuilder_loongarch<ELFT>; |
83 | for (const auto &RelSect : Base::Sections) |
84 | if (Error Err = Base::forEachRelaRelocation(RelSect, this, |
85 | &Self::addSingleRelocation)) |
86 | return Err; |
87 | |
88 | return Error::success(); |
89 | } |
90 | |
91 | Error addSingleRelocation(const typename ELFT::Rela &Rel, |
92 | const typename ELFT::Shdr &FixupSect, |
93 | Block &BlockToFix) { |
94 | using Base = ELFLinkGraphBuilder<ELFT>; |
95 | |
96 | uint32_t SymbolIndex = Rel.getSymbol(false); |
97 | auto ObjSymbol = Base::Obj.getRelocationSymbol(Rel, Base::SymTabSec); |
98 | if (!ObjSymbol) |
99 | return ObjSymbol.takeError(); |
100 | |
101 | Symbol *GraphSymbol = Base::getGraphSymbol(SymbolIndex); |
102 | if (!GraphSymbol) |
103 | return make_error<StringError>( |
104 | formatv("Could not find symbol at given index, did you add it to " |
105 | "JITSymbolTable? index: {0}, shndx: {1} Size of table: {2}" , |
106 | SymbolIndex, (*ObjSymbol)->st_shndx, |
107 | Base::GraphSymbols.size()), |
108 | inconvertibleErrorCode()); |
109 | |
110 | uint32_t Type = Rel.getType(false); |
111 | Expected<loongarch::EdgeKind_loongarch> Kind = getRelocationKind(Type); |
112 | if (!Kind) |
113 | return Kind.takeError(); |
114 | |
115 | int64_t Addend = Rel.r_addend; |
116 | auto FixupAddress = orc::ExecutorAddr(FixupSect.sh_addr) + Rel.r_offset; |
117 | Edge::OffsetT Offset = FixupAddress - BlockToFix.getAddress(); |
118 | Edge GE(*Kind, Offset, *GraphSymbol, Addend); |
119 | LLVM_DEBUG({ |
120 | dbgs() << " " ; |
121 | printEdge(dbgs(), BlockToFix, GE, loongarch::getEdgeKindName(*Kind)); |
122 | dbgs() << "\n" ; |
123 | }); |
124 | |
125 | BlockToFix.addEdge(E: std::move(GE)); |
126 | |
127 | return Error::success(); |
128 | } |
129 | |
130 | public: |
131 | ELFLinkGraphBuilder_loongarch(StringRef FileName, |
132 | const object::ELFFile<ELFT> &Obj, Triple TT, |
133 | SubtargetFeatures Features) |
134 | : ELFLinkGraphBuilder<ELFT>(Obj, std::move(TT), std::move(Features), |
135 | FileName, loongarch::getEdgeKindName) {} |
136 | }; |
137 | |
138 | Error buildTables_ELF_loongarch(LinkGraph &G) { |
139 | LLVM_DEBUG(dbgs() << "Visiting edges in graph:\n" ); |
140 | |
141 | GOTTableManager GOT; |
142 | PLTTableManager PLT(GOT); |
143 | visitExistingEdges(G, Vs&: GOT, Vs&: PLT); |
144 | return Error::success(); |
145 | } |
146 | |
147 | } // namespace |
148 | |
149 | namespace llvm { |
150 | namespace jitlink { |
151 | |
152 | Expected<std::unique_ptr<LinkGraph>> |
153 | createLinkGraphFromELFObject_loongarch(MemoryBufferRef ObjectBuffer) { |
154 | LLVM_DEBUG({ |
155 | dbgs() << "Building jitlink graph for new input " |
156 | << ObjectBuffer.getBufferIdentifier() << "...\n" ; |
157 | }); |
158 | |
159 | auto ELFObj = object::ObjectFile::createELFObjectFile(Object: ObjectBuffer); |
160 | if (!ELFObj) |
161 | return ELFObj.takeError(); |
162 | |
163 | auto Features = (*ELFObj)->getFeatures(); |
164 | if (!Features) |
165 | return Features.takeError(); |
166 | |
167 | if ((*ELFObj)->getArch() == Triple::loongarch64) { |
168 | auto &ELFObjFile = cast<object::ELFObjectFile<object::ELF64LE>>(Val&: **ELFObj); |
169 | return ELFLinkGraphBuilder_loongarch<object::ELF64LE>( |
170 | (*ELFObj)->getFileName(), ELFObjFile.getELFFile(), |
171 | (*ELFObj)->makeTriple(), std::move(*Features)) |
172 | .buildGraph(); |
173 | } |
174 | |
175 | assert((*ELFObj)->getArch() == Triple::loongarch32 && |
176 | "Invalid triple for LoongArch ELF object file" ); |
177 | auto &ELFObjFile = cast<object::ELFObjectFile<object::ELF32LE>>(Val&: **ELFObj); |
178 | return ELFLinkGraphBuilder_loongarch<object::ELF32LE>( |
179 | (*ELFObj)->getFileName(), ELFObjFile.getELFFile(), |
180 | (*ELFObj)->makeTriple(), std::move(*Features)) |
181 | .buildGraph(); |
182 | } |
183 | |
184 | void link_ELF_loongarch(std::unique_ptr<LinkGraph> G, |
185 | std::unique_ptr<JITLinkContext> Ctx) { |
186 | PassConfiguration Config; |
187 | const Triple &TT = G->getTargetTriple(); |
188 | if (Ctx->shouldAddDefaultTargetPasses(TT)) { |
189 | // Add eh-frame passes. |
190 | Config.PrePrunePasses.push_back(x: DWARFRecordSectionSplitter(".eh_frame" )); |
191 | Config.PrePrunePasses.push_back( |
192 | x: EHFrameEdgeFixer(".eh_frame" , G->getPointerSize(), Pointer32, Pointer64, |
193 | Delta32, Delta64, NegDelta32)); |
194 | Config.PrePrunePasses.push_back(x: EHFrameNullTerminator(".eh_frame" )); |
195 | |
196 | // Add a mark-live pass. |
197 | if (auto MarkLive = Ctx->getMarkLivePass(TT)) |
198 | Config.PrePrunePasses.push_back(x: std::move(MarkLive)); |
199 | else |
200 | Config.PrePrunePasses.push_back(x: markAllSymbolsLive); |
201 | |
202 | // Add an in-place GOT/PLTStubs build pass. |
203 | Config.PostPrunePasses.push_back(x: buildTables_ELF_loongarch); |
204 | } |
205 | |
206 | if (auto Err = Ctx->modifyPassConfig(G&: *G, Config)) |
207 | return Ctx->notifyFailed(Err: std::move(Err)); |
208 | |
209 | ELFJITLinker_loongarch::link(Args: std::move(Ctx), Args: std::move(G), Args: std::move(Config)); |
210 | } |
211 | |
212 | } // namespace jitlink |
213 | } // namespace llvm |
214 | |