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
27using namespace llvm;
28using namespace llvm::jitlink;
29using namespace llvm::jitlink::loongarch;
30
31namespace {
32
33class ELFJITLinker_loongarch : public JITLinker<ELFJITLinker_loongarch> {
34  friend class JITLinker<ELFJITLinker_loongarch>;
35
36public:
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
42private:
43  Error applyFixup(LinkGraph &G, Block &B, const Edge &E) const {
44    return loongarch::applyFixup(G, B, E);
45  }
46};
47
48template <typename ELFT>
49class ELFLinkGraphBuilder_loongarch : public ELFLinkGraphBuilder<ELFT> {
50private:
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
130public:
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
138Error 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
149namespace llvm {
150namespace jitlink {
151
152Expected<std::unique_ptr<LinkGraph>>
153createLinkGraphFromELFObject_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
184void 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 passes.
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