1 | //===--------- ELF_x86.cpp - JIT linker implementation for ELF/x86 --------===// |
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/x86 jit-link implementation. |
10 | // |
11 | //===----------------------------------------------------------------------===// |
12 | |
13 | #include "llvm/ExecutionEngine/JITLink/ELF_x86.h" |
14 | #include "DefineExternalSectionStartAndEndSymbols.h" |
15 | #include "ELFLinkGraphBuilder.h" |
16 | #include "JITLinkGeneric.h" |
17 | #include "llvm/BinaryFormat/ELF.h" |
18 | #include "llvm/ExecutionEngine/JITLink/x86.h" |
19 | #include "llvm/Object/ELFObjectFile.h" |
20 | |
21 | #define DEBUG_TYPE "jitlink" |
22 | |
23 | using namespace llvm; |
24 | using namespace llvm::jitlink; |
25 | |
26 | namespace { |
27 | constexpr StringRef ELFGOTSymbolName = "_GLOBAL_OFFSET_TABLE_" ; |
28 | |
29 | Error buildTables_ELF_x86(LinkGraph &G) { |
30 | LLVM_DEBUG(dbgs() << "Visiting edges in graph:\n" ); |
31 | |
32 | x86::GOTTableManager GOT; |
33 | x86::PLTTableManager PLT(GOT); |
34 | visitExistingEdges(G, Vs&: GOT, Vs&: PLT); |
35 | return Error::success(); |
36 | } |
37 | } // namespace |
38 | |
39 | namespace llvm::jitlink { |
40 | |
41 | class ELFJITLinker_x86 : public JITLinker<ELFJITLinker_x86> { |
42 | friend class JITLinker<ELFJITLinker_x86>; |
43 | |
44 | public: |
45 | ELFJITLinker_x86(std::unique_ptr<JITLinkContext> Ctx, |
46 | std::unique_ptr<LinkGraph> G, PassConfiguration PassConfig) |
47 | : JITLinker(std::move(Ctx), std::move(G), std::move(PassConfig)) { |
48 | getPassConfig().PostAllocationPasses.push_back( |
49 | x: [this](LinkGraph &G) { return getOrCreateGOTSymbol(G); }); |
50 | } |
51 | |
52 | private: |
53 | Symbol *GOTSymbol = nullptr; |
54 | |
55 | Error getOrCreateGOTSymbol(LinkGraph &G) { |
56 | auto DefineExternalGOTSymbolIfPresent = |
57 | createDefineExternalSectionStartAndEndSymbolsPass( |
58 | F: [&](LinkGraph &LG, Symbol &Sym) -> SectionRangeSymbolDesc { |
59 | if (Sym.getName() != nullptr && |
60 | *Sym.getName() == ELFGOTSymbolName) |
61 | if (auto *GOTSection = G.findSectionByName( |
62 | Name: x86::GOTTableManager::getSectionName())) { |
63 | GOTSymbol = &Sym; |
64 | return {*GOTSection, true}; |
65 | } |
66 | return {}; |
67 | }); |
68 | |
69 | // Try to attach _GLOBAL_OFFSET_TABLE_ to the GOT if it's defined as an |
70 | // external. |
71 | if (auto Err = DefineExternalGOTSymbolIfPresent(G)) |
72 | return Err; |
73 | |
74 | // If we succeeded then we're done. |
75 | if (GOTSymbol) |
76 | return Error::success(); |
77 | |
78 | // Otherwise look for a GOT section: If it already has a start symbol we'll |
79 | // record it, otherwise we'll create our own. |
80 | // If there's a GOT section but we didn't find an external GOT symbol... |
81 | if (auto *GOTSection = |
82 | G.findSectionByName(Name: x86::GOTTableManager::getSectionName())) { |
83 | |
84 | // Check for an existing defined symbol. |
85 | for (auto *Sym : GOTSection->symbols()) |
86 | if (Sym->getName() != nullptr && *Sym->getName() == ELFGOTSymbolName) { |
87 | GOTSymbol = Sym; |
88 | return Error::success(); |
89 | } |
90 | |
91 | // If there's no defined symbol then create one. |
92 | SectionRange SR(*GOTSection); |
93 | |
94 | if (SR.empty()) { |
95 | GOTSymbol = |
96 | &G.addAbsoluteSymbol(Name: ELFGOTSymbolName, Address: orc::ExecutorAddr(), Size: 0, |
97 | L: Linkage::Strong, S: Scope::Local, IsLive: true); |
98 | } else { |
99 | GOTSymbol = |
100 | &G.addDefinedSymbol(Content&: *SR.getFirstBlock(), Offset: 0, Name: ELFGOTSymbolName, Size: 0, |
101 | L: Linkage::Strong, S: Scope::Local, IsCallable: false, IsLive: true); |
102 | } |
103 | } |
104 | |
105 | return Error::success(); |
106 | } |
107 | |
108 | Error applyFixup(LinkGraph &G, Block &B, const Edge &E) const { |
109 | return x86::applyFixup(G, B, E, GOTSymbol); |
110 | } |
111 | }; |
112 | |
113 | class ELFLinkGraphBuilder_x86 : public ELFLinkGraphBuilder<object::ELF32LE> { |
114 | private: |
115 | using ELFT = object::ELF32LE; |
116 | |
117 | Expected<x86::EdgeKind_x86> getRelocationKind(const uint32_t Type) { |
118 | switch (Type) { |
119 | case ELF::R_386_32: |
120 | return x86::Pointer32; |
121 | case ELF::R_386_PC32: |
122 | return x86::PCRel32; |
123 | case ELF::R_386_16: |
124 | return x86::Pointer16; |
125 | case ELF::R_386_PC16: |
126 | return x86::PCRel16; |
127 | case ELF::R_386_GOT32: |
128 | return x86::RequestGOTAndTransformToDelta32FromGOT; |
129 | case ELF::R_386_GOT32X: |
130 | // TODO: Add a relaxable edge kind and update relaxation optimization. |
131 | return x86::RequestGOTAndTransformToDelta32FromGOT; |
132 | case ELF::R_386_GOTPC: |
133 | return x86::Delta32; |
134 | case ELF::R_386_GOTOFF: |
135 | return x86::Delta32FromGOT; |
136 | case ELF::R_386_PLT32: |
137 | return x86::BranchPCRel32; |
138 | } |
139 | |
140 | return make_error<JITLinkError>( |
141 | Args: "In " + G->getName() + ": Unsupported x86 relocation type " + |
142 | object::getELFRelocationTypeName(Machine: ELF::EM_386, Type)); |
143 | } |
144 | |
145 | Error addRelocations() override { |
146 | LLVM_DEBUG(dbgs() << "Adding relocations\n" ); |
147 | using Base = ELFLinkGraphBuilder<ELFT>; |
148 | using Self = ELFLinkGraphBuilder_x86; |
149 | |
150 | for (const auto &RelSect : Base::Sections) { |
151 | // Validate the section to read relocation entries from. |
152 | if (RelSect.sh_type == ELF::SHT_RELA) |
153 | return make_error<StringError>( |
154 | Args: "No SHT_RELA in valid x86 ELF object files" , |
155 | Args: inconvertibleErrorCode()); |
156 | |
157 | if (Error Err = Base::forEachRelRelocation(RelSect, Instance: this, |
158 | Method: &Self::addSingleRelocation)) |
159 | return Err; |
160 | } |
161 | |
162 | return Error::success(); |
163 | } |
164 | |
165 | Error addSingleRelocation(const typename ELFT::Rel &Rel, |
166 | const typename ELFT::Shdr &FixupSection, |
167 | Block &BlockToFix) { |
168 | using Base = ELFLinkGraphBuilder<ELFT>; |
169 | |
170 | auto ELFReloc = Rel.getType(isMips64EL: false); |
171 | |
172 | // R_386_NONE is a no-op. |
173 | if (LLVM_UNLIKELY(ELFReloc == ELF::R_386_NONE)) |
174 | return Error::success(); |
175 | |
176 | uint32_t SymbolIndex = Rel.getSymbol(isMips64EL: false); |
177 | auto ObjSymbol = Base::Obj.getRelocationSymbol(Rel, SymTab: Base::SymTabSec); |
178 | if (!ObjSymbol) |
179 | return ObjSymbol.takeError(); |
180 | |
181 | Symbol *GraphSymbol = Base::getGraphSymbol(SymIndex: SymbolIndex); |
182 | if (!GraphSymbol) |
183 | return make_error<StringError>( |
184 | Args: formatv(Fmt: "Could not find symbol at given index, did you add it to " |
185 | "JITSymbolTable? index: {0}, shndx: {1} Size of table: {2}" , |
186 | Vals&: SymbolIndex, Vals: (*ObjSymbol)->st_shndx, |
187 | Vals: Base::GraphSymbols.size()), |
188 | Args: inconvertibleErrorCode()); |
189 | |
190 | Expected<x86::EdgeKind_x86> Kind = getRelocationKind(Type: ELFReloc); |
191 | if (!Kind) |
192 | return Kind.takeError(); |
193 | |
194 | auto FixupAddress = orc::ExecutorAddr(FixupSection.sh_addr) + Rel.r_offset; |
195 | int64_t Addend = 0; |
196 | |
197 | switch (*Kind) { |
198 | case x86::Pointer32: |
199 | case x86::PCRel32: |
200 | case x86::RequestGOTAndTransformToDelta32FromGOT: |
201 | case x86::Delta32: |
202 | case x86::Delta32FromGOT: |
203 | case x86::BranchPCRel32: |
204 | case x86::BranchPCRel32ToPtrJumpStub: |
205 | case x86::BranchPCRel32ToPtrJumpStubBypassable: { |
206 | const char *FixupContent = BlockToFix.getContent().data() + |
207 | (FixupAddress - BlockToFix.getAddress()); |
208 | Addend = *(const support::little32_t *)FixupContent; |
209 | break; |
210 | } |
211 | case x86::Pointer16: |
212 | case x86::PCRel16: { |
213 | const char *FixupContent = BlockToFix.getContent().data() + |
214 | (FixupAddress - BlockToFix.getAddress()); |
215 | Addend = *(const support::little16_t *)FixupContent; |
216 | break; |
217 | } |
218 | } |
219 | |
220 | Edge::OffsetT Offset = FixupAddress - BlockToFix.getAddress(); |
221 | Edge GE(*Kind, Offset, *GraphSymbol, Addend); |
222 | LLVM_DEBUG({ |
223 | dbgs() << " " ; |
224 | printEdge(dbgs(), BlockToFix, GE, x86::getEdgeKindName(*Kind)); |
225 | dbgs() << "\n" ; |
226 | }); |
227 | |
228 | BlockToFix.addEdge(E: std::move(GE)); |
229 | return Error::success(); |
230 | } |
231 | |
232 | public: |
233 | ELFLinkGraphBuilder_x86(StringRef FileName, const object::ELFFile<ELFT> &Obj, |
234 | std::shared_ptr<orc::SymbolStringPool> SSP, Triple TT, |
235 | SubtargetFeatures Features) |
236 | : ELFLinkGraphBuilder<ELFT>(Obj, std::move(SSP), std::move(TT), |
237 | std::move(Features), FileName, |
238 | x86::getEdgeKindName) {} |
239 | }; |
240 | |
241 | Expected<std::unique_ptr<LinkGraph>> |
242 | createLinkGraphFromELFObject_x86(MemoryBufferRef ObjectBuffer, |
243 | std::shared_ptr<orc::SymbolStringPool> SSP) { |
244 | LLVM_DEBUG({ |
245 | dbgs() << "Building jitlink graph for new input " |
246 | << ObjectBuffer.getBufferIdentifier() << "...\n" ; |
247 | }); |
248 | |
249 | auto ELFObj = object::ObjectFile::createELFObjectFile(Object: ObjectBuffer); |
250 | if (!ELFObj) |
251 | return ELFObj.takeError(); |
252 | |
253 | auto Features = (*ELFObj)->getFeatures(); |
254 | if (!Features) |
255 | return Features.takeError(); |
256 | |
257 | assert((*ELFObj)->getArch() == Triple::x86 && |
258 | "Only x86 (little endian) is supported for now" ); |
259 | |
260 | auto &ELFObjFile = cast<object::ELFObjectFile<object::ELF32LE>>(Val&: **ELFObj); |
261 | |
262 | return ELFLinkGraphBuilder_x86((*ELFObj)->getFileName(), |
263 | ELFObjFile.getELFFile(), std::move(SSP), |
264 | (*ELFObj)->makeTriple(), std::move(*Features)) |
265 | .buildGraph(); |
266 | } |
267 | |
268 | void link_ELF_x86(std::unique_ptr<LinkGraph> G, |
269 | std::unique_ptr<JITLinkContext> Ctx) { |
270 | PassConfiguration Config; |
271 | const Triple &TT = G->getTargetTriple(); |
272 | if (Ctx->shouldAddDefaultTargetPasses(TT)) { |
273 | if (auto MarkLive = Ctx->getMarkLivePass(TT)) |
274 | Config.PrePrunePasses.push_back(x: std::move(MarkLive)); |
275 | else |
276 | Config.PrePrunePasses.push_back(x: markAllSymbolsLive); |
277 | |
278 | // Add an in-place GOT and PLT build pass. |
279 | Config.PostPrunePasses.push_back(x: buildTables_ELF_x86); |
280 | |
281 | // Add GOT/Stubs optimizer pass. |
282 | Config.PreFixupPasses.push_back(x: x86::optimizeGOTAndStubAccesses); |
283 | } |
284 | if (auto Err = Ctx->modifyPassConfig(G&: *G, Config)) |
285 | return Ctx->notifyFailed(Err: std::move(Err)); |
286 | |
287 | ELFJITLinker_x86::link(Args: std::move(Ctx), Args: std::move(G), Args: std::move(Config)); |
288 | } |
289 | |
290 | } // namespace llvm::jitlink |
291 | |