1 //===------- ELF_riscv.cpp -JIT linker implementation for ELF/riscv -------===//
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/riscv jit-link implementation.
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #include "llvm/ExecutionEngine/JITLink/ELF_riscv.h"
14 #include "ELFLinkGraphBuilder.h"
15 #include "JITLinkGeneric.h"
16 #include "PerGraphGOTAndPLTStubsBuilder.h"
17 #include "llvm/BinaryFormat/ELF.h"
18 #include "llvm/ExecutionEngine/JITLink/JITLink.h"
19 #include "llvm/ExecutionEngine/JITLink/riscv.h"
20 #include "llvm/Object/ELF.h"
21 #include "llvm/Object/ELFObjectFile.h"
22 
23 #define DEBUG_TYPE "jitlink"
24 using namespace llvm;
25 using namespace llvm::jitlink;
26 using namespace llvm::jitlink::riscv;
27 
28 namespace {
29 
30 class PerGraphGOTAndPLTStubsBuilder_ELF_riscv
31     : public PerGraphGOTAndPLTStubsBuilder<
32           PerGraphGOTAndPLTStubsBuilder_ELF_riscv> {
33 public:
34   static constexpr size_t StubEntrySize = 16;
35   static const uint8_t NullGOTEntryContent[8];
36   static const uint8_t RV64StubContent[StubEntrySize];
37   static const uint8_t RV32StubContent[StubEntrySize];
38 
39   using PerGraphGOTAndPLTStubsBuilder<
40       PerGraphGOTAndPLTStubsBuilder_ELF_riscv>::PerGraphGOTAndPLTStubsBuilder;
41 
42   bool isRV64() const { return G.getPointerSize() == 8; }
43 
44   bool isGOTEdgeToFix(Edge &E) const { return E.getKind() == R_RISCV_GOT_HI20; }
45 
46   Symbol &createGOTEntry(Symbol &Target) {
47     Block &GOTBlock =
48         G.createContentBlock(getGOTSection(), getGOTEntryBlockContent(),
49                              orc::ExecutorAddr(), G.getPointerSize(), 0);
50     GOTBlock.addEdge(isRV64() ? R_RISCV_64 : R_RISCV_32, 0, Target, 0);
51     return G.addAnonymousSymbol(GOTBlock, 0, G.getPointerSize(), false, false);
52   }
53 
54   Symbol &createPLTStub(Symbol &Target) {
55     Block &StubContentBlock = G.createContentBlock(
56         getStubsSection(), getStubBlockContent(), orc::ExecutorAddr(), 4, 0);
57     auto &GOTEntrySymbol = getGOTEntry(Target);
58     StubContentBlock.addEdge(R_RISCV_CALL, 0, GOTEntrySymbol, 0);
59     return G.addAnonymousSymbol(StubContentBlock, 0, StubEntrySize, true,
60                                 false);
61   }
62 
63   void fixGOTEdge(Edge &E, Symbol &GOTEntry) {
64     // Replace the relocation pair (R_RISCV_GOT_HI20, R_RISCV_PCREL_LO12)
65     // with (R_RISCV_PCREL_HI20, R_RISCV_PCREL_LO12)
66     // Therefore, here just change the R_RISCV_GOT_HI20 to R_RISCV_PCREL_HI20
67     E.setKind(R_RISCV_PCREL_HI20);
68     E.setTarget(GOTEntry);
69   }
70 
71   void fixPLTEdge(Edge &E, Symbol &PLTStubs) {
72     assert(E.getKind() == R_RISCV_CALL_PLT && "Not a R_RISCV_CALL_PLT edge?");
73     E.setKind(R_RISCV_CALL);
74     E.setTarget(PLTStubs);
75   }
76 
77   bool isExternalBranchEdge(Edge &E) const {
78     return E.getKind() == R_RISCV_CALL_PLT;
79   }
80 
81 private:
82   Section &getGOTSection() const {
83     if (!GOTSection)
84       GOTSection = &G.createSection("$__GOT", MemProt::Read);
85     return *GOTSection;
86   }
87 
88   Section &getStubsSection() const {
89     if (!StubsSection)
90       StubsSection =
91           &G.createSection("$__STUBS", MemProt::Read | MemProt::Exec);
92     return *StubsSection;
93   }
94 
95   ArrayRef<char> getGOTEntryBlockContent() {
96     return {reinterpret_cast<const char *>(NullGOTEntryContent),
97             G.getPointerSize()};
98   }
99 
100   ArrayRef<char> getStubBlockContent() {
101     auto StubContent = isRV64() ? RV64StubContent : RV32StubContent;
102     return {reinterpret_cast<const char *>(StubContent), StubEntrySize};
103   }
104 
105   mutable Section *GOTSection = nullptr;
106   mutable Section *StubsSection = nullptr;
107 };
108 
109 const uint8_t PerGraphGOTAndPLTStubsBuilder_ELF_riscv::NullGOTEntryContent[8] =
110     {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
111 
112 const uint8_t
113     PerGraphGOTAndPLTStubsBuilder_ELF_riscv::RV64StubContent[StubEntrySize] = {
114         0x17, 0x0e, 0x00, 0x00,  // auipc t3, literal
115         0x03, 0x3e, 0x0e, 0x00,  // ld    t3, literal(t3)
116         0x67, 0x00, 0x0e, 0x00,  // jr    t3
117         0x13, 0x00, 0x00, 0x00}; // nop
118 
119 const uint8_t
120     PerGraphGOTAndPLTStubsBuilder_ELF_riscv::RV32StubContent[StubEntrySize] = {
121         0x17, 0x0e, 0x00, 0x00,  // auipc t3, literal
122         0x03, 0x2e, 0x0e, 0x00,  // lw    t3, literal(t3)
123         0x67, 0x00, 0x0e, 0x00,  // jr    t3
124         0x13, 0x00, 0x00, 0x00}; // nop
125 } // namespace
126 namespace llvm {
127 namespace jitlink {
128 
129 static Expected<const Edge &> getRISCVPCRelHi20(const Edge &E) {
130   using namespace riscv;
131   assert((E.getKind() == R_RISCV_PCREL_LO12_I ||
132           E.getKind() == R_RISCV_PCREL_LO12_S) &&
133          "Can only have high relocation for R_RISCV_PCREL_LO12_I or "
134          "R_RISCV_PCREL_LO12_S");
135 
136   const Symbol &Sym = E.getTarget();
137   const Block &B = Sym.getBlock();
138   orc::ExecutorAddrDiff Offset = Sym.getOffset();
139 
140   struct Comp {
141     bool operator()(const Edge &Lhs, orc::ExecutorAddrDiff Offset) {
142       return Lhs.getOffset() < Offset;
143     }
144     bool operator()(orc::ExecutorAddrDiff Offset, const Edge &Rhs) {
145       return Offset < Rhs.getOffset();
146     }
147   };
148 
149   auto Bound =
150       std::equal_range(B.edges().begin(), B.edges().end(), Offset, Comp{});
151 
152   for (auto It = Bound.first; It != Bound.second; ++It) {
153     if (It->getKind() == R_RISCV_PCREL_HI20)
154       return *It;
155   }
156 
157   return make_error<JITLinkError>(
158       "No HI20 PCREL relocation type be found for LO12 PCREL relocation type");
159 }
160 
161 static uint32_t extractBits(uint32_t Num, unsigned Low, unsigned Size) {
162   return (Num & (((1ULL << (Size + 1)) - 1) << Low)) >> Low;
163 }
164 
165 inline Error checkAlignment(llvm::orc::ExecutorAddr loc, uint64_t v, int n,
166                             const Edge &E) {
167   if (v & (n - 1))
168     return make_error<JITLinkError>("0x" + llvm::utohexstr(loc.getValue()) +
169                                     " improper alignment for relocation " +
170                                     formatv("{0:d}", E.getKind()) + ": 0x" +
171                                     llvm::utohexstr(v) + " is not aligned to " +
172                                     Twine(n) + " bytes");
173   return Error::success();
174 }
175 
176 static inline bool isInRangeForImmS32(int64_t Value) {
177   return (Value >= std::numeric_limits<int32_t>::min() &&
178           Value <= std::numeric_limits<int32_t>::max());
179 }
180 
181 class ELFJITLinker_riscv : public JITLinker<ELFJITLinker_riscv> {
182   friend class JITLinker<ELFJITLinker_riscv>;
183 
184 public:
185   ELFJITLinker_riscv(std::unique_ptr<JITLinkContext> Ctx,
186                      std::unique_ptr<LinkGraph> G, PassConfiguration PassConfig)
187       : JITLinker(std::move(Ctx), std::move(G), std::move(PassConfig)) {}
188 
189 private:
190   Error applyFixup(LinkGraph &G, Block &B, const Edge &E) const {
191     using namespace riscv;
192     using namespace llvm::support;
193 
194     char *BlockWorkingMem = B.getAlreadyMutableContent().data();
195     char *FixupPtr = BlockWorkingMem + E.getOffset();
196     orc::ExecutorAddr FixupAddress = B.getAddress() + E.getOffset();
197     switch (E.getKind()) {
198     case R_RISCV_32: {
199       int64_t Value = (E.getTarget().getAddress() + E.getAddend()).getValue();
200       *(little32_t *)FixupPtr = static_cast<uint32_t>(Value);
201       break;
202     }
203     case R_RISCV_64: {
204       int64_t Value = (E.getTarget().getAddress() + E.getAddend()).getValue();
205       *(little64_t *)FixupPtr = static_cast<uint64_t>(Value);
206       break;
207     }
208     case R_RISCV_BRANCH: {
209       int64_t Value = E.getTarget().getAddress() + E.getAddend() - FixupAddress;
210       Error AlignmentIssue = checkAlignment(FixupAddress, Value, 2, E);
211       if (AlignmentIssue) {
212         return AlignmentIssue;
213       }
214       int64_t Lo = Value & 0xFFF;
215       uint32_t Imm31_25 = extractBits(Lo, 5, 6) << 25 | extractBits(Lo, 12, 1)
216                                                             << 31;
217       uint32_t Imm11_7 = extractBits(Lo, 1, 4) << 8 | extractBits(Lo, 11, 1)
218                                                           << 7;
219       uint32_t RawInstr = *(little32_t *)FixupPtr;
220       *(little32_t *)FixupPtr = (RawInstr & 0x1FFF07F) | Imm31_25 | Imm11_7;
221       break;
222     }
223     case R_RISCV_HI20: {
224       int64_t Value = (E.getTarget().getAddress() + E.getAddend()).getValue();
225       int64_t Hi = Value + 0x800;
226       if (LLVM_UNLIKELY(!isInRangeForImmS32(Hi)))
227         return makeTargetOutOfRangeError(G, B, E);
228       uint32_t RawInstr = *(little32_t *)FixupPtr;
229       *(little32_t *)FixupPtr =
230           (RawInstr & 0xFFF) | (static_cast<uint32_t>(Hi & 0xFFFFF000));
231       break;
232     }
233     case R_RISCV_LO12_I: {
234       // FIXME: We assume that R_RISCV_HI20 is present in object code and pairs
235       // with current relocation R_RISCV_LO12_I. So here may need a check.
236       int64_t Value = (E.getTarget().getAddress() + E.getAddend()).getValue();
237       int32_t Lo = Value & 0xFFF;
238       uint32_t RawInstr = *(little32_t *)FixupPtr;
239       *(little32_t *)FixupPtr =
240           (RawInstr & 0xFFFFF) | (static_cast<uint32_t>(Lo & 0xFFF) << 20);
241       break;
242     }
243     case R_RISCV_CALL: {
244       int64_t Value = E.getTarget().getAddress() + E.getAddend() - FixupAddress;
245       int64_t Hi = Value + 0x800;
246       if (LLVM_UNLIKELY(!isInRangeForImmS32(Hi)))
247         return makeTargetOutOfRangeError(G, B, E);
248       int32_t Lo = Value & 0xFFF;
249       uint32_t RawInstrAuipc = *(little32_t *)FixupPtr;
250       uint32_t RawInstrJalr = *(little32_t *)(FixupPtr + 4);
251       *(little32_t *)FixupPtr =
252           RawInstrAuipc | (static_cast<uint32_t>(Hi & 0xFFFFF000));
253       *(little32_t *)(FixupPtr + 4) =
254           RawInstrJalr | (static_cast<uint32_t>(Lo) << 20);
255       break;
256     }
257     case R_RISCV_PCREL_HI20: {
258       int64_t Value = E.getTarget().getAddress() + E.getAddend() - FixupAddress;
259       int64_t Hi = Value + 0x800;
260       if (LLVM_UNLIKELY(!isInRangeForImmS32(Hi)))
261         return makeTargetOutOfRangeError(G, B, E);
262       uint32_t RawInstr = *(little32_t *)FixupPtr;
263       *(little32_t *)FixupPtr =
264           (RawInstr & 0xFFF) | (static_cast<uint32_t>(Hi & 0xFFFFF000));
265       break;
266     }
267     case R_RISCV_PCREL_LO12_I: {
268       // FIXME: We assume that R_RISCV_PCREL_HI20 is present in object code and
269       // pairs with current relocation R_RISCV_PCREL_LO12_I. So here may need a
270       // check.
271       auto RelHI20 = getRISCVPCRelHi20(E);
272       if (!RelHI20)
273         return RelHI20.takeError();
274       int64_t Value = RelHI20->getTarget().getAddress() +
275                       RelHI20->getAddend() - E.getTarget().getAddress();
276       int64_t Lo = Value & 0xFFF;
277       uint32_t RawInstr = *(little32_t *)FixupPtr;
278       *(little32_t *)FixupPtr =
279           (RawInstr & 0xFFFFF) | (static_cast<uint32_t>(Lo & 0xFFF) << 20);
280       break;
281     }
282     case R_RISCV_PCREL_LO12_S: {
283       // FIXME: We assume that R_RISCV_PCREL_HI20 is present in object code and
284       // pairs with current relocation R_RISCV_PCREL_LO12_S. So here may need a
285       // check.
286       auto RelHI20 = getRISCVPCRelHi20(E);
287       int64_t Value = RelHI20->getTarget().getAddress() +
288                       RelHI20->getAddend() - E.getTarget().getAddress();
289       int64_t Lo = Value & 0xFFF;
290       uint32_t Imm31_25 = extractBits(Lo, 5, 7) << 25;
291       uint32_t Imm11_7 = extractBits(Lo, 0, 5) << 7;
292       uint32_t RawInstr = *(little32_t *)FixupPtr;
293 
294       *(little32_t *)FixupPtr = (RawInstr & 0x1FFF07F) | Imm31_25 | Imm11_7;
295       break;
296     }
297     }
298     return Error::success();
299   }
300 };
301 
302 template <typename ELFT>
303 class ELFLinkGraphBuilder_riscv : public ELFLinkGraphBuilder<ELFT> {
304 private:
305   static Expected<riscv::EdgeKind_riscv>
306   getRelocationKind(const uint32_t Type) {
307     using namespace riscv;
308     switch (Type) {
309     case ELF::R_RISCV_32:
310       return EdgeKind_riscv::R_RISCV_32;
311     case ELF::R_RISCV_64:
312       return EdgeKind_riscv::R_RISCV_64;
313     case ELF::R_RISCV_BRANCH:
314       return EdgeKind_riscv::R_RISCV_BRANCH;
315     case ELF::R_RISCV_HI20:
316       return EdgeKind_riscv::R_RISCV_HI20;
317     case ELF::R_RISCV_LO12_I:
318       return EdgeKind_riscv::R_RISCV_LO12_I;
319     case ELF::R_RISCV_CALL:
320       return EdgeKind_riscv::R_RISCV_CALL;
321     case ELF::R_RISCV_PCREL_HI20:
322       return EdgeKind_riscv::R_RISCV_PCREL_HI20;
323     case ELF::R_RISCV_PCREL_LO12_I:
324       return EdgeKind_riscv::R_RISCV_PCREL_LO12_I;
325     case ELF::R_RISCV_PCREL_LO12_S:
326       return EdgeKind_riscv::R_RISCV_PCREL_LO12_S;
327     case ELF::R_RISCV_GOT_HI20:
328       return EdgeKind_riscv::R_RISCV_GOT_HI20;
329     case ELF::R_RISCV_CALL_PLT:
330       return EdgeKind_riscv::R_RISCV_CALL_PLT;
331     }
332 
333     return make_error<JITLinkError>("Unsupported riscv relocation:" +
334                                     formatv("{0:d}", Type));
335   }
336 
337   Error addRelocations() override {
338     LLVM_DEBUG(dbgs() << "Processing relocations:\n");
339 
340     using Base = ELFLinkGraphBuilder<ELFT>;
341     using Self = ELFLinkGraphBuilder_riscv<ELFT>;
342     for (const auto &RelSect : Base::Sections)
343       if (Error Err = Base::forEachRelocation(RelSect, this,
344                                               &Self::addSingleRelocation))
345         return Err;
346 
347     return Error::success();
348   }
349 
350   Error addSingleRelocation(const typename ELFT::Rela &Rel,
351                             const typename ELFT::Shdr &FixupSect,
352                             Block &BlockToFix) {
353     using Base = ELFLinkGraphBuilder<ELFT>;
354 
355     uint32_t SymbolIndex = Rel.getSymbol(false);
356     auto ObjSymbol = Base::Obj.getRelocationSymbol(Rel, Base::SymTabSec);
357     if (!ObjSymbol)
358       return ObjSymbol.takeError();
359 
360     Symbol *GraphSymbol = Base::getGraphSymbol(SymbolIndex);
361     if (!GraphSymbol)
362       return make_error<StringError>(
363           formatv("Could not find symbol at given index, did you add it to "
364                   "JITSymbolTable? index: {0}, shndx: {1} Size of table: {2}",
365                   SymbolIndex, (*ObjSymbol)->st_shndx,
366                   Base::GraphSymbols.size()),
367           inconvertibleErrorCode());
368 
369     uint32_t Type = Rel.getType(false);
370     Expected<riscv::EdgeKind_riscv> Kind = getRelocationKind(Type);
371     if (!Kind)
372       return Kind.takeError();
373 
374     int64_t Addend = Rel.r_addend;
375     auto FixupAddress = orc::ExecutorAddr(FixupSect.sh_addr) + Rel.r_offset;
376     Edge::OffsetT Offset = FixupAddress - BlockToFix.getAddress();
377     Edge GE(*Kind, Offset, *GraphSymbol, Addend);
378     LLVM_DEBUG({
379       dbgs() << "    ";
380       printEdge(dbgs(), BlockToFix, GE, riscv::getEdgeKindName(*Kind));
381       dbgs() << "\n";
382     });
383 
384     BlockToFix.addEdge(std::move(GE));
385     return Error::success();
386   }
387 
388 public:
389   ELFLinkGraphBuilder_riscv(StringRef FileName,
390                             const object::ELFFile<ELFT> &Obj, const Triple T)
391       : ELFLinkGraphBuilder<ELFT>(Obj, std::move(T), FileName,
392                                   riscv::getEdgeKindName) {}
393 };
394 
395 Expected<std::unique_ptr<LinkGraph>>
396 createLinkGraphFromELFObject_riscv(MemoryBufferRef ObjectBuffer) {
397   LLVM_DEBUG({
398     dbgs() << "Building jitlink graph for new input "
399            << ObjectBuffer.getBufferIdentifier() << "...\n";
400   });
401 
402   auto ELFObj = object::ObjectFile::createELFObjectFile(ObjectBuffer);
403   if (!ELFObj)
404     return ELFObj.takeError();
405 
406   if ((*ELFObj)->getArch() == Triple::riscv64) {
407     auto &ELFObjFile = cast<object::ELFObjectFile<object::ELF64LE>>(**ELFObj);
408     return ELFLinkGraphBuilder_riscv<object::ELF64LE>(
409                (*ELFObj)->getFileName(), ELFObjFile.getELFFile(),
410                (*ELFObj)->makeTriple())
411         .buildGraph();
412   } else {
413     assert((*ELFObj)->getArch() == Triple::riscv32 &&
414            "Invalid triple for RISCV ELF object file");
415     auto &ELFObjFile = cast<object::ELFObjectFile<object::ELF32LE>>(**ELFObj);
416     return ELFLinkGraphBuilder_riscv<object::ELF32LE>(
417                (*ELFObj)->getFileName(), ELFObjFile.getELFFile(),
418                (*ELFObj)->makeTriple())
419         .buildGraph();
420   }
421 }
422 
423 void link_ELF_riscv(std::unique_ptr<LinkGraph> G,
424                     std::unique_ptr<JITLinkContext> Ctx) {
425   PassConfiguration Config;
426   const Triple &TT = G->getTargetTriple();
427   if (Ctx->shouldAddDefaultTargetPasses(TT)) {
428     if (auto MarkLive = Ctx->getMarkLivePass(TT))
429       Config.PrePrunePasses.push_back(std::move(MarkLive));
430     else
431       Config.PrePrunePasses.push_back(markAllSymbolsLive);
432     Config.PostPrunePasses.push_back(
433         PerGraphGOTAndPLTStubsBuilder_ELF_riscv::asPass);
434   }
435   if (auto Err = Ctx->modifyPassConfig(*G, Config))
436     return Ctx->notifyFailed(std::move(Err));
437 
438   ELFJITLinker_riscv::link(std::move(Ctx), std::move(G), std::move(Config));
439 }
440 
441 } // namespace jitlink
442 } // namespace llvm
443