LLVM  14.0.0git
ELF_riscv.cpp
Go to the documentation of this file.
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 
14 #include "ELFLinkGraphBuilder.h"
15 #include "JITLinkGeneric.h"
17 #include "llvm/BinaryFormat/ELF.h"
20 #include "llvm/Object/ELF.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
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 
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 = G.createContentBlock(
48  getGOTSection(), getGOTEntryBlockContent(), 0, G.getPointerSize(), 0);
49  GOTBlock.addEdge(isRV64() ? R_RISCV_64 : R_RISCV_32, 0, Target, 0);
50  return G.addAnonymousSymbol(GOTBlock, 0, G.getPointerSize(), false, false);
51  }
52 
53  Symbol &createPLTStub(Symbol &Target) {
54  Block &StubContentBlock =
55  G.createContentBlock(getStubsSection(), getStubBlockContent(), 0, 4, 0);
56  auto &GOTEntrySymbol = getGOTEntry(Target);
57  StubContentBlock.addEdge(R_RISCV_CALL, 0, GOTEntrySymbol, 0);
58  return G.addAnonymousSymbol(StubContentBlock, 0, StubEntrySize, true,
59  false);
60  }
61 
62  void fixGOTEdge(Edge &E, Symbol &GOTEntry) {
63  // Replace the relocation pair (R_RISCV_GOT_HI20, R_RISCV_PCREL_LO12)
64  // with (R_RISCV_PCREL_HI20, R_RISCV_PCREL_LO12)
65  // Therefore, here just change the R_RISCV_GOT_HI20 to R_RISCV_PCREL_HI20
66  E.setKind(R_RISCV_PCREL_HI20);
67  E.setTarget(GOTEntry);
68  }
69 
70  void fixPLTEdge(Edge &E, Symbol &PLTStubs) {
71  assert(E.getKind() == R_RISCV_CALL_PLT && "Not a R_RISCV_CALL_PLT edge?");
72  E.setKind(R_RISCV_CALL);
73  E.setTarget(PLTStubs);
74  }
75 
76  bool isExternalBranchEdge(Edge &E) const {
77  return E.getKind() == R_RISCV_CALL_PLT;
78  }
79 
80 private:
81  Section &getGOTSection() const {
82  if (!GOTSection)
83  GOTSection = &G.createSection("$__GOT", MemProt::Read);
84  return *GOTSection;
85  }
86 
87  Section &getStubsSection() const {
88  if (!StubsSection)
89  StubsSection =
90  &G.createSection("$__STUBS", MemProt::Read | MemProt::Exec);
91  return *StubsSection;
92  }
93 
94  ArrayRef<char> getGOTEntryBlockContent() {
95  return {reinterpret_cast<const char *>(NullGOTEntryContent),
96  G.getPointerSize()};
97  }
98 
99  ArrayRef<char> getStubBlockContent() {
100  auto StubContent = isRV64() ? RV64StubContent : RV32StubContent;
101  return {reinterpret_cast<const char *>(StubContent), StubEntrySize};
102  }
103 
104  mutable Section *GOTSection = nullptr;
105  mutable Section *StubsSection = nullptr;
106 };
107 
108 const uint8_t PerGraphGOTAndPLTStubsBuilder_ELF_riscv::NullGOTEntryContent[8] =
109  {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
110 
111 const uint8_t
112  PerGraphGOTAndPLTStubsBuilder_ELF_riscv::RV64StubContent[StubEntrySize] = {
113  0x17, 0x0e, 0x00, 0x00, // auipc t3, literal
114  0x03, 0x3e, 0x0e, 0x00, // ld t3, literal(t3)
115  0x67, 0x00, 0x0e, 0x00, // jr t3
116  0x13, 0x00, 0x00, 0x00}; // nop
117 
118 const uint8_t
119  PerGraphGOTAndPLTStubsBuilder_ELF_riscv::RV32StubContent[StubEntrySize] = {
120  0x17, 0x0e, 0x00, 0x00, // auipc t3, literal
121  0x03, 0x2e, 0x0e, 0x00, // lw t3, literal(t3)
122  0x67, 0x00, 0x0e, 0x00, // jr t3
123  0x13, 0x00, 0x00, 0x00}; // nop
124 } // namespace
125 namespace llvm {
126 namespace jitlink {
127 
129  using namespace riscv;
130  assert((E.getKind() == R_RISCV_PCREL_LO12_I ||
131  E.getKind() == R_RISCV_PCREL_LO12_S) &&
132  "Can only have high relocation for R_RISCV_PCREL_LO12_I or "
133  "R_RISCV_PCREL_LO12_S");
134 
135  const Symbol &Sym = E.getTarget();
136  const Block &B = Sym.getBlock();
138 
139  struct Comp {
140  bool operator()(const Edge &Lhs, JITTargetAddress Offset) {
141  return Lhs.getOffset() < Offset;
142  }
143  bool operator()(JITTargetAddress Offset, const Edge &Rhs) {
144  return Offset < Rhs.getOffset();
145  }
146  };
147 
148  auto Bound =
149  std::equal_range(B.edges().begin(), B.edges().end(), Offset, Comp{});
150 
151  for (auto It = Bound.first; It != Bound.second; ++It) {
152  if (It->getKind() == R_RISCV_PCREL_HI20)
153  return *It;
154  }
155 
156  return make_error<JITLinkError>(
157  "No HI20 PCREL relocation type be found for LO12 PCREL relocation type");
158 }
159 
160 static uint32_t extractBits(uint64_t Num, unsigned High, unsigned Low) {
161  return (Num & ((1ULL << (High + 1)) - 1)) >> Low;
162 }
163 
164 class ELFJITLinker_riscv : public JITLinker<ELFJITLinker_riscv> {
166 
167 public:
168  ELFJITLinker_riscv(std::unique_ptr<JITLinkContext> Ctx,
169  std::unique_ptr<LinkGraph> G, PassConfiguration PassConfig)
170  : JITLinker(std::move(Ctx), std::move(G), std::move(PassConfig)) {}
171 
172 private:
173  Error applyFixup(LinkGraph &G, Block &B, const Edge &E) const {
174  using namespace riscv;
175  using namespace llvm::support;
176 
177  char *BlockWorkingMem = B.getAlreadyMutableContent().data();
178  char *FixupPtr = BlockWorkingMem + E.getOffset();
179  JITTargetAddress FixupAddress = B.getAddress() + E.getOffset();
180  switch (E.getKind()) {
181  case R_RISCV_32: {
182  int64_t Value = E.getTarget().getAddress() + E.getAddend();
183  *(little32_t *)FixupPtr = static_cast<uint32_t>(Value);
184  break;
185  }
186  case R_RISCV_64: {
187  int64_t Value = E.getTarget().getAddress() + E.getAddend();
188  *(little64_t *)FixupPtr = static_cast<uint64_t>(Value);
189  break;
190  }
191  case R_RISCV_HI20: {
192  int64_t Value = E.getTarget().getAddress() + E.getAddend();
193  int32_t Hi = (Value + 0x800) & 0xFFFFF000;
194  uint32_t RawInstr = *(little32_t *)FixupPtr;
195  *(little32_t *)FixupPtr = (RawInstr & 0xFFF) | static_cast<uint32_t>(Hi);
196  break;
197  }
198  case R_RISCV_LO12_I: {
199  int64_t Value = E.getTarget().getAddress() + E.getAddend();
200  int32_t Lo = Value & 0xFFF;
201  uint32_t RawInstr = *(little32_t *)FixupPtr;
202  *(little32_t *)FixupPtr =
203  (RawInstr & 0xFFFFF) | (static_cast<uint32_t>(Lo & 0xFFF) << 20);
204  break;
205  }
206  case R_RISCV_CALL: {
207  int64_t Value = E.getTarget().getAddress() + E.getAddend() - FixupAddress;
208  int32_t Hi = (Value + 0x800) & 0xFFFFF000;
209  int32_t Lo = Value & 0xFFF;
210  uint32_t RawInstrAuipc = *(little32_t *)FixupPtr;
211  uint32_t RawInstrJalr = *(little32_t *)(FixupPtr + 4);
212  *(little32_t *)FixupPtr = RawInstrAuipc | static_cast<uint32_t>(Hi);
213  *(little32_t *)(FixupPtr + 4) =
214  RawInstrJalr | (static_cast<uint32_t>(Lo) << 20);
215  break;
216  }
217  case R_RISCV_PCREL_HI20: {
218  int64_t Value = E.getTarget().getAddress() + E.getAddend() - FixupAddress;
219  int32_t Hi = (Value + 0x800) & 0xFFFFF000;
220  uint32_t RawInstr = *(little32_t *)FixupPtr;
221  *(little32_t *)FixupPtr = (RawInstr & 0xFFF) | static_cast<uint32_t>(Hi);
222  break;
223  }
224  case R_RISCV_PCREL_LO12_I: {
225  auto RelHI20 = getRISCVPCRelHi20(E);
226  if (!RelHI20)
227  return RelHI20.takeError();
228  int64_t Value = RelHI20->getTarget().getAddress() +
229  RelHI20->getAddend() - E.getTarget().getAddress();
230  int64_t Lo = Value & 0xFFF;
231  uint32_t RawInstr = *(little32_t *)FixupPtr;
232  *(little32_t *)FixupPtr =
233  (RawInstr & 0xFFFFF) | (static_cast<uint32_t>(Lo & 0xFFF) << 20);
234  break;
235  }
236  case R_RISCV_PCREL_LO12_S: {
237  auto RelHI20 = getRISCVPCRelHi20(E);
238  int64_t Value = RelHI20->getTarget().getAddress() +
239  RelHI20->getAddend() - E.getTarget().getAddress();
240  int64_t Lo = Value & 0xFFF;
241  uint32_t Imm31_25 = extractBits(Lo, 11, 5) << 25;
242  uint32_t Imm11_7 = extractBits(Lo, 4, 0) << 7;
243  uint32_t RawInstr = *(little32_t *)FixupPtr;
244 
245  *(little32_t *)FixupPtr = (RawInstr & 0x1FFF07F) | Imm31_25 | Imm11_7;
246  break;
247  }
248  }
249  return Error::success();
250  }
251 };
252 
253 template <typename ELFT>
255 private:
257  getRelocationKind(const uint32_t Type) {
258  using namespace riscv;
259  switch (Type) {
260  case ELF::R_RISCV_32:
262  case ELF::R_RISCV_64:
264  case ELF::R_RISCV_HI20:
266  case ELF::R_RISCV_LO12_I:
268  case ELF::R_RISCV_CALL:
280  }
281 
282  return make_error<JITLinkError>("Unsupported riscv relocation:" +
283  formatv("{0:d}", Type));
284  }
285 
286  Error addRelocations() override {
287  LLVM_DEBUG(dbgs() << "Processing relocations:\n");
288 
290  using Self = ELFLinkGraphBuilder_riscv<ELFT>;
291  for (const auto &RelSect : Base::Sections)
292  if (Error Err = Base::forEachRelocation(RelSect, this,
293  &Self::addSingleRelocation))
294  return Err;
295 
296  return Error::success();
297  }
298 
299  Error addSingleRelocation(const typename ELFT::Rela &Rel,
300  const typename ELFT::Shdr &FixupSect,
301  Section &GraphSection) {
303 
304  uint32_t SymbolIndex = Rel.getSymbol(false);
305  auto ObjSymbol = Base::Obj.getRelocationSymbol(Rel, Base::SymTabSec);
306  if (!ObjSymbol)
307  return ObjSymbol.takeError();
308 
309  Symbol *GraphSymbol = Base::getGraphSymbol(SymbolIndex);
310  if (!GraphSymbol)
311  return make_error<StringError>(
312  formatv("Could not find symbol at given index, did you add it to "
313  "JITSymbolTable? index: {0}, shndx: {1} Size of table: {2}",
314  SymbolIndex, (*ObjSymbol)->st_shndx,
315  Base::GraphSymbols.size()),
317 
318  uint32_t Type = Rel.getType(false);
319  Expected<riscv::EdgeKind_riscv> Kind = getRelocationKind(Type);
320  if (!Kind)
321  return Kind.takeError();
322 
323  int64_t Addend = Rel.r_addend;
324  Block *BlockToFix = *(GraphSection.blocks().begin());
325  JITTargetAddress FixupAddress = FixupSect.sh_addr + Rel.r_offset;
326  Edge::OffsetT Offset = FixupAddress - BlockToFix->getAddress();
327  Edge GE(*Kind, Offset, *GraphSymbol, Addend);
328  LLVM_DEBUG({
329  dbgs() << " ";
330  printEdge(dbgs(), *BlockToFix, GE, riscv::getEdgeKindName(*Kind));
331  dbgs() << "\n";
332  });
333 
334  BlockToFix->addEdge(std::move(GE));
335  return Error::success();
336  }
337 
338 public:
340  const object::ELFFile<ELFT> &Obj, const Triple T)
341  : ELFLinkGraphBuilder<ELFT>(Obj, std::move(T), FileName,
342  riscv::getEdgeKindName) {}
343 };
344 
347  LLVM_DEBUG({
348  dbgs() << "Building jitlink graph for new input "
349  << ObjectBuffer.getBufferIdentifier() << "...\n";
350  });
351 
352  auto ELFObj = object::ObjectFile::createELFObjectFile(ObjectBuffer);
353  if (!ELFObj)
354  return ELFObj.takeError();
355 
356  if ((*ELFObj)->getArch() == Triple::riscv64) {
357  auto &ELFObjFile = cast<object::ELFObjectFile<object::ELF64LE>>(**ELFObj);
359  (*ELFObj)->getFileName(), ELFObjFile.getELFFile(),
360  (*ELFObj)->makeTriple())
361  .buildGraph();
362  } else {
363  assert((*ELFObj)->getArch() == Triple::riscv32 &&
364  "Invalid triple for RISCV ELF object file");
365  auto &ELFObjFile = cast<object::ELFObjectFile<object::ELF32LE>>(**ELFObj);
367  (*ELFObj)->getFileName(), ELFObjFile.getELFFile(),
368  (*ELFObj)->makeTriple())
369  .buildGraph();
370  }
371 }
372 
373 void link_ELF_riscv(std::unique_ptr<LinkGraph> G,
374  std::unique_ptr<JITLinkContext> Ctx) {
375  PassConfiguration Config;
376  const Triple &TT = G->getTargetTriple();
377  if (Ctx->shouldAddDefaultTargetPasses(TT)) {
378  if (auto MarkLive = Ctx->getMarkLivePass(TT))
379  Config.PrePrunePasses.push_back(std::move(MarkLive));
380  else
381  Config.PrePrunePasses.push_back(markAllSymbolsLive);
382  Config.PostPrunePasses.push_back(
383  PerGraphGOTAndPLTStubsBuilder_ELF_riscv::asPass);
384  }
385  if (auto Err = Ctx->modifyPassConfig(*G, Config))
386  return Ctx->notifyFailed(std::move(Err));
387 
389 }
390 
391 } // namespace jitlink
392 } // namespace llvm
llvm::Triple::riscv64
@ riscv64
Definition: Triple.h:74
llvm
This file implements support for optimizing divisions by a constant.
Definition: AllocatorList.h:23
llvm::support::detail::packed_endian_specific_integral
Definition: Endian.h:206
T
llvm::Target
Target - Wrapper for Target specific information.
Definition: TargetRegistry.h:137
High
uint64_t High
Definition: NVVMIntrRange.cpp:61
llvm::MipsISD::Lo
@ Lo
Definition: MipsISelLowering.h:79
llvm::Error::success
static ErrorSuccess success()
Create a success value.
Definition: Error.h:331
llvm::Triple
Triple - Helper class for working with autoconf configuration names.
Definition: Triple.h:45
llvm::Type
The instances of the Type class are immutable: once they are created, they are never changed.
Definition: Type.h:45
Offset
uint64_t Offset
Definition: ELFObjHandler.cpp:81
llvm::Expected
Tagged union holding either a T or a Error.
Definition: APFloat.h:42
llvm::MemoryBufferRef
Definition: MemoryBufferRef.h:22
LLVM_DEBUG
#define LLVM_DEBUG(X)
Definition: Debug.h:101
llvm::MipsISD::Hi
@ Hi
Definition: MipsISelLowering.h:75
llvm::dbgs
raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
Definition: Debug.cpp:163
llvm::formatv
auto formatv(const char *Fmt, Ts &&... Vals) -> formatv_object< decltype(std::make_tuple(detail::build_format_adapter(std::forward< Ts >(Vals))...))>
Definition: FormatVariadic.h:250
ELF.h
E
static GCRegistry::Add< CoreCLRGC > E("coreclr", "CoreCLR-compatible GC")
B
static GCRegistry::Add< OcamlGC > B("ocaml", "ocaml 3.10-compatible GC")
llvm::object::ObjectFile::createELFObjectFile
static Expected< std::unique_ptr< ObjectFile > > createELFObjectFile(MemoryBufferRef Object, bool InitContent=true)
Definition: ELFObjectFile.cpp:72
llvm::lltok::Kind
Kind
Definition: LLToken.h:18
G
const DataFlowGraph & G
Definition: RDFGraph.cpp:202
uint64_t
ELF.h
move
compiles ldr LCPI1_0 ldr ldr mov lsr tst moveq r1 ldr LCPI1_1 and r0 bx lr It would be better to do something like to fold the shift into the conditional move
Definition: README.txt:546
assert
assert(ImpDefSCC.getReg()==AMDGPU::SCC &&ImpDefSCC.isDef())
llvm::move
OutputIt move(R &&Range, OutputIt Out)
Provide wrappers to std::move which take ranges instead of having to pass begin/end explicitly.
Definition: STLExtras.h:1609
riscv.h
llvm::AArch64CC::GE
@ GE
Definition: AArch64BaseInfo.h:265
llvm::ArrayRef< char >
llvm::StringRef
StringRef - Represent a constant reference to a string, i.e.
Definition: StringRef.h:58
uint32_t
ELF_riscv.h
ELFObjectFile.h
PerGraphGOTAndPLTStubsBuilder.h
std
Definition: BitVector.h:838
llvm::inconvertibleErrorCode
std::error_code inconvertibleErrorCode()
The value returned by this function can be returned from convertToErrorCode for Error values where no...
Definition: Error.cpp:77
llvm::Error
Lightweight error class with error context and mandatory checking.
Definition: Error.h:157
llvm::Triple::riscv32
@ riscv32
Definition: Triple.h:73
llvm::support
Definition: Endian.h:25
llvm::MemoryBufferRef::getBufferIdentifier
StringRef getBufferIdentifier() const
Definition: MemoryBufferRef.h:33
ELFLinkGraphBuilder.h
llvm::Value
LLVM Value Representation.
Definition: Value.h:74
llvm::object::ELFFile
Definition: ELF.h:94
JITLinkGeneric.h
llvm::sampleprof::Base
@ Base
Definition: Discriminator.h:58
Shdr
Elf_Shdr Shdr
Definition: ELFObjHandler.cpp:79