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