1 //===----- ELF_aarch64.cpp - JIT linker implementation for ELF/aarch64 ----===//
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/aarch64 jit-link implementation.
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #include "llvm/ExecutionEngine/JITLink/ELF_aarch64.h"
14 #include "ELFLinkGraphBuilder.h"
15 #include "JITLinkGeneric.h"
16 #include "llvm/BinaryFormat/ELF.h"
17 #include "llvm/ExecutionEngine/JITLink/aarch64.h"
18 #include "llvm/Object/ELFObjectFile.h"
19 
20 #define DEBUG_TYPE "jitlink"
21 
22 using namespace llvm;
23 using namespace llvm::jitlink;
24 
25 namespace llvm {
26 namespace jitlink {
27 
28 class ELFJITLinker_aarch64 : public JITLinker<ELFJITLinker_aarch64> {
29   friend class JITLinker<ELFJITLinker_aarch64>;
30 
31 public:
32   ELFJITLinker_aarch64(std::unique_ptr<JITLinkContext> Ctx,
33                        std::unique_ptr<LinkGraph> G,
34                        PassConfiguration PassConfig)
35       : JITLinker(std::move(Ctx), std::move(G), std::move(PassConfig)) {}
36 
37 private:
38   Error applyFixup(LinkGraph &G, Block &B, const Edge &E) const {
39     using namespace aarch64;
40     using namespace llvm::support;
41 
42     char *BlockWorkingMem = B.getAlreadyMutableContent().data();
43     char *FixupPtr = BlockWorkingMem + E.getOffset();
44     auto FixupAddress = B.getAddress() + E.getOffset();
45     switch (E.getKind()) {
46     case aarch64::R_AARCH64_CALL26: {
47       assert((FixupAddress.getValue() & 0x3) == 0 &&
48              "Call-inst is not 32-bit aligned");
49       int64_t Value = E.getTarget().getAddress() - FixupAddress + E.getAddend();
50 
51       if (static_cast<uint64_t>(Value) & 0x3)
52         return make_error<JITLinkError>("Call target is not 32-bit aligned");
53 
54       if (!fitsRangeSignedInt<27>(Value))
55         return makeTargetOutOfRangeError(G, B, E);
56 
57       uint32_t RawInstr = *(little32_t *)FixupPtr;
58       assert((RawInstr & 0x7fffffff) == 0x14000000 &&
59              "RawInstr isn't a B or BR immediate instruction");
60       uint32_t Imm = (static_cast<uint32_t>(Value) & ((1 << 28) - 1)) >> 2;
61       uint32_t FixedInstr = RawInstr | Imm;
62       *(little32_t *)FixupPtr = FixedInstr;
63       break;
64     }
65     }
66     return Error::success();
67   }
68 
69   template <uint8_t Bits> static bool fitsRangeSignedInt(int64_t Value) {
70     return Value >= -(1ll << Bits) && Value < (1ll << Bits);
71   }
72 };
73 
74 template <typename ELFT>
75 class ELFLinkGraphBuilder_aarch64 : public ELFLinkGraphBuilder<ELFT> {
76 private:
77   static Expected<aarch64::EdgeKind_aarch64>
78   getRelocationKind(const uint32_t Type) {
79     using namespace aarch64;
80     switch (Type) {
81     case ELF::R_AARCH64_CALL26:
82       return EdgeKind_aarch64::R_AARCH64_CALL26;
83     }
84 
85     return make_error<JITLinkError>("Unsupported aarch64 relocation:" +
86                                     formatv("{0:d}", Type));
87   }
88 
89   Error addRelocations() override {
90     LLVM_DEBUG(dbgs() << "Processing relocations:\n");
91 
92     using Base = ELFLinkGraphBuilder<ELFT>;
93     using Self = ELFLinkGraphBuilder_aarch64<ELFT>;
94     for (const auto &RelSect : Base::Sections)
95       if (Error Err = Base::forEachRelocation(RelSect, this,
96                                               &Self::addSingleRelocation))
97         return Err;
98 
99     return Error::success();
100   }
101 
102   Error addSingleRelocation(const typename ELFT::Rela &Rel,
103                             const typename ELFT::Shdr &FixupSect,
104                             Section &GraphSection) {
105     using Base = ELFLinkGraphBuilder<ELFT>;
106 
107     uint32_t SymbolIndex = Rel.getSymbol(false);
108     auto ObjSymbol = Base::Obj.getRelocationSymbol(Rel, Base::SymTabSec);
109     if (!ObjSymbol)
110       return ObjSymbol.takeError();
111 
112     Symbol *GraphSymbol = Base::getGraphSymbol(SymbolIndex);
113     if (!GraphSymbol)
114       return make_error<StringError>(
115           formatv("Could not find symbol at given index, did you add it to "
116                   "JITSymbolTable? index: {0}, shndx: {1} Size of table: {2}",
117                   SymbolIndex, (*ObjSymbol)->st_shndx,
118                   Base::GraphSymbols.size()),
119           inconvertibleErrorCode());
120 
121     uint32_t Type = Rel.getType(false);
122     Expected<aarch64::EdgeKind_aarch64> Kind = getRelocationKind(Type);
123     if (!Kind)
124       return Kind.takeError();
125 
126     int64_t Addend = Rel.r_addend;
127     Block *BlockToFix = *(GraphSection.blocks().begin());
128     orc::ExecutorAddr FixupAddress =
129         orc::ExecutorAddr(FixupSect.sh_addr) + Rel.r_offset;
130     Edge::OffsetT Offset = FixupAddress - BlockToFix->getAddress();
131     Edge GE(*Kind, Offset, *GraphSymbol, Addend);
132     LLVM_DEBUG({
133       dbgs() << "    ";
134       printEdge(dbgs(), *BlockToFix, GE, aarch64::getEdgeKindName(*Kind));
135       dbgs() << "\n";
136     });
137 
138     BlockToFix->addEdge(std::move(GE));
139     return Error::success();
140   }
141 
142 public:
143   ELFLinkGraphBuilder_aarch64(StringRef FileName,
144                               const object::ELFFile<ELFT> &Obj, const Triple T)
145       : ELFLinkGraphBuilder<ELFT>(Obj, std::move(T), FileName,
146                                   aarch64::getEdgeKindName) {}
147 };
148 
149 Expected<std::unique_ptr<LinkGraph>>
150 createLinkGraphFromELFObject_aarch64(MemoryBufferRef ObjectBuffer) {
151   LLVM_DEBUG({
152     dbgs() << "Building jitlink graph for new input "
153            << ObjectBuffer.getBufferIdentifier() << "...\n";
154   });
155 
156   auto ELFObj = object::ObjectFile::createELFObjectFile(ObjectBuffer);
157   if (!ELFObj)
158     return ELFObj.takeError();
159 
160   assert((*ELFObj)->getArch() == Triple::aarch64 &&
161          "Only AArch64 (little endian) is supported for now");
162 
163   auto &ELFObjFile = cast<object::ELFObjectFile<object::ELF64LE>>(**ELFObj);
164   return ELFLinkGraphBuilder_aarch64<object::ELF64LE>((*ELFObj)->getFileName(),
165                                                       ELFObjFile.getELFFile(),
166                                                       (*ELFObj)->makeTriple())
167       .buildGraph();
168 }
169 
170 void link_ELF_aarch64(std::unique_ptr<LinkGraph> G,
171                       std::unique_ptr<JITLinkContext> Ctx) {
172   PassConfiguration Config;
173   const Triple &TT = G->getTargetTriple();
174   if (Ctx->shouldAddDefaultTargetPasses(TT)) {
175     if (auto MarkLive = Ctx->getMarkLivePass(TT))
176       Config.PrePrunePasses.push_back(std::move(MarkLive));
177     else
178       Config.PrePrunePasses.push_back(markAllSymbolsLive);
179   }
180   if (auto Err = Ctx->modifyPassConfig(*G, Config))
181     return Ctx->notifyFailed(std::move(Err));
182 
183   ELFJITLinker_aarch64::link(std::move(Ctx), std::move(G), std::move(Config));
184 }
185 
186 } // namespace jitlink
187 } // namespace llvm
188