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
23using namespace llvm;
24using namespace llvm::jitlink;
25
26namespace {
27constexpr StringRef ELFGOTSymbolName = "_GLOBAL_OFFSET_TABLE_";
28
29Error 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
39namespace llvm::jitlink {
40
41class ELFJITLinker_x86 : public JITLinker<ELFJITLinker_x86> {
42 friend class JITLinker<ELFJITLinker_x86>;
43
44public:
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
52private:
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
113class ELFLinkGraphBuilder_x86 : public ELFLinkGraphBuilder<object::ELF32LE> {
114private:
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
232public:
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
241Expected<std::unique_ptr<LinkGraph>>
242createLinkGraphFromELFObject_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
268void 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