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", sys::Memory::MF_READ);
84  return *GOTSection;
85  }
86 
87  Section &getStubsSection() const {
88  if (!StubsSection) {
89  auto StubsProt = static_cast<sys::Memory::ProtectionFlags>(
91  StubsSection = &G.createSection("$__STUBS", StubsProt);
92  }
93  return *StubsSection;
94  }
95 
96  ArrayRef<char> getGOTEntryBlockContent() {
97  return {reinterpret_cast<const char *>(NullGOTEntryContent),
98  G.getPointerSize()};
99  }
100 
101  ArrayRef<char> getStubBlockContent() {
102  auto StubContent = isRV64() ? RV64StubContent : RV32StubContent;
103  return {reinterpret_cast<const char *>(StubContent), StubEntrySize};
104  }
105 
106  mutable Section *GOTSection = nullptr;
107  mutable Section *StubsSection = nullptr;
108 };
109 
110 const uint8_t PerGraphGOTAndPLTStubsBuilder_ELF_riscv::NullGOTEntryContent[8] =
111  {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
112 
113 const uint8_t
114  PerGraphGOTAndPLTStubsBuilder_ELF_riscv::RV64StubContent[StubEntrySize] = {
115  0x17, 0x0e, 0x00, 0x00, // auipc t3, literal
116  0x03, 0x3e, 0x0e, 0x00, // ld t3, literal(t3)
117  0x67, 0x00, 0x0e, 0x00, // jr t3
118  0x13, 0x00, 0x00, 0x00}; // nop
119 
120 const uint8_t
121  PerGraphGOTAndPLTStubsBuilder_ELF_riscv::RV32StubContent[StubEntrySize] = {
122  0x17, 0x0e, 0x00, 0x00, // auipc t3, literal
123  0x03, 0x2e, 0x0e, 0x00, // lw t3, literal(t3)
124  0x67, 0x00, 0x0e, 0x00, // jr t3
125  0x13, 0x00, 0x00, 0x00}; // nop
126 } // namespace
127 namespace llvm {
128 namespace jitlink {
129 
131  using namespace riscv;
132  assert((E.getKind() == R_RISCV_PCREL_LO12_I ||
133  E.getKind() == R_RISCV_PCREL_LO12_S) &&
134  "Can only have high relocation for R_RISCV_PCREL_LO12_I or "
135  "R_RISCV_PCREL_LO12_S");
136 
137  const Symbol &Sym = E.getTarget();
138  const Block &B = Sym.getBlock();
140 
141  struct Comp {
142  bool operator()(const Edge &Lhs, JITTargetAddress Offset) {
143  return Lhs.getOffset() < Offset;
144  }
145  bool operator()(JITTargetAddress Offset, const Edge &Rhs) {
146  return Offset < Rhs.getOffset();
147  }
148  };
149 
150  auto Bound =
151  std::equal_range(B.edges().begin(), B.edges().end(), Offset, Comp{});
152 
153  for (auto It = Bound.first; It != Bound.second; ++It) {
154  if (It->getKind() == R_RISCV_PCREL_HI20)
155  return *It;
156  }
157 
158  return make_error<JITLinkError>(
159  "No HI20 PCREL relocation type be found for LO12 PCREL relocation type");
160 }
161 
162 static uint32_t extractBits(uint64_t Num, unsigned High, unsigned Low) {
163  return (Num & ((1ULL << (High + 1)) - 1)) >> Low;
164 }
165 
166 class ELFJITLinker_riscv : public JITLinker<ELFJITLinker_riscv> {
168 
169 public:
170  ELFJITLinker_riscv(std::unique_ptr<JITLinkContext> Ctx,
171  std::unique_ptr<LinkGraph> G, PassConfiguration PassConfig)
172  : JITLinker(std::move(Ctx), std::move(G), std::move(PassConfig)) {}
173 
174 private:
175  Error applyFixup(LinkGraph &G, Block &B, const Edge &E) const {
176  using namespace riscv;
177  using namespace llvm::support;
178 
179  char *BlockWorkingMem = B.getAlreadyMutableContent().data();
180  char *FixupPtr = BlockWorkingMem + E.getOffset();
181  JITTargetAddress FixupAddress = B.getAddress() + E.getOffset();
182  switch (E.getKind()) {
183  case R_RISCV_32: {
184  int64_t Value = E.getTarget().getAddress() + E.getAddend();
185  *(little32_t *)FixupPtr = static_cast<uint32_t>(Value);
186  break;
187  }
188  case R_RISCV_64: {
189  int64_t Value = E.getTarget().getAddress() + E.getAddend();
190  *(little64_t *)FixupPtr = static_cast<uint64_t>(Value);
191  break;
192  }
193  case R_RISCV_HI20: {
194  int64_t Value = E.getTarget().getAddress() + E.getAddend();
195  int32_t Hi = (Value + 0x800) & 0xFFFFF000;
196  uint32_t RawInstr = *(little32_t *)FixupPtr;
197  *(little32_t *)FixupPtr = (RawInstr & 0xFFF) | static_cast<uint32_t>(Hi);
198  break;
199  }
200  case R_RISCV_LO12_I: {
201  int64_t Value = E.getTarget().getAddress() + E.getAddend();
202  int32_t Lo = Value & 0xFFF;
203  uint32_t RawInstr = *(little32_t *)FixupPtr;
204  *(little32_t *)FixupPtr =
205  (RawInstr & 0xFFFFF) | (static_cast<uint32_t>(Lo & 0xFFF) << 20);
206  break;
207  }
208  case R_RISCV_CALL: {
209  int64_t Value = E.getTarget().getAddress() + E.getAddend() - FixupAddress;
210  int32_t Hi = (Value + 0x800) & 0xFFFFF000;
211  int32_t Lo = Value & 0xFFF;
212  uint32_t RawInstrAuipc = *(little32_t *)FixupPtr;
213  uint32_t RawInstrJalr = *(little32_t *)(FixupPtr + 4);
214  *(little32_t *)FixupPtr = RawInstrAuipc | static_cast<uint32_t>(Hi);
215  *(little32_t *)(FixupPtr + 4) =
216  RawInstrJalr | (static_cast<uint32_t>(Lo) << 20);
217  break;
218  }
219  case R_RISCV_PCREL_HI20: {
220  int64_t Value = E.getTarget().getAddress() + E.getAddend() - FixupAddress;
221  int32_t Hi = (Value + 0x800) & 0xFFFFF000;
222  uint32_t RawInstr = *(little32_t *)FixupPtr;
223  *(little32_t *)FixupPtr = (RawInstr & 0xFFF) | static_cast<uint32_t>(Hi);
224  break;
225  }
226  case R_RISCV_PCREL_LO12_I: {
227  auto RelHI20 = getRISCVPCRelHi20(E);
228  if (!RelHI20)
229  return RelHI20.takeError();
230  int64_t Value = RelHI20->getTarget().getAddress() +
231  RelHI20->getAddend() - E.getTarget().getAddress();
232  int64_t Lo = Value & 0xFFF;
233  uint32_t RawInstr = *(little32_t *)FixupPtr;
234  *(little32_t *)FixupPtr =
235  (RawInstr & 0xFFFFF) | (static_cast<uint32_t>(Lo & 0xFFF) << 20);
236  break;
237  }
238  case R_RISCV_PCREL_LO12_S: {
239  auto RelHI20 = getRISCVPCRelHi20(E);
240  int64_t Value = RelHI20->getTarget().getAddress() +
241  RelHI20->getAddend() - E.getTarget().getAddress();
242  int64_t Lo = Value & 0xFFF;
243  uint32_t Imm31_25 = extractBits(Lo, 11, 5) << 25;
244  uint32_t Imm11_7 = extractBits(Lo, 4, 0) << 7;
245  uint32_t RawInstr = *(little32_t *)FixupPtr;
246 
247  *(little32_t *)FixupPtr = (RawInstr & 0x1FFF07F) | Imm31_25 | Imm11_7;
248  break;
249  }
250  }
251  return Error::success();
252  }
253 };
254 
255 template <typename ELFT>
257 private:
259  getRelocationKind(const uint32_t Type) {
260  using namespace riscv;
261  switch (Type) {
262  case ELF::R_RISCV_32:
264  case ELF::R_RISCV_64:
266  case ELF::R_RISCV_HI20:
268  case ELF::R_RISCV_LO12_I:
270  case ELF::R_RISCV_CALL:
282  }
283 
284  return make_error<JITLinkError>("Unsupported riscv relocation:" +
285  formatv("{0:d}", Type));
286  }
287 
288  Error addRelocations() override {
290  LLVM_DEBUG(dbgs() << "Adding relocations\n");
291 
292  // TODO a partern is forming of iterate some sections but only give me
293  // ones I am interested, I should abstract that concept some where
294  for (auto &SecRef : Base::Sections) {
295  if (SecRef.sh_type != ELF::SHT_RELA && SecRef.sh_type != ELF::SHT_REL)
296  continue;
297  auto RelSectName = Base::Obj.getSectionName(SecRef);
298  if (!RelSectName)
299  return RelSectName.takeError();
300 
301  LLVM_DEBUG({
302  dbgs() << "Adding relocations from section " << *RelSectName << "\n";
303  });
304 
305  auto UpdateSection = Base::Obj.getSection(SecRef.sh_info);
306  if (!UpdateSection)
307  return UpdateSection.takeError();
308 
309  auto UpdateSectionName = Base::Obj.getSectionName(**UpdateSection);
310  if (!UpdateSectionName)
311  return UpdateSectionName.takeError();
312  // Don't process relocations for debug sections.
313  if (Base::isDwarfSection(*UpdateSectionName)) {
314  LLVM_DEBUG({
315  dbgs() << " Target is dwarf section " << *UpdateSectionName
316  << ". Skipping.\n";
317  });
318  continue;
319  } else
320  LLVM_DEBUG({
321  dbgs() << " For target section " << *UpdateSectionName << "\n";
322  });
323 
324  auto *JITSection = Base::G->findSectionByName(*UpdateSectionName);
325  if (!JITSection)
326  return make_error<llvm::StringError>(
327  "Refencing a section that wasn't added to graph" +
328  *UpdateSectionName,
330 
331  auto Relocations = Base::Obj.relas(SecRef);
332  if (!Relocations)
333  return Relocations.takeError();
334 
335  for (const auto &Rela : *Relocations) {
336  auto Type = Rela.getType(false);
337 
338  LLVM_DEBUG({
339  dbgs() << "Relocation Type: " << Type << "\n"
340  << "Name: " << Base::Obj.getRelocationTypeName(Type) << "\n";
341  });
342 
343  auto SymbolIndex = Rela.getSymbol(false);
344  auto Symbol = Base::Obj.getRelocationSymbol(Rela, Base::SymTabSec);
345  if (!Symbol)
346  return Symbol.takeError();
347 
348  auto BlockToFix = *(JITSection->blocks().begin());
349  auto *TargetSymbol = Base::getGraphSymbol(SymbolIndex);
350 
351  if (!TargetSymbol) {
352  return make_error<llvm::StringError>(
353  "Could not find symbol at given index, did you add it to "
354  "JITSymbolTable? index: " +
355  std::to_string(SymbolIndex) + ", shndx: " +
356  std::to_string((*Symbol)->st_shndx) + " Size of table: " +
357  std::to_string(Base::GraphSymbols.size()),
359  }
360  int64_t Addend = Rela.r_addend;
361  JITTargetAddress FixupAddress =
362  (*UpdateSection)->sh_addr + Rela.r_offset;
363 
364  LLVM_DEBUG({
365  dbgs() << "Processing relocation at "
366  << format("0x%016" PRIx64, FixupAddress) << "\n";
367  });
368  auto Kind = getRelocationKind(Type);
369  if (!Kind)
370  return Kind.takeError();
371 
372  BlockToFix->addEdge(*Kind, FixupAddress - BlockToFix->getAddress(),
373  *TargetSymbol, Addend);
374  }
375  }
376  return Error::success();
377  }
378 
379 public:
381  const object::ELFFile<ELFT> &Obj, const Triple T)
382  : ELFLinkGraphBuilder<ELFT>(Obj, std::move(T), FileName,
383  riscv::getEdgeKindName) {}
384 };
385 
388  LLVM_DEBUG({
389  dbgs() << "Building jitlink graph for new input "
390  << ObjectBuffer.getBufferIdentifier() << "...\n";
391  });
392 
393  auto ELFObj = object::ObjectFile::createELFObjectFile(ObjectBuffer);
394  if (!ELFObj)
395  return ELFObj.takeError();
396 
397  if ((*ELFObj)->getArch() == Triple::riscv64) {
398  auto &ELFObjFile = cast<object::ELFObjectFile<object::ELF64LE>>(**ELFObj);
400  (*ELFObj)->getFileName(), ELFObjFile.getELFFile(),
401  (*ELFObj)->makeTriple())
402  .buildGraph();
403  } else {
404  assert((*ELFObj)->getArch() == Triple::riscv32 &&
405  "Invalid triple for RISCV ELF object file");
406  auto &ELFObjFile = cast<object::ELFObjectFile<object::ELF32LE>>(**ELFObj);
408  (*ELFObj)->getFileName(), ELFObjFile.getELFFile(),
409  (*ELFObj)->makeTriple())
410  .buildGraph();
411  }
412 }
413 
414 void link_ELF_riscv(std::unique_ptr<LinkGraph> G,
415  std::unique_ptr<JITLinkContext> Ctx) {
416  PassConfiguration Config;
417  const Triple &TT = G->getTargetTriple();
418  if (Ctx->shouldAddDefaultTargetPasses(TT)) {
419  if (auto MarkLive = Ctx->getMarkLivePass(TT))
420  Config.PrePrunePasses.push_back(std::move(MarkLive));
421  else
422  Config.PrePrunePasses.push_back(markAllSymbolsLive);
423  Config.PostPrunePasses.push_back(
424  PerGraphGOTAndPLTStubsBuilder_ELF_riscv::asPass);
425  }
426  if (auto Err = Ctx->modifyPassConfig(*G, Config))
427  return Ctx->notifyFailed(std::move(Err));
428 
430 }
431 
432 } // namespace jitlink
433 } // namespace llvm
llvm::Triple::riscv64
@ riscv64
Definition: Triple.h:74
llvm::sys::Memory::MF_READ
@ MF_READ
Definition: Memory.h:55
llvm
---------------------— PointerInfo ------------------------------------—
Definition: AllocatorList.h:23
llvm::support::detail::packed_endian_specific_integral
Definition: Endian.h:206
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
T
#define T
Definition: Mips16ISelLowering.cpp:341
Offset
uint64_t Offset
Definition: ELFObjHandler.cpp:81
llvm::Expected
Tagged union holding either a T or a Error.
Definition: APFloat.h:42
llvm::ELF::SHT_RELA
@ SHT_RELA
Definition: ELF.h:913
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")
llvm::sys::Memory::ProtectionFlags
ProtectionFlags
Definition: Memory.h:54
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
llvm::sys::Memory::MF_EXEC
@ MF_EXEC
Definition: Memory.h:57
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:1605
riscv.h
llvm::ArrayRef< char >
llvm::StringRef
StringRef - Represent a constant reference to a string, i.e.
Definition: StringRef.h:58
uint32_t
llvm::format
format_object< Ts... > format(const char *Fmt, const Ts &... Vals)
These are helper functions used to produce formatted output.
Definition: Format.h:124
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::to_string
std::string to_string(const T &Value)
Definition: ScopedPrinter.h:63
llvm::MemoryBufferRef::getBufferIdentifier
StringRef getBufferIdentifier() const
Definition: MemoryBufferRef.h:33
ELFLinkGraphBuilder.h
llvm::ELF::SHT_REL
@ SHT_REL
Definition: ELF.h:918
isDwarfSection
static bool isDwarfSection(const MCObjectFileInfo *FI, const MCSection *Section)
Definition: NVPTXTargetStreamer.cpp:42
llvm::Value
LLVM Value Representation.
Definition: Value.h:75
llvm::object::ELFFile
Definition: ELF.h:98
JITLinkGeneric.h
llvm::sampleprof::Base
@ Base
Definition: Discriminator.h:58