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 "llvm/ExecutionEngine/JITLink/JITLink.h"
15 #include "llvm/ExecutionEngine/JITLink/riscv.h"
16 #include "llvm/Object/ELF.h"
17 #include "llvm/Object/ELFObjectFile.h"
19 #include "ELFLinkGraphBuilder.h"
20 #include "JITLinkGeneric.h"
22 #define DEBUG_TYPE "jitlink"
28 static Expected
<const Edge
&> getRISCVPCRelHi20(const Edge
&E
) {
29 using namespace riscv
;
30 assert((E
.getKind() == R_RISCV_PCREL_LO12_I
||
31 E
.getKind() == R_RISCV_PCREL_LO12_S
) &&
32 "Can only have high relocation for R_RISCV_PCREL_LO12_I or "
33 "R_RISCV_PCREL_LO12_S");
35 const Symbol
&Sym
= E
.getTarget();
36 const Block
&B
= Sym
.getBlock();
37 JITTargetAddress Offset
= Sym
.getOffset();
40 bool operator()(const Edge
&Lhs
, JITTargetAddress Offset
) {
41 return Lhs
.getOffset() < Offset
;
43 bool operator()(JITTargetAddress Offset
, const Edge
&Rhs
) {
44 return Offset
< Rhs
.getOffset();
49 std::equal_range(B
.edges().begin(), B
.edges().end(), Offset
, Comp
{});
51 for (auto It
= Bound
.first
; It
!= Bound
.second
; ++It
) {
52 if (It
->getKind() == R_RISCV_PCREL_HI20
)
56 return make_error
<JITLinkError
>(
57 "No HI20 PCREL relocation type be found for LO12 PCREL relocation type");
60 static uint32_t extractBits(uint64_t Num
, unsigned High
, unsigned Low
) {
61 return (Num
& ((1ULL << (High
+ 1)) - 1)) >> Low
;
64 class ELFJITLinker_riscv
: public JITLinker
<ELFJITLinker_riscv
> {
65 friend class JITLinker
<ELFJITLinker_riscv
>;
68 ELFJITLinker_riscv(std::unique_ptr
<JITLinkContext
> Ctx
,
69 std::unique_ptr
<LinkGraph
> G
, PassConfiguration PassConfig
)
70 : JITLinker(std::move(Ctx
), std::move(G
), std::move(PassConfig
)) {}
73 Error
applyFixup(LinkGraph
&G
, Block
&B
, const Edge
&E
) const {
74 using namespace riscv
;
75 using namespace llvm::support
;
77 char *BlockWorkingMem
= B
.getAlreadyMutableContent().data();
78 char *FixupPtr
= BlockWorkingMem
+ E
.getOffset();
79 JITTargetAddress FixupAddress
= B
.getAddress() + E
.getOffset();
80 switch (E
.getKind()) {
82 int64_t Value
= E
.getTarget().getAddress() + E
.getAddend();
83 int32_t Hi
= (Value
+ 0x800) & 0xFFFFF000;
84 uint32_t RawInstr
= *(little32_t
*)FixupPtr
;
85 *(little32_t
*)FixupPtr
= (RawInstr
& 0xFFF) | static_cast<uint32_t>(Hi
);
88 case R_RISCV_LO12_I
: {
89 int64_t Value
= E
.getTarget().getAddress() + E
.getAddend();
90 int32_t Lo
= Value
& 0xFFF;
91 uint32_t RawInstr
= *(little32_t
*)FixupPtr
;
92 *(little32_t
*)FixupPtr
=
93 (RawInstr
& 0xFFFFF) | (static_cast<uint32_t>(Lo
& 0xFFF) << 20);
97 int64_t Value
= E
.getTarget().getAddress() + E
.getAddend() - FixupAddress
;
98 int32_t Hi
= (Value
+ 0x800) & 0xFFFFF000;
99 int32_t Lo
= Value
& 0xFFF;
100 uint32_t RawInstrAuipc
= *(little32_t
*)FixupPtr
;
101 uint32_t RawInstrJalr
= *(little32_t
*)(FixupPtr
+ 4);
102 *(little32_t
*)FixupPtr
= RawInstrAuipc
| static_cast<uint32_t>(Hi
);
103 *(little32_t
*)(FixupPtr
+ 4) =
104 RawInstrJalr
| (static_cast<uint32_t>(Lo
) << 20);
107 case R_RISCV_PCREL_HI20
: {
108 int64_t Value
= E
.getTarget().getAddress() + E
.getAddend() - FixupAddress
;
109 int32_t Hi
= (Value
+ 0x800) & 0xFFFFF000;
110 uint32_t RawInstr
= *(little32_t
*)FixupPtr
;
111 *(little32_t
*)FixupPtr
= (RawInstr
& 0xFFF) | static_cast<uint32_t>(Hi
);
114 case R_RISCV_PCREL_LO12_I
: {
115 auto RelHI20
= getRISCVPCRelHi20(E
);
117 return RelHI20
.takeError();
118 int64_t Value
= RelHI20
->getTarget().getAddress() +
119 RelHI20
->getAddend() - E
.getTarget().getAddress();
120 int64_t Lo
= Value
& 0xFFF;
121 uint32_t RawInstr
= *(little32_t
*)FixupPtr
;
122 *(little32_t
*)FixupPtr
=
123 (RawInstr
& 0xFFFFF) | (static_cast<uint32_t>(Lo
& 0xFFF) << 20);
126 case R_RISCV_PCREL_LO12_S
: {
127 auto RelHI20
= getRISCVPCRelHi20(E
);
128 int64_t Value
= RelHI20
->getTarget().getAddress() +
129 RelHI20
->getAddend() - E
.getTarget().getAddress();
130 int64_t Lo
= Value
& 0xFFF;
131 uint32_t Imm31_25
= extractBits(Lo
, 11, 5) << 25;
132 uint32_t Imm11_7
= extractBits(Lo
, 4, 0) << 7;
133 uint32_t RawInstr
= *(little32_t
*)FixupPtr
;
135 *(little32_t
*)FixupPtr
= (RawInstr
& 0x1FFF07F) | Imm31_25
| Imm11_7
;
139 return Error::success();
143 template <typename ELFT
>
144 class ELFLinkGraphBuilder_riscv
: public ELFLinkGraphBuilder
<ELFT
> {
146 static Expected
<riscv::EdgeKind_riscv
>
147 getRelocationKind(const uint32_t Type
) {
148 using namespace riscv
;
150 case ELF::R_RISCV_32
:
151 return EdgeKind_riscv::R_RISCV_32
;
152 case ELF::R_RISCV_64
:
153 return EdgeKind_riscv::R_RISCV_64
;
154 case ELF::R_RISCV_HI20
:
155 return EdgeKind_riscv::R_RISCV_HI20
;
156 case ELF::R_RISCV_LO12_I
:
157 return EdgeKind_riscv::R_RISCV_LO12_I
;
158 case ELF::R_RISCV_CALL
:
159 return EdgeKind_riscv::R_RISCV_CALL
;
160 case ELF::R_RISCV_PCREL_HI20
:
161 return EdgeKind_riscv::R_RISCV_PCREL_HI20
;
162 case ELF::R_RISCV_PCREL_LO12_I
:
163 return EdgeKind_riscv::R_RISCV_PCREL_LO12_I
;
164 case ELF::R_RISCV_PCREL_LO12_S
:
165 return EdgeKind_riscv::R_RISCV_PCREL_LO12_S
;
168 return make_error
<JITLinkError
>("Unsupported riscv relocation:" +
169 formatv("{0:d}", Type
));
172 Error
addRelocations() override
{
173 using Base
= ELFLinkGraphBuilder
<ELFT
>;
174 LLVM_DEBUG(dbgs() << "Adding relocations\n");
176 // TODO a partern is forming of iterate some sections but only give me
177 // ones I am interested, I should abstract that concept some where
178 for (auto &SecRef
: Base::Sections
) {
179 if (SecRef
.sh_type
!= ELF::SHT_RELA
&& SecRef
.sh_type
!= ELF::SHT_REL
)
181 auto RelSectName
= Base::Obj
.getSectionName(SecRef
);
183 return RelSectName
.takeError();
186 dbgs() << "Adding relocations from section " << *RelSectName
<< "\n";
189 auto UpdateSection
= Base::Obj
.getSection(SecRef
.sh_info
);
191 return UpdateSection
.takeError();
193 auto UpdateSectionName
= Base::Obj
.getSectionName(**UpdateSection
);
194 if (!UpdateSectionName
)
195 return UpdateSectionName
.takeError();
196 // Don't process relocations for debug sections.
197 if (Base::isDwarfSection(*UpdateSectionName
)) {
199 dbgs() << " Target is dwarf section " << *UpdateSectionName
205 dbgs() << " For target section " << *UpdateSectionName
<< "\n";
208 auto *JITSection
= Base::G
->findSectionByName(*UpdateSectionName
);
210 return make_error
<llvm::StringError
>(
211 "Refencing a section that wasn't added to graph" +
213 llvm::inconvertibleErrorCode());
215 auto Relocations
= Base::Obj
.relas(SecRef
);
217 return Relocations
.takeError();
219 for (const auto &Rela
: *Relocations
) {
220 auto Type
= Rela
.getType(false);
223 dbgs() << "Relocation Type: " << Type
<< "\n"
224 << "Name: " << Base::Obj
.getRelocationTypeName(Type
) << "\n";
227 auto SymbolIndex
= Rela
.getSymbol(false);
228 auto Symbol
= Base::Obj
.getRelocationSymbol(Rela
, Base::SymTabSec
);
230 return Symbol
.takeError();
232 auto BlockToFix
= *(JITSection
->blocks().begin());
233 auto *TargetSymbol
= Base::getGraphSymbol(SymbolIndex
);
236 return make_error
<llvm::StringError
>(
237 "Could not find symbol at given index, did you add it to "
238 "JITSymbolTable? index: " +
239 std::to_string(SymbolIndex
) + ", shndx: " +
240 std::to_string((*Symbol
)->st_shndx
) + " Size of table: " +
241 std::to_string(Base::GraphSymbols
.size()),
242 llvm::inconvertibleErrorCode());
244 int64_t Addend
= Rela
.r_addend
;
245 JITTargetAddress FixupAddress
=
246 (*UpdateSection
)->sh_addr
+ Rela
.r_offset
;
249 dbgs() << "Processing relocation at "
250 << format("0x%016" PRIx64
, FixupAddress
) << "\n";
252 auto Kind
= getRelocationKind(Type
);
254 return Kind
.takeError();
256 BlockToFix
->addEdge(*Kind
, FixupAddress
- BlockToFix
->getAddress(),
257 *TargetSymbol
, Addend
);
260 return Error::success();
264 ELFLinkGraphBuilder_riscv(StringRef FileName
,
265 const object::ELFFile
<ELFT
> &Obj
, const Triple T
)
266 : ELFLinkGraphBuilder
<ELFT
>(Obj
, std::move(T
), FileName
,
267 riscv::getEdgeKindName
) {}
270 Expected
<std::unique_ptr
<LinkGraph
>>
271 createLinkGraphFromELFObject_riscv(MemoryBufferRef ObjectBuffer
) {
273 dbgs() << "Building jitlink graph for new input "
274 << ObjectBuffer
.getBufferIdentifier() << "...\n";
277 auto ELFObj
= object::ObjectFile::createELFObjectFile(ObjectBuffer
);
279 return ELFObj
.takeError();
281 if ((*ELFObj
)->getArch() == Triple::riscv64
) {
282 auto &ELFObjFile
= cast
<object::ELFObjectFile
<object::ELF64LE
>>(**ELFObj
);
283 return ELFLinkGraphBuilder_riscv
<object::ELF64LE
>(
284 (*ELFObj
)->getFileName(), ELFObjFile
.getELFFile(),
285 (*ELFObj
)->makeTriple())
288 assert((*ELFObj
)->getArch() == Triple::riscv32
&&
289 "Invalid triple for RISCV ELF object file");
290 auto &ELFObjFile
= cast
<object::ELFObjectFile
<object::ELF32LE
>>(**ELFObj
);
291 return ELFLinkGraphBuilder_riscv
<object::ELF32LE
>(
292 (*ELFObj
)->getFileName(), ELFObjFile
.getELFFile(),
293 (*ELFObj
)->makeTriple())
298 void link_ELF_riscv(std::unique_ptr
<LinkGraph
> G
,
299 std::unique_ptr
<JITLinkContext
> Ctx
) {
300 PassConfiguration Config
;
301 const Triple
&TT
= G
->getTargetTriple();
302 if (Ctx
->shouldAddDefaultTargetPasses(TT
)) {
303 if (auto MarkLive
= Ctx
->getMarkLivePass(TT
))
304 Config
.PrePrunePasses
.push_back(std::move(MarkLive
));
306 Config
.PrePrunePasses
.push_back(markAllSymbolsLive
);
308 if (auto Err
= Ctx
->modifyPassConfig(*G
, Config
))
309 return Ctx
->notifyFailed(std::move(Err
));
311 ELFJITLinker_riscv::link(std::move(Ctx
), std::move(G
), std::move(Config
));
314 } // namespace jitlink