1 //===------- ELF_riscv.cpp -JIT linker implementation for ELF/riscv -------===//
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
7 //===----------------------------------------------------------------------===//
9 // ELF/riscv jit-link implementation.
11 //===----------------------------------------------------------------------===//
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"
23 #define DEBUG_TYPE "jitlink"
25 using namespace llvm::jitlink
;
26 using namespace llvm::jitlink::riscv
;
30 class PerGraphGOTAndPLTStubsBuilder_ELF_riscv
31 : public PerGraphGOTAndPLTStubsBuilder
<
32 PerGraphGOTAndPLTStubsBuilder_ELF_riscv
> {
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
];
39 using PerGraphGOTAndPLTStubsBuilder
<
40 PerGraphGOTAndPLTStubsBuilder_ELF_riscv
>::PerGraphGOTAndPLTStubsBuilder
;
42 bool isRV64() const { return G
.getPointerSize() == 8; }
44 bool isGOTEdgeToFix(Edge
&E
) const { return E
.getKind() == R_RISCV_GOT_HI20
; }
46 Symbol
&createGOTEntry(Symbol
&Target
) {
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);
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,
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
);
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
);
77 bool isExternalBranchEdge(Edge
&E
) const {
78 return E
.getKind() == R_RISCV_CALL_PLT
;
82 Section
&getGOTSection() const {
84 GOTSection
= &G
.createSection("$__GOT", MemProt::Read
);
88 Section
&getStubsSection() const {
91 &G
.createSection("$__STUBS", MemProt::Read
| MemProt::Exec
);
95 ArrayRef
<char> getGOTEntryBlockContent() {
96 return {reinterpret_cast<const char *>(NullGOTEntryContent
),
100 ArrayRef
<char> getStubBlockContent() {
101 auto StubContent
= isRV64() ? RV64StubContent
: RV32StubContent
;
102 return {reinterpret_cast<const char *>(StubContent
), StubEntrySize
};
105 mutable Section
*GOTSection
= nullptr;
106 mutable Section
*StubsSection
= nullptr;
109 const uint8_t PerGraphGOTAndPLTStubsBuilder_ELF_riscv::NullGOTEntryContent
[8] =
110 {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
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
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
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");
136 const Symbol
&Sym
= E
.getTarget();
137 const Block
&B
= Sym
.getBlock();
138 orc::ExecutorAddrDiff Offset
= Sym
.getOffset();
141 bool operator()(const Edge
&Lhs
, orc::ExecutorAddrDiff Offset
) {
142 return Lhs
.getOffset() < Offset
;
144 bool operator()(orc::ExecutorAddrDiff Offset
, const Edge
&Rhs
) {
145 return Offset
< Rhs
.getOffset();
150 std::equal_range(B
.edges().begin(), B
.edges().end(), Offset
, Comp
{});
152 for (auto It
= Bound
.first
; It
!= Bound
.second
; ++It
) {
153 if (It
->getKind() == R_RISCV_PCREL_HI20
)
157 return make_error
<JITLinkError
>(
158 "No HI20 PCREL relocation type be found for LO12 PCREL relocation type");
161 static uint32_t extractBits(uint32_t Num
, unsigned Low
, unsigned Size
) {
162 return (Num
& (((1ULL << (Size
+ 1)) - 1) << Low
)) >> Low
;
165 inline Error
checkAlignment(llvm::orc::ExecutorAddr loc
, uint64_t v
, int n
,
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();
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());
181 class ELFJITLinker_riscv
: public JITLinker
<ELFJITLinker_riscv
> {
182 friend class JITLinker
<ELFJITLinker_riscv
>;
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
)) {}
190 Error
applyFixup(LinkGraph
&G
, Block
&B
, const Edge
&E
) const {
191 using namespace riscv
;
192 using namespace llvm::support
;
194 char *BlockWorkingMem
= B
.getAlreadyMutableContent().data();
195 char *FixupPtr
= BlockWorkingMem
+ E
.getOffset();
196 orc::ExecutorAddr FixupAddress
= B
.getAddress() + E
.getOffset();
197 switch (E
.getKind()) {
199 int64_t Value
= (E
.getTarget().getAddress() + E
.getAddend()).getValue();
200 *(little32_t
*)FixupPtr
= static_cast<uint32_t>(Value
);
204 int64_t Value
= (E
.getTarget().getAddress() + E
.getAddend()).getValue();
205 *(little64_t
*)FixupPtr
= static_cast<uint64_t>(Value
);
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
;
214 int64_t Lo
= Value
& 0xFFF;
215 uint32_t Imm31_25
= extractBits(Lo
, 5, 6) << 25 | extractBits(Lo
, 12, 1)
217 uint32_t Imm11_7
= extractBits(Lo
, 1, 4) << 8 | extractBits(Lo
, 11, 1)
219 uint32_t RawInstr
= *(little32_t
*)FixupPtr
;
220 *(little32_t
*)FixupPtr
= (RawInstr
& 0x1FFF07F) | Imm31_25
| Imm11_7
;
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));
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);
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);
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));
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
271 auto RelHI20
= getRISCVPCRelHi20(E
);
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);
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
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
;
294 *(little32_t
*)FixupPtr
= (RawInstr
& 0x1FFF07F) | Imm31_25
| Imm11_7
;
298 return Error::success();
302 template <typename ELFT
>
303 class ELFLinkGraphBuilder_riscv
: public ELFLinkGraphBuilder
<ELFT
> {
305 static Expected
<riscv::EdgeKind_riscv
>
306 getRelocationKind(const uint32_t Type
) {
307 using namespace riscv
;
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
;
333 return make_error
<JITLinkError
>("Unsupported riscv relocation:" +
334 formatv("{0:d}", Type
));
337 Error
addRelocations() override
{
338 LLVM_DEBUG(dbgs() << "Processing relocations:\n");
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
))
347 return Error::success();
350 Error
addSingleRelocation(const typename
ELFT::Rela
&Rel
,
351 const typename
ELFT::Shdr
&FixupSect
,
353 using Base
= ELFLinkGraphBuilder
<ELFT
>;
355 uint32_t SymbolIndex
= Rel
.getSymbol(false);
356 auto ObjSymbol
= Base::Obj
.getRelocationSymbol(Rel
, Base::SymTabSec
);
358 return ObjSymbol
.takeError();
360 Symbol
*GraphSymbol
= Base::getGraphSymbol(SymbolIndex
);
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());
369 uint32_t Type
= Rel
.getType(false);
370 Expected
<riscv::EdgeKind_riscv
> Kind
= getRelocationKind(Type
);
372 return Kind
.takeError();
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
);
380 printEdge(dbgs(), BlockToFix
, GE
, riscv::getEdgeKindName(*Kind
));
384 BlockToFix
.addEdge(std::move(GE
));
385 return Error::success();
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
) {}
395 Expected
<std::unique_ptr
<LinkGraph
>>
396 createLinkGraphFromELFObject_riscv(MemoryBufferRef ObjectBuffer
) {
398 dbgs() << "Building jitlink graph for new input "
399 << ObjectBuffer
.getBufferIdentifier() << "...\n";
402 auto ELFObj
= object::ObjectFile::createELFObjectFile(ObjectBuffer
);
404 return ELFObj
.takeError();
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())
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())
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
));
431 Config
.PrePrunePasses
.push_back(markAllSymbolsLive
);
432 Config
.PostPrunePasses
.push_back(
433 PerGraphGOTAndPLTStubsBuilder_ELF_riscv::asPass
);
435 if (auto Err
= Ctx
->modifyPassConfig(*G
, Config
))
436 return Ctx
->notifyFailed(std::move(Err
));
438 ELFJITLinker_riscv::link(std::move(Ctx
), std::move(G
), std::move(Config
));
441 } // namespace jitlink