1 //===--- ELF_loongarch.cpp - JIT linker implementation for ELF/loongarch --===//
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/loongarch jit-link implementation.
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #include "llvm/ExecutionEngine/JITLink/ELF_loongarch.h"
14 #include "llvm/BinaryFormat/ELF.h"
15 #include "llvm/ExecutionEngine/JITLink/DWARFRecordSectionSplitter.h"
16 #include "llvm/ExecutionEngine/JITLink/JITLink.h"
17 #include "llvm/ExecutionEngine/JITLink/loongarch.h"
18 #include "llvm/Object/ELF.h"
19 #include "llvm/Object/ELFObjectFile.h"
20 
21 #include "EHFrameSupportImpl.h"
22 #include "ELFLinkGraphBuilder.h"
23 #include "JITLinkGeneric.h"
24 
25 #define DEBUG_TYPE "jitlink"
26 
27 using namespace llvm;
28 using namespace llvm::jitlink;
29 using namespace llvm::jitlink::loongarch;
30 
31 namespace {
32 
33 class ELFJITLinker_loongarch : public JITLinker<ELFJITLinker_loongarch> {
34   friend class JITLinker<ELFJITLinker_loongarch>;
35 
36 public:
37   ELFJITLinker_loongarch(std::unique_ptr<JITLinkContext> Ctx,
38                          std::unique_ptr<LinkGraph> G,
39                          PassConfiguration PassConfig)
40       : JITLinker(std::move(Ctx), std::move(G), std::move(PassConfig)) {}
41 
42 private:
43   Error applyFixup(LinkGraph &G, Block &B, const Edge &E) const {
44     return loongarch::applyFixup(G, B, E);
45   }
46 };
47 
48 template <typename ELFT>
49 class ELFLinkGraphBuilder_loongarch : public ELFLinkGraphBuilder<ELFT> {
50 private:
51   static Expected<loongarch::EdgeKind_loongarch>
52   getRelocationKind(const uint32_t Type) {
53     using namespace loongarch;
54     switch (Type) {
55     case ELF::R_LARCH_64:
56       return Pointer64;
57     case ELF::R_LARCH_32:
58       return Pointer32;
59     case ELF::R_LARCH_32_PCREL:
60       return Delta32;
61     case ELF::R_LARCH_B26:
62       return Branch26PCRel;
63     case ELF::R_LARCH_PCALA_HI20:
64       return Page20;
65     case ELF::R_LARCH_PCALA_LO12:
66       return PageOffset12;
67     case ELF::R_LARCH_GOT_PC_HI20:
68       return RequestGOTAndTransformToPage20;
69     case ELF::R_LARCH_GOT_PC_LO12:
70       return RequestGOTAndTransformToPageOffset12;
71     }
72 
73     return make_error<JITLinkError>(
74         "Unsupported loongarch relocation:" + formatv("{0:d}: ", Type) +
75         object::getELFRelocationTypeName(ELF::EM_LOONGARCH, Type));
76   }
77 
78   Error addRelocations() override {
79     LLVM_DEBUG(dbgs() << "Processing relocations:\n");
80 
81     using Base = ELFLinkGraphBuilder<ELFT>;
82     using Self = ELFLinkGraphBuilder_loongarch<ELFT>;
83     for (const auto &RelSect : Base::Sections)
84       if (Error Err = Base::forEachRelaRelocation(RelSect, this,
85                                                   &Self::addSingleRelocation))
86         return Err;
87 
88     return Error::success();
89   }
90 
91   Error addSingleRelocation(const typename ELFT::Rela &Rel,
92                             const typename ELFT::Shdr &FixupSect,
93                             Block &BlockToFix) {
94     using Base = ELFLinkGraphBuilder<ELFT>;
95 
96     uint32_t SymbolIndex = Rel.getSymbol(false);
97     auto ObjSymbol = Base::Obj.getRelocationSymbol(Rel, Base::SymTabSec);
98     if (!ObjSymbol)
99       return ObjSymbol.takeError();
100 
101     Symbol *GraphSymbol = Base::getGraphSymbol(SymbolIndex);
102     if (!GraphSymbol)
103       return make_error<StringError>(
104           formatv("Could not find symbol at given index, did you add it to "
105                   "JITSymbolTable? index: {0}, shndx: {1} Size of table: {2}",
106                   SymbolIndex, (*ObjSymbol)->st_shndx,
107                   Base::GraphSymbols.size()),
108           inconvertibleErrorCode());
109 
110     uint32_t Type = Rel.getType(false);
111     Expected<loongarch::EdgeKind_loongarch> Kind = getRelocationKind(Type);
112     if (!Kind)
113       return Kind.takeError();
114 
115     int64_t Addend = Rel.r_addend;
116     auto FixupAddress = orc::ExecutorAddr(FixupSect.sh_addr) + Rel.r_offset;
117     Edge::OffsetT Offset = FixupAddress - BlockToFix.getAddress();
118     Edge GE(*Kind, Offset, *GraphSymbol, Addend);
119     LLVM_DEBUG({
120       dbgs() << "    ";
121       printEdge(dbgs(), BlockToFix, GE, loongarch::getEdgeKindName(*Kind));
122       dbgs() << "\n";
123     });
124 
125     BlockToFix.addEdge(std::move(GE));
126 
127     return Error::success();
128   }
129 
130 public:
131   ELFLinkGraphBuilder_loongarch(StringRef FileName,
132                                 const object::ELFFile<ELFT> &Obj, Triple TT,
133                                 SubtargetFeatures Features)
134       : ELFLinkGraphBuilder<ELFT>(Obj, std::move(TT), std::move(Features),
135                                   FileName, loongarch::getEdgeKindName) {}
136 };
137 
138 Error buildTables_ELF_loongarch(LinkGraph &G) {
139   LLVM_DEBUG(dbgs() << "Visiting edges in graph:\n");
140 
141   GOTTableManager GOT;
142   PLTTableManager PLT(GOT);
143   visitExistingEdges(G, GOT, PLT);
144   return Error::success();
145 }
146 
147 } // namespace
148 
149 namespace llvm {
150 namespace jitlink {
151 
152 Expected<std::unique_ptr<LinkGraph>>
153 createLinkGraphFromELFObject_loongarch(MemoryBufferRef ObjectBuffer) {
154   LLVM_DEBUG({
155     dbgs() << "Building jitlink graph for new input "
156            << ObjectBuffer.getBufferIdentifier() << "...\n";
157   });
158 
159   auto ELFObj = object::ObjectFile::createELFObjectFile(ObjectBuffer);
160   if (!ELFObj)
161     return ELFObj.takeError();
162 
163   auto Features = (*ELFObj)->getFeatures();
164   if (!Features)
165     return Features.takeError();
166 
167   if ((*ELFObj)->getArch() == Triple::loongarch64) {
168     auto &ELFObjFile = cast<object::ELFObjectFile<object::ELF64LE>>(**ELFObj);
169     return ELFLinkGraphBuilder_loongarch<object::ELF64LE>(
170                (*ELFObj)->getFileName(), ELFObjFile.getELFFile(),
171                (*ELFObj)->makeTriple(), std::move(*Features))
172         .buildGraph();
173   }
174 
175   assert((*ELFObj)->getArch() == Triple::loongarch32 &&
176          "Invalid triple for LoongArch ELF object file");
177   auto &ELFObjFile = cast<object::ELFObjectFile<object::ELF32LE>>(**ELFObj);
178   return ELFLinkGraphBuilder_loongarch<object::ELF32LE>(
179              (*ELFObj)->getFileName(), ELFObjFile.getELFFile(),
180              (*ELFObj)->makeTriple(), std::move(*Features))
181       .buildGraph();
182 }
183 
184 void link_ELF_loongarch(std::unique_ptr<LinkGraph> G,
185                         std::unique_ptr<JITLinkContext> Ctx) {
186   PassConfiguration Config;
187   const Triple &TT = G->getTargetTriple();
188   if (Ctx->shouldAddDefaultTargetPasses(TT)) {
189     // Add eh-frame passses.
190     Config.PrePrunePasses.push_back(DWARFRecordSectionSplitter(".eh_frame"));
191     Config.PrePrunePasses.push_back(
192         EHFrameEdgeFixer(".eh_frame", G->getPointerSize(), Pointer32, Pointer64,
193                          Delta32, Delta64, NegDelta32));
194     Config.PrePrunePasses.push_back(EHFrameNullTerminator(".eh_frame"));
195 
196     // Add a mark-live pass.
197     if (auto MarkLive = Ctx->getMarkLivePass(TT))
198       Config.PrePrunePasses.push_back(std::move(MarkLive));
199     else
200       Config.PrePrunePasses.push_back(markAllSymbolsLive);
201 
202     // Add an in-place GOT/PLTStubs build pass.
203     Config.PostPrunePasses.push_back(buildTables_ELF_loongarch);
204   }
205 
206   if (auto Err = Ctx->modifyPassConfig(*G, Config))
207     return Ctx->notifyFailed(std::move(Err));
208 
209   ELFJITLinker_loongarch::link(std::move(Ctx), std::move(G), std::move(Config));
210 }
211 
212 } // namespace jitlink
213 } // namespace llvm
214