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 | |
23 | using namespace llvm; |
24 | using namespace llvm::jitlink; |
25 | |
26 | namespace { |
27 | constexpr StringRef ELFGOTSymbolName = "_GLOBAL_OFFSET_TABLE_" ; |
28 | |
29 | Error 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 | |
39 | namespace llvm::jitlink { |
40 | |
41 | class ELFJITLinker_i386 : public JITLinker<ELFJITLinker_i386> { |
42 | friend class JITLinker<ELFJITLinker_i386>; |
43 | |
44 | public: |
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 | |
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() == 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 | |
112 | template <typename ELFT> |
113 | class ELFLinkGraphBuilder_i386 : public ELFLinkGraphBuilder<ELFT> { |
114 | private: |
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 | |
211 | public: |
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 | |
218 | Expected<std::unique_ptr<LinkGraph>> |
219 | createLinkGraphFromELFObject_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 | |
243 | void 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 | |